diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000000..8dd399ab55 --- /dev/null +++ b/.flake8 @@ -0,0 +1,3 @@ +[flake8] +max-line-length = 88 +extend-ignore = E203 diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000000..e54ece627c --- /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/issues) and also the [draft release notes](https://gist.github.com/KelSolaar/4a6ebe9ec3d389f0934b154fec8df51d). + The issue could already be fixed in the [develop](https://github.com/colour-science/colour) 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 0000000000..91ed3e5445 --- /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/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 0000000000..e859646d57 --- /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/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 0000000000..fbd9e95eb7 --- /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/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/discussions) area. + placeholder: > + << The discussions area is this way: https://github.com/colour-science/colour/discussions... >> diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..d9138f6df4 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,33 @@ + + +# 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) compliant. + + diff --git a/.github/workflows/continuous-integration-documentation.yml b/.github/workflows/continuous-integration-documentation.yml new file mode 100644 index 0000000000..1f9e68eb72 --- /dev/null +++ b/.github/workflows/continuous-integration-documentation.yml @@ -0,0 +1,55 @@ +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 update + sudo apt-get --yes install graphviz graphviz-dev 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 + - uses: actions/upload-artifact@v2 + with: + name: ${{ env.CI_PACKAGE }}-plots + path: | + docs/_static/Basics_*.png + docs/_static/Examples_*.png + docs/_static/Plotting_*.png + docs/_static/Tutorial_*.png diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration-quality-unit-tests.yml similarity index 51% rename from .github/workflows/continuous-integration.yml rename to .github/workflows/continuous-integration-quality-unit-tests.yml index daf4c598fa..367a8fbef9 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration-quality-unit-tests.yml @@ -1,14 +1,14 @@ -name: Continuous Integration +name: Continuous Integration - Quality & Unit Tests on: [push, pull_request] jobs: - continuous-integration: + continuous-integration-package: name: ${{ matrix.os }} - Python ${{ matrix.python-version }} strategy: matrix: - os: [macOS-latest, ubuntu-18.04, windows-latest] - python-version: [2.7, 3.6, 3.7, 3.8] + os: [macOS-latest, ubuntu-20.04, windows-latest] + python-version: [3.8, 3.9, '3.10'] fail-fast: false runs-on: ${{ matrix.os }} steps: @@ -18,8 +18,8 @@ jobs: echo "CI_PYTHON_VERSION=${{ matrix.python-version }}" >> $GITHUB_ENV echo "CI_PACKAGE=colour" >> $GITHUB_ENV echo "CI_SHA=${{ github.sha }}" >> $GITHUB_ENV - echo "CI_OPENIMAGEIO_VERSION=2.1.10.1" >> $GITHUB_ENV - echo "CI_OPENIMAGEIO_ARTIFACT=https://github.com/colour-science/artifacts/releases/download/OpenImageIO-Release-2.1.10.1/OpenImageIO-Release-2.1.10.1.zip" >> $GITHUB_ENV + echo "CI_OPENIMAGEIO_VERSION=2.2.18.0" >> $GITHUB_ENV + echo "CI_OPENIMAGEIO_ARTIFACT=https://github.com/colour-science/artifacts/releases/download/OpenImageIO-Release-2.2.18.0/OpenImageIO-Release-2.2.18.0.zip" >> $GITHUB_ENV echo "COVERALLS_REPO_TOKEN=${{ secrets.COVERALLS_REPO_TOKEN }}" >> $GITHUB_ENV echo "MPLBACKEND=AGG" >> $GITHUB_ENV shell: bash @@ -27,63 +27,60 @@ jobs: uses: actions/setup-python@v1 with: python-version: ${{ matrix.python-version }} - - name: Update OS & Install APT Dependencies (Ubuntu) - if: matrix.os == 'ubuntu-18.04' - run: | - sudo apt-get --yes install libboost-all-dev libilmbase-dev libopenexr-dev libpng-dev libtiff5-dev - - name: Update OS & Install APT Dependencies (macOs) + - name: Install Dependencies (macOS) if: matrix.os == 'macOS-latest' run: | - brew install gnu-sed + brew install gnu-sed graphviz ln -s /usr/local/bin/gsed /usr/local/bin/sed shell: bash + - name: Install Dependencies (Ubuntu) + if: matrix.os == 'ubuntu-20.04' + run: | + sudo apt-get --yes install graphviz graphviz-dev libboost-all-dev libilmbase-dev libopenexr-dev libpng-dev libtiff5-dev - 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 --version 1.1.12 echo "$HOME/.poetry/bin" >> $GITHUB_PATH shell: bash - - name: Update pyproject.toml file (Python 2.7) - if: matrix.python-version == '2.7' + - name: Install Package Dependencies (macOS & Ubuntu) + if: matrix.os == 'macOS-latest' || matrix.os == 'ubuntu-20.04' run: | - sed -i.bak 's/python = "~2.7 || ^3.6"/python = "~2.7"/g' pyproject.toml - sed -i.bak '/scipy = "\^1\.1\.0"/ a qtconsole = "4.7.7"' 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 '/scipy = "\^1\.1\.0"/ a pywin32 = "228"' 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 + poetry run python -m pip install --upgrade pip + poetry install --extras "graphviz meshing optional plotting" + poetry run python -c "import imageio;imageio.plugins.freeimage.download()" shell: bash - - name: Install Package Dependencies + - name: Install Package Dependencies (Windows) + if: matrix.os == 'windows-latest' run: | - poetry install --extras "optional plotting" + poetry run python -m pip install --upgrade pip + poetry install --extras "meshing optional plotting" poetry run python -c "import imageio;imageio.plugins.freeimage.download()" shell: bash - name: Install OpenImageIO (Ubuntu) - if: matrix.os == 'ubuntu-18.04' && matrix.python-version == '3.6' + if: matrix.os == 'ubuntu-20.04' && matrix.python-version == '3.8' run: | curl -L $CI_OPENIMAGEIO_ARTIFACT -o OpenImageIO-Release-$CI_OPENIMAGEIO_VERSION.zip unzip OpenImageIO-Release-$CI_OPENIMAGEIO_VERSION.zip -d OpenImageIO-Release-$CI_OPENIMAGEIO_VERSION sudo cp OpenImageIO-Release-$CI_OPENIMAGEIO_VERSION/linux64/bin/* /usr/bin/ sudo cp -r OpenImageIO-Release-$CI_OPENIMAGEIO_VERSION/linux64/lib/* /usr/lib/ sudo rm -rf /usr/lib/python$CI_PYTHON_VERSION - cp OpenImageIO-Release-$CI_OPENIMAGEIO_VERSION/linux64/lib/python$CI_PYTHON_VERSION/site-packages/OpenImageIO.so $(poetry env info -p)/lib/python$CI_PYTHON_VERSION/site-packages/ + cp OpenImageIO-Release-$CI_OPENIMAGEIO_VERSION/linux64/lib/python$CI_PYTHON_VERSION/site-packages/OpenImageIO.*.so $(poetry env info -p)/lib/python$CI_PYTHON_VERSION/site-packages/OpenImageIO.so + shell: bash + - name: Pre-Commit (All Files) + run: | + poetry run pre-commit run --all-files shell: bash - - name: Lint with flake8 + - name: Test Optimised Python Execution run: | - poetry run flake8 $CI_PACKAGE --count --show-source --statistics + 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 --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 - if: matrix.python-version == '3.6' || matrix.python-version == '3.7' || matrix.python-version == '3.8' + if: matrix.os == 'ubuntu-20.04' && matrix.python-version == '3.8' run: | if [ -z "$COVERALLS_REPO_TOKEN" ]; then echo \"COVERALLS_REPO_TOKEN\" secret is undefined!; else poetry run coveralls; fi shell: bash 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 0000000000..d7c73293a1 --- /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 ac7d5ab546..3a623fd213 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,9 @@ *.pyo .DS_Store .coverage +.dmypy.json .idea +.mypy_cache __pycache__ build dist diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f75972493c..6fcdb23564 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,11 +1,26 @@ repos: -- repo: https://gitlab.com/pycqa/flake8 - rev: 3.7.8 +- 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: 22.1.0 + hooks: + - id: black + language_version: python3.8 +- repo: https://github.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 +- repo: https://github.com/pycqa/pydocstyle + rev: 6.1.1 hooks: - - id: yapf - exclude: setup\.py + - id: pydocstyle + args: + - --convention=numpy + - --add-ignore=D104,D200,D202,D205,D301,D400 \ No newline at end of file diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000000..1ddedf9de1 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,23 @@ +version: 2 + +build: + os: ubuntu-20.04 + tools: + python: "3.8" + apt_packages: + - graphviz + - graphviz-dev + +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 6e5818f1b4..0000000000 --- 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/.style.yapf b/.style.yapf deleted file mode 100644 index 45e56e93f0..0000000000 --- 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/BIBLIOGRAPHY.bib b/BIBLIOGRAPHY.bib index e690534434..aa6caa7a3c 100644 --- a/BIBLIOGRAPHY.bib +++ b/BIBLIOGRAPHY.bib @@ -61,6 +61,14 @@ @misc{ASTMInternational2011a of last reapproval. A superscript epsilon) indicates an editorial change since the last revision or reapproval.}, } +@misc{ASTMInternational2015, + title = {ASTM E313-15e1 - Standard Practice for Calculating + Yellowness and Whiteness Indices from Instrumentally Measured + Color Coordinates}, + author = {{ASTM International}}, + year = 2015, + doi = {10.1520/E0313-20}, +} @misc{ASTMInternational2015b, title = {ASTM E308-15 - Standard Practice for Computing the Colors of Objects by Using the CIE System}, @@ -69,6 +77,39 @@ @misc{ASTMInternational2015b pages = {1--47}, doi = {10.1520/E0308-15}, } +@article{Abebe2017a, + title = {Perceptual Lightness Modeling for High-Dynamic-Range + Imaging}, + author = {Abebe, Mekides Assefa and Pouli, Tania and Larabi, + Mohamed-Chaker and Reinhard, Erik}, + year = 2017, + month = nov, + journal = {ACM Transactions on Applied Perception}, + volume = 15, + number = 1, + pages = {1--19}, + issn = {1544-3558, 1544-3965}, + doi = {10.1145/3086577}, + abstract = {The human visual system (HVS) non-linearly processes + light from the real world, allowing us to perceive detail over a + wide range of illumination. Although models that describe this + non-linearity are constructed based on psycho-visual experiments, + they generally apply to a limited range of illumination and + therefore may not fully explain the behavior of the HVS under more + extreme illumination conditions. We propose a modified + experimental protocol for measuring visual responses to emissive + stimuli that do not require participant training, nor requiring + the exclusion of non-expert participants. Furthermore, the + protocol can be applied to stimuli covering an extended luminance + range. Based on the outcome of our experiment, we propose a new + model describing lightness response over an extended luminance + range. The model can be integrated with existing color appearance + models or perceptual color spaces. To demonstrate the + effectiveness of our model in high dynamic range applications, we + evaluate its suitability for dynamic range expansion relative to + existing solutions.}, + langid = {english}, +} @misc{AdobeSystems2005a, title = {Adobe RGB (1998) Color Image Encoding}, author = {{Adobe Systems}}, @@ -129,11 +170,11 @@ @book{Barten1999 author = {Barten, Peter G.}, year = 1999, month = dec, + number = 1999, publisher = {SPIE}, issn = 10924388, doi = {10.1117/3.353254}, isbn = {978-0-8194-7849-8}, - number = 1999, pmid = 18723593, } @inproceedings{Barten2003, @@ -165,12 +206,12 @@ @article{Bianco2010a author = {Bianco, S. and Schettini, R.}, year = 2010, month = jun, + journal = {Color Research \& Application}, volume = 35, + number = 3, pages = {184--192}, issn = 03612317, doi = {10.1002/col.20573}, - journal = {Color Research \& Application}, - number = 3, } @misc{BlackmagicDesign2020, title = {DaVinci Wide Gamut - DaVinci Resolve Studio 17 @@ -179,13 +220,28 @@ @misc{BlackmagicDesign2020 year = 2020, month = nov, } +@misc{BlackmagicDesign2020a, + title = {Wide Gamut Intermediate DaVinci Resolve}, + author = {{Blackmagic Design}}, + year = 2020, + url = {https://documents.blackmagicdesign.com/InformationNotes/DaVinci_Resolve_17_Wide_Gamut_Intermediate.pdf?_v=1607414410000}, + urldate = {2020-12-12}, +} +@misc{BlackmagicDesign2021, + title = {Blackmagic Generation 5 Color Science}, + author = {{Blackmagic Design}}, + year = 2021, + url = {https://drive.google.com/file/d/1FF5WO2nvI9GEWb4_EntrBoV9ZIuFToZd/view}, +} @article{Bodhaine1999a, title = {On Rayleigh Optical Depth Calculations}, author = {Bodhaine, Barry A. and Wood, Norman B. and Dutton, Ellsworth G. and Slusser, James R.}, year = 1999, month = nov, + journal = {Journal of Atmospheric and Oceanic Technology}, volume = 16, + number = 11, pages = {1854--1861}, issn = {0739-0572}, doi = {10.1175/1520-0426(1999)016<1854:ORODC>2.0.CO;2}, @@ -208,8 +264,6 @@ @article{Bodhaine1999a estimated, such as aerosol optical depth, particularly in the UV region of the spectrum. All of the calculations are simple enough to be done easily in a spreadsheet.}, - journal = {Journal of Atmospheric and Oceanic Technology}, - number = 11, } @misc{Borer2017a, title = {Private Discussion with Mansencal, T. and Shaw, N.}, @@ -235,7 +289,9 @@ @article{Breneman1987b author = {Breneman, Edwin J.}, year = 1987, month = jun, + journal = {Journal of the Optical Society of America A}, volume = 4, + number = 6, pages = 1115, issn = {1084-7529}, doi = {10.1364/JOSAA.4.001115}, @@ -273,8 +329,6 @@ @article{Breneman1987b results of these experiments also indicated that higher purities are needed in order to produce the same absolute color appearances at low levels of illuminance.}, - journal = {Journal of the Optical Society of America A}, - number = 6, pmid = 3598755, } @article{Brill2008a, @@ -283,7 +337,9 @@ @article{Brill2008a author = {Brill, Michael H. and Susstrunk, Sabine}, year = 2008, month = oct, + journal = {Color Research \& Application}, volume = 33, + number = 5, pages = {424--426}, issn = 03612317, doi = {10.1002/col.20432}, @@ -298,10 +354,8 @@ @article{Brill2008a action of adaptation at the boundary of that octant. Such modifications may be needed to avoid the mathematical problems in CIECAM02.}, - journal = {Color Research \& Application}, keywords = {Chromatic adaptation,CIECAM02,Color appearance,Gamut,Model,Primary}, - number = 5, } @misc{Broadbent2009a, title = {Calculation from the Original Experimental Data of @@ -309,6 +363,8 @@ @misc{Broadbent2009a Co-Ordinates and Color Matching Functions.}, author = {Broadbent, A. D.}, year = 2009, + journal = {Qu\'ebec, Canada: D\'epartement de g\'enie chimique, + \ldots}, pages = {1--17}, url = {http://www.cis.rit.edu/mcsl/research/1931.php}, urldate = {2014-06-12}, @@ -325,8 +381,6 @@ @misc{Broadbent2009a less reproduce the entire sequence of calculations. All the tables of numerical data are given in the accompanying computer worksheet file CIE1931\_RGB.xls.}, - journal = {Qu\'ebec, Canada: D\'epartement de g\'enie chimique, - \ldots}, } @book{Burger2009b, title = {Principles of Digital Image Processing}, @@ -410,10 +464,10 @@ @book{CIETC1-482004h title = {CIE 015:2004 Colorimetry, 3rd Edition}, author = {{CIE TC 1-48}}, year = 2004, + journal = {CIE 015:2004 Colorimetry, 3rd Edition}, publisher = {Commission Internationale de l'Eclairage}, url = {http://www.cie.co.at/publications/colorimetry-3rd-edition}, isbn = {978-3-901906-33-6}, - journal = {CIE 015:2004 Colorimetry, 3rd Edition}, } @incollection{CIETC1-482004i, title = {APPENDIX E. INFORMATION ON THE USE OF PLANCK'S @@ -486,13 +540,13 @@ @book{CIETC1-902017 scientific use}, author = {{CIE TC 1-90}}, year = 2017, + series = {Technical report / CIE}, + number = 224, publisher = {CIE Central Bureau}, address = {Vienna}, - annotation = {OCLC: 988568299}, isbn = {978-3-902842-61-9}, - language = {eng fre ger}, - number = 224, - series = {Technical report / CIE}, + langid = {eng fre ger}, + annotation = {OCLC: 988568299}, } @misc{CIEce, title = {CIE 15:2004 Tables Data}, @@ -586,7 +640,9 @@ @article{Cao2013 author = {Cao, Renbo and Trussell, H Joel and Shamey, Renzo}, year = 2013, month = aug, + journal = {Journal of the Optical Society of America A}, volume = 30, + number = 8, pages = 1508, issn = {1084-7529}, doi = {10.1364/JOSAA.30.001508}, @@ -607,8 +663,6 @@ @article{Cao2013 conversion time of less than 1 s for 1891 samples.}, isbn = {1520-8532 (Electronic)\textbackslash r1084-7529 (Linking)}, - journal = {Journal of the Optical Society of America A}, - number = 8, pmid = 24323208, } @techreport{Carter2018, @@ -629,7 +683,7 @@ @misc{Castro2014a row of a 2d array}, author = {Castro, Saullo}, year = 2014, - url = {http://stackoverflow.com/questions/26511401/numpy-fastest-way-of-computing-diagonal-for-each-row-of-a-2d-array/26517247\#26517247}, + url = {http://stackoverflow.com/questions/26511401/numpy-fastest-way-of-computing-diagonal-for-each-row-of-a-2d-array/26517247#26517247}, urldate = {2014-08-22}, } @article{Centore2012a, @@ -638,14 +692,14 @@ @article{Centore2012a author = {Centore, Paul}, year = 2012, month = dec, + journal = {Color Research \& Application}, volume = 37, + number = 6, pages = {455--464}, issn = 03612317, doi = {10.1002/col.20715}, - journal = {Color Research \& Application}, keywords = {algorithm,inverse renotation,munsell,open source,renotation}, - number = 6, } @misc{Centore2014k, title = {MunsellAndKubelkaMunkToolboxApr2014 - @@ -734,7 +788,7 @@ @misc{Chamberlain2015 title = {LUT documentation (to create from another program)}, author = {Chamberlain, Peter}, year = 2015, - url = {https://forum.blackmagicdesign.com/viewtopic.php?f=21\&t=40284\#p232952}, + url = {https://forum.blackmagicdesign.com/viewtopic.php?f=21\&t=40284#p232952}, urldate = {2018-08-23}, } @article{Cheung2004, @@ -744,7 +798,9 @@ @article{Cheung2004 author = {Cheung, Vien and Westland, Stephen and Connah, David and Ripamonti, Caterina}, year = 2004, + journal = {Coloration Technology}, volume = 120, + number = 1, pages = {19--25}, issn = 14723581, doi = {10.1111/j.1478-4408.2004.tb00201.x}, @@ -761,8 +817,6 @@ @article{Cheung2004 time-consuming to train, it is concluded that polynomial transforms offer the better alternative for camera characterisation.}, - journal = {Coloration Technology}, - number = 1, } @misc{Colblindora, title = {Deuteranopia - Red-Green Color Blindness}, @@ -794,7 +848,9 @@ @article{Cowan2004 and Walker, Brad}, year = 2004, month = sep, + journal = {SMPTE Motion Imaging Journal}, volume = 113, + number = 9, pages = {281--292}, issn = {2160-2492}, doi = {10.5594/j11549}, @@ -812,8 +868,43 @@ @article{Cowan2004 would see a one-code value change with 12-bit encoding. This result matches the results of published contrast sensitivity experiments.}, - journal = {SMPTE Motion Imaging Journal}, - number = 9, +} +@article{Cui2002, + ids = {Cui2002a}, + title = {Uniform colour spaces based on the DIN99 + colour-difference formula}, + author = {Cui, G. and Luo, M. R. and Rigg, B. and Roesler, G. + and Witt, K.}, + year = 2002, + journal = {Color Research \& Application}, + volume = 27, + number = 4, + pages = {282--290}, + issn = {1520-6378}, + doi = {10.1002/col.10066}, + abstract = {Several colour-difference formulas such as CMC, + CIE94, and CIEDE2000 have been developed by modifying CIELAB. + These formulas give much better fits for experimental data based + on small colour differences than does CIELAB. None of these has an + associated uniform colour space (UCS). The need for a UCS is + demonstrated by the widespread use of the a*b* diagram despite the + lack of uniformity. This article describes the development of + formulas, with the same basic structure as the DIN99 formula, that + predict the experimental data sets better than do the CMC and + CIE94 colour-difference formulas and only slightly worse than + CIEDE2000 (which was optimized on the experimental data). However, + these formulas all have an associated UCS. The spaces are similar + in form to L*a*b*. \textcopyright{} 2002 Wiley Periodicals, Inc. + Col Res Appl, 27, 282\textendash 290, 2002; Published online in + Wiley InterScience (www.interscience.wiley.com). DOI + 10.1002/col.10066}, + copyright = {Copyright \textcopyright{} 2002 Wiley Periodicals, + Inc.}, + langid = {english}, + keywords = {colour discrimination ellipses,colour-difference + metrics,uniform colour space}, + annotation = {\_eprint: + https://onlinelibrary.wiley.com/doi/pdf/10.1002/col.10066}, } @misc{DJI2017, title = {White Paper on D-Log and D-Gamut of DJI Cinema Color @@ -830,19 +921,56 @@ @article{Darrodi2015a and Goodman, Teresa and Mackiewicz, Michal}, year = 2015, month = mar, + journal = {Journal of the Optical Society of America A}, volume = 32, + number = 3, pages = 381, issn = {1084-7529}, doi = {10.1364/JOSAA.32.000381}, - journal = {Journal of the Optical Society of America A}, - number = 3, +} +@article{David2015a, + title = {Development of the IES method for evaluating the + color rendition of light sources}, + author = {David, Aurelien and Fini, Paul T. and Houser, Kevin + W. and Ohno, Yoshi and Royer, Michael P. and Smet, Kevin A. G. and + Wei, Minchen and Whitehead, Lorne}, + year = 2015, + month = jun, + journal = {Optics Express}, + volume = 23, + number = 12, + pages = 15888, + issn = {1094-4087}, + doi = {10.1364/OE.23.015888}, + abstract = {We have developed a two-measure system for + evaluating light sources' color rendition that builds upon + conceptual progress of numerous researchers over the last two + decades. The system quantifies the color fidelity and color gamut + (change in object chroma) of a light source in comparison to a + reference illuminant. The calculations are based on a newly + developed set of reflectance data from real samples uniformly + distributed in color space (thereby fairly representing all + colors) and in wavelength space (thereby precluding artificial + optimization of the color rendition scores by spectral + engineering). The color fidelity score Rf is an improved version + of the CIE color rendering index. The color gamut score Rg is an + improved version of the Gamut Area Index. In combination, they + provide two complementary assessments to guide the optimization of + future light sources. This method summarizes the findings of the + Color Metric Task Group of the Illuminating Engineering Society of + North America (IES). It is adopted in the upcoming IES TM-30-2015, + and is proposed for consideration with the International + Commission on Illumination (CIE).}, + langid = {english}, } @article{Davis2010a, title = {Color quality scale}, author = {Davis, Wendy and Ohno, Yoshiro}, year = 2010, month = mar, + journal = {Optical Engineering}, volume = 49, + number = 3, pages = 033602, issn = {0091-3286}, doi = {10.1117/1.3360335}, @@ -867,8 +995,6 @@ @article{Davis2010a adaptation transform used in the calculations are updated. Supplementary scales have also been developed for expert users.}, isbn = {0091-3286}, - journal = {Optical Engineering}, - number = 3, } @misc{DigitalCinemaInitiatives2007b, title = {Digital Cinema System Specification - Version 1.1}, @@ -891,49 +1017,49 @@ @misc{Dyer2017 @misc{EasyRGBh, title = {RGB --{$>$} CMY}, author = {{EasyRGB}}, - url = {http://www.easyrgb.com/index.php?X=MATH\&H=11\#text11}, + url = {http://www.easyrgb.com/index.php?X=MATH\&H=11#text11}, urldate = {2014-05-18}, } @misc{EasyRGBi, title = {CMY --{$>$} RGB}, author = {{EasyRGB}}, - url = {http://www.easyrgb.com/index.php?X=MATH\&H=12\#text12}, + url = {http://www.easyrgb.com/index.php?X=MATH\&H=12#text12}, urldate = {2014-05-18}, } @misc{EasyRGBj, title = {RGB --{$>$} HSV}, author = {{EasyRGB}}, - url = {http://www.easyrgb.com/index.php?X=MATH\&H=20\#text20}, + url = {http://www.easyrgb.com/index.php?X=MATH\&H=20#text20}, urldate = {2014-05-18}, } @misc{EasyRGBk, title = {HSL --{$>$} RGB}, author = {{EasyRGB}}, - url = {http://www.easyrgb.com/index.php?X=MATH\&H=19\#text19}, + url = {http://www.easyrgb.com/index.php?X=MATH\&H=19#text19}, urldate = {2014-05-18}, } @misc{EasyRGBl, title = {RGB --{$>$} HSL}, author = {{EasyRGB}}, - url = {http://www.easyrgb.com/index.php?X=MATH\&H=18\#text18}, + url = {http://www.easyrgb.com/index.php?X=MATH\&H=18#text18}, urldate = {2014-05-18}, } @misc{EasyRGBm, title = {CMYK --{$>$} CMY}, author = {{EasyRGB}}, - url = {http://www.easyrgb.com/index.php?X=MATH\&H=14\#text14}, + url = {http://www.easyrgb.com/index.php?X=MATH\&H=14#text14}, urldate = {2014-05-18}, } @misc{EasyRGBn, title = {HSV --{$>$} RGB}, author = {{EasyRGB}}, - url = {http://www.easyrgb.com/index.php?X=MATH\&H=21\#text21}, + url = {http://www.easyrgb.com/index.php?X=MATH\&H=21#text21}, urldate = {2014-05-18}, } @misc{EasyRGBo, title = {CMY --{$>$} CMYK}, author = {{EasyRGB}}, - url = {http://www.easyrgb.com/index.php?X=MATH\&H=13\#text13}, + url = {http://www.easyrgb.com/index.php?X=MATH\&H=13#text13}, urldate = {2014-05-18}, } @inproceedings{Ebner1998, @@ -989,7 +1115,9 @@ @article{Fairchild1991a author = {Fairchild, Mark D.}, year = 1991, month = aug, + journal = {Color Research \& Application}, volume = 16, + number = 4, pages = {243--250}, issn = 03612317, doi = {10.1002/col.5080160406}, @@ -1006,15 +1134,15 @@ @article{Fairchild1991a well as results from another study and found to be significantly better at predicting corresponding colors than other proposed models.}, - journal = {Color Research \& Application}, - number = 4, } @article{Fairchild1996a, title = {Refinement of the RLAB color space}, author = {Fairchild, Mark D.}, year = 1996, month = oct, + journal = {Color Research \& Application}, volume = 21, + number = 5, pages = {338--346}, issn = {0361-2317}, doi = {10.1002/(SICI)1520-6378(199610)21:5<338::AID-COL3>3.0.CO;2-Z}, @@ -1026,10 +1154,8 @@ @article{Fairchild1996a their differences, and describes evolutionary enhancements to the RLAB model that simplify it and improve its performance. (C) 1996 John Wiley \& Sons, Inc.}, - journal = {Color Research \& Application}, keywords = {color appearance,color spaces,color-appearance models}, - number = 5, } @misc{Fairchild1998b, title = {Colorimetric Characterization of The Apple Studio @@ -1175,12 +1301,12 @@ @article{Fairman1985b integration}, author = {Fairman, Hugh S.}, year = 1985, + journal = {Color Research \& Application}, volume = 10, + number = 4, pages = {199--203}, issn = 03612317, doi = {10.1002/col.5080100407}, - journal = {Color Research \& Application}, - number = 4, } @article{Fairman1997, title = {How the CIE 1931 color-matching functions were @@ -1189,18 +1315,18 @@ @article{Fairman1997 Hemmendinger, Henry}, year = 1997, month = feb, + journal = {Color Research \& Application}, volume = 22, + number = 1, pages = {11--23}, issn = {0361-2317}, doi = {10.1002/(SICI)1520-6378(199702)22:1<11::AID-COL4>3.0.CO;2-7}, abstract = {Page 1. How the CIE 1931 Color-Matching Functions Were Derived from Wright-Guild Data Hugh S. Fairman, 1 Michael H. Brill, 2 Henry Hemmendinger 3}, - journal = {Color Research \& Application}, keywords = {alychne,chromaticity diagram,cie,cie 1931 system,color-matching,colorimetry,cus,guild data,mation,primary colors,spectrum lo-,transfor-,wright}, - number = 1, } @misc{FiLMiCInc2017, title = {FiLMiC Pro - User Manual v6 - Revision 1}, @@ -1215,7 +1341,9 @@ @article{Finlayson2015 Hurlbert, Anya}, year = 2015, month = may, + journal = {IEEE Transactions on Image Processing}, volume = 24, + number = 5, pages = {1460--1470}, issn = 10577149, doi = {10.1109/TIP.2015.2405336}, @@ -1244,16 +1372,75 @@ @article{Finlayson2015 experiments presented in this paper demonstrate that RPCC enhances color correction performance on real and synthetic data.}, isbn = {1057-7149 VO - 24}, - journal = {IEEE Transactions on Image Processing}, keywords = {camera characterization,Color correction,polynomial regression}, - number = 5, } @misc{Forsythe2018, title = {Private Discussion with Mansencal, T}, author = {Forsythe, Alex}, year = 2018, } +@misc{Frohlich2017, + title = {Encoding high dynamic range and wide color gamut + imagery}, + author = {Fr{\"o}hlich, Jan}, + year = 2017, + publisher = {Universit\"at Stuttgart}, + url = {http://elib.uni-stuttgart.de/handle/11682/9681}, + urldate = {2021-08-07}, + abstract = {In dieser Dissertation wird ein szenischer + Bewegtbilddatensatz mit erweitertem Dynamikumfang (High Dynamic + Range, HDR) und gro\ss em Farbumfang (Wide Color Gamut, WCG) + eingef\"uhrt und es werden Modelle zur Kodierung von HDR und WCG + Bildern vorgestellt. Die objektive und visuelle Evaluation neuer + HDR und WCG Bildverarbeitungsalgorithmen, Kompressionsverfahren + und Bildwiedergabeger\"ate erfordert einen Referenzdatensatz hoher + Qualit\"at. Daher wird ein neuer HDR- und WCG-Video-Datensatz mit + einem Dynamikumfang von bis zu 18 fotografischen Blenden + eingef\"uhrt. Er enth\"alt inszenierte und dokumentarische Szenen. + Die einzelnen Szenen sind konzipiert um eine Herausforderung f\"ur + Tone Mapping Operatoren, Gamut Mapping Algorithmen, + Kompressionscodecs und HDR und WCG Bildanzeigeger\"ate + darzustellen. Die Szenen sind mit professionellem Licht, Maske und + Filmausstattung aufgenommen. Um einen cinematischen Bildeindruck + zu erhalten, werden digitale Filmkameras mit `Super-35 mm' + Sensorgr\"o\ss e verwendet. Der zus\"atzliche Informationsgehalt + von HDR- und WCG-Videosignalen erfordert im Vergleich zu Signalen + mit herk\"ommlichem Dynamikumfang eine neue und effizientere + Signalkodierung. Ein Farbraum f\"ur HDR und WCG Video sollte nicht + nur effizient quantisieren, sondern wegen der unterschiedlichen + Monitoreigenschaften auf der Empf\"angerseite auch f\"ur die + Dynamik- und Farbumfangsanpassung geeignet sein. Bisher wurden + Methoden f\"ur die Quantisierung von HDR Luminanzsignalen + vorgeschlagen. Es fehlt jedoch noch ein entsprechendes Modell + f\"ur Farbdifferenzsignale. Es werden daher zwei neue Farbr\"aume + eingef\"uhrt, die sich sowohl f\"ur die effiziente Kodierung von + HDR und WCG Signalen als auch f\"ur die Dynamik- und + Farbumfangsanpassung eignen. Diese Farbr\"aume werden mit + existierenden HDR und WCG Farbsignalkodierungen des aktuellen + Stands der Technik verglichen. Die vorgestellten + Kodierungsschemata erlauben es, HDR- und WCG-Video mittels drei + Farbkan\"alen mit 12 Bits tonaler Aufl\"osung zu quantisieren, + ohne dass Quantisierungsartefakte sichtbar werden. W\"ahrend die + Speicherung und \"Ubertragung von HDR und WCG Video mit 12-Bit + Farbtiefe pro Kanal angestrebt wird, unterst\"utzen aktuell + verbreitete Dateiformate, Videoschnittstellen und + Kompressionscodecs oft nur niedrigere Bittiefen. Um diese + existierende Infrastruktur f\"ur die HDR Video\"ubertragung und + -speicherung nutzen zu k\"onnen, wird ein neues + bildinhaltsabh\"angiges Quantisierungsschema eingef\"uhrt. Diese + Quantisierungsmethode nutzt Bildeigenschaften wie Rauschen und + Textur um die ben\"otigte tonale Aufl\"osung f\"ur die visuell + verlustlose Quantisierung zu sch\"atzen. Die vorgestellte Methode + erlaubt es HDR Video mit einer Bittiefe von 10 Bits ohne sichtbare + Unterschiede zum Original zu quantisieren und kommt mit weniger + Rechenkraft im Vergleich zu aktuellen HDR Bilddifferenzmetriken + aus.}, + collaborator = {Universit{\"a}t Stuttgart and Universit{\"a}t + Stuttgart}, + langid = {english}, + keywords = 004, +} @misc{Fujifilm2016, title = {F-Log Data Sheet Ver.1.0}, author = {{Fujifilm}}, @@ -1270,13 +1457,30 @@ @misc{Gaggioni pages = {1--13}, url = {http://pro.sony.com/bbsccms/assets/files/mkt/cinema/solutions/slog_manual.pdf}, } +@article{Garcia2007, + title = {Measurement of the relationship between perceived + and computed color differences}, + author = {Garc{\'i}a, Pedro A. and Huertas, Rafael and + Melgosa, Manuel and Cui, Guihua}, + year = 2007, + month = jul, + journal = {Journal of the Optical Society of America A}, + volume = 24, + number = 7, + pages = 1823, + issn = {1084-7529, 1520-8532}, + doi = {10.1364/JOSAA.24.001823}, + langid = {english}, +} @article{Glasser1958a, title = {Cube-Root Color Coordinate System}, author = {Glasser, L. G. and McKinney, A. H. and Reilly, C. D. and Schnelle, P. D.}, year = 1958, month = oct, + journal = {Journal of the Optical Society of America}, volume = 48, + number = 10, pages = 736, publisher = {OSA}, issn = {0030-3941}, @@ -1300,8 +1504,6 @@ @article{Glasser1958a the spacing of Munsell colors is described, and the appropriateness of the assumptions required to obtain this behavior is discussed.}, - journal = {Journal of the Optical Society of America}, - number = 10, } @misc{GoPro2016a, title = {gopro.py}, @@ -1365,6 +1567,7 @@ @article{Hellwig2020 Space}, author = {Hellwig, Luke and Fairchild, Mark D.}, year = 2020, + journal = {Journal of Perceptual Imaging}, issn = {2575-8144}, doi = {10.2352/J.Percept.Imaging.2020.3.2.020401}, abstract = {A new color space, I G P G T G, was developed. I G P @@ -1381,8 +1584,7 @@ @article{Hellwig2020 performed well in this experiment but poorly on extant visual data. The mixed results indicate that it is possible to derive a moderately hue-linear color space without visual data.}, - journal = {Journal of Perceptual Imaging}, - language = {en}, + langid = {english}, } @article{Hernandez-Andres1999a, title = {Calculating correlated color temperatures across the @@ -1391,7 +1593,9 @@ @article{Hernandez-Andres1999a L. and Romero, Javier}, year = 1999, month = sep, + journal = {Applied Optics}, volume = 38, + number = 27, pages = 5703, publisher = {Departamento de Optica, Facultad de Ciencias, Universidad de Granada, Granada 18071, Spain.}, @@ -1410,8 +1614,6 @@ @article{Hernandez-Andres1999a epicenters for different CCT ranges, our simple equation is accurate across wide chromaticity and CCT ranges (3000-10(6) K) spanned by daylight and skylight.}, - journal = {Applied Optics}, - number = 27, } @misc{Hewlett-PackardDevelopmentCompany2009a, title = {Understanding the HP DreamColor LP2480zx DCI-P3 @@ -1431,20 +1633,34 @@ @misc{Houston2015a author = {Houston, Jim}, year = 2015, } +@article{Huang2015, + title = {Power functions improving the performance of + color-difference formulas}, + author = {Huang, Min and Cui, Guihua and Melgosa, Manuel and + {S{\'a}nchez-Mara{\~n}{\'o}n}, Manuel and Li, Changjun and Luo, M. + Ronnier and Liu, Haoxue}, + year = 2015, + journal = {Optical Society of America}, + volume = 23, + number = 1, + pages = {597--610}, + issn = {1094-4087}, + doi = {10.1364/OE.23.000597}, +} @article{Hung1995, title = {Determination of constant Hue Loci for a CRT gamut and their predictions using color appearance spaces}, author = {Hung, Po-Chieh and Berns, Roy S.}, year = 1995, month = oct, + journal = {Color Research \& Application}, volume = 20, + number = 5, pages = {285--295}, issn = 03612317, doi = {10.1002/col.5080200506}, - journal = {Color Research \& Application}, keywords = {color appearance spaces,experiments to evaluate color space hue linearity,perceived hue}, - number = 5, } @book{Hunt2004b, title = {The Reproduction of Colour}, @@ -1509,13 +1725,6 @@ @book{IESComputerCommittee2014a publisher = {Illuminating Engineering Society}, isbn = {978-0-87995-295-2}, } -@misc{ISO2002, - title = {INTERNATIONAL STANDARD 7589-2002 - Photography - - Illuminants for sensitometry - Specifications for daylight, - incandescent tungsten and printer}, - author = {{ISO}}, - year = 2002, -} @misc{InternationalColorConsortium2010, title = {Specification ICC.1:2010 (Profile version 4.3.0.0)}, author = {{International Color Consortium}}, @@ -1532,6 +1741,21 @@ @misc{InternationalElectrotechnicalCommission1999a pages = 51, url = {https://webstore.iec.ch/publication/6169}, } +@misc{InternationalOrganizationforStandardization2002, + title = {INTERNATIONAL STANDARD ISO 7589-2002 - Photography - + Illuminants for sensitometry - Specifications for daylight, + incandescent tungsten and printer}, + author = {{International Organization for Standardization}}, + year = 2002, +} +@misc{InternationalOrganizationforStandardization2012, + title = {INTERNATIONAL STANDARD ISO 17321-1 - Graphic + technology and photography - Colour characterisation of digital + still cameras (DSCs) - Part 1: Stimuli, metrology and test + procedures}, + author = {{International Organization for Standardization}}, + year = 2012, +} @misc{InternationalTelecommunicationUnion1998a, title = {Recommendation ITU-R BT.470-6 - CONVENTIONAL TELEVISION SYSTEMS}, @@ -1623,13 +1847,44 @@ @article{Jakob2019 author = {Jakob, Wenzel and Hanika, Johannes}, year = 2019, month = may, + journal = {Computer Graphics Forum}, volume = 38, + number = 2, pages = {147--155}, issn = {0167-7055, 1467-8659}, doi = {10.1111/cgf.13626}, - journal = {Computer Graphics Forum}, - language = {en}, - number = 2, + langid = {english}, +} +@inproceedings{Jiang2013, + title = {What is the space of spectral sensitivity functions + for digital color cameras?}, + booktitle = {2013 IEEE Workshop on Applications of Computer + Vision (WACV)}, + author = {Jiang, Jun and Liu, Dengyu and Gu, Jinwei and + Susstrunk, Sabine}, + year = 2013, + month = jan, + pages = {168--179}, + publisher = {IEEE}, + issn = 21583978, + doi = {10.1109/WACV.2013.6475015}, + abstract = {Camera spectral sensitivity functions relate scene + radiance with captured RGB triplets. They are important for many + computer vision tasks that use color information, such as + multispectral imaging, color rendering, and color constancy. In + this paper, we aim to explore the space of spectral sensitivity + functions for digital color cameras. After collecting a database + of 28 cameras covering a variety of types, we find this space + convex and two-dimensional. Based on this statistical model, we + propose two methods to recover camera spectral sensitivities using + regular reflective color targets (e.g., color checker) from a + single image with and without knowing the illumination. We show + the proposed model is more accurate and robust for estimating + camera spectral sensitivities than other basis functions. We also + show two applications for the recovery of camera spectral + sensitivities - simulation of color rendering for cameras and + computational color constancy.}, + isbn = {978-1-4673-5054-9}, } @article{Kang2002a, title = {Design of advanced color: Temperature control system @@ -1637,13 +1892,13 @@ @article{Kang2002a author = {Kang, Bongsoon and Moon, Ohak and Hong, Changhee and Lee, Honam and Cho, Bonghwan and Kim, Youngsun}, year = 2002, + journal = {Journal of the Korean Physical Society}, volume = 41, + number = 6, pages = {865--871}, url = {http://cat.inist.fr/?aModele=afficheN\&cpsidt=14448733}, urldate = {2014-09-25}, - journal = {Journal of the Korean Physical Society}, keywords = {chromaticity,cie-xyz,color temperature,hdtv}, - number = 6, } @misc{Kienzle2011a, title = {refl1d.numpyerrors - Refl1D v0.6.19 documentation}, @@ -1652,6 +1907,34 @@ @misc{Kienzle2011a url = {http://www.reflectometry.org/danse/docs/refl1d/_modules/refl1d/numpyerrors.html}, urldate = {2015-01-30}, } +@article{Kim2009, + title = {Modeling Human Color Perception under Extended + Luminance Levels}, + author = {Kim, Mh and Weyrich, T and Kautz, J}, + year = 2009, + journal = {ACM Transactions on Graphics}, + volume = 28, + number = 3, + pages = {27:1--27:9}, + issn = 07300301, + doi = {10.1145/1531326.1531333}, + abstract = {Display technology is advancing quickly with peak + luminance increasing significantly, enabling high-dynamic-range + displays. However, perceptual color appearance under extended + luminance levels has not been studied, mainly due to the + unavailability of psychophysical data. Therefore, we conduct a + psychophysical study in order to acquire appearance data for many + different luminance levels (up to 16,860 cd/m(2)) covering most of + the dynamic range of the human visual system. These experimental + data allow us to quantify human color perception under extended + luminance levels, yielding a generalized color appearance model. + Our proposed appearance model is efficient, accurate and + invertible. It can be used to adapt the tone and color of images + to different dynamic ranges for cross-media reproduction while + maintaining appearance that is close to human perception.}, + isbn = {978-1-60558-726-4}, + keywords = {color appearance,color reproduction,psychophysics}, +} @misc{Kirk2006, title = {Truelight Software Library 2.0}, author = {Kirk, Richard}, @@ -1659,18 +1942,58 @@ @misc{Kirk2006 url = {https://www.filmlight.ltd.uk/pdf/whitepapers/FL-TL-TN-0057-SoftwareLib.pdf}, urldate = {2017-07-08}, } +@article{Konovalenko2021, + title = {ProLab: perceptually uniform projective colour + coordinate system}, + shorttitle = {ProLab}, + author = {Konovalenko, Ivan A. and Smagina, Anna A. and + Nikolaev, Dmitry P. and Nikolaev, Petr P.}, + year = 2021, + month = jan, + journal = {arXiv:2012.07653 [cs]}, + eprint = {2012.07653}, + eprinttype = {arxiv}, + primaryclass = {cs}, + url = {http://arxiv.org/abs/2012.07653}, + urldate = {2021-08-28}, + abstract = {In this work, we propose proLab: a new colour + coordinate system derived as a 3D projective transformation of CIE + XYZ. We show that proLab is far ahead of the widely used CIELAB + coordinate system (though inferior to the modern CAM16-UCS) + according to perceptual uniformity evaluated by the STRESS metric + in reference to the CIEDE2000 colour difference formula. At the + same time, angular errors of chromaticity estimation that are + standard for linear colour spaces can also be used in proLab since + projective transformations preserve the linearity of manifolds. + Unlike in linear spaces, angular errors for different hues are + normalized according to human colour discrimination thresholds + within proLab. We also demonstrate that shot noise in proLab is + more homoscedastic than in CAM16-UCS or other standard colour + spaces. This makes proLab a convenient coordinate system in which + to perform linear colour analysis.}, + archiveprefix = {arXiv}, + langid = {english}, + keywords = {⛔ No DOI found,Computer Science - Computer Vision + and Pattern Recognition}, +} +@misc{Konovalenko2021a, + title = {proLab\_param.m}, + author = {Konovalenko, Ivan A.}, + year = 2021, + url = {https://github.com/konovalenko-iitp/proLab/blob/71a81bf9c49d4477ccf8a9c196ded93b5b604299/matlab/proLab_color_conversions/proLab_param.m}, +} @article{Krystek1985b, title = {An algorithm to calculate correlated colour temperature}, author = {Krystek, M}, year = 1985, + journal = {Color Research \& Application}, volume = 10, + number = 1, pages = {38--40}, publisher = {Wiley Subscription Services, Inc., A Wiley Company}, issn = 03612317, doi = {10.1002/col.5080100109}, - journal = {Color Research \& Application}, - number = 1, } @misc{Laurent2012a, title = {Reproducibility of python pseudo-random numbers @@ -1686,7 +2009,9 @@ @article{Li2002a and Hunt, Robert W. G.}, year = 2002, month = feb, + journal = {Color Research \& Application}, volume = 27, + number = 1, pages = {49--58}, issn = {0361-2317}, doi = {10.1002/col.10005}, @@ -1705,9 +2030,7 @@ @article{Li2002a reversibility, but also gives a more accurate prediction to almost all experimental data sets than does the original transform. (C) 2002 John Wiley \& Sons, Inc.}, - journal = {Color Research \& Application}, keywords = {Chromatic adaptation,Color appearance}, - number = 1, } @misc{Li2007e, title = {The Problem with CAT02 and Its Correction}, @@ -1724,16 +2047,16 @@ @article{Li2017 and Brill, Michael H and Pointer, Michael}, year = 2017, month = dec, + journal = {Color Research \& Application}, volume = 42, + number = 6, pages = {703--718}, issn = 03612317, doi = {10.1002/col.22131}, - journal = {Color Research \& Application}, keywords = {CAM02-UCS,CAT02,chromatic adaptation,color-appearance models,color-difference evaluation CIECAM02,corresponding color datasets,LUTCHI color-appearance datasets}, - number = 6, } @misc{Lindbloom2003c, title = {Delta E (CIE 1976)}, @@ -1812,7 +2135,9 @@ @article{Lu2016c Tao and Husak, Walt and Pytlarz, Jaclyn and Atkins, Robin and Froehlich, Jan and Su, Guan-Ming}, year = 2016, + journal = {ZTE Communications}, volume = 14, + number = 1, pages = {32--38}, url = {http://www.cnki.net/kcms/detail/34.1294.TN.20160205.1903.006.html}, abstract = {High Dynamic Range (HDR) and Wider Colour Gamut @@ -1833,16 +2158,16 @@ @article{Lu2016c chroma subsampling, and it also has good compression efficiency. Therefore it is desirable to adopt ITP colour space as a new signal format for HDR/WCG video compression.}, - journal = {ZTE Communications}, keywords = {HDR,ICT CP,ITP,WCG,Y′CbCr}, - number = 1, } @article{Luo1996b, title = {The LLAB (l:c) colour model}, author = {Luo, Ming Ronnier and Lo, Mei-Chun and Kuo, Wen-Guey}, year = 1996, month = dec, + journal = {Color Research \& Application}, volume = 21, + number = 6, pages = {412--429}, publisher = {Wiley Subscription Services, Inc., A Wiley Company}, issn = {0361-2317}, @@ -1868,11 +2193,9 @@ @article{Luo1996b model does not give predictions for chroma (as distinct from colourfulness), or for brightness, and it does not include any rod response. \textcopyright{} 1996 John Wiley \& Sons, Inc.}, - journal = {Color Research \& Application}, keywords = {chromatic adaptation transform,colour appearance,colour appearance model,colour difference,colour difference formula,corresponding colours,uniform colour space}, - number = 6, } @inproceedings{Luo1996c, title = {Two Unsolved Issues in Colour Management - Colour @@ -1890,7 +2213,9 @@ @article{Luo1999 author = {Luo, M. Ronnier and Rhodes, Peter A.}, year = 1999, month = aug, + journal = {Color Research \& Application}, volume = 24, + number = 4, pages = {295--296}, issn = {0361-2317}, doi = {10.1002/(SICI)1520-6378(199908)24:4<295::AID-COL10>3.0.CO;2-K}, @@ -1916,8 +2241,6 @@ @article{Luo1999 that the optimized polypeptide protocol is most accurate for extended peptides of limited size and number of formal charges, defining a domain of applicability for this approach.}, - journal = {Color Research \& Application}, - number = 4, } @article{Luo2006b, title = {Uniform colour spaces based on CIECAM02 colour @@ -1925,7 +2248,9 @@ @article{Luo2006b author = {Luo, M. Ronnier and Cui, Guihua and Li, Changjun}, year = 2006, month = aug, + journal = {Color Research \& Application}, volume = 31, + number = 4, pages = {320--330}, issn = {0361-2317}, doi = {10.1002/col.20227}, @@ -1951,11 +2276,9 @@ @article{Luo2006b Periodicals, Inc. Col Res Appl, 31, 320-330, 2006; Published online in Wiley InterScience DOI 10.1002/col.20227}, isbn = {0361-2317}, - journal = {Color Research \& Application}, keywords = {Colour appearance data,Colour appearance model,Colour difference data,Colour difference formula,Uniform colour space}, - number = 4, } @incollection{Luo2013, title = {CIECAM02 and Its Recent Developments}, @@ -1977,7 +2300,9 @@ @article{MacAdam1935a author = {MacAdam, David L.}, year = 1935, month = nov, + journal = {Journal of the Optical Society of America}, volume = 25, + number = 11, pages = {361--367}, publisher = {OSA}, doi = {10.1364/JOSA.25.000361}, @@ -1992,15 +2317,15 @@ @article{MacAdam1935a Tables have been prepared showing the maximum visual efficiency as a function of excitation purity for twenty-four dominant wave-lengths.}, - journal = {Journal of the Optical Society of America}, - number = 11, } @article{Macadam1942, title = {Visual Sensitivities to Color Differences in Daylight}, author = {Macadam, David L.}, year = 1942, + journal = {Journal of the Optical Society of America}, volume = 32, + number = 5, pages = 28, issn = {0030-3941}, doi = {10.1364/JOSA.32.000247}, @@ -2043,8 +2368,6 @@ @article{Macadam1942 of purity differences from a neutral stimulus, as functions of dominant wave-length.}, isbn = {0030-3941}, - journal = {Journal of the Optical Society of America}, - number = 5, } @article{Machado2009, title = {A Physiologically-based Model for Simulation of @@ -2052,7 +2375,10 @@ @article{Machado2009 author = {Machado, G.M. and Oliveira, M.M. and Fernandes, L.}, year = 2009, month = nov, + journal = {IEEE Transactions on Visualization and Computer + Graphics}, volume = 15, + number = 6, pages = {1291--1298}, issn = {1077-2626}, doi = {10.1109/TVCG.2009.113}, @@ -2072,13 +2398,10 @@ @article{Machado2009 visualization experiences for individuals with CVD. It also provides a framework for testing hypotheses about some aspects of the retinal photoreceptors in color vision deficient individuals.}, - journal = {IEEE Transactions on Visualization and Computer - Graphics}, + pmid = 19834201, keywords = {Anomalous Trichromacy,Color Perception,Dichromacy,Models of Color Vision,Simulation of Color Vision Deficiency}, - number = 6, - pmid = 19834201, } @misc{Machado2010a, title = {A model for simulation of color vision deficiency @@ -2096,6 +2419,8 @@ @article{Mallett2019 sRGB Reflectance}, author = {Mallett, Ian and Yuksel, Cem}, year = 2019, + journal = {Eurographics Symposium on Rendering - DL-only and + Industry Track}, pages = {7 pages}, publisher = {The Eurographics Association}, issn = {1727-3463}, @@ -2123,8 +2448,6 @@ @article{Mallett2019 discuss important optimizations and generalization to other RGB spaces.}, isbn = 9783038680956, - journal = {Eurographics Symposium on Rendering - DL-only and - Industry Track}, keywords = {Computing methodologies,Reflectance modeling}, } @misc{Malvar2003, @@ -2164,6 +2487,22 @@ @misc{Mansencald author = {Mansencal, Thomas}, url = {https://github.com/KelSolaar/Foundations/blob/develop/foundations/data_structures.py}, } +@article{Martinez-Verdu2007, + title = {Computation and visualization of the MacAdam limits + for any lightness, hue angle, and light source}, + author = {{Mart{\'i}nez-Verd{\'u}}, Francisco and Perales, + Esther and Chorro, Elisabet and {de Fez}, Dolores and Viqueira, + Valent{\'i}n and Gilabert, Eduardo}, + year = 2007, + month = jun, + journal = {Journal of the Optical Society of America A}, + volume = 24, + number = 6, + pages = 1501, + issn = {1084-7529, 1520-8532}, + doi = {10.1364/JOSAA.24.001501}, + langid = {english}, +} @misc{Melgosa2013b, title = {CIE / ISO new standard: CIEDE2000}, author = {Melgosa, Manuel}, @@ -2177,12 +2516,12 @@ @article{Meng2015c Johannes and Dachsbacher, Carsten}, year = 2015, month = jul, + journal = {Computer Graphics Forum}, volume = 34, + number = 4, pages = {31--40}, issn = 01677055, doi = {10.1111/cgf.12676}, - journal = {Computer Graphics Forum}, - number = 4, } @misc{Miller2014a, title = {A Perceptual EOTF for Extended Dynamic Range Imagery}, @@ -2196,6 +2535,7 @@ @article{Mokrzycki2011 author = {Mokrzycki, Wojciech and Tatol, Maciej}, year = 2011, month = apr, + journal = {Machine Graphics and Vision}, volume = 20, pages = {383--411}, url = {https://www.researchgate.net/publication/236023905_Color_difference_Delta_E_-_A_survey}, @@ -2210,14 +2550,15 @@ @article{Mokrzycki2011 popular colors spaces, as both linear and nonlinear due to perceptual abilities, and are briefly discussed and compared to the sample values.}, - journal = {Machine Graphics and Vision}, keywords = {⛔ No DOI found}, } @article{Moroney2003, title = {A Radial Sampling of the OSA Uniform Color Scales}, author = {Moroney, Nathan}, year = 2003, + journal = {Color and Imaging Conference}, volume = 2003, + number = 1, pages = {175--180}, issn = {2166-9635}, url = {https://www.ingentaconnect.com/content/ist/cic/2003/00002003/00000001/art00031}, @@ -2236,12 +2577,10 @@ @article{Moroney2003 of five different color spaces.}, eissn = {2169-2629}, itemtype = {ARTICLE}, - journal = {Color and Imaging Conference}, - keywords = {⛔ No DOI found}, - number = 1, parent_itemid = {infobike://ist/cic}, publication_date = {2003-01-01T00:00:00}, publishercode = {ist}, + keywords = {⛔ No DOI found}, } @article{Moroneya, title = {The CIECAM02 color appearance model}, @@ -2249,6 +2588,8 @@ @article{Moroneya Robert W. G. and Li, Changjun and Luo, Ming Ronnier and Newman, Todd}, year = 2002, + journal = {Color and Imaging Conference}, + number = 1, pages = {23--27}, url = {http://www.ingentaconnect.com/content/ist/cic/2002/00002002/00000001/art00006}, urldate = {2014-09-27}, @@ -2262,15 +2603,15 @@ @article{Moroneya the calculations for the perceptual attribute correlates. The format of this paper is an annotated description of the forward equations for the model.}, - journal = {Color and Imaging Conference}, - number = 1, } @article{Morovic2000, title = {Calculating medium and image gamut boundaries for gamut mapping}, author = {Morovi{\v c}, J{\'a}n and Luo, M. Ronnier}, year = 2000, + journal = {Color Research and Application}, volume = 25, + number = 6, pages = {394--401}, issn = 03612317, doi = {10.1002/1520-6378(200012)25:63.0.CO;2-Y}, @@ -2283,10 +2624,8 @@ @article{Morovic2000 the article, the focus is both on colour reproduction media and colour images as well as on the suitability of the methods for use in gamut mapping. \textcopyright{} 2000 John Wiley \& Sons. Inc.}, - journal = {Color Research and Application}, keywords = {Cross-media reproduction,Gamut boundary calculation,Gamut mapping}, - number = 6, } @misc{MunsellColorScienceb, title = {Macbeth Colorchecker}, @@ -2318,15 +2657,55 @@ @article{Nayatani1995a Kenjiro Hashimoto Tadashi}, year = 1995, month = jun, + journal = {Color Research \& Application}, volume = 20, + number = 3, pages = {156--167}, publisher = {Wiley Subscription Services, Inc., A Wiley Company}, issn = 03612317, doi = {10.1002/col.5080200305}, - journal = {Color Research \& Application}, keywords = {color-vision model,lightness dependency of chroma,nonlinear color-appearance model}, - number = 3, +} +@article{Nayatani1997, + title = {Simple estimation methods for the + Helmholtz\textemdash Kohlrausch effect}, + author = {Nayatani, Yoshinobu}, + year = 1997, + journal = {Color Research \& Application}, + volume = 22, + number = 6, + pages = {385--401}, + issn = {1520-6378}, + doi = {10.1002/(SICI)1520-6378(199712)22:6<385::AID-COL6>3.0.CO;2-R}, + abstract = {Four kinds of simple estimation equations are + proposed for the Helmholtz\textemdash Kohlrausch effect. Two of + them can be used for luminous colors, and the other two for object + colors. In each of luminous and object colors, the two estimation + equations are given to each of the Variable-Achromatic-Color (VAC) + and the Variable-Chromatic-Color (VCC) methods. All the equations + are similar in type to the Ware\textemdash Cowan equation. They + give the ratio between luminance (or metric lightness) of test + color stimulus and its equivalent luminance (or equivalent + lightness) directly. Though their computations are simple, they + can apply to various H\textemdash K effects including their + adapting luminance dependency. The applicable fields of the + proposed equations are wider than those of the Ware\textemdash + Cowan equation. The proposed equations can be applied to predict + the H\textemdash K effect within the whole chromaticity gamut + including spectral colors, spectral luminosity functions based on + direct color matching from 0.01 Td to 100 000 Td using the + photopic and the scotopic spectral luminosity functions specified + by CIE, equivalent lightness values of NCS colors, and others. + \textcopyright{} 1997 John Wiley \& Sons, Inc. Col Res Appl. 22, + 385\textendash 401, 1997}, + copyright = {Copyright \textcopyright{} 1997 John Wiley \& Sons, + Inc.}, + langid = {english}, + keywords = {CIELUV formula,color appearance,equivalent + lightness,equivalent luminance,Helmholtz—Kohlrausch effect}, + annotation = {\_eprint: + https://onlinelibrary.wiley.com/doi/pdf/10.1002/\%28SICI\%291520-6378\%28199712\%2922\%3A6\%3C385\%3A\%3AAID-COL6\%3E3.0.CO\%3B2-R}, } @article{Newhall1943a, title = {Final Report of the OSA Subcommittee on the Spacing @@ -2335,7 +2714,9 @@ @article{Newhall1943a Deane B.}, year = 1943, month = jul, + journal = {Journal of the Optical Society of America}, volume = 33, + number = 7, pages = 385, issn = {0030-3941}, doi = {10.1364/JOSA.33.000385}, @@ -2352,15 +2733,23 @@ @article{Newhall1943a ideal of psychological equispacing and precise applicability. The new solid is defined in terms of the I.C.I. standard coordinate system and Illuminant C.}, - journal = {Journal of the Optical Society of America}, - number = 7, +} +@misc{Nikon2018, + title = {N-Log Specification Document - Version 1.0.0}, + author = {{Nikon}}, + year = 2018, + pages = {1--5}, + url = {http://download.nikonimglib.com/archive3/hDCmK00m9JDI03RPruD74xpoU905/N-Log_Specification_(En)01.pdf}, + urldate = {2019-09-09}, } @article{Ohno2005, title = {Spectral design considerations for white LED color rendering}, author = {Ohno, Yoshi}, year = 2005, + journal = {Optical Engineering}, volume = 44, + number = 11, pages = 111302, issn = {0091-3286}, doi = {10.1117/1.2130694}, @@ -2380,8 +2769,6 @@ @article{Ohno2005 demonstrated several problems with the current CRI, and the need for improvements is discussed.}, isbn = 3018408551, - journal = {Optical Engineering}, - number = 11, } @misc{Ohno2008a, title = {NIST CQS simulation}, @@ -2393,21 +2780,21 @@ @misc{Ohno2013 title = {NIST CQS simulation}, author = {Ohno, Yoshiro and Davis, Wendy}, year = 2013, - url = {https://www.researchgate.net/file.PostFileLoader.html?id=5541c498f15bc7cc2c8b4578\&assetKey=AS\%3A273582771376136\%401442238623549}, + url = {https://www.researchgate.net/file.PostFileLoader.html?id=5541c498f15bc7cc2c8b4578\&assetKey=AS%3A273582771376136%401442238623549}, } @article{Ohno2014a, title = {Practical Use and Calculation of CCT and Duv}, author = {Ohno, Yoshiro}, year = 2014, month = jan, + journal = {LEUKOS}, volume = 10, + number = 1, pages = {47--55}, issn = {1550-2724}, doi = {10.1080/15502724.2014.839020}, - journal = {LEUKOS}, keywords = {chromaticity,correlated color temperature,duv,Duv,light source,planckian locus,Planckian locus}, - number = 1, } @misc{Ohta1997a, title = {The basis of color reproduction engineering}, @@ -2422,17 +2809,24 @@ @article{Otsu2018 author = {Otsu, H. and Yamamoto, M. and Hachisuka, T.}, year = 2018, month = sep, + journal = {Computer Graphics Forum}, volume = 37, + number = 6, pages = {370--381}, issn = 01677055, doi = {10.1111/cgf.13332}, - journal = {Computer Graphics Forum}, + langid = {english}, keywords = {3,7,according to acm ccs,and texture,categories and subject descriptors,color,computer graphics,i,realism,shading,shadowing,spectral reflectance reconstruction,spectral rendering,three-dimensional graphics and}, - language = {en}, - number = 6, +} +@misc{Ottosson2020, + title = {A perceptual color space for image processing}, + author = {Ottosson, Bj{\"o}rn}, + year = 2020, + url = {https://bottosson.github.io/posts/oklab/}, + urldate = {2020-12-24}, } @misc{Panasonic2014a, title = {VARICAM V-Log/V-Gamut}, @@ -2447,10 +2841,17 @@ @misc{Pointer1980a year = 1980, url = {http://www.cis.rit.edu/research/mcsl2/online/PointerData.xls}, } +@misc{REDDigitalCinema2017, + title = {White Paper on REDWideGamutRGB and Log3G10}, + author = {{RED Digital Cinema}}, + year = 2017, + url = {https://www.red.com/download/white-paper-on-redwidegamutrgb-and-log3g10}, + urldate = {2021-01-16}, +} @misc{Reitza, title = {CaseInsensitiveDict}, author = {Reitz, Kenneth}, - url = {https://github.com/kennethreitz/requests/blob/v1.2.3/requests/structures.py\#L37}, + url = {https://github.com/kennethreitz/requests/blob/v1.2.3/requests/structures.py#L37}, } @misc{RenewableResourceDataCenter2003a, title = {Reference Solar Spectral Irradiance: ASTM G-173}, @@ -2478,7 +2879,9 @@ @article{Safdar2017 and Luo, Ming Ronnier}, year = 2017, month = jun, + journal = {Optics Express}, volume = 25, + number = 13, pages = 15131, issn = {1094-4087}, doi = {10.1364/OE.25.015131}, @@ -2496,10 +2899,70 @@ @article{Safdar2017 and its performance in predicting a wide range of experimental data is presented in comparison with the other state of the art color spaces.}, - journal = {Optics Express}, keywords = {and visual optics,color,Color,Color vision,Colorimetry,Vision}, - number = 13, +} +@article{Safdar2018, + ids = {Safdar2019}, + title = {A Colour Appearance Model based on J z a z b z + Colour Space}, + author = {Safdar, Muhammad and Hardeberg, Jon Y. and Kim, Youn + Jin and Luo, Ming Ronnier}, + year = 2018, + month = nov, + journal = {Color and Imaging Conference}, + volume = 2018, + number = 1, + pages = {96--101}, + issn = {2166-9635}, + doi = {10.2352/ISSN.2169-2629.2018.26.96}, + abstract = {The current CIE colour appearance model CIECAM02 and + its variant named CAM16 can well predict common colour appearance + attributes including lightness, brightness, chroma, colourfulness, + saturation, hue angle, and hue composition. These models are + complicated as well as have mathematical problems. The current + study aimed a new colour appearance model based on Jzazbz color + space to obtain either better or similar performance compared with + CAM16 but the new model should be computationally simple and + robust. Such a model will particularly be suitable for color + management in high dynamic range and wide color gamut + applications. A range of experimental data were collected and a + set of equations was derived. Some initial test results are + presented in this paper.}, + isbn = 9780892083374, +} +@article{Safdar2021, + title = {ZCAM, a colour appearance model based on a high + dynamic range uniform colour space}, + author = {Safdar, Muhammad and Hardeberg, Jon Yngve and + Ronnier Luo, Ming}, + year = 2021, + month = feb, + journal = {Optics Express}, + volume = 29, + number = 4, + pages = 6036, + issn = {1094-4087}, + doi = {10.1364/OE.413659}, + abstract = {A colour appearance model based on a uniform colour + space is proposed. The proposed colour appearance model, ZCAM, + comprises of comparatively simple mathematical equations, and + plausibly agrees with the psychophysical phenomenon of colour + appearance perception. ZCAM consists of ten colour appearance + attributes including brightness, lightness, colourfulness, chroma, + hue angle, hue composition, saturation, vividness, blackness, and + whiteness. Despite its relatively simpler mathematical structure, + ZCAM performed at least similar to the CIE standard colour + appearance model CIECAM02 and its revision, CAM16, in predicting a + range of reliable experimental data.}, + langid = {english}, +} +@misc{Sarifuddin2005, + title = {A New Perceptually Uniform Color Space with + Associated Color Similarity Measure for ContentBased Image and + Video Retrieval}, + author = {Sarifuddin, Madenda and Missaoui, Rokia}, + year = 2005, } @misc{Sastanina, title = {How to make scipy.interpolate give an extrapolated @@ -2515,7 +2978,9 @@ @article{Sharma2005b author = {Sharma, Gaurav and Wu, Wencheng and Dalal, Edul N.}, year = 2005, month = feb, + journal = {Color Research \& Application}, volume = 30, + number = 1, pages = {21--30}, issn = {0361-2317}, doi = {10.1002/col.20070}, @@ -2534,10 +2999,8 @@ @article{Sharma2005b the first author's website. Finally, we also point out\textbackslash nsmall mathematical discontinuities in the formula.}, - journal = {Color Research \& Application}, keywords = {CIE,CIE94,CIEDE2000,CIELAB,CMC,Color-difference metrics}, - number = 1, } @misc{Shirley2015a, title = {The prismatic color space for rgb computations}, @@ -2578,7 +3041,9 @@ @article{Smits1999a author = {Smits, Brian}, year = 1999, month = jan, + journal = {Journal of Graphics Tools}, volume = 4, + number = 4, pages = {11--22}, publisher = {AK Peters, Ltd.}, issn = {1086-7651}, @@ -2588,8 +3053,6 @@ @article{Smits1999a using a full spectral representation, as RGB represen- tations have limitations in some situations4. The spectral representation does come at some cost, not ...}, - journal = {Journal of Graphics Tools}, - number = 4, } @book{SocietyofMotionPictureandTelevisionEngineers1993a, title = {RP 177:1993 - Derivation of Basic Television Color @@ -2597,6 +3060,7 @@ @book{SocietyofMotionPictureandTelevisionEngineers1993a author = {{Society of Motion Picture and Television Engineers}}, year = 1993, month = jan, + journal = {RP 177:1993}, volume = {RP 177:199}, publisher = {The Society of Motion Picture and Television Engineers}, @@ -2612,7 +3076,6 @@ @book{SocietyofMotionPictureandTelevisionEngineers1993a reference primaries to another set of reference primaries or to a set of display primaries.}, isbn = {978-1-61482-191-5}, - journal = {RP 177:1993}, } @misc{SocietyofMotionPictureandTelevisionEngineers1999b, title = {ANSI/SMPTE 240M-1995 - Signal Parameters - 1125-Line @@ -2620,13 +3083,14 @@ @misc{SocietyofMotionPictureandTelevisionEngineers1999b author = {{Society of Motion Picture and Television Engineers}}, year = 1999, pages = {1--7}, - url = {http://car.france3.mars.free.fr/HD/INA-\%2026\%20jan\%2006/SMPTE\%20normes\%20et\%20confs/s240m.pdf}, + url = {http://car.france3.mars.free.fr/HD/INA-%2026%20jan%2006/SMPTE%20normes%20et%20confs/s240m.pdf}, } @book{SocietyofMotionPictureandTelevisionEngineers2004a, title = {RP 145:2004: SMPTE C Color Monitor Colorimetry}, author = {{Society of Motion Picture and Television Engineers}}, year = 2004, month = jan, + journal = {RP 145:2004}, volume = {RP 145:200}, publisher = {The Society of Motion Picture and Television Engineers}, @@ -2637,7 +3101,6 @@ @book{SocietyofMotionPictureandTelevisionEngineers2004a professional monitors used in systems based on SMPTE C colorimetry.}, isbn = {978-1-61482-164-9}, - journal = {RP 145:2004}, } @misc{SocietyofMotionPictureandTelevisionEngineers2014a, title = {SMPTE ST 2084:2014 - Dynamic Range Electro-Optical @@ -2655,7 +3118,7 @@ @misc{SonyCorporation title = {S-Log Whitepaper}, author = {{Sony Corporation}}, pages = {1--17}, - url = {http://www.theodoropoulos.info/attachments/076_on\%20S-Log.pdf}, + url = {http://www.theodoropoulos.info/attachments/076_on%20S-Log.pdf}, } @misc{SonyCorporation2012a, title = {S-Log2 Technical Paper}, @@ -2730,13 +3193,13 @@ @article{Stearns1988a author = {Stearns, E. I. and Stearns, R. E.}, year = 1988, month = aug, + journal = {Color Research \& Application}, volume = 13, + number = 4, pages = {257--259}, publisher = {Wiley Subscription Services, Inc., A Wiley Company}, issn = 03612317, doi = {10.1002/col.5080130410}, - journal = {Color Research \& Application}, - number = 4, } @misc{Susstrunk1999a, title = {Standard RGB Color Spaces}, @@ -2951,6 +3414,7 @@ @article{Ward2002 Prefiltering and Sharp Color Primaries}, author = {Ward, Greg and {Eydelberg-Vileshin}, Elena}, year = 2002, + journal = {Eurographics workshop on Rendering}, pages = {117--124}, publisher = {Eurographics Association}, doi = {10.2312/EGWR/EGWR02/117-124}, @@ -2958,7 +3422,6 @@ @article{Ward2002 of many samples over the visible , and advanced tools developed by the research community offer multispectral sampling towards this goal. However, for practical reasons including efficiency, white}, - journal = {Eurographics workshop on Rendering}, } @misc{Ward2016, title = {Private Discussion with Mansencal, T.}, @@ -3038,7 +3501,7 @@ @misc{Wikipedia2001 title = {Approximation}, author = {{Wikipedia}}, year = 2001, - url = {http://en.wikipedia.org/wiki/Color_temperature\#Approximation}, + url = {http://en.wikipedia.org/wiki/Color_temperature#Approximation}, urldate = {2014-06-28}, } @misc{Wikipedia2001a, @@ -3073,14 +3536,14 @@ @misc{Wikipedia2003a title = {Lagrange polynomial - Definition}, author = {{Wikipedia}}, year = 2003, - url = {https://en.wikipedia.org/wiki/Lagrange_polynomial\#Definition}, + url = {https://en.wikipedia.org/wiki/Lagrange_polynomial#Definition}, urldate = {2016-01-20}, } @misc{Wikipedia2003b, title = {Luminosity function}, author = {{Wikipedia}}, year = 2003, - url = {https://en.wikipedia.org/wiki/Luminosity_function\#Details}, + url = {https://en.wikipedia.org/wiki/Luminosity_function#Details}, urldate = {2014-10-20}, } @misc{Wikipedia2003c, @@ -3094,7 +3557,7 @@ @misc{Wikipedia2003d title = {Michaelis-Menten kinetics}, author = {{Wikipedia}}, year = 2003, - url = {https://en.wikipedia.org/wiki/Michaelis\%E2\%80\%93Menten_kinetics}, + url = {https://en.wikipedia.org/wiki/Michaelis%E2%80%93Menten_kinetics}, urldate = {2017-04-29}, } @misc{Wikipedia2003e, @@ -3115,7 +3578,7 @@ @misc{Wikipedia2004a title = {Surfaces}, author = {{Wikipedia}}, year = 2004, - url = {http://en.wikipedia.org/wiki/Gamut\#Surfaces}, + url = {http://en.wikipedia.org/wiki/Gamut#Surfaces}, urldate = {2014-09-10}, } @misc{Wikipedia2004b, @@ -3171,7 +3634,7 @@ @misc{Wikipedia2005d title = {Mesopic weighting function}, author = {{Wikipedia}}, year = 2005, - url = {http://en.wikipedia.org/wiki/Mesopic_vision\#Mesopic_weighting_function}, + url = {http://en.wikipedia.org/wiki/Mesopic_vision#Mesopic_weighting_function}, urldate = {2014-06-20}, } @misc{Wikipedia2006, @@ -3185,14 +3648,14 @@ @misc{Wikipedia2006a title = {White points of standard illuminants}, author = {{Wikipedia}}, year = 2006, - url = {http://en.wikipedia.org/wiki/Standard_illuminant\#White_points_of_standard_illuminants}, + url = {http://en.wikipedia.org/wiki/Standard_illuminant#White_points_of_standard_illuminants}, urldate = {2014-02-24}, } @misc{Wikipedia2007, title = {CAT02}, author = {{Wikipedia}}, year = 2007, - url = {http://en.wikipedia.org/wiki/CIECAM02\#CAT02}, + url = {http://en.wikipedia.org/wiki/CIECAM02#CAT02}, urldate = {2014-02-24}, } @misc{Wikipedia2007a, @@ -3220,7 +3683,7 @@ @misc{Wikipedia2007d title = {The reverse transformation}, author = {{Wikipedia}}, year = 2007, - url = {http://en.wikipedia.org/wiki/CIELUV\#The_reverse_transformation}, + url = {http://en.wikipedia.org/wiki/CIELUV#The_reverse_transformation}, urldate = {2014-02-24}, } @misc{Wikipedia2008, @@ -3248,21 +3711,28 @@ @misc{Wikipedia2008c title = {Relation to CIE XYZ}, author = {{Wikipedia}}, year = 2008, - url = {http://en.wikipedia.org/wiki/CIE_1960_color_space\#Relation_to_CIE_XYZ}, + url = {http://en.wikipedia.org/wiki/CIE_1960_color_space#Relation_to_CIE_XYZ}, urldate = {2014-02-24}, } +@misc{Wikipedia2015, + title = {HCL color space}, + author = {{Wikipedia}}, + year = 2015, + url = {https://en.wikipedia.org/wiki/HCL_color_space}, + urldate = {2021-04-04}, +} @article{Wyszecki1963b, title = {Proposal for a New Color-Difference Formula}, author = {Wyszecki, G{\"u}nter}, year = 1963, month = nov, + journal = {Journal of the Optical Society of America}, volume = 53, + number = 11, pages = 1318, publisher = {OSA}, issn = {0030-3941}, doi = {10.1364/JOSA.53.001318}, - journal = {Journal of the Optical Society of America}, - number = 11, } @incollection{Wyszecki2000, title = {Table 2(5.4.1) MacAdam Ellipses (Observer PGN) @@ -3409,7 +3879,7 @@ @misc{X-Rite2016 Classic Charts}, author = {{X-Rite}}, year = 2016, - url = {http://xritephoto.com/ph_product_overview.aspx?ID=938\&Action=Support\&SupportID=5884\#}, + url = {http://xritephoto.com/ph_product_overview.aspx?ID=938\&Action=Support\&SupportID=5884#}, urldate = {2018-10-29}, } @misc{Yorke2014a, @@ -3420,3 +3890,17 @@ @misc{Yorke2014a url = {http://stackoverflow.com/a/23521245/931625}, urldate = {2015-03-27}, } +@article{Zhai2018, + title = {Study of chromatic adaptation via neutral white + matches on different viewing media}, + author = {Zhai, Qiyan and Luo, Ming R.}, + year = 2018, + month = mar, + journal = {Optics Express}, + volume = 26, + number = 6, + pages = 7724, + issn = {1094-4087}, + doi = {10.1364/OE.26.007724}, + langid = {english}, +} diff --git a/CHANGES.rst b/CHANGES.rst index 562e1b3372..cc89ce4670 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,6 @@ About ----- | **Colour** by Colour Developers -| Copyright © 2013-2020 – Colour Developers – `colour-developers@colour-science.org `__ +| Copyright 2013 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 `__ diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 36f8ab9b38..ee901d68e0 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -10,6 +10,6 @@ About ----- | **Colour** by Colour Developers -| Copyright © 2013-2020 – Colour Developers – `colour-developers@colour-science.org `__ +| Copyright 2013 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 `__ diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index 6d8eea5439..0c00d0c655 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -4,31 +4,31 @@ Contributors The Colour Developers --------------------- -- **Thomas Mansencal**, *Visual Effects Artist @ Weta Digital* +- **Thomas Mansencal**, *Lead Pipeline Developer @ WetaFX* - Project coordination, overall development. + Maintainer, project coordination, overall development. - **Michael Mauderer**, *HCI Researcher @ University of Dundee* - Colour appearance models, overall development. + Maintainer, colour appearance models, overall development. -- **Michael Parsons**, *Colour Scientist @ The Moving Picture Company* +- **Nick Shaw**, *Workflow Consultant @ Antler Post* - Continuous technical support. + Maintainer, Y'CbCr colour encoding, RED colourspaces derivation, LUT IO, continuous technical support. -- **Nick Shaw**, *Workflow Consultant @ Antler Post* +- **Zach Lewis**, *Technical Director @ Method Studios* - YCbCr Colour Encoding, RED Colourspaces Derivation. + Maintainer, CLF, technical support. -- **Luke Canavan** +- **Michael Parsons**, *Colour Scientist @ The Moving Picture Company* - Colour Quality Scale, Luminous Flux. + Maintainer, technical support. -- **Sean Cooper**, *Colour Scientist* +- **Kevin Wheatley**, *Head of Imaging @ Framestore* Technical support. -- **Kevin Wheatley**, *Head of Imaging @ Framestore* +- **Sean Cooper**, *Colour Scientist* Technical support. @@ -54,9 +54,24 @@ Google Summer of Code The Need for Speed. +2021 +~~~~ + +- **Cédric Dollet**, *Student @ CY Tech, France* + + New Colour Appearance Models. + +- **Geetansh Saxena**, *Student @ Cluster Innovation Centre, University of Delhi* + + New Colour Models. + Development & Technical Support ------------------------------- +- **Luke Canavan** + + Colour Quality Scale, luminous flux. + - **Katherine Crowson** CIECAM02 improvements, technical support. @@ -133,6 +148,38 @@ Development & Technical Support Documentation improvements. +- **Jedediah Smith**, *VFX Supervisor @ Method Studios* + + RED Log3G10 improvements. + +- **Frederic Savoir**, *Technology Director @ Amazing Digital Studios* + + DaVinci Intermediate OETF implementation. + +- **Saransh Chopra**, *Student @ Cluster Innovation Centre, University of Delhi* + + HCL colourspace implementation. + +- **Ilia Sibiryakov**, *Student @ University of Portsmouth* + + Helmholtz—Kohlrausch effect implementation. + +- **Tim Gates** + + Documentation improvements. + +- **Gajendra Pal** + + Documentation improvements. + +- **Nicolas Tessore** + + Technical support. + +- **Aurélien Pierre**, *Core Developer @ darktable-org* + + Documentation improvements. + Acknowledgements ---------------- - **Steve Agland**, *Supervising Lighting Technical Director @ Animal Logic* @@ -170,7 +217,7 @@ Acknowledgements Special Mentions ---------------- -- **Aurélia Sellier**, *FX Production Coordinator @ Weta Digital* +- **Aurélia Sellier**, *FX Production Coordinator @ WetaFX* Issues & Discussions -------------------- @@ -187,13 +234,20 @@ Issues & Discussions - Ryan Bass - Vishal Vashistha - Vlad Enache +- @aarondemolder +- @abnormally-distributed +- @ademmler +- @ahemberger - @alban-sol +- @alianoroozi +- @Alt-Shivam - @Ampersandme - @AndersAtEndian - @anshulxyz - @Apoorva-13 - @aurelienbl - @awehring +- @baileyji - @beckstev - @bersbersbers - @brandondube @@ -204,6 +258,7 @@ Issues & Discussions - @chesschi - @ChunHsinWang - @codycuellar +- @daviesj - @dfoxfranke - @dtbulmerJRs - @Edwardlin-zlt @@ -211,59 +266,89 @@ Issues & Discussions - @fangjy88 - @Floschoe - @foutoucour +- @goofy2k +- @gutenzwerg - @habemus-papadum - @hajimen +- @heinemannj - @henczati - @hminle - @iCagarro - @iinnovations - @jaguarondi +- @Jerry2001 +- @JoshuaEbenezer - @KOLANICH +- @KrisKennaway +- @kunal9922 - @Kunkka1988 - @lavrovd - @LeCyberDucky - @Legendin - @leklbk - @lensz +- @lishichengyan - @MarcusCalhoun-Lopez - @matthiasbirkich - @meshing +- @MMehdiMousavi - @mokincha - @monkeywithacupcake +- @MrColourBlind - @Myndex - @naavis - @nadersadoughi +- @Naughty-Monkey +- @NekoAlosama - @NoRoKr - @nschloe +- @parthxtripathi +- @Patil2099 +- @Paul-Sims - @peteroupc - @pfk-beta - @priikone - @Queuecumber +- @ramparvathaneni - @Rob-Welch - @robbuckley +- @romanovar - @Ron024 - @rsnitsch +- @RutNij +- @sdbbs - @senyai - @shanest +- @shirubana +- @shpurdikhub - @spicymatt +- @ssh4net - @stakemura +- @starkcolour +- @starkfan007 - @tashdor - @TFiFiE - @thunders82 - @tingeman +- @tospe - @totyped +- @ujjayants - @ValZapod - @vidakDK +- @volkerjaenisch - @vvclin-git - @Wagyx +- @whornsby - @Willingo +- @willSmallHD +- @wuuawu - @xjossy -- @zachlewis +- @yuhao About ----- | **Colour** by Colour Developers -| Copyright © 2013-2020 – Colour Developers – `colour-developers@colour-science.org `__ +| Copyright 2013 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 `__ diff --git a/LICENSE b/LICENSE index 4e127e4128..5dad9165bf 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013-2020, Colour Developers +Copyright 2013 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 824d00642b..d4267c2122 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,6 @@ -.. image:: https://raw.githubusercontent.com/colour-science/colour-branding/master/images/Colour_Logo_Medium_001.png +.. image:: https://raw.githubusercontent.com/colour-science/colour-branding/master/images/Colour_Logo_001.png + +| .. start-badges @@ -13,14 +15,14 @@ .. |coveralls| image:: http://img.shields.io/coveralls/colour-science/colour/develop.svg?style=flat-square :target: https://coveralls.io/r/colour-science/colour :alt: Coverage Status -.. |codacy| image:: https://img.shields.io/codacy/grade/7d0d61f8e7294533b27ae00ee6f50fb2/develop.svg?style=flat-square +.. |codacy| image:: https://img.shields.io/codacy/grade/1f3b8d3bba7440ba9ebc1170589628b1/develop.svg?style=flat-square :target: https://www.codacy.com/app/colour-science/colour :alt: Code Grade .. |version| image:: https://img.shields.io/pypi/v/colour-science.svg?style=flat-square :target: https://pypi.org/project/colour-science :alt: Package Version -.. |zenodo| image:: https://img.shields.io/badge/DOI-10.5281/zenodo.3757045-blue.svg?style=flat-square - :target: https://dx.doi.org/10.5281/zenodo.3757045 +.. |zenodo| image:: https://img.shields.io/badge/DOI-10.5281/zenodo.4445350-blue.svg?style=flat-square + :target: https://dx.doi.org/10.5281/zenodo.4445350 :alt: DOI .. end-badges @@ -37,7 +39,7 @@ It is freely available under the .. contents:: **Table of Contents** :backlinks: none - :depth: 3 + :depth: 2 .. sectnum:: @@ -174,7 +176,7 @@ If you'd like to join them, please consider - +

Richard Lackey

@@ -182,13 +184,13 @@ If you'd like to join them, please consider - +

Liam Collod

- +

Nick Shaw

@@ -200,7 +202,7 @@ If you'd like to join them, please consider - +

Ilia Sibiryakov

@@ -231,9 +233,10 @@ If you'd like to join them, please consider

Christophe Brejon

- - + + +

Mario Rokicki

@@ -249,57 +252,6 @@ If you'd like to join them, please consider Features -------- -**Colour** features a rich dataset and collection of objects, please see the -`features `__ page for more -information. - -Installation ------------- - -**Colour** and its primary dependencies can be easily installed from the -`Python Package Index `__ -by issuing this command in a shell: - -.. code-block:: bash - - $ pip install --user colour-science - -The detailed installation procedure for the secondary dependencies is -described in the `Installation Guide `__. - -**Colour** is also available for `Anaconda `__ -from *Continuum Analytics* via `conda-forge `__: - -.. code-block:: bash - - $ conda install -c conda-forge colour-science - -Documentation -------------- - -Tutorial -~~~~~~~~ - -The `static tutorial `__ -provides an introduction to **Colour**. An interactive version is available via -`Google Colab `__. - -How-To Guide -~~~~~~~~~~~~ - -The `How-To `__ -guide for **Colour** shows various techniques to solve specific problems and -highlights some interesting use cases. - -API Reference -~~~~~~~~~~~~~ - -The main technical reference for **Colour** and its API is the -`Colour Manual `__. - -Examples -~~~~~~~~ - Most of the objects are available from the ``colour`` namespace: .. code-block:: python @@ -307,7 +259,7 @@ Most of the objects are available from the ``colour`` namespace: >>> import colour Automatic Colour Conversion Graph - ``colour.graph`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Starting with version *0.3.14*, **Colour** implements an automatic colour conversion graph enabling easier colour conversions. @@ -337,7 +289,7 @@ conversion graph enabling easier colour conversions. array([ 0.47924575, 0.31676968, 0.17362725]) Chromatic Adaptation - ``colour.adaptation`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -348,10 +300,10 @@ Chromatic Adaptation - ``colour.adaptation`` ... XYZ, colour.xy_to_XYZ(D65), colour.xy_to_XYZ(A)) array([ 0.2533053 , 0.13765138, 0.01543307]) >>> sorted(colour.CHROMATIC_ADAPTATION_METHODS) - ['CIE 1994', 'CMCCAT2000', 'Fairchild 1990', 'Von Kries'] + ['CIE 1994', 'CMCCAT2000', 'Fairchild 1990', 'Von Kries', 'Zhai 2018'] Algebra - ``colour.algebra`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Kernel Interpolation ******************** @@ -374,7 +326,7 @@ Sprague (1880) Interpolation array([ 6.72951612, 7.81406251, 43.77379185]) Colour Appearance Models - ``colour.appearance`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -383,10 +335,16 @@ Colour Appearance Models - ``colour.appearance`` >>> L_A = 318.31 >>> Y_b = 20.0 >>> colour.XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b) - CAM_Specification_CIECAM02(J=34.434525727858997, C=67.365010921125915, h=22.279164147957076, s=62.814855853327131, Q=177.47124941102123, M=70.024939419291385, H=2.689608534423904, HC=None) + CAM_Specification_CIECAM02(J=34.434525727858997, C=67.365010921125943, h=22.279164147957065, s=62.81485585332716, Q=177.47124941102123, M=70.024939419291414, H=2.6896085344238898, HC=None) + >>> colour.XYZ_to_CAM16(XYZ, XYZ_w, L_A, Y_b) + CAM_Specification_CAM16(J=33.880368498111686, C=69.444353357408033, h=19.510887327451748, s=64.03612114840314, Q=176.03752758512178, M=72.18638534116765, H=399.52975599115319, HC=None) + >>> colour.XYZ_to_Kim2009(XYZ, XYZ_w, L_A) + CAM_Specification_Kim2009(J=19.879918542450902, C=55.839055250876946, h=22.013388165090046, s=112.97979354939129, Q=36.309026130161449, M=46.346415858227864, H=2.3543198369639931, HC=None) + >>> colour.XYZ_to_ZCAM(XYZ, XYZ_w, L_A, Y_b) + CAM_Specification_ZCAM(J=38.347186278956357, C=21.12138989208518, h=33.711578931095197, s=81.444585609489536, Q=76.986725284523772, M=42.403805833900506, H=0.45779200212219573, HC=None, V=43.623590687423544, K=43.20894953152817, W=34.829588380192149) Colour Blindness - ``colour.blindness`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -402,7 +360,7 @@ Colour Blindness - ``colour.blindness`` [ 0.00644047, 0.25921579, 0.73434374]]) Colour Correction - ``colour characterisation`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -416,19 +374,19 @@ Colour Correction - ``colour characterisation`` ['Cheung 2004', 'Finlayson 2015', 'Vandermonde'] ACES Input Transform - ``colour characterisation`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python >>> sensitivities = colour.MSDS_CAMERA_SENSITIVITIES['Nikon 5100 (NPL)'] >>> illuminant = colour.SDS_ILLUMINANTS['D55'] >>> colour.matrix_idt(sensitivities, illuminant) - array([[ 0.46579991, 0.13409239, 0.01935141], - [ 0.01786094, 0.77557292, -0.16775555], - [ 0.03458652, -0.16152926, 0.74270359]]) + (array([[ 0.46579986, 0.13409221, 0.01935163], + [ 0.01786092, 0.77557268, -0.16775531], + [ 0.03458647, -0.16152923, 0.74270363]]), array([ 1.58214188, 1. , 1.28910346])) Colorimetry - ``colour.colorimetry`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Spectral Computations ********************* @@ -515,7 +473,8 @@ Lightness Computation >>> colour.lightness(12.19722535) 41.527875844653451 >>> sorted(colour.LIGHTNESS_METHODS) - ['CIE 1976', + ['Abebe 2017' + 'CIE 1976', 'Fairchild 2010', 'Fairchild 2011', 'Glasser 1958', @@ -563,9 +522,9 @@ Yellowness Computation >>> XYZ = [95.00000000, 100.00000000, 105.00000000] >>> colour.yellowness(XYZ) - 11.065000000000003 + 4.3400000000000034 >>> sorted(colour.YELLOWNESS_METHODS) - ['ASTM D1925', 'ASTM E313'] + ['ASTM D1925', 'ASTM E313', 'ASTM E313 Alternative'] Luminous Flux, Efficiency & Efficacy Computation ************************************************ @@ -583,7 +542,7 @@ Luminous Flux, Efficiency & Efficacy Computation 136.21708031547874 Contrast Sensitivity Function - ``colour.contrast`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -594,7 +553,7 @@ Contrast Sensitivity Function - ``colour.contrast`` Colour Difference - ``colour.difference`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -619,7 +578,7 @@ Colour Difference - ``colour.difference`` 'cie2000'] IO - ``colour.io`` -^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~ Images ****** @@ -654,7 +613,7 @@ Look Up Table (LUT) Data array([ 0.00575674, 0.00181493, 0.00121419]) Colour Models - ``colour.models`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CIE xyY Colourspace ******************* @@ -693,7 +652,7 @@ CIE 1964 U*V*W* Colourspace .. code-block:: python - >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952* 100] + >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952 * 100] >>> colour.XYZ_to_UVW(XYZ) array([ 94.55035725, 11.55536523, 40.54757405]) @@ -702,7 +661,7 @@ Hunter L,a,b Colour Scale .. code-block:: python - >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952* 100] + >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952 * 100] >>> colour.XYZ_to_Hunter_Lab(XYZ) array([ 34.92452577, 47.06189858, 14.38615107]) @@ -711,7 +670,7 @@ Hunter Rd,a,b Colour Scale .. code-block:: python - >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952* 100] + >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952 * 100] >>> colour.XYZ_to_Hunter_Rdab(XYZ) array([ 12.197225 , 57.12537874, 17.46241341]) @@ -720,7 +679,7 @@ CAM02-LCD, CAM02-SCD, and CAM02-UCS Colourspaces - Luo, Cui and Li (2006) .. code-block:: python - >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952* 100] + >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952 * 100] >>> XYZ_w = [95.05, 100.00, 108.88] >>> L_A = 318.31 >>> Y_b = 20.0 @@ -730,13 +689,17 @@ CAM02-LCD, CAM02-SCD, and CAM02-UCS Colourspaces - Luo, Cui and Li (2006) >>> JMh = (specification.J, specification.M, specification.h) >>> colour.JMh_CIECAM02_to_CAM02UCS(JMh) array([ 47.16899898, 38.72623785, 15.8663383 ]) + >>> XYZ = [0.20654008, 0.12197225, 0.05136952] + >>> XYZ_w = [95.05 / 100, 100.00 / 100, 108.88 / 100] + >>> colour.XYZ_to_CAM02UCS(XYZ, XYZ_w=XYZ_w, L_A=L_A, Y_b=Y_b) + array([ 47.16899898, 38.72623785, 15.8663383 ]) CAM16-LCD, CAM16-SCD, and CAM16-UCS Colourspaces - Li et al. (2017) ******************************************************************* .. code-block:: python - >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952* 100] + >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952 * 100] >>> XYZ_w = [95.05, 100.00, 108.88] >>> L_A = 318.31 >>> Y_b = 20.0 @@ -746,13 +709,25 @@ CAM16-LCD, CAM16-SCD, and CAM16-UCS Colourspaces - Li et al. (2017) >>> JMh = (specification.J, specification.M, specification.h) >>> colour.JMh_CAM16_to_CAM16UCS(JMh) array([ 46.55542238, 40.22460974, 14.25288392] + >>> XYZ = [0.20654008, 0.12197225, 0.05136952] + >>> XYZ_w = [95.05 / 100, 100.00 / 100, 108.88 / 100] + >>> colour.XYZ_to_CAM16UCS(XYZ, XYZ_w=XYZ_w, L_A=L_A, Y_b=Y_b) + array([ 46.55542238, 40.22460974, 14.25288392]) + +ICaCb Colourspace +****************** + +.. code-block:: python + + >>> XYZ_to_ICaCb(np.array([0.20654008, 0.12197225, 0.05136952])) + array([ 0.06875297, 0.05753352, 0.02081548]) -IGPGTG Colourspace +IgPgTg Colourspace ****************** .. code-block:: python - >>> colour.XYZ_to_IGPGTG([0.20654008, 0.12197225, 0.05136952]) + >>> colour.XYZ_to_IgPgTg([0.20654008, 0.12197225, 0.05136952]) array([ 0.42421258, 0.18632491, 0.10689223]) IPT Colourspace @@ -763,8 +738,8 @@ IPT Colourspace >>> colour.XYZ_to_IPT([0.20654008, 0.12197225, 0.05136952]) array([ 0.38426191, 0.38487306, 0.18886838]) -DIN99 Colourspace -***************** +DIN99 Colourspace and DIN99b, DIN99c, DIN99d Refined Formulas +************************************************************* .. code-block:: python @@ -788,21 +763,37 @@ hdr-IPT Colourspace >>> colour.XYZ_to_hdr_IPT([0.20654008, 0.12197225, 0.05136952]) array([ 25.18261761, -22.62111297, 3.18511729]) +Oklab Colourspace +***************** + +.. code-block:: python + + >>> colour.XYZ_to_Oklab([0.20654008, 0.12197225, 0.05136952]) + array([ 0.51634019, 0.154695 , 0.06289579]) + OSA UCS Colourspace ******************* .. code-block:: python - >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952* 100] + >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952 * 100] >>> colour.XYZ_to_OSA_UCS(XYZ) array([-3.0049979 , 2.99713697, -9.66784231]) -JzAzBz Colourspace +ProLab Colourspace +****************** + +.. code-block:: python + + >>> colour.XYZ_to_ProLab([0.51634019, 0.15469500, 0.06289579]) + array([1.24610688, 2.39525236, 0.41902126]) + +Jzazbz Colourspace ****************** .. code-block:: python - >>> colour.XYZ_to_JzAzBz([0.20654008, 0.12197225, 0.05136952]) + >>> colour.XYZ_to_Jzazbz([0.20654008, 0.12197225, 0.05136952]) array([ 0.00535048, 0.00924302, 0.00526007]) Y'CbCr Colour Encoding @@ -821,12 +812,12 @@ YCoCg Colour Encoding >>> colour.RGB_to_YCoCg([0.75, 0.75, 0.0]) array([ 0.5625, 0.375 , 0.1875]) -ICTCP Colour Encoding +ICtCp Colour Encoding ********************* .. code-block:: python - >>> colour.RGB_to_ICTCP([0.45620519, 0.03081071, 0.04091952]) + >>> colour.RGB_to_ICtCp([0.45620519, 0.03081071, 0.04091952]) array([ 0.07351364, 0.00475253, 0.09351596]) HSV Colourspace @@ -837,6 +828,14 @@ HSV Colourspace >>> colour.RGB_to_HSV([0.45620519, 0.03081071, 0.04091952]) array([ 0.99603944, 0.93246304, 0.45620519]) +IHLS Colourspace +**************** + +.. code-block:: python + + >>> colour.RGB_to_IHLS([0.45620519, 0.03081071, 0.04091952]) + array([ 6.26236117, 0.12197943, 0.42539448]) + Prismatic Colourspace ********************* @@ -895,6 +894,7 @@ RGB Colourspaces 'Apple RGB', 'Best RGB', 'Beta RGB', + 'Blackmagic Wide Gamut', 'CIE RGB', 'Cinema Gamut', 'ColorMatch RGB', @@ -952,24 +952,14 @@ OETFs >>> sorted(colour.OETFS) ['ARIB STD-B67', + 'Blackmagic Film Generation 5', + 'DaVinci Intermediate', 'ITU-R BT.2100 HLG', 'ITU-R BT.2100 PQ', 'ITU-R BT.601', 'ITU-R BT.709', 'SMPTE 240M'] -OETFs Inverse -************* - -.. code-block:: python - - >>> sorted(colour.OETF_INVERSES) - ['ARIB STD-B67', - 'ITU-R BT.2100 HLG', - 'ITU-R BT.2100 PQ', - 'ITU-R BT.601', - 'ITU-R BT.709'] - EOTFs ***** @@ -986,21 +976,6 @@ EOTFs 'ST 2084', 'sRGB'] -EOTFs Inverse -************* - -.. code-block:: python - - >>> sorted(colour.EOTF_INVERSES) - ['DCDM', - 'DICOM GSDF', - 'ITU-R BT.1886', - 'ITU-R BT.2020', - 'ITU-R BT.2100 HLG', - 'ITU-R BT.2100 PQ', - 'ST 2084', - 'sRGB'] - OOTFs ***** @@ -1009,14 +984,6 @@ OOTFs >>> sorted(colour.OOTFS) ['ITU-R BT.2100 HLG', 'ITU-R BT.2100 PQ'] -OOTFs Inverse -************* - -.. code-block:: python - - >>> sorted(colour.OOTF_INVERSES) - ['ITU-R BT.2100 HLG', 'ITU-R BT.2100 PQ'] - Log Encoding / Decoding *********************** @@ -1038,6 +1005,7 @@ Log Encoding / Decoding 'Log2', 'Log3G10', 'Log3G12', + 'N-Log', 'PLog', 'Panalog', 'Protune', @@ -1102,7 +1070,7 @@ CCTFs Encoding / Decoding 'sRGB'] Colour Notation Systems - ``colour.notation`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Munsell Value ************* @@ -1132,7 +1100,7 @@ Munsell Colour array([ 0.38736945, 0.35751656, 0.59362 ]) Optical Phenomena - ``colour.phenomena`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -1150,7 +1118,7 @@ Optical Phenomena - ``colour.phenomena`` extrapolator_args={'right': None, 'method': 'Constant', 'left': None}) Light Quality - ``colour.quality`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Colour Fidelity Index ********************* @@ -1189,7 +1157,7 @@ Academy Spectral Similarity Index (SSI) 94.0 Spectral Up-Sampling & Reflectance Recovery - ``colour.recovery`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -1209,7 +1177,7 @@ Spectral Up-Sampling & Reflectance Recovery - ``colour.recovery`` ['Jakob 2019', 'Mallett 2019', 'Meng 2015', 'Otsu 2018', 'Smits 1999'] Correlated Colour Temperature Computation Methods - ``colour.temperature`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -1228,7 +1196,7 @@ Correlated Colour Temperature Computation Methods - ``colour.temperature`` 'mccamy1992'] Colour Volume - ``colour.volume`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -1236,7 +1204,7 @@ Colour Volume - ``colour.volume`` 821958.30000000005 Geometry Primitives Generation - ``colour.geometry`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -1261,7 +1229,7 @@ Geometry Primitives Generation - ``colour.geometry`` ['Cube MPL', 'Grid MPL', 'Quad MPL', 'Sphere'] Plotting - ``colour.plotting`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Most of the objects are available from the ``colour.plotting`` namespace: @@ -1301,11 +1269,11 @@ Blackbody ... blackbody_sds, ... y_label='W / (sr m$^2$) / m', ... plot_kwargs={ - ... use_sd_colours=True, - ... normalise_sd_colours=True, + ... 'use_sd_colours': True, + ... 'normalise_sd_colours': True, ... }, ... legend_location='upper right', - ... bounding_box=(0, 1250, 0, 2.5e15)) + ... bounding_box=(0, 1250, 0, 2.5e6)) .. image:: https://colour.readthedocs.io/en/develop/_static/Examples_Plotting_Blackbodies.png @@ -1335,7 +1303,7 @@ Luminous Efficiency ... y_label='Luminous Efficiency', ... legend_location='upper right', ... y_tighten=True, - ... margins=(0, 0, 0, .1)) + ... margins=(0, 0, 0, 0.1)) .. image:: https://colour.readthedocs.io/en/develop/_static/Examples_Plotting_Luminous_Efficiency.png @@ -1377,16 +1345,6 @@ Chromaticities Prediction .. image:: https://colour.readthedocs.io/en/develop/_static/Examples_Plotting_Chromaticities_Prediction.png -Colour Temperature -****************** - -.. code-block:: python - - >>> plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS(['A', 'B', 'C']) - -.. image:: https://colour.readthedocs.io/en/develop/_static/Examples_Plotting_CCT_CIE_1960_UCS_Chromaticity_Diagram.png - - Chromaticities ************** @@ -1420,19 +1378,81 @@ ANSI/IES TM-30-18 Colour Rendition Report .. image:: https://colour.readthedocs.io/en/develop/_static/Examples_Plotting_Colour_Rendition_Report.png +Gamut Section +************* + +.. code-block:: python + + >>> plot_visible_spectrum_section(section_colours='RGB', section_opacity=0.15) + +.. image:: https://colour.readthedocs.io/en/develop/_static/Examples_Plotting_Plot_Visible_Spectrum_Section.png + +.. code-block:: python + + >>> plot_RGB_colourspace_section('sRGB', section_colours='RGB', section_opacity=0.15) + +.. image:: https://colour.readthedocs.io/en/develop/_static/Examples_Plotting_Plot_RGB_Colourspace_Section.png + +Colour Temperature +****************** + +.. code-block:: python + + >>> plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS(['A', 'B', 'C']) + +.. image:: https://colour.readthedocs.io/en/develop/_static/Examples_Plotting_CCT_CIE_1960_UCS_Chromaticity_Diagram.png + +User Guide +---------- + +Installation +~~~~~~~~~~~~ + +**Colour** and its primary dependencies can be easily installed from the +`Python Package Index `__ +by issuing this command in a shell: + +.. code-block:: bash + + $ pip install --user colour-science + +The detailed installation procedure for the secondary dependencies is +described in the `Installation Guide `__. + +**Colour** is also available for `Anaconda `__ +from *Continuum Analytics* via `conda-forge `__: + +.. code-block:: bash + + $ conda install -c conda-forge colour-science + +Tutorial +~~~~~~~~ + +The `static tutorial `__ +provides an introduction to **Colour**. An interactive version is available via +`Google Colab `__. + +How-To +~~~~~~ + +The `Google Colab How-To `__ +guide for **Colour** shows various techniques to solve specific problems and +highlights some interesting use cases. + Contributing ------------- +~~~~~~~~~~~~ If you would like to contribute to **Colour**, please refer to the following `Contributing `__ guide. Changes -------- +~~~~~~~ The changes are viewable on the `Releases `__ page. Bibliography ------------- +~~~~~~~~~~~~ The bibliography is available on the `Bibliography `__ page. @@ -1440,14 +1460,21 @@ It is also viewable directly from the repository in `BibTeX `__ format. +API Reference +------------- + +The main technical reference for **Colour** is the +`API Reference `__. + See Also -------- -Here is a list of notable colour science packages sorted by languages: +Software +~~~~~~~~ **Python** -- `Colorio `__ by Schlömer, N. +- `Colorio `__ by Schlömer, N. - `ColorPy `__ by Kness, M. - `Colorspacious `__ by Smith, N. J., et al. - `python-colormath `__ by Taylor, G., et al. @@ -1478,6 +1505,17 @@ is available on the `Code of Conduct `__ +- `Facebook `__ +- `Github Discussions `__ +- `Gitter `__ +- `Twitter `__ + Thank You! ---------- @@ -1547,6 +1585,6 @@ About ----- | **Colour** by Colour Developers -| Copyright © 2013-2020 – Colour Developers – `colour-developers@colour-science.org `__ +| Copyright 2013 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 `__ diff --git a/SPONSORS.rst b/SPONSORS.rst index 3f51e17534..47b3db8426 100644 --- a/SPONSORS.rst +++ b/SPONSORS.rst @@ -78,33 +78,6 @@ If you'd like to join them, please consider - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -155,77 +128,62 @@ If you'd like to join them, please consider + + + +.. raw:: html + +

Recurring Donations

+ +.. raw:: html + + + - - - - - - - @@ -267,7 +225,7 @@ If you'd like to join them, please consider @@ -275,13 +233,13 @@ If you'd like to join them, please consider @@ -293,7 +251,7 @@ If you'd like to join them, please consider @@ -324,9 +282,10 @@ If you'd like to join them, please consider

Christophe Brejon

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - + + +

Pablo Garcia Soriano

- - + +
- - + + - - + + - - + + - - + + - - + + - - + + - - + +
- +

Richard Lackey

- +

Liam Collod

- +

Nick Shaw

- +

Ilia Sibiryakov

- - + + +

Mario Rokicki

@@ -394,194 +353,6 @@ If you'd like to join them, please consider
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/TODO.rst b/TODO.rst index 334c843c65..293d561b32 100644 --- a/TODO.rst +++ b/TODO.rst @@ -6,206 +6,195 @@ TODO - colour/__init__.py - - Line 337 : # TODO: Remove legacy printing support when deemed appropriate. + - Line 858 : # TODO: Remove legacy printing support when deemed appropriate. - colour/colorimetry/spectrum.py - - Line 1093 : # TODO: Provide support for fractional interval like 0.1, etc... + - Line 1135 : # TODO: Provide support for fractional interval like 0.1, etc... -- colour/colorimetry/tristimulus.py +- colour/colorimetry/tristimulus_values.py - - Line 757 : # TODO: Investigate code vectorisation. - - -- colour/colorimetry/blackbody.py - - - Line 580 : # TODO: Remove warning when deemed appropriate. + - Line 969 : # TODO: Investigate code vectorisation. - colour/colorimetry/tests/test_spectrum.py - - Line 1459 : # TODO: Remove statement whenever we make "Scipy" 0.19.0 the minimum version. Skipping tests because of "Scipy" 0.19.0 interpolation code changes. - - Line 1665 : # TODO: Remove statement whenever we make "Scipy" 0.19.0 the minimum version. Skipping tests because of "Scipy" 0.19.0 interpolation code changes. + - Line 1508 : # TODO: Remove statement whenever we make "Scipy" 0.19.0 the minimum version. Skipping tests because of "Scipy" 0.19.0 interpolation code changes. + - Line 1761 : # TODO: Remove statement whenever we make "Scipy" 0.19.0 the minimum version. Skipping tests because of "Scipy" 0.19.0 interpolation code changes. - colour/appearance/ciecam02.py - - Line 313 : # TODO: Compute hue composition. + - Line 381 : # TODO: Compute hue composition. - colour/appearance/cam16.py - - Line 292 : # TODO: Compute hue composition. + - Line 321 : # TODO: Compute hue composition. - colour/appearance/hunt.py - - Line 414 : # TODO: Implement hue quadrature & composition computation. - - Line 445 : # TODO: Implement whiteness-blackness :math:`Q_{wb}` computation. + - Line 495 : # TODO: Implement hue quadrature & composition computation. + - Line 528 : # TODO: Implement whiteness-blackness :math:`Q_{wb}` computation. - colour/appearance/rlab.py - - Line 264 : # TODO: Implement hue composition computation. + - Line 291 : # TODO: Implement hue composition computation. - colour/appearance/nayatani95.py - - Line 266 : # TODO: Implement hue quadrature & composition computation. - - Line 278 : # TODO: Investigate components usage. M_RG, M_YB = tsplit(colourfulness_components(C_RG, C_YB, brightness_ideal_white)) + - Line 319 : # TODO: Implement hue quadrature & composition computation. + - Line 332 : # TODO: Investigate components usage. M_RG, M_YB = tsplit(colourfulness_components(C_RG, C_YB, brightness_ideal_white)) - colour/appearance/llab.py - - Line 320 : # TODO: Implement hue composition computation. + - Line 363 : # TODO: Implement hue composition computation. -- colour/appearance/tests/test_cam16.py +- colour/recovery/otsu2018.py - - Line 37 : # TODO: The current fixture data is generated from direct computations using our model implementation. We have asked ground truth data to Li et al. (2016) and will update the "cam16.csv" file accordingly whenever we receive it. + - Line 640 : # TODO: Remove pragma when https://github.com/python/mypy/issues/3004 is resolved. -- colour/recovery/otsu2018.py +- colour/io/ocio.py - - Line 849 : # TODO: Python 3 "yield from child.leaves". + - Line 59 : # TODO: Reinstate when "Pypi" wheel compatible with "ARM" on "macOS" is released. -- colour/io/image.py +- colour/io/tests/test_ocio.py - - Line 64 : # TODO: Overhaul by using "np.sctypeDict". + - Line 36 : # TODO: Remove when "Pypi" wheel compatible with "ARM" on "macOS" is released. - colour/io/tests/test_image.py - - Line 210 : # TODO: Investigate "OIIO" behaviour here: 1.0 != 15360.0 image = read_image_OpenImageIO( os.path.join(RESOURCES_DIRECTORY, 'Colour_Logo.png'), 'float16') self.assertIs(image.dtype, np.dtype('float16')) self.assertEqual(np.min(image), 0.0) self.assertEqual(np.max(image), 1.0) + - Line 304 : # TODO: Investigate "OIIO" behaviour here: 1.0 != 15360.0 image = read_image_OpenImageIO( os.path.join(RESOURCES_DIRECTORY, 'Colour_Logo.png'), 'float16') self.assertIs(image.dtype, np.dtype('float16')) self.assertEqual(np.min(image), 0.0) self.assertEqual(np.max(image), 1.0) -- colour/io/luts/lut.py +- colour/io/luts/sequence.py - - Line 131 : # TODO: Re-enable when dropping Python 2.7. pylint: disable=E1121 + - Line 115 : # TODO: Remove pragma when https://github.com/python/mypy/issues/3004 is resolved. -- colour/graph/conversion.py +- colour/io/luts/operator.py - - Line 953 : # TODO: Remove the following warning whenever the automatic colour conversion graph implementation is considered stable. + - Line 80 : # TODO: Remove pragma when https://github.com/python/mypy/issues/3004 is resolved. + - Line 249 : # TODO: Remove pragma when https://github.com/python/mypy/issues/3004 is resolved. -- colour/models/rgb/derivation.py +- colour/io/luts/lut.py - - Line 211 : # TODO: Investigate if we return an ndarray here with primaries and whitepoint stacked together. + - Line 168 : # TODO: Remove pragma when https://github.com/python/mypy/issues/3004 is resolved. + - Line 2226 : # TODO: Drop "sklearn" requirement whenever "Scipy" 1.7 can be defined as the minimal version. -- colour/models/rgb/rgb_colourspace.py +- colour/graph/conversion.py - - Line 559 : # TODO: Revisit for potential behaviour / type checking. - - Line 592 : # TODO: Revisit for potential behaviour / type checking. + - Line 1241 : # TODO: Remove the following warning whenever the automatic colour conversion graph implementation is considered stable. -- colour/models/rgb/tests/test_derivation.py +- colour/models/rgb/derivation.py - - Line 279 : # TODO: Simplify that monster. + - Line 244 : # TODO: Investigate if we return an ndarray here with primaries and whitepoint stacked together. -- colour/models/rgb/transfer_functions/tests/test__init__.py +- colour/models/rgb/tests/test_derivation.py - - Line 39 : # TODO: Use "assertWarns" when dropping Python 2.7. - - Line 56 : # TODO: Use "assertWarns" when dropping Python 2.7. + - Line 325 : # TODO: Simplify that monster. - colour/utilities/verbose.py - - Line 609 : # TODO: Implement support for "pyproject.toml" file whenever "TOML" is supported in the standard library. - - -- colour/utilities/common.py - - - Line 709 : # TODO: Remove when dropping Python 2.7. + - Line 627 : # TODO: Implement support for "pyproject.toml" file whenever "TOML" is supported in the standard library. - colour/utilities/array.py - - Line 81 : # TODO: Remove when https://github.com/numpy/numpy/issues/5718 is addressed. - - Line 234 : # TODO: Change to "DEFAULT_INT_DTYPE" when and if https://github.com/numpy/numpy/issues/11956 is addressed. - - Line 370 : # TODO: Investigate behaviour on Windows. - - -- colour/utilities/tests/test_deprecation.py - - - Line 317 : # TODO: Use "assertWarns" when dropping Python 2.7. + - Line 559 : # TODO: Remove when https://github.com/numpy/numpy/issues/5718 is addressed. + - Line 605 : # TODO: Reassess implementation when and if https://github.com/numpy/numpy/issues/11956 is addressed. + - Line 833 : # TODO: Investigate behaviour on Windows. + - Line 890 : # TODO: Annotate with "Union[Literal['ignore', 'reference', '1', '100'], str]" when Python 3.7 is dropped. - colour/plotting/models.py - - Line 1725 : # TODO: Filter appropriate colour models. + - Line 1984 : # TODO: Filter appropriate colour models. - colour/plotting/graph.py - - Line 72 : # TODO: Investigate API to trigger the conversion graph build. + - Line 88 : # TODO: Investigate API to trigger the conversion graph build. - colour/plotting/common.py - - Line 666 : # TODO: Reassess according to https://github.com/matplotlib/matplotlib/issues/1077 - - Line 791 : # TODO: Consider using "MutableMapping" here. + - Line 796 : # TODO: Reassess according to https://github.com/matplotlib/matplotlib/issues/1077 + - Line 925 : # TODO: Consider using "MutableMapping" here. + - Line 1567 : # TODO: Remove when "Matplotlib" minimum version can be set to 3.5.0. - colour/characterisation/aces_it.py - - Line 322 : # TODO: Remove when removing the "colour.sd_blackbody" definition warning. + - Line 382 : # TODO: Remove when removing the "colour.sd_blackbody" definition warning. - colour/characterisation/correction.py - - Line 353 : # TODO: Generalise polynomial expansion. + - Line 409 : # TODO: Generalise polynomial expansion. - colour/notation/munsell.py - - Line 1078 : # TODO: Consider refactoring implementation. + - Line 1241 : # TODO: Consider refactoring implementation. -- colour/continuous/signal.py +- colour/continuous/abstract.py - - Line 389 : # TODO: Check for interpolator capabilities. - - Line 454 : # TODO: Check for extrapolator capabilities. + - Line 157 : # TODO: Remove pragma when https://github.com/python/mypy/issues/4165 is resolved. -- colour/continuous/multi_signals.py +- colour/continuous/signal.py - - Line 1354 : # TODO: Implement support for Signal class passing. + - Line 416 : # TODO: Check for interpolator compatibility. + - Line 476 : # TODO: Check for extrapolator compatibility. -- colour/continuous/tests/test_multi_signal.py +- colour/hints/__init__.py - - Line 112 : # TODO: Use "assertWarns" when dropping Python 2.7. + - Line 45 : # TODO: Drop "typing_extensions" when "Google Colab" uses Python >= 3.8. + - Line 160 : # TODO: Use "typing.Literal" when minimal Python version is raised to 3.8. + - Line 163 : # TODO: Revisit to use Protocol. + - Line 180 : # TODO: Use "numpy.typing.NDArray" when minimal Numpy version is raised to 1.21. + - Line 187 : # TODO: Drop when minimal Python is raised to 3.9. -- colour/continuous/tests/test_signal.py +- colour/algebra/interpolation.py - - Line 102 : # TODO: Use "assertWarns" when dropping Python 2.7. + - Line 430 : # TODO: Remove pragma when https://github.com/python/mypy/issues/3004 is resolved. + - Line 827 : # TODO: Remove pragma when https://github.com/python/mypy/issues/3004 is resolved. + - Line 1051 : # TODO: Remove pragma when https://github.com/python/mypy/issues/3004 is resolved. + - Line 1422 : # TODO: Remove pragma when https://github.com/python/mypy/issues/3004 is resolved. -- colour/volume/rgb.py +- colour/algebra/common.py - - Line 300 : # TODO: Investigate for generator yielding directly a ndarray. + - Line 51 : # TODO: Annotate with "bool" when Python 3.7 is dropped. - colour/algebra/tests/test_interpolation.py - - Line 534 : # TODO: Revisit if the interpolator can be applied on non-uniform "x" independent variable. - - -- colour/algebra/tests/test_random.py - - - Line 68 : # TODO: Use "assertWarns" when dropping Python 2.7. + - Line 1171 : # TODO: Revisit if the interpolator can be applied on non-uniform "x" independent variable. About ----- | **Colour** by Colour Developers -| Copyright © 2013-2020 – Colour Developers – `colour-developers@colour-science.org `__ +| Copyright 2013 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 `__ diff --git a/colour/__init__.py b/colour/__init__.py index aa187e62d3..637734a4b7 100644 --- a/colour/__init__.py +++ b/colour/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Colour ====== @@ -31,6 +30,7 @@ - examples: Examples for the sub-packages. - geometry: Geometry primitives generation. - graph: Graph for automatic colour conversions. +- hints: Type hints for annotations. - io: Input / output objects for reading and writing data. - models: Colour models. - notation: Colour notation systems. @@ -44,123 +44,393 @@ - volume: Colourspace volumes computation and optimal colour stimuli. """ -from __future__ import absolute_import - import numpy as np import sys from .utilities.deprecation import ModuleAPI, build_API_changes from .utilities.documentation import is_documentation_building -from .utilities.common import (domain_range_scale, get_domain_range_scale, - set_domain_range_scale) -from .adaptation import (CHROMATIC_ADAPTATION_METHODS, - CHROMATIC_ADAPTATION_TRANSFORMS, - VIEWING_CONDITIONS_CMCCAT2000, chromatic_adaptation) -from .algebra import (CubicSplineInterpolator, Extrapolator, - KernelInterpolator, NearestNeighbourInterpolator, - LinearInterpolator, NullInterpolator, PchipInterpolator, - SpragueInterpolator, TABLE_INTERPOLATION_METHODS, - kernel_cardinal_spline, kernel_lanczos, kernel_linear, - kernel_nearest_neighbour, kernel_sinc, - table_interpolation, lagrange_coefficients) +from .utilities.array import ( + domain_range_scale, + get_domain_range_scale, + set_domain_range_scale, +) + +from .hints import Any + +from .adaptation import ( + CHROMATIC_ADAPTATION_METHODS, + CHROMATIC_ADAPTATION_TRANSFORMS, + VIEWING_CONDITIONS_CMCCAT2000, + chromatic_adaptation, +) +from .algebra import ( + CubicSplineInterpolator, + Extrapolator, + KernelInterpolator, + NearestNeighbourInterpolator, + LinearInterpolator, + NullInterpolator, + PchipInterpolator, + SpragueInterpolator, + TABLE_INTERPOLATION_METHODS, + kernel_cardinal_spline, + kernel_lanczos, + kernel_linear, + kernel_nearest_neighbour, + kernel_sinc, + table_interpolation, + lagrange_coefficients, +) from .colorimetry import ( - BANDPASS_CORRECTION_METHODS, CCS_ILLUMINANTS, CCS_LIGHT_SOURCES, - LIGHTNESS_METHODS, LUMINANCE_METHODS, MSDS_CMFS, MSDS_TO_XYZ_METHODS, - MultiSpectralDistributions, SDS_ILLUMINANTS, SDS_LEFS, SDS_LIGHT_SOURCES, - SD_GAUSSIAN_METHODS, SD_MULTI_LEDS_METHODS, SD_SINGLE_LED_METHODS, - SD_TO_XYZ_METHODS, SPECTRAL_SHAPE_ASTME308, SPECTRAL_SHAPE_DEFAULT, - SpectralDistribution, SpectralShape, TVS_ILLUMINANTS_HUNTERLAB, - WHITENESS_METHODS, YELLOWNESS_METHODS, bandpass_correction, - colorimetric_purity, complementary_wavelength, dominant_wavelength, - excitation_purity, lightness, luminance, luminous_efficacy, - luminous_efficiency, luminous_flux, msds_constant, msds_ones, msds_zeros, - msds_to_XYZ, sd_CIE_illuminant_D_series, sd_CIE_standard_illuminant_A, - sd_blackbody, sd_constant, sd_gaussian, - sd_mesopic_luminous_efficiency_function, sd_multi_leds, sd_ones, - sd_single_led, sd_to_XYZ, sd_zeros, wavelength_to_XYZ, whiteness, - yellowness) + BANDPASS_CORRECTION_METHODS, + CCS_ILLUMINANTS, + CCS_LIGHT_SOURCES, + LIGHTNESS_METHODS, + LUMINANCE_METHODS, + MSDS_CMFS, + MSDS_TO_XYZ_METHODS, + MultiSpectralDistributions, + SDS_ILLUMINANTS, + SDS_LEFS, + SDS_LIGHT_SOURCES, + SD_GAUSSIAN_METHODS, + SD_MULTI_LEDS_METHODS, + SD_SINGLE_LED_METHODS, + SD_TO_XYZ_METHODS, + SPECTRAL_SHAPE_ASTME308, + SPECTRAL_SHAPE_DEFAULT, + SpectralDistribution, + SpectralShape, + TVS_ILLUMINANTS, + TVS_ILLUMINANTS_HUNTERLAB, + WHITENESS_METHODS, + YELLOWNESS_METHODS, + bandpass_correction, + colorimetric_purity, + complementary_wavelength, + dominant_wavelength, + excitation_purity, + lightness, + luminance, + luminous_efficacy, + luminous_efficiency, + luminous_flux, + msds_constant, + msds_ones, + msds_zeros, + msds_to_XYZ, + sd_CIE_illuminant_D_series, + sd_CIE_standard_illuminant_A, + sd_blackbody, + sd_constant, + sd_gaussian, + sd_mesopic_luminous_efficiency_function, + sd_multi_leds, + sd_ones, + sd_single_led, + sd_to_XYZ, + sd_zeros, + spectral_uniformity, + wavelength_to_XYZ, + whiteness, + yellowness, +) from .blindness import ( - CVD_MATRICES_MACHADO2010, matrix_anomalous_trichromacy_Machado2009, - matrix_cvd_Machado2009, msds_cmfs_anomalous_trichromacy_Machado2009) + CVD_MATRICES_MACHADO2010, + matrix_anomalous_trichromacy_Machado2009, + matrix_cvd_Machado2009, + msds_cmfs_anomalous_trichromacy_Machado2009, +) from .appearance import ( - CAM_Specification_ATD95, CAM_Specification_CAM16, - CAM_Specification_CIECAM02, CAM_Specification_Hunt, CAM_Specification_LLAB, - CAM_Specification_Nayatani95, CAM_Specification_RLAB, CAM16_to_XYZ, - CIECAM02_to_XYZ, VIEWING_CONDITIONS_CAM16, VIEWING_CONDITIONS_CIECAM02, - VIEWING_CONDITIONS_HUNT, VIEWING_CONDITIONS_LLAB, VIEWING_CONDITIONS_RLAB, - XYZ_to_ATD95, XYZ_to_CAM16, XYZ_to_CIECAM02, XYZ_to_Hunt, XYZ_to_LLAB, - XYZ_to_Nayatani95, XYZ_to_RLAB) -from .difference import DELTA_E_METHODS, delta_E -from .geometry import (PRIMITIVE_METHODS, primitive, - PRIMITIVE_VERTICES_METHODS, primitive_vertices) -from .io import (LUT1D, LUT3x1D, LUT3D, LUTSequence, READ_IMAGE_METHODS, - SpectralDistribution_IESTM2714, WRITE_IMAGE_METHODS, - read_image, read_LUT, read_sds_from_csv_file, - read_sds_from_xrite_file, read_spectral_data_from_csv_file, - write_image, write_LUT, write_sds_to_csv_file) + CAM_Specification_ATD95, + CAM_Specification_CAM16, + CAM_Specification_CIECAM02, + CAM_Specification_Hunt, + CAM_Specification_Kim2009, + CAM_Specification_LLAB, + CAM_Specification_Nayatani95, + CAM_Specification_RLAB, + CAM_Specification_ZCAM, + CAM16_to_XYZ, + CIECAM02_to_XYZ, + HKE_NAYATANI1997_METHODS, + HelmholtzKohlrausch_effect_object_Nayatani1997, + HelmholtzKohlrausch_effect_luminous_Nayatani1997, + Kim2009_to_XYZ, + MEDIA_PARAMETERS_KIM2009, + VIEWING_CONDITIONS_CAM16, + VIEWING_CONDITIONS_CIECAM02, + VIEWING_CONDITIONS_HUNT, + VIEWING_CONDITIONS_KIM2009, + VIEWING_CONDITIONS_LLAB, + VIEWING_CONDITIONS_RLAB, + VIEWING_CONDITIONS_ZCAM, + XYZ_to_ATD95, + XYZ_to_CAM16, + XYZ_to_CIECAM02, + XYZ_to_Kim2009, + XYZ_to_Hunt, + XYZ_to_LLAB, + XYZ_to_Nayatani95, + XYZ_to_RLAB, + XYZ_to_ZCAM, + ZCAM_to_XYZ, +) +from .difference import ( + DELTA_E_METHODS, + delta_E, + INDEX_STRESS_METHODS, + index_stress, +) +from .geometry import ( + PRIMITIVE_METHODS, + primitive, + PRIMITIVE_VERTICES_METHODS, + primitive_vertices, +) +from .io import ( + LUT1D, + LUT3x1D, + LUT3D, + LUTOperatorMatrix, + LUTSequence, + READ_IMAGE_METHODS, + SpectralDistribution_IESTM2714, + WRITE_IMAGE_METHODS, + read_image, + read_LUT, + read_sds_from_csv_file, + read_sds_from_xrite_file, + read_spectral_data_from_csv_file, + SpectralDistribution_Sekonic, + SpectralDistribution_UPRTek, + write_image, + write_LUT, + write_sds_to_csv_file, +) from .models import ( - CAM02LCD_to_JMh_CIECAM02, CAM02SCD_to_JMh_CIECAM02, - CAM02UCS_to_JMh_CIECAM02, CAM16LCD_to_JMh_CAM16, CAM16SCD_to_JMh_CAM16, - CAM16UCS_to_JMh_CAM16, CCTF_DECODINGS, CCTF_ENCODINGS, CMYK_to_CMY, - CMY_to_CMYK, CMY_to_RGB, CV_range, DATA_MACADAM_1942_ELLIPSES, - DIN99_to_Lab, EOTFS, EOTF_INVERSES, HDR_CIELAB_METHODS, HDR_IPT_METHODS, - HSL_to_RGB, HSV_to_RGB, Hunter_Lab_to_XYZ, Hunter_Rdab_to_XYZ, - ICTCP_to_RGB, IGPGTG_to_XYZ, IPT_hue_angle, IPT_to_XYZ, - JMh_CAM16_to_CAM16LCD, JMh_CAM16_to_CAM16SCD, JMh_CAM16_to_CAM16UCS, - JMh_CIECAM02_to_CAM02LCD, JMh_CIECAM02_to_CAM02SCD, - JMh_CIECAM02_to_CAM02UCS, JzAzBz_to_XYZ, LCHab_to_Lab, LCHuv_to_Luv, - LOG_DECODINGS, LOG_ENCODINGS, Lab_to_DIN99, Lab_to_LCHab, Lab_to_XYZ, - Luv_to_LCHuv, Luv_to_XYZ, Luv_to_uv, Luv_uv_to_xy, OETFS, OETF_INVERSES, - OOTFS, OOTF_INVERSES, OSA_UCS_to_XYZ, Prismatic_to_RGB, RGB_COLOURSPACES, - RGB_Colourspace, RGB_luminance, RGB_luminance_equation, RGB_to_CMY, - RGB_to_HSL, RGB_to_HSV, RGB_to_ICTCP, RGB_to_Prismatic, RGB_to_RGB, - RGB_to_XYZ, RGB_to_YCbCr, RGB_to_YCoCg, RGB_to_YcCbcCrc, UCS_to_XYZ, - UCS_to_uv, UCS_uv_to_xy, UVW_to_XYZ, WEIGHTS_YCBCR, XYZ_to_Hunter_Lab, - XYZ_to_Hunter_Rdab, XYZ_to_IGPGTG, XYZ_to_IPT, XYZ_to_JzAzBz, - XYZ_to_K_ab_HunterLab1966, XYZ_to_Lab, XYZ_to_Luv, XYZ_to_OSA_UCS, - XYZ_to_RGB, XYZ_to_UCS, XYZ_to_UVW, XYZ_to_hdr_CIELab, XYZ_to_hdr_IPT, - XYZ_to_sRGB, XYZ_to_xy, XYZ_to_xyY, YCbCr_to_RGB, YCoCg_to_RGB, - YcCbcCrc_to_RGB, cctf_decoding, cctf_encoding, - chromatically_adapted_primaries, eotf, eotf_inverse, full_to_legal, - gamma_function, hdr_CIELab_to_XYZ, hdr_IPT_to_XYZ, legal_to_full, - linear_function, log_decoding, log_encoding, matrix_RGB_to_RGB, - normalised_primary_matrix, oetf, oetf_inverse, ootf, ootf_inverse, - primaries_whitepoint, sRGB_to_XYZ, uv_to_Luv, uv_to_UCS, xyY_to_XYZ, - xyY_to_xy, xy_to_Luv_uv, xy_to_UCS_uv, xy_to_XYZ, xy_to_xyY) + CAM02LCD_to_JMh_CIECAM02, + CAM02SCD_to_JMh_CIECAM02, + CAM02UCS_to_JMh_CIECAM02, + CAM02LCD_to_XYZ, + CAM02SCD_to_XYZ, + CAM02UCS_to_XYZ, + CAM16LCD_to_JMh_CAM16, + CAM16SCD_to_JMh_CAM16, + CAM16UCS_to_JMh_CAM16, + CAM16LCD_to_XYZ, + CAM16SCD_to_XYZ, + CAM16UCS_to_XYZ, + CCTF_DECODINGS, + CCTF_ENCODINGS, + CMYK_to_CMY, + CMY_to_CMYK, + CMY_to_RGB, + COLOURSPACE_MODELS, + CV_range, + DATA_MACADAM_1942_ELLIPSES, + DIN99_to_Lab, + DIN99_to_XYZ, + EOTFS, + EOTF_INVERSES, + HCL_to_RGB, + HDR_CIELAB_METHODS, + HDR_IPT_METHODS, + HSL_to_RGB, + HSV_to_RGB, + Hunter_Lab_to_XYZ, + Hunter_Rdab_to_XYZ, + ICaCb_to_XYZ, + ICtCp_to_RGB, + ICtCp_to_XYZ, + IHLS_to_RGB, + IgPgTg_to_XYZ, + IPT_hue_angle, + IPT_to_XYZ, + JMh_CAM16_to_CAM16LCD, + JMh_CAM16_to_CAM16SCD, + JMh_CAM16_to_CAM16UCS, + JMh_CIECAM02_to_CAM02LCD, + JMh_CIECAM02_to_CAM02SCD, + JMh_CIECAM02_to_CAM02UCS, + Jzazbz_to_XYZ, + LCHab_to_Lab, + LCHuv_to_Luv, + LOG_DECODINGS, + LOG_ENCODINGS, + Lab_to_DIN99, + Lab_to_LCHab, + Lab_to_XYZ, + Luv_to_LCHuv, + Luv_to_XYZ, + Luv_to_uv, + Luv_uv_to_xy, + OETFS, + OETF_INVERSES, + OOTFS, + OOTF_INVERSES, + OSA_UCS_to_XYZ, + Oklab_to_XYZ, + Prismatic_to_RGB, + ProLab_to_XYZ, + RGB_COLOURSPACES, + RGB_Colourspace, + RGB_luminance, + RGB_luminance_equation, + RGB_to_CMY, + RGB_to_HCL, + RGB_to_HSL, + RGB_to_HSV, + RGB_to_ICtCp, + RGB_to_IHLS, + RGB_to_Prismatic, + RGB_to_RGB, + RGB_to_XYZ, + RGB_to_YCbCr, + RGB_to_YCoCg, + RGB_to_YcCbcCrc, + UCS_to_XYZ, + UCS_to_uv, + UCS_uv_to_xy, + UVW_to_XYZ, + WEIGHTS_YCBCR, + XYZ_to_CAM02LCD, + XYZ_to_CAM02SCD, + XYZ_to_CAM02UCS, + XYZ_to_CAM16LCD, + XYZ_to_CAM16SCD, + XYZ_to_CAM16UCS, + XYZ_to_DIN99, + XYZ_to_Hunter_Lab, + XYZ_to_Hunter_Rdab, + XYZ_to_ICaCb, + XYZ_to_ICtCp, + XYZ_to_IgPgTg, + XYZ_to_IPT, + XYZ_to_Jzazbz, + XYZ_to_K_ab_HunterLab1966, + XYZ_to_Lab, + XYZ_to_Luv, + XYZ_to_OSA_UCS, + XYZ_to_Oklab, + XYZ_to_ProLab, + XYZ_to_RGB, + XYZ_to_UCS, + XYZ_to_UVW, + XYZ_to_hdr_CIELab, + XYZ_to_hdr_IPT, + XYZ_to_sRGB, + XYZ_to_xy, + XYZ_to_xyY, + YCbCr_to_RGB, + YCoCg_to_RGB, + YcCbcCrc_to_RGB, + cctf_decoding, + cctf_encoding, + chromatically_adapted_primaries, + eotf, + eotf_inverse, + full_to_legal, + gamma_function, + hdr_CIELab_to_XYZ, + hdr_IPT_to_XYZ, + legal_to_full, + linear_function, + log_decoding, + log_encoding, + matrix_RGB_to_RGB, + matrix_YCbCr, + normalised_primary_matrix, + oetf, + oetf_inverse, + offset_YCbCr, + ootf, + ootf_inverse, + primaries_whitepoint, + sRGB_to_XYZ, + uv_to_Luv, + uv_to_UCS, + xyY_to_XYZ, + xyY_to_xy, + xy_to_Luv_uv, + xy_to_UCS_uv, + xy_to_XYZ, + xy_to_xyY, +) from .corresponding import ( - BRENEMAN_EXPERIMENTS, BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES, - CORRESPONDING_CHROMATICITIES_PREDICTION_MODELS, CorrespondingColourDataset, + BRENEMAN_EXPERIMENTS, + BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES, + CORRESPONDING_CHROMATICITIES_PREDICTION_MODELS, + CorrespondingColourDataset, CorrespondingChromaticitiesPrediction, - corresponding_chromaticities_prediction) -from .contrast import (CONTRAST_SENSITIVITY_METHODS, - contrast_sensitivity_function) -from .phenomena import (rayleigh_scattering, scattering_cross_section, - sd_rayleigh_scattering) -from .notation import (MUNSELL_COLOURS, MUNSELL_VALUE_METHODS, - munsell_colour_to_xyY, munsell_value, - xyY_to_munsell_colour) -from .quality import (COLOUR_FIDELITY_INDEX_METHODS, - COLOUR_QUALITY_SCALE_METHODS, colour_fidelity_index, - colour_quality_scale, colour_rendering_index, - spectral_similarity_index) + corresponding_chromaticities_prediction, +) +from .contrast import ( + CONTRAST_SENSITIVITY_METHODS, + contrast_sensitivity_function, +) +from .phenomena import ( + rayleigh_scattering, + scattering_cross_section, + sd_rayleigh_scattering, +) +from .notation import ( + MUNSELL_COLOURS, + MUNSELL_VALUE_METHODS, + munsell_colour_to_xyY, + munsell_value, + xyY_to_munsell_colour, +) +from .quality import ( + COLOUR_FIDELITY_INDEX_METHODS, + COLOUR_QUALITY_SCALE_METHODS, + colour_fidelity_index, + colour_quality_scale, + colour_rendering_index, + spectral_similarity_index, +) from .recovery import XYZ_TO_SD_METHODS, XYZ_to_sd -from .temperature import (CCT_TO_UV_METHODS, CCT_TO_XY_METHODS, CCT_to_uv, - CCT_to_xy, UV_TO_CCT_METHODS, XY_TO_CCT_METHODS, - uv_to_CCT, xy_to_CCT) +from .temperature import ( + CCT_TO_UV_METHODS, + CCT_TO_XY_METHODS, + CCT_to_uv, + CCT_to_xy, + UV_TO_CCT_METHODS, + XY_TO_CCT_METHODS, + uv_to_CCT, + xy_to_CCT, +) from .characterisation import ( - CCS_COLOURCHECKERS, MATRIX_COLOUR_CORRECTION_METHODS, - COLOUR_CORRECTION_METHODS, MSDS_CAMERA_SENSITIVITIES, - MSDS_DISPLAY_PRIMARIES, POLYNOMIAL_EXPANSION_METHODS, SDS_COLOURCHECKERS, - SDS_FILTERS, SDS_LENSES, colour_correction, matrix_colour_correction, - matrix_idt, polynomial_expansion, sd_to_aces_relative_exposure_values) + CCS_COLOURCHECKERS, + MATRIX_COLOUR_CORRECTION_METHODS, + COLOUR_CORRECTION_METHODS, + MSDS_CAMERA_SENSITIVITIES, + MSDS_DISPLAY_PRIMARIES, + POLYNOMIAL_EXPANSION_METHODS, + SDS_COLOURCHECKERS, + SDS_FILTERS, + SDS_LENSES, + camera_RGB_to_ACES2065_1, + colour_correction, + matrix_colour_correction, + matrix_idt, + polynomial_expansion, + sd_to_ACES2065_1, + sd_to_aces_relative_exposure_values, +) from .volume import ( - OPTIMAL_COLOUR_STIMULI_ILLUMINANTS, RGB_colourspace_limits, + OPTIMAL_COLOUR_STIMULI_ILLUMINANTS, + RGB_colourspace_limits, RGB_colourspace_pointer_gamut_coverage_MonteCarlo, RGB_colourspace_visible_spectrum_coverage_MonteCarlo, RGB_colourspace_volume_MonteCarlo, - RGB_colourspace_volume_coverage_MonteCarlo, is_within_macadam_limits, - is_within_mesh_volume, is_within_pointer_gamut, is_within_visible_spectrum) + RGB_colourspace_volume_coverage_MonteCarlo, + is_within_macadam_limits, + is_within_mesh_volume, + is_within_pointer_gamut, + is_within_visible_spectrum, +) from .graph import describe_conversion_path, convert from colour.utilities import is_matplotlib_installed @@ -170,174 +440,424 @@ import colour.plotting as plotting # noqa else: - class MockPlotting(object): + class MockPlotting: # pragma: no cover """ Mock object for :mod:`colour.plotting` sub-package raising an exception if the sub-package is accessed but *Matplotlib* is not installed. """ - def __getattr__(self, attribute): + def __getattr__(self, attribute) -> Any: + """Return the value from the attribute with given name.""" + is_matplotlib_installed(raise_exception=True) - globals()['plotting'] = MockPlotting() + globals()["plotting"] = MockPlotting() # pragma: no cover -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'domain_range_scale', 'get_domain_range_scale', 'set_domain_range_scale' + "domain_range_scale", + "get_domain_range_scale", + "set_domain_range_scale", +] +__all__ += [ + "CHROMATIC_ADAPTATION_METHODS", + "CHROMATIC_ADAPTATION_TRANSFORMS", + "VIEWING_CONDITIONS_CMCCAT2000", + "chromatic_adaptation", +] +__all__ += [ + "CubicSplineInterpolator", + "Extrapolator", + "KernelInterpolator", + "NearestNeighbourInterpolator", + "LinearInterpolator", + "NullInterpolator", + "PchipInterpolator", + "SpragueInterpolator", + "TABLE_INTERPOLATION_METHODS", + "kernel_cardinal_spline", + "kernel_lanczos", + "kernel_linear", + "kernel_nearest_neighbour", + "kernel_sinc", + "table_interpolation", + "lagrange_coefficients", ] __all__ += [ - 'CHROMATIC_ADAPTATION_METHODS', 'CHROMATIC_ADAPTATION_TRANSFORMS', - 'VIEWING_CONDITIONS_CMCCAT2000', 'chromatic_adaptation' + "BANDPASS_CORRECTION_METHODS", + "CCS_ILLUMINANTS", + "CCS_LIGHT_SOURCES", + "LIGHTNESS_METHODS", + "LUMINANCE_METHODS", + "MSDS_CMFS", + "MSDS_TO_XYZ_METHODS", + "MultiSpectralDistributions", + "SDS_ILLUMINANTS", + "SDS_LEFS", + "SDS_LIGHT_SOURCES", + "SD_GAUSSIAN_METHODS", + "SD_MULTI_LEDS_METHODS", + "SD_SINGLE_LED_METHODS", + "SD_TO_XYZ_METHODS", + "SPECTRAL_SHAPE_ASTME308", + "SPECTRAL_SHAPE_DEFAULT", + "SpectralDistribution", + "SpectralShape", + "TVS_ILLUMINANTS", + "TVS_ILLUMINANTS_HUNTERLAB", + "WHITENESS_METHODS", + "YELLOWNESS_METHODS", + "bandpass_correction", + "colorimetric_purity", + "complementary_wavelength", + "dominant_wavelength", + "excitation_purity", + "lightness", + "luminance", + "luminous_efficacy", + "luminous_efficiency", + "luminous_flux", + "msds_constant", + "msds_ones", + "msds_zeros", + "msds_to_XYZ", + "sd_CIE_illuminant_D_series", + "sd_CIE_standard_illuminant_A", + "sd_blackbody", + "sd_constant", + "sd_gaussian", + "sd_mesopic_luminous_efficiency_function", + "sd_multi_leds", + "sd_ones", + "sd_single_led", + "sd_to_XYZ", + "sd_zeros", + "spectral_uniformity", + "wavelength_to_XYZ", + "whiteness", + "yellowness", ] __all__ += [ - 'CubicSplineInterpolator', 'Extrapolator', 'KernelInterpolator', - 'NearestNeighbourInterpolator', 'LinearInterpolator', 'NullInterpolator', - 'PchipInterpolator', 'SpragueInterpolator', 'TABLE_INTERPOLATION_METHODS', - 'kernel_cardinal_spline', 'kernel_lanczos', 'kernel_linear', - 'kernel_nearest_neighbour', 'kernel_sinc', 'table_interpolation', - 'lagrange_coefficients' + "CVD_MATRICES_MACHADO2010", + "matrix_anomalous_trichromacy_Machado2009", + "matrix_cvd_Machado2009", + "msds_cmfs_anomalous_trichromacy_Machado2009", ] __all__ += [ - 'BANDPASS_CORRECTION_METHODS', 'CCS_ILLUMINANTS', 'CCS_LIGHT_SOURCES', - 'LIGHTNESS_METHODS', 'LUMINANCE_METHODS', 'MSDS_CMFS', - 'MSDS_TO_XYZ_METHODS', 'MultiSpectralDistributions', 'SDS_ILLUMINANTS', - 'SDS_LEFS', 'SDS_LIGHT_SOURCES', 'SD_GAUSSIAN_METHODS', - 'SD_MULTI_LEDS_METHODS', 'SD_SINGLE_LED_METHODS', 'SD_TO_XYZ_METHODS', - 'SPECTRAL_SHAPE_ASTME308', 'SPECTRAL_SHAPE_DEFAULT', - 'SpectralDistribution', 'SpectralShape', 'TVS_ILLUMINANTS_HUNTERLAB', - 'WHITENESS_METHODS', 'YELLOWNESS_METHODS', 'bandpass_correction', - 'colorimetric_purity', 'complementary_wavelength', 'dominant_wavelength', - 'excitation_purity', 'lightness', 'luminance', 'luminous_efficacy', - 'luminous_efficiency', 'luminous_flux', 'msds_constant', 'msds_ones', - 'msds_zeros', 'msds_to_XYZ', 'sd_CIE_illuminant_D_series', - 'sd_CIE_standard_illuminant_A', 'sd_blackbody', 'sd_constant', - 'sd_gaussian', 'sd_mesopic_luminous_efficiency_function', 'sd_multi_leds', - 'sd_ones', 'sd_single_led', 'sd_to_XYZ', 'sd_zeros', 'wavelength_to_XYZ', - 'whiteness', 'yellowness' + "CAM_Specification_ATD95", + "CAM_Specification_CAM16", + "CAM_Specification_CIECAM02", + "CAM_Specification_Hunt", + "CAM_Specification_Kim2009", + "CAM_Specification_LLAB", + "CAM_Specification_Nayatani95", + "CAM_Specification_RLAB", + "CAM_Specification_ZCAM", + "CAM16_to_XYZ", + "CIECAM02_to_XYZ", + "HKE_NAYATANI1997_METHODS", + "HelmholtzKohlrausch_effect_object_Nayatani1997", + "HelmholtzKohlrausch_effect_luminous_Nayatani1997", + "Kim2009_to_XYZ", + "MEDIA_PARAMETERS_KIM2009", + "VIEWING_CONDITIONS_CAM16", + "VIEWING_CONDITIONS_CIECAM02", + "VIEWING_CONDITIONS_HUNT", + "VIEWING_CONDITIONS_KIM2009", + "VIEWING_CONDITIONS_LLAB", + "VIEWING_CONDITIONS_RLAB", + "VIEWING_CONDITIONS_ZCAM", + "XYZ_to_ATD95", + "XYZ_to_CAM16", + "XYZ_to_CIECAM02", + "XYZ_to_Kim2009", + "XYZ_to_Hunt", + "XYZ_to_LLAB", + "XYZ_to_Nayatani95", + "XYZ_to_RLAB", + "XYZ_to_ZCAM", + "ZCAM_to_XYZ", ] __all__ += [ - 'CVD_MATRICES_MACHADO2010', 'matrix_anomalous_trichromacy_Machado2009', - 'matrix_cvd_Machado2009', 'msds_cmfs_anomalous_trichromacy_Machado2009' + "DELTA_E_METHODS", + "delta_E", + "INDEX_STRESS_METHODS", + "index_stress", ] __all__ += [ - 'CAM_Specification_ATD95', 'CAM_Specification_CAM16', - 'CAM_Specification_CIECAM02', 'CAM_Specification_Hunt', - 'CAM_Specification_LLAB', 'CAM_Specification_Nayatani95', - 'CAM_Specification_RLAB', 'CAM16_to_XYZ', 'CIECAM02_to_XYZ', - 'VIEWING_CONDITIONS_CAM16', 'VIEWING_CONDITIONS_CIECAM02', - 'VIEWING_CONDITIONS_HUNT', 'VIEWING_CONDITIONS_LLAB', - 'VIEWING_CONDITIONS_RLAB', 'XYZ_to_ATD95', 'XYZ_to_CAM16', - 'XYZ_to_CIECAM02', 'XYZ_to_Hunt', 'XYZ_to_LLAB', 'XYZ_to_Nayatani95', - 'XYZ_to_RLAB' + "PRIMITIVE_METHODS", + "primitive", + "PRIMITIVE_VERTICES_METHODS", + "primitive_vertices", ] -__all__ += ['DELTA_E_METHODS', 'delta_E'] __all__ += [ - 'PRIMITIVE_METHODS', 'primitive', 'PRIMITIVE_VERTICES_METHODS', - 'primitive_vertices' + "LUT1D", + "LUT3x1D", + "LUT3D", + "LUTOperatorMatrix", + "LUTSequence", + "READ_IMAGE_METHODS", + "SpectralDistribution_IESTM2714", + "WRITE_IMAGE_METHODS", + "read_image", + "read_LUT", + "read_sds_from_csv_file", + "read_sds_from_xrite_file", + "read_spectral_data_from_csv_file", + "SpectralDistribution_UPRTek", + "SpectralDistribution_Sekonic", + "write_image", + "write_LUT", + "write_sds_to_csv_file", ] __all__ += [ - 'LUT1D', 'LUT3x1D', 'LUT3D', 'LUTSequence', 'READ_IMAGE_METHODS', - 'SpectralDistribution_IESTM2714', 'WRITE_IMAGE_METHODS', 'read_image', - 'read_LUT', 'read_sds_from_csv_file', 'read_sds_from_xrite_file', - 'read_spectral_data_from_csv_file', 'write_image', 'write_LUT', - 'write_sds_to_csv_file' + "CAM02LCD_to_JMh_CIECAM02", + "CAM02SCD_to_JMh_CIECAM02", + "CAM02UCS_to_JMh_CIECAM02", + "CAM02LCD_to_XYZ", + "CAM02SCD_to_XYZ", + "CAM02UCS_to_XYZ", + "CAM16LCD_to_JMh_CAM16", + "CAM16SCD_to_JMh_CAM16", + "CAM16UCS_to_JMh_CAM16", + "CAM16LCD_to_XYZ", + "CAM16SCD_to_XYZ", + "CAM16UCS_to_XYZ", + "CCTF_DECODINGS", + "CCTF_ENCODINGS", + "CMYK_to_CMY", + "CMY_to_CMYK", + "CMY_to_RGB", + "COLOURSPACE_MODELS", + "CV_range", + "DATA_MACADAM_1942_ELLIPSES", + "DIN99_to_Lab", + "DIN99_to_XYZ", + "EOTFS", + "EOTF_INVERSES", + "HCL_to_RGB", + "HDR_CIELAB_METHODS", + "HDR_IPT_METHODS", + "HSL_to_RGB", + "HSV_to_RGB", + "Hunter_Lab_to_XYZ", + "Hunter_Rdab_to_XYZ", + "ICaCb_to_XYZ", + "ICtCp_to_RGB", + "ICtCp_to_XYZ", + "IHLS_to_RGB", + "IgPgTg_to_XYZ", + "IPT_hue_angle", + "IPT_to_XYZ", + "JMh_CAM16_to_CAM16LCD", + "JMh_CAM16_to_CAM16SCD", + "JMh_CAM16_to_CAM16UCS", + "JMh_CIECAM02_to_CAM02LCD", + "JMh_CIECAM02_to_CAM02SCD", + "JMh_CIECAM02_to_CAM02UCS", + "Jzazbz_to_XYZ", + "LCHab_to_Lab", + "LCHuv_to_Luv", + "LOG_DECODINGS", + "LOG_ENCODINGS", + "Lab_to_DIN99", + "Lab_to_LCHab", + "Lab_to_XYZ", + "Luv_to_LCHuv", + "Luv_to_XYZ", + "Luv_to_uv", + "Luv_uv_to_xy", + "OETFS", + "OETF_INVERSES", + "OOTFS", + "OOTF_INVERSES", + "OSA_UCS_to_XYZ", + "Oklab_to_XYZ", + "Prismatic_to_RGB", + "ProLab_to_XYZ", + "RGB_COLOURSPACES", + "RGB_Colourspace", + "RGB_luminance", + "RGB_luminance_equation", + "RGB_to_CMY", + "RGB_to_HCL", + "RGB_to_HSL", + "RGB_to_HSV", + "RGB_to_ICtCp", + "RGB_to_IHLS", + "RGB_to_Prismatic", + "RGB_to_RGB", + "RGB_to_XYZ", + "RGB_to_YCbCr", + "RGB_to_YCoCg", + "RGB_to_YcCbcCrc", + "UCS_to_XYZ", + "UCS_to_uv", + "UCS_uv_to_xy", + "UVW_to_XYZ", + "WEIGHTS_YCBCR", + "XYZ_to_CAM02LCD", + "XYZ_to_CAM02SCD", + "XYZ_to_CAM02UCS", + "XYZ_to_CAM16LCD", + "XYZ_to_CAM16SCD", + "XYZ_to_CAM16UCS", + "XYZ_to_DIN99", + "XYZ_to_Hunter_Lab", + "XYZ_to_Hunter_Rdab", + "XYZ_to_ICaCb", + "XYZ_to_ICtCp", + "XYZ_to_IgPgTg", + "XYZ_to_IPT", + "XYZ_to_Jzazbz", + "XYZ_to_K_ab_HunterLab1966", + "XYZ_to_Lab", + "XYZ_to_Luv", + "XYZ_to_OSA_UCS", + "XYZ_to_Oklab", + "XYZ_to_ProLab", + "XYZ_to_RGB", + "XYZ_to_UCS", + "XYZ_to_UVW", + "XYZ_to_hdr_CIELab", + "XYZ_to_hdr_IPT", + "XYZ_to_sRGB", + "XYZ_to_xy", + "XYZ_to_xyY", + "YCbCr_to_RGB", + "YCoCg_to_RGB", + "YcCbcCrc_to_RGB", + "cctf_decoding", + "cctf_encoding", + "chromatically_adapted_primaries", + "eotf", + "eotf_inverse", + "full_to_legal", + "gamma_function", + "hdr_CIELab_to_XYZ", + "hdr_IPT_to_XYZ", + "legal_to_full", + "linear_function", + "log_decoding", + "log_encoding", + "matrix_RGB_to_RGB", + "matrix_YCbCr", + "normalised_primary_matrix", + "oetf", + "oetf_inverse", + "offset_YCbCr", + "ootf", + "ootf_inverse", + "primaries_whitepoint", + "sRGB_to_XYZ", + "uv_to_Luv", + "uv_to_UCS", + "xyY_to_XYZ", + "xyY_to_xy", + "xy_to_Luv_uv", + "xy_to_UCS_uv", + "xy_to_XYZ", + "xy_to_xyY", ] __all__ += [ - 'CAM02LCD_to_JMh_CIECAM02', 'CAM02SCD_to_JMh_CIECAM02', - 'CAM02UCS_to_JMh_CIECAM02', 'CAM16LCD_to_JMh_CAM16', - 'CAM16SCD_to_JMh_CAM16', 'CAM16UCS_to_JMh_CAM16', 'CCTF_DECODINGS', - 'CCTF_ENCODINGS', 'CMYK_to_CMY', 'CMY_to_CMYK', 'CMY_to_RGB', 'CV_range', - 'DATA_MACADAM_1942_ELLIPSES', 'DIN99_to_Lab', 'EOTFS', 'EOTF_INVERSES', - 'HDR_CIELAB_METHODS', 'HDR_IPT_METHODS', 'HSL_to_RGB', 'HSV_to_RGB', - 'Hunter_Lab_to_XYZ', 'Hunter_Rdab_to_XYZ', 'ICTCP_to_RGB', 'IGPGTG_to_XYZ', - 'IPT_hue_angle', 'IPT_to_XYZ', 'JMh_CAM16_to_CAM16LCD', - 'JMh_CAM16_to_CAM16SCD', 'JMh_CAM16_to_CAM16UCS', - 'JMh_CIECAM02_to_CAM02LCD', 'JMh_CIECAM02_to_CAM02SCD', - 'JMh_CIECAM02_to_CAM02UCS', 'JzAzBz_to_XYZ', 'LCHab_to_Lab', - 'LCHuv_to_Luv', 'LOG_DECODINGS', 'LOG_ENCODINGS', 'Lab_to_DIN99', - 'Lab_to_LCHab', 'Lab_to_XYZ', 'Luv_to_LCHuv', 'Luv_to_XYZ', 'Luv_to_uv', - 'Luv_uv_to_xy', 'OETFS', 'OETF_INVERSES', 'OOTFS', 'OOTF_INVERSES', - 'OSA_UCS_to_XYZ', 'Prismatic_to_RGB', 'RGB_COLOURSPACES', - 'RGB_Colourspace', 'RGB_luminance', 'RGB_luminance_equation', 'RGB_to_CMY', - 'RGB_to_HSL', 'RGB_to_HSV', 'RGB_to_ICTCP', 'RGB_to_Prismatic', - 'RGB_to_RGB', 'RGB_to_XYZ', 'RGB_to_YCbCr', 'RGB_to_YCoCg', - 'RGB_to_YcCbcCrc', 'UCS_to_XYZ', 'UCS_to_uv', 'UCS_uv_to_xy', 'UVW_to_XYZ', - 'WEIGHTS_YCBCR', 'XYZ_to_Hunter_Lab', 'XYZ_to_Hunter_Rdab', - 'XYZ_to_IGPGTG', 'XYZ_to_IPT', 'XYZ_to_JzAzBz', - 'XYZ_to_K_ab_HunterLab1966', 'XYZ_to_Lab', 'XYZ_to_Luv', 'XYZ_to_OSA_UCS', - 'XYZ_to_RGB', 'XYZ_to_UCS', 'XYZ_to_UVW', 'XYZ_to_hdr_CIELab', - 'XYZ_to_hdr_IPT', 'XYZ_to_sRGB', 'XYZ_to_xy', 'XYZ_to_xyY', 'YCbCr_to_RGB', - 'YCoCg_to_RGB', 'YcCbcCrc_to_RGB', 'cctf_decoding', 'cctf_encoding', - 'chromatically_adapted_primaries', 'eotf', 'eotf_inverse', 'full_to_legal', - 'gamma_function', 'hdr_CIELab_to_XYZ', 'hdr_IPT_to_XYZ', 'legal_to_full', - 'linear_function', 'log_decoding', 'log_encoding', 'matrix_RGB_to_RGB', - 'normalised_primary_matrix', 'oetf', 'oetf_inverse', 'ootf', - 'ootf_inverse', 'primaries_whitepoint', 'sRGB_to_XYZ', 'uv_to_Luv', - 'uv_to_UCS', 'xyY_to_XYZ', 'xyY_to_xy', 'xy_to_Luv_uv', 'xy_to_UCS_uv', - 'xy_to_XYZ', 'xy_to_xyY' + "BRENEMAN_EXPERIMENTS", + "BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES", + "CORRESPONDING_CHROMATICITIES_PREDICTION_MODELS", + "CorrespondingColourDataset", + "CorrespondingChromaticitiesPrediction", + "corresponding_chromaticities_prediction", ] __all__ += [ - 'BRENEMAN_EXPERIMENTS', 'BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES', - 'CORRESPONDING_CHROMATICITIES_PREDICTION_MODELS', - 'CorrespondingColourDataset', 'CorrespondingChromaticitiesPrediction', - 'corresponding_chromaticities_prediction' + "CONTRAST_SENSITIVITY_METHODS", + "contrast_sensitivity_function", ] -__all__ += ['CONTRAST_SENSITIVITY_METHODS', 'contrast_sensitivity_function'] __all__ += [ - 'rayleigh_scattering', 'scattering_cross_section', 'sd_rayleigh_scattering' + "rayleigh_scattering", + "scattering_cross_section", + "sd_rayleigh_scattering", ] __all__ += [ - 'MUNSELL_COLOURS', 'MUNSELL_VALUE_METHODS', 'munsell_colour_to_xyY', - 'munsell_value', 'xyY_to_munsell_colour' + "MUNSELL_COLOURS", + "MUNSELL_VALUE_METHODS", + "munsell_colour_to_xyY", + "munsell_value", + "xyY_to_munsell_colour", ] __all__ += [ - 'COLOUR_FIDELITY_INDEX_METHODS', 'COLOUR_QUALITY_SCALE_METHODS', - 'colour_fidelity_index', 'colour_quality_scale', 'colour_rendering_index', - 'spectral_similarity_index' + "COLOUR_FIDELITY_INDEX_METHODS", + "COLOUR_QUALITY_SCALE_METHODS", + "colour_fidelity_index", + "colour_quality_scale", + "colour_rendering_index", + "spectral_similarity_index", ] -__all__ += ['XYZ_TO_SD_METHODS', 'XYZ_to_sd'] __all__ += [ - 'CCT_TO_UV_METHODS', 'CCT_TO_XY_METHODS', 'CCT_to_uv', 'CCT_to_xy', - 'UV_TO_CCT_METHODS', 'XY_TO_CCT_METHODS', 'uv_to_CCT', 'xy_to_CCT' + "XYZ_TO_SD_METHODS", + "XYZ_to_sd", ] __all__ += [ - 'CCS_COLOURCHECKERS', 'MATRIX_COLOUR_CORRECTION_METHODS', - 'COLOUR_CORRECTION_METHODS', 'MSDS_CAMERA_SENSITIVITIES', - 'MSDS_DISPLAY_PRIMARIES', 'POLYNOMIAL_EXPANSION_METHODS', - 'SDS_COLOURCHECKERS', 'SDS_FILTERS', 'SDS_LENSES', 'colour_correction', - 'matrix_colour_correction', 'matrix_idt', 'polynomial_expansion', - 'sd_to_aces_relative_exposure_values' + "CCT_TO_UV_METHODS", + "CCT_TO_XY_METHODS", + "CCT_to_uv", + "CCT_to_xy", + "UV_TO_CCT_METHODS", + "XY_TO_CCT_METHODS", + "uv_to_CCT", + "xy_to_CCT", ] __all__ += [ - 'OPTIMAL_COLOUR_STIMULI_ILLUMINANTS', 'RGB_colourspace_limits', - 'RGB_colourspace_pointer_gamut_coverage_MonteCarlo', - 'RGB_colourspace_visible_spectrum_coverage_MonteCarlo', - 'RGB_colourspace_volume_MonteCarlo', - 'RGB_colourspace_volume_coverage_MonteCarlo', 'is_within_macadam_limits', - 'is_within_mesh_volume', 'is_within_pointer_gamut', - 'is_within_visible_spectrum' + "CCS_COLOURCHECKERS", + "MATRIX_COLOUR_CORRECTION_METHODS", + "COLOUR_CORRECTION_METHODS", + "MSDS_CAMERA_SENSITIVITIES", + "MSDS_DISPLAY_PRIMARIES", + "POLYNOMIAL_EXPANSION_METHODS", + "SDS_COLOURCHECKERS", + "SDS_FILTERS", + "SDS_LENSES", + "camera_RGB_to_ACES2065_1", + "colour_correction", + "matrix_colour_correction", + "matrix_idt", + "polynomial_expansion", + "sd_to_ACES2065_1", + "sd_to_aces_relative_exposure_values", +] +__all__ += [ + "OPTIMAL_COLOUR_STIMULI_ILLUMINANTS", + "RGB_colourspace_limits", + "RGB_colourspace_pointer_gamut_coverage_MonteCarlo", + "RGB_colourspace_visible_spectrum_coverage_MonteCarlo", + "RGB_colourspace_volume_MonteCarlo", + "RGB_colourspace_volume_coverage_MonteCarlo", + "is_within_macadam_limits", + "is_within_mesh_volume", + "is_within_pointer_gamut", + "is_within_visible_spectrum", +] +__all__ += [ + "describe_conversion_path", + "convert", ] -__all__ += ['describe_conversion_path', 'convert'] -__application_name__ = 'Colour' +__application_name__ = "Colour" -__major_version__ = '0' -__minor_version__ = '3' -__change_version__ = '16' -__version__ = '.'.join( - (__major_version__, - __minor_version__, - __change_version__)) # yapf: disable +__major_version__ = "0" +__minor_version__ = "4" +__change_version__ = "0" +__version__ = ".".join( + (__major_version__, __minor_version__, __change_version__) +) # TODO: Remove legacy printing support when deemed appropriate. try: - np.set_printoptions(legacy='1.13') + np.set_printoptions(legacy="1.13") except TypeError: # pragma: no cover pass @@ -346,1633 +866,62 @@ def __getattr__(self, attribute): # --- API Changes and Deprecation Management ---# # ----------------------------------------------------------------------------# class colour(ModuleAPI): - def __getattr__(self, attribute): - return super(colour, self).__getattr__(attribute) + """Define a class acting like the *colour* module.""" + def __getattr__(self, attribute) -> Any: + """Return the value from the attribute with given name.""" -colour.__application_name__ = __application_name__ + return super().__getattr__(attribute) -colour.__major_version__ = __major_version__ -colour.__minor_version__ = __minor_version__ -colour.__change_version__ = __change_version__ -colour.__version__ = __version__ -# v0.3.11 +colour.__application_name__ = __application_name__ # type: ignore[attr-defined] + +colour.__major_version__ = __major_version__ # type: ignore[attr-defined] +colour.__minor_version__ = __minor_version__ # type: ignore[attr-defined] +colour.__change_version__ = __change_version__ # type: ignore[attr-defined] +colour.__version__ = __version__ # type: ignore[attr-defined] + +# v0.4.0 API_CHANGES = { - 'ObjectFutureAccessChange': [ - [ - 'colour.ACES_2065_1_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ACES2065_1', - ], - [ - 'colour.ACES_CCT_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ACESCCT', - ], - [ - 'colour.ACES_CC_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ACESCC', - ], - [ - 'colour.ACES_CG_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ACESCG', - ], - [ - 'colour.ACES_PROXY_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ACESPROXY', - ], - [ - 'colour.ACES_RICD', - 'colour.models.MSDS_ACES_RICD', - ], - [ - 'colour.ADOBE_RGB1998_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ADOBE_RGB1998', - ], - [ - 'colour.ADOBE_WIDE_GAMUT_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ADOBE_WIDE_GAMUT_RGB', - ], - [ - 'colour.ALEXA_WIDE_GAMUT_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ALEXA_WIDE_GAMUT', - ], - [ - 'colour.APPLE_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_APPLE_RGB', - ], - [ - 'colour.AVOGADRO_CONSTANT', - 'colour.constants.CONSTANT_AVOGADRO', - ], - [ - 'colour.AbstractContinuousFunction', - 'colour.continuous.AbstractContinuousFunction', - ], - [ - 'colour.BEST_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_BEST_RGB', - ], - [ - 'colour.BETA_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_BETA_RGB', - ], - [ - 'colour.BOLTZMANN_CONSTANT', - 'colour.constants.CONSTANT_BOLTZMANN', - ], - [ - 'colour.BRADFORD_CAT', - 'colour.adaptation.CAT_BRADFORD', - ], - [ - 'colour.BS_CAT', - 'colour.adaptation.CAT_BIANCO2010', - ], - [ - 'colour.BS_PC_CAT', - 'colour.adaptation.CAT_PC_BIANCO2010', - ], - [ - 'colour.BT2020_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_BT2020', - ], - [ - 'colour.BT470_525_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_BT470_525', - ], - [ - 'colour.BT470_625_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_BT470_625', - ], - [ - 'colour.BT709_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_BT709', - ], - [ - 'colour.CAM16_InductionFactors', - 'colour.appearance.InductionFactors_CAM16', - ], - [ - 'colour.CAT02_BRILL_CAT', - 'colour.adaptation.CAT_CAT02_BRILL2008', - ], - [ - 'colour.CAT02_CAT', - 'colour.adaptation.CAT_CAT02', - ], - [ - 'colour.CCT_to_uv_Krystek1985', - 'colour.temperature.CCT_to_uv_Krystek1985', - ], - [ - 'colour.CCT_to_uv_Ohno2013', - 'colour.temperature.CCT_to_uv_Ohno2013', - ], - [ - 'colour.CCT_to_uv_Robertson1968', - 'colour.temperature.CCT_to_uv_Robertson1968', - ], - [ - 'colour.CCT_to_xy_CIE_D', - 'colour.temperature.CCT_to_xy_CIE_D', - ], - [ - 'colour.CCT_to_xy_Kang2002', - 'colour.temperature.CCT_to_xy_Kang2002', - ], - [ - 'colour.CIECAM02_InductionFactors', - 'colour.appearance.InductionFactors_CIECAM02', - ], - [ - 'colour.CIE_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_CIE_RGB', - ], - [ - 'colour.CINEMA_GAMUT_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_CINEMA_GAMUT', - ], - [ - 'colour.CMCCAT2000_CAT', - 'colour.adaptation.CAT_CMCCAT2000', - ], - [ - 'colour.InductionFactors_CMCCAT2000', - 'colour.adaptation.InductionFactors_CMCCAT2000', - ], - [ - 'colour.CMCCAT97_CAT', - 'colour.adaptation.CAT_CMCCAT97', - ], - [ - 'colour.COLOR_MATCH_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_COLOR_MATCH_RGB', - ], - [ - 'colour.COLOURCHECKER_INDEXES_TO_NAMES_MAPPING', - 'colour.characterisation.COLOURCHECKER_INDEXES_TO_NAMES_MAPPING', - ], - [ - 'colour.COLOURSPACE_MODELS', - 'colour.models.COLOURSPACE_MODELS', - ], - [ - 'colour.COLOURSPACE_MODELS_LABELS', - 'colour.models.COLOURSPACE_MODELS_AXIS_LABELS', - ], - [ - 'colour.CQS_Specification', - 'colour.quality.ColourRendering_Specification_CQS', - ], - [ - 'colour.CRI_Specification', - 'colour.quality.ColourRendering_Specification_CRI', - ], - [ - 'colour.CaseInsensitiveMapping', - 'colour.utilities.CaseInsensitiveMapping', - ], - [ - 'colour.ColourWarning', - 'colour.utilities.ColourWarning', - ], - [ - 'colour.DCI_P3_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_DCI_P3', - ], - [ - 'colour.DCI_P3_P_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_DCI_P3_P', - ], - [ - 'colour.DEFAULT_FLOAT_DTYPE', - 'colour.constants.DEFAULT_FLOAT_DTYPE', - ], - [ - 'colour.DON_RGB_4_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_DON_RGB_4', - ], - [ - 'colour.DRAGON_COLOR_2_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_DRAGON_COLOR_2', - ], - [ - 'colour.DRAGON_COLOR_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_DRAGON_COLOR', - ], - [ - 'colour.D_ILLUMINANTS_S_SPDS', - 'colour.colorimetry.SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES', - ], - [ - 'colour.ECI_RGB_V2_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ECI_RGB_V2', - ], - [ - 'colour.EKTA_SPACE_PS_5_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_EKTA_SPACE_PS_5', - ], - [ - 'colour.EPSILON', - 'colour.constants.EPSILON', - ], - [ - 'colour.ERIMM_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ERIMM_RGB', - ], - [ - 'colour.FAIRCHILD_CAT', - 'colour.adaptation.CAT_FAIRCHILD', - ], - [ - 'colour.FLOATING_POINT_NUMBER_PATTERN', - 'colour.constants.FLOATING_POINT_NUMBER_PATTERN', - ], - [ - 'colour.Hunt_InductionFactors', - 'colour.appearance.InductionFactors_Hunt', - ], - [ - 'colour.INTEGER_THRESHOLD', - 'colour.constants.INTEGER_THRESHOLD', - ], - [ - 'colour.KP_M', - 'colour.constants.CONSTANT_KP_M', - ], - [ - 'colour.K_M', - 'colour.constants.CONSTANT_K_M', - ], - [ - 'colour.LIGHT_SPEED', - 'colour.constants.CONSTANT_LIGHT_SPEED', - ], - [ - 'colour.LLAB_InductionFactors', - 'colour.appearance.InductionFactors_LLAB', - ], - [ - 'colour.LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs', - 'colour.colorimetry.LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs', - ], - [ - 'colour.LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs', - 'colour.colorimetry.LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs', - ], - [ - 'colour.LMS_ConeFundamentals', - 'colour.colorimetry.LMS_ConeFundamentals', - ], - [ - 'colour.LineSegmentsIntersections_Specification', - 'colour.algebra.LineSegmentsIntersections_Specification', - ], - [ - 'colour.Lookup', - 'colour.utilities.Lookup', - ], - [ - 'colour.MAX_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_MAX_RGB', - ], - [ - 'colour.MUNSELL_COLOURS_1929', - 'colour.notation.MUNSELL_COLOURS_1929', - ], - [ - 'colour.MUNSELL_COLOURS_ALL', - 'colour.notation.MUNSELL_COLOURS_ALL', - ], - [ - 'colour.MUNSELL_COLOURS_REAL', - 'colour.notation.MUNSELL_COLOURS_REAL', - ], - [ - 'colour.MultiSignal', - 'colour.continuous.MultiSignals', - ], - [ - 'colour.NTSC1953_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_NTSC1953', - ], - [ - 'colour.PAL_SECAM_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_PAL_SECAM', - ], - [ - 'colour.PLANCK_CONSTANT', - 'colour.constants.CONSTANT_PLANCK', - ], - [ - 'colour.PROPHOTO_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_PROPHOTO_RGB', - ], - [ - 'colour.PROTUNE_NATIVE_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_PROTUNE_NATIVE', - ], - [ - 'colour.RED_COLOR_2_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_RED_COLOR_2', - ], - [ - 'colour.RED_COLOR_3_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_RED_COLOR_3', - ], - [ - 'colour.RED_COLOR_4_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_RED_COLOR_4', - ], - [ - 'colour.RED_COLOR_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_RED_COLOR', - ], - [ - 'colour.RED_WIDE_GAMUT_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_RED_WIDE_GAMUT_RGB', - ], - [ - 'colour.RGB_10_degree_cmfs_to_LMS_10_degree_cmfs', - 'colour.colorimetry.RGB_10_degree_cmfs_to_LMS_10_degree_cmfs', - ], - [ - 'colour.RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs', - 'colour.colorimetry.RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs', - ], - [ - 'colour.RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs', - 'colour.colorimetry.RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs', - ], - [ - 'colour.RGB_ColourMatchingFunctions', - 'colour.colorimetry.RGB_ColourMatchingFunctions', - ], - [ - 'colour.RGB_DisplayPrimaries', - 'colour.characterisation.RGB_DisplayPrimaries', - ], - [ - 'colour.RGB_SpectralSensitivities', - 'colour.characterisation.RGB_CameraSensitivities', - ], - [ - 'colour.RGB_to_sd_Smits1999', - 'colour.recovery.RGB_to_sd_Smits1999', - ], - [ - 'colour.RIMM_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_RIMM_RGB', - ], - [ - 'colour.ROMM_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ROMM_RGB', - ], - [ - 'colour.RUSSELL_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_RUSSELL_RGB', - ], - [ - 'colour.SHARP_CAT', - 'colour.adaptation.CAT_SHARP', - ], - [ - 'colour.SMITS_1999_SPDS', - 'colour.recovery.SDS_SMITS1999', - ], - [ - 'colour.SMPTE_240M_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_SMPTE_240M', - ], - [ - 'colour.S_GAMUT3_CINE_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_S_GAMUT3_CINE', - ], - [ - 'colour.S_GAMUT3_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_S_GAMUT3', - ], - [ - 'colour.S_GAMUT_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_S_GAMUT', - ], - [ - 'colour.Signal', - 'colour.continuous.Signal', - ], - [ - 'colour.Structure', - 'colour.utilities.Structure', - ], - [ - 'colour.TCS_SPDS', - 'colour.quality.SDS_TCS', - ], - [ - 'colour.VON_KRIES_CAT', - 'colour.adaptation.CAT_VON_KRIES', - ], - [ - 'colour.VS_SPDS', - 'colour.quality.SDS_VS', - ], - [ - 'colour.V_GAMUT_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_V_GAMUT', - ], - [ - 'colour.XTREME_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_XTREME_RGB', - ], - [ - 'colour.XYZ_ColourMatchingFunctions', - 'colour.colorimetry.XYZ_ColourMatchingFunctions', - ], - [ - 'colour.XYZ_SCALING_CAT', - 'colour.adaptation.CAT_XYZ_SCALING', - ], - [ - 'colour.XYZ_to_colourspace_model', - 'colour.models.XYZ_to_colourspace_model', - ], - [ - 'colour.XYZ_to_sd_Meng2015', - 'colour.recovery.XYZ_to_sd_Meng2015', - ], - [ - 'colour.adjust_tristimulus_weighting_factors_ASTME30815', - 'colour.colorimetry.adjust_tristimulus_weighting_factors_ASTME308', - ], - [ - 'colour.as_namedtuple', - 'colour.utilities.as_namedtuple', - ], - [ - 'colour.as_numeric', - 'colour.utilities.as_numeric', - ], - [ - 'colour.bandpass_correction_Stearns1988', - 'colour.colorimetry.bandpass_correction_Stearns1988', - ], - [ - 'colour.batch', - 'colour.utilities.batch', - ], - [ - 'colour.blackbody_spectral_radiance', - 'colour.colorimetry.blackbody_spectral_radiance', - ], - [ - 'colour.cartesian_to_cylindrical', - 'colour.algebra.cartesian_to_cylindrical', - ], - [ - 'colour.cartesian_to_polar', - 'colour.algebra.cartesian_to_polar', - ], - [ - 'colour.cartesian_to_spherical', - 'colour.algebra.cartesian_to_spherical', - ], - [ - 'colour.centroid', - 'colour.utilities.centroid', - ], - [ - 'colour.chromatic_adaptation_CIE1994', - 'colour.adaptation.chromatic_adaptation_CIE1994', - ], + "ObjectRenamed": [ [ - 'colour.chromatic_adaptation_CMCCAT2000', - 'colour.adaptation.chromatic_adaptation_CMCCAT2000', + "colour.RGB_to_ICTCP", + "colour.RGB_to_ICtCp", ], [ - 'colour.chromatic_adaptation_Fairchild1990', - 'colour.adaptation.chromatic_adaptation_Fairchild1990', + "colour.ICTCP_to_RGB", + "colour.ICtCp_to_RGB", ], [ - 'colour.chromatic_adaptation_VonKries', - 'colour.adaptation.chromatic_adaptation_VonKries', + "colour.RGB_to_IGPGTG", + "colour.RGB_to_IgPgTg", ], [ - 'colour.chromatic_adaptation_forward_CMCCAT2000', - 'colour.adaptation.chromatic_adaptation_forward_CMCCAT2000', + "colour.IGPGTG_to_RGB", + "colour.IgPgTg_to_RGB", ], [ - 'colour.matrix_chromatic_adaptation_VonKries', - 'colour.adaptation.matrix_chromatic_adaptation_VonKries', + "colour.XYZ_to_JzAzBz", + "colour.XYZ_to_Jzazbz", ], [ - 'colour.chromatic_adaptation_reverse_CMCCAT2000', - 'colour.adaptation.chromatic_adaptation_inverse_CMCCAT2000', - ], - [ - 'colour.closest', - 'colour.utilities.closest', - ], - [ - 'colour.closest_indexes', - 'colour.utilities.closest_indexes', - ], - [ - 'colour.corresponding_chromaticities_prediction_CIE1994', - 'colour.corresponding.corresponding_chromaticities_prediction_CIE1994', # noqa - ], - [ - 'colour.corresponding_chromaticities_prediction_CMCCAT2000', - 'colour.corresponding.corresponding_chromaticities_prediction_CMCCAT2000', # noqa - ], - [ - 'colour.corresponding_chromaticities_prediction_Fairchild1990', - 'colour.corresponding.corresponding_chromaticities_prediction_Fairchild1990', # noqa - ], - [ - 'colour.corresponding_chromaticities_prediction_VonKries', - 'colour.corresponding.corresponding_chromaticities_prediction_VonKries', # noqa - ], - [ - 'colour.cylindrical_to_cartesian', - 'colour.algebra.cylindrical_to_cartesian', - ], - [ - 'colour.delta_E_CAM02LCD', - 'colour.difference.delta_E_CAM02LCD', - ], - [ - 'colour.delta_E_CAM02SCD', - 'colour.difference.delta_E_CAM02SCD', - ], - [ - 'colour.delta_E_CAM02UCS', - 'colour.difference.delta_E_CAM02UCS', - ], - [ - 'colour.delta_E_CAM16LCD', - 'colour.difference.delta_E_CAM16LCD', - ], - [ - 'colour.delta_E_CAM16SCD', - 'colour.difference.delta_E_CAM16SCD', - ], - [ - 'colour.delta_E_CAM16UCS', - 'colour.difference.delta_E_CAM16UCS', - ], - [ - 'colour.delta_E_CIE1976', - 'colour.difference.delta_E_CIE1976', - ], - [ - 'colour.delta_E_CIE1994', - 'colour.difference.delta_E_CIE1994', - ], - [ - 'colour.delta_E_CIE2000', - 'colour.difference.delta_E_CIE2000', - ], - [ - 'colour.delta_E_CMC', - 'colour.difference.delta_E_CMC', - ], - [ - 'colour.dot_matrix', - 'colour.utilities.matrix_dot', - ], - [ - 'colour.dot_vector', - 'colour.utilities.vector_dot', - ], - [ - 'colour.eotf_BT1886', - 'colour.models.eotf_BT1886', - ], - [ - 'colour.eotf_BT2020', - 'colour.models.eotf_BT2020', - ], - [ - 'colour.eotf_BT2100_HLG', - 'colour.models.eotf_HLG_BT2100', - ], - [ - 'colour.eotf_BT2100_PQ', - 'colour.models.eotf_PQ_BT2100', - ], - [ - 'colour.eotf_DCIP3', - 'colour.models.eotf_DCDM', - ], - [ - 'colour.eotf_DICOMGSDF', - 'colour.models.eotf_DICOMGSDF', - ], - [ - 'colour.cctf_decoding_ProPhotoRGB', - 'colour.models.cctf_decoding_ProPhotoRGB', - ], - [ - 'colour.cctf_decoding_RIMMRGB', - 'colour.models.cctf_decoding_RIMMRGB', - ], - [ - 'colour.cctf_decoding_ROMMRGB', - 'colour.models.cctf_decoding_ROMMRGB', - ], - [ - 'colour.eotf_SMPTE240M', - 'colour.models.eotf_SMPTE240M', - ], - [ - 'colour.eotf_ST2084', - 'colour.models.eotf_ST2084', - ], - [ - 'colour.eotf_reverse_BT1886', - 'colour.models.eotf_inverse_BT1886', - ], - [ - 'colour.eotf_reverse_BT2100_HLG', - 'colour.models.eotf_inverse_HLG_BT2100', - ], - [ - 'colour.eotf_reverse_BT2100_PQ', - 'colour.models.eotf_inverse_PQ_BT2100', - ], - [ - 'colour.eotf_reverse_ST2084', - 'colour.models.eotf_inverse_ST2084', - ], - [ - 'colour.eotf_reverse_sRGB', - 'colour.models.eotf_inverse_sRGB', - ], - [ - 'colour.eotf_sRGB', - 'colour.models.eotf_sRGB', - ], - [ - 'colour.euclidean_distance', - 'colour.algebra.euclidean_distance', - ], - [ - 'colour.extend_line_segment', - 'colour.algebra.extend_line_segment', - ], - [ - 'colour.fill_nan', - 'colour.utilities.fill_nan', - ], - [ - 'colour.filter_kwargs', - 'colour.utilities.filter_kwargs', - ], - [ - 'colour.filter_warnings', - 'colour.utilities.filter_warnings', - ], - [ - 'colour.first_item', - 'colour.utilities.first_item', - ], - [ - 'colour.handle_numpy_errors', - 'colour.utilities.handle_numpy_errors', - ], - [ - 'colour.ignore_numpy_errors', - 'colour.utilities.ignore_numpy_errors', - ], - [ - 'colour.ignore_python_warnings', - 'colour.utilities.ignore_python_warnings', - ], - [ - 'colour.in_array', - 'colour.utilities.in_array', - ], - [ - 'colour.intersect_line_segments', - 'colour.algebra.intersect_line_segments', - ], - [ - 'colour.interval', - 'colour.utilities.interval', - ], - [ - 'colour.is_identity', - 'colour.algebra.is_identity', - ], - [ - 'colour.is_integer', - 'colour.utilities.is_integer', - ], - [ - 'colour.is_iterable', - 'colour.utilities.is_iterable', - ], - [ - 'colour.is_numeric', - 'colour.utilities.is_numeric', - ], - [ - 'colour.is_openimageio_installed', - 'colour.utilities.is_openimageio_installed', - ], - [ - 'colour.is_pandas_installed', - 'colour.utilities.is_pandas_installed', - ], - [ - 'colour.is_string', - 'colour.utilities.is_string', - ], - [ - 'colour.is_uniform', - 'colour.utilities.is_uniform', - ], - [ - 'colour.lagrange_coefficients_ASTME2022', - 'colour.colorimetry.lagrange_coefficients_ASTME2022', - ], - [ - 'colour.lightness_CIE1976', - 'colour.colorimetry.lightness_CIE1976', - ], - [ - 'colour.lightness_Fairchild2010', - 'colour.colorimetry.lightness_Fairchild2010', - ], - [ - 'colour.lightness_Fairchild2011', - 'colour.colorimetry.lightness_Fairchild2011', - ], - [ - 'colour.lightness_Glasser1958', - 'colour.colorimetry.lightness_Glasser1958', - ], - [ - 'colour.lightness_Wyszecki1963', - 'colour.colorimetry.lightness_Wyszecki1963', - ], - [ - 'colour.linear_conversion', - 'colour.utilities.linear_conversion', - ], - [ - 'colour.log_decoding_ACEScc', - 'colour.models.log_decoding_ACEScc', - ], - [ - 'colour.log_decoding_ACEScct', - 'colour.models.log_decoding_ACEScct', - ], - [ - 'colour.log_decoding_ACESproxy', - 'colour.models.log_decoding_ACESproxy', - ], - [ - 'colour.log_decoding_ALEXALogC', - 'colour.models.log_decoding_ALEXALogC', - ], - [ - 'colour.log_decoding_CanonLog', - 'colour.models.log_decoding_CanonLog', - ], - [ - 'colour.log_decoding_CanonLog2', - 'colour.models.log_decoding_CanonLog2', - ], - [ - 'colour.log_decoding_CanonLog3', - 'colour.models.log_decoding_CanonLog3', - ], - [ - 'colour.log_decoding_Cineon', - 'colour.models.log_decoding_Cineon', - ], - [ - 'colour.log_decoding_ERIMMRGB', - 'colour.models.log_decoding_ERIMMRGB', - ], - [ - 'colour.log_decoding_Log3G10', - 'colour.models.log_decoding_Log3G10', - ], - [ - 'colour.log_decoding_Log3G12', - 'colour.models.log_decoding_Log3G12', - ], - [ - 'colour.log_decoding_Panalog', - 'colour.models.log_decoding_Panalog', - ], - [ - 'colour.log_decoding_PivotedLog', - 'colour.models.log_decoding_PivotedLog', - ], - [ - 'colour.log_decoding_Protune', - 'colour.models.log_decoding_Protune', - ], - [ - 'colour.log_decoding_REDLog', - 'colour.models.log_decoding_REDLog', - ], - [ - 'colour.log_decoding_REDLogFilm', - 'colour.models.log_decoding_REDLogFilm', - ], - [ - 'colour.log_decoding_SLog', - 'colour.models.log_decoding_SLog', - ], - [ - 'colour.log_decoding_SLog2', - 'colour.models.log_decoding_SLog2', - ], - [ - 'colour.log_decoding_SLog3', - 'colour.models.log_decoding_SLog3', - ], - [ - 'colour.log_decoding_VLog', - 'colour.models.log_decoding_VLog', - ], - [ - 'colour.log_decoding_ViperLog', - 'colour.models.log_decoding_ViperLog', - ], - [ - 'colour.log_encoding_ACEScc', - 'colour.models.log_encoding_ACEScc', - ], - [ - 'colour.log_encoding_ACEScct', - 'colour.models.log_encoding_ACEScct', - ], - [ - 'colour.log_encoding_ACESproxy', - 'colour.models.log_encoding_ACESproxy', - ], - [ - 'colour.log_encoding_ALEXALogC', - 'colour.models.log_encoding_ALEXALogC', - ], - [ - 'colour.log_encoding_CanonLog', - 'colour.models.log_encoding_CanonLog', - ], - [ - 'colour.log_encoding_CanonLog2', - 'colour.models.log_encoding_CanonLog2', - ], - [ - 'colour.log_encoding_CanonLog3', - 'colour.models.log_encoding_CanonLog3', - ], - [ - 'colour.log_encoding_Cineon', - 'colour.models.log_encoding_Cineon', - ], - [ - 'colour.log_encoding_ERIMMRGB', - 'colour.models.log_encoding_ERIMMRGB', - ], - [ - 'colour.log_encoding_Log3G10', - 'colour.models.log_encoding_Log3G10', - ], - [ - 'colour.log_encoding_Log3G12', - 'colour.models.log_encoding_Log3G12', - ], - [ - 'colour.log_encoding_Panalog', - 'colour.models.log_encoding_Panalog', - ], - [ - 'colour.log_encoding_PivotedLog', - 'colour.models.log_encoding_PivotedLog', - ], - [ - 'colour.log_encoding_Protune', - 'colour.models.log_encoding_Protune', - ], - [ - 'colour.log_encoding_REDLog', - 'colour.models.log_encoding_REDLog', - ], - [ - 'colour.log_encoding_REDLogFilm', - 'colour.models.log_encoding_REDLogFilm', - ], - [ - 'colour.log_encoding_SLog', - 'colour.models.log_encoding_SLog', - ], - [ - 'colour.log_encoding_SLog2', - 'colour.models.log_encoding_SLog2', - ], - [ - 'colour.log_encoding_SLog3', - 'colour.models.log_encoding_SLog3', - ], - [ - 'colour.log_encoding_VLog', - 'colour.models.log_encoding_VLog', - ], - [ - 'colour.luminance_ASTMD153508', - 'colour.colorimetry.luminance_ASTMD1535', - ], - [ - 'colour.luminance_CIE1976', - 'colour.colorimetry.luminance_CIE1976', - ], - [ - 'colour.luminance_Fairchild2010', - 'colour.colorimetry.luminance_Fairchild2010', - ], - [ - 'colour.luminance_Fairchild2011', - 'colour.colorimetry.luminance_Fairchild2011', - ], - [ - 'colour.luminance_Newhall1943', - 'colour.colorimetry.luminance_Newhall1943', - ], - [ - 'colour.mesopic_weighting_function', - 'colour.colorimetry.mesopic_weighting_function', - ], - [ - 'colour.message_box', - 'colour.utilities.message_box', - ], - [ - 'colour.munsell_value_ASTMD153508', - 'colour.notation.munsell_value_ASTMD1535', - ], - [ - 'colour.munsell_value_Ladd1955', - 'colour.notation.munsell_value_Ladd1955', - ], - [ - 'colour.munsell_value_McCamy1987', - 'colour.notation.munsell_value_McCamy1987', - ], - [ - 'colour.munsell_value_Moon1943', - 'colour.notation.munsell_value_Moon1943', - ], - [ - 'colour.munsell_value_Munsell1933', - 'colour.notation.munsell_value_Munsell1933', - ], - [ - 'colour.munsell_value_Priest1920', - 'colour.notation.munsell_value_Priest1920', - ], - [ - 'colour.munsell_value_Saunderson1944', - 'colour.notation.munsell_value_Saunderson1944', - ], - [ - 'colour.ndarray_write', - 'colour.utilities.ndarray_write', - ], - [ - 'colour.normalise_maximum', - 'colour.utilities.normalise_maximum', - ], - [ - 'colour.normalise_vector', - 'colour.algebra.normalise_vector', - ], - [ - 'colour.numpy_print_options', - 'colour.utilities.numpy_print_options', - ], - [ - 'colour.oetf_ARIBSTDB67', - 'colour.models.oetf_ARIBSTDB67', - ], - [ - 'colour.oetf_BT2020', - 'colour.models.eotf_inverse_BT2020', - ], - [ - 'colour.oetf_BT2100_HLG', - 'colour.models.oetf_HLG_BT2100', - ], - [ - 'colour.oetf_BT2100_PQ', - 'colour.models.oetf_PQ_BT2100', - ], - [ - 'colour.oetf_BT601', - 'colour.models.oetf_BT601', - ], - [ - 'colour.oetf_BT709', - 'colour.models.oetf_BT709', - ], - [ - 'colour.oetf_DCIP3', - 'colour.models.eotf_inverse_DCIP3', - ], - [ - 'colour.oetf_DICOMGSDF', - 'colour.models.eotf_inverse_DICOMGSDF', - ], - [ - 'colour.cctf_encoding_ProPhotoRGB', - 'colour.models.cctf_encoding_ProPhotoRGB', - ], - [ - 'colour.cctf_encoding_RIMMRGB', - 'colour.models.cctf_encoding_RIMMRGB', - ], - [ - 'colour.cctf_encoding_ROMMRGB', - 'colour.models.cctf_encoding_ROMMRGB', - ], - [ - 'colour.oetf_SMPTE240M', - 'colour.models.oetf_SMPTE240M', - ], - [ - 'colour.oetf_reverse_ARIBSTDB67', - 'colour.models.oetf_inverse_ARIBSTDB67', - ], - [ - 'colour.oetf_reverse_BT2100_HLG', - 'colour.models.oetf_inverse_HLG_BT2100', - ], - [ - 'colour.oetf_reverse_BT2100_PQ', - 'colour.models.oetf_inverse_PQ_BT2100', - ], - [ - 'colour.oetf_reverse_BT601', - 'colour.models.oetf_inverse_BT601', - ], - [ - 'colour.oetf_reverse_BT709', - 'colour.models.oetf_inverse_BT709', - ], - [ - 'colour.ootf_BT2100_HLG', - 'colour.models.ootf_HLG_BT2100', - ], - [ - 'colour.ootf_BT2100_PQ', - 'colour.models.ootf_PQ_BT2100', - ], - [ - 'colour.ootf_reverse_BT2100_HLG', - 'colour.models.ootf_inverse_HLG_BT2100', - ], - [ - 'colour.ootf_reverse_BT2100_PQ', - 'colour.models.ootf_inverse_PQ_BT2100', - ], - [ - 'colour.orient', - 'colour.utilities.orient', - ], - [ - 'colour.planck_law', - 'colour.colorimetry.planck_law', - ], - [ - 'colour.polar_to_cartesian', - 'colour.algebra.polar_to_cartesian', - ], - [ - 'colour.print_numpy_errors', - 'colour.utilities.print_numpy_errors', - ], - [ - 'colour.raise_numpy_errors', - 'colour.utilities.raise_numpy_errors', - ], - [ - 'colour.random_triplet_generator', - 'colour.algebra.random_triplet_generator', - ], - [ - 'colour.rayleigh_optical_depth', - 'colour.phenomena.rayleigh_optical_depth', - ], - [ - 'colour.reaction_rate_MichealisMenten', - 'colour.biochemistry.reaction_rate_MichealisMenten', - ], - [ - 'colour.row_as_diagonal', - 'colour.utilities.row_as_diagonal', - ], - [ - 'colour.sRGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_sRGB', - ], - [ - 'colour.sd_to_XYZ_ASTME30815', - 'colour.colorimetry.sd_to_XYZ_ASTME308', - ], - [ - 'colour.sd_to_XYZ_integration', - 'colour.colorimetry.sd_to_XYZ_integration', - ], - [ - 'colour.sd_to_XYZ_tristimulus_weighting_factors_ASTME30815', - 'colour.colorimetry.sd_to_XYZ_tristimulus_weighting_factors_ASTME308', # noqa - ], - [ - 'colour.spherical_to_cartesian', - 'colour.algebra.spherical_to_cartesian', - ], - [ - 'colour.substrate_concentration_MichealisMenten', - 'colour.biochemistry.substrate_concentration_MichealisMenten', - ], - [ - 'colour.tristimulus_weighting_factors_ASTME2022', - 'colour.colorimetry.tristimulus_weighting_factors_ASTME2022', - ], - [ - 'colour.tsplit', - 'colour.utilities.tsplit', - ], - [ - 'colour.tstack', - 'colour.utilities.tstack', - ], - [ - 'colour.uv_to_CCT_Ohno2013', - 'colour.temperature.uv_to_CCT_Ohno2013', - ], - [ - 'colour.uv_to_CCT_Robertson1968', - 'colour.temperature.uv_to_CCT_Robertson1968', - ], - [ - 'colour.warn_numpy_errors', - 'colour.utilities.warn_numpy_errors', - ], - [ - 'colour.warning', - 'colour.utilities.warning', - ], - [ - 'colour.whiteness_ASTME313', - 'colour.colorimetry.whiteness_ASTME313', - ], - [ - 'colour.whiteness_Berger1959', - 'colour.colorimetry.whiteness_Berger1959', - ], - [ - 'colour.whiteness_CIE2004', - 'colour.colorimetry.whiteness_CIE2004', - ], - [ - 'colour.whiteness_Ganz1979', - 'colour.colorimetry.whiteness_Ganz1979', - ], - [ - 'colour.whiteness_Stensby1968', - 'colour.colorimetry.whiteness_Stensby1968', - ], - [ - 'colour.whiteness_Taube1960', - 'colour.colorimetry.whiteness_Taube1960', - ], - [ - 'colour.xy_to_CCT_Hernandez1999', - 'colour.temperature.xy_to_CCT_Hernandez1999', - ], - [ - 'colour.xy_to_CCT_McCamy1992', - 'colour.temperature.xy_to_CCT_McCamy1992', - ], - [ - 'colour.yellowness_ASTMD1925', - 'colour.colorimetry.yellowness_ASTMD1925', - ], - [ - 'colour.yellowness_ASTME313', - 'colour.colorimetry.yellowness_ASTME313', + "colour.JzAzBz_to_XYZ", + "colour.Jzazbz_to_XYZ", ], ] } -""" -Defines *colour* package API changes. - -API_CHANGES : dict -""" - -API_CHANGES.update({ - 'ObjectRemoved': [ - 'colour.DEFAULT_WAVELENGTH_DECIMALS', - 'colour.ArbitraryPrecisionMapping', - 'colour.SpectralMapping', - ], - 'ObjectRenamed': [ - [ - 'colour.eotf_ARIBSTDB67', - 'colour.models.oetf_inverse_ARIBSTDB67', - ], - [ - 'colour.eotf_BT709', - 'colour.models.oetf_inverse_BT709', - ], - [ - 'colour.oetf_BT1886', - 'colour.models.eotf_inverse_BT1886', - ], - [ - 'colour.eotf_sRGB', - 'colour.models.eotf_sRGB', - ], - [ - 'colour.ALEXA_WIDE_GAMUT_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ALEXA_WIDE_GAMUT', - ], - [ - 'colour.NTSC_1953_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_NTSC1953', - ], - [ - 'colour.PAL_SECAM_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_PAL_SECAM', - ], - [ - 'colour.REC_709_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_BT709', - ], - [ - 'colour.REC_2020_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_BT2020', - ], - [ - 'colour.SMPTE_C_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_SMPTE_240M', - ], - [ - 'colour.TriSpectralPowerDistribution', - 'colour.MultiSpectralDistributions', - ], - ] -}) - -# v0.3.12 -API_CHANGES['ObjectRenamed'] = API_CHANGES['ObjectRenamed'] + [ - [ - 'colour.CIE_standard_illuminant_A_function', - 'colour.sd_CIE_standard_illuminant_A', - ], - [ - 'colour.COLOURCHECKERS_SPDS', - 'colour.SDS_COLOURCHECKERS', - ], - [ - 'colour.D_illuminant_relative_spd', - 'colour.sd_CIE_illuminant_D_series', - ], - [ - 'colour.ILLUMINANTS_RELATIVE_SPDS', - 'colour.SDS_ILLUMINANTS', - ], - [ - 'colour.LIGHT_SOURCES_RELATIVE_SPDS', - 'colour.SDS_LIGHT_SOURCES', - ], - [ - 'colour.MultiSpectralPowerDistribution', - 'colour.MultiSpectralDistributions', - ], - [ - 'colour.REFLECTANCE_RECOVERY_METHODS', - 'colour.XYZ_TO_SD_METHODS', - ], - [ - 'colour.SPECTRAL_TO_XYZ_METHODS', - 'colour.SD_TO_XYZ_METHODS', - ], - [ - 'colour.SpectralPowerDistribution', - 'colour.SpectralDistribution', - ], - [ - 'colour.blackbody_spd', - 'colour.sd_blackbody', - ], - [ - 'colour.constant_spd', - 'colour.sd_constant', - ], - [ - 'colour.first_order_colour_fit', - 'colour.matrix_colour_correction', - ], - [ - 'colour.IES_TM2714_Spd', - 'colour.SpectralDistribution_IESTM2714', - ], - [ - 'colour.function_gamma', - 'colour.gamma_function', - ], - [ - 'colour.function_linear', - 'colour.linear_function', - ], - [ - 'colour.mesopic_luminous_efficiency_function', - 'colour.sd_mesopic_luminous_efficiency_function', - ], - [ - 'colour.ones_spd', - 'colour.sd_ones', - ], - [ - 'colour.rayleigh_scattering_spd', - 'colour.sd_rayleigh_scattering', - ], - [ - 'colour.read_spds_from_csv_file', - 'colour.read_sds_from_csv_file', - ], - [ - 'colour.read_spds_from_xrite_file', - 'colour.read_sds_from_xrite_file', - ], - [ - 'colour.spectral_to_aces_relative_exposure_values', - 'colour.sd_to_aces_relative_exposure_values', - ], - [ - 'colour.spectral_to_XYZ', - 'colour.sd_to_XYZ', - ], - [ - 'colour.write_spds_to_csv_file', - 'colour.write_sds_to_csv_file', - ], - [ - 'colour.XYZ_to_spectral', - 'colour.XYZ_to_sd', - ], - [ - 'colour.zeros_spd', - 'colour.sd_zeros', - ], -] - -# v0.3.14 -API_CHANGES['ObjectRenamed'] = API_CHANGES['ObjectRenamed'] + [ - [ - 'colour.ASTME30815_PRACTISE_SHAPE', - 'colour.SPECTRAL_SHAPE_ASTME308', - ], - [ - 'colour.decoding_cctf', - 'colour.cctf_decoding', - ], - [ - 'colour.DECODING_CCTFS', - 'colour.CCTF_DECODINGS', - ], - [ - 'colour.encoding_cctf', - 'colour.cctf_encoding', - ], - [ - 'colour.ENCODING_CCTFS', - 'colour.CCTF_ENCODINGS', - ], - [ - 'colour.EOTFS_REVERSE', - 'colour.EOTF_INVERSES', - ], - [ - 'colour.eotf_reverse', - 'colour.eotf_inverse', - ], - [ - 'colour.log_decoding_curve', - 'colour.log_decoding', - ], - [ - 'colour.LOG_DECODING_CURVES', - 'colour.LOG_DECODINGS', - ], - [ - 'colour.log_encoding_curve', - 'colour.log_encoding', - ], - [ - 'colour.LOG_ENCODING_CURVES', - 'colour.LOG_ENCODINGS', - ], - [ - 'colour.OETFS_REVERSE', - 'colour.OETF_INVERSES', - ], - [ - 'colour.oetf_reverse', - 'colour.oetf_inverse', - ], - [ - 'colour.OOTFS_REVERSE', - 'colour.OOTF_INVERSES', - ], - [ - 'colour.ootf_reverse', - 'colour.ootf_inverse', - ], - [ - 'colour.MultiSpectralDistribution', - 'colour.MultiSpectralDistributions', - ], -] - -# v0.3.16 -API_CHANGES['ObjectRenamed'] = API_CHANGES['ObjectRenamed'] + [ - [ - 'colour.ASTME308_PRACTISE_SHAPE', - 'colour.SPECTRAL_SHAPE_ASTME308', - ], - [ - 'colour.ATD95_Specification', - 'colour.CAM_Specification_ATD95', - ], - [ - 'colour.BRENEMAN_EXPERIMENTS_PRIMARIES_CHROMATICITIES', - 'colour.BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES', - ], - [ - 'colour.CAM16_Specification', - 'colour.CAM_Specification_CAM16', - ], - [ - 'colour.CIECAM02_Specification', - 'colour.CAM_Specification_CIECAM02', - ], - [ - 'colour.CAMERAS_RGB_SPECTRAL_SENSITIVITIES', - 'colour.MSDS_CAMERA_SENSITIVITIES', - ], - [ - 'colour.CMCCAT2000_VIEWING_CONDITIONS', - 'colour.VIEWING_CONDITIONS_CMCCAT2000', - ], - [ - 'colour.CMFS', - 'colour.colorimetry.MSDS_CMFS', - ], - [ - 'colour.COLOURCHECKERS', - 'colour.CCS_COLOURCHECKERS', - ], - [ - 'colour.CMCCAT2000_VIEWING_CONDITIONS', - 'colour.VIEWING_CONDITIONS_CMCCAT2000', - ], - [ - 'colour.COLOURCHECKERS', - 'colour.CCS_COLOURCHECKERS', - ], - [ - 'colour.COLOURCHECKERS_SDS', - 'colour.SDS_COLOURCHECKERS', - ], - [ - 'colour.COLOUR_CORRECTION_MATRIX_METHODS', - 'colour.MATRIX_COLOUR_CORRECTION_METHODS', - ], - [ - 'colour.DEFAULT_SPECTRAL_SHAPE', - 'colour.SPECTRAL_SHAPE_DEFAULT', - ], - [ - 'colour.DISPLAYS_RGB_PRIMARIES', - 'colour.MSDS_DISPLAY_PRIMARIES', - ], - [ - 'colour.Hunt_Specification', - 'colour.CAM_Specification_Hunt', - ], - [ - 'colour.HUNTERLAB_ILLUMINANTS', - 'colour.TVS_ILLUMINANTS_HUNTERLAB', - ], - [ - 'colour.ILLUMINANTS', - 'colour.CCS_ILLUMINANTS', - ], - [ - 'colour.ILLUMINANTS_OPTIMAL_COLOUR_STIMULI', - 'colour.OPTIMAL_COLOUR_STIMULI_ILLUMINANTS', - ], - [ - 'colour.ILLUMINANTS_SDS', - 'colour.SDS_ILLUMINANTS', - ], - [ - 'colour.LEFS', - 'colour.SDS_LEFS', - ], - [ - 'colour.LIGHT_SOURCES', - 'colour.CCS_LIGHT_SOURCES', - ], - [ - 'colour.LIGHT_SOURCES_SDS', - 'colour.SDS_LIGHT_SOURCES', - ], - [ - 'colour.LLAB_Specification', - 'colour.CAM_Specification_LLAB', - ], - [ - 'colour.LMS_CMFS', - 'colour.colorimetry.MSDS_CMFS_LMS', - ], - [ - 'colour.MULTI_SD_TO_XYZ_METHODS', - 'colour.MSDS_TO_XYZ_METHODS', - ], - [ - 'colour.multi_sds_to_XYZ', - 'colour.msds_to_XYZ', - ], - [ - 'colour.Nayatani95_Specification', - 'colour.CAM_Specification_Nayatani95', - ], - [ - 'colour.PHOTOPIC_LEFS', - 'colour.colorimetry.SDS_LEFS_PHOTOPIC', - ], - [ - 'colour.POINTER_GAMUT_BOUNDARIES', - 'colour.models.CCS_POINTER_GAMUT_BOUNDARY', - ], - [ - 'colour.POINTER_GAMUT_DATA', - 'colour.models.DATA_POINTER_GAMUT_VOLUME', - ], - [ - 'colour.POINTER_GAMUT_ILLUMINANT', - 'colour.models.CCS_ILLUMINANT_POINTER_GAMUT', - ], - [ - 'colour.RGB_CMFS', - 'colour.colorimetry.MSDS_CMFS_RGB', - ], - [ - 'colour.RGB_to_RGB_matrix', - 'colour.matrix_RGB_to_RGB', - ], - [ - 'colour.RLAB_D_FACTOR', - 'colour.appearance.D_FACTOR_RLAB', - ], - [ - 'colour.RLAB_Specification', - 'colour.CAM_Specification_RLAB', - ], - [ - 'colour.SCOTOPIC_LEFS', - 'colour.colorimetry.SDS_LEFS_SCOTOPIC', - ], - [ - 'colour.STANDARD_OBSERVERS_CMFS', - 'colour.colorimetry.MSDS_CMFS_STANDARD_OBSERVER', - ], - [ - 'colour.YCBCR_WEIGHTS', - 'colour.WEIGHTS_YCBCR', - ], - [ - 'colour.characterisation.colour_correction_matrix', - 'colour.characterisation.matrix_colour_correction', - ], - # Not strictly needed but in use by A.M.P.A.S. - [ - 'colour.characterisation.idt_matrix', - 'colour.characterisation.matrix_idt', - ], -] +"""Defines the *colour.models* sub-package API changes.""" if not is_documentation_building(): - sys.modules['colour'] = colour(sys.modules['colour'], - build_API_changes(API_CHANGES)) + sys.modules["colour"] = colour( # type: ignore[assignment] + sys.modules["colour"], build_API_changes(API_CHANGES) + ) del ModuleAPI, is_documentation_building, build_API_changes, sys + +colour.__disable_lazy_load__ = True # type: ignore[attr-defined] +__disable_lazy_load__ = colour.__disable_lazy_load__ # type: ignore[attr-defined] +""" +Ensures that the lazy loaded datasets are not transformed during import. +See :class:`colour.utilities.LazyCaseInsensitiveMapping` for more information. +""" diff --git a/colour/adaptation/__init__.py b/colour/adaptation/__init__.py index 63f93dbd09..0f053ae857 100644 --- a/colour/adaptation/__init__.py +++ b/colour/adaptation/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ References ---------- @@ -20,129 +19,196 @@ - :cite:`Westland2012k` : Westland, S., Ripamonti, C., & Cheung, V. (2012). CMCCAT2000. In Computational Colour Science Using MATLAB (2nd ed., pp. 83-86). ISBN:978-0-470-66569-5 +- :cite:`Zhai2018` : Zhai, Q., & Luo, M. R. (2018). Study of chromatic + adaptation via neutral white matches on different viewing media. Optics + Express, 26(6), 7724. doi:10.1364/OE.26.007724 """ -from __future__ import absolute_import - -import sys - -from colour.utilities.deprecation import ModuleAPI, build_API_changes -from colour.utilities.documentation import is_documentation_building - -from colour.utilities import (CaseInsensitiveMapping, filter_kwargs, - get_domain_range_scale, as_float_array) - -from .datasets import * # noqa -from . import datasets -from .vonkries import (matrix_chromatic_adaptation_VonKries, - chromatic_adaptation_VonKries) +from __future__ import annotations + +from colour.hints import Any, ArrayLike, Literal, NDArray, Union +from colour.utilities import ( + CaseInsensitiveMapping, + filter_kwargs, + get_domain_range_scale, + as_float_array, +) + +from .datasets import CHROMATIC_ADAPTATION_TRANSFORMS +from .datasets import ( + CAT_BIANCO2010, + CAT_BRADFORD, + CAT_CAT02, + CAT_CAT02_BRILL2008, + CAT_CAT16, + CAT_CMCCAT2000, + CAT_CMCCAT97, + CAT_FAIRCHILD, + CAT_PC_BIANCO2010, + CAT_SHARP, + CAT_VON_KRIES, + CAT_XYZ_SCALING, +) +from .vonkries import ( + matrix_chromatic_adaptation_VonKries, + chromatic_adaptation_VonKries, +) from .fairchild1990 import chromatic_adaptation_Fairchild1990 from .cmccat2000 import ( - InductionFactors_CMCCAT2000, VIEWING_CONDITIONS_CMCCAT2000, + InductionFactors_CMCCAT2000, + VIEWING_CONDITIONS_CMCCAT2000, chromatic_adaptation_forward_CMCCAT2000, - chromatic_adaptation_inverse_CMCCAT2000, chromatic_adaptation_CMCCAT2000) + chromatic_adaptation_inverse_CMCCAT2000, + chromatic_adaptation_CMCCAT2000, +) from .cie1994 import chromatic_adaptation_CIE1994 +from .zhai2018 import chromatic_adaptation_Zhai2018 +from colour.utilities import validate_method -__all__ = [] -__all__ += datasets.__all__ +__all__ = ["CHROMATIC_ADAPTATION_TRANSFORMS"] +__all__ += [ + "CAT_BIANCO2010", + "CAT_BRADFORD", + "CAT_CAT02", + "CAT_CAT02_BRILL2008", + "CAT_CAT16", + "CAT_CMCCAT2000", + "CAT_CMCCAT97", + "CAT_FAIRCHILD", + "CAT_PC_BIANCO2010", + "CAT_SHARP", + "CAT_VON_KRIES", + "CAT_XYZ_SCALING", +] +__all__ += [ + "matrix_chromatic_adaptation_VonKries", + "chromatic_adaptation_VonKries", +] __all__ += [ - 'matrix_chromatic_adaptation_VonKries', 'chromatic_adaptation_VonKries' + "chromatic_adaptation_Fairchild1990", ] -__all__ += ['chromatic_adaptation_Fairchild1990'] __all__ += [ - 'InductionFactors_CMCCAT2000', 'VIEWING_CONDITIONS_CMCCAT2000', - 'chromatic_adaptation_forward_CMCCAT2000', - 'chromatic_adaptation_inverse_CMCCAT2000', - 'chromatic_adaptation_CMCCAT2000' + "InductionFactors_CMCCAT2000", + "VIEWING_CONDITIONS_CMCCAT2000", + "chromatic_adaptation_forward_CMCCAT2000", + "chromatic_adaptation_inverse_CMCCAT2000", + "chromatic_adaptation_CMCCAT2000", ] -__all__ += ['chromatic_adaptation_CIE1994'] - -CHROMATIC_ADAPTATION_METHODS = CaseInsensitiveMapping({ - 'CIE 1994': chromatic_adaptation_CIE1994, - 'CMCCAT2000': chromatic_adaptation_CMCCAT2000, - 'Fairchild 1990': chromatic_adaptation_Fairchild1990, - 'Von Kries': chromatic_adaptation_VonKries, -}) +__all__ += [ + "chromatic_adaptation_CIE1994", +] +__all__ += [ + "chromatic_adaptation_Zhai2018", +] + +CHROMATIC_ADAPTATION_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "CIE 1994": chromatic_adaptation_CIE1994, + "CMCCAT2000": chromatic_adaptation_CMCCAT2000, + "Fairchild 1990": chromatic_adaptation_Fairchild1990, + "Von Kries": chromatic_adaptation_VonKries, + "Zhai 2018": chromatic_adaptation_Zhai2018, + } +) CHROMATIC_ADAPTATION_METHODS.__doc__ = """ Supported chromatic adaptation methods. References ---------- :cite:`CIETC1-321994b`, :cite:`Fairchild1991a`, :cite:`Fairchild2013s`, -:cite:`Fairchild2013t`, :cite:`Li2002a`, :cite:`Westland2012k` - -CHROMATIC_ADAPTATION_METHODS : CaseInsensitiveMapping - **{'CIE 1994', 'CMCCAT2000', 'Fairchild 1990', 'Von Kries'}** +:cite:`Fairchild2013t`, :cite:`Li2002a`, :cite:`Westland2012k`, +:cite:`Zhai2018` """ -def chromatic_adaptation(XYZ, XYZ_w, XYZ_wr, method='Von Kries', **kwargs): +def chromatic_adaptation( + XYZ: ArrayLike, + XYZ_w: ArrayLike, + XYZ_wr: ArrayLike, + method: Union[ + Literal[ + "CIE 1994", + "CMCCAT2000", + "Fairchild 1990", + "Zhai 2018", + "Von Kries", + ], + str, + ] = "Von Kries", + **kwargs: Any, +) -> NDArray: """ - Adapts given stimulus from test viewing conditions to reference viewing + Adapt given stimulus from test viewing conditions to reference viewing conditions. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values of stimulus to adapt. - XYZ_w : array_like + XYZ_w Test viewing condition *CIE XYZ* tristimulus values of the whitepoint. - XYZ_wr : array_like + XYZ_wr Reference viewing condition *CIE XYZ* tristimulus values of the whitepoint. - method : unicode, optional - **{'Von Kries', 'CIE 1994', 'CMCCAT2000', 'Fairchild 1990'}**, + method Computation method. Other Parameters ---------------- - E_o1 : numeric + E_o1 {:func:`colour.adaptation.chromatic_adaptation_CIE1994`}, Test illuminance :math:`E_{o1}` in :math:`cd/m^2`. - E_o2 : numeric + E_o2 {:func:`colour.adaptation.chromatic_adaptation_CIE1994`}, Reference illuminance :math:`E_{o2}` in :math:`cd/m^2`. - L_A1 : numeric or array_like - {:func:`colour.adaptation.chromatic_adaptation_CMCCAT2000`}, - Luminance of test adapting field :math:`L_{A1}` in :math:`cd/m^2`. - L_A2 : numeric or array_like - {:func:`colour.adaptation.chromatic_adaptation_CMCCAT2000`}, - Luminance of reference adapting field :math:`L_{A2}` in :math:`cd/m^2`. - Y_n : numeric or array_like - {:func:`colour.adaptation.chromatic_adaptation_Fairchild1990`}, - Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`. - Y_o : numeric + n + {:func:`colour.adaptation.chromatic_adaptation_CIE1994`}, + Noise component in fundamental primary system. + Y_o {:func:`colour.adaptation.chromatic_adaptation_CIE1994`}, Luminance factor :math:`Y_o` of achromatic background normalised to domain [0.18, 1] in **'Reference'** domain-range scale. - direction : unicode, optional + direction {:func:`colour.adaptation.chromatic_adaptation_CMCCAT2000`}, - **{'Forward', 'Inverse'}**, Chromatic adaptation direction. - discount_illuminant : bool, optional - {:func:`colour.adaptation.chromatic_adaptation_Fairchild1990`}, - Truth value indicating if the illuminant should be discounted. - n : numeric, optional - {:func:`colour.adaptation.chromatic_adaptation_CIE1994`}, - Noise component in fundamental primary system. - surround : InductionFactors_CMCCAT2000, optional + L_A1 + {:func:`colour.adaptation.chromatic_adaptation_CMCCAT2000`}, + Luminance of test adapting field :math:`L_{A1}` in :math:`cd/m^2`. + L_A2 + {:func:`colour.adaptation.chromatic_adaptation_CMCCAT2000`}, + Luminance of reference adapting field :math:`L_{A2}` in :math:`cd/m^2`. + surround {:func:`colour.adaptation.chromatic_adaptation_CMCCAT2000`}, Surround viewing conditions induction factors. - transform : unicode, optional - {:func:`colour.adaptation.chromatic_adaptation_VonKries`}, - **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', - 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02 Brill 2008', - 'Bianco 2010', 'Bianco PC 2010'}**, + discount_illuminant + {:func:`colour.adaptation.chromatic_adaptation_Fairchild1990`}, + Truth value indicating if the illuminant should be discounted. + Y_n + {:func:`colour.adaptation.chromatic_adaptation_Fairchild1990`}, + Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`. + D_b + {:func:`colour.adaptation.chromatic_adaptation_Zhai2018`}, + Degree of adaptation :math:`D_\\beta` of input illuminant + :math:`\\beta`. + D_d + {:func:`colour.adaptation.chromatic_adaptation_Zhai2018`}, + Degree of adaptation :math:`D_\\Delta` of output illuminant + :math:`\\Delta`. + transform + {:func:`colour.adaptation.chromatic_adaptation_VonKries`, + :func:`colour.adaptation.chromatic_adaptation_Zhai2018`}, Chromatic adaptation transform. + XYZ_wo + {:func:`colour.adaptation.chromatic_adaptation_Zhai2018`}, + Baseline illuminant (:math:`BI`) :math:`o`. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ_c* tristimulus values of the stimulus corresponding colour. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -152,6 +218,8 @@ def chromatic_adaptation(XYZ, XYZ_w, XYZ_wr, method='Von Kries', **kwargs): +------------+-----------------------+---------------+ | ``XYZ_wr`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ + | ``XYZ_wo`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ | ``Y_o`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ @@ -168,7 +236,6 @@ def chromatic_adaptation(XYZ, XYZ_w, XYZ_wr, method='Von Kries', **kwargs): Examples -------- - *Von Kries* chromatic adaptation: >>> import numpy as np @@ -210,31 +277,55 @@ def chromatic_adaptation(XYZ, XYZ_w, XYZ_wr, method='Von Kries', **kwargs): ... XYZ, XYZ_w, XYZ_wr, method='Fairchild 1990', Y_n=Y_n) ... # doctest: +ELLIPSIS array([ 0.2332526..., 0.2332455..., 0.7611593...]) + + *Zhai and Luo (2018)* chromatic adaptation: + + >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + >>> XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775]) + >>> XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460]) + >>> chromatic_adaptation(XYZ, XYZ_w, XYZ_wr, method='Zhai 2018') + ... # doctest: +ELLIPSIS + array([ 0.2163881..., 0.1257 , 0.0384749...]) + >>> chromatic_adaptation( + ... XYZ, XYZ_w, XYZ_wr, method='Zhai 2018', D_b=0.9, + ... XYZ_wo=np.array([100, 100, 100])) + ... # doctest: +ELLIPSIS + array([ 0.2152436..., 0.1253522..., 0.0388406...]) """ + method = validate_method(method, CHROMATIC_ADAPTATION_METHODS) + function = CHROMATIC_ADAPTATION_METHODS[method] - domain_range_reference = get_domain_range_scale() == 'reference' - domain_100 = (chromatic_adaptation_CIE1994, - chromatic_adaptation_CMCCAT2000, - chromatic_adaptation_Fairchild1990) + domain_range_reference = get_domain_range_scale() == "reference" + domain_100 = ( + chromatic_adaptation_CIE1994, + chromatic_adaptation_CMCCAT2000, + chromatic_adaptation_Fairchild1990, + chromatic_adaptation_Zhai2018, + ) if function in domain_100 and domain_range_reference: XYZ = as_float_array(XYZ) * 100 XYZ_w = as_float_array(XYZ_w) * 100 XYZ_wr = as_float_array(XYZ_wr) * 100 - if kwargs.get('Y_o'): - kwargs['Y_o'] = kwargs['Y_o'] * 100 - kwargs.update({'XYZ_w': XYZ_w, 'XYZ_wr': XYZ_wr}) + if "Y_o" in kwargs: + kwargs["Y_o"] = kwargs["Y_o"] * 100 + + if "XYZ_wo" in kwargs: + kwargs["XYZ_wo"] = kwargs["XYZ_wo"] * 100 + + kwargs.update({"XYZ_w": XYZ_w, "XYZ_wr": XYZ_wr}) if function is chromatic_adaptation_CIE1994: from colour import XYZ_to_xy - kwargs.update({'xy_o1': XYZ_to_xy(XYZ_w), 'xy_o2': XYZ_to_xy(XYZ_wr)}) - + kwargs.update({"xy_o1": XYZ_to_xy(XYZ_w), "xy_o2": XYZ_to_xy(XYZ_wr)}) elif function is chromatic_adaptation_Fairchild1990: - kwargs.update({'XYZ_n': XYZ_w, 'XYZ_r': XYZ_wr}) + kwargs.update({"XYZ_n": XYZ_w, "XYZ_r": XYZ_wr}) + elif function is chromatic_adaptation_Zhai2018: + kwargs.update({"XYZ_wb": XYZ_w, "XYZ_wd": XYZ_wr}) XYZ_c = function(XYZ, **filter_kwargs(function, **kwargs)) @@ -244,92 +335,7 @@ def chromatic_adaptation(XYZ, XYZ_w, XYZ_wr, method='Von Kries', **kwargs): return XYZ_c -__all__ += ['CHROMATIC_ADAPTATION_METHODS', 'chromatic_adaptation'] - - -# ----------------------------------------------------------------------------# -# --- API Changes and Deprecation Management ---# -# ----------------------------------------------------------------------------# -class adaptation(ModuleAPI): - def __getattr__(self, attribute): - return super(adaptation, self).__getattr__(attribute) - - -# v0.3.14 -API_CHANGES = { - 'ObjectRenamed': [[ - 'colour.adaptation.chromatic_adaptation_reverse_CMCCAT2000', - 'colour.adaptation.chromatic_adaptation_inverse_CMCCAT2000', - ], ] -} -""" -Defines *colour.adaptation* sub-package API changes. - -API_CHANGES : dict -""" - -# v0.3.16 -API_CHANGES['ObjectRenamed'] = API_CHANGES['ObjectRenamed'] + [ - [ - 'colour.adaptation.BRADFORD_CAT', - 'colour.adaptation.CAT_BRADFORD', - ], - [ - 'colour.adaptation.BS_CAT', - 'colour.adaptation.CAT_BIANCO2010', - ], - [ - 'colour.adaptation.BS_PC_CAT', - 'colour.adaptation.CAT_PC_BIANCO2010)', - ], - [ - 'colour.adaptation.CAT02_BRILL_CAT', - 'colour.adaptation.CAT_CAT02_BRILL2008', - ], - [ - 'colour.adaptation.CAT02_CAT', - 'colour.adaptation.CAT_CAT02', - ], - [ - 'colour.adaptation.CMCCAT97_CAT', - 'colour.adaptation.CAT_CMCCAT97', - ], - [ - 'colour.adaptation.CMCCAT2000_CAT', - 'colour.adaptation.CAT_CMCCAT2000', - ], - [ - 'colour.adaptation.CMCCAT2000_InductionFactors', - 'colour.adaptation.InductionFactors_CMCCAT2000', - ], - [ - 'colour.adaptation.CMCCAT2000_VIEWING_CONDITIONS', - 'colour.adaptation.VIEWING_CONDITIONS_CMCCAT2000', - ], - [ - 'colour.adaptation.FAIRCHILD_CAT', - 'colour.adaptation.CAT_FAIRCHILD', - ], - [ - 'colour.adaptation.SHARP_CAT', - 'colour.adaptation.CAT_SHARP', - ], - [ - 'colour.adaptation.VON_KRIES_CAT', - 'colour.adaptation.CAT_VON_KRIES', - ], - [ - 'colour.adaptation.XYZ_SCALING_CAT', - 'colour.adaptation.CAT_XYZ_SCALING', - ], - [ - 'colour.adaptation.chromatic_adaptation_matrix_VonKries', - 'colour.adaptation.matrix_chromatic_adaptation_VonKries)', - ], +__all__ += [ + "CHROMATIC_ADAPTATION_METHODS", + "chromatic_adaptation", ] - -if not is_documentation_building(): - sys.modules['colour.adaptation'] = adaptation( - sys.modules['colour.adaptation'], build_API_changes(API_CHANGES)) - - del ModuleAPI, is_documentation_building, build_API_changes, sys diff --git a/colour/adaptation/cie1994.py b/colour/adaptation/cie1994.py index d94a908dcc..f122e8e1ea 100644 --- a/colour/adaptation/cie1994.py +++ b/colour/adaptation/cie1994.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ CIE 1994 Chromatic Adaptation Model =================================== -Defines *CIE 1994* chromatic adaptation model objects: +Defines the *CIE 1994* chromatic adaptation model objects: - :func:`colour.adaptation.chromatic_adaptation_CIE1994` @@ -15,80 +14,103 @@ ISBN:978-3-900734-51-0 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.algebra import spow +from colour.algebra import spow, vector_dot from colour.adaptation import CAT_VON_KRIES -from colour.utilities import (as_float_array, vector_dot, from_range_100, - to_domain_100, tsplit, tstack, usage_warning) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import ( + ArrayLike, + FloatingOrArrayLike, + FloatingOrNDArray, + NDArray, +) +from colour.utilities import ( + as_float_array, + from_range_100, + to_domain_100, + tsplit, + tstack, + usage_warning, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'MATRIX_XYZ_TO_RGB_CIE1994', 'MATRIX_RGB_TO_XYZ_CIE1994', - 'chromatic_adaptation_CIE1994', 'XYZ_to_RGB_CIE1994', 'RGB_to_XYZ_CIE1994', - 'intermediate_values', 'effective_adapting_responses', 'beta_1', 'beta_2', - 'exponential_factors', 'K_coefficient', 'corresponding_colour' + "MATRIX_XYZ_TO_RGB_CIE1994", + "MATRIX_RGB_TO_XYZ_CIE1994", + "chromatic_adaptation_CIE1994", + "XYZ_to_RGB_CIE1994", + "RGB_to_XYZ_CIE1994", + "intermediate_values", + "effective_adapting_responses", + "beta_1", + "beta_2", + "exponential_factors", + "K_coefficient", + "corresponding_colour", ] -MATRIX_XYZ_TO_RGB_CIE1994 = CAT_VON_KRIES +MATRIX_XYZ_TO_RGB_CIE1994: NDArray = CAT_VON_KRIES """ *CIE 1994* colour appearance model *CIE XYZ* tristimulus values to cone responses matrix. - -MATRIX_XYZ_TO_RGB_CIE1994 : array_like, (3, 3) """ -MATRIX_RGB_TO_XYZ_CIE1994 = np.linalg.inv(MATRIX_XYZ_TO_RGB_CIE1994) +MATRIX_RGB_TO_XYZ_CIE1994: NDArray = np.linalg.inv(MATRIX_XYZ_TO_RGB_CIE1994) """ *CIE 1994* colour appearance model cone responses to *CIE XYZ* tristimulus values matrix. - -MATRIX_RGB_TO_XYZ_CIE1994 : array_like, (3, 3) """ -def chromatic_adaptation_CIE1994(XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2, n=1): +def chromatic_adaptation_CIE1994( + XYZ_1: ArrayLike, + xy_o1: ArrayLike, + xy_o2: ArrayLike, + Y_o: FloatingOrArrayLike, + E_o1: FloatingOrArrayLike, + E_o2: FloatingOrArrayLike, + n: FloatingOrArrayLike = 1, +) -> NDArray: """ - Adapts given stimulus *CIE XYZ_1* tristimulus values from test viewing + Adapt given stimulus *CIE XYZ_1* tristimulus values from test viewing conditions to reference viewing conditions using *CIE 1994* chromatic adaptation model. Parameters ---------- - XYZ_1 : array_like + XYZ_1 *CIE XYZ* tristimulus values of test sample / stimulus. - xy_o1 : array_like + xy_o1 Chromaticity coordinates :math:`x_{o1}` and :math:`y_{o1}` of test illuminant and background. - xy_o2 : array_like + xy_o2 Chromaticity coordinates :math:`x_{o2}` and :math:`y_{o2}` of reference illuminant and background. - Y_o : numeric + Y_o Luminance factor :math:`Y_o` of achromatic background as percentage normalised to domain [18, 100] in **'Reference'** domain-range scale. - E_o1 : numeric + E_o1 Test illuminance :math:`E_{o1}` in :math:`cd/m^2`. - E_o2 : numeric + E_o2 Reference illuminance :math:`E_{o2}` in :math:`cd/m^2`. - n : numeric, optional + n Noise component in fundamental primary system. Returns ------- - ndarray + :class:`numpy.ndarray` Adapted *CIE XYZ_2* tristimulus values of test stimulus. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -121,13 +143,15 @@ def chromatic_adaptation_CIE1994(XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2, n=1): """ XYZ_1 = to_domain_100(XYZ_1) - Y_o = to_domain_100(Y_o) + Y_o = as_float_array(to_domain_100(Y_o)) E_o1 = as_float_array(E_o1) E_o2 = as_float_array(E_o2) if np.any(Y_o < 18) or np.any(Y_o > 100): - usage_warning(('"Y_o" luminance factor must be in [18, 100] domain, ' - 'unpredictable results may occur!')) + usage_warning( + '"Y_o" luminance factor must be in [18, 100] domain, ' + "unpredictable results may occur!" + ) RGB_1 = XYZ_to_RGB_CIE1994(XYZ_1) @@ -142,25 +166,26 @@ def chromatic_adaptation_CIE1994(XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2, n=1): K = K_coefficient(xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o, n) - RGB_2 = corresponding_colour(RGB_1, xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o, K, - n) + RGB_2 = corresponding_colour( + RGB_1, xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o, K, n + ) XYZ_2 = RGB_to_XYZ_CIE1994(RGB_2) return from_range_100(XYZ_2) -def XYZ_to_RGB_CIE1994(XYZ): +def XYZ_to_RGB_CIE1994(XYZ: ArrayLike) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to cone responses. + Convert from *CIE XYZ* tristimulus values to cone responses. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. Returns ------- - ndarray + :class:`numpy.ndarray` Cone responses. Examples @@ -173,18 +198,18 @@ def XYZ_to_RGB_CIE1994(XYZ): return vector_dot(MATRIX_XYZ_TO_RGB_CIE1994, XYZ) -def RGB_to_XYZ_CIE1994(RGB): +def RGB_to_XYZ_CIE1994(RGB: ArrayLike) -> NDArray: """ - Converts from cone responses to *CIE XYZ* tristimulus values. + Convert from cone responses to *CIE XYZ* tristimulus values. Parameters ---------- - RGB : array_like + RGB Cone responses. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Examples @@ -197,19 +222,19 @@ def RGB_to_XYZ_CIE1994(RGB): return vector_dot(MATRIX_RGB_TO_XYZ_CIE1994, RGB) -def intermediate_values(xy_o): +def intermediate_values(xy_o: ArrayLike) -> NDArray: """ - Returns the intermediate values :math:`\\xi`, :math:`\\eta`, + Return the intermediate values :math:`\\xi`, :math:`\\eta`, :math:`\\zeta`. Parameters ---------- - xy_o : array_like + xy_o Chromaticity coordinates :math:`x_o` and :math:`y_o` of whitepoint. Returns ------- - ndarray + :class:`numpy.ndarray` Intermediate values :math:`\\xi`, :math:`\\eta`, :math:`\\zeta`. Examples @@ -231,24 +256,26 @@ def intermediate_values(xy_o): return xez -def effective_adapting_responses(xez, Y_o, E_o): +def effective_adapting_responses( + xez: ArrayLike, Y_o: FloatingOrArrayLike, E_o: FloatingOrArrayLike +) -> NDArray: """ - Derives the effective adapting responses in the fundamental primary system + Derive the effective adapting responses in the fundamental primary system of the test or reference field. Parameters ---------- - xez: ndarray + xez Intermediate values :math:`\\xi`, :math:`\\eta`, :math:`\\zeta`. - E_o : numeric - Test or reference illuminance :math:`E_{o}` in lux. - Y_o : numeric + Y_o Luminance factor :math:`Y_o` of achromatic background as percentage normalised to domain [18, 100] in **'Reference'** domain-range scale. + E_o + Test or reference illuminance :math:`E_{o}` in lux. Returns ------- - ndarray + :class:`numpy.ndarray` Effective adapting responses. Examples @@ -264,25 +291,26 @@ def effective_adapting_responses(xez, Y_o, E_o): Y_o = as_float_array(Y_o) E_o = as_float_array(E_o) - RGB_o = (( - (Y_o[..., np.newaxis] * E_o[..., np.newaxis]) / (100 * np.pi)) * xez) + RGB_o = ( + (Y_o[..., np.newaxis] * E_o[..., np.newaxis]) / (100 * np.pi) + ) * xez return RGB_o -def beta_1(x): +def beta_1(x: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Computes the exponent :math:`\\beta_1` for the middle and long-wavelength + Compute the exponent :math:`\\beta_1` for the middle and long-wavelength sensitive cones. Parameters ---------- - x: numeric or array_like + x Middle and long-wavelength sensitive cone response. Returns ------- - numeric or array_like + :class:`numpy.floating` or :class:`numpy.ndarray` Exponent :math:`\\beta_1`. Examples @@ -294,19 +322,19 @@ def beta_1(x): return (6.469 + 6.362 * spow(x, 0.4495)) / (6.469 + spow(x, 0.4495)) -def beta_2(x): +def beta_2(x: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Computes the exponent :math:`\\beta_2` for the short-wavelength sensitive + Compute the exponent :math:`\\beta_2` for the short-wavelength sensitive cones. Parameters ---------- - x: numeric or array_like + x Short-wavelength sensitive cone response. Returns ------- - numeric or array_like + :class:`numpy.floating` or :class:`numpy.ndarray` Exponent :math:`\\beta_2`. Examples @@ -315,23 +343,24 @@ def beta_2(x): 4.6522416... """ - return 0.7844 * (8.414 + 8.091 * spow(x, 0.5128)) / ( - 8.414 + spow(x, 0.5128)) + return ( + 0.7844 * (8.414 + 8.091 * spow(x, 0.5128)) / (8.414 + spow(x, 0.5128)) + ) -def exponential_factors(RGB_o): +def exponential_factors(RGB_o: ArrayLike) -> NDArray: """ - Returns the chromatic adaptation exponential factors :math:`\\beta_1(R_o)`, + Return the chromatic adaptation exponential factors :math:`\\beta_1(R_o)`, :math:`\\beta_1(G_o)` and :math:`\\beta_2(B_o)` of given cone responses. Parameters ---------- - RGB_o: array_like + RGB_o Cone responses. Returns ------- - ndarray + :class:`numpy.ndarray` Chromatic adaptation exponential factors :math:`\\beta_1(R_o)`, :math:`\\beta_1(G_o)` and :math:`\\beta_2(B_o)`. @@ -353,35 +382,42 @@ def exponential_factors(RGB_o): return bRGB_o -def K_coefficient(xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o, n=1): +def K_coefficient( + xez_1: ArrayLike, + xez_2: ArrayLike, + bRGB_o1: ArrayLike, + bRGB_o2: ArrayLike, + Y_o: FloatingOrArrayLike, + n: FloatingOrArrayLike = 1, +) -> FloatingOrNDArray: """ - Computes the coefficient :math:`K` for correcting the difference between + Compute the coefficient :math:`K` for correcting the difference between the test and references illuminances. Parameters ---------- - xez_1: array_like + xez_1 Intermediate values :math:`\\xi_1`, :math:`\\eta_1`, :math:`\\zeta_1` for the test illuminant and background. - xez_2: array_like + xez_2 Intermediate values :math:`\\xi_2`, :math:`\\eta_2`, :math:`\\zeta_2` for the reference illuminant and background. - bRGB_o1: array_like + bRGB_o1 Chromatic adaptation exponential factors :math:`\\beta_1(R_{o1})`, :math:`\\beta_1(G_{o1})` and :math:`\\beta_2(B_{o1})` of test sample. - bRGB_o2: array_like + bRGB_o2 Chromatic adaptation exponential factors :math:`\\beta_1(R_{o2})`, :math:`\\beta_1(G_{o2})` and :math:`\\beta_2(B_{o2})` of reference sample. - Y_o : numeric or array_like + Y_o Luminance factor :math:`Y_o` of achromatic background as percentage normalised to domain [18, 100] in **'Reference'** domain-range scale. - n : numeric or array_like, optional + n Noise component in fundamental primary system. Returns ------- - numeric or array_like + :class:`numpy.floating` or :class:`numpy.ndarray` Coefficient :math:`K`. Examples @@ -400,49 +436,61 @@ def K_coefficient(xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o, n=1): bR_o1, bG_o1, _bB_o1 = tsplit(bRGB_o1) bR_o2, bG_o2, _bB_o2 = tsplit(bRGB_o2) Y_o = as_float_array(Y_o) + n = as_float_array(n) - K = (spow((Y_o * xi_1 + n) / (20 * xi_1 + n), (2 / 3) * bR_o1) / spow( - (Y_o * xi_2 + n) / (20 * xi_2 + n), (2 / 3) * bR_o2)) + K = spow((Y_o * xi_1 + n) / (20 * xi_1 + n), (2 / 3) * bR_o1) / spow( + (Y_o * xi_2 + n) / (20 * xi_2 + n), (2 / 3) * bR_o2 + ) - K *= (spow((Y_o * eta_1 + n) / (20 * eta_1 + n), (1 / 3) * bG_o1) / spow( - (Y_o * eta_2 + n) / (20 * eta_2 + n), (1 / 3) * bG_o2)) + K *= spow((Y_o * eta_1 + n) / (20 * eta_1 + n), (1 / 3) * bG_o1) / spow( + (Y_o * eta_2 + n) / (20 * eta_2 + n), (1 / 3) * bG_o2 + ) return K -def corresponding_colour(RGB_1, xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o, K, n=1): +def corresponding_colour( + RGB_1: ArrayLike, + xez_1: ArrayLike, + xez_2: ArrayLike, + bRGB_o1: ArrayLike, + bRGB_o2: ArrayLike, + Y_o: FloatingOrArrayLike, + K: FloatingOrArrayLike, + n: FloatingOrArrayLike = 1, +) -> NDArray: """ - Computes the corresponding colour cone responses of given test sample cone + Compute the corresponding colour cone responses of given test sample cone responses :math:`RGB_1`. Parameters ---------- - RGB_1: array_like + RGB_1 Test sample cone responses :math:`RGB_1`. - xez_1: array_like + xez_1 Intermediate values :math:`\\xi_1`, :math:`\\eta_1`, :math:`\\zeta_1` for the test illuminant and background. - xez_2: array_like + xez_2 Intermediate values :math:`\\xi_2`, :math:`\\eta_2`, :math:`\\zeta_2` for the reference illuminant and background. - bRGB_o1: array_like + bRGB_o1 Chromatic adaptation exponential factors :math:`\\beta_1(R_{o1})`, :math:`\\beta_1(G_{o1})` and :math:`\\beta_2(B_{o1})` of test sample. - bRGB_o2: array_like + bRGB_o2 Chromatic adaptation exponential factors :math:`\\beta_1(R_{o2})`, :math:`\\beta_1(G_{o2})` and :math:`\\beta_2(B_{o2})` of reference sample. - Y_o : numeric or array_like + Y_o Luminance factor :math:`Y_o` of achromatic background as percentage normalised to domain [18, 100] in **'Reference'** domain-range scale. - K : numeric or array_like + K Coefficient :math:`K`. - n : numeric or array_like, optional + n Noise component in fundamental primary system. Returns ------- - ndarray + :class:`numpy.ndarray` Corresponding colour cone responses of given test sample cone responses. @@ -454,7 +502,7 @@ def corresponding_colour(RGB_1, xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o, K, n=1): >>> bRGB_o1 = np.array([3.74852518, 3.63920879, 2.78924811]) >>> bRGB_o2 = np.array([3.68102374, 3.68102256, 3.56557351]) >>> Y_o = 20 - >>> K = 1.0 + >>> K = 1 >>> corresponding_colour(RGB_1, xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o, K) ... # doctest: +ELLIPSIS array([ 23.1636901..., 20.0211948..., 16.2001664...]) @@ -467,18 +515,25 @@ def corresponding_colour(RGB_1, xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o, K, n=1): bR_o2, bG_o2, bB_o2 = tsplit(bRGB_o2) Y_o = as_float_array(Y_o) K = as_float_array(K) - - def RGB_c(x_1, x_2, y_1, y_2, z): - """ - Computes the corresponding colour cone responses component. - """ - - return ((Y_o * x_2 + n) * spow(K, 1 / y_2) * spow( - (z + n) / (Y_o * x_1 + n), y_1 / y_2) - n) - - R_2 = RGB_c(xi_1, xi_2, bR_o1, bR_o2, R_1) - G_2 = RGB_c(eta_1, eta_2, bG_o1, bG_o2, G_1) - B_2 = RGB_c(zeta_1, zeta_2, bB_o1, bB_o2, B_1) + n = as_float_array(n) + + def RGB_c( + x_1: NDArray, + x_2: NDArray, + y_1: NDArray, + y_2: NDArray, + z: NDArray, + n: NDArray, + ) -> NDArray: + """Compute the corresponding colour cone responses component.""" + + return (Y_o * x_2 + n) * spow(K, 1 / y_2) * spow( + (z + n) / (Y_o * x_1 + n), y_1 / y_2 + ) - n + + R_2 = RGB_c(xi_1, xi_2, bR_o1, bR_o2, R_1, n) + G_2 = RGB_c(eta_1, eta_2, bG_o1, bG_o2, G_1, n) + B_2 = RGB_c(zeta_1, zeta_2, bB_o1, bB_o2, B_1, n) RGB_2 = tstack([R_2, G_2, B_2]) diff --git a/colour/adaptation/cmccat2000.py b/colour/adaptation/cmccat2000.py index 0ec1a09f87..0e5303459f 100644 --- a/colour/adaptation/cmccat2000.py +++ b/colour/adaptation/cmccat2000.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ CMCCAT2000 Chromatic Adaptation Model ===================================== -Defines *CMCCAT2000* chromatic adaptation model objects: +Defines the *CMCCAT2000* chromatic adaptation model objects: - :class:`colour.adaptation.InductionFactors_CMCCAT2000` - :class:`colour.VIEWING_CONDITIONS_CMCCAT2000` @@ -21,45 +20,60 @@ 83-86). ISBN:978-0-470-66569-5 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from collections import namedtuple +from typing import NamedTuple from colour.adaptation import CAT_CMCCAT2000 -from colour.utilities import (CaseInsensitiveMapping, as_float_array, - vector_dot, from_range_100, to_domain_100) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.algebra import vector_dot +from colour.hints import ( + ArrayLike, + Floating, + FloatingOrArrayLike, + Literal, + NDArray, + Union, +) +from colour.utilities import ( + CaseInsensitiveMapping, + as_float_array, + from_range_100, + to_domain_100, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'CAT_INVERSE_CMCCAT2000', 'InductionFactors_CMCCAT2000', - 'VIEWING_CONDITIONS_CMCCAT2000', 'chromatic_adaptation_forward_CMCCAT2000', - 'chromatic_adaptation_inverse_CMCCAT2000', - 'chromatic_adaptation_CMCCAT2000' + "CAT_INVERSE_CMCCAT2000", + "InductionFactors_CMCCAT2000", + "VIEWING_CONDITIONS_CMCCAT2000", + "chromatic_adaptation_forward_CMCCAT2000", + "chromatic_adaptation_inverse_CMCCAT2000", + "chromatic_adaptation_CMCCAT2000", ] -CAT_INVERSE_CMCCAT2000 = np.linalg.inv(CAT_CMCCAT2000) +CAT_INVERSE_CMCCAT2000: NDArray = np.linalg.inv(CAT_CMCCAT2000) """ Inverse *CMCCAT2000* chromatic adaptation transform. -CAT_INVERSE_CMCCAT2000 : array_like, (3, 3) +CAT_INVERSE_CMCCAT2000 """ -class InductionFactors_CMCCAT2000( - namedtuple('InductionFactors_CMCCAT2000', ('F', ))): +class InductionFactors_CMCCAT2000(NamedTuple): """ *CMCCAT2000* chromatic adaptation model induction factors. Parameters ---------- - F : numeric or array_like + F :math:`F` surround condition. References @@ -67,60 +81,63 @@ class InductionFactors_CMCCAT2000( :cite:`Li2002a`, :cite:`Westland2012k` """ + F: Floating + -VIEWING_CONDITIONS_CMCCAT2000 = CaseInsensitiveMapping({ - 'Average': InductionFactors_CMCCAT2000(1), - 'Dim': InductionFactors_CMCCAT2000(0.8), - 'Dark': InductionFactors_CMCCAT2000(0.8) -}) +VIEWING_CONDITIONS_CMCCAT2000: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Average": InductionFactors_CMCCAT2000(1), + "Dim": InductionFactors_CMCCAT2000(0.8), + "Dark": InductionFactors_CMCCAT2000(0.8), + } +) VIEWING_CONDITIONS_CMCCAT2000.__doc__ = """ Reference *CMCCAT2000* chromatic adaptation model viewing conditions. References ---------- :cite:`Li2002a`, :cite:`Westland2012k` - -VIEWING_CONDITIONS_CMCCAT2000 : CaseInsensitiveMapping - ('Average', 'Dim', 'Dark') """ def chromatic_adaptation_forward_CMCCAT2000( - XYZ, - XYZ_w, - XYZ_wr, - L_A1, - L_A2, - surround=VIEWING_CONDITIONS_CMCCAT2000['Average']): + XYZ: ArrayLike, + XYZ_w: ArrayLike, + XYZ_wr: ArrayLike, + L_A1: FloatingOrArrayLike, + L_A2: FloatingOrArrayLike, + surround: InductionFactors_CMCCAT2000 = VIEWING_CONDITIONS_CMCCAT2000[ + "Average" + ], +) -> NDArray: """ - Adapts given stimulus *CIE XYZ* tristimulus values from test viewing + Adapt given stimulus *CIE XYZ* tristimulus values from test viewing conditions to reference viewing conditions using *CMCCAT2000* forward chromatic adaptation model. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values of the stimulus to adapt. - XYZ_w : array_like + XYZ_w Test viewing condition *CIE XYZ* tristimulus values of the whitepoint. - XYZ_wr : array_like + XYZ_wr Reference viewing condition *CIE XYZ* tristimulus values of the whitepoint. - L_A1 : numeric or array_like + L_A1 Luminance of test adapting field :math:`L_{A1}` in :math:`cd/m^2`. - L_A2 : numeric or array_like + L_A2 Luminance of reference adapting field :math:`L_{A2}` in :math:`cd/m^2`. - surround : InductionFactors_CMCCAT2000, optional + surround Surround viewing conditions induction factors. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ_c* tristimulus values of the stimulus corresponding colour. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -163,55 +180,61 @@ def chromatic_adaptation_forward_CMCCAT2000( RGB_w = vector_dot(CAT_CMCCAT2000, XYZ_w) RGB_wr = vector_dot(CAT_CMCCAT2000, XYZ_wr) - D = (surround.F * (0.08 * np.log10(0.5 * (L_A1 + L_A2)) + 0.76 - 0.45 * - (L_A1 - L_A2) / (L_A1 + L_A2))) + D = surround.F * ( + 0.08 * np.log10(0.5 * (L_A1 + L_A2)) + + 0.76 + - 0.45 * (L_A1 - L_A2) / (L_A1 + L_A2) + ) D = np.clip(D, 0, 1) a = D * XYZ_w[..., 1] / XYZ_wr[..., 1] - RGB_c = ( - RGB * (a[..., np.newaxis] * (RGB_wr / RGB_w) + 1 - D[..., np.newaxis])) + RGB_c = RGB * ( + a[..., np.newaxis] * (RGB_wr / RGB_w) + 1 - D[..., np.newaxis] + ) XYZ_c = vector_dot(CAT_INVERSE_CMCCAT2000, RGB_c) return from_range_100(XYZ_c) def chromatic_adaptation_inverse_CMCCAT2000( - XYZ_c, - XYZ_w, - XYZ_wr, - L_A1, - L_A2, - surround=VIEWING_CONDITIONS_CMCCAT2000['Average']): + XYZ_c: ArrayLike, + XYZ_w: ArrayLike, + XYZ_wr: ArrayLike, + L_A1: FloatingOrArrayLike, + L_A2: FloatingOrArrayLike, + surround: InductionFactors_CMCCAT2000 = VIEWING_CONDITIONS_CMCCAT2000[ + "Average" + ], +) -> NDArray: """ - Adapts given stimulus corresponding colour *CIE XYZ* tristimulus values + Adapt given stimulus corresponding colour *CIE XYZ* tristimulus values from reference viewing conditions to test viewing conditions using *CMCCAT2000* inverse chromatic adaptation model. Parameters ---------- - XYZ_c : array_like + XYZ_c *CIE XYZ* tristimulus values of the stimulus to adapt. - XYZ_w : array_like + XYZ_w Test viewing condition *CIE XYZ* tristimulus values of the whitepoint. - XYZ_wr : array_like + XYZ_wr Reference viewing condition *CIE XYZ* tristimulus values of the whitepoint. - L_A1 : numeric or array_like + L_A1 Luminance of test adapting field :math:`L_{A1}` in :math:`cd/m^2`. - L_A2 : numeric or array_like + L_A2 Luminance of reference adapting field :math:`L_{A2}` in :math:`cd/m^2`. - surround : InductionFactors_CMCCAT2000, optional + surround Surround viewing conditions induction factors. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ_c* tristimulus values of the adapted stimulus. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -255,29 +278,36 @@ def chromatic_adaptation_inverse_CMCCAT2000( RGB_w = vector_dot(CAT_CMCCAT2000, XYZ_w) RGB_wr = vector_dot(CAT_CMCCAT2000, XYZ_wr) - D = (surround.F * (0.08 * np.log10(0.5 * (L_A1 + L_A2)) + 0.76 - 0.45 * - (L_A1 - L_A2) / (L_A1 + L_A2))) + D = surround.F * ( + 0.08 * np.log10(0.5 * (L_A1 + L_A2)) + + 0.76 + - 0.45 * (L_A1 - L_A2) / (L_A1 + L_A2) + ) D = np.clip(D, 0, 1) a = D * XYZ_w[..., 1] / XYZ_wr[..., 1] - RGB = (RGB_c / (a[..., np.newaxis] * - (RGB_wr / RGB_w) + 1 - D[..., np.newaxis])) + RGB = RGB_c / ( + a[..., np.newaxis] * (RGB_wr / RGB_w) + 1 - D[..., np.newaxis] + ) XYZ = vector_dot(CAT_INVERSE_CMCCAT2000, RGB) return from_range_100(XYZ) def chromatic_adaptation_CMCCAT2000( - XYZ, - XYZ_w, - XYZ_wr, - L_A1, - L_A2, - surround=VIEWING_CONDITIONS_CMCCAT2000['Average'], - direction='Forward'): + XYZ: ArrayLike, + XYZ_w: ArrayLike, + XYZ_wr: ArrayLike, + L_A1: FloatingOrArrayLike, + L_A2: FloatingOrArrayLike, + surround: InductionFactors_CMCCAT2000 = VIEWING_CONDITIONS_CMCCAT2000[ + "Average" + ], + direction: Union[Literal["Forward", "Inverse"], str] = "Forward", +) -> NDArray: """ - Adapts given stimulus *CIE XYZ* tristimulus values using given viewing + Adapt given stimulus *CIE XYZ* tristimulus values using given viewing conditions. This definition is a convenient wrapper around @@ -286,32 +316,30 @@ def chromatic_adaptation_CMCCAT2000( Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values of the stimulus to adapt. - XYZ_w : array_like + XYZ_w Source viewing condition *CIE XYZ* tristimulus values of the whitepoint. - XYZ_wr : array_like + XYZ_wr Target viewing condition *CIE XYZ* tristimulus values of the whitepoint. - L_A1 : numeric or array_like + L_A1 Luminance of test adapting field :math:`L_{A1}` in :math:`cd/m^2`. - L_A2 : numeric or array_like + L_A2 Luminance of reference adapting field :math:`L_{A2}` in :math:`cd/m^2`. - surround : InductionFactors_CMCCAT2000, optional + surround Surround viewing conditions induction factors. - direction : unicode, optional - **{'Forward', 'Inverse'}**, + direction Chromatic adaptation direction. Returns ------- - ndarray + :class:`numpy.ndarray` Adapted stimulus *CIE XYZ* tristimulus values. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -357,9 +385,17 @@ def chromatic_adaptation_CMCCAT2000( array([ 22.48, 22.74, 8.54]) """ - if direction.lower() == 'forward': - return chromatic_adaptation_forward_CMCCAT2000(XYZ, XYZ_w, XYZ_wr, - L_A1, L_A2, surround) + direction = validate_method( + direction, + ["Forward", "Inverse"], + '"{0}" direction is invalid, it must be one of {1}!', + ) + + if direction == "forward": + return chromatic_adaptation_forward_CMCCAT2000( + XYZ, XYZ_w, XYZ_wr, L_A1, L_A2, surround + ) else: - return chromatic_adaptation_inverse_CMCCAT2000(XYZ, XYZ_w, XYZ_wr, - L_A1, L_A2, surround) + return chromatic_adaptation_inverse_CMCCAT2000( + XYZ, XYZ_w, XYZ_wr, L_A1, L_A2, surround + ) diff --git a/colour/adaptation/datasets/__init__.py b/colour/adaptation/datasets/__init__.py index 261c28f7d7..762d95ce45 100644 --- a/colour/adaptation/datasets/__init__.py +++ b/colour/adaptation/datasets/__init__.py @@ -1,15 +1,33 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .cat import CHROMATIC_ADAPTATION_TRANSFORMS -from .cat import (CAT_XYZ_SCALING, CAT_VON_KRIES, CAT_BRADFORD, CAT_SHARP, - CAT_FAIRCHILD, CAT_CMCCAT97, CAT_CMCCAT2000, CAT_CAT02, - CAT_CAT02_BRILL2008, CAT_BIANCO2010, CAT_PC_BIANCO2010) +from .cat import ( + CAT_XYZ_SCALING, + CAT_VON_KRIES, + CAT_BRADFORD, + CAT_SHARP, + CAT_FAIRCHILD, + CAT_CMCCAT97, + CAT_CMCCAT2000, + CAT_CAT02, + CAT_CAT02_BRILL2008, + CAT_CAT16, + CAT_BIANCO2010, + CAT_PC_BIANCO2010, +) -__all__ = ['CHROMATIC_ADAPTATION_TRANSFORMS'] +__all__ = [ + "CHROMATIC_ADAPTATION_TRANSFORMS", +] __all__ += [ - 'CAT_XYZ_SCALING', 'CAT_VON_KRIES', 'CAT_BRADFORD', 'CAT_SHARP', - 'CAT_FAIRCHILD', 'CAT_CMCCAT97', 'CAT_CMCCAT2000', 'CAT_CAT02', - 'CAT_CAT02_BRILL2008', 'CAT_BIANCO2010', 'CAT_PC_BIANCO2010' + "CAT_XYZ_SCALING", + "CAT_VON_KRIES", + "CAT_BRADFORD", + "CAT_SHARP", + "CAT_FAIRCHILD", + "CAT_CMCCAT97", + "CAT_CMCCAT2000", + "CAT_CAT02", + "CAT_CAT02_BRILL2008", + "CAT_CAT16", + "CAT_BIANCO2010", + "CAT_PC_BIANCO2010", ] diff --git a/colour/adaptation/datasets/cat.py b/colour/adaptation/datasets/cat.py index ac6f052ea6..159107b0de 100644 --- a/colour/adaptation/datasets/cat.py +++ b/colour/adaptation/datasets/cat.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Chromatic Adaptation Transforms =============================== @@ -23,6 +22,8 @@ transform. - :attr:`colour.adaptation.CAT_CAT02_BRILL2008`: *Brill and Susstrunk (2008)* corrected CAT02 chromatic adaptation transform. +- :attr:`colour.adaptation.CAT_CAT16`: *CAT16* chromatic adaptation + transform. - :attr:`colour.adaptation.CAT_BIANCO2010`: *Bianco and Schettini (2010)* chromatic adaptation transform. - :attr:`colour.adaptation.CAT_PC_BIANCO2010`: @@ -49,6 +50,10 @@ (2007). The Problem with CAT02 and Its Correction. https://pdfs.semanticscholar.org/b5a9/\ 0215ad9a1fb6b01f310b3d64305f7c9feb3a.pdf +- :cite:`Li2017` : Li, C., Li, Z., Wang, Z., Xu, Y., Luo, M. R., Cui, G., + Melgosa, M., Brill, M. H., & Pointer, M. (2017). Comprehensive color + solutions: CAM16, CAT16, and CAM16-UCS. Color Research & Application, + 42(6), 703-718. doi:10.1002/col.22131 - :cite:`Lindbloom2009g` : Fairchild, M. D. (2013). Chromatic Adaptation Models. In Color Appearance Models (3rd ed., pp. 4179-4252). Wiley. ISBN:B00DAYO8E2 @@ -66,42 +71,52 @@ 2014, from http://en.wikipedia.org/wiki/CIECAM02#CAT02 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +from colour.hints import NDArray from colour.utilities import CaseInsensitiveMapping -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'CAT_XYZ_SCALING', 'CAT_VON_KRIES', 'CAT_BRADFORD', 'CAT_SHARP', - 'CAT_FAIRCHILD', 'CAT_CMCCAT97', 'CAT_CMCCAT2000', 'CAT_CAT02', - 'CAT_CAT02_BRILL2008', 'CAT_BIANCO2010', 'CAT_PC_BIANCO2010', - 'CHROMATIC_ADAPTATION_TRANSFORMS' + "CAT_XYZ_SCALING", + "CAT_VON_KRIES", + "CAT_BRADFORD", + "CAT_SHARP", + "CAT_FAIRCHILD", + "CAT_CMCCAT97", + "CAT_CMCCAT2000", + "CAT_CAT02", + "CAT_CAT02_BRILL2008", + "CAT_CAT16", + "CAT_BIANCO2010", + "CAT_PC_BIANCO2010", + "CHROMATIC_ADAPTATION_TRANSFORMS", ] -CAT_XYZ_SCALING = np.array(np.identity(3)).reshape([3, 3]) +CAT_XYZ_SCALING: NDArray = np.array(np.identity(3)).reshape([3, 3]) """ *XYZ Scaling* chromatic adaptation transform. References ---------- :cite:`Lindbloom2009g` - -CAT_XYZ_SCALING : array_like, (3, 3) """ -CAT_VON_KRIES = np.array([ - [0.4002400, 0.7076000, -0.0808100], - [-0.2263000, 1.1653200, 0.0457000], - [0.0000000, 0.0000000, 0.9182200], -]) +CAT_VON_KRIES: NDArray = np.array( + [ + [0.4002400, 0.7076000, -0.0808100], + [-0.2263000, 1.1653200, 0.0457000], + [0.0000000, 0.0000000, 0.9182200], + ] +) """ *Von Kries* chromatic adaptation transform. @@ -109,105 +124,105 @@ ---------- :cite:`CIETC1-321994b`, :cite:`Fairchild2013ba`, :cite:`Lindbloom2009g`, :cite:`Nayatani1995a` - -CAT_VON_KRIES : array_like, (3, 3) """ -CAT_BRADFORD = np.array([ - [0.8951000, 0.2664000, -0.1614000], - [-0.7502000, 1.7135000, 0.0367000], - [0.0389000, -0.0685000, 1.0296000], -]) +CAT_BRADFORD: NDArray = np.array( + [ + [0.8951000, 0.2664000, -0.1614000], + [-0.7502000, 1.7135000, 0.0367000], + [0.0389000, -0.0685000, 1.0296000], + ] +) """ *Bradford* chromatic adaptation transform. References ---------- :cite:`Lindbloom2009g` - -CAT_BRADFORD : array_like, (3, 3) """ -CAT_SHARP = np.array([ - [1.2694, -0.0988, -0.1706], - [-0.8364, 1.8006, 0.0357], - [0.0297, -0.0315, 1.0018], -]) +CAT_SHARP: NDArray = np.array( + [ + [1.2694, -0.0988, -0.1706], + [-0.8364, 1.8006, 0.0357], + [0.0297, -0.0315, 1.0018], + ] +) """ *Sharp* chromatic adaptation transform. References ---------- :cite:`Bianco2010a` - -CAT_SHARP : array_like, (3, 3) """ -CAT_FAIRCHILD = np.array([ - [0.8562, 0.3372, -0.1934], - [-0.8360, 1.8327, 0.0033], - [0.0357, -0.0469, 1.0112], -]) +CAT_FAIRCHILD: NDArray = np.array( + [ + [0.8562, 0.3372, -0.1934], + [-0.8360, 1.8327, 0.0033], + [0.0357, -0.0469, 1.0112], + ] +) """ *Fairchild* chromatic adaptation transform. References ---------- :cite:`Fairchildb` - -CAT_FAIRCHILD : array_like, (3, 3) """ -CAT_CMCCAT97 = np.array([ - [0.8951, -0.7502, 0.0389], - [0.2664, 1.7135, 0.0685], - [-0.1614, 0.0367, 1.0296], -]) +CAT_CMCCAT97: NDArray = np.array( + [ + [0.8951, -0.7502, 0.0389], + [0.2664, 1.7135, 0.0685], + [-0.1614, 0.0367, 1.0296], + ] +) """ *CMCCAT97* chromatic adaptation transform. References ---------- :cite:`Westland2012g` - -CAT_CMCCAT97 : array_like, (3, 3) """ -CAT_CMCCAT2000 = np.array([ - [0.7982, 0.3389, -0.1371], - [-0.5918, 1.5512, 0.0406], - [0.0008, 0.0239, 0.9753], -]) +CAT_CMCCAT2000: NDArray = np.array( + [ + [0.7982, 0.3389, -0.1371], + [-0.5918, 1.5512, 0.0406], + [0.0008, 0.0239, 0.9753], + ] +) """ *CMCCAT2000* chromatic adaptation transform. References ---------- :cite:`Westland2012k` - -CAT_CMCCAT2000 : array_like, (3, 3) """ -CAT_CAT02 = np.array([ - [0.7328, 0.4296, -0.1624], - [-0.7036, 1.6975, 0.0061], - [0.0030, 0.0136, 0.9834], -]) +CAT_CAT02: NDArray = np.array( + [ + [0.7328, 0.4296, -0.1624], + [-0.7036, 1.6975, 0.0061], + [0.0030, 0.0136, 0.9834], + ] +) """ *CAT02* chromatic adaptation transform. References ---------- :cite:`Wikipedia2007` - -CAT_CAT02 : array_like, (3, 3) """ -CAT_CAT02_BRILL2008 = np.array([ - [0.7328, 0.4296, -0.1624], - [-0.7036, 1.6975, 0.0061], - [0.0000, 0.0000, 1.0000], -]) +CAT_CAT02_BRILL2008: NDArray = np.array( + [ + [0.7328, 0.4296, -0.1624], + [-0.7036, 1.6975, 0.0061], + [0.0000, 0.0000, 1.0000], + ] +) """ *Brill and Susstrunk (2008)* corrected CAT02 chromatic adaptation transform. @@ -215,30 +230,45 @@ References ---------- :cite:`Brill2008a`, :cite:`Li2007e` +""" + +CAT_CAT16: NDArray = np.array( + [ + [0.401288, 0.650173, -0.051461], + [-0.250268, 1.204414, 0.045854], + [-0.002079, 0.048952, 0.953127], + ] +) +""" +*CAT16* chromatic adaptation transform. -CAT_CAT02_BRILL2008 : array_like, (3, 3) +References +---------- +:cite:`Li2017` """ -CAT_BIANCO2010 = np.array([ - [0.8752, 0.2787, -0.1539], - [-0.8904, 1.8709, 0.0195], - [-0.0061, 0.0162, 0.9899], -]) +CAT_BIANCO2010: NDArray = np.array( + [ + [0.8752, 0.2787, -0.1539], + [-0.8904, 1.8709, 0.0195], + [-0.0061, 0.0162, 0.9899], + ] +) """ *Bianco and Schettini (2010)* chromatic adaptation transform. References ---------- :cite:`Bianco2010a` - -CAT_BIANCO2010 : array_like, (3, 3) """ -CAT_PC_BIANCO2010 = np.array([ - [0.6489, 0.3915, -0.0404], - [-0.3775, 1.3055, 0.0720], - [-0.0271, 0.0888, 0.9383], -]) +CAT_PC_BIANCO2010: NDArray = np.array( + [ + [0.6489, 0.3915, -0.0404], + [-0.3775, 1.3055, 0.0720], + [-0.0271, 0.0888, 0.9383], + ] +) """ *Bianco and Schettini PC (2010)* chromatic adaptation transform. @@ -246,37 +276,35 @@ ---------- :cite:`Bianco2010a` -CAT_PC_BIANCO2010 : array_like, (3, 3) - Notes ----- - This chromatic adaptation transform has no negative lobes. """ -CHROMATIC_ADAPTATION_TRANSFORMS = CaseInsensitiveMapping({ - 'XYZ Scaling': CAT_XYZ_SCALING, - 'Von Kries': CAT_VON_KRIES, - 'Bradford': CAT_BRADFORD, - 'Sharp': CAT_SHARP, - 'Fairchild': CAT_FAIRCHILD, - 'CMCCAT97': CAT_CMCCAT97, - 'CMCCAT2000': CAT_CMCCAT2000, - 'CAT02': CAT_CAT02, - 'CAT02 Brill 2008': CAT_CAT02_BRILL2008, - 'Bianco 2010': CAT_BIANCO2010, - 'Bianco PC 2010': CAT_PC_BIANCO2010 -}) +CHROMATIC_ADAPTATION_TRANSFORMS: CaseInsensitiveMapping = ( + CaseInsensitiveMapping( + { + "XYZ Scaling": CAT_XYZ_SCALING, + "Von Kries": CAT_VON_KRIES, + "Bradford": CAT_BRADFORD, + "Sharp": CAT_SHARP, + "Fairchild": CAT_FAIRCHILD, + "CMCCAT97": CAT_CMCCAT97, + "CMCCAT2000": CAT_CMCCAT2000, + "CAT02": CAT_CAT02, + "CAT02 Brill 2008": CAT_CAT02_BRILL2008, + "CAT16": CAT_CAT16, + "Bianco 2010": CAT_BIANCO2010, + "Bianco PC 2010": CAT_PC_BIANCO2010, + } + ) +) CHROMATIC_ADAPTATION_TRANSFORMS.__doc__ = """ Chromatic adaptation transforms. References ---------- :cite:`Bianco2010a`, :cite:`Brill2008a`, :cite:`Fairchildb`, :cite:`Li2007e`, -:cite:`Lindbloom2009g`, :cite:`Westland2012g`, :cite:`Westland2012k`, -:cite:`Wikipedia2007` - -CHROMATIC_ADAPTATION_TRANSFORMS : CaseInsensitiveMapping - **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', 'Fairchild, - 'CMCCAT97', 'CMCCAT2000', 'CAT02 Brill 2008', 'Bianco 2010', - 'Bianco PC 2010'}** +:cite:`Li2017`, :cite:`Lindbloom2009g`, :cite:`Westland2012g`, +:cite:`Westland2012k`, :cite:`Wikipedia2007` """ diff --git a/colour/adaptation/fairchild1990.py b/colour/adaptation/fairchild1990.py index de5080bf95..f851b71b70 100644 --- a/colour/adaptation/fairchild1990.py +++ b/colour/adaptation/fairchild1990.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Fairchild (1990) Chromatic Adaptation Model =========================================== -Defines *Fairchild (1990)* chromatic adaptation model objects: +Defines the *Fairchild (1990)* chromatic adaptation model objects: - :func:`colour.adaptation.chromatic_adaptation_Fairchild1990` @@ -16,77 +15,85 @@ In Color Appearance Models (3rd ed., pp. 4418-4495). Wiley. ISBN:B00DAYO8E2 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.algebra import spow +from colour.algebra import spow, vector_dot from colour.adaptation import CAT_VON_KRIES -from colour.utilities import (as_float_array, vector_dot, from_range_100, ones, - row_as_diagonal, to_domain_100, tsplit, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import ArrayLike, Boolean, FloatingOrArrayLike, NDArray +from colour.utilities import ( + as_float_array, + from_range_100, + ones, + row_as_diagonal, + to_domain_100, + tsplit, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'MATRIX_XYZ_TO_RGB_FAIRCHILD1990', 'MATRIX_RGB_TO_XYZ_FAIRCHILD1990', - 'chromatic_adaptation_Fairchild1990', 'XYZ_to_RGB_Fairchild1990', - 'RGB_to_XYZ_Fairchild1990', 'degrees_of_adaptation' + "MATRIX_XYZ_TO_RGB_FAIRCHILD1990", + "MATRIX_RGB_TO_XYZ_FAIRCHILD1990", + "chromatic_adaptation_Fairchild1990", + "XYZ_to_RGB_Fairchild1990", + "RGB_to_XYZ_Fairchild1990", + "degrees_of_adaptation", ] -MATRIX_XYZ_TO_RGB_FAIRCHILD1990 = CAT_VON_KRIES +MATRIX_XYZ_TO_RGB_FAIRCHILD1990: NDArray = CAT_VON_KRIES """ *Fairchild (1990)* colour appearance model *CIE XYZ* tristimulus values to cone responses matrix. - -MATRIX_XYZ_TO_RGB_FAIRCHILD1990 : array_like, (3, 3) """ -MATRIX_RGB_TO_XYZ_FAIRCHILD1990 = np.linalg.inv(CAT_VON_KRIES) +MATRIX_RGB_TO_XYZ_FAIRCHILD1990: NDArray = np.linalg.inv(CAT_VON_KRIES) """ *Fairchild (1990)* colour appearance model cone responses to *CIE XYZ* tristimulus values matrix. - -MATRIX_RGB_TO_XYZ_FAIRCHILD1990 : array_like, (3, 3) """ -def chromatic_adaptation_Fairchild1990(XYZ_1, - XYZ_n, - XYZ_r, - Y_n, - discount_illuminant=False): +def chromatic_adaptation_Fairchild1990( + XYZ_1: ArrayLike, + XYZ_n: ArrayLike, + XYZ_r: ArrayLike, + Y_n: FloatingOrArrayLike, + discount_illuminant: Boolean = False, +) -> NDArray: """ - Adapts given stimulus *CIE XYZ_1* tristimulus values from test viewing + Adapt given stimulus *CIE XYZ_1* tristimulus values from test viewing conditions to reference viewing conditions using *Fairchild (1990)* chromatic adaptation model. Parameters ---------- - XYZ_1 : array_like + XYZ_1 *CIE XYZ_1* tristimulus values of test sample / stimulus. - XYZ_n : array_like + XYZ_n Test viewing condition *CIE XYZ_n* tristimulus values of whitepoint. - XYZ_r : array_like + XYZ_r Reference viewing condition *CIE XYZ_r* tristimulus values of whitepoint. - Y_n : numeric or array_like + Y_n Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`. - discount_illuminant : bool, optional + discount_illuminant Truth value indicating if the illuminant should be discounted. Returns ------- - ndarray + :class:`numpy.ndarray` Adapted *CIE XYZ_2* tristimulus values of stimulus. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -128,7 +135,8 @@ def chromatic_adaptation_Fairchild1990(XYZ_1, LMS_r = vector_dot(MATRIX_XYZ_TO_RGB_FAIRCHILD1990, XYZ_r) p_LMS = degrees_of_adaptation( - LMS_1, Y_n, discount_illuminant=discount_illuminant) + LMS_1, Y_n, discount_illuminant=discount_illuminant + ) a_LMS_1 = p_LMS / LMS_n a_LMS_2 = p_LMS / LMS_r @@ -150,18 +158,18 @@ def chromatic_adaptation_Fairchild1990(XYZ_1, return from_range_100(XYZ_c) -def XYZ_to_RGB_Fairchild1990(XYZ): +def XYZ_to_RGB_Fairchild1990(XYZ: ArrayLike) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to cone responses. + Convert from *CIE XYZ* tristimulus values to cone responses. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. Returns ------- - ndarray + :class:`numpy.ndarray` Cone responses. Examples @@ -174,18 +182,18 @@ def XYZ_to_RGB_Fairchild1990(XYZ): return vector_dot(MATRIX_XYZ_TO_RGB_FAIRCHILD1990, XYZ) -def RGB_to_XYZ_Fairchild1990(RGB): +def RGB_to_XYZ_Fairchild1990(RGB: ArrayLike) -> NDArray: """ - Converts from cone responses to *CIE XYZ* tristimulus values. + Convert from cone responses to *CIE XYZ* tristimulus values. Parameters ---------- - RGB : array_like + RGB Cone responses. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Examples @@ -198,25 +206,30 @@ def RGB_to_XYZ_Fairchild1990(RGB): return vector_dot(MATRIX_RGB_TO_XYZ_FAIRCHILD1990, RGB) -def degrees_of_adaptation(LMS, Y_n, v=1 / 3, discount_illuminant=False): +def degrees_of_adaptation( + LMS: ArrayLike, + Y_n: FloatingOrArrayLike, + v: FloatingOrArrayLike = 1 / 3, + discount_illuminant: Boolean = False, +) -> NDArray: """ - Computes the degrees of adaptation :math:`p_L`, :math:`p_M` and + Compute the degrees of adaptation :math:`p_L`, :math:`p_M` and :math:`p_S`. Parameters ---------- - LMS : array_like + LMS Cone responses. - Y_n : numeric or array_like + Y_n Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`. - v : numeric or array_like, optional + v Exponent :math:`v`. - discount_illuminant : bool, optional + discount_illuminant Truth value indicating if the illuminant should be discounted. Returns ------- - ndarray + :class:`numpy.ndarray` Degrees of adaptation :math:`p_L`, :math:`p_M` and :math:`p_S`. Examples @@ -244,17 +257,13 @@ def degrees_of_adaptation(LMS, Y_n, v=1 / 3, discount_illuminant=False): Ye_n = spow(Y_n, v) - def m_E(x, y): - """ - Computes the :math:`m_E` term. - """ + def m_E(x: NDArray, y: NDArray) -> NDArray: + """Compute the :math:`m_E` term.""" return (3 * (x / y)) / (L / L_E + M / M_E + S / S_E) - def P_c(x): - """ - Computes the :math:`P_L`, :math:`P_M` or :math:`P_S` terms. - """ + def P_c(x: NDArray) -> NDArray: + """Compute the :math:`P_L`, :math:`P_M` or :math:`P_S` terms.""" return (1 + Ye_n + x) / (1 + Ye_n + 1 / x) diff --git a/colour/adaptation/tests/__init__.py b/colour/adaptation/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/adaptation/tests/__init__.py +++ b/colour/adaptation/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/adaptation/tests/test__init__.py b/colour/adaptation/tests/test__init__.py index 06eb2acf6c..6d87aed839 100644 --- a/colour/adaptation/tests/test__init__.py +++ b/colour/adaptation/tests/test__init__.py @@ -1,38 +1,32 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.adaptation` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.adaptation` module.""" import numpy as np import unittest -from six.moves import zip from colour.adaptation import chromatic_adaptation from colour.utilities import domain_range_scale -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestChromaticAdaptation'] +__all__ = [ + "TestChromaticAdaptation", +] class TestChromaticAdaptation(unittest.TestCase): """ - Defines :func:`colour.adaptation.chromatic_adaptation` definition unit + Define :func:`colour.adaptation.chromatic_adaptation` definition unit tests methods. """ def test_chromatic_adaptation(self): - """ - Tests :func:`colour.adaptation.chromatic_adaptation` definition. - """ + """Test :func:`colour.adaptation.chromatic_adaptation` definition.""" XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775]) @@ -40,7 +34,8 @@ def test_chromatic_adaptation(self): np.testing.assert_almost_equal( chromatic_adaptation(XYZ, XYZ_w, XYZ_wr), np.array([0.21638819, 0.12570000, 0.03847494]), - decimal=7) + decimal=7, + ) Y_o = 0.2 E_o = 1000 @@ -49,30 +44,36 @@ def test_chromatic_adaptation(self): XYZ, XYZ_w, XYZ_wr, - method='CIE 1994', + method="CIE 1994", Y_o=Y_o, E_o1=E_o, - E_o2=E_o), + E_o2=E_o, + ), np.array([0.21347453, 0.12252986, 0.03347887]), - decimal=7) + decimal=7, + ) L_A = 200 np.testing.assert_almost_equal( chromatic_adaptation( - XYZ, XYZ_w, XYZ_wr, method='CMCCAT2000', L_A1=L_A, L_A2=L_A), + XYZ, XYZ_w, XYZ_wr, method="CMCCAT2000", L_A1=L_A, L_A2=L_A + ), np.array([0.21498829, 0.12474711, 0.03910138]), - decimal=7) + decimal=7, + ) Y_n = 200 np.testing.assert_almost_equal( chromatic_adaptation( - XYZ, XYZ_w, XYZ_wr, method='Fairchild 1990', Y_n=Y_n), + XYZ, XYZ_w, XYZ_wr, method="Fairchild 1990", Y_n=Y_n + ), np.array([0.21394049, 0.12262315, 0.03891917]), - decimal=7) + decimal=7, + ) def test_domain_range_scale_chromatic_adaptation(self): """ - Tests :func:`colour.adaptation.chromatic_adaptation` definition domain + Test :func:`colour.adaptation.chromatic_adaptation` definition domain and range scale support. """ @@ -84,7 +85,7 @@ def test_domain_range_scale_chromatic_adaptation(self): L_A = 200 Y_n = 200 - m = ('Von Kries', 'CIE 1994', 'CMCCAT2000', 'Fairchild 1990') + m = ("Von Kries", "CIE 1994", "CMCCAT2000", "Fairchild 1990") v = [ chromatic_adaptation( XYZ, @@ -96,10 +97,12 @@ def test_domain_range_scale_chromatic_adaptation(self): E_o2=E_o, L_A1=L_A, L_A2=L_A, - Y_n=Y_n) for method in m + Y_n=Y_n, + ) + for method in m ] - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for method, value in zip(m, v): for scale, factor in d_r: with domain_range_scale(scale): @@ -114,10 +117,12 @@ def test_domain_range_scale_chromatic_adaptation(self): E_o2=E_o, L_A1=L_A, L_A2=L_A, - Y_n=Y_n), + Y_n=Y_n, + ), value * factor, - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/adaptation/tests/test_cie1994.py b/colour/adaptation/tests/test_cie1994.py index d20f228c14..28d7770fff 100644 --- a/colour/adaptation/tests/test_cie1994.py +++ b/colour/adaptation/tests/test_cie1994.py @@ -1,10 +1,5 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.adaptation.cie1994` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.adaptation.cie1994` module.""" import numpy as np import unittest @@ -13,25 +8,27 @@ from colour.adaptation import chromatic_adaptation_CIE1994 from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestChromaticAdaptationCIE1994'] +__all__ = [ + "TestChromaticAdaptationCIE1994", +] class TestChromaticAdaptationCIE1994(unittest.TestCase): """ - Defines :func:`colour.adaptation.cie1994.chromatic_adaptation_CIE1994` + Define :func:`colour.adaptation.cie1994.chromatic_adaptation_CIE1994` definition unit tests methods. """ def test_chromatic_adaptation_CIE1994(self): """ - Tests :func:`colour.adaptation.cie1994.chromatic_adaptation_CIE1994` + Test :func:`colour.adaptation.cie1994.chromatic_adaptation_CIE1994` definition. """ @@ -42,9 +39,11 @@ def test_chromatic_adaptation_CIE1994(self): xy_o2=np.array([0.31270, 0.32900]), Y_o=20, E_o1=1000, - E_o2=1000), + E_o2=1000, + ), np.array([24.03379521, 21.15621214, 17.64301199]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( chromatic_adaptation_CIE1994( @@ -53,9 +52,11 @@ def test_chromatic_adaptation_CIE1994(self): xy_o2=np.array([0.31270, 0.32900]), Y_o=50, E_o1=100, - E_o2=1000), + E_o2=1000, + ), np.array([21.12891746, 19.42980532, 19.49577765]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( chromatic_adaptation_CIE1994( @@ -64,13 +65,15 @@ def test_chromatic_adaptation_CIE1994(self): xy_o2=np.array([0.37208, 0.37529]), Y_o=20, E_o1=100, - E_o2=1000), + E_o2=1000, + ), np.array([9.14287406, 9.35843355, 15.95753504]), - decimal=7) + decimal=7, + ) def test_n_dimensional_chromatic_adaptation_CIE1994(self): """ - Tests :func:`colour.adaptation.cie1994.chromatic_adaptation_CIE1994` + Test :func:`colour.adaptation.cie1994.chromatic_adaptation_CIE1994` definition n-dimensional arrays support. """ @@ -80,15 +83,17 @@ def test_n_dimensional_chromatic_adaptation_CIE1994(self): Y_o = 20 E_o1 = 1000 E_o2 = 1000 - XYZ_2 = chromatic_adaptation_CIE1994(XYZ_1, xy_o1, xy_o2, Y_o, E_o1, - E_o2) + XYZ_2 = chromatic_adaptation_CIE1994( + XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2 + ) XYZ_1 = np.tile(XYZ_1, (6, 1)) XYZ_2 = np.tile(XYZ_2, (6, 1)) np.testing.assert_almost_equal( chromatic_adaptation_CIE1994(XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2), XYZ_2, - decimal=7) + decimal=7, + ) xy_o1 = np.tile(xy_o1, (6, 1)) xy_o2 = np.tile(xy_o2, (6, 1)) @@ -98,7 +103,8 @@ def test_n_dimensional_chromatic_adaptation_CIE1994(self): np.testing.assert_almost_equal( chromatic_adaptation_CIE1994(XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2), XYZ_2, - decimal=7) + decimal=7, + ) XYZ_1 = np.reshape(XYZ_1, (2, 3, 3)) xy_o1 = np.reshape(xy_o1, (2, 3, 2)) @@ -110,11 +116,12 @@ def test_n_dimensional_chromatic_adaptation_CIE1994(self): np.testing.assert_almost_equal( chromatic_adaptation_CIE1994(XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2), XYZ_2, - decimal=7) + decimal=7, + ) def test_domain_range_scale_chromatic_adaptation_CIE1994(self): """ - Tests :func:`colour.adaptation.cie1994.chromatic_adaptation_CIE1994` + Test :func:`colour.adaptation.cie1994.chromatic_adaptation_CIE1994` definition domain and range scale support. """ @@ -124,22 +131,25 @@ def test_domain_range_scale_chromatic_adaptation_CIE1994(self): Y_o = 20 E_o1 = 1000 E_o2 = 1000 - XYZ_2 = chromatic_adaptation_CIE1994(XYZ_1, xy_o1, xy_o2, Y_o, E_o1, - E_o2) + XYZ_2 = chromatic_adaptation_CIE1994( + XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2 + ) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - chromatic_adaptation_CIE1994(XYZ_1 * factor, xy_o1, xy_o2, - Y_o * factor, E_o1, E_o2), + chromatic_adaptation_CIE1994( + XYZ_1 * factor, xy_o1, xy_o2, Y_o * factor, E_o1, E_o2 + ), XYZ_2 * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_chromatic_adaptation_CIE1994(self): """ - Tests :func:`colour.adaptation.cie1994.chromatic_adaptation_CIE1994` + Test :func:`colour.adaptation.cie1994.chromatic_adaptation_CIE1994` definition nan support. """ @@ -155,5 +165,5 @@ def test_nan_chromatic_adaptation_CIE1994(self): chromatic_adaptation_CIE1994(XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/adaptation/tests/test_cmccat2000.py b/colour/adaptation/tests/test_cmccat2000.py index 775f566474..68ee26ad1b 100644 --- a/colour/adaptation/tests/test_cmccat2000.py +++ b/colour/adaptation/tests/test_cmccat2000.py @@ -1,10 +1,5 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.adaptation.cmccat2000. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.adaptation.cmccat2000.""" import numpy as np import unittest @@ -12,32 +7,33 @@ from colour.adaptation.cmccat2000 import ( chromatic_adaptation_forward_CMCCAT2000, - chromatic_adaptation_inverse_CMCCAT2000) + chromatic_adaptation_inverse_CMCCAT2000, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestChromaticAdaptationForwardCMCCAT2000', - 'TestChromaticAdaptationInverseCMCCAT2000' + "TestChromaticAdaptationForwardCMCCAT2000", + "TestChromaticAdaptationInverseCMCCAT2000", ] class TestChromaticAdaptationForwardCMCCAT2000(unittest.TestCase): """ - Defines :func:`colour.adaptation.cmccat2000.\ + Define :func:`colour.adaptation.cmccat2000.\ chromatic_adaptation_forward_CMCCAT2000` definition unit tests methods. """ def test_chromatic_adaptation_forward_CMCCAT2000(self): """ - Tests :func:`colour.adaptation.cmccat2000.\ + Test :func:`colour.adaptation.cmccat2000.\ chromatic_adaptation_forward_CMCCAT2000` definition. """ @@ -45,31 +41,41 @@ def test_chromatic_adaptation_forward_CMCCAT2000(self): chromatic_adaptation_forward_CMCCAT2000( np.array([22.48, 22.74, 8.54]), np.array([111.15, 100.00, 35.20]), - np.array([94.81, 100.00, 107.30]), 200, 200), + np.array([94.81, 100.00, 107.30]), + 200, + 200, + ), np.array([19.52698326, 23.06833960, 24.97175229]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( chromatic_adaptation_forward_CMCCAT2000( np.array([0.14222010, 0.23042768, 0.10495772]) * 100, np.array([0.95045593, 1.00000000, 1.08905775]) * 100, - np.array([1.09846607, 1.00000000, 0.35582280]) * 100, 100, - 100), + np.array([1.09846607, 1.00000000, 0.35582280]) * 100, + 100, + 100, + ), np.array([17.90511171, 22.75299363, 3.79837384]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( chromatic_adaptation_forward_CMCCAT2000( np.array([0.07818780, 0.06157201, 0.28099326]) * 100, np.array([0.95045593, 1.00000000, 1.08905775]) * 100, - np.array([0.99144661, 1.00000000, 0.67315942]) * 100, 100, - 100), + np.array([0.99144661, 1.00000000, 0.67315942]) * 100, + 100, + 100, + ), np.array([6.76564344, 5.86585763, 18.40577315]), - decimal=7) + decimal=7, + ) def test_n_dimensional_chromatic_adaptation_forward_CMCCAT2000(self): """ - Tests :func:`colour.adaptation.cmccat2000.\ + Test :func:`colour.adaptation.cmccat2000.\ chromatic_adaptation_forward_CMCCAT2000` definition n-dimensional arrays support. """ @@ -80,25 +86,30 @@ def test_n_dimensional_chromatic_adaptation_forward_CMCCAT2000(self): L_A1 = 200 L_A2 = 200 XYZ_c = chromatic_adaptation_forward_CMCCAT2000( - XYZ, XYZ_w, XYZ_wr, L_A1, L_A2) + XYZ, XYZ_w, XYZ_wr, L_A1, L_A2 + ) XYZ = np.tile(XYZ, (6, 1)) XYZ_c = np.tile(XYZ_c, (6, 1)) np.testing.assert_almost_equal( - chromatic_adaptation_forward_CMCCAT2000(XYZ, XYZ_w, XYZ_wr, L_A1, - L_A2), + chromatic_adaptation_forward_CMCCAT2000( + XYZ, XYZ_w, XYZ_wr, L_A1, L_A2 + ), XYZ_c, - decimal=7) + decimal=7, + ) XYZ_w = np.tile(XYZ_w, (6, 1)) XYZ_wr = np.tile(XYZ_wr, (6, 1)) L_A1 = np.tile(L_A1, 6) L_A2 = np.tile(L_A2, 6) np.testing.assert_almost_equal( - chromatic_adaptation_forward_CMCCAT2000(XYZ, XYZ_w, XYZ_wr, L_A1, - L_A2), + chromatic_adaptation_forward_CMCCAT2000( + XYZ, XYZ_w, XYZ_wr, L_A1, L_A2 + ), XYZ_c, - decimal=7) + decimal=7, + ) XYZ = np.reshape(XYZ, (2, 3, 3)) XYZ_w = np.reshape(XYZ_w, (2, 3, 3)) @@ -107,14 +118,16 @@ def test_n_dimensional_chromatic_adaptation_forward_CMCCAT2000(self): L_A2 = np.reshape(L_A2, (2, 3)) XYZ_c = np.reshape(XYZ_c, (2, 3, 3)) np.testing.assert_almost_equal( - chromatic_adaptation_forward_CMCCAT2000(XYZ, XYZ_w, XYZ_wr, L_A1, - L_A2), + chromatic_adaptation_forward_CMCCAT2000( + XYZ, XYZ_w, XYZ_wr, L_A1, L_A2 + ), XYZ_c, - decimal=7) + decimal=7, + ) def test_domain_range_scale_chromatic_adaptation_CMCCAT2000(self): """ - Tests :func:`colour.adaptation.cmccat2000.\ + Test :func:`colour.adaptation.cmccat2000.\ chromatic_adaptation_forward_CMCCAT2000` definition domain and range scale support. """ @@ -125,22 +138,28 @@ def test_domain_range_scale_chromatic_adaptation_CMCCAT2000(self): L_A1 = 200 L_A2 = 200 XYZ_c = chromatic_adaptation_forward_CMCCAT2000( - XYZ, XYZ_w, XYZ_wr, L_A1, L_A2) + XYZ, XYZ_w, XYZ_wr, L_A1, L_A2 + ) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( chromatic_adaptation_forward_CMCCAT2000( - XYZ * factor, XYZ_w * factor, XYZ_wr * factor, L_A1, - L_A2), + XYZ * factor, + XYZ_w * factor, + XYZ_wr * factor, + L_A1, + L_A2, + ), XYZ_c * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_chromatic_adaptation_forward_CMCCAT2000(self): """ - Tests :func:`colour.adaptation.cmccat2000.\ + Test :func:`colour.adaptation.cmccat2000.\ chromatic_adaptation_forward_CMCCAT2000` definition nan support. """ @@ -152,19 +171,20 @@ def test_nan_chromatic_adaptation_forward_CMCCAT2000(self): XYZ_wr = np.array(case) L_A1 = case[0] L_A2 = case[0] - chromatic_adaptation_forward_CMCCAT2000(XYZ, XYZ_w, XYZ_wr, L_A1, - L_A2) + chromatic_adaptation_forward_CMCCAT2000( + XYZ, XYZ_w, XYZ_wr, L_A1, L_A2 + ) class TestChromaticAdaptationInverseCMCCAT2000(unittest.TestCase): """ - Defines :func:`colour.adaptation.cmccat2000.\ + Define :func:`colour.adaptation.cmccat2000.\ chromatic_adaptation_inverse_CMCCAT2000` definition unit tests methods. """ def test_chromatic_adaptation_inverse_CMCCAT2000(self): """ - Tests :func:`colour.adaptation.cmccat2000.\ + Test :func:`colour.adaptation.cmccat2000.\ chromatic_adaptation_inverse_CMCCAT2000` definition. """ @@ -172,31 +192,41 @@ def test_chromatic_adaptation_inverse_CMCCAT2000(self): chromatic_adaptation_inverse_CMCCAT2000( np.array([19.52698326, 23.06833960, 24.97175229]), np.array([111.15, 100.00, 35.20]), - np.array([94.81, 100.00, 107.30]), 200, 200), + np.array([94.81, 100.00, 107.30]), + 200, + 200, + ), np.array([22.48, 22.74, 8.54]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( chromatic_adaptation_inverse_CMCCAT2000( np.array([17.90511171, 22.75299363, 3.79837384]), np.array([0.95045593, 1.00000000, 1.08905775]) * 100, - np.array([1.09846607, 1.00000000, 0.35582280]) * 100, 100, - 100), + np.array([1.09846607, 1.00000000, 0.35582280]) * 100, + 100, + 100, + ), np.array([0.14222010, 0.23042768, 0.10495772]) * 100, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( chromatic_adaptation_inverse_CMCCAT2000( np.array([6.76564344, 5.86585763, 18.40577315]), np.array([0.95045593, 1.00000000, 1.08905775]) * 100, - np.array([0.99144661, 1.00000000, 0.67315942]) * 100, 100, - 100), + np.array([0.99144661, 1.00000000, 0.67315942]) * 100, + 100, + 100, + ), np.array([0.07818780, 0.06157201, 0.28099326]) * 100, - decimal=7) + decimal=7, + ) def test_n_dimensional_chromatic_adaptation_inverse_CMCCAT2000(self): """ - Tests :func:`colour.adaptation.cmccat2000.\ + Test :func:`colour.adaptation.cmccat2000.\ chromatic_adaptation_inverse_CMCCAT2000` definition n-dimensional arrays support. """ @@ -206,26 +236,31 @@ def test_n_dimensional_chromatic_adaptation_inverse_CMCCAT2000(self): XYZ_wr = np.array([94.81, 100.00, 107.30]) L_A1 = 200 L_A2 = 200 - XYZ = chromatic_adaptation_inverse_CMCCAT2000(XYZ_c, XYZ_w, XYZ_wr, - L_A1, L_A2) + XYZ = chromatic_adaptation_inverse_CMCCAT2000( + XYZ_c, XYZ_w, XYZ_wr, L_A1, L_A2 + ) XYZ_c = np.tile(XYZ_c, (6, 1)) XYZ = np.tile(XYZ, (6, 1)) np.testing.assert_almost_equal( - chromatic_adaptation_inverse_CMCCAT2000(XYZ_c, XYZ_w, XYZ_wr, L_A1, - L_A2), + chromatic_adaptation_inverse_CMCCAT2000( + XYZ_c, XYZ_w, XYZ_wr, L_A1, L_A2 + ), XYZ, - decimal=7) + decimal=7, + ) XYZ_w = np.tile(XYZ_w, (6, 1)) XYZ_wr = np.tile(XYZ_wr, (6, 1)) L_A1 = np.tile(L_A1, 6) L_A2 = np.tile(L_A2, 6) np.testing.assert_almost_equal( - chromatic_adaptation_inverse_CMCCAT2000(XYZ_c, XYZ_w, XYZ_wr, L_A1, - L_A2), + chromatic_adaptation_inverse_CMCCAT2000( + XYZ_c, XYZ_w, XYZ_wr, L_A1, L_A2 + ), XYZ, - decimal=7) + decimal=7, + ) XYZ_c = np.reshape(XYZ_c, (2, 3, 3)) XYZ_w = np.reshape(XYZ_w, (2, 3, 3)) @@ -234,14 +269,16 @@ def test_n_dimensional_chromatic_adaptation_inverse_CMCCAT2000(self): L_A2 = np.reshape(L_A2, (2, 3)) XYZ = np.reshape(XYZ, (2, 3, 3)) np.testing.assert_almost_equal( - chromatic_adaptation_inverse_CMCCAT2000(XYZ_c, XYZ_w, XYZ_wr, L_A1, - L_A2), + chromatic_adaptation_inverse_CMCCAT2000( + XYZ_c, XYZ_w, XYZ_wr, L_A1, L_A2 + ), XYZ, - decimal=7) + decimal=7, + ) def test_domain_range_scale_chromatic_adaptation_CMCCAT2000(self): """ - Tests :func:`colour.adaptation.cmccat2000.\ + Test :func:`colour.adaptation.cmccat2000.\ chromatic_adaptation_inverse_CMCCAT2000` definition domain and range scale support. """ @@ -251,23 +288,29 @@ def test_domain_range_scale_chromatic_adaptation_CMCCAT2000(self): XYZ_wr = np.array([94.81, 100.00, 107.30]) L_A1 = 200 L_A2 = 200 - XYZ = chromatic_adaptation_inverse_CMCCAT2000(XYZ_c, XYZ_w, XYZ_wr, - L_A1, L_A2) + XYZ = chromatic_adaptation_inverse_CMCCAT2000( + XYZ_c, XYZ_w, XYZ_wr, L_A1, L_A2 + ) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( chromatic_adaptation_inverse_CMCCAT2000( - XYZ_c * factor, XYZ_w * factor, XYZ_wr * factor, L_A1, - L_A2), + XYZ_c * factor, + XYZ_w * factor, + XYZ_wr * factor, + L_A1, + L_A2, + ), XYZ * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_chromatic_adaptation_inverse_CMCCAT2000(self): """ - Tests :func:`colour.adaptation.cmccat2000.\ + Test :func:`colour.adaptation.cmccat2000.\ chromatic_adaptation_inverse_CMCCAT2000` definition nan support. """ @@ -279,9 +322,10 @@ def test_nan_chromatic_adaptation_inverse_CMCCAT2000(self): XYZ_wr = np.array(case) L_A1 = case[0] L_A2 = case[0] - chromatic_adaptation_inverse_CMCCAT2000(XYZ_c, XYZ_w, XYZ_wr, L_A1, - L_A2) + chromatic_adaptation_inverse_CMCCAT2000( + XYZ_c, XYZ_w, XYZ_wr, L_A1, L_A2 + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/adaptation/tests/test_fairchild1990.py b/colour/adaptation/tests/test_fairchild1990.py index 3c8302d3a9..d410d29922 100644 --- a/colour/adaptation/tests/test_fairchild1990.py +++ b/colour/adaptation/tests/test_fairchild1990.py @@ -1,10 +1,5 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.adaptation.fairchild1990` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.adaptation.fairchild1990` module.""" import numpy as np import unittest @@ -13,25 +8,27 @@ from colour.adaptation import chromatic_adaptation_Fairchild1990 from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestChromaticAdaptationFairchild1990'] +__all__ = [ + "TestChromaticAdaptationFairchild1990", +] class TestChromaticAdaptationFairchild1990(unittest.TestCase): """ - Defines :func:`colour.adaptation.fairchild1990.\ + Define :func:`colour.adaptation.fairchild1990.\ chromatic_adaptation_Fairchild1990` definition unit tests methods. """ def test_chromatic_adaptation_Fairchild1990(self): """ - Tests :func:`colour.adaptation.fairchild1990.\ + Test :func:`colour.adaptation.fairchild1990.\ chromatic_adaptation_Fairchild1990` definition. """ @@ -39,29 +36,38 @@ def test_chromatic_adaptation_Fairchild1990(self): chromatic_adaptation_Fairchild1990( np.array([19.53, 23.07, 24.97]), np.array([111.15, 100.00, 35.20]), - np.array([94.81, 100.00, 107.30]), 200), + np.array([94.81, 100.00, 107.30]), + 200, + ), np.array([23.32526349, 23.32455819, 76.11593750]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( chromatic_adaptation_Fairchild1990( np.array([0.14222010, 0.23042768, 0.10495772]) * 100, np.array([0.95045593, 1.00000000, 1.08905775]) * 100, - np.array([1.09846607, 1.00000000, 0.35582280]) * 100, 200), + np.array([1.09846607, 1.00000000, 0.35582280]) * 100, + 200, + ), np.array([19.28089326, 22.91583715, 3.42923503]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( chromatic_adaptation_Fairchild1990( np.array([0.07818780, 0.06157201, 0.28099326]) * 100, np.array([0.95045593, 1.00000000, 1.08905775]) * 100, - np.array([0.99144661, 1.00000000, 0.67315942]) * 100, 200), + np.array([0.99144661, 1.00000000, 0.67315942]) * 100, + 200, + ), np.array([6.35093475, 6.13061347, 17.36852430]), - decimal=7) + decimal=7, + ) def test_n_dimensional_chromatic_adaptation_Fairchild1990(self): """ - Tests :func:`colour.adaptation.fairchild1990.\ + Test :func:`colour.adaptation.fairchild1990.\ chromatic_adaptation_Fairchild1990` definition n-dimensional arrays support. """ @@ -76,7 +82,8 @@ def test_n_dimensional_chromatic_adaptation_Fairchild1990(self): np.testing.assert_almost_equal( chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_n, XYZ_r, Y_n), XYZ_c, - decimal=7) + decimal=7, + ) XYZ_n = np.tile(XYZ_n, (6, 1)) XYZ_r = np.tile(XYZ_r, (6, 1)) @@ -84,7 +91,8 @@ def test_n_dimensional_chromatic_adaptation_Fairchild1990(self): np.testing.assert_almost_equal( chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_n, XYZ_r, Y_n), XYZ_c, - decimal=7) + decimal=7, + ) XYZ_1 = np.reshape(XYZ_1, (2, 3, 3)) XYZ_n = np.reshape(XYZ_n, (2, 3, 3)) @@ -94,11 +102,12 @@ def test_n_dimensional_chromatic_adaptation_Fairchild1990(self): np.testing.assert_almost_equal( chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_n, XYZ_r, Y_n), XYZ_c, - decimal=7) + decimal=7, + ) def test_domain_range_scale_chromatic_adaptation_Fairchild1990(self): """ - Tests :func:`colour.adaptation.fairchild1990.\ + Test :func:`colour.adaptation.fairchild1990.\ chromatic_adaptation_Fairchild1990` definition domain and range scale support. """ @@ -108,19 +117,21 @@ def test_domain_range_scale_chromatic_adaptation_Fairchild1990(self): Y_n = 200 XYZ_c = chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_n, XYZ_r, Y_n) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( chromatic_adaptation_Fairchild1990( - XYZ_1 * factor, XYZ_n * factor, XYZ_r * factor, Y_n), + XYZ_1 * factor, XYZ_n * factor, XYZ_r * factor, Y_n + ), XYZ_c * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_chromatic_adaptation_Fairchild1990(self): """ - Tests :func:`colour.adaptation.fairchild1990.\ + Test :func:`colour.adaptation.fairchild1990.\ chromatic_adaptation_Fairchild1990` definition nan support. """ @@ -134,5 +145,5 @@ def test_nan_chromatic_adaptation_Fairchild1990(self): chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_n, XYZ_r, Y_n) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/adaptation/tests/test_vonkries.py b/colour/adaptation/tests/test_vonkries.py index 7472214454..3731d26aff 100644 --- a/colour/adaptation/tests/test_vonkries.py +++ b/colour/adaptation/tests/test_vonkries.py @@ -1,128 +1,136 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.adaptation.vonkries` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.adaptation.vonkries` module.""" import numpy as np import unittest from itertools import permutations -from functools import partial -from colour.adaptation import (matrix_chromatic_adaptation_VonKries, - chromatic_adaptation_VonKries) +from colour.adaptation import ( + matrix_chromatic_adaptation_VonKries, + chromatic_adaptation_VonKries, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestMatrixChromaticAdaptationVonKries', 'TestChromaticAdaptationVonKries' + "TestMatrixChromaticAdaptationVonKries", + "TestChromaticAdaptationVonKries", ] class TestMatrixChromaticAdaptationVonKries(unittest.TestCase): """ - Defines :func:`colour.adaptation.vonkries.\ + Define :func:`colour.adaptation.vonkries.\ matrix_chromatic_adaptation_VonKries` definition unit tests methods. """ def test_matrix_chromatic_adaptation_VonKries(self): """ - Tests :func:`colour.adaptation.vonkries.\ + Test :func:`colour.adaptation.vonkries.\ matrix_chromatic_adaptation_VonKries` definition. """ np.testing.assert_almost_equal( matrix_chromatic_adaptation_VonKries( np.array([0.95045593, 1.00000000, 1.08905775]), - np.array([0.96429568, 1.00000000, 0.82510460])), - np.array([ - [1.04257389, 0.03089108, -0.05281257], - [0.02219345, 1.00185663, -0.02107375], - [-0.00116488, -0.00342053, 0.76178907], - ]), - decimal=7) + np.array([0.96429568, 1.00000000, 0.82510460]), + ), + np.array( + [ + [1.04257389, 0.03089108, -0.05281257], + [0.02219345, 1.00185663, -0.02107375], + [-0.00116488, -0.00342053, 0.76178907], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( matrix_chromatic_adaptation_VonKries( np.array([0.95045593, 1.00000000, 1.08905775]), - np.array([1.09846607, 1.00000000, 0.35582280])), - np.array([ - [1.17159793, 0.16088780, -0.16158366], - [0.11462057, 0.96182051, -0.06497572], - [-0.00413024, -0.00912739, 0.33871096], - ]), - decimal=7) + np.array([1.09846607, 1.00000000, 0.35582280]), + ), + np.array( + [ + [1.17159793, 0.16088780, -0.16158366], + [0.11462057, 0.96182051, -0.06497572], + [-0.00413024, -0.00912739, 0.33871096], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( matrix_chromatic_adaptation_VonKries( np.array([0.95045593, 1.00000000, 1.08905775]), - np.array([0.99144661, 1.00000000, 0.67315942])), + np.array([0.99144661, 1.00000000, 0.67315942]), + ), np.linalg.inv( matrix_chromatic_adaptation_VonKries( np.array([0.99144661, 1.00000000, 0.67315942]), - np.array([0.95045593, 1.00000000, 1.08905775]))), - decimal=7) + np.array([0.95045593, 1.00000000, 1.08905775]), + ) + ), + decimal=7, + ) np.testing.assert_almost_equal( matrix_chromatic_adaptation_VonKries( np.array([0.95045593, 1.00000000, 1.08905775]), np.array([0.96429568, 1.00000000, 0.82510460]), - transform='XYZ Scaling'), - np.array([ - [1.01456117, 0.00000000, 0.00000000], - [0.00000000, 1.00000000, 0.00000000], - [0.00000000, 0.00000000, 0.75763163], - ]), - decimal=7) + transform="XYZ Scaling", + ), + np.array( + [ + [1.01456117, 0.00000000, 0.00000000], + [0.00000000, 1.00000000, 0.00000000], + [0.00000000, 0.00000000, 0.75763163], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( matrix_chromatic_adaptation_VonKries( np.array([0.95045593, 1.00000000, 1.08905775]), np.array([0.96429568, 1.00000000, 0.82510460]), - transform='Bradford'), - np.array([ - [1.04792979, 0.02294687, -0.05019227], - [0.02962781, 0.99043443, -0.01707380], - [-0.00924304, 0.01505519, 0.75187428], - ]), - decimal=7) + transform="Bradford", + ), + np.array( + [ + [1.04792979, 0.02294687, -0.05019227], + [0.02962781, 0.99043443, -0.01707380], + [-0.00924304, 0.01505519, 0.75187428], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( matrix_chromatic_adaptation_VonKries( np.array([0.95045593, 1.00000000, 1.08905775]), np.array([0.96429568, 1.00000000, 0.82510460]), - transform='Von Kries'), - np.array([ - [1.01611856, 0.05535971, -0.05219186], - [0.00608087, 0.99555604, -0.00122642], - [0.00000000, 0.00000000, 0.75763163], - ]), - decimal=7) - - def test_raise_exception_matrix_chromatic_adaptation_VonKries(self): - """ - Tests :func:`colour.adaptation.vonkries.\ -matrix_chromatic_adaptation_VonKries` definition raised exception. - """ - - self.assertRaises( - KeyError, - partial(matrix_chromatic_adaptation_VonKries, - np.array([0.95045593, 1.00000000, 1.08905775]), - np.array([0.96429568, 1.00000000, 0.82510460]), - 'Undefined')) + transform="Von Kries", + ), + np.array( + [ + [1.01611856, 0.05535971, -0.05219186], + [0.00608087, 0.99555604, -0.00122642], + [0.00000000, 0.00000000, 0.75763163], + ] + ), + decimal=7, + ) def test_n_dimensional_matrix_chromatic_adaptation_VonKries(self): """ - Tests :func:`colour.adaptation.vonkries.\ + Test :func:`colour.adaptation.vonkries.\ matrix_chromatic_adaptation_VonKries` definition n-dimensional arrays support. """ @@ -134,17 +142,19 @@ def test_n_dimensional_matrix_chromatic_adaptation_VonKries(self): XYZ_wr = np.tile(XYZ_wr, (6, 1)) M = np.reshape(np.tile(M, (6, 1)), (6, 3, 3)) np.testing.assert_almost_equal( - matrix_chromatic_adaptation_VonKries(XYZ_w, XYZ_wr), M, decimal=7) + matrix_chromatic_adaptation_VonKries(XYZ_w, XYZ_wr), M, decimal=7 + ) XYZ_w = np.reshape(XYZ_w, (2, 3, 3)) XYZ_wr = np.reshape(XYZ_wr, (2, 3, 3)) M = np.reshape(M, (2, 3, 3, 3)) np.testing.assert_almost_equal( - matrix_chromatic_adaptation_VonKries(XYZ_w, XYZ_wr), M, decimal=7) + matrix_chromatic_adaptation_VonKries(XYZ_w, XYZ_wr), M, decimal=7 + ) def test_domain_range_scale_chromatic_adaptation_VonKries(self): """ - Tests :func:`colour.adaptation.vonkries.\ + Test :func:`colour.adaptation.vonkries.\ matrix_chromatic_adaptation_VonKries` definition domain and range scale support. """ @@ -153,19 +163,21 @@ def test_domain_range_scale_chromatic_adaptation_VonKries(self): XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460]) M = matrix_chromatic_adaptation_VonKries(XYZ_w, XYZ_wr) - d_r = (('reference', 1), (1, 1), (100, 0.01)) + d_r = (("reference", 1), ("1", 1), ("100", 0.01)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( matrix_chromatic_adaptation_VonKries( - XYZ_w * factor, XYZ_wr * factor), + XYZ_w * factor, XYZ_wr * factor + ), M, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_matrix_chromatic_adaptation_VonKries(self): """ - Tests :func:`colour.adaptation.vonkries.\ + Test :func:`colour.adaptation.vonkries.\ matrix_chromatic_adaptation_VonKries` definition nan support. """ @@ -179,13 +191,13 @@ def test_nan_matrix_chromatic_adaptation_VonKries(self): class TestChromaticAdaptationVonKries(unittest.TestCase): """ - Defines :func:`colour.adaptation.vonkries.chromatic_adaptation_VonKries` + Define :func:`colour.adaptation.vonkries.chromatic_adaptation_VonKries` definition unit tests methods. """ def test_chromatic_adaptation_VonKries(self): """ - Tests :func:`colour.adaptation.vonkries.chromatic_adaptation_VonKries` + Test :func:`colour.adaptation.vonkries.chromatic_adaptation_VonKries` definition. """ @@ -193,56 +205,68 @@ def test_chromatic_adaptation_VonKries(self): chromatic_adaptation_VonKries( np.array([0.20654008, 0.12197225, 0.05136952]), np.array([0.95045593, 1.00000000, 1.08905775]), - np.array([0.96429568, 1.00000000, 0.82510460])), + np.array([0.96429568, 1.00000000, 0.82510460]), + ), np.array([0.21638819, 0.12570000, 0.03847494]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( chromatic_adaptation_VonKries( np.array([0.14222010, 0.23042768, 0.10495772]), np.array([0.95045593, 1.00000000, 1.08905775]), - np.array([1.09846607, 1.00000000, 0.35582280])), + np.array([1.09846607, 1.00000000, 0.35582280]), + ), np.array([0.18673833, 0.23111171, 0.03285972]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( chromatic_adaptation_VonKries( np.array([0.07818780, 0.06157201, 0.28099326]), np.array([0.95045593, 1.00000000, 1.08905775]), - np.array([0.99144661, 1.00000000, 0.67315942])), + np.array([0.99144661, 1.00000000, 0.67315942]), + ), np.array([0.06385467, 0.05509729, 0.17506386]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( chromatic_adaptation_VonKries( np.array([0.20654008, 0.12197225, 0.05136952]), np.array([0.95045593, 1.00000000, 1.08905775]), np.array([0.96429568, 1.00000000, 0.82510460]), - transform='XYZ Scaling'), + transform="XYZ Scaling", + ), np.array([0.20954755, 0.12197225, 0.03891917]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( chromatic_adaptation_VonKries( np.array([0.20654008, 0.12197225, 0.05136952]), np.array([0.95045593, 1.00000000, 1.08905775]), np.array([0.96429568, 1.00000000, 0.82510460]), - transform='Bradford'), + transform="Bradford", + ), np.array([0.21666003, 0.12604777, 0.03855068]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( chromatic_adaptation_VonKries( np.array([0.20654008, 0.12197225, 0.05136952]), np.array([0.95045593, 1.00000000, 1.08905775]), np.array([0.96429568, 1.00000000, 0.82510460]), - transform='Von Kries'), + transform="Von Kries", + ), np.array([0.21394049, 0.12262315, 0.03891917]), - decimal=7) + decimal=7, + ) def test_n_dimensional_chromatic_adaptation_VonKries(self): """ - Tests :func:`colour.adaptation.vonkries.chromatic_adaptation_VonKries` + Test :func:`colour.adaptation.vonkries.chromatic_adaptation_VonKries` definition n-dimensional arrays support. """ @@ -256,22 +280,20 @@ def test_n_dimensional_chromatic_adaptation_VonKries(self): XYZ_wr = np.tile(XYZ_wr, (6, 1)) XYZ_a = np.tile(XYZ_a, (6, 1)) np.testing.assert_almost_equal( - chromatic_adaptation_VonKries(XYZ, XYZ_w, XYZ_wr), - XYZ_a, - decimal=7) + chromatic_adaptation_VonKries(XYZ, XYZ_w, XYZ_wr), XYZ_a, decimal=7 + ) XYZ = np.reshape(XYZ, (2, 3, 3)) XYZ_w = np.reshape(XYZ_w, (2, 3, 3)) XYZ_wr = np.reshape(XYZ_wr, (2, 3, 3)) XYZ_a = np.reshape(XYZ_a, (2, 3, 3)) np.testing.assert_almost_equal( - chromatic_adaptation_VonKries(XYZ, XYZ_w, XYZ_wr), - XYZ_a, - decimal=7) + chromatic_adaptation_VonKries(XYZ, XYZ_w, XYZ_wr), XYZ_a, decimal=7 + ) def test_domain_range_scale_chromatic_adaptation_VonKries(self): """ - Tests :func:`colour.adaptation.vonkries.chromatic_adaptation_VonKries` + Test :func:`colour.adaptation.vonkries.chromatic_adaptation_VonKries` definition domain and range scale support. """ @@ -280,19 +302,21 @@ def test_domain_range_scale_chromatic_adaptation_VonKries(self): XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460]) XYZ_a = chromatic_adaptation_VonKries(XYZ, XYZ_w, XYZ_wr) - d_r = (('reference', 1), (1, 1), (100, 0.01)) + d_r = (("reference", 1), ("1", 1), ("100", 0.01)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - chromatic_adaptation_VonKries(XYZ * factor, XYZ_w * factor, - XYZ_wr * factor), + chromatic_adaptation_VonKries( + XYZ * factor, XYZ_w * factor, XYZ_wr * factor + ), XYZ_a * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_chromatic_adaptation_VonKries(self): """ - Tests :func:`colour.adaptation.vonkries.chromatic_adaptation_VonKries` + Test :func:`colour.adaptation.vonkries.chromatic_adaptation_VonKries` definition nan support. """ @@ -305,5 +329,5 @@ def test_nan_chromatic_adaptation_VonKries(self): chromatic_adaptation_VonKries(XYZ, XYZ_w, XYZ_wr) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/adaptation/tests/test_zhai2018.py b/colour/adaptation/tests/test_zhai2018.py new file mode 100644 index 0000000000..e464a661f2 --- /dev/null +++ b/colour/adaptation/tests/test_zhai2018.py @@ -0,0 +1,186 @@ +# !/usr/bin/env python +"""Defines the unit tests for the :mod:`colour.adaptation.zhai2018` module.""" + +import numpy as np +import unittest +from itertools import permutations + +from colour.adaptation import chromatic_adaptation_Zhai2018 +from colour.utilities import domain_range_scale, ignore_numpy_errors + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestChromaticAdaptationZhai2018", +] + + +class TestChromaticAdaptationZhai2018(unittest.TestCase): + """ + Define :func:`colour.adaptation.zhai2018.chromatic_adaptation_Zhai2018` + definition unit tests methods. + """ + + def test_chromatic_adaptation_Zhai2018(self): + """ + Test :func:`colour.adaptation.zhai2018.chromatic_adaptation_Zhai2018` + definition. + """ + + np.testing.assert_almost_equal( + chromatic_adaptation_Zhai2018( + XYZ_b=np.array([48.900, 43.620, 6.250]), + XYZ_wb=np.array([109.850, 100, 35.585]), + XYZ_wd=np.array([95.047, 100, 108.883]), + D_b=0.9407, + D_d=0.9800, + XYZ_wo=np.array([100, 100, 100]), + ), + np.array([39.18561644, 42.15461798, 19.23672036]), + decimal=7, + ) + + np.testing.assert_almost_equal( + chromatic_adaptation_Zhai2018( + XYZ_b=np.array([48.900, 43.620, 6.250]), + XYZ_wb=np.array([109.850, 100, 35.585]), + XYZ_wd=np.array([95.047, 100, 108.883]), + D_b=0.9407, + D_d=0.9800, + XYZ_wo=np.array([100, 100, 100]), + transform="CAT16", + ), + np.array([40.37398343, 43.69426311, 20.51733764]), + decimal=7, + ) + + np.testing.assert_almost_equal( + chromatic_adaptation_Zhai2018( + XYZ_b=np.array([52.034, 58.824, 23.703]), + XYZ_wb=np.array([92.288, 100, 38.775]), + XYZ_wd=np.array([105.432, 100, 137.392]), + D_b=0.6709, + D_d=0.5331, + XYZ_wo=np.array([97.079, 100, 141.798]), + ), + np.array([57.03242915, 58.93434364, 64.76261333]), + decimal=7, + ) + + np.testing.assert_almost_equal( + chromatic_adaptation_Zhai2018( + XYZ_b=np.array([52.034, 58.824, 23.703]), + XYZ_wb=np.array([92.288, 100, 38.775]), + XYZ_wd=np.array([105.432, 100, 137.392]), + D_b=0.6709, + D_d=0.5331, + XYZ_wo=np.array([97.079, 100, 141.798]), + transform="CAT16", + ), + np.array([56.77130011, 58.81317888, 64.66922808]), + decimal=7, + ) + + np.testing.assert_almost_equal( + chromatic_adaptation_Zhai2018( + XYZ_b=np.array([48.900, 43.620, 6.250]), + XYZ_wb=np.array([109.850, 100, 35.585]), + XYZ_wd=np.array([95.047, 100, 108.883]), + ), + np.array([38.72444735, 42.09232891, 20.05297620]), + decimal=7, + ) + + def test_n_dimensional_chromatic_adaptation_Zhai2018(self): + """ + Test :func:`colour.adaptation.zhai2018.chromatic_adaptation_Zhai2018` + definition n-dimensional arrays support. + """ + + XYZ_b = np.array([48.900, 43.620, 6.250]) + XYZ_wb = np.array([109.850, 100, 35.585]) + XYZ_wd = np.array([95.047, 100, 108.883]) + D_b = 0.9407 + D_d = 0.9800 + XYZ_d = chromatic_adaptation_Zhai2018(XYZ_b, XYZ_wb, XYZ_wd, D_b, D_d) + + XYZ_b = np.tile(XYZ_b, (6, 1)) + XYZ_d = np.tile(XYZ_d, (6, 1)) + np.testing.assert_almost_equal( + chromatic_adaptation_Zhai2018(XYZ_b, XYZ_wb, XYZ_wd, D_b, D_d), + XYZ_d, + decimal=7, + ) + + XYZ_wb = np.tile(XYZ_wb, (6, 1)) + XYZ_wd = np.tile(XYZ_wd, (6, 1)) + D_b = np.tile(D_b, (6, 1)) + D_d = np.tile(D_d, (6, 1)) + np.testing.assert_almost_equal( + chromatic_adaptation_Zhai2018(XYZ_b, XYZ_wb, XYZ_wd, D_b, D_d), + XYZ_d, + decimal=7, + ) + + XYZ_b = np.reshape(XYZ_b, (2, 3, 3)) + XYZ_wb = np.reshape(XYZ_wb, (2, 3, 3)) + XYZ_wd = np.reshape(XYZ_wd, (2, 3, 3)) + D_b = np.reshape(D_b, (2, 3, 1)) + D_d = np.reshape(D_d, (2, 3, 1)) + XYZ_d = np.reshape(XYZ_d, (2, 3, 3)) + np.testing.assert_almost_equal( + chromatic_adaptation_Zhai2018(XYZ_b, XYZ_wb, XYZ_wd, D_b, D_d), + XYZ_d, + decimal=7, + ) + + def test_domain_range_scale_chromatic_adaptation_Zhai2018(self): + """ + Test :func:`colour.adaptation.zhai2018.chromatic_adaptation_Zhai2018` + definition domain and range scale support. + """ + + XYZ_b = np.array([48.900, 43.620, 6.250]) + XYZ_wb = np.array([109.850, 100, 35.585]) + XYZ_wd = np.array([95.047, 100, 108.883]) + XYZ_d = chromatic_adaptation_Zhai2018(XYZ_b, XYZ_wb, XYZ_wd) + + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + chromatic_adaptation_Zhai2018( + XYZ_b * factor, XYZ_wb * factor, XYZ_wd * factor + ), + XYZ_d * factor, + decimal=7, + ) + + @ignore_numpy_errors + def test_nan_chromatic_adaptation_Zhai2018(self): + """ + Test :func:`colour.adaptation.zhai2018.chromatic_adaptation_Zhai2018` + definition nan support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + XYZ_b = np.array(case) + XYZ_wb = np.array(case) + XYZ_wd = np.array(case) + D_b = case[0] + D_d = case[0] + XYZ_wo = np.array(case) + chromatic_adaptation_Zhai2018( + XYZ_b, XYZ_wb, XYZ_wd, D_b, D_d, XYZ_wo + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/adaptation/vonkries.py b/colour/adaptation/vonkries.py index 4fb2db7711..f9deab71ee 100644 --- a/colour/adaptation/vonkries.py +++ b/colour/adaptation/vonkries.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Von Kries Chromatic Adaptation Model ==================================== -Defines *Von Kries* chromatic adaptation model objects: +Defines the *Von Kries* chromatic adaptation model objects: - :func:`colour.adaptation.matrix_chromatic_adaptation_VonKries` - :func:`colour.adaptation.chromatic_adaptation_VonKries` @@ -15,56 +14,75 @@ ISBN:B00DAYO8E2 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.adaptation import CHROMATIC_ADAPTATION_TRANSFORMS -from colour.utilities import (matrix_dot, vector_dot, from_range_1, - row_as_diagonal, to_domain_1) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.algebra import matrix_dot, vector_dot +from colour.hints import ArrayLike, Literal, NDArray, Union +from colour.utilities import ( + from_range_1, + row_as_diagonal, + to_domain_1, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'matrix_chromatic_adaptation_VonKries', 'chromatic_adaptation_VonKries' + "matrix_chromatic_adaptation_VonKries", + "chromatic_adaptation_VonKries", ] -def matrix_chromatic_adaptation_VonKries(XYZ_w, XYZ_wr, transform='CAT02'): +def matrix_chromatic_adaptation_VonKries( + XYZ_w: ArrayLike, + XYZ_wr: ArrayLike, + transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", +) -> NDArray: """ - Computes the *chromatic adaptation* matrix from test viewing conditions + Compute the *chromatic adaptation* matrix from test viewing conditions to reference viewing conditions. Parameters ---------- - XYZ_w : array_like - Test viewing condition *CIE XYZ* tristimulus values of whitepoint. - XYZ_wr : array_like - Reference viewing condition *CIE XYZ* tristimulus values of whitepoint. - transform : unicode, optional - **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', - 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02 Brill 2008', - 'Bianco 2010', 'Bianco PC 2010'}**, + XYZ_w + Test viewing conditions *CIE XYZ* tristimulus values of whitepoint. + XYZ_wr + Reference viewing conditions *CIE XYZ* tristimulus values of + whitepoint. + transform Chromatic adaptation transform. Returns ------- - ndarray + :class:`numpy.ndarray` Chromatic adaptation matrix :math:`M_{cat}`. - Raises - ------ - KeyError - If chromatic adaptation method is not defined. - Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -102,18 +120,19 @@ def matrix_chromatic_adaptation_VonKries(XYZ_w, XYZ_wr, transform='CAT02'): XYZ_w = to_domain_1(XYZ_w) XYZ_wr = to_domain_1(XYZ_wr) - M = CHROMATIC_ADAPTATION_TRANSFORMS.get(transform) + transform = validate_method( + transform, + CHROMATIC_ADAPTATION_TRANSFORMS, + '"{0}" chromatic adaptation transform is invalid, ' + "it must be one of {1}!", + ) - if M is None: - raise KeyError( - '"{0}" chromatic adaptation transform is not defined! Supported ' - 'methods: "{1}".'.format(transform, - CHROMATIC_ADAPTATION_TRANSFORMS.keys())) + M = CHROMATIC_ADAPTATION_TRANSFORMS[transform] - rgb_w = np.einsum('...i,...ij->...j', XYZ_w, np.transpose(M)) - rgb_wr = np.einsum('...i,...ij->...j', XYZ_wr, np.transpose(M)) + RGB_w = np.einsum("...i,...ij->...j", XYZ_w, np.transpose(M)) + RGB_wr = np.einsum("...i,...ij->...j", XYZ_wr, np.transpose(M)) - D = rgb_wr / rgb_w + D = RGB_wr / RGB_w D = row_as_diagonal(D) @@ -123,33 +142,51 @@ def matrix_chromatic_adaptation_VonKries(XYZ_w, XYZ_wr, transform='CAT02'): return M_CAT -def chromatic_adaptation_VonKries(XYZ, XYZ_w, XYZ_wr, transform='CAT02'): +def chromatic_adaptation_VonKries( + XYZ: ArrayLike, + XYZ_w: ArrayLike, + XYZ_wr: ArrayLike, + transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", +) -> NDArray: """ - Adapts given stimulus from test viewing conditions to reference viewing + Adapt given stimulus from test viewing conditions to reference viewing conditions. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values of stimulus to adapt. - XYZ_w : array_like - Test viewing condition *CIE XYZ* tristimulus values of whitepoint. - XYZ_wr : array_like - Reference viewing condition *CIE XYZ* tristimulus values of whitepoint. - transform : unicode, optional - **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', - 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02 Brill 2008', - 'Bianco 2010', 'Bianco PC 2010'}**, + XYZ_w + Test viewing conditions *CIE XYZ* tristimulus values of whitepoint. + XYZ_wr + Reference viewing conditions *CIE XYZ* tristimulus values of + whitepoint. + transform Chromatic adaptation transform. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ_c* tristimulus values of the stimulus corresponding colour. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ diff --git a/colour/adaptation/zhai2018.py b/colour/adaptation/zhai2018.py new file mode 100644 index 0000000000..6f4f75c8ed --- /dev/null +++ b/colour/adaptation/zhai2018.py @@ -0,0 +1,173 @@ +""" +Zhai and Luo (2018) Chromatic Adaptation Model +============================================== + +Defines the *Zhai and Luo (2018)* chromatic adaptation model object: + +- :func:`colour.adaptation.chromatic_adaptation_Zhai2018` + +References +---------- +- :cite:`Zhai2018` : Zhai, Q., & Luo, M. R. (2018). Study of chromatic + adaptation via neutral white matches on different viewing media. Optics + Express, 26(6), 7724. doi:10.1364/OE.26.007724 +""" + +import numpy as np + +from colour.algebra import vector_dot +from colour.adaptation import CHROMATIC_ADAPTATION_TRANSFORMS +from colour.hints import ( + ArrayLike, + FloatingOrArrayLike, + Literal, + NDArray, + Union, +) +from colour.utilities import ( + as_float_array, + from_range_100, + to_domain_100, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "chromatic_adaptation_Zhai2018", +] + + +def chromatic_adaptation_Zhai2018( + XYZ_b: ArrayLike, + XYZ_wb: ArrayLike, + XYZ_wd: ArrayLike, + D_b: FloatingOrArrayLike = 1, + D_d: FloatingOrArrayLike = 1, + XYZ_wo: ArrayLike = np.array([1, 1, 1]), + transform: Union[Literal["CAT02", "CAT16"], str] = "CAT02", +) -> NDArray: + """ + Adapt given sample colour :math:`XYZ_{\\beta}` tristimulus values from + input viewing conditions under :math:`\\beta` illuminant to output viewing + conditions under :math:`\\delta` illuminant using *Zhai and Luo (2018)* + chromatic adaptation model. + + According to the definition of :math:`D`, a one-step CAT such as CAT02 can + only be used to transform colors from an incomplete adapted field into a + complete adapted field. When CAT02 are used to transform an incomplete to + incomplete case, :math:`D` has no baseline level to refer to. + *Smet et al. (2017)* proposed a new concept of two-step CAT to replace the + present CATs such as CAT02 with only one-step transform in order to define + :math:`D` more clearly. A two-step CAT involves an illuminant representing + the baseline states between the test and reference illuminants for the + calculation. In the first step the test color is transformed from test + illuminant to the baseline illuminant (:math:`BI`), and it is then + transformed to the reference illuminant Degrees of adaptation under the + other illuminants should be calculated relative to the adaptation under the + :math:`BI`. When :math:`D` becomes lower towards zero, the adaptation point + of the observer moves towards the :math:`BI`. Therefore, the chromaticity + of the :math:`BI` should be an intrinsic property of the human vision + system. + + Parameters + ---------- + XYZ_b + Sample colour :math:`XYZ_{\\beta}` under input illuminant + :math:`\\beta`. + XYZ_wb + Input illuminant :math:`\\beta`. + XYZ_wd + Output illuminant :math:`\\delta`. + D_b + Degree of adaptation :math:`D_{\\beta}` of input illuminant + :math:`\\beta`. + D_d + Degree of adaptation :math:`D_{\\delta}` of output illuminant + :math:`\\delta`. + XYZ_wo + Baseline illuminant (:math:`BI`) :math:`o`. + transform + Chromatic adaptation transform. + + Returns + ------- + :class:`numpy.ndarray` + Sample corresponding colour :math:`XYZ_{\\delta}` tristimulus values + under output illuminant :math:`D_{\\delta}`. + + Notes + ----- + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``XYZ_b`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + | ``XYZ_wb`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + | ``XYZ_wd`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + | ``XYZ_wo`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + +------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``XYZ_d`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + References + ---------- + :cite:`Zhai2018` + + Examples + -------- + >>> XYZ_b = np.array([48.900, 43.620, 6.250]) + >>> XYZ_wb = np.array([109.850, 100, 35.585]) + >>> XYZ_wd = np.array([95.047, 100, 108.883]) + >>> D_b = 0.9407 + >>> D_d = 0.9800 + >>> XYZ_wo = np.array([100, 100, 100]) + >>> chromatic_adaptation_Zhai2018( + ... XYZ_b, XYZ_wb, XYZ_wd, D_b, D_d, XYZ_wo) # doctest: +ELLIPSIS + array([ 39.1856164..., 42.1546179..., 19.2367203...]) + >>> XYZ_d = np.array([39.18561644, 42.15461798, 19.23672036]) + >>> chromatic_adaptation_Zhai2018( + ... XYZ_d, XYZ_wd, XYZ_wb, D_d, D_b, XYZ_wo) # doctest: +ELLIPSIS + array([ 48.9 , 43.62, 6.25]) + """ + + XYZ_b = to_domain_100(XYZ_b) + XYZ_wb = to_domain_100(XYZ_wb) + XYZ_wd = to_domain_100(XYZ_wd) + XYZ_wo = to_domain_100(XYZ_wo) + D_b = as_float_array(D_b) + D_d = as_float_array(D_d) + + Y_wb = XYZ_wb[..., 1][..., np.newaxis] + Y_wd = XYZ_wd[..., 1][..., np.newaxis] + Y_wo = XYZ_wo[..., 1][..., np.newaxis] + + transform = validate_method(transform, ["CAT02", "CAT16"]) + M = CHROMATIC_ADAPTATION_TRANSFORMS[transform] + + RGB_b = vector_dot(M, XYZ_b) + RGB_wb = vector_dot(M, XYZ_wb) + RGB_wd = vector_dot(M, XYZ_wd) + RGB_wo = vector_dot(M, XYZ_wo) + + D_RGB_b = D_b * (Y_wb / Y_wo) * (RGB_wo / RGB_wb) + 1 - D_b + D_RGB_d = D_d * (Y_wd / Y_wo) * (RGB_wo / RGB_wd) + 1 - D_d + + D_RGB = D_RGB_b / D_RGB_d + + RGB_d = D_RGB * RGB_b + + XYZ_d = vector_dot(np.linalg.inv(M), RGB_d) + + return from_range_100(XYZ_d) diff --git a/colour/algebra/__init__.py b/colour/algebra/__init__.py index 94dcdf1e25..38ef4b299e 100644 --- a/colour/algebra/__init__.py +++ b/colour/algebra/__init__.py @@ -1,52 +1,113 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .coordinates import * # noqa from . import coordinates -from .common import (is_spow_enabled, set_spow_enable, spow_enable, spow, - smoothstep_function) -from .extrapolation import Extrapolator +from .common import ( + is_spow_enabled, + set_spow_enable, + spow_enable, + spow, + normalise_maximum, + vector_dot, + matrix_dot, + linear_conversion, + linstep_function, + lerp, + smoothstep_function, + smooth, + is_identity, +) from .geometry import ( - normalise_vector, euclidean_distance, extend_line_segment, - LineSegmentsIntersections_Specification, intersect_line_segments, - ellipse_coefficients_general_form, ellipse_coefficients_canonical_form, - point_at_angle_on_ellipse, ellipse_fitting_Halir1998, - ELLIPSE_FITTING_METHODS, ellipse_fitting) + normalise_vector, + euclidean_distance, + manhattan_distance, + extend_line_segment, + LineSegmentsIntersections_Specification, + intersect_line_segments, + ellipse_coefficients_general_form, + ellipse_coefficients_canonical_form, + point_at_angle_on_ellipse, + ellipse_fitting_Halir1998, + ELLIPSE_FITTING_METHODS, + ellipse_fitting, +) from .interpolation import ( - kernel_nearest_neighbour, kernel_linear, kernel_sinc, kernel_lanczos, - kernel_cardinal_spline, KernelInterpolator, NearestNeighbourInterpolator, - LinearInterpolator, SpragueInterpolator, CubicSplineInterpolator, - PchipInterpolator, NullInterpolator, lagrange_coefficients, - table_interpolation_trilinear, table_interpolation_tetrahedral, - TABLE_INTERPOLATION_METHODS, table_interpolation) -from .matrix import is_identity + kernel_nearest_neighbour, + kernel_linear, + kernel_sinc, + kernel_lanczos, + kernel_cardinal_spline, + KernelInterpolator, + NearestNeighbourInterpolator, + LinearInterpolator, + SpragueInterpolator, + CubicSplineInterpolator, + PchipInterpolator, + NullInterpolator, + lagrange_coefficients, + table_interpolation_trilinear, + table_interpolation_tetrahedral, + TABLE_INTERPOLATION_METHODS, + table_interpolation, +) +from .extrapolation import Extrapolator from .random import random_triplet_generator from .regression import least_square_mapping_MoorePenrose __all__ = [] __all__ += coordinates.__all__ __all__ += [ - 'is_spow_enabled', 'set_spow_enable', 'spow_enable', 'spow', - 'smoothstep_function' + "is_spow_enabled", + "set_spow_enable", + "spow_enable", + "spow", + "normalise_maximum", + "vector_dot", + "matrix_dot", + "linear_conversion", + "linstep_function", + "lerp", + "smoothstep_function", + "smooth", + "is_identity", +] +__all__ += [ + "normalise_vector", + "euclidean_distance", + "manhattan_distance", + "extend_line_segment", + "LineSegmentsIntersections_Specification", + "intersect_line_segments", + "ellipse_coefficients_general_form", + "ellipse_coefficients_canonical_form", + "point_at_angle_on_ellipse", + "ellipse_fitting_Halir1998", + "ELLIPSE_FITTING_METHODS", + "ellipse_fitting", +] +__all__ += [ + "kernel_nearest_neighbour", + "kernel_linear", + "kernel_sinc", + "kernel_lanczos", + "kernel_cardinal_spline", + "KernelInterpolator", + "NearestNeighbourInterpolator", + "LinearInterpolator", + "SpragueInterpolator", + "CubicSplineInterpolator", + "PchipInterpolator", + "NullInterpolator", + "lagrange_coefficients", + "table_interpolation_trilinear", + "table_interpolation_tetrahedral", + "TABLE_INTERPOLATION_METHODS", + "table_interpolation", +] +__all__ += [ + "Extrapolator", ] -__all__ += ['Extrapolator'] __all__ += [ - 'normalise_vector', 'euclidean_distance', 'extend_line_segment', - 'LineSegmentsIntersections_Specification', 'intersect_line_segments', - 'ellipse_coefficients_general_form', 'ellipse_coefficients_canonical_form', - 'point_at_angle_on_ellipse', 'ellipse_fitting_Halir1998', - 'ELLIPSE_FITTING_METHODS', 'ellipse_fitting' + "random_triplet_generator", ] __all__ += [ - 'kernel_nearest_neighbour', 'kernel_linear', 'kernel_sinc', - 'kernel_lanczos', 'kernel_cardinal_spline', 'KernelInterpolator', - 'NearestNeighbourInterpolator', 'LinearInterpolator', - 'SpragueInterpolator', 'CubicSplineInterpolator', 'PchipInterpolator', - 'NullInterpolator', 'lagrange_coefficients', - 'table_interpolation_trilinear', 'table_interpolation_tetrahedral', - 'TABLE_INTERPOLATION_METHODS', 'table_interpolation' + "least_square_mapping_MoorePenrose", ] -__all__ += ['is_identity'] -__all__ += ['random_triplet_generator'] -__all__ += ['least_square_mapping_MoorePenrose'] diff --git a/colour/algebra/common.py b/colour/algebra/common.py index 9898e723ed..a9579c84a6 100644 --- a/colour/algebra/common.py +++ b/colour/algebra/common.py @@ -1,51 +1,68 @@ -# -*- coding: utf-8 -*- """ Common Utilities ================ -Defines common algebra utilities objects that don't fall in any specific -category: - -- :func:`colour.algebra.spow`: Safe (symmetrical) power. -- :func:`colour.algebra.smoothstep_function`: *Smoothstep* sigmoid-like - function. +Defines the common algebra utilities objects that don't fall in any specific +category. """ -from __future__ import division, unicode_literals +from __future__ import annotations import functools import numpy as np -from colour.utilities import as_float_array, as_float - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import ( + Any, + ArrayLike, + Boolean, + Callable, + Floating, + FloatingOrArrayLike, + FloatingOrNDArray, + Integer, + NDArray, + Optional, +) +from colour.utilities import as_float_array, as_float, tsplit + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'is_spow_enabled', 'set_spow_enable', 'spow_enable', 'spow', - 'smoothstep_function' + "is_spow_enabled", + "set_spow_enable", + "spow_enable", + "spow", + "normalise_maximum", + "vector_dot", + "matrix_dot", + "linear_conversion", + "linstep_function", + "lerp", + "smoothstep_function", + "smooth", + "is_identity", ] +# TODO: Annotate with "bool" when Python 3.7 is dropped. _SPOW_ENABLED = True """ Global variable storing the current *Colour* safe / symmetrical power function enabled state. - -_SPOW_ENABLED : bool """ -def is_spow_enabled(): +def is_spow_enabled() -> bool: """ - Returns whether *Colour* safe / symmetrical power function is enabled. + Return whether *Colour* safe / symmetrical power function is enabled. Returns ------- - bool + :class:`bool` Whether *Colour* safe / symmetrical power function is enabled. Examples @@ -61,13 +78,13 @@ def is_spow_enabled(): return _SPOW_ENABLED -def set_spow_enable(enable): +def set_spow_enable(enable: bool): """ - Sets *Colour* safe / symmetrical power function enabled state. + Set *Colour* safe / symmetrical power function enabled state. Parameters ---------- - enable : bool + enable Whether to enable *Colour* safe / symmetrical power function. Examples @@ -85,54 +102,54 @@ def set_spow_enable(enable): _SPOW_ENABLED = enable -class spow_enable(object): +class spow_enable: """ - A context manager and decorator temporarily setting *Colour* safe / + Define a context manager and decorator temporarily setting *Colour* safe / symmetrical power function enabled state. Parameters ---------- - enable : bool + enable Whether to enable or disable *Colour* safe / symmetrical power function. """ - def __init__(self, enable): + def __init__(self, enable: bool): self._enable = enable self._previous_state = is_spow_enabled() - def __enter__(self): + def __enter__(self) -> spow_enable: """ - Called upon entering the context manager and decorator. + Set the *Colour* safe / symmetrical power function enabled state + upon entering the context manager. """ set_spow_enable(self._enable) return self - def __exit__(self, *args): + def __exit__(self, *args: Any): """ - Called upon exiting the context manager and decorator. + Set the *Colour* safe / symmetrical power function enabled state + upon exiting the context manager. """ set_spow_enable(self._previous_state) - def __call__(self, function): - """ - Calls the wrapped definition. - """ + def __call__(self, function: Callable) -> Callable: + """Call the wrapped definition.""" @functools.wraps(function) - def wrapper(*args, **kwargs): + def wrapper(*args: Any, **kwargs: Any) -> Any: with self: return function(*args, **kwargs) return wrapper -def spow(a, p): +def spow(a: FloatingOrArrayLike, p: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Raises given array :math:`a` to the power :math:`p` as follows: + Raise given array :math:`a` to the power :math:`p` as follows: :math:`sign(a) * |a|^p`. This definition avoids NaNs generation when array :math:`a` is negative and @@ -141,15 +158,15 @@ def spow(a, p): the :func:`spow_enable` context manager. Parameters - ---------------- - a : numeric or array_like + ---------- + a Array :math:`a`. - p : numeric or array_like + p Power :math:`p`. Returns ------- - numeric or ndarray + :class:`np.floating` or :class:`numpy.ndarray` Array :math:`a` safely raised to the power :math:`p`. Examples @@ -175,24 +192,254 @@ def spow(a, p): return as_float(a_p) -def smoothstep_function(x, a=0, b=1, clip=False): +def normalise_maximum( + a: ArrayLike, + axis: Optional[Integer] = None, + factor: Floating = 1, + clip: Boolean = True, +) -> NDArray: """ - Evaluates the *smoothstep* sigmoid-like function on array :math:`x`. + Normalise given array :math:`a` values by :math:`a` maximum value and + optionally clip them between. Parameters ---------- - x : numeric or array_like + a + Array :math:`a` to normalise. + axis + Normalization axis. + factor + Normalization factor. + clip + Clip values to domain [0, 'factor']. + + Returns + ------- + :class:`numpy.ndarray` + Maximum normalised array :math:`a`. + + Examples + -------- + >>> a = np.array([0.48222001, 0.31654775, 0.22070353]) + >>> normalise_maximum(a) # doctest: +ELLIPSIS + array([ 1. , 0.6564384..., 0.4576822...]) + """ + + a = as_float_array(a) + + maximum = np.max(a, axis=axis) + a = a * (1 / maximum[..., np.newaxis]) * factor + + return np.clip(a, 0, factor) if clip else a + + +def vector_dot(m: ArrayLike, v: ArrayLike) -> NDArray: + """ + Perform the dot product of the matrix array :math:`m` with the vector + array :math:`v`. + + This definition is a convenient wrapper around :func:`np.einsum` with the + following subscripts: *'...ij,...j->...i'*. + + Parameters + ---------- + m + Matrix array :math:`m`. + v + Vector array :math:`v`. + + Returns + ------- + :class:`numpy.ndarray` + Transformed vector array :math:`v`. + + Examples + -------- + >>> m = np.array( + ... [[0.7328, 0.4296, -0.1624], + ... [-0.7036, 1.6975, 0.0061], + ... [0.0030, 0.0136, 0.9834]] + ... ) + >>> m = np.reshape(np.tile(m, (6, 1)), (6, 3, 3)) + >>> v = np.array([0.20654008, 0.12197225, 0.05136952]) + >>> v = np.tile(v, (6, 1)) + >>> vector_dot(m, v) # doctest: +ELLIPSIS + array([[ 0.1954094..., 0.0620396..., 0.0527952...], + [ 0.1954094..., 0.0620396..., 0.0527952...], + [ 0.1954094..., 0.0620396..., 0.0527952...], + [ 0.1954094..., 0.0620396..., 0.0527952...], + [ 0.1954094..., 0.0620396..., 0.0527952...], + [ 0.1954094..., 0.0620396..., 0.0527952...]]) + """ + + return np.einsum("...ij,...j->...i", as_float_array(m), as_float_array(v)) + + +def matrix_dot(a: ArrayLike, b: ArrayLike) -> NDArray: + """ + Perform the dot product of the matrix array :math:`a` with the matrix + array :math:`b`. + + This definition is a convenient wrapper around :func:`np.einsum` with the + following subscripts: *'...ij,...jk->...ik'*. + + Parameters + ---------- + a + Matrix array :math:`a`. + b + Matrix array :math:`b`. + + Returns + ------- + :class:`numpy.ndarray` + + Examples + -------- + >>> a = np.array( + ... [[0.7328, 0.4296, -0.1624], + ... [-0.7036, 1.6975, 0.0061], + ... [0.0030, 0.0136, 0.9834]] + ... ) + >>> a = np.reshape(np.tile(a, (6, 1)), (6, 3, 3)) + >>> b = a + >>> matrix_dot(a, b) # doctest: +ELLIPSIS + array([[[ 0.2342420..., 1.0418482..., -0.2760903...], + [-1.7099407..., 2.5793226..., 0.1306181...], + [-0.0044203..., 0.0377490..., 0.9666713...]], + + [[ 0.2342420..., 1.0418482..., -0.2760903...], + [-1.7099407..., 2.5793226..., 0.1306181...], + [-0.0044203..., 0.0377490..., 0.9666713...]], + + [[ 0.2342420..., 1.0418482..., -0.2760903...], + [-1.7099407..., 2.5793226..., 0.1306181...], + [-0.0044203..., 0.0377490..., 0.9666713...]], + + [[ 0.2342420..., 1.0418482..., -0.2760903...], + [-1.7099407..., 2.5793226..., 0.1306181...], + [-0.0044203..., 0.0377490..., 0.9666713...]], + + [[ 0.2342420..., 1.0418482..., -0.2760903...], + [-1.7099407..., 2.5793226..., 0.1306181...], + [-0.0044203..., 0.0377490..., 0.9666713...]], + + [[ 0.2342420..., 1.0418482..., -0.2760903...], + [-1.7099407..., 2.5793226..., 0.1306181...], + [-0.0044203..., 0.0377490..., 0.9666713...]]]) + """ + + return np.einsum( + "...ij,...jk->...ik", as_float_array(a), as_float_array(b) + ) + + +def linear_conversion( + a: ArrayLike, old_range: ArrayLike, new_range: ArrayLike +) -> NDArray: + """ + Perform a simple linear conversion of given array :math:`a` between the + old and new ranges. + + Parameters + ---------- + a + Array :math:`a` to perform the linear conversion onto. + old_range + Old range. + new_range + New range. + + Returns + ------- + :class:`numpy.ndarray` + Linear conversion result. + + Examples + -------- + >>> a = np.linspace(0, 1, 10) + >>> linear_conversion(a, np.array([0, 1]), np.array([1, 10])) + array([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.]) + """ + + a = as_float_array(a) + + in_min, in_max = tsplit(old_range) + out_min, out_max = tsplit(new_range) + + return ((a - in_min) / (in_max - in_min)) * (out_max - out_min) + out_min + + +def linstep_function( + x: FloatingOrArrayLike, + a: FloatingOrArrayLike = 0, + b: FloatingOrArrayLike = 1, + clip: Boolean = False, +) -> NDArray: + """ + Perform a simple linear interpolation between given array :math:`a` and + array :math:`b` using :math:`x` array. + + Parameters + ---------- + x + Array :math:`x` value to use to interpolate between array :math:`a` and + array :math:`b`. + a + Array :math:`a`, the start of the range in which to interpolate. + b + Array :math:`b`, the end of the range in which to interpolate. + clip + Whether to clip the output values to range [``a``, ``b``]. + + Returns + ------- + :class:`numpy.ndarray` + Linear interpolation result. + + Examples + -------- + >>> a = 0 + >>> b = 2 + >>> linstep_function(0.5, a, b) + 1.0 + """ + + x = as_float_array(x) + a = as_float_array(a) + b = as_float_array(b) + + y = (1 - x) * a + x * b + + return np.clip(y, a, b) if clip else y + + +lerp = linstep_function + + +def smoothstep_function( + x: FloatingOrArrayLike, + a: FloatingOrArrayLike = 0, + b: FloatingOrArrayLike = 1, + clip: Boolean = False, +) -> NDArray: + """ + Evaluate the *smoothstep* sigmoid-like function on array :math:`x`. + + Parameters + ---------- + x Array :math:`x`. - a : numeric, optional + a Low input domain limit, i.e. the left edge. - b : numeric, optional + b High input domain limit, i.e. the right edge. - clip : bool, optional - Whether to scale, bias and clip input values to domain [0, 1]. + clip + Whether to scale, bias and clip input values to domain [``a``, ``b``]. Returns ------- - array_like + :class:`numpy.ndarray` Array :math:`x` after *smoothstep* sigmoid-like function evaluation. Examples @@ -203,7 +450,37 @@ def smoothstep_function(x, a=0, b=1, clip=False): """ x = as_float_array(x) + a = as_float_array(a) + b = as_float_array(b) i = np.clip((x - a) / (b - a), 0, 1) if clip else x - return (i ** 2) * (3 - 2 * i) + return (i**2) * (3 - 2 * i) + + +smooth = smoothstep_function + + +def is_identity(a: ArrayLike) -> Boolean: + """ + Return whether :math:`a` array is an identity matrix. + + Parameters + ---------- + a + Array :math:`a` to test. + + Returns + ------- + :class:`bool` + Whether :math:`a` array is an identity matrix. + + Examples + -------- + >>> is_identity(np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]).reshape(3, 3)) + True + >>> is_identity(np.array([1, 2, 0, 0, 1, 0, 0, 0, 1]).reshape(3, 3)) + False + """ + + return np.array_equal(np.identity(len(np.diag(a))), a) diff --git a/colour/algebra/coordinates/__init__.py b/colour/algebra/coordinates/__init__.py index 481769ee86..e74ed73d47 100644 --- a/colour/algebra/coordinates/__init__.py +++ b/colour/algebra/coordinates/__init__.py @@ -1,13 +1,17 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .transformations import ( - cartesian_to_spherical, spherical_to_cartesian, cartesian_to_polar, - polar_to_cartesian, cartesian_to_cylindrical, cylindrical_to_cartesian) + cartesian_to_spherical, + spherical_to_cartesian, + cartesian_to_polar, + polar_to_cartesian, + cartesian_to_cylindrical, + cylindrical_to_cartesian, +) __all__ = [ - 'cartesian_to_spherical', 'spherical_to_cartesian', 'cartesian_to_polar', - 'polar_to_cartesian', 'cartesian_to_cylindrical', - 'cylindrical_to_cartesian' + "cartesian_to_spherical", + "spherical_to_cartesian", + "cartesian_to_polar", + "polar_to_cartesian", + "cartesian_to_cylindrical", + "cylindrical_to_cartesian", ] diff --git a/colour/algebra/coordinates/tests/__init__.py b/colour/algebra/coordinates/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/algebra/coordinates/tests/__init__.py +++ b/colour/algebra/coordinates/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/algebra/coordinates/tests/test_transformations.py b/colour/algebra/coordinates/tests/test_transformations.py index c7515eaf08..7baea7ce0b 100644 --- a/colour/algebra/coordinates/tests/test_transformations.py +++ b/colour/algebra/coordinates/tests/test_transformations.py @@ -1,64 +1,72 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.algebra.coordinates.transformations` -module. +Defines the unit tests for the +:mod:`colour.algebra.coordinates.transformations` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from itertools import permutations -from colour.algebra import (cartesian_to_spherical, spherical_to_cartesian, - cartesian_to_polar, polar_to_cartesian, - cartesian_to_cylindrical, cylindrical_to_cartesian) +from colour.algebra import ( + cartesian_to_spherical, + spherical_to_cartesian, + cartesian_to_polar, + polar_to_cartesian, + cartesian_to_cylindrical, + cylindrical_to_cartesian, +) from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestCartesianToSpherical', 'TestSphericalToCartesian', - 'TestCartesianToPolar', 'TestPolarToCartesian', - 'TestCartesianToCylindrical', 'TestCylindricalToCartesian' + "TestCartesianToSpherical", + "TestSphericalToCartesian", + "TestCartesianToPolar", + "TestPolarToCartesian", + "TestCartesianToCylindrical", + "TestCylindricalToCartesian", ] class TestCartesianToSpherical(unittest.TestCase): """ - Defines :func:`colour.algebra.coordinates.transformations.\ + Define :func:`colour.algebra.coordinates.transformations.\ cartesian_to_spherical` definition unit tests methods. """ def test_cartesian_to_spherical(self): """ - Tests :func:`colour.algebra.coordinates.transformations.\ + Test :func:`colour.algebra.coordinates.transformations.\ cartesian_to_spherical` definition. """ np.testing.assert_almost_equal( cartesian_to_spherical(np.array([3, 1, 6])), np.array([6.78232998, 0.48504979, 0.32175055]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( cartesian_to_spherical(np.array([-1, 9, 16])), np.array([18.38477631, 0.51501513, 1.68145355]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( cartesian_to_spherical(np.array([6.3434, -0.9345, 18.5675])), np.array([19.64342307, 0.33250603, -0.14626640]), - decimal=7) + decimal=7, + ) def test_n_dimensional_cartesian_to_spherical(self): """ - Tests :func:`colour.algebra.coordinates.transformations.\ + Test :func:`colour.algebra.coordinates.transformations.\ cartesian_to_spherical` definition n-dimensional arrays support. """ @@ -68,17 +76,19 @@ def test_n_dimensional_cartesian_to_spherical(self): a_i = np.tile(a_i, (6, 1)) a_o = np.tile(a_o, (6, 1)) np.testing.assert_almost_equal( - cartesian_to_spherical(a_i), a_o, decimal=7) + cartesian_to_spherical(a_i), a_o, decimal=7 + ) a_i = np.reshape(a_i, (2, 3, 3)) a_o = np.reshape(a_o, (2, 3, 3)) np.testing.assert_almost_equal( - cartesian_to_spherical(a_i), a_o, decimal=7) + cartesian_to_spherical(a_i), a_o, decimal=7 + ) @ignore_numpy_errors def test_nan_cartesian_to_spherical(self): """ - Tests :func:`colour.algebra.coordinates.transformations.\ + Test :func:`colour.algebra.coordinates.transformations.\ cartesian_to_spherical` definition nan support. """ @@ -91,37 +101,43 @@ def test_nan_cartesian_to_spherical(self): class TestSphericalToCartesian(unittest.TestCase): """ - Defines :func:`colour.algebra.coordinates.transformations.\ + Define :func:`colour.algebra.coordinates.transformations.\ spherical_to_cartesian` definition unit tests methods. """ def test_spherical_to_cartesian(self): """ - Tests :func:`colour.algebra.coordinates.transformations.\ + Test :func:`colour.algebra.coordinates.transformations.\ spherical_to_cartesian` definition. """ np.testing.assert_almost_equal( spherical_to_cartesian( - np.array([6.78232998, 0.48504979, 0.32175055])), + np.array([6.78232998, 0.48504979, 0.32175055]) + ), np.array([3.00000000, 0.99999999, 6.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( spherical_to_cartesian( - np.array([18.38477631, 0.51501513, 1.68145355])), + np.array([18.38477631, 0.51501513, 1.68145355]) + ), np.array([-1.00000003, 9.00000007, 15.99999996]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( spherical_to_cartesian( - np.array([19.64342307, 0.33250603, -0.14626640])), + np.array([19.64342307, 0.33250603, -0.14626640]) + ), np.array([6.34339996, -0.93449999, 18.56750001]), - decimal=7) + decimal=7, + ) def test_n_dimensional_spherical_to_cartesian(self): """ - Tests :func:`colour.algebra.coordinates.transformations.\ + Test :func:`colour.algebra.coordinates.transformations.\ spherical_to_cartesian` definition n-dimensional arrays support. """ @@ -131,17 +147,19 @@ def test_n_dimensional_spherical_to_cartesian(self): a_i = np.tile(a_i, (6, 1)) a_o = np.tile(a_o, (6, 1)) np.testing.assert_almost_equal( - spherical_to_cartesian(a_i), a_o, decimal=7) + spherical_to_cartesian(a_i), a_o, decimal=7 + ) a_i = np.reshape(a_i, (2, 3, 3)) a_o = np.reshape(a_o, (2, 3, 3)) np.testing.assert_almost_equal( - spherical_to_cartesian(a_i), a_o, decimal=7) + spherical_to_cartesian(a_i), a_o, decimal=7 + ) @ignore_numpy_errors def test_nan_spherical_to_cartesian(self): """ - Tests :func:`colour.algebra.coordinates.transformations.\ + Test :func:`colour.algebra.coordinates.transformations.\ spherical_to_cartesian` definition nan support. """ @@ -154,34 +172,37 @@ def test_nan_spherical_to_cartesian(self): class TestCartesianToPolar(unittest.TestCase): """ - Defines :func:`colour.algebra.coordinates.transformations.\ + Define :func:`colour.algebra.coordinates.transformations.\ cartesian_to_polar` definition unit tests methods. """ def test_cartesian_to_polar(self): """ - Tests :func:`colour.algebra.coordinates.transformations.\ + Test :func:`colour.algebra.coordinates.transformations.\ cartesian_to_polar` definition. """ np.testing.assert_almost_equal( cartesian_to_polar(np.array([3, 1])), np.array([3.16227766, 0.32175055]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( cartesian_to_polar(np.array([-1, 9])), np.array([9.05538514, 1.68145355]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( cartesian_to_polar(np.array([6.3434, -0.9345])), np.array([6.41186508, -0.14626640]), - decimal=7) + decimal=7, + ) def test_n_dimensional_cartesian_to_polar(self): """ - Tests :func:`colour.algebra.coordinates.transformations.\ + Test :func:`colour.algebra.coordinates.transformations.\ cartesian_to_polar` definition n-dimensional arrays support. """ @@ -199,7 +220,7 @@ def test_n_dimensional_cartesian_to_polar(self): @ignore_numpy_errors def test_nan_cartesian_to_polar(self): """ - Tests :func:`colour.algebra.coordinates.transformations.\ + Test :func:`colour.algebra.coordinates.transformations.\ cartesian_to_polar` definition nan support. """ @@ -212,34 +233,37 @@ def test_nan_cartesian_to_polar(self): class TestPolarToCartesian(unittest.TestCase): """ - Defines :func:`colour.algebra.coordinates.transformations.\ + Define :func:`colour.algebra.coordinates.transformations.\ polar_to_cartesian` definition unit tests methods. """ def test_polar_to_cartesian(self): """ - Tests :func:`colour.algebra.coordinates.transformations.\ + Test :func:`colour.algebra.coordinates.transformations.\ polar_to_cartesian` definition. """ np.testing.assert_almost_equal( polar_to_cartesian(np.array([0.32175055, 1.08574654])), np.array([0.15001697, 0.28463718]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( polar_to_cartesian(np.array([1.68145355, 1.05578119])), np.array([0.82819662, 1.46334425]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( polar_to_cartesian(np.array([-0.14626640, 1.23829030])), np.array([-0.04774323, -0.13825500]), - decimal=7) + decimal=7, + ) def test_n_dimensional_polar_to_cartesian(self): """ - Tests :func:`colour.algebra.coordinates.transformations.\ + Test :func:`colour.algebra.coordinates.transformations.\ polar_to_cartesian` definition n-dimensional arrays support. """ @@ -257,7 +281,7 @@ def test_n_dimensional_polar_to_cartesian(self): @ignore_numpy_errors def test_nan_polar_to_cartesian(self): """ - Tests :func:`colour.algebra.coordinates.transformations.\ + Test :func:`colour.algebra.coordinates.transformations.\ polar_to_cartesian` definition nan support. """ @@ -270,34 +294,37 @@ def test_nan_polar_to_cartesian(self): class TestCartesianToCylindrical(unittest.TestCase): """ - Defines :func:`colour.algebra.coordinates.transformations.\ + Define :func:`colour.algebra.coordinates.transformations.\ cartesian_to_cylindrical` definition unit tests methods. """ def test_cartesian_to_cylindrical(self): """ - Tests :func:`colour.algebra.coordinates.transformations.\ + Test :func:`colour.algebra.coordinates.transformations.\ cartesian_to_cylindrical` definition. """ np.testing.assert_almost_equal( cartesian_to_cylindrical(np.array([3, 1, 6])), np.array([3.16227766, 0.32175055, 6.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( cartesian_to_cylindrical(np.array([-1, 9, 16])), np.array([9.05538514, 1.68145355, 16.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( cartesian_to_cylindrical(np.array([6.3434, -0.9345, 18.5675])), np.array([6.41186508, -0.14626640, 18.56750000]), - decimal=7) + decimal=7, + ) def test_n_dimensional_cartesian_to_cylindrical(self): """ - Tests :func:`colour.algebra.coordinates.transformations.\ + Test :func:`colour.algebra.coordinates.transformations.\ cartesian_to_cylindrical` definition n-dimensional arrays support. """ @@ -307,17 +334,19 @@ def test_n_dimensional_cartesian_to_cylindrical(self): a_i = np.tile(a_i, (6, 1)) a_o = np.tile(a_o, (6, 1)) np.testing.assert_almost_equal( - cartesian_to_cylindrical(a_i), a_o, decimal=7) + cartesian_to_cylindrical(a_i), a_o, decimal=7 + ) a_i = np.reshape(a_i, (2, 3, 3)) a_o = np.reshape(a_o, (2, 3, 3)) np.testing.assert_almost_equal( - cartesian_to_cylindrical(a_i), a_o, decimal=7) + cartesian_to_cylindrical(a_i), a_o, decimal=7 + ) @ignore_numpy_errors def test_nan_cartesian_to_cylindrical(self): """ - Tests :func:`colour.algebra.coordinates.transformations.\ + Test :func:`colour.algebra.coordinates.transformations.\ cartesian_to_cylindrical` definition nan support. """ @@ -330,37 +359,43 @@ def test_nan_cartesian_to_cylindrical(self): class TestCylindricalToCartesian(unittest.TestCase): """ - Defines :func:`colour.algebra.coordinates.transformations.\ + Define :func:`colour.algebra.coordinates.transformations.\ cylindrical_to_cartesian` definition unit tests methods. """ def test_cylindrical_to_cartesian(self): """ - Tests :func:`colour.algebra.coordinates.transformations.\ + Test :func:`colour.algebra.coordinates.transformations.\ cylindrical_to_cartesian` definition. """ np.testing.assert_almost_equal( cylindrical_to_cartesian( - np.array([0.32175055, 1.08574654, 6.78232998])), + np.array([0.32175055, 1.08574654, 6.78232998]) + ), np.array([0.15001697, 0.28463718, 6.78232998]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( cylindrical_to_cartesian( - np.array([1.68145355, 1.05578119, 18.38477631])), + np.array([1.68145355, 1.05578119, 18.38477631]) + ), np.array([0.82819662, 1.46334425, 18.38477631]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( cylindrical_to_cartesian( - np.array([-0.14626640, 1.23829030, 19.64342307])), + np.array([-0.14626640, 1.23829030, 19.64342307]) + ), np.array([-0.04774323, -0.13825500, 19.64342307]), - decimal=7) + decimal=7, + ) def test_n_dimensional_cylindrical_to_cartesian(self): """ - Tests :func:`colour.algebra.coordinates.transformations.\ + Test :func:`colour.algebra.coordinates.transformations.\ cylindrical_to_cartesian` definition n-dimensional arrays support. """ @@ -370,17 +405,19 @@ def test_n_dimensional_cylindrical_to_cartesian(self): a_i = np.tile(a_i, (6, 1)) a_o = np.tile(a_o, (6, 1)) np.testing.assert_almost_equal( - cylindrical_to_cartesian(a_i), a_o, decimal=7) + cylindrical_to_cartesian(a_i), a_o, decimal=7 + ) a_i = np.reshape(a_i, (2, 3, 3)) a_o = np.reshape(a_o, (2, 3, 3)) np.testing.assert_almost_equal( - cylindrical_to_cartesian(a_i), a_o, decimal=7) + cylindrical_to_cartesian(a_i), a_o, decimal=7 + ) @ignore_numpy_errors def test_nan_cylindrical_to_cartesian(self): """ - Tests :func:`colour.algebra.coordinates.transformations.\ + Test :func:`colour.algebra.coordinates.transformations.\ cylindrical_to_cartesian` definition nan support. """ @@ -391,5 +428,5 @@ def test_nan_cylindrical_to_cartesian(self): cylindrical_to_cartesian(a_i) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/algebra/coordinates/transformations.py b/colour/algebra/coordinates/transformations.py index a693923d44..c583e1097a 100644 --- a/colour/algebra/coordinates/transformations.py +++ b/colour/algebra/coordinates/transformations.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Coordinates System Transformations ================================== -Defines objects to apply transformations on coordinates systems. +Defines the objects to apply transformations on coordinates systems. The following transformations are available: @@ -29,40 +28,44 @@ http://en.wikipedia.org/wiki/List_of_common_coordinate_transformations """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +from colour.hints import ArrayLike, NDArray from colour.utilities import as_float_array, tsplit, tstack -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'cartesian_to_spherical', 'spherical_to_cartesian', 'cartesian_to_polar', - 'polar_to_cartesian', 'cartesian_to_cylindrical', - 'cylindrical_to_cartesian' + "cartesian_to_spherical", + "spherical_to_cartesian", + "cartesian_to_polar", + "polar_to_cartesian", + "cartesian_to_cylindrical", + "cylindrical_to_cartesian", ] -def cartesian_to_spherical(a): +def cartesian_to_spherical(a: ArrayLike) -> NDArray: """ - Transforms given cartesian coordinates array :math:`xyz` to spherical + Transform given cartesian coordinates array :math:`xyz` to spherical coordinates array :math:`\\rho\\theta\\phi` (radial distance, inclination or elevation and azimuth). Parameters ---------- - a : array_like + a Cartesian coordinates array :math:`xyz` to transform. Returns ------- - ndarray + :class:`numpy.ndarray` Spherical coordinates array :math:`\\rho\\theta\\phi`, :math:`\\rho` is in range [0, +inf], :math:`\\theta` is in range [0, pi] radians, i.e. [0, 180] degrees, and :math:`\\phi` is in range [-pi, pi] radians, i.e. @@ -90,15 +93,15 @@ def cartesian_to_spherical(a): return rtp -def spherical_to_cartesian(a): +def spherical_to_cartesian(a: ArrayLike) -> NDArray: """ - Transforms given spherical coordinates array :math:`\\rho\\theta\\phi` + Transform given spherical coordinates array :math:`\\rho\\theta\\phi` (radial distance, inclination or elevation and azimuth) to cartesian coordinates array :math:`xyz`. Parameters ---------- - a : array_like + a Spherical coordinates array :math:`\\rho\\theta\\phi` to transform, :math:`\\rho` is in range [0, +inf], :math:`\\theta` is in range [0, pi] radians, i.e. [0, 180] degrees, and :math:`\\phi` is in range @@ -106,7 +109,7 @@ def spherical_to_cartesian(a): Returns ------- - ndarray + :class:`numpy.ndarray` Cartesian coordinates array :math:`xyz`. References @@ -131,20 +134,20 @@ def spherical_to_cartesian(a): return xyz -def cartesian_to_polar(a): +def cartesian_to_polar(a: ArrayLike) -> NDArray: """ - Transforms given cartesian coordinates array :math:`xy` to polar + Transform given cartesian coordinates array :math:`xy` to polar coordinates array :math:`\\rho\\phi` (radial coordinate, angular coordinate). Parameters ---------- - a : array_like + a Cartesian coordinates array :math:`xy` to transform. Returns ------- - ndarray + :class:`numpy.ndarray` Polar coordinates array :math:`\\rho\\phi`, :math:`\\rho` is in range [0, +inf], :math:`\\phi` is in range [-pi, pi] radians, i.e. [-180, 180] degrees. @@ -168,21 +171,21 @@ def cartesian_to_polar(a): return tstack([rho, phi]) -def polar_to_cartesian(a): +def polar_to_cartesian(a: ArrayLike) -> NDArray: """ - Transforms given polar coordinates array :math:`\\rho\\phi` (radial + Transform given polar coordinates array :math:`\\rho\\phi` (radial coordinate, angular coordinate) to cartesian coordinates array :math:`xy`. Parameters ---------- - a : array_like + a Polar coordinates array :math:`\\rho\\phi` to transform, :math:`\\rho` is in range [0, +inf], :math:`\\phi` is in range [-pi, pi] radians i.e. [-180, 180] degrees. Returns ------- - ndarray + :class:`numpy.ndarray` Cartesian coordinates array :math:`xy`. References @@ -204,20 +207,20 @@ def polar_to_cartesian(a): return tstack([x, y]) -def cartesian_to_cylindrical(a): +def cartesian_to_cylindrical(a: ArrayLike) -> NDArray: """ - Transforms given cartesian coordinates array :math:`xyz` to cylindrical + Transform given cartesian coordinates array :math:`xyz` to cylindrical coordinates array :math:`\\rho\\phi z` (radial distance, azimuth and height). Parameters ---------- - a : array_like + a Cartesian coordinates array :math:`xyz` to transform. Returns ------- - ndarray + :class:`numpy.ndarray` Cylindrical coordinates array :math:`\\rho\\phi z`, :math:`\\rho` is in range [0, +inf], :math:`\\phi` is in range [-pi, pi] radians i.e. [-180, 180] degrees, :math:`z` is in range [0, +inf]. @@ -240,15 +243,15 @@ def cartesian_to_cylindrical(a): return tstack([rho, phi, a[..., -1]]) -def cylindrical_to_cartesian(a): +def cylindrical_to_cartesian(a: ArrayLike) -> NDArray: """ - Transforms given cylindrical coordinates array :math:`\\rho\\phi z` + Transform given cylindrical coordinates array :math:`\\rho\\phi z` (radial distance, azimuth and height) to cartesian coordinates array :math:`xyz`. Parameters ---------- - a : array_like + a Cylindrical coordinates array :math:`\\rho\\phi z` to transform, :math:`\\rho` is in range [0, +inf], :math:`\\phi` is in range [-pi, pi] radians i.e. [-180, 180] degrees, :math:`z` is in range @@ -256,7 +259,7 @@ def cylindrical_to_cartesian(a): Returns ------- - ndarray + :class:`numpy.ndarray` Cartesian coordinates array :math:`xyz`. References diff --git a/colour/algebra/extrapolation.py b/colour/algebra/extrapolation.py index 06b71adf82..5e02bc2d99 100644 --- a/colour/algebra/extrapolation.py +++ b/colour/algebra/extrapolation.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Extrapolation ============= -Defines classes for extrapolating variables: +Defines the classes for extrapolating variables: - :class:`colour.Extrapolator`: 1-D function extrapolation. @@ -17,26 +16,49 @@ ed., p. 38). ISBN:978-0-470-66569-5 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +from colour.algebra import NullInterpolator from colour.constants import DEFAULT_FLOAT_DTYPE -from colour.utilities import as_float, is_numeric, is_string - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['Extrapolator'] - - -class Extrapolator(object): +from colour.hints import ( + DTypeNumber, + FloatingOrArrayLike, + FloatingOrNDArray, + Literal, + NDArray, + Number, + Optional, + Type, + TypeInterpolator, + Union, + cast, +) +from colour.utilities import ( + as_float, + attest, + is_numeric, + is_string, + optional, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "Extrapolator", +] + + +class Extrapolator: """ - Extrapolates the 1-D function of given interpolator. + Extrapolate the 1-D function of given interpolator. The :class:`colour.Extrapolator` class acts as a wrapper around a given *Colour* or *scipy* interpolator class instance with compatible signature. @@ -54,16 +76,15 @@ class Extrapolator(object): Parameters ---------- - interpolator : object + interpolator Interpolator object. - method : unicode, optional - **{'Linear', 'Constant'}**, + method Extrapolation method. - left : numeric, optional + left Value to return for x < xi[0]. - right : numeric, optional + right Value to return for x > xi[-1]. - dtype : type + dtype Data type used for internal conversions. Methods @@ -73,7 +94,7 @@ class Extrapolator(object): Notes ----- - - The interpolator must define *x* and *y* attributes. + - The interpolator must define ``x`` and ``y`` properties. References ---------- @@ -91,7 +112,7 @@ class Extrapolator(object): >>> extrapolator(1) -1.0 - Extrapolating an *array_like* variable: + Extrapolating an `ArrayLike` variable: >>> extrapolator(np.array([6, 7 , 8])) array([ 4., 5., 6.]) @@ -115,164 +136,171 @@ class Extrapolator(object): array([ 0., 0., 3., 3.]) """ - def __init__(self, - interpolator=None, - method='Linear', - left=None, - right=None, - dtype=None): - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE - - self._interpolator = None - self.interpolator = interpolator - self._method = None - self.method = method - self._right = None + def __init__( + self, + interpolator: Optional[TypeInterpolator] = None, + method: Union[Literal["Linear", "Constant"], str] = "Linear", + left: Optional[Number] = None, + right: Optional[Number] = None, + dtype: Optional[Type[DTypeNumber]] = None, + ): + dtype = cast(Type[DTypeNumber], optional(dtype, DEFAULT_FLOAT_DTYPE)) + + self._interpolator: TypeInterpolator = NullInterpolator( + np.array([-np.inf, np.inf]), np.array([-np.inf, np.inf]) + ) + self.interpolator = optional(interpolator, self._interpolator) + self._method: Union[Literal["Linear", "Constant"], str] = "Linear" + self.method = cast( + Union[Literal["Linear", "Constant"], str], + optional(method, self._method), + ) + self._right: Optional[Number] = None self.right = right - self._left = None + self._left: Optional[Number] = None self.left = left - self._dtype = dtype + self._dtype: Type[DTypeNumber] = dtype @property - def interpolator(self): + def interpolator(self) -> TypeInterpolator: """ Getter and setter property for the *Colour* or *scipy* interpolator class instance. Parameters ---------- - value : callable + value Value to set the *Colour* or *scipy* interpolator class instance with. Returns ------- - callable + TypeInterpolator *Colour* or *scipy* interpolator class instance. """ return self._interpolator @interpolator.setter - def interpolator(self, value): - """ - Setter for the **self.interpolator** property. - """ + def interpolator(self, value: TypeInterpolator): + """Setter for the **self.interpolator** property.""" - if value is not None: - assert hasattr(value, 'x'), ( - '"{0}" interpolator has no "x" attribute!'.format(value)) - assert hasattr(value, 'y'), ( - '"{0}" interpolator has no "y" attribute!'.format(value)) + attest( + hasattr(value, "x"), + f'"{value}" interpolator has no "x" attribute!', + ) + + attest( + hasattr(value, "y"), + f'"{value}" interpolator has no "y" attribute!', + ) self._interpolator = value @property - def method(self): + def method(self) -> Union[Literal["Linear", "Constant"], str]: """ Getter and setter property for the extrapolation method. Parameters ---------- - value : unicode + value Value to set the extrapolation method. with. Returns ------- - unicode + :class:`str` Extrapolation method. """ return self._method @method.setter - def method(self, value): - """ - Setter for the **self.method** property. - """ + def method(self, value: Union[Literal["Linear", "Constant"], str]): + """Setter for the **self.method** property.""" - if value is not None: - assert is_string(value), ( - ('"{0}" attribute: "{1}" is not a "string" like object!' - ).format('method', value)) - value = value.lower() + attest( + is_string(value), + f'"method" property: "{value}" type is not "str"!', + ) + + value = validate_method(value, ["Linear", "Constant"]) self._method = value @property - def left(self): + def left(self) -> Optional[Number]: """ Getter and setter property for left value to return for x < xi[0]. Parameters ---------- - value : numeric + value Left value to return for x < xi[0]. Returns ------- - numeric + :py:data:`None` or Number Left value to return for x < xi[0]. """ return self._left @left.setter - def left(self, value): - """ - Setter for the **self.left** property. - """ + def left(self, value: Optional[Number]): + """Setter for the **self.left** property.""" if value is not None: - assert is_numeric(value), ( - '"{0}" attribute: "{1}" is not a "numeric"!').format( - 'left', value) - self._left = value + attest( + is_numeric(value), + f'"left" property: "{value}" is not a "number"!', + ) + + self._left = value @property - def right(self): + def right(self) -> Optional[Number]: """ Getter and setter property for right value to return for x > xi[-1]. Parameters ---------- - value : numeric + value Right value to return for x > xi[-1]. Returns ------- - numeric + :py:data:`None` or Number Right value to return for x > xi[-1]. """ return self._right @right.setter - def right(self, value): - """ - Setter for the **self.right** property. - """ + def right(self, value: Optional[Number]): + """Setter for the **self.right** property.""" if value is not None: - assert is_numeric(value), ( - '"{0}" attribute: "{1}" is not a "numeric"!').format( - 'right', value) - self._right = value + attest( + is_numeric(value), + f'"right" property: "{value}" is not a "number"!', + ) + + self._right = value - def __call__(self, x): + def __call__(self, x: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Evaluates the Extrapolator at given point(s). + Evaluate the Extrapolator at given point(s). Parameters ---------- - x : numeric or array_like + x Point(s) to evaluate the Extrapolator at. Returns ------- - float or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Extrapolated points value(s). """ @@ -282,18 +310,18 @@ def __call__(self, x): return xe - def _evaluate(self, x): + def _evaluate(self, x: NDArray) -> NDArray: """ - Performs the extrapolating evaluation at given points. + Perform the extrapolating evaluation at given points. Parameters ---------- - x : ndarray + x Points to evaluate the Extrapolator at. Returns ------- - ndarray + :class:`numpy.ndarray` Extrapolated points values. """ @@ -302,12 +330,14 @@ def _evaluate(self, x): y = np.empty_like(x) - if self._method == 'linear': - y[x < xi[0]] = (yi[0] + (x[x < xi[0]] - xi[0]) * (yi[1] - yi[0]) / - (xi[1] - xi[0])) - y[x > xi[-1]] = (yi[-1] + (x[x > xi[-1]] - xi[-1]) * - (yi[-1] - yi[-2]) / (xi[-1] - xi[-2])) - elif self._method == 'constant': + if self._method == "linear": + y[x < xi[0]] = yi[0] + (x[x < xi[0]] - xi[0]) * (yi[1] - yi[0]) / ( + xi[1] - xi[0] + ) + y[x > xi[-1]] = yi[-1] + (x[x > xi[-1]] - xi[-1]) * ( + yi[-1] - yi[-2] + ) / (xi[-1] - xi[-2]) + elif self._method == "constant": y[x < xi[0]] = yi[0] y[x > xi[-1]] = yi[-1] diff --git a/colour/algebra/geometry.py b/colour/algebra/geometry.py index 7f32407107..faf9b315a0 100644 --- a/colour/algebra/geometry.py +++ b/colour/algebra/geometry.py @@ -1,13 +1,14 @@ -# -*- coding: utf-8 -*- """ Geometry ======== -Defines objects related to geometrical computations: +Defines the objects related to geometrical computations: - :func:`colour.algebra.normalise_vector` - :func:`colour.algebra.euclidean_distance` +- :func:`colour.algebra.manhattan_distance` - :func:`colour.algebra.extend_line_segment` +- :class:`colour.algebra.LineSegmentsIntersections_Specification` - :func:`colour.algebra.intersect_line_segments` - :func:`colour.algebra.ellipse_coefficients_general_form` - :func:`colour.algebra.ellipse_coefficients_canonical_form` @@ -35,42 +36,64 @@ 2018, from https://en.wikipedia.org/wiki/Ellipse """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from collections import namedtuple - -from colour.utilities import (CaseInsensitiveMapping, as_float_array, ones, - tsplit, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from dataclasses import dataclass + +from colour.hints import ( + ArrayLike, + Floating, + FloatingOrNDArray, + Literal, + NDArray, + Union, +) +from colour.utilities import ( + CaseInsensitiveMapping, + as_float, + as_float_array, + ones, + tsplit, + tstack, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'normalise_vector', 'euclidean_distance', 'extend_line_segment', - 'LineSegmentsIntersections_Specification', 'intersect_line_segments', - 'ellipse_coefficients_general_form', 'ellipse_coefficients_canonical_form', - 'point_at_angle_on_ellipse', 'ellipse_fitting_Halir1998', - 'ELLIPSE_FITTING_METHODS', 'ellipse_fitting' + "normalise_vector", + "euclidean_distance", + "manhattan_distance", + "extend_line_segment", + "LineSegmentsIntersections_Specification", + "intersect_line_segments", + "ellipse_coefficients_general_form", + "ellipse_coefficients_canonical_form", + "point_at_angle_on_ellipse", + "ellipse_fitting_Halir1998", + "ELLIPSE_FITTING_METHODS", + "ellipse_fitting", ] -def normalise_vector(a): +def normalise_vector(a: ArrayLike) -> NDArray: """ - Normalises given vector :math:`a`. + Normalise given vector :math:`a`. Parameters ---------- - a : array_like + a Vector :math:`a` to normalise. Returns ------- - ndarray + :class:`numpy.ndarray` Normalised vector :math:`a`. Examples @@ -80,25 +103,31 @@ def normalise_vector(a): array([ 0.8419703..., 0.4972256..., 0.2094102...]) """ + a = as_float_array(a) + return a / np.linalg.norm(a) -def euclidean_distance(a, b): +def euclidean_distance(a: ArrayLike, b: ArrayLike) -> FloatingOrNDArray: """ - Returns the euclidean distance between point arrays :math:`a` and - :math:`b`. + Return the *Euclidean* distance between point array :math:`a` and point + array :math:`b`. + + For a two-dimensional space, the metric is as follows: + + :math:`E_D = [(x_a - x_b)^2 + (y_a - y_b)^2]^{1/2}` Parameters ---------- - a : array_like + a Point array :math:`a`. - b : array_like + b Point array :math:`b`. Returns ------- - numeric or ndarray - Euclidean distance. + :class:`np.floating` or :class:`numpy.ndarray` + *Euclidean* distance. Examples -------- @@ -108,26 +137,64 @@ def euclidean_distance(a, b): 451.7133019... """ - return np.linalg.norm(as_float_array(a) - as_float_array(b), axis=-1) + return as_float( + np.linalg.norm(as_float_array(a) - as_float_array(b), axis=-1) + ) -def extend_line_segment(a, b, distance=1): +def manhattan_distance(a: ArrayLike, b: ArrayLike) -> FloatingOrNDArray: """ - Extends the line segment defined by point arrays :math:`a` and :math:`b` by + Return the *Manhattan* (or *City-Block*) distance between point array + :math:`a` and point array :math:`b`. + + For a two-dimensional space, the metric is as follows: + + :math:`M_D = |x_a - x_b| + |y_a - y_b|` + + Parameters + ---------- + a + Point array :math:`a`. + b + Point array :math:`b`. + + Returns + ------- + :class:`np.floating` or :class:`numpy.ndarray` + *Manhattan* distance. + + Examples + -------- + >>> a = np.array([100.00000000, 21.57210357, 272.22819350]) + >>> b = np.array([100.00000000, 426.67945353, 72.39590835]) + >>> manhattan_distance(a, b) # doctest: +ELLIPSIS + 604.9396351... + """ + + return as_float( + np.sum(np.abs(as_float_array(a) - as_float_array(b)), axis=-1) + ) + + +def extend_line_segment( + a: ArrayLike, b: ArrayLike, distance: Floating = 1 +) -> NDArray: + """ + Extend the line segment defined by point arrays :math:`a` and :math:`b` by given distance and return the new end point. Parameters ---------- - a : array_like + a Point array :math:`a`. - b : array_like + b Point array :math:`b`. - distance : numeric, optional + distance Distance to extend the line segment. Returns ------- - ndarray + :class:`numpy.ndarray` New end point. References @@ -159,45 +226,51 @@ def extend_line_segment(a, b, distance=1): return xy_c -class LineSegmentsIntersections_Specification( - namedtuple('LineSegmentsIntersections_Specification', - ('xy', 'intersect', 'parallel', 'coincident'))): +@dataclass +class LineSegmentsIntersections_Specification: """ - Defines the specification for intersection of line segments :math:`l_1` and + Define the specification for intersection of line segments :math:`l_1` and :math:`l_2` returned by :func:`colour.algebra.intersect_line_segments` definition. Parameters ---------- - xy : array_like + xy Array of :math:`l_1` and :math:`l_2` line segments intersections coordinates. Non existing segments intersections coordinates are set with `np.nan`. - intersect : array_like + intersect Array of *bool* indicating if line segments :math:`l_1` and :math:`l_2` intersect. - parallel : array_like - Array of *bool* indicating if line segments :math:`l_1` and :math:`l_2` - are parallel. - coincident : array_like - Array of *bool* indicating if line segments :math:`l_1` and :math:`l_2` - are coincident. + parallel + Array of :class:`bool` indicating if line segments :math:`l_1` and + :math:`l_2` are parallel. + coincident + Array of :class:`bool` indicating if line segments :math:`l_1` and + :math:`l_2` are coincident. """ + xy: NDArray + intersect: NDArray + parallel: NDArray + coincident: NDArray -def intersect_line_segments(l_1, l_2): + +def intersect_line_segments( + l_1: ArrayLike, l_2: ArrayLike +) -> LineSegmentsIntersections_Specification: """ - Computes :math:`l_1` line segments intersections with :math:`l_2` line + Compute :math:`l_1` line segments intersections with :math:`l_2` line segments. Parameters ---------- - l_1 : array_like + l_1 :math:`l_1` line segments array, each row is a line segment such as (:math:`x_1`, :math:`y_1`, :math:`x_2`, :math:`y_2`) where (:math:`x_1`, :math:`y_1`) and (:math:`x_2`, :math:`y_2`) are respectively the start and end points of :math:`l_1` line segments. - l_2 : array_like + l_2 :math:`l_2` line segments array, each row is a line segment such as (:math:`x_3`, :math:`y_3`, :math:`x_4`, :math:`y_4`) where (:math:`x_3`, :math:`y_3`) and (:math:`x_4`, :math:`y_4`) are @@ -205,7 +278,7 @@ def intersect_line_segments(l_1, l_2): Returns ------- - LineSegmentsIntersections_Specification + :class:`colour.algebra.LineSegmentsIntersections_Specification` Line segments intersections specification. References @@ -258,13 +331,13 @@ def intersect_line_segments(l_1, l_2): r_1, c_1 = l_1.shape[0], l_1.shape[1] r_2, c_2 = l_2.shape[0], l_2.shape[1] - x_1, y_1, x_2, y_2 = [ + x_1, y_1, x_2, y_2 = ( np.tile(l_1[:, i, np.newaxis], (1, r_2)) for i in range(c_1) - ] + ) l_2 = np.transpose(l_2) - x_3, y_3, x_4, y_4 = [np.tile(l_2[i, :], (r_1, 1)) for i in range(c_2)] + x_3, y_3, x_4, y_4 = (np.tile(l_2[i, :], (r_1, 1)) for i in range(c_2)) x_4_x_3 = x_4 - x_3 y_1_y_3 = y_1 - y_3 @@ -284,16 +357,18 @@ def intersect_line_segments(l_1, l_2): xy = tstack([x_1 + x_2_x_1 * u_a, y_1 + y_2_y_1 * u_a]) xy[~intersect] = np.nan parallel = denominator == 0 - coincident = np.logical_and.reduce((numerator_a == 0, numerator_b == 0, - parallel)) + coincident = np.logical_and.reduce( + (numerator_a == 0, numerator_b == 0, parallel) + ) - return LineSegmentsIntersections_Specification(xy, intersect, parallel, - coincident) + return LineSegmentsIntersections_Specification( + xy, intersect, parallel, coincident + ) -def ellipse_coefficients_general_form(coefficients): +def ellipse_coefficients_general_form(coefficients: ArrayLike) -> NDArray: """ - Returns the general form ellipse coefficients from given canonical form + Return the general form ellipse coefficients from given canonical form ellipse coefficients. The canonical form ellipse coefficients are as follows: the center @@ -303,12 +378,12 @@ def ellipse_coefficients_general_form(coefficients): Parameters ---------- - coefficients : array_like + coefficients Canonical form ellipse coefficients. Returns ------- - ndarray + :class:`numpy.ndarray` General form ellipse coefficients. References @@ -327,24 +402,24 @@ def ellipse_coefficients_general_form(coefficients): theta = np.radians(theta) cos_theta = np.cos(theta) sin_theta = np.sin(theta) - cos_theta_2 = cos_theta ** 2 - sin_theta_2 = sin_theta ** 2 - a_a_2 = a_a ** 2 - a_b_2 = a_b ** 2 + cos_theta_2 = cos_theta**2 + sin_theta_2 = sin_theta**2 + a_a_2 = a_a**2 + a_b_2 = a_b**2 a = a_a_2 * sin_theta_2 + a_b_2 * cos_theta_2 b = 2 * (a_b_2 - a_a_2) * sin_theta * cos_theta c = a_a_2 * cos_theta_2 + a_b_2 * sin_theta_2 d = -2 * a * x_c - b * y_c e = -b * x_c - 2 * c * y_c - f = a * x_c ** 2 + b * x_c * y_c + c * y_c ** 2 - a_a_2 * a_b_2 + f = a * x_c**2 + b * x_c * y_c + c * y_c**2 - a_a_2 * a_b_2 return np.array([a, b, c, d, e, f]) -def ellipse_coefficients_canonical_form(coefficients): +def ellipse_coefficients_canonical_form(coefficients: ArrayLike) -> NDArray: """ - Returns the canonical form ellipse coefficients from given general form + Return the canonical form ellipse coefficients from given general form ellipse coefficients. The general form ellipse coefficients are the coefficients of the implicit @@ -358,12 +433,12 @@ def ellipse_coefficients_canonical_form(coefficients): Parameters ---------- - coefficients : array_like + coefficients General form ellipse coefficients. Returns ------- - ndarray + :class:`numpy.ndarray` Canonical form ellipse coefficients. References @@ -379,9 +454,9 @@ def ellipse_coefficients_canonical_form(coefficients): a, b, c, d, e, f = tsplit(coefficients) - d_1 = b ** 2 - 4 * a * c - n_p_1 = 2 * (a * e ** 2 + c * d ** 2 - b * d * e + d_1 * f) - n_p_2 = np.sqrt((a - c) ** 2 + b ** 2) + d_1 = b**2 - 4 * a * c + n_p_1 = 2 * (a * e**2 + c * d**2 - b * d * e + d_1 * f) + n_p_2 = np.sqrt((a - c) ** 2 + b**2) a_a = -np.sqrt(n_p_1 * (a + c + n_p_2)) / d_1 a_b = -np.sqrt(n_p_1 * (a + c - n_p_2)) / d_1 @@ -405,17 +480,19 @@ def ellipse_coefficients_canonical_form(coefficients): return np.array([x_c, y_c, a_a, a_b, theta]) -def point_at_angle_on_ellipse(phi, coefficients): +def point_at_angle_on_ellipse( + phi: ArrayLike, coefficients: ArrayLike +) -> NDArray: """ - Returns the coordinates of the point at angle :math:`\\phi` in degrees on + Return the coordinates of the point at angle :math:`\\phi` in degrees on the ellipse with given canonical form coefficients. Parameters ---------- - phi : array_like + phi Point at angle :math:`\\phi` in degrees to retrieve the coordinates of. - coefficients : array_like + coefficients General form ellipse coefficients as follows: the center coordinates :math:`x_c` and :math:`y_c`, semi-major axis length :math:`a_a`, semi-minor axis length :math:`a_b` and rotation angle :math:`\\theta` @@ -423,7 +500,7 @@ def point_at_angle_on_ellipse(phi, coefficients): Returns ------- - ndarray + :class:`numpy.ndarray` Coordinates of the point at angle :math:`\\phi` Examples @@ -448,13 +525,13 @@ def point_at_angle_on_ellipse(phi, coefficients): return tstack([x, y]) -def ellipse_fitting_Halir1998(a): +def ellipse_fitting_Halir1998(a: ArrayLike) -> NDArray: """ - Returns the coefficients of the implicit second-order polynomial/quadratic + Return the coefficients of the implicit second-order polynomial/quadratic curve that fits given point array :math:`a` using *Halir and Flusser (1998)* method. - The implicit second-order polynomial is expressed as follows:: + The implicit second-order polynomial is expressed as follows: :math:`F\\left(x, y\\right)` = ax^2 + bxy + cy^2 + dx + ey + f = 0` @@ -464,12 +541,12 @@ def ellipse_fitting_Halir1998(a): Parameters ---------- - a : array_like + a Point array :math:`a` to be fitted. Returns ------- - ndarray + :class:`numpy.ndarray` Coefficients of the implicit second-order polynomial/quadratic curve that fits given point array :math:`a`. @@ -490,7 +567,7 @@ def ellipse_fitting_Halir1998(a): x, y = tsplit(a) # Quadratic part of the design matrix. - D1 = tstack([x ** 2, x * y, y ** 2]) + D1 = tstack([x**2, x * y, y**2]) # Linear part of the design matrix. D2 = tstack([x, y, ones(x.shape)]) @@ -520,28 +597,27 @@ def ellipse_fitting_Halir1998(a): return A -ELLIPSE_FITTING_METHODS = CaseInsensitiveMapping({ - 'Halir 1998': ellipse_fitting_Halir1998 -}) +ELLIPSE_FITTING_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + {"Halir 1998": ellipse_fitting_Halir1998} +) ELLIPSE_FITTING_METHODS.__doc__ = """ Supported ellipse fitting methods. References ---------- :cite:`Halir1998` - -ELLIPSE_FITTING_METHODS : CaseInsensitiveMapping - **{'Halir 1998'}** """ -def ellipse_fitting(a, method='Halir 1998'): +def ellipse_fitting( + a: ArrayLike, method: Union[Literal["Halir 1998"], str] = "Halir 1998" +) -> NDArray: """ - Returns the coefficients of the implicit second-order polynomial/quadratic + Return the coefficients of the implicit second-order polynomial/quadratic curve that fits given point array :math:`a` using given method. - The implicit second-order polynomial is expressed as follows:: + The implicit second-order polynomial is expressed as follows: :math:`F\\left(x, y\\right)` = ax^2 + bxy + cy^2 + dx + ey + f = 0` @@ -551,15 +627,14 @@ def ellipse_fitting(a, method='Halir 1998'): Parameters ---------- - a : array_like + a Point array :math:`a` to be fitted. - method : unicode, optional - **{'Halir 1998'}**, + method Computation method. Returns ------- - ndarray + :class:`numpy.ndarray` Coefficients of the implicit second-order polynomial/quadratic curve that fits given point array :math:`a`. @@ -577,6 +652,8 @@ def ellipse_fitting(a, method='Halir 1998'): array([-0., -0., 2., 1., 0.]) """ + method = validate_method(method, ELLIPSE_FITTING_METHODS) + function = ELLIPSE_FITTING_METHODS[method] return function(a) diff --git a/colour/algebra/interpolation.py b/colour/algebra/interpolation.py index c64c97b74c..d54a9b7987 100644 --- a/colour/algebra/interpolation.py +++ b/colour/algebra/interpolation.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Interpolation ============= -Defines classes and definitions for interpolating variables. +Defines the classes and definitions for interpolating variables. - :class:`colour.KernelInterpolator`: 1-D function generic interpolation with arbitrary kernel. @@ -58,55 +57,90 @@ October 14, 2017, from https://en.wikipedia.org/wiki/Lanczos_resampling """ -from __future__ import division, unicode_literals +from __future__ import annotations import itertools import numpy as np import scipy.interpolate -from six.moves import reduce -from collections import OrderedDict -try: # pragma: no cover - from collections import Mapping -except ImportError: # pragma: no cover - from collections.abc import Mapping - -from colour.constants import DEFAULT_FLOAT_DTYPE, DEFAULT_INT_DTYPE -from colour.utilities import (CaseInsensitiveMapping, as_float_array, as_float, - closest_indexes, interval, is_integer, - is_numeric, runtime_warning, tsplit) -from colour.utilities.deprecation import ObjectRenamed - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from collections.abc import Mapping +from functools import reduce + +from colour.constants import DEFAULT_FLOAT_DTYPE +from colour.hints import ( + Any, + ArrayLike, + Callable, + Dict, + DTypeNumber, + Floating, + FloatingOrArrayLike, + FloatingOrNDArray, + Integer, + Literal, + NDArray, + Optional, + Tuple, + Type, + Union, + cast, +) +from colour.utilities import ( + CaseInsensitiveMapping, + as_array, + as_float_array, + as_float, + as_int_array, + attest, + closest_indexes, + interval, + is_numeric, + optional, + runtime_warning, + tsplit, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'kernel_nearest_neighbour', 'kernel_linear', 'kernel_sinc', - 'kernel_lanczos', 'kernel_cardinal_spline', 'KernelInterpolator', - 'NearestNeighbourInterpolator', 'LinearInterpolator', - 'SpragueInterpolator', 'CubicSplineInterpolator', 'PchipInterpolator', - 'NullInterpolator', 'lagrange_coefficients', - 'vertices_and_relative_coordinates', 'table_interpolation_trilinear', - 'table_interpolation_tetrahedral', 'TABLE_INTERPOLATION_METHODS', - 'table_interpolation' + "kernel_nearest_neighbour", + "kernel_linear", + "kernel_sinc", + "kernel_lanczos", + "kernel_cardinal_spline", + "KernelInterpolator", + "NearestNeighbourInterpolator", + "LinearInterpolator", + "SpragueInterpolator", + "CubicSplineInterpolator", + "PchipInterpolator", + "NullInterpolator", + "lagrange_coefficients", + "vertices_and_relative_coordinates", + "table_interpolation_trilinear", + "table_interpolation_tetrahedral", + "TABLE_INTERPOLATION_METHODS", + "table_interpolation", ] -def kernel_nearest_neighbour(x): +def kernel_nearest_neighbour(x: ArrayLike) -> NDArray: """ - Returns the *nearest-neighbour* kernel evaluated at given samples. + Return the *nearest-neighbour* kernel evaluated at given samples. Parameters ---------- - x : array_like + x Samples at which to evaluate the *nearest-neighbour* kernel. Returns ------- - ndarray + :class:`numpy.ndarray` The *nearest-neighbour* kernel evaluated at given samples. References @@ -122,18 +156,18 @@ def kernel_nearest_neighbour(x): return np.where(np.abs(x) < 0.5, 1, 0) -def kernel_linear(x): +def kernel_linear(x: ArrayLike) -> NDArray: """ - Returns the *linear* kernel evaluated at given samples. + Return the *linear* kernel evaluated at given samples. Parameters ---------- - x : array_like + x Samples at which to evaluate the *linear* kernel. Returns ------- - ndarray + :class:`numpy.ndarray` The *linear* kernel evaluated at given samples. References @@ -152,20 +186,20 @@ def kernel_linear(x): return np.where(np.abs(x) < 1, 1 - np.abs(x), 0) -def kernel_sinc(x, a=3): +def kernel_sinc(x: ArrayLike, a: Floating = 3) -> NDArray: """ - Returns the *sinc* kernel evaluated at given samples. + Return the *sinc* kernel evaluated at given samples. Parameters ---------- - x : array_like + x Samples at which to evaluate the *sinc* kernel. - a : int, optional + a Size of the *sinc* kernel. Returns ------- - ndarray + :class:`numpy.ndarray` The *sinc* kernel evaluated at given samples. References @@ -181,25 +215,27 @@ def kernel_sinc(x, a=3): 3.8981718...e-17]) """ - assert a >= 1, '"a" must be equal or superior to 1!' + x = as_float_array(x) + + attest(bool(a >= 1), '"a" must be equal or superior to 1!') return np.where(np.abs(x) < a, np.sinc(x), 0) -def kernel_lanczos(x, a=3): +def kernel_lanczos(x: ArrayLike, a: Floating = 3) -> NDArray: """ - Returns the *lanczos* kernel evaluated at given samples. + Return the *lanczos* kernel evaluated at given samples. Parameters ---------- - x : array_like + x Samples at which to evaluate the *lanczos* kernel. - a : int, optional + a Size of the *lanczos* kernel. Returns ------- - ndarray + :class:`numpy.ndarray` The *lanczos* kernel evaluated at given samples. References @@ -215,14 +251,18 @@ def kernel_lanczos(x, a=3): 3.2237621...e-17]) """ - assert a >= 1, '"a" must be equal or superior to 1!' + x = as_float_array(x) + + attest(bool(a >= 1), '"a" must be equal or superior to 1!') return np.where(np.abs(x) < a, np.sinc(x) * np.sinc(x / a), 0) -def kernel_cardinal_spline(x, a=0.5, b=0.0): +def kernel_cardinal_spline( + x: ArrayLike, a: Floating = 0.5, b: Floating = 0.0 +) -> NDArray: """ - Returns the *cardinal spline* kernel evaluated at given samples. + Return the *cardinal spline* kernel evaluated at given samples. Notable *cardinal spline* :math:`a` and :math:`b` parameterizations: @@ -232,16 +272,16 @@ def kernel_cardinal_spline(x, a=0.5, b=0.0): Parameters ---------- - x : array_like + x Samples at which to evaluate the *cardinal spline* kernel. - a : int, optional + a :math:`a` control parameter. - b : int, optional + b :math:`b` control parameter. Returns ------- - ndarray + :class:`numpy.ndarray` The *cardinal spline* kernel evaluated at given samples. References @@ -257,49 +297,56 @@ def kernel_cardinal_spline(x, a=0.5, b=0.0): 0.0781893..., 0. ]) """ + x = as_float_array(x) + x_abs = np.abs(x) y = np.where( x_abs < 1, - (-6 * a - 9 * b + 12) * x_abs ** 3 + (6 * a + 12 * b - 18) * x_abs ** 2 - - 2 * b + 6, - (-6 * a - b) * x_abs ** 3 + (30 * a + 6 * b) * x_abs ** 2 + - (-48 * a - 12 * b) * x_abs + 24 * a + 8 * b, + (-6 * a - 9 * b + 12) * x_abs**3 + + (6 * a + 12 * b - 18) * x_abs**2 + - 2 * b + + 6, + (-6 * a - b) * x_abs**3 + + (30 * a + 6 * b) * x_abs**2 + + (-48 * a - 12 * b) * x_abs + + 24 * a + + 8 * b, ) y[x_abs >= 2] = 0 return 1 / 6 * y -class KernelInterpolator(object): +class KernelInterpolator: """ Kernel based interpolation of a 1-D function. The reconstruction of a continuous signal can be described as a linear convolution operation. Interpolation can be expressed as a convolution of the given discrete function :math:`g(x)` with some continuous interpolation - kernel :math:`k(w)`: + kernel :math:`k(w)`:: - :math:`\\hat{g}(w_0) = [k * g](w_0) = \ + :math:`\\hat{g}(w_0) = [k * g](w_0) = \ \\sum_{x=-\\infty}^{\\infty}k(w_0 - x)\\cdot g(x)` Parameters ---------- - x : array_like + x Independent :math:`x` variable values corresponding with :math:`y` variable. - y : array_like + y Dependent and already known :math:`y` variable values to interpolate. - window : int, optional + window Width of the window in samples on each side. - kernel : callable, optional + kernel Kernel to use for interpolation. - kernel_kwargs : dict, optional + kernel_kwargs Arguments to use when calling the kernel. - padding_kwargs : dict, optional + padding_kwargs Arguments to use when padding :math:`y` variable values with the :func:`np.pad` definition. - dtype : type + dtype Data type used for internal conversions. Attributes @@ -331,7 +378,7 @@ class KernelInterpolator(object): >>> f(0.5) # doctest: +ELLIPSIS 6.9411400... - Interpolating an *array_like* variable: + Interpolating an `ArrayLike` variable: >>> f([0.25, 0.75]) # doctest: +ELLIPSIS array([ 6.1806208..., 8.0823848...]) @@ -354,273 +401,267 @@ class KernelInterpolator(object): array([ 5.3961792..., 5.6521093...]) """ - def __init__(self, - x, - y, - window=3, - kernel=kernel_lanczos, - kernel_kwargs=None, - padding_kwargs=None, - dtype=None): - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE - - self._x_p = None - self._y_p = None - - self._x = None - self._y = None - self._window = None - self._padding_kwargs = { - 'pad_width': (window, window), - 'mode': 'reflect' + def __init__( + self, + x: ArrayLike, + y: ArrayLike, + window: Floating = 3, + kernel: Callable = kernel_lanczos, + kernel_kwargs: Optional[Dict] = None, + padding_kwargs: Optional[Dict] = None, + dtype: Optional[Type[DTypeNumber]] = None, + ): + dtype = cast(Type[DTypeNumber], optional(dtype, DEFAULT_FLOAT_DTYPE)) + + self._x_p: NDArray = np.array([]) + self._y_p: NDArray = np.array([]) + + self._x: NDArray = np.array([]) + self._y: NDArray = np.array([]) + self._window: Floating = 3 + self._padding_kwargs: Dict = { + "pad_width": (window, window), + "mode": "reflect", } - self._dtype = dtype - - self.x = x - self.y = y + self._kernel: Callable = kernel_lanczos + self._kernel_kwargs: Dict = {} + self._dtype: Type[DTypeNumber] = dtype + + # TODO: Remove pragma when https://github.com/python/mypy/issues/3004 + # is resolved. + self.x = x # type: ignore[assignment] + self.y = y # type: ignore[assignment] self.window = window - self.padding_kwargs = padding_kwargs - - self._kernel = None + self.padding_kwargs = optional(padding_kwargs, self._padding_kwargs) self.kernel = kernel - self._kernel_kwargs = {} - self.kernel_kwargs = kernel_kwargs + self.kernel_kwargs = optional(kernel_kwargs, self._kernel_kwargs) self._validate_dimensions() @property - def x(self): + def x(self) -> NDArray: """ Getter and setter property for the independent :math:`x` variable. Parameters ---------- - value : array_like + value Value to set the independent :math:`x` variable with. Returns ------- - array_like + :class:`numpy.ndarray` Independent :math:`x` variable. """ return self._x @x.setter - def x(self, value): - """ - Setter for the **self.x** property. - """ + def x(self, value: ArrayLike): + """Setter for the **self.x** property.""" - if value is not None: - value = np.atleast_1d(value).astype(self._dtype) + value = np.atleast_1d(value).astype(self._dtype) - assert value.ndim == 1, ( - '"x" independent variable must have exactly one dimension!') + attest( + value.ndim == 1, + '"x" independent variable must have exactly one dimension!', + ) - value_interval = interval(value) + value_interval = interval(value) - if value_interval.size != 1: - runtime_warning(('"x" independent variable is not uniform, ' - 'unpredictable results may occur!')) + if value_interval.size != 1: + runtime_warning( + '"x" independent variable is not uniform, ' + "unpredictable results may occur!" + ) - self._x = value + self._x = as_array(value, self._dtype) - if self._window is not None: - self._x_p = np.pad( - self._x, (self._window, self._window), - 'linear_ramp', - end_values=( - np.min(self._x) - self._window * value_interval[0], - np.max(self._x) + self._window * value_interval[0])) + self._x_p = np.pad( + self._x, + as_int_array([self._window, self._window]), + "linear_ramp", + end_values=( + np.min(self._x) - self._window * value_interval[0], + np.max(self._x) + self._window * value_interval[0], + ), + ) @property - def y(self): + def y(self) -> NDArray: """ Getter and setter property for the dependent and already known :math:`y` variable. Parameters ---------- - value : array_like + value Value to set the dependent and already known :math:`y` variable with. Returns ------- - array_like + :class:`numpy.ndarray` Dependent and already known :math:`y` variable. """ return self._y @y.setter - def y(self, value): - """ - Setter for the **self.y** property. - """ + def y(self, value: ArrayLike): + """Setter for the **self.y** property.""" - if value is not None: - value = np.atleast_1d(value).astype(self._dtype) + value = np.atleast_1d(value).astype(self._dtype) - assert value.ndim == 1, ( - '"y" dependent variable must have exactly one dimension!') + attest( + value.ndim == 1, + '"y" dependent variable must have exactly one dimension!', + ) - self._y = value + self._y = as_array(value, self._dtype) - if self._window is not None: - self._y_p = np.pad(self._y, **self._padding_kwargs) + if self._window is not None: + self._y_p = np.pad(self._y, **self._padding_kwargs) @property - def window(self): + def window(self) -> Floating: """ Getter and setter property for the window. Parameters ---------- - value : int + value Value to set the window with. Returns ------- - int + :class:`numpy.floating` Window. """ return self._window @window.setter - def window(self, value): - """ - Setter for the **self.window** property. - """ - - if value is not None: - assert is_integer(value), '"window" must be an integer!' + def window(self, value: Floating): + """Setter for the **self.window** property.""" - assert value >= 1, '"window" must be equal or superior to 1!' + attest( + bool(value >= 1), '"window" must be equal to or greater than 1!' + ) - self._window = value + self._window = value - # Triggering "self._x_p" update. - if self._x is not None: - self.x = self._x + # Triggering "self._x_p" update. + if self._x is not None: + self.x = self._x - # Triggering "self._y_p" update. - if self._y is not None: - self.y = self._y + # Triggering "self._y_p" update. + if self._y is not None: + self.y = self._y @property - def kernel(self): + def kernel(self) -> Callable: """ Getter and setter property for the kernel callable. Parameters ---------- - value : callable + value Value to set the kernel callable. Returns ------- - callable + Callable Kernel callable. """ return self._kernel @kernel.setter - def kernel(self, value): - """ - Setter for the **self.kernel** property. - """ + def kernel(self, value: Callable): + """Setter for the **self.kernel** property.""" - if value is not None: - assert hasattr( - value, - '__call__'), ('"{0}" attribute: "{1}" is not callable!'.format( - 'kernel', value)) + attest( + hasattr(value, "__call__"), + f'"kernel" property: "{value}" is not callable!', + ) - self._kernel = value + self._kernel = value @property - def kernel_kwargs(self): + def kernel_kwargs(self) -> Dict: """ Getter and setter property for the kernel call time arguments. Parameters ---------- - value : dict + value Value to call the interpolation kernel with. Returns ------- - dict + :class:`dict` Kernel call time arguments. """ return self._kernel_kwargs @kernel_kwargs.setter - def kernel_kwargs(self, value): - """ - Setter for the **self.kernel_kwargs** property. - """ + def kernel_kwargs(self, value: Dict): + """Setter for the **self.kernel_kwargs** property.""" - if value is not None: - assert isinstance(value, (dict, OrderedDict)), ( - '"{0}" attribute: "{1}" type is not "dict" or "OrderedDict"!' - ).format('kernel_kwargs', value) + attest( + isinstance(value, dict), + f'"kernel_kwargs" property: "{value}" type is not "dict"!', + ) - self._kernel_kwargs = value + self._kernel_kwargs = value @property - def padding_kwargs(self): + def padding_kwargs(self) -> Dict: """ Getter and setter property for the kernel call time arguments. Parameters ---------- - value : dict + value Value to call the interpolation kernel with. Returns ------- - dict + :class:`dict` Kernel call time arguments. """ return self._padding_kwargs @padding_kwargs.setter - def padding_kwargs(self, value): - """ - Setter for the **self.padding_kwargs** property. - """ + def padding_kwargs(self, value: Dict): + """Setter for the **self.padding_kwargs** property.""" - if value is not None: - assert isinstance(value, Mapping), ( - '"{0}" attribute: "{1}" type is not a "Mapping" instance!' - ).format('padding_kwargs', value) + attest( + isinstance(value, Mapping), + f'"padding_kwargs" property: "{value}" type is not a "Mapping" instance!', + ) - self._padding_kwargs = value + self._padding_kwargs = value - # Triggering "self._y_p" update. - if self._y is not None: - self.y = self._y + # Triggering "self._y_p" update. + if self._y is not None: + self.y = self._y - def __call__(self, x): + def __call__(self, x: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Evaluates the interpolator at given point(s). + Evaluate the interpolator at given point(s). Parameters ---------- - x : numeric or array_like + x Point(s) to evaluate the interpolant at. Returns ------- - float or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Interpolated value(s). """ @@ -630,18 +671,18 @@ def __call__(self, x): return xi - def _evaluate(self, x): + def _evaluate(self, x: NDArray) -> NDArray: """ - Performs the interpolator evaluation at given points. + Perform the interpolator evaluation at given points. Parameters ---------- - x : ndarray + x Points to evaluate the interpolant at. Returns ------- - ndarray + :class:`numpy.ndarray` Interpolated points values. """ @@ -651,86 +692,45 @@ def _evaluate(self, x): x_interval = interval(self._x)[0] x_f = np.floor(x / x_interval) - windows = (x_f[:, np.newaxis] + np.arange(-self._window + 1, - self._window + 1)) + windows = x_f[:, np.newaxis] + np.arange( + -self._window + 1, self._window + 1 + ) clip_l = min(self._x_p) / x_interval clip_h = max(self._x_p) / x_interval windows = np.clip(windows, clip_l, clip_h) - clip_l - windows = np.around(windows).astype(DEFAULT_INT_DTYPE) + windows = as_int_array(np.around(windows)) return np.sum( - self._y_p[windows] * self._kernel( - x[:, np.newaxis] / x_interval - windows - - min(self._x_p) / x_interval, **self._kernel_kwargs), - axis=-1) + self._y_p[windows] + * self._kernel( + x[:, np.newaxis] / x_interval + - windows + - min(self._x_p) / x_interval, + **self._kernel_kwargs, + ), + axis=-1, + ) def _validate_dimensions(self): - """ - Validates variables dimensions to be the same. - """ + """Validate that the variables dimensions are the same.""" if len(self._x) != len(self._y): raise ValueError( - ('"x" independent and "y" dependent variables have different ' - 'dimensions: "{0}", "{1}"').format( - len(self._x), len(self._y))) + '"x" independent and "y" dependent variables have different ' + f'dimensions: "{len(self._x)}", "{len(self._y)}"' + ) - def _validate_interpolation_range(self, x): - """ - Validates given point to be in interpolation range. - """ + def _validate_interpolation_range(self, x: NDArray): + """Validate given point to be in interpolation range.""" below_interpolation_range = x < self._x[0] above_interpolation_range = x > self._x[-1] if below_interpolation_range.any(): - raise ValueError('"{0}" is below interpolation range.'.format(x)) + raise ValueError(f'"{x}" is below interpolation range.') if above_interpolation_range.any(): - raise ValueError('"{0}" is above interpolation range.'.format(x)) - - # ------------------------------------------------------------------------# - # --- API Changes and Deprecation Management ---# - # ------------------------------------------------------------------------# - @property - def kernel_args(self): - # Docstrings are omitted for documentation purposes. - runtime_warning( - str( - ObjectRenamed('KernelInterpolator.kernel_args', - 'KernelInterpolator.kernel_kwargs'))) - - return self.kernel_kwargs - - @kernel_args.setter - def kernel_args(self, value): - # Docstrings are omitted for documentation purposes. - runtime_warning( - str( - ObjectRenamed('KernelInterpolator.kernel_args', - 'KernelInterpolator.kernel_kwargs'))) - - self.kernel_kwargs = value - - @property - def padding_args(self): - # Docstrings are omitted for documentation purposes. - runtime_warning( - str( - ObjectRenamed('KernelInterpolator.padding_args', - 'KernelInterpolator.padding_kwargs'))) - - return self.padding_kwargs - - @padding_args.setter - def padding_args(self, value): - # Docstrings are omitted for documentation purposes. - runtime_warning( - str( - ObjectRenamed('KernelInterpolator.padding_args', - 'KernelInterpolator.padding_kwargs'))) - - self.padding_kwargs = value + raise ValueError(f'"{x}" is above interpolation range.') class NearestNeighbourInterpolator(KernelInterpolator): @@ -739,46 +739,46 @@ class NearestNeighbourInterpolator(KernelInterpolator): Other Parameters ---------------- - x : array_like + dtype + Data type used for internal conversions. + padding_kwargs + Arguments to use when padding :math:`y` variable values with the + :func:`np.pad` definition. + window + Width of the window in samples on each side. + x Independent :math:`x` variable values corresponding with :math:`y` variable. - y : array_like + y Dependent and already known :math:`y` variable values to interpolate. - window : int, optional - Width of the window in samples on each side. - padding_kwargs : dict, optional - Arguments to use when padding :math:`y` variable values with the - :func:`np.pad` definition. - dtype : type - Data type used for internal conversions. Methods ------- - :meth:`~colour.NearestNeighbourInterpolator.__init__` """ - def __init__(self, *args, **kwargs): - kwargs['kernel'] = kernel_nearest_neighbour - if 'kernel_kwargs' in kwargs: - del kwargs['kernel_kwargs'] + def __init__(self, *args: Any, **kwargs: Any): + kwargs["kernel"] = kernel_nearest_neighbour + if "kernel_kwargs" in kwargs: + del kwargs["kernel_kwargs"] - super(NearestNeighbourInterpolator, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) -class LinearInterpolator(object): +class LinearInterpolator: """ - Linearly interpolates a 1-D function. + Interpolate linearly a 1-D function. Parameters ---------- - x : array_like + x Independent :math:`x` variable values corresponding with :math:`y` variable. - y : array_like + y Dependent and already known :math:`y` variable values to interpolate. - dtype : type + dtype Data type used for internal conversions. Attributes @@ -803,108 +803,111 @@ class LinearInterpolator(object): ... 69.5900, 27.8007, 86.0500]) >>> x = np.arange(len(y)) >>> f = LinearInterpolator(x, y) - >>> # Doctests ellipsis for Python 2.x compatibility. >>> f(0.5) # doctest: +ELLIPSIS 7.64... - Interpolating an *array_like* variable: + Interpolating an `ArrayLike` variable: >>> f([0.25, 0.75]) array([ 6.7825, 8.5075]) """ - def __init__(self, x, y, dtype=None): - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE + def __init__( + self, + x: ArrayLike, + y: ArrayLike, + dtype: Optional[Type[DTypeNumber]] = None, + ): + dtype = cast(Type[DTypeNumber], optional(dtype, DEFAULT_FLOAT_DTYPE)) - self._x = None - self._y = None - self._dtype = dtype + self._x: NDArray = np.array([]) + self._y: NDArray = np.array([]) + self._dtype: Type[DTypeNumber] = dtype - self.x = x - self.y = y + # TODO: Remove pragma when https://github.com/python/mypy/issues/3004 + # is resolved. + self.x = x # type: ignore[assignment] + self.y = y # type: ignore[assignment] self._validate_dimensions() @property - def x(self): + def x(self) -> NDArray: """ Getter and setter property for the independent :math:`x` variable. Parameters ---------- - value : array_like + value Value to set the independent :math:`x` variable with. Returns ------- - array_like + :class:`numpy.ndarray` Independent :math:`x` variable. """ return self._x @x.setter - def x(self, value): - """ - Setter for the **self.x** property. - """ + def x(self, value: ArrayLike): + """Setter for the **self.x** property.""" - if value is not None: - value = np.atleast_1d(value).astype(self._dtype) + value = np.atleast_1d(value).astype(self._dtype) - assert value.ndim == 1, ( - '"x" independent variable must have exactly one dimension!') + attest( + value.ndim == 1, + '"x" independent variable must have exactly one dimension!', + ) self._x = value @property - def y(self): + def y(self) -> NDArray: """ Getter and setter property for the dependent and already known :math:`y` variable. Parameters ---------- - value : array_like + value Value to set the dependent and already known :math:`y` variable with. Returns ------- - array_like + :class:`numpy.ndarray` Dependent and already known :math:`y` variable. """ return self._y @y.setter - def y(self, value): - """ - Setter for the **self.y** property. - """ + def y(self, value: ArrayLike): + """Setter for the **self.y** property.""" - if value is not None: - value = np.atleast_1d(value).astype(self._dtype) + value = np.atleast_1d(value).astype(self._dtype) - assert value.ndim == 1, ( - '"y" dependent variable must have exactly one dimension!') + attest( + value.ndim == 1, + '"y" dependent variable must have exactly one dimension!', + ) self._y = value - def __call__(self, x): + def __call__(self, x: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Evaluates the interpolating polynomial at given point(s). + Evaluate the interpolating polynomial at given point(s). Parameters ---------- - x : numeric or array_like + x Point(s) to evaluate the interpolant at. Returns ------- - float or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Interpolated value(s). """ @@ -914,18 +917,18 @@ def __call__(self, x): return xi - def _evaluate(self, x): + def _evaluate(self, x: NDArray) -> NDArray: """ - Performs the interpolating polynomial evaluation at given points. + Perform the interpolating polynomial evaluation at given points. Parameters ---------- - x : ndarray + x Points to evaluate the interpolant at. Returns ------- - ndarray + :class:`numpy.ndarray` Interpolated points values. """ @@ -935,34 +938,30 @@ def _evaluate(self, x): return np.interp(x, self._x, self._y) def _validate_dimensions(self): - """ - Validates variables dimensions to be the same. - """ + """Validate that the variables dimensions are the same.""" if len(self._x) != len(self._y): raise ValueError( - ('"x" independent and "y" dependent variables have different ' - 'dimensions: "{0}", "{1}"').format( - len(self._x), len(self._y))) + '"x" independent and "y" dependent variables have different ' + f'dimensions: "{len(self._x)}", "{len(self._y)}"' + ) - def _validate_interpolation_range(self, x): - """ - Validates given point to be in interpolation range. - """ + def _validate_interpolation_range(self, x: NDArray): + """Validate given point to be in interpolation range.""" below_interpolation_range = x < self._x[0] above_interpolation_range = x > self._x[-1] if below_interpolation_range.any(): - raise ValueError('"{0}" is below interpolation range.'.format(x)) + raise ValueError(f'"{x}" is below interpolation range.') if above_interpolation_range.any(): - raise ValueError('"{0}" is above interpolation range.'.format(x)) + raise ValueError(f'"{x}" is above interpolation range.') -class SpragueInterpolator(object): +class SpragueInterpolator: """ - Constructs a fifth-order polynomial that passes through :math:`y` dependent + Construct a fifth-order polynomial that passes through :math:`y` dependent variable. *Sprague (1880)* method is recommended by the *CIE* for interpolating @@ -970,13 +969,13 @@ class SpragueInterpolator(object): Parameters ---------- - x : array_like + x Independent :math:`x` variable values corresponding with :math:`y` variable. - y : array_like + y Dependent and already known :math:`y` variable values to interpolate. - dtype : type + dtype Data type used for internal conversions. Attributes @@ -1009,173 +1008,219 @@ class SpragueInterpolator(object): >>> f(0.5) # doctest: +ELLIPSIS 7.2185025... - Interpolating an *array_like* variable: + Interpolating an `ArrayLike` variable: >>> f([0.25, 0.75]) # doctest: +ELLIPSIS array([ 6.7295161..., 7.8140625...]) """ - SPRAGUE_C_COEFFICIENTS = np.array([ - [884, -1960, 3033, -2648, 1080, -180], - [508, -540, 488, -367, 144, -24], - [-24, 144, -367, 488, -540, 508], - [-180, 1080, -2648, 3033, -1960, 884], - ]) + SPRAGUE_C_COEFFICIENTS = np.array( + [ + [884, -1960, 3033, -2648, 1080, -180], + [508, -540, 488, -367, 144, -24], + [-24, 144, -367, 488, -540, 508], + [-180, 1080, -2648, 3033, -1960, 884], + ] + ) """ Defines the coefficients used to generate extra points for boundaries interpolation. - SPRAGUE_C_COEFFICIENTS : array_like, (4, 6) + SPRAGUE_C_COEFFICIENTS, (4, 6) References ---------- :cite:`CIETC1-382005h` """ - def __init__(self, x, y, dtype=None): - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE + def __init__( + self, + x: ArrayLike, + y: ArrayLike, + dtype: Optional[Type[DTypeNumber]] = None, + ): + dtype = cast(Type[DTypeNumber], optional(dtype, DEFAULT_FLOAT_DTYPE)) - self._xp = None - self._yp = None + self._xp: NDArray = np.array([]) + self._yp: NDArray = np.array([]) - self._x = None - self._y = None - self._dtype = dtype + self._x: NDArray = np.array([]) + self._y: NDArray = np.array([]) + self._dtype: Type[DTypeNumber] = dtype - self.x = x - self.y = y + # TODO: Remove pragma when https://github.com/python/mypy/issues/3004 + # is resolved. + self.x = x # type: ignore[assignment] + self.y = y # type: ignore[assignment] self._validate_dimensions() @property - def x(self): + def x(self) -> NDArray: """ Getter and setter property for the independent :math:`x` variable. Parameters ---------- - value : array_like + value Value to set the independent :math:`x` variable with. Returns ------- - array_like + :class:`numpy.ndarray` Independent :math:`x` variable. """ return self._x @x.setter - def x(self, value): - """ - Setter for the **self.x** property. - """ + def x(self, value: ArrayLike): + """Setter for the **self.x** property.""" - if value is not None: - value = np.atleast_1d(value).astype(self._dtype) + value = as_array(np.atleast_1d(value), self._dtype) - assert value.ndim == 1, ( - '"x" independent variable must have exactly one dimension!') + attest( + value.ndim == 1, + '"x" independent variable must have exactly one dimension!', + ) - value_interval = interval(value)[0] + self._x = value - xp1 = value[0] - value_interval * 2 - xp2 = value[0] - value_interval - xp3 = value[-1] + value_interval - xp4 = value[-1] + value_interval * 2 + value_interval = interval(self._x)[0] - self._xp = np.concatenate(((xp1, xp2), value, (xp3, xp4))) + xp1 = self._x[0] - value_interval * 2 + xp2 = self._x[0] - value_interval + xp3 = self._x[-1] + value_interval + xp4 = self._x[-1] + value_interval * 2 - self._x = value + self._xp = np.concatenate( + [ + as_array([xp1, xp2], self._dtype), + value, + as_array([xp3, xp4], self._dtype), + ] + ) @property - def y(self): + def y(self) -> NDArray: """ Getter and setter property for the dependent and already known :math:`y` variable. Parameters ---------- - value : array_like + value Value to set the dependent and already known :math:`y` variable with. Returns ------- - array_like + :class:`numpy.ndarray` Dependent and already known :math:`y` variable. """ return self._y @y.setter - def y(self, value): - """ - Setter for the **self.y** property. - """ + def y(self, value: ArrayLike): + """Setter for the **self.y** property.""" - if value is not None: - value = np.atleast_1d(value).astype(self._dtype) + value = as_array(np.atleast_1d(value), self._dtype) - assert value.ndim == 1, ( - '"y" dependent variable must have exactly one dimension!') + attest( + value.ndim == 1, + '"y" dependent variable must have exactly one dimension!', + ) - assert len(value) >= 6, ( - '"y" dependent variable values count must be normalised to' - 'domain [6:]!') - - yp1 = np.ravel( - (np.dot(self.SPRAGUE_C_COEFFICIENTS[0], - np.array(value[0:6]).reshape([6, 1]))) / 209)[0] - yp2 = np.ravel( - (np.dot(self.SPRAGUE_C_COEFFICIENTS[1], - np.array(value[0:6]).reshape([6, 1]))) / 209)[0] - yp3 = np.ravel( - (np.dot(self.SPRAGUE_C_COEFFICIENTS[2], - np.array(value[-6:]).reshape([6, 1]))) / 209)[0] - yp4 = np.ravel( - (np.dot(self.SPRAGUE_C_COEFFICIENTS[3], - np.array(value[-6:]).reshape([6, 1]))) / 209)[0] - - self._yp = np.concatenate(((yp1, yp2), value, (yp3, yp4))) + attest( + len(value) >= 6, + '"y" dependent variable values count must be equal to or ' + "greater than 6!", + ) self._y = value - def __call__(self, x): + yp1 = np.ravel( + ( + np.dot( + self.SPRAGUE_C_COEFFICIENTS[0], + np.array(value[0:6]).reshape([6, 1]), + ) + ) + / 209 + )[0] + yp2 = np.ravel( + ( + np.dot( + self.SPRAGUE_C_COEFFICIENTS[1], + np.array(value[0:6]).reshape([6, 1]), + ) + ) + / 209 + )[0] + yp3 = np.ravel( + ( + np.dot( + self.SPRAGUE_C_COEFFICIENTS[2], + np.array(value[-6:]).reshape([6, 1]), + ) + ) + / 209 + )[0] + yp4 = np.ravel( + ( + np.dot( + self.SPRAGUE_C_COEFFICIENTS[3], + np.array(value[-6:]).reshape([6, 1]), + ) + ) + / 209 + )[0] + + self._yp = np.concatenate( + [ + as_array([yp1, yp2], self._dtype), + value, + as_array([yp3, yp4], self._dtype), + ] + ) + + def __call__(self, x: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Evaluates the interpolating polynomial at given point(s). + Evaluate the interpolating polynomial at given point(s). Parameters ---------- - x : numeric or array_like + x Point(s) to evaluate the interpolant at. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Interpolated value(s). """ - return self._evaluate(x) + x = np.atleast_1d(x).astype(self._dtype) - def _evaluate(self, x): + xi = as_float(self._evaluate(x)) + + return xi + + def _evaluate(self, x: NDArray) -> NDArray: """ - Performs the interpolating polynomial evaluation at given point. + Perform the interpolating polynomial evaluation at given point. Parameters ---------- - x : numeric + x Point to evaluate the interpolant at. Returns ------- - float + :class:`numpy.ndarray` Interpolated point values. """ - x = as_float_array(x) - self._validate_dimensions() self._validate_interpolation_range(x) @@ -1185,51 +1230,73 @@ def _evaluate(self, x): r = self._yp a0p = r[i] - a1p = ((2 * r[i - 2] - 16 * r[i - 1] + 16 * r[i + 1] - - 2 * r[i + 2]) / 24) # yapf: disable - a2p = ((-r[i - 2] + 16 * r[i - 1] - 30 * r[i] + 16 * r[i + 1] - - r[i + 2]) / 24) # yapf: disable - a3p = ((-9 * r[i - 2] + 39 * r[i - 1] - 70 * r[i] + 66 * r[i + 1] - - 33 * r[i + 2] + 7 * r[i + 3]) / 24) - a4p = ((13 * r[i - 2] - 64 * r[i - 1] + 126 * r[i] - 124 * r[i + 1] + - 61 * r[i + 2] - 12 * r[i + 3]) / 24) - a5p = ((-5 * r[i - 2] + 25 * r[i - 1] - 50 * r[i] + 50 * r[i + 1] - - 25 * r[i + 2] + 5 * r[i + 3]) / 24) - - y = (a0p + a1p * X + a2p * X ** 2 + a3p * X ** 3 + a4p * X ** 4 + - a5p * X ** 5) + a1p = ( + 2 * r[i - 2] - 16 * r[i - 1] + 16 * r[i + 1] - 2 * r[i + 2] + ) / 24 + a2p = ( + -r[i - 2] + 16 * r[i - 1] - 30 * r[i] + 16 * r[i + 1] - r[i + 2] + ) / 24 + a3p = ( + -9 * r[i - 2] + + 39 * r[i - 1] + - 70 * r[i] + + 66 * r[i + 1] + - 33 * r[i + 2] + + 7 * r[i + 3] + ) / 24 + a4p = ( + 13 * r[i - 2] + - 64 * r[i - 1] + + 126 * r[i] + - 124 * r[i + 1] + + 61 * r[i + 2] + - 12 * r[i + 3] + ) / 24 + a5p = ( + -5 * r[i - 2] + + 25 * r[i - 1] + - 50 * r[i] + + 50 * r[i + 1] + - 25 * r[i + 2] + + 5 * r[i + 3] + ) / 24 + + y = ( + a0p + + a1p * X + + a2p * X**2 + + a3p * X**3 + + a4p * X**4 + + a5p * X**5 + ) return y def _validate_dimensions(self): - """ - Validates variables dimensions to be the same. - """ + """Validate that the variables dimensions are the same.""" if len(self._x) != len(self._y): raise ValueError( - ('"x" independent and "y" dependent variables have different ' - 'dimensions: "{0}", "{1}"').format( - len(self._x), len(self._y))) + '"x" independent and "y" dependent variables have different ' + f'dimensions: "{len(self._x)}", "{len(self._y)}"' + ) - def _validate_interpolation_range(self, x): - """ - Validates given point to be in interpolation range. - """ + def _validate_interpolation_range(self, x: NDArray): + """Validate given point to be in interpolation range.""" below_interpolation_range = x < self._x[0] above_interpolation_range = x > self._x[-1] if below_interpolation_range.any(): - raise ValueError('"{0}" is below interpolation range.'.format(x)) + raise ValueError(f'"{x}" is below interpolation range.') if above_interpolation_range.any(): - raise ValueError('"{0}" is above interpolation range.'.format(x)) + raise ValueError(f'"{x}" is above interpolation range.') class CubicSplineInterpolator(scipy.interpolate.interp1d): """ - Interpolates a 1-D function using cubic spline interpolation. + Interpolate a 1-D function using cubic spline interpolation. Methods ------- @@ -1240,14 +1307,13 @@ class CubicSplineInterpolator(scipy.interpolate.interp1d): - This class is a wrapper around *scipy.interpolate.interp1d* class. """ - def __init__(self, *args, **kwargs): - super(CubicSplineInterpolator, self).__init__( - kind='cubic', *args, **kwargs) + def __init__(self, *args: Any, **kwargs: Any): + super().__init__(kind="cubic", *args, **kwargs) class PchipInterpolator(scipy.interpolate.PchipInterpolator): """ - Interpolates a 1-D function using Piecewise Cubic Hermite Interpolating + Interpolate a 1-D function using Piecewise Cubic Hermite Interpolating Polynomial interpolation. Attributes @@ -1264,47 +1330,47 @@ class PchipInterpolator(scipy.interpolate.PchipInterpolator): class. """ - def __init__(self, x, y, *args, **kwargs): - super(PchipInterpolator, self).__init__(x, y, *args, **kwargs) + def __init__(self, x: ArrayLike, y: ArrayLike, *args: Any, **kwargs: Any): + super().__init__(x, y, *args, **kwargs) - self._y = y + self._y: NDArray = as_float_array(y) @property - def y(self): + def y(self) -> NDArray: """ Getter property for the dependent and already known :math:`y` variable. Returns ------- - array_like + :class:`numpy.ndarray` Dependent and already known :math:`y` variable. """ return self._y -class NullInterpolator(object): +class NullInterpolator: """ - Performs 1-D function null interpolation, i.e. a call within given + Perform 1-D function null interpolation, i.e. a call within given tolerances will return existing :math:`y` variable values and ``default`` if outside tolerances. Parameters ---------- - x : ndarray + x Independent :math:`x` variable values corresponding with :math:`y` variable. - y : ndarray + y Dependent and already known :math:`y` variable values to interpolate. - absolute_tolerance : numeric, optional + absolute_tolerance Absolute tolerance. - relative_tolerance : numeric, optional + relative_tolerance Relative tolerance. - default : numeric, optional + default Default value for interpolation outside tolerances. - dtype : type + dtype Data type used for internal conversions. Attributes @@ -1335,25 +1401,28 @@ class NullInterpolator(object): 9.3699999... """ - def __init__(self, - x, - y, - absolute_tolerance=10e-7, - relative_tolerance=10e-7, - default=np.nan, - dtype=None): - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE - - self._x = None - self._y = None - self._absolute_tolerance = None - self._relative_tolerance = None - self._default = None - self._dtype = dtype - - self.x = x - self.y = y + def __init__( + self, + x: ArrayLike, + y: ArrayLike, + absolute_tolerance: Floating = 10e-7, + relative_tolerance: Floating = 10e-7, + default: Floating = np.nan, + dtype: Optional[Type[DTypeNumber]] = None, + ): + dtype = cast(Type[DTypeNumber], optional(dtype, DEFAULT_FLOAT_DTYPE)) + + self._x: NDArray = np.array([]) + self._y: NDArray = np.array([]) + self._absolute_tolerance: float = 10e-7 + self._relative_tolerance: float = 10e-7 + self._default: Floating = np.nan + self._dtype: Type[DTypeNumber] = dtype + + # TODO: Remove pragma when https://github.com/python/mypy/issues/3004 + # is resolved. + self.x = x # type: ignore[assignment] + self.y = y # type: ignore[assignment] self.absolute_tolerance = absolute_tolerance self.relative_tolerance = relative_tolerance self.default = default @@ -1361,175 +1430,167 @@ def __init__(self, self._validate_dimensions() @property - def x(self): + def x(self) -> NDArray: """ Getter and setter property for the independent :math:`x` variable. Parameters ---------- - value : array_like + value Value to set the independent :math:`x` variable with. Returns ------- - array_like + :class:`numpy.ndarray` Independent :math:`x` variable. """ return self._x @x.setter - def x(self, value): - """ - Setter for the **self.x** property. - """ + def x(self, value: ArrayLike): + """Setter for the **self.x** property.""" - if value is not None: - value = np.atleast_1d(value).astype(self._dtype) + value = np.atleast_1d(value).astype(self._dtype) - assert value.ndim == 1, ( - '"x" independent variable must have exactly one dimension!') + attest( + value.ndim == 1, + '"x" independent variable must have exactly one dimension!', + ) self._x = value @property - def y(self): + def y(self) -> NDArray: """ Getter and setter property for the dependent and already known :math:`y` variable. Parameters ---------- - value : array_like + value Value to set the dependent and already known :math:`y` variable with. Returns ------- - array_like + :class:`numpy.ndarray` Dependent and already known :math:`y` variable. """ return self._y @y.setter - def y(self, value): - """ - Setter for the **self.y** property. - """ + def y(self, value: ArrayLike): + """Setter for the **self.y** property.""" - if value is not None: - value = np.atleast_1d(value).astype(self._dtype) + value = np.atleast_1d(value).astype(self._dtype) - assert value.ndim == 1, ( - '"y" dependent variable must have exactly one dimension!') + attest( + value.ndim == 1, + '"y" dependent variable must have exactly one dimension!', + ) self._y = value @property - def relative_tolerance(self): + def relative_tolerance(self) -> Floating: """ Getter and setter property for the relative tolerance. Parameters ---------- - value : numeric + value Value to set the relative tolerance with. Returns ------- - numeric + :class:`numpy.floating` Relative tolerance. """ return self._relative_tolerance @relative_tolerance.setter - def relative_tolerance(self, value): - """ - Setter for the **self.relative_tolerance** property. - """ + def relative_tolerance(self, value: Floating): + """Setter for the **self.relative_tolerance** property.""" - if value is not None: - assert is_numeric(value), ( - '"relative_tolerance" variable must be a "numeric"!') + attest( + is_numeric(value), + '"relative_tolerance" variable must be a "numeric"!', + ) - self._relative_tolerance = value + self._relative_tolerance = float(value) @property - def absolute_tolerance(self): + def absolute_tolerance(self) -> Floating: """ Getter and setter property for the absolute tolerance. Parameters ---------- - value : numeric + value Value to set the absolute tolerance with. Returns ------- - numeric + :class:`numpy.floating` Absolute tolerance. """ return self._absolute_tolerance @absolute_tolerance.setter - def absolute_tolerance(self, value): - """ - Setter for the **self.absolute_tolerance** property. - """ + def absolute_tolerance(self, value: Floating): + """Setter for the **self.absolute_tolerance** property.""" - if value is not None: - assert is_numeric(value), ( - '"absolute_tolerance" variable must be a "numeric"!') + attest( + is_numeric(value), + '"absolute_tolerance" variable must be a "numeric"!', + ) - self._absolute_tolerance = value + self._absolute_tolerance = float(value) @property - def default(self): + def default(self) -> Floating: """ Getter and setter property for the default value for call outside tolerances. Parameters ---------- - value : numeric + value Value to set the default value with. Returns ------- - numeric + :class:`numpy.floating` Default value. """ return self._default @default.setter - def default(self, value): - """ - Setter for the **self.default** property. - """ + def default(self, value: Floating): + """Setter for the **self.default** property.""" - if value is not None: - assert is_numeric(value), ( - '"default" variable must be a "numeric"!') + attest(is_numeric(value), '"default" variable must be a "numeric"!') self._default = value - def __call__(self, x): + def __call__(self, x: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Evaluates the interpolator at given point(s). + Evaluate the interpolator at given point(s). Parameters ---------- - x : numeric or array_like + x Point(s) to evaluate the interpolant at. Returns ------- - float or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Interpolated value(s). """ @@ -1539,18 +1600,18 @@ def __call__(self, x): return xi - def _evaluate(self, x): + def _evaluate(self, x: NDArray) -> NDArray: """ - Performs the interpolator evaluation at given points. + Perform the interpolator evaluation at given points. Parameters ---------- - x : ndarray + x Points to evaluate the interpolant at. Returns ------- - ndarray + :class:`numpy.ndarray` Interpolated points values. """ @@ -1559,55 +1620,54 @@ def _evaluate(self, x): indexes = closest_indexes(self._x, x) values = self._y[indexes] - values[~np.isclose( - self._x[indexes], - x, - rtol=self._absolute_tolerance, - atol=self._relative_tolerance)] = self._default + values[ + ~np.isclose( + self._x[indexes], + x, + rtol=self._absolute_tolerance, + atol=self._relative_tolerance, + ) + ] = self._default return values def _validate_dimensions(self): - """ - Validates variables dimensions to be the same. - """ + """Validate that the variables dimensions are the same.""" if len(self._x) != len(self._y): raise ValueError( - ('"x" independent and "y" dependent variables have different ' - 'dimensions: "{0}", "{1}"').format( - len(self._x), len(self._y))) + '"x" independent and "y" dependent variables have different ' + f'dimensions: "{len(self._x)}", "{len(self._y)}"' + ) - def _validate_interpolation_range(self, x): - """ - Validates given point to be in interpolation range. - """ + def _validate_interpolation_range(self, x: NDArray): + """Validate given point to be in interpolation range.""" below_interpolation_range = x < self._x[0] above_interpolation_range = x > self._x[-1] if below_interpolation_range.any(): - raise ValueError('"{0}" is below interpolation range.'.format(x)) + raise ValueError(f'"{x}" is below interpolation range.') if above_interpolation_range.any(): - raise ValueError('"{0}" is above interpolation range.'.format(x)) + raise ValueError(f'"{x}" is above interpolation range.') -def lagrange_coefficients(r, n=4): +def lagrange_coefficients(r: Floating, n: Integer = 4) -> NDArray: """ - Computes the *Lagrange Coefficients* at given point :math:`r` for degree + Compute the *Lagrange Coefficients* at given point :math:`r` for degree :math:`n`. Parameters ---------- - r : numeric + r Point to get the *Lagrange Coefficients* at. - n : int, optional + n Degree of the *Lagrange Coefficients* being calculated. Returns ------- - ndarray + :class:`numpy.ndarray` References ---------- @@ -1622,29 +1682,32 @@ def lagrange_coefficients(r, n=4): r_i = np.arange(n) L_n = [] for j in range(len(r_i)): - basis = [(r - r_i[i]) / (r_i[j] - r_i[i]) for i in range(len(r_i)) - if i != j] + basis = [ + (r - r_i[i]) / (r_i[j] - r_i[i]) for i in range(len(r_i)) if i != j + ] L_n.append(reduce(lambda x, y: x * y, basis)) # noqa return np.array(L_n) -def vertices_and_relative_coordinates(V_xyz, table): +def vertices_and_relative_coordinates( + V_xyz: ArrayLike, table: ArrayLike +) -> Tuple[NDArray, NDArray]: """ - Computes the vertices coordinates and indexes relative :math:`V_{xyzr}` + Compute the vertices coordinates and indexes relative :math:`V_{xyzr}` coordinates from given :math:`V_{xyzr}` values and interpolation table. Parameters ---------- - V_xyz : array_like + V_xyz :math:`V_{xyz}` values to transform to indexes relative :math:`V_{xyzr}` values. - table : array_like + table 4-Dimensional (NxNxNx3) interpolation table. Returns ------- - tuple + :class:`tuple` Vertices coordinates and indexes relative :math:`V_{xyzr}` coordinates. Examples @@ -1710,7 +1773,8 @@ def vertices_and_relative_coordinates(V_xyz, table): # table axis, ``i_f`` and ``i_c`` respectively the floor and ceiling # indexes encompassing a given V_xyz value. i_m = np.array(table.shape[0:-1]) - 1 - i_f = np.floor(V_xyz * i_m).astype(DEFAULT_INT_DTYPE) + i_f = as_int_array(np.floor(V_xyz * i_m)) + i_f = np.clip(i_f, 0, i_m) i_c = np.clip(i_f + 1, 0, i_m) # Relative to indexes ``V_xyz`` values. @@ -1721,29 +1785,35 @@ def vertices_and_relative_coordinates(V_xyz, table): # Vertices computations by indexing ``table`` with the ``i_f`` and ``i_c`` # indexes. 8 encompassing vertices are computed for a given V_xyz value # forming a cube around it: - vertices = np.array([ - table[i_f_c[i[0]][..., 0], i_f_c[i[1]][..., 1], i_f_c[i[2]][..., 2]] - for i in itertools.product(*zip([0, 0, 0], [1, 1, 1])) - ]) + vertices = np.array( + [ + table[ + i_f_c[i[0]][..., 0], i_f_c[i[1]][..., 1], i_f_c[i[2]][..., 2] + ] + for i in itertools.product(*zip([0, 0, 0], [1, 1, 1])) + ] + ) return vertices, V_xyzr -def table_interpolation_trilinear(V_xyz, table): +def table_interpolation_trilinear( + V_xyz: ArrayLike, table: ArrayLike +) -> NDArray: """ - Performs trilinear interpolation of given :math:`V_{xyz}` values using + Perform the trilinear interpolation of given :math:`V_{xyz}` values using given interpolation table. Parameters ---------- - V_xyz : array_like + V_xyz :math:`V_{xyz}` values to interpolate. - table : array_like + table 4-Dimensional (NxNxNx3) interpolation table. Returns ------- - ndarray + :class:`numpy.ndarray` Interpolated :math:`V_{xyz}` values. References @@ -1776,34 +1846,47 @@ def table_interpolation_trilinear(V_xyz, table): vertices, V_xyzr = vertices_and_relative_coordinates(V_xyz, table) vertices = np.moveaxis(vertices, 0, 1) - x, y, z = [f[:, np.newaxis] for f in tsplit(V_xyzr)] + x, y, z = (f[:, np.newaxis] for f in tsplit(V_xyzr)) weights = np.moveaxis( np.transpose( - [(1 - x) * (1 - y) * (1 - z), (1 - x) * (1 - y) * z, - (1 - x) * y * (1 - z), (1 - x) * y * z, x * (1 - y) * (1 - z), - x * (1 - y) * z, x * y * (1 - z), x * y * z]), 0, -1) + [ + (1 - x) * (1 - y) * (1 - z), + (1 - x) * (1 - y) * z, + (1 - x) * y * (1 - z), + (1 - x) * y * z, + x * (1 - y) * (1 - z), + x * (1 - y) * z, + x * y * (1 - z), + x * y * z, + ] + ), + 0, + -1, + ) xyz_o = np.reshape(np.sum(vertices * weights, 1), V_xyz.shape) return xyz_o -def table_interpolation_tetrahedral(V_xyz, table): +def table_interpolation_tetrahedral( + V_xyz: ArrayLike, table: ArrayLike +) -> NDArray: """ - Performs tetrahedral interpolation of given :math:`V_{xyz}` values using + Perform the tetrahedral interpolation of given :math:`V_{xyz}` values using given interpolation table. Parameters ---------- - V_xyz : array_like + V_xyz :math:`V_{xyz}` values to interpolate. - table : array_like + table 4-Dimensional (NxNxNx3) interpolation table. Returns ------- - ndarray + :class:`numpy.ndarray` Interpolated :math:`V_{xyz}` values. References @@ -1837,63 +1920,68 @@ def table_interpolation_tetrahedral(V_xyz, table): vertices = np.moveaxis(vertices, 0, -1) V000, V001, V010, V011, V100, V101, V110, V111 = tsplit(vertices) - x, y, z = [r[:, np.newaxis] for r in tsplit(V_xyzr)] - - xyz_o = np.select([ - np.logical_and(x > y, y > z), - np.logical_and(x > y, x > z), - np.logical_and(x > y, np.logical_and(y <= z, x <= z)), - np.logical_and(x <= y, z > y), - np.logical_and(x <= y, z > x), - np.logical_and(x <= y, np.logical_and(z <= y, z <= x)), - ], [ - (1 - x) * V000 + (x - y) * V100 + (y - z) * V110 + z * V111, - (1 - x) * V000 + (x - z) * V100 + (z - y) * V101 + y * V111, - (1 - z) * V000 + (z - x) * V001 + (x - y) * V101 + y * V111, - (1 - z) * V000 + (z - y) * V001 + (y - x) * V011 + x * V111, - (1 - y) * V000 + (y - z) * V010 + (z - x) * V011 + x * V111, - (1 - y) * V000 + (y - x) * V010 + (x - z) * V110 + z * V111, - ]) + x, y, z = (r[:, np.newaxis] for r in tsplit(V_xyzr)) + + xyz_o = np.select( + [ + np.logical_and(x > y, y > z), + np.logical_and(x > y, x > z), + np.logical_and(x > y, np.logical_and(y <= z, x <= z)), + np.logical_and(x <= y, z > y), + np.logical_and(x <= y, z > x), + np.logical_and(x <= y, np.logical_and(z <= y, z <= x)), + ], + [ + (1 - x) * V000 + (x - y) * V100 + (y - z) * V110 + z * V111, + (1 - x) * V000 + (x - z) * V100 + (z - y) * V101 + y * V111, + (1 - z) * V000 + (z - x) * V001 + (x - y) * V101 + y * V111, + (1 - z) * V000 + (z - y) * V001 + (y - x) * V011 + x * V111, + (1 - y) * V000 + (y - z) * V010 + (z - x) * V011 + x * V111, + (1 - y) * V000 + (y - x) * V010 + (x - z) * V110 + z * V111, + ], + ) xyz_o = np.reshape(xyz_o, V_xyz.shape) return xyz_o -TABLE_INTERPOLATION_METHODS = CaseInsensitiveMapping({ - 'Trilinear': table_interpolation_trilinear, - 'Tetrahedral': table_interpolation_tetrahedral, -}) +TABLE_INTERPOLATION_METHODS = CaseInsensitiveMapping( + { + "Trilinear": table_interpolation_trilinear, + "Tetrahedral": table_interpolation_tetrahedral, + } +) TABLE_INTERPOLATION_METHODS.__doc__ = """ Supported table interpolation methods. References ---------- :cite:`Bourkeb`, :cite:`Kirk2006` - -TABLE_INTERPOLATION_METHODS : CaseInsensitiveMapping - **{'Trilinear', 'Tetrahedral'}** """ -def table_interpolation(V_xyz, table, method='Trilinear'): +def table_interpolation( + V_xyz: ArrayLike, + table: ArrayLike, + method: Union[Literal["Trilinear", "Tetrahedral"], str] = "Trilinear", +) -> NDArray: """ - Performs interpolation of given :math:`V_{xyz}` values using given + Perform interpolation of given :math:`V_{xyz}` values using given interpolation table. Parameters ---------- - V_xyz : array_like + V_xyz :math:`V_{xyz}` values to interpolate. - table : array_like + table 4-Dimensional (NxNxNx3) interpolation table. - method : unicode, optional - **{'Trilinear', 'Tetrahedral'}**, + method Interpolation method. Returns ------- - ndarray + :class:`numpy.ndarray` Interpolated :math:`V_{xyz}` values. References @@ -1926,4 +2014,6 @@ def table_interpolation(V_xyz, table, method='Trilinear'): [ 1.1178206..., 0.1762039..., 0.2209534...]]) """ - return TABLE_INTERPOLATION_METHODS.get(method)(V_xyz, table) + method = validate_method(method, TABLE_INTERPOLATION_METHODS) + + return TABLE_INTERPOLATION_METHODS[method](V_xyz, table) diff --git a/colour/algebra/matrix.py b/colour/algebra/matrix.py deleted file mode 100644 index 666bd9a065..0000000000 --- a/colour/algebra/matrix.py +++ /dev/null @@ -1,47 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Matrix Helpers -============== - -Defines matrices computation helpers objects. -""" - -from __future__ import division, unicode_literals - -import numpy as np - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['is_identity'] - - -def is_identity(a, n=3): - """ - Returns if :math:`a` array is an identity matrix. - - Parameters - ---------- - a : array_like, (N) - Variable :math:`a` to test. - n : int, optional - Matrix dimension. - - Returns - ------- - bool - Is identity matrix. - - Examples - -------- - >>> is_identity(np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]).reshape(3, 3)) - True - >>> is_identity(np.array([1, 2, 0, 0, 1, 0, 0, 0, 1]).reshape(3, 3)) - False - """ - - return np.array_equal(np.identity(n), a) diff --git a/colour/algebra/random.py b/colour/algebra/random.py index 866fcb9da4..53c2614c75 100644 --- a/colour/algebra/random.py +++ b/colour/algebra/random.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Random Numbers Utilities ======================== -Defines random numbers generator objects: +Defines the random number generator objects: - :func:`colour.algebra.random_triplet_generator` References @@ -15,44 +14,49 @@ reproducibility-of-python-pseudo-random-numbers-across-systems-and-versions """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.constants import DEFAULT_INT_DTYPE -from colour.utilities import runtime_warning, tstack +from colour.hints import ArrayLike, Integer, NDArray +from colour.utilities import as_float_array, tstack -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['RANDOM_STATE', 'random_triplet_generator'] +__all__ = [ + "RANDOM_STATE", + "random_triplet_generator", +] RANDOM_STATE = np.random.RandomState() -def random_triplet_generator(size, - limits=np.array([[0, 1], [0, 1], [0, 1]]), - random_state=RANDOM_STATE): +def random_triplet_generator( + size: Integer, + limits: ArrayLike = np.array([[0, 1], [0, 1], [0, 1]]), + random_state: np.random.RandomState = RANDOM_STATE, +) -> NDArray: """ - Returns a generator yielding random triplets. + Return a generator yielding random triplets. Parameters ---------- - size : int + size Generator size. - limits : array_like, (3, 2) + limits Random values limits on each triplet axis. - random_state : RandomState + random_state Mersenne Twister pseudo-random number generator. Returns ------- - generator - Random triplets generator. + :class:`numpy.ndarray` + Random triplet generator. Notes ----- @@ -80,13 +84,12 @@ def random_triplet_generator(size, [ 0.4347915..., 0.9566529..., 0.4084438...]]) """ - integer_size = DEFAULT_INT_DTYPE(size) - if integer_size != size: - runtime_warning( - '"size" has been cast to integer: {0}'.format(integer_size)) + limit_x, limit_y, limit_z = as_float_array(limits) - return tstack([ - random_state.uniform(*limits[0], size=integer_size), - random_state.uniform(*limits[1], size=integer_size), - random_state.uniform(*limits[2], size=integer_size), - ]) + return tstack( + [ + random_state.uniform(limit_x[0], limit_x[1], size=size), + random_state.uniform(limit_y[0], limit_y[1], size=size), + random_state.uniform(limit_z[0], limit_z[1], size=size), + ] + ) diff --git a/colour/algebra/regression.py b/colour/algebra/regression.py index 29e9faa083..eae946c466 100644 --- a/colour/algebra/regression.py +++ b/colour/algebra/regression.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Regression ========== @@ -16,36 +15,40 @@ doi:10.1109/TIP.2015.2405336 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import ArrayLike, NDArray -__all__ = ['least_square_mapping_MoorePenrose'] +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "least_square_mapping_MoorePenrose", +] -def least_square_mapping_MoorePenrose(y, x): + +def least_square_mapping_MoorePenrose(y: ArrayLike, x: ArrayLike) -> NDArray: """ - Computes the *least-squares* mapping from dependent variable :math:`y` to + Compute the *least-squares* mapping from dependent variable :math:`y` to independent variable :math:`x` using *Moore-Penrose* inverse. Parameters ---------- - y : array_like + y Dependent and already known :math:`y` variable. - x : array_like, optional + x Independent :math:`x` variable(s) values corresponding with :math:`y` variable. Returns ------- - ndarray + :class:`numpy.ndarray` *Least-squares* mapping. References diff --git a/colour/algebra/tests/__init__.py b/colour/algebra/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/algebra/tests/__init__.py +++ b/colour/algebra/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/algebra/tests/test_common.py b/colour/algebra/tests/test_common.py index a56ca2555f..2c07c2c87e 100644 --- a/colour/algebra/tests/test_common.py +++ b/colour/algebra/tests/test_common.py @@ -1,39 +1,52 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.algebra.common` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.algebra.common` module.""" import numpy as np import unittest -from colour.algebra import (is_spow_enabled, set_spow_enable, spow_enable, - spow, smoothstep_function) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.algebra import ( + is_spow_enabled, + set_spow_enable, + spow_enable, + spow, + smoothstep_function, + normalise_maximum, + vector_dot, + matrix_dot, + linear_conversion, + linstep_function, + is_identity, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'TestIsSpowEnabled', 'TestSetSpowEnabled', 'TestSpowEnable', 'TestSpow', - 'TestSmoothstepFunction' + "TestIsSpowEnabled", + "TestSetSpowEnabled", + "TestSpowEnable", + "TestSpow", + "TestSmoothstepFunction", + "TestNormaliseMaximum", + "TestVectorDot", + "TestMatrixDot", + "TestLinearConversion", + "TestLinstepFunction", + "TestIsIdentity", ] class TestIsSpowEnabled(unittest.TestCase): """ - Defines :func:`colour.algebra.common.is_spow_enabled` definition unit + Define :func:`colour.algebra.common.is_spow_enabled` definition unit tests methods. """ def test_is_spow_enabled(self): - """ - Tests :func:`colour.algebra.common.is_spow_enabled` definition. - """ + """Test :func:`colour.algebra.common.is_spow_enabled` definition.""" with spow_enable(True): self.assertTrue(is_spow_enabled()) @@ -44,14 +57,12 @@ def test_is_spow_enabled(self): class TestSetSpowEnabled(unittest.TestCase): """ - Defines :func:`colour.algebra.common.set_spow_enable` definition unit + Define :func:`colour.algebra.common.set_spow_enable` definition unit tests methods. """ def test_set_spow_enable(self): - """ - Tests :func:`colour.algebra.common.set_spow_enable` definition. - """ + """Test :func:`colour.algebra.common.set_spow_enable` definition.""" with spow_enable(is_spow_enabled()): set_spow_enable(True) @@ -64,14 +75,12 @@ def test_set_spow_enable(self): class TestSpowEnable(unittest.TestCase): """ - Defines :func:`colour.algebra.common.spow_enable` definition unit + Define :func:`colour.algebra.common.spow_enable` definition unit tests methods. """ def test_spow_enable(self): - """ - Tests :func:`colour.algebra.common.spow_enable` definition. - """ + """Test :func:`colour.algebra.common.spow_enable` definition.""" with spow_enable(True): self.assertTrue(is_spow_enabled()) @@ -81,9 +90,7 @@ def test_spow_enable(self): @spow_enable(True) def fn_a(): - """ - :func:`spow_enable` unit tests :func:`fn_a` definition. - """ + """:func:`spow_enable` unit tests :func:`fn_a` definition.""" self.assertTrue(is_spow_enabled()) @@ -91,9 +98,7 @@ def fn_a(): @spow_enable(False) def fn_b(): - """ - :func:`spow_enable` unit tests :func:`fn_b` definition. - """ + """:func:`spow_enable` unit tests :func:`fn_b` definition.""" self.assertFalse(is_spow_enabled()) @@ -102,14 +107,12 @@ def fn_b(): class TestSpow(unittest.TestCase): """ - Defines :func:`colour.algebra.common.spow` definition unit + Define :func:`colour.algebra.common.spow` definition unit tests methods. """ def test_spow(self): - """ - Tests :func:`colour.algebra.common.spow` definition. - """ + """Test :func:`colour.algebra.common.spow` definition.""" self.assertEqual(spow(2, 2), 4.0) @@ -118,26 +121,292 @@ def test_spow(self): np.testing.assert_almost_equal( spow([2, -2, -2, 0], [2, 2, 0.15, 0]), np.array([4.00000000, -4.00000000, -1.10956947, 0.00000000]), - decimal=7) + decimal=7, + ) with spow_enable(True): np.testing.assert_almost_equal( - spow(-2, 0.15), -1.10956947, decimal=7) + spow(-2, 0.15), -1.10956947, decimal=7 + ) with spow_enable(False): np.testing.assert_equal(spow(-2, 0.15), np.nan) +class TestNormaliseMaximum(unittest.TestCase): + """ + Define :func:`colour.utilities.array.normalise_maximum` definition unit + tests methods. + """ + + def test_normalise_maximum(self): + """Test :func:`colour.utilities.array.normalise_maximum` definition.""" + + np.testing.assert_almost_equal( + normalise_maximum(np.array([0.20654008, 0.12197225, 0.05136952])), + np.array([1.00000000, 0.59055003, 0.24871454]), + decimal=7, + ) + + np.testing.assert_almost_equal( + normalise_maximum( + np.array( + [ + [0.20654008, 0.12197225, 0.05136952], + [0.14222010, 0.23042768, 0.10495772], + [0.07818780, 0.06157201, 0.28099326], + ] + ) + ), + np.array( + [ + [0.73503571, 0.43407536, 0.18281406], + [0.50613349, 0.82004700, 0.37352398], + [0.27825507, 0.21912273, 1.00000000], + ] + ), + decimal=7, + ) + + np.testing.assert_almost_equal( + normalise_maximum( + np.array( + [ + [0.20654008, 0.12197225, 0.05136952], + [0.14222010, 0.23042768, 0.10495772], + [0.07818780, 0.06157201, 0.28099326], + ] + ), + axis=-1, + ), + np.array( + [ + [1.00000000, 0.59055003, 0.24871454], + [0.61720059, 1.00000000, 0.45549094], + [0.27825507, 0.21912273, 1.00000000], + ] + ), + decimal=7, + ) + + np.testing.assert_almost_equal( + normalise_maximum( + np.array([0.20654008, 0.12197225, 0.05136952]), factor=10 + ), + np.array([10.00000000, 5.90550028, 2.48714535]), + decimal=7, + ) + + np.testing.assert_almost_equal( + normalise_maximum( + np.array([-0.11518475, -0.10080000, 0.05089373]) + ), + np.array([0.00000000, 0.00000000, 1.00000000]), + decimal=7, + ) + + np.testing.assert_almost_equal( + normalise_maximum( + np.array([-0.20654008, -0.12197225, 0.05136952]), clip=False + ), + np.array([-4.02067374, -2.37440899, 1.00000000]), + decimal=7, + ) + + +class TestVectorDot(unittest.TestCase): + """ + Define :func:`colour.utilities.array.vector_dot` definition unit tests + methods. + """ + + def test_vector_dot(self): + """Test :func:`colour.utilities.array.vector_dot` definition.""" + + m = np.array( + [ + [0.7328, 0.4296, -0.1624], + [-0.7036, 1.6975, 0.0061], + [0.0030, 0.0136, 0.9834], + ] + ) + m = np.reshape(np.tile(m, (6, 1)), (6, 3, 3)) + + v = np.array([0.20654008, 0.12197225, 0.05136952]) + v = np.tile(v, (6, 1)) + + np.testing.assert_almost_equal( + vector_dot(m, v), + np.array( + [ + [0.19540944, 0.06203965, 0.05279523], + [0.19540944, 0.06203965, 0.05279523], + [0.19540944, 0.06203965, 0.05279523], + [0.19540944, 0.06203965, 0.05279523], + [0.19540944, 0.06203965, 0.05279523], + [0.19540944, 0.06203965, 0.05279523], + ] + ), + decimal=7, + ) + + +class TestMatrixDot(unittest.TestCase): + """ + Define :func:`colour.utilities.array.matrix_dot` definition unit tests + methods. + """ + + def test_matrix_dot(self): + """Test :func:`colour.utilities.array.matrix_dot` definition.""" + + a = np.array( + [ + [0.7328, 0.4296, -0.1624], + [-0.7036, 1.6975, 0.0061], + [0.0030, 0.0136, 0.9834], + ] + ) + a = np.reshape(np.tile(a, (6, 1)), (6, 3, 3)) + + b = a + + np.testing.assert_almost_equal( + matrix_dot(a, b), + np.array( + [ + [ + [0.23424208, 1.04184824, -0.27609032], + [-1.70994078, 2.57932265, 0.13061813], + [-0.00442036, 0.03774904, 0.96667132], + ], + [ + [0.23424208, 1.04184824, -0.27609032], + [-1.70994078, 2.57932265, 0.13061813], + [-0.00442036, 0.03774904, 0.96667132], + ], + [ + [0.23424208, 1.04184824, -0.27609032], + [-1.70994078, 2.57932265, 0.13061813], + [-0.00442036, 0.03774904, 0.96667132], + ], + [ + [0.23424208, 1.04184824, -0.27609032], + [-1.70994078, 2.57932265, 0.13061813], + [-0.00442036, 0.03774904, 0.96667132], + ], + [ + [0.23424208, 1.04184824, -0.27609032], + [-1.70994078, 2.57932265, 0.13061813], + [-0.00442036, 0.03774904, 0.96667132], + ], + [ + [0.23424208, 1.04184824, -0.27609032], + [-1.70994078, 2.57932265, 0.13061813], + [-0.00442036, 0.03774904, 0.96667132], + ], + ] + ), + decimal=7, + ) + + +class TestLinearConversion(unittest.TestCase): + """ + Define :func:`colour.utilities.array.linear_conversion` definition unit + tests methods. + """ + + def test_linear_conversion(self): + """Test :func:`colour.utilities.array.linear_conversion` definition.""" + + np.testing.assert_almost_equal( + linear_conversion( + np.linspace(0, 1, 10), np.array([0, 1]), np.array([1, np.pi]) + ), + np.array( + [ + 1.00000000, + 1.23795474, + 1.47590948, + 1.71386422, + 1.95181896, + 2.18977370, + 2.42772844, + 2.66568318, + 2.90363791, + 3.14159265, + ] + ), + decimal=8, + ) + + +class TestLinstepFunction(unittest.TestCase): + """ + Define :func:`colour.utilities.array.linstep_function` definition unit + tests methods. + """ + + def test_linstep_function(self): + """Test :func:`colour.utilities.array.linstep_function` definition.""" + + np.testing.assert_almost_equal( + linstep_function( + np.linspace(0, 1, 10), + np.linspace(0, 1, 10), + np.linspace(0, 2, 10), + ), + np.array( + [ + 0.00000000, + 0.12345679, + 0.27160494, + 0.44444444, + 0.64197531, + 0.86419753, + 1.11111111, + 1.38271605, + 1.67901235, + 2.00000000, + ] + ), + decimal=8, + ) + + np.testing.assert_almost_equal( + linstep_function( + np.linspace(0, 2, 10), + np.linspace(0.25, 0.5, 10), + np.linspace(0.5, 0.75, 10), + clip=True, + ), + np.array( + [ + 0.25000000, + 0.33333333, + 0.41666667, + 0.50000000, + 0.58333333, + 0.63888889, + 0.66666667, + 0.69444444, + 0.72222222, + 0.75000000, + ] + ), + decimal=8, + ) + + class TestSmoothstepFunction(unittest.TestCase): """ - Defines :func:`colour.algebra.common.smoothstep_function` definition unit + Define :func:`colour.algebra.common.smoothstep_function` definition unit tests methods. """ def test_smoothstep_function(self): - """ - Tests :func:`colour.algebra.common.smoothstep_function` definition. - """ + """Test :func:`colour.algebra.common.smoothstep_function` definition.""" self.assertEqual(smoothstep_function(0.5), 0.5) self.assertEqual(smoothstep_function(0.25), 0.15625) @@ -146,11 +415,35 @@ def test_smoothstep_function(self): x = np.linspace(-2, 2, 5) np.testing.assert_almost_equal( smoothstep_function(x), - np.array([28.00000, 5.00000, 0.00000, 1.00000, -4.00000])) + np.array([28.00000, 5.00000, 0.00000, 1.00000, -4.00000]), + ) np.testing.assert_almost_equal( smoothstep_function(x, -2, 2, clip=True), - np.array([0.00000, 0.15625, 0.50000, 0.84375, 1.00000])) + np.array([0.00000, 0.15625, 0.50000, 0.84375, 1.00000]), + ) + + +class TestIsIdentity(unittest.TestCase): + """ + Define :func:`colour.algebra.matrix.is_identity` definition unit tests + methods. + """ + + def test_is_identity(self): + """Test :func:`colour.algebra.matrix.is_identity` definition.""" + + self.assertTrue( + is_identity(np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]).reshape([3, 3])) + ) + + self.assertFalse( + is_identity(np.array([1, 2, 0, 0, 1, 0, 0, 0, 1]).reshape([3, 3])) + ) + + self.assertTrue(is_identity(np.array([1, 0, 0, 1]).reshape([2, 2]))) + + self.assertFalse(is_identity(np.array([1, 2, 0, 1]).reshape([2, 2]))) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/algebra/tests/test_extrapolation.py b/colour/algebra/tests/test_extrapolation.py index 93743f9f12..d474be0ba3 100644 --- a/colour/algebra/tests/test_extrapolation.py +++ b/colour/algebra/tests/test_extrapolation.py @@ -1,152 +1,166 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.algebra.extrapolation` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.algebra.extrapolation` module.""" import numpy as np import unittest from itertools import permutations -from colour.algebra import (Extrapolator, LinearInterpolator, - CubicSplineInterpolator, PchipInterpolator) +from colour.algebra import ( + Extrapolator, + LinearInterpolator, + CubicSplineInterpolator, + PchipInterpolator, +) from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestExtrapolator'] +__all__ = [ + "TestExtrapolator", +] class TestExtrapolator(unittest.TestCase): """ - Defines :func:`colour.algebra.extrapolation.Extrapolator` class units + Define :class:`colour.algebra.extrapolation.Extrapolator` class unit tests methods. """ def test_required_attributes(self): - """ - Tests presence of required attributes. - """ + """Test the presence of required attributes.""" - required_attributes = ('interpolator', ) + required_attributes = ("interpolator",) for attribute in required_attributes: self.assertIn(attribute, dir(Extrapolator)) def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__init__', ) + required_methods = ("__init__",) for method in required_methods: # pragma: no cover self.assertIn(method, dir(Extrapolator)) def test_interpolator(self): """ - Tests :func:`colour.algebra.extrapolation.Extrapolator.interpolator` + Test :attr:`colour.algebra.extrapolation.Extrapolator.interpolator` property. """ extrapolator = Extrapolator( - LinearInterpolator(np.array([5, 6, 7]), np.array([5, 6, 7]))) + LinearInterpolator(np.array([5, 6, 7]), np.array([5, 6, 7])) + ) self.assertIsInstance(extrapolator.interpolator, LinearInterpolator) def test_method(self): """ - Tests :func:`colour.algebra.extrapolation.Extrapolator.method` + Test :attr:`colour.algebra.extrapolation.Extrapolator.method` property. """ extrapolator = Extrapolator( - LinearInterpolator(np.array([5, 6, 7]), np.array([5, 6, 7]))) - self.assertEqual(extrapolator.method, 'linear') + LinearInterpolator(np.array([5, 6, 7]), np.array([5, 6, 7])) + ) + self.assertEqual(extrapolator.method, "linear") extrapolator = Extrapolator( LinearInterpolator(np.array([5, 6, 7]), np.array([5, 6, 7])), - method='Constant') - self.assertEqual(extrapolator.method, 'constant') + method="Constant", + ) + self.assertEqual(extrapolator.method, "constant") def test_left(self): """ - Tests :func:`colour.algebra.extrapolation.Extrapolator.left` + Test :attr:`colour.algebra.extrapolation.Extrapolator.left` property. """ extrapolator = Extrapolator( LinearInterpolator(np.array([5, 6, 7]), np.array([5, 6, 7])), - left=0) + left=0, + ) self.assertEqual(extrapolator.left, 0) def test_right(self): """ - Tests :func:`colour.algebra.extrapolation.Extrapolator.right` + Test :attr:`colour.algebra.extrapolation.Extrapolator.right` property. """ extrapolator = Extrapolator( LinearInterpolator(np.array([5, 6, 7]), np.array([5, 6, 7])), - right=0) + right=0, + ) self.assertEqual(extrapolator.right, 0) def test__call__(self): """ - Tests :func:`colour.algebra.extrapolation.Extrapolator.__call__` + Test :meth:`colour.algebra.extrapolation.Extrapolator.__call__` method. """ extrapolator = Extrapolator( - LinearInterpolator(np.array([5, 6, 7]), np.array([5, 6, 7]))) + LinearInterpolator(np.array([5, 6, 7]), np.array([5, 6, 7])) + ) np.testing.assert_almost_equal(extrapolator((4, 8)), (4, 8)) self.assertEqual(extrapolator(4), 4) extrapolator = Extrapolator( LinearInterpolator(np.array([3, 4, 5]), np.array([1, 2, 3])), - method='Constant') + method="Constant", + ) np.testing.assert_almost_equal( - extrapolator((0.1, 0.2, 8, 9)), (1, 1, 3, 3)) - self.assertEqual(extrapolator(0.1), 1.) + extrapolator((0.1, 0.2, 8, 9)), (1, 1, 3, 3) + ) + self.assertEqual(extrapolator(0.1), 1.0) extrapolator = Extrapolator( LinearInterpolator(np.array([3, 4, 5]), np.array([1, 2, 3])), - method='Constant', - left=0) + method="Constant", + left=0, + ) np.testing.assert_almost_equal( - extrapolator((0.1, 0.2, 8, 9)), (0, 0, 3, 3)) + extrapolator((0.1, 0.2, 8, 9)), (0, 0, 3, 3) + ) self.assertEqual(extrapolator(0.1), 0) extrapolator = Extrapolator( LinearInterpolator(np.array([3, 4, 5]), np.array([1, 2, 3])), - method='Constant', - right=0) + method="Constant", + right=0, + ) np.testing.assert_almost_equal( - extrapolator((0.1, 0.2, 8, 9)), (1, 1, 0, 0)) + extrapolator((0.1, 0.2, 8, 9)), (1, 1, 0, 0) + ) self.assertEqual(extrapolator(9), 0) extrapolator = Extrapolator( CubicSplineInterpolator( - np.array([3, 4, 5, 6]), np.array([1, 2, 3, 4]))) + np.array([3, 4, 5, 6]), np.array([1, 2, 3, 4]) + ) + ) np.testing.assert_almost_equal( - extrapolator((0.1, 0.2, 8.0, 9.0)), (-1.9, -1.8, 6.0, 7.0)) + extrapolator((0.1, 0.2, 8.0, 9.0)), (-1.9, -1.8, 6.0, 7.0) + ) self.assertEqual(extrapolator(9), 7) extrapolator = Extrapolator( - PchipInterpolator(np.array([3, 4, 5]), np.array([1, 2, 3]))) + PchipInterpolator(np.array([3, 4, 5]), np.array([1, 2, 3])) + ) np.testing.assert_almost_equal( - extrapolator((0.1, 0.2, 8.0, 9.0)), (-1.9, -1.8, 6.0, 7.0)) - self.assertEqual(extrapolator(9), 7.) + extrapolator((0.1, 0.2, 8.0, 9.0)), (-1.9, -1.8, 6.0, 7.0) + ) + self.assertEqual(extrapolator(9), 7.0) @ignore_numpy_errors def test_nan__call__(self): """ - Tests :func:`colour.algebra.extrapolation.Extrapolator.__call__` + Test :method:`colour.algebra.extrapolation.Extrapolator.__call__` method nan support. """ @@ -154,9 +168,10 @@ def test_nan__call__(self): cases = set(permutations(cases * 3, r=3)) for case in cases: extrapolator = Extrapolator( - LinearInterpolator(np.array(case), np.array(case))) + LinearInterpolator(np.array(case), np.array(case)) + ) extrapolator(case[0]) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/algebra/tests/test_geometry.py b/colour/algebra/tests/test_geometry.py index ac1b180de9..d22cf726d1 100644 --- a/colour/algebra/tests/test_geometry.py +++ b/colour/algebra/tests/test_geometry.py @@ -1,98 +1,109 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.algebra.geometry` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.algebra.geometry` module.""" import numpy as np import unittest from itertools import permutations from colour.algebra import ( - normalise_vector, euclidean_distance, extend_line_segment, - intersect_line_segments, ellipse_coefficients_general_form, - ellipse_coefficients_canonical_form, point_at_angle_on_ellipse, - ellipse_fitting_Halir1998) + normalise_vector, + euclidean_distance, + manhattan_distance, + extend_line_segment, + intersect_line_segments, + ellipse_coefficients_general_form, + ellipse_coefficients_canonical_form, + point_at_angle_on_ellipse, + ellipse_fitting_Halir1998, +) from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestNormaliseVector', 'TestEuclideanDistance', 'TestExtendLineSegment', - 'TestIntersectLineSegments', 'TestEllipseCoefficientsCanonicalForm', - 'TestEllipseCoefficientsGeneralForm', 'TestPointAtAngleOnEllipse', - 'TestEllipseFittingHalir1998' + "TestNormaliseVector", + "TestEuclideanDistance", + "TestManhattanDistance", + "TestExtendLineSegment", + "TestIntersectLineSegments", + "TestEllipseCoefficientsCanonicalForm", + "TestEllipseCoefficientsGeneralForm", + "TestPointAtAngleOnEllipse", + "TestEllipseFittingHalir1998", ] class TestNormaliseVector(unittest.TestCase): """ - Defines :func:`colour.algebra.geometry.normalise_vector` definition unit + Define :func:`colour.algebra.geometry.normalise_vector` definition unit tests methods. """ def test_normalise_vector(self): - """ - Tests :func:`colour.algebra.geometry.normalise_vector` definition. - """ + """Test :func:`colour.algebra.geometry.normalise_vector` definition.""" np.testing.assert_almost_equal( normalise_vector(np.array([0.20654008, 0.12197225, 0.05136952])), np.array([0.84197033, 0.49722560, 0.20941026]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( normalise_vector(np.array([0.14222010, 0.23042768, 0.10495772])), np.array([0.48971705, 0.79344877, 0.36140872]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( normalise_vector(np.array([0.07818780, 0.06157201, 0.28099326])), np.array([0.26229003, 0.20655044, 0.94262445]), - decimal=7) + decimal=7, + ) class TestEuclideanDistance(unittest.TestCase): """ - Defines :func:`colour.algebra.geometry.euclidean_distance` definition unit + Define :func:`colour.algebra.geometry.euclidean_distance` definition unit tests methods. """ def test_euclidean_distance(self): - """ - Tests :func:`colour.algebra.geometry.euclidean_distance` definition. - """ + """Test :func:`colour.algebra.geometry.euclidean_distance` definition.""" self.assertAlmostEqual( euclidean_distance( np.array([100.00000000, 21.57210357, 272.22819350]), - np.array([100.00000000, 426.67945353, 72.39590835])), + np.array([100.00000000, 426.67945353, 72.39590835]), + ), 451.71330197, - places=7) + places=7, + ) self.assertAlmostEqual( euclidean_distance( np.array([100.00000000, 21.57210357, 272.22819350]), - np.array([100.00000000, 74.05216981, 276.45318193])), + np.array([100.00000000, 74.05216981, 276.45318193]), + ), 52.64986116, - places=7) + places=7, + ) self.assertAlmostEqual( euclidean_distance( np.array([100.00000000, 21.57210357, 272.22819350]), - np.array([100.00000000, 8.32281957, -73.58297716])), + np.array([100.00000000, 8.32281957, -73.58297716]), + ), 346.06489172, - places=7) + places=7, + ) def test_n_dimensional_euclidean_distance(self): """ - Tests :func:`colour.algebra.geometry.euclidean_distance` definition + Test :func:`colour.algebra.geometry.euclidean_distance` definition n-dimensional arrays support. """ @@ -104,18 +115,20 @@ def test_n_dimensional_euclidean_distance(self): b = np.tile(b, (6, 1)) distance = np.tile(distance, 6) np.testing.assert_almost_equal( - euclidean_distance(a, b), distance, decimal=7) + euclidean_distance(a, b), distance, decimal=7 + ) a = np.reshape(a, (2, 3, 3)) b = np.reshape(b, (2, 3, 3)) distance = np.reshape(distance, (2, 3)) np.testing.assert_almost_equal( - euclidean_distance(a, b), distance, decimal=7) + euclidean_distance(a, b), distance, decimal=7 + ) @ignore_numpy_errors def test_nan_euclidean_distance(self): """ - Tests :func:`colour.algebra.geometry.euclidean_distance` definition nan + Test :func:`colour.algebra.geometry.euclidean_distance` definition nan support. """ @@ -127,196 +140,315 @@ def test_nan_euclidean_distance(self): euclidean_distance(a, b) -class TestExtendLineSegment(unittest.TestCase): +class TestManhattanDistance(unittest.TestCase): """ - Defines :func:`colour.algebra.geometry.extend_line_segment` definition unit + Define :func:`colour.algebra.geometry.manhattan_distance` definition unit tests methods. """ - def test_extend_line_segment(self): + def test_manhattan_distance(self): + """Test :func:`colour.algebra.geometry.manhattan_distance` definition.""" + + self.assertAlmostEqual( + manhattan_distance( + np.array([100.00000000, 21.57210357, 272.22819350]), + np.array([100.00000000, 426.67945353, 72.39590835]), + ), + 604.93963510999993, + places=7, + ) + + self.assertAlmostEqual( + manhattan_distance( + np.array([100.00000000, 21.57210357, 272.22819350]), + np.array([100.00000000, 74.05216981, 276.45318193]), + ), + 56.705054670000052, + places=7, + ) + + self.assertAlmostEqual( + manhattan_distance( + np.array([100.00000000, 21.57210357, 272.22819350]), + np.array([100.00000000, 8.32281957, -73.58297716]), + ), + 359.06045465999995, + places=7, + ) + + def test_n_dimensional_manhattan_distance(self): + """ + Test :func:`colour.algebra.geometry.manhattan_distance` definition + n-dimensional arrays support. + """ + + a = np.array([100.00000000, 21.57210357, 272.22819350]) + b = np.array([100.00000000, 426.67945353, 72.39590835]) + distance = manhattan_distance(a, b) + + a = np.tile(a, (6, 1)) + b = np.tile(b, (6, 1)) + distance = np.tile(distance, 6) + np.testing.assert_almost_equal( + manhattan_distance(a, b), distance, decimal=7 + ) + + a = np.reshape(a, (2, 3, 3)) + b = np.reshape(b, (2, 3, 3)) + distance = np.reshape(distance, (2, 3)) + np.testing.assert_almost_equal( + manhattan_distance(a, b), distance, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_manhattan_distance(self): """ - Tests :func:`colour.algebra.geometry.extend_line_segment` definition. + Test :func:`colour.algebra.geometry.manhattan_distance` definition nan + support. """ + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + a = np.array(case) + b = np.array(case) + manhattan_distance(a, b) + + +class TestExtendLineSegment(unittest.TestCase): + """ + Define :func:`colour.algebra.geometry.extend_line_segment` definition unit + tests methods. + """ + + def test_extend_line_segment(self): + """Test :func:`colour.algebra.geometry.extend_line_segment` definition.""" + np.testing.assert_almost_equal( extend_line_segment( np.array([0.95694934, 0.13720932]), - np.array([0.28382835, 0.60608318])), + np.array([0.28382835, 0.60608318]), + ), np.array([-0.5367248, 1.17765341]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( extend_line_segment( np.array([0.95694934, 0.13720932]), - np.array([0.28382835, 0.60608318]), 5), + np.array([0.28382835, 0.60608318]), + 5, + ), np.array([-3.81893739, 3.46393435]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( extend_line_segment( np.array([0.95694934, 0.13720932]), - np.array([0.28382835, 0.60608318]), -1), + np.array([0.28382835, 0.60608318]), + -1, + ), np.array([1.1043815, 0.03451295]), - decimal=7) + decimal=7, + ) class TestIntersectLineSegments(unittest.TestCase): """ - Defines :func:`colour.algebra.geometry.intersect_line_segments` definition + Define :func:`colour.algebra.geometry.intersect_line_segments` definition unit tests methods. """ def test_intersect_line_segments(self): """ - Tests :func:`colour.algebra.geometry.intersect_line_segments` + Test :func:`colour.algebra.geometry.intersect_line_segments` definition. """ - l_1 = np.array([ - [[0.15416284, 0.7400497], [0.26331502, 0.53373939]], - [[0.01457496, 0.91874701], [0.90071485, 0.03342143]], - ]) - l_2 = np.array([ - [[0.95694934, 0.13720932], [0.28382835, 0.60608318]], - [[0.94422514, 0.85273554], [0.00225923, 0.52122603]], - [[0.55203763, 0.48537741], [0.76813415, 0.16071675]], - [[0.01457496, 0.91874701], [0.90071485, 0.03342143]], - ]) + l_1 = np.array( + [ + [[0.15416284, 0.7400497], [0.26331502, 0.53373939]], + [[0.01457496, 0.91874701], [0.90071485, 0.03342143]], + ] + ) + l_2 = np.array( + [ + [[0.95694934, 0.13720932], [0.28382835, 0.60608318]], + [[0.94422514, 0.85273554], [0.00225923, 0.52122603]], + [[0.55203763, 0.48537741], [0.76813415, 0.16071675]], + [[0.01457496, 0.91874701], [0.90071485, 0.03342143]], + ] + ) s = intersect_line_segments(l_1, l_2) np.testing.assert_almost_equal( s.xy, - np.array([[[np.nan, np.nan], [0.22791841, 0.60064309], - [np.nan, np.nan], [np.nan, np.nan]], - [[0.42814517, 0.50555685], [0.30560559, 0.62798382], - [0.7578749, 0.17613012], [np.nan, np.nan]]]), - decimal=7) + np.array( + [ + [ + [np.nan, np.nan], + [0.22791841, 0.60064309], + [np.nan, np.nan], + [np.nan, np.nan], + ], + [ + [0.42814517, 0.50555685], + [0.30560559, 0.62798382], + [0.7578749, 0.17613012], + [np.nan, np.nan], + ], + ] + ), + decimal=7, + ) np.testing.assert_array_equal( s.intersect, - np.array([[False, True, False, False], [True, True, True, False]])) + np.array([[False, True, False, False], [True, True, True, False]]), + ) np.testing.assert_array_equal( s.parallel, - np.array([[False, False, False, False], - [False, False, False, True]])) + np.array( + [[False, False, False, False], [False, False, False, True]] + ), + ) np.testing.assert_array_equal( s.coincident, - np.array([[False, False, False, False], - [False, False, False, True]])) + np.array( + [[False, False, False, False], [False, False, False, True]] + ), + ) class TestEllipseCoefficientsCanonicalForm(unittest.TestCase): """ - Defines :func:`colour.algebra.geometry.ellipse_coefficients_canonical_form` + Define :func:`colour.algebra.geometry.ellipse_coefficients_canonical_form` definition unit tests methods. """ def test_ellipse_coefficients_canonical_form(self): """ - Tests :func:`colour.algebra.geometry.\ + Test :func:`colour.algebra.geometry.\ ellipse_coefficients_canonical_form` definition. """ np.testing.assert_almost_equal( ellipse_coefficients_canonical_form( - np.array([2.5, -3.0, 2.5, -1.0, -1.0, -3.5])), + np.array([2.5, -3.0, 2.5, -1.0, -1.0, -3.5]) + ), np.array([0.5, 0.5, 2, 1, 45]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( ellipse_coefficients_canonical_form( - np.array([1.0, 0.0, 1.0, 0.0, 0.0, -1.0])), + np.array([1.0, 0.0, 1.0, 0.0, 0.0, -1.0]) + ), np.array([0.0, 0.0, 1, 1, 0]), - decimal=7) + decimal=7, + ) class TestEllipseCoefficientsGeneralForm(unittest.TestCase): """ - Defines :func:`colour.algebra.geometry.ellipse_coefficients_general_form` + Define :func:`colour.algebra.geometry.ellipse_coefficients_general_form` definition unit tests methods. """ def test_ellipse_coefficients_general_form(self): """ - Tests :func:`colour.algebra.geometry.ellipse_coefficients_general_form` + Test :func:`colour.algebra.geometry.ellipse_coefficients_general_form` definition. """ np.testing.assert_almost_equal( ellipse_coefficients_general_form(np.array([0.5, 0.5, 2, 1, 45])), np.array([2.5, -3.0, 2.5, -1.0, -1.0, -3.5]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( ellipse_coefficients_general_form(np.array([0.0, 0.0, 1, 1, 0])), np.array([1.0, 0.0, 1.0, 0.0, 0.0, -1.0]), - decimal=7) + decimal=7, + ) class TestPointAtAngleOnEllipse(unittest.TestCase): """ - Defines :func:`colour.algebra.geometry.point_at_angle_on_ellipse` + Define :func:`colour.algebra.geometry.point_at_angle_on_ellipse` definition unit tests methods. """ def test_point_at_angle_on_ellipse(self): """ - Tests :func:`colour.algebra.geometry.point_at_angle_on_ellipse` + Test :func:`colour.algebra.geometry.point_at_angle_on_ellipse` definition. """ np.testing.assert_almost_equal( point_at_angle_on_ellipse( - np.array([0, 90, 180, 270]), np.array([0.0, 0.0, 2, 1, 0])), + np.array([0, 90, 180, 270]), np.array([0.0, 0.0, 2, 1, 0]) + ), np.array([[2, 0], [0, 1], [-2, 0], [0, -1]]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( point_at_angle_on_ellipse( - np.linspace(0, 360, 10), np.array([0.5, 0.5, 2, 1, 45])), - np.array([ - [1.91421356, 1.91421356], - [1.12883096, 2.03786992], - [0.04921137, 1.44193985], - [-0.81947922, 0.40526565], - [-1.07077081, -0.58708129], - [-0.58708129, -1.07077081], - [0.40526565, -0.81947922], - [1.44193985, 0.04921137], - [2.03786992, 1.12883096], - [1.91421356, 1.91421356], - ]), - decimal=7) + np.linspace(0, 360, 10), np.array([0.5, 0.5, 2, 1, 45]) + ), + np.array( + [ + [1.91421356, 1.91421356], + [1.12883096, 2.03786992], + [0.04921137, 1.44193985], + [-0.81947922, 0.40526565], + [-1.07077081, -0.58708129], + [-0.58708129, -1.07077081], + [0.40526565, -0.81947922], + [1.44193985, 0.04921137], + [2.03786992, 1.12883096], + [1.91421356, 1.91421356], + ] + ), + decimal=7, + ) class TestEllipseFittingHalir1998(unittest.TestCase): """ - Defines :func:`colour.algebra.geometry.ellipse_fitting_Halir1998` + Define :func:`colour.algebra.geometry.ellipse_fitting_Halir1998` definition unit tests methods. """ def test_ellipse_fitting_Halir1998(self): """ - Tests :func:`colour.algebra.geometry.ellipse_fitting_Halir1998` + Test :func:`colour.algebra.geometry.ellipse_fitting_Halir1998` definition. """ np.testing.assert_almost_equal( ellipse_fitting_Halir1998( - np.array([[2, 0], [0, 1], [-2, 0], [0, -1]])), - np.array([ - 0.24253563, - 0.00000000, - 0.97014250, - 0.00000000, - 0.00000000, - -0.97014250, - ]), - decimal=7) - - -if __name__ == '__main__': + np.array([[2, 0], [0, 1], [-2, 0], [0, -1]]) + ), + np.array( + [ + 0.24253563, + 0.00000000, + 0.97014250, + 0.00000000, + 0.00000000, + -0.97014250, + ] + ), + decimal=7, + ) + + +if __name__ == "__main__": unittest.main() diff --git a/colour/algebra/tests/test_interpolation.py b/colour/algebra/tests/test_interpolation.py index 8d9bc7fcc6..572a2976ea 100644 --- a/colour/algebra/tests/test_interpolation.py +++ b/colour/algebra/tests/test_interpolation.py @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.algebra.interpolation` module. +Defines the unit tests for the :mod:`colour.algebra.interpolation` module. References ---------- @@ -9,7 +8,7 @@ 199-203. doi:10.1002/col.5080100407 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import os @@ -17,327 +16,845 @@ from itertools import permutations from colour.algebra.interpolation import vertices_and_relative_coordinates +from colour.hints import NDArray, Tuple from colour.algebra import ( - kernel_nearest_neighbour, kernel_linear, kernel_sinc, kernel_lanczos, - kernel_cardinal_spline, KernelInterpolator, NearestNeighbourInterpolator, - LinearInterpolator, SpragueInterpolator, CubicSplineInterpolator, - PchipInterpolator, NullInterpolator, lagrange_coefficients, - table_interpolation_trilinear, table_interpolation_tetrahedral) + kernel_nearest_neighbour, + kernel_linear, + kernel_sinc, + kernel_lanczos, + kernel_cardinal_spline, + KernelInterpolator, + NearestNeighbourInterpolator, + LinearInterpolator, + SpragueInterpolator, + CubicSplineInterpolator, + PchipInterpolator, + NullInterpolator, + lagrange_coefficients, + table_interpolation_trilinear, + table_interpolation_tetrahedral, +) from colour.algebra import random_triplet_generator from colour.io import read_LUT from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'DATA_POINTS_A', 'DATA_POINTS_A_LINEAR_INTERPOLATED_10_SAMPLES', - 'DATA_POINTS_A_SPRAGUE_INTERPOLATED_10_SAMPLES', - 'DATA_POINTS_A_CUBIC_SPLINE_INTERPOLATED_X2_SAMPLES', - 'LAGRANGE_COEFFICIENTS_A', 'LAGRANGE_COEFFICIENTS_B', 'LUT_TABLE', - 'TestKernelNearestNeighbour', 'TestKernelLinear', 'TestKernelSinc', - 'TestKernelLanczos', 'TestKernelCardinalSpline', 'TestKernelInterpolator', - 'TestLinearInterpolator', 'TestSpragueInterpolator', - 'TestCubicSplineInterpolator', 'TestPchipInterpolator', - 'TestNullInterpolator', 'TestLagrangeCoefficients', - 'TestVerticesAndRelativeCoordinates', 'TestTableInterpolationTrilinear', - 'TestTableInterpolationTetrahedral' + "DATA_POINTS_A", + "DATA_POINTS_A_LINEAR_INTERPOLATED_10_SAMPLES", + "DATA_POINTS_A_SPRAGUE_INTERPOLATED_10_SAMPLES", + "DATA_POINTS_A_CUBIC_SPLINE_INTERPOLATED_X2_SAMPLES", + "LAGRANGE_COEFFICIENTS_A", + "LAGRANGE_COEFFICIENTS_B", + "LUT_TABLE", + "TestKernelNearestNeighbour", + "TestKernelLinear", + "TestKernelSinc", + "TestKernelLanczos", + "TestKernelCardinalSpline", + "TestKernelInterpolator", + "TestLinearInterpolator", + "TestSpragueInterpolator", + "TestCubicSplineInterpolator", + "TestPchipInterpolator", + "TestNullInterpolator", + "TestLagrangeCoefficients", + "TestVerticesAndRelativeCoordinates", + "TestTableInterpolationTrilinear", + "TestTableInterpolationTetrahedral", ] -DATA_POINTS_A = (9.3700, 12.3200, 12.4600, 9.5100, 5.9200, 4.3300, 4.2900, - 3.8800, 4.5100, 10.9200, 27.5000, 49.6700, 69.5900, 81.7300, - 88.1900, 86.0500) - -DATA_POINTS_A_LINEAR_INTERPOLATED_10_SAMPLES = ( - 9.370, 9.665, 9.960, 10.255, 10.550, 10.845, 11.140, 11.435, 11.730, - 12.025, 12.320, 12.334, 12.348, 12.362, 12.376, 12.390, 12.404, 12.418, - 12.432, 12.446, 12.460, 12.165, 11.870, 11.575, 11.280, 10.985, 10.690, - 10.395, 10.100, 9.805, 9.510, 9.151, 8.792, 8.433, 8.074, 7.715, 7.356, - 6.997, 6.638, 6.279, 5.920, 5.761, 5.602, 5.443, 5.284, 5.125, 4.966, - 4.807, 4.648, 4.489, 4.330, 4.326, 4.322, 4.318, 4.314, 4.310, 4.306, - 4.302, 4.298, 4.294, 4.290, 4.249, 4.208, 4.167, 4.126, 4.085, 4.044, - 4.003, 3.962, 3.921, 3.880, 3.943, 4.006, 4.069, 4.132, 4.195, 4.258, - 4.321, 4.384, 4.447, 4.510, 5.151, 5.792, 6.433, 7.074, 7.715, 8.356, - 8.997, 9.638, 10.279, 10.920, 12.578, 14.236, 15.894, 17.552, 19.210, - 20.868, 22.526, 24.184, 25.842, 27.500, 29.717, 31.934, 34.151, 36.368, - 38.585, 40.802, 43.019, 45.236, 47.453, 49.670, 51.662, 53.654, 55.646, - 57.638, 59.630, 61.622, 63.614, 65.606, 67.598, 69.590, 70.804, 72.018, - 73.232, 74.446, 75.660, 76.874, 78.088, 79.302, 80.516, 81.730, 82.376, - 83.022, 83.668, 84.314, 84.960, 85.606, 86.252, 86.898, 87.544, 88.190, - 87.976, 87.762, 87.548, 87.334, 87.120, 86.906, 86.692, 86.478, 86.264, - 86.050) - -DATA_POINTS_A_SPRAGUE_INTERPOLATED_10_SAMPLES = ( - 9.37000000, 9.72075073, 10.06936191, 10.41147570, 10.74302270, 11.06022653, - 11.35960827, 11.63799100, 11.89250427, 12.12058860, 12.32000000, - 12.48873542, 12.62489669, 12.72706530, 12.79433478, 12.82623598, - 12.82266243, 12.78379557, 12.71003009, 12.60189921, 12.46000000, - 12.28440225, 12.07404800, 11.82976500, 11.55443200, 11.25234375, - 10.92857600, 10.58835050, 10.23640000, 9.87633325, 9.51000000, 9.13692962, - 8.75620800, 8.36954763, 7.98097600, 7.59601562, 7.22086400, 6.86157362, - 6.52323200, 6.20914162, 5.92000000, 5.65460200, 5.41449600, 5.20073875, - 5.01294400, 4.84968750, 4.70891200, 4.58833225, 4.48584000, 4.39990900, - 4.33000000, 4.27757887, 4.24595200, 4.23497388, 4.24099200, 4.25804688, - 4.27907200, 4.29709387, 4.30643200, 4.30389887, 4.29000000, 4.26848387, - 4.24043200, 4.20608887, 4.16603200, 4.12117188, 4.07275200, 4.02234887, - 3.97187200, 3.92356387, 3.88000000, 3.84319188, 3.81318400, 3.79258487, - 3.78691200, 3.80367187, 3.85144000, 3.93894087, 4.07412800, 4.26326387, - 4.51000000, 4.81362075, 5.17028800, 5.58225150, 6.05776000, 6.60890625, - 7.24947200, 7.99277300, 8.84950400, 9.82558375, 10.92000000, 12.12700944, - 13.44892800, 14.88581406, 16.43283200, 18.08167969, 19.82201600, - 21.64288831, 23.53416000, 25.48793794, 27.50000000, 29.57061744, - 31.69964800, 33.88185481, 36.10777600, 38.36511719, 40.64014400, - 42.91907456, 45.18947200, 47.44163694, 49.67000000, 51.87389638, - 54.05273600, 56.20157688, 58.31198400, 60.37335938, 62.37427200, - 64.30378787, 66.15280000, 67.91535838, 69.59000000, 71.17616669, - 72.66283200, 74.04610481, 75.33171200, 76.53183594, 77.66195200, - 78.73766606, 79.77155200, 80.76998919, 81.73000000, 82.64375688, - 83.51935227, 84.35919976, 85.15567334, 85.89451368, 86.55823441, - 87.12952842, 87.59467414, 87.94694187, 88.19000000, 88.33345751, - 88.37111372, 88.30221714, 88.13600972, 87.88846516, 87.57902706, - 87.22734720, 86.85002373, 86.45733945, 86.05000000) - -DATA_POINTS_A_CUBIC_SPLINE_INTERPOLATED_X2_SAMPLES = ( - 9.37000000, 11.08838189, 12.26359953, 12.78808025, 12.55425139, - 11.50391691, 9.87473603, 8.01707329, 6.30369624, 5.08664365, 4.43550284, - 4.25438019, 4.29206798, 4.21753374, 3.98875865, 3.79691327, 4.02534907, - 5.23223510, 8.08816250, 13.36306794, 21.19519815, 30.89350026, 41.64531611, - 52.53540869, 62.65180882, 71.10713687, 77.46889540, 82.31355134, - 86.05208477, 88.28078752, 88.45998434, 86.05000000) - -LAGRANGE_COEFFICIENTS_A = np.array([ - [0.92625, 0.09750, -0.02375], - [0.85500, 0.19000, -0.04500], - [0.78625, 0.27750, -0.06375], - [0.72000, 0.36000, -0.08000], - [0.65625, 0.43750, -0.09375], - [0.59500, 0.51000, -0.10500], - [0.53625, 0.57750, -0.11375], - [0.48000, 0.64000, -0.12000], - [0.42625, 0.69750, -0.12375], - [0.37500, 0.75000, -0.12500], - [0.32625, 0.79750, -0.12375], - [0.28000, 0.84000, -0.12000], - [0.23625, 0.87750, -0.11375], - [0.19500, 0.91000, -0.10500], - [0.15625, 0.93750, -0.09375], - [0.12000, 0.96000, -0.08000], - [0.08625, 0.97750, -0.06375], - [0.05500, 0.99000, -0.04500], - [0.02625, 0.99750, -0.02375], -]) - -LAGRANGE_COEFFICIENTS_B = np.array([ - [-0.0154375, 0.9725625, 0.0511875, -0.0083125], - [-0.0285000, 0.9405000, 0.1045000, -0.0165000], - [-0.0393125, 0.9041875, 0.1595625, -0.0244375], - [-0.0480000, 0.8640000, 0.2160000, -0.0320000], - [-0.0546875, 0.8203125, 0.2734375, -0.0390625], - [-0.0595000, 0.7735000, 0.3315000, -0.0455000], - [-0.0625625, 0.7239375, 0.3898125, -0.0511875], - [-0.0640000, 0.6720000, 0.4480000, -0.0560000], - [-0.0639375, 0.6180625, 0.5056875, -0.0598125], - [-0.0625000, 0.5625000, 0.5625000, -0.0625000], - [-0.0598125, 0.5056875, 0.6180625, -0.0639375], - [-0.0560000, 0.4480000, 0.6720000, -0.0640000], - [-0.0511875, 0.3898125, 0.7239375, -0.0625625], - [-0.0455000, 0.3315000, 0.7735000, -0.0595000], - [-0.0390625, 0.2734375, 0.8203125, -0.0546875], - [-0.0320000, 0.2160000, 0.8640000, -0.0480000], - [-0.0244375, 0.1595625, 0.9041875, -0.0393125], - [-0.0165000, 0.1045000, 0.9405000, -0.0285000], - [-0.0083125, 0.0511875, 0.9725625, -0.0154375], -]) - -LUT_TABLE = read_LUT( +DATA_POINTS_A: Tuple = ( + 9.3700, + 12.3200, + 12.4600, + 9.5100, + 5.9200, + 4.3300, + 4.2900, + 3.8800, + 4.5100, + 10.9200, + 27.5000, + 49.6700, + 69.5900, + 81.7300, + 88.1900, + 86.0500, +) + +DATA_POINTS_A_LINEAR_INTERPOLATED_10_SAMPLES: Tuple = ( + 9.370, + 9.665, + 9.960, + 10.255, + 10.550, + 10.845, + 11.140, + 11.435, + 11.730, + 12.025, + 12.320, + 12.334, + 12.348, + 12.362, + 12.376, + 12.390, + 12.404, + 12.418, + 12.432, + 12.446, + 12.460, + 12.165, + 11.870, + 11.575, + 11.280, + 10.985, + 10.690, + 10.395, + 10.100, + 9.805, + 9.510, + 9.151, + 8.792, + 8.433, + 8.074, + 7.715, + 7.356, + 6.997, + 6.638, + 6.279, + 5.920, + 5.761, + 5.602, + 5.443, + 5.284, + 5.125, + 4.966, + 4.807, + 4.648, + 4.489, + 4.330, + 4.326, + 4.322, + 4.318, + 4.314, + 4.310, + 4.306, + 4.302, + 4.298, + 4.294, + 4.290, + 4.249, + 4.208, + 4.167, + 4.126, + 4.085, + 4.044, + 4.003, + 3.962, + 3.921, + 3.880, + 3.943, + 4.006, + 4.069, + 4.132, + 4.195, + 4.258, + 4.321, + 4.384, + 4.447, + 4.510, + 5.151, + 5.792, + 6.433, + 7.074, + 7.715, + 8.356, + 8.997, + 9.638, + 10.279, + 10.920, + 12.578, + 14.236, + 15.894, + 17.552, + 19.210, + 20.868, + 22.526, + 24.184, + 25.842, + 27.500, + 29.717, + 31.934, + 34.151, + 36.368, + 38.585, + 40.802, + 43.019, + 45.236, + 47.453, + 49.670, + 51.662, + 53.654, + 55.646, + 57.638, + 59.630, + 61.622, + 63.614, + 65.606, + 67.598, + 69.590, + 70.804, + 72.018, + 73.232, + 74.446, + 75.660, + 76.874, + 78.088, + 79.302, + 80.516, + 81.730, + 82.376, + 83.022, + 83.668, + 84.314, + 84.960, + 85.606, + 86.252, + 86.898, + 87.544, + 88.190, + 87.976, + 87.762, + 87.548, + 87.334, + 87.120, + 86.906, + 86.692, + 86.478, + 86.264, + 86.050, +) + +DATA_POINTS_A_SPRAGUE_INTERPOLATED_10_SAMPLES: Tuple = ( + 9.37000000, + 9.72075073, + 10.06936191, + 10.41147570, + 10.74302270, + 11.06022653, + 11.35960827, + 11.63799100, + 11.89250427, + 12.12058860, + 12.32000000, + 12.48873542, + 12.62489669, + 12.72706530, + 12.79433478, + 12.82623598, + 12.82266243, + 12.78379557, + 12.71003009, + 12.60189921, + 12.46000000, + 12.28440225, + 12.07404800, + 11.82976500, + 11.55443200, + 11.25234375, + 10.92857600, + 10.58835050, + 10.23640000, + 9.87633325, + 9.51000000, + 9.13692962, + 8.75620800, + 8.36954763, + 7.98097600, + 7.59601562, + 7.22086400, + 6.86157362, + 6.52323200, + 6.20914162, + 5.92000000, + 5.65460200, + 5.41449600, + 5.20073875, + 5.01294400, + 4.84968750, + 4.70891200, + 4.58833225, + 4.48584000, + 4.39990900, + 4.33000000, + 4.27757887, + 4.24595200, + 4.23497388, + 4.24099200, + 4.25804688, + 4.27907200, + 4.29709387, + 4.30643200, + 4.30389887, + 4.29000000, + 4.26848387, + 4.24043200, + 4.20608887, + 4.16603200, + 4.12117188, + 4.07275200, + 4.02234887, + 3.97187200, + 3.92356387, + 3.88000000, + 3.84319188, + 3.81318400, + 3.79258487, + 3.78691200, + 3.80367187, + 3.85144000, + 3.93894087, + 4.07412800, + 4.26326387, + 4.51000000, + 4.81362075, + 5.17028800, + 5.58225150, + 6.05776000, + 6.60890625, + 7.24947200, + 7.99277300, + 8.84950400, + 9.82558375, + 10.92000000, + 12.12700944, + 13.44892800, + 14.88581406, + 16.43283200, + 18.08167969, + 19.82201600, + 21.64288831, + 23.53416000, + 25.48793794, + 27.50000000, + 29.57061744, + 31.69964800, + 33.88185481, + 36.10777600, + 38.36511719, + 40.64014400, + 42.91907456, + 45.18947200, + 47.44163694, + 49.67000000, + 51.87389638, + 54.05273600, + 56.20157688, + 58.31198400, + 60.37335938, + 62.37427200, + 64.30378787, + 66.15280000, + 67.91535838, + 69.59000000, + 71.17616669, + 72.66283200, + 74.04610481, + 75.33171200, + 76.53183594, + 77.66195200, + 78.73766606, + 79.77155200, + 80.76998919, + 81.73000000, + 82.64375688, + 83.51935227, + 84.35919976, + 85.15567334, + 85.89451368, + 86.55823441, + 87.12952842, + 87.59467414, + 87.94694187, + 88.19000000, + 88.33345751, + 88.37111372, + 88.30221714, + 88.13600972, + 87.88846516, + 87.57902706, + 87.22734720, + 86.85002373, + 86.45733945, + 86.05000000, +) + +DATA_POINTS_A_CUBIC_SPLINE_INTERPOLATED_X2_SAMPLES: Tuple = ( + 9.37000000, + 11.08838189, + 12.26359953, + 12.78808025, + 12.55425139, + 11.50391691, + 9.87473603, + 8.01707329, + 6.30369624, + 5.08664365, + 4.43550284, + 4.25438019, + 4.29206798, + 4.21753374, + 3.98875865, + 3.79691327, + 4.02534907, + 5.23223510, + 8.08816250, + 13.36306794, + 21.19519815, + 30.89350026, + 41.64531611, + 52.53540869, + 62.65180882, + 71.10713687, + 77.46889540, + 82.31355134, + 86.05208477, + 88.28078752, + 88.45998434, + 86.05000000, +) + +LAGRANGE_COEFFICIENTS_A: NDArray = np.array( + [ + [0.92625, 0.09750, -0.02375], + [0.85500, 0.19000, -0.04500], + [0.78625, 0.27750, -0.06375], + [0.72000, 0.36000, -0.08000], + [0.65625, 0.43750, -0.09375], + [0.59500, 0.51000, -0.10500], + [0.53625, 0.57750, -0.11375], + [0.48000, 0.64000, -0.12000], + [0.42625, 0.69750, -0.12375], + [0.37500, 0.75000, -0.12500], + [0.32625, 0.79750, -0.12375], + [0.28000, 0.84000, -0.12000], + [0.23625, 0.87750, -0.11375], + [0.19500, 0.91000, -0.10500], + [0.15625, 0.93750, -0.09375], + [0.12000, 0.96000, -0.08000], + [0.08625, 0.97750, -0.06375], + [0.05500, 0.99000, -0.04500], + [0.02625, 0.99750, -0.02375], + ] +) + +LAGRANGE_COEFFICIENTS_B: NDArray = np.array( + [ + [-0.0154375, 0.9725625, 0.0511875, -0.0083125], + [-0.0285000, 0.9405000, 0.1045000, -0.0165000], + [-0.0393125, 0.9041875, 0.1595625, -0.0244375], + [-0.0480000, 0.8640000, 0.2160000, -0.0320000], + [-0.0546875, 0.8203125, 0.2734375, -0.0390625], + [-0.0595000, 0.7735000, 0.3315000, -0.0455000], + [-0.0625625, 0.7239375, 0.3898125, -0.0511875], + [-0.0640000, 0.6720000, 0.4480000, -0.0560000], + [-0.0639375, 0.6180625, 0.5056875, -0.0598125], + [-0.0625000, 0.5625000, 0.5625000, -0.0625000], + [-0.0598125, 0.5056875, 0.6180625, -0.0639375], + [-0.0560000, 0.4480000, 0.6720000, -0.0640000], + [-0.0511875, 0.3898125, 0.7239375, -0.0625625], + [-0.0455000, 0.3315000, 0.7735000, -0.0595000], + [-0.0390625, 0.2734375, 0.8203125, -0.0546875], + [-0.0320000, 0.2160000, 0.8640000, -0.0480000], + [-0.0244375, 0.1595625, 0.9041875, -0.0393125], + [-0.0165000, 0.1045000, 0.9405000, -0.0285000], + [-0.0083125, 0.0511875, 0.9725625, -0.0154375], + ] +) + +LUT_TABLE: NDArray = read_LUT( os.path.join( - os.path.dirname(__file__), '..', '..', 'io', 'luts', 'tests', - 'resources', 'iridas_cube', 'Colour_Correct.cube')).table + os.path.dirname(__file__), + "..", + "..", + "io", + "luts", + "tests", + "resources", + "iridas_cube", + "Colour_Correct.cube", + ) +).table # type: ignore[union-attr] class TestKernelNearestNeighbour(unittest.TestCase): """ - Defines :func:`colour.algebra.interpolation.kernel_nearest_neighbour` - definition units tests methods. + Define :func:`colour.algebra.interpolation.kernel_nearest_neighbour` + definition unit tests methods. """ def test_kernel_nearest(self): """ - Tests :func:`colour.algebra.interpolation.kernel_nearest_neighbour` + Test :func:`colour.algebra.interpolation.kernel_nearest_neighbour` definition. """ np.testing.assert_almost_equal( kernel_nearest_neighbour(np.linspace(-5, 5, 25)), - np.array([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 - ]), - decimal=7) + np.array( + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ] + ), + decimal=7, + ) class TestKernelLinear(unittest.TestCase): """ - Defines :func:`colour.algebra.interpolation.kernel_linear` definition - units tests methods. + Define :func:`colour.algebra.interpolation.kernel_linear` definition + unit tests methods. """ def test_kernel_linear(self): - """ - Tests :func:`colour.algebra.interpolation.kernel_linear` definition. - """ + """Test :func:`colour.algebra.interpolation.kernel_linear` definition.""" np.testing.assert_almost_equal( kernel_linear(np.linspace(-5, 5, 25)), - np.array([ - 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, - 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, - 0.16666667, 0.58333333, 1.00000000, 0.58333333, 0.16666667, - 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, - 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000 - ]), - decimal=7) + np.array( + [ + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.16666667, + 0.58333333, + 1.00000000, + 0.58333333, + 0.16666667, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + ] + ), + decimal=7, + ) class TestKernelSinc(unittest.TestCase): """ - Defines :func:`colour.algebra.interpolation.kernel_sinc` definition - units tests methods. + Define :func:`colour.algebra.interpolation.kernel_sinc` definition + unit tests methods. """ def test_kernel_sinc(self): - """ - Tests :func:`colour.algebra.interpolation.kernel_sinc` definition. - """ + """Test :func:`colour.algebra.interpolation.kernel_sinc` definition.""" np.testing.assert_almost_equal( kernel_sinc(np.linspace(-5, 5, 25)), - np.array([ - 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, - 0.02824617, 0.12732395, 0.03954464, -0.16539867, -0.18006326, - 0.19098593, 0.73791298, 1.00000000, 0.73791298, 0.19098593, - -0.18006326, -0.16539867, 0.03954464, 0.12732395, 0.02824617, - 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000 - ]), - decimal=7) + np.array( + [ + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.02824617, + 0.12732395, + 0.03954464, + -0.16539867, + -0.18006326, + 0.19098593, + 0.73791298, + 1.00000000, + 0.73791298, + 0.19098593, + -0.18006326, + -0.16539867, + 0.03954464, + 0.12732395, + 0.02824617, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( kernel_sinc(np.linspace(-5, 5, 25), 1), - np.array([ - 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, - 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, - 0.19098593, 0.73791298, 1.00000000, 0.73791298, 0.19098593, - 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, - 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000 - ]), - decimal=7) + np.array( + [ + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.19098593, + 0.73791298, + 1.00000000, + 0.73791298, + 0.19098593, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + ] + ), + decimal=7, + ) class TestKernelLanczos(unittest.TestCase): """ - Defines :func:`colour.algebra.interpolation.kernel_lanczos` definition - units tests methods. + Define :func:`colour.algebra.interpolation.kernel_lanczos` definition + unit tests methods. """ def test_kernel_lanczos(self): - """ - Tests :func:`colour.algebra.interpolation.kernel_lanczos` definition. - """ + """Test :func:`colour.algebra.interpolation.kernel_lanczos` definition.""" np.testing.assert_almost_equal( kernel_lanczos(np.linspace(-5, 5, 25)), - np.array([ - 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, - 0.00000000e+00, 8.06009483e-04, 2.43170841e-02, 1.48478897e-02, - -9.33267411e-02, -1.32871018e-01, 1.67651704e-01, - 7.14720157e-01, 1.00000000e+00, 7.14720157e-01, 1.67651704e-01, - -1.32871018e-01, -9.33267411e-02, 1.48478897e-02, - 2.43170841e-02, 8.06009483e-04, 0.00000000e+00, 0.00000000e+00, - 0.00000000e+00, 0.00000000e+00, 0.00000000e+00 - ]), - decimal=7) + np.array( + [ + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 8.06009483e-04, + 2.43170841e-02, + 1.48478897e-02, + -9.33267411e-02, + -1.32871018e-01, + 1.67651704e-01, + 7.14720157e-01, + 1.00000000e00, + 7.14720157e-01, + 1.67651704e-01, + -1.32871018e-01, + -9.33267411e-02, + 1.48478897e-02, + 2.43170841e-02, + 8.06009483e-04, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( kernel_lanczos(np.linspace(-5, 5, 25), 1), - np.array([ - 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, - 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, - 0.03647563, 0.54451556, 1.00000000, 0.54451556, 0.03647563, - 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, - 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000 - ]), - decimal=7) + np.array( + [ + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.03647563, + 0.54451556, + 1.00000000, + 0.54451556, + 0.03647563, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + ] + ), + decimal=7, + ) class TestKernelCardinalSpline(unittest.TestCase): """ - Defines :func:`colour.algebra.interpolation.kernel_cardinal_spline` - definition units tests methods. + Define :func:`colour.algebra.interpolation.kernel_cardinal_spline` + definition unit tests methods. """ def test_kernel_cardinal_spline(self): """ - Tests :func:`colour.algebra.interpolation.kernel_cardinal_spline` + Test :func:`colour.algebra.interpolation.kernel_cardinal_spline` definition. """ np.testing.assert_almost_equal( kernel_cardinal_spline(np.linspace(-5, 5, 25)), - np.array([ - 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, - 0.00000000, 0.00000000, 0.00000000, -0.03703704, -0.0703125, - 0.13194444, 0.67447917, 1.00000000, 0.67447917, 0.13194444, - -0.0703125, -0.03703704, 0.00000000, 0.00000000, 0.00000000, - 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000 - ]), - decimal=7) + np.array( + [ + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + -0.03703704, + -0.0703125, + 0.13194444, + 0.67447917, + 1.00000000, + 0.67447917, + 0.13194444, + -0.0703125, + -0.03703704, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( kernel_cardinal_spline(np.linspace(-5, 5, 25), 0, 1), - np.array([ - 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, - 0.00000000, 0.00000000, 0.00000000, 0.00617284, 0.0703125, - 0.26157407, 0.52922454, 0.66666667, 0.52922454, 0.26157407, - 0.0703125, 0.00617284, 0.00000000, 0.00000000, 0.00000000, - 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000 - ]), - decimal=7) + np.array( + [ + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00617284, + 0.0703125, + 0.26157407, + 0.52922454, + 0.66666667, + 0.52922454, + 0.26157407, + 0.0703125, + 0.00617284, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + 0.00000000, + ] + ), + decimal=7, + ) class TestKernelInterpolator(unittest.TestCase): """ - Defines :func:`colour.algebra.interpolation.KernelInterpolator` class units + Define :class:`colour.algebra.interpolation.KernelInterpolator` class unit tests methods. """ def test_required_attributes(self): - """ - Tests presence of required attributes. - """ - - required_attributes = ('x', 'y', 'window', 'kernel', 'kernel_kwargs', - 'padding_kwargs') + """Test the presence of required attributes.""" + + required_attributes = ( + "x", + "y", + "window", + "kernel", + "kernel_kwargs", + "padding_kwargs", + ) for attribute in required_attributes: self.assertIn(attribute, dir(KernelInterpolator)) def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__init__', '__call__') + required_methods = ("__init__", "__call__") for method in required_methods: # pragma: no cover self.assertIn(method, dir(KernelInterpolator)) def test_x(self): """ - Tests :func:`colour.algebra.interpolation.KernelInterpolator.x` + Test :attr:`colour.algebra.interpolation.KernelInterpolator.x` property. """ @@ -348,7 +865,7 @@ def test_x(self): def test_y(self): """ - Tests :func:`colour.algebra.interpolation.KernelInterpolator.y` + Test :attr:`colour.algebra.interpolation.KernelInterpolator.y` property. """ @@ -359,7 +876,7 @@ def test_y(self): def test_window(self): """ - Tests :func:`colour.algebra.interpolation.KernelInterpolator.window` + Test :attr:`colour.algebra.interpolation.KernelInterpolator.window` property. """ @@ -370,7 +887,7 @@ def test_window(self): def test_kernel(self): """ - Tests :func:`colour.algebra.interpolation.KernelInterpolator.kernel` + Test :attr:`colour.algebra.interpolation.KernelInterpolator.kernel` property. """ @@ -381,34 +898,37 @@ def test_kernel(self): def test_kernel_kwargs(self): """ - Tests :func:`colour.algebra.interpolation.KernelInterpolator.\ + Test :attr:`colour.algebra.interpolation.KernelInterpolator.\ kernel_kwargs` property. """ x = y = np.linspace(0, 1, 10) - kernel_kwargs = {'a': 1} + kernel_kwargs = {"a": 1} kernel_interpolator = KernelInterpolator( - x, y, kernel_kwargs=kernel_kwargs) + x, y, kernel_kwargs=kernel_kwargs + ) self.assertDictEqual(kernel_interpolator.kernel_kwargs, kernel_kwargs) def test_padding_kwargs(self): """ - Tests :func:`colour.algebra.interpolation.KernelInterpolator.\ + Test :attr:`colour.algebra.interpolation.KernelInterpolator.\ padding_kwargs` property. """ x = y = np.linspace(0, 1, 10) - padding_kwargs = {'pad_width': (3, 3), 'mode': 'mean'} + padding_kwargs = {"pad_width": (3, 3), "mode": "mean"} kernel_interpolator = KernelInterpolator( - x, y, padding_kwargs=padding_kwargs) + x, y, padding_kwargs=padding_kwargs + ) - self.assertDictEqual(kernel_interpolator.padding_kwargs, - padding_kwargs) + self.assertDictEqual( + kernel_interpolator.padding_kwargs, padding_kwargs + ) def test_raise_exception___init__(self): """ - Tests :func:`colour.algebra.interpolation.KernelInterpolator.__init__` + Test :meth:`colour.algebra.interpolation.KernelInterpolator.__init__` method raised exception. """ @@ -421,7 +941,7 @@ def test_raise_exception___init__(self): def test__call__(self): """ - Tests :func:`colour.algebra.interpolation.KernelInterpolator.__call__` + Test :meth:`colour.algebra.interpolation.KernelInterpolator.__call__` method. """ @@ -432,67 +952,181 @@ def test__call__(self): kernel_interpolator = KernelInterpolator(x, y) np.testing.assert_almost_equal( kernel_interpolator(x_i), - np.array([ - 4.43848790, 4.26286480, 3.64640076, 2.77982023, 2.13474499, - 2.08206794, 2.50585862, 3.24992692, 3.84593162, 4.06289704, - 3.80825633, 3.21068994, 2.65177161, 2.32137382, 2.45995375, - 2.88799997, 3.43843598, 3.79504892, 3.79937086, 3.47673343, - 2.99303182, 2.59305006, 2.47805594, 2.82957843, 3.14159265 - ]), - decimal=7) + np.array( + [ + 4.43848790, + 4.26286480, + 3.64640076, + 2.77982023, + 2.13474499, + 2.08206794, + 2.50585862, + 3.24992692, + 3.84593162, + 4.06289704, + 3.80825633, + 3.21068994, + 2.65177161, + 2.32137382, + 2.45995375, + 2.88799997, + 3.43843598, + 3.79504892, + 3.79937086, + 3.47673343, + 2.99303182, + 2.59305006, + 2.47805594, + 2.82957843, + 3.14159265, + ] + ), + decimal=7, + ) kernel_interpolator = KernelInterpolator(x, y, kernel=kernel_sinc) np.testing.assert_almost_equal( kernel_interpolator(x_i), - np.array([ - 4.43848790, 4.47570010, 3.84353906, 3.05959493, 2.53514958, - 2.19916874, 2.93225625, 3.32187855, 4.09458791, 4.23088094, - 3.92591447, 3.53263071, 2.65177161, 2.73541557, 2.65740315, - 3.17077616, 3.69624479, 3.87159620, 4.06433758, 3.56283868, - 3.28312289, 2.79652091, 2.62481419, 3.22117115, 3.14159265 - ]), - decimal=7) + np.array( + [ + 4.43848790, + 4.47570010, + 3.84353906, + 3.05959493, + 2.53514958, + 2.19916874, + 2.93225625, + 3.32187855, + 4.09458791, + 4.23088094, + 3.92591447, + 3.53263071, + 2.65177161, + 2.73541557, + 2.65740315, + 3.17077616, + 3.69624479, + 3.87159620, + 4.06433758, + 3.56283868, + 3.28312289, + 2.79652091, + 2.62481419, + 3.22117115, + 3.14159265, + ] + ), + decimal=7, + ) kernel_interpolator = KernelInterpolator(x, y, window=1) np.testing.assert_almost_equal( kernel_interpolator(x_i), - np.array([ - 4.43848790, 4.96712277, 4.09584229, 3.23991575, 2.80418924, - 2.28470276, 3.20024753, 3.41120944, 4.46416970, 4.57878168, - 4.15371498, 3.92841633, 2.65177161, 3.02110187, 2.79812654, - 3.44218674, 4.00032377, 4.01356870, 4.47633386, 3.70912627, - 3.58365067, 3.14325415, 2.88247572, 3.37531662, 3.14159265 - ]), - decimal=7) + np.array( + [ + 4.43848790, + 4.96712277, + 4.09584229, + 3.23991575, + 2.80418924, + 2.28470276, + 3.20024753, + 3.41120944, + 4.46416970, + 4.57878168, + 4.15371498, + 3.92841633, + 2.65177161, + 3.02110187, + 2.79812654, + 3.44218674, + 4.00032377, + 4.01356870, + 4.47633386, + 3.70912627, + 3.58365067, + 3.14325415, + 2.88247572, + 3.37531662, + 3.14159265, + ] + ), + decimal=7, + ) kernel_interpolator = KernelInterpolator( - x, y, window=1, kernel_kwargs={'a': 1}) + x, y, window=1, kernel_kwargs={"a": 1} + ) np.testing.assert_almost_equal( kernel_interpolator(x_i), - np.array([ - 4.43848790, 3.34379320, 3.62463711, 2.34585418, 2.04767083, - 2.09444849, 2.13349835, 3.10304927, 3.29553153, 3.59884738, - 3.48484031, 2.72974983, 2.65177161, 2.03850468, 2.29470194, - 2.76179863, 2.80189050, 3.75979450, 2.98422257, 3.48444099, - 2.49208997, 2.46516442, 2.42336082, 2.25975903, 3.14159265 - ]), - decimal=7) + np.array( + [ + 4.43848790, + 3.34379320, + 3.62463711, + 2.34585418, + 2.04767083, + 2.09444849, + 2.13349835, + 3.10304927, + 3.29553153, + 3.59884738, + 3.48484031, + 2.72974983, + 2.65177161, + 2.03850468, + 2.29470194, + 2.76179863, + 2.80189050, + 3.75979450, + 2.98422257, + 3.48444099, + 2.49208997, + 2.46516442, + 2.42336082, + 2.25975903, + 3.14159265, + ] + ), + decimal=7, + ) kernel_interpolator = KernelInterpolator( - x, y, padding_kwargs={ - 'pad_width': (3, 3), - 'mode': 'mean' - }) + x, y, padding_kwargs={"pad_width": (3, 3), "mode": "mean"} + ) np.testing.assert_almost_equal( kernel_interpolator(x_i), - np.array([ - 4.4384879, 4.35723245, 3.62918155, 2.77471295, 2.13474499, - 2.08206794, 2.50585862, 3.24992692, 3.84593162, 4.06289704, - 3.80825633, 3.21068994, 2.65177161, 2.32137382, 2.45995375, - 2.88799997, 3.43843598, 3.79504892, 3.79937086, 3.47673343, - 2.99303182, 2.59771985, 2.49380017, 2.76339043, 3.14159265 - ]), - decimal=7) + np.array( + [ + 4.4384879, + 4.35723245, + 3.62918155, + 2.77471295, + 2.13474499, + 2.08206794, + 2.50585862, + 3.24992692, + 3.84593162, + 4.06289704, + 3.80825633, + 3.21068994, + 2.65177161, + 2.32137382, + 2.45995375, + 2.88799997, + 3.43843598, + 3.79504892, + 3.79937086, + 3.47673343, + 2.99303182, + 2.59771985, + 2.49380017, + 2.76339043, + 3.14159265, + ] + ), + decimal=7, + ) x_1 = np.arange(1, 10, 1) x_2 = x_1 * 10 @@ -503,16 +1137,18 @@ def test__call__(self): np.testing.assert_almost_equal( KernelInterpolator(x_1, y)(x_i), KernelInterpolator(x_2, y)(x_i * 10), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( KernelInterpolator(x_1, y)(x_i), KernelInterpolator(x_3, y)(x_i / 10), - decimal=7) + decimal=7, + ) def test_raise_exception___call__(self): """ - Tests :func:`colour.algebra.interpolation.KernelInterpolator.__call__` + Test :meth:`colour.algebra.interpolation.KernelInterpolator.__call__` method raised exception. """ @@ -526,7 +1162,7 @@ def test_raise_exception___call__(self): @ignore_numpy_errors def test_nan__call__(self): """ - Tests :func:`colour.algebra.interpolation.KernelInterpolator.__call__` + Test :meth:`colour.algebra.interpolation.KernelInterpolator.__call__` method nan support. """ @@ -540,14 +1176,12 @@ def test_nan__call__(self): class TestNearestNeighbourInterpolator(unittest.TestCase): """ - Defines :func:`colour.algebra.interpolation.NearestNeighbourInterpolator` - class units tests methods. + Define :class:`colour.algebra.interpolation.NearestNeighbourInterpolator` + class unit tests methods. """ def test_required_attributes(self): - """ - Tests presence of required attributes. - """ + """Test the presence of required attributes.""" required_attributes = () @@ -555,57 +1189,52 @@ def test_required_attributes(self): self.assertIn(attribute, dir(NearestNeighbourInterpolator)) def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__init__', ) + required_methods = ("__init__",) for method in required_methods: # pragma: no cover self.assertIn(method, dir(NearestNeighbourInterpolator)) def test___init__(self): """ - Tests :func:`colour.algebra.interpolation.KernelInterpolator.__init__` + Test :meth:`colour.algebra.interpolation.KernelInterpolator.__init__` method. """ x = y = np.linspace(0, 1, 10) nearest_neighbour_interpolator = NearestNeighbourInterpolator( - x, y, kernel_kwargs={'a': 1}) + x, y, kernel_kwargs={"a": 1} + ) self.assertDictEqual(nearest_neighbour_interpolator.kernel_kwargs, {}) class TestLinearInterpolator(unittest.TestCase): """ - Defines :func:`colour.algebra.interpolation.LinearInterpolator` class units + Define :class:`colour.algebra.interpolation.LinearInterpolator` class unit tests methods. """ def test_required_attributes(self): - """ - Tests presence of required attributes. - """ + """Test the presence of required attributes.""" - required_attributes = ('x', 'y') + required_attributes = ("x", "y") for attribute in required_attributes: self.assertIn(attribute, dir(LinearInterpolator)) def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__init__', '__call__') + required_methods = ("__init__", "__call__") for method in required_methods: # pragma: no cover self.assertIn(method, dir(LinearInterpolator)) def test_raise_exception___init__(self): """ - Tests :func:`colour.algebra.interpolation.LinearInterpolator.__init__` + Test :meth:`colour.algebra.interpolation.LinearInterpolator.__init__` method raised exception. """ @@ -614,7 +1243,7 @@ def test_raise_exception___init__(self): def test__call__(self): """ - Tests :func:`colour.algebra.interpolation.LinearInterpolator.__call__` + Test :meth:`colour.algebra.interpolation.LinearInterpolator.__call__` method. """ @@ -623,22 +1252,24 @@ def test__call__(self): linear_interpolator = LinearInterpolator(x, DATA_POINTS_A) for i, value in enumerate( - np.arange(0, - len(DATA_POINTS_A) - 1 + interval, interval)): + np.arange(0, len(DATA_POINTS_A) - 1 + interval, interval) + ): self.assertAlmostEqual( DATA_POINTS_A_LINEAR_INTERPOLATED_10_SAMPLES[i], linear_interpolator(value), - places=7) + places=7, + ) np.testing.assert_almost_equal( linear_interpolator( - np.arange(0, - len(DATA_POINTS_A) - 1 + interval, interval)), - DATA_POINTS_A_LINEAR_INTERPOLATED_10_SAMPLES) + np.arange(0, len(DATA_POINTS_A) - 1 + interval, interval) + ), + DATA_POINTS_A_LINEAR_INTERPOLATED_10_SAMPLES, + ) def test_raise_exception___call__(self): """ - Tests :func:`colour.algebra.interpolation.LinearInterpolator.__call__` + Test :meth:`colour.algebra.interpolation.LinearInterpolator.__call__` method raised exception. """ @@ -652,7 +1283,7 @@ def test_raise_exception___call__(self): @ignore_numpy_errors def test_nan__call__(self): """ - Tests :func:`colour.algebra.interpolation.LinearInterpolator.__call__` + Test :meth:`colour.algebra.interpolation.LinearInterpolator.__call__` method nan support. """ @@ -661,7 +1292,8 @@ def test_nan__call__(self): for case in cases: try: linear_interpolator = LinearInterpolator( - np.array(case), np.array(case)) + np.array(case), np.array(case) + ) linear_interpolator(case[0]) except ValueError: pass @@ -669,33 +1301,29 @@ def test_nan__call__(self): class TestSpragueInterpolator(unittest.TestCase): """ - Defines :func:`colour.algebra.interpolation.SpragueInterpolator` class + Define :class:`colour.algebra.interpolation.SpragueInterpolator` class unit tests methods. """ def test_required_attributes(self): - """ - Tests presence of required attributes. - """ + """Test the presence of required attributes.""" - required_attributes = ('x', 'y') + required_attributes = ("x", "y") for attribute in required_attributes: self.assertIn(attribute, dir(SpragueInterpolator)) def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__init__', '__call__') + required_methods = ("__init__", "__call__") for method in required_methods: # pragma: no cover self.assertIn(method, dir(SpragueInterpolator)) def test_raise_exception___init__(self): """ - Tests :func:`colour.algebra.interpolation.SpragueInterpolator.__init__` + Test :meth:`colour.algebra.interpolation.SpragueInterpolator.__init__` method raised exception. """ @@ -704,7 +1332,7 @@ def test_raise_exception___init__(self): def test__call__(self): """ - Tests :func:`colour.algebra.interpolation.SpragueInterpolator.__call__` + Test :meth:`colour.algebra.interpolation.SpragueInterpolator.__call__` method. """ @@ -713,22 +1341,24 @@ def test__call__(self): sprague_interpolator = SpragueInterpolator(x, DATA_POINTS_A) for i, value in enumerate( - np.arange(0, - len(DATA_POINTS_A) - 1 + interval, interval)): + np.arange(0, len(DATA_POINTS_A) - 1 + interval, interval) + ): self.assertAlmostEqual( DATA_POINTS_A_SPRAGUE_INTERPOLATED_10_SAMPLES[i], sprague_interpolator(value), - places=7) + places=7, + ) np.testing.assert_almost_equal( sprague_interpolator( - np.arange(0, - len(DATA_POINTS_A) - 1 + interval, interval)), - DATA_POINTS_A_SPRAGUE_INTERPOLATED_10_SAMPLES) + np.arange(0, len(DATA_POINTS_A) - 1 + interval, interval) + ), + DATA_POINTS_A_SPRAGUE_INTERPOLATED_10_SAMPLES, + ) def test_raise_exception___call__(self): """ - Tests :func:`colour.algebra.interpolation.SpragueInterpolator.__call__` + Test :meth:`colour.algebra.interpolation.SpragueInterpolator.__call__` method raised exception. """ @@ -742,7 +1372,7 @@ def test_raise_exception___call__(self): @ignore_numpy_errors def test_nan__call__(self): """ - Tests :func:`colour.algebra.interpolation.SpragueInterpolator.__call__` + Test :meth:`colour.algebra.interpolation.SpragueInterpolator.__call__` method nan support. """ @@ -751,7 +1381,8 @@ def test_nan__call__(self): for case in cases: try: sprague_interpolator = SpragueInterpolator( - np.array(case), np.array(case)) + np.array(case), np.array(case) + ) sprague_interpolator(case[0]) # pragma: no cover except AssertionError: pass @@ -759,14 +1390,14 @@ def test_nan__call__(self): class TestCubicSplineInterpolator(unittest.TestCase): """ - Defines :func:`colour.algebra.interpolation.CubicSplineInterpolator` class + Define :class:`colour.algebra.interpolation.CubicSplineInterpolator` class unit tests methods. """ def test__call__(self): """ - Tests :func:`colour.algebra.interpolation.\ -CubicSplineInterpolator.__call__` method. + Test :meth:`colour.algebra.interpolation.CubicSplineInterpolator.\ +__call__` method. Notes ----- @@ -776,34 +1407,30 @@ def test__call__(self): np.testing.assert_almost_equal( CubicSplineInterpolator( - np.linspace(0, 1, len(DATA_POINTS_A)), - DATA_POINTS_A)(np.linspace(0, 1, - len(DATA_POINTS_A) * 2)), - DATA_POINTS_A_CUBIC_SPLINE_INTERPOLATED_X2_SAMPLES) + np.linspace(0, 1, len(DATA_POINTS_A)), DATA_POINTS_A + )(np.linspace(0, 1, len(DATA_POINTS_A) * 2)), + DATA_POINTS_A_CUBIC_SPLINE_INTERPOLATED_X2_SAMPLES, + ) class TestPchipInterpolator(unittest.TestCase): """ - Defines :func:`colour.algebra.interpolation.PchipInterpolator` class + Define :class:`colour.algebra.interpolation.PchipInterpolator` class unit tests methods. """ def test_required_attributes(self): - """ - Tests presence of required attributes. - """ + """Test the presence of required attributes.""" - required_attributes = ('x', 'y') + required_attributes = ("x", "y") for attribute in required_attributes: self.assertIn(attribute, dir(PchipInterpolator)) def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__init__', ) + required_methods = ("__init__",) for method in required_methods: # pragma: no cover self.assertIn(method, dir(PchipInterpolator)) @@ -811,33 +1438,29 @@ def test_required_methods(self): class TestNullInterpolator(unittest.TestCase): """ - Defines :func:`colour.algebra.interpolation.NullInterpolator` class + Define :class:`colour.algebra.interpolation.NullInterpolator` class unit tests methods. """ def test_required_attributes(self): - """ - Tests presence of required attributes. - """ + """Test the presence of required attributes.""" - required_attributes = ('x', 'y') + required_attributes = ("x", "y") for attribute in required_attributes: self.assertIn(attribute, dir(NullInterpolator)) def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__init__', '__call__') + required_methods = ("__init__", "__call__") for method in required_methods: # pragma: no cover self.assertIn(method, dir(NullInterpolator)) def test_x(self): """ - Tests :func:`colour.algebra.interpolation.NullInterpolator.x` + Test :attr:`colour.algebra.interpolation.NullInterpolator.x` property. """ @@ -848,7 +1471,7 @@ def test_x(self): def test_y(self): """ - Tests :func:`colour.algebra.interpolation.NullInterpolator.y` + Test :attr:`colour.algebra.interpolation.NullInterpolator.y` property. """ @@ -859,7 +1482,7 @@ def test_y(self): def test_absolute_tolerance(self): """ - Tests :func:`colour.algebra.interpolation.NullInterpolator.\ + Test :attr:`colour.algebra.interpolation.NullInterpolator.\ absolute_tolerance` property. """ @@ -870,7 +1493,7 @@ def test_absolute_tolerance(self): def test_relative_tolerance(self): """ - Tests :func:`colour.algebra.interpolation.NullInterpolator.\ + Test :attr:`colour.algebra.interpolation.NullInterpolator.\ relative_tolerance` property. """ @@ -881,7 +1504,7 @@ def test_relative_tolerance(self): def test_default(self): """ - Tests :func:`colour.algebra.interpolation.NullInterpolator.\ + Test :attr:`colour.algebra.interpolation.NullInterpolator.\ default` property. """ @@ -892,7 +1515,7 @@ def test_default(self): def test_raise_exception___init__(self): """ - Tests :func:`colour.algebra.interpolation.NullInterpolator.__init__` + Test :meth:`colour.algebra.interpolation.NullInterpolator.__init__` method raised exception. """ @@ -901,7 +1524,7 @@ def test_raise_exception___init__(self): def test__call__(self): """ - Tests :func:`colour.algebra.interpolation.NullInterpolator.__call__` + Test :meth:`colour.algebra.interpolation.NullInterpolator.__call__` method. """ @@ -909,16 +1532,18 @@ def test__call__(self): null_interpolator = NullInterpolator(x, DATA_POINTS_A) np.testing.assert_almost_equal( null_interpolator(np.array([0.75, 2.0, 3.0, 4.75])), - np.array([np.nan, 12.46, 9.51, np.nan])) + np.array([np.nan, 12.46, 9.51, np.nan]), + ) null_interpolator = NullInterpolator(x, DATA_POINTS_A, 0.25, 0.25) np.testing.assert_almost_equal( null_interpolator(np.array([0.75, 2.0, 3.0, 4.75])), - np.array([12.32, 12.46, 9.51, 4.33])) + np.array([12.32, 12.46, 9.51, 4.33]), + ) def test_raise_exception___call__(self): """ - Tests :func:`colour.algebra.interpolation.NullInterpolator.__call__` + Test :meth:`colour.algebra.interpolation.NullInterpolator.__call__` method raised exception. """ @@ -932,7 +1557,7 @@ def test_raise_exception___call__(self): @ignore_numpy_errors def test_nan__call__(self): """ - Tests :func:`colour.algebra.interpolation.NullInterpolator.__call__` + Test :meth:`colour.algebra.interpolation.NullInterpolator.__call__` method nan support. """ @@ -941,7 +1566,8 @@ def test_nan__call__(self): for case in cases: try: null_interpolator = NullInterpolator( - np.array(case), np.array(case)) + np.array(case), np.array(case) + ) null_interpolator(case[0]) except ValueError: pass @@ -949,13 +1575,13 @@ def test_nan__call__(self): class TestLagrangeCoefficients(unittest.TestCase): """ - Defines :func:`colour.algebra.interpolation.lagrange_coefficients` + Define :func:`colour.algebra.interpolation.lagrange_coefficients` definition unit tests methods. """ def test_lagrange_coefficients(self): """ - Tests :func:`colour.algebra.interpolation.lagrange_coefficients` + Test :func:`colour.algebra.interpolation.lagrange_coefficients` definition. Notes @@ -977,13 +1603,13 @@ def test_lagrange_coefficients(self): class TestVerticesAndRelativeCoordinates(unittest.TestCase): """ - Defines :func:`colour.algebra.interpolation.\ + Define :func:`colour.algebra.interpolation.\ vertices_and_relative_coordinates` definition unit tests methods. """ def test_vertices_and_relative_coordinates(self): """ - Tests :func:`colour.algebra.interpolation.\ + Test :func:`colour.algebra.interpolation.\ vertices_and_relative_coordinates` definition. """ @@ -994,75 +1620,81 @@ def test_vertices_and_relative_coordinates(self): np.testing.assert_almost_equal( vertices, - np.array([ - [ - [0.58919500, 0.58919500, 0.13916400], - [0.33333300, 0.00000000, 0.33333300], - [0.83331100, 0.83331100, 0.83331100], - [0.79789400, -0.03541200, -0.03541200], - ], - [ - [0.59460100, 0.59460100, 0.36958600], - [0.39062300, 0.00000000, 0.78124600], - [0.83331100, 0.83331100, 1.24996300], - [0.75276700, -0.02847900, 0.36214400], - ], - [ - [0.66343200, 0.93018800, 0.12992000], - [0.41665500, 0.41665500, 0.41665500], - [0.70710200, 1.11043500, 0.70710200], - [0.63333300, 0.31666700, 0.00000000], - ], - [ - [0.68274900, 0.99108200, 0.37441600], - [0.41665500, 0.41665500, 0.83330800], - [0.51971400, 0.74472900, 0.74472900], - [0.73227800, 0.31562600, 0.31562600], - ], - [ - [0.89131800, 0.61982300, 0.07683300], - [0.75276700, -0.02847900, 0.36214400], - [1.06561000, 0.64895700, 0.64895700], - [1.19684100, -0.05311700, -0.05311700], - ], + np.array( [ - [0.95000000, 0.63333300, 0.31666700], - [0.66666700, 0.00000000, 0.66666700], - [1.00000000, 0.66666700, 1.00000000], - [1.16258800, -0.05037200, 0.35394800], - ], - [ - [0.88379200, 0.88379200, 0.20874600], - [0.73227800, 0.31562600, 0.31562600], - [0.89460600, 0.89460600, 0.66959000], - [1.03843900, 0.31089900, -0.05287000], - ], - [ - [0.88919900, 0.88919900, 0.43916800], - [0.66666700, 0.33333300, 0.66666700], - [1.24996600, 1.24996600, 1.24996600], - [1.13122500, 0.29792000, 0.29792000], - ], - ])) + [ + [0.58919500, 0.58919500, 0.13916400], + [0.33333300, 0.00000000, 0.33333300], + [0.83331100, 0.83331100, 0.83331100], + [0.79789400, -0.03541200, -0.03541200], + ], + [ + [0.59460100, 0.59460100, 0.36958600], + [0.39062300, 0.00000000, 0.78124600], + [0.83331100, 0.83331100, 1.24996300], + [0.75276700, -0.02847900, 0.36214400], + ], + [ + [0.66343200, 0.93018800, 0.12992000], + [0.41665500, 0.41665500, 0.41665500], + [0.70710200, 1.11043500, 0.70710200], + [0.63333300, 0.31666700, 0.00000000], + ], + [ + [0.68274900, 0.99108200, 0.37441600], + [0.41665500, 0.41665500, 0.83330800], + [0.51971400, 0.74472900, 0.74472900], + [0.73227800, 0.31562600, 0.31562600], + ], + [ + [0.89131800, 0.61982300, 0.07683300], + [0.75276700, -0.02847900, 0.36214400], + [1.06561000, 0.64895700, 0.64895700], + [1.19684100, -0.05311700, -0.05311700], + ], + [ + [0.95000000, 0.63333300, 0.31666700], + [0.66666700, 0.00000000, 0.66666700], + [1.00000000, 0.66666700, 1.00000000], + [1.16258800, -0.05037200, 0.35394800], + ], + [ + [0.88379200, 0.88379200, 0.20874600], + [0.73227800, 0.31562600, 0.31562600], + [0.89460600, 0.89460600, 0.66959000], + [1.03843900, 0.31089900, -0.05287000], + ], + [ + [0.88919900, 0.88919900, 0.43916800], + [0.66666700, 0.33333300, 0.66666700], + [1.24996600, 1.24996600, 1.24996600], + [1.13122500, 0.29792000, 0.29792000], + ], + ] + ), + ) np.testing.assert_almost_equal( V_xyzr, - np.array([ - [0.90108952, 0.09318647, 0.75894709], - [0.64169675, 0.64826849, 0.30437460], - [0.91805308, 0.92882336, 0.33814877], - [0.14444798, 0.01869077, 0.59305522], - ])) + np.array( + [ + [0.90108952, 0.09318647, 0.75894709], + [0.64169675, 0.64826849, 0.30437460], + [0.91805308, 0.92882336, 0.33814877], + [0.14444798, 0.01869077, 0.59305522], + ] + ), + ) class TestTableInterpolationTrilinear(unittest.TestCase): """ - Defines :func:`colour.algebra.interpolation.\ + Define :func:`colour.algebra.interpolation.\ table_interpolation_trilinear` definition unit tests methods. """ def test_interpolation_trilinear(self): """ - Tests :func:`colour.algebra.interpolation.\ + Test :func:`colour.algebra.interpolation.\ table_interpolation_trilinear` definition. """ @@ -1072,35 +1704,38 @@ def test_interpolation_trilinear(self): np.testing.assert_almost_equal( table_interpolation_trilinear(V_xyz, LUT_TABLE), - np.array([ - [1.07937594, -0.02773926, 0.55498254], - [0.53983424, 0.37099516, 0.13994561], - [1.13449122, -0.00305380, 0.13792909], - [0.73411897, 1.00141020, 0.59348239], - [0.74066176, 0.44679540, 0.55030394], - [0.20634750, 0.84797880, 0.55905579], - [0.92348649, 0.73112515, 0.42362820], - [0.03639248, 0.70357649, 0.52375041], - [0.29215488, 0.19697840, 0.44603879], - [0.47793470, 0.08696360, 0.70288463], - [0.88883354, 0.68680856, 0.87404642], - [0.21430977, 0.16796653, 0.19634247], - [0.82118989, 0.69239283, 0.39932389], - [1.06679072, 0.37974319, 0.49759377], - [0.17856230, 0.44755467, 0.62045271], - [0.59220355, 0.93136492, 0.30063692], - ])) + np.array( + [ + [1.07937594, -0.02773926, 0.55498254], + [0.53983424, 0.37099516, 0.13994561], + [1.13449122, -0.00305380, 0.13792909], + [0.73411897, 1.00141020, 0.59348239], + [0.74066176, 0.44679540, 0.55030394], + [0.20634750, 0.84797880, 0.55905579], + [0.92348649, 0.73112515, 0.42362820], + [0.03639248, 0.70357649, 0.52375041], + [0.29215488, 0.19697840, 0.44603879], + [0.47793470, 0.08696360, 0.70288463], + [0.88883354, 0.68680856, 0.87404642], + [0.21430977, 0.16796653, 0.19634247], + [0.82118989, 0.69239283, 0.39932389], + [1.06679072, 0.37974319, 0.49759377], + [0.17856230, 0.44755467, 0.62045271], + [0.59220355, 0.93136492, 0.30063692], + ] + ), + ) class TestTableInterpolationTetrahedral(unittest.TestCase): """ - Defines :func:`colour.algebra.interpolation.\ + Define :func:`colour.algebra.interpolation.\ table_interpolation_tetrahedral` definition unit tests methods. """ def test_interpolation_tetrahedral(self): """ - Tests :func:`colour.algebra.interpolation.\ + Test :func:`colour.algebra.interpolation.\ table_interpolation_tetrahedral` definition. """ @@ -1110,25 +1745,28 @@ def test_interpolation_tetrahedral(self): np.testing.assert_almost_equal( table_interpolation_tetrahedral(V_xyz, LUT_TABLE), - np.array([ - [1.08039215, -0.02840092, 0.55855303], - [0.52208945, 0.35297753, 0.13599555], - [1.14373467, -0.00422138, 0.13413290], - [0.71384967, 0.98420883, 0.57982724], - [0.76771576, 0.46280975, 0.55106736], - [0.20861663, 0.85077712, 0.57102264], - [0.90398698, 0.72351675, 0.41151955], - [0.03749453, 0.70226823, 0.52614254], - [0.29626758, 0.21645072, 0.47615873], - [0.46729624, 0.07494851, 0.68892548], - [0.85907681, 0.67744258, 0.84410486], - [0.24335535, 0.20896545, 0.21996717], - [0.79244027, 0.66930773, 0.39213595], - [1.08383608, 0.37985897, 0.49011919], - [0.14683649, 0.43624903, 0.58706947], - [0.61272658, 0.92799297, 0.29650424], - ])) - - -if __name__ == '__main__': + np.array( + [ + [1.08039215, -0.02840092, 0.55855303], + [0.52208945, 0.35297753, 0.13599555], + [1.14373467, -0.00422138, 0.13413290], + [0.71384967, 0.98420883, 0.57982724], + [0.76771576, 0.46280975, 0.55106736], + [0.20861663, 0.85077712, 0.57102264], + [0.90398698, 0.72351675, 0.41151955], + [0.03749453, 0.70226823, 0.52614254], + [0.29626758, 0.21645072, 0.47615873], + [0.46729624, 0.07494851, 0.68892548], + [0.85907681, 0.67744258, 0.84410486], + [0.24335535, 0.20896545, 0.21996717], + [0.79244027, 0.66930773, 0.39213595], + [1.08383608, 0.37985897, 0.49011919], + [0.14683649, 0.43624903, 0.58706947], + [0.61272658, 0.92799297, 0.29650424], + ] + ), + ) + + +if __name__ == "__main__": unittest.main() diff --git a/colour/algebra/tests/test_matrix.py b/colour/algebra/tests/test_matrix.py deleted file mode 100644 index 15ff9de6a0..0000000000 --- a/colour/algebra/tests/test_matrix.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.algebra.matrix` module. -""" - -from __future__ import division, unicode_literals - -import numpy as np -import unittest - -from colour.algebra import is_identity - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['TestIsIdentity'] - - -class TestIsIdentity(unittest.TestCase): - """ - Defines :func:`colour.algebra.matrix.is_identity` definition unit tests - methods. - """ - - def test_is_identity(self): - """ - Tests :func:`colour.algebra.matrix.is_identity` definition. - """ - - self.assertTrue( - is_identity(np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]).reshape([3, 3]))) - - self.assertFalse( - is_identity(np.array([1, 2, 0, 0, 1, 0, 0, 0, 1]).reshape([3, 3]))) - - self.assertTrue( - is_identity(np.array([1, 0, 0, 1]).reshape([2, 2]), n=2)) - - self.assertFalse( - is_identity(np.array([1, 2, 0, 1]).reshape([2, 2]), n=2)) - - -if __name__ == '__main__': - unittest.main() diff --git a/colour/algebra/tests/test_random.py b/colour/algebra/tests/test_random.py index ed58a99a89..128bb236a8 100644 --- a/colour/algebra/tests/test_random.py +++ b/colour/algebra/tests/test_random.py @@ -1,6 +1,6 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.algebra.random` module. +Defines the unit tests for the :mod:`colour.algebra.random` module. + References ---------- - :cite:`Laurent2012a` : Laurent. (2012). Reproducibility of python @@ -10,45 +10,51 @@ reproducibility-of-python-pseudo-random-numbers-across-systems-and-versions """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import unittest from colour.algebra import random_triplet_generator +from colour.hints import NDArray -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['RANDOM_TRIPLETS', 'TestRandomTripletGenerator'] +__all__ = [ + "RANDOM_TRIPLETS", + "TestRandomTripletGenerator", +] -RANDOM_TRIPLETS = np.array([ - [0.96702984, 0.77938292, 0.43614665], - [0.54723225, 0.19768507, 0.94897731], - [0.97268436, 0.86299324, 0.78630599], - [0.71481599, 0.98340068, 0.86628930], - [0.69772882, 0.16384224, 0.17316542], - [0.21608950, 0.59733394, 0.07494859], - [0.97627445, 0.00898610, 0.60074272], - [0.00623026, 0.38657128, 0.16797218], - [0.25298236, 0.04416006, 0.73338017], - [0.43479153, 0.95665297, 0.40844386], -]) +RANDOM_TRIPLETS: NDArray = np.array( + [ + [0.96702984, 0.77938292, 0.43614665], + [0.54723225, 0.19768507, 0.94897731], + [0.97268436, 0.86299324, 0.78630599], + [0.71481599, 0.98340068, 0.86628930], + [0.69772882, 0.16384224, 0.17316542], + [0.21608950, 0.59733394, 0.07494859], + [0.97627445, 0.00898610, 0.60074272], + [0.00623026, 0.38657128, 0.16797218], + [0.25298236, 0.04416006, 0.73338017], + [0.43479153, 0.95665297, 0.40844386], + ] +) class TestRandomTripletGenerator(unittest.TestCase): """ - Defines :func:`colour.algebra.random.random_triplet_generator` definition + Define :func:`colour.algebra.random.random_triplet_generator` definition unit tests methods. """ def test_random_triplet_generator(self): """ - Tests :func:`colour.algebra.random.random_triplet_generator` + Test :func:`colour.algebra.random.random_triplet_generator` definition. Notes @@ -64,11 +70,9 @@ def test_random_triplet_generator(self): np.testing.assert_almost_equal( RANDOM_TRIPLETS, random_triplet_generator(10, random_state=prng), - decimal=7) - - # TODO: Use "assertWarns" when dropping Python 2.7. - random_triplet_generator(5.5, random_state=prng) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/algebra/tests/test_regression.py b/colour/algebra/tests/test_regression.py index 910ad0f481..be95d4ea60 100644 --- a/colour/algebra/tests/test_regression.py +++ b/colour/algebra/tests/test_regression.py @@ -1,34 +1,31 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.algebra.regression` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.algebra.regression` module.""" import numpy as np import unittest from colour.algebra import least_square_mapping_MoorePenrose -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestLeastSquareMappingMoorePenrose'] +__all__ = [ + "TestLeastSquareMappingMoorePenrose", +] class TestLeastSquareMappingMoorePenrose(unittest.TestCase): """ - Defines :func:`colour.algebra.regression.\ + Define :func:`colour.algebra.regression.\ least_square_mapping_MoorePenrose` definition unit tests methods. """ def test_least_square_mapping_MoorePenrose(self): """ - Tests :func:`colour.algebra.regression.\ + Test :func:`colour.algebra.regression.\ least_square_mapping_MoorePenrose` definition. """ @@ -38,37 +35,55 @@ def test_least_square_mapping_MoorePenrose(self): np.testing.assert_almost_equal( least_square_mapping_MoorePenrose(y, x), - np.array([ - [1.05263767, 0.13780789, -0.22763399], - [0.07395843, 1.02939945, -0.10601150], - [0.05725508, -0.20526336, 1.10151945], - ]), - decimal=7) + np.array( + [ + [1.05263767, 0.13780789, -0.22763399], + [0.07395843, 1.02939945, -0.10601150], + [0.05725508, -0.20526336, 1.10151945], + ] + ), + decimal=7, + ) y = prng.random_sample((4, 3, 2)) x = y + (prng.random_sample((4, 3, 2)) - 0.5) * 0.5 np.testing.assert_almost_equal( least_square_mapping_MoorePenrose(y, x), - np.array([ - [ - [[1.05968114, -0.0896093, -0.02923021], - [3.77254737, 0.06682885, -2.78161763]], - [[-0.77388532, 1.78761209, -0.44050114], - [-4.1282882, 0.55185528, 5.049136]], - [[0.36246422, -0.56421525, 1.4208154], - [2.07589501, 0.40261387, -1.47059455]], - ], + np.array( [ - [[0.237067, 0.4794514, 0.04004058], - [0.67778963, 0.15901967, 0.23854131]], - [[-0.4225357, 0.99316309, -0.14598921], - [-3.46789045, 1.09102153, 3.31051434]], - [[-0.91661817, 1.49060435, -0.45074387], - [-4.18896905, 0.25487186, 4.75951391]], - ], - ]), - decimal=7) + [ + [ + [1.05968114, -0.0896093, -0.02923021], + [3.77254737, 0.06682885, -2.78161763], + ], + [ + [-0.77388532, 1.78761209, -0.44050114], + [-4.1282882, 0.55185528, 5.049136], + ], + [ + [0.36246422, -0.56421525, 1.4208154], + [2.07589501, 0.40261387, -1.47059455], + ], + ], + [ + [ + [0.237067, 0.4794514, 0.04004058], + [0.67778963, 0.15901967, 0.23854131], + ], + [ + [-0.4225357, 0.99316309, -0.14598921], + [-3.46789045, 1.09102153, 3.31051434], + ], + [ + [-0.91661817, 1.49060435, -0.45074387], + [-4.18896905, 0.25487186, 4.75951391], + ], + ], + ] + ), + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/appearance/__init__.py b/colour/appearance/__init__.py index 1ea9b73b5a..af848205bd 100644 --- a/colour/appearance/__init__.py +++ b/colour/appearance/__init__.py @@ -1,139 +1,124 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - -import sys - -from colour.utilities.deprecation import ModuleAPI, build_API_changes -from colour.utilities.documentation import is_documentation_building - -from .hunt import (InductionFactors_Hunt, VIEWING_CONDITIONS_HUNT, - CAM_Specification_Hunt, XYZ_to_Hunt) +from .hunt import ( + InductionFactors_Hunt, + VIEWING_CONDITIONS_HUNT, + CAM_Specification_Hunt, + XYZ_to_Hunt, +) from .atd95 import CAM_Specification_ATD95, XYZ_to_ATD95 -from .ciecam02 import (InductionFactors_CIECAM02, VIEWING_CONDITIONS_CIECAM02, - CAM_Specification_CIECAM02, XYZ_to_CIECAM02, - CIECAM02_to_XYZ) -from .cam16 import (InductionFactors_CAM16, VIEWING_CONDITIONS_CAM16, - CAM_Specification_CAM16, XYZ_to_CAM16, CAM16_to_XYZ) -from .llab import (InductionFactors_LLAB, VIEWING_CONDITIONS_LLAB, - CAM_Specification_LLAB, XYZ_to_LLAB) +from .ciecam02 import ( + InductionFactors_CIECAM02, + VIEWING_CONDITIONS_CIECAM02, + CAM_KWARGS_CIECAM02_sRGB, + CAM_Specification_CIECAM02, + XYZ_to_CIECAM02, + CIECAM02_to_XYZ, +) +from .cam16 import ( + InductionFactors_CAM16, + VIEWING_CONDITIONS_CAM16, + CAM_Specification_CAM16, + XYZ_to_CAM16, + CAM16_to_XYZ, +) +from .hke import ( + HKE_NAYATANI1997_METHODS, + HelmholtzKohlrausch_effect_object_Nayatani1997, + HelmholtzKohlrausch_effect_luminous_Nayatani1997, +) +from .hke import coefficient_q_Nayatani1997, coefficient_K_Br_Nayatani1997 +from .kim2009 import ( + InductionFactors_Kim2009, + VIEWING_CONDITIONS_KIM2009, + MediaParameters_Kim2009, + MEDIA_PARAMETERS_KIM2009, + CAM_Specification_Kim2009, + XYZ_to_Kim2009, + Kim2009_to_XYZ, +) +from .llab import ( + InductionFactors_LLAB, + VIEWING_CONDITIONS_LLAB, + CAM_Specification_LLAB, + XYZ_to_LLAB, +) from .nayatani95 import CAM_Specification_Nayatani95, XYZ_to_Nayatani95 -from .rlab import (VIEWING_CONDITIONS_RLAB, D_FACTOR_RLAB, - CAM_Specification_RLAB, XYZ_to_RLAB) +from .rlab import ( + VIEWING_CONDITIONS_RLAB, + D_FACTOR_RLAB, + CAM_Specification_RLAB, + XYZ_to_RLAB, +) +from .zcam import ( + InductionFactors_ZCAM, + VIEWING_CONDITIONS_ZCAM, + CAM_Specification_ZCAM, + XYZ_to_ZCAM, + ZCAM_to_XYZ, +) __all__ = [ - 'InductionFactors_Hunt', 'VIEWING_CONDITIONS_HUNT', - 'CAM_Specification_Hunt', 'XYZ_to_Hunt' + "InductionFactors_Hunt", + "VIEWING_CONDITIONS_HUNT", + "CAM_Specification_Hunt", + "XYZ_to_Hunt", ] -__all__ += ['CAM_Specification_ATD95', 'XYZ_to_ATD95'] __all__ += [ - 'InductionFactors_CIECAM02', 'VIEWING_CONDITIONS_CIECAM02', - 'CAM_Specification_CIECAM02', 'XYZ_to_CIECAM02', 'CIECAM02_to_XYZ' + "CAM_Specification_ATD95", + "XYZ_to_ATD95", ] __all__ += [ - 'InductionFactors_CAM16', 'VIEWING_CONDITIONS_CAM16', - 'CAM_Specification_CAM16', 'XYZ_to_CAM16', 'CAM16_to_XYZ' + "InductionFactors_CIECAM02", + "VIEWING_CONDITIONS_CIECAM02", + "CAM_KWARGS_CIECAM02_sRGB", + "CAM_Specification_CIECAM02", + "XYZ_to_CIECAM02", + "CIECAM02_to_XYZ", ] __all__ += [ - 'InductionFactors_LLAB', 'VIEWING_CONDITIONS_LLAB', - 'CAM_Specification_LLAB', 'XYZ_to_LLAB' + "InductionFactors_CAM16", + "VIEWING_CONDITIONS_CAM16", + "CAM_Specification_CAM16", + "XYZ_to_CAM16", + "CAM16_to_XYZ", ] -__all__ += ['CAM_Specification_Nayatani95', 'XYZ_to_Nayatani95'] __all__ += [ - 'VIEWING_CONDITIONS_RLAB', 'D_FACTOR_RLAB', 'CAM_Specification_RLAB', - 'XYZ_to_RLAB' + "HKE_NAYATANI1997_METHODS", + "HelmholtzKohlrausch_effect_object_Nayatani1997", + "HelmholtzKohlrausch_effect_luminous_Nayatani1997", +] +__all__ += [ + "coefficient_q_Nayatani1997", + "coefficient_K_Br_Nayatani1997", +] +__all__ += [ + "InductionFactors_Kim2009", + "VIEWING_CONDITIONS_KIM2009", + "MediaParameters_Kim2009", + "MEDIA_PARAMETERS_KIM2009", + "CAM_Specification_Kim2009", + "XYZ_to_Kim2009", + "Kim2009_to_XYZ", +] +__all__ += [ + "InductionFactors_LLAB", + "VIEWING_CONDITIONS_LLAB", + "CAM_Specification_LLAB", + "XYZ_to_LLAB", +] +__all__ += [ + "CAM_Specification_Nayatani95", + "XYZ_to_Nayatani95", +] +__all__ += [ + "VIEWING_CONDITIONS_RLAB", + "D_FACTOR_RLAB", + "CAM_Specification_RLAB", + "XYZ_to_RLAB", +] +__all__ += [ + "InductionFactors_ZCAM", + "VIEWING_CONDITIONS_ZCAM", + "CAM_Specification_ZCAM", + "XYZ_to_ZCAM", + "ZCAM_to_XYZ", ] - - -# ----------------------------------------------------------------------------# -# --- API Changes and Deprecation Management ---# -# ----------------------------------------------------------------------------# -class appearance(ModuleAPI): - def __getattr__(self, attribute): - return super(appearance, self).__getattr__(attribute) - - -# v0.3.16 -API_CHANGES = { - 'ObjectRenamed': [ - [ - 'colour.appearance.ATD95_Specification', - 'colour.appearance.CAM_Specification_ATD95', - ], - [ - 'colour.appearance.CAM16_InductionFactors', - 'colour.appearance.InductionFactors_CAM16', - ], - [ - 'colour.appearance.CAM16_VIEWING_CONDITIONS', - 'colour.appearance.VIEWING_CONDITIONS_CAM16', - ], - [ - 'colour.appearance.CAM16_Specification', - 'colour.appearance.CAM_Specification_CAM16', - ], - [ - 'colour.appearance.CIECAM02_InductionFactors', - 'colour.appearance.InductionFactors_CIECAM02', - ], - [ - 'colour.appearance.CIECAM02_VIEWING_CONDITIONS', - 'colour.appearance.VIEWING_CONDITIONS_CIECAM02', - ], - [ - 'colour.appearance.CIECAM02_Specification', - 'colour.appearance.CAM_Specification_CIECAM02', - ], - [ - 'colour.appearance.Hunt_InductionFactors', - 'colour.appearance.InductionFactors_Hunt', - ], - [ - 'colour.appearance.HUNT_VIEWING_CONDITIONS', - 'colour.appearance.VIEWING_CONDITIONS_HUNT', - ], - [ - 'colour.appearance.Hunt_Specification', - 'colour.appearance.CAM_Specification_Hunt', - ], - [ - 'colour.appearance.LLAB_InductionFactors', - 'colour.appearance.InductionFactors_LLAB', - ], - [ - 'colour.appearance.LLAB_VIEWING_CONDITIONS', - 'colour.appearance.VIEWING_CONDITIONS_LLAB', - ], - [ - 'colour.appearance.LLAB_Specification', - 'colour.appearance.CAM_Specification_LLAB', - ], - [ - 'colour.appearance.Nayatani95_Specification', - 'colour.appearance.CAM_Specification_Nayatani95', - ], - [ - 'colour.appearance.RLAB_VIEWING_CONDITIONS', - 'colour.appearance.VIEWING_CONDITIONS_RLAB', - ], - [ - 'colour.appearance.RLAB_D_FACTOR', - 'colour.appearance.D_FACTOR_RLAB', - ], - [ - 'colour.appearance.RLAB_Specification', - 'colour.appearance.CAM_Specification_RLAB', - ], - ] -} -""" -Defines *colour.appearance* sub-package API changes. - -API_CHANGES : dict -""" - -if not is_documentation_building(): - sys.modules['colour.appearance'] = appearance( - sys.modules['colour.appearance'], build_API_changes(API_CHANGES)) - - del ModuleAPI, is_documentation_building, build_API_changes, sys diff --git a/colour/appearance/atd95.py b/colour/appearance/atd95.py index 83bffa833e..5b9fe6b24a 100644 --- a/colour/appearance/atd95.py +++ b/colour/appearance/atd95.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ ATD (1995) Colour Vision Model ============================== -Defines *ATD (1995)* colour vision model objects: +Defines the *ATD (1995)* colour vision model objects: - :class:`colour.CAM_Specification_ATD95` - :func:`colour.XYZ_to_ATD95` @@ -25,62 +24,78 @@ doi:10.1117/12.206546 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from collections import namedtuple - -from colour.algebra import spow -from colour.utilities import (as_float_array, vector_dot, from_range_degrees, - to_domain_100, tsplit, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from dataclasses import dataclass, field + +from colour.algebra import spow, vector_dot +from colour.hints import ( + ArrayLike, + FloatingOrArrayLike, + FloatingOrNDArray, + NDArray, + Optional, +) +from colour.utilities import ( + MixinDataclassArithmetic, + as_float, + as_float_array, + from_range_degrees, + to_domain_100, + tsplit, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'CAM_ReferenceSpecification_ATD95', 'CAM_Specification_ATD95', - 'XYZ_to_ATD95', 'luminance_to_retinal_illuminance', 'XYZ_to_LMS_ATD95', - 'opponent_colour_dimensions', 'final_response' + "CAM_ReferenceSpecification_ATD95", + "CAM_Specification_ATD95", + "XYZ_to_ATD95", + "luminance_to_retinal_illuminance", + "XYZ_to_LMS_ATD95", + "opponent_colour_dimensions", + "final_response", ] -class CAM_ReferenceSpecification_ATD95( - namedtuple( - 'CAM_ReferenceSpecification_ATD95', - ('H', 'C', 'Br', 'A_1', 'T_1', 'D_1', 'A_2', 'T_2', 'D_2'))): +@dataclass +class CAM_ReferenceSpecification_ATD95(MixinDataclassArithmetic): """ - Defines the *ATD (1995)* colour vision model reference specification. + Define the *ATD (1995)* colour vision model reference specification. This specification has field names consistent with *Fairchild (2013)* reference. Parameters ---------- - H : numeric or array_like + H *Hue* angle :math:`H` in degrees. - C : numeric or array_like + C Correlate of *saturation* :math:`C`. *Guth (1995)* incorrectly uses the terms saturation and chroma interchangeably. However, :math:`C` is here a measure of saturation rather than chroma since it is measured relative to the achromatic response for the stimulus rather than that of a similarly illuminated white. - Br : numeric or array_like + Br Correlate of *brightness* :math:`Br`. - A_1 : numeric or array_like + A_1 First stage :math:`A_1` response. - T_1 : numeric or array_like + T_1 First stage :math:`T_1` response. - D_1 : numeric or array_like + D_1 First stage :math:`D_1` response. - A_2 : numeric or array_like + A_2 Second stage :math:`A_2` response. - T_2 : numeric or array_like + T_2 Second stage :math:`A_2` response. - D_2 : numeric or array_like + D_2 Second stage :math:`D_2` response. References @@ -88,12 +103,21 @@ class CAM_ReferenceSpecification_ATD95( :cite:`Fairchild2013v`, :cite:`Guth1995a` """ + H: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + C: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + Br: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + A_1: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + T_1: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + D_1: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + A_2: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + T_2: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + D_2: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) -class CAM_Specification_ATD95( - namedtuple('CAM_Specification_ATD95', - ('h', 'C', 'Q', 'A_1', 'T_1', 'D_1', 'A_2', 'T_2', 'D_2'))): + +@dataclass +class CAM_Specification_ATD95(MixinDataclassArithmetic): """ - Defines the *ATD (1995)* colour vision model specification. + Define the *ATD (1995)* colour vision model specification. This specification has field names consistent with the remaining colour appearance models in :mod:`colour.appearance` but diverge from @@ -101,27 +125,27 @@ class CAM_Specification_ATD95( Parameters ---------- - h : numeric or array_like + h *Hue* angle :math:`H` in degrees. - C : numeric or array_like + C Correlate of *saturation* :math:`C`. *Guth (1995)* incorrectly uses the terms saturation and chroma interchangeably. However, :math:`C` is here a measure of saturation rather than chroma since it is measured relative to the achromatic response for the stimulus rather than that of a similarly illuminated white. - Q : numeric or array_like + Q Correlate of *brightness* :math:`Br`. - A_1 : numeric or array_like + A_1 First stage :math:`A_1` response. - T_1 : numeric or array_like + T_1 First stage :math:`T_1` response. - D_1 : numeric or array_like + D_1 First stage :math:`D_1` response. - A_2 : numeric or array_like + A_2 Second stage :math:`A_2` response. - T_2 : numeric or array_like + T_2 Second stage :math:`A_2` response. - D_2 : numeric or array_like + D_2 Second stage :math:`D_2` response. Notes @@ -133,40 +157,57 @@ class CAM_Specification_ATD95( :cite:`Fairchild2013v`, :cite:`Guth1995a` """ - -def XYZ_to_ATD95(XYZ, XYZ_0, Y_0, k_1, k_2, sigma=300): + h: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + C: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + Q: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + A_1: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + T_1: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + D_1: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + A_2: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + T_2: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + D_2: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + + +def XYZ_to_ATD95( + XYZ: ArrayLike, + XYZ_0: ArrayLike, + Y_0: FloatingOrArrayLike, + k_1: FloatingOrArrayLike, + k_2: FloatingOrArrayLike, + sigma: FloatingOrArrayLike = 300, +) -> CAM_Specification_ATD95: """ - Computes the *ATD (1995)* colour vision model correlates. + Compute the *ATD (1995)* colour vision model correlates. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values of test sample / stimulus. - XYZ_0 : array_like + XYZ_0 *CIE XYZ* tristimulus values of reference white. - Y_0 : numeric or array_like + Y_0 Absolute adapting field luminance in :math:`cd/m^2`. - k_1 : numeric or array_like + k_1 Application specific weight :math:`k_1`. - k_2 : numeric or array_like + k_2 Application specific weight :math:`k_2`. - sigma : numeric or array_like, optional + sigma Constant :math:`\\sigma` varied to predict different types of data. Returns ------- - CAM_Specification_ATD95 + :class:`colour.CAM_Specification_ATD95` *ATD (1995)* colour vision model specification. Notes ----- - +---------------------------+-----------------------+---------------+ - | **Domain** | **Scale - Reference** | **Scale - 1** | - +===========================+=======================+===============+ - | ``XYZ`` | [0, 100] | [0, 1] | - +---------------------------+-----------------------+---------------+ - | ``XYZ_0`` | [0, 100] | [0, 1] | - +---------------------------+-----------------------+---------------+ + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``XYZ`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ + | ``XYZ_0`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ +-------------------------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | @@ -217,34 +258,45 @@ def XYZ_to_ATD95(XYZ, XYZ_0, Y_0, k_1, k_2, sigma=300): A_1, T_1, D_1, A_2, T_2, D_2 = tsplit(opponent_colour_dimensions(LMS_g)) # Computing the correlate of *brightness* :math:`Br`. - Br = spow(A_1 ** 2 + T_1 ** 2 + D_1 ** 2, 0.5) + Br = spow(A_1**2 + T_1**2 + D_1**2, 0.5) # Computing the correlate of *saturation* :math:`C`. - C = spow(T_2 ** 2 + D_2 ** 2, 0.5) / A_2 + C = spow(T_2**2 + D_2**2, 0.5) / A_2 # Computing the *hue* :math:`H`. Note that the reference does not take the # modulus of the :math:`H`, thus :math:`H` can exceed 360 degrees. H = T_2 / D_2 return CAM_Specification_ATD95( - from_range_degrees(H), C, Br, A_1, T_1, D_1, A_2, T_2, D_2) - - -def luminance_to_retinal_illuminance(XYZ, Y_c): + as_float(from_range_degrees(H)), + C, + Br, + A_1, + T_1, + D_1, + A_2, + T_2, + D_2, + ) + + +def luminance_to_retinal_illuminance( + XYZ: ArrayLike, Y_c: FloatingOrArrayLike +) -> NDArray: """ - Converts from luminance in :math:`cd/m^2` to retinal illuminance in + Convert from luminance in :math:`cd/m^2` to retinal illuminance in trolands. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. - Y_c : numeric or array_like + Y_c Absolute adapting field luminance in :math:`cd/m^2`. Returns ------- - ndarray + :class:`numpy.ndarray` Converted *CIE XYZ* tristimulus values in trolands. Examples @@ -258,21 +310,21 @@ def luminance_to_retinal_illuminance(XYZ, Y_c): XYZ = as_float_array(XYZ) Y_c = as_float_array(Y_c) - return 18 * spow(Y_c[..., np.newaxis] * XYZ / 100, 0.8) + return as_float_array(18 * spow(Y_c[..., np.newaxis] * XYZ / 100, 0.8)) -def XYZ_to_LMS_ATD95(XYZ): +def XYZ_to_LMS_ATD95(XYZ: ArrayLike) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to *LMS* cone responses. + Convert from *CIE XYZ* tristimulus values to *LMS* cone responses. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. Returns ------- - ndarray + :class:`numpy.ndarray` *LMS* cone responses. Examples @@ -282,31 +334,34 @@ def XYZ_to_LMS_ATD95(XYZ): array([ 6.2283272..., 7.4780666..., 3.8859772...]) """ - LMS = vector_dot([ - [0.2435, 0.8524, -0.0516], - [-0.3954, 1.1642, 0.0837], - [0.0000, 0.0400, 0.6225], - ], XYZ) - + LMS = vector_dot( + [ + [0.2435, 0.8524, -0.0516], + [-0.3954, 1.1642, 0.0837], + [0.0000, 0.0400, 0.6225], + ], + XYZ, + ) LMS *= np.array([0.66, 1.0, 0.43]) - LMS = spow(LMS, 0.7) - LMS += np.array([0.024, 0.036, 0.31]) - return LMS + LMS_p = spow(LMS, 0.7) + LMS_p += np.array([0.024, 0.036, 0.31]) + + return as_float_array(LMS_p) -def opponent_colour_dimensions(LMS_g): +def opponent_colour_dimensions(LMS_g: ArrayLike) -> NDArray: """ - Returns opponent colour dimensions from given post adaptation cone signals. + Return opponent colour dimensions from given post adaptation cone signals. Parameters ---------- - LMS_g : array_like + LMS_g Post adaptation cone signals. Returns ------- - ndarray + :class:`numpy.ndarray` Opponent colour dimensions. Examples @@ -335,18 +390,18 @@ def opponent_colour_dimensions(LMS_g): return tstack([A_1, T_1, D_1, A_2, T_2, D_2]) -def final_response(value): +def final_response(value: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the final response of given opponent colour dimension. + Return the final response of given opponent colour dimension. Parameters ---------- - value : numeric or array_like + value Opponent colour dimension. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Final response of opponent colour dimension. Examples @@ -357,4 +412,4 @@ def final_response(value): value = as_float_array(value) - return value / (200 + np.abs(value)) + return as_float(value / (200 + np.abs(value))) diff --git a/colour/appearance/cam16.py b/colour/appearance/cam16.py index c4408b059a..3006225fea 100644 --- a/colour/appearance/cam16.py +++ b/colour/appearance/cam16.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ CAM16 Colour Appearance Model ============================= -Defines *CAM16* colour appearance model objects: +Defines the *CAM16* colour appearance model objects: - :class:`colour.appearance.InductionFactors_CAM16` - :attr:`colour.VIEWING_CONDITIONS_CAM16` @@ -19,116 +18,144 @@ 42(6), 703-718. doi:10.1002/col.22131 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from collections import namedtuple +from dataclasses import astuple, dataclass, field -from colour.algebra import spow +from colour.algebra import spow, vector_dot +from colour.adaptation import CAT_CAT16 from colour.appearance.ciecam02 import ( - VIEWING_CONDITIONS_CIECAM02, P, achromatic_response_forward, - achromatic_response_inverse, brightness_correlate, chroma_correlate, - colourfulness_correlate, degree_of_adaptation, eccentricity_factor, - hue_angle, hue_quadrature, lightness_correlate, - opponent_colour_dimensions_forward, opponent_colour_dimensions_inverse, + InductionFactors_CIECAM02, + VIEWING_CONDITIONS_CIECAM02, + P, + achromatic_response_forward, + achromatic_response_inverse, + brightness_correlate, + chroma_correlate, + colourfulness_correlate, + degree_of_adaptation, + eccentricity_factor, + hue_angle, + hue_quadrature, + lightness_correlate, + opponent_colour_dimensions_forward, + opponent_colour_dimensions_inverse, post_adaptation_non_linear_response_compression_forward, post_adaptation_non_linear_response_compression_inverse, matrix_post_adaptation_non_linear_response_compression, - saturation_correlate, temporary_magnitude_quantity_inverse, - viewing_condition_dependent_parameters) -from colour.utilities import (CaseInsensitiveMapping, as_float_array, - as_namedtuple, vector_dot, from_range_100, - from_range_degrees, ones, to_domain_100, - to_domain_degrees, tsplit) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + saturation_correlate, + temporary_magnitude_quantity_inverse, + viewing_condition_dependent_parameters, +) +from colour.hints import ( + ArrayLike, + Boolean, + FloatingOrArrayLike, + FloatingOrNDArray, + NDArray, + Optional, + Union, +) +from colour.utilities import ( + CaseInsensitiveMapping, + MixinDataclassArithmetic, + as_float, + as_float_array, + from_range_100, + from_range_degrees, + has_only_nan, + ones, + to_domain_100, + to_domain_degrees, + tsplit, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'MATRIX_16', 'MATRIX_INVERSE_16', 'InductionFactors_CAM16', - 'VIEWING_CONDITIONS_CAM16', 'CAM_Specification_CAM16', 'XYZ_to_CAM16', - 'CAM16_to_XYZ' + "MATRIX_16", + "MATRIX_INVERSE_16", + "InductionFactors_CAM16", + "VIEWING_CONDITIONS_CAM16", + "CAM_Specification_CAM16", + "XYZ_to_CAM16", + "CAM16_to_XYZ", ] -MATRIX_16 = np.array([ - [0.401288, 0.650173, -0.051461], - [-0.250268, 1.204414, 0.045854], - [-0.002079, 0.048952, 0.953127], -]) -""" -Adaptation matrix :math:`M_{16}`. - -MATRIX_16 : array_like, (3, 3) -""" +MATRIX_16: NDArray = CAT_CAT16 +"""Adaptation matrix :math:`M_{16}`.""" -MATRIX_INVERSE_16 = np.linalg.inv(MATRIX_16) -""" -Inverse adaptation matrix :math:`M^{-1}_{16}`. - -MATRIX_INVERSE_16 : array_like, (3, 3) -""" +MATRIX_INVERSE_16: NDArray = np.linalg.inv(MATRIX_16) +"""Inverse adaptation matrix :math:`M^{-1}_{16}`.""" class InductionFactors_CAM16( - namedtuple('InductionFactors_CAM16', ('F', 'c', 'N_c'))): + namedtuple("InductionFactors_CAM16", ("F", "c", "N_c")) +): """ *CAM16* colour appearance model induction factors. Parameters ---------- - F : numeric or array_like + F Maximum degree of adaptation :math:`F`. - c : numeric or array_like - Exponential non linearity :math:`c`. - N_c : numeric or array_like + c + Exponential non-linearity :math:`c`. + N_c Chromatic induction factor :math:`N_c`. + Notes + ----- + - The *CAM16* colour appearance model induction factors are the same as + *CIECAM02* colour appearance model. + References ---------- :cite:`Li2017` """ -VIEWING_CONDITIONS_CAM16 = CaseInsensitiveMapping(VIEWING_CONDITIONS_CIECAM02) +VIEWING_CONDITIONS_CAM16: CaseInsensitiveMapping = CaseInsensitiveMapping( + VIEWING_CONDITIONS_CIECAM02 +) VIEWING_CONDITIONS_CAM16.__doc__ = """ Reference *CAM16* colour appearance model viewing conditions. References ---------- :cite:`Li2017` - -VIEWING_CONDITIONS_CAM16 : CaseInsensitiveMapping - **{'Average', 'Dim', 'Dark'}** """ -class CAM_Specification_CAM16( - namedtuple('CAM_Specification_CAM16', - ('J', 'C', 'h', 's', 'Q', 'M', 'H', 'HC'))): +@dataclass +class CAM_Specification_CAM16(MixinDataclassArithmetic): """ - Defines the *CAM16* colour appearance model specification. + Define the *CAM16* colour appearance model specification. Parameters ---------- - J : numeric or array_like + J Correlate of *Lightness* :math:`J`. - C : numeric or array_like + C Correlate of *chroma* :math:`C`. - h : numeric or array_like + h *Hue* angle :math:`h` in degrees. - s : numeric or array_like + s Correlate of *saturation* :math:`s`. - Q : numeric or array_like + Q Correlate of *brightness* :math:`Q`. - M : numeric or array_like + M Correlate of *colourfulness* :math:`M`. - H : numeric or array_like + H *Hue* :math:`h` quadrature :math:`H`. - HC : numeric or array_like + HC *Hue* :math:`h` composition :math:`H^C`. References @@ -136,72 +163,65 @@ class CAM_Specification_CAM16( :cite:`Li2017` """ - def __new__(cls, - J=None, - C=None, - h=None, - s=None, - Q=None, - M=None, - H=None, - HC=None): - """ - Returns a new instance of the :class:`colour.CAM_Specification_CAM16` - class. - """ - - return super(CAM_Specification_CAM16, cls).__new__( - cls, J, C, h, s, Q, M, H, HC) - - -def XYZ_to_CAM16(XYZ, - XYZ_w, - L_A, - Y_b, - surround=VIEWING_CONDITIONS_CAM16['Average'], - discount_illuminant=False): + J: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + C: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + h: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + s: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + Q: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + M: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + H: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + HC: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + + +def XYZ_to_CAM16( + XYZ: ArrayLike, + XYZ_w: ArrayLike, + L_A: FloatingOrArrayLike, + Y_b: FloatingOrArrayLike, + surround: Union[ + InductionFactors_CIECAM02, InductionFactors_CAM16 + ] = VIEWING_CONDITIONS_CAM16["Average"], + discount_illuminant: Boolean = False, +) -> CAM_Specification_CAM16: """ - Computes the *CAM16* colour appearance model correlates from given + Compute the *CAM16* colour appearance model correlates from given *CIE XYZ* tristimulus values. - This is the *forward* implementation. - Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values of test sample / stimulus. - XYZ_w : array_like + XYZ_w *CIE XYZ* tristimulus values of reference white. - L_A : numeric or array_like + L_A Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`, (often taken to be 20% of the luminance of a white object in the scene). - Y_b : numeric or array_like + Y_b Luminous factor of background :math:`Y_b` such as :math:`Y_b = 100 x L_b / L_w` where :math:`L_w` is the luminance of the light source and :math:`L_b` is the luminance of the background. For viewing images, :math:`Y_b` can be the average :math:`Y` value for the pixels in the entire image, or frequently, a :math:`Y` value of 20, approximate an :math:`L^*` of 50 is used. - surround : InductionFactors_CAM16, optional + surround Surround viewing conditions induction factors. - discount_illuminant : bool, optional + discount_illuminant Truth value indicating if the illuminant should be discounted. Returns ------- - CAM_Specification_CAM16 + :class:`colour.CAM_Specification_CAM16` *CAM16* colour appearance model specification. Notes ----- - - +---------------------------+-----------------------+---------------+ - | **Domain** | **Scale - Reference** | **Scale - 1** | - +===========================+=======================+===============+ - | ``XYZ`` | [0, 100] | [0, 1] | - +---------------------------+-----------------------+---------------+ - | ``XYZ_w`` | [0, 100] | [0, 1] | - +---------------------------+-----------------------+---------------+ + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``XYZ`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ + | ``XYZ_w`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ +-------------------------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | @@ -249,19 +269,27 @@ def XYZ_to_CAM16(XYZ, RGB_w = vector_dot(MATRIX_16, XYZ_w) # Computing degree of adaptation :math:`D`. - D = (np.clip(degree_of_adaptation(surround.F, L_A), 0, 1) - if not discount_illuminant else ones(L_A.shape)) - - n, F_L, N_bb, N_cb, z = tsplit( - viewing_condition_dependent_parameters(Y_b, Y_w, L_A)) - - D_RGB = (D[..., np.newaxis] * Y_w[..., np.newaxis] / RGB_w + 1 - - D[..., np.newaxis]) + D = ( + np.clip(degree_of_adaptation(surround.F, L_A), 0, 1) + if not discount_illuminant + else ones(L_A.shape) + ) + + n, F_L, N_bb, N_cb, z = viewing_condition_dependent_parameters( + Y_b, Y_w, L_A + ) + + D_RGB = ( + D[..., np.newaxis] * Y_w[..., np.newaxis] / RGB_w + + 1 + - D[..., np.newaxis] + ) RGB_wc = D_RGB * RGB_w - # Applying forward post-adaptation non linear response compression. + # Applying forward post-adaptation non-linear response compression. RGB_aw = post_adaptation_non_linear_response_compression_forward( - RGB_wc, F_L) + RGB_wc, F_L + ) # Computing achromatic responses for the whitepoint. A_w = achromatic_response_forward(RGB_aw, N_bb) @@ -274,7 +302,7 @@ def XYZ_to_CAM16(XYZ, RGB_c = D_RGB * RGB # Step 3 - # Applying forward post-adaptation non linear response compression. + # Applying forward post-adaptation non-linear response compression. RGB_a = post_adaptation_non_linear_response_compression_forward(RGB_c, F_L) # Step 4 @@ -315,21 +343,29 @@ def XYZ_to_CAM16(XYZ, s = saturation_correlate(M, Q) return CAM_Specification_CAM16( - from_range_100(J), from_range_100(C), from_range_degrees(h), - from_range_100(s), from_range_100(Q), from_range_100(M), - from_range_degrees(H, 400), None) - - -def CAM16_to_XYZ(specification, - XYZ_w, - L_A, - Y_b, - surround=VIEWING_CONDITIONS_CAM16['Average'], - discount_illuminant=False): + as_float(from_range_100(J)), + as_float(from_range_100(C)), + as_float(from_range_degrees(h)), + as_float(from_range_100(s)), + as_float(from_range_100(Q)), + as_float(from_range_100(M)), + as_float(from_range_degrees(H, 400)), + None, + ) + + +def CAM16_to_XYZ( + specification: CAM_Specification_CAM16, + XYZ_w: ArrayLike, + L_A: FloatingOrArrayLike, + Y_b: FloatingOrArrayLike, + surround: Union[ + InductionFactors_CIECAM02, InductionFactors_CAM16 + ] = VIEWING_CONDITIONS_CAM16["Average"], + discount_illuminant: Boolean = False, +) -> NDArray: """ - Converts from *CAM16* specification to *CIE XYZ* tristimulus values. - - This is the *inverse* implementation. + Convert from *CAM16* specification to *CIE XYZ* tristimulus values. Parameters ---------- @@ -338,26 +374,26 @@ def CAM16_to_XYZ(specification, *Lightness* :math:`J`, correlate of *chroma* :math:`C` or correlate of *colourfulness* :math:`M` and *hue* angle :math:`h` in degrees must be specified, e.g. :math:`JCh` or :math:`JMh`. - XYZ_w : array_like + XYZ_w *CIE XYZ* tristimulus values of reference white. - L_A : numeric or array_like + L_A Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`, (often taken to be 20% of the luminance of a white object in the scene). - Y_b : numeric or array_like + Y_b Luminous factor of background :math:`Y_b` such as :math:`Y_b = 100 x L_b / L_w` where :math:`L_w` is the luminance of the light source and :math:`L_b` is the luminance of the background. For viewing images, :math:`Y_b` can be the average :math:`Y` value for the pixels in the entire image, or frequently, a :math:`Y` value of 20, approximate an :math:`L^*` of 50 is used. - surround : InductionFactors_CAM16, optional + surround Surround viewing conditions. - discount_illuminant : bool, optional + discount_illuminant Discount the illuminant. Returns ------- - XYZ : ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Raises @@ -368,7 +404,6 @@ def CAM16_to_XYZ(specification, Notes ----- - +-------------------------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +===============================+=======================+===============+ @@ -389,14 +424,11 @@ def CAM16_to_XYZ(specification, | ``XYZ_w`` | [0, 100] | [0, 1] | +-------------------------------+-----------------------+---------------+ - +---------------------------+-----------------------+---------------+ - | **Range** | **Scale - Reference** | **Scale - 1** | - +===========================+=======================+===============+ - | ``XYZ`` | [0, 100] | [0, 1] | - +---------------------------+-----------------------+---------------+ - - - ``CAM_Specification_CAM16`` can also be passed as a compatible argument - to :func:`colour.utilities.as_namedtuple` definition. + +-----------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +===========+=======================+===============+ + | ``XYZ`` | [0, 100] | [0, 1] | + +-----------+-----------------------+---------------+ References ---------- @@ -405,8 +437,8 @@ def CAM16_to_XYZ(specification, Examples -------- >>> specification = CAM_Specification_CAM16(J=41.731207905126638, - ... C=0.103355738709070, - ... h=217.067959767393010) + ... C=0.103355738709070, + ... h=217.067959767393010) >>> XYZ_w = np.array([95.05, 100.00, 108.88]) >>> L_A = 318.31 >>> Y_b = 20.0 @@ -414,12 +446,12 @@ def CAM16_to_XYZ(specification, array([ 19.01..., 20... , 21.78...]) """ - J, C, h, _s, _Q, M, _H, _HC = as_namedtuple(specification, - CAM_Specification_CAM16) + J, C, h, _s, _Q, M, _H, _HC = astuple(specification) + J = to_domain_100(J) - C = to_domain_100(C) if C is not None else C + C = to_domain_100(C) h = to_domain_degrees(h) - M = to_domain_100(M) if M is not None else M + M = to_domain_100(M) L_A = as_float_array(L_A) XYZ_w = to_domain_100(XYZ_w) _X_w, Y_w, _Z_w = tsplit(XYZ_w) @@ -429,29 +461,39 @@ def CAM16_to_XYZ(specification, RGB_w = vector_dot(MATRIX_16, XYZ_w) # Computing degree of adaptation :math:`D`. - D = (np.clip(degree_of_adaptation(surround.F, L_A), 0, 1) - if not discount_illuminant else ones(L_A.shape)) - - n, F_L, N_bb, N_cb, z = tsplit( - viewing_condition_dependent_parameters(Y_b, Y_w, L_A)) - - D_RGB = (D[..., np.newaxis] * Y_w[..., np.newaxis] / RGB_w + 1 - - D[..., np.newaxis]) + D = ( + np.clip(degree_of_adaptation(surround.F, L_A), 0, 1) + if not discount_illuminant + else ones(L_A.shape) + ) + + n, F_L, N_bb, N_cb, z = viewing_condition_dependent_parameters( + Y_b, Y_w, L_A + ) + + D_RGB = ( + D[..., np.newaxis] * Y_w[..., np.newaxis] / RGB_w + + 1 + - D[..., np.newaxis] + ) RGB_wc = D_RGB * RGB_w - # Applying forward post-adaptation non linear response compression. + # Applying forward post-adaptation non-linear response compression. RGB_aw = post_adaptation_non_linear_response_compression_forward( - RGB_wc, F_L) + RGB_wc, F_L + ) # Computing achromatic responses for the whitepoint. A_w = achromatic_response_forward(RGB_aw, N_bb) # Step 1 - if C is None and M is not None: + if has_only_nan(C) and not has_only_nan(M): C = M / spow(F_L, 0.25) - elif C is None: - raise ValueError('Either "C" or "M" correlate must be defined in ' - 'the "CAM_Specification_CAM16" argument!') + elif has_only_nan(C): + raise ValueError( + 'Either "C" or "M" correlate must be defined in ' + 'the "CAM_Specification_CAM16" argument!' + ) # Step 2 # Computing temporary magnitude quantity :math:`t`. @@ -472,11 +514,11 @@ def CAM16_to_XYZ(specification, a, b = tsplit(opponent_colour_dimensions_inverse(P_n, h)) # Step 4 - # Computing post-adaptation non linear response compression matrix. + # Applying post-adaptation non-linear response compression matrix. RGB_a = matrix_post_adaptation_non_linear_response_compression(P_2, a, b) # Step 5 - # Applying inverse post-adaptation non linear response compression. + # Applying inverse post-adaptation non-linear response compression. RGB_c = post_adaptation_non_linear_response_compression_inverse(RGB_a, F_L) # Step 6 diff --git a/colour/appearance/ciecam02.py b/colour/appearance/ciecam02.py index 1625eaa1aa..1e025c6535 100644 --- a/colour/appearance/ciecam02.py +++ b/colour/appearance/ciecam02.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ CIECAM02 Colour Appearance Model ================================ -Defines *CIECAM02* colour appearance model objects: +Defines the *CIECAM02* colour appearance model objects: - :class:`colour.appearance.InductionFactors_CIECAM02` - :attr:`colour.VIEWING_CONDITIONS_CIECAM02` @@ -15,6 +14,11 @@ ---------- - :cite:`Fairchild2004c` : Fairchild, M. D. (2004). CIECAM02. In Color Appearance Models (2nd ed., pp. 289-301). Wiley. ISBN:978-0-470-01216-1 +- :cite:`InternationalElectrotechnicalCommission1999a` : International + Electrotechnical Commission. (1999). IEC 61966-2-1:1999 - Multimedia + systems and equipment - Colour measurement and management - Part 2-1: + Colour management - Default RGB colour space - sRGB (p. 51). + https://webstore.iec.ch/publication/6169 - :cite:`Luo2013` : Luo, Ming Ronnier, & Li, C. (2013). CIECAM02 and Its Recent Developments. In C. Fernandez-Maloigne (Ed.), Advanced Color Image Processing and Analysis (pp. 19-58). Springer New York. @@ -26,68 +30,114 @@ Appearance Models (2nd ed., pp. 289-301). Wiley. ISBN:978-0-470-01216-1 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from collections import namedtuple +from dataclasses import astuple, dataclass, field -from colour.algebra import spow +from colour.algebra import matrix_dot, spow, vector_dot from colour.adaptation import CAT_CAT02 -from colour.appearance.hunt import (MATRIX_HPE_TO_XYZ, MATRIX_XYZ_TO_HPE, - luminance_level_adaptation_factor) +from colour.appearance.hunt import ( + MATRIX_HPE_TO_XYZ, + MATRIX_XYZ_TO_HPE, + luminance_level_adaptation_factor, +) +from colour.colorimetry import CCS_ILLUMINANTS from colour.constants import EPSILON +from colour.hints import ( + ArrayLike, + Boolean, + Dict, + FloatingOrArrayLike, + FloatingOrNDArray, + NDArray, + Optional, + Tuple, +) +from colour.models import xy_to_XYZ from colour.utilities import ( - CaseInsensitiveMapping, as_float_array, as_int_array, as_namedtuple, - as_float, from_range_degrees, matrix_dot, vector_dot, from_range_100, ones, - to_domain_100, to_domain_degrees, tsplit, tstack, zeros) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + CaseInsensitiveMapping, + MixinDataclassArithmetic, + as_float, + as_float_array, + as_int_array, + from_range_degrees, + from_range_100, + has_only_nan, + ones, + to_domain_100, + to_domain_degrees, + tsplit, + tstack, + zeros, +) +from colour.utilities.documentation import ( + DocstringDict, + is_documentation_building, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'CAT02_INVERSE_CAT', 'InductionFactors_CIECAM02', - 'VIEWING_CONDITIONS_CIECAM02', 'HUE_DATA_FOR_HUE_QUADRATURE', - 'CAM_Specification_CIECAM02', 'XYZ_to_CIECAM02', 'CIECAM02_to_XYZ', - 'chromatic_induction_factors', 'base_exponential_non_linearity', - 'viewing_condition_dependent_parameters', 'degree_of_adaptation', - 'full_chromatic_adaptation_forward', 'full_chromatic_adaptation_inverse', - 'RGB_to_rgb', 'rgb_to_RGB', - 'post_adaptation_non_linear_response_compression_forward', - 'post_adaptation_non_linear_response_compression_inverse', - 'opponent_colour_dimensions_forward', 'opponent_colour_dimensions_inverse', - 'hue_angle', 'hue_quadrature', 'eccentricity_factor', - 'achromatic_response_forward', 'achromatic_response_inverse', - 'lightness_correlate', 'brightness_correlate', - 'temporary_magnitude_quantity_forward', - 'temporary_magnitude_quantity_inverse', 'chroma_correlate', - 'colourfulness_correlate', 'saturation_correlate', 'P', - 'matrix_post_adaptation_non_linear_response_compression' + "CAT_INVERSE_CAT02", + "InductionFactors_CIECAM02", + "VIEWING_CONDITIONS_CIECAM02", + "HUE_DATA_FOR_HUE_QUADRATURE", + "CAM_KWARGS_CIECAM02_sRGB", + "CAM_Specification_CIECAM02", + "XYZ_to_CIECAM02", + "CIECAM02_to_XYZ", + "chromatic_induction_factors", + "base_exponential_non_linearity", + "viewing_condition_dependent_parameters", + "degree_of_adaptation", + "full_chromatic_adaptation_forward", + "full_chromatic_adaptation_inverse", + "RGB_to_rgb", + "rgb_to_RGB", + "post_adaptation_non_linear_response_compression_forward", + "post_adaptation_non_linear_response_compression_inverse", + "opponent_colour_dimensions_forward", + "opponent_colour_dimensions_inverse", + "hue_angle", + "hue_quadrature", + "eccentricity_factor", + "achromatic_response_forward", + "achromatic_response_inverse", + "lightness_correlate", + "brightness_correlate", + "temporary_magnitude_quantity_forward", + "temporary_magnitude_quantity_inverse", + "chroma_correlate", + "colourfulness_correlate", + "saturation_correlate", + "P", + "matrix_post_adaptation_non_linear_response_compression", ] -CAT02_INVERSE_CAT = np.linalg.inv(CAT_CAT02) -""" -Inverse CAT02 chromatic adaptation transform. - -CAT02_INVERSE_CAT : array_like, (3, 3) -""" +CAT_INVERSE_CAT02: NDArray = np.linalg.inv(CAT_CAT02) +"""Inverse CAT02 chromatic adaptation transform.""" class InductionFactors_CIECAM02( - namedtuple('InductionFactors_CIECAM02', ('F', 'c', 'N_c'))): + namedtuple("InductionFactors_CIECAM02", ("F", "c", "N_c")) +): """ *CIECAM02* colour appearance model induction factors. Parameters ---------- - F : numeric or array_like + F Maximum degree of adaptation :math:`F`. - c : numeric or array_like - Exponential non linearity :math:`c`. - N_c : numeric or array_like + c + Exponential non-linearity :math:`c`. + N_c Chromatic induction factor :math:`N_c`. References @@ -97,11 +147,13 @@ class InductionFactors_CIECAM02( """ -VIEWING_CONDITIONS_CIECAM02 = CaseInsensitiveMapping({ - 'Average': InductionFactors_CIECAM02(1, 0.69, 1), - 'Dim': InductionFactors_CIECAM02(0.9, 0.59, 0.9), - 'Dark': InductionFactors_CIECAM02(0.8, 0.525, 0.8) -}) +VIEWING_CONDITIONS_CIECAM02: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Average": InductionFactors_CIECAM02(1, 0.69, 1), + "Dim": InductionFactors_CIECAM02(0.9, 0.59, 0.9), + "Dark": InductionFactors_CIECAM02(0.8, 0.525, 0.8), + } +) VIEWING_CONDITIONS_CIECAM02.__doc__ = """ Reference *CIECAM02* colour appearance model viewing conditions. @@ -109,41 +161,58 @@ class InductionFactors_CIECAM02( ---------- :cite:`Fairchild2004c`, :cite:`Luo2013`, :cite:`Moroneya`, :cite:`Wikipedia2007a` - -VIEWING_CONDITIONS_CIECAM02 : CaseInsensitiveMapping - **{'Average', 'Dim', 'Dark'}** """ -HUE_DATA_FOR_HUE_QUADRATURE = { - 'h_i': np.array([20.14, 90.00, 164.25, 237.53, 380.14]), - 'e_i': np.array([0.8, 0.7, 1.0, 1.2, 0.8]), - 'H_i': np.array([0.0, 100.0, 200.0, 300.0, 400.0]) +HUE_DATA_FOR_HUE_QUADRATURE: Dict = { + "h_i": np.array([20.14, 90.00, 164.25, 237.53, 380.14]), + "e_i": np.array([0.8, 0.7, 1.0, 1.2, 0.8]), + "H_i": np.array([0.0, 100.0, 200.0, 300.0, 400.0]), } +CAM_KWARGS_CIECAM02_sRGB: Dict = { + "XYZ_w": xy_to_XYZ( + CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"] + ) + * 100, + "L_A": 64 / np.pi * 0.2, + "Y_b": 20, + "surround": VIEWING_CONDITIONS_CIECAM02["Average"], +} +if is_documentation_building(): # pragma: no cover + CAM_KWARGS_CIECAM02_sRGB = DocstringDict(CAM_KWARGS_CIECAM02_sRGB) + CAM_KWARGS_CIECAM02_sRGB.__doc__ = """ +Default parameter values for the *CIECAM02* colour appearance model usage in +the context of *sRGB*. + +References +---------- +:cite:`Fairchild2004c`, :cite:`InternationalElectrotechnicalCommission1999a`, +:cite:`Luo2013`, :cite:`Moroneya`, :cite:`Wikipedia2007a` +""" + -class CAM_Specification_CIECAM02( - namedtuple('CAM_Specification_CIECAM02', - ('J', 'C', 'h', 's', 'Q', 'M', 'H', 'HC'))): +@dataclass +class CAM_Specification_CIECAM02(MixinDataclassArithmetic): """ - Defines the *CIECAM02* colour appearance model specification. + Define the *CIECAM02* colour appearance model specification. Parameters ---------- - J : numeric or array_like + J Correlate of *Lightness* :math:`J`. - C : numeric or array_like + C Correlate of *chroma* :math:`C`. - h : numeric or array_like + h *Hue* angle :math:`h` in degrees. - s : numeric or array_like + s Correlate of *saturation* :math:`s`. - Q : numeric or array_like + Q Correlate of *brightness* :math:`Q`. - M : numeric or array_like + M Correlate of *colourfulness* :math:`M`. - H : numeric or array_like + H *Hue* :math:`h` quadrature :math:`H`. - HC : numeric or array_like + HC *Hue* :math:`h` composition :math:`H^C`. References @@ -152,72 +221,65 @@ class CAM_Specification_CIECAM02( :cite:`Wikipedia2007a` """ - def __new__(cls, - J=None, - C=None, - h=None, - s=None, - Q=None, - M=None, - H=None, - HC=None): - """ - Returns a new instance of the :class:`colour.\ -CAM_Specification_CIECAM02` class. - """ - - return super(CAM_Specification_CIECAM02, cls).__new__( - cls, J, C, h, s, Q, M, H, HC) - - -def XYZ_to_CIECAM02(XYZ, - XYZ_w, - L_A, - Y_b, - surround=VIEWING_CONDITIONS_CIECAM02['Average'], - discount_illuminant=False): + J: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + C: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + h: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + s: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + Q: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + M: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + H: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + HC: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + + +def XYZ_to_CIECAM02( + XYZ: ArrayLike, + XYZ_w: ArrayLike, + L_A: FloatingOrArrayLike, + Y_b: FloatingOrArrayLike, + surround: InductionFactors_CIECAM02 = VIEWING_CONDITIONS_CIECAM02[ + "Average" + ], + discount_illuminant: Boolean = False, +) -> CAM_Specification_CIECAM02: """ - Computes the *CIECAM02* colour appearance model correlates from given + Compute the *CIECAM02* colour appearance model correlates from given *CIE XYZ* tristimulus values. - This is the *forward* implementation. - Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values of test sample / stimulus. - XYZ_w : array_like + XYZ_w *CIE XYZ* tristimulus values of reference white. - L_A : numeric or array_like + L_A Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`, (often taken to be 20% of the luminance of a white object in the scene). - Y_b : numeric or array_like + Y_b Luminous factor of background :math:`Y_b` such as :math:`Y_b = 100 x L_b / L_w` where :math:`L_w` is the luminance of the light source and :math:`L_b` is the luminance of the background. For viewing images, :math:`Y_b` can be the average :math:`Y` value for the pixels in the entire image, or frequently, a :math:`Y` value of 20, approximate an :math:`L^*` of 50 is used. - surround : InductionFactors_CIECAM02, optional + surround Surround viewing conditions induction factors. - discount_illuminant : bool, optional + discount_illuminant Truth value indicating if the illuminant should be discounted. Returns ------- - CAM_Specification_CIECAM02 + :class:`colour.CAM_Specification_CIECAM02` *CIECAM02* colour appearance model specification. Notes ----- - - +------------------------------+-----------------------+---------------+ - | **Domain** | **Scale - Reference** | **Scale - 1** | - +==============================+=======================+===============+ - | ``XYZ`` | [0, 100] | [0, 1] | - +------------------------------+-----------------------+---------------+ - | ``XYZ_w`` | [0, 100] | [0, 1] | - +------------------------------+-----------------------+---------------+ + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``XYZ`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ + | ``XYZ_w`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ +----------------------------------+-----------------------\ +---------------+ @@ -278,8 +340,9 @@ def XYZ_to_CIECAM02(XYZ, L_A = as_float_array(L_A) Y_b = as_float_array(Y_b) - n, F_L, N_bb, N_cb, z = tsplit( - viewing_condition_dependent_parameters(Y_b, Y_w, L_A)) + n, F_L, N_bb, N_cb, z = viewing_condition_dependent_parameters( + Y_b, Y_w, L_A + ) # Converting *CIE XYZ* tristimulus values to *CMCCAT2000* transform # sharpened *RGB* values. @@ -287,8 +350,11 @@ def XYZ_to_CIECAM02(XYZ, RGB_w = vector_dot(CAT_CAT02, XYZ_w) # Computing degree of adaptation :math:`D`. - D = (degree_of_adaptation(surround.F, L_A) - if not discount_illuminant else ones(L_A.shape)) + D = ( + degree_of_adaptation(surround.F, L_A) + if not discount_illuminant + else ones(L_A.shape) + ) # Computing full chromatic adaptation. RGB_c = full_chromatic_adaptation_forward(RGB, RGB_w, Y_w, D) @@ -298,10 +364,11 @@ def XYZ_to_CIECAM02(XYZ, RGB_p = RGB_to_rgb(RGB_c) RGB_pw = RGB_to_rgb(RGB_wc) - # Applying forward post-adaptation non linear response compression. + # Applying forward post-adaptation non-linear response compression. RGB_a = post_adaptation_non_linear_response_compression_forward(RGB_p, F_L) RGB_aw = post_adaptation_non_linear_response_compression_forward( - RGB_pw, F_L) + RGB_pw, F_L + ) # Converting to preliminary cartesian coordinates. a, b = tsplit(opponent_colour_dimensions_forward(RGB_a)) @@ -336,49 +403,57 @@ def XYZ_to_CIECAM02(XYZ, s = saturation_correlate(M, Q) return CAM_Specification_CIECAM02( - from_range_100(J), from_range_100(C), from_range_degrees(h), - from_range_100(s), from_range_100(Q), from_range_100(M), - from_range_degrees(H, 400), None) + as_float(from_range_100(J)), + as_float(from_range_100(C)), + as_float(from_range_degrees(h)), + as_float(from_range_100(s)), + as_float(from_range_100(Q)), + as_float(from_range_100(M)), + as_float(from_range_degrees(H, 400)), + None, + ) -def CIECAM02_to_XYZ(specification, - XYZ_w, - L_A, - Y_b, - surround=VIEWING_CONDITIONS_CIECAM02['Average'], - discount_illuminant=False): +def CIECAM02_to_XYZ( + specification: CAM_Specification_CIECAM02, + XYZ_w: ArrayLike, + L_A: FloatingOrArrayLike, + Y_b: FloatingOrArrayLike, + surround: InductionFactors_CIECAM02 = VIEWING_CONDITIONS_CIECAM02[ + "Average" + ], + discount_illuminant: Boolean = False, +) -> NDArray: """ - Converts from *CIECAM02* specification to *CIE XYZ* tristimulus values. - - This is the *inverse* implementation. + Convert from *CIECAM02* specification to *CIE XYZ* tristimulus values. Parameters ---------- - specification : CAM_Specification_CIECAM02 + specification *CIECAM02* colour appearance model specification. Correlate of *Lightness* :math:`J`, correlate of *chroma* :math:`C` or correlate of *colourfulness* :math:`M` and *hue* angle :math:`h` in degrees must be specified, e.g. :math:`JCh` or :math:`JMh`. - XYZ_w : array_like + XYZ_w *CIE XYZ* tristimulus values of reference white. - L_A : numeric or array_like + L_A Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`, (often taken to be 20% of the luminance of a white object in the scene). - Y_b : numeric or array_like + Y_b Luminous factor of background :math:`Y_b` such as :math:`Y_b = 100 x L_b / L_w` where :math:`L_w` is the luminance of the light source and :math:`L_b` is the luminance of the background. For viewing images, :math:`Y_b` can be the average :math:`Y` value for the pixels in the entire image, or frequently, a :math:`Y` value of 20, approximate an :math:`L^*` of 50 is used. - surround : InductionFactors_CIECAM02, optional + surround Surround viewing conditions. - discount_illuminant : bool, optional + discount_illuminant Discount the illuminant. Returns ------- - XYZ : ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Raises @@ -387,13 +462,8 @@ def CIECAM02_to_XYZ(specification, If neither *C* or *M* correlates have been defined in the ``CAM_Specification_CIECAM02`` argument. - Warnings - -------- - The output range of that definition is non standard! - Notes ----- - +----------------------------------+-----------------------\ +---------------+ | **Domain** | **Scale - Reference** \ @@ -433,14 +503,11 @@ def CIECAM02_to_XYZ(specification, +----------------------------------+-----------------------\ +---------------+ - +------------------------------+-----------------------+---------------+ - | **Range** | **Scale - Reference** | **Scale - 1** | - +==============================+=======================+===============+ - | ``XYZ`` | [0, 100] | [0, 1] | - +------------------------------+-----------------------+---------------+ - - - ``CAM_Specification_CIECAM02`` can also be passed as a compatible - argument to :func:`colour.utilities.as_namedtuple` definition. + +-----------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +===========+=======================+===============+ + | ``XYZ`` | [0, 100] | [0, 1] | + +-----------+-----------------------+---------------+ References ---------- @@ -450,8 +517,8 @@ def CIECAM02_to_XYZ(specification, Examples -------- >>> specification = CAM_Specification_CIECAM02(J=41.731091132513917, - ... C=0.104707757171031, - ... h=219.048432658311780) + ... C=0.104707757171031, + ... h=219.048432658311780) >>> XYZ_w = np.array([95.05, 100.00, 108.88]) >>> L_A = 318.31 >>> Y_b = 20.0 @@ -459,32 +526,38 @@ def CIECAM02_to_XYZ(specification, array([ 19.01..., 20... , 21.78...]) """ - J, C, h, _s, _Q, M, _H, _HC = as_namedtuple(specification, - CAM_Specification_CIECAM02) + J, C, h, _s, _Q, M, _H, _HC = astuple(specification) + J = to_domain_100(J) - C = to_domain_100(C) if C is not None else C + C = to_domain_100(C) h = to_domain_degrees(h) - M = to_domain_100(M) if M is not None else M + M = to_domain_100(M) L_A = as_float_array(L_A) XYZ_w = to_domain_100(XYZ_w) _X_w, Y_w, _Z_w = tsplit(XYZ_w) - n, F_L, N_bb, N_cb, z = tsplit( - viewing_condition_dependent_parameters(Y_b, Y_w, L_A)) + n, F_L, N_bb, N_cb, z = viewing_condition_dependent_parameters( + Y_b, Y_w, L_A + ) - if C is None and M is not None: + if has_only_nan(C) and not has_only_nan(M): C = M / spow(F_L, 0.25) - elif C is None: - raise ValueError('Either "C" or "M" correlate must be defined in ' - 'the "CAM_Specification_CIECAM02" argument!') + elif has_only_nan(C): + raise ValueError( + 'Either "C" or "M" correlate must be defined in ' + 'the "CAM_Specification_CIECAM02" argument!' + ) # Converting *CIE XYZ* tristimulus values to *CMCCAT2000* transform # sharpened *RGB* values. RGB_w = vector_dot(CAT_CAT02, XYZ_w) # Computing degree of adaptation :math:`D`. - D = (degree_of_adaptation(surround.F, L_A) - if not discount_illuminant else ones(L_A.shape)) + D = ( + degree_of_adaptation(surround.F, L_A) + if not discount_illuminant + else ones(L_A.shape) + ) # Computing full chromatic adaptation. RGB_wc = full_chromatic_adaptation_forward(RGB_w, RGB_w, Y_w, D) @@ -492,9 +565,10 @@ def CIECAM02_to_XYZ(specification, # Converting to *Hunt-Pointer-Estevez* colourspace. RGB_pw = RGB_to_rgb(RGB_wc) - # Applying post-adaptation non linear response compression. + # Applying post-adaptation non-linear response compression. RGB_aw = post_adaptation_non_linear_response_compression_forward( - RGB_pw, F_L) + RGB_pw, F_L + ) # Computing achromatic response for the whitepoint. A_w = achromatic_response_forward(RGB_aw, N_bb) @@ -515,10 +589,10 @@ def CIECAM02_to_XYZ(specification, # Computing opponent colour dimensions :math:`a` and :math:`b`. a, b = tsplit(opponent_colour_dimensions_inverse(P_n, h)) - # Computing post-adaptation non linear response compression matrix. + # Applying post-adaptation non-linear response compression matrix. RGB_a = matrix_post_adaptation_non_linear_response_compression(P_2, a, b) - # Applying inverse post-adaptation non linear response compression. + # Applying inverse post-adaptation non-linear response compression. RGB_p = post_adaptation_non_linear_response_compression_inverse(RGB_a, F_L) # Converting to *Hunt-Pointer-Estevez* colourspace. @@ -529,23 +603,23 @@ def CIECAM02_to_XYZ(specification, # Converting *CMCCAT2000* transform sharpened *RGB* values to *CIE XYZ* # tristimulus values. - XYZ = vector_dot(CAT02_INVERSE_CAT, RGB) + XYZ = vector_dot(CAT_INVERSE_CAT02, RGB) return from_range_100(XYZ) -def chromatic_induction_factors(n): +def chromatic_induction_factors(n: FloatingOrArrayLike) -> NDArray: """ - Returns the chromatic induction factors :math:`N_{bb}` and :math:`N_{cb}`. + Return the chromatic induction factors :math:`N_{bb}` and :math:`N_{cb}`. Parameters ---------- - n : numeric or array_like + n Function of the luminance factor of the background :math:`n`. Returns ------- - ndarray + :class:`numpy.ndarray` Chromatic induction factors :math:`N_{bb}` and :math:`N_{cb}`. Examples @@ -556,25 +630,27 @@ def chromatic_induction_factors(n): n = as_float_array(n) - N_bb = N_cb = 0.725 * spow(1 / n, 0.2) + N_bb = N_cb = as_float(0.725) * spow(1 / n, 0.2) N_bbcb = tstack([N_bb, N_cb]) return N_bbcb -def base_exponential_non_linearity(n): +def base_exponential_non_linearity( + n: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Returns the base exponential non linearity :math:`n`. + Return the base exponential non-linearity :math:`n`. Parameters ---------- - n : numeric or array_like + n Function of the luminance factor of the background :math:`n`. Returns ------- - numeric or ndarray - Base exponential non linearity :math:`z`. + :class:`numpy.floating` or :class:`numpy.ndarray` + Base exponential non-linearity :math:`z`. Examples -------- @@ -589,29 +665,39 @@ def base_exponential_non_linearity(n): return z -def viewing_condition_dependent_parameters(Y_b, Y_w, L_A): +def viewing_condition_dependent_parameters( + Y_b: FloatingOrArrayLike, + Y_w: FloatingOrArrayLike, + L_A: FloatingOrArrayLike, +) -> Tuple[ + FloatingOrNDArray, + FloatingOrNDArray, + FloatingOrNDArray, + FloatingOrNDArray, + FloatingOrNDArray, +]: """ - Returns the viewing condition dependent parameters. + Return the viewing condition dependent parameters. Parameters ---------- - Y_b : numeric or array_like + Y_b Adapting field *Y* tristimulus value :math:`Y_b`. - Y_w : numeric or array_like + Y_w Whitepoint *Y* tristimulus value :math:`Y_w`. - L_A : numeric or array_like + L_A Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`. Returns ------- - ndarray + :class:`tuple` Viewing condition dependent parameters. Examples -------- >>> viewing_condition_dependent_parameters(20.0, 100.0, 318.31) ... # doctest: +ELLIPSIS - array([ 0.2..., 1.1675444..., 1.000304 , 1.000304 , 1.9272136...]) + (0.2000000..., 1.1675444..., 1.0003040..., 1.0003040..., 1.9272135...) """ Y_b = as_float_array(Y_b) @@ -623,25 +709,27 @@ def viewing_condition_dependent_parameters(Y_b, Y_w, L_A): N_bb, N_cb = tsplit(chromatic_induction_factors(n)) z = base_exponential_non_linearity(n) - return tstack([n, F_L, N_bb, N_cb, z]) + return n, F_L, N_bb, N_cb, z -def degree_of_adaptation(F, L_A): +def degree_of_adaptation( + F: FloatingOrArrayLike, L_A: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the degree of adaptation :math:`D` from given surround maximum - degree of adaptation :math:`F` and Adapting field *luminance* :math:`L_A` + Return the degree of adaptation :math:`D` from given surround maximum + degree of adaptation :math:`F` and adapting field *luminance* :math:`L_A` in :math:`cd/m^2`. Parameters ---------- - F : numeric or array_like + F Surround maximum degree of adaptation :math:`F`. - L_A : numeric or array_like + L_A Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Degree of adaptation :math:`D`. Examples @@ -658,26 +746,31 @@ def degree_of_adaptation(F, L_A): return D -def full_chromatic_adaptation_forward(RGB, RGB_w, Y_w, D): +def full_chromatic_adaptation_forward( + RGB: ArrayLike, + RGB_w: ArrayLike, + Y_w: FloatingOrArrayLike, + D: FloatingOrArrayLike, +) -> NDArray: """ - Applies full chromatic adaptation to given *CMCCAT2000* transform sharpened + Apply full chromatic adaptation to given *CMCCAT2000* transform sharpened *RGB* array using given *CMCCAT2000* transform sharpened whitepoint *RGB_w* array. Parameters ---------- - RGB : array_like + RGB *CMCCAT2000* transform sharpened *RGB* array. - RGB_w : array_like + RGB_w *CMCCAT2000* transform sharpened whitepoint *RGB_w* array. - Y_w : numeric or array_like + Y_w Whitepoint *Y* tristimulus value :math:`Y_w`. - D : numeric or array_like + D Degree of adaptation :math:`D`. Returns ------- - ndarray + :class:`numpy.ndarray` Adapted *RGB* array. Examples @@ -696,32 +789,40 @@ def full_chromatic_adaptation_forward(RGB, RGB_w, Y_w, D): Y_w = as_float_array(Y_w) D = as_float_array(D) - RGB_c = (((Y_w[..., np.newaxis] * D[..., np.newaxis] / RGB_w) + 1 - - D[..., np.newaxis]) * RGB) + RGB_c = ( + (Y_w[..., np.newaxis] * D[..., np.newaxis] / RGB_w) + + 1 + - D[..., np.newaxis] + ) * RGB return RGB_c -def full_chromatic_adaptation_inverse(RGB, RGB_w, Y_w, D): +def full_chromatic_adaptation_inverse( + RGB: ArrayLike, + RGB_w: ArrayLike, + Y_w: FloatingOrArrayLike, + D: FloatingOrArrayLike, +) -> NDArray: """ - Reverts full chromatic adaptation of given *CMCCAT2000* transform sharpened + Revert full chromatic adaptation of given *CMCCAT2000* transform sharpened *RGB* array using given *CMCCAT2000* transform sharpened whitepoint *RGB_w* array. Parameters ---------- - RGB : array_like + RGB *CMCCAT2000* transform sharpened *RGB* array. - RGB_w : array_like + RGB_w *CMCCAT2000* transform sharpened whitepoint *RGB_w* array. - Y_w : numeric or array_like + Y_w Whitepoint *Y* tristimulus value :math:`Y_w`. - D : numeric or array_like + D Degree of adaptation :math:`D`. Returns ------- - ndarray + :class:`numpy.ndarray` Adapted *RGB* array. Examples @@ -739,25 +840,28 @@ def full_chromatic_adaptation_inverse(RGB, RGB_w, Y_w, D): Y_w = as_float_array(Y_w) D = as_float_array(D) - RGB_c = (RGB / (Y_w[..., np.newaxis] * - (D[..., np.newaxis] / RGB_w) + 1 - D[..., np.newaxis])) + RGB_c = RGB / ( + Y_w[..., np.newaxis] * (D[..., np.newaxis] / RGB_w) + + 1 + - D[..., np.newaxis] + ) return RGB_c -def RGB_to_rgb(RGB): +def RGB_to_rgb(RGB: ArrayLike) -> NDArray: """ - Converts given *RGB* array to *Hunt-Pointer-Estevez* + Convert given *RGB* array to *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace. Parameters ---------- - RGB : array_like + RGB *RGB* array. Returns ------- - ndarray + :class:`numpy.ndarray` *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array. Examples @@ -767,24 +871,24 @@ def RGB_to_rgb(RGB): array([ 19.9969397..., 20.0018612..., 20.0135053...]) """ - rgb = vector_dot(matrix_dot(MATRIX_XYZ_TO_HPE, CAT02_INVERSE_CAT), RGB) + rgb = vector_dot(matrix_dot(MATRIX_XYZ_TO_HPE, CAT_INVERSE_CAT02), RGB) return rgb -def rgb_to_RGB(rgb): +def rgb_to_RGB(rgb: ArrayLike) -> NDArray: """ - Converts given *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` + Convert given *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array to *RGB* array. Parameters ---------- - rgb : array_like + rgb *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *RGB* array. Examples @@ -799,21 +903,23 @@ def rgb_to_RGB(rgb): return RGB -def post_adaptation_non_linear_response_compression_forward(RGB, F_L): +def post_adaptation_non_linear_response_compression_forward( + RGB: ArrayLike, F_L: FloatingOrArrayLike +) -> NDArray: """ - Returns given *CMCCAT2000* transform sharpened *RGB* array with post - adaptation non linear response compression. + Return given *CMCCAT2000* transform sharpened *RGB* array with post + adaptation non-linear response compression. Parameters ---------- - RGB : array_like + RGB *CMCCAT2000* transform sharpened *RGB* array. - F_L : array_like + F_L *Luminance* level adaptation factor :math:`F_L`. Returns ------- - ndarray + :class:`numpy.ndarray` Compressed *CMCCAT2000* transform sharpened *RGB* array. Notes @@ -839,21 +945,23 @@ def post_adaptation_non_linear_response_compression_forward(RGB, F_L): return RGB_c -def post_adaptation_non_linear_response_compression_inverse(RGB, F_L): +def post_adaptation_non_linear_response_compression_inverse( + RGB: ArrayLike, F_L: FloatingOrArrayLike +) -> NDArray: """ - Returns given *CMCCAT2000* transform sharpened *RGB* array without post - adaptation non linear response compression. + Return given *CMCCAT2000* transform sharpened *RGB* array without post + adaptation non-linear response compression. Parameters ---------- - RGB : array_like + RGB *CMCCAT2000* transform sharpened *RGB* array. - F_L : array_like + F_L *Luminance* level adaptation factor :math:`F_L`. Returns ------- - ndarray + :class:`numpy.ndarray` Uncompressed *CMCCAT2000* transform sharpened *RGB* array. Examples @@ -868,26 +976,31 @@ def post_adaptation_non_linear_response_compression_inverse(RGB, F_L): RGB = as_float_array(RGB) F_L = as_float_array(F_L) - RGB_p = ((np.sign(RGB - 0.1) * (100 / F_L[..., np.newaxis]) * spow( - (27.13 * np.absolute(RGB - 0.1)) / (400 - np.absolute(RGB - 0.1)), - 1 / 0.42))) + RGB_p = ( + np.sign(RGB - 0.1) + * (100 / F_L[..., np.newaxis]) + * spow( + (27.13 * np.absolute(RGB - 0.1)) / (400 - np.absolute(RGB - 0.1)), + 1 / 0.42, + ) + ) return RGB_p -def opponent_colour_dimensions_forward(RGB): +def opponent_colour_dimensions_forward(RGB: ArrayLike) -> NDArray: """ - Returns opponent colour dimensions from given compressed *CMCCAT2000* + Return opponent colour dimensions from given compressed *CMCCAT2000* transform sharpened *RGB* array for forward *CIECAM02* implementation. Parameters ---------- - RGB : array_like + RGB Compressed *CMCCAT2000* transform sharpened *RGB* array. Returns ------- - ndarray + :class:`numpy.ndarray` Opponent colour dimensions. Examples @@ -907,21 +1020,23 @@ def opponent_colour_dimensions_forward(RGB): return ab -def opponent_colour_dimensions_inverse(P_n, h): +def opponent_colour_dimensions_inverse( + P_n: ArrayLike, h: FloatingOrArrayLike +) -> NDArray: """ - Returns opponent colour dimensions from given points :math:`P_n` and hue + Return opponent colour dimensions from given points :math:`P_n` and hue :math:`h` in degrees for inverse *CIECAM02* implementation. Parameters ---------- - P_n : array_like + P_n Points :math:`P_n`. - h : numeric or array_like + h Hue :math:`h` in degrees. Returns ------- - ndarray + :class:`numpy.ndarray` Opponent colour dimensions. Notes @@ -952,8 +1067,15 @@ def opponent_colour_dimensions_inverse(P_n, h): b = np.where( np.isfinite(P_1) * np.abs(sin_hr) >= np.abs(cos_hr), - (n / (P_4 + (2 + P_3) * (220 / 1403) * (cos_hr / sin_hr) - - (27 / 1403) + P_3 * (6300 / 1403))), + ( + n + / ( + P_4 + + (2 + P_3) * (220 / 1403) * (cos_hr / sin_hr) + - (27 / 1403) + + P_3 * (6300 / 1403) + ) + ), b, ) @@ -965,8 +1087,14 @@ def opponent_colour_dimensions_inverse(P_n, h): a = np.where( np.isfinite(P_1) * np.abs(sin_hr) < np.abs(cos_hr), - (n / (P_5 + (2 + P_3) * (220 / 1403) - ( - (27 / 1403) - P_3 * (6300 / 1403)) * (sin_hr / cos_hr))), + ( + n + / ( + P_5 + + (2 + P_3) * (220 / 1403) + - ((27 / 1403) - P_3 * (6300 / 1403)) * (sin_hr / cos_hr) + ) + ), a, ) @@ -981,20 +1109,22 @@ def opponent_colour_dimensions_inverse(P_n, h): return ab -def hue_angle(a, b): +def hue_angle( + a: FloatingOrArrayLike, b: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the *hue* angle :math:`h` in degrees. + Return the *hue* angle :math:`h` in degrees. Parameters ---------- - a : numeric or array_like + a Opponent colour dimension :math:`a`. - b : numeric or array_like + b Opponent colour dimension :math:`b`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Hue* angle :math:`h` in degrees. Examples @@ -1010,21 +1140,21 @@ def hue_angle(a, b): h = np.degrees(np.arctan2(b, a)) % 360 - return h + return as_float(h) -def hue_quadrature(h): +def hue_quadrature(h: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the hue quadrature from given hue :math:`h` angle in degrees. + Return the hue quadrature from given hue :math:`h` angle in degrees. Parameters ---------- - h : numeric or array_like + h Hue :math:`h` angle in degrees. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Hue quadrature. Examples @@ -1035,13 +1165,13 @@ def hue_quadrature(h): h = as_float_array(h) - h_i = HUE_DATA_FOR_HUE_QUADRATURE['h_i'] - e_i = HUE_DATA_FOR_HUE_QUADRATURE['e_i'] - H_i = HUE_DATA_FOR_HUE_QUADRATURE['H_i'] + h_i = HUE_DATA_FOR_HUE_QUADRATURE["h_i"] + e_i = HUE_DATA_FOR_HUE_QUADRATURE["e_i"] + H_i = HUE_DATA_FOR_HUE_QUADRATURE["H_i"] # *np.searchsorted* returns an erroneous index if a *nan* is used as input. h[np.asarray(np.isnan(h))] = 0 - i = as_int_array(np.searchsorted(h_i, h, side='left') - 1) + i = as_int_array(np.searchsorted(h_i, h, side="left") - 1) h_ii = h_i[i] e_ii = e_i[i] @@ -1049,8 +1179,10 @@ def hue_quadrature(h): h_ii1 = h_i[i + 1] e_ii1 = e_i[i + 1] - H = H_ii + ((100 * (h - h_ii) / e_ii) / ( - (h - h_ii) / e_ii + (h_ii1 - h) / e_ii1)) + H = H_ii + ( + (100 * (h - h_ii) / e_ii) / ((h - h_ii) / e_ii + (h_ii1 - h) / e_ii1) + ) + H = np.where( h < 20.14, 385.9 + (14.1 * h / 0.856) / (h / 0.856 + (20.14 - h) / 0.8), @@ -1058,26 +1190,29 @@ def hue_quadrature(h): ) H = np.where( h >= 237.53, - H_ii + ((85.9 * (h - h_ii) / e_ii) / ( - (h - h_ii) / e_ii + (360 - h) / 0.856)), + H_ii + + ( + (85.9 * (h - h_ii) / e_ii) + / ((h - h_ii) / e_ii + (360 - h) / 0.856) + ), H, ) return as_float(H) -def eccentricity_factor(h): +def eccentricity_factor(h: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the eccentricity factor :math:`e_t` from given hue :math:`h` angle + Return the eccentricity factor :math:`e_t` from given hue :math:`h` angle in degrees for forward *CIECAM02* implementation. Parameters ---------- - h : numeric or array_like + h Hue :math:`h` angle in degrees. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Eccentricity factor :math:`e_t`. Examples @@ -1093,22 +1228,24 @@ def eccentricity_factor(h): return e_t -def achromatic_response_forward(RGB, N_bb): +def achromatic_response_forward( + RGB: ArrayLike, N_bb: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the achromatic response :math:`A` from given compressed + Return the achromatic response :math:`A` from given compressed *CMCCAT2000* transform sharpened *RGB* array and :math:`N_{bb}` chromatic induction factor for forward *CIECAM02* implementation. Parameters ---------- - RGB : array_like + RGB Compressed *CMCCAT2000* transform sharpened *RGB* array. - N_bb : numeric or array_like + N_bb Chromatic induction factor :math:`N_{bb}`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Achromatic response :math:`A`. Examples @@ -1126,27 +1263,32 @@ def achromatic_response_forward(RGB, N_bb): return A -def achromatic_response_inverse(A_w, J, c, z): +def achromatic_response_inverse( + A_w: FloatingOrArrayLike, + J: FloatingOrArrayLike, + c: FloatingOrArrayLike, + z: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Returns the achromatic response :math:`A` from given achromatic response + Return the achromatic response :math:`A` from given achromatic response :math:`A_w` for the whitepoint, *Lightness* correlate :math:`J`, surround - exponential non linearity :math:`c` and base exponential non linearity + exponential non-linearity :math:`c` and base exponential non-linearity :math:`z` for inverse *CIECAM02* implementation. Parameters ---------- - A_w : numeric or array_like + A_w Achromatic response :math:`A_w` for the whitepoint. - J : numeric or array_like + J *Lightness* correlate :math:`J`. - c : numeric or array_like - Surround exponential non linearity :math:`c`. - z : numeric or array_like - Base exponential non linearity :math:`z`. + c + Surround exponential non-linearity :math:`c`. + z + Base exponential non-linearity :math:`z`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Achromatic response :math:`A`. Examples @@ -1169,24 +1311,29 @@ def achromatic_response_inverse(A_w, J, c, z): return A -def lightness_correlate(A, A_w, c, z): +def lightness_correlate( + A: FloatingOrArrayLike, + A_w: FloatingOrArrayLike, + c: FloatingOrArrayLike, + z: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Returns the *Lightness* correlate :math:`J`. + Return the *Lightness* correlate :math:`J`. Parameters ---------- - A : numeric or array_like + A Achromatic response :math:`A` for the stimulus. - A_w : numeric or array_like + A_w Achromatic response :math:`A_w` for the whitepoint. - c : numeric or array_like - Surround exponential non linearity :math:`c`. - z : numeric or array_like - Base exponential non linearity :math:`z`. + c + Surround exponential non-linearity :math:`c`. + z + Base exponential non-linearity :math:`z`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Lightness* correlate :math:`J`. Examples @@ -1209,24 +1356,29 @@ def lightness_correlate(A, A_w, c, z): return J -def brightness_correlate(c, J, A_w, F_L): +def brightness_correlate( + c: FloatingOrArrayLike, + J: FloatingOrArrayLike, + A_w: FloatingOrArrayLike, + F_L: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Returns the *brightness* correlate :math:`Q`. + Return the *brightness* correlate :math:`Q`. Parameters ---------- - c : numeric or array_like - Surround exponential non linearity :math:`c`. - J : numeric or array_like + c + Surround exponential non-linearity :math:`c`. + J *Lightness* correlate :math:`J`. - A_w : numeric or array_like + A_w Achromatic response :math:`A_w` for the whitepoint. - F_L : numeric or array_like + F_L *Luminance* level adaptation factor :math:`F_L`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Brightness* correlate :math:`Q`. Examples @@ -1249,29 +1401,36 @@ def brightness_correlate(c, J, A_w, F_L): return Q -def temporary_magnitude_quantity_forward(N_c, N_cb, e_t, a, b, RGB_a): +def temporary_magnitude_quantity_forward( + N_c: FloatingOrArrayLike, + N_cb: FloatingOrArrayLike, + e_t: FloatingOrArrayLike, + a: FloatingOrArrayLike, + b: FloatingOrArrayLike, + RGB_a: ArrayLike, +) -> FloatingOrNDArray: """ - Returns the temporary magnitude quantity :math:`t`. for forward *CIECAM02* + Return the temporary magnitude quantity :math:`t`. for forward *CIECAM02* implementation. Parameters ---------- - N_c : numeric or array_like + N_c Surround chromatic induction factor :math:`N_{c}`. - N_cb : numeric or array_like + N_cb Chromatic induction factor :math:`N_{cb}`. - e_t : numeric or array_like + e_t Eccentricity factor :math:`e_t`. - a : numeric or array_like + a Opponent colour dimension :math:`a`. - b : numeric or array_like + b Opponent colour dimension :math:`b`. - RGB_a : array_like + RGB_a Compressed stimulus *CMCCAT2000* transform sharpened *RGB* array. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Temporary magnitude quantity :math:`t`. Examples @@ -1294,29 +1453,34 @@ def temporary_magnitude_quantity_forward(N_c, N_cb, e_t, a, b, RGB_a): b = as_float_array(b) Ra, Ga, Ba = tsplit(RGB_a) - t = (((50000 / 13) * N_c * N_cb) * (e_t * spow(a ** 2 + b ** 2, 0.5)) / - (Ra + Ga + 21 * Ba / 20)) + t = ( + ((50000 / 13) * N_c * N_cb) + * (e_t * spow(a**2 + b**2, 0.5)) + / (Ra + Ga + 21 * Ba / 20) + ) return t -def temporary_magnitude_quantity_inverse(C, J, n): +def temporary_magnitude_quantity_inverse( + C: FloatingOrArrayLike, J: FloatingOrArrayLike, n: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the temporary magnitude quantity :math:`t`. for inverse *CIECAM02* + Return the temporary magnitude quantity :math:`t`. for inverse *CIECAM02* implementation. Parameters ---------- - C : numeric or array_like + C *Chroma* correlate :math:`C`. - J : numeric or array_like + J *Lightness* correlate :math:`J`. - n : numeric or array_like + n Function of the luminance factor of the background :math:`n`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Temporary magnitude quantity :math:`t`. Notes @@ -1331,43 +1495,52 @@ def temporary_magnitude_quantity_inverse(C, J, n): >>> n = 0.2 >>> temporary_magnitude_quantity_inverse(C, J, n) # doctest: +ELLIPSIS 202.3873619... - """ + """ C = as_float_array(C) J = np.maximum(J, EPSILON) n = as_float_array(n) - t = spow(C / (np.sqrt(J / 100) * spow(1.64 - 0.29 ** n, 0.73)), 1 / 0.9) + t = spow(C / (np.sqrt(J / 100) * spow(1.64 - 0.29**n, 0.73)), 1 / 0.9) return t -def chroma_correlate(J, n, N_c, N_cb, e_t, a, b, RGB_a): +def chroma_correlate( + J: FloatingOrArrayLike, + n: FloatingOrArrayLike, + N_c: FloatingOrArrayLike, + N_cb: FloatingOrArrayLike, + e_t: FloatingOrArrayLike, + a: FloatingOrArrayLike, + b: FloatingOrArrayLike, + RGB_a: ArrayLike, +) -> FloatingOrNDArray: """ - Returns the *chroma* correlate :math:`C`. + Return the *chroma* correlate :math:`C`. Parameters ---------- - J : numeric or array_like + J *Lightness* correlate :math:`J`. - n : numeric or array_like + n Function of the luminance factor of the background :math:`n`. - N_c : numeric or array_like + N_c Surround chromatic induction factor :math:`N_{c}`. - N_cb : numeric or array_like + N_cb Chromatic induction factor :math:`N_{cb}`. - e_t : numeric or array_like + e_t Eccentricity factor :math:`e_t`. - a : numeric or array_like + a Opponent colour dimension :math:`a`. - b : numeric or array_like + b Opponent colour dimension :math:`b`. - RGB_a : array_like + RGB_a Compressed stimulus *CMCCAT2000* transform sharpened *RGB* array. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Chroma* correlate :math:`C`. Examples @@ -1389,25 +1562,27 @@ def chroma_correlate(J, n, N_c, N_cb, e_t, a, b, RGB_a): n = as_float_array(n) t = temporary_magnitude_quantity_forward(N_c, N_cb, e_t, a, b, RGB_a) - C = spow(t, 0.9) * spow(J / 100, 0.5) * spow(1.64 - 0.29 ** n, 0.73) + C = spow(t, 0.9) * spow(J / 100, 0.5) * spow(1.64 - 0.29**n, 0.73) return C -def colourfulness_correlate(C, F_L): +def colourfulness_correlate( + C: FloatingOrArrayLike, F_L: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the *colourfulness* correlate :math:`M`. + Return the *colourfulness* correlate :math:`M`. Parameters ---------- - C : numeric or array_like + C *Chroma* correlate :math:`C`. - F_L : numeric or array_like + F_L *Luminance* level adaptation factor :math:`F_L`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Colourfulness* correlate :math:`M`. Examples @@ -1426,20 +1601,22 @@ def colourfulness_correlate(C, F_L): return M -def saturation_correlate(M, Q): +def saturation_correlate( + M: FloatingOrArrayLike, Q: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the *saturation* correlate :math:`s`. + Return the *saturation* correlate :math:`s`. Parameters ---------- - M : numeric or array_like + M *Colourfulness* correlate :math:`M`. - Q : numeric or array_like + Q *Brightness* correlate :math:`C`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Saturation* correlate :math:`s`. Examples @@ -1458,28 +1635,35 @@ def saturation_correlate(M, Q): return s -def P(N_c, N_cb, e_t, t, A, N_bb): +def P( + N_c: FloatingOrArrayLike, + N_cb: FloatingOrArrayLike, + e_t: FloatingOrArrayLike, + t: FloatingOrArrayLike, + A: FloatingOrArrayLike, + N_bb: FloatingOrArrayLike, +) -> NDArray: """ - Returns the points :math:`P_1`, :math:`P_2` and :math:`P_3`. + Return the points :math:`P_1`, :math:`P_2` and :math:`P_3`. Parameters ---------- - N_c : numeric or array_like + N_c Surround chromatic induction factor :math:`N_{c}`. - N_cb : numeric or array_like + N_cb Chromatic induction factor :math:`N_{cb}`. - e_t : numeric or array_like + e_t Eccentricity factor :math:`e_t`. - t : numeric or array_like + t Temporary magnitude quantity :math:`t`. - A : numeric or array_like + A Achromatic response :math:`A` for the stimulus. - N_bb : numeric or array_like + N_bb Chromatic induction factor :math:`N_{bb}`. Returns ------- - ndarray + :class:`numpy.ndarray` Points :math:`P`. Examples @@ -1510,22 +1694,24 @@ def P(N_c, N_cb, e_t, t, A, N_bb): return P_n -def matrix_post_adaptation_non_linear_response_compression(P_2, a, b): +def matrix_post_adaptation_non_linear_response_compression( + P_2: FloatingOrArrayLike, a: FloatingOrArrayLike, b: FloatingOrArrayLike +) -> NDArray: """ - Returns the post-adaptation non-linear-response compression matrix. + Apply the post-adaptation non-linear-response compression matrix. Parameters ---------- - P_2 : numeric or array_like + P_2 Point :math:`P_2`. - a : numeric or array_like + a Opponent colour dimension :math:`a`. - b : numeric or array_like + b Opponent colour dimension :math:`b`. Returns ------- - ndarray + :class:`numpy.ndarray` Points :math:`P`. Examples @@ -1542,10 +1728,16 @@ def matrix_post_adaptation_non_linear_response_compression(P_2, a, b): a = as_float_array(a) b = as_float_array(b) - R_a = (460 * P_2 + 451 * a + 288 * b) / 1403 - G_a = (460 * P_2 - 891 * a - 261 * b) / 1403 - B_a = (460 * P_2 - 220 * a - 6300 * b) / 1403 - - RGB_a = tstack([R_a, G_a, B_a]) + RGB_a = ( + vector_dot( + [ + [460, 451, 288], + [460, -891, -261], + [460, -220, -6300], + ], + tstack([P_2, a, b]), + ) + / 1403 + ) return RGB_a diff --git a/colour/appearance/hke.py b/colour/appearance/hke.py new file mode 100644 index 0000000000..46a739ecc3 --- /dev/null +++ b/colour/appearance/hke.py @@ -0,0 +1,283 @@ +""" +Helmholtz—Kohlrausch Effect +=========================== + +Defines the following methods for estimating Helmholtz-Kohlrausch effect (HKE): + +- :attr:`colour.HKE_NAYATANI1997_METHODS`: Nayatani HKE computation methods, + choice between variable achromatic colour ('VAC') and variable chromatic + colour ('VCC'). +- :func:`colour.HelmholtzKohlrausch_effect_object_Nayatani1997`: + *Nayatani (1997)* HKE estimation for object colours. +- :func:`colour.HelmholtzKohlrausch_effect_luminous_Nayatani1997`: + *Nayatani (1997)* HKE estimation for luminous colours. +- :func:`colour.appearance.coefficient_q_Nayatani1997`: + Calculates :math:`WI` coefficient for *Nayatani 1997* HKE estimation. +- :func:`colour.appearance.coefficient_K_Br_Nayatani1997`: + Calculates :math:`K_{Br}` coefficient for *Nayatani 1997* HKE estimation. + +References +---------- +- :cite:`Nayatani1997` : Nayatani, Y. (1997). Simple estimation methods for + the Helmholtz—Kohlrausch effect. Color Research & Application, 22(6), + 385-401. doi:10.1002/(SICI)1520-6378(199712)22:6<385::AID-COL6>3.0.CO;2-R +""" + +from __future__ import annotations + +import numpy as np + +from colour.algebra import spow +from colour.hints import ( + ArrayLike, + FloatingOrArrayLike, + FloatingOrNDArray, + Literal, + Union, +) +from colour.utilities import ( + CaseInsensitiveMapping, + as_float_array, + tsplit, + validate_method, +) + +__author__ = "Ilia Sibiryakov" +__copyright__ = "Copyright 2013 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__ = [ + "HKE_NAYATANI1997_METHODS", + "HelmholtzKohlrausch_effect_object_Nayatani1997", + "HelmholtzKohlrausch_effect_luminous_Nayatani1997", + "coefficient_q_Nayatani1997", + "coefficient_K_Br_Nayatani1997", +] + +HKE_NAYATANI1997_METHODS = CaseInsensitiveMapping( + { + "VAC": -0.1340, + "VCC": -0.8660, + } +) +HKE_NAYATANI1997_METHODS.__doc__ = """ +*Nayatani (1997)* *HKE* computation methods, choice between variable achromatic +colour ('VAC') and variable chromatic colour ('VCC') + +References +---------- +:cite:`Nayatani1997` +""" + + +def HelmholtzKohlrausch_effect_object_Nayatani1997( + uv: ArrayLike, + uv_c: ArrayLike, + L_a: FloatingOrArrayLike, + method: Union[Literal["VAC", "VCC"], str] = "VCC", +) -> FloatingOrNDArray: + """ + Return the *HKE* value for object colours using *Nayatani (1997)* method. + + Parameters + ---------- + uv + *CIE uv* chromaticity coordinates of samples. + uv_c + *CIE uv* chromaticity coordinates of reference white. + L_a + Adapting luminance in :math:`cd/m^2`. + method + Which estimation method to use, *VCC* or *VAC*. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + Luminance factor (:math:`\\Gamma`) value(s) computed with Nayatani + object colour estimation method. + + References + ---------- + :cite:`Nayatani1997` + + Examples + -------- + >>> import colour + >>> white = colour.xy_to_Luv_uv(colour.temperature.CCT_to_xy_CIE_D(6504)) + >>> colours = colour.XYZ_to_xy( + ... [colour.wavelength_to_XYZ(430 + i * 50) for i in range(5)]) + >>> L_adapting = 65 + >>> HelmholtzKohlrausch_effect_object_Nayatani1997( # doctest: +ELLIPSIS + ... colour.xy_to_Luv_uv(colours), white, L_adapting) + array([ 2.2468383..., 1.4619799..., 1.1801658..., 0.9031318..., \ +1.7999376...]) + """ + + u, v = tsplit(uv) + u_c, v_c = tsplit(uv_c) + + method = validate_method(method, HKE_NAYATANI1997_METHODS) + + K_Br = coefficient_K_Br_Nayatani1997(L_a) + q = coefficient_q_Nayatani1997(np.arctan2(v - v_c, u - u_c)) + S_uv = 13 * np.sqrt((u - u_c) ** 2 + (v - v_c) ** 2) + + return 1 + (HKE_NAYATANI1997_METHODS[method] * q + 0.0872 * K_Br) * S_uv + + +def HelmholtzKohlrausch_effect_luminous_Nayatani1997( + uv: ArrayLike, + uv_c: ArrayLike, + L_a: FloatingOrArrayLike, + method: Union[Literal["VAC", "VCC"], str] = "VCC", +) -> FloatingOrNDArray: + """ + Return the *HKE* factor for luminous colours using *Nayatani (1997)* method. + + Parameters + ---------- + uv + *CIE uv* chromaticity coordinates of samples. + uv_c + *CIE uv* chromaticity coordinates of reference white. + L_a + Adapting luminance in :math:`cd/m^2`. + method + Which estimation method to use, *VCC* or *VAC*. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + Luminance factor (:math:`\\Gamma`) value(s) computed with Nayatani + luminous colour estimation method. + + References + ---------- + :cite:`Nayatani1997` + + Examples + -------- + >>> import colour + >>> white = colour.xy_to_Luv_uv(colour.temperature.CCT_to_xy_CIE_D(6504)) + >>> colours = colour.XYZ_to_xy( + ... [colour.wavelength_to_XYZ(430 + i * 50) for i in range(5)]) + >>> L_adapting = 65 + >>> HelmholtzKohlrausch_effect_luminous_Nayatani1997( # doctest: +ELLIPSIS + ... colour.xy_to_Luv_uv(colours), white, L_adapting) + array([ 7.4460471..., 2.4767159..., 1.4723422..., 0.7938695..., \ +4.1828629...]) + """ + + return ( + 0.4462 + * ( + HelmholtzKohlrausch_effect_object_Nayatani1997( + uv, uv_c, L_a, method + ) + + 0.3086 + ) + ** 3 + ) + + +def coefficient_q_Nayatani1997( + theta: FloatingOrArrayLike, +) -> FloatingOrNDArray: + """ + Return the :math:`q(\\theta)` coefficient for *Nayatani (1997)* *HKE* + computations. + + The hue angle :math:`\\theta` can be computed as follows: + + :math:`tan^{-1}\\cfrac{v' - v'_c}{u' - u'_c}` + + where :math:`u'` and :math:`v'` are the CIE 1976 chromaticity coordinates + of the test chromatic light and :math:`u'_c` and :math:`v'_c` are the CIE + 1976 chromaticity coordinates of the reference white light. + + Parameters + ---------- + theta + Hue angle (:math:`\\theta`) in radians. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + :math:`q` coefficient for *Nayatani (1997)* *HKE* methods. + + References + ---------- + :cite:`Nayatani1997` + + Examples + -------- + This recreates *FIG. A-1*. + + >>> import matplotlib.pyplot as plt + >>> angles = [(np.pi * 2 / 100 * i) for i in range(100)] + >>> q_values = coefficient_q_Nayatani1997(angles) + >>> plt.plot(np.array(angles), q_values / (np.pi * 2) * 180) + ... # doctest: +ELLIPSIS + [] + >>> plt.show() # doctest: +SKIP + """ + + theta = as_float_array(theta) + + theta_2, theta_3, theta_4 = 2 * theta, 3 * theta, 4 * theta + + return ( + -0.01585 + - 0.03017 * np.cos(theta) + - 0.04556 * np.cos(theta_2) + - 0.02667 * np.cos(theta_3) + - 0.00295 * np.cos(theta_4) + + 0.14592 * np.sin(theta) + + 0.05084 * np.sin(theta_2) + - 0.01900 * np.sin(theta_3) + - 0.00764 * np.sin(theta_4) + ) + + +def coefficient_K_Br_Nayatani1997( + L_a: FloatingOrArrayLike, +) -> FloatingOrNDArray: + """ + Return the :math:`K_{Br}` coefficient for *Nayatani (1997)* *HKE* + computations. + + Parameters + ---------- + L_a + Adapting luminance in :math:`cd/m^2`. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + :math:`K_{Br}` coefficient for *Nayatani (1997)* *HKE* methods. + + Notes + ----- + - The :math:`K_{Br}` coefficient is normalised to unity around + :math:`63.66cd/m^2`. + + References + ---------- + :cite:`Nayatani1997` + + Examples + -------- + >>> L_a_values = [10 + i * 20 for i in range(5)] + >>> coefficient_K_Br_Nayatani1997(L_a_values) # doctest: +ELLIPSIS + array([ 0.7134481..., 0.8781172..., 0.9606248..., 1.0156689..., \ +1.0567008...]) + >>> coefficient_K_Br_Nayatani1997(63.66) # doctest: +ELLIPSIS + 1.0001284... + """ + + L_a_4495 = spow(L_a, 0.4495) + + return 0.2717 * (6.469 + 6.362 * L_a_4495) / (6.469 + L_a_4495) diff --git a/colour/appearance/hunt.py b/colour/appearance/hunt.py index 27af95905b..1336ee95b3 100644 --- a/colour/appearance/hunt.py +++ b/colour/appearance/hunt.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Hunt Colour Appearance Model ============================ -Defines *Hunt* colour appearance model objects: +Defines the *Hunt* colour appearance model objects: - :class:`colour.appearance.InductionFactors_Hunt` - :attr:`colour.VIEWING_CONDITIONS_HUNT` @@ -18,54 +17,93 @@ ed.). John Wiley & Sons, Ltd. doi:10.1002/0470024275 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from collections import namedtuple - -from colour.algebra import spow +from dataclasses import dataclass, field + +from colour.algebra import spow, vector_dot +from colour.hints import ( + ArrayLike, + Boolean, + Dict, + FloatingOrArrayLike, + FloatingOrNDArray, + NDArray, + Optional, + cast, +) from colour.utilities import ( - CaseInsensitiveMapping, as_float_array, vector_dot, from_range_degrees, - ones, to_domain_100, tsplit, tstack, usage_warning, zeros) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + CaseInsensitiveMapping, + MixinDataclassArithmetic, + as_float, + as_float_array, + from_range_degrees, + ones, + to_domain_100, + tsplit, + tstack, + usage_warning, + zeros, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'InductionFactors_Hunt', 'VIEWING_CONDITIONS_HUNT', - 'HUE_DATA_FOR_HUE_QUADRATURE', 'MATRIX_XYZ_TO_HPE', 'MATRIX_HPE_TO_XYZ', - 'CAM_ReferenceSpecification_Hunt', 'CAM_Specification_Hunt', 'XYZ_to_Hunt', - 'luminance_level_adaptation_factor', 'illuminant_scotopic_luminance', - 'XYZ_to_rgb', 'f_n', 'chromatic_adaptation', - 'adjusted_reference_white_signals', 'achromatic_post_adaptation_signal', - 'colour_difference_signals', 'hue_angle', 'eccentricity_factor', - 'low_luminance_tritanopia_factor', 'yellowness_blueness_response', - 'redness_greenness_response', 'overall_chromatic_response', - 'saturation_correlate', 'achromatic_signal', 'brightness_correlate', - 'lightness_correlate', 'chroma_correlate', 'colourfulness_correlate' + "InductionFactors_Hunt", + "VIEWING_CONDITIONS_HUNT", + "HUE_DATA_FOR_HUE_QUADRATURE", + "MATRIX_XYZ_TO_HPE", + "MATRIX_HPE_TO_XYZ", + "CAM_ReferenceSpecification_Hunt", + "CAM_Specification_Hunt", + "XYZ_to_Hunt", + "luminance_level_adaptation_factor", + "illuminant_scotopic_luminance", + "XYZ_to_rgb", + "f_n", + "chromatic_adaptation", + "adjusted_reference_white_signals", + "achromatic_post_adaptation_signal", + "colour_difference_signals", + "hue_angle", + "eccentricity_factor", + "low_luminance_tritanopia_factor", + "yellowness_blueness_response", + "redness_greenness_response", + "overall_chromatic_response", + "saturation_correlate", + "achromatic_signal", + "brightness_correlate", + "lightness_correlate", + "chroma_correlate", + "colourfulness_correlate", ] class InductionFactors_Hunt( - namedtuple('InductionFactors_Hunt', ('N_c', 'N_b', 'N_cb', 'N_bb'))): + namedtuple("InductionFactors_Hunt", ("N_c", "N_b", "N_cb", "N_bb")) +): """ *Hunt* colour appearance model induction factors. Parameters ---------- - N_c : numeric or array_like + N_c Chromatic surround induction factor :math:`N_c`. - N_b : numeric or array_like + N_b *Brightness* surround induction factor :math:`N_b`. - N_cb : numeric or array_like, optional + N_cb Chromatic background induction factor :math:`N_{cb}`, approximated using tristimulus values :math:`Y_w` and :math:`Y_b` of respectively the reference white and the background if not specified. - N_bb : numeric or array_like, optional + N_bb *Brightness* background induction factor :math:`N_{bb}`, approximated using tristimulus values :math:`Y_w` and :math:`Y_b` of respectively the reference white and the background if not specified. @@ -77,26 +115,26 @@ class InductionFactors_Hunt( def __new__(cls, N_c, N_b, N_cb=None, N_bb=None): """ - Returns a new instance of the + Return a new instance of the :class:`colour.appearance.InductionFactors_Hunt` class. """ - return super(InductionFactors_Hunt, cls).__new__( - cls, N_c, N_b, N_cb, N_bb) - - -VIEWING_CONDITIONS_HUNT = CaseInsensitiveMapping({ - 'Small Areas, Uniform Background & Surrounds': - InductionFactors_Hunt(1, 300), - 'Normal Scenes': - InductionFactors_Hunt(1, 75), - 'Television & CRT, Dim Surrounds': - InductionFactors_Hunt(1, 25), - 'Large Transparencies On Light Boxes': - InductionFactors_Hunt(0.7, 25), - 'Projected Transparencies, Dark Surrounds': - InductionFactors_Hunt(0.7, 10) -}) + return super().__new__(cls, N_c, N_b, N_cb, N_bb) + + +VIEWING_CONDITIONS_HUNT: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Small Areas, Uniform Background & Surrounds": InductionFactors_Hunt( + 1, 300 + ), + "Normal Scenes": InductionFactors_Hunt(1, 75), + "Television & CRT, Dim Surrounds": InductionFactors_Hunt(1, 25), + "Large Transparencies On Light Boxes": InductionFactors_Hunt(0.7, 25), + "Projected Transparencies, Dark Surrounds": InductionFactors_Hunt( + 0.7, 10 + ), + } +) VIEWING_CONDITIONS_HUNT.__doc__ = """ Reference *Hunt* colour appearance model viewing conditions. @@ -104,13 +142,6 @@ def __new__(cls, N_c, N_b, N_cb=None, N_bb=None): ---------- :cite:`Fairchild2013u`, :cite:`Hunt2004b` -VIEWING_CONDITIONS_HUNT : CaseInsensitiveMapping - **{'Small Areas, Uniform Background & Surrounds', - 'Normal Scenes', - 'Television & CRT, Dim Surrounds', - 'Large Transparencies On Light Boxes', - 'Projected Transparencies, Dark Surrounds'}** - Aliases: - 'small_uniform': 'Small Areas, Uniform Background & Surrounds' @@ -120,68 +151,69 @@ def __new__(cls, N_c, N_b, N_cb=None, N_bb=None): - 'projected_dark': 'Projected Transparencies, Dark Surrounds' """ -VIEWING_CONDITIONS_HUNT['small_uniform'] = ( - VIEWING_CONDITIONS_HUNT['Small Areas, Uniform Background & Surrounds']) -VIEWING_CONDITIONS_HUNT['normal'] = (VIEWING_CONDITIONS_HUNT['Normal Scenes']) -VIEWING_CONDITIONS_HUNT['tv_dim'] = ( - VIEWING_CONDITIONS_HUNT['Television & CRT, Dim Surrounds']) -VIEWING_CONDITIONS_HUNT['light_boxes'] = ( - VIEWING_CONDITIONS_HUNT['Large Transparencies On Light Boxes']) -VIEWING_CONDITIONS_HUNT['projected_dark'] = ( - VIEWING_CONDITIONS_HUNT['Projected Transparencies, Dark Surrounds']) - -HUE_DATA_FOR_HUE_QUADRATURE = { - 'h_s': np.array([20.14, 90.00, 164.25, 237.53]), - 'e_s': np.array([0.8, 0.7, 1.0, 1.2]) +VIEWING_CONDITIONS_HUNT["small_uniform"] = VIEWING_CONDITIONS_HUNT[ + "Small Areas, Uniform Background & Surrounds" +] +VIEWING_CONDITIONS_HUNT["normal"] = VIEWING_CONDITIONS_HUNT["Normal Scenes"] +VIEWING_CONDITIONS_HUNT["tv_dim"] = VIEWING_CONDITIONS_HUNT[ + "Television & CRT, Dim Surrounds" +] +VIEWING_CONDITIONS_HUNT["light_boxes"] = VIEWING_CONDITIONS_HUNT[ + "Large Transparencies On Light Boxes" +] +VIEWING_CONDITIONS_HUNT["projected_dark"] = VIEWING_CONDITIONS_HUNT[ + "Projected Transparencies, Dark Surrounds" +] + +HUE_DATA_FOR_HUE_QUADRATURE: Dict = { + "h_s": np.array([20.14, 90.00, 164.25, 237.53]), + "e_s": np.array([0.8, 0.7, 1.0, 1.2]), } -MATRIX_XYZ_TO_HPE = np.array([ - [0.38971, 0.68898, -0.07868], - [-0.22981, 1.18340, 0.04641], - [0.00000, 0.00000, 1.00000], -]) +MATRIX_XYZ_TO_HPE: NDArray = np.array( + [ + [0.38971, 0.68898, -0.07868], + [-0.22981, 1.18340, 0.04641], + [0.00000, 0.00000, 1.00000], + ] +) """ *Hunt* colour appearance model *CIE XYZ* tristimulus values to *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace matrix. - -MATRIX_XYZ_TO_HPE : array_like, (3, 3) """ -MATRIX_HPE_TO_XYZ = np.linalg.inv(MATRIX_XYZ_TO_HPE) +MATRIX_HPE_TO_XYZ: NDArray = np.linalg.inv(MATRIX_XYZ_TO_HPE) """ *Hunt* colour appearance model *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_HPE_TO_XYZ : array_like, (3, 3) """ -class CAM_ReferenceSpecification_Hunt( - namedtuple('CAM_ReferenceSpecification_Hunt', - ('J', 'C_94', 'h_S', 's', 'Q', 'M_94', 'H', 'H_C'))): +@dataclass +class CAM_ReferenceSpecification_Hunt(MixinDataclassArithmetic): """ - Defines the *Hunt* colour appearance model reference specification. + Define the *Hunt* colour appearance model reference specification. This specification has field names consistent with *Fairchild (2013)* reference. Parameters ---------- - J : numeric or array_like + J Correlate of *Lightness* :math:`J`. - C_94 : numeric or array_like + C_94 Correlate of *chroma* :math:`C_94`. - h_S : numeric or array_like + h_S *Hue* angle :math:`h_S` in degrees. - s : numeric or array_like + s Correlate of *saturation* :math:`s`. - Q : numeric or array_like + Q Correlate of *brightness* :math:`Q`. - M_94 : numeric or array_like + M_94 Correlate of *colourfulness* :math:`M_94`. - H : numeric or array_like + H *Hue* :math:`h` quadrature :math:`H`. - H_C : numeric or array_like + H_C *Hue* :math:`h` composition :math:`H_C`. References @@ -189,12 +221,20 @@ class CAM_ReferenceSpecification_Hunt( :cite:`Fairchild2013u`, :cite:`Hunt2004b` """ + J: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + C_94: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + h_S: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + s: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + Q: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + M_94: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + H: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + H_C: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + -class CAM_Specification_Hunt( - namedtuple('CAM_Specification_Hunt', - ('J', 'C', 'h', 's', 'Q', 'M', 'H', 'HC'))): +@dataclass +class CAM_Specification_Hunt(MixinDataclassArithmetic): """ - Defines the *Hunt* colour appearance model specification. + Define the *Hunt* colour appearance model specification. This specification has field names consistent with the remaining colour appearance models in :mod:`colour.appearance` but diverge from @@ -202,21 +242,21 @@ class CAM_Specification_Hunt( Parameters ---------- - J : numeric or array_like + J Correlate of *Lightness* :math:`J`. - C : numeric or array_like + C Correlate of *chroma* :math:`C_94`. - h : numeric or array_like + h *Hue* angle :math:`h_S` in degrees. - s : numeric or array_like + s Correlate of *saturation* :math:`s`. - Q : numeric or array_like + Q Correlate of *brightness* :math:`Q`. - M : numeric or array_like + M Correlate of *colourfulness* :math:`M_94`. - H : numeric or array_like + H *Hue* :math:`h` quadrature :math:`H`. - HC : numeric or array_like + HC *Hue* :math:`h` composition :math:`H_C`. Notes @@ -228,85 +268,95 @@ class CAM_Specification_Hunt( :cite:`Fairchild2013u`, :cite:`Hunt2004b` """ - -def XYZ_to_Hunt(XYZ, - XYZ_w, - XYZ_b, - L_A, - surround=VIEWING_CONDITIONS_HUNT['Normal Scenes'], - L_AS=None, - CCT_w=None, - XYZ_p=None, - p=None, - S=None, - S_w=None, - helson_judd_effect=False, - discount_illuminant=True): + J: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + C: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + h: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + s: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + Q: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + M: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + H: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + HC: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + + +def XYZ_to_Hunt( + XYZ: ArrayLike, + XYZ_w: ArrayLike, + XYZ_b: ArrayLike, + L_A: FloatingOrArrayLike, + surround: InductionFactors_Hunt = VIEWING_CONDITIONS_HUNT["Normal Scenes"], + L_AS: Optional[FloatingOrArrayLike] = None, + CCT_w: Optional[FloatingOrArrayLike] = None, + XYZ_p: Optional[ArrayLike] = None, + p: Optional[FloatingOrArrayLike] = None, + S: Optional[FloatingOrArrayLike] = None, + S_w: Optional[FloatingOrArrayLike] = None, + helson_judd_effect: Boolean = False, + discount_illuminant: Boolean = True, +) -> CAM_Specification_Hunt: """ - Computes the *Hunt* colour appearance model correlates. + Compute the *Hunt* colour appearance model correlates. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values of test sample / stimulus. - XYZ_w : array_like + XYZ_w *CIE XYZ* tristimulus values of reference white. - XYZ_b : array_like + XYZ_b *CIE XYZ* tristimulus values of background. - L_A : numeric or array_like + L_A Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`. - surround : InductionFactors_Hunt, optional + surround Surround viewing conditions induction factors. - L_AS : numeric or array_like, optional + L_AS Scotopic luminance :math:`L_{AS}` of the illuminant, approximated if not specified. - CCT_w : numeric or array_like, optional + CCT_w Correlated color temperature :math:`T_{cp}`: of the illuminant, needed to approximate :math:`L_{AS}`. - XYZ_p : array_like, optional + XYZ_p *CIE XYZ* tristimulus values of proximal field, assumed to be equal to background if not specified. - p : numeric or array_like, optional + p Simultaneous contrast / assimilation factor :math:`p` with value normalised to domain [-1, 0] when simultaneous contrast occurs and normalised to domain [0, 1] when assimilation occurs. - S : numeric or array_like, optional + S Scotopic response :math:`S` to the stimulus, approximated using tristimulus values :math:`Y` of the stimulus if not specified. - S_w : numeric or array_like, optional + S_w Scotopic response :math:`S_w` for the reference white, approximated using the tristimulus values :math:`Y_w` of the reference white if not specified. - helson_judd_effect : bool, optional + helson_judd_effect Truth value indicating whether the *Helson-Judd* effect should be accounted for. - discount_illuminant : bool, optional + discount_illuminant Truth value indicating if the illuminant should be discounted. Returns ------- - CAM_Specification_Hunt + :class:`colour.CAM_Specification_Hunt` *Hunt* colour appearance model specification. Raises ------ ValueError - If an illegal arguments combination is specified. + If an illegal argument combination is specified. Notes ----- - - +--------------------------+-----------------------+---------------+ - | **Domain** | **Scale - Reference** | **Scale - 1** | - +==========================+=======================+===============+ - | ``XYZ`` | [0, 100] | [0, 1] | - +--------------------------+-----------------------+---------------+ - | ``XYZ_w`` | [0, 100] | [0, 1] | - +--------------------------+-----------------------+---------------+ - | ``XYZ_b`` | [0, 100] | [0, 1] | - +--------------------------+-----------------------+---------------+ - | ``XYZ_p`` | [0, 100] | [0, 1] | - +--------------------------+-----------------------+---------------+ + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``XYZ`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ + | ``XYZ_w`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ + | ``XYZ_b`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ + | ``XYZ_p`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ +------------------------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | @@ -331,6 +381,7 @@ def XYZ_to_Hunt(XYZ, CAM_Specification_Hunt(J=30.0462678..., C=0.1210508..., h=269.2737594..., \ s=0.0199093..., Q=22.2097654..., M=0.1238964..., H=None, HC=None) """ + XYZ = to_domain_100(XYZ) XYZ_w = to_domain_100(XYZ_w) XYZ_b = to_domain_100(XYZ_b) @@ -345,45 +396,56 @@ def XYZ_to_Hunt(XYZ, X_p = X_b Y_p = Y_b Z_p = Y_b - usage_warning('Unspecified proximal field "XYZ_p" argument, using ' - 'background "XYZ_b" as approximation!') + usage_warning( + 'Unspecified proximal field "XYZ_p" argument, using ' + 'background "XYZ_b" as approximation!' + ) if surround.N_cb is None: N_cb = 0.725 * spow(Y_w / Y_b, 0.2) - usage_warning('Unspecified "N_cb" argument, using approximation: ' - '"{0}"'.format(N_cb)) + usage_warning( + f'Unspecified "N_cb" argument, using approximation: "{N_cb}"' + ) if surround.N_bb is None: N_bb = 0.725 * spow(Y_w / Y_b, 0.2) - usage_warning('Unspecified "N_bb" argument, using approximation: ' - '"{0}"'.format(N_bb)) + usage_warning( + f'Unspecified "N_bb" argument, using approximation: "{N_bb}"' + ) if L_AS is None and CCT_w is None: - raise ValueError('Either the scotopic luminance "L_AS" of the ' - 'illuminant or its correlated colour temperature ' - '"CCT_w" must be specified!') - if L_AS is None: + raise ValueError( + 'Either the scotopic luminance "L_AS" of the ' + "illuminant or its correlated colour temperature " + '"CCT_w" must be specified!' + ) + elif L_AS is None and CCT_w is not None: L_AS = illuminant_scotopic_luminance(L_A, CCT_w) usage_warning( - 'Unspecified "L_AS" argument, using approximation from "CCT": ' - '"{0}"'.format(L_AS)) + f'Unspecified "L_AS" argument, using approximation from "CCT": ' + f'"{L_AS}"' + ) if (S is None and S_w is not None) or (S is not None and S_w is None): - raise ValueError('Either both stimulus scotopic response "S" and ' - 'reference white scotopic response "S_w" arguments ' - 'need to be specified or none of them!') + raise ValueError( + 'Either both stimulus scotopic response "S" and ' + 'reference white scotopic response "S_w" arguments ' + "need to be specified or none of them!" + ) elif S is None and S_w is None: - S = Y - S_w = Y_w + S_p = Y + S_w_p = Y_w usage_warning( - 'Unspecified stimulus scotopic response "S" and reference ' - 'white scotopic response "S_w" arguments, using ' - 'approximation: "{0}", "{1}"'.format(S, S_w)) + f'Unspecified stimulus scotopic response "S" and reference white ' + f'scotopic response "S_w" arguments, using approximation: ' + f'"{S}", "{S_w}"' + ) if p is None: usage_warning( 'Unspecified simultaneous contrast / assimilation "p" ' - 'argument, model will not account for simultaneous chromatic ' - 'contrast!') + "argument, model will not account for simultaneous chromatic " + "contrast!" + ) XYZ_p = tstack([X_p, Y_p, Z_p]) @@ -391,12 +453,30 @@ def XYZ_to_Hunt(XYZ, F_L = luminance_level_adaptation_factor(L_A) # Computing test sample chromatic adaptation. - rgb_a = chromatic_adaptation(XYZ, XYZ_w, XYZ_b, L_A, F_L, XYZ_p, p, - helson_judd_effect, discount_illuminant) + rgb_a = chromatic_adaptation( + XYZ, + XYZ_w, + XYZ_b, + L_A, + F_L, + XYZ_p, + p, + helson_judd_effect, + discount_illuminant, + ) # Computing reference white chromatic adaptation. - rgb_aw = chromatic_adaptation(XYZ_w, XYZ_w, XYZ_b, L_A, F_L, XYZ_p, p, - helson_judd_effect, discount_illuminant) + rgb_aw = chromatic_adaptation( + XYZ_w, + XYZ_w, + XYZ_b, + L_A, + F_L, + XYZ_p, + p, + helson_judd_effect, + discount_illuminant, + ) # Computing opponent colour dimensions. # Computing achromatic post adaptation signals. @@ -438,8 +518,10 @@ def XYZ_to_Hunt(XYZ, # Computing the correlate of *brightness* :math:`Q`. # ------------------------------------------------------------------------- # Computing achromatic signal :math:`A`. - A = achromatic_signal(L_AS, S, S_w, N_bb, A_a) - A_w = achromatic_signal(L_AS, S_w, S_w, N_bb, A_aw) + A = achromatic_signal(cast(FloatingOrNDArray, L_AS), S_p, S_w_p, N_bb, A_a) + A_w = achromatic_signal( + cast(FloatingOrNDArray, L_AS), S_w_p, S_w_p, N_bb, A_aw + ) Q = brightness_correlate(A, A_w, M, surround.N_b) brightness_w = brightness_correlate(A_w, A_w, M_w, surround.N_b) @@ -460,22 +542,32 @@ def XYZ_to_Hunt(XYZ, # ------------------------------------------------------------------------- M_94 = colourfulness_correlate(F_L, C_94) - return CAM_Specification_Hunt(J, C_94, from_range_degrees(h), s, Q, M_94, - None, None) - - -def luminance_level_adaptation_factor(L_A): + return CAM_Specification_Hunt( + J, + C_94, + as_float(from_range_degrees(h)), + s, + Q, + M_94, + None, + None, + ) + + +def luminance_level_adaptation_factor( + L_A: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Returns the *luminance* level adaptation factor :math:`F_L`. + Return the *luminance* level adaptation factor :math:`F_L`. Parameters ---------- - L_A : numeric or array_like + L_A Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Luminance* level adaptation factor :math:`F_L` Examples @@ -487,27 +579,29 @@ def luminance_level_adaptation_factor(L_A): L_A = as_float_array(L_A) k = 1 / (5 * L_A + 1) - k4 = k ** 4 + k4 = k**4 F_L = 0.2 * k4 * (5 * L_A) + 0.1 * (1 - k4) ** 2 * spow(5 * L_A, 1 / 3) - return F_L + return as_float(F_L) -def illuminant_scotopic_luminance(L_A, CCT): +def illuminant_scotopic_luminance( + L_A: FloatingOrArrayLike, CCT: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the approximate scotopic luminance :math:`L_{AS}` of the + Return the approximate scotopic luminance :math:`L_{AS}` of the illuminant. Parameters ---------- - L_A : numeric or array_like + L_A Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`. - CCT : numeric or array_like + CCT Correlated color temperature :math:`T_{cp}` of the illuminant. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Approximate scotopic luminance :math:`L_{AS}`. Examples @@ -521,22 +615,22 @@ def illuminant_scotopic_luminance(L_A, CCT): CCT = 2.26 * L_A * spow((CCT / 4000) - 0.4, 1 / 3) - return CCT + return as_float(CCT) -def XYZ_to_rgb(XYZ): +def XYZ_to_rgb(XYZ: ArrayLike) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to *Hunt-Pointer-Estevez* + Convert from *CIE XYZ* tristimulus values to *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. Returns ------- - ndarray + :class:`numpy.ndarray` *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace. Examples @@ -549,19 +643,19 @@ def XYZ_to_rgb(XYZ): return vector_dot(MATRIX_XYZ_TO_HPE, XYZ) -def f_n(x): +def f_n(x: ArrayLike) -> NDArray: """ - Defines the nonlinear response function of the *Hunt* colour appearance + Define the nonlinear response function of the *Hunt* colour appearance model used to model the nonlinear behaviour of various visual responses. Parameters ---------- - x : numeric or array_like or array_like + x Visual response variable :math:`x`. Returns ------- - numeric or array_like + :class:`numpy.ndarray` Modeled visual response variable :math:`x`. @@ -572,54 +666,54 @@ def f_n(x): array([ 5.8968592..., 5.8969521..., 5.8975927...]) """ - x = as_float_array(x) - x_p = spow(x, 0.73) x_m = 40 * (x_p / (x_p + 2)) - return x_m + return as_float_array(x_m) -def chromatic_adaptation(XYZ, - XYZ_w, - XYZ_b, - L_A, - F_L, - XYZ_p=None, - p=None, - helson_judd_effect=False, - discount_illuminant=True): +def chromatic_adaptation( + XYZ: ArrayLike, + XYZ_w: ArrayLike, + XYZ_b: ArrayLike, + L_A, + F_L, + XYZ_p: ArrayLike = None, + p: Optional[FloatingOrArrayLike] = None, + helson_judd_effect: Boolean = False, + discount_illuminant: Boolean = True, +) -> NDArray: """ - Applies chromatic adaptation to given *CIE XYZ* tristimulus values. + Apply chromatic adaptation to given *CIE XYZ* tristimulus values. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values of test sample. - XYZ_b : array_like + XYZ_b *CIE XYZ* tristimulus values of background. - XYZ_w : array_like + XYZ_w *CIE XYZ* tristimulus values of reference white. - L_A : numeric or array_like + L_A Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`. - F_L : numeric or array_like + F_L Luminance adaptation factor :math:`F_L`. - XYZ_p : array_like, optional + XYZ_p *CIE XYZ* tristimulus values of proximal field, assumed to be equal to background if not specified. - p : numeric or array_like, optional + p Simultaneous contrast / assimilation factor :math:`p` with value normalised to domain [-1, 0] when simultaneous contrast occurs and normalised to domain [0, 1] when assimilation occurs. - helson_judd_effect : bool, optional + helson_judd_effect Truth value indicating whether the *Helson-Judd* effect should be accounted for. - discount_illuminant : bool, optional + discount_illuminant Truth value indicating if the illuminant should be discounted. Returns ------- - ndarray + :class:`numpy.ndarray` Adapted *CIE XYZ* tristimulus values. Examples @@ -660,19 +754,20 @@ def chromatic_adaptation(XYZ, # Computing chromatic adaptation factors. if not discount_illuminant: L_A_p = spow(L_A, 1 / 3) - F_rgb = ((1 + L_A_p + h_rgb) / (1 + L_A_p + (1 / h_rgb))) + F_rgb = (1 + L_A_p + h_rgb) / (1 + L_A_p + (1 / h_rgb)) else: F_rgb = ones(h_rgb.shape) # Computing Helson-Judd effect parameters. if helson_judd_effect: - D_rgb = (f_n((Y_b / Y_w) * F_L * F_rgb[..., 1]) - f_n( - (Y_b / Y_w) * F_L * F_rgb)) + D_rgb = f_n((Y_b / Y_w) * F_L * F_rgb[..., 1]) - f_n( + (Y_b / Y_w) * F_L * F_rgb + ) else: D_rgb = zeros(F_rgb.shape) # Computing cone bleach factors. - B_rgb = (10 ** 7) / ((10 ** 7) + 5 * L_A[..., np.newaxis] * (rgb_w / 100)) + B_rgb = (10**7) / ((10**7) + 5 * L_A[..., np.newaxis] * (rgb_w / 100)) # Computing adjusted reference white signals. if XYZ_p is not None and p is not None: @@ -680,35 +775,41 @@ def chromatic_adaptation(XYZ, rgb_w = adjusted_reference_white_signals(rgb_p, B_rgb, rgb_w, p) # Computing adapted cone responses. - rgb_a = 1 - rgb_a += B_rgb * (f_n(F_L[..., np.newaxis] * F_rgb * rgb / rgb_w) + D_rgb) + rgb_a = 1 + B_rgb * ( + f_n(F_L[..., np.newaxis] * F_rgb * rgb / rgb_w) + D_rgb + ) return rgb_a -def adjusted_reference_white_signals(rgb_p, rgb_b, rgb_w, p): +def adjusted_reference_white_signals( + rgb_p: ArrayLike, + rgb_b: ArrayLike, + rgb_w: ArrayLike, + p: FloatingOrArrayLike, +) -> NDArray: """ - Adjusts the white point for simultaneous chromatic contrast. + Adjust the white point for simultaneous chromatic contrast. Parameters ---------- - rgb_p : array_like + rgb_p Cone signals *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array of the proximal field. - rgb_b : array_like + rgb_b Cone signals *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array of the background. - rgb_w : array_like + rgb_w Cone signals array *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array of the reference white. - p : numeric or array_like + p Simultaneous contrast / assimilation factor :math:`p` with value normalised to domain [-1, 0] when simultaneous contrast occurs and normalised to domain [0, 1] when assimilation occurs. Returns ------- - ndarray + :class:`numpy.ndarray` Adjusted cone signals *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array of the reference white. @@ -729,25 +830,28 @@ def adjusted_reference_white_signals(rgb_p, rgb_b, rgb_w, p): p = as_float_array(p) p_rgb = rgb_p / rgb_b - rgb_w = (rgb_w * (spow((1 - p) * p_rgb + (1 + p) / p_rgb, 0.5)) / (spow( - (1 + p) * p_rgb + (1 - p) / p_rgb, 0.5))) + rgb_w = ( + rgb_w + * (spow((1 - p) * p_rgb + (1 + p) / p_rgb, 0.5)) + / (spow((1 + p) * p_rgb + (1 - p) / p_rgb, 0.5)) + ) return rgb_w -def achromatic_post_adaptation_signal(rgb): +def achromatic_post_adaptation_signal(rgb: ArrayLike) -> NDArray: """ - Returns the achromatic post adaptation signal :math:`A` from given + Return the achromatic post adaptation signal :math:`A` from given *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array. Parameters ---------- - rgb : array_like + rgb *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Achromatic post adaptation signal :math:`A`. Examples @@ -764,20 +868,20 @@ def achromatic_post_adaptation_signal(rgb): return A -def colour_difference_signals(rgb): +def colour_difference_signals(rgb: ArrayLike) -> NDArray: """ - Returns the colour difference signals :math:`C_1`, :math:`C_2` and + Return the colour difference signals :math:`C_1`, :math:`C_2` and :math:`C_3` from given *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array. Parameters ---------- - rgb : array_like + rgb *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` Colour difference signals :math:`C_1`, :math:`C_2` and :math:`C_3`. Examples @@ -798,19 +902,19 @@ def colour_difference_signals(rgb): return C -def hue_angle(C): +def hue_angle(C: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the *hue* angle :math:`h` in degrees from given colour difference + Return the *hue* angle :math:`h` in degrees from given colour difference signals :math:`C`. Parameters ---------- - C : array_like + C Colour difference signals :math:`C`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Hue* angle :math:`h` in degrees. Examples @@ -826,57 +930,60 @@ def hue_angle(C): C_1, C_2, C_3 = tsplit(C) - hue = (180 * np.arctan2(0.5 * (C_2 - C_3) / 4.5, C_1 - - (C_2 / 11)) / np.pi) % 360 - return hue + hue = ( + 180 * np.arctan2(0.5 * (C_2 - C_3) / 4.5, C_1 - (C_2 / 11)) / np.pi + ) % 360 + return as_float(hue) -def eccentricity_factor(hue): +def eccentricity_factor(hue: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns eccentricity factor :math:`e_s` from given hue angle :math:`h` + Return eccentricity factor :math:`e_s` from given hue angle :math:`h` in degrees. Parameters ---------- - hue : numeric or array_like + hue Hue angle :math:`h` in degrees. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Eccentricity factor :math:`e_s`. Examples -------- >>> eccentricity_factor(269.273759) # doctest: +ELLIPSIS - array(1.1108365...) + 1.1108365... """ hue = as_float_array(hue) - h_s = HUE_DATA_FOR_HUE_QUADRATURE['h_s'] - e_s = HUE_DATA_FOR_HUE_QUADRATURE['e_s'] + h_s = HUE_DATA_FOR_HUE_QUADRATURE["h_s"] + e_s = HUE_DATA_FOR_HUE_QUADRATURE["e_s"] x = np.interp(hue, h_s, e_s) x = np.where(hue < 20.14, 0.856 - (hue / 20.14) * 0.056, x) x = np.where(hue > 237.53, 0.856 + 0.344 * (360 - hue) / (360 - 237.53), x) - return x + return as_float(x) -def low_luminance_tritanopia_factor(L_A): +def low_luminance_tritanopia_factor( + L_A: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Returns the low luminance tritanopia factor :math:`F_t` from given adapting + Return the low luminance tritanopia factor :math:`F_t` from given adapting field *luminance* :math:`L_A` in :math:`cd/m^2`. Parameters ---------- - L_A : numeric or array_like + L_A Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Low luminance tritanopia factor :math:`F_t`. Examples @@ -889,29 +996,35 @@ def low_luminance_tritanopia_factor(L_A): F_t = L_A / (L_A + 0.1) - return F_t + return as_float(F_t) -def yellowness_blueness_response(C, e_s, N_c, N_cb, F_t): +def yellowness_blueness_response( + C: FloatingOrArrayLike, + e_s: FloatingOrArrayLike, + N_c: FloatingOrArrayLike, + N_cb: FloatingOrArrayLike, + F_t: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Returns the yellowness / blueness response :math:`M_{yb}`. + Return the yellowness / blueness response :math:`M_{yb}`. Parameters ---------- - C : array_like + C Colour difference signals :math:`C`. - e_s : numeric or array_like + e_s Eccentricity factor :math:`e_s`. - N_c : numeric or array_like + N_c Chromatic surround induction factor :math:`N_c`. - N_cb : numeric or array_like + N_cb Chromatic background induction factor :math:`N_{cb}`. - F_t : numeric or array_like + F_t Low luminance tritanopia factor :math:`F_t`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Yellowness / blueness response :math:`M_{yb}`. Examples @@ -937,29 +1050,35 @@ def yellowness_blueness_response(C, e_s, N_c, N_cb, F_t): F_t = as_float_array(F_t) M_yb = ( - 100 * (0.5 * (C_2 - C_3) / 4.5) * (e_s * (10 / 13) * N_c * N_cb * F_t)) + 100 * (0.5 * (C_2 - C_3) / 4.5) * (e_s * (10 / 13) * N_c * N_cb * F_t) + ) - return M_yb + return as_float(M_yb) -def redness_greenness_response(C, e_s, N_c, N_cb): +def redness_greenness_response( + C: FloatingOrArrayLike, + e_s: FloatingOrArrayLike, + N_c: FloatingOrArrayLike, + N_cb: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Returns the redness / greenness response :math:`M_{yb}`. + Return the redness / greenness response :math:`M_{yb}`. Parameters ---------- - C : array_like + C Colour difference signals :math:`C`. - e_s : numeric or array_like + e_s Eccentricity factor :math:`e_s`. - N_c : numeric or array_like + N_c Chromatic surround induction factor :math:`N_c`. - N_cb : numeric or array_like + N_cb Chromatic background induction factor :math:`N_{cb}`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Redness / greenness response :math:`M_{rg}`. Examples @@ -983,23 +1102,25 @@ def redness_greenness_response(C, e_s, N_c, N_cb): M_rg = 100 * (C_1 - (C_2 / 11)) * (e_s * (10 / 13) * N_c * N_cb) - return M_rg + return as_float(M_rg) -def overall_chromatic_response(M_yb, M_rg): +def overall_chromatic_response( + M_yb: FloatingOrArrayLike, M_rg: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the overall chromatic response :math:`M`. + Return the overall chromatic response :math:`M`. Parameters ---------- - M_yb : numeric or array_like + M_yb Yellowness / blueness response :math:`M_{yb}`. - M_rg : numeric or array_like + M_rg Redness / greenness response :math:`M_{rg}`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Overall chromatic response :math:`M`. Examples @@ -1013,26 +1134,28 @@ def overall_chromatic_response(M_yb, M_rg): M_yb = as_float_array(M_yb) M_rg = as_float_array(M_rg) - M = spow((M_yb ** 2) + (M_rg ** 2), 0.5) + M = spow((M_yb**2) + (M_rg**2), 0.5) return M -def saturation_correlate(M, rgb_a): +def saturation_correlate( + M: FloatingOrArrayLike, rgb_a: ArrayLike +) -> FloatingOrNDArray: """ - Returns the *saturation* correlate :math:`s`. + Return the *saturation* correlate :math:`s`. Parameters ---------- - M : numeric or array_like + M Overall chromatic response :math:`M`. - rgb_a : array_like + rgb_a Adapted *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Saturation* correlate :math:`s`. Examples @@ -1048,29 +1171,35 @@ def saturation_correlate(M, rgb_a): s = 50 * M / np.sum(rgb_a, axis=-1) - return s + return as_float(s) -def achromatic_signal(L_AS, S, S_w, N_bb, A_a): +def achromatic_signal( + L_AS: FloatingOrArrayLike, + S: FloatingOrArrayLike, + S_w: FloatingOrArrayLike, + N_bb: FloatingOrArrayLike, + A_a: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Returns the achromatic signal :math:`A`. + Return the achromatic signal :math:`A`. Parameters ---------- - L_AS : numeric or array_like + L_AS Scotopic luminance :math:`L_{AS}` of the illuminant. - S : numeric or array_like + S Scotopic response :math:`S` to the stimulus. - S_w : numeric or array_like + S_w Scotopic response :math:`S_w` for the reference white. - N_bb : numeric or array_like + N_bb Brightness background induction factor :math:`N_{bb}`. - A_a: numeric or array_like + A_a Achromatic post adaptation signal of the stimulus :math:`A_a`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Achromatic signal :math:`A`. Examples @@ -1093,8 +1222,8 @@ def achromatic_signal(L_AS, S, S_w, N_bb, A_a): j = 0.00001 / ((5 * L_AS / 2.26) + 0.00001) # Computing scotopic luminance level adaptation factor :math:`F_{LS}`. - F_LS = 3800 * (j ** 2) * (5 * L_AS / 2.26) - F_LS += 0.2 * (spow(1 - (j ** 2), 0.4)) * (spow(5 * L_AS / 2.26, 1 / 6)) + F_LS = 3800 * (j**2) * (5 * L_AS / 2.26) + F_LS += 0.2 * (spow(1 - (j**2), 0.4)) * (spow(5 * L_AS / 2.26, 1 / 6)) # Computing cone bleach factors :math:`B_S`. B_S = 0.5 / (1 + 0.3 * spow((5 * L_AS / 2.26) * (S / S_w), 0.3)) @@ -1104,29 +1233,34 @@ def achromatic_signal(L_AS, S, S_w, N_bb, A_a): A_S = (f_n(F_LS * S / S_w) * 3.05 * B_S) + 0.3 # Computing achromatic signal :math:`A`. - A = N_bb * (A_a - 1 + A_S - 0.3 + np.sqrt((1 + (0.3 ** 2)))) + A = N_bb * (A_a - 1 + A_S - 0.3 + np.sqrt(1 + (0.3**2))) - return A + return as_float(A) -def brightness_correlate(A, A_w, M, N_b): +def brightness_correlate( + A: FloatingOrArrayLike, + A_w: FloatingOrArrayLike, + M: FloatingOrArrayLike, + N_b: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Returns the *brightness* correlate :math:`Q`. + Return the *brightness* correlate :math:`Q`. Parameters ---------- - A : numeric or array_like + A Achromatic signal :math:`A`. - A_w: numeric or array_like + A_w Achromatic post adaptation signal of the reference white :math:`A_w`. - M : numeric or array_like + M Overall chromatic response :math:`M`. - N_b : numeric or array_like + N_b Brightness surround induction factor :math:`N_b`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Brightness* correlate :math:`Q`. Examples @@ -1152,24 +1286,29 @@ def brightness_correlate(A, A_w, M, N_b): return Q -def lightness_correlate(Y_b, Y_w, Q, Q_w): +def lightness_correlate( + Y_b: FloatingOrArrayLike, + Y_w: FloatingOrArrayLike, + Q: FloatingOrArrayLike, + Q_w: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Returns the *Lightness* correlate :math:`J`. + Return the *Lightness* correlate :math:`J`. Parameters ---------- - Y_b : numeric or array_like + Y_b Tristimulus values :math:`Y_b` the background. - Y_w : numeric or array_like + Y_w Tristimulus values :math:`Y_b` the reference white. - Q : numeric or array_like + Q *Brightness* correlate :math:`Q` of the stimulus. - Q_w : numeric or array_like + Q_w *Brightness* correlate :math:`Q` of the reference white. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Lightness* correlate :math:`J`. Examples @@ -1193,26 +1332,32 @@ def lightness_correlate(Y_b, Y_w, Q, Q_w): return J -def chroma_correlate(s, Y_b, Y_w, Q, Q_w): +def chroma_correlate( + s: FloatingOrArrayLike, + Y_b: FloatingOrArrayLike, + Y_w: FloatingOrArrayLike, + Q: FloatingOrArrayLike, + Q_w: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Returns the *chroma* correlate :math:`C_94`. + Return the *chroma* correlate :math:`C_94`. Parameters ---------- - s : numeric or array_like + s *Saturation* correlate :math:`s`. - Y_b : numeric or array_like + Y_b Tristimulus values :math:`Y_b` the background. - Y_w : numeric or array_like + Y_w Tristimulus values :math:`Y_b` the reference white. - Q : numeric or array_like + Q *Brightness* correlate :math:`Q` of the stimulus. - Q_w : numeric or array_like + Q_w *Brightness* correlate :math:`Q` of the reference white. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Chroma* correlate :math:`C_94`. Examples @@ -1232,26 +1377,32 @@ def chroma_correlate(s, Y_b, Y_w, Q, Q_w): Q = as_float_array(Q) Q_w = as_float_array(Q_w) - C_94 = (2.44 * spow(s, 0.69) * (spow(Q / Q_w, Y_b / Y_w)) * - (1.64 - spow(0.29, Y_b / Y_w))) + C_94 = ( + 2.44 + * spow(s, 0.69) + * (spow(Q / Q_w, Y_b / Y_w)) + * (1.64 - spow(0.29, Y_b / Y_w)) + ) return C_94 -def colourfulness_correlate(F_L, C_94): +def colourfulness_correlate( + F_L: FloatingOrArrayLike, C_94: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the *colourfulness* correlate :math:`M_94`. + Return the *colourfulness* correlate :math:`M_94`. Parameters ---------- - F_L : numeric or array_like + F_L Luminance adaptation factor :math:`F_L`. - C_94 : numeric + C_94 *Chroma* correlate :math:`C_94`. Returns ------- - numeric + :class:`numpy.floating` or :class:`numpy.ndarray` *Colourfulness* correlate :math:`M_94`. Examples diff --git a/colour/appearance/kim2009.py b/colour/appearance/kim2009.py new file mode 100644 index 0000000000..172cc663df --- /dev/null +++ b/colour/appearance/kim2009.py @@ -0,0 +1,561 @@ +""" +Kim, Weyrich and Kautz (2009) Colour Appearance Model +===================================================== + +Defines the *Kim, Weyrich and Kautz (2009)* colour appearance model objects: + +- :class:`colour.appearance.InductionFactors_Kim2009` +- :attr:`colour.VIEWING_CONDITIONS_KIM2009` +- :class:`colour.appearance.MediaParameters_Kim2009` +- :attr:`colour.MEDIA_PARAMETERS_KIM2009` +- :class:`colour.CAM_Specification_Kim2009` +- :func:`colour.XYZ_to_Kim2009` +- :func:`colour.Kim2009_to_XYZ` + +References +---------- +- :cite:`Kim2009` : Kim, M., Weyrich, T., & Kautz, J. (2009). Modeling Human + Color Perception under Extended Luminance Levels. ACM Transactions on + Graphics, 28(3), 27:1--27:9. doi:10.1145/1531326.1531333 +""" + +from __future__ import annotations + +import numpy as np +from collections import namedtuple +from dataclasses import astuple, dataclass, field + +from colour.adaptation import CAT_CAT02 +from colour.appearance.ciecam02 import ( + VIEWING_CONDITIONS_CIECAM02, + CAT_INVERSE_CAT02, + RGB_to_rgb, + degree_of_adaptation, + full_chromatic_adaptation_forward, + full_chromatic_adaptation_inverse, + hue_quadrature, + rgb_to_RGB, +) +from colour.algebra import vector_dot, spow +from colour.hints import ( + ArrayLike, + Boolean, + Floating, + FloatingOrArrayLike, + FloatingOrNDArray, + NDArray, + Optional, +) +from colour.utilities import ( + CaseInsensitiveMapping, + MixinDataclassArithmetic, + as_float, + as_float_array, + from_range_100, + from_range_degrees, + has_only_nan, + ones, + to_domain_100, + to_domain_degrees, + tsplit, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "InductionFactors_Kim2009", + "VIEWING_CONDITIONS_KIM2009", + "MediaParameters_Kim2009", + "MEDIA_PARAMETERS_KIM2009", + "CAM_Specification_Kim2009", + "XYZ_to_Kim2009", + "Kim2009_to_XYZ", +] + + +class InductionFactors_Kim2009( + namedtuple("InductionFactors_Kim2009", ("F", "c", "N_c")) +): + """ + *Kim, Weyrich and Kautz (2009)* colour appearance model induction factors. + + Parameters + ---------- + F + Maximum degree of adaptation :math:`F`. + c + Exponential non-linearity :math:`c`. + N_c + Chromatic induction factor :math:`N_c`. + + Notes + ----- + - The *Kim, Weyrich and Kautz (2009)* colour appearance model induction + factors are the same as *CIECAM02* colour appearance model. + - The *Kim, Weyrich and Kautz (2009)* colour appearance model separates + the surround modelled by the + :class:`colour.appearance.InductionFactors_Kim2009` class instance from + the media, modeled with the + :class:`colour.appearance.MediaParameters_Kim2009` class instance. + + References + ---------- + :cite:`Kim2009` + """ + + +VIEWING_CONDITIONS_KIM2009: CaseInsensitiveMapping = CaseInsensitiveMapping( + VIEWING_CONDITIONS_CIECAM02 +) +VIEWING_CONDITIONS_KIM2009.__doc__ = """ +Reference *Kim, Weyrich and Kautz (2009)* colour appearance model viewing +conditions. + +References +---------- +:cite:`Kim2009` +""" + + +class MediaParameters_Kim2009(namedtuple("MediaParameters_Kim2009", ("E",))): + """ + *Kim, Weyrich and Kautz (2009)* colour appearance model media parameters. + + Parameters + ---------- + E + Lightness prediction modulating parameter :math:`E`. + + References + ---------- + :cite:`Kim2009` + """ + + def __new__(cls, E): + """ + Return a new instance of the + :class:`colour.appearance.MediaParameters_Kim2009` class. + """ + + return super().__new__(cls, E) + + +MEDIA_PARAMETERS_KIM2009: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "High-luminance LCD Display": MediaParameters_Kim2009(1), + "Transparent Advertising Media": MediaParameters_Kim2009(1.2175), + "CRT Displays": MediaParameters_Kim2009(1.4572), + "Reflective Paper": MediaParameters_Kim2009(1.7526), + } +) +MEDIA_PARAMETERS_KIM2009.__doc__ = """ +Reference *Kim, Weyrich and Kautz (2009)* colour appearance model media +parameters. + +References +---------- +:cite:`Kim2009` + +Aliases: + +- 'bright_lcd_display': 'High-luminance LCD Display' +- 'advertising_transparencies': 'Transparent Advertising Media' +- 'crt': 'CRT Displays' +- 'paper': 'Reflective Paper' +""" +MEDIA_PARAMETERS_KIM2009["bright_lcd_display"] = MEDIA_PARAMETERS_KIM2009[ + "High-luminance LCD Display" +] +MEDIA_PARAMETERS_KIM2009[ + "advertising_transparencies" +] = MEDIA_PARAMETERS_KIM2009["Transparent Advertising Media"] +MEDIA_PARAMETERS_KIM2009["crt"] = MEDIA_PARAMETERS_KIM2009["CRT Displays"] +MEDIA_PARAMETERS_KIM2009["paper"] = MEDIA_PARAMETERS_KIM2009[ + "Reflective Paper" +] + + +@dataclass +class CAM_Specification_Kim2009(MixinDataclassArithmetic): + """ + Define the *Kim, Weyrich and Kautz (2009)* colour appearance model + specification. + + Parameters + ---------- + J + Correlate of *Lightness* :math:`J`. + C + Correlate of *chroma* :math:`C`. + h + *Hue* angle :math:`h` in degrees. + s + Correlate of *saturation* :math:`s`. + Q + Correlate of *brightness* :math:`Q`. + M + Correlate of *colourfulness* :math:`M`. + H + *Hue* :math:`h` quadrature :math:`H`. + HC + *Hue* :math:`h` composition :math:`H^C`. + + References + ---------- + :cite:`Kim2009` + """ + + J: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + C: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + h: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + s: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + Q: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + M: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + H: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + HC: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + + +def XYZ_to_Kim2009( + XYZ: ArrayLike, + XYZ_w: ArrayLike, + L_A: FloatingOrArrayLike, + media: MediaParameters_Kim2009 = MEDIA_PARAMETERS_KIM2009["CRT Displays"], + surround: InductionFactors_Kim2009 = VIEWING_CONDITIONS_KIM2009["Average"], + discount_illuminant: Boolean = False, + n_c: Floating = 0.57, +) -> CAM_Specification_Kim2009: + """ + Compute the *Kim, Weyrich and Kautz (2009)* colour appearance model + correlates from given *CIE XYZ* tristimulus values. + + Parameters + ---------- + XYZ + *CIE XYZ* tristimulus values of test sample / stimulus. + XYZ_w + *CIE XYZ* tristimulus values of reference white. + L_A + Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`, (often taken + to be 20% of the luminance of a white object in the scene). + media + Media parameters. + surround + Surround viewing conditions induction factors. + discount_illuminant + Truth value indicating if the illuminant should be discounted. + n_c + Cone response sigmoidal curve modulating factor :math:`n_c`. + + Returns + ------- + :class:`colour.CAM_Specification_Kim2009` + *Kim, Weyrich and Kautz (2009)* colour appearance model specification. + + Notes + ----- + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``XYZ`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ + | ``XYZ_w`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ + + +---------------------------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +=================================+=======================+===============+ + | ``CAM_Specification_Kim2009.J`` | [0, 100] | [0, 1] | + +---------------------------------+-----------------------+---------------+ + | ``CAM_Specification_Kim2009.C`` | [0, 100] | [0, 1] | + +---------------------------------+-----------------------+---------------+ + | ``CAM_Specification_Kim2009.h`` | [0, 360] | [0, 1] | + +---------------------------------+-----------------------+---------------+ + | ``CAM_Specification_Kim2009.s`` | [0, 100] | [0, 1] | + +---------------------------------+-----------------------+---------------+ + | ``CAM_Specification_Kim2009.Q`` | [0, 100] | [0, 1] | + +---------------------------------+-----------------------+---------------+ + | ``CAM_Specification_Kim2009.M`` | [0, 100] | [0, 1] | + +---------------------------------+-----------------------+---------------+ + | ``CAM_Specification_Kim2009.H`` | [0, 400] | [0, 1] | + +---------------------------------+-----------------------+---------------+ + + References + ---------- + :cite:`Kim2009` + + Examples + -------- + >>> XYZ = np.array([19.01, 20.00, 21.78]) + >>> XYZ_w = np.array([95.05, 100.00, 108.88]) + >>> L_A = 318.31 + >>> media = MEDIA_PARAMETERS_KIM2009['CRT Displays'] + >>> surround = VIEWING_CONDITIONS_KIM2009['Average'] + >>> XYZ_to_Kim2009(XYZ, XYZ_w, L_A, media, surround) + ... # doctest: +ELLIPSIS + CAM_Specification_Kim2009(J=28.8619089..., C=0.5592455..., \ +h=219.0480667..., s=9.3837797..., Q=52.7138883..., M=0.4641738..., \ +H=278.0602824..., HC=None) + """ + + XYZ = to_domain_100(XYZ) + XYZ_w = to_domain_100(XYZ_w) + _X_w, Y_w, _Z_w = tsplit(XYZ_w) + L_A = as_float_array(L_A) + + # Converting *CIE XYZ* tristimulus values to *CMCCAT2000* transform + # sharpened *RGB* values. + RGB = vector_dot(CAT_CAT02, XYZ) + RGB_w = vector_dot(CAT_CAT02, XYZ_w) + + # Computing degree of adaptation :math:`D`. + D = ( + degree_of_adaptation(surround.F, L_A) + if not discount_illuminant + else ones(L_A.shape) + ) + + # Computing full chromatic adaptation. + XYZ_c = full_chromatic_adaptation_forward(RGB, RGB_w, Y_w, D) + XYZ_wc = full_chromatic_adaptation_forward(RGB_w, RGB_w, Y_w, D) + + # Converting to *Hunt-Pointer-Estevez* colourspace. + LMS = RGB_to_rgb(XYZ_c) + LMS_w = RGB_to_rgb(XYZ_wc) + + # Cones absolute response. + LMS_n_c = spow(LMS, n_c) + LMS_w_n_c = spow(LMS_w, n_c) + L_A_n_c = spow(L_A, n_c) + LMS_p = LMS_n_c / (LMS_n_c + L_A_n_c) + LMS_wp = LMS_w_n_c / (LMS_w_n_c + L_A_n_c) + + # Achromatic signal :math:`A` and :math:`A_w`. + v_A = np.array([40, 20, 1]) + A = np.sum(v_A * LMS_p, axis=-1) / 61 + A_w = np.sum(v_A * LMS_wp, axis=-1) / 61 + + # Perceived *Lightness* :math:`J_p`. + a_j, b_j, o_j, n_j = 0.89, 0.24, 0.65, 3.65 + A_A_w = A / A_w + J_p = spow( + (-(A_A_w - b_j) * spow(o_j, n_j)) / (A_A_w - b_j - a_j), 1 / n_j + ) + + # Computing the media dependent *Lightness* :math:`J`. + J = 100 * (media.E * (J_p - 1) + 1) + + # Computing the correlate of *brightness* :math:`Q`. + n_q = 0.1308 + Q = J * spow(Y_w, n_q) + + # Opponent signals :math:`a` and :math:`b`. + a = (1 / 11) * np.sum(np.array([11, -12, 1]) * LMS_p, axis=-1) + b = (1 / 9) * np.sum(np.array([1, 1, -2]) * LMS_p, axis=-1) + + # Computing the correlate of *chroma* :math:`C`. + a_k, n_k = 456.5, 0.62 + C = a_k * spow(np.sqrt(a**2 + b**2), n_k) + + # Computing the correlate of *colourfulness* :math:`M`. + a_m, b_m = 0.11, 0.61 + M = C * (a_m * np.log10(Y_w) + b_m) + + # Computing the correlate of *saturation* :math:`s`. + s = 100 * np.sqrt(M / Q) + + # Computing the *hue* angle :math:`h`. + h = np.degrees(np.arctan2(b, a)) % 360 + + # Computing hue :math:`h` quadrature :math:`H`. + H = hue_quadrature(h) + + return CAM_Specification_Kim2009( + as_float(from_range_100(J)), + as_float(from_range_100(C)), + as_float(from_range_degrees(h)), + as_float(from_range_100(s)), + as_float(from_range_100(Q)), + as_float(from_range_100(M)), + as_float(from_range_degrees(H, 400)), + None, + ) + + +def Kim2009_to_XYZ( + specification: CAM_Specification_Kim2009, + XYZ_w: ArrayLike, + L_A: FloatingOrArrayLike, + media: MediaParameters_Kim2009 = MEDIA_PARAMETERS_KIM2009["CRT Displays"], + surround: InductionFactors_Kim2009 = VIEWING_CONDITIONS_KIM2009["Average"], + discount_illuminant: Boolean = False, + n_c: Floating = 0.57, +) -> NDArray: + """ + Convert from *Kim, Weyrich and Kautz (2009)* specification to *CIE XYZ* + tristimulus values. + + Parameters + ---------- + specification + *Kim, Weyrich and Kautz (2009)* colour appearance model specification. + Correlate of *Lightness* :math:`J`, correlate of *chroma* :math:`C` or + correlate of *colourfulness* :math:`M` and *hue* angle :math:`h` in + degrees must be specified, e.g. :math:`JCh` or :math:`JMh`. + XYZ_w + *CIE XYZ* tristimulus values of reference white. + L_A + Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`, (often taken + to be 20% of the luminance of a white object in the scene). + media + Media parameters. + surroundl + Surround viewing conditions induction factors. + discount_illuminant + Discount the illuminant. + n_c + Cone response sigmoidal curve modulating factor :math:`n_c`. + + Returns + ------- + :class:`numpy.ndarray` + *CIE XYZ* tristimulus values. + + Raises + ------ + ValueError + If neither *C* or *M* correlates have been defined in the + ``CAM_Specification_Kim2009`` argument. + + Notes + ----- + +---------------------------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +=================================+=======================+===============+ + | ``CAM_Specification_Kim2009.J`` | [0, 100] | [0, 1] | + +---------------------------------+-----------------------+---------------+ + | ``CAM_Specification_Kim2009.C`` | [0, 100] | [0, 1] | + +---------------------------------+-----------------------+---------------+ + | ``CAM_Specification_Kim2009.h`` | [0, 360] | [0, 1] | + +---------------------------------+-----------------------+---------------+ + | ``CAM_Specification_Kim2009.s`` | [0, 100] | [0, 1] | + +---------------------------------+-----------------------+---------------+ + | ``CAM_Specification_Kim2009.Q`` | [0, 100] | [0, 1] | + +---------------------------------+-----------------------+---------------+ + | ``CAM_Specification_Kim2009.M`` | [0, 100] | [0, 1] | + +---------------------------------+-----------------------+---------------+ + | ``CAM_Specification_Kim2009.H`` | [0, 360] | [0, 1] | + +---------------------------------+-----------------------+---------------+ + | ``XYZ_w`` | [0, 100] | [0, 1] | + +---------------------------------+-----------------------+---------------+ + + +-----------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +===========+=======================+===============+ + | ``XYZ`` | [0, 100] | [0, 1] | + +-----------+-----------------------+---------------+ + + References + ---------- + :cite:`Kim2009` + + Examples + -------- + >>> specification = CAM_Specification_Kim2009(J=28.861908975839647, + ... C=0.5592455924373706, + ... h=219.04806677662953) + >>> XYZ_w = np.array([95.05, 100.00, 108.88]) + >>> L_A = 318.31 + >>> media = MEDIA_PARAMETERS_KIM2009['CRT Displays'] + >>> surround = VIEWING_CONDITIONS_KIM2009['Average'] + >>> Kim2009_to_XYZ(specification, XYZ_w, L_A, media, surround) + ... # doctest: +ELLIPSIS + array([ 19.0099995..., 19.9999999..., 21.7800000...]) + """ + + J, C, h, _s, _Q, M, _H, _HC = astuple(specification) + + J = to_domain_100(J) + C = to_domain_100(C) + h = to_domain_degrees(h) + M = to_domain_100(M) + L_A = as_float_array(L_A) + XYZ_w = to_domain_100(XYZ_w) + _X_w, Y_w, _Z_w = tsplit(XYZ_w) + + # Converting *CIE XYZ* tristimulus values to *CMCCAT2000* transform + # sharpened *RGB* values. + RGB_w = vector_dot(CAT_CAT02, XYZ_w) + + # Computing degree of adaptation :math:`D`. + D = ( + degree_of_adaptation(surround.F, L_A) + if not discount_illuminant + else ones(L_A.shape) + ) + + # Computing full chromatic adaptation. + XYZ_wc = full_chromatic_adaptation_forward(RGB_w, RGB_w, Y_w, D) + + # Converting to *Hunt-Pointer-Estevez* colourspace. + LMS_w = RGB_to_rgb(XYZ_wc) + + # n_q = 0.1308 + # J = Q / spow(Y_w, n_q) + if has_only_nan(C) and not has_only_nan(M): + a_m, b_m = 0.11, 0.61 + C = M / (a_m * np.log10(Y_w) + b_m) + elif has_only_nan(C): + raise ValueError( + 'Either "C" or "M" correlate must be defined in ' + 'the "CAM_Specification_Kim2009" argument!' + ) + + # Cones absolute response. + LMS_w_n_c = spow(LMS_w, n_c) + L_A_n_c = spow(L_A, n_c) + LMS_wp = LMS_w_n_c / (LMS_w_n_c + L_A_n_c) + + # Achromatic signal :math:`A_w` + v_A = np.array([40, 20, 1]) + A_w = np.sum(v_A * LMS_wp, axis=-1) / 61 + + # Perceived *Lightness* :math:`J_p`. + J_p = (J / 100 - 1) / media.E + 1 + + # Achromatic signal :math:`A`. + a_j, b_j, n_j, o_j = 0.89, 0.24, 3.65, 0.65 + J_p_n_j = spow(J_p, n_j) + A = A_w * ((a_j * J_p_n_j) / (J_p_n_j + spow(o_j, n_j)) + b_j) + + # Opponent signals :math:`a` and :math:`b`. + a_k, n_k = 456.5, 0.62 + C_a_k_n_k = spow(C / a_k, 1 / n_k) + hr = np.radians(h) + a, b = np.cos(hr) * C_a_k_n_k, np.sin(hr) * C_a_k_n_k + + # Cones absolute response. + M = np.array( + [ + [1.0000, 0.3215, 0.2053], + [1.0000, -0.6351, -0.1860], + [1.0000, -0.1568, -4.4904], + ] + ) + LMS_p = vector_dot(M, tstack([A, a, b])) + LMS = spow((-spow(L_A, n_c) * LMS_p) / (LMS_p - 1), 1 / n_c) + + # Converting to *Hunt-Pointer-Estevez* colourspace. + RGB_c = rgb_to_RGB(LMS) + + # Applying inverse full chromatic adaptation. + RGB = full_chromatic_adaptation_inverse(RGB_c, RGB_w, Y_w, D) + + XYZ = vector_dot(CAT_INVERSE_CAT02, RGB) + + return from_range_100(XYZ) diff --git a/colour/appearance/llab.py b/colour/appearance/llab.py index 6ac5a7b0d1..6fbb806e23 100644 --- a/colour/appearance/llab.py +++ b/colour/appearance/llab.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ :math:`LLAB(l:c)` Colour Appearance Model ========================================= -Defines *:math:`LLAB(l:c)`* colour appearance model objects: +Defines the *:math:`LLAB(l:c)`* colour appearance model objects: - :class:`colour.appearance.InductionFactors_LLAB` - :attr:`colour.VIEWING_CONDITIONS_LLAB` @@ -26,47 +25,71 @@ Colour_Appearance_and_Gamut_Mapping """ -from __future__ import division, unicode_literals - import numpy as np from collections import namedtuple - -from colour.algebra import polar_to_cartesian, spow -from colour.utilities import (CaseInsensitiveMapping, as_float_array, - vector_dot, from_range_degrees, to_domain_100, - tsplit, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from dataclasses import dataclass, field + +from colour.algebra import polar_to_cartesian, spow, vector_dot +from colour.hints import ( + ArrayLike, + FloatingOrArrayLike, + FloatingOrNDArray, + NDArray, + Optional, +) +from colour.utilities import ( + CaseInsensitiveMapping, + MixinDataclassArithmetic, + as_float, + as_float_array, + from_range_degrees, + to_domain_100, + tsplit, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'InductionFactors_LLAB', 'VIEWING_CONDITIONS_LLAB', - 'MATRIX_XYZ_TO_RGB_LLAB', 'MATRIX_RGB_TO_XYZ_LLAB', - 'CAM_ReferenceSpecification_LLAB', 'CAM_Specification_LLAB', 'XYZ_to_LLAB', - 'XYZ_to_RGB_LLAB', 'chromatic_adaptation', 'f', - 'opponent_colour_dimensions', 'hue_angle', 'chroma_correlate', - 'colourfulness_correlate', 'saturation_correlate', 'final_opponent_signals' + "InductionFactors_LLAB", + "VIEWING_CONDITIONS_LLAB", + "MATRIX_XYZ_TO_RGB_LLAB", + "MATRIX_RGB_TO_XYZ_LLAB", + "CAM_ReferenceSpecification_LLAB", + "CAM_Specification_LLAB", + "XYZ_to_LLAB", + "XYZ_to_RGB_LLAB", + "chromatic_adaptation", + "f", + "opponent_colour_dimensions", + "hue_angle", + "chroma_correlate", + "colourfulness_correlate", + "saturation_correlate", + "final_opponent_signals", ] class InductionFactors_LLAB( - namedtuple('InductionFactors_LLAB', ('D', 'F_S', 'F_L', 'F_C'))): + namedtuple("InductionFactors_LLAB", ("D", "F_S", "F_L", "F_C")) +): """ *:math:`LLAB(l:c)`* colour appearance model induction factors. Parameters ---------- - D : numeric or array_like + D *Discounting-the-Illuminant* factor :math:`D`. - F_S : numeric or array_like + F_S Surround induction factor :math:`F_S`. - F_L : numeric or array_like + F_L *Lightness* induction factor :math:`F_L`. - F_C : numeric or array_like + F_C *Chroma* induction factor :math:`F_C`. References @@ -75,18 +98,25 @@ class InductionFactors_LLAB( """ -VIEWING_CONDITIONS_LLAB = CaseInsensitiveMapping({ - 'Reference Samples & Images, Average Surround, Subtending > 4': ( - InductionFactors_LLAB(1, 3, 0, 1)), - 'Reference Samples & Images, Average Surround, Subtending < 4': ( - InductionFactors_LLAB(1, 3, 1, 1)), - 'Television & VDU Displays, Dim Surround': (InductionFactors_LLAB( - 0.7, 3.5, 1, 1)), - 'Cut Sheet Transparency, Dim Surround': (InductionFactors_LLAB( - 1, 5, 1, 1.1)), - '35mm Projection Transparency, Dark Surround': (InductionFactors_LLAB( - 0.7, 4, 1, 1)) -}) +VIEWING_CONDITIONS_LLAB: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Reference Samples & Images, Average Surround, Subtending > 4": ( + InductionFactors_LLAB(1, 3, 0, 1) + ), + "Reference Samples & Images, Average Surround, Subtending < 4": ( + InductionFactors_LLAB(1, 3, 1, 1) + ), + "Television & VDU Displays, Dim Surround": ( + InductionFactors_LLAB(0.7, 3.5, 1, 1) + ), + "Cut Sheet Transparency, Dim Surround": ( + InductionFactors_LLAB(1, 5, 1, 1.1) + ), + "35mm Projection Transparency, Dark Surround": ( + InductionFactors_LLAB(0.7, 4, 1, 1) + ), + } +) VIEWING_CONDITIONS_LLAB.__doc__ = """ Reference :math:`LLAB(l:c)` colour appearance model viewing conditions. @@ -94,13 +124,6 @@ class InductionFactors_LLAB( ---------- :cite:`Fairchild2013x`, :cite:`Luo1996b`, :cite:`Luo1996c` -VIEWING_CONDITIONS_LLAB : CaseInsensitiveMapping - **{'Reference Samples & Images, Average Surround, Subtending > 4', - 'Reference Samples & Images, Average Surround, Subtending < 4', - 'Television & VDU Displays, Dim Surround', - 'Cut Sheet Transparency, Dim Surround':, - '35mm Projection Transparency, Dark Surround'}** - Aliases: - 'ref_average_4_plus': @@ -111,45 +134,45 @@ class InductionFactors_LLAB( - 'sheet_dim': 'Cut Sheet Transparency, Dim Surround' - 'projected_dark': '35mm Projection Transparency, Dark Surround' """ -VIEWING_CONDITIONS_LLAB['ref_average_4_plus'] = ( # yapf: disable - VIEWING_CONDITIONS_LLAB['Reference Samples & Images, ' - 'Average Surround, Subtending > 4']) -VIEWING_CONDITIONS_LLAB['ref_average_4_minus'] = ( # yapf: disable - VIEWING_CONDITIONS_LLAB['Reference Samples & Images, ' - 'Average Surround, Subtending < 4']) -VIEWING_CONDITIONS_LLAB['tv_dim'] = ( - VIEWING_CONDITIONS_LLAB['Television & VDU Displays, Dim Surround']) -VIEWING_CONDITIONS_LLAB['sheet_dim'] = ( - VIEWING_CONDITIONS_LLAB['Cut Sheet Transparency, Dim Surround']) -VIEWING_CONDITIONS_LLAB['projected_dark'] = ( - VIEWING_CONDITIONS_LLAB['35mm Projection Transparency, Dark Surround']) - -MATRIX_XYZ_TO_RGB_LLAB = np.array([ - [0.8951, 0.2664, -0.1614], - [-0.7502, 1.7135, 0.0367], - [0.0389, -0.0685, 1.0296], -]) +VIEWING_CONDITIONS_LLAB["ref_average_4_plus"] = VIEWING_CONDITIONS_LLAB[ + "Reference Samples & Images, " "Average Surround, Subtending > 4" +] +VIEWING_CONDITIONS_LLAB["ref_average_4_minus"] = VIEWING_CONDITIONS_LLAB[ + "Reference Samples & Images, " "Average Surround, Subtending < 4" +] +VIEWING_CONDITIONS_LLAB["tv_dim"] = VIEWING_CONDITIONS_LLAB[ + "Television & VDU Displays, Dim Surround" +] +VIEWING_CONDITIONS_LLAB["sheet_dim"] = VIEWING_CONDITIONS_LLAB[ + "Cut Sheet Transparency, Dim Surround" +] +VIEWING_CONDITIONS_LLAB["projected_dark"] = VIEWING_CONDITIONS_LLAB[ + "35mm Projection Transparency, Dark Surround" +] + +MATRIX_XYZ_TO_RGB_LLAB: NDArray = np.array( + [ + [0.8951, 0.2664, -0.1614], + [-0.7502, 1.7135, 0.0367], + [0.0389, -0.0685, 1.0296], + ] +) """ LLAB(l:c) colour appearance model *CIE XYZ* tristimulus values to normalised cone responses matrix. - -MATRIX_XYZ_TO_RGB_LLAB : array_like, (3, 3) """ -MATRIX_RGB_TO_XYZ_LLAB = np.linalg.inv(MATRIX_XYZ_TO_RGB_LLAB) +MATRIX_RGB_TO_XYZ_LLAB: NDArray = np.linalg.inv(MATRIX_XYZ_TO_RGB_LLAB) """ LLAB(l:c) colour appearance model normalised cone responses to *CIE XYZ* tristimulus values matrix. - -MATRIX_RGB_TO_XYZ_LLAB : array_like, (3, 3) """ -class CAM_ReferenceSpecification_LLAB( - namedtuple('CAM_ReferenceSpecification_LLAB', - ('L_L', 'Ch_L', 'h_L', 's_L', 'C_L', 'HC', 'A_L', 'B_L'))): +@dataclass +class CAM_ReferenceSpecification_LLAB(MixinDataclassArithmetic): """ - Defines the *:math:`LLAB(l:c)`* colour appearance model reference + Define the *:math:`LLAB(l:c)`* colour appearance model reference specification. This specification has field names consistent with *Fairchild (2013)* @@ -157,21 +180,21 @@ class CAM_ReferenceSpecification_LLAB( Parameters ---------- - L_L : numeric or array_like + L_L Correlate of *Lightness* :math:`L_L`. - Ch_L : numeric or array_like + Ch_L Correlate of *chroma* :math:`Ch_L`. - h_L : numeric or array_like + h_L *Hue* angle :math:`h_L` in degrees. - s_L : numeric or array_like + s_L Correlate of *saturation* :math:`s_L`. - C_L : numeric or array_like + C_L Correlate of *colourfulness* :math:`C_L`. - HC : numeric or array_like + HC *Hue* :math:`h` composition :math:`H^C`. - A_L : numeric or array_like + A_L Opponent signal :math:`A_L`. - B_L : numeric or array_like + B_L Opponent signal :math:`B_L`. References @@ -179,12 +202,20 @@ class CAM_ReferenceSpecification_LLAB( :cite:`Fairchild2013x`, :cite:`Luo1996b`, :cite:`Luo1996c` """ + L_L: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + Ch_L: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + h_L: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + s_L: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + C_L: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + HC: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + A_L: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + B_L: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + -class CAM_Specification_LLAB( - namedtuple('CAM_Specification_LLAB', - ('J', 'C', 'h', 's', 'M', 'HC', 'a', 'b'))): +@dataclass +class CAM_Specification_LLAB(MixinDataclassArithmetic): """ - Defines the *:math:`LLAB(l:c)`* colour appearance model specification. + Define the *:math:`LLAB(l:c)`* colour appearance model specification. This specification has field names consistent with the remaining colour appearance models in :mod:`colour.appearance` but diverge from @@ -192,21 +223,21 @@ class CAM_Specification_LLAB( Parameters ---------- - J : numeric or array_like + J Correlate of *Lightness* :math:`L_L`. - C : numeric or array_like + C Correlate of *chroma* :math:`Ch_L`. - h : numeric or array_like + h *Hue* angle :math:`h_L` in degrees. - s : numeric or array_like + s Correlate of *saturation* :math:`s_L`. - M : numeric or array_like + M Correlate of *colourfulness* :math:`C_L`. - HC : numeric or array_like + HC *Hue* :math:`h` composition :math:`H^C`. - a : numeric or array_like + a Opponent signal :math:`A_L`. - b : numeric or array_like + b Opponent signal :math:`B_L`. Notes @@ -218,45 +249,55 @@ class CAM_Specification_LLAB( :cite:`Fairchild2013x`, :cite:`Luo1996b`, :cite:`Luo1996c` """ + J: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + C: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + h: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + s: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + M: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + HC: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + a: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + b: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + def XYZ_to_LLAB( - XYZ, - XYZ_0, - Y_b, - L, - surround=VIEWING_CONDITIONS_LLAB[ - 'Reference Samples & Images, Average Surround, Subtending < 4']): + XYZ: ArrayLike, + XYZ_0: ArrayLike, + Y_b: FloatingOrArrayLike, + L: FloatingOrArrayLike, + surround: InductionFactors_LLAB = VIEWING_CONDITIONS_LLAB[ + "Reference Samples & Images, Average Surround, Subtending < 4" + ], +) -> CAM_Specification_LLAB: """ - Computes the *:math:`LLAB(l:c)`* colour appearance model correlates. + Compute the *:math:`LLAB(l:c)`* colour appearance model correlates. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values of test sample / stimulus. - XYZ_0 : array_like + XYZ_0 *CIE XYZ* tristimulus values of reference white. - Y_b : numeric or array_like + Y_b Luminance factor of the background in :math:`cd/m^2`. - L : numeric or array_like + L Absolute luminance :math:`L` of reference white in :math:`cd/m^2`. - surround : InductionFactors_LLAB, optional + surround Surround viewing conditions induction factors. Returns ------- - CAM_Specification_LLAB + :class:`colour.CAM_Specification_LLAB` *:math:`LLAB(l:c)`* colour appearance model specification. Notes ----- - - +--------------------------+-----------------------+---------------+ - | **Domain** | **Scale - Reference** | **Scale - 1** | - +==========================+=======================+===============+ - | ``XYZ`` | [0, 100] | [0, 1] | - +--------------------------+-----------------------+---------------+ - | ``XYZ_0`` | [0, 100] | [0, 1] | - +--------------------------+-----------------------+---------------+ + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``XYZ`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ + | ``XYZ_0`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ +------------------------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | @@ -296,7 +337,8 @@ def XYZ_to_LLAB( # ------------------------------------------------------------------------- # Computing opponent colour dimensions. L_L, a, b = tsplit( - opponent_colour_dimensions(XYZ_r, Y_b, surround.F_S, surround.F_L)) + opponent_colour_dimensions(XYZ_r, Y_b, surround.F_S, surround.F_L) + ) # Computing perceptual correlates. # ------------------------------------------------------------------------- @@ -325,22 +367,30 @@ def XYZ_to_LLAB( # ------------------------------------------------------------------------- A_L, B_L = tsplit(final_opponent_signals(C_L, h_L)) - return CAM_Specification_LLAB(L_L, Ch_L, from_range_degrees(h_L), s_L, C_L, - None, A_L, B_L) + return CAM_Specification_LLAB( + L_L, + Ch_L, + as_float(from_range_degrees(h_L)), + s_L, + C_L, + None, + A_L, + B_L, + ) -def XYZ_to_RGB_LLAB(XYZ): +def XYZ_to_RGB_LLAB(XYZ: ArrayLike) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to normalised cone responses. + Convert from *CIE XYZ* tristimulus values to normalised cone responses. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. Returns ------- - ndarray + :class:`numpy.ndarray` Normalised cone responses. Examples @@ -358,28 +408,34 @@ def XYZ_to_RGB_LLAB(XYZ): return vector_dot(MATRIX_XYZ_TO_RGB_LLAB, XYZ_n) -def chromatic_adaptation(RGB, RGB_0, RGB_0r, Y, D=1): +def chromatic_adaptation( + RGB: ArrayLike, + RGB_0: ArrayLike, + RGB_0r: ArrayLike, + Y: FloatingOrArrayLike, + D: FloatingOrArrayLike = 1, +) -> NDArray: """ - Applies chromatic adaptation to given *RGB* normalised cone responses + Apply chromatic adaptation to given *RGB* normalised cone responses array. Parameters ---------- - RGB : array_like + RGB *RGB* normalised cone responses array of test sample / stimulus. - RGB_0 : array_like + RGB_0 *RGB* normalised cone responses array of reference white. - RGB_0r : array_like + RGB_0r *RGB* normalised cone responses array of reference illuminant *CIE Standard Illuminant D Series* *D65*. - Y : numeric or array_like + Y Tristimulus values :math:`Y` of the stimulus. - D : numeric or array_like, optional + D *Discounting-the-Illuminant* factor normalised to domain [0, 1]. Returns ------- - ndarray + :class:`numpy.ndarray` Adapted *CIE XYZ* tristimulus values. Examples @@ -396,6 +452,7 @@ def chromatic_adaptation(RGB, RGB_0, RGB_0r, Y, D=1): R_0, G_0, B_0 = tsplit(RGB_0) R_0r, G_0r, B_0r = tsplit(RGB_0r) Y = as_float_array(Y) + D = as_float_array(D) beta = spow(B_0 / B_0r, 0.0834) @@ -412,29 +469,29 @@ def chromatic_adaptation(RGB, RGB_0, RGB_0r, Y, D=1): return XYZ_r -def f(x, F_S): +def f(x: FloatingOrArrayLike, F_S: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the nonlinear response function of the *:math:`LLAB(l:c)`* colour + Define the nonlinear response function of the *:math:`LLAB(l:c)`* colour appearance model used to model the nonlinear behaviour of various visual responses. Parameters ---------- - x : numeric or array_like or array_like + x Visual response variable :math:`x`. - F_S : numeric or array_like + F_S Surround induction factor :math:`F_S`. Returns ------- - numeric or array_like + :class:`numpy.floating` or :class:`numpy.ndarray` Modeled visual response variable :math:`x`. Examples -------- >>> x = np.array([0.23350512, 0.23351103, 0.23355179]) >>> f(0.200009186234000, 3) # doctest: +ELLIPSIS - array(0.5848125...) + 0.5848125... """ x = as_float_array(x) @@ -446,12 +503,17 @@ def f(x, F_S): ((spow(0.008856, 1 / F_S) - (16 / 116)) / 0.008856) * x + (16 / 116), ) - return x_m + return as_float(x_m) -def opponent_colour_dimensions(XYZ, Y_b, F_S, F_L): +def opponent_colour_dimensions( + XYZ: ArrayLike, + Y_b: FloatingOrArrayLike, + F_S: FloatingOrArrayLike, + F_L: FloatingOrArrayLike, +) -> NDArray: """ - Returns opponent colour dimensions from given adapted *CIE XYZ* tristimulus + Return opponent colour dimensions from given adapted *CIE XYZ* tristimulus values. The opponent colour dimensions are based on a modified *CIE L\\*a\\*b\\** @@ -459,18 +521,18 @@ def opponent_colour_dimensions(XYZ, Y_b, F_S, F_L): Parameters ---------- - XYZ : array_like + XYZ Adapted *CIE XYZ* tristimulus values. - Y_b : numeric or array_like + Y_b Luminance factor of the background in :math:`cd/m^2`. - F_S : numeric or array_like + F_S Surround induction factor :math:`F_S`. - F_L : numeric or array_like + F_L Lightness induction factor :math:`F_L`. Returns ------- - ndarray + :class:`numpy.ndarray` Opponent colour dimensions. Examples @@ -501,20 +563,22 @@ def opponent_colour_dimensions(XYZ, Y_b, F_S, F_L): return Lab -def hue_angle(a, b): +def hue_angle( + a: FloatingOrArrayLike, b: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the *hue* angle :math:`h_L` in degrees. + Return the *hue* angle :math:`h_L` in degrees. Parameters ---------- - a : numeric or array_like + a Opponent colour dimension :math:`a`. - b : numeric or array_like + b Opponent colour dimension :math:`b`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Hue* angle :math:`h_L` in degrees. Examples @@ -528,23 +592,25 @@ def hue_angle(a, b): h_L = np.degrees(np.arctan2(b, a)) % 360 - return h_L + return as_float(h_L) -def chroma_correlate(a, b): +def chroma_correlate( + a: FloatingOrArrayLike, b: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the correlate of *chroma* :math:`Ch_L`. + Return the correlate of *chroma* :math:`Ch_L`. Parameters ---------- - a : numeric or array_like + a Opponent colour dimension :math:`a`. - b : numeric or array_like + b Opponent colour dimension :math:`b`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Correlate of *chroma* :math:`Ch_L`. Examples @@ -558,30 +624,35 @@ def chroma_correlate(a, b): a = as_float_array(a) b = as_float_array(b) - c = spow(a ** 2 + b ** 2, 0.5) - Ch_L = 25 * np.log(1 + 0.05 * c) + c = spow(a**2 + b**2, 0.5) + Ch_L = 25 * np.log1p(0.05 * c) - return Ch_L + return as_float(Ch_L) -def colourfulness_correlate(L, L_L, Ch_L, F_C): +def colourfulness_correlate( + L: FloatingOrArrayLike, + L_L: FloatingOrArrayLike, + Ch_L: FloatingOrArrayLike, + F_C: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Returns the correlate of *colourfulness* :math:`C_L`. + Return the correlate of *colourfulness* :math:`C_L`. Parameters ---------- - L : numeric or array_like + L Absolute luminance :math:`L` of reference white in :math:`cd/m^2`. - L_L : numeric or array_like + L_L Correlate of *Lightness* :math:`L_L`. - Ch_L : numeric or array_like + Ch_L Correlate of *chroma* :math:`Ch_L`. - F_C : numeric or array_like + F_C Chroma induction factor :math:`F_C`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Correlate of *colourfulness* :math:`C_L`. Examples @@ -600,26 +671,28 @@ def colourfulness_correlate(L, L_L, Ch_L, F_C): F_C = as_float_array(F_C) S_C = 1 + 0.47 * np.log10(L) - 0.057 * np.log10(L) ** 2 - S_M = 0.7 + 0.02 * L_L - 0.0002 * L_L ** 2 + S_M = 0.7 + 0.02 * L_L - 0.0002 * L_L**2 C_L = Ch_L * S_M * S_C * F_C - return C_L + return as_float(C_L) -def saturation_correlate(Ch_L, L_L): +def saturation_correlate( + Ch_L: FloatingOrArrayLike, L_L: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the correlate of *saturation* :math:`S_L`. + Return the correlate of *saturation* :math:`S_L`. Parameters ---------- - Ch_L : numeric or array_like + Ch_L Correlate of *chroma* :math:`Ch_L`. - L_L : numeric or array_like + L_L Correlate of *Lightness* :math:`L_L`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Correlate of *saturation* :math:`S_L`. Examples @@ -638,20 +711,22 @@ def saturation_correlate(Ch_L, L_L): return S_L -def final_opponent_signals(C_L, h_L): +def final_opponent_signals( + C_L: FloatingOrArrayLike, h_L: FloatingOrArrayLike +) -> NDArray: """ - Returns the final opponent signals :math:`A_L` and :math:`B_L`. + Return the final opponent signals :math:`A_L` and :math:`B_L`. Parameters ---------- - C_L : numeric or array_like + C_L Correlate of *colourfulness* :math:`C_L`. - h_L : numeric or array_like + h_L Correlate of *hue* :math:`h_L` in degrees. Returns ------- - ndarray + :class:`numpy.ndarray` Final opponent signals :math:`A_L` and :math:`B_L`. Examples @@ -662,6 +737,6 @@ def final_opponent_signals(C_L, h_L): array([-0.0119478..., -0.0139711...]) """ - AB_L = polar_to_cartesian(tstack([C_L, np.radians(h_L)])) + AB_L = polar_to_cartesian(tstack([as_float_array(C_L), np.radians(h_L)])) return AB_L diff --git a/colour/appearance/nayatani95.py b/colour/appearance/nayatani95.py index 8f6f5a3182..9d484d7df6 100644 --- a/colour/appearance/nayatani95.py +++ b/colour/appearance/nayatani95.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Nayatani (1995) Colour Appearance Model ======================================= -Defines *Nayatani (1995)* colour appearance model objects: +Defines the *Nayatani (1995)* colour appearance model objects: - :class:`colour.CAM_Specification_Nayatani95` - :func:`colour.XYZ_to_Nayatani95` @@ -19,54 +18,80 @@ Application, 20(3), 156-167. doi:10.1002/col.5080200305 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from collections import namedtuple - -from colour.algebra import spow -from colour.adaptation.cie1994 import (MATRIX_XYZ_TO_RGB_CIE1994, beta_1, - exponential_factors, - intermediate_values) +from dataclasses import dataclass, field + +from colour.algebra import spow, vector_dot +from colour.adaptation.cie1994 import ( + MATRIX_XYZ_TO_RGB_CIE1994, + beta_1, + exponential_factors, + intermediate_values, +) +from colour.hints import ( + ArrayLike, + FloatingOrArrayLike, + FloatingOrNDArray, + NDArray, + Optional, + cast, +) from colour.models import XYZ_to_xy -from colour.utilities import (as_float_array, vector_dot, from_range_degrees, - to_domain_100, tsplit, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.utilities import ( + MixinDataclassArithmetic, + as_float, + as_float_array, + from_range_degrees, + to_domain_100, + tsplit, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'MATRIX_XYZ_TO_RGB_NAYATANI95', 'CAM_ReferenceSpecification_Nayatani95', - 'CAM_Specification_Nayatani95', 'XYZ_to_Nayatani95', - 'illuminance_to_luminance', 'XYZ_to_RGB_Nayatani95', 'scaling_coefficient', - 'achromatic_response', 'tritanopic_response', 'protanopic_response', - 'brightness_correlate', 'ideal_white_brightness_correlate', - 'achromatic_lightness_correlate', - 'normalised_achromatic_lightness_correlate', 'hue_angle', - 'saturation_components', 'saturation_correlate', 'chroma_components', - 'chroma_correlate', 'colourfulness_components', 'colourfulness_correlate', - 'chromatic_strength_function' + "MATRIX_XYZ_TO_RGB_NAYATANI95", + "CAM_ReferenceSpecification_Nayatani95", + "CAM_Specification_Nayatani95", + "XYZ_to_Nayatani95", + "illuminance_to_luminance", + "XYZ_to_RGB_Nayatani95", + "scaling_coefficient", + "achromatic_response", + "tritanopic_response", + "protanopic_response", + "brightness_correlate", + "ideal_white_brightness_correlate", + "achromatic_lightness_correlate", + "normalised_achromatic_lightness_correlate", + "hue_angle", + "saturation_components", + "saturation_correlate", + "chroma_components", + "chroma_correlate", + "colourfulness_components", + "colourfulness_correlate", + "chromatic_strength_function", ] -MATRIX_XYZ_TO_RGB_NAYATANI95 = MATRIX_XYZ_TO_RGB_CIE1994 +MATRIX_XYZ_TO_RGB_NAYATANI95: NDArray = MATRIX_XYZ_TO_RGB_CIE1994 """ *Nayatani (1995)* colour appearance model *CIE XYZ* tristimulus values to cone responses matrix. - -MATRIX_XYZ_TO_RGB_NAYATANI95 : array_like, (3, 3) """ -class CAM_ReferenceSpecification_Nayatani95( - namedtuple('CAM_ReferenceSpecification_Nayatani95', - ('L_star_P', 'C', 'theta', 'S', 'B_r', 'M', 'H', 'H_C', - 'L_star_N'))): +@dataclass +class CAM_ReferenceSpecification_Nayatani95(MixinDataclassArithmetic): """ - Defines the *Nayatani (1995)* colour appearance model reference + Define the *Nayatani (1995)* colour appearance model reference specification. This specification has field names consistent with *Fairchild (2013)* @@ -74,23 +99,23 @@ class CAM_ReferenceSpecification_Nayatani95( Parameters ---------- - L_star_P : numeric or array_like + L_star_P Correlate of *achromatic Lightness* :math:`L_p^\\star`. - C : numeric or array_like + C Correlate of *chroma* :math:`C`. - theta : numeric or array_like + theta *Hue* angle :math:`\\theta` in degrees. - S : numeric or array_like + S Correlate of *saturation* :math:`S`. - B_r : numeric or array_like + B_r Correlate of *brightness* :math:`B_r`. - M : numeric or array_like + M Correlate of *colourfulness* :math:`M`. - H : numeric or array_like + H *Hue* :math:`h` quadrature :math:`H`. - H_C : numeric or array_like + H_C *Hue* :math:`h` composition :math:`H_C`. - L_star_N : numeric or array_like + L_star_N Correlate of *normalised achromatic Lightness* :math:`L_n^\\star`. References @@ -98,13 +123,21 @@ class CAM_ReferenceSpecification_Nayatani95( :cite:`Fairchild2013ba`, :cite:`Nayatani1995a` """ + L_star_P: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + C: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + theta: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + S: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + B_r: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + M: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + H: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + H_C: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + L_star_N: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) -class CAM_Specification_Nayatani95( - namedtuple( - 'CAM_Specification_Nayatani95', - ('L_star_P', 'C', 'h', 's', 'Q', 'M', 'H', 'HC', 'L_star_N'))): + +@dataclass +class CAM_Specification_Nayatani95(MixinDataclassArithmetic): """ - Defines the *Nayatani (1995)* colour appearance model specification. + Define the *Nayatani (1995)* colour appearance model specification. This specification has field names consistent with the remaining colour appearance models in :mod:`colour.appearance` but diverge from @@ -112,23 +145,23 @@ class CAM_Specification_Nayatani95( Parameters ---------- - L_star_P : numeric or array_like + L_star_P Correlate of *achromatic Lightness* :math:`L_p^\\star`. - C : numeric or array_like + C Correlate of *chroma* :math:`C`. - h : numeric or array_like + h *Hue* angle :math:`\\theta` in degrees. - s : numeric or array_like + s Correlate of *saturation* :math:`S`. - Q : numeric or array_like + Q Correlate of *brightness* :math:`B_r`. - M : numeric or array_like + M Correlate of *colourfulness* :math:`M`. - H : numeric or array_like + H *Hue* :math:`h` quadrature :math:`H`. - HC : numeric or array_like + HC *Hue* :math:`h` composition :math:`H_C`. - L_star_N : numeric or array_like + L_star_N Correlate of *normalised achromatic Lightness* :math:`L_n^\\star`. Notes @@ -140,43 +173,59 @@ class CAM_Specification_Nayatani95( :cite:`Fairchild2013ba`, :cite:`Nayatani1995a` """ - -def XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or, n=1): + L_star_P: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + C: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + h: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + s: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + Q: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + M: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + H: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + HC: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + L_star_N: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + + +def XYZ_to_Nayatani95( + XYZ: ArrayLike, + XYZ_n: ArrayLike, + Y_o: FloatingOrArrayLike, + E_o: FloatingOrArrayLike, + E_or: FloatingOrArrayLike, + n: FloatingOrArrayLike = 1, +) -> CAM_Specification_Nayatani95: """ - Computes the *Nayatani (1995)* colour appearance model correlates. + Compute the *Nayatani (1995)* colour appearance model correlates. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values of test sample / stimulus. - XYZ_n : array_like + XYZ_n *CIE XYZ* tristimulus values of reference white. - Y_o : numeric or array_like + Y_o Luminance factor :math:`Y_o` of achromatic background as percentage normalised to domain [0.18, 1.0] in **'Reference'** domain-range scale. - E_o : numeric or array_like + E_o Illuminance :math:`E_o` of the viewing field in lux. - E_or : numeric or array_like + E_or Normalising illuminance :math:`E_{or}` in lux usually normalised to domain [1000, 3000]. - n : numeric or array_like, optional - Noise term used in the non linear chromatic adaptation model. + n + Noise term used in the non-linear chromatic adaptation model. Returns ------- - CAM_Specification_Nayatani95 + :class:`colour.CAM_Specification_Nayatani95` *Nayatani (1995)* colour appearance model specification. Notes ----- - - +--------------------------------+-----------------------+---------------+ - | **Domain** | **Scale - Reference** | **Scale - 1** | - +================================+=======================+===============+ - | ``XYZ`` | [0, 100] | [0, 1] | - +--------------------------------+-----------------------+---------------+ - | ``XYZ_n`` | [0, 100] | [0, 1] | - +--------------------------------+-----------------------+---------------+ + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``XYZ`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ + | ``XYZ_n`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ +------------------------------------+-----------------------\ +---------------+ @@ -202,8 +251,8 @@ def XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or, n=1): >>> E_or = 1000.0 >>> XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or) # doctest: +ELLIPSIS CAM_Specification_Nayatani95(L_star_P=49.9998829..., C=0.0133550..., \ -h=257.5232268..., s=0.0133550..., Q=62.6266734..., M=0.0167262..., H=None, \ -HC=None, L_star_N=50.0039154...) +h=257.5232268..., s=0.0133550..., Q=62.6266734..., M=0.0167262..., \ +H=None, HC=None, L_star_N=50.0039154...) """ XYZ = to_domain_100(XYZ) @@ -222,8 +271,9 @@ def XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or, n=1): xi, eta, _zeta = tsplit(xez) # Computing adapting field cone responses. - RGB_o = (( - (Y_o[..., np.newaxis] * E_o[..., np.newaxis]) / (100 * np.pi)) * xez) + RGB_o = ( + (Y_o[..., np.newaxis] * E_o[..., np.newaxis]) / (100 * np.pi) + ) * xez # Computing stimulus cone responses. RGB = XYZ_to_RGB_Nayatani95(XYZ) @@ -252,15 +302,17 @@ def XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or, n=1): # Computing *brightness* :math:`B_{rw}` of ideal white. brightness_ideal_white = ideal_white_brightness_correlate( - bRGB_o, xez, bL_or, n) + bRGB_o, xez, bL_or, n + ) # Computing the correlate of achromatic *Lightness* :math:`L_p^\\star`. - L_star_P = (achromatic_lightness_correlate(Q_response)) + L_star_P = achromatic_lightness_correlate(Q_response) # Computing the correlate of normalised achromatic *Lightness* # :math:`L_n^\\star`. - L_star_N = (normalised_achromatic_lightness_correlate( - B_r, brightness_ideal_white)) + L_star_N = normalised_achromatic_lightness_correlate( + B_r, brightness_ideal_white + ) # Computing the *hue* angle :math:`\\theta`. theta = hue_angle(p_response, t_response) @@ -268,7 +320,8 @@ def XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or, n=1): # Computing the correlate of *saturation* :math:`S`. S_RG, S_YB = tsplit( - saturation_components(theta, bL_or, t_response, p_response)) + saturation_components(theta, bL_or, t_response, p_response) + ) S = saturation_correlate(S_RG, S_YB) # Computing the correlate of *chroma* :math:`C`. @@ -281,25 +334,36 @@ def XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or, n=1): # brightness_ideal_white)) M = colourfulness_correlate(C, brightness_ideal_white) - return CAM_Specification_Nayatani95(L_star_P, C, from_range_degrees(theta), - S, B_r, M, None, None, L_star_N) - - -def illuminance_to_luminance(E, Y_f): + return CAM_Specification_Nayatani95( + L_star_P, + C, + as_float(from_range_degrees(theta)), + S, + B_r, + M, + None, + None, + L_star_N, + ) + + +def illuminance_to_luminance( + E: FloatingOrArrayLike, Y_f: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Converts given *illuminance* :math:`E` value in lux to *luminance* in + Convert given *illuminance* :math:`E` value in lux to *luminance* in :math:`cd/m^2`. Parameters ---------- - E : numeric or array_like + E *Illuminance* :math:`E` in lux. - Y_f : numeric or array_like + Y_f *Luminance* factor :math:`Y_f` in :math:`cd/m^2`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Luminance* :math:`Y` in :math:`cd/m^2`. Examples @@ -314,18 +378,18 @@ def illuminance_to_luminance(E, Y_f): return Y_f * E / (100 * np.pi) -def XYZ_to_RGB_Nayatani95(XYZ): +def XYZ_to_RGB_Nayatani95(XYZ: ArrayLike) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to cone responses. + Convert from *CIE XYZ* tristimulus values to cone responses. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. Returns ------- - ndarray + :class:`numpy.ndarray` Cone responses. Examples @@ -338,20 +402,22 @@ def XYZ_to_RGB_Nayatani95(XYZ): return vector_dot(MATRIX_XYZ_TO_RGB_NAYATANI95, XYZ) -def scaling_coefficient(x, y): +def scaling_coefficient( + x: FloatingOrArrayLike, y: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the scaling coefficient :math:`e(R)` or :math:`e(G)`. + Return the scaling coefficient :math:`e(R)` or :math:`e(G)`. Parameters ---------- - x: numeric or array_like + x Cone response. - y: numeric or array_like + y Intermediate value. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Scaling coefficient :math:`e(R)` or :math:`e(G)`. Examples @@ -359,42 +425,50 @@ def scaling_coefficient(x, y): >>> x = 20.000520600000002 >>> y = 1.000042192 >>> scaling_coefficient(x, y) - array(1.0) + 1.0 """ x = as_float_array(x) y = as_float_array(y) - return np.where(x >= (20 * y), 1.758, 1) + return as_float(np.where(x >= (20 * y), 1.758, 1)) -def achromatic_response(RGB, bRGB_o, xez, bL_or, eR, eG, n=1): +def achromatic_response( + RGB: ArrayLike, + bRGB_o: ArrayLike, + xez: ArrayLike, + bL_or: FloatingOrArrayLike, + eR: FloatingOrArrayLike, + eG: FloatingOrArrayLike, + n: FloatingOrArrayLike = 1, +) -> FloatingOrNDArray: """ - Returns the achromatic response :math:`Q` from given stimulus cone + Return the achromatic response :math:`Q` from given stimulus cone responses. Parameters ---------- - RGB: ndarray + RGB Stimulus cone responses. - bRGB_o: ndarray + bRGB_o Chromatic adaptation exponential factors :math:`\\beta_1(R_o)`, :math:`\\beta_1(G_o)` and :math:`\\beta_2(B_o)`. - xez: ndarray + xez Intermediate values :math:`\\xi`, :math:`\\eta`, :math:`\\zeta`. - bL_or: numeric or array_like + bL_or Normalising chromatic adaptation exponential factor :math:`\\beta_1(B_or)`. - eR: numeric or array_like + eR Scaling coefficient :math:`e(R)`. - eG: numeric or array_like + eG Scaling coefficient :math:`e(G)`. - n : numeric or array_like, optional - Noise term used in the non linear chromatic adaptation model. + n + Noise term used in the non-linear chromatic adaptation model. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Achromatic response :math:`Q`. Examples @@ -422,29 +496,31 @@ def achromatic_response(RGB, bRGB_o, xez, bL_or, eR, eG, n=1): Q += (1 / 3) * bG_o * eG * np.log10((G + n) / (20 * eta + n)) Q *= 41.69 / bL_or - return Q + return as_float(Q) -def tritanopic_response(RGB, bRGB_o, xez, n): +def tritanopic_response( + RGB: ArrayLike, bRGB_o: ArrayLike, xez: ArrayLike, n: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the tritanopic response :math:`t` from given stimulus cone + Return the tritanopic response :math:`t` from given stimulus cone responses. Parameters ---------- - RGB: ndarray + RGB Stimulus cone responses. - bRGB_o: ndarray + bRGB_o Chromatic adaptation exponential factors :math:`\\beta_1(R_o)`, :math:`\\beta_1(G_o)` and :math:`\\beta_2(B_o)`. - xez: ndarray + xez Intermediate values :math:`\\xi`, :math:`\\eta`, :math:`\\zeta`. - n : numeric or array_like, optional - Noise term used in the non linear chromatic adaptation model. + n + Noise term used in the non-linear chromatic adaptation model. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Tritanopic response :math:`t`. Examples @@ -465,29 +541,31 @@ def tritanopic_response(RGB, bRGB_o, xez, n): t += -(12 / 11) * bG_o * np.log10((G + n) / (20 * eta + n)) t += (1 / 11) * bB_o * np.log10((B + n) / (20 * zeta + n)) - return t + return as_float(t) -def protanopic_response(RGB, bRGB_o, xez, n): +def protanopic_response( + RGB: ArrayLike, bRGB_o: ArrayLike, xez: ArrayLike, n: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the protanopic response :math:`p` from given stimulus cone + Return the protanopic response :math:`p` from given stimulus cone responses. Parameters ---------- - RGB: ndarray + RGB Stimulus cone responses. - bRGB_o: ndarray + bRGB_o Chromatic adaptation exponential factors :math:`\\beta_1(R_o)`, :math:`\\beta_1(G_o)` and :math:`\\beta_2(B_o)`. - xez: ndarray + xez Intermediate values :math:`\\xi`, :math:`\\eta`, :math:`\\zeta`. - n : numeric or array_like, optional - Noise term used in the non linear chromatic adaptation model. + n + Noise term used in the non-linear chromatic adaptation model. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Protanopic response :math:`p`. Examples @@ -508,26 +586,29 @@ def protanopic_response(RGB, bRGB_o, xez, n): p += (1 / 9) * bG_o * np.log10((G + n) / (20 * eta + n)) p += -(2 / 9) * bB_o * np.log10((B + n) / (20 * zeta + n)) - return p + return as_float(p) -def brightness_correlate(bRGB_o, bL_or, Q): +def brightness_correlate( + bRGB_o: ArrayLike, bL_or: FloatingOrArrayLike, Q: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the *brightness* correlate :math:`B_r`. + Return the *brightness* correlate :math:`B_r`. Parameters ---------- - bRGB_o: ndarray + bRGB_o Chromatic adaptation exponential factors :math:`\\beta_1(R_o)`, :math:`\\beta_1(G_o)` and :math:`\\beta_2(B_o)`. - bL_or: numeric or array_like + bL_or Normalising chromatic adaptation exponential factor :math:`\\beta_1(B_or)`. - Q : numeric or array_like + Q Achromatic response :math:`Q`. + Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Brightness* correlate :math:`B_r`. Examples @@ -545,29 +626,34 @@ def brightness_correlate(bRGB_o, bL_or, Q): B_r = (50 / bL_or) * ((2 / 3) * bR_o + (1 / 3) * bG_o) + Q - return B_r + return as_float(B_r) -def ideal_white_brightness_correlate(bRGB_o, xez, bL_or, n): +def ideal_white_brightness_correlate( + bRGB_o: ArrayLike, + xez: ArrayLike, + bL_or: FloatingOrArrayLike, + n: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Returns the ideal white *brightness* correlate :math:`B_{rw}`. + Return the ideal white *brightness* correlate :math:`B_{rw}`. Parameters ---------- - bRGB_o: ndarray + bRGB_o Chromatic adaptation exponential factors :math:`\\beta_1(R_o)`, :math:`\\beta_1(G_o)` and :math:`\\beta_2(B_o)`. - xez: ndarray + xez Intermediate values :math:`\\xi`, :math:`\\eta`, :math:`\\zeta`. - bL_or: numeric or array_like + bL_or Normalising chromatic adaptation exponential factor :math:`\\beta_1(B_or)`. - n : numeric or array_like, optional - Noise term used in the non linear chromatic adaptation model. + n + Noise term used in the non-linear chromatic adaptation model. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Ideal white *brightness* correlate :math:`B_{rw}`. Examples @@ -591,21 +677,23 @@ def ideal_white_brightness_correlate(bRGB_o, xez, bL_or, n): B_rw += (50 / bL_or) * (2 / 3) * bR_o B_rw += (50 / bL_or) * (1 / 3) * bG_o - return B_rw + return as_float(B_rw) -def achromatic_lightness_correlate(Q): +def achromatic_lightness_correlate( + Q: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Returns the *achromatic Lightness* correlate :math:`L_p^\\star`. + Return the *achromatic Lightness* correlate :math:`L_p^\\star`. Parameters ---------- - Q : numeric or array_like + Q Achromatic response :math:`Q`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Achromatic Lightness* correlate :math:`L_p^\\star`. Examples @@ -617,23 +705,25 @@ def achromatic_lightness_correlate(Q): Q = as_float_array(Q) - return Q + 50 + return as_float(Q + 50) -def normalised_achromatic_lightness_correlate(B_r, B_rw): +def normalised_achromatic_lightness_correlate( + B_r: FloatingOrArrayLike, B_rw: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the *normalised achromatic Lightness* correlate :math:`L_n^\\star`. + Return the *normalised achromatic Lightness* correlate :math:`L_n^\\star`. Parameters ---------- - B_r : numeric or array_like + B_r *Brightness* correlate :math:`B_r`. - B_rw : numeric or array_like + B_rw Ideal white *brightness* correlate :math:`B_{rw}`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Normalised achromatic Lightness* correlate :math:`L_n^\\star`. Examples @@ -648,23 +738,25 @@ def normalised_achromatic_lightness_correlate(B_r, B_rw): B_r = as_float_array(B_r) B_rw = as_float_array(B_rw) - return 100 * (B_r / B_rw) + return as_float(100 * (B_r / B_rw)) -def hue_angle(p, t): +def hue_angle( + p: FloatingOrArrayLike, t: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the *hue* angle :math:`h` in degrees. + Return the *hue* angle :math:`h` in degrees. Parameters ---------- - p : numeric or array_like + p Protanopic response :math:`p`. - t : numeric or array_like + t Tritanopic response :math:`t`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Hue* angle :math:`h` in degrees. Examples @@ -680,23 +772,25 @@ def hue_angle(p, t): h_L = np.degrees(np.arctan2(p, t)) % 360 - return h_L + return as_float(h_L) -def chromatic_strength_function(theta): +def chromatic_strength_function( + theta: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Defines the chromatic strength function :math:`E_s(\\theta)` used to + Define the chromatic strength function :math:`E_s(\\theta)` used to correct saturation scale as function of hue angle :math:`\\theta` in degrees. Parameters ---------- - theta : numeric or array_like + theta Hue angle :math:`\\theta` in degrees. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Corrected saturation scale. Examples @@ -708,7 +802,7 @@ def chromatic_strength_function(theta): theta = np.radians(theta) - E_s = 0.9394 + E_s = cast(NDArray, 0.9394) E_s += -0.2478 * np.sin(1 * theta) E_s += -0.0743 * np.sin(2 * theta) E_s += +0.0666 * np.sin(3 * theta) @@ -718,28 +812,33 @@ def chromatic_strength_function(theta): E_s += -0.0573 * np.cos(3 * theta) E_s += -0.0061 * np.cos(4 * theta) - return E_s + return as_float(E_s) -def saturation_components(h, bL_or, t, p): +def saturation_components( + h: FloatingOrArrayLike, + bL_or: FloatingOrArrayLike, + t: FloatingOrArrayLike, + p: FloatingOrArrayLike, +) -> NDArray: """ - Returns the *saturation* components :math:`S_{RG}` and :math:`S_{YB}`. + Return the *saturation* components :math:`S_{RG}` and :math:`S_{YB}`. Parameters ---------- - h: numeric or array_like + h Correlate of *hue* :math:`h` in degrees. - bL_or: numeric or array_like + bL_or Normalising chromatic adaptation exponential factor :math:`\\beta_1(B_or)`. - t : numeric or array_like + t Tritanopic response :math:`t`. - p : numeric or array_like + p Protanopic response :math:`p`. Returns ------- - numeric or ndarray + :class:`numpy.ndarray` *Saturation* components :math:`S_{RG}` and :math:`S_{YB}`. Examples @@ -764,20 +863,22 @@ def saturation_components(h, bL_or, t, p): return tstack([S_RG, S_YB]) -def saturation_correlate(S_RG, S_YB): +def saturation_correlate( + S_RG: FloatingOrArrayLike, S_YB: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the correlate of *saturation* :math:`S`. + Return the correlate of *saturation* :math:`S`. Parameters ---------- - S_RG : numeric or array_like + S_RG *Saturation* component :math:`S_{RG}`. - S_YB : numeric or array_like + S_YB *Saturation* component :math:`S_{YB}`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Correlate of *saturation* :math:`S`. Examples @@ -793,25 +894,29 @@ def saturation_correlate(S_RG, S_YB): S = np.hypot(S_RG, S_YB) - return S + return as_float(S) -def chroma_components(L_star_P, S_RG, S_YB): +def chroma_components( + L_star_P: FloatingOrArrayLike, + S_RG: FloatingOrArrayLike, + S_YB: FloatingOrArrayLike, +) -> NDArray: """ - Returns the *chroma* components :math:`C_{RG}` and :math:`C_{YB}`. + Return the *chroma* components :math:`C_{RG}` and :math:`C_{YB}`. Parameters ---------- - L_star_P : numeric or array_like + L_star_P *Achromatic Lightness* correlate :math:`L_p^\\star`. - S_RG : numeric or array_like + S_RG *Saturation* component :math:`S_{RG}`. - S_YB : numeric or array_like + S_YB *Saturation* component :math:`S_{YB}`. Returns ------- - ndarray + :class:`numpy.ndarray` *Chroma* components :math:`C_{RG}` and :math:`C_{YB}`. Examples @@ -833,20 +938,22 @@ def chroma_components(L_star_P, S_RG, S_YB): return tstack([C_RG, C_YB]) -def chroma_correlate(L_star_P, S): +def chroma_correlate( + L_star_P: FloatingOrArrayLike, S: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the correlate of *chroma* :math:`C`. + Return the correlate of *chroma* :math:`C`. Parameters ---------- - L_star_P : numeric or array_like + L_star_P *Achromatic Lightness* correlate :math:`L_p^\\star`. - S : numeric or array_like + S Correlate of *saturation* :math:`S`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Correlate of *chroma* :math:`C`. Examples @@ -865,22 +972,26 @@ def chroma_correlate(L_star_P, S): return C -def colourfulness_components(C_RG, C_YB, B_rw): +def colourfulness_components( + C_RG: FloatingOrArrayLike, + C_YB: FloatingOrArrayLike, + B_rw: FloatingOrArrayLike, +) -> NDArray: """ - Returns the *colourfulness* components :math:`M_{RG}` and :math:`M_{YB}`. + Return the *colourfulness* components :math:`M_{RG}` and :math:`M_{YB}`. Parameters ---------- - C_RG : numeric or array_like + C_RG *Chroma* component :math:`C_{RG}`. - C_YB : numeric or array_like + C_YB *Chroma* component :math:`C_{YB}`. - B_rw : numeric or array_like + B_rw Ideal white *brightness* correlate :math:`B_{rw}`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Colourfulness* components :math:`M_{RG}` and :math:`M_{YB}`. Examples @@ -889,7 +1000,7 @@ def colourfulness_components(C_RG, C_YB, B_rw): >>> C_YB = -0.013039632941332 >>> B_rw = 125.24353925846037 >>> colourfulness_components(C_RG, C_YB, B_rw) # doctest: +ELLIPSIS - (-0.0036136..., -0.0163312...) + array([-0.0036136..., -0.0163313...]) """ C_RG = as_float_array(C_RG) @@ -899,23 +1010,25 @@ def colourfulness_components(C_RG, C_YB, B_rw): M_RG = C_RG * B_rw / 100 M_YB = C_YB * B_rw / 100 - return M_RG, M_YB + return tstack([M_RG, M_YB]) -def colourfulness_correlate(C, B_rw): +def colourfulness_correlate( + C: FloatingOrArrayLike, B_rw: FloatingOrArrayLike +) -> FloatingOrNDArray: """ - Returns the correlate of *colourfulness* :math:`M`. + Return the correlate of *colourfulness* :math:`M`. Parameters ---------- - C : numeric or array_like + C Correlate of *chroma* :math:`C`. - B_rw : numeric or array_like + B_rw Ideal white *brightness* correlate :math:`B_{rw}`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Correlate of *colourfulness* :math:`M`. Examples @@ -931,4 +1044,4 @@ def colourfulness_correlate(C, B_rw): M = C * B_rw / 100 - return M + return as_float(M) diff --git a/colour/appearance/rlab.py b/colour/appearance/rlab.py index ce37741f5a..6f03c7b8e7 100644 --- a/colour/appearance/rlab.py +++ b/colour/appearance/rlab.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ RLAB Colour Appearance Model ============================ -Defines *RLAB* colour appearance model objects: +Defines the *RLAB* colour appearance model objects: - :attr:`colour.VIEWING_CONDITIONS_RLAB` - :attr:`colour.D_FACTOR_RLAB` @@ -19,61 +18,74 @@ Appearance Models (3rd ed., pp. 5563-5824). Wiley. ISBN:B00DAYO8E2 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from collections import namedtuple +from dataclasses import dataclass, field -from colour.algebra import spow +from colour.algebra import matrix_dot, spow, vector_dot from colour.appearance.hunt import MATRIX_XYZ_TO_HPE, XYZ_to_rgb -from colour.utilities import (CaseInsensitiveMapping, as_float_array, - matrix_dot, vector_dot, from_range_degrees, - to_domain_100, tsplit, row_as_diagonal) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import ( + ArrayLike, + FloatingOrArrayLike, + FloatingOrNDArray, + NDArray, + Optional, +) +from colour.utilities import ( + CaseInsensitiveMapping, + MixinDataclassArray, + as_float, + as_float_array, + from_range_degrees, + row_as_diagonal, + to_domain_100, + tsplit, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'MATRIX_R', 'VIEWING_CONDITIONS_RLAB', 'D_FACTOR_RLAB', - 'CAM_ReferenceSpecification_RLAB', 'CAM_Specification_RLAB', 'XYZ_to_RLAB' + "MATRIX_R", + "VIEWING_CONDITIONS_RLAB", + "D_FACTOR_RLAB", + "CAM_ReferenceSpecification_RLAB", + "CAM_Specification_RLAB", + "XYZ_to_RLAB", ] -MATRIX_R = np.array([ - [1.9569, -1.1882, 0.2313], - [0.3612, 0.6388, 0.0000], - [0.0000, 0.0000, 1.0000], -]) -""" -*RLAB* colour appearance model precomputed helper matrix. - -MATRIX_R : array_like, (3, 3) -""" - -VIEWING_CONDITIONS_RLAB = CaseInsensitiveMapping({ - 'Average': 1 / 2.3, - 'Dim': 1 / 2.9, - 'Dark': 1 / 3.5 -}) +MATRIX_R: NDArray = np.array( + [ + [1.9569, -1.1882, 0.2313], + [0.3612, 0.6388, 0.0000], + [0.0000, 0.0000, 1.0000], + ] +) +"""*RLAB* colour appearance model precomputed helper matrix.""" + +VIEWING_CONDITIONS_RLAB: CaseInsensitiveMapping = CaseInsensitiveMapping( + {"Average": 1 / 2.3, "Dim": 1 / 2.9, "Dark": 1 / 3.5} +) VIEWING_CONDITIONS_RLAB.__doc__ = """ Reference *RLAB* colour appearance model viewing conditions. References ---------- :cite:`Fairchild1996a`, :cite:`Fairchild2013w` - -VIEWING_CONDITIONS_RLAB : CaseInsensitiveMapping - **{'Average', 'Dim', 'Dark'}** """ -D_FACTOR_RLAB = CaseInsensitiveMapping({ - 'Hard Copy Images': 1, - 'Soft Copy Images': 0, - 'Projected Transparencies, Dark Room': 0.5 -}) +D_FACTOR_RLAB: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Hard Copy Images": 1, + "Soft Copy Images": 0, + "Projected Transparencies, Dark Room": 0.5, + } +) D_FACTOR_RLAB.__doc__ = """ *RLAB* colour appearance model *Discounting-the-Illuminant* factor values. @@ -81,47 +93,42 @@ ---------- :cite:`Fairchild1996a`, :cite:`Fairchild2013w` -D_FACTOR_RLAB : CaseInsensitiveMapping - **{'Hard Copy Images', - 'Soft Copy Images', - 'Projected Transparencies, Dark Room'}** - Aliases: - 'hard_cp_img': 'Hard Copy Images' - 'soft_cp_img': 'Soft Copy Images' - 'projected_dark': 'Projected Transparencies, Dark Room' """ -D_FACTOR_RLAB['hard_cp_img'] = D_FACTOR_RLAB['Hard Copy Images'] -D_FACTOR_RLAB['soft_cp_img'] = D_FACTOR_RLAB['Soft Copy Images'] -D_FACTOR_RLAB['projected_dark'] = ( - D_FACTOR_RLAB['Projected Transparencies, Dark Room']) +D_FACTOR_RLAB["hard_cp_img"] = D_FACTOR_RLAB["Hard Copy Images"] +D_FACTOR_RLAB["soft_cp_img"] = D_FACTOR_RLAB["Soft Copy Images"] +D_FACTOR_RLAB["projected_dark"] = D_FACTOR_RLAB[ + "Projected Transparencies, Dark Room" +] -class CAM_ReferenceSpecification_RLAB( - namedtuple('CAM_ReferenceSpecification_RLAB', - ('LR', 'CR', 'hR', 'sR', 'HR', 'aR', 'bR'))): +@dataclass +class CAM_ReferenceSpecification_RLAB(MixinDataclassArray): """ - Defines the *RLAB* colour appearance model reference specification. + Define the *RLAB* colour appearance model reference specification. This specification has field names consistent with *Fairchild (2013)* reference. Parameters ---------- - LR : numeric or array_like + LR Correlate of *Lightness* :math:`L^R`. - CR : numeric or array_like + CR Correlate of *achromatic chroma* :math:`C^R`. - hR : numeric or array_like + hR *Hue* angle :math:`h^R` in degrees. - sR : numeric or array_like + sR Correlate of *saturation* :math:`s^R`. - HR : numeric or array_like + HR *Hue* :math:`h` composition :math:`H^R`. - aR : numeric or array_like + aR Red-green chromatic response :math:`a^R`. - bR : numeric or array_like + bR Yellow-blue chromatic response :math:`b^R`. References @@ -129,12 +136,19 @@ class CAM_ReferenceSpecification_RLAB( :cite:`Fairchild1996a`, :cite:`Fairchild2013w` """ + LR: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + CR: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + hR: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + sR: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + HR: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + aR: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + bR: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + -class CAM_Specification_RLAB( - namedtuple('CAM_Specification_RLAB', - ('J', 'C', 'h', 's', 'HC', 'a', 'b'))): +@dataclass +class CAM_Specification_RLAB(MixinDataclassArray): """ - Defines the *RLAB* colour appearance model specification. + Define the *RLAB* colour appearance model specification. This specification has field names consistent with the remaining colour appearance models in :mod:`colour.appearance` but diverge from @@ -142,19 +156,19 @@ class CAM_Specification_RLAB( Parameters ---------- - J : numeric or array_like + J Correlate of *Lightness* :math:`L^R`. - C : numeric or array_like + C Correlate of *achromatic chroma* :math:`C^R`. - h : numeric or array_like + h *Hue* angle :math:`h^R` in degrees. - s : numeric or array_like + s Correlate of *saturation* :math:`s^R`. - HC : numeric or array_like + HC *Hue* :math:`h` composition :math:`H^C`. - a : numeric or array_like + a Red-green chromatic response :math:`a^R`. - b : numeric or array_like + b Yellow-blue chromatic response :math:`b^R`. Notes @@ -166,27 +180,37 @@ class CAM_Specification_RLAB( :cite:`Fairchild1996a`, :cite:`Fairchild2013w` """ - -def XYZ_to_RLAB(XYZ, - XYZ_n, - Y_n, - sigma=VIEWING_CONDITIONS_RLAB['Average'], - D=D_FACTOR_RLAB['Hard Copy Images']): + J: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + C: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + h: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + s: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + HC: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + a: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + b: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + + +def XYZ_to_RLAB( + XYZ: ArrayLike, + XYZ_n: ArrayLike, + Y_n: FloatingOrArrayLike, + sigma: FloatingOrArrayLike = VIEWING_CONDITIONS_RLAB["Average"], + D: FloatingOrArrayLike = D_FACTOR_RLAB["Hard Copy Images"], +) -> CAM_Specification_RLAB: """ - Computes the *RLAB* model color appearance correlates. + Compute the *RLAB* model color appearance correlates. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values of test sample / stimulus. - XYZ_n : array_like + XYZ_n *CIE XYZ* tristimulus values of reference white. - Y_n : numeric or array_like + Y_n Absolute adapting luminance in :math:`cd/m^2`. - sigma : numeric or array_like, optional + sigma Relative luminance of the surround, see :attr:`colour.VIEWING_CONDITIONS_RLAB` for reference. - D : numeric or array_like, optional + D *Discounting-the-Illuminant* factor normalised to domain [0, 1]. Returns @@ -196,14 +220,13 @@ def XYZ_to_RLAB(XYZ, Notes ----- - - +--------------------------+-----------------------+---------------+ - | **Domain** | **Scale - Reference** | **Scale - 1** | - +==========================+=======================+===============+ - | ``XYZ`` | [0, 100] | [0, 1] | - +--------------------------+-----------------------+---------------+ - | ``XYZ_n`` | [0, 100] | [0, 1] | - +--------------------------+-----------------------+---------------+ + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``XYZ`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ + | ``XYZ_n`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ +------------------------------+-----------------------\ +---------------+ @@ -229,7 +252,8 @@ def XYZ_to_RLAB(XYZ, >>> D = D_FACTOR_RLAB['Hard Copy Images'] >>> XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma, D) # doctest: +ELLIPSIS CAM_Specification_RLAB(J=49.8347069..., C=54.8700585..., \ -h=286.4860208..., s=1.1010410..., HC=None, a=15.5711021..., b=-52.6142956...) +h=286.4860208..., s=1.1010410..., HC=None, a=15.5711021..., \ +b=-52.6142956...) """ XYZ = to_domain_100(XYZ) @@ -242,13 +266,15 @@ def XYZ_to_RLAB(XYZ, LMS_n = XYZ_to_rgb(XYZ_n) # Computing the :math:`A` matrix. - LMS_l_E = (3 * LMS_n) / (LMS_n[0] + LMS_n[1] + LMS_n[2]) - LMS_p_L = ((1 + spow(Y_n[..., np.newaxis], 1 / 3) + LMS_l_E) / - (1 + spow(Y_n[..., np.newaxis], 1 / 3) + (1 / LMS_l_E))) + LMS_l_E = (3 * LMS_n) / np.sum(LMS_n, axis=-1)[..., np.newaxis] + LMS_p_L = (1 + spow(Y_n[..., np.newaxis], 1 / 3) + LMS_l_E) / ( + 1 + spow(Y_n[..., np.newaxis], 1 / 3) + (1 / LMS_l_E) + ) LMS_a_L = (LMS_p_L + D[..., np.newaxis] * (1 - LMS_p_L)) / LMS_n - aR = row_as_diagonal(LMS_a_L) - M = matrix_dot(matrix_dot(MATRIX_R, aR), MATRIX_XYZ_TO_HPE) + M = matrix_dot( + matrix_dot(MATRIX_R, row_as_diagonal(LMS_a_L)), MATRIX_XYZ_TO_HPE + ) XYZ_ref = vector_dot(M, XYZ) X_ref, Y_ref, Z_ref = tsplit(XYZ_ref) @@ -257,8 +283,8 @@ def XYZ_to_RLAB(XYZ, LR = 100 * spow(Y_ref, sigma) # Computing opponent colour dimensions :math:`a^R` and :math:`b^R`. - aR = 430 * (spow(X_ref, sigma) - spow(Y_ref, sigma)) - bR = 170 * (spow(Y_ref, sigma) - spow(Z_ref, sigma)) + aR = as_float(430 * (spow(X_ref, sigma) - spow(Y_ref, sigma))) + bR = as_float(170 * (spow(Y_ref, sigma) - spow(Z_ref, sigma))) # Computing the *hue* angle :math:`h^R`. hR = np.degrees(np.arctan2(bR, aR)) % 360 @@ -270,5 +296,12 @@ def XYZ_to_RLAB(XYZ, # Computing the correlate of *saturation* :math:`s^R`. sR = CR / LR - return CAM_Specification_RLAB(LR, CR, from_range_degrees(hR), sR, None, aR, - bR) + return CAM_Specification_RLAB( + LR, + CR, + as_float(from_range_degrees(hR)), + sR, + None, + aR, + bR, + ) diff --git a/colour/appearance/tests/__init__.py b/colour/appearance/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/appearance/tests/__init__.py +++ b/colour/appearance/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/appearance/tests/common.py b/colour/appearance/tests/common.py deleted file mode 100644 index a5c970a706..0000000000 --- a/colour/appearance/tests/common.py +++ /dev/null @@ -1,217 +0,0 @@ -# !/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Defines the common unit tests objects for :mod:`colour.appearance` package. -""" - -from __future__ import division, unicode_literals - -import csv -import numpy as np -import os -from abc import ABCMeta, abstractmethod -from collections import defaultdict - -from colour.constants import DEFAULT_FLOAT_DTYPE - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['ColourAppearanceModelTest'] - - -class ColourAppearanceModelTest(object): - """ - Defines the base class for tests of: mod:`colour.appearance` package. - - Each colour appearance model is tested against a respective '.csv' file - whose content has been generated from data of the following file by - *Fairchild (2013)*: http://rit-mcsl.org/fairchild//files/AppModEx.xls - - Methods - ------- - - :meth:`~colour.apperance.tests.ColourAppearanceModelTest.load_fixtures` - - :meth:`~colour.apperance.tests.ColourAppearanceModelTest.\ -output_specification_from_data` - - :meth:`~colour.apperance.tests.ColourAppearanceModelTest.\ -check_specification_attribute` - - :meth:`~colour.apperance.tests.ColourAppearanceModelTest.\ -check_model_consistency` - - :meth:`~colour.apperance.tests.ColourAppearanceModelTest.\ -test_forward_examples` - """ - - __metaclass__ = ABCMeta - - FIXTURE_BASENAME = None - """ - '.csv' file fixture path for the colour appearance model being tested, - must be reimplemented by each colour appearance model test sub-class. - """ - - OUTPUT_ATTRIBUTES = None - """ - Binding the fixture attributes to the colour appearance model - specification attributes, must be reimplemented by each colour appearance - model test sub-class. - """ - - @staticmethod - def load_fixtures(file_name, fixtures_directory='fixtures'): - """ - Loads the fixtures data with given name. - - Parameters - ---------- - file_name : unicode - '.csv' fixture file name. - fixtures_directory : unicode - Relative directory path containing the '.csv' fixtures files. - - Returns - ------- - list - Fixtures data as a *list* of *dict* where each *dict* is a fixture - case. - """ - - path = os.path.dirname(__file__) - with open(os.path.join(path, fixtures_directory, - file_name)) as in_file: - result = [] - for case_data in csv.DictReader(in_file): - for key in case_data: - try: - case_data[key] = DEFAULT_FLOAT_DTYPE(case_data[key]) - except ValueError: - pass - result.append(case_data) - return result - - @abstractmethod - def output_specification_from_data(self, data): - """ - Returns the colour appearance model output specification from given - fixture data. - - Parameters - ---------- - data : list - Tested colour appearance model fixture data. - - Returns - ------- - *_Specification - Tested colour appearance model specification. - """ - - pass - - def check_specification_attribute(self, case, data, attribute, expected): - """ - Tests given colour appearance model specification attribute value. - - Parameters - ---------- - case : int - Fixture case number. - data : dict. - Fixture case data. - attribute : unicode. - Tested attribute name. - expected : numeric. - Expected attribute value. - - Returns - ------- - None - """ - - specification = self.output_specification_from_data(data) - value = getattr(specification, attribute) - - error_message = ( - 'Parameter "{0}" in test case "{1}" does not match target value.\n' - 'Expected: "{2}" \n' - 'Received "{3}"').format(attribute, case, expected, value) - - np.testing.assert_allclose( - value, - expected, - err_msg=error_message, - rtol=0.01, - atol=0.01, - verbose=False) - - np.testing.assert_almost_equal( - value, expected, decimal=1, err_msg=error_message) - - def check_model_consistency(self, data, output_attributes): - """ - Checks the colour appearance model consistency with the tested colour - appearance model fixture case data. - - Parameters - ---------- - data : list - Tested model fixture case data. - output_attributes : dict. - Fixture case data parameters to the colour appearance model - specification output binding. - - Returns - ------- - tuple - """ - - for data_attr, specification_attr in sorted(output_attributes.items()): - self.check_specification_attribute( - data.get('Case'), data, specification_attr, data[data_attr]) - - def fixtures(self): - """ - Returns the fixtures case for tested colour appearance model.. - - Returns - ------- - list - Filtered fixtures case data. - """ - - return self.load_fixtures(self.FIXTURE_BASENAME) - - def test_examples(self): - """ - Tests the colour appearance model implementation. - - Returns - ------- - tuple - """ - - for data in self.fixtures(): - self.check_model_consistency(data, self.OUTPUT_ATTRIBUTES) - - def test_n_dimensional_examples(self): - """ - Tests the colour appearance model implementation n-dimensional arrays - support. - - Returns - ------- - tuple - """ - - data = defaultdict(list) - for fixture in self.fixtures(): - for key, value in fixture.items(): - data[key].append(value) - - for key in data: - data[key] = np.array(data[key]) - - self.check_model_consistency(data, self.OUTPUT_ATTRIBUTES) diff --git a/colour/appearance/tests/fixtures/atd95.csv b/colour/appearance/tests/fixtures/atd95.csv deleted file mode 100644 index 49f6fae2eb..0000000000 --- a/colour/appearance/tests/fixtures/atd95.csv +++ /dev/null @@ -1,5 +0,0 @@ -Case,X,Y,Z,X_0,Y_0,Z_0,Y_02,sigma,K_1,K_2,A_1,T_1,D_1,A_2,T_2,D_2,Br,C,H -1,19.01,20,21.78,95.05,100,108.88,318.31,300,0,50,0.1788,0.0287,0.0108,0.0192,0.0205,0.0108,0.1814,1.206,1.91 -2,57.06,43.06,31.96,95.05,100,108.88,31.83,300,0,50,0.2031,0.068,0.0005,0.0224,0.0308,0.0005,0.2142,1.371,63.96 -3,3.53,6.56,2.14,109.85,100,35.58,318.31,300,0,50,0.1068,-0.011,0.0044,0.0106,-0.0014,0.0044,0.1075,0.436,-0.31 -4,19.01,20,21.78,109.85,100,35.58,31.83,300,0,50,0.146,0.0007,0.013,0.0152,0.0102,0.013,0.1466,1.091,0.79 diff --git a/colour/appearance/tests/fixtures/cam16.csv b/colour/appearance/tests/fixtures/cam16.csv deleted file mode 100644 index 8642cbd3cf..0000000000 --- a/colour/appearance/tests/fixtures/cam16.csv +++ /dev/null @@ -1,6 +0,0 @@ -Case,X,Y,Z,X_w,Y_w,Z_w,L_A,Y_b,F,c,N_c,J,C,h,s,Q,M,H -1,19.01,20,21.78,95.05,100,108.88,318.31,20,1,0.69,1,41.731207905126638,0.10335573870906986,217.06795976739301,2.3450150729795514,195.37170899282242,0.10743677233590453,275.59498614520169 -2,57.06,43.06,31.96,95.05,100,108.88,31.83,20,1,0.69,1,65.428280687118487,49.679564197569391,17.486592427576902,52.943088676264715,152.06985267941533,42.624733207401754,398.03047943444096 -3,3.53,6.56,2.14,109.85,100,35.58,318.31,20,1,0.69,1,21.360528925833023,50.99381895244953,178.86724265889916,61.579530918782368,139.7858276764222,53.007325822072467,223.01823805698746 -4,19.01,20,21.78,109.85,100,35.58,31.83,20,1,0.69,1,41.065617863199705,49.085994083065998,259.03370342099356,59.086460362422713,120.63283635524722,42.115454026328912,311.32917536350629 -5,61.45276998,7.00421901,82.24067384,95.05,100,108.88,4.074366543152521,20,1,0.69,1,21.038019570427817,457.78881605029704,350.06445097569986,241.50642843725919,56.741439883697304,330.94646230955709,376.43915876803396 diff --git a/colour/appearance/tests/fixtures/ciecam02.csv b/colour/appearance/tests/fixtures/ciecam02.csv deleted file mode 100644 index 41a81bb364..0000000000 --- a/colour/appearance/tests/fixtures/ciecam02.csv +++ /dev/null @@ -1,6 +0,0 @@ -Case,X,Y,Z,X_w,Y_w,Z_w,L_A,F,D,Y_b,N_c,F_L,N_bb,N_cb,h,H,H_C,J,Q,s,C,M,a_c,b_c,a_M,b_M,a_s,b_s,c -1,19.01,20,21.78,95.05,100,108.88,318.31,1,0.994,20,1,1.17,1,1,219,278.1,"78B 22G",41.73,195.37,2.36,0.1,0.11,-0.08,-0.07,-0.08,-0.07,-1.83,-1.49,0.69 -2,57.06,43.06,31.96,95.05,100,108.88,31.83,1,0.875,20,1,0.54,1,1,19.6,399.6,100R,65.96,152.67,52.25,48.57,41.67,45.77,16.26,39.27,13.95,49.23,17.49,0.69 -3,3.53,6.56,2.14,109.85,100,35.58,318.31,1,0.994,20,1,1.17,1,1,177.1,220.4,"80G 20B",21.79,141.17,58.79,46.94,48.8,-46.89,2.34,-48.74,2.43,-58.72,2.93,0.69 -4,19.01,20,21.78,109.85,100,35.58,31.83,1,0.875,20,1,0.54,1,1,248.9,305.8,94B6R,42.53,122.83,60.22,51.92,44.54,-18.69,-48.44,-16.03,-41.56,-21.67,-56.18,0.69 -5,61.45276998,7.00421901,82.24067384,95.05,100,108.88,4.074366543152521,1,-1,20,1,-1,-1,-1,349.12875710099053,375.5788601911363,-1,21.72630603341673,57.657243286322725,227.15081998415593,411.5190338631848,297.49693233026602,-1,-1,-1,-1,-1,-1,0.69 diff --git a/colour/appearance/tests/fixtures/hunt.csv b/colour/appearance/tests/fixtures/hunt.csv deleted file mode 100644 index 65d17a484c..0000000000 --- a/colour/appearance/tests/fixtures/hunt.csv +++ /dev/null @@ -1,5 +0,0 @@ -Case,X,Y,Z,X_w,Y_w,Z_w,L_A,N_c,N_b,Discounting,h_S,H,s,Q,J,C_94,M94,T -1,19.01,20,21.78,95.05,100,108.88,318.31,1,75,1,269.3,317.2,0.03,31.92,42.12,0.16,0.16,6504 -2,57.06,43.06,31.96,95.05,100,108.88,31.83,1,75,1,18.6,398.8,153.36,31.22,66.76,63.89,58.28,6504 -3,3.53,6.56,2.14,109.85,100,35.58,318.31,1,75,1,178.3,222.2,245.4,18.9,19.56,74.58,76.33,2856 -4,19.01,20,21.78,109.84,100,35.58,31.83,1,75,1,262.8,313.4,209.29,22.15,40.27,73.84,67.35,2856 diff --git a/colour/appearance/tests/fixtures/llab.csv b/colour/appearance/tests/fixtures/llab.csv deleted file mode 100644 index 93d4980b47..0000000000 --- a/colour/appearance/tests/fixtures/llab.csv +++ /dev/null @@ -1,5 +0,0 @@ -Case,X,Y,Z,X_0,Y_0,Z_0,L,Y_b,F_S,F_L,F_C,L_L,Ch_L,C_L,s_L,h_L,A_L,B_L -1,19.01,20,21.78,95.05,100,108.88,318.31,20,3,1,1,37.37,0.01,0.02,0,229.5,-0.01,-0.01 -2,57.06,43.06,31.96,95.05,100,108.88,31.83,20,3,1,1,61.26,30.51,56.55,0.5,22.3,52.33,21.43 -3,3.53,6.56,2.14,109.85,100,35.58,318.31,20,3,1,1,16.25,30.43,53.83,1.87,173.8,-53.51,5.83 -4,19.01,20,21.78,109.85,100,35.58,31.83,20,3,1,1,39.82,29.34,54.59,0.74,271.9,1.76,-54.56 diff --git a/colour/appearance/tests/fixtures/nayatani95.csv b/colour/appearance/tests/fixtures/nayatani95.csv deleted file mode 100644 index 3195deb5e3..0000000000 --- a/colour/appearance/tests/fixtures/nayatani95.csv +++ /dev/null @@ -1,5 +0,0 @@ -Case,X,Y,Z,X_n,Y_n,Z_n,E_o,E_or,B_r,L_star_P,L_star_N,theta,H,S,C,M,Y_o -1,19.01,20,21.78,95.05,100,108.88,5000,1000,62.6,50,50,257.5,317.8,0.01,0.01,0.02,20 -2,57.06,43.06,31.96,95.05,100,108.88,500,1000,67.3,73,75.9,21.6,2.1,37.1,48.3,42.9,20 -3,3.53,6.56,2.14,109.85,100,35.58,5000,1000,37.5,24.5,29.7,190.6,239.4,81.3,49.3,62.1,20 -4,19.01,20,21.78,109.85,100,35.58,500,1000,44.2,49.4,49.4,236.3,303.6,40.2,39.9,35.8,20 diff --git a/colour/appearance/tests/fixtures/rlab.csv b/colour/appearance/tests/fixtures/rlab.csv deleted file mode 100644 index 6db4ae6c79..0000000000 --- a/colour/appearance/tests/fixtures/rlab.csv +++ /dev/null @@ -1,5 +0,0 @@ -Case,X,Y,Z,X_n,Y_n,Z_n,Y_n2,sigma,D,LR,aR,bR,hR,CR,sR -1,19.01,20,21.78,95.05,100,108.88,318.31,0.4347,1,49.67,0,-0.01,270,0.01,0 -2,57.06,43.06,31.96,95.05,100,108.88,31.83,0.4347,1,69.33,46.33,18.09,21.3,49.74,0.72 -3,3.53,6.56,2.14,109.85,100,35.58,318.31,0.4347,1,30.78,-40.96,2.25,176.9,41.02,1.33 -4,19.01,20,21.78,109.85,100,35.58,31.83,0.4347,1,49.83,15.57,-52.61,286.5,54.87,1.1 diff --git a/colour/appearance/tests/test_atd95.py b/colour/appearance/tests/test_atd95.py index 9486f36612..4e0d926a1e 100644 --- a/colour/appearance/tests/test_atd95.py +++ b/colour/appearance/tests/test_atd95.py @@ -1,76 +1,177 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.appearance.atd95` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.appearance.atd95` module.""" import numpy as np +import unittest from itertools import permutations from colour.appearance import XYZ_to_ATD95 -from colour.appearance.tests.common import ColourAppearanceModelTest -from colour.utilities import domain_range_scale, ignore_numpy_errors, tstack +from colour.utilities import ( + as_float_array, + domain_range_scale, + ignore_numpy_errors, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestATD95ColourAppearanceModel'] +__all__ = [ + "TestXYZ_to_ATD95", +] -class TestATD95ColourAppearanceModel(ColourAppearanceModelTest): +class TestXYZ_to_ATD95(unittest.TestCase): """ - Defines :mod:`colour.appearance.atd95` module unit tests methods for - *ATD (1995)* colour vision model. + Define :func:`colour.appearance.atd95.XYZ_to_ATD95` definition unit + tests methods. """ - FIXTURE_BASENAME = 'atd95.csv' - - OUTPUT_ATTRIBUTES = { - 'H': 'h', - 'C': 'C', - 'Br': 'Q', - 'A_1': 'A_1', - 'T_1': 'T_1', - 'D_1': 'D_1', - 'A_2': 'A_2', - 'T_2': 'T_2', - 'D_2': 'D_2' - } - - def output_specification_from_data(self, data): + def test_XYZ_to_ATD95(self): """ - Returns the *ATD (1995)* colour vision model output specification from - given data. - - Parameters - ---------- - data : list - Fixture data. - - Returns - ------- - CAM_Specification_ATD95 - *ATD (1995)* colour vision model specification. + Test :func:`colour.appearance.atd95.XYZ_to_ATD95` definition. + + Notes + ----- + - The test values have been generated from data of the following file + by *Fairchild (2013)*: + http://rit-mcsl.org/fairchild//files/AppModEx.xls """ - XYZ = tstack([data['X'], data['Y'], data['Z']]) - XYZ_0 = tstack([data['X_0'], data['Y_0'], data['Z_0']]) + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_0 = np.array([95.05, 100.00, 108.88]) + Y_02 = 318.31 + K_1 = 0 + K_2 = 50 + sigma = 300 + np.testing.assert_allclose( + XYZ_to_ATD95(XYZ, XYZ_0, Y_02, K_1, K_2, sigma), + np.array( + [ + 1.91, + 1.206, + 0.1814, + 0.1788, + 0.0287, + 0.0108, + 0.0192, + 0.0205, + 0.0108, + ] + ), + rtol=0.01, + atol=0.01, + ) - specification = XYZ_to_ATD95(XYZ, XYZ_0, data['Y_02'], data['K_1'], - data['K_2'], data['sigma']) + XYZ = np.array([57.06, 43.06, 31.96]) + Y_02 = 31.83 + np.testing.assert_allclose( + XYZ_to_ATD95(XYZ, XYZ_0, Y_02, K_1, K_2, sigma), + np.array( + [ + 63.96, + 1.371, + 0.2142, + 0.2031, + 0.068, + 0.0005, + 0.0224, + 0.0308, + 0.0005, + ] + ), + rtol=0.01, + atol=0.01, + ) + + XYZ = np.array([3.53, 6.56, 2.14]) + XYZ_0 = np.array([109.85, 100.00, 35.58]) + Y_02 = 318.31 + np.testing.assert_allclose( + XYZ_to_ATD95(XYZ, XYZ_0, Y_02, K_1, K_2, sigma), + np.array( + [ + -0.31, + 0.436, + 0.1075, + 0.1068, + -0.011, + 0.0044, + 0.0106, + -0.0014, + 0.0044, + ] + ), + rtol=0.01, + atol=0.01, + ) + + XYZ = np.array([19.01, 20.00, 21.78]) + Y_02 = 31.83 + np.testing.assert_allclose( + XYZ_to_ATD95(XYZ, XYZ_0, Y_02, K_1, K_2, sigma), + np.array( + [ + 0.79, + 1.091, + 0.1466, + 0.146, + 0.0007, + 0.013, + 0.0152, + 0.0102, + 0.013, + ] + ), + rtol=0.01, + atol=0.01, + ) - return specification + def test_n_dimensional_XYZ_to_ATD95(self): + """ + Test :func:`colour.appearance.atd95.XYZ_to_ATD95` definition + n-dimensional support. + """ + + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_0 = np.array([95.05, 100.00, 108.88]) + Y_02 = 318.31 + K_1 = 0 + K_2 = 50 + sigma = 300 + specification = XYZ_to_ATD95(XYZ, XYZ_0, Y_02, K_1, K_2, sigma) + + XYZ = np.tile(XYZ, (6, 1)) + specification = np.tile(specification, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_ATD95(XYZ, XYZ_0, Y_02, K_1, K_2, sigma), + specification, + decimal=7, + ) + + XYZ_0 = np.tile(XYZ_0, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_ATD95(XYZ, XYZ_0, Y_02, K_1, K_2, sigma), + specification, + decimal=7, + ) + + XYZ = np.reshape(XYZ, (2, 3, 3)) + XYZ_0 = np.reshape(XYZ_0, (2, 3, 3)) + specification = np.reshape(specification, (2, 3, 9)) + np.testing.assert_almost_equal( + XYZ_to_ATD95(XYZ, XYZ_0, Y_02, K_1, K_2, sigma), + specification, + decimal=7, + ) @ignore_numpy_errors def test_domain_range_scale_XYZ_to_ATD95(self): """ - Tests :func:`colour.appearance.atd95.XYZ_to_ATD95` definition domain + Test :func:`colour.appearance.atd95.XYZ_to_ATD95` definition domain and range scale support. """ @@ -82,22 +183,24 @@ def test_domain_range_scale_XYZ_to_ATD95(self): specification = XYZ_to_ATD95(XYZ, XYZ_0, Y_0, k_1, k_2) d_r = ( - ('reference', 1, 1), - (1, 0.01, np.array([1 / 360, 1, 1, 1, 1, 1, 1, 1, 1])), - (100, 1, np.array([100 / 360, 1, 1, 1, 1, 1, 1, 1, 1])), + ("reference", 1, 1), + ("1", 0.01, np.array([1 / 360, 1, 1, 1, 1, 1, 1, 1, 1])), + ("100", 1, np.array([100 / 360, 1, 1, 1, 1, 1, 1, 1, 1])), ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - XYZ_to_ATD95(XYZ * factor_a, XYZ_0 * factor_a, Y_0, k_1, - k_2), - specification * factor_b, - decimal=7) + XYZ_to_ATD95( + XYZ * factor_a, XYZ_0 * factor_a, Y_0, k_1, k_2 + ), + as_float_array(specification) * factor_b, + decimal=7, + ) @ignore_numpy_errors def test_nan_XYZ_to_ATD95(self): """ - Tests :func:`colour.appearance.atd95.XYZ_to_ATD95` definition nan + Test :func:`colour.appearance.atd95.XYZ_to_ATD95` definition nan support. """ diff --git a/colour/appearance/tests/test_cam16.py b/colour/appearance/tests/test_cam16.py index 991c0830ec..8f3dfac422 100644 --- a/colour/appearance/tests/test_cam16.py +++ b/colour/appearance/tests/test_cam16.py @@ -1,115 +1,235 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.appearance.cam16` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.appearance.cam16` module.""" import numpy as np +import unittest from itertools import permutations -from colour.appearance import (VIEWING_CONDITIONS_CAM16, - InductionFactors_CAM16, CAM_Specification_CAM16, - XYZ_to_CAM16, CAM16_to_XYZ) -from colour.appearance.tests.common import ColourAppearanceModelTest -from colour.utilities import (as_namedtuple, domain_range_scale, - ignore_numpy_errors, tsplit, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.appearance import ( + VIEWING_CONDITIONS_CAM16, + InductionFactors_CAM16, + CAM_Specification_CAM16, + XYZ_to_CAM16, + CAM16_to_XYZ, +) +from colour.utilities import ( + as_float_array, + domain_range_scale, + ignore_numpy_errors, + tsplit, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'TestCAM16ColourAppearanceModelForward', - 'TestCAM16ColourAppearanceModelInverse' + "TestXYZ_to_CAM16", + "TestCAM16_to_XYZ", ] -class TestCAM16ColourAppearanceModelForward(ColourAppearanceModelTest): +class TestXYZ_to_CAM16(unittest.TestCase): """ - Defines :mod:`colour.appearance.cam16` module units tests methods for - *CAM16* colour appearance model forward implementation. + Define :func:`colour.appearance.cam16.XYZ_to_CAM16` definition unit + tests methods. """ - # TODO: The current fixture data is generated from direct computations - # using our model implementation. We have asked ground truth data to - # Li et al. (2016) and will update the "cam16.csv" file accordingly - # whenever we receive it. - FIXTURE_BASENAME = 'cam16.csv' - - OUTPUT_ATTRIBUTES = { - 'J': 'J', - 'C': 'C', - 'h': 'h', - 's': 's', - 'Q': 'Q', - 'M': 'M', - 'H': 'H' - } - - def output_specification_from_data(self, data): + def test_XYZ_to_CAM16(self): + """Test :func:`colour.appearance.cam16.XYZ_to_CAM16` definition.""" + + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_w = np.array([95.05, 100.00, 108.88]) + L_A = 318.31 + Y_b = 20 + surround = VIEWING_CONDITIONS_CAM16["Average"] + np.testing.assert_almost_equal( + XYZ_to_CAM16(XYZ, XYZ_w, L_A, Y_b, surround), + np.array( + [ + 41.73120791, + 0.10335574, + 217.06795977, + 2.34501507, + 195.37170899, + 0.10743677, + 275.59498615, + np.nan, + ] + ), + decimal=7, + ) + + XYZ = np.array([57.06, 43.06, 31.96]) + L_A = 31.83 + np.testing.assert_almost_equal( + XYZ_to_CAM16(XYZ, XYZ_w, L_A, Y_b, surround), + np.array( + [ + 65.42828069, + 49.67956420, + 17.48659243, + 52.94308868, + 152.06985268, + 42.62473321, + 398.03047943, + np.nan, + ] + ), + decimal=7, + ) + + XYZ = np.array([3.53, 6.56, 2.14]) + XYZ_w = np.array([109.85, 100, 35.58]) + L_A = 318.31 + np.testing.assert_almost_equal( + XYZ_to_CAM16(XYZ, XYZ_w, L_A, Y_b, surround), + np.array( + [ + 21.36052893, + 50.99381895, + 178.86724266, + 61.57953092, + 139.78582768, + 53.00732582, + 223.01823806, + np.nan, + ] + ), + decimal=7, + ) + + XYZ = np.array([19.01, 20.00, 21.78]) + L_A = 318.31 + np.testing.assert_almost_equal( + XYZ_to_CAM16(XYZ, XYZ_w, L_A, Y_b, surround), + np.array( + [ + 41.36326063, + 52.81154022, + 258.88676291, + 53.12406914, + 194.52011798, + 54.89682038, + 311.24768647, + np.nan, + ] + ), + decimal=7, + ) + + XYZ = np.array([61.45276998, 7.00421901, 82.2406738]) + XYZ_w = np.array([95.05, 100.00, 108.88]) + L_A = 4.074366543152521 + np.testing.assert_almost_equal( + XYZ_to_CAM16(XYZ, XYZ_w, L_A, Y_b, surround), + np.array( + [ + 21.03801957, + 457.78881613, + 350.06445098, + 241.50642846, + 56.74143988, + 330.94646237, + 376.43915877, + np.nan, + ] + ), + decimal=7, + ) + + def test_n_dimensional_XYZ_to_CAM16(self): """ - Returns the *CAM16* colour appearance model output specification from - given data. - - Parameters - ---------- - data : list - Fixture data. - - Returns - ------- - CAM_Specification_CAM16 - *CAM16* colour appearance model specification. + Test :func:`colour.appearance.cam16.XYZ_to_CAM16` definition + n-dimensional support. """ - XYZ = tstack([data['X'], data['Y'], data['Z']]) - XYZ_w = tstack([data['X_w'], data['Y_w'], data['Z_w']]) + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_w = np.array([95.05, 100.00, 108.88]) + L_A = 318.31 + Y_b = 20 + surround = VIEWING_CONDITIONS_CAM16["Average"] + specification = XYZ_to_CAM16(XYZ, XYZ_w, L_A, Y_b, surround) + + XYZ = np.tile(XYZ, (6, 1)) + specification = np.tile(specification, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_CAM16(XYZ, XYZ_w, L_A, Y_b, surround), + specification, + decimal=7, + ) - specification = XYZ_to_CAM16( - XYZ, XYZ_w, data['L_A'], data['Y_b'], - InductionFactors_CAM16(data['F'], data['c'], data['N_c'])) + XYZ_w = np.tile(XYZ_w, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_CAM16(XYZ, XYZ_w, L_A, Y_b, surround), + specification, + decimal=7, + ) - return specification + XYZ = np.reshape(XYZ, (2, 3, 3)) + XYZ_w = np.reshape(XYZ_w, (2, 3, 3)) + specification = np.reshape(specification, (2, 3, 8)) + np.testing.assert_almost_equal( + XYZ_to_CAM16(XYZ, XYZ_w, L_A, Y_b, surround), + specification, + decimal=7, + ) @ignore_numpy_errors def test_domain_range_scale_XYZ_to_CAM16(self): """ - Tests :func:`colour.appearance.cam16.XYZ_to_CAM16` definition domain + Test :func:`colour.appearance.cam16.XYZ_to_CAM16` definition domain and range scale support. """ XYZ = np.array([19.01, 20.00, 21.78]) XYZ_w = np.array([95.05, 100.00, 108.88]) L_A = 318.31 - Y_b = 20.0 - surround = VIEWING_CONDITIONS_CAM16['Average'] - specification = XYZ_to_CAM16(XYZ, XYZ_w, L_A, Y_b, surround)[:-1] + Y_b = 20 + surround = VIEWING_CONDITIONS_CAM16["Average"] + specification = XYZ_to_CAM16(XYZ, XYZ_w, L_A, Y_b, surround) d_r = ( - ('reference', 1, 1), - (1, 0.01, - np.array([ - 1 / 100, 1 / 100, 1 / 360, 1 / 100, 1 / 100, 1 / 100, 1 / 400 - ])), - (100, 1, np.array([1, 1, 100 / 360, 1, 1, 1, 100 / 400])), + ("reference", 1, 1), + ( + "1", + 0.01, + np.array( + [ + 1 / 100, + 1 / 100, + 1 / 360, + 1 / 100, + 1 / 100, + 1 / 100, + 1 / 400, + np.nan, + ] + ), + ), + ( + "100", + 1, + np.array([1, 1, 100 / 360, 1, 1, 1, 100 / 400, np.nan]), + ), ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - XYZ_to_CAM16(XYZ * factor_a, XYZ_w * factor_a, L_A, Y_b, - surround)[:-1], - specification * factor_b, - decimal=7) + XYZ_to_CAM16( + XYZ * factor_a, XYZ_w * factor_a, L_A, Y_b, surround + ), + as_float_array(specification) * factor_b, + decimal=7, + ) @ignore_numpy_errors def test_nan_XYZ_to_CAM16(self): """ - Tests :func:`colour.appearance.cam16.XYZ_to_CAM16` definition + Test :func:`colour.appearance.cam16.XYZ_to_CAM16` definition nan support. """ @@ -124,166 +244,188 @@ def test_nan_XYZ_to_CAM16(self): XYZ_to_CAM16(XYZ, XYZ_w, L_A, Y_b, surround) -class TestCAM16ColourAppearanceModelInverse(ColourAppearanceModelTest): +class TestCAM16_to_XYZ(unittest.TestCase): """ - Defines :mod:`colour.appearance.cam16` module units tests methods for - *CAM16* colour appearance model inverse implementation. + Define :func:`colour.appearance.cam16.CAM16_to_XYZ` definition unit tests + methods. """ - FIXTURE_BASENAME = 'cam16.csv' - - OUTPUT_ATTRIBUTES = {'X': 0, 'Y': 1, 'Z': 2} - - def output_specification_from_data(self, data): - """ - Returns the colour appearance model output specification from given - fixture data. - - Parameters - ---------- - data : list - Tested colour appearance model fixture data. - - Notes - ----- - - This method is a dummy object. - """ - - pass + def test_CAM16_to_XYZ(self): + """Test :func:`colour.appearance.cam16.CAM16_to_XYZ` definition.""" - def _XYZ_from_data(self, data, correlates): - """ - Returns the *CIE XYZ* tristimulus values from given *CAM16* colour - appearance model input data. - - Parameters - ---------- - data : list - Fixture data. - correlates : array_like - Correlates used to build the input *CAM16* colour appearance - model specification. - - Returns - ------- - array_like - *CIE XYZ* tristimulus values - """ + specification = CAM_Specification_CAM16( + 41.73120791, 0.10335574, 217.06795977 + ) + XYZ_w = np.array([95.05, 100.00, 108.88]) + L_A = 318.31 + Y_b = 20 + surround = VIEWING_CONDITIONS_CAM16["Average"] + np.testing.assert_almost_equal( + CAM16_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), + np.array([19.01, 20.00, 21.78]), + decimal=7, + ) - XYZ_w = tstack([data['X_w'], data['Y_w'], data['Z_w']]) + specification = CAM_Specification_CAM16( + 65.42828069, 49.67956420, 17.48659243 + ) + L_A = 31.83 + np.testing.assert_almost_equal( + CAM16_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), + np.array([57.06, 43.06, 31.96]), + decimal=7, + ) - i, j, k = correlates - specification = as_namedtuple({ - i: data[i], - j: data[j], - k: data[k] - }, CAM_Specification_CAM16) + specification = CAM_Specification_CAM16( + 21.36052893, 50.99381895, 178.86724266 + ) + XYZ_w = np.array([109.85, 100, 35.58]) + L_A = 318.31 + np.testing.assert_almost_equal( + CAM16_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), + np.array([3.53, 6.56, 2.14]), + decimal=7, + ) - XYZ = CAM16_to_XYZ( - specification, XYZ_w, data['L_A'], data['Y_b'], - InductionFactors_CAM16(data['F'], data['c'], data['N_c'])) + specification = CAM_Specification_CAM16( + 41.36326063, 52.81154022, 258.88676291 + ) + L_A = 318.31 + np.testing.assert_almost_equal( + CAM16_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), + np.array([19.01, 20.00, 21.78]), + decimal=7, + ) - return XYZ + specification = CAM_Specification_CAM16( + 21.03801957, 457.78881613, 350.06445098 + ) + XYZ_w = np.array([95.05, 100.00, 108.88]) + L_A = 4.074366543152521 + np.testing.assert_almost_equal( + CAM16_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), + np.array([61.45276998, 7.00421901, 82.2406738]), + decimal=7, + ) - def check_specification_attribute(self, case, data, attribute, expected): + def test_n_dimensional_CAM16_to_XYZ(self): """ - Tests *CIE XYZ* tristimulus values output from *CAM16* colour - appearance model input data. - - Parameters - ---------- - case : int - Fixture case number. - data : dict. - Fixture case data. - attribute : unicode. - Tested attribute name. - expected : float. - Expected attribute value. - - Warnings - -------- - The method name does not reflect the underlying implementation. + Test :func:`colour.appearance.cam16.CAM16_to_XYZ` definition + n-dimensional support. """ - for correlates in (('J', 'C', 'h'), ('J', 'M', 'h')): - XYZ = self._XYZ_from_data(data, correlates) - value = tsplit(XYZ)[attribute] + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_w = np.array([95.05, 100.00, 108.88]) + L_A = 318.31 + Y_b = 20 + surround = VIEWING_CONDITIONS_CAM16["Average"] + specification = XYZ_to_CAM16(XYZ, XYZ_w, L_A, Y_b, surround) + XYZ = CAM16_to_XYZ(specification, XYZ_w, L_A, Y_b, surround) - error_message = ('Parameter "{0}" in test case "{1}" ' - 'does not match target value.\n' - 'Expected: "{2}" \n' - 'Received "{3}"').format(attribute, case, - expected, value) + specification = CAM_Specification_CAM16( + *np.transpose(np.tile(tsplit(specification), (6, 1))).tolist() + ) + XYZ = np.tile(XYZ, (6, 1)) + np.testing.assert_almost_equal( + CAM16_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), + XYZ, + decimal=7, + ) - np.testing.assert_allclose( - value, - expected, - err_msg=error_message, - rtol=0.01, - atol=0.01, - verbose=False) + XYZ_w = np.tile(XYZ_w, (6, 1)) + np.testing.assert_almost_equal( + CAM16_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), + XYZ, + decimal=7, + ) - np.testing.assert_almost_equal( - value, expected, decimal=1, err_msg=error_message) + specification = CAM_Specification_CAM16( + *tsplit(np.reshape(specification, (2, 3, 8))).tolist() + ) + XYZ_w = np.reshape(XYZ_w, (2, 3, 3)) + XYZ = np.reshape(XYZ, (2, 3, 3)) + np.testing.assert_almost_equal( + CAM16_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), + XYZ, + decimal=7, + ) @ignore_numpy_errors def test_domain_range_scale_CAM16_to_XYZ(self): """ - Tests :func:`colour.appearance.cam16.CAM16_to_XYZ` definition domain + Test :func:`colour.appearance.cam16.CAM16_to_XYZ` definition domain and range scale support. """ - XYZ_i = np.array([19.01, 20.00, 21.78]) + XYZ = np.array([19.01, 20.00, 21.78]) XYZ_w = np.array([95.05, 100.00, 108.88]) L_A = 318.31 - Y_b = 20.0 - surround = VIEWING_CONDITIONS_CAM16['Average'] - specification = XYZ_to_CAM16(XYZ_i, XYZ_w, L_A, Y_b, surround) + Y_b = 20 + surround = VIEWING_CONDITIONS_CAM16["Average"] + specification = XYZ_to_CAM16(XYZ, XYZ_w, L_A, Y_b, surround) XYZ = CAM16_to_XYZ(specification, XYZ_w, L_A, Y_b, surround) d_r = ( - ('reference', 1, 1, 1), - (1, - np.array([ - 1 / 100, 1 / 100, 1 / 360, 1 / 100, 1 / 100, 1 / 100, 1 / 400 - ]), 0.01, 0.01), - (100, np.array([1, 1, 100 / 360, 1, 1, 1, 100 / 400]), 1, 1), + ("reference", 1, 1), + ( + "1", + np.array( + [ + 1 / 100, + 1 / 100, + 1 / 360, + 1 / 100, + 1 / 100, + 1 / 100, + 1 / 400, + np.nan, + ] + ), + 0.01, + ), + ( + "100", + np.array([1, 1, 100 / 360, 1, 1, 1, 100 / 400, np.nan]), + 1, + ), ) - for scale, factor_a, factor_b, factor_c in d_r: + for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - CAM16_to_XYZ(specification[:-1] * factor_a, - XYZ_w * factor_b, L_A, Y_b, surround), - XYZ * factor_c, - decimal=7) + CAM16_to_XYZ( + specification * factor_a, + XYZ_w * factor_b, + L_A, + Y_b, + surround, + ), + XYZ * factor_b, + decimal=7, + ) @ignore_numpy_errors def test_raise_exception_CAM16_to_XYZ(self): """ - Tests :func:`colour.appearance.cam16.CAM16_to_XYZ` definition raised + Test :func:`colour.appearance.cam16.CAM16_to_XYZ` definition raised exception. """ - try: - CAM16_to_XYZ( - CAM_Specification_CAM16( - 41.731207905126638, - None, - 217.06795976739301, - ), - np.array([95.05, 100.00, 108.88]), - 318.31, - 20.0, - VIEWING_CONDITIONS_CAM16['Average'], - ) - except ValueError: - pass + self.assertRaises( + ValueError, + CAM16_to_XYZ, + CAM_Specification_CAM16( + 41.731207905126638, None, 217.06795976739301 + ), + np.array([95.05, 100.00, 108.88]), + 318.31, + 20.0, + VIEWING_CONDITIONS_CAM16["Average"], + ) @ignore_numpy_errors def test_nan_CAM16_to_XYZ(self): """ - Tests :func:`colour.appearance.cam16.CAM16_to_XYZ` definition nan + Test :func:`colour.appearance.cam16.CAM16_to_XYZ` definition nan support. """ @@ -298,4 +440,9 @@ def test_nan_CAM16_to_XYZ(self): Y_b = case[0] surround = InductionFactors_CAM16(case[0], case[0], case[0]) CAM16_to_XYZ( - CAM_Specification_CAM16(J, C, h), XYZ_w, L_A, Y_b, surround) + CAM_Specification_CAM16(J, C, h, M=50), + XYZ_w, + L_A, + Y_b, + surround, + ) diff --git a/colour/appearance/tests/test_ciecam02.py b/colour/appearance/tests/test_ciecam02.py index 322488f9d4..a5b700ee44 100644 --- a/colour/appearance/tests/test_ciecam02.py +++ b/colour/appearance/tests/test_ciecam02.py @@ -1,111 +1,210 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.appearance.ciecam02` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.appearance.ciecam02` module.""" import numpy as np +import unittest from itertools import permutations from colour.appearance import ( - VIEWING_CONDITIONS_CIECAM02, InductionFactors_CIECAM02, - CAM_Specification_CIECAM02, XYZ_to_CIECAM02, CIECAM02_to_XYZ) -from colour.appearance.tests.common import ColourAppearanceModelTest -from colour.utilities import (as_namedtuple, domain_range_scale, - ignore_numpy_errors, tsplit, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + VIEWING_CONDITIONS_CIECAM02, + InductionFactors_CIECAM02, + CAM_Specification_CIECAM02, + XYZ_to_CIECAM02, + CIECAM02_to_XYZ, +) +from colour.utilities import ( + as_float_array, + domain_range_scale, + ignore_numpy_errors, + tsplit, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'TestCIECAM02ColourAppearanceModelForward', - 'TestCIECAM02ColourAppearanceModelInverse' + "TestXYZ_to_CIECAM02", + "TestCIECAM02_to_XYZ", ] -class TestCIECAM02ColourAppearanceModelForward(ColourAppearanceModelTest): +class TestXYZ_to_CIECAM02(unittest.TestCase): """ - Defines :mod:`colour.appearance.ciecam02` module units tests methods for - *CIECAM02* colour appearance model forward implementation. + Define :func:`colour.appearance.ciecam02.XYZ_to_CIECAM02` definition unit + tests methods. """ - FIXTURE_BASENAME = 'ciecam02.csv' + def test_XYZ_to_CIECAM02(self): + """ + Test :func:`colour.appearance.ciecam02.XYZ_to_CIECAM02` definition. + + Notes + ----- + - The test values have been generated from data of the following file + by *Fairchild (2013)*: + http://rit-mcsl.org/fairchild//files/AppModEx.xls + """ + + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_w = np.array([95.05, 100.00, 108.88]) + L_A = 318.31 + Y_b = 20 + surround = InductionFactors_CIECAM02(1, 0.69, 1) + np.testing.assert_allclose( + XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround), + np.array([41.73, 0.1, 219, 2.36, 195.37, 0.11, 278.1, np.nan]), + rtol=0.01, + atol=0.01, + ) + + XYZ = np.array([57.06, 43.06, 31.96]) + L_A = 31.83 + np.testing.assert_allclose( + XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround), + np.array( + [65.96, 48.57, 19.6, 52.25, 152.67, 41.67, 399.6, np.nan] + ), + rtol=0.01, + atol=0.01, + ) + + XYZ = np.array([3.53, 6.56, 2.14]) + XYZ_w = np.array([109.85, 100.00, 35.58]) + L_A = 318.31 + np.testing.assert_allclose( + XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround), + np.array( + [21.79, 46.94, 177.1, 58.79, 141.17, 48.8, 220.4, np.nan] + ), + rtol=0.01, + atol=0.01, + ) + + XYZ = np.array([19.01, 20.00, 21.78]) + L_A = 31.83 + np.testing.assert_allclose( + XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround), + np.array( + [42.53, 51.92, 248.9, 60.22, 122.83, 44.54, 305.8, np.nan] + ), + rtol=0.01, + atol=0.01, + ) - OUTPUT_ATTRIBUTES = { - 'J': 'J', - 'C': 'C', - 'h': 'h', - 's': 's', - 'Q': 'Q', - 'M': 'M', - 'H': 'H' - } + XYZ = np.array([61.45276998, 7.00421901, 82.24067384]) + XYZ_w = np.array([95.05, 100, 108.88]) + L_A = 4.074366543152521 + np.testing.assert_allclose( + XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround), + np.array( + [ + 21.72630603341673, + 411.5190338631848, + 349.12875710099053, + 227.15081998415593, + 57.657243286322725, + 297.49693233026602, + 375.5788601911363, + np.nan, + ] + ), + rtol=0.01, + atol=0.01, + ) - def output_specification_from_data(self, data): + def test_n_dimensional_XYZ_to_CIECAM02(self): """ - Returns the *CIECAM02* colour appearance model output specification - from given data. - - Parameters - ---------- - data : list - Fixture data. - - Returns - ------- - CAM_Specification_CIECAM02 - *CIECAM02* colour appearance model specification. + Test :func:`colour.appearance.ciecam02.XYZ_to_CIECAM02` definition + n-dimensional support. """ - XYZ = tstack([data['X'], data['Y'], data['Z']]) - XYZ_w = tstack([data['X_w'], data['Y_w'], data['Z_w']]) + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_w = np.array([95.05, 100.00, 108.88]) + L_A = 318.31 + Y_b = 20 + surround = VIEWING_CONDITIONS_CIECAM02["Average"] + specification = XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround) + + XYZ = np.tile(XYZ, (6, 1)) + specification = np.tile(specification, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround), + specification, + decimal=7, + ) - specification = XYZ_to_CIECAM02( - XYZ, XYZ_w, data['L_A'], data['Y_b'], - InductionFactors_CIECAM02(data['F'], data['c'], data['N_c'])) + XYZ_w = np.tile(XYZ_w, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround), + specification, + decimal=7, + ) - return specification + XYZ = np.reshape(XYZ, (2, 3, 3)) + XYZ_w = np.reshape(XYZ_w, (2, 3, 3)) + specification = np.reshape(specification, (2, 3, 8)) + np.testing.assert_almost_equal( + XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround), + specification, + decimal=7, + ) @ignore_numpy_errors def test_domain_range_scale_XYZ_to_CIECAM02(self): """ - Tests :func:`colour.appearance.cam16.XYZ_to_CIECAM02` definition domain - and range scale support. + Test :func:`colour.appearance.ciecam02.XYZ_to_CIECAM02` definition + domain and range scale support. """ XYZ = np.array([19.01, 20.00, 21.78]) XYZ_w = np.array([95.05, 100.00, 108.88]) L_A = 318.31 - Y_b = 20.0 - surround = VIEWING_CONDITIONS_CIECAM02['Average'] - specification = XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround)[:-1] + Y_b = 20 + surround = VIEWING_CONDITIONS_CIECAM02["Average"] + specification = XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround) d_r = ( - ('reference', 1, 1), - (1, 0.01, - np.array([ - 1 / 100, 1 / 100, 1 / 360, 1 / 100, 1 / 100, 1 / 100, 1 / 400 - ])), - (100, 1, np.array([1, 1, 100 / 360, 1, 1, 1, 100 / 400])), + ("reference", 1, 1), + ( + "1", + 0.01, + np.array( + [ + 1 / 100, + 1 / 100, + 1 / 360, + 1 / 100, + 1 / 100, + 1 / 100, + 1 / 400, + np.nan, + ] + ), + ), + ( + "100", + 1, + np.array([1, 1, 100 / 360, 1, 1, 1, 100 / 400, np.nan]), + ), ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - XYZ_to_CIECAM02(XYZ * factor_a, XYZ_w * factor_a, L_A, Y_b, - surround)[:-1], - specification * factor_b, - decimal=7) + XYZ_to_CIECAM02( + XYZ * factor_a, XYZ_w * factor_a, L_A, Y_b, surround + ), + as_float_array(specification) * factor_b, + decimal=7, + ) @ignore_numpy_errors def test_nan_XYZ_to_CIECAM02(self): """ - Tests :func:`colour.appearance.ciecam02.XYZ_to_CIECAM02` definition + Test :func:`colour.appearance.ciecam02.XYZ_to_CIECAM02` definition nan support. """ @@ -120,166 +219,200 @@ def test_nan_XYZ_to_CIECAM02(self): XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround) -class TestCIECAM02ColourAppearanceModelInverse(ColourAppearanceModelTest): +class TestCIECAM02_to_XYZ(unittest.TestCase): """ - Defines :mod:`colour.appearance.ciecam02` module units tests methods for - *CIECAM02* colour appearance model inverse implementation. + Define :func:`colour.appearance.ciecam02.CIECAM02_to_XYZ` definition unit + tests methods. """ - FIXTURE_BASENAME = 'ciecam02.csv' - - OUTPUT_ATTRIBUTES = {'X': 0, 'Y': 1, 'Z': 2} - - def output_specification_from_data(self, data): - """ - Returns the colour appearance model output specification from given - fixture data. - - Parameters - ---------- - data : list - Tested colour appearance model fixture data. + def test_CIECAM02_to_XYZ(self): + """Test :func:`colour.appearance.ciecam02.CIECAM02_to_XYZ` definition.""" - Notes - ----- - - This method is a dummy object. - """ - - pass - - def _XYZ_from_data(self, data, correlates): - """ - Returns the *CIE XYZ* tristimulus values from given *CIECAM02* colour - appearance model input data. - - Parameters - ---------- - data : list - Fixture data. - correlates : array_like - Correlates used to build the input *CIECAM02* colour appearance - model specification. - - Returns - ------- - array_like - *CIE XYZ* tristimulus values - """ + specification = CAM_Specification_CIECAM02( + 41.73, 0.1, 219, 2.36, 195.37, 0.11, 278.1 + ) + XYZ_w = np.array([95.05, 100.00, 108.88]) + L_A = 318.31 + Y_b = 20 + surround = InductionFactors_CIECAM02(1, 0.69, 1) + np.testing.assert_allclose( + CIECAM02_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), + np.array([19.01, 20.00, 21.78]), + rtol=0.01, + atol=0.01, + ) - XYZ_w = tstack([data['X_w'], data['Y_w'], data['Z_w']]) + specification = CAM_Specification_CIECAM02( + 65.96, 48.57, 19.6, 52.25, 152.67, 41.67, 399.6, np.nan + ) + L_A = 31.83 + np.testing.assert_allclose( + CIECAM02_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), + np.array([57.06, 43.06, 31.96]), + rtol=0.01, + atol=0.01, + ) - i, j, k = correlates - specification = as_namedtuple({ - i: data[i], - j: data[j], - k: data[k] - }, CAM_Specification_CIECAM02) + specification = CAM_Specification_CIECAM02( + 21.79, 46.94, 177.1, 58.79, 141.17, 48.8, 220.4, np.nan + ) + XYZ_w = np.array([109.85, 100.00, 35.58]) + L_A = 318.31 + np.testing.assert_allclose( + CIECAM02_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), + np.array([3.53, 6.56, 2.14]), + rtol=0.01, + atol=0.01, + ) - XYZ = CIECAM02_to_XYZ( - specification, XYZ_w, data['L_A'], data['Y_b'], - InductionFactors_CIECAM02(data['F'], data['c'], data['N_c'])) + specification = CAM_Specification_CIECAM02( + 42.53, 51.92, 248.9, 60.22, 122.83, 44.54, 305.8, np.nan + ) + L_A = 31.83 + np.testing.assert_allclose( + CIECAM02_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), + np.array([19.01, 20.00, 21.78]), + rtol=0.01, + atol=0.01, + ) - return XYZ + specification = CAM_Specification_CIECAM02( + 21.72630603341673, + 411.5190338631848, + 349.12875710099053, + 227.15081998415593, + 57.657243286322725, + 297.49693233026602, + 375.5788601911363, + np.nan, + ) + XYZ_w = np.array([95.05, 100, 108.88]) + L_A = 4.074366543152521 + np.testing.assert_allclose( + CIECAM02_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), + np.array([61.45276998, 7.00421901, 82.24067384]), + rtol=0.01, + atol=0.01, + ) - def check_specification_attribute(self, case, data, attribute, expected): + def test_n_dimensional_CIECAM02_to_XYZ(self): """ - Tests *CIE XYZ* tristimulus values output from *CIECAM02* colour - appearance model input data. - - Parameters - ---------- - case : int - Fixture case number. - data : dict. - Fixture case data. - attribute : unicode. - Tested attribute name. - expected : float. - Expected attribute value. - - Warnings - -------- - The method name does not reflect the underlying implementation. + Test :func:`colour.appearance.ciecam02.CIECAM02_to_XYZ` definition + n-dimensional support. """ - for correlates in (('J', 'C', 'h'), ('J', 'M', 'h')): - XYZ = self._XYZ_from_data(data, correlates) - value = tsplit(XYZ)[attribute] + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_w = np.array([95.05, 100.00, 108.88]) + L_A = 318.31 + Y_b = 20 + surround = VIEWING_CONDITIONS_CIECAM02["Average"] + specification = XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround) + XYZ = CIECAM02_to_XYZ(specification, XYZ_w, L_A, Y_b, surround) - error_message = ('Parameter "{0}" in test case "{1}" ' - 'does not match target value.\n' - 'Expected: "{2}" \n' - 'Received "{3}"').format(attribute, case, - expected, value) + specification = CAM_Specification_CIECAM02( + *np.transpose(np.tile(tsplit(specification), (6, 1))).tolist() + ) + XYZ = np.tile(XYZ, (6, 1)) + np.testing.assert_almost_equal( + CIECAM02_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), + XYZ, + decimal=7, + ) - np.testing.assert_allclose( - value, - expected, - err_msg=error_message, - rtol=0.01, - atol=0.01, - verbose=False) + XYZ_w = np.tile(XYZ_w, (6, 1)) + np.testing.assert_almost_equal( + CIECAM02_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), + XYZ, + decimal=7, + ) - np.testing.assert_almost_equal( - value, expected, decimal=1, err_msg=error_message) + specification = CAM_Specification_CIECAM02( + *tsplit(np.reshape(specification, (2, 3, 8))).tolist() + ) + XYZ_w = np.reshape(XYZ_w, (2, 3, 3)) + XYZ = np.reshape(XYZ, (2, 3, 3)) + np.testing.assert_almost_equal( + CIECAM02_to_XYZ(specification, XYZ_w, L_A, Y_b, surround), + XYZ, + decimal=7, + ) @ignore_numpy_errors def test_domain_range_scale_CIECAM02_to_XYZ(self): """ - Tests :func:`colour.appearance.cam16.CIECAM02_to_XYZ` definition domain - and range scale support. + Test :func:`colour.appearance.ciecam02.CIECAM02_to_XYZ` definition + domain and range scale support. """ XYZ_i = np.array([19.01, 20.00, 21.78]) XYZ_w = np.array([95.05, 100.00, 108.88]) L_A = 318.31 - Y_b = 20.0 - surround = VIEWING_CONDITIONS_CIECAM02['Average'] + Y_b = 20 + surround = VIEWING_CONDITIONS_CIECAM02["Average"] specification = XYZ_to_CIECAM02(XYZ_i, XYZ_w, L_A, Y_b, surround) XYZ = CIECAM02_to_XYZ(specification, XYZ_w, L_A, Y_b, surround) d_r = ( - ('reference', 1, 1, 1), - (1, - np.array([ - 1 / 100, 1 / 100, 1 / 360, 1 / 100, 1 / 100, 1 / 100, 1 / 400 - ]), 0.01, 0.01), - (100, np.array([1, 1, 100 / 360, 1, 1, 1, 100 / 400]), 1, 1), + ("reference", 1, 1), + ( + "1", + np.array( + [ + 1 / 100, + 1 / 100, + 1 / 360, + 1 / 100, + 1 / 100, + 1 / 100, + 1 / 400, + np.nan, + ] + ), + 0.01, + ), + ( + "100", + np.array([1, 1, 100 / 360, 1, 1, 1, 100 / 400, np.nan]), + 1, + ), ) - for scale, factor_a, factor_b, factor_c in d_r: + for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - CIECAM02_to_XYZ(specification[:-1] * factor_a, - XYZ_w * factor_b, L_A, Y_b, surround), - XYZ * factor_c, - decimal=7) + CIECAM02_to_XYZ( + specification * factor_a, + XYZ_w * factor_b, + L_A, + Y_b, + surround, + ), + XYZ * factor_b, + decimal=7, + ) @ignore_numpy_errors def test_raise_exception_CIECAM02_to_XYZ(self): """ - Tests :func:`colour.appearance.cam16.CIECAM02_to_XYZ` definition raised - exception. + Test :func:`colour.appearance.ciecam02.CIECAM02_to_XYZ` definition + raised exception. """ - try: - CIECAM02_to_XYZ( - CAM_Specification_CIECAM02( - 41.731091132513917, - None, - 219.04843265831178, - ), - np.array([95.05, 100.00, 108.88]), - 318.31, - 20.0, - VIEWING_CONDITIONS_CIECAM02['Average'], - ) - except ValueError: - pass + self.assertRaises( + ValueError, + CIECAM02_to_XYZ, + CAM_Specification_CIECAM02( + 41.731091132513917, None, 219.04843265831178 + ), + np.array([95.05, 100.00, 108.88]), + 318.31, + 20.0, + VIEWING_CONDITIONS_CIECAM02["Average"], + ) @ignore_numpy_errors def test_nan_CIECAM02_to_XYZ(self): """ - Tests :func:`colour.appearance.ciecam02.CIECAM02_to_XYZ` definition + Test :func:`colour.appearance.ciecam02.CIECAM02_to_XYZ` definition nan support. """ @@ -294,4 +427,9 @@ def test_nan_CIECAM02_to_XYZ(self): Y_b = case[0] surround = InductionFactors_CIECAM02(case[0], case[0], case[0]) CIECAM02_to_XYZ( - CAM_Specification_CIECAM02(J, C, h), XYZ_w, L_A, Y_b, surround) + CAM_Specification_CIECAM02(J, C, h, M=50), + XYZ_w, + L_A, + Y_b, + surround, + ) diff --git a/colour/appearance/tests/test_hke.py b/colour/appearance/tests/test_hke.py new file mode 100644 index 0000000000..ec7787b77e --- /dev/null +++ b/colour/appearance/tests/test_hke.py @@ -0,0 +1,393 @@ +# !/usr/bin/env python +"""Defines the unit tests for the :mod:`colour.appearance.hke` module.""" + +import numpy as np +import unittest +from itertools import permutations + +from colour.appearance.hke import ( + HelmholtzKohlrausch_effect_object_Nayatani1997, + HelmholtzKohlrausch_effect_luminous_Nayatani1997, + coefficient_K_Br_Nayatani1997, + coefficient_q_Nayatani1997, +) +from colour.utilities import ignore_numpy_errors + +__author__ = "Ilia Sibiryakov" +__copyright__ = "Copyright 2013 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__ = [ + "TestHelmholtzKohlrauschEffectObjectNayatani1997", + "TestHelmholtzKohlrauschEffectLuminousNayatani1997", + "TestCoefficient_K_Br_Nayatani1997", + "TestCoefficient_q_Nayatani1997", +] + + +class TestHelmholtzKohlrauschEffectObjectNayatani1997(unittest.TestCase): + """ + Define :func:`colour.colour.appearance.hke.\ +HelmholtzKohlrausch_effect_object_Nayatani1997` definition unit tests methods. + """ + + def test_HelmholtzKohlrausch_effect_object_Nayatani1997(self): + """ + Test :func:`colour.appearance.hke.\ +HelmholtzKohlrausch_effect_object_Nayatani1997` definition. + """ + + self.assertAlmostEqual( + HelmholtzKohlrausch_effect_object_Nayatani1997( + np.array([0.40351010, 0.53933673]), + np.array([0.19783001, 0.46831999]), + 63.66, + method="VCC", + ), + 1.344152435497761, + places=7, + ) + + self.assertAlmostEqual( + HelmholtzKohlrausch_effect_object_Nayatani1997( + np.array([0.40351010, 0.53933673]), + np.array([0.19783001, 0.46831999]), + 63.66, + method="VAC", + ), + 1.261777232837009, + places=7, + ) + + def test_n_dimensional_HelmholtzKohlrausch_effect_object_Nayatani1997( + self, + ): + """ + Test :func:`colour.appearance.hke.\ +HelmholtzKohlrausch_effect_object_Nayatani1997` definition n_dimensional + arrays support. + """ + + uv_d65 = np.array([0.19783001, 0.46831999]) + uv = np.array([0.40351010, 0.53933673]) + L_a = 63.66 + + result_vcc = HelmholtzKohlrausch_effect_object_Nayatani1997( + uv, uv_d65, L_a, method="VCC" + ) + result_vac = HelmholtzKohlrausch_effect_object_Nayatani1997( + uv, uv_d65, L_a, method="VAC" + ) + + uv_d65 = np.tile(uv_d65, (6, 1)) + uv = np.tile(uv, (6, 1)) + result_vcc = np.tile(result_vcc, 6) + result_vac = np.tile(result_vac, 6) + + np.testing.assert_almost_equal( + HelmholtzKohlrausch_effect_object_Nayatani1997( + uv, uv_d65, L_a, method="VCC" + ), + result_vcc, + decimal=7, + ) + + np.testing.assert_almost_equal( + HelmholtzKohlrausch_effect_object_Nayatani1997( + uv, uv_d65, L_a, method="VAC" + ), + result_vac, + decimal=7, + ) + + uv_d65 = np.reshape(uv_d65, (2, 3, 2)) + uv = np.reshape(uv, (2, 3, 2)) + result_vcc = np.reshape(result_vcc, (2, 3)) + result_vac = np.reshape(result_vac, (2, 3)) + + np.testing.assert_almost_equal( + HelmholtzKohlrausch_effect_object_Nayatani1997( + uv, uv_d65, L_a, method="VCC" + ), + result_vcc, + decimal=7, + ) + + np.testing.assert_almost_equal( + HelmholtzKohlrausch_effect_object_Nayatani1997( + uv, uv_d65, L_a, method="VAC" + ), + result_vac, + decimal=7, + ) + + @ignore_numpy_errors + def test_nan_HelmholtzKohlrausch_effect_object_Nayatani1997(self): + """ + Test :func:`colour.appearance.hke.\ +HelmholtzKohlrausch_effect_object_Nayatani1997` definition nan support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 2, r=2)) + for case in cases: + HelmholtzKohlrausch_effect_object_Nayatani1997(case, case, case[0]) + + +class TestHelmholtzKohlrauschEffectLuminousNayatani1997(unittest.TestCase): + """ + Define :func:`colour.appearance.hke.\ +HelmholtzKohlrausch_effect_luminous_Nayatani1997` definition unit tests + methods. + """ + + def test_HelmholtzKohlrausch_effect_luminous_Nayatani1997(self): + """ + Test :func:`colour.appearance.hke.\ +HelmholtzKohlrausch_effect_luminous_Nayatani1997` definition. + """ + + self.assertAlmostEqual( + HelmholtzKohlrausch_effect_luminous_Nayatani1997( + np.array([0.40351010, 0.53933673]), + np.array([0.19783001, 0.46831999]), + 63.66, + method="VCC", + ), + 2.014433723774654, + places=7, + ) + + self.assertAlmostEqual( + HelmholtzKohlrausch_effect_luminous_Nayatani1997( + np.array([0.40351010, 0.53933673]), + np.array([0.19783001, 0.46831999]), + 63.66, + method="VAC", + ), + 1.727991241148628, + places=7, + ) + + def test_n_dimensional_HelmholtzKohlrausch_effect_luminous_Nayatani1997( + self, + ): + """ + Test :func:`colour.appearance.hke.\ +HelmholtzKohlrausch_effect_luminous_Nayatani1997` definition n_dimensional + arrays support. + """ + + uv_d65 = np.array([0.19783001, 0.46831999]) + uv = np.array([0.40351010, 0.53933673]) + L_a = 63.66 + + result_vcc = HelmholtzKohlrausch_effect_luminous_Nayatani1997( + uv, uv_d65, L_a, method="VCC" + ) + result_vac = HelmholtzKohlrausch_effect_luminous_Nayatani1997( + uv, uv_d65, L_a, method="VAC" + ) + + uv_d65 = np.tile(uv_d65, (6, 1)) + uv = np.tile(uv, (6, 1)) + result_vcc = np.tile(result_vcc, 6) + result_vac = np.tile(result_vac, 6) + + np.testing.assert_almost_equal( + HelmholtzKohlrausch_effect_luminous_Nayatani1997( + uv, uv_d65, L_a, method="VCC" + ), + result_vcc, + decimal=7, + ) + + np.testing.assert_almost_equal( + HelmholtzKohlrausch_effect_luminous_Nayatani1997( + uv, uv_d65, L_a, method="VAC" + ), + result_vac, + decimal=7, + ) + + uv_d65 = np.reshape(uv_d65, (2, 3, 2)) + uv = np.reshape(uv, (2, 3, 2)) + result_vcc = np.reshape(result_vcc, (2, 3)) + result_vac = np.reshape(result_vac, (2, 3)) + + np.testing.assert_almost_equal( + HelmholtzKohlrausch_effect_luminous_Nayatani1997( + uv, uv_d65, L_a, method="VCC" + ), + result_vcc, + decimal=7, + ) + + np.testing.assert_almost_equal( + HelmholtzKohlrausch_effect_luminous_Nayatani1997( + uv, uv_d65, L_a, method="VAC" + ), + result_vac, + decimal=7, + ) + + @ignore_numpy_errors + def test_nan_HelmholtzKohlrausch_effect_luminous_Nayatani1997(self): + """ + Test :func:`colour.appearance.hke.\ +HelmholtzKohlrausch_effect_luminous_Nayatani1997` definition nan support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 2, r=2)) + for case in cases: + HelmholtzKohlrausch_effect_luminous_Nayatani1997( + case, case, case[0] + ) + + +class TestCoefficient_K_Br_Nayatani1997(unittest.TestCase): + """ + Define :func:`colour.appearance.hke.coefficient_K_Br_Nayatani1997` + definition unit tests methods. + """ + + def test_coefficient_K_Br_Nayatani1997(self): + """ + Test :func:`colour.appearance.hke.coefficient_K_Br_Nayatani1997` + definition. + """ + + self.assertAlmostEqual( + coefficient_K_Br_Nayatani1997(10.00000000), + 0.71344817765758839, + places=7, + ) + + self.assertAlmostEqual( + coefficient_K_Br_Nayatani1997(63.66000000), + 1.000128455584031, + places=7, + ) + + self.assertAlmostEqual( + coefficient_K_Br_Nayatani1997(1000.00000000), + 1.401080840298197, + places=7, + ) + + self.assertAlmostEqual( + coefficient_K_Br_Nayatani1997(10000.00000000), + 1.592511806930447, + places=7, + ) + + def test_n_dimensional_coefficient_K_Br_Nayatani1997(self): + """ + Test :func:`colour.appearance.hke.coefficient_K_Br_Nayatani1997` + definition n_dimensional arrays support. + """ + + L_a = 63.66 + K_Br = coefficient_K_Br_Nayatani1997(L_a) + + L_a = np.tile(L_a, 6) + K_Br = np.tile(K_Br, 6) + np.testing.assert_almost_equal( + coefficient_K_Br_Nayatani1997(L_a), K_Br, decimal=7 + ) + + L_a = np.reshape(L_a, (2, 3)) + K_Br = np.reshape(K_Br, (2, 3)) + np.testing.assert_almost_equal( + coefficient_K_Br_Nayatani1997(L_a), K_Br, decimal=7 + ) + + L_a = np.reshape(L_a, (2, 3, 1)) + K_Br = np.reshape(K_Br, (2, 3, 1)) + np.testing.assert_almost_equal( + coefficient_K_Br_Nayatani1997(L_a), K_Br, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_coefficient_K_Br_Nayatani1997(self): + """ + Test :func:`colour.appearance.hke.coefficient_K_Br_Nayatani1997` + definition nan support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + for case in cases: + coefficient_K_Br_Nayatani1997(case) + + +class TestCoefficient_q_Nayatani1997(unittest.TestCase): + """ + Define :func:`colour.appearance.hke.coefficient_q_Nayatani1997` + definition unit tests methods. + """ + + def test_coefficient_q_Nayatani1997(self): + """ + Test :func:`colour.appearance.hke.coefficient_q_Nayatani1997` + definition. + """ + + self.assertAlmostEqual( + coefficient_q_Nayatani1997(0.00000000), + -0.121200000000000, + places=7, + ) + + self.assertAlmostEqual( + coefficient_q_Nayatani1997(0.78539816), 0.125211117768464, places=7 + ) + + self.assertAlmostEqual( + coefficient_q_Nayatani1997(1.57079633), 0.191679999416415, places=7 + ) + + self.assertAlmostEqual( + coefficient_q_Nayatani1997(2.35619449), 0.028480866426611, places=7 + ) + + def test_n_dimensional_coefficient_q_Nayatani1997(self): + """ + Test :func:`colour.appearance.hke.coefficient_q_Nayatani1997` + definition n_dimensional arrays support. + """ + + L_a = 63.66 + q = coefficient_q_Nayatani1997(L_a) + + L_a = np.tile(L_a, 6) + q = np.tile(q, 6) + np.testing.assert_almost_equal( + coefficient_q_Nayatani1997(L_a), q, decimal=7 + ) + + L_a = np.reshape(L_a, (2, 3)) + q = np.reshape(q, (2, 3)) + np.testing.assert_almost_equal( + coefficient_q_Nayatani1997(L_a), q, decimal=7 + ) + + L_a = np.reshape(L_a, (2, 3, 1)) + q = np.reshape(q, (2, 3, 1)) + np.testing.assert_almost_equal( + coefficient_q_Nayatani1997(L_a), q, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_coefficient_q_Nayatani1997(self): + """ + Test :func:`colour.appearance.hke.coefficient_q_Nayatani1997` + definition nan support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + for case in cases: + coefficient_q_Nayatani1997(case) diff --git a/colour/appearance/tests/test_hunt.py b/colour/appearance/tests/test_hunt.py index ba9a3693dd..a20e866d07 100644 --- a/colour/appearance/tests/test_hunt.py +++ b/colour/appearance/tests/test_hunt.py @@ -1,79 +1,143 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.appearance.hunt` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.appearance.hunt` module.""" import numpy as np +import unittest from itertools import permutations -from colour.appearance import (VIEWING_CONDITIONS_HUNT, InductionFactors_Hunt, - XYZ_to_Hunt) -from colour.appearance.tests.common import ColourAppearanceModelTest -from colour.utilities import domain_range_scale, ignore_numpy_errors, tstack +from colour.appearance import ( + VIEWING_CONDITIONS_HUNT, + InductionFactors_Hunt, + XYZ_to_Hunt, +) +from colour.utilities import ( + as_float_array, + domain_range_scale, + ignore_numpy_errors, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestHuntColourAppearanceModel'] +__all__ = [ + "TestXYZ_to_Hunt", +] -class TestHuntColourAppearanceModel(ColourAppearanceModelTest): +class TestXYZ_to_Hunt(unittest.TestCase): """ - Defines :mod:`colour.appearance.hunt` module unit tests methods for - *Hunt* colour appearance model. + Define :func:`colour.appearance.hunt.XYZ_to_Hunt` definition unit tests + methods. """ - FIXTURE_BASENAME = 'hunt.csv' + def test_XYZ_to_Hunt(self): + """ + Test :func:`colour.appearance.hunt.XYZ_to_Hunt` definition. - OUTPUT_ATTRIBUTES = { - 'J': 'J', - 'C_94': 'C', - 'h_S': 'h', - 's': 's', - 'Q': 'Q', - 'M94': 'M' - } + Notes + ----- + - The test values have been generated from data of the following file + by *Fairchild (2013)*: + http://rit-mcsl.org/fairchild//files/AppModEx.xls + """ + + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_w = np.array([95.05, 100.00, 108.88]) + XYZ_b = XYZ_w * np.array([1, 0.2, 1]) + L_A = 318.31 + surround = VIEWING_CONDITIONS_HUNT["Normal Scenes"] + CCT_w = 6504.0 + np.testing.assert_allclose( + XYZ_to_Hunt(XYZ, XYZ_w, XYZ_b, L_A, surround, CCT_w=CCT_w), + np.array([42.12, 0.16, 269.3, 0.03, 31.92, 0.16, np.nan, np.nan]), + rtol=0.01, + atol=0.01, + ) + + XYZ = np.array([57.06, 43.06, 31.96]) + L_A = 31.83 + np.testing.assert_allclose( + XYZ_to_Hunt(XYZ, XYZ_w, XYZ_b, L_A, surround, CCT_w=CCT_w), + np.array( + [66.76, 63.89, 18.6, 153.36, 31.22, 58.28, np.nan, np.nan] + ), + rtol=0.01, + atol=0.01, + ) + + XYZ = np.array([3.53, 6.56, 2.14]) + XYZ_w = np.array([109.85, 100.00, 35.58]) + L_A = 318.31 + CCT_w = 2856 + np.testing.assert_allclose( + XYZ_to_Hunt(XYZ, XYZ_w, XYZ_b, L_A, surround, CCT_w=CCT_w), + np.array( + [19.56, 74.58, 178.3, 245.4, 18.9, 76.33, np.nan, np.nan] + ), + rtol=0.01, + atol=0.01, + ) + + XYZ = np.array([19.01, 20.00, 21.78]) + L_A = 31.83 + np.testing.assert_allclose( + XYZ_to_Hunt(XYZ, XYZ_w, XYZ_b, L_A, surround, CCT_w=CCT_w), + np.array( + [40.27, 73.84, 262.8, 209.29, 22.15, 67.35, np.nan, np.nan] + ), + rtol=0.01, + atol=0.01, + ) - def output_specification_from_data(self, data): + def test_n_dimensional_XYZ_to_Hunt(self): """ - Returns the *Hunt* colour appearance model output specification from - given data. - - Parameters - ---------- - data : list - Fixture data. - - Returns - ------- - CAM_Specification_Hunt - Hunt colour appearance model specification. + Test :func:`colour.appearance.hunt.XYZ_to_Hunt` definition + n-dimensional support. """ - XYZ = tstack([data['X'], data['Y'], data['Z']]) - XYZ_w = tstack([data['X_w'], data['Y_w'], data['Z_w']]) - XYZ_b = tstack([data['X_w'], 0.2 * data['Y_w'], data['Z_w']]) - + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_w = np.array([95.05, 100.00, 108.88]) + XYZ_b = XYZ_w * np.array([1, 0.2, 1]) + L_A = 318.31 + surround = VIEWING_CONDITIONS_HUNT["Normal Scenes"] + CCT_w = 6504.0 specification = XYZ_to_Hunt( - XYZ, - XYZ_w, - XYZ_b, - data['L_A'], - InductionFactors_Hunt(data['N_c'], data['N_b']), - CCT_w=data['T']) + XYZ, XYZ_w, XYZ_b, L_A, surround, CCT_w=CCT_w + ) + + XYZ = np.tile(XYZ, (6, 1)) + specification = np.tile(specification, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_Hunt(XYZ, XYZ_w, XYZ_b, L_A, surround, CCT_w=CCT_w), + specification, + decimal=7, + ) + + XYZ_w = np.tile(XYZ_w, (6, 1)) + XYZ_b = np.tile(XYZ_b, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_Hunt(XYZ, XYZ_w, XYZ_b, L_A, surround, CCT_w=CCT_w), + specification, + decimal=7, + ) - return specification + XYZ = np.reshape(XYZ, (2, 3, 3)) + XYZ_w = np.reshape(XYZ_w, (2, 3, 3)) + XYZ_b = np.reshape(XYZ_b, (2, 3, 3)) + specification = np.reshape(specification, (2, 3, 8)) + np.testing.assert_almost_equal( + XYZ_to_Hunt(XYZ, XYZ_w, XYZ_b, L_A, surround, CCT_w=CCT_w), + specification, + decimal=7, + ) def test_domain_range_scale_XYZ_to_Hunt(self): """ - Tests :func:`colour.appearance.hunt.XYZ_to_Hunt` definition domain + Test :func:`colour.appearance.hunt.XYZ_to_Hunt` definition domain and range scale support. """ @@ -81,15 +145,16 @@ def test_domain_range_scale_XYZ_to_Hunt(self): XYZ_w = np.array([95.05, 100.00, 108.88]) XYZ_b = np.array([95.05, 100.00, 108.88]) L_A = 318.31 - surround = VIEWING_CONDITIONS_HUNT['Normal Scenes'] + surround = VIEWING_CONDITIONS_HUNT["Normal Scenes"] CCT_w = 6504.0 specification = XYZ_to_Hunt( - XYZ, XYZ_w, XYZ_b, L_A, surround, CCT_w=CCT_w)[:-2] + XYZ, XYZ_w, XYZ_b, L_A, surround, CCT_w=CCT_w + ) d_r = ( - ('reference', 1, 1), - (1, 0.01, np.array([1, 1, 1 / 360, 1, 1, 1])), - (100, 1, np.array([1, 1, 100 / 360, 1, 1, 1])), + ("reference", 1, 1), + ("1", 0.01, np.array([1, 1, 1 / 360, 1, 1, 1, np.nan, np.nan])), + ("100", 1, np.array([1, 1, 100 / 360, 1, 1, 1, np.nan, np.nan])), ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): @@ -100,14 +165,16 @@ def test_domain_range_scale_XYZ_to_Hunt(self): XYZ_b * factor_a, L_A, surround, - CCT_w=CCT_w)[:-2], - specification * factor_b, - decimal=7) + CCT_w=CCT_w, + ), + as_float_array(specification) * factor_b, + decimal=7, + ) @ignore_numpy_errors - def test_raise_exception_CIECAM02_to_XYZ(self): + def test_raise_exception_XYZ_to_Hunt(self): """ - Tests :func:`colour.appearance.hunt.XYZ_to_Hunt` definition raised + Test :func:`colour.appearance.hunt.XYZ_to_Hunt` definition raised exception. """ @@ -115,7 +182,7 @@ def test_raise_exception_CIECAM02_to_XYZ(self): XYZ_w = np.array([95.05, 100.00, 108.88]) XYZ_b = np.array([95.05, 100.00, 108.88]) L_A = 318.31 - surround = VIEWING_CONDITIONS_HUNT['Normal Scenes'] + surround = VIEWING_CONDITIONS_HUNT["Normal Scenes"] CCT_w = 6504.0 S = S_w = 0.5 @@ -135,9 +202,9 @@ def test_raise_exception_CIECAM02_to_XYZ(self): pass @ignore_numpy_errors - def test_XYZ_p_CIECAM02_to_XYZ(self): + def test_XYZ_p_XYZ_to_Hunt(self): """ - Tests :func:`colour.appearance.hunt.XYZ_to_Hunt` definition *XYZ_p* + Test :func:`colour.appearance.hunt.XYZ_to_Hunt` definition *XYZ_p* argument handling. """ @@ -145,7 +212,7 @@ def test_XYZ_p_CIECAM02_to_XYZ(self): XYZ_w = np.array([95.05, 100.00, 108.88]) XYZ_b = XYZ_p = np.array([95.05, 100.00, 108.88]) L_A = 318.31 - surround = VIEWING_CONDITIONS_HUNT['Normal Scenes'] + surround = VIEWING_CONDITIONS_HUNT["Normal Scenes"] CCT_w = 6504.0 np.testing.assert_almost_equal( @@ -157,21 +224,26 @@ def test_XYZ_p_CIECAM02_to_XYZ(self): surround, XYZ_p=XYZ_p, CCT_w=CCT_w, - )[:-2], - np.array([ - 30.046267861960700, - 0.121050839936350, - 269.273759446144600, - 0.019909320692942, - 22.209765491265024, - 0.123896438259997, - ]), - decimal=7) + ), + np.array( + [ + 30.046267861960700, + 0.121050839936350, + 269.273759446144600, + 0.019909320692942, + 22.209765491265024, + 0.123896438259997, + np.nan, + np.nan, + ] + ), + decimal=7, + ) @ignore_numpy_errors def test_nan_XYZ_to_Hunt(self): """ - Tests :func:`colour.appearance.hunt.XYZ_to_Hunt` definition + Test :func:`colour.appearance.hunt.XYZ_to_Hunt` definition nan support. """ diff --git a/colour/appearance/tests/test_kim2009.py b/colour/appearance/tests/test_kim2009.py new file mode 100644 index 0000000000..00d0255e06 --- /dev/null +++ b/colour/appearance/tests/test_kim2009.py @@ -0,0 +1,453 @@ +# !/usr/bin/env python +"""Defines the unit tests for the :mod:`colour.appearance.kim2009` module.""" + +import numpy as np +import unittest +from itertools import permutations + +from colour.appearance import ( + MEDIA_PARAMETERS_KIM2009, + VIEWING_CONDITIONS_KIM2009, + InductionFactors_Kim2009, + CAM_Specification_Kim2009, + MediaParameters_Kim2009, + XYZ_to_Kim2009, + Kim2009_to_XYZ, +) +from colour.utilities import ( + as_float_array, + domain_range_scale, + ignore_numpy_errors, + tsplit, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestXYZ_to_Kim2009", + "TestKim2009_to_XYZ", +] + + +class TestXYZ_to_Kim2009(unittest.TestCase): + """ + Define :func:`colour.appearance.kim2009.XYZ_to_Kim2009` definition unit + tests methods. + """ + + def test_XYZ_to_Kim2009(self): + """Test :func:`colour.appearance.kim2009.XYZ_to_Kim2009` definition.""" + + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_w = np.array([95.05, 100.00, 108.88]) + L_a = 318.31 + media = MEDIA_PARAMETERS_KIM2009["CRT Displays"] + surround = VIEWING_CONDITIONS_KIM2009["Average"] + np.testing.assert_almost_equal( + XYZ_to_Kim2009(XYZ, XYZ_w, L_a, media, surround), + np.array( + [ + 28.86190898, + 0.55924559, + 219.04806678, + 9.38377973, + 52.71388839, + 0.46417384, + 278.06028246, + np.nan, + ] + ), + decimal=7, + ) + + XYZ = np.array([57.06, 43.06, 31.96]) + L_a = 31.83 + np.testing.assert_almost_equal( + XYZ_to_Kim2009(XYZ, XYZ_w, L_a, media, surround), + np.array( + [ + 70.15940419, + 57.89295872, + 21.27017200, + 61.23630434, + 128.14034598, + 48.05115573, + 1.41841443, + np.nan, + ] + ), + decimal=7, + ) + + XYZ = np.array([3.53, 6.56, 2.14]) + XYZ_w = np.array([109.85, 100.00, 35.58]) + L_a = 318.31 + np.testing.assert_almost_equal( + XYZ_to_Kim2009(XYZ, XYZ_w, L_a, media, surround), + np.array( + [ + -4.83430022, + 37.42013921, + 177.12166057, + np.nan, + -8.82944930, + 31.05871555, + 220.36270343, + np.nan, + ] + ), + decimal=7, + ) + + XYZ = np.array([19.01, 20.00, 21.78]) + L_a = 31.83 + np.testing.assert_almost_equal( + XYZ_to_Kim2009(XYZ, XYZ_w, L_a, media, surround), + np.array( + [ + 47.20460719, + 56.35723637, + 241.04877377, + 73.65830083, + 86.21530880, + 46.77650619, + 301.77516676, + np.nan, + ] + ), + decimal=7, + ) + + def test_n_dimensional_XYZ_to_Kim2009(self): + """ + Test :func:`colour.appearance.kim2009.XYZ_to_Kim2009` definition + n-dimensional support. + """ + + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_w = np.array([95.05, 100.00, 108.88]) + L_a = 318.31 + media = MEDIA_PARAMETERS_KIM2009["CRT Displays"] + surround = VIEWING_CONDITIONS_KIM2009["Average"] + specification = XYZ_to_Kim2009(XYZ, XYZ_w, L_a, media, surround) + + XYZ = np.tile(XYZ, (6, 1)) + specification = np.tile(specification, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_Kim2009(XYZ, XYZ_w, L_a, media, surround), + specification, + decimal=7, + ) + + XYZ_w = np.tile(XYZ_w, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_Kim2009(XYZ, XYZ_w, L_a, media, surround), + specification, + decimal=7, + ) + + XYZ = np.reshape(XYZ, (2, 3, 3)) + XYZ_w = np.reshape(XYZ_w, (2, 3, 3)) + specification = np.reshape(specification, (2, 3, 8)) + np.testing.assert_almost_equal( + XYZ_to_Kim2009(XYZ, XYZ_w, L_a, media, surround), + specification, + decimal=7, + ) + + @ignore_numpy_errors + def test_domain_range_scale_XYZ_to_Kim2009(self): + """ + Test :func:`colour.appearance.kim2009.XYZ_to_Kim2009` definition + domain and range scale support. + """ + + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_w = np.array([95.05, 100.00, 108.88]) + L_a = 318.31 + media = MEDIA_PARAMETERS_KIM2009["CRT Displays"] + surround = VIEWING_CONDITIONS_KIM2009["Average"] + specification = XYZ_to_Kim2009(XYZ, XYZ_w, L_a, media, surround) + + d_r = ( + ("reference", 1, 1), + ( + "1", + 0.01, + np.array( + [ + 1 / 100, + 1 / 100, + 1 / 360, + 1 / 100, + 1 / 100, + 1 / 100, + 1 / 400, + np.nan, + ] + ), + ), + ( + "100", + 1, + np.array([1, 1, 100 / 360, 1, 1, 1, 100 / 400, np.nan]), + ), + ) + for scale, factor_a, factor_b in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + XYZ_to_Kim2009( + XYZ * factor_a, XYZ_w * factor_a, L_a, media, surround + ), + as_float_array(specification) * factor_b, + decimal=7, + ) + + @ignore_numpy_errors + def test_nan_XYZ_to_Kim2009(self): + """ + Test :func:`colour.appearance.kim2009.XYZ_to_Kim2009` definition + nan support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + XYZ = np.array(case) + XYZ_w = np.array(case) + L_a = case[0] + media = MediaParameters_Kim2009(case[0]) + surround = InductionFactors_Kim2009(case[0], case[0], case[0]) + XYZ_to_Kim2009(XYZ, XYZ_w, L_a, media, surround) + + +class TestKim2009_to_XYZ(unittest.TestCase): + """ + Define :func:`colour.appearance.kim2009.Kim2009_to_XYZ` definition unit + tests methods. + """ + + def test_Kim2009_to_XYZ(self): + """Test :func:`colour.appearance.kim2009.Kim2009_to_XYZ` definition.""" + + specification = CAM_Specification_Kim2009( + 28.86190898, + 0.55924559, + 219.04806678, + 9.38377973, + 52.71388839, + 0.46417384, + 278.06028246, + np.nan, + ) + XYZ_w = np.array([95.05, 100.00, 108.88]) + L_a = 318.31 + media = MEDIA_PARAMETERS_KIM2009["CRT Displays"] + surround = VIEWING_CONDITIONS_KIM2009["Average"] + np.testing.assert_allclose( + Kim2009_to_XYZ(specification, XYZ_w, L_a, media, surround), + np.array([19.01, 20.00, 21.78]), + atol=0.01, + rtol=0.01, + ) + + specification = CAM_Specification_Kim2009( + 70.15940419, + 57.89295872, + 21.27017200, + 61.23630434, + 128.14034598, + 48.05115573, + 1.41841443, + np.nan, + ) + L_a = 31.83 + np.testing.assert_allclose( + Kim2009_to_XYZ(specification, XYZ_w, L_a, media, surround), + np.array([57.06, 43.06, 31.96]), + atol=0.01, + rtol=0.01, + ) + + specification = CAM_Specification_Kim2009( + -4.83430022, + 37.42013921, + 177.12166057, + np.nan, + -8.82944930, + 31.05871555, + 220.36270343, + np.nan, + ) + XYZ_w = np.array([109.85, 100.00, 35.58]) + L_a = 318.31 + np.testing.assert_allclose( + Kim2009_to_XYZ(specification, XYZ_w, L_a, media, surround), + np.array([3.53, 6.56, 2.14]), + atol=0.01, + rtol=0.01, + ) + + specification = CAM_Specification_Kim2009( + 47.20460719, + 56.35723637, + 241.04877377, + 73.65830083, + 86.21530880, + 46.77650619, + 301.77516676, + np.nan, + ) + L_a = 31.83 + np.testing.assert_allclose( + Kim2009_to_XYZ(specification, XYZ_w, L_a, media, surround), + np.array([19.01, 20.00, 21.78]), + atol=0.01, + rtol=0.01, + ) + + def test_n_dimensional_Kim2009_to_XYZ(self): + """ + Test :func:`colour.appearance.kim2009.Kim2009_to_XYZ` definition + n-dimensional support. + """ + + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_w = np.array([95.05, 100.00, 108.88]) + L_a = 318.31 + media = MEDIA_PARAMETERS_KIM2009["CRT Displays"] + surround = VIEWING_CONDITIONS_KIM2009["Average"] + specification = XYZ_to_Kim2009(XYZ, XYZ_w, L_a, media, surround) + XYZ = Kim2009_to_XYZ(specification, XYZ_w, L_a, media, surround) + + specification = CAM_Specification_Kim2009( + *np.transpose(np.tile(tsplit(specification), (6, 1))).tolist() + ) + XYZ = np.tile(XYZ, (6, 1)) + np.testing.assert_almost_equal( + Kim2009_to_XYZ(specification, XYZ_w, L_a, media, surround), + XYZ, + decimal=7, + ) + + XYZ_w = np.tile(XYZ_w, (6, 1)) + np.testing.assert_almost_equal( + Kim2009_to_XYZ(specification, XYZ_w, L_a, media, surround), + XYZ, + decimal=7, + ) + + specification = CAM_Specification_Kim2009( + *tsplit(np.reshape(specification, (2, 3, 8))).tolist() + ) + XYZ_w = np.reshape(XYZ_w, (2, 3, 3)) + XYZ = np.reshape(XYZ, (2, 3, 3)) + np.testing.assert_almost_equal( + Kim2009_to_XYZ(specification, XYZ_w, L_a, media, surround), + XYZ, + decimal=7, + ) + + @ignore_numpy_errors + def test_domain_range_scale_Kim2009_to_XYZ(self): + """ + Test :func:`colour.appearance.kim2009.Kim2009_to_XYZ` definition + domain and range scale support. + """ + + XYZ_i = np.array([19.01, 20.00, 21.78]) + XYZ_w = np.array([95.05, 100.00, 108.88]) + L_a = 318.31 + media = MEDIA_PARAMETERS_KIM2009["CRT Displays"] + surround = VIEWING_CONDITIONS_KIM2009["Average"] + specification = XYZ_to_Kim2009(XYZ_i, XYZ_w, L_a, media, surround) + XYZ = Kim2009_to_XYZ(specification, XYZ_w, L_a, media, surround) + + d_r = ( + ("reference", 1, 1), + ( + "1", + np.array( + [ + 1 / 100, + 1 / 100, + 1 / 360, + 1 / 100, + 1 / 100, + 1 / 100, + 1 / 400, + np.nan, + ] + ), + 0.01, + ), + ( + "100", + np.array([1, 1, 100 / 360, 1, 1, 1, 100 / 400, np.nan]), + 1, + ), + ) + for scale, factor_a, factor_b in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + Kim2009_to_XYZ( + specification * factor_a, + XYZ_w * factor_b, + L_a, + media, + surround, + ), + XYZ * factor_b, + decimal=7, + ) + + @ignore_numpy_errors + def test_raise_exception_Kim2009_to_XYZ(self): + """ + Test :func:`colour.appearance.kim2009.Kim2009_to_XYZ` definition + raised exception. + """ + + self.assertRaises( + ValueError, + Kim2009_to_XYZ, + CAM_Specification_Kim2009( + 41.731091132513917, + None, + 219.04843265831178, + ), + np.array([95.05, 100.00, 108.88]), + 318.31, + 20.0, + VIEWING_CONDITIONS_KIM2009["Average"], + ) + + @ignore_numpy_errors + def test_nan_Kim2009_to_XYZ(self): + """ + Test :func:`colour.appearance.kim2009.Kim2009_to_XYZ` definition nan + support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + J = case[0] + C = case[0] + h = case[0] + XYZ_w = np.array(case) + L_a = case[0] + media = MediaParameters_Kim2009(case[0]) + surround = InductionFactors_Kim2009(case[0], case[0], case[0]) + Kim2009_to_XYZ( + CAM_Specification_Kim2009(J, C, h, M=50), + XYZ_w, + L_a, + media, + surround, + ) diff --git a/colour/appearance/tests/test_llab.py b/colour/appearance/tests/test_llab.py index 0ed13d25a2..f4b5d311bc 100644 --- a/colour/appearance/tests/test_llab.py +++ b/colour/appearance/tests/test_llab.py @@ -1,125 +1,141 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.appearance.llab` module. -""" - -from __future__ import division, unicode_literals -from colour.utilities.array import tstack +"""Defines the unit tests for the :mod:`colour.appearance.llab` module.""" import numpy as np - -try: - from unittest import mock -except ImportError: # pragma: no cover - import mock +import unittest +from unittest import mock from itertools import permutations -from colour.appearance import (VIEWING_CONDITIONS_LLAB, InductionFactors_LLAB, - XYZ_to_LLAB, llab) -from colour.appearance.tests.common import ColourAppearanceModelTest -from colour.utilities import domain_range_scale, ignore_numpy_errors - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['TestLLABColourAppearanceModel'] - - -class TestLLABColourAppearanceModel(ColourAppearanceModelTest): +from colour.appearance import ( + VIEWING_CONDITIONS_LLAB, + InductionFactors_LLAB, + XYZ_to_LLAB, + llab, +) +from colour.utilities import ( + as_float_array, + domain_range_scale, + ignore_numpy_errors, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestXYZ_to_LLAB", +] + + +class TestXYZ_to_LLAB(unittest.TestCase): """ - Defines :mod:`colour.appearance.llab` module unit tests methods for - *LLAB(l:c)* colour appearance model. + Define :func:`colour.appearance.llab.XYZ_to_LLAB` definition unit + tests methods. """ - FIXTURE_BASENAME = 'llab.csv' - - OUTPUT_ATTRIBUTES = { - 'L_L': 'J', - 'Ch_L': 'C', - 'h_L': 'h', - 's_L': 's', - 'C_L': 'M', - 'A_L': 'a', - 'B_L': 'b' - } - - def output_specification_from_data(self, data): - """ - Returns the *LLAB(l:c)* colour appearance model output specification - from given data. - - Parameters - ---------- - data : list - Fixture data. - - Returns - ------- - CAM_Specification_LLAB - *LLAB(l:c)* colour appearance model specification. - """ - - XYZ = tstack([data['X'], data['Y'], data['Z']]) - XYZ_0 = tstack([data['X_0'], data['Y_0'], data['Z_0']]) - - specification = XYZ_to_LLAB( - XYZ, XYZ_0, data['Y_b'], data['L'], - InductionFactors_LLAB(1, data['F_S'], data['F_L'], data['F_C'])) - - return specification - - def test_examples(self): + def test_XYZ_to_LLAB(self): """ - Tests the colour appearance model implementation. - - Returns - ------- - tuple + Test :func:`colour.appearance.llab.XYZ_to_LLAB` definition. Notes ----- - - Reference data was computed using a rounded - :attr:`colour.appearance.llab.MATRIX_RGB_TO_XYZ_LLAB`, therefore a - patched version is used for unit tests. + - The test values have been generated from data of the following file + by *Fairchild (2013)*: + http://rit-mcsl.org/fairchild//files/AppModEx.xls """ with mock.patch( - 'colour.appearance.llab.MATRIX_RGB_TO_XYZ_LLAB', - np.around( - np.linalg.inv(llab.MATRIX_XYZ_TO_RGB_LLAB), decimals=4)): - super(TestLLABColourAppearanceModel, self).test_examples() - - def test_n_dimensional_examples(self): + "colour.appearance.llab.MATRIX_RGB_TO_XYZ_LLAB", + np.around(np.linalg.inv(llab.MATRIX_XYZ_TO_RGB_LLAB), decimals=4), + ): + + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_0 = np.array([95.05, 100.00, 108.88]) + Y_b = 20 + L = 318.31 + surround = VIEWING_CONDITIONS_LLAB[ + "Reference Samples & Images, Average Surround, Subtending < 4" + ] + np.testing.assert_allclose( + XYZ_to_LLAB(XYZ, XYZ_0, Y_b, L, surround), + np.array([37.37, 0.01, 229.5, 0, 0.02, np.nan, -0.01, -0.01]), + rtol=0.01, + atol=0.01, + ) + + XYZ = np.array([57.06, 43.06, 31.96]) + L = 31.83 + np.testing.assert_allclose( + XYZ_to_LLAB(XYZ, XYZ_0, Y_b, L, surround), + np.array( + [61.26, 30.51, 22.3, 0.5, 56.55, np.nan, 52.33, 21.43] + ), + rtol=0.01, + atol=0.01, + ) + + XYZ = np.array([3.53, 6.56, 2.14]) + XYZ_0 = np.array([109.85, 100.00, 35.58]) + L = 318.31 + np.testing.assert_allclose( + XYZ_to_LLAB(XYZ, XYZ_0, Y_b, L, surround), + np.array( + [16.25, 30.43, 173.8, 1.87, 53.83, np.nan, -53.51, 5.83] + ), + rtol=0.01, + atol=0.01, + ) + + XYZ = np.array([19.01, 20.00, 21.78]) + L = 31.83 + np.testing.assert_allclose( + XYZ_to_LLAB(XYZ, XYZ_0, Y_b, L, surround), + np.array( + [39.82, 29.34, 271.9, 0.74, 54.59, np.nan, 1.76, -54.56] + ), + rtol=0.01, + atol=0.01, + ) + + def test_n_dimensional_XYZ_to_LLAB(self): + """ + Test :func:`colour.appearance.llab.XYZ_to_LLAB` definition + n-dimensional support. """ - Tests the colour appearance model implementation n-dimensional arrays - support. - Returns - ------- - tuple + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_0 = np.array([95.05, 100.00, 108.88]) + Y_b = 20 + L = 318.31 + surround = surround = VIEWING_CONDITIONS_LLAB[ + "Reference Samples & Images, Average Surround, Subtending < 4" + ] + specification = XYZ_to_LLAB(XYZ, XYZ_0, Y_b, L, surround) + + XYZ = np.tile(XYZ, (6, 1)) + specification = np.tile(specification, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_LLAB(XYZ, XYZ_0, Y_b, L, surround), specification, decimal=7 + ) - Notes - ----- - - Reference data was computed using a rounded - :attr:`colour.appearance.llab.MATRIX_RGB_TO_XYZ_LLAB`, therefore a - patched version is used for unit tests. - """ + XYZ_0 = np.tile(XYZ_0, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_LLAB(XYZ, XYZ_0, Y_b, L, surround), specification, decimal=7 + ) - with mock.patch( - 'colour.appearance.llab.MATRIX_RGB_TO_XYZ_LLAB', - np.around( - np.linalg.inv(llab.MATRIX_XYZ_TO_RGB_LLAB), decimals=4)): - super(TestLLABColourAppearanceModel, - self).test_n_dimensional_examples() + XYZ = np.reshape(XYZ, (2, 3, 3)) + XYZ_0 = np.reshape(XYZ_0, (2, 3, 3)) + specification = np.reshape(specification, (2, 3, 8)) + np.testing.assert_almost_equal( + XYZ_to_LLAB(XYZ, XYZ_0, Y_b, L, surround), specification, decimal=7 + ) def test_colourspace_conversion_matrices_precision(self): """ - Tests for loss of precision in conversion between + Test for loss of precision in conversion between *LLAB(l:c)* colour appearance model *CIE XYZ* tristimulus values and normalised cone responses matrix. """ @@ -133,34 +149,36 @@ def test_colourspace_conversion_matrices_precision(self): def test_domain_range_scale_XYZ_to_LLAB(self): """ - Tests :func:`colour.appearance.llab.XYZ_to_LLAB` definition domain + Test :func:`colour.appearance.llab.XYZ_to_LLAB` definition domain and range scale support. """ XYZ = np.array([19.01, 20.00, 21.78]) XYZ_0 = np.array([95.05, 100.00, 108.88]) - Y_b = 20.0 + Y_b = 20 L = 318.31 - surround = VIEWING_CONDITIONS_LLAB['ref_average_4_minus'] - specification = XYZ_to_LLAB(XYZ, XYZ_0, Y_b, L, surround)[:5] + surround = VIEWING_CONDITIONS_LLAB["ref_average_4_minus"] + specification = XYZ_to_LLAB(XYZ, XYZ_0, Y_b, L, surround) d_r = ( - ('reference', 1, 1), - (1, 0.01, np.array([1, 1, 1 / 360, 1, 1])), - (100, 1, np.array([1, 1, 100 / 360, 1, 1])), + ("reference", 1, 1), + ("1", 0.01, np.array([1, 1, 1 / 360, 1, 1, np.nan, 1, 1])), + ("100", 1, np.array([1, 1, 100 / 360, 1, 1, np.nan, 1, 1])), ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - XYZ_to_LLAB(XYZ * factor_a, XYZ_0 * factor_a, Y_b, L, - surround)[:5], - specification * factor_b, - decimal=7) + XYZ_to_LLAB( + XYZ * factor_a, XYZ_0 * factor_a, Y_b, L, surround + ), + as_float_array(specification) * factor_b, + decimal=7, + ) @ignore_numpy_errors def test_nan_XYZ_to_LLAB(self): """ - Tests :func:`colour.appearance.llab.XYZ_to_LLAB` definition + Test :func:`colour.appearance.llab.XYZ_to_LLAB` definition nan support. """ diff --git a/colour/appearance/tests/test_nayatani95.py b/colour/appearance/tests/test_nayatani95.py index 406e1de6bd..7351634a0f 100644 --- a/colour/appearance/tests/test_nayatani95.py +++ b/colour/appearance/tests/test_nayatani95.py @@ -1,73 +1,131 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.appearance.nayatani95` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.appearance.nayatani95` module.""" import numpy as np +import unittest from itertools import permutations from colour.appearance import XYZ_to_Nayatani95 -from colour.appearance.tests.common import ColourAppearanceModelTest -from colour.utilities import domain_range_scale, ignore_numpy_errors, tstack +from colour.utilities import ( + as_float_array, + domain_range_scale, + ignore_numpy_errors, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestNayatani95ColourAppearanceModel'] +__all__ = [ + "TestXYZ_to_Nayatani95", +] -class TestNayatani95ColourAppearanceModel(ColourAppearanceModelTest): +class TestXYZ_to_Nayatani95(unittest.TestCase): """ - Defines :mod:`colour.appearance.nayatani95` module unit tests methods for - *Nayatani (1995)* colour appearance model. + Define :func:`colour.appearance.nayatani95.XYZ_to_Nayatani95` definition + unit tests methods. """ - FIXTURE_BASENAME = 'nayatani95.csv' + def test_XYZ_to_Nayatani95(self): + """ + Test :func:`colour.appearance.nayatani95.XYZ_to_Nayatani95` + definition. + + Notes + ----- + - The test values have been generated from data of the following file + by *Fairchild (2013)*: + http://rit-mcsl.org/fairchild//files/AppModEx.xls + """ + + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_n = np.array([95.05, 100.00, 108.88]) + Y_o = 20 + E_o = 5000 + E_or = 1000 + np.testing.assert_allclose( + XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or), + np.array([50, 0.01, 257.5, 0.01, 62.6, 0.02, np.nan, np.nan, 50]), + rtol=0.01, + atol=0.01, + ) + + XYZ = np.array([57.06, 43.06, 31.96]) + E_o = 500 + np.testing.assert_allclose( + XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or), + np.array([73, 48.3, 21.6, 37.1, 67.3, 42.9, np.nan, np.nan, 75.9]), + rtol=0.01, + atol=0.01, + ) + + XYZ = np.array([3.53, 6.56, 2.14]) + XYZ_n = np.array([109.85, 100.00, 35.58]) + E_o = 5000 + np.testing.assert_allclose( + XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or), + np.array( + [24.5, 49.3, 190.6, 81.3, 37.5, 62.1, np.nan, np.nan, 29.7] + ), + rtol=0.01, + atol=0.01, + ) - OUTPUT_ATTRIBUTES = { - 'L_star_P': 'L_star_P', - 'C': 'C', - 'theta': 'h', - 'S': 's', - 'B_r': 'Q', - 'M': 'M', - 'L_star_N': 'L_star_N' - } + XYZ = np.array([19.01, 20.00, 21.78]) + E_o = 500 + np.testing.assert_allclose( + XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or), + np.array( + [49.4, 39.9, 236.3, 40.2, 44.2, 35.8, np.nan, np.nan, 49.4] + ), + rtol=0.01, + atol=0.01, + ) - def output_specification_from_data(self, data): + def test_n_dimensional_XYZ_to_Nayatani95(self): """ - Returns the *Nayatani (1995)* colour appearance model output - specification from given data. - - Parameters - ---------- - data : list - Fixture data. - - Returns - ------- - CAM_Specification_Nayatani95 - *Nayatani (1995)* colour appearance model specification. + Test :func:`colour.appearance.nayatani95.XYZ_to_Nayatani95` definition + n-dimensional support. """ - XYZ = tstack([data['X'], data['Y'], data['Z']]) - XYZ_n = tstack([data['X_n'], data['Y_n'], data['Z_n']]) + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_n = np.array([95.05, 100.00, 108.88]) + Y_o = 20 + E_o = 5000 + E_or = 1000 + specification = XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or) + + XYZ = np.tile(XYZ, (6, 1)) + specification = np.tile(specification, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or), + specification, + decimal=7, + ) - specification = XYZ_to_Nayatani95(XYZ, XYZ_n, data['Y_o'], data['E_o'], - data['E_or']) + XYZ_n = np.tile(XYZ_n, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or), + specification, + decimal=7, + ) - return specification + XYZ = np.reshape(XYZ, (2, 3, 3)) + XYZ_n = np.reshape(XYZ_n, (2, 3, 3)) + specification = np.reshape(specification, (2, 3, 9)) + np.testing.assert_almost_equal( + XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or), + specification, + decimal=7, + ) def test_domain_range_scale_XYZ_to_Nayatani95(self): """ - Tests :func:`colour.appearance.nayatani95.XYZ_to_Nayatani95` definition + Test :func:`colour.appearance.nayatani95.XYZ_to_Nayatani95` definition domain and range scale support. """ @@ -76,25 +134,31 @@ def test_domain_range_scale_XYZ_to_Nayatani95(self): Y_o = 20.0 E_o = 5000.0 E_or = 1000.0 - specification = XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or)[:6] + specification = XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or) d_r = ( - ('reference', 1, 1), - (1, 0.01, np.array([1, 1, 1 / 360, 1, 1, 1])), - (100, 1, np.array([1, 1, 100 / 360, 1, 1, 1])), + ("reference", 1, 1), + ("1", 0.01, np.array([1, 1, 1 / 360, 1, 1, 1, np.nan, np.nan, 1])), + ( + "100", + 1, + np.array([1, 1, 100 / 360, 1, 1, 1, np.nan, np.nan, 1]), + ), ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - XYZ_to_Nayatani95(XYZ * factor_a, XYZ_n * factor_a, Y_o, - E_o, E_or)[:6], - specification * factor_b, - decimal=7) + XYZ_to_Nayatani95( + XYZ * factor_a, XYZ_n * factor_a, Y_o, E_o, E_or + ), + as_float_array(specification) * factor_b, + decimal=7, + ) @ignore_numpy_errors def test_nan_XYZ_to_Nayatani95(self): """ - Tests :func:`colour.appearance.nayatani95.XYZ_to_Nayatani95` definition + Test :func:`colour.appearance.nayatani95.XYZ_to_Nayatani95` definition nan support. """ diff --git a/colour/appearance/tests/test_rlab.py b/colour/appearance/tests/test_rlab.py index 34f37ec4b3..036b16c5b4 100644 --- a/colour/appearance/tests/test_rlab.py +++ b/colour/appearance/tests/test_rlab.py @@ -1,100 +1,151 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.appearance.rlab` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.appearance.rlab` module.""" import numpy as np +import unittest from itertools import permutations -from colour.appearance import (D_FACTOR_RLAB, VIEWING_CONDITIONS_RLAB, - XYZ_to_RLAB) -from colour.appearance.tests.common import ColourAppearanceModelTest -from colour.utilities import domain_range_scale, ignore_numpy_errors, tstack +from colour.appearance import ( + D_FACTOR_RLAB, + VIEWING_CONDITIONS_RLAB, + XYZ_to_RLAB, +) +from colour.utilities import ( + as_float_array, + domain_range_scale, + ignore_numpy_errors, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestXYZ_to_RLAB", +] + + +class TestXYZ_to_RLAB(unittest.TestCase): + """ + Define :func:`colour.appearance.rlab.XYZ_to_RLAB` definition unit + tests methods. + """ -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + def test_XYZ_to_RLAB(self): + """ + Test :func:`colour.appearance.rlab.XYZ_to_RLAB` definition. -__all__ = ['TestRLABColourAppearanceModel'] + Notes + ----- + - The test values have been generated from data of the following file + by *Fairchild (2013)*: + http://rit-mcsl.org/fairchild//files/AppModEx.xls + """ + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_n = np.array([95.05, 100.00, 108.88]) + Y_n = 318.31 + sigma = 0.4347 + np.testing.assert_allclose( + XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma), + np.array([49.67, 0.01, 270, 0, np.nan, 0, -0.01]), + rtol=0.01, + atol=0.01, + ) -class TestRLABColourAppearanceModel(ColourAppearanceModelTest): - """ - Defines :mod:`colour.appearance.rlab` module unit tests methods for - *RLAB* colour appearance model. - """ + XYZ = np.array([57.06, 43.06, 31.96]) + Y_n = 31.83 + np.testing.assert_allclose( + XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma), + np.array([69.33, 49.74, 21.3, 0.72, np.nan, 46.33, 18.09]), + rtol=0.01, + atol=0.01, + ) - FIXTURE_BASENAME = 'rlab.csv' + XYZ = np.array([3.53, 6.56, 2.14]) + XYZ_n = np.array([109.85, 100.00, 35.58]) + Y_n = 318.31 + np.testing.assert_allclose( + XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma), + np.array([30.78, 41.02, 176.9, 1.33, np.nan, -40.96, 2.25]), + rtol=0.01, + atol=0.01, + ) - OUTPUT_ATTRIBUTES = { - 'LR': 'J', - 'CR': 'C', - 'hR': 'h', - 'sR': 's', - 'aR': 'a', - 'bR': 'b' - } + XYZ = np.array([19.01, 20.00, 21.78]) + Y_n = 31.83 + np.testing.assert_allclose( + XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma), + np.array([49.83, 54.87, 286.5, 1.1, np.nan, 15.57, -52.61]), + rtol=0.01, + atol=0.01, + ) - def output_specification_from_data(self, data): + def test_n_dimensional_XYZ_to_RLAB(self): """ - Returns the *RLAB* colour appearance model output specification from - given data. - - Parameters - ---------- - data : list - Fixture data. - - Returns - ------- - CAM_Specification_RLAB - *RLAB* colour appearance model specification. + Test :func:`colour.appearance.rlab.XYZ_to_RLAB` definition + n-dimensional support. """ - XYZ = tstack([data['X'], data['Y'], data['Z']]) - XYZ_n = tstack([data['X_n'], data['Y_n'], data['Z_n']]) + XYZ = np.array([19.01, 20.00, 21.78]) + XYZ_n = np.array([95.05, 100.00, 108.88]) + Y_n = 318.31 + sigma = 0.4347 + specification = XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma) + + XYZ = np.tile(XYZ, (6, 1)) + specification = np.tile(specification, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma), specification, decimal=7 + ) - specification = XYZ_to_RLAB(XYZ, XYZ_n, data['Y_n2'], data['sigma'], - data['D']) + XYZ_n = np.tile(XYZ_n, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma), specification, decimal=7 + ) - return specification + XYZ = np.reshape(XYZ, (2, 3, 3)) + XYZ_n = np.reshape(XYZ_n, (2, 3, 3)) + specification = np.reshape(specification, (2, 3, 7)) + np.testing.assert_almost_equal( + XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma), specification, decimal=7 + ) def test_domain_range_scale_XYZ_to_RLAB(self): """ - Tests :func:`colour.appearance.rlab.XYZ_to_RLAB` definition domain and + Test :func:`colour.appearance.rlab.XYZ_to_RLAB` definition domain and range scale support. """ XYZ = np.array([19.01, 20.00, 21.78]) XYZ_n = np.array([109.85, 100, 35.58]) Y_n = 31.83 - sigma = VIEWING_CONDITIONS_RLAB['Average'] - D = D_FACTOR_RLAB['Hard Copy Images'] - specification = XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma, D)[:4] + sigma = VIEWING_CONDITIONS_RLAB["Average"] + D = D_FACTOR_RLAB["Hard Copy Images"] + specification = XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma, D) d_r = ( - ('reference', 1, 1), - (1, 0.01, np.array([1, 1, 1 / 360, 1])), - (100, 1, np.array([1, 1, 100 / 360, 1])), + ("reference", 1, 1), + ("1", 0.01, np.array([1, 1, 1 / 360, 1, np.nan, 1, 1])), + ("100", 1, np.array([1, 1, 100 / 360, 1, np.nan, 1, 1])), ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - XYZ_to_RLAB(XYZ * factor_a, XYZ_n * factor_a, Y_n, sigma, - D)[:4], - specification * factor_b, - decimal=7) + XYZ_to_RLAB( + XYZ * factor_a, XYZ_n * factor_a, Y_n, sigma, D + ), + as_float_array(specification) * factor_b, + decimal=7, + ) @ignore_numpy_errors def test_nan_XYZ_to_RLAB(self): """ - Tests :func:`colour.appearance.rlab.XYZ_to_RLAB` definition nan + Test :func:`colour.appearance.rlab.XYZ_to_RLAB` definition nan support. """ diff --git a/colour/appearance/tests/test_zcam.py b/colour/appearance/tests/test_zcam.py new file mode 100644 index 0000000000..4703dc7dbb --- /dev/null +++ b/colour/appearance/tests/test_zcam.py @@ -0,0 +1,540 @@ +# !/usr/bin/env python +"""Defines the unit tests for the :mod:`colour.appearance.zcam` module.""" + +import numpy as np +import unittest +from itertools import permutations + +from colour.appearance import ( + VIEWING_CONDITIONS_ZCAM, + InductionFactors_ZCAM, + CAM_Specification_ZCAM, + XYZ_to_ZCAM, + ZCAM_to_XYZ, +) +from colour.utilities import ( + as_float_array, + domain_range_scale, + ignore_numpy_errors, + tsplit, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestXYZ_to_ZCAM", + "TestZCAM_to_XYZ", +] + + +class TestXYZ_to_ZCAM(unittest.TestCase): + """ + Define :func:`colour.appearance.zcam.XYZ_to_ZCAM` definition unit tests + methods. + """ + + def test_XYZ_to_ZCAM(self): + """Test :func:`colour.appearance.zcam.XYZ_to_ZCAM` definition.""" + + XYZ = np.array([185, 206, 163]) + XYZ_w = np.array([256, 264, 202]) + L_a = 264 + Y_b = 100 + surround = VIEWING_CONDITIONS_ZCAM["Average"] + np.testing.assert_allclose( + XYZ_to_ZCAM(XYZ, XYZ_w, L_a, Y_b, surround), + np.array( + [ + 92.2520, + 3.0216, + 196.3524, + 19.1314, + 321.3464, + 10.5252, + 237.6401, + np.nan, + 34.7022, + 25.2994, + 91.6837, + ] + ), + rtol=0.025, + atol=0.025, + ) + + XYZ = np.array([89, 96, 120]) + np.testing.assert_allclose( + XYZ_to_ZCAM(XYZ, XYZ_w, L_a, Y_b, surround), + np.array( + [ + 71.2071, + 6.8539, + 250.6422, + 32.7963, + 248.0394, + 23.8744, + 307.0595, + np.nan, + 18.2796, + 40.4621, + 70.4026, + ] + ), + rtol=0.025, + atol=0.025, + ) + + # NOTE: Hue quadrature :math:`H_z` is significantly different for this + # test, i.e. 47.748252 vs 43.8258. + # NOTE: :math:`F_L` as reported in the supplemental document has the + # same value as for :math:`L_a` = 264 instead of 150. The values seem + # to be computed for :math:`L_a` = 264 and :math:`Y_b` = 100. + XYZ = np.array([79, 81, 62]) + # L_a = 150 + # Y_b = 60 + surround = VIEWING_CONDITIONS_ZCAM["Dim"] + np.testing.assert_allclose( + XYZ_to_ZCAM(XYZ, XYZ_w, L_a, Y_b, surround), + np.array( + [ + 68.8890, + 0.9774, + 58.7532, + 12.5916, + 196.7686, + 2.7918, + 43.8258, + np.nan, + 11.0371, + 44.4143, + 68.8737, + ] + ), + rtol=0.025, + atol=4, + ) + + XYZ = np.array([910, 1114, 500]) + XYZ_w = np.array([2103, 2259, 1401]) + L_a = 359 + Y_b = 16 + surround = VIEWING_CONDITIONS_ZCAM["Dark"] + np.testing.assert_allclose( + XYZ_to_ZCAM(XYZ, XYZ_w, L_a, Y_b, surround), + np.array( + [ + 82.6445, + 13.0838, + 123.9464, + 44.7277, + 114.7431, + 18.1655, + 178.6422, + np.nan, + 34.4874, + 26.8778, + 78.2653, + ] + ), + rtol=0.025, + atol=0.025, + ) + + XYZ = np.array([96, 67, 28]) + np.testing.assert_allclose( + XYZ_to_ZCAM(XYZ, XYZ_w, L_a, Y_b, surround), + np.array( + [ + 33.0139, + 19.4070, + 389.7720 % 360, + 86.1882, + 45.8363, + 26.9446, + 397.3301, + np.nan, + 43.6447, + 47.9942, + 30.2593, + ] + ), + rtol=0.025, + atol=0.025, + ) + + def test_n_dimensional_XYZ_to_ZCAM(self): + """ + Test :func:`colour.appearance.zcam.XYZ_to_ZCAM` definition + n-dimensional support. + """ + + XYZ = np.array([185, 206, 163]) + XYZ_w = np.array([256, 264, 202]) + L_a = 264 + Y_b = 100 + surround = VIEWING_CONDITIONS_ZCAM["Average"] + specification = XYZ_to_ZCAM(XYZ, XYZ_w, L_a, Y_b, surround) + + XYZ = np.tile(XYZ, (6, 1)) + specification = np.tile(specification, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_ZCAM(XYZ, XYZ_w, L_a, Y_b, surround), + specification, + decimal=7, + ) + + XYZ_w = np.tile(XYZ_w, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_ZCAM(XYZ, XYZ_w, L_a, Y_b, surround), + specification, + decimal=7, + ) + + XYZ = np.reshape(XYZ, (2, 3, 3)) + XYZ_w = np.reshape(XYZ_w, (2, 3, 3)) + specification = np.reshape(specification, (2, 3, 11)) + np.testing.assert_almost_equal( + XYZ_to_ZCAM(XYZ, XYZ_w, L_a, Y_b, surround), + specification, + decimal=7, + ) + + @ignore_numpy_errors + def test_domain_range_scale_XYZ_to_ZCAM(self): + """ + Test :func:`colour.appearance.zcam.XYZ_to_ZCAM` definition + domain and range scale support. + """ + + XYZ = np.array([185, 206, 163]) + XYZ_w = np.array([256, 264, 202]) + L_a = 264 + Y_b = 100 + surround = VIEWING_CONDITIONS_ZCAM["Average"] + specification = XYZ_to_ZCAM(XYZ, XYZ_w, L_a, Y_b, surround) + + d_r = ( + ("reference", 1, 1), + ( + "1", + 1, + np.array([1, 1, 1 / 360, 1, 1, 1, 1 / 400, np.nan, 1, 1, 1]), + ), + ( + "100", + 100, + np.array( + [ + 100, + 100, + 100 / 360, + 100, + 100, + 100, + 100 / 400, + np.nan, + 100, + 100, + 100, + ] + ), + ), + ) + for scale, factor_a, factor_b in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + XYZ_to_ZCAM( + XYZ * factor_a, XYZ_w * factor_a, L_a, Y_b, surround + ), + as_float_array(specification) * factor_b, + decimal=7, + ) + + @ignore_numpy_errors + def test_nan_XYZ_to_ZCAM(self): + """ + Test :func:`colour.appearance.zcam.XYZ_to_ZCAM` definition + nan support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + XYZ = np.array(case) + XYZ_w = np.array(case) + L_a = case[0] + Y_b = 100 + surround = InductionFactors_ZCAM( + case[0], case[0], case[0], case[0] + ) + XYZ_to_ZCAM(XYZ, XYZ_w, L_a, Y_b, surround) + + +class TestZCAM_to_XYZ(unittest.TestCase): + """ + Define :func:`colour.appearance.zcam.ZCAM_to_XYZ` definition unit + tests methods. + """ + + def test_ZCAM_to_XYZ(self): + """Test :func:`colour.appearance.zcam.ZCAM_to_XYZ` definition.""" + + specification = CAM_Specification_ZCAM( + 92.2520, + 3.0216, + 196.3524, + 19.1314, + 321.3464, + 10.5252, + 237.6401, + np.nan, + 34.7022, + 25.2994, + 91.6837, + ) + XYZ_w = np.array([256, 264, 202]) + L_a = 264 + Y_b = 100 + surround = VIEWING_CONDITIONS_ZCAM["Average"] + np.testing.assert_allclose( + ZCAM_to_XYZ(specification, XYZ_w, L_a, Y_b, surround), + np.array([185, 206, 163]), + atol=0.01, + rtol=0.01, + ) + + specification = CAM_Specification_ZCAM( + 71.2071, + 6.8539, + 250.6422, + 32.7963, + 248.0394, + 23.8744, + 307.0595, + np.nan, + 18.2796, + 40.4621, + 70.4026, + ) + np.testing.assert_allclose( + ZCAM_to_XYZ(specification, XYZ_w, L_a, Y_b, surround), + np.array([89, 96, 120]), + atol=0.01, + rtol=0.01, + ) + + specification = CAM_Specification_ZCAM( + 68.8890, + 0.9774, + 58.7532, + 12.5916, + 196.7686, + 2.7918, + 43.8258, + np.nan, + 11.0371, + 44.4143, + 68.8737, + ) + surround = VIEWING_CONDITIONS_ZCAM["Dim"] + np.testing.assert_allclose( + ZCAM_to_XYZ(specification, XYZ_w, L_a, Y_b, surround), + np.array([79, 81, 62]), + atol=0.01, + rtol=0.01, + ) + + specification = CAM_Specification_ZCAM( + 82.6445, + 13.0838, + 123.9464, + 44.7277, + 114.7431, + 18.1655, + 178.6422, + np.nan, + 34.4874, + 26.8778, + 78.2653, + ) + XYZ_w = np.array([2103, 2259, 1401]) + L_a = 359 + Y_b = 16 + surround = VIEWING_CONDITIONS_ZCAM["Dark"] + np.testing.assert_allclose( + ZCAM_to_XYZ(specification, XYZ_w, L_a, Y_b, surround), + np.array([910, 1114, 500]), + atol=0.01, + rtol=0.01, + ) + + specification = CAM_Specification_ZCAM( + 33.0139, + 19.4070, + 389.7720 % 360, + 86.1882, + 45.8363, + 26.9446, + 397.3301, + np.nan, + 43.6447, + 47.9942, + 30.2593, + ) + np.testing.assert_allclose( + ZCAM_to_XYZ(specification, XYZ_w, L_a, Y_b, surround), + np.array([96, 67, 28]), + atol=0.01, + rtol=0.01, + ) + + def test_n_dimensional_ZCAM_to_XYZ(self): + """ + Test :func:`colour.appearance.zcam.ZCAM_to_XYZ` definition + n-dimensional support. + """ + + XYZ = np.array([185, 206, 163]) + XYZ_w = np.array([256, 264, 202]) + L_a = 264 + Y_b = 100 + surround = VIEWING_CONDITIONS_ZCAM["Average"] + specification = XYZ_to_ZCAM(XYZ, XYZ_w, L_a, Y_b, surround) + XYZ = ZCAM_to_XYZ(specification, XYZ_w, L_a, Y_b, surround) + + specification = CAM_Specification_ZCAM( + *np.transpose(np.tile(tsplit(specification), (6, 1))).tolist() + ) + XYZ = np.tile(XYZ, (6, 1)) + np.testing.assert_almost_equal( + ZCAM_to_XYZ(specification, XYZ_w, L_a, Y_b, surround), + XYZ, + decimal=7, + ) + + XYZ_w = np.tile(XYZ_w, (6, 1)) + np.testing.assert_almost_equal( + ZCAM_to_XYZ(specification, XYZ_w, L_a, Y_b, surround), + XYZ, + decimal=7, + ) + + specification = CAM_Specification_ZCAM( + *tsplit(np.reshape(specification, (2, 3, 11))).tolist() + ) + XYZ_w = np.reshape(XYZ_w, (2, 3, 3)) + XYZ = np.reshape(XYZ, (2, 3, 3)) + np.testing.assert_almost_equal( + ZCAM_to_XYZ(specification, XYZ_w, L_a, Y_b, surround), + XYZ, + decimal=7, + ) + + @ignore_numpy_errors + def test_domain_range_scale_ZCAM_to_XYZ(self): + """ + Test :func:`colour.appearance.zcam.ZCAM_to_XYZ` definition + domain and range scale support. + """ + + XYZ_i = np.array([185, 206, 163]) + XYZ_w = np.array([256, 264, 202]) + L_a = 264 + Y_b = 100 + surround = VIEWING_CONDITIONS_ZCAM["Average"] + specification = XYZ_to_ZCAM(XYZ_i, XYZ_w, L_a, Y_b, surround) + XYZ = ZCAM_to_XYZ(specification, XYZ_w, L_a, Y_b, surround) + + d_r = ( + ("reference", 1, 1), + ( + "1", + np.array([1, 1, 1 / 360, 1, 1, 1, 1 / 400, np.nan, 1, 1, 1]), + 1, + ), + ( + "100", + np.array( + [ + 100, + 100, + 100 / 360, + 100, + 100, + 100, + 100 / 400, + np.nan, + 100, + 100, + 100, + ] + ), + 100, + ), + ) + for scale, factor_a, factor_b in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + ZCAM_to_XYZ( + specification * factor_a, + XYZ_w * factor_b, + L_a, + Y_b, + surround, + ), + XYZ * factor_b, + decimal=7, + ) + + @ignore_numpy_errors + def test_raise_exception_ZCAM_to_XYZ(self): + """ + Test :func:`colour.appearance.zcam.ZCAM_to_XYZ` definition + raised exception. + """ + + self.assertRaises( + ValueError, + ZCAM_to_XYZ, + CAM_Specification_ZCAM( + 41.731091132513917, + None, + 219.04843265831178, + ), + np.array([256, 264, 202]), + 318.31, + 20.0, + VIEWING_CONDITIONS_ZCAM["Average"], + ) + + @ignore_numpy_errors + def test_nan_ZCAM_to_XYZ(self): + """ + Test :func:`colour.appearance.zcam.ZCAM_to_XYZ` definition nan + support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + J = case[0] + C = case[0] + h = case[0] + XYZ_w = np.array(case) + L_a = case[0] + Y_b = 100 + surround = InductionFactors_ZCAM( + case[0], case[0], case[0], case[0] + ) + ZCAM_to_XYZ( + CAM_Specification_ZCAM(J, C, h, M=50), + XYZ_w, + L_a, + Y_b, + surround, + ) diff --git a/colour/appearance/zcam.py b/colour/appearance/zcam.py new file mode 100644 index 0000000000..ef6367743e --- /dev/null +++ b/colour/appearance/zcam.py @@ -0,0 +1,780 @@ +""" +ZCAM Colour Appearance Model +============================ + +Defines the *ZCAM* colour appearance model objects: + +- :class:`colour.appearance.InductionFactors_ZCAM` +- :attr:`colour.VIEWING_CONDITIONS_ZCAM` +- :class:`colour.CAM_Specification_ZCAM` +- :func:`colour.XYZ_to_ZCAM` +- :func:`colour.ZCAM_to_XYZ` + +References +---------- +- :cite:`Safdar2018` : Safdar, M., Hardeberg, J. Y., Kim, Y. J., & Luo, M. R. + (2018). A Colour Appearance Model based on J z a z b z Colour Space. Color + and Imaging Conference, 2018(1), 96-101. + doi:10.2352/ISSN.2169-2629.2018.26.96 +- :cite:`Safdar2021` : Safdar, M., Hardeberg, J. Y., & Ronnier Luo, M. + (2021). ZCAM, a colour appearance model based on a high dynamic range + uniform colour space. Optics Express, 29(4), 6036. doi:10.1364/OE.413659 +- :cite:`Zhai2018` : Zhai, Q., & Luo, M. R. (2018). Study of chromatic + adaptation via neutral white matches on different viewing media. Optics + Express, 26(6), 7724. doi:10.1364/OE.26.007724 +""" + +from __future__ import annotations + +import numpy as np +from collections import namedtuple +from dataclasses import astuple, dataclass, field + +from colour.adaptation import chromatic_adaptation_Zhai2018 +from colour.appearance.ciecam02 import ( + VIEWING_CONDITIONS_CIECAM02, + degree_of_adaptation, + hue_angle, +) +from colour.algebra import spow +from colour.colorimetry import CCS_ILLUMINANTS +from colour.hints import ( + ArrayLike, + Boolean, + Dict, + FloatingOrArrayLike, + FloatingOrNDArray, + NDArray, + Optional, +) +from colour.models import Izazbz_to_XYZ, XYZ_to_Izazbz, xy_to_XYZ +from colour.utilities import ( + CaseInsensitiveMapping, + MixinDataclassArithmetic, + as_float, + as_float_array, + as_int_array, + domain_range_scale, + from_range_1, + from_range_degrees, + has_only_nan, + ones, + to_domain_1, + to_domain_degrees, + tsplit, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "InductionFactors_ZCAM", + "VIEWING_CONDITIONS_ZCAM", + "CAM_Specification_ZCAM", + "XYZ_to_ZCAM", + "ZCAM_to_XYZ", +] + + +class InductionFactors_ZCAM( + namedtuple("InductionFactors_ZCAM", ("F_s", "F", "c", "N_c")) +): + """ + *ZCAM* colour appearance model induction factors. + + Parameters + ---------- + F_s + Surround impact :math:`F_s`. + F + Maximum degree of adaptation :math:`F`. + c + Exponential non-linearity :math:`c`. + N_c + Chromatic induction factor :math:`N_c`. + + Notes + ----- + - The *ZCAM* colour appearance model induction factors are inherited from + the *CIECAM02* colour appearance model. + + References + ---------- + :cite:`Safdar2021` + """ + + +VIEWING_CONDITIONS_ZCAM: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Average": InductionFactors_ZCAM( + 0.69, *VIEWING_CONDITIONS_CIECAM02["Average"] + ), + "Dim": InductionFactors_ZCAM( + 0.59, *VIEWING_CONDITIONS_CIECAM02["Dim"] + ), + "Dark": InductionFactors_ZCAM( + 0.525, *VIEWING_CONDITIONS_CIECAM02["Dark"] + ), + } +) +VIEWING_CONDITIONS_ZCAM.__doc__ = """ +Reference *ZCAM* colour appearance model viewing conditions. + +References +---------- +:cite:`Safdar2021` +""" + +HUE_DATA_FOR_HUE_QUADRATURE: Dict = { + "h_i": np.array([33.44, 89.29, 146.30, 238.36, 393.44]), + "e_i": np.array([0.68, 0.64, 1.52, 0.77, 0.68]), + "H_i": np.array([0.0, 100.0, 200.0, 300.0, 400.0]), +} + + +@dataclass +class CAM_ReferenceSpecification_ZCAM(MixinDataclassArithmetic): + """ + Define the *ZCAM* colour appearance model reference specification. + + This specification has field names consistent with :cite:`Safdar2021` + reference. + + Parameters + ---------- + J_z + Correlate of *Lightness* :math:`J_z`. + C_z + Correlate of *chroma* :math:`C_z`. + h_z + *Hue* angle :math:`h_z` in degrees. + S_z + Correlate of *saturation* :math:`S_z`. + Q_z + Correlate of *brightness* :math:`Q_z`. + M_z + Correlate of *colourfulness* :math:`M_z`. + H + *Hue* :math:`h` quadrature :math:`H`. + H_z + *Hue* :math:`h` composition :math:`H_z`. + V_z + Correlate of *vividness* :math:`V_z`. + K_z + Correlate of *blackness* :math:`K_z`. + W_z + Correlate of *whiteness* :math:`W_z`. + + References + ---------- + :cite:`Safdar2021` + """ + + J_z: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + C_z: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + h_z: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + S_z: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + Q_z: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + M_z: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + H: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + H_z: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + V_z: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + K_z: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + W_z: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + + +@dataclass +class CAM_Specification_ZCAM(MixinDataclassArithmetic): + """ + Define the *ZCAM* colour appearance model specification. + + Parameters + ---------- + J + *Lightness* :math:`J` is the "brightness of an area (:math:`Q`) judged + relative to the brightness of a similarly illuminated area that appears + to be white or highly transmitting (:math:`Q_w`)", i.e., + :math:`J = (Q/Q_w)`. It is a visual scale with two well defined levels + i.e., zero and 100 for a pure black and a reference white, + respectively. Note that in HDR visual field, samples could have a + higher luminance than that of the reference white, so the lightness + could be over 100. Subscripts :math:`s` and :math:`w` are used to + annotate the sample and the reference white, respectively. + C + *Chroma* :math:`C` is "colourfulness of an area (:math:`M`) judged as + a proportion of the brightness of a similarly illuminated area that + appears white or highly transmitting (:math:`Q_w`)", i.e., + :math:`C = (M/Q_w)`. It is an open-end scale with origin as a colour + in the neutral axis. It can be estimated as the magnitude of the + chromatic difference between the test colour and a neutral colour + having the lightness same as the test colour. + h + *Hue* angle :math:`h` is a scale ranged from :math:`0^{\\circ}` to + :math:`360^{\\circ}` with the hues following rainbow sequence. The same + distance between pairs of hues in a constant lightness and chroma shows + the same perceived colour difference. + s + *Saturation* :math:`s` is the "colourfulness (:math:`M`) of an area + judged in proportion to its brightness (:math:`Q`)", i.e., + :math:`s = (M/Q)`. It can also be defined as the chroma of an area + judged in proportion to its lightness, i.e., :math:`s = (C/J)`. It is + an open-end scale with all neutral colours to have saturation of zero. + For example, the red bricks in a building would exhibit different + colours when illuminated by daylight. Those (directly) under daylight + will appear to be bright and colourful, and those under shadow will + appear darker and less colourful. However, the two areas have the same + saturation. + Q + *Brightness* :math:`Q` is an "attribute of a visual perception + according to which an area appears to emit, or reflect, more or less + light". It is an open-end scale with origin as pure black or complete + darkness. It is an absolute scale according to the illumination + condition i.e., an increase of brightness of an object when the + illuminance of light is increased. This is a visual phenomenon known as + Stevens effect. + M + *Colourfulness* :math:`M` is an "attribute of a visual perception + according to which the perceived colour of an area appears to be more + or less chromatic". It is an open-end scale with origin as a neutral + colour i.e., appearance of no hue. It is an absolute scale according to + the illumination condition i.e., an increase of colourfulness of an + object when the illuminance of light is increased. This is a visual + phenomenon known as Hunt effect. + H + *Hue* :math:`h` quadrature :math:`H_C` is an "attribute of a visual + perception according to which an area appears to be similar to one of + the colours: red, yellow, green, and blue, or to a combination of + adjacent pairs of these colours considered in a closed ring". It has + a 0-400 scale, i.e., hue quadrature of 0, 100, 200, 300, and 400 + range from unitary red to, yellow, green, blue, and back to red, + respectively. For example, a cyan colour consists of 50% green and + 50% blue, corresponding to a hue quadrature of 250. + HC + *Hue* :math:`h` composition :math:`H^C` used to define the hue + appearance of a sample. Note that hue circles formed by the equal hue + angle and equal hue composition appear to be quite different. + V + *Vividness* :math:`V` is an "attribute of colour used to indicate the + degree of departure of the colour (of stimulus) from a neutral black + colour", i.e., :math:`V = \\sqrt{J^2 + C^2}`. It is an open-end scale + with origin at pure black. This reflects the visual phenomena of an + object illuminated by a light to increase both the lightness and the + chroma. + K + *Blackness* :math:`K` is a visual attribute according to which an area + appears to contain more or less black content. It is a scale in the + Natural Colour System (NCS) and can also be defined in resemblance to a + pure black. It is an open-end scale with 100 as pure black (luminance + of 0 :math:`cd/m^2`), i.e., + :math:`K = (100 - \\sqrt{J^2 + C^2} = (100 - V)`. The visual effect can + be illustrated by mixing a black to a colour pigment. The more black + pigment is added, the higher blackness will be. A blacker colour will + have less lightness and/or chroma than a less black colour. + W + *Whiteness* :math:`W` is a visual attribute according to which an area + appears to contain more or less white content. It is a scale of the NCS + and can also be defined in resemblance to a pure white. It is an + open-end scale with 100 as reference white, i.e., + :math:`W = (100 - \\sqrt{(100 - J)^2 + C^2} = (100 - D)`. The visual + effect can be illustrated by mixing a white to a colour pigment. The + more white pigment is added, the higher whiteness will be. A whiter + colour will have a lower chroma and higher lightness than the less + white colour. + + References + ---------- + :cite:`Safdar2021` + """ + + J: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + C: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + h: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + s: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + Q: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + M: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + H: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + HC: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + V: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + K: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + W: Optional[FloatingOrNDArray] = field(default_factory=lambda: None) + + +TVS_D65: NDArray = xy_to_XYZ( + CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"] +) + + +def XYZ_to_ZCAM( + XYZ: ArrayLike, + XYZ_w: ArrayLike, + L_A: FloatingOrArrayLike, + Y_b: FloatingOrArrayLike, + surround: InductionFactors_ZCAM = VIEWING_CONDITIONS_ZCAM["Average"], + discount_illuminant: Boolean = False, +) -> CAM_Specification_ZCAM: + """ + Compute the *ZCAM* colour appearance model correlates from given *CIE XYZ* + tristimulus values. + + Parameters + ---------- + XYZ + Absolute *CIE XYZ* tristimulus values of test sample / stimulus. + XYZ_w + Absolute *CIE XYZ* tristimulus values of the white under reference + illuminant. + L_A + Test adapting field *luminance* :math:`L_A` in :math:`cd/m^2` such as + :math:`L_A = L_w * Y_b / 100` (where :math:`L_w` is luminance of the + reference white and :math:`Y_b` is the background luminance factor). + Y_b + Luminous factor of background :math:`Y_b` such as + :math:`Y_b = 100 * L_b / L_w` where :math:`L_w` is the luminance of the + light source and :math:`L_b` is the luminance of the background. For + viewing images, :math:`Y_b` can be the average :math:`Y` value for the + pixels in the entire image, or frequently, a :math:`Y` value of 20, + approximate an :math:`L^*` of 50 is used. + surround + Surround viewing conditions induction factors. + discount_illuminant + Truth value indicating if the illuminant should be discounted. + + Returns + ------- + :class:`colour.CAM_Specification_ZCAM` + *ZCAM* colour appearance model specification. + + Warnings + -------- + The underlying *SMPTE ST 2084:2014* transfer function is an absolute + transfer function. + + Notes + ----- + - *Safdar, Hardeberg and Luo (2021)* does not specify how the chromatic + adaptation to *CIE Standard Illuminant D65* in *Step 0* should be + performed. A one-step *Von Kries* chromatic adaptation transform is not + symmetrical or transitive when a degree of adaptation is involved. + *Safdar, Hardeberg and Luo (2018)* uses *Zhai and Luo (2018)* two-steps + chromatic adaptation transform, thus it seems sensible to adopt this + transform for the *ZCAM* colour appearance model until more information + is available. It is worth noting that a one-step *Von Kries* chromatic + adaptation transform with support for degree of adaptation produces + values closer to the supplemental document compared to the + *Zhai and Luo (2018)* two-steps chromatic adaptation transform but then + the *ZCAM* colour appearance model does not round-trip properly. + - The underlying *SMPTE ST 2084:2014* transfer function is an absolute + transfer function, thus the domain and range values for the *Reference* + and *1* scales are only indicative that the data is not affected by + scale transformations. + + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``XYZ`` | [UN] | [UN] | + +------------+-----------------------+---------------+ + | ``XYZ_tw`` | [UN] | [UN] | + +------------+-----------------------+---------------+ + | ``XYZ_rw`` | [UN] | [UN] | + +------------+-----------------------+---------------+ + + +-------------------------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +===============================+=======================+===============+ + | ``CAM_Specification_ZCAM.J`` | [UN] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.C`` | [UN] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.h`` | [0, 360] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.s`` | [UN] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.Q`` | [UN] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.M`` | [UN] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.H`` | [0, 400] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.HC`` | [UN] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.V`` | [UN] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.K`` | [UN] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.H`` | [UN] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + + References + ---------- + :cite:`Safdar2018`, :cite:`Safdar2021`, :cite:`Zhai2018` + + Examples + -------- + >>> XYZ = np.array([185, 206, 163]) + >>> XYZ_w = np.array([256, 264, 202]) + >>> L_A = 264 + >>> Y_b = 100 + >>> surround = VIEWING_CONDITIONS_ZCAM['Average'] + >>> XYZ_to_ZCAM(XYZ, XYZ_w, L_A, Y_b, surround) + ... # doctest: +ELLIPSIS + CAM_Specification_ZCAM(J=92.2504437..., C=3.0216926..., h=196.3245737..., \ +s=19.1319556..., Q=321.3408463..., M=10.5256217..., H=237.6114442..., \ +HC=None, V=34.7006776..., K=25.8835968..., W=91.6821728...) + """ + + XYZ = to_domain_1(XYZ) + XYZ_w = to_domain_1(XYZ_w) + _X_w, Y_w, _Z_w = tsplit(XYZ_w) + L_A = as_float_array(L_A) + Y_b = as_float_array(Y_b) + + F_s, _F, _c, _N_c = surround + + # Step 0 (Forward) - Chromatic adaptation from reference illuminant to + # "CIE Standard Illuminant D65" illuminant using "CAT02". + # Computing degree of adaptation :math:`D`. + D = ( + degree_of_adaptation(surround.F, L_A) + if not discount_illuminant + else ones(L_A.shape) + ) + + XYZ_D65 = chromatic_adaptation_Zhai2018( + XYZ, XYZ_w, TVS_D65, D, D, transform="CAT02" + ) + + # Step 1 (Forward) - Computing factors related with viewing conditions and + # independent of the test stimulus. + # Background factor :math:`F_b` + F_b = np.sqrt(Y_b / Y_w) + # Luminance level adaptation factor :math:`F_L` + F_L = 0.171 * spow(L_A, 1 / 3) * (1 - np.exp(-48 / 9 * L_A)) + + # Step 2 (Forward) - Computing achromatic response (:math:`I_z` and + # :math:`I_{z,w}`), redness-greenness (:math:`a_z` and :math:`a_{z,w}`), + # and yellowness-blueness (:math:`b_z`, :math:`b_{z,w}`). + with domain_range_scale("ignore"): + I_z, a_z, b_z = tsplit(XYZ_to_Izazbz(XYZ_D65, method="Safdar 2021")) + I_z_w, _a_z_w, b_z_w = tsplit( + XYZ_to_Izazbz(XYZ_w, method="Safdar 2021") + ) + + # Step 3 (Forward) - Computing hue angle :math:`h_z` + h_z = hue_angle(a_z, b_z) + + # Step 4 (Forward) - Computing hue quadrature :math:`H`. + H = hue_quadrature(h_z) + + # Computing eccentricity factor :math:`e_z`. + e_z = 1.015 + np.cos(np.radians(89.038 + h_z % 360)) + + # Step 5 (Forward) - Computing brightness :math:`Q_z`, + # lightness :math:`J_z`, colourfulness :math`M_z`, and chroma :math:`C_z` + Q_z_p = (1.6 * F_s) / F_b**0.12 + Q_z_m = F_s**2.2 * F_b**0.5 * spow(F_L, 0.2) + Q_z = 2700 * spow(I_z, Q_z_p) * Q_z_m + Q_z_w = 2700 * spow(I_z_w, Q_z_p) * Q_z_m + + J_z = 100 * (Q_z / Q_z_w) + + M_z = ( + 100 + * (a_z**2 + b_z**2) ** 0.37 + * ( + (spow(e_z, 0.068) * spow(F_L, 0.2)) + / (F_b**0.1 * spow(I_z_w, 0.78)) + ) + ) + + C_z = 100 * (M_z / Q_z_w) + + # Step 6 (Forward) - Computing saturation :math:`S_z`, + # vividness :math:`V_z`, blackness :math:`K_z`, and whiteness :math:`W_z`. + S_z = 100 * spow(F_L, 0.6) * np.sqrt(M_z / Q_z) + + V_z = np.sqrt((J_z - 58) ** 2 + 3.4 * C_z**2) + + K_z = 100 - 0.8 * np.sqrt(J_z**2 + 8 * C_z**2) + + W_z = 100 - np.sqrt((100 - J_z) ** 2 + C_z**2) + + return CAM_Specification_ZCAM( + as_float(from_range_1(J_z)), + as_float(from_range_1(C_z)), + as_float(from_range_degrees(h_z)), + as_float(from_range_1(S_z)), + as_float(from_range_1(Q_z)), + as_float(from_range_1(M_z)), + as_float(from_range_degrees(H, 400)), + None, + as_float(from_range_1(V_z)), + as_float(from_range_1(K_z)), + as_float(from_range_1(W_z)), + ) + + +def ZCAM_to_XYZ( + specification: CAM_Specification_ZCAM, + XYZ_w: ArrayLike, + L_A: FloatingOrArrayLike, + Y_b: FloatingOrArrayLike, + surround: InductionFactors_ZCAM = VIEWING_CONDITIONS_ZCAM["Average"], + discount_illuminant: Boolean = False, +) -> NDArray: + """ + Convert from *ZCAM* specification to *CIE XYZ* tristimulus values. + + Parameters + ---------- + specification + *ZCAM* colour appearance model specification. + Correlate of *Lightness* :math:`J`, correlate of *chroma* :math:`C` or + correlate of *colourfulness* :math:`M` and *hue* angle :math:`h` in + degrees must be specified, e.g. :math:`JCh` or :math:`JMh`. + XYZ_w + Absolute *CIE XYZ* tristimulus values of the white under reference + illuminant. + L_A + Test adapting field *luminance* :math:`L_A` in :math:`cd/m^2` such as + :math:`L_A = L_w * Y_b / 100` (where :math:`L_w` is luminance of the + reference white and :math:`Y_b` is the background luminance factor). + Y_b + Luminous factor of background :math:`Y_b` such as + :math:`Y_b = 100 x L_b / L_w` where :math:`L_w` is the luminance of the + light source and :math:`L_b` is the luminance of the background. For + viewing images, :math:`Y_b` can be the average :math:`Y` value for the + pixels in the entire image, or frequently, a :math:`Y` value of 20, + approximate an :math:`L^*` of 50 is used. + surround + Surround viewing conditions induction factors. + discount_illuminant + Truth value indicating if the illuminant should be discounted. + + Returns + ------- + :class:`numpy.ndarray` + *CIE XYZ* tristimulus values. + + Raises + ------ + ValueError + If neither *C* or *M* correlates have been defined in the + ``CAM_Specification_ZCAM`` argument. + + Warnings + -------- + The underlying *SMPTE ST 2084:2014* transfer function is an absolute + transfer function. + + Notes + ----- + - *Safdar, Hardeberg and Luo (2021)* does not specify how the chromatic + adaptation to *CIE Standard Illuminant D65* in *Step 0* should be + performed. A one-step *Von Kries* chromatic adaptation transform is not + symetrical or transitive when a degree of adptation is involved. + *Safdar, Hardeberg and Luo (2018)* uses *Zhai and Luo (2018)* two-steps + chromatic adaptation transform, thus it seems sensible to adopt this + transform for the *ZCAM* colour appearance model until more information + is available. It is worth noting that a one-step *Von Kries* chromatic + adaptation transform with support for degree of adaptation produces + values closer to the supplemental document compared to the + *Zhai and Luo (2018)* two-steps chromatic adaptation transform but then + the *ZCAM* colour appearance model does not round-trip properly. + - *Step 4* of the inverse model uses a rounded exponent of 1.3514 + preventing the model to round-trip properly. Given that this + implementation takes some liberties with respect to the chromatic + adaptation transform to use, it was deemed appropriate to use an + exponent value that enables the *ZCAM* colour appearance model to + round-trip. + - The underlying *SMPTE ST 2084:2014* transfer function is an absolute + transfer function, thus the domain and range values for the *Reference* + and *1* scales are only indicative that the data is not affected by + scale transformations. + + +-------------------------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +===============================+=======================+===============+ + | ``CAM_Specification_ZCAM.J`` | [UN] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.C`` | [UN] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.h`` | [0, 360] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.s`` | [UN] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.Q`` | [UN] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.M`` | [UN] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.H`` | [0, 400] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.HC`` | [UN] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.V`` | [UN] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.K`` | [UN] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + | ``CAM_Specification_ZCAM.H`` | [UN] | [0, 1] | + +-------------------------------+-----------------------+---------------+ + + +-----------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +===========+=======================+===============+ + | ``XYZ`` | [UN] | [UN] | + +-----------+-----------------------+---------------+ + + References + ---------- + :cite:`Safdar2018`, :cite:`Safdar2021`, :cite:`Zhai2018` + + Examples + -------- + >>> specification = CAM_Specification_ZCAM(J=92.250443780723629, + ... C=3.0216926733329013, + ... h=196.32457375575581) + >>> XYZ_w = np.array([256, 264, 202]) + >>> L_A = 264 + >>> Y_b = 100 + >>> surround = VIEWING_CONDITIONS_ZCAM['Average'] + >>> ZCAM_to_XYZ(specification, XYZ_w, L_A, Y_b, surround) + ... # doctest: +ELLIPSIS + array([ 185., 206., 163.]) + """ + + J_z, C_z, h_z, _S_z, _Q_z, M_z, _H, _H_Z, _V_z, _K_z, _W_z = astuple( + specification + ) + + J_z = to_domain_1(J_z) + C_z = to_domain_1(C_z) + h_z = to_domain_degrees(h_z) + M_z = to_domain_1(M_z) + + XYZ_w = to_domain_1(XYZ_w) + _X_w, Y_w, _Z_w = tsplit(XYZ_w) + L_A = as_float_array(L_A) + Y_b = as_float_array(Y_b) + + F_s, F, c, N_c = surround + + # Step 0 (Forward) - Chromatic adaptation from reference illuminant to + # "CIE Standard Illuminant D65" illuminant using "CAT02". + # Computing degree of adaptation :math:`D`. + D = ( + degree_of_adaptation(surround.F, L_A) + if not discount_illuminant + else ones(L_A.shape) + ) + + # Step 1 (Forward) - Computing factors related with viewing conditions and + # independent of the test stimulus. + # Background factor :math:`F_b` + F_b = np.sqrt(Y_b / Y_w) + # Luminance level adaptation factor :math:`F_L` + F_L = 0.171 * spow(L_A, 1 / 3) * (1 - np.exp(-48 / 9 * L_A)) + + # Step 2 (Forward) - Computing achromatic response (:math:`I_{z,w}`), + # redness-greenness (:math:`a_{z,w}`), and yellowness-blueness + # (:math:`b_{z,w}`). + with domain_range_scale("ignore"): + I_z_w, _A_z_w, B_z_w = tsplit( + XYZ_to_Izazbz(XYZ_w, method="Safdar 2021") + ) + + # Step 1 (Inverse) - Computing achromatic response (:math:`I_z`). + Q_z_p = (1.6 * F_s) / F_b**0.12 + Q_z_m = F_s**2.2 * F_b**0.5 * spow(F_L, 0.2) + Q_z_w = 2700 * spow(I_z_w, Q_z_p) * Q_z_m + + I_z_p = (F_b**0.12) / (1.6 * F_s) + I_z_d = 2700 * 100 * Q_z_m + + I_z = spow((J_z * Q_z_w) / I_z_d, I_z_p) + + # Step 2 (Inverse) - Computing chroma :math:`C_z`. + if has_only_nan(M_z) and not has_only_nan(C_z): + M_z = (C_z * Q_z_w) / 100 + elif has_only_nan(M_z): + raise ValueError( + 'Either "C" or "M" correlate must be defined in ' + 'the "CAM_Specification_ZCAM" argument!' + ) + + # Step 3 (Inverse) - Computing hue angle :math:`h_z` + # :math:`h_z` is currently required as an input. + + # Computing eccentricity factor :math:`e_z`. + e_z = 1.015 + np.cos(np.radians(89.038 + h_z % 360)) + h_z_r = np.radians(h_z) + + # Step 4 (Inverse) - Computing redness-greenness (:math:`a_z`), and + # yellowness-blueness (:math:`b_z`). + # C_z_p_e = 1.3514 + C_z_p_e = 50 / 37 + C_z_p = spow( + (M_z * spow(I_z_w, 0.78) * F_b**0.1) + / (100 * e_z**0.068 * spow(F_L, 0.2)), + C_z_p_e, + ) + a_z = C_z_p * np.cos(h_z_r) + b_z = C_z_p * np.sin(h_z_r) + + # Step 5 (Inverse) - Computing tristimulus values :math:`XYZ_{D65}`. + with domain_range_scale("ignore"): + XYZ_D65 = Izazbz_to_XYZ(tstack([I_z, a_z, b_z]), method="Safdar 2021") + + XYZ = chromatic_adaptation_Zhai2018( + XYZ_D65, TVS_D65, XYZ_w, D, D, transform="CAT02" + ) + + return from_range_1(XYZ) + + +def hue_quadrature(h: FloatingOrArrayLike) -> FloatingOrNDArray: + """ + Return the hue quadrature from given hue :math:`h` angle in degrees. + + Parameters + ---------- + h + Hue :math:`h` angle in degrees. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + Hue quadrature. + + Examples + -------- + >>> hue_quadrature(196.3185839) # doctest: +ELLIPSIS + 237.6052911... + """ + + h = as_float_array(h) + + h_i = HUE_DATA_FOR_HUE_QUADRATURE["h_i"] + e_i = HUE_DATA_FOR_HUE_QUADRATURE["e_i"] + H_i = HUE_DATA_FOR_HUE_QUADRATURE["H_i"] + + # :math:`h_p` = :math:`h_z` + 360 if :math:`h_z` < :math:`h_1, i.e. h_i[0] + h[h <= h_i[0]] += 360 + # *np.searchsorted* returns an erroneous index if a *nan* is used as input. + h[np.asarray(np.isnan(h))] = 0 + i = as_int_array(np.searchsorted(h_i, h, side="left") - 1) + + h_ii = h_i[i] + e_ii = e_i[i] + H_ii = H_i[i] + h_ii1 = h_i[i + 1] + e_ii1 = e_i[i + 1] + + H = H_ii + ( + (100 * (h - h_ii) / e_ii) / ((h - h_ii) / e_ii + (h_ii1 - h) / e_ii1) + ) + + return as_float(H) diff --git a/colour/biochemistry/__init__.py b/colour/biochemistry/__init__.py index 2378d67184..11b21f7361 100644 --- a/colour/biochemistry/__init__.py +++ b/colour/biochemistry/__init__.py @@ -1,11 +1,69 @@ -# -*- coding: utf-8 -*- +import sys -from __future__ import absolute_import +from colour.utilities.deprecation import ModuleAPI, build_API_changes +from colour.utilities.documentation import is_documentation_building -from .michaelis_menten import (reaction_rate_MichealisMenten, - substrate_concentration_MichealisMenten) +from colour.hints import Any -__all__ = [] +from .michaelis_menten import ( + REACTION_RATE_MICHAELISMENTEN_METHODS, + reaction_rate_MichaelisMenten, + SUBSTRATE_CONCENTRATION_MICHAELISMENTEN_METHODS, + substrate_concentration_MichaelisMenten, +) +from .michaelis_menten import ( + reaction_rate_MichaelisMenten_Michaelis1913, + substrate_concentration_MichaelisMenten_Michaelis1913, + reaction_rate_MichaelisMenten_Abebe2017, + substrate_concentration_MichaelisMenten_Abebe2017, +) + +__all__ = [ + "REACTION_RATE_MICHAELISMENTEN_METHODS", + "reaction_rate_MichaelisMenten", + "SUBSTRATE_CONCENTRATION_MICHAELISMENTEN_METHODS", + "substrate_concentration_MichaelisMenten", +] __all__ += [ - 'reaction_rate_MichealisMenten', 'substrate_concentration_MichealisMenten' + "reaction_rate_MichaelisMenten_Michaelis1913", + "substrate_concentration_MichaelisMenten_Michaelis1913", + "reaction_rate_MichaelisMenten_Abebe2017", + "substrate_concentration_MichaelisMenten_Abebe2017", ] + + +# ----------------------------------------------------------------------------# +# --- API Changes and Deprecation Management ---# +# ----------------------------------------------------------------------------# +class biochemistry(ModuleAPI): + """Define a class acting like the *biochemistry* module.""" + + def __getattr__(self, attribute) -> Any: + """Return the value from the attribute with given name.""" + + return super().__getattr__(attribute) + + +# v0.4.0 +API_CHANGES = { + "ObjectRenamed": [ + [ + "colour.biochemistry.reaction_rate_MichealisMenten", + "colour.biochemistry.reaction_rate_MichaelisMenten", + ], + [ + "colour.biochemistry.substrate_concentration_MichealisMenten", + "colour.biochemistry.substrate_concentration_MichaelisMenten", + ], + ] +} +"""Defines the *colour.biochemistry* sub-package API changes.""" + +if not is_documentation_building(): + sys.modules[ + "colour.biochemistry" + ] = biochemistry( # type:ignore[assignment] + sys.modules["colour.biochemistry"], build_API_changes(API_CHANGES) + ) + + del ModuleAPI, is_documentation_building, build_API_changes, sys diff --git a/colour/biochemistry/michaelis_menten.py b/colour/biochemistry/michaelis_menten.py index 78377a1325..9d70a4b46d 100644 --- a/colour/biochemistry/michaelis_menten.py +++ b/colour/biochemistry/michaelis_menten.py @@ -1,55 +1,89 @@ -# -*- coding: utf-8 -*- """ Michaelis-Menten Kinetics ========================= Implements support for *Michaelis-Menten* kinetics, a model of enzyme kinetics: -- :func:`colour.biochemistry.reaction_rate_MichealisMenten` -- :func:`colour.biochemistry.substrate_concentration_MichealisMenten` +- :func:`colour.biochemistry.reaction_rate_MichaelisMenten_Michaelis1913` +- :func:`colour.biochemistry.reaction_rate_MichaelisMenten_Abebe2017` +- :func:`colour.biochemistry.REACTION_RATE_MICHAELISMENTEN_METHODS` +- :func:`colour.biochemistry.reaction_rate_MichaelisMenten` +- :func:`colour.biochemistry.\ +substrate_concentration_MichaelisMenten_Michaelis1913` +- :func:`colour.biochemistry.\ +substrate_concentration_MichaelisMenten_Abebe2017` +- :func:`colour.biochemistry.SUBSTRATE_CONCENTRATION_MICHAELISMENTEN_METHODS` +- :func:`colour.biochemistry.substrate_concentration_MichaelisMenten` References ---------- +- :cite:`Abebe2017a` : Abebe, M. A., Pouli, T., Larabi, M.-C., & Reinhard, + E. (2017). Perceptual Lightness Modeling for High-Dynamic-Range Imaging. + ACM Transactions on Applied Perception, 15(1), 1-19. doi:10.1145/3086577 - :cite:`Wikipedia2003d` : Wikipedia. (2003). Michaelis-Menten kinetics. Retrieved April 29, 2017, from https://en.wikipedia.org/wiki/Michaelis%E2%80%93Menten_kinetics """ -from __future__ import division, unicode_literals +from __future__ import annotations -from colour.utilities import as_float_array +from colour.hints import ( + Any, + FloatingOrArrayLike, + FloatingOrNDArray, + Literal, + Union, +) +from colour.utilities import ( + CaseInsensitiveMapping, + as_float, + as_float_array, + filter_kwargs, + validate_method, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'reaction_rate_MichealisMenten', 'substrate_concentration_MichealisMenten' + "reaction_rate_MichaelisMenten_Michaelis1913", + "reaction_rate_MichaelisMenten_Abebe2017", + "REACTION_RATE_MICHAELISMENTEN_METHODS", + "reaction_rate_MichaelisMenten", + "substrate_concentration_MichaelisMenten_Michaelis1913", + "substrate_concentration_MichaelisMenten_Abebe2017", + "SUBSTRATE_CONCENTRATION_MICHAELISMENTEN_METHODS", + "substrate_concentration_MichaelisMenten", ] -def reaction_rate_MichealisMenten(S, V_max, K_m): +def reaction_rate_MichaelisMenten_Michaelis1913( + S: FloatingOrArrayLike, + V_max: FloatingOrArrayLike, + K_m: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Describes the rate of enzymatic reactions, by relating reaction rate + Describe the rate of enzymatic reactions, by relating reaction rate :math:`v` to concentration of a substrate :math:`S`. Parameters ---------- - S : array_like + S Concentration of a substrate :math:`S`. - V_max : array_like + V_max Maximum rate :math:`V_{max}` achieved by the system, at saturating substrate concentration. - K_m : array_like - Substrate concentration :math:`V_{max}` at which the reaction rate is + K_m + Substrate concentration :math:`K_m` at which the reaction rate is half of :math:`V_{max}`. Returns ------- - array_like + :class:`numpy.floating` or :class:`numpy.ndarray` Reaction rate :math:`v`. References @@ -58,7 +92,7 @@ def reaction_rate_MichealisMenten(S, V_max, K_m): Examples -------- - >>> reaction_rate_MichealisMenten(0.5, 2.5, 0.8) # doctest: +ELLIPSIS + >>> reaction_rate_MichaelisMenten(0.5, 2.5, 0.8) # doctest: +ELLIPSIS 0.9615384... """ @@ -68,28 +102,160 @@ def reaction_rate_MichealisMenten(S, V_max, K_m): v = (V_max * S) / (K_m + S) - return v + return as_float(v) -def substrate_concentration_MichealisMenten(v, V_max, K_m): +def reaction_rate_MichaelisMenten_Abebe2017( + S: FloatingOrArrayLike, + V_max: FloatingOrArrayLike, + K_m: FloatingOrArrayLike, + b_m: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Describes the rate of enzymatic reactions, by relating concentration of a + Describe the rate of enzymatic reactions, by relating reaction rate + :math:`v` to concentration of a substrate :math:`S` according to the + modified *Michaelis-Menten* kinetics equation as given by + *Abebe, Pouli, Larabi and Reinhard (2017)*. + + Parameters + ---------- + S + Concentration of a substrate :math:`S` (or + :math:`(\\cfrac{Y}{Y_n})^{\\epsilon}`). + V_max + Maximum rate :math:`V_{max}` (or :math:`a_m`) achieved by the system, + at saturating substrate concentration. + K_m + Substrate concentration :math:`K_m` (or :math:`c_m`) at which the + reaction rate is half of :math:`V_{max}`. + b_m + Bias factor :math:`b_m`. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + Reaction rate :math:`v`. + + References + ---------- + :cite:`Abebe2017a` + + Examples + -------- + >>> reaction_rate_MichaelisMenten_Abebe2017(0.5, 1.448, 0.635, 0.813) + ... # doctest: +ELLIPSIS + 0.6951512... + """ + + S = as_float_array(S) + V_max = as_float_array(V_max) + K_m = as_float_array(K_m) + b_m = as_float_array(b_m) + + v = (V_max * S) / (b_m * S + K_m) + + return as_float(v) + + +REACTION_RATE_MICHAELISMENTEN_METHODS: CaseInsensitiveMapping = ( + CaseInsensitiveMapping( + { + "Michaelis 1913": reaction_rate_MichaelisMenten_Michaelis1913, + "Abebe 2017": reaction_rate_MichaelisMenten_Abebe2017, + } + ) +) +REACTION_RATE_MICHAELISMENTEN_METHODS.__doc__ = """ +Supported *Michaelis-Menten* kinetics reaction rate equation computation +methods. + +References +---------- +:cite:`Wikipedia2003d`, :cite:`Abebe2017a` +""" + + +def reaction_rate_MichaelisMenten( + S: FloatingOrArrayLike, + V_max: FloatingOrArrayLike, + K_m: FloatingOrArrayLike, + method: Union[ + Literal["Michaelis 1913", "Abebe 2017"], str + ] = "Michaelis 1913", + **kwargs: Any, +) -> FloatingOrNDArray: + """ + Describe the rate of enzymatic reactions, by relating reaction rate + :math:`v` to concentration of a substrate :math:`S` according to given + method. + + Parameters + ---------- + S + Concentration of a substrate :math:`S`. + V_max + Maximum rate :math:`V_{max}` achieved by the system, at saturating + substrate concentration. + K_m + Substrate concentration :math:`K_m` at which the reaction rate is + half of :math:`V_{max}`. + method + Computation method. + + Other Parameters + ---------------- + b_m + {:func:`colour.biochemistry.reaction_rate_MichaelisMenten_Abebe2017`}, + Bias factor :math:`b_m`. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + Reaction rate :math:`v`. + + References + ---------- + :cite:`Wikipedia2003d`, :cite:`Abebe2017a` + + Examples + -------- + >>> reaction_rate_MichaelisMenten(0.5, 2.5, 0.8) # doctest: +ELLIPSIS + 0.9615384... + >>> reaction_rate_MichaelisMenten( + ... 0.5, 2.5, 0.8, method='Abebe 2017', b_m=0.813) # doctest: +ELLIPSIS + 1.0360547... + """ + + method = validate_method(method, REACTION_RATE_MICHAELISMENTEN_METHODS) + + function = REACTION_RATE_MICHAELISMENTEN_METHODS[method] + + return function(S, V_max, K_m, **filter_kwargs(function, **kwargs)) + + +def substrate_concentration_MichaelisMenten_Michaelis1913( + v: FloatingOrArrayLike, + V_max: FloatingOrArrayLike, + K_m: FloatingOrArrayLike, +) -> FloatingOrNDArray: + """ + Describe the rate of enzymatic reactions, by relating concentration of a substrate :math:`S` to reaction rate :math:`v`. Parameters ---------- - v : array_like + v Reaction rate :math:`v`. - V_max : array_like + V_max Maximum rate :math:`V_{max}` achieved by the system, at saturating substrate concentration. - K_m : array_like - Substrate concentration :math:`V_{max}` at which the reaction rate is + K_m + Substrate concentration :math:`K_m` at which the reaction rate is half of :math:`V_{max}`. Returns ------- - array_like + :class:`numpy.floating` or :class:`numpy.ndarray` Concentration of a substrate :math:`S`. References @@ -98,7 +264,7 @@ def substrate_concentration_MichealisMenten(v, V_max, K_m): Examples -------- - >>> substrate_concentration_MichealisMenten(0.961538461538461, 2.5, 0.8) + >>> substrate_concentration_MichaelisMenten(0.961538461538461, 2.5, 0.8) ... # doctest: +ELLIPSIS 0.4999999... """ @@ -109,4 +275,135 @@ def substrate_concentration_MichealisMenten(v, V_max, K_m): S = (v * K_m) / (V_max - v) - return S + return as_float(S) + + +def substrate_concentration_MichaelisMenten_Abebe2017( + v: FloatingOrArrayLike, + V_max: FloatingOrArrayLike, + K_m: FloatingOrArrayLike, + b_m: FloatingOrArrayLike, +) -> FloatingOrNDArray: + """ + Describe the rate of enzymatic reactions, by relating concentration of a + substrate :math:`S` to reaction rate :math:`v` according to the modified + *Michaelis-Menten* kinetics equation as given by + *Abebe, Pouli, Larabi and Reinhard (2017)*. + + Parameters + ---------- + v + Reaction rate :math:`v`. + V_max + Maximum rate :math:`V_{max}` (or :math:`a_m`) achieved by the system, + at saturating substrate concentration. + K_m + Substrate concentration :math:`K_m` (or :math:`c_m`) at which the + reaction rate is half of :math:`V_{max}`. + b_m + Bias factor :math:`b_m`. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + Concentration of a substrate :math:`S`. + + References + ---------- + :cite:`Abebe2017a` + + Examples + -------- + >>> substrate_concentration_MichaelisMenten_Abebe2017( + ... 0.695151224195871, 1.448, 0.635, 0.813) # doctest: +ELLIPSIS + 0.4999999... + """ + + v = as_float_array(v) + V_max = as_float_array(V_max) + K_m = as_float_array(K_m) + b_m = as_float_array(b_m) + + S = (v * K_m) / (V_max - b_m * v) + + return as_float(S) + + +SUBSTRATE_CONCENTRATION_MICHAELISMENTEN_METHODS: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "Michaelis 1913": substrate_concentration_MichaelisMenten_Michaelis1913, + "Abebe 2017": substrate_concentration_MichaelisMenten_Abebe2017, + } +) +SUBSTRATE_CONCENTRATION_MICHAELISMENTEN_METHODS.__doc__ = """ +Supported *Michaelis-Menten* kinetics substrate concentration equation +computation methods. + +References +---------- +:cite:`Wikipedia2003d`, :cite:`Abebe2017a` +""" + + +def substrate_concentration_MichaelisMenten( + v: FloatingOrArrayLike, + V_max: FloatingOrArrayLike, + K_m: FloatingOrArrayLike, + method: Union[ + Literal["Michaelis 1913", "Abebe 2017"], str + ] = "Michaelis 1913", + **kwargs: Any, +) -> FloatingOrNDArray: + """ + Describe the rate of enzymatic reactions, by relating concentration of a + substrate :math:`S` to reaction rate :math:`v` according to given method. + + Parameters + ---------- + v + Reaction rate :math:`v`. + V_max + Maximum rate :math:`V_{max}` achieved by the system, at saturating + substrate concentration. + K_m + Substrate concentration :math:`K_m` at which the reaction rate is + half of :math:`V_{max}`. + method + Computation method. + + Other Parameters + ---------------- + b_m + {:func:`colour.biochemistry.\ +substrate_concentration_MichaelisMenten_Abebe2017`}, + Bias factor :math:`b_m`. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + Concentration of a substrate :math:`S`. + + References + ---------- + :cite:`Wikipedia2003d`, :cite:`Abebe2017a` + + Examples + -------- + >>> substrate_concentration_MichaelisMenten(0.961538461538461, 2.5, 0.8) + ... # doctest: +ELLIPSIS + 0.4999999... + >>> substrate_concentration_MichaelisMenten( + ... 1.036054703688355, 2.5, 0.8, method='Abebe 2017', b_m=0.813) + ... # doctest: +ELLIPSIS + 0.5000000... + """ + + method = validate_method( + method, SUBSTRATE_CONCENTRATION_MICHAELISMENTEN_METHODS + ) + + function = SUBSTRATE_CONCENTRATION_MICHAELISMENTEN_METHODS[method] + + return function(v, V_max, K_m, **filter_kwargs(function, **kwargs)) diff --git a/colour/biochemistry/tests/__init__.py b/colour/biochemistry/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/biochemistry/tests/__init__.py +++ b/colour/biochemistry/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/biochemistry/tests/test_michaelis_menten.py b/colour/biochemistry/tests/test_michaelis_menten.py index 1398fbca8b..2f1bf7ac0f 100644 --- a/colour/biochemistry/tests/test_michaelis_menten.py +++ b/colour/biochemistry/tests/test_michaelis_menten.py @@ -1,92 +1,108 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.biochemistry.michaelis_menten` module. +Defines the unit tests for the :mod:`colour.biochemistry.michaelis_menten` +module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from itertools import permutations -from colour.biochemistry import (reaction_rate_MichealisMenten, - substrate_concentration_MichealisMenten) +from colour.biochemistry import ( + reaction_rate_MichaelisMenten_Michaelis1913, + substrate_concentration_MichaelisMenten_Michaelis1913, + reaction_rate_MichaelisMenten_Abebe2017, + substrate_concentration_MichaelisMenten_Abebe2017, +) from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestReactionRateMichealisMenten', - 'TestSubstrateConcentrationMichealisMenten' + "TestReactionRateMichaelisMentenMichaelis1913", + "TestSubstrateConcentrationMichaelisMentenMichaelis1913", + "TestReactionRateMichaelisMentenAbebe2017", + "TestSubstrateConcentrationMichaelisMentenAbebe2017", ] -class TestReactionRateMichealisMenten(unittest.TestCase): +class TestReactionRateMichaelisMentenMichaelis1913(unittest.TestCase): """ - Defines :func:`colour.biochemistry.michaelis_menten.\ -reaction_rate_MichealisMenten` definition unit tests methods. + Define :func:`colour.biochemistry.michaelis_menten.\ +reaction_rate_MichaelisMenten_Michaelis1913` definition unit tests methods. """ - def test_reaction_rate_MichealisMenten(self): + def test_reaction_rate_MichaelisMenten_Michaelis1913(self): """ - Tests :func:`colour.biochemistry.michaelis_menten.\ -reaction_rate_MichealisMenten` definition. + Test :func:`colour.biochemistry.michaelis_menten.\ +reaction_rate_MichaelisMenten_Michaelis1913` definition. """ self.assertAlmostEqual( - reaction_rate_MichealisMenten(0.25, 0.5, 0.25), + reaction_rate_MichaelisMenten_Michaelis1913(0.25, 0.5, 0.25), 0.250000000000000, - places=7) + places=7, + ) self.assertAlmostEqual( - reaction_rate_MichealisMenten(0.5, 0.5, 0.25), + reaction_rate_MichaelisMenten_Michaelis1913(0.5, 0.5, 0.25), 0.333333333333333, - places=7) + places=7, + ) self.assertAlmostEqual( - reaction_rate_MichealisMenten(0.65, 0.75, 0.35), + reaction_rate_MichaelisMenten_Michaelis1913(0.65, 0.75, 0.35), 0.487500000000000, - places=7) + places=7, + ) - def test_n_dimensional_reaction_rate_MichealisMenten(self): + def test_n_dimensional_reaction_rate_MichaelisMenten_Michaelis1913(self): """ - Tests :func:`colour.biochemistry.michaelis_menten.\ -reaction_rate_MichealisMenten` definition n-dimensional arrays + Test :func:`colour.biochemistry.michaelis_menten.\ +reaction_rate_MichaelisMenten_Michaelis1913` definition n-dimensional arrays support. """ v = 0.5 V_max = 0.5 K_m = 0.25 - S = reaction_rate_MichealisMenten(v, V_max, K_m) + S = reaction_rate_MichaelisMenten_Michaelis1913(v, V_max, K_m) v = np.tile(v, (6, 1)) S = np.tile(S, (6, 1)) np.testing.assert_almost_equal( - reaction_rate_MichealisMenten(v, V_max, K_m), S, decimal=7) + reaction_rate_MichaelisMenten_Michaelis1913(v, V_max, K_m), + S, + decimal=7, + ) V_max = np.tile(V_max, (6, 1)) K_m = np.tile(K_m, (6, 1)) np.testing.assert_almost_equal( - reaction_rate_MichealisMenten(v, V_max, K_m), S, decimal=7) + reaction_rate_MichaelisMenten_Michaelis1913(v, V_max, K_m), + S, + decimal=7, + ) v = np.reshape(v, (2, 3, 1)) V_max = np.reshape(V_max, (2, 3, 1)) K_m = np.reshape(K_m, (2, 3, 1)) S = np.reshape(S, (2, 3, 1)) np.testing.assert_almost_equal( - reaction_rate_MichealisMenten(v, V_max, K_m), S, decimal=7) + reaction_rate_MichaelisMenten_Michaelis1913(v, V_max, K_m), + S, + decimal=7, + ) @ignore_numpy_errors - def test_nan_reaction_rate_MichealisMenten(self): + def test_nan_reaction_rate_MichaelisMenten_Michaelis1913(self): """ - Tests :func:`colour.biochemistry.michaelis_menten.\ -reaction_rate_MichealisMenten` definition nan support. + Test :func:`colour.biochemistry.michaelis_menten.\ +reaction_rate_MichaelisMenten_Michaelis1913` definition nan support. """ cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] @@ -95,76 +111,293 @@ def test_nan_reaction_rate_MichealisMenten(self): v = np.array(case) V_max = np.array(case) K_m = np.array(case) - reaction_rate_MichealisMenten(v, V_max, K_m) + reaction_rate_MichaelisMenten_Michaelis1913(v, V_max, K_m) -class TestSubstrateConcentrationMichealisMenten(unittest.TestCase): +class TestSubstrateConcentrationMichaelisMentenMichaelis1913( + unittest.TestCase +): """ - Defines :func:`colour.biochemistry.michaelis_menten.\ -reaction_rate_MichealisMenten` definition unit tests methods. + Define :func:`colour.biochemistry.michaelis_menten.\ +reaction_rate_MichaelisMenten_Michaelis1913` definition unit tests methods. """ - def test_substrate_concentration_MichealisMenten(self): + def test_substrate_concentration_MichaelisMenten_Michaelis1913(self): """ - Tests :func:`colour.biochemistry.michaelis_menten.\ -substrate_concentration_MichealisMenten` definition. + Test :func:`colour.biochemistry.michaelis_menten.\ +substrate_concentration_MichaelisMenten_Michaelis1913` definition. """ self.assertAlmostEqual( - substrate_concentration_MichealisMenten(0.25, 0.5, 0.25), + substrate_concentration_MichaelisMenten_Michaelis1913( + 0.25, 0.5, 0.25 + ), 0.250000000000000, - places=7) + places=7, + ) self.assertAlmostEqual( - substrate_concentration_MichealisMenten(1 / 3, 0.5, 0.25), + substrate_concentration_MichaelisMenten_Michaelis1913( + 1 / 3, 0.5, 0.25 + ), 0.500000000000000, - places=7) + places=7, + ) self.assertAlmostEqual( - substrate_concentration_MichealisMenten(0.4875, 0.75, 0.35), + substrate_concentration_MichaelisMenten_Michaelis1913( + 0.4875, 0.75, 0.35 + ), 0.650000000000000, - places=7) + places=7, + ) - def test_n_dimensional_substrate_concentration_MichealisMenten(self): + def test_n_dimensional_substrate_concentration_MichaelisMenten_Michaelis1913( # noqa + self, + ): """ - Tests :func:`colour.biochemistry.michaelis_menten.\ -substrate_concentration_MichealisMenten` definition n-dimensional arrays - support. + Test :func:`colour.biochemistry.michaelis_menten.\ +substrate_concentration_MichaelisMenten_Michaelis1913` definition n-dimensional + arrays support. """ S = 1 / 3 V_max = 0.5 K_m = 0.25 - v = substrate_concentration_MichealisMenten(S, V_max, K_m) + v = substrate_concentration_MichaelisMenten_Michaelis1913( + S, V_max, K_m + ) + + S = np.tile(S, (6, 1)) + v = np.tile(v, (6, 1)) + np.testing.assert_almost_equal( + substrate_concentration_MichaelisMenten_Michaelis1913( + S, V_max, K_m + ), + v, + decimal=7, + ) + + V_max = np.tile(V_max, (6, 1)) + K_m = np.tile(K_m, (6, 1)) + np.testing.assert_almost_equal( + substrate_concentration_MichaelisMenten_Michaelis1913( + S, V_max, K_m + ), + v, + decimal=7, + ) + + S = np.reshape(S, (2, 3, 1)) + V_max = np.reshape(V_max, (2, 3, 1)) + K_m = np.reshape(K_m, (2, 3, 1)) + v = np.reshape(v, (2, 3, 1)) + np.testing.assert_almost_equal( + substrate_concentration_MichaelisMenten_Michaelis1913( + S, V_max, K_m + ), + v, + decimal=7, + ) + + @ignore_numpy_errors + def test_nan_substrate_concentration_MichaelisMenten_Michaelis1913(self): + """ + Test :func:`colour.biochemistry.michaelis_menten.\ +substrate_concentration_MichaelisMenten_Michaelis1913` definition nan support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + s = np.array(case) + V_max = np.array(case) + K_m = np.array(case) + substrate_concentration_MichaelisMenten_Michaelis1913( + s, V_max, K_m + ) + + +class TestReactionRateMichaelisMentenAbebe2017(unittest.TestCase): + """ + Define :func:`colour.biochemistry.michaelis_menten.\ +reaction_rate_MichaelisMenten_Abebe2017` definition unit tests methods. + """ + + def test_reaction_rate_MichaelisMenten_Abebe2017(self): + """ + Test :func:`colour.biochemistry.michaelis_menten.\ +reaction_rate_MichaelisMenten_Abebe2017` definition. + """ + + self.assertAlmostEqual( + reaction_rate_MichaelisMenten_Abebe2017(0.25, 0.5, 0.25, 0.25), + 0.400000000000000, + places=7, + ) + + self.assertAlmostEqual( + reaction_rate_MichaelisMenten_Abebe2017(0.5, 0.5, 0.25, 0.25), + 0.666666666666666, + places=7, + ) + + self.assertAlmostEqual( + reaction_rate_MichaelisMenten_Abebe2017(0.65, 0.75, 0.35, 0.25), + 0.951219512195122, + places=7, + ) + + def test_n_dimensional_reaction_rate_MichaelisMenten_Abebe2017(self): + """ + Test :func:`colour.biochemistry.michaelis_menten.\ +reaction_rate_MichaelisMenten_Abebe2017` definition n-dimensional arrays + support. + """ + + v = 0.5 + V_max = 0.5 + K_m = 0.25 + b_m = 0.25 + S = reaction_rate_MichaelisMenten_Abebe2017(v, V_max, K_m, b_m) + + v = np.tile(v, (6, 1)) + S = np.tile(S, (6, 1)) + np.testing.assert_almost_equal( + reaction_rate_MichaelisMenten_Abebe2017(v, V_max, K_m, b_m), + S, + decimal=7, + ) + + V_max = np.tile(V_max, (6, 1)) + K_m = np.tile(K_m, (6, 1)) + b_m = np.tile(b_m, (6, 1)) + np.testing.assert_almost_equal( + reaction_rate_MichaelisMenten_Abebe2017(v, V_max, K_m, b_m), + S, + decimal=7, + ) + + v = np.reshape(v, (2, 3, 1)) + V_max = np.reshape(V_max, (2, 3, 1)) + K_m = np.reshape(K_m, (2, 3, 1)) + b_m = np.reshape(b_m, (2, 3, 1)) + S = np.reshape(S, (2, 3, 1)) + np.testing.assert_almost_equal( + reaction_rate_MichaelisMenten_Abebe2017(v, V_max, K_m, b_m), + S, + decimal=7, + ) + + @ignore_numpy_errors + def test_nan_reaction_rate_MichaelisMenten_Abebe2017(self): + """ + Test :func:`colour.biochemistry.michaelis_menten.\ +reaction_rate_MichaelisMenten_Abebe2017` definition nan support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + v = np.array(case) + V_max = np.array(case) + K_m = np.array(case) + b_m = np.array(case) + reaction_rate_MichaelisMenten_Abebe2017(v, V_max, K_m, b_m) + + +class TestSubstrateConcentrationMichaelisMentenAbebe2017(unittest.TestCase): + """ + Define :func:`colour.biochemistry.michaelis_menten.\ +reaction_rate_MichaelisMenten_Abebe2017` definition unit tests methods. + """ + + def test_substrate_concentration_MichaelisMenten_Abebe2017(self): + """ + Test :func:`colour.biochemistry.michaelis_menten.\ +substrate_concentration_MichaelisMenten_Abebe2017` definition. + """ + + self.assertAlmostEqual( + substrate_concentration_MichaelisMenten_Abebe2017( + 0.400000000000000, 0.5, 0.25, 0.25 + ), + 0.250000000000000, + places=7, + ) + + self.assertAlmostEqual( + substrate_concentration_MichaelisMenten_Abebe2017( + 0.666666666666666, 0.5, 0.25, 0.25 + ), + 0.500000000000000, + places=7, + ) + + self.assertAlmostEqual( + substrate_concentration_MichaelisMenten_Abebe2017( + 0.951219512195122, 0.75, 0.35, 0.25 + ), + 0.650000000000000, + places=7, + ) + + def test_n_dimensional_substrate_concentration_MichaelisMenten_Abebe2017( # noqa + self, + ): + """ + Test :func:`colour.biochemistry.michaelis_menten.\ +substrate_concentration_MichaelisMenten_Abebe2017` definition n-dimensional + arrays support. + """ + + S = 0.400000000000000 + V_max = 0.5 + K_m = 0.25 + b_m = 0.25 + v = substrate_concentration_MichaelisMenten_Abebe2017( + S, V_max, K_m, b_m + ) S = np.tile(S, (6, 1)) v = np.tile(v, (6, 1)) np.testing.assert_almost_equal( - substrate_concentration_MichealisMenten(S, V_max, K_m), + substrate_concentration_MichaelisMenten_Abebe2017( + S, V_max, K_m, b_m + ), v, - decimal=7) + decimal=7, + ) V_max = np.tile(V_max, (6, 1)) K_m = np.tile(K_m, (6, 1)) + b_m = np.tile(b_m, (6, 1)) np.testing.assert_almost_equal( - substrate_concentration_MichealisMenten(S, V_max, K_m), + substrate_concentration_MichaelisMenten_Abebe2017( + S, V_max, K_m, b_m + ), v, - decimal=7) + decimal=7, + ) S = np.reshape(S, (2, 3, 1)) V_max = np.reshape(V_max, (2, 3, 1)) K_m = np.reshape(K_m, (2, 3, 1)) + b_m = np.reshape(b_m, (2, 3, 1)) v = np.reshape(v, (2, 3, 1)) np.testing.assert_almost_equal( - substrate_concentration_MichealisMenten(S, V_max, K_m), + substrate_concentration_MichaelisMenten_Abebe2017( + S, V_max, K_m, b_m + ), v, - decimal=7) + decimal=7, + ) @ignore_numpy_errors - def test_nan_substrate_concentration_MichealisMenten(self): + def test_nan_substrate_concentration_MichaelisMenten_Abebe2017(self): """ - Tests :func:`colour.biochemistry.michaelis_menten.\ -substrate_concentration_MichealisMenten` definition nan support. + Test :func:`colour.biochemistry.michaelis_menten.\ +substrate_concentration_MichaelisMenten_Abebe2017` definition nan support. """ cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] @@ -173,8 +406,11 @@ def test_nan_substrate_concentration_MichealisMenten(self): s = np.array(case) V_max = np.array(case) K_m = np.array(case) - substrate_concentration_MichealisMenten(s, V_max, K_m) + b_m = np.array(case) + substrate_concentration_MichaelisMenten_Abebe2017( + s, V_max, K_m, b_m + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/blindness/__init__.py b/colour/blindness/__init__.py index 83c96d97be..a563c62bf1 100644 --- a/colour/blindness/__init__.py +++ b/colour/blindness/__init__.py @@ -1,60 +1,17 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - -import sys - -from colour.utilities.deprecation import ModuleAPI, build_API_changes -from colour.utilities.documentation import is_documentation_building from .datasets import * # noqa from . import datasets -from .machado2009 import (msds_cmfs_anomalous_trichromacy_Machado2009, - matrix_anomalous_trichromacy_Machado2009, - matrix_cvd_Machado2009) +from .machado2009 import ( + msds_cmfs_anomalous_trichromacy_Machado2009, + matrix_anomalous_trichromacy_Machado2009, + matrix_cvd_Machado2009, +) __all__ = [] __all__ += datasets.__all__ __all__ += [ - 'msds_cmfs_anomalous_trichromacy_Machado2009', - 'matrix_anomalous_trichromacy_Machado2009', 'matrix_cvd_Machado2009' + "msds_cmfs_anomalous_trichromacy_Machado2009", + "matrix_anomalous_trichromacy_Machado2009", + "matrix_cvd_Machado2009", ] - - -# ----------------------------------------------------------------------------# -# --- API Changes and Deprecation Management ---# -# ----------------------------------------------------------------------------# -class blindness(ModuleAPI): - def __getattr__(self, attribute): - return super(blindness, self).__getattr__(attribute) - - -# v0.3.16 -API_CHANGES = { - 'ObjectRenamed': [ - [ - 'colour.blindness.anomalous_trichromacy_cmfs_Machado2009', - 'colour.blindness.msds_cmfs_anomalous_trichromacy_Machado2009', - ], - [ - 'colour.blindness.anomalous_trichromacy_matrix_Machado2009', - 'colour.blindness.matrix_anomalous_trichromacy_Machado2009', - ], - [ - 'colour.blindness.cvd_matrix_Machado2009', - 'colour.blindness.matrix_cvd_Machado2009', - ], - ] -} -""" -Defines *colour.blindness* sub-package API changes. - -API_CHANGES : dict -""" - -if not is_documentation_building(): - sys.modules['colour.blindness'] = blindness( - sys.modules['colour.blindness'], build_API_changes(API_CHANGES)) - - del ModuleAPI, is_documentation_building, build_API_changes, sys diff --git a/colour/blindness/datasets/__init__.py b/colour/blindness/datasets/__init__.py index d98838ea51..6e7b10d336 100644 --- a/colour/blindness/datasets/__init__.py +++ b/colour/blindness/datasets/__init__.py @@ -1,8 +1,7 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- - -from __future__ import absolute_import from .machado2010 import CVD_MATRICES_MACHADO2010 -__all__ = ['CVD_MATRICES_MACHADO2010'] +__all__ = [ + "CVD_MATRICES_MACHADO2010", +] diff --git a/colour/blindness/datasets/machado2010.py b/colour/blindness/datasets/machado2010.py index 5ea3d77db7..ffe69a7a18 100644 --- a/colour/blindness/datasets/machado2010.py +++ b/colour/blindness/datasets/machado2010.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Pre-Computed Matrices for simulation of CVD - Machado (2009) ============================================================ @@ -14,230 +13,262 @@ http://www.lume.ufrgs.br/handle/10183/26950 """ -from __future__ import division, unicode_literals - import numpy as np from colour.utilities import CaseInsensitiveMapping -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['CVD_MATRICES_MACHADO2010'] +__all__ = [ + "CVD_MATRICES_MACHADO2010", +] -CVD_MATRICES_MACHADO2010 = CaseInsensitiveMapping({ - 'Protanomaly': { - 0.0: - np.array([ - [1.000000, 0.000000, -0.000000], - [0.000000, 1.000000, 0.000000], - [-0.000000, -0.000000, 1.000000], - ]), - 0.1: - np.array([ - [0.856167, 0.182038, -0.038205], - [0.029342, 0.955115, 0.015544], - [-0.002880, -0.001563, 1.004443], - ]), - 0.2: - np.array([ - [0.734766, 0.334872, -0.069637], - [0.051840, 0.919198, 0.028963], - [-0.004928, -0.004209, 1.009137], - ]), - 0.3: - np.array([ - [0.630323, 0.465641, -0.095964], - [0.069181, 0.890046, 0.040773], - [-0.006308, -0.007724, 1.014032], - ]), - 0.4: - np.array([ - [0.539009, 0.579343, -0.118352], - [0.082546, 0.866121, 0.051332], - [-0.007136, -0.011959, 1.019095], - ]), - 0.5: - np.array([ - [0.458064, 0.679578, -0.137642], - [0.092785, 0.846313, 0.060902], - [-0.007494, -0.016807, 1.024301], - ]), - 0.6: - np.array([ - [0.385450, 0.769005, -0.154455], - [0.100526, 0.829802, 0.069673], - [-0.007442, -0.022190, 1.029632], - ]), - 0.7: - np.array([ - [0.319627, 0.849633, -0.169261], - [0.106241, 0.815969, 0.077790], - [-0.007025, -0.028051, 1.035076], - ]), - 0.8: - np.array([ - [0.259411, 0.923008, -0.182420], - [0.110296, 0.804340, 0.085364], - [-0.006276, -0.034346, 1.040622], - ]), - 0.9: - np.array([ - [0.203876, 0.990338, -0.194214], - [0.112975, 0.794542, 0.092483], - [-0.005222, -0.041043, 1.046265], - ]), - 1.0: - np.array([ - [0.152286, 1.052583, -0.204868], - [0.114503, 0.786281, 0.099216], - [-0.003882, -0.048116, 1.051998], - ]) - }, - 'Deuteranomaly': { - 0.0: - np.array([ - [1.000000, 0.000000, -0.000000], - [0.000000, 1.000000, 0.000000], - [-0.000000, -0.000000, 1.000000], - ]), - 0.1: - np.array([ - [0.866435, 0.177704, -0.044139], - [0.049567, 0.939063, 0.011370], - [-0.003453, 0.007233, 0.996220], - ]), - 0.2: - np.array([ - [0.760729, 0.319078, -0.079807], - [0.090568, 0.889315, 0.020117], - [-0.006027, 0.013325, 0.992702], - ]), - 0.3: - np.array([ - [0.675425, 0.433850, -0.109275], - [0.125303, 0.847755, 0.026942], - [-0.007950, 0.018572, 0.989378], - ]), - 0.4: - np.array([ - [0.605511, 0.528560, -0.134071], - [0.155318, 0.812366, 0.032316], - [-0.009376, 0.023176, 0.986200], - ]), - 0.5: - np.array([ - [0.547494, 0.607765, -0.155259], - [0.181692, 0.781742, 0.036566], - [-0.010410, 0.027275, 0.983136], - ]), - 0.6: - np.array([ - [0.498864, 0.674741, -0.173604], - [0.205199, 0.754872, 0.039929], - [-0.011131, 0.030969, 0.980162], - ]), - 0.7: - np.array([ - [0.457771, 0.731899, -0.189670], - [0.226409, 0.731012, 0.042579], - [-0.011595, 0.034333, 0.977261], - ]), - 0.8: - np.array([ - [0.422823, 0.781057, -0.203881], - [0.245752, 0.709602, 0.044646], - [-0.011843, 0.037423, 0.974421], - ]), - 0.9: - np.array([ - [0.392952, 0.823610, -0.216562], - [0.263559, 0.690210, 0.046232], - [-0.011910, 0.040281, 0.971630], - ]), - 1.0: - np.array([ - [0.367322, 0.860646, -0.227968], - [0.280085, 0.672501, 0.047413], - [-0.011820, 0.042940, 0.968881], - ]) - }, - 'Tritanomaly': { - 0.0: - np.array([ - [1.000000, 0.000000, -0.000000], - [0.000000, 1.000000, 0.000000], - [-0.000000, -0.000000, 1.000000], - ]), - 0.1: - np.array([ - [0.926670, 0.092514, -0.019184], - [0.021191, 0.964503, 0.014306], - [0.008437, 0.054813, 0.936750], - ]), - 0.2: - np.array([ - [0.895720, 0.133330, -0.029050], - [0.029997, 0.945400, 0.024603], - [0.013027, 0.104707, 0.882266], - ]), - 0.3: - np.array([ - [0.905871, 0.127791, -0.033662], - [0.026856, 0.941251, 0.031893], - [0.013410, 0.148296, 0.838294], - ]), - 0.4: - np.array([ - [0.948035, 0.089490, -0.037526], - [0.014364, 0.946792, 0.038844], - [0.010853, 0.193991, 0.795156], - ]), - 0.5: - np.array([ - [1.017277, 0.027029, -0.044306], - [-0.006113, 0.958479, 0.047634], - [0.006379, 0.248708, 0.744913], - ]), - 0.6: - np.array([ - [1.104996, -0.046633, -0.058363], - [-0.032137, 0.971635, 0.060503], - [0.001336, 0.317922, 0.680742], - ]), - 0.7: - np.array([ - [1.193214, -0.109812, -0.083402], - [-0.058496, 0.979410, 0.079086], - [-0.002346, 0.403492, 0.598854], - ]), - 0.8: - np.array([ - [1.257728, -0.139648, -0.118081], - [-0.078003, 0.975409, 0.102594], - [-0.003316, 0.501214, 0.502102], - ]), - 0.9: - np.array([ - [1.278864, -0.125333, -0.153531], - [-0.084748, 0.957674, 0.127074], - [-0.000989, 0.601151, 0.399838], - ]), - 1.0: - np.array([ - [1.255528, -0.076749, -0.178779], - [-0.078411, 0.930809, 0.147602], - [0.004733, 0.691367, 0.303900], - ]) +CVD_MATRICES_MACHADO2010: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Protanomaly": { + 0.0: np.array( + [ + [1.000000, 0.000000, -0.000000], + [0.000000, 1.000000, 0.000000], + [-0.000000, -0.000000, 1.000000], + ] + ), + 0.1: np.array( + [ + [0.856167, 0.182038, -0.038205], + [0.029342, 0.955115, 0.015544], + [-0.002880, -0.001563, 1.004443], + ] + ), + 0.2: np.array( + [ + [0.734766, 0.334872, -0.069637], + [0.051840, 0.919198, 0.028963], + [-0.004928, -0.004209, 1.009137], + ] + ), + 0.3: np.array( + [ + [0.630323, 0.465641, -0.095964], + [0.069181, 0.890046, 0.040773], + [-0.006308, -0.007724, 1.014032], + ] + ), + 0.4: np.array( + [ + [0.539009, 0.579343, -0.118352], + [0.082546, 0.866121, 0.051332], + [-0.007136, -0.011959, 1.019095], + ] + ), + 0.5: np.array( + [ + [0.458064, 0.679578, -0.137642], + [0.092785, 0.846313, 0.060902], + [-0.007494, -0.016807, 1.024301], + ] + ), + 0.6: np.array( + [ + [0.385450, 0.769005, -0.154455], + [0.100526, 0.829802, 0.069673], + [-0.007442, -0.022190, 1.029632], + ] + ), + 0.7: np.array( + [ + [0.319627, 0.849633, -0.169261], + [0.106241, 0.815969, 0.077790], + [-0.007025, -0.028051, 1.035076], + ] + ), + 0.8: np.array( + [ + [0.259411, 0.923008, -0.182420], + [0.110296, 0.804340, 0.085364], + [-0.006276, -0.034346, 1.040622], + ] + ), + 0.9: np.array( + [ + [0.203876, 0.990338, -0.194214], + [0.112975, 0.794542, 0.092483], + [-0.005222, -0.041043, 1.046265], + ] + ), + 1.0: np.array( + [ + [0.152286, 1.052583, -0.204868], + [0.114503, 0.786281, 0.099216], + [-0.003882, -0.048116, 1.051998], + ] + ), + }, + "Deuteranomaly": { + 0.0: np.array( + [ + [1.000000, 0.000000, -0.000000], + [0.000000, 1.000000, 0.000000], + [-0.000000, -0.000000, 1.000000], + ] + ), + 0.1: np.array( + [ + [0.866435, 0.177704, -0.044139], + [0.049567, 0.939063, 0.011370], + [-0.003453, 0.007233, 0.996220], + ] + ), + 0.2: np.array( + [ + [0.760729, 0.319078, -0.079807], + [0.090568, 0.889315, 0.020117], + [-0.006027, 0.013325, 0.992702], + ] + ), + 0.3: np.array( + [ + [0.675425, 0.433850, -0.109275], + [0.125303, 0.847755, 0.026942], + [-0.007950, 0.018572, 0.989378], + ] + ), + 0.4: np.array( + [ + [0.605511, 0.528560, -0.134071], + [0.155318, 0.812366, 0.032316], + [-0.009376, 0.023176, 0.986200], + ] + ), + 0.5: np.array( + [ + [0.547494, 0.607765, -0.155259], + [0.181692, 0.781742, 0.036566], + [-0.010410, 0.027275, 0.983136], + ] + ), + 0.6: np.array( + [ + [0.498864, 0.674741, -0.173604], + [0.205199, 0.754872, 0.039929], + [-0.011131, 0.030969, 0.980162], + ] + ), + 0.7: np.array( + [ + [0.457771, 0.731899, -0.189670], + [0.226409, 0.731012, 0.042579], + [-0.011595, 0.034333, 0.977261], + ] + ), + 0.8: np.array( + [ + [0.422823, 0.781057, -0.203881], + [0.245752, 0.709602, 0.044646], + [-0.011843, 0.037423, 0.974421], + ] + ), + 0.9: np.array( + [ + [0.392952, 0.823610, -0.216562], + [0.263559, 0.690210, 0.046232], + [-0.011910, 0.040281, 0.971630], + ] + ), + 1.0: np.array( + [ + [0.367322, 0.860646, -0.227968], + [0.280085, 0.672501, 0.047413], + [-0.011820, 0.042940, 0.968881], + ] + ), + }, + "Tritanomaly": { + 0.0: np.array( + [ + [1.000000, 0.000000, -0.000000], + [0.000000, 1.000000, 0.000000], + [-0.000000, -0.000000, 1.000000], + ] + ), + 0.1: np.array( + [ + [0.926670, 0.092514, -0.019184], + [0.021191, 0.964503, 0.014306], + [0.008437, 0.054813, 0.936750], + ] + ), + 0.2: np.array( + [ + [0.895720, 0.133330, -0.029050], + [0.029997, 0.945400, 0.024603], + [0.013027, 0.104707, 0.882266], + ] + ), + 0.3: np.array( + [ + [0.905871, 0.127791, -0.033662], + [0.026856, 0.941251, 0.031893], + [0.013410, 0.148296, 0.838294], + ] + ), + 0.4: np.array( + [ + [0.948035, 0.089490, -0.037526], + [0.014364, 0.946792, 0.038844], + [0.010853, 0.193991, 0.795156], + ] + ), + 0.5: np.array( + [ + [1.017277, 0.027029, -0.044306], + [-0.006113, 0.958479, 0.047634], + [0.006379, 0.248708, 0.744913], + ] + ), + 0.6: np.array( + [ + [1.104996, -0.046633, -0.058363], + [-0.032137, 0.971635, 0.060503], + [0.001336, 0.317922, 0.680742], + ] + ), + 0.7: np.array( + [ + [1.193214, -0.109812, -0.083402], + [-0.058496, 0.979410, 0.079086], + [-0.002346, 0.403492, 0.598854], + ] + ), + 0.8: np.array( + [ + [1.257728, -0.139648, -0.118081], + [-0.078003, 0.975409, 0.102594], + [-0.003316, 0.501214, 0.502102], + ] + ), + 0.9: np.array( + [ + [1.278864, -0.125333, -0.153531], + [-0.084748, 0.957674, 0.127074], + [-0.000989, 0.601151, 0.399838], + ] + ), + 1.0: np.array( + [ + [1.255528, -0.076749, -0.178779], + [-0.078411, 0.930809, 0.147602], + [0.004733, 0.691367, 0.303900], + ] + ), + }, } -}) +) CVD_MATRICES_MACHADO2010.__doc__ = """ Machado (2010) Simulation matrices :math:`\\Phi_{CVD}`. - -CVD_MATRICES_MACHADO2010 : CaseInsensitiveMapping - {'Protanomaly', 'Deuteranomaly', 'Tritanomaly'} """ diff --git a/colour/blindness/machado2009.py b/colour/blindness/machado2009.py index ceaf26b465..9be44fc55a 100644 --- a/colour/blindness/machado2009.py +++ b/colour/blindness/machado2009.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Simulation of CVD - Machado, Oliveira and Fernandes (2009) ========================================================== -Defines *Machado et al. (2009)* objects for simulation of colour vision +Defines the *Machado et al. (2009)* objects for simulation of colour vision deficiency: - :func:`colour.msds_cmfs_anomalous_trichromacy_Machado2009` @@ -27,56 +26,66 @@ 15(6), 1291-1298. doi:10.1109/TVCG.2009.113 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +from colour.algebra import matrix_dot, vector_dot from colour.blindness import CVD_MATRICES_MACHADO2010 -from colour.colorimetry import SpectralShape -from colour.utilities import (matrix_dot, vector_dot, tsplit, tstack, - usage_warning) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.characterisation import RGB_DisplayPrimaries +from colour.colorimetry import ( + LMS_ConeFundamentals, + SpectralShape, + reshape_msds, +) +from colour.hints import ArrayLike, Floating, Literal, NDArray, Union, cast +from colour.utilities import as_float_array, tsplit, tstack, usage_warning + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'MATRIX_LMS_TO_WSYBRG', 'matrix_RGB_to_WSYBRG', - 'msds_cmfs_anomalous_trichromacy_Machado2009', - 'matrix_anomalous_trichromacy_Machado2009', 'matrix_cvd_Machado2009' + "MATRIX_LMS_TO_WSYBRG", + "matrix_RGB_to_WSYBRG", + "msds_cmfs_anomalous_trichromacy_Machado2009", + "matrix_anomalous_trichromacy_Machado2009", + "matrix_cvd_Machado2009", ] -MATRIX_LMS_TO_WSYBRG = np.array([ - [0.600, 0.400, 0.000], - [0.240, 0.105, -0.700], - [1.200, -1.600, 0.400], -]) +MATRIX_LMS_TO_WSYBRG: NDArray = np.array( + [ + [0.600, 0.400, 0.000], + [0.240, 0.105, -0.700], + [1.200, -1.600, 0.400], + ] +) """ Ingling and Tsou (1977) matrix converting from cones responses to opponent-colour space. - -MATRIX_LMS_TO_WSYBRG : array_like, (3, 3) """ -def matrix_RGB_to_WSYBRG(cmfs, primaries): +def matrix_RGB_to_WSYBRG( + cmfs: LMS_ConeFundamentals, primaries: RGB_DisplayPrimaries +) -> NDArray: """ - Computes the matrix transforming from *RGB* colourspace to opponent-colour + Compute the matrix transforming from *RGB* colourspace to opponent-colour space using *Machado et al. (2009)* method. Parameters ---------- - cmfs : LMS_ConeFundamentals + cmfs *LMS* cone fundamentals colour matching functions. - primaries : RGB_DisplayPrimaries + primaries *RGB* display primaries tri-spectral distributions. Returns ------- - ndarray + :class:`numpy.ndarray` Matrix transforming from *RGB* colourspace to opponent-colour space. Examples @@ -97,9 +106,12 @@ def matrix_RGB_to_WSYBRG(cmfs, primaries): WSYBRG = vector_dot(MATRIX_LMS_TO_WSYBRG, cmfs.values) WS, YB, RG = tsplit(WSYBRG) - extrapolator_kwargs = {'method': 'Constant', 'left': 0, 'right': 0} - primaries = primaries.copy().align( - cmfs.shape, extrapolator_kwargs=extrapolator_kwargs) + # pylint: disable=E1102 + primaries = reshape_msds( + primaries, # type: ignore[assignment] + cmfs.shape, + extrapolator_kwargs={"method": "Constant", "left": 0, "right": 0}, + ) R, G, B = tsplit(primaries.values) @@ -115,11 +127,13 @@ def matrix_RGB_to_WSYBRG(cmfs, primaries): RG_G = np.trapz(G * RG, wavelengths) RG_B = np.trapz(B * RG, wavelengths) - M_G = np.array([ - [WS_R, WS_G, WS_B], - [YB_R, YB_G, YB_B], - [RG_R, RG_G, RG_B], - ]) + M_G = np.array( + [ + [WS_R, WS_G, WS_B], + [YB_R, YB_G, YB_B], + [RG_R, RG_G, RG_B], + ] + ) PWS = 1 / (WS_R + WS_G + WS_B) PYB = 1 / (YB_R + YB_G + YB_B) @@ -130,17 +144,19 @@ def matrix_RGB_to_WSYBRG(cmfs, primaries): return M_G -def msds_cmfs_anomalous_trichromacy_Machado2009(cmfs, d_LMS): +def msds_cmfs_anomalous_trichromacy_Machado2009( + cmfs: LMS_ConeFundamentals, d_LMS: ArrayLike +) -> LMS_ConeFundamentals: """ - Shifts given *LMS* cone fundamentals colour matching functions with given + Shift given *LMS* cone fundamentals colour matching functions with given :math:`\\Delta_{LMS}` shift amount in nanometers to simulate anomalous trichromacy using *Machado et al. (2009)* method. Parameters ---------- - cmfs : LMS_ConeFundamentals + cmfs *LMS* cone fundamentals colour matching functions. - d_LMS : array_like + d_LMS :math:`\\Delta_{LMS}` shift amount in nanometers. Notes @@ -152,7 +168,7 @@ def msds_cmfs_anomalous_trichromacy_Machado2009(cmfs, d_LMS): Returns ------- - LMS_ConeFundamentals + :class:`colour.LMS_ConeFundamentals` Anomalous trichromacy *LMS* cone fundamentals colour matching functions. @@ -181,11 +197,11 @@ def msds_cmfs_anomalous_trichromacy_Machado2009(cmfs, d_LMS): array([ 0.0891288..., 0.0870524 , 0.955393 ]) """ - cmfs = cmfs.copy() + cmfs = cast(LMS_ConeFundamentals, cmfs.copy()) if cmfs.shape.interval != 1: - cmfs.interpolate(SpectralShape(interval=1)) + cmfs.interpolate(SpectralShape(cmfs.shape.start, cmfs.shape.end, 1)) - cmfs.extrapolator_kwargs = {'method': 'Constant', 'left': 0, 'right': 0} + cmfs.extrapolator_kwargs = {"method": "Constant", "left": 0, "right": 0} L, M, _S = tsplit(cmfs.values) d_L, d_M, d_S = tsplit(d_LMS) @@ -193,19 +209,18 @@ def msds_cmfs_anomalous_trichromacy_Machado2009(cmfs, d_LMS): if d_S != 0: usage_warning( '"Machado et al. (2009)" simulation of tritanomaly is based on ' - 'the shift paradigm as an approximation to the actual phenomenon ' - 'and restrain the model from trying to model tritanopia.\n' - 'The pre-generated matrices are using a shift value in domain ' - '[5, 59] contrary to the domain [0, 20] used for protanomaly and ' - 'deuteranomaly simulation.') + "the shift paradigm as an approximation to the actual phenomenon " + "and restrain the model from trying to model tritanopia.\n" + "The pre-generated matrices are using a shift value in domain " + "[5, 59] contrary to the domain [0, 20] used for protanomaly and " + "deuteranomaly simulation." + ) area_L = np.trapz(L, cmfs.wavelengths) area_M = np.trapz(M, cmfs.wavelengths) - def alpha(x): - """ - Computes :math:`alpha` factor. - """ + def alpha(x: NDArray) -> NDArray: + """Compute :math:`alpha` factor.""" return (20 - x) / 20 @@ -214,33 +229,37 @@ def alpha(x): # CVD_Simulation/CVD_Simulation.html#Errata L_a = alpha(d_L) * L + 0.96 * area_L / area_M * (1 - alpha(d_L)) * M M_a = alpha(d_M) * M + 1 / 0.96 * area_M / area_L * (1 - alpha(d_M)) * L - S_a = cmfs[cmfs.wavelengths - d_S][:, 2] + S_a = as_float_array(cmfs[cmfs.wavelengths - d_S])[:, 2] LMS_a = tstack([L_a, M_a, S_a]) cmfs[cmfs.wavelengths] = LMS_a - severity = '{0}, {1}, {2}'.format(d_L, d_M, d_S) - template = '{0} - Anomalous Trichromacy ({1})' + severity = f"{d_L}, {d_M}, {d_S}" + template = "{0} - Anomalous Trichromacy ({1})" cmfs.name = template.format(cmfs.name, severity) cmfs.strict_name = template.format(cmfs.strict_name, severity) return cmfs -def matrix_anomalous_trichromacy_Machado2009(cmfs, primaries, d_LMS): +def matrix_anomalous_trichromacy_Machado2009( + cmfs: LMS_ConeFundamentals, + primaries: RGB_DisplayPrimaries, + d_LMS: ArrayLike, +) -> NDArray: """ - Computes the *Machado et al. (2009)* *CVD* matrix for given *LMS* cone + Compute the *Machado et al. (2009)* *CVD* matrix for given *LMS* cone fundamentals colour matching functions and display primaries tri-spectral distributions with given :math:`\\Delta_{LMS}` shift amount in nanometers to simulate anomalous trichromacy. Parameters ---------- - cmfs : LMS_ConeFundamentals + cmfs *LMS* cone fundamentals colour matching functions. - primaries : RGB_DisplayPrimaries + primaries *RGB* display primaries tri-spectral distributions. - d_LMS : array_like + d_LMS :math:`\\Delta_{LMS}` shift amount in nanometers. Notes @@ -252,7 +271,7 @@ def matrix_anomalous_trichromacy_Machado2009(cmfs, primaries, d_LMS): Returns ------- - ndarray + :class:`numpy.ndarray` Anomalous trichromacy matrix. References @@ -275,7 +294,12 @@ def matrix_anomalous_trichromacy_Machado2009(cmfs, primaries, d_LMS): """ if cmfs.shape.interval != 1: - cmfs = cmfs.copy().interpolate(SpectralShape(interval=1)) + # pylint: disable=E1102 + cmfs = reshape_msds( + cmfs, # type: ignore[assignment] + SpectralShape(cmfs.shape.start, cmfs.shape.end, 1), + "Interpolate", + ) M_n = matrix_RGB_to_WSYBRG(cmfs, primaries) cmfs_a = msds_cmfs_anomalous_trichromacy_Machado2009(cmfs, d_LMS) @@ -284,15 +308,19 @@ def matrix_anomalous_trichromacy_Machado2009(cmfs, primaries, d_LMS): return matrix_dot(np.linalg.inv(M_n), M_a) -def matrix_cvd_Machado2009(deficiency, severity): +def matrix_cvd_Machado2009( + deficiency: Union[ + Literal["Deuteranomaly", "Protanomaly", "Tritanomaly"], str + ], + severity: Floating, +) -> NDArray: """ - Computes *Machado et al. (2009)* *CVD* matrix for given deficiency and + Compute *Machado et al. (2009)* *CVD* matrix for given deficiency and severity using the pre-computed matrices dataset. Parameters ---------- - deficiency : unicode - {'Protanomaly', 'Deuteranomaly', 'Tritanomaly'} + deficiency Colour blindness / vision deficiency types : - *Protanomaly* : defective long-wavelength cones (L-cones). The complete absence of L-cones is known as *Protanopia* or @@ -303,12 +331,12 @@ def matrix_cvd_Machado2009(deficiency, severity): - *Tritanomaly* : defective short-wavelength cones (S-cones), an alleviated form of blue-yellow color blindness. The complete absence of S-cones is known as *Tritanopia*. - severity : numeric + severity Severity of the colour vision deficiency in domain [0, 1]. Returns ------- - ndarray + :class:`numpy.ndarray` *CVD* matrix. References @@ -324,18 +352,19 @@ def matrix_cvd_Machado2009(deficiency, severity): [-0.004238 ..., -0.0024515..., 1.0066895...]]) """ - if deficiency.lower() == 'tritanomaly': + if deficiency.lower() == "tritanomaly": usage_warning( '"Machado et al. (2009)" simulation of tritanomaly is based on ' - 'the shift paradigm as an approximation to the actual phenomenon ' - 'and restrain the model from trying to model tritanopia.\n' - 'The pre-generated matrices are using a shift value in domain ' - '[5, 59] contrary to the domain [0, 20] used for protanomaly and ' - 'deuteranomaly simulation.') + "the shift paradigm as an approximation to the actual phenomenon " + "and restrain the model from trying to model tritanopia.\n" + "The pre-generated matrices are using a shift value in domain " + "[5, 59] contrary to the domain [0, 20] used for protanomaly and " + "deuteranomaly simulation." + ) matrices = CVD_MATRICES_MACHADO2010[deficiency] samples = np.array(sorted(matrices.keys())) - index = min(np.searchsorted(samples, severity), len(samples) - 1) + index = np.clip(np.searchsorted(samples, severity), 0, len(samples) - 1) a = samples[index] b = samples[min(index + 1, len(samples) - 1)] diff --git a/colour/blindness/tests/__init__.py b/colour/blindness/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/blindness/tests/__init__.py +++ b/colour/blindness/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/blindness/tests/test_machado2009.py b/colour/blindness/tests/test_machado2009.py index b0b0d815ca..01426221e0 100644 --- a/colour/blindness/tests/test_machado2009.py +++ b/colour/blindness/tests/test_machado2009.py @@ -1,210 +1,270 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.blindness.machado2009` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.blindness.machado2009` module.""" import numpy as np import unittest -from colour.blindness import (CVD_MATRICES_MACHADO2010, matrix_cvd_Machado2009, - msds_cmfs_anomalous_trichromacy_Machado2009, - matrix_anomalous_trichromacy_Machado2009) +from colour.blindness import ( + CVD_MATRICES_MACHADO2010, + matrix_cvd_Machado2009, + msds_cmfs_anomalous_trichromacy_Machado2009, + matrix_anomalous_trichromacy_Machado2009, +) from colour.characterisation import MSDS_DISPLAY_PRIMARIES from colour.colorimetry import MSDS_CMFS_LMS from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestMsdsCmfsAnomalousTrichromacyMachado2009', - 'TestMatrixAnomalousTrichromacyMachado2009', 'TestMatrixCvdMachado2009' + "TestMsdsCmfsAnomalousTrichromacyMachado2009", + "TestMatrixAnomalousTrichromacyMachado2009", + "TestMatrixCvdMachado2009", ] class TestMsdsCmfsAnomalousTrichromacyMachado2009(unittest.TestCase): """ - Defines :func:`colour.blindness.machado2009.\ + Define :func:`colour.blindness.machado2009.\ msds_cmfs_anomalous_trichromacy_Machado2009` definition unit tests methods. """ def test_msds_cmfs_anomalous_trichromacy_Machado2009(self): """ - Tests :func:`colour.blindness.machado2009.\ + Test :func:`colour.blindness.machado2009.\ msds_cmfs_anomalous_trichromacy_Machado2009` definition. """ - cmfs = MSDS_CMFS_LMS.get('Smith & Pokorny 1975 Normal Trichromats') + cmfs = MSDS_CMFS_LMS.get("Smith & Pokorny 1975 Normal Trichromats") np.testing.assert_almost_equal( msds_cmfs_anomalous_trichromacy_Machado2009( - cmfs, np.array([0, 0, 0], ))[450], + cmfs, + np.array( + [0, 0, 0], + ), + )[450], cmfs[450], - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( msds_cmfs_anomalous_trichromacy_Machado2009( - cmfs, np.array([1, 0, 0], ))[450], + cmfs, + np.array( + [1, 0, 0], + ), + )[450], np.array([0.03631700, 0.06350000, 0.91000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( msds_cmfs_anomalous_trichromacy_Machado2009( - cmfs, np.array([0, 1, 0], ))[450], + cmfs, + np.array( + [0, 1, 0], + ), + )[450], np.array([0.03430000, 0.06178404, 0.91000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( msds_cmfs_anomalous_trichromacy_Machado2009( - cmfs, np.array([0, 0, 1], ))[450], + cmfs, + np.array( + [0, 0, 1], + ), + )[450], np.array([0.03430000, 0.06350000, 0.92270240]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( msds_cmfs_anomalous_trichromacy_Machado2009( - cmfs, np.array([10, 0, 0], ))[450], + cmfs, + np.array( + [10, 0, 0], + ), + )[450], np.array([0.05447001, 0.06350000, 0.91000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( msds_cmfs_anomalous_trichromacy_Machado2009( - cmfs, np.array([0, 10, 0], ))[450], + cmfs, + np.array( + [0, 10, 0], + ), + )[450], np.array([0.03430000, 0.04634036, 0.91000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( msds_cmfs_anomalous_trichromacy_Machado2009( - cmfs, np.array([0, 0, 10], ))[450], + cmfs, + np.array( + [0, 0, 10], + ), + )[450], np.array([0.03430000, 0.06350000, 1.00000000]), - decimal=7) + decimal=7, + ) class TestMatrixAnomalousTrichromacyMachado2009(unittest.TestCase): """ - Defines :func:`colour.blindness.machado2009.\ + Define :func:`colour.blindness.machado2009.\ matrix_anomalous_trichromacy_Machado2009` definition unit tests methods. """ def test_matrix_anomalous_trichromacy_Machado2009(self): """ - Tests :func:`colour.blindness.machado2009.\ + Test :func:`colour.blindness.machado2009.\ matrix_anomalous_trichromacy_Machado2009` definition. """ - cmfs = MSDS_CMFS_LMS.get('Smith & Pokorny 1975 Normal Trichromats') - primaries = MSDS_DISPLAY_PRIMARIES['Typical CRT Brainard 1997'] + cmfs = MSDS_CMFS_LMS.get("Smith & Pokorny 1975 Normal Trichromats") + primaries = MSDS_DISPLAY_PRIMARIES["Typical CRT Brainard 1997"] np.testing.assert_almost_equal( - matrix_anomalous_trichromacy_Machado2009(cmfs, primaries, - np.array([0, 0, 0])), + matrix_anomalous_trichromacy_Machado2009( + cmfs, primaries, np.array([0, 0, 0]) + ), np.identity(3), - decimal=7) + decimal=7, + ) np.testing.assert_allclose( - matrix_anomalous_trichromacy_Machado2009(cmfs, primaries, - np.array([2, 0, 0])), - CVD_MATRICES_MACHADO2010.get('Protanomaly').get(0.1), + matrix_anomalous_trichromacy_Machado2009( + cmfs, primaries, np.array([2, 0, 0]) + ), + CVD_MATRICES_MACHADO2010.get("Protanomaly").get(0.1), rtol=0.0001, - atol=0.0001) + atol=0.0001, + ) np.testing.assert_allclose( - matrix_anomalous_trichromacy_Machado2009(cmfs, primaries, - np.array([20, 0, 0])), - CVD_MATRICES_MACHADO2010.get('Protanomaly').get(1.0), + matrix_anomalous_trichromacy_Machado2009( + cmfs, primaries, np.array([20, 0, 0]) + ), + CVD_MATRICES_MACHADO2010.get("Protanomaly").get(1.0), rtol=0.0001, - atol=0.0001) + atol=0.0001, + ) np.testing.assert_allclose( - matrix_anomalous_trichromacy_Machado2009(cmfs, primaries, - np.array([0, 2, 0])), - CVD_MATRICES_MACHADO2010.get('Deuteranomaly').get(0.1), + matrix_anomalous_trichromacy_Machado2009( + cmfs, primaries, np.array([0, 2, 0]) + ), + CVD_MATRICES_MACHADO2010.get("Deuteranomaly").get(0.1), rtol=0.0001, - atol=0.0001) + atol=0.0001, + ) np.testing.assert_allclose( - matrix_anomalous_trichromacy_Machado2009(cmfs, primaries, - np.array([0, 20, 0])), - CVD_MATRICES_MACHADO2010.get('Deuteranomaly').get(1.0), + matrix_anomalous_trichromacy_Machado2009( + cmfs, primaries, np.array([0, 20, 0]) + ), + CVD_MATRICES_MACHADO2010.get("Deuteranomaly").get(1.0), rtol=0.0001, - atol=0.0001) + atol=0.0001, + ) np.testing.assert_allclose( matrix_anomalous_trichromacy_Machado2009( - cmfs, primaries, np.array([0, 0, 5.00056688094503])), - CVD_MATRICES_MACHADO2010.get('Tritanomaly').get(0.1), + cmfs, primaries, np.array([0, 0, 5.00056688094503]) + ), + CVD_MATRICES_MACHADO2010.get("Tritanomaly").get(0.1), rtol=0.0001, - atol=0.0001) + atol=0.0001, + ) np.testing.assert_allclose( matrix_anomalous_trichromacy_Machado2009( - cmfs, primaries, np.array([0, 0, 59.00590434857581])), - CVD_MATRICES_MACHADO2010.get('Tritanomaly').get(1.0), + cmfs, primaries, np.array([0, 0, 59.00590434857581]) + ), + CVD_MATRICES_MACHADO2010.get("Tritanomaly").get(1.0), rtol=0.001, - atol=0.001) + atol=0.001, + ) class TestMatrixCvdMachado2009(unittest.TestCase): """ - Defines :func:`colour.blindness.machado2009.matrix_cvd_Machado2009` + Define :func:`colour.blindness.machado2009.matrix_cvd_Machado2009` definition unit tests methods. """ def test_matrix_cvd_Machado2009(self): """ - Tests :func:`colour.blindness.machado2009.matrix_cvd_Machado2009` + Test :func:`colour.blindness.machado2009.matrix_cvd_Machado2009` definition. """ np.testing.assert_almost_equal( - matrix_cvd_Machado2009('Protanomaly', 0.0), - np.array([ - [1, 0, 0], - [0, 1, 0], - [0, 0, 1], - ]), - decimal=7) + matrix_cvd_Machado2009("Protanomaly", 0.0), + np.array( + [ + [1, 0, 0], + [0, 1, 0], + [0, 0, 1], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - matrix_cvd_Machado2009('Deuteranomaly', 0.1), - np.array([ - [0.86643500, 0.17770400, -0.04413900], - [0.04956700, 0.93906300, 0.01137000], - [-0.00345300, 0.00723300, 0.99622000], - ]), - decimal=7) + matrix_cvd_Machado2009("Deuteranomaly", 0.1), + np.array( + [ + [0.86643500, 0.17770400, -0.04413900], + [0.04956700, 0.93906300, 0.01137000], + [-0.00345300, 0.00723300, 0.99622000], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - matrix_cvd_Machado2009('Tritanomaly', 1.0), - np.array([ - [1.25552800, -0.07674900, -0.17877900], - [-0.07841100, 0.93080900, 0.14760200], - [0.00473300, 0.69136700, 0.30390000], - ]), - decimal=7) + matrix_cvd_Machado2009("Tritanomaly", 1.0), + np.array( + [ + [1.25552800, -0.07674900, -0.17877900], + [-0.07841100, 0.93080900, 0.14760200], + [0.00473300, 0.69136700, 0.30390000], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - matrix_cvd_Machado2009('Tritanomaly', 0.55), - np.array([ - [1.06088700, -0.01504350, -0.04584350], - [-0.01895750, 0.96774750, 0.05121150], - [0.00317700, 0.27513700, 0.72168600], - ]), - decimal=7) + matrix_cvd_Machado2009("Tritanomaly", 0.55), + np.array( + [ + [1.06088700, -0.01504350, -0.04584350], + [-0.01895750, 0.96774750, 0.05121150], + [0.00317700, 0.27513700, 0.72168600], + ] + ), + decimal=7, + ) @ignore_numpy_errors def test_nan_matrix_cvd_Machado2009(self): """ - Tests :func:`colour.blindness.machado2009.matrix_cvd_Machado2009` + Test :func:`colour.blindness.machado2009.matrix_cvd_Machado2009` definition nan support. """ for case in [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]: - matrix_cvd_Machado2009('Tritanomaly', case) + matrix_cvd_Machado2009("Tritanomaly", case) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/characterisation/__init__.py b/colour/characterisation/__init__.py index d4554d240e..79d8b0c626 100644 --- a/colour/characterisation/__init__.py +++ b/colour/characterisation/__init__.py @@ -1,51 +1,85 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - import sys from colour.utilities.deprecation import ModuleAPI, build_API_changes from colour.utilities.documentation import is_documentation_building +from colour.hints import Any + from .cameras import RGB_CameraSensitivities from .displays import RGB_DisplayPrimaries from .datasets import * # noqa from . import datasets from .aces_it import ( - sd_to_aces_relative_exposure_values, read_training_data_rawtoaces_v1, - generate_illuminants_rawtoaces_v1, white_balance_multipliers, - best_illuminant, normalise_illuminant, training_data_sds_to_RGB, - training_data_sds_to_XYZ, optimisation_factory_rawtoaces_v1, - optimisation_factory_JzAzBz, matrix_idt) + sd_to_aces_relative_exposure_values, + sd_to_ACES2065_1, + read_training_data_rawtoaces_v1, + generate_illuminants_rawtoaces_v1, + white_balance_multipliers, + best_illuminant, + normalise_illuminant, + training_data_sds_to_RGB, + training_data_sds_to_XYZ, + optimisation_factory_rawtoaces_v1, + optimisation_factory_Jzazbz, + matrix_idt, + camera_RGB_to_ACES2065_1, +) from .correction import ( - matrix_augmented_Cheung2004, polynomial_expansion_Finlayson2015, - polynomial_expansion_Vandermonde, POLYNOMIAL_EXPANSION_METHODS, - polynomial_expansion, matrix_colour_correction_Cheung2004, + matrix_augmented_Cheung2004, + polynomial_expansion_Finlayson2015, + polynomial_expansion_Vandermonde, + POLYNOMIAL_EXPANSION_METHODS, + polynomial_expansion, + matrix_colour_correction_Cheung2004, matrix_colour_correction_Finlayson2015, - matrix_colour_correction_Vandermonde, MATRIX_COLOUR_CORRECTION_METHODS, - matrix_colour_correction, colour_correction_Cheung2004, - colour_correction_Finlayson2015, colour_correction_Vandermonde, - COLOUR_CORRECTION_METHODS, colour_correction) + matrix_colour_correction_Vandermonde, + MATRIX_COLOUR_CORRECTION_METHODS, + matrix_colour_correction, + colour_correction_Cheung2004, + colour_correction_Finlayson2015, + colour_correction_Vandermonde, + COLOUR_CORRECTION_METHODS, + colour_correction, +) -__all__ = ['RGB_CameraSensitivities'] -__all__ += ['RGB_DisplayPrimaries'] +__all__ = [ + "RGB_CameraSensitivities", +] +__all__ += [ + "RGB_DisplayPrimaries", +] __all__ += datasets.__all__ __all__ += [ - 'sd_to_aces_relative_exposure_values', 'read_training_data_rawtoaces_v1', - 'generate_illuminants_rawtoaces_v1', 'white_balance_multipliers', - 'best_illuminant', 'normalise_illuminant', 'training_data_sds_to_RGB', - 'training_data_sds_to_XYZ', 'optimisation_factory_rawtoaces_v1', - 'optimisation_factory_JzAzBz', 'matrix_idt' + "sd_to_aces_relative_exposure_values", + "sd_to_ACES2065_1", + "read_training_data_rawtoaces_v1", + "generate_illuminants_rawtoaces_v1", + "white_balance_multipliers", + "best_illuminant", + "normalise_illuminant", + "training_data_sds_to_RGB", + "training_data_sds_to_XYZ", + "optimisation_factory_rawtoaces_v1", + "optimisation_factory_Jzazbz", + "matrix_idt", + "camera_RGB_to_ACES2065_1", ] __all__ += [ - 'matrix_augmented_Cheung2004', 'polynomial_expansion_Finlayson2015', - 'polynomial_expansion_Vandermonde', 'POLYNOMIAL_EXPANSION_METHODS', - 'polynomial_expansion', 'matrix_colour_correction_Cheung2004', - 'matrix_colour_correction_Finlayson2015', - 'matrix_colour_correction_Vandermonde', 'MATRIX_COLOUR_CORRECTION_METHODS', - 'matrix_colour_correction', 'colour_correction_Cheung2004', - 'colour_correction_Finlayson2015', 'colour_correction_Vandermonde', - 'COLOUR_CORRECTION_METHODS', 'colour_correction' + "matrix_augmented_Cheung2004", + "polynomial_expansion_Finlayson2015", + "polynomial_expansion_Vandermonde", + "POLYNOMIAL_EXPANSION_METHODS", + "polynomial_expansion", + "matrix_colour_correction_Cheung2004", + "matrix_colour_correction_Finlayson2015", + "matrix_colour_correction_Vandermonde", + "MATRIX_COLOUR_CORRECTION_METHODS", + "matrix_colour_correction", + "colour_correction_Cheung2004", + "colour_correction_Finlayson2015", + "colour_correction_Vandermonde", + "COLOUR_CORRECTION_METHODS", + "colour_correction", ] @@ -53,72 +87,30 @@ # --- API Changes and Deprecation Management ---# # ----------------------------------------------------------------------------# class characterisation(ModuleAPI): - def __getattr__(self, attribute): - return super(characterisation, self).__getattr__(attribute) + """Define a class acting like the *characterisation* module.""" + + def __getattr__(self, attribute) -> Any: + """Return the value from the attribute with given name.""" + + return super().__getattr__(attribute) -# v0.3.16 +# v0.4.0 API_CHANGES = { - 'ObjectRenamed': [ - [ - 'colour.characterisation.RGB_SpectralSensitivities', - 'colour.characterisation.RGB_CameraSensitivities', - ], - [ - 'colour.characterisation.CAMERA_RGB_SPECTRAL_SENSITIVITIES', - 'colour.characterisation.MSDS_CAMERA_SENSITIVITIES', - ], - [ - 'colour.characterisation.COLOURCHECKERS', - 'colour.characterisation.CCS_COLOURCHECKERS', - ], - [ - 'colour.characterisation.COLOURCHECKER_SDS', - 'colour.characterisation.SDS_COLOURCHECKERS', - ], + "ObjectRenamed": [ [ - 'colour.characterisation.COLOUR_CORRECTION_MATRIX_METHODS', - 'colour.characterisation.MATRIX_COLOUR_CORRECTION_METHODS', - ], - [ - 'colour.characterisation.DISPLAY_RGB_PRIMARIES', - 'colour.characterisation.MSDS_DISPLAY_PRIMARIES', - ], - [ - 'colour.characterisation.augmented_matrix_Cheung2004', - 'colour.characterisation.matrix_augmented_Cheung2004', - ], - [ - 'colour.characterisation.colour_correction_matrix', - 'colour.characterisation.matrix_colour_correction', - ], - [ - 'colour.characterisation.colour_correction_matrix_Cheung2004', - 'colour.characterisation.matrix_colour_correction_Cheung2004', - ], - [ - 'colour.characterisation.colour_correction_matrix_Finlayson2015', - 'colour.characterisation.matrix_colour_correction_Finlayson2015', - ], - [ - 'colour.characterisation.colour_correction_matrix_Vandermonde', - 'colour.characterisation.matrix_colour_correction_Vandermonde', - ], - # Not strictly needed but in use by A.M.P.A.S. - [ - 'colour.characterisation.idt_matrix', - 'colour.characterisation.matrix_idt', + "colour.characterisation.optimisation_factory_JzAzBz", + "colour.characterisation.optimisation_factory_Jzazbz", ], ] } -""" -Defines *colour.characterisation* sub-package API changes. - -API_CHANGES : dict -""" +"""Defines the *colour.characterisation* sub-package API changes.""" if not is_documentation_building(): - sys.modules['colour.characterisation'] = characterisation( - sys.modules['colour.characterisation'], build_API_changes(API_CHANGES)) + sys.modules[ + "colour.characterisation" + ] = characterisation( # type: ignore[assignment] + sys.modules["colour.characterisation"], build_API_changes(API_CHANGES) + ) del ModuleAPI, is_documentation_building, build_API_changes, sys diff --git a/colour/characterisation/aces_it.py b/colour/characterisation/aces_it.py index 9128f2c053..ece4e2feb6 100644 --- a/colour/characterisation/aces_it.py +++ b/colour/characterisation/aces_it.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Academy Color Encoding System - Input Transform =============================================== @@ -6,6 +5,7 @@ Defines the *Academy Color Encoding System* (ACES) *Input Transform* utilities: - :func:`colour.sd_to_aces_relative_exposure_values` +- :func:`colour.sd_to_ACES2065_1` - :func:`colour.characterisation.read_training_data_rawtoaces_v1` - :func:`colour.characterisation.generate_illuminants_rawtoaces_v1` - :func:`colour.characterisation.white_balance_multipliers` @@ -14,8 +14,9 @@ - :func:`colour.characterisation.training_data_sds_to_RGB` - :func:`colour.characterisation.training_data_sds_to_XYZ` - :func:`colour.characterisation.optimisation_factory_rawtoaces_v1` -- :func:`colour.characterisation.optimisation_factory_JzAzBz` +- :func:`colour.characterisation.optimisation_factory_Jzazbz` - :func:`colour.matrix_idt` +- :func:`colour.camera_RGB_to_ACES2065_1` References ---------- @@ -47,91 +48,142 @@ http://www.oscars.org/science-technology/council/projects/aces.html """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import os from scipy.optimize import minimize from colour.adaptation import matrix_chromatic_adaptation_VonKries -from colour.algebra import euclidean_distance +from colour.algebra import euclidean_distance, vector_dot from colour.colorimetry import ( - MSDS_CMFS, SDS_ILLUMINANTS, SpectralShape, sds_and_msds_to_msds, - sd_CIE_illuminant_D_series, sd_blackbody, sd_to_XYZ) -from colour.constants import DEFAULT_INT_DTYPE -from colour.characterisation import MSDS_ACES_RICD + MultiSpectralDistributions, + SDS_ILLUMINANTS, + SpectralDistribution, + SpectralShape, + handle_spectral_arguments, + reshape_msds, + reshape_sd, + sds_and_msds_to_msds, + sd_CIE_illuminant_D_series, + sd_blackbody, + sd_to_XYZ, +) +from colour.characterisation import MSDS_ACES_RICD, RGB_CameraSensitivities +from colour.hints import ( + ArrayLike, + Boolean, + Callable, + Dict, + Floating, + FloatingOrNDArray, + Literal, + Mapping, + NDArray, + Optional, + Tuple, + Union, + cast, +) from colour.io import read_sds_from_csv_file -from colour.models import XYZ_to_JzAzBz, XYZ_to_Lab, XYZ_to_xy, xy_to_XYZ -from colour.models.rgb import (RGB_COLOURSPACE_ACES2065_1, RGB_to_XYZ, - XYZ_to_RGB, normalised_primary_matrix) +from colour.models import XYZ_to_Jzazbz, XYZ_to_Lab, XYZ_to_xy, xy_to_XYZ +from colour.models.rgb import ( + RGB_COLOURSPACE_ACES2065_1, + RGB_to_XYZ, + XYZ_to_RGB, + normalised_primary_matrix, +) from colour.temperature import CCT_to_xy_CIE_D -from colour.utilities import (CaseInsensitiveMapping, as_float_array, - vector_dot, from_range_1, runtime_warning, - tsplit, suppress_warnings) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.utilities import ( + CaseInsensitiveMapping, + as_float, + as_float_array, + from_range_1, + optional, + runtime_warning, + suppress_warnings, + tsplit, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'FLARE_PERCENTAGE', 'S_FLARE_FACTOR', - 'sd_to_aces_relative_exposure_values', 'SPECTRAL_SHAPE_RAWTOACES', - 'RESOURCES_DIRECTORY_RAWTOACES', 'read_training_data_rawtoaces_v1', - 'generate_illuminants_rawtoaces_v1', 'white_balance_multipliers', - 'best_illuminant', 'normalise_illuminant', 'training_data_sds_to_RGB', - 'training_data_sds_to_XYZ', 'optimisation_factory_rawtoaces_v1', - 'optimisation_factory_JzAzBz', 'matrix_idt' + "FLARE_PERCENTAGE", + "S_FLARE_FACTOR", + "sd_to_aces_relative_exposure_values", + "sd_to_ACES2065_1", + "SPECTRAL_SHAPE_RAWTOACES", + "RESOURCES_DIRECTORY_RAWTOACES", + "read_training_data_rawtoaces_v1", + "generate_illuminants_rawtoaces_v1", + "white_balance_multipliers", + "best_illuminant", + "normalise_illuminant", + "training_data_sds_to_RGB", + "training_data_sds_to_XYZ", + "optimisation_factory_rawtoaces_v1", + "optimisation_factory_Jzazbz", + "matrix_idt", + "camera_RGB_to_ACES2065_1", ] -FLARE_PERCENTAGE = 0.00500 -""" -Flare percentage in the *ACES* system. - -FLARE_PERCENTAGE : float -""" +FLARE_PERCENTAGE: Floating = 0.00500 +"""Flare percentage in the *ACES* system.""" -S_FLARE_FACTOR = 0.18000 / (0.18000 + FLARE_PERCENTAGE) -""" -Flare modulation factor in the *ACES* system. - -S_FLARE_FACTOR : float -""" +S_FLARE_FACTOR: Floating = 0.18000 / (0.18000 + FLARE_PERCENTAGE) +"""Flare modulation factor in the *ACES* system.""" def sd_to_aces_relative_exposure_values( - sd, - illuminant=SDS_ILLUMINANTS['D65'], - apply_chromatic_adaptation=False, - chromatic_adaptation_transform='CAT02'): + sd: SpectralDistribution, + illuminant: Optional[SpectralDistribution] = None, + apply_chromatic_adaptation: Boolean = False, + chromatic_adaptation_transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", +) -> NDArray: """ - Converts given spectral distribution to *ACES2065-1* colourspace relative + Convert given spectral distribution to *ACES2065-1* colourspace relative exposure values. Parameters ---------- - sd : SpectralDistribution + sd Spectral distribution. - illuminant : SpectralDistribution, optional - *Illuminant* spectral distribution. - apply_chromatic_adaptation : bool, optional + illuminant + *Illuminant* spectral distribution, default to + *CIE Standard Illuminant D65*. + apply_chromatic_adaptation Whether to apply chromatic adaptation using given transform. - chromatic_adaptation_transform : unicode, optional - **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', - 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02 Brill 2008', - 'Bianco 2010', 'Bianco PC 2010'}**, + chromatic_adaptation_transform *Chromatic adaptation* transform. Returns ------- - ndarray, (3,) + :class:`numpy.ndarray` *ACES2065-1* colourspace relative exposure values array. Notes ----- - +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -161,22 +213,24 @@ def sd_to_aces_relative_exposure_values( array([ 0.1180779..., 0.0869031..., 0.0589125...]) """ + illuminant = cast( + SpectralDistribution, optional(illuminant, SDS_ILLUMINANTS["D65"]) + ) + shape = MSDS_ACES_RICD.shape if sd.shape != MSDS_ACES_RICD.shape: - sd = sd.copy().align(shape) + sd = reshape_sd(sd, shape) if illuminant.shape != MSDS_ACES_RICD.shape: - illuminant = illuminant.copy().align(shape) + illuminant = reshape_sd(illuminant, shape) s_v = sd.values i_v = illuminant.values r_bar, g_bar, b_bar = tsplit(MSDS_ACES_RICD.values) - def k(x, y): - """ - Computes the :math:`K_r`, :math:`K_g` or :math:`K_b` scale factors. - """ + def k(x: NDArray, y: NDArray) -> NDArray: + """Compute the :math:`K_r`, :math:`K_g` or :math:`K_b` scale factors.""" return 1 / np.sum(x * y) @@ -196,26 +250,34 @@ def k(x, y): if apply_chromatic_adaptation: xy = XYZ_to_xy(sd_to_XYZ(illuminant) / 100) - NPM = normalised_primary_matrix(RGB_COLOURSPACE_ACES2065_1.primaries, - xy) - XYZ = RGB_to_XYZ(E_rgb, xy, RGB_COLOURSPACE_ACES2065_1.whitepoint, NPM, - chromatic_adaptation_transform) - E_rgb = XYZ_to_RGB(XYZ, RGB_COLOURSPACE_ACES2065_1.whitepoint, - RGB_COLOURSPACE_ACES2065_1.whitepoint, - RGB_COLOURSPACE_ACES2065_1.matrix_XYZ_to_RGB) + NPM = normalised_primary_matrix( + RGB_COLOURSPACE_ACES2065_1.primaries, xy + ) + XYZ = RGB_to_XYZ( + E_rgb, + xy, + RGB_COLOURSPACE_ACES2065_1.whitepoint, + NPM, + chromatic_adaptation_transform, + ) + E_rgb = XYZ_to_RGB( + XYZ, + RGB_COLOURSPACE_ACES2065_1.whitepoint, + RGB_COLOURSPACE_ACES2065_1.whitepoint, + RGB_COLOURSPACE_ACES2065_1.matrix_XYZ_to_RGB, + ) return from_range_1(E_rgb) -SPECTRAL_SHAPE_RAWTOACES = SpectralShape(380, 780, 5) -""" -Default spectral shape according to *RAW to ACES* v1. +sd_to_ACES2065_1 = sd_to_aces_relative_exposure_values -SPECTRAL_SHAPE_RAWTOACES : SpectralShape -""" +SPECTRAL_SHAPE_RAWTOACES: SpectralShape = SpectralShape(380, 780, 5) +"""Default spectral shape according to *RAW to ACES* v1.""" -RESOURCES_DIRECTORY_RAWTOACES = os.path.join( - os.path.dirname(__file__), 'datasets', 'rawtoaces') +RESOURCES_DIRECTORY_RAWTOACES: str = os.path.join( + os.path.dirname(__file__), "datasets", "rawtoaces" +) """ *RAW to ACES* resources directory. @@ -224,20 +286,18 @@ def k(x, y): - *Colour* only ships a minimal dataset from *RAW to ACES*, please see `Colour - Datasets `_ for the complete *RAW to ACES* v1 dataset, i.e. *3372171*. - -RESOURCES_DIRECTORY_RAWTOACES : unicode """ -_TRAINING_DATA_RAWTOACES_V1 = None +_TRAINING_DATA_RAWTOACES_V1: Optional[MultiSpectralDistributions] = None -def read_training_data_rawtoaces_v1(): +def read_training_data_rawtoaces_v1() -> MultiSpectralDistributions: """ - Reads the *RAW to ACES* v1 190 patches. + Read the *RAW to ACES* v1 190 patches. Returns ------- - MultiSpectralDistributions + :class:`colour.MultiSpectralDistributions` *RAW to ACES* v1 190 patches. References @@ -255,21 +315,22 @@ def read_training_data_rawtoaces_v1(): if _TRAINING_DATA_RAWTOACES_V1 is not None: training_data = _TRAINING_DATA_RAWTOACES_V1 else: - path = os.path.join(RESOURCES_DIRECTORY_RAWTOACES, '190_Patches.csv') + path = os.path.join(RESOURCES_DIRECTORY_RAWTOACES, "190_Patches.csv") training_data = sds_and_msds_to_msds( - read_sds_from_csv_file(path).values()) + list(read_sds_from_csv_file(path).values()) + ) _TRAINING_DATA_RAWTOACES_V1 = training_data return training_data -_ILLUMINANTS_RAWTOACES_V1 = None +_ILLUMINANTS_RAWTOACES_V1: Optional[CaseInsensitiveMapping] = None -def generate_illuminants_rawtoaces_v1(): +def generate_illuminants_rawtoaces_v1() -> CaseInsensitiveMapping: """ - Generates a series of illuminants according to *RAW to ACES* v1: + Generate a series of illuminants according to *RAW to ACES* v1: - *CIE Illuminant D Series* in range [4000, 25000] kelvin degrees. - *Blackbodies* in range [1000, 3500] kelvin degrees. @@ -277,7 +338,7 @@ def generate_illuminants_rawtoaces_v1(): Returns ------- - CaseInsensitiveMapping + :class:`colour.utilities.CaseInsensitiveMapping` Series of illuminants. Notes @@ -293,9 +354,7 @@ def generate_illuminants_rawtoaces_v1(): Examples -------- - >>> # Doctests skip for Python 2.x compatibility. >>> list(sorted(generate_illuminants_rawtoaces_v1().keys())) - ... # doctest: +SKIP ['1000K Blackbody', '1500K Blackbody', '2000K Blackbody', \ '2500K Blackbody', '3000K Blackbody', '3500K Blackbody', 'D100', 'D105', \ 'D110', 'D115', 'D120', 'D125', 'D130', 'D135', 'D140', 'D145', 'D150', \ @@ -317,7 +376,7 @@ def generate_illuminants_rawtoaces_v1(): CCT = i * 1.4388 / 1.4380 xy = CCT_to_xy_CIE_D(CCT) sd = sd_CIE_illuminant_D_series(xy) - sd.name = 'D{0:d}'.format(DEFAULT_INT_DTYPE(CCT / 100)) + sd.name = f"D{int(CCT / 100):d}" illuminants[sd.name] = sd.align(SPECTRAL_SHAPE_RAWTOACES) # TODO: Remove when removing the "colour.sd_blackbody" definition @@ -330,8 +389,10 @@ def generate_illuminants_rawtoaces_v1(): # A.M.P.A.S. variant of ISO 7589 Studio Tungsten. sd = read_sds_from_csv_file( - os.path.join(RESOURCES_DIRECTORY_RAWTOACES, - 'AMPAS_ISO_7589_Tungsten.csv'))['iso7589'] + os.path.join( + RESOURCES_DIRECTORY_RAWTOACES, "AMPAS_ISO_7589_Tungsten.csv" + ) + )["iso7589"] illuminants.update({sd.name: sd}) _ILLUMINANTS_RAWTOACES_V1 = illuminants @@ -339,21 +400,23 @@ def generate_illuminants_rawtoaces_v1(): return illuminants -def white_balance_multipliers(sensitivities, illuminant): +def white_balance_multipliers( + sensitivities: RGB_CameraSensitivities, illuminant: SpectralDistribution +) -> NDArray: """ - Computes the *RGB* white balance multipliers for given camera *RGB* + Compute the *RGB* white balance multipliers for given camera *RGB* spectral sensitivities and illuminant. Parameters ---------- - sensitivities : RGB_CameraSensitivities + sensitivities Camera *RGB* spectral sensitivities. - illuminant : SpectralDistribution + illuminant Illuminant spectral distribution. Returns ------- - ndarray + :class:`numpy.ndarray` *RGB* white balance multipliers. References @@ -375,34 +438,40 @@ def white_balance_multipliers(sensitivities, illuminant): shape = sensitivities.shape if illuminant.shape != shape: - runtime_warning('Aligning "{0}" illuminant shape to "{1}".'.format( - illuminant.name, shape)) - illuminant = illuminant.copy().align(shape) + runtime_warning( + f'Aligning "{illuminant.name}" illuminant shape to "{shape}".' + ) + illuminant = reshape_sd(illuminant, shape) RGB_w = 1 / np.sum( - sensitivities.values * illuminant.values[..., np.newaxis], axis=0) + sensitivities.values * illuminant.values[..., np.newaxis], axis=0 + ) RGB_w *= 1 / np.min(RGB_w) return RGB_w -def best_illuminant(RGB_w, sensitivities, illuminants): +def best_illuminant( + RGB_w: ArrayLike, + sensitivities: RGB_CameraSensitivities, + illuminants: Mapping, +) -> SpectralDistribution: """ - Select the best illuminant for given *RGB* white balance multipliers, - and sensitivities in given series of illuminants. + Select the best illuminant for given *RGB* white balance multipliers, and + sensitivities in given series of illuminants. Parameters ---------- - RGB_w : array_like + RGB_w *RGB* white balance multipliers. - sensitivities : RGB_CameraSensitivities + sensitivities Camera *RGB* spectral sensitivities. - illuminants : SpectralDistribution + illuminants Illuminant spectral distributions to choose the best illuminant from. Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Best illuminant. Examples @@ -415,9 +484,7 @@ def best_illuminant(RGB_w, sensitivities, illuminants): >>> illuminants = generate_illuminants_rawtoaces_v1() >>> RGB_w = white_balance_multipliers( ... sensitivities, SDS_ILLUMINANTS['FL2']) - >>> # Doctests skip for Python 2.x compatibility. >>> best_illuminant(RGB_w, sensitivities, illuminants).name - ... # doctest: +SKIP 'D40' """ @@ -432,12 +499,14 @@ def best_illuminant(RGB_w, sensitivities, illuminants): sse = sse_c illuminant_b = illuminant - return illuminant_b + return illuminant_b # type: ignore[return-value] -def normalise_illuminant(illuminant, sensitivities): +def normalise_illuminant( + illuminant: SpectralDistribution, sensitivities: RGB_CameraSensitivities +) -> SpectralDistribution: """ - Normalises given illuminant with given camera *RGB* spectral sensitivities. + Normalise given illuminant with given camera *RGB* spectral sensitivities. The multiplicative inverse scaling factor :math:`k` is computed by multiplying the illuminant by the sensitivies channel with the maximum @@ -445,14 +514,14 @@ def normalise_illuminant(illuminant, sensitivities): Parameters ---------- - illuminant : SpectralDistribution + illuminant Illuminant spectral distribution. - sensitivities : RGB_CameraSensitivities + sensitivities Camera *RGB* spectral sensitivities. Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Normalised illuminant. Examples @@ -472,9 +541,10 @@ def normalise_illuminant(illuminant, sensitivities): shape = sensitivities.shape if illuminant.shape != shape: - runtime_warning('Aligning "{0}" illuminant shape to "{1}".'.format( - illuminant.name, shape)) - illuminant = illuminant.copy().align(shape) + runtime_warning( + f'Aligning "{illuminant.name}" illuminant shape to "{shape}".' + ) + illuminant = reshape_sd(illuminant, shape) c_i = np.argmax(np.max(sensitivities.values, axis=0)) k = 1 / np.sum(illuminant.values * sensitivities.values[..., c_i]) @@ -482,24 +552,29 @@ def normalise_illuminant(illuminant, sensitivities): return illuminant * k -def training_data_sds_to_RGB(training_data, sensitivities, illuminant): +def training_data_sds_to_RGB( + training_data: MultiSpectralDistributions, + sensitivities: RGB_CameraSensitivities, + illuminant: SpectralDistribution, +) -> Tuple[NDArray, NDArray]: """ - Converts given training data to *RGB* tristimulus values using given + Convert given training data to *RGB* tristimulus values using given illuminant and given camera *RGB* spectral sensitivities. Parameters ---------- - training_data : MultiSpectralDistributions + training_data Training data multi-spectral distributions. - sensitivities : RGB_CameraSensitivities + sensitivities Camera *RGB* spectral sensitivities. - illuminant : SpectralDistribution + illuminant Illuminant spectral distribution. Returns ------- - ndarray - Training data *RGB* tristimulus values. + :class:`tuple` + Tuple of training data *RGB* tristimulus values and white balance + multipliers. Examples -------- @@ -511,58 +586,92 @@ def training_data_sds_to_RGB(training_data, sensitivities, illuminant): >>> illuminant = normalise_illuminant( ... SDS_ILLUMINANTS['D55'], sensitivities) >>> training_data = read_training_data_rawtoaces_v1() - >>> training_data_sds_to_RGB(training_data, sensitivities, illuminant)[:5] - ... # doctest: +ELLIPSIS + >>> RGB, RGB_w = training_data_sds_to_RGB( + ... training_data, sensitivities, illuminant) + >>> RGB[:5] # doctest: +ELLIPSIS array([[ 0.0207582..., 0.0196857..., 0.0213935...], [ 0.0895775..., 0.0891922..., 0.0891091...], [ 0.7810230..., 0.7801938..., 0.7764302...], [ 0.1995 ..., 0.1995 ..., 0.1995 ...], [ 0.5898478..., 0.5904015..., 0.5851076...]]) + >>> RGB_w # doctest: +ELLIPSIS + array([ 2.3414154..., 1. , 1.5163375...]) """ shape = sensitivities.shape if illuminant.shape != shape: - runtime_warning('Aligning "{0}" illuminant shape to "{1}".'.format( - illuminant.name, shape)) - illuminant = illuminant.copy().align(shape) + runtime_warning( + f'Aligning "{illuminant.name}" illuminant shape to "{shape}".' + ) + illuminant = reshape_sd(illuminant, shape) if training_data.shape != shape: - runtime_warning('Aligning "{0}" training data shape to "{1}".'.format( - training_data.name, shape)) - training_data = training_data.copy().align(shape) + runtime_warning( + f'Aligning "{training_data.name}" training data shape to "{shape}".' + ) + # pylint: disable=E1102 + training_data = reshape_msds(training_data, shape) RGB_w = white_balance_multipliers(sensitivities, illuminant) RGB = np.dot( np.transpose( - illuminant.values[..., np.newaxis] * training_data.values), - sensitivities.values) - RGB *= RGB_w - - return RGB + illuminant.values[..., np.newaxis] * training_data.values + ), + sensitivities.values, + ) + RGB *= RGB_w -def training_data_sds_to_XYZ(training_data, cmfs, illuminant): + return RGB, RGB_w + + +def training_data_sds_to_XYZ( + training_data: MultiSpectralDistributions, + cmfs: MultiSpectralDistributions, + illuminant: SpectralDistribution, + chromatic_adaptation_transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", +) -> NDArray: """ - Converts given training data to *CIE XYZ* tristimulus values using given + Convert given training data to *CIE XYZ* tristimulus values using given illuminant and given standard observer colour matching functions. Parameters ---------- - training_data : MultiSpectralDistributions + training_data Training data multi-spectral distributions. - cmfs : XYZ_ColourMatchingFunctions + cmfs Standard observer colour matching functions. - illuminant : SpectralDistribution + illuminant Illuminant spectral distribution. + chromatic_adaptation_transform + *Chromatic adaptation* transform, if *None* no chromatic adaptation is + performed. Returns ------- - ndarray + :class:`numpy.ndarray` Training data *CIE XYZ* tristimulus values. Examples -------- + >>> from colour import MSDS_CMFS >>> path = os.path.join( ... RESOURCES_DIRECTORY_RAWTOACES, ... 'CANON_EOS_5DMark_II_RGB_Sensitivities.csv') @@ -583,38 +692,46 @@ def training_data_sds_to_XYZ(training_data, cmfs, illuminant): shape = cmfs.shape if illuminant.shape != shape: - runtime_warning('Aligning "{0}" illuminant shape to "{1}".'.format( - illuminant.name, shape)) - illuminant = illuminant.copy().align(shape) + runtime_warning( + f'Aligning "{illuminant.name}" illuminant shape to "{shape}".' + ) + illuminant = reshape_sd(illuminant, shape) if training_data.shape != shape: - runtime_warning('Aligning "{0}" training data shape to "{1}".'.format( - training_data.name, shape)) - training_data = training_data.copy().align(shape) + runtime_warning( + f'Aligning "{training_data.name}" training data shape to "{shape}".' + ) + # pylint: disable=E1102 + training_data = reshape_msds(training_data, shape) XYZ = np.dot( np.transpose( - illuminant.values[..., np.newaxis] * training_data.values), - cmfs.values) + illuminant.values[..., np.newaxis] * training_data.values + ), + cmfs.values, + ) XYZ *= 1 / np.sum(cmfs.values[..., 1] * illuminant.values) XYZ_w = np.dot(np.transpose(cmfs.values), illuminant.values) XYZ_w *= 1 / XYZ_w[1] - M_CAT = matrix_chromatic_adaptation_VonKries( - XYZ_w, xy_to_XYZ(RGB_COLOURSPACE_ACES2065_1.whitepoint)) + if chromatic_adaptation_transform is not None: + M_CAT = matrix_chromatic_adaptation_VonKries( + XYZ_w, + xy_to_XYZ(RGB_COLOURSPACE_ACES2065_1.whitepoint), + chromatic_adaptation_transform, + ) - XYZ = vector_dot(M_CAT, XYZ) + XYZ = vector_dot(M_CAT, XYZ) return XYZ -def optimisation_factory_rawtoaces_v1(): +def optimisation_factory_rawtoaces_v1() -> Tuple[Callable, Callable]: """ - Factory that returns the objective function and *CIE XYZ* colourspace to - optimisation colourspace/colour model function according to *RAW to ACES* - v1. + Produce the objective function and *CIE XYZ* colourspace to optimisation + colourspace/colour model function according to *RAW to ACES* v1. The objective function returns the euclidean distance between the training data *RGB* tristimulus values and the training data *CIE XYZ* tristimulus @@ -622,13 +739,12 @@ def optimisation_factory_rawtoaces_v1(): Returns ------- - tuple + :class:`tuple` Objective function and *CIE XYZ* colourspace to *CIE L\\*a\\*b\\** colourspace function. Examples -------- - >>> # Doctests skip for Python 2.x compatibility. >>> optimisation_factory_rawtoaces_v1() # doctest: +SKIP (\ .objective_function at 0x...>, \ @@ -636,112 +752,136 @@ def optimisation_factory_rawtoaces_v1(): .XYZ_to_optimization_colour_model at 0x...>) """ - def objective_function(M, RGB, Lab): - """ - Objective function according to *RAW to ACES* v1. - """ + def objective_function( + M: ArrayLike, RGB: ArrayLike, Lab: ArrayLike + ) -> FloatingOrNDArray: + """Objective function according to *RAW to ACES* v1.""" M = np.reshape(M, [3, 3]) - XYZ_t = vector_dot(RGB_COLOURSPACE_ACES2065_1.matrix_RGB_to_XYZ, - vector_dot(M, RGB)) + XYZ_t = vector_dot( + RGB_COLOURSPACE_ACES2065_1.matrix_RGB_to_XYZ, vector_dot(M, RGB) + ) Lab_t = XYZ_to_Lab(XYZ_t, RGB_COLOURSPACE_ACES2065_1.whitepoint) - return np.linalg.norm(Lab_t - Lab) + return as_float(np.linalg.norm(Lab_t - Lab)) - def XYZ_to_optimization_colour_model(XYZ): - """ - *CIE XYZ* colourspace to *CIE L\\*a\\*b\\** colourspace function. - """ + def XYZ_to_optimization_colour_model(XYZ: ArrayLike) -> NDArray: + """*CIE XYZ* colourspace to *CIE L\\*a\\*b\\** colourspace function.""" return XYZ_to_Lab(XYZ, RGB_COLOURSPACE_ACES2065_1.whitepoint) return objective_function, XYZ_to_optimization_colour_model -def optimisation_factory_JzAzBz(): +def optimisation_factory_Jzazbz() -> Tuple[Callable, Callable]: """ - Factory that returns the objective function and *CIE XYZ* colourspace to - optimisation colourspace/colour model function based on the - :math:`J_zA_zB_z` colourspace. + Produce the objective function and *CIE XYZ* colourspace to optimisation + colourspace/colour model function based on the :math:`J_za_zb_z` + colourspace. The objective function returns the euclidean distance between the training data *RGB* tristimulus values and the training data *CIE XYZ* tristimulus - values** in the :math:`J_zA_zB_z` colourspace. + values** in the :math:`J_za_zb_z` colourspace. Returns ------- - tuple - Objective function and *CIE XYZ* colourspace to :math:`J_zA_zB_z` + :class:`tuple` + Objective function and *CIE XYZ* colourspace to :math:`J_za_zb_z` colourspace function. Examples -------- - >>> # Doctests skip for Python 2.x compatibility. - >>> optimisation_factory_JzAzBz() # doctest: +SKIP - (\ + >>> optimisation_factory_Jzazbz() # doctest: +SKIP + (\ .objective_function at 0x...>, \ -\ +\ .XYZ_to_optimization_colour_model at 0x...>) """ - def objective_function(M, RGB, Jab): - """ - :math:`J_zA_zB_z` colourspace based objective function. - """ + def objective_function( + M: ArrayLike, RGB: ArrayLike, Jab: ArrayLike + ) -> FloatingOrNDArray: + """:math:`J_za_zb_z` colourspace based objective function.""" M = np.reshape(M, [3, 3]) - XYZ_t = vector_dot(RGB_COLOURSPACE_ACES2065_1.matrix_RGB_to_XYZ, - vector_dot(M, RGB)) - Jab_t = XYZ_to_JzAzBz(XYZ_t) + XYZ_t = vector_dot( + RGB_COLOURSPACE_ACES2065_1.matrix_RGB_to_XYZ, vector_dot(M, RGB) + ) + Jab_t = XYZ_to_Jzazbz(XYZ_t) return np.sum(euclidean_distance(Jab, Jab_t)) - def XYZ_to_optimization_colour_model(XYZ): - """ - *CIE XYZ* colourspace to :math:`J_zA_zB_z` colourspace function. - """ + def XYZ_to_optimization_colour_model(XYZ: ArrayLike) -> NDArray: + """*CIE XYZ* colourspace to :math:`J_za_zb_z` colourspace function.""" - return XYZ_to_JzAzBz(XYZ) + return XYZ_to_Jzazbz(XYZ) return objective_function, XYZ_to_optimization_colour_model -def matrix_idt(sensitivities, - illuminant, - training_data=None, - cmfs=MSDS_CMFS['CIE 1931 2 Degree Standard Observer'].copy() - .align(SPECTRAL_SHAPE_RAWTOACES), - optimisation_factory=optimisation_factory_rawtoaces_v1, - optimisation_kwargs=None): +def matrix_idt( + sensitivities: RGB_CameraSensitivities, + illuminant: SpectralDistribution, + training_data: Optional[MultiSpectralDistributions] = None, + cmfs: Optional[MultiSpectralDistributions] = None, + optimisation_factory: Callable = optimisation_factory_rawtoaces_v1, + optimisation_kwargs: Optional[Dict] = None, + chromatic_adaptation_transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", + additional_data: Boolean = False, +) -> Union[Tuple[NDArray, NDArray, NDArray, NDArray], Tuple[NDArray, NDArray]]: """ - Computes an *Input Device Transform* (IDT) matrix for given camera *RGB* + Compute an *Input Device Transform* (IDT) matrix for given camera *RGB* spectral sensitivities, illuminant, training data, standard observer colour matching functions and optimization settings according to *RAW to ACES* v1 and *P-2013-001* procedures. Parameters ---------- - sensitivities : RGB_CameraSensitivities + sensitivities Camera *RGB* spectral sensitivities. - illuminant : SpectralDistribution + illuminant Illuminant spectral distribution. - training_data : MultiSpectralDistributions, optional + training_data Training data multi-spectral distributions, defaults to using the *RAW to ACES* v1 190 patches. - cmfs : XYZ_ColourMatchingFunctions - Standard observer colour matching functions. - optimisation_factory : callable, optional + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + optimisation_factory Callable producing the objective function and the *CIE XYZ* to optimisation colour model function. - optimisation_kwargs : dict_like, optional + optimisation_kwargs Parameters for :func:`scipy.optimize.minimize` definition. + chromatic_adaptation_transform + *Chromatic adaptation* transform, if *None* no chromatic adaptation is + performed. + additional_data + If *True*, the *XYZ* and *RGB* tristimulus values are also returned. Returns ------- - ndarray - *Input Device Transform* (IDT) matrix. + :class:`tuple` + Tuple of *Input Device Transform* (IDT) matrix and white balance + multipliers or tuple of *Input Device Transform* (IDT) matrix, white + balance multipliers, *XYZ* and *RGB* tristimulus values. References ---------- @@ -759,62 +899,146 @@ def matrix_idt(sensitivities, >>> sensitivities = sds_and_msds_to_msds( ... read_sds_from_csv_file(path).values()) >>> illuminant = SDS_ILLUMINANTS['D55'] - >>> np.around( - ... matrix_idt(sensitivities, illuminant), 3) + >>> M, RGB_w = matrix_idt(sensitivities, illuminant) + >>> np.around(M, 3) array([[ 0.85 , -0.016, 0.151], [ 0.051, 1.126, -0.185], [ 0.02 , -0.194, 1.162]]) + >>> RGB_w # doctest: +ELLIPSIS + array([ 2.3414154..., 1. , 1.5163375...]) The *RAW to ACES* v1 matrix for the same camera and optimized by - `Ceres Solver `__ is as follows: + `Ceres Solver `__ is as follows:: - 0.864994 -0.026302 0.161308 - 0.056527 1.122997 -0.179524 - 0.023683 -0.202547 1.178864 + 0.864994 -0.026302 0.161308 + 0.056527 1.122997 -0.179524 + 0.023683 -0.202547 1.178864 - >>> np.around(matrix_idt( + >>> M, RGB_w = matrix_idt( ... sensitivities, illuminant, - ... optimisation_factory=optimisation_factory_JzAzBz), 3) + ... optimisation_factory=optimisation_factory_Jzazbz) + >>> np.around(M, 3) array([[ 0.848, -0.016, 0.158], [ 0.053, 1.114, -0.175], [ 0.023, -0.225, 1.196]]) + >>> RGB_w # doctest: +ELLIPSIS + array([ 2.3414154..., 1. , 1.5163375...]) """ - if training_data is None: - training_data = read_training_data_rawtoaces_v1() + training_data = optional(training_data, read_training_data_rawtoaces_v1()) + + cmfs, illuminant = handle_spectral_arguments( + cmfs, illuminant, shape_default=SPECTRAL_SHAPE_RAWTOACES + ) shape = cmfs.shape if sensitivities.shape != shape: - runtime_warning('Aligning "{0}" sensitivities shape to "{1}".'.format( - sensitivities.name, shape)) - sensitivities = sensitivities.copy().align(shape) - - if illuminant.shape != shape: - runtime_warning('Aligning "{0}" illuminant shape to "{1}".'.format( - illuminant.name, shape)) - illuminant = illuminant.copy().align(shape) + runtime_warning( + f'Aligning "{sensitivities.name}" sensitivities shape to "{shape}".' + ) + # pylint: disable=E1102 + sensitivities = reshape_msds(sensitivities, shape) # type: ignore[assignment] if training_data.shape != shape: - runtime_warning('Aligning "{0}" training data shape to "{1}".'.format( - training_data.name, shape)) - training_data = training_data.copy().align(shape) + runtime_warning( + f'Aligning "{training_data.name}" training data shape to "{shape}".' + ) + # pylint: disable=E1102 + training_data = reshape_msds(training_data, shape) illuminant = normalise_illuminant(illuminant, sensitivities) - RGB = training_data_sds_to_RGB(training_data, sensitivities, illuminant) - XYZ = training_data_sds_to_XYZ(training_data, cmfs, illuminant) + RGB, RGB_w = training_data_sds_to_RGB( + training_data, sensitivities, illuminant + ) + + XYZ = training_data_sds_to_XYZ( + training_data, cmfs, illuminant, chromatic_adaptation_transform + ) - objective_function, XYZ_to_optimization_colour_model = ( - optimisation_factory()) + ( + objective_function, + XYZ_to_optimization_colour_model, + ) = optimisation_factory() optimisation_settings = { - 'method': 'BFGS', - 'jac': '2-point', + "method": "BFGS", + "jac": "2-point", } if optimisation_kwargs is not None: optimisation_settings.update(optimisation_kwargs) - M = minimize(objective_function, np.ravel(np.identity(3)), - (RGB, XYZ_to_optimization_colour_model(XYZ)), - **optimisation_settings).x.reshape([3, 3]) + M = minimize( + objective_function, + np.ravel(np.identity(3)), + (RGB, XYZ_to_optimization_colour_model(XYZ)), + **optimisation_settings, + ).x.reshape([3, 3]) + + if additional_data: + return M, RGB_w, XYZ, RGB + else: + return M, RGB_w + + +def camera_RGB_to_ACES2065_1( + RGB: ArrayLike, + B: ArrayLike, + b: ArrayLike, + k: ArrayLike = np.ones(3), + clip: Boolean = False, +) -> NDArray: + """ + Convert given camera *RGB* colourspace array to *ACES2065-1* colourspace + using the *Input Device Transform* (IDT) matrix :math:`B`, the white + balance multipliers :math:`b` and the exposure factor :math:`k` according + to *P-2013-001* procedure. + + Parameters + ---------- + RGB + Camera *RGB* colourspace array. + B + *Input Device Transform* (IDT) matrix :math:`B`. + b + White balance multipliers :math:`b`. + k + Exposure factor :math:`k` that results in a nominally "18% gray" object + in the scene producing ACES values [0.18, 0.18, 0.18]. + clip + Whether to clip the white balanced camera *RGB* colourspace array + between :math:`-\\infty` and 1. The intent is to keep sensor saturated + values achromatic after white balancing. + + Returns + ------- + :class:`numpy.ndarray` + *ACES2065-1* colourspace relative exposure values array. + + References + ---------- + :cite:`TheAcademyofMotionPictureArtsandSciences2015c` + + Examples + -------- + >>> path = os.path.join( + ... RESOURCES_DIRECTORY_RAWTOACES, + ... 'CANON_EOS_5DMark_II_RGB_Sensitivities.csv') + >>> sensitivities = sds_and_msds_to_msds( + ... read_sds_from_csv_file(path).values()) + >>> illuminant = SDS_ILLUMINANTS['D55'] + >>> B, b = matrix_idt(sensitivities, illuminant) + >>> camera_RGB_to_ACES2065_1(np.array([0.1, 0.2, 0.3]), B, b) + ... # doctest: +ELLIPSIS + array([ 0.2646811..., 0.1528898..., 0.4944335...]) + """ + + RGB = as_float_array(RGB) + B = as_float_array(B) + b = as_float_array(b) + k = as_float_array(k) + + RGB_r = b * RGB / np.min(b) + + RGB_r = np.clip(RGB_r, -np.inf, 1) if clip else RGB_r - return M + return k * vector_dot(B, RGB_r) diff --git a/colour/characterisation/cameras.py b/colour/characterisation/cameras.py index 152739aa3f..898ce05664 100644 --- a/colour/characterisation/cameras.py +++ b/colour/characterisation/cameras.py @@ -1,70 +1,104 @@ -# -*- coding: utf-8 -*- """ Cameras Sensitivities ===================== -Defines spectral distributions classes for the datasets from -:mod:`colour.characterisation.datasets.cameras` module: +Defines the spectral distributions classes for the datasets from +the :mod:`colour.characterisation.datasets.cameras` module: - :class:`colour.characterisation.RGB_CameraSensitivities`: Implements support for a camera *RGB* sensitivities. """ -from __future__ import division, unicode_literals +from __future__ import annotations -from colour.colorimetry import MultiSpectralDistributions +from colour.colorimetry import ( + MultiSpectralDistributions, + SpectralDistribution, + SpectralShape, +) +from colour.continuous import MultiSignals, Signal +from colour.hints import ArrayLike, Any, Optional, Sequence, Union +from colour.utilities import is_pandas_installed -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +if is_pandas_installed(): + from pandas import DataFrame, Series +else: # pragma: no cover + from unittest import mock -__all__ = ['RGB_CameraSensitivities'] + DataFrame = mock.MagicMock() + Series = mock.MagicMock() + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "RGB_CameraSensitivities", +] class RGB_CameraSensitivities(MultiSpectralDistributions): """ - Implements support for a camera *RGB* sensitivities. + Implement support for a camera *RGB* sensitivities. Parameters ---------- - data : Series or Dataframe or Signal or MultiSignals or \ -MultiSpectralDistributions or array_like or dict_like, optional + data Data to be stored in the multi-spectral distributions. - domain : array_like, optional + domain Values to initialise the multiple :class:`colour.SpectralDistribution` class instances :attr:`colour.continuous.Signal.wavelengths` attribute with. If both ``data`` and ``domain`` arguments are defined, the latter will be used to initialise the - :attr:`colour.continuous.Signal.wavelengths` attribute. - labels : array_like, optional + :attr:`colour.continuous.Signal.wavelengths` property. + labels Names to use for the :class:`colour.SpectralDistribution` class instances. Other Parameters ---------------- - name : unicode, optional - Multi-spectral distributions name. - interpolator : object, optional - Interpolator class type to use as interpolating function for the - :class:`colour.SpectralDistribution` class instances. - interpolator_kwargs : dict_like, optional - Arguments to use when instantiating the interpolating function of the - :class:`colour.SpectralDistribution` class instances. - extrapolator : object, optional + extrapolator Extrapolator class type to use as extrapolating function for the :class:`colour.SpectralDistribution` class instances. - extrapolator_kwargs : dict_like, optional + extrapolator_kwargs Arguments to use when instantiating the extrapolating function of the :class:`colour.SpectralDistribution` class instances. - strict_labels : array_like, optional + interpolator + Interpolator class type to use as interpolating function for the + :class:`colour.SpectralDistribution` class instances. + interpolator_kwargs + Arguments to use when instantiating the interpolating function + of the :class:`colour.SpectralDistribution` class instances. + name + Multi-spectral distributions name. + strict_labels Multi-spectral distributions labels for figures, default to - :attr:`colour.characterisation.RGB_CameraSensitivities.labels` - attribute value. + :attr:`colour.colorimetry.RGB_CameraSensitivities.labels` property + value. """ - def __init__(self, data=None, domain=None, labels=None, **kwargs): - super(RGB_CameraSensitivities, self).__init__( - data, domain, labels=('red', 'green', 'blue'), **kwargs) + def __init__( + self, + data: Optional[ + Union[ + ArrayLike, + DataFrame, + dict, + MultiSignals, + MultiSpectralDistributions, + Sequence, + Series, + Signal, + SpectralDistribution, + ] + ] = None, + domain: Optional[Union[ArrayLike, SpectralShape]] = None, + labels: Optional[Sequence] = None, + **kwargs: Any, + ): + super().__init__( + data, domain, labels=("red", "green", "blue"), **kwargs + ) diff --git a/colour/characterisation/correction.py b/colour/characterisation/correction.py index 5605b9a84d..8f1762d0da 100644 --- a/colour/characterisation/correction.py +++ b/colour/characterisation/correction.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Colour Correction ================= @@ -57,49 +56,76 @@ May 2, 2018, from https://en.wikipedia.org/wiki/Vandermonde_matrix """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.algebra import least_square_mapping_MoorePenrose, spow -from colour.utilities import (CaseInsensitiveMapping, as_float_array, as_int, - closest, filter_kwargs, ones, tsplit, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import ( + ArrayLike, + Any, + Boolean, + Integer, + Literal, + NDArray, + Union, +) +from colour.utilities import ( + CaseInsensitiveMapping, + as_float_array, + as_int, + closest, + filter_kwargs, + ones, + tsplit, + tstack, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'matrix_augmented_Cheung2004', 'polynomial_expansion_Finlayson2015', - 'polynomial_expansion_Vandermonde', 'POLYNOMIAL_EXPANSION_METHODS', - 'polynomial_expansion', 'matrix_colour_correction_Cheung2004', - 'matrix_colour_correction_Finlayson2015', - 'matrix_colour_correction_Vandermonde', 'MATRIX_COLOUR_CORRECTION_METHODS', - 'matrix_colour_correction', 'colour_correction_Cheung2004', - 'colour_correction_Finlayson2015', 'colour_correction_Vandermonde', - 'COLOUR_CORRECTION_METHODS', 'colour_correction' + "matrix_augmented_Cheung2004", + "polynomial_expansion_Finlayson2015", + "polynomial_expansion_Vandermonde", + "POLYNOMIAL_EXPANSION_METHODS", + "polynomial_expansion", + "matrix_colour_correction_Cheung2004", + "matrix_colour_correction_Finlayson2015", + "matrix_colour_correction_Vandermonde", + "MATRIX_COLOUR_CORRECTION_METHODS", + "matrix_colour_correction", + "colour_correction_Cheung2004", + "colour_correction_Finlayson2015", + "colour_correction_Vandermonde", + "COLOUR_CORRECTION_METHODS", + "colour_correction", ] -def matrix_augmented_Cheung2004(RGB, terms=3): +def matrix_augmented_Cheung2004( + RGB: ArrayLike, + terms: Literal[3, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22] = 3, +) -> NDArray: """ - Performs polynomial expansion of given *RGB* colourspace array using + Perform polynomial expansion of given *RGB* colourspace array using *Cheung et al. (2004)* method. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array to expand. - terms : int, optional - Number of terms of the expanded polynomial, must be one of - *[3, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22]*. + terms + Number of terms of the expanded polynomial. Returns ------- - ndarray + :class:`numpy.ndarray` Expanded *RGB* colourspace array. Notes @@ -118,222 +144,249 @@ def matrix_augmented_Cheung2004(RGB, terms=3): array([ 0.1722481..., 0.0917066..., 0.0641693..., 0.0010136..., 1...]) """ + RGB = as_float_array(RGB) + R, G, B = tsplit(RGB) tail = ones(R.shape) existing_terms = np.array([3, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22]) closest_terms = as_int(closest(existing_terms, terms)) if closest_terms != terms: - raise ValueError('"Cheung et al. (2004)" method does not define ' - 'an augmented matrix with {0} terms, ' - 'closest augmented matrix has {1} terms!'.format( - terms, closest_terms)) + raise ValueError( + f'"Cheung et al. (2004)" method does not define an augmented ' + f"matrix with {terms} terms, closest augmented matrix has " + f"{closest_terms} terms!" + ) if terms == 3: return RGB elif terms == 5: - return tstack([ - R, - G, - B, - R * G * B, - tail, - ]) + return tstack( + [ + R, + G, + B, + R * G * B, + tail, + ] + ) elif terms == 7: - return tstack([ - R, - G, - B, - R * G, - R * B, - G * B, - tail, - ]) + return tstack( + [ + R, + G, + B, + R * G, + R * B, + G * B, + tail, + ] + ) elif terms == 8: - return tstack([ - R, - G, - B, - R * G, - R * B, - G * B, - R * G * B, - tail, - ]) + return tstack( + [ + R, + G, + B, + R * G, + R * B, + G * B, + R * G * B, + tail, + ] + ) elif terms == 10: - return tstack([ - R, - G, - B, - R * G, - R * B, - G * B, - R ** 2, - G ** 2, - B ** 2, - tail, - ]) + return tstack( + [ + R, + G, + B, + R * G, + R * B, + G * B, + R**2, + G**2, + B**2, + tail, + ] + ) elif terms == 11: - return tstack([ - R, - G, - B, - R * G, - R * B, - G * B, - R ** 2, - G ** 2, - B ** 2, - R * G * B, - tail, - ]) + return tstack( + [ + R, + G, + B, + R * G, + R * B, + G * B, + R**2, + G**2, + B**2, + R * G * B, + tail, + ] + ) elif terms == 14: - return tstack([ - R, - G, - B, - R * G, - R * B, - G * B, - R ** 2, - G ** 2, - B ** 2, - R * G * B, - R ** 3, - G ** 3, - B ** 3, - tail, - ]) + return tstack( + [ + R, + G, + B, + R * G, + R * B, + G * B, + R**2, + G**2, + B**2, + R * G * B, + R**3, + G**3, + B**3, + tail, + ] + ) elif terms == 16: - return tstack([ - R, - G, - B, - R * G, - R * B, - G * B, - R ** 2, - G ** 2, - B ** 2, - R * G * B, - R ** 2 * G, - G ** 2 * B, - B ** 2 * R, - R ** 3, - G ** 3, - B ** 3, - ]) + return tstack( + [ + R, + G, + B, + R * G, + R * B, + G * B, + R**2, + G**2, + B**2, + R * G * B, + R**2 * G, + G**2 * B, + B**2 * R, + R**3, + G**3, + B**3, + ] + ) elif terms == 17: - return tstack([ - R, - G, - B, - R * G, - R * B, - G * B, - R ** 2, - G ** 2, - B ** 2, - R * G * B, - R ** 2 * G, - G ** 2 * B, - B ** 2 * R, - R ** 3, - G ** 3, - B ** 3, - tail, - ]) + return tstack( + [ + R, + G, + B, + R * G, + R * B, + G * B, + R**2, + G**2, + B**2, + R * G * B, + R**2 * G, + G**2 * B, + B**2 * R, + R**3, + G**3, + B**3, + tail, + ] + ) elif terms == 19: - return tstack([ - R, - G, - B, - R * G, - R * B, - G * B, - R ** 2, - G ** 2, - B ** 2, - R * G * B, - R ** 2 * G, - G ** 2 * B, - B ** 2 * R, - R ** 2 * B, - G ** 2 * R, - B ** 2 * G, - R ** 3, - G ** 3, - B ** 3, - ]) + return tstack( + [ + R, + G, + B, + R * G, + R * B, + G * B, + R**2, + G**2, + B**2, + R * G * B, + R**2 * G, + G**2 * B, + B**2 * R, + R**2 * B, + G**2 * R, + B**2 * G, + R**3, + G**3, + B**3, + ] + ) elif terms == 20: - return tstack([ - R, - G, - B, - R * G, - R * B, - G * B, - R ** 2, - G ** 2, - B ** 2, - R * G * B, - R ** 2 * G, - G ** 2 * B, - B ** 2 * R, - R ** 2 * B, - G ** 2 * R, - B ** 2 * G, - R ** 3, - G ** 3, - B ** 3, - tail, - ]) + return tstack( + [ + R, + G, + B, + R * G, + R * B, + G * B, + R**2, + G**2, + B**2, + R * G * B, + R**2 * G, + G**2 * B, + B**2 * R, + R**2 * B, + G**2 * R, + B**2 * G, + R**3, + G**3, + B**3, + tail, + ] + ) elif terms == 22: - return tstack([ - R, - G, - B, - R * G, - R * B, - G * B, - R ** 2, - G ** 2, - B ** 2, - R * G * B, - R ** 2 * G, - G ** 2 * B, - B ** 2 * R, - R ** 2 * B, - G ** 2 * R, - B ** 2 * G, - R ** 3, - G ** 3, - B ** 3, - R ** 2 * G * B, - R * G ** 2 * B, - R * G * B ** 2, - ]) - - -def polynomial_expansion_Finlayson2015(RGB, - degree=1, - root_polynomial_expansion=True): + return tstack( + [ + R, + G, + B, + R * G, + R * B, + G * B, + R**2, + G**2, + B**2, + R * G * B, + R**2 * G, + G**2 * B, + B**2 * R, + R**2 * B, + G**2 * R, + B**2 * G, + R**3, + G**3, + B**3, + R**2 * G * B, + R * G**2 * B, + R * G * B**2, + ] + ) + + +def polynomial_expansion_Finlayson2015( + RGB: ArrayLike, + degree: Literal[1, 2, 3, 4] = 1, + root_polynomial_expansion: Boolean = True, +) -> NDArray: """ - Performs polynomial expansion of given *RGB* colourspace array using + Perform polynomial expansion of given *RGB* colourspace array using *Finlayson et al. (2015)* method. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array to expand. - degree : int, optional + degree Expanded polynomial degree. - root_polynomial_expansion : bool + root_polynomial_expansion Whether to use the root-polynomials set for the expansion. Returns ------- - ndarray + :class:`numpy.ndarray` Expanded *RGB* colourspace array. References @@ -349,161 +402,178 @@ def polynomial_expansion_Finlayson2015(RGB, 0.1051335...]) """ + RGB = as_float_array(RGB) + R, G, B = tsplit(RGB) # TODO: Generalise polynomial expansion. existing_degrees = np.array([1, 2, 3, 4]) closest_degree = as_int(closest(existing_degrees, degree)) if closest_degree != degree: - raise ValueError('"Finlayson et al. (2015)" method does not define ' - 'a polynomial expansion for {0} degree, ' - 'closest polynomial expansion is {1} degree!'.format( - degree, closest_degree)) + raise ValueError( + f'"Finlayson et al. (2015)" method does not define a polynomial ' + f"expansion for {degree} degree, closest polynomial expansion is " + f"{closest_degree} degree!" + ) if degree == 1: return RGB elif degree == 2: if root_polynomial_expansion: - return tstack([ - R, - G, - B, - spow(R * G, 1 / 2), - spow(G * B, 1 / 2), - spow(R * B, 1 / 2), - ]) + return tstack( + [ + R, + G, + B, + spow(R * G, 1 / 2), + spow(G * B, 1 / 2), + spow(R * B, 1 / 2), + ] + ) else: - return tstack([ - R, - G, - B, - R ** 2, - G ** 2, - B ** 2, - R * G, - G * B, - R * B, - ]) + return tstack( + [ + R, + G, + B, + R**2, + G**2, + B**2, + R * G, + G * B, + R * B, + ] + ) elif degree == 3: if root_polynomial_expansion: - return tstack([ - R, - G, - B, - spow(R * G, 1 / 2), - spow(G * B, 1 / 2), - spow(R * B, 1 / 2), - spow(R * G ** 2, 1 / 3), - spow(G * B ** 2, 1 / 3), - spow(R * B ** 2, 1 / 3), - spow(G * R ** 2, 1 / 3), - spow(B * G ** 2, 1 / 3), - spow(B * R ** 2, 1 / 3), - spow(R * G * B, 1 / 3), - ]) + return tstack( + [ + R, + G, + B, + spow(R * G, 1 / 2), + spow(G * B, 1 / 2), + spow(R * B, 1 / 2), + spow(R * G**2, 1 / 3), + spow(G * B**2, 1 / 3), + spow(R * B**2, 1 / 3), + spow(G * R**2, 1 / 3), + spow(B * G**2, 1 / 3), + spow(B * R**2, 1 / 3), + spow(R * G * B, 1 / 3), + ] + ) else: - return tstack([ - R, - G, - B, - R ** 2, - G ** 2, - B ** 2, - R * G, - G * B, - R * B, - R ** 3, - G ** 3, - B ** 3, - R * G ** 2, - G * B ** 2, - R * B ** 2, - G * R ** 2, - B * G ** 2, - B * R ** 2, - R * G * B, - ]) + return tstack( + [ + R, + G, + B, + R**2, + G**2, + B**2, + R * G, + G * B, + R * B, + R**3, + G**3, + B**3, + R * G**2, + G * B**2, + R * B**2, + G * R**2, + B * G**2, + B * R**2, + R * G * B, + ] + ) elif degree == 4: if root_polynomial_expansion: - return tstack([ - R, - G, - B, - spow(R * G, 1 / 2), - spow(G * B, 1 / 2), - spow(R * B, 1 / 2), - spow(R * G ** 2, 1 / 3), - spow(G * B ** 2, 1 / 3), - spow(R * B ** 2, 1 / 3), - spow(G * R ** 2, 1 / 3), - spow(B * G ** 2, 1 / 3), - spow(B * R ** 2, 1 / 3), - spow(R * G * B, 1 / 3), - spow(R ** 3 * G, 1 / 4), - spow(R ** 3 * B, 1 / 4), - spow(G ** 3 * R, 1 / 4), - spow(G ** 3 * B, 1 / 4), - spow(B ** 3 * R, 1 / 4), - spow(B ** 3 * G, 1 / 4), - spow(R ** 2 * G * B, 1 / 4), - spow(G ** 2 * R * B, 1 / 4), - spow(B ** 2 * R * G, 1 / 4), - ]) + return tstack( + [ + R, + G, + B, + spow(R * G, 1 / 2), + spow(G * B, 1 / 2), + spow(R * B, 1 / 2), + spow(R * G**2, 1 / 3), + spow(G * B**2, 1 / 3), + spow(R * B**2, 1 / 3), + spow(G * R**2, 1 / 3), + spow(B * G**2, 1 / 3), + spow(B * R**2, 1 / 3), + spow(R * G * B, 1 / 3), + spow(R**3 * G, 1 / 4), + spow(R**3 * B, 1 / 4), + spow(G**3 * R, 1 / 4), + spow(G**3 * B, 1 / 4), + spow(B**3 * R, 1 / 4), + spow(B**3 * G, 1 / 4), + spow(R**2 * G * B, 1 / 4), + spow(G**2 * R * B, 1 / 4), + spow(B**2 * R * G, 1 / 4), + ] + ) else: - return tstack([ - R, - G, - B, - R ** 2, - G ** 2, - B ** 2, - R * G, - G * B, - R * B, - R ** 3, - G ** 3, - B ** 3, - R * G ** 2, - G * B ** 2, - R * B ** 2, - G * R ** 2, - B * G ** 2, - B * R ** 2, - R * G * B, - R ** 4, - G ** 4, - B ** 4, - R ** 3 * G, - R ** 3 * B, - G ** 3 * R, - G ** 3 * B, - B ** 3 * R, - B ** 3 * G, - R ** 2 * G ** 2, - G ** 2 * B ** 2, - R ** 2 * B ** 2, - R ** 2 * G * B, - G ** 2 * R * B, - B ** 2 * R * G, - ]) - - -def polynomial_expansion_Vandermonde(a, degree=1): + return tstack( + [ + R, + G, + B, + R**2, + G**2, + B**2, + R * G, + G * B, + R * B, + R**3, + G**3, + B**3, + R * G**2, + G * B**2, + R * B**2, + G * R**2, + B * G**2, + B * R**2, + R * G * B, + R**4, + G**4, + B**4, + R**3 * G, + R**3 * B, + G**3 * R, + G**3 * B, + B**3 * R, + B**3 * G, + R**2 * G**2, + G**2 * B**2, + R**2 * B**2, + R**2 * G * B, + G**2 * R * B, + B**2 * R * G, + ] + ) + + +def polynomial_expansion_Vandermonde( + a: ArrayLike, degree: Integer = 1 +) -> NDArray: """ - Performs polynomial expansion of given :math:`a` array using *Vandermonde* + Perform polynomial expansion of given :math:`a` array using *Vandermonde* method. Parameters ---------- - a : array_like + a :math:`a` array to expand. - degree : int, optional + degree Expanded polynomial degree. Returns ------- - ndarray + :class:`numpy.ndarray` Expanded :math:`a` array. References @@ -519,17 +589,19 @@ def polynomial_expansion_Vandermonde(a, degree=1): a = as_float_array(a) - a_e = np.transpose(np.vander(np.ravel(a), degree + 1)) - a_e = np.hstack(a_e.reshape(a_e.shape[0], -1, 3)) + a_e = np.transpose(np.vander(np.ravel(a), int(degree) + 1)) + a_e = np.hstack(list(np.reshape(a_e, (a_e.shape[0], -1, 3)))) - return np.squeeze(a_e[:, 0:a_e.shape[-1] - a.shape[-1] + 1]) + return np.squeeze(a_e[:, 0 : a_e.shape[-1] - a.shape[-1] + 1]) -POLYNOMIAL_EXPANSION_METHODS = CaseInsensitiveMapping({ - 'Cheung 2004': matrix_augmented_Cheung2004, - 'Finlayson 2015': polynomial_expansion_Finlayson2015, - 'Vandermonde': polynomial_expansion_Vandermonde, -}) +POLYNOMIAL_EXPANSION_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Cheung 2004": matrix_augmented_Cheung2004, + "Finlayson 2015": polynomial_expansion_Finlayson2015, + "Vandermonde": polynomial_expansion_Vandermonde, + } +) POLYNOMIAL_EXPANSION_METHODS.__doc__ = """ Supported polynomial expansion methods. @@ -537,43 +609,44 @@ def polynomial_expansion_Vandermonde(a, degree=1): ---------- :cite:`Cheung2004`, :cite:`Finlayson2015`, :cite:`Westland2004`, :cite:`Wikipedia2003e` - -POLYNOMIAL_EXPANSION_METHODS : CaseInsensitiveMapping - **{'Cheung 2004', 'Finlayson 2015', 'Vandermonde'}** """ -def polynomial_expansion(a, method='Cheung 2004', **kwargs): +def polynomial_expansion( + a: ArrayLike, + method: Union[ + Literal["Cheung 2004", "Finlayson 2015", "Vandermonde"], str + ] = "Cheung 2004", + **kwargs: Any, +) -> NDArray: """ - Performs polynomial expansion of given :math:`a` array. + Perform polynomial expansion of given :math:`a` array. Parameters ---------- - a : array_like, (3, n) + a :math:`a` array to expand. - method : unicode, optional - **{'Cheung 2004', 'Finlayson 2015', 'Vandermonde'}**, + method Computation method. Other Parameters ---------------- - degree : int + degree {:func:`colour.characterisation.polynomial_expansion_Finlayson2015`, :func:`colour.characterisation.polynomial_expansion_Vandermonde`}, Expanded polynomial degree, must be one of *[1, 2, 3, 4]* for :func:`colour.characterisation.polynomial_expansion_Finlayson2015` definition. - terms : int - {:func:`colour.characterisation.matrix_augmented_Cheung2004`}, - Number of terms of the expanded polynomial, must be one of - *[3, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22]*. - root_polynomial_expansion : bool + root_polynomial_expansion {:func:`colour.characterisation.polynomial_expansion_Finlayson2015`}, Whether to use the root-polynomials set for the expansion. + terms + {:func:`colour.characterisation.matrix_augmented_Cheung2004`}, + Number of terms of the expanded polynomial. Returns ------- - ndarray, (3, n) + :class:`numpy.ndarray` Expanded :math:`a` array. References @@ -590,29 +663,34 @@ def polynomial_expansion(a, method='Cheung 2004', **kwargs): array([ 0.1722481..., 0.0917066..., 0.0641693..., 0.0010136..., 1...]) """ + method = validate_method(method, POLYNOMIAL_EXPANSION_METHODS) + function = POLYNOMIAL_EXPANSION_METHODS[method] return function(a, **filter_kwargs(function, **kwargs)) -def matrix_colour_correction_Cheung2004(M_T, M_R, terms=3): +def matrix_colour_correction_Cheung2004( + M_T: ArrayLike, + M_R: ArrayLike, + terms: Literal[3, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22] = 3, +) -> NDArray: """ - Computes a colour correction matrix from given :math:`M_T` colour array to + Compute a colour correction matrix from given :math:`M_T` colour array to :math:`M_R` colour array using *Cheung et al. (2004)* method. Parameters ---------- - M_T : array_like, (3, n) + M_T Test array :math:`M_T` to fit onto array :math:`M_R`. - M_R : array_like, (3, n) + M_R Reference array the array :math:`M_T` will be colour fitted against. - terms : int, optional - Number of terms of the expanded polynomial, must be one of - *[3, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22]*. + terms + Number of terms of the expanded polynomial. Returns ------- - ndarray, (3, n) + :class:`numpy.ndarray` Colour correction matrix. References @@ -631,31 +709,34 @@ def matrix_colour_correction_Cheung2004(M_T, M_R, terms=3): """ return least_square_mapping_MoorePenrose( - matrix_augmented_Cheung2004(M_T, terms), M_R) + matrix_augmented_Cheung2004(M_T, terms), M_R + ) -def matrix_colour_correction_Finlayson2015(M_T, - M_R, - degree=1, - root_polynomial_expansion=True): +def matrix_colour_correction_Finlayson2015( + M_T: ArrayLike, + M_R: ArrayLike, + degree: Literal[1, 2, 3, 4] = 1, + root_polynomial_expansion: Boolean = True, +) -> NDArray: """ - Computes a colour correction matrix from given :math:`M_T` colour array to + Compute a colour correction matrix from given :math:`M_T` colour array to :math:`M_R` colour array using *Finlayson et al. (2015)* method. Parameters ---------- - M_T : array_like, (n, 3) + M_T Test array :math:`M_T` to fit onto array :math:`M_R`. - M_R : array_like, (n, 3) + M_R Reference array the array :math:`M_T` will be colour fitted against. - degree : int, optional + degree Expanded polynomial degree. - root_polynomial_expansion : bool + root_polynomial_expansion Whether to use the root-polynomials set for the expansion. Returns ------- - ndarray, (n, 3) + :class:`numpy.ndarray` Colour correction matrix. References @@ -674,27 +755,32 @@ def matrix_colour_correction_Finlayson2015(M_T, """ return least_square_mapping_MoorePenrose( - polynomial_expansion_Finlayson2015(M_T, degree, - root_polynomial_expansion), M_R) + polynomial_expansion_Finlayson2015( + M_T, degree, root_polynomial_expansion + ), + M_R, + ) -def matrix_colour_correction_Vandermonde(M_T, M_R, degree=1): +def matrix_colour_correction_Vandermonde( + M_T: ArrayLike, M_R: ArrayLike, degree: Integer = 1 +) -> NDArray: """ - Computes a colour correction matrix from given :math:`M_T` colour array to + Compute a colour correction matrix from given :math:`M_T` colour array to :math:`M_R` colour array using *Vandermonde* method. Parameters ---------- - M_T : array_like, (n, 3) + M_T Test array :math:`M_T` to fit onto array :math:`M_R`. - M_R : array_like, (n, 3) + M_R Reference array the array :math:`M_T` will be colour fitted against. - degree : int, optional + degree Expanded polynomial degree. Returns ------- - ndarray, (n, 3) + :class:`numpy.ndarray` Colour correction matrix. References @@ -713,14 +799,19 @@ def matrix_colour_correction_Vandermonde(M_T, M_R, degree=1): """ return least_square_mapping_MoorePenrose( - polynomial_expansion_Vandermonde(M_T, degree), M_R) - - -MATRIX_COLOUR_CORRECTION_METHODS = CaseInsensitiveMapping({ - 'Cheung 2004': matrix_colour_correction_Cheung2004, - 'Finlayson 2015': matrix_colour_correction_Finlayson2015, - 'Vandermonde': matrix_colour_correction_Vandermonde, -}) + polynomial_expansion_Vandermonde(M_T, degree), M_R + ) + + +MATRIX_COLOUR_CORRECTION_METHODS: CaseInsensitiveMapping = ( + CaseInsensitiveMapping( + { + "Cheung 2004": matrix_colour_correction_Cheung2004, + "Finlayson 2015": matrix_colour_correction_Finlayson2015, + "Vandermonde": matrix_colour_correction_Vandermonde, + } + ) +) MATRIX_COLOUR_CORRECTION_METHODS.__doc__ = """ Supported colour correction matrix methods. @@ -728,15 +819,19 @@ def matrix_colour_correction_Vandermonde(M_T, M_R, degree=1): ---------- :cite:`Cheung2004`, :cite:`Finlayson2015`, :cite:`Westland2004`, :cite:`Wikipedia2003e` - -POLYNOMIAL_EXPANSION_METHODS : CaseInsensitiveMapping - **{'Cheung 2004', 'Finlayson 2015', 'Vandermonde'}** """ -def matrix_colour_correction(M_T, M_R, method='Cheung 2004', **kwargs): +def matrix_colour_correction( + M_T: ArrayLike, + M_R: ArrayLike, + method: Union[ + Literal["Cheung 2004", "Finlayson 2015", "Vandermonde"], str + ] = "Cheung 2004", + **kwargs: Any, +) -> NDArray: """ - Computes a colour correction matrix from given :math:`M_T` colour array to + Compute a colour correction matrix from given :math:`M_T` colour array to :math:`M_R` colour array. The resulting colour correction matrix is computed using multiple linear or @@ -746,33 +841,31 @@ def matrix_colour_correction(M_T, M_R, method='Cheung 2004', **kwargs): Parameters ---------- - M_T : array_like, (n, 3) + M_T Test array :math:`M_T` to fit onto array :math:`M_R`. - M_R : array_like, (n, 3) + M_R Reference array the array :math:`M_T` will be colour fitted against. - method : unicode, optional - **{'Cheung 2004', 'Finlayson 2015', 'Vandermonde'}**, + method Computation method. Other Parameters ---------------- - degree : int + degree {:func:`colour.characterisation.polynomial_expansion_Finlayson2015`, :func:`colour.characterisation.polynomial_expansion_Vandermonde`}, Expanded polynomial degree, must be one of *[1, 2, 3, 4]* for :func:`colour.characterisation.polynomial_expansion_Finlayson2015` definition. - terms : int - {:func:`colour.characterisation.matrix_augmented_Cheung2004`}, - Number of terms of the expanded polynomial, must be one of - *[3, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22]*. - root_polynomial_expansion : bool + root_polynomial_expansion {:func:`colour.characterisation.polynomial_expansion_Finlayson2015`}, Whether to use the root-polynomials set for the expansion. + terms + {:func:`colour.characterisation.matrix_augmented_Cheung2004`}, + Number of terms of the expanded polynomial. Returns ------- - ndarray, (n, 3) + :class:`numpy.ndarray` Colour correction matrix. References @@ -840,32 +933,38 @@ def matrix_colour_correction(M_T, M_R, method='Cheung 2004', **kwargs): [-0.0631495..., 0.0921247..., 0.9713415...]]) """ + method = validate_method(method, MATRIX_COLOUR_CORRECTION_METHODS) + function = MATRIX_COLOUR_CORRECTION_METHODS[method] return function(M_T, M_R, **filter_kwargs(function, **kwargs)) -def colour_correction_Cheung2004(RGB, M_T, M_R, terms=3): +def colour_correction_Cheung2004( + RGB: ArrayLike, + M_T: ArrayLike, + M_R: ArrayLike, + terms: Literal[3, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22] = 3, +) -> NDArray: """ - Performs colour correction of given *RGB* colourspace array using the + Perform colour correction of given *RGB* colourspace array using the colour correction matrix from given :math:`M_T` colour array to :math:`M_R` colour array using *Cheung et al. (2004)* method. Parameters ---------- - RGB : array_like, (n, 3) + RGB *RGB* colourspace array to colour correct. - M_T : array_like, (n, 3) + M_T Test array :math:`M_T` to fit onto array :math:`M_R`. - M_R : array_like, (n, 3) + M_R Reference array the array :math:`M_T` will be colour fitted against. - terms : int, optional - Number of terms of the expanded polynomial, must be one of - *[3, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22]*. + terms + Number of terms of the expanded polynomial. Returns ------- - ndarray + :class:`numpy.ndarray` Colour corrected *RGB* colourspace array. References @@ -894,32 +993,34 @@ def colour_correction_Cheung2004(RGB, M_T, M_R, terms=3): return np.reshape(np.transpose(np.dot(CCM, np.transpose(RGB_e))), shape) -def colour_correction_Finlayson2015(RGB, - M_T, - M_R, - degree=1, - root_polynomial_expansion=True): +def colour_correction_Finlayson2015( + RGB: ArrayLike, + M_T: ArrayLike, + M_R: ArrayLike, + degree: Literal[1, 2, 3, 4] = 1, + root_polynomial_expansion: Boolean = True, +) -> NDArray: """ - Performs colour correction of given *RGB* colourspace array using the + Perform colour correction of given *RGB* colourspace array using the colour correction matrix from given :math:`M_T` colour array to :math:`M_R` colour array using *Finlayson et al. (2015)* method. Parameters ---------- - RGB : array_like, (n, 3) + RGB *RGB* colourspace array to colour correct. - M_T : array_like, (n, 3) + M_T Test array :math:`M_T` to fit onto array :math:`M_R`. - M_R : array_like, (n, 3) + M_R Reference array the array :math:`M_T` will be colour fitted against. - degree : int, optional + degree Expanded polynomial degree. - root_polynomial_expansion : bool + root_polynomial_expansion Whether to use the root-polynomials set for the expansion. Returns ------- - ndarray + :class:`numpy.ndarray` Colour corrected *RGB* colourspace array. References @@ -941,35 +1042,39 @@ def colour_correction_Finlayson2015(RGB, RGB = np.reshape(RGB, (-1, 3)) - RGB_e = polynomial_expansion_Finlayson2015(RGB, degree, - root_polynomial_expansion) + RGB_e = polynomial_expansion_Finlayson2015( + RGB, degree, root_polynomial_expansion + ) - CCM = matrix_colour_correction_Finlayson2015(M_T, M_R, degree, - root_polynomial_expansion) + CCM = matrix_colour_correction_Finlayson2015( + M_T, M_R, degree, root_polynomial_expansion + ) return np.reshape(np.transpose(np.dot(CCM, np.transpose(RGB_e))), shape) -def colour_correction_Vandermonde(RGB, M_T, M_R, degree=1): +def colour_correction_Vandermonde( + RGB: ArrayLike, M_T: ArrayLike, M_R: ArrayLike, degree: Integer = 1 +) -> NDArray: """ - Performs colour correction of given *RGB* colourspace array using the + Perform colour correction of given *RGB* colourspace array using the colour correction matrix from given :math:`M_T` colour array to :math:`M_R` colour array using *Vandermonde* method. Parameters ---------- - RGB : array_like, (n, 3) + RGB *RGB* colourspace array to colour correct. - M_T : array_like, (n, 3) + M_T Test array :math:`M_T` to fit onto array :math:`M_R`. - M_R : array_like, (n, 3) + M_R Reference array the array :math:`M_T` will be colour fitted against. - degree : int, optional + degree Expanded polynomial degree. Returns ------- - ndarray + :class:`numpy.ndarray` Colour corrected *RGB* colourspace array. References @@ -998,11 +1103,13 @@ def colour_correction_Vandermonde(RGB, M_T, M_R, degree=1): return np.reshape(np.transpose(np.dot(CCM, np.transpose(RGB_e))), shape) -COLOUR_CORRECTION_METHODS = CaseInsensitiveMapping({ - 'Cheung 2004': colour_correction_Cheung2004, - 'Finlayson 2015': colour_correction_Finlayson2015, - 'Vandermonde': colour_correction_Vandermonde, -}) +COLOUR_CORRECTION_METHODS = CaseInsensitiveMapping( + { + "Cheung 2004": colour_correction_Cheung2004, + "Finlayson 2015": colour_correction_Finlayson2015, + "Vandermonde": colour_correction_Vandermonde, + } +) COLOUR_CORRECTION_METHODS.__doc__ = """ Supported colour correction methods. @@ -1010,49 +1117,52 @@ def colour_correction_Vandermonde(RGB, M_T, M_R, degree=1): ---------- :cite:`Cheung2004`, :cite:`Finlayson2015`, :cite:`Westland2004`, :cite:`Wikipedia2003e` - -COLOUR_CORRECTION_METHODS : CaseInsensitiveMapping - **{'Cheung 2004', 'Finlayson 2015', 'Vandermonde'}** """ -def colour_correction(RGB, M_T, M_R, method='Cheung 2004', **kwargs): +def colour_correction( + RGB: ArrayLike, + M_T: ArrayLike, + M_R: ArrayLike, + method: Union[ + Literal["Cheung 2004", "Finlayson 2015", "Vandermonde"], str + ] = "Cheung 2004", + **kwargs: Any, +) -> NDArray: """ - Performs colour correction of given *RGB* colourspace array using the + Perform colour correction of given *RGB* colourspace array using the colour correction matrix from given :math:`M_T` colour array to :math:`M_R` colour array. Parameters ---------- - RGB : array_like, (n, 3) + RGB *RGB* colourspace array to colour correct. - M_T : array_like, (n, 3) + M_T Test array :math:`M_T` to fit onto array :math:`M_R`. - M_R : array_like, (n, 3) + M_R Reference array the array :math:`M_T` will be colour fitted against. - method : unicode, optional - **{'Cheung 2004', 'Finlayson 2015', 'Vandermonde'}**, + method Computation method. Other Parameters ---------------- - degree : int + degree {:func:`colour.characterisation.polynomial_expansion_Finlayson2015`, :func:`colour.characterisation.polynomial_expansion_Vandermonde`}, Expanded polynomial degree, must be one of *[1, 2, 3, 4]* for :func:`colour.characterisation.polynomial_expansion_Finlayson2015` definition. - terms : int - {:func:`colour.characterisation.matrix_augmented_Cheung2004`}, - Number of terms of the expanded polynomial, must be one of - *[3, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22]*. - root_polynomial_expansion : bool + root_polynomial_expansion {:func:`colour.characterisation.polynomial_expansion_Finlayson2015`}, Whether to use the root-polynomials set for the expansion. + terms + {:func:`colour.characterisation.matrix_augmented_Cheung2004`}, + Number of terms of the expanded polynomial. Returns ------- - ndarray + :class:`numpy.ndarray` Colour corrected *RGB* colourspace array. References @@ -1119,6 +1229,8 @@ def colour_correction(RGB, M_T, M_R, method='Cheung 2004', **kwargs): array([ 0.1334872..., 0.0843921..., 0.0599014...]) """ + method = validate_method(method, COLOUR_CORRECTION_METHODS) + function = COLOUR_CORRECTION_METHODS[method] return function(RGB, M_T, M_R, **filter_kwargs(function, **kwargs)) diff --git a/colour/characterisation/datasets/__init__.py b/colour/characterisation/datasets/__init__.py index 4ca4af4aa5..d93d169a04 100644 --- a/colour/characterisation/datasets/__init__.py +++ b/colour/characterisation/datasets/__init__.py @@ -1,18 +1,31 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .aces_it import MSDS_ACES_RICD from .cameras import MSDS_CAMERA_SENSITIVITIES -from .colour_checkers import (CCS_COLOURCHECKERS, ColourChecker, - SDS_COLOURCHECKERS) +from .colour_checkers import ( + CCS_COLOURCHECKERS, + ColourChecker, + SDS_COLOURCHECKERS, +) from .displays import MSDS_DISPLAY_PRIMARIES from .filters import SDS_FILTERS from .lenses import SDS_LENSES -__all__ = ['MSDS_ACES_RICD'] -__all__ += ['MSDS_CAMERA_SENSITIVITIES'] -__all__ += ['CCS_COLOURCHECKERS', 'ColourChecker', 'SDS_COLOURCHECKERS'] -__all__ += ['MSDS_DISPLAY_PRIMARIES'] -__all__ += ['SDS_FILTERS'] -__all__ += ['SDS_LENSES'] +__all__ = [ + "MSDS_ACES_RICD", +] +__all__ += [ + "MSDS_CAMERA_SENSITIVITIES", +] +__all__ += [ + "CCS_COLOURCHECKERS", + "ColourChecker", + "SDS_COLOURCHECKERS", +] +__all__ += [ + "MSDS_DISPLAY_PRIMARIES", +] +__all__ += [ + "SDS_FILTERS", +] +__all__ += [ + "SDS_LENSES", +] diff --git a/colour/characterisation/datasets/aces_it.py b/colour/characterisation/datasets/aces_it.py index b6250ba667..494346af5f 100644 --- a/colour/characterisation/datasets/aces_it.py +++ b/colour/characterisation/datasets/aces_it.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Academy Color Encoding System - Input Transform Dataset ======================================================= @@ -25,20 +24,24 @@ http://www.oscars.org/science-technology/council/projects/aces.html """ -from __future__ import division, unicode_literals +from __future__ import annotations from colour.characterisation import RGB_CameraSensitivities +from colour.hints import Dict -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['DATA_ACES_RICD', 'MSDS_ACES_RICD'] +__all__ = [ + "DATA_ACES_RICD", + "MSDS_ACES_RICD", +] -DATA_ACES_RICD = { +DATA_ACES_RICD: Dict = { 360.0: (1.20e-06, 0.0000000, 5.70e-06), 361.0: (1.40e-06, 0.0000000, 6.40e-06), 362.0: (1.50e-06, 0.0000000, 7.20e-06), @@ -509,10 +512,12 @@ 827.0: (0.0000000, 0.0000000, 0.0000000), 828.0: (0.0000000, 0.0000000, 0.0000000), 829.0: (0.0000000, 0.0000000, 0.0000000), - 830.0: (0.0000000, 0.0000000, 0.0000000) + 830.0: (0.0000000, 0.0000000, 0.0000000), } -MSDS_ACES_RICD = RGB_CameraSensitivities(DATA_ACES_RICD, name='ACES RICD') +MSDS_ACES_RICD: RGB_CameraSensitivities = RGB_CameraSensitivities( + DATA_ACES_RICD, name="ACES RICD" +) """ *ACES Reference Input Capture Device* spectral sensitivities. @@ -521,6 +526,4 @@ :cite:`TheAcademyofMotionPictureArtsandSciences2014q`, :cite:`TheAcademyofMotionPictureArtsandSciences2014r`, :cite:`TheAcademyofMotionPictureArtsandSciencese` - -MSDS_ACES_RICD : RGB_CameraSensitivities """ diff --git a/colour/characterisation/datasets/cameras/__init__.py b/colour/characterisation/datasets/cameras/__init__.py index 7af4d420c5..058b931e5d 100644 --- a/colour/characterisation/datasets/cameras/__init__.py +++ b/colour/characterisation/datasets/cameras/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ References ---------- @@ -8,22 +7,22 @@ doi:10.1364/JOSAA.32.000381 """ -from __future__ import absolute_import +from __future__ import annotations from .dslr import MSDS_CAMERA_SENSITIVITIES_DSLR -from colour.utilities import CaseInsensitiveMapping +from colour.utilities import LazyCaseInsensitiveMapping -MSDS_CAMERA_SENSITIVITIES = CaseInsensitiveMapping( - MSDS_CAMERA_SENSITIVITIES_DSLR) +MSDS_CAMERA_SENSITIVITIES: LazyCaseInsensitiveMapping = ( + LazyCaseInsensitiveMapping(MSDS_CAMERA_SENSITIVITIES_DSLR) +) MSDS_CAMERA_SENSITIVITIES.__doc__ = """ Multi-spectral distributions of camera sensitivities. References ---------- :cite:`Darrodi2015a` - -MSDS_CAMERA_SENSITIVITIES : CaseInsensitiveMapping - **{Nikon 5100 (NPL), Sigma SDMerill (NPL)}** """ -__all__ = ['MSDS_CAMERA_SENSITIVITIES'] +__all__ = [ + "MSDS_CAMERA_SENSITIVITIES", +] diff --git a/colour/characterisation/datasets/cameras/dslr/__init__.py b/colour/characterisation/datasets/cameras/dslr/__init__.py index c522d58e14..d89d5c22d6 100644 --- a/colour/characterisation/datasets/cameras/dslr/__init__.py +++ b/colour/characterisation/datasets/cameras/dslr/__init__.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .sensitivities import MSDS_CAMERA_SENSITIVITIES_DSLR -__all__ = ['MSDS_CAMERA_SENSITIVITIES_DSLR'] +__all__ = [ + "MSDS_CAMERA_SENSITIVITIES_DSLR", +] diff --git a/colour/characterisation/datasets/cameras/dslr/sensitivities.py b/colour/characterisation/datasets/cameras/dslr/sensitivities.py index 60afe905d3..f73151573f 100644 --- a/colour/characterisation/datasets/cameras/dslr/sensitivities.py +++ b/colour/characterisation/datasets/cameras/dslr/sensitivities.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Sensitivities of *DSLR* Cameras =============================== @@ -8,9 +7,11 @@ Each *DSLR* camera data is in the form of a *dict* of :class:`colour.characterisation.RGB_CameraSensitivities` classes as follows:: - {'name': RGB_CameraSensitivities, - ..., - 'name': RGB_CameraSensitivities} + { + 'name': RGB_CameraSensitivities, + ..., + 'name': RGB_CameraSensitivities + } The following *DSLR* cameras are available: @@ -25,485 +26,601 @@ doi:10.1364/JOSAA.32.000381 """ -from __future__ import division, unicode_literals +from __future__ import annotations + +from functools import partial from colour.characterisation import RGB_CameraSensitivities -from colour.utilities import CaseInsensitiveMapping +from colour.hints import Dict +from colour.utilities import LazyCaseInsensitiveMapping -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['DATA_CAMERA_SENSITIVITIES_DSLR', 'MSDS_CAMERA_SENSITIVITIES_DSLR'] +__all__ = [ + "DATA_CAMERA_SENSITIVITIES_DSLR", + "MSDS_CAMERA_SENSITIVITIES_DSLR", +] -DATA_CAMERA_SENSITIVITIES_DSLR = { - 'Nikon 5100 (NPL)': { +DATA_CAMERA_SENSITIVITIES_DSLR: Dict = { + "Nikon 5100 (NPL)": { 380.0: ( 0.00156384299336578000, 0.00011500000000000000, - 0.00180956039402335990), + 0.00180956039402335990, + ), 385.0: ( 0.00189691771384825000, 0.00152114360178015000, - 0.00048982814544150399), + 0.00048982814544150399, + ), 390.0: ( 0.00000000000000000000, 0.00057430499183558695, - 0.00087943069176996504), + 0.00087943069176996504, + ), 395.0: ( 0.00000000000000000000, 0.00000000000000000000, - 0.00000000000000000000), + 0.00000000000000000000, + ), 400.0: ( 0.00000000000000000000, 0.00000000000000000000, - 0.00153246068848051000), + 0.00153246068848051000, + ), 405.0: ( 0.00071776703300973298, 0.00119722386224553000, - 0.00569805602282062030), + 0.00569805602282062030, + ), 410.0: ( 0.00292397466563330000, 0.00133571498448177000, - 0.01660828769874150200), + 0.01660828769874150200, + ), 415.0: ( 0.01293626801713740000, 0.01319431696052810100, - 0.07879120559214590500), + 0.07879120559214590500, + ), 420.0: ( 0.04959786481566520000, 0.06497102451249539600, - 0.36171350364994898000), + 0.36171350364994898000, + ), 425.0: ( 0.07607250435970400200, 0.11510308718828900000, - 0.65970462106512295000), + 0.65970462106512295000, + ), 430.0: ( 0.07658892708274399300, 0.13706582547087201000, - 0.75534360010359503000), + 0.75534360010359503000, + ), 435.0: ( 0.06833381956036009600, 0.15242852584030600000, - 0.81045312707380701000), + 0.81045312707380701000, + ), 440.0: ( 0.06131816189646559900, 0.16864005450745301000, - 0.87494523362472998000), + 0.87494523362472998000, + ), 445.0: ( 0.05473314457789760200, 0.18329934605049600000, - 0.92671273991178704000), + 0.92671273991178704000, + ), 450.0: ( 0.04886204743702320100, 0.19603263456229600000, - 0.96314088025989897000), + 0.96314088025989897000, + ), 455.0: ( 0.04284591974257399800, 0.21733653278361301000, - 0.98065048133510302000), + 0.98065048133510302000, + ), 460.0: ( 0.04022845332691499900, 0.25424357380995000000, - 1.00000000000000000000), + 1.00000000000000000000, + ), 465.0: ( 0.04340795992263239700, 0.30864811930649899000, - 0.99640467488711104000), + 0.99640467488711104000, + ), 470.0: ( 0.04762021431177430200, 0.37346871184252001000, - 0.98896988650084305000), + 0.98896988650084305000, + ), 475.0: ( 0.05077188480559390000, 0.42915806139893697000, - 0.95660139953157997000), + 0.95660139953157997000, + ), 480.0: ( 0.05280329597225499900, 0.45965432432137399000, - 0.90495886986980800000), + 0.90495886986980800000, + ), 485.0: ( 0.05257122025495090300, 0.47106435446394301000, - 0.83940927710351598000), + 0.83940927710351598000, + ), 490.0: ( 0.04789463902845950100, 0.48885616444524799000, - 0.75146259578963404000), + 0.75146259578963404000, + ), 495.0: ( 0.04823994170483859900, 0.53715178104087602000, - 0.66010202032260801000), + 0.66010202032260801000, + ), 500.0: ( 0.05022924089718029700, 0.61649118695883898000, - 0.56706879193613802000), + 0.56706879193613802000, + ), 505.0: ( 0.05507649735001429700, 0.70700638759968903000, - 0.47935094782603899000), + 0.47935094782603899000, + ), 510.0: ( 0.06370211901178619900, 0.80096424601366301000, - 0.39406273870351299000), + 0.39406273870351299000, + ), 515.0: ( 0.08038951305895999900, 0.88137256686267296000, - 0.31427061879449603000), + 0.31427061879449603000, + ), 520.0: ( 0.10038750399831201000, 0.93887792119838498000, - 0.24981663439426000000), + 0.24981663439426000000, + ), 525.0: ( 0.11861314902313400000, 0.98446559576523596000, - 0.20182351924718100000), + 0.20182351924718100000, + ), 530.0: ( 0.12360875120338000000, 1.00000000000000000000, - 0.16163395085177601000), + 0.16163395085177601000, + ), 535.0: ( 0.10306249932787701000, 0.99084026557129701000, - 0.13516143147333401000), + 0.13516143147333401000, + ), 540.0: ( 0.07634108360672720000, 0.96154626462922099000, - 0.10998875716043301000), + 0.10998875716043301000, + ), 545.0: ( 0.05278086364640900000, 0.92814388346877297000, - 0.08639435407789379500), + 0.08639435407789379500, + ), 550.0: ( 0.04118873831058649700, 0.88910231592076505000, - 0.06525313059219839400), + 0.06525313059219839400, + ), 555.0: ( 0.03904385351931050100, 0.83494222924161199000, - 0.04785595345227559900), + 0.04785595345227559900, + ), 560.0: ( 0.04254429440089119900, 0.77631807500187500000, - 0.03413932303860940000), + 0.03413932303860940000, + ), 565.0: ( 0.06021313241068020100, 0.70731424532056497000, - 0.02401990976851929900), + 0.02401990976851929900, + ), 570.0: ( 0.11179621705066800000, 0.63579620249170998000, - 0.01976793598476750100), + 0.01976793598476750100, + ), 575.0: ( 0.26967059703276203000, 0.56551528450380395000, - 0.01634844781073010000), + 0.01634844781073010000, + ), 580.0: ( 0.56450337990639099000, 0.49275517253522499000, - 0.01381733937020259900), + 0.01381733937020259900, + ), 585.0: ( 0.85360126947261405000, 0.42475654159075799000, - 0.01195294647966710000), + 0.01195294647966710000, + ), 590.0: ( 0.98103242181506201000, 0.35178931226078303000, - 0.01000909395820090100), + 0.01000909395820090100, + ), 595.0: ( 1.00000000000000000000, 0.27817849879541801000, - 0.00758776308929657970), + 0.00758776308929657970, + ), 600.0: ( 0.96307105371259005000, 0.21167353249961901000, - 0.00645584463521649970), + 0.00645584463521649970, + ), 605.0: ( 0.90552061898043101000, 0.15671644549433000000, - 0.00522978285684488030), + 0.00522978285684488030, + ), 610.0: ( 0.83427841652645296000, 0.11803962073050200000, - 0.00365998459503786990), + 0.00365998459503786990, + ), 615.0: ( 0.76798733762510296000, 0.08885249534231440300, - 0.00395538505488667040), + 0.00395538505488667040, + ), 620.0: ( 0.70366798041157996000, 0.07010184404853669900, - 0.00396835221654468030), + 0.00396835221654468030, + ), 625.0: ( 0.63916484476123703000, 0.05690899470893220200, - 0.00349138004486036990), + 0.00349138004486036990, + ), 630.0: ( 0.57081292173776299000, 0.04729879101895839700, - 0.00404302103181797010), + 0.00404302103181797010, + ), 635.0: ( 0.49581796193158800000, 0.04119589002556579800, - 0.00418929985295813000), + 0.00418929985295813000, + ), 640.0: ( 0.43833913452368101000, 0.03525207084991220000, - 0.00554676856500057980), + 0.00554676856500057980, + ), 645.0: ( 0.38896992260406899000, 0.03069313144532450100, - 0.00546423323547744030), + 0.00546423323547744030, + ), 650.0: ( 0.34295621205484700000, 0.02680396295683950100, - 0.00597382847392098970), + 0.00597382847392098970, + ), 655.0: ( 0.29278541836293998000, 0.02352430119871520100, - 0.00630906774763779000), + 0.00630906774763779000, + ), 660.0: ( 0.23770718073119301000, 0.02034633252474659900, - 0.00610412697742267980), + 0.00610412697742267980, + ), 665.0: ( 0.16491386803178501000, 0.01545848325340879900, - 0.00483655792375416000), + 0.00483655792375416000, + ), 670.0: ( 0.09128771706377150600, 0.00944075104617158980, - 0.00302664794586984980), + 0.00302664794586984980, + ), 675.0: ( 0.04205615047283590300, 0.00508102204063505970, - 0.00172169700987674990), + 0.00172169700987674990, + ), 680.0: ( 0.02058267877678380100, 0.00291019166901752010, - 0.00078065128657817595), + 0.00078065128657817595, + ), 685.0: ( 0.01028680596369610000, 0.00162657557793382010, - 0.00056963070848184102), + 0.00056963070848184102, + ), 690.0: ( 0.00540759846247261970, 0.00092251569139627796, - 0.00027523296133938200), + 0.00027523296133938200, + ), 695.0: ( 0.00272409261591003000, 0.00049743349969026901, - 0.00029672137857068598), + 0.00029672137857068598, + ), 700.0: ( 0.00127834798711079000, 0.00041215940263165701, - 0.00024951192304202899), + 0.00024951192304202899, + ), 705.0: ( 0.00078123118374132301, 0.00031692634104666300, - 8.5000000000000006e-05), + 8.5000000000000006e-05, + ), 710.0: ( 0.00047981421940270001, 0.00025621496960251102, - 0.00041916895092770603), + 0.00041916895092770603, + ), 715.0: ( 0.00049133356428571098, 0.00000000000000000000, - 0.00015331743444139899), + 0.00015331743444139899, + ), 720.0: ( 0.00017414897796340199, 0.00024353518865341200, - 1.8300000000000001e-05), + 1.8300000000000001e-05, + ), 725.0: ( 0.00012017462571764001, 6.0200000000000000e-05, - 0.00000000000000000000), + 0.00000000000000000000, + ), 730.0: ( 0.00000000000000000000, 0.00000000000000000000, - 0.00033869381945204901), + 0.00033869381945204901, + ), 735.0: ( 6.1199999999999997e-05, 0.00000000000000000000, - 0.00000000000000000000), + 0.00000000000000000000, + ), 740.0: ( 0.00000000000000000000, 0.00000000000000000000, - 0.00000000000000000000), + 0.00000000000000000000, + ), 745.0: ( 0.00000000000000000000, 1.7099999999999999e-05, - 0.00016527828734010200), + 0.00016527828734010200, + ), 750.0: ( 0.00031099754946016501, 5.2099999999999999e-05, - 0.00017755262214537101), + 0.00017755262214537101, + ), 755.0: ( 0.00000000000000000000, 8.8499999999999996e-05, - 0.00000000000000000000), + 0.00000000000000000000, + ), 760.0: ( 0.00000000000000000000, 0.00000000000000000000, - 2.4300000000000001e-05), + 2.4300000000000001e-05, + ), 765.0: ( 0.00000000000000000000, 0.00000000000000000000, - 6.1799999999999998e-05), + 6.1799999999999998e-05, + ), 770.0: ( 8.5599999999999994e-05, 0.00013799999999999999, - 0.00026260703183506501), + 0.00026260703183506501, + ), 775.0: ( 0.00013831372865247499, 0.0001786501727059410, - 0.00028050537004191899), + 0.00028050537004191899, + ), 780.0: ( 3.6199999999999999e-05, 4.2500000000000003e-05, - 0.00000000000000000000) - + 0.00000000000000000000, + ), }, - 'Sigma SDMerill (NPL)': { + "Sigma SDMerill (NPL)": { 400.0: ( 0.00562107440608700020, 0.00632809751263116970, - 0.16215942413307899000), + 0.16215942413307899000, + ), 410.0: ( 0.00650335624511722000, 0.00976180459591275040, - 0.28549837804628603000), + 0.28549837804628603000, + ), 420.0: ( 0.07407911289140040000, 0.02527177008261050100, - 0.39690431060902098000), + 0.39690431060902098000, + ), 430.0: ( 0.04302295946292879900, 0.08375118585311219800, - 0.50831024317175599000), + 0.50831024317175599000, + ), 440.0: ( 0.03450952562247010200, 0.14370381974360999000, - 0.62211847246948804000), + 0.62211847246948804000, + ), 450.0: ( 0.01889156723434350100, 0.18361168930882199000, - 0.73742136245769496000), + 0.73742136245769496000, + ), 460.0: ( 0.00731107699680200000, 0.40909478009952999000, - 0.94538036670138004000), + 0.94538036670138004000, + ), 470.0: ( 0.04549915123096019700, 0.51595564086176404000, - 0.96441494770280400000), + 0.96441494770280400000, + ), 480.0: ( 0.05676752921111680200, 0.60120664662705503000, - 1.00000000000000000000), + 1.00000000000000000000, + ), 490.0: ( 0.13419592065917799000, 0.67031679980136305000, - 0.98598021188452500000), + 0.98598021188452500000, + ), 500.0: ( 0.16475268997837600000, 0.75258747153475802000, - 0.98340266357529005000), + 0.98340266357529005000, + ), 510.0: ( 0.21712641978639199000, 0.84381384368944201000, - 0.96969219567072595000), + 0.96969219567072595000, + ), 520.0: ( 0.30648343835824399000, 0.90151724558812696000, - 0.94280817402079797000), + 0.94280817402079797000, + ), 530.0: ( 0.34984579614888500000, 0.91975030668767699000, - 0.89664279918070899000), + 0.89664279918070899000, + ), 540.0: ( 0.44374258133259298000, 0.96799429052157804000, - 0.88444590220041897000), + 0.88444590220041897000, + ), 550.0: ( 0.44488860528126301000, 0.95725231064041105000, - 0.86791899071597101000), + 0.86791899071597101000, + ), 560.0: ( 0.47897575674702603000, 0.95204791860047400000, - 0.83375679584908402000), + 0.83375679584908402000, + ), 570.0: ( 0.50950291481073895000, 0.97628014458399803000, - 0.83204140240572999000), + 0.83204140240572999000, + ), 580.0: ( 0.59262909378530504000, 0.97258624388955806000, - 0.80054956384778198000), + 0.80054956384778198000, + ), 590.0: ( 0.67383327560697603000, 1.00000000000000000000, - 0.78289512474646505000), + 0.78289512474646505000, + ), 600.0: ( 0.71403771488106504000, 0.96948452757777404000, - 0.73946953007191796000), + 0.73946953007191796000, + ), 610.0: ( 0.86000761311495100000, 0.95441319124850699000, - 0.66718640174985699000), + 0.66718640174985699000, + ), 620.0: ( 0.89810302849565204000, 0.93335435890921303000, - 0.62043627806816704000), + 0.62043627806816704000, + ), 630.0: ( 1.00000000000000000000, 0.92571406833636205000, - 0.61116087876956704000), + 0.61116087876956704000, + ), 640.0: ( 0.99494213311245205000, 0.88486439541503403000, - 0.55173556195710605000), + 0.55173556195710605000, + ), 650.0: ( 0.92085127736137995000, 0.76165184741615699000, - 0.46538831744516401000), + 0.46538831744516401000, + ), 660.0: ( 0.18143311631425299000, 0.14052437057150499000, - 0.07961907836720690000), + 0.07961907836720690000, + ), 670.0: ( 0.00630978795372749960, 0.00414367215817645990, - 0.00059244446107236802), + 0.00059244446107236802, + ), 680.0: ( 0.00528874383171553000, 0.00183198958165669010, - 0.00468563680483140980) - } -} # yapf: disable + 0.00468563680483140980, + ), + }, +} -MSDS_CAMERA_SENSITIVITIES_DSLR = CaseInsensitiveMapping({ - 'Nikon 5100 (NPL)': - RGB_CameraSensitivities( - DATA_CAMERA_SENSITIVITIES_DSLR['Nikon 5100 (NPL)'], - name='Nikon 5100 (NPL)'), - 'Sigma SDMerill (NPL)': - RGB_CameraSensitivities( - DATA_CAMERA_SENSITIVITIES_DSLR['Sigma SDMerill (NPL)'], - name='Sigma SDMerill (NPL)') -}) +MSDS_CAMERA_SENSITIVITIES_DSLR = LazyCaseInsensitiveMapping( + { + "Nikon 5100 (NPL)": partial( + RGB_CameraSensitivities, + DATA_CAMERA_SENSITIVITIES_DSLR["Nikon 5100 (NPL)"], + name="Nikon 5100 (NPL)", + ), + "Sigma SDMerill (NPL)": partial( + RGB_CameraSensitivities, + DATA_CAMERA_SENSITIVITIES_DSLR["Sigma SDMerill (NPL)"], + name="Sigma SDMerill (NPL)", + ), + } +) """ Multi-spectral distributions of *DSLR* camera sensitivities. References ---------- :cite:`Darrodi2015a` - -MSDS_CAMERA_SENSITIVITIES_DSLR : CaseInsensitiveMapping - **{Nikon 5100 (NPL), Sigma SDMerill (NPL)}** """ diff --git a/colour/characterisation/datasets/colour_checkers/__init__.py b/colour/characterisation/datasets/colour_checkers/__init__.py index ebdbd94f52..3a838a9340 100644 --- a/colour/characterisation/datasets/colour_checkers/__init__.py +++ b/colour/characterisation/datasets/colour_checkers/__init__.py @@ -1,9 +1,10 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .chromaticity_coordinates import CCS_COLOURCHECKERS, ColourChecker from .sds import SDS_COLOURCHECKERS -__all__ = ['CCS_COLOURCHECKERS', 'ColourChecker'] -__all__ += ['SDS_COLOURCHECKERS'] +__all__ = [ + "CCS_COLOURCHECKERS", + "ColourChecker", +] +__all__ += [ + "SDS_COLOURCHECKERS", +] diff --git a/colour/characterisation/datasets/colour_checkers/chromaticity_coordinates.py b/colour/characterisation/datasets/colour_checkers/chromaticity_coordinates.py index 1a61f19969..b1d42265c8 100644 --- a/colour/characterisation/datasets/colour_checkers/chromaticity_coordinates.py +++ b/colour/characterisation/datasets/colour_checkers/chromaticity_coordinates.py @@ -1,12 +1,11 @@ -# -*- coding: utf-8 -*- """ Chromaticity Coordinates of the Colour Checkers =============================================== Defines the chromaticity coordinates of the colour checkers. -Each colour checker data is in the form of an :class:`OrderedDict` class -instance of 24 samples as follows:: +Each colour checker data is in the form of an :class:`dict` class instance of +24 samples as follows:: {'name': 'xyY', ..., 'name': 'xyY'} @@ -44,285 +43,283 @@ SupportID=5884# """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from collections import OrderedDict, namedtuple +from collections import namedtuple from colour.colorimetry import CCS_ILLUMINANTS +from colour.hints import Dict, NDArray, Tuple from colour.models import Lab_to_XYZ, XYZ_to_xyY from colour.utilities import CaseInsensitiveMapping -__author__ = 'Colour Developers, Danny Pascale ' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__copyright__ += ', ' +__author__ = "Colour Developers, Danny Pascale " +__copyright__ = "Copyright 2013 Colour Developers" +__copyright__ += ", " __copyright__ += ( - 'BabelColor ColorChecker data: Copyright (C) 2004-2012 Danny Pascale ' - '(www.babelcolor.com); used by permission.') -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + "BabelColor ColorChecker data: Copyright (C) 2004-2012 Danny Pascale " + "(www.babelcolor.com); used by permission." +) +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" __all__ = [ - 'ColourChecker', 'SAMPLE_LABELS_COLORCHECKER_CLASSIC', - 'DATA_COLORCHECKER1976', 'CCS_ILLUMINANT_COLORCHECKER1976', - 'CCS_COLORCHECKER1976', 'DATA_COLORCHECKER2005', - 'CCS_ILLUMINANT_COLORCHECKER2005', 'CCS_COLORCHECKER2005', - 'DATA_BABELCOLOR_AVERAGE', 'CCS_ILLUMINANT_BABELCOLOR_AVERAGE', - 'CCS_BABELCOLOR_AVERAGE', 'DATA_COLORCHECKER24_BEFORE_NOV2014', - 'DATA_COLORCHECKER24_BEFORE_NOV2014', - 'CCS_ILLUMINANT_COLORCHECKER24_BEFORE_NOV2014', - 'CCS_COLORCHECKER24_BEFORE_NOV2014', 'DATA_COLORCHECKER24_AFTER_NOV2014', - 'DATA_COLORCHECKER24_AFTER_NOV2014', - 'CCS_ILLUMINANT_COLORCHECKER24_AFTER_NOV2014', - 'CCS_COLORCHECKER24_AFTER_NOV2014', 'CCS_COLOURCHECKERS' + "ColourChecker", + "SAMPLE_LABELS_COLORCHECKER_CLASSIC", + "DATA_COLORCHECKER1976", + "CCS_ILLUMINANT_COLORCHECKER1976", + "CCS_COLORCHECKER1976", + "DATA_COLORCHECKER2005", + "CCS_ILLUMINANT_COLORCHECKER2005", + "CCS_COLORCHECKER2005", + "DATA_BABELCOLOR_AVERAGE", + "CCS_ILLUMINANT_BABELCOLOR_AVERAGE", + "CCS_BABELCOLOR_AVERAGE", + "DATA_COLORCHECKER24_BEFORE_NOV2014_CIE_LAB", + "DATA_COLORCHECKER24_BEFORE_NOV2014", + "CCS_ILLUMINANT_COLORCHECKER24_BEFORE_NOV2014", + "CCS_COLORCHECKER24_BEFORE_NOV2014", + "DATA_COLORCHECKER24_AFTER_NOV2014", + "DATA_COLORCHECKER24_AFTER_NOV2014", + "CCS_ILLUMINANT_COLORCHECKER24_AFTER_NOV2014", + "CCS_COLORCHECKER24_AFTER_NOV2014", + "CCS_COLOURCHECKERS", ] class ColourChecker( - namedtuple('ColourChecker', ('name', 'data', 'illuminant'))): + namedtuple("ColourChecker", ("name", "data", "illuminant")) +): """ *Colour Checker* data. Parameters ---------- - name : unicode + name *Colour Checker* name. - data : OrderedDict + data Chromaticity coordinates in *CIE xyY* colourspace. - illuminant : array_like + illuminant *Colour Checker* illuminant chromaticity coordinates. """ -SAMPLE_LABELS_COLORCHECKER_CLASSIC = ( - 'dark skin', - 'light skin', - 'blue sky', - 'foliage', - 'blue flower', - 'bluish green', - 'orange', - 'purplish blue', - 'moderate red', - 'purple', - 'yellow green', - 'orange yellow', - 'blue', - 'green', - 'red', - 'yellow', - 'magenta', - 'cyan', - 'white 9.5 (.05 D)', - 'neutral 8 (.23 D)', - 'neutral 6.5 (.44 D)', - 'neutral 5 (.70 D)', - 'neutral 3.5 (1.05 D)', - 'black 2 (1.5 D)', +SAMPLE_LABELS_COLORCHECKER_CLASSIC: Tuple = ( + "dark skin", + "light skin", + "blue sky", + "foliage", + "blue flower", + "bluish green", + "orange", + "purplish blue", + "moderate red", + "purple", + "yellow green", + "orange yellow", + "blue", + "green", + "red", + "yellow", + "magenta", + "cyan", + "white 9.5 (.05 D)", + "neutral 8 (.23 D)", + "neutral 6.5 (.44 D)", + "neutral 5 (.70 D)", + "neutral 3.5 (1.05 D)", + "black 2 (1.5 D)", ) -""" -*ColorChecker Classic* illuminant. - -SAMPLE_LABELS_COLORCHECKER_CLASSIC : tuple -""" +"""*ColorChecker Classic* illuminant.""" -DATA_COLORCHECKER1976 = OrderedDict( - zip(SAMPLE_LABELS_COLORCHECKER_CLASSIC, [ - np.array([0.4002, 0.3504, 0.1005]), - np.array([0.3773, 0.3446, 0.3582]), - np.array([0.2470, 0.2514, 0.1933]), - np.array([0.3372, 0.4220, 0.1329]), - np.array([0.2651, 0.2400, 0.2427]), - np.array([0.2608, 0.3430, 0.4306]), - np.array([0.5060, 0.4070, 0.3005]), - np.array([0.2110, 0.1750, 0.1200]), - np.array([0.4533, 0.3058, 0.1977]), - np.array([0.2845, 0.2020, 0.0656]), - np.array([0.3800, 0.4887, 0.4429]), - np.array([0.4729, 0.4375, 0.4306]), - np.array([0.1866, 0.1285, 0.0611]), - np.array([0.3046, 0.4782, 0.2339]), - np.array([0.5385, 0.3129, 0.1200]), - np.array([0.4480, 0.4703, 0.5910]), - np.array([0.3635, 0.2325, 0.1977]), - np.array([0.1958, 0.2519, 0.1977]), - np.array([0.3101, 0.3163, 0.9001]), - np.array([0.3101, 0.3163, 0.5910]), - np.array([0.3101, 0.3163, 0.3620]), - np.array([0.3101, 0.3163, 0.1977]), - np.array([0.3101, 0.3163, 0.0900]), - np.array([0.3101, 0.3163, 0.0313]), - ])) - -CCS_ILLUMINANT_COLORCHECKER1976 = ( - CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['C']) -""" -*ColorChecker Classic 1976* illuminant. +DATA_COLORCHECKER1976: Dict = dict( + zip( + SAMPLE_LABELS_COLORCHECKER_CLASSIC, + [ + np.array([0.4002, 0.3504, 0.1005]), + np.array([0.3773, 0.3446, 0.3582]), + np.array([0.2470, 0.2514, 0.1933]), + np.array([0.3372, 0.4220, 0.1329]), + np.array([0.2651, 0.2400, 0.2427]), + np.array([0.2608, 0.3430, 0.4306]), + np.array([0.5060, 0.4070, 0.3005]), + np.array([0.2110, 0.1750, 0.1200]), + np.array([0.4533, 0.3058, 0.1977]), + np.array([0.2845, 0.2020, 0.0656]), + np.array([0.3800, 0.4887, 0.4429]), + np.array([0.4729, 0.4375, 0.4306]), + np.array([0.1866, 0.1285, 0.0611]), + np.array([0.3046, 0.4782, 0.2339]), + np.array([0.5385, 0.3129, 0.1200]), + np.array([0.4480, 0.4703, 0.5910]), + np.array([0.3635, 0.2325, 0.1977]), + np.array([0.1958, 0.2519, 0.1977]), + np.array([0.3101, 0.3163, 0.9001]), + np.array([0.3101, 0.3163, 0.5910]), + np.array([0.3101, 0.3163, 0.3620]), + np.array([0.3101, 0.3163, 0.1977]), + np.array([0.3101, 0.3163, 0.0900]), + np.array([0.3101, 0.3163, 0.0313]), + ], + ) +) -CCS_ILLUMINANT_COLORCHECKER1976 : ndarray -""" +CCS_ILLUMINANT_COLORCHECKER1976: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +]["C"] +"""*ColorChecker Classic 1976* illuminant.""" -CCS_COLORCHECKER1976 = ColourChecker('ColorChecker 1976', - DATA_COLORCHECKER1976, - CCS_ILLUMINANT_COLORCHECKER1976) +CCS_COLORCHECKER1976: ColourChecker = ColourChecker( + "ColorChecker 1976", DATA_COLORCHECKER1976, CCS_ILLUMINANT_COLORCHECKER1976 +) """ *ColorChecker Classic* developed by *McCamy et al.* (1976) at Macbeth, a Division of Kollmorgen. - -CCS_COLORCHECKER1976 : ColourChecker -""" - -DATA_COLORCHECKER2005 = OrderedDict( - zip(SAMPLE_LABELS_COLORCHECKER_CLASSIC, [ - np.array([0.4316, 0.3777, 0.1008]), - np.array([0.4197, 0.3744, 0.3495]), - np.array([0.2760, 0.3016, 0.1836]), - np.array([0.3703, 0.4499, 0.1325]), - np.array([0.2999, 0.2856, 0.2304]), - np.array([0.2848, 0.3911, 0.4178]), - np.array([0.5295, 0.4055, 0.3118]), - np.array([0.2305, 0.2106, 0.1126]), - np.array([0.5012, 0.3273, 0.1938]), - np.array([0.3319, 0.2482, 0.0637]), - np.array([0.3984, 0.5008, 0.4446]), - np.array([0.4957, 0.4427, 0.4357]), - np.array([0.2018, 0.1692, 0.0575]), - np.array([0.3253, 0.5032, 0.2318]), - np.array([0.5686, 0.3303, 0.1257]), - np.array([0.4697, 0.4734, 0.5981]), - np.array([0.4159, 0.2688, 0.2009]), - np.array([0.2131, 0.3023, 0.1930]), - np.array([0.3469, 0.3608, 0.9131]), - np.array([0.3440, 0.3584, 0.5894]), - np.array([0.3432, 0.3581, 0.3632]), - np.array([0.3446, 0.3579, 0.1915]), - np.array([0.3401, 0.3548, 0.0883]), - np.array([0.3406, 0.3537, 0.0311]), - ])) - -CCS_ILLUMINANT_COLORCHECKER2005 = ( - CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['ICC D50']) -""" -*ColorChecker Classic 2005* illuminant. - -CCS_ILLUMINANT_COLORCHECKER2005 : ndarray """ -CCS_COLORCHECKER2005 = ColourChecker('ColorChecker 2005', - DATA_COLORCHECKER2005, - CCS_ILLUMINANT_COLORCHECKER2005) -""" -*ColorChecker Classic* data from *GretagMacbeth (2005)*. - -CCS_COLORCHECKER2005 : ColourChecker -""" -DATA_BABELCOLOR_AVERAGE = OrderedDict( - zip(SAMPLE_LABELS_COLORCHECKER_CLASSIC, [ - np.array([0.4325, 0.3788, 0.1034]), - np.array([0.4191, 0.3748, 0.3525]), - np.array([0.2761, 0.3004, 0.1847]), - np.array([0.3700, 0.4501, 0.1335]), - np.array([0.3020, 0.2877, 0.2324]), - np.array([0.2856, 0.3910, 0.4174]), - np.array([0.5291, 0.4075, 0.3117]), - np.array([0.2339, 0.2155, 0.1140]), - np.array([0.5008, 0.3293, 0.1979]), - np.array([0.3326, 0.2556, 0.0644]), - np.array([0.3989, 0.4998, 0.4435]), - np.array([0.4962, 0.4428, 0.4358]), - np.array([0.2040, 0.1696, 0.0579]), - np.array([0.3270, 0.5033, 0.2307]), - np.array([0.5709, 0.3298, 0.1268]), - np.array([0.4694, 0.4732, 0.6081]), - np.array([0.4177, 0.2704, 0.2007]), - np.array([0.2151, 0.3037, 0.1903]), - np.array([0.3488, 0.3628, 0.9129]), - np.array([0.3451, 0.3596, 0.5885]), - np.array([0.3446, 0.3590, 0.3595]), - np.array([0.3438, 0.3589, 0.1912]), - np.array([0.3423, 0.3576, 0.0893]), - np.array([0.3439, 0.3565, 0.0320]), - ])) - -CCS_ILLUMINANT_BABELCOLOR_AVERAGE = ( - CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['ICC D50']) -""" -*BabelColor Average* illuminant. +DATA_COLORCHECKER2005: Dict = dict( + zip( + SAMPLE_LABELS_COLORCHECKER_CLASSIC, + [ + np.array([0.4316, 0.3777, 0.1008]), + np.array([0.4197, 0.3744, 0.3495]), + np.array([0.2760, 0.3016, 0.1836]), + np.array([0.3703, 0.4499, 0.1325]), + np.array([0.2999, 0.2856, 0.2304]), + np.array([0.2848, 0.3911, 0.4178]), + np.array([0.5295, 0.4055, 0.3118]), + np.array([0.2305, 0.2106, 0.1126]), + np.array([0.5012, 0.3273, 0.1938]), + np.array([0.3319, 0.2482, 0.0637]), + np.array([0.3984, 0.5008, 0.4446]), + np.array([0.4957, 0.4427, 0.4357]), + np.array([0.2018, 0.1692, 0.0575]), + np.array([0.3253, 0.5032, 0.2318]), + np.array([0.5686, 0.3303, 0.1257]), + np.array([0.4697, 0.4734, 0.5981]), + np.array([0.4159, 0.2688, 0.2009]), + np.array([0.2131, 0.3023, 0.1930]), + np.array([0.3469, 0.3608, 0.9131]), + np.array([0.3440, 0.3584, 0.5894]), + np.array([0.3432, 0.3581, 0.3632]), + np.array([0.3446, 0.3579, 0.1915]), + np.array([0.3401, 0.3548, 0.0883]), + np.array([0.3406, 0.3537, 0.0311]), + ], + ) +) -CCS_ILLUMINANT_BABELCOLOR_AVERAGE : ndarray -""" +CCS_ILLUMINANT_COLORCHECKER2005: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +]["ICC D50"] +"""*ColorChecker Classic 2005* illuminant.""" -CCS_BABELCOLOR_AVERAGE = ColourChecker('BabelColor Average', - DATA_BABELCOLOR_AVERAGE, - CCS_ILLUMINANT_BABELCOLOR_AVERAGE) -""" -Average data derived from measurements of 30 *ColorChecker Classic* charts. - -CCS_BABELCOLOR_AVERAGE : ColourChecker -""" +CCS_COLORCHECKER2005: ColourChecker = ColourChecker( + "ColorChecker 2005", DATA_COLORCHECKER2005, CCS_ILLUMINANT_COLORCHECKER2005 +) +"""*ColorChecker Classic* data from *GretagMacbeth (2005)*.""" +DATA_BABELCOLOR_AVERAGE: Dict = dict( + zip( + SAMPLE_LABELS_COLORCHECKER_CLASSIC, + [ + np.array([0.4325, 0.3788, 0.1034]), + np.array([0.4191, 0.3748, 0.3525]), + np.array([0.2761, 0.3004, 0.1847]), + np.array([0.3700, 0.4501, 0.1335]), + np.array([0.3020, 0.2877, 0.2324]), + np.array([0.2856, 0.3910, 0.4174]), + np.array([0.5291, 0.4075, 0.3117]), + np.array([0.2339, 0.2155, 0.1140]), + np.array([0.5008, 0.3293, 0.1979]), + np.array([0.3326, 0.2556, 0.0644]), + np.array([0.3989, 0.4998, 0.4435]), + np.array([0.4962, 0.4428, 0.4358]), + np.array([0.2040, 0.1696, 0.0579]), + np.array([0.3270, 0.5033, 0.2307]), + np.array([0.5709, 0.3298, 0.1268]), + np.array([0.4694, 0.4732, 0.6081]), + np.array([0.4177, 0.2704, 0.2007]), + np.array([0.2151, 0.3037, 0.1903]), + np.array([0.3488, 0.3628, 0.9129]), + np.array([0.3451, 0.3596, 0.5885]), + np.array([0.3446, 0.3590, 0.3595]), + np.array([0.3438, 0.3589, 0.1912]), + np.array([0.3423, 0.3576, 0.0893]), + np.array([0.3439, 0.3565, 0.0320]), + ], + ) +) -DATA_COLORCHECKER24_BEFORE_NOV2014 = OrderedDict( - zip(SAMPLE_LABELS_COLORCHECKER_CLASSIC, [ - np.array([37.986, 13.555, 14.059]), - np.array([65.711, 18.13, 17.81]), - np.array([49.927, -4.88, -21.905]), - np.array([43.139, -13.095, 21.905]), - np.array([55.112, 8.844, -25.399]), - np.array([70.719, -33.397, -0.199]), - np.array([62.661, 36.067, 57.096]), - np.array([40.02, 10.41, -45.964]), - np.array([51.124, 48.239, 16.248]), - np.array([30.325, 22.976, -21.587]), - np.array([72.532, -23.709, 57.255]), - np.array([71.941, 19.363, 67.857]), - np.array([28.778, 14.179, -50.297]), - np.array([55.261, -38.342, 31.37]), - np.array([42.101, 53.378, 28.19]), - np.array([81.733, 4.039, 79.819]), - np.array([51.935, 49.986, -14.574]), - np.array([51.038, -28.631, -28.638]), - np.array([96.539, -0.425, 1.186]), - np.array([81.257, -0.638, -0.335]), - np.array([66.766, -0.734, -0.504]), - np.array([50.867, -0.153, -0.27]), - np.array([35.656, -0.421, -1.231]), - np.array([20.461, -0.079, -0.973]), - ])) -""" -*ColorChecker24 - Before November 2014* illuminant. +CCS_ILLUMINANT_BABELCOLOR_AVERAGE: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +]["ICC D50"] +"""*BabelColor Average* illuminant.""" -Notes ------ -- *X-Rite* data is given as *CIE L\\*a\\*b\\** colourspace values under - *CIE Illuminant D Series D50* for the - *CIE 1931 2 Degree Standard Observer*. +CCS_BABELCOLOR_AVERAGE: ColourChecker = ColourChecker( + "BabelColor Average", + DATA_BABELCOLOR_AVERAGE, + CCS_ILLUMINANT_BABELCOLOR_AVERAGE, +) +"""Average data derived from measurements of 30 *ColorChecker Classic* charts.""" -DATA_COLORCHECKER24_BEFORE_NOV2014 : ndarray -""" +DATA_COLORCHECKER24_BEFORE_NOV2014_CIE_LAB: Dict = dict( + zip( + SAMPLE_LABELS_COLORCHECKER_CLASSIC, + [ + np.array([37.986, 13.555, 14.059]), + np.array([65.711, 18.13, 17.81]), + np.array([49.927, -4.88, -21.905]), + np.array([43.139, -13.095, 21.905]), + np.array([55.112, 8.844, -25.399]), + np.array([70.719, -33.397, -0.199]), + np.array([62.661, 36.067, 57.096]), + np.array([40.02, 10.41, -45.964]), + np.array([51.124, 48.239, 16.248]), + np.array([30.325, 22.976, -21.587]), + np.array([72.532, -23.709, 57.255]), + np.array([71.941, 19.363, 67.857]), + np.array([28.778, 14.179, -50.297]), + np.array([55.261, -38.342, 31.37]), + np.array([42.101, 53.378, 28.19]), + np.array([81.733, 4.039, 79.819]), + np.array([51.935, 49.986, -14.574]), + np.array([51.038, -28.631, -28.638]), + np.array([96.539, -0.425, 1.186]), + np.array([81.257, -0.638, -0.335]), + np.array([66.766, -0.734, -0.504]), + np.array([50.867, -0.153, -0.27]), + np.array([35.656, -0.421, -1.231]), + np.array([20.461, -0.079, -0.973]), + ], + ) +) -DATA_COLORCHECKER24_BEFORE_NOV2014 = OrderedDict( +DATA_COLORCHECKER24_BEFORE_NOV2014: Dict = dict( zip( SAMPLE_LABELS_COLORCHECKER_CLASSIC, XYZ_to_xyY( Lab_to_XYZ( - list(DATA_COLORCHECKER24_BEFORE_NOV2014.values()), - CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'][ - 'ICC D50'])))) - -CCS_ILLUMINANT_COLORCHECKER24_BEFORE_NOV2014 = ( - CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['ICC D50']) -""" -*ColorChecker24 - Before November 2014* illuminant. + list(DATA_COLORCHECKER24_BEFORE_NOV2014_CIE_LAB.values()), + CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][ + "ICC D50" + ], + ) + ), + ) +) -CCS_ILLUMINANT_COLORCHECKER24_BEFORE_NOV2014 : ndarray -""" +CCS_ILLUMINANT_COLORCHECKER24_BEFORE_NOV2014: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +]["ICC D50"] +"""*ColorChecker24 - Before November 2014* illuminant.""" -CCS_COLORCHECKER24_BEFORE_NOV2014 = ColourChecker( - 'ColorChecker24 - Before November 2014', +CCS_COLORCHECKER24_BEFORE_NOV2014: ColourChecker = ColourChecker( + "ColorChecker24 - Before November 2014", DATA_COLORCHECKER24_BEFORE_NOV2014, - CCS_ILLUMINANT_COLORCHECKER24_BEFORE_NOV2014) + CCS_ILLUMINANT_COLORCHECKER24_BEFORE_NOV2014, +) """ Reference *ColorChecker Classic* data from *X-Rite (2015)*. @@ -331,87 +328,75 @@ class ColourChecker( - The rounded *ColorChecker24 - Before November 2014* values should match the *ColorChecker Classic 2005* values. They are given for reference of the original *CIE L\\*a\\*b\\** colourspace values. - -CCS_COLORCHECKER24_BEFORE_NOV2014 : ColourChecker -""" - -DATA_COLORCHECKER24_AFTER_NOV2014 = OrderedDict(( - ('dark skin', np.array([37.54, 14.37, 14.92])), - ('light skin', np.array([64.66, 19.27, 17.5])), - ('blue sky', np.array([49.32, -3.82, -22.54])), - ('foliage', np.array([43.46, -12.74, 22.72])), - ('blue flower', np.array([54.94, 9.61, -24.79])), - ('bluish green', np.array([70.48, -32.26, -0.37])), - ('orange', np.array([62.73, 35.83, 56.5])), - ('purplish blue', np.array([39.43, 10.75, -45.17])), - ('moderate red', np.array([50.57, 48.64, 16.67])), - ('purple', np.array([30.1, 22.54, -20.87])), - ('yellow green', np.array([71.77, -24.13, 58.19])), - ('orange yellow', np.array([71.51, 18.24, 67.37])), - ('blue', np.array([28.37, 15.42, -49.8])), - ('green', np.array([54.38, -39.72, 32.27])), - ('red', np.array([42.43, 51.05, 28.62])), - ('yellow', np.array([81.8, 2.67, 80.41])), - ('magenta', np.array([50.63, 51.28, -14.12])), - ('cyan', np.array([49.57, -29.71, -28.32])), - ('white 9.5 (.05 D)', np.array([95.19, -1.03, 2.93])), - ('neutral 8 (.23 D)', np.array([81.29, -0.57, 0.44])), - ('neutral 6.5 (.44 D)', np.array([66.89, -0.75, -0.06])), - ('neutral 5 (.70 D)', np.array([50.76, -0.13, 0.14])), - ('neutral 3.5 (1.05 D)', np.array([35.63, -0.46, -0.48])), - ('black 2 (1.5 D)', np.array([20.64, 0.07, -0.46])), -)) """ -*ColorChecker24 - After November 2014* illuminant. -Notes ------ -- *X-Rite* data is given as *CIE L\\*a\\*b\\** colourspace values under - *CIE Illuminant D Series D50* for the - *CIE 1931 2 Degree Standard Observer*. - -DATA_COLORCHECKER24_AFTER_NOV2014 : ndarray -""" +DATA_COLORCHECKER24_AFTER_NOV2014_CIE_LAB: Dict = dict( + [ + ("dark skin", np.array([37.54, 14.37, 14.92])), + ("light skin", np.array([64.66, 19.27, 17.5])), + ("blue sky", np.array([49.32, -3.82, -22.54])), + ("foliage", np.array([43.46, -12.74, 22.72])), + ("blue flower", np.array([54.94, 9.61, -24.79])), + ("bluish green", np.array([70.48, -32.26, -0.37])), + ("orange", np.array([62.73, 35.83, 56.5])), + ("purplish blue", np.array([39.43, 10.75, -45.17])), + ("moderate red", np.array([50.57, 48.64, 16.67])), + ("purple", np.array([30.1, 22.54, -20.87])), + ("yellow green", np.array([71.77, -24.13, 58.19])), + ("orange yellow", np.array([71.51, 18.24, 67.37])), + ("blue", np.array([28.37, 15.42, -49.8])), + ("green", np.array([54.38, -39.72, 32.27])), + ("red", np.array([42.43, 51.05, 28.62])), + ("yellow", np.array([81.8, 2.67, 80.41])), + ("magenta", np.array([50.63, 51.28, -14.12])), + ("cyan", np.array([49.57, -29.71, -28.32])), + ("white 9.5 (.05 D)", np.array([95.19, -1.03, 2.93])), + ("neutral 8 (.23 D)", np.array([81.29, -0.57, 0.44])), + ("neutral 6.5 (.44 D)", np.array([66.89, -0.75, -0.06])), + ("neutral 5 (.70 D)", np.array([50.76, -0.13, 0.14])), + ("neutral 3.5 (1.05 D)", np.array([35.63, -0.46, -0.48])), + ("black 2 (1.5 D)", np.array([20.64, 0.07, -0.46])), + ] +) -DATA_COLORCHECKER24_AFTER_NOV2014 = OrderedDict( +DATA_COLORCHECKER24_AFTER_NOV2014: Dict = dict( zip( - DATA_COLORCHECKER24_AFTER_NOV2014.keys(), + SAMPLE_LABELS_COLORCHECKER_CLASSIC, XYZ_to_xyY( Lab_to_XYZ( - list(DATA_COLORCHECKER24_AFTER_NOV2014.values()), - CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'][ - 'ICC D50'])))) + list(DATA_COLORCHECKER24_AFTER_NOV2014_CIE_LAB.values()), + CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][ + "ICC D50" + ], + ) + ), + ) +) -CCS_ILLUMINANT_COLORCHECKER24_AFTER_NOV2014 = ( - CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['ICC D50']) -""" -*ColorChecker24 - After November 2014* illuminant. +CCS_ILLUMINANT_COLORCHECKER24_AFTER_NOV2014: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +]["ICC D50"] +"""*ColorChecker24 - After November 2014* illuminant.""" -CCS_ILLUMINANT_COLORCHECKER24_AFTER_NOV2014 : ndarray -""" - -CCS_COLORCHECKER24_AFTER_NOV2014 = ColourChecker( - 'ColorChecker24 - After November 2014', DATA_COLORCHECKER24_AFTER_NOV2014, - CCS_ILLUMINANT_COLORCHECKER24_AFTER_NOV2014) +CCS_COLORCHECKER24_AFTER_NOV2014: ColourChecker = ColourChecker( + "ColorChecker24 - After November 2014", + DATA_COLORCHECKER24_AFTER_NOV2014, + CCS_ILLUMINANT_COLORCHECKER24_AFTER_NOV2014, +) """ Reference *ColorChecker Classic* data from *X-Rite (2015)* and matching the *ColorChecker Classic* edition after November 2014. - -CCS_COLORCHECKER24_AFTER_NOV2014 : ColourChecker """ -CCS_COLOURCHECKERS = CaseInsensitiveMapping({ - 'ColorChecker 1976': - CCS_COLORCHECKER1976, - 'ColorChecker 2005': - CCS_COLORCHECKER2005, - 'BabelColor Average': - CCS_BABELCOLOR_AVERAGE, - 'ColorChecker24 - Before November 2014': - CCS_COLORCHECKER24_BEFORE_NOV2014, - 'ColorChecker24 - After November 2014': - CCS_COLORCHECKER24_AFTER_NOV2014, -}) +CCS_COLOURCHECKERS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "ColorChecker 1976": CCS_COLORCHECKER1976, + "ColorChecker 2005": CCS_COLORCHECKER2005, + "BabelColor Average": CCS_BABELCOLOR_AVERAGE, + "ColorChecker24 - Before November 2014": CCS_COLORCHECKER24_BEFORE_NOV2014, + "ColorChecker24 - After November 2014": CCS_COLORCHECKER24_AFTER_NOV2014, + } +) CCS_COLOURCHECKERS.__doc__ = """ Chromaticity coordinates of the colour checkers. @@ -419,11 +404,6 @@ class ColourChecker( ---------- :cite:`BabelColor2012b`, :cite:`BabelColor2012c`, :cite:`X-Rite2016` -CCS_COLOURCHECKERS : CaseInsensitiveMapping - **{'ColorChecker 1976', 'ColorChecker 2005', 'BabelColor Average', - 'ColorChecker24 - Before November 2014', - 'ColorChecker24 - After November 2014'}** - Aliases: - 'babel_average': 'BabelColor Average' @@ -431,9 +411,11 @@ class ColourChecker( - 'ccb2014': 'ColorChecker24 - Before November 2014' - 'cca2014': 'ColorChecker24 - After November 2014' """ -CCS_COLOURCHECKERS['babel_average'] = CCS_COLOURCHECKERS['BabelColor Average'] -CCS_COLOURCHECKERS['cc2005'] = CCS_COLOURCHECKERS['ColorChecker 2005'] -CCS_COLOURCHECKERS['ccb2014'] = CCS_COLOURCHECKERS[ - 'ColorChecker24 - Before November 2014'] -CCS_COLOURCHECKERS['cca2014'] = CCS_COLOURCHECKERS[ - 'ColorChecker24 - After November 2014'] +CCS_COLOURCHECKERS["babel_average"] = CCS_COLOURCHECKERS["BabelColor Average"] +CCS_COLOURCHECKERS["cc2005"] = CCS_COLOURCHECKERS["ColorChecker 2005"] +CCS_COLOURCHECKERS["ccb2014"] = CCS_COLOURCHECKERS[ + "ColorChecker24 - Before November 2014" +] +CCS_COLOURCHECKERS["cca2014"] = CCS_COLOURCHECKERS[ + "ColorChecker24 - After November 2014" +] diff --git a/colour/characterisation/datasets/colour_checkers/sds.py b/colour/characterisation/datasets/colour_checkers/sds.py index ddcbf05204..2fe91f8b72 100644 --- a/colour/characterisation/datasets/colour_checkers/sds.py +++ b/colour/characterisation/datasets/colour_checkers/sds.py @@ -1,13 +1,11 @@ -# -*- coding: utf-8 -*- """ Spectral Distributions of the Colour Checkers ============================================= Defines the spectral distributions of the colour checkers. -Each colour checker data is in the form of :class:`OrderedDict` -class instance of :class:`colour.SpectralDistribution` classes as -follows:: +Each colour checker data is in the form of :class:`dict` class instance of +:class:`colour.SpectralDistribution` classes as follows:: {'name': SpectralDistribution, ..., 'name': SpectralDistribution} @@ -45,2969 +43,3126 @@ class instance of :class:`colour.SpectralDistribution` classes as engineering. """ -from __future__ import division, unicode_literals +from __future__ import annotations -from colour.colorimetry import SpectralDistribution -from colour.utilities import CaseInsensitiveMapping +from functools import partial -from collections import OrderedDict +from colour.colorimetry import SpectralDistribution +from colour.hints import Dict +from colour.utilities import CaseInsensitiveMapping, LazyCaseInsensitiveMapping -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__copyright__ += ', ' +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 Colour Developers" +__copyright__ += ", " __copyright__ += ( - 'BabelColor ColorChecker data: Copyright (C) 2004-2012 Danny Pascale ' - '(www.babelcolor.com); used by permission.') -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + "BabelColor ColorChecker data: Copyright (C) 2004-2012 Danny Pascale " + "(www.babelcolor.com); used by permission." +) +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" __all__ = [ - 'DATA_BABELCOLOR_AVERAGE', 'SDS_BABELCOLOR_AVERAGE', - 'DATA_COLORCHECKER_N_OHTA', 'SDS_COLORCHECKER_N_OHTA', 'SDS_COLOURCHECKERS' + "DATA_BABELCOLOR_AVERAGE", + "SDS_BABELCOLOR_AVERAGE", + "DATA_COLORCHECKER_N_OHTA", + "SDS_COLORCHECKER_N_OHTA", + "SDS_COLOURCHECKERS", ] -DATA_BABELCOLOR_AVERAGE = OrderedDict(( - ('dark skin', { - 380: 0.055, - 390: 0.058, - 400: 0.061, - 410: 0.062, - 420: 0.062, - 430: 0.062, - 440: 0.062, - 450: 0.062, - 460: 0.062, - 470: 0.062, - 480: 0.062, - 490: 0.063, - 500: 0.065, - 510: 0.070, - 520: 0.076, - 530: 0.079, - 540: 0.081, - 550: 0.084, - 560: 0.091, - 570: 0.103, - 580: 0.119, - 590: 0.134, - 600: 0.143, - 610: 0.147, - 620: 0.151, - 630: 0.158, - 640: 0.168, - 650: 0.179, - 660: 0.188, - 670: 0.190, - 680: 0.186, - 690: 0.181, - 700: 0.182, - 710: 0.187, - 720: 0.196, - 730: 0.209 - }), - ('light skin', { - 380: 0.117, - 390: 0.143, - 400: 0.175, - 410: 0.191, - 420: 0.196, - 430: 0.199, - 440: 0.204, - 450: 0.213, - 460: 0.228, - 470: 0.251, - 480: 0.280, - 490: 0.309, - 500: 0.329, - 510: 0.333, - 520: 0.315, - 530: 0.286, - 540: 0.273, - 550: 0.276, - 560: 0.277, - 570: 0.289, - 580: 0.339, - 590: 0.420, - 600: 0.488, - 610: 0.525, - 620: 0.546, - 630: 0.562, - 640: 0.578, - 650: 0.595, - 660: 0.612, - 670: 0.625, - 680: 0.638, - 690: 0.656, - 700: 0.678, - 710: 0.700, - 720: 0.717, - 730: 0.734 - }), - ('blue sky', { - 380: 0.130, - 390: 0.177, - 400: 0.251, - 410: 0.306, - 420: 0.324, - 430: 0.330, - 440: 0.333, - 450: 0.331, - 460: 0.323, - 470: 0.311, - 480: 0.298, - 490: 0.285, - 500: 0.269, - 510: 0.250, - 520: 0.231, - 530: 0.214, - 540: 0.199, - 550: 0.185, - 560: 0.169, - 570: 0.157, - 580: 0.149, - 590: 0.145, - 600: 0.142, - 610: 0.141, - 620: 0.141, - 630: 0.141, - 640: 0.143, - 650: 0.147, - 660: 0.152, - 670: 0.154, - 680: 0.150, - 690: 0.144, - 700: 0.136, - 710: 0.132, - 720: 0.135, - 730: 0.147 - }), - ('foliage', { - 380: 0.051, - 390: 0.054, - 400: 0.056, - 410: 0.057, - 420: 0.058, - 430: 0.059, - 440: 0.060, - 450: 0.061, - 460: 0.062, - 470: 0.063, - 480: 0.065, - 490: 0.067, - 500: 0.075, - 510: 0.101, - 520: 0.145, - 530: 0.178, - 540: 0.184, - 550: 0.170, - 560: 0.149, - 570: 0.133, - 580: 0.122, - 590: 0.115, - 600: 0.109, - 610: 0.105, - 620: 0.104, - 630: 0.106, - 640: 0.109, - 650: 0.112, - 660: 0.114, - 670: 0.114, - 680: 0.112, - 690: 0.112, - 700: 0.115, - 710: 0.120, - 720: 0.125, - 730: 0.13 - }), - ('blue flower', { - 380: 0.144, - 390: 0.198, - 400: 0.294, - 410: 0.375, - 420: 0.408, - 430: 0.421, - 440: 0.426, - 450: 0.426, - 460: 0.419, - 470: 0.403, - 480: 0.379, - 490: 0.346, - 500: 0.311, - 510: 0.281, - 520: 0.254, - 530: 0.229, - 540: 0.214, - 550: 0.208, - 560: 0.202, - 570: 0.194, - 580: 0.193, - 590: 0.200, - 600: 0.214, - 610: 0.230, - 620: 0.241, - 630: 0.254, - 640: 0.279, - 650: 0.313, - 660: 0.348, - 670: 0.366, - 680: 0.366, - 690: 0.359, - 700: 0.358, - 710: 0.365, - 720: 0.377, - 730: 0.398 - }), - ('bluish green', { - 380: 0.136, - 390: 0.179, - 400: 0.247, - 410: 0.297, - 420: 0.320, - 430: 0.337, - 440: 0.355, - 450: 0.381, - 460: 0.419, - 470: 0.466, - 480: 0.510, - 490: 0.546, - 500: 0.567, - 510: 0.574, - 520: 0.569, - 530: 0.551, - 540: 0.524, - 550: 0.488, - 560: 0.445, - 570: 0.400, - 580: 0.350, - 590: 0.299, - 600: 0.252, - 610: 0.221, - 620: 0.204, - 630: 0.196, - 640: 0.191, - 650: 0.188, - 660: 0.191, - 670: 0.199, - 680: 0.212, - 690: 0.223, - 700: 0.232, - 710: 0.233, - 720: 0.229, - 730: 0.229 - }), - ('orange', { - 380: 0.054, - 390: 0.054, - 400: 0.053, - 410: 0.054, - 420: 0.054, - 430: 0.055, - 440: 0.055, - 450: 0.055, - 460: 0.056, - 470: 0.057, - 480: 0.058, - 490: 0.061, - 500: 0.068, - 510: 0.089, - 520: 0.125, - 530: 0.154, - 540: 0.174, - 550: 0.199, - 560: 0.248, - 570: 0.335, - 580: 0.444, - 590: 0.538, - 600: 0.587, - 610: 0.595, - 620: 0.591, - 630: 0.587, - 640: 0.584, - 650: 0.584, - 660: 0.590, - 670: 0.603, - 680: 0.620, - 690: 0.639, - 700: 0.655, - 710: 0.663, - 720: 0.663, - 730: 0.667 - }), - ('purplish blue', { - 380: 0.122, - 390: 0.164, - 400: 0.229, - 410: 0.286, - 420: 0.327, - 430: 0.361, - 440: 0.388, - 450: 0.400, - 460: 0.392, - 470: 0.362, - 480: 0.316, - 490: 0.260, - 500: 0.209, - 510: 0.168, - 520: 0.138, - 530: 0.117, - 540: 0.104, - 550: 0.096, - 560: 0.090, - 570: 0.086, - 580: 0.084, - 590: 0.084, - 600: 0.084, - 610: 0.084, - 620: 0.084, - 630: 0.085, - 640: 0.090, - 650: 0.098, - 660: 0.109, - 670: 0.123, - 680: 0.143, - 690: 0.169, - 700: 0.205, - 710: 0.244, - 720: 0.287, - 730: 0.332 - }), - ('moderate red', { - 380: 0.096, - 390: 0.115, - 400: 0.131, - 410: 0.135, - 420: 0.133, - 430: 0.132, - 440: 0.130, - 450: 0.128, - 460: 0.125, - 470: 0.120, - 480: 0.115, - 490: 0.110, - 500: 0.105, - 510: 0.100, - 520: 0.095, - 530: 0.093, - 540: 0.092, - 550: 0.093, - 560: 0.096, - 570: 0.108, - 580: 0.156, - 590: 0.265, - 600: 0.399, - 610: 0.500, - 620: 0.556, - 630: 0.579, - 640: 0.588, - 650: 0.591, - 660: 0.593, - 670: 0.594, - 680: 0.598, - 690: 0.602, - 700: 0.607, - 710: 0.609, - 720: 0.609, - 730: 0.61 - }), - ('purple', { - 380: 0.092, - 390: 0.116, - 400: 0.146, - 410: 0.169, - 420: 0.178, - 430: 0.173, - 440: 0.158, - 450: 0.139, - 460: 0.119, - 470: 0.101, - 480: 0.087, - 490: 0.075, - 500: 0.066, - 510: 0.060, - 520: 0.056, - 530: 0.053, - 540: 0.051, - 550: 0.051, - 560: 0.052, - 570: 0.052, - 580: 0.051, - 590: 0.052, - 600: 0.058, - 610: 0.073, - 620: 0.096, - 630: 0.119, - 640: 0.141, - 650: 0.166, - 660: 0.194, - 670: 0.227, - 680: 0.265, - 690: 0.309, - 700: 0.355, - 710: 0.396, - 720: 0.436, - 730: 0.478 - }), - ('yellow green', { - 380: 0.061, - 390: 0.061, - 400: 0.062, - 410: 0.063, - 420: 0.064, - 430: 0.066, - 440: 0.069, - 450: 0.075, - 460: 0.085, - 470: 0.105, - 480: 0.139, - 490: 0.192, - 500: 0.271, - 510: 0.376, - 520: 0.476, - 530: 0.531, - 540: 0.549, - 550: 0.546, - 560: 0.528, - 570: 0.504, - 580: 0.471, - 590: 0.428, - 600: 0.381, - 610: 0.347, - 620: 0.327, - 630: 0.318, - 640: 0.312, - 650: 0.310, - 660: 0.314, - 670: 0.327, - 680: 0.345, - 690: 0.363, - 700: 0.376, - 710: 0.381, - 720: 0.378, - 730: 0.379 - }), - ('orange yellow', { - 380: 0.063, - 390: 0.063, - 400: 0.063, - 410: 0.064, - 420: 0.064, - 430: 0.064, - 440: 0.065, - 450: 0.066, - 460: 0.067, - 470: 0.068, - 480: 0.071, - 490: 0.076, - 500: 0.087, - 510: 0.125, - 520: 0.206, - 530: 0.305, - 540: 0.383, - 550: 0.431, - 560: 0.469, - 570: 0.518, - 580: 0.568, - 590: 0.607, - 600: 0.628, - 610: 0.637, - 620: 0.640, - 630: 0.642, - 640: 0.645, - 650: 0.648, - 660: 0.651, - 670: 0.653, - 680: 0.657, - 690: 0.664, - 700: 0.673, - 710: 0.680, - 720: 0.684, - 730: 0.688 - }), - ('blue', { - 380: 0.066, - 390: 0.079, - 400: 0.102, - 410: 0.146, - 420: 0.200, - 430: 0.244, - 440: 0.282, - 450: 0.309, - 460: 0.308, - 470: 0.278, - 480: 0.231, - 490: 0.178, - 500: 0.130, - 510: 0.094, - 520: 0.070, - 530: 0.054, - 540: 0.046, - 550: 0.042, - 560: 0.039, - 570: 0.038, - 580: 0.038, - 590: 0.038, - 600: 0.038, - 610: 0.039, - 620: 0.039, - 630: 0.040, - 640: 0.041, - 650: 0.042, - 660: 0.044, - 670: 0.045, - 680: 0.046, - 690: 0.046, - 700: 0.048, - 710: 0.052, - 720: 0.057, - 730: 0.065 - }), - ('green', { - 380: 0.052, - 390: 0.053, - 400: 0.054, - 410: 0.055, - 420: 0.057, - 430: 0.059, - 440: 0.061, - 450: 0.066, - 460: 0.075, - 470: 0.093, - 480: 0.125, - 490: 0.178, - 500: 0.246, - 510: 0.307, - 520: 0.337, - 530: 0.334, - 540: 0.317, - 550: 0.293, - 560: 0.262, - 570: 0.230, - 580: 0.198, - 590: 0.165, - 600: 0.135, - 610: 0.115, - 620: 0.104, - 630: 0.098, - 640: 0.094, - 650: 0.092, - 660: 0.093, - 670: 0.097, - 680: 0.102, - 690: 0.108, - 700: 0.113, - 710: 0.115, - 720: 0.114, - 730: 0.114 - }), - ('red', { - 380: 0.050, - 390: 0.049, - 400: 0.048, - 410: 0.047, - 420: 0.047, - 430: 0.047, - 440: 0.047, - 450: 0.047, - 460: 0.046, - 470: 0.045, - 480: 0.044, - 490: 0.044, - 500: 0.045, - 510: 0.046, - 520: 0.047, - 530: 0.048, - 540: 0.049, - 550: 0.050, - 560: 0.054, - 570: 0.060, - 580: 0.072, - 590: 0.104, - 600: 0.178, - 610: 0.312, - 620: 0.467, - 630: 0.581, - 640: 0.644, - 650: 0.675, - 660: 0.690, - 670: 0.698, - 680: 0.706, - 690: 0.715, - 700: 0.724, - 710: 0.730, - 720: 0.734, - 730: 0.738 - }), - ('yellow', { - 380: 0.058, - 390: 0.054, - 400: 0.052, - 410: 0.052, - 420: 0.053, - 430: 0.054, - 440: 0.056, - 450: 0.059, - 460: 0.067, - 470: 0.081, - 480: 0.107, - 490: 0.152, - 500: 0.225, - 510: 0.336, - 520: 0.462, - 530: 0.559, - 540: 0.616, - 550: 0.650, - 560: 0.672, - 570: 0.694, - 580: 0.710, - 590: 0.723, - 600: 0.731, - 610: 0.739, - 620: 0.746, - 630: 0.752, - 640: 0.758, - 650: 0.764, - 660: 0.769, - 670: 0.771, - 680: 0.776, - 690: 0.782, - 700: 0.790, - 710: 0.796, - 720: 0.799, - 730: 0.804 - }), - ('magenta', { - 380: 0.145, - 390: 0.195, - 400: 0.283, - 410: 0.346, - 420: 0.362, - 430: 0.354, - 440: 0.334, - 450: 0.306, - 460: 0.276, - 470: 0.248, - 480: 0.218, - 490: 0.190, - 500: 0.168, - 510: 0.149, - 520: 0.127, - 530: 0.107, - 540: 0.100, - 550: 0.102, - 560: 0.104, - 570: 0.109, - 580: 0.137, - 590: 0.200, - 600: 0.290, - 610: 0.400, - 620: 0.516, - 630: 0.615, - 640: 0.687, - 650: 0.732, - 660: 0.760, - 670: 0.774, - 680: 0.783, - 690: 0.793, - 700: 0.803, - 710: 0.812, - 720: 0.817, - 730: 0.825 - }), - ('cyan', { - 380: 0.108, - 390: 0.141, - 400: 0.192, - 410: 0.236, - 420: 0.261, - 430: 0.286, - 440: 0.317, - 450: 0.353, - 460: 0.390, - 470: 0.426, - 480: 0.446, - 490: 0.444, - 500: 0.423, - 510: 0.385, - 520: 0.337, - 530: 0.283, - 540: 0.231, - 550: 0.185, - 560: 0.146, - 570: 0.118, - 580: 0.101, - 590: 0.090, - 600: 0.082, - 610: 0.076, - 620: 0.074, - 630: 0.073, - 640: 0.073, - 650: 0.074, - 660: 0.076, - 670: 0.077, - 680: 0.076, - 690: 0.075, - 700: 0.073, - 710: 0.072, - 720: 0.074, - 730: 0.079 - }), - ('white 9.5 (.05 D)', { - 380: 0.189, - 390: 0.255, - 400: 0.423, - 410: 0.660, - 420: 0.811, - 430: 0.862, - 440: 0.877, - 450: 0.884, - 460: 0.891, - 470: 0.896, - 480: 0.899, - 490: 0.904, - 500: 0.907, - 510: 0.909, - 520: 0.911, - 530: 0.910, - 540: 0.911, - 550: 0.914, - 560: 0.913, - 570: 0.916, - 580: 0.915, - 590: 0.916, - 600: 0.914, - 610: 0.915, - 620: 0.918, - 630: 0.919, - 640: 0.921, - 650: 0.923, - 660: 0.924, - 670: 0.922, - 680: 0.922, - 690: 0.925, - 700: 0.927, - 710: 0.930, - 720: 0.930, - 730: 0.933 - }), - ('neutral 8 (.23 D)', { - 380: 0.171, - 390: 0.232, - 400: 0.365, - 410: 0.507, - 420: 0.567, - 430: 0.583, - 440: 0.588, - 450: 0.590, - 460: 0.591, - 470: 0.590, - 480: 0.588, - 490: 0.588, - 500: 0.589, - 510: 0.589, - 520: 0.591, - 530: 0.590, - 540: 0.590, - 550: 0.590, - 560: 0.589, - 570: 0.591, - 580: 0.590, - 590: 0.590, - 600: 0.587, - 610: 0.585, - 620: 0.583, - 630: 0.580, - 640: 0.578, - 650: 0.576, - 660: 0.574, - 670: 0.572, - 680: 0.571, - 690: 0.569, - 700: 0.568, - 710: 0.568, - 720: 0.566, - 730: 0.566 - }), - ('neutral 6.5 (.44 D)', { - 380: 0.144, - 390: 0.192, - 400: 0.272, - 410: 0.331, - 420: 0.350, - 430: 0.357, - 440: 0.361, - 450: 0.363, - 460: 0.363, - 470: 0.361, - 480: 0.359, - 490: 0.358, - 500: 0.358, - 510: 0.359, - 520: 0.360, - 530: 0.360, - 540: 0.361, - 550: 0.361, - 560: 0.360, - 570: 0.362, - 580: 0.362, - 590: 0.361, - 600: 0.359, - 610: 0.358, - 620: 0.355, - 630: 0.352, - 640: 0.350, - 650: 0.348, - 660: 0.345, - 670: 0.343, - 680: 0.340, - 690: 0.338, - 700: 0.335, - 710: 0.334, - 720: 0.332, - 730: 0.331 - }), - ('neutral 5 (.70 D)', { - 380: 0.105, - 390: 0.131, - 400: 0.163, - 410: 0.180, - 420: 0.186, - 430: 0.190, - 440: 0.193, - 450: 0.194, - 460: 0.194, - 470: 0.192, - 480: 0.191, - 490: 0.191, - 500: 0.191, - 510: 0.192, - 520: 0.192, - 530: 0.192, - 540: 0.192, - 550: 0.192, - 560: 0.192, - 570: 0.193, - 580: 0.192, - 590: 0.192, - 600: 0.191, - 610: 0.189, - 620: 0.188, - 630: 0.186, - 640: 0.184, - 650: 0.182, - 660: 0.181, - 670: 0.179, - 680: 0.178, - 690: 0.176, - 700: 0.174, - 710: 0.173, - 720: 0.172, - 730: 0.171 - }), - ('neutral 3.5 (1.05 D)', { - 380: 0.068, - 390: 0.077, - 400: 0.084, - 410: 0.087, - 420: 0.089, - 430: 0.090, - 440: 0.092, - 450: 0.092, - 460: 0.091, - 470: 0.090, - 480: 0.090, - 490: 0.090, - 500: 0.090, - 510: 0.090, - 520: 0.090, - 530: 0.090, - 540: 0.090, - 550: 0.090, - 560: 0.090, - 570: 0.090, - 580: 0.090, - 590: 0.089, - 600: 0.089, - 610: 0.088, - 620: 0.087, - 630: 0.086, - 640: 0.086, - 650: 0.085, - 660: 0.084, - 670: 0.084, - 680: 0.083, - 690: 0.083, - 700: 0.082, - 710: 0.081, - 720: 0.081, - 730: 0.081 - }), - ('black 2 (1.5 D)', { - 380: 0.031, - 390: 0.032, - 400: 0.032, - 410: 0.033, - 420: 0.033, - 430: 0.033, - 440: 0.033, - 450: 0.033, - 460: 0.032, - 470: 0.032, - 480: 0.032, - 490: 0.032, - 500: 0.032, - 510: 0.032, - 520: 0.032, - 530: 0.032, - 540: 0.032, - 550: 0.032, - 560: 0.032, - 570: 0.032, - 580: 0.032, - 590: 0.032, - 600: 0.032, - 610: 0.032, - 620: 0.032, - 630: 0.032, - 640: 0.032, - 650: 0.032, - 660: 0.032, - 670: 0.032, - 680: 0.032, - 690: 0.032, - 700: 0.032, - 710: 0.032, - 720: 0.032, - 730: 0.033 - }), -)) +DATA_BABELCOLOR_AVERAGE: Dict = dict( + [ + ( + "dark skin", + { + 380: 0.055, + 390: 0.058, + 400: 0.061, + 410: 0.062, + 420: 0.062, + 430: 0.062, + 440: 0.062, + 450: 0.062, + 460: 0.062, + 470: 0.062, + 480: 0.062, + 490: 0.063, + 500: 0.065, + 510: 0.070, + 520: 0.076, + 530: 0.079, + 540: 0.081, + 550: 0.084, + 560: 0.091, + 570: 0.103, + 580: 0.119, + 590: 0.134, + 600: 0.143, + 610: 0.147, + 620: 0.151, + 630: 0.158, + 640: 0.168, + 650: 0.179, + 660: 0.188, + 670: 0.190, + 680: 0.186, + 690: 0.181, + 700: 0.182, + 710: 0.187, + 720: 0.196, + 730: 0.209, + }, + ), + ( + "light skin", + { + 380: 0.117, + 390: 0.143, + 400: 0.175, + 410: 0.191, + 420: 0.196, + 430: 0.199, + 440: 0.204, + 450: 0.213, + 460: 0.228, + 470: 0.251, + 480: 0.280, + 490: 0.309, + 500: 0.329, + 510: 0.333, + 520: 0.315, + 530: 0.286, + 540: 0.273, + 550: 0.276, + 560: 0.277, + 570: 0.289, + 580: 0.339, + 590: 0.420, + 600: 0.488, + 610: 0.525, + 620: 0.546, + 630: 0.562, + 640: 0.578, + 650: 0.595, + 660: 0.612, + 670: 0.625, + 680: 0.638, + 690: 0.656, + 700: 0.678, + 710: 0.700, + 720: 0.717, + 730: 0.734, + }, + ), + ( + "blue sky", + { + 380: 0.130, + 390: 0.177, + 400: 0.251, + 410: 0.306, + 420: 0.324, + 430: 0.330, + 440: 0.333, + 450: 0.331, + 460: 0.323, + 470: 0.311, + 480: 0.298, + 490: 0.285, + 500: 0.269, + 510: 0.250, + 520: 0.231, + 530: 0.214, + 540: 0.199, + 550: 0.185, + 560: 0.169, + 570: 0.157, + 580: 0.149, + 590: 0.145, + 600: 0.142, + 610: 0.141, + 620: 0.141, + 630: 0.141, + 640: 0.143, + 650: 0.147, + 660: 0.152, + 670: 0.154, + 680: 0.150, + 690: 0.144, + 700: 0.136, + 710: 0.132, + 720: 0.135, + 730: 0.147, + }, + ), + ( + "foliage", + { + 380: 0.051, + 390: 0.054, + 400: 0.056, + 410: 0.057, + 420: 0.058, + 430: 0.059, + 440: 0.060, + 450: 0.061, + 460: 0.062, + 470: 0.063, + 480: 0.065, + 490: 0.067, + 500: 0.075, + 510: 0.101, + 520: 0.145, + 530: 0.178, + 540: 0.184, + 550: 0.170, + 560: 0.149, + 570: 0.133, + 580: 0.122, + 590: 0.115, + 600: 0.109, + 610: 0.105, + 620: 0.104, + 630: 0.106, + 640: 0.109, + 650: 0.112, + 660: 0.114, + 670: 0.114, + 680: 0.112, + 690: 0.112, + 700: 0.115, + 710: 0.120, + 720: 0.125, + 730: 0.13, + }, + ), + ( + "blue flower", + { + 380: 0.144, + 390: 0.198, + 400: 0.294, + 410: 0.375, + 420: 0.408, + 430: 0.421, + 440: 0.426, + 450: 0.426, + 460: 0.419, + 470: 0.403, + 480: 0.379, + 490: 0.346, + 500: 0.311, + 510: 0.281, + 520: 0.254, + 530: 0.229, + 540: 0.214, + 550: 0.208, + 560: 0.202, + 570: 0.194, + 580: 0.193, + 590: 0.200, + 600: 0.214, + 610: 0.230, + 620: 0.241, + 630: 0.254, + 640: 0.279, + 650: 0.313, + 660: 0.348, + 670: 0.366, + 680: 0.366, + 690: 0.359, + 700: 0.358, + 710: 0.365, + 720: 0.377, + 730: 0.398, + }, + ), + ( + "bluish green", + { + 380: 0.136, + 390: 0.179, + 400: 0.247, + 410: 0.297, + 420: 0.320, + 430: 0.337, + 440: 0.355, + 450: 0.381, + 460: 0.419, + 470: 0.466, + 480: 0.510, + 490: 0.546, + 500: 0.567, + 510: 0.574, + 520: 0.569, + 530: 0.551, + 540: 0.524, + 550: 0.488, + 560: 0.445, + 570: 0.400, + 580: 0.350, + 590: 0.299, + 600: 0.252, + 610: 0.221, + 620: 0.204, + 630: 0.196, + 640: 0.191, + 650: 0.188, + 660: 0.191, + 670: 0.199, + 680: 0.212, + 690: 0.223, + 700: 0.232, + 710: 0.233, + 720: 0.229, + 730: 0.229, + }, + ), + ( + "orange", + { + 380: 0.054, + 390: 0.054, + 400: 0.053, + 410: 0.054, + 420: 0.054, + 430: 0.055, + 440: 0.055, + 450: 0.055, + 460: 0.056, + 470: 0.057, + 480: 0.058, + 490: 0.061, + 500: 0.068, + 510: 0.089, + 520: 0.125, + 530: 0.154, + 540: 0.174, + 550: 0.199, + 560: 0.248, + 570: 0.335, + 580: 0.444, + 590: 0.538, + 600: 0.587, + 610: 0.595, + 620: 0.591, + 630: 0.587, + 640: 0.584, + 650: 0.584, + 660: 0.590, + 670: 0.603, + 680: 0.620, + 690: 0.639, + 700: 0.655, + 710: 0.663, + 720: 0.663, + 730: 0.667, + }, + ), + ( + "purplish blue", + { + 380: 0.122, + 390: 0.164, + 400: 0.229, + 410: 0.286, + 420: 0.327, + 430: 0.361, + 440: 0.388, + 450: 0.400, + 460: 0.392, + 470: 0.362, + 480: 0.316, + 490: 0.260, + 500: 0.209, + 510: 0.168, + 520: 0.138, + 530: 0.117, + 540: 0.104, + 550: 0.096, + 560: 0.090, + 570: 0.086, + 580: 0.084, + 590: 0.084, + 600: 0.084, + 610: 0.084, + 620: 0.084, + 630: 0.085, + 640: 0.090, + 650: 0.098, + 660: 0.109, + 670: 0.123, + 680: 0.143, + 690: 0.169, + 700: 0.205, + 710: 0.244, + 720: 0.287, + 730: 0.332, + }, + ), + ( + "moderate red", + { + 380: 0.096, + 390: 0.115, + 400: 0.131, + 410: 0.135, + 420: 0.133, + 430: 0.132, + 440: 0.130, + 450: 0.128, + 460: 0.125, + 470: 0.120, + 480: 0.115, + 490: 0.110, + 500: 0.105, + 510: 0.100, + 520: 0.095, + 530: 0.093, + 540: 0.092, + 550: 0.093, + 560: 0.096, + 570: 0.108, + 580: 0.156, + 590: 0.265, + 600: 0.399, + 610: 0.500, + 620: 0.556, + 630: 0.579, + 640: 0.588, + 650: 0.591, + 660: 0.593, + 670: 0.594, + 680: 0.598, + 690: 0.602, + 700: 0.607, + 710: 0.609, + 720: 0.609, + 730: 0.61, + }, + ), + ( + "purple", + { + 380: 0.092, + 390: 0.116, + 400: 0.146, + 410: 0.169, + 420: 0.178, + 430: 0.173, + 440: 0.158, + 450: 0.139, + 460: 0.119, + 470: 0.101, + 480: 0.087, + 490: 0.075, + 500: 0.066, + 510: 0.060, + 520: 0.056, + 530: 0.053, + 540: 0.051, + 550: 0.051, + 560: 0.052, + 570: 0.052, + 580: 0.051, + 590: 0.052, + 600: 0.058, + 610: 0.073, + 620: 0.096, + 630: 0.119, + 640: 0.141, + 650: 0.166, + 660: 0.194, + 670: 0.227, + 680: 0.265, + 690: 0.309, + 700: 0.355, + 710: 0.396, + 720: 0.436, + 730: 0.478, + }, + ), + ( + "yellow green", + { + 380: 0.061, + 390: 0.061, + 400: 0.062, + 410: 0.063, + 420: 0.064, + 430: 0.066, + 440: 0.069, + 450: 0.075, + 460: 0.085, + 470: 0.105, + 480: 0.139, + 490: 0.192, + 500: 0.271, + 510: 0.376, + 520: 0.476, + 530: 0.531, + 540: 0.549, + 550: 0.546, + 560: 0.528, + 570: 0.504, + 580: 0.471, + 590: 0.428, + 600: 0.381, + 610: 0.347, + 620: 0.327, + 630: 0.318, + 640: 0.312, + 650: 0.310, + 660: 0.314, + 670: 0.327, + 680: 0.345, + 690: 0.363, + 700: 0.376, + 710: 0.381, + 720: 0.378, + 730: 0.379, + }, + ), + ( + "orange yellow", + { + 380: 0.063, + 390: 0.063, + 400: 0.063, + 410: 0.064, + 420: 0.064, + 430: 0.064, + 440: 0.065, + 450: 0.066, + 460: 0.067, + 470: 0.068, + 480: 0.071, + 490: 0.076, + 500: 0.087, + 510: 0.125, + 520: 0.206, + 530: 0.305, + 540: 0.383, + 550: 0.431, + 560: 0.469, + 570: 0.518, + 580: 0.568, + 590: 0.607, + 600: 0.628, + 610: 0.637, + 620: 0.640, + 630: 0.642, + 640: 0.645, + 650: 0.648, + 660: 0.651, + 670: 0.653, + 680: 0.657, + 690: 0.664, + 700: 0.673, + 710: 0.680, + 720: 0.684, + 730: 0.688, + }, + ), + ( + "blue", + { + 380: 0.066, + 390: 0.079, + 400: 0.102, + 410: 0.146, + 420: 0.200, + 430: 0.244, + 440: 0.282, + 450: 0.309, + 460: 0.308, + 470: 0.278, + 480: 0.231, + 490: 0.178, + 500: 0.130, + 510: 0.094, + 520: 0.070, + 530: 0.054, + 540: 0.046, + 550: 0.042, + 560: 0.039, + 570: 0.038, + 580: 0.038, + 590: 0.038, + 600: 0.038, + 610: 0.039, + 620: 0.039, + 630: 0.040, + 640: 0.041, + 650: 0.042, + 660: 0.044, + 670: 0.045, + 680: 0.046, + 690: 0.046, + 700: 0.048, + 710: 0.052, + 720: 0.057, + 730: 0.065, + }, + ), + ( + "green", + { + 380: 0.052, + 390: 0.053, + 400: 0.054, + 410: 0.055, + 420: 0.057, + 430: 0.059, + 440: 0.061, + 450: 0.066, + 460: 0.075, + 470: 0.093, + 480: 0.125, + 490: 0.178, + 500: 0.246, + 510: 0.307, + 520: 0.337, + 530: 0.334, + 540: 0.317, + 550: 0.293, + 560: 0.262, + 570: 0.230, + 580: 0.198, + 590: 0.165, + 600: 0.135, + 610: 0.115, + 620: 0.104, + 630: 0.098, + 640: 0.094, + 650: 0.092, + 660: 0.093, + 670: 0.097, + 680: 0.102, + 690: 0.108, + 700: 0.113, + 710: 0.115, + 720: 0.114, + 730: 0.114, + }, + ), + ( + "red", + { + 380: 0.050, + 390: 0.049, + 400: 0.048, + 410: 0.047, + 420: 0.047, + 430: 0.047, + 440: 0.047, + 450: 0.047, + 460: 0.046, + 470: 0.045, + 480: 0.044, + 490: 0.044, + 500: 0.045, + 510: 0.046, + 520: 0.047, + 530: 0.048, + 540: 0.049, + 550: 0.050, + 560: 0.054, + 570: 0.060, + 580: 0.072, + 590: 0.104, + 600: 0.178, + 610: 0.312, + 620: 0.467, + 630: 0.581, + 640: 0.644, + 650: 0.675, + 660: 0.690, + 670: 0.698, + 680: 0.706, + 690: 0.715, + 700: 0.724, + 710: 0.730, + 720: 0.734, + 730: 0.738, + }, + ), + ( + "yellow", + { + 380: 0.058, + 390: 0.054, + 400: 0.052, + 410: 0.052, + 420: 0.053, + 430: 0.054, + 440: 0.056, + 450: 0.059, + 460: 0.067, + 470: 0.081, + 480: 0.107, + 490: 0.152, + 500: 0.225, + 510: 0.336, + 520: 0.462, + 530: 0.559, + 540: 0.616, + 550: 0.650, + 560: 0.672, + 570: 0.694, + 580: 0.710, + 590: 0.723, + 600: 0.731, + 610: 0.739, + 620: 0.746, + 630: 0.752, + 640: 0.758, + 650: 0.764, + 660: 0.769, + 670: 0.771, + 680: 0.776, + 690: 0.782, + 700: 0.790, + 710: 0.796, + 720: 0.799, + 730: 0.804, + }, + ), + ( + "magenta", + { + 380: 0.145, + 390: 0.195, + 400: 0.283, + 410: 0.346, + 420: 0.362, + 430: 0.354, + 440: 0.334, + 450: 0.306, + 460: 0.276, + 470: 0.248, + 480: 0.218, + 490: 0.190, + 500: 0.168, + 510: 0.149, + 520: 0.127, + 530: 0.107, + 540: 0.100, + 550: 0.102, + 560: 0.104, + 570: 0.109, + 580: 0.137, + 590: 0.200, + 600: 0.290, + 610: 0.400, + 620: 0.516, + 630: 0.615, + 640: 0.687, + 650: 0.732, + 660: 0.760, + 670: 0.774, + 680: 0.783, + 690: 0.793, + 700: 0.803, + 710: 0.812, + 720: 0.817, + 730: 0.825, + }, + ), + ( + "cyan", + { + 380: 0.108, + 390: 0.141, + 400: 0.192, + 410: 0.236, + 420: 0.261, + 430: 0.286, + 440: 0.317, + 450: 0.353, + 460: 0.390, + 470: 0.426, + 480: 0.446, + 490: 0.444, + 500: 0.423, + 510: 0.385, + 520: 0.337, + 530: 0.283, + 540: 0.231, + 550: 0.185, + 560: 0.146, + 570: 0.118, + 580: 0.101, + 590: 0.090, + 600: 0.082, + 610: 0.076, + 620: 0.074, + 630: 0.073, + 640: 0.073, + 650: 0.074, + 660: 0.076, + 670: 0.077, + 680: 0.076, + 690: 0.075, + 700: 0.073, + 710: 0.072, + 720: 0.074, + 730: 0.079, + }, + ), + ( + "white 9.5 (.05 D)", + { + 380: 0.189, + 390: 0.255, + 400: 0.423, + 410: 0.660, + 420: 0.811, + 430: 0.862, + 440: 0.877, + 450: 0.884, + 460: 0.891, + 470: 0.896, + 480: 0.899, + 490: 0.904, + 500: 0.907, + 510: 0.909, + 520: 0.911, + 530: 0.910, + 540: 0.911, + 550: 0.914, + 560: 0.913, + 570: 0.916, + 580: 0.915, + 590: 0.916, + 600: 0.914, + 610: 0.915, + 620: 0.918, + 630: 0.919, + 640: 0.921, + 650: 0.923, + 660: 0.924, + 670: 0.922, + 680: 0.922, + 690: 0.925, + 700: 0.927, + 710: 0.930, + 720: 0.930, + 730: 0.933, + }, + ), + ( + "neutral 8 (.23 D)", + { + 380: 0.171, + 390: 0.232, + 400: 0.365, + 410: 0.507, + 420: 0.567, + 430: 0.583, + 440: 0.588, + 450: 0.590, + 460: 0.591, + 470: 0.590, + 480: 0.588, + 490: 0.588, + 500: 0.589, + 510: 0.589, + 520: 0.591, + 530: 0.590, + 540: 0.590, + 550: 0.590, + 560: 0.589, + 570: 0.591, + 580: 0.590, + 590: 0.590, + 600: 0.587, + 610: 0.585, + 620: 0.583, + 630: 0.580, + 640: 0.578, + 650: 0.576, + 660: 0.574, + 670: 0.572, + 680: 0.571, + 690: 0.569, + 700: 0.568, + 710: 0.568, + 720: 0.566, + 730: 0.566, + }, + ), + ( + "neutral 6.5 (.44 D)", + { + 380: 0.144, + 390: 0.192, + 400: 0.272, + 410: 0.331, + 420: 0.350, + 430: 0.357, + 440: 0.361, + 450: 0.363, + 460: 0.363, + 470: 0.361, + 480: 0.359, + 490: 0.358, + 500: 0.358, + 510: 0.359, + 520: 0.360, + 530: 0.360, + 540: 0.361, + 550: 0.361, + 560: 0.360, + 570: 0.362, + 580: 0.362, + 590: 0.361, + 600: 0.359, + 610: 0.358, + 620: 0.355, + 630: 0.352, + 640: 0.350, + 650: 0.348, + 660: 0.345, + 670: 0.343, + 680: 0.340, + 690: 0.338, + 700: 0.335, + 710: 0.334, + 720: 0.332, + 730: 0.331, + }, + ), + ( + "neutral 5 (.70 D)", + { + 380: 0.105, + 390: 0.131, + 400: 0.163, + 410: 0.180, + 420: 0.186, + 430: 0.190, + 440: 0.193, + 450: 0.194, + 460: 0.194, + 470: 0.192, + 480: 0.191, + 490: 0.191, + 500: 0.191, + 510: 0.192, + 520: 0.192, + 530: 0.192, + 540: 0.192, + 550: 0.192, + 560: 0.192, + 570: 0.193, + 580: 0.192, + 590: 0.192, + 600: 0.191, + 610: 0.189, + 620: 0.188, + 630: 0.186, + 640: 0.184, + 650: 0.182, + 660: 0.181, + 670: 0.179, + 680: 0.178, + 690: 0.176, + 700: 0.174, + 710: 0.173, + 720: 0.172, + 730: 0.171, + }, + ), + ( + "neutral 3.5 (1.05 D)", + { + 380: 0.068, + 390: 0.077, + 400: 0.084, + 410: 0.087, + 420: 0.089, + 430: 0.090, + 440: 0.092, + 450: 0.092, + 460: 0.091, + 470: 0.090, + 480: 0.090, + 490: 0.090, + 500: 0.090, + 510: 0.090, + 520: 0.090, + 530: 0.090, + 540: 0.090, + 550: 0.090, + 560: 0.090, + 570: 0.090, + 580: 0.090, + 590: 0.089, + 600: 0.089, + 610: 0.088, + 620: 0.087, + 630: 0.086, + 640: 0.086, + 650: 0.085, + 660: 0.084, + 670: 0.084, + 680: 0.083, + 690: 0.083, + 700: 0.082, + 710: 0.081, + 720: 0.081, + 730: 0.081, + }, + ), + ( + "black 2 (1.5 D)", + { + 380: 0.031, + 390: 0.032, + 400: 0.032, + 410: 0.033, + 420: 0.033, + 430: 0.033, + 440: 0.033, + 450: 0.033, + 460: 0.032, + 470: 0.032, + 480: 0.032, + 490: 0.032, + 500: 0.032, + 510: 0.032, + 520: 0.032, + 530: 0.032, + 540: 0.032, + 550: 0.032, + 560: 0.032, + 570: 0.032, + 580: 0.032, + 590: 0.032, + 600: 0.032, + 610: 0.032, + 620: 0.032, + 630: 0.032, + 640: 0.032, + 650: 0.032, + 660: 0.032, + 670: 0.032, + 680: 0.032, + 690: 0.032, + 700: 0.032, + 710: 0.032, + 720: 0.032, + 730: 0.033, + }, + ), + ] +) -SDS_BABELCOLOR_AVERAGE = OrderedDict( - (key, SpectralDistribution(value, name=key)) - for key, value in DATA_BABELCOLOR_AVERAGE.items()) +SDS_BABELCOLOR_AVERAGE: LazyCaseInsensitiveMapping = ( + LazyCaseInsensitiveMapping( + (key, partial(SpectralDistribution, value, name=key)) + for key, value in DATA_BABELCOLOR_AVERAGE.items() + ) +) """ Average data derived from measurements of 30 *ColorChecker Classic* charts. References ---------- :cite:`BabelColor2012b`, :cite:`BabelColor2012c`, - -SDS_BABELCOLOR_AVERAGE : dict """ -DATA_COLORCHECKER_N_OHTA = OrderedDict(( - ('dark skin', { - 380: 0.048, - 385: 0.051, - 390: 0.055, - 395: 0.060, - 400: 0.065, - 405: 0.068, - 410: 0.068, - 415: 0.067, - 420: 0.064, - 425: 0.062, - 430: 0.059, - 435: 0.057, - 440: 0.055, - 445: 0.054, - 450: 0.053, - 455: 0.053, - 460: 0.052, - 465: 0.052, - 470: 0.052, - 475: 0.053, - 480: 0.054, - 485: 0.055, - 490: 0.057, - 495: 0.059, - 500: 0.061, - 505: 0.062, - 510: 0.065, - 515: 0.067, - 520: 0.070, - 525: 0.072, - 530: 0.074, - 535: 0.075, - 540: 0.076, - 545: 0.078, - 550: 0.079, - 555: 0.082, - 560: 0.087, - 565: 0.092, - 570: 0.100, - 575: 0.107, - 580: 0.115, - 585: 0.122, - 590: 0.129, - 595: 0.134, - 600: 0.138, - 605: 0.142, - 610: 0.146, - 615: 0.150, - 620: 0.154, - 625: 0.158, - 630: 0.163, - 635: 0.167, - 640: 0.173, - 645: 0.180, - 650: 0.188, - 655: 0.196, - 660: 0.204, - 665: 0.213, - 670: 0.222, - 675: 0.231, - 680: 0.242, - 685: 0.251, - 690: 0.261, - 695: 0.271, - 700: 0.282, - 705: 0.294, - 710: 0.305, - 715: 0.318, - 720: 0.334, - 725: 0.354, - 730: 0.372, - 735: 0.392, - 740: 0.409, - 745: 0.420, - 750: 0.436, - 755: 0.450, - 760: 0.462, - 765: 0.465, - 770: 0.448, - 775: 0.432, - 780: 0.421 - }), - ('light skin', { - 380: 0.103, - 385: 0.120, - 390: 0.141, - 395: 0.163, - 400: 0.182, - 405: 0.192, - 410: 0.197, - 415: 0.199, - 420: 0.201, - 425: 0.203, - 430: 0.205, - 435: 0.208, - 440: 0.212, - 445: 0.217, - 450: 0.224, - 455: 0.231, - 460: 0.240, - 465: 0.251, - 470: 0.262, - 475: 0.273, - 480: 0.282, - 485: 0.289, - 490: 0.293, - 495: 0.296, - 500: 0.301, - 505: 0.310, - 510: 0.321, - 515: 0.326, - 520: 0.322, - 525: 0.310, - 530: 0.298, - 535: 0.291, - 540: 0.292, - 545: 0.297, - 550: 0.300, - 555: 0.298, - 560: 0.295, - 565: 0.295, - 570: 0.305, - 575: 0.326, - 580: 0.358, - 585: 0.397, - 590: 0.435, - 595: 0.468, - 600: 0.494, - 605: 0.514, - 610: 0.530, - 615: 0.541, - 620: 0.550, - 625: 0.557, - 630: 0.564, - 635: 0.569, - 640: 0.574, - 645: 0.582, - 650: 0.590, - 655: 0.597, - 660: 0.605, - 665: 0.614, - 670: 0.624, - 675: 0.637, - 680: 0.652, - 685: 0.668, - 690: 0.682, - 695: 0.697, - 700: 0.713, - 705: 0.728, - 710: 0.745, - 715: 0.753, - 720: 0.762, - 725: 0.774, - 730: 0.783, - 735: 0.788, - 740: 0.791, - 745: 0.787, - 750: 0.789, - 755: 0.794, - 760: 0.801, - 765: 0.799, - 770: 0.771, - 775: 0.747, - 780: 0.734 - }), - ('blue sky', { - 380: 0.113, - 385: 0.138, - 390: 0.174, - 395: 0.219, - 400: 0.266, - 405: 0.300, - 410: 0.320, - 415: 0.330, - 420: 0.336, - 425: 0.337, - 430: 0.337, - 435: 0.337, - 440: 0.335, - 445: 0.334, - 450: 0.331, - 455: 0.327, - 460: 0.322, - 465: 0.316, - 470: 0.310, - 475: 0.302, - 480: 0.293, - 485: 0.285, - 490: 0.276, - 495: 0.268, - 500: 0.260, - 505: 0.251, - 510: 0.243, - 515: 0.234, - 520: 0.225, - 525: 0.215, - 530: 0.208, - 535: 0.203, - 540: 0.198, - 545: 0.195, - 550: 0.191, - 555: 0.188, - 560: 0.183, - 565: 0.177, - 570: 0.172, - 575: 0.167, - 580: 0.163, - 585: 0.160, - 590: 0.157, - 595: 0.153, - 600: 0.150, - 605: 0.147, - 610: 0.144, - 615: 0.141, - 620: 0.137, - 625: 0.133, - 630: 0.130, - 635: 0.126, - 640: 0.123, - 645: 0.120, - 650: 0.118, - 655: 0.115, - 660: 0.112, - 665: 0.110, - 670: 0.108, - 675: 0.106, - 680: 0.105, - 685: 0.104, - 690: 0.104, - 695: 0.103, - 700: 0.103, - 705: 0.102, - 710: 0.102, - 715: 0.102, - 720: 0.102, - 725: 0.102, - 730: 0.102, - 735: 0.104, - 740: 0.104, - 745: 0.104, - 750: 0.104, - 755: 0.106, - 760: 0.106, - 765: 0.107, - 770: 0.110, - 775: 0.115, - 780: 0.12 - }), - ('foliage', { - 380: 0.048, - 385: 0.049, - 390: 0.049, - 395: 0.049, - 400: 0.050, - 405: 0.049, - 410: 0.049, - 415: 0.050, - 420: 0.050, - 425: 0.051, - 430: 0.052, - 435: 0.053, - 440: 0.054, - 445: 0.056, - 450: 0.058, - 455: 0.060, - 460: 0.061, - 465: 0.063, - 470: 0.064, - 475: 0.065, - 480: 0.067, - 485: 0.068, - 490: 0.070, - 495: 0.072, - 500: 0.078, - 505: 0.088, - 510: 0.106, - 515: 0.130, - 520: 0.155, - 525: 0.173, - 530: 0.181, - 535: 0.182, - 540: 0.177, - 545: 0.168, - 550: 0.157, - 555: 0.147, - 560: 0.137, - 565: 0.129, - 570: 0.126, - 575: 0.125, - 580: 0.122, - 585: 0.119, - 590: 0.115, - 595: 0.109, - 600: 0.104, - 605: 0.100, - 610: 0.098, - 615: 0.097, - 620: 0.098, - 625: 0.100, - 630: 0.100, - 635: 0.099, - 640: 0.097, - 645: 0.096, - 650: 0.095, - 655: 0.095, - 660: 0.095, - 665: 0.097, - 670: 0.101, - 675: 0.110, - 680: 0.125, - 685: 0.147, - 690: 0.174, - 695: 0.210, - 700: 0.247, - 705: 0.283, - 710: 0.311, - 715: 0.329, - 720: 0.343, - 725: 0.353, - 730: 0.358, - 735: 0.362, - 740: 0.364, - 745: 0.360, - 750: 0.362, - 755: 0.364, - 760: 0.368, - 765: 0.368, - 770: 0.355, - 775: 0.346, - 780: 0.341 - }), - ('blue flower', { - 380: 0.123, - 385: 0.152, - 390: 0.197, - 395: 0.258, - 400: 0.328, - 405: 0.385, - 410: 0.418, - 415: 0.437, - 420: 0.446, - 425: 0.448, - 430: 0.448, - 435: 0.447, - 440: 0.444, - 445: 0.440, - 450: 0.434, - 455: 0.428, - 460: 0.421, - 465: 0.413, - 470: 0.405, - 475: 0.394, - 480: 0.381, - 485: 0.372, - 490: 0.362, - 495: 0.352, - 500: 0.342, - 505: 0.330, - 510: 0.314, - 515: 0.294, - 520: 0.271, - 525: 0.249, - 530: 0.231, - 535: 0.219, - 540: 0.211, - 545: 0.209, - 550: 0.209, - 555: 0.207, - 560: 0.201, - 565: 0.196, - 570: 0.196, - 575: 0.199, - 580: 0.206, - 585: 0.215, - 590: 0.223, - 595: 0.229, - 600: 0.235, - 605: 0.241, - 610: 0.245, - 615: 0.245, - 620: 0.243, - 625: 0.243, - 630: 0.247, - 635: 0.254, - 640: 0.269, - 645: 0.291, - 650: 0.318, - 655: 0.351, - 660: 0.384, - 665: 0.417, - 670: 0.446, - 675: 0.470, - 680: 0.490, - 685: 0.504, - 690: 0.511, - 695: 0.517, - 700: 0.520, - 705: 0.522, - 710: 0.523, - 715: 0.522, - 720: 0.521, - 725: 0.521, - 730: 0.522, - 735: 0.521, - 740: 0.521, - 745: 0.516, - 750: 0.514, - 755: 0.514, - 760: 0.517, - 765: 0.515, - 770: 0.500, - 775: 0.491, - 780: 0.487 - }), - ('bluish green', { - 380: 0.110, - 385: 0.133, - 390: 0.167, - 395: 0.208, - 400: 0.252, - 405: 0.284, - 410: 0.303, - 415: 0.314, - 420: 0.322, - 425: 0.329, - 430: 0.336, - 435: 0.344, - 440: 0.353, - 445: 0.363, - 450: 0.375, - 455: 0.390, - 460: 0.408, - 465: 0.433, - 470: 0.460, - 475: 0.492, - 480: 0.523, - 485: 0.548, - 490: 0.566, - 495: 0.577, - 500: 0.582, - 505: 0.583, - 510: 0.580, - 515: 0.576, - 520: 0.569, - 525: 0.560, - 530: 0.549, - 535: 0.535, - 540: 0.519, - 545: 0.501, - 550: 0.480, - 555: 0.458, - 560: 0.436, - 565: 0.414, - 570: 0.392, - 575: 0.369, - 580: 0.346, - 585: 0.324, - 590: 0.302, - 595: 0.279, - 600: 0.260, - 605: 0.245, - 610: 0.234, - 615: 0.226, - 620: 0.221, - 625: 0.217, - 630: 0.215, - 635: 0.212, - 640: 0.210, - 645: 0.209, - 650: 0.208, - 655: 0.209, - 660: 0.211, - 665: 0.215, - 670: 0.220, - 675: 0.227, - 680: 0.233, - 685: 0.239, - 690: 0.244, - 695: 0.249, - 700: 0.252, - 705: 0.252, - 710: 0.250, - 715: 0.248, - 720: 0.244, - 725: 0.245, - 730: 0.245, - 735: 0.251, - 740: 0.260, - 745: 0.269, - 750: 0.278, - 755: 0.288, - 760: 0.297, - 765: 0.301, - 770: 0.297, - 775: 0.296, - 780: 0.296 - }), - ('orange', { - 380: 0.053, - 385: 0.054, - 390: 0.054, - 395: 0.054, - 400: 0.054, - 405: 0.054, - 410: 0.053, - 415: 0.053, - 420: 0.052, - 425: 0.052, - 430: 0.052, - 435: 0.052, - 440: 0.052, - 445: 0.052, - 450: 0.052, - 455: 0.052, - 460: 0.052, - 465: 0.052, - 470: 0.053, - 475: 0.054, - 480: 0.055, - 485: 0.056, - 490: 0.057, - 495: 0.059, - 500: 0.061, - 505: 0.064, - 510: 0.068, - 515: 0.076, - 520: 0.086, - 525: 0.101, - 530: 0.120, - 535: 0.143, - 540: 0.170, - 545: 0.198, - 550: 0.228, - 555: 0.260, - 560: 0.297, - 565: 0.338, - 570: 0.380, - 575: 0.418, - 580: 0.452, - 585: 0.481, - 590: 0.503, - 595: 0.520, - 600: 0.532, - 605: 0.543, - 610: 0.552, - 615: 0.560, - 620: 0.566, - 625: 0.572, - 630: 0.578, - 635: 0.583, - 640: 0.587, - 645: 0.593, - 650: 0.599, - 655: 0.602, - 660: 0.604, - 665: 0.606, - 670: 0.608, - 675: 0.611, - 680: 0.615, - 685: 0.619, - 690: 0.622, - 695: 0.625, - 700: 0.628, - 705: 0.630, - 710: 0.633, - 715: 0.633, - 720: 0.633, - 725: 0.636, - 730: 0.637, - 735: 0.639, - 740: 0.638, - 745: 0.633, - 750: 0.633, - 755: 0.636, - 760: 0.641, - 765: 0.639, - 770: 0.616, - 775: 0.598, - 780: 0.582 - }), - ('purplish blue', { - 380: 0.099, - 385: 0.120, - 390: 0.150, - 395: 0.189, - 400: 0.231, - 405: 0.268, - 410: 0.293, - 415: 0.311, - 420: 0.324, - 425: 0.335, - 430: 0.348, - 435: 0.361, - 440: 0.373, - 445: 0.383, - 450: 0.387, - 455: 0.383, - 460: 0.374, - 465: 0.361, - 470: 0.345, - 475: 0.325, - 480: 0.301, - 485: 0.275, - 490: 0.247, - 495: 0.223, - 500: 0.202, - 505: 0.184, - 510: 0.167, - 515: 0.152, - 520: 0.137, - 525: 0.125, - 530: 0.116, - 535: 0.110, - 540: 0.106, - 545: 0.103, - 550: 0.099, - 555: 0.094, - 560: 0.090, - 565: 0.086, - 570: 0.083, - 575: 0.083, - 580: 0.083, - 585: 0.085, - 590: 0.086, - 595: 0.087, - 600: 0.087, - 605: 0.086, - 610: 0.085, - 615: 0.084, - 620: 0.084, - 625: 0.085, - 630: 0.088, - 635: 0.092, - 640: 0.098, - 645: 0.105, - 650: 0.111, - 655: 0.118, - 660: 0.123, - 665: 0.126, - 670: 0.126, - 675: 0.124, - 680: 0.120, - 685: 0.117, - 690: 0.115, - 695: 0.115, - 700: 0.116, - 705: 0.118, - 710: 0.120, - 715: 0.124, - 720: 0.128, - 725: 0.133, - 730: 0.139, - 735: 0.149, - 740: 0.162, - 745: 0.178, - 750: 0.197, - 755: 0.219, - 760: 0.242, - 765: 0.259, - 770: 0.275, - 775: 0.294, - 780: 0.316 - }), - ('moderate red', { - 380: 0.096, - 385: 0.108, - 390: 0.123, - 395: 0.135, - 400: 0.144, - 405: 0.145, - 410: 0.144, - 415: 0.141, - 420: 0.138, - 425: 0.134, - 430: 0.132, - 435: 0.132, - 440: 0.131, - 445: 0.131, - 450: 0.129, - 455: 0.128, - 460: 0.126, - 465: 0.126, - 470: 0.125, - 475: 0.123, - 480: 0.119, - 485: 0.114, - 490: 0.109, - 495: 0.105, - 500: 0.103, - 505: 0.102, - 510: 0.100, - 515: 0.097, - 520: 0.094, - 525: 0.091, - 530: 0.089, - 535: 0.090, - 540: 0.092, - 545: 0.096, - 550: 0.102, - 555: 0.106, - 560: 0.108, - 565: 0.109, - 570: 0.112, - 575: 0.126, - 580: 0.157, - 585: 0.208, - 590: 0.274, - 595: 0.346, - 600: 0.415, - 605: 0.473, - 610: 0.517, - 615: 0.547, - 620: 0.567, - 625: 0.582, - 630: 0.591, - 635: 0.597, - 640: 0.601, - 645: 0.604, - 650: 0.607, - 655: 0.608, - 660: 0.607, - 665: 0.606, - 670: 0.605, - 675: 0.605, - 680: 0.605, - 685: 0.604, - 690: 0.605, - 695: 0.606, - 700: 0.606, - 705: 0.604, - 710: 0.602, - 715: 0.601, - 720: 0.599, - 725: 0.598, - 730: 0.596, - 735: 0.595, - 740: 0.593, - 745: 0.587, - 750: 0.584, - 755: 0.584, - 760: 0.586, - 765: 0.584, - 770: 0.566, - 775: 0.551, - 780: 0.54 - }), - ('purple', { - 380: 0.101, - 385: 0.115, - 390: 0.135, - 395: 0.157, - 400: 0.177, - 405: 0.191, - 410: 0.199, - 415: 0.203, - 420: 0.206, - 425: 0.198, - 430: 0.190, - 435: 0.179, - 440: 0.168, - 445: 0.156, - 450: 0.144, - 455: 0.132, - 460: 0.120, - 465: 0.110, - 470: 0.101, - 475: 0.093, - 480: 0.086, - 485: 0.080, - 490: 0.075, - 495: 0.070, - 500: 0.067, - 505: 0.063, - 510: 0.061, - 515: 0.059, - 520: 0.058, - 525: 0.056, - 530: 0.054, - 535: 0.053, - 540: 0.052, - 545: 0.052, - 550: 0.053, - 555: 0.054, - 560: 0.055, - 565: 0.055, - 570: 0.054, - 575: 0.053, - 580: 0.052, - 585: 0.052, - 590: 0.053, - 595: 0.055, - 600: 0.059, - 605: 0.065, - 610: 0.074, - 615: 0.086, - 620: 0.099, - 625: 0.113, - 630: 0.126, - 635: 0.138, - 640: 0.149, - 645: 0.161, - 650: 0.172, - 655: 0.182, - 660: 0.193, - 665: 0.205, - 670: 0.217, - 675: 0.232, - 680: 0.248, - 685: 0.266, - 690: 0.282, - 695: 0.301, - 700: 0.319, - 705: 0.338, - 710: 0.355, - 715: 0.371, - 720: 0.388, - 725: 0.406, - 730: 0.422, - 735: 0.436, - 740: 0.451, - 745: 0.460, - 750: 0.471, - 755: 0.481, - 760: 0.492, - 765: 0.495, - 770: 0.482, - 775: 0.471, - 780: 0.467 - }), - ('yellow green', { - 380: 0.056, - 385: 0.058, - 390: 0.059, - 395: 0.059, - 400: 0.060, - 405: 0.061, - 410: 0.061, - 415: 0.061, - 420: 0.062, - 425: 0.063, - 430: 0.064, - 435: 0.066, - 440: 0.068, - 445: 0.071, - 450: 0.075, - 455: 0.079, - 460: 0.085, - 465: 0.093, - 470: 0.104, - 475: 0.118, - 480: 0.135, - 485: 0.157, - 490: 0.185, - 495: 0.221, - 500: 0.269, - 505: 0.326, - 510: 0.384, - 515: 0.440, - 520: 0.484, - 525: 0.516, - 530: 0.534, - 535: 0.542, - 540: 0.545, - 545: 0.541, - 550: 0.533, - 555: 0.524, - 560: 0.513, - 565: 0.501, - 570: 0.487, - 575: 0.472, - 580: 0.454, - 585: 0.436, - 590: 0.416, - 595: 0.394, - 600: 0.374, - 605: 0.358, - 610: 0.346, - 615: 0.337, - 620: 0.331, - 625: 0.328, - 630: 0.325, - 635: 0.322, - 640: 0.320, - 645: 0.319, - 650: 0.319, - 655: 0.320, - 660: 0.324, - 665: 0.330, - 670: 0.337, - 675: 0.345, - 680: 0.354, - 685: 0.362, - 690: 0.368, - 695: 0.375, - 700: 0.379, - 705: 0.381, - 710: 0.379, - 715: 0.376, - 720: 0.373, - 725: 0.372, - 730: 0.375, - 735: 0.382, - 740: 0.392, - 745: 0.401, - 750: 0.412, - 755: 0.422, - 760: 0.433, - 765: 0.436, - 770: 0.426, - 775: 0.413, - 780: 0.404 - }), - ('orange yellow', { - 380: 0.060, - 385: 0.061, - 390: 0.063, - 395: 0.064, - 400: 0.065, - 405: 0.065, - 410: 0.064, - 415: 0.064, - 420: 0.064, - 425: 0.064, - 430: 0.064, - 435: 0.065, - 440: 0.065, - 445: 0.066, - 450: 0.067, - 455: 0.068, - 460: 0.069, - 465: 0.073, - 470: 0.077, - 475: 0.084, - 480: 0.092, - 485: 0.100, - 490: 0.107, - 495: 0.115, - 500: 0.123, - 505: 0.133, - 510: 0.146, - 515: 0.166, - 520: 0.193, - 525: 0.229, - 530: 0.273, - 535: 0.323, - 540: 0.374, - 545: 0.418, - 550: 0.456, - 555: 0.487, - 560: 0.512, - 565: 0.534, - 570: 0.554, - 575: 0.570, - 580: 0.584, - 585: 0.598, - 590: 0.609, - 595: 0.617, - 600: 0.624, - 605: 0.630, - 610: 0.635, - 615: 0.640, - 620: 0.645, - 625: 0.650, - 630: 0.654, - 635: 0.658, - 640: 0.662, - 645: 0.667, - 650: 0.672, - 655: 0.675, - 660: 0.676, - 665: 0.677, - 670: 0.678, - 675: 0.681, - 680: 0.685, - 685: 0.688, - 690: 0.690, - 695: 0.693, - 700: 0.696, - 705: 0.698, - 710: 0.698, - 715: 0.698, - 720: 0.698, - 725: 0.700, - 730: 0.701, - 735: 0.701, - 740: 0.701, - 745: 0.695, - 750: 0.694, - 755: 0.696, - 760: 0.700, - 765: 0.698, - 770: 0.673, - 775: 0.653, - 780: 0.639 - }), - ('blue', { - 380: 0.069, - 385: 0.081, - 390: 0.096, - 395: 0.114, - 400: 0.136, - 405: 0.156, - 410: 0.175, - 415: 0.193, - 420: 0.208, - 425: 0.224, - 430: 0.244, - 435: 0.265, - 440: 0.290, - 445: 0.316, - 450: 0.335, - 455: 0.342, - 460: 0.338, - 465: 0.324, - 470: 0.302, - 475: 0.273, - 480: 0.239, - 485: 0.205, - 490: 0.172, - 495: 0.144, - 500: 0.120, - 505: 0.101, - 510: 0.086, - 515: 0.074, - 520: 0.066, - 525: 0.059, - 530: 0.054, - 535: 0.051, - 540: 0.048, - 545: 0.046, - 550: 0.045, - 555: 0.044, - 560: 0.043, - 565: 0.042, - 570: 0.041, - 575: 0.041, - 580: 0.040, - 585: 0.040, - 590: 0.040, - 595: 0.040, - 600: 0.039, - 605: 0.039, - 610: 0.040, - 615: 0.040, - 620: 0.040, - 625: 0.040, - 630: 0.041, - 635: 0.041, - 640: 0.042, - 645: 0.042, - 650: 0.042, - 655: 0.043, - 660: 0.043, - 665: 0.043, - 670: 0.044, - 675: 0.044, - 680: 0.044, - 685: 0.044, - 690: 0.045, - 695: 0.046, - 700: 0.048, - 705: 0.050, - 710: 0.051, - 715: 0.053, - 720: 0.056, - 725: 0.060, - 730: 0.064, - 735: 0.070, - 740: 0.079, - 745: 0.091, - 750: 0.104, - 755: 0.120, - 760: 0.138, - 765: 0.154, - 770: 0.168, - 775: 0.186, - 780: 0.204 - }), - ('green', { - 380: 0.055, - 385: 0.056, - 390: 0.057, - 395: 0.058, - 400: 0.058, - 405: 0.058, - 410: 0.059, - 415: 0.059, - 420: 0.059, - 425: 0.060, - 430: 0.062, - 435: 0.063, - 440: 0.065, - 445: 0.067, - 450: 0.070, - 455: 0.074, - 460: 0.078, - 465: 0.084, - 470: 0.091, - 475: 0.101, - 480: 0.113, - 485: 0.125, - 490: 0.140, - 495: 0.157, - 500: 0.180, - 505: 0.208, - 510: 0.244, - 515: 0.286, - 520: 0.324, - 525: 0.351, - 530: 0.363, - 535: 0.363, - 540: 0.355, - 545: 0.342, - 550: 0.323, - 555: 0.303, - 560: 0.281, - 565: 0.260, - 570: 0.238, - 575: 0.217, - 580: 0.196, - 585: 0.177, - 590: 0.158, - 595: 0.140, - 600: 0.124, - 605: 0.111, - 610: 0.101, - 615: 0.094, - 620: 0.089, - 625: 0.086, - 630: 0.084, - 635: 0.082, - 640: 0.080, - 645: 0.078, - 650: 0.077, - 655: 0.076, - 660: 0.075, - 665: 0.075, - 670: 0.075, - 675: 0.077, - 680: 0.078, - 685: 0.080, - 690: 0.082, - 695: 0.085, - 700: 0.088, - 705: 0.089, - 710: 0.089, - 715: 0.090, - 720: 0.090, - 725: 0.090, - 730: 0.089, - 735: 0.092, - 740: 0.094, - 745: 0.097, - 750: 0.102, - 755: 0.106, - 760: 0.110, - 765: 0.111, - 770: 0.112, - 775: 0.112, - 780: 0.112 - }), - ('red', { - 380: 0.052, - 385: 0.052, - 390: 0.052, - 395: 0.052, - 400: 0.051, - 405: 0.051, - 410: 0.050, - 415: 0.050, - 420: 0.049, - 425: 0.049, - 430: 0.049, - 435: 0.049, - 440: 0.049, - 445: 0.049, - 450: 0.049, - 455: 0.048, - 460: 0.048, - 465: 0.047, - 470: 0.047, - 475: 0.046, - 480: 0.045, - 485: 0.045, - 490: 0.044, - 495: 0.044, - 500: 0.044, - 505: 0.044, - 510: 0.044, - 515: 0.044, - 520: 0.044, - 525: 0.044, - 530: 0.044, - 535: 0.044, - 540: 0.045, - 545: 0.046, - 550: 0.047, - 555: 0.048, - 560: 0.050, - 565: 0.053, - 570: 0.057, - 575: 0.063, - 580: 0.072, - 585: 0.086, - 590: 0.109, - 595: 0.143, - 600: 0.192, - 605: 0.256, - 610: 0.332, - 615: 0.413, - 620: 0.486, - 625: 0.550, - 630: 0.598, - 635: 0.631, - 640: 0.654, - 645: 0.672, - 650: 0.686, - 655: 0.694, - 660: 0.700, - 665: 0.704, - 670: 0.707, - 675: 0.712, - 680: 0.718, - 685: 0.721, - 690: 0.724, - 695: 0.727, - 700: 0.729, - 705: 0.730, - 710: 0.730, - 715: 0.729, - 720: 0.727, - 725: 0.728, - 730: 0.729, - 735: 0.729, - 740: 0.727, - 745: 0.723, - 750: 0.721, - 755: 0.724, - 760: 0.728, - 765: 0.727, - 770: 0.702, - 775: 0.680, - 780: 0.664 - }), - ('yellow', { - 380: 0.054, - 385: 0.053, - 390: 0.054, - 395: 0.053, - 400: 0.053, - 405: 0.053, - 410: 0.053, - 415: 0.052, - 420: 0.052, - 425: 0.052, - 430: 0.053, - 435: 0.053, - 440: 0.053, - 445: 0.054, - 450: 0.055, - 455: 0.056, - 460: 0.059, - 465: 0.065, - 470: 0.075, - 475: 0.093, - 480: 0.121, - 485: 0.157, - 490: 0.202, - 495: 0.252, - 500: 0.303, - 505: 0.351, - 510: 0.394, - 515: 0.436, - 520: 0.475, - 525: 0.512, - 530: 0.544, - 535: 0.572, - 540: 0.597, - 545: 0.615, - 550: 0.630, - 555: 0.645, - 560: 0.660, - 565: 0.673, - 570: 0.686, - 575: 0.698, - 580: 0.708, - 585: 0.718, - 590: 0.726, - 595: 0.732, - 600: 0.737, - 605: 0.742, - 610: 0.746, - 615: 0.749, - 620: 0.753, - 625: 0.757, - 630: 0.761, - 635: 0.765, - 640: 0.768, - 645: 0.772, - 650: 0.777, - 655: 0.779, - 660: 0.780, - 665: 0.780, - 670: 0.781, - 675: 0.782, - 680: 0.785, - 685: 0.785, - 690: 0.787, - 695: 0.789, - 700: 0.792, - 705: 0.792, - 710: 0.793, - 715: 0.792, - 720: 0.790, - 725: 0.792, - 730: 0.792, - 735: 0.790, - 740: 0.787, - 745: 0.782, - 750: 0.778, - 755: 0.780, - 760: 0.782, - 765: 0.781, - 770: 0.752, - 775: 0.728, - 780: 0.71 - }), - ('magenta', { - 380: 0.118, - 385: 0.142, - 390: 0.179, - 395: 0.228, - 400: 0.283, - 405: 0.322, - 410: 0.343, - 415: 0.354, - 420: 0.359, - 425: 0.357, - 430: 0.350, - 435: 0.339, - 440: 0.327, - 445: 0.313, - 450: 0.298, - 455: 0.282, - 460: 0.267, - 465: 0.253, - 470: 0.239, - 475: 0.225, - 480: 0.209, - 485: 0.195, - 490: 0.182, - 495: 0.172, - 500: 0.163, - 505: 0.155, - 510: 0.146, - 515: 0.135, - 520: 0.124, - 525: 0.113, - 530: 0.106, - 535: 0.102, - 540: 0.102, - 545: 0.105, - 550: 0.107, - 555: 0.107, - 560: 0.106, - 565: 0.107, - 570: 0.112, - 575: 0.123, - 580: 0.141, - 585: 0.166, - 590: 0.198, - 595: 0.235, - 600: 0.279, - 605: 0.333, - 610: 0.394, - 615: 0.460, - 620: 0.522, - 625: 0.580, - 630: 0.628, - 635: 0.666, - 640: 0.696, - 645: 0.722, - 650: 0.742, - 655: 0.756, - 660: 0.766, - 665: 0.774, - 670: 0.780, - 675: 0.785, - 680: 0.791, - 685: 0.794, - 690: 0.798, - 695: 0.801, - 700: 0.804, - 705: 0.806, - 710: 0.807, - 715: 0.807, - 720: 0.807, - 725: 0.810, - 730: 0.813, - 735: 0.814, - 740: 0.813, - 745: 0.810, - 750: 0.808, - 755: 0.811, - 760: 0.814, - 765: 0.813, - 770: 0.785, - 775: 0.765, - 780: 0.752 - }), - ('cyan', { - 380: 0.093, - 385: 0.110, - 390: 0.134, - 395: 0.164, - 400: 0.195, - 405: 0.220, - 410: 0.238, - 415: 0.249, - 420: 0.258, - 425: 0.270, - 430: 0.281, - 435: 0.296, - 440: 0.315, - 445: 0.334, - 450: 0.352, - 455: 0.370, - 460: 0.391, - 465: 0.414, - 470: 0.434, - 475: 0.449, - 480: 0.458, - 485: 0.461, - 490: 0.457, - 495: 0.447, - 500: 0.433, - 505: 0.414, - 510: 0.392, - 515: 0.366, - 520: 0.339, - 525: 0.310, - 530: 0.282, - 535: 0.255, - 540: 0.228, - 545: 0.204, - 550: 0.180, - 555: 0.159, - 560: 0.141, - 565: 0.126, - 570: 0.114, - 575: 0.104, - 580: 0.097, - 585: 0.092, - 590: 0.088, - 595: 0.083, - 600: 0.080, - 605: 0.077, - 610: 0.075, - 615: 0.074, - 620: 0.073, - 625: 0.073, - 630: 0.073, - 635: 0.073, - 640: 0.073, - 645: 0.073, - 650: 0.074, - 655: 0.075, - 660: 0.076, - 665: 0.076, - 670: 0.077, - 675: 0.076, - 680: 0.075, - 685: 0.074, - 690: 0.074, - 695: 0.073, - 700: 0.072, - 705: 0.072, - 710: 0.071, - 715: 0.073, - 720: 0.075, - 725: 0.078, - 730: 0.082, - 735: 0.090, - 740: 0.100, - 745: 0.116, - 750: 0.133, - 755: 0.154, - 760: 0.176, - 765: 0.191, - 770: 0.200, - 775: 0.208, - 780: 0.214 - }), - ('white 9.5 (.05 D)', { - 380: 0.153, - 385: 0.189, - 390: 0.245, - 395: 0.319, - 400: 0.409, - 405: 0.536, - 410: 0.671, - 415: 0.772, - 420: 0.840, - 425: 0.868, - 430: 0.878, - 435: 0.882, - 440: 0.883, - 445: 0.885, - 450: 0.886, - 455: 0.886, - 460: 0.887, - 465: 0.888, - 470: 0.888, - 475: 0.888, - 480: 0.888, - 485: 0.888, - 490: 0.888, - 495: 0.888, - 500: 0.887, - 505: 0.887, - 510: 0.887, - 515: 0.887, - 520: 0.887, - 525: 0.887, - 530: 0.887, - 535: 0.887, - 540: 0.887, - 545: 0.886, - 550: 0.886, - 555: 0.887, - 560: 0.887, - 565: 0.887, - 570: 0.888, - 575: 0.888, - 580: 0.887, - 585: 0.886, - 590: 0.886, - 595: 0.886, - 600: 0.887, - 605: 0.888, - 610: 0.889, - 615: 0.890, - 620: 0.891, - 625: 0.891, - 630: 0.891, - 635: 0.891, - 640: 0.890, - 645: 0.889, - 650: 0.889, - 655: 0.889, - 660: 0.889, - 665: 0.889, - 670: 0.888, - 675: 0.888, - 680: 0.888, - 685: 0.888, - 690: 0.888, - 695: 0.888, - 700: 0.888, - 705: 0.887, - 710: 0.886, - 715: 0.886, - 720: 0.886, - 725: 0.885, - 730: 0.885, - 735: 0.885, - 740: 0.884, - 745: 0.884, - 750: 0.883, - 755: 0.882, - 760: 0.882, - 765: 0.881, - 770: 0.880, - 775: 0.880, - 780: 0.879 - }), - ('neutral 8 (.23 D)', { - 380: 0.150, - 385: 0.184, - 390: 0.235, - 395: 0.299, - 400: 0.372, - 405: 0.459, - 410: 0.529, - 415: 0.564, - 420: 0.580, - 425: 0.584, - 430: 0.585, - 435: 0.587, - 440: 0.587, - 445: 0.588, - 450: 0.588, - 455: 0.587, - 460: 0.586, - 465: 0.585, - 470: 0.583, - 475: 0.582, - 480: 0.581, - 485: 0.580, - 490: 0.580, - 495: 0.580, - 500: 0.580, - 505: 0.580, - 510: 0.580, - 515: 0.581, - 520: 0.581, - 525: 0.582, - 530: 0.582, - 535: 0.582, - 540: 0.583, - 545: 0.583, - 550: 0.583, - 555: 0.584, - 560: 0.584, - 565: 0.585, - 570: 0.586, - 575: 0.587, - 580: 0.588, - 585: 0.588, - 590: 0.588, - 595: 0.588, - 600: 0.588, - 605: 0.587, - 610: 0.586, - 615: 0.586, - 620: 0.585, - 625: 0.584, - 630: 0.583, - 635: 0.581, - 640: 0.580, - 645: 0.579, - 650: 0.578, - 655: 0.577, - 660: 0.576, - 665: 0.575, - 670: 0.574, - 675: 0.573, - 680: 0.572, - 685: 0.571, - 690: 0.570, - 695: 0.569, - 700: 0.568, - 705: 0.567, - 710: 0.566, - 715: 0.565, - 720: 0.564, - 725: 0.562, - 730: 0.562, - 735: 0.560, - 740: 0.560, - 745: 0.558, - 750: 0.557, - 755: 0.556, - 760: 0.555, - 765: 0.554, - 770: 0.553, - 775: 0.551, - 780: 0.55 - }), - ('neutral 6.5 (.44 D)', { - 380: 0.138, - 385: 0.167, - 390: 0.206, - 395: 0.249, - 400: 0.289, - 405: 0.324, - 410: 0.346, - 415: 0.354, - 420: 0.357, - 425: 0.358, - 430: 0.359, - 435: 0.360, - 440: 0.361, - 445: 0.362, - 450: 0.362, - 455: 0.361, - 460: 0.361, - 465: 0.359, - 470: 0.358, - 475: 0.358, - 480: 0.357, - 485: 0.356, - 490: 0.356, - 495: 0.356, - 500: 0.356, - 505: 0.356, - 510: 0.356, - 515: 0.356, - 520: 0.357, - 525: 0.357, - 530: 0.357, - 535: 0.358, - 540: 0.358, - 545: 0.358, - 550: 0.358, - 555: 0.358, - 560: 0.359, - 565: 0.359, - 570: 0.360, - 575: 0.361, - 580: 0.361, - 585: 0.361, - 590: 0.361, - 595: 0.361, - 600: 0.360, - 605: 0.360, - 610: 0.359, - 615: 0.358, - 620: 0.357, - 625: 0.356, - 630: 0.355, - 635: 0.354, - 640: 0.353, - 645: 0.352, - 650: 0.351, - 655: 0.350, - 660: 0.349, - 665: 0.348, - 670: 0.346, - 675: 0.346, - 680: 0.345, - 685: 0.344, - 690: 0.343, - 695: 0.342, - 700: 0.341, - 705: 0.340, - 710: 0.339, - 715: 0.338, - 720: 0.337, - 725: 0.336, - 730: 0.335, - 735: 0.334, - 740: 0.333, - 745: 0.332, - 750: 0.331, - 755: 0.330, - 760: 0.329, - 765: 0.328, - 770: 0.327, - 775: 0.326, - 780: 0.325 - }), - ('neutral 5 (.70 D)', { - 380: 0.113, - 385: 0.131, - 390: 0.150, - 395: 0.169, - 400: 0.183, - 405: 0.193, - 410: 0.199, - 415: 0.201, - 420: 0.202, - 425: 0.203, - 430: 0.203, - 435: 0.204, - 440: 0.205, - 445: 0.205, - 450: 0.205, - 455: 0.205, - 460: 0.204, - 465: 0.204, - 470: 0.203, - 475: 0.203, - 480: 0.202, - 485: 0.202, - 490: 0.202, - 495: 0.202, - 500: 0.202, - 505: 0.202, - 510: 0.202, - 515: 0.202, - 520: 0.202, - 525: 0.202, - 530: 0.203, - 535: 0.203, - 540: 0.203, - 545: 0.203, - 550: 0.203, - 555: 0.203, - 560: 0.203, - 565: 0.203, - 570: 0.204, - 575: 0.204, - 580: 0.205, - 585: 0.205, - 590: 0.205, - 595: 0.205, - 600: 0.204, - 605: 0.204, - 610: 0.204, - 615: 0.203, - 620: 0.203, - 625: 0.202, - 630: 0.201, - 635: 0.201, - 640: 0.200, - 645: 0.199, - 650: 0.198, - 655: 0.198, - 660: 0.197, - 665: 0.197, - 670: 0.196, - 675: 0.195, - 680: 0.195, - 685: 0.194, - 690: 0.194, - 695: 0.193, - 700: 0.192, - 705: 0.192, - 710: 0.191, - 715: 0.191, - 720: 0.190, - 725: 0.189, - 730: 0.189, - 735: 0.188, - 740: 0.188, - 745: 0.187, - 750: 0.187, - 755: 0.186, - 760: 0.185, - 765: 0.185, - 770: 0.184, - 775: 0.184, - 780: 0.183 - }), - ('neutral 3.5 (1.05 D)', { - 380: 0.074, - 385: 0.079, - 390: 0.084, - 395: 0.088, - 400: 0.091, - 405: 0.093, - 410: 0.094, - 415: 0.094, - 420: 0.094, - 425: 0.094, - 430: 0.094, - 435: 0.095, - 440: 0.095, - 445: 0.095, - 450: 0.095, - 455: 0.094, - 460: 0.094, - 465: 0.094, - 470: 0.094, - 475: 0.093, - 480: 0.093, - 485: 0.093, - 490: 0.093, - 495: 0.092, - 500: 0.092, - 505: 0.093, - 510: 0.093, - 515: 0.093, - 520: 0.093, - 525: 0.093, - 530: 0.093, - 535: 0.093, - 540: 0.093, - 545: 0.093, - 550: 0.093, - 555: 0.092, - 560: 0.093, - 565: 0.093, - 570: 0.093, - 575: 0.093, - 580: 0.093, - 585: 0.093, - 590: 0.093, - 595: 0.092, - 600: 0.092, - 605: 0.092, - 610: 0.092, - 615: 0.091, - 620: 0.091, - 625: 0.091, - 630: 0.090, - 635: 0.090, - 640: 0.090, - 645: 0.090, - 650: 0.089, - 655: 0.089, - 660: 0.089, - 665: 0.088, - 670: 0.088, - 675: 0.088, - 680: 0.087, - 685: 0.087, - 690: 0.087, - 695: 0.087, - 700: 0.086, - 705: 0.086, - 710: 0.086, - 715: 0.086, - 720: 0.085, - 725: 0.085, - 730: 0.085, - 735: 0.085, - 740: 0.085, - 745: 0.084, - 750: 0.084, - 755: 0.084, - 760: 0.084, - 765: 0.084, - 770: 0.083, - 775: 0.083, - 780: 0.083 - }), - ('black 2 (1.5 D)', { - 380: 0.032, - 385: 0.033, - 390: 0.033, - 395: 0.034, - 400: 0.035, - 405: 0.035, - 410: 0.036, - 415: 0.036, - 420: 0.036, - 425: 0.036, - 430: 0.036, - 435: 0.036, - 440: 0.035, - 445: 0.035, - 450: 0.035, - 455: 0.035, - 460: 0.035, - 465: 0.035, - 470: 0.035, - 475: 0.035, - 480: 0.034, - 485: 0.034, - 490: 0.034, - 495: 0.034, - 500: 0.034, - 505: 0.034, - 510: 0.034, - 515: 0.034, - 520: 0.034, - 525: 0.034, - 530: 0.034, - 535: 0.034, - 540: 0.034, - 545: 0.034, - 550: 0.034, - 555: 0.034, - 560: 0.033, - 565: 0.033, - 570: 0.033, - 575: 0.033, - 580: 0.033, - 585: 0.033, - 590: 0.033, - 595: 0.033, - 600: 0.033, - 605: 0.033, - 610: 0.033, - 615: 0.033, - 620: 0.033, - 625: 0.033, - 630: 0.033, - 635: 0.033, - 640: 0.033, - 645: 0.033, - 650: 0.033, - 655: 0.033, - 660: 0.033, - 665: 0.033, - 670: 0.033, - 675: 0.033, - 680: 0.033, - 685: 0.033, - 690: 0.032, - 695: 0.032, - 700: 0.032, - 705: 0.032, - 710: 0.032, - 715: 0.032, - 720: 0.032, - 725: 0.032, - 730: 0.032, - 735: 0.032, - 740: 0.032, - 745: 0.032, - 750: 0.032, - 755: 0.032, - 760: 0.032, - 765: 0.032, - 770: 0.032, - 775: 0.032, - 780: 0.032 - }), -)) +DATA_COLORCHECKER_N_OHTA = dict( + [ + ( + "dark skin", + { + 380: 0.048, + 385: 0.051, + 390: 0.055, + 395: 0.060, + 400: 0.065, + 405: 0.068, + 410: 0.068, + 415: 0.067, + 420: 0.064, + 425: 0.062, + 430: 0.059, + 435: 0.057, + 440: 0.055, + 445: 0.054, + 450: 0.053, + 455: 0.053, + 460: 0.052, + 465: 0.052, + 470: 0.052, + 475: 0.053, + 480: 0.054, + 485: 0.055, + 490: 0.057, + 495: 0.059, + 500: 0.061, + 505: 0.062, + 510: 0.065, + 515: 0.067, + 520: 0.070, + 525: 0.072, + 530: 0.074, + 535: 0.075, + 540: 0.076, + 545: 0.078, + 550: 0.079, + 555: 0.082, + 560: 0.087, + 565: 0.092, + 570: 0.100, + 575: 0.107, + 580: 0.115, + 585: 0.122, + 590: 0.129, + 595: 0.134, + 600: 0.138, + 605: 0.142, + 610: 0.146, + 615: 0.150, + 620: 0.154, + 625: 0.158, + 630: 0.163, + 635: 0.167, + 640: 0.173, + 645: 0.180, + 650: 0.188, + 655: 0.196, + 660: 0.204, + 665: 0.213, + 670: 0.222, + 675: 0.231, + 680: 0.242, + 685: 0.251, + 690: 0.261, + 695: 0.271, + 700: 0.282, + 705: 0.294, + 710: 0.305, + 715: 0.318, + 720: 0.334, + 725: 0.354, + 730: 0.372, + 735: 0.392, + 740: 0.409, + 745: 0.420, + 750: 0.436, + 755: 0.450, + 760: 0.462, + 765: 0.465, + 770: 0.448, + 775: 0.432, + 780: 0.421, + }, + ), + ( + "light skin", + { + 380: 0.103, + 385: 0.120, + 390: 0.141, + 395: 0.163, + 400: 0.182, + 405: 0.192, + 410: 0.197, + 415: 0.199, + 420: 0.201, + 425: 0.203, + 430: 0.205, + 435: 0.208, + 440: 0.212, + 445: 0.217, + 450: 0.224, + 455: 0.231, + 460: 0.240, + 465: 0.251, + 470: 0.262, + 475: 0.273, + 480: 0.282, + 485: 0.289, + 490: 0.293, + 495: 0.296, + 500: 0.301, + 505: 0.310, + 510: 0.321, + 515: 0.326, + 520: 0.322, + 525: 0.310, + 530: 0.298, + 535: 0.291, + 540: 0.292, + 545: 0.297, + 550: 0.300, + 555: 0.298, + 560: 0.295, + 565: 0.295, + 570: 0.305, + 575: 0.326, + 580: 0.358, + 585: 0.397, + 590: 0.435, + 595: 0.468, + 600: 0.494, + 605: 0.514, + 610: 0.530, + 615: 0.541, + 620: 0.550, + 625: 0.557, + 630: 0.564, + 635: 0.569, + 640: 0.574, + 645: 0.582, + 650: 0.590, + 655: 0.597, + 660: 0.605, + 665: 0.614, + 670: 0.624, + 675: 0.637, + 680: 0.652, + 685: 0.668, + 690: 0.682, + 695: 0.697, + 700: 0.713, + 705: 0.728, + 710: 0.745, + 715: 0.753, + 720: 0.762, + 725: 0.774, + 730: 0.783, + 735: 0.788, + 740: 0.791, + 745: 0.787, + 750: 0.789, + 755: 0.794, + 760: 0.801, + 765: 0.799, + 770: 0.771, + 775: 0.747, + 780: 0.734, + }, + ), + ( + "blue sky", + { + 380: 0.113, + 385: 0.138, + 390: 0.174, + 395: 0.219, + 400: 0.266, + 405: 0.300, + 410: 0.320, + 415: 0.330, + 420: 0.336, + 425: 0.337, + 430: 0.337, + 435: 0.337, + 440: 0.335, + 445: 0.334, + 450: 0.331, + 455: 0.327, + 460: 0.322, + 465: 0.316, + 470: 0.310, + 475: 0.302, + 480: 0.293, + 485: 0.285, + 490: 0.276, + 495: 0.268, + 500: 0.260, + 505: 0.251, + 510: 0.243, + 515: 0.234, + 520: 0.225, + 525: 0.215, + 530: 0.208, + 535: 0.203, + 540: 0.198, + 545: 0.195, + 550: 0.191, + 555: 0.188, + 560: 0.183, + 565: 0.177, + 570: 0.172, + 575: 0.167, + 580: 0.163, + 585: 0.160, + 590: 0.157, + 595: 0.153, + 600: 0.150, + 605: 0.147, + 610: 0.144, + 615: 0.141, + 620: 0.137, + 625: 0.133, + 630: 0.130, + 635: 0.126, + 640: 0.123, + 645: 0.120, + 650: 0.118, + 655: 0.115, + 660: 0.112, + 665: 0.110, + 670: 0.108, + 675: 0.106, + 680: 0.105, + 685: 0.104, + 690: 0.104, + 695: 0.103, + 700: 0.103, + 705: 0.102, + 710: 0.102, + 715: 0.102, + 720: 0.102, + 725: 0.102, + 730: 0.102, + 735: 0.104, + 740: 0.104, + 745: 0.104, + 750: 0.104, + 755: 0.106, + 760: 0.106, + 765: 0.107, + 770: 0.110, + 775: 0.115, + 780: 0.12, + }, + ), + ( + "foliage", + { + 380: 0.048, + 385: 0.049, + 390: 0.049, + 395: 0.049, + 400: 0.050, + 405: 0.049, + 410: 0.049, + 415: 0.050, + 420: 0.050, + 425: 0.051, + 430: 0.052, + 435: 0.053, + 440: 0.054, + 445: 0.056, + 450: 0.058, + 455: 0.060, + 460: 0.061, + 465: 0.063, + 470: 0.064, + 475: 0.065, + 480: 0.067, + 485: 0.068, + 490: 0.070, + 495: 0.072, + 500: 0.078, + 505: 0.088, + 510: 0.106, + 515: 0.130, + 520: 0.155, + 525: 0.173, + 530: 0.181, + 535: 0.182, + 540: 0.177, + 545: 0.168, + 550: 0.157, + 555: 0.147, + 560: 0.137, + 565: 0.129, + 570: 0.126, + 575: 0.125, + 580: 0.122, + 585: 0.119, + 590: 0.115, + 595: 0.109, + 600: 0.104, + 605: 0.100, + 610: 0.098, + 615: 0.097, + 620: 0.098, + 625: 0.100, + 630: 0.100, + 635: 0.099, + 640: 0.097, + 645: 0.096, + 650: 0.095, + 655: 0.095, + 660: 0.095, + 665: 0.097, + 670: 0.101, + 675: 0.110, + 680: 0.125, + 685: 0.147, + 690: 0.174, + 695: 0.210, + 700: 0.247, + 705: 0.283, + 710: 0.311, + 715: 0.329, + 720: 0.343, + 725: 0.353, + 730: 0.358, + 735: 0.362, + 740: 0.364, + 745: 0.360, + 750: 0.362, + 755: 0.364, + 760: 0.368, + 765: 0.368, + 770: 0.355, + 775: 0.346, + 780: 0.341, + }, + ), + ( + "blue flower", + { + 380: 0.123, + 385: 0.152, + 390: 0.197, + 395: 0.258, + 400: 0.328, + 405: 0.385, + 410: 0.418, + 415: 0.437, + 420: 0.446, + 425: 0.448, + 430: 0.448, + 435: 0.447, + 440: 0.444, + 445: 0.440, + 450: 0.434, + 455: 0.428, + 460: 0.421, + 465: 0.413, + 470: 0.405, + 475: 0.394, + 480: 0.381, + 485: 0.372, + 490: 0.362, + 495: 0.352, + 500: 0.342, + 505: 0.330, + 510: 0.314, + 515: 0.294, + 520: 0.271, + 525: 0.249, + 530: 0.231, + 535: 0.219, + 540: 0.211, + 545: 0.209, + 550: 0.209, + 555: 0.207, + 560: 0.201, + 565: 0.196, + 570: 0.196, + 575: 0.199, + 580: 0.206, + 585: 0.215, + 590: 0.223, + 595: 0.229, + 600: 0.235, + 605: 0.241, + 610: 0.245, + 615: 0.245, + 620: 0.243, + 625: 0.243, + 630: 0.247, + 635: 0.254, + 640: 0.269, + 645: 0.291, + 650: 0.318, + 655: 0.351, + 660: 0.384, + 665: 0.417, + 670: 0.446, + 675: 0.470, + 680: 0.490, + 685: 0.504, + 690: 0.511, + 695: 0.517, + 700: 0.520, + 705: 0.522, + 710: 0.523, + 715: 0.522, + 720: 0.521, + 725: 0.521, + 730: 0.522, + 735: 0.521, + 740: 0.521, + 745: 0.516, + 750: 0.514, + 755: 0.514, + 760: 0.517, + 765: 0.515, + 770: 0.500, + 775: 0.491, + 780: 0.487, + }, + ), + ( + "bluish green", + { + 380: 0.110, + 385: 0.133, + 390: 0.167, + 395: 0.208, + 400: 0.252, + 405: 0.284, + 410: 0.303, + 415: 0.314, + 420: 0.322, + 425: 0.329, + 430: 0.336, + 435: 0.344, + 440: 0.353, + 445: 0.363, + 450: 0.375, + 455: 0.390, + 460: 0.408, + 465: 0.433, + 470: 0.460, + 475: 0.492, + 480: 0.523, + 485: 0.548, + 490: 0.566, + 495: 0.577, + 500: 0.582, + 505: 0.583, + 510: 0.580, + 515: 0.576, + 520: 0.569, + 525: 0.560, + 530: 0.549, + 535: 0.535, + 540: 0.519, + 545: 0.501, + 550: 0.480, + 555: 0.458, + 560: 0.436, + 565: 0.414, + 570: 0.392, + 575: 0.369, + 580: 0.346, + 585: 0.324, + 590: 0.302, + 595: 0.279, + 600: 0.260, + 605: 0.245, + 610: 0.234, + 615: 0.226, + 620: 0.221, + 625: 0.217, + 630: 0.215, + 635: 0.212, + 640: 0.210, + 645: 0.209, + 650: 0.208, + 655: 0.209, + 660: 0.211, + 665: 0.215, + 670: 0.220, + 675: 0.227, + 680: 0.233, + 685: 0.239, + 690: 0.244, + 695: 0.249, + 700: 0.252, + 705: 0.252, + 710: 0.250, + 715: 0.248, + 720: 0.244, + 725: 0.245, + 730: 0.245, + 735: 0.251, + 740: 0.260, + 745: 0.269, + 750: 0.278, + 755: 0.288, + 760: 0.297, + 765: 0.301, + 770: 0.297, + 775: 0.296, + 780: 0.296, + }, + ), + ( + "orange", + { + 380: 0.053, + 385: 0.054, + 390: 0.054, + 395: 0.054, + 400: 0.054, + 405: 0.054, + 410: 0.053, + 415: 0.053, + 420: 0.052, + 425: 0.052, + 430: 0.052, + 435: 0.052, + 440: 0.052, + 445: 0.052, + 450: 0.052, + 455: 0.052, + 460: 0.052, + 465: 0.052, + 470: 0.053, + 475: 0.054, + 480: 0.055, + 485: 0.056, + 490: 0.057, + 495: 0.059, + 500: 0.061, + 505: 0.064, + 510: 0.068, + 515: 0.076, + 520: 0.086, + 525: 0.101, + 530: 0.120, + 535: 0.143, + 540: 0.170, + 545: 0.198, + 550: 0.228, + 555: 0.260, + 560: 0.297, + 565: 0.338, + 570: 0.380, + 575: 0.418, + 580: 0.452, + 585: 0.481, + 590: 0.503, + 595: 0.520, + 600: 0.532, + 605: 0.543, + 610: 0.552, + 615: 0.560, + 620: 0.566, + 625: 0.572, + 630: 0.578, + 635: 0.583, + 640: 0.587, + 645: 0.593, + 650: 0.599, + 655: 0.602, + 660: 0.604, + 665: 0.606, + 670: 0.608, + 675: 0.611, + 680: 0.615, + 685: 0.619, + 690: 0.622, + 695: 0.625, + 700: 0.628, + 705: 0.630, + 710: 0.633, + 715: 0.633, + 720: 0.633, + 725: 0.636, + 730: 0.637, + 735: 0.639, + 740: 0.638, + 745: 0.633, + 750: 0.633, + 755: 0.636, + 760: 0.641, + 765: 0.639, + 770: 0.616, + 775: 0.598, + 780: 0.582, + }, + ), + ( + "purplish blue", + { + 380: 0.099, + 385: 0.120, + 390: 0.150, + 395: 0.189, + 400: 0.231, + 405: 0.268, + 410: 0.293, + 415: 0.311, + 420: 0.324, + 425: 0.335, + 430: 0.348, + 435: 0.361, + 440: 0.373, + 445: 0.383, + 450: 0.387, + 455: 0.383, + 460: 0.374, + 465: 0.361, + 470: 0.345, + 475: 0.325, + 480: 0.301, + 485: 0.275, + 490: 0.247, + 495: 0.223, + 500: 0.202, + 505: 0.184, + 510: 0.167, + 515: 0.152, + 520: 0.137, + 525: 0.125, + 530: 0.116, + 535: 0.110, + 540: 0.106, + 545: 0.103, + 550: 0.099, + 555: 0.094, + 560: 0.090, + 565: 0.086, + 570: 0.083, + 575: 0.083, + 580: 0.083, + 585: 0.085, + 590: 0.086, + 595: 0.087, + 600: 0.087, + 605: 0.086, + 610: 0.085, + 615: 0.084, + 620: 0.084, + 625: 0.085, + 630: 0.088, + 635: 0.092, + 640: 0.098, + 645: 0.105, + 650: 0.111, + 655: 0.118, + 660: 0.123, + 665: 0.126, + 670: 0.126, + 675: 0.124, + 680: 0.120, + 685: 0.117, + 690: 0.115, + 695: 0.115, + 700: 0.116, + 705: 0.118, + 710: 0.120, + 715: 0.124, + 720: 0.128, + 725: 0.133, + 730: 0.139, + 735: 0.149, + 740: 0.162, + 745: 0.178, + 750: 0.197, + 755: 0.219, + 760: 0.242, + 765: 0.259, + 770: 0.275, + 775: 0.294, + 780: 0.316, + }, + ), + ( + "moderate red", + { + 380: 0.096, + 385: 0.108, + 390: 0.123, + 395: 0.135, + 400: 0.144, + 405: 0.145, + 410: 0.144, + 415: 0.141, + 420: 0.138, + 425: 0.134, + 430: 0.132, + 435: 0.132, + 440: 0.131, + 445: 0.131, + 450: 0.129, + 455: 0.128, + 460: 0.126, + 465: 0.126, + 470: 0.125, + 475: 0.123, + 480: 0.119, + 485: 0.114, + 490: 0.109, + 495: 0.105, + 500: 0.103, + 505: 0.102, + 510: 0.100, + 515: 0.097, + 520: 0.094, + 525: 0.091, + 530: 0.089, + 535: 0.090, + 540: 0.092, + 545: 0.096, + 550: 0.102, + 555: 0.106, + 560: 0.108, + 565: 0.109, + 570: 0.112, + 575: 0.126, + 580: 0.157, + 585: 0.208, + 590: 0.274, + 595: 0.346, + 600: 0.415, + 605: 0.473, + 610: 0.517, + 615: 0.547, + 620: 0.567, + 625: 0.582, + 630: 0.591, + 635: 0.597, + 640: 0.601, + 645: 0.604, + 650: 0.607, + 655: 0.608, + 660: 0.607, + 665: 0.606, + 670: 0.605, + 675: 0.605, + 680: 0.605, + 685: 0.604, + 690: 0.605, + 695: 0.606, + 700: 0.606, + 705: 0.604, + 710: 0.602, + 715: 0.601, + 720: 0.599, + 725: 0.598, + 730: 0.596, + 735: 0.595, + 740: 0.593, + 745: 0.587, + 750: 0.584, + 755: 0.584, + 760: 0.586, + 765: 0.584, + 770: 0.566, + 775: 0.551, + 780: 0.54, + }, + ), + ( + "purple", + { + 380: 0.101, + 385: 0.115, + 390: 0.135, + 395: 0.157, + 400: 0.177, + 405: 0.191, + 410: 0.199, + 415: 0.203, + 420: 0.206, + 425: 0.198, + 430: 0.190, + 435: 0.179, + 440: 0.168, + 445: 0.156, + 450: 0.144, + 455: 0.132, + 460: 0.120, + 465: 0.110, + 470: 0.101, + 475: 0.093, + 480: 0.086, + 485: 0.080, + 490: 0.075, + 495: 0.070, + 500: 0.067, + 505: 0.063, + 510: 0.061, + 515: 0.059, + 520: 0.058, + 525: 0.056, + 530: 0.054, + 535: 0.053, + 540: 0.052, + 545: 0.052, + 550: 0.053, + 555: 0.054, + 560: 0.055, + 565: 0.055, + 570: 0.054, + 575: 0.053, + 580: 0.052, + 585: 0.052, + 590: 0.053, + 595: 0.055, + 600: 0.059, + 605: 0.065, + 610: 0.074, + 615: 0.086, + 620: 0.099, + 625: 0.113, + 630: 0.126, + 635: 0.138, + 640: 0.149, + 645: 0.161, + 650: 0.172, + 655: 0.182, + 660: 0.193, + 665: 0.205, + 670: 0.217, + 675: 0.232, + 680: 0.248, + 685: 0.266, + 690: 0.282, + 695: 0.301, + 700: 0.319, + 705: 0.338, + 710: 0.355, + 715: 0.371, + 720: 0.388, + 725: 0.406, + 730: 0.422, + 735: 0.436, + 740: 0.451, + 745: 0.460, + 750: 0.471, + 755: 0.481, + 760: 0.492, + 765: 0.495, + 770: 0.482, + 775: 0.471, + 780: 0.467, + }, + ), + ( + "yellow green", + { + 380: 0.056, + 385: 0.058, + 390: 0.059, + 395: 0.059, + 400: 0.060, + 405: 0.061, + 410: 0.061, + 415: 0.061, + 420: 0.062, + 425: 0.063, + 430: 0.064, + 435: 0.066, + 440: 0.068, + 445: 0.071, + 450: 0.075, + 455: 0.079, + 460: 0.085, + 465: 0.093, + 470: 0.104, + 475: 0.118, + 480: 0.135, + 485: 0.157, + 490: 0.185, + 495: 0.221, + 500: 0.269, + 505: 0.326, + 510: 0.384, + 515: 0.440, + 520: 0.484, + 525: 0.516, + 530: 0.534, + 535: 0.542, + 540: 0.545, + 545: 0.541, + 550: 0.533, + 555: 0.524, + 560: 0.513, + 565: 0.501, + 570: 0.487, + 575: 0.472, + 580: 0.454, + 585: 0.436, + 590: 0.416, + 595: 0.394, + 600: 0.374, + 605: 0.358, + 610: 0.346, + 615: 0.337, + 620: 0.331, + 625: 0.328, + 630: 0.325, + 635: 0.322, + 640: 0.320, + 645: 0.319, + 650: 0.319, + 655: 0.320, + 660: 0.324, + 665: 0.330, + 670: 0.337, + 675: 0.345, + 680: 0.354, + 685: 0.362, + 690: 0.368, + 695: 0.375, + 700: 0.379, + 705: 0.381, + 710: 0.379, + 715: 0.376, + 720: 0.373, + 725: 0.372, + 730: 0.375, + 735: 0.382, + 740: 0.392, + 745: 0.401, + 750: 0.412, + 755: 0.422, + 760: 0.433, + 765: 0.436, + 770: 0.426, + 775: 0.413, + 780: 0.404, + }, + ), + ( + "orange yellow", + { + 380: 0.060, + 385: 0.061, + 390: 0.063, + 395: 0.064, + 400: 0.065, + 405: 0.065, + 410: 0.064, + 415: 0.064, + 420: 0.064, + 425: 0.064, + 430: 0.064, + 435: 0.065, + 440: 0.065, + 445: 0.066, + 450: 0.067, + 455: 0.068, + 460: 0.069, + 465: 0.073, + 470: 0.077, + 475: 0.084, + 480: 0.092, + 485: 0.100, + 490: 0.107, + 495: 0.115, + 500: 0.123, + 505: 0.133, + 510: 0.146, + 515: 0.166, + 520: 0.193, + 525: 0.229, + 530: 0.273, + 535: 0.323, + 540: 0.374, + 545: 0.418, + 550: 0.456, + 555: 0.487, + 560: 0.512, + 565: 0.534, + 570: 0.554, + 575: 0.570, + 580: 0.584, + 585: 0.598, + 590: 0.609, + 595: 0.617, + 600: 0.624, + 605: 0.630, + 610: 0.635, + 615: 0.640, + 620: 0.645, + 625: 0.650, + 630: 0.654, + 635: 0.658, + 640: 0.662, + 645: 0.667, + 650: 0.672, + 655: 0.675, + 660: 0.676, + 665: 0.677, + 670: 0.678, + 675: 0.681, + 680: 0.685, + 685: 0.688, + 690: 0.690, + 695: 0.693, + 700: 0.696, + 705: 0.698, + 710: 0.698, + 715: 0.698, + 720: 0.698, + 725: 0.700, + 730: 0.701, + 735: 0.701, + 740: 0.701, + 745: 0.695, + 750: 0.694, + 755: 0.696, + 760: 0.700, + 765: 0.698, + 770: 0.673, + 775: 0.653, + 780: 0.639, + }, + ), + ( + "blue", + { + 380: 0.069, + 385: 0.081, + 390: 0.096, + 395: 0.114, + 400: 0.136, + 405: 0.156, + 410: 0.175, + 415: 0.193, + 420: 0.208, + 425: 0.224, + 430: 0.244, + 435: 0.265, + 440: 0.290, + 445: 0.316, + 450: 0.335, + 455: 0.342, + 460: 0.338, + 465: 0.324, + 470: 0.302, + 475: 0.273, + 480: 0.239, + 485: 0.205, + 490: 0.172, + 495: 0.144, + 500: 0.120, + 505: 0.101, + 510: 0.086, + 515: 0.074, + 520: 0.066, + 525: 0.059, + 530: 0.054, + 535: 0.051, + 540: 0.048, + 545: 0.046, + 550: 0.045, + 555: 0.044, + 560: 0.043, + 565: 0.042, + 570: 0.041, + 575: 0.041, + 580: 0.040, + 585: 0.040, + 590: 0.040, + 595: 0.040, + 600: 0.039, + 605: 0.039, + 610: 0.040, + 615: 0.040, + 620: 0.040, + 625: 0.040, + 630: 0.041, + 635: 0.041, + 640: 0.042, + 645: 0.042, + 650: 0.042, + 655: 0.043, + 660: 0.043, + 665: 0.043, + 670: 0.044, + 675: 0.044, + 680: 0.044, + 685: 0.044, + 690: 0.045, + 695: 0.046, + 700: 0.048, + 705: 0.050, + 710: 0.051, + 715: 0.053, + 720: 0.056, + 725: 0.060, + 730: 0.064, + 735: 0.070, + 740: 0.079, + 745: 0.091, + 750: 0.104, + 755: 0.120, + 760: 0.138, + 765: 0.154, + 770: 0.168, + 775: 0.186, + 780: 0.204, + }, + ), + ( + "green", + { + 380: 0.055, + 385: 0.056, + 390: 0.057, + 395: 0.058, + 400: 0.058, + 405: 0.058, + 410: 0.059, + 415: 0.059, + 420: 0.059, + 425: 0.060, + 430: 0.062, + 435: 0.063, + 440: 0.065, + 445: 0.067, + 450: 0.070, + 455: 0.074, + 460: 0.078, + 465: 0.084, + 470: 0.091, + 475: 0.101, + 480: 0.113, + 485: 0.125, + 490: 0.140, + 495: 0.157, + 500: 0.180, + 505: 0.208, + 510: 0.244, + 515: 0.286, + 520: 0.324, + 525: 0.351, + 530: 0.363, + 535: 0.363, + 540: 0.355, + 545: 0.342, + 550: 0.323, + 555: 0.303, + 560: 0.281, + 565: 0.260, + 570: 0.238, + 575: 0.217, + 580: 0.196, + 585: 0.177, + 590: 0.158, + 595: 0.140, + 600: 0.124, + 605: 0.111, + 610: 0.101, + 615: 0.094, + 620: 0.089, + 625: 0.086, + 630: 0.084, + 635: 0.082, + 640: 0.080, + 645: 0.078, + 650: 0.077, + 655: 0.076, + 660: 0.075, + 665: 0.075, + 670: 0.075, + 675: 0.077, + 680: 0.078, + 685: 0.080, + 690: 0.082, + 695: 0.085, + 700: 0.088, + 705: 0.089, + 710: 0.089, + 715: 0.090, + 720: 0.090, + 725: 0.090, + 730: 0.089, + 735: 0.092, + 740: 0.094, + 745: 0.097, + 750: 0.102, + 755: 0.106, + 760: 0.110, + 765: 0.111, + 770: 0.112, + 775: 0.112, + 780: 0.112, + }, + ), + ( + "red", + { + 380: 0.052, + 385: 0.052, + 390: 0.052, + 395: 0.052, + 400: 0.051, + 405: 0.051, + 410: 0.050, + 415: 0.050, + 420: 0.049, + 425: 0.049, + 430: 0.049, + 435: 0.049, + 440: 0.049, + 445: 0.049, + 450: 0.049, + 455: 0.048, + 460: 0.048, + 465: 0.047, + 470: 0.047, + 475: 0.046, + 480: 0.045, + 485: 0.045, + 490: 0.044, + 495: 0.044, + 500: 0.044, + 505: 0.044, + 510: 0.044, + 515: 0.044, + 520: 0.044, + 525: 0.044, + 530: 0.044, + 535: 0.044, + 540: 0.045, + 545: 0.046, + 550: 0.047, + 555: 0.048, + 560: 0.050, + 565: 0.053, + 570: 0.057, + 575: 0.063, + 580: 0.072, + 585: 0.086, + 590: 0.109, + 595: 0.143, + 600: 0.192, + 605: 0.256, + 610: 0.332, + 615: 0.413, + 620: 0.486, + 625: 0.550, + 630: 0.598, + 635: 0.631, + 640: 0.654, + 645: 0.672, + 650: 0.686, + 655: 0.694, + 660: 0.700, + 665: 0.704, + 670: 0.707, + 675: 0.712, + 680: 0.718, + 685: 0.721, + 690: 0.724, + 695: 0.727, + 700: 0.729, + 705: 0.730, + 710: 0.730, + 715: 0.729, + 720: 0.727, + 725: 0.728, + 730: 0.729, + 735: 0.729, + 740: 0.727, + 745: 0.723, + 750: 0.721, + 755: 0.724, + 760: 0.728, + 765: 0.727, + 770: 0.702, + 775: 0.680, + 780: 0.664, + }, + ), + ( + "yellow", + { + 380: 0.054, + 385: 0.053, + 390: 0.054, + 395: 0.053, + 400: 0.053, + 405: 0.053, + 410: 0.053, + 415: 0.052, + 420: 0.052, + 425: 0.052, + 430: 0.053, + 435: 0.053, + 440: 0.053, + 445: 0.054, + 450: 0.055, + 455: 0.056, + 460: 0.059, + 465: 0.065, + 470: 0.075, + 475: 0.093, + 480: 0.121, + 485: 0.157, + 490: 0.202, + 495: 0.252, + 500: 0.303, + 505: 0.351, + 510: 0.394, + 515: 0.436, + 520: 0.475, + 525: 0.512, + 530: 0.544, + 535: 0.572, + 540: 0.597, + 545: 0.615, + 550: 0.630, + 555: 0.645, + 560: 0.660, + 565: 0.673, + 570: 0.686, + 575: 0.698, + 580: 0.708, + 585: 0.718, + 590: 0.726, + 595: 0.732, + 600: 0.737, + 605: 0.742, + 610: 0.746, + 615: 0.749, + 620: 0.753, + 625: 0.757, + 630: 0.761, + 635: 0.765, + 640: 0.768, + 645: 0.772, + 650: 0.777, + 655: 0.779, + 660: 0.780, + 665: 0.780, + 670: 0.781, + 675: 0.782, + 680: 0.785, + 685: 0.785, + 690: 0.787, + 695: 0.789, + 700: 0.792, + 705: 0.792, + 710: 0.793, + 715: 0.792, + 720: 0.790, + 725: 0.792, + 730: 0.792, + 735: 0.790, + 740: 0.787, + 745: 0.782, + 750: 0.778, + 755: 0.780, + 760: 0.782, + 765: 0.781, + 770: 0.752, + 775: 0.728, + 780: 0.71, + }, + ), + ( + "magenta", + { + 380: 0.118, + 385: 0.142, + 390: 0.179, + 395: 0.228, + 400: 0.283, + 405: 0.322, + 410: 0.343, + 415: 0.354, + 420: 0.359, + 425: 0.357, + 430: 0.350, + 435: 0.339, + 440: 0.327, + 445: 0.313, + 450: 0.298, + 455: 0.282, + 460: 0.267, + 465: 0.253, + 470: 0.239, + 475: 0.225, + 480: 0.209, + 485: 0.195, + 490: 0.182, + 495: 0.172, + 500: 0.163, + 505: 0.155, + 510: 0.146, + 515: 0.135, + 520: 0.124, + 525: 0.113, + 530: 0.106, + 535: 0.102, + 540: 0.102, + 545: 0.105, + 550: 0.107, + 555: 0.107, + 560: 0.106, + 565: 0.107, + 570: 0.112, + 575: 0.123, + 580: 0.141, + 585: 0.166, + 590: 0.198, + 595: 0.235, + 600: 0.279, + 605: 0.333, + 610: 0.394, + 615: 0.460, + 620: 0.522, + 625: 0.580, + 630: 0.628, + 635: 0.666, + 640: 0.696, + 645: 0.722, + 650: 0.742, + 655: 0.756, + 660: 0.766, + 665: 0.774, + 670: 0.780, + 675: 0.785, + 680: 0.791, + 685: 0.794, + 690: 0.798, + 695: 0.801, + 700: 0.804, + 705: 0.806, + 710: 0.807, + 715: 0.807, + 720: 0.807, + 725: 0.810, + 730: 0.813, + 735: 0.814, + 740: 0.813, + 745: 0.810, + 750: 0.808, + 755: 0.811, + 760: 0.814, + 765: 0.813, + 770: 0.785, + 775: 0.765, + 780: 0.752, + }, + ), + ( + "cyan", + { + 380: 0.093, + 385: 0.110, + 390: 0.134, + 395: 0.164, + 400: 0.195, + 405: 0.220, + 410: 0.238, + 415: 0.249, + 420: 0.258, + 425: 0.270, + 430: 0.281, + 435: 0.296, + 440: 0.315, + 445: 0.334, + 450: 0.352, + 455: 0.370, + 460: 0.391, + 465: 0.414, + 470: 0.434, + 475: 0.449, + 480: 0.458, + 485: 0.461, + 490: 0.457, + 495: 0.447, + 500: 0.433, + 505: 0.414, + 510: 0.392, + 515: 0.366, + 520: 0.339, + 525: 0.310, + 530: 0.282, + 535: 0.255, + 540: 0.228, + 545: 0.204, + 550: 0.180, + 555: 0.159, + 560: 0.141, + 565: 0.126, + 570: 0.114, + 575: 0.104, + 580: 0.097, + 585: 0.092, + 590: 0.088, + 595: 0.083, + 600: 0.080, + 605: 0.077, + 610: 0.075, + 615: 0.074, + 620: 0.073, + 625: 0.073, + 630: 0.073, + 635: 0.073, + 640: 0.073, + 645: 0.073, + 650: 0.074, + 655: 0.075, + 660: 0.076, + 665: 0.076, + 670: 0.077, + 675: 0.076, + 680: 0.075, + 685: 0.074, + 690: 0.074, + 695: 0.073, + 700: 0.072, + 705: 0.072, + 710: 0.071, + 715: 0.073, + 720: 0.075, + 725: 0.078, + 730: 0.082, + 735: 0.090, + 740: 0.100, + 745: 0.116, + 750: 0.133, + 755: 0.154, + 760: 0.176, + 765: 0.191, + 770: 0.200, + 775: 0.208, + 780: 0.214, + }, + ), + ( + "white 9.5 (.05 D)", + { + 380: 0.153, + 385: 0.189, + 390: 0.245, + 395: 0.319, + 400: 0.409, + 405: 0.536, + 410: 0.671, + 415: 0.772, + 420: 0.840, + 425: 0.868, + 430: 0.878, + 435: 0.882, + 440: 0.883, + 445: 0.885, + 450: 0.886, + 455: 0.886, + 460: 0.887, + 465: 0.888, + 470: 0.888, + 475: 0.888, + 480: 0.888, + 485: 0.888, + 490: 0.888, + 495: 0.888, + 500: 0.887, + 505: 0.887, + 510: 0.887, + 515: 0.887, + 520: 0.887, + 525: 0.887, + 530: 0.887, + 535: 0.887, + 540: 0.887, + 545: 0.886, + 550: 0.886, + 555: 0.887, + 560: 0.887, + 565: 0.887, + 570: 0.888, + 575: 0.888, + 580: 0.887, + 585: 0.886, + 590: 0.886, + 595: 0.886, + 600: 0.887, + 605: 0.888, + 610: 0.889, + 615: 0.890, + 620: 0.891, + 625: 0.891, + 630: 0.891, + 635: 0.891, + 640: 0.890, + 645: 0.889, + 650: 0.889, + 655: 0.889, + 660: 0.889, + 665: 0.889, + 670: 0.888, + 675: 0.888, + 680: 0.888, + 685: 0.888, + 690: 0.888, + 695: 0.888, + 700: 0.888, + 705: 0.887, + 710: 0.886, + 715: 0.886, + 720: 0.886, + 725: 0.885, + 730: 0.885, + 735: 0.885, + 740: 0.884, + 745: 0.884, + 750: 0.883, + 755: 0.882, + 760: 0.882, + 765: 0.881, + 770: 0.880, + 775: 0.880, + 780: 0.879, + }, + ), + ( + "neutral 8 (.23 D)", + { + 380: 0.150, + 385: 0.184, + 390: 0.235, + 395: 0.299, + 400: 0.372, + 405: 0.459, + 410: 0.529, + 415: 0.564, + 420: 0.580, + 425: 0.584, + 430: 0.585, + 435: 0.587, + 440: 0.587, + 445: 0.588, + 450: 0.588, + 455: 0.587, + 460: 0.586, + 465: 0.585, + 470: 0.583, + 475: 0.582, + 480: 0.581, + 485: 0.580, + 490: 0.580, + 495: 0.580, + 500: 0.580, + 505: 0.580, + 510: 0.580, + 515: 0.581, + 520: 0.581, + 525: 0.582, + 530: 0.582, + 535: 0.582, + 540: 0.583, + 545: 0.583, + 550: 0.583, + 555: 0.584, + 560: 0.584, + 565: 0.585, + 570: 0.586, + 575: 0.587, + 580: 0.588, + 585: 0.588, + 590: 0.588, + 595: 0.588, + 600: 0.588, + 605: 0.587, + 610: 0.586, + 615: 0.586, + 620: 0.585, + 625: 0.584, + 630: 0.583, + 635: 0.581, + 640: 0.580, + 645: 0.579, + 650: 0.578, + 655: 0.577, + 660: 0.576, + 665: 0.575, + 670: 0.574, + 675: 0.573, + 680: 0.572, + 685: 0.571, + 690: 0.570, + 695: 0.569, + 700: 0.568, + 705: 0.567, + 710: 0.566, + 715: 0.565, + 720: 0.564, + 725: 0.562, + 730: 0.562, + 735: 0.560, + 740: 0.560, + 745: 0.558, + 750: 0.557, + 755: 0.556, + 760: 0.555, + 765: 0.554, + 770: 0.553, + 775: 0.551, + 780: 0.55, + }, + ), + ( + "neutral 6.5 (.44 D)", + { + 380: 0.138, + 385: 0.167, + 390: 0.206, + 395: 0.249, + 400: 0.289, + 405: 0.324, + 410: 0.346, + 415: 0.354, + 420: 0.357, + 425: 0.358, + 430: 0.359, + 435: 0.360, + 440: 0.361, + 445: 0.362, + 450: 0.362, + 455: 0.361, + 460: 0.361, + 465: 0.359, + 470: 0.358, + 475: 0.358, + 480: 0.357, + 485: 0.356, + 490: 0.356, + 495: 0.356, + 500: 0.356, + 505: 0.356, + 510: 0.356, + 515: 0.356, + 520: 0.357, + 525: 0.357, + 530: 0.357, + 535: 0.358, + 540: 0.358, + 545: 0.358, + 550: 0.358, + 555: 0.358, + 560: 0.359, + 565: 0.359, + 570: 0.360, + 575: 0.361, + 580: 0.361, + 585: 0.361, + 590: 0.361, + 595: 0.361, + 600: 0.360, + 605: 0.360, + 610: 0.359, + 615: 0.358, + 620: 0.357, + 625: 0.356, + 630: 0.355, + 635: 0.354, + 640: 0.353, + 645: 0.352, + 650: 0.351, + 655: 0.350, + 660: 0.349, + 665: 0.348, + 670: 0.346, + 675: 0.346, + 680: 0.345, + 685: 0.344, + 690: 0.343, + 695: 0.342, + 700: 0.341, + 705: 0.340, + 710: 0.339, + 715: 0.338, + 720: 0.337, + 725: 0.336, + 730: 0.335, + 735: 0.334, + 740: 0.333, + 745: 0.332, + 750: 0.331, + 755: 0.330, + 760: 0.329, + 765: 0.328, + 770: 0.327, + 775: 0.326, + 780: 0.325, + }, + ), + ( + "neutral 5 (.70 D)", + { + 380: 0.113, + 385: 0.131, + 390: 0.150, + 395: 0.169, + 400: 0.183, + 405: 0.193, + 410: 0.199, + 415: 0.201, + 420: 0.202, + 425: 0.203, + 430: 0.203, + 435: 0.204, + 440: 0.205, + 445: 0.205, + 450: 0.205, + 455: 0.205, + 460: 0.204, + 465: 0.204, + 470: 0.203, + 475: 0.203, + 480: 0.202, + 485: 0.202, + 490: 0.202, + 495: 0.202, + 500: 0.202, + 505: 0.202, + 510: 0.202, + 515: 0.202, + 520: 0.202, + 525: 0.202, + 530: 0.203, + 535: 0.203, + 540: 0.203, + 545: 0.203, + 550: 0.203, + 555: 0.203, + 560: 0.203, + 565: 0.203, + 570: 0.204, + 575: 0.204, + 580: 0.205, + 585: 0.205, + 590: 0.205, + 595: 0.205, + 600: 0.204, + 605: 0.204, + 610: 0.204, + 615: 0.203, + 620: 0.203, + 625: 0.202, + 630: 0.201, + 635: 0.201, + 640: 0.200, + 645: 0.199, + 650: 0.198, + 655: 0.198, + 660: 0.197, + 665: 0.197, + 670: 0.196, + 675: 0.195, + 680: 0.195, + 685: 0.194, + 690: 0.194, + 695: 0.193, + 700: 0.192, + 705: 0.192, + 710: 0.191, + 715: 0.191, + 720: 0.190, + 725: 0.189, + 730: 0.189, + 735: 0.188, + 740: 0.188, + 745: 0.187, + 750: 0.187, + 755: 0.186, + 760: 0.185, + 765: 0.185, + 770: 0.184, + 775: 0.184, + 780: 0.183, + }, + ), + ( + "neutral 3.5 (1.05 D)", + { + 380: 0.074, + 385: 0.079, + 390: 0.084, + 395: 0.088, + 400: 0.091, + 405: 0.093, + 410: 0.094, + 415: 0.094, + 420: 0.094, + 425: 0.094, + 430: 0.094, + 435: 0.095, + 440: 0.095, + 445: 0.095, + 450: 0.095, + 455: 0.094, + 460: 0.094, + 465: 0.094, + 470: 0.094, + 475: 0.093, + 480: 0.093, + 485: 0.093, + 490: 0.093, + 495: 0.092, + 500: 0.092, + 505: 0.093, + 510: 0.093, + 515: 0.093, + 520: 0.093, + 525: 0.093, + 530: 0.093, + 535: 0.093, + 540: 0.093, + 545: 0.093, + 550: 0.093, + 555: 0.092, + 560: 0.093, + 565: 0.093, + 570: 0.093, + 575: 0.093, + 580: 0.093, + 585: 0.093, + 590: 0.093, + 595: 0.092, + 600: 0.092, + 605: 0.092, + 610: 0.092, + 615: 0.091, + 620: 0.091, + 625: 0.091, + 630: 0.090, + 635: 0.090, + 640: 0.090, + 645: 0.090, + 650: 0.089, + 655: 0.089, + 660: 0.089, + 665: 0.088, + 670: 0.088, + 675: 0.088, + 680: 0.087, + 685: 0.087, + 690: 0.087, + 695: 0.087, + 700: 0.086, + 705: 0.086, + 710: 0.086, + 715: 0.086, + 720: 0.085, + 725: 0.085, + 730: 0.085, + 735: 0.085, + 740: 0.085, + 745: 0.084, + 750: 0.084, + 755: 0.084, + 760: 0.084, + 765: 0.084, + 770: 0.083, + 775: 0.083, + 780: 0.083, + }, + ), + ( + "black 2 (1.5 D)", + { + 380: 0.032, + 385: 0.033, + 390: 0.033, + 395: 0.034, + 400: 0.035, + 405: 0.035, + 410: 0.036, + 415: 0.036, + 420: 0.036, + 425: 0.036, + 430: 0.036, + 435: 0.036, + 440: 0.035, + 445: 0.035, + 450: 0.035, + 455: 0.035, + 460: 0.035, + 465: 0.035, + 470: 0.035, + 475: 0.035, + 480: 0.034, + 485: 0.034, + 490: 0.034, + 495: 0.034, + 500: 0.034, + 505: 0.034, + 510: 0.034, + 515: 0.034, + 520: 0.034, + 525: 0.034, + 530: 0.034, + 535: 0.034, + 540: 0.034, + 545: 0.034, + 550: 0.034, + 555: 0.034, + 560: 0.033, + 565: 0.033, + 570: 0.033, + 575: 0.033, + 580: 0.033, + 585: 0.033, + 590: 0.033, + 595: 0.033, + 600: 0.033, + 605: 0.033, + 610: 0.033, + 615: 0.033, + 620: 0.033, + 625: 0.033, + 630: 0.033, + 635: 0.033, + 640: 0.033, + 645: 0.033, + 650: 0.033, + 655: 0.033, + 660: 0.033, + 665: 0.033, + 670: 0.033, + 675: 0.033, + 680: 0.033, + 685: 0.033, + 690: 0.032, + 695: 0.032, + 700: 0.032, + 705: 0.032, + 710: 0.032, + 715: 0.032, + 720: 0.032, + 725: 0.032, + 730: 0.032, + 735: 0.032, + 740: 0.032, + 745: 0.032, + 750: 0.032, + 755: 0.032, + 760: 0.032, + 765: 0.032, + 770: 0.032, + 775: 0.032, + 780: 0.032, + }, + ), + ] +) -SDS_COLORCHECKER_N_OHTA = OrderedDict( - (key, SpectralDistribution(value, name=key)) - for key, value in DATA_COLORCHECKER_N_OHTA.items()) +SDS_COLORCHECKER_N_OHTA: LazyCaseInsensitiveMapping = ( + LazyCaseInsensitiveMapping( + (key, partial(SpectralDistribution, value, name=key)) + for key, value in DATA_COLORCHECKER_N_OHTA.items() + ) +) """ *ColorChecker Classic* data Measured by *Ohta (1997)*. References ---------- :cite:`Ohta1997a`, :cite:`MunsellColorScienceb` - -SDS_COLORCHECKER_N_OHTA : dict """ -SDS_COLOURCHECKERS = CaseInsensitiveMapping({ - 'BabelColor Average': SDS_BABELCOLOR_AVERAGE, - 'ColorChecker N Ohta': SDS_COLORCHECKER_N_OHTA, -}) +SDS_COLOURCHECKERS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "BabelColor Average": SDS_BABELCOLOR_AVERAGE, + "ColorChecker N Ohta": SDS_COLORCHECKER_N_OHTA, + } +) SDS_COLOURCHECKERS.__doc__ = """ Spectral distributions of the colour checkers. @@ -3017,9 +3172,6 @@ class instance of :class:`colour.SpectralDistribution` classes as :cite:`MunsellColorScienceb`, :cite:`InternationalOrganizationforStandardization2012` -SDS_COLOURCHECKERS : CaseInsensitiveMapping - **{'BabelColor Average', 'ColorChecker N Ohta', 'ISO 17321-1'}** - Notes ----- - Data from :cite:`InternationalOrganizationforStandardization2012` and @@ -3031,6 +3183,6 @@ class instance of :class:`colour.SpectralDistribution` classes as - 'cc_ohta': 'ColorChecker N Ohta' - 'ISO 17321-1': 'ColorChecker N Ohta' """ -SDS_COLOURCHECKERS['babel_average'] = SDS_COLOURCHECKERS['BabelColor Average'] -SDS_COLOURCHECKERS['cc_ohta'] = SDS_COLOURCHECKERS['ColorChecker N Ohta'] -SDS_COLOURCHECKERS['ISO 17321-1'] = SDS_COLOURCHECKERS['ColorChecker N Ohta'] +SDS_COLOURCHECKERS["babel_average"] = SDS_COLOURCHECKERS["BabelColor Average"] +SDS_COLOURCHECKERS["cc_ohta"] = SDS_COLOURCHECKERS["ColorChecker N Ohta"] +SDS_COLOURCHECKERS["ISO 17321-1"] = SDS_COLOURCHECKERS["ColorChecker N Ohta"] diff --git a/colour/characterisation/datasets/displays/__init__.py b/colour/characterisation/datasets/displays/__init__.py index 5776fd07f0..b6d1f6088f 100644 --- a/colour/characterisation/datasets/displays/__init__.py +++ b/colour/characterisation/datasets/displays/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ References ---------- @@ -12,13 +11,11 @@ http://www.lume.ufrgs.br/handle/10183/26950 """ -from __future__ import absolute_import - from .crt import MSDS_DISPLAY_PRIMARIES_CRT from .lcd import MSDS_DISPLAY_PRIMARIES_LCD -from colour.utilities import CaseInsensitiveMapping +from colour.utilities import LazyCaseInsensitiveMapping -MSDS_DISPLAY_PRIMARIES = CaseInsensitiveMapping(MSDS_DISPLAY_PRIMARIES_CRT) +MSDS_DISPLAY_PRIMARIES = LazyCaseInsensitiveMapping(MSDS_DISPLAY_PRIMARIES_CRT) MSDS_DISPLAY_PRIMARIES.update(MSDS_DISPLAY_PRIMARIES_LCD) MSDS_DISPLAY_PRIMARIES.__doc__ = """ Primaries multi-spectral distributions of displays. @@ -26,9 +23,8 @@ References ---------- :cite:`Fairchild1998b`, :cite:`Machado2010a` - -MSDS_DISPLAY_PRIMARIES : CaseInsensitiveMapping - **{Apple Studio Display, Typical CRT Brainard 1997}** """ -__all__ = ['MSDS_DISPLAY_PRIMARIES'] +__all__ = [ + "MSDS_DISPLAY_PRIMARIES", +] diff --git a/colour/characterisation/datasets/displays/crt/__init__.py b/colour/characterisation/datasets/displays/crt/__init__.py index d90ed1b14a..d073253d43 100644 --- a/colour/characterisation/datasets/displays/crt/__init__.py +++ b/colour/characterisation/datasets/displays/crt/__init__.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .primaries import MSDS_DISPLAY_PRIMARIES_CRT -__all__ = ['MSDS_DISPLAY_PRIMARIES_CRT'] +__all__ = [ + "MSDS_DISPLAY_PRIMARIES_CRT", +] diff --git a/colour/characterisation/datasets/displays/crt/primaries.py b/colour/characterisation/datasets/displays/crt/primaries.py index 1ad989af50..158b0e8d22 100644 --- a/colour/characterisation/datasets/displays/crt/primaries.py +++ b/colour/characterisation/datasets/displays/crt/primaries.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Primaries of CRT Displays ========================= @@ -8,9 +7,11 @@ Each *CRT* display data is in the form of a *dict* of :class:`colour.characterisation.RGB_DisplayPrimaries` classes as follows:: - {'name': RGB_DisplayPrimaries, - ..., - 'name': RGB_DisplayPrimaries} + { + 'name': RGB_DisplayPrimaries, + ..., + 'name': RGB_DisplayPrimaries + } The following *CRT* displays are available: @@ -24,22 +25,28 @@ http://www.lume.ufrgs.br/handle/10183/26950 """ -from __future__ import division, unicode_literals +from __future__ import annotations + +from functools import partial from colour.characterisation import RGB_DisplayPrimaries -from colour.utilities import CaseInsensitiveMapping +from colour.hints import Dict +from colour.utilities import LazyCaseInsensitiveMapping -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['DATA_DISPLAY_PRIMARIES_CRT', 'MSDS_DISPLAY_PRIMARIES_CRT'] +__all__ = [ + "DATA_DISPLAY_PRIMARIES_CRT", + "MSDS_DISPLAY_PRIMARIES_CRT", +] -DATA_DISPLAY_PRIMARIES_CRT = { - 'Typical CRT Brainard 1997': { +DATA_DISPLAY_PRIMARIES_CRT: Dict = { + "Typical CRT Brainard 1997": { 380.0: (0.0025, 0.0018, 0.0219), 385.0: (0.0017, 0.0016, 0.0336), 390.0: (0.0017, 0.0020, 0.0524), @@ -120,23 +127,25 @@ 765.0: (0.0067, 0.0018, 0.0015), 770.0: (0.0070, 0.0021, 0.0028), 775.0: (0.0073, 0.0015, 0.0046), - 780.0: (0.0066, 0.0018, 0.0058) + 780.0: (0.0066, 0.0018, 0.0058), } } -MSDS_DISPLAY_PRIMARIES_CRT = CaseInsensitiveMapping({ - 'Typical CRT Brainard 1997': - RGB_DisplayPrimaries( - DATA_DISPLAY_PRIMARIES_CRT['Typical CRT Brainard 1997'], - name='Typical CRT Brainard 1997') -}) +MSDS_DISPLAY_PRIMARIES_CRT: LazyCaseInsensitiveMapping = ( + LazyCaseInsensitiveMapping( + { + "Typical CRT Brainard 1997": partial( + RGB_DisplayPrimaries, + DATA_DISPLAY_PRIMARIES_CRT["Typical CRT Brainard 1997"], + name="Typical CRT Brainard 1997", + ) + } + ) +) """ Primaries multi-spectral distributions of *CRT* displays. References ---------- :cite:`Machado2010a` - -MSDS_DISPLAY_PRIMARIES_CRT : CaseInsensitiveMapping - **{'Typical CRT Brainard 1997'}** """ diff --git a/colour/characterisation/datasets/displays/lcd/__init__.py b/colour/characterisation/datasets/displays/lcd/__init__.py index 2d8b618ba2..e053aa0784 100644 --- a/colour/characterisation/datasets/displays/lcd/__init__.py +++ b/colour/characterisation/datasets/displays/lcd/__init__.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .primaries import MSDS_DISPLAY_PRIMARIES_LCD -__all__ = ['MSDS_DISPLAY_PRIMARIES_LCD'] +__all__ = [ + "MSDS_DISPLAY_PRIMARIES_LCD", +] diff --git a/colour/characterisation/datasets/displays/lcd/primaries.py b/colour/characterisation/datasets/displays/lcd/primaries.py index 1e1ae86091..29db36490f 100644 --- a/colour/characterisation/datasets/displays/lcd/primaries.py +++ b/colour/characterisation/datasets/displays/lcd/primaries.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Primaries of LCD Displays ========================= @@ -8,9 +7,11 @@ Each *LCD* display data is in the form of a *dict* of :class:`colour.characterisation.RGB_DisplayPrimaries` classes as follows:: - {'name': RGB_DisplayPrimaries, - ..., - 'name': RGB_DisplayPrimaries} + { + 'name': RGB_DisplayPrimaries, + ..., + 'name': RGB_DisplayPrimaries + } The following *LCD* displays are available: @@ -28,22 +29,28 @@ http://www.lume.ufrgs.br/handle/10183/26950 """ -from __future__ import division, unicode_literals +from __future__ import annotations + +from functools import partial from colour.characterisation import RGB_DisplayPrimaries -from colour.utilities import CaseInsensitiveMapping +from colour.hints import Dict +from colour.utilities import LazyCaseInsensitiveMapping -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['DATA_DISPLAY_PRIMARIES_LCD', 'MSDS_DISPLAY_PRIMARIES_LCD'] +__all__ = [ + "DATA_DISPLAY_PRIMARIES_LCD", + "MSDS_DISPLAY_PRIMARIES_LCD", +] -DATA_DISPLAY_PRIMARIES_LCD = { - 'Apple Studio Display': { +DATA_DISPLAY_PRIMARIES_LCD: Dict = { + "Apple Studio Display": { 380: (0.0000, 0.0000, 0.0000), 385: (0.0000, 0.0000, 0.0000), 390: (0.0000, 0.0000, 0.0000), @@ -124,23 +131,25 @@ 765: (0.0000, 0.0000, 0.0000), 770: (0.0000, 0.0000, 0.0000), 775: (0.0000, 0.0119, 0.0000), - 780: (0.0000, 0.0000, 0.0000) + 780: (0.0000, 0.0000, 0.0000), } } -MSDS_DISPLAY_PRIMARIES_LCD = CaseInsensitiveMapping({ - 'Apple Studio Display': - RGB_DisplayPrimaries( - DATA_DISPLAY_PRIMARIES_LCD['Apple Studio Display'], - name='Apple Studio Display') -}) +MSDS_DISPLAY_PRIMARIES_LCD: LazyCaseInsensitiveMapping = ( + LazyCaseInsensitiveMapping( + { + "Apple Studio Display": partial( + RGB_DisplayPrimaries, + DATA_DISPLAY_PRIMARIES_LCD["Apple Studio Display"], + name="Apple Studio Display", + ) + } + ) +) """ Primaries multi-spectral distributions of *LCD* displays. References ---------- :cite:`Fairchild1998b`, :cite:`Machado2010a` - -MSDS_DISPLAY_PRIMARIES_LCD : CaseInsensitiveMapping - **{'Apple Studio Display'}** """ diff --git a/colour/characterisation/datasets/filters/__init__.py b/colour/characterisation/datasets/filters/__init__.py index 80d6b69915..88be30ca3f 100644 --- a/colour/characterisation/datasets/filters/__init__.py +++ b/colour/characterisation/datasets/filters/__init__.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .sds import SDS_FILTERS -__all__ = ['SDS_FILTERS'] +__all__ = [ + "SDS_FILTERS", +] diff --git a/colour/characterisation/datasets/filters/sds.py b/colour/characterisation/datasets/filters/sds.py index a367df6a24..c5eb9a5047 100644 --- a/colour/characterisation/datasets/filters/sds.py +++ b/colour/characterisation/datasets/filters/sds.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- """ Spectral Distributions of Filters ================================= Defines the spectral distributions of filters. -Each filter data is in the form of :class:`OrderedDict` class instance of +Each filter data is in the form of :class:`dict` class instance of :class:`colour.SpectralDistribution` classes as follows:: {'name': SpectralDistribution, ..., 'name': SpectralDistribution} @@ -16,27 +15,35 @@ References ---------- -- :cite:`ISO2002` : ISO. (2002). INTERNATIONAL STANDARD 7589-2002 - - Photography - Illuminants for sensitometry - Specifications for daylight, - incandescent tungsten and printer. +- :cite:`InternationalOrganizationforStandardization2002` : International + Organization for Standardization. (2002). INTERNATIONAL STANDARD ISO + 7589-2002 - Photography - Illuminants for sensitometry - Specifications for + daylight, incandescent tungsten and printer. """ -from __future__ import division, unicode_literals +from __future__ import annotations -from colour.colorimetry import SpectralDistribution -from colour.utilities import CaseInsensitiveMapping - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from functools import partial -__all__ = ['DATA_FILTERS_ISO', 'SDS_FILTERS_ISO', 'SDS_FILTERS'] - -DATA_FILTERS_ISO = { - 'ISO 7589 Diffuser': { +from colour.colorimetry import SpectralDistribution +from colour.hints import Dict +from colour.utilities import LazyCaseInsensitiveMapping + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "DATA_FILTERS_ISO", + "SDS_FILTERS_ISO", + "SDS_FILTERS", +] + +DATA_FILTERS_ISO: Dict = { + "ISO 7589 Diffuser": { 350: 0.00, 360: 0.00, 370: 0.00, @@ -62,28 +69,30 @@ } } -SDS_FILTERS_ISO = CaseInsensitiveMapping({ - 'ISO 7589 Diffuser': - SpectralDistribution( - DATA_FILTERS_ISO['ISO 7589 Diffuser'], name='ISO 7589 Diffuser'), -}) +SDS_FILTERS_ISO: LazyCaseInsensitiveMapping = LazyCaseInsensitiveMapping( + { + "ISO 7589 Diffuser": partial( + SpectralDistribution, + DATA_FILTERS_ISO["ISO 7589 Diffuser"], + name="ISO 7589 Diffuser", + ), + } +) SDS_FILTERS_ISO.__doc__ = """ Spectral distributions of *ISO* filters. References ---------- -:cite:`ISO2002` - -SDS_FILTERS_ISO : CaseInsensitiveMapping +:cite:`InternationalOrganizationforStandardization2002` """ -SDS_FILTERS = CaseInsensitiveMapping(SDS_FILTERS_ISO) +SDS_FILTERS: LazyCaseInsensitiveMapping = LazyCaseInsensitiveMapping( + SDS_FILTERS_ISO +) SDS_FILTERS.__doc__ = """ Spectral distributions of filters. References ---------- -:cite:`ISO2002` - -SDS_FILTERS : CaseInsensitiveMapping +:cite:`InternationalOrganizationforStandardization2002` """ diff --git a/colour/characterisation/datasets/lenses/__init__.py b/colour/characterisation/datasets/lenses/__init__.py index 683c84e66b..c2409a1d3d 100644 --- a/colour/characterisation/datasets/lenses/__init__.py +++ b/colour/characterisation/datasets/lenses/__init__.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .sds import SDS_LENSES -__all__ = ['SDS_LENSES'] +__all__ = [ + "SDS_LENSES", +] diff --git a/colour/characterisation/datasets/lenses/sds.py b/colour/characterisation/datasets/lenses/sds.py index 0976d357da..940200c23d 100644 --- a/colour/characterisation/datasets/lenses/sds.py +++ b/colour/characterisation/datasets/lenses/sds.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- """ Spectral Distributions of Lenses ================================ -Defines spectral distributions of lenses. +Defines the spectral distributions of lenses. -Each lens data is in the form of :class:`OrderedDict` class instance of +Each lens data is in the form of :class:`dict` class instance of :class:`colour.SpectralDistribution` classes as follows:: {'name': SpectralDistribution, ..., 'name': SpectralDistribution} @@ -16,27 +15,35 @@ References ---------- -- :cite:`ISO2002` : ISO. (2002). INTERNATIONAL STANDARD 7589-2002 - - Photography - Illuminants for sensitometry - Specifications for daylight, - incandescent tungsten and printer. +- :cite:`InternationalOrganizationforStandardization2002` : International + Organization for Standardization. (2002). INTERNATIONAL STANDARD ISO + 7589-2002 - Photography - Illuminants for sensitometry - Specifications for + daylight, incandescent tungsten and printer. """ -from __future__ import division, unicode_literals +from __future__ import annotations -from colour.colorimetry import SpectralDistribution -from colour.utilities import CaseInsensitiveMapping - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from functools import partial -__all__ = ['DATA_LENSES_ISO', 'SDS_LENSES_ISO', 'SDS_LENSES'] - -DATA_LENSES_ISO = { - 'ISO Standard Lens': { +from colour.colorimetry import SpectralDistribution +from colour.hints import Dict +from colour.utilities import LazyCaseInsensitiveMapping + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "DATA_LENSES_ISO", + "SDS_LENSES_ISO", + "SDS_LENSES", +] + +DATA_LENSES_ISO: Dict = { + "ISO Standard Lens": { 350: 0.00, 360: 0.07, 370: 0.23, @@ -75,28 +82,30 @@ } } -SDS_LENSES_ISO = CaseInsensitiveMapping({ - 'ISO Standard Lens': - SpectralDistribution( - DATA_LENSES_ISO['ISO Standard Lens'], name='ISO Standard Lens'), -}) +SDS_LENSES_ISO: LazyCaseInsensitiveMapping = LazyCaseInsensitiveMapping( + { + "ISO Standard Lens": partial( + SpectralDistribution, + DATA_LENSES_ISO["ISO Standard Lens"], + name="ISO Standard Lens", + ), + } +) SDS_LENSES_ISO.__doc__ = """ Spectral distributions of *ISO* lenses. References ---------- -:cite:`ISO2002` - -SDS_LENSES_ISO : CaseInsensitiveMapping +:cite:`InternationalOrganizationforStandardization2002` """ -SDS_LENSES = CaseInsensitiveMapping(SDS_LENSES_ISO) +SDS_LENSES: LazyCaseInsensitiveMapping = LazyCaseInsensitiveMapping( + SDS_LENSES_ISO +) SDS_LENSES.__doc__ = """ Spectral distributions of lenses. References ---------- -:cite:`ISO2002` - -SDS_LENSES : CaseInsensitiveMapping +:cite:`InternationalOrganizationforStandardization2002` """ diff --git a/colour/characterisation/displays.py b/colour/characterisation/displays.py index aa8209e07c..d1dd2c4ab0 100644 --- a/colour/characterisation/displays.py +++ b/colour/characterisation/displays.py @@ -1,72 +1,106 @@ -# -*- coding: utf-8 -*- """ RGB Display Primaries ===================== -Defines spectral distributions classes for the datasets from -:mod:`colour.characterisation.datasets.displays` module: +Defines the spectral distributions classes for the datasets from +the :mod:`colour.characterisation.datasets.displays` module: - :class:`colour.characterisation.RGB_DisplayPrimaries`: Implements support for a *RGB* display (such as a *CRT* or *LCD*) primaries multi-spectral distributions. """ -from __future__ import division, unicode_literals +from __future__ import annotations -from colour.colorimetry import MultiSpectralDistributions +from colour.colorimetry import ( + MultiSpectralDistributions, + SpectralDistribution, + SpectralShape, +) +from colour.continuous import MultiSignals, Signal +from colour.hints import ArrayLike, Any, Optional, Sequence, Union +from colour.utilities import is_pandas_installed -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +if is_pandas_installed(): + from pandas import DataFrame, Series +else: # pragma: no cover + from unittest import mock -__all__ = ['RGB_DisplayPrimaries'] + DataFrame = mock.MagicMock() + Series = mock.MagicMock() + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "RGB_DisplayPrimaries", +] class RGB_DisplayPrimaries(MultiSpectralDistributions): """ - Implements support for a *RGB* display (such as a *CRT* or *LCD*) + Implement support for a *RGB* display (such as a *CRT* or *LCD*) primaries multi-spectral distributions. Parameters ---------- - data : Series or Dataframe or Signal or MultiSignals or \ -MultiSpectralDistributions or array_like or dict_like, optional + data Data to be stored in the multi-spectral distributions. - domain : array_like, optional + domain Values to initialise the multiple :class:`colour.SpectralDistribution` class instances :attr:`colour.continuous.Signal.wavelengths` attribute with. If both ``data`` and ``domain`` arguments are defined, the latter will be used to initialise the - :attr:`colour.continuous.Signal.wavelengths` attribute. - labels : array_like, optional + :attr:`colour.continuous.Signal.wavelengths` property. + labels Names to use for the :class:`colour.SpectralDistribution` class instances. Other Parameters ---------------- - name : unicode, optional - Multi-spectral distributions name. - interpolator : object, optional - Interpolator class type to use as interpolating function for the - :class:`colour.SpectralDistribution` class instances. - interpolator_kwargs : dict_like, optional - Arguments to use when instantiating the interpolating function - of the :class:`colour.SpectralDistribution` class instances. - extrapolator : object, optional + extrapolator Extrapolator class type to use as extrapolating function for the :class:`colour.SpectralDistribution` class instances. - extrapolator_kwargs : dict_like, optional + extrapolator_kwargs Arguments to use when instantiating the extrapolating function of the :class:`colour.SpectralDistribution` class instances. - strict_labels : array_like, optional + interpolator + Interpolator class type to use as interpolating function for the + :class:`colour.SpectralDistribution` class instances. + interpolator_kwargs + Arguments to use when instantiating the interpolating function + of the :class:`colour.SpectralDistribution` class instances. + name + Multi-spectral distributions name. + strict_labels Multi-spectral distributions labels for figures, default to - :attr:`colour.characterisation.RGB_DisplayPrimaries.labels` attribute + :attr:`colour.colorimetry.RGB_DisplayPrimaries.labels` property value. """ - def __init__(self, data=None, domain=None, labels=None, **kwargs): - super(RGB_DisplayPrimaries, self).__init__( - data, domain, labels=('red', 'green', 'blue'), **kwargs) + def __init__( + self, + data: Optional[ + Union[ + ArrayLike, + DataFrame, + dict, + MultiSignals, + MultiSpectralDistributions, + Sequence, + Series, + Signal, + SpectralDistribution, + ] + ] = None, + domain: Optional[Union[ArrayLike, SpectralShape]] = None, + labels: Optional[Sequence] = None, + **kwargs: Any, + ): + super().__init__( + data, domain, labels=("red", "green", "blue"), **kwargs + ) diff --git a/colour/characterisation/tests/__init__.py b/colour/characterisation/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/characterisation/tests/__init__.py +++ b/colour/characterisation/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/characterisation/tests/test_aces_it.py b/colour/characterisation/tests/test_aces_it.py index df90544d93..0353371fd9 100644 --- a/colour/characterisation/tests/test_aces_it.py +++ b/colour/characterisation/tests/test_aces_it.py @@ -1,64 +1,97 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.characterisation.aces_it` module. -""" +"""Defines the unit tests for the :mod:`colour.characterisation.aces_it` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import os import unittest from colour.characterisation import ( - MSDS_ACES_RICD, MSDS_CAMERA_SENSITIVITIES, SDS_COLOURCHECKERS, - sd_to_aces_relative_exposure_values, read_training_data_rawtoaces_v1, - generate_illuminants_rawtoaces_v1, white_balance_multipliers, - best_illuminant, normalise_illuminant, training_data_sds_to_RGB, - training_data_sds_to_XYZ, optimisation_factory_rawtoaces_v1, - optimisation_factory_JzAzBz, matrix_idt) + MSDS_ACES_RICD, + MSDS_CAMERA_SENSITIVITIES, + SDS_COLOURCHECKERS, + sd_to_aces_relative_exposure_values, + read_training_data_rawtoaces_v1, + generate_illuminants_rawtoaces_v1, + white_balance_multipliers, + best_illuminant, + normalise_illuminant, + training_data_sds_to_RGB, + training_data_sds_to_XYZ, + optimisation_factory_rawtoaces_v1, + optimisation_factory_Jzazbz, + matrix_idt, + camera_RGB_to_ACES2065_1, +) from colour.characterisation.aces_it import RESOURCES_DIRECTORY_RAWTOACES -from colour.colorimetry import (MSDS_CMFS, SDS_ILLUMINANTS, SpectralShape, - sds_and_msds_to_msds, sd_constant, sd_ones) +from colour.colorimetry import ( + MSDS_CMFS, + MultiSpectralDistributions, + SDS_ILLUMINANTS, + SpectralDistribution, + SpectralShape, + reshape_msds, + sds_and_msds_to_msds, + sd_constant, + sd_ones, +) from colour.io import read_sds_from_csv_file from colour.utilities import domain_range_scale -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'MSDS_CANON_EOS_5DMARK_II', 'SD_AMPAS_ISO7589_STUDIO_TUNGSTEN', - 'TestSpectralToAcesRelativeExposureValues', - 'TestReadTrainingDataRawtoacesV1', 'TestGenerateIlluminantsRawtoacesV1', - 'TestWhiteBalanceMultipliers', 'TestBestIlluminant', - 'TestNormaliseIlluminant', 'TestTrainingDataSdsToRGB', - 'TestTrainingDataSdsToXYZ', 'TestOptimizationFactoryRawtoacesV1', - 'TestOptimizationFactoryJzAzBz', 'TestMatrixIdt' + "MSDS_CANON_EOS_5DMARK_II", + "SD_AMPAS_ISO7589_STUDIO_TUNGSTEN", + "TestSpectralToAcesRelativeExposureValues", + "TestReadTrainingDataRawtoacesV1", + "TestGenerateIlluminantsRawtoacesV1", + "TestWhiteBalanceMultipliers", + "TestBestIlluminant", + "TestNormaliseIlluminant", + "TestTrainingDataSdsToRGB", + "TestTrainingDataSdsToXYZ", + "TestOptimizationFactoryRawtoacesV1", + "TestOptimizationFactoryJzazbz", + "TestMatrixIdt", + "TestCamera_RGB_to_ACES2065_1", ] -MSDS_CANON_EOS_5DMARK_II = sds_and_msds_to_msds( +MSDS_CANON_EOS_5DMARK_II: MultiSpectralDistributions = sds_and_msds_to_msds( + list( + read_sds_from_csv_file( + os.path.join( + RESOURCES_DIRECTORY_RAWTOACES, + "CANON_EOS_5DMark_II_RGB_Sensitivities.csv", + ) + ).values() + ) +) + +SD_AMPAS_ISO7589_STUDIO_TUNGSTEN: SpectralDistribution = ( read_sds_from_csv_file( - os.path.join(RESOURCES_DIRECTORY_RAWTOACES, - 'CANON_EOS_5DMark_II_RGB_Sensitivities.csv')).values()) - -SD_AMPAS_ISO7589_STUDIO_TUNGSTEN = read_sds_from_csv_file( - os.path.join(RESOURCES_DIRECTORY_RAWTOACES, - 'AMPAS_ISO_7589_Tungsten.csv'))['iso7589'] + os.path.join( + RESOURCES_DIRECTORY_RAWTOACES, "AMPAS_ISO_7589_Tungsten.csv" + ) + )["iso7589"] +) class TestSpectralToAcesRelativeExposureValues(unittest.TestCase): """ - Defines :func:`colour.characterisation.aces_it.\ + Define :func:`colour.characterisation.aces_it.\ sd_to_aces_relative_exposure_values` definition unit tests methods. """ def test_spectral_to_aces_relative_exposure_values(self): """ - Tests :func:`colour.characterisation.aces_it. -sd_to_aces_relative_exposure_values` definition. + Test :func:`colour.characterisation.aces_it. + sd_to_aces_relative_exposure_values` definition. """ shape = MSDS_ACES_RICD.shape @@ -66,74 +99,85 @@ def test_spectral_to_aces_relative_exposure_values(self): np.testing.assert_almost_equal( sd_to_aces_relative_exposure_values(grey_reflector), np.array([0.18, 0.18, 0.18]), - decimal=7) + decimal=7, + ) perfect_reflector = sd_ones(shape) np.testing.assert_almost_equal( sd_to_aces_relative_exposure_values(perfect_reflector), np.array([0.97783784, 0.97783784, 0.97783784]), - decimal=7) + decimal=7, + ) - dark_skin = SDS_COLOURCHECKERS['ColorChecker N Ohta']['dark skin'] + dark_skin = SDS_COLOURCHECKERS["ColorChecker N Ohta"]["dark skin"] np.testing.assert_almost_equal( sd_to_aces_relative_exposure_values(dark_skin), np.array([0.11718149, 0.08663609, 0.05897268]), - decimal=7) + decimal=7, + ) - dark_skin = SDS_COLOURCHECKERS['ColorChecker N Ohta']['dark skin'] + dark_skin = SDS_COLOURCHECKERS["ColorChecker N Ohta"]["dark skin"] np.testing.assert_almost_equal( - sd_to_aces_relative_exposure_values(dark_skin, - SDS_ILLUMINANTS['A']), + sd_to_aces_relative_exposure_values( + dark_skin, SDS_ILLUMINANTS["A"] + ), np.array([0.13583991, 0.09431845, 0.05928214]), - decimal=7) + decimal=7, + ) - dark_skin = SDS_COLOURCHECKERS['ColorChecker N Ohta']['dark skin'] + dark_skin = SDS_COLOURCHECKERS["ColorChecker N Ohta"]["dark skin"] np.testing.assert_almost_equal( sd_to_aces_relative_exposure_values( - dark_skin, apply_chromatic_adaptation=True), + dark_skin, apply_chromatic_adaptation=True + ), np.array([0.11807796, 0.08690312, 0.05891252]), - decimal=7) + decimal=7, + ) - dark_skin = SDS_COLOURCHECKERS['ColorChecker N Ohta']['dark skin'] + dark_skin = SDS_COLOURCHECKERS["ColorChecker N Ohta"]["dark skin"] np.testing.assert_almost_equal( sd_to_aces_relative_exposure_values( dark_skin, apply_chromatic_adaptation=True, - chromatic_adaptation_transform='Bradford'), + chromatic_adaptation_transform="Bradford", + ), np.array([0.11805993, 0.08689013, 0.05900396]), - decimal=7) + decimal=7, + ) def test_domain_range_scale_spectral_to_aces_relative_exposure_values( - self): + self, + ): """ - Tests :func:`colour.characterisation.aces_it. -sd_to_aces_relative_exposure_values` definition domain and range scale - support. + Test :func:`colour.characterisation.aces_it. + sd_to_aces_relative_exposure_values` definition domain and range scale + support. """ shape = MSDS_ACES_RICD.shape grey_reflector = sd_constant(0.18, shape) RGB = sd_to_aces_relative_exposure_values(grey_reflector) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( sd_to_aces_relative_exposure_values(grey_reflector), RGB * factor, - decimal=7) + decimal=7, + ) class TestReadTrainingDataRawtoacesV1(unittest.TestCase): """ - Defines :func:`colour.characterisation.aces_it.\ + Define :func:`colour.characterisation.aces_it.\ read_training_data_rawtoaces_v1` definition unit tests methods. """ def test_read_training_data_rawtoaces_v1(self): """ - Tests :func:`colour.characterisation.aces_it. -read_training_data_rawtoaces_v1` definition. + Test :func:`colour.characterisation.aces_it. + read_training_data_rawtoaces_v1` definition. """ self.assertEqual(len(read_training_data_rawtoaces_v1().labels), 190) @@ -141,633 +185,759 @@ def test_read_training_data_rawtoaces_v1(self): class TestGenerateIlluminantsRawtoacesV1(unittest.TestCase): """ - Defines :func:`colour.characterisation.aces_it.\ + Define :func:`colour.characterisation.aces_it.\ generate_illuminants_rawtoaces_v1` definition unit tests methods. """ def test_generate_illuminants_rawtoaces_v1(self): """ - Tests :func:`colour.characterisation.aces_it. -generate_illuminants_rawtoaces_v1` definition. + Test :func:`colour.characterisation.aces_it. + generate_illuminants_rawtoaces_v1` definition. """ self.assertListEqual( - list(sorted(generate_illuminants_rawtoaces_v1().keys())), [ - '1000K Blackbody', '1500K Blackbody', '2000K Blackbody', - '2500K Blackbody', '3000K Blackbody', '3500K Blackbody', - 'D100', 'D105', 'D110', 'D115', 'D120', 'D125', 'D130', 'D135', - 'D140', 'D145', 'D150', 'D155', 'D160', 'D165', 'D170', 'D175', - 'D180', 'D185', 'D190', 'D195', 'D200', 'D205', 'D210', 'D215', - 'D220', 'D225', 'D230', 'D235', 'D240', 'D245', 'D250', 'D40', - 'D45', 'D50', 'D55', 'D60', 'D65', 'D70', 'D75', 'D80', 'D85', - 'D90', 'D95', 'iso7589' - ]) + list(sorted(generate_illuminants_rawtoaces_v1().keys())), + [ + "1000K Blackbody", + "1500K Blackbody", + "2000K Blackbody", + "2500K Blackbody", + "3000K Blackbody", + "3500K Blackbody", + "D100", + "D105", + "D110", + "D115", + "D120", + "D125", + "D130", + "D135", + "D140", + "D145", + "D150", + "D155", + "D160", + "D165", + "D170", + "D175", + "D180", + "D185", + "D190", + "D195", + "D200", + "D205", + "D210", + "D215", + "D220", + "D225", + "D230", + "D235", + "D240", + "D245", + "D250", + "D40", + "D45", + "D50", + "D55", + "D60", + "D65", + "D70", + "D75", + "D80", + "D85", + "D90", + "D95", + "iso7589", + ], + ) class TestWhiteBalanceMultipliers(unittest.TestCase): """ - Defines :func:`colour.characterisation.aces_it.white_balance_multipliers` + Define :func:`colour.characterisation.aces_it.white_balance_multipliers` definition unit tests methods. """ def test_white_balance_multipliers(self): """ - Tests :func:`colour.characterisation.aces_it.white_balance_multipliers` + Test :func:`colour.characterisation.aces_it.white_balance_multipliers` definition. """ np.testing.assert_almost_equal( - white_balance_multipliers(MSDS_CANON_EOS_5DMARK_II, - SDS_ILLUMINANTS['D55']), + white_balance_multipliers( + MSDS_CANON_EOS_5DMARK_II, SDS_ILLUMINANTS["D55"] + ), np.array([2.34141541, 1.00000000, 1.51633759]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( white_balance_multipliers( MSDS_CANON_EOS_5DMARK_II, - SDS_ILLUMINANTS['ISO 7589 Studio Tungsten']), + SDS_ILLUMINANTS["ISO 7589 Studio Tungsten"], + ), np.array([1.57095278, 1.00000000, 2.43560477]), - decimal=7) + decimal=7, + ) class TestBestIlluminant(unittest.TestCase): """ - Defines :func:`colour.characterisation.aces_it.best_illuminant` definition + Define :func:`colour.characterisation.aces_it.best_illuminant` definition unit tests methods. """ def test_best_illuminant(self): """ - Tests :func:`colour.characterisation.aces_it.best_illuminant` + Test :func:`colour.characterisation.aces_it.best_illuminant` definition. """ self.assertEqual( best_illuminant( white_balance_multipliers( - MSDS_CANON_EOS_5DMARK_II, - SDS_ILLUMINANTS['FL2']), MSDS_CANON_EOS_5DMARK_II, - generate_illuminants_rawtoaces_v1()).name, 'D40') + MSDS_CANON_EOS_5DMARK_II, SDS_ILLUMINANTS["FL2"] + ), + MSDS_CANON_EOS_5DMARK_II, + generate_illuminants_rawtoaces_v1(), + ).name, + "D40", + ) self.assertEqual( best_illuminant( white_balance_multipliers( - MSDS_CANON_EOS_5DMARK_II, - SDS_ILLUMINANTS['A']), MSDS_CANON_EOS_5DMARK_II, - generate_illuminants_rawtoaces_v1()).name, '3000K Blackbody') + MSDS_CANON_EOS_5DMARK_II, SDS_ILLUMINANTS["A"] + ), + MSDS_CANON_EOS_5DMARK_II, + generate_illuminants_rawtoaces_v1(), + ).name, + "3000K Blackbody", + ) class TestNormaliseIlluminant(unittest.TestCase): """ - Defines :func:`colour.characterisation.aces_it.normalise_illuminant` + Define :func:`colour.characterisation.aces_it.normalise_illuminant` definition unit tests methods. """ def test_normalise_illuminant(self): """ - Tests :func:`colour.characterisation.aces_it.normalise_illuminant` + Test :func:`colour.characterisation.aces_it.normalise_illuminant` definition. """ self.assertAlmostEqual( np.sum( - normalise_illuminant(SDS_ILLUMINANTS['D55'], - MSDS_CANON_EOS_5DMARK_II).values), + normalise_illuminant( + SDS_ILLUMINANTS["D55"], MSDS_CANON_EOS_5DMARK_II + ).values + ), 3.439037388220850, - places=7) + places=7, + ) class TestTrainingDataSdsToRGB(unittest.TestCase): """ - Defines :func:`colour.characterisation.aces_it.training_data_sds_to_RGB` + Define :func:`colour.characterisation.aces_it.training_data_sds_to_RGB` definition unit tests methods. """ def test_training_data_sds_to_RGB(self): """ - Tests :func:`colour.characterisation.aces_it.training_data_sds_to_RGB` + Test :func:`colour.characterisation.aces_it.training_data_sds_to_RGB` definition. """ + RGB, RGB_w = training_data_sds_to_RGB( + read_training_data_rawtoaces_v1(), + MSDS_CANON_EOS_5DMARK_II, + SDS_ILLUMINANTS["D55"], + ) + np.testing.assert_almost_equal( + RGB, + np.array( + [ + [42.00296381, 39.83290349, 43.28842394], + [181.25453293, 180.47486885, 180.30657630], + [1580.35041765, 1578.67251435, 1571.05703787], + [403.67553672, 403.67553672, 403.67553672], + [1193.51958332, 1194.63985124, 1183.92806238], + [862.07824054, 863.30644583, 858.29863779], + [605.42274304, 602.94953701, 596.61414309], + [395.70687930, 394.67167942, 392.97719777], + [227.27502116, 228.33554705, 227.96959477], + [130.97735082, 132.12395139, 131.97239271], + [61.79308820, 61.85572037, 62.40560537], + [592.29430914, 383.93309398, 282.70032306], + [504.67305022, 294.69245978, 193.90976423], + [640.93167741, 494.91914821, 421.68337308], + [356.53952646, 239.77610719, 181.18147755], + [179.58569818, 130.00540238, 109.23999883], + [1061.07297514, 818.29727750, 730.13362169], + [765.75936417, 522.06805938, 456.59355601], + [104.70554060, 80.35106922, 65.75667232], + [694.19925422, 161.06849749, 214.20170991], + [1054.83161580, 709.41713619, 668.10329523], + [697.35479081, 276.20032105, 275.86226833], + [183.26315174, 65.93801513, 74.60775905], + [359.74416854, 59.73576149, 89.81296522], + [1043.53760601, 405.48081521, 376.37298474], + [344.35374209, 111.26727966, 109.10587712], + [215.18064862, 87.41152853, 85.18152727], + [555.37005673, 134.76016985, 111.54658160], + [931.71846961, 210.02605133, 150.65312210], + [211.01186324, 50.73939233, 54.55750662], + [654.45781665, 132.73694874, 107.20009737], + [1193.89772859, 625.60766645, 521.51066476], + [802.65730883, 228.94887565, 178.30864097], + [149.82853589, 44.31839648, 55.29195048], + [80.88083928, 33.78936351, 41.73438243], + [579.50157840, 240.80755019, 188.50864121], + [537.09280420, 80.41714202, 48.28907694], + [777.62363031, 205.11587061, 122.43126732], + [292.65436510, 59.53457252, 44.27126512], + [511.68625012, 134.76897130, 85.73242441], + [903.64947615, 462.49015529, 350.74183199], + [852.95457070, 291.64071698, 151.51871958], + [1427.59841722, 907.54863477, 724.29520203], + [527.68979414, 169.76114596, 89.48561902], + [496.62188809, 317.11827387, 243.77642038], + [554.39017413, 284.77453644, 181.92376325], + [310.50669032, 96.25812545, 41.22765558], + [1246.49891599, 522.05121993, 238.28646123], + [240.19646249, 118.57745244, 82.68426681], + [1005.98836135, 355.93514762, 118.60457241], + [792.31376787, 369.56509398, 143.27388201], + [459.04590557, 315.46594358, 215.53901098], + [806.50918893, 352.20277469, 97.69239677], + [1574.11778922, 1078.61331515, 697.02647383], + [1015.45155837, 598.98507153, 301.94169280], + [479.68722930, 242.23619637, 72.60351059], + [1131.70538515, 628.32510627, 213.67910327], + [185.86573238, 162.55033903, 137.59385867], + [1131.77074807, 603.89218698, 153.83160203], + [638.14148862, 527.18090248, 410.12394346], + [884.58039320, 655.09236879, 329.23967927], + [1172.73094356, 840.43080883, 380.90114088], + [1490.24223350, 1111.18491878, 482.33357611], + [1054.70234779, 513.29967197, 91.55980977], + [1532.99674295, 1035.15868150, 253.21942988], + [662.35328287, 528.52354760, 326.56458987], + [1769.55456145, 1557.58571488, 1155.79098414], + [1196.62083017, 1079.28012658, 888.47017893], + [1578.73591185, 1089.40083172, 314.45691871], + [252.98204345, 206.56788008, 153.62801631], + [973.59975800, 714.51185344, 251.12884859], + [1661.01720988, 1340.46809762, 619.61710815], + [656.66179353, 566.61547800, 322.22788098], + [676.69663303, 571.86743785, 249.62031449], + [1229.28626315, 1020.14702709, 353.11090960], + [390.76190378, 324.36051944, 119.31108035], + [1524.10495708, 1366.72397704, 633.03830849], + [1264.54750712, 1149.12002542, 335.25348483], + [265.96753330, 260.89397210, 130.78590008], + [90.15969432, 90.72350914, 55.12008388], + [298.22463247, 300.48700028, 101.95760063], + [813.34391710, 820.12623357, 313.17818415], + [186.96402165, 190.38042094, 104.27515726], + [230.34939258, 235.91900919, 120.77815429], + [469.57926615, 472.51064145, 256.40912347], + [117.81249486, 129.17019984, 69.78861213], + [133.39581196, 151.50390168, 77.66255652], + [164.19259747, 172.13159331, 80.92295294], + [146.12230124, 149.32536508, 87.48300520], + [201.93215173, 208.89885695, 111.84447436], + [248.41427850, 282.34047722, 122.55482010], + [304.35509339, 377.38986207, 118.66130122], + [381.85533606, 530.40398972, 150.83506876], + [967.19810669, 1161.33086750, 663.54746741], + [613.98437237, 865.41677370, 362.92357557], + [410.21304405, 611.89683658, 284.09389273], + [279.50447144, 416.01646348, 213.18049093], + [334.48807624, 487.46571814, 235.49134434], + [664.04349337, 867.87454943, 549.71146455], + [311.66934673, 431.38058636, 256.13307806], + [110.04404638, 203.88196409, 104.63331585], + [153.35857585, 312.67834716, 149.90942505], + [273.46344514, 462.41992197, 292.50571823], + [184.77058437, 267.46361125, 193.71894670], + [75.79805899, 163.84071881, 95.67465270], + [461.73803707, 668.68797906, 484.77687282], + [523.01992144, 790.69326153, 598.73122243], + [105.89414085, 124.92341127, 113.03925656], + [279.33299507, 446.45128537, 344.73426977], + [340.57250119, 381.28610429, 353.83182947], + [141.00956904, 329.50139051, 228.90179483], + [117.29728945, 156.88993944, 139.49878229], + [565.12438106, 696.52297174, 615.88218349], + [1046.73447319, 1446.22424473, 1277.47338963], + [133.87404291, 253.25944193, 224.75872956], + [586.52626500, 1015.43013448, 885.49907251], + [927.08412116, 1197.93784752, 1140.76612264], + [81.29463446, 202.46201173, 186.35209411], + [350.90699453, 788.82959642, 669.10307704], + [278.88231719, 581.42068355, 526.82554470], + [642.66176703, 990.64038619, 907.64284280], + [689.10344984, 942.49383066, 900.33073076], + [190.62073977, 540.21088595, 523.62573562], + [322.35685764, 676.02683754, 692.94583013], + [896.29532467, 1289.90474463, 1311.34615018], + [204.06785020, 321.83261403, 337.01923114], + [237.10512554, 549.97044011, 646.06486244], + [907.26703197, 1252.44260107, 1309.50173432], + [504.74103065, 728.27088424, 782.27808125], + [470.91049729, 912.49116456, 1059.41083523], + [231.75497961, 539.14727494, 732.41647792], + [624.91135978, 943.51709467, 1086.48492282], + [104.84186738, 398.05825469, 663.96030581], + [100.47632953, 226.41423139, 323.51675153], + [998.19560093, 1168.81108673, 1283.07267859], + [350.74519746, 457.74100518, 552.52270183], + [223.19531677, 560.14850077, 855.05346039], + [66.92044931, 128.18947830, 205.30719728], + [280.63458798, 518.51069955, 784.38948897], + [1071.24122457, 1267.16339790, 1467.81704311], + [271.47257445, 553.57609491, 914.33723598], + [211.86582477, 295.18643027, 418.51776463], + [153.86457460, 342.06625645, 649.82579665], + [179.59188635, 265.25370235, 413.68135787], + [529.77485058, 737.79030218, 1046.29865466], + [208.71936449, 421.30392624, 796.71281168], + [685.50294808, 879.76243717, 1195.00892794], + [85.02189613, 113.33360860, 171.03209018], + [72.06980264, 139.42600347, 315.97906141], + [349.57868286, 426.82308690, 556.49647978], + [726.50329821, 882.48411184, 1163.20130103], + [102.62158777, 177.73895468, 467.26740089], + [208.63097281, 322.84137064, 639.30554347], + [377.19498209, 456.13180268, 706.44272480], + [149.91131672, 218.16462984, 455.15510078], + [556.80606655, 673.96774240, 1020.98785748], + [172.19546054, 181.38617476, 478.69666973], + [494.98572332, 534.88874559, 773.75255591], + [1166.31475206, 1207.81829513, 1411.04368728], + [324.81131421, 298.91188334, 521.96994638], + [731.58631467, 725.95113189, 1192.71141630], + [376.70584074, 352.06184423, 572.37854429], + [421.32413767, 465.07677606, 910.85999527], + [155.65680826, 145.82096629, 282.56390371], + [982.43736509, 991.65710582, 1312.39630323], + [41.37244888, 33.41882583, 59.48460827], + [282.61535563, 188.37255834, 441.62967707], + [182.28936533, 136.29152918, 248.30801310], + [398.28853814, 281.28601665, 641.78038278], + [494.34030557, 393.91395210, 664.96627121], + [579.86630787, 449.57878986, 836.64303806], + [281.30892711, 142.60663373, 309.93723963], + [439.97606151, 345.13329865, 425.68615785], + [887.17712876, 583.53811414, 886.88440975], + [841.97939219, 617.28846790, 810.67002861], + [1280.60242984, 1139.62066080, 1255.46929276], + [336.77846782, 246.82877629, 324.48823631], + [1070.92080733, 527.41599474, 913.93600561], + [676.57753460, 329.48235976, 509.56020035], + [1353.12934453, 1048.28092139, 1227.42851889], + [248.56120754, 78.30056642, 137.39216268], + [675.76876164, 381.60749713, 545.08703142], + [1008.57884369, 704.64042514, 836.94311729], + [1207.19931876, 527.74482440, 737.30284625], + [1157.60714894, 736.24734736, 846.01278626], + [861.62204402, 714.70913295, 747.29294390], + [255.83324360, 94.08214754, 147.60127564], + [1522.93390177, 1017.14491217, 1073.23488749], + [460.59077351, 93.73852735, 210.75844436], + [909.87331348, 498.83253656, 750.09672276], + ] + ), + decimal=7, + ) + np.testing.assert_almost_equal( - training_data_sds_to_RGB(read_training_data_rawtoaces_v1(), - MSDS_CANON_EOS_5DMARK_II, - SDS_ILLUMINANTS['D55']), - np.array([ - [42.00296381, 39.83290349, 43.28842394], - [181.25453293, 180.47486885, 180.30657630], - [1580.35041765, 1578.67251435, 1571.05703787], - [403.67553672, 403.67553672, 403.67553672], - [1193.51958332, 1194.63985124, 1183.92806238], - [862.07824054, 863.30644583, 858.29863779], - [605.42274304, 602.94953701, 596.61414309], - [395.70687930, 394.67167942, 392.97719777], - [227.27502116, 228.33554705, 227.96959477], - [130.97735082, 132.12395139, 131.97239271], - [61.79308820, 61.85572037, 62.40560537], - [592.29430914, 383.93309398, 282.70032306], - [504.67305022, 294.69245978, 193.90976423], - [640.93167741, 494.91914821, 421.68337308], - [356.53952646, 239.77610719, 181.18147755], - [179.58569818, 130.00540238, 109.23999883], - [1061.07297514, 818.29727750, 730.13362169], - [765.75936417, 522.06805938, 456.59355601], - [104.70554060, 80.35106922, 65.75667232], - [694.19925422, 161.06849749, 214.20170991], - [1054.83161580, 709.41713619, 668.10329523], - [697.35479081, 276.20032105, 275.86226833], - [183.26315174, 65.93801513, 74.60775905], - [359.74416854, 59.73576149, 89.81296522], - [1043.53760601, 405.48081521, 376.37298474], - [344.35374209, 111.26727966, 109.10587712], - [215.18064862, 87.41152853, 85.18152727], - [555.37005673, 134.76016985, 111.54658160], - [931.71846961, 210.02605133, 150.65312210], - [211.01186324, 50.73939233, 54.55750662], - [654.45781665, 132.73694874, 107.20009737], - [1193.89772859, 625.60766645, 521.51066476], - [802.65730883, 228.94887565, 178.30864097], - [149.82853589, 44.31839648, 55.29195048], - [80.88083928, 33.78936351, 41.73438243], - [579.50157840, 240.80755019, 188.50864121], - [537.09280420, 80.41714202, 48.28907694], - [777.62363031, 205.11587061, 122.43126732], - [292.65436510, 59.53457252, 44.27126512], - [511.68625012, 134.76897130, 85.73242441], - [903.64947615, 462.49015529, 350.74183199], - [852.95457070, 291.64071698, 151.51871958], - [1427.59841722, 907.54863477, 724.29520203], - [527.68979414, 169.76114596, 89.48561902], - [496.62188809, 317.11827387, 243.77642038], - [554.39017413, 284.77453644, 181.92376325], - [310.50669032, 96.25812545, 41.22765558], - [1246.49891599, 522.05121993, 238.28646123], - [240.19646249, 118.57745244, 82.68426681], - [1005.98836135, 355.93514762, 118.60457241], - [792.31376787, 369.56509398, 143.27388201], - [459.04590557, 315.46594358, 215.53901098], - [806.50918893, 352.20277469, 97.69239677], - [1574.11778922, 1078.61331515, 697.02647383], - [1015.45155837, 598.98507153, 301.94169280], - [479.68722930, 242.23619637, 72.60351059], - [1131.70538515, 628.32510627, 213.67910327], - [185.86573238, 162.55033903, 137.59385867], - [1131.77074807, 603.89218698, 153.83160203], - [638.14148862, 527.18090248, 410.12394346], - [884.58039320, 655.09236879, 329.23967927], - [1172.73094356, 840.43080883, 380.90114088], - [1490.24223350, 1111.18491878, 482.33357611], - [1054.70234779, 513.29967197, 91.55980977], - [1532.99674295, 1035.15868150, 253.21942988], - [662.35328287, 528.52354760, 326.56458987], - [1769.55456145, 1557.58571488, 1155.79098414], - [1196.62083017, 1079.28012658, 888.47017893], - [1578.73591185, 1089.40083172, 314.45691871], - [252.98204345, 206.56788008, 153.62801631], - [973.59975800, 714.51185344, 251.12884859], - [1661.01720988, 1340.46809762, 619.61710815], - [656.66179353, 566.61547800, 322.22788098], - [676.69663303, 571.86743785, 249.62031449], - [1229.28626315, 1020.14702709, 353.11090960], - [390.76190378, 324.36051944, 119.31108035], - [1524.10495708, 1366.72397704, 633.03830849], - [1264.54750712, 1149.12002542, 335.25348483], - [265.96753330, 260.89397210, 130.78590008], - [90.15969432, 90.72350914, 55.12008388], - [298.22463247, 300.48700028, 101.95760063], - [813.34391710, 820.12623357, 313.17818415], - [186.96402165, 190.38042094, 104.27515726], - [230.34939258, 235.91900919, 120.77815429], - [469.57926615, 472.51064145, 256.40912347], - [117.81249486, 129.17019984, 69.78861213], - [133.39581196, 151.50390168, 77.66255652], - [164.19259747, 172.13159331, 80.92295294], - [146.12230124, 149.32536508, 87.48300520], - [201.93215173, 208.89885695, 111.84447436], - [248.41427850, 282.34047722, 122.55482010], - [304.35509339, 377.38986207, 118.66130122], - [381.85533606, 530.40398972, 150.83506876], - [967.19810669, 1161.33086750, 663.54746741], - [613.98437237, 865.41677370, 362.92357557], - [410.21304405, 611.89683658, 284.09389273], - [279.50447144, 416.01646348, 213.18049093], - [334.48807624, 487.46571814, 235.49134434], - [664.04349337, 867.87454943, 549.71146455], - [311.66934673, 431.38058636, 256.13307806], - [110.04404638, 203.88196409, 104.63331585], - [153.35857585, 312.67834716, 149.90942505], - [273.46344514, 462.41992197, 292.50571823], - [184.77058437, 267.46361125, 193.71894670], - [75.79805899, 163.84071881, 95.67465270], - [461.73803707, 668.68797906, 484.77687282], - [523.01992144, 790.69326153, 598.73122243], - [105.89414085, 124.92341127, 113.03925656], - [279.33299507, 446.45128537, 344.73426977], - [340.57250119, 381.28610429, 353.83182947], - [141.00956904, 329.50139051, 228.90179483], - [117.29728945, 156.88993944, 139.49878229], - [565.12438106, 696.52297174, 615.88218349], - [1046.73447319, 1446.22424473, 1277.47338963], - [133.87404291, 253.25944193, 224.75872956], - [586.52626500, 1015.43013448, 885.49907251], - [927.08412116, 1197.93784752, 1140.76612264], - [81.29463446, 202.46201173, 186.35209411], - [350.90699453, 788.82959642, 669.10307704], - [278.88231719, 581.42068355, 526.82554470], - [642.66176703, 990.64038619, 907.64284280], - [689.10344984, 942.49383066, 900.33073076], - [190.62073977, 540.21088595, 523.62573562], - [322.35685764, 676.02683754, 692.94583013], - [896.29532467, 1289.90474463, 1311.34615018], - [204.06785020, 321.83261403, 337.01923114], - [237.10512554, 549.97044011, 646.06486244], - [907.26703197, 1252.44260107, 1309.50173432], - [504.74103065, 728.27088424, 782.27808125], - [470.91049729, 912.49116456, 1059.41083523], - [231.75497961, 539.14727494, 732.41647792], - [624.91135978, 943.51709467, 1086.48492282], - [104.84186738, 398.05825469, 663.96030581], - [100.47632953, 226.41423139, 323.51675153], - [998.19560093, 1168.81108673, 1283.07267859], - [350.74519746, 457.74100518, 552.52270183], - [223.19531677, 560.14850077, 855.05346039], - [66.92044931, 128.18947830, 205.30719728], - [280.63458798, 518.51069955, 784.38948897], - [1071.24122457, 1267.16339790, 1467.81704311], - [271.47257445, 553.57609491, 914.33723598], - [211.86582477, 295.18643027, 418.51776463], - [153.86457460, 342.06625645, 649.82579665], - [179.59188635, 265.25370235, 413.68135787], - [529.77485058, 737.79030218, 1046.29865466], - [208.71936449, 421.30392624, 796.71281168], - [685.50294808, 879.76243717, 1195.00892794], - [85.02189613, 113.33360860, 171.03209018], - [72.06980264, 139.42600347, 315.97906141], - [349.57868286, 426.82308690, 556.49647978], - [726.50329821, 882.48411184, 1163.20130103], - [102.62158777, 177.73895468, 467.26740089], - [208.63097281, 322.84137064, 639.30554347], - [377.19498209, 456.13180268, 706.44272480], - [149.91131672, 218.16462984, 455.15510078], - [556.80606655, 673.96774240, 1020.98785748], - [172.19546054, 181.38617476, 478.69666973], - [494.98572332, 534.88874559, 773.75255591], - [1166.31475206, 1207.81829513, 1411.04368728], - [324.81131421, 298.91188334, 521.96994638], - [731.58631467, 725.95113189, 1192.71141630], - [376.70584074, 352.06184423, 572.37854429], - [421.32413767, 465.07677606, 910.85999527], - [155.65680826, 145.82096629, 282.56390371], - [982.43736509, 991.65710582, 1312.39630323], - [41.37244888, 33.41882583, 59.48460827], - [282.61535563, 188.37255834, 441.62967707], - [182.28936533, 136.29152918, 248.30801310], - [398.28853814, 281.28601665, 641.78038278], - [494.34030557, 393.91395210, 664.96627121], - [579.86630787, 449.57878986, 836.64303806], - [281.30892711, 142.60663373, 309.93723963], - [439.97606151, 345.13329865, 425.68615785], - [887.17712876, 583.53811414, 886.88440975], - [841.97939219, 617.28846790, 810.67002861], - [1280.60242984, 1139.62066080, 1255.46929276], - [336.77846782, 246.82877629, 324.48823631], - [1070.92080733, 527.41599474, 913.93600561], - [676.57753460, 329.48235976, 509.56020035], - [1353.12934453, 1048.28092139, 1227.42851889], - [248.56120754, 78.30056642, 137.39216268], - [675.76876164, 381.60749713, 545.08703142], - [1008.57884369, 704.64042514, 836.94311729], - [1207.19931876, 527.74482440, 737.30284625], - [1157.60714894, 736.24734736, 846.01278626], - [861.62204402, 714.70913295, 747.29294390], - [255.83324360, 94.08214754, 147.60127564], - [1522.93390177, 1017.14491217, 1073.23488749], - [460.59077351, 93.73852735, 210.75844436], - [909.87331348, 498.83253656, 750.09672276], - ]), - decimal=7) + RGB_w, np.array([2.34141541, 1.00000000, 1.51633759]), decimal=7 + ) training_data = sds_and_msds_to_msds( - SDS_COLOURCHECKERS['BabelColor Average'].values()) + SDS_COLOURCHECKERS["BabelColor Average"].values() + ) + RGB, RGB_w = training_data_sds_to_RGB( + training_data, MSDS_CANON_EOS_5DMARK_II, SDS_ILLUMINANTS["D55"] + ) np.testing.assert_almost_equal( - training_data_sds_to_RGB(training_data, MSDS_CANON_EOS_5DMARK_II, - SDS_ILLUMINANTS['D55']), - np.array([ - [263.80361607, 170.29412869, 132.71463416], - [884.07936328, 628.44083126, 520.43504675], - [324.17856150, 443.95092266, 606.43750890], - [243.82059773, 253.22111395, 144.98600653], - [481.54199203, 527.96925768, 764.50624747], - [628.07015143, 979.73104655, 896.85237907], - [927.63600544, 391.11468312, 150.73047156], - [203.13259862, 317.65395368, 639.54581080], - [686.28955864, 260.78688114, 254.89963998], - [174.05857536, 132.16684952, 230.54054095], - [806.50094411, 817.35481419, 312.91902292], - [1111.20280010, 608.82554576, 194.31984092], - [94.99792206, 185.04148229, 456.53592437], - [340.60457483, 498.62910631, 254.08356415], - [531.53679194, 136.11844274, 109.19876416], - [1387.37113491, 952.84382040, 286.23152122], - [681.97933172, 326.66634506, 526.23078660], - [244.90739217, 554.88866566, 741.21522946], - [1841.80020583, 1834.49277300, 1784.07500285], - [1179.76201558, 1189.84138939, 1182.25520674], - [720.27089899, 726.91855632, 724.84766858], - [382.16849234, 387.41521539, 386.87510339], - [178.43859184, 181.76108810, 182.71062184], - [64.77754952, 64.80020759, 65.45515287], - ]), - decimal=7) + RGB, + np.array( + [ + [263.80361607, 170.29412869, 132.71463416], + [884.07936328, 628.44083126, 520.43504675], + [324.17856150, 443.95092266, 606.43750890], + [243.82059773, 253.22111395, 144.98600653], + [481.54199203, 527.96925768, 764.50624747], + [628.07015143, 979.73104655, 896.85237907], + [927.63600544, 391.11468312, 150.73047156], + [203.13259862, 317.65395368, 639.54581080], + [686.28955864, 260.78688114, 254.89963998], + [174.05857536, 132.16684952, 230.54054095], + [806.50094411, 817.35481419, 312.91902292], + [1111.20280010, 608.82554576, 194.31984092], + [94.99792206, 185.04148229, 456.53592437], + [340.60457483, 498.62910631, 254.08356415], + [531.53679194, 136.11844274, 109.19876416], + [1387.37113491, 952.84382040, 286.23152122], + [681.97933172, 326.66634506, 526.23078660], + [244.90739217, 554.88866566, 741.21522946], + [1841.80020583, 1834.49277300, 1784.07500285], + [1179.76201558, 1189.84138939, 1182.25520674], + [720.27089899, 726.91855632, 724.84766858], + [382.16849234, 387.41521539, 386.87510339], + [178.43859184, 181.76108810, 182.71062184], + [64.77754952, 64.80020759, 65.45515287], + ] + ), + decimal=7, + ) + + np.testing.assert_almost_equal( + RGB_w, np.array([2.34141541, 1.00000000, 1.51633759]), decimal=7 + ) class TestTrainingDataSdsToXYZ(unittest.TestCase): """ - Defines :func:`colour.characterisation.aces_it.training_data_sds_to_XYZ` + Define :func:`colour.characterisation.aces_it.training_data_sds_to_XYZ` definition unit tests methods. """ def test_training_data_sds_to_XYZ(self): """ - Tests :func:`colour.characterisation.aces_it.training_data_sds_to_XYZ` + Test :func:`colour.characterisation.aces_it.training_data_sds_to_XYZ` definition. """ np.testing.assert_almost_equal( training_data_sds_to_XYZ( read_training_data_rawtoaces_v1(), - MSDS_CMFS['CIE 1931 2 Degree Standard Observer'], - SDS_ILLUMINANTS['D55']), - np.array([ - [0.01743541, 0.01795040, 0.01961110], - [0.08556071, 0.08957352, 0.09017032], - [0.74558770, 0.78175495, 0.78343383], - [0.19005289, 0.19950000, 0.20126062], - [0.56263167, 0.59145443, 0.58944868], - [0.40708229, 0.42774653, 0.42813199], - [0.28533739, 0.29945717, 0.29732644], - [0.18670375, 0.19575576, 0.19612855], - [0.10734487, 0.11290543, 0.11381239], - [0.06188310, 0.06524694, 0.06594260], - [0.02905436, 0.03045954, 0.03111642], - [0.25031624, 0.22471846, 0.12599982], - [0.20848487, 0.18072652, 0.08216289], - [0.28173081, 0.26937432, 0.19943363], - [0.15129458, 0.13765872, 0.08086671], - [0.07854243, 0.07274480, 0.05123870], - [0.46574583, 0.43948749, 0.34501135], - [0.33111608, 0.29368033, 0.21379720], - [0.04596029, 0.04443836, 0.03115443], - [0.28422646, 0.15495892, 0.11586479], - [0.47490187, 0.41497780, 0.33505853], - [0.29452546, 0.20003225, 0.13705453], - [0.06905269, 0.04421818, 0.03449201], - [0.13040440, 0.06239791, 0.04175606], - [0.43838730, 0.29962261, 0.18439668], - [0.13390118, 0.08356608, 0.04956679], - [0.08356733, 0.05794634, 0.03910007], - [0.21637988, 0.12469189, 0.04842559], - [0.37899204, 0.22130821, 0.07365608], - [0.07733610, 0.04256869, 0.02300063], - [0.25696432, 0.14119282, 0.04740500], - [0.51960474, 0.41409496, 0.25643556], - [0.32241564, 0.19954021, 0.08051276], - [0.05811798, 0.03389661, 0.02553745], - [0.03192572, 0.02139972, 0.01894908], - [0.24605476, 0.17854962, 0.09147038], - [0.20624731, 0.10555152, 0.01675508], - [0.31255107, 0.19334840, 0.05143990], - [0.11006219, 0.06057155, 0.01700794], - [0.20509764, 0.12555310, 0.03594860], - [0.38058683, 0.30396093, 0.16256996], - [0.34354473, 0.23964048, 0.06111316], - [0.62251344, 0.54770879, 0.34634977], - [0.21294652, 0.14470338, 0.03492000], - [0.22064317, 0.19656587, 0.11907643], - [0.23955073, 0.19768225, 0.08595970], - [0.12377361, 0.08353105, 0.01434151], - [0.52378659, 0.40757502, 0.10242337], - [0.09732322, 0.07735501, 0.03254246], - [0.41081884, 0.30127969, 0.04240016], - [0.32946008, 0.27129095, 0.05232655], - [0.19870991, 0.18701769, 0.09764509], - [0.31867743, 0.25717029, 0.02158054], - [0.67745549, 0.64283785, 0.31268426], - [0.43182429, 0.39425828, 0.13198410], - [0.19075096, 0.16573196, 0.01845293], - [0.47578930, 0.43714747, 0.07974541], - [0.08420865, 0.08615579, 0.06605406], - [0.47306132, 0.43488423, 0.05262924], - [0.28242654, 0.28638349, 0.19186089], - [0.37367384, 0.38524079, 0.13498637], - [0.49536547, 0.51027091, 0.15645211], - [0.63680942, 0.67272132, 0.19642820], - [0.43790684, 0.39093965, 0.02518505], - [0.63216527, 0.66425603, 0.07124985], - [0.28682848, 0.29807036, 0.14308787], - [0.78666095, 0.83181391, 0.53110094], - [0.54475049, 0.57280425, 0.43240766], - [0.65555915, 0.68992930, 0.10030198], - [0.10560623, 0.10992647, 0.06863885], - [0.40588908, 0.43345904, 0.08589490], - [0.69824760, 0.76446843, 0.23843395], - [0.27951451, 0.30869595, 0.13310650], - [0.28351930, 0.32278417, 0.09130925], - [0.51144946, 0.58985649, 0.11409286], - [0.16769668, 0.19357639, 0.04824163], - [0.64027510, 0.74864980, 0.24145602], - [0.51533750, 0.64418491, 0.09390029], - [0.10903312, 0.13420204, 0.04403235], - [0.03916991, 0.04755109, 0.02410291], - [0.12726285, 0.16825903, 0.03705646], - [0.34079923, 0.44119883, 0.10621489], - [0.08299513, 0.10226271, 0.04607974], - [0.10117617, 0.12690940, 0.05211600], - [0.20673305, 0.25456362, 0.11244267], - [0.05040081, 0.06702198, 0.02944861], - [0.05809758, 0.07896803, 0.03312583], - [0.07202711, 0.09383365, 0.03453490], - [0.06392748, 0.07896740, 0.03860393], - [0.08851258, 0.11174080, 0.04873213], - [0.09821259, 0.13743849, 0.03901353], - [0.12253000, 0.18989034, 0.03327101], - [0.15082798, 0.25948217, 0.03805919], - [0.41476613, 0.56455709, 0.26988900], - [0.25043710, 0.40869656, 0.12211755], - [0.17536685, 0.28765326, 0.10166502], - [0.12038544, 0.19242328, 0.07754636], - [0.14661345, 0.23524743, 0.09334793], - [0.29469553, 0.41056592, 0.23093160], - [0.13015693, 0.19492122, 0.09333495], - [0.04081181, 0.08280292, 0.03122401], - [0.06569736, 0.13553353, 0.05266408], - [0.12177383, 0.20160583, 0.11621774], - [0.08354206, 0.11970984, 0.08207175], - [0.02834645, 0.06259404, 0.03135058], - [0.20884161, 0.29927365, 0.20553553], - [0.23180119, 0.33870071, 0.24267407], - [0.04413521, 0.05398934, 0.04862030], - [0.13068910, 0.19470885, 0.15073584], - [0.16108644, 0.18484544, 0.17474649], - [0.06206737, 0.12873462, 0.09368693], - [0.05126858, 0.06722639, 0.05961970], - [0.25534374, 0.31335090, 0.27780291], - [0.48369629, 0.63319069, 0.57347864], - [0.06066266, 0.09712274, 0.09253437], - [0.27940216, 0.41909220, 0.39351159], - [0.44664100, 0.54665344, 0.55342931], - [0.03590889, 0.06959304, 0.07535965], - [0.16621092, 0.30339106, 0.29722885], - [0.12909138, 0.22008859, 0.22690521], - [0.31015553, 0.42498221, 0.42044232], - [0.33970423, 0.42779997, 0.43883150], - [0.10000582, 0.19440825, 0.23393750], - [0.16694758, 0.26056864, 0.32541934], - [0.43598087, 0.55484571, 0.63089871], - [0.10305166, 0.13633951, 0.16650820], - [0.12725465, 0.19404057, 0.30068226], - [0.44450660, 0.54666776, 0.64220554], - [0.25312549, 0.31346831, 0.38485942], - [0.24557618, 0.34698805, 0.51328941], - [0.13585660, 0.18761687, 0.36302217], - [0.32288492, 0.39652004, 0.54579104], - [0.08400465, 0.11889755, 0.34519851], - [0.06038029, 0.07936884, 0.16393180], - [0.47840043, 0.53070661, 0.64043584], - [0.16727376, 0.19048161, 0.27055547], - [0.14740952, 0.19227205, 0.44545300], - [0.03953792, 0.04540593, 0.10766386], - [0.16200092, 0.18995251, 0.41003367], - [0.53147895, 0.57554326, 0.74787983], - [0.17107460, 0.19285623, 0.48157477], - [0.11394187, 0.12139868, 0.21928748], - [0.10838799, 0.11193347, 0.34884682], - [0.10390937, 0.10854555, 0.22459293], - [0.28493924, 0.30349174, 0.54832107], - [0.13572090, 0.13988801, 0.43412229], - [0.36141619, 0.37929776, 0.62919317], - [0.04527113, 0.04612919, 0.09028801], - [0.05164102, 0.04505136, 0.17732932], - [0.18148861, 0.19085005, 0.29528314], - [0.37792382, 0.39238764, 0.61357669], - [0.08148672, 0.06054619, 0.27321036], - [0.13431208, 0.12118937, 0.35762939], - [0.19932157, 0.19328547, 0.37878896], - [0.09456787, 0.08094285, 0.25785832], - [0.29868476, 0.28967149, 0.54786550], - [0.09582629, 0.06156148, 0.27163852], - [0.25053785, 0.23630807, 0.40751054], - [0.56821117, 0.57452018, 0.72419232], - [0.16116009, 0.13379410, 0.28760107], - [0.37816205, 0.32564214, 0.64945876], - [0.19440630, 0.16599850, 0.31684298], - [0.24229817, 0.19698372, 0.51538353], - [0.08104904, 0.06295569, 0.15738669], - [0.48808364, 0.46372832, 0.69336648], - [0.01983575, 0.01538929, 0.03252398], - [0.13468770, 0.08473328, 0.25136965], - [0.08762890, 0.06560340, 0.13804375], - [0.20192043, 0.12939477, 0.36343630], - [0.24231283, 0.19018859, 0.36604686], - [0.28784724, 0.21105155, 0.46114703], - [0.12549222, 0.07471177, 0.17126268], - [0.20910983, 0.18235419, 0.22475458], - [0.43032307, 0.32727171, 0.49574549], - [0.39105442, 0.32475758, 0.42885925], - [0.60567491, 0.57928897, 0.64030251], - [0.15645417, 0.12986348, 0.17171885], - [0.50025055, 0.32646202, 0.51899239], - [0.29822363, 0.19839451, 0.27397060], - [0.63136923, 0.55375993, 0.63816664], - [0.10261977, 0.05754107, 0.07473368], - [0.30325538, 0.21976283, 0.29171854], - [0.46794841, 0.39368920, 0.44286306], - [0.54326558, 0.36319029, 0.41127862], - [0.52355493, 0.42261205, 0.43529051], - [0.39852212, 0.37568122, 0.37825751], - [0.10892106, 0.06698290, 0.07939788], - [0.68780223, 0.58022018, 0.54422258], - [0.18984448, 0.09051898, 0.12104133], - [0.41991006, 0.29457037, 0.40780639], - ]), - decimal=7) + MSDS_CMFS["CIE 1931 2 Degree Standard Observer"], + SDS_ILLUMINANTS["D55"], + ), + np.array( + [ + [0.01743541, 0.01795040, 0.01961110], + [0.08556071, 0.08957352, 0.09017032], + [0.74558770, 0.78175495, 0.78343383], + [0.19005289, 0.19950000, 0.20126062], + [0.56263167, 0.59145443, 0.58944868], + [0.40708229, 0.42774653, 0.42813199], + [0.28533739, 0.29945717, 0.29732644], + [0.18670375, 0.19575576, 0.19612855], + [0.10734487, 0.11290543, 0.11381239], + [0.06188310, 0.06524694, 0.06594260], + [0.02905436, 0.03045954, 0.03111642], + [0.25031624, 0.22471846, 0.12599982], + [0.20848487, 0.18072652, 0.08216289], + [0.28173081, 0.26937432, 0.19943363], + [0.15129458, 0.13765872, 0.08086671], + [0.07854243, 0.07274480, 0.05123870], + [0.46574583, 0.43948749, 0.34501135], + [0.33111608, 0.29368033, 0.21379720], + [0.04596029, 0.04443836, 0.03115443], + [0.28422646, 0.15495892, 0.11586479], + [0.47490187, 0.41497780, 0.33505853], + [0.29452546, 0.20003225, 0.13705453], + [0.06905269, 0.04421818, 0.03449201], + [0.13040440, 0.06239791, 0.04175606], + [0.43838730, 0.29962261, 0.18439668], + [0.13390118, 0.08356608, 0.04956679], + [0.08356733, 0.05794634, 0.03910007], + [0.21637988, 0.12469189, 0.04842559], + [0.37899204, 0.22130821, 0.07365608], + [0.07733610, 0.04256869, 0.02300063], + [0.25696432, 0.14119282, 0.04740500], + [0.51960474, 0.41409496, 0.25643556], + [0.32241564, 0.19954021, 0.08051276], + [0.05811798, 0.03389661, 0.02553745], + [0.03192572, 0.02139972, 0.01894908], + [0.24605476, 0.17854962, 0.09147038], + [0.20624731, 0.10555152, 0.01675508], + [0.31255107, 0.19334840, 0.05143990], + [0.11006219, 0.06057155, 0.01700794], + [0.20509764, 0.12555310, 0.03594860], + [0.38058683, 0.30396093, 0.16256996], + [0.34354473, 0.23964048, 0.06111316], + [0.62251344, 0.54770879, 0.34634977], + [0.21294652, 0.14470338, 0.03492000], + [0.22064317, 0.19656587, 0.11907643], + [0.23955073, 0.19768225, 0.08595970], + [0.12377361, 0.08353105, 0.01434151], + [0.52378659, 0.40757502, 0.10242337], + [0.09732322, 0.07735501, 0.03254246], + [0.41081884, 0.30127969, 0.04240016], + [0.32946008, 0.27129095, 0.05232655], + [0.19870991, 0.18701769, 0.09764509], + [0.31867743, 0.25717029, 0.02158054], + [0.67745549, 0.64283785, 0.31268426], + [0.43182429, 0.39425828, 0.13198410], + [0.19075096, 0.16573196, 0.01845293], + [0.47578930, 0.43714747, 0.07974541], + [0.08420865, 0.08615579, 0.06605406], + [0.47306132, 0.43488423, 0.05262924], + [0.28242654, 0.28638349, 0.19186089], + [0.37367384, 0.38524079, 0.13498637], + [0.49536547, 0.51027091, 0.15645211], + [0.63680942, 0.67272132, 0.19642820], + [0.43790684, 0.39093965, 0.02518505], + [0.63216527, 0.66425603, 0.07124985], + [0.28682848, 0.29807036, 0.14308787], + [0.78666095, 0.83181391, 0.53110094], + [0.54475049, 0.57280425, 0.43240766], + [0.65555915, 0.68992930, 0.10030198], + [0.10560623, 0.10992647, 0.06863885], + [0.40588908, 0.43345904, 0.08589490], + [0.69824760, 0.76446843, 0.23843395], + [0.27951451, 0.30869595, 0.13310650], + [0.28351930, 0.32278417, 0.09130925], + [0.51144946, 0.58985649, 0.11409286], + [0.16769668, 0.19357639, 0.04824163], + [0.64027510, 0.74864980, 0.24145602], + [0.51533750, 0.64418491, 0.09390029], + [0.10903312, 0.13420204, 0.04403235], + [0.03916991, 0.04755109, 0.02410291], + [0.12726285, 0.16825903, 0.03705646], + [0.34079923, 0.44119883, 0.10621489], + [0.08299513, 0.10226271, 0.04607974], + [0.10117617, 0.12690940, 0.05211600], + [0.20673305, 0.25456362, 0.11244267], + [0.05040081, 0.06702198, 0.02944861], + [0.05809758, 0.07896803, 0.03312583], + [0.07202711, 0.09383365, 0.03453490], + [0.06392748, 0.07896740, 0.03860393], + [0.08851258, 0.11174080, 0.04873213], + [0.09821259, 0.13743849, 0.03901353], + [0.12253000, 0.18989034, 0.03327101], + [0.15082798, 0.25948217, 0.03805919], + [0.41476613, 0.56455709, 0.26988900], + [0.25043710, 0.40869656, 0.12211755], + [0.17536685, 0.28765326, 0.10166502], + [0.12038544, 0.19242328, 0.07754636], + [0.14661345, 0.23524743, 0.09334793], + [0.29469553, 0.41056592, 0.23093160], + [0.13015693, 0.19492122, 0.09333495], + [0.04081181, 0.08280292, 0.03122401], + [0.06569736, 0.13553353, 0.05266408], + [0.12177383, 0.20160583, 0.11621774], + [0.08354206, 0.11970984, 0.08207175], + [0.02834645, 0.06259404, 0.03135058], + [0.20884161, 0.29927365, 0.20553553], + [0.23180119, 0.33870071, 0.24267407], + [0.04413521, 0.05398934, 0.04862030], + [0.13068910, 0.19470885, 0.15073584], + [0.16108644, 0.18484544, 0.17474649], + [0.06206737, 0.12873462, 0.09368693], + [0.05126858, 0.06722639, 0.05961970], + [0.25534374, 0.31335090, 0.27780291], + [0.48369629, 0.63319069, 0.57347864], + [0.06066266, 0.09712274, 0.09253437], + [0.27940216, 0.41909220, 0.39351159], + [0.44664100, 0.54665344, 0.55342931], + [0.03590889, 0.06959304, 0.07535965], + [0.16621092, 0.30339106, 0.29722885], + [0.12909138, 0.22008859, 0.22690521], + [0.31015553, 0.42498221, 0.42044232], + [0.33970423, 0.42779997, 0.43883150], + [0.10000582, 0.19440825, 0.23393750], + [0.16694758, 0.26056864, 0.32541934], + [0.43598087, 0.55484571, 0.63089871], + [0.10305166, 0.13633951, 0.16650820], + [0.12725465, 0.19404057, 0.30068226], + [0.44450660, 0.54666776, 0.64220554], + [0.25312549, 0.31346831, 0.38485942], + [0.24557618, 0.34698805, 0.51328941], + [0.13585660, 0.18761687, 0.36302217], + [0.32288492, 0.39652004, 0.54579104], + [0.08400465, 0.11889755, 0.34519851], + [0.06038029, 0.07936884, 0.16393180], + [0.47840043, 0.53070661, 0.64043584], + [0.16727376, 0.19048161, 0.27055547], + [0.14740952, 0.19227205, 0.44545300], + [0.03953792, 0.04540593, 0.10766386], + [0.16200092, 0.18995251, 0.41003367], + [0.53147895, 0.57554326, 0.74787983], + [0.17107460, 0.19285623, 0.48157477], + [0.11394187, 0.12139868, 0.21928748], + [0.10838799, 0.11193347, 0.34884682], + [0.10390937, 0.10854555, 0.22459293], + [0.28493924, 0.30349174, 0.54832107], + [0.13572090, 0.13988801, 0.43412229], + [0.36141619, 0.37929776, 0.62919317], + [0.04527113, 0.04612919, 0.09028801], + [0.05164102, 0.04505136, 0.17732932], + [0.18148861, 0.19085005, 0.29528314], + [0.37792382, 0.39238764, 0.61357669], + [0.08148672, 0.06054619, 0.27321036], + [0.13431208, 0.12118937, 0.35762939], + [0.19932157, 0.19328547, 0.37878896], + [0.09456787, 0.08094285, 0.25785832], + [0.29868476, 0.28967149, 0.54786550], + [0.09582629, 0.06156148, 0.27163852], + [0.25053785, 0.23630807, 0.40751054], + [0.56821117, 0.57452018, 0.72419232], + [0.16116009, 0.13379410, 0.28760107], + [0.37816205, 0.32564214, 0.64945876], + [0.19440630, 0.16599850, 0.31684298], + [0.24229817, 0.19698372, 0.51538353], + [0.08104904, 0.06295569, 0.15738669], + [0.48808364, 0.46372832, 0.69336648], + [0.01983575, 0.01538929, 0.03252398], + [0.13468770, 0.08473328, 0.25136965], + [0.08762890, 0.06560340, 0.13804375], + [0.20192043, 0.12939477, 0.36343630], + [0.24231283, 0.19018859, 0.36604686], + [0.28784724, 0.21105155, 0.46114703], + [0.12549222, 0.07471177, 0.17126268], + [0.20910983, 0.18235419, 0.22475458], + [0.43032307, 0.32727171, 0.49574549], + [0.39105442, 0.32475758, 0.42885925], + [0.60567491, 0.57928897, 0.64030251], + [0.15645417, 0.12986348, 0.17171885], + [0.50025055, 0.32646202, 0.51899239], + [0.29822363, 0.19839451, 0.27397060], + [0.63136923, 0.55375993, 0.63816664], + [0.10261977, 0.05754107, 0.07473368], + [0.30325538, 0.21976283, 0.29171854], + [0.46794841, 0.39368920, 0.44286306], + [0.54326558, 0.36319029, 0.41127862], + [0.52355493, 0.42261205, 0.43529051], + [0.39852212, 0.37568122, 0.37825751], + [0.10892106, 0.06698290, 0.07939788], + [0.68780223, 0.58022018, 0.54422258], + [0.18984448, 0.09051898, 0.12104133], + [0.41991006, 0.29457037, 0.40780639], + ] + ), + decimal=7, + ) training_data = sds_and_msds_to_msds( - SDS_COLOURCHECKERS['BabelColor Average'].values()) + SDS_COLOURCHECKERS["BabelColor Average"].values() + ) + + np.testing.assert_almost_equal( + training_data_sds_to_XYZ( + training_data, + MSDS_CMFS["CIE 1931 2 Degree Standard Observer"], + SDS_ILLUMINANTS["D55"], + ), + np.array( + [ + [0.11386016, 0.10184316, 0.06318332], + [0.38043230, 0.34842093, 0.23582246], + [0.17359019, 0.18707491, 0.31848244], + [0.10647823, 0.13300376, 0.06486355], + [0.24658643, 0.23417740, 0.40546447], + [0.30550003, 0.42171110, 0.41928361], + [0.38409200, 0.30325611, 0.05955461], + [0.13149767, 0.11720378, 0.35673016], + [0.28717811, 0.19215580, 0.12514286], + [0.08401031, 0.06423349, 0.12782115], + [0.33990604, 0.44124555, 0.10834694], + [0.46443889, 0.42686462, 0.07340585], + [0.07650085, 0.06051409, 0.26167301], + [0.14598990, 0.23185071, 0.09380297], + [0.20642710, 0.12162691, 0.04673088], + [0.57371755, 0.59896814, 0.08930486], + [0.30208819, 0.19714705, 0.28492050], + [0.14184323, 0.19554336, 0.36653731], + [0.86547610, 0.91241348, 0.88583082], + [0.55802432, 0.58852191, 0.59042758], + [0.34102067, 0.35951875, 0.36251375], + [0.18104441, 0.19123509, 0.19353380], + [0.08461047, 0.08944605, 0.09150081], + [0.03058273, 0.03200953, 0.03277947], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( training_data_sds_to_XYZ( training_data, - MSDS_CMFS['CIE 1931 2 Degree Standard Observer'], - SDS_ILLUMINANTS['D55']), - np.array([ - [0.11386016, 0.10184316, 0.06318332], - [0.38043230, 0.34842093, 0.23582246], - [0.17359019, 0.18707491, 0.31848244], - [0.10647823, 0.13300376, 0.06486355], - [0.24658643, 0.23417740, 0.40546447], - [0.30550003, 0.42171110, 0.41928361], - [0.38409200, 0.30325611, 0.05955461], - [0.13149767, 0.11720378, 0.35673016], - [0.28717811, 0.19215580, 0.12514286], - [0.08401031, 0.06423349, 0.12782115], - [0.33990604, 0.44124555, 0.10834694], - [0.46443889, 0.42686462, 0.07340585], - [0.07650085, 0.06051409, 0.26167301], - [0.14598990, 0.23185071, 0.09380297], - [0.20642710, 0.12162691, 0.04673088], - [0.57371755, 0.59896814, 0.08930486], - [0.30208819, 0.19714705, 0.28492050], - [0.14184323, 0.19554336, 0.36653731], - [0.86547610, 0.91241348, 0.88583082], - [0.55802432, 0.58852191, 0.59042758], - [0.34102067, 0.35951875, 0.36251375], - [0.18104441, 0.19123509, 0.19353380], - [0.08461047, 0.08944605, 0.09150081], - [0.03058273, 0.03200953, 0.03277947], - ]), - decimal=7) + MSDS_CMFS["CIE 1931 2 Degree Standard Observer"], + SDS_ILLUMINANTS["D55"], + chromatic_adaptation_transform="Bradford", + ), + np.array( + [ + [0.11386557, 0.10185906, 0.06306965], + [0.38044920, 0.34846911, 0.23548776], + [0.17349711, 0.18690409, 0.31901794], + [0.10656174, 0.13314825, 0.06450454], + [0.24642109, 0.23388536, 0.40625776], + [0.30564803, 0.42194543, 0.41894818], + [0.38414010, 0.30337780, 0.05881558], + [0.13128440, 0.11682332, 0.35780551], + [0.28707604, 0.19200780, 0.12518610], + [0.08392779, 0.06409174, 0.12816180], + [0.34028525, 0.44190577, 0.10665985], + [0.46462806, 0.42722924, 0.07207641], + [0.07631823, 0.06018898, 0.26258457], + [0.14620929, 0.23222248, 0.09296807], + [0.20635082, 0.12152088, 0.04669974], + [0.57410962, 0.59968182, 0.08713069], + [0.30185180, 0.19675858, 0.28565273], + [0.14177898, 0.19541060, 0.36711242], + [0.86550834, 0.91247072, 0.88567193], + [0.55803077, 0.58853268, 0.59040518], + [0.34102300, 0.35952246, 0.36250826], + [0.18104563, 0.19123690, 0.19353274], + [0.08461039, 0.08944568, 0.09150425], + [0.03058222, 0.03200864, 0.03278183], + ] + ), + decimal=7, + ) class TestOptimizationFactoryRawtoacesV1(unittest.TestCase): """ - Defines :func:`colour.characterisation.aces_it.\ + Define :func:`colour.characterisation.aces_it.\ optimisation_factory_rawtoaces_v1` definition unit tests methods. """ def test_optimisation_factory_rawtoaces_v1(self): """ - Tests :func:`colour.characterisation.aces_it.\ + Test :func:`colour.characterisation.aces_it.\ optimisation_factory_rawtoaces_v1` definition. """ self.assertEqual(len(optimisation_factory_rawtoaces_v1()), 2) -class TestOptimizationFactoryJzAzBz(unittest.TestCase): +class TestOptimizationFactoryJzazbz(unittest.TestCase): """ - Defines :func:`colour.characterisation.aces_it.\ -optimisation_factory_JzAzBz` definition unit tests methods. + Define :func:`colour.characterisation.aces_it.\ +optimisation_factory_Jzazbz` definition unit tests methods. """ - def test_optimisation_factory_JzAzBz(self): + def test_optimisation_factory_Jzazbz(self): """ - Tests :func:`colour.characterisation.aces_it.\ -optimisation_factory_JzAzBz` definition. + Test :func:`colour.characterisation.aces_it.\ +optimisation_factory_Jzazbz` definition. """ - self.assertEqual(len(optimisation_factory_JzAzBz()), 2) + self.assertEqual(len(optimisation_factory_Jzazbz()), 2) class TestMatrixIdt(unittest.TestCase): """ - Defines :func:`colour.characterisation.aces_it.matrix_idt` + Define :func:`colour.characterisation.aces_it.matrix_idt` definition unit tests methods. """ def test_matrix_idt(self): """ - Tests :func:`colour.characterisation.aces_it.matrix_idt` + Test :func:`colour.characterisation.aces_it.matrix_idt` definition. """ @@ -778,14 +948,17 @@ def test_matrix_idt(self): # 0.056527 1.122997 -0.179524 # 0.023683 -0.202547 1.178864 np.testing.assert_allclose( - matrix_idt(MSDS_CANON_EOS_5DMARK_II, SDS_ILLUMINANTS['D55']), - np.array([ - [0.84993207, -0.01605594, 0.15143504], - [0.05090392, 1.12559930, -0.18498249], - [0.02006825, -0.19445149, 1.16206549], - ]), + matrix_idt(MSDS_CANON_EOS_5DMARK_II, SDS_ILLUMINANTS["D55"])[0], + np.array( + [ + [0.84993207, -0.01605594, 0.15143504], + [0.05090392, 1.12559930, -0.18498249], + [0.02006825, -0.19445149, 1.16206549], + ] + ), rtol=0.0001, - atol=0.0001) + atol=0.0001, + ) # The *RAW to ACES* v1 matrix for the same camera and optimized by # `Ceres Solver `__ is as follows: @@ -794,59 +967,175 @@ def test_matrix_idt(self): # 0.021805 1.066614 -0.088418 # -0.019718 -0.206664 1.226381 np.testing.assert_allclose( - matrix_idt(MSDS_CANON_EOS_5DMARK_II, - SD_AMPAS_ISO7589_STUDIO_TUNGSTEN), - np.array([ - [0.85895300, -0.04381920, 0.15978620], - [0.01024800, 1.08825364, -0.11392229], - [-0.02327674, -0.18044292, 1.15903609], - ]), + matrix_idt( + MSDS_CANON_EOS_5DMARK_II, SD_AMPAS_ISO7589_STUDIO_TUNGSTEN + )[0], + np.array( + [ + [0.85895300, -0.04381920, 0.15978620], + [0.01024800, 1.08825364, -0.11392229], + [-0.02327674, -0.18044292, 1.15903609], + ] + ), rtol=0.0001, - atol=0.0001) - + atol=0.0001, + ) + + M, RGB_w = matrix_idt( + MSDS_CANON_EOS_5DMARK_II, + SDS_ILLUMINANTS["D55"], + optimisation_factory=optimisation_factory_Jzazbz, + ) np.testing.assert_allclose( - matrix_idt( - MSDS_CANON_EOS_5DMARK_II, - SDS_ILLUMINANTS['D55'], - optimisation_factory=optimisation_factory_JzAzBz), - np.array([ - [0.84841492, -0.01569765, 0.15799332], - [0.05333075, 1.11428542, -0.17523500], - [0.02262287, -0.22527728, 1.19646895], - ]), + M, + np.array( + [ + [0.84841492, -0.01569765, 0.15799332], + [0.05333075, 1.11428542, -0.17523500], + [0.02262287, -0.22527728, 1.19646895], + ] + ), rtol=0.0001, - atol=0.0001) - + atol=0.0001, + ) np.testing.assert_allclose( - matrix_idt( - MSDS_CANON_EOS_5DMARK_II, - SDS_ILLUMINANTS['D55'], - optimisation_kwargs={'method': 'Nelder-Mead'}), - np.array([ - [0.71327381, 0.19213397, 0.11115511], - [-0.05788252, 1.31165598, -0.21730625], - [-0.05913103, -0.02787107, 1.10737947], - ]), + RGB_w, + np.array([2.34141541, 1.00000000, 1.51633759]), + rtol=0.0001, + atol=0.0001, + ) + + M, RGB_w = matrix_idt( + MSDS_CANON_EOS_5DMARK_II, + SDS_ILLUMINANTS["D55"], + optimisation_kwargs={"method": "Nelder-Mead"}, + ) + np.testing.assert_allclose( + M, + np.array( + [ + [0.71327381, 0.19213397, 0.11115511], + [-0.05788252, 1.31165598, -0.21730625], + [-0.05913103, -0.02787107, 1.10737947], + ] + ), rtol=0.0001, - atol=0.0001) + atol=0.0001, + ) + np.testing.assert_allclose( + RGB_w, + np.array([2.34141541, 1.00000000, 1.51633759]), + rtol=0.0001, + atol=0.0001, + ) training_data = sds_and_msds_to_msds( - SDS_COLOURCHECKERS['BabelColor Average'].values()) + SDS_COLOURCHECKERS["BabelColor Average"].values() + ) + # pylint: disable=E1102 np.testing.assert_allclose( matrix_idt( - MSDS_CAMERA_SENSITIVITIES['Nikon 5100 (NPL)'].copy().align( - SpectralShape(400, 700, 10)), + reshape_msds( + MSDS_CAMERA_SENSITIVITIES["Nikon 5100 (NPL)"], + SpectralShape(400, 700, 10), + ), SD_AMPAS_ISO7589_STUDIO_TUNGSTEN, - training_data=training_data), - np.array([ - [0.74041064, 0.10951105, 0.11963256], - [-0.00467360, 1.09238438, -0.11398966], - [0.06728533, -0.29530438, 1.18589793], - ]), + training_data=training_data, + )[0], + np.array( + [ + [0.74041064, 0.10951105, 0.11963256], + [-0.00467360, 1.09238438, -0.11398966], + [0.06728533, -0.29530438, 1.18589793], + ] + ), + rtol=0.0001, + atol=0.0001, + ) + + np.testing.assert_allclose( + matrix_idt( + MSDS_CANON_EOS_5DMARK_II, + SDS_ILLUMINANTS["D55"], + chromatic_adaptation_transform="Bradford", + )[0], + np.array( + [ + [0.85020607, -0.01371074, 0.14907913], + [0.05074081, 1.12898863, -0.18800656], + [0.02095822, -0.20110079, 1.16769711], + ] + ), rtol=0.0001, - atol=0.0001) + atol=0.0001, + ) + + _M, RGB_w, XYZ, RGB = matrix_idt( + MSDS_CANON_EOS_5DMARK_II, + SDS_ILLUMINANTS["D55"], + additional_data=True, + ) + + np.testing.assert_almost_equal( + RGB_w, np.array([2.34141541, 1.00000000, 1.51633759]) + ) + + np.testing.assert_almost_equal( + XYZ[:5, ...], + np.array( + [ + [0.01743160, 0.01794927, 0.01960625], + [0.08556139, 0.08957352, 0.09017387], + [0.74560311, 0.78175547, 0.78350814], + [0.19005289, 0.19950000, 0.20126062], + [0.56264334, 0.59145486, 0.58950505], + ] + ), + ) + + np.testing.assert_almost_equal( + RGB[:5, ...], + np.array( + [ + [0.02075823, 0.01968577, 0.02139352], + [0.08957758, 0.08919227, 0.08910910], + [0.78102307, 0.78019384, 0.77643020], + [0.19950000, 0.19950000, 0.19950000], + [0.58984787, 0.59040152, 0.58510766], + ] + ), + ) + + +class TestCamera_RGB_to_ACES2065_1(unittest.TestCase): + """ + Define :func:`colour.characterisation.aces_it.\ +camera_RGB_to_ACES2065_1` definition unit tests methods. + """ + + def test_camera_RGB_to_ACES2065_1(self): + """ + Test :func:`colour.characterisation.aces_it.\ +camera_RGB_to_ACES2065_1` definition. + """ + + B, b = matrix_idt(MSDS_CANON_EOS_5DMARK_II, SDS_ILLUMINANTS["D55"]) + np.testing.assert_almost_equal( + camera_RGB_to_ACES2065_1(np.array([0.1, 0.2, 0.3]), B, b), + np.array([0.26468115, 0.15288980, 0.49443355]), + ) + + np.testing.assert_almost_equal( + camera_RGB_to_ACES2065_1(np.array([1.5, 1.5, 1.5]), B, b), + np.array([3.30542136, 1.44643555, 2.42192985]), + ) + + np.testing.assert_almost_equal( + camera_RGB_to_ACES2065_1(np.array([1.0, 1.0, 1.0]), B, b, True), + np.array([2.20361424, 0.96429036, 1.61461990]), + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/characterisation/tests/test_correction.py b/colour/characterisation/tests/test_correction.py index e564e9eb9c..7b32ff60d0 100644 --- a/colour/characterisation/tests/test_correction.py +++ b/colour/characterisation/tests/test_correction.py @@ -1,104 +1,119 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.characterisation.correction` module. +Defines the unit tests for the :mod:`colour.characterisation.correction` +module. """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +import platform import unittest from itertools import permutations from numpy.linalg import LinAlgError from colour.characterisation.correction import ( - matrix_augmented_Cheung2004, polynomial_expansion_Finlayson2015, - polynomial_expansion_Vandermonde, matrix_colour_correction_Cheung2004, + matrix_augmented_Cheung2004, + polynomial_expansion_Finlayson2015, + polynomial_expansion_Vandermonde, + matrix_colour_correction_Cheung2004, matrix_colour_correction_Finlayson2015, - matrix_colour_correction_Vandermonde, colour_correction_Cheung2004, - colour_correction_Finlayson2015, colour_correction_Vandermonde) + matrix_colour_correction_Vandermonde, + colour_correction_Cheung2004, + colour_correction_Finlayson2015, + colour_correction_Vandermonde, +) +from colour.hints import NDArray from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'MATRIX_TEST', 'MATRIX_REFERENCE', 'TestMatrixAugmentedCheung2004', - 'TestPolynomialExpansionFinlayson2015', - 'TestPolynomialExpansionVandermonde', - 'TestMatrixColourCorrectionCheung2004', - 'TestMatrixColourCorrectionFinlayson2015', - 'TestMatrixColourCorrectionVandermonde', 'TestColourCorrectionCheung2004', - 'TestColourCorrectionFinlayson2015', 'TestColourCorrectionVandermonde' + "MATRIX_TEST", + "MATRIX_REFERENCE", + "TestMatrixAugmentedCheung2004", + "TestPolynomialExpansionFinlayson2015", + "TestPolynomialExpansionVandermonde", + "TestMatrixColourCorrectionCheung2004", + "TestMatrixColourCorrectionFinlayson2015", + "TestMatrixColourCorrectionVandermonde", + "TestColourCorrectionCheung2004", + "TestColourCorrectionFinlayson2015", + "TestColourCorrectionVandermonde", ] -MATRIX_TEST = np.array([ - [0.17224810, 0.09170660, 0.06416938], - [0.49189645, 0.27802050, 0.21923399], - [0.10999751, 0.18658946, 0.29938611], - [0.11666120, 0.14327905, 0.05713804], - [0.18988879, 0.18227649, 0.36056247], - [0.12501329, 0.42223442, 0.37027445], - [0.64785606, 0.22396782, 0.03365194], - [0.06761093, 0.11076896, 0.39779139], - [0.49101797, 0.09448929, 0.11623839], - [0.11622386, 0.04425753, 0.14469986], - [0.36867946, 0.44545230, 0.06028681], - [0.61632937, 0.32323906, 0.02437089], - [0.03016472, 0.06153243, 0.29014596], - [0.11103655, 0.30553067, 0.08149137], - [0.41162190, 0.05816656, 0.04845934], - [0.73339206, 0.53075188, 0.02475212], - [0.47347718, 0.08834792, 0.30310315], - [0.00000000, 0.25187016, 0.35062450], - [0.76809639, 0.78486240, 0.77808297], - [0.53822392, 0.54307997, 0.54710883], - [0.35458526, 0.35318419, 0.35524431], - [0.17976704, 0.18000531, 0.17991488], - [0.09351417, 0.09510603, 0.09675027], - [0.03405071, 0.03295077, 0.03702047], -]) - -MATRIX_REFERENCE = np.array([ - [0.15579559, 0.09715755, 0.07514556], - [0.39113140, 0.25943419, 0.21266708], - [0.12824821, 0.18463570, 0.31508023], - [0.12028974, 0.13455659, 0.07408400], - [0.19368988, 0.21158946, 0.37955964], - [0.19957424, 0.36085439, 0.40678123], - [0.48896605, 0.20691688, 0.05816533], - [0.09775522, 0.16710693, 0.47147724], - [0.39358649, 0.12233400, 0.10526425], - [0.10780332, 0.07258529, 0.16151473], - [0.27502671, 0.34705454, 0.09728099], - [0.43980441, 0.26880559, 0.05430533], - [0.05887212, 0.11126272, 0.38552469], - [0.12705825, 0.25787860, 0.13566464], - [0.35612929, 0.07933258, 0.05118732], - [0.48131976, 0.42082843, 0.07120612], - [0.34665585, 0.15170714, 0.24969804], - [0.08261116, 0.24588716, 0.48707733], - [0.66054904, 0.65941137, 0.66376412], - [0.48051509, 0.47870296, 0.48230082], - [0.33045354, 0.32904184, 0.33228886], - [0.18001305, 0.17978567, 0.18004416], - [0.10283975, 0.10424680, 0.10384975], - [0.04742204, 0.04772203, 0.04914226], -]) +MATRIX_TEST: NDArray = np.array( + [ + [0.17224810, 0.09170660, 0.06416938], + [0.49189645, 0.27802050, 0.21923399], + [0.10999751, 0.18658946, 0.29938611], + [0.11666120, 0.14327905, 0.05713804], + [0.18988879, 0.18227649, 0.36056247], + [0.12501329, 0.42223442, 0.37027445], + [0.64785606, 0.22396782, 0.03365194], + [0.06761093, 0.11076896, 0.39779139], + [0.49101797, 0.09448929, 0.11623839], + [0.11622386, 0.04425753, 0.14469986], + [0.36867946, 0.44545230, 0.06028681], + [0.61632937, 0.32323906, 0.02437089], + [0.03016472, 0.06153243, 0.29014596], + [0.11103655, 0.30553067, 0.08149137], + [0.41162190, 0.05816656, 0.04845934], + [0.73339206, 0.53075188, 0.02475212], + [0.47347718, 0.08834792, 0.30310315], + [0.00000000, 0.25187016, 0.35062450], + [0.76809639, 0.78486240, 0.77808297], + [0.53822392, 0.54307997, 0.54710883], + [0.35458526, 0.35318419, 0.35524431], + [0.17976704, 0.18000531, 0.17991488], + [0.09351417, 0.09510603, 0.09675027], + [0.03405071, 0.03295077, 0.03702047], + ] +) + +MATRIX_REFERENCE: NDArray = np.array( + [ + [0.15579559, 0.09715755, 0.07514556], + [0.39113140, 0.25943419, 0.21266708], + [0.12824821, 0.18463570, 0.31508023], + [0.12028974, 0.13455659, 0.07408400], + [0.19368988, 0.21158946, 0.37955964], + [0.19957424, 0.36085439, 0.40678123], + [0.48896605, 0.20691688, 0.05816533], + [0.09775522, 0.16710693, 0.47147724], + [0.39358649, 0.12233400, 0.10526425], + [0.10780332, 0.07258529, 0.16151473], + [0.27502671, 0.34705454, 0.09728099], + [0.43980441, 0.26880559, 0.05430533], + [0.05887212, 0.11126272, 0.38552469], + [0.12705825, 0.25787860, 0.13566464], + [0.35612929, 0.07933258, 0.05118732], + [0.48131976, 0.42082843, 0.07120612], + [0.34665585, 0.15170714, 0.24969804], + [0.08261116, 0.24588716, 0.48707733], + [0.66054904, 0.65941137, 0.66376412], + [0.48051509, 0.47870296, 0.48230082], + [0.33045354, 0.32904184, 0.33228886], + [0.18001305, 0.17978567, 0.18004416], + [0.10283975, 0.10424680, 0.10384975], + [0.04742204, 0.04772203, 0.04914226], + ] +) class TestMatrixAugmentedCheung2004(unittest.TestCase): """ - Defines :func:`colour.characterisation.correction.\ + Define :func:`colour.characterisation.correction.\ matrix_augmented_Cheung2004` definition unit tests methods. """ def test_matrix_augmented_Cheung2004(self): """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ matrix_augmented_Cheung2004` definition. """ @@ -107,82 +122,220 @@ def test_matrix_augmented_Cheung2004(self): polynomials = [ np.array([0.17224810, 0.09170660, 0.06416938]), np.array( - [0.17224810, 0.09170660, 0.06416938, 0.00101364, 1.00000000]), - np.array([ - 0.17224810, 0.09170660, 0.06416938, 0.01579629, 0.01105305, - 0.00588476, 1.00000000 - ]), - np.array([ - 0.17224810, 0.09170660, 0.06416938, 0.01579629, 0.01105305, - 0.00588476, 0.00101364, 1.00000000 - ]), - np.array([ - 0.17224810, 0.09170660, 0.06416938, 0.01579629, 0.01105305, - 0.00588476, 0.02966941, 0.00841010, 0.00411771, 1.00000000 - ]), - np.array([ - 0.17224810, 0.09170660, 0.06416938, 0.01579629, 0.01105305, - 0.00588476, 0.02966941, 0.00841010, 0.00411771, 0.00101364, - 1.00000000 - ]), - np.array([ - 0.17224810, 0.09170660, 0.06416938, 0.01579629, 0.01105305, - 0.00588476, 0.02966941, 0.00841010, 0.00411771, 0.00101364, - 0.00511050, 0.00077126, 0.00026423, 1.00000000 - ]), - np.array([ - 0.17224810, 0.09170660, 0.06416938, 0.01579629, 0.01105305, - 0.00588476, 0.02966941, 0.00841010, 0.00411771, 0.00101364, - 0.00272088, 0.00053967, 0.00070927, 0.00511050, 0.00077126, - 0.00026423 - ]), - np.array([ - 0.17224810, 0.09170660, 0.06416938, 0.01579629, 0.01105305, - 0.00588476, 0.02966941, 0.00841010, 0.00411771, 0.00101364, - 0.00272088, 0.00053967, 0.00070927, 0.00511050, 0.00077126, - 0.00026423, 1.00000000 - ]), - np.array([ - 0.17224810, 0.09170660, 0.06416938, 0.01579629, 0.01105305, - 0.00588476, 0.02966941, 0.00841010, 0.00411771, 0.00101364, - 0.00272088, 0.00053967, 0.00070927, 0.00190387, 0.00144862, - 0.00037762, 0.00511050, 0.00077126, 0.00026423 - ]), - np.array([ - 0.17224810, 0.09170660, 0.06416938, 0.01579629, 0.01105305, - 0.00588476, 0.02966941, 0.00841010, 0.00411771, 0.00101364, - 0.00272088, 0.00053967, 0.00070927, 0.00190387, 0.00144862, - 0.00037762, 0.00511050, 0.00077126, 0.00026423, 1.00000000 - ]), - np.array([ - 0.17224810, 0.09170660, 0.06416938, 0.01579629, 0.01105305, - 0.00588476, 0.02966941, 0.00841010, 0.00411771, 0.00101364, - 0.00272088, 0.00053967, 0.00070927, 0.00190387, 0.00144862, - 0.00037762, 0.00511050, 0.00077126, 0.00026423, 0.00017460, - 0.00009296, 0.00006504 - ]), + [0.17224810, 0.09170660, 0.06416938, 0.00101364, 1.00000000] + ), + np.array( + [ + 0.17224810, + 0.09170660, + 0.06416938, + 0.01579629, + 0.01105305, + 0.00588476, + 1.00000000, + ] + ), + np.array( + [ + 0.17224810, + 0.09170660, + 0.06416938, + 0.01579629, + 0.01105305, + 0.00588476, + 0.00101364, + 1.00000000, + ] + ), + np.array( + [ + 0.17224810, + 0.09170660, + 0.06416938, + 0.01579629, + 0.01105305, + 0.00588476, + 0.02966941, + 0.00841010, + 0.00411771, + 1.00000000, + ] + ), + np.array( + [ + 0.17224810, + 0.09170660, + 0.06416938, + 0.01579629, + 0.01105305, + 0.00588476, + 0.02966941, + 0.00841010, + 0.00411771, + 0.00101364, + 1.00000000, + ] + ), + np.array( + [ + 0.17224810, + 0.09170660, + 0.06416938, + 0.01579629, + 0.01105305, + 0.00588476, + 0.02966941, + 0.00841010, + 0.00411771, + 0.00101364, + 0.00511050, + 0.00077126, + 0.00026423, + 1.00000000, + ] + ), + np.array( + [ + 0.17224810, + 0.09170660, + 0.06416938, + 0.01579629, + 0.01105305, + 0.00588476, + 0.02966941, + 0.00841010, + 0.00411771, + 0.00101364, + 0.00272088, + 0.00053967, + 0.00070927, + 0.00511050, + 0.00077126, + 0.00026423, + ] + ), + np.array( + [ + 0.17224810, + 0.09170660, + 0.06416938, + 0.01579629, + 0.01105305, + 0.00588476, + 0.02966941, + 0.00841010, + 0.00411771, + 0.00101364, + 0.00272088, + 0.00053967, + 0.00070927, + 0.00511050, + 0.00077126, + 0.00026423, + 1.00000000, + ] + ), + np.array( + [ + 0.17224810, + 0.09170660, + 0.06416938, + 0.01579629, + 0.01105305, + 0.00588476, + 0.02966941, + 0.00841010, + 0.00411771, + 0.00101364, + 0.00272088, + 0.00053967, + 0.00070927, + 0.00190387, + 0.00144862, + 0.00037762, + 0.00511050, + 0.00077126, + 0.00026423, + ] + ), + np.array( + [ + 0.17224810, + 0.09170660, + 0.06416938, + 0.01579629, + 0.01105305, + 0.00588476, + 0.02966941, + 0.00841010, + 0.00411771, + 0.00101364, + 0.00272088, + 0.00053967, + 0.00070927, + 0.00190387, + 0.00144862, + 0.00037762, + 0.00511050, + 0.00077126, + 0.00026423, + 1.00000000, + ] + ), + np.array( + [ + 0.17224810, + 0.09170660, + 0.06416938, + 0.01579629, + 0.01105305, + 0.00588476, + 0.02966941, + 0.00841010, + 0.00411771, + 0.00101364, + 0.00272088, + 0.00053967, + 0.00070927, + 0.00190387, + 0.00144862, + 0.00037762, + 0.00511050, + 0.00077126, + 0.00026423, + 0.00017460, + 0.00009296, + 0.00006504, + ] + ), ] - for i, terms in enumerate([3, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, - 22]): + for i, terms in enumerate( + [3, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22] + ): np.testing.assert_almost_equal( matrix_augmented_Cheung2004(RGB, terms), polynomials[i], - decimal=7) + decimal=7, + ) def test_raise_exception_matrix_augmented_Cheung2004(self): """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ matrix_augmented_Cheung2004` definition raised exception. """ - self.assertRaises(ValueError, matrix_augmented_Cheung2004, - np.array([0.17224810, 0.09170660, 0.06416938]), 4) + self.assertRaises( + ValueError, + matrix_augmented_Cheung2004, + np.array([0.17224810, 0.09170660, 0.06416938]), + 4, + ) @ignore_numpy_errors def test_nan_matrix_augmented_Cheung2004(self): """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ matrix_augmented_Cheung2004` definition nan support. """ @@ -194,13 +347,13 @@ def test_nan_matrix_augmented_Cheung2004(self): class TestPolynomialExpansionFinlayson2015(unittest.TestCase): """ - Defines :func:`colour.characterisation.correction.\ + Define :func:`colour.characterisation.correction.\ polynomial_expansion_Finlayson2015` definition unit tests methods. """ def test_polynomial_expansion_Finlayson2015(self): """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ polynomial_expansion_Finlayson2015` definition. """ @@ -209,48 +362,140 @@ def test_polynomial_expansion_Finlayson2015(self): polynomials = [ [ np.array([0.17224810, 0.09170660, 0.06416938]), - np.array([0.17224810, 0.09170660, 0.06416938]) + np.array([0.17224810, 0.09170660, 0.06416938]), ], [ - np.array([ - 0.17224810, 0.09170660, 0.06416938, 0.02966941, 0.00841010, - 0.00411771, 0.01579629, 0.00588476, 0.01105305 - ]), - np.array([ - 0.17224810, 0.09170660, 0.06416938, 0.12568328, 0.07671216, - 0.10513350 - ]) + np.array( + [ + 0.17224810, + 0.09170660, + 0.06416938, + 0.02966941, + 0.00841010, + 0.00411771, + 0.01579629, + 0.00588476, + 0.01105305, + ] + ), + np.array( + [ + 0.17224810, + 0.09170660, + 0.06416938, + 0.12568328, + 0.07671216, + 0.10513350, + ] + ), ], [ - np.array([ - 0.17224810, 0.09170660, 0.06416938, 0.02966941, 0.00841010, - 0.00411771, 0.01579629, 0.00588476, 0.01105305, 0.00511050, - 0.00077126, 0.00026423, 0.00144862, 0.00037762, 0.00070927, - 0.00272088, 0.00053967, 0.00190387, 0.00101364 - ]), - np.array([ - 0.17224810, 0.09170660, 0.06416938, 0.12568328, 0.07671216, - 0.10513350, 0.11314930, 0.07228010, 0.08918053, 0.13960570, - 0.08141598, 0.12394021, 0.10045255 - ]) + np.array( + [ + 0.17224810, + 0.09170660, + 0.06416938, + 0.02966941, + 0.00841010, + 0.00411771, + 0.01579629, + 0.00588476, + 0.01105305, + 0.00511050, + 0.00077126, + 0.00026423, + 0.00144862, + 0.00037762, + 0.00070927, + 0.00272088, + 0.00053967, + 0.00190387, + 0.00101364, + ] + ), + np.array( + [ + 0.17224810, + 0.09170660, + 0.06416938, + 0.12568328, + 0.07671216, + 0.10513350, + 0.11314930, + 0.07228010, + 0.08918053, + 0.13960570, + 0.08141598, + 0.12394021, + 0.10045255, + ] + ), ], [ - np.array([ - 0.17224810, 0.09170660, 0.06416938, 0.02966941, 0.00841010, - 0.00411771, 0.01579629, 0.00588476, 0.01105305, 0.00511050, - 0.00077126, 0.00026423, 0.00144862, 0.00037762, 0.00070927, - 0.00272088, 0.00053967, 0.00190387, 0.00101364, 0.00088027, - 0.00007073, 0.00001696, 0.00046867, 0.00032794, 0.00013285, - 0.00004949, 0.00004551, 0.00002423, 0.00024952, 0.00003463, - 0.00012217, 0.00017460, 0.00009296, 0.00006504 - ]), - np.array([ - 0.17224810, 0.09170660, 0.06416938, 0.12568328, 0.07671216, - 0.10513350, 0.11314930, 0.07228010, 0.08918053, 0.13960570, - 0.08141598, 0.12394021, 0.10045255, 0.14713499, 0.13456986, - 0.10735915, 0.08387498, 0.08213618, 0.07016104, 0.11495009, - 0.09819082, 0.08980545 - ]) + np.array( + [ + 0.17224810, + 0.09170660, + 0.06416938, + 0.02966941, + 0.00841010, + 0.00411771, + 0.01579629, + 0.00588476, + 0.01105305, + 0.00511050, + 0.00077126, + 0.00026423, + 0.00144862, + 0.00037762, + 0.00070927, + 0.00272088, + 0.00053967, + 0.00190387, + 0.00101364, + 0.00088027, + 0.00007073, + 0.00001696, + 0.00046867, + 0.00032794, + 0.00013285, + 0.00004949, + 0.00004551, + 0.00002423, + 0.00024952, + 0.00003463, + 0.00012217, + 0.00017460, + 0.00009296, + 0.00006504, + ] + ), + np.array( + [ + 0.17224810, + 0.09170660, + 0.06416938, + 0.12568328, + 0.07671216, + 0.10513350, + 0.11314930, + 0.07228010, + 0.08918053, + 0.13960570, + 0.08141598, + 0.12394021, + 0.10045255, + 0.14713499, + 0.13456986, + 0.10735915, + 0.08387498, + 0.08213618, + 0.07016104, + 0.11495009, + 0.09819082, + 0.08980545, + ] + ), ], ] @@ -258,25 +503,31 @@ def test_polynomial_expansion_Finlayson2015(self): np.testing.assert_almost_equal( polynomial_expansion_Finlayson2015(RGB, i + 1, False), polynomials[i][0], - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( polynomial_expansion_Finlayson2015(RGB, i + 1, True), polynomials[i][1], - decimal=7) + decimal=7, + ) def test_raise_exception_polynomial_expansion_Finlayson2015(self): """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ polynomial_expansion_Finlayson2015` definition raised exception. """ - self.assertRaises(ValueError, polynomial_expansion_Finlayson2015, - np.array([0.17224810, 0.09170660, 0.06416938]), 5) + self.assertRaises( + ValueError, + polynomial_expansion_Finlayson2015, + np.array([0.17224810, 0.09170660, 0.06416938]), + 5, + ) @ignore_numpy_errors def test_nan_polynomial_expansion_Finlayson2015(self): """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ polynomial_expansion_Finlayson2015` definition nan support. """ @@ -288,13 +539,13 @@ def test_nan_polynomial_expansion_Finlayson2015(self): class TestPolynomialExpansionVandermonde(unittest.TestCase): """ - Defines :func:`colour.characterisation.correction.\ + Define :func:`colour.characterisation.correction.\ polynomial_expansion_Vandermonde` definition unit tests methods. """ def test_polynomial_expansion_Vandermonde(self): """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ polynomial_expansion_Vandermonde` definition. """ @@ -302,31 +553,61 @@ def test_polynomial_expansion_Vandermonde(self): polynomials = [ np.array([0.17224810, 0.09170660, 0.06416938, 1.00000000]), - np.array([ - 0.02966941, 0.00841010, 0.00411771, 0.17224810, 0.09170660, - 0.06416938, 1.00000000 - ]), - np.array([ - 0.00511050, 0.00077126, 0.00026423, 0.02966941, 0.00841010, - 0.00411771, 0.17224810, 0.09170660, 0.06416938, 1.00000000 - ]), - np.array([ - 0.00088027, 0.00007073, 0.00001696, 0.00511050, 0.00077126, - 0.00026423, 0.02966941, 0.00841010, 0.00411771, 0.17224810, - 0.09170660, 0.06416938, 1.00000000 - ]), + np.array( + [ + 0.02966941, + 0.00841010, + 0.00411771, + 0.17224810, + 0.09170660, + 0.06416938, + 1.00000000, + ] + ), + np.array( + [ + 0.00511050, + 0.00077126, + 0.00026423, + 0.02966941, + 0.00841010, + 0.00411771, + 0.17224810, + 0.09170660, + 0.06416938, + 1.00000000, + ] + ), + np.array( + [ + 0.00088027, + 0.00007073, + 0.00001696, + 0.00511050, + 0.00077126, + 0.00026423, + 0.02966941, + 0.00841010, + 0.00411771, + 0.17224810, + 0.09170660, + 0.06416938, + 1.00000000, + ] + ), ] for i in range(4): np.testing.assert_almost_equal( polynomial_expansion_Vandermonde(RGB, i + 1), polynomials[i], - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_polynomial_expansion_Vandermonde(self): """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ polynomial_expansion_Vandermonde` definition nan support. """ @@ -338,198 +619,303 @@ def test_nan_polynomial_expansion_Vandermonde(self): class TestMatrixColourCorrectionCheung2004(unittest.TestCase): """ - Defines :func:`colour.characterisation.correction.\ + Define :func:`colour.characterisation.correction.\ matrix_colour_correction_Cheung2004` definition unit tests methods. """ def test_matrix_colour_correction_Cheung2004(self): """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ matrix_colour_correction_Cheung2004` definition. """ np.testing.assert_almost_equal( matrix_colour_correction_Cheung2004(MATRIX_TEST, MATRIX_REFERENCE), - np.array([ - [0.69822661, 0.03071629, 0.16210422], - [0.06893498, 0.67579611, 0.16430385], - [-0.06314956, 0.09212471, 0.97134152], - ]), - decimal=7) + np.array( + [ + [0.69822661, 0.03071629, 0.16210422], + [0.06893498, 0.67579611, 0.16430385], + [-0.06314956, 0.09212471, 0.97134152], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( matrix_colour_correction_Cheung2004( - MATRIX_TEST, MATRIX_REFERENCE, terms=7), - np.array([ - [ - 0.80512769, 0.04001012, -0.01255261, -0.41056170, - -0.28052094, 0.68417697, 0.02251728 - ], - [ - 0.03270288, 0.71452384, 0.17581905, -0.00897913, - 0.04900199, -0.17162742, 0.01688472 - ], + MATRIX_TEST, MATRIX_REFERENCE, terms=7 + ), + np.array( [ - -0.03973098, -0.07164767, 1.16401636, 0.29017859, - -0.88909018, 0.26675507, 0.02345109 - ], - ]), - decimal=7) + [ + 0.80512769, + 0.04001012, + -0.01255261, + -0.41056170, + -0.28052094, + 0.68417697, + 0.02251728, + ], + [ + 0.03270288, + 0.71452384, + 0.17581905, + -0.00897913, + 0.04900199, + -0.17162742, + 0.01688472, + ], + [ + -0.03973098, + -0.07164767, + 1.16401636, + 0.29017859, + -0.88909018, + 0.26675507, + 0.02345109, + ], + ] + ), + decimal=7, + ) @ignore_numpy_errors - def test_nan_matrix_colour_correction_Cheung2004(self): + def test_nan_matrix_colour_correction_Cheung2004(self): # pragma: no cover """ - Tests :func:`colour.characterisation.correction. - matrix_colour_correction_Cheung2004` definition nan support. + Test :func:`colour.characterisation.correction.\ + matrix_colour_correction_Cheung2004` definition nan support. """ + # NOTE: Hangs on "Linux". + if platform.system() == "Linux": + return + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = list(set(permutations(cases * 3, r=3)))[0:4] for case in cases: try: matrix_colour_correction_Cheung2004( np.vstack([case, case, case]), - np.transpose(np.vstack([case, case, case]))) + np.transpose(np.vstack([case, case, case])), + ) except LinAlgError: pass class TestMatrixColourCorrectionFinlayson2015(unittest.TestCase): """ - Defines :func:`colour.characterisation.correction.\ + Define :func:`colour.characterisation.correction.\ matrix_colour_correction_Finlayson2015` definition unit tests methods. """ def test_matrix_colour_correction_Finlayson2015(self): """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ matrix_colour_correction_Finlayson2015` definition. """ np.testing.assert_almost_equal( - matrix_colour_correction_Finlayson2015(MATRIX_TEST, - MATRIX_REFERENCE), - np.array([ - [0.69822661, 0.03071629, 0.16210422], - [0.06893498, 0.67579611, 0.16430385], - [-0.06314956, 0.09212471, 0.97134152], - ]), - decimal=7) + matrix_colour_correction_Finlayson2015( + MATRIX_TEST, MATRIX_REFERENCE + ), + np.array( + [ + [0.69822661, 0.03071629, 0.16210422], + [0.06893498, 0.67579611, 0.16430385], + [-0.06314956, 0.09212471, 0.97134152], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( matrix_colour_correction_Finlayson2015( - MATRIX_TEST, MATRIX_REFERENCE, degree=3), - np.array([ - [ - 2.87796213, 9.85720054, 2.99863978, 76.97227806, - 73.73571500, -49.37563169, -48.70879206, -47.53280959, - 29.88241815, -39.82871801, -37.11388282, 23.30393209, - 3.81579802 - ], - [ - -0.78448243, 5.63631335, 0.95306110, 14.19762287, - 20.60124427, -18.05512861, -14.52994195, -13.10606336, - 10.53666341, -3.63132534, -12.49672335, 8.17401039, - 3.37995231 - ], + MATRIX_TEST, MATRIX_REFERENCE, degree=3 + ), + np.array( [ - -2.39092600, 10.57193455, 4.16361285, 23.41748866, - 58.26902059, -39.39669827, -26.63805785, -35.98397757, - 21.25508558, -4.12726077, -34.31995017, 18.72796247, - 7.33531009 - ], - ]), - decimal=7) + [ + 2.87796213, + 9.85720054, + 2.99863978, + 76.97227806, + 73.73571500, + -49.37563169, + -48.70879206, + -47.53280959, + 29.88241815, + -39.82871801, + -37.11388282, + 23.30393209, + 3.81579802, + ], + [ + -0.78448243, + 5.63631335, + 0.95306110, + 14.19762287, + 20.60124427, + -18.05512861, + -14.52994195, + -13.10606336, + 10.53666341, + -3.63132534, + -12.49672335, + 8.17401039, + 3.37995231, + ], + [ + -2.39092600, + 10.57193455, + 4.16361285, + 23.41748866, + 58.26902059, + -39.39669827, + -26.63805785, + -35.98397757, + 21.25508558, + -4.12726077, + -34.31995017, + 18.72796247, + 7.33531009, + ], + ] + ), + decimal=7, + ) @ignore_numpy_errors - def test_nan_matrix_colour_correction_Finlayson2015(self): + def test_nan_matrix_colour_correction_Finlayson2015( + self, + ): # pragma: no cover """ - Tests :func:`colour.characterisation.correction. - matrix_colour_correction_Finlayson2015` definition nan support. + Test :func:`colour.characterisation.correction.\ + matrix_colour_correction_Finlayson2015` definition nan support. """ + # NOTE: Hangs on "Linux". + if platform.system() == "Linux": + return + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = list(set(permutations(cases * 3, r=3)))[0:4] for case in cases: try: matrix_colour_correction_Finlayson2015( np.vstack([case, case, case]), - np.transpose(np.vstack([case, case, case]))) + np.transpose(np.vstack([case, case, case])), + ) except LinAlgError: pass class TestMatrixColourCorrectionVandermonde(unittest.TestCase): """ - Defines :func:`colour.characterisation.correction.\ + Define :func:`colour.characterisation.correction.\ matrix_colour_correction_Vandermonde` definition unit tests methods. """ def test_matrix_colour_correction_Vandermonde(self): """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ matrix_colour_correction_Vandermonde` definition. """ np.testing.assert_almost_equal( - matrix_colour_correction_Vandermonde(MATRIX_TEST, - MATRIX_REFERENCE), - np.array([ - [0.66770040, 0.02514036, 0.12745797, 0.02485425], - [0.03155494, 0.66896825, 0.12187874, 0.03043460], - [-0.14502258, 0.07716975, 0.87841836, 0.06666049], - ]), - decimal=7) + matrix_colour_correction_Vandermonde( + MATRIX_TEST, MATRIX_REFERENCE + ), + np.array( + [ + [0.66770040, 0.02514036, 0.12745797, 0.02485425], + [0.03155494, 0.66896825, 0.12187874, 0.03043460], + [-0.14502258, 0.07716975, 0.87841836, 0.06666049], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( matrix_colour_correction_Vandermonde( - MATRIX_TEST, MATRIX_REFERENCE, degree=3), - np.array([ - [ - -0.04328223, -1.87886146, 1.83369170, -0.10798116, - 1.06608177, -0.87495813, 0.75525839, -0.08558123, - 0.15919076, 0.02404598 - ], - [ - 0.00998152, 0.44525275, -0.53192490, 0.00904507, - -0.41034458, 0.36173334, 0.02904178, 0.78362950, - 0.07894900, 0.01986479 - ], + MATRIX_TEST, MATRIX_REFERENCE, degree=3 + ), + np.array( [ - -1.66921744, 3.62954420, -2.96789849, 2.31451409, - -3.10767297, 1.85975390, -0.98795093, 0.85962796, - 0.63591240, 0.07302317 - ], - ]), - decimal=7) + [ + -0.04328223, + -1.87886146, + 1.83369170, + -0.10798116, + 1.06608177, + -0.87495813, + 0.75525839, + -0.08558123, + 0.15919076, + 0.02404598, + ], + [ + 0.00998152, + 0.44525275, + -0.53192490, + 0.00904507, + -0.41034458, + 0.36173334, + 0.02904178, + 0.78362950, + 0.07894900, + 0.01986479, + ], + [ + -1.66921744, + 3.62954420, + -2.96789849, + 2.31451409, + -3.10767297, + 1.85975390, + -0.98795093, + 0.85962796, + 0.63591240, + 0.07302317, + ], + ] + ), + decimal=7, + ) @ignore_numpy_errors - def test_nan_matrix_colour_correction_Vandermonde(self): + def test_nan_matrix_colour_correction_Vandermonde( + self, + ): # pragma: no cover """ - Tests :func:`colour.characterisation.correction. - matrix_colour_correction_Vandermonde` definition nan support. + Test :func:`colour.characterisation.correction.\ + matrix_colour_correction_Vandermonde` definition nan support. """ + # NOTE: Hangs on "Linux". + if platform.system() == "Linux": + return + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = list(set(permutations(cases * 3, r=3)))[0:4] for case in cases: try: matrix_colour_correction_Vandermonde( np.vstack([case, case, case]), - np.transpose(np.vstack([case, case, case]))) + np.transpose(np.vstack([case, case, case])), + ) except LinAlgError: pass class TestColourCorrectionCheung2004(unittest.TestCase): """ - Defines :func:`colour.characterisation.correction.\ + Define :func:`colour.characterisation.correction.\ colour_correction_Cheung2004` definition unit tests methods. """ def test_colour_correction_Cheung2004(self): """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ colour_correction_Cheung2004` definition. """ @@ -538,135 +924,162 @@ def test_colour_correction_Cheung2004(self): np.testing.assert_almost_equal( colour_correction_Cheung2004(RGB, MATRIX_TEST, MATRIX_REFERENCE), np.array([0.13348722, 0.08439216, 0.05990144]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( colour_correction_Cheung2004( - RGB, MATRIX_TEST, MATRIX_REFERENCE, terms=7), + RGB, MATRIX_TEST, MATRIX_REFERENCE, terms=7 + ), np.array([0.15850295, 0.09871628, 0.08105752]), - decimal=7) + decimal=7, + ) def test_n_dimensional_colour_correction_Cheung2004(self): """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ colour_correction_Cheung2004` definition n-dimensional support. """ RGB = np.array([0.17224810, 0.09170660, 0.06416938]) - RGB_c = colour_correction_Cheung2004(RGB, MATRIX_TEST, - MATRIX_REFERENCE) + RGB_c = colour_correction_Cheung2004( + RGB, MATRIX_TEST, MATRIX_REFERENCE + ) RGB = np.tile(RGB, (6, 1)) RGB_c = np.tile(RGB_c, (6, 1)) np.testing.assert_almost_equal( colour_correction_Cheung2004(RGB, MATRIX_TEST, MATRIX_REFERENCE), RGB_c, - decimal=7) + decimal=7, + ) RGB = np.reshape(RGB, (2, 3, 3)) RGB_c = np.reshape(RGB_c, (2, 3, 3)) np.testing.assert_almost_equal( colour_correction_Cheung2004(RGB, MATRIX_TEST, MATRIX_REFERENCE), RGB_c, - decimal=7) + decimal=7, + ) @ignore_numpy_errors - def test_nan_colour_correction_Cheung2004(self): + def test_nan_colour_correction_Cheung2004(self): # pragma: no cover """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ colour_correction_Cheung2004` definition nan support. """ + # NOTE: Hangs on "Linux". + if platform.system() == "Linux": + return + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = list(set(permutations(cases * 3, r=3)))[0:4] for case in cases: try: colour_correction_Cheung2004( - case, np.vstack([case, case, case]), - np.transpose(np.vstack([case, case, case]))) + case, + np.vstack([case, case, case]), + np.transpose(np.vstack([case, case, case])), + ) except LinAlgError: pass class TestColourCorrectionFinlayson2015(unittest.TestCase): """ - Defines :func:`colour.characterisation.correction.\ + Define :func:`colour.characterisation.correction.\ colour_correction_Finlayson2015` definition unit tests methods. """ def test_colour_correction_Finlayson2015(self): """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ colour_correction_Finlayson2015` definition. """ RGB = np.array([0.17224810, 0.09170660, 0.06416938]) np.testing.assert_almost_equal( - colour_correction_Finlayson2015(RGB, MATRIX_TEST, - MATRIX_REFERENCE), + colour_correction_Finlayson2015( + RGB, MATRIX_TEST, MATRIX_REFERENCE + ), np.array([0.13348722, 0.08439216, 0.05990144]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( colour_correction_Finlayson2015( - RGB, MATRIX_TEST, MATRIX_REFERENCE, degree=3), + RGB, MATRIX_TEST, MATRIX_REFERENCE, degree=3 + ), np.array([0.13914542, 0.08602124, 0.06422973]), - decimal=7) + decimal=7, + ) def test_n_dimensional_colour_correction_Finlayson2015(self): """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ colour_correction_Finlayson2015` definition n-dimensional support. """ RGB = np.array([0.17224810, 0.09170660, 0.06416938]) - RGB_c = colour_correction_Finlayson2015(RGB, MATRIX_TEST, - MATRIX_REFERENCE) + RGB_c = colour_correction_Finlayson2015( + RGB, MATRIX_TEST, MATRIX_REFERENCE + ) RGB = np.tile(RGB, (6, 1)) RGB_c = np.tile(RGB_c, (6, 1)) np.testing.assert_almost_equal( - colour_correction_Finlayson2015(RGB, MATRIX_TEST, - MATRIX_REFERENCE), + colour_correction_Finlayson2015( + RGB, MATRIX_TEST, MATRIX_REFERENCE + ), RGB_c, - decimal=7) + decimal=7, + ) RGB = np.reshape(RGB, (2, 3, 3)) RGB_c = np.reshape(RGB_c, (2, 3, 3)) np.testing.assert_almost_equal( - colour_correction_Finlayson2015(RGB, MATRIX_TEST, - MATRIX_REFERENCE), + colour_correction_Finlayson2015( + RGB, MATRIX_TEST, MATRIX_REFERENCE + ), RGB_c, - decimal=7) + decimal=7, + ) @ignore_numpy_errors - def test_nan_colour_correction_Finlayson2015(self): + def test_nan_colour_correction_Finlayson2015(self): # pragma: no cover """ - Tests :func:`colour.characterisation.correction.\ -colour_correction_Finlayson2015` definition nan support. + Test :func:`colour.characterisation.correction. + colour_correction_Finlayson2015` definition nan support. """ + # NOTE: Hangs on "Linux". + if platform.system() == "Linux": + return + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = list(set(permutations(cases * 3, r=3)))[0:4] for case in cases: try: colour_correction_Finlayson2015( - case, np.vstack([case, case, case]), - np.transpose(np.vstack([case, case, case]))) + case, + np.vstack([case, case, case]), + np.transpose(np.vstack([case, case, case])), + ) except LinAlgError: pass class TestColourCorrectionVandermonde(unittest.TestCase): """ - Defines :func:`colour.characterisation.correction.\ + Define :func:`colour.characterisation.correction.\ colour_correction_Vandermonde` definition unit tests methods. """ def test_colour_correction_Vandermonde(self): """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ colour_correction_Vandermonde` definition. """ @@ -675,45 +1088,55 @@ def test_colour_correction_Vandermonde(self): np.testing.assert_almost_equal( colour_correction_Vandermonde(RGB, MATRIX_TEST, MATRIX_REFERENCE), np.array([0.15034881, 0.10503956, 0.10512517]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( colour_correction_Vandermonde( - RGB, MATRIX_TEST, MATRIX_REFERENCE, degree=3), + RGB, MATRIX_TEST, MATRIX_REFERENCE, degree=3 + ), np.array([0.15747814, 0.10035799, 0.06616709]), - decimal=7) + decimal=7, + ) def test_n_dimensional_colour_correction_Vandermonde(self): """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ colour_correction_Vandermonde` definition n-dimensional support. """ RGB = np.array([0.17224810, 0.09170660, 0.06416938]) - RGB_c = colour_correction_Vandermonde(RGB, MATRIX_TEST, - MATRIX_REFERENCE) + RGB_c = colour_correction_Vandermonde( + RGB, MATRIX_TEST, MATRIX_REFERENCE + ) RGB = np.tile(RGB, (6, 1)) RGB_c = np.tile(RGB_c, (6, 1)) np.testing.assert_almost_equal( colour_correction_Vandermonde(RGB, MATRIX_TEST, MATRIX_REFERENCE), RGB_c, - decimal=7) + decimal=7, + ) RGB = np.reshape(RGB, (2, 3, 3)) RGB_c = np.reshape(RGB_c, (2, 3, 3)) np.testing.assert_almost_equal( colour_correction_Vandermonde(RGB, MATRIX_TEST, MATRIX_REFERENCE), RGB_c, - decimal=7) + decimal=7, + ) @ignore_numpy_errors - def test_nan_colour_correction_Vandermonde(self): + def test_nan_colour_correction_Vandermonde(self): # pragma: no cover """ - Tests :func:`colour.characterisation.correction.\ + Test :func:`colour.characterisation.correction.\ colour_correction_Vandermonde` definition nan support. """ + # NOTE: Hangs on "Linux". + if platform.system() == "Linux": + return + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = list(set(permutations(cases * 3, r=3)))[0:4] for case in cases: @@ -727,5 +1150,5 @@ def test_nan_colour_correction_Vandermonde(self): pass -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/colorimetry/__init__.py b/colour/colorimetry/__init__.py index 4fbfb4afea..5c3eabe502 100644 --- a/colour/colorimetry/__init__.py +++ b/colour/colorimetry/__init__.py @@ -1,20 +1,35 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - -import sys - -from colour.utilities.deprecation import ModuleAPI, build_API_changes -from colour.utilities.documentation import is_documentation_building - -from .spectrum import (SpectralShape, SPECTRAL_SHAPE_DEFAULT, - SpectralDistribution, MultiSpectralDistributions, - sds_and_msds_to_sds, sds_and_msds_to_msds) +from .spectrum import ( + SpectralShape, + SPECTRAL_SHAPE_DEFAULT, + SpectralDistribution, + MultiSpectralDistributions, + reshape_sd, + reshape_msds, + sds_and_msds_to_sds, + sds_and_msds_to_msds, +) from .blackbody import sd_blackbody, blackbody_spectral_radiance, planck_law -from .cmfs import (LMS_ConeFundamentals, RGB_ColourMatchingFunctions, - XYZ_ColourMatchingFunctions) -from .datasets import * # noqa -from . import datasets +from .cmfs import ( + LMS_ConeFundamentals, + RGB_ColourMatchingFunctions, + XYZ_ColourMatchingFunctions, +) +from .datasets import ( + CCS_ILLUMINANTS, + CCS_LIGHT_SOURCES, + MSDS_CMFS, + MSDS_CMFS_LMS, + MSDS_CMFS_RGB, + MSDS_CMFS_STANDARD_OBSERVER, + SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES, + SDS_ILLUMINANTS, + SDS_LEFS, + SDS_LEFS_PHOTOPIC, + SDS_LEFS_SCOTOPIC, + SDS_LIGHT_SOURCES, + TVS_ILLUMINANTS, + TVS_ILLUMINANTS_HUNTERLAB, +) from .generation import sd_constant, sd_zeros, sd_ones from .generation import msds_constant, msds_zeros, msds_ones from .generation import SD_GAUSSIAN_METHODS @@ -23,35 +38,62 @@ from .generation import sd_single_led, sd_single_led_Ohno2005 from .generation import SD_MULTI_LEDS_METHODS from .generation import sd_multi_leds, sd_multi_leds_Ohno2005 -from .tristimulus import SD_TO_XYZ_METHODS, MSDS_TO_XYZ_METHODS -from .tristimulus import sd_to_XYZ, msds_to_XYZ -from .tristimulus import ( - SPECTRAL_SHAPE_ASTME308, lagrange_coefficients_ASTME2022, +from .tristimulus_values import SD_TO_XYZ_METHODS, MSDS_TO_XYZ_METHODS +from .tristimulus_values import sd_to_XYZ, msds_to_XYZ +from .tristimulus_values import ( + SPECTRAL_SHAPE_ASTME308, + handle_spectral_arguments, + lagrange_coefficients_ASTME2022, tristimulus_weighting_factors_ASTME2022, - adjust_tristimulus_weighting_factors_ASTME308, sd_to_XYZ_integration, - sd_to_XYZ_tristimulus_weighting_factors_ASTME308, sd_to_XYZ_ASTME308, - msds_to_XYZ_integration, msds_to_XYZ_ASTME308, wavelength_to_XYZ) + adjust_tristimulus_weighting_factors_ASTME308, + sd_to_XYZ_integration, + sd_to_XYZ_tristimulus_weighting_factors_ASTME308, + sd_to_XYZ_ASTME308, + msds_to_XYZ_integration, + msds_to_XYZ_ASTME308, + wavelength_to_XYZ, +) +from .uniformity import spectral_uniformity from .correction import BANDPASS_CORRECTION_METHODS from .correction import bandpass_correction from .correction import bandpass_correction_Stearns1988 -from .illuminants import (sd_CIE_standard_illuminant_A, - sd_CIE_illuminant_D_series, daylight_locus_function) -from .lefs import (sd_mesopic_luminous_efficiency_function, - mesopic_weighting_function) +from .illuminants import ( + sd_CIE_standard_illuminant_A, + sd_CIE_illuminant_D_series, + daylight_locus_function, +) +from .lefs import ( + sd_mesopic_luminous_efficiency_function, + mesopic_weighting_function, +) from .lightness import LIGHTNESS_METHODS from .lightness import lightness -from .lightness import (lightness_Glasser1958, lightness_Wyszecki1963, - lightness_CIE1976, lightness_Fairchild2010, - lightness_Fairchild2011) +from .lightness import ( + lightness_Glasser1958, + lightness_Wyszecki1963, + lightness_CIE1976, + lightness_Fairchild2010, + lightness_Fairchild2011, + lightness_Abebe2017, +) from .lightness import intermediate_lightness_function_CIE1976 from .luminance import LUMINANCE_METHODS from .luminance import luminance -from .luminance import (luminance_Newhall1943, luminance_ASTMD1535, - luminance_CIE1976, luminance_Fairchild2010, - luminance_Fairchild2011) +from .luminance import ( + luminance_Newhall1943, + luminance_ASTMD1535, + luminance_CIE1976, + luminance_Fairchild2010, + luminance_Fairchild2011, + luminance_Abebe2017, +) from .luminance import intermediate_luminance_function_CIE1976 -from .dominant import (dominant_wavelength, complementary_wavelength, - excitation_purity, colorimetric_purity) +from .dominant import ( + dominant_wavelength, + complementary_wavelength, + excitation_purity, + colorimetric_purity, +) from .photometry import luminous_flux, luminous_efficiency, luminous_efficacy from .transformations import RGB_10_degree_cmfs_to_LMS_10_degree_cmfs from .transformations import RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs @@ -60,234 +102,216 @@ from .transformations import LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs from .whiteness import WHITENESS_METHODS from .whiteness import whiteness -from .whiteness import (whiteness_Berger1959, whiteness_Taube1960, - whiteness_Stensby1968, whiteness_ASTME313, - whiteness_Ganz1979, whiteness_CIE2004) +from .whiteness import ( + whiteness_Berger1959, + whiteness_Taube1960, + whiteness_Stensby1968, + whiteness_ASTME313, + whiteness_Ganz1979, + whiteness_CIE2004, +) from .yellowness import YELLOWNESS_METHODS from .yellowness import yellowness -from .yellowness import yellowness_ASTMD1925, yellowness_ASTME313 +from .yellowness import ( + yellowness_ASTMD1925, + yellowness_ASTME313_alternative, + YELLOWNESS_COEFFICIENTS_ASTME313, + yellowness_ASTME313, +) __all__ = [ - 'SpectralShape', 'SPECTRAL_SHAPE_DEFAULT', 'SpectralDistribution', - 'MultiSpectralDistributions', 'sds_and_msds_to_sds', 'sds_and_msds_to_msds' + "SpectralShape", + "SPECTRAL_SHAPE_DEFAULT", + "SpectralDistribution", + "MultiSpectralDistributions", + "reshape_sd", + "reshape_msds", + "sds_and_msds_to_sds", + "sds_and_msds_to_msds", ] -__all__ += ['sd_blackbody', 'blackbody_spectral_radiance', 'planck_law'] __all__ += [ - 'LMS_ConeFundamentals', 'RGB_ColourMatchingFunctions', - 'XYZ_ColourMatchingFunctions' + "sd_blackbody", + "blackbody_spectral_radiance", + "planck_law", ] -__all__ += datasets.__all__ -__all__ += ['sd_constant', 'sd_zeros', 'sd_ones'] -__all__ += ['msds_constant', 'msds_zeros', 'msds_ones'] -__all__ += ['SD_GAUSSIAN_METHODS'] -__all__ += ['sd_gaussian', 'sd_gaussian_normal', 'sd_gaussian_fwhm'] -__all__ += ['SD_SINGLE_LED_METHODS'] -__all__ += ['sd_single_led', 'sd_single_led_Ohno2005'] -__all__ += ['SD_MULTI_LEDS_METHODS'] -__all__ += ['sd_multi_leds', 'sd_multi_leds_Ohno2005'] -__all__ += ['SD_TO_XYZ_METHODS', 'MSDS_TO_XYZ_METHODS'] -__all__ += ['sd_to_XYZ', 'msds_to_XYZ'] __all__ += [ - 'SPECTRAL_SHAPE_ASTME308', 'lagrange_coefficients_ASTME2022', - 'tristimulus_weighting_factors_ASTME2022', - 'adjust_tristimulus_weighting_factors_ASTME308', 'sd_to_XYZ_integration', - 'sd_to_XYZ_tristimulus_weighting_factors_ASTME308', 'sd_to_XYZ_ASTME308', - 'msds_to_XYZ_integration', 'msds_to_XYZ_ASTME308', 'wavelength_to_XYZ' + "LMS_ConeFundamentals", + "RGB_ColourMatchingFunctions", + "XYZ_ColourMatchingFunctions", ] -__all__ += ['BANDPASS_CORRECTION_METHODS'] -__all__ += ['bandpass_correction'] -__all__ += ['bandpass_correction_Stearns1988'] __all__ += [ - 'sd_CIE_standard_illuminant_A', 'sd_CIE_illuminant_D_series', - 'daylight_locus_function' + "CCS_ILLUMINANTS", + "MSDS_CMFS", + "MSDS_CMFS_LMS", + "MSDS_CMFS_RGB", + "MSDS_CMFS_STANDARD_OBSERVER", + "SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES", + "SDS_ILLUMINANTS", + "SDS_LEFS", + "SDS_LEFS_PHOTOPIC", + "SDS_LEFS_SCOTOPIC", + "TVS_ILLUMINANTS", + "TVS_ILLUMINANTS_HUNTERLAB", + "CCS_LIGHT_SOURCES", + "SDS_LIGHT_SOURCES", ] __all__ += [ - 'sd_mesopic_luminous_efficiency_function', 'mesopic_weighting_function' + "sd_constant", + "sd_zeros", + "sd_ones", ] -__all__ += ['LIGHTNESS_METHODS'] -__all__ += ['lightness'] __all__ += [ - 'lightness_Glasser1958', 'lightness_Wyszecki1963', 'lightness_CIE1976', - 'lightness_Fairchild2010', 'lightness_Fairchild2011' + "msds_constant", + "msds_zeros", + "msds_ones", ] -__all__ += ['intermediate_lightness_function_CIE1976'] -__all__ += ['LUMINANCE_METHODS'] -__all__ += ['luminance'] __all__ += [ - 'luminance_Newhall1943', 'luminance_ASTMD1535', 'luminance_CIE1976', - 'luminance_Fairchild2010', 'luminance_Fairchild2011' + "SD_GAUSSIAN_METHODS", ] -__all__ += ['intermediate_luminance_function_CIE1976'] __all__ += [ - 'dominant_wavelength', 'complementary_wavelength', 'excitation_purity', - 'colorimetric_purity' + "sd_gaussian", + "sd_gaussian_normal", + "sd_gaussian_fwhm", ] -__all__ += ['luminous_flux', 'luminous_efficiency', 'luminous_efficacy'] -__all__ += ['RGB_10_degree_cmfs_to_LMS_10_degree_cmfs'] -__all__ += ['RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs'] -__all__ += ['RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs'] -__all__ += ['LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs'] -__all__ += ['LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs'] -__all__ += ['WHITENESS_METHODS'] -__all__ += ['whiteness'] __all__ += [ - 'whiteness_Berger1959', 'whiteness_Taube1960', 'whiteness_Stensby1968', - 'whiteness_ASTME313', 'whiteness_Ganz1979', 'whiteness_CIE2004' + "SD_SINGLE_LED_METHODS", ] -__all__ += ['YELLOWNESS_METHODS'] -__all__ += ['yellowness'] -__all__ += ['yellowness_ASTMD1925', 'yellowness_ASTME313'] - - -# ----------------------------------------------------------------------------# -# --- API Changes and Deprecation Management ---# -# ----------------------------------------------------------------------------# -class colorimetry(ModuleAPI): - def __getattr__(self, attribute): - return super(colorimetry, self).__getattr__(attribute) - - -# v0.3.12 -API_CHANGES = { - 'ObjectRenamed': [ - [ - 'colour.colorimetry.spectral_to_XYZ_ASTME30815', - 'colour.colorimetry.sd_to_XYZ_ASTME308', - ], - [ - 'colour.colorimetry.spectral_to_XYZ_integration', - 'colour.colorimetry.sd_to_XYZ_integration', - ], - [ - 'colour.colorimetry.spectral_to_XYZ_tristimulus_weighting_factors_ASTME30815', # noqa - 'colour.colorimetry.sd_to_XYZ_tristimulus_weighting_factors_ASTME308', # noqa - ], - ] -} -""" -Defines *colour.colorimetry* sub-package API changes. - -API_CHANGES : dict -""" - -# v0.3.14 -API_CHANGES['ObjectRenamed'] = API_CHANGES['ObjectRenamed'] + [ - [ - 'colour.colorimetry.adjust_tristimulus_weighting_factors_ASTME30815', # noqa - 'colour.colorimetry.adjust_tristimulus_weighting_factors_ASTME308', # noqa - ], - [ - 'colour.colorimetry.lagrange_coefficients_ASTME202211', - 'colour.colorimetry.lagrange_coefficients_ASTME2022', - ], - [ - 'colour.colorimetry.luminance_ASTMD153508', - 'colour.colorimetry.luminance_ASTMD1535', - ], - [ - 'colour.colorimetry.sd_to_XYZ_ASTME30815', - 'colour.colorimetry.sd_to_XYZ_ASTME308', - ], - [ - 'colour.colorimetry.sd_to_XYZ_tristimulus_weighting_factors_ASTME30815', # noqa - 'colour.colorimetry.sd_to_XYZ_tristimulus_weighting_factors_ASTME308', # noqa - ], - [ - 'colour.colorimetry.tristimulus_weighting_factors_ASTME202211', - 'colour.colorimetry.tristimulus_weighting_factors_ASTME2022', - ], +__all__ += [ + "sd_single_led", + "sd_single_led_Ohno2005", ] - -# v0.3.16 -API_CHANGES['ObjectRenamed'] = API_CHANGES['ObjectRenamed'] + [ - [ - 'colour.colorimetry.ASTME308_PRACTISE_SHAPE', - 'colour.colorimetry.SPECTRAL_SHAPE_ASTME308', - ], - [ - 'colour.colorimetry.CMFS', - 'colour.colorimetry.MSDS_CMFS', - ], - [ - 'colour.colorimetry.D_ILLUMINANTS_S_SDS', - 'colour.colorimetry.SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES', - ], - [ - 'colour.colorimetry.DEFAULT_SPECTRAL_SHAPE', - 'colour.colorimetry.SPECTRAL_SHAPE_DEFAULT', - ], - [ - 'colour.colorimetry.HUNTERLAB_ILLUMINANTS', - 'colour.colorimetry.TVS_ILLUMINANTS_HUNTERLAB', - ], - [ - 'colour.colorimetry.ILLUMINANTS', - 'colour.colorimetry.CCS_ILLUMINANTS', - ], - [ - 'colour.colorimetry.ILLUMINANTS_SDS', - 'colour.colorimetry.SDS_ILLUMINANTS', - ], - [ - 'colour.colorimetry.LEFS', - 'colour.colorimetry.SDS_LEFS', - ], - [ - 'colour.colorimetry.LIGHT_SOURCES', - 'colour.colorimetry.CCS_LIGHT_SOURCES', - ], - [ - 'colour.colorimetry.LIGHT_SOURCES_SDS', - 'colour.colorimetry.SDS_LIGHT_SOURCES', - ], - [ - 'colour.colorimetry.LMS_CMFS', - 'colour.colorimetry.MSDS_CMFS_LMS', - ], - [ - 'colour.colorimetry.MULTI_SD_TO_XYZ_METHODS', - 'colour.colorimetry.MSDS_TO_XYZ_METHODS', - ], - [ - 'colour.colorimetry.multi_sds_to_XYZ_integration', - 'colour.colorimetry.msds_to_XYZ_integration', - ], - [ - 'colour.colorimetry.multi_sds_to_XYZ_ASTME308', - 'colour.colorimetry.msds_to_XYZ_ASTME308', - ], - [ - 'colour.colorimetry.multi_sds_to_XYZ', - 'colour.colorimetry.msds_to_XYZ', - ], - [ - 'colour.colorimetry.PHOTOPIC_LEFS', - 'colour.colorimetry.SDS_LEFS_PHOTOPIC', - ], - [ - 'colour.colorimetry.RGB_CMFS', - 'colour.colorimetry.MSDS_CMFS_RGB', - ], - [ - 'colour.colorimetry.SCOTOPIC_LEFS', - 'colour.colorimetry.SDS_LEFS_SCOTOPIC', - ], - [ - 'colour.colorimetry.STANDARD_OBSERVERS_CMFS', - 'colour.colorimetry.MSDS_CMFS_STANDARD_OBSERVER', - ], - [ - 'colour.colorimetry.sds_and_multi_sds_to_sds', - 'colour.colorimetry.sds_and_msds_to_sds', - ], - [ - 'colour.colorimetry.sds_and_multi_sds_to_multi_sds', - 'colour.colorimetry.sds_and_msds_to_msds', - ], +__all__ += [ + "SD_MULTI_LEDS_METHODS", +] +__all__ += [ + "sd_multi_leds", + "sd_multi_leds_Ohno2005", +] +__all__ += [ + "SD_TO_XYZ_METHODS", + "MSDS_TO_XYZ_METHODS", +] +__all__ += [ + "sd_to_XYZ", + "msds_to_XYZ", +] +__all__ += [ + "SPECTRAL_SHAPE_ASTME308", + "handle_spectral_arguments", + "lagrange_coefficients_ASTME2022", + "tristimulus_weighting_factors_ASTME2022", + "adjust_tristimulus_weighting_factors_ASTME308", + "sd_to_XYZ_integration", + "sd_to_XYZ_tristimulus_weighting_factors_ASTME308", + "sd_to_XYZ_ASTME308", + "msds_to_XYZ_integration", + "msds_to_XYZ_ASTME308", + "wavelength_to_XYZ", +] +__all__ += [ + "spectral_uniformity", +] +__all__ += [ + "BANDPASS_CORRECTION_METHODS", +] +__all__ += [ + "bandpass_correction", +] +__all__ += [ + "bandpass_correction_Stearns1988", +] +__all__ += [ + "sd_CIE_standard_illuminant_A", + "sd_CIE_illuminant_D_series", + "daylight_locus_function", +] +__all__ += [ + "sd_mesopic_luminous_efficiency_function", + "mesopic_weighting_function", +] +__all__ += [ + "LIGHTNESS_METHODS", +] +__all__ += [ + "lightness", +] +__all__ += [ + "lightness_Glasser1958", + "lightness_Wyszecki1963", + "lightness_CIE1976", + "lightness_Fairchild2010", + "lightness_Fairchild2011", + "lightness_Abebe2017", +] +__all__ += [ + "intermediate_lightness_function_CIE1976", +] +__all__ += [ + "LUMINANCE_METHODS", +] +__all__ += [ + "luminance", +] +__all__ += [ + "luminance_Newhall1943", + "luminance_ASTMD1535", + "luminance_CIE1976", + "luminance_Fairchild2010", + "luminance_Fairchild2011", + "luminance_Abebe2017", +] +__all__ += [ + "intermediate_luminance_function_CIE1976", +] +__all__ += [ + "dominant_wavelength", + "complementary_wavelength", + "excitation_purity", + "colorimetric_purity", +] +__all__ += [ + "luminous_flux", + "luminous_efficiency", + "luminous_efficacy", +] +__all__ += [ + "RGB_10_degree_cmfs_to_LMS_10_degree_cmfs", +] +__all__ += [ + "RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs", +] +__all__ += [ + "RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs", +] +__all__ += [ + "LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs", +] +__all__ += [ + "LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs", +] +__all__ += [ + "WHITENESS_METHODS", +] +__all__ += [ + "whiteness", +] +__all__ += [ + "whiteness_Berger1959", + "whiteness_Taube1960", + "whiteness_Stensby1968", + "whiteness_ASTME313", + "whiteness_Ganz1979", + "whiteness_CIE2004", +] +__all__ += [ + "YELLOWNESS_METHODS", +] +__all__ += [ + "yellowness", +] +__all__ += [ + "yellowness_ASTMD1925", + "yellowness_ASTME313_alternative", + "YELLOWNESS_COEFFICIENTS_ASTME313", + "yellowness_ASTME313", ] - -if not is_documentation_building(): - sys.modules['colour.colorimetry'] = colorimetry( - sys.modules['colour.colorimetry'], build_API_changes(API_CHANGES)) - - del ModuleAPI, is_documentation_building, build_API_changes, sys diff --git a/colour/colorimetry/blackbody.py b/colour/colorimetry/blackbody.py index 0226a305a8..3d1d8a4b61 100644 --- a/colour/colorimetry/blackbody.py +++ b/colour/colorimetry/blackbody.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ Blackbody - Planckian Radiator ============================== -Defines objects to compute the spectral radiance of a planckian radiator and -its spectral distribution. +Defines the objects to compute the spectral radiance of a planckian radiator +and its spectral distribution. References ---------- @@ -13,60 +12,71 @@ 3rd Edition (pp. 77-82). ISBN:978-3-901906-33-6 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.colorimetry import (SPECTRAL_SHAPE_DEFAULT, SpectralDistribution) -from colour.utilities import as_float_array, usage_warning +from colour.colorimetry import ( + SPECTRAL_SHAPE_DEFAULT, + SpectralDistribution, + SpectralShape, +) +from colour.hints import Floating, FloatingOrArrayLike, FloatingOrNDArray +from colour.utilities import as_float_array -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'CONSTANT_C1', 'CONSTANT_C2', 'CONSTANT_N', 'planck_law', - 'blackbody_spectral_radiance', 'sd_blackbody' + "CONSTANT_C1", + "CONSTANT_C2", + "CONSTANT_N", + "planck_law", + "blackbody_spectral_radiance", + "sd_blackbody", ] # 2 * math.pi * CONSTANT_PLANCK * CONSTANT_LIGHT_SPEED ** 2 -CONSTANT_C1 = 3.741771e-16 +CONSTANT_C1: float = 3.741771e-16 # CONSTANT_PLANCK * CONSTANT_LIGHT_SPEED / CONSTANT_BOLTZMANN -CONSTANT_C2 = 1.4388e-2 +CONSTANT_C2: float = 1.4388e-2 -CONSTANT_N = 1 +CONSTANT_N: float = 1 -def planck_law(wavelength, - temperature, - c1=CONSTANT_C1, - c2=CONSTANT_C2, - n=CONSTANT_N): +def planck_law( + wavelength: FloatingOrArrayLike, + temperature: FloatingOrArrayLike, + c1: Floating = CONSTANT_C1, + c2: Floating = CONSTANT_C2, + n: Floating = CONSTANT_N, +) -> FloatingOrNDArray: """ - Returns the spectral radiance of a blackbody at thermodynamic temperature + Return the spectral radiance of a blackbody at thermodynamic temperature :math:`T[K]` in a medium having index of refraction :math:`n`. Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength in meters. - temperature : numeric or array_like + temperature Temperature :math:`T[K]` in kelvin degrees. - c1 : numeric or array_like, optional + c1 The official value of :math:`c1` is provided by the Committee on Data for Science and Technology (CODATA) and is :math:`c1=3,741771x10.16\\ W/m_2` *(Mohr and Taylor, 2000)*. - c2 : numeric or array_like, optional + c2 Since :math:`T` is measured on the International Temperature Scale, the value of :math:`c2` used in colorimetry should follow that adopted in the current International Temperature Scale (ITS-90) *(Preston-Thomas, 1990; Mielenz et aI., 1991)*, namely :math:`c2=1,4388x10.2\\ m/K`. - n : numeric or array_like, optional + n Medium index of refraction. For dry air at 15C and 101 325 Pa, containing 0,03 percent by volume of carbon dioxide, it is approximately 1,00028 throughout the visible region although @@ -74,7 +84,7 @@ def planck_law(wavelength, Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Radiance in *watts per steradian per square metre* (:math:`W/sr/m^2`). Notes @@ -89,7 +99,6 @@ def planck_law(wavelength, Examples -------- - >>> # Doctests ellipsis for Python 2.x compatibility. >>> planck_law(500 * 1e-9, 5500) # doctest: +ELLIPSIS 20472701909806.5... """ @@ -97,8 +106,7 @@ def planck_law(wavelength, l = as_float_array(wavelength) # noqa t = as_float_array(temperature) - p = (((c1 * n ** -2 * l ** -5) / np.pi) * (np.exp(c2 / (n * l * t)) - 1) ** - -1) + p = ((c1 * n**-2 * l**-5) / np.pi) * (np.expm1(c2 / (n * l * t))) ** -1 return p @@ -106,34 +114,36 @@ def planck_law(wavelength, blackbody_spectral_radiance = planck_law -def sd_blackbody(temperature, - shape=SPECTRAL_SHAPE_DEFAULT, - c1=CONSTANT_C1, - c2=CONSTANT_C2, - n=CONSTANT_N): +def sd_blackbody( + temperature: Floating, + shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, + c1: Floating = CONSTANT_C1, + c2: Floating = CONSTANT_C2, + n: Floating = CONSTANT_N, +) -> SpectralDistribution: """ - Returns the spectral distribution of the planckian radiator for given + Return the spectral distribution of the planckian radiator for given temperature :math:`T[K]` with values in *watts per steradian per square metre per nanometer* (:math:`W/sr/m^2/nm`). Parameters ---------- - temperature : numeric + temperature Temperature :math:`T[K]` in kelvin degrees. - shape : SpectralShape, optional + shape Spectral shape used to create the spectral distribution of the planckian radiator. - c1 : numeric, optional + c1 The official value of :math:`c1` is provided by the Committee on Data for Science and Technology (CODATA) and is :math:`c1=3,741771x10.16\\ W/m_2` *(Mohr and Taylor, 2000)*. - c2 : numeric, optional + c2 Since :math:`T` is measured on the International Temperature Scale, the value of :math:`c2` used in colorimetry should follow that adopted in the current International Temperature Scale (ITS-90) *(Preston-Thomas, 1990; Mielenz et aI., 1991)*, namely :math:`c2=1,4388x10.2\\ m/K`. - n : numeric, optional + n Medium index of refraction. For dry air at 15C and 101 325 Pa, containing 0,03 percent by volume of carbon dioxide, it is approximately 1,00028 throughout the visible region although @@ -141,7 +151,7 @@ def sd_blackbody(temperature, Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Blackbody spectral distribution with values in *watts per steradian per square metre per nanometer* (:math:`W/sr/m^2/nm`). @@ -578,17 +588,9 @@ def sd_blackbody(temperature, extrapolator_kwargs={...}) """ - # TODO: Remove warning when deemed appropriate. - usage_warning( - 'For consistency reasons, the unit of the planckian radiator spectral ' - 'distribution values has been changed from "W/sr/m^2/m" to ' - '"W/sr/m^2/nm".\nThey are now multiplied by 1e-9. Note that this only ' - 'affects computations requiring absolute quantities.\n' - 'See https://github.com/colour-science/colour/issues/559 for more ' - 'background information.') - wavelengths = shape.range() return SpectralDistribution( planck_law(wavelengths * 1e-9, temperature, c1, c2, n) * 1e-9, wavelengths, - name='{0}K Blackbody'.format(temperature)) + name=f"{temperature}K Blackbody", + ) diff --git a/colour/colorimetry/cmfs.py b/colour/colorimetry/cmfs.py index efa4bf28cc..d3c1ccef10 100644 --- a/colour/colorimetry/cmfs.py +++ b/colour/colorimetry/cmfs.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ Colour Matching Functions ========================= -Defines colour matching functions classes for the datasets from -:mod:`colour.colorimetry.datasets.cmfs` module: +Defines the colour matching functions classes for the datasets from +the :mod:`colour.colorimetry.datasets.cmfs` module: - :class:`colour.colorimetry.LMS_ConeFundamentals`: Implements support for the *Stockman and Sharpe* *LMS* cone fundamentals colour matching @@ -15,169 +14,240 @@ for the *CIE Standard Observers* *XYZ* colour matching functions. """ -from __future__ import division, unicode_literals +from __future__ import annotations -from colour.colorimetry import MultiSpectralDistributions +from colour.colorimetry import ( + MultiSpectralDistributions, + SpectralDistribution, + SpectralShape, +) +from colour.continuous import MultiSignals, Signal +from colour.hints import ArrayLike, Any, Optional, Sequence, Union +from colour.utilities import is_pandas_installed -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +if is_pandas_installed(): + from pandas import DataFrame, Series +else: # pragma: no cover + from unittest import mock + + DataFrame = mock.MagicMock() + Series = mock.MagicMock() + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'LMS_ConeFundamentals', 'RGB_ColourMatchingFunctions', - 'XYZ_ColourMatchingFunctions' + "LMS_ConeFundamentals", + "RGB_ColourMatchingFunctions", + "XYZ_ColourMatchingFunctions", ] class LMS_ConeFundamentals(MultiSpectralDistributions): """ - Implements support for the Stockman and Sharpe *LMS* cone fundamentals + Implement support for the Stockman and Sharpe *LMS* cone fundamentals colour matching functions. Parameters ---------- - data : Series or Dataframe or Signal or MultiSignals or \ -MultiSpectralDistributions or array_like or dict_like, optional + data Data to be stored in the multi-spectral distributions. - domain : array_like, optional + domain + Values to initialise the multiple :class:`colour.SpectralDistribution` class instances :attr:`colour.continuous.Signal.wavelengths` attribute with. If both ``data`` and ``domain`` arguments are defined, the latter will be used to initialise the - Values to initialise the multiple :class:`colour.SpectralDistribution` - :attr:`colour.continuous.Signal.wavelengths` attribute. - labels : array_like, optional + :attr:`colour.continuous.Signal.wavelengths` property. + labels Names to use for the :class:`colour.SpectralDistribution` class instances. Other Parameters ---------------- - name : unicode, optional - Multi-spectral distributions name. - interpolator : object, optional - Interpolator class type to use as interpolating function for the - :class:`colour.SpectralDistribution` class instances. - interpolator_kwargs : dict_like, optional - Arguments to use when instantiating the interpolating function - of the :class:`colour.SpectralDistribution` class instances. - extrapolator : object, optional + extrapolator Extrapolator class type to use as extrapolating function for the :class:`colour.SpectralDistribution` class instances. - extrapolator_kwargs : dict_like, optional + extrapolator_kwargs Arguments to use when instantiating the extrapolating function of the :class:`colour.SpectralDistribution` class instances. - strict_labels : array_like, optional + interpolator + Interpolator class type to use as interpolating function for the + :class:`colour.SpectralDistribution` class instances. + interpolator_kwargs + Arguments to use when instantiating the interpolating function + of the :class:`colour.SpectralDistribution` class instances. + name + Multi-spectral distributions name. + strict_labels Multi-spectral distributions labels for figures, default to - :attr:`colour.colorimetry.LMS_ConeFundamentals.labels` attribute value. + :attr:`colour.colorimetry.LMS_ConeFundamentals.labels` property + value. """ - def __init__(self, data=None, domain=None, labels=None, **kwargs): - super(LMS_ConeFundamentals, self).__init__( + def __init__( + self, + data: Optional[ + Union[ + ArrayLike, + DataFrame, + dict, + MultiSignals, + MultiSpectralDistributions, + Sequence, + Series, + Signal, + SpectralDistribution, + ] + ] = None, + domain: Optional[Union[ArrayLike, SpectralShape]] = None, + labels: Optional[Sequence] = None, + **kwargs: Any, + ): + super().__init__( data, domain, - labels=('l_bar', 'm_bar', 's_bar'), - strict_labels=('$\\bar{l}$', '$\\bar{m}$', '$\\bar{s}$'), - **kwargs) + labels=("l_bar", "m_bar", "s_bar"), + strict_labels=("$\\bar{l}$", "$\\bar{m}$", "$\\bar{s}$"), + **kwargs, + ) class RGB_ColourMatchingFunctions(MultiSpectralDistributions): """ - Implements support for the *CIE RGB* colour matching functions. + Implement support for the *CIE RGB* colour matching functions. Parameters ---------- - data : Series or Dataframe or Signal or MultiSignals or \ -MultiSpectralDistributions or array_like or dict_like, optional + data Data to be stored in the multi-spectral distributions. - domain : array_like, optional + domain Values to initialise the multiple :class:`colour.SpectralDistribution` class instances :attr:`colour.continuous.Signal.wavelengths` attribute with. If both ``data`` and ``domain`` arguments are defined, the latter will be used to initialise the - :attr:`colour.continuous.Signal.wavelengths` attribute. - labels : array_like, optional + :attr:`colour.continuous.Signal.wavelengths` property. + labels Names to use for the :class:`colour.SpectralDistribution` class instances. Other Parameters ---------------- - name : unicode, optional - Multi-spectral distributions name. - interpolator : object, optional - Interpolator class type to use as interpolating function for the - :class:`colour.SpectralDistribution` class instances. - interpolator_kwargs : dict_like, optional - Arguments to use when instantiating the interpolating function - of the :class:`colour.SpectralDistribution` class instances. - extrapolator : object, optional + extrapolator Extrapolator class type to use as extrapolating function for the :class:`colour.SpectralDistribution` class instances. - extrapolator_kwargs : dict_like, optional + extrapolator_kwargs Arguments to use when instantiating the extrapolating function of the :class:`colour.SpectralDistribution` class instances. - strict_labels : array_like, optional + interpolator + Interpolator class type to use as interpolating function for the + :class:`colour.SpectralDistribution` class instances. + interpolator_kwargs + Arguments to use when instantiating the interpolating function + of the :class:`colour.SpectralDistribution` class instances. + name + Multi-spectral distributions name. + strict_labels Multi-spectral distributions labels for figures, default to - :attr:`colour.colorimetry.RGB_ColourMatchingFunctions.labels` attribute + :attr:`colour.colorimetry.RGB_ColourMatchingFunctions.labels` property value. """ - def __init__(self, data=None, domain=None, labels=None, **kwargs): - super(RGB_ColourMatchingFunctions, self).__init__( + def __init__( + self, + data: Optional[ + Union[ + ArrayLike, + DataFrame, + dict, + MultiSignals, + MultiSpectralDistributions, + Sequence, + Series, + Signal, + SpectralDistribution, + ] + ] = None, + domain: Optional[Union[ArrayLike, SpectralShape]] = None, + labels: Optional[Sequence] = None, + **kwargs: Any, + ): + super().__init__( data, domain, - labels=('r_bar', 'g_bar', 'b_bar'), - strict_labels=('$\\bar{r}$', '$\\bar{g}$', '$\\bar{b}$'), - **kwargs) + labels=("r_bar", "g_bar", "b_bar"), + strict_labels=("$\\bar{r}$", "$\\bar{g}$", "$\\bar{b}$"), + **kwargs, + ) class XYZ_ColourMatchingFunctions(MultiSpectralDistributions): """ - Implements support for the *CIE* Standard Observers *XYZ* colour matching + Implement support for the *CIE* Standard Observers *XYZ* colour matching functions. Parameters ---------- - data : Series or Dataframe or Signal or MultiSignals or \ -MultiSpectralDistributions or array_like or dict_like, optional + data Data to be stored in the multi-spectral distributions. - domain : array_like, optional + domain Values to initialise the multiple :class:`colour.SpectralDistribution` class instances :attr:`colour.continuous.Signal.wavelengths` attribute with. If both ``data`` and ``domain`` arguments are defined, the latter will be used to initialise the - :attr:`colour.continuous.Signal.wavelengths` attribute. - labels : array_like, optional + :attr:`colour.continuous.Signal.wavelengths` property. + labels Names to use for the :class:`colour.SpectralDistribution` class instances. Other Parameters ---------------- - name : unicode, optional - Multi-spectral distributions name. - interpolator : object, optional - Interpolator class type to use as interpolating function for the - :class:`colour.SpectralDistribution` class instances. - interpolator_kwargs : dict_like, optional - Arguments to use when instantiating the interpolating function - of the :class:`colour.SpectralDistribution` class instances. - extrapolator : object, optional + extrapolator Extrapolator class type to use as extrapolating function for the :class:`colour.SpectralDistribution` class instances. - extrapolator_kwargs : dict_like, optional + extrapolator_kwargs Arguments to use when instantiating the extrapolating function of the :class:`colour.SpectralDistribution` class instances. - strict_labels : array_like, optional + interpolator + Interpolator class type to use as interpolating function for the + :class:`colour.SpectralDistribution` class instances. + interpolator_kwargs + Arguments to use when instantiating the interpolating function + of the :class:`colour.SpectralDistribution` class instances. + name + Multi-spectral distributions name. + strict_labels Multi-spectral distributions labels for figures, default to - :attr:`colour.colorimetry.XYZ_ColourMatchingFunctions.labels` attribute + :attr:`colour.colorimetry.XYZ_ColourMatchingFunctions.labels` property value. """ - def __init__(self, data=None, domain=None, labels=None, **kwargs): - super(XYZ_ColourMatchingFunctions, self).__init__( + def __init__( + self, + data: Optional[ + Union[ + ArrayLike, + DataFrame, + dict, + MultiSignals, + MultiSpectralDistributions, + Sequence, + Series, + Signal, + SpectralDistribution, + ] + ] = None, + domain: Optional[Union[ArrayLike, SpectralShape]] = None, + labels: Optional[Sequence] = None, + **kwargs: Any, + ): + super().__init__( data, domain, - labels=('x_bar', 'y_bar', 'z_bar'), - strict_labels=('$\\bar{x}$', '$\\bar{y}$', '$\\bar{z}$'), - **kwargs) + labels=("x_bar", "y_bar", "z_bar"), + strict_labels=("$\\bar{x}$", "$\\bar{y}$", "$\\bar{z}$"), + **kwargs, + ) diff --git a/colour/colorimetry/correction.py b/colour/colorimetry/correction.py index 2ed325bbad..62833296f9 100644 --- a/colour/colorimetry/correction.py +++ b/colour/colorimetry/correction.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Spectral Bandpass Dependence Correction ======================================= -Defines objects to perform spectral bandpass dependence correction. +Defines the objects to perform spectral bandpass dependence correction. The following correction methods are available: @@ -25,40 +24,45 @@ MATLAB (2nd ed., p. 38). ISBN:978-0-470-66569-5 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.utilities import CaseInsensitiveMapping +from colour.colorimetry import SpectralDistribution +from colour.hints import Floating, Literal, Union +from colour.utilities import CaseInsensitiveMapping, validate_method -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'bandpass_correction_Stearns1988', 'BANDPASS_CORRECTION_METHODS', - 'bandpass_correction' + "bandpass_correction_Stearns1988", + "BANDPASS_CORRECTION_METHODS", + "bandpass_correction", ] -CONSTANT_ALPHA_STEARNS = 0.083 +CONSTANT_ALPHA_STEARNS: Floating = 0.083 -def bandpass_correction_Stearns1988(sd): +def bandpass_correction_Stearns1988( + sd: SpectralDistribution, +) -> SpectralDistribution: """ - Implements spectral bandpass dependence correction on given spectral + Implement spectral bandpass dependence correction on given spectral distribution using *Stearns and Stearns (1988)* method. Parameters ---------- - sd : SpectralDistribution + sd Spectral distribution. Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Spectral bandpass dependence corrected spectral distribution. References @@ -93,47 +97,50 @@ def bandpass_correction_Stearns1988(sd): """ values = np.copy(sd.values) - values[0] = (1 + CONSTANT_ALPHA_STEARNS - ) * values[0] - CONSTANT_ALPHA_STEARNS * values[1] - values[-1] = (1 + CONSTANT_ALPHA_STEARNS - ) * values[-1] - CONSTANT_ALPHA_STEARNS * values[-2] + values[0] = (1 + CONSTANT_ALPHA_STEARNS) * values[ + 0 + ] - CONSTANT_ALPHA_STEARNS * values[1] + values[-1] = (1 + CONSTANT_ALPHA_STEARNS) * values[ + -1 + ] - CONSTANT_ALPHA_STEARNS * values[-2] for i in range(1, len(values) - 1): - values[i] = (-CONSTANT_ALPHA_STEARNS * values[i - 1] + - (1 + 2 * CONSTANT_ALPHA_STEARNS) * values[i] - - CONSTANT_ALPHA_STEARNS * values[i + 1]) + values[i] = ( + -CONSTANT_ALPHA_STEARNS * values[i - 1] + + (1 + 2 * CONSTANT_ALPHA_STEARNS) * values[i] + - CONSTANT_ALPHA_STEARNS * values[i + 1] + ) sd.values = values return sd -BANDPASS_CORRECTION_METHODS = CaseInsensitiveMapping({ - 'Stearns 1988': bandpass_correction_Stearns1988 -}) +BANDPASS_CORRECTION_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + {"Stearns 1988": bandpass_correction_Stearns1988} +) BANDPASS_CORRECTION_METHODS.__doc__ = """ Supported spectral bandpass dependence correction methods. - -BANDPASS_CORRECTION_METHODS : CaseInsensitiveMapping - **{'Stearns 1988', }** """ -def bandpass_correction(sd, method='Stearns 1988'): +def bandpass_correction( + sd: SpectralDistribution, + method: Union[Literal["Stearns 1988"], str] = "Stearns 1988", +) -> SpectralDistribution: """ - Implements spectral bandpass dependence correction on given spectral + Implement spectral bandpass dependence correction on given spectral distribution using given method. Parameters ---------- - sd : SpectralDistribution + sd Spectral distribution. - method : unicode, optional - {'Stearns 1988', } + method Correction method. Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Spectral bandpass dependence corrected spectral distribution. References @@ -167,4 +174,6 @@ def bandpass_correction(sd, method='Stearns 1988'): extrapolator_kwargs={...}) """ - return BANDPASS_CORRECTION_METHODS.get(method)(sd) + method = validate_method(method, BANDPASS_CORRECTION_METHODS) + + return BANDPASS_CORRECTION_METHODS[method](sd) diff --git a/colour/colorimetry/datasets/__init__.py b/colour/colorimetry/datasets/__init__.py index c003d4365d..3f27422840 100644 --- a/colour/colorimetry/datasets/__init__.py +++ b/colour/colorimetry/datasets/__init__.py @@ -1,9 +1,9 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - -from .cmfs import (MSDS_CMFS, MSDS_CMFS_LMS, MSDS_CMFS_RGB, - MSDS_CMFS_STANDARD_OBSERVER) +from .cmfs import ( + MSDS_CMFS, + MSDS_CMFS_LMS, + MSDS_CMFS_RGB, + MSDS_CMFS_STANDARD_OBSERVER, +) from .illuminants import * # noqa from . import illuminants from .light_sources import * # noqa @@ -11,9 +11,15 @@ from .lefs import SDS_LEFS, SDS_LEFS_PHOTOPIC, SDS_LEFS_SCOTOPIC __all__ = [ - 'MSDS_CMFS', 'MSDS_CMFS_LMS', 'MSDS_CMFS_RGB', - 'MSDS_CMFS_STANDARD_OBSERVER' + "MSDS_CMFS", + "MSDS_CMFS_LMS", + "MSDS_CMFS_RGB", + "MSDS_CMFS_STANDARD_OBSERVER", ] __all__ += illuminants.__all__ __all__ += light_sources.__all__ -__all__ += ['SDS_LEFS', 'SDS_LEFS_PHOTOPIC', 'SDS_LEFS_SCOTOPIC'] +__all__ += [ + "SDS_LEFS", + "SDS_LEFS_PHOTOPIC", + "SDS_LEFS_SCOTOPIC", +] diff --git a/colour/colorimetry/datasets/cmfs.py b/colour/colorimetry/datasets/cmfs.py index d03365711e..9c8f28aaf4 100644 --- a/colour/colorimetry/datasets/cmfs.py +++ b/colour/colorimetry/datasets/cmfs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Multi-Spectral Distributions of the Colour Matching Functions ============================================================= @@ -62,29 +61,39 @@ http://www.lume.ufrgs.br/handle/10183/26950 """ -from __future__ import division, unicode_literals +from __future__ import annotations -from colour.colorimetry import (LMS_ConeFundamentals, - RGB_ColourMatchingFunctions, - XYZ_ColourMatchingFunctions) -from colour.utilities import CaseInsensitiveMapping +from functools import partial -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.colorimetry import ( + LMS_ConeFundamentals, + RGB_ColourMatchingFunctions, + XYZ_ColourMatchingFunctions, +) +from colour.hints import Dict +from colour.utilities import LazyCaseInsensitiveMapping + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'DATA_CMFS_LMS', 'MSDS_CMFS_LMS', 'DATA_CMFS_RGB', 'MSDS_CMFS_RGB', - 'DATA_CMFS_STANDARD_OBSERVER', 'MSDS_CMFS_STANDARD_OBSERVER', 'MSDS_CMFS' + "DATA_CMFS_LMS", + "MSDS_CMFS_LMS", + "DATA_CMFS_RGB", + "MSDS_CMFS_RGB", + "DATA_CMFS_STANDARD_OBSERVER", + "MSDS_CMFS_STANDARD_OBSERVER", + "MSDS_CMFS", ] # *S-cone* spectral sensitivity data wasn't measurable after 615 nm and has # been set to zero. -DATA_CMFS_LMS = { - 'Stockman & Sharpe 2 Degree Cone Fundamentals': { +DATA_CMFS_LMS: Dict = { + "Stockman & Sharpe 2 Degree Cone Fundamentals": { 390: (4.15003e-04, 3.68349e-04, 9.54729e-03), 391: (5.02650e-04, 4.48015e-04, 1.14794e-02), 392: (6.07367e-04, 5.43965e-04, 1.37986e-02), @@ -525,9 +534,9 @@ 827: (1.15888e-06, 1.12416e-07, 0.000000000), 828: (1.09348e-06, 1.06398e-07, 0.000000000), 829: (1.03203e-06, 1.00711e-07, 0.000000000), - 830: (9.74306e-07, 9.53411e-08, 0.000000000) + 830: (9.74306e-07, 9.53411e-08, 0.000000000), }, - 'Stockman & Sharpe 10 Degree Cone Fundamentals': { + "Stockman & Sharpe 10 Degree Cone Fundamentals": { 390: (4.07619e-04, 3.58227e-04, 6.14265e-03), 391: (4.97068e-04, 4.38660e-04, 7.44280e-03), 392: (6.04713e-04, 5.36230e-04, 9.01661e-03), @@ -968,9 +977,9 @@ 827: (1.03839e-06, 1.00093e-07, 0.000000000), 828: (9.79788e-07, 9.47349e-08, 0.000000000), 829: (9.24725e-07, 8.96718e-08, 0.000000000), - 830: (8.73008e-07, 8.48902e-08, 0.000000000) + 830: (8.73008e-07, 8.48902e-08, 0.000000000), }, - 'Smith & Pokorny 1975 Normal Trichromats': { + "Smith & Pokorny 1975 Normal Trichromats": { 380: (0.0000, 0.0000, 0.0000), 385: (0.0000, 0.0000, 0.0000), 390: (0.0000, 0.0000, 0.0000), @@ -1051,42 +1060,42 @@ 765: (0.0000, 0.0000, 0.0000), 770: (0.0000, 0.0000, 0.0000), 775: (0.0000, 0.0000, 0.0000), - 780: (0.0000, 0.0000, 0.0000) - } + 780: (0.0000, 0.0000, 0.0000), + }, } -MSDS_CMFS_LMS = CaseInsensitiveMapping({ - 'Stockman & Sharpe 2 Degree Cone Fundamentals': - LMS_ConeFundamentals( - DATA_CMFS_LMS['Stockman & Sharpe 2 Degree Cone Fundamentals'], - name='Stockman & Sharpe 2 Degree Cone Fundamentals', - strict_name='Stockman & Sharpe 2$^\\circ$ Cone Fundamentals'), - 'Stockman & Sharpe 10 Degree Cone Fundamentals': - LMS_ConeFundamentals( - DATA_CMFS_LMS['Stockman & Sharpe 10 Degree Cone Fundamentals'], - name='Stockman & Sharpe 10 Degree Cone Fundamentals', - strict_name='Stockman & Sharpe 10$^\\circ$ Cone Fundamentals'), - 'Smith & Pokorny 1975 Normal Trichromats': - LMS_ConeFundamentals( - DATA_CMFS_LMS['Smith & Pokorny 1975 Normal Trichromats'], - name='Smith & Pokorny 1975 Normal Trichromats', - strict_name='Smith & Pokorny (1975) Normal Trichromats') -}) +MSDS_CMFS_LMS: LazyCaseInsensitiveMapping = LazyCaseInsensitiveMapping( + { + "Stockman & Sharpe 2 Degree Cone Fundamentals": partial( + LMS_ConeFundamentals, + DATA_CMFS_LMS["Stockman & Sharpe 2 Degree Cone Fundamentals"], + name="Stockman & Sharpe 2 Degree Cone Fundamentals", + strict_name="Stockman & Sharpe 2$^\\circ$ Cone Fundamentals", + ), + "Stockman & Sharpe 10 Degree Cone Fundamentals": partial( + LMS_ConeFundamentals, + DATA_CMFS_LMS["Stockman & Sharpe 10 Degree Cone Fundamentals"], + name="Stockman & Sharpe 10 Degree Cone Fundamentals", + strict_name="Stockman & Sharpe 10$^\\circ$ Cone Fundamentals", + ), + "Smith & Pokorny 1975 Normal Trichromats": partial( + LMS_ConeFundamentals, + DATA_CMFS_LMS["Smith & Pokorny 1975 Normal Trichromats"], + name="Smith & Pokorny 1975 Normal Trichromats", + strict_name="Smith & Pokorny (1975) Normal Trichromats", + ), + } +) MSDS_CMFS_LMS.__doc__ = """ Multi-spectral distributions of the *LMS* colour matching functions. References ---------- :cite:`CVRLu`, :cite:`Machado2010a` - -MSDS_CMFS_LMS : CaseInsensitiveMapping - {'Stockman & Sharpe 2 Degree Cone Fundamentals', - 'Stockman & Sharpe 10 Degree Cone Fundamentals', - 'Smith & Pokorny 1975 Normal Trichromats'} """ -DATA_CMFS_RGB = { - 'Wright & Guild 1931 2 Degree RGB CMFs': { +DATA_CMFS_RGB: Dict = { + "Wright & Guild 1931 2 Degree RGB CMFs": { 380: (0.00003, -0.00001, 0.00117), 385: (0.00005, -0.00002, 0.00189), 390: (0.00010, -0.00004, 0.00359), @@ -1167,9 +1176,9 @@ 765: (0.00004, 0.00000, 0.00000), 770: (0.00003, 0.00000, 0.00000), 775: (0.00001, 0.00000, 0.00000), - 780: (0.00000, 0.00000, 0.00000) + 780: (0.00000, 0.00000, 0.00000), }, - 'Stiles & Burch 1955 2 Degree RGB CMFs': { + "Stiles & Burch 1955 2 Degree RGB CMFs": { 390: (1.83970e-003, -4.53930e-004, 1.21520e-002), 395: (4.61530e-003, -1.04640e-003, 3.11100e-002), 400: (9.62640e-003, -2.16890e-003, 6.23710e-002), @@ -1198,30 +1207,30 @@ 515: (-2.17250e-001, 8.08520e-001, 2.87810e-002), 520: (-1.47680e-001, 9.10760e-001, 1.33090e-002), 525: (-3.51840e-002, 9.84820e-001, 2.11700e-003), - 530: (1.06140e-001, 1.03390e+000, -4.15740e-003), - 535: (2.59810e-001, 1.05380e+000, -8.30320e-003), - 540: (4.19760e-001, 1.05120e+000, -1.21910e-002), - 545: (5.92590e-001, 1.04980e+000, -1.40390e-002), - 550: (7.90040e-001, 1.03680e+000, -1.46810e-002), - 555: (1.00780e+000, 9.98260e-001, -1.49470e-002), - 560: (1.22830e+000, 9.37830e-001, -1.46130e-002), - 565: (1.47270e+000, 8.80390e-001, -1.37820e-002), - 570: (1.74760e+000, 8.28350e-001, -1.26500e-002), - 575: (2.02140e+000, 7.46860e-001, -1.13560e-002), - 580: (2.27240e+000, 6.49300e-001, -9.93170e-003), - 585: (2.48960e+000, 5.63170e-001, -8.41480e-003), - 590: (2.67250e+000, 4.76750e-001, -7.02100e-003), - 595: (2.80930e+000, 3.84840e-001, -5.74370e-003), - 600: (2.87170e+000, 3.00690e-001, -4.27430e-003), - 605: (2.85250e+000, 2.28530e-001, -2.91320e-003), - 610: (2.76010e+000, 1.65750e-001, -2.26930e-003), - 615: (2.59890e+000, 1.13730e-001, -1.99660e-003), - 620: (2.37430e+000, 7.46820e-002, -1.50690e-003), - 625: (2.10540e+000, 4.65040e-002, -9.38220e-004), - 630: (1.81450e+000, 2.63330e-002, -5.53160e-004), - 635: (1.52470e+000, 1.27240e-002, -3.16680e-004), - 640: (1.25430e+000, 4.50330e-003, -1.43190e-004), - 645: (1.00760e+000, 9.66110e-005, -4.08310e-006), + 530: (1.06140e-001, 1.03390e000, -4.15740e-003), + 535: (2.59810e-001, 1.05380e000, -8.30320e-003), + 540: (4.19760e-001, 1.05120e000, -1.21910e-002), + 545: (5.92590e-001, 1.04980e000, -1.40390e-002), + 550: (7.90040e-001, 1.03680e000, -1.46810e-002), + 555: (1.00780e000, 9.98260e-001, -1.49470e-002), + 560: (1.22830e000, 9.37830e-001, -1.46130e-002), + 565: (1.47270e000, 8.80390e-001, -1.37820e-002), + 570: (1.74760e000, 8.28350e-001, -1.26500e-002), + 575: (2.02140e000, 7.46860e-001, -1.13560e-002), + 580: (2.27240e000, 6.49300e-001, -9.93170e-003), + 585: (2.48960e000, 5.63170e-001, -8.41480e-003), + 590: (2.67250e000, 4.76750e-001, -7.02100e-003), + 595: (2.80930e000, 3.84840e-001, -5.74370e-003), + 600: (2.87170e000, 3.00690e-001, -4.27430e-003), + 605: (2.85250e000, 2.28530e-001, -2.91320e-003), + 610: (2.76010e000, 1.65750e-001, -2.26930e-003), + 615: (2.59890e000, 1.13730e-001, -1.99660e-003), + 620: (2.37430e000, 7.46820e-002, -1.50690e-003), + 625: (2.10540e000, 4.65040e-002, -9.38220e-004), + 630: (1.81450e000, 2.63330e-002, -5.53160e-004), + 635: (1.52470e000, 1.27240e-002, -3.16680e-004), + 640: (1.25430e000, 4.50330e-003, -1.43190e-004), + 645: (1.00760e000, 9.66110e-005, -4.08310e-006), 650: (7.86420e-001, -1.96450e-003, 1.10810e-004), 655: (5.96590e-001, -2.63270e-003, 1.91750e-004), 660: (4.43200e-001, -2.62620e-003, 2.26560e-004), @@ -1238,9 +1247,9 @@ 715: (9.40920e-003, -8.57600e-005, 7.43510e-006), 720: (6.51770e-003, -5.76770e-005, 6.10570e-006), 725: (4.53770e-003, -3.90030e-005, 5.02770e-006), - 730: (3.17420e-003, -2.65110e-005, 4.12510e-006) + 730: (3.17420e-003, -2.65110e-005, 4.12510e-006), }, - 'Stiles & Burch 1959 10 Degree RGB CMFs': { + "Stiles & Burch 1959 10 Degree RGB CMFs": { 390: (1.5000e-03, -4.0000e-04, 6.2000e-03), 395: (3.8000e-03, -1.0000e-03, 1.6100e-02), 400: (8.9000e-03, -2.5000e-03, 4.0000e-02), @@ -1252,7 +1261,7 @@ 430: (7.4500e-02, -3.4900e-02, 7.6380e-01), 435: (5.6100e-02, -2.7600e-02, 8.7780e-01), 440: (3.2300e-02, -1.6900e-02, 9.7550e-01), - 445: (-4.4000e-03, 2.4000e-03, 1.0019e+00), + 445: (-4.4000e-03, 2.4000e-03, 1.0019e00), 450: (-4.7800e-02, 2.8300e-02, 9.9960e-01), 455: (-9.7000e-02, 6.3600e-02, 9.1390e-01), 460: (-1.5860e-01, 1.0820e-01, 8.2970e-01), @@ -1269,30 +1278,30 @@ 515: (-2.8450e-01, 8.7150e-01, 2.0000e-02), 520: (-1.8550e-01, 9.4770e-01, 9.5000e-03), 525: (-4.3500e-02, 9.9450e-01, 7.0000e-04), - 530: (1.2700e-01, 1.0203e+00, -4.3000e-03), - 535: (3.1290e-01, 1.0375e+00, -6.4000e-03), - 540: (5.3620e-01, 1.0517e+00, -8.2000e-03), - 545: (7.7220e-01, 1.0390e+00, -9.4000e-03), - 550: (1.0059e+00, 1.0029e+00, -9.7000e-03), - 555: (1.2710e+00, 9.6980e-01, -9.7000e-03), - 560: (1.5574e+00, 9.1620e-01, -9.3000e-03), - 565: (1.8465e+00, 8.5710e-01, -8.7000e-03), - 570: (2.1511e+00, 7.8230e-01, -8.0000e-03), - 575: (2.4250e+00, 6.9530e-01, -7.3000e-03), - 580: (2.6574e+00, 5.9660e-01, -6.3000e-03), - 585: (2.9151e+00, 5.0630e-01, -5.3700e-03), - 590: (3.0779e+00, 4.2030e-01, -4.4500e-03), - 595: (3.1613e+00, 3.3600e-01, -3.5700e-03), - 600: (3.1673e+00, 2.5910e-01, -2.7700e-03), - 605: (3.1048e+00, 1.9170e-01, -2.0800e-03), - 610: (2.9462e+00, 1.3670e-01, -1.5000e-03), - 615: (2.7194e+00, 9.3800e-02, -1.0300e-03), - 620: (2.4526e+00, 6.1100e-02, -6.8000e-04), - 625: (2.1700e+00, 3.7100e-02, -4.4200e-04), - 630: (1.8358e+00, 2.1500e-02, -2.7200e-04), - 635: (1.5179e+00, 1.1200e-02, -1.4100e-04), - 640: (1.2428e+00, 4.4000e-03, -5.4900e-05), - 645: (1.0070e+00, 7.8000e-05, -2.2000e-06), + 530: (1.2700e-01, 1.0203e00, -4.3000e-03), + 535: (3.1290e-01, 1.0375e00, -6.4000e-03), + 540: (5.3620e-01, 1.0517e00, -8.2000e-03), + 545: (7.7220e-01, 1.0390e00, -9.4000e-03), + 550: (1.0059e00, 1.0029e00, -9.7000e-03), + 555: (1.2710e00, 9.6980e-01, -9.7000e-03), + 560: (1.5574e00, 9.1620e-01, -9.3000e-03), + 565: (1.8465e00, 8.5710e-01, -8.7000e-03), + 570: (2.1511e00, 7.8230e-01, -8.0000e-03), + 575: (2.4250e00, 6.9530e-01, -7.3000e-03), + 580: (2.6574e00, 5.9660e-01, -6.3000e-03), + 585: (2.9151e00, 5.0630e-01, -5.3700e-03), + 590: (3.0779e00, 4.2030e-01, -4.4500e-03), + 595: (3.1613e00, 3.3600e-01, -3.5700e-03), + 600: (3.1673e00, 2.5910e-01, -2.7700e-03), + 605: (3.1048e00, 1.9170e-01, -2.0800e-03), + 610: (2.9462e00, 1.3670e-01, -1.5000e-03), + 615: (2.7194e00, 9.3800e-02, -1.0300e-03), + 620: (2.4526e00, 6.1100e-02, -6.8000e-04), + 625: (2.1700e00, 3.7100e-02, -4.4200e-04), + 630: (1.8358e00, 2.1500e-02, -2.7200e-04), + 635: (1.5179e00, 1.1200e-02, -1.4100e-04), + 640: (1.2428e00, 4.4000e-03, -5.4900e-05), + 645: (1.0070e00, 7.8000e-05, -2.2000e-06), 650: (7.8270e-01, -1.3680e-03, 2.3700e-05), 655: (5.9340e-01, -1.9880e-03, 2.8600e-05), 660: (4.4420e-01, -2.1680e-03, 2.6100e-05), @@ -1329,43 +1338,42 @@ 815: (1.1000e-05, -1.9600e-08, 2.5300e-10), 820: (8.1800e-06, -1.0900e-08, 1.5200e-10), 825: (6.0900e-06, -5.7000e-09, 8.6400e-11), - 830: (4.5500e-06, -2.7700e-09, 4.4200e-11) - } + 830: (4.5500e-06, -2.7700e-09, 4.4200e-11), + }, } -MSDS_CMFS_RGB = CaseInsensitiveMapping({ - 'Wright & Guild 1931 2 Degree RGB CMFs': - RGB_ColourMatchingFunctions( - DATA_CMFS_RGB['Wright & Guild 1931 2 Degree RGB CMFs'], - name='Wright & Guild 1931 2 Degree RGB CMFs', - strict_name='Wright & Guild (1931) 2$^\\circ$ RGB CMFs', +MSDS_CMFS_RGB: LazyCaseInsensitiveMapping = LazyCaseInsensitiveMapping( + { + "Wright & Guild 1931 2 Degree RGB CMFs": partial( + RGB_ColourMatchingFunctions, + DATA_CMFS_RGB["Wright & Guild 1931 2 Degree RGB CMFs"], + name="Wright & Guild 1931 2 Degree RGB CMFs", + strict_name="Wright & Guild (1931) 2$^\\circ$ RGB CMFs", ), - 'Stiles & Burch 1955 2 Degree RGB CMFs': - RGB_ColourMatchingFunctions( - DATA_CMFS_RGB['Stiles & Burch 1955 2 Degree RGB CMFs'], - name='Stiles & Burch 1955 2 Degree RGB CMFs', - strict_name='Stiles & Burch (1955) 2$^\\circ$ RGB CMFs'), - 'Stiles & Burch 1959 10 Degree RGB CMFs': - RGB_ColourMatchingFunctions( - DATA_CMFS_RGB['Stiles & Burch 1959 10 Degree RGB CMFs'], - name='Stiles & Burch 1959 10 Degree RGB CMFs', - strict_name='Stiles & Burch (1959) 10$^\\circ$ RGB CMFs') -}) + "Stiles & Burch 1955 2 Degree RGB CMFs": partial( + RGB_ColourMatchingFunctions, + DATA_CMFS_RGB["Stiles & Burch 1955 2 Degree RGB CMFs"], + name="Stiles & Burch 1955 2 Degree RGB CMFs", + strict_name="Stiles & Burch (1955) 2$^\\circ$ RGB CMFs", + ), + "Stiles & Burch 1959 10 Degree RGB CMFs": partial( + RGB_ColourMatchingFunctions, + DATA_CMFS_RGB["Stiles & Burch 1959 10 Degree RGB CMFs"], + name="Stiles & Burch 1959 10 Degree RGB CMFs", + strict_name="Stiles & Burch (1959) 10$^\\circ$ RGB CMFs", + ), + } +) MSDS_CMFS_RGB.__doc__ = """ Multi-spectral distributions of the *RGB* colour matching functions. References ---------- :cite:`Broadbent2009a`, :cite:`CVRLt`, :cite:`CVRLw` - -MSDS_CMFS_RGB : CaseInsensitiveMapping - **{'Wright & Guild 1931 2 Degree RGB CMFs', - 'Stiles & Burch 1955 2 Degree RGB CMFs', - 'Stiles & Burch 1959 10 Degree RGB CMFs'}** """ -DATA_CMFS_STANDARD_OBSERVER = { - 'CIE 1931 2 Degree Standard Observer': { +DATA_CMFS_STANDARD_OBSERVER: Dict = { + "CIE 1931 2 Degree Standard Observer": { 360: (0.000129900000, 0.000003917000, 0.000606100000), 361: (0.000145847000, 0.000004393581, 0.000680879200), 362: (0.000163802100, 0.000004929604, 0.000765145600), @@ -1836,9 +1844,9 @@ 827: (0.000001544022, 0.000000557575, 0.000000000000), 828: (0.000001439440, 0.000000519808, 0.000000000000), 829: (0.000001341977, 0.000000484612, 0.000000000000), - 830: (0.000001251141, 0.000000451810, 0.000000000000) + 830: (0.000001251141, 0.000000451810, 0.000000000000), }, - 'CIE 1964 10 Degree Standard Observer': { + "CIE 1964 10 Degree Standard Observer": { 360: (0.000000122200, 0.000000013398, 0.000000535027), 361: (0.000000185138, 0.000000020294, 0.000000810720), 362: (0.000000278830, 0.000000030560, 0.000001221200), @@ -2309,9 +2317,9 @@ 827: (0.000001851900, 0.000000749780, 0.000000000000), 828: (0.000001746500, 0.000000707440, 0.000000000000), 829: (0.000001647100, 0.000000667480, 0.000000000000), - 830: (0.000001553140, 0.000000629700, 0.000000000000) + 830: (0.000001553140, 0.000000629700, 0.000000000000), }, - 'CIE 2012 2 Degree Standard Observer': { + "CIE 2012 2 Degree Standard Observer": { 390: (3.769647e-03, 4.146161e-04, 1.847260e-02), 391: (4.532416e-03, 5.028333e-04, 2.221101e-02), 392: (5.446553e-03, 6.084991e-04, 2.669819e-02), @@ -2342,61 +2350,61 @@ 417: (1.687901e-01, 1.664093e-02, 8.655573e-01), 418: (1.808328e-01, 1.785302e-02, 9.295791e-01), 419: (1.925216e-01, 1.907018e-02, 9.921293e-01), - 420: (2.035729e-01, 2.027369e-02, 1.051821e+00), - 421: (2.137531e-01, 2.144805e-02, 1.107509e+00), - 422: (2.231348e-01, 2.260041e-02, 1.159527e+00), - 423: (2.319245e-01, 2.374789e-02, 1.208869e+00), - 424: (2.403892e-01, 2.491247e-02, 1.256834e+00), - 425: (2.488523e-01, 2.612106e-02, 1.305008e+00), - 426: (2.575896e-01, 2.739923e-02, 1.354758e+00), - 427: (2.664991e-01, 2.874993e-02, 1.405594e+00), - 428: (2.753532e-01, 3.016909e-02, 1.456414e+00), - 429: (2.838921e-01, 3.165145e-02, 1.505960e+00), - 430: (2.918246e-01, 3.319038e-02, 1.552826e+00), - 431: (2.989200e-01, 3.477912e-02, 1.595902e+00), - 432: (3.052993e-01, 3.641495e-02, 1.635768e+00), - 433: (3.112031e-01, 3.809569e-02, 1.673573e+00), - 434: (3.169047e-01, 3.981843e-02, 1.710604e+00), - 435: (3.227087e-01, 4.157940e-02, 1.748280e+00), - 436: (3.288194e-01, 4.337098e-02, 1.787504e+00), - 437: (3.349242e-01, 4.517180e-02, 1.826609e+00), - 438: (3.405452e-01, 4.695420e-02, 1.863108e+00), - 439: (3.451688e-01, 4.868718e-02, 1.894332e+00), - 440: (3.482554e-01, 5.033657e-02, 1.917479e+00), - 441: (3.494153e-01, 5.187611e-02, 1.930529e+00), - 442: (3.489075e-01, 5.332218e-02, 1.934819e+00), - 443: (3.471746e-01, 5.470603e-02, 1.932650e+00), - 444: (3.446705e-01, 5.606335e-02, 1.926395e+00), - 445: (3.418483e-01, 5.743393e-02, 1.918437e+00), - 446: (3.390240e-01, 5.885107e-02, 1.910430e+00), - 447: (3.359926e-01, 6.030809e-02, 1.901224e+00), - 448: (3.324276e-01, 6.178644e-02, 1.889000e+00), - 449: (3.280157e-01, 6.326570e-02, 1.871996e+00), - 450: (3.224637e-01, 6.472352e-02, 1.848545e+00), - 451: (3.156225e-01, 6.614749e-02, 1.817792e+00), - 452: (3.078201e-01, 6.757256e-02, 1.781627e+00), - 453: (2.994771e-01, 6.904928e-02, 1.742514e+00), - 454: (2.909776e-01, 7.063280e-02, 1.702749e+00), - 455: (2.826646e-01, 7.238339e-02, 1.664439e+00), - 456: (2.747962e-01, 7.435960e-02, 1.629207e+00), - 457: (2.674312e-01, 7.659383e-02, 1.597360e+00), - 458: (2.605847e-01, 7.911436e-02, 1.568896e+00), - 459: (2.542749e-01, 8.195345e-02, 1.543823e+00), - 460: (2.485254e-01, 8.514816e-02, 1.522157e+00), - 461: (2.433039e-01, 8.872657e-02, 1.503611e+00), - 462: (2.383414e-01, 9.266008e-02, 1.486673e+00), - 463: (2.333253e-01, 9.689723e-02, 1.469595e+00), - 464: (2.279619e-01, 1.013746e-01, 1.450709e+00), - 465: (2.219781e-01, 1.060145e-01, 1.428440e+00), - 466: (2.151735e-01, 1.107377e-01, 1.401587e+00), - 467: (2.075619e-01, 1.155111e-01, 1.370094e+00), - 468: (1.992183e-01, 1.203122e-01, 1.334220e+00), - 469: (1.902290e-01, 1.251161e-01, 1.294275e+00), - 470: (1.806905e-01, 1.298957e-01, 1.250610e+00), - 471: (1.707154e-01, 1.346299e-01, 1.203696e+00), - 472: (1.604471e-01, 1.393309e-01, 1.154316e+00), - 473: (1.500244e-01, 1.440235e-01, 1.103284e+00), - 474: (1.395705e-01, 1.487372e-01, 1.051347e+00), + 420: (2.035729e-01, 2.027369e-02, 1.051821e00), + 421: (2.137531e-01, 2.144805e-02, 1.107509e00), + 422: (2.231348e-01, 2.260041e-02, 1.159527e00), + 423: (2.319245e-01, 2.374789e-02, 1.208869e00), + 424: (2.403892e-01, 2.491247e-02, 1.256834e00), + 425: (2.488523e-01, 2.612106e-02, 1.305008e00), + 426: (2.575896e-01, 2.739923e-02, 1.354758e00), + 427: (2.664991e-01, 2.874993e-02, 1.405594e00), + 428: (2.753532e-01, 3.016909e-02, 1.456414e00), + 429: (2.838921e-01, 3.165145e-02, 1.505960e00), + 430: (2.918246e-01, 3.319038e-02, 1.552826e00), + 431: (2.989200e-01, 3.477912e-02, 1.595902e00), + 432: (3.052993e-01, 3.641495e-02, 1.635768e00), + 433: (3.112031e-01, 3.809569e-02, 1.673573e00), + 434: (3.169047e-01, 3.981843e-02, 1.710604e00), + 435: (3.227087e-01, 4.157940e-02, 1.748280e00), + 436: (3.288194e-01, 4.337098e-02, 1.787504e00), + 437: (3.349242e-01, 4.517180e-02, 1.826609e00), + 438: (3.405452e-01, 4.695420e-02, 1.863108e00), + 439: (3.451688e-01, 4.868718e-02, 1.894332e00), + 440: (3.482554e-01, 5.033657e-02, 1.917479e00), + 441: (3.494153e-01, 5.187611e-02, 1.930529e00), + 442: (3.489075e-01, 5.332218e-02, 1.934819e00), + 443: (3.471746e-01, 5.470603e-02, 1.932650e00), + 444: (3.446705e-01, 5.606335e-02, 1.926395e00), + 445: (3.418483e-01, 5.743393e-02, 1.918437e00), + 446: (3.390240e-01, 5.885107e-02, 1.910430e00), + 447: (3.359926e-01, 6.030809e-02, 1.901224e00), + 448: (3.324276e-01, 6.178644e-02, 1.889000e00), + 449: (3.280157e-01, 6.326570e-02, 1.871996e00), + 450: (3.224637e-01, 6.472352e-02, 1.848545e00), + 451: (3.156225e-01, 6.614749e-02, 1.817792e00), + 452: (3.078201e-01, 6.757256e-02, 1.781627e00), + 453: (2.994771e-01, 6.904928e-02, 1.742514e00), + 454: (2.909776e-01, 7.063280e-02, 1.702749e00), + 455: (2.826646e-01, 7.238339e-02, 1.664439e00), + 456: (2.747962e-01, 7.435960e-02, 1.629207e00), + 457: (2.674312e-01, 7.659383e-02, 1.597360e00), + 458: (2.605847e-01, 7.911436e-02, 1.568896e00), + 459: (2.542749e-01, 8.195345e-02, 1.543823e00), + 460: (2.485254e-01, 8.514816e-02, 1.522157e00), + 461: (2.433039e-01, 8.872657e-02, 1.503611e00), + 462: (2.383414e-01, 9.266008e-02, 1.486673e00), + 463: (2.333253e-01, 9.689723e-02, 1.469595e00), + 464: (2.279619e-01, 1.013746e-01, 1.450709e00), + 465: (2.219781e-01, 1.060145e-01, 1.428440e00), + 466: (2.151735e-01, 1.107377e-01, 1.401587e00), + 467: (2.075619e-01, 1.155111e-01, 1.370094e00), + 468: (1.992183e-01, 1.203122e-01, 1.334220e00), + 469: (1.902290e-01, 1.251161e-01, 1.294275e00), + 470: (1.806905e-01, 1.298957e-01, 1.250610e00), + 471: (1.707154e-01, 1.346299e-01, 1.203696e00), + 472: (1.604471e-01, 1.393309e-01, 1.154316e00), + 473: (1.500244e-01, 1.440235e-01, 1.103284e00), + 474: (1.395705e-01, 1.487372e-01, 1.051347e00), 475: (1.291920e-01, 1.535066e-01, 9.991789e-01), 476: (1.189859e-01, 1.583644e-01, 9.473958e-01), 477: (1.090615e-01, 1.633199e-01, 8.966222e-01), @@ -2505,256 +2513,256 @@ 580: (9.638388e-01, 8.963613e-01, 2.117772e-04), 581: (9.812596e-01, 8.883069e-01, 1.929758e-04), 582: (9.992953e-01, 8.808462e-01, 1.759024e-04), - 583: (1.017343e+00, 8.736445e-01, 1.603947e-04), - 584: (1.034790e+00, 8.663755e-01, 1.463059e-04), - 585: (1.051011e+00, 8.587203e-01, 1.335031e-04), - 586: (1.065522e+00, 8.504295e-01, 1.218660e-04), - 587: (1.078421e+00, 8.415047e-01, 1.112857e-04), - 588: (1.089944e+00, 8.320109e-01, 1.016634e-04), - 589: (1.100320e+00, 8.220154e-01, 9.291003e-05), - 590: (1.109767e+00, 8.115868e-01, 8.494468e-05), - 591: (1.118438e+00, 8.007874e-01, 7.769425e-05), - 592: (1.126266e+00, 7.896515e-01, 7.109247e-05), - 593: (1.133138e+00, 7.782053e-01, 6.507936e-05), - 594: (1.138952e+00, 7.664733e-01, 5.960061e-05), - 595: (1.143620e+00, 7.544785e-01, 5.460706e-05), - 596: (1.147095e+00, 7.422473e-01, 5.005417e-05), - 597: (1.149464e+00, 7.298229e-01, 4.590157e-05), - 598: (1.150838e+00, 7.172525e-01, 4.211268e-05), - 599: (1.151326e+00, 7.045818e-01, 3.865437e-05), - 600: (1.151033e+00, 6.918553e-01, 3.549661e-05), - 601: (1.150002e+00, 6.791009e-01, 3.261220e-05), - 602: (1.148061e+00, 6.662846e-01, 2.997643e-05), - 603: (1.144998e+00, 6.533595e-01, 2.756693e-05), - 604: (1.140622e+00, 6.402807e-01, 2.536339e-05), - 605: (1.134757e+00, 6.270066e-01, 2.334738e-05), - 606: (1.127298e+00, 6.135148e-01, 2.150221e-05), - 607: (1.118342e+00, 5.998494e-01, 1.981268e-05), - 608: (1.108033e+00, 5.860682e-01, 1.826500e-05), - 609: (1.096515e+00, 5.722261e-01, 1.684667e-05), - 610: (1.083928e+00, 5.583746e-01, 1.554631e-05), - 611: (1.070387e+00, 5.445535e-01, 1.435360e-05), - 612: (1.055934e+00, 5.307673e-01, 1.325915e-05), - 613: (1.040592e+00, 5.170130e-01, 1.225443e-05), - 614: (1.024385e+00, 5.032889e-01, 1.133169e-05), - 615: (1.007344e+00, 4.895950e-01, 1.048387e-05), - 616: (9.895268e-01, 4.759442e-01, 0.000000e+00), - 617: (9.711213e-01, 4.623958e-01, 0.000000e+00), - 618: (9.523257e-01, 4.490154e-01, 0.000000e+00), - 619: (9.333248e-01, 4.358622e-01, 0.000000e+00), - 620: (9.142877e-01, 4.229897e-01, 0.000000e+00), - 621: (8.952798e-01, 4.104152e-01, 0.000000e+00), - 622: (8.760157e-01, 3.980356e-01, 0.000000e+00), - 623: (8.561607e-01, 3.857300e-01, 0.000000e+00), - 624: (8.354235e-01, 3.733907e-01, 0.000000e+00), - 625: (8.135565e-01, 3.609245e-01, 0.000000e+00), - 626: (7.904565e-01, 3.482860e-01, 0.000000e+00), - 627: (7.664364e-01, 3.355702e-01, 0.000000e+00), - 628: (7.418777e-01, 3.228963e-01, 0.000000e+00), - 629: (7.171219e-01, 3.103704e-01, 0.000000e+00), - 630: (6.924717e-01, 2.980865e-01, 0.000000e+00), - 631: (6.681600e-01, 2.861160e-01, 0.000000e+00), - 632: (6.442697e-01, 2.744822e-01, 0.000000e+00), - 633: (6.208450e-01, 2.631953e-01, 0.000000e+00), - 634: (5.979243e-01, 2.522628e-01, 0.000000e+00), - 635: (5.755410e-01, 2.416902e-01, 0.000000e+00), - 636: (5.537296e-01, 2.314809e-01, 0.000000e+00), - 637: (5.325412e-01, 2.216378e-01, 0.000000e+00), - 638: (5.120218e-01, 2.121622e-01, 0.000000e+00), - 639: (4.922070e-01, 2.030542e-01, 0.000000e+00), - 640: (4.731224e-01, 1.943124e-01, 0.000000e+00), - 641: (4.547417e-01, 1.859227e-01, 0.000000e+00), - 642: (4.368719e-01, 1.778274e-01, 0.000000e+00), - 643: (4.193121e-01, 1.699654e-01, 0.000000e+00), - 644: (4.018980e-01, 1.622841e-01, 0.000000e+00), - 645: (3.844986e-01, 1.547397e-01, 0.000000e+00), - 646: (3.670592e-01, 1.473081e-01, 0.000000e+00), - 647: (3.497167e-01, 1.400169e-01, 0.000000e+00), - 648: (3.326305e-01, 1.329013e-01, 0.000000e+00), - 649: (3.159341e-01, 1.259913e-01, 0.000000e+00), - 650: (2.997374e-01, 1.193120e-01, 0.000000e+00), - 651: (2.841189e-01, 1.128820e-01, 0.000000e+00), - 652: (2.691053e-01, 1.067113e-01, 0.000000e+00), - 653: (2.547077e-01, 1.008052e-01, 0.000000e+00), - 654: (2.409319e-01, 9.516653e-02, 0.000000e+00), - 655: (2.277792e-01, 8.979594e-02, 0.000000e+00), - 656: (2.152431e-01, 8.469044e-02, 0.000000e+00), - 657: (2.033010e-01, 7.984009e-02, 0.000000e+00), - 658: (1.919276e-01, 7.523372e-02, 0.000000e+00), - 659: (1.810987e-01, 7.086061e-02, 0.000000e+00), - 660: (1.707914e-01, 6.671045e-02, 0.000000e+00), - 661: (1.609842e-01, 6.277360e-02, 0.000000e+00), - 662: (1.516577e-01, 5.904179e-02, 0.000000e+00), - 663: (1.427936e-01, 5.550703e-02, 0.000000e+00), - 664: (1.343737e-01, 5.216139e-02, 0.000000e+00), - 665: (1.263808e-01, 4.899699e-02, 0.000000e+00), - 666: (1.187979e-01, 4.600578e-02, 0.000000e+00), - 667: (1.116088e-01, 4.317885e-02, 0.000000e+00), - 668: (1.047975e-01, 4.050755e-02, 0.000000e+00), - 669: (9.834835e-02, 3.798376e-02, 0.000000e+00), - 670: (9.224597e-02, 3.559982e-02, 0.000000e+00), - 671: (8.647506e-02, 3.334856e-02, 0.000000e+00), - 672: (8.101986e-02, 3.122332e-02, 0.000000e+00), - 673: (7.586514e-02, 2.921780e-02, 0.000000e+00), - 674: (7.099633e-02, 2.732601e-02, 0.000000e+00), - 675: (6.639960e-02, 2.554223e-02, 0.000000e+00), - 676: (6.206225e-02, 2.386121e-02, 0.000000e+00), - 677: (5.797409e-02, 2.227859e-02, 0.000000e+00), - 678: (5.412533e-02, 2.079020e-02, 0.000000e+00), - 679: (5.050600e-02, 1.939185e-02, 0.000000e+00), - 680: (4.710606e-02, 1.807939e-02, 0.000000e+00), - 681: (4.391411e-02, 1.684817e-02, 0.000000e+00), - 682: (4.091411e-02, 1.569188e-02, 0.000000e+00), - 683: (3.809067e-02, 1.460446e-02, 0.000000e+00), - 684: (3.543034e-02, 1.358062e-02, 0.000000e+00), - 685: (3.292138e-02, 1.261573e-02, 0.000000e+00), - 686: (3.055672e-02, 1.170696e-02, 0.000000e+00), - 687: (2.834146e-02, 1.085608e-02, 0.000000e+00), - 688: (2.628033e-02, 1.006476e-02, 0.000000e+00), - 689: (2.437465e-02, 9.333376e-03, 0.000000e+00), - 690: (2.262306e-02, 8.661284e-03, 0.000000e+00), - 691: (2.101935e-02, 8.046048e-03, 0.000000e+00), - 692: (1.954647e-02, 7.481130e-03, 0.000000e+00), - 693: (1.818727e-02, 6.959987e-03, 0.000000e+00), - 694: (1.692727e-02, 6.477070e-03, 0.000000e+00), - 695: (1.575417e-02, 6.027677e-03, 0.000000e+00), - 696: (1.465854e-02, 5.608169e-03, 0.000000e+00), - 697: (1.363571e-02, 5.216691e-03, 0.000000e+00), - 698: (1.268205e-02, 4.851785e-03, 0.000000e+00), - 699: (1.179394e-02, 4.512008e-03, 0.000000e+00), - 700: (1.096778e-02, 4.195941e-03, 0.000000e+00), - 701: (1.019964e-02, 3.902057e-03, 0.000000e+00), - 702: (9.484317e-03, 3.628371e-03, 0.000000e+00), - 703: (8.816851e-03, 3.373005e-03, 0.000000e+00), - 704: (8.192921e-03, 3.134315e-03, 0.000000e+00), - 705: (7.608750e-03, 2.910864e-03, 0.000000e+00), - 706: (7.061391e-03, 2.701528e-03, 0.000000e+00), - 707: (6.549509e-03, 2.505796e-03, 0.000000e+00), - 708: (6.071970e-03, 2.323231e-03, 0.000000e+00), - 709: (5.627476e-03, 2.153333e-03, 0.000000e+00), - 710: (5.214608e-03, 1.995557e-03, 0.000000e+00), - 711: (4.831848e-03, 1.849316e-03, 0.000000e+00), - 712: (4.477579e-03, 1.713976e-03, 0.000000e+00), - 713: (4.150166e-03, 1.588899e-03, 0.000000e+00), - 714: (3.847988e-03, 1.473453e-03, 0.000000e+00), - 715: (3.569452e-03, 1.367022e-03, 0.000000e+00), - 716: (3.312857e-03, 1.268954e-03, 0.000000e+00), - 717: (3.076022e-03, 1.178421e-03, 0.000000e+00), - 718: (2.856894e-03, 1.094644e-03, 0.000000e+00), - 719: (2.653681e-03, 1.016943e-03, 0.000000e+00), - 720: (2.464821e-03, 9.447269e-04, 0.000000e+00), - 721: (2.289060e-03, 8.775171e-04, 0.000000e+00), - 722: (2.125694e-03, 8.150438e-04, 0.000000e+00), - 723: (1.974121e-03, 7.570755e-04, 0.000000e+00), - 724: (1.833723e-03, 7.033755e-04, 0.000000e+00), - 725: (1.703876e-03, 6.537050e-04, 0.000000e+00), - 726: (1.583904e-03, 6.078048e-04, 0.000000e+00), - 727: (1.472939e-03, 5.653435e-04, 0.000000e+00), - 728: (1.370151e-03, 5.260046e-04, 0.000000e+00), - 729: (1.274803e-03, 4.895061e-04, 0.000000e+00), - 730: (1.186238e-03, 4.555970e-04, 0.000000e+00), - 731: (1.103871e-03, 4.240548e-04, 0.000000e+00), - 732: (1.027194e-03, 3.946860e-04, 0.000000e+00), - 733: (9.557493e-04, 3.673178e-04, 0.000000e+00), - 734: (8.891262e-04, 3.417941e-04, 0.000000e+00), - 735: (8.269535e-04, 3.179738e-04, 0.000000e+00), - 736: (7.689351e-04, 2.957441e-04, 0.000000e+00), - 737: (7.149425e-04, 2.750558e-04, 0.000000e+00), - 738: (6.648590e-04, 2.558640e-04, 0.000000e+00), - 739: (6.185421e-04, 2.381142e-04, 0.000000e+00), - 740: (5.758303e-04, 2.217445e-04, 0.000000e+00), - 741: (5.365046e-04, 2.066711e-04, 0.000000e+00), - 742: (5.001842e-04, 1.927474e-04, 0.000000e+00), - 743: (4.665005e-04, 1.798315e-04, 0.000000e+00), - 744: (4.351386e-04, 1.678023e-04, 0.000000e+00), - 745: (4.058303e-04, 1.565566e-04, 0.000000e+00), - 746: (3.783733e-04, 1.460168e-04, 0.000000e+00), - 747: (3.526892e-04, 1.361535e-04, 0.000000e+00), - 748: (3.287199e-04, 1.269451e-04, 0.000000e+00), - 749: (3.063998e-04, 1.183671e-04, 0.000000e+00), - 750: (2.856577e-04, 1.103928e-04, 0.000000e+00), - 751: (2.664108e-04, 1.029908e-04, 0.000000e+00), - 752: (2.485462e-04, 9.611836e-05, 0.000000e+00), - 753: (2.319529e-04, 8.973323e-05, 0.000000e+00), - 754: (2.165300e-04, 8.379694e-05, 0.000000e+00), - 755: (2.021853e-04, 7.827442e-05, 0.000000e+00), - 756: (1.888338e-04, 7.313312e-05, 0.000000e+00), - 757: (1.763935e-04, 6.834142e-05, 0.000000e+00), - 758: (1.647895e-04, 6.387035e-05, 0.000000e+00), - 759: (1.539542e-04, 5.969389e-05, 0.000000e+00), - 760: (1.438270e-04, 5.578862e-05, 0.000000e+00), - 761: (1.343572e-04, 5.213509e-05, 0.000000e+00), - 762: (1.255141e-04, 4.872179e-05, 0.000000e+00), - 763: (1.172706e-04, 4.553845e-05, 0.000000e+00), - 764: (1.095983e-04, 4.257443e-05, 0.000000e+00), - 765: (1.024685e-04, 3.981884e-05, 0.000000e+00), - 766: (9.584715e-05, 3.725877e-05, 0.000000e+00), - 767: (8.968316e-05, 3.487467e-05, 0.000000e+00), - 768: (8.392734e-05, 3.264765e-05, 0.000000e+00), - 769: (7.853708e-05, 3.056140e-05, 0.000000e+00), - 770: (7.347551e-05, 2.860175e-05, 0.000000e+00), - 771: (6.871576e-05, 2.675841e-05, 0.000000e+00), - 772: (6.425257e-05, 2.502943e-05, 0.000000e+00), - 773: (6.008292e-05, 2.341373e-05, 0.000000e+00), - 774: (5.620098e-05, 2.190914e-05, 0.000000e+00), - 775: (5.259870e-05, 2.051259e-05, 0.000000e+00), - 776: (4.926279e-05, 1.921902e-05, 0.000000e+00), - 777: (4.616623e-05, 1.801796e-05, 0.000000e+00), - 778: (4.328212e-05, 1.689899e-05, 0.000000e+00), - 779: (4.058715e-05, 1.585309e-05, 0.000000e+00), - 780: (3.806114e-05, 1.487243e-05, 0.000000e+00), - 781: (3.568818e-05, 1.395085e-05, 0.000000e+00), - 782: (3.346023e-05, 1.308528e-05, 0.000000e+00), - 783: (3.137090e-05, 1.227327e-05, 0.000000e+00), - 784: (2.941371e-05, 1.151233e-05, 0.000000e+00), - 785: (2.758222e-05, 1.080001e-05, 0.000000e+00), - 786: (2.586951e-05, 1.013364e-05, 0.000000e+00), - 787: (2.426701e-05, 9.509919e-06, 0.000000e+00), - 788: (2.276639e-05, 8.925630e-06, 0.000000e+00), - 789: (2.136009e-05, 8.377852e-06, 0.000000e+00), - 790: (2.004122e-05, 7.863920e-06, 0.000000e+00), - 791: (1.880380e-05, 7.381539e-06, 0.000000e+00), - 792: (1.764358e-05, 6.929096e-06, 0.000000e+00), - 793: (1.655671e-05, 6.505136e-06, 0.000000e+00), - 794: (1.553939e-05, 6.108221e-06, 0.000000e+00), - 795: (1.458792e-05, 5.736935e-06, 0.000000e+00), - 796: (1.369853e-05, 5.389831e-06, 0.000000e+00), - 797: (1.286705e-05, 5.065269e-06, 0.000000e+00), - 798: (1.208947e-05, 4.761667e-06, 0.000000e+00), - 799: (1.136207e-05, 4.477561e-06, 0.000000e+00), - 800: (1.068141e-05, 4.211597e-06, 0.000000e+00), - 801: (1.004411e-05, 3.962457e-06, 0.000000e+00), - 802: (9.446399e-06, 3.728674e-06, 0.000000e+00), - 803: (8.884754e-06, 3.508881e-06, 0.000000e+00), - 804: (8.356050e-06, 3.301868e-06, 0.000000e+00), - 805: (7.857521e-06, 3.106561e-06, 0.000000e+00), - 806: (7.386996e-06, 2.922119e-06, 0.000000e+00), - 807: (6.943576e-06, 2.748208e-06, 0.000000e+00), - 808: (6.526548e-06, 2.584560e-06, 0.000000e+00), - 809: (6.135087e-06, 2.430867e-06, 0.000000e+00), - 810: (5.768284e-06, 2.286786e-06, 0.000000e+00), - 811: (5.425069e-06, 2.151905e-06, 0.000000e+00), - 812: (5.103974e-06, 2.025656e-06, 0.000000e+00), - 813: (4.803525e-06, 1.907464e-06, 0.000000e+00), - 814: (4.522350e-06, 1.796794e-06, 0.000000e+00), - 815: (4.259166e-06, 1.693147e-06, 0.000000e+00), - 816: (4.012715e-06, 1.596032e-06, 0.000000e+00), - 817: (3.781597e-06, 1.504903e-06, 0.000000e+00), - 818: (3.564496e-06, 1.419245e-06, 0.000000e+00), - 819: (3.360236e-06, 1.338600e-06, 0.000000e+00), - 820: (3.167765e-06, 1.262556e-06, 0.000000e+00), - 821: (2.986206e-06, 1.190771e-06, 0.000000e+00), - 822: (2.814999e-06, 1.123031e-06, 0.000000e+00), - 823: (2.653663e-06, 1.059151e-06, 0.000000e+00), - 824: (2.501725e-06, 9.989507e-07, 0.000000e+00), - 825: (2.358723e-06, 9.422514e-07, 0.000000e+00), - 826: (2.224206e-06, 8.888804e-07, 0.000000e+00), - 827: (2.097737e-06, 8.386690e-07, 0.000000e+00), - 828: (1.978894e-06, 7.914539e-07, 0.000000e+00), - 829: (1.867268e-06, 7.470770e-07, 0.000000e+00), - 830: (1.762465e-06, 7.053860e-07, 0.000000e+00) + 583: (1.017343e00, 8.736445e-01, 1.603947e-04), + 584: (1.034790e00, 8.663755e-01, 1.463059e-04), + 585: (1.051011e00, 8.587203e-01, 1.335031e-04), + 586: (1.065522e00, 8.504295e-01, 1.218660e-04), + 587: (1.078421e00, 8.415047e-01, 1.112857e-04), + 588: (1.089944e00, 8.320109e-01, 1.016634e-04), + 589: (1.100320e00, 8.220154e-01, 9.291003e-05), + 590: (1.109767e00, 8.115868e-01, 8.494468e-05), + 591: (1.118438e00, 8.007874e-01, 7.769425e-05), + 592: (1.126266e00, 7.896515e-01, 7.109247e-05), + 593: (1.133138e00, 7.782053e-01, 6.507936e-05), + 594: (1.138952e00, 7.664733e-01, 5.960061e-05), + 595: (1.143620e00, 7.544785e-01, 5.460706e-05), + 596: (1.147095e00, 7.422473e-01, 5.005417e-05), + 597: (1.149464e00, 7.298229e-01, 4.590157e-05), + 598: (1.150838e00, 7.172525e-01, 4.211268e-05), + 599: (1.151326e00, 7.045818e-01, 3.865437e-05), + 600: (1.151033e00, 6.918553e-01, 3.549661e-05), + 601: (1.150002e00, 6.791009e-01, 3.261220e-05), + 602: (1.148061e00, 6.662846e-01, 2.997643e-05), + 603: (1.144998e00, 6.533595e-01, 2.756693e-05), + 604: (1.140622e00, 6.402807e-01, 2.536339e-05), + 605: (1.134757e00, 6.270066e-01, 2.334738e-05), + 606: (1.127298e00, 6.135148e-01, 2.150221e-05), + 607: (1.118342e00, 5.998494e-01, 1.981268e-05), + 608: (1.108033e00, 5.860682e-01, 1.826500e-05), + 609: (1.096515e00, 5.722261e-01, 1.684667e-05), + 610: (1.083928e00, 5.583746e-01, 1.554631e-05), + 611: (1.070387e00, 5.445535e-01, 1.435360e-05), + 612: (1.055934e00, 5.307673e-01, 1.325915e-05), + 613: (1.040592e00, 5.170130e-01, 1.225443e-05), + 614: (1.024385e00, 5.032889e-01, 1.133169e-05), + 615: (1.007344e00, 4.895950e-01, 1.048387e-05), + 616: (9.895268e-01, 4.759442e-01, 0.000000e00), + 617: (9.711213e-01, 4.623958e-01, 0.000000e00), + 618: (9.523257e-01, 4.490154e-01, 0.000000e00), + 619: (9.333248e-01, 4.358622e-01, 0.000000e00), + 620: (9.142877e-01, 4.229897e-01, 0.000000e00), + 621: (8.952798e-01, 4.104152e-01, 0.000000e00), + 622: (8.760157e-01, 3.980356e-01, 0.000000e00), + 623: (8.561607e-01, 3.857300e-01, 0.000000e00), + 624: (8.354235e-01, 3.733907e-01, 0.000000e00), + 625: (8.135565e-01, 3.609245e-01, 0.000000e00), + 626: (7.904565e-01, 3.482860e-01, 0.000000e00), + 627: (7.664364e-01, 3.355702e-01, 0.000000e00), + 628: (7.418777e-01, 3.228963e-01, 0.000000e00), + 629: (7.171219e-01, 3.103704e-01, 0.000000e00), + 630: (6.924717e-01, 2.980865e-01, 0.000000e00), + 631: (6.681600e-01, 2.861160e-01, 0.000000e00), + 632: (6.442697e-01, 2.744822e-01, 0.000000e00), + 633: (6.208450e-01, 2.631953e-01, 0.000000e00), + 634: (5.979243e-01, 2.522628e-01, 0.000000e00), + 635: (5.755410e-01, 2.416902e-01, 0.000000e00), + 636: (5.537296e-01, 2.314809e-01, 0.000000e00), + 637: (5.325412e-01, 2.216378e-01, 0.000000e00), + 638: (5.120218e-01, 2.121622e-01, 0.000000e00), + 639: (4.922070e-01, 2.030542e-01, 0.000000e00), + 640: (4.731224e-01, 1.943124e-01, 0.000000e00), + 641: (4.547417e-01, 1.859227e-01, 0.000000e00), + 642: (4.368719e-01, 1.778274e-01, 0.000000e00), + 643: (4.193121e-01, 1.699654e-01, 0.000000e00), + 644: (4.018980e-01, 1.622841e-01, 0.000000e00), + 645: (3.844986e-01, 1.547397e-01, 0.000000e00), + 646: (3.670592e-01, 1.473081e-01, 0.000000e00), + 647: (3.497167e-01, 1.400169e-01, 0.000000e00), + 648: (3.326305e-01, 1.329013e-01, 0.000000e00), + 649: (3.159341e-01, 1.259913e-01, 0.000000e00), + 650: (2.997374e-01, 1.193120e-01, 0.000000e00), + 651: (2.841189e-01, 1.128820e-01, 0.000000e00), + 652: (2.691053e-01, 1.067113e-01, 0.000000e00), + 653: (2.547077e-01, 1.008052e-01, 0.000000e00), + 654: (2.409319e-01, 9.516653e-02, 0.000000e00), + 655: (2.277792e-01, 8.979594e-02, 0.000000e00), + 656: (2.152431e-01, 8.469044e-02, 0.000000e00), + 657: (2.033010e-01, 7.984009e-02, 0.000000e00), + 658: (1.919276e-01, 7.523372e-02, 0.000000e00), + 659: (1.810987e-01, 7.086061e-02, 0.000000e00), + 660: (1.707914e-01, 6.671045e-02, 0.000000e00), + 661: (1.609842e-01, 6.277360e-02, 0.000000e00), + 662: (1.516577e-01, 5.904179e-02, 0.000000e00), + 663: (1.427936e-01, 5.550703e-02, 0.000000e00), + 664: (1.343737e-01, 5.216139e-02, 0.000000e00), + 665: (1.263808e-01, 4.899699e-02, 0.000000e00), + 666: (1.187979e-01, 4.600578e-02, 0.000000e00), + 667: (1.116088e-01, 4.317885e-02, 0.000000e00), + 668: (1.047975e-01, 4.050755e-02, 0.000000e00), + 669: (9.834835e-02, 3.798376e-02, 0.000000e00), + 670: (9.224597e-02, 3.559982e-02, 0.000000e00), + 671: (8.647506e-02, 3.334856e-02, 0.000000e00), + 672: (8.101986e-02, 3.122332e-02, 0.000000e00), + 673: (7.586514e-02, 2.921780e-02, 0.000000e00), + 674: (7.099633e-02, 2.732601e-02, 0.000000e00), + 675: (6.639960e-02, 2.554223e-02, 0.000000e00), + 676: (6.206225e-02, 2.386121e-02, 0.000000e00), + 677: (5.797409e-02, 2.227859e-02, 0.000000e00), + 678: (5.412533e-02, 2.079020e-02, 0.000000e00), + 679: (5.050600e-02, 1.939185e-02, 0.000000e00), + 680: (4.710606e-02, 1.807939e-02, 0.000000e00), + 681: (4.391411e-02, 1.684817e-02, 0.000000e00), + 682: (4.091411e-02, 1.569188e-02, 0.000000e00), + 683: (3.809067e-02, 1.460446e-02, 0.000000e00), + 684: (3.543034e-02, 1.358062e-02, 0.000000e00), + 685: (3.292138e-02, 1.261573e-02, 0.000000e00), + 686: (3.055672e-02, 1.170696e-02, 0.000000e00), + 687: (2.834146e-02, 1.085608e-02, 0.000000e00), + 688: (2.628033e-02, 1.006476e-02, 0.000000e00), + 689: (2.437465e-02, 9.333376e-03, 0.000000e00), + 690: (2.262306e-02, 8.661284e-03, 0.000000e00), + 691: (2.101935e-02, 8.046048e-03, 0.000000e00), + 692: (1.954647e-02, 7.481130e-03, 0.000000e00), + 693: (1.818727e-02, 6.959987e-03, 0.000000e00), + 694: (1.692727e-02, 6.477070e-03, 0.000000e00), + 695: (1.575417e-02, 6.027677e-03, 0.000000e00), + 696: (1.465854e-02, 5.608169e-03, 0.000000e00), + 697: (1.363571e-02, 5.216691e-03, 0.000000e00), + 698: (1.268205e-02, 4.851785e-03, 0.000000e00), + 699: (1.179394e-02, 4.512008e-03, 0.000000e00), + 700: (1.096778e-02, 4.195941e-03, 0.000000e00), + 701: (1.019964e-02, 3.902057e-03, 0.000000e00), + 702: (9.484317e-03, 3.628371e-03, 0.000000e00), + 703: (8.816851e-03, 3.373005e-03, 0.000000e00), + 704: (8.192921e-03, 3.134315e-03, 0.000000e00), + 705: (7.608750e-03, 2.910864e-03, 0.000000e00), + 706: (7.061391e-03, 2.701528e-03, 0.000000e00), + 707: (6.549509e-03, 2.505796e-03, 0.000000e00), + 708: (6.071970e-03, 2.323231e-03, 0.000000e00), + 709: (5.627476e-03, 2.153333e-03, 0.000000e00), + 710: (5.214608e-03, 1.995557e-03, 0.000000e00), + 711: (4.831848e-03, 1.849316e-03, 0.000000e00), + 712: (4.477579e-03, 1.713976e-03, 0.000000e00), + 713: (4.150166e-03, 1.588899e-03, 0.000000e00), + 714: (3.847988e-03, 1.473453e-03, 0.000000e00), + 715: (3.569452e-03, 1.367022e-03, 0.000000e00), + 716: (3.312857e-03, 1.268954e-03, 0.000000e00), + 717: (3.076022e-03, 1.178421e-03, 0.000000e00), + 718: (2.856894e-03, 1.094644e-03, 0.000000e00), + 719: (2.653681e-03, 1.016943e-03, 0.000000e00), + 720: (2.464821e-03, 9.447269e-04, 0.000000e00), + 721: (2.289060e-03, 8.775171e-04, 0.000000e00), + 722: (2.125694e-03, 8.150438e-04, 0.000000e00), + 723: (1.974121e-03, 7.570755e-04, 0.000000e00), + 724: (1.833723e-03, 7.033755e-04, 0.000000e00), + 725: (1.703876e-03, 6.537050e-04, 0.000000e00), + 726: (1.583904e-03, 6.078048e-04, 0.000000e00), + 727: (1.472939e-03, 5.653435e-04, 0.000000e00), + 728: (1.370151e-03, 5.260046e-04, 0.000000e00), + 729: (1.274803e-03, 4.895061e-04, 0.000000e00), + 730: (1.186238e-03, 4.555970e-04, 0.000000e00), + 731: (1.103871e-03, 4.240548e-04, 0.000000e00), + 732: (1.027194e-03, 3.946860e-04, 0.000000e00), + 733: (9.557493e-04, 3.673178e-04, 0.000000e00), + 734: (8.891262e-04, 3.417941e-04, 0.000000e00), + 735: (8.269535e-04, 3.179738e-04, 0.000000e00), + 736: (7.689351e-04, 2.957441e-04, 0.000000e00), + 737: (7.149425e-04, 2.750558e-04, 0.000000e00), + 738: (6.648590e-04, 2.558640e-04, 0.000000e00), + 739: (6.185421e-04, 2.381142e-04, 0.000000e00), + 740: (5.758303e-04, 2.217445e-04, 0.000000e00), + 741: (5.365046e-04, 2.066711e-04, 0.000000e00), + 742: (5.001842e-04, 1.927474e-04, 0.000000e00), + 743: (4.665005e-04, 1.798315e-04, 0.000000e00), + 744: (4.351386e-04, 1.678023e-04, 0.000000e00), + 745: (4.058303e-04, 1.565566e-04, 0.000000e00), + 746: (3.783733e-04, 1.460168e-04, 0.000000e00), + 747: (3.526892e-04, 1.361535e-04, 0.000000e00), + 748: (3.287199e-04, 1.269451e-04, 0.000000e00), + 749: (3.063998e-04, 1.183671e-04, 0.000000e00), + 750: (2.856577e-04, 1.103928e-04, 0.000000e00), + 751: (2.664108e-04, 1.029908e-04, 0.000000e00), + 752: (2.485462e-04, 9.611836e-05, 0.000000e00), + 753: (2.319529e-04, 8.973323e-05, 0.000000e00), + 754: (2.165300e-04, 8.379694e-05, 0.000000e00), + 755: (2.021853e-04, 7.827442e-05, 0.000000e00), + 756: (1.888338e-04, 7.313312e-05, 0.000000e00), + 757: (1.763935e-04, 6.834142e-05, 0.000000e00), + 758: (1.647895e-04, 6.387035e-05, 0.000000e00), + 759: (1.539542e-04, 5.969389e-05, 0.000000e00), + 760: (1.438270e-04, 5.578862e-05, 0.000000e00), + 761: (1.343572e-04, 5.213509e-05, 0.000000e00), + 762: (1.255141e-04, 4.872179e-05, 0.000000e00), + 763: (1.172706e-04, 4.553845e-05, 0.000000e00), + 764: (1.095983e-04, 4.257443e-05, 0.000000e00), + 765: (1.024685e-04, 3.981884e-05, 0.000000e00), + 766: (9.584715e-05, 3.725877e-05, 0.000000e00), + 767: (8.968316e-05, 3.487467e-05, 0.000000e00), + 768: (8.392734e-05, 3.264765e-05, 0.000000e00), + 769: (7.853708e-05, 3.056140e-05, 0.000000e00), + 770: (7.347551e-05, 2.860175e-05, 0.000000e00), + 771: (6.871576e-05, 2.675841e-05, 0.000000e00), + 772: (6.425257e-05, 2.502943e-05, 0.000000e00), + 773: (6.008292e-05, 2.341373e-05, 0.000000e00), + 774: (5.620098e-05, 2.190914e-05, 0.000000e00), + 775: (5.259870e-05, 2.051259e-05, 0.000000e00), + 776: (4.926279e-05, 1.921902e-05, 0.000000e00), + 777: (4.616623e-05, 1.801796e-05, 0.000000e00), + 778: (4.328212e-05, 1.689899e-05, 0.000000e00), + 779: (4.058715e-05, 1.585309e-05, 0.000000e00), + 780: (3.806114e-05, 1.487243e-05, 0.000000e00), + 781: (3.568818e-05, 1.395085e-05, 0.000000e00), + 782: (3.346023e-05, 1.308528e-05, 0.000000e00), + 783: (3.137090e-05, 1.227327e-05, 0.000000e00), + 784: (2.941371e-05, 1.151233e-05, 0.000000e00), + 785: (2.758222e-05, 1.080001e-05, 0.000000e00), + 786: (2.586951e-05, 1.013364e-05, 0.000000e00), + 787: (2.426701e-05, 9.509919e-06, 0.000000e00), + 788: (2.276639e-05, 8.925630e-06, 0.000000e00), + 789: (2.136009e-05, 8.377852e-06, 0.000000e00), + 790: (2.004122e-05, 7.863920e-06, 0.000000e00), + 791: (1.880380e-05, 7.381539e-06, 0.000000e00), + 792: (1.764358e-05, 6.929096e-06, 0.000000e00), + 793: (1.655671e-05, 6.505136e-06, 0.000000e00), + 794: (1.553939e-05, 6.108221e-06, 0.000000e00), + 795: (1.458792e-05, 5.736935e-06, 0.000000e00), + 796: (1.369853e-05, 5.389831e-06, 0.000000e00), + 797: (1.286705e-05, 5.065269e-06, 0.000000e00), + 798: (1.208947e-05, 4.761667e-06, 0.000000e00), + 799: (1.136207e-05, 4.477561e-06, 0.000000e00), + 800: (1.068141e-05, 4.211597e-06, 0.000000e00), + 801: (1.004411e-05, 3.962457e-06, 0.000000e00), + 802: (9.446399e-06, 3.728674e-06, 0.000000e00), + 803: (8.884754e-06, 3.508881e-06, 0.000000e00), + 804: (8.356050e-06, 3.301868e-06, 0.000000e00), + 805: (7.857521e-06, 3.106561e-06, 0.000000e00), + 806: (7.386996e-06, 2.922119e-06, 0.000000e00), + 807: (6.943576e-06, 2.748208e-06, 0.000000e00), + 808: (6.526548e-06, 2.584560e-06, 0.000000e00), + 809: (6.135087e-06, 2.430867e-06, 0.000000e00), + 810: (5.768284e-06, 2.286786e-06, 0.000000e00), + 811: (5.425069e-06, 2.151905e-06, 0.000000e00), + 812: (5.103974e-06, 2.025656e-06, 0.000000e00), + 813: (4.803525e-06, 1.907464e-06, 0.000000e00), + 814: (4.522350e-06, 1.796794e-06, 0.000000e00), + 815: (4.259166e-06, 1.693147e-06, 0.000000e00), + 816: (4.012715e-06, 1.596032e-06, 0.000000e00), + 817: (3.781597e-06, 1.504903e-06, 0.000000e00), + 818: (3.564496e-06, 1.419245e-06, 0.000000e00), + 819: (3.360236e-06, 1.338600e-06, 0.000000e00), + 820: (3.167765e-06, 1.262556e-06, 0.000000e00), + 821: (2.986206e-06, 1.190771e-06, 0.000000e00), + 822: (2.814999e-06, 1.123031e-06, 0.000000e00), + 823: (2.653663e-06, 1.059151e-06, 0.000000e00), + 824: (2.501725e-06, 9.989507e-07, 0.000000e00), + 825: (2.358723e-06, 9.422514e-07, 0.000000e00), + 826: (2.224206e-06, 8.888804e-07, 0.000000e00), + 827: (2.097737e-06, 8.386690e-07, 0.000000e00), + 828: (1.978894e-06, 7.914539e-07, 0.000000e00), + 829: (1.867268e-06, 7.470770e-07, 0.000000e00), + 830: (1.762465e-06, 7.053860e-07, 0.000000e00), }, - 'CIE 2012 10 Degree Standard Observer': { + "CIE 2012 10 Degree Standard Observer": { 390: (2.952420e-03, 4.076779e-04, 1.318752e-02), 391: (3.577275e-03, 4.977769e-04, 1.597879e-02), 392: (4.332146e-03, 6.064754e-04, 1.935758e-02), @@ -2786,61 +2794,61 @@ 418: (1.802197e-01, 2.212935e-02, 8.563391e-01), 419: (1.941448e-01, 2.392985e-02, 9.253017e-01), 420: (2.077647e-01, 2.576133e-02, 9.933444e-01), - 421: (2.207911e-01, 2.760156e-02, 1.059178e+00), - 422: (2.332355e-01, 2.945513e-02, 1.122832e+00), - 423: (2.452462e-01, 3.133884e-02, 1.184947e+00), - 424: (2.570397e-01, 3.327575e-02, 1.246476e+00), - 425: (2.688989e-01, 3.529554e-02, 1.308674e+00), - 426: (2.810677e-01, 3.742705e-02, 1.372628e+00), - 427: (2.933967e-01, 3.967137e-02, 1.437661e+00), - 428: (3.055933e-01, 4.201998e-02, 1.502449e+00), - 429: (3.173165e-01, 4.446166e-02, 1.565456e+00), - 430: (3.281798e-01, 4.698226e-02, 1.624940e+00), - 431: (3.378678e-01, 4.956742e-02, 1.679488e+00), - 432: (3.465097e-01, 5.221219e-02, 1.729668e+00), - 433: (3.543953e-01, 5.491387e-02, 1.776755e+00), - 434: (3.618655e-01, 5.766919e-02, 1.822228e+00), - 435: (3.693084e-01, 6.047429e-02, 1.867751e+00), - 436: (3.770107e-01, 6.332195e-02, 1.914504e+00), - 437: (3.846850e-01, 6.619271e-02, 1.961055e+00), - 438: (3.918591e-01, 6.906185e-02, 2.005136e+00), - 439: (3.980192e-01, 7.190190e-02, 2.044296e+00), - 440: (4.026189e-01, 7.468288e-02, 2.075946e+00), - 441: (4.052637e-01, 7.738452e-02, 2.098231e+00), - 442: (4.062482e-01, 8.003601e-02, 2.112591e+00), - 443: (4.060660e-01, 8.268524e-02, 2.121427e+00), - 444: (4.052283e-01, 8.538745e-02, 2.127239e+00), - 445: (4.042529e-01, 8.820537e-02, 2.132574e+00), - 446: (4.034808e-01, 9.118925e-02, 2.139093e+00), - 447: (4.025362e-01, 9.431041e-02, 2.144815e+00), - 448: (4.008675e-01, 9.751346e-02, 2.146832e+00), - 449: (3.979327e-01, 1.007349e-01, 2.142250e+00), - 450: (3.932139e-01, 1.039030e-01, 2.128264e+00), - 451: (3.864108e-01, 1.069639e-01, 2.103205e+00), - 452: (3.779513e-01, 1.099676e-01, 2.069388e+00), - 453: (3.684176e-01, 1.129992e-01, 2.030030e+00), - 454: (3.583473e-01, 1.161541e-01, 1.988178e+00), - 455: (3.482214e-01, 1.195389e-01, 1.946651e+00), - 456: (3.383830e-01, 1.232503e-01, 1.907521e+00), - 457: (3.288309e-01, 1.273047e-01, 1.870689e+00), - 458: (3.194977e-01, 1.316964e-01, 1.835578e+00), - 459: (3.103345e-01, 1.364178e-01, 1.801657e+00), - 460: (3.013112e-01, 1.414586e-01, 1.768440e+00), - 461: (2.923754e-01, 1.468003e-01, 1.735338e+00), - 462: (2.833273e-01, 1.524002e-01, 1.701254e+00), - 463: (2.739463e-01, 1.582021e-01, 1.665053e+00), - 464: (2.640352e-01, 1.641400e-01, 1.625712e+00), - 465: (2.534221e-01, 1.701373e-01, 1.582342e+00), - 466: (2.420135e-01, 1.761233e-01, 1.534439e+00), - 467: (2.299346e-01, 1.820896e-01, 1.482544e+00), - 468: (2.173617e-01, 1.880463e-01, 1.427438e+00), - 469: (2.044672e-01, 1.940065e-01, 1.369876e+00), - 470: (1.914176e-01, 1.999859e-01, 1.310576e+00), - 471: (1.783672e-01, 2.060054e-01, 1.250226e+00), - 472: (1.654407e-01, 2.120981e-01, 1.189511e+00), - 473: (1.527391e-01, 2.183041e-01, 1.129050e+00), - 474: (1.403439e-01, 2.246686e-01, 1.069379e+00), - 475: (1.283167e-01, 2.312426e-01, 1.010952e+00), + 421: (2.207911e-01, 2.760156e-02, 1.059178e00), + 422: (2.332355e-01, 2.945513e-02, 1.122832e00), + 423: (2.452462e-01, 3.133884e-02, 1.184947e00), + 424: (2.570397e-01, 3.327575e-02, 1.246476e00), + 425: (2.688989e-01, 3.529554e-02, 1.308674e00), + 426: (2.810677e-01, 3.742705e-02, 1.372628e00), + 427: (2.933967e-01, 3.967137e-02, 1.437661e00), + 428: (3.055933e-01, 4.201998e-02, 1.502449e00), + 429: (3.173165e-01, 4.446166e-02, 1.565456e00), + 430: (3.281798e-01, 4.698226e-02, 1.624940e00), + 431: (3.378678e-01, 4.956742e-02, 1.679488e00), + 432: (3.465097e-01, 5.221219e-02, 1.729668e00), + 433: (3.543953e-01, 5.491387e-02, 1.776755e00), + 434: (3.618655e-01, 5.766919e-02, 1.822228e00), + 435: (3.693084e-01, 6.047429e-02, 1.867751e00), + 436: (3.770107e-01, 6.332195e-02, 1.914504e00), + 437: (3.846850e-01, 6.619271e-02, 1.961055e00), + 438: (3.918591e-01, 6.906185e-02, 2.005136e00), + 439: (3.980192e-01, 7.190190e-02, 2.044296e00), + 440: (4.026189e-01, 7.468288e-02, 2.075946e00), + 441: (4.052637e-01, 7.738452e-02, 2.098231e00), + 442: (4.062482e-01, 8.003601e-02, 2.112591e00), + 443: (4.060660e-01, 8.268524e-02, 2.121427e00), + 444: (4.052283e-01, 8.538745e-02, 2.127239e00), + 445: (4.042529e-01, 8.820537e-02, 2.132574e00), + 446: (4.034808e-01, 9.118925e-02, 2.139093e00), + 447: (4.025362e-01, 9.431041e-02, 2.144815e00), + 448: (4.008675e-01, 9.751346e-02, 2.146832e00), + 449: (3.979327e-01, 1.007349e-01, 2.142250e00), + 450: (3.932139e-01, 1.039030e-01, 2.128264e00), + 451: (3.864108e-01, 1.069639e-01, 2.103205e00), + 452: (3.779513e-01, 1.099676e-01, 2.069388e00), + 453: (3.684176e-01, 1.129992e-01, 2.030030e00), + 454: (3.583473e-01, 1.161541e-01, 1.988178e00), + 455: (3.482214e-01, 1.195389e-01, 1.946651e00), + 456: (3.383830e-01, 1.232503e-01, 1.907521e00), + 457: (3.288309e-01, 1.273047e-01, 1.870689e00), + 458: (3.194977e-01, 1.316964e-01, 1.835578e00), + 459: (3.103345e-01, 1.364178e-01, 1.801657e00), + 460: (3.013112e-01, 1.414586e-01, 1.768440e00), + 461: (2.923754e-01, 1.468003e-01, 1.735338e00), + 462: (2.833273e-01, 1.524002e-01, 1.701254e00), + 463: (2.739463e-01, 1.582021e-01, 1.665053e00), + 464: (2.640352e-01, 1.641400e-01, 1.625712e00), + 465: (2.534221e-01, 1.701373e-01, 1.582342e00), + 466: (2.420135e-01, 1.761233e-01, 1.534439e00), + 467: (2.299346e-01, 1.820896e-01, 1.482544e00), + 468: (2.173617e-01, 1.880463e-01, 1.427438e00), + 469: (2.044672e-01, 1.940065e-01, 1.369876e00), + 470: (1.914176e-01, 1.999859e-01, 1.310576e00), + 471: (1.783672e-01, 2.060054e-01, 1.250226e00), + 472: (1.654407e-01, 2.120981e-01, 1.189511e00), + 473: (1.527391e-01, 2.183041e-01, 1.129050e00), + 474: (1.403439e-01, 2.246686e-01, 1.069379e00), + 475: (1.283167e-01, 2.312426e-01, 1.010952e00), 476: (1.167124e-01, 2.380741e-01, 9.541809e-01), 477: (1.056121e-01, 2.451798e-01, 8.995253e-01), 478: (9.508569e-02, 2.525682e-01, 8.473720e-01), @@ -2943,286 +2951,296 @@ 575: (9.721304e-01, 9.286495e-01, 2.076129e-04), 576: (9.849237e-01, 9.187953e-01, 1.888948e-04), 577: (9.970067e-01, 9.083014e-01, 1.719127e-04), - 578: (1.008907e+00, 8.976352e-01, 1.565030e-04), - 579: (1.021163e+00, 8.872401e-01, 1.425177e-04), - 580: (1.034327e+00, 8.775360e-01, 1.298230e-04), - 581: (1.048753e+00, 8.687920e-01, 1.182974e-04), - 582: (1.063937e+00, 8.607474e-01, 1.078310e-04), - 583: (1.079166e+00, 8.530233e-01, 9.832455e-05), - 584: (1.093723e+00, 8.452535e-01, 8.968787e-05), - 585: (1.106886e+00, 8.370838e-01, 8.183954e-05), - 586: (1.118106e+00, 8.282409e-01, 7.470582e-05), - 587: (1.127493e+00, 8.187320e-01, 6.821991e-05), - 588: (1.135317e+00, 8.086352e-01, 6.232132e-05), - 589: (1.141838e+00, 7.980296e-01, 5.695534e-05), - 590: (1.147304e+00, 7.869950e-01, 5.207245e-05), - 591: (1.151897e+00, 7.756040e-01, 4.762781e-05), - 592: (1.155582e+00, 7.638996e-01, 4.358082e-05), - 593: (1.158284e+00, 7.519157e-01, 3.989468e-05), - 594: (1.159934e+00, 7.396832e-01, 3.653612e-05), - 595: (1.160477e+00, 7.272309e-01, 3.347499e-05), - 596: (1.159890e+00, 7.145878e-01, 3.068400e-05), - 597: (1.158259e+00, 7.017926e-01, 2.813839e-05), - 598: (1.155692e+00, 6.888866e-01, 2.581574e-05), - 599: (1.152293e+00, 6.759103e-01, 2.369574e-05), - 600: (1.148163e+00, 6.629035e-01, 2.175998e-05), - 601: (1.143345e+00, 6.498911e-01, 1.999179e-05), - 602: (1.137685e+00, 6.368410e-01, 1.837603e-05), - 603: (1.130993e+00, 6.237092e-01, 1.689896e-05), - 604: (1.123097e+00, 6.104541e-01, 1.554815e-05), - 605: (1.113846e+00, 5.970375e-01, 1.431231e-05), - 606: (1.103152e+00, 5.834395e-01, 1.318119e-05), - 607: (1.091121e+00, 5.697044e-01, 1.214548e-05), - 608: (1.077902e+00, 5.558892e-01, 1.119673e-05), - 609: (1.063644e+00, 5.420475e-01, 1.032727e-05), - 610: (1.048485e+00, 5.282296e-01, 9.530130e-06), - 611: (1.032546e+00, 5.144746e-01, 8.798979e-06), - 612: (1.015870e+00, 5.007881e-01, 8.128065e-06), + 578: (1.008907e00, 8.976352e-01, 1.565030e-04), + 579: (1.021163e00, 8.872401e-01, 1.425177e-04), + 580: (1.034327e00, 8.775360e-01, 1.298230e-04), + 581: (1.048753e00, 8.687920e-01, 1.182974e-04), + 582: (1.063937e00, 8.607474e-01, 1.078310e-04), + 583: (1.079166e00, 8.530233e-01, 9.832455e-05), + 584: (1.093723e00, 8.452535e-01, 8.968787e-05), + 585: (1.106886e00, 8.370838e-01, 8.183954e-05), + 586: (1.118106e00, 8.282409e-01, 7.470582e-05), + 587: (1.127493e00, 8.187320e-01, 6.821991e-05), + 588: (1.135317e00, 8.086352e-01, 6.232132e-05), + 589: (1.141838e00, 7.980296e-01, 5.695534e-05), + 590: (1.147304e00, 7.869950e-01, 5.207245e-05), + 591: (1.151897e00, 7.756040e-01, 4.762781e-05), + 592: (1.155582e00, 7.638996e-01, 4.358082e-05), + 593: (1.158284e00, 7.519157e-01, 3.989468e-05), + 594: (1.159934e00, 7.396832e-01, 3.653612e-05), + 595: (1.160477e00, 7.272309e-01, 3.347499e-05), + 596: (1.159890e00, 7.145878e-01, 3.068400e-05), + 597: (1.158259e00, 7.017926e-01, 2.813839e-05), + 598: (1.155692e00, 6.888866e-01, 2.581574e-05), + 599: (1.152293e00, 6.759103e-01, 2.369574e-05), + 600: (1.148163e00, 6.629035e-01, 2.175998e-05), + 601: (1.143345e00, 6.498911e-01, 1.999179e-05), + 602: (1.137685e00, 6.368410e-01, 1.837603e-05), + 603: (1.130993e00, 6.237092e-01, 1.689896e-05), + 604: (1.123097e00, 6.104541e-01, 1.554815e-05), + 605: (1.113846e00, 5.970375e-01, 1.431231e-05), + 606: (1.103152e00, 5.834395e-01, 1.318119e-05), + 607: (1.091121e00, 5.697044e-01, 1.214548e-05), + 608: (1.077902e00, 5.558892e-01, 1.119673e-05), + 609: (1.063644e00, 5.420475e-01, 1.032727e-05), + 610: (1.048485e00, 5.282296e-01, 9.530130e-06), + 611: (1.032546e00, 5.144746e-01, 8.798979e-06), + 612: (1.015870e00, 5.007881e-01, 8.128065e-06), 613: (9.984859e-01, 4.871687e-01, 7.512160e-06), 614: (9.804227e-01, 4.736160e-01, 6.946506e-06), 615: (9.617111e-01, 4.601308e-01, 6.426776e-06), - 616: (9.424119e-01, 4.467260e-01, 0.000000e+00), - 617: (9.227049e-01, 4.334589e-01, 0.000000e+00), - 618: (9.027804e-01, 4.203919e-01, 0.000000e+00), - 619: (8.828123e-01, 4.075810e-01, 0.000000e+00), - 620: (8.629581e-01, 3.950755e-01, 0.000000e+00), - 621: (8.432731e-01, 3.828894e-01, 0.000000e+00), - 622: (8.234742e-01, 3.709190e-01, 0.000000e+00), - 623: (8.032342e-01, 3.590447e-01, 0.000000e+00), - 624: (7.822715e-01, 3.471615e-01, 0.000000e+00), - 625: (7.603498e-01, 3.351794e-01, 0.000000e+00), - 626: (7.373739e-01, 3.230562e-01, 0.000000e+00), - 627: (7.136470e-01, 3.108859e-01, 0.000000e+00), - 628: (6.895336e-01, 2.987840e-01, 0.000000e+00), - 629: (6.653567e-01, 2.868527e-01, 0.000000e+00), - 630: (6.413984e-01, 2.751807e-01, 0.000000e+00), - 631: (6.178723e-01, 2.638343e-01, 0.000000e+00), - 632: (5.948484e-01, 2.528330e-01, 0.000000e+00), - 633: (5.723600e-01, 2.421835e-01, 0.000000e+00), - 634: (5.504353e-01, 2.318904e-01, 0.000000e+00), - 635: (5.290979e-01, 2.219564e-01, 0.000000e+00), - 636: (5.083728e-01, 2.123826e-01, 0.000000e+00), - 637: (4.883006e-01, 2.031698e-01, 0.000000e+00), - 638: (4.689171e-01, 1.943179e-01, 0.000000e+00), - 639: (4.502486e-01, 1.858250e-01, 0.000000e+00), - 640: (4.323126e-01, 1.776882e-01, 0.000000e+00), - 641: (4.150790e-01, 1.698926e-01, 0.000000e+00), - 642: (3.983657e-01, 1.623822e-01, 0.000000e+00), - 643: (3.819846e-01, 1.550986e-01, 0.000000e+00), - 644: (3.657821e-01, 1.479918e-01, 0.000000e+00), - 645: (3.496358e-01, 1.410203e-01, 0.000000e+00), - 646: (3.334937e-01, 1.341614e-01, 0.000000e+00), - 647: (3.174776e-01, 1.274401e-01, 0.000000e+00), - 648: (3.017298e-01, 1.208887e-01, 0.000000e+00), - 649: (2.863684e-01, 1.145345e-01, 0.000000e+00), - 650: (2.714900e-01, 1.083996e-01, 0.000000e+00), - 651: (2.571632e-01, 1.025007e-01, 0.000000e+00), - 652: (2.434102e-01, 9.684588e-02, 0.000000e+00), - 653: (2.302389e-01, 9.143944e-02, 0.000000e+00), - 654: (2.176527e-01, 8.628318e-02, 0.000000e+00), - 655: (2.056507e-01, 8.137687e-02, 0.000000e+00), - 656: (1.942251e-01, 7.671708e-02, 0.000000e+00), - 657: (1.833530e-01, 7.229404e-02, 0.000000e+00), - 658: (1.730097e-01, 6.809696e-02, 0.000000e+00), - 659: (1.631716e-01, 6.411549e-02, 0.000000e+00), - 660: (1.538163e-01, 6.033976e-02, 0.000000e+00), - 661: (1.449230e-01, 5.676054e-02, 0.000000e+00), - 662: (1.364729e-01, 5.336992e-02, 0.000000e+00), - 663: (1.284483e-01, 5.016027e-02, 0.000000e+00), - 664: (1.208320e-01, 4.712405e-02, 0.000000e+00), - 665: (1.136072e-01, 4.425383e-02, 0.000000e+00), - 666: (1.067579e-01, 4.154205e-02, 0.000000e+00), - 667: (1.002685e-01, 3.898042e-02, 0.000000e+00), - 668: (9.412394e-02, 3.656091e-02, 0.000000e+00), - 669: (8.830929e-02, 3.427597e-02, 0.000000e+00), - 670: (8.281010e-02, 3.211852e-02, 0.000000e+00), - 671: (7.761208e-02, 3.008192e-02, 0.000000e+00), - 672: (7.270064e-02, 2.816001e-02, 0.000000e+00), - 673: (6.806167e-02, 2.634698e-02, 0.000000e+00), - 674: (6.368176e-02, 2.463731e-02, 0.000000e+00), - 675: (5.954815e-02, 2.302574e-02, 0.000000e+00), - 676: (5.564917e-02, 2.150743e-02, 0.000000e+00), - 677: (5.197543e-02, 2.007838e-02, 0.000000e+00), - 678: (4.851788e-02, 1.873474e-02, 0.000000e+00), - 679: (4.526737e-02, 1.747269e-02, 0.000000e+00), - 680: (4.221473e-02, 1.628841e-02, 0.000000e+00), - 681: (3.934954e-02, 1.517767e-02, 0.000000e+00), - 682: (3.665730e-02, 1.413473e-02, 0.000000e+00), - 683: (3.412407e-02, 1.315408e-02, 0.000000e+00), - 684: (3.173768e-02, 1.223092e-02, 0.000000e+00), - 685: (2.948752e-02, 1.136106e-02, 0.000000e+00), - 686: (2.736717e-02, 1.054190e-02, 0.000000e+00), - 687: (2.538113e-02, 9.775050e-03, 0.000000e+00), - 688: (2.353356e-02, 9.061962e-03, 0.000000e+00), - 689: (2.182558e-02, 8.402962e-03, 0.000000e+00), - 690: (2.025590e-02, 7.797457e-03, 0.000000e+00), - 691: (1.881892e-02, 7.243230e-03, 0.000000e+00), - 692: (1.749930e-02, 6.734381e-03, 0.000000e+00), - 693: (1.628167e-02, 6.265001e-03, 0.000000e+00), - 694: (1.515301e-02, 5.830085e-03, 0.000000e+00), - 695: (1.410230e-02, 5.425391e-03, 0.000000e+00), - 696: (1.312106e-02, 5.047634e-03, 0.000000e+00), - 697: (1.220509e-02, 4.695140e-03, 0.000000e+00), - 698: (1.135114e-02, 4.366592e-03, 0.000000e+00), - 699: (1.055593e-02, 4.060685e-03, 0.000000e+00), - 700: (9.816228e-03, 3.776140e-03, 0.000000e+00), - 701: (9.128517e-03, 3.511578e-03, 0.000000e+00), - 702: (8.488116e-03, 3.265211e-03, 0.000000e+00), - 703: (7.890589e-03, 3.035344e-03, 0.000000e+00), - 704: (7.332061e-03, 2.820496e-03, 0.000000e+00), - 705: (6.809147e-03, 2.619372e-03, 0.000000e+00), - 706: (6.319204e-03, 2.430960e-03, 0.000000e+00), - 707: (5.861036e-03, 2.254796e-03, 0.000000e+00), - 708: (5.433624e-03, 2.090489e-03, 0.000000e+00), - 709: (5.035802e-03, 1.937586e-03, 0.000000e+00), - 710: (4.666298e-03, 1.795595e-03, 0.000000e+00), - 711: (4.323750e-03, 1.663989e-03, 0.000000e+00), - 712: (4.006709e-03, 1.542195e-03, 0.000000e+00), - 713: (3.713708e-03, 1.429639e-03, 0.000000e+00), - 714: (3.443294e-03, 1.325752e-03, 0.000000e+00), - 715: (3.194041e-03, 1.229980e-03, 0.000000e+00), - 716: (2.964424e-03, 1.141734e-03, 0.000000e+00), - 717: (2.752492e-03, 1.060269e-03, 0.000000e+00), - 718: (2.556406e-03, 9.848854e-04, 0.000000e+00), - 719: (2.374564e-03, 9.149703e-04, 0.000000e+00), - 720: (2.205568e-03, 8.499903e-04, 0.000000e+00), - 721: (2.048294e-03, 7.895158e-04, 0.000000e+00), - 722: (1.902113e-03, 7.333038e-04, 0.000000e+00), - 723: (1.766485e-03, 6.811458e-04, 0.000000e+00), - 724: (1.640857e-03, 6.328287e-04, 0.000000e+00), - 725: (1.524672e-03, 5.881375e-04, 0.000000e+00), - 726: (1.417322e-03, 5.468389e-04, 0.000000e+00), - 727: (1.318031e-03, 5.086349e-04, 0.000000e+00), - 728: (1.226059e-03, 4.732403e-04, 0.000000e+00), - 729: (1.140743e-03, 4.404016e-04, 0.000000e+00), - 730: (1.061495e-03, 4.098928e-04, 0.000000e+00), - 731: (9.877949e-04, 3.815137e-04, 0.000000e+00), - 732: (9.191847e-04, 3.550902e-04, 0.000000e+00), - 733: (8.552568e-04, 3.304668e-04, 0.000000e+00), - 734: (7.956433e-04, 3.075030e-04, 0.000000e+00), - 735: (7.400120e-04, 2.860718e-04, 0.000000e+00), - 736: (6.880980e-04, 2.660718e-04, 0.000000e+00), - 737: (6.397864e-04, 2.474586e-04, 0.000000e+00), - 738: (5.949726e-04, 2.301919e-04, 0.000000e+00), - 739: (5.535291e-04, 2.142225e-04, 0.000000e+00), - 740: (5.153113e-04, 1.994949e-04, 0.000000e+00), - 741: (4.801234e-04, 1.859336e-04, 0.000000e+00), - 742: (4.476245e-04, 1.734067e-04, 0.000000e+00), - 743: (4.174846e-04, 1.617865e-04, 0.000000e+00), - 744: (3.894221e-04, 1.509641e-04, 0.000000e+00), - 745: (3.631969e-04, 1.408466e-04, 0.000000e+00), - 746: (3.386279e-04, 1.313642e-04, 0.000000e+00), - 747: (3.156452e-04, 1.224905e-04, 0.000000e+00), - 748: (2.941966e-04, 1.142060e-04, 0.000000e+00), - 749: (2.742235e-04, 1.064886e-04, 0.000000e+00), - 750: (2.556624e-04, 9.931439e-05, 0.000000e+00), - 751: (2.384390e-04, 9.265512e-05, 0.000000e+00), - 752: (2.224525e-04, 8.647225e-05, 0.000000e+00), - 753: (2.076036e-04, 8.072780e-05, 0.000000e+00), - 754: (1.938018e-04, 7.538716e-05, 0.000000e+00), - 755: (1.809649e-04, 7.041878e-05, 0.000000e+00), - 756: (1.690167e-04, 6.579338e-05, 0.000000e+00), - 757: (1.578839e-04, 6.148250e-05, 0.000000e+00), - 758: (1.474993e-04, 5.746008e-05, 0.000000e+00), - 759: (1.378026e-04, 5.370272e-05, 0.000000e+00), - 760: (1.287394e-04, 5.018934e-05, 0.000000e+00), - 761: (1.202644e-04, 4.690245e-05, 0.000000e+00), - 762: (1.123502e-04, 4.383167e-05, 0.000000e+00), - 763: (1.049725e-04, 4.096780e-05, 0.000000e+00), - 764: (9.810596e-05, 3.830123e-05, 0.000000e+00), - 765: (9.172477e-05, 3.582218e-05, 0.000000e+00), - 766: (8.579861e-05, 3.351903e-05, 0.000000e+00), - 767: (8.028174e-05, 3.137419e-05, 0.000000e+00), - 768: (7.513013e-05, 2.937068e-05, 0.000000e+00), - 769: (7.030565e-05, 2.749380e-05, 0.000000e+00), - 770: (6.577532e-05, 2.573083e-05, 0.000000e+00), - 771: (6.151508e-05, 2.407249e-05, 0.000000e+00), - 772: (5.752025e-05, 2.251704e-05, 0.000000e+00), - 773: (5.378813e-05, 2.106350e-05, 0.000000e+00), - 774: (5.031350e-05, 1.970991e-05, 0.000000e+00), - 775: (4.708916e-05, 1.845353e-05, 0.000000e+00), - 776: (4.410322e-05, 1.728979e-05, 0.000000e+00), - 777: (4.133150e-05, 1.620928e-05, 0.000000e+00), - 778: (3.874992e-05, 1.520262e-05, 0.000000e+00), - 779: (3.633762e-05, 1.426169e-05, 0.000000e+00), - 780: (3.407653e-05, 1.337946e-05, 0.000000e+00), - 781: (3.195242e-05, 1.255038e-05, 0.000000e+00), - 782: (2.995808e-05, 1.177169e-05, 0.000000e+00), - 783: (2.808781e-05, 1.104118e-05, 0.000000e+00), - 784: (2.633581e-05, 1.035662e-05, 0.000000e+00), - 785: (2.469630e-05, 9.715798e-06, 0.000000e+00), - 786: (2.316311e-05, 9.116316e-06, 0.000000e+00), - 787: (2.172855e-05, 8.555201e-06, 0.000000e+00), - 788: (2.038519e-05, 8.029561e-06, 0.000000e+00), - 789: (1.912625e-05, 7.536768e-06, 0.000000e+00), - 790: (1.794555e-05, 7.074424e-06, 0.000000e+00), - 791: (1.683776e-05, 6.640464e-06, 0.000000e+00), - 792: (1.579907e-05, 6.233437e-06, 0.000000e+00), - 793: (1.482604e-05, 5.852035e-06, 0.000000e+00), - 794: (1.391527e-05, 5.494963e-06, 0.000000e+00), - 795: (1.306345e-05, 5.160948e-06, 0.000000e+00), - 796: (1.226720e-05, 4.848687e-06, 0.000000e+00), - 797: (1.152279e-05, 4.556705e-06, 0.000000e+00), - 798: (1.082663e-05, 4.283580e-06, 0.000000e+00), - 799: (1.017540e-05, 4.027993e-06, 0.000000e+00), - 800: (9.565993e-06, 3.788729e-06, 0.000000e+00), - 801: (8.995405e-06, 3.564599e-06, 0.000000e+00), - 802: (8.460253e-06, 3.354285e-06, 0.000000e+00), - 803: (7.957382e-06, 3.156557e-06, 0.000000e+00), - 804: (7.483997e-06, 2.970326e-06, 0.000000e+00), - 805: (7.037621e-06, 2.794625e-06, 0.000000e+00), - 806: (6.616311e-06, 2.628701e-06, 0.000000e+00), - 807: (6.219265e-06, 2.472248e-06, 0.000000e+00), - 808: (5.845844e-06, 2.325030e-06, 0.000000e+00), - 809: (5.495311e-06, 2.186768e-06, 0.000000e+00), - 810: (5.166853e-06, 2.057152e-06, 0.000000e+00), - 811: (4.859511e-06, 1.935813e-06, 0.000000e+00), - 812: (4.571973e-06, 1.822239e-06, 0.000000e+00), - 813: (4.302920e-06, 1.715914e-06, 0.000000e+00), - 814: (4.051121e-06, 1.616355e-06, 0.000000e+00), - 815: (3.815429e-06, 1.523114e-06, 0.000000e+00), - 816: (3.594719e-06, 1.435750e-06, 0.000000e+00), - 817: (3.387736e-06, 1.353771e-06, 0.000000e+00), - 818: (3.193301e-06, 1.276714e-06, 0.000000e+00), - 819: (3.010363e-06, 1.204166e-06, 0.000000e+00), - 820: (2.837980e-06, 1.135758e-06, 0.000000e+00), - 821: (2.675365e-06, 1.071181e-06, 0.000000e+00), - 822: (2.522020e-06, 1.010243e-06, 0.000000e+00), - 823: (2.377511e-06, 9.527779e-07, 0.000000e+00), - 824: (2.241417e-06, 8.986224e-07, 0.000000e+00), - 825: (2.113325e-06, 8.476168e-07, 0.000000e+00), - 826: (1.992830e-06, 7.996052e-07, 0.000000e+00), - 827: (1.879542e-06, 7.544361e-07, 0.000000e+00), - 828: (1.773083e-06, 7.119624e-07, 0.000000e+00), - 829: (1.673086e-06, 6.720421e-07, 0.000000e+00), - 830: (1.579199e-06, 6.345380e-07, 0.000000e+00) - } + 616: (9.424119e-01, 4.467260e-01, 0.000000e00), + 617: (9.227049e-01, 4.334589e-01, 0.000000e00), + 618: (9.027804e-01, 4.203919e-01, 0.000000e00), + 619: (8.828123e-01, 4.075810e-01, 0.000000e00), + 620: (8.629581e-01, 3.950755e-01, 0.000000e00), + 621: (8.432731e-01, 3.828894e-01, 0.000000e00), + 622: (8.234742e-01, 3.709190e-01, 0.000000e00), + 623: (8.032342e-01, 3.590447e-01, 0.000000e00), + 624: (7.822715e-01, 3.471615e-01, 0.000000e00), + 625: (7.603498e-01, 3.351794e-01, 0.000000e00), + 626: (7.373739e-01, 3.230562e-01, 0.000000e00), + 627: (7.136470e-01, 3.108859e-01, 0.000000e00), + 628: (6.895336e-01, 2.987840e-01, 0.000000e00), + 629: (6.653567e-01, 2.868527e-01, 0.000000e00), + 630: (6.413984e-01, 2.751807e-01, 0.000000e00), + 631: (6.178723e-01, 2.638343e-01, 0.000000e00), + 632: (5.948484e-01, 2.528330e-01, 0.000000e00), + 633: (5.723600e-01, 2.421835e-01, 0.000000e00), + 634: (5.504353e-01, 2.318904e-01, 0.000000e00), + 635: (5.290979e-01, 2.219564e-01, 0.000000e00), + 636: (5.083728e-01, 2.123826e-01, 0.000000e00), + 637: (4.883006e-01, 2.031698e-01, 0.000000e00), + 638: (4.689171e-01, 1.943179e-01, 0.000000e00), + 639: (4.502486e-01, 1.858250e-01, 0.000000e00), + 640: (4.323126e-01, 1.776882e-01, 0.000000e00), + 641: (4.150790e-01, 1.698926e-01, 0.000000e00), + 642: (3.983657e-01, 1.623822e-01, 0.000000e00), + 643: (3.819846e-01, 1.550986e-01, 0.000000e00), + 644: (3.657821e-01, 1.479918e-01, 0.000000e00), + 645: (3.496358e-01, 1.410203e-01, 0.000000e00), + 646: (3.334937e-01, 1.341614e-01, 0.000000e00), + 647: (3.174776e-01, 1.274401e-01, 0.000000e00), + 648: (3.017298e-01, 1.208887e-01, 0.000000e00), + 649: (2.863684e-01, 1.145345e-01, 0.000000e00), + 650: (2.714900e-01, 1.083996e-01, 0.000000e00), + 651: (2.571632e-01, 1.025007e-01, 0.000000e00), + 652: (2.434102e-01, 9.684588e-02, 0.000000e00), + 653: (2.302389e-01, 9.143944e-02, 0.000000e00), + 654: (2.176527e-01, 8.628318e-02, 0.000000e00), + 655: (2.056507e-01, 8.137687e-02, 0.000000e00), + 656: (1.942251e-01, 7.671708e-02, 0.000000e00), + 657: (1.833530e-01, 7.229404e-02, 0.000000e00), + 658: (1.730097e-01, 6.809696e-02, 0.000000e00), + 659: (1.631716e-01, 6.411549e-02, 0.000000e00), + 660: (1.538163e-01, 6.033976e-02, 0.000000e00), + 661: (1.449230e-01, 5.676054e-02, 0.000000e00), + 662: (1.364729e-01, 5.336992e-02, 0.000000e00), + 663: (1.284483e-01, 5.016027e-02, 0.000000e00), + 664: (1.208320e-01, 4.712405e-02, 0.000000e00), + 665: (1.136072e-01, 4.425383e-02, 0.000000e00), + 666: (1.067579e-01, 4.154205e-02, 0.000000e00), + 667: (1.002685e-01, 3.898042e-02, 0.000000e00), + 668: (9.412394e-02, 3.656091e-02, 0.000000e00), + 669: (8.830929e-02, 3.427597e-02, 0.000000e00), + 670: (8.281010e-02, 3.211852e-02, 0.000000e00), + 671: (7.761208e-02, 3.008192e-02, 0.000000e00), + 672: (7.270064e-02, 2.816001e-02, 0.000000e00), + 673: (6.806167e-02, 2.634698e-02, 0.000000e00), + 674: (6.368176e-02, 2.463731e-02, 0.000000e00), + 675: (5.954815e-02, 2.302574e-02, 0.000000e00), + 676: (5.564917e-02, 2.150743e-02, 0.000000e00), + 677: (5.197543e-02, 2.007838e-02, 0.000000e00), + 678: (4.851788e-02, 1.873474e-02, 0.000000e00), + 679: (4.526737e-02, 1.747269e-02, 0.000000e00), + 680: (4.221473e-02, 1.628841e-02, 0.000000e00), + 681: (3.934954e-02, 1.517767e-02, 0.000000e00), + 682: (3.665730e-02, 1.413473e-02, 0.000000e00), + 683: (3.412407e-02, 1.315408e-02, 0.000000e00), + 684: (3.173768e-02, 1.223092e-02, 0.000000e00), + 685: (2.948752e-02, 1.136106e-02, 0.000000e00), + 686: (2.736717e-02, 1.054190e-02, 0.000000e00), + 687: (2.538113e-02, 9.775050e-03, 0.000000e00), + 688: (2.353356e-02, 9.061962e-03, 0.000000e00), + 689: (2.182558e-02, 8.402962e-03, 0.000000e00), + 690: (2.025590e-02, 7.797457e-03, 0.000000e00), + 691: (1.881892e-02, 7.243230e-03, 0.000000e00), + 692: (1.749930e-02, 6.734381e-03, 0.000000e00), + 693: (1.628167e-02, 6.265001e-03, 0.000000e00), + 694: (1.515301e-02, 5.830085e-03, 0.000000e00), + 695: (1.410230e-02, 5.425391e-03, 0.000000e00), + 696: (1.312106e-02, 5.047634e-03, 0.000000e00), + 697: (1.220509e-02, 4.695140e-03, 0.000000e00), + 698: (1.135114e-02, 4.366592e-03, 0.000000e00), + 699: (1.055593e-02, 4.060685e-03, 0.000000e00), + 700: (9.816228e-03, 3.776140e-03, 0.000000e00), + 701: (9.128517e-03, 3.511578e-03, 0.000000e00), + 702: (8.488116e-03, 3.265211e-03, 0.000000e00), + 703: (7.890589e-03, 3.035344e-03, 0.000000e00), + 704: (7.332061e-03, 2.820496e-03, 0.000000e00), + 705: (6.809147e-03, 2.619372e-03, 0.000000e00), + 706: (6.319204e-03, 2.430960e-03, 0.000000e00), + 707: (5.861036e-03, 2.254796e-03, 0.000000e00), + 708: (5.433624e-03, 2.090489e-03, 0.000000e00), + 709: (5.035802e-03, 1.937586e-03, 0.000000e00), + 710: (4.666298e-03, 1.795595e-03, 0.000000e00), + 711: (4.323750e-03, 1.663989e-03, 0.000000e00), + 712: (4.006709e-03, 1.542195e-03, 0.000000e00), + 713: (3.713708e-03, 1.429639e-03, 0.000000e00), + 714: (3.443294e-03, 1.325752e-03, 0.000000e00), + 715: (3.194041e-03, 1.229980e-03, 0.000000e00), + 716: (2.964424e-03, 1.141734e-03, 0.000000e00), + 717: (2.752492e-03, 1.060269e-03, 0.000000e00), + 718: (2.556406e-03, 9.848854e-04, 0.000000e00), + 719: (2.374564e-03, 9.149703e-04, 0.000000e00), + 720: (2.205568e-03, 8.499903e-04, 0.000000e00), + 721: (2.048294e-03, 7.895158e-04, 0.000000e00), + 722: (1.902113e-03, 7.333038e-04, 0.000000e00), + 723: (1.766485e-03, 6.811458e-04, 0.000000e00), + 724: (1.640857e-03, 6.328287e-04, 0.000000e00), + 725: (1.524672e-03, 5.881375e-04, 0.000000e00), + 726: (1.417322e-03, 5.468389e-04, 0.000000e00), + 727: (1.318031e-03, 5.086349e-04, 0.000000e00), + 728: (1.226059e-03, 4.732403e-04, 0.000000e00), + 729: (1.140743e-03, 4.404016e-04, 0.000000e00), + 730: (1.061495e-03, 4.098928e-04, 0.000000e00), + 731: (9.877949e-04, 3.815137e-04, 0.000000e00), + 732: (9.191847e-04, 3.550902e-04, 0.000000e00), + 733: (8.552568e-04, 3.304668e-04, 0.000000e00), + 734: (7.956433e-04, 3.075030e-04, 0.000000e00), + 735: (7.400120e-04, 2.860718e-04, 0.000000e00), + 736: (6.880980e-04, 2.660718e-04, 0.000000e00), + 737: (6.397864e-04, 2.474586e-04, 0.000000e00), + 738: (5.949726e-04, 2.301919e-04, 0.000000e00), + 739: (5.535291e-04, 2.142225e-04, 0.000000e00), + 740: (5.153113e-04, 1.994949e-04, 0.000000e00), + 741: (4.801234e-04, 1.859336e-04, 0.000000e00), + 742: (4.476245e-04, 1.734067e-04, 0.000000e00), + 743: (4.174846e-04, 1.617865e-04, 0.000000e00), + 744: (3.894221e-04, 1.509641e-04, 0.000000e00), + 745: (3.631969e-04, 1.408466e-04, 0.000000e00), + 746: (3.386279e-04, 1.313642e-04, 0.000000e00), + 747: (3.156452e-04, 1.224905e-04, 0.000000e00), + 748: (2.941966e-04, 1.142060e-04, 0.000000e00), + 749: (2.742235e-04, 1.064886e-04, 0.000000e00), + 750: (2.556624e-04, 9.931439e-05, 0.000000e00), + 751: (2.384390e-04, 9.265512e-05, 0.000000e00), + 752: (2.224525e-04, 8.647225e-05, 0.000000e00), + 753: (2.076036e-04, 8.072780e-05, 0.000000e00), + 754: (1.938018e-04, 7.538716e-05, 0.000000e00), + 755: (1.809649e-04, 7.041878e-05, 0.000000e00), + 756: (1.690167e-04, 6.579338e-05, 0.000000e00), + 757: (1.578839e-04, 6.148250e-05, 0.000000e00), + 758: (1.474993e-04, 5.746008e-05, 0.000000e00), + 759: (1.378026e-04, 5.370272e-05, 0.000000e00), + 760: (1.287394e-04, 5.018934e-05, 0.000000e00), + 761: (1.202644e-04, 4.690245e-05, 0.000000e00), + 762: (1.123502e-04, 4.383167e-05, 0.000000e00), + 763: (1.049725e-04, 4.096780e-05, 0.000000e00), + 764: (9.810596e-05, 3.830123e-05, 0.000000e00), + 765: (9.172477e-05, 3.582218e-05, 0.000000e00), + 766: (8.579861e-05, 3.351903e-05, 0.000000e00), + 767: (8.028174e-05, 3.137419e-05, 0.000000e00), + 768: (7.513013e-05, 2.937068e-05, 0.000000e00), + 769: (7.030565e-05, 2.749380e-05, 0.000000e00), + 770: (6.577532e-05, 2.573083e-05, 0.000000e00), + 771: (6.151508e-05, 2.407249e-05, 0.000000e00), + 772: (5.752025e-05, 2.251704e-05, 0.000000e00), + 773: (5.378813e-05, 2.106350e-05, 0.000000e00), + 774: (5.031350e-05, 1.970991e-05, 0.000000e00), + 775: (4.708916e-05, 1.845353e-05, 0.000000e00), + 776: (4.410322e-05, 1.728979e-05, 0.000000e00), + 777: (4.133150e-05, 1.620928e-05, 0.000000e00), + 778: (3.874992e-05, 1.520262e-05, 0.000000e00), + 779: (3.633762e-05, 1.426169e-05, 0.000000e00), + 780: (3.407653e-05, 1.337946e-05, 0.000000e00), + 781: (3.195242e-05, 1.255038e-05, 0.000000e00), + 782: (2.995808e-05, 1.177169e-05, 0.000000e00), + 783: (2.808781e-05, 1.104118e-05, 0.000000e00), + 784: (2.633581e-05, 1.035662e-05, 0.000000e00), + 785: (2.469630e-05, 9.715798e-06, 0.000000e00), + 786: (2.316311e-05, 9.116316e-06, 0.000000e00), + 787: (2.172855e-05, 8.555201e-06, 0.000000e00), + 788: (2.038519e-05, 8.029561e-06, 0.000000e00), + 789: (1.912625e-05, 7.536768e-06, 0.000000e00), + 790: (1.794555e-05, 7.074424e-06, 0.000000e00), + 791: (1.683776e-05, 6.640464e-06, 0.000000e00), + 792: (1.579907e-05, 6.233437e-06, 0.000000e00), + 793: (1.482604e-05, 5.852035e-06, 0.000000e00), + 794: (1.391527e-05, 5.494963e-06, 0.000000e00), + 795: (1.306345e-05, 5.160948e-06, 0.000000e00), + 796: (1.226720e-05, 4.848687e-06, 0.000000e00), + 797: (1.152279e-05, 4.556705e-06, 0.000000e00), + 798: (1.082663e-05, 4.283580e-06, 0.000000e00), + 799: (1.017540e-05, 4.027993e-06, 0.000000e00), + 800: (9.565993e-06, 3.788729e-06, 0.000000e00), + 801: (8.995405e-06, 3.564599e-06, 0.000000e00), + 802: (8.460253e-06, 3.354285e-06, 0.000000e00), + 803: (7.957382e-06, 3.156557e-06, 0.000000e00), + 804: (7.483997e-06, 2.970326e-06, 0.000000e00), + 805: (7.037621e-06, 2.794625e-06, 0.000000e00), + 806: (6.616311e-06, 2.628701e-06, 0.000000e00), + 807: (6.219265e-06, 2.472248e-06, 0.000000e00), + 808: (5.845844e-06, 2.325030e-06, 0.000000e00), + 809: (5.495311e-06, 2.186768e-06, 0.000000e00), + 810: (5.166853e-06, 2.057152e-06, 0.000000e00), + 811: (4.859511e-06, 1.935813e-06, 0.000000e00), + 812: (4.571973e-06, 1.822239e-06, 0.000000e00), + 813: (4.302920e-06, 1.715914e-06, 0.000000e00), + 814: (4.051121e-06, 1.616355e-06, 0.000000e00), + 815: (3.815429e-06, 1.523114e-06, 0.000000e00), + 816: (3.594719e-06, 1.435750e-06, 0.000000e00), + 817: (3.387736e-06, 1.353771e-06, 0.000000e00), + 818: (3.193301e-06, 1.276714e-06, 0.000000e00), + 819: (3.010363e-06, 1.204166e-06, 0.000000e00), + 820: (2.837980e-06, 1.135758e-06, 0.000000e00), + 821: (2.675365e-06, 1.071181e-06, 0.000000e00), + 822: (2.522020e-06, 1.010243e-06, 0.000000e00), + 823: (2.377511e-06, 9.527779e-07, 0.000000e00), + 824: (2.241417e-06, 8.986224e-07, 0.000000e00), + 825: (2.113325e-06, 8.476168e-07, 0.000000e00), + 826: (1.992830e-06, 7.996052e-07, 0.000000e00), + 827: (1.879542e-06, 7.544361e-07, 0.000000e00), + 828: (1.773083e-06, 7.119624e-07, 0.000000e00), + 829: (1.673086e-06, 6.720421e-07, 0.000000e00), + 830: (1.579199e-06, 6.345380e-07, 0.000000e00), + }, } -MSDS_CMFS_STANDARD_OBSERVER = CaseInsensitiveMapping({ - 'CIE 1931 2 Degree Standard Observer': - XYZ_ColourMatchingFunctions( - DATA_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer'], - name='CIE 1931 2 Degree Standard Observer', - strict_name='CIE 1931 2$^\\circ$ Standard Observer'), - 'CIE 1964 10 Degree Standard Observer': - XYZ_ColourMatchingFunctions( +MSDS_CMFS_STANDARD_OBSERVER: ( + LazyCaseInsensitiveMapping +) = LazyCaseInsensitiveMapping( + { + "CIE 1931 2 Degree Standard Observer": partial( + XYZ_ColourMatchingFunctions, + DATA_CMFS_STANDARD_OBSERVER["CIE 1931 2 Degree Standard Observer"], + name="CIE 1931 2 Degree Standard Observer", + strict_name="CIE 1931 2$^\\circ$ Standard Observer", + ), + "CIE 1964 10 Degree Standard Observer": partial( + XYZ_ColourMatchingFunctions, DATA_CMFS_STANDARD_OBSERVER[ - 'CIE 1964 10 Degree Standard Observer'], - name='CIE 1964 10 Degree Standard Observer', - strict_name='CIE 1964 10$^\\circ$ Standard Observer'), - 'CIE 2012 2 Degree Standard Observer': - XYZ_ColourMatchingFunctions( - DATA_CMFS_STANDARD_OBSERVER['CIE 2012 2 Degree Standard Observer'], - name='CIE 2012 2 Degree Standard Observer', - strict_name='CIE 2012 2$^\\circ$ Standard Observer'), - 'CIE 2012 10 Degree Standard Observer': - XYZ_ColourMatchingFunctions( + "CIE 1964 10 Degree Standard Observer" + ], + name="CIE 1964 10 Degree Standard Observer", + strict_name="CIE 1964 10$^\\circ$ Standard Observer", + ), + "CIE 2012 2 Degree Standard Observer": partial( + XYZ_ColourMatchingFunctions, + DATA_CMFS_STANDARD_OBSERVER["CIE 2012 2 Degree Standard Observer"], + name="CIE 2012 2 Degree Standard Observer", + strict_name="CIE 2012 2$^\\circ$ Standard Observer", + ), + "CIE 2012 10 Degree Standard Observer": partial( + XYZ_ColourMatchingFunctions, DATA_CMFS_STANDARD_OBSERVER[ - 'CIE 2012 10 Degree Standard Observer'], - name='CIE 2012 10 Degree Standard Observer', - strict_name='CIE 2012 10$^\\circ$ Standard Observer') -}) + "CIE 2012 10 Degree Standard Observer" + ], + name="CIE 2012 10 Degree Standard Observer", + strict_name="CIE 2012 10$^\\circ$ Standard Observer", + ), + } +) MSDS_CMFS_STANDARD_OBSERVER.__doc__ = """ Multi-spectral distributions of the *CIE* Standard Observer colour matching functions. @@ -3231,23 +3249,19 @@ ---------- :cite:`CVRLr`, :cite:`CVRLs` -MSDS_CMFS_STANDARD_OBSERVER : CaseInsensitiveMapping - **{'CIE 1931 2 Degree Standard Observer', - 'CIE 1964 10 Degree Standard Observer', - 'CIE 2012 2 Degree Standard Observer', - 'CIE 2012 10 Degree Standard Observer'}** - Aliases: - 'cie_2_1931': 'CIE 1931 2 Degree Standard Observer' - 'cie_10_1964': 'CIE 1964 10 Degree Standard Observer' """ -MSDS_CMFS_STANDARD_OBSERVER['cie_2_1931'] = ( - MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']) -MSDS_CMFS_STANDARD_OBSERVER['cie_10_1964'] = ( - MSDS_CMFS_STANDARD_OBSERVER['CIE 1964 10 Degree Standard Observer']) +MSDS_CMFS_STANDARD_OBSERVER["cie_2_1931"] = MSDS_CMFS_STANDARD_OBSERVER[ + "CIE 1931 2 Degree Standard Observer" +] +MSDS_CMFS_STANDARD_OBSERVER["cie_10_1964"] = MSDS_CMFS_STANDARD_OBSERVER[ + "CIE 1964 10 Degree Standard Observer" +] -MSDS_CMFS = CaseInsensitiveMapping(MSDS_CMFS_LMS) +MSDS_CMFS = LazyCaseInsensitiveMapping(MSDS_CMFS_LMS) MSDS_CMFS.__doc__ = """ Multi-spectral distributions of the colour matching functions. @@ -3255,17 +3269,6 @@ ---------- :cite:`Broadbent2009a`, :cite:`CVRLr`, :cite:`CVRLs`, :cite:`CVRLt`, :cite:`CVRLu`, :cite:`CVRLw`, :cite:`Machado2010a` - -MSDS_CMFS : CaseInsensitiveMapping - **{'Stockman & Sharpe 10 Degree Cone Fundamentals', - 'Stockman & Sharpe 2 Degree Cone Fundamentals', - 'Wright & Guild 1931 2 Degree RGB CMFs', - 'Stiles & Burch 1955 2 Degree RGB CMFs', - 'Stiles & Burch 1959 10 Degree RGB CMFs', - 'CIE 1931 2 Degree Standard Observer', - 'CIE 1964 10 Degree Standard Observer', - 'CIE 2012 2 Degree Standard Observer', - 'CIE 2012 10 Degree Standard Observer'}** """ MSDS_CMFS.update(MSDS_CMFS_RGB) MSDS_CMFS.update(MSDS_CMFS_STANDARD_OBSERVER) diff --git a/colour/colorimetry/datasets/illuminants/__init__.py b/colour/colorimetry/datasets/illuminants/__init__.py index ad57a88abe..3580d98bc0 100644 --- a/colour/colorimetry/datasets/illuminants/__init__.py +++ b/colour/colorimetry/datasets/illuminants/__init__.py @@ -1,14 +1,15 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .chromaticity_coordinates import CCS_ILLUMINANTS from .sds_d_illuminant_series import ( - SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES) + SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES, +) from .hunterlab import TVS_ILLUMINANTS_HUNTERLAB from .sds import SDS_ILLUMINANTS +from .tristimulus_values import TVS_ILLUMINANTS __all__ = [ - 'CCS_ILLUMINANTS', 'SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES', - 'TVS_ILLUMINANTS_HUNTERLAB', 'SDS_ILLUMINANTS' + "CCS_ILLUMINANTS", + "SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES", + "TVS_ILLUMINANTS_HUNTERLAB", + "SDS_ILLUMINANTS", + "TVS_ILLUMINANTS", ] diff --git a/colour/colorimetry/datasets/illuminants/chromaticity_coordinates.py b/colour/colorimetry/datasets/illuminants/chromaticity_coordinates.py index 5c14a378ba..67012c3bad 100644 --- a/colour/colorimetry/datasets/illuminants/chromaticity_coordinates.py +++ b/colour/colorimetry/datasets/illuminants/chromaticity_coordinates.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Chromaticity Coordinates of Illuminants ======================================= @@ -36,6 +35,7 @@ *CIE 1931 2 Degree Standard Observer* only: - ACES +- Blackmagic Wide Gamut - DCI-P3 - ICC D50 @@ -44,6 +44,9 @@ References ---------- +- :cite:`BlackmagicDesign2021` : Blackmagic Design. (2021). Blackmagic + Generation 5 Color Science. https://drive.google.com/file/d/\ +1FF5WO2nvI9GEWb4_EntrBoV9ZIuFToZd/view - :cite:`CIETC1-482004h` : CIE TC 1-48. (2004). CIE 015:2004 Colorimetry, 3rd Edition. In CIE 015:2004 Colorimetry, 3rd Edition. Commission Internationale de l'Eclairage. ISBN:978-3-901906-33-6 @@ -55,9 +58,10 @@ Digital Cinema System Specification - Version 1.1. http://www.dcimovies.com/archives/spec_v1_1/\ DCI_DCinema_System_Spec_v1_1.pdf -- :cite:`ISO2002` : ISO. (2002). INTERNATIONAL STANDARD 7589-2002 - - Photography - Illuminants for sensitometry - Specifications for daylight, - incandescent tungsten and printer. +- :cite:`InternationalOrganizationforStandardization2002` : International + Organization for Standardization. (2002). INTERNATIONAL STANDARD ISO + 7589-2002 - Photography - Illuminants for sensitometry - Specifications for + daylight, incandescent tungsten and printer. - :cite:`InternationalColorConsortium2010` : International Color Consortium. (2010). Specification ICC.1:2010 (Profile version 4.3.0.0) (pp. 1-130). http://www.color.org/specification/ICC1v43_2010-12.pdf @@ -73,85 +77,89 @@ White_points_of_standard_illuminants """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.utilities import CaseInsensitiveMapping -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'CCS_ILLUMINANTS_CIE_STANDARD_OBSERVER_2_DEGREE_CIE1931', - 'CCS_ILLUMINANTS_ACES_STANDARD_OBSERVER_2_DEGREE_CIE1931', - 'CCS_ILLUMINANTS_DCI_STANDARD_OBSERVER_2_DEGREE_CIE1931', - 'CCS_ILLUMINANTS_ICC_STANDARD_OBSERVER_2_DEGREE_CIE1931', - 'CCS_ILLUMINANTS_ISO_STANDARD_OBSERVER_2_DEGREE_CIE1931', - 'CCS_ILLUMINANTS_CIE_STANDARD_OBSERVER_10_DEGREE_CIE1964', - 'CCS_ILLUMINANTS_ISO_STANDARD_OBSERVER_10_DEGREE_CIE1964', - 'CCS_ILLUMINANTS' + "CCS_ILLUMINANTS_CIE_STANDARD_OBSERVER_2_DEGREE_CIE1931", + "CCS_ILLUMINANTS_ACES_STANDARD_OBSERVER_2_DEGREE_CIE1931", + "CCS_ILLUMINANTS_BLACKMAGIC_DESIGN_STANDARD_OBSERVER_2_DEGREE_CIE1931", + "CCS_ILLUMINANTS_DCI_STANDARD_OBSERVER_2_DEGREE_CIE1931", + "CCS_ILLUMINANTS_ICC_STANDARD_OBSERVER_2_DEGREE_CIE1931", + "CCS_ILLUMINANTS_ISO_STANDARD_OBSERVER_2_DEGREE_CIE1931", + "CCS_ILLUMINANTS_CIE_STANDARD_OBSERVER_10_DEGREE_CIE1964", + "CCS_ILLUMINANTS_ISO_STANDARD_OBSERVER_10_DEGREE_CIE1964", + "CCS_ILLUMINANTS", ] -CCS_ILLUMINANTS_CIE_STANDARD_OBSERVER_2_DEGREE_CIE1931 = ( - CaseInsensitiveMapping({ - 'A': np.array([0.44758, 0.40745]), - 'B': np.array([0.34842, 0.35161]), - 'C': np.array([0.31006, 0.31616]), - 'D50': np.array([0.34570, 0.35850]), - 'D55': np.array([0.33243, 0.34744]), - 'D60': np.array([0.321616709705268, 0.337619916550817]), - 'D65': np.array([0.31270, 0.32900]), - 'D75': np.array([0.29903, 0.31488]), - 'E': np.array([1 / 3, 1 / 3]), - 'FL1': np.array([0.31310, 0.33710]), - 'FL2': np.array([0.37210, 0.37510]), - 'FL3': np.array([0.40910, 0.39410]), - 'FL4': np.array([0.44020, 0.40310]), - 'FL5': np.array([0.31380, 0.34520]), - 'FL6': np.array([0.37790, 0.38820]), - 'FL7': np.array([0.31290, 0.32920]), - 'FL8': np.array([0.34580, 0.35860]), - 'FL9': np.array([0.37410, 0.37270]), - 'FL10': np.array([0.34580, 0.35880]), - 'FL11': np.array([0.38050, 0.37690]), - 'FL12': np.array([0.43700, 0.40420]), - 'FL3.1': np.array([0.44070, 0.40330]), - 'FL3.2': np.array([0.38080, 0.37340]), - 'FL3.3': np.array([0.31530, 0.34390]), - 'FL3.4': np.array([0.44290, 0.40430]), - 'FL3.5': np.array([0.37490, 0.36720]), - 'FL3.6': np.array([0.34880, 0.36000]), - 'FL3.7': np.array([0.43840, 0.40450]), - 'FL3.8': np.array([0.38200, 0.38320]), - 'FL3.9': np.array([0.34990, 0.35910]), - 'FL3.10': np.array([0.34550, 0.35600]), - 'FL3.11': np.array([0.32450, 0.34340]), - 'FL3.12': np.array([0.43770, 0.40370]), - 'FL3.13': np.array([0.38300, 0.37240]), - 'FL3.14': np.array([0.34470, 0.36090]), - 'FL3.15': np.array([0.31270, 0.32880]), - 'HP1': np.array([0.53300, 0.4150]), - 'HP2': np.array([0.47780, 0.41580]), - 'HP3': np.array([0.43020, 0.40750]), - 'HP4': np.array([0.38120, 0.37970]), - 'HP5': np.array([0.37760, 0.37130]), - 'LED-B1': np.array([0.45600, 0.40780]), - 'LED-B2': np.array([0.43570, 0.40120]), - 'LED-B3': np.array([0.37560, 0.37230]), - 'LED-B4': np.array([0.34220, 0.35020]), - 'LED-B5': np.array([0.31180, 0.32360]), - 'LED-BH1': np.array([0.44740, 0.40660]), - 'LED-RGB1': np.array([0.45570, 0.42110]), - 'LED-V1': np.array([0.45480, 0.40440]), - 'LED-V2': np.array([0.37810, 0.37750]), - 'ID65': np.array([0.310656625403120, 0.330663091836953]), - 'ID50': np.array([0.343211370103531, 0.360207541805137]) - })) +CCS_ILLUMINANTS_CIE_STANDARD_OBSERVER_2_DEGREE_CIE1931: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "A": np.array([0.44758, 0.40745]), + "B": np.array([0.34842, 0.35161]), + "C": np.array([0.31006, 0.31616]), + "D50": np.array([0.34570, 0.35850]), + "D55": np.array([0.33243, 0.34744]), + "D60": np.array([0.321616709705268, 0.337619916550817]), + "D65": np.array([0.31270, 0.32900]), + "D75": np.array([0.29903, 0.31488]), + "E": np.array([1 / 3, 1 / 3]), + "FL1": np.array([0.31310, 0.33710]), + "FL2": np.array([0.37210, 0.37510]), + "FL3": np.array([0.40910, 0.39410]), + "FL4": np.array([0.44020, 0.40310]), + "FL5": np.array([0.31380, 0.34520]), + "FL6": np.array([0.37790, 0.38820]), + "FL7": np.array([0.31290, 0.32920]), + "FL8": np.array([0.34580, 0.35860]), + "FL9": np.array([0.37410, 0.37270]), + "FL10": np.array([0.34580, 0.35880]), + "FL11": np.array([0.38050, 0.37690]), + "FL12": np.array([0.43700, 0.40420]), + "FL3.1": np.array([0.44070, 0.40330]), + "FL3.2": np.array([0.38080, 0.37340]), + "FL3.3": np.array([0.31530, 0.34390]), + "FL3.4": np.array([0.44290, 0.40430]), + "FL3.5": np.array([0.37490, 0.36720]), + "FL3.6": np.array([0.34880, 0.36000]), + "FL3.7": np.array([0.43840, 0.40450]), + "FL3.8": np.array([0.38200, 0.38320]), + "FL3.9": np.array([0.34990, 0.35910]), + "FL3.10": np.array([0.34550, 0.35600]), + "FL3.11": np.array([0.32450, 0.34340]), + "FL3.12": np.array([0.43770, 0.40370]), + "FL3.13": np.array([0.38300, 0.37240]), + "FL3.14": np.array([0.34470, 0.36090]), + "FL3.15": np.array([0.31270, 0.32880]), + "HP1": np.array([0.53300, 0.4150]), + "HP2": np.array([0.47780, 0.41580]), + "HP3": np.array([0.43020, 0.40750]), + "HP4": np.array([0.38120, 0.37970]), + "HP5": np.array([0.37760, 0.37130]), + "LED-B1": np.array([0.45600, 0.40780]), + "LED-B2": np.array([0.43570, 0.40120]), + "LED-B3": np.array([0.37560, 0.37230]), + "LED-B4": np.array([0.34220, 0.35020]), + "LED-B5": np.array([0.31180, 0.32360]), + "LED-BH1": np.array([0.44740, 0.40660]), + "LED-RGB1": np.array([0.45570, 0.42110]), + "LED-V1": np.array([0.45480, 0.40440]), + "LED-V2": np.array([0.37810, 0.37750]), + "ID65": np.array([0.310656625403120, 0.330663091836953]), + "ID50": np.array([0.343211370103531, 0.360207541805137]), + } +) """ Chromaticity coordinates of the *CIE* illuminants for the *CIE 1931 2 Degree Standard Observer*. @@ -159,14 +167,15 @@ References ---------- :cite:`CIETC1-482004h`, :cite:`Wikipedia2006a` - -CCS_ILLUMINANTS_CIE_STANDARD_OBSERVER_2_DEGREE_CIE1931 : CaseInsensitiveMapping """ -CCS_ILLUMINANTS_ACES_STANDARD_OBSERVER_2_DEGREE_CIE1931 = ( - CaseInsensitiveMapping({ - 'ACES': np.array([0.32168, 0.33767]), - })) +CCS_ILLUMINANTS_ACES_STANDARD_OBSERVER_2_DEGREE_CIE1931: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "ACES": np.array([0.32168, 0.33767]), + } +) """ Chromaticity coordinates of the *Academy Color Encoding System* (ACES) illuminants for the *CIE 1931 2 Degree Standard Observer*. @@ -174,14 +183,31 @@ References ---------- :cite:`TheAcademyofMotionPictureArtsandSciences2014q` +""" + +CCS_ILLUMINANTS_BLACKMAGIC_DESIGN_STANDARD_OBSERVER_2_DEGREE_CIE1931: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "Blackmagic Wide Gamut": np.array([0.3127170, 0.3290312]), + } +) +""" +Chromaticity coordinates of the *Blackmagic Design* illuminants for the +*CIE 1931 2 Degree Standard Observer*. -CCS_ILLUMINANTS_ICC_STANDARD_OBSERVER_2_DEGREE_CIE1931 : CaseInsensitiveMapping +References +---------- +:cite:`BlackmagicDesign2021` """ -CCS_ILLUMINANTS_DCI_STANDARD_OBSERVER_2_DEGREE_CIE1931 = ( - CaseInsensitiveMapping({ - 'DCI-P3': np.array([0.31400, 0.35100]), - })) +CCS_ILLUMINANTS_DCI_STANDARD_OBSERVER_2_DEGREE_CIE1931: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "DCI-P3": np.array([0.31400, 0.35100]), + } +) """ Chromaticity coordinates of the *DCI* illuminants for the *CIE 1931 2 Degree Standard Observer*. @@ -189,14 +215,13 @@ References ---------- :cite:`DigitalCinemaInitiatives2007b` - -CCS_ILLUMINANTS_DCI_STANDARD_OBSERVER_2_DEGREE_CIE1931 : CaseInsensitiveMapping """ -CCS_ILLUMINANTS_ICC_STANDARD_OBSERVER_2_DEGREE_CIE1931 = ( - CaseInsensitiveMapping({ - 'ICC D50': np.array([0.345702914918791, 0.358538596679933]) - })) +CCS_ILLUMINANTS_ICC_STANDARD_OBSERVER_2_DEGREE_CIE1931: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + {"ICC D50": np.array([0.345702914918791, 0.358538596679933])} +) """ Chromaticity coordinates of the *ICC* illuminants for the *CIE 1931 2 Degree Standard Observer*. @@ -204,93 +229,102 @@ References ---------- :cite:`InternationalColorConsortium2010` - -CCS_ILLUMINANTS_ICC_STANDARD_OBSERVER_2_DEGREE_CIE1931 : CaseInsensitiveMapping """ -CCS_ILLUMINANTS_ISO_STANDARD_OBSERVER_2_DEGREE_CIE1931 = ( - CaseInsensitiveMapping({ - 'ISO 7589 Photographic Daylight': - np.array([0.332039098470978, 0.347263885596614]), - 'ISO 7589 Sensitometric Daylight': - np.array([0.333818313227557, 0.353436231513603]), - 'ISO 7589 Studio Tungsten': - np.array([0.430944089109761, 0.403585442674295]), - 'ISO 7589 Sensitometric Studio Tungsten': - np.array([0.431418223648390, 0.407471441950342]), - 'ISO 7589 Photoflood': - np.array([0.411146015714843, 0.393719378241161]), - 'ISO 7589 Sensitometric Photoflood': - np.array([0.412024776908998, 0.398177410548532]), - 'ISO 7589 Sensitometric Printer': - np.array([0.412087967973680, 0.421104984758526]), - })) +CCS_ILLUMINANTS_ISO_STANDARD_OBSERVER_2_DEGREE_CIE1931: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "ISO 7589 Photographic Daylight": np.array( + [0.332039098470978, 0.347263885596614] + ), + "ISO 7589 Sensitometric Daylight": np.array( + [0.333818313227557, 0.353436231513603] + ), + "ISO 7589 Studio Tungsten": np.array( + [0.430944089109761, 0.403585442674295] + ), + "ISO 7589 Sensitometric Studio Tungsten": np.array( + [0.431418223648390, 0.407471441950342] + ), + "ISO 7589 Photoflood": np.array( + [0.411146015714843, 0.393719378241161] + ), + "ISO 7589 Sensitometric Photoflood": np.array( + [0.412024776908998, 0.398177410548532] + ), + "ISO 7589 Sensitometric Printer": np.array( + [0.412087967973680, 0.421104984758526] + ), + } +) """ Chromaticity coordinates of the *ISO* illuminants for the *CIE 1931 2 Degree Standard Observer*. References ---------- -:cite:`ISO2002` - -CCS_ILLUMINANTS_ISO_STANDARD_OBSERVER_2_DEGREE_CIE1931 : CaseInsensitiveMapping +:cite:`InternationalOrganizationforStandardization2002` """ -CCS_ILLUMINANTS_CIE_STANDARD_OBSERVER_10_DEGREE_CIE1964 = ( - CaseInsensitiveMapping({ - 'A': np.array([0.45117, 0.40594]), - 'B': np.array([0.34980, 0.35270]), - 'C': np.array([0.31039, 0.31905]), - 'D50': np.array([0.34773, 0.35952]), - 'D55': np.array([0.33412, 0.34877]), - 'D60': np.array([0.322986926715820, 0.339275732345997]), - 'D65': np.array([0.31382, 0.33100]), - 'D75': np.array([0.29968, 0.31740]), - 'E': np.array([1 / 3, 1 / 3]), - 'FL1': np.array([0.31811, 0.33559]), - 'FL2': np.array([0.37925, 0.36733]), - 'FL3': np.array([0.41761, 0.38324]), - 'FL4': np.array([0.44920, 0.39074]), - 'FL5': np.array([0.31975, 0.34246]), - 'FL6': np.array([0.38660, 0.37847]), - 'FL7': np.array([0.31569, 0.32960]), - 'FL8': np.array([0.34902, 0.35939]), - 'FL9': np.array([0.37829, 0.37045]), - 'FL10': np.array([0.35090, 0.35444]), - 'FL11': np.array([0.38541, 0.37123]), - 'FL12': np.array([0.44256, 0.39717]), - 'FL3.1': np.array([0.449830684010003, 0.390231404321266]), - 'FL3.2': np.array([0.386924116672933, 0.365756034732821]), - 'FL3.3': np.array([0.321176986855865, 0.340501092654981]), - 'FL3.4': np.array([0.448121275113995, 0.397077112142482]), - 'FL3.5': np.array([0.377814166608895, 0.366625766963060]), - 'FL3.6': np.array([0.351976478983504, 0.361094432889677]), - 'FL3.7': np.array([0.444309208810922, 0.396791387314871]), - 'FL3.8': np.array([0.387588931999771, 0.376305569410173]), - 'FL3.9': np.array([0.354688990710449, 0.353445033593383]), - 'FL3.10': np.array([0.349344792334400, 0.354984421140869]), - 'FL3.11': np.array([0.329267975695120, 0.338865386643537]), - 'FL3.12': np.array([0.442252080438001, 0.401220551071252]), - 'FL3.13': np.array([0.386275268780817, 0.374283190950586]), - 'FL3.14': np.array([0.347255078638291, 0.366808242504180]), - 'FL3.15': np.array([0.314613997909246, 0.333377149377113]), - 'HP1': np.array([0.543334600247307, 0.405289298480431]), - 'HP2': np.array([0.482647330648721, 0.410815644179685]), - 'HP3': np.array([0.435560034503954, 0.398801084399711]), - 'HP4': np.array([0.385193641123543, 0.368275479241015]), - 'HP5': np.array([0.380316415606638, 0.366617114797851]), - 'LED-B1': np.array([0.462504966271043, 0.403041801546906]), - 'LED-B2': np.array([0.442119475258745, 0.396633702892576]), - 'LED-B3': np.array([0.380851979328052, 0.368518548904765]), - 'LED-B4': np.array([0.348371362473402, 0.345065503264192]), - 'LED-B5': np.array([0.316916877024753, 0.322060276350364]), - 'LED-BH1': np.array([0.452772610754910, 0.400032462750000]), - 'LED-RGB1': np.array([0.457036370583652, 0.425381348780888]), - 'LED-V1': np.array([0.453602699414564, 0.398199587905174]), - 'LED-V2': np.array([0.377728483834020, 0.374512315539769]), - 'ID65': np.array([0.312074043269908, 0.332660121024630]), - 'ID50': np.array([0.345621427535976, 0.361228962209198]) - })) +CCS_ILLUMINANTS_CIE_STANDARD_OBSERVER_10_DEGREE_CIE1964: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "A": np.array([0.45117, 0.40594]), + "B": np.array([0.34980, 0.35270]), + "C": np.array([0.31039, 0.31905]), + "D50": np.array([0.34773, 0.35952]), + "D55": np.array([0.33412, 0.34877]), + "D60": np.array([0.322986926715820, 0.339275732345997]), + "D65": np.array([0.31382, 0.33100]), + "D75": np.array([0.29968, 0.31740]), + "E": np.array([1 / 3, 1 / 3]), + "FL1": np.array([0.31811, 0.33559]), + "FL2": np.array([0.37925, 0.36733]), + "FL3": np.array([0.41761, 0.38324]), + "FL4": np.array([0.44920, 0.39074]), + "FL5": np.array([0.31975, 0.34246]), + "FL6": np.array([0.38660, 0.37847]), + "FL7": np.array([0.31569, 0.32960]), + "FL8": np.array([0.34902, 0.35939]), + "FL9": np.array([0.37829, 0.37045]), + "FL10": np.array([0.35090, 0.35444]), + "FL11": np.array([0.38541, 0.37123]), + "FL12": np.array([0.44256, 0.39717]), + "FL3.1": np.array([0.449830684010003, 0.390231404321266]), + "FL3.2": np.array([0.386924116672933, 0.365756034732821]), + "FL3.3": np.array([0.321176986855865, 0.340501092654981]), + "FL3.4": np.array([0.448121275113995, 0.397077112142482]), + "FL3.5": np.array([0.377814166608895, 0.366625766963060]), + "FL3.6": np.array([0.351976478983504, 0.361094432889677]), + "FL3.7": np.array([0.444309208810922, 0.396791387314871]), + "FL3.8": np.array([0.387588931999771, 0.376305569410173]), + "FL3.9": np.array([0.354688990710449, 0.353445033593383]), + "FL3.10": np.array([0.349344792334400, 0.354984421140869]), + "FL3.11": np.array([0.329267975695120, 0.338865386643537]), + "FL3.12": np.array([0.442252080438001, 0.401220551071252]), + "FL3.13": np.array([0.386275268780817, 0.374283190950586]), + "FL3.14": np.array([0.347255078638291, 0.366808242504180]), + "FL3.15": np.array([0.314613997909246, 0.333377149377113]), + "HP1": np.array([0.543334600247307, 0.405289298480431]), + "HP2": np.array([0.482647330648721, 0.410815644179685]), + "HP3": np.array([0.435560034503954, 0.398801084399711]), + "HP4": np.array([0.385193641123543, 0.368275479241015]), + "HP5": np.array([0.380316415606638, 0.366617114797851]), + "LED-B1": np.array([0.462504966271043, 0.403041801546906]), + "LED-B2": np.array([0.442119475258745, 0.396633702892576]), + "LED-B3": np.array([0.380851979328052, 0.368518548904765]), + "LED-B4": np.array([0.348371362473402, 0.345065503264192]), + "LED-B5": np.array([0.316916877024753, 0.322060276350364]), + "LED-BH1": np.array([0.452772610754910, 0.400032462750000]), + "LED-RGB1": np.array([0.457036370583652, 0.425381348780888]), + "LED-V1": np.array([0.453602699414564, 0.398199587905174]), + "LED-V2": np.array([0.377728483834020, 0.374512315539769]), + "ID65": np.array([0.312074043269908, 0.332660121024630]), + "ID50": np.array([0.345621427535976, 0.361228962209198]), + } +) """ Chromaticity coordinates of the *CIE* illuminants for the *CIE 1964 10 Degree Standard Observer*. @@ -298,48 +332,54 @@ References ---------- :cite:`CIETC1-482004h`, :cite:`Wikipedia2006a` - -CCS_ILLUMINANTS_CIE_STANDARD_OBSERVER_10_DEGREE_CIE1964 : \ -CaseInsensitiveMapping """ -CCS_ILLUMINANTS_ISO_STANDARD_OBSERVER_10_DEGREE_CIE1964 = ( - CaseInsensitiveMapping({ - 'ISO 7589 Photographic Daylight': - np.array([0.333716908394534, 0.348592494683065]), - 'ISO 7589 Sensitometric Daylight': - np.array([0.336125906007630, 0.354997062476417]), - 'ISO 7589 Studio Tungsten': - np.array([0.434575926493196, 0.402219691745325]), - 'ISO 7589 Sensitometric Studio Tungsten': - np.array([0.435607674215215, 0.406129244796761]), - 'ISO 7589 Photoflood': - np.array([0.414144647169611, 0.392458587686395]), - 'ISO 7589 Sensitometric Photoflood': - np.array([0.415625819190627, 0.397002292994179]), - 'ISO 7589 Sensitometric Printer': - np.array([0.418841052206998, 0.418695130974955]), - })) +CCS_ILLUMINANTS_ISO_STANDARD_OBSERVER_10_DEGREE_CIE1964: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "ISO 7589 Photographic Daylight": np.array( + [0.333716908394534, 0.348592494683065] + ), + "ISO 7589 Sensitometric Daylight": np.array( + [0.336125906007630, 0.354997062476417] + ), + "ISO 7589 Studio Tungsten": np.array( + [0.434575926493196, 0.402219691745325] + ), + "ISO 7589 Sensitometric Studio Tungsten": np.array( + [0.435607674215215, 0.406129244796761] + ), + "ISO 7589 Photoflood": np.array( + [0.414144647169611, 0.392458587686395] + ), + "ISO 7589 Sensitometric Photoflood": np.array( + [0.415625819190627, 0.397002292994179] + ), + "ISO 7589 Sensitometric Printer": np.array( + [0.418841052206998, 0.418695130974955] + ), + } +) """ Chromaticity coordinates of the *ISO* illuminants for the *CIE 1964 10 Degree Standard Observer*. References ---------- -:cite:`ISO2002` - -CCS_ILLUMINANTS_ISO_STANDARD_OBSERVER_10_DEGREE_CIE1964 : \ -CaseInsensitiveMapping +:cite:`InternationalOrganizationforStandardization2002` """ -CCS_ILLUMINANTS = CaseInsensitiveMapping({ - 'CIE 1931 2 Degree Standard Observer': - CaseInsensitiveMapping( - CCS_ILLUMINANTS_CIE_STANDARD_OBSERVER_2_DEGREE_CIE1931), - 'CIE 1964 10 Degree Standard Observer': - CaseInsensitiveMapping( - CCS_ILLUMINANTS_CIE_STANDARD_OBSERVER_10_DEGREE_CIE1964) -}) +CCS_ILLUMINANTS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "CIE 1931 2 Degree Standard Observer": CaseInsensitiveMapping( + CCS_ILLUMINANTS_CIE_STANDARD_OBSERVER_2_DEGREE_CIE1931 + ), + "CIE 1964 10 Degree Standard Observer": CaseInsensitiveMapping( + CCS_ILLUMINANTS_CIE_STANDARD_OBSERVER_10_DEGREE_CIE1964 + ), + } +) CCS_ILLUMINANTS.__doc__ = """ Chromaticity coordinates of the illuminants. @@ -381,35 +421,43 @@ References ---------- -:cite:`CIETC1-482004h`, :cite:`DigitalCinemaInitiatives2007b`, :cite:`ISO2002`, +:cite:`CIETC1-482004h`, :cite:`DigitalCinemaInitiatives2007b`, +:cite:`InternationalOrganizationforStandardization2002`, :cite:`InternationalColorConsortium2010`, :cite:`TheAcademyofMotionPictureArtsandSciences2014q`, :cite:`Wikipedia2006a` -CCS_ILLUMINANTS : CaseInsensitiveMapping - **{'CIE 1931 2 Degree Standard Observer', - 'CIE 1964 10 Degree Standard Observer'}** - Aliases: - 'cie_2_1931': 'CIE 1931 2 Degree Standard Observer' - 'cie_10_1964': 'CIE 1964 10 Degree Standard Observer' """ -CCS_ILLUMINANTS['cie_2_1931'] = ( - CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']) -CCS_ILLUMINANTS['cie_10_1964'] = ( - CCS_ILLUMINANTS['CIE 1964 10 Degree Standard Observer']) +CCS_ILLUMINANTS["cie_2_1931"] = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +] +CCS_ILLUMINANTS["cie_10_1964"] = CCS_ILLUMINANTS[ + "CIE 1964 10 Degree Standard Observer" +] + +CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"].update( + CCS_ILLUMINANTS_ACES_STANDARD_OBSERVER_2_DEGREE_CIE1931 +) -CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'].update( - CCS_ILLUMINANTS_ACES_STANDARD_OBSERVER_2_DEGREE_CIE1931) +CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"].update( + CCS_ILLUMINANTS_BLACKMAGIC_DESIGN_STANDARD_OBSERVER_2_DEGREE_CIE1931 +) -CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'].update( - CCS_ILLUMINANTS_DCI_STANDARD_OBSERVER_2_DEGREE_CIE1931) +CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"].update( + CCS_ILLUMINANTS_DCI_STANDARD_OBSERVER_2_DEGREE_CIE1931 +) -CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'].update( - CCS_ILLUMINANTS_ICC_STANDARD_OBSERVER_2_DEGREE_CIE1931) +CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"].update( + CCS_ILLUMINANTS_ICC_STANDARD_OBSERVER_2_DEGREE_CIE1931 +) -CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'].update( - CCS_ILLUMINANTS_ISO_STANDARD_OBSERVER_2_DEGREE_CIE1931) +CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"].update( + CCS_ILLUMINANTS_ISO_STANDARD_OBSERVER_2_DEGREE_CIE1931 +) -CCS_ILLUMINANTS['CIE 1964 10 Degree Standard Observer'].update( - CCS_ILLUMINANTS_ISO_STANDARD_OBSERVER_10_DEGREE_CIE1964) +CCS_ILLUMINANTS["CIE 1964 10 Degree Standard Observer"].update( + CCS_ILLUMINANTS_ISO_STANDARD_OBSERVER_10_DEGREE_CIE1964 +) diff --git a/colour/colorimetry/datasets/illuminants/hunterlab.py b/colour/colorimetry/datasets/illuminants/hunterlab.py index e196099341..f09f5cc589 100644 --- a/colour/colorimetry/datasets/illuminants/hunterlab.py +++ b/colour/colorimetry/datasets/illuminants/hunterlab.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ -Tristimulus Values of the Hunter L,a,b Illuminants -================================================== +CIE XYZ Tristimulus Values of the Hunter L,a,b Illuminants +========================================================== -Defines the tristimulus values of the *Hunter L,a,b* illuminants dataset for -the *CIE 1931 2 Degree Standard Observer* and +Defines the *CIE XYZ* tristimulus values of the *Hunter L,a,b* illuminants +dataset for the *CIE 1931 2 Degree Standard Observer* and *CIE 1964 10 Degree Standard Observer*. The currently implemented data has been extracted from :cite:`HunterLab2008b`, @@ -21,113 +20,117 @@ an02_02.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from collections import namedtuple +from colour.hints import Tuple from colour.utilities import CaseInsensitiveMapping -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'Illuminant_Specification_HunterLab', - 'DATA_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_2_DEGREE_CIE1931', - 'TVS_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_2_DEGREE_CIE1931', - 'DATA_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_10_DEGREE_CIE1964', - 'TVS_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_10_DEGREE_CIE1964', - 'TVS_ILLUMINANTS_HUNTERLAB' + "Illuminant_Specification_HunterLab", + "DATA_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_2_DEGREE_CIE1931", + "TVS_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_2_DEGREE_CIE1931", + "DATA_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_10_DEGREE_CIE1964", + "TVS_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_10_DEGREE_CIE1964", + "TVS_ILLUMINANTS_HUNTERLAB", ] Illuminant_Specification_HunterLab = namedtuple( - 'Illuminant_Specification_HunterLab', ('name', 'XYZ_n', 'K_ab')) - -DATA_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_2_DEGREE_CIE1931 = ( - ('A', np.array([109.83, 100.00, 35.55]), np.array([185.20, 38.40])), - ('C', np.array([98.04, 100.00, 118.11]), np.array([175.00, 70.00])), - ('D50', np.array([96.38, 100.00, 82.45]), np.array([173.51, 58.48])), - ('D60', np.array([95.23, 100.00, 100.86]), np.array([172.47, 64.72])), - ('D65', np.array([95.02, 100.00, 108.82]), np.array([172.30, 67.20])), - ('D75', np.array([94.96, 100.00, 122.53]), np.array([172.22, 71.30])), - ('FL2', np.array([98.09, 100.00, 67.53]), np.array([175.00, 52.90])), - ('TL 4', np.array([101.40, 100.00, 65.90]), np.array([178.00, 52.30])), - ('UL 3000', np.array([107.99, 100.00, 33.91]), np.array([183.70, 37.50])), + "Illuminant_Specification_HunterLab", ("name", "XYZ_n", "K_ab") ) -TVS_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_2_DEGREE_CIE1931 = ( - CaseInsensitiveMapping({ +DATA_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_2_DEGREE_CIE1931: Tuple = ( + ("A", np.array([109.83, 100.00, 35.55]), np.array([185.20, 38.40])), + ("C", np.array([98.04, 100.00, 118.11]), np.array([175.00, 70.00])), + ("D50", np.array([96.38, 100.00, 82.45]), np.array([173.51, 58.48])), + ("D60", np.array([95.23, 100.00, 100.86]), np.array([172.47, 64.72])), + ("D65", np.array([95.02, 100.00, 108.82]), np.array([172.30, 67.20])), + ("D75", np.array([94.96, 100.00, 122.53]), np.array([172.22, 71.30])), + ("FL2", np.array([98.09, 100.00, 67.53]), np.array([175.00, 52.90])), + ("TL 4", np.array([101.40, 100.00, 65.90]), np.array([178.00, 52.30])), + ("UL 3000", np.array([107.99, 100.00, 33.91]), np.array([183.70, 37.50])), +) + +TVS_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_2_DEGREE_CIE1931: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { x[0]: Illuminant_Specification_HunterLab(*x) for x in DATA_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_2_DEGREE_CIE1931 - })) + } +) """ -Tristimulus values of the *Hunter L,a,b* illuminants for the +*CIE XYZ* tristimulus values of the *Hunter L,a,b* illuminants for the *CIE 1931 2 Degree Standard Observer*. References ---------- :cite:`HunterLab2008b`, :cite:`HunterLab2008c` - -TVS_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_2_DEGREE_CIE1931 : - CaseInsensitiveMapping """ -DATA_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_10_DEGREE_CIE1964 = ( - ('A', np.array([111.16, 100.00, 35.19]), np.array([186.30, 38.20])), - ('C', np.array([97.30, 100.00, 116.14]), np.array([174.30, 69.40])), - ('D50', np.array([96.72, 100.00, 81.45]), np.array([173.82, 58.13])), - ('D60', np.array([95.21, 100.00, 99.60]), np.array([172.45, 64.28])), - ('D65', np.array([94.83, 100.00, 107.38]), np.array([172.10, 66.70])), - ('D75', np.array([94.45, 100.00, 120.70]), np.array([171.76, 70.76])), - ('FL2', np.array([102.13, 100.00, 69.37]), np.array([178.60, 53.60])), - ('TL 4', np.array([103.82, 100.00, 66.90]), np.array([180.10, 52.70])), - ('UL 3000', np.array([111.12, 100.00, 35.21]), np.array([186.30, 38.20])), +DATA_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_10_DEGREE_CIE1964: Tuple = ( + ("A", np.array([111.16, 100.00, 35.19]), np.array([186.30, 38.20])), + ("C", np.array([97.30, 100.00, 116.14]), np.array([174.30, 69.40])), + ("D50", np.array([96.72, 100.00, 81.45]), np.array([173.82, 58.13])), + ("D60", np.array([95.21, 100.00, 99.60]), np.array([172.45, 64.28])), + ("D65", np.array([94.83, 100.00, 107.38]), np.array([172.10, 66.70])), + ("D75", np.array([94.45, 100.00, 120.70]), np.array([171.76, 70.76])), + ("FL2", np.array([102.13, 100.00, 69.37]), np.array([178.60, 53.60])), + ("TL 4", np.array([103.82, 100.00, 66.90]), np.array([180.10, 52.70])), + ("UL 3000", np.array([111.12, 100.00, 35.21]), np.array([186.30, 38.20])), ) -TVS_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_10_DEGREE_CIE1964 = ( - CaseInsensitiveMapping({ +TVS_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_10_DEGREE_CIE1964: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { x[0]: Illuminant_Specification_HunterLab(*x) for x in DATA_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_10_DEGREE_CIE1964 - })) + } +) """ -Tristimulus values of the *Hunter L,a,b* illuminants for the +*CIE XYZ* tristimulus values of the *Hunter L,a,b* illuminants for the *CIE 1964 10 Degree Standard Observer*. References ---------- :cite:`HunterLab2008b`, :cite:`HunterLab2008c` - -TVS_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_10_DEGREE_CIE1964 : - CaseInsensitiveMapping """ -TVS_ILLUMINANTS_HUNTERLAB = CaseInsensitiveMapping({ - 'CIE 1931 2 Degree Standard Observer': - TVS_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_2_DEGREE_CIE1931, - 'CIE 1964 10 Degree Standard Observer': - TVS_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_10_DEGREE_CIE1964 -}) +TVS_ILLUMINANTS_HUNTERLAB: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "CIE 1931 2 Degree Standard Observer": ( + TVS_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_2_DEGREE_CIE1931 + ), + "CIE 1964 10 Degree Standard Observer": ( + TVS_ILLUMINANTS_HUNTERLAB_STANDARD_OBSERVER_10_DEGREE_CIE1964 + ), + } +) TVS_ILLUMINANTS_HUNTERLAB.__doc__ = """ -Tristimulus values of the *HunterLab* illuminants. +*CIE XYZ* tristimulus values of the *HunterLab* illuminants. References ---------- :cite:`HunterLab2008b`, :cite:`HunterLab2008c` -TVS_ILLUMINANTS_HUNTERLAB : CaseInsensitiveMapping - **{'CIE 1931 2 Degree Standard Observer', - 'CIE 1964 10 Degree Standard Observer'}** - Aliases: - 'cie_2_1931': 'CIE 1931 2 Degree Standard Observer' - 'cie_10_1964': 'CIE 1964 10 Degree Standard Observer' """ -TVS_ILLUMINANTS_HUNTERLAB['cie_2_1931'] = ( - TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer']) -TVS_ILLUMINANTS_HUNTERLAB['cie_10_1964'] = ( - TVS_ILLUMINANTS_HUNTERLAB['CIE 1964 10 Degree Standard Observer']) +TVS_ILLUMINANTS_HUNTERLAB["cie_2_1931"] = TVS_ILLUMINANTS_HUNTERLAB[ + "CIE 1931 2 Degree Standard Observer" +] +TVS_ILLUMINANTS_HUNTERLAB["cie_10_1964"] = TVS_ILLUMINANTS_HUNTERLAB[ + "CIE 1964 10 Degree Standard Observer" +] diff --git a/colour/colorimetry/datasets/illuminants/sds.py b/colour/colorimetry/datasets/illuminants/sds.py index 831edf3f28..2de988fed3 100644 --- a/colour/colorimetry/datasets/illuminants/sds.py +++ b/colour/colorimetry/datasets/illuminants/sds.py @@ -1,7 +1,6 @@ -# -*- coding: utf-8 -*- """ Spectral Distributions of the Illuminants -================================= +========================================= Defines the spectral distributions of the illuminants. @@ -51,31 +50,38 @@ S., Luo, M. R., Melgosa, M., Ohno, Y., Pointer, M. R., Rich, D. C., Vienot, F., Whitehead, L., & Wold, J. H. (2018). CIE 015:2018 Colorimetry, 4th Edition. International Commission on Illumination. doi:10.25039/TR.015.2018 -- :cite:`ISO2002` : ISO. (2002). INTERNATIONAL STANDARD 7589-2002 - - Photography - Illuminants for sensitometry - Specifications for daylight, - incandescent tungsten and printer. +- :cite:`InternationalOrganizationforStandardization2002` : International + Organization for Standardization. (2002). INTERNATIONAL STANDARD ISO + 7589-2002 - Photography - Illuminants for sensitometry - Specifications for + daylight, incandescent tungsten and printer. """ -from __future__ import division, unicode_literals +from __future__ import annotations + +from functools import partial from colour.algebra import LinearInterpolator from colour.colorimetry.spectrum import SpectralDistribution -from colour.utilities import CaseInsensitiveMapping +from colour.hints import Dict +from colour.utilities import LazyCaseInsensitiveMapping -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'DATA_ILLUMINANTS_CIE', 'SDS_ILLUMINANTS_CIE', 'DATA_ILLUMINANTS_ISO', - 'SDS_ILLUMINANTS_ISO', 'SDS_ILLUMINANTS' + "DATA_ILLUMINANTS_CIE", + "SDS_ILLUMINANTS_CIE", + "DATA_ILLUMINANTS_ISO", + "SDS_ILLUMINANTS_ISO", + "SDS_ILLUMINANTS", ] -DATA_ILLUMINANTS_CIE = { - 'A': { +DATA_ILLUMINANTS_CIE: Dict = { + "A": { 300: 0.930483, 305: 1.128210, 310: 1.357690, @@ -172,9 +178,9 @@ 765: 234.589000, 770: 237.008000, 775: 239.370000, - 780: 241.675000 + 780: 241.675000, }, - 'B': { + "B": { 320: 0.02, 325: 0.26, 330: 0.50, @@ -267,9 +273,9 @@ 765: 84.90, 770: 85.40, 775: 86.10, - 780: 87.00 + 780: 87.00, }, - 'C': { + "C": { 300: 0.00, 305: 0.00, 310: 0.00, @@ -366,9 +372,9 @@ 765: 58.00, 770: 58.20, 775: 58.50, - 780: 59.10 + 780: 59.10, }, - 'D50': { + "D50": { 300: 0.019, 305: 1.035, 310: 2.051, @@ -465,9 +471,9 @@ 765: 70.307, 770: 82.923, 775: 80.599, - 780: 78.274 + 780: 78.274, }, - 'D55': { + "D55": { 300: 0.024, 305: 1.048, 310: 2.072, @@ -564,9 +570,9 @@ 765: 64.360, 770: 75.927, 775: 73.872, - 780: 71.818 + 780: 71.818, }, - 'D60': { + "D60": { 300: 0.029300000000000, 305: 1.289900000000000, 310: 2.550500000000000, @@ -673,9 +679,9 @@ 815: 57.786200000000001, 820: 60.697400000000002, 825: 62.226949999999995, - 830: 63.756500000000003 + 830: 63.756500000000003, }, - 'D65': { + "D65": { 300: 0.034100, 305: 1.664300, 310: 3.294500, @@ -772,9 +778,9 @@ 765: 56.611800, 770: 66.805400, 775: 65.094100, - 780: 63.382800 + 780: 63.382800, }, - 'D75': { + "D75": { 300: 0.043, 305: 2.588, 310: 5.133, @@ -871,21 +877,9 @@ 765: 51.985, 770: 61.352, 775: 59.838, - 780: 58.324 + 780: 58.324, }, - 'E': { - 300: 100.000, - 305: 100.000, - 310: 100.000, - 315: 100.000, - 320: 100.000, - 325: 100.000, - 330: 100.000, - 335: 100.000, - 340: 100.000, - 345: 100.000, - 350: 100.000, - 355: 100.000, + "E": { 360: 100.000, 365: 100.000, 370: 100.000, @@ -970,9 +964,19 @@ 765: 100.000, 770: 100.000, 775: 100.000, - 780: 100.000 + 780: 100.000, + 785: 100.000, + 790: 100.000, + 795: 100.000, + 800: 100.000, + 805: 100.000, + 810: 100.000, + 815: 100.000, + 820: 100.000, + 825: 100.000, + 830: 100.000, }, - 'FL1': { + "FL1": { 380: 1.87, 385: 2.36, 390: 2.94, @@ -1053,9 +1057,9 @@ 765: 0.69, 770: 0.61, 775: 0.52, - 780: 0.43 + 780: 0.43, }, - 'FL2': { + "FL2": { 380: 1.18, 385: 1.48, 390: 1.84, @@ -1136,9 +1140,9 @@ 765: 0.47, 770: 0.40, 775: 0.33, - 780: 0.27 + 780: 0.27, }, - 'FL3': { + "FL3": { 380: 0.82, 385: 1.02, 390: 1.26, @@ -1219,9 +1223,9 @@ 765: 0.39, 770: 0.33, 775: 0.28, - 780: 0.21 + 780: 0.21, }, - 'FL4': { + "FL4": { 380: 0.57, 385: 0.70, 390: 0.87, @@ -1302,9 +1306,9 @@ 765: 0.36, 770: 0.31, 775: 0.26, - 780: 0.19 + 780: 0.19, }, - 'FL5': { + "FL5": { 380: 1.87, 385: 2.35, 390: 2.92, @@ -1385,9 +1389,9 @@ 765: 0.64, 770: 0.55, 775: 0.47, - 780: 0.40 + 780: 0.40, }, - 'FL6': { + "FL6": { 380: 1.05, 385: 1.31, 390: 1.63, @@ -1468,9 +1472,9 @@ 765: 0.41, 770: 0.33, 775: 0.26, - 780: 0.21 + 780: 0.21, }, - 'FL7': { + "FL7": { 380: 2.56, 385: 3.18, 390: 3.84, @@ -1551,9 +1555,9 @@ 765: 1.32, 770: 1.17, 775: 0.99, - 780: 0.81 + 780: 0.81, }, - 'FL8': { + "FL8": { 380: 1.21, 385: 1.50, 390: 1.81, @@ -1634,9 +1638,9 @@ 765: 2.15, 770: 1.89, 775: 1.61, - 780: 1.32 + 780: 1.32, }, - 'FL9': { + "FL9": { 380: 0.90, 385: 1.12, 390: 1.36, @@ -1717,9 +1721,9 @@ 765: 1.83, 770: 1.61, 775: 1.38, - 780: 1.12 + 780: 1.12, }, - 'FL10': { + "FL10": { 380: 1.11, 385: 0.80, 390: 0.62, @@ -1800,9 +1804,9 @@ 765: 0.22, 770: 0.17, 775: 0.12, - 780: 0.09 + 780: 0.09, }, - 'FL11': { + "FL11": { 380: 0.91, 385: 0.63, 390: 0.46, @@ -1883,9 +1887,9 @@ 765: 0.26, 770: 0.16, 775: 0.12, - 780: 0.09 + 780: 0.09, }, - 'FL12': { + "FL12": { 380: 0.96, 385: 0.64, 390: 0.40, @@ -1966,9 +1970,9 @@ 765: 0.19, 770: 0.15, 775: 0.10, - 780: 0.05 + 780: 0.05, }, - 'FL3.1': { + "FL3.1": { 380: 2.39, 385: 2.93, 390: 3.82, @@ -2049,9 +2053,9 @@ 765: 0.95, 770: 1.50, 775: 0.89, - 780: 0.68 + 780: 0.68, }, - 'FL3.2': { + "FL3.2": { 380: 5.80, 385: 6.99, 390: 8.70, @@ -2132,9 +2136,9 @@ 765: 4.01, 770: 4.09, 775: 3.30, - 780: 2.82 + 780: 2.82, }, - 'FL3.3': { + "FL3.3": { 380: 8.94, 385: 11.21, 390: 14.08, @@ -2215,9 +2219,9 @@ 765: 2.19, 770: 2.71, 775: 2.00, - 780: 1.80 + 780: 1.80, }, - 'FL3.4': { + "FL3.4": { 380: 3.46, 385: 3.86, 390: 4.41, @@ -2298,9 +2302,9 @@ 765: 13.63, 770: 10.43, 775: 9.67, - 780: 8.07 + 780: 8.07, }, - 'FL3.5': { + "FL3.5": { 380: 4.72, 385: 5.82, 390: 7.18, @@ -2381,9 +2385,9 @@ 765: 12.42, 770: 9.43, 775: 8.96, - 780: 7.39 + 780: 7.39, }, - 'FL3.6': { + "FL3.6": { 380: 5.53, 385: 6.63, 390: 8.07, @@ -2464,9 +2468,9 @@ 765: 11.28, 770: 8.51, 775: 8.24, - 780: 7.02 + 780: 7.02, }, - 'FL3.7': { + "FL3.7": { 380: 3.79, 385: 2.56, 390: 1.91, @@ -2547,9 +2551,9 @@ 765: 0.46, 770: 0.99, 775: 0.43, - 780: 0.00 + 780: 0.00, }, - 'FL3.8': { + "FL3.8": { 380: 4.18, 385: 2.93, 390: 2.29, @@ -2630,9 +2634,9 @@ 765: 0.61, 770: 1.25, 775: 0.79, - 780: 0.58 + 780: 0.58, }, - 'FL3.9': { + "FL3.9": { 380: 3.77, 385: 2.64, 390: 2.06, @@ -2713,9 +2717,9 @@ 765: 0.10, 770: 0.68, 775: 0.16, - 780: 0.00 + 780: 0.00, }, - 'FL3.10': { + "FL3.10": { 380: 0.25, 385: 0.00, 390: 0.00, @@ -2796,9 +2800,9 @@ 765: 0.00, 770: 0.00, 775: 0.00, - 780: 0.00 + 780: 0.00, }, - 'FL3.11': { + "FL3.11": { 380: 3.85, 385: 2.91, 390: 2.56, @@ -2879,9 +2883,9 @@ 765: 0.45, 770: 1.04, 775: 0.45, - 780: 0.00 + 780: 0.00, }, - 'FL3.12': { + "FL3.12": { 380: 1.62, 385: 2.06, 390: 2.71, @@ -2962,9 +2966,9 @@ 765: 4.50, 770: 4.81, 775: 3.72, - 780: 3.28 + 780: 3.28, }, - 'FL3.13': { + "FL3.13": { 380: 2.23, 385: 2.92, 390: 3.91, @@ -3045,9 +3049,9 @@ 765: 3.74, 770: 4.04, 775: 3.14, - 780: 2.75 + 780: 2.75, }, - 'FL3.14': { + "FL3.14": { 380: 2.87, 385: 3.69, 390: 4.87, @@ -3128,9 +3132,9 @@ 765: 4.08, 770: 4.43, 775: 3.39, - 780: 3.17 + 780: 3.17, }, - 'FL3.15': { + "FL3.15": { 380: 300, 385: 286, 390: 268, @@ -3211,9 +3215,9 @@ 765: 51, 770: 46, 775: 41, - 780: 37 + 780: 37, }, - 'HP1': { + "HP1": { 380: 1.90, 385: 2.20, 390: 2.50, @@ -3294,9 +3298,9 @@ 765: 31.97, 770: 27.87, 775: 5.89, - 780: 6.69 + 780: 6.69, }, - 'HP2': { + "HP2": { 380: 2.64, 385: 2.77, 390: 3.42, @@ -3377,9 +3381,9 @@ 765: 83.04, 770: 86.25, 775: 63.93, - 780: 64.92 + 780: 64.92, }, - 'HP3': { + "HP3": { 380: 3.15, 385: 7.49, 390: 10.87, @@ -3460,9 +3464,9 @@ 765: 17.81, 770: 16.07, 775: 14.83, - 780: 14.61 + 780: 14.61, }, - 'HP4': { + "HP4": { 380: 9.80, 385: 13.30, 390: 19.97, @@ -3543,9 +3547,9 @@ 765: 24.46, 770: 22.05, 775: 16.11, - 780: 12.91 + 780: 12.91, }, - 'HP5': { + "HP5": { 380: 0.34, 385: 7.11, 390: 11.49, @@ -3626,9 +3630,9 @@ 765: 34.58, 770: 30.21, 775: 19.71, - 780: 15.61 + 780: 15.61, }, - 'LED-B1': { + "LED-B1": { 380: 0.00, 385: 0.01, 390: 0.01, @@ -3709,9 +3713,9 @@ 765: 0.86, 770: 0.76, 775: 0.68, - 780: 0.61 + 780: 0.61, }, - 'LED-B2': { + "LED-B2": { 380: 0.00, 385: 0.00, 390: 0.00, @@ -3792,9 +3796,9 @@ 765: 0.76, 770: 0.67, 775: 0.59, - 780: 0.52 + 780: 0.52, }, - 'LED-B3': { + "LED-B3": { 380: 0.00, 385: 0.00, 390: 0.01, @@ -3875,9 +3879,9 @@ 765: 0.43, 770: 0.37, 775: 0.32, - 780: 0.28 + 780: 0.28, }, - 'LED-B4': { + "LED-B4": { 380: 0.00, 385: 0.00, 390: 0.00, @@ -3958,9 +3962,9 @@ 765: 0.55, 770: 0.50, 775: 0.45, - 780: 0.42 + 780: 0.42, }, - 'LED-B5': { + "LED-B5": { 380: 0.00, 385: 0.01, 390: 0.01, @@ -4041,9 +4045,9 @@ 765: 0.39, 770: 0.35, 775: 0.31, - 780: 0.27 + 780: 0.27, }, - 'LED-BH1': { + "LED-BH1": { 380: 0.00, 385: 0.01, 390: 0.02, @@ -4124,9 +4128,9 @@ 765: 0.34, 770: 0.30, 775: 0.28, - 780: 0.25 + 780: 0.25, }, - 'LED-RGB1': { + "LED-RGB1": { 380: 0.00, 385: 0.00, 390: 0.00, @@ -4207,9 +4211,9 @@ 765: 0.27, 770: 0.24, 775: 0.21, - 780: 0.17 + 780: 0.17, }, - 'LED-V1': { + "LED-V1": { 380: 0.01, 385: 0.03, 390: 0.14, @@ -4290,9 +4294,9 @@ 765: 1.90, 770: 1.65, 775: 1.44, - 780: 1.22 + 780: 1.22, }, - 'LED-V2': { + "LED-V2": { 380: 0.01, 385: 0.06, 390: 0.26, @@ -4373,9 +4377,9 @@ 765: 1.30, 770: 1.10, 775: 0.96, - 780: 0.84 + 780: 0.84, }, - 'ID65': { + "ID65": { 300: 0.00, 305: 0.00, 310: 0.00, @@ -4472,9 +4476,9 @@ 765: 49.09, 770: 57.70, 775: 56.00, - 780: 54.30 + 780: 54.30, }, - 'ID50': { + "ID50": { 300: 0.00, 305: 0.00, 310: 0.00, @@ -4571,117 +4575,22 @@ 765: 60.97, 770: 71.62, 775: 69.34, - 780: 67.06 - } + 780: 67.06, + }, } -SDS_ILLUMINANTS_CIE = CaseInsensitiveMapping({ - 'A': - SpectralDistribution(DATA_ILLUMINANTS_CIE['A'], name='A'), - 'B': - SpectralDistribution(DATA_ILLUMINANTS_CIE['B'], name='B'), - 'C': - SpectralDistribution(DATA_ILLUMINANTS_CIE['C'], name='C'), - 'D50': - SpectralDistribution(DATA_ILLUMINANTS_CIE['D50'], name='D50'), - 'D55': - SpectralDistribution(DATA_ILLUMINANTS_CIE['D55'], name='D55'), - 'D60': - SpectralDistribution(DATA_ILLUMINANTS_CIE['D60'], name='D60'), - 'D65': - SpectralDistribution(DATA_ILLUMINANTS_CIE['D65'], name='D65'), - 'D75': - SpectralDistribution(DATA_ILLUMINANTS_CIE['D75'], name='D75'), - 'E': - SpectralDistribution(DATA_ILLUMINANTS_CIE['E'], name='E'), - 'FL1': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL1'], name='FL1'), - 'FL2': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL2'], name='FL2'), - 'FL3': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL3'], name='FL3'), - 'FL4': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL4'], name='FL4'), - 'FL5': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL5'], name='FL5'), - 'FL6': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL6'], name='FL6'), - 'FL7': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL7'], name='FL7'), - 'FL8': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL8'], name='FL8'), - 'FL9': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL9'], name='FL9'), - 'FL10': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL10'], name='FL10'), - 'FL11': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL11'], name='FL11'), - 'FL12': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL12'], name='FL12'), - 'FL3.1': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL3.1'], name='FL3.1'), - 'FL3.2': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL3.2'], name='FL3.2'), - 'FL3.3': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL3.3'], name='FL3.3'), - 'FL3.4': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL3.4'], name='FL3.4'), - 'FL3.5': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL3.5'], name='FL3.5'), - 'FL3.6': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL3.6'], name='FL3.6'), - 'FL3.7': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL3.7'], name='FL3.7'), - 'FL3.8': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL3.8'], name='FL3.8'), - 'FL3.9': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL3.9'], name='FL3.9'), - 'FL3.10': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL3.10'], name='FL3.10'), - 'FL3.11': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL3.11'], name='FL3.11'), - 'FL3.12': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL3.12'], name='FL3.12'), - 'FL3.13': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL3.13'], name='FL3.13'), - 'FL3.14': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL3.14'], name='FL3.14'), - 'FL3.15': - SpectralDistribution(DATA_ILLUMINANTS_CIE['FL3.15'], name='FL3.15'), - 'HP1': - SpectralDistribution(DATA_ILLUMINANTS_CIE['HP1'], name='HP1'), - 'HP2': - SpectralDistribution(DATA_ILLUMINANTS_CIE['HP2'], name='HP2'), - 'HP3': - SpectralDistribution(DATA_ILLUMINANTS_CIE['HP3'], name='HP3'), - 'HP4': - SpectralDistribution(DATA_ILLUMINANTS_CIE['HP4'], name='HP4'), - 'HP5': - SpectralDistribution(DATA_ILLUMINANTS_CIE['HP5'], name='HP5'), - 'LED-B1': - SpectralDistribution(DATA_ILLUMINANTS_CIE['LED-B1'], name='LED-B1'), - 'LED-B2': - SpectralDistribution(DATA_ILLUMINANTS_CIE['LED-B2'], name='LED-B2'), - 'LED-B3': - SpectralDistribution(DATA_ILLUMINANTS_CIE['LED-B3'], name='LED-B3'), - 'LED-B4': - SpectralDistribution(DATA_ILLUMINANTS_CIE['LED-B4'], name='LED-B4'), - 'LED-B5': - SpectralDistribution(DATA_ILLUMINANTS_CIE['LED-B5'], name='LED-B5'), - 'LED-BH1': - SpectralDistribution(DATA_ILLUMINANTS_CIE['LED-BH1'], name='LED-BH1'), - 'LED-RGB1': - SpectralDistribution( - DATA_ILLUMINANTS_CIE['LED-RGB1'], name='LED-RGB1'), - 'LED-V1': - SpectralDistribution(DATA_ILLUMINANTS_CIE['LED-V1'], name='LED-V1'), - 'LED-V2': - SpectralDistribution(DATA_ILLUMINANTS_CIE['LED-V2'], name='LED-V2'), - 'ID65': - SpectralDistribution(DATA_ILLUMINANTS_CIE['ID65'], name='ID65'), - 'ID50': - SpectralDistribution(DATA_ILLUMINANTS_CIE['ID50'], name='ID50'), -}) +SDS_ILLUMINANTS_CIE: LazyCaseInsensitiveMapping = LazyCaseInsensitiveMapping( + ( + key, + partial( + SpectralDistribution, + value, + name=key, + interpolator=LinearInterpolator, + ), + ) + for key, value in DATA_ILLUMINANTS_CIE.items() +) SDS_ILLUMINANTS_CIE.__doc__ = """ Spectral distributions of the *CIE* illuminants. @@ -4699,12 +4608,10 @@ References ---------- :cite:`Carter2018`, :cite:`CIEce`, :cite:`CIEcf` - -SDS_ILLUMINANTS_CIE : CaseInsensitiveMapping """ -DATA_ILLUMINANTS_ISO = { - 'ISO 7589 Photographic Daylight': { +DATA_ILLUMINANTS_ISO: Dict = { + "ISO 7589 Photographic Daylight": { 350: 28, 360: 31, 370: 34, @@ -4741,7 +4648,7 @@ 680: 90, 690: 80, }, - 'ISO 7589 Sensitometric Daylight': { + "ISO 7589 Sensitometric Daylight": { 350: 0.00, 360: 2.17, 370: 7.82, @@ -4778,7 +4685,7 @@ 680: 84.60, 690: 75.20, }, - 'ISO 7589 Studio Tungsten': { + "ISO 7589 Studio Tungsten": { 350: 1, 360: 3, 370: 5, @@ -4815,7 +4722,7 @@ 680: 162, 690: 167, }, - 'ISO 7589 Sensitometric Studio Tungsten': { + "ISO 7589 Sensitometric Studio Tungsten": { 350: 0.00, 360: 0.21, 370: 1.15, @@ -4852,7 +4759,7 @@ 680: 152.28, 690: 156.98, }, - 'ISO 7589 Photoflood': { + "ISO 7589 Photoflood": { 350: 11, 360: 14, 370: 16, @@ -4889,7 +4796,7 @@ 680: 144, 690: 146, }, - 'ISO 7589 Sensitometric Photoflood': { + "ISO 7589 Sensitometric Photoflood": { 350: 0.00, 360: 0.98, 370: 3.68, @@ -4926,7 +4833,7 @@ 680: 135.36, 690: 137.24, }, - 'ISO 7589 Sensitometric Printer': { + "ISO 7589 Sensitometric Printer": { 350: 0.00, 360: 0.00, 370: 0.00, @@ -4949,39 +4856,21 @@ 540: 87.12, 550: 94.00, 560: 100.00, - } + }, } -SDS_ILLUMINANTS_ISO = CaseInsensitiveMapping({ - 'ISO 7589 Photographic Daylight': - SpectralDistribution( - DATA_ILLUMINANTS_ISO['ISO 7589 Photographic Daylight'], - name='ISO 7589 Photographic Daylight'), - 'ISO 7589 Sensitometric Daylight': - SpectralDistribution( - DATA_ILLUMINANTS_ISO['ISO 7589 Sensitometric Daylight'], - name='ISO 7589 Sensitometric Daylight'), - 'ISO 7589 Studio Tungsten': - SpectralDistribution( - DATA_ILLUMINANTS_ISO['ISO 7589 Studio Tungsten'], - name='ISO 7589 Studio Tungsten'), - 'ISO 7589 Sensitometric Studio Tungsten': - SpectralDistribution( - DATA_ILLUMINANTS_ISO['ISO 7589 Sensitometric Studio Tungsten'], - name='ISO 7589 Sensitometric Studio Tungsten'), - 'ISO 7589 Photoflood': - SpectralDistribution( - DATA_ILLUMINANTS_ISO['ISO 7589 Photoflood'], - name='ISO 7589 Photoflood'), - 'ISO 7589 Sensitometric Photoflood': - SpectralDistribution( - DATA_ILLUMINANTS_ISO['ISO 7589 Sensitometric Photoflood'], - name='ISO 7589 Sensitometric Photoflood'), - 'ISO 7589 Sensitometric Printer': - SpectralDistribution( - DATA_ILLUMINANTS_ISO['ISO 7589 Sensitometric Printer'], - name='ISO 7589 Sensitometric Printer'), -}) +SDS_ILLUMINANTS_ISO: LazyCaseInsensitiveMapping = LazyCaseInsensitiveMapping( + ( + key, + partial( + SpectralDistribution, + value, + name=key, + interpolator=LinearInterpolator, + ), + ) + for key, value in DATA_ILLUMINANTS_ISO.items() +) SDS_ILLUMINANTS_ISO.__doc__ = """ Spectral distributions of the *ISO* illuminants. @@ -5004,26 +4893,25 @@ References ---------- -:cite:`ISO2002` - -SDS_ILLUMINANTS_ISO : CaseInsensitiveMapping +:cite:`InternationalOrganizationforStandardization2002` """ -SDS_ILLUMINANTS = CaseInsensitiveMapping(SDS_ILLUMINANTS_CIE) +SDS_ILLUMINANTS: LazyCaseInsensitiveMapping = LazyCaseInsensitiveMapping( + SDS_ILLUMINANTS_CIE +) SDS_ILLUMINANTS.__doc__ = """ Spectral distributions of the illuminants. +Notes +----- +- *CIE 15:2004* recommends using linear interpolation for + *CIE Standard Illuminant D Series*, for consistency all the illuminants are + using a linear interpolator. + References ---------- -:cite:`Carter2018`, :cite:`CIEce`, :cite:`CIEcf`, :cite:`ISO2002` - -SDS_ILLUMINANTS : CaseInsensitiveMapping +:cite:`Carter2018`, :cite:`CIEce`, :cite:`CIEcf`, +:cite:`InternationalOrganizationforStandardization2002` """ SDS_ILLUMINANTS.update(SDS_ILLUMINANTS_ISO) - -# *CIE 15:2004* recommends using linear interpolation for -# *CIE Standard Illuminant D Series*, for consistency all the illuminants are -# using a linear interpolator. -for _sd in SDS_ILLUMINANTS.values(): - _sd.interpolator = LinearInterpolator diff --git a/colour/colorimetry/datasets/illuminants/sds_d_illuminant_series.py b/colour/colorimetry/datasets/illuminants/sds_d_illuminant_series.py index 72b170922b..239cfed7ad 100644 --- a/colour/colorimetry/datasets/illuminants/sds_d_illuminant_series.py +++ b/colour/colorimetry/datasets/illuminants/sds_d_illuminant_series.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ CIE Illuminant D Series :math:`S_n(\\lambda)` Distributions =========================================================== @@ -18,25 +17,28 @@ ISBN:978-0-471-39918-6 """ -from __future__ import division, unicode_literals +from __future__ import annotations + +from functools import partial from colour.colorimetry import SpectralDistribution -from colour.utilities import CaseInsensitiveMapping +from colour.hints import Dict +from colour.utilities import LazyCaseInsensitiveMapping -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'DATA_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES', - 'SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES' + "DATA_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES", + "SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES", ] -DATA_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES = { - 'S0': { +DATA_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES: Dict = { + "S0": { 300: 0.04, 305: 3.02, 310: 6.00, @@ -143,9 +145,9 @@ 815: 56.10, 820: 58.90, 825: 60.40, - 830: 61.90 + 830: 61.90, }, - 'S1': { + "S1": { 300: 0.02, 305: 2.26, 310: 4.50, @@ -252,9 +254,9 @@ 815: -8.80, 820: -9.30, 825: -9.55, - 830: -9.80 + 830: -9.80, }, - 'S2': { + "S2": { 300: 0.00, 305: 1.00, 310: 2.00, @@ -362,27 +364,34 @@ 820: 6.10, 825: 6.30, 830: 6.50, - } + }, } -SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES = CaseInsensitiveMapping({ - 'S0': - SpectralDistribution( - DATA_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES['S0'], name='S0'), - 'S1': - SpectralDistribution( - DATA_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES['S1'], name='S1'), - 'S2': - SpectralDistribution( - DATA_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES['S2'], name='S2') -}) +SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES: ( + LazyCaseInsensitiveMapping +) = LazyCaseInsensitiveMapping( + { + "S0": partial( + SpectralDistribution, + DATA_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES["S0"], + name="S0", + ), + "S1": partial( + SpectralDistribution, + DATA_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES["S1"], + name="S1", + ), + "S2": partial( + SpectralDistribution, + DATA_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES["S2"], + name="S2", + ), + } +) SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES.__doc__ = """ *CIE Illuminant D Series* :math:`S_n(\\lambda)` spectral distributions. References ---------- :cite:`Lindbloom2007a`, :cite:`Wyszecki2000z` - -SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES : CaseInsensitiveMapping - **{'S0', 'S1', 'S1'}** """ diff --git a/colour/colorimetry/datasets/illuminants/tristimulus_values.py b/colour/colorimetry/datasets/illuminants/tristimulus_values.py new file mode 100644 index 0000000000..e2fdce2bc8 --- /dev/null +++ b/colour/colorimetry/datasets/illuminants/tristimulus_values.py @@ -0,0 +1,124 @@ +""" +CIE XYZ Tristimulus Values of Illuminants +========================================= + +Defines the *CIE XYZ* tristimulus values of the illuminants for the +*CIE 1931 2 Degree Standard Observer* and +*CIE 1964 10 Degree Standard Observer*. + +The following *CIE* illuminants are available: + +- CIE Standard Illuminant A +- CIE Illuminant C +- CIE Illuminant D Series (D50, D55, D60, D65, D75) + +Notes +----- +- The intent of the data in this module is to provide a practical reference + if it is required to use the exact *CIE XYZ* tristimulus values of the + *CIE* illuminants as given in :cite:`Carter2018`. Indeed different rounding + practises in the colorimetric conversions yield different values for those + illuminants, as a related example, *CIE Standard Illuminant D Series D65* + chromaticity coordinates are commonly given as (0.31270, 0.32900) but + :cite:`Carter2018` defines them as (0.31271, 0.32903). + +References +---------- +- :cite:`Carter2018` : Carter, E. C., Schanda, J. D., Hirschler, R., Jost, + S., Luo, M. R., Melgosa, M., Ohno, Y., Pointer, M. R., Rich, D. C., Vienot, + F., Whitehead, L., & Wold, J. H. (2018). CIE 015:2018 Colorimetry, 4th + Edition. International Commission on Illumination. doi:10.25039/TR.015.2018 +""" + +from __future__ import annotations + +import numpy as np + +from colour.utilities import CaseInsensitiveMapping + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TVS_ILLUMINANTS_CIE_STANDARD_OBSERVER_2_DEGREE_CIE1931", + "TVS_ILLUMINANTS_CIE_STANDARD_OBSERVER_10_DEGREE_CIE1964", + "TVS_ILLUMINANTS", +] + +TVS_ILLUMINANTS_CIE_STANDARD_OBSERVER_2_DEGREE_CIE1931: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "A": np.array([109.85, 100.00, 35.58]), + "C": np.array([98.07, 100.00, 118.22]), + "D50": np.array([96.42, 100.00, 82.51]), + "D55": np.array([95.68, 100.00, 92.14]), + "D65": np.array([95.04, 100.00, 108.88]), + "D75": np.array([94.97, 100.00, 122.61]), + } +) +""" +*CIE XYZ* tristimulus values of the *CIE* illuminants for the +*CIE 1931 2 Degree Standard Observer*. + +References +---------- +:cite:`Carter2018` +""" + +TVS_ILLUMINANTS_CIE_STANDARD_OBSERVER_10_DEGREE_CIE1964: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "A": np.array([111.14, 100.00, 35.20]), + "C": np.array([97.29, 100.00, 116.14]), + "D50": np.array([96.72, 100.00, 81.43]), + "D55": np.array([95.80, 100.00, 90.93]), + "D65": np.array([94.81, 100.00, 107.32]), + "D75": np.array([94.42, 100.00, 120.64]), + } +) +""" +*CIE XYZ* tristimulus values of the *CIE* illuminants for the +*CIE 1964 10 Degree Standard Observer*. + +References +---------- +:cite:`Carter2018` + +TVS_ILLUMINANTS_CIE_STANDARD_OBSERVER_10_DEGREE_CIE1964 : \ +CaseInsensitiveMapping +""" + +TVS_ILLUMINANTS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "CIE 1931 2 Degree Standard Observer": CaseInsensitiveMapping( + TVS_ILLUMINANTS_CIE_STANDARD_OBSERVER_2_DEGREE_CIE1931 + ), + "CIE 1964 10 Degree Standard Observer": CaseInsensitiveMapping( + TVS_ILLUMINANTS_CIE_STANDARD_OBSERVER_10_DEGREE_CIE1964 + ), + } +) +TVS_ILLUMINANTS.__doc__ = """ +*CIE XYZ* tristimulus values of the illuminants. + +References +---------- +:cite:`Carter2018` + +Aliases: + +- 'cie_2_1931': 'CIE 1931 2 Degree Standard Observer' +- 'cie_10_1964': 'CIE 1964 10 Degree Standard Observer' +""" +TVS_ILLUMINANTS["cie_2_1931"] = TVS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +] +TVS_ILLUMINANTS["cie_10_1964"] = TVS_ILLUMINANTS[ + "CIE 1964 10 Degree Standard Observer" +] diff --git a/colour/colorimetry/datasets/lefs.py b/colour/colorimetry/datasets/lefs.py index e9bd096aa2..db5a954e04 100644 --- a/colour/colorimetry/datasets/lefs.py +++ b/colour/colorimetry/datasets/lefs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Spectral Distributions of the Luminous Efficiency Functions =========================================================== @@ -42,25 +41,32 @@ http://en.wikipedia.org/wiki/Mesopic_vision#Mesopic_weighting_function """ -from __future__ import division, unicode_literals +from __future__ import annotations + +from functools import partial from colour.colorimetry import SpectralDistribution -from colour.utilities import CaseInsensitiveMapping +from colour.hints import Dict +from colour.utilities import CaseInsensitiveMapping, LazyCaseInsensitiveMapping -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'DATA_LEFS_PHOTOPIC', 'SDS_LEFS_PHOTOPIC', 'DATA_LEFS_SCOTOPIC', - 'SDS_LEFS_SCOTOPIC', 'SDS_LEFS', 'DATA_MESOPIC_X' + "DATA_LEFS_PHOTOPIC", + "SDS_LEFS_PHOTOPIC", + "DATA_LEFS_SCOTOPIC", + "SDS_LEFS_SCOTOPIC", + "SDS_LEFS", + "DATA_MESOPIC_X", ] -DATA_LEFS_PHOTOPIC = { - 'CIE 1924 Photopic Standard Observer': { +DATA_LEFS_PHOTOPIC: Dict = { + "CIE 1924 Photopic Standard Observer": { 360: 0.0000039170000, 361: 0.0000043935810, 362: 0.0000049296040, @@ -531,9 +537,9 @@ 827: 0.0000005575746, 828: 0.0000005198080, 829: 0.0000004846123, - 830: 0.0000004518100 + 830: 0.0000004518100, }, - 'Judd Modified CIE 1951 Photopic Standard Observer': { + "Judd Modified CIE 1951 Photopic Standard Observer": { 370: 0.0001, 380: 0.0004, 390: 0.0015, @@ -574,9 +580,9 @@ 740: 0.0002, 750: 0.0001, 760: 0.0001, - 770: 0.0000 + 770: 0.0000, }, - 'Judd-Vos Modified CIE 1978 Photopic Standard Observer': { + "Judd-Vos Modified CIE 1978 Photopic Standard Observer": { 380: 0.0002000000, 381: 0.0002282100, 382: 0.0002610900, @@ -977,9 +983,9 @@ 777: 0.0000184529, 778: 0.0000172169, 779: 0.0000160646, - 780: 0.0000149900 + 780: 0.0000149900, }, - 'CIE 1964 Photopic 10 Degree Standard Observer': { + "CIE 1964 Photopic 10 Degree Standard Observer": { 360: 0.000000013398, 361: 0.000000020294, 362: 0.000000030560, @@ -1450,9 +1456,9 @@ 827: 0.000000749780, 828: 0.000000707440, 829: 0.000000667480, - 830: 0.000000629700 + 830: 0.000000629700, }, - 'CIE 2008 2 Degree Physiologically Relevant LEF': { + "CIE 2008 2 Degree Physiologically Relevant LEF": { 390: 4.14616e-04, 391: 5.02833e-04, 392: 6.08499e-04, @@ -1893,9 +1899,9 @@ 827: 8.38669e-07, 828: 7.91454e-07, 829: 7.47077e-07, - 830: 7.05386e-07 + 830: 7.05386e-07, }, - 'CIE 2008 10 Degree Physiologically Relevant LEF': { + "CIE 2008 10 Degree Physiologically Relevant LEF": { 390: 4.07678e-04, 391: 4.97777e-04, 392: 6.06475e-04, @@ -2336,44 +2342,57 @@ 827: 7.54436e-07, 828: 7.11962e-07, 829: 6.72042e-07, - 830: 6.34538e-07 - } + 830: 6.34538e-07, + }, } -SDS_LEFS_PHOTOPIC = CaseInsensitiveMapping({ - 'CIE 1924 Photopic Standard Observer': - SpectralDistribution( - DATA_LEFS_PHOTOPIC['CIE 1924 Photopic Standard Observer'], - name='CIE 1924 Photopic Standard Observer'), - 'Judd Modified CIE 1951 Photopic Standard Observer': - SpectralDistribution( +SDS_LEFS_PHOTOPIC: LazyCaseInsensitiveMapping = LazyCaseInsensitiveMapping( + { + "CIE 1924 Photopic Standard Observer": partial( + SpectralDistribution, + DATA_LEFS_PHOTOPIC["CIE 1924 Photopic Standard Observer"], + name="CIE 1924 Photopic Standard Observer", + ), + "Judd Modified CIE 1951 Photopic Standard Observer": partial( + SpectralDistribution, DATA_LEFS_PHOTOPIC[ - 'Judd Modified CIE 1951 Photopic Standard Observer'], - name='Judd Modified CIE 1951 Photopic Standard Observer'), - 'Judd-Vos Modified CIE 1978 Photopic Standard Observer': - SpectralDistribution( + "Judd Modified CIE 1951 Photopic Standard Observer" + ], + name="Judd Modified CIE 1951 Photopic Standard Observer", + ), + "Judd-Vos Modified CIE 1978 Photopic Standard Observer": partial( + SpectralDistribution, DATA_LEFS_PHOTOPIC[ - 'Judd-Vos Modified CIE 1978 Photopic Standard Observer'], - name='Judd-Vos Modified CIE 1978 Photopic Standard Observer'), - 'CIE 1964 Photopic 10 Degree Standard Observer': - SpectralDistribution( + "Judd-Vos Modified CIE 1978 Photopic Standard Observer" + ], + name="Judd-Vos Modified CIE 1978 Photopic Standard Observer", + ), + "CIE 1964 Photopic 10 Degree Standard Observer": partial( + SpectralDistribution, DATA_LEFS_PHOTOPIC[ - 'CIE 1964 Photopic 10 Degree Standard Observer'], - name='CIE 1964 Photopic 10 Degree Standard Observer', - strict_name='CIE 1964 Photopic 10$^\\circ$ Standard Observer'), - 'CIE 2008 2 Degree Physiologically Relevant LEF': - SpectralDistribution( + "CIE 1964 Photopic 10 Degree Standard Observer" + ], + name="CIE 1964 Photopic 10 Degree Standard Observer", + strict_name="CIE 1964 Photopic 10$^\\circ$ Standard Observer", + ), + "CIE 2008 2 Degree Physiologically Relevant LEF": partial( + SpectralDistribution, DATA_LEFS_PHOTOPIC[ - 'CIE 2008 2 Degree Physiologically Relevant LEF'], - name='CIE 2008 2 Degree Physiologically Relevant LEF', - strict_name='CIE 2008 2$^\\circ$ Physiologically Relevant LEF'), - 'CIE 2008 10 Degree Physiologically Relevant LEF': - SpectralDistribution( + "CIE 2008 2 Degree Physiologically Relevant LEF" + ], + name="CIE 2008 2 Degree Physiologically Relevant LEF", + strict_name="CIE 2008 2$^\\circ$ Physiologically Relevant LEF", + ), + "CIE 2008 10 Degree Physiologically Relevant LEF": partial( + SpectralDistribution, DATA_LEFS_PHOTOPIC[ - 'CIE 2008 10 Degree Physiologically Relevant LEF'], - name='CIE 2008 10 Degree Physiologically Relevant LEF', - strict_name='CIE 2008 10$^\\circ$ Physiologically Relevant LEF') -}) + "CIE 2008 10 Degree Physiologically Relevant LEF" + ], + name="CIE 2008 10 Degree Physiologically Relevant LEF", + strict_name="CIE 2008 10$^\\circ$ Physiologically Relevant LEF", + ), + } +) SDS_LEFS_PHOTOPIC.__doc__ = """ Spectral distributions of the photopic luminous efficiency functions. @@ -2381,26 +2400,20 @@ ---------- :cite:`CVRLq`, :cite:`CVRLs` -SDS_LEFS_PHOTOPIC : CaseInsensitiveMapping - **{'CIE 1924 Photopic Standard Observer', - 'Judd Modified CIE 1951 Photopic Standard Observer', - 'Judd-Vos Modified CIE 1978 Photopic Standard Observer', - 'CIE 1964 Photopic 10 Degree Standard Observer', - 'CIE 2008 2 Degree Physiologically Relevant LEF', - 'CIE 2008 10 Degree Physiologically Relevant LEF'}** - Aliases: - 'cie_2_1924': 'CIE 1931 2 Degree Standard Observer' - 'cie_10_1964': 'CIE 1964 Photopic 10 Degree Standard Observer' """ -SDS_LEFS_PHOTOPIC['cie_2_1924'] = ( - SDS_LEFS_PHOTOPIC['CIE 1924 Photopic Standard Observer']) -SDS_LEFS_PHOTOPIC['cie_10_1964'] = ( - SDS_LEFS_PHOTOPIC['CIE 1964 Photopic 10 Degree Standard Observer']) +SDS_LEFS_PHOTOPIC["cie_2_1924"] = SDS_LEFS_PHOTOPIC[ + "CIE 1924 Photopic Standard Observer" +] +SDS_LEFS_PHOTOPIC["cie_10_1964"] = SDS_LEFS_PHOTOPIC[ + "CIE 1964 Photopic 10 Degree Standard Observer" +] -DATA_LEFS_SCOTOPIC = { - 'CIE 1951 Scotopic Standard Observer': { +DATA_LEFS_SCOTOPIC: Dict = { + "CIE 1951 Scotopic Standard Observer": { 380: 0.0005890000, 381: 0.0006650000, 382: 0.0007520000, @@ -2805,12 +2818,15 @@ } } -SDS_LEFS_SCOTOPIC = CaseInsensitiveMapping({ - 'CIE 1951 Scotopic Standard Observer': - SpectralDistribution( - DATA_LEFS_SCOTOPIC['CIE 1951 Scotopic Standard Observer'], - name='CIE 1951 Scotopic Standard Observer') -}) +SDS_LEFS_SCOTOPIC: LazyCaseInsensitiveMapping = LazyCaseInsensitiveMapping( + { + "CIE 1951 Scotopic Standard Observer": partial( + SpectralDistribution, + DATA_LEFS_SCOTOPIC["CIE 1951 Scotopic Standard Observer"], + name="CIE 1951 Scotopic Standard Observer", + ) + } +) SDS_LEFS_SCOTOPIC.__doc__ = """ Spectral distributions of the scotopic luminous efficiency functions. @@ -2818,83 +2834,50 @@ ---------- :cite:`CVRLs` -SDS_LEFS_SCOTOPIC : CaseInsensitiveMapping - **{'CIE 1951 Scotopic Standard Observer', }** - Aliases: - 'cie_1951': 'CIE 1951 Scotopic Standard Observer' """ -SDS_LEFS_SCOTOPIC['cie_1951'] = ( - SDS_LEFS_SCOTOPIC['CIE 1951 Scotopic Standard Observer']) +SDS_LEFS_SCOTOPIC["cie_1951"] = SDS_LEFS_SCOTOPIC[ + "CIE 1951 Scotopic Standard Observer" +] -SDS_LEFS = CaseInsensitiveMapping(SDS_LEFS_PHOTOPIC) +SDS_LEFS: LazyCaseInsensitiveMapping = LazyCaseInsensitiveMapping( + SDS_LEFS_PHOTOPIC +) SDS_LEFS.__doc__ = """ Spectral distributions of the luminous efficiency functions. References ---------- :cite:`CVRLq`, :cite:`CVRLs`, :cite:`Wikipedia2005d` - -SDS_LEFS : CaseInsensitiveMapping - **{'CIE 1924 Photopic Standard Observer', - 'Judd Modified CIE 1951 Photopic Standard Observer', - 'Judd-Vos Modified CIE 1978 Photopic Standard Observer', - 'CIE 1964 Photopic 10 Degree Standard Observer', - 'CIE 2008 2 Degree Physiologically Relevant LEF', - 'CIE 2008 10 Degree Physiologically Relevant LEF', - 'CIE 1951 Scotopic Standard Observer'}** """ SDS_LEFS.update(SDS_LEFS_SCOTOPIC) -DATA_MESOPIC_X = { - 0.01: - CaseInsensitiveMapping({ - 'Blue Heavy': CaseInsensitiveMapping({ - 'MOVE': 0.13, - 'LRC': 0.04 - }), - 'Red Heavy': CaseInsensitiveMapping({ - 'MOVE': 0.00, - 'LRC': 0.01 - }) - }), - 0.1: - CaseInsensitiveMapping({ - 'Blue Heavy': CaseInsensitiveMapping({ - 'MOVE': 0.42, - 'LRC': 0.28 - }), - 'Red Heavy': CaseInsensitiveMapping({ - 'MOVE': 0.34, - 'LRC': 0.11 - }) - }), - 1.0: - CaseInsensitiveMapping({ - 'Blue Heavy': CaseInsensitiveMapping({ - 'MOVE': 0.70, - 'LRC': 1.00 - }), - 'Red Heavy': CaseInsensitiveMapping({ - 'MOVE': 0.68, - 'LRC': 1.00 - }) - }), - 10: - CaseInsensitiveMapping({ - 'Blue Heavy': CaseInsensitiveMapping({ - 'MOVE': 0.98, - 'LRC': 1.00 - }), - 'Red Heavy': CaseInsensitiveMapping({ - 'MOVE': 0.98, - 'LRC': 1.00 - }) - }) +DATA_MESOPIC_X: Dict = { + 0.01: CaseInsensitiveMapping( + { + "Blue Heavy": CaseInsensitiveMapping({"MOVE": 0.13, "LRC": 0.04}), + "Red Heavy": CaseInsensitiveMapping({"MOVE": 0.00, "LRC": 0.01}), + } + ), + 0.1: CaseInsensitiveMapping( + { + "Blue Heavy": CaseInsensitiveMapping({"MOVE": 0.42, "LRC": 0.28}), + "Red Heavy": CaseInsensitiveMapping({"MOVE": 0.34, "LRC": 0.11}), + } + ), + 1.0: CaseInsensitiveMapping( + { + "Blue Heavy": CaseInsensitiveMapping({"MOVE": 0.70, "LRC": 1.00}), + "Red Heavy": CaseInsensitiveMapping({"MOVE": 0.68, "LRC": 1.00}), + } + ), + 10: CaseInsensitiveMapping( + { + "Blue Heavy": CaseInsensitiveMapping({"MOVE": 0.98, "LRC": 1.00}), + "Red Heavy": CaseInsensitiveMapping({"MOVE": 0.98, "LRC": 1.00}), + } + ), } -""" -Weighting factors for the mesopic luminous efficiency function calculation. - -DATA_MESOPIC_X : CaseInsensitiveMapping -""" +"""Weighting factors for the mesopic luminous efficiency function calculation.""" diff --git a/colour/colorimetry/datasets/light_sources/__init__.py b/colour/colorimetry/datasets/light_sources/__init__.py index 87e9e7b127..69fb736f65 100644 --- a/colour/colorimetry/datasets/light_sources/__init__.py +++ b/colour/colorimetry/datasets/light_sources/__init__.py @@ -1,8 +1,7 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .chromaticity_coordinates import CCS_LIGHT_SOURCES from .sds import SDS_LIGHT_SOURCES -__all__ = ['CCS_LIGHT_SOURCES', 'SDS_LIGHT_SOURCES'] +__all__ = [ + "CCS_LIGHT_SOURCES", + "SDS_LIGHT_SOURCES", +] diff --git a/colour/colorimetry/datasets/light_sources/chromaticity_coordinates.py b/colour/colorimetry/datasets/light_sources/chromaticity_coordinates.py index 87883e2e64..d407b87cd7 100644 --- a/colour/colorimetry/datasets/light_sources/chromaticity_coordinates.py +++ b/colour/colorimetry/datasets/light_sources/chromaticity_coordinates.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Chromaticity Coordinates of the Light Sources ============================================= @@ -43,52 +42,49 @@ http://www.cis.rit.edu/research/mcsl2/online/PointerData.xls """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.utilities import CaseInsensitiveMapping -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'CCS_LIGHT_SOURCES_RIT_STANDARD_OBSERVER_2_DEGREE_CIE1931', - 'CCS_LIGHT_SOURCES_RIT_STANDARD_OBSERVER_10_DEGREE_CIE1964', - 'CCS_LIGHT_SOURCES_NIST_TRADITIONAL_STANDARD_OBSERVER_2_DEGREE_CIE1931', - 'CCS_LIGHT_SOURCES_NIST_TRADITIONAL_STANDARD_OBSERVER_10_DEGREE_CIE1964', - 'CCS_LIGHT_SOURCES_NIST_LED_STANDARD_OBSERVER_2_DEGREE_CIE1931', - 'CCS_LIGHT_SOURCES_NIST_LED_STANDARD_OBSERVER_10_DEGREE_CIE1964', - 'CCS_LIGHT_SOURCES_NIST_PHILIPS_STANDARD_OBSERVER_2_DEGREE_CIE1931', - 'CCS_LIGHT_SOURCES_NIST_PHILIPS_STANDARD_OBSERVER_10_DEGREE_CIE1964', - 'CCS_LIGHT_SOURCES_COMMON_STANDARD_OBSERVER_2_DEGREE_CIE1931', - 'CCS_LIGHT_SOURCES_COMMON_STANDARD_OBSERVER_10_DEGREE_CIE1964', - 'CCS_LIGHT_SOURCES' + "CCS_LIGHT_SOURCES_RIT_STANDARD_OBSERVER_2_DEGREE_CIE1931", + "CCS_LIGHT_SOURCES_RIT_STANDARD_OBSERVER_10_DEGREE_CIE1964", + "CCS_LIGHT_SOURCES_NIST_TRADITIONAL_STANDARD_OBSERVER_2_DEGREE_CIE1931", + "CCS_LIGHT_SOURCES_NIST_TRADITIONAL_STANDARD_OBSERVER_10_DEGREE_CIE1964", + "CCS_LIGHT_SOURCES_NIST_LED_STANDARD_OBSERVER_2_DEGREE_CIE1931", + "CCS_LIGHT_SOURCES_NIST_LED_STANDARD_OBSERVER_10_DEGREE_CIE1964", + "CCS_LIGHT_SOURCES_NIST_PHILIPS_STANDARD_OBSERVER_2_DEGREE_CIE1931", + "CCS_LIGHT_SOURCES_NIST_PHILIPS_STANDARD_OBSERVER_10_DEGREE_CIE1964", + "CCS_LIGHT_SOURCES_COMMON_STANDARD_OBSERVER_2_DEGREE_CIE1931", + "CCS_LIGHT_SOURCES_COMMON_STANDARD_OBSERVER_10_DEGREE_CIE1964", + "CCS_LIGHT_SOURCES", ] -CCS_LIGHT_SOURCES_RIT_STANDARD_OBSERVER_2_DEGREE_CIE1931 = ( - CaseInsensitiveMapping({ - 'Natural': - np.array([0.381585730647787, 0.359224138274067]), - 'Philips TL-84': - np.array([0.378413599970988, 0.379290254544090]), - 'SA': - np.array([0.447573030734154, 0.407438137156467]), - 'SC': - np.array([0.310056734303928, 0.316145704789204]), - 'T8 Luxline Plus White': - np.array([0.410492204086250, 0.388932529676840]), - 'T8 Polylux 3000': - np.array([0.431706082207185, 0.413877736072647]), - 'T8 Polylux 4000': - np.array([0.379219473139794, 0.384469085577631]), - 'Thorn Kolor-rite': - np.array([0.381919124282806, 0.374309261641251]) - })) +CCS_LIGHT_SOURCES_RIT_STANDARD_OBSERVER_2_DEGREE_CIE1931: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "Natural": np.array([0.381585730647787, 0.359224138274067]), + "Philips TL-84": np.array([0.378413599970988, 0.379290254544090]), + "SA": np.array([0.447573030734154, 0.407438137156467]), + "SC": np.array([0.310056734303928, 0.316145704789204]), + "T8 Luxline Plus White": np.array( + [0.410492204086250, 0.388932529676840] + ), + "T8 Polylux 3000": np.array([0.431706082207185, 0.413877736072647]), + "T8 Polylux 4000": np.array([0.379219473139794, 0.384469085577631]), + "Thorn Kolor-rite": np.array([0.381919124282806, 0.374309261641251]), + } +) """ Chromaticity coordinates of the light sources from the *RIT* *PointerData.xls* spreadsheet for the *CIE 1931 2 Degree Standard Observer*. @@ -102,65 +98,47 @@ References ---------- :cite:`Pointer1980a` - -CCS_LIGHT_SOURCES_RIT_STANDARD_OBSERVER_2_DEGREE_CIE1931 : \ -CaseInsensitiveMapping - **{'Natural', 'Philips TL-84', 'T8 Luxline Plus White', 'SA', 'SC', - 'T8 Polylux 3000', 'T8 Polylux 4000', 'Thorn Kolor-rite'}** """ -CCS_LIGHT_SOURCES_RIT_STANDARD_OBSERVER_10_DEGREE_CIE1964 = ( - CaseInsensitiveMapping({ - 'Natural': - np.array([0.384870991183035, 0.353869223366545]), - 'Philips TL-84': - np.array([0.383592002892950, 0.373922741815762]), - 'SA': - np.array([0.451176803594070, 0.405936046781591]), - 'SC': - np.array([0.310388637415649, 0.319050651220986]), - 'T8 Luxline Plus White': - np.array([0.416946978831203, 0.380991426462756]), - 'T8 Polylux 3000': - np.array([0.439038926288670, 0.404554330124715]), - 'T8 Polylux 4000': - np.array([0.385115161872875, 0.377800928395769]), - 'Thorn Kolor-rite': - np.array([0.385533929282467, 0.370840492090948]) - })) +CCS_LIGHT_SOURCES_RIT_STANDARD_OBSERVER_10_DEGREE_CIE1964: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "Natural": np.array([0.384870991183035, 0.353869223366545]), + "Philips TL-84": np.array([0.383592002892950, 0.373922741815762]), + "SA": np.array([0.451176803594070, 0.405936046781591]), + "SC": np.array([0.310388637415649, 0.319050651220986]), + "T8 Luxline Plus White": np.array( + [0.416946978831203, 0.380991426462756] + ), + "T8 Polylux 3000": np.array([0.439038926288670, 0.404554330124715]), + "T8 Polylux 4000": np.array([0.385115161872875, 0.377800928395769]), + "Thorn Kolor-rite": np.array([0.385533929282467, 0.370840492090948]), + } +) """ Chromaticity coordinates of the light sources from the *RIT* *PointerData.xls* spreadsheet for the *CIE 1964 10 Degree Standard Observer*. - -CCS_LIGHT_SOURCES_RIT_STANDARD_OBSERVER_10_DEGREE_CIE1964 : \ -CaseInsensitiveMapping - **{'Natural', 'Philips TL-84', 'T8 Luxline Plus White', 'SA', 'SC', - 'T8 Polylux 3000', 'T8 Polylux 4000', 'Thorn Kolor-rite'}** """ -CCS_LIGHT_SOURCES_NIST_TRADITIONAL_STANDARD_OBSERVER_2_DEGREE_CIE1931 = ( - CaseInsensitiveMapping({ - 'Cool White FL': - np.array([0.369256318971281, 0.372549878176631]), - 'Daylight FL': - np.array([0.312662993963651, 0.331985688793009]), - 'HPS': - np.array([0.521677696062816, 0.417971177117239]), - 'Incandescent': - np.array([0.450730217519680, 0.408046128945005]), - 'LPS': - np.array([0.575151311365165, 0.424232234924905]), - 'Mercury': - np.array([0.392018457637112, 0.383777071984453]), - 'Metal Halide': - np.array([0.372544558972793, 0.385603925927588]), - 'Neodimium Incandescent': - np.array([0.447398697052100, 0.395008601248268]), - 'Super HPS': - np.array([0.470061659271846, 0.406116584248741]), - 'Triphosphor FL': - np.array([0.413163268257275, 0.396422053758680]) - })) +CCS_LIGHT_SOURCES_NIST_TRADITIONAL_STANDARD_OBSERVER_2_DEGREE_CIE1931: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "Cool White FL": np.array([0.369256318971281, 0.372549878176631]), + "Daylight FL": np.array([0.312662993963651, 0.331985688793009]), + "HPS": np.array([0.521677696062816, 0.417971177117239]), + "Incandescent": np.array([0.450730217519680, 0.408046128945005]), + "LPS": np.array([0.575151311365165, 0.424232234924905]), + "Mercury": np.array([0.392018457637112, 0.383777071984453]), + "Metal Halide": np.array([0.372544558972793, 0.385603925927588]), + "Neodimium Incandescent": np.array( + [0.447398697052100, 0.395008601248268] + ), + "Super HPS": np.array([0.470061659271846, 0.406116584248741]), + "Triphosphor FL": np.array([0.413163268257275, 0.396422053758680]), + } +) """ Chromaticity coordinates of the traditional light sources from the *NIST* *NIST CQS simulation 7.4.xls* spreadsheet for the @@ -169,274 +147,247 @@ References ---------- :cite:`Ohno2008a` - -CCS_LIGHT_SOURCES_NIST_TRADITIONAL_STANDARD_OBSERVER_2_DEGREE_CIE1931 : \ -CaseInsensitiveMapping - **{'Cool White FL', 'Daylight FL', 'HPS', 'Incandescent', 'LPS', 'Mercury', - 'Metal Halide', 'Neodimium Incandescent', 'Super HPS', 'Triphosphor FL'}** """ -CCS_LIGHT_SOURCES_NIST_TRADITIONAL_STANDARD_OBSERVER_10_DEGREE_CIE1964 = ( - CaseInsensitiveMapping({ - 'Cool White FL': - np.array([0.376715047518455, 0.364576802118673]), - 'Daylight FL': - np.array([0.317395878738965, 0.330780819136676]), - 'HPS': - np.array([0.531764495177513, 0.408752715284645]), - 'Incandescent': - np.array([0.454365604973572, 0.406573684216774]), - 'LPS': - np.array([0.589960045887891, 0.410039954112109]), - 'Mercury': - np.array([0.401266412873755, 0.364732538221183]), - 'Metal Halide': - np.array([0.378786167751226, 0.377496928504661]), - 'Neodimium Incandescent': - np.array([0.447516717156694, 0.396734151368497]), - 'Super HPS': - np.array([0.473859567146135, 0.401381825309197]), - 'Triphosphor FL': - np.array([0.418591963931736, 0.388947713332192]) - })) +CCS_LIGHT_SOURCES_NIST_TRADITIONAL_STANDARD_OBSERVER_10_DEGREE_CIE1964: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "Cool White FL": np.array([0.376715047518455, 0.364576802118673]), + "Daylight FL": np.array([0.317395878738965, 0.330780819136676]), + "HPS": np.array([0.531764495177513, 0.408752715284645]), + "Incandescent": np.array([0.454365604973572, 0.406573684216774]), + "LPS": np.array([0.589960045887891, 0.410039954112109]), + "Mercury": np.array([0.401266412873755, 0.364732538221183]), + "Metal Halide": np.array([0.378786167751226, 0.377496928504661]), + "Neodimium Incandescent": np.array( + [0.447516717156694, 0.396734151368497] + ), + "Super HPS": np.array([0.473859567146135, 0.401381825309197]), + "Triphosphor FL": np.array([0.418591963931736, 0.388947713332192]), + } +) """ Chromaticity coordinates of the traditional light sources from the *NIST* *NIST CQS simulation 7.4.xls* spreadsheet for the *CIE 1964 10 Degree Standard Observer*. - -CCS_LIGHT_SOURCES_NIST_TRADITIONAL_STANDARD_OBSERVER_10_DEGREE_CIE1964 : \ -CaseInsensitiveMapping - **{'Cool White FL', 'Daylight FL', 'HPS', 'Incandescent', 'LPS', 'Mercury', - 'Metal Halide', 'Neodimium Incandescent', 'Super HPS', 'Triphosphor FL'}** """ -CCS_LIGHT_SOURCES_NIST_LED_STANDARD_OBSERVER_2_DEGREE_CIE1931 = ( - CaseInsensitiveMapping({ - '3-LED-1 (457/540/605)': - np.array([0.417057686949170, 0.396262457986602]), - '3-LED-2 (473/545/616)': - np.array([0.417060475566006, 0.396268120523418]), - '3-LED-2 Yellow': - np.array([0.436563079184047, 0.443649619298676]), - '3-LED-3 (465/546/614)': - np.array([0.380460502184482, 0.376772001481922]), - '3-LED-4 (455/547/623)': - np.array([0.417067943691045, 0.396276280071757]), - '4-LED No Yellow': - np.array([0.417060589301332, 0.396268153712350]), - '4-LED Yellow': - np.array([0.417069637940463, 0.396276766014859]), - '4-LED-1 (461/526/576/624)': - np.array([0.417067615440556, 0.396275056779587]), - '4-LED-2 (447/512/573/627)': - np.array([0.417071570560054, 0.396278745130373]), - 'Luxeon WW 2880': - np.array([0.459088527920913, 0.432916480607903]), - 'PHOS-1': - np.array([0.436443167801164, 0.404616033549917]), - 'PHOS-2': - np.array([0.452704462198571, 0.437584543052711]), - 'PHOS-3': - np.array([0.436899870751359, 0.404037372134463]), - 'PHOS-4': - np.array([0.436936023906427, 0.404113558278629]), - 'Phosphor LED YAG': - np.array([0.307761817314310, 0.325268939239941]) - })) +CCS_LIGHT_SOURCES_NIST_LED_STANDARD_OBSERVER_2_DEGREE_CIE1931: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "3-LED-1 (457/540/605)": np.array( + [0.417057686949170, 0.396262457986602] + ), + "3-LED-2 (473/545/616)": np.array( + [0.417060475566006, 0.396268120523418] + ), + "3-LED-2 Yellow": np.array([0.436563079184047, 0.443649619298676]), + "3-LED-3 (465/546/614)": np.array( + [0.380460502184482, 0.376772001481922] + ), + "3-LED-4 (455/547/623)": np.array( + [0.417067943691045, 0.396276280071757] + ), + "4-LED No Yellow": np.array([0.417060589301332, 0.396268153712350]), + "4-LED Yellow": np.array([0.417069637940463, 0.396276766014859]), + "4-LED-1 (461/526/576/624)": np.array( + [0.417067615440556, 0.396275056779587] + ), + "4-LED-2 (447/512/573/627)": np.array( + [0.417071570560054, 0.396278745130373] + ), + "Luxeon WW 2880": np.array([0.459088527920913, 0.432916480607903]), + "PHOS-1": np.array([0.436443167801164, 0.404616033549917]), + "PHOS-2": np.array([0.452704462198571, 0.437584543052711]), + "PHOS-3": np.array([0.436899870751359, 0.404037372134463]), + "PHOS-4": np.array([0.436936023906427, 0.404113558278629]), + "Phosphor LED YAG": np.array([0.307761817314310, 0.325268939239941]), + } +) """ Chromaticity coordinates of the LED light sources from the *NIST* *NIST CQS simulation 7.4.xls* spreadsheet for the *CIE 1931 2 Degree Standard Observer*. - -CCS_LIGHT_SOURCES_NIST_LED_STANDARD_OBSERVER_2_DEGREE_CIE1931 : - **{'3-LED-1 (457/540/605)', '3-LED-2 (473/545/616)', '3-LED-2 Yellow', - '3-LED-3 (465/546/614)', '3-LED-4 (455/547/623)', '4-LED No Yellow', - '4-LED Yellow', '4-LED-1 (461/526/576/624)', '4-LED-2 (447/512/573/627)', - 'Luxeon WW 2880', 'PHOS-1', 'PHOS-2', 'PHOS-3', 'PHOS-4', - 'Phosphor LED YAG'}** """ -CCS_LIGHT_SOURCES_NIST_LED_STANDARD_OBSERVER_10_DEGREE_CIE1964 = ( - CaseInsensitiveMapping({ - '3-LED-1 (457/540/605)': - np.array([0.425099988926548, 0.389451349911075]), - '3-LED-2 (473/545/616)': - np.array([0.422222118774217, 0.401298495594226]), - '3-LED-2 Yellow': - np.array([0.446222216139125, 0.441646464276087]), - '3-LED-3 (465/546/614)': - np.array([0.387470465801936, 0.376404716015666]), - '3-LED-4 (455/547/623)': - np.array([0.422865464107041, 0.388772240171637]), - '4-LED No Yellow': - np.array([0.419807532952439, 0.399465294930377]), - '4-LED Yellow': - np.array([0.422720601750053, 0.390284663473479]), - '4-LED-1 (461/526/576/624)': - np.array([0.423899783323037, 0.394170886226971]), - '4-LED-2 (447/512/573/627)': - np.array([0.421571042053867, 0.394089741928601]), - 'Luxeon WW 2880': - np.array([0.466639299623263, 0.430817417218051]), - 'PHOS-1': - np.array([0.440120001281140, 0.403135783393416]), - 'PHOS-2': - np.array([0.461487398870558, 0.436150294667024]), - 'PHOS-3': - np.array([0.440892655302172, 0.408662264402299]), - 'PHOS-4': - np.array([0.441760443951475, 0.407267478268879]), - 'Phosphor LED YAG': - np.array([0.312807834772696, 0.334180937864035]) - })) +CCS_LIGHT_SOURCES_NIST_LED_STANDARD_OBSERVER_10_DEGREE_CIE1964: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "3-LED-1 (457/540/605)": np.array( + [0.425099988926548, 0.389451349911075] + ), + "3-LED-2 (473/545/616)": np.array( + [0.422222118774217, 0.401298495594226] + ), + "3-LED-2 Yellow": np.array([0.446222216139125, 0.441646464276087]), + "3-LED-3 (465/546/614)": np.array( + [0.387470465801936, 0.376404716015666] + ), + "3-LED-4 (455/547/623)": np.array( + [0.422865464107041, 0.388772240171637] + ), + "4-LED No Yellow": np.array([0.419807532952439, 0.399465294930377]), + "4-LED Yellow": np.array([0.422720601750053, 0.390284663473479]), + "4-LED-1 (461/526/576/624)": np.array( + [0.423899783323037, 0.394170886226971] + ), + "4-LED-2 (447/512/573/627)": np.array( + [0.421571042053867, 0.394089741928601] + ), + "Luxeon WW 2880": np.array([0.466639299623263, 0.430817417218051]), + "PHOS-1": np.array([0.440120001281140, 0.403135783393416]), + "PHOS-2": np.array([0.461487398870558, 0.436150294667024]), + "PHOS-3": np.array([0.440892655302172, 0.408662264402299]), + "PHOS-4": np.array([0.441760443951475, 0.407267478268879]), + "Phosphor LED YAG": np.array([0.312807834772696, 0.334180937864035]), + } +) """ Chromaticity coordinates of the LED light sources from the *NIST* *NIST CQS simulation 7.4.xls* spreadsheet for the *CIE 1964 10 Degree Standard Observer*. - -CCS_LIGHT_SOURCES_NIST_LED_STANDARD_OBSERVER_10_DEGREE_CIE1964 : \ -CaseInsensitiveMapping - **{'3-LED-1 (457/540/605)', '3-LED-2 (473/545/616)', '3-LED-2 Yellow', - '3-LED-3 (465/546/614)', '3-LED-4 (455/547/623)', '4-LED No Yellow', - '4-LED Yellow', '4-LED-1 (461/526/576/624)', '4-LED-2 (447/512/573/627)', - 'Luxeon WW 2880', 'PHOS-1', 'PHOS-2', 'PHOS-3', 'PHOS-4', - 'Phosphor LED YAG'}** """ -CCS_LIGHT_SOURCES_NIST_PHILIPS_STANDARD_OBSERVER_2_DEGREE_CIE1931 = ( - CaseInsensitiveMapping({ - '60 A/W (Soft White)': - np.array([0.450730217519680, 0.408046128945005]), - 'C100S54 (HPS)': - np.array([0.529231515407657, 0.411370164988427]), - 'C100S54C (HPS)': - np.array([0.502380414374839, 0.415877299905475]), - 'F32T8/TL830 (Triphosphor)': - np.array([0.443250764475753, 0.409523700296928]), - 'F32T8/TL835 (Triphosphor)': - np.array([0.407150274569933, 0.393172743482571]), - 'F32T8/TL841 (Triphosphor)': - np.array([0.385376686681605, 0.390370762102806]), - 'F32T8/TL850 (Triphosphor)': - np.array([0.343768910392287, 0.358447436104108]), - 'F32T8/TL865 /PLUS (Triphosphor)': - np.array([0.316368879615201, 0.345320790143017]), - 'F34/CW/RS/EW (Cool White FL)': - np.array([0.377250931364378, 0.393087658636060]), - 'F34T12/LW/RS /EW': - np.array([0.378863642993776, 0.394960629979820]), - 'F34T12WW/RS /EW (Warm White FL)': - np.array([0.438466967656789, 0.408635441565706]), - 'F40/C50 (Broadband FL)': - np.array([0.345836574973021, 0.361724450389430]), - 'F40/C75 (Broadband FL)': - np.array([0.299966663385220, 0.316582165804824]), - 'F40/CWX (Broadband FL)': - np.array([0.375037045754214, 0.360543952129462]), - 'F40/DX (Broadband FL)': - np.array([0.311922310746537, 0.342802103417329]), - 'F40/DXTP (Delux FL)': - np.array([0.313066543826958, 0.342225714484412]), - 'F40/N (Natural FL)': - np.array([0.376878697365115, 0.354153458302878]), - 'H38HT-100 (Mercury)': - np.array([0.311200590193641, 0.382944245857018]), - 'H38JA-100/DX (Mercury DX)': - np.array([0.389791630360359, 0.373394688931767]), - 'MHC100/U/MP /3K': - np.array([0.428581768670222, 0.388168915678330]), - 'MHC100/U/MP /4K': - np.array([0.373145253482762, 0.371366990216717]), - 'SDW-T 100W/LV (Super HPS)': - np.array([0.472339157938672, 0.407106330880316]) - })) +CCS_LIGHT_SOURCES_NIST_PHILIPS_STANDARD_OBSERVER_2_DEGREE_CIE1931: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "60 A/W (Soft White)": np.array( + [0.450730217519680, 0.408046128945005] + ), + "C100S54 (HPS)": np.array([0.529231515407657, 0.411370164988427]), + "C100S54C (HPS)": np.array([0.502380414374839, 0.415877299905475]), + "F32T8/TL830 (Triphosphor)": np.array( + [0.443250764475753, 0.409523700296928] + ), + "F32T8/TL835 (Triphosphor)": np.array( + [0.407150274569933, 0.393172743482571] + ), + "F32T8/TL841 (Triphosphor)": np.array( + [0.385376686681605, 0.390370762102806] + ), + "F32T8/TL850 (Triphosphor)": np.array( + [0.343768910392287, 0.358447436104108] + ), + "F32T8/TL865 /PLUS (Triphosphor)": np.array( + [0.316368879615201, 0.345320790143017] + ), + "F34/CW/RS/EW (Cool White FL)": np.array( + [0.377250931364378, 0.393087658636060] + ), + "F34T12/LW/RS /EW": np.array([0.378863642993776, 0.394960629979820]), + "F34T12WW/RS /EW (Warm White FL)": np.array( + [0.438466967656789, 0.408635441565706] + ), + "F40/C50 (Broadband FL)": np.array( + [0.345836574973021, 0.361724450389430] + ), + "F40/C75 (Broadband FL)": np.array( + [0.299966663385220, 0.316582165804824] + ), + "F40/CWX (Broadband FL)": np.array( + [0.375037045754214, 0.360543952129462] + ), + "F40/DX (Broadband FL)": np.array( + [0.311922310746537, 0.342802103417329] + ), + "F40/DXTP (Delux FL)": np.array( + [0.313066543826958, 0.342225714484412] + ), + "F40/N (Natural FL)": np.array([0.376878697365115, 0.354153458302878]), + "H38HT-100 (Mercury)": np.array( + [0.311200590193641, 0.382944245857018] + ), + "H38JA-100/DX (Mercury DX)": np.array( + [0.389791630360359, 0.373394688931767] + ), + "MHC100/U/MP /3K": np.array([0.428581768670222, 0.388168915678330]), + "MHC100/U/MP /4K": np.array([0.373145253482762, 0.371366990216717]), + "SDW-T 100W/LV (Super HPS)": np.array( + [0.472339157938672, 0.407106330880316] + ), + } +) """ Chromaticity coordinates of the Philips light sources from the *NIST* *NIST CQS simulation 7.4.xls* spreadsheet for the *CIE 1931 2 Degree Standard Observer*. - -CCS_LIGHT_SOURCES_NIST_PHILIPS_STANDARD_OBSERVER_2_DEGREE_CIE1931 : \ -CaseInsensitiveMapping - **{'60 A/W (Soft White)', 'C100S54 (HPS)', 'C100S54C (HPS)', - 'F32T8/TL830 (Triphosphor)', 'F32T8/TL835 (Triphosphor)', - 'F32T8/TL841 (Triphosphor)', 'F32T8/TL850 (Triphosphor)', - 'F32T8/TL865 /PLUS (Triphosphor)', 'F34/CW/RS/EW (Cool White FL)', - 'F34T12/LW/RS /EW', 'F34T12WW/RS /EW (Warm White FL)', - 'F40/C50 (Broadband FL)', 'F40/C75 (Broadband FL)', - 'F40/CWX (Broadband FL)', 'F40/DX (Broadband FL)', 'F40/DXTP (Delux FL)', - 'F40/N (Natural FL)', 'H38HT-100 (Mercury)', 'H38JA-100/DX (Mercury DX)', - 'MHC100/U/MP /3K', 'MHC100/U/MP /4K', 'SDW-T 100W/LV (Super HPS)'}** """ -CCS_LIGHT_SOURCES_NIST_PHILIPS_STANDARD_OBSERVER_10_DEGREE_CIE1964 = ( - CaseInsensitiveMapping({ - '60 A/W (Soft White)': - np.array([0.454365604973572, 0.406573684216774]), - 'C100S54 (HPS)': - np.array([0.538554605063010, 0.402575827972962]), - 'C100S54C (HPS)': - np.array([0.509663059970892, 0.409064508209193]), - 'F32T8/TL830 (Triphosphor)': - np.array([0.448795219301811, 0.403574636091678]), - 'F32T8/TL835 (Triphosphor)': - np.array([0.412082534290652, 0.388001071127592]), - 'F32T8/TL841 (Triphosphor)': - np.array([0.390908619219527, 0.385290559992705]), - 'F32T8/TL850 (Triphosphor)': - np.array([0.347882431257452, 0.355845742210551]), - 'F32T8/TL865 /PLUS (Triphosphor)': - np.array([0.320698199593768, 0.343871441043854]), - 'F34/CW/RS/EW (Cool White FL)': - np.array([0.386514853545337, 0.382843326097814]), - 'F34T12/LW/RS /EW': - np.array([0.389628909159399, 0.382074721889904]), - 'F34T12WW/RS /EW (Warm White FL)': - np.array([0.448395377616960, 0.395666643335296]), - 'F40/C50 (Broadband FL)': - np.array([0.349880827196884, 0.360661316491439]), - 'F40/C75 (Broadband FL)': - np.array([0.301988533872761, 0.318479025875818]), - 'F40/CWX (Broadband FL)': - np.array([0.378502309910296, 0.356371890168937]), - 'F40/DX (Broadband FL)': - np.array([0.316783037559153, 0.341749269085077]), - 'F40/DXTP (Delux FL)': - np.array([0.318774745065791, 0.339798825605488]), - 'F40/N (Natural FL)': - np.array([0.378833157741751, 0.350724402658646]), - 'H38HT-100 (Mercury)': - np.array([0.326260627082484, 0.360001095895205]), - 'H38JA-100/DX (Mercury DX)': - np.array([0.397058597517533, 0.356532431806974]), - 'MHC100/U/MP /3K': - np.array([0.431422986591898, 0.380642213887539]), - 'MHC100/U/MP /4K': - np.array([0.375707105948115, 0.366156465779779]), - 'SDW-T 100W/LV (Super HPS)': - np.array([0.476461908192661, 0.402288012403575]) - })) +CCS_LIGHT_SOURCES_NIST_PHILIPS_STANDARD_OBSERVER_10_DEGREE_CIE1964: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "60 A/W (Soft White)": np.array( + [0.454365604973572, 0.406573684216774] + ), + "C100S54 (HPS)": np.array([0.538554605063010, 0.402575827972962]), + "C100S54C (HPS)": np.array([0.509663059970892, 0.409064508209193]), + "F32T8/TL830 (Triphosphor)": np.array( + [0.448795219301811, 0.403574636091678] + ), + "F32T8/TL835 (Triphosphor)": np.array( + [0.412082534290652, 0.388001071127592] + ), + "F32T8/TL841 (Triphosphor)": np.array( + [0.390908619219527, 0.385290559992705] + ), + "F32T8/TL850 (Triphosphor)": np.array( + [0.347882431257452, 0.355845742210551] + ), + "F32T8/TL865 /PLUS (Triphosphor)": np.array( + [0.320698199593768, 0.343871441043854] + ), + "F34/CW/RS/EW (Cool White FL)": np.array( + [0.386514853545337, 0.382843326097814] + ), + "F34T12/LW/RS /EW": np.array([0.389628909159399, 0.382074721889904]), + "F34T12WW/RS /EW (Warm White FL)": np.array( + [0.448395377616960, 0.395666643335296] + ), + "F40/C50 (Broadband FL)": np.array( + [0.349880827196884, 0.360661316491439] + ), + "F40/C75 (Broadband FL)": np.array( + [0.301988533872761, 0.318479025875818] + ), + "F40/CWX (Broadband FL)": np.array( + [0.378502309910296, 0.356371890168937] + ), + "F40/DX (Broadband FL)": np.array( + [0.316783037559153, 0.341749269085077] + ), + "F40/DXTP (Delux FL)": np.array( + [0.318774745065791, 0.339798825605488] + ), + "F40/N (Natural FL)": np.array([0.378833157741751, 0.350724402658646]), + "H38HT-100 (Mercury)": np.array( + [0.326260627082484, 0.360001095895205] + ), + "H38JA-100/DX (Mercury DX)": np.array( + [0.397058597517533, 0.356532431806974] + ), + "MHC100/U/MP /3K": np.array([0.431422986591898, 0.380642213887539]), + "MHC100/U/MP /4K": np.array([0.375707105948115, 0.366156465779779]), + "SDW-T 100W/LV (Super HPS)": np.array( + [0.476461908192661, 0.402288012403575] + ), + } +) """ Chromaticity coordinates of the Philips light sources from the *NIST* *NIST CQS simulation 7.4.xls* spreadsheet for the *CIE 1964 10 Degree Standard Observer*. - -CCS_LIGHT_SOURCES_NIST_PHILIPS_STANDARD_OBSERVER_10_DEGREE_CIE1964 : \ -CaseInsensitiveMapping - **{'60 A/W (Soft White)', 'C100S54 (HPS)', 'C100S54C (HPS)', - 'F32T8/TL830 (Triphosphor)', 'F32T8/TL835 (Triphosphor)', - 'F32T8/TL841 (Triphosphor)', 'F32T8/TL850 (Triphosphor)', - 'F32T8/TL865 /PLUS (Triphosphor)', 'F34/CW/RS/EW (Cool White FL)', - 'F34T12/LW/RS /EW', 'F34T12WW/RS /EW (Warm White FL)', - 'F40/C50 (Broadband FL)', 'F40/C75 (Broadband FL)', - 'F40/CWX (Broadband FL)', 'F40/DX (Broadband FL)', 'F40/DXTP (Delux FL)', - 'F40/N (Natural FL)', 'H38HT-100 (Mercury)', 'H38JA-100/DX (Mercury DX)', - 'MHC100/U/MP /3K', 'MHC100/U/MP /4K', 'SDW-T 100W/LV (Super HPS)'}** """ -CCS_LIGHT_SOURCES_COMMON_STANDARD_OBSERVER_2_DEGREE_CIE1931 = ( - CaseInsensitiveMapping({ - 'Kinoton 75P': np.array([0.315252413629716, 0.332870794805328]) - })) +CCS_LIGHT_SOURCES_COMMON_STANDARD_OBSERVER_2_DEGREE_CIE1931: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + {"Kinoton 75P": np.array([0.315252413629716, 0.332870794805328])} +) """ Chromaticity coordinates of the common light sources for the *CIE 1931 2 Degree Standard Observer*. @@ -444,66 +395,67 @@ References ---------- :cite:`Houston2015a` - -CCS_LIGHT_SOURCES_COMMON_STANDARD_OBSERVER_2_DEGREE_CIE1931 : \ -CaseInsensitiveMapping - **{'Kinoton 75P', }** """ -CCS_LIGHT_SOURCES_COMMON_STANDARD_OBSERVER_10_DEGREE_CIE1964 = ( - CaseInsensitiveMapping({ - 'Kinoton 75P': np.array([0.317086642148234, 0.336222428041514]) - })) +CCS_LIGHT_SOURCES_COMMON_STANDARD_OBSERVER_10_DEGREE_CIE1964: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + {"Kinoton 75P": np.array([0.317086642148234, 0.336222428041514])} +) """ Chromaticity coordinates of the common light sources for the *CIE 1964 10 Degree Standard Observer*. - -CCS_LIGHT_SOURCES_COMMON_STANDARD_OBSERVER_10_DEGREE_CIE1964 : \ -CaseInsensitiveMapping - **{'Kinoton 75P', }** """ -CCS_LIGHT_SOURCES = CaseInsensitiveMapping({ - 'CIE 1931 2 Degree Standard Observer': - CaseInsensitiveMapping( - CCS_LIGHT_SOURCES_RIT_STANDARD_OBSERVER_2_DEGREE_CIE1931), - 'CIE 1964 10 Degree Standard Observer': - CaseInsensitiveMapping( - CCS_LIGHT_SOURCES_RIT_STANDARD_OBSERVER_10_DEGREE_CIE1964) -}) +CCS_LIGHT_SOURCES: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "CIE 1931 2 Degree Standard Observer": CaseInsensitiveMapping( + CCS_LIGHT_SOURCES_RIT_STANDARD_OBSERVER_2_DEGREE_CIE1931 + ), + "CIE 1964 10 Degree Standard Observer": CaseInsensitiveMapping( + CCS_LIGHT_SOURCES_RIT_STANDARD_OBSERVER_10_DEGREE_CIE1964 + ), + } +) CCS_LIGHT_SOURCES.__doc__ = """ Chromaticity coordinates of the light sources. -CCS_LIGHT_SOURCES : CaseInsensitiveMapping - **{'CIE 1931 2 Degree Standard Observer', - 'CIE 1964 10 Degree Standard Observer'}** - Aliases: - 'cie_2_1931': 'CIE 1931 2 Degree Standard Observer' - 'cie_10_1964': 'CIE 1964 10 Degree Standard Observer' """ -CCS_LIGHT_SOURCES['cie_2_1931'] = ( - CCS_LIGHT_SOURCES['CIE 1931 2 Degree Standard Observer']) -CCS_LIGHT_SOURCES['cie_10_1964'] = ( - CCS_LIGHT_SOURCES['CIE 1964 10 Degree Standard Observer']) - -CCS_LIGHT_SOURCES['CIE 1931 2 Degree Standard Observer'].update( - CCS_LIGHT_SOURCES_NIST_TRADITIONAL_STANDARD_OBSERVER_2_DEGREE_CIE1931) -CCS_LIGHT_SOURCES['CIE 1964 10 Degree Standard Observer'].update( - CCS_LIGHT_SOURCES_NIST_TRADITIONAL_STANDARD_OBSERVER_10_DEGREE_CIE1964) - -CCS_LIGHT_SOURCES['CIE 1931 2 Degree Standard Observer'].update( - CCS_LIGHT_SOURCES_NIST_LED_STANDARD_OBSERVER_2_DEGREE_CIE1931) -CCS_LIGHT_SOURCES['CIE 1964 10 Degree Standard Observer'].update( - CCS_LIGHT_SOURCES_NIST_LED_STANDARD_OBSERVER_10_DEGREE_CIE1964) - -CCS_LIGHT_SOURCES['CIE 1931 2 Degree Standard Observer'].update( - CCS_LIGHT_SOURCES_NIST_PHILIPS_STANDARD_OBSERVER_2_DEGREE_CIE1931) -CCS_LIGHT_SOURCES['CIE 1964 10 Degree Standard Observer'].update( - CCS_LIGHT_SOURCES_NIST_PHILIPS_STANDARD_OBSERVER_10_DEGREE_CIE1964) +CCS_LIGHT_SOURCES["cie_2_1931"] = CCS_LIGHT_SOURCES[ + "CIE 1931 2 Degree Standard Observer" +] +CCS_LIGHT_SOURCES["cie_10_1964"] = CCS_LIGHT_SOURCES[ + "CIE 1964 10 Degree Standard Observer" +] -CCS_LIGHT_SOURCES['CIE 1931 2 Degree Standard Observer'].update( - CCS_LIGHT_SOURCES_COMMON_STANDARD_OBSERVER_2_DEGREE_CIE1931) -CCS_LIGHT_SOURCES['CIE 1964 10 Degree Standard Observer'].update( - CCS_LIGHT_SOURCES_COMMON_STANDARD_OBSERVER_10_DEGREE_CIE1964) +CCS_LIGHT_SOURCES["CIE 1931 2 Degree Standard Observer"].update( + CCS_LIGHT_SOURCES_NIST_TRADITIONAL_STANDARD_OBSERVER_2_DEGREE_CIE1931 +) +CCS_LIGHT_SOURCES["CIE 1964 10 Degree Standard Observer"].update( + CCS_LIGHT_SOURCES_NIST_TRADITIONAL_STANDARD_OBSERVER_10_DEGREE_CIE1964 +) + +CCS_LIGHT_SOURCES["CIE 1931 2 Degree Standard Observer"].update( + CCS_LIGHT_SOURCES_NIST_LED_STANDARD_OBSERVER_2_DEGREE_CIE1931 +) +CCS_LIGHT_SOURCES["CIE 1964 10 Degree Standard Observer"].update( + CCS_LIGHT_SOURCES_NIST_LED_STANDARD_OBSERVER_10_DEGREE_CIE1964 +) + +CCS_LIGHT_SOURCES["CIE 1931 2 Degree Standard Observer"].update( + CCS_LIGHT_SOURCES_NIST_PHILIPS_STANDARD_OBSERVER_2_DEGREE_CIE1931 +) +CCS_LIGHT_SOURCES["CIE 1964 10 Degree Standard Observer"].update( + CCS_LIGHT_SOURCES_NIST_PHILIPS_STANDARD_OBSERVER_10_DEGREE_CIE1964 +) + +CCS_LIGHT_SOURCES["CIE 1931 2 Degree Standard Observer"].update( + CCS_LIGHT_SOURCES_COMMON_STANDARD_OBSERVER_2_DEGREE_CIE1931 +) +CCS_LIGHT_SOURCES["CIE 1964 10 Degree Standard Observer"].update( + CCS_LIGHT_SOURCES_COMMON_STANDARD_OBSERVER_10_DEGREE_CIE1964 +) diff --git a/colour/colorimetry/datasets/light_sources/sds.py b/colour/colorimetry/datasets/light_sources/sds.py index 9b7b0e8e09..877f267eb0 100644 --- a/colour/colorimetry/datasets/light_sources/sds.py +++ b/colour/colorimetry/datasets/light_sources/sds.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Spectral Distributions of the Light Sources =========================================== @@ -49,30 +48,38 @@ http://www.cis.rit.edu/research/mcsl2/online/PointerData.xls """ -from __future__ import division, unicode_literals +from __future__ import annotations + +from functools import partial from colour.algebra import LinearInterpolator from colour.colorimetry.spectrum import SpectralDistribution -from colour.utilities import CaseInsensitiveMapping +from colour.hints import Dict +from colour.utilities import LazyCaseInsensitiveMapping -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'DATA_LIGHT_SOURCES_RIT', 'SDS_LIGHT_SOURCES_RIT', - 'DATA_LIGHT_SOURCES_NIST_TRADITIONAL', - 'SDS_LIGHT_SOURCES_NIST_TRADITIONAL', 'DATA_LIGHT_SOURCES_NIST_LED', - 'SDS_LIGHT_SOURCES_NIST_LED', 'DATA_LIGHT_SOURCES_NIST_PHILIPS', - 'SDS_LIGHT_SOURCES_NIST_PHILIPS', 'DATA_LIGHT_SOURCES_COMMON', - 'SDS_LIGHT_SOURCES_COMMON', 'SDS_LIGHT_SOURCES' + "DATA_LIGHT_SOURCES_RIT", + "SDS_LIGHT_SOURCES_RIT", + "DATA_LIGHT_SOURCES_NIST_TRADITIONAL", + "SDS_LIGHT_SOURCES_NIST_TRADITIONAL", + "DATA_LIGHT_SOURCES_NIST_LED", + "SDS_LIGHT_SOURCES_NIST_LED", + "DATA_LIGHT_SOURCES_NIST_PHILIPS", + "SDS_LIGHT_SOURCES_NIST_PHILIPS", + "DATA_LIGHT_SOURCES_COMMON", + "SDS_LIGHT_SOURCES_COMMON", + "SDS_LIGHT_SOURCES", ] -DATA_LIGHT_SOURCES_RIT = { - 'Natural': { +DATA_LIGHT_SOURCES_RIT: Dict = { + "Natural": { 380: 1.88, 385: 2.24, 390: 2.65, @@ -153,9 +160,9 @@ 765: 1.65, 770: 1.47, 775: 1.30, - 780: 1.20 + 780: 1.20, }, - 'Philips TL-84': { + "Philips TL-84": { 380: 0.784, 385: 0.553, 390: 0.416, @@ -236,9 +243,9 @@ 765: 0.100, 770: 0.110, 775: 0.110, - 780: 0.100 + 780: 0.100, }, - 'SA': { + "SA": { 380: 9.80, 385: 10.90, 390: 12.09, @@ -319,9 +326,9 @@ 765: 234.59, 770: 237.01, 775: 239.37, - 780: 241.68 + 780: 241.68, }, - 'SC': { + "SC": { 380: 33.00, 385: 39.92, 390: 47.40, @@ -402,9 +409,9 @@ 765: 58.00, 770: 58.20, 775: 58.50, - 780: 59.10 + 780: 59.10, }, - 'T8 Luxline Plus White': { + "T8 Luxline Plus White": { 380: 0.833, 385: 1.040, 390: 1.296, @@ -485,9 +492,9 @@ 765: 0.360, 770: 0.320, 775: 0.300, - 780: 0.300 + 780: 0.300, }, - 'T8 Polylux 3000': { + "T8 Polylux 3000": { 380: 0.552, 385: 0.381, 390: 0.289, @@ -568,9 +575,9 @@ 765: 0.064, 770: 0.093, 775: 0.051, - 780: 0.027 + 780: 0.027, }, - 'T8 Polylux 4000': { + "T8 Polylux 4000": { 380: 0.498, 385: 0.384, 390: 0.394, @@ -651,9 +658,9 @@ 765: 0.064, 770: 0.093, 775: 0.051, - 780: 0.055 + 780: 0.055, }, - 'Thorn Kolor-rite': { + "Thorn Kolor-rite": { 380: 1.111, 385: 1.316, 390: 1.565, @@ -734,45 +741,25 @@ 765: 1.710, 770: 1.500, 775: 1.330, - 780: 1.200 - } + 780: 1.200, + }, } -SDS_LIGHT_SOURCES_RIT = CaseInsensitiveMapping({ - 'Natural': - SpectralDistribution( - DATA_LIGHT_SOURCES_RIT['Natural'], - name='Natural'), - 'Philips TL-84': - SpectralDistribution( - DATA_LIGHT_SOURCES_RIT['Philips TL-84'], - name='Philips TL-84'), - 'SA': - SpectralDistribution( - DATA_LIGHT_SOURCES_RIT['SA'], - name='SA'), - 'SC': - SpectralDistribution( - DATA_LIGHT_SOURCES_RIT['SC'], - name='SC'), - 'T8 Luxline Plus White': - SpectralDistribution( - DATA_LIGHT_SOURCES_RIT['T8 Luxline Plus White'], - name='T8 Luxline Plus White'), - 'T8 Polylux 3000': - SpectralDistribution( - DATA_LIGHT_SOURCES_RIT['T8 Polylux 3000'], - name='T8 Polylux 3000'), - 'T8 Polylux 4000': - SpectralDistribution( - DATA_LIGHT_SOURCES_RIT['T8 Polylux 4000'], - name='T8 Polylux 4000'), - 'Thorn Kolor-rite': - SpectralDistribution( - DATA_LIGHT_SOURCES_RIT['Thorn Kolor-rite'], - name='Thorn Kolor-rite') -}) # yapf: disable -""" +SDS_LIGHT_SOURCES_RIT: ( + LazyCaseInsensitiveMapping +) = LazyCaseInsensitiveMapping( + ( + key, + partial( + SpectralDistribution, + value, + name=key, + interpolator=LinearInterpolator, + ), + ) + for key, value in DATA_LIGHT_SOURCES_RIT.items() +) +SDS_LIGHT_SOURCES_RIT.__doc__ = """ Spectral distributions of the light sources from the *RIT* *PointerData.xls* spreadsheet. @@ -785,14 +772,10 @@ References ---------- :cite:`Pointer1980a` - -DATA_LIGHT_SOURCES_RIT : CaseInsensitiveMapping - **{'Natural', 'Philips TL-84', 'T8 Luxline Plus White', 'SA', 'SC', - 'T8 Polylux 3000', 'T8 Polylux 4000', 'Thorn Kolor-rite'}** """ -DATA_LIGHT_SOURCES_NIST_TRADITIONAL = { - 'Cool White FL': { +DATA_LIGHT_SOURCES_NIST_TRADITIONAL: Dict = { + "Cool White FL": { 380: 0.03353465, 385: 0.04082136, 390: 0.04995733, @@ -873,9 +856,9 @@ 765: 0.01817522, 770: 0.01183473, 775: 0.00953278, - 780: 0.00920353 + 780: 0.00920353, }, - 'Daylight FL': { + "Daylight FL": { 380: 0.04474924, 385: 0.05659396, 390: 0.07026586, @@ -956,9 +939,9 @@ 765: 0.01382840, 770: 0.01316616, 775: 0.01241027, - 780: 0.01204411 + 780: 0.01204411, }, - 'HPS': { + "HPS": { 380: 0.0083939426, 385: 0.0091861681, 390: 0.0106542846, @@ -1039,9 +1022,9 @@ 765: 0.0802690011, 770: 0.0540981011, 775: 0.0326486660, - 780: 0.0352925378 + 780: 0.0352925378, }, - 'Incandescent': { + "Incandescent": { 380: 0.0221129274, 385: 0.0257550191, 390: 0.0303031416, @@ -1122,9 +1105,9 @@ 765: 0.9669876832, 770: 0.9766525623, 775: 0.9854043089, - 780: 1.0000000000 + 780: 1.0000000000, }, - 'LPS': { + "LPS": { 380: 0.0, 385: 0.0, 390: 0.0, @@ -1205,9 +1188,9 @@ 765: 0.0, 770: 0.0, 775: 0.0, - 780: 0.0 + 780: 0.0, }, - 'Mercury': { + "Mercury": { 380: 0.0104373669, 385: 0.0078158007, 390: 0.0200578779, @@ -1288,9 +1271,9 @@ 765: 0.0096855504, 770: 0.0113490129, 775: 0.0099005410, - 780: 0.0094954250 + 780: 0.0094954250, }, - 'Metal Halide': { + "Metal Halide": { 380: 0.0601404826, 385: 0.0630286148, 390: 0.2366604181, @@ -1371,9 +1354,9 @@ 765: 0.0414979164, 770: 0.0344678340, 775: 0.0236333145, - 780: 0.0254235315 + 780: 0.0254235315, }, - 'Neodimium Incandescent': { + "Neodimium Incandescent": { 380: 0.0334673674, 385: 0.0367106969, 390: 0.0410280255, @@ -1454,9 +1437,9 @@ 765: 0.8999932814, 770: 0.9622718511, 775: 0.9966525876, - 780: 1.0000000000 + 780: 1.0000000000, }, - 'Super HPS': { + "Super HPS": { 380: 0.0000000000, 385: 0.0035961070, 390: 0.0045981474, @@ -1537,9 +1520,9 @@ 765: 0.0604807146, 770: 0.0604807146, 775: 0.0604807146, - 780: 0.0604807146 + 780: 0.0604807146, }, - 'Triphosphor FL': { + "Triphosphor FL": { 380: 0.0000000000, 385: 0.0000000000, 390: 0.0000000000, @@ -1620,64 +1603,35 @@ 765: 0.0079622762, 770: 0.0024428504, 775: 0.0029993177, - 780: 0.0005290507 - } + 780: 0.0005290507, + }, } -SDS_LIGHT_SOURCES_NIST_TRADITIONAL = CaseInsensitiveMapping({ - 'Cool White FL': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_TRADITIONAL['Cool White FL'], - name='Cool White FL'), - 'Daylight FL': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_TRADITIONAL['Daylight FL'], - name='Daylight FL'), - 'HPS': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_TRADITIONAL['HPS'], name='HPS'), - 'Incandescent': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_TRADITIONAL['Incandescent'], - name='Incandescent'), - 'LPS': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_TRADITIONAL['LPS'], name='LPS'), - 'Mercury': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_TRADITIONAL['Mercury'], name='Mercury'), - 'Metal Halide': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_TRADITIONAL['Metal Halide'], - name='Metal Halide'), - 'Neodimium Incandescent': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_TRADITIONAL['Neodimium Incandescent'], - name='Neodimium Incandescent'), - 'Super HPS': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_TRADITIONAL['Super HPS'], - name='Super HPS'), - 'Triphosphor FL': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_TRADITIONAL['Triphosphor FL'], - name='Triphosphor FL') -}) -""" +SDS_LIGHT_SOURCES_NIST_TRADITIONAL: ( + LazyCaseInsensitiveMapping +) = LazyCaseInsensitiveMapping( + ( + key, + partial( + SpectralDistribution, + value, + name=key, + interpolator=LinearInterpolator, + ), + ) + for key, value in DATA_LIGHT_SOURCES_NIST_TRADITIONAL.items() +) +SDS_LIGHT_SOURCES_NIST_TRADITIONAL.__doc__ = """ Spectral distributions of the traditional light sources from the *NIST* *NIST CQS simulation 7.4.xls* spreadsheet. References ---------- :cite:`Ohno2008a` - -SDS_LIGHT_SOURCES_NIST_TRADITIONAL : CaseInsensitiveMapping - **{'Cool White FL', 'Daylight FL', 'HPS', 'Incandescent', 'LPS', 'Mercury', - 'Metal Halide', 'Neodimium Incandescent', 'Super HPS', 'Triphosphor FL'}** """ -DATA_LIGHT_SOURCES_NIST_LED = { - '3-LED-1 (457/540/605)': { +DATA_LIGHT_SOURCES_NIST_LED: Dict = { + "3-LED-1 (457/540/605)": { 380: 8.47479023841784e-08, 385: 5.45760813791522e-07, 390: 3.10162282092079e-06, @@ -1758,9 +1712,9 @@ 765: 1.24216741363154e-25, 770: 9.91044375901472e-27, 775: 7.48346361630974e-28, - 780: 5.34607293395408e-29 + 780: 5.34607293395408e-29, }, - '3-LED-2 (473/545/616)': { + "3-LED-2 (473/545/616)": { 380: 8.80700887841404e-11, 385: 8.64191446543499e-10, 390: 7.48391162992935e-09, @@ -1841,9 +1795,9 @@ 765: 1.96520133984630e-24, 770: 1.19562718661274e-25, 775: 8.49126403034339e-27, - 780: 6.18707499978848e-28 + 780: 6.18707499978848e-28, }, - '3-LED-2 Yellow': { + "3-LED-2 Yellow": { 380: 5.28857670457154e-11, 385: 5.18882652575306e-10, 390: 4.49324294120251e-09, @@ -1924,9 +1878,9 @@ 765: 2.18831363146954e-24, 770: 1.38312763678070e-25, 775: 9.98183844142717e-27, - 780: 7.30800253280185e-28 + 780: 7.30800253280185e-28, }, - '3-LED-3 (465/546/614)': { + "3-LED-3 (465/546/614)": { 380: 4.24356931933744e-09, 385: 3.35055502496331e-08, 390: 2.33461667941402e-07, @@ -2007,9 +1961,9 @@ 765: 2.09993710079583e-24, 770: 1.70971251548166e-25, 775: 1.35854661437952e-26, - 780: 1.03007323231651e-27 + 780: 1.03007323231651e-27, }, - '3-LED-4 (455/547/623)': { + "3-LED-4 (455/547/623)": { 380: 1.48554815599882e-07, 385: 9.10000000000000e-07, 390: 4.91945989968639e-06, @@ -2090,9 +2044,9 @@ 765: 7.96184389637815e-23, 770: 2.37929410472859e-24, 775: 7.60786774257376e-26, - 780: 3.1397886480576e-27 + 780: 3.1397886480576e-27, }, - '4-LED No Yellow': { + "4-LED No Yellow": { 380: 5.00830470941566e-06, 385: 2.37160686348979e-05, 390: 9.94095202401805e-05, @@ -2173,9 +2127,9 @@ 765: 1.55965542079349e-24, 770: 3.62239387510338e-26, 775: 7.42464806370353e-28, - 780: 1.34297913021045e-29 + 780: 1.34297913021045e-29, }, - '4-LED Yellow': { + "4-LED Yellow": { 380: 8.34073345191213e-07, 385: 4.62309206822133e-06, 390: 2.26138576311572e-05, @@ -2256,9 +2210,9 @@ 765: 6.93791409316552e-22, 770: 2.01795866069721e-23, 775: 5.17975154307456e-25, - 780: 1.17332723412027e-26 + 780: 1.17332723412027e-26, }, - '4-LED-1 (461/526/576/624)': { + "4-LED-1 (461/526/576/624)": { 380: 2.47034736673368e-08, 385: 1.74863383163897e-07, 390: 1.09241483462778e-06, @@ -2339,9 +2293,9 @@ 765: 2.27129761972571e-22, 770: 6.31463339864241e-24, 775: 1.54930155880890e-25, - 780: 3.35458329193818e-27 + 780: 3.35458329193818e-27, }, - '4-LED-2 (447/512/573/627)': { + "4-LED-2 (447/512/573/627)": { 380: 3.47088525590474e-06, 385: 1.73227787723854e-05, 390: 7.62995752388568e-05, @@ -2422,9 +2376,9 @@ 765: 1.61410755281383e-21, 770: 4.81433876182624e-23, 775: 1.26722581811040e-24, - 780: 2.94363913832964e-26 + 780: 2.94363913832964e-26, }, - 'Luxeon WW 2880': { + "Luxeon WW 2880": { 380: 0.0000000000, 385: 0.0000000000, 390: 0.0000000000, @@ -2505,9 +2459,9 @@ 765: 1.0039448e-05, 770: 8.9298495e-06, 775: 7.6177480e-06, - 780: 7.0016420e-06 + 780: 7.0016420e-06, }, - 'PHOS-1': { + "PHOS-1": { 380: 0.0111298531, 385: 0.0181132825, 390: 0.0278369484, @@ -2588,9 +2542,9 @@ 765: 7.47174315193493e-05, 770: 2.58870000000000e-05, 775: 8.46950409085507e-06, - 780: 2.61668300203868e-06 + 780: 2.61668300203868e-06, }, - 'PHOS-2': { + "PHOS-2": { 380: 6.24336458874078e-06, 385: 1.80201765628625e-05, 390: 4.91152235508884e-05, @@ -2671,9 +2625,9 @@ 765: 1.31785543464018e-10, 770: 2.57451133233467e-11, 775: 4.74939818412281e-12, - 780: 8.27368713020501e-13 + 780: 8.27368713020501e-13, }, - 'PHOS-3': { + "PHOS-3": { 380: 2.51463414652205e-05, 385: 6.85384929115295e-05, 390: 0.0001764051, @@ -2754,9 +2708,9 @@ 765: 2.97187556394679e-11, 770: 5.48247997165375e-12, 775: 9.55081042264917e-13, - 780: 1.57115898612229e-13 + 780: 1.57115898612229e-13, }, - 'PHOS-4': { + "PHOS-4": { 380: 2.65174294164563e-05, 385: 7.20892236476949e-05, 390: 0.0001850652, @@ -2837,9 +2791,9 @@ 765: 3.17087855185187e-11, 770: 5.84959831217044e-12, 775: 1.01903527241427e-12, - 780: 1.67636709023523e-13 + 780: 1.67636709023523e-13, }, - 'Phosphor LED YAG': { + "Phosphor LED YAG": { 380: 0.0007674402, 385: 0.0007988052, 390: 0.0009481510, @@ -2920,81 +2874,31 @@ 765: 0.0102635827, 770: 0.0091149715, 775: 0.0079991914, - 780: 0.0070995878 - } + 780: 0.0070995878, + }, } -SDS_LIGHT_SOURCES_NIST_LED = CaseInsensitiveMapping({ - '3-LED-1 (457/540/605)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_LED['3-LED-1 (457/540/605)'], - name='3-LED-1 (457/540/605)'), - '3-LED-2 (473/545/616)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_LED['3-LED-2 (473/545/616)'], - name='3-LED-2 (473/545/616)'), - '3-LED-2 Yellow': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_LED['3-LED-2 Yellow'], - name='3-LED-2 Yellow'), - '3-LED-3 (465/546/614)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_LED['3-LED-3 (465/546/614)'], - name='3-LED-3 (465/546/614)'), - '3-LED-4 (455/547/623)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_LED['3-LED-4 (455/547/623)'], - name='3-LED-4 (455/547/623)'), - '4-LED No Yellow': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_LED['4-LED No Yellow'], - name='4-LED No Yellow'), - '4-LED Yellow': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_LED['4-LED Yellow'], name='4-LED Yellow'), - '4-LED-1 (461/526/576/624)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_LED['4-LED-1 (461/526/576/624)'], - name='4-LED-1 (461/526/576/624)'), - '4-LED-2 (447/512/573/627)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_LED['4-LED-2 (447/512/573/627)'], - name='4-LED-2 (447/512/573/627)'), - 'Luxeon WW 2880': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_LED['Luxeon WW 2880'], - name='Luxeon WW 2880'), - 'PHOS-1': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_LED['PHOS-1'], name='PHOS-1'), - 'PHOS-2': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_LED['PHOS-2'], name='PHOS-2'), - 'PHOS-3': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_LED['PHOS-3'], name='PHOS-3'), - 'PHOS-4': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_LED['PHOS-4'], name='PHOS-4'), - 'Phosphor LED YAG': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_LED['Phosphor LED YAG'], - name='Phosphor LED YAG') -}) -""" +SDS_LIGHT_SOURCES_NIST_LED: ( + LazyCaseInsensitiveMapping +) = LazyCaseInsensitiveMapping( + ( + key, + partial( + SpectralDistribution, + value, + name=key, + interpolator=LinearInterpolator, + ), + ) + for key, value in DATA_LIGHT_SOURCES_NIST_LED.items() +) +SDS_LIGHT_SOURCES_NIST_LED.__doc__ = """ Spectral distributions of the LED light sources from the *NIST* *NIST CQS simulation 7.4.xls* spreadsheet. - -SDS_LIGHT_SOURCES_NIST_LED : CaseInsensitiveMapping - **{'3-LED-1 (457/540/605)', '3-LED-2 (473/545/616)', '3-LED-2 Yellow', - '3-LED-3 (465/546/614)', '3-LED-4 (455/547/623)', '4-LED No Yellow', - '4-LED Yellow', '4-LED-1 (461/526/576/624)', '4-LED-2 (447/512/573/627)', - 'Luxeon WW 2880', 'PHOS-1', 'PHOS-2', 'PHOS-3', 'PHOS-4', - 'Phosphor LED YAG'}** """ -DATA_LIGHT_SOURCES_NIST_PHILIPS = { - '60 A/W (Soft White)': { +DATA_LIGHT_SOURCES_NIST_PHILIPS: Dict = { + "60 A/W (Soft White)": { 380: 0.0221129274, 385: 0.0257550191, 390: 0.0303031416, @@ -3075,9 +2979,9 @@ 765: 0.9669876832, 770: 0.9766525623, 775: 0.9854043089, - 780: 1.0000000000 + 780: 1.0000000000, }, - 'C100S54 (HPS)': { + "C100S54 (HPS)": { 380: 0.0090968865, 385: 0.0099177519, 390: 0.0106049886, @@ -3158,9 +3062,9 @@ 765: 0.0290665851, 770: 0.0318508294, 775: 0.0300437580, - 780: 0.0343141879 + 780: 0.0343141879, }, - 'C100S54C (HPS)': { + "C100S54C (HPS)": { 380: 0.0000000000, 385: 0.0091384562, 390: 0.0210275171, @@ -3241,9 +3145,9 @@ 765: 0.1421969643, 770: 0.1445350862, 775: 0.1425608179, - 780: 0.1393291025 + 780: 0.1393291025, }, - 'F32T8/TL830 (Triphosphor)': { + "F32T8/TL830 (Triphosphor)": { 380: 0.0039380786, 385: 0.0021247895, 390: 0.0010924490, @@ -3324,9 +3228,9 @@ 765: 0.0021757603, 770: 0.0016340710, 775: 0.0007446283, - 780: 0.0000000000 + 780: 0.0000000000, }, - 'F32T8/TL835 (Triphosphor)': { + "F32T8/TL835 (Triphosphor)": { 380: 0.0044539119, 385: 0.0027246990, 390: 0.0014395978, @@ -3407,9 +3311,9 @@ 765: 0.0028316149, 770: 0.0020137199, 775: 0.0020856503, - 780: 0.0015424431 + 780: 0.0015424431, }, - 'F32T8/TL841 (Triphosphor)': { + "F32T8/TL841 (Triphosphor)": { 380: 0.0040371862, 385: 0.0024796605, 390: 0.0014675082, @@ -3490,9 +3394,9 @@ 765: 0.0019168880, 770: 0.0010922856, 775: 0.0004971904, - 780: 0.0000000000 + 780: 0.0000000000, }, - 'F32T8/TL850 (Triphosphor)': { + "F32T8/TL850 (Triphosphor)": { 380: 0.0034742373, 385: 0.0023983574, 390: 0.0018341880, @@ -3573,9 +3477,9 @@ 765: 0.0029392159, 770: 0.0029753244, 775: 0.0019264764, - 780: 0.0031394812 + 780: 0.0031394812, }, - 'F32T8/TL865 /PLUS (Triphosphor)': { + "F32T8/TL865 /PLUS (Triphosphor)": { 380: 0.0043823207, 385: 0.0033077391, 390: 0.0028637083, @@ -3656,9 +3560,9 @@ 765: 0.0072031048, 770: 0.0075045320, 775: 0.0070549007, - 780: 0.0062780959 + 780: 0.0062780959, }, - 'F34/CW/RS/EW (Cool White FL)': { + "F34/CW/RS/EW (Cool White FL)": { 380: 0.0296401248, 385: 0.0373569560, 390: 0.0467012986, @@ -3739,9 +3643,9 @@ 765: 0.0114365646, 770: 0.0102565619, 775: 0.0107649726, - 780: 0.0787313519 + 780: 0.0787313519, }, - 'F34T12/LW/RS /EW': { + "F34T12/LW/RS /EW": { 380: 0.0138848383, 385: 0.0171879452, 390: 0.0227765198, @@ -3822,9 +3726,9 @@ 765: 0.0048038975, 770: 0.0058633690, 775: 0.0077827634, - 780: 0.0553820445 + 780: 0.0553820445, }, - 'F34T12WW/RS /EW (Warm White FL)': { + "F34T12WW/RS /EW (Warm White FL)": { 380: 0.0183484283, 385: 0.0208397838, 390: 0.0263238473, @@ -3905,9 +3809,9 @@ 765: 0.0162500215, 770: 0.0154892424, 775: 0.0161230441, - 780: 0.0941165395 + 780: 0.0941165395, }, - 'F40/C50 (Broadband FL)': { + "F40/C50 (Broadband FL)": { 380: 0.0279518444, 385: 0.0297109166, 390: 0.0329570640, @@ -3988,9 +3892,9 @@ 765: 0.0689298270, 770: 0.0730299188, 775: 0.0751929522, - 780: 0.0561935817 + 780: 0.0561935817, }, - 'F40/C75 (Broadband FL)': { + "F40/C75 (Broadband FL)": { 380: 0.0560479743, 385: 0.0650016588, 390: 0.0822455418, @@ -4071,9 +3975,9 @@ 765: 0.0765039678, 770: 0.0585353785, 775: 0.0536685951, - 780: 0.0000000000 + 780: 0.0000000000, }, - 'F40/CWX (Broadband FL)': { + "F40/CWX (Broadband FL)": { 380: 0.0273834847, 385: 0.0335910321, 390: 0.0415441874, @@ -4154,9 +4058,9 @@ 765: 0.0216177183, 770: 0.0225290016, 775: 0.0228098117, - 780: 0.0234304973 + 780: 0.0234304973, }, - 'F40/DX (Broadband FL)': { + "F40/DX (Broadband FL)": { 380: 0.0377831211, 385: 0.0479805195, 390: 0.0605756851, @@ -4237,9 +4141,9 @@ 765: 0.0096318955, 770: 0.0121471762, 775: 0.0047304152, - 780: 0.0000000000 + 780: 0.0000000000, }, - 'F40/DXTP (Delux FL)': { + "F40/DXTP (Delux FL)": { 380: 0.0453299851, 385: 0.0565911781, 390: 0.0698329640, @@ -4320,9 +4224,9 @@ 765: 0.0178226176, 770: 0.0166535773, 775: 0.0162608849, - 780: 0.0131835384 + 780: 0.0131835384, }, - 'F40/N (Natural FL)': { + "F40/N (Natural FL)": { 380: 0.0310803197, 385: 0.0378149459, 390: 0.0472093878, @@ -4403,9 +4307,9 @@ 765: 0.0816302019, 770: 0.0794656172, 775: 0.0624670576, - 780: 0.0538005265 + 780: 0.0538005265, }, - 'H38HT-100 (Mercury)': { + "H38HT-100 (Mercury)": { 380: 0.0000000000, 385: 0.0116674647, 390: 0.0303725622, @@ -4486,9 +4390,9 @@ 765: 0.0850852124, 770: 0.0864842549, 775: 0.0853029284, - 780: 0.0833691938 + 780: 0.0833691938, }, - 'H38JA-100/DX (Mercury DX)': { + "H38JA-100/DX (Mercury DX)": { 380: 0.0000000000, 385: 0.0142531794, 390: 0.0355109960, @@ -4569,9 +4473,9 @@ 765: 0.1137426110, 770: 0.1156128625, 775: 0.1140336555, - 780: 0.1114486233 + 780: 0.1114486233, }, - 'MHC100/U/MP /3K': { + "MHC100/U/MP /3K": { 380: 0.0376147854, 385: 0.0647066508, 390: 0.0898482711, @@ -4652,9 +4556,9 @@ 765: 0.1081531758, 770: 0.1143784298, 775: 0.1376604646, - 780: 0.1168708812 + 780: 0.1168708812, }, - 'MHC100/U/MP /4K': { + "MHC100/U/MP /4K": { 380: 0.0637674013, 385: 0.1123934055, 390: 0.1593879234, @@ -4735,9 +4639,9 @@ 765: 0.1418151423, 770: 0.1594540493, 775: 0.2387771482, - 780: 0.1694033761 + 780: 0.1694033761, }, - 'SDW-T 100W/LV (Super HPS)': { + "SDW-T 100W/LV (Super HPS)": { 380: 0.0000000000, 385: 0.0141961629, 390: 0.0271220229, @@ -4818,118 +4722,31 @@ 765: 0.2452291219, 770: 0.2492613850, 775: 0.2458566141, - 780: 0.2402832833 - } + 780: 0.2402832833, + }, } -SDS_LIGHT_SOURCES_NIST_PHILIPS = CaseInsensitiveMapping({ - '60 A/W (Soft White)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['60 A/W (Soft White)'], - name='60 A/W (Soft White)'), - 'C100S54 (HPS)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['C100S54 (HPS)'], - name='C100S54 (HPS)'), - 'C100S54C (HPS)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['C100S54C (HPS)'], - name='C100S54C (HPS)'), - 'F32T8/TL830 (Triphosphor)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['F32T8/TL830 (Triphosphor)'], - name='F32T8/TL830 (Triphosphor)'), - 'F32T8/TL835 (Triphosphor)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['F32T8/TL835 (Triphosphor)'], - name='F32T8/TL835 (Triphosphor)'), - 'F32T8/TL841 (Triphosphor)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['F32T8/TL841 (Triphosphor)'], - name='F32T8/TL841 (Triphosphor)'), - 'F32T8/TL850 (Triphosphor)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['F32T8/TL850 (Triphosphor)'], - name='F32T8/TL850 (Triphosphor)'), - 'F32T8/TL865 /PLUS (Triphosphor)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['F32T8/TL865 /PLUS (Triphosphor)'], - name='F32T8/TL865 /PLUS (Triphosphor)'), - 'F34/CW/RS/EW (Cool White FL)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['F34/CW/RS/EW (Cool White FL)'], - name='F34/CW/RS/EW (Cool White FL)'), - 'F34T12/LW/RS /EW': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['F34T12/LW/RS /EW'], - name='F34T12/LW/RS /EW'), - 'F34T12WW/RS /EW (Warm White FL)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['F34T12WW/RS /EW (Warm White FL)'], - name='F34T12WW/RS /EW (Warm White FL)'), - 'F40/C50 (Broadband FL)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['F40/C50 (Broadband FL)'], - name='F40/C50 (Broadband FL)'), - 'F40/C75 (Broadband FL)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['F40/C75 (Broadband FL)'], - name='F40/C75 (Broadband FL)'), - 'F40/CWX (Broadband FL)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['F40/CWX (Broadband FL)'], - name='F40/CWX (Broadband FL)'), - 'F40/DX (Broadband FL)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['F40/DX (Broadband FL)'], - name='F40/DX (Broadband FL)'), - 'F40/DXTP (Delux FL)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['F40/DXTP (Delux FL)'], - name='F40/DXTP (Delux FL)'), - 'F40/N (Natural FL)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['F40/N (Natural FL)'], - name='F40/N (Natural FL)'), - 'H38HT-100 (Mercury)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['H38HT-100 (Mercury)'], - name='H38HT-100 (Mercury)'), - 'H38JA-100/DX (Mercury DX)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['H38JA-100/DX (Mercury DX)'], - name='H38JA-100/DX (Mercury DX)'), - 'MHC100/U/MP /3K': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['MHC100/U/MP /3K'], - name='MHC100/U/MP /3K'), - 'MHC100/U/MP /4K': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['MHC100/U/MP /4K'], - name='MHC100/U/MP /4K'), - 'SDW-T 100W/LV (Super HPS)': - SpectralDistribution( - DATA_LIGHT_SOURCES_NIST_PHILIPS['SDW-T 100W/LV (Super HPS)'], - name='SDW-T 100W/LV (Super HPS)') -}) -""" +SDS_LIGHT_SOURCES_NIST_PHILIPS: ( + LazyCaseInsensitiveMapping +) = LazyCaseInsensitiveMapping( + ( + key, + partial( + SpectralDistribution, + value, + name=key, + interpolator=LinearInterpolator, + ), + ) + for key, value in DATA_LIGHT_SOURCES_NIST_PHILIPS.items() +) +SDS_LIGHT_SOURCES_NIST_PHILIPS.__doc__ = """ Spectral distributions of the Philips light sources from the *NIST* *NIST CQS simulation 7.4.xls* spreadsheet. - -SDS_LIGHT_SOURCES_NIST_PHILIPS : CaseInsensitiveMapping - **{'60 A/W (Soft White)', 'C100S54 (HPS)', 'C100S54C (HPS)', - 'F32T8/TL830 (Triphosphor)', 'F32T8/TL835 (Triphosphor)', - 'F32T8/TL841 (Triphosphor)', 'F32T8/TL850 (Triphosphor)', - 'F32T8/TL865 /PLUS (Triphosphor)', 'F34/CW/RS/EW (Cool White FL)', - 'F34T12/LW/RS /EW', 'F34T12WW/RS /EW (Warm White FL)', - 'F40/C50 (Broadband FL)', 'F40/C75 (Broadband FL)', - 'F40/CWX (Broadband FL)', 'F40/DX (Broadband FL)', 'F40/DXTP (Delux FL)', - 'F40/N (Natural FL)', 'H38HT-100 (Mercury)', 'H38JA-100/DX (Mercury DX)', - 'MHC100/U/MP /3K', 'MHC100/U/MP /4K', 'SDW-T 100W/LV (Super HPS)'}** """ -DATA_LIGHT_SOURCES_COMMON = { - 'Kinoton 75P': { +DATA_LIGHT_SOURCES_COMMON: Dict = { + "Kinoton 75P": { 380: 0.0001099667, 382: 0.0001415667, 384: 0.0001502000, @@ -5130,44 +4947,48 @@ 774: 0.0000183800, 776: 0.0000155000, 778: 0.0000097800, - 780: 0.0000141000 + 780: 0.0000141000, } } -SDS_LIGHT_SOURCES_COMMON = CaseInsensitiveMapping({ - 'Kinoton 75P': - SpectralDistribution( - DATA_LIGHT_SOURCES_COMMON['Kinoton 75P'], name='Kinoton 75P') -}) +SDS_LIGHT_SOURCES_COMMON: ( + LazyCaseInsensitiveMapping +) = LazyCaseInsensitiveMapping( + { + "Kinoton 75P": partial( + SpectralDistribution, + DATA_LIGHT_SOURCES_COMMON["Kinoton 75P"], + name="Kinoton 75P", + interpolator=LinearInterpolator, + ) + } +) """ Spectral distributions of the common light sources. References ---------- :cite:`Houston2015a` - -SDS_LIGHT_SOURCES_COMMON : CaseInsensitiveMapping - **{'Kinoton 75P', }** """ -SDS_LIGHT_SOURCES = CaseInsensitiveMapping(SDS_LIGHT_SOURCES_RIT) +SDS_LIGHT_SOURCES: (LazyCaseInsensitiveMapping) = LazyCaseInsensitiveMapping( + SDS_LIGHT_SOURCES_RIT +) SDS_LIGHT_SOURCES.__doc__ = """ Spectral distributions of the light sources. +Notes +----- +- *CIE 15:2004* recommends using linear interpolation for + *CIE Standard Illuminant D Series*, for consistency all the illuminants are + using a linear interpolator. + References ---------- :cite:`Houston2015a`, :cite:`Ohno2008a`, :cite:`Pointer1980a` - -SDS_LIGHT_SOURCES : CaseInsensitiveMapping """ SDS_LIGHT_SOURCES.update(SDS_LIGHT_SOURCES_NIST_TRADITIONAL) SDS_LIGHT_SOURCES.update(SDS_LIGHT_SOURCES_NIST_LED) SDS_LIGHT_SOURCES.update(SDS_LIGHT_SOURCES_NIST_PHILIPS) SDS_LIGHT_SOURCES.update(SDS_LIGHT_SOURCES_COMMON) - -# *CIE 15:2004* recommends using linear interpolation for -# *CIE Standard Illuminant D Series*, for consistency all the light sources are -# using a linear interpolator. -for _sd in SDS_LIGHT_SOURCES.values(): - _sd.interpolator = LinearInterpolator diff --git a/colour/colorimetry/dominant.py b/colour/colorimetry/dominant.py index 990fb2a61f..122ee0fc7d 100644 --- a/colour/colorimetry/dominant.py +++ b/colour/colorimetry/dominant.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ Dominant Wavelength and Purity ============================== -Defines objects to compute the *dominant wavelength* and *purity* of a colour -and related quantities: +Defines the objects to compute the *dominant wavelength* and *purity* of a +colour and related quantities: - :func:`colour.dominant_wavelength` - :func:`colour.complementary_wavelength` @@ -22,52 +21,71 @@ whitepaper_howtocalculateluminositywavelengthandpurity.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import scipy.spatial.distance -from colour.algebra import (euclidean_distance, extend_line_segment, - intersect_line_segments) -from colour.colorimetry import MSDS_CMFS +from colour.algebra import ( + euclidean_distance, + extend_line_segment, + intersect_line_segments, +) +from colour.colorimetry import ( + MultiSpectralDistributions, + handle_spectral_arguments, +) +from colour.hints import ( + ArrayLike, + Boolean, + FloatingOrNDArray, + NDArray, + Optional, + Tuple, +) from colour.models import XYZ_to_xy from colour.utilities import as_float_array -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'closest_spectral_locus_wavelength', 'dominant_wavelength', - 'complementary_wavelength', 'excitation_purity', 'colorimetric_purity' + "closest_spectral_locus_wavelength", + "dominant_wavelength", + "complementary_wavelength", + "excitation_purity", + "colorimetric_purity", ] -def closest_spectral_locus_wavelength(xy, xy_n, xy_s, inverse=False): +def closest_spectral_locus_wavelength( + xy: ArrayLike, xy_n: ArrayLike, xy_s: ArrayLike, inverse: Boolean = False +) -> Tuple[NDArray, NDArray]: """ - Returns the coordinates and closest spectral locus wavelength index to the + Return the coordinates and closest spectral locus wavelength index to the point where the line defined by the given achromatic stimulus :math:`xy_n` to colour stimulus :math:`xy_n` *CIE xy* chromaticity coordinates intersects the spectral locus. Parameters ---------- - xy : array_like + xy Colour stimulus *CIE xy* chromaticity coordinates. - xy_n : array_like + xy_n Achromatic stimulus *CIE xy* chromaticity coordinates. - xy_s : array_like + xy_s Spectral locus *CIE xy* chromaticity coordinates. - inverse : bool, optional + inverse The intersection will be computed using the colour stimulus :math:`xy` to achromatic stimulus :math:`xy_n` inverse direction. Returns ------- - tuple + :class:`tuple` Closest wavelength index, intersection point *CIE xy* chromaticity coordinates. @@ -78,6 +96,7 @@ def closest_spectral_locus_wavelength(xy, xy_n, xy_s, inverse=False): Examples -------- + >>> from colour.colorimetry import MSDS_CMFS >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] >>> xy = np.array([0.54369557, 0.32107944]) >>> xy_n = np.array([0.31270000, 0.32900000]) @@ -93,21 +112,26 @@ def closest_spectral_locus_wavelength(xy, xy_n, xy_s, inverse=False): xy_n = np.resize(xy_n, xy.shape) xy_s = as_float_array(xy_s) - xy_e = (extend_line_segment(xy, xy_n) - if inverse else extend_line_segment(xy_n, xy)) + xy_e = ( + extend_line_segment(xy, xy_n) + if inverse + else extend_line_segment(xy_n, xy) + ) # Closing horse-shoe shape to handle line of purples intersections. xy_s = np.vstack([xy_s, xy_s[0, :]]) xy_wl = intersect_line_segments( np.concatenate((xy_n, xy_e), -1), - np.hstack([xy_s, np.roll(xy_s, 1, axis=0)])).xy + np.hstack([xy_s, np.roll(xy_s, 1, axis=0)]), + ).xy xy_wl = xy_wl[~np.isnan(xy_wl).any(axis=-1)] if not len(xy_wl): raise ValueError( - 'No closest spectral locus wavelength index and coordinates found ' - 'for "{0}" colour stimulus and "{1}" achromatic stimulus "xy" ' - 'chromaticity coordinates!'.format(xy, xy_n)) + f"No closest spectral locus wavelength index and coordinates " + f'found for "{xy}" colour stimulus and "{xy_n}" achromatic ' + f'stimulus "xy" chromaticity coordinates!' + ) i_wl = np.argmin(scipy.spatial.distance.cdist(xy_wl, xy_s), axis=-1) @@ -117,40 +141,43 @@ def closest_spectral_locus_wavelength(xy, xy_n, xy_s, inverse=False): return i_wl, xy_wl -def dominant_wavelength(xy, - xy_n, - cmfs=MSDS_CMFS['CIE 1931 2 Degree Standard Observer'], - inverse=False): +def dominant_wavelength( + xy: ArrayLike, + xy_n: ArrayLike, + cmfs: Optional[MultiSpectralDistributions] = None, + inverse: bool = False, +) -> Tuple[NDArray, NDArray, NDArray]: """ - Returns the *dominant wavelength* :math:`\\lambda_d` for given colour + Return the *dominant wavelength* :math:`\\lambda_d` for given colour stimulus :math:`xy` and the related :math:`xy_wl` first and :math:`xy_{cw}` second intersection coordinates with the spectral locus. In the eventuality where the :math:`xy_wl` first intersection coordinates - are on the line of purples, the *complementary wavelength* will be - computed in lieu. + are on the line of purples, the *complementary wavelength* will be computed + in lieu. - The *complementary wavelength* is indicated by a negative sign - and the :math:`xy_{cw}` second intersection coordinates which are set by - default to the same value than :math:`xy_wl` first intersection coordinates - will be set to the *complementary dominant wavelength* intersection - coordinates with the spectral locus. + The *complementary wavelength* is indicated by a negative sign and the + :math:`xy_{cw}` second intersection coordinates which are set by default to + the same value than :math:`xy_wl` first intersection coordinates will be + set to the *complementary dominant wavelength* intersection coordinates + with the spectral locus. Parameters ---------- - xy : array_like + xy Colour stimulus *CIE xy* chromaticity coordinates. - xy_n : array_like + xy_n Achromatic stimulus *CIE xy* chromaticity coordinates. - cmfs : XYZ_ColourMatchingFunctions, optional - Standard observer colour matching functions. - inverse : bool, optional + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + inverse Inverse the computation direction to retrieve the *complementary wavelength*. Returns ------- - tuple + :class:`tuple` *Dominant wavelength*, first intersection point *CIE xy* chromaticity coordinates, second intersection point *CIE xy* chromaticity coordinates. @@ -163,10 +190,11 @@ def dominant_wavelength(xy, -------- *Dominant wavelength* computation: + >>> from colour.colorimetry import MSDS_CMFS >>> from pprint import pprint + >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] >>> xy = np.array([0.54369557, 0.32107944]) >>> xy_n = np.array([0.31270000, 0.32900000]) - >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] >>> pprint(dominant_wavelength(xy, xy_n, cmfs)) # doctest: +ELLIPSIS (array(616...), array([ 0.6835474..., 0.3162840...]), @@ -176,12 +204,14 @@ def dominant_wavelength(xy, is located on the line of purples: >>> xy = np.array([0.37605506, 0.24452225]) - >>> pprint(dominant_wavelength(xy, xy_n, cmfs)) # doctest: +ELLIPSIS + >>> pprint(dominant_wavelength(xy, xy_n)) # doctest: +ELLIPSIS (array(-509.0), array([ 0.4572314..., 0.1362814...]), array([ 0.0104096..., 0.7320745...])) """ + cmfs, _illuminant = handle_spectral_arguments(cmfs) + xy = as_float_array(xy) xy_n = np.resize(xy_n, xy.shape) @@ -191,15 +221,19 @@ def dominant_wavelength(xy, xy_cwl = xy_wl wl = cmfs.wavelengths[i_wl] - xy_e = (extend_line_segment(xy, xy_n) - if inverse else extend_line_segment(xy_n, xy)) + xy_e = ( + extend_line_segment(xy, xy_n) + if inverse + else extend_line_segment(xy_n, xy) + ) intersect = intersect_line_segments( - np.concatenate((xy_n, xy_e), -1), np.hstack([xy_s[0], - xy_s[-1]])).intersect + np.concatenate((xy_n, xy_e), -1), np.hstack([xy_s[0], xy_s[-1]]) + ).intersect intersect = np.reshape(intersect, wl.shape) i_wl_r, xy_cwl_r = closest_spectral_locus_wavelength( - xy, xy_n, xy_s, not inverse) + xy, xy_n, xy_s, not inverse + ) wl_r = -cmfs.wavelengths[i_wl_r] wl = np.where(intersect, wl_r, wl) @@ -209,9 +243,12 @@ def dominant_wavelength(xy, def complementary_wavelength( - xy, xy_n, cmfs=MSDS_CMFS['CIE 1931 2 Degree Standard Observer']): + xy: ArrayLike, + xy_n: ArrayLike, + cmfs: Optional[MultiSpectralDistributions] = None, +) -> Tuple[NDArray, NDArray, NDArray]: """ - Returns the *complementary wavelength* :math:`\\lambda_c` for given colour + Return the *complementary wavelength* :math:`\\lambda_c` for given colour stimulus :math:`xy` and the related :math:`xy_wl` first and :math:`xy_{cw}` second intersection coordinates with the spectral locus. @@ -227,16 +264,17 @@ def complementary_wavelength( Parameters ---------- - xy : array_like + xy Colour stimulus *CIE xy* chromaticity coordinates. - xy_n : array_like + xy_n Achromatic stimulus *CIE xy* chromaticity coordinates. - cmfs : XYZ_ColourMatchingFunctions, optional - Standard observer colour matching functions. + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. Returns ------- - tuple + :class:`tuple` *Complementary wavelength*, first intersection point *CIE xy* chromaticity coordinates, second intersection point *CIE xy* chromaticity coordinates. @@ -249,10 +287,11 @@ def complementary_wavelength( -------- *Complementary wavelength* computation: + >>> from colour.colorimetry import MSDS_CMFS >>> from pprint import pprint + >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] >>> xy = np.array([0.37605506, 0.24452225]) >>> xy_n = np.array([0.31270000, 0.32900000]) - >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] >>> pprint(complementary_wavelength(xy, xy_n, cmfs)) # doctest: +ELLIPSIS (array(509.0), array([ 0.0104096..., 0.7320745...]), @@ -262,7 +301,7 @@ def complementary_wavelength( the line of purples: >>> xy = np.array([0.54369557, 0.32107944]) - >>> pprint(complementary_wavelength(xy, xy_n, cmfs)) # doctest: +ELLIPSIS + >>> pprint(complementary_wavelength(xy, xy_n)) # doctest: +ELLIPSIS (array(492.0), array([ 0.0364795 , 0.3384712...]), array([ 0.0364795 , 0.3384712...])) @@ -271,25 +310,28 @@ def complementary_wavelength( return dominant_wavelength(xy, xy_n, cmfs, True) -def excitation_purity(xy, - xy_n, - cmfs=MSDS_CMFS['CIE 1931 2 Degree Standard Observer']): +def excitation_purity( + xy: ArrayLike, + xy_n: ArrayLike, + cmfs: Optional[MultiSpectralDistributions] = None, +) -> FloatingOrNDArray: """ - Returns the *excitation purity* :math:`P_e` for given colour stimulus + Return the *excitation purity* :math:`P_e` for given colour stimulus :math:`xy`. Parameters ---------- - xy : array_like + xy Colour stimulus *CIE xy* chromaticity coordinates. - xy_n : array_like + xy_n Achromatic stimulus *CIE xy* chromaticity coordinates. - cmfs : XYZ_ColourMatchingFunctions, optional - Standard observer colour matching functions. + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. Returns ------- - numeric or array_like + :class:`np.floating` or :class:`numpy.ndarray` *Excitation purity* :math:`P_e`. References @@ -298,9 +340,10 @@ def excitation_purity(xy, Examples -------- + >>> from colour.colorimetry import MSDS_CMFS + >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] >>> xy = np.array([0.54369557, 0.32107944]) >>> xy_n = np.array([0.31270000, 0.32900000]) - >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] >>> excitation_purity(xy, xy_n, cmfs) # doctest: +ELLIPSIS 0.6228856... """ @@ -312,25 +355,28 @@ def excitation_purity(xy, return P_e -def colorimetric_purity(xy, - xy_n, - cmfs=MSDS_CMFS['CIE 1931 2 Degree Standard Observer']): +def colorimetric_purity( + xy: ArrayLike, + xy_n: ArrayLike, + cmfs: Optional[MultiSpectralDistributions] = None, +) -> FloatingOrNDArray: """ - Returns the *colorimetric purity* :math:`P_c` for given colour stimulus + Return the *colorimetric purity* :math:`P_c` for given colour stimulus :math:`xy`. Parameters ---------- - xy : array_like + xy Colour stimulus *CIE xy* chromaticity coordinates. - xy_n : array_like + xy_n Achromatic stimulus *CIE xy* chromaticity coordinates. - cmfs : XYZ_ColourMatchingFunctions, optional - Standard observer colour matching functions. + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. Returns ------- - numeric or array_like + :class:`np.floating` or :class:`numpy.ndarray` *Colorimetric purity* :math:`P_c`. References @@ -339,9 +385,10 @@ def colorimetric_purity(xy, Examples -------- + >>> from colour.colorimetry import MSDS_CMFS + >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] >>> xy = np.array([0.54369557, 0.32107944]) >>> xy_n = np.array([0.31270000, 0.32900000]) - >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] >>> colorimetric_purity(xy, xy_n, cmfs) # doctest: +ELLIPSIS 0.6135828... """ diff --git a/colour/colorimetry/generation.py b/colour/colorimetry/generation.py index 03f51ce0ed..f37f122315 100644 --- a/colour/colorimetry/generation.py +++ b/colour/colorimetry/generation.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Spectral Generation =================== @@ -33,48 +32,84 @@ usp=sharing """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.constants import DEFAULT_FLOAT_DTYPE from colour.colorimetry import ( - SPECTRAL_SHAPE_DEFAULT, MultiSpectralDistributions, SpectralDistribution) -from colour.utilities import CaseInsensitiveMapping, as_float_array, full, ones - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + SPECTRAL_SHAPE_DEFAULT, + MultiSpectralDistributions, + SpectralDistribution, + SpectralShape, +) +from colour.hints import ( + Any, + ArrayLike, + Floating, + Literal, + NDArray, + Optional, + Sequence, + Union, +) +from colour.utilities import ( + CaseInsensitiveMapping, + as_float_array, + full, + ones, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'sd_constant', 'sd_zeros', 'sd_ones', 'msds_constant', 'msds_zeros', - 'msds_ones', 'sd_gaussian_normal', 'sd_gaussian_fwhm', - 'SD_GAUSSIAN_METHODS', 'sd_gaussian', 'sd_single_led_Ohno2005', - 'SD_SINGLE_LED_METHODS', 'sd_single_led', 'sd_multi_leds_Ohno2005', - 'SD_MULTI_LEDS_METHODS', 'sd_multi_leds' + "sd_constant", + "sd_zeros", + "sd_ones", + "msds_constant", + "msds_zeros", + "msds_ones", + "sd_gaussian_normal", + "sd_gaussian_fwhm", + "SD_GAUSSIAN_METHODS", + "sd_gaussian", + "sd_single_led_Ohno2005", + "SD_SINGLE_LED_METHODS", + "sd_single_led", + "sd_multi_leds_Ohno2005", + "SD_MULTI_LEDS_METHODS", + "sd_multi_leds", ] -def sd_constant(k, shape=SPECTRAL_SHAPE_DEFAULT, dtype=None): +def sd_constant( + k: Floating, shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, **kwargs: Any +) -> SpectralDistribution: """ - Returns a spectral distribution of given spectral shape filled with + Return a spectral distribution of given spectral shape filled with constant :math:`k` values. Parameters ---------- - k : numeric + k Constant :math:`k` to fill the spectral distribution with. - shape : SpectralShape, optional + shape Spectral shape used to create the spectral distribution. - dtype : type - Data type used for the spectral distribution. + + Other Parameters + ---------------- + kwargs + {:class:`colour.SpectralDistribution`}, + See the documentation of the previously listed class. Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Constant :math:`k` filled spectral distribution. Notes @@ -91,28 +126,35 @@ def sd_constant(k, shape=SPECTRAL_SHAPE_DEFAULT, dtype=None): 100.0 """ - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE + settings = {"name": f"{k} Constant"} + settings.update(kwargs) - wavelengths = shape.range(dtype) - values = full(len(wavelengths), k, dtype) + wavelengths = shape.range() + values = full(len(wavelengths), k) - name = '{0} Constant'.format(k) - return SpectralDistribution(values, wavelengths, name=name, dtype=dtype) + return SpectralDistribution(values, wavelengths, **settings) -def sd_zeros(shape=SPECTRAL_SHAPE_DEFAULT): +def sd_zeros( + shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, **kwargs: Any +) -> SpectralDistribution: """ - Returns a spectral distribution of given spectral shape filled with zeros. + Return a spectral distribution of given spectral shape filled with zeros. Parameters ---------- - shape : SpectralShape, optional + shape Spectral shape used to create the spectral distribution. + Other Parameters + ---------------- + kwargs + {:func:`colour.sd_constant`}, + See the documentation of the previously listed definition. + Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Zeros filled spectral distribution. Notes @@ -129,21 +171,29 @@ def sd_zeros(shape=SPECTRAL_SHAPE_DEFAULT): 0.0 """ - return sd_constant(0, shape) + return sd_constant(0, shape, **kwargs) -def sd_ones(shape=SPECTRAL_SHAPE_DEFAULT): +def sd_ones( + shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, **kwargs: Any +) -> SpectralDistribution: """ - Returns a spectral distribution of given spectral shape filled with ones. + Return a spectral distribution of given spectral shape filled with ones. Parameters ---------- - shape : SpectralShape, optional + shape Spectral shape used to create the spectral distribution. + Other Parameters + ---------------- + kwargs + {:func:`colour.sd_constant`}, + See the documentation of the previously listed definition. + Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Ones filled spectral distribution. Notes @@ -160,29 +210,38 @@ def sd_ones(shape=SPECTRAL_SHAPE_DEFAULT): 1.0 """ - return sd_constant(1, shape) + return sd_constant(1, shape, **kwargs) -def msds_constant(k, labels, shape=SPECTRAL_SHAPE_DEFAULT, dtype=None): +def msds_constant( + k: Floating, + labels: Sequence, + shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, + **kwargs: Any, +) -> MultiSpectralDistributions: """ - Returns the multi-spectral distributions with given labels and given + Return the multi-spectral distributions with given labels and given spectral shape filled with constant :math:`k` values. Parameters ---------- - k : numeric + k Constant :math:`k` to fill the multi-spectral distributions with. - labels : array_like + labels Names to use for the :class:`colour.SpectralDistribution` class instances. - shape : SpectralShape, optional + shape Spectral shape used to create the multi-spectral distributions. - dtype : type - Data type used for the multi-spectral distributions. + + Other Parameters + ---------------- + kwargs + {:class:`colour.MultiSpectralDistributions`}, + See the documentation of the previously listed class. Returns ------- - MultiSpectralDistributions + :class:`colour.MultiSpectralDistributions` Constant :math:`k` filled multi-spectral distributions. Notes @@ -201,33 +260,43 @@ def msds_constant(k, labels, shape=SPECTRAL_SHAPE_DEFAULT, dtype=None): ['a', 'b', 'c'] """ - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE + settings = {"name": f"{k} Constant"} + settings.update(kwargs) - wavelengths = shape.range(dtype) - values = full([len(wavelengths), len(labels)], k, dtype) + wavelengths = shape.range() + values = full((len(wavelengths), len(labels)), k) - name = '{0} Constant'.format(k) return MultiSpectralDistributions( - values, wavelengths, name=name, labels=labels, dtype=dtype) + values, wavelengths, labels=labels, **settings + ) -def msds_zeros(labels, shape=SPECTRAL_SHAPE_DEFAULT): +def msds_zeros( + labels: Sequence, + shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, + **kwargs: Any, +) -> MultiSpectralDistributions: """ - Returns the multi-spectral distributionss with given labels and given + Return the multi-spectral distributionss with given labels and given spectral shape filled with zeros. Parameters ---------- - labels : array_like + labels Names to use for the :class:`colour.SpectralDistribution` class instances. - shape : SpectralShape, optional + shape Spectral shape used to create the multi-spectral distributions. + Other Parameters + ---------------- + kwargs + {:func:`colour.msds_constant`}, + See the documentation of the previously listed definition. + Returns ------- - MultiSpectralDistributions + :class:`colour.MultiSpectralDistributions` Zeros filled multi-spectral distributions. Notes @@ -246,25 +315,35 @@ def msds_zeros(labels, shape=SPECTRAL_SHAPE_DEFAULT): ['a', 'b', 'c'] """ - return msds_constant(0, labels, shape) + return msds_constant(0, labels, shape, **kwargs) -def msds_ones(labels, shape=SPECTRAL_SHAPE_DEFAULT): +def msds_ones( + labels: Sequence, + shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, + **kwargs: Any, +) -> MultiSpectralDistributions: """ - Returns the multi-spectral distributionss with given labels and given + Return the multi-spectral distributionss with given labels and given spectral shape filled with ones. Parameters ---------- - labels : array_like + labels Names to use for the :class:`colour.SpectralDistribution` class instances. - shape : SpectralShape, optional + shape Spectral shape used to create the multi-spectral distributions. + Other Parameters + ---------------- + kwargs + {:func:`colour.msds_constant`}, + See the documentation of the previously listed definition. + Returns ------- - MultiSpectralDistributions + :class:`colour.MultiSpectralDistributions` Ones filled multi-spectral distributions. Notes @@ -283,27 +362,38 @@ def msds_ones(labels, shape=SPECTRAL_SHAPE_DEFAULT): ['a', 'b', 'c'] """ - return msds_constant(1, labels, shape) + return msds_constant(1, labels, shape, **kwargs) -def sd_gaussian_normal(mu, sigma, shape=SPECTRAL_SHAPE_DEFAULT): +def sd_gaussian_normal( + mu: Floating, + sigma: Floating, + shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, + **kwargs: Any, +) -> SpectralDistribution: """ - Returns a gaussian spectral distribution of given spectral shape at + Return a gaussian spectral distribution of given spectral shape at given mean wavelength :math:`\\mu` and standard deviation :math:`sigma`. Parameters ---------- - mu : numeric + mu Mean wavelength :math:`\\mu` the gaussian spectral distribution will peak at. - sigma : numeric + sigma Standard deviation :math:`sigma` of the gaussian spectral distribution. - shape : SpectralShape, optional + shape Spectral shape used to create the spectral distribution. + Other Parameters + ---------------- + kwargs + {:class:`colour.SpectralDistribution`}, + See the documentation of the previously listed class. + Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Gaussian spectral distribution. Notes @@ -322,34 +412,46 @@ def sd_gaussian_normal(mu, sigma, shape=SPECTRAL_SHAPE_DEFAULT): 0.6065306... """ - wavelengths = shape.range() + settings = {"name": f"{mu}nm - {sigma} Sigma - Gaussian"} + settings.update(kwargs) - values = np.exp(-(wavelengths - mu) ** 2 / (2 * sigma ** 2.)) + wavelengths = shape.range() - name = '{0}nm - {1} Sigma - Gaussian'.format(mu, sigma) + values = np.exp(-((wavelengths - mu) ** 2) / (2 * sigma**2)) - return SpectralDistribution(values, wavelengths, name=name) + return SpectralDistribution(values, wavelengths, **settings) -def sd_gaussian_fwhm(peak_wavelength, fwhm, shape=SPECTRAL_SHAPE_DEFAULT): +def sd_gaussian_fwhm( + peak_wavelength: Floating, + fwhm: Floating, + shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, + **kwargs: Any, +) -> SpectralDistribution: """ - Returns a gaussian spectral distribution of given spectral shape at given + Return a gaussian spectral distribution of given spectral shape at given peak wavelength and full width at half maximum. Parameters ---------- - peak_wavelength : numeric + peak_wavelength Wavelength the gaussian spectral distribution will peak at. - fwhm : numeric + fwhm Full width at half maximum, i.e. width of the gaussian spectral distribution measured between those points on the *y* axis which are half the maximum amplitude. - shape : SpectralShape, optional + shape Spectral shape used to create the spectral distribution. + Other Parameters + ---------------- + kwargs + {:class:`colour.SpectralDistribution`}, + See the documentation of the previously listed class. + Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Gaussian spectral distribution. Notes @@ -368,54 +470,60 @@ def sd_gaussian_fwhm(peak_wavelength, fwhm, shape=SPECTRAL_SHAPE_DEFAULT): 0.3678794... """ - wavelengths = shape.range() + settings = {"name": f"{peak_wavelength}nm - {fwhm} FWHM - Gaussian"} + settings.update(kwargs) - values = np.exp(-((wavelengths - peak_wavelength) / fwhm) ** 2) + wavelengths = shape.range() - name = '{0}nm - {1} FWHM - Gaussian'.format(peak_wavelength, fwhm) + values = np.exp(-(((wavelengths - peak_wavelength) / fwhm) ** 2)) - return SpectralDistribution(values, wavelengths, name=name) + return SpectralDistribution(values, wavelengths, **settings) -SD_GAUSSIAN_METHODS = CaseInsensitiveMapping({ - 'Normal': sd_gaussian_normal, - 'FWHM': sd_gaussian_fwhm -}) +SD_GAUSSIAN_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + {"Normal": sd_gaussian_normal, "FWHM": sd_gaussian_fwhm} +) SD_GAUSSIAN_METHODS.__doc__ = """ Supported gaussian spectral distribution computation methods. - -SD_GAUSSIAN_METHODS : CaseInsensitiveMapping - **{'Normal', 'FWHM'}** """ -def sd_gaussian(mu_peak_wavelength, - sigma_fwhm, - shape=SPECTRAL_SHAPE_DEFAULT, - method='Normal'): +def sd_gaussian( + mu_peak_wavelength: Floating, + sigma_fwhm: Floating, + shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, + method: Union[Literal["Normal", "FWHM"], str] = "Normal", + **kwargs: Any, +) -> SpectralDistribution: """ - Returns a gaussian spectral distribution of given spectral shape using + Return a gaussian spectral distribution of given spectral shape using given method. Parameters ---------- - mu_peak_wavelength : numeric + mu_peak_wavelength Mean wavelength :math:`\\mu` the gaussian spectral distribution will peak at. - sigma_fwhm : numeric + sigma_fwhm Standard deviation :math:`sigma` of the gaussian spectral distribution or Full width at half maximum, i.e. width of the gaussian spectral distribution measured between those points on the *y* axis which are half the maximum amplitude. - shape : SpectralShape, optional + shape Spectral shape used to create the spectral distribution. - method : unicode, optional - **{'Normal', 'FWHM'}**, + method Computation method. + Other Parameters + ---------------- + kwargs + {:func:`colour.colorimetry.sd_gaussian_normal`, + :func:`colour.colorimetry.sd_gaussian_fwhm`}, + See the documentation of the previously listed definitions. + Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Gaussian spectral distribution. Notes @@ -441,30 +549,44 @@ def sd_gaussian(mu_peak_wavelength, 0.3678794... """ - return SD_GAUSSIAN_METHODS[method](mu_peak_wavelength, sigma_fwhm, shape) + method = validate_method(method, SD_GAUSSIAN_METHODS) + + return SD_GAUSSIAN_METHODS[method]( + mu_peak_wavelength, sigma_fwhm, shape, **kwargs + ) -def sd_single_led_Ohno2005(peak_wavelength, fwhm, - shape=SPECTRAL_SHAPE_DEFAULT): +def sd_single_led_Ohno2005( + peak_wavelength: Floating, + fwhm: Floating, + shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, + **kwargs: Any, +) -> SpectralDistribution: """ - Returns a single *LED* spectral distribution of given spectral shape at + Return a single *LED* spectral distribution of given spectral shape at given peak wavelength and full width at half maximum according to *Ohno (2005)* method. Parameters ---------- - peak_wavelength : numeric + peak_wavelength Wavelength the single *LED* spectral distribution will peak at. - fwhm : numeric + fwhm Full width at half maximum, i.e. width of the underlying gaussian spectral distribution measured between those points on the *y* axis which are half the maximum amplitude. - shape : SpectralShape, optional + shape Spectral shape used to create the spectral distribution. + Other Parameters + ---------------- + kwargs + {:func:`colour.colorimetry.sd_gaussian_fwhm`}, + See the documentation of the previously listed definition. + Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Single *LED* spectral distribution. Notes @@ -485,53 +607,60 @@ def sd_single_led_Ohno2005(peak_wavelength, fwhm, 1.0000000... """ - sd = sd_gaussian_fwhm(peak_wavelength, fwhm, shape) + settings = {"name": f"{peak_wavelength}nm - {fwhm} FWHM LED - Ohno (2005)"} + settings.update(kwargs) - sd.values = (sd.values + 2 * sd.values ** 5) / 3 + sd = sd_gaussian_fwhm(peak_wavelength, fwhm, shape, **kwargs) - sd.name = '{0}nm - {1} FWHM LED - Ohno (2005)'.format( - peak_wavelength, fwhm) + sd.values = (sd.values + 2 * sd.values**5) / 3 return sd -SD_SINGLE_LED_METHODS = CaseInsensitiveMapping({ - 'Ohno 2005': sd_single_led_Ohno2005, -}) +SD_SINGLE_LED_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Ohno 2005": sd_single_led_Ohno2005, + } +) SD_SINGLE_LED_METHODS.__doc__ = """ Supported single *LED* spectral distribution computation methods. - -SD_SINGLE_LED_METHODS : CaseInsensitiveMapping - **{'Ohno 2005'}** """ -def sd_single_led(peak_wavelength, - fwhm, - shape=SPECTRAL_SHAPE_DEFAULT, - method='Ohno 2005'): +def sd_single_led( + peak_wavelength: Floating, + fwhm: Floating, + shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, + method: Union[Literal["Ohno 2005"], str] = "Ohno 2005", + **kwargs: Any, +) -> SpectralDistribution: """ - Returns a single *LED* spectral distribution of given spectral shape at + Return a single *LED* spectral distribution of given spectral shape at given peak wavelength and full width at half maximum according to given method. Parameters ---------- - peak_wavelength : numeric + peak_wavelength Wavelength the single *LED* spectral distribution will peak at. - fwhm : numeric + fwhm Full width at half maximum, i.e. width of the underlying gaussian spectral distribution measured between those points on the *y* axis which are half the maximum amplitude. - shape : SpectralShape, optional + shape Spectral shape used to create the spectral distribution. - method : unicode, optional - **{'Ohno 2005'}**, + method Computation method. + Other Parameters + ---------------- + kwargs + {:func:`colour.colorimetry.sd_single_led_Ohno2005`}, + See the documentation of the previously listed definition. + Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Single *LED* spectral distribution. Notes @@ -552,40 +681,53 @@ def sd_single_led(peak_wavelength, 1.0000000... """ - return SD_SINGLE_LED_METHODS[method](peak_wavelength, fwhm, shape) + method = validate_method(method, SD_SINGLE_LED_METHODS) + return SD_SINGLE_LED_METHODS[method]( + peak_wavelength, fwhm, shape, **kwargs + ) -def sd_multi_leds_Ohno2005(peak_wavelengths, - fwhm, - peak_power_ratios=None, - shape=SPECTRAL_SHAPE_DEFAULT): + +def sd_multi_leds_Ohno2005( + peak_wavelengths: ArrayLike, + fwhm: ArrayLike, + peak_power_ratios: Optional[ArrayLike] = None, + shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, + **kwargs: Any, +) -> SpectralDistribution: """ - Returns a multi *LED* spectral distribution of given spectral shape at + Return a multi *LED* spectral distribution of given spectral shape at given peak wavelengths and full widths at half maximum according to *Ohno (2005)* method. - The multi *LED* spectral distribution is generated using many single - *LED* spectral distributions generated with - :func:`colour.sd_single_led_Ohno2005` definition. + The multi *LED* spectral distribution is generated using many single *LED* + spectral distributions generated with :func:`colour.sd_single_led_Ohno2005` + definition. Parameters ---------- - peak_wavelengths : array_like + peak_wavelengths Wavelengths the multi *LED* spectral distribution will peak at, i.e. the peaks for each generated single *LED* spectral distributions. - fwhm : array_like + fwhm Full widths at half maximum, i.e. widths of the underlying gaussian spectral distributions measured between those points on the *y* axis which are half the maximum amplitude. - peak_power_ratios : array_like, optional + peak_power_ratios Peak power ratios for each generated single *LED* spectral distributions. - shape : SpectralShape, optional + shape Spectral shape used to create the spectral distribution. + Other Parameters + ---------------- + kwargs + {:func:`colour.colorimetry.sd_single_led_Ohno2005`}, + See the documentation of the previously listed definition. + Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Multi *LED* spectral distribution. Notes @@ -615,85 +757,84 @@ def sd_multi_leds_Ohno2005(peak_wavelengths, if peak_power_ratios is None: peak_power_ratios = ones(peak_wavelengths.shape) else: - peak_power_ratios = np.resize(peak_power_ratios, - peak_wavelengths.shape) + peak_power_ratios = np.resize( + peak_power_ratios, peak_wavelengths.shape + ) sd = sd_zeros(shape) for (peak_wavelength, fwhm_s, peak_power_ratio) in zip( - peak_wavelengths, fwhm, peak_power_ratios): - sd += sd_single_led_Ohno2005(peak_wavelength, - fwhm_s) * peak_power_ratio - - def _format_array(a): - """ - Formats given array :math:`a`. - - Parameters - ---------- - a : array_like - Array to format + peak_wavelengths, fwhm, peak_power_ratios + ): + sd += ( # type: ignore[misc] + sd_single_led_Ohno2005(peak_wavelength, fwhm_s, **kwargs) + * peak_power_ratio + ) - Returns - ------- - unicode - Formatted array :math:`a`. - """ + def _format_array(a: NDArray) -> str: + """Format given array :math:`a`.""" - return ', '.join([str(e) for e in a]) + return ", ".join([str(e) for e in a]) sd.name = ( - '{0}nm - {1}FWHM - {2} Peak Power Ratios - LED - Ohno (2005)'.format( - _format_array(peak_wavelengths), - _format_array(fwhm), - _format_array(peak_power_ratios), - )) + f"{_format_array(peak_wavelengths)}nm - " + f"{_format_array(fwhm)}FWHM - " + f"{_format_array(peak_power_ratios)} Peak Power Ratios - " + f"LED - Ohno (2005)" + ) return sd -SD_MULTI_LEDS_METHODS = CaseInsensitiveMapping({ - 'Ohno 2005': sd_multi_leds_Ohno2005, -}) +SD_MULTI_LEDS_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Ohno 2005": sd_multi_leds_Ohno2005, + } +) SD_MULTI_LEDS_METHODS.__doc__ = """ Supported multi *LED* spectral distribution computation methods. - -SD_MULTI_LEDS_METHODS : CaseInsensitiveMapping - **{'Ohno 2005'}** """ -def sd_multi_leds(peak_wavelengths, - fwhm, - peak_power_ratios=None, - shape=SPECTRAL_SHAPE_DEFAULT, - method='Ohno 2005'): +def sd_multi_leds( + peak_wavelengths: ArrayLike, + fwhm: ArrayLike, + peak_power_ratios: Optional[ArrayLike] = None, + shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, + method: Union[Literal["Ohno 2005"], str] = "Ohno 2005", + **kwargs: Any, +) -> SpectralDistribution: """ - Returns a multi *LED* spectral distribution of given spectral shape at + Return a multi *LED* spectral distribution of given spectral shape at given peak wavelengths and full widths at half maximum according to given method. Parameters ---------- - peak_wavelengths : array_like + peak_wavelengths Wavelengths the multi *LED* spectral distribution will peak at, i.e. the peaks for each generated single *LED* spectral distributions. - fwhm : array_like + fwhm Full widths at half maximum, i.e. widths of the underlying gaussian spectral distributions measured between those points on the *y* axis which are half the maximum amplitude. - peak_power_ratios : array_like, optional + peak_power_ratios Peak power ratios for each generated single *LED* spectral distributions. - shape : SpectralShape, optional + shape Spectral shape used to create the spectral distribution. - method : unicode, optional - **{'Ohno 2005'}**, + method Computation method. + Other Parameters + ---------------- + kwargs + {:func:`colour.colorimetry.sd_multi_leds_Ohno2005`}, + See the documentation of the previously listed definition. + Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Multi *LED* spectral distribution. Notes @@ -718,5 +859,8 @@ def sd_multi_leds(peak_wavelengths, 0.1295132... """ - return SD_MULTI_LEDS_METHODS[method](peak_wavelengths, fwhm, - peak_power_ratios, shape) + method = validate_method(method, SD_MULTI_LEDS_METHODS) + + return SD_MULTI_LEDS_METHODS[method]( + peak_wavelengths, fwhm, peak_power_ratios, shape, **kwargs + ) diff --git a/colour/colorimetry/illuminants.py b/colour/colorimetry/illuminants.py index 87d6e2c027..dd4e164ae1 100644 --- a/colour/colorimetry/illuminants.py +++ b/colour/colorimetry/illuminants.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Illuminants =========== -Defines *CIE* illuminants computation related objects: +Defines the *CIE* illuminants computation related objects: - :func:`colour.sd_CIE_standard_illuminant_A` - :func:`colour.sd_CIE_illuminant_D_series` @@ -25,30 +24,42 @@ ISBN:978-0-471-39918-6 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.algebra import LinearInterpolator -from colour.colorimetry import (SPECTRAL_SHAPE_DEFAULT, - SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES, - SpectralDistribution) -from colour.utilities import as_float_array, as_numeric, tsplit - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.colorimetry import ( + SPECTRAL_SHAPE_DEFAULT, + SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES, + SpectralDistribution, + SpectralShape, +) +from colour.hints import ( + ArrayLike, + Boolean, + FloatingOrArrayLike, + FloatingOrNDArray, +) +from colour.utilities import as_float_array, as_float, tsplit + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'sd_CIE_standard_illuminant_A', 'sd_CIE_illuminant_D_series', - 'daylight_locus_function' + "sd_CIE_standard_illuminant_A", + "sd_CIE_illuminant_D_series", + "daylight_locus_function", ] -def sd_CIE_standard_illuminant_A(shape=SPECTRAL_SHAPE_DEFAULT): +def sd_CIE_standard_illuminant_A( + shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, +) -> SpectralDistribution: """ *CIE Standard Illuminant A* is intended to represent typical, domestic, tungsten-filament lighting. @@ -60,13 +71,13 @@ def sd_CIE_standard_illuminant_A(shape=SPECTRAL_SHAPE_DEFAULT): Parameters ---------- - shape : SpectralShape, optional + shape Spectral shape used to create the spectral distribution of the *CIE Standard Illuminant A*. Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` *CIE Standard Illuminant A*. spectral distribution. References @@ -116,32 +127,39 @@ def sd_CIE_standard_illuminant_A(shape=SPECTRAL_SHAPE_DEFAULT): """ wavelengths = shape.range() - values = (100 * (560 / wavelengths) ** 5 * (((np.exp( - (1.435 * 10 ** 7) / (2848 * 560)) - 1) / (np.exp( - (1.435 * 10 ** 7) / (2848 * wavelengths)) - 1)))) + values = ( + 100 + * (560 / wavelengths) ** 5 + * ( + np.expm1((1.435 * 10**7) / (2848 * 560)) + / np.expm1((1.435 * 10**7) / (2848 * wavelengths)) + ) + ) return SpectralDistribution( - values, wavelengths, name='CIE Standard Illuminant A') + values, wavelengths, name="CIE Standard Illuminant A" + ) -def sd_CIE_illuminant_D_series(xy, M1_M2_rounding=True): +def sd_CIE_illuminant_D_series( + xy: ArrayLike, M1_M2_rounding: Boolean = True +) -> SpectralDistribution: """ - Returns the spectral distribution of given *CIE Illuminant D Series* using + Return the spectral distribution of given *CIE Illuminant D Series* using given *CIE xy* chromaticity coordinates. Parameters ---------- - xy : array_like + xy *CIE xy* chromaticity coordinates. - M1_M2_rounding : bool, optional + M1_M2_rounding Whether to round :math:`M1` and :math:`M2` variables to 3 decimal places in order to yield the internationally agreed values. Returns ------- - SpectralDistribution - *CIE Illuminant D Series* spectral - distribution. + :class:`colour.SpectralDistribution` + *CIE Illuminant D Series* spectral distribution. Notes ----- @@ -277,6 +295,8 @@ def sd_CIE_illuminant_D_series(xy, M1_M2_rounding=True): extrapolator_kwargs={...}) """ + xy = as_float_array(xy) + x, y = tsplit(xy) M = 0.0241 + 0.2562 * x - 0.7341 * y @@ -287,31 +307,32 @@ def sd_CIE_illuminant_D_series(xy, M1_M2_rounding=True): M1 = np.around(M1, 3) M2 = np.around(M2, 3) - S0 = SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES['S0'] - S1 = SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES['S1'] - S2 = SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES['S2'] + S0 = SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES["S0"] + S1 = SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES["S1"] + S2 = SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES["S2"] distribution = S0.values + M1 * S1.values + M2 * S2.values return SpectralDistribution( distribution, S0.wavelengths, - name='CIE xy ({0}, {1}) - CIE Illuminant D Series'.format(*xy), - interpolator=LinearInterpolator) + name=f"CIE xy ({xy[0]}, {xy[1]}) - CIE Illuminant D Series", + interpolator=LinearInterpolator, + ) -def daylight_locus_function(x_D): +def daylight_locus_function(x_D: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the daylight locus as *CIE xy* chromaticity coordinates. + Return the daylight locus as *CIE xy* chromaticity coordinates. Parameters ---------- - x_D : numeric or array_like - *x* chromaticity coordinates + x_D + Chromaticity coordinate :math:`x_D`. Returns ------- - numeric or array_like + :class:`numpy.floating` or :class:`numpy.ndarray` Daylight locus as *CIE xy* chromaticity coordinates. References @@ -326,6 +347,6 @@ def daylight_locus_function(x_D): x_D = as_float_array(x_D) - y_D = -3.000 * x_D ** 2 + 2.870 * x_D - 0.275 + y_D = -3.000 * x_D**2 + 2.870 * x_D - 0.275 - return as_numeric(y_D) + return as_float(y_D) diff --git a/colour/colorimetry/lefs.py b/colour/colorimetry/lefs.py index 3466ea9454..541db46785 100644 --- a/colour/colorimetry/lefs.py +++ b/colour/colorimetry/lefs.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Luminous Efficiency Functions Spectral Distributions ==================================================== -Defines luminous efficiency functions computation related objects. +Defines the luminous efficiency functions computation related objects. References ---------- @@ -12,58 +11,73 @@ http://en.wikipedia.org/wiki/Mesopic_vision#Mesopic_weighting_function """ -from __future__ import division, unicode_literals +from __future__ import annotations -from colour.colorimetry import (SDS_LEFS_PHOTOPIC, SDS_LEFS_SCOTOPIC, - SpectralDistribution, SpectralShape) +from colour.colorimetry import ( + SDS_LEFS_PHOTOPIC, + SDS_LEFS_SCOTOPIC, + SpectralDistribution, + SpectralShape, +) from colour.colorimetry.datasets.lefs import DATA_MESOPIC_X -from colour.utilities import closest +from colour.hints import ( + Floating, + FloatingOrArrayLike, + FloatingOrNDArray, + Literal, + Optional, + Union, + cast, +) +from colour.utilities import closest, optional, validate_method -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'mesopic_weighting_function', 'sd_mesopic_luminous_efficiency_function' + "mesopic_weighting_function", + "sd_mesopic_luminous_efficiency_function", ] def mesopic_weighting_function( - wavelength, - Lp, - source='Blue Heavy', - method='MOVE', - photopic_lef=SDS_LEFS_PHOTOPIC['CIE 1924 Photopic Standard Observer'], - scotopic_lef=SDS_LEFS_SCOTOPIC['CIE 1951 Scotopic Standard Observer']): + wavelength: FloatingOrArrayLike, + L_p: Floating, + source: Union[Literal["Blue Heavy", "Red Heavy"], str] = "Blue Heavy", + method: Union[Literal["MOVE", "LRC"], str] = "MOVE", + photopic_lef: Optional[SpectralDistribution] = None, + scotopic_lef: Optional[SpectralDistribution] = None, +) -> FloatingOrNDArray: """ - Calculates the mesopic weighting function factor at given wavelength - :math:`\\lambda` using the photopic luminance :math:`L_p`. + Calculate the mesopic weighting function factor :math:`V_m` at given + wavelength :math:`\\lambda` using the photopic luminance :math:`L_p`. Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` to calculate the mesopic weighting function factor. - Lp : numeric + L_p Photopic luminance :math:`L_p`. - source : unicode, optional - **{'Blue Heavy', 'Red Heavy'}**, + source Light source colour temperature. - method : unicode, optional - **{'MOVE', 'LRC'}**, + method Method to calculate the weighting factor. - photopic_lef : SpectralDistribution, optional - :math:`V(\\lambda)` photopic luminous efficiency function. - scotopic_lef : SpectralDistribution, optional - :math:`V^\\prime(\\lambda)` scotopic luminous efficiency function. + photopic_lef + :math:`V(\\lambda)` photopic luminous efficiency function, default to + the *CIE 1924 Photopic Standard Observer*. + scotopic_lef + :math:`V^\\prime(\\lambda)` scotopic luminous efficiency function, + default to the *CIE 1951 Scotopic Standard Observer*. Returns ------- - numeric or ndarray - Mesopic weighting function factor. + :class:`numpy.floating` or :class:`numpy.ndarray` + Mesopic weighting function factor :math:`V_m`. References ---------- @@ -75,44 +89,70 @@ def mesopic_weighting_function( 0.7052200... """ + photopic_lef = cast( + SpectralDistribution, + optional( + photopic_lef, + SDS_LEFS_PHOTOPIC["CIE 1924 Photopic Standard Observer"], + ), + ) + + scotopic_lef = cast( + SpectralDistribution, + optional( + scotopic_lef, + SDS_LEFS_SCOTOPIC["CIE 1951 Scotopic Standard Observer"], + ), + ) + + source = validate_method( + source, + ["Blue Heavy", "Red Heavy"], + '"{0}" light source colour temperature is invalid, ' + "it must be one of {1}!", + ) + method = validate_method(method, ["MOVE", "LRC"]) + mesopic_x_luminance_values = sorted(DATA_MESOPIC_X.keys()) index = mesopic_x_luminance_values.index( - closest(mesopic_x_luminance_values, Lp)) + closest(mesopic_x_luminance_values, L_p) + ) x = DATA_MESOPIC_X[mesopic_x_luminance_values[index]][source][method] - Vm = (1 - x) * scotopic_lef[wavelength] + x * photopic_lef[wavelength] + V_m = (1 - x) * scotopic_lef[wavelength] + x * photopic_lef[wavelength] - return Vm + return V_m def sd_mesopic_luminous_efficiency_function( - Lp, - source='Blue Heavy', - method='MOVE', - photopic_lef=SDS_LEFS_PHOTOPIC['CIE 1924 Photopic Standard Observer'], - scotopic_lef=SDS_LEFS_SCOTOPIC['CIE 1951 Scotopic Standard Observer']): + L_p: Floating, + source: Union[Literal["Blue Heavy", "Red Heavy"], str] = "Blue Heavy", + method: Union[Literal["MOVE", "LRC"], str] = "MOVE", + photopic_lef: Optional[SpectralDistribution] = None, + scotopic_lef: Optional[SpectralDistribution] = None, +) -> SpectralDistribution: """ - Returns the mesopic luminous efficiency function :math:`V_m(\\lambda)` for + Return the mesopic luminous efficiency function :math:`V_m(\\lambda)` for given photopic luminance :math:`L_p`. Parameters ---------- - Lp : numeric + L_p Photopic luminance :math:`L_p`. - source : unicode, optional - **{'Blue Heavy', 'Red Heavy'}**, + source Light source colour temperature. - method : unicode, optional - **{'MOVE', 'LRC'}**, + method Method to calculate the weighting factor. - photopic_lef : SpectralDistribution, optional - :math:`V(\\lambda)` photopic luminous efficiency function. - scotopic_lef : SpectralDistribution, optional - :math:`V^\\prime(\\lambda)` scotopic luminous efficiency function. + photopic_lef + :math:`V(\\lambda)` photopic luminous efficiency function, default to + the *CIE 1924 Photopic Standard Observer*. + scotopic_lef + :math:`V^\\prime(\\lambda)` scotopic luminous efficiency function, + default to the *CIE 1951 Scotopic Standard Observer*. Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Mesopic luminous efficiency function :math:`V_m(\\lambda)`. References @@ -531,22 +571,36 @@ def sd_mesopic_luminous_efficiency_function( extrapolator_kwargs={...}) """ - photopic_lef_shape = photopic_lef.shape - scotopic_lef_shape = scotopic_lef.shape + photopic_lef = cast( + SpectralDistribution, + optional( + photopic_lef, + SDS_LEFS_PHOTOPIC["CIE 1924 Photopic Standard Observer"], + ), + ) + + scotopic_lef = cast( + SpectralDistribution, + optional( + scotopic_lef, + SDS_LEFS_SCOTOPIC["CIE 1951 Scotopic Standard Observer"], + ), + ) + shape = SpectralShape( - max(photopic_lef_shape.start, scotopic_lef_shape.start), - min(photopic_lef_shape.end, scotopic_lef_shape.end), - max(photopic_lef_shape.interval, scotopic_lef_shape.interval)) + max([photopic_lef.shape.start, scotopic_lef.shape.start]), + min([photopic_lef.shape.end, scotopic_lef.shape.end]), + max([photopic_lef.shape.interval, scotopic_lef.shape.interval]), + ) wavelengths = shape.range() - sd_data = dict( - zip( - wavelengths, - mesopic_weighting_function(wavelengths, Lp, source, method, - photopic_lef, scotopic_lef))) - sd = SpectralDistribution( - sd_data, name='{0} Lp Mesopic Luminous Efficiency Function'.format(Lp)) + mesopic_weighting_function( + wavelengths, L_p, source, method, photopic_lef, scotopic_lef + ), + wavelengths, + name=f"{L_p} Lp Mesopic Luminous Efficiency Function", + ) return sd.normalise() diff --git a/colour/colorimetry/lightness.py b/colour/colorimetry/lightness.py index bac624be4f..85c5b63d4e 100644 --- a/colour/colorimetry/lightness.py +++ b/colour/colorimetry/lightness.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Lightness :math:`L` =================== -Defines *Lightness* :math:`L` computation objects. +Defines the *Lightness* :math:`L` computation objects. The following methods are available: @@ -21,6 +20,9 @@ - :func:`colour.colorimetry.lightness_Fairchild2011`: *Lightness* :math:`L_{hdr}` computation of given *luminance* :math:`Y` using *Fairchild and Chen (2011)* method. +- :func:`colour.colorimetry.lightness_Abebe2017`: *Lightness* :math:`L` + computation of given *luminance* :math:`Y` using + *Abebe, Pouli, Larabi and Reinhard (2017)* method. - :attr:`colour.LIGHTNESS_METHODS`: Supported *Lightness* :math:`L` computation methods. - :func:`colour.lightness`: *Lightness* :math:`L` computation of given @@ -28,6 +30,9 @@ References ---------- +- :cite:`Abebe2017a` : Abebe, M. A., Pouli, T., Larabi, M.-C., & Reinhard, + E. (2017). Perceptual Lightness Modeling for High-Dynamic-Range Imaging. + ACM Transactions on Applied Perception, 15(1), 1-19. doi:10.1145/3086577 - :cite:`CIETC1-482004m` : CIE TC 1-48. (2004). CIE 1976 uniform colour spaces. In CIE 015:2004 Colorimetry, 3rd Edition (p. 24). ISBN:978-3-901906-33-6 @@ -55,50 +60,72 @@ ISBN:978-0-471-39918-6 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.algebra import spow -from colour.biochemistry import reaction_rate_MichealisMenten -from colour.utilities import (CaseInsensitiveMapping, as_float_array, as_float, - filter_kwargs, from_range_100, - get_domain_range_scale, to_domain_1, - to_domain_100, usage_warning) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.biochemistry import ( + reaction_rate_MichaelisMenten_Michaelis1913, + reaction_rate_MichaelisMenten_Abebe2017, +) +from colour.hints import ( + Any, + FloatingOrArrayLike, + FloatingOrNDArray, + Literal, + Union, +) +from colour.utilities import ( + CaseInsensitiveMapping, + as_float, + as_float_array, + filter_kwargs, + from_range_100, + get_domain_range_scale, + to_domain_1, + to_domain_100, + usage_warning, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'lightness_Glasser1958', 'lightness_Wyszecki1963', - 'intermediate_lightness_function_CIE1976', 'lightness_CIE1976', - 'lightness_Fairchild2010', 'lightness_Fairchild2011', 'LIGHTNESS_METHODS', - 'lightness' + "lightness_Glasser1958", + "lightness_Wyszecki1963", + "intermediate_lightness_function_CIE1976", + "lightness_CIE1976", + "lightness_Fairchild2010", + "lightness_Fairchild2011", + "lightness_Abebe2017", + "LIGHTNESS_METHODS", + "lightness", ] -def lightness_Glasser1958(Y): +def lightness_Glasser1958(Y: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the *Lightness* :math:`L` of given *luminance* :math:`Y` using + Return the *Lightness* :math:`L` of given *luminance* :math:`Y` using *Glasser et al. (1958)* method. Parameters ---------- - Y : numeric or array_like - *luminance* :math:`Y`. + Y + *Luminance* :math:`Y`. Returns ------- - numeric or array_like + :class:`numpy.floating` or :class:`numpy.ndarray` *Lightness* :math:`L`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -125,28 +152,27 @@ def lightness_Glasser1958(Y): L = 25.29 * spow(Y, 1 / 3) - 18.38 - return from_range_100(L) + return as_float(from_range_100(L)) -def lightness_Wyszecki1963(Y): +def lightness_Wyszecki1963(Y: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the *Lightness* :math:`W` of given *luminance* :math:`Y` using + Return the *Lightness* :math:`W` of given *luminance* :math:`Y` using *Wyszecki (1963)* method. Parameters ---------- - Y : numeric or array_like - *luminance* :math:`Y`. + Y + *Luminance* :math:`Y`. Returns ------- - numeric or array_like + :class:`numpy.floating` or :class:`numpy.ndarray` *Lightness* :math:`W`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -172,35 +198,38 @@ def lightness_Wyszecki1963(Y): Y = to_domain_100(Y) if np.any(Y < 1) or np.any(Y > 98): - usage_warning('"W*" Lightness computation is only applicable for ' - '1% < "Y" < 98%, unpredictable results may occur!') + usage_warning( + '"W*" Lightness computation is only applicable for ' + '1% < "Y" < 98%, unpredictable results may occur!' + ) W = 25 * spow(Y, 1 / 3) - 17 - return from_range_100(W) + return as_float(from_range_100(W)) -def intermediate_lightness_function_CIE1976(Y, Y_n=100): +def intermediate_lightness_function_CIE1976( + Y: FloatingOrArrayLike, Y_n: FloatingOrArrayLike = 100 +) -> FloatingOrNDArray: """ - Returns the intermediate value :math:`f(Y/Yn)` in the *Lightness* + Return the intermediate value :math:`f(Y/Yn)` in the *Lightness* :math:`L^*` computation for given *luminance* :math:`Y` using given reference white *luminance* :math:`Y_n` as per *CIE 1976* recommendation. Parameters ---------- - Y : numeric or array_like - *luminance* :math:`Y`. - Y_n : numeric or array_like, optional + Y + *Luminance* :math:`Y`. + Y_n White reference *luminance* :math:`Y_n`. Returns ------- - numeric or array_like + :class:`numpy.floating` or :class:`numpy.ndarray` Intermediate value :math:`f(Y/Yn)`. Notes ----- - +-------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +=============+=======================+===============+ @@ -232,37 +261,37 @@ def intermediate_lightness_function_CIE1976(Y, Y_n=100): Y_Y_n = Y / Y_n - f_Y_Y_n = as_float( - np.where( - Y_Y_n > (24 / 116) ** 3, - spow(Y_Y_n, 1 / 3), - (841 / 108) * Y_Y_n + 16 / 116, - )) + f_Y_Y_n = np.where( + Y_Y_n > (24 / 116) ** 3, + spow(Y_Y_n, 1 / 3), + (841 / 108) * Y_Y_n + 16 / 116, + ) - return f_Y_Y_n + return as_float(f_Y_Y_n) -def lightness_CIE1976(Y, Y_n=100): +def lightness_CIE1976( + Y: FloatingOrArrayLike, Y_n: FloatingOrArrayLike = 100 +) -> FloatingOrNDArray: """ - Returns the *Lightness* :math:`L^*` of given *luminance* :math:`Y` using + Return the *Lightness* :math:`L^*` of given *luminance* :math:`Y` using given reference white *luminance* :math:`Y_n` as per *CIE 1976* recommendation. Parameters ---------- - Y : numeric or array_like - *luminance* :math:`Y`. - Y_n : numeric or array_like, optional + Y + *Luminance* :math:`Y`. + Y_n White reference *luminance* :math:`Y_n`. Returns ------- - numeric or array_like + :class:`numpy.floating` or :class:`numpy.ndarray` *Lightness* :math:`L^*`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -290,34 +319,31 @@ def lightness_CIE1976(Y, Y_n=100): L_star = 116 * intermediate_lightness_function_CIE1976(Y, Y_n) - 16 - return from_range_100(L_star) + return as_float(from_range_100(L_star)) -def lightness_Fairchild2010(Y, epsilon=1.836): +def lightness_Fairchild2010( + Y: FloatingOrArrayLike, epsilon: FloatingOrArrayLike = 1.836 +) -> FloatingOrNDArray: """ - Computes *Lightness* :math:`L_{hdr}` of given *luminance* :math:`Y` using - *Fairchild and Wyble (2010)* method according to *Michealis-Menten* + Compute *Lightness* :math:`L_{hdr}` of given *luminance* :math:`Y` using + *Fairchild and Wyble (2010)* method according to *Michaelis-Menten* kinetics. Parameters ---------- - Y : array_like - *luminance* :math:`Y`. - epsilon : numeric or array_like, optional + Y + *Luminance* :math:`Y`. + epsilon :math:`\\epsilon` exponent. Returns ------- - array_like + :class:`numpy.floating` or :class:`numpy.ndarray` *Lightness* :math:`L_{hdr}`. - Warnings - -------- - The input domain of that definition is non standard! - Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -344,36 +370,42 @@ def lightness_Fairchild2010(Y, epsilon=1.836): maximum_perception = 100 - L_hdr = reaction_rate_MichealisMenten( - spow(Y, epsilon), maximum_perception, 0.184 ** epsilon) + 0.02 + L_hdr = ( + reaction_rate_MichaelisMenten_Michaelis1913( + spow(Y, epsilon), maximum_perception, spow(0.184, epsilon) + ) + + 0.02 + ) - return from_range_100(L_hdr) + return as_float(from_range_100(L_hdr)) -def lightness_Fairchild2011(Y, epsilon=0.474, method='hdr-CIELAB'): +def lightness_Fairchild2011( + Y: FloatingOrArrayLike, + epsilon: FloatingOrArrayLike = 0.474, + method: Union[Literal["hdr-CIELAB", "hdr-IPT"], str] = "hdr-CIELAB", +) -> FloatingOrNDArray: """ - Computes *Lightness* :math:`L_{hdr}` of given *luminance* :math:`Y` using - *Fairchild and Chen (2011)* method according to *Michealis-Menten* + Compute *Lightness* :math:`L_{hdr}` of given *luminance* :math:`Y` using + *Fairchild and Chen (2011)* method according to *Michaelis-Menten* kinetics. Parameters ---------- - Y : array_like - *luminance* :math:`Y`. - epsilon : numeric or array_like, optional + Y + *Luminance* :math:`Y`. + epsilon :math:`\\epsilon` exponent. - method : unicode, optional - **{'hdr-CIELAB', 'hdr-IPT'}**, + method *Lightness* :math:`L_{hdr}` computation method. Returns ------- - array_like + :class:`numpy.floating` or :class:`numpy.ndarray` *Lightness* :math:`L_{hdr}`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -400,25 +432,118 @@ def lightness_Fairchild2011(Y, epsilon=0.474, method='hdr-CIELAB'): """ Y = to_domain_1(Y) + method = validate_method(method, ["hdr-CIELAB", "hdr-IPT"]) - if method.lower() == 'hdr-cielab': + if method == "hdr-cielab": maximum_perception = 247 else: maximum_perception = 246 - L_hdr = reaction_rate_MichealisMenten( - spow(Y, epsilon), maximum_perception, 2 ** epsilon) + 0.02 + L_hdr = ( + reaction_rate_MichaelisMenten_Michaelis1913( + spow(Y, epsilon), maximum_perception, spow(2, epsilon) + ) + + 0.02 + ) + + return as_float(from_range_100(L_hdr)) - return from_range_100(L_hdr) +def lightness_Abebe2017( + Y: FloatingOrArrayLike, + Y_n: FloatingOrArrayLike = 100, + method: Union[ + Literal["Michaelis-Menten", "Stevens"], str + ] = "Michaelis-Menten", +) -> FloatingOrNDArray: + """ + Compute *Lightness* :math:`L` of given *luminance* :math:`Y` using + *Abebe, Pouli, Larabi and Reinhard (2017)* method according to + *Michaelis-Menten* kinetics or *Stevens's Power Law*. -LIGHTNESS_METHODS = CaseInsensitiveMapping({ - 'Glasser 1958': lightness_Glasser1958, - 'Wyszecki 1963': lightness_Wyszecki1963, - 'CIE 1976': lightness_CIE1976, - 'Fairchild 2010': lightness_Fairchild2010, - 'Fairchild 2011': lightness_Fairchild2011 -}) + Parameters + ---------- + Y + *Luminance* :math:`Y` in :math:`cd/m^2`. + Y_n + Adapting luminance :math:`Y_n` in :math:`cd/m^2`. + method + *Lightness* :math:`L` computation method. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + *Lightness* :math:`L`. + + Notes + ----- + - *Abebe, Pouli, Larabi and Reinhard (2017)* method uses absolute + luminance levels, thus the domain and range values for the *Reference* + and *1* scales are only indicative that the data is not affected by + scale transformations. + + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``Y`` | ``UN`` | ``UN`` | + +------------+-----------------------+---------------+ + | ``Y_n`` | ``UN`` | ``UN`` | + +------------+-----------------------+---------------+ + + +------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``L`` | ``UN`` | ``UN`` | + +------------+-----------------------+---------------+ + + References + ---------- + :cite:`Abebe2017a` + + Examples + -------- + >>> lightness_Abebe2017(12.19722535) # doctest: +ELLIPSIS + 0.4869555... + >>> lightness_Abebe2017(12.19722535, method='Stevens') + ... # doctest: +ELLIPSIS + 0.4745447... + """ + + Y = as_float_array(Y) + Y_n = as_float_array(Y_n) + method = validate_method(method, ["Michaelis-Menten", "Stevens"]) + + Y_Y_n = Y / Y_n + if method == "stevens": + L = np.where( + Y_n <= 100, + 1.226 * spow(Y_Y_n, 0.266) - 0.226, + 1.127 * spow(Y_Y_n, 0.230) - 0.127, + ) + else: + L = np.where( + Y_n <= 100, + reaction_rate_MichaelisMenten_Abebe2017( + spow(Y_Y_n, 0.582), 1.448, 0.635, 0.813 + ), + reaction_rate_MichaelisMenten_Abebe2017( + spow(Y_Y_n, 0.293), 1.680, 1.584, 0.096 + ), + ) + + return as_float(L) + + +LIGHTNESS_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Glasser 1958": lightness_Glasser1958, + "Wyszecki 1963": lightness_Wyszecki1963, + "CIE 1976": lightness_CIE1976, + "Fairchild 2010": lightness_Fairchild2010, + "Fairchild 2011": lightness_Fairchild2011, + "Abebe 2017": lightness_Abebe2017, + } +) LIGHTNESS_METHODS.__doc__ = """ Supported *Lightness* computation methods. @@ -427,49 +552,57 @@ def lightness_Fairchild2011(Y, epsilon=0.474, method='hdr-CIELAB'): :cite:`CIETC1-482004m`, :cite:`Fairchild2010`, :cite:`Fairchild2011`, :cite:`Glasser1958a`, :cite:`Wyszecki1963b`, :cite:`Wyszecki2000bd` -LIGHTNESS_METHODS : CaseInsensitiveMapping - **{'Glasser 1958', 'Wyszecki 1963', 'CIE 1976', 'Fairchild 2010', - 'Fairchild 2011'}** - Aliases: - 'Lstar1976': 'CIE 1976' """ -LIGHTNESS_METHODS['Lstar1976'] = LIGHTNESS_METHODS['CIE 1976'] - - -def lightness(Y, method='CIE 1976', **kwargs): +LIGHTNESS_METHODS["Lstar1976"] = LIGHTNESS_METHODS["CIE 1976"] + + +def lightness( + Y: FloatingOrArrayLike, + method: Union[ + Literal[ + "Abebe 2017", + "CIE 1976", + "Glasser 1958", + "Fairchild 2010", + "Fairchild 2011", + "Wyszecki 1963", + ], + str, + ] = "CIE 1976", + **kwargs: Any, +) -> FloatingOrNDArray: """ - Returns the *Lightness* :math:`L` of given *luminance* :math:`Y` using + Return the *Lightness* :math:`L` of given *luminance* :math:`Y` using given method. Parameters ---------- - Y : numeric or array_like - *luminance* :math:`Y`. - method : unicode, optional - **{'CIE 1976', 'Glasser 1958', 'Wyszecki 1963', 'Fairchild 2010', - 'Fairchild 2011'}**, + Y + *Luminance* :math:`Y`. + method Computation method. Other Parameters ---------------- - Y_n : numeric or array_like, optional - {:func:`colour.colorimetry.lightness_CIE1976`}, + Y_n + {:func:`colour.colorimetry.lightness_Abebe2017`, + :func:`colour.colorimetry.lightness_CIE1976`}, White reference *luminance* :math:`Y_n`. - epsilon : numeric or array_like, optional + epsilon {:func:`colour.colorimetry.lightness_Fairchild2010`, :func:`colour.colorimetry.lightness_Fairchild2011`}, :math:`\\epsilon` exponent. Returns ------- - numeric or array_like + :class:`numpy.floating` or :class:`numpy.ndarray` *Lightness* :math:`L`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -484,9 +617,9 @@ def lightness(Y, method='CIE 1976', **kwargs): References ---------- - :cite:`CIETC1-482004m`, :cite:`Fairchild2010`, :cite:`Fairchild2011`, - :cite:`Glasser1958a`, :cite:`Wikipedia2007c`, :cite:`Wyszecki1963b`, - :cite:`Wyszecki2000bd` + :cite:`Abebe2017a`, :cite:`CIETC1-482004m`, :cite:`Fairchild2010`, + :cite:`Fairchild2011`, :cite:`Glasser1958a`, :cite:`Wikipedia2007c`, + :cite:`Wyszecki1963b`, :cite:`Wyszecki2000bd` Examples -------- @@ -503,16 +636,40 @@ def lightness(Y, method='CIE 1976', **kwargs): >>> lightness(12.19722535, epsilon=0.710, method='Fairchild 2011') ... # doctest: +ELLIPSIS 29.8295108... + >>> lightness(12.19722535, epsilon=0.710, method='Fairchild 2011') + ... # doctest: +ELLIPSIS + 29.8295108... + >>> lightness(12.19722535, method='Abebe 2017') + ... # doctest: +ELLIPSIS + 48.6955571... """ Y = as_float_array(Y) + method = validate_method(method, LIGHTNESS_METHODS) function = LIGHTNESS_METHODS[method] - domain_range_reference = get_domain_range_scale() == 'reference' + # NOTE: "Abebe et al. (2017)" uses absolute luminance levels and has + # undefined domain-range scale, yet we modify its behaviour consistency + # with the other methods. + domain_range_reference = get_domain_range_scale() == "reference" + domain_range_1 = get_domain_range_scale() == "1" + domain_range_100 = get_domain_range_scale() == "100" + domain_1 = (lightness_Fairchild2010, lightness_Fairchild2011) + domain_undefined = (lightness_Abebe2017,) if function in domain_1 and domain_range_reference: Y = Y / 100 - return function(Y, **filter_kwargs(function, **kwargs)) + if function in domain_undefined and domain_range_1: + Y = Y * 100 + + L = function(Y, **filter_kwargs(function, **kwargs)) + + if function in domain_undefined and ( + domain_range_reference or domain_range_100 + ): + L *= 100 + + return L diff --git a/colour/colorimetry/luminance.py b/colour/colorimetry/luminance.py index c971b7132c..7296753881 100644 --- a/colour/colorimetry/luminance.py +++ b/colour/colorimetry/luminance.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Luminance :math:`Y` =================== -Defines *luminance* :math:`Y` computation objects. +Defines the *luminance* :math:`Y` computation objects. The following methods are available: @@ -22,6 +21,9 @@ - :func:`colour.colorimetry.luminance_Fairchild2011`: *luminance* :math:`Y` computation of given *Lightness* :math:`L_{hdr}` using *Fairchild and Chen (2011)* method. +- :func:`colour.colorimetry.luminance_Abebe2017`: *Luminance* :math:`Y` + computation of given *Lightness* :math:`L` using + *Abebe, Pouli, Larabi and Reinhard (2017)* method. - :attr:`colour.LUMINANCE_METHODS`: Supported *luminance* :math:`Y` computation methods. - :func:`colour.luminance`: *Luminance* :math:`Y` computation of given @@ -30,6 +32,9 @@ References ---------- +- :cite:`Abebe2017a` : Abebe, M. A., Pouli, T., Larabi, M.-C., & Reinhard, + E. (2017). Perceptual Lightness Modeling for High-Dynamic-Range Imaging. + ACM Transactions on Applied Perception, 15(1), 1-19. doi:10.1145/3086577 - :cite:`ASTMInternational2008a` : ASTM International. (2008). ASTM D1535-08e1 - Standard Practice for Specifying Color by the Munsell System. doi:10.1520/D1535-08E01 @@ -56,49 +61,72 @@ ISBN:978-0-471-39918-6 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.biochemistry import substrate_concentration_MichealisMenten -from colour.utilities import (CaseInsensitiveMapping, as_float_array, as_float, - filter_kwargs, from_range_1, from_range_100, - get_domain_range_scale, to_domain_10, - to_domain_100) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.algebra import spow +from colour.biochemistry import ( + substrate_concentration_MichaelisMenten_Michaelis1913, + substrate_concentration_MichaelisMenten_Abebe2017, +) +from colour.hints import ( + Any, + FloatingOrArrayLike, + FloatingOrNDArray, + Literal, + Union, +) +from colour.utilities import ( + CaseInsensitiveMapping, + as_float, + as_float_array, + filter_kwargs, + from_range_1, + from_range_100, + get_domain_range_scale, + to_domain_10, + to_domain_100, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'luminance_Newhall1943', 'luminance_ASTMD1535', - 'intermediate_luminance_function_CIE1976', 'luminance_CIE1976', - 'luminance_Fairchild2010', 'luminance_Fairchild2011', 'LUMINANCE_METHODS', - 'luminance' + "luminance_Newhall1943", + "luminance_ASTMD1535", + "intermediate_luminance_function_CIE1976", + "luminance_CIE1976", + "luminance_Fairchild2010", + "luminance_Fairchild2011", + "luminance_Abebe2017", + "LUMINANCE_METHODS", + "luminance", ] -def luminance_Newhall1943(V): +def luminance_Newhall1943(V: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the *luminance* :math:`R_Y` of given *Munsell* value :math:`V` + Return the *luminance* :math:`R_Y` of given *Munsell* value :math:`V` using *Newhall et al. (1943)* method. Parameters ---------- - V : numeric or array_like + V *Munsell* value :math:`V`. Returns ------- - numeric or array_like - *luminance* :math:`R_Y`. + :class:`numpy.floating` or :class:`numpy.ndarray` + *Luminance* :math:`R_Y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -123,30 +151,34 @@ def luminance_Newhall1943(V): V = to_domain_10(V) - R_Y = (1.2219 * V - 0.23111 * (V * V) + 0.23951 * (V ** 3) - - 0.021009 * (V ** 4) + 0.0008404 * (V ** 5)) + R_Y = ( + 1.2219 * V + - 0.23111 * (V * V) + + 0.23951 * (V**3) + - 0.021009 * (V**4) + + 0.0008404 * (V**5) + ) - return from_range_100(R_Y) + return as_float(from_range_100(R_Y)) -def luminance_ASTMD1535(V): +def luminance_ASTMD1535(V: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the *luminance* :math:`Y` of given *Munsell* value :math:`V` using + Return the *luminance* :math:`Y` of given *Munsell* value :math:`V` using *ASTM D1535-08e1* method. Parameters ---------- - V : numeric or array_like + V *Munsell* value :math:`V`. Returns ------- - numeric or array_like - *luminance* :math:`Y`. + :class:`numpy.floating` or :class:`numpy.ndarray` + *Luminance* :math:`Y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -171,33 +203,39 @@ def luminance_ASTMD1535(V): V = to_domain_10(V) - Y = (1.1914 * V - 0.22533 * (V ** 2) + 0.23352 * (V ** 3) - - 0.020484 * (V ** 4) + 0.00081939 * (V ** 5)) + Y = ( + 1.1914 * V + - 0.22533 * (V**2) + + 0.23352 * (V**3) + - 0.020484 * (V**4) + + 0.00081939 * (V**5) + ) - return from_range_100(Y) + return as_float(from_range_100(Y)) -def intermediate_luminance_function_CIE1976(f_Y_Y_n, Y_n=100): +def intermediate_luminance_function_CIE1976( + f_Y_Y_n: FloatingOrArrayLike, Y_n: FloatingOrArrayLike = 100 +) -> FloatingOrNDArray: """ - Returns the *luminance* :math:`Y` in the *luminance* :math:`Y` + Return the *luminance* :math:`Y` in the *luminance* :math:`Y` computation for given intermediate value :math:`f(Y/Yn)` using given reference white *luminance* :math:`Y_n` as per *CIE 1976* recommendation. Parameters ---------- - f_Y_Y_n : numeric or array_like + f_Y_Y_n Intermediate value :math:`f(Y/Yn)`. - Y_n : numeric or array_like + Y_n White reference *luminance* :math:`Y_n`. Returns ------- - numeric or array_like - *luminance* :math:`Y`. + :class:`numpy.floating` or :class:`numpy.ndarray` + *Luminance* :math:`Y`. Notes ----- - +-------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +=============+=======================+===============+ @@ -227,36 +265,36 @@ def intermediate_luminance_function_CIE1976(f_Y_Y_n, Y_n=100): f_Y_Y_n = as_float_array(f_Y_Y_n) Y_n = as_float_array(Y_n) - Y = as_float( - np.where( - f_Y_Y_n > 24 / 116, - Y_n * f_Y_Y_n ** 3, - Y_n * (f_Y_Y_n - 16 / 116) * (108 / 841), - )) + Y = np.where( + f_Y_Y_n > 24 / 116, + Y_n * f_Y_Y_n**3, + Y_n * (f_Y_Y_n - 16 / 116) * (108 / 841), + ) - return Y + return as_float(Y) -def luminance_CIE1976(L_star, Y_n=100): +def luminance_CIE1976( + L_star: FloatingOrArrayLike, Y_n: FloatingOrArrayLike = 100 +) -> FloatingOrNDArray: """ - Returns the *luminance* :math:`Y` of given *Lightness* :math:`L^*` with + Return the *luminance* :math:`Y` of given *Lightness* :math:`L^*` with given reference white *luminance* :math:`Y_n`. Parameters ---------- - L_star : numeric or array_like + L_star *Lightness* :math:`L^*` - Y_n : numeric or array_like + Y_n White reference *luminance* :math:`Y_n`. Returns ------- - numeric or array_like - *luminance* :math:`Y`. + :class:`numpy.floating` or :class:`numpy.ndarray` + *Luminance* :math:`Y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -288,34 +326,31 @@ def luminance_CIE1976(L_star, Y_n=100): Y = intermediate_luminance_function_CIE1976(f_Y_Y_n, Y_n) - return from_range_100(Y) + return as_float(from_range_100(Y)) -def luminance_Fairchild2010(L_hdr, epsilon=1.836): +def luminance_Fairchild2010( + L_hdr: FloatingOrArrayLike, epsilon: FloatingOrArrayLike = 1.836 +) -> FloatingOrNDArray: """ - Computes *luminance* :math:`Y` of given *Lightness* :math:`L_{hdr}` using - *Fairchild and Wyble (2010)* method according to *Michealis-Menten* + Compute *luminance* :math:`Y` of given *Lightness* :math:`L_{hdr}` using + *Fairchild and Wyble (2010)* method according to *Michaelis-Menten* kinetics. Parameters ---------- - L_hdr : array_like + L_hdr *Lightness* :math:`L_{hdr}`. - epsilon : numeric or array_like, optional + epsilon :math:`\\epsilon` exponent. Returns ------- - array_like - *luminance* :math:`Y`. - - Warnings - -------- - The output range of that definition is non standard! + :class:`numpy.floating` or :class:`numpy.ndarray` + *Luminance* :math:`Y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -343,36 +378,42 @@ def luminance_Fairchild2010(L_hdr, epsilon=1.836): Y = np.exp( np.log( - substrate_concentration_MichealisMenten(L_hdr - 0.02, 100, 0.184 ** - epsilon)) / epsilon) + substrate_concentration_MichaelisMenten_Michaelis1913( + L_hdr - 0.02, 100, spow(0.184, epsilon) + ) + ) + / epsilon + ) - return from_range_1(Y) + return as_float(from_range_1(Y)) -def luminance_Fairchild2011(L_hdr, epsilon=0.474, method='hdr-CIELAB'): +def luminance_Fairchild2011( + L_hdr: FloatingOrArrayLike, + epsilon: FloatingOrArrayLike = 0.474, + method: Union[Literal["hdr-CIELAB", "hdr-IPT"], str] = "hdr-CIELAB", +) -> FloatingOrNDArray: """ - Computes *luminance* :math:`Y` of given *Lightness* :math:`L_{hdr}` using - *Fairchild and Chen (2011)* method according to *Michealis-Menten* + Compute *luminance* :math:`Y` of given *Lightness* :math:`L_{hdr}` using + *Fairchild and Chen (2011)* method according to *Michaelis-Menten* kinetics. Parameters ---------- - L_hdr : array_like + L_hdr *Lightness* :math:`L_{hdr}`. - epsilon : numeric or array_like, optional + epsilon :math:`\\epsilon` exponent. - method : unicode, optional - **{'hdr-CIELAB', 'hdr-IPT'}**, + method *Lightness* :math:`L_{hdr}` computation method. Returns ------- - array_like - *luminance* :math:`Y`. + :class:`numpy.floating` or :class:`numpy.ndarray` + *Luminance* :math:`Y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -399,27 +440,126 @@ def luminance_Fairchild2011(L_hdr, epsilon=0.474, method='hdr-CIELAB'): """ L_hdr = to_domain_100(L_hdr) + method = validate_method(method, ["hdr-CIELAB", "hdr-IPT"]) - if method.lower() == 'hdr-cielab': + if method == "hdr-cielab": maximum_perception = 247 else: maximum_perception = 246 Y = np.exp( np.log( - substrate_concentration_MichealisMenten( - L_hdr - 0.02, maximum_perception, 2 ** epsilon)) / epsilon) + substrate_concentration_MichaelisMenten_Michaelis1913( + L_hdr - 0.02, maximum_perception, spow(2, epsilon) + ) + ) + / epsilon + ) + + return as_float(from_range_1(Y)) + + +def luminance_Abebe2017( + L: FloatingOrArrayLike, + Y_n: FloatingOrArrayLike = 100, + method: Union[ + Literal["Michaelis-Menten", "Stevens"], str + ] = "Michaelis-Menten", +) -> FloatingOrNDArray: + """ + Compute *luminance* :math:`Y` of *Lightness* :math:`L` using + *Abebe, Pouli, Larabi and Reinhard (2017)* method according to + *Michaelis-Menten* kinetics or *Stevens's Power Law*. + + Parameters + ---------- + L + *Lightness* :math:`L`. + Y_n + Adapting luminance :math:`Y_n` in :math:`cd/m^2`. + method + *Luminance* :math:`Y` computation method. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + *Luminance* :math:`Y` in :math:`cd/m^2`. + + Notes + ----- + - *Abebe, Pouli, Larabi and Reinhard (2017)* method uses absolute + luminance levels, thus the domain and range values for the *Reference* + and *1* scales are only indicative that the data is not affected by + scale transformations. + + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``L`` | ``UN`` | ``UN`` | + +------------+-----------------------+---------------+ + | ``Y_n`` | ``UN`` | ``UN`` | + +------------+-----------------------+---------------+ + + +------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``Y`` | ``UN`` | ``UN`` | + +------------+-----------------------+---------------+ - return from_range_1(Y) + References + ---------- + :cite:`Abebe2017a` + Examples + -------- + >>> luminance_Abebe2017(0.486955571109229) # doctest: +ELLIPSIS + 12.1972253... + >>> luminance_Abebe2017(0.474544792145434, method='Stevens') + ... # doctest: +ELLIPSIS + 12.1972253... + """ -LUMINANCE_METHODS = CaseInsensitiveMapping({ - 'Newhall 1943': luminance_Newhall1943, - 'ASTM D1535': luminance_ASTMD1535, - 'CIE 1976': luminance_CIE1976, - 'Fairchild 2010': luminance_Fairchild2010, - 'Fairchild 2011': luminance_Fairchild2011 -}) + L = as_float_array(L) + Y_n = as_float_array(Y_n) + method = validate_method(method, ["Michaelis-Menten", "Stevens"]) + + if method == "stevens": + Y = np.where( + Y_n <= 100, + spow((L + 0.226) / 1.226, 1 / 0.266), + spow((L + 0.127) / 1.127, 1 / 0.230), + ) + else: + Y = np.where( + Y_n <= 100, + spow( + substrate_concentration_MichaelisMenten_Abebe2017( + L, 1.448, 0.635, 0.813 + ), + 1 / 0.582, + ), + spow( + substrate_concentration_MichaelisMenten_Abebe2017( + L, 1.680, 1.584, 0.096 + ), + 1 / 0.293, + ), + ) + Y = Y * Y_n + + return as_float(Y) + + +LUMINANCE_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Newhall 1943": luminance_Newhall1943, + "ASTM D1535": luminance_ASTMD1535, + "CIE 1976": luminance_CIE1976, + "Fairchild 2010": luminance_Fairchild2010, + "Fairchild 2011": luminance_Fairchild2011, + "Abebe 2017": luminance_Abebe2017, + } +) LUMINANCE_METHODS.__doc__ = """ Supported *luminance* computation methods. @@ -428,50 +568,59 @@ def luminance_Fairchild2011(L_hdr, epsilon=0.474, method='hdr-CIELAB'): :cite:`ASTMInternational2008a`, :cite:`CIETC1-482004m`, :cite:`Fairchild2010`, :cite:`Fairchild2011`, :cite:`Newhall1943a`, :cite:`Wyszecki2000bd` -LUMINANCE_METHODS : CaseInsensitiveMapping - **{'Newhall 1943', 'ASTM D1535', 'CIE 1976', 'Fairchild 2010'}** - Aliases: - 'astm2008': 'ASTM D1535' - 'cie1976': 'CIE 1976' """ -LUMINANCE_METHODS['astm2008'] = (LUMINANCE_METHODS['ASTM D1535']) -LUMINANCE_METHODS['cie1976'] = (LUMINANCE_METHODS['CIE 1976']) - - -def luminance(LV, method='CIE 1976', **kwargs): +LUMINANCE_METHODS["astm2008"] = LUMINANCE_METHODS["ASTM D1535"] +LUMINANCE_METHODS["cie1976"] = LUMINANCE_METHODS["CIE 1976"] + + +def luminance( + LV: FloatingOrArrayLike, + method: Union[ + Literal[ + "Abebe 2017", + "CIE 1976", + "Glasser 1958", + "Fairchild 2010", + "Fairchild 2011", + "Wyszecki 1963", + ], + str, + ] = "CIE 1976", + **kwargs: Any, +) -> FloatingOrNDArray: """ - Returns the *luminance* :math:`Y` of given *Lightness* :math:`L^*` or given + Return the *luminance* :math:`Y` of given *Lightness* :math:`L^*` or given *Munsell* value :math:`V`. Parameters ---------- - LV : numeric or array_like + LV *Lightness* :math:`L^*` or *Munsell* value :math:`V`. - method : unicode, optional - **{'CIE 1976', 'Newhall 1943', 'ASTM D1535', 'Fairchild 2010', - 'Fairchild 2011'}**, + method Computation method. Other Parameters ---------------- - Y_n : numeric or array_like, optional - {:func:`colour.colorimetry.luminance_CIE1976`}, + Y_n + {:func:`colour.colorimetry.luminance_Abebe2017`, + :func:`colour.colorimetry.luminance_CIE1976`}, White reference *luminance* :math:`Y_n`. - epsilon : numeric or array_like, optional + epsilon {:func:`colour.colorimetry.lightness_Fairchild2010`, :func:`colour.colorimetry.lightness_Fairchild2011`}, :math:`\\epsilon` exponent. Returns ------- - numeric or array_like - *luminance* :math:`Y`. + :class:`numpy.floating` or :class:`numpy.ndarray` + *Luminance* :math:`Y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -486,7 +635,7 @@ def luminance(LV, method='CIE 1976', **kwargs): References ---------- - :cite:`ASTMInternational2008a`, :cite:`CIETC1-482004m`, + :cite:`Abebe2017a`, :cite:`ASTMInternational2008a`, :cite:`CIETC1-482004m`, :cite:`Fairchild2010`, :cite:`Fairchild2011`, :cite:`Newhall1943a`, :cite:`Wikipedia2001b`, :cite:`Wyszecki2000bd` @@ -509,18 +658,33 @@ def luminance(LV, method='CIE 1976', **kwargs): 12.1972253... """ + LV = as_float_array(LV) + method = validate_method(method, LUMINANCE_METHODS) + function = LUMINANCE_METHODS[method] - domain_range_reference = get_domain_range_scale() == 'reference' + # NOTE: "Abebe et al. (2017)" uses absolute luminance levels and has + # undefined domain-range scale, yet we modify its behaviour consistency + # with the other methods. + domain_range_reference = get_domain_range_scale() == "reference" + domain_range_1 = get_domain_range_scale() == "1" + domain_1 = (luminance_Fairchild2010, luminance_Fairchild2011) domain_10 = (luminance_Newhall1943, luminance_ASTMD1535) + domain_undefined = (luminance_Abebe2017,) if function in domain_10 and domain_range_reference: - LV /= 10 + LV = LV / 10 + + if function in domain_undefined and domain_range_1: + LV = LV * 100 Y_V = function(LV, **filter_kwargs(function, **kwargs)) if function in domain_1 and domain_range_reference: Y_V *= 100 + if function in domain_undefined and domain_range_1: + Y_V /= 100 + return Y_V diff --git a/colour/colorimetry/photometry.py b/colour/colorimetry/photometry.py index 318b824e05..49072e24b8 100644 --- a/colour/colorimetry/photometry.py +++ b/colour/colorimetry/photometry.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Photometry ========== -Defines photometric quantities computation related objects. +Defines the photometric quantities computation related objects. References ---------- @@ -14,43 +13,55 @@ April 3, 2016, from https://en.wikipedia.org/wiki/Luminous_efficacy """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.colorimetry import SDS_LEFS_PHOTOPIC +from colour.colorimetry import ( + SDS_LEFS_PHOTOPIC, + SpectralDistribution, + reshape_sd, +) from colour.constants import CONSTANT_K_M -from colour.utilities import as_float - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['luminous_flux', 'luminous_efficiency', 'luminous_efficacy'] - - -def luminous_flux(sd, - lef=SDS_LEFS_PHOTOPIC['CIE 1924 Photopic Standard Observer'], - K_m=CONSTANT_K_M): +from colour.hints import Floating, Optional, cast +from colour.utilities import as_float_scalar, optional + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "luminous_flux", + "luminous_efficiency", + "luminous_efficacy", +] + + +def luminous_flux( + sd: SpectralDistribution, + lef: Optional[SpectralDistribution] = None, + K_m: Floating = CONSTANT_K_M, +) -> Floating: """ - Returns the *luminous flux* for given spectral distribution using given + Return the *luminous flux* for given spectral distribution using given luminous efficiency function. Parameters ---------- - sd : SpectralDistribution + sd test spectral distribution - lef : SpectralDistribution, optional - :math:`V(\\lambda)` luminous efficiency function. - K_m : numeric, optional - :math:`lm\\cdot W^{-1}` maximum photopic luminous efficiency + lef + :math:`V(\\lambda)` luminous efficiency function, default to the + *CIE 1924 Photopic Standard Observer*. + K_m + :math:`lm\\cdot W^{-1}` maximum photopic luminous efficiency. Returns ------- - numeric + :class:`numpy.floating` Luminous flux. References @@ -65,36 +76,42 @@ def luminous_flux(sd, 23807.6555273... """ - lef = lef.copy().align( + lef = cast( + SpectralDistribution, + optional( + lef, SDS_LEFS_PHOTOPIC["CIE 1924 Photopic Standard Observer"] + ), + ) + + lef = reshape_sd( + lef, sd.shape, - extrapolator_kwargs={ - 'method': 'Constant', - 'left': 0, - 'right': 0 - }) - sd = sd.copy() * lef + extrapolator_kwargs={"method": "Constant", "left": 0, "right": 0}, + ) - flux = K_m * np.trapz(sd.values, sd.wavelengths) + flux = K_m * np.trapz(lef.values * sd.values, sd.wavelengths) - return as_float(flux) + return as_float_scalar(flux) def luminous_efficiency( - sd, lef=SDS_LEFS_PHOTOPIC['CIE 1924 Photopic Standard Observer']): + sd: SpectralDistribution, lef: Optional[SpectralDistribution] = None +) -> Floating: """ - Returns the *luminous efficiency* of given spectral distribution using + Return the *luminous efficiency* of given spectral distribution using given luminous efficiency function. Parameters ---------- - sd : SpectralDistribution + sd test spectral distribution - lef : SpectralDistribution, optional - :math:`V(\\lambda)` luminous efficiency function. + lef + :math:`V(\\lambda)` luminous efficiency function, default to the + *CIE 1924 Photopic Standard Observer*. Returns ------- - numeric + :class:`numpy.floating` Luminous efficiency. References @@ -109,37 +126,44 @@ def luminous_efficiency( 0.1994393... """ - lef = lef.copy().align( + lef = cast( + SpectralDistribution, + optional( + lef, SDS_LEFS_PHOTOPIC["CIE 1924 Photopic Standard Observer"] + ), + ) + + lef = reshape_sd( + lef, sd.shape, - extrapolator_kwargs={ - 'method': 'Constant', - 'left': 0, - 'right': 0 - }) - sd = sd.copy() + extrapolator_kwargs={"method": "Constant", "left": 0, "right": 0}, + ) - efficiency = (np.trapz(lef.values * sd.values, sd.wavelengths) / np.trapz( - sd.values, sd.wavelengths)) + efficiency = np.trapz(lef.values * sd.values, sd.wavelengths) / np.trapz( + sd.values, sd.wavelengths + ) - return efficiency + return as_float_scalar(efficiency) def luminous_efficacy( - sd, lef=SDS_LEFS_PHOTOPIC['CIE 1924 Photopic Standard Observer']): + sd: SpectralDistribution, lef: Optional[SpectralDistribution] = None +) -> Floating: """ - Returns the *luminous efficacy* in :math:`lm\\cdot W^{-1}` of given + Return the *luminous efficacy* in :math:`lm\\cdot W^{-1}` of given spectral distribution using given luminous efficiency function. Parameters ---------- - sd : SpectralDistribution + sd test spectral distribution - lef : SpectralDistribution, optional - :math:`V(\\lambda)` luminous efficiency function. + lef + :math:`V(\\lambda)` luminous efficiency function, default to the + *CIE 1924 Photopic Standard Observer*. Returns ------- - numeric + :class:`numpy.floating` Luminous efficacy in :math:`lm\\cdot W^{-1}`. References @@ -156,4 +180,4 @@ def luminous_efficacy( efficacy = CONSTANT_K_M * luminous_efficiency(sd, lef) - return as_float(efficacy) + return as_float_scalar(efficacy) diff --git a/colour/colorimetry/spectrum.py b/colour/colorimetry/spectrum.py index ed904f7cf4..ab99a7e29b 100644 --- a/colour/colorimetry/spectrum.py +++ b/colour/colorimetry/spectrum.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Spectrum ======== @@ -11,6 +10,8 @@ - :class:`colour.MultiSpectralDistributions` - :func:`colour.colorimetry.sds_and_msds_to_sds` - :func:`colour.colorimetry.sds_and_msds_to_msds` +- :func:`colour.colorimetry.reshape_sd` +- :func:`colour.colorimetry.reshape_msds` References ---------- @@ -24,45 +25,100 @@ 015:2004 Colorimetry, 3rd Edition (p. 24). ISBN:978-3-901906-33-6 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from six.moves import zip +from collections.abc import Mapping -from colour.algebra import (Extrapolator, CubicSplineInterpolator, - SpragueInterpolator) +from colour.algebra import ( + Extrapolator, + CubicSplineInterpolator, + SpragueInterpolator, +) from colour.constants import DEFAULT_FLOAT_DTYPE from colour.continuous import Signal, MultiSignals -from colour.utilities import (as_float, as_int, first_item, is_iterable, - is_numeric, is_string, is_uniform, interval, - runtime_warning, tstack, usage_warning) -from colour.utilities.deprecation import (ObjectRemoved, ObjectRenamed, - handle_arguments_deprecation) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import ( + ArrayLike, + Any, + Dict, + DTypeFloating, + FloatingOrArrayLike, + Generator, + Integer, + List, + Literal, + NDArray, + Number, + Optional, + Sequence, + Tuple, + Type, + TypeExtrapolator, + TypeInterpolator, + Union, + cast, +) +from colour.utilities import ( + CACHE_REGISTRY, + as_float_array, + as_int, + attest, + filter_kwargs, + first_item, + is_iterable, + is_numeric, + is_pandas_installed, + is_string, + is_uniform, + interval, + optional, + runtime_warning, + tstack, + validate_method, +) + +if is_pandas_installed(): + from pandas import DataFrame, Series +else: # pragma: no cover + from unittest import mock + + DataFrame = mock.MagicMock() + Series = mock.MagicMock() + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'SpectralShape', 'SPECTRAL_SHAPE_DEFAULT', 'SpectralDistribution', - 'MultiSpectralDistributions', 'sds_and_msds_to_sds', 'sds_and_msds_to_msds' + "SpectralShape", + "SPECTRAL_SHAPE_DEFAULT", + "SpectralDistribution", + "MultiSpectralDistributions", + "reshape_sd", + "reshape_msds", + "sds_and_msds_to_sds", + "sds_and_msds_to_msds", ] +_CACHE_SHAPE_RANGE: Dict = CACHE_REGISTRY.register_cache( + f"{__name__}._CACHE_SHAPE_RANGE" +) -class SpectralShape(object): + +class SpectralShape: """ - Defines the base object for spectral distribution shape. + Define the base object for spectral distribution shape. Parameters ---------- - start : numeric, optional + start Wavelength :math:`\\lambda_{i}` range start in nm. - end : numeric, optional + end Wavelength :math:`\\lambda_{i}` range end in nm. - interval : numeric, optional + interval Wavelength :math:`\\lambda_{i}` range interval. Attributes @@ -91,212 +147,188 @@ class SpectralShape(object): SpectralShape(360, 830, 1) """ - def __init__(self, start=None, end=None, interval=None): - self._range = None - - self._start = None - self._end = None - self._interval = None + def __init__(self, start: Number, end: Number, interval: Number): + self._start: Number = 360 + self._end: Number = 780 + self._interval: Number = 1 self.start = start self.end = end self.interval = interval @property - def start(self): + def start(self) -> Number: """ Getter and setter property for the spectral shape start. Parameters ---------- - value : numeric + value Value to set the spectral shape start with. Returns ------- - numeric + Number Spectral shape start. """ return self._start @start.setter - def start(self, value): - """ - Setter for the **self.start** property. - """ - - if value is not None: - assert is_numeric(value), ( - '"{0}" attribute: "{1}" is not a "numeric"!'.format( - 'start', value)) + def start(self, value: Number): + """Setter for the **self.start** property.""" - if self._end is not None: - assert value < self._end, ( - '"{0}" attribute value must be strictly less than ' - '"{1}"!'.format('start', self._end)) + attest( + is_numeric(value), + f'"start" property: "{value}" is not a "number"!', + ) - # Invalidating the *range* cache. - if value != self._start: - self._range = None + attest( + bool(value < self._end), + f'"start" attribute value must be strictly less than ' + f'"{self._end}"!', + ) self._start = value @property - def end(self): + def end(self) -> Number: """ Getter and setter property for the spectral shape end. Parameters ---------- - value : numeric + value Value to set the spectral shape end with. Returns ------- - numeric + Number Spectral shape end. """ return self._end @end.setter - def end(self, value): - """ - Setter for the **self.end** property. - """ - - if value is not None: - assert is_numeric(value), ( - '"{0}" attribute: "{1}" is not a "numeric"!'.format( - 'end', value)) + def end(self, value: Number): + """Setter for the **self.end** property.""" - if self._start is not None: - assert value > self._start, ( - '"{0}" attribute value must be strictly greater than ' - '"{1}"!'.format('end', self._start)) + attest( + is_numeric(value), + f'"end" property: "{value}" is not a "number"!', + ) - # Invalidating the *range* cache. - if value != self._end: - self._range = None + attest( + bool(value > self._start), + f'"end" attribute value must be strictly greater than ' + f'"{self._start}"!', + ) self._end = value @property - def interval(self): + def interval(self) -> Number: """ Getter and setter property for the spectral shape interval. Parameters ---------- - value : numeric + value Value to set the spectral shape interval with. Returns ------- - numeric + Number Spectral shape interval. """ return self._interval @interval.setter - def interval(self, value): - """ - Setter for the **self.interval** property. - """ + def interval(self, value: Number): + """Setter for the **self.interval** property.""" - if value is not None: - assert is_numeric(value), ( - '"{0}" attribute: "{1}" is not a "numeric"!'.format( - 'interval', value)) - - # Invalidating the *range* cache. - if value != self._interval: - self._range = None + attest( + is_numeric(value), + f'"interval" property: "{value}" is not a "number"!', + ) self._interval = value @property - def boundaries(self): + def boundaries(self) -> Tuple: """ Getter and setter property for the spectral shape boundaries. Parameters ---------- - value : array_like + value Value to set the spectral shape boundaries with. Returns ------- - tuple + :class:`tuple` Spectral shape boundaries. """ return self._start, self._end @boundaries.setter - def boundaries(self, value): - """ - Setter for the **self.boundaries** property. - """ + def boundaries(self, value: ArrayLike): + """Setter for the **self.boundaries** property.""" - if value is not None: - assert is_iterable(value), ( - '"{0}" attribute: "{1}" is not an "iterable"!'.format( - 'boundaries', value)) + value = np.asarray(value) - assert len(value) == 2, ( - '"{0}" attribute: "{1}" must have exactly ' - 'two elements!'.format('boundaries', value)) + attest( + value.size == 2, + f'"boundaries" property: "{value}" must have exactly two ' + f"elements!", + ) - start, end = value - self.start = start - self.end = end + self.start, self.end = value - def __str__(self): + def __str__(self) -> str: """ - Returns a formatted string representation of the spectral shape. + Return a formatted string representation of the spectral shape. Returns ------- - unicode + :class:`str` Formatted string representation. """ - return '({0}, {1}, {2})'.format(self._start, self._end, self._interval) + return f"({self._start}, {self._end}, {self._interval})" - def __repr__(self): + def __repr__(self) -> str: """ - Returns an evaluable string representation of the spectral shape. + Return an evaluable string representation of the spectral shape. Returns ------- - unicode + :class:`str` Evaluable string representation. """ - return 'SpectralShape({0}, {1}, {2})'.format(self._start, self._end, - self._interval) + return f"SpectralShape({self._start}, {self._end}, {self._interval})" - def __hash__(self): + def __hash__(self) -> Integer: """ - Returns the spectral shape hash. + Return the spectral shape hash. Returns ------- - int + :class:`numpy.integer` Object hash. """ return hash(repr(self)) - def __iter__(self): + def __iter__(self) -> Generator: """ - Returns a generator for the spectral shape data. + Return a generator for the spectral shape data. - Returns - ------- - generator + Yields + ------ + Generator Spectral shape data generator. Examples @@ -317,22 +349,23 @@ def __iter__(self): 10.0 """ - return iter(self.range()) + yield from self.range() - def __contains__(self, wavelength): + def __contains__(self, wavelength: FloatingOrArrayLike) -> bool: """ - Returns if the spectral shape contains given wavelength + Return if the spectral shape contains given wavelength :math:`\\lambda`. Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda`. Returns ------- - bool - Is wavelength :math:`\\lambda` contained in the spectral shape. + :class:`bool` + Whether wavelength :math:`\\lambda` is contained in the spectral + shape. Examples -------- @@ -348,20 +381,24 @@ def __contains__(self, wavelength): False """ - tolerance = 10 ** -np.finfo(DEFAULT_FLOAT_DTYPE).precision + decimals = np.finfo(DEFAULT_FLOAT_DTYPE).precision - return np.all( - np.in1d( - np.around(wavelength / tolerance).astype(np.int64), - np.around(self.range() / tolerance).astype(np.int64))) + return bool( + np.all( + np.in1d( + np.around(wavelength, decimals), + np.around(self.range(), decimals), + ) + ) + ) - def __len__(self): + def __len__(self) -> Integer: """ - Returns the spectral shape wavelength :math:`\\lambda_n` count. + Return the spectral shape wavelength :math:`\\lambda_n` count. Returns ------- - int + :class:`numpy.integer` Spectral shape wavelength :math:`\\lambda_n` count. Examples @@ -372,19 +409,19 @@ def __len__(self): return len(self.range()) - def __eq__(self, shape): + def __eq__(self, other: Any) -> bool: """ - Returns the spectral shape equality with given other spectral shape. + Return whether the spectral shape is equal to given other object. Parameters ---------- - shape : SpectralShape - Spectral shape to compare for equality. + other + Object to test whether it is equal to the spectral shape. Returns ------- - bool - Spectral shape equality. + :class:`bool` + Whether given object is equal to the spectral shape. Examples -------- @@ -394,22 +431,24 @@ def __eq__(self, shape): False """ - return (isinstance(shape, self.__class__) and - np.array_equal(self.range(), shape.range())) + if isinstance(other, SpectralShape): + return np.array_equal(self.range(), other.range()) + else: + return False - def __ne__(self, shape): + def __ne__(self, other: Any) -> bool: """ - Returns the spectral shape inequality with given other spectral shape. + Return whether the spectral shape is not equal to given other object. Parameters ---------- - shape : SpectralShape - Spectral shape to compare for inequality. + other + Object to test whether it is not equal to the spectral shape. Returns ------- - bool - Spectral shape inequality. + :class:`bool` + Whether given object is not equal to the spectral shape. Examples -------- @@ -419,28 +458,22 @@ def __ne__(self, shape): True """ - return not (self == shape) + return not (self == other) - def range(self, dtype=None): + def range(self, dtype: Optional[Type[DTypeFloating]] = None) -> NDArray: """ - Returns an iterable range for the spectral shape. + Return an iterable range for the spectral shape. Parameters ---------- - dtype : type + dtype Data type used to generate the range. Returns ------- - ndarray + :class:`numpy.ndarray` Iterable range for the spectral distribution shape - Raises - ------ - RuntimeError - If one of spectral shape *start*, *end* or *interval* attributes is - not defined. - Examples -------- >>> SpectralShape(0, 10, 0.1).range() @@ -458,43 +491,42 @@ def range(self, dtype=None): 9.9, 10. ]) """ - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) - if None in (self._start, self._end, self._interval): - raise RuntimeError(('One of the spectral shape "start", "end" or ' - '"interval" attributes is not defined!')) + hash_key = tuple(hash(arg) for arg in (self, dtype)) + if hash_key in _CACHE_SHAPE_RANGE: + return _CACHE_SHAPE_RANGE[hash_key].copy() - if self._range is None: - samples = as_int( - round((self._interval + self._end - self._start) / - self._interval)) - range_, current_interval = np.linspace( - self._start, self._end, samples, retstep=True, dtype=dtype) + start, end, interval = ( + dtype(self._start), + dtype(self._end), + dtype(self._interval), + ) - self._range = range_ + samples = as_int(round((interval + end - start) / interval)) + range_, interval_effective = np.linspace( + start, end, samples, retstep=True, dtype=dtype + ) - if current_interval != self._interval: - self._interval = current_interval - runtime_warning( - ('"{0}" shape could not be honoured, using ' - '"{1}"!').format((self._start, self._end, self._interval), - self)) + _CACHE_SHAPE_RANGE[hash_key] = range_ - return self._range + if interval_effective != self._interval: + self._interval = interval_effective + runtime_warning( + f'"{(self._start, self._end, self._interval)}" shape could ' + f'not be honoured, using "{self}"!' + ) + return range_ -SPECTRAL_SHAPE_DEFAULT = SpectralShape(360, 780, 1) -""" -Default spectral shape according to *ASTM E308-15* practise shape. -SPECTRAL_SHAPE_DEFAULT : SpectralShape -""" +SPECTRAL_SHAPE_DEFAULT: SpectralShape = SpectralShape(360, 780, 1) +"""Default spectral shape according to *ASTM E308-15* practise shape.""" class SpectralDistribution(Signal): """ - Defines the spectral distribution: the base object for spectral + Define the spectral distribution: the base object for spectral computations. The spectral distribution will be initialised according to *CIE 15:2004* @@ -511,31 +543,30 @@ class SpectralDistribution(Signal): Parameters ---------- - data : Series or Signal, SpectralDistribution or array_like or \ -dict_like, optional + data Data to be stored in the spectral distribution. - domain : array_like, optional + domain Values to initialise the - :attr:`colour.SpectralDistribution.wavelength` attribute with. + :attr:`colour.SpectralDistribution.wavelength` property with. If both ``data`` and ``domain`` arguments are defined, the latter will be used to initialise the - :attr:`colour.SpectralDistribution.wavelength` attribute. + :attr:`colour.SpectralDistribution.wavelength` property. Other Parameters ---------------- - name : unicode, optional - Spectral distribution name. - interpolator : object, optional - Interpolator class type to use as interpolating function. - interpolator_kwargs : dict_like, optional - Arguments to use when instantiating the interpolating function. - extrapolator : object, optional + extrapolator Extrapolator class type to use as extrapolating function. - extrapolator_kwargs : dict_like, optional + extrapolator_kwargs Arguments to use when instantiating the extrapolating function. - strict_name : unicode, optional + interpolator + Interpolator class type to use as interpolating function. + interpolator_kwargs + Arguments to use when instantiating the interpolating function. + name + Spectral distribution name. + strict_name Spectral distribution name for figures, default to - :attr:`colour.SpectralDistribution.name` attribute value. + :attr:`colour.SpectralDistribution.name` property value. Attributes ---------- @@ -602,7 +633,7 @@ class SpectralDistribution(Signal): extrapolator=Extrapolator, extrapolator_kwargs={...}) - Instantiation with a *Pandas* `Series`: + Instantiation with a *Pandas* :class:`pandas.Series`: >>> from colour.utilities import is_pandas_installed >>> if is_pandas_installed(): @@ -617,133 +648,134 @@ class SpectralDistribution(Signal): [ 5.1000000...e+02 3.1416000...e-01]] """ - def __init__(self, data=None, domain=None, **kwargs): - domain, range_ = self.signal_unpack_data(data, domain) - - uniform = is_uniform(domain) if domain is not None else True + def __init__( + self, + data: Optional[ + Union[ArrayLike, dict, Series, Signal, SpectralDistribution] + ] = None, + domain: Optional[Union[ArrayLike, SpectralShape]] = None, + **kwargs: Any, + ): + domain = ( + domain.range() if isinstance(domain, SpectralShape) else domain + ) + domain_unpacked, range_unpacked = self.signal_unpack_data(data, domain) # Initialising with *CIE 15:2004* and *CIE 167:2005* recommendations # defaults. - kwargs['interpolator'] = kwargs.get( - 'interpolator', SpragueInterpolator - if uniform else CubicSplineInterpolator) - kwargs['interpolator_kwargs'] = kwargs.get('interpolator_kwargs', {}) + kwargs["interpolator"] = kwargs.get( + "interpolator", + SpragueInterpolator + if is_uniform(domain_unpacked) + else CubicSplineInterpolator, + ) + kwargs["interpolator_kwargs"] = kwargs.get("interpolator_kwargs", {}) - kwargs['extrapolator'] = kwargs.get('extrapolator', Extrapolator) - kwargs['extrapolator_kwargs'] = kwargs.get('extrapolator_kwargs', { - 'method': 'Constant', - 'left': None, - 'right': None - }) + kwargs["extrapolator"] = kwargs.get("extrapolator", Extrapolator) + kwargs["extrapolator_kwargs"] = kwargs.get( + "extrapolator_kwargs", + {"method": "Constant", "left": None, "right": None}, + ) - super(SpectralDistribution, self).__init__(range_, domain, **kwargs) + super().__init__(range_unpacked, domain_unpacked, **kwargs) - self._strict_name = None - self.strict_name = kwargs.get('strict_name') + self._strict_name: str = self.name + self.strict_name = kwargs.get("strict_name", self._strict_name) @property - def strict_name(self): + def strict_name(self) -> str: """ Getter and setter property for the spectral distribution strict name. Parameters ---------- - value : unicode + value Value to set the spectral distribution strict name with. Returns ------- - unicode + :class:`str` Spectral distribution strict name. """ - if self._strict_name is not None: - return self._strict_name - else: - return self._name + return self._strict_name @strict_name.setter - def strict_name(self, value): - """ - Setter for **self.strict_name** property. - """ + def strict_name(self, value: str): + """Setter for the **self.strict_name** property.""" - if value is not None: - assert is_string(value), ( - ('"{0}" attribute: "{1}" is not a "string" like object!' - ).format('strict_name', value)) - self._strict_name = value + attest( + is_string(value), + f'"strict_name" property: "{value}" type is not "str"!', + ) + + self._strict_name = value @property - def wavelengths(self): + def wavelengths(self) -> NDArray: """ Getter and setter property for the spectral distribution wavelengths :math:`\\lambda_n`. Parameters ---------- - value : array_like + value Value to set the spectral distribution wavelengths :math:`\\lambda_n` with. Returns ------- - ndarray + :class:`numpy.ndarray` Spectral distribution wavelengths :math:`\\lambda_n`. """ return self.domain @wavelengths.setter - def wavelengths(self, value): - """ - Setter for the **self.wavelengths** property. - """ + def wavelengths(self, value: ArrayLike): + """Setter for the **self.wavelengths** property.""" - self.domain = value + self.domain = as_float_array(value, self.dtype) @property - def values(self): + def values(self) -> NDArray: """ Getter and setter property for the spectral distribution values. Parameters ---------- - value : array_like - Value to set the spectral distribution wavelengths values - with. + value + Value to set the spectral distribution wavelengths values with. Returns ------- - ndarray + :class:`numpy.ndarray` Spectral distribution values. """ return self.range @values.setter - def values(self, value): - """ - Setter for the **self.values** property. - """ + def values(self, value: ArrayLike): + """Setter for the **self.values** property.""" - self.range = value + self.range = as_float_array(value, self.dtype) @property - def shape(self): + def shape(self) -> SpectralShape: """ Getter property for the spectral distribution shape. Returns ------- - SpectralShape + :class:`colour.SpectralShape` Spectral distribution shape. Notes ----- - A spectral distribution with a non-uniformly spaced independent variable have multiple intervals, in that case - :attr:`colour.SpectralDistribution.shape` attribute returns + :attr:`colour.SpectralDistribution.shape` property returns the *minimum* interval size. Examples @@ -772,20 +804,25 @@ def shape(self): wavelengths_interval = interval(self.wavelengths) if wavelengths_interval.size != 1: - runtime_warning(('"{0}" spectral distribution is not uniform, ' - 'using minimum interval!'.format(self.name))) + runtime_warning( + f'"{self.name}" spectral distribution is not uniform, using ' + f"minimum interval!" + ) return SpectralShape( - min(self.wavelengths), max(self.wavelengths), - as_float(min(wavelengths_interval))) - - def interpolate(self, - shape, - interpolator=None, - interpolator_kwargs=None, - **kwargs): - """ - Interpolates the spectral distribution in-place according to + min(self.wavelengths), + max(self.wavelengths), + min(wavelengths_interval), + ) + + def interpolate( + self, + shape: SpectralShape, + interpolator: Optional[Type[TypeInterpolator]] = None, + interpolator_kwargs: Optional[Dict] = None, + ) -> SpectralDistribution: + """ + Interpolate the spectral distribution in-place according to *CIE 167:2005* recommendation (if the interpolator has not been changed at instantiation time) or given interpolation arguments. @@ -815,21 +852,16 @@ def interpolate(self, Parameters ---------- - shape : SpectralShape, optional + shape Spectral shape used for interpolation. - interpolator : object, optional + interpolator Interpolator class type to use as interpolating function. - interpolator_kwargs : dict_like, optional + interpolator_kwargs Arguments to use when instantiating the interpolating function. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Interpolated spectral distribution. Notes @@ -866,7 +898,7 @@ def interpolate(self, ... } >>> sd = SpectralDistribution(data) >>> with numpy_print_options(suppress=True): - ... print(sd.interpolate(SpectralShape(interval=1))) + ... print(sd.interpolate(SpectralShape(500, 600, 1))) ... # doctest: +ELLIPSIS [[ 500. 0.0651 ...] [ 501. 0.0653522...] @@ -976,7 +1008,7 @@ def interpolate(self, >>> sd = SpectralDistribution(data) >>> sd[510] = np.pi / 10 >>> with numpy_print_options(suppress=True): - ... print(sd.interpolate(SpectralShape(interval=1))) + ... print(sd.interpolate(SpectralShape(500, 600, 1))) ... # doctest: +ELLIPSIS [[ 500. 0.0651 ...] [ 501. 0.1365202...] @@ -1081,30 +1113,44 @@ def interpolate(self, [ 600. 0.136 ...]] """ - interpolator_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['interpolator_args', 'interpolator_kwargs']], - }, **kwargs).get('interpolator_kwargs', interpolator_kwargs) + shape_start, shape_end, shape_interval = as_float_array( + [ + self.shape.start, + self.shape.end, + self.shape.interval, + ] + ) - self_shape = self.shape - s_e_i = zip((shape.start, shape.end, shape.interval), - (self_shape.start, self_shape.end, self_shape.interval)) shape = SpectralShape( - *[x[0] if x[0] is not None else x[1] for x in s_e_i]) + *[ + x[0] if x[0] is not None else x[1] + for x in zip( + (shape.start, shape.end, shape.interval), + (shape_start, shape_end, shape_interval), + ) + ] + ) + # Defining proper interpolation bounds. # TODO: Provide support for fractional interval like 0.1, etc... - if (round(self_shape.start) != self_shape.start or - round(self_shape.end) != self_shape.end): + if ( + np.around(shape_start) != shape_start + or np.around(shape_end) != shape_end + ): runtime_warning( - 'Fractional bound encountered, rounding will occur!') + "Fractional bound encountered, rounding will occur!" + ) - shape.start = max(shape.start, np.ceil(self_shape.start)) - shape.end = min(shape.end, np.floor(self_shape.end)) + shape.start = max([shape.start, np.ceil(shape_start)]) + shape.end = min([shape.end, np.floor(shape_end)]) if interpolator is None: # User has specifically chosen the interpolator thus it is used # instead of those from *CIE 167:2005* recommendation. - if self.interpolator not in (SpragueInterpolator, - CubicSplineInterpolator): + if self.interpolator not in ( + SpragueInterpolator, + CubicSplineInterpolator, + ): interpolator = self.interpolator elif self.is_uniform(): interpolator = SpragueInterpolator @@ -1114,47 +1160,48 @@ def interpolate(self, if interpolator_kwargs is None: # User has specifically chosen the interpolator thus its keyword # arguments are used. - if self.interpolator not in (SpragueInterpolator, - CubicSplineInterpolator): + if self.interpolator not in ( + SpragueInterpolator, + CubicSplineInterpolator, + ): interpolator_kwargs = self.interpolator_kwargs else: interpolator_kwargs = {} - interpolator = interpolator(self.wavelengths, self.values, - **interpolator_kwargs) + wavelengths, values = self.wavelengths, self.values self.domain = shape.range() - self.range = interpolator(self.domain) + self.range = as_float_array( + interpolator(wavelengths, values, **interpolator_kwargs)( + self.domain + ) + ) return self - def extrapolate(self, - shape, - extrapolator=None, - extrapolator_kwargs=None, - **kwargs): + def extrapolate( + self, + shape: SpectralShape, + extrapolator: Optional[Type[TypeExtrapolator]] = None, + extrapolator_kwargs: Optional[Dict] = None, + ) -> SpectralDistribution: """ - Extrapolates the spectral distribution in-place according to + Extrapolate the spectral distribution in-place according to *CIE 15:2004* and *CIE 167:2005* recommendations or given extrapolation arguments. Parameters ---------- - shape : SpectralShape + shape Spectral shape used for extrapolation. - extrapolator : object, optional + extrapolator Extrapolator class type to use as extrapolating function. - extrapolator_kwargs : dict_like, optional + extrapolator_kwargs Arguments to use when instantiating the extrapolating function. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Extrapolated spectral distribution. References @@ -1173,7 +1220,7 @@ def extrapolate(self, ... 600: 0.1360 ... } >>> sd = SpectralDistribution(data) - >>> sd.extrapolate(SpectralShape(400, 700)).shape + >>> sd.extrapolate(SpectralShape(400, 700, 20)).shape SpectralShape(400.0, 700.0, 20.0) >>> with numpy_print_options(suppress=True): ... print(sd) @@ -1195,26 +1242,27 @@ def extrapolate(self, [ 700. 0.136 ]] """ - extrapolator_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['extrapolator_args', 'extrapolator_kwargs']], - }, **kwargs).get('extrapolator_kwargs', extrapolator_kwargs) - - self_shape = self.shape - wavelengths = np.hstack([ - np.arange(shape.start, self_shape.start, self_shape.interval), - np.arange(self_shape.end + self_shape.interval, - shape.end + self_shape.interval, self_shape.interval) - ]) - - if extrapolator is None: - extrapolator = Extrapolator - - if extrapolator_kwargs is None: - extrapolator_kwargs = { - 'method': 'Constant', - 'left': None, - 'right': None - } + shape_start, shape_end, shape_interval = as_float_array( + [ + self.shape.start, + self.shape.end, + self.shape.interval, + ] + ) + + wavelengths = np.hstack( + [ + np.arange(shape.start, shape_start, shape_interval), + np.arange(shape_end, shape.end, shape_interval) + + shape_interval, + ] + ) + + extrapolator = optional(extrapolator, Extrapolator) + extrapolator_kwargs = optional( + extrapolator_kwargs, + {"method": "Constant", "left": None, "right": None}, + ) self_extrapolator = self.extrapolator self_extrapolator_kwargs = self.extrapolator_kwargs @@ -1231,15 +1279,16 @@ def extrapolate(self, return self - def align(self, - shape, - interpolator=None, - interpolator_kwargs=None, - extrapolator=None, - extrapolator_kwargs=None, - **kwargs): + def align( + self, + shape: SpectralShape, + interpolator: Optional[Type[TypeInterpolator]] = None, + interpolator_kwargs: Optional[Dict] = None, + extrapolator: Optional[Type[TypeExtrapolator]] = None, + extrapolator_kwargs: Optional[Dict] = None, + ) -> SpectralDistribution: """ - Aligns the spectral distribution in-place to given spectral shape: + Align the spectral distribution in-place to given spectral shape: Interpolates first then extrapolates to fit the given range. Interpolation is performed according to *CIE 167:2005* recommendation @@ -1272,25 +1321,20 @@ def align(self, Parameters ---------- - shape : SpectralShape + shape Spectral shape used for alignment. - interpolator : object, optional + interpolator Interpolator class type to use as interpolating function. - interpolator_kwargs : dict_like, optional + interpolator_kwargs Arguments to use when instantiating the interpolating function. - extrapolator : object, optional + extrapolator Extrapolator class type to use as extrapolating function. - extrapolator_kwargs : dict_like, optional + extrapolator_kwargs Arguments to use when instantiating the extrapolating function. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Aligned spectral distribution. Examples @@ -1376,18 +1420,18 @@ def align(self, return self - def trim(self, shape): + def trim(self, shape: SpectralShape) -> SpectralDistribution: """ - Trims the spectral distribution wavelengths to given spectral shape. + Trim the spectral distribution wavelengths to given spectral shape. Parameters ---------- - shape : SpectralShape + shape Spectral shape used for trimming. Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Trimmed spectral distribution. Examples @@ -1402,7 +1446,7 @@ def trim(self, shape): ... 600: 0.1360 ... } >>> sd = SpectralDistribution(data) - >>> sd = sd.interpolate(SpectralShape(interval=1)) + >>> sd = sd.interpolate(SpectralShape(500, 600, 1)) >>> with numpy_print_options(suppress=True): ... print(sd.trim(SpectralShape(520, 580, 5))) ... # doctest: +ELLIPSIS @@ -1469,11 +1513,12 @@ def trim(self, shape): [ 580. 0.1128 ...]] """ - start = max(shape.start, self.shape.start) - end = min(shape.end, self.shape.end) + start = max([shape.start, self.shape.start]) + end = min([shape.end, self.shape.end]) indexes = np.where( - np.logical_and(self.domain >= start, self.domain <= end)) + np.logical_and(self.domain >= start, self.domain <= end) + ) wavelengths = self.wavelengths[indexes] values = self.values[indexes] @@ -1483,18 +1528,18 @@ def trim(self, shape): return self - def normalise(self, factor=1): + def normalise(self, factor: Number = 1) -> SpectralDistribution: """ - Normalises the spectral distribution using given normalization factor. + Normalise the spectral distribution using given normalization factor. Parameters ---------- - factor : numeric, optional - Normalization factor + factor + Normalization factor. Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Normalised spectral distribution. Examples @@ -1519,78 +1564,14 @@ def normalise(self, factor=1): [ 600. 1. ...]] """ - self *= 1 / max(self.values) * factor + self *= 1 / max(self.values) * factor # type: ignore[misc] return self - # ------------------------------------------------------------------------# - # --- API Changes and Deprecation Management ---# - # ------------------------------------------------------------------------# - @property - def title(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('SpectralPowerDistribution.title', - 'SpectralDistribution.strict_name'))) - - return self.strict_name - - @title.setter - def title(self, value): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('SpectralPowerDistribution.title', - 'SpectralDistribution.strict_name'))) - - self.strict_name = value - - @property - def data(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - raise AttributeError(str(ObjectRemoved('SpectralDistribution.data'))) - - @property - def items(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - raise AttributeError(str(ObjectRemoved('SpectralDistribution.items'))) - - def __iter__(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - raise AttributeError( - str(ObjectRemoved('SpectralDistribution.__iter__'))) - - def get(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - raise AttributeError(str(ObjectRemoved('SpectralDistribution.get'))) - - def zeros(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - raise AttributeError(str(ObjectRemoved('SpectralDistribution.zeros'))) - - def trim_wavelengths(self, shape): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('SpectralPowerDistribution.trim_wavelengths', - 'SpectralDistribution.trim'))) - - return self.trim(shape) - - def clone(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('SpectralPowerDistribution.clone', - 'SpectralDistribution.copy'))) - - return self.copy() - class MultiSpectralDistributions(MultiSignals): """ - Defines the multi-spectral distributions: the base object for multi + Define the multi-spectral distributions: the base object for multi spectral computations. It is used to model colour matching functions, display primaries, camera sensitivities, etc... @@ -1609,38 +1590,37 @@ class MultiSpectralDistributions(MultiSignals): Parameters ---------- - data : Series or Dataframe or Signal or MultiSignals or \ -MultiSpectralDistributions or array_like or dict_like, optional + data Data to be stored in the multi-spectral distributions. - domain : array_like, optional + domain Values to initialise the multiple :class:`colour.SpectralDistribution` class instances :attr:`colour.continuous.Signal.wavelengths` attribute with. If both ``data`` and ``domain`` arguments are defined, the latter will be used to initialise the - :attr:`colour.continuous.Signal.wavelengths` attribute. - labels : array_like, optional + :attr:`colour.continuous.Signal.wavelengths` property. + labels Names to use for the :class:`colour.SpectralDistribution` class instances. Other Parameters ---------------- - name : unicode, optional - Multi-spectral distributions name. - interpolator : object, optional - Interpolator class type to use as interpolating function for the - :class:`colour.SpectralDistribution` class instances. - interpolator_kwargs : dict_like, optional - Arguments to use when instantiating the interpolating function of the - :class:`colour.SpectralDistribution` class instances. - extrapolator : object, optional + extrapolator Extrapolator class type to use as extrapolating function for the :class:`colour.SpectralDistribution` class instances. - extrapolator_kwargs : dict_like, optional + extrapolator_kwargs Arguments to use when instantiating the extrapolating function of the :class:`colour.SpectralDistribution` class instances. - strict_labels : array_like, optional + interpolator + Interpolator class type to use as interpolating function for the + :class:`colour.SpectralDistribution` class instances. + interpolator_kwargs + Arguments to use when instantiating the interpolating function of the + :class:`colour.SpectralDistribution` class instances. + name + Multi-spectral distributions name. + strict_labels Multi-spectral distributions labels for figures, default to - :attr:`colour.MultiSpectralDistributions.labels` attribute value. + :attr:`colour.MultiSpectralDistributions.labels` property value. Attributes ---------- @@ -1746,7 +1726,28 @@ class instances :attr:`colour.continuous.Signal.wavelengths` attribute 3.9000000...e-03]] """ - def __init__(self, data=None, domain=None, labels=None, **kwargs): + def __init__( + self, + data: Optional[ + Union[ + ArrayLike, + DataFrame, + dict, + MultiSignals, + MultiSpectralDistributions, + Sequence, + Series, + Signal, + SpectralDistribution, + ] + ] = None, + domain: Optional[Union[ArrayLike, SpectralShape]] = None, + labels: Optional[Sequence] = None, + **kwargs: Any, + ): + domain = ( + domain.range() if isinstance(domain, SpectralShape) else domain + ) signals = self.multi_signals_unpack_data(data, domain, labels) domain = signals[list(signals.keys())[0]].domain if signals else None @@ -1754,168 +1755,167 @@ def __init__(self, data=None, domain=None, labels=None, **kwargs): # Initialising with *CIE 15:2004* and *CIE 167:2005* recommendations # defaults. - kwargs['interpolator'] = kwargs.get( - 'interpolator', SpragueInterpolator - if uniform else CubicSplineInterpolator) - kwargs['interpolator_kwargs'] = kwargs.get('interpolator_kwargs', {}) - - kwargs['extrapolator'] = kwargs.get('extrapolator', Extrapolator) - kwargs['extrapolator_kwargs'] = kwargs.get('extrapolator_kwargs', { - 'method': 'Constant', - 'left': None, - 'right': None - }) - - super(MultiSpectralDistributions, self).__init__( - signals, domain, signal_type=SpectralDistribution, **kwargs) - - self._strict_name = None - self.strict_name = kwargs.get('strict_name') - self._strict_labels = None - self.strict_labels = kwargs.get('strict_labels') + kwargs["interpolator"] = kwargs.get( + "interpolator", + SpragueInterpolator if uniform else CubicSplineInterpolator, + ) + kwargs["interpolator_kwargs"] = kwargs.get("interpolator_kwargs", {}) + + kwargs["extrapolator"] = kwargs.get("extrapolator", Extrapolator) + kwargs["extrapolator_kwargs"] = kwargs.get( + "extrapolator_kwargs", + {"method": "Constant", "left": None, "right": None}, + ) + + super().__init__( + signals, domain, signal_type=SpectralDistribution, **kwargs + ) + + self._strict_name: str = self.name + self.strict_name = kwargs.get("strict_name", self._strict_name) + self._strict_labels: List = list(self.signals.keys()) + self.strict_labels = kwargs.get("strict_labels", self._strict_labels) @property - def strict_name(self): + def strict_name(self) -> str: """ Getter and setter property for the multi-spectral distributions strict name. Parameters ---------- - value : unicode + value Value to set the multi-spectral distributions strict name with. Returns ------- - unicode + :class:`str` Multi-spectral distributions strict name. """ - if self._strict_name is not None: - return self._strict_name - else: - return self._name + return self._strict_name @strict_name.setter - def strict_name(self, value): - """ - Setter for **self.strict_name** property. - """ + def strict_name(self, value: str): + """Setter for the **self.strict_name** property.""" - if value is not None: - assert is_string(value), ( - ('"{0}" attribute: "{1}" is not a "string" like object!' - ).format('strict_name', value)) - self._strict_name = value + attest( + is_string(value), + f'"strict_name" property: "{value}" type is not "str"!', + ) + + self._strict_name = value @property - def strict_labels(self): + def strict_labels(self) -> List[str]: """ Getter and setter property for the multi-spectral distributions strict labels. Parameters ---------- - value : array_like + value Value to set the multi-spectral distributions strict labels with. Returns ------- - array_like + :class:`list` Multi-spectral distributions strict labels. """ - if self._strict_labels is not None: - return self._strict_labels - else: - return self.labels + return self._strict_labels @strict_labels.setter - def strict_labels(self, value): - """ - Setter for **self.strict_labels** property. - """ + def strict_labels(self, value: Sequence): + """Setter for the **self.strict_labels** property.""" + + attest( + is_iterable(value), + f'"strict_labels" property: "{value}" is not an "iterable" like object!', + ) - if value is not None: - assert is_iterable(value), ( - '"{0}" attribute: "{1}" is not an "iterable" like object!'. - format('strict_labels', value)) + attest( + len(set(value)) == len(value), + '"strict_labels" property: values must be unique!', + ) - assert len(value) == len( - self.labels), ('"{0}" attribute: length must be "{1}"!'.format( - 'strict_labels', len(self.labels))) - self._strict_labels = value + attest( + len(value) == len(self.labels), + f'"strict_labels" property: length must be "{len(self.labels)}"!', + ) + + self._strict_labels = [str(label) for label in value] + for i, signal in enumerate(self.signals.values()): + cast( + SpectralDistribution, signal + ).strict_name = self._strict_labels[i] @property - def wavelengths(self): + def wavelengths(self) -> NDArray: """ Getter and setter property for the multi-spectral distributions wavelengths :math:`\\lambda_n`. Parameters ---------- - value : array_like + value Value to set the multi-spectral distributions wavelengths :math:`\\lambda_n` with. Returns ------- - ndarray + :class:`numpy.ndarray` Multi-spectral distributions wavelengths :math:`\\lambda_n`. """ return self.domain @wavelengths.setter - def wavelengths(self, value): - """ - Setter for the **self.wavelengths** property. - """ + def wavelengths(self, value: ArrayLike): + """Setter for the **self.wavelengths** property.""" - self.domain = value + self.domain = as_float_array(value, self.dtype) @property - def values(self): + def values(self) -> NDArray: """ Getter and setter property for the multi-spectral distributions values. Parameters ---------- - value : array_like + value Value to set the multi-spectral distributions wavelengths values with. Returns ------- - ndarray + :class:`numpy.ndarray` Multi-spectral distributions values. """ return self.range @values.setter - def values(self, value): - """ - Setter for the **self.values** property. - """ + def values(self, value: ArrayLike): + """Setter for the **self.values** property.""" - self.range = value + self.range = as_float_array(value, self.dtype) @property - def shape(self): + def shape(self) -> SpectralShape: """ Getter property for the multi-spectral distributions shape. Returns ------- - SpectralShape + :class:`colour.SpectralShape` Multi-spectral distributions shape. Notes ----- - Multi-spectral distributions with a non-uniformly spaced independent variable have multiple intervals, in that case - :attr:`colour.MultiSpectralDistributions.shape` attribute returns + :attr:`colour.MultiSpectralDistributions.shape` property returns the *minimum* interval size. Examples @@ -1944,16 +1944,16 @@ def shape(self): SpectralShape(500.0, 560.0, 1.0) """ - if self.signals: - return first_item(self._signals.values()).shape + return first_item(self._signals.values()).shape - def interpolate(self, - shape, - interpolator=None, - interpolator_kwargs=None, - **kwargs): + def interpolate( + self, + shape: SpectralShape, + interpolator: Optional[Type[TypeInterpolator]] = None, + interpolator_kwargs: Optional[Dict] = None, + ) -> MultiSpectralDistributions: """ - Interpolates the multi-spectral distributions in-place according to + Interpolate the multi-spectral distributions in-place according to *CIE 167:2005* recommendation (if the interpolator has not been changed at instantiation time) or given interpolation arguments. @@ -1983,21 +1983,16 @@ def interpolate(self, Parameters ---------- - shape : SpectralShape, optional + shape Spectral shape used for interpolation. - interpolator : object, optional + interpolator Interpolator class type to use as interpolating function. - interpolator_kwargs : dict_like, optional + interpolator_kwargs Arguments to use when instantiating the interpolating function. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - MultiSpectralDistributions + :class:`colour.MultiSpectralDistributions` Interpolated multi-spectral distributions. Notes @@ -2031,7 +2026,7 @@ def interpolate(self, ... } >>> msds = MultiSpectralDistributions(data) >>> with numpy_print_options(suppress=True): - ... print(msds.interpolate(SpectralShape(interval=1))) + ... print(msds.interpolate(SpectralShape(500, 560, 1))) ... # doctest: +ELLIPSIS [[ 500. 0.0049 ... 0.323 ... 0.272 ...] [ 501. 0.0043252... 0.3400642... 0.2599848...] @@ -2101,7 +2096,7 @@ def interpolate(self, >>> data[511] = (0.00314, 0.31416, 0.03142) >>> msds = MultiSpectralDistributions(data) >>> with numpy_print_options(suppress=True): - ... print(msds.interpolate(SpectralShape(interval=1))) + ... print(msds.interpolate(SpectralShape(500, 560, 1))) ... # doctest: +ELLIPSIS [[ 500. 0.0049 ... 0.323 ... 0.272 ...] [ 501. 0.0300110... 0.9455153... 0.5985102...] @@ -2166,42 +2161,36 @@ def interpolate(self, [ 560. 0.5945 ... 0.995 ... 0.0039 ...]] """ - interpolator_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['interpolator_args', 'interpolator_kwargs']], - }, **kwargs).get('interpolator_kwargs', interpolator_kwargs) - for signal in self.signals.values(): - signal.interpolate(shape, interpolator, interpolator_kwargs) + cast(SpectralDistribution, signal).interpolate( + shape, interpolator, interpolator_kwargs + ) return self - def extrapolate(self, - shape, - extrapolator=None, - extrapolator_kwargs=None, - **kwargs): + def extrapolate( + self, + shape: SpectralShape, + extrapolator: Optional[Type[TypeExtrapolator]] = None, + extrapolator_kwargs: Optional[Dict] = None, + ) -> MultiSpectralDistributions: """ - Extrapolates the multi-spectral distributions in-place according to + Extrapolate the multi-spectral distributions in-place according to *CIE 15:2004* and *CIE 167:2005* recommendations or given extrapolation arguments. Parameters ---------- - shape : SpectralShape + shape Spectral shape used for extrapolation. - extrapolator : object, optional + extrapolator Extrapolator class type to use as extrapolating function. - extrapolator_kwargs : dict_like, optional + extrapolator_kwargs Arguments to use when instantiating the extrapolating function. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - MultiSpectralDistributions + :class:`colour.MultiSpectralDistributions` Extrapolated multi-spectral distributions. References @@ -2221,7 +2210,7 @@ def extrapolate(self, ... 560: (0.594500, 0.995000, 0.003900) ... } >>> msds = MultiSpectralDistributions(data) - >>> msds.extrapolate(SpectralShape(400, 700)).shape + >>> msds.extrapolate(SpectralShape(400, 700, 10)).shape SpectralShape(400.0, 700.0, 10.0) >>> with numpy_print_options(suppress=True): ... print(msds) @@ -2258,24 +2247,23 @@ def extrapolate(self, [ 700. 0.5945 0.995 0.0039 ]] """ - extrapolator_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['extrapolator_args', 'extrapolator_kwargs']], - }, **kwargs).get('extrapolator_kwargs', extrapolator_kwargs) - for signal in self.signals.values(): - signal.extrapolate(shape, extrapolator, extrapolator_kwargs) + cast(SpectralDistribution, signal).extrapolate( + shape, extrapolator, extrapolator_kwargs + ) return self - def align(self, - shape, - interpolator=None, - interpolator_kwargs=None, - extrapolator=None, - extrapolator_kwargs=None, - **kwargs): + def align( + self, + shape: SpectralShape, + interpolator: Optional[Type[TypeInterpolator]] = None, + interpolator_kwargs: Optional[Dict] = None, + extrapolator: Optional[Type[TypeExtrapolator]] = None, + extrapolator_kwargs: Optional[Dict] = None, + ) -> MultiSpectralDistributions: """ - Aligns the multi-spectral distributions in-place to given spectral + Align the multi-spectral distributions in-place to given spectral shape: Interpolates first then extrapolates to fit the given range. Interpolation is performed according to *CIE 167:2005* recommendation @@ -2308,25 +2296,20 @@ def align(self, Parameters ---------- - shape : SpectralShape + shape Spectral shape used for alignment. - interpolator : object, optional + interpolator Interpolator class type to use as interpolating function. - interpolator_kwargs : dict_like, optional + interpolator_kwargs Arguments to use when instantiating the interpolating function. - extrapolator : object, optional + extrapolator Extrapolator class type to use as extrapolating function. - extrapolator_kwargs : dict_like, optional + extrapolator_kwargs Arguments to use when instantiating the extrapolating function. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - MultiSpectralDistributions + :class:`colour.MultiSpectralDistributions` Aligned multi-spectral distributions. Examples @@ -2408,32 +2391,29 @@ def align(self, [ 565. 0.5945 ... 0.995 ... 0.0039 ...]] """ - interpolator_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['interpolator_args', 'interpolator_kwargs']], - }, **kwargs).get('interpolator_kwargs', interpolator_kwargs) - - extrapolator_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['extrapolator_args', 'extrapolator_kwargs']], - }, **kwargs).get('extrapolator_kwargs', extrapolator_kwargs) - for signal in self.signals.values(): - signal.align(shape, interpolator, interpolator_kwargs, - extrapolator, extrapolator_kwargs) + cast(SpectralDistribution, signal).align( + shape, + interpolator, + interpolator_kwargs, + extrapolator, + extrapolator_kwargs, + ) return self - def trim(self, shape): + def trim(self, shape: SpectralShape) -> MultiSpectralDistributions: """ - Trims the multi-spectral distributions wavelengths to given shape. + Trim the multi-spectral distributions wavelengths to given shape. Parameters ---------- - shape : SpectralShape + shape Spectral shape used for trimming. Returns ------- - MultiSpectralDistributions + :class:`colour.MultiSpectralDistributions` Trimmed multi-spectral distributions. Examples @@ -2449,7 +2429,7 @@ def trim(self, shape): ... 560: (0.594500, 0.995000, 0.003900) ... } >>> msds = MultiSpectralDistributions(data) - >>> msds = msds.interpolate(SpectralShape(interval=1)) + >>> msds = msds.interpolate(SpectralShape(500, 560, 1)) >>> with numpy_print_options(suppress=True): ... print(msds.trim(SpectralShape(520, 580, 5))) ... # doctest: +ELLIPSIS @@ -2497,23 +2477,23 @@ def trim(self, shape): """ for signal in self.signals.values(): - signal.trim(shape) + cast(SpectralDistribution, signal).trim(shape) return self - def normalise(self, factor=1): + def normalise(self, factor: Number = 1) -> MultiSpectralDistributions: """ - Normalises the multi-spectral distributions with given normalization + Normalise the multi-spectral distributions with given normalization factor. Parameters ---------- - factor : numeric, optional - Normalization factor + factor + Normalization factor. Returns ------- - MultiSpectralDistributions + :class:`colour.MultiSpectralDistributions` Normalised multi- spectral distribution. Notes @@ -2546,19 +2526,18 @@ def normalise(self, factor=1): """ for signal in self.signals.values(): - signal.normalise(factor) + cast(SpectralDistribution, signal).normalise(factor) return self - def to_sds(self): + def to_sds(self) -> List[SpectralDistribution]: """ - Converts the multi-spectral distributions to a list of spectral - distributions and update their name and strict name using the labels - and strict labels. + Convert the multi-spectral distributions to a list of spectral + distributions. Returns ------- - list + :class:`list` List of spectral distributions. Examples @@ -2600,125 +2579,152 @@ def to_sds(self): [ 560. 0.0039 ...]] """ - sds = [] - for i, signal in enumerate(self.signals.values()): - signal = signal.copy() - signal.name = '{0} - {1}'.format(self.labels[i], signal.name) - signal.strict_name = '{0} - {1}'.format(self.strict_labels[i], - signal.strict_name) + return [ + cast(SpectralDistribution, signal.copy()) + for signal in self.signals.values() + ] - sds.append(signal) - return sds +_CACHE_RESHAPED_SDS_AND_MSDS: Dict = CACHE_REGISTRY.register_cache( + f"{__name__}._CACHE_RESHAPED_SDS_AND_MSDS" +) - # ------------------------------------------------------------------------# - # --- API Changes and Deprecation Management ---# - # ------------------------------------------------------------------------# - @property - def title(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('TriSpectralPowerDistribution.title', - 'SpectralDistribution.strict_name'))) - return self.strict_name +def reshape_sd( + sd: SpectralDistribution, + shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, + method: Union[ + Literal["Align", "Extrapolate", "Interpolate", "Trim"], str + ] = "Align", + **kwargs: Any, +) -> SpectralDistribution: + """ + Reshape given spectral distribution with given spectral shape. - @title.setter - def title(self, value): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('TriSpectralPowerDistribution.title', - 'SpectralDistribution.strict_name'))) + The reshaped object is cached, thus another call to the definition with the + same arguments will yield the cached object immediately. - self.strict_name = value + Parameters + ---------- + sd + Spectral distribution to reshape. + shape + Spectral shape to reshape the spectral distribution with. + method + Reshape method. - @property - def data(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - raise AttributeError( - str(ObjectRemoved('MultiSpectralDistributions.data'))) + Other Parameters + ---------------- + kwargs + {:meth:`colour.SpectralDistribution.align`, + :meth:`colour.SpectralDistribution.extrapolate`, + :meth:`colour.SpectralDistribution.interpolate`, + :meth:`colour.SpectralDistribution.trim`}, + See the documentation of the previously listed methods. - @property - def items(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - raise AttributeError( - str(ObjectRemoved('MultiSpectralDistributions.items'))) + Returns + ------- + :class:`colour.SpectralDistribution` - @property - def mapping(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - raise AttributeError( - str(ObjectRemoved('MultiSpectralDistributions.mapping'))) + Warnings + -------- + Contrary to *Numpy*, reshaping a spectral distribution alters its data! + """ - @property - def x(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - raise AttributeError( - str(ObjectRemoved('MultiSpectralDistributions.x'))) + method = validate_method( + method, valid_methods=["Align", "Extrapolate", "Interpolate", "Trim"] + ) - @property - def y(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - raise AttributeError( - str(ObjectRemoved('MultiSpectralDistributions.y'))) + # Handling dict-like keyword arguments. + kwargs_items = list(kwargs.items()) + for i, (keyword, value) in enumerate(kwargs_items): + if isinstance(value, Mapping): + kwargs_items[i] = (keyword, tuple(value.items())) - @property - def z(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - raise AttributeError( - str(ObjectRemoved('MultiSpectralDistributions.z'))) - - def __iter__(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - raise AttributeError( - str(ObjectRemoved('MultiSpectralDistributions.__iter__'))) - - def get(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - raise AttributeError( - str(ObjectRemoved('MultiSpectralDistributions.get'))) - - def zeros(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - raise AttributeError( - str(ObjectRemoved('MultiSpectralDistributions.zeros'))) - - def trim_wavelengths(self, shape): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('TriSpectralPowerDistribution.trim_wavelengths', - 'MultiSpectralDistributions.trim'))) - - return self.trim(shape) - - def clone(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('TriSpectralPowerDistribution.clone', - 'MultiSpectralDistributions.copy'))) - - return self.copy() - - -def sds_and_msds_to_sds(sds): + hash_key = tuple( + hash(arg) for arg in (sd, shape, method, tuple(kwargs_items)) + ) + if hash_key in _CACHE_RESHAPED_SDS_AND_MSDS: + return _CACHE_RESHAPED_SDS_AND_MSDS[hash_key].copy() + + function = getattr(sd, method) + + reshaped_sd = getattr(sd.copy(), method)( + shape, **filter_kwargs(function, **kwargs) + ) + + _CACHE_RESHAPED_SDS_AND_MSDS[hash_key] = reshaped_sd + + return reshaped_sd + + +def reshape_msds( + msds: MultiSpectralDistributions, + shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, + method: Union[ + Literal["Align", "Extrapolate", "Interpolate", "Trim"], str + ] = "Align", + **kwargs: Any, +) -> MultiSpectralDistributions: + """ + Reshape given multi-spectral distributions with given spectral shape. + + The reshaped object is cached, thus another call to the definition with the + same arguments will yield the cached object immediately. + + Parameters + ---------- + msds + Spectral distribution to reshape. + shape + Spectral shape to reshape the multi-spectral distributions with. + method + Reshape method. + + Other Parameters + ---------------- + kwargs + {:meth:`colour.MultiSpectralDistributions.align`, + :meth:`colour.MultiSpectralDistributions.extrapolate`, + :meth:`colour.MultiSpectralDistributions.interpolate`, + :meth:`colour.MultiSpectralDistributions.trim`}, + See the documentation of the previously listed methods. + + Returns + ------- + :class:`colour.MultiSpectralDistributions` + + Warnings + -------- + Contrary to *Numpy*, reshaping a multi-spectral distributions alters its + data! + """ + + return reshape_sd( + msds, shape, method, **kwargs # type: ignore[arg-type] + ) # type: ignore[return-value] + + +def sds_and_msds_to_sds( + sds: Union[ + Sequence[Union[SpectralDistribution, MultiSpectralDistributions]], + MultiSpectralDistributions, + ] +) -> List[SpectralDistribution]: """ - Converts given spectral and multi-spectral distributions to a flat list of + Convert given spectral and multi-spectral distributions to a list of spectral distributions. Parameters ---------- - sds : array_like - Spectral and multi-spectral distributions to convert to a flat list of + sds + Spectral and multi-spectral distributions to convert to a list of spectral distributions. Returns ------- - list - Flat list of spectral distributions. + :class:`list` + List of spectral distributions. Examples -------- @@ -2747,24 +2753,28 @@ def sds_and_msds_to_sds(sds): 8 """ - if not len(sds): - return - if isinstance(sds, MultiSpectralDistributions): - sds = sds.to_sds() + sds_converted = sds.to_sds() else: - sds = list(sds) - for i, sd in enumerate(sds[:]): - if isinstance(sd, MultiSpectralDistributions): - sds.remove(sd) - sds[i:i] = sd.to_sds() + sds_converted = [] + for sd in sds: + sds_converted += ( + sd.to_sds() + if isinstance(sd, MultiSpectralDistributions) + else [sd] + ) - return sds + return sds_converted -def sds_and_msds_to_msds(sds): +def sds_and_msds_to_msds( + sds: Union[ + Sequence[Union[SpectralDistribution, MultiSpectralDistributions]], + MultiSpectralDistributions, + ] +) -> MultiSpectralDistributions: """ - Converts given spectral and multi-spectral distributions to + Convert given spectral and multi-spectral distributions to multi-spectral distributions. The spectral and multi-spectral distributions will be aligned to the @@ -2772,13 +2782,13 @@ def sds_and_msds_to_msds(sds): Parameters ---------- - sds : array_like + sds Spectral and multi-spectral distributions to convert to multi-spectral distributions. Returns ------- - MultiSpectralDistributions + :class:`colour.MultiSpectralDistributions` Multi-spectral distributions. Examples @@ -2840,41 +2850,37 @@ def sds_and_msds_to_msds(sds): extrapolator_kwargs={...}) """ - if not len(sds): - return - if isinstance(sds, MultiSpectralDistributions): - msds = sds - elif len(sds) == 1 and isinstance(sds[0], MultiSpectralDistributions): - msds = sds[0] + msds_converted = sds else: - sds_u = [] - shapes = [] - for sd in sds: - if isinstance(sd, MultiSpectralDistributions): - sds_m = sds_and_msds_to_sds(sd) - sds_u.extend(sds_m) - shapes.extend([sd_m.shape for sd_m in sds_m]) - else: - sds_u.append(sd) - shapes.append(sd.shape) + sds_converted = sds_and_msds_to_sds(sds) - shapes = tuple(set(shapes)) + shapes = tuple({sd.shape for sd in sds_converted}) shape = SpectralShape( - max([shape.start for shape in shapes]), - min([shape.end for shape in shapes]), - min([shape.interval for shape in shapes])) + max(shape.start for shape in shapes), + min(shape.end for shape in shapes), + min(shape.interval for shape in shapes), + ) values = [] labels = [] strict_labels = [] - for sd_u in sds_u: - sd_u.align(shape) - values.append(sd_u.values) - labels.append(sd_u.name) - strict_labels.append(sd_u.strict_name) - - msds = MultiSpectralDistributions( - tstack(values), shape.range(), labels, strict_labels=strict_labels) - - return msds + for sd in sds_converted: + if sd.shape != shape: + sd = sd.align(shape) + + values.append(sd.values) + labels.append( + sd.name if sd.name not in labels else f"{sd.name} ({id(sd)})" + ) + strict_labels.append( + sd.strict_name + if sd.strict_name not in strict_labels + else f"{sd.strict_name} ({id(sd)})" + ) + + msds_converted = MultiSpectralDistributions( + tstack(values), shape.range(), labels, strict_labels=strict_labels + ) + + return msds_converted diff --git a/colour/colorimetry/tests/__init__.py b/colour/colorimetry/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/colorimetry/tests/__init__.py +++ b/colour/colorimetry/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/colorimetry/tests/test_blackbody.py b/colour/colorimetry/tests/test_blackbody.py index e549bf6307..aa6a4684cf 100644 --- a/colour/colorimetry/tests/test_blackbody.py +++ b/colour/colorimetry/tests/test_blackbody.py @@ -1,29 +1,30 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.colorimetry.blackbody` module. -""" +"""Defines the unit tests for the :mod:`colour.colorimetry.blackbody` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import unittest from itertools import permutations from colour.colorimetry import SpectralShape, planck_law, sd_blackbody +from colour.hints import Dict, NDArray from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'DATA_PLANCK_LAW', 'DATA_BLACKBODY', 'TestPlanckLaw', 'TestSdBlackbody' + "DATA_PLANCK_LAW", + "DATA_BLACKBODY", + "TestPlanckLaw", + "TestSdBlackbody", ] -DATA_PLANCK_LAW = { +DATA_PLANCK_LAW: Dict = { 1667: { 10: 0.000000000000000, 20: 0.000000000000000, @@ -3025,7 +3026,7 @@ 9960: 7818858886.277912139892578, 9970: 7788114277.527709007263184, 9980: 7757520610.135956764221191, - 9990: 7727076995.859315872192383 + 9990: 7727076995.859315872192383, }, 100000: { 10: 671890609624107008.000000000000000, @@ -4026,495 +4027,495 @@ 9960: 83512005091.210250854492188, 9970: 83178059708.148803710937500, 9980: 82845781868.989334106445312, - 9990: 82515161591.536300659179688 - } + 9990: 82515161591.536300659179688, + }, } -DATA_BLACKBODY = np.array([ - 6654.27827064, - 6709.60527925, - 6764.82512152, - 6819.93307864, - 6874.92448983, - 6929.79475262, - 6984.53932320, - 7039.15371664, - 7093.63350719, - 7147.97432845, - 7202.17187363, - 7256.22189565, - 7310.12020737, - 7363.86268165, - 7417.44525154, - 7470.86391025, - 7524.11471135, - 7577.19376869, - 7630.09725652, - 7682.82140944, - 7735.36252240, - 7787.71695067, - 7839.88110979, - 7891.85147549, - 7943.62458362, - 7995.19703005, - 8046.56547051, - 8097.72662050, - 8148.67725513, - 8199.41420894, - 8249.93437573, - 8300.23470836, - 8350.31221854, - 8400.16397663, - 8449.78711137, - 8499.17880967, - 8548.33631631, - 8597.25693372, - 8645.93802164, - 8694.37699689, - 8742.57133299, - 8790.51855994, - 8838.21626380, - 8885.66208644, - 8932.85372514, - 8979.78893227, - 9026.46551490, - 9072.88133449, - 9119.03430644, - 9164.92239976, - 9210.54363669, - 9255.89609224, - 9300.97789384, - 9345.78722093, - 9390.32230452, - 9434.58142677, - 9478.56292060, - 9522.26516922, - 9565.68660570, - 9608.82571256, - 9651.68102126, - 9694.25111185, - 9736.53461241, - 9778.53019868, - 9820.23659354, - 9861.65256660, - 9902.77693369, - 9943.60855641, - 9984.14634166, - 10024.38924118, - 10064.33625106, - 10103.98641125, - 10143.33880512, - 10182.39255895, - 10221.14684147, - 10259.60086337, - 10297.75387680, - 10335.60517492, - 10373.15409140, - 10410.39999994, - 10447.34231378, - 10483.98048522, - 10520.31400514, - 10556.34240253, - 10592.06524395, - 10627.48213314, - 10662.59271046, - 10697.39665243, - 10731.89367128, - 10766.08351444, - 10799.96596406, - 10833.54083657, - 10866.80798215, - 10899.76728432, - 10932.41865941, - 10964.76205614, - 10996.79745511, - 11028.52486836, - 11059.94433889, - 11091.05594022, - 11121.85977591, - 11152.35597912, - 11182.54471212, - 11212.42616589, - 11242.00055963, - 11271.26814031, - 11300.22918226, - 11328.88398670, - 11357.23288130, - 11385.27621977, - 11413.01438137, - 11440.44777057, - 11467.57681651, - 11494.40197267, - 11520.92371641, - 11547.14254852, - 11573.05899287, - 11598.67359596, - 11623.98692649, - 11648.99957501, - 11673.71215347, - 11698.12529482, - 11722.23965267, - 11746.05590082, - 11769.57473294, - 11792.79686212, - 11815.72302055, - 11838.35395910, - 11860.69044694, - 11882.73327121, - 11904.48323661, - 11925.94116506, - 11947.10789532, - 11967.98428265, - 11988.57119843, - 12008.86952986, - 12028.88017954, - 12048.60406519, - 12068.04211929, - 12087.19528873, - 12106.06453447, - 12124.65083127, - 12142.95516728, - 12160.97854379, - 12178.72197486, - 12196.18648704, - 12213.37311905, - 12230.28292145, - 12246.91695637, - 12263.27629719, - 12279.36202824, - 12295.17524453, - 12310.71705141, - 12325.98856435, - 12340.99090860, - 12355.72521896, - 12370.19263945, - 12384.39432308, - 12398.33143156, - 12412.00513505, - 12425.41661189, - 12438.56704832, - 12451.45763827, - 12464.08958306, - 12476.46409119, - 12488.58237807, - 12500.44566579, - 12512.05518287, - 12523.41216403, - 12534.51784999, - 12545.37348716, - 12555.98032751, - 12566.33962828, - 12576.45265178, - 12586.32066519, - 12595.94494032, - 12605.32675343, - 12614.46738498, - 12623.36811947, - 12632.03024523, - 12640.45505419, - 12648.64384173, - 12656.59790644, - 12664.31854999, - 12671.80707689, - 12679.06479434, - 12686.09301201, - 12692.89304194, - 12699.46619826, - 12705.81379711, - 12711.93715643, - 12717.83759579, - 12723.51643624, - 12728.97500013, - 12734.21461098, - 12739.23659331, - 12744.04227248, - 12748.63297453, - 12753.01002609, - 12757.17475416, - 12761.12848599, - 12764.87254898, - 12768.40827049, - 12771.73697774, - 12774.85999765, - 12777.77865673, - 12780.49428093, - 12783.00819555, - 12785.32172507, - 12787.43619307, - 12789.35292209, - 12791.07323350, - 12792.59844743, - 12793.92988261, - 12795.06885627, - 12796.01668406, - 12796.77467992, - 12797.34415597, - 12797.72642243, - 12797.92278749, - 12797.93455726, - 12797.76303562, - 12797.40952415, - 12796.87532205, - 12796.16172604, - 12795.27003026, - 12794.20152618, - 12792.95750257, - 12791.53924533, - 12789.94803749, - 12788.18515908, - 12786.25188706, - 12784.14949528, - 12781.87925435, - 12779.44243162, - 12776.84029106, - 12774.07409325, - 12771.14509526, - 12768.05455060, - 12764.80370917, - 12761.39381718, - 12757.82611712, - 12754.10184764, - 12750.22224355, - 12746.18853574, - 12742.00195113, - 12737.66371261, - 12733.17503899, - 12728.53714494, - 12723.75124098, - 12718.81853337, - 12713.74022411, - 12708.51751090, - 12703.15158703, - 12697.64364143, - 12691.99485855, - 12686.20641836, - 12680.27949632, - 12674.21526329, - 12668.01488555, - 12661.67952475, - 12655.21033783, - 12648.60847706, - 12641.87508995, - 12635.01131925, - 12628.01830291, - 12620.89717405, - 12613.64906092, - 12606.27508691, - 12598.77637047, - 12591.15402515, - 12583.40915951, - 12575.54287714, - 12567.55627664, - 12559.45045156, - 12551.22649042, - 12542.88547667, - 12534.42848868, - 12525.85659972, - 12517.17087794, - 12508.37238636, - 12499.46218286, - 12490.44132013, - 12481.31084572, - 12472.07180197, - 12462.72522603, - 12453.27214983, - 12443.71360008, - 12434.05059828, - 12424.28416067, - 12414.41529824, - 12404.44501674, - 12394.37431665, - 12384.20419318, - 12373.93563628, - 12363.56963061, - 12353.10715554, - 12342.54918519, - 12331.89668836, - 12321.15062856, - 12310.31196403, - 12299.38164769, - 12288.36062720, - 12277.24984489, - 12266.05023782, - 12254.76273776, - 12243.38827118, - 12231.92775927, - 12220.38211792, - 12208.75225776, - 12197.03908412, - 12185.24349708, - 12173.36639143, - 12161.40865671, - 12149.37117718, - 12137.25483188, - 12125.06049458, - 12112.78903381, - 12100.44131289, - 12088.01818988, - 12075.52051766, - 12062.94914387, - 12050.30491098, - 12037.58865624, - 12024.80121175, - 12011.94340442, - 11999.01605600, - 11986.01998309, - 11972.95599717, - 11959.82490456, - 11946.62750648, - 11933.36459906, - 11920.03697332, - 11906.64541520, - 11893.19070557, - 11879.67362026, - 11866.09493006, - 11852.45540071, - 11838.75579295, - 11824.99686253, - 11811.17936021, - 11797.30403177, - 11783.37161803, - 11769.38285489, - 11755.33847331, - 11741.23919934, - 11727.08575414, - 11712.87885400, - 11698.61921032, - 11684.30752968, - 11669.94451382, - 11655.53085966, - 11641.06725934, - 11626.55440020, - 11611.99296484, - 11597.38363109, - 11582.72707208, - 11568.02395621, - 11553.27494718, - 11538.48070404, - 11523.64188117, - 11508.75912831, - 11493.83309058, - 11478.86440851, - 11463.85371803, - 11448.80165051, - 11433.70883278, - 11418.57588715, - 11403.40343140, - 11388.19207883, - 11372.94243828, - 11357.65511413, - 11342.33070633, - 11326.96981042, - 11311.57301754, - 11296.14091447, - 11280.67408363, - 11265.17310311, - 11249.63854667, - 11234.07098379, - 11218.47097968, - 11202.83909529, - 11187.17588733, - 11171.48190831, - 11155.75770652, - 11140.00382611, - 11124.22080706, - 11108.40918520, - 11092.56949227, - 11076.70225590, - 11060.80799967, - 11044.88724309, - 11028.94050163, - 11012.96828677, - 10996.97110598, - 10980.94946277, - 10964.90385669, - 10948.83478337, - 10932.74273452, - 10916.62819797, - 10900.49165767, - 10884.33359373, - 10868.15448243, - 10851.95479625, - 10835.73500386, - 10819.49557018, - 10803.23695638, - 10786.95961991, - 10770.66401450, - 10754.35059021, - 10738.01979343, - 10721.67206689, - 10705.30784972, - 10688.92757743, - 10672.53168195, - 10656.12059164, - 10639.69473134, - 10623.25452233, - 10606.80038243, - 10590.33272593, - 10573.85196369, - 10557.35850312, - 10540.85274820, - 10524.33509952, - 10507.80595427, - 10491.26570629, - 10474.71474607, - 10458.15346079, - 10441.58223429, - 10425.00144718, - 10408.41147676, - 10391.81269711, - 10375.20547907, - 10358.59019029, - 10341.96719523, - 10325.33685516, - 10308.69952825, - 10292.05556949, - 10275.40533080, - 10258.74916099, - 10242.08740581, - 10225.42040795, - 10208.74850708, - 10192.07203984, - 10175.39133990, - 10158.70673793, - 10142.01856166, - 10125.32713586, - 10108.63278242, - 10091.93582030, - 10075.23656557, - 10058.53533147, - 10041.83242836, - 10025.12816380, - 10008.42284253, - 9991.71676650, - 9975.01023489, - 9958.30354412, - 9941.59698789, - 9924.89085715, - 9908.18544019, - 9891.48102260, - 9874.77788729, - 9858.07631455, - 9841.37658201, - 9824.67896473, - 9807.98373514, - 9791.29116311, - 9774.60151594, - 9757.91505839, - 9741.23205271, -]) +DATA_BLACKBODY: NDArray = np.array( + [ + 6654.27827064, + 6709.60527925, + 6764.82512152, + 6819.93307864, + 6874.92448983, + 6929.79475262, + 6984.53932320, + 7039.15371664, + 7093.63350719, + 7147.97432845, + 7202.17187363, + 7256.22189565, + 7310.12020737, + 7363.86268165, + 7417.44525154, + 7470.86391025, + 7524.11471135, + 7577.19376869, + 7630.09725652, + 7682.82140944, + 7735.36252240, + 7787.71695067, + 7839.88110979, + 7891.85147549, + 7943.62458362, + 7995.19703005, + 8046.56547051, + 8097.72662050, + 8148.67725513, + 8199.41420894, + 8249.93437573, + 8300.23470836, + 8350.31221854, + 8400.16397663, + 8449.78711137, + 8499.17880967, + 8548.33631631, + 8597.25693372, + 8645.93802164, + 8694.37699689, + 8742.57133299, + 8790.51855994, + 8838.21626380, + 8885.66208644, + 8932.85372514, + 8979.78893227, + 9026.46551490, + 9072.88133449, + 9119.03430644, + 9164.92239976, + 9210.54363669, + 9255.89609224, + 9300.97789384, + 9345.78722093, + 9390.32230452, + 9434.58142677, + 9478.56292060, + 9522.26516922, + 9565.68660570, + 9608.82571256, + 9651.68102126, + 9694.25111185, + 9736.53461241, + 9778.53019868, + 9820.23659354, + 9861.65256660, + 9902.77693369, + 9943.60855641, + 9984.14634166, + 10024.38924118, + 10064.33625106, + 10103.98641125, + 10143.33880512, + 10182.39255895, + 10221.14684147, + 10259.60086337, + 10297.75387680, + 10335.60517492, + 10373.15409140, + 10410.39999994, + 10447.34231378, + 10483.98048522, + 10520.31400514, + 10556.34240253, + 10592.06524395, + 10627.48213314, + 10662.59271046, + 10697.39665243, + 10731.89367128, + 10766.08351444, + 10799.96596406, + 10833.54083657, + 10866.80798215, + 10899.76728432, + 10932.41865941, + 10964.76205614, + 10996.79745511, + 11028.52486836, + 11059.94433889, + 11091.05594022, + 11121.85977591, + 11152.35597912, + 11182.54471212, + 11212.42616589, + 11242.00055963, + 11271.26814031, + 11300.22918226, + 11328.88398670, + 11357.23288130, + 11385.27621977, + 11413.01438137, + 11440.44777057, + 11467.57681651, + 11494.40197267, + 11520.92371641, + 11547.14254852, + 11573.05899287, + 11598.67359596, + 11623.98692649, + 11648.99957501, + 11673.71215347, + 11698.12529482, + 11722.23965267, + 11746.05590082, + 11769.57473294, + 11792.79686212, + 11815.72302055, + 11838.35395910, + 11860.69044694, + 11882.73327121, + 11904.48323661, + 11925.94116506, + 11947.10789532, + 11967.98428265, + 11988.57119843, + 12008.86952986, + 12028.88017954, + 12048.60406519, + 12068.04211929, + 12087.19528873, + 12106.06453447, + 12124.65083127, + 12142.95516728, + 12160.97854379, + 12178.72197486, + 12196.18648704, + 12213.37311905, + 12230.28292145, + 12246.91695637, + 12263.27629719, + 12279.36202824, + 12295.17524453, + 12310.71705141, + 12325.98856435, + 12340.99090860, + 12355.72521896, + 12370.19263945, + 12384.39432308, + 12398.33143156, + 12412.00513505, + 12425.41661189, + 12438.56704832, + 12451.45763827, + 12464.08958306, + 12476.46409119, + 12488.58237807, + 12500.44566579, + 12512.05518287, + 12523.41216403, + 12534.51784999, + 12545.37348716, + 12555.98032751, + 12566.33962828, + 12576.45265178, + 12586.32066519, + 12595.94494032, + 12605.32675343, + 12614.46738498, + 12623.36811947, + 12632.03024523, + 12640.45505419, + 12648.64384173, + 12656.59790644, + 12664.31854999, + 12671.80707689, + 12679.06479434, + 12686.09301201, + 12692.89304194, + 12699.46619826, + 12705.81379711, + 12711.93715643, + 12717.83759579, + 12723.51643624, + 12728.97500013, + 12734.21461098, + 12739.23659331, + 12744.04227248, + 12748.63297453, + 12753.01002609, + 12757.17475416, + 12761.12848599, + 12764.87254898, + 12768.40827049, + 12771.73697774, + 12774.85999765, + 12777.77865673, + 12780.49428093, + 12783.00819555, + 12785.32172507, + 12787.43619307, + 12789.35292209, + 12791.07323350, + 12792.59844743, + 12793.92988261, + 12795.06885627, + 12796.01668406, + 12796.77467992, + 12797.34415597, + 12797.72642243, + 12797.92278749, + 12797.93455726, + 12797.76303562, + 12797.40952415, + 12796.87532205, + 12796.16172604, + 12795.27003026, + 12794.20152618, + 12792.95750257, + 12791.53924533, + 12789.94803749, + 12788.18515908, + 12786.25188706, + 12784.14949528, + 12781.87925435, + 12779.44243162, + 12776.84029106, + 12774.07409325, + 12771.14509526, + 12768.05455060, + 12764.80370917, + 12761.39381718, + 12757.82611712, + 12754.10184764, + 12750.22224355, + 12746.18853574, + 12742.00195113, + 12737.66371261, + 12733.17503899, + 12728.53714494, + 12723.75124098, + 12718.81853337, + 12713.74022411, + 12708.51751090, + 12703.15158703, + 12697.64364143, + 12691.99485855, + 12686.20641836, + 12680.27949632, + 12674.21526329, + 12668.01488555, + 12661.67952475, + 12655.21033783, + 12648.60847706, + 12641.87508995, + 12635.01131925, + 12628.01830291, + 12620.89717405, + 12613.64906092, + 12606.27508691, + 12598.77637047, + 12591.15402515, + 12583.40915951, + 12575.54287714, + 12567.55627664, + 12559.45045156, + 12551.22649042, + 12542.88547667, + 12534.42848868, + 12525.85659972, + 12517.17087794, + 12508.37238636, + 12499.46218286, + 12490.44132013, + 12481.31084572, + 12472.07180197, + 12462.72522603, + 12453.27214983, + 12443.71360008, + 12434.05059828, + 12424.28416067, + 12414.41529824, + 12404.44501674, + 12394.37431665, + 12384.20419318, + 12373.93563628, + 12363.56963061, + 12353.10715554, + 12342.54918519, + 12331.89668836, + 12321.15062856, + 12310.31196403, + 12299.38164769, + 12288.36062720, + 12277.24984489, + 12266.05023782, + 12254.76273776, + 12243.38827118, + 12231.92775927, + 12220.38211792, + 12208.75225776, + 12197.03908412, + 12185.24349708, + 12173.36639143, + 12161.40865671, + 12149.37117718, + 12137.25483188, + 12125.06049458, + 12112.78903381, + 12100.44131289, + 12088.01818988, + 12075.52051766, + 12062.94914387, + 12050.30491098, + 12037.58865624, + 12024.80121175, + 12011.94340442, + 11999.01605600, + 11986.01998309, + 11972.95599717, + 11959.82490456, + 11946.62750648, + 11933.36459906, + 11920.03697332, + 11906.64541520, + 11893.19070557, + 11879.67362026, + 11866.09493006, + 11852.45540071, + 11838.75579295, + 11824.99686253, + 11811.17936021, + 11797.30403177, + 11783.37161803, + 11769.38285489, + 11755.33847331, + 11741.23919934, + 11727.08575414, + 11712.87885400, + 11698.61921032, + 11684.30752968, + 11669.94451382, + 11655.53085966, + 11641.06725934, + 11626.55440020, + 11611.99296484, + 11597.38363109, + 11582.72707208, + 11568.02395621, + 11553.27494718, + 11538.48070404, + 11523.64188117, + 11508.75912831, + 11493.83309058, + 11478.86440851, + 11463.85371803, + 11448.80165051, + 11433.70883278, + 11418.57588715, + 11403.40343140, + 11388.19207883, + 11372.94243828, + 11357.65511413, + 11342.33070633, + 11326.96981042, + 11311.57301754, + 11296.14091447, + 11280.67408363, + 11265.17310311, + 11249.63854667, + 11234.07098379, + 11218.47097968, + 11202.83909529, + 11187.17588733, + 11171.48190831, + 11155.75770652, + 11140.00382611, + 11124.22080706, + 11108.40918520, + 11092.56949227, + 11076.70225590, + 11060.80799967, + 11044.88724309, + 11028.94050163, + 11012.96828677, + 10996.97110598, + 10980.94946277, + 10964.90385669, + 10948.83478337, + 10932.74273452, + 10916.62819797, + 10900.49165767, + 10884.33359373, + 10868.15448243, + 10851.95479625, + 10835.73500386, + 10819.49557018, + 10803.23695638, + 10786.95961991, + 10770.66401450, + 10754.35059021, + 10738.01979343, + 10721.67206689, + 10705.30784972, + 10688.92757743, + 10672.53168195, + 10656.12059164, + 10639.69473134, + 10623.25452233, + 10606.80038243, + 10590.33272593, + 10573.85196369, + 10557.35850312, + 10540.85274820, + 10524.33509952, + 10507.80595427, + 10491.26570629, + 10474.71474607, + 10458.15346079, + 10441.58223429, + 10425.00144718, + 10408.41147676, + 10391.81269711, + 10375.20547907, + 10358.59019029, + 10341.96719523, + 10325.33685516, + 10308.69952825, + 10292.05556949, + 10275.40533080, + 10258.74916099, + 10242.08740581, + 10225.42040795, + 10208.74850708, + 10192.07203984, + 10175.39133990, + 10158.70673793, + 10142.01856166, + 10125.32713586, + 10108.63278242, + 10091.93582030, + 10075.23656557, + 10058.53533147, + 10041.83242836, + 10025.12816380, + 10008.42284253, + 9991.71676650, + 9975.01023489, + 9958.30354412, + 9941.59698789, + 9924.89085715, + 9908.18544019, + 9891.48102260, + 9874.77788729, + 9858.07631455, + 9841.37658201, + 9824.67896473, + 9807.98373514, + 9791.29116311, + 9774.60151594, + 9757.91505839, + 9741.23205271, + ] +) class TestPlanckLaw(unittest.TestCase): """ - Defines :func:`colour.colorimetry.blackbody.planck_law` definition units + Define :func:`colour.colorimetry.blackbody.planck_law` definition unit tests methods. """ def test_planck_law(self): - """ - Tests :func:`colour.colorimetry.blackbody.planck_law` definition. - """ + """Test :func:`colour.colorimetry.blackbody.planck_law` definition.""" for temperature, wavelengths in sorted(DATA_PLANCK_LAW.items()): for wavelength, radiance in sorted(wavelengths.items()): @@ -4523,11 +4524,12 @@ def test_planck_law(self): radiance, rtol=0.0000001, atol=0.0000001, - verbose=False) + verbose=False, + ) def test_n_dimensional_planck_law(self): """ - Tests :func:`colour.colorimetry.blackbody.planck_law` definition + Test :func:`colour.colorimetry.blackbody.planck_law` definition n-dimensional arrays support. """ @@ -4549,7 +4551,7 @@ def test_n_dimensional_planck_law(self): @ignore_numpy_errors def test_nan_planck_law(self): """ - Tests :func:`colour.colorimetry.blackbody.planck_law` definition + Test :func:`colour.colorimetry.blackbody.planck_law` definition nan support. """ @@ -4561,21 +4563,20 @@ def test_nan_planck_law(self): class TestSdBlackbody(unittest.TestCase): """ - Defines :func:`colour.colorimetry.blackbody.sd_blackbody` definition unit + Define :func:`colour.colorimetry.blackbody.sd_blackbody` definition unit tests methods. """ def test_sd_blackbody(self): - """ - Tests :func:`colour.colorimetry.blackbody.sd_blackbody` definition. - """ + """Test :func:`colour.colorimetry.blackbody.sd_blackbody` definition.""" np.testing.assert_allclose( sd_blackbody(5000, SpectralShape(360, 830, 1)).values, DATA_BLACKBODY, rtol=0.0000001, - atol=0.0000001) + atol=0.0000001, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/colorimetry/tests/test_correction.py b/colour/colorimetry/tests/test_correction.py index 791f000db7..78dbcfd00c 100644 --- a/colour/colorimetry/tests/test_correction.py +++ b/colour/colorimetry/tests/test_correction.py @@ -1,48 +1,75 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.colorimetry.correction` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.colorimetry.correction` module.""" import numpy as np import unittest -from colour.colorimetry import (SpectralDistribution, - bandpass_correction_Stearns1988) +from colour.colorimetry import ( + SpectralDistribution, + bandpass_correction_Stearns1988, +) +from colour.hints import Tuple -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'DATA_NON_BANDPASS_CORRECTED', 'DATA_BANDPASS_CORRECTED', - 'TestBandpassCorrectionStearns1988' + "DATA_NON_BANDPASS_CORRECTED", + "DATA_BANDPASS_CORRECTED", + "TestBandpassCorrectionStearns1988", ] -DATA_NON_BANDPASS_CORRECTED = (9.3700, 12.3200, 12.4600, 9.5100, 5.9200, - 4.3300, 4.2900, 3.8800, 4.5100, 10.9200, - 27.5000, 49.6700, 69.5900, 81.7300, 88.1900, - 86.0500) +DATA_NON_BANDPASS_CORRECTED: Tuple = ( + 9.3700, + 12.3200, + 12.4600, + 9.5100, + 5.9200, + 4.3300, + 4.2900, + 3.8800, + 4.5100, + 10.9200, + 27.5000, + 49.6700, + 69.5900, + 81.7300, + 88.1900, + 86.0500, +) -DATA_BANDPASS_CORRECTED = (9.12515000, 12.57355255, 12.69542514, 9.54357971, - 5.75121288, 4.21535933, 4.33022518, 3.79034131, - 4.03770167, 10.11509076, 27.10283747, 49.88971449, - 70.21750370, 82.14935719, 88.88373581, 85.87238000) +DATA_BANDPASS_CORRECTED: Tuple = ( + 9.12515000, + 12.57355255, + 12.69542514, + 9.54357971, + 5.75121288, + 4.21535933, + 4.33022518, + 3.79034131, + 4.03770167, + 10.11509076, + 27.10283747, + 49.88971449, + 70.21750370, + 82.14935719, + 88.88373581, + 85.87238000, +) class TestBandpassCorrectionStearns1988(unittest.TestCase): """ - Defines :func:`colour.colorimetry.correction.\ + Define :func:`colour.colorimetry.correction.\ bandpass_correction_Stearns1988` definition unit tests methods. """ def test_bandpass_correction_Stearns1988(self): """ - Tests :func:`colour.colorimetry.correction.\ + Test :func:`colour.colorimetry.correction.\ bandpass_correction_Stearns1988` definition. """ @@ -50,12 +77,15 @@ def test_bandpass_correction_Stearns1988(self): dict( zip( range(len(DATA_NON_BANDPASS_CORRECTED)), - DATA_NON_BANDPASS_CORRECTED))) + DATA_NON_BANDPASS_CORRECTED, + ) + ) + ) np.testing.assert_almost_equal( - bandpass_correction_Stearns1988(sd).values, - DATA_BANDPASS_CORRECTED) + bandpass_correction_Stearns1988(sd).values, DATA_BANDPASS_CORRECTED + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/colorimetry/tests/test_dominant.py b/colour/colorimetry/tests/test_dominant.py index 8543e46606..7ecc79168b 100644 --- a/colour/colorimetry/tests/test_dominant.py +++ b/colour/colorimetry/tests/test_dominant.py @@ -1,82 +1,87 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.colorimetry.dominant` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.colorimetry.dominant` module.""" import numpy as np import unittest from itertools import permutations -from colour.colorimetry import (MSDS_CMFS, CCS_ILLUMINANTS, - dominant_wavelength, complementary_wavelength, - excitation_purity, colorimetric_purity) -from colour.colorimetry.dominant import (closest_spectral_locus_wavelength) +from colour.colorimetry import ( + MSDS_CMFS, + CCS_ILLUMINANTS, + dominant_wavelength, + complementary_wavelength, + excitation_purity, + colorimetric_purity, +) +from colour.colorimetry.dominant import ( + closest_spectral_locus_wavelength, +) from colour.models import XYZ_to_xy from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931', 'CCS_D65', - 'TestClosestSpectralLocusWavelength', 'TestDominantWavelength', - 'TestComplementaryWavelength', 'TestExcitationPurity', - 'TestColorimetricPurity' + "TestClosestSpectralLocusWavelength", + "TestDominantWavelength", + "TestComplementaryWavelength", + "TestExcitationPurity", + "TestColorimetricPurity", ] -CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931 = MSDS_CMFS[ - 'CIE 1931 2 Degree Standard Observer'] -CCS_D65 = CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65'] - class TestClosestSpectralLocusWavelength(unittest.TestCase): """ - Defines :func:`colour.colorimetry.dominant.\ -closest_spectral_locus_wavelength` definition units tests methods. + Define :func:`colour.colorimetry.dominant.\ +closest_spectral_locus_wavelength` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" + + self._xy_s = XYZ_to_xy( + MSDS_CMFS["CIE 1931 2 Degree Standard Observer"].values + ) - self._xy_s = XYZ_to_xy(CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931.values) + self._xy_D65 = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][ + "D65" + ] def test_closest_spectral_locus_wavelength(self): """ - Tests :func:`colour.colorimetry.dominant.\ + Test :func:`colour.colorimetry.dominant.\ closest_spectral_locus_wavelength` definition. """ xy = np.array([0.54369557, 0.32107944]) - xy_n = CCS_D65 + xy_n = self._xy_D65 i_wl, xy_wl = closest_spectral_locus_wavelength(xy, xy_n, self._xy_s) self.assertEqual(i_wl, np.array(256)) np.testing.assert_almost_equal( - xy_wl, np.array([0.68354746, 0.31628409]), decimal=7) + xy_wl, np.array([0.68354746, 0.31628409]), decimal=7 + ) xy = np.array([0.37605506, 0.24452225]) i_wl, xy_wl = closest_spectral_locus_wavelength(xy, xy_n, self._xy_s) self.assertEqual(i_wl, np.array(248)) np.testing.assert_almost_equal( - xy_wl, np.array([0.45723147, 0.13628148]), decimal=7) + xy_wl, np.array([0.45723147, 0.13628148]), decimal=7 + ) def test_n_dimensional_closest_spectral_locus_wavelength(self): """ - Tests :func:`colour.colorimetry.dominant.\ + Test :func:`colour.colorimetry.dominant.\ closest_spectral_locus_wavelength` definition n-dimensional arrays support. """ xy = np.array([0.54369557, 0.32107944]) - xy_n = CCS_D65 + xy_n = self._xy_D65 i_wl, xy_wl = closest_spectral_locus_wavelength(xy, xy_n, self._xy_s) i_wl_r, xy_wl_r = np.array(256), np.array([0.68354746, 0.31628409]) np.testing.assert_almost_equal(i_wl, i_wl_r) @@ -101,7 +106,7 @@ def test_n_dimensional_closest_spectral_locus_wavelength(self): @ignore_numpy_errors def test_nan_closest_spectral_locus_wavelength(self): """ - Tests :func:`colour.colorimetry.dominant.\ + Test :func:`colour.colorimetry.dominant.\ closest_spectral_locus_wavelength` definition nan support. """ @@ -116,58 +121,67 @@ def test_nan_closest_spectral_locus_wavelength(self): class TestDominantWavelength(unittest.TestCase): """ - Defines :func:`colour.colorimetry.dominant.dominant_wavelength` definition - units tests methods. + Define :func:`colour.colorimetry.dominant.dominant_wavelength` definition + unit tests methods. """ + def setUp(self): + """Initialise the common tests attributes.""" + + self._xy_D65 = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][ + "D65" + ] + def test_dominant_wavelength(self): """ - Tests :func:`colour.colorimetry.dominant.dominant_wavelength` + Test :func:`colour.colorimetry.dominant.dominant_wavelength` definition. """ xy = np.array([0.54369557, 0.32107944]) - xy_n = CCS_D65 - wl, xy_wl, xy_cwl = dominant_wavelength( - xy, xy_n, CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931) + xy_n = self._xy_D65 + wl, xy_wl, xy_cwl = dominant_wavelength(xy, xy_n) self.assertEqual(wl, np.array(616.0)) np.testing.assert_almost_equal( - xy_wl, np.array([0.68354746, 0.31628409]), decimal=7) + xy_wl, np.array([0.68354746, 0.31628409]), decimal=7 + ) np.testing.assert_almost_equal( - xy_cwl, np.array([0.68354746, 0.31628409]), decimal=7) + xy_cwl, np.array([0.68354746, 0.31628409]), decimal=7 + ) xy = np.array([0.37605506, 0.24452225]) - i_wl, xy_wl, xy_cwl = dominant_wavelength( - xy, xy_n, CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931) + i_wl, xy_wl, xy_cwl = dominant_wavelength(xy, xy_n) self.assertEqual(i_wl, np.array(-509.0)) np.testing.assert_almost_equal( - xy_wl, np.array([0.45723147, 0.13628148]), decimal=7) + xy_wl, np.array([0.45723147, 0.13628148]), decimal=7 + ) np.testing.assert_almost_equal( - xy_cwl, np.array([0.01040962, 0.73207453]), decimal=7) + xy_cwl, np.array([0.01040962, 0.73207453]), decimal=7 + ) def test_n_dimensional_dominant_wavelength(self): """ - Tests :func:`colour.colorimetry.dominant.dominant_wavelength` + Test :func:`colour.colorimetry.dominant.dominant_wavelength` definition n-dimensional arrays support. """ xy = np.array([0.54369557, 0.32107944]) - xy_n = CCS_D65 - wl, xy_wl, xy_cwl = dominant_wavelength( - xy, xy_n, CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931) - wl_r, xy_wl_r, xy_cwl_r = (np.array(616.0), - np.array([0.68354746, 0.31628409]), - np.array([0.68354746, 0.31628409])) + xy_n = self._xy_D65 + wl, xy_wl, xy_cwl = dominant_wavelength(xy, xy_n) + wl_r, xy_wl_r, xy_cwl_r = ( + np.array(616.0), + np.array([0.68354746, 0.31628409]), + np.array([0.68354746, 0.31628409]), + ) np.testing.assert_almost_equal(wl, wl_r) np.testing.assert_almost_equal(xy_wl, xy_wl_r, decimal=7) np.testing.assert_almost_equal(xy_cwl, xy_cwl_r, decimal=7) xy = np.tile(xy, (6, 1)) xy_n = np.tile(xy_n, (6, 1)) - wl, xy_wl, xy_cwl = dominant_wavelength( - xy, xy_n, CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931) + wl, xy_wl, xy_cwl = dominant_wavelength(xy, xy_n) wl_r = np.tile(wl_r, 6) xy_wl_r = np.tile(xy_wl_r, (6, 1)) xy_cwl_r = np.tile(xy_cwl_r, (6, 1)) @@ -177,8 +191,7 @@ def test_n_dimensional_dominant_wavelength(self): xy = np.reshape(xy, (2, 3, 2)) xy_n = np.reshape(xy_n, (2, 3, 2)) - wl, xy_wl, xy_cwl = dominant_wavelength( - xy, xy_n, CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931) + wl, xy_wl, xy_cwl = dominant_wavelength(xy, xy_n) wl_r = np.reshape(wl_r, (2, 3)) xy_wl_r = np.reshape(xy_wl_r, (2, 3, 2)) xy_cwl_r = np.reshape(xy_cwl_r, (2, 3, 2)) @@ -189,7 +202,7 @@ def test_n_dimensional_dominant_wavelength(self): @ignore_numpy_errors def test_nan_dominant_wavelength(self): """ - Tests :func:`colour.colorimetry.dominant.dominant_wavelength` + Test :func:`colour.colorimetry.dominant.dominant_wavelength` definition nan support. """ @@ -197,66 +210,74 @@ def test_nan_dominant_wavelength(self): cases = set(permutations(cases * 3, r=2)) for case in cases: try: - dominant_wavelength(case, case, - CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931) + dominant_wavelength(case, case) except ValueError: pass class TestComplementaryWavelength(unittest.TestCase): """ - Defines :func:`colour.colorimetry.dominant.complementary_wavelength` - definition units tests methods. + Define :func:`colour.colorimetry.dominant.complementary_wavelength` + definition unit tests methods. """ + def setUp(self): + """Initialise the common tests attributes.""" + + self._xy_D65 = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][ + "D65" + ] + def test_complementary_wavelength(self): """ - Tests :func:`colour.colorimetry.dominant.complementary_wavelength` + Test :func:`colour.colorimetry.dominant.complementary_wavelength` definition. """ xy = np.array([0.54369557, 0.32107944]) - xy_n = CCS_D65 - wl, xy_wl, xy_cwl = complementary_wavelength( - xy, xy_n, CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931) + xy_n = self._xy_D65 + wl, xy_wl, xy_cwl = complementary_wavelength(xy, xy_n) self.assertEqual(wl, np.array(492.0)) np.testing.assert_almost_equal( - xy_wl, np.array([0.03647950, 0.33847127]), decimal=7) + xy_wl, np.array([0.03647950, 0.33847127]), decimal=7 + ) np.testing.assert_almost_equal( - xy_cwl, np.array([0.03647950, 0.33847127]), decimal=7) + xy_cwl, np.array([0.03647950, 0.33847127]), decimal=7 + ) xy = np.array([0.37605506, 0.24452225]) - i_wl, xy_wl, xy_cwl = complementary_wavelength( - xy, xy_n, CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931) + i_wl, xy_wl, xy_cwl = complementary_wavelength(xy, xy_n) self.assertEqual(i_wl, np.array(509.0)) np.testing.assert_almost_equal( - xy_wl, np.array([0.01040962, 0.73207453]), decimal=7) + xy_wl, np.array([0.01040962, 0.73207453]), decimal=7 + ) np.testing.assert_almost_equal( - xy_cwl, np.array([0.01040962, 0.73207453]), decimal=7) + xy_cwl, np.array([0.01040962, 0.73207453]), decimal=7 + ) def test_n_dimensional_complementary_wavelength(self): """ - Tests :func:`colour.colorimetry.dominant.complementary_wavelength` + Test :func:`colour.colorimetry.dominant.complementary_wavelength` definition n-dimensional arrays support. """ xy = np.array([0.54369557, 0.32107944]) - xy_n = CCS_D65 - wl, xy_wl, xy_cwl = complementary_wavelength( - xy, xy_n, CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931) - wl_r, xy_wl_r, xy_cwl_r = (np.array(492.0), - np.array([0.03647950, 0.33847127]), - np.array([0.03647950, 0.33847127])) + xy_n = self._xy_D65 + wl, xy_wl, xy_cwl = complementary_wavelength(xy, xy_n) + wl_r, xy_wl_r, xy_cwl_r = ( + np.array(492.0), + np.array([0.03647950, 0.33847127]), + np.array([0.03647950, 0.33847127]), + ) np.testing.assert_almost_equal(wl, wl_r) np.testing.assert_almost_equal(xy_wl, xy_wl_r, decimal=7) np.testing.assert_almost_equal(xy_cwl, xy_cwl_r, decimal=7) xy = np.tile(xy, (6, 1)) xy_n = np.tile(xy_n, (6, 1)) - wl, xy_wl, xy_cwl = complementary_wavelength( - xy, xy_n, CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931) + wl, xy_wl, xy_cwl = complementary_wavelength(xy, xy_n) wl_r = np.tile(wl_r, 6) xy_wl_r = np.tile(xy_wl_r, (6, 1)) xy_cwl_r = np.tile(xy_cwl_r, (6, 1)) @@ -266,8 +287,7 @@ def test_n_dimensional_complementary_wavelength(self): xy = np.reshape(xy, (2, 3, 2)) xy_n = np.reshape(xy_n, (2, 3, 2)) - wl, xy_wl, xy_cwl = complementary_wavelength( - xy, xy_n, CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931) + wl, xy_wl, xy_cwl = complementary_wavelength(xy, xy_n) wl_r = np.reshape(wl_r, (2, 3)) xy_wl_r = np.reshape(xy_wl_r, (2, 3, 2)) xy_cwl_r = np.reshape(xy_cwl_r, (2, 3, 2)) @@ -278,7 +298,7 @@ def test_n_dimensional_complementary_wavelength(self): @ignore_numpy_errors def test_nan_complementary_wavelength(self): """ - Tests :func:`colour.colorimetry.dominant.complementary_wavelength` + Test :func:`colour.colorimetry.dominant.complementary_wavelength` definition nan support. """ @@ -286,72 +306,67 @@ def test_nan_complementary_wavelength(self): cases = set(permutations(cases * 3, r=2)) for case in cases: try: - complementary_wavelength( - case, case, CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931) + complementary_wavelength(case, case) except ValueError: pass class TestExcitationPurity(unittest.TestCase): """ - Defines :func:`colour.colorimetry.dominant.excitation_purity` definition - units tests methods. + Define :func:`colour.colorimetry.dominant.excitation_purity` definition + unit tests methods. """ + def setUp(self): + """Initialise the common tests attributes.""" + + self._xy_D65 = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][ + "D65" + ] + def test_excitation_purity(self): - """ - Tests :func:`colour.colorimetry.dominant.excitation_purity` definition. - """ + """Test :func:`colour.colorimetry.dominant.excitation_purity` definition.""" xy = np.array([0.54369557, 0.32107944]) - xy_n = CCS_D65 + xy_n = self._xy_D65 self.assertAlmostEqual( - excitation_purity(xy, xy_n, - CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931), - 0.622885671878446, - places=7) + excitation_purity(xy, xy_n), 0.622885671878446, places=7 + ) xy = np.array([0.37605506, 0.24452225]) self.assertAlmostEqual( - excitation_purity(xy, xy_n, - CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931), - 0.438347859215887, - places=7) + excitation_purity(xy, xy_n), 0.438347859215887, places=7 + ) def test_n_dimensional_excitation_purity(self): """ - Tests :func:`colour.colorimetry.dominant.excitation_purity` definition + Test :func:`colour.colorimetry.dominant.excitation_purity` definition n-dimensional arrays support. """ xy = np.array([0.54369557, 0.32107944]) - xy_n = CCS_D65 - P_e = excitation_purity(xy, xy_n, - CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931) + xy_n = self._xy_D65 + P_e = excitation_purity(xy, xy_n) xy = np.tile(xy, (6, 1)) xy_n = np.tile(xy_n, (6, 1)) P_e = np.tile(P_e, 6) np.testing.assert_almost_equal( - excitation_purity(xy, xy_n, - CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931), - P_e, - decimal=7) + excitation_purity(xy, xy_n), P_e, decimal=7 + ) xy = np.reshape(xy, (2, 3, 2)) xy_n = np.reshape(xy_n, (2, 3, 2)) P_e = np.reshape(P_e, (2, 3)) np.testing.assert_almost_equal( - excitation_purity(xy, xy_n, - CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931), - P_e, - decimal=7) + excitation_purity(xy, xy_n), P_e, decimal=7 + ) @ignore_numpy_errors def test_nan_excitation_purity(self): """ - Tests :func:`colour.colorimetry.dominant.excitation_purity` definition + Test :func:`colour.colorimetry.dominant.excitation_purity` definition nan support. """ @@ -359,73 +374,70 @@ def test_nan_excitation_purity(self): cases = set(permutations(cases * 3, r=2)) for case in cases: try: - excitation_purity(case, case, - CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931) + excitation_purity(case, case) except ValueError: pass class TestColorimetricPurity(unittest.TestCase): """ - Defines :func:`colour.colorimetry.dominant.colorimetric_purity` definition - units tests methods. + Define :func:`colour.colorimetry.dominant.colorimetric_purity` definition + unit tests methods. """ + def setUp(self): + """Initialise the common tests attributes.""" + + self._xy_D65 = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][ + "D65" + ] + def test_colorimetric_purity(self): """ - Tests :func:`colour.colorimetry.dominant.colorimetric_purity` + Test :func:`colour.colorimetry.dominant.colorimetric_purity` definition. """ xy = np.array([0.54369557, 0.32107944]) - xy_n = CCS_D65 + xy_n = self._xy_D65 self.assertAlmostEqual( - colorimetric_purity(xy, xy_n, - CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931), - 0.613582813175483, - places=7) + colorimetric_purity(xy, xy_n), 0.613582813175483, places=7 + ) xy = np.array([0.37605506, 0.24452225]) self.assertAlmostEqual( - colorimetric_purity(xy, xy_n, - CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931), - 0.244307811178847, - places=7) + colorimetric_purity(xy, xy_n), 0.244307811178847, places=7 + ) def test_n_dimensional_colorimetric_purity(self): """ - Tests :func:`colour.colorimetry.dominant.colorimetric_purity` + Test :func:`colour.colorimetry.dominant.colorimetric_purity` definition n-dimensional arrays support. """ xy = np.array([0.54369557, 0.32107944]) - xy_n = CCS_D65 - P_e = colorimetric_purity(xy, xy_n, - CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931) + xy_n = self._xy_D65 + P_e = colorimetric_purity(xy, xy_n) xy = np.tile(xy, (6, 1)) xy_n = np.tile(xy_n, (6, 1)) P_e = np.tile(P_e, 6) np.testing.assert_almost_equal( - colorimetric_purity(xy, xy_n, - CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931), - P_e, - decimal=7) + colorimetric_purity(xy, xy_n), P_e, decimal=7 + ) xy = np.reshape(xy, (2, 3, 2)) xy_n = np.reshape(xy_n, (2, 3, 2)) P_e = np.reshape(P_e, (2, 3)) np.testing.assert_almost_equal( - colorimetric_purity(xy, xy_n, - CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931), - P_e, - decimal=7) + colorimetric_purity(xy, xy_n), P_e, decimal=7 + ) @ignore_numpy_errors def test_nan_colorimetric_purity(self): """ - Tests :func:`colour.colorimetry.dominant.colorimetric_purity` + Test :func:`colour.colorimetry.dominant.colorimetric_purity` definition nan support. """ @@ -433,11 +445,10 @@ def test_nan_colorimetric_purity(self): cases = set(permutations(cases * 3, r=2)) for case in cases: try: - colorimetric_purity(case, case, - CMFS_STANDARD_OBSERVER_2_DEGREE_CIE1931) + colorimetric_purity(case, case) except ValueError: pass -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/colorimetry/tests/test_generation.py b/colour/colorimetry/tests/test_generation.py index be4744195e..5a207a1da3 100644 --- a/colour/colorimetry/tests/test_generation.py +++ b/colour/colorimetry/tests/test_generation.py @@ -1,42 +1,50 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.colorimetry.generation` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.colorimetry.generation` module.""" import numpy as np import unittest from colour.colorimetry.generation import ( - sd_constant, sd_zeros, sd_ones, msds_constant, msds_zeros, msds_ones, - sd_gaussian_normal, sd_gaussian_fwhm, sd_single_led_Ohno2005, - sd_multi_leds_Ohno2005) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + sd_constant, + sd_zeros, + sd_ones, + msds_constant, + msds_zeros, + msds_ones, + sd_gaussian_normal, + sd_gaussian_fwhm, + sd_single_led_Ohno2005, + sd_multi_leds_Ohno2005, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'TestSdConstant', 'TestSdZeros', 'TestSdOnes', 'TestMsdsConstant', - 'TestMsdsZeros', 'TestMsdsOnes', 'TestSdGaussianNormal', - 'TestSdGaussianFwhm', 'TestSdSingleLedOhno2005', 'TestSdMultiLedsOhno2005' + "TestSdConstant", + "TestSdZeros", + "TestSdOnes", + "TestMsdsConstant", + "TestMsdsZeros", + "TestMsdsOnes", + "TestSdGaussianNormal", + "TestSdGaussianFwhm", + "TestSdSingleLedOhno2005", + "TestSdMultiLedsOhno2005", ] class TestSdConstant(unittest.TestCase): """ - Defines :func:`colour.colorimetry.generation.sd_constant` definition unit + Define :func:`colour.colorimetry.generation.sd_constant` definition unit tests methods. """ def test_sd_constant(self): - """ - Tests :func:`colour.colorimetry.generation.sd_constant` definition. - """ + """Test :func:`colour.colorimetry.generation.sd_constant` definition.""" sd = sd_constant(np.pi) @@ -49,13 +57,13 @@ def test_sd_constant(self): class TestSdZeros(unittest.TestCase): """ - Defines :func:`colour.colorimetry.generation.sd_zeros` definition unit + Define :func:`colour.colorimetry.generation.sd_zeros` definition unit tests methods. """ def test_sd_zeros(self): """ - Tests :func:`colour.colorimetry.generation.sd_zeros` + Test :func:`colour.colorimetry.generation.sd_zeros` definition. """ @@ -70,14 +78,12 @@ def test_sd_zeros(self): class TestSdOnes(unittest.TestCase): """ - Defines :func:`colour.colorimetry.generation.sd_ones` definition unit + Define :func:`colour.colorimetry.generation.sd_ones` definition unit tests methods. """ def test_sd_ones(self): - """ - Tests :func:`colour.colorimetry.generation.sd_ones` definition. - """ + """Test :func:`colour.colorimetry.generation.sd_ones` definition.""" sd = sd_ones() @@ -90,40 +96,41 @@ def test_sd_ones(self): class TestMsdsConstant(unittest.TestCase): """ - Defines :func:`colour.colorimetry.generation.msds_constant` definition unit + Define :func:`colour.colorimetry.generation.msds_constant` definition unit tests methods. """ def test_msds_constant(self): - """ - Tests :func:`colour.colorimetry.generation.msds_constant` definition. - """ + """Test :func:`colour.colorimetry.generation.msds_constant` definition.""" - msds = msds_constant(np.pi, labels=['a', 'b', 'c']) + msds = msds_constant(np.pi, labels=["a", "b", "c"]) np.testing.assert_almost_equal( - msds[360], np.array([np.pi, np.pi, np.pi]), decimal=7) + msds[360], np.array([np.pi, np.pi, np.pi]), decimal=7 + ) np.testing.assert_almost_equal( - msds[555], np.array([np.pi, np.pi, np.pi]), decimal=7) + msds[555], np.array([np.pi, np.pi, np.pi]), decimal=7 + ) np.testing.assert_almost_equal( - msds[780], np.array([np.pi, np.pi, np.pi]), decimal=7) + msds[780], np.array([np.pi, np.pi, np.pi]), decimal=7 + ) class TestMsdsZeros(unittest.TestCase): """ - Defines :func:`colour.colorimetry.generation.msds_zeros` definition unit + Define :func:`colour.colorimetry.generation.msds_zeros` definition unit tests methods. """ def test_msds_zeros(self): """ - Tests :func:`colour.colorimetry.generation.msds_zeros` + Test :func:`colour.colorimetry.generation.msds_zeros` definition. """ - msds = msds_zeros(labels=['a', 'b', 'c']) + msds = msds_zeros(labels=["a", "b", "c"]) np.testing.assert_equal(msds[360], np.array([0, 0, 0])) @@ -134,16 +141,14 @@ def test_msds_zeros(self): class TestMsdsOnes(unittest.TestCase): """ - Defines :func:`colour.colorimetry.generation.msds_ones` definition unit + Define :func:`colour.colorimetry.generation.msds_ones` definition unit tests methods. """ def test_msds_ones(self): - """ - Tests :func:`colour.colorimetry.generation.msds_ones` definition. - """ + """Test :func:`colour.colorimetry.generation.msds_ones` definition.""" - msds = msds_ones(labels=['a', 'b', 'c']) + msds = msds_ones(labels=["a", "b", "c"]) np.testing.assert_equal(msds[360], np.array([1, 1, 1])) @@ -154,13 +159,13 @@ def test_msds_ones(self): class TestSdGaussianNormal(unittest.TestCase): """ - Defines :func:`colour.colorimetry.generation.sd_gaussian_normal` + Define :func:`colour.colorimetry.generation.sd_gaussian_normal` definition unit tests methods. """ def test_sd_gaussian_normal(self): """ - Tests :func:`colour.colorimetry.generation.sd_gaussian_normal` + Test :func:`colour.colorimetry.generation.sd_gaussian_normal` definition. """ @@ -175,13 +180,13 @@ def test_sd_gaussian_normal(self): class TestSdGaussianFwhm(unittest.TestCase): """ - Defines :func:`colour.colorimetry.generation.sd_gaussian_fwhm` definition + Define :func:`colour.colorimetry.generation.sd_gaussian_fwhm` definition unit tests methods. """ def test_sd_gaussian_fwhm(self): """ - Tests :func:`colour.colorimetry.generation.sd_gaussian_fwhm` + Test :func:`colour.colorimetry.generation.sd_gaussian_fwhm` definition. """ @@ -196,13 +201,13 @@ def test_sd_gaussian_fwhm(self): class TestSdSingleLedOhno2005(unittest.TestCase): """ - Defines :func:`colour.colorimetry.generation.sd_single_led_Ohno2005` + Define :func:`colour.colorimetry.generation.sd_single_led_Ohno2005` definition unit tests methods. """ def test_sd_single_led_Ohno2005(self): """ - Tests :func:`colour.colorimetry.generation.sd_single_led_Ohno2005` + Test :func:`colour.colorimetry.generation.sd_single_led_Ohno2005` definition. """ @@ -217,13 +222,13 @@ def test_sd_single_led_Ohno2005(self): class TestSdMultiLedsOhno2005(unittest.TestCase): """ - Defines :func:`colour.colorimetry.generation.sd_multi_leds_Ohno2005` + Define :func:`colour.colorimetry.generation.sd_multi_leds_Ohno2005` definition unit tests methods. """ def test_sd_multi_leds_Ohno2005(self): """ - Tests :func:`colour.colorimetry.generation.sd_multi_leds_Ohno2005` + Test :func:`colour.colorimetry.generation.sd_multi_leds_Ohno2005` definition. """ @@ -251,5 +256,5 @@ def test_sd_multi_leds_Ohno2005(self): self.assertAlmostEqual(sd[640], 0.070140708922879, places=7) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/colorimetry/tests/test_illuminants.py b/colour/colorimetry/tests/test_illuminants.py index 4c3a68f834..59559b2ff0 100644 --- a/colour/colorimetry/tests/test_illuminants.py +++ b/colour/colorimetry/tests/test_illuminants.py @@ -1,165 +1,172 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.colorimetry.illuminants` module. -""" +"""Defines the unit tests for the :mod:`colour.colorimetry.illuminants` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import unittest from colour.colorimetry import ( - SDS_ILLUMINANTS, SpectralShape, sd_CIE_standard_illuminant_A, - sd_CIE_illuminant_D_series, daylight_locus_function) + SDS_ILLUMINANTS, + SpectralShape, + sd_CIE_standard_illuminant_A, + sd_CIE_illuminant_D_series, + daylight_locus_function, +) +from colour.hints import NDArray from colour.temperature import CCT_to_xy_CIE_D from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'DATA_A', 'TestSdCIEStandardIlluminantA', 'TestSdCIEIlluminantDSeries', - 'TestDaylightLocusFunction' + "DATA_A", + "TestSdCIEStandardIlluminantA", + "TestSdCIEIlluminantDSeries", + "TestDaylightLocusFunction", ] -DATA_A = np.array([ - 6.14461778, - 6.94719899, - 7.82134941, - 8.76980228, - 9.79509961, - 10.89957616, - 12.08534536, - 13.35428726, - 14.70803845, - 16.14798414, - 17.67525215, - 19.29070890, - 20.99495729, - 22.78833636, - 24.67092269, - 26.64253337, - 28.70273044, - 30.85082676, - 33.08589297, - 35.40676571, - 37.81205669, - 40.30016269, - 42.86927625, - 45.51739693, - 48.24234315, - 51.04176432, - 53.91315329, - 56.85385894, - 59.86109896, - 62.93197247, - 66.06347275, - 69.25249966, - 72.49587199, - 75.79033948, - 79.13259455, - 82.51928367, - 85.94701837, - 89.41238582, - 92.91195891, - 96.44230599, - 100.00000000, - 103.58162718, - 107.18379528, - 110.80314124, - 114.43633837, - 118.08010305, - 121.73120094, - 125.38645263, - 129.04273891, - 132.69700551, - 136.34626740, - 139.98761262, - 143.61820577, - 147.23529096, - 150.83619449, - 154.41832708, - 157.97918574, - 161.51635535, - 165.02750987, - 168.51041325, - 171.96292009, - 175.38297597, - 178.76861756, - 182.11797252, - 185.42925911, - 188.70078570, - 191.93094995, - 195.11823798, - 198.26122323, - 201.35856531, - 204.40900861, - 207.41138086, - 210.36459156, - 213.26763031, - 216.11956508, - 218.91954039, - 221.66677545, - 224.36056217, - 227.00026327, - 229.58531022, - 232.11520118, - 234.58949900, - 237.00782911, - 239.36987744, - 241.67538835, - 243.92416253, - 246.11605493, - 248.25097273, - 250.32887325, - 252.34976196, - 254.31369047, - 256.22075454, - 258.07109218, - 259.86488167, - 261.60233977, -]) +DATA_A: NDArray = np.array( + [ + 6.14461778, + 6.94719899, + 7.82134941, + 8.76980228, + 9.79509961, + 10.89957616, + 12.08534536, + 13.35428726, + 14.70803845, + 16.14798414, + 17.67525215, + 19.29070890, + 20.99495729, + 22.78833636, + 24.67092269, + 26.64253337, + 28.70273044, + 30.85082676, + 33.08589297, + 35.40676571, + 37.81205669, + 40.30016269, + 42.86927625, + 45.51739693, + 48.24234315, + 51.04176432, + 53.91315329, + 56.85385894, + 59.86109896, + 62.93197247, + 66.06347275, + 69.25249966, + 72.49587199, + 75.79033948, + 79.13259455, + 82.51928367, + 85.94701837, + 89.41238582, + 92.91195891, + 96.44230599, + 100.00000000, + 103.58162718, + 107.18379528, + 110.80314124, + 114.43633837, + 118.08010305, + 121.73120094, + 125.38645263, + 129.04273891, + 132.69700551, + 136.34626740, + 139.98761262, + 143.61820577, + 147.23529096, + 150.83619449, + 154.41832708, + 157.97918574, + 161.51635535, + 165.02750987, + 168.51041325, + 171.96292009, + 175.38297597, + 178.76861756, + 182.11797252, + 185.42925911, + 188.70078570, + 191.93094995, + 195.11823798, + 198.26122323, + 201.35856531, + 204.40900861, + 207.41138086, + 210.36459156, + 213.26763031, + 216.11956508, + 218.91954039, + 221.66677545, + 224.36056217, + 227.00026327, + 229.58531022, + 232.11520118, + 234.58949900, + 237.00782911, + 239.36987744, + 241.67538835, + 243.92416253, + 246.11605493, + 248.25097273, + 250.32887325, + 252.34976196, + 254.31369047, + 256.22075454, + 258.07109218, + 259.86488167, + 261.60233977, + ] +) class TestSdCIEStandardIlluminantA(unittest.TestCase): """ - Defines :func:`colour.colorimetry.illuminants.\ + Define :func:`colour.colorimetry.illuminants.\ sd_CIE_standard_illuminant_A` definition unit tests methods. """ def test_sd_CIE_standard_illuminant_A(self): """ - Tests :func:`colour.colorimetry.illuminants.\ + Test :func:`colour.colorimetry.illuminants.\ sd_CIE_standard_illuminant_A` definition. """ np.testing.assert_almost_equal( sd_CIE_standard_illuminant_A(SpectralShape(360, 830, 5)).values, DATA_A, - decimal=7) + decimal=7, + ) class TestSdCIEIlluminantDSeries(unittest.TestCase): """ - Defines :func:`colour.colorimetry.illuminants.sd_CIE_illuminant_D_series` + Define :func:`colour.colorimetry.illuminants.sd_CIE_illuminant_D_series` definition unit tests methods. """ def test_sd_CIE_illuminant_D_series(self): """ - Tests :func:`colour.colorimetry.illuminants.\ + Test :func:`colour.colorimetry.illuminants.\ sd_CIE_illuminant_D_series` definition. """ for name, CCT, tolerance in ( - ('D50', 5000, 0.001), - ('D55', 5500, 0.001), - ('D65', 6500, 0.00001), - ('D75', 7500, 0.0001), + ("D50", 5000, 0.001), + ("D55", 5500, 0.001), + ("D65", 6500, 0.00001), + ("D75", 7500, 0.0001), ): CCT = CCT * 1.4388 / 1.4380 xy = CCT_to_xy_CIE_D(CCT) @@ -170,33 +177,37 @@ def test_sd_CIE_illuminant_D_series(self): sd_r.values, sd_t[sd_r.wavelengths], rtol=tolerance, - atol=tolerance) + atol=tolerance, + ) class TestDaylightLocusFunction(unittest.TestCase): """ - Defines :func:`colour.colorimetry.illuminants.daylight_locus_function` + Define :func:`colour.colorimetry.illuminants.daylight_locus_function` definition unit tests methods. """ def test_daylight_locus_function(self): """ - Tests :func:`colour.colorimetry.illuminants.daylight_locus_function` + Test :func:`colour.colorimetry.illuminants.daylight_locus_function` definition. """ self.assertAlmostEqual( - daylight_locus_function(0.31270), 0.329105129999999, places=7) + daylight_locus_function(0.31270), 0.329105129999999, places=7 + ) self.assertAlmostEqual( - daylight_locus_function(0.34570), 0.358633529999999, places=7) + daylight_locus_function(0.34570), 0.358633529999999, places=7 + ) self.assertAlmostEqual( - daylight_locus_function(0.44758), 0.408571030799999, places=7) + daylight_locus_function(0.44758), 0.408571030799999, places=7 + ) def test_n_dimensional_daylight_locus_function(self): """ - Tests :func:`colour.colorimetry.illuminants.daylight_locus_function` + Test :func:`colour.colorimetry.illuminants.daylight_locus_function` definition n-dimensional support. """ @@ -206,17 +217,19 @@ def test_n_dimensional_daylight_locus_function(self): x_D = np.tile(x_D, (6, 1)) y_D = np.tile(y_D, (6, 1)) np.testing.assert_almost_equal( - daylight_locus_function(x_D), y_D, decimal=7) + daylight_locus_function(x_D), y_D, decimal=7 + ) x_D = np.reshape(x_D, (2, 3, 1)) y_D = np.reshape(y_D, (2, 3, 1)) np.testing.assert_almost_equal( - daylight_locus_function(x_D), y_D, decimal=7) + daylight_locus_function(x_D), y_D, decimal=7 + ) @ignore_numpy_errors def test_nan_daylight_locus_function(self): """ - Tests :func:`colour.colorimetry.illuminants.daylight_locus_function` + Test :func:`colour.colorimetry.illuminants.daylight_locus_function` definition nan support. """ @@ -225,5 +238,5 @@ def test_nan_daylight_locus_function(self): daylight_locus_function(case) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/colorimetry/tests/test_lefs.py b/colour/colorimetry/tests/test_lefs.py index d2cef74b07..55a5e38697 100644 --- a/colour/colorimetry/tests/test_lefs.py +++ b/colour/colorimetry/tests/test_lefs.py @@ -1,163 +1,468 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.colorimetry.lefs` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.colorimetry.lefs` module.""" import numpy as np import unittest -from colour.colorimetry import (mesopic_weighting_function, - sd_mesopic_luminous_efficiency_function) +from colour.colorimetry import ( + mesopic_weighting_function, + sd_mesopic_luminous_efficiency_function, +) +from colour.hints import Tuple from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'DATA_MESOPIC_LEF', 'TestMesopicWeightingFunction', - 'TestSdMesopicLuminousEfficiencyFunction' + "DATA_MESOPIC_LEF", + "TestMesopicWeightingFunction", + "TestSdMesopicLuminousEfficiencyFunction", ] -DATA_MESOPIC_LEF = ( - 0.000423996221042, 0.000478105586021, 0.000539901310829, 0.000612292743837, - 0.000696128469661, 0.000792943994169, 0.000907002019269, 0.001038991062951, - 0.001192298013423, 0.001370309788741, 0.001577100133700, 0.001816732944066, - 0.002094217578858, 0.002416076568544, 0.002788832443596, 0.003219694655734, - 0.003722285106836, 0.004295769587890, 0.004953150383591, 0.005714298941411, - 0.006578479021522, 0.007565798249134, 0.008691294922216, 0.009963802139828, - 0.011405891376328, 0.013040134056330, 0.014875060844050, 0.016931081053406, - 0.019221107382718, 0.021751183317789, 0.024534221556937, 0.027577375342778, - 0.030917278398861, 0.034514896661912, 0.038399887696429, 0.042574431719405, - 0.047107420323755, 0.051932288874149, 0.057054111113075, 0.062546652908218, - 0.068346295876871, 0.074525532476839, 0.080944093388470, 0.087734408501845, - 0.094891523456653, 0.102273099642187, 0.109877001609769, 0.117842125286601, - 0.126031604242477, 0.134377265167241, 0.143017004659794, 0.151812831610346, - 0.160832831284698, 0.170008823906160, 0.179272634672297, 0.188693478005329, - 0.198204129185973, 0.207803250139006, 0.217418447891767, 0.227114713721992, - 0.236819654657020, 0.246462295387537, 0.256115392006985, 0.265716062322745, - 0.275338793469816, 0.284852031027409, 0.294464840899776, 0.303490271989407, - 0.313234753059177, 0.322325715015028, 0.331451347544553, 0.340612983748718, - 0.349811757758196, 0.358361748146747, 0.367637792269774, 0.376267037500552, - 0.384939235443477, 0.393654052823306, 0.402407748998520, 0.411196583327596, - 0.419329893917046, 0.428180314526271, 0.436380414469050, 0.445311711528396, - 0.454294964705678, 0.462650937976521, 0.471757050335889, 0.480930016713931, - 0.490177596952101, 0.499507550891852, 0.509614559626605, 0.519129332097892, - 0.529425904805685, 0.539131693733156, 0.549621720283231, 0.560210336613821, - 0.570219774106467, 0.581020791225554, 0.591909355273857, 0.602868354806121, - 0.613880678377089, 0.624937372323546, 0.636061964882719, 0.646598972109041, - 0.657953823040568, 0.668784162222337, 0.679793972609001, 0.690988775626207, - 0.702382747907381, 0.713303244319125, 0.724451313004257, 0.735847049962440, - 0.746811890694040, 0.758029423698604, 0.769496415708862, 0.780522562977852, - 0.791780560381546, 0.802612337360557, 0.813079325993057, 0.823929780124040, - 0.835225181574250, 0.845634267482597, 0.856481883918201, 0.867692173456598, - 0.878502108708491, 0.888148910997447, 0.898640517466401, 0.907932280953237, - 0.917425551604990, 0.925773944818251, 0.935065632506880, 0.943236567905124, - 0.950906320820545, 0.958693118011219, 0.965841393473861, 0.972282552199726, - 0.977992469040708, 0.983610660576306, 0.988346542031548, 0.992096449883431, - 0.995443641860918, 0.997620215802517, 0.999345724088358, 1.000000000000000, - 0.999649798070971, 0.999048794086766, 0.997535634478154, 0.995761525329390, - 0.993014375646354, 0.989955936938867, 0.985874118212809, 0.981445295294584, - 0.976688569332386, 0.970936319192731, 0.964894766246075, 0.958583260353477, - 0.952011998742489, 0.944491622775044, 0.936708904475390, 0.929350665610349, - 0.921042964190838, 0.912477248043005, 0.903660431384556, 0.894595846967911, - 0.884599956036092, 0.875050033330603, 0.865945730653430, 0.855922407876719, - 0.845684595123427, 0.835249901264545, 0.825322906165605, 0.815207983624290, - 0.804220500547010, 0.794420922234228, 0.783746644454823, 0.773568077475167, - 0.762780824836753, 0.752271069434583, 0.741754989619506, 0.731290980804024, - 0.720798313160170, 0.710193918959030, 0.699636273302128, 0.689065687470055, - 0.678559906736370, 0.668059341866812, 0.657569762999898, 0.647157872557888, - 0.636820835214096, 0.626487173259215, 0.616154150851706, 0.605889614493019, - 0.595700080460271, 0.585593706535502, 0.575441266250360, 0.565388301637279, - 0.555374278353102, 0.545468087493321, 0.535597256497158, 0.525826697054231, - 0.516015244478566, 0.506232237548814, 0.496559550172129, 0.486874671127568, - 0.477329907437551, 0.467802847366283, 0.458370440306148, 0.448972251399137, - 0.439760637410242, 0.430613136861096, 0.421544622781150, 0.412568127724641, - 0.403755029915728, 0.395035990575763, 0.386410412793862, 0.377877749401719, - 0.369440537270578, 0.361107481350787, 0.352859660514895, 0.344705630485531, - 0.336647077772802, 0.328691712475589, 0.320841076613668, 0.313080884593691, - 0.305410539504777, 0.297822525480953, 0.290302706156591, 0.282872778768479, - 0.275531191822063, 0.268290082505259, 0.261147799838367, 0.254117629825878, - 0.247188574621679, 0.240357052760484, 0.233605744351966, 0.226937937143361, - 0.220352731030957, 0.213846544549470, 0.207394601159948, 0.200978993535962, - 0.194581814351080, 0.188194308912381, 0.181822635824193, 0.175498734556038, - 0.169247675364920, 0.163087659295321, 0.157025700050094, 0.151071000005320, - 0.145246954751365, 0.139584565754938, 0.134108701934053, 0.128840802337855, - 0.123766619272101, 0.118863181142645, 0.114107516355346, 0.109476653316060, - 0.104961368330458, 0.100567919488016, 0.096292436342783, 0.092129674606303, - 0.088077824596383, 0.084130597430525, 0.080288739247331, 0.076555990214584, - 0.072936727678744, 0.069434592322446, 0.066049096194732, 0.062779244444135, - 0.059627872397338, 0.056597078717201, 0.053689599245262, 0.050906885990927, - 0.048244432277043, 0.045695095070067, 0.043251069286492, 0.040905236764061, - 0.038653732504820, 0.036495591265975, 0.034428544786178, 0.032450118727706, - 0.030557907444960, 0.028749627753821, 0.027023338509577, 0.025377681739976, - 0.023811304447019, 0.022322652530592, 0.020908627975848, 0.019568807970913, - 0.018305670643421, 0.017121625428881, 0.016019287839177, 0.014998624949462, - 0.014053763109294, 0.013178438781201, 0.012366256017720, 0.011610755153519, - 0.010909796310463, 0.010258783274767, 0.009647645070271, 0.009066521771446, - 0.008505352350642, 0.007956703372438, 0.007422984272341, 0.006909491686943, - 0.006421306227945, 0.005963729506196, 0.005537741450392, 0.005140198329894, - 0.004770003226209, 0.004426333586668, 0.004108134205482, 0.003814898916435, - 0.003545595063702, 0.003298484428587, 0.003071843028242, 0.002863947377246, - 0.002673821526309, 0.002500026114851, 0.002340163148156, 0.002191847872506, - 0.002052667559907, 0.001920775671288, 0.001795998699814, 0.001678462683692, - 0.001568320640552, 0.001465704482962, 0.001370202034484, 0.001281000501467, - 0.001197625742439, 0.001119582510866, 0.001046395670422, 0.000977644493684, - 0.000913102154518, 0.000852563310843, 0.000795835861579, 0.000742713469792, - 0.000692929112612, 0.000646294076190, 0.000602683198736, 0.000561985056886, - 0.000524088227275, 0.000488865075198, 0.000456100902232, 0.000425592157031, - 0.000397124984426, 0.000370486903095, 0.000345511126189, 0.000322107797301, - 0.000300185818831, 0.000279651295749, 0.000260410283283, 0.000242354880318, - 0.000225413417278, 0.000209548506700, 0.000194724821881, 0.000180904975355, - 0.000168036678203, 0.000156055324297, 0.000144907947270, 0.000134542904857, - 0.000124905757365, 0.000115947828132, 0.000107630246890, 0.000099921825045, - 0.000092792011184, 0.000086209517226, 0.000080138556943, 0.000074542690348, - 0.000069382092589, 0.000064618362399, 0.000060213148254, 0.000056133629529, - 0.000052356318257, 0.000048854801130, 0.000045601913255, 0.000042571186609, - 0.000039737093303, 0.000037085765108, 0.000034608253334, 0.000032294917398, - 0.000030137485583, 0.000028122278208, 0.000026238700578, 0.000024479703696, - 0.000022839057899, 0.000021310391162, 0.000019886198277, 0.000018558646686, - 0.000017321059987, 0.000016166624394, 0.000015088526124, 0.000014080323512, - 0.000013137863053, 0.000012257679587, 0.000011436518999, 0.000010671063462, - 0.000009957265824, 0.000009291440160, 0.000008670655686, 0.000008091780515, - 0.000007551893809) +DATA_MESOPIC_LEF: Tuple = ( + 0.000423996221042, + 0.000478105586021, + 0.000539901310829, + 0.000612292743837, + 0.000696128469661, + 0.000792943994169, + 0.000907002019269, + 0.001038991062951, + 0.001192298013423, + 0.001370309788741, + 0.001577100133700, + 0.001816732944066, + 0.002094217578858, + 0.002416076568544, + 0.002788832443596, + 0.003219694655734, + 0.003722285106836, + 0.004295769587890, + 0.004953150383591, + 0.005714298941411, + 0.006578479021522, + 0.007565798249134, + 0.008691294922216, + 0.009963802139828, + 0.011405891376328, + 0.013040134056330, + 0.014875060844050, + 0.016931081053406, + 0.019221107382718, + 0.021751183317789, + 0.024534221556937, + 0.027577375342778, + 0.030917278398861, + 0.034514896661912, + 0.038399887696429, + 0.042574431719405, + 0.047107420323755, + 0.051932288874149, + 0.057054111113075, + 0.062546652908218, + 0.068346295876871, + 0.074525532476839, + 0.080944093388470, + 0.087734408501845, + 0.094891523456653, + 0.102273099642187, + 0.109877001609769, + 0.117842125286601, + 0.126031604242477, + 0.134377265167241, + 0.143017004659794, + 0.151812831610346, + 0.160832831284698, + 0.170008823906160, + 0.179272634672297, + 0.188693478005329, + 0.198204129185973, + 0.207803250139006, + 0.217418447891767, + 0.227114713721992, + 0.236819654657020, + 0.246462295387537, + 0.256115392006985, + 0.265716062322745, + 0.275338793469816, + 0.284852031027409, + 0.294464840899776, + 0.303490271989407, + 0.313234753059177, + 0.322325715015028, + 0.331451347544553, + 0.340612983748718, + 0.349811757758196, + 0.358361748146747, + 0.367637792269774, + 0.376267037500552, + 0.384939235443477, + 0.393654052823306, + 0.402407748998520, + 0.411196583327596, + 0.419329893917046, + 0.428180314526271, + 0.436380414469050, + 0.445311711528396, + 0.454294964705678, + 0.462650937976521, + 0.471757050335889, + 0.480930016713931, + 0.490177596952101, + 0.499507550891852, + 0.509614559626605, + 0.519129332097892, + 0.529425904805685, + 0.539131693733156, + 0.549621720283231, + 0.560210336613821, + 0.570219774106467, + 0.581020791225554, + 0.591909355273857, + 0.602868354806121, + 0.613880678377089, + 0.624937372323546, + 0.636061964882719, + 0.646598972109041, + 0.657953823040568, + 0.668784162222337, + 0.679793972609001, + 0.690988775626207, + 0.702382747907381, + 0.713303244319125, + 0.724451313004257, + 0.735847049962440, + 0.746811890694040, + 0.758029423698604, + 0.769496415708862, + 0.780522562977852, + 0.791780560381546, + 0.802612337360557, + 0.813079325993057, + 0.823929780124040, + 0.835225181574250, + 0.845634267482597, + 0.856481883918201, + 0.867692173456598, + 0.878502108708491, + 0.888148910997447, + 0.898640517466401, + 0.907932280953237, + 0.917425551604990, + 0.925773944818251, + 0.935065632506880, + 0.943236567905124, + 0.950906320820545, + 0.958693118011219, + 0.965841393473861, + 0.972282552199726, + 0.977992469040708, + 0.983610660576306, + 0.988346542031548, + 0.992096449883431, + 0.995443641860918, + 0.997620215802517, + 0.999345724088358, + 1.000000000000000, + 0.999649798070971, + 0.999048794086766, + 0.997535634478154, + 0.995761525329390, + 0.993014375646354, + 0.989955936938867, + 0.985874118212809, + 0.981445295294584, + 0.976688569332386, + 0.970936319192731, + 0.964894766246075, + 0.958583260353477, + 0.952011998742489, + 0.944491622775044, + 0.936708904475390, + 0.929350665610349, + 0.921042964190838, + 0.912477248043005, + 0.903660431384556, + 0.894595846967911, + 0.884599956036092, + 0.875050033330603, + 0.865945730653430, + 0.855922407876719, + 0.845684595123427, + 0.835249901264545, + 0.825322906165605, + 0.815207983624290, + 0.804220500547010, + 0.794420922234228, + 0.783746644454823, + 0.773568077475167, + 0.762780824836753, + 0.752271069434583, + 0.741754989619506, + 0.731290980804024, + 0.720798313160170, + 0.710193918959030, + 0.699636273302128, + 0.689065687470055, + 0.678559906736370, + 0.668059341866812, + 0.657569762999898, + 0.647157872557888, + 0.636820835214096, + 0.626487173259215, + 0.616154150851706, + 0.605889614493019, + 0.595700080460271, + 0.585593706535502, + 0.575441266250360, + 0.565388301637279, + 0.555374278353102, + 0.545468087493321, + 0.535597256497158, + 0.525826697054231, + 0.516015244478566, + 0.506232237548814, + 0.496559550172129, + 0.486874671127568, + 0.477329907437551, + 0.467802847366283, + 0.458370440306148, + 0.448972251399137, + 0.439760637410242, + 0.430613136861096, + 0.421544622781150, + 0.412568127724641, + 0.403755029915728, + 0.395035990575763, + 0.386410412793862, + 0.377877749401719, + 0.369440537270578, + 0.361107481350787, + 0.352859660514895, + 0.344705630485531, + 0.336647077772802, + 0.328691712475589, + 0.320841076613668, + 0.313080884593691, + 0.305410539504777, + 0.297822525480953, + 0.290302706156591, + 0.282872778768479, + 0.275531191822063, + 0.268290082505259, + 0.261147799838367, + 0.254117629825878, + 0.247188574621679, + 0.240357052760484, + 0.233605744351966, + 0.226937937143361, + 0.220352731030957, + 0.213846544549470, + 0.207394601159948, + 0.200978993535962, + 0.194581814351080, + 0.188194308912381, + 0.181822635824193, + 0.175498734556038, + 0.169247675364920, + 0.163087659295321, + 0.157025700050094, + 0.151071000005320, + 0.145246954751365, + 0.139584565754938, + 0.134108701934053, + 0.128840802337855, + 0.123766619272101, + 0.118863181142645, + 0.114107516355346, + 0.109476653316060, + 0.104961368330458, + 0.100567919488016, + 0.096292436342783, + 0.092129674606303, + 0.088077824596383, + 0.084130597430525, + 0.080288739247331, + 0.076555990214584, + 0.072936727678744, + 0.069434592322446, + 0.066049096194732, + 0.062779244444135, + 0.059627872397338, + 0.056597078717201, + 0.053689599245262, + 0.050906885990927, + 0.048244432277043, + 0.045695095070067, + 0.043251069286492, + 0.040905236764061, + 0.038653732504820, + 0.036495591265975, + 0.034428544786178, + 0.032450118727706, + 0.030557907444960, + 0.028749627753821, + 0.027023338509577, + 0.025377681739976, + 0.023811304447019, + 0.022322652530592, + 0.020908627975848, + 0.019568807970913, + 0.018305670643421, + 0.017121625428881, + 0.016019287839177, + 0.014998624949462, + 0.014053763109294, + 0.013178438781201, + 0.012366256017720, + 0.011610755153519, + 0.010909796310463, + 0.010258783274767, + 0.009647645070271, + 0.009066521771446, + 0.008505352350642, + 0.007956703372438, + 0.007422984272341, + 0.006909491686943, + 0.006421306227945, + 0.005963729506196, + 0.005537741450392, + 0.005140198329894, + 0.004770003226209, + 0.004426333586668, + 0.004108134205482, + 0.003814898916435, + 0.003545595063702, + 0.003298484428587, + 0.003071843028242, + 0.002863947377246, + 0.002673821526309, + 0.002500026114851, + 0.002340163148156, + 0.002191847872506, + 0.002052667559907, + 0.001920775671288, + 0.001795998699814, + 0.001678462683692, + 0.001568320640552, + 0.001465704482962, + 0.001370202034484, + 0.001281000501467, + 0.001197625742439, + 0.001119582510866, + 0.001046395670422, + 0.000977644493684, + 0.000913102154518, + 0.000852563310843, + 0.000795835861579, + 0.000742713469792, + 0.000692929112612, + 0.000646294076190, + 0.000602683198736, + 0.000561985056886, + 0.000524088227275, + 0.000488865075198, + 0.000456100902232, + 0.000425592157031, + 0.000397124984426, + 0.000370486903095, + 0.000345511126189, + 0.000322107797301, + 0.000300185818831, + 0.000279651295749, + 0.000260410283283, + 0.000242354880318, + 0.000225413417278, + 0.000209548506700, + 0.000194724821881, + 0.000180904975355, + 0.000168036678203, + 0.000156055324297, + 0.000144907947270, + 0.000134542904857, + 0.000124905757365, + 0.000115947828132, + 0.000107630246890, + 0.000099921825045, + 0.000092792011184, + 0.000086209517226, + 0.000080138556943, + 0.000074542690348, + 0.000069382092589, + 0.000064618362399, + 0.000060213148254, + 0.000056133629529, + 0.000052356318257, + 0.000048854801130, + 0.000045601913255, + 0.000042571186609, + 0.000039737093303, + 0.000037085765108, + 0.000034608253334, + 0.000032294917398, + 0.000030137485583, + 0.000028122278208, + 0.000026238700578, + 0.000024479703696, + 0.000022839057899, + 0.000021310391162, + 0.000019886198277, + 0.000018558646686, + 0.000017321059987, + 0.000016166624394, + 0.000015088526124, + 0.000014080323512, + 0.000013137863053, + 0.000012257679587, + 0.000011436518999, + 0.000010671063462, + 0.000009957265824, + 0.000009291440160, + 0.000008670655686, + 0.000008091780515, + 0.000007551893809, +) class TestMesopicWeightingFunction(unittest.TestCase): """ - Defines :func:`colour.colorimetry.lefs.mesopic_weighting_function` + Define :func:`colour.colorimetry.lefs.mesopic_weighting_function` definition unit tests methods. """ def test_mesopic_weighting_function(self): """ - Tests :func:`colour.colorimetry.lefs.mesopic_weighting_function` + Test :func:`colour.colorimetry.lefs.mesopic_weighting_function` definition. """ self.assertAlmostEqual( - mesopic_weighting_function(500, 0.2), 0.70522000, places=7) + mesopic_weighting_function(500, 0.2), 0.70522000, places=7 + ) self.assertAlmostEqual( mesopic_weighting_function( - 500, 0.2, source='Red Heavy', method='LRC'), + 500, 0.2, source="Red Heavy", method="LRC" + ), 0.90951000, - places=7) + places=7, + ) self.assertAlmostEqual( mesopic_weighting_function( - 700, 10, source='Red Heavy', method='LRC'), + 700, 10, source="Red Heavy", method="LRC" + ), 0.00410200, - places=7) + places=7, + ) def test_n_dimensional_mesopic_weighting_function(self): """ - Tests :func:`colour.colorimetry.lefs.mesopic_weighting_function` + Test :func:`colour.colorimetry.lefs.mesopic_weighting_function` definition n-dimensional arrays support. """ @@ -179,31 +484,33 @@ def test_n_dimensional_mesopic_weighting_function(self): @ignore_numpy_errors def test_nan_mesopic_weighting_function(self): """ - Tests :func:`colour.colorimetry.lefs.mesopic_weighting_function` + Test :func:`colour.colorimetry.lefs.mesopic_weighting_function` definition nan support. """ mesopic_weighting_function( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]), 0.2), + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]), 0.2 + ), class TestSdMesopicLuminousEfficiencyFunction(unittest.TestCase): """ - Defines :func:`colour.colorimetry.lefs.\ + Define :func:`colour.colorimetry.lefs.\ sd_mesopic_luminous_efficiency_function` definition unit tests methods. """ def test_sd_mesopic_luminous_efficiency_function(self): """ - Tests :func:`colour.colorimetry.lefs.\ + Test :func:`colour.colorimetry.lefs.\ sd_mesopic_luminous_efficiency_function` definition. """ np.testing.assert_almost_equal( sd_mesopic_luminous_efficiency_function(0.2).values, DATA_MESOPIC_LEF, - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/colorimetry/tests/test_lightness.py b/colour/colorimetry/tests/test_lightness.py index 0946091a1a..22a6f91991 100644 --- a/colour/colorimetry/tests/test_lightness.py +++ b/colour/colorimetry/tests/test_lightness.py @@ -1,58 +1,66 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.colorimetry.lightness` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.colorimetry.lightness` module.""" import numpy as np import unittest -from colour.colorimetry import (lightness_Glasser1958, lightness_Wyszecki1963, - intermediate_lightness_function_CIE1976, - lightness_CIE1976, lightness_Fairchild2010, - lightness_Fairchild2011) +from colour.colorimetry import ( + lightness_Glasser1958, + lightness_Wyszecki1963, + intermediate_lightness_function_CIE1976, + lightness_CIE1976, + lightness_Fairchild2010, + lightness_Fairchild2011, + lightness_Abebe2017, +) from colour.colorimetry.lightness import lightness from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestLightnessGlasser1958', 'TestLightnessWyszecki1963', - 'TestIntermediateLightnessFunctionCIE1976', 'TestLightnessCIE1976', - 'TestLightnessFairchild2010', 'TestLightnessFairchild2011', 'TestLightness' + "TestLightnessGlasser1958", + "TestLightnessWyszecki1963", + "TestIntermediateLightnessFunctionCIE1976", + "TestLightnessCIE1976", + "TestLightnessFairchild2010", + "TestLightnessFairchild2011", + "TestLightnessAbebe2017", + "TestLightness", ] class TestLightnessGlasser1958(unittest.TestCase): """ - Defines :func:`colour.colorimetry.lightness.lightness_Glasser1958` + Define :func:`colour.colorimetry.lightness.lightness_Glasser1958` definition unit tests methods. """ def test_lightness_Glasser1958(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_Glasser1958` + Test :func:`colour.colorimetry.lightness.lightness_Glasser1958` definition. """ self.assertAlmostEqual( - lightness_Glasser1958(12.19722535), 39.83512646492521, places=7) + lightness_Glasser1958(12.19722535), 39.83512646492521, places=7 + ) self.assertAlmostEqual( - lightness_Glasser1958(23.04276781), 53.585946877480623, places=7) + lightness_Glasser1958(23.04276781), 53.585946877480623, places=7 + ) self.assertAlmostEqual( - lightness_Glasser1958(6.15720079), 27.972867038082629, places=7) + lightness_Glasser1958(6.15720079), 27.972867038082629, places=7 + ) def test_n_dimensional_lightness_Glasser1958(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_Glasser1958` + Test :func:`colour.colorimetry.lightness.lightness_Glasser1958` definition n-dimensional arrays support. """ @@ -73,55 +81,60 @@ def test_n_dimensional_lightness_Glasser1958(self): def test_domain_range_scale_lightness_Glasser1958(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_Glasser1958` + Test :func:`colour.colorimetry.lightness.lightness_Glasser1958` definition domain and range scale support. """ L = lightness_Glasser1958(12.19722535) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( lightness_Glasser1958(12.19722535 * factor), L * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_lightness_Glasser1958(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_Glasser1958` + Test :func:`colour.colorimetry.lightness.lightness_Glasser1958` definition nan support. """ lightness_Glasser1958( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLightnessWyszecki1963(unittest.TestCase): """ - Defines :func:`colour.colorimetry.lightness.lightness_Wyszecki1963` + Define :func:`colour.colorimetry.lightness.lightness_Wyszecki1963` definition unit tests methods. """ def test_lightness_Wyszecki1963(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_Wyszecki1963` + Test :func:`colour.colorimetry.lightness.lightness_Wyszecki1963` definition. """ self.assertAlmostEqual( - lightness_Wyszecki1963(12.19722535), 40.547574599570197, places=7) + lightness_Wyszecki1963(12.19722535), 40.547574599570197, places=7 + ) self.assertAlmostEqual( - lightness_Wyszecki1963(23.04276781), 54.140714588256841, places=7) + lightness_Wyszecki1963(23.04276781), 54.140714588256841, places=7 + ) self.assertAlmostEqual( - lightness_Wyszecki1963(6.15720079), 28.821339499883976, places=7) + lightness_Wyszecki1963(6.15720079), 28.821339499883976, places=7 + ) def test_n_dimensional_lightness_Wyszecki1963(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_Wyszecki1963` + Test :func:`colour.colorimetry.lightness.lightness_Wyszecki1963` definition n-dimensional arrays support. """ @@ -142,61 +155,66 @@ def test_n_dimensional_lightness_Wyszecki1963(self): def test_domain_range_scale_lightness_Wyszecki1963(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_Wyszecki1963` + Test :func:`colour.colorimetry.lightness.lightness_Wyszecki1963` definition domain and range scale support. """ W = lightness_Wyszecki1963(12.19722535) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( lightness_Wyszecki1963(12.19722535 * factor), W * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_lightness_Wyszecki1963(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_Wyszecki1963` + Test :func:`colour.colorimetry.lightness.lightness_Wyszecki1963` definition nan support. """ lightness_Wyszecki1963( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestIntermediateLightnessFunctionCIE1976(unittest.TestCase): """ - Defines :func:`colour.colorimetry.lightness.\ + Define :func:`colour.colorimetry.lightness.\ intermediate_lightness_function_CIE1976` definition unit tests methods. """ def test_intermediate_lightness_function_CIE1976(self): """ - Tests :func:`colour.colorimetry.lightness.\ + Test :func:`colour.colorimetry.lightness.\ intermediate_lightness_function_CIE1976` definition. """ self.assertAlmostEqual( intermediate_lightness_function_CIE1976(12.19722535), 0.495929964178047, - places=7) + places=7, + ) self.assertAlmostEqual( intermediate_lightness_function_CIE1976(23.04276781), 0.613072093530391, - places=7) + places=7, + ) self.assertAlmostEqual( intermediate_lightness_function_CIE1976(6.15720079), 0.394876333449113, - places=7) + places=7, + ) def test_n_dimensional_intermediate_lightness_function_CIE1976(self): """ - Tests :func:`colour.colorimetry.lightness.\ + Test :func:`colour.colorimetry.lightness.\ intermediate_lightness_function_CIE1976` definition n-dimensional arrays support. """ @@ -207,78 +225,89 @@ def test_n_dimensional_intermediate_lightness_function_CIE1976(self): Y = np.tile(Y, 6) f_Y_Y_n = np.tile(f_Y_Y_n, 6) np.testing.assert_almost_equal( - intermediate_lightness_function_CIE1976(Y), f_Y_Y_n, decimal=7) + intermediate_lightness_function_CIE1976(Y), f_Y_Y_n, decimal=7 + ) Y = np.reshape(Y, (2, 3)) f_Y_Y_n = np.reshape(f_Y_Y_n, (2, 3)) np.testing.assert_almost_equal( - intermediate_lightness_function_CIE1976(Y), f_Y_Y_n, decimal=7) + intermediate_lightness_function_CIE1976(Y), f_Y_Y_n, decimal=7 + ) Y = np.reshape(Y, (2, 3, 1)) f_Y_Y_n = np.reshape(f_Y_Y_n, (2, 3, 1)) np.testing.assert_almost_equal( - intermediate_lightness_function_CIE1976(Y), f_Y_Y_n, decimal=7) + intermediate_lightness_function_CIE1976(Y), f_Y_Y_n, decimal=7 + ) def test_domain_range_scale_intermediate_lightness_function_CIE1976(self): """ - Tests :func:`colour.colorimetry.lightness.\ + Test :func:`colour.colorimetry.lightness.\ intermediate_lightness_function_CIE1976` definition domain and range scale support. """ f_Y_Y_n = intermediate_lightness_function_CIE1976(12.19722535, 100) - for scale in ('reference', 1, 100): + for scale in ("reference", "1", "100"): with domain_range_scale(scale): np.testing.assert_almost_equal( intermediate_lightness_function_CIE1976(12.19722535, 100), f_Y_Y_n, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_intermediate_lightness_function_CIE1976(self): """ - Tests :func:`colour.colorimetry.lightness.\ + Test :func:`colour.colorimetry.lightness.\ intermediate_lightness_function_CIE1976` definition nan support. """ intermediate_lightness_function_CIE1976( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLightnessCIE1976(unittest.TestCase): """ - Defines :func:`colour.colorimetry.lightness.lightness_CIE1976` definition + Define :func:`colour.colorimetry.lightness.lightness_CIE1976` definition unit tests methods. """ def test_lightness_CIE1976(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_CIE1976` + Test :func:`colour.colorimetry.lightness.lightness_CIE1976` definition. """ self.assertAlmostEqual( - lightness_CIE1976(12.19722535), 41.527875844653451, places=7) + lightness_CIE1976(12.19722535), 41.527875844653451, places=7 + ) self.assertAlmostEqual( - lightness_CIE1976(23.04276781), 55.116362849525402, places=7) + lightness_CIE1976(23.04276781), 55.116362849525402, places=7 + ) self.assertAlmostEqual( - lightness_CIE1976(6.15720079), 29.805654680097106, places=7) + lightness_CIE1976(6.15720079), 29.805654680097106, places=7 + ) self.assertAlmostEqual( - lightness_CIE1976(12.19722535, 50), 56.480581732417676, places=7) + lightness_CIE1976(12.19722535, 50), 56.480581732417676, places=7 + ) self.assertAlmostEqual( - lightness_CIE1976(12.19722535, 75), 47.317620274162735, places=7) + lightness_CIE1976(12.19722535, 75), 47.317620274162735, places=7 + ) self.assertAlmostEqual( - lightness_CIE1976(12.19722535, 95), 42.519930728120940, places=7) + lightness_CIE1976(12.19722535, 95), 42.519930728120940, places=7 + ) def test_n_dimensional_lightness_CIE1976(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_CIE1976` + Test :func:`colour.colorimetry.lightness.lightness_CIE1976` definition n-dimensional arrays support. """ @@ -299,24 +328,25 @@ def test_n_dimensional_lightness_CIE1976(self): def test_domain_range_scale_lightness_CIE1976(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_CIE1976` + Test :func:`colour.colorimetry.lightness.lightness_CIE1976` definition domain and range scale support. """ L_star = lightness_CIE1976(12.19722535, 100) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( lightness_CIE1976(12.19722535 * factor, 100), L_star * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_lightness_CIE1976(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_CIE1976` + Test :func:`colour.colorimetry.lightness.lightness_CIE1976` definition nan support. """ @@ -325,45 +355,51 @@ def test_nan_lightness_CIE1976(self): class TestLightnessFairchild2010(unittest.TestCase): """ - Defines :func:`colour.colorimetry.lightness.lightness_Fairchild2010` + Define :func:`colour.colorimetry.lightness.lightness_Fairchild2010` definition unit tests methods. """ def test_lightness_Fairchild2010(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_Fairchild2010` + Test :func:`colour.colorimetry.lightness.lightness_Fairchild2010` definition. """ self.assertAlmostEqual( lightness_Fairchild2010(12.19722535 / 100), 31.996390226262736, - places=7) + places=7, + ) self.assertAlmostEqual( lightness_Fairchild2010(23.04276781 / 100), 60.203153682783302, - places=7) + places=7, + ) self.assertAlmostEqual( lightness_Fairchild2010(6.15720079 / 100), 11.836517240976489, - places=7) + places=7, + ) self.assertAlmostEqual( lightness_Fairchild2010(12.19722535 / 100, 2.75), 24.424283249379986, - places=7) + places=7, + ) self.assertAlmostEqual( - lightness_Fairchild2010(1008), 100.019986327374240, places=7) + lightness_Fairchild2010(1008), 100.019986327374240, places=7 + ) self.assertAlmostEqual( - lightness_Fairchild2010(100800), 100.019999997090270, places=7) + lightness_Fairchild2010(100800), 100.019999997090270, places=7 + ) def test_n_dimensional_lightness_Fairchild2010(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_Fairchild2010` + Test :func:`colour.colorimetry.lightness.lightness_Fairchild2010` definition n-dimensional arrays support. """ @@ -373,86 +409,97 @@ def test_n_dimensional_lightness_Fairchild2010(self): Y = np.tile(Y, 6) L_hdr = np.tile(L_hdr, 6) np.testing.assert_almost_equal( - lightness_Fairchild2010(Y), L_hdr, decimal=7) + lightness_Fairchild2010(Y), L_hdr, decimal=7 + ) Y = np.reshape(Y, (2, 3)) L_hdr = np.reshape(L_hdr, (2, 3)) np.testing.assert_almost_equal( - lightness_Fairchild2010(Y), L_hdr, decimal=7) + lightness_Fairchild2010(Y), L_hdr, decimal=7 + ) Y = np.reshape(Y, (2, 3, 1)) L_hdr = np.reshape(L_hdr, (2, 3, 1)) np.testing.assert_almost_equal( - lightness_Fairchild2010(Y), L_hdr, decimal=7) + lightness_Fairchild2010(Y), L_hdr, decimal=7 + ) def test_domain_range_scale_lightness_Fairchild2010(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_Fairchild2010` + Test :func:`colour.colorimetry.lightness.lightness_Fairchild2010` definition domain and range scale support. """ L_hdr = lightness_Fairchild2010(12.19722535 / 100) - d_r = (('reference', 1, 1), (1, 1, 0.01), (100, 100, 1)) + d_r = (("reference", 1, 1), ("1", 1, 0.01), ("100", 100, 1)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( lightness_Fairchild2010(12.19722535 / 100 * factor_a), L_hdr * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_lightness_Fairchild2010(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_Fairchild2010` + Test :func:`colour.colorimetry.lightness.lightness_Fairchild2010` definition nan support. """ lightness_Fairchild2010( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLightnessFairchild2011(unittest.TestCase): """ - Defines :func:`colour.colorimetry.lightness.lightness_Fairchild2011` + Define :func:`colour.colorimetry.lightness.lightness_Fairchild2011` definition unit tests methods. """ def test_lightness_Fairchild2011(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_Fairchild2011` + Test :func:`colour.colorimetry.lightness.lightness_Fairchild2011` definition. """ self.assertAlmostEqual( lightness_Fairchild2011(12.19722535 / 100), 51.852958445912506, - places=7) + places=7, + ) self.assertAlmostEqual( lightness_Fairchild2011(23.04276781 / 100), 65.275207956353853, - places=7) + places=7, + ) self.assertAlmostEqual( lightness_Fairchild2011(6.15720079 / 100), 39.818935510715917, - places=7) + places=7, + ) self.assertAlmostEqual( lightness_Fairchild2011(12.19722535 / 100, 2.75), 0.13268968410139345, - places=7) + places=7, + ) self.assertAlmostEqual( - lightness_Fairchild2011(1008), 234.72925682, places=7) + lightness_Fairchild2011(1008), 234.72925682, places=7 + ) self.assertAlmostEqual( - lightness_Fairchild2011(100800), 245.5705978, places=7) + lightness_Fairchild2011(100800), 245.5705978, places=7 + ) def test_n_dimensional_lightness_Fairchild2011(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_Fairchild2011` + Test :func:`colour.colorimetry.lightness.lightness_Fairchild2011` definition n-dimensional arrays support. """ @@ -462,70 +509,168 @@ def test_n_dimensional_lightness_Fairchild2011(self): Y = np.tile(Y, 6) L_hdr = np.tile(L_hdr, 6) np.testing.assert_almost_equal( - lightness_Fairchild2011(Y), L_hdr, decimal=7) + lightness_Fairchild2011(Y), L_hdr, decimal=7 + ) Y = np.reshape(Y, (2, 3)) L_hdr = np.reshape(L_hdr, (2, 3)) np.testing.assert_almost_equal( - lightness_Fairchild2011(Y), L_hdr, decimal=7) + lightness_Fairchild2011(Y), L_hdr, decimal=7 + ) Y = np.reshape(Y, (2, 3, 1)) L_hdr = np.reshape(L_hdr, (2, 3, 1)) np.testing.assert_almost_equal( - lightness_Fairchild2011(Y), L_hdr, decimal=7) + lightness_Fairchild2011(Y), L_hdr, decimal=7 + ) def test_domain_range_scale_lightness_Fairchild2011(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_Fairchild2011` + Test :func:`colour.colorimetry.lightness.lightness_Fairchild2011` definition domain and range scale support. """ L_hdr = lightness_Fairchild2011(12.19722535 / 100) - d_r = (('reference', 1, 1), (1, 1, 0.01), (100, 100, 1)) + d_r = (("reference", 1, 1), ("1", 1, 0.01), ("100", 100, 1)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( lightness_Fairchild2011(12.19722535 / 100 * factor_a), L_hdr * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_lightness_Fairchild2011(self): """ - Tests :func:`colour.colorimetry.lightness.lightness_Fairchild2011` + Test :func:`colour.colorimetry.lightness.lightness_Fairchild2011` definition nan support. """ lightness_Fairchild2011( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) + + +class TestLightnessAbebe2017(unittest.TestCase): + """ + Define :func:`colour.colorimetry.lightness.lightness_Abebe2017` + definition unit tests methods. + """ + + def test_lightness_Abebe2017(self): + """ + Test :func:`colour.colorimetry.lightness.lightness_Abebe2017` + definition. + """ + + self.assertAlmostEqual( + lightness_Abebe2017(12.19722535), 0.486955571109229, places=7 + ) + + self.assertAlmostEqual( + lightness_Abebe2017(12.19722535, method="Stevens"), + 0.474544792145434, + places=7, + ) + + self.assertAlmostEqual( + lightness_Abebe2017(12.19722535, 1000), 0.286847428534793, places=7 + ) + + self.assertAlmostEqual( + lightness_Abebe2017(12.19722535, 4000), 0.192145492588158, places=7 + ) + + self.assertAlmostEqual( + lightness_Abebe2017(12.19722535, 4000, method="Stevens"), + 0.170365211220992, + places=7, + ) + + def test_n_dimensional_lightness_Abebe2017(self): + """ + Test :func:`colour.colorimetry.lightness.lightness_Abebe2017` + definition n-dimensional arrays support. + """ + + Y = 12.19722535 + L = lightness_Abebe2017(Y) + + Y = np.tile(Y, 6) + L = np.tile(L, 6) + np.testing.assert_almost_equal(lightness_Abebe2017(Y), L, decimal=7) + + Y = np.reshape(Y, (2, 3)) + L = np.reshape(L, (2, 3)) + np.testing.assert_almost_equal(lightness_Abebe2017(Y), L, decimal=7) + + Y = np.reshape(Y, (2, 3, 1)) + L = np.reshape(L, (2, 3, 1)) + np.testing.assert_almost_equal(lightness_Abebe2017(Y), L, decimal=7) + + def test_domain_range_scale_lightness_Abebe2017(self): + """ + Test :func:`colour.colorimetry.lightness.lightness_Abebe2017` + definition domain and range scale support. + """ + + L = lightness_Abebe2017(12.19722535) + + d_r = (("reference", 1), ("1", 1), ("100", 1)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + lightness_Abebe2017(12.19722535 * factor, 100 * factor), + L * factor, + decimal=7, + ) + + @ignore_numpy_errors + def test_nan_lightness_Abebe2017(self): + """ + Test :func:`colour.colorimetry.lightness.lightness_Abebe2017` + definition nan support. + """ + + lightness_Abebe2017( + *[np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])] * 2 + ) class TestLightness(unittest.TestCase): """ - Defines :func:`colour.colorimetry.lightness.lightness` definition unit + Define :func:`colour.colorimetry.lightness.lightness` definition unit tests methods. """ def test_domain_range_scale_lightness(self): """ - Tests :func:`colour.colorimetry.lightness.lightness` definition domain + Test :func:`colour.colorimetry.lightness.lightness` definition domain and range scale support. """ - m = ('Glasser 1958', 'Wyszecki 1963', 'CIE 1976', 'Fairchild 2010', - 'Fairchild 2011') + m = ( + "Glasser 1958", + "Wyszecki 1963", + "CIE 1976", + "Fairchild 2010", + "Fairchild 2011", + "Abebe 2017", + ) v = [lightness(12.19722535, method, Y_n=100) for method in m] - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for method, value in zip(m, v): for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( lightness(12.19722535 * factor, method, Y_n=100), value * factor, - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/colorimetry/tests/test_luminance.py b/colour/colorimetry/tests/test_luminance.py index 69ad92979c..fb0d162a18 100644 --- a/colour/colorimetry/tests/test_luminance.py +++ b/colour/colorimetry/tests/test_luminance.py @@ -1,58 +1,66 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.colorimetry.luminance` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.colorimetry.luminance` module.""" import numpy as np import unittest from colour.colorimetry import ( - luminance_Newhall1943, intermediate_luminance_function_CIE1976, - luminance_CIE1976, luminance_ASTMD1535, luminance_Fairchild2010, - luminance_Fairchild2011) + luminance_Newhall1943, + intermediate_luminance_function_CIE1976, + luminance_CIE1976, + luminance_ASTMD1535, + luminance_Fairchild2010, + luminance_Fairchild2011, + luminance_Abebe2017, +) from colour.colorimetry.luminance import luminance from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestLuminanceNewhall1943', 'TestLuminanceASTMD1535', - 'TestIntermediateLuminanceFunctionCIE1976', 'TestLuminanceCIE1976', - 'TestLuminanceFairchild2010', 'TestLuminanceFairchild2011', 'TestLuminance' + "TestLuminanceNewhall1943", + "TestLuminanceASTMD1535", + "TestIntermediateLuminanceFunctionCIE1976", + "TestLuminanceCIE1976", + "TestLuminanceFairchild2010", + "TestLuminanceFairchild2011", + "TestLuminanceAbebe2017", + "TestLuminance", ] class TestLuminanceNewhall1943(unittest.TestCase): """ - Defines :func:`colour.colorimetry.luminance.luminance_Newhall1943` + Define :func:`colour.colorimetry.luminance.luminance_Newhall1943` definition unit tests methods. """ def test_luminance_Newhall1943(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_Newhall1943` + Test :func:`colour.colorimetry.luminance.luminance_Newhall1943` definition. """ self.assertAlmostEqual( - luminance_Newhall1943(4.08244375), 12.550078816731881, places=7) + luminance_Newhall1943(4.08244375), 12.550078816731881, places=7 + ) self.assertAlmostEqual( - luminance_Newhall1943(5.39132685), 23.481252371310738, places=7) + luminance_Newhall1943(5.39132685), 23.481252371310738, places=7 + ) self.assertAlmostEqual( - luminance_Newhall1943(2.97619312), 6.4514266875601924, places=7) + luminance_Newhall1943(2.97619312), 6.4514266875601924, places=7 + ) def test_n_dimensional_luminance_Newhall1943(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_Newhall1943` + Test :func:`colour.colorimetry.luminance.luminance_Newhall1943` definition n-dimensional arrays support. """ @@ -73,55 +81,60 @@ def test_n_dimensional_luminance_Newhall1943(self): def test_domain_range_scale_luminance_Newhall1943(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_Newhall1943` + Test :func:`colour.colorimetry.luminance.luminance_Newhall1943` definition domain and range scale support. """ Y = luminance_Newhall1943(4.08244375) - d_r = (('reference', 1, 1), (1, 0.1, 0.01), (100, 10, 1)) + d_r = (("reference", 1, 1), ("1", 0.1, 0.01), ("100", 10, 1)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( luminance_Newhall1943(4.08244375 * factor_a), Y * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_luminance_Newhall1943(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_Newhall1943` + Test :func:`colour.colorimetry.luminance.luminance_Newhall1943` definition nan support. """ luminance_Newhall1943( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLuminanceASTMD1535(unittest.TestCase): """ - Defines :func:`colour.colorimetry.luminance.luminance_ASTMD1535` + Define :func:`colour.colorimetry.luminance.luminance_ASTMD1535` definition unit tests methods. """ def test_luminance_ASTMD1535(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_ASTMD1535` + Test :func:`colour.colorimetry.luminance.luminance_ASTMD1535` definition. """ self.assertAlmostEqual( - luminance_ASTMD1535(4.08244375), 12.236342675366036, places=7) + luminance_ASTMD1535(4.08244375), 12.236342675366036, places=7 + ) self.assertAlmostEqual( - luminance_ASTMD1535(5.39132685), 22.893999867280378, places=7) + luminance_ASTMD1535(5.39132685), 22.893999867280378, places=7 + ) self.assertAlmostEqual( - luminance_ASTMD1535(2.97619312), 6.2902253509053132, places=7) + luminance_ASTMD1535(2.97619312), 6.2902253509053132, places=7 + ) def test_n_dimensional_luminance_ASTMD1535(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_ASTMD1535` + Test :func:`colour.colorimetry.luminance.luminance_ASTMD1535` definition n-dimensional arrays support. """ @@ -142,63 +155,68 @@ def test_n_dimensional_luminance_ASTMD1535(self): def test_domain_range_scale_luminance_ASTMD1535(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_ASTMD1535` + Test :func:`colour.colorimetry.luminance.luminance_ASTMD1535` definition domain and range scale support. """ Y = luminance_ASTMD1535(4.08244375) - d_r = (('reference', 1, 1), (1, 0.1, 0.01), (100, 10, 1)) + d_r = (("reference", 1, 1), ("1", 0.1, 0.01), ("100", 10, 1)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( luminance_ASTMD1535(4.08244375 * factor_a), Y * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_luminance_ASTMD1535(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_ASTMD1535` + Test :func:`colour.colorimetry.luminance.luminance_ASTMD1535` definition nan support. """ luminance_ASTMD1535( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestIntermediateLuminanceFunctionCIE1976(unittest.TestCase): """ - Defines :func:`colour.colorimetry.luminance.\ + Define :func:`colour.colorimetry.luminance.\ intermediate_luminance_function_CIE1976` definition unit tests methods. """ def test_intermediate_luminance_function_CIE1976(self): """ - Tests :func:`colour.colorimetry.luminance.\ + Test :func:`colour.colorimetry.luminance.\ intermediate_luminance_function_CIE1976` definition. """ self.assertAlmostEqual( intermediate_luminance_function_CIE1976(0.495929964178047), 12.197225350000002, - places=7) + places=7, + ) self.assertAlmostEqual( intermediate_luminance_function_CIE1976(0.613072093530391), 23.042767810000004, - places=7) + places=7, + ) self.assertAlmostEqual( intermediate_luminance_function_CIE1976(0.394876333449113), 6.157200790000001, - places=7) + places=7, + ) def test_n_dimensional_intermediate_luminance_function_CIE1976(self): """ - Tests :func:`colour.colorimetry.luminance.\ + Test :func:`colour.colorimetry.luminance.\ intermediate_luminance_function_CIE1976` definition n-dimensional arrays -support. + support. """ f_Y_Y_n = 0.495929964178047 @@ -207,89 +225,97 @@ def test_n_dimensional_intermediate_luminance_function_CIE1976(self): f_Y_Y_n = np.tile(f_Y_Y_n, 6) Y = np.tile(Y, 6) np.testing.assert_almost_equal( - intermediate_luminance_function_CIE1976(f_Y_Y_n), Y, decimal=7) + intermediate_luminance_function_CIE1976(f_Y_Y_n), Y, decimal=7 + ) f_Y_Y_n = np.reshape(f_Y_Y_n, (2, 3)) Y = np.reshape(Y, (2, 3)) np.testing.assert_almost_equal( - intermediate_luminance_function_CIE1976(f_Y_Y_n), Y, decimal=7) + intermediate_luminance_function_CIE1976(f_Y_Y_n), Y, decimal=7 + ) f_Y_Y_n = np.reshape(f_Y_Y_n, (2, 3, 1)) Y = np.reshape(Y, (2, 3, 1)) np.testing.assert_almost_equal( - intermediate_luminance_function_CIE1976(f_Y_Y_n), Y, decimal=7) + intermediate_luminance_function_CIE1976(f_Y_Y_n), Y, decimal=7 + ) def test_domain_range_scale_intermediate_luminance_function_CIE1976(self): """ - Tests :func:`colour.colorimetry.luminance.\ + Test :func:`colour.colorimetry.luminance.\ intermediate_luminance_function_CIE1976` definition domain and range scale -support. + support. """ Y = intermediate_luminance_function_CIE1976(41.527875844653451, 100) - for scale in ('reference', 1, 100): + for scale in ("reference", "1", "100"): with domain_range_scale(scale): np.testing.assert_almost_equal( intermediate_luminance_function_CIE1976( - 41.527875844653451, 100), + 41.527875844653451, 100 + ), Y, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_intermediate_luminance_function_CIE1976(self): """ - Tests :func:`colour.colorimetry.luminance.\ + Test :func:`colour.colorimetry.luminance.\ intermediate_luminance_function_CIE1976` definition nan support. """ intermediate_luminance_function_CIE1976( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLuminanceCIE1976(unittest.TestCase): """ - Defines :func:`colour.colorimetry.luminance.luminance_CIE1976` definition + Define :func:`colour.colorimetry.luminance.luminance_CIE1976` definition unit tests methods. """ def test_luminance_CIE1976(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_CIE1976` + Test :func:`colour.colorimetry.luminance.luminance_CIE1976` definition. """ self.assertAlmostEqual( - luminance_CIE1976(41.527875844653451), - 12.197225350000002, - places=7) + luminance_CIE1976(41.527875844653451), 12.197225350000002, places=7 + ) self.assertAlmostEqual( - luminance_CIE1976(55.116362849525402), - 23.042767810000004, - places=7) + luminance_CIE1976(55.116362849525402), 23.042767810000004, places=7 + ) self.assertAlmostEqual( - luminance_CIE1976(29.805654680097106), 6.157200790000001, places=7) + luminance_CIE1976(29.805654680097106), 6.157200790000001, places=7 + ) self.assertAlmostEqual( luminance_CIE1976(56.480581732417676, 50), 12.197225349999998, - places=7) + places=7, + ) self.assertAlmostEqual( luminance_CIE1976(47.317620274162735, 75), 12.197225350000002, - places=7) + places=7, + ) self.assertAlmostEqual( luminance_CIE1976(42.519930728120940, 95), 12.197225350000005, - places=7) + places=7, + ) def test_n_dimensional_luminance_CIE1976(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_CIE1976` + Test :func:`colour.colorimetry.luminance.luminance_CIE1976` definition n-dimensional arrays support. """ @@ -310,24 +336,25 @@ def test_n_dimensional_luminance_CIE1976(self): def test_domain_range_scale_luminance_CIE1976(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_CIE1976` + Test :func:`colour.colorimetry.luminance.luminance_CIE1976` definition domain and range scale support. """ Y = luminance_CIE1976(41.527875844653451, 100) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( luminance_CIE1976(41.527875844653451 * factor, 100), Y * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_luminance_CIE1976(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_CIE1976` + Test :func:`colour.colorimetry.luminance.luminance_CIE1976` definition nan support. """ @@ -336,49 +363,55 @@ def test_nan_luminance_CIE1976(self): class TestLuminanceFairchild2010(unittest.TestCase): """ - Defines :func:`colour.colorimetry.luminance.luminance_Fairchild2010` + Define :func:`colour.colorimetry.luminance.luminance_Fairchild2010` definition unit tests methods. """ def test_luminance_Fairchild2010(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_Fairchild2010` + Test :func:`colour.colorimetry.luminance.luminance_Fairchild2010` definition. """ self.assertAlmostEqual( luminance_Fairchild2010(31.996390226262736), 0.12197225350000002, - places=7) + places=7, + ) self.assertAlmostEqual( luminance_Fairchild2010(60.203153682783302), 0.23042767809999998, - places=7) + places=7, + ) self.assertAlmostEqual( luminance_Fairchild2010(11.836517240976489), 0.06157200790000001, - places=7) + places=7, + ) self.assertAlmostEqual( luminance_Fairchild2010(24.424283249379986, 2.75), 0.12197225350000002, - places=7) + places=7, + ) self.assertAlmostEqual( luminance_Fairchild2010(100.019986327374240), 1008.00000024, - places=7) + places=7, + ) self.assertAlmostEqual( luminance_Fairchild2010(100.019999997090270), 100799.92312466, - places=7) + places=7, + ) def test_n_dimensional_luminance_Fairchild2010(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_Fairchild2010` + Test :func:`colour.colorimetry.luminance.luminance_Fairchild2010` definition n-dimensional arrays support. """ @@ -388,90 +421,101 @@ def test_n_dimensional_luminance_Fairchild2010(self): L_hdr = np.tile(L_hdr, 6) Y = np.tile(Y, 6) np.testing.assert_almost_equal( - luminance_Fairchild2010(L_hdr), Y, decimal=7) + luminance_Fairchild2010(L_hdr), Y, decimal=7 + ) L_hdr = np.reshape(L_hdr, (2, 3)) Y = np.reshape(Y, (2, 3)) np.testing.assert_almost_equal( - luminance_Fairchild2010(L_hdr), Y, decimal=7) + luminance_Fairchild2010(L_hdr), Y, decimal=7 + ) L_hdr = np.reshape(L_hdr, (2, 3, 1)) Y = np.reshape(Y, (2, 3, 1)) np.testing.assert_almost_equal( - luminance_Fairchild2010(L_hdr), Y, decimal=7) + luminance_Fairchild2010(L_hdr), Y, decimal=7 + ) def test_domain_range_scale_luminance_Fairchild2010(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_Fairchild2010` + Test :func:`colour.colorimetry.luminance.luminance_Fairchild2010` definition domain and range scale support. """ Y = luminance_Fairchild2010(31.996390226262736) - d_r = (('reference', 1, 1), (1, 0.01, 1), (100, 1, 100)) + d_r = (("reference", 1, 1), ("1", 0.01, 1), ("100", 1, 100)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( luminance_Fairchild2010(31.996390226262736 * factor_a), Y * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_luminance_Fairchild2010(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_Fairchild2010` + Test :func:`colour.colorimetry.luminance.luminance_Fairchild2010` definition nan support. """ luminance_Fairchild2010( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLuminanceFairchild2011(unittest.TestCase): """ - Defines :func:`colour.colorimetry.luminance.luminance_Fairchild2011` + Define :func:`colour.colorimetry.luminance.luminance_Fairchild2011` definition unit tests methods. """ def test_luminance_Fairchild2011(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_Fairchild2011` + Test :func:`colour.colorimetry.luminance.luminance_Fairchild2011` definition. """ self.assertAlmostEqual( luminance_Fairchild2011(51.852958445912506), 0.12197225350000007, - places=7) + places=7, + ) self.assertAlmostEqual( luminance_Fairchild2011(65.275207956353853), 0.23042767809999998, - places=7) + places=7, + ) self.assertAlmostEqual( luminance_Fairchild2011(39.818935510715917), 0.061572007900000038, - places=7) + places=7, + ) self.assertAlmostEqual( luminance_Fairchild2011(0.13268968410139345, 2.75), 0.12197225350000002, - places=7) + places=7, + ) self.assertAlmostEqual( luminance_Fairchild2011(234.72925681957565), 1008.00000000, - places=7) + places=7, + ) self.assertAlmostEqual( luminance_Fairchild2011(245.57059778237573), 100800.00000000, - places=7) + places=7, + ) def test_n_dimensional_luminance_Fairchild2011(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_Fairchild2011` + Test :func:`colour.colorimetry.luminance.luminance_Fairchild2011` definition n-dimensional arrays support. """ @@ -481,71 +525,178 @@ def test_n_dimensional_luminance_Fairchild2011(self): L_hdr = np.tile(L_hdr, 6) Y = np.tile(Y, 6) np.testing.assert_almost_equal( - luminance_Fairchild2011(L_hdr), Y, decimal=7) + luminance_Fairchild2011(L_hdr), Y, decimal=7 + ) L_hdr = np.reshape(L_hdr, (2, 3)) Y = np.reshape(Y, (2, 3)) np.testing.assert_almost_equal( - luminance_Fairchild2011(L_hdr), Y, decimal=7) + luminance_Fairchild2011(L_hdr), Y, decimal=7 + ) L_hdr = np.reshape(L_hdr, (2, 3, 1)) Y = np.reshape(Y, (2, 3, 1)) np.testing.assert_almost_equal( - luminance_Fairchild2011(L_hdr), Y, decimal=7) + luminance_Fairchild2011(L_hdr), Y, decimal=7 + ) def test_domain_range_scale_luminance_Fairchild2011(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_Fairchild2011` + Test :func:`colour.colorimetry.luminance.luminance_Fairchild2011` definition domain and range scale support. """ Y = luminance_Fairchild2011(26.459509817572265) - d_r = (('reference', 1, 1), (1, 0.01, 1), (100, 1, 100)) + d_r = (("reference", 1, 1), ("1", 0.01, 1), ("100", 1, 100)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( luminance_Fairchild2011(26.459509817572265 * factor_a), Y * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_luminance_Fairchild2011(self): """ - Tests :func:`colour.colorimetry.luminance.luminance_Fairchild2011` + Test :func:`colour.colorimetry.luminance.luminance_Fairchild2011` definition nan support. """ luminance_Fairchild2011( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) + + +class TestLuminanceAbebe2017(unittest.TestCase): + """ + Define :func:`colour.colorimetry.luminance.luminance_Abebe2017` + definition unit tests methods. + """ + + def test_luminance_Abebe2017(self): + """ + Test :func:`colour.colorimetry.luminance.luminance_Abebe2017` + definition. + """ + + self.assertAlmostEqual( + luminance_Abebe2017(0.486955571109229), + 12.197225350000004, + places=7, + ) + + self.assertAlmostEqual( + luminance_Abebe2017(0.474544792145434, method="Stevens"), + 12.197225350000025, + places=7, + ) + + self.assertAlmostEqual( + luminance_Abebe2017(0.286847428534793, 1000), + 12.197225350000046, + places=7, + ) + + self.assertAlmostEqual( + luminance_Abebe2017(0.192145492588158, 4000), + 12.197225350000121, + places=7, + ) + + self.assertAlmostEqual( + luminance_Abebe2017(0.170365211220992, 4000, method="Stevens"), + 12.197225349999933, + places=7, + ) + + def test_n_dimensional_luminance_Abebe2017(self): + """ + Test :func:`colour.colorimetry.luminance.luminance_Abebe2017` + definition n-dimensional arrays support. + """ + + L = 0.486955571109229 + Y = luminance_Abebe2017(L) + + L = np.tile(L, 6) + Y = np.tile(Y, 6) + np.testing.assert_almost_equal(luminance_Abebe2017(L), Y, decimal=7) + + L = np.reshape(L, (2, 3)) + Y = np.reshape(Y, (2, 3)) + np.testing.assert_almost_equal(luminance_Abebe2017(L), Y, decimal=7) + + L = np.reshape(L, (2, 3, 1)) + Y = np.reshape(Y, (2, 3, 1)) + np.testing.assert_almost_equal(luminance_Abebe2017(L), Y, decimal=7) + + def test_domain_range_scale_luminance_Abebe2017(self): + """ + Test :func:`colour.colorimetry.luminance.luminance_Abebe2017` + definition domain and range scale support. + """ + + L = luminance_Abebe2017(0.486955571109229) + + d_r = (("reference", 1), ("1", 1), ("100", 1)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + luminance_Abebe2017( + 0.486955571109229 * factor, 100 * factor + ), + L * factor, + decimal=7, + ) + + @ignore_numpy_errors + def test_nan_luminance_Abebe2017(self): + """ + Test :func:`colour.colorimetry.luminance.luminance_Abebe2017` + definition nan support. + """ + + luminance_Abebe2017( + *[np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])] * 2 + ) class TestLuminance(unittest.TestCase): """ - Defines :func:`colour.colorimetry.luminance.luminance` definition unit + Define :func:`colour.colorimetry.luminance.luminance` definition unit tests methods. """ def test_domain_range_scale_luminance(self): """ - Tests :func:`colour.colorimetry.luminance.luminance` definition + Test :func:`colour.colorimetry.luminance.luminance` definition domain and range scale support. """ - m = ('Newhall 1943', 'ASTM D1535', 'CIE 1976', 'Fairchild 2010', - 'Fairchild 2011') + m = ( + "Newhall 1943", + "ASTM D1535", + "CIE 1976", + "Fairchild 2010", + "Fairchild 2011", + "Abebe 2017", + ) v = [luminance(41.527875844653451, method, Y_n=100) for method in m] - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for method, value in zip(m, v): for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( luminance( - 41.527875844653451 * factor, method, Y_n=100), + 41.527875844653451 * factor, method, Y_n=100 + ), value * factor, - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/colorimetry/tests/test_photometry.py b/colour/colorimetry/tests/test_photometry.py index 8fa0390c5d..caff76ef28 100644 --- a/colour/colorimetry/tests/test_photometry.py +++ b/colour/colorimetry/tests/test_photometry.py @@ -1,115 +1,125 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.colorimetry.photometry` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.colorimetry.photometry` module.""" import unittest -from colour.colorimetry import (SDS_ILLUMINANTS, SDS_LIGHT_SOURCES, - luminous_flux, luminous_efficiency, - luminous_efficacy, sd_zeros) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.colorimetry import ( + SDS_ILLUMINANTS, + SDS_LIGHT_SOURCES, + luminous_flux, + luminous_efficiency, + luminous_efficacy, + sd_zeros, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'TestLuminousFlux', 'TestLuminousEfficiency', 'TestLuminousEfficacy' + "TestLuminousFlux", + "TestLuminousEfficiency", + "TestLuminousEfficacy", ] class TestLuminousFlux(unittest.TestCase): """ - Defines :func:`colour.colorimetry.photometry.luminous_flux` definition unit + Define :func:`colour.colorimetry.photometry.luminous_flux` definition unit tests methods. """ def test_luminous_flux(self): - """ - Tests :func:`colour.colorimetry.photometry.luminous_flux` definition. - """ + """Test :func:`colour.colorimetry.photometry.luminous_flux` definition.""" self.assertAlmostEqual( - luminous_flux(SDS_ILLUMINANTS['FL2'].copy().normalise()), + luminous_flux(SDS_ILLUMINANTS["FL2"].copy().normalise()), 28588.73612977, - places=7) + places=7, + ) self.assertAlmostEqual( - luminous_flux(SDS_LIGHT_SOURCES['Neodimium Incandescent']), + luminous_flux(SDS_LIGHT_SOURCES["Neodimium Incandescent"]), 23807.65552737, - places=7) + places=7, + ) self.assertAlmostEqual( - luminous_flux(SDS_LIGHT_SOURCES['F32T8/TL841 (Triphosphor)']), + luminous_flux(SDS_LIGHT_SOURCES["F32T8/TL841 (Triphosphor)"]), 13090.06759053, - places=7) + places=7, + ) class TestLuminousEfficiency(unittest.TestCase): """ - Defines :func:`colour.colorimetry.photometry.luminous_efficiency` + Define :func:`colour.colorimetry.photometry.luminous_efficiency` definition unit tests methods. """ def test_luminous_efficiency(self): """ - Tests :func:`colour.colorimetry.photometry.luminous_efficiency` + Test :func:`colour.colorimetry.photometry.luminous_efficiency` definition. """ self.assertAlmostEqual( - luminous_efficiency(SDS_ILLUMINANTS['FL2'].copy().normalise()), + luminous_efficiency(SDS_ILLUMINANTS["FL2"].copy().normalise()), 0.49317624, - places=7) + places=7, + ) self.assertAlmostEqual( - luminous_efficiency(SDS_LIGHT_SOURCES['Neodimium Incandescent']), + luminous_efficiency(SDS_LIGHT_SOURCES["Neodimium Incandescent"]), 0.19943936, - places=7) + places=7, + ) self.assertAlmostEqual( luminous_efficiency( - SDS_LIGHT_SOURCES['F32T8/TL841 (Triphosphor)']), + SDS_LIGHT_SOURCES["F32T8/TL841 (Triphosphor)"] + ), 0.51080919, - places=7) + places=7, + ) class TestLuminousEfficacy(unittest.TestCase): """ - Defines :func:`colour.colorimetry.photometry.luminous_efficacy` + Define :func:`colour.colorimetry.photometry.luminous_efficacy` definition unit tests methods. """ def test_luminous_efficacy(self): """ - Tests :func:`colour.colorimetry.photometry.luminous_efficacy` + Test :func:`colour.colorimetry.photometry.luminous_efficacy` definition. """ self.assertAlmostEqual( - luminous_efficacy(SDS_ILLUMINANTS['FL2'].copy().normalise()), + luminous_efficacy(SDS_ILLUMINANTS["FL2"].copy().normalise()), 336.83937176, - places=7) + places=7, + ) self.assertAlmostEqual( - luminous_efficacy(SDS_LIGHT_SOURCES['Neodimium Incandescent']), + luminous_efficacy(SDS_LIGHT_SOURCES["Neodimium Incandescent"]), 136.21708032, - places=7) + places=7, + ) self.assertAlmostEqual( - luminous_efficacy(SDS_LIGHT_SOURCES['F32T8/TL841 (Triphosphor)']), + luminous_efficacy(SDS_LIGHT_SOURCES["F32T8/TL841 (Triphosphor)"]), 348.88267549, - places=7) + places=7, + ) sd = sd_zeros() sd[555] = 1 self.assertAlmostEqual(luminous_efficacy(sd), 683.00000000, places=7) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/colorimetry/tests/test_spectrum.py b/colour/colorimetry/tests/test_spectrum.py index 706642dad8..4884718274 100644 --- a/colour/colorimetry/tests/test_spectrum.py +++ b/colour/colorimetry/tests/test_spectrum.py @@ -1,37 +1,51 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.colorimetry.spectrum` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.colorimetry.spectrum` module.""" +import colour import numpy as np import unittest import scipy from distutils.version import LooseVersion +from colour.algebra import CubicSplineInterpolator +from colour.colorimetry.spectrum import SPECTRAL_SHAPE_DEFAULT from colour.colorimetry.spectrum import ( - SpectralShape, SpectralDistribution, MultiSpectralDistributions, - sds_and_msds_to_sds, sds_and_msds_to_msds) + SpectralShape, + SpectralDistribution, + MultiSpectralDistributions, + reshape_sd, + reshape_msds, + sds_and_msds_to_sds, + sds_and_msds_to_msds, +) +from colour.hints import Dict, Tuple from colour.utilities import tstack -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'DATA_SAMPLE', 'DATA_SAMPLE_NON_UNIFORM', 'DATA_SAMPLE_INTERPOLATED', - 'DATA_SAMPLE_INTERPOLATED_NON_UNIFORM', 'DATA_SAMPLE_NORMALISED', - 'DATA_STANDARD_OBSERVER_2_DEGREE_CIE1931', 'DATA_CMFS', - 'DATA_SAMPLE_ABRIDGED', 'DATA_MULTI_SAMPLE_ABRIDGED', 'TestSpectralShape', - 'TestSpectralDistribution', 'TestMultiSpectralDistributions', - 'TestSdsAndMdsToSds', 'TestSdsAndMsdsToMsds' + "DATA_SAMPLE", + "DATA_SAMPLE_NON_UNIFORM", + "DATA_SAMPLE_INTERPOLATED", + "DATA_SAMPLE_INTERPOLATED_NON_UNIFORM", + "DATA_SAMPLE_NORMALISED", + "DATA_STANDARD_OBSERVER_2_DEGREE_CIE1931", + "DATA_CMFS", + "DATA_SAMPLE_ABRIDGED", + "DATA_MULTI_SAMPLE_ABRIDGED", + "TestSpectralShape", + "TestSpectralDistribution", + "TestMultiSpectralDistributions", + "TestReshapeSd", + "TestSdsAndMdsToSds", + "TestSdsAndMsdsToMsds", ] -DATA_SAMPLE = { +DATA_SAMPLE: Dict = { 340: 0.0000, 360: 0.0000, 380: 0.0000, @@ -56,10 +70,10 @@ 760: 0.0000, 780: 0.0000, 800: 0.0000, - 820: 0.0000 + 820: 0.0000, } -DATA_SAMPLE_NON_UNIFORM = { +DATA_SAMPLE_NON_UNIFORM: Dict = { 391.898: 16.331740, 392.069: 16.333122, 405.606: 40.197224, @@ -113,10 +127,10 @@ 723.642: 16.333122, 761.265: 41.342187, 786.089: 8.850659, - 805.862: 8.850659 + 805.862: 8.850659, } -DATA_SAMPLE_INTERPOLATED = ( +DATA_SAMPLE_INTERPOLATED: Tuple = ( 0.000000000000000, 0.000230709627131, 0.000384144814593, @@ -597,9 +611,10 @@ 0.000000000000000, 0.000000000000000, 0.000000000000000, - 0.000000000000000) # yapf: disable + 0.000000000000000, +) -DATA_SAMPLE_INTERPOLATED_NON_UNIFORM = ( +DATA_SAMPLE_INTERPOLATED_NON_UNIFORM: Tuple = ( 16.329808636577400, 16.722487609243078, 17.780769796558388, @@ -1013,23 +1028,38 @@ 4.521982149490528, 5.392381559425689, 6.434033619962720, - 7.652991396265083) # yapf: disable - -DATA_SAMPLE_NORMALISED = (0.000000000000000, 0.000000000000000, - 0.000000000000000, 22.475455820476860, - 22.615708274894811, 19.705469845722302, - 18.828892005610097, 19.600280504908834, - 22.826086956521742, 24.719495091164092, - 27.068723702664798, 30.504908835904626, - 39.551192145862551, 47.685834502103788, - 52.980364656381497, 59.186535764375883, - 69.985974754558200, 84.046283309957929, - 100.000000000000000, 0.000000000000000, - 0.000000000000000, 0.000000000000000, - 0.000000000000000, 0.000000000000000, - 0.000000000000000) - -DATA_STANDARD_OBSERVER_2_DEGREE_CIE1931 = { + 7.652991396265083, +) + +DATA_SAMPLE_NORMALISED: Tuple = ( + 0.000000000000000, + 0.000000000000000, + 0.000000000000000, + 22.475455820476860, + 22.615708274894811, + 19.705469845722302, + 18.828892005610097, + 19.600280504908834, + 22.826086956521742, + 24.719495091164092, + 27.068723702664798, + 30.504908835904626, + 39.551192145862551, + 47.685834502103788, + 52.980364656381497, + 59.186535764375883, + 69.985974754558200, + 84.046283309957929, + 100.000000000000000, + 0.000000000000000, + 0.000000000000000, + 0.000000000000000, + 0.000000000000000, + 0.000000000000000, + 0.000000000000000, +) + +DATA_STANDARD_OBSERVER_2_DEGREE_CIE1931: Dict = { 380: (0.001368, 0.000039, 0.006450), 385: (0.002236, 0.000064, 0.010550), 390: (0.004243, 0.000120, 0.020050), @@ -1110,10 +1140,10 @@ 765: (0.000117, 0.000042, 0.000000), 770: (0.000083, 0.000030, 0.000000), 775: (0.000059, 0.000021, 0.000000), - 780: (0.000042, 0.000015, 0.000000) + 780: (0.000042, 0.000015, 0.000000), } -DATA_CMFS = { +DATA_CMFS: Dict = { 380: np.array([0.001368, 3.90e-05, 0.006450]), 385: np.array([0.002236, 6.40e-05, 0.010550]), 390: np.array([0.004243, 0.000120, 0.020050]), @@ -1194,60 +1224,65 @@ 765: np.array([0.000117, 4.20e-05, 0.000000]), 770: np.array([8.30e-05, 3.00e-05, 0.000000]), 775: np.array([5.90e-05, 2.10e-05, 0.000000]), - 780: np.array([4.20e-05, 1.50e-05, 0.000000]) + 780: np.array([4.20e-05, 1.50e-05, 0.000000]), } -DATA_SAMPLE_ABRIDGED = { +DATA_SAMPLE_ABRIDGED: Dict = { 500: 0.0651, 520: 0.0705, 540: 0.0772, 560: 0.0870, 580: 0.1128, - 600: 0.1360 + 600: 0.1360, } -DATA_MULTI_SAMPLE_ABRIDGED = { +DATA_MULTI_SAMPLE_ABRIDGED: Dict = { 500: (0.004900, 0.323000, 0.272000), 510: (0.009300, 0.503000, 0.158200), 520: (0.063270, 0.710000, 0.078250), 530: (0.165500, 0.862000, 0.042160), 540: (0.290400, 0.954000, 0.020300), 550: (0.433450, 0.994950, 0.008750), - 560: (0.594500, 0.995000, 0.003900) + 560: (0.594500, 0.995000, 0.003900), } class TestSpectralShape(unittest.TestCase): """ - Defines :class:`colour.colorimetry.spectrum.SpectralShape` class unit tests + Define :class:`colour.colorimetry.spectrum.SpectralShape` class unit tests methods. """ def test_required_attributes(self): - """ - Tests presence of required attributes. - """ + """Test the presence of required attributes.""" - required_attributes = ('start', 'end', 'interval', 'boundaries') + required_attributes = ("start", "end", "interval", "boundaries") for attribute in required_attributes: self.assertIn(attribute, dir(SpectralShape)) def test_required_methods(self): - """ - Tests presence of required methods. - """ - - required_methods = ('__init__', '__str__', '__repr__', '__hash__', - '__iter__', '__contains__', '__len__', '__eq__', - '__ne__', 'range') + """Test the presence of required methods.""" + + required_methods = ( + "__init__", + "__str__", + "__repr__", + "__hash__", + "__iter__", + "__contains__", + "__len__", + "__eq__", + "__ne__", + "range", + ) for method in required_methods: self.assertIn(method, dir(SpectralShape)) def test_start(self): """ - Tests :attr:`colour.colorimetry.spectrum.SpectralShape.start` + Test :attr:`colour.colorimetry.spectrum.SpectralShape.start` attribute. """ @@ -1258,9 +1293,7 @@ def test_start(self): self.assertRaises(AssertionError, lambda: SpectralShape(360, 0, 1)) def test_end(self): - """ - Tests :attr:`colour.colorimetry.spectrum.SpectralShape.end` attribute. - """ + """Test :attr:`colour.colorimetry.spectrum.SpectralShape.end` property.""" self.assertEqual(SpectralShape(360, 830, 1).end, 830) @@ -1270,19 +1303,19 @@ def test_end(self): def test_interval(self): """ - Tests :attr:`colour.colorimetry.spectrum.SpectralShape.interval` - attribute. + Test :attr:`colour.colorimetry.spectrum.SpectralShape.interval` + property. """ self.assertEqual(SpectralShape(360, 830, 1).interval, 1) def test_boundaries(self): """ - Tests :attr:`colour.colorimetry.spectrum.SpectralShape.boundaries` - attribute. + Test :attr:`colour.colorimetry.spectrum.SpectralShape.boundaries` + property. """ - shape = SpectralShape() + shape = SpectralShape(400, 700, 1) shape.boundaries = (360, 830) self.assertEqual(shape.start, 360) @@ -1290,7 +1323,7 @@ def test_boundaries(self): def test__hash__(self): """ - Tests :func:`colour.colorimetry.spectrum.SpectralShape.__hash__` + Test :meth:`colour.colorimetry.spectrum.SpectralShape.__hash__` method. """ @@ -1298,17 +1331,18 @@ def test__hash__(self): def test__iter__(self): """ - Tests :func:`colour.colorimetry.spectrum.SpectralShape.__iter__` + Test :meth:`colour.colorimetry.spectrum.SpectralShape.__iter__` method. """ np.testing.assert_almost_equal( [wavelength for wavelength in SpectralShape(0, 10, 0.1)], - np.arange(0, 10 + 0.1, 0.1)) + np.arange(0, 10 + 0.1, 0.1), + ) def test__contains__(self): """ - Tests :func:`colour.colorimetry.spectrum.SpectralShape.__contains__` + Test :meth:`colour.colorimetry.spectrum.SpectralShape.__contains__` method. """ @@ -1322,7 +1356,7 @@ def test__contains__(self): def test__len__(self): """ - Tests :func:`colour.colorimetry.spectrum.SpectralShape.__len__` + Test :meth:`colour.colorimetry.spectrum.SpectralShape.__len__` method. """ @@ -1330,94 +1364,89 @@ def test__len__(self): def test__eq__(self): """ - Tests :func:`colour.colorimetry.spectrum.SpectralShape.__eq__` + Test :meth:`colour.colorimetry.spectrum.SpectralShape.__eq__` method. """ self.assertEqual(SpectralShape(0, 10, 0.1), SpectralShape(0, 10, 0.1)) + self.assertNotEqual(SpectralShape(0, 10, 0.1), None) def test__ne__(self): """ - Tests :func:`colour.colorimetry.spectrum.SpectralShape.__ne__` + Test :meth:`colour.colorimetry.spectrum.SpectralShape.__ne__` method. """ self.assertNotEqual( - SpectralShape(0, 10, 0.1), SpectralShape(1, 10, 0.1)) + SpectralShape(0, 10, 0.1), SpectralShape(1, 10, 0.1) + ) def test_range(self): - """ - Tests :func:`colour.colorimetry.spectrum.SpectralShape.range` method. - """ + """Test :func:`colour.colorimetry.spectrum.SpectralShape.range` method.""" np.testing.assert_almost_equal( [wavelength for wavelength in SpectralShape(0, 10, 0.1)], - np.arange(0, 10 + 0.1, 0.1)) - - def test_raise_exception_range(self): - """ - Tests :func:`colour.colorimetry.spectrum.SpectralShape.range` method - raised exception. - """ - - self.assertRaises(RuntimeError, SpectralShape().range) + np.arange(0, 10 + 0.1, 0.1), + ) class TestSpectralDistribution(unittest.TestCase): """ - Defines :class:`colour.colorimetry.spectrum.SpectralDistribution` + Define :class:`colour.colorimetry.spectrum.SpectralDistribution` class unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" - self._sd = SpectralDistribution(DATA_SAMPLE, name='Sample') + self._sd = SpectralDistribution(DATA_SAMPLE, name="Sample") self._non_uniform_sd = SpectralDistribution( DATA_SAMPLE_NON_UNIFORM, - name='Non Uniform Sample', - strict_name='Strict Non Uniform Sample') + name="Non Uniform Sample", + strict_name="Strict Non Uniform Sample", + ) self._phi = (1 + np.sqrt(5)) / 2 def test_required_attributes(self): - """ - Tests presence of required attributes. - """ + """Test the presence of required attributes.""" - required_attributes = ('strict_name', 'wavelengths', 'values', 'shape') + required_attributes = ("strict_name", "wavelengths", "values", "shape") for attribute in required_attributes: self.assertIn(attribute, dir(SpectralDistribution)) def test_required_methods(self): - """ - Tests presence of required methods. - """ - - required_methods = ('__init__', 'interpolate', 'extrapolate', 'align', - 'trim', 'normalise') + """Test the presence of required methods.""" + + required_methods = ( + "__init__", + "interpolate", + "extrapolate", + "align", + "trim", + "normalise", + ) for method in required_methods: self.assertIn(method, dir(SpectralDistribution)) def test_strict_name(self): """ - Tests :attr:`colour.colorimetry.spectrum.\ -SpectralDistribution.strict_name` attribute. + Test :attr:`colour.colorimetry.spectrum.SpectralDistribution.strict_name` + property. """ - self.assertEqual(self._sd.strict_name, 'Sample') - self.assertEqual(self._non_uniform_sd.strict_name, - 'Strict Non Uniform Sample') + self.assertEqual(self._sd.strict_name, "Sample") + self.assertEqual( + self._non_uniform_sd.strict_name, "Strict Non Uniform Sample" + ) def test_wavelengths(self): """ - Tests :attr:`colour.colorimetry.spectrum.\ -SpectralDistribution.wavelengths` attribute. + Test :attr:`colour.colorimetry.spectrum.SpectralDistribution.wavelengths` + property. """ np.testing.assert_array_equal(self._sd.wavelengths, self._sd.domain) @@ -1428,8 +1457,8 @@ def test_wavelengths(self): def test_values(self): """ - Tests :attr:`colour.colorimetry.spectrum.\ -SpectralDistribution.values` attribute. + Test :attr:`colour.colorimetry.spectrum.SpectralDistribution.values` + property. """ np.testing.assert_array_equal(self._sd.values, self._sd.range) @@ -1440,65 +1469,94 @@ def test_values(self): def test_shape(self): """ - Tests :attr:`colour.colorimetry.spectrum.\ -SpectralDistribution.shape` attribute. + Test :attr:`colour.colorimetry.spectrum.SpectralDistribution.shape` + property. """ self.assertEqual(self._sd.shape, SpectralShape(340, 820, 20)) + def test__init__(self): + """ + Test :meth:`colour.colorimetry.spectrum.SpectralDistribution.__init__` + method. + """ + + np.testing.assert_almost_equal( + SpectralDistribution(DATA_SAMPLE).wavelengths, + SpectralDistribution( + DATA_SAMPLE.values(), + SpectralShape(340, 820, 20), + ).wavelengths, + ) + def test_interpolate(self): """ - Tests :func:`colour.colorimetry.spectrum.\ + Test :func:`colour.colorimetry.spectrum.\ SpectralDistribution.interpolate` method. """ np.testing.assert_almost_equal( - self._sd.copy().interpolate(SpectralShape(interval=1)).values, + reshape_sd( + self._sd, + SpectralShape(self._sd.shape.start, self._sd.shape.end, 1), + "Interpolate", + ).values, DATA_SAMPLE_INTERPOLATED, - decimal=7) + decimal=7, + ) # TODO: Remove statement whenever we make "Scipy" 0.19.0 the minimum # version. # Skipping tests because of "Scipy" 0.19.0 interpolation code changes. - if LooseVersion(scipy.__version__) < LooseVersion('0.19.0'): + if LooseVersion(scipy.__version__) < LooseVersion("0.19.0"): return # pragma: no cover np.testing.assert_allclose( - self._non_uniform_sd.copy().interpolate( - SpectralShape(interval=1)).values, + reshape_sd( + self._non_uniform_sd, + SpectralShape( + self._non_uniform_sd.shape.start, + self._non_uniform_sd.shape.end, + 1, + ), + "Interpolate", + ).values, DATA_SAMPLE_INTERPOLATED_NON_UNIFORM, rtol=0.0000001, - atol=0.0000001) + atol=0.0000001, + ) def test_extrapolate(self): """ - Tests :func:`colour.colorimetry.spectrum.\ + Test :func:`colour.colorimetry.spectrum.\ SpectralDistribution.extrapolate` method. """ data = dict(zip(range(25, 35), [0] * 5 + [1] * 5)) sd = SpectralDistribution(data) - sd.extrapolate(SpectralShape(10, 50)) + sd.extrapolate(SpectralShape(10, 50, 5)) self.assertAlmostEqual(sd[10], 0, places=7) self.assertAlmostEqual(sd[50], 1, places=7) sd = SpectralDistribution( - np.linspace(0, 1, 10), np.linspace(25, 35, 10)) + np.linspace(0, 1, 10), np.linspace(25, 35, 10) + ) sd.extrapolate( - SpectralShape(10, 50), + SpectralShape(10, 50, 10), extrapolator_kwargs={ - 'method': 'Linear', - 'left': None, - 'right': None - }) + "method": "Linear", + "left": None, + "right": None, + }, + ) self.assertAlmostEqual(sd[10], -1.5000000000000004, places=7) self.assertAlmostEqual(sd[50], 2.4999999999999964, places=7) def test_align(self): """ - Tests :func:`colour.colorimetry.spectrum.\ + Test :func:`colour.colorimetry.spectrum.\ SpectralDistribution.align` method. """ @@ -1510,7 +1568,7 @@ def test_align(self): def test_trim(self): """ - Tests :func:`colour.colorimetry.spectrum.\ + Test :func:`colour.colorimetry.spectrum.\ SpectralDistribution.trim` method. """ @@ -1522,33 +1580,36 @@ def test_trim(self): def test_normalise(self): """ - Tests :func:`colour.colorimetry.spectrum.\ + Test :func:`colour.colorimetry.spectrum.\ SpectralDistribution.normalise` method. """ - np.testing.assert_almost_equal(self._sd.copy().normalise(100).values, - DATA_SAMPLE_NORMALISED) + np.testing.assert_almost_equal( + self._sd.copy().normalise(100).values, DATA_SAMPLE_NORMALISED + ) class TestMultiSpectralDistributions(unittest.TestCase): """ - Defines :class:`colour.colorimetry.spectrum.MultiSpectralDistributions` + Define :class:`colour.colorimetry.spectrum.MultiSpectralDistributions` class unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" - self._labels = ('x_bar', 'y_bar', 'z_bar') - self._strict_labels = ('Strict x_bar', 'Strict y_bar', - 'Strict z_bar') + self._labels = ("x_bar", "y_bar", "z_bar") + self._strict_labels = ( + "Strict x_bar", + "Strict y_bar", + "Strict z_bar", + ) self._msds = MultiSpectralDistributions( DATA_STANDARD_OBSERVER_2_DEGREE_CIE1931, - name='Observer', - labels=self._labels) + name="Observer", + labels=self._labels, + ) sd = SpectralDistribution(DATA_SAMPLE) domain = sd.domain @@ -1556,7 +1617,7 @@ def setUp(self): self._sample_msds = MultiSpectralDistributions( range_, domain, - name='Sample Observer', + name="Sample Observer", labels=self._labels, ) @@ -1566,53 +1627,65 @@ def setUp(self): self._non_uniform_sample_msds = MultiSpectralDistributions( range_, domain, - name='Non Uniform Sample Observer', - strict_name='Strict Non Uniform Sample Observer', + name="Non Uniform Sample Observer", + strict_name="Strict Non Uniform Sample Observer", labels=self._labels, - strict_labels=('Strict x_bar', 'Strict y_bar', 'Strict z_bar')) + strict_labels=("Strict x_bar", "Strict y_bar", "Strict z_bar"), + ) self._phi = (1 + np.sqrt(5)) / 2 def test_required_attributes(self): - """ - Tests presence of required attributes. - """ - - required_attributes = ('strict_name', 'strict_labels', 'wavelengths', - 'values', 'shape') + """Test the presence of required attributes.""" + + required_attributes = ( + "strict_name", + "strict_labels", + "wavelengths", + "values", + "shape", + ) for attribute in required_attributes: self.assertIn(attribute, dir(MultiSpectralDistributions)) def test_required_methods(self): - """ - Tests presence of required methods. - """ - - required_methods = ('__init__', 'interpolate', 'extrapolate', 'align', - 'trim', 'normalise', 'to_sds') + """Test the presence of required methods.""" + + required_methods = ( + "__init__", + "interpolate", + "extrapolate", + "align", + "trim", + "normalise", + "to_sds", + ) for method in required_methods: self.assertIn(method, dir(MultiSpectralDistributions)) def test_strict_name(self): """ - Tests :attr:`colour.colorimetry.spectrum.\ -MultiSpectralDistributions.strict_name` attribute. + Test :attr:`colour.colorimetry.spectrum.MultiSpectralDistributions.strict_name` + property. """ - self.assertEqual(self._sample_msds.strict_name, 'Sample Observer') - self.assertEqual(self._non_uniform_sample_msds.strict_name, - 'Strict Non Uniform Sample Observer') + self.assertEqual(self._sample_msds.strict_name, "Sample Observer") + self.assertEqual( + self._non_uniform_sample_msds.strict_name, + "Strict Non Uniform Sample Observer", + ) def test_wavelengths(self): """ - Tests :attr:`colour.colorimetry.spectrum.\ -MultiSpectralDistributions.wavelengths` attribute. + Test :attr:`colour.colorimetry.spectrum.MultiSpectralDistributions.wavelengths` + property. """ - np.testing.assert_array_equal(self._msds.wavelengths, - self._msds.domain) + np.testing.assert_array_equal( + self._msds.wavelengths, self._msds.domain + ) msds = self._msds.copy() msds.wavelengths = msds.wavelengths + 10 @@ -1620,8 +1693,8 @@ def test_wavelengths(self): def test_values(self): """ - Tests :attr:`colour.colorimetry.spectrum.\ -MultiSpectralDistributions.values` attribute. + Test :attr:`colour.colorimetry.spectrum.MultiSpectralDistributions.values` + property. """ np.testing.assert_array_equal(self._msds.values, self._msds.range) @@ -1632,84 +1705,121 @@ def test_values(self): def test_strict_labels(self): """ - Tests :attr:`colour.colorimetry.spectrum.\ -MultiSpectralDistributions.strict_labels` attribute. + Test :attr:`colour.colorimetry.spectrum.MultiSpectralDistributions.\ +strict_labels` property. """ self.assertTupleEqual( - tuple(self._sample_msds.strict_labels), self._labels) + tuple(self._sample_msds.strict_labels), self._labels + ) self.assertEqual( tuple(self._non_uniform_sample_msds.strict_labels), - ('Strict x_bar', 'Strict y_bar', 'Strict z_bar')) + ("Strict x_bar", "Strict y_bar", "Strict z_bar"), + ) def test_shape(self): """ - Tests :attr:`colour.colorimetry.spectrum.\ -MultiSpectralDistributions.shape` attribute. + Test :attr:`colour.colorimetry.spectrum.MultiSpectralDistributions.shape` + property. """ self.assertEqual(self._msds.shape, SpectralShape(380, 780, 5)) + def test__init__(self): + """ + Test :func:`colour.colorimetry.spectrum.\ +MultiSpectralDistributions.__init__` method. + """ + + np.testing.assert_almost_equal( + MultiSpectralDistributions(DATA_CMFS).wavelengths, + MultiSpectralDistributions( + DATA_CMFS.values(), + SpectralShape(380, 780, 5), + ).wavelengths, + ) + def test_interpolate(self): """ - Tests :func:`colour.colorimetry.spectrum.\ + Test :func:`colour.colorimetry.spectrum.\ MultiSpectralDistributions.interpolate` method. """ - msds = self._sample_msds.copy() - - msds.interpolate(SpectralShape(interval=1)) + # pylint: disable=E1102 + msds = reshape_msds( + self._sample_msds, + SpectralShape( + self._sample_msds.shape.start, self._sample_msds.shape.end, 1 + ), + "Interpolate", + ) for signal in msds.signals.values(): np.testing.assert_almost_equal( - signal.values, DATA_SAMPLE_INTERPOLATED, decimal=7) + signal.values, DATA_SAMPLE_INTERPOLATED, decimal=7 + ) # TODO: Remove statement whenever we make "Scipy" 0.19.0 the minimum # version. # Skipping tests because of "Scipy" 0.19.0 interpolation code changes. - if LooseVersion(scipy.__version__) < LooseVersion('0.19.0'): + if LooseVersion(scipy.__version__) < LooseVersion("0.19.0"): return # pragma: no cover - msds = self._non_uniform_sample_msds.copy() - msds.interpolate(SpectralShape(interval=1)) + # pylint: disable=E1102 + msds = reshape_msds( + self._non_uniform_sample_msds, + SpectralShape( + self._non_uniform_sample_msds.shape.start, + self._non_uniform_sample_msds.shape.end, + 1, + ), + "Interpolate", + ) for signal in msds.signals.values(): np.testing.assert_allclose( signal.values, DATA_SAMPLE_INTERPOLATED_NON_UNIFORM, rtol=0.0000001, - atol=0.0000001) + atol=0.0000001, + ) def test_extrapolate(self): """ - Tests :func:`colour.colorimetry.spectrum.\ + Test :func:`colour.colorimetry.spectrum.\ MultiSpectralDistributions.extrapolate` method. """ data = dict(zip(range(25, 35), tstack([[0] * 5 + [1] * 5] * 3))) msds = MultiSpectralDistributions(data) - msds.extrapolate(SpectralShape(10, 50)) + msds.extrapolate(SpectralShape(10, 50, 5)) np.testing.assert_almost_equal( - msds[10], np.array([0.0, 0.0, 0.0]), decimal=7) + msds[10], np.array([0.0, 0.0, 0.0]), decimal=7 + ) np.testing.assert_almost_equal( - msds[50], np.array([1.0, 1.0, 1.0]), decimal=7) + msds[50], np.array([1.0, 1.0, 1.0]), decimal=7 + ) msds = MultiSpectralDistributions( - tstack([np.linspace(0, 1, 10)] * 3), np.linspace(25, 35, 10)) + tstack([np.linspace(0, 1, 10)] * 3), np.linspace(25, 35, 10) + ) msds.extrapolate( - SpectralShape(10, 50), + SpectralShape(10, 50, 10), extrapolator_kwargs={ - 'method': 'Linear', - 'left': None, - 'right': None - }) + "method": "Linear", + "left": None, + "right": None, + }, + ) np.testing.assert_almost_equal( - msds[10], np.array([-1.5, -1.5, -1.5]), decimal=7) + msds[10], np.array([-1.5, -1.5, -1.5]), decimal=7 + ) np.testing.assert_almost_equal( - msds[50], np.array([2.5, 2.5, 2.5]), decimal=7) + msds[50], np.array([2.5, 2.5, 2.5]), decimal=7 + ) def test_align(self): """ - Tests :func:`colour.colorimetry.spectrum.\ + Test :func:`colour.colorimetry.spectrum.\ MultiSpectralDistributions.align` method. """ @@ -1723,7 +1833,7 @@ def test_align(self): def test_trim(self): """ - Tests :func:`colour.colorimetry.spectrum.\ + Test :func:`colour.colorimetry.spectrum.\ MultiSpectralDistributions.trim` method. """ @@ -1735,17 +1845,18 @@ def test_trim(self): def test_normalise(self): """ - Tests :func:`colour.colorimetry.spectrum. -MultiSpectralDistributions.normalise` method. + Test :func:`colour.colorimetry.spectrum. + MultiSpectralDistributions.normalise` method. """ np.testing.assert_almost_equal( self._sample_msds.copy().normalise(100).values, - tstack([DATA_SAMPLE_NORMALISED] * 3)) + tstack([DATA_SAMPLE_NORMALISED] * 3), + ) def test_to_sds(self): """ - Tests :func:`colour.colorimetry.spectrum.\ + Test :func:`colour.colorimetry.spectrum.\ MultiSpectralDistributions.to_sds` method. """ @@ -1753,59 +1864,113 @@ def test_to_sds(self): self.assertEqual(len(sds), 3) for i, sd in enumerate(sds): - self.assertEqual( - sd.name, '{0} - {1}'.format( - self._labels[i], self._non_uniform_sample_msds.name)) - self.assertEqual( - sd.strict_name, '{0} - {1}'.format( - self._strict_labels[i], - self._non_uniform_sample_msds.strict_name)) + self.assertEqual(sd.name, self._labels[i]) + self.assertEqual(sd.strict_name, self._strict_labels[i]) + + +class TestReshapeSd(unittest.TestCase): + """ + Define :func:`colour.colorimetry.spectrum.reshape_sd` definition unit + tests methods. + """ + + def test_reshape_sd(self): + """Test :func:`colour.colorimetry.spectrum.reshape_sd` definition.""" + + sd = SpectralDistribution(DATA_SAMPLE_ABRIDGED) + sd_reshaped = reshape_sd(sd) + self.assertEqual(sd_reshaped, sd.copy().align(SPECTRAL_SHAPE_DEFAULT)) + self.assertEqual(reshape_sd(sd), sd_reshaped) + + shape = colour.SpectralShape(100, 900, 1) + extrapolator_kwargs = { + "method": "Constant", + "left": 0.05, + "right": 0.15, + } + sd_reshaped = reshape_sd( + sd, + shape, + method="Extrapolate", + extrapolator_kwargs=extrapolator_kwargs, + ) + self.assertEqual( + sd_reshaped, + sd.copy().extrapolate( + shape, extrapolator_kwargs=extrapolator_kwargs + ), + ) + + shape = colour.SpectralShape(400, 700, 1) + interpolator_kwargs = {"fill_value": 0} + sd_reshaped = reshape_sd( + sd, + shape, + method="Interpolate", + interpolator=CubicSplineInterpolator, + interpolator_kwargs=interpolator_kwargs, + ) + self.assertEqual( + sd_reshaped, + sd.copy().interpolate( + shape, + interpolator=CubicSplineInterpolator, + interpolator_kwargs=interpolator_kwargs, + ), + ) + + sd = SpectralDistribution(DATA_SAMPLE) + shape = colour.SpectralShape(500, 600, 1) + sd_reshaped = reshape_sd(sd, shape, method="Trim") + self.assertEqual(sd_reshaped, sd.copy().trim(shape)) class TestSdsAndMdsToSds(unittest.TestCase): """ - Defines :func:`colour.colorimetry.spectrum.sds_and_msds_to_sds` definition + Define :func:`colour.colorimetry.spectrum.sds_and_msds_to_sds` definition unit tests methods. """ def test_sds_and_msds_to_sds(self): """ - Tests :func:`colour.colorimetry.spectrum.sds_and_msds_to_sds` + Test :func:`colour.colorimetry.spectrum.sds_and_msds_to_sds` definition. """ - self.assertIsNone(sds_and_msds_to_sds([])) - sd_1 = SpectralDistribution(DATA_SAMPLE_ABRIDGED) sd_2 = SpectralDistribution(DATA_SAMPLE_ABRIDGED) multi_sds_1 = MultiSpectralDistributions(DATA_MULTI_SAMPLE_ABRIDGED) multi_sds_2 = MultiSpectralDistributions(DATA_MULTI_SAMPLE_ABRIDGED) self.assertEqual( - len(sds_and_msds_to_sds([ - sd_1, - sd_2, - multi_sds_1, - multi_sds_2, - ])), 8) + len( + sds_and_msds_to_sds( + [ + sd_1, + sd_2, + multi_sds_1, + multi_sds_2, + ] + ) + ), + 8, + ) self.assertEqual(len(sds_and_msds_to_sds(multi_sds_1)), 3) class TestSdsAndMsdsToMsds(unittest.TestCase): """ - Defines :func:`colour.colorimetry.spectrum.sds_and_msds_to_msds` + Define :func:`colour.colorimetry.spectrum.sds_and_msds_to_msds` definition unit tests methods. """ def test_sds_and_msds_to_msds(self): """ - Tests :func:`colour.colorimetry.spectrum.sds_and_msds_to_msds` + Test :func:`colour.colorimetry.spectrum.sds_and_msds_to_msds` definition. """ - self.assertIsNone(sds_and_msds_to_msds([])) - sd_1 = SpectralDistribution(DATA_SAMPLE_ABRIDGED) sd_2 = SpectralDistribution(DATA_SAMPLE_ABRIDGED) multi_sds_1 = MultiSpectralDistributions(DATA_MULTI_SAMPLE_ABRIDGED) @@ -1813,33 +1978,42 @@ def test_sds_and_msds_to_msds(self): self.assertEqual(sds_and_msds_to_msds(multi_sds_1), multi_sds_1) + multi_sds_0 = sds_and_msds_to_msds([multi_sds_1]) + np.testing.assert_array_equal(multi_sds_0.range, multi_sds_1.range) self.assertEqual(sds_and_msds_to_msds([multi_sds_1]), multi_sds_1) shape = SpectralShape(500, 560, 10) self.assertEqual( sds_and_msds_to_msds([sd_1, sd_2, multi_sds_1, multi_sds_2]).shape, - shape) + shape, + ) np.testing.assert_almost_equal( - sds_and_msds_to_msds([sd_1, sd_2, multi_sds_1, - multi_sds_2]).wavelengths, + sds_and_msds_to_msds( + [sd_1, sd_2, multi_sds_1, multi_sds_2] + ).wavelengths, shape.range(), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - sds_and_msds_to_msds([sd_1, sd_2, multi_sds_1, - multi_sds_2]).values, - tstack([sd_1.align(shape).values, - sd_2.align(shape).values] + - [ - sd.values - for sd in sds_and_msds_to_sds(multi_sds_1.align(shape)) - ] + [ - sd.values - for sd in sds_and_msds_to_sds(multi_sds_2.align(shape)) - ]), - decimal=7) - - -if __name__ == '__main__': + sds_and_msds_to_msds( + [sd_1, sd_2, multi_sds_1, multi_sds_2] + ).values, + tstack( + [sd_1.align(shape).values, sd_2.align(shape).values] + + [ + sd.values + for sd in sds_and_msds_to_sds(multi_sds_1.align(shape)) + ] + + [ + sd.values + for sd in sds_and_msds_to_sds(multi_sds_2.align(shape)) + ] + ), + decimal=7, + ) + + +if __name__ == "__main__": unittest.main() diff --git a/colour/colorimetry/tests/test_transformations.py b/colour/colorimetry/tests/test_transformations.py index c582955241..85e65715d7 100644 --- a/colour/colorimetry/tests/test_transformations.py +++ b/colour/colorimetry/tests/test_transformations.py @@ -1,68 +1,65 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.colorimetry.transformations` module. +Defines the unit tests for the :mod:`colour.colorimetry.transformations` +module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.colorimetry import (MSDS_CMFS, - RGB_10_degree_cmfs_to_LMS_10_degree_cmfs, - RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs, - RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs, - LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs, - LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs) +from colour.colorimetry import ( + MSDS_CMFS, + RGB_10_degree_cmfs_to_LMS_10_degree_cmfs, + RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs, + RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs, + LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs, + LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs, +) from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestRGB_2_degree_cmfs_to_XYZ_2_degree_cmfs', - 'TestRGB_10_degree_cmfs_to_XYZ_10_degree_cmfs', - 'TestRGB_10_degree_cmfs_to_LMS_10_degree_cmfs', - 'TestLMS_2_degree_cmfs_to_XYZ_2_degree_cmfs', - 'TestLMS_10_degree_cmfs_to_XYZ_10_degree_cmfs' + "TestRGB_2_degree_cmfs_to_XYZ_2_degree_cmfs", + "TestRGB_10_degree_cmfs_to_XYZ_10_degree_cmfs", + "TestRGB_10_degree_cmfs_to_LMS_10_degree_cmfs", + "TestLMS_2_degree_cmfs_to_XYZ_2_degree_cmfs", + "TestLMS_10_degree_cmfs_to_XYZ_10_degree_cmfs", ] class TestRGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(unittest.TestCase): """ - Defines :func:`colour.colorimetry.transformations.\ + Define :func:`colour.colorimetry.transformations.\ RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs` definition unit tests methods. """ def test_RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(self): """ - Tests :func:`colour.colorimetry.transformations.\ + Test :func:`colour.colorimetry.transformations.\ RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs` definition. """ - cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] + cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] np.testing.assert_allclose( - RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(435), - cmfs[435], - atol=0.0025) + RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(435), cmfs[435], atol=0.0025 + ) np.testing.assert_allclose( - RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(545), - cmfs[545], - atol=0.0025) + RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(545), cmfs[545], atol=0.0025 + ) np.testing.assert_allclose( - RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(700), - cmfs[700], - atol=0.0025) + RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(700), cmfs[700], atol=0.0025 + ) def test_n_dimensional_RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(self): """ - Tests :func:`colour.colorimetry.transformations.\ + Test :func:`colour.colorimetry.transformations.\ RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs` definition n-dimensional arrays support. """ @@ -73,60 +70,67 @@ def test_n_dimensional_RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(self): wl = np.tile(wl, 6) XYZ = np.tile(XYZ, (6, 1)) np.testing.assert_almost_equal( - RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(wl), XYZ) + RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(wl), XYZ + ) wl = np.reshape(wl, (2, 3)) XYZ = np.reshape(XYZ, (2, 3, 3)) np.testing.assert_almost_equal( - RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(wl), XYZ) + RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(wl), XYZ + ) wl = np.reshape(wl, (2, 3, 1)) XYZ = np.reshape(XYZ, (2, 3, 1, 3)) np.testing.assert_almost_equal( - RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(wl), XYZ) + RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(wl), XYZ + ) @ignore_numpy_errors def test_nan_RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(self): """ - Tests :func:`colour.colorimetry.transformations.\ + Test :func:`colour.colorimetry.transformations.\ RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs` definition nan support. """ RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestRGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(unittest.TestCase): """ - Defines :func:`colour.colorimetry.transformations.\ + Define :func:`colour.colorimetry.transformations.\ RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs` definition unit tests methods. """ def test_RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(self): """ - Tests :func:`colour.colorimetry.transformations.\ + Test :func:`colour.colorimetry.transformations.\ RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs` definition. """ - cmfs = MSDS_CMFS['CIE 1964 10 Degree Standard Observer'] + cmfs = MSDS_CMFS["CIE 1964 10 Degree Standard Observer"] np.testing.assert_allclose( RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(435), cmfs[435], - atol=0.025) + atol=0.025, + ) np.testing.assert_allclose( RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(545), cmfs[545], - atol=0.025) + atol=0.025, + ) np.testing.assert_allclose( RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(700), cmfs[700], - atol=0.025) + atol=0.025, + ) def test_n_dimensional_RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(self): """ - Tests :func:`colour.colorimetry.transformations.\ + Test :func:`colour.colorimetry.transformations.\ RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs` definition n-dimensional arrays support. """ @@ -137,60 +141,67 @@ def test_n_dimensional_RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(self): wl = np.tile(wl, 6) XYZ = np.tile(XYZ, (6, 1)) np.testing.assert_almost_equal( - RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(wl), XYZ) + RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(wl), XYZ + ) wl = np.reshape(wl, (2, 3)) XYZ = np.reshape(XYZ, (2, 3, 3)) np.testing.assert_almost_equal( - RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(wl), XYZ) + RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(wl), XYZ + ) wl = np.reshape(wl, (2, 3, 1)) XYZ = np.reshape(XYZ, (2, 3, 1, 3)) np.testing.assert_almost_equal( - RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(wl), XYZ) + RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(wl), XYZ + ) @ignore_numpy_errors def test_nan_RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(self): """ - Tests :func:`colour.colorimetry.transformations.\ + Test :func:`colour.colorimetry.transformations.\ RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs` definition nan support. """ RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestRGB_10_degree_cmfs_to_LMS_10_degree_cmfs(unittest.TestCase): """ - Defines :func:`colour.colorimetry.transformations.\ + Define :func:`colour.colorimetry.transformations.\ RGB_10_degree_cmfs_to_LMS_10_degree_cmfs` definition unit tests methods. """ def test_RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(self): """ - Tests :func:`colour.colorimetry.transformations.\ + Test :func:`colour.colorimetry.transformations.\ RGB_10_degree_cmfs_to_LMS_10_degree_cmfs` definition. """ - cmfs = MSDS_CMFS['Stockman & Sharpe 10 Degree Cone Fundamentals'] + cmfs = MSDS_CMFS["Stockman & Sharpe 10 Degree Cone Fundamentals"] np.testing.assert_allclose( RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(435), cmfs[435], - atol=0.0025) + atol=0.0025, + ) np.testing.assert_allclose( RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(545), cmfs[545], - atol=0.0025) + atol=0.0025, + ) np.testing.assert_allclose( RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(700), cmfs[700], - atol=0.0025) + atol=0.0025, + ) def test_n_dimensional_RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(self): """ - Tests :func:`colour.colorimetry.transformations.\ + Test :func:`colour.colorimetry.transformations.\ RGB_10_degree_cmfs_to_LMS_10_degree_cmfs` definition n-dimensional arrays support. """ @@ -201,60 +212,67 @@ def test_n_dimensional_RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(self): wl = np.tile(wl, 6) LMS = np.tile(LMS, (6, 1)) np.testing.assert_almost_equal( - RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(wl), LMS) + RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(wl), LMS + ) wl = np.reshape(wl, (2, 3)) LMS = np.reshape(LMS, (2, 3, 3)) np.testing.assert_almost_equal( - RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(wl), LMS) + RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(wl), LMS + ) wl = np.reshape(wl, (2, 3, 1)) LMS = np.reshape(LMS, (2, 3, 1, 3)) np.testing.assert_almost_equal( - RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(wl), LMS) + RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(wl), LMS + ) @ignore_numpy_errors def test_nan_RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(self): """ - Tests :func:`colour.colorimetry.transformations.\ + Test :func:`colour.colorimetry.transformations.\ RGB_10_degree_cmfs_to_LMS_10_degree_cmfs` definition nan support. """ RGB_10_degree_cmfs_to_LMS_10_degree_cmfs( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(unittest.TestCase): """ - Defines :func:`colour.colorimetry.transformations.\ + Define :func:`colour.colorimetry.transformations.\ LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs` definition unit tests methods. """ def test_LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(self): """ - Tests :func:`colour.colorimetry.transformations.\ + Test :func:`colour.colorimetry.transformations.\ LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs` definition. """ - cmfs = MSDS_CMFS['CIE 2012 2 Degree Standard Observer'] + cmfs = MSDS_CMFS["CIE 2012 2 Degree Standard Observer"] np.testing.assert_allclose( LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(435), cmfs[435], - atol=0.00015) + atol=0.00015, + ) np.testing.assert_allclose( LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(545), cmfs[545], - atol=0.00015) + atol=0.00015, + ) np.testing.assert_allclose( LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(700), cmfs[700], - atol=0.00015) + atol=0.00015, + ) def test_n_dimensional_LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(self): """ - Tests :func:`colour.colorimetry.transformations.\ + Test :func:`colour.colorimetry.transformations.\ LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs` definition n-dimensional arrays support. """ @@ -265,60 +283,67 @@ def test_n_dimensional_LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(self): wl = np.tile(wl, 6) XYZ = np.tile(XYZ, (6, 1)) np.testing.assert_almost_equal( - LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(wl), XYZ) + LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(wl), XYZ + ) wl = np.reshape(wl, (2, 3)) XYZ = np.reshape(XYZ, (2, 3, 3)) np.testing.assert_almost_equal( - LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(wl), XYZ) + LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(wl), XYZ + ) wl = np.reshape(wl, (2, 3, 1)) XYZ = np.reshape(XYZ, (2, 3, 1, 3)) np.testing.assert_almost_equal( - LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(wl), XYZ) + LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(wl), XYZ + ) @ignore_numpy_errors def test_nan_LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(self): """ - Tests :func:`colour.colorimetry.transformations.\ + Test :func:`colour.colorimetry.transformations.\ LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs` definition nan support. """ LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(unittest.TestCase): """ - Defines :func:`colour.colorimetry.transformations.\ + Define :func:`colour.colorimetry.transformations.\ LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs` definition unit tests methods. """ def test_LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(self): """ - Tests :func:`colour.colorimetry.transformations.\ + Test :func:`colour.colorimetry.transformations.\ LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs` definition. """ - cmfs = MSDS_CMFS['CIE 2012 10 Degree Standard Observer'] + cmfs = MSDS_CMFS["CIE 2012 10 Degree Standard Observer"] np.testing.assert_allclose( LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(435), cmfs[435], - atol=0.00015) + atol=0.00015, + ) np.testing.assert_allclose( LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(545), cmfs[545], - atol=0.00015) + atol=0.00015, + ) np.testing.assert_allclose( LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(700), cmfs[700], - atol=0.00015) + atol=0.00015, + ) def test_n_dimensional_LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(self): """ - Tests :func:`colour.colorimetry.transformations.\ + Test :func:`colour.colorimetry.transformations.\ LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs` definition n-dimensional arrays support. """ @@ -329,28 +354,32 @@ def test_n_dimensional_LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(self): wl = np.tile(wl, 6) XYZ = np.tile(XYZ, (6, 1)) np.testing.assert_almost_equal( - LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(wl), XYZ) + LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(wl), XYZ + ) wl = np.reshape(wl, (2, 3)) XYZ = np.reshape(XYZ, (2, 3, 3)) np.testing.assert_almost_equal( - LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(wl), XYZ) + LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(wl), XYZ + ) wl = np.reshape(wl, (2, 3, 1)) XYZ = np.reshape(XYZ, (2, 3, 1, 3)) np.testing.assert_almost_equal( - LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(wl), XYZ) + LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(wl), XYZ + ) @ignore_numpy_errors def test_nan_LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(self): """ - Tests :func:`colour.colorimetry.transformations.\ + Test :func:`colour.colorimetry.transformations.\ LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs` definition nan support. """ LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/colorimetry/tests/test_tristimulus.py b/colour/colorimetry/tests/test_tristimulus.py deleted file mode 100644 index 0a58174a2a..0000000000 --- a/colour/colorimetry/tests/test_tristimulus.py +++ /dev/null @@ -1,1140 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.colorimetry.tristimulus` module. - -References ----------- -- :cite:`ASTMInternational2015b` : ASTM International. (2015). ASTM E308-15 - - Standard Practice for Computing the Colors of Objects by Using the CIE - System (pp. 1-47). doi:10.1520/E0308-15 -""" - -from __future__ import division, unicode_literals - -import numpy as np -import unittest - -from colour.algebra import LinearInterpolator -from colour.colorimetry import (MSDS_CMFS, sd_CIE_standard_illuminant_A, - SDS_ILLUMINANTS, MultiSpectralDistributions, - SpectralDistribution, SpectralShape) -from colour.colorimetry import ( - lagrange_coefficients_ASTME2022, tristimulus_weighting_factors_ASTME2022, - adjust_tristimulus_weighting_factors_ASTME308, sd_to_XYZ_integration, - sd_to_XYZ_tristimulus_weighting_factors_ASTME308, sd_to_XYZ_ASTME308, - msds_to_XYZ_integration, msds_to_XYZ_ASTME308, wavelength_to_XYZ) -from colour.utilities import domain_range_scale - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = [ - 'SD_SAMPLE', 'LAGRANGE_COEFFICIENTS_A', 'LAGRANGE_COEFFICIENTS_B', - 'TWF_A_CIE_1964_10_10', 'TWF_A_CIE_1964_10_20', 'TWF_D65_CIE_1931_2_20', - 'TWF_D65_CIE_1931_2_20_K1', 'TWF_D65_CIE_1931_2_20_A', 'DATA_TWO', - 'MSDS_TWO', 'TVS_D65_INTEGRATION_MSDS', 'TVS_D65_ARRAY_INTEGRATION', - 'TVS_D65_ARRAY_K1_INTEGRATION', 'TVS_D65_ASTME308_MSDS', - 'TVS_D65_ASTME308_K1_MSDS', 'TestLagrangeCoefficientsASTME2022', - 'TestTristimulusWeightingFactorsASTME2022', - 'TestAdjustTristimulusWeightingFactorsASTME308', - 'TestSd_to_XYZ_integration', 'TestSd_to_XYZ_ASTME308', - 'TestMsds_to_XYZ_integration', 'TestMsds_to_XYZ_ASTME308', - 'TestWavelength_to_XYZ' -] - -SD_SAMPLE = SpectralDistribution({ - 340: 0.0000, - 345: 0.0000, - 350: 0.0000, - 355: 0.0000, - 360: 0.0000, - 365: 0.0000, - 370: 0.0000, - 375: 0.0000, - 380: 0.0000, - 385: 0.0000, - 390: 0.0000, - 395: 0.0000, - 400: 0.0641, - 405: 0.0650, - 410: 0.0654, - 415: 0.0652, - 420: 0.0645, - 425: 0.0629, - 430: 0.0605, - 435: 0.0581, - 440: 0.0562, - 445: 0.0551, - 450: 0.0543, - 455: 0.0539, - 460: 0.0537, - 465: 0.0538, - 470: 0.0541, - 475: 0.0547, - 480: 0.0559, - 485: 0.0578, - 490: 0.0603, - 495: 0.0629, - 500: 0.0651, - 505: 0.0667, - 510: 0.0680, - 515: 0.0691, - 520: 0.0705, - 525: 0.0720, - 530: 0.0736, - 535: 0.0753, - 540: 0.0772, - 545: 0.0791, - 550: 0.0809, - 555: 0.0833, - 560: 0.0870, - 565: 0.0924, - 570: 0.0990, - 575: 0.1061, - 580: 0.1128, - 585: 0.1190, - 590: 0.1251, - 595: 0.1308, - 600: 0.1360, - 605: 0.1403, - 610: 0.1439, - 615: 0.1473, - 620: 0.1511, - 625: 0.1550, - 630: 0.1590, - 635: 0.1634, - 640: 0.1688, - 645: 0.1753, - 650: 0.1828, - 655: 0.1909, - 660: 0.1996, - 665: 0.2088, - 670: 0.2187, - 675: 0.2291, - 680: 0.2397, - 685: 0.2505, - 690: 0.2618, - 695: 0.2733, - 700: 0.2852, - 705: 0.0000, - 710: 0.0000, - 715: 0.0000, - 720: 0.0000, - 725: 0.0000, - 730: 0.0000, - 735: 0.0000, - 740: 0.0000, - 745: 0.0000, - 750: 0.0000, - 755: 0.0000, - 760: 0.0000, - 765: 0.0000, - 770: 0.0000, - 775: 0.0000, - 780: 0.0000, - 785: 0.0000, - 790: 0.0000, - 795: 0.0000, - 800: 0.0000, - 805: 0.0000, - 810: 0.0000, - 815: 0.0000, - 820: 0.0000, - 825: 0.0000, - 830: 0.0000 -}) - -LAGRANGE_COEFFICIENTS_A = np.array([ - [-0.0285, 0.9405, 0.1045, -0.0165], - [-0.0480, 0.8640, 0.2160, -0.0320], - [-0.0595, 0.7735, 0.3315, -0.0455], - [-0.0640, 0.6720, 0.4480, -0.0560], - [-0.0625, 0.5625, 0.5625, -0.0625], - [-0.0560, 0.4480, 0.6720, -0.0640], - [-0.0455, 0.3315, 0.7735, -0.0595], - [-0.0320, 0.2160, 0.8640, -0.0480], - [-0.0165, 0.1045, 0.9405, -0.0285], -]) - -LAGRANGE_COEFFICIENTS_B = np.array([ - [0.8550, 0.1900, -0.0450], - [0.7200, 0.3600, -0.0800], - [0.5950, 0.5100, -0.1050], - [0.4800, 0.6400, -0.1200], - [0.3750, 0.7500, -0.1250], - [0.2800, 0.8400, -0.1200], - [0.1950, 0.9100, -0.1050], - [0.1200, 0.9600, -0.0800], - [0.0550, 0.9900, -0.0450], -]) - -TWF_A_CIE_1964_10_10 = np.array([ - [-0.000, -0.000, -0.000], - [-0.000, -0.000, -0.000], - [-0.000, -0.000, -0.000], - [0.002, 0.000, 0.008], - [0.025, 0.003, 0.110], - [0.134, 0.014, 0.615], - [0.377, 0.039, 1.792], - [0.686, 0.084, 3.386], - [0.964, 0.156, 4.944], - [1.080, 0.259, 5.806], - [1.006, 0.424, 5.812], - [0.731, 0.696, 4.919], - [0.343, 1.082, 3.300], - [0.078, 1.616, 1.973], - [0.022, 2.422, 1.152], - [0.218, 3.529, 0.658], - [0.750, 4.840, 0.382], - [1.642, 6.100, 0.211], - [2.842, 7.250, 0.102], - [4.336, 8.114, 0.032], - [6.200, 8.758, 0.001], - [8.262, 8.988, -0.000], - [10.227, 8.760, 0.000], - [11.945, 8.304, 0.000], - [12.746, 7.468, 0.000], - [12.337, 6.323, 0.000], - [10.817, 5.033, 0.000], - [8.560, 3.744, 0.000], - [6.014, 2.506, 0.000], - [3.887, 1.560, 0.000], - [2.309, 0.911, 0.000], - [1.276, 0.499, 0.000], - [0.666, 0.259, 0.000], - [0.336, 0.130, 0.000], - [0.166, 0.065, 0.000], - [0.082, 0.032, 0.000], - [0.040, 0.016, 0.000], - [0.020, 0.008, 0.000], - [0.010, 0.004, 0.000], - [0.005, 0.002, 0.000], - [0.003, 0.001, 0.000], - [0.001, 0.001, 0.000], - [0.001, 0.000, 0.000], - [0.000, 0.000, 0.000], - [0.000, 0.000, 0.000], - [0.000, 0.000, 0.000], - [0.000, 0.000, 0.000], - [0.000, 0.000, 0.000], -]) - -TWF_A_CIE_1964_10_20 = np.array([ - [-0.000, -0.000, -0.001], - [-0.009, -0.001, -0.041], - [0.060, 0.005, 0.257], - [0.773, 0.078, 3.697], - [1.900, 0.304, 9.755], - [1.971, 0.855, 11.487], - [0.718, 2.146, 6.785], - [0.043, 4.899, 2.321], - [1.522, 9.647, 0.743], - [5.677, 14.461, 0.196], - [12.445, 17.474, 0.005], - [20.554, 17.584, -0.003], - [25.332, 14.896, 0.000], - [21.571, 10.080, 0.000], - [12.179, 5.068, 0.000], - [4.668, 1.830, 0.000], - [1.324, 0.513, 0.000], - [0.318, 0.123, 0.000], - [0.075, 0.029, 0.000], - [0.018, 0.007, 0.000], - [0.005, 0.002, 0.000], - [0.001, 0.001, 0.000], - [0.000, 0.000, 0.000], - [0.000, 0.000, 0.000], -]) - -TWF_D65_CIE_1931_2_20 = np.array([ - [-0.001, -0.000, -0.005], - [-0.008, -0.000, -0.039], - [0.179, 0.002, 0.829], - [2.542, 0.071, 12.203], - [6.670, 0.453, 33.637], - [6.333, 1.316, 36.334], - [2.213, 2.933, 18.278], - [0.052, 6.866, 5.543], - [1.348, 14.106, 1.611], - [5.767, 18.981, 0.382], - [11.301, 18.863, 0.068], - [16.256, 15.455, 0.025], - [17.933, 10.699, 0.013], - [14.020, 6.277, 0.003], - [7.057, 2.743, 0.000], - [2.527, 0.927, -0.000], - [0.670, 0.242, -0.000], - [0.140, 0.050, 0.000], - [0.035, 0.013, 0.000], - [0.008, 0.003, 0.000], - [0.002, 0.001, 0.000], - [0.000, 0.000, 0.000], - [0.000, 0.000, 0.000], - [0.000, 0.000, 0.000], -]) - -TWF_D65_CIE_1931_2_20_K1 = np.array([ - [-0.10095678, -0.00265636, -0.48295051], - [-0.83484763, -0.02190274, -4.11563004], - [18.94315946, 0.22803520, 87.62930101], - [268.66426663, 7.45156533, 1289.46785306], - [704.84093814, 47.85119956, 3554.47729494], - [669.16372619, 139.09334752, 3839.47028848], - [233.89418975, 309.95075927, 1931.47489098], - [5.51347204, 725.55154566, 585.77542998], - [142.48116090, 1490.55009270, 170.27819443], - [609.43424752, 2005.73581058, 40.34233506], - [1194.21293134, 1993.32004423, 7.15981395], - [1717.79835378, 1633.12477710, 2.59651081], - [1895.00791740, 1130.54333854, 1.34461357], - [1481.55235852, 663.25632432, 0.29999368], - [745.76471129, 289.85683288, 0.01943154], - [267.01875994, 97.97358872, -0.00261658], - [70.75239887, 25.56445574, -0.00019929], - [14.78862574, 5.31713332, 0.00000000], - [3.67620064, 1.32650433, 0.00000000], - [0.89699648, 0.32392186, 0.00000000], - [0.16623785, 0.06003153, 0.00000000], - [0.04824448, 0.01742197, 0.00000000], - [0.01310759, 0.00473339, 0.00000000], - [0.00223616, 0.00080752, 0.00000000], -]) - -TWF_D65_CIE_1931_2_20_A = np.array([ - [0.170, 0.002, 0.785], - [2.542, 0.071, 12.203], - [6.670, 0.453, 33.637], - [6.333, 1.316, 36.334], - [2.213, 2.933, 18.278], - [0.052, 6.866, 5.543], - [1.348, 14.106, 1.611], - [5.767, 18.981, 0.382], - [11.301, 18.863, 0.068], - [16.256, 15.455, 0.025], - [17.933, 10.699, 0.013], - [14.020, 6.277, 0.003], - [7.057, 2.743, 0.000], - [2.527, 0.927, -0.000], - [0.670, 0.242, -0.000], - [0.185, 0.067, 0.000], -]) - -DATA_TWO = np.array([ - [[0.01367208, 0.09127947, 0.01524376, 0.02810712, 0.19176012, 0.04299992], - [0.01591516, 0.31454948, 0.08416876, 0.09071489, 0.71026170, 0.04374762], - [0.00959792, 0.25822842, 0.41388571, 0.22275120, 0.00407416, 0.37439537], - [0.01106279, 0.07090867, 0.02204929, 0.12487984, 0.18168917, 0.00202945], - [0.01791409, 0.29707789, 0.56295109, 0.23752193, 0.00236515, 0.58190280], - [0.10565346, 0.46204320, 0.19180590, 0.56250858, 0.42085907, 0.00270085]], - [[0.04325933, 0.26825359, 0.23732357, 0.05175860, 0.01181048, 0.08233768], - [0.02577249, 0.08305486, 0.04303044, 0.32298771, 0.23022813, 0.00813306], - [0.02484169, 0.12027161, 0.00541695, 0.00654612, 0.18603799, 0.36247808], - [0.01861601, 0.12924391, 0.00785840, 0.40062562, 0.94044405, 0.32133976], - [0.03102159, 0.16815442, 0.37186235, 0.08610666, 0.00413520, 0.78492409], - [0.04727245, 0.32210270, 0.22679484, 0.31613642, 0.11242847, 0.00244144]], -]) - -MSDS_TWO = MultiSpectralDistributions( - np.transpose(np.reshape(DATA_TWO, [-1, 6])), - SpectralShape(400, 700, 60).range()) - -TVS_D65_INTEGRATION_MSDS = np.array([ - [7.50219602, 3.95048275, 8.40152163], - [26.92629005, 15.07170066, 28.71020457], - [16.70060700, 28.21421317, 25.64802044], - [11.57577260, 8.64108703, 6.57740493], - [18.73108262, 35.07369122, 30.14365007], - [45.16559608, 39.61411218, 43.68158810], - [8.17318743, 13.09236381, 25.93755134], - [22.46715798, 19.31066951, 7.95954422], - [6.58106180, 2.52865132, 11.09122159], - [43.91745731, 27.98043364, 11.73313699], - [8.53693599, 19.70195654, 17.70110118], - [23.91114755, 26.21471641, 30.67613685], -]) - -TVS_D65_ARRAY_INTEGRATION = np.array([ - [ - [7.19510558, 3.86227393, 10.09950719], - [25.57464912, 14.71934603, 34.84931928], - [17.58300551, 28.56388139, 30.18370150], - [11.32631694, 8.46087304, 7.90263107], - [19.65793587, 35.59047030, 35.14042633], - [45.82162927, 39.26057155, 51.79537877], - ], - [ - [8.82617380, 13.38600040, 30.56510531], - [22.33167167, 18.95683859, 9.39034481], - [6.69130415, 2.57592352, 13.25898396], - [41.81950400, 27.11920225, 14.26746010], - [9.24148668, 20.20448258, 20.19416075], - [24.78545992, 26.22388193, 36.44325237], - ], -]) - -TVS_D65_ARRAY_K1_INTEGRATION = np.array([ - [ - [776.11755347, 416.61356647, 1089.40789347], - [2758.67169575, 1587.73804035, 3759.10653835], - [1896.63363103, 3081.11250154, 3255.83833518], - [1221.74071019, 912.65263825, 852.43651028], - [2120.45103874, 3839.05259546, 3790.50750799], - [4942.66142767, 4234.93698798, 5587.03443988], - ], - [ - [952.05669281, 1443.91347280, 3296.97938475], - [2408.86005021, 2044.82547598, 1012.91236929], - [721.77378835, 277.85825207, 1430.21253594], - [4510.96245706, 2925.27867434, 1538.99426670], - [996.85542597, 2179.40562871, 2178.29223889], - [2673.54388456, 2828.70277157, 3931.04000571], - ], -]) - -TVS_D65_ASTME308_MSDS = np.array([ - [7.50450425, 3.95744742, 8.38735462], - [26.94116124, 15.09801442, 28.66753115], - [16.70212538, 28.20596151, 25.65809190], - [11.57025728, 8.64549437, 6.55935421], - [18.74248163, 35.06128859, 30.17576781], - [45.12240306, 39.62432052, 43.58455883], - [8.17632546, 13.09396693, 25.92811880], - [22.44582614, 19.31227394, 7.92623840], - [6.57937576, 2.53370970, 11.07068448], - [43.91405117, 28.00039763, 11.68910584], - [8.54996478, 19.69029667, 17.73601959], - [23.88899194, 26.21653407, 30.62958339], -]) - -TVS_D65_ASTME308_K1_MSDS = np.array([ - [793.00584037, 418.18604067, 886.29721234], - [2846.89001419, 1595.41699464, 3029.31664392], - [1764.92444185, 2980.54228038, 2711.30724359], - [1222.63660519, 913.57500818, 693.13122104], - [1980.53021265, 3704.94914782, 3188.69299177], - [4768.11365133, 4187.12769685, 4605.60865221], - [863.99762445, 1383.64799389, 2739.84116154], - [2371.86503263, 2040.74053594, 837.57076242], - [695.24691183, 267.73875067, 1169.84642292], - [4640.42632139, 2958.82021193, 1235.19540951], - [903.48033348, 2080.68644330, 1874.17671434], - [2524.36530136, 2770.31818974, 3236.64797963], -]) - - -class TestLagrangeCoefficientsASTME2022(unittest.TestCase): - """ - Defines :func:`colour.colorimetry.tristimulus.\ -lagrange_coefficients_ASTME2022` definition unit tests methods. - """ - - def test_lagrange_coefficients_ASTME2022(self): - """ - Tests :func:`colour.colorimetry.tristimulus.\ -lagrange_coefficients_ASTME2022` definition. - """ - - np.testing.assert_almost_equal( - lagrange_coefficients_ASTME2022(10, 'inner'), - LAGRANGE_COEFFICIENTS_A, - decimal=7) - - np.testing.assert_almost_equal( - lagrange_coefficients_ASTME2022(10, 'boundary'), - LAGRANGE_COEFFICIENTS_B, - decimal=7) - - -class TestTristimulusWeightingFactorsASTME2022(unittest.TestCase): - """ - Defines :func:`colour.colorimetry.tristimulus.\ -tristimulus_weighting_factors_ASTME2022` definition unit tests methods. - """ - - def test_tristimulus_weighting_factors_ASTME2022(self): - """ - Tests :func:`colour.colorimetry.tristimulus.\ -tristimulus_weighting_factors_ASTME2022` definition. - - Notes - ----- - - :attr:`TWF_A_CIE_1964_10_10`, :attr:`TWF_A_CIE_1964_10_20` and - :attr:`TWF_D65_CIE_1931_2_20` attributes data is matching - :cite:`ASTMInternational2015b`. - - References - ---------- - :cite:`ASTMInternational2015b` - """ - - cmfs = MSDS_CMFS['CIE 1964 10 Degree Standard Observer'] - A = sd_CIE_standard_illuminant_A(cmfs.shape) - - twf = tristimulus_weighting_factors_ASTME2022( - cmfs, A, SpectralShape(360, 830, 10)) - np.testing.assert_almost_equal( - np.round(twf, 3), TWF_A_CIE_1964_10_10, decimal=3) - - twf = tristimulus_weighting_factors_ASTME2022( - cmfs, A, SpectralShape(360, 830, 20)) - np.testing.assert_almost_equal( - np.round(twf, 3), TWF_A_CIE_1964_10_20, decimal=3) - - cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] - D65 = SDS_ILLUMINANTS['D65'].copy().align( - cmfs.shape, interpolator=LinearInterpolator) - twf = tristimulus_weighting_factors_ASTME2022( - cmfs, D65, SpectralShape(360, 830, 20)) - np.testing.assert_almost_equal( - np.round(twf, 3), TWF_D65_CIE_1931_2_20, decimal=3) - - twf = tristimulus_weighting_factors_ASTME2022( - cmfs, D65, SpectralShape(360, 830, 20), k=1) - np.testing.assert_almost_equal( - twf, TWF_D65_CIE_1931_2_20_K1, decimal=7) - - def test_raise_exception_tristimulus_weighting_factors_ASTME2022(self): - """ - Tests :func:`colour.colorimetry.tristimulus.\ -tristimulus_weighting_factors_ASTME2022` definition raised exception. - """ - - shape = SpectralShape(360, 830, 10) - cmfs_1 = MSDS_CMFS['CIE 1964 10 Degree Standard Observer'] - cmfs_2 = cmfs_1.copy().align(shape) - A_1 = sd_CIE_standard_illuminant_A(cmfs_1.shape) - A_2 = sd_CIE_standard_illuminant_A(cmfs_2.shape) - - self.assertRaises(ValueError, tristimulus_weighting_factors_ASTME2022, - cmfs_1, A_2, shape) - - self.assertRaises(ValueError, tristimulus_weighting_factors_ASTME2022, - cmfs_2, A_1, shape) - - -class TestAdjustTristimulusWeightingFactorsASTME308(unittest.TestCase): - """ - Defines :func:`colour.colorimetry.tristimulus.\ -adjust_tristimulus_weighting_factors_ASTME308` definition unit tests methods. - """ - - def test_adjust_tristimulus_weighting_factors_ASTME308(self): - """ - Tests :func:`colour.colorimetry.tristimulus.\ -adjust_tristimulus_weighting_factors_ASTME308` definition. - """ - - np.testing.assert_almost_equal( - adjust_tristimulus_weighting_factors_ASTME308( - TWF_D65_CIE_1931_2_20, SpectralShape(360, 830, 20), - SpectralShape(400, 700, 20)), - TWF_D65_CIE_1931_2_20_A, - decimal=3) - - -class TestSd_to_XYZ_integration(unittest.TestCase): - """ - Defines :func:`colour.colorimetry.tristimulus.sd_to_XYZ_integration` - definition unit tests methods. - """ - - def test_sd_to_XYZ_integration(self): - """ - Tests :func:`colour.colorimetry.tristimulus.sd_to_XYZ_integration` - definition. - """ - - cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] - np.testing.assert_almost_equal( - sd_to_XYZ_integration(SD_SAMPLE, cmfs, SDS_ILLUMINANTS['A']), - np.array([14.46341147, 10.85819624, 2.04695585]), - decimal=7) - - cmfs = MSDS_CMFS['CIE 1964 10 Degree Standard Observer'] - np.testing.assert_almost_equal( - sd_to_XYZ_integration(SD_SAMPLE, cmfs, SDS_ILLUMINANTS['C']), - np.array([10.77002699, 9.44876636, 6.62415290]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_integration(SD_SAMPLE, cmfs, SDS_ILLUMINANTS['FL2']), - np.array([11.57540576, 9.98608874, 3.95242590]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_integration( - SD_SAMPLE, cmfs, SDS_ILLUMINANTS['FL2'], k=683), - np.array([122375.09261493, 105572.84645912, 41785.01342332]), - decimal=7) - - def test_domain_range_scale_sd_to_XYZ_integration(self): - """ - Tests :func:`colour.colorimetry.tristimulus.sd_to_XYZ_integration` - definition domain and range scale support. - """ - - cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] - XYZ = sd_to_XYZ_integration(SD_SAMPLE, cmfs, SDS_ILLUMINANTS['A']) - - d_r = (('reference', 1), (1, 0.01), (100, 1)) - for scale, factor in d_r: - with domain_range_scale(scale): - np.testing.assert_almost_equal( - sd_to_XYZ_integration(SD_SAMPLE, cmfs, - SDS_ILLUMINANTS['A']), - XYZ * factor, - decimal=7) - - -class TestSd_to_XYZ_tristimulus_weighting_factors_ASTME308(unittest.TestCase): - """ - Defines :func:`colour.colorimetry.tristimulus.\ -sd_to_XYZ_tristimulus_weighting_factors_ASTME308` - definition unit tests methods. - """ - - def test_sd_to_XYZ_tristimulus_weighting_factors_ASTME308(self): - """ - Tests :func:`colour.colorimetry.tristimulus.\ -sd_to_XYZ_tristimulus_weighting_factors_ASTME308` - definition. - """ - - cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] - np.testing.assert_almost_equal( - sd_to_XYZ_tristimulus_weighting_factors_ASTME308( - SD_SAMPLE, cmfs, SDS_ILLUMINANTS['A']), - np.array([14.46341867, 10.85820227, 2.04697034]), - decimal=7) - - cmfs = MSDS_CMFS['CIE 1964 10 Degree Standard Observer'] - np.testing.assert_almost_equal( - sd_to_XYZ_tristimulus_weighting_factors_ASTME308( - SD_SAMPLE, cmfs, SDS_ILLUMINANTS['C']), - np.array([10.77005571, 9.44877491, 6.62428210]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_tristimulus_weighting_factors_ASTME308( - SD_SAMPLE, cmfs, SDS_ILLUMINANTS['FL2']), - np.array([11.57542759, 9.98605604, 3.95273304]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_tristimulus_weighting_factors_ASTME308( - SD_SAMPLE.copy().trim(SpectralShape(400, 700, 5)), cmfs, - SDS_ILLUMINANTS['A']), - np.array([14.38153638, 10.74503131, 2.01613844]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_tristimulus_weighting_factors_ASTME308( - SD_SAMPLE.copy().interpolate(SpectralShape(400, 700, 10)), - cmfs, SDS_ILLUMINANTS['A']), - np.array([14.38257202, 10.74568178, 2.01588427]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_tristimulus_weighting_factors_ASTME308( - SD_SAMPLE.copy().interpolate(SpectralShape(400, 700, 20)), - cmfs, SDS_ILLUMINANTS['A']), - np.array([14.38329645, 10.74603515, 2.01561113]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_tristimulus_weighting_factors_ASTME308( - SD_SAMPLE.copy().interpolate(SpectralShape(400, 700, 20)), - cmfs, - SDS_ILLUMINANTS['A'], - k=1), - np.array([1636.74881983, 1222.84626486, 229.36669308]), - decimal=7) - - def test_domain_range_scale_sd_to_XYZ_twf_ASTME308(self): - """ - Tests :func:`colour.colorimetry.tristimulus.\ -sd_to_XYZ_tristimulus_weighting_factors_ASTME308` definition domain and -range scale support. - """ - - cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] - XYZ = sd_to_XYZ_tristimulus_weighting_factors_ASTME308( - SD_SAMPLE, cmfs, SDS_ILLUMINANTS['A']) - - d_r = (('reference', 1), (1, 0.01), (100, 1)) - for scale, factor in d_r: - with domain_range_scale(scale): - np.testing.assert_almost_equal( - sd_to_XYZ_tristimulus_weighting_factors_ASTME308( - SD_SAMPLE, cmfs, SDS_ILLUMINANTS['A']), - XYZ * factor, - decimal=7) - - -class TestSd_to_XYZ_ASTME308(unittest.TestCase): - """ - Defines :func:`colour.colorimetry.tristimulus.sd_to_XYZ_ASTME308` - definition unit tests methods. - """ - - def setUp(self): - """ - Initialises common tests attributes. - """ - - self._sd = SD_SAMPLE.copy() - self._cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] - self._A = sd_CIE_standard_illuminant_A(self._cmfs.shape) - - def test_sd_to_XYZ_ASTME308_mi_1nm(self): - """ - Tests :func:`colour.colorimetry.tristimulus.sd_to_XYZ_ASTME308` - definition for 1 nm measurement intervals. - """ - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308(self._sd.copy().align(self._cmfs.shape), - self._cmfs, self._A), - np.array([14.46372680, 10.85832950, 2.04663200]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(self._cmfs.shape), - self._cmfs, - self._A, - use_practice_range=False), - np.array([14.46366018, 10.85827949, 2.04662258]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(400, 700, 1)), self._cmfs, - self._A), - np.array([14.54173397, 10.88628632, 2.04965822]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(400, 700, 1)), - self._cmfs, - self._A, - use_practice_range=False), - np.array([14.54203076, 10.88636754, 2.04964877]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(400, 700, 1)), - self._cmfs, - self._A, - k=1), - np.array([1568.98152997, 1174.57671769, 221.14803420]), - decimal=7) - - def test_sd_to_XYZ_ASTME308_mi_5nm(self): - """ - Tests :func:`colour.colorimetry.tristimulus.sd_to_XYZ_ASTME308` - definition for 5 nm measurement intervals. - """ - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(360, 830, 5)), self._cmfs, - self._A), - np.array([14.46372173, 10.85832502, 2.04664734]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(360, 830, 5)), - self._cmfs, - self._A, - use_practice_range=False), - np.array([14.46366388, 10.85828159, 2.04663915]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(360, 830, 5)), - self._cmfs, - self._A, - mi_5nm_omission_method=False), - np.array([14.46373399, 10.85833553, 2.0466465]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(400, 700, 5)), self._cmfs, - self._A), - np.array([14.54025742, 10.88576251, 2.04950226]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(400, 700, 5)), - self._cmfs, - self._A, - use_practice_range=False), - np.array([14.54051517, 10.88583304, 2.04949406]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(400, 700, 5)), - self._cmfs, - self._A, - mi_5nm_omission_method=False), - np.array([14.54022093, 10.88575468, 2.04951057]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(360, 830, 5)), - self._cmfs, - self._A, - use_practice_range=False, - mi_5nm_omission_method=False), - np.array([14.46366737, 10.85828552, 2.04663707]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(400, 700, 5)), - self._cmfs, - self._A, - use_practice_range=False, - mi_5nm_omission_method=False), - np.array([14.54051772, 10.88583590, 2.04950113]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(400, 700, 5)), - self._cmfs, - self._A, - k=1), - np.array([1568.82479013, 1174.52212708, 221.13156963]), - decimal=7) - - def test_sd_to_XYZ_ASTME308_mi_10nm(self): - """ - Tests :func:`colour.colorimetry.tristimulus.sd_to_XYZ_ASTME308` - definition for 10 nm measurement intervals. - """ - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(360, 830, 10)), self._cmfs, - self._A), - np.array([14.47779980, 10.86358645, 2.04751388]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(360, 830, 10)), - self._cmfs, - self._A, - use_practice_range=False), - np.array([14.47773312, 10.86353641, 2.04750445]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(400, 700, 10)), self._cmfs, - self._A), - np.array([14.54137532, 10.88641727, 2.04931318]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(400, 700, 10)), - self._cmfs, - self._A, - use_practice_range=False), - np.array([14.54167211, 10.88649849, 2.04930374]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(400, 700, 10)), - self._cmfs, - self._A, - k=1), - np.array([1568.94283333, 1174.59084705, 221.11080639]), - decimal=7) - - def test_sd_to_XYZ_ASTME308_mi_20nm(self): - """ - Tests :func:`colour.colorimetry.tristimulus.sd_to_XYZ_ASTME308` - definition for 20 nm measurement intervals. - """ - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(360, 820, 20)), self._cmfs, - self._A), - np.array([14.50187464, 10.87217124, 2.04918305]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(360, 820, 20)), - self._cmfs, - self._A, - use_practice_range=False), - np.array([14.50180785, 10.87212116, 2.04917361]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(360, 820, 20)), - self._cmfs, - self._A, - mi_20nm_interpolation_method=False), - np.array([14.50216194, 10.87236873, 2.04977256]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(400, 700, 20)), self._cmfs, - self._A), - np.array([14.54114025, 10.88634755, 2.04916445]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(400, 700, 20)), - self._cmfs, - self._A, - use_practice_range=False), - np.array([14.54143704, 10.88642877, 2.04915501]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(400, 700, 20)), - self._cmfs, - self._A, - mi_20nm_interpolation_method=False), - np.array([14.54242562, 10.88694088, 2.04919645]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(360, 820, 20)), - self._cmfs, - self._A, - use_practice_range=False, - mi_20nm_interpolation_method=False), - np.array([14.50209515, 10.87231865, 2.04976312]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(400, 700, 20)), - self._cmfs, - self._A, - use_practice_range=False, - mi_20nm_interpolation_method=False), - np.array([14.54272240, 10.88702210, 2.04918701]), - decimal=7) - - np.testing.assert_almost_equal( - sd_to_XYZ_ASTME308( - self._sd.copy().align(SpectralShape(400, 700, 20)), - self._cmfs, - self._A, - k=1), - np.array([1568.91747040, 1174.58332427, 221.09475945]), - decimal=7) - - def test_raise_exception_sd_to_XYZ_ASTME308(self): - """ - Tests :func:`colour.colorimetry.tristimulus.sd_to_XYZ_ASTME308` - definition raised exception. - """ - - self.assertRaises(ValueError, sd_to_XYZ_ASTME308, - self._sd.copy().align(SpectralShape(360, 820, 2))) - - -class TestMsds_to_XYZ_integration(unittest.TestCase): - """ - Defines :func:`colour.colorimetry.tristimulus.msds_to_XYZ_integration` - definition unit tests methods. - """ - - def test_msds_to_XYZ_integration(self): - """ - Tests :func:`colour.colorimetry.tristimulus.msds_to_XYZ_integration` - definition. - """ - - cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] - np.testing.assert_almost_equal( - msds_to_XYZ_integration(MSDS_TWO, cmfs, SDS_ILLUMINANTS['D65']), - TVS_D65_INTEGRATION_MSDS, - decimal=7) - - np.testing.assert_almost_equal( - msds_to_XYZ_integration( - DATA_TWO, - cmfs, - SDS_ILLUMINANTS['D65'], - shape=SpectralShape(400, 700, 60)), - TVS_D65_ARRAY_INTEGRATION, - decimal=7) - - np.testing.assert_almost_equal( - msds_to_XYZ_integration( - DATA_TWO, - cmfs, - SDS_ILLUMINANTS['D65'], - 1, - shape=SpectralShape(400, 700, 60)), - TVS_D65_ARRAY_K1_INTEGRATION, - decimal=7) - - def test_domain_range_scale_msds_to_XYZ_integration(self): - """ - Tests :func:`colour.colorimetry.tristimulus.msds_to_XYZ_integration` - definition domain and range scale support. - """ - - cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] - d_r = (('reference', 1), (1, 0.01), (100, 1)) - for scale, factor in d_r: - with domain_range_scale(scale): - np.testing.assert_almost_equal( - msds_to_XYZ_integration( - DATA_TWO, - cmfs, - SDS_ILLUMINANTS['D65'], - shape=SpectralShape(400, 700, 60)), - TVS_D65_ARRAY_INTEGRATION * factor, - decimal=7) - - -class TestMsds_to_XYZ_ASTME308(unittest.TestCase): - """ - Defines :func:`colour.colorimetry.tristimulus.msds_to_XYZ_ASTME308` - definition unit tests methods. - """ - - def test_msds_to_XYZ_ASTME308(self): - """ - Tests :func:`colour.colorimetry.tristimulus.msds_to_XYZ_ASTME308` - definition. - """ - - cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] - msds = MSDS_TWO.copy().align(SpectralShape(400, 700, 20)) - np.testing.assert_almost_equal( - msds_to_XYZ_ASTME308(msds, cmfs, SDS_ILLUMINANTS['D65']), - TVS_D65_ASTME308_MSDS, - decimal=7) - - np.testing.assert_almost_equal( - msds_to_XYZ_ASTME308(msds, cmfs, SDS_ILLUMINANTS['D65'], k=1), - TVS_D65_ASTME308_K1_MSDS, - decimal=7) - - def test_domain_range_scale_msds_to_XYZ_ASTME308(self): - """ - Tests :func:`colour.colorimetry.tristimulus.msds_to_XYZ_ASTME308` - definition domain and range scale support. - """ - - cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] - d_r = (('reference', 1), (1, 0.01), (100, 1)) - for scale, factor in d_r: - with domain_range_scale(scale): - np.testing.assert_almost_equal( - msds_to_XYZ_ASTME308( - MSDS_TWO.copy().align(SpectralShape(400, 700, 20)), - cmfs, SDS_ILLUMINANTS['D65']), - TVS_D65_ASTME308_MSDS * factor, - decimal=7) - - def test_raise_exception_msds_to_XYZ_ASTME308(self): - """ - Tests :func:`colour.colorimetry.tristimulus.msds_to_XYZ_ASTME308` - definition raise exception. - """ - - self.assertRaises(ValueError, msds_to_XYZ_ASTME308, DATA_TWO) - - -class TestWavelength_to_XYZ(unittest.TestCase): - """ - Defines :func:`colour.colorimetry.tristimulus.wavelength_to_XYZ` definition - unit tests methods. - """ - - def test_wavelength_to_XYZ(self): - """ - Tests :func:`colour.colorimetry.tristimulus.wavelength_to_XYZ` - definition. - """ - - np.testing.assert_almost_equal( - wavelength_to_XYZ( - 480, MSDS_CMFS['CIE 1931 2 Degree Standard Observer']), - np.array([0.09564, 0.13902, 0.81295]), - decimal=7) - - np.testing.assert_almost_equal( - wavelength_to_XYZ( - 480, MSDS_CMFS['CIE 2012 2 Degree Standard Observer']), - np.array([0.08182895, 0.17880480, 0.75523790]), - decimal=7) - - np.testing.assert_almost_equal( - wavelength_to_XYZ( - 641.5, MSDS_CMFS['CIE 2012 2 Degree Standard Observer']), - np.array([0.44575583, 0.18184213, 0.00000000]), - decimal=7) - - def test_raise_exception_wavelength_to_XYZ(self): - """ - Tests :func:`colour.colorimetry.tristimulus.wavelength_to_XYZ` - definition raised exception. - """ - - self.assertRaises(ValueError, wavelength_to_XYZ, 1) - - self.assertRaises(ValueError, wavelength_to_XYZ, 1000) - - def test_n_dimensional_wavelength_to_XYZ(self): - """ - Tests :func:`colour.colorimetry.tristimulus.wavelength_to_XYZ` - definition n-dimensional arrays support. - """ - - cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] - wl = 480 - XYZ = wavelength_to_XYZ(wl, cmfs) - - wl = np.tile(wl, 6) - XYZ = np.tile(XYZ, (6, 1)) - np.testing.assert_almost_equal( - wavelength_to_XYZ(wl, cmfs), XYZ, decimal=7) - - wl = np.reshape(wl, (2, 3)) - XYZ = np.reshape(XYZ, (2, 3, 3)) - np.testing.assert_almost_equal( - wavelength_to_XYZ(wl, cmfs), XYZ, decimal=7) - - wl = np.reshape(wl, (2, 3, 1)) - XYZ = np.reshape(XYZ, (2, 3, 1, 3)) - np.testing.assert_almost_equal( - wavelength_to_XYZ(wl, cmfs), XYZ, decimal=7) - - -if __name__ == '__main__': - unittest.main() diff --git a/colour/colorimetry/tests/test_tristimulus_values.py b/colour/colorimetry/tests/test_tristimulus_values.py new file mode 100644 index 0000000000..93940fc419 --- /dev/null +++ b/colour/colorimetry/tests/test_tristimulus_values.py @@ -0,0 +1,1555 @@ +""" +Defines the unit tests for the :mod:`colour.colorimetry.tristimulus_values` +module. + +References +---------- +- :cite:`ASTMInternational2015b` : ASTM International. (2015). ASTM E308-15 - + Standard Practice for Computing the Colors of Objects by Using the CIE + System (pp. 1-47). doi:10.1520/E0308-15 +""" + +from __future__ import annotations + +import numpy as np +import unittest + +from colour.algebra import LinearInterpolator +from colour.colorimetry import ( + MSDS_CMFS, + SDS_ILLUMINANTS, + MultiSpectralDistributions, + SpectralDistribution, + SpectralShape, + reshape_msds, + reshape_sd, + sd_CIE_standard_illuminant_A, + sd_ones, +) +from colour.colorimetry import ( + handle_spectral_arguments, + lagrange_coefficients_ASTME2022, + tristimulus_weighting_factors_ASTME2022, + adjust_tristimulus_weighting_factors_ASTME308, + sd_to_XYZ_integration, + sd_to_XYZ_tristimulus_weighting_factors_ASTME308, + sd_to_XYZ_ASTME308, + sd_to_XYZ, + msds_to_XYZ_integration, + msds_to_XYZ_ASTME308, + wavelength_to_XYZ, +) +from colour.hints import NDArray +from colour.utilities import domain_range_scale + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "SD_SAMPLE", + "LAGRANGE_COEFFICIENTS_A", + "LAGRANGE_COEFFICIENTS_B", + "TWF_A_CIE_1964_10_10", + "TWF_A_CIE_1964_10_20", + "TWF_D65_CIE_1931_2_20", + "TWF_D65_CIE_1931_2_20_K1", + "TWF_D65_CIE_1931_2_20_A", + "DATA_TWO", + "MSDS_TWO", + "TVS_D65_INTEGRATION_MSDS", + "TVS_D65_ARRAY_INTEGRATION", + "TVS_D65_ARRAY_K1_INTEGRATION", + "TVS_D65_ASTME308_MSDS", + "TVS_D65_ASTME308_K1_MSDS", + "TestHandleSpectralArguments", + "TestLagrangeCoefficientsASTME2022", + "TestTristimulusWeightingFactorsASTME2022", + "TestAdjustTristimulusWeightingFactorsASTME308", + "TestSd_to_XYZ_integration", + "TestSd_to_XYZ_ASTME308", + "TestSd_to_XYZ", + "TestMsds_to_XYZ_integration", + "TestMsds_to_XYZ_ASTME308", + "TestWavelength_to_XYZ", +] + +SD_SAMPLE: SpectralDistribution = SpectralDistribution( + { + 340: 0.0000, + 345: 0.0000, + 350: 0.0000, + 355: 0.0000, + 360: 0.0000, + 365: 0.0000, + 370: 0.0000, + 375: 0.0000, + 380: 0.0000, + 385: 0.0000, + 390: 0.0000, + 395: 0.0000, + 400: 0.0641, + 405: 0.0650, + 410: 0.0654, + 415: 0.0652, + 420: 0.0645, + 425: 0.0629, + 430: 0.0605, + 435: 0.0581, + 440: 0.0562, + 445: 0.0551, + 450: 0.0543, + 455: 0.0539, + 460: 0.0537, + 465: 0.0538, + 470: 0.0541, + 475: 0.0547, + 480: 0.0559, + 485: 0.0578, + 490: 0.0603, + 495: 0.0629, + 500: 0.0651, + 505: 0.0667, + 510: 0.0680, + 515: 0.0691, + 520: 0.0705, + 525: 0.0720, + 530: 0.0736, + 535: 0.0753, + 540: 0.0772, + 545: 0.0791, + 550: 0.0809, + 555: 0.0833, + 560: 0.0870, + 565: 0.0924, + 570: 0.0990, + 575: 0.1061, + 580: 0.1128, + 585: 0.1190, + 590: 0.1251, + 595: 0.1308, + 600: 0.1360, + 605: 0.1403, + 610: 0.1439, + 615: 0.1473, + 620: 0.1511, + 625: 0.1550, + 630: 0.1590, + 635: 0.1634, + 640: 0.1688, + 645: 0.1753, + 650: 0.1828, + 655: 0.1909, + 660: 0.1996, + 665: 0.2088, + 670: 0.2187, + 675: 0.2291, + 680: 0.2397, + 685: 0.2505, + 690: 0.2618, + 695: 0.2733, + 700: 0.2852, + 705: 0.0000, + 710: 0.0000, + 715: 0.0000, + 720: 0.0000, + 725: 0.0000, + 730: 0.0000, + 735: 0.0000, + 740: 0.0000, + 745: 0.0000, + 750: 0.0000, + 755: 0.0000, + 760: 0.0000, + 765: 0.0000, + 770: 0.0000, + 775: 0.0000, + 780: 0.0000, + 785: 0.0000, + 790: 0.0000, + 795: 0.0000, + 800: 0.0000, + 805: 0.0000, + 810: 0.0000, + 815: 0.0000, + 820: 0.0000, + 825: 0.0000, + 830: 0.0000, + } +) + +LAGRANGE_COEFFICIENTS_A: NDArray = np.array( + [ + [-0.0285, 0.9405, 0.1045, -0.0165], + [-0.0480, 0.8640, 0.2160, -0.0320], + [-0.0595, 0.7735, 0.3315, -0.0455], + [-0.0640, 0.6720, 0.4480, -0.0560], + [-0.0625, 0.5625, 0.5625, -0.0625], + [-0.0560, 0.4480, 0.6720, -0.0640], + [-0.0455, 0.3315, 0.7735, -0.0595], + [-0.0320, 0.2160, 0.8640, -0.0480], + [-0.0165, 0.1045, 0.9405, -0.0285], + ] +) + +LAGRANGE_COEFFICIENTS_B: NDArray = np.array( + [ + [0.8550, 0.1900, -0.0450], + [0.7200, 0.3600, -0.0800], + [0.5950, 0.5100, -0.1050], + [0.4800, 0.6400, -0.1200], + [0.3750, 0.7500, -0.1250], + [0.2800, 0.8400, -0.1200], + [0.1950, 0.9100, -0.1050], + [0.1200, 0.9600, -0.0800], + [0.0550, 0.9900, -0.0450], + ] +) + +TWF_A_CIE_1964_10_10: NDArray = np.array( + [ + [-0.000, -0.000, -0.000], + [-0.000, -0.000, -0.000], + [-0.000, -0.000, -0.000], + [0.002, 0.000, 0.008], + [0.025, 0.003, 0.110], + [0.134, 0.014, 0.615], + [0.377, 0.039, 1.792], + [0.686, 0.084, 3.386], + [0.964, 0.156, 4.944], + [1.080, 0.259, 5.806], + [1.006, 0.424, 5.812], + [0.731, 0.696, 4.919], + [0.343, 1.082, 3.300], + [0.078, 1.616, 1.973], + [0.022, 2.422, 1.152], + [0.218, 3.529, 0.658], + [0.750, 4.840, 0.382], + [1.642, 6.100, 0.211], + [2.842, 7.250, 0.102], + [4.336, 8.114, 0.032], + [6.200, 8.758, 0.001], + [8.262, 8.988, -0.000], + [10.227, 8.760, 0.000], + [11.945, 8.304, 0.000], + [12.746, 7.468, 0.000], + [12.337, 6.323, 0.000], + [10.817, 5.033, 0.000], + [8.560, 3.744, 0.000], + [6.014, 2.506, 0.000], + [3.887, 1.560, 0.000], + [2.309, 0.911, 0.000], + [1.276, 0.499, 0.000], + [0.666, 0.259, 0.000], + [0.336, 0.130, 0.000], + [0.166, 0.065, 0.000], + [0.082, 0.032, 0.000], + [0.040, 0.016, 0.000], + [0.020, 0.008, 0.000], + [0.010, 0.004, 0.000], + [0.005, 0.002, 0.000], + [0.003, 0.001, 0.000], + [0.001, 0.001, 0.000], + [0.001, 0.000, 0.000], + [0.000, 0.000, 0.000], + [0.000, 0.000, 0.000], + [0.000, 0.000, 0.000], + [0.000, 0.000, 0.000], + [0.000, 0.000, 0.000], + ] +) + +TWF_A_CIE_1964_10_20: NDArray = np.array( + [ + [-0.000, -0.000, -0.001], + [-0.009, -0.001, -0.041], + [0.060, 0.005, 0.257], + [0.773, 0.078, 3.697], + [1.900, 0.304, 9.755], + [1.971, 0.855, 11.487], + [0.718, 2.146, 6.785], + [0.043, 4.899, 2.321], + [1.522, 9.647, 0.743], + [5.677, 14.461, 0.196], + [12.445, 17.474, 0.005], + [20.554, 17.584, -0.003], + [25.332, 14.896, 0.000], + [21.571, 10.080, 0.000], + [12.179, 5.068, 0.000], + [4.668, 1.830, 0.000], + [1.324, 0.513, 0.000], + [0.318, 0.123, 0.000], + [0.075, 0.029, 0.000], + [0.018, 0.007, 0.000], + [0.005, 0.002, 0.000], + [0.001, 0.001, 0.000], + [0.000, 0.000, 0.000], + [0.000, 0.000, 0.000], + ] +) + +TWF_D65_CIE_1931_2_20: NDArray = np.array( + [ + [-0.001, -0.000, -0.005], + [-0.008, -0.000, -0.039], + [0.179, 0.002, 0.829], + [2.542, 0.071, 12.203], + [6.670, 0.453, 33.637], + [6.333, 1.316, 36.334], + [2.213, 2.933, 18.278], + [0.052, 6.866, 5.543], + [1.348, 14.106, 1.611], + [5.767, 18.981, 0.382], + [11.301, 18.863, 0.068], + [16.256, 15.455, 0.025], + [17.933, 10.699, 0.013], + [14.020, 6.277, 0.003], + [7.057, 2.743, 0.000], + [2.527, 0.927, -0.000], + [0.670, 0.242, -0.000], + [0.140, 0.050, 0.000], + [0.035, 0.013, 0.000], + [0.008, 0.003, 0.000], + [0.002, 0.001, 0.000], + [0.000, 0.000, 0.000], + [0.000, 0.000, 0.000], + [0.000, 0.000, 0.000], + ] +) + +TWF_D65_CIE_1931_2_20_K1: NDArray = np.array( + [ + [-0.10095678, -0.00265636, -0.48295051], + [-0.83484763, -0.02190274, -4.11563004], + [18.94315946, 0.22803520, 87.62930101], + [268.66426663, 7.45156533, 1289.46785306], + [704.84093814, 47.85119956, 3554.47729494], + [669.16372619, 139.09334752, 3839.47028848], + [233.89418975, 309.95075927, 1931.47489098], + [5.51347204, 725.55154566, 585.77542998], + [142.48116090, 1490.55009270, 170.27819443], + [609.43424752, 2005.73581058, 40.34233506], + [1194.21293134, 1993.32004423, 7.15981395], + [1717.79835378, 1633.12477710, 2.59651081], + [1895.00791740, 1130.54333854, 1.34461357], + [1481.55235852, 663.25632432, 0.29999368], + [745.76471129, 289.85683288, 0.01943154], + [267.01875994, 97.97358872, -0.00261658], + [70.75239887, 25.56445574, -0.00019929], + [14.78862574, 5.31713332, 0.00000000], + [3.67620064, 1.32650433, 0.00000000], + [0.89699648, 0.32392186, 0.00000000], + [0.16623785, 0.06003153, 0.00000000], + [0.04824448, 0.01742197, 0.00000000], + [0.01310759, 0.00473339, 0.00000000], + [0.00223616, 0.00080752, 0.00000000], + ] +) + +TWF_D65_CIE_1931_2_20_A: NDArray = np.array( + [ + [0.170, 0.002, 0.785], + [2.542, 0.071, 12.203], + [6.670, 0.453, 33.637], + [6.333, 1.316, 36.334], + [2.213, 2.933, 18.278], + [0.052, 6.866, 5.543], + [1.348, 14.106, 1.611], + [5.767, 18.981, 0.382], + [11.301, 18.863, 0.068], + [16.256, 15.455, 0.025], + [17.933, 10.699, 0.013], + [14.020, 6.277, 0.003], + [7.057, 2.743, 0.000], + [2.527, 0.927, -0.000], + [0.670, 0.242, -0.000], + [0.185, 0.067, 0.000], + ] +) + +DATA_TWO: NDArray = np.array( + [ + [ + [ + 0.01367208, + 0.09127947, + 0.01524376, + 0.02810712, + 0.19176012, + 0.04299992, + ], + [ + 0.01591516, + 0.31454948, + 0.08416876, + 0.09071489, + 0.71026170, + 0.04374762, + ], + [ + 0.00959792, + 0.25822842, + 0.41388571, + 0.22275120, + 0.00407416, + 0.37439537, + ], + [ + 0.01106279, + 0.07090867, + 0.02204929, + 0.12487984, + 0.18168917, + 0.00202945, + ], + [ + 0.01791409, + 0.29707789, + 0.56295109, + 0.23752193, + 0.00236515, + 0.58190280, + ], + [ + 0.10565346, + 0.46204320, + 0.19180590, + 0.56250858, + 0.42085907, + 0.00270085, + ], + ], + [ + [ + 0.04325933, + 0.26825359, + 0.23732357, + 0.05175860, + 0.01181048, + 0.08233768, + ], + [ + 0.02577249, + 0.08305486, + 0.04303044, + 0.32298771, + 0.23022813, + 0.00813306, + ], + [ + 0.02484169, + 0.12027161, + 0.00541695, + 0.00654612, + 0.18603799, + 0.36247808, + ], + [ + 0.01861601, + 0.12924391, + 0.00785840, + 0.40062562, + 0.94044405, + 0.32133976, + ], + [ + 0.03102159, + 0.16815442, + 0.37186235, + 0.08610666, + 0.00413520, + 0.78492409, + ], + [ + 0.04727245, + 0.32210270, + 0.22679484, + 0.31613642, + 0.11242847, + 0.00244144, + ], + ], + ] +) + +MSDS_TWO: MultiSpectralDistributions = MultiSpectralDistributions( + np.transpose(np.reshape(DATA_TWO, [-1, 6])), + SpectralShape(400, 700, 60).range(), +) + +TVS_D65_INTEGRATION_MSDS: NDArray = np.array( + [ + [7.50219602, 3.95048275, 8.40152163], + [26.92629005, 15.07170066, 28.71020457], + [16.70060700, 28.21421317, 25.64802044], + [11.57577260, 8.64108703, 6.57740493], + [18.73108262, 35.07369122, 30.14365007], + [45.16559608, 39.61411218, 43.68158810], + [8.17318743, 13.09236381, 25.93755134], + [22.46715798, 19.31066951, 7.95954422], + [6.58106180, 2.52865132, 11.09122159], + [43.91745731, 27.98043364, 11.73313699], + [8.53693599, 19.70195654, 17.70110118], + [23.91114755, 26.21471641, 30.67613685], + ] +) + +TVS_D65_ARRAY_INTEGRATION: NDArray = np.array( + [ + [ + [7.19510558, 3.86227393, 10.09950719], + [25.57464912, 14.71934603, 34.84931928], + [17.58300551, 28.56388139, 30.18370150], + [11.32631694, 8.46087304, 7.90263107], + [19.65793587, 35.59047030, 35.14042633], + [45.82162927, 39.26057155, 51.79537877], + ], + [ + [8.82617380, 13.38600040, 30.56510531], + [22.33167167, 18.95683859, 9.39034481], + [6.69130415, 2.57592352, 13.25898396], + [41.81950400, 27.11920225, 14.26746010], + [9.24148668, 20.20448258, 20.19416075], + [24.78545992, 26.22388193, 36.44325237], + ], + ] +) + +TVS_D65_ARRAY_K1_INTEGRATION: NDArray = np.array( + [ + [ + [776.11755347, 416.61356647, 1089.40789347], + [2758.67169575, 1587.73804035, 3759.10653835], + [1896.63363103, 3081.11250154, 3255.83833518], + [1221.74071019, 912.65263825, 852.43651028], + [2120.45103874, 3839.05259546, 3790.50750799], + [4942.66142767, 4234.93698798, 5587.03443988], + ], + [ + [952.05669281, 1443.91347280, 3296.97938475], + [2408.86005021, 2044.82547598, 1012.91236929], + [721.77378835, 277.85825207, 1430.21253594], + [4510.96245706, 2925.27867434, 1538.99426670], + [996.85542597, 2179.40562871, 2178.29223889], + [2673.54388456, 2828.70277157, 3931.04000571], + ], + ] +) + +TVS_D65_ASTME308_MSDS: NDArray = np.array( + [ + [7.50450425, 3.95744742, 8.38735462], + [26.94116124, 15.09801442, 28.66753115], + [16.70212538, 28.20596151, 25.65809190], + [11.57025728, 8.64549437, 6.55935421], + [18.74248163, 35.06128859, 30.17576781], + [45.12240306, 39.62432052, 43.58455883], + [8.17632546, 13.09396693, 25.92811880], + [22.44582614, 19.31227394, 7.92623840], + [6.57937576, 2.53370970, 11.07068448], + [43.91405117, 28.00039763, 11.68910584], + [8.54996478, 19.69029667, 17.73601959], + [23.88899194, 26.21653407, 30.62958339], + ] +) + +TVS_D65_ASTME308_K1_MSDS: NDArray = np.array( + [ + [793.00584037, 418.18604067, 886.29721234], + [2846.89001419, 1595.41699464, 3029.31664392], + [1764.92444185, 2980.54228038, 2711.30724359], + [1222.63660519, 913.57500818, 693.13122104], + [1980.53021265, 3704.94914782, 3188.69299177], + [4768.11365133, 4187.12769685, 4605.60865221], + [863.99762445, 1383.64799389, 2739.84116154], + [2371.86503263, 2040.74053594, 837.57076242], + [695.24691183, 267.73875067, 1169.84642292], + [4640.42632139, 2958.82021193, 1235.19540951], + [903.48033348, 2080.68644330, 1874.17671434], + [2524.36530136, 2770.31818974, 3236.64797963], + ] +) + + +class TestHandleSpectralArguments(unittest.TestCase): + """ + Define :func:`colour.colorimetry.tristimulus_values.\ +handle_spectral_arguments` definition unit tests methods. + """ + + def test_handle_spectral_arguments(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.\ +handle_spectral_arguments` definition. + """ + + cmfs, illuminant = handle_spectral_arguments() + # pylint: disable=E1102 + self.assertEqual( + cmfs, + reshape_msds(MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]), + ) + self.assertEqual(illuminant, reshape_sd(SDS_ILLUMINANTS["D65"])) + + shape = SpectralShape(400, 700, 20) + cmfs, illuminant = handle_spectral_arguments(shape_default=shape) + self.assertEqual(cmfs.shape, shape) + self.assertEqual(illuminant.shape, shape) + + cmfs, illuminant = handle_spectral_arguments( + cmfs_default="CIE 2012 2 Degree Standard Observer", + illuminant_default="E", + shape_default=shape, + ) + self.assertEqual( + cmfs, + reshape_msds( + MSDS_CMFS["CIE 2012 2 Degree Standard Observer"], shape=shape + ), + ) + self.assertEqual( + illuminant, sd_ones(shape, interpolator=LinearInterpolator) * 100 + ) + + +class TestLagrangeCoefficientsASTME2022(unittest.TestCase): + """ + Define :func:`colour.colorimetry.tristimulus_values.\ +lagrange_coefficients_ASTME2022` definition unit tests methods. + """ + + def test_lagrange_coefficients_ASTME2022(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.\ +lagrange_coefficients_ASTME2022` definition. + """ + + np.testing.assert_almost_equal( + lagrange_coefficients_ASTME2022(10, "inner"), + LAGRANGE_COEFFICIENTS_A, + decimal=7, + ) + + np.testing.assert_almost_equal( + lagrange_coefficients_ASTME2022(10, "boundary"), + LAGRANGE_COEFFICIENTS_B, + decimal=7, + ) + + # Testing that the cache returns a copy of the data. + lagrange_coefficients = lagrange_coefficients_ASTME2022(10) + + np.testing.assert_almost_equal( + lagrange_coefficients, LAGRANGE_COEFFICIENTS_A, decimal=7 + ) + + lagrange_coefficients *= 10 + + np.testing.assert_almost_equal( + lagrange_coefficients_ASTME2022(10), + LAGRANGE_COEFFICIENTS_A, + decimal=7, + ) + + +class TestTristimulusWeightingFactorsASTME2022(unittest.TestCase): + """ + Define :func:`colour.colorimetry.tristimulus_values.\ +tristimulus_weighting_factors_ASTME2022` definition unit tests methods. + """ + + def test_tristimulus_weighting_factors_ASTME2022(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.\ +tristimulus_weighting_factors_ASTME2022` definition. + + Notes + ----- + - :attr:`TWF_A_CIE_1964_10_10`, :attr:`TWF_A_CIE_1964_10_20` and + :attr:`TWF_D65_CIE_1931_2_20` attributes data is matching + :cite:`ASTMInternational2015b`. + + References + ---------- + :cite:`ASTMInternational2015b` + """ + + cmfs = MSDS_CMFS["CIE 1964 10 Degree Standard Observer"] + A = sd_CIE_standard_illuminant_A(cmfs.shape) + + twf = tristimulus_weighting_factors_ASTME2022( + cmfs, A, SpectralShape(360, 830, 10) + ) + np.testing.assert_almost_equal( + np.round(twf, 3), TWF_A_CIE_1964_10_10, decimal=3 + ) + + twf = tristimulus_weighting_factors_ASTME2022( + cmfs, A, SpectralShape(360, 830, 20) + ) + np.testing.assert_almost_equal( + np.round(twf, 3), TWF_A_CIE_1964_10_20, decimal=3 + ) + + cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] + D65 = reshape_sd( + SDS_ILLUMINANTS["D65"], cmfs.shape, interpolator=LinearInterpolator + ) + twf = tristimulus_weighting_factors_ASTME2022( + cmfs, D65, SpectralShape(360, 830, 20) + ) + np.testing.assert_almost_equal( + np.round(twf, 3), TWF_D65_CIE_1931_2_20, decimal=3 + ) + + twf = tristimulus_weighting_factors_ASTME2022( + cmfs, D65, SpectralShape(360, 830, 20), k=1 + ) + np.testing.assert_almost_equal( + twf, TWF_D65_CIE_1931_2_20_K1, decimal=7 + ) + + # Testing that the cache returns a copy of the data. + cmfs = MSDS_CMFS["CIE 1964 10 Degree Standard Observer"] + twf = tristimulus_weighting_factors_ASTME2022( + cmfs, A, SpectralShape(360, 830, 10) + ) + np.testing.assert_almost_equal( + np.round(twf, 3), TWF_A_CIE_1964_10_10, decimal=3 + ) + + np.testing.assert_almost_equal( + np.round( + tristimulus_weighting_factors_ASTME2022( + cmfs, A, SpectralShape(360, 830, 10) + ), + 3, + ), + TWF_A_CIE_1964_10_10, + decimal=3, + ) + + def test_raise_exception_tristimulus_weighting_factors_ASTME2022(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.\ +tristimulus_weighting_factors_ASTME2022` definition raised exception. + """ + + shape = SpectralShape(360, 830, 10) + cmfs_1 = MSDS_CMFS["CIE 1964 10 Degree Standard Observer"] + # pylint: disable=E1102 + cmfs_2 = reshape_msds(cmfs_1, shape) + A_1 = sd_CIE_standard_illuminant_A(cmfs_1.shape) + A_2 = sd_CIE_standard_illuminant_A(cmfs_2.shape) + + self.assertRaises( + ValueError, + tristimulus_weighting_factors_ASTME2022, + cmfs_1, + A_2, + shape, + ) + + self.assertRaises( + ValueError, + tristimulus_weighting_factors_ASTME2022, + cmfs_2, + A_1, + shape, + ) + + +class TestAdjustTristimulusWeightingFactorsASTME308(unittest.TestCase): + """ + Define :func:`colour.colorimetry.tristimulus_values.\ +adjust_tristimulus_weighting_factors_ASTME308` definition unit tests methods. + """ + + def test_adjust_tristimulus_weighting_factors_ASTME308(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.\ +adjust_tristimulus_weighting_factors_ASTME308` definition. + """ + + np.testing.assert_almost_equal( + adjust_tristimulus_weighting_factors_ASTME308( + TWF_D65_CIE_1931_2_20, + SpectralShape(360, 830, 20), + SpectralShape(400, 700, 20), + ), + TWF_D65_CIE_1931_2_20_A, + decimal=3, + ) + + +class TestSd_to_XYZ_integration(unittest.TestCase): + """ + Define :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ_integration` + definition unit tests methods. + """ + + def test_sd_to_XYZ_integration(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.\ +sd_to_XYZ_integration` definition. + """ + + cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] + np.testing.assert_almost_equal( + sd_to_XYZ_integration(SD_SAMPLE, cmfs, SDS_ILLUMINANTS["A"]), + np.array([14.46341147, 10.85819624, 2.04695585]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_integration( + SD_SAMPLE.values, + cmfs, + SDS_ILLUMINANTS["A"], + shape=SD_SAMPLE.shape, + ), + np.array([14.46365947, 10.85828084, 2.04663993]), + decimal=7, + ) + + cmfs = MSDS_CMFS["CIE 1964 10 Degree Standard Observer"] + np.testing.assert_almost_equal( + sd_to_XYZ_integration(SD_SAMPLE, cmfs, SDS_ILLUMINANTS["C"]), + np.array([10.77002699, 9.44876636, 6.62415290]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_integration(SD_SAMPLE, cmfs, SDS_ILLUMINANTS["FL2"]), + np.array([11.57540576, 9.98608874, 3.95242590]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_integration( + SD_SAMPLE, cmfs, SDS_ILLUMINANTS["FL2"], k=683 + ), + np.array([122375.09261493, 105572.84645912, 41785.01342332]), + decimal=7, + ) + + def test_domain_range_scale_sd_to_XYZ_integration(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.\ +sd_to_XYZ_integration` definition domain and range scale support. + """ + + cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] + XYZ = sd_to_XYZ_integration(SD_SAMPLE, cmfs, SDS_ILLUMINANTS["A"]) + + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + sd_to_XYZ_integration( + SD_SAMPLE, cmfs, SDS_ILLUMINANTS["A"] + ), + XYZ * factor, + decimal=7, + ) + + +class TestSd_to_XYZ_tristimulus_weighting_factors_ASTME308(unittest.TestCase): + """ + Define :func:`colour.colorimetry.tristimulus_values.\ +sd_to_XYZ_tristimulus_weighting_factors_ASTME308` + definition unit tests methods. + """ + + def test_sd_to_XYZ_tristimulus_weighting_factors_ASTME308(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.\ +sd_to_XYZ_tristimulus_weighting_factors_ASTME308` + definition. + """ + + cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] + np.testing.assert_almost_equal( + sd_to_XYZ_tristimulus_weighting_factors_ASTME308( + SD_SAMPLE, cmfs, SDS_ILLUMINANTS["A"] + ), + np.array([14.46341867, 10.85820227, 2.04697034]), + decimal=7, + ) + + cmfs = MSDS_CMFS["CIE 1964 10 Degree Standard Observer"] + np.testing.assert_almost_equal( + sd_to_XYZ_tristimulus_weighting_factors_ASTME308( + SD_SAMPLE, cmfs, SDS_ILLUMINANTS["C"] + ), + np.array([10.77005571, 9.44877491, 6.62428210]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_tristimulus_weighting_factors_ASTME308( + SD_SAMPLE, cmfs, SDS_ILLUMINANTS["FL2"] + ), + np.array([11.57542759, 9.98605604, 3.95273304]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_tristimulus_weighting_factors_ASTME308( + reshape_sd(SD_SAMPLE, SpectralShape(400, 700, 5), "Trim"), + cmfs, + SDS_ILLUMINANTS["A"], + ), + np.array([14.38153638, 10.74503131, 2.01613844]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_tristimulus_weighting_factors_ASTME308( + reshape_sd( + SD_SAMPLE, SpectralShape(400, 700, 10), "Interpolate" + ), + cmfs, + SDS_ILLUMINANTS["A"], + ), + np.array([14.38257202, 10.74568178, 2.01588427]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_tristimulus_weighting_factors_ASTME308( + reshape_sd( + SD_SAMPLE, SpectralShape(400, 700, 20), "Interpolate" + ), + cmfs, + SDS_ILLUMINANTS["A"], + ), + np.array([14.38329645, 10.74603515, 2.01561113]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_tristimulus_weighting_factors_ASTME308( + reshape_sd( + SD_SAMPLE, SpectralShape(400, 700, 20), "Interpolate" + ), + cmfs, + SDS_ILLUMINANTS["A"], + k=1, + ), + np.array([1636.74881983, 1222.84626486, 229.36669308]), + decimal=7, + ) + + def test_domain_range_scale_sd_to_XYZ_twf_ASTME308(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.\ +sd_to_XYZ_tristimulus_weighting_factors_ASTME308` definition domain and + range scale support. + """ + + cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] + XYZ = sd_to_XYZ_tristimulus_weighting_factors_ASTME308( + SD_SAMPLE, cmfs, SDS_ILLUMINANTS["A"] + ) + + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + sd_to_XYZ_tristimulus_weighting_factors_ASTME308( + SD_SAMPLE, cmfs, SDS_ILLUMINANTS["A"] + ), + XYZ * factor, + decimal=7, + ) + + +class TestSd_to_XYZ_ASTME308(unittest.TestCase): + """ + Define :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ_ASTME308` + definition unit tests methods. + """ + + def setUp(self): + """Initialise the common tests attributes.""" + + self._sd = SD_SAMPLE.copy() + self._cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] + self._A = sd_CIE_standard_illuminant_A(self._cmfs.shape) + + def test_sd_to_XYZ_ASTME308_mi_1nm(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ_ASTME308` + definition for 1 nm measurement intervals. + """ + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, self._cmfs.shape), self._cmfs, self._A + ), + np.array([14.46372680, 10.85832950, 2.04663200]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, self._cmfs.shape), + self._cmfs, + self._A, + use_practice_range=False, + ), + np.array([14.46366018, 10.85827949, 2.04662258]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(400, 700, 1)), + self._cmfs, + self._A, + ), + np.array([14.54173397, 10.88628632, 2.04965822]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(400, 700, 1)), + self._cmfs, + self._A, + use_practice_range=False, + ), + np.array([14.54203076, 10.88636754, 2.04964877]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(400, 700, 1)), + self._cmfs, + self._A, + k=1, + ), + np.array([1568.98152997, 1174.57671769, 221.14803420]), + decimal=7, + ) + + def test_sd_to_XYZ_ASTME308_mi_5nm(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ_ASTME308` + definition for 5 nm measurement intervals. + """ + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(360, 830, 5)), + self._cmfs, + self._A, + ), + np.array([14.46372173, 10.85832502, 2.04664734]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(360, 830, 5)), + self._cmfs, + self._A, + use_practice_range=False, + ), + np.array([14.46366388, 10.85828159, 2.04663915]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(360, 830, 5)), + self._cmfs, + self._A, + mi_5nm_omission_method=False, + ), + np.array([14.46373399, 10.85833553, 2.0466465]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(400, 700, 5)), + self._cmfs, + self._A, + ), + np.array([14.54025742, 10.88576251, 2.04950226]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(400, 700, 5)), + self._cmfs, + self._A, + use_practice_range=False, + ), + np.array([14.54051517, 10.88583304, 2.04949406]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(400, 700, 5)), + self._cmfs, + self._A, + mi_5nm_omission_method=False, + ), + np.array([14.54022093, 10.88575468, 2.04951057]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(360, 830, 5)), + self._cmfs, + self._A, + use_practice_range=False, + mi_5nm_omission_method=False, + ), + np.array([14.46366737, 10.85828552, 2.04663707]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(400, 700, 5)), + self._cmfs, + self._A, + use_practice_range=False, + mi_5nm_omission_method=False, + ), + np.array([14.54051772, 10.88583590, 2.04950113]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(400, 700, 5)), + self._cmfs, + self._A, + k=1, + ), + np.array([1568.82479013, 1174.52212708, 221.13156963]), + decimal=7, + ) + + def test_sd_to_XYZ_ASTME308_mi_10nm(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ_ASTME308` + definition for 10 nm measurement intervals. + """ + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(360, 830, 10)), + self._cmfs, + self._A, + ), + np.array([14.47779980, 10.86358645, 2.04751388]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(360, 830, 10)), + self._cmfs, + self._A, + use_practice_range=False, + ), + np.array([14.47773312, 10.86353641, 2.04750445]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(400, 700, 10)), + self._cmfs, + self._A, + ), + np.array([14.54137532, 10.88641727, 2.04931318]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(400, 700, 10)), + self._cmfs, + self._A, + use_practice_range=False, + ), + np.array([14.54167211, 10.88649849, 2.04930374]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(400, 700, 10)), + self._cmfs, + self._A, + k=1, + ), + np.array([1568.94283333, 1174.59084705, 221.11080639]), + decimal=7, + ) + + def test_sd_to_XYZ_ASTME308_mi_20nm(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ_ASTME308` + definition for 20 nm measurement intervals. + """ + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(360, 820, 20)), + self._cmfs, + self._A, + ), + np.array([14.50187464, 10.87217124, 2.04918305]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(360, 820, 20)), + self._cmfs, + self._A, + use_practice_range=False, + ), + np.array([14.50180785, 10.87212116, 2.04917361]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(360, 820, 20)), + self._cmfs, + self._A, + mi_20nm_interpolation_method=False, + ), + np.array([14.50216194, 10.87236873, 2.04977256]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(400, 700, 20)), + self._cmfs, + self._A, + ), + np.array([14.54114025, 10.88634755, 2.04916445]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(400, 700, 20)), + self._cmfs, + self._A, + use_practice_range=False, + ), + np.array([14.54143704, 10.88642877, 2.04915501]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(400, 700, 20)), + self._cmfs, + self._A, + mi_20nm_interpolation_method=False, + ), + np.array([14.54242562, 10.88694088, 2.04919645]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(360, 820, 20)), + self._cmfs, + self._A, + use_practice_range=False, + mi_20nm_interpolation_method=False, + ), + np.array([14.50209515, 10.87231865, 2.04976312]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(400, 700, 20)), + self._cmfs, + self._A, + use_practice_range=False, + mi_20nm_interpolation_method=False, + ), + np.array([14.54272240, 10.88702210, 2.04918701]), + decimal=7, + ) + + np.testing.assert_almost_equal( + sd_to_XYZ_ASTME308( + reshape_sd(self._sd, SpectralShape(400, 700, 20)), + self._cmfs, + self._A, + k=1, + ), + np.array([1568.91747040, 1174.58332427, 221.09475945]), + decimal=7, + ) + + def test_raise_exception_sd_to_XYZ_ASTME308(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ_ASTME308` + definition raised exception. + """ + + self.assertRaises( + ValueError, + sd_to_XYZ_ASTME308, + reshape_sd(self._sd, SpectralShape(360, 820, 2)), + ) + + +class TestSd_to_XYZ(unittest.TestCase): + """ + Define :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ` definition + unit tests methods. + """ + + def setUp(self): + """Initialise the common tests attributes.""" + + self._cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] + self._A = sd_CIE_standard_illuminant_A(self._cmfs.shape) + self._sd = reshape_sd(SD_SAMPLE, self._cmfs.shape) + + def test_sd_to_XYZ(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.sd_to_XYZ` + definition. + """ + + # Testing that the cache returns a copy of the data. + XYZ = sd_to_XYZ(self._sd, self._cmfs, self._A) + + np.testing.assert_almost_equal( + XYZ, np.array([14.46372680, 10.85832950, 2.04663200]), decimal=7 + ) + + XYZ *= 10 + + np.testing.assert_almost_equal( + sd_to_XYZ(self._sd, self._cmfs, self._A), + np.array([14.46372680, 10.85832950, 2.04663200]), + decimal=7, + ) + + +class TestMsds_to_XYZ_integration(unittest.TestCase): + """ + Define :func:`colour.colorimetry.tristimulus_values.\ +msds_to_XYZ_integration` definition unit tests methods. + """ + + def test_msds_to_XYZ_integration(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.\ +msds_to_XYZ_integration` definition. + """ + + cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] + np.testing.assert_almost_equal( + msds_to_XYZ_integration(MSDS_TWO, cmfs, SDS_ILLUMINANTS["D65"]), + TVS_D65_INTEGRATION_MSDS, + decimal=7, + ) + + np.testing.assert_almost_equal( + msds_to_XYZ_integration( + DATA_TWO, + cmfs, + SDS_ILLUMINANTS["D65"], + shape=SpectralShape(400, 700, 60), + ), + TVS_D65_ARRAY_INTEGRATION, + decimal=7, + ) + + np.testing.assert_almost_equal( + msds_to_XYZ_integration( + DATA_TWO, + cmfs, + SDS_ILLUMINANTS["D65"], + 1, + shape=SpectralShape(400, 700, 60), + ), + TVS_D65_ARRAY_K1_INTEGRATION, + decimal=7, + ) + + def test_domain_range_scale_msds_to_XYZ_integration(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.\ +msds_to_XYZ_integration` definition domain and range scale support. + """ + + cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + msds_to_XYZ_integration( + DATA_TWO, + cmfs, + SDS_ILLUMINANTS["D65"], + shape=SpectralShape(400, 700, 60), + ), + TVS_D65_ARRAY_INTEGRATION * factor, + decimal=7, + ) + + +class TestMsds_to_XYZ_ASTME308(unittest.TestCase): + """ + Define :func:`colour.colorimetry.tristimulus_values.msds_to_XYZ_ASTME308` + definition unit tests methods. + """ + + def test_msds_to_XYZ_ASTME308(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.\ +msds_to_XYZ_ASTME308` definition. + """ + + cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] + # pylint: disable=E1102 + msds = reshape_msds(MSDS_TWO, SpectralShape(400, 700, 20)) + np.testing.assert_almost_equal( + msds_to_XYZ_ASTME308(msds, cmfs, SDS_ILLUMINANTS["D65"]), + TVS_D65_ASTME308_MSDS, + decimal=7, + ) + + np.testing.assert_almost_equal( + msds_to_XYZ_ASTME308(msds, cmfs, SDS_ILLUMINANTS["D65"], k=1), + TVS_D65_ASTME308_K1_MSDS, + decimal=7, + ) + + def test_domain_range_scale_msds_to_XYZ_ASTME308(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.\ +msds_to_XYZ_ASTME308` definition domain and range scale support. + """ + + cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) + for scale, factor in d_r: + with domain_range_scale(scale): + # pylint: disable=E1102 + np.testing.assert_almost_equal( + msds_to_XYZ_ASTME308( + reshape_msds(MSDS_TWO, SpectralShape(400, 700, 20)), + cmfs, + SDS_ILLUMINANTS["D65"], + ), + TVS_D65_ASTME308_MSDS * factor, + decimal=7, + ) + + def test_raise_exception_msds_to_XYZ_ASTME308(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.\ +msds_to_XYZ_ASTME308` definition raise exception. + """ + + self.assertRaises(ValueError, msds_to_XYZ_ASTME308, DATA_TWO) + + +class TestWavelength_to_XYZ(unittest.TestCase): + """ + Define :func:`colour.colorimetry.tristimulus_values.wavelength_to_XYZ` + definition unit tests methods. + """ + + def test_wavelength_to_XYZ(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.wavelength_to_XYZ` + definition. + """ + + np.testing.assert_almost_equal( + wavelength_to_XYZ( + 480, MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] + ), + np.array([0.09564, 0.13902, 0.81295]), + decimal=7, + ) + + np.testing.assert_almost_equal( + wavelength_to_XYZ( + 480, MSDS_CMFS["CIE 2012 2 Degree Standard Observer"] + ), + np.array([0.08182895, 0.17880480, 0.75523790]), + decimal=7, + ) + + np.testing.assert_almost_equal( + wavelength_to_XYZ( + 641.5, MSDS_CMFS["CIE 2012 2 Degree Standard Observer"] + ), + np.array([0.44575583, 0.18184213, 0.00000000]), + decimal=7, + ) + + def test_raise_exception_wavelength_to_XYZ(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.wavelength_to_XYZ` + definition raised exception. + """ + + self.assertRaises(ValueError, wavelength_to_XYZ, 1) + + self.assertRaises(ValueError, wavelength_to_XYZ, 1000) + + def test_n_dimensional_wavelength_to_XYZ(self): + """ + Test :func:`colour.colorimetry.tristimulus_values.wavelength_to_XYZ` + definition n-dimensional arrays support. + """ + + cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] + wl = 480 + XYZ = wavelength_to_XYZ(wl, cmfs) + + wl = np.tile(wl, 6) + XYZ = np.tile(XYZ, (6, 1)) + np.testing.assert_almost_equal( + wavelength_to_XYZ(wl, cmfs), XYZ, decimal=7 + ) + + wl = np.reshape(wl, (2, 3)) + XYZ = np.reshape(XYZ, (2, 3, 3)) + np.testing.assert_almost_equal( + wavelength_to_XYZ(wl, cmfs), XYZ, decimal=7 + ) + + wl = np.reshape(wl, (2, 3, 1)) + XYZ = np.reshape(XYZ, (2, 3, 1, 3)) + np.testing.assert_almost_equal( + wavelength_to_XYZ(wl, cmfs), XYZ, decimal=7 + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/colorimetry/tests/test_uniformity.py b/colour/colorimetry/tests/test_uniformity.py new file mode 100644 index 0000000000..eef4164df5 --- /dev/null +++ b/colour/colorimetry/tests/test_uniformity.py @@ -0,0 +1,254 @@ +"""Defines the unit tests for the :mod:`colour.colorimetry.uniformity` module.""" + +from __future__ import annotations + +import numpy as np +import unittest + +from colour.colorimetry import spectral_uniformity +from colour.hints import NDArray + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "DATA_UNIFORMITY_FIRST_ORDER_DERIVATIVES", + "TestSpectralUniformity", +] + +DATA_UNIFORMITY_FIRST_ORDER_DERIVATIVES: NDArray = np.array( + [ + 9.55142857e-06, + 1.14821429e-05, + 1.87842857e-05, + 2.87114286e-05, + 3.19714286e-05, + 3.23428571e-05, + 3.38500000e-05, + 3.99257143e-05, + 4.13335714e-05, + 2.40021429e-05, + 5.76214286e-06, + 1.47571429e-06, + 9.79285714e-07, + 2.00571429e-06, + 3.71571429e-06, + 5.76785714e-06, + 7.55571429e-06, + 7.46357143e-06, + 5.74928571e-06, + 3.86928571e-06, + 3.54071429e-06, + 4.47428571e-06, + 5.64357143e-06, + 7.63714286e-06, + 1.01714286e-05, + 1.22542857e-05, + 1.48100000e-05, + 1.65171429e-05, + 1.54307143e-05, + 1.45364286e-05, + 1.40378571e-05, + 1.15878571e-05, + 1.07435714e-05, + 1.09792857e-05, + 1.03985714e-05, + 8.29714286e-06, + 6.30571429e-06, + 5.09428571e-06, + 4.85000000e-06, + 5.53714286e-06, + 6.41285714e-06, + 7.25928571e-06, + 7.77500000e-06, + 7.16071429e-06, + 6.66357143e-06, + 6.73285714e-06, + 7.53071429e-06, + 1.07335714e-05, + 1.62342857e-05, + 2.25707143e-05, + 2.70564286e-05, + 2.77814286e-05, + 2.50257143e-05, + 1.79664286e-05, + 1.05050000e-05, + 5.96571429e-06, + 3.64214286e-06, + 2.16642857e-06, + 1.29357143e-06, + 8.36428571e-07, + 7.25000000e-07, + 6.39285714e-07, + 6.62857143e-07, + 8.55714286e-07, + 1.45071429e-06, + 2.25428571e-06, + 3.41428571e-06, + 4.98642857e-06, + 6.49071429e-06, + 7.89285714e-06, + 9.16642857e-06, + 9.95214286e-06, + 9.76642857e-06, + 9.31500000e-06, + 8.90928571e-06, + 8.15785714e-06, + 6.89357143e-06, + 5.57214286e-06, + 4.45928571e-06, + 3.47785714e-06, + 2.76500000e-06, + 2.31142857e-06, + 1.70928571e-06, + 1.17714286e-06, + 9.84285714e-07, + 8.82857143e-07, + 7.41428571e-07, + 7.01428571e-07, + 7.08571429e-07, + 6.66428571e-07, + 7.59285714e-07, + 8.70000000e-07, + 8.27142857e-07, + 7.17142857e-07, + 6.60000000e-07, + ] +) + +DATA_UNIFORMITY_SECOND_ORDER_DERIVATIVES: NDArray = np.array( + [ + 7.97142857e-09, + 3.69285714e-08, + 9.21500000e-08, + 6.66714286e-08, + 6.75428571e-08, + 1.30571429e-07, + 1.83300000e-07, + 8.26071429e-08, + 4.10357143e-08, + 1.64628571e-07, + 1.47007143e-07, + 4.51000000e-08, + 2.17285714e-08, + 1.48071429e-08, + 1.10071429e-08, + 7.44285714e-09, + 2.18571429e-09, + 6.13571429e-09, + 1.97571429e-08, + 2.90714286e-08, + 2.38642857e-08, + 1.47428571e-08, + 1.36000000e-08, + 1.38214286e-08, + 1.03000000e-08, + 1.15142857e-08, + 1.10000000e-08, + 1.16500000e-08, + 2.53071429e-08, + 2.91571429e-08, + 1.73857143e-08, + 1.17571429e-08, + 1.42714286e-08, + 1.35642857e-08, + 6.82142857e-09, + 1.81571429e-08, + 2.31285714e-08, + 1.85857143e-08, + 1.46142857e-08, + 9.45714286e-09, + 5.62142857e-09, + 4.75000000e-09, + 4.68571429e-09, + 8.71428571e-09, + 1.58500000e-08, + 2.12142857e-08, + 3.20785714e-08, + 4.23357143e-08, + 3.73000000e-08, + 2.26357143e-08, + 6.57857143e-09, + 3.39285714e-09, + 1.18071429e-08, + 3.26500000e-08, + 3.33500000e-08, + 1.87428571e-08, + 1.10214286e-08, + 7.07142857e-09, + 4.00000000e-09, + 2.15714286e-09, + 1.74285714e-09, + 1.77857143e-09, + 2.10714286e-09, + 3.57857143e-09, + 4.47142857e-09, + 5.59285714e-09, + 6.45000000e-09, + 6.65000000e-09, + 8.52142857e-09, + 7.58571429e-09, + 4.87857143e-09, + 2.58571429e-09, + 2.62857143e-09, + 1.84285714e-09, + 1.41428571e-09, + 2.24285714e-09, + 3.65714286e-09, + 3.47142857e-09, + 3.28571429e-09, + 2.60000000e-09, + 2.27857143e-09, + 1.84285714e-09, + 2.57142857e-09, + 1.32142857e-09, + 5.57142857e-10, + 6.85714286e-10, + 1.10000000e-09, + 6.42857143e-10, + 2.21428571e-10, + 4.50000000e-10, + 7.07142857e-10, + 2.50000000e-10, + 1.85714286e-10, + 2.14285714e-10, + 2.28571429e-10, + ] +) + + +class TestSpectralUniformity(unittest.TestCase): + """ + Define :func:`colour.colorimetry.uniformity.spectral_uniformity` + definition unit tests methods. + """ + + def test_spectral_uniformity(self): + """ + Test :func:`colour.colorimetry.uniformity.spectral_uniformity` + definition. + """ + + from colour.quality.datasets import SDS_TCS + + np.testing.assert_almost_equal( + spectral_uniformity(SDS_TCS.values()), + DATA_UNIFORMITY_FIRST_ORDER_DERIVATIVES, + decimal=7, + ) + + np.testing.assert_almost_equal( + spectral_uniformity( + SDS_TCS.values(), use_second_order_derivatives=True + ), + DATA_UNIFORMITY_SECOND_ORDER_DERIVATIVES, + decimal=7, + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/colorimetry/tests/test_whiteness.py b/colour/colorimetry/tests/test_whiteness.py index ca08fd1d64..f88b26b42c 100644 --- a/colour/colorimetry/tests/test_whiteness.py +++ b/colour/colorimetry/tests/test_whiteness.py @@ -1,70 +1,80 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.colorimetry.whiteness` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.colorimetry.whiteness` module.""" import numpy as np import unittest from itertools import permutations -from colour.colorimetry import (whiteness_Berger1959, whiteness_Taube1960, - whiteness_Stensby1968, whiteness_ASTME313, - whiteness_Ganz1979, whiteness_CIE2004) +from colour.colorimetry import ( + whiteness_Berger1959, + whiteness_Taube1960, + whiteness_Stensby1968, + whiteness_ASTME313, + whiteness_Ganz1979, + whiteness_CIE2004, +) from colour.colorimetry.whiteness import whiteness from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestWhitenessBerger1959', 'TestWhitenessTaube1960', - 'TestWhitenessStensby1968', 'TestWhitenessASTM313', - 'TestWhitenessGanz1979', 'TestWhitenessCIE2004', 'TestWhiteness' + "TestWhitenessBerger1959", + "TestWhitenessTaube1960", + "TestWhitenessStensby1968", + "TestWhitenessASTM313", + "TestWhitenessGanz1979", + "TestWhitenessCIE2004", + "TestWhiteness", ] class TestWhitenessBerger1959(unittest.TestCase): """ - Defines :func:`colour.colorimetry.whiteness.whiteness_Berger1959` + Define :func:`colour.colorimetry.whiteness.whiteness_Berger1959` definition unit tests methods. """ def test_whiteness_Berger1959(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_Berger1959` + Test :func:`colour.colorimetry.whiteness.whiteness_Berger1959` definition. """ self.assertAlmostEqual( whiteness_Berger1959( np.array([95.00000000, 100.00000000, 105.00000000]), - np.array([94.80966767, 100.00000000, 107.30513595])), + np.array([94.80966767, 100.00000000, 107.30513595]), + ), 30.36380179, - places=7) + places=7, + ) self.assertAlmostEqual( whiteness_Berger1959( np.array([105.00000000, 100.00000000, 95.00000000]), - np.array([94.80966767, 100.00000000, 107.30513595])), + np.array([94.80966767, 100.00000000, 107.30513595]), + ), 5.530469280673941, - places=7) + places=7, + ) self.assertAlmostEqual( whiteness_Berger1959( np.array([100.00000000, 100.00000000, 100.00000000]), - np.array([100.00000000, 100.00000000, 100.00000000])), + np.array([100.00000000, 100.00000000, 100.00000000]), + ), 33.300000000000011, - places=7) + places=7, + ) def test_n_dimensional_whiteness_Berger1959(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_Berger1959` + Test :func:`colour.colorimetry.whiteness.whiteness_Berger1959` definition n_dimensional arrays support. """ @@ -76,17 +86,19 @@ def test_n_dimensional_whiteness_Berger1959(self): XYZ_0 = np.tile(XYZ_0, (6, 1)) W = np.tile(W, 6) np.testing.assert_almost_equal( - whiteness_Berger1959(XYZ, XYZ_0), W, decimal=7) + whiteness_Berger1959(XYZ, XYZ_0), W, decimal=7 + ) XYZ = np.reshape(XYZ, (2, 3, 3)) XYZ_0 = np.reshape(XYZ_0, (2, 3, 3)) W = np.reshape(W, (2, 3)) np.testing.assert_almost_equal( - whiteness_Berger1959(XYZ, XYZ_0), W, decimal=7) + whiteness_Berger1959(XYZ, XYZ_0), W, decimal=7 + ) def test_domain_range_scale_whiteness_Berger1959(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_Berger1959` + Test :func:`colour.colorimetry.whiteness.whiteness_Berger1959` definition domain and range scale support. """ @@ -94,18 +106,19 @@ def test_domain_range_scale_whiteness_Berger1959(self): XYZ_0 = np.array([94.80966767, 100.00000000, 107.30513595]) W = whiteness_Berger1959(XYZ, XYZ_0) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( whiteness_Berger1959(XYZ * factor, XYZ_0 * factor), W * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_whiteness_Berger1959(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_Berger1959` + Test :func:`colour.colorimetry.whiteness.whiteness_Berger1959` definition nan support. """ @@ -119,40 +132,46 @@ def test_nan_whiteness_Berger1959(self): class TestWhitenessTaube1960(unittest.TestCase): """ - Defines :func:`colour.colorimetry.whiteness.whiteness_Taube1960` + Define :func:`colour.colorimetry.whiteness.whiteness_Taube1960` definition unit tests methods. """ def test_whiteness_Taube1960(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_Taube1960` + Test :func:`colour.colorimetry.whiteness.whiteness_Taube1960` definition. """ self.assertAlmostEqual( whiteness_Taube1960( np.array([95.00000000, 100.00000000, 105.00000000]), - np.array([94.80966767, 100.00000000, 107.30513595])), + np.array([94.80966767, 100.00000000, 107.30513595]), + ), 91.407173833416152, - places=7) + places=7, + ) self.assertAlmostEqual( whiteness_Taube1960( np.array([105.00000000, 100.00000000, 95.00000000]), - np.array([94.80966767, 100.00000000, 107.30513595])), + np.array([94.80966767, 100.00000000, 107.30513595]), + ), 54.130300134995593, - places=7) + places=7, + ) self.assertAlmostEqual( whiteness_Taube1960( np.array([100.00000000, 100.00000000, 100.00000000]), - np.array([100.00000000, 100.00000000, 100.00000000])), + np.array([100.00000000, 100.00000000, 100.00000000]), + ), 100.0, - places=7) + places=7, + ) def test_n_dimensional_whiteness_Taube1960(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_Taube1960` + Test :func:`colour.colorimetry.whiteness.whiteness_Taube1960` definition n_dimensional arrays support. """ @@ -164,17 +183,19 @@ def test_n_dimensional_whiteness_Taube1960(self): XYZ_0 = np.tile(XYZ_0, (6, 1)) WI = np.tile(WI, 6) np.testing.assert_almost_equal( - whiteness_Taube1960(XYZ, XYZ_0), WI, decimal=7) + whiteness_Taube1960(XYZ, XYZ_0), WI, decimal=7 + ) XYZ = np.reshape(XYZ, (2, 3, 3)) XYZ_0 = np.reshape(XYZ_0, (2, 3, 3)) WI = np.reshape(WI, (2, 3)) np.testing.assert_almost_equal( - whiteness_Taube1960(XYZ, XYZ_0), WI, decimal=7) + whiteness_Taube1960(XYZ, XYZ_0), WI, decimal=7 + ) def test_domain_range_scale_whiteness_Taube1960(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_Taube1960` + Test :func:`colour.colorimetry.whiteness.whiteness_Taube1960` definition domain and range scale support. """ @@ -182,18 +203,19 @@ def test_domain_range_scale_whiteness_Taube1960(self): XYZ_0 = np.array([94.80966767, 100.00000000, 107.30513595]) WI = whiteness_Taube1960(XYZ, XYZ_0) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( whiteness_Taube1960(XYZ * factor, XYZ_0 * factor), WI * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_whiteness_Berger1959(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_Berger1959` + Test :func:`colour.colorimetry.whiteness.whiteness_Berger1959` definition nan support. """ @@ -207,34 +229,39 @@ def test_nan_whiteness_Berger1959(self): class TestWhitenessStensby1968(unittest.TestCase): """ - Defines :func:`colour.colorimetry.whiteness.whiteness_Stensby1968` + Define :func:`colour.colorimetry.whiteness.whiteness_Stensby1968` definition unit tests methods. """ def test_whiteness_Stensby1968(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_Stensby1968` + Test :func:`colour.colorimetry.whiteness.whiteness_Stensby1968` definition. """ self.assertAlmostEqual( whiteness_Stensby1968( - np.array([100.00000000, -2.46875131, -16.72486654])), + np.array([100.00000000, -2.46875131, -16.72486654]) + ), 142.76834569, - places=7) + places=7, + ) self.assertAlmostEqual( whiteness_Stensby1968( - np.array([100.00000000, 14.40943727, -9.61394885])), + np.array([100.00000000, 14.40943727, -9.61394885]) + ), 172.07015836, - places=7) + places=7, + ) self.assertAlmostEqual( - whiteness_Stensby1968(np.array([1, 1, 1])), 1.00000000, places=7) + whiteness_Stensby1968(np.array([1, 1, 1])), 1.00000000, places=7 + ) def test_n_dimensional_whiteness_Stensby1968(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_Stensby1968` + Test :func:`colour.colorimetry.whiteness.whiteness_Stensby1968` definition n_dimensional arrays support. """ @@ -244,34 +271,35 @@ def test_n_dimensional_whiteness_Stensby1968(self): Lab = np.tile(Lab, (6, 1)) WI = np.tile(WI, 6) np.testing.assert_almost_equal( - whiteness_Stensby1968(Lab), WI, decimal=7) + whiteness_Stensby1968(Lab), WI, decimal=7 + ) Lab = np.reshape(Lab, (2, 3, 3)) WI = np.reshape(WI, (2, 3)) np.testing.assert_almost_equal( - whiteness_Stensby1968(Lab), WI, decimal=7) + whiteness_Stensby1968(Lab), WI, decimal=7 + ) def test_domain_range_scale_whiteness_Stensby1968(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_Stensby1968` + Test :func:`colour.colorimetry.whiteness.whiteness_Stensby1968` definition domain and range scale support. """ Lab = np.array([100.00000000, -2.46875131, -16.72486654]) WI = whiteness_Stensby1968(Lab) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - whiteness_Stensby1968(Lab * factor), - WI * factor, - decimal=7) + whiteness_Stensby1968(Lab * factor), WI * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_whiteness_Stensby1968(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_Stensby1968` + Test :func:`colour.colorimetry.whiteness.whiteness_Stensby1968` definition nan support. """ @@ -284,37 +312,43 @@ def test_nan_whiteness_Stensby1968(self): class TestWhitenessASTM313(unittest.TestCase): """ - Defines :func:`colour.colorimetry.whiteness.whiteness_ASTME313` + Define :func:`colour.colorimetry.whiteness.whiteness_ASTME313` definition unit tests methods. """ def test_whiteness_ASTME313(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_ASTME313` + Test :func:`colour.colorimetry.whiteness.whiteness_ASTME313` definition. """ self.assertAlmostEqual( whiteness_ASTME313( - np.array([95.00000000, 100.00000000, 105.00000000])), + np.array([95.00000000, 100.00000000, 105.00000000]) + ), 55.740000000000009, - places=7) + places=7, + ) self.assertAlmostEqual( whiteness_ASTME313( - np.array([105.00000000, 100.00000000, 95.00000000])), + np.array([105.00000000, 100.00000000, 95.00000000]) + ), 21.860000000000014, - places=7) + places=7, + ) self.assertAlmostEqual( whiteness_ASTME313( - np.array([100.00000000, 100.00000000, 100.00000000])), + np.array([100.00000000, 100.00000000, 100.00000000]) + ), 38.800000000000011, - places=7) + places=7, + ) def test_n_dimensional_whiteness_ASTME313(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_ASTME313` + Test :func:`colour.colorimetry.whiteness.whiteness_ASTME313` definition n_dimensional arrays support. """ @@ -331,23 +365,24 @@ def test_n_dimensional_whiteness_ASTME313(self): def test_domain_range_scale_whiteness_ASTME313(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_ASTME313` + Test :func:`colour.colorimetry.whiteness.whiteness_ASTME313` definition domain and range scale support. """ XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) WI = whiteness_ASTME313(XYZ) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - whiteness_ASTME313(XYZ * factor), WI * factor, decimal=7) + whiteness_ASTME313(XYZ * factor), WI * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_whiteness_ASTME313(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_ASTME313` + Test :func:`colour.colorimetry.whiteness.whiteness_ASTME313` definition nan support. """ @@ -360,34 +395,37 @@ def test_nan_whiteness_ASTME313(self): class TestWhitenessGanz1979(unittest.TestCase): """ - Defines :func:`colour.colorimetry.whiteness.whiteness_Ganz1979` + Define :func:`colour.colorimetry.whiteness.whiteness_Ganz1979` definition unit tests methods. """ def test_whiteness_Ganz1979(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_Ganz1979` + Test :func:`colour.colorimetry.whiteness.whiteness_Ganz1979` definition. """ np.testing.assert_almost_equal( whiteness_Ganz1979(np.array([0.3139, 0.3311]), 100), np.array([99.33176520, 1.76108290]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( whiteness_Ganz1979(np.array([0.3500, 0.3334]), 100), np.array([23.38525400, -32.66182560]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( whiteness_Ganz1979(np.array([0.3334, 0.3334]), 100), np.array([54.39939920, -16.04152380]), - decimal=7) + decimal=7, + ) def test_n_dimensional_whiteness_Ganz1979(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_Ganz1979` + Test :func:`colour.colorimetry.whiteness.whiteness_Ganz1979` definition n_dimensional arrays support. """ @@ -398,21 +436,24 @@ def test_n_dimensional_whiteness_Ganz1979(self): xy = np.tile(xy, (6, 1)) WT = np.tile(WT, (6, 1)) np.testing.assert_almost_equal( - whiteness_Ganz1979(xy, Y), WT, decimal=7) + whiteness_Ganz1979(xy, Y), WT, decimal=7 + ) Y = np.tile(Y, 6) np.testing.assert_almost_equal( - whiteness_Ganz1979(xy, Y), WT, decimal=7) + whiteness_Ganz1979(xy, Y), WT, decimal=7 + ) xy = np.reshape(xy, (2, 3, 2)) Y = np.reshape(Y, (2, 3)) WT = np.reshape(WT, (2, 3, 2)) np.testing.assert_almost_equal( - whiteness_Ganz1979(xy, Y), WT, decimal=7) + whiteness_Ganz1979(xy, Y), WT, decimal=7 + ) def test_domain_range_scale_whiteness_Ganz1979(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_Ganz1979` + Test :func:`colour.colorimetry.whiteness.whiteness_Ganz1979` definition domain and range scale support. """ @@ -420,16 +461,17 @@ def test_domain_range_scale_whiteness_Ganz1979(self): Y = 100 WT = whiteness_Ganz1979(xy, Y) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - whiteness_Ganz1979(xy, Y * factor), WT * factor, decimal=7) + whiteness_Ganz1979(xy, Y * factor), WT * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_whiteness_Ganz1979(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_Ganz1979` + Test :func:`colour.colorimetry.whiteness.whiteness_Ganz1979` definition nan support. """ @@ -443,37 +485,43 @@ def test_nan_whiteness_Ganz1979(self): class TestWhitenessCIE2004(unittest.TestCase): """ - Defines :func:`colour.colorimetry.whiteness.whiteness_CIE2004` + Define :func:`colour.colorimetry.whiteness.whiteness_CIE2004` definition unit tests methods. """ def test_whiteness_CIE2004(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_CIE2004` + Test :func:`colour.colorimetry.whiteness.whiteness_CIE2004` definition. """ np.testing.assert_almost_equal( whiteness_CIE2004( - np.array([0.3139, 0.3311]), 100, np.array([0.3139, 0.3311])), + np.array([0.3139, 0.3311]), 100, np.array([0.3139, 0.3311]) + ), np.array([100.00000000, 0.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( whiteness_CIE2004( - np.array([0.3500, 0.3334]), 100, np.array([0.3139, 0.3311])), + np.array([0.3500, 0.3334]), 100, np.array([0.3139, 0.3311]) + ), np.array([67.21000000, -34.60500000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( whiteness_CIE2004( - np.array([0.3334, 0.3334]), 100, np.array([0.3139, 0.3311])), + np.array([0.3334, 0.3334]), 100, np.array([0.3139, 0.3311]) + ), np.array([80.49000000, -18.00500000]), - decimal=7) + decimal=7, + ) def test_n_dimensional_whiteness_CIE2004(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_CIE2004` + Test :func:`colour.colorimetry.whiteness.whiteness_CIE2004` definition n_dimensional arrays support. """ @@ -485,23 +533,26 @@ def test_n_dimensional_whiteness_CIE2004(self): xy = np.tile(xy, (6, 1)) WT = np.tile(WT, (6, 1)) np.testing.assert_almost_equal( - whiteness_CIE2004(xy, Y, xy_n), WT, decimal=7) + whiteness_CIE2004(xy, Y, xy_n), WT, decimal=7 + ) Y = np.tile(Y, 6) xy_n = np.tile(xy_n, (6, 1)) np.testing.assert_almost_equal( - whiteness_CIE2004(xy, Y, xy_n), WT, decimal=7) + whiteness_CIE2004(xy, Y, xy_n), WT, decimal=7 + ) xy = np.reshape(xy, (2, 3, 2)) Y = np.reshape(Y, (2, 3)) xy_n = np.reshape(xy_n, (2, 3, 2)) WT = np.reshape(WT, (2, 3, 2)) np.testing.assert_almost_equal( - whiteness_CIE2004(xy, Y, xy_n), WT, decimal=7) + whiteness_CIE2004(xy, Y, xy_n), WT, decimal=7 + ) def test_domain_range_scale_whiteness_CIE2004(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_CIE2004` + Test :func:`colour.colorimetry.whiteness.whiteness_CIE2004` definition domain and range scale support. """ @@ -510,18 +561,19 @@ def test_domain_range_scale_whiteness_CIE2004(self): xy_n = np.array([0.3139, 0.3311]) WT = whiteness_CIE2004(xy, Y, xy_n) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( whiteness_CIE2004(xy, Y * factor, xy_n), WT * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_whiteness_CIE2004(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness_CIE2004` + Test :func:`colour.colorimetry.whiteness.whiteness_CIE2004` definition nan support. """ @@ -536,32 +588,39 @@ def test_nan_whiteness_CIE2004(self): class TestWhiteness(unittest.TestCase): """ - Defines :func:`colour.colorimetry.whiteness.whiteness` definition unit + Define :func:`colour.colorimetry.whiteness.whiteness` definition unit tests methods. """ def test_domain_range_scale_whiteness(self): """ - Tests :func:`colour.colorimetry.whiteness.whiteness` definition domain + Test :func:`colour.colorimetry.whiteness.whiteness` definition domain and range scale support. """ XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) XYZ_0 = np.array([94.80966767, 100.00000000, 107.30513595]) - m = ('Berger 1959', 'Taube 1960', 'Stensby 1968', 'ASTM E313', - 'Ganz 1979', 'CIE 2004') + m = ( + "Berger 1959", + "Taube 1960", + "Stensby 1968", + "ASTM E313", + "Ganz 1979", + "CIE 2004", + ) v = [whiteness(XYZ, XYZ_0, method) for method in m] - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for method, value in zip(m, v): for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( whiteness(XYZ * factor, XYZ_0 * factor, method), value * factor, - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/colorimetry/tests/test_yellowness.py b/colour/colorimetry/tests/test_yellowness.py index 742cdec7e6..2a64bdf5ee 100644 --- a/colour/colorimetry/tests/test_yellowness.py +++ b/colour/colorimetry/tests/test_yellowness.py @@ -1,63 +1,72 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.colorimetry.yellowness` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.colorimetry.yellowness` module.""" import numpy as np import unittest from itertools import permutations -from colour.colorimetry import yellowness_ASTMD1925, yellowness_ASTME313 +from colour.colorimetry import ( + yellowness_ASTMD1925, + yellowness_ASTME313_alternative, + yellowness_ASTME313, +) +from colour.colorimetry.yellowness import YELLOWNESS_COEFFICIENTS_ASTME313 from colour.colorimetry.yellowness import yellowness from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestYellownessASTMD1925', 'TestYellownessASTM313', 'TestYellowness' + "TestYellownessASTMD1925", + "TestYellownessASTM313Alternative", + "TestYellownessASTM313", + "TestYellowness", ] class TestYellownessASTMD1925(unittest.TestCase): """ - Defines :func:`colour.colorimetry.yellowness.yellowness_ASTMD1925` + Define :func:`colour.colorimetry.yellowness.yellowness_ASTMD1925` definition unit tests methods. """ def test_yellowness_ASTMD1925(self): """ - Tests :func:`colour.colorimetry.yellowness.yellowness_ASTMD1925` + Test :func:`colour.colorimetry.yellowness.yellowness_ASTMD1925` definition. """ self.assertAlmostEqual( yellowness_ASTMD1925( - np.array([95.00000000, 100.00000000, 105.00000000])), + np.array([95.00000000, 100.00000000, 105.00000000]) + ), 10.299999999999997, - places=7) + places=7, + ) self.assertAlmostEqual( yellowness_ASTMD1925( - np.array([105.00000000, 100.00000000, 95.00000000])), + np.array([105.00000000, 100.00000000, 95.00000000]) + ), 33.700000000000003, - places=7) + places=7, + ) self.assertAlmostEqual( yellowness_ASTMD1925( - np.array([100.00000000, 100.00000000, 100.00000000])), + np.array([100.00000000, 100.00000000, 100.00000000]) + ), 22.0, - places=7) + places=7, + ) def test_n_dimensional_yellowness_ASTMD1925(self): """ - Tests :func:`colour.colorimetry.yellowness.yellowness_ASTMD1925` + Test :func:`colour.colorimetry.yellowness.yellowness_ASTMD1925` definition n_dimensional arrays support. """ @@ -67,32 +76,35 @@ def test_n_dimensional_yellowness_ASTMD1925(self): XYZ = np.tile(XYZ, (6, 1)) YI = np.tile(YI, 6) np.testing.assert_almost_equal( - yellowness_ASTMD1925(XYZ), YI, decimal=7) + yellowness_ASTMD1925(XYZ), YI, decimal=7 + ) XYZ = np.reshape(XYZ, (2, 3, 3)) YI = np.reshape(YI, (2, 3)) np.testing.assert_almost_equal( - yellowness_ASTMD1925(XYZ), YI, decimal=7) + yellowness_ASTMD1925(XYZ), YI, decimal=7 + ) def test_domain_range_scale_yellowness_ASTMD1925(self): """ - Tests :func:`colour.colorimetry.yellowness.yellowness_ASTMD1925` + Test :func:`colour.colorimetry.yellowness.yellowness_ASTMD1925` definition domain and range scale support. """ XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) YI = 10.299999999999997 - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - yellowness_ASTMD1925(XYZ * factor), YI * factor, decimal=7) + yellowness_ASTMD1925(XYZ * factor), YI * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_yellowness_ASTMD1925(self): """ - Tests :func:`colour.colorimetry.yellowness.yellowness_ASTMD1925` + Test :func:`colour.colorimetry.yellowness.yellowness_ASTMD1925` definition nan support. """ @@ -103,39 +115,145 @@ def test_nan_yellowness_ASTMD1925(self): yellowness_ASTMD1925(XYZ) +class TestYellownessASTM313Alternative(unittest.TestCase): + """ + Define :func:`colour.colorimetry.yellowness.\ +yellowness_ASTME313_alternative` definition unit tests methods. + """ + + def test_yellowness_ASTME313_alternative(self): + """ + Test :func:`colour.colorimetry.yellowness.\ +yellowness_ASTME313_alternative` definition. + """ + + self.assertAlmostEqual( + yellowness_ASTME313_alternative( + np.array([95.00000000, 100.00000000, 105.00000000]) + ), + 11.065000000000003, + places=7, + ) + + self.assertAlmostEqual( + yellowness_ASTME313_alternative( + np.array([105.00000000, 100.00000000, 95.00000000]) + ), + 19.534999999999989, + places=7, + ) + + self.assertAlmostEqual( + yellowness_ASTME313_alternative( + np.array([100.00000000, 100.00000000, 100.00000000]) + ), + 15.300000000000002, + places=7, + ) + + def test_n_dimensional_yellowness_ASTME313_alternative(self): + """ + Test :func:`colour.colorimetry.yellowness.\ +yellowness_ASTME313_alternative` definition n_dimensional arrays support. + """ + + XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) + YI = yellowness_ASTME313_alternative(XYZ) + + XYZ = np.tile(XYZ, (6, 1)) + YI = np.tile(YI, 6) + np.testing.assert_almost_equal( + yellowness_ASTME313_alternative(XYZ), YI, decimal=7 + ) + + XYZ = np.reshape(XYZ, (2, 3, 3)) + YI = np.reshape(YI, (2, 3)) + np.testing.assert_almost_equal( + yellowness_ASTME313_alternative(XYZ), YI, decimal=7 + ) + + def test_domain_range_scale_yellowness_ASTME313_alternative(self): + """ + Test :func:`colour.colorimetry.yellowness.\ +yellowness_ASTME313_alternative` definition domain and range scale support. + """ + + XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) + YI = 11.065000000000003 + + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + yellowness_ASTME313_alternative(XYZ * factor), + YI * factor, + decimal=7, + ) + + @ignore_numpy_errors + def test_nan_yellowness_ASTME313_alternative(self): + """ + Test :func:`colour.colorimetry.yellowness.\ +yellowness_ASTME313_alternative` definition nan support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + XYZ = np.array(case) + yellowness_ASTME313_alternative(XYZ) + + class TestYellownessASTM313(unittest.TestCase): """ - Defines :func:`colour.colorimetry.yellowness.yellowness_ASTME313` + Define :func:`colour.colorimetry.yellowness.yellowness_ASTME313` definition unit tests methods. """ def test_yellowness_ASTME313(self): """ - Tests :func:`colour.colorimetry.yellowness.yellowness_ASTME313` + Test :func:`colour.colorimetry.yellowness.yellowness_ASTME313` definition. """ self.assertAlmostEqual( yellowness_ASTME313( - np.array([95.00000000, 100.00000000, 105.00000000])), - 11.065000000000003, - places=7) + np.array([95.00000000, 100.00000000, 105.00000000]) + ), + 4.340000000000003, + places=7, + ) self.assertAlmostEqual( yellowness_ASTME313( - np.array([105.00000000, 100.00000000, 95.00000000])), - 19.534999999999989, - places=7) + np.array([105.00000000, 100.00000000, 95.00000000]) + ), + 28.660000000000011, + places=7, + ) self.assertAlmostEqual( yellowness_ASTME313( - np.array([100.00000000, 100.00000000, 100.00000000])), - 15.300000000000002, - places=7) + np.array([100.00000000, 100.00000000, 100.00000000]) + ), + 16.500000000000000, + places=7, + ) + + self.assertAlmostEqual( + yellowness_ASTME313( + np.array([95.00000000, 100.00000000, 105.00000000]), + YELLOWNESS_COEFFICIENTS_ASTME313[ + "CIE 1931 2 Degree Standard Observer" + ]["C"], + ), + 10.089500000000001, + places=7, + ) def test_n_dimensional_yellowness_ASTME313(self): """ - Tests :func:`colour.colorimetry.yellowness.yellowness_ASTME313` + Test :func:`colour.colorimetry.yellowness.yellowness_ASTME313` definition n_dimensional arrays support. """ @@ -152,23 +270,24 @@ def test_n_dimensional_yellowness_ASTME313(self): def test_domain_range_scale_yellowness_ASTME313(self): """ - Tests :func:`colour.colorimetry.yellowness.yellowness_ASTME313` + Test :func:`colour.colorimetry.yellowness.yellowness_ASTME313` definition domain and range scale support. """ XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) - YI = 11.065000000000003 + YI = 4.340000000000003 - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - yellowness_ASTME313(XYZ * factor), YI * factor, decimal=7) + yellowness_ASTME313(XYZ * factor), YI * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_yellowness_ASTME313(self): """ - Tests :func:`colour.colorimetry.yellowness.yellowness_ASTME313` + Test :func:`colour.colorimetry.yellowness.yellowness_ASTME313` definition nan support. """ @@ -181,30 +300,31 @@ def test_nan_yellowness_ASTME313(self): class TestYellowness(unittest.TestCase): """ - Defines :func:`colour.colorimetry.yellowness.yellowness` definition unit + Define :func:`colour.colorimetry.yellowness.yellowness` definition unit tests methods. """ def test_domain_range_scale_yellowness(self): """ - Tests :func:`colour.colorimetry.yellowness.yellowness` definition + Test :func:`colour.colorimetry.yellowness.yellowness` definition domain and range scale support. """ XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) - m = ('ASTM D1925', 'ASTM E313') + m = ("ASTM D1925", "ASTM E313") v = [yellowness(XYZ, method) for method in m] - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for method, value in zip(m, v): for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( yellowness(XYZ * factor, method), value * factor, - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/colorimetry/transformations.py b/colour/colorimetry/transformations.py index e93dc12bc4..6379eeded9 100644 --- a/colour/colorimetry/transformations.py +++ b/colour/colorimetry/transformations.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Colour Matching Functions Transformations ========================================= @@ -31,42 +30,51 @@ Formulae (pp. 138-139). Wiley. ISBN:978-0-471-39918-6 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.colorimetry import MSDS_CMFS_LMS, MSDS_CMFS_RGB, SDS_LEFS_PHOTOPIC -from colour.utilities import vector_dot, tstack - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.algebra import vector_dot +from colour.colorimetry import ( + MSDS_CMFS_LMS, + MSDS_CMFS_RGB, + SDS_LEFS_PHOTOPIC, + reshape_sd, +) +from colour.hints import FloatingOrArrayLike, NDArray +from colour.utilities import tstack + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs', - 'RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs', - 'RGB_10_degree_cmfs_to_LMS_10_degree_cmfs', - 'LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs', - 'LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs' + "RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs", + "RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs", + "RGB_10_degree_cmfs_to_LMS_10_degree_cmfs", + "LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs", + "LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs", ] -def RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(wavelength): +def RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs( + wavelength: FloatingOrArrayLike, +) -> NDArray: """ - Converts *Wright & Guild 1931 2 Degree RGB CMFs* colour matching functions + Convert *Wright & Guild 1931 2 Degree RGB CMFs* colour matching functions into the *CIE 1931 2 Degree Standard Observer* colour matching functions. Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` in nm. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE 1931 2 Degree Standard Observer* spectral tristimulus values. Notes @@ -86,31 +94,36 @@ def RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(wavelength): array([ 0.0113577..., 0.004102 , 0. ]) """ - cmfs = MSDS_CMFS_RGB['Wright & Guild 1931 2 Degree RGB CMFs'] + cmfs = MSDS_CMFS_RGB["Wright & Guild 1931 2 Degree RGB CMFs"] rgb_bar = cmfs[wavelength] rgb = rgb_bar / np.sum(rgb_bar) - M1 = np.array([ - [0.49000, 0.31000, 0.20000], - [0.17697, 0.81240, 0.01063], - [0.00000, 0.01000, 0.99000], - ]) - - M2 = np.array([ - [0.66697, 1.13240, 1.20063], - [0.66697, 1.13240, 1.20063], - [0.66697, 1.13240, 1.20063], - ]) + M1 = np.array( + [ + [0.49000, 0.31000, 0.20000], + [0.17697, 0.81240, 0.01063], + [0.00000, 0.01000, 0.99000], + ] + ) + + M2 = np.array( + [ + [0.66697, 1.13240, 1.20063], + [0.66697, 1.13240, 1.20063], + [0.66697, 1.13240, 1.20063], + ] + ) xyz = vector_dot(M1, rgb) xyz /= vector_dot(M2, rgb) x, y, z = xyz[..., 0], xyz[..., 1], xyz[..., 2] - V = SDS_LEFS_PHOTOPIC['CIE 1924 Photopic Standard Observer'].copy() - V.align(cmfs.shape) + V = reshape_sd( + SDS_LEFS_PHOTOPIC["CIE 1924 Photopic Standard Observer"], cmfs.shape + ) L = V[wavelength] x_bar = x / y * L @@ -122,20 +135,22 @@ def RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(wavelength): return xyz_bar -def RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(wavelength): +def RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs( + wavelength: FloatingOrArrayLike, +) -> NDArray: """ - Converts *Stiles & Burch 1959 10 Degree RGB CMFs* colour matching + Convert *Stiles & Burch 1959 10 Degree RGB CMFs* colour matching functions into the *CIE 1964 10 Degree Standard Observer* colour matching functions. Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` in nm. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE 1964 10 Degree Standard Observer* spectral tristimulus values. Notes @@ -155,35 +170,39 @@ def RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(wavelength): array([ 0.0096432..., 0.0037526..., -0.0000041...]) """ - cmfs = MSDS_CMFS_RGB['Stiles & Burch 1959 10 Degree RGB CMFs'] + cmfs = MSDS_CMFS_RGB["Stiles & Burch 1959 10 Degree RGB CMFs"] rgb_bar = cmfs[wavelength] - M = np.array([ - [0.341080, 0.189145, 0.387529], - [0.139058, 0.837460, 0.073316], - [0.000000, 0.039553, 2.026200], - ]) + M = np.array( + [ + [0.341080, 0.189145, 0.387529], + [0.139058, 0.837460, 0.073316], + [0.000000, 0.039553, 2.026200], + ] + ) xyz_bar = vector_dot(M, rgb_bar) return xyz_bar -def RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(wavelength): +def RGB_10_degree_cmfs_to_LMS_10_degree_cmfs( + wavelength: FloatingOrArrayLike, +) -> NDArray: """ - Converts *Stiles & Burch 1959 10 Degree RGB CMFs* colour matching + Convert *Stiles & Burch 1959 10 Degree RGB CMFs* colour matching functions into the *Stockman & Sharpe 10 Degree Cone Fundamentals* spectral sensitivity functions. Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` in nm. Returns ------- - ndarray + :class:`numpy.ndarray` *Stockman & Sharpe 10 Degree Cone Fundamentals* spectral tristimulus values. @@ -204,15 +223,17 @@ def RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(wavelength): array([ 0.0052860..., 0.0003252..., 0. ]) """ - cmfs = MSDS_CMFS_RGB['Stiles & Burch 1959 10 Degree RGB CMFs'] + cmfs = MSDS_CMFS_RGB["Stiles & Burch 1959 10 Degree RGB CMFs"] rgb_bar = cmfs[wavelength] - M = np.array([ - [0.1923252690, 0.749548882, 0.0675726702], - [0.0192290085, 0.940908496, 0.113830196], - [0.0000000000, 0.0105107859, 0.991427669], - ]) + M = np.array( + [ + [0.1923252690, 0.749548882, 0.0675726702], + [0.0192290085, 0.940908496, 0.113830196], + [0.0000000000, 0.0105107859, 0.991427669], + ] + ) lms_bar = vector_dot(M, rgb_bar) lms_bar[..., -1][np.asarray(np.asarray(wavelength) > 505)] = 0 @@ -220,20 +241,22 @@ def RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(wavelength): return lms_bar -def LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(wavelength): +def LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs( + wavelength: FloatingOrArrayLike, +) -> NDArray: """ - Converts *Stockman & Sharpe 2 Degree Cone Fundamentals* colour matching + Convert *Stockman & Sharpe 2 Degree Cone Fundamentals* colour matching functions into the *CIE 2012 2 Degree Standard Observer* colour matching functions. Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` in nm. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE 2012 2 Degree Standard Observer* spectral tristimulus values. Notes @@ -253,35 +276,39 @@ def LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(wavelength): array([ 0.0109677..., 0.0041959..., 0. ]) """ - cmfs = MSDS_CMFS_LMS['Stockman & Sharpe 2 Degree Cone Fundamentals'] + cmfs = MSDS_CMFS_LMS["Stockman & Sharpe 2 Degree Cone Fundamentals"] lms_bar = cmfs[wavelength] - M = np.array([ - [1.94735469, -1.41445123, 0.36476327], - [0.68990272, 0.34832189, 0.00000000], - [0.00000000, 0.00000000, 1.93485343], - ]) + M = np.array( + [ + [1.94735469, -1.41445123, 0.36476327], + [0.68990272, 0.34832189, 0.00000000], + [0.00000000, 0.00000000, 1.93485343], + ] + ) xyz_bar = vector_dot(M, lms_bar) return xyz_bar -def LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(wavelength): +def LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs( + wavelength: FloatingOrArrayLike, +) -> NDArray: """ - Converts *Stockman & Sharpe 10 Degree Cone Fundamentals* colour matching + Convert *Stockman & Sharpe 10 Degree Cone Fundamentals* colour matching functions into the *CIE 2012 10 Degree Standard Observer* colour matching functions. Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` in nm. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE 2012 10 Degree Standard Observer* spectral tristimulus values. Notes @@ -301,15 +328,17 @@ def LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(wavelength): array([ 0.0098162..., 0.0037761..., 0. ]) """ - cmfs = MSDS_CMFS_LMS['Stockman & Sharpe 10 Degree Cone Fundamentals'] + cmfs = MSDS_CMFS_LMS["Stockman & Sharpe 10 Degree Cone Fundamentals"] lms_bar = cmfs[wavelength] - M = np.array([ - [1.93986443, -1.34664359, 0.43044935], - [0.69283932, 0.34967567, 0.00000000], - [0.00000000, 0.00000000, 2.14687945], - ]) + M = np.array( + [ + [1.93986443, -1.34664359, 0.43044935], + [0.69283932, 0.34967567, 0.00000000], + [0.00000000, 0.00000000, 2.14687945], + ] + ) xyz_bar = vector_dot(M, lms_bar) diff --git a/colour/colorimetry/tristimulus.py b/colour/colorimetry/tristimulus_values.py similarity index 55% rename from colour/colorimetry/tristimulus.py rename to colour/colorimetry/tristimulus_values.py index 84c5ceb2dc..b49d6897d4 100644 --- a/colour/colorimetry/tristimulus.py +++ b/colour/colorimetry/tristimulus_values.py @@ -1,11 +1,11 @@ -# -*- coding: utf-8 -*- """ Tristimulus Values ================== -Defines objects for tristimulus values computation from spectral data: +Defines the objects for tristimulus values computation from spectral data: - :attr:`colour.SPECTRAL_SHAPE_ASTME308` +- :func:`colour.colorimetry.handle_spectral_arguments` - :func:`colour.colorimetry.tristimulus_weighting_factors_ASTME2022` - :func:`colour.colorimetry.sd_to_XYZ_integration` - :func:`colour.colorimetry.\ @@ -34,71 +34,191 @@ Quantitative Data and Formulae (pp. 158-163). Wiley. ISBN:978-0-471-39918-6 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.algebra import lagrange_coefficients -from colour.colorimetry import (SPECTRAL_SHAPE_DEFAULT, - MultiSpectralDistributions, SpectralShape, - MSDS_CMFS_STANDARD_OBSERVER, sd_ones) -from colour.constants import DEFAULT_INT_DTYPE -from colour.utilities import (CaseInsensitiveMapping, as_float_array, - filter_kwargs, from_range_100, - get_domain_range_scale, runtime_warning, tsplit) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.colorimetry import ( + SPECTRAL_SHAPE_DEFAULT, + MultiSpectralDistributions, + SpectralDistribution, + SpectralShape, + reshape_msds, + reshape_sd, +) +from colour.hints import ( + Any, + ArrayLike, + Boolean, + Dict, + FloatingOrNDArray, + Integer, + Literal, + NDArray, + Number, + Optional, + Tuple, + Union, + cast, +) +from colour.utilities import ( + CACHE_REGISTRY, + CaseInsensitiveMapping, + as_float_array, + as_int_scalar, + attest, + filter_kwargs, + from_range_100, + get_domain_range_scale, + optional, + runtime_warning, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'SPECTRAL_SHAPE_ASTME308', 'lagrange_coefficients_ASTME2022', - 'tristimulus_weighting_factors_ASTME2022', - 'adjust_tristimulus_weighting_factors_ASTME308', 'sd_to_XYZ_integration', - 'sd_to_XYZ_tristimulus_weighting_factors_ASTME308', 'sd_to_XYZ_ASTME308', - 'SD_TO_XYZ_METHODS', 'sd_to_XYZ', 'msds_to_XYZ_integration', - 'msds_to_XYZ_ASTME308', 'MSDS_TO_XYZ_METHODS', 'msds_to_XYZ', - 'wavelength_to_XYZ' + "SPECTRAL_SHAPE_ASTME308", + "handle_spectral_arguments", + "lagrange_coefficients_ASTME2022", + "tristimulus_weighting_factors_ASTME2022", + "adjust_tristimulus_weighting_factors_ASTME308", + "sd_to_XYZ_integration", + "sd_to_XYZ_tristimulus_weighting_factors_ASTME308", + "sd_to_XYZ_ASTME308", + "SD_TO_XYZ_METHODS", + "sd_to_XYZ", + "msds_to_XYZ_integration", + "msds_to_XYZ_ASTME308", + "MSDS_TO_XYZ_METHODS", + "msds_to_XYZ", + "wavelength_to_XYZ", ] -SPECTRAL_SHAPE_ASTME308 = SPECTRAL_SHAPE_DEFAULT +SPECTRAL_SHAPE_ASTME308: SpectralShape = SPECTRAL_SHAPE_DEFAULT SPECTRAL_SHAPE_ASTME308.__doc__ = """ Shape for *ASTM E308-15* practise: (360, 780, 1). References ---------- :cite:`ASTMInternational2015b` - -SPECTRAL_SHAPE_ASTME308 : SpectralShape """ -_CACHE_LAGRANGE_INTERPOLATING_COEFFICIENTS = None +_CACHE_LAGRANGE_INTERPOLATING_COEFFICIENTS: Dict = ( + CACHE_REGISTRY.register_cache( + f"{__name__}._CACHE_LAGRANGE_INTERPOLATING_COEFFICIENTS" + ) +) + +_CACHE_TRISTIMULUS_WEIGHTING_FACTORS: Dict = CACHE_REGISTRY.register_cache( + f"{__name__}._CACHE_TRISTIMULUS_WEIGHTING_FACTORS" +) + +_CACHE_SD_TO_XYZ: Dict = CACHE_REGISTRY.register_cache( + f"{__name__}._CACHE_SD_TO_XYZ" +) + + +def handle_spectral_arguments( + cmfs: Optional[MultiSpectralDistributions] = None, + illuminant: Optional[SpectralDistribution] = None, + cmfs_default: str = "CIE 1931 2 Degree Standard Observer", + illuminant_default: str = "D65", + shape_default: SpectralShape = SPECTRAL_SHAPE_DEFAULT, + issue_runtime_warnings: bool = True, +) -> Tuple[MultiSpectralDistributions, SpectralDistribution]: + """ + Handle the spectral arguments of various *Colour* definitions performing + spectral computations. + + - If ``cmfs`` is not given, one is chosen according to ``cmfs_default``. + The returned colour matching functions adopt the spectral shape given + by ``shape_default``. + - If ``illuminant`` is not given, one is chosen according to + ``illuminant_default``. The returned illuminant adopts the spectral + shape of the returned colour matching functions. + - If ``illuminant`` is given, the returned illuminant spectral shape is + aligned to that of the returned colour matching functions. + + Parameters + ---------- + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + illuminant + Illuminant spectral distribution, default to + *CIE Standard Illuminant D65*. + cmfs_default + The default colour matching functions to use if ``cmfs`` is not given. + illuminant_default + The default illuminant to use if ``illuminant`` is not given. + shape_default + The default spectral shape to align the final colour matching functions + and illuminant. + issue_runtime_warnings + Whether to issue the runtime warnings. + + Returns + ------- + :class:`tuple` + Colour matching functions and illuminant. + + Examples + -------- + >>> cmfs, illuminant = handle_spectral_arguments() + >>> cmfs.name, cmfs.shape, illuminant.name, illuminant.shape + ('CIE 1931 2 Degree Standard Observer', SpectralShape(360.0, 780.0, 1.0), \ +'D65', SpectralShape(360.0, 780.0, 1.0)) + >>> cmfs, illuminant = handle_spectral_arguments( + ... shape_default=SpectralShape(400, 700, 20)) + >>> cmfs.name, cmfs.shape, illuminant.name, illuminant.shape + ('CIE 1931 2 Degree Standard Observer', \ +SpectralShape(400.0, 700.0, 20.0), 'D65', SpectralShape(400.0, 700.0, 20.0)) + """ + + from colour import MSDS_CMFS, SDS_ILLUMINANTS + + cmfs = optional(cmfs, reshape_msds(MSDS_CMFS[cmfs_default], shape_default)) + illuminant = optional( + illuminant, reshape_sd(SDS_ILLUMINANTS[illuminant_default], cmfs.shape) + ) + + if illuminant.shape != cmfs.shape: + issue_runtime_warnings and runtime_warning( + f'Aligning "{illuminant.name}" illuminant shape to "{cmfs.name}" ' + f"colour matching functions shape." + ) -_CACHE_TRISTIMULUS_WEIGHTING_FACTORS = None + illuminant = reshape_sd(illuminant, cmfs.shape) -_CACHE_SD_TO_XYZ = None + return cmfs, illuminant -def lagrange_coefficients_ASTME2022(interval=10, interval_type='inner'): +def lagrange_coefficients_ASTME2022( + interval: Integer = 10, + interval_type: Union[Literal["Boundary", "Inner"], str] = "Inner", +) -> NDArray: """ - Computes the *Lagrange Coefficients* for given interval size using practise + Compute the *Lagrange Coefficients* for given interval size using practise *ASTM E2022-11* method. Parameters ---------- - interval : int + interval Interval size in nm. - interval_type : unicode, optional - **{'inner', 'boundary'}**, + interval_type If the interval is an *inner* interval *Lagrange Coefficients* are computed for degree 4. Degree 3 is used for a *boundary* interval. Returns ------- - ndarray + :class:`numpy.ndarray` *Lagrange Coefficients*. References @@ -132,28 +252,38 @@ def lagrange_coefficients_ASTME2022(interval=10, interval_type='inner'): """ global _CACHE_LAGRANGE_INTERPOLATING_COEFFICIENTS - if _CACHE_LAGRANGE_INTERPOLATING_COEFFICIENTS is None: - _CACHE_LAGRANGE_INTERPOLATING_COEFFICIENTS = {} - hash_key = tuple([hash(arg) for arg in (interval, interval_type)]) + interval_type = validate_method( + interval_type, + ["Boundary", "Inner"], + '"{0}" interval type is invalid, it must be one of {1}!', + ) + + hash_key = tuple(hash(arg) for arg in (interval, interval_type)) if hash_key in _CACHE_LAGRANGE_INTERPOLATING_COEFFICIENTS: - return _CACHE_LAGRANGE_INTERPOLATING_COEFFICIENTS[hash_key] + return np.copy(_CACHE_LAGRANGE_INTERPOLATING_COEFFICIENTS[hash_key]) r_n = np.linspace(1 / interval, 1 - (1 / interval), interval - 1) d = 3 - if interval_type.lower() == 'inner': + if interval_type == "inner": r_n += 1 d = 4 - lica = _CACHE_LAGRANGE_INTERPOLATING_COEFFICIENTS[hash_key] = ( - as_float_array([lagrange_coefficients(r, d) for r in r_n])) + lica = as_float_array([lagrange_coefficients(r, d) for r in r_n]) + + _CACHE_LAGRANGE_INTERPOLATING_COEFFICIENTS[hash_key] = np.copy(lica) return lica -def tristimulus_weighting_factors_ASTME2022(cmfs, illuminant, shape, k=None): +def tristimulus_weighting_factors_ASTME2022( + cmfs: MultiSpectralDistributions, + illuminant: SpectralDistribution, + shape: SpectralShape, + k: Optional[Number] = None, +) -> NDArray: """ - Returns a table of tristimulus weighting factors for given colour matching + Return a table of tristimulus weighting factors for given colour matching functions and illuminant using practise *ASTM E2022-11* method. The computed table of tristimulus weighting factors should be used with @@ -161,13 +291,13 @@ def tristimulus_weighting_factors_ASTME2022(cmfs, illuminant, shape, k=None): Parameters ---------- - cmfs : XYZ_ColourMatchingFunctions + cmfs Standard observer colour matching functions. - illuminant : SpectralDistribution + illuminant Illuminant spectral distribution. - shape : SpectralShape + shape Shape used to build the table, only the interval is needed. - k : numeric, optional + k Normalisation constant :math:`k`. For reflecting or transmitting object colours, :math:`k` is chosen so that :math:`Y = 100` for objects for which the spectral reflectance factor :math:`R(\\lambda)` of the object @@ -185,7 +315,7 @@ def tristimulus_weighting_factors_ASTME2022(cmfs, illuminant, shape, k=None): Returns ------- - ndarray + :class:`numpy.ndarray` Tristimulus weighting factors table. Raises @@ -210,8 +340,8 @@ def tristimulus_weighting_factors_ASTME2022(cmfs, illuminant, shape, k=None): Examples -------- - >>> from colour import (MSDS_CMFS, sd_CIE_standard_illuminant_A, - ... SpectralDistribution, SpectralShape) + >>> from colour import (MSDS_CMFS, SpectralDistribution, SpectralShape, + ... sd_CIE_standard_illuminant_A) >>> from colour.utilities import numpy_print_options >>> cmfs = MSDS_CMFS['CIE 1964 10 Degree Standard Observer'] >>> A = sd_CIE_standard_illuminant_A(cmfs.shape) @@ -246,33 +376,30 @@ def tristimulus_weighting_factors_ASTME2022(cmfs, illuminant, shape, k=None): """ if cmfs.shape.interval != 1: - raise ValueError('"{0}" shape "interval" must be 1!'.format(cmfs)) + raise ValueError(f'"{cmfs}" shape "interval" must be 1!') if illuminant.shape.interval != 1: - raise ValueError( - '"{0}" shape "interval" must be 1!'.format(illuminant)) + raise ValueError(f'"{illuminant}" shape "interval" must be 1!') global _CACHE_TRISTIMULUS_WEIGHTING_FACTORS - if _CACHE_TRISTIMULUS_WEIGHTING_FACTORS is None: - _CACHE_TRISTIMULUS_WEIGHTING_FACTORS = {} - hash_key = tuple([ - hash(arg) for arg in (cmfs, illuminant, shape, k, - get_domain_range_scale()) - ]) + hash_key = tuple( + hash(arg) + for arg in (cmfs, illuminant, shape, k, get_domain_range_scale()) + ) if hash_key in _CACHE_TRISTIMULUS_WEIGHTING_FACTORS: - return _CACHE_TRISTIMULUS_WEIGHTING_FACTORS[hash_key] + return np.copy(_CACHE_TRISTIMULUS_WEIGHTING_FACTORS[hash_key]) Y = cmfs.values S = illuminant.values - interval_i = DEFAULT_INT_DTYPE(shape.interval) + interval_i = int(shape.interval) W = S[::interval_i, np.newaxis] * Y[::interval_i, :] # First and last measurement intervals *Lagrange Coefficients*. - c_c = lagrange_coefficients_ASTME2022(interval_i, 'boundary') + c_c = lagrange_coefficients_ASTME2022(interval_i, "boundary") # Intermediate measurement intervals *Lagrange Coefficients*. - c_b = lagrange_coefficients_ASTME2022(interval_i, 'inner') + c_b = lagrange_coefficients_ASTME2022(interval_i, "inner") # Total wavelengths count. w_c = len(Y) @@ -297,8 +424,12 @@ def tristimulus_weighting_factors_ASTME2022(cmfs, illuminant, shape, k=None): # Last interval. for j in range(r_c): for k in range(i_cm, i_cm - 3, -1): - W[k, i] = (W[k, i] + c_c[r_c - j - 1, i_cm - k] * S[j + w_lif] - * Y[j + w_lif, i]) + W[k, i] = ( + W[k, i] + + c_c[r_c - j - 1, i_cm - k] + * S[j + w_lif] + * Y[j + w_lif, i] + ) # Intermediate intervals. for j in range(i_c - 3): @@ -310,20 +441,21 @@ def tristimulus_weighting_factors_ASTME2022(cmfs, illuminant, shape, k=None): W[j + 3, i] = W[j + 3, i] + c_b[k, 3] * S[w_i] * Y[w_i, i] # Extrapolation of potential incomplete interval. - for j in range( - DEFAULT_INT_DTYPE(w_c - ((w_c - 1) % interval_i)), w_c, 1): + for j in range(as_int_scalar(w_c - ((w_c - 1) % interval_i)), w_c, 1): W[i_cm, i] = W[i_cm, i] + S[j] * Y[j, i] - W *= 100 / np.sum(W, axis=0)[1] if k_n is None else k_n + W *= cast(Number, optional(k_n, 100 / np.sum(W, axis=0)[1])) - _CACHE_TRISTIMULUS_WEIGHTING_FACTORS[hash_key] = W + _CACHE_TRISTIMULUS_WEIGHTING_FACTORS[hash_key] = np.copy(W) return W -def adjust_tristimulus_weighting_factors_ASTME308(W, shape_r, shape_t): +def adjust_tristimulus_weighting_factors_ASTME308( + W: ArrayLike, shape_r: SpectralShape, shape_t: SpectralShape +) -> NDArray: """ - Adjusts given table of tristimulus weighting factors to account for a + Adjust given table of tristimulus weighting factors to account for a shorter wavelengths range of the test spectral shape compared to the reference spectral shape using practise *ASTM E308-15* method: Weights at the wavelengths for which data are not available are added to @@ -332,16 +464,16 @@ def adjust_tristimulus_weighting_factors_ASTME308(W, shape_r, shape_t): Parameters ---------- - W : array_like + W Tristimulus weighting factors table. - shape_r : SpectralShape + shape_r Reference spectral shape. - shape_t : SpectralShape + shape_t Test spectral shape. Returns ------- - ndarray + :class:`numpy.ndarray` Adjusted tristimulus weighting factors. References @@ -350,8 +482,8 @@ def adjust_tristimulus_weighting_factors_ASTME308(W, shape_r, shape_t): Examples -------- - >>> from colour import (MSDS_CMFS, sd_CIE_standard_illuminant_A, - ... SpectralDistribution, SpectralShape) + >>> from colour import (MSDS_CMFS, SpectralDistribution, SpectralShape, + ... sd_CIE_standard_illuminant_A) >>> from colour.utilities import numpy_print_options >>> cmfs = MSDS_CMFS['CIE 1964 10 Degree Standard Observer'] >>> A = sd_CIE_standard_illuminant_A(cmfs.shape) @@ -379,41 +511,47 @@ def adjust_tristimulus_weighting_factors_ASTME308(W, shape_r, shape_t): [ 0.4171109..., 0.1618194..., 0. ...]]) """ - W = np.copy(W) + W = as_float_array(W).copy() - start_index = DEFAULT_INT_DTYPE( - (shape_t.start - shape_r.start) / shape_r.interval) + start_index = int((shape_t.start - shape_r.start) / shape_r.interval) for i in range(start_index): W[start_index] += W[i] - end_index = DEFAULT_INT_DTYPE( - (shape_r.end - shape_t.end) / shape_r.interval) + end_index = int((shape_r.end - shape_t.end) / shape_r.interval) for i in range(end_index): W[-end_index - 1] += W[-i - 1] - return W[start_index:-end_index or None, ...] + return W[start_index : -end_index or None, ...] def sd_to_XYZ_integration( - sd, - cmfs=MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer'] - .copy().trim(SPECTRAL_SHAPE_DEFAULT), - illuminant=sd_ones(), - k=None): + sd: Union[ArrayLike, SpectralDistribution, MultiSpectralDistributions], + cmfs: Optional[MultiSpectralDistributions] = None, + illuminant: Optional[SpectralDistribution] = None, + k: Optional[Number] = None, + shape: Optional[SpectralShape] = None, +) -> NDArray: """ - Converts given spectral distribution to *CIE XYZ* tristimulus values + Convert given spectral distribution to *CIE XYZ* tristimulus values using given colour matching functions and illuminant according to classical integration method. + The spectral distribution can be either a + :class:`colour.SpectralDistribution` class instance or an `ArrayLike` in + which case the ``shape`` must be passed. + Parameters ---------- - sd : SpectralDistribution - Spectral distribution. - cmfs : XYZ_ColourMatchingFunctions - Standard observer colour matching functions. - illuminant : SpectralDistribution, optional - Illuminant spectral distribution. - k : numeric, optional + sd + Spectral distribution, if an `ArrayLike` the wavelengths are + expected to be in the last axis, e.g. for a spectral array with + 77 bins, ``sd`` shape could be (77, ) or (1, 77). + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + illuminant + Illuminant spectral distribution, default to *CIE Illuminant E*. + k Normalisation constant :math:`k`. For reflecting or transmitting object colours, :math:`k` is chosen so that :math:`Y = 100` for objects for which the spectral reflectance factor :math:`R(\\lambda)` of the object @@ -428,104 +566,157 @@ def sd_to_XYZ_integration( 683 :math:`lm\\cdot W^{-1}`) and :math:`\\Phi_\\lambda(\\lambda)` must be the spectral concentration of the radiometric quantity corresponding to the photometric quantity required. + shape + Spectral shape of the spectral distribution, ``cmfs`` and + ``illuminant`` will be aligned to it if ``sd`` is an `ArrayLike`. Returns ------- - ndarray, (3,) + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Notes ----- - +-----------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +===========+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +-----------+-----------------------+---------------+ + - The code path using the `ArrayLike` spectral distribution produces + results different to the code path using a + :class:`colour.SpectralDistribution` class instance: the former + favours execution speed by aligning the colour matching functions and + illuminant to the given spectral shape while the latter favours + precision by aligning the spectral distribution to the colour matching + functions. + References ---------- :cite:`Wyszecki2000bf` Examples -------- - >>> from colour import ( - ... MSDS_CMFS, SDS_ILLUMINANTS, SpectralDistribution) + >>> from colour import MSDS_CMFS, SDS_ILLUMINANTS, SpectralDistribution >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] - >>> data = { - ... 400: 0.0641, - ... 420: 0.0645, - ... 440: 0.0562, - ... 460: 0.0537, - ... 480: 0.0559, - ... 500: 0.0651, - ... 520: 0.0705, - ... 540: 0.0772, - ... 560: 0.0870, - ... 580: 0.1128, - ... 600: 0.1360, - ... 620: 0.1511, - ... 640: 0.1688, - ... 660: 0.1996, - ... 680: 0.2397, - ... 700: 0.2852 - ... } - >>> sd = SpectralDistribution(data) >>> illuminant = SDS_ILLUMINANTS['D65'] + >>> shape = SpectralShape(400, 700, 20) + >>> data = np.array([ + ... 0.0641, 0.0645, 0.0562, 0.0537, 0.0559, 0.0651, 0.0705, 0.0772, + ... 0.0870, 0.1128, 0.1360, 0.1511, 0.1688, 0.1996, 0.2397, 0.2852 + ... ]) + >>> sd = SpectralDistribution(data, shape) >>> sd_to_XYZ_integration(sd, cmfs, illuminant) ... # doctest: +ELLIPSIS array([ 10.8404805..., 9.6838697..., 6.2115722...]) + >>> sd_to_XYZ_integration(data, cmfs, illuminant, shape=shape) + ... # doctest: +ELLIPSIS + array([ 10.8993917..., 9.6986145..., 6.2540301...]) + + # The default CMFS are the "CIE 1931 2 Degree Standard Observer", and the + # default illuminant is "CIE Illuminant E": + + >>> sd_to_XYZ_integration(sd) + ... # doctest: +ELLIPSIS + array([ 11.7786939..., 9.9583972..., 5.7371816...]) """ - if illuminant.shape != cmfs.shape: - runtime_warning( - 'Aligning "{0}" illuminant shape to "{1}" colour matching ' - 'functions shape.'.format(illuminant.name, cmfs.name)) - illuminant = illuminant.copy().align(cmfs.shape) + # NOTE: The "illuminant" argument is reshaped by the + # `handle_spectral_arguments` definition, but, in this case, it is not + # desirable as we want to reshape it according to the final "shape" which + # is only available after the subsequent if/else block thus we are careful + # not unpacking over it. + if illuminant is None: + cmfs, illuminant = handle_spectral_arguments( + cmfs, illuminant, illuminant_default="E" + ) + else: + cmfs, _illuminant = handle_spectral_arguments( + cmfs, illuminant, illuminant_default="E" + ) - if sd.shape != cmfs.shape: - runtime_warning('Aligning "{0}" spectral distribution shape to "{1}" ' - 'colour matching functions shape.'.format( - sd.name, cmfs.name)) - sd = sd.copy().align(cmfs.shape) + if isinstance(sd, (SpectralDistribution, MultiSpectralDistributions)): + shape = cmfs.shape - S = illuminant.values - x_bar, y_bar, z_bar = tsplit(cmfs.values) - R = sd.values + if sd.shape != shape: + runtime_warning( + f'Aligning "{sd.name}" spectral data shape to "{shape}".' + ) + + sd = ( + reshape_sd(sd, shape) + if isinstance(sd, SpectralDistribution) + else reshape_msds(sd, shape) + ) + + R = np.transpose(sd.values) + shape_R = R.shape + wl_c_r = R.shape[-1] + else: + attest( + shape is not None, + "A spectral shape must be explicitly passed with a spectral data " + "array!", + ) - dw = cmfs.shape.interval + shape = cast(SpectralShape, shape) - k = 100 / (np.sum(y_bar * S) * dw) if k is None else k + R = as_float_array(sd) + shape_R = R.shape + wl_c_r = R.shape[-1] + wl_c = len(shape.range()) - X_p = R * x_bar * S * dw - Y_p = R * y_bar * S * dw - Z_p = R * z_bar * S * dw + attest( + wl_c_r == wl_c, + f"Spectral data array with {wl_c_r} wavelengths is not compatible " + f"with spectral shape with {wl_c} wavelengths!", + ) - XYZ = k * np.sum(np.array([X_p, Y_p, Z_p]), axis=-1) + if cmfs.shape != shape: + runtime_warning(f'Aligning "{cmfs.name}" cmfs shape to "{shape}".') + # pylint: disable=E1102 + cmfs = reshape_msds(cmfs, shape) - return from_range_100(XYZ) + if illuminant.shape != shape: + runtime_warning( + f'Aligning "{illuminant.name}" illuminant shape to "{shape}".' + ) + illuminant = reshape_sd(illuminant, shape) + + XYZ_b = cmfs.values + S = illuminant.values + R = np.reshape(R, (-1, wl_c_r)) + + d_w = cmfs.shape.interval + + k = cast(Number, optional(k, 100 / (np.sum(XYZ_b[..., 1] * S) * d_w))) + + XYZ = k * np.dot(R * S, XYZ_b) * d_w + + return from_range_100(np.reshape(XYZ, list(shape_R[:-1]) + [3])) def sd_to_XYZ_tristimulus_weighting_factors_ASTME308( - sd, - cmfs=MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer'] - .copy().trim(SPECTRAL_SHAPE_ASTME308), - illuminant=sd_ones(SPECTRAL_SHAPE_ASTME308), - k=None): + sd: SpectralDistribution, + cmfs: Optional[MultiSpectralDistributions] = None, + illuminant: Optional[SpectralDistribution] = None, + k: Optional[Number] = None, +) -> NDArray: """ - Converts given spectral distribution to *CIE XYZ* tristimulus values + Convert given spectral distribution to *CIE XYZ* tristimulus values using given colour matching functions and illuminant using a table of tristimulus weighting factors according to practise *ASTM E308-15* method. Parameters ---------- - sd : SpectralDistribution + sd Spectral distribution. - cmfs : XYZ_ColourMatchingFunctions - Standard observer colour matching functions. - illuminant : SpectralDistribution, optional - Illuminant spectral distribution. - k : numeric, optional + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + illuminant + Illuminant spectral distribution, default to *CIE Illuminant E*. + k Normalisation constant :math:`k`. For reflecting or transmitting object colours, :math:`k` is chosen so that :math:`Y = 100` for objects for which the spectral reflectance factor :math:`R(\\lambda)` of the object @@ -543,12 +734,11 @@ def sd_to_XYZ_tristimulus_weighting_factors_ASTME308( Returns ------- - ndarray, (3,) + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Notes ----- - +-----------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +===========+=======================+===============+ @@ -561,58 +751,69 @@ def sd_to_XYZ_tristimulus_weighting_factors_ASTME308( Examples -------- - >>> from colour import ( - ... MSDS_CMFS, SDS_ILLUMINANTS, SpectralDistribution) + >>> from colour import MSDS_CMFS, SDS_ILLUMINANTS, SpectralDistribution >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] - >>> data = { - ... 400: 0.0641, - ... 420: 0.0645, - ... 440: 0.0562, - ... 460: 0.0537, - ... 480: 0.0559, - ... 500: 0.0651, - ... 520: 0.0705, - ... 540: 0.0772, - ... 560: 0.0870, - ... 580: 0.1128, - ... 600: 0.1360, - ... 620: 0.1511, - ... 640: 0.1688, - ... 660: 0.1996, - ... 680: 0.2397, - ... 700: 0.2852 - ... } - >>> sd = SpectralDistribution(data) >>> illuminant = SDS_ILLUMINANTS['D65'] + >>> shape = SpectralShape(400, 700, 20) + >>> data = np.array([ + ... 0.0641, 0.0645, 0.0562, 0.0537, 0.0559, 0.0651, 0.0705, 0.0772, + ... 0.0870, 0.1128, 0.1360, 0.1511, 0.1688, 0.1996, 0.2397, 0.2852 + ... ]) + >>> sd = SpectralDistribution(data, shape) >>> sd_to_XYZ_tristimulus_weighting_factors_ASTME308( ... sd, cmfs, illuminant) # doctest: +ELLIPSIS array([ 10.8405832..., 9.6844909..., 6.2155622...]) + + # The default CMFS are the "CIE 1931 2 Degree Standard Observer", and the + # default illuminant is "CIE Illuminant E": + + >>> sd_to_XYZ_tristimulus_weighting_factors_ASTME308(sd) + ... # doctest: +ELLIPSIS + array([ 11.7786111..., 9.9589055..., 5.7403205...]) """ + cmfs, illuminant = handle_spectral_arguments( + cmfs, + illuminant, + "CIE 1931 2 Degree Standard Observer", + "E", + SPECTRAL_SHAPE_ASTME308, + ) + if cmfs.shape.interval != 1: - runtime_warning('Interpolating "{0}" cmfs to 1nm interval.'.format( - cmfs.name)) - cmfs = cmfs.copy().interpolate(SpectralShape(interval=1)) + runtime_warning(f'Interpolating "{cmfs.name}" cmfs to 1nm interval.') + # pylint: disable=E1102 + cmfs = reshape_msds( + cmfs, + SpectralShape(cmfs.shape.start, cmfs.shape.end, 1), + "Interpolate", + ) if illuminant.shape != cmfs.shape: runtime_warning( - 'Aligning "{0}" illuminant shape to "{1}" colour matching ' - 'functions shape.'.format(illuminant.name, cmfs.name)) - illuminant = illuminant.copy().align(cmfs.shape) + f'Aligning "{illuminant.name}" illuminant shape to "{cmfs.name}" ' + f"colour matching functions shape." + ) + illuminant = reshape_sd(illuminant, cmfs.shape) if sd.shape.boundaries != cmfs.shape.boundaries: - runtime_warning('Trimming "{0}" spectral distribution shape to "{1}" ' - 'colour matching functions shape.'.format( - illuminant.name, cmfs.name)) - sd = sd.copy().trim(cmfs.shape) + runtime_warning( + f'Trimming "{illuminant.name}" spectral distribution shape to ' + f'"{cmfs.name}" colour matching functions shape.' + ) + sd = reshape_sd(sd, cmfs.shape, "Trim") W = tristimulus_weighting_factors_ASTME2022( - cmfs, illuminant, - SpectralShape(cmfs.shape.start, cmfs.shape.end, sd.shape.interval), k) + cmfs, + illuminant, + SpectralShape(cmfs.shape.start, cmfs.shape.end, sd.shape.interval), + k, + ) start_w = cmfs.shape.start end_w = cmfs.shape.start + sd.shape.interval * (W.shape[0] - 1) W = adjust_tristimulus_weighting_factors_ASTME308( - W, SpectralShape(start_w, end_w, sd.shape.interval), sd.shape) + W, SpectralShape(start_w, end_w, sd.shape.interval), sd.shape + ) R = sd.values XYZ = np.sum(W * R[..., np.newaxis], axis=0) @@ -621,40 +822,41 @@ def sd_to_XYZ_tristimulus_weighting_factors_ASTME308( def sd_to_XYZ_ASTME308( - sd, - cmfs=MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer'] - .copy().trim(SPECTRAL_SHAPE_ASTME308), - illuminant=sd_ones(SPECTRAL_SHAPE_ASTME308), - use_practice_range=True, - mi_5nm_omission_method=True, - mi_20nm_interpolation_method=True, - k=None): + sd: SpectralDistribution, + cmfs: Optional[MultiSpectralDistributions] = None, + illuminant: Optional[SpectralDistribution] = None, + use_practice_range: Boolean = True, + mi_5nm_omission_method: Boolean = True, + mi_20nm_interpolation_method: Boolean = True, + k: Optional[Number] = None, +) -> NDArray: """ - Converts given spectral distribution to *CIE XYZ* tristimulus values using + Convert given spectral distribution to *CIE XYZ* tristimulus values using given colour matching functions and illuminant according to practise *ASTM E308-15* method. Parameters ---------- - sd : SpectralDistribution + sd Spectral distribution. - cmfs : XYZ_ColourMatchingFunctions - Standard observer colour matching functions. - illuminant : SpectralDistribution, optional - Illuminant spectral distribution. - use_practice_range : bool, optional + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + illuminant + Illuminant spectral distribution, default to *CIE Illuminant E*. + use_practice_range Practise *ASTM E308-15* working wavelengths range is [360, 780], if *True* this argument will trim the colour matching functions appropriately. - mi_5nm_omission_method : bool, optional + mi_5nm_omission_method 5 nm measurement intervals spectral distribution conversion to tristimulus values will use a 5 nm version of the colour matching functions instead of a table of tristimulus weighting factors. - mi_20nm_interpolation_method : bool, optional + mi_20nm_interpolation_method 20 nm measurement intervals spectral distribution conversion to tristimulus values will use a dedicated interpolation method instead of a table of tristimulus weighting factors. - k : numeric, optional + k Normalisation constant :math:`k`. For reflecting or transmitting object colours, :math:`k` is chosen so that :math:`Y = 100` for objects for which the spectral reflectance factor :math:`R(\\lambda)` of the object @@ -672,12 +874,11 @@ def sd_to_XYZ_ASTME308( Returns ------- - ndarray, (3,) + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Notes ----- - +-----------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +===========+=======================+===============+ @@ -690,76 +891,89 @@ def sd_to_XYZ_ASTME308( Examples -------- - >>> from colour import ( - ... MSDS_CMFS, SDS_ILLUMINANTS, SpectralDistribution) + >>> from colour import MSDS_CMFS, SDS_ILLUMINANTS, SpectralDistribution >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] - >>> data = { - ... 400: 0.0641, - ... 420: 0.0645, - ... 440: 0.0562, - ... 460: 0.0537, - ... 480: 0.0559, - ... 500: 0.0651, - ... 520: 0.0705, - ... 540: 0.0772, - ... 560: 0.0870, - ... 580: 0.1128, - ... 600: 0.1360, - ... 620: 0.1511, - ... 640: 0.1688, - ... 660: 0.1996, - ... 680: 0.2397, - ... 700: 0.2852 - ... } - >>> sd = SpectralDistribution(data) >>> illuminant = SDS_ILLUMINANTS['D65'] + >>> shape = SpectralShape(400, 700, 20) + >>> data = np.array([ + ... 0.0641, 0.0645, 0.0562, 0.0537, 0.0559, 0.0651, 0.0705, 0.0772, + ... 0.0870, 0.1128, 0.1360, 0.1511, 0.1688, 0.1996, 0.2397, 0.2852 + ... ]) + >>> sd = SpectralDistribution(data, shape) >>> sd_to_XYZ_ASTME308(sd, cmfs, illuminant) ... # doctest: +ELLIPSIS array([ 10.8401953..., 9.6841740..., 6.2158913...]) + + # The default CMFS are the "CIE 1931 2 Degree Standard Observer", and the + # default illuminant is "CIE Illuminant E": + + >>> sd_to_XYZ_ASTME308(sd) + ... # doctest: +ELLIPSIS + array([ 11.7781589..., 9.9585580..., 5.7408602...]) """ + cmfs, illuminant = handle_spectral_arguments( + cmfs, + illuminant, + "CIE 1931 2 Degree Standard Observer", + "E", + SPECTRAL_SHAPE_ASTME308, + ) + if sd.shape.interval not in (1, 5, 10, 20): raise ValueError( - 'Tristimulus values conversion from spectral data according to ' + "Tristimulus values conversion from spectral data according to " 'practise "ASTM E308-15" should be performed on spectral data ' - 'with measurement interval of 1, 5, 10 or 20nm!') + "with measurement interval of 1, 5, 10 or 20nm!" + ) if use_practice_range: - cmfs = cmfs.copy().trim(SPECTRAL_SHAPE_ASTME308) + # pylint: disable=E1102 + cmfs = reshape_msds(cmfs, SPECTRAL_SHAPE_ASTME308, "Trim") method = sd_to_XYZ_tristimulus_weighting_factors_ASTME308 if sd.shape.interval == 1: method = sd_to_XYZ_integration elif sd.shape.interval == 5 and mi_5nm_omission_method: if cmfs.shape.interval != 5: - cmfs = cmfs.copy().interpolate(SpectralShape(interval=5)) + # pylint: disable=E1102 + cmfs = reshape_msds( + cmfs, + SpectralShape(cmfs.shape.start, cmfs.shape.end, 5), + "Interpolate", + ) method = sd_to_XYZ_integration elif sd.shape.interval == 20 and mi_20nm_interpolation_method: - sd = sd.copy() + sd = cast(SpectralDistribution, sd.copy()) if sd.shape.boundaries != cmfs.shape.boundaries: runtime_warning( - 'Trimming "{0}" spectral distribution shape to "{1}" ' - 'colour matching functions shape.'.format( - illuminant.name, cmfs.name)) + f'Trimming "{illuminant.name}" spectral distribution shape to ' + f'"{cmfs.name}" colour matching functions shape.' + ) sd.trim(cmfs.shape) # Extrapolation of additional 20nm padding intervals. sd.align(SpectralShape(sd.shape.start - 20, sd.shape.end + 20, 10)) for i in range(2): sd[sd.wavelengths[i]] = ( - 3 * sd.values[i + 2] - - 3 * sd.values[i + 4] + sd.values[i + 6]) # yapf: disable + 3 * sd.values[i + 2] - 3 * sd.values[i + 4] + sd.values[i + 6] + ) i_e = len(sd.domain) - 1 - i sd[sd.wavelengths[i_e]] = ( - sd.values[i_e - 6] - 3 * sd.values[i_e - 4] + - 3 * sd.values[i_e - 2]) + sd.values[i_e - 6] + - 3 * sd.values[i_e - 4] + + 3 * sd.values[i_e - 2] + ) # Interpolating every odd numbered values. # TODO: Investigate code vectorisation. for i in range(3, len(sd.domain) - 3, 2): sd[sd.wavelengths[i]] = ( - -0.0625 * sd.values[i - 3] + 0.5625 * sd.values[i - 1] + - 0.5625 * sd.values[i + 1] - 0.0625 * sd.values[i + 3]) + -0.0625 * sd.values[i - 3] + + 0.5625 * sd.values[i - 1] + + 0.5625 * sd.values[i + 1] + - 0.0625 * sd.values[i + 3] + ) # Discarding the additional 20nm padding intervals. sd.trim(SpectralShape(sd.shape.start + 20, sd.shape.end - 20, 10)) @@ -769,10 +983,9 @@ def sd_to_XYZ_ASTME308( return XYZ -SD_TO_XYZ_METHODS = CaseInsensitiveMapping({ - 'ASTM E308': sd_to_XYZ_ASTME308, - 'Integration': sd_to_XYZ_integration -}) +SD_TO_XYZ_METHODS = CaseInsensitiveMapping( + {"ASTM E308": sd_to_XYZ_ASTME308, "Integration": sd_to_XYZ_integration} +) SD_TO_XYZ_METHODS.__doc__ = """ Supported spectral distribution to *CIE XYZ* tristimulus values conversion methods. @@ -782,37 +995,42 @@ def sd_to_XYZ_ASTME308( :cite:`ASTMInternational2011a`, :cite:`ASTMInternational2015b`, :cite:`Wyszecki2000bf` -SD_TO_XYZ_METHODS : CaseInsensitiveMapping - **{'ASTM E308', 'Integration'}** - Aliases: - 'astm2015': 'ASTM E308' """ -SD_TO_XYZ_METHODS['astm2015'] = SD_TO_XYZ_METHODS['ASTM E308'] +SD_TO_XYZ_METHODS["astm2015"] = SD_TO_XYZ_METHODS["ASTM E308"] def sd_to_XYZ( - sd, - cmfs=MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer'] - .copy().trim(SPECTRAL_SHAPE_DEFAULT), - illuminant=sd_ones(), - k=None, - method='ASTM E308', - **kwargs): + sd: Union[ArrayLike, SpectralDistribution, MultiSpectralDistributions], + cmfs: Optional[MultiSpectralDistributions] = None, + illuminant: Optional[SpectralDistribution] = None, + k: Optional[Number] = None, + method: Union[Literal["ASTM E308", "Integration"], str] = "ASTM E308", + **kwargs: Any, +) -> NDArray: """ - Converts given spectral distribution to *CIE XYZ* tristimulus values using + Convert given spectral distribution to *CIE XYZ* tristimulus values using given colour matching functions, illuminant and method. + If ``method`` is *Integration*, the spectral distribution can be either a + :class:`colour.SpectralDistribution` class instance or an `ArrayLike` in + which case the ``shape`` must be passed. + Parameters ---------- - sd : SpectralDistribution - Spectral distribution. - cmfs : XYZ_ColourMatchingFunctions - Standard observer colour matching functions. - illuminant : SpectralDistribution, optional - Illuminant spectral distribution. - k : numeric, optional + sd + Spectral distribution, if an `ArrayLike` and ``method`` is + *Integration* the wavelengths are expected to be in the last axis, e.g. + for a spectral array with 77 bins, ``sd`` shape could be (77, ) or + (1, 77). + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + illuminant + Illuminant spectral distribution, default to *CIE Illuminant E*. + k Normalisation constant :math:`k`. For reflecting or transmitting object colours, :math:`k` is chosen so that :math:`Y = 100` for objects for which the spectral reflectance factor :math:`R(\\lambda)` of the object @@ -827,23 +1045,25 @@ def sd_to_XYZ( 683 :math:`lm\\cdot W^{-1}`) and :math:`\\Phi_\\lambda(\\lambda)` must be the spectral concentration of the radiometric quantity corresponding to the photometric quantity required. - method : unicode, optional - **{'ASTM E308', 'Integration'}**, + method Computation method. Other Parameters ---------------- - mi_5nm_omission_method : bool, optional + mi_5nm_omission_method {:func:`colour.colorimetry.sd_to_XYZ_ASTME308`}, 5 nm measurement intervals spectral distribution conversion to tristimulus values will use a 5 nm version of the colour matching functions instead of a table of tristimulus weighting factors. - mi_20nm_interpolation_method : bool, optional + mi_20nm_interpolation_method {:func:`colour.colorimetry.sd_to_XYZ_ASTME308`}, 20 nm measurement intervals spectral distribution conversion to tristimulus values will use a dedicated interpolation method instead of a table of tristimulus weighting factors. - use_practice_range : bool, optional + shape + Spectral shape of the spectral distribution, ``cmfs`` and + ``illuminant`` will be aligned to it if ``sd`` is an `ArrayLike`. + use_practice_range {:func:`colour.colorimetry.sd_to_XYZ_ASTME308`}, Practise *ASTM E308-15* working wavelengths range is [360, 780], if *True* this argument will trim the colour matching functions @@ -851,18 +1071,25 @@ def sd_to_XYZ( Returns ------- - ndarray, (3,) + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Notes ----- - +-----------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +===========+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +-----------+-----------------------+---------------+ + - The code path using the `ArrayLike` spectral distribution produces + results different to the code path using a + :class:`colour.SpectralDistribution` class instance: the former + favours execution speed by aligning the colour matching functions and + illuminant to the given spectral shape while the latter favours + precision by aligning the spectral distribution to the colour matching + functions. + References ---------- :cite:`ASTMInternational2011a`, :cite:`ASTMInternational2015b`, @@ -870,29 +1097,17 @@ def sd_to_XYZ( Examples -------- + >>> import numpy as np >>> from colour import ( - ... MSDS_CMFS, SDS_ILLUMINANTS, SpectralDistribution) + ... MSDS_CMFS, SDS_ILLUMINANTS, SpectralDistribution, SpectralShape) >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] - >>> data = { - ... 400: 0.0641, - ... 420: 0.0645, - ... 440: 0.0562, - ... 460: 0.0537, - ... 480: 0.0559, - ... 500: 0.0651, - ... 520: 0.0705, - ... 540: 0.0772, - ... 560: 0.0870, - ... 580: 0.1128, - ... 600: 0.1360, - ... 620: 0.1511, - ... 640: 0.1688, - ... 660: 0.1996, - ... 680: 0.2397, - ... 700: 0.2852 - ... } - >>> sd = SpectralDistribution(data) >>> illuminant = SDS_ILLUMINANTS['D65'] + >>> shape = SpectralShape(400, 700, 20) + >>> data = np.array([ + ... 0.0641, 0.0645, 0.0562, 0.0537, 0.0559, 0.0651, 0.0705, 0.0772, + ... 0.0870, 0.1128, 0.1360, 0.1511, 0.1688, 0.1996, 0.2397, 0.2852 + ... ]) + >>> sd = SpectralDistribution(data, shape) >>> sd_to_XYZ(sd, cmfs, illuminant) ... # doctest: +ELLIPSIS array([ 10.8401953..., 9.6841740..., 6.2158913...]) @@ -902,52 +1117,83 @@ def sd_to_XYZ( >>> sd_to_XYZ(sd, cmfs, illuminant, method='Integration') ... # doctest: +ELLIPSIS array([ 10.8404805..., 9.6838697..., 6.2115722...]) + >>> sd_to_XYZ(data, cmfs, illuminant, method='Integration', shape=shape) + ... # doctest: +ELLIPSIS + array([ 10.8993917..., 9.6986145..., 6.2540301...]) + + # The default CMFS are the "CIE 1931 2 Degree Standard Observer", and the + # default illuminant is "CIE Illuminant E": + + >>> sd_to_XYZ(sd) + ... # doctest: +ELLIPSIS + array([ 11.7781589..., 9.9585580..., 5.7408602...]) """ + cmfs, illuminant = handle_spectral_arguments( + cmfs, illuminant, illuminant_default="E" + ) + + method = validate_method(method, SD_TO_XYZ_METHODS) + global _CACHE_SD_TO_XYZ - if _CACHE_SD_TO_XYZ is None: - _CACHE_SD_TO_XYZ = {} - hash_key = tuple([ - hash(arg) for arg in (sd, cmfs, illuminant, k, method, - tuple(kwargs.items()), get_domain_range_scale()) - ]) + hash_key = tuple( + hash(arg) + for arg in [ + sd + if isinstance( + sd, (SpectralDistribution, MultiSpectralDistributions) + ) + else sd.tobytes(), # type: ignore[union-attr] + cmfs, + illuminant, + k, + method, + tuple(kwargs.items()), + get_domain_range_scale(), + ] + ) if hash_key in _CACHE_SD_TO_XYZ: - return _CACHE_SD_TO_XYZ[hash_key] + return np.copy(_CACHE_SD_TO_XYZ[hash_key]) function = SD_TO_XYZ_METHODS[method] - XYZ = _CACHE_SD_TO_XYZ[hash_key] = function( - sd, cmfs, illuminant, k=k, **filter_kwargs(function, **kwargs)) + XYZ = function( + sd, cmfs, illuminant, k=k, **filter_kwargs(function, **kwargs) + ) + + _CACHE_SD_TO_XYZ[hash_key] = np.copy(XYZ) return XYZ def msds_to_XYZ_integration( - msds, - cmfs=MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer'] - .copy().trim(SPECTRAL_SHAPE_DEFAULT), - illuminant=sd_ones(), - k=None, - shape=SPECTRAL_SHAPE_DEFAULT): + msds: Union[ArrayLike, SpectralDistribution, MultiSpectralDistributions], + cmfs: Optional[MultiSpectralDistributions] = None, + illuminant: Optional[SpectralDistribution] = None, + k: Optional[Number] = None, + shape: Optional[SpectralShape] = None, +) -> NDArray: """ - Converts given multi-spectral distributions to *CIE XYZ* tristimulus values - using given colour matching functions and illuminant. The multi-spectral - distribution can be either a :class:`colour.MultiSpectralDistributions` - class instance or an *array_like* in which case the ``shape`` must be - passed. + Convert given multi-spectral distributions to *CIE XYZ* tristimulus values + using given colour matching functions and illuminant. + + The multi-spectral distributions can be either a + :class:`colour.MultiSpectralDistributions` class instance or an + `ArrayLike` in which case the ``shape`` must be passed. Parameters ---------- - msds : MultiSpectralDistributions or array_like - Multi-spectral distributions, if an *array_like* the wavelengths are + msds + Multi-spectral distributions, if an `ArrayLike` the wavelengths are expected to be in the last axis, e.g. for a 512x384 multi-spectral image with 77 bins, ``msds`` shape should be (384, 512, 77). - cmfs : XYZ_ColourMatchingFunctions - Standard observer colour matching functions. - illuminant : SpectralDistribution, optional - Illuminant spectral distribution. - k : numeric, optional + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + illuminant + Illuminant spectral distribution, default to *CIE Illuminant E*. + k Normalisation constant :math:`k`. For reflecting or transmitting object colours, :math:`k` is chosen so that :math:`Y = 100` for objects for which the spectral reflectance factor :math:`R(\\lambda)` of the object @@ -962,26 +1208,25 @@ class instance or an *array_like* in which case the ``shape`` must be 683 :math:`lm\\cdot W^{-1}`) and :math:`\\Phi_\\lambda(\\lambda)` must be the spectral concentration of the radiometric quantity corresponding to the photometric quantity required. - shape : SpectralShape, optional + shape Spectral shape of the multi-spectral distributions, ``cmfs`` and - ``illuminant`` will be aligned to it. + ``illuminant`` will be aligned to it if ``msds`` is an `ArrayLike`. Returns ------- - array_like + :class:`numpy.ndarray` *CIE XYZ* tristimulus values, for a 512x384 multi-spectral image with 77 bins, the output shape will be (384, 512, 3). Notes ----- - +-----------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +===========+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +-----------+-----------------------+---------------+ - - The code path using the *array_like* multi-spectral distributions + - The code path using the `ArrayLike` multi-spectral distributions produces results different to the code path using a :class:`colour.MultiSpectralDistributions` class instance: the former favours execution speed by aligning the colour matching functions and @@ -995,9 +1240,10 @@ class instance or an *array_like* in which case the ``shape`` must be Examples -------- - >>> from colour import SDS_ILLUMINANTS + >>> from colour import MSDS_CMFS, SDS_ILLUMINANTS + >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] + >>> illuminant = SDS_ILLUMINANTS['D65'] >>> shape = SpectralShape(400, 700, 60) - >>> D65 = SDS_ILLUMINANTS['D65'] >>> data = np.array([ ... [0.0137, 0.0159, 0.0096, 0.0111, 0.0179, 0.1057, 0.0433, ... 0.0258, 0.0248, 0.0186, 0.0310, 0.0473], @@ -1012,130 +1258,96 @@ class instance or an *array_like* in which case the ``shape`` must be ... [0.0430, 0.0437, 0.3744, 0.0020, 0.5819, 0.0027, 0.0823, ... 0.0081, 0.3625, 0.3213, 0.7849, 0.0024], ... ]) - >>> msds = MultiSpectralDistributions(data, shape.range()) - >>> msds_to_XYZ_integration(msds, illuminant=D65, shape=shape) + >>> msds = MultiSpectralDistributions(data, shape) + >>> msds_to_XYZ_integration(msds, cmfs, illuminant) ... # doctest: +ELLIPSIS - array([[ 7.5029651..., 3.9487840..., 8.4034770...], - [ 26.925986 ..., 15.0724738..., 28.7058153...], - [ 16.7031140..., 28.2172235..., 25.6456293...], - [ 11.5767146..., 8.6401095..., 6.5768486...], - [ 18.7313077..., 35.0750086..., 30.1457629...], - [ 45.1657291..., 39.6137391..., 43.6784025...], - [ 8.1755520..., 13.0934236..., 25.9421257...], - [ 22.4676530..., 19.3099303..., 7.9637645...], - [ 6.5780111..., 2.5254943..., 11.0930902...], - [ 43.9146821..., 27.9803874..., 11.7292796...], - [ 8.5363407..., 19.7029458..., 17.7051147...], - [ 23.9088530..., 26.2129842..., 30.6763518...]]) - >>> msds = np.array([ - ... [ - ... [0.0137, 0.0913, 0.0152, 0.0281, 0.1918, 0.0430], - ... [0.0159, 0.3145, 0.0842, 0.0907, 0.7103, 0.0437], - ... [0.0096, 0.2582, 0.4139, 0.2228, 0.0041, 0.3744], - ... [0.0111, 0.0709, 0.0220, 0.1249, 0.1817, 0.0020], - ... [0.0179, 0.2971, 0.5630, 0.2375, 0.0024, 0.5819], - ... [0.1057, 0.4620, 0.1918, 0.5625, 0.4209, 0.0027], - ... ], - ... [ - ... [0.0433, 0.2683, 0.2373, 0.0518, 0.0118, 0.0823], - ... [0.0258, 0.0831, 0.0430, 0.3230, 0.2302, 0.0081], - ... [0.0248, 0.1203, 0.0054, 0.0065, 0.1860, 0.3625], - ... [0.0186, 0.1292, 0.0079, 0.4006, 0.9404, 0.3213], - ... [0.0310, 0.1682, 0.3719, 0.0861, 0.0041, 0.7849], - ... [0.0473, 0.3221, 0.2268, 0.3161, 0.1124, 0.0024], - ... ], - ... ]) - >>> msds_to_XYZ_integration(msds, illuminant=D65, shape=shape) + array([[ 7.5029704..., 3.9487844..., 8.4034669...], + [ 26.9259681..., 15.0724609..., 28.7057807...], + [ 16.7032188..., 28.2172346..., 25.6455984...], + [ 11.5767013..., 8.6400993..., 6.5768406...], + [ 18.7314793..., 35.0750364..., 30.1457266...], + [ 45.1656756..., 39.6136917..., 43.6783499...], + [ 8.1755696..., 13.0934177..., 25.9420944...], + [ 22.4676286..., 19.3099080..., 7.9637549...], + [ 6.5781241..., 2.5255349..., 11.0930768...], + [ 43.9147364..., 27.9803924..., 11.7292655...], + [ 8.5365923..., 19.7030166..., 17.7050933...], + [ 23.9088250..., 26.2129529..., 30.6763148...]]) + >>> data = np.reshape(data, (2, 6, 6)) + >>> msds_to_XYZ_integration(data, cmfs, illuminant, shape=shape) ... # doctest: +ELLIPSIS - array([[[ 7.1958378..., 3.8605390..., 10.1016398...], - [ 25.5738615..., 14.7200581..., 34.8440007...], - [ 17.5854414..., 28.5668344..., 30.1806687...], - [ 11.3271912..., 8.4598177..., 7.9015758...], - [ 19.6581831..., 35.5918480..., 35.1430220...], - [ 45.8212491..., 39.2600939..., 51.7907710...]], + array([[[ 1.3104332..., 1.1377026..., 1.8267926...], + [ 2.1875548..., 2.2510619..., 3.0721540...], + [ 16.8714661..., 17.7063715..., 35.8709902...], + [ 12.1648722..., 12.7222194..., 10.4880888...], + [ 16.0419431..., 23.0985768..., 11.1479902...], + [ 9.2391014..., 3.8301575..., 5.4703803...]], - [[ 8.8287837..., 13.3870357..., 30.5702050...], - [ 22.3324362..., 18.9560919..., 9.3952305...], - [ 6.6887212..., 2.5728891..., 13.2618778...], - [ 41.8166227..., 27.1191979..., 14.2627944...], - [ 9.2414098..., 20.2056200..., 20.1992502...], - [ 24.7830551..., 26.2221584..., 36.4430633...]]]) - """ - - if isinstance(msds, MultiSpectralDistributions): - return as_float_array([ - sd_to_XYZ_integration(sd, cmfs, illuminant, k) - for sd in msds.to_sds() - ]) - else: - msds = as_float_array(msds) - - msd_shape_m_1, shape_wl_count = msds.shape[-1], len(shape.range()) - assert msd_shape_m_1 == shape_wl_count, ( - 'Multi-spectral distributions array with {0} wavelengths ' - 'is not compatible with spectral shape with {1} wavelengths!'. - format(msd_shape_m_1, shape_wl_count)) - - if cmfs.shape != shape: - runtime_warning('Aligning "{0}" cmfs shape to "{1}".'.format( - cmfs.name, shape)) - cmfs = cmfs.copy().align(shape) + [[ 13.8734231..., 17.3942194..., 11.0364103...], + [ 27.7096381..., 20.8626722..., 35.5581690...], + [ 22.7886687..., 11.4769218..., 78.3300659...], + [ 51.1284864..., 52.2463568..., 26.1483754...], + [ 14.4749229..., 20.5011495..., 6.6228107...], + [ 33.6001365..., 36.3242617..., 2.8254217...]]]) - if illuminant.shape != shape: - runtime_warning('Aligning "{0}" illuminant shape to "{1}".'.format( - illuminant.name, shape)) - illuminant = illuminant.copy().align(shape) + # The default CMFS are the "CIE 1931 2 Degree Standard Observer", and the + # default illuminant is "CIE Illuminant E": - S = illuminant.values - x_bar, y_bar, z_bar = tsplit(cmfs.values) - dw = cmfs.shape.interval - - k = 100 / (np.sum(y_bar * S) * dw) if k is None else k - - X_p = msds * x_bar * S * dw - Y_p = msds * y_bar * S * dw - Z_p = msds * z_bar * S * dw - - XYZ = k * np.sum(np.array([X_p, Y_p, Z_p]), axis=-1) + >>> msds_to_XYZ_integration(msds) + ... # doctest: +ELLIPSIS + array([[ 8.2415862..., 4.2543993..., 7.6100842...], + [ 29.6144619..., 16.1158465..., 25.9015472...], + [ 16.6799560..., 27.2350547..., 22.9413337...], + [ 12.5597688..., 9.0667136..., 5.9670327...], + [ 18.5804689..., 33.6618109..., 26.9249733...], + [ 47.7113308..., 40.4573249..., 39.6439145...], + [ 7.830207 ..., 12.3689624..., 23.3742655...], + [ 24.1695370..., 20.0629815..., 7.2718670...], + [ 7.2333751..., 2.7982097..., 10.0688374...], + [ 48.7358074..., 30.2417164..., 10.6753233...], + [ 8.3231013..., 18.6791507..., 15.8228184...], + [ 24.6452277..., 26.0809382..., 27.7106399...]]) + """ - return from_range_100(np.rollaxis(XYZ, 0, msds.ndim)) + return sd_to_XYZ_integration(msds, cmfs, illuminant, k, shape) def msds_to_XYZ_ASTME308( - msds, - cmfs=MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer'] - .copy().trim(SPECTRAL_SHAPE_ASTME308), - illuminant=sd_ones(SPECTRAL_SHAPE_ASTME308), - use_practice_range=True, - mi_5nm_omission_method=True, - mi_20nm_interpolation_method=True, - k=None): + msds: MultiSpectralDistributions, + cmfs: Optional[MultiSpectralDistributions] = None, + illuminant: Optional[SpectralDistribution] = None, + use_practice_range: Boolean = True, + mi_5nm_omission_method: Boolean = True, + mi_20nm_interpolation_method: Boolean = True, + k: Optional[Number] = None, +) -> NDArray: """ - Converts given multi-spectral distributions to *CIE XYZ* tristimulus values + Convert given multi-spectral distributions to *CIE XYZ* tristimulus values using given colour matching functions and illuminant according to practise *ASTM E308-15* method. Parameters ---------- - msds : MultiSpectralDistributions or array_like + msds Multi-spectral distributions. - cmfs : XYZ_ColourMatchingFunctions - Standard observer colour matching functions. - illuminant : SpectralDistribution, optional - Illuminant spectral distribution. - use_practice_range : bool, optional + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + illuminant + Illuminant spectral distribution, default to *CIE Illuminant E*. + use_practice_range Practise *ASTM E308-15* working wavelengths range is [360, 780], if *True* this argument will trim the colour matching functions appropriately. - mi_5nm_omission_method : bool, optional + mi_5nm_omission_method 5 nm measurement intervals multi-spectral distributions conversion to tristimulus values will use a 5 nm version of the colour matching functions instead of a table of tristimulus weighting factors. - mi_20nm_interpolation_method : bool, optional + mi_20nm_interpolation_method 20 nm measurement intervals multi-spectral distributions conversion to tristimulus values will use a dedicated interpolation method instead of a table of tristimulus weighting factors. - k : numeric, optional + k Normalisation constant :math:`k`. For reflecting or transmitting object colours, :math:`k` is chosen so that :math:`Y = 100` for objects for which the spectral reflectance factor :math:`R(\\lambda)` of the object @@ -1150,25 +1362,21 @@ def msds_to_XYZ_ASTME308( 683 :math:`lm\\cdot W^{-1}`) and :math:`\\Phi_\\lambda(\\lambda)` must be the spectral concentration of the radiometric quantity corresponding to the photometric quantity required. - shape : SpectralShape, optional - Spectral shape of the multi-spectral distributions, ``cmfs`` and - ``illuminant`` will be aligned to it. Returns ------- - array_like + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Notes ----- - +-----------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +===========+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +-----------+-----------------------+---------------+ - - The code path using the *array_like* multi-spectral distributions + - The code path using the `ArrayLike` multi-spectral distributions produces results different to the code path using a :class:`colour.MultiSpectralDistributions` class instance: the former favours execution speed by aligning the colour matching functions and @@ -1182,9 +1390,10 @@ def msds_to_XYZ_ASTME308( Examples -------- - >>> from colour import SDS_ILLUMINANTS + >>> from colour import MSDS_CMFS, SDS_ILLUMINANTS + >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] + >>> illuminant = SDS_ILLUMINANTS['D65'] >>> shape = SpectralShape(400, 700, 60) - >>> D65 = SDS_ILLUMINANTS['D65'] >>> data = np.array([ ... [0.0137, 0.0159, 0.0096, 0.0111, 0.0179, 0.1057, 0.0433, ... 0.0258, 0.0248, 0.0186, 0.0310, 0.0473], @@ -1199,9 +1408,9 @@ def msds_to_XYZ_ASTME308( ... [0.0430, 0.0437, 0.3744, 0.0020, 0.5819, 0.0027, 0.0823, ... 0.0081, 0.3625, 0.3213, 0.7849, 0.0024], ... ]) - >>> msds = MultiSpectralDistributions(data, shape.range()) + >>> msds = MultiSpectralDistributions(data, shape) >>> msds = msds.align(SpectralShape(400, 700, 20)) - >>> msds_to_XYZ_ASTME308(msds, illuminant=D65) + >>> msds_to_XYZ_ASTME308(msds, cmfs, illuminant) ... # doctest: +ELLIPSIS array([[ 7.5052758..., 3.9557516..., 8.38929 ...], [ 26.9408494..., 15.0987746..., 28.6631260...], @@ -1215,24 +1424,59 @@ def msds_to_XYZ_ASTME308( [ 43.9113380..., 28.0003541..., 11.6852531...], [ 8.5496209..., 19.6913570..., 17.7400079...], [ 23.8866733..., 26.2147704..., 30.6297684...]]) + + # The default CMFS are the "CIE 1931 2 Degree Standard Observer", and the + # default illuminant is "CIE Illuminant E": + + >>> msds_to_XYZ_ASTME308(msds) + ... # doctest: +ELLIPSIS + array([[ 8.2439318..., 4.2617641..., 7.5977409...], + [ 29.6290771..., 16.1443076..., 25.8640484...], + [ 16.6819067..., 27.2271403..., 22.9490590...], + [ 12.5543694..., 9.0705685..., 5.9516323...], + [ 18.5921357..., 33.6508573..., 26.9511144...], + [ 47.6698072..., 40.4630866..., 39.5612904...], + [ 7.8336896..., 12.3711768..., 23.3654245...], + [ 24.1486630..., 20.0621956..., 7.2438655...], + [ 7.2323703..., 2.8033217..., 10.0510790...], + [ 48.7322793..., 30.2614779..., 10.6377135...], + [ 8.3365770..., 18.6690888..., 15.8517212...], + [ 24.6240657..., 26.0805317..., 27.6706915...]]) """ + cmfs, illuminant = handle_spectral_arguments( + cmfs, + illuminant, + "CIE 1931 2 Degree Standard Observer", + "E", + SPECTRAL_SHAPE_ASTME308, + ) + if isinstance(msds, MultiSpectralDistributions): - return as_float_array([ - sd_to_XYZ_ASTME308(sd, cmfs, illuminant, use_practice_range, - mi_5nm_omission_method, - mi_20nm_interpolation_method, k) - for sd in msds.to_sds() - ]) + return as_float_array( + [ + sd_to_XYZ_ASTME308( + sd, + cmfs, + illuminant, + use_practice_range, + mi_5nm_omission_method, + mi_20nm_interpolation_method, + k, + ) + for sd in msds.to_sds() + ] + ) else: - raise ValueError('"ASTM E308-15" method does not support "array_like" ' - 'multi-spectral distributions!') + raise ValueError( + '"ASTM E308-15" method does not support "ArrayLike" ' + "multi-spectral distributions!" + ) -MSDS_TO_XYZ_METHODS = CaseInsensitiveMapping({ - 'ASTM E308': msds_to_XYZ_ASTME308, - 'Integration': msds_to_XYZ_integration -}) +MSDS_TO_XYZ_METHODS = CaseInsensitiveMapping( + {"ASTM E308": msds_to_XYZ_ASTME308, "Integration": msds_to_XYZ_integration} +) MSDS_TO_XYZ_METHODS.__doc__ = """ Supported multi-spectral array to *CIE XYZ* tristimulus values conversion methods. @@ -1242,42 +1486,40 @@ def msds_to_XYZ_ASTME308( :cite:`ASTMInternational2011a`, :cite:`ASTMInternational2015b`, :cite:`Wyszecki2000bf` -MSDS_TO_XYZ_METHODS : CaseInsensitiveMapping - **{'ASTM E308', 'Integration'}** - Aliases: - 'astm2015': 'ASTM E308' """ -MSDS_TO_XYZ_METHODS['astm2015'] = MSDS_TO_XYZ_METHODS['ASTM E308'] +MSDS_TO_XYZ_METHODS["astm2015"] = MSDS_TO_XYZ_METHODS["ASTM E308"] def msds_to_XYZ( - msds, - cmfs=MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer'] - .copy().trim(SPECTRAL_SHAPE_DEFAULT), - illuminant=sd_ones(), - k=None, - method='ASTM E308', - **kwargs): + msds: Union[ArrayLike, SpectralDistribution, MultiSpectralDistributions], + cmfs: Optional[MultiSpectralDistributions] = None, + illuminant: Optional[SpectralDistribution] = None, + k: Optional[Number] = None, + method: Union[Literal["ASTM E308", "Integration"], str] = "ASTM E308", + **kwargs: Any, +) -> NDArray: """ - Converts given multi-spectral distributions to *CIE XYZ* tristimulus values + Convert given multi-spectral distributions to *CIE XYZ* tristimulus values using given colour matching functions and illuminant. For the *Integration* method, the multi-spectral distributions can be either a :class:`colour.MultiSpectralDistributions` class instance or an - *array_like* in which case the ``shape`` must be passed. + `ArrayLike` in which case the ``shape`` must be passed. Parameters ---------- - msds : MultiSpectralDistributions or array_like - Multi-spectral distributions, if an *array_like* the wavelengths are + msds + Multi-spectral distributions, if an `ArrayLike` the wavelengths are expected to be in the last axis, e.g. for a 512x384 multi-spectral image with 77 bins, ``msds`` shape should be (384, 512, 77). - cmfs : XYZ_ColourMatchingFunctions - Standard observer colour matching functions. - illuminant : SpectralDistribution, optional - Illuminant spectral distribution. - k : numeric, optional + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + illuminant + Illuminant spectral distribution, default to *CIE Illuminant E*. + k Normalisation constant :math:`k`. For reflecting or transmitting object colours, :math:`k` is chosen so that :math:`Y = 100` for objects for which the spectral reflectance factor :math:`R(\\lambda)` of the object @@ -1292,48 +1534,46 @@ def msds_to_XYZ( 683 :math:`lm\\cdot W^{-1}`) and :math:`\\Phi_\\lambda(\\lambda)` must be the spectral concentration of the radiometric quantity corresponding to the photometric quantity required. - method : unicode, optional - **{'ASTM E308', 'Integration'}**, + method Computation method. Other Parameters ---------------- - use_practice_range : bool, optional - {:func:`colour.colorimetry.msds_to_XYZ_ASTME308`}, - Practise *ASTM E308-15* working wavelengths range is [360, 780], - if *True* this argument will trim the colour matching functions - appropriately. - mi_5nm_omission_method : bool, optional + mi_5nm_omission_method {:func:`colour.colorimetry.msds_to_XYZ_ASTME308`}, 5 nm measurement intervals multi-spectral distributions conversion to tristimulus values will use a 5 nm version of the colour matching functions instead of a table of tristimulus weighting factors. - mi_20nm_interpolation_method : bool, optional + mi_20nm_interpolation_method {:func:`colour.colorimetry.msds_to_XYZ_ASTME308`}, 20 nm measurement intervals multi-spectral distributions conversion to tristimulus values will use a dedicated interpolation method instead of a table of tristimulus weighting factors. - shape : SpectralShape, optional + shape {:func:`colour.colorimetry.msds_to_XYZ_integration`}, Spectral shape of the multi-spectral distributions array :math:`msds`, ``cmfs`` and ``illuminant`` will be aligned to it. + use_practice_range + {:func:`colour.colorimetry.msds_to_XYZ_ASTME308`}, + Practise *ASTM E308-15* working wavelengths range is [360, 780], + if *True* this argument will trim the colour matching functions + appropriately. Returns ------- - array_like + :class:`numpy.ndarray` *CIE XYZ* tristimulus values, for a 512x384 multi-spectral image with 77 wavelengths, the output shape will be (384, 512, 3). Notes ----- - +-----------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +===========+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +-----------+-----------------------+---------------+ - - The code path using the *array_like* multi-spectral distributions + - The code path using the `ArrayLike` multi-spectral distributions produces results different to the code path using a :class:`colour.MultiSpectralDistributions` class instance: the former favours execution speed by aligning the colour matching functions and @@ -1348,6 +1588,9 @@ def msds_to_XYZ( Examples -------- + >>> from colour import MSDS_CMFS, SDS_ILLUMINANTS, SpectralDistribution + >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] + >>> illuminant = SDS_ILLUMINANTS['D65'] >>> shape = SpectralShape(400, 700, 60) >>> data = np.array([ ... [0.0137, 0.0159, 0.0096, 0.0111, 0.0179, 0.1057, 0.0433, @@ -1363,8 +1606,42 @@ def msds_to_XYZ( ... [0.0430, 0.0437, 0.3744, 0.0020, 0.5819, 0.0027, 0.0823, ... 0.0081, 0.3625, 0.3213, 0.7849, 0.0024], ... ]) - >>> msds = MultiSpectralDistributions(data, shape.range()) - >>> msds_to_XYZ(msds, method='Integration', shape=shape) + >>> msds = MultiSpectralDistributions(data, shape) + >>> msds_to_XYZ(msds, cmfs, illuminant, method='Integration') + ... # doctest: +ELLIPSIS + array([[ 7.5029704..., 3.9487844..., 8.4034669...], + [ 26.9259681..., 15.0724609..., 28.7057807...], + [ 16.7032188..., 28.2172346..., 25.6455984...], + [ 11.5767013..., 8.6400993..., 6.5768406...], + [ 18.7314793..., 35.0750364..., 30.1457266...], + [ 45.1656756..., 39.6136917..., 43.6783499...], + [ 8.1755696..., 13.0934177..., 25.9420944...], + [ 22.4676286..., 19.3099080..., 7.9637549...], + [ 6.5781241..., 2.5255349..., 11.0930768...], + [ 43.9147364..., 27.9803924..., 11.7292655...], + [ 8.5365923..., 19.7030166..., 17.7050933...], + [ 23.9088250..., 26.2129529..., 30.6763148...]]) + >>> data = np.reshape(data, (2, 6, 6)) + >>> msds_to_XYZ(data, cmfs, illuminant, method='Integration', shape=shape) + ... # doctest: +ELLIPSIS + array([[[ 1.3104332..., 1.1377026..., 1.8267926...], + [ 2.1875548..., 2.2510619..., 3.0721540...], + [ 16.8714661..., 17.7063715..., 35.8709902...], + [ 12.1648722..., 12.7222194..., 10.4880888...], + [ 16.0419431..., 23.0985768..., 11.1479902...], + [ 9.2391014..., 3.8301575..., 5.4703803...]], + + [[ 13.8734231..., 17.3942194..., 11.0364103...], + [ 27.7096381..., 20.8626722..., 35.5581690...], + [ 22.7886687..., 11.4769218..., 78.3300659...], + [ 51.1284864..., 52.2463568..., 26.1483754...], + [ 14.4749229..., 20.5011495..., 6.6228107...], + [ 33.6001365..., 36.3242617..., 2.8254217...]]]) + + # The default CMFS are the "CIE 1931 2 Degree Standard Observer", and the + # default illuminant is "CIE Illuminant E": + + >>> msds_to_XYZ(msds, method='Integration') ... # doctest: +ELLIPSIS array([[ 8.2415862..., 4.2543993..., 7.6100842...], [ 29.6144619..., 16.1158465..., 25.9015472...], @@ -1378,52 +1655,23 @@ def msds_to_XYZ( [ 48.7358074..., 30.2417164..., 10.6753233...], [ 8.3231013..., 18.6791507..., 15.8228184...], [ 24.6452277..., 26.0809382..., 27.7106399...]]) - >>> msds = np.array([ - ... [ - ... [0.0137, 0.0913, 0.0152, 0.0281, 0.1918, 0.0430], - ... [0.0159, 0.3145, 0.0842, 0.0907, 0.7103, 0.0437], - ... [0.0096, 0.2582, 0.4139, 0.2228, 0.0041, 0.3744], - ... [0.0111, 0.0709, 0.0220, 0.1249, 0.1817, 0.0020], - ... [0.0179, 0.2971, 0.5630, 0.2375, 0.0024, 0.5819], - ... [0.1057, 0.4620, 0.1918, 0.5625, 0.4209, 0.0027], - ... ], - ... [ - ... [0.0433, 0.2683, 0.2373, 0.0518, 0.0118, 0.0823], - ... [0.0258, 0.0831, 0.0430, 0.3230, 0.2302, 0.0081], - ... [0.0248, 0.1203, 0.0054, 0.0065, 0.1860, 0.3625], - ... [0.0186, 0.1292, 0.0079, 0.4006, 0.9404, 0.3213], - ... [0.0310, 0.1682, 0.3719, 0.0861, 0.0041, 0.7849], - ... [0.0473, 0.3221, 0.2268, 0.3161, 0.1124, 0.0024], - ... ], - ... ]) - >>> msds_to_XYZ(msds, method='Integration', shape=shape) - ... # doctest: +ELLIPSIS - array([[[ 7.6862675..., 4.0925470..., 8.4950412...], - [ 27.4119366..., 15.5014764..., 29.2825122...], - [ 17.1283666..., 27.7798651..., 25.5232032...], - [ 11.9824544..., 8.8127109..., 6.6518695...], - [ 19.1030682..., 34.4597818..., 29.7653804...], - [ 46.8243374..., 39.9551652..., 43.6541858...]], - - [[ 8.0978189..., 12.7544378..., 25.8004512...], - [ 23.4360673..., 19.6127966..., 7.9342408...], - [ 7.0933208..., 2.7894394..., 11.1527704...], - [ 45.6313772..., 29.0068105..., 11.9934522...], - [ 8.9327884..., 19.4008147..., 17.1534186...], - [ 24.6610235..., 26.1093760..., 30.7298791...]]]) """ + method = validate_method(method, MSDS_TO_XYZ_METHODS) + function = MSDS_TO_XYZ_METHODS[method] - return function(msds, cmfs, illuminant, k, - **filter_kwargs(function, **kwargs)) + return function( + msds, cmfs, illuminant, k, **filter_kwargs(function, **kwargs) + ) -def wavelength_to_XYZ(wavelength, - cmfs=MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer']): +def wavelength_to_XYZ( + wavelength: FloatingOrNDArray, + cmfs: Optional[MultiSpectralDistributions] = None, +) -> NDArray: """ - Converts given wavelength :math:`\\lambda` to *CIE XYZ* tristimulus values + Convert given wavelength :math:`\\lambda` to *CIE XYZ* tristimulus values using given colour matching functions. If the wavelength :math:`\\lambda` is not available in the colour matching @@ -1434,14 +1682,15 @@ def wavelength_to_XYZ(wavelength, Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` in nm. - cmfs : XYZ_ColourMatchingFunctions, optional - Standard observer colour matching functions. + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Raises @@ -1452,7 +1701,6 @@ def wavelength_to_XYZ(wavelength, Notes ----- - +-----------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +===========+=======================+===============+ @@ -1469,14 +1717,17 @@ def wavelength_to_XYZ(wavelength, array([ 0.0914287..., 0.1418350..., 0.7915726...]) """ - cmfs_shape = cmfs.shape - if (np.min(wavelength) < cmfs_shape.start or - np.max(wavelength) > cmfs_shape.end): + cmfs, _illuminant = handle_spectral_arguments(cmfs) + + shape = cmfs.shape + if np.min(wavelength) < shape.start or np.max(wavelength) > shape.end: raise ValueError( - '"{0}nm" wavelength is not in "[{1}, {2}]" domain!'.format( - wavelength, cmfs_shape.start, cmfs_shape.end)) + f'"{wavelength}nm" wavelength is not in ' + f'"[{shape.start}, {shape.end}]" domain!' + ) - XYZ = np.reshape(cmfs[np.ravel(wavelength)], - as_float_array(wavelength).shape + (3, )) + XYZ = np.reshape( + cmfs[np.ravel(wavelength)], as_float_array(wavelength).shape + (3,) + ) return XYZ diff --git a/colour/colorimetry/uniformity.py b/colour/colorimetry/uniformity.py new file mode 100644 index 0000000000..5cae05f44e --- /dev/null +++ b/colour/colorimetry/uniformity.py @@ -0,0 +1,128 @@ +""" +Spectral Uniformity +=================== + +Defines the objects to compute the *spectral uniformity* +(or *spectral flatness*) of spectral distributions. + +References +---------- +- :cite:`David2015a` : David, A., Fini, P. T., Houser, K. W., Ohno, Y., + Royer, M. P., Smet, K. A. G., Wei, M., & Whitehead, L. (2015). Development + of the IES method for evaluating the color rendition of light sources. + Optics Express, 23(12), 15888. doi:10.1364/OE.23.015888 +""" + +from __future__ import annotations + +import numpy as np + +from colour.colorimetry import ( + MultiSpectralDistributions, + SpectralDistribution, + sds_and_msds_to_msds, +) +from colour.hints import Boolean, List, NDArray, Union + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "spectral_uniformity", +] + + +def spectral_uniformity( + sds: Union[ + List[Union[SpectralDistribution, MultiSpectralDistributions]], + MultiSpectralDistributions, + ], + use_second_order_derivatives: Boolean = False, +) -> NDArray: + """ + Compute the *spectral uniformity* (or *spectral flatness*) of given + spectral distributions. + + Spectral uniformity :math:`(r')^2` is computed as follows: + + :math:`mean((r'_1)^2, (r'_2)^2, ..., (r'_n)^2)` + + where :math:`(r'_i)^2` is the first-order derivative, squared, of the + reflectance :math:`r_i` of a test sample. + + Parameters + ---------- + sds + Spectral distributions or multi-spectral distributions to + compute the spectral uniformity of. `sds` can be a single + :class:`colour.MultiSpectralDistributions` class instance, a list + of :class:`colour.MultiSpectralDistributions` class instances or a + list of :class:`colour.SpectralDistribution` class instances. + use_second_order_derivatives + Whether to use the second-order derivatives in the computations. + + Returns + ------- + :class:`numpy.ndarray` + Spectral uniformity. + + Warnings + -------- + The spectral distributions must have the same spectral shape. + + References + ---------- + :cite:`David2015a` + + Examples + -------- + >>> from colour.quality.datasets import SDS_TCS + >>> spectral_uniformity(SDS_TCS.values()) # doctest: +ELLIPSIS + array([ 9.5514285...e-06, 1.1482142...e-05, 1.8784285...e-05, + 2.8711428...e-05, 3.1971428...e-05, 3.2342857...e-05, + 3.3850000...e-05, 3.9925714...e-05, 4.1333571...e-05, + 2.4002142...e-05, 5.7621428...e-06, 1.4757142...e-06, + 9.7928571...e-07, 2.0057142...e-06, 3.7157142...e-06, + 5.7678571...e-06, 7.5557142...e-06, 7.4635714...e-06, + 5.7492857...e-06, 3.8692857...e-06, 3.5407142...e-06, + 4.4742857...e-06, 5.6435714...e-06, 7.6371428...e-06, + 1.0171428...e-05, 1.2254285...e-05, 1.4810000...e-05, + 1.6517142...e-05, 1.5430714...e-05, 1.4536428...e-05, + 1.4037857...e-05, 1.1587857...e-05, 1.0743571...e-05, + 1.0979285...e-05, 1.0398571...e-05, 8.2971428...e-06, + 6.3057142...e-06, 5.0942857...e-06, 4.8500000...e-06, + 5.5371428...e-06, 6.4128571...e-06, 7.2592857...e-06, + 7.7750000...e-06, 7.1607142...e-06, 6.6635714...e-06, + 6.7328571...e-06, 7.5307142...e-06, 1.0733571...e-05, + 1.6234285...e-05, 2.2570714...e-05, 2.7056428...e-05, + 2.7781428...e-05, 2.5025714...e-05, 1.7966428...e-05, + 1.0505000...e-05, 5.9657142...e-06, 3.6421428...e-06, + 2.1664285...e-06, 1.2935714...e-06, 8.3642857...e-07, + 7.2500000...e-07, 6.3928571...e-07, 6.6285714...e-07, + 8.5571428...e-07, 1.4507142...e-06, 2.2542857...e-06, + 3.4142857...e-06, 4.9864285...e-06, 6.4907142...e-06, + 7.8928571...e-06, 9.1664285...e-06, 9.9521428...e-06, + 9.7664285...e-06, 9.3150000...e-06, 8.9092857...e-06, + 8.1578571...e-06, 6.8935714...e-06, 5.5721428...e-06, + 4.4592857...e-06, 3.4778571...e-06, 2.7650000...e-06, + 2.3114285...e-06, 1.7092857...e-06, 1.1771428...e-06, + 9.8428571...e-07, 8.8285714...e-07, 7.4142857...e-07, + 7.0142857...e-07, 7.0857142...e-07, 6.6642857...e-07, + 7.5928571...e-07, 8.7000000...e-07, 8.2714285...e-07, + 7.1714285...e-07, 6.6000000...e-07]) + """ + + msds = sds_and_msds_to_msds(sds) + + interval = msds.shape.interval + + r_i = np.gradient(np.transpose(msds.values), axis=1) / interval + + if use_second_order_derivatives: + r_i = np.gradient(r_i, axis=1) / interval + + return np.mean(r_i**2, axis=0) diff --git a/colour/colorimetry/whiteness.py b/colour/colorimetry/whiteness.py index 7e4ddd36fa..8020ba1cf1 100644 --- a/colour/colorimetry/whiteness.py +++ b/colour/colorimetry/whiteness.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Whiteness Index :math:`W` ========================= -Defines *whiteness* index :math:`W` computation objects: +Defines the *whiteness* index :math:`W` computation objects: - :func:`colour.colorimetry.whiteness_Berger1959`: *Whiteness* index :math:`WI` computation of given sample *CIE XYZ* tristimulus values using @@ -43,46 +42,69 @@ c/09_color_calculations_en.pdf """ -from __future__ import division, unicode_literals - -from colour.utilities import (CaseInsensitiveMapping, get_domain_range_scale, - filter_kwargs, from_range_100, to_domain_100, - tsplit, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from __future__ import annotations + +from colour.hints import ( + Any, + ArrayLike, + FloatingOrNDArray, + Literal, + NDArray, + Union, +) +from colour.utilities import ( + CaseInsensitiveMapping, + as_float, + as_float_array, + get_domain_range_scale, + filter_kwargs, + from_range_100, + to_domain_100, + tsplit, + tstack, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'whiteness_Berger1959', 'whiteness_Taube1960', 'whiteness_Stensby1968', - 'whiteness_ASTME313', 'whiteness_Ganz1979', 'whiteness_CIE2004', - 'WHITENESS_METHODS', 'whiteness' + "whiteness_Berger1959", + "whiteness_Taube1960", + "whiteness_Stensby1968", + "whiteness_ASTME313", + "whiteness_Ganz1979", + "whiteness_CIE2004", + "WHITENESS_METHODS", + "whiteness", ] -def whiteness_Berger1959(XYZ, XYZ_0): +def whiteness_Berger1959( + XYZ: ArrayLike, XYZ_0: ArrayLike +) -> FloatingOrNDArray: """ - Returns the *whiteness* index :math:`WI` of given sample *CIE XYZ* + Return the *whiteness* index :math:`WI` of given sample *CIE XYZ* tristimulus values using *Berger (1959)* method. Parameters ---------- - XYZ : array_like - *CIE XYZ* tristimulus values of sample. - XYZ_0 : array_like - *CIE XYZ* tristimulus values of reference white. + XYZ + *CIE XYZ* tristimulus values of the sample. + XYZ_0 + *CIE XYZ* tristimulus values of the reference white. Returns ------- - numeric or ndarray + :class:`np.floating` or :class:`numpy.ndarray` *Whiteness* :math:`WI`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -118,29 +140,28 @@ def whiteness_Berger1959(XYZ, XYZ_0): WI = 0.333 * Y + 125 * (Z / Z_0) - 125 * (X / X_0) - return from_range_100(WI) + return as_float(from_range_100(WI)) -def whiteness_Taube1960(XYZ, XYZ_0): +def whiteness_Taube1960(XYZ: ArrayLike, XYZ_0: ArrayLike) -> FloatingOrNDArray: """ - Returns the *whiteness* index :math:`WI` of given sample *CIE XYZ* + Return the *whiteness* index :math:`WI` of given sample *CIE XYZ* tristimulus values using *Taube (1960)* method. Parameters ---------- - XYZ : array_like - *CIE XYZ* tristimulus values of sample. - XYZ_0 : array_like - *CIE XYZ* tristimulus values of reference white. + XYZ + *CIE XYZ* tristimulus values of the sample. + XYZ_0 + *CIE XYZ* tristimulus values of the reference white. Returns ------- - numeric or ndarray + :class:`np.floating` or :class:`numpy.ndarray` *Whiteness* :math:`WI`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -176,27 +197,26 @@ def whiteness_Taube1960(XYZ, XYZ_0): WI = 400 * (Z / Z_0) - 3 * Y - return from_range_100(WI) + return as_float(from_range_100(WI)) -def whiteness_Stensby1968(Lab): +def whiteness_Stensby1968(Lab: ArrayLike) -> FloatingOrNDArray: """ - Returns the *whiteness* index :math:`WI` of given sample *CIE L\\*a\\*b\\** + Return the *whiteness* index :math:`WI` of given sample *CIE L\\*a\\*b\\** colourspace array using *Stensby (1968)* method. Parameters ---------- - Lab : array_like - *CIE L\\*a\\*b\\** colourspace array of sample. + Lab + *CIE L\\*a\\*b\\** colourspace array of the sample. Returns ------- - numeric or ndarray + :class:`np.floating` or :class:`numpy.ndarray` *Whiteness* :math:`WI`. Notes ----- - +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ @@ -232,27 +252,26 @@ def whiteness_Stensby1968(Lab): WI = L - 3 * b + 3 * a - return from_range_100(WI) + return as_float(from_range_100(WI)) -def whiteness_ASTME313(XYZ): +def whiteness_ASTME313(XYZ: ArrayLike) -> FloatingOrNDArray: """ - Returns the *whiteness* index :math:`WI` of given sample *CIE XYZ* + Return the *whiteness* index :math:`WI` of given sample *CIE XYZ* tristimulus values using *ASTM E313* method. Parameters ---------- - XYZ : array_like - *CIE XYZ* tristimulus values of sample. + XYZ + *CIE XYZ* tristimulus values of the sample. Returns ------- - numeric or ndarray + :class:`np.floating` or :class:`numpy.ndarray` *Whiteness* :math:`WI`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -281,30 +300,29 @@ def whiteness_ASTME313(XYZ): WI = 3.388 * Z - 3 * Y - return from_range_100(WI) + return as_float(from_range_100(WI)) -def whiteness_Ganz1979(xy, Y): +def whiteness_Ganz1979(xy: ArrayLike, Y: FloatingOrNDArray) -> NDArray: """ - Returns the *whiteness* index :math:`W` and *tint* :math:`T` of given + Return the *whiteness* index :math:`W` and *tint* :math:`T` of given sample *CIE xy* chromaticity coordinates using *Ganz and Griesser (1979)* method. Parameters ---------- - xy : array_like - Chromaticity coordinates *CIE xy* of sample. - Y : numeric or array_like - Tristimulus :math:`Y` value of sample. + xy + Chromaticity coordinates *CIE xy* of the sample. + Y + Tristimulus :math:`Y` value of the sample. Returns ------- - ndarray + :class:`numpy.ndarray` *Whiteness* :math:`W` and *tint* :math:`T`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -350,38 +368,40 @@ def whiteness_Ganz1979(xy, Y): return from_range_100(WT) -def whiteness_CIE2004(xy, - Y, - xy_n, - observer='CIE 1931 2 Degree Standard Observer'): +def whiteness_CIE2004( + xy: ArrayLike, + Y: FloatingOrNDArray, + xy_n: ArrayLike, + observer: Literal[ + "CIE 1931 2 Degree Standard Observer", + "CIE 1964 10 Degree Standard Observer", + ] = ("CIE 1931 2 Degree Standard Observer"), +) -> NDArray: """ - Returns the *whiteness* :math:`W` or :math:`W_{10}` and *tint* :math:`T` + Return the *whiteness* :math:`W` or :math:`W_{10}` and *tint* :math:`T` or :math:`T_{10}` of given sample *CIE xy* chromaticity coordinates using *CIE 2004* method. Parameters ---------- - xy : array_like - Chromaticity coordinates *CIE xy* of sample. - Y : numeric or array_like - Tristimulus :math:`Y` value of sample. - xy_n : array_like - Chromaticity coordinates *xy_n* of perfect diffuser. - observer : unicode, optional - **{'CIE 1931 2 Degree Standard Observer', - 'CIE 1964 10 Degree Standard Observer'}**, + xy + Chromaticity coordinates *CIE xy* of the sample. + Y + Tristimulus :math:`Y` value of the sample. + xy_n + Chromaticity coordinates *xy_n* of a perfect diffuser. + observer *CIE Standard Observer* used for computations, *tint* :math:`T` or :math:`T_{10}` value is dependent on viewing field angular subtense. Returns ------- - ndarray + :class:`numpy.ndarray` *Whiteness* :math:`W` or :math:`W_{10}` and *tint* :math:`T` or :math:`T_{10}` of given sample. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -424,21 +444,23 @@ def whiteness_CIE2004(xy, x_n, y_n = tsplit(xy_n) W = Y + 800 * (x_n - x) + 1700 * (y_n - y) - T = (1000 if '1931' in observer else 900) * (x_n - x) - 650 * (y_n - y) + T = (1000 if "1931" in observer else 900) * (x_n - x) - 650 * (y_n - y) WT = tstack([W, T]) return from_range_100(WT) -WHITENESS_METHODS = CaseInsensitiveMapping({ - 'Berger 1959': whiteness_Berger1959, - 'Taube 1960': whiteness_Taube1960, - 'Stensby 1968': whiteness_Stensby1968, - 'ASTM E313': whiteness_ASTME313, - 'Ganz 1979': whiteness_Ganz1979, - 'CIE 2004': whiteness_CIE2004 -}) +WHITENESS_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Berger 1959": whiteness_Berger1959, + "Taube 1960": whiteness_Taube1960, + "Stensby 1968": whiteness_Stensby1968, + "ASTM E313": whiteness_ASTME313, + "Ganz 1979": whiteness_Ganz1979, + "CIE 2004": whiteness_CIE2004, + } +) WHITENESS_METHODS.__doc__ = """ Supported *whiteness* computation methods. @@ -446,49 +468,55 @@ def whiteness_CIE2004(xy, ---------- :cite:`CIETC1-482004k`, :cite:`X-Rite2012a` -WHITENESS_METHODS : CaseInsensitiveMapping - **{'CIE 2004', 'Berger 1959', 'Taube 1960', 'Stensby 1968', 'ASTM E313', - 'Ganz 1979', 'CIE 2004'}** - Aliases: - 'cie2004': 'CIE 2004' """ -WHITENESS_METHODS['cie2004'] = WHITENESS_METHODS['CIE 2004'] - - -def whiteness(XYZ, XYZ_0, method='CIE 2004', **kwargs): +WHITENESS_METHODS["cie2004"] = WHITENESS_METHODS["CIE 2004"] + + +def whiteness( + XYZ: ArrayLike, + XYZ_0: ArrayLike, + method: Union[ + Literal[ + "ASTM E313", + "CIE 2004", + "Berger 1959", + "Ganz 1979", + "Stensby 1968", + "Taube 1960", + ], + str, + ] = "CIE 2004", + **kwargs: Any, +) -> FloatingOrNDArray: """ - Returns the *whiteness* :math:`W` using given method. + Return the *whiteness* :math:`W` using given method. Parameters ---------- - XYZ : array_like - *CIE XYZ* tristimulus values of sample. - XYZ_0 : array_like - *CIE XYZ* tristimulus values of reference white. - method : unicode, optional - **{'CIE 2004', 'Berger 1959', 'Taube 1960', 'Stensby 1968', - 'ASTM E313', 'Ganz 1979'}**, + XYZ + *CIE XYZ* tristimulus values of the sample. + XYZ_0 + *CIE XYZ* tristimulus values of the reference white. + method Computation method. Other Parameters ---------------- - observer : unicode, optional + observer {:func:`colour.colorimetry.whiteness_CIE2004`}, - **{'CIE 1931 2 Degree Standard Observer', - 'CIE 1964 10 Degree Standard Observer'}**, *CIE Standard Observer* used for computations, *tint* :math:`T` or :math:`T_{10}` value is dependent on viewing field angular subtense. Returns ------- - numeric or ndarray - *whiteness* :math:`W`. + :class:`np.floating` or :class:`numpy.ndarray` + *Whiteness* :math:`W`. Notes ----- - +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ @@ -523,26 +551,29 @@ def whiteness(XYZ, XYZ_0, method='CIE 2004', **kwargs): 91.4071738... """ - kwargs.update({'XYZ': XYZ, 'XYZ_0': XYZ_0}) + XYZ = as_float_array(XYZ) + XYZ_0 = as_float_array(XYZ_0) + + method = validate_method(method, WHITENESS_METHODS) + + kwargs.update({"XYZ": XYZ, "XYZ_0": XYZ_0}) - function = WHITENESS_METHODS.get(method) + function = WHITENESS_METHODS[method] if function is whiteness_Stensby1968: from colour.models import XYZ_to_Lab, XYZ_to_xy - if get_domain_range_scale() == 'reference': + if get_domain_range_scale() == "reference": XYZ = XYZ / 100 XYZ_0 = XYZ_0 / 100 - kwargs.update({'Lab': XYZ_to_Lab(XYZ, XYZ_to_xy(XYZ_0))}) + kwargs.update({"Lab": XYZ_to_Lab(XYZ, XYZ_to_xy(XYZ_0))}) elif function in (whiteness_Ganz1979, whiteness_CIE2004): from colour.models import XYZ_to_xy _X_0, Y_0, _Z_0 = tsplit(XYZ_0) - kwargs.update({ - 'xy': XYZ_to_xy(XYZ), - 'Y': Y_0, - 'xy_n': XYZ_to_xy(XYZ_0) - }) + kwargs.update( + {"xy": XYZ_to_xy(XYZ), "Y": Y_0, "xy_n": XYZ_to_xy(XYZ_0)} + ) return function(**filter_kwargs(function, **kwargs)) diff --git a/colour/colorimetry/yellowness.py b/colour/colorimetry/yellowness.py index 0658e72144..c48c910897 100644 --- a/colour/colorimetry/yellowness.py +++ b/colour/colorimetry/yellowness.py @@ -1,71 +1,95 @@ -# -*- coding: utf-8 -*- """ Yellowness Index :math:`Y` ========================== -Defines *yellowness* index :math:`Y` computation objects: +Defines the *yellowness* index :math:`Y` computation objects: - :func:`colour.colorimetry.yellowness_ASTMD1925`: *Yellowness* index :math:`YI` computation of given sample *CIE XYZ* tristimulus values using *ASTM D1925* method. -- :func:`colour.colorimetry.yellowness_ASTME313`: *Yellowness* index - :math:`YI` computation of given sample *CIE XYZ* tristimulus values using - *ASTM E313* method. +- :func:`colour.colorimetry.yellowness_ASTME313_alternative`: *Yellowness* + index :math:`YI` computation of given sample *CIE XYZ* tristimulus values + using the alternative *ASTM E313* method. +- :func:`colour.colorimetry.yellowness_ASTME313`: *Yellowness* + index :math:`YI` computation of given sample *CIE XYZ* tristimulus values + using the recommended *ASTM E313* method. - :attr:`colour.YELLOWNESS_METHODS`: Supported *yellowness* computations methods. -- :func:`colour.whiteness`: *Yellowness* :math:`YI` computation using given +- :func:`colour.yellownes`: *Yellowness* :math:`YI` computation using given method. References ---------- +- :cite:`ASTMInternational2015` : ASTM International. (2015). ASTM E313-15e1 + - Standard Practice for Calculating Yellowness and Whiteness Indices from + Instrumentally Measured Color Coordinates. doi:10.1520/E0313-20 - :cite:`X-Rite2012a` : X-Rite, & Pantone. (2012). Color iQC and Color iMatch Color Calculations Guide. https://www.xrite.com/-/media/xrite/files/apps_engineering_techdocuments/\ c/09_color_calculations_en.pdf """ -from __future__ import division, unicode_literals - -from colour.utilities import (CaseInsensitiveMapping, from_range_100, - to_domain_100, tsplit) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from __future__ import annotations + +import numpy as np + +from colour.hints import ( + Any, + ArrayLike, + FloatingOrNDArray, + Literal, + Union, +) +from colour.utilities import ( + CaseInsensitiveMapping, + as_float, + filter_kwargs, + from_range_100, + to_domain_100, + tsplit, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'yellowness_ASTMD1925', 'yellowness_ASTME313', 'YELLOWNESS_METHODS', - 'yellowness' + "yellowness_ASTMD1925", + "yellowness_ASTME313_alternative", + "YELLOWNESS_COEFFICIENTS_ASTME313", + "yellowness_ASTME313", + "YELLOWNESS_METHODS", + "yellowness", ] -def yellowness_ASTMD1925(XYZ): +def yellowness_ASTMD1925(XYZ: ArrayLike) -> FloatingOrNDArray: """ - Returns the *yellowness* index :math:`YI` of given sample *CIE XYZ* + Return the *yellowness* index :math:`YI` of given sample *CIE XYZ* tristimulus values using *ASTM D1925* method. ASTM D1925 has been specifically developed for the definition of the - Yellowness of homogeneous, non-fluorescent, almost neutral-transparent, + yellowness of homogeneous, non-fluorescent, almost neutral-transparent, white-scattering or opaque plastics as they will be reviewed under daylight condition. It can be other materials as well, as long as they fit into this description. Parameters ---------- - XYZ : array_like - *CIE XYZ* tristimulus values of sample. + XYZ + *CIE XYZ* tristimulus values of the sample. Returns ------- - numeric or ndarray - *Whiteness* :math:`YI`. + :class:`np.floating` or :class:`numpy.ndarray` + *Yellowness* :math:`YI`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -78,13 +102,15 @@ def yellowness_ASTMD1925(XYZ): | ``YI`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ + - Input *CIE XYZ* tristimulus values must be adapted to + *CIE Illuminant C*. + References ---------- - :cite:`X-Rite2012a` + :cite:`ASTMInternational2015`, :cite:`X-Rite2012a` Examples -------- - >>> import numpy as np >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) >>> yellowness_ASTMD1925(XYZ) # doctest: +ELLIPSIS 10.2999999... @@ -94,30 +120,34 @@ def yellowness_ASTMD1925(XYZ): YI = (100 * (1.28 * X - 1.06 * Z)) / Y - return from_range_100(YI) + return as_float(from_range_100(YI)) -def yellowness_ASTME313(XYZ): +def yellowness_ASTME313_alternative(XYZ: ArrayLike) -> FloatingOrNDArray: """ - Returns the *yellowness* index :math:`YI` of given sample *CIE XYZ* - tristimulus values using *ASTM E313* method. + Return the *yellowness* index :math:`YI` of given sample *CIE XYZ* + tristimulus values using the alternative *ASTM E313* method. - ASTM E313 has successfully been used for a variety of white or near white - materials. This includes coatings, Plastics, Textiles. + In the original form of *Test Method E313*, an alternative equation was + recommended for a *yellowness* index. In terms of colorimeter readings, + it was :math:`YI = 100(1 − B/G)` where :math:`B` and :math:`G` are, + respectively, blue and green colorimeter readings. Its derivation assumed + that, because of the limitation of the concept to yellow (or blue) colors, + it was not necessary to take account of variations in the amber or red + colorimeter reading :math:`A`. This equation is no longer recommended. Parameters ---------- - XYZ : array_like - *CIE XYZ* tristimulus values of sample. + XYZ + *CIE XYZ* tristimulus values of the sample. Returns ------- - numeric or ndarray - *Whiteness* :math:`YI`. + :class:`np.floating` or :class:`numpy.ndarray` + *Yellowness* :math:`YI`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -130,15 +160,17 @@ def yellowness_ASTME313(XYZ): | ``YI`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ + - Input *CIE XYZ* tristimulus values must be adapted to + *CIE Illuminant C*. + References ---------- - :cite:`X-Rite2012a` + :cite:`ASTMInternational2015`, :cite:`X-Rite2012a` Examples -------- - >>> import numpy as np >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) - >>> yellowness_ASTME313(XYZ) # doctest: +ELLIPSIS + >>> yellowness_ASTME313_alternative(XYZ) # doctest: +ELLIPSIS 11.0650000... """ @@ -146,45 +178,158 @@ def yellowness_ASTME313(XYZ): WI = 100 * (1 - (0.847 * Z) / Y) - return from_range_100(WI) + return as_float(from_range_100(WI)) + + +YELLOWNESS_COEFFICIENTS_ASTME313: CaseInsensitiveMapping = ( + CaseInsensitiveMapping( + { + "CIE 1931 2 Degree Standard Observer": CaseInsensitiveMapping( + { + "C": np.array([1.2769, 1.0592]), + "D65": np.array([1.2985, 1.1335]), + } + ), + "CIE 1964 10 Degree Standard Observer": CaseInsensitiveMapping( + { + "C": np.array([1.2871, 1.0781]), + "D65": np.array([1.3013, 1.1498]), + } + ), + } + ) +) +YELLOWNESS_COEFFICIENTS_ASTME313.__doc__ = """ +Coefficients :math:`C_X` and :math:`C_Z` for the *ASTM E313* *yellowness* index +:math:`YI` computation method. + +References +---------- +:cite:`ASTMInternational2015` + +Aliases: + +- 'cie_2_1931': 'CIE 1931 2 Degree Standard Observer' +- 'cie_10_1964': 'CIE 1964 10 Degree Standard Observer' +""" +YELLOWNESS_COEFFICIENTS_ASTME313[ + "cie_2_1931" +] = YELLOWNESS_COEFFICIENTS_ASTME313["CIE 1931 2 Degree Standard Observer"] +YELLOWNESS_COEFFICIENTS_ASTME313[ + "cie_10_1964" +] = YELLOWNESS_COEFFICIENTS_ASTME313["CIE 1964 10 Degree Standard Observer"] + + +def yellowness_ASTME313( + XYZ: ArrayLike, + C_XZ: ArrayLike = YELLOWNESS_COEFFICIENTS_ASTME313[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], +) -> FloatingOrNDArray: + """ + Return the *yellowness* index :math:`YI` of given sample *CIE XYZ* + tristimulus values using *ASTM E313* method. + + ASTM E313 has successfully been used for a variety of white or near white + materials. This includes coatings, plastics, textiles. + + Parameters + ---------- + XYZ + *CIE XYZ* tristimulus values of the sample. + C_XZ + Coefficients :math:`C_X` and :math:`C_Z` for the + *CIE 1931 2 Degree Standard Observer* and + *CIE 1964 10 Degree Standard Observer* and *CIE Illuminant C* and + *CIE Standard Illuminant D65*. + + Returns + ------- + :class:`np.floating` or :class:`numpy.ndarray` + *Yellowness* :math:`YI`. + + Notes + ----- + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``XYZ`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ + + +------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``YI`` | [0, 100] | [0, 1] | + +------------+-----------------------+---------------+ + References + ---------- + :cite:`ASTMInternational2015` -YELLOWNESS_METHODS = CaseInsensitiveMapping({ - 'ASTM D1925': yellowness_ASTMD1925, - 'ASTM E313': yellowness_ASTME313 -}) + Examples + -------- + >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) + >>> yellowness_ASTME313(XYZ) # doctest: +ELLIPSIS + 4.3400000... + """ + + X, Y, Z = tsplit(to_domain_100(XYZ)) + C_X, C_Z = tsplit(C_XZ) + + WI = 100 * (C_X * X - C_Z * Z) / Y + + return as_float(from_range_100(WI)) + + +YELLOWNESS_METHODS = CaseInsensitiveMapping( + { + "ASTM D1925": yellowness_ASTMD1925, + "ASTM E313 Alternative": yellowness_ASTME313_alternative, + "ASTM E313": yellowness_ASTME313, + } +) YELLOWNESS_METHODS.__doc__ = """ Supported *yellowness* computation methods. References ---------- -:cite:`X-Rite2012a` - -YELLOWNESS_METHODS : CaseInsensitiveMapping - **{'ASTM E313', 'ASTM D1925'}** +:cite:`ASTMInternational2015`, :cite:`X-Rite2012a` """ -def yellowness(XYZ, method='ASTM E313'): +def yellowness( + XYZ: ArrayLike, + method: Union[ + Literal["ASTM D1925", "ASTM E313", "ASTM E313 Alternative"], str + ] = "ASTM E313", + **kwargs: Any, +) -> FloatingOrNDArray: """ - Returns the *yellowness* :math:`W` using given method. + Return the *yellowness* :math:`W` using given method. Parameters ---------- - XYZ : array_like - *CIE XYZ* tristimulus values of sample. - method : unicode, optional - **{'ASTM E313', 'ASTM D1925'}**, + XYZ + *CIE XYZ* tristimulus values of the sample. + method Computation method. + Other Parameters + ---------------- + C_XZ + {:func:`colour.colorimetry.yellowness_ASTME313`}, + Coefficients :math:`C_X` and :math:`C_Z` for the + *CIE 1931 2 Degree Standard Observer* and + *CIE 1964 10 Degree Standard Observer* and *CIE Illuminant C* and + *CIE Standard Illuminant D65*. + Returns ------- - numeric or ndarray - *yellowness* :math:`Y`. + :class:`np.floating` or :class:`numpy.ndarray` + *Yellowness* :math:`Y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -199,17 +344,21 @@ def yellowness(XYZ, method='ASTM E313'): References ---------- - :cite:`X-Rite2012a` + :cite:`ASTMInternational2015`, :cite:`X-Rite2012a` Examples -------- - >>> import numpy as np >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) >>> yellowness(XYZ) # doctest: +ELLIPSIS + 4.3400000... + >>> yellowness(XYZ, method='ASTM E313 Alternative') # doctest: +ELLIPSIS 11.0650000... - >>> method = 'ASTM D1925' - >>> yellowness(XYZ, method=method) # doctest: +ELLIPSIS + >>> yellowness(XYZ, method='ASTM D1925') # doctest: +ELLIPSIS 10.2999999... """ - return YELLOWNESS_METHODS.get(method)(XYZ) + method = validate_method(method, YELLOWNESS_METHODS) + + function = YELLOWNESS_METHODS[method] + + return function(XYZ, **filter_kwargs(function, **kwargs)) diff --git a/colour/constants/__init__.py b/colour/constants/__init__.py index 70a9cc8cf3..36ac6b11fc 100644 --- a/colour/constants/__init__.py +++ b/colour/constants/__init__.py @@ -1,19 +1,32 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .cie import CONSTANT_K_M, CONSTANT_KP_M -from .codata import (CONSTANT_AVOGADRO, CONSTANT_BOLTZMANN, - CONSTANT_LIGHT_SPEED, CONSTANT_PLANCK) -from .common import (FLOATING_POINT_NUMBER_PATTERN, INTEGER_THRESHOLD, EPSILON, - DEFAULT_FLOAT_DTYPE, DEFAULT_INT_DTYPE) +from .codata import ( + CONSTANT_AVOGADRO, + CONSTANT_BOLTZMANN, + CONSTANT_LIGHT_SPEED, + CONSTANT_PLANCK, +) +from .common import ( + FLOATING_POINT_NUMBER_PATTERN, + INTEGER_THRESHOLD, + EPSILON, + DEFAULT_INT_DTYPE, + DEFAULT_FLOAT_DTYPE, +) -__all__ = ['CONSTANT_K_M', 'CONSTANT_KP_M'] +__all__ = [ + "CONSTANT_K_M", + "CONSTANT_KP_M", +] __all__ += [ - 'CONSTANT_AVOGADRO', 'CONSTANT_BOLTZMANN', 'CONSTANT_LIGHT_SPEED', - 'CONSTANT_PLANCK' + "CONSTANT_AVOGADRO", + "CONSTANT_BOLTZMANN", + "CONSTANT_LIGHT_SPEED", + "CONSTANT_PLANCK", ] __all__ += [ - 'FLOATING_POINT_NUMBER_PATTERN', 'INTEGER_THRESHOLD', 'EPSILON', - 'DEFAULT_FLOAT_DTYPE', 'DEFAULT_INT_DTYPE' + "FLOATING_POINT_NUMBER_PATTERN", + "INTEGER_THRESHOLD", + "EPSILON", + "DEFAULT_INT_DTYPE", + "DEFAULT_FLOAT_DTYPE", ] diff --git a/colour/constants/cie.py b/colour/constants/cie.py index 2054e0d756..4fc2bd46a0 100644 --- a/colour/constants/cie.py +++ b/colour/constants/cie.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ CIE Constants ============= @@ -13,29 +12,30 @@ ISBN:978-0-471-39918-6 """ -from __future__ import division, unicode_literals +from colour.utilities.documentation import ( + DocstringFloat, + is_documentation_building, +) -from colour.utilities.documentation import (DocstringFloat, - is_documentation_building) +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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) 2013-2020 - 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__ = [ + "CONSTANT_K_M", + "CONSTANT_KP_M", +] -__all__ = ['CONSTANT_K_M', 'CONSTANT_KP_M'] - -CONSTANT_K_M = 683 +CONSTANT_K_M: float = 683 if is_documentation_building(): # pragma: no cover CONSTANT_K_M = DocstringFloat(CONSTANT_K_M) CONSTANT_K_M.__doc__ = """ Rounded maximum photopic luminous efficiency :math:`K_m` value in :math:`lm\\cdot W^{-1}`. -CONSTANT_K_M : numeric - Notes ----- - To be adequate for all practical applications the :math:`K_m` value has @@ -46,15 +46,13 @@ :cite:`Wyszecki2000s` """ -CONSTANT_KP_M = 1700 +CONSTANT_KP_M: float = 1700 if is_documentation_building(): # pragma: no cover CONSTANT_KP_M = DocstringFloat(CONSTANT_KP_M) CONSTANT_KP_M.__doc__ = """ Rounded maximum scotopic luminous efficiency :math:`K^{\\prime}_m` value in :math:`lm\\cdot W^{-1}`. -CONSTANT_KP_M : numeric - Notes ----- - To be adequate for all practical applications the :math:`K^{\\prime}_m` diff --git a/colour/constants/codata.py b/colour/constants/codata.py index 0c9a1a0766..ada64f3bfd 100644 --- a/colour/constants/codata.py +++ b/colour/constants/codata.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Fundamental Physical Constants ============================== @@ -7,55 +6,49 @@ Science and Technology (CODATA). """ -from __future__ import division, unicode_literals +from colour.utilities.documentation import ( + DocstringFloat, + is_documentation_building, +) -from colour.utilities.documentation import (DocstringFloat, - is_documentation_building) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'CONSTANT_AVOGADRO', 'CONSTANT_BOLTZMANN', 'CONSTANT_LIGHT_SPEED', - 'CONSTANT_PLANCK' + "CONSTANT_AVOGADRO", + "CONSTANT_BOLTZMANN", + "CONSTANT_LIGHT_SPEED", + "CONSTANT_PLANCK", ] -CONSTANT_AVOGADRO = 6.02214179e23 +CONSTANT_AVOGADRO: float = 6.02214179e23 if is_documentation_building(): # pragma: no cover CONSTANT_AVOGADRO = DocstringFloat(CONSTANT_AVOGADRO) CONSTANT_AVOGADRO.__doc__ = """ Avogadro constant. - -CONSTANT_AVOGADRO : numeric """ -CONSTANT_BOLTZMANN = 1.38065e-23 +CONSTANT_BOLTZMANN: float = 1.38065e-23 if is_documentation_building(): # pragma: no cover CONSTANT_BOLTZMANN = DocstringFloat(CONSTANT_BOLTZMANN) CONSTANT_BOLTZMANN.__doc__ = """ Boltzmann constant. - -CONSTANT_BOLTZMANN : numeric """ -CONSTANT_LIGHT_SPEED = 299792458 +CONSTANT_LIGHT_SPEED: float = 299792458 if is_documentation_building(): # pragma: no cover CONSTANT_LIGHT_SPEED = DocstringFloat(CONSTANT_LIGHT_SPEED) CONSTANT_LIGHT_SPEED.__doc__ = """ Speed of light in vacuum. - -CONSTANT_LIGHT_SPEED : numeric """ -CONSTANT_PLANCK = 6.62607e-34 +CONSTANT_PLANCK: float = 6.62607e-34 if is_documentation_building(): # pragma: no cover CONSTANT_PLANCK = DocstringFloat(CONSTANT_PLANCK) CONSTANT_PLANCK.__doc__ = """ Planck constant. - -CONSTANT_PLANCK : numeric """ diff --git a/colour/constants/common.py b/colour/constants/common.py index 966febdc77..fce6c88810 100644 --- a/colour/constants/common.py +++ b/colour/constants/common.py @@ -1,68 +1,68 @@ -# -*- coding: utf-8 -*- """ Common Constants ================ -Defines common constants objects that don't belong to any specific category. +Defines the common constants objects that don't belong to any specific +category. """ -from __future__ import division, unicode_literals +from __future__ import annotations import os import numpy as np -from colour.utilities.documentation import (DocstringFloat, - is_documentation_building) +from colour.utilities.documentation import ( + DocstringFloat, + is_documentation_building, +) +from colour.hints import DTypeFloating, Floating, Type, Union, cast -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'FLOATING_POINT_NUMBER_PATTERN', 'INTEGER_THRESHOLD', 'EPSILON', - 'DEFAULT_FLOAT_DTYPE', 'DEFAULT_INT_DTYPE' + "FLOATING_POINT_NUMBER_PATTERN", + "INTEGER_THRESHOLD", + "EPSILON", + "DEFAULT_INT_DTYPE", + "DEFAULT_FLOAT_DTYPE", ] -FLOATING_POINT_NUMBER_PATTERN = '[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?' -""" -Floating point number regex matching pattern. - -FLOATING_POINT_NUMBER_PATTERN : unicode -""" +FLOATING_POINT_NUMBER_PATTERN: str = "[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?" +"""Floating point number regex matching pattern.""" -INTEGER_THRESHOLD = 1e-3 +INTEGER_THRESHOLD: float = 1e-3 if is_documentation_building(): # pragma: no cover INTEGER_THRESHOLD = DocstringFloat(INTEGER_THRESHOLD) INTEGER_THRESHOLD.__doc__ = """ Integer threshold value when checking if a floating point number is almost an integer. - -INTEGER_THRESHOLD : numeric """ -EPSILON = np.finfo(np.float_).eps +EPSILON: Floating = cast(Floating, np.finfo(np.float_).eps) """ Default epsilon value for tolerance and singularities avoidance in various computations. - -EPSILON : numeric -""" - -DEFAULT_FLOAT_DTYPE = np.sctypeDict.get( - os.environ.get('COLOUR_SCIENCE__FLOAT_PRECISION', 'float64'), 'float64') -""" -Default floating point number dtype. - -DEFAULT_FLOAT_DTYPE : type """ -DEFAULT_INT_DTYPE = np.sctypeDict.get( - os.environ.get('COLOUR_SCIENCE__INT_PRECISION', 'int64'), 'int64') -""" -Default integer number dtype. - -DEFAULT_INT_DTYPE : type -""" +DEFAULT_INT_DTYPE: Type[Union[np.int32, np.int64]] = cast( + Type[Union[np.int32, np.int64]], + np.sctypeDict.get( + os.environ.get("COLOUR_SCIENCE__DEFAULT_INT_DTYPE", "int64"), np.int64 + ), +) +"""Default integer number dtype.""" + + +DEFAULT_FLOAT_DTYPE: Type[DTypeFloating] = cast( + Type[DTypeFloating], + np.sctypeDict.get( + os.environ.get("COLOUR_SCIENCE__DEFAULT_FLOAT_DTYPE", "float64"), + np.float64, + ), +) +"""Default floating point number dtype.""" diff --git a/colour/continuous/__init__.py b/colour/continuous/__init__.py index 56044d8b8e..68516be701 100644 --- a/colour/continuous/__init__.py +++ b/colour/continuous/__init__.py @@ -1,45 +1,14 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - -import sys - -from colour.utilities.deprecation import ModuleAPI, build_API_changes -from colour.utilities.documentation import is_documentation_building - from .abstract import AbstractContinuousFunction from .signal import Signal from .multi_signals import MultiSignals __all__ = [] -__all__ += ['AbstractContinuousFunction'] -__all__ += ['Signal'] -__all__ += ['MultiSignals'] - - -# ----------------------------------------------------------------------------# -# --- API Changes and Deprecation Management ---# -# ----------------------------------------------------------------------------# -class continuous(ModuleAPI): - def __getattr__(self, attribute): - return super(continuous, self).__getattr__(attribute) - - -# v0.3.14 -API_CHANGES = { - 'ObjectRenamed': [[ - 'colour.continuous.MultiSignal', - 'colour.continuous.MultiSignals', - ], ] -} -""" -Defines *colour.continuous* sub-package API changes. - -API_CHANGES : dict -""" - -if not is_documentation_building(): - sys.modules['colour.continuous'] = continuous( - sys.modules['colour.continuous'], build_API_changes(API_CHANGES)) - - del ModuleAPI, is_documentation_building, build_API_changes, sys +__all__ += [ + "AbstractContinuousFunction", +] +__all__ += [ + "Signal", +] +__all__ += [ + "MultiSignals", +] diff --git a/colour/continuous/abstract.py b/colour/continuous/abstract.py index a26b2ff2c7..dbb8eede0a 100644 --- a/colour/continuous/abstract.py +++ b/colour/continuous/abstract.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Abstract Continuous Function ============================ @@ -9,38 +8,55 @@ - :class:`colour.continuous.AbstractContinuousFunction`. """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from abc import ABCMeta, abstractmethod, abstractproperty +from abc import ABC, abstractmethod from copy import deepcopy -# Python 3 compatibility. -try: - from operator import div, idiv -except ImportError: - from operator import truediv, itruediv - - div = truediv - idiv = itruediv -from six import add_metaclass - -from colour.utilities import as_float, closest, is_uniform, is_string - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['AbstractContinuousFunction'] - - -@add_metaclass(ABCMeta) -class AbstractContinuousFunction: +from colour.hints import ( + ArrayLike, + Any, + Boolean, + Callable, + Dict, + DTypeFloating, + FloatingOrArrayLike, + FloatingOrNDArray, + Integer, + Literal, + NDArray, + Number, + Optional, + Type, + TypeExtrapolator, + TypeInterpolator, + Union, +) +from colour.utilities import ( + as_float, + attest, + closest, + is_uniform, + is_string, + optional, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "AbstractContinuousFunction", +] + + +class AbstractContinuousFunction(ABC): """ - Defines the base class for abstract continuous function. + Define the base class for abstract continuous function. This is an :class:`ABCMeta` abstract class that must be inherited by sub-classes. @@ -48,22 +64,23 @@ class AbstractContinuousFunction: The sub-classes are expected to implement the :meth:`colour.continuous.AbstractContinuousFunction.function` method so that evaluating the function for any independent domain - :math:`x \\in\\mathbb{R}` variable returns a corresponding range - :math:`y \\in\\mathbb{R}` variable. A conventional implementation adopts an + variable :math:`x \\in\\mathbb{R}` returns a corresponding range variable + :math:`y \\in\\mathbb{R}`. A conventional implementation adopts an interpolating function encapsulated inside an extrapolating function. The resulting function independent domain, stored as discrete values in the :attr:`colour.continuous.AbstractContinuousFunction.domain` attribute corresponds with the function dependent and already known range stored in - the :attr:`colour.continuous.AbstractContinuousFunction.range` attribute. + the :attr:`colour.continuous.AbstractContinuousFunction.range` property. Parameters ---------- - name : unicode, optional + name Continuous function name. Attributes ---------- - :attr:`~colour.continuous.AbstractContinuousFunction.name` + - :attr:`~colour.continuous.AbstractContinuousFunction.dtype` - :attr:`~colour.continuous.AbstractContinuousFunction.domain` - :attr:`~colour.continuous.AbstractContinuousFunction.range` - :attr:`~colour.continuous.AbstractContinuousFunction.interpolator` @@ -104,160 +121,170 @@ class AbstractContinuousFunction: - :meth:`~colour.continuous.AbstractContinuousFunction.copy` """ - def __init__(self, name=None): - self._name = '{0} ({1})'.format(self.__class__.__name__, id(self)) - self.name = name + def __init__(self, name: Optional[str] = None): + self._name: str = f"{self.__class__.__name__} ({id(self)})" + self.name = optional(name, self._name) @property - def name(self): + def name(self) -> str: """ Getter and setter property for the abstract continuous function name. Parameters ---------- - value : unicode + value Value to set the abstract continuous function name with. Returns ------- - unicode + :class:`str` Abstract continuous function name. """ return self._name @name.setter - def name(self, value): - """ - Setter for **self.name** property. - """ + def name(self, value: str): + """Setter for the **self.name** property.""" + + attest( + is_string(value), + f'"name" property: "{value}" type is not "str"!', + ) - if value is not None: - assert is_string(value), ( - ('"{0}" attribute: "{1}" type is not "str" or "unicode"!' - ).format('name', value)) - self._name = value + self._name = value - def _get_dtype(self): + # TODO: Remove pragma when https://github.com/python/mypy/issues/4165 is + # resolved. + @property # type: ignore[misc] + @abstractmethod + def dtype(self) -> Type[DTypeFloating]: """ Getter and setter property for the abstract continuous function dtype, must be reimplemented by sub-classes. Parameters ---------- - value : type + value Value to set the abstract continuous function dtype with. Returns ------- - type + Type[DTypeFloating] Abstract continuous function dtype. """ - pass + ... # pragma: no cover - def _set_dtype(self, value): + @dtype.setter # type: ignore[misc] + @abstractmethod + def dtype(self, value: Type[DTypeFloating]): """ - Setter for **self.dtype** property, must be reimplemented by + Setter for the **self.dtype** property, must be reimplemented by sub-classes. """ - pass - - domain = abstractproperty(_get_dtype, _set_dtype) + ... # pragma: no cover - def _get_domain(self): + @property # type: ignore[misc] + @abstractmethod + def domain(self) -> NDArray: """ Getter and setter property for the abstract continuous function - independent domain :math:`x` variable, must be reimplemented by + independent domain variable :math:`x`, must be reimplemented by sub-classes. Parameters ---------- - value : array_like + value Value to set the abstract continuous function independent domain - :math:`x` variable with. + variable :math:`x` with. Returns ------- - ndarray - Abstract continuous function independent domain - :math:`x` variable. + :class:`numpy.ndarray` + Abstract continuous function independent domain variable :math:`x`. """ - pass + ... # pragma: no cover - def _set_domain(self, value): + @domain.setter # type: ignore[misc] + @abstractmethod + def domain(self, value: ArrayLike): """ Setter for the **self.domain** property, must be reimplemented by sub-classes. """ - pass - - domain = abstractproperty(_get_domain, _set_domain) + ... # pragma: no cover - def _get_range(self): + @property # type: ignore[misc] + @abstractmethod + def range(self) -> NDArray: """ Getter and setter property for the abstract continuous function - corresponding range :math:`y` variable, - must be reimplemented by sub-classes. + corresponding range variable :math:`y`, must be reimplemented by + sub-classes. Parameters ---------- - value : array_like + value Value to set the abstract continuous function corresponding range - :math:`y` variable with. + variable :math:`y` with. Returns ------- - ndarray - Abstract continuous function corresponding range - :math:`y` variable. + :class:`numpy.ndarray` + Abstract continuous function corresponding range variable + :math:`y`. """ - pass + ... # pragma: no cover - def _set_range(self, value): + @range.setter # type: ignore[misc] + @abstractmethod + def range(self, value: ArrayLike): """ Setter for the **self.range** property, must be reimplemented by sub-classes. """ - pass - - range = abstractproperty(_get_range, _set_range) + ... # pragma: no cover - def _get_interpolator(self): + @property # type: ignore[misc] + @abstractmethod + def interpolator(self) -> Type[TypeInterpolator]: """ Getter and setter property for the abstract continuous function interpolator type, must be reimplemented by sub-classes. Parameters ---------- - value : type + value Value to set the abstract continuous function interpolator type with. Returns ------- - type + Type[TypeInterpolator] Abstract continuous function interpolator type. """ - pass + ... # pragma: no cover - def _set_interpolator(self, value): + @interpolator.setter # type: ignore[misc] + @abstractmethod + def interpolator(self, value: Type[TypeInterpolator]): """ Setter for the **self.interpolator** property, must be reimplemented by sub-classes. """ - pass - - interpolator = abstractproperty(_get_interpolator, _set_interpolator) + ... # pragma: no cover - def _get_interpolator_kwargs(self): + @property # type: ignore[misc] + @abstractmethod + def interpolator_kwargs(self) -> Dict: """ Getter and setter property for the abstract continuous function interpolator instantiation time arguments, must be reimplemented by @@ -265,60 +292,63 @@ def _get_interpolator_kwargs(self): Parameters ---------- - value : dict + value Value to set the abstract continuous function interpolator instantiation time arguments to. Returns ------- - dict + :class:`dict` Abstract continuous function interpolator instantiation time arguments. """ - pass + ... # pragma: no cover - def _set_interpolator_kwargs(self, value): + @interpolator_kwargs.setter # type: ignore[misc] + @abstractmethod + def interpolator_kwargs(self, value: dict): """ Setter for the **self.interpolator_kwargs** property, must be reimplemented by sub-classes. """ - pass - - interpolator_kwargs = abstractproperty(_get_interpolator_kwargs, - _set_interpolator_kwargs) + ... # pragma: no cover - def _get_extrapolator(self): + @property # type: ignore[misc] + @abstractmethod + def extrapolator(self) -> Type[TypeExtrapolator]: """ Getter and setter property for the abstract continuous function extrapolator type, must be reimplemented by sub-classes. Parameters ---------- - value : type + value Value to set the abstract continuous function extrapolator type with. Returns ------- - type + Type[TypeExtrapolator] Abstract continuous function extrapolator type. """ - pass + ... # pragma: no cover - def _set_extrapolator(self, value): + @extrapolator.setter # type: ignore[misc] + @abstractmethod + def extrapolator(self, value: Type[TypeExtrapolator]): """ Setter for the **self.extrapolator** property, must be reimplemented by sub-classes. """ - pass - - extrapolator = abstractproperty(_get_extrapolator, _set_extrapolator) + ... # pragma: no cover - def _get_extrapolator_kwargs(self): + @property # type: ignore[misc] + @abstractmethod + def extrapolator_kwargs(self) -> Dict: """ Getter and setter property for the abstract continuous function extrapolator instantiation time arguments, must be reimplemented by @@ -326,470 +356,491 @@ def _get_extrapolator_kwargs(self): Parameters ---------- - value : dict + value Value to set the abstract continuous function extrapolator instantiation time arguments to. Returns ------- - dict + :class:`dict` Abstract continuous function extrapolator instantiation time arguments. """ - pass + ... # pragma: no cover - def _set_extrapolator_kwargs(self, value): + @extrapolator_kwargs.setter # type: ignore[misc] + @abstractmethod + def extrapolator_kwargs(self, value: dict): """ Setter for the **self.extrapolator_kwargs** property, must be reimplemented by sub-classes. """ - pass - - extrapolator_kwargs = abstractproperty(_get_extrapolator_kwargs, - _set_extrapolator_kwargs) + ... # pragma: no cover - def _get_function(self): + @property + @abstractmethod + def function(self) -> Callable: """ - Getter and setter property for the abstract continuous function - callable, must be reimplemented by sub-classes. - - Parameters - ---------- - value : object - Attribute value. + Getter property for the abstract continuous function callable, must be + reimplemented by sub-classes. Returns ------- - callable + Callable Abstract continuous function callable. """ - pass - - def _set_function(self, value): - """ - Setter for the **self.function** property, must be reimplemented by - sub-classes. - """ - - pass - - function = abstractproperty(_get_function, _set_function) + ... # pragma: no cover @abstractmethod - def __str__(self): + def __str__(self) -> str: """ - Returns a formatted string representation of the abstract continuous + Return a formatted string representation of the abstract continuous function, must be reimplemented by sub-classes. Returns ------- - unicode + :class:`str` Formatted string representation. """ - return '<{0} object at {1}>'.format(self.__class__.__name__, id(self)) + ... # pragma: no cover @abstractmethod - def __repr__(self): + def __repr__(self) -> str: """ - Returns an evaluable string representation of the abstract continuous + Return an evaluable string representation of the abstract continuous function, must be reimplemented by sub-classes. Returns ------- - unicode + :class:`str` Evaluable string representation. """ - return '{0}()'.format(self.__class__.__name__) + ... # pragma: no cover @abstractmethod - def __hash__(self): + def __hash__(self) -> Integer: """ - Returns the abstract continuous function hash. + Return the abstract continuous function hash. Returns ------- - int + :class:`numpy.integer` Object hash. """ - pass + ... # pragma: no cover @abstractmethod - def __getitem__(self, x): + def __getitem__( + self, x: Union[FloatingOrArrayLike, slice] + ) -> FloatingOrNDArray: """ - Returns the corresponding range :math:`y` variable for independent - domain :math:`x` variable, must be reimplemented by sub-classes. + Return the corresponding range variable :math:`y` for independent + domain variable :math:`x`, must be reimplemented by sub-classes. Parameters ---------- - x : numeric, array_like or slice - Independent domain :math:`x` variable. + x + Independent domain variable :math:`x`. Returns ------- - numeric or ndarray - math:`y` range value. + :class:`numpy.floating` or :class:`numpy.ndarray` + Variable :math:`y` range value. """ - pass + ... # pragma: no cover @abstractmethod - def __setitem__(self, x, y): + def __setitem__( + self, x: Union[FloatingOrArrayLike, slice], y: FloatingOrArrayLike + ): """ - Sets the corresponding range :math:`y` variable for independent domain - :math:`x` variable, must be reimplemented by sub-classes. + Set the corresponding range variable :math:`y` for independent domain + variable :math:`x`, must be reimplemented by sub-classes. Parameters ---------- - x : numeric, array_like or slice - Independent domain :math:`x` variable. - y : numeric or ndarray - Corresponding range :math:`y` variable. + x + Independent domain variable :math:`x`. + y + Corresponding range variable :math:`y`. """ - pass + ... # pragma: no cover @abstractmethod - def __contains__(self, x): + def __contains__(self, x: Union[FloatingOrArrayLike, slice]) -> bool: """ - Returns whether the abstract continuous function contains given - independent domain :math:`x` variable, must be reimplemented by + Return whether the abstract continuous function contains given + independent domain variable :math:`x`, must be reimplemented by sub-classes. Parameters ---------- - x : numeric, array_like or slice - Independent domain :math:`x` variable. + x + Independent domain variable :math:`x`. Returns ------- - bool - Is :math:`x` domain value contained. + :class:`bool` + Whether :math:`x` domain value is contained. """ - pass + ... # pragma: no cover - def __len__(self): + def __len__(self) -> Integer: """ - Returns the abstract continuous function independent domain :math:`x` + Return the abstract continuous function independent domain :math:`x` variable elements count. Returns ------- - int - Independent domain :math:`x` variable elements count. + :class:`numpy.integer` + Independent domain variable :math:`x` elements count. """ return len(self.domain) @abstractmethod - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: """ - Returns whether the abstract continuous function is equal to given + Return whether the abstract continuous function is equal to given other object, must be reimplemented by sub-classes. Parameters ---------- - other : object + other Object to test whether it is equal to the abstract continuous function. Returns ------- - bool - Is given object equal to the abstract continuous function. + :class:`bool` + Whether given object is equal to the abstract continuous function. """ - pass + ... # pragma: no cover @abstractmethod - def __ne__(self, other): + def __ne__(self, other: Any) -> bool: """ - Returns whether the abstract continuous function is not equal to given + Return whether the abstract continuous function is not equal to given other object, must be reimplemented by sub-classes. Parameters ---------- - other : object + other Object to test whether it is not equal to the abstract continuous function. Returns ------- - bool - Is given object not equal to the abstract continuous function. + :class:`bool` + Whether given object is not equal to the abstract continuous + function. """ - pass + ... # pragma: no cover - def __add__(self, a): + def __add__( + self, a: Union[FloatingOrArrayLike, AbstractContinuousFunction] + ) -> AbstractContinuousFunction: """ - Implements support for addition. + Implement support for addition. Parameters ---------- - a : numeric or array_like or AbstractContinuousFunction - :math:`a` variable to add. + a + Variable :math:`a` to add. Returns ------- - AbstractContinuousFunction + :class:`colour.continuous.AbstractContinuousFunction` Variable added abstract continuous function. """ - return self.arithmetical_operation(a, '+') + return self.arithmetical_operation(a, "+") - def __iadd__(self, a): + def __iadd__( + self, a: Union[FloatingOrArrayLike, AbstractContinuousFunction] + ) -> AbstractContinuousFunction: """ - Implements support for in-place addition. + Implement support for in-place addition. Parameters ---------- - a : numeric or array_like or AbstractContinuousFunction - :math:`a` variable to add in-place. + a + Variable :math:`a` to add in-place. Returns ------- - AbstractContinuousFunction + :class:`colour.continuous.AbstractContinuousFunction` In-place variable added abstract continuous function. """ - return self.arithmetical_operation(a, '+', True) + return self.arithmetical_operation(a, "+", True) - def __sub__(self, a): + def __sub__( + self, a: Union[FloatingOrArrayLike, AbstractContinuousFunction] + ) -> AbstractContinuousFunction: """ - Implements support for subtraction. + Implement support for subtraction. Parameters ---------- - a : numeric or array_like or AbstractContinuousFunction - :math:`a` variable to subtract. + a + Variable :math:`a` to subtract. Returns ------- - AbstractContinuousFunction + :class:`colour.continuous.AbstractContinuousFunction` Variable subtracted abstract continuous function. """ - return self.arithmetical_operation(a, '-') + return self.arithmetical_operation(a, "-") - def __isub__(self, a): + def __isub__( + self, a: Union[FloatingOrArrayLike, AbstractContinuousFunction] + ) -> AbstractContinuousFunction: """ - Implements support for in-place subtraction. + Implement support for in-place subtraction. Parameters ---------- - a : numeric or array_like or AbstractContinuousFunction - :math:`a` variable to subtract in-place. + a + Variable :math:`a` to subtract in-place. Returns ------- - AbstractContinuousFunction + :class:`colour.continuous.AbstractContinuousFunction` In-place variable subtracted abstract continuous function. """ - return self.arithmetical_operation(a, '-', True) + return self.arithmetical_operation(a, "-", True) - def __mul__(self, a): + def __mul__( + self, a: Union[FloatingOrArrayLike, AbstractContinuousFunction] + ) -> AbstractContinuousFunction: """ - Implements support for multiplication. + Implement support for multiplication. Parameters ---------- - a : numeric or array_like or AbstractContinuousFunction - :math:`a` variable to multiply by. + a + Variable :math:`a` to multiply by. Returns ------- - AbstractContinuousFunction + :class:`colour.continuous.AbstractContinuousFunction` Variable multiplied abstract continuous function. """ - return self.arithmetical_operation(a, '*') + return self.arithmetical_operation(a, "*") - def __imul__(self, a): + def __imul__( + self, a: Union[FloatingOrArrayLike, AbstractContinuousFunction] + ) -> AbstractContinuousFunction: """ - Implements support for in-place multiplication. + Implement support for in-place multiplication. Parameters ---------- - a : numeric or array_like or AbstractContinuousFunction - :math:`a` variable to multiply by in-place. + a + Variable :math:`a` to multiply by in-place. Returns ------- - AbstractContinuousFunction + :class:`colour.continuous.AbstractContinuousFunction` In-place variable multiplied abstract continuous function. """ - return self.arithmetical_operation(a, '*', True) + return self.arithmetical_operation(a, "*", True) - def __div__(self, a): + def __div__( + self, a: Union[FloatingOrArrayLike, AbstractContinuousFunction] + ) -> AbstractContinuousFunction: """ - Implements support for division. + Implement support for division. Parameters ---------- - a : numeric or array_like or AbstractContinuousFunction - :math:`a` variable to divide by. + a + Variable :math:`a` to divide by. Returns ------- - AbstractContinuousFunction + :class:`colour.continuous.AbstractContinuousFunction` Variable divided abstract continuous function. """ - return self.arithmetical_operation(a, '/') + return self.arithmetical_operation(a, "/") - def __idiv__(self, a): + def __idiv__( + self, a: Union[FloatingOrArrayLike, AbstractContinuousFunction] + ) -> AbstractContinuousFunction: """ - Implements support for in-place division. + Implement support for in-place division. Parameters ---------- - a : numeric or array_like or AbstractContinuousFunction - :math:`a` variable to divide by in-place. + a + Variable :math:`a` to divide by in-place. Returns ------- - AbstractContinuousFunction + :class:`colour.continuous.AbstractContinuousFunction` In-place variable divided abstract continuous function. """ - return self.arithmetical_operation(a, '/', True) + return self.arithmetical_operation(a, "/", True) __itruediv__ = __idiv__ __truediv__ = __div__ - def __pow__(self, a): + def __pow__( + self, a: Union[FloatingOrArrayLike, AbstractContinuousFunction] + ) -> AbstractContinuousFunction: """ - Implements support for exponentiation. + Implement support for exponentiation. Parameters ---------- - a : numeric or array_like or AbstractContinuousFunction - :math:`a` variable to exponentiate by. + a + Variable :math:`a` to exponentiate by. Returns ------- - AbstractContinuousFunction + :class:`colour.continuous.AbstractContinuousFunction` Variable exponentiated abstract continuous function. """ - return self.arithmetical_operation(a, '**') + return self.arithmetical_operation(a, "**") - def __ipow__(self, a): + def __ipow__( + self, a: Union[FloatingOrArrayLike, AbstractContinuousFunction] + ) -> AbstractContinuousFunction: """ - Implements support for in-place exponentiation. + Implement support for in-place exponentiation. Parameters ---------- - a : numeric or array_like or AbstractContinuousFunction - :math:`a` variable to exponentiate by in-place. + a + Variable :math:`a` to exponentiate by in-place. Returns ------- - AbstractContinuousFunction + :class:`colour.continuous.AbstractContinuousFunction` In-place variable exponentiated abstract continuous function. """ - return self.arithmetical_operation(a, '**', True) + return self.arithmetical_operation(a, "**", True) @abstractmethod - def arithmetical_operation(self, a, operation, in_place=False): - """ - Performs given arithmetical operation with :math:`a` operand, the + def arithmetical_operation( + self, + a: Union[FloatingOrArrayLike, AbstractContinuousFunction], + operation: Literal["+", "-", "*", "/", "**"], + in_place: Boolean = False, + ) -> AbstractContinuousFunction: + """ + Perform given arithmetical operation with operand :math:`a`, the operation can be either performed on a copy or in-place, must be reimplemented by sub-classes. Parameters ---------- - a : numeric or ndarray or AbstractContinuousFunction - Operand. - operation : object + a + Operand :math:`a`. + operation Operation to perform. - in_place : bool, optional + in_place Operation happens in place. Returns ------- - AbstractContinuousFunction + :class:`colour.continuous.AbstractContinuousFunction` Abstract continuous function. """ - pass + ... # pragma: no cover @abstractmethod - def fill_nan(self, method='Interpolation', default=0): - """ - Fill NaNs in independent domain :math:`x` variable and corresponding - range :math:`y` variable using given method, must be reimplemented by + def fill_nan( + self, + method: Union[ + Literal["Constant", "Interpolation"], str + ] = "Interpolation", + default: Number = 0, + ) -> AbstractContinuousFunction: + """ + Fill NaNs in independent domain variable :math:`x` and corresponding + range variable :math:`y` using given method, must be reimplemented by sub-classes. Parameters ---------- - method : unicode, optional - **{'Interpolation', 'Constant'}**, + method *Interpolation* method linearly interpolates through the NaNs, *Constant* method replaces NaNs with ``default``. - default : numeric, optional + default Value to use with the *Constant* method. Returns ------- - AbstractContinuousFunction + :class:`colour.continuous.AbstractContinuousFunction` NaNs filled abstract continuous function. """ - pass + ... # pragma: no cover - def domain_distance(self, a): + def domain_distance(self, a: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the euclidean distance between given array and independent + Return the euclidean distance between given array and independent domain :math:`x` closest element. Parameters ---------- - a : numeric or array_like - :math:`a` variable to compute the euclidean distance with - independent domain :math:`x` variable. + a + Variable :math:`a` to compute the euclidean distance with + independent domain variable :math:`x`. Returns ------- - numeric or array_like - Euclidean distance between independent domain :math:`x` variable - and given :math:`a` variable. + :class:`numpy.floating` or :class:`numpy.ndarray` + Euclidean distance between independent domain variable :math:`x` + and given variable :math:`a`. """ n = closest(self.domain, a) return as_float(np.abs(a - n)) - def is_uniform(self): + def is_uniform(self) -> Boolean: """ - Returns if independent domain :math:`x` variable is uniform. + Return if independent domain variable :math:`x` is uniform. Returns ------- - bool - Is independent domain :math:`x` variable uniform. + :class:`bool` + Is independent domain variable :math:`x` uniform. """ return is_uniform(self.domain) - def copy(self): + def copy(self) -> AbstractContinuousFunction: """ - Returns a copy of the sub-class instance. + Return a copy of the sub-class instance. Returns ------- - AbstractContinuousFunction + :class:`colour.continuous.AbstractContinuousFunction` Abstract continuous function copy. """ diff --git a/colour/continuous/multi_signals.py b/colour/continuous/multi_signals.py index 33c6c189bb..ac6a83ac82 100644 --- a/colour/continuous/multi_signals.py +++ b/colour/continuous/multi_signals.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Multi Signals ============= @@ -8,43 +7,72 @@ - :class:`colour.continuous.MultiSignals` """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np - -# Python 3 compatibility. -try: - from operator import div, idiv -except ImportError: - from operator import truediv, itruediv - - div = truediv - idiv = itruediv -from collections import OrderedDict -try: # pragma: no cover - from collections import Iterator, Mapping, Sequence -except ImportError: # pragma: no cover - from collections.abc import Iterator, Mapping, Sequence +from collections.abc import Iterator, Mapping, ValuesView from colour.constants import DEFAULT_FLOAT_DTYPE from colour.continuous import AbstractContinuousFunction, Signal -from colour.utilities import (as_float_array, first_item, is_pandas_installed, - required, tsplit, tstack, usage_warning) -from colour.utilities.deprecation import ObjectRenamed - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['MultiSignals'] +from colour.hints import ( + Any, + ArrayLike, + Boolean, + Callable, + Dict, + DTypeFloating, + FloatingOrArrayLike, + FloatingOrNDArray, + Integer, + List, + Literal, + NDArray, + Number, + Optional, + Sequence, + Type, + TypeExtrapolator, + TypeInterpolator, + Union, + cast, +) +from colour.utilities import ( + as_float_array, + attest, + first_item, + is_iterable, + is_pandas_installed, + optional, + required, + tsplit, + tstack, + validate_method, +) +from colour.utilities.documentation import is_documentation_building + +if is_pandas_installed(): + from pandas import DataFrame, Series +else: # pragma: no cover + from unittest import mock + + DataFrame = mock.MagicMock() + Series = mock.MagicMock() + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "MultiSignals", +] class MultiSignals(AbstractContinuousFunction): """ - Defines the base class for multi-continuous signals, a container for + Define the base class for multi-continuous signals, a container for multiple :class:`colour.continuous.Signal` sub-class instances. .. important:: @@ -55,39 +83,37 @@ class MultiSignals(AbstractContinuousFunction): Parameters ---------- - data : Series or Dataframe or Signal or MultiSignals or array_like or \ -dict_like, optional + data Data to be stored in the multi-continuous signals. - domain : array_like, optional + domain Values to initialise the multiple :class:`colour.continuous.Signal` sub-class instances :attr:`colour.continuous.Signal.domain` attribute with. If both ``data`` and ``domain`` arguments are defined, the latter will be used to initialise the :attr:`colour.continuous.Signal.domain` attribute. - labels : array_like, optional + labels Names to use for the :class:`colour.continuous.Signal` sub-class instances. Other Parameters ---------------- - name : unicode, optional - multi-continuous signals name. - dtype : type, optional - **{np.float16, np.float32, np.float64, np.float128}**, + dtype Floating point data type. - interpolator : object, optional - Interpolator class type to use as interpolating function for the - :class:`colour.continuous.Signal` sub-class instances. - interpolator_kwargs : dict_like, optional - Arguments to use when instantiating the interpolating function - of the :class:`colour.continuous.Signal` sub-class instances. - extrapolator : object, optional + extrapolator Extrapolator class type to use as extrapolating function for the :class:`colour.continuous.Signal` sub-class instances. - extrapolator_kwargs : dict_like, optional + extrapolator_kwargs Arguments to use when instantiating the extrapolating function of the :class:`colour.continuous.Signal` sub-class instances. - signal_type : type, optional + interpolator + Interpolator class type to use as interpolating function for the + :class:`colour.continuous.Signal` sub-class instances. + interpolator_kwargs + Arguments to use when instantiating the interpolating function + of the :class:`colour.continuous.Signal` sub-class instances. + name + multi-continuous signals name. + signal_type The :class:`colour.continuous.Signal` sub-class type used for instances. @@ -220,7 +246,7 @@ class MultiSignals(AbstractContinuousFunction): [ 900. 90.] [ 1000. 100.]] - Instantiation with a *Pandas* `DataFrame`: + Instantiation with a *Pandas* :class:`pandas.DataFrame`: >>> if is_pandas_installed(): ... from pandas import DataFrame @@ -270,346 +296,349 @@ class MultiSignals(AbstractContinuousFunction): [ 0.8414709..., 1.0914709..., 1.3414709...]]) """ - def __init__(self, data=None, domain=None, labels=None, **kwargs): - super(MultiSignals, self).__init__(kwargs.get('name')) - - self._signal_type = kwargs.get('signal_type', Signal) - - self._signals = self.multi_signals_unpack_data(data, domain, labels, - **kwargs) + def __init__( + self, + data: Optional[ + Union[ + ArrayLike, + DataFrame, + dict, + MultiSignals, + Sequence, + Series, + Signal, + ] + ] = None, + domain: Optional[ArrayLike] = None, + labels: Optional[Sequence] = None, + **kwargs: Any, + ): + super().__init__(kwargs.get("name")) + + self._signal_type: Type[Signal] = kwargs.get("signal_type", Signal) + + self._signals: Dict[str, Signal] = self.multi_signals_unpack_data( + data, domain, labels, **kwargs + ) @property - def dtype(self): + def dtype(self) -> Type[DTypeFloating]: """ Getter and setter property for the continuous signal dtype. Parameters ---------- - value : type + value Value to set the continuous signal dtype with. Returns ------- - type + Type[DTypeFloating] Continuous signal dtype. """ - if self._signals: - return first_item(self._signals.values()).dtype + return first_item(self._signals.values()).dtype @dtype.setter - def dtype(self, value): - """ - Setter for **self.dtype** property. - """ + def dtype(self, value: Type[DTypeFloating]): + """Setter for the **self.dtype** property.""" - if value is not None: - for signal in self._signals.values(): - signal.dtype = value + for signal in self._signals.values(): + signal.dtype = value @property - def domain(self): + def domain(self) -> NDArray: """ Getter and setter property for the :class:`colour.continuous.Signal` - sub-class instances independent domain :math:`x` variable. + sub-class instances independent domain variable :math:`x`. Parameters ---------- - value : array_like + value Value to set the :class:`colour.continuous.Signal` sub-class - instances independent domain :math:`x` variable with. + instances independent domain variable :math:`x` with. Returns ------- - ndarray + :class:`numpy.ndarray` :class:`colour.continuous.Signal` sub-class instances independent - domain :math:`x` variable. + domain variable :math:`x`. """ - if self._signals: - return first_item(self._signals.values()).domain + return first_item(self._signals.values()).domain @domain.setter - def domain(self, value): - """ - Setter for the **self.domain** property. - """ + def domain(self, value: ArrayLike): + """Setter for the **self.domain** property.""" - if value is not None: - for signal in self._signals.values(): - signal.domain = value + for signal in self._signals.values(): + signal.domain = as_float_array(value, self.dtype) @property - def range(self): + def range(self) -> NDArray: """ Getter and setter property for the :class:`colour.continuous.Signal` - sub-class instances corresponding range :math:`y` variable. + sub-class instances corresponding range variable :math:`y`. Parameters ---------- - value : array_like + value Value to set the :class:`colour.continuous.Signal` sub-class - instances corresponding range :math:`y` variable with. + instances corresponding range variable :math:`y` with. Returns ------- - ndarray + :class:`numpy.ndarray` :class:`colour.continuous.Signal` sub-class instances corresponding - range :math:`y` variable. + range variable :math:`y`. """ - if self._signals: - return tstack([signal.range for signal in self._signals.values()]) + return tstack([signal.range for signal in self._signals.values()]) @range.setter - def range(self, value): - """ - Setter for the **self.range** property. - """ + def range(self, value: ArrayLike): + """Setter for the **self.range** property.""" - if value is not None: - value = as_float_array(value) + value = as_float_array(value) - if value.ndim in (0, 1): - for signal in self._signals.values(): - signal.range = value - else: - assert value.shape[-1] == len(self._signals), ( - 'Corresponding "y" variable columns must have ' - 'same count than underlying "Signal" components!') + if value.ndim in (0, 1): + for signal in self._signals.values(): + signal.range = value + else: + attest( + value.shape[-1] == len(self._signals), + 'Corresponding "y" variable columns must have ' + 'same count than underlying "Signal" components!', + ) - for signal, y in zip(self._signals.values(), tsplit(value)): - signal.range = y + for signal, y in zip(self._signals.values(), tsplit(value)): + signal.range = y @property - def interpolator(self): + def interpolator(self) -> Type[TypeInterpolator]: """ Getter and setter property for the :class:`colour.continuous.Signal` sub-class instances interpolator type. Parameters ---------- - value : type + value Value to set the :class:`colour.continuous.Signal` sub-class instances interpolator type with. Returns ------- - type + Type[TypeInterpolator] :class:`colour.continuous.Signal` sub-class instances interpolator type. """ - if self._signals: - return first_item(self._signals.values()).interpolator + return first_item(self._signals.values()).interpolator @interpolator.setter - def interpolator(self, value): - """ - Setter for the **self.interpolator** property. - """ + def interpolator(self, value: Type[TypeInterpolator]): + """Setter for the **self.interpolator** property.""" if value is not None: for signal in self._signals.values(): signal.interpolator = value @property - def interpolator_kwargs(self): + def interpolator_kwargs(self) -> Dict: """ Getter and setter property for the :class:`colour.continuous.Signal` sub-class instances interpolator instantiation time arguments. Parameters ---------- - value : dict + value Value to set the :class:`colour.continuous.Signal` sub-class instances interpolator instantiation time arguments to. Returns ------- - dict + :class:`dict` :class:`colour.continuous.Signal` sub-class instances interpolator instantiation time arguments. """ - if self._signals: - return first_item(self._signals.values()).interpolator_kwargs + return first_item(self._signals.values()).interpolator_kwargs @interpolator_kwargs.setter - def interpolator_kwargs(self, value): - """ - Setter for the **self.interpolator_kwargs** property. - """ + def interpolator_kwargs(self, value: dict): + """Setter for the **self.interpolator_kwargs** property.""" - if value is not None: - for signal in self._signals.values(): - signal.interpolator_kwargs = value + for signal in self._signals.values(): + signal.interpolator_kwargs = value @property - def extrapolator(self): + def extrapolator(self) -> Type[TypeExtrapolator]: """ Getter and setter property for the :class:`colour.continuous.Signal` sub-class instances extrapolator type. Parameters ---------- - value : type + value Value to set the :class:`colour.continuous.Signal` sub-class instances extrapolator type with. Returns ------- - type + Type[TypeExtrapolator] :class:`colour.continuous.Signal` sub-class instances extrapolator type. """ - if self._signals: - return first_item(self._signals.values()).extrapolator + return first_item(self._signals.values()).extrapolator @extrapolator.setter - def extrapolator(self, value): - """ - Setter for the **self.extrapolator** property. - """ + def extrapolator(self, value: Type[TypeExtrapolator]): + """Setter for the **self.extrapolator** property.""" - if value is not None: - for signal in self._signals.values(): - signal.extrapolator = value + for signal in self._signals.values(): + signal.extrapolator = value @property - def extrapolator_kwargs(self): + def extrapolator_kwargs(self) -> Dict: """ Getter and setter property for the :class:`colour.continuous.Signal` sub-class instances extrapolator instantiation time arguments. Parameters ---------- - value : dict + value Value to set the :class:`colour.continuous.Signal` sub-class instances extrapolator instantiation time arguments to. Returns ------- - dict + :class:`dict` :class:`colour.continuous.Signal` sub-class instances extrapolator instantiation time arguments. """ - if self._signals: - return first_item(self._signals.values()).extrapolator_kwargs + return first_item(self._signals.values()).extrapolator_kwargs @extrapolator_kwargs.setter - def extrapolator_kwargs(self, value): - """ - Setter for the **self.extrapolator_kwargs** property. - """ + def extrapolator_kwargs(self, value: dict): + """Setter for the **self.extrapolator_kwargs** property.""" - if value is not None: - for signal in self._signals.values(): - signal.extrapolator_kwargs = value + for signal in self._signals.values(): + signal.extrapolator_kwargs = value @property - def function(self): + def function(self) -> Callable: """ Getter property for the :class:`colour.continuous.Signal` sub-class instances callable. Returns ------- - callable + Callable :class:`colour.continuous.Signal` sub-class instances callable. """ - if self._signals: - return first_item(self._signals.values()).function + return first_item(self._signals.values()).function @property - def signals(self): + def signals(self) -> Dict[str, Signal]: """ Getter and setter property for the :class:`colour.continuous.Signal` sub-class instances. Parameters ---------- - value : Series or Dataframe or Signal or MultiSignals or array_like \ -or dict_like + value Attribute value. Returns ------- - OrderedDict + :class:`dict` :class:`colour.continuous.Signal` sub-class instances. """ return self._signals @signals.setter - def signals(self, value): - """ - Setter for the **self.signals** property. - """ - - if value is not None: - self._signals = self.multi_signals_unpack_data( - value, signal_type=self._signal_type) + def signals( + self, + value: Optional[ + Union[ArrayLike, DataFrame, dict, MultiSignals, Signal, Series] + ], + ): + """Setter for the **self.signals** property.""" + + self._signals = self.multi_signals_unpack_data( + value, signal_type=self._signal_type + ) @property - def labels(self): + def labels(self) -> List[str]: """ Getter and setter property for the :class:`colour.continuous.Signal` - sub-class instances name. + sub-class instance names. Parameters ---------- - value : array_like + value Value to set the :class:`colour.continuous.Signal` sub-class - instances name. + instance names. Returns ------- - dict - :class:`colour.continuous.Signal` sub-class instance name. + :class:`list` + :class:`colour.continuous.Signal` sub-class instance names. """ - if self._signals: - return list(self._signals.keys()) + return [str(key) for key in self._signals.keys()] @labels.setter - def labels(self, value): - """ - Setter for the **self.labels** property. - """ + def labels(self, value: Sequence): + """Setter for the **self.labels** property.""" - if value is not None: - assert len(value) == len(self._signals), ( - '"labels" length does not match "signals" length!') - self._signals = OrderedDict( - [(value[i], signal) - for i, (_key, signal) in enumerate(self._signals.items())]) + attest( + is_iterable(value), + f'"labels" property: "{value}" is not an "iterable" like object!', + ) + + attest( + len(set(value)) == len(value), + '"labels" property: values must be unique!', + ) + + attest( + len(value) == len(self.labels), + f'"labels" property: length must be "{len(self._signals)}"!', + ) + + self._signals = { + str(value[i]): signal + for i, signal in enumerate(self._signals.values()) + } @property - def signal_type(self): + def signal_type(self) -> Type[Signal]: """ Getter property for the :class:`colour.continuous.Signal` sub-class instances type. Returns ------- - type + Type[Signal] :class:`colour.continuous.Signal` sub-class instances type. """ return self._signal_type - def __str__(self): + def __str__(self) -> str: """ - Returns a formatted string representation of the multi-continuous + Return a formatted string representation of the multi-continuous signals. Returns ------- - unicode + :class:`str` Formatted string representation. Examples @@ -633,16 +662,16 @@ def __str__(self): try: return str(np.hstack([self.domain[:, np.newaxis], self.range])) except TypeError: - return super(MultiSignals, self).__str__() + return super().__str__() - def __repr__(self): + def __repr__(self) -> str: """ - Returns an evaluable string representation of the multi-continuous + Return an evaluable string representation of the multi-continuous signals. Returns ------- - unicode + :class:`str` Evaluable string representation. Examples @@ -661,75 +690,90 @@ def __repr__(self): [ 7., 80., 90., 100.], [ 8., 90., 100., 110.], [ 9., 100., 110., 120.]], - labels=[0, 1, 2], + labels=['0', '1', '2'], interpolator=KernelInterpolator, interpolator_kwargs={}, extrapolator=Extrapolator, extrapolator_kwargs={...) """ + if is_documentation_building(): # pragma: no cover + return f"{self.__class__.__name__}(name='{self.name}', ...)" + try: representation = repr( - np.hstack([self.domain[:, np.newaxis], self.range])) - representation = representation.replace('array', - self.__class__.__name__) + np.hstack([self.domain[:, np.newaxis], self.range]) + ) representation = representation.replace( - ' [', - '{0}['.format(' ' * (len(self.__class__.__name__) + 2))) - representation = ('{0},\n' - '{1}labels={2},\n' - '{1}interpolator={3},\n' - '{1}interpolator_kwargs={4},\n' - '{1}extrapolator={5},\n' - '{1}extrapolator_kwargs={6})').format( - representation[:-1], - ' ' * (len(self.__class__.__name__) + 1), - repr(self.labels), self.interpolator.__name__ - if self.interpolator is not None else - self.interpolator, - repr(self.interpolator_kwargs), - self.extrapolator.__name__ - if self.extrapolator is not None else - self.extrapolator, - repr(self.extrapolator_kwargs)) + "array", self.__class__.__name__ + ) + representation = representation.replace( + " [", + f"{' ' * (len(self.__class__.__name__) + 2)}[", + ) + indentation = " " * (len(self.__class__.__name__) + 1) + interpolator = ( + self.interpolator.__name__ + if self.interpolator is not None + else self.interpolator + ) + extrapolator = ( + self.extrapolator.__name__ + if self.extrapolator is not None + else self.extrapolator + ) + representation = ( + f"{representation[:-1]},\n" + f"{indentation}labels={repr(self.labels)},\n" + f"{indentation}interpolator={interpolator},\n" + f"{indentation}interpolator_kwargs=" + f"{repr(self.interpolator_kwargs)},\n" + f"{indentation}extrapolator={extrapolator},\n" + f"{indentation}extrapolator_kwargs=" + f"{repr(self.extrapolator_kwargs)})" + ) return representation except TypeError: - return super(MultiSignals, self).__repr__() + return super().__repr__() - def __hash__(self): + def __hash__(self) -> Integer: """ - Returns the abstract continuous function hash. + Return the abstract continuous function hash. Returns ------- - int + :class:`numpy.integer` Object hash. """ - return hash(( - self.domain.tobytes(), - self.range.tobytes(), - self.interpolator.__name__, - repr(self.interpolator_kwargs), - self.extrapolator.__name__, - repr(self.extrapolator_kwargs), - )) + return hash( + ( + self.domain.tobytes(), + self.range.tobytes(), + self.interpolator.__name__, + repr(self.interpolator_kwargs), + self.extrapolator.__name__, + repr(self.extrapolator_kwargs), + ) + ) - def __getitem__(self, x): + def __getitem__( + self, x: Union[FloatingOrArrayLike, slice] + ) -> FloatingOrNDArray: """ - Returns the corresponding range :math:`y` variable for independent - domain :math:`x` variable. + Return the corresponding range variable :math:`y` for independent + domain variable :math:`x`. Parameters ---------- - x : numeric, array_like or slice - Independent domain :math:`x` variable. + x + Independent domain variable :math:`x`. Returns ------- - numeric or ndarray - math:`y` range value. + :class:`numpy.floating` or :class:`numpy.ndarray` + Variable :math:`y` range value. Examples -------- @@ -778,23 +822,23 @@ def __getitem__(self, x): x_r, x_c = (x[0], x[1]) if isinstance(x, tuple) else (x, slice(None)) - if self._signals: - return tstack( - [signal[x_r] for signal in self._signals.values()])[..., x_c] - else: - raise RuntimeError('No underlying "Signal" defined!') + return tstack([signal[x_r] for signal in self._signals.values()])[ + ..., x_c + ] - def __setitem__(self, x, y): + def __setitem__( + self, x: Union[FloatingOrArrayLike, slice], y: FloatingOrArrayLike + ): """ - Sets the corresponding range :math:`y` variable for independent domain - :math:`x` variable. + Set the corresponding range variable :math:`y` for independent domain + variable :math:`x`. Parameters ---------- - x : numeric, array_like or slice - Independent domain :math:`x` variable. - y : numeric or ndarray - Corresponding range :math:`y` variable. + x + Independent domain variable :math:`x`. + y + Corresponding range variable :math:`y`. Examples -------- @@ -893,36 +937,40 @@ def __setitem__(self, x, y): x_r, x_c = (x[0], x[1]) if isinstance(x, tuple) else (x, slice(None)) - assert y.ndim in range(3), ( + attest( + y.ndim in range(3), 'Corresponding "y" variable must be a numeric or a 1-dimensional ' - 'or 2-dimensional array!') + "or 2-dimensional array!", + ) if y.ndim == 0: y = np.tile(y, len(self._signals)) elif y.ndim == 1: y = y[np.newaxis, :] - assert y.shape[-1] == len(self._signals), ( + attest( + y.shape[-1] == len(self._signals), 'Corresponding "y" variable columns must have same count than ' - 'underlying "Signal" components!') + 'underlying "Signal" components!', + ) for signal, y in list(zip(self._signals.values(), tsplit(y)))[x_c]: signal[x_r] = y - def __contains__(self, x): + def __contains__(self, x: Union[FloatingOrArrayLike, slice]) -> bool: """ - Returns whether the multi-continuous signals contains given independent - domain :math:`x` variable. + Return whether the multi-continuous signals contains given independent + domain variable :math:`x`. Parameters ---------- - x : numeric, array_like or slice - Independent domain :math:`x` variable. + x + Independent domain variable :math:`x`. Returns ------- - bool - Is :math:`x` domain value contained. + :class:`bool` + Whether :math:`x` domain value is contained. Examples -------- @@ -936,25 +984,22 @@ def __contains__(self, x): False """ - if self._signals: - return x in first_item(self._signals.values()) - else: - raise RuntimeError('No underlying "Signal" defined!') + return x in first_item(self._signals.values()) - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: """ - Returns whether the multi-continuous signals is equal to given other + Return whether the multi-continuous signals is equal to given other object. Parameters ---------- - other : object + other Object to test whether it is equal to the multi-continuous signals. Returns ------- - bool - Is given object equal to the multi-continuous signals. + :class:`bool` + Whether given object is equal to the multi-continuous signals. Examples -------- @@ -976,35 +1021,35 @@ def __eq__(self, other): """ if isinstance(other, MultiSignals): - if all([ + return all( + [ np.array_equal(self.domain, other.domain), - np.array_equal( - self.range, - other.range), self.interpolator is other.interpolator, + np.array_equal(self.range, other.range), + self.interpolator is other.interpolator, self.interpolator_kwargs == other.interpolator_kwargs, self.extrapolator is other.extrapolator, self.extrapolator_kwargs == other.extrapolator_kwargs, - self.labels == other.labels - ]): - return True - - return False + self.labels == other.labels, + ] + ) + else: + return False - def __ne__(self, other): + def __ne__(self, other: Any) -> bool: """ - Returns whether the multi-continuous signals is not equal to given + Return whether the multi-continuous signals is not equal to given other object. Parameters ---------- - other : object + other Object to test whether it is not equal to the multi-continuous signals. Returns ------- - bool - Is given object not equal to the multi-continuous signals. + :class:`bool` + Whether given object is not equal to the multi-continuous signals. Examples -------- @@ -1027,23 +1072,28 @@ def __ne__(self, other): return not (self == other) - def arithmetical_operation(self, a, operation, in_place=False): + def arithmetical_operation( + self, + a: Union[FloatingOrArrayLike, AbstractContinuousFunction], + operation: Literal["+", "-", "*", "/", "**"], + in_place: Boolean = False, + ) -> AbstractContinuousFunction: """ - Performs given arithmetical operation with :math:`a` operand, the + Perform given arithmetical operation with operand :math:`a`, the operation can be either performed on a copy or in-place. Parameters ---------- - a : numeric or ndarray or Signal - Operand. - operation : object + a + Operand :math:`a`. + operation Operation to perform. - in_place : bool, optional + in_place Operation happens in place. Returns ------- - MultiSignals + :class:`colour.continuous.MultiSignals` multi-continuous signals. Examples @@ -1077,7 +1127,7 @@ def arithmetical_operation(self, a, operation, in_place=False): [ 8. 100. 110. 120.] [ 9. 110. 120. 130.]] - Adding an *array_like* variable: + Adding an `ArrayLike` variable: >>> a = np.linspace(10, 100, 10) >>> print(multi_signals_1.arithmetical_operation(a, '+', True)) @@ -1135,95 +1185,117 @@ def arithmetical_operation(self, a, operation, in_place=False): [ 9. 347. 378. 409.]] """ - multi_signals = self if in_place else self.copy() + multi_signals = cast(MultiSignals, self if in_place else self.copy()) if isinstance(a, MultiSignals): - assert len(self.signals) == len(a.signals), ( + attest( + len(self.signals) == len(a.signals), '"MultiSignals" operands must have same count than ' - 'underlying "Signal" components!') - for signal_a, signal_b in zip(multi_signals.signals.values(), - a.signals.values()): + 'underlying "Signal" components!', + ) + + for signal_a, signal_b in zip( + multi_signals.signals.values(), a.signals.values() + ): signal_a.arithmetical_operation(signal_b, operation, True) else: - a = as_float_array(a) + a = as_float_array(a) # type: ignore[arg-type] - assert a.ndim in range(3), ( + attest( + a.ndim in range(3), 'Operand "a" variable must be a numeric or a 1-dimensional or ' - '2-dimensional array!') + "2-dimensional array!", + ) if a.ndim in (0, 1): for signal in multi_signals.signals.values(): signal.arithmetical_operation(a, operation, True) else: - assert a.shape[-1] == len(multi_signals.signals), ( + attest( + a.shape[-1] == len(multi_signals.signals), 'Operand "a" variable columns must have same count than ' - 'underlying "Signal" components!') + 'underlying "Signal" components!', + ) - for signal, y in zip(multi_signals.signals.values(), - tsplit(a)): + for signal, y in zip( + multi_signals.signals.values(), tsplit(a) + ): signal.arithmetical_operation(y, operation, True) return multi_signals @staticmethod - def multi_signals_unpack_data(data=None, - domain=None, - labels=None, - dtype=None, - signal_type=Signal, - **kwargs): + def multi_signals_unpack_data( + data: Optional[ + Union[ + ArrayLike, + DataFrame, + dict, + MultiSignals, + Sequence, + Series, + Signal, + ] + ] = None, + domain: Optional[ArrayLike] = None, + labels: Optional[Sequence] = None, + dtype: Optional[Type[DTypeFloating]] = None, + signal_type: Type[Signal] = Signal, + **kwargs: Any, + ) -> Dict[str, Signal]: """ Unpack given data for multi-continuous signals instantiation. Parameters ---------- - data : Series or Dataframe or Signal or MultiSignals or array_like or \ -dict_like, optional + data Data to unpack for multi-continuous signals instantiation. - domain : array_like, optional + domain Values to initialise the multiple :class:`colour.continuous.Signal` sub-class instances :attr:`colour.continuous.Signal.domain` attribute with. If both ``data`` and ``domain`` arguments are defined, the latter will be used to initialise the - :attr:`colour.continuous.Signal.domain` attribute. - dtype : type, optional - **{np.float16, np.float32, np.float64, np.float128}**, + :attr:`colour.continuous.Signal.domain` property. + labels + Names to use for the :class:`colour.continuous.Signal` sub-class + instances. + dtype Floating point data type. - signal_type : type, optional + signal_type A :class:`colour.continuous.Signal` sub-class type. Other Parameters ---------------- - name : unicode, optional - multi-continuous signals name. - interpolator : object, optional - Interpolator class type to use as interpolating function for the - :class:`colour.continuous.Signal` sub-class instances. - interpolator_kwargs : dict_like, optional - Arguments to use when instantiating the interpolating function - of the :class:`colour.continuous.Signal` sub-class instances. - extrapolator : object, optional + extrapolator Extrapolator class type to use as extrapolating function for the :class:`colour.continuous.Signal` sub-class instances. - extrapolator_kwargs : dict_like, optional + extrapolator_kwargs Arguments to use when instantiating the extrapolating function of the :class:`colour.continuous.Signal` sub-class instances. + interpolator + Interpolator class type to use as interpolating function for the + :class:`colour.continuous.Signal` sub-class instances. + interpolator_kwargs + Arguments to use when instantiating the interpolating function + of the :class:`colour.continuous.Signal` sub-class instances. + name + multi-continuous signals name. Returns ------- - dict + :class:`dict` Mapping of labeled :class:`colour.continuous.Signal` sub-class instances. Examples -------- - Unpacking using implicit *domain* and a single signal: + Unpacking using implicit *domain* and data for a single signal: >>> range_ = np.linspace(10, 100, 10) >>> signals = MultiSignals.multi_signals_unpack_data(range_) >>> list(signals.keys()) - [0] - >>> print(signals[0]) + ['0'] + >>> print(signals['0']) [[ 0. 10.] [ 1. 20.] [ 2. 30.] @@ -1235,13 +1307,13 @@ def multi_signals_unpack_data(data=None, [ 8. 90.] [ 9. 100.]] - Unpacking using explicit *domain* and a single signal: + Unpacking using explicit *domain* and data for a single signal: >>> domain = np.arange(100, 1100, 100) >>> signals = MultiSignals.multi_signals_unpack_data(range_, domain) >>> list(signals.keys()) - [0] - >>> print(signals[0]) + ['0'] + >>> print(signals['0']) [[ 100. 10.] [ 200. 20.] [ 300. 30.] @@ -1253,14 +1325,14 @@ def multi_signals_unpack_data(data=None, [ 900. 90.] [ 1000. 100.]] - Unpacking using multiple signals: + Unpacking using data for multiple signals: >>> range_ = tstack([np.linspace(10, 100, 10)] * 3) >>> range_ += np.array([0, 10, 20]) >>> signals = MultiSignals.multi_signals_unpack_data(range_, domain) >>> list(signals.keys()) - [0, 1, 2] - >>> print(signals[2]) + ['0', '1', '2'] + >>> print(signals['2']) [[ 100. 30.] [ 200. 40.] [ 300. 50.] @@ -1277,8 +1349,28 @@ def multi_signals_unpack_data(data=None, >>> signals = MultiSignals.multi_signals_unpack_data( ... dict(zip(domain, range_))) >>> list(signals.keys()) - [0, 1, 2] - >>> print(signals[2]) + ['0', '1', '2'] + >>> print(signals['2']) + [[ 100. 30.] + [ 200. 40.] + [ 300. 50.] + [ 400. 60.] + [ 500. 70.] + [ 600. 80.] + [ 700. 90.] + [ 800. 100.] + [ 900. 110.] + [ 1000. 120.]] + + Unpacking using a sequence of *Signal* instances, note how the keys + are :class:`str` instances because the *Signal* names are used: + + >>> signals = MultiSignals.multi_signals_unpack_data( + ... dict(zip(domain, range_))).values() + >>> signals = MultiSignals.multi_signals_unpack_data(signals) + >>> list(signals.keys()) + ['0', '1', '2'] + >>> print(signals['2']) [[ 100. 30.] [ 200. 40.] [ 300. 50.] @@ -1296,8 +1388,8 @@ def multi_signals_unpack_data(data=None, ... dict(zip(domain, range_))) >>> signals = MultiSignals.multi_signals_unpack_data(signals) >>> list(signals.keys()) - [0, 1, 2] - >>> print(signals[2]) + ['0', '1', '2'] + >>> print(signals['2']) [[ 100. 30.] [ 200. 40.] [ 300. 50.] @@ -1327,7 +1419,7 @@ def multi_signals_unpack_data(data=None, [ 900. 90.] [ 1000. 100.]] - Unpacking using a *Pandas* `DataFrame`: + Unpacking using a *Pandas* :class:`pandas.DataFrame`: >>> if is_pandas_installed(): ... from pandas import DataFrame @@ -1347,88 +1439,143 @@ def multi_signals_unpack_data(data=None, [ 1000. 120.]] """ - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) + + settings = {} + settings.update(kwargs) + settings.update({"dtype": dtype}) + + # domain_unpacked, range_unpacked, signals = ( + # np.array([]), np.array([]), {}) - domain_u, range_u, signals = None, None, None - signals = OrderedDict() - # TODO: Implement support for Signal class passing. - if isinstance(data, MultiSignals): + signals = {} + + if isinstance(data, Signal): + signals[data.name] = data + elif isinstance(data, MultiSignals): signals = data.signals - elif (issubclass(type(data), Sequence) or - isinstance(data, (tuple, list, np.ndarray, Iterator))): - data = tsplit(list(data) if isinstance(data, Iterator) else data) - assert data.ndim in (1, 2), ( - 'User "data" must be 1-dimensional or 2-dimensional!') - if data.ndim == 1: - data = data[np.newaxis, :] - for i, range_u in enumerate(data): - signals[i] = signal_type(range_u, domain, **kwargs) - elif (issubclass(type(data), Mapping) or - isinstance(data, (dict, OrderedDict))): - - # Handling `MultiSignals.multi_signals_unpack_data` method output - # used as argument to `MultiSignals.multi_signals_unpack_data` - # method. - is_signal = all([ - True if isinstance(i, Signal) else False - for i in data.values() - ]) + elif issubclass(type(data), Sequence) or isinstance( + data, (tuple, list, np.ndarray, Iterator, ValuesView) + ): + data_sequence = list(data) # type: ignore[arg-type] + + is_signal = all( + [ + True if isinstance(i, Signal) else False + for i in data_sequence + ] + ) if is_signal: - for label, signal in data.items(): - signals[label] = signal_type(signal.range, signal.domain, - **kwargs) + for signal in data_sequence: + signals[signal.name] = signal_type( + signal.range, signal.domain, **settings + ) else: - domain_u, range_u = zip(*sorted(data.items())) - for i, range_u in enumerate(tsplit(range_u)): - signals[i] = signal_type(range_u, domain_u, **kwargs) - elif is_pandas_installed(): - from pandas import DataFrame, Series + data_array = tsplit(data_sequence) + attest( + data_array.ndim in (1, 2), + 'User "data" must be 1-dimensional or 2-dimensional!', + ) + + if data_array.ndim == 1: + data_array = data_array[np.newaxis, :] + + for i, range_unpacked in enumerate(data_array): + signals[str(i)] = signal_type( + range_unpacked, domain, **settings + ) + elif issubclass(type(data), Mapping) or isinstance(data, dict): + data_mapping = dict(data) # type: ignore[arg-type] + + is_signal = all( + [ + True if isinstance(i, Signal) else False + for i in data_mapping.values() + ] + ) + if is_signal: + for label, signal in data_mapping.items(): + signals[label] = signal_type( + signal.range, signal.domain, **settings + ) + else: + domain_unpacked, range_unpacked = zip( + *sorted(data_mapping.items()) + ) + for i, range_unpacked in enumerate(tsplit(range_unpacked)): + signals[str(i)] = signal_type( + range_unpacked, domain_unpacked, **settings + ) + elif is_pandas_installed(): if isinstance(data, Series): - signals[0] = signal_type(data, **kwargs) + signals["0"] = signal_type(data, **settings) elif isinstance(data, DataFrame): - domain_u = data.index.values - signals = OrderedDict(((label, - signal_type( - data[label], - domain_u, - name=label, - **kwargs)) for label in data)) - - if domain is not None and signals is not None: + domain_unpacked = data.index.values + signals = { + label: signal_type( + data[label], domain_unpacked, **settings + ) + for label in data + } + + if domain is not None: + domain_array = as_float_array(list(domain), dtype) # type: ignore[arg-type] + for signal in signals.values(): - assert len(domain) == len(signal.domain), ( - 'User "domain" is not compatible with unpacked signals!') - signal.domain = domain + attest( + len(domain_array) == len(signal.domain), + 'User "domain" length is not compatible with unpacked ' + '"signals"!', + ) - if labels is not None and signals is not None: - assert len(labels) == len(signals), ( - 'User "labels" is not compatible with unpacked signals!') - signals = OrderedDict( - [(labels[i], signal) - for i, (_key, signal) in enumerate(signals.items())]) + signal.domain = domain_array + + signals = {str(label): signal for label, signal in signals.items()} + + if labels is not None: + attest( + len(labels) == len(signals), + 'User "labels" length is not compatible with unpacked ' + '"signals"!', + ) + + signals = { + str(labels[i]): signal + for i, signal in enumerate(signals.values()) + } + + for label in signals: + signals[label].name = label + + if not signals: + signals = {"Undefined": Signal(name="Undefined")} return signals - def fill_nan(self, method='Interpolation', default=0): + def fill_nan( + self, + method: Union[ + Literal["Constant", "Interpolation"], str + ] = "Interpolation", + default: Number = 0, + ) -> AbstractContinuousFunction: """ - Fill NaNs in independent domain :math:`x` variable and corresponding - range :math:`y` variable using given method. + Fill NaNs in independent domain variable :math:`x` and corresponding + range variable :math:`y` using given method. Parameters ---------- - method : unicode, optional - **{'Interpolation', 'Constant'}**, + method *Interpolation* method linearly interpolates through the NaNs, *Constant* method replaces NaNs with ``default``. - default : numeric, optional + default Value to use with the *Constant* method. Returns ------- - Signal + :class:`colour.continuous.MultiSignals` NaNs filled multi-continuous signals. >>> domain = np.arange(0, 10, 1) @@ -1472,21 +1619,24 @@ def fill_nan(self, method='Interpolation', default=0): [ 9. 100. 110. 120.]] """ + method = validate_method(method, ["Interpolation", "Constant"]) + for signal in self._signals.values(): signal.fill_nan(method, default) return self - @required('Pandas') - def to_dataframe(self): + @required("Pandas") + def to_dataframe(self) -> DataFrame: """ - Converts the continuous signal to a *Pandas* :class:`DataFrame` class - instance. + Convert the continuous signal to a *Pandas* :class:`pandas.DataFrame` + class instance. Returns ------- - DataFrame - Continuous signal as a *Pandas* :class:`DataFrame` class instance. + :class:`pandas.DataFrame` + Continuous signal as a *Pandas* :class:`pandas.DataFrame` class + instance. Examples -------- @@ -1509,50 +1659,6 @@ def to_dataframe(self): 9.0 100.0 110.0 120.0 """ - from pandas import DataFrame - return DataFrame( - data=self.range, index=self.domain, columns=self.labels) - - # ------------------------------------------------------------------------# - # --- API Changes and Deprecation Management ---# - # ------------------------------------------------------------------------# - @property - def interpolator_args(self): - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('MultiSignals.interpolator_args', - 'MultiSignals.interpolator_kwargs'))) - - return self.interpolator_kwargs - - @interpolator_args.setter - def interpolator_args(self, value): - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('MultiSignals.interpolator_args', - 'MultiSignals.interpolator_kwargs'))) - - self.interpolator_kwargs = value - - @property - def extrapolator_args(self): - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('MultiSignals.extrapolator_args', - 'MultiSignals.extrapolator_kwargs'))) - - return self.extrapolator_kwargs - - @extrapolator_args.setter - def extrapolator_args(self, value): - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('MultiSignals.extrapolator_args', - 'MultiSignals.extrapolator_kwargs'))) - - self.extrapolator_kwargs = value + data=self.range, index=self.domain, columns=self.labels + ) diff --git a/colour/continuous/signal.py b/colour/continuous/signal.py index 953cb5f3b8..852076f34b 100644 --- a/colour/continuous/signal.py +++ b/colour/continuous/signal.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Signal ====== @@ -8,55 +7,92 @@ - :class:`colour.continuous.Signal` """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from operator import add, mul, pow, sub, iadd, imul, ipow, isub - -# Python 3 compatibility. -try: - from operator import div, idiv -except ImportError: - from operator import truediv, itruediv - - div = truediv - idiv = itruediv -from collections import OrderedDict -try: # pragma: no cover - from collections import Iterator, Mapping, Sequence -except ImportError: # pragma: no cover - from collections.abc import Iterator, Mapping, Sequence - +from operator import ( + add, + mul, + pow, + sub, + truediv, + iadd, + imul, + ipow, + isub, + itruediv, +) +from collections.abc import Iterator, Mapping, Sequence, ValuesView from colour.algebra import Extrapolator, KernelInterpolator from colour.constants import DEFAULT_FLOAT_DTYPE from colour.continuous import AbstractContinuousFunction -from colour.utilities import (as_array, fill_nan, full, is_pandas_installed, - required, runtime_warning, tsplit, tstack, - usage_warning) -from colour.utilities.deprecation import ObjectRenamed - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['Signal'] +from colour.hints import ( + Any, + ArrayLike, + Boolean, + Callable, + DTypeFloating, + Dict, + FloatingOrArrayLike, + FloatingOrNDArray, + Integer, + Literal, + NDArray, + Number, + Optional, + Tuple, + Type, + TypeExtrapolator, + TypeInterpolator, + Union, + cast, +) +from colour.utilities import ( + as_float_array, + attest, + fill_nan, + full, + is_pandas_installed, + optional, + required, + runtime_warning, + tsplit, + tstack, + validate_method, +) +from colour.utilities.documentation import is_documentation_building + +if is_pandas_installed(): + from pandas import Series +else: # pragma: no cover + from unittest import mock + + Series = mock.MagicMock() + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "Signal", +] class Signal(AbstractContinuousFunction): """ - Defines the base class for continuous signal. + Define the base class for continuous signal. The class implements the :meth:`Signal.function` method so that evaluating - the function for any independent domain :math:`x \\in\\mathbb{R}` variable - returns a corresponding range :math:`y \\in\\mathbb{R}` variable. + the function for any independent domain variable :math:`x \\in\\mathbb{R}` + returns a corresponding range variable :math:`y \\in\\mathbb{R}`. It adopts an interpolating function encapsulated inside an extrapolating function. The resulting function independent domain, stored as discrete - values in the :attr:`colour.continuous.Signal.domain` attribute corresponds + values in the :attr:`colour.continuous.Signal.domain` property corresponds with the function dependent and already known range stored in the - :attr:`colour.continuous.Signal.range` attribute. + :attr:`colour.continuous.Signal.range` property. .. important:: @@ -66,29 +102,28 @@ class Signal(AbstractContinuousFunction): Parameters ---------- - data : Series or Signal or array_like or dict_like, optional + data Data to be stored in the continuous signal. - domain : array_like, optional + domain Values to initialise the :attr:`colour.continuous.Signal.domain` attribute with. If both ``data`` and ``domain`` arguments are defined, the latter with be used to initialise the - :attr:`colour.continuous.Signal.domain` attribute. + :attr:`colour.continuous.Signal.domain` property. Other Parameters ---------------- - name : unicode, optional - Continuous signal name. - dtype : type, optional - **{np.float16, np.float32, np.float64, np.float128}**, + dtype Floating point data type. - interpolator : object, optional - Interpolator class type to use as interpolating function. - interpolator_kwargs : dict_like, optional - Arguments to use when instantiating the interpolating function. - extrapolator : object, optional + extrapolator Extrapolator class type to use as extrapolating function. - extrapolator_kwargs : dict_like, optional + extrapolator_kwargs Arguments to use when instantiating the extrapolating function. + interpolator + Interpolator class type to use as interpolating function. + interpolator_kwargs + Arguments to use when instantiating the interpolating function. + name + Continuous signal name. Attributes ---------- @@ -163,7 +198,7 @@ class Signal(AbstractContinuousFunction): [ 900. 90.] [ 1000. 100.]] - Instantiation with a *Pandas* `Series`: + Instantiation with a *Pandas* :class:`pandas.Series`: >>> if is_pandas_installed(): ... from pandas import Series @@ -207,205 +242,196 @@ class Signal(AbstractContinuousFunction): array([ 0. , 0.4794253..., 0.8414709...]) """ - def __init__(self, data=None, domain=None, **kwargs): - super(Signal, self).__init__(kwargs.get('name')) - - self._dtype = None - self._domain = None - self._range = None - self._interpolator = KernelInterpolator - self._interpolator_kwargs = {} - self._extrapolator = Extrapolator - self._extrapolator_kwargs = { - 'method': 'Constant', - 'left': np.nan, - 'right': np.nan + def __init__( + self, + data: Optional[Union[ArrayLike, dict, Series, Signal]] = None, + domain: Optional[ArrayLike] = None, + **kwargs: Any, + ): + super().__init__(kwargs.get("name")) + + self._dtype: Type[DTypeFloating] = DEFAULT_FLOAT_DTYPE + self._domain: NDArray = np.array([]) + self._range: NDArray = np.array([]) + self._interpolator: Type[TypeInterpolator] = KernelInterpolator + self._interpolator_kwargs: Dict = {} + self._extrapolator: Type[TypeExtrapolator] = Extrapolator + self._extrapolator_kwargs: Dict = { + "method": "Constant", + "left": np.nan, + "right": np.nan, } self.domain, self.range = self.signal_unpack_data(data, domain) - self.dtype = kwargs.get('dtype', DEFAULT_FLOAT_DTYPE) + self.dtype = kwargs.get("dtype", self._dtype) - self.interpolator = kwargs.get('interpolator') - self.interpolator_kwargs = kwargs.get('interpolator_kwargs') - self.extrapolator = kwargs.get('extrapolator') - self.extrapolator_kwargs = kwargs.get('extrapolator_kwargs') + self.interpolator = kwargs.get("interpolator", self._interpolator) + self.interpolator_kwargs = kwargs.get( + "interpolator_kwargs", self._interpolator_kwargs + ) + self.extrapolator = kwargs.get("extrapolator", self._extrapolator) + self.extrapolator_kwargs = kwargs.get( + "extrapolator_kwargs", self._extrapolator_kwargs + ) self._create_function() @property - def dtype(self): + def dtype(self) -> Type[DTypeFloating]: """ Getter and setter property for the continuous signal dtype. Parameters ---------- - value : type + value Value to set the continuous signal dtype with. Returns ------- - type + DTypeFloating Continuous signal dtype. """ return self._dtype @dtype.setter - def dtype(self, value): - """ - Setter for **self.dtype** property. - """ - - if value is not None: + def dtype(self, value: Type[DTypeFloating]): + """Setter for the **self.dtype** property.""" - assert value in np.sctypes['float'], ( - '"dtype" must be one of the following types: {0}'.format( - np.sctypes['float'])) + attest( + value in np.sctypes["float"], + f'"dtype" must be one of the following types: ' + f"{np.sctypes['float']}", + ) - self._dtype = value + self._dtype = value - # The following self-assignments are written as intended and - # triggers the rebuild of the underlying function. - self.domain = self.domain - self.range = self.range + # The following self-assignments are written as intended and + # triggers the rebuild of the underlying function. + self.domain = self.domain + self.range = self.range @property - def domain(self): + def domain(self) -> NDArray: """ Getter and setter property for the continuous signal independent - domain :math:`x` variable. + domain variable :math:`x`. Parameters ---------- - value : array_like + value Value to set the continuous signal independent domain - :math:`x` variable with. + variable :math:`x` with. Returns ------- - ndarray - Continuous signal independent domain :math:`x` variable. + :class:`numpy.ndarray` + Continuous signal independent domain variable :math:`x`. """ return np.copy(self._domain) @domain.setter - def domain(self, value): - """ - Setter for the **self.domain** property. - """ + def domain(self, value: ArrayLike): + """Setter for the **self.domain** property.""" - if value is not None: - if np.asarray(value).dtype != object: - if not np.all(np.isfinite(value)): - runtime_warning( - '"{0}" new "domain" variable is not finite: {1}, ' - 'unpredictable results may occur!'.format( - self.name, value)) + value = as_float_array(value, self.dtype) - value = np.copy(value).astype(self.dtype) + if not np.all(np.isfinite(value)): + runtime_warning( + f'"{self.name}" new "domain" variable is not finite: {value}, ' + f"unpredictable results may occur!" + ) - if self._range is not None: - if value.size != self._range.size: - runtime_warning( - '"{0}" new "domain" and current "range" variables ' - 'have different size, "range" variable will be ' - 'resized to "domain" variable shape!'.format( - self.name)) - self._range = np.resize(self._range, value.shape) + if value.size != self._range.size: + self._range = np.resize(self._range, value.shape) - self._domain = value - self._create_function() + self._domain = value + self._create_function() @property - def range(self): + def range(self) -> NDArray: """ Getter and setter property for the continuous signal corresponding - range :math:`y` variable. + range variable :math:`y`. Parameters ---------- - value : array_like + value Value to set the continuous signal corresponding range :math:`y` variable with. Returns ------- - ndarray - Continuous signal corresponding range :math:`y` variable. + :class:`numpy.ndarray` + Continuous signal corresponding range variable :math:`y`. """ return np.copy(self._range) @range.setter - def range(self, value): - """ - Setter for the **self.range** property. - """ + def range(self, value: ArrayLike): + """Setter for the **self.range** property.""" - if value is not None: - if np.asarray(value).dtype != object: - if not np.all(np.isfinite(value)): - runtime_warning( - '"{0}" new "range" variable is not finite: {1}, ' - 'unpredictable results may occur!'.format( - self.name, value)) + value = as_float_array(value, self.dtype) - value = np.copy(value).astype(self.dtype) + if not np.all(np.isfinite(value)): + runtime_warning( + f'"{self.name}" new "range" variable is not finite: {value}, ' + f"unpredictable results may occur!" + ) - if self._domain is not None: - assert value.size == self._domain.size, ( - '"domain" and "range" variables must have same size!') + attest( + value.size == self._domain.size, + '"domain" and "range" variables must have same size!', + ) - self._range = value - self._create_function() + self._range = value + self._create_function() @property - def interpolator(self): + def interpolator(self) -> Type[TypeInterpolator]: """ Getter and setter property for the continuous signal interpolator type. Parameters ---------- - value : type + value Value to set the continuous signal interpolator type with. Returns ------- - type + Type[TypeInterpolator] Continuous signal interpolator type. """ return self._interpolator @interpolator.setter - def interpolator(self, value): - """ - Setter for the **self.interpolator** property. - """ + def interpolator(self, value: Type[TypeInterpolator]): + """Setter for the **self.interpolator** property.""" - if value is not None: - # TODO: Check for interpolator capabilities. - self._interpolator = value - self._create_function() + # TODO: Check for interpolator compatibility. + self._interpolator = value + self._create_function() @property - def interpolator_kwargs(self): + def interpolator_kwargs(self) -> Dict: """ Getter and setter property for the continuous signal interpolator instantiation time arguments. Parameters ---------- - value : dict + value Value to set the continuous signal interpolator instantiation time arguments to. Returns ------- - dict + :class:`dict` Continuous signal interpolator instantiation time arguments. """ @@ -413,64 +439,59 @@ def interpolator_kwargs(self): return self._interpolator_kwargs @interpolator_kwargs.setter - def interpolator_kwargs(self, value): - """ - Setter for the **self.interpolator_kwargs** property. - """ + def interpolator_kwargs(self, value: dict): + """Setter for the **self.interpolator_kwargs** property.""" - if value is not None: - assert isinstance(value, (dict, OrderedDict)), ( - '"{0}" attribute: "{1}" type is not "dict" or "OrderedDict"!' - ).format('interpolator_kwargs', value) + attest( + isinstance(value, dict), + f'"interpolator_kwargs" property: "{value}" type is not "dict"!', + ) - self._interpolator_kwargs = value - self._create_function() + self._interpolator_kwargs = value + self._create_function() @property - def extrapolator(self): + def extrapolator(self) -> Type[TypeExtrapolator]: """ Getter and setter property for the continuous signal extrapolator type. Parameters ---------- - value : type + value Value to set the continuous signal extrapolator type with. Returns ------- - type + Type[TypeExtrapolator] Continuous signal extrapolator type. """ return self._extrapolator @extrapolator.setter - def extrapolator(self, value): - """ - Setter for the **self.extrapolator** property. - """ + def extrapolator(self, value: Type[TypeExtrapolator]): + """Setter for the **self.extrapolator** property.""" - if value is not None: - # TODO: Check for extrapolator capabilities. - self._extrapolator = value - self._create_function() + # TODO: Check for extrapolator compatibility. + self._extrapolator = value + self._create_function() @property - def extrapolator_kwargs(self): + def extrapolator_kwargs(self) -> Dict: """ Getter and setter property for the continuous signal extrapolator instantiation time arguments. Parameters ---------- - value : dict + value Value to set the continuous signal extrapolator instantiation time arguments to. Returns ------- - dict + :class:`dict` Continuous signal extrapolator instantiation time arguments. """ @@ -478,39 +499,37 @@ def extrapolator_kwargs(self): return self._extrapolator_kwargs @extrapolator_kwargs.setter - def extrapolator_kwargs(self, value): - """ - Setter for the **self.extrapolator_kwargs** property. - """ + def extrapolator_kwargs(self, value: dict): + """Setter for the **self.extrapolator_kwargs** property.""" - if value is not None: - assert isinstance(value, (dict, OrderedDict)), ( - '"{0}" attribute: "{1}" type is not "dict" or "OrderedDict"!'. - format('extrapolator_kwargs', value)) + attest( + isinstance(value, dict), + f'"extrapolator_kwargs" property: "{value}" type is not "dict"!', + ) - self._extrapolator_kwargs = value - self._create_function() + self._extrapolator_kwargs = value + self._create_function() @property - def function(self): + def function(self) -> Callable: """ Getter property for the continuous signal callable. Returns ------- - callable + Callable Continuous signal callable. """ return self._function - def __str__(self): + def __str__(self) -> str: """ - Returns a formatted string representation of the continuous signal. + Return a formatted string representation of the continuous signal. Returns ------- - unicode + :class:`str` Formatted string representation. Examples @@ -532,15 +551,15 @@ def __str__(self): try: return str(tstack([self.domain, self.range])) except TypeError: - return super(Signal, self).__str__() + return super().__str__() - def __repr__(self): + def __repr__(self) -> str: """ - Returns an evaluable string representation of the continuous signal. + Return an evaluable string representation of the continuous signal. Returns ------- - unicode + :class:`str` Evaluable string representation. Examples @@ -563,62 +582,70 @@ def __repr__(self): extrapolator_kwargs={...}) """ + if is_documentation_building(): # pragma: no cover + return f"{self.__class__.__name__}(name='{self.name}', ...)" + try: representation = repr(tstack([self.domain, self.range])) - representation = representation.replace('array', - self.__class__.__name__) representation = representation.replace( - ' [', - '{0}['.format(' ' * (len(self.__class__.__name__) + 2))) - representation = ('{0},\n' - '{1}interpolator={2},\n' - '{1}interpolator_kwargs={3},\n' - '{1}extrapolator={4},\n' - '{1}extrapolator_kwargs={5})').format( - representation[:-1], - ' ' * (len(self.__class__.__name__) + 1), - self.interpolator.__name__, - repr(self.interpolator_kwargs), - self.extrapolator.__name__, - repr(self.extrapolator_kwargs)) + "array", self.__class__.__name__ + ) + representation = representation.replace( + " [", + f"{' ' * (len(self.__class__.__name__) + 2)}[", + ) + indentation = " " * (len(self.__class__.__name__) + 1) + representation = ( + f"{representation[:-1]},\n" + f"{indentation}interpolator={self.interpolator.__name__},\n" + f"{indentation}interpolator_kwargs=" + f"{repr(self.interpolator_kwargs)},\n" + f"{indentation}extrapolator={self.extrapolator.__name__},\n" + f"{indentation}extrapolator_kwargs=" + f"{repr(self.extrapolator_kwargs)})" + ) return representation except TypeError: - return super(Signal, self).__repr__() + return super().__repr__() - def __hash__(self): + def __hash__(self) -> Integer: """ - Returns the abstract continuous function hash. + Return the abstract continuous function hash. Returns ------- - int + :class:`numpy.integer` Object hash. """ - return hash(( - self.domain.tobytes(), - self.range.tobytes(), - self.interpolator.__name__, - repr(self.interpolator_kwargs), - self.extrapolator.__name__, - repr(self.extrapolator_kwargs), - )) - - def __getitem__(self, x): + return hash( + ( + self.domain.tobytes(), + self.range.tobytes(), + self.interpolator.__name__, + repr(self.interpolator_kwargs), + self.extrapolator.__name__, + repr(self.extrapolator_kwargs), + ) + ) + + def __getitem__( + self, x: Union[FloatingOrArrayLike, slice] + ) -> FloatingOrNDArray: """ - Returns the corresponding range :math:`y` variable for independent - domain :math:`x` variable. + Return the corresponding range variable :math:`y` for independent + domain variable :math:`x`. Parameters ---------- - x : numeric, array_like or slice - Independent domain :math:`x` variable. + x + Independent domain variable :math:`x`. Returns ------- - numeric or ndarray - math:`y` range value. + :class:`numpy.floating` or :class:`numpy.ndarray` + Variable :math:`y` range value. Examples -------- @@ -651,17 +678,19 @@ def __getitem__(self, x): else: return self._function(x) - def __setitem__(self, x, y): + def __setitem__( + self, x: Union[FloatingOrArrayLike, slice], y: FloatingOrArrayLike + ): """ - Sets the corresponding range :math:`y` variable for independent domain - :math:`x` variable. + Set the corresponding range variable :math:`y` for independent domain + variable :math:`x`. Parameters ---------- - x : numeric, array_like or slice - Independent domain :math:`x` variable. - y : numeric or ndarray - Corresponding range :math:`y` variable. + x + Independent domain variable :math:`x`. + y + Corresponding range variable :math:`y`. Examples -------- @@ -741,20 +770,20 @@ def __setitem__(self, x, y): self._create_function() - def __contains__(self, x): + def __contains__(self, x: Union[FloatingOrArrayLike, slice]) -> bool: """ - Returns whether the continuous signal contains given independent domain - :math:`x` variable. + Return whether the continuous signal contains given independent domain + variable :math:`x`. Parameters ---------- - x : numeric, array_like or slice - Independent domain :math:`x` variable. + x + Independent domain variable :math:`x`. Returns ------- - bool - Is :math:`x` domain value contained. + :class:`bool` + Whether :math:`x` domain value is contained. Examples -------- @@ -768,27 +797,31 @@ def __contains__(self, x): False """ - return np.all( - np.where( - np.logical_and(x >= np.min(self._domain), - x <= np.max(self._domain)), - True, - False, - )) - - def __eq__(self, other): + return bool( + np.all( + np.where( + np.logical_and( + x >= np.min(self._domain), x <= np.max(self._domain) + ), + True, + False, + ) + ) + ) + + def __eq__(self, other: Any) -> bool: """ - Returns whether the continuous signal is equal to given other object. + Return whether the continuous signal is equal to given other object. Parameters ---------- - other : object + other Object to test whether it is equal to the continuous signal. Returns ------- - bool - Is given object equal to the continuous signal. + :class:`bool` + Whether given object is equal to the continuous signal. Examples -------- @@ -810,32 +843,33 @@ def __eq__(self, other): """ if isinstance(other, Signal): - if all([ + return all( + [ np.array_equal(self._domain, other.domain), np.array_equal(self._range, other.range), self._interpolator is other.interpolator, self._interpolator_kwargs == other.interpolator_kwargs, self._extrapolator is other.extrapolator, - self._extrapolator_kwargs == other.extrapolator_kwargs - ]): - return True - - return False + self._extrapolator_kwargs == other.extrapolator_kwargs, + ] + ) + else: + return False - def __ne__(self, other): + def __ne__(self, other: Any) -> bool: """ - Returns whether the continuous signal is not equal to given other + Return whether the continuous signal is not equal to given other object. Parameters ---------- - other : object + other Object to test whether it is not equal to the continuous signal. Returns ------- - bool - Is given object not equal to the continuous signal. + :class:`bool` + Whether given object is not equal to the continuous signal. Examples -------- @@ -859,56 +893,62 @@ def __ne__(self, other): return not (self == other) def _create_function(self): - """ - Creates the continuous signal underlying function. - """ + """Create the continuous signal underlying function.""" - if self._domain is not None and self._range is not None: + if self._domain.size != 0 and self._range.size != 0: self._function = self._extrapolator( - self._interpolator(self.domain, self.range, - **self._interpolator_kwargs), - **self._extrapolator_kwargs) + self._interpolator( + self.domain, self.range, **self._interpolator_kwargs + ), + **self._extrapolator_kwargs, + ) else: - def _undefined_function(*args, **kwargs): + def _undefined_function(*args: Any, **kwargs: Any): """ - Raises a :class:`RuntimeError` exception. + Raise a :class:`ValueError` exception. Other Parameters ---------------- - \\*args : list, optional + args Arguments. - \\**kwargs : dict, optional + kwargs Keywords arguments. Raises ------ - RuntimeError + ValueError """ - raise RuntimeError( - 'Underlying signal interpolator function does not exists, ' - 'please ensure you defined both ' - '"domain" and "range" variables!') + raise ValueError( + "Underlying signal interpolator function does not exists, " + "please ensure you defined both " + '"domain" and "range" variables!' + ) self._function = _undefined_function - def _fill_domain_nan(self, method='Interpolation', default=0): + def _fill_domain_nan( + self, + method: Union[ + Literal["Constant", "Interpolation"], str + ] = "Interpolation", + default: Number = 0, + ): """ - Fill NaNs in independent domain :math:`x` variable using given method. + Fill NaNs in independent domain variable :math:`x` using given method. Parameters ---------- - method : unicode, optional - **{'Interpolation', 'Constant'}**, + method *Interpolation* method linearly interpolates through the NaNs, *Constant* method replaces NaNs with ``default``. - default : numeric, optional + default Value to use with the *Constant* method. Returns ------- - Signal + :class:`colour.continuous.Signal` NaNs filled continuous signal independent domain :math:`x` variable. """ @@ -916,22 +956,27 @@ def _fill_domain_nan(self, method='Interpolation', default=0): self._domain = fill_nan(self._domain, method, default) self._create_function() - def _fill_range_nan(self, method='Interpolation', default=0): + def _fill_range_nan( + self, + method: Union[ + Literal["Constant", "Interpolation"], str + ] = "Interpolation", + default: Number = 0, + ): """ - Fill NaNs in corresponding range :math:`y` variable using given method. + Fill NaNs in corresponding range variable :math:`y` using given method. Parameters ---------- - method : unicode, optional - **{'Interpolation', 'Constant'}**, + method *Interpolation* method linearly interpolates through the NaNs, *Constant* method replaces NaNs with ``default``. - default : numeric, optional + default Value to use with the *Constant* method. Returns ------- - Signal + :class:`colour.continuous.Signal` NaNs filled continuous signal i corresponding range :math:`y` variable. """ @@ -939,23 +984,28 @@ def _fill_range_nan(self, method='Interpolation', default=0): self._range = fill_nan(self._range, method, default) self._create_function() - def arithmetical_operation(self, a, operation, in_place=False): + def arithmetical_operation( + self, + a: Union[FloatingOrArrayLike, AbstractContinuousFunction], + operation: Literal["+", "-", "*", "/", "**"], + in_place: Boolean = False, + ) -> AbstractContinuousFunction: """ - Performs given arithmetical operation with :math:`a` operand, the + Perform given arithmetical operation with operand :math:`a`, the operation can be either performed on a copy or in-place. Parameters ---------- - a : numeric or ndarray or Signal - Operand. - operation : object + a + Operand :math:`a`. + operation Operation to perform. - in_place : bool, optional + in_place Operation happens in place. Returns ------- - Signal + :class:`colour.continuous.Signal` Continuous signal. Examples @@ -987,7 +1037,7 @@ def arithmetical_operation(self, a, operation, in_place=False): [ 8. 100.] [ 9. 110.]] - Adding an *array_like* variable: + Adding an `ArrayLike` variable: >>> a = np.linspace(10, 100, 10) >>> print(signal_1.arithmetical_operation(a, '+', True)) @@ -1018,17 +1068,17 @@ def arithmetical_operation(self, a, operation, in_place=False): [ 9. 310.]] """ - operation, ioperator = { - '+': (add, iadd), - '-': (sub, isub), - '*': (mul, imul), - '/': (div, idiv), - '**': (pow, ipow) + operator, ioperator = { + "+": (add, iadd), + "-": (sub, isub), + "*": (mul, imul), + "/": (truediv, itruediv), + "**": (pow, ipow), }[operation] if in_place: if isinstance(a, Signal): - self[self._domain] = operation(self._range, a[self._domain]) + self[self._domain] = operator(self._range, a[self._domain]) exclusive_or = np.setxor1d(self._domain, a.domain) self[exclusive_or] = full(exclusive_or.shape, np.nan) else: @@ -1041,28 +1091,31 @@ def arithmetical_operation(self, a, operation, in_place=False): return copy @staticmethod - def signal_unpack_data(data=None, domain=None, dtype=None): + def signal_unpack_data( + data=Optional[Union[ArrayLike, dict, Series, "Signal"]], + domain: Optional[ArrayLike] = None, + dtype: Optional[Type[DTypeFloating]] = None, + ) -> Tuple: """ Unpack given data for continuous signal instantiation. Parameters ---------- - data : Series or Signal or array_like or dict_like, optional + data Data to unpack for continuous signal instantiation. - domain : array_like, optional + domain Values to initialise the :attr:`colour.continuous.Signal.domain` attribute with. If both ``data`` and ``domain`` arguments are defined, the latter will be used to initialise the - :attr:`colour.continuous.Signal.domain` attribute. - dtype : type, optional - **{np.float16, np.float32, np.float64, np.float128}**, + :attr:`colour.continuous.Signal.domain` property. + dtype Floating point data type. Returns ------- - tuple - Independent domain :math:`x` variable and corresponding range - :math:`y` variable unpacked for continuous signal instantiation. + :class:`tuple` + Independent domain variable :math:`x` and corresponding range + variable :math:`y` unpacked for continuous signal instantiation. Examples -------- @@ -1093,7 +1146,7 @@ def signal_unpack_data(data=None, domain=None, dtype=None): >>> print(range_) [ 10. 20. 30. 40. 50. 60. 70. 80. 90. 100.] - Unpacking using a *Pandas* `Series`: + Unpacking using a *Pandas* :class:`pandas.Series`: >>> if is_pandas_installed(): ... from pandas import Series @@ -1115,55 +1168,69 @@ def signal_unpack_data(data=None, domain=None, dtype=None): [ 10. 20. 30. 40. 50. 60. 70. 80. 90. 100.] """ - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) + + domain_unpacked: NDArray = np.array([]) + range_unpacked: NDArray = np.array([]) - domain_u, range_u = None, None if isinstance(data, Signal): - domain_u = data.domain - range_u = data.range - elif (issubclass(type(data), Sequence) or - isinstance(data, (tuple, list, np.ndarray, Iterator))): - data = tsplit(list(data) if isinstance(data, Iterator) else data) - assert data.ndim == 1, 'User "data" must be 1-dimensional!' - domain_u, range_u = np.arange(0, data.size, dtype=dtype), data - elif (issubclass(type(data), Mapping) or - isinstance(data, (dict, OrderedDict))): - domain_u, range_u = tsplit(sorted(data.items())) + domain_unpacked = data.domain + range_unpacked = data.range + elif issubclass(type(data), Sequence) or isinstance( + data, (tuple, list, np.ndarray, Iterator, ValuesView) + ): + data_array = tsplit(list(data)) + + attest(data_array.ndim == 1, 'User "data" must be 1-dimensional!') + + domain_unpacked, range_unpacked = ( + np.arange(0, data_array.size, dtype=dtype), + data_array, + ) + elif issubclass(type(data), Mapping) or isinstance(data, dict): + domain_unpacked, range_unpacked = tsplit(sorted(data.items())) elif is_pandas_installed(): - from pandas import Series - if isinstance(data, Series): - domain_u = data.index.values - range_u = data.values + domain_unpacked = data.index.values + range_unpacked = data.values + + if domain is not None: + domain_array = as_float_array(list(domain), dtype) # type: ignore[arg-type] + + attest( + len(domain_array) == len(range_unpacked), + 'User "domain" length is not compatible with unpacked "range"!', + ) - if domain is not None and range_u is not None: - assert len(domain) == len(range_u), ( - 'User "domain" is not compatible with unpacked range!') - domain_u = as_array(domain, dtype) + domain_unpacked = domain_array - if range_u is not None: - range_u = as_array(range_u, dtype) + if range_unpacked is not None: + range_unpacked = as_float_array(range_unpacked, dtype) - return domain_u, range_u + return domain_unpacked, range_unpacked - def fill_nan(self, method='Interpolation', default=0): + def fill_nan( + self, + method: Union[ + Literal["Constant", "Interpolation"], str + ] = "Interpolation", + default: Number = 0, + ) -> AbstractContinuousFunction: """ - Fill NaNs in independent domain :math:`x` variable and corresponding - range :math:`y` variable using given method. + Fill NaNs in independent domain variable :math:`x` and corresponding + range variable :math:`y` using given method. Parameters ---------- - method : unicode, optional - **{'Interpolation', 'Constant'}**, + method *Interpolation* method linearly interpolates through the NaNs, *Constant* method replaces NaNs with ``default``. - default : numeric, optional + default Value to use with the *Constant* method. Returns ------- - Signal + :class:`colour.continuous.Signal` NaNs filled continuous signal. Examples @@ -1207,21 +1274,24 @@ def fill_nan(self, method='Interpolation', default=0): [ 9. 100.]] """ + method = validate_method(method, ["Interpolation", "Constant"]) + self._fill_domain_nan(method, default) self._fill_range_nan(method, default) return self - @required('Pandas') - def to_series(self): + @required("Pandas") + def to_series(self) -> Series: """ - Converts the continuous signal to a *Pandas* :class:`Series` class - instance. + Convert the continuous signal to a *Pandas* :class:`pandas.Series` + class instance. Returns ------- - Series - Continuous signal as a *Pandas* :class:`Series` class instance. + :class:`pandas.Series` + Continuous signal as a *Pandas*:class:`pandas.Series` class + instance. Examples -------- @@ -1242,49 +1312,4 @@ def to_series(self): Name: Signal (...), dtype: float64 """ - from pandas import Series - return Series(data=self._range, index=self._domain, name=self.name) - - # ------------------------------------------------------------------------# - # --- API Changes and Deprecation Management ---# - # ------------------------------------------------------------------------# - @property - def interpolator_args(self): - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('Signal.interpolator_args', - 'Signal.interpolator_kwargs'))) - - return self.interpolator_kwargs - - @interpolator_args.setter - def interpolator_args(self, value): - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('Signal.interpolator_args', - 'Signal.interpolator_kwargs'))) - - self.interpolator_kwargs = value - - @property - def extrapolator_args(self): - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('Signal.extrapolator_args', - 'Signal.extrapolator_kwargs'))) - - return self.extrapolator_kwargs - - @extrapolator_args.setter - def extrapolator_args(self, value): - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('Signal.extrapolator_args', - 'Signal.extrapolator_kwargs'))) - - self.extrapolator_kwargs = value diff --git a/colour/continuous/tests/__init__.py b/colour/continuous/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/continuous/tests/__init__.py +++ b/colour/continuous/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/continuous/tests/test_abstract.py b/colour/continuous/tests/test_abstract.py index d1fc36f730..9333e1aa98 100644 --- a/colour/continuous/tests/test_abstract.py +++ b/colour/continuous/tests/test_abstract.py @@ -1,58 +1,78 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.continuous.abstract` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.continuous.abstract` module.""" import unittest from colour.continuous import AbstractContinuousFunction -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestAbstractContinuousFunction'] +__all__ = [ + "TestAbstractContinuousFunction", +] class TestAbstractContinuousFunction(unittest.TestCase): """ - Defines :class:`colour.continuous.abstract.AbstractContinuousFunction` + Define :class:`colour.continuous.abstract.AbstractContinuousFunction` class unit tests methods. """ def test_required_attributes(self): - """ - Tests presence of required attributes. - """ + """Test the presence of required attributes.""" - required_attributes = ('name', 'domain', 'range', 'interpolator', - 'interpolator_kwargs', 'extrapolator', - 'extrapolator_kwargs', 'function') + required_attributes = ( + "name", + "domain", + "range", + "interpolator", + "interpolator_kwargs", + "extrapolator", + "extrapolator_kwargs", + "function", + ) for attribute in required_attributes: self.assertIn(attribute, dir(AbstractContinuousFunction)) def test_required_methods(self): - """ - Tests presence of required methods. - """ - - required_methods = ('__init__', '__str__', '__repr__', '__hash__', - '__getitem__', '__setitem__', '__contains__', - '__len__', '__eq__', '__ne__', '__iadd__', - '__add__', '__isub__', '__sub__', '__imul__', - '__mul__', '__idiv__', '__div__', '__ipow__', - '__pow__', 'arithmetical_operation', 'fill_nan', - 'domain_distance', 'is_uniform', 'copy') + """Test the presence of required methods.""" + + required_methods = ( + "__init__", + "__str__", + "__repr__", + "__hash__", + "__getitem__", + "__setitem__", + "__contains__", + "__len__", + "__eq__", + "__ne__", + "__iadd__", + "__add__", + "__isub__", + "__sub__", + "__imul__", + "__mul__", + "__idiv__", + "__div__", + "__ipow__", + "__pow__", + "arithmetical_operation", + "fill_nan", + "domain_distance", + "is_uniform", + "copy", + ) for method in required_methods: self.assertIn(method, dir(AbstractContinuousFunction)) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/continuous/tests/test_multi_signal.py b/colour/continuous/tests/test_multi_signal.py index 46aaf98366..0f3762f989 100644 --- a/colour/continuous/tests/test_multi_signal.py +++ b/colour/continuous/tests/test_multi_signal.py @@ -1,43 +1,45 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.continuous.multi_signals` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.continuous.multi_signals` module.""" import numpy as np -import operator import unittest import re import textwrap -from six import string_types -from colour.algebra import (CubicSplineInterpolator, Extrapolator, - KernelInterpolator) +from colour.algebra import ( + CubicSplineInterpolator, + Extrapolator, + KernelInterpolator, +) from colour.constants import DEFAULT_FLOAT_DTYPE from colour.continuous import MultiSignals, Signal -from colour.utilities import is_pandas_installed, tsplit, tstack - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['TestMultiSignals'] +from colour.utilities import ( + ColourRuntimeWarning, + attest, + is_pandas_installed, + tsplit, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestMultiSignals", +] class TestMultiSignals(unittest.TestCase): """ - Defines :class:`colour.continuous.multi_signals.MultiSignals` class unit + Define :class:`colour.continuous.multi_signals.MultiSignals` class unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._range_1 = np.linspace(10, 100, 10) self._range_2 = tstack([self._range_1] * 3) + np.array([0, 10, 20]) @@ -47,35 +49,51 @@ def setUp(self): self._multi_signals = MultiSignals(self._range_2) def test_required_attributes(self): - """ - Tests presence of required attributes. - """ - - required_attributes = ('dtype', 'domain', 'range', 'interpolator', - 'interpolator_kwargs', 'extrapolator', - 'extrapolator_kwargs', 'function', 'signals', - 'labels', 'signal_type') + """Test the presence of required attributes.""" + + required_attributes = ( + "dtype", + "domain", + "range", + "interpolator", + "interpolator_kwargs", + "extrapolator", + "extrapolator_kwargs", + "function", + "signals", + "labels", + "signal_type", + ) for attribute in required_attributes: self.assertIn(attribute, dir(MultiSignals)) def test_required_methods(self): - """ - Tests presence of required methods. - """ - - required_methods = ('__init__', '__str__', '__repr__', '__hash__', - '__getitem__', '__setitem__', '__contains__', - '__eq__', '__ne__', 'arithmetical_operation', - 'multi_signals_unpack_data', 'fill_nan', - 'domain_distance', 'to_dataframe') + """Test the presence of required methods.""" + + required_methods = ( + "__init__", + "__str__", + "__repr__", + "__hash__", + "__getitem__", + "__setitem__", + "__contains__", + "__eq__", + "__ne__", + "arithmetical_operation", + "multi_signals_unpack_data", + "fill_nan", + "domain_distance", + "to_dataframe", + ) for method in required_methods: self.assertIn(method, dir(MultiSignals)) def test_dtype(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.dtype` + Test :func:`colour.continuous.multi_signals.MultiSignals.dtype` property. """ @@ -87,7 +105,7 @@ def test_dtype(self): def test_domain(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.domain` + Test :func:`colour.continuous.multi_signals.MultiSignals.domain` property. """ @@ -95,29 +113,39 @@ def test_domain(self): np.testing.assert_almost_equal( multi_signals[np.array([0, 1, 2])], - np.array([[10.0, 20.0, 30.0], [20.0, 30.0, 40.0], - [30.0, 40.0, 50.0]]), - decimal=7) + np.array( + [[10.0, 20.0, 30.0], [20.0, 30.0, 40.0], [30.0, 40.0, 50.0]] + ), + decimal=7, + ) multi_signals.domain = self._domain_1 * 10 - np.testing.assert_array_equal(multi_signals.domain, - self._domain_1 * 10) + np.testing.assert_array_equal( + multi_signals.domain, self._domain_1 * 10 + ) np.testing.assert_almost_equal( multi_signals[np.array([0, 1, 2]) * 10], - np.array([[10.0, 20.0, 30.0], [20.0, 30.0, 40.0], - [30.0, 40.0, 50.0]]), - decimal=7) + np.array( + [[10.0, 20.0, 30.0], [20.0, 30.0, 40.0], [30.0, 40.0, 50.0]] + ), + decimal=7, + ) - # TODO: Use "assertWarns" when dropping Python 2.7. domain = np.linspace(0, 1, 10) domain[0] = -np.inf - multi_signals.domain = domain + + def assert_warns(): + """Help to test the runtime warning.""" + + multi_signals.domain = domain + + self.assertWarns(ColourRuntimeWarning, assert_warns) def test_range(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.range` + Test :func:`colour.continuous.multi_signals.MultiSignals.range` property. """ @@ -125,20 +153,26 @@ def test_range(self): np.testing.assert_almost_equal( multi_signals[np.array([0, 1, 2])], - np.array([[10.0, 20.0, 30.0], [20.0, 30.0, 40.0], - [30.0, 40.0, 50.0]]), - decimal=7) + np.array( + [[10.0, 20.0, 30.0], [20.0, 30.0, 40.0], [30.0, 40.0, 50.0]] + ), + decimal=7, + ) multi_signals.range = self._range_1 * 10 - np.testing.assert_array_equal(multi_signals.range, - tstack([self._range_1] * 3) * 10) + np.testing.assert_array_equal( + multi_signals.range, tstack([self._range_1] * 3) * 10 + ) np.testing.assert_almost_equal( multi_signals[np.array([0, 1, 2])], - np.array([[10.0, 10.0, 10.0], [20.0, 20.0, 20.0], - [30.0, 30.0, 30.0]]) * 10, - decimal=7) + np.array( + [[10.0, 10.0, 10.0], [20.0, 20.0, 20.0], [30.0, 30.0, 30.0]] + ) + * 10, + decimal=7, + ) multi_signals.range = self._range_2 * 10 @@ -146,13 +180,16 @@ def test_range(self): np.testing.assert_almost_equal( multi_signals[np.array([0, 1, 2])], - np.array([[10.0, 20.0, 30.0], [20.0, 30.0, 40.0], - [30.0, 40.0, 50.0]]) * 10, - decimal=7) + np.array( + [[10.0, 20.0, 30.0], [20.0, 30.0, 40.0], [30.0, 40.0, 50.0]] + ) + * 10, + decimal=7, + ) def test_interpolator(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.interpolator` + Test :func:`colour.continuous.multi_signals.MultiSignals.interpolator` property. """ @@ -160,27 +197,37 @@ def test_interpolator(self): np.testing.assert_almost_equal( multi_signals[np.linspace(0, 5, 5)], - np.array([[10.00000000, 20.00000000, - 30.00000000], [22.83489024, 32.80460562, 42.77432100], - [34.80044921, 44.74343470, - 54.68642018], [47.55353925, 57.52325463, 67.49297001], - [60.00000000, 70.00000000, 80.00000000]]), - decimal=7) + np.array( + [ + [10.00000000, 20.00000000, 30.00000000], + [22.83489024, 32.80460562, 42.77432100], + [34.80044921, 44.74343470, 54.68642018], + [47.55353925, 57.52325463, 67.49297001], + [60.00000000, 70.00000000, 80.00000000], + ] + ), + decimal=7, + ) multi_signals.interpolator = CubicSplineInterpolator np.testing.assert_almost_equal( multi_signals[np.linspace(0, 5, 5)], - np.array([[10.00000000, 20.00000000, - 30.00000000], [22.50000000, 32.50000000, 42.50000000], - [35.00000000, 45.00000000, - 55.00000000], [47.50000000, 57.50000000, 67.50000000], - [60.00000000, 70.00000000, 80.00000000]]), - decimal=7) + np.array( + [ + [10.00000000, 20.00000000, 30.00000000], + [22.50000000, 32.50000000, 42.50000000], + [35.00000000, 45.00000000, 55.00000000], + [47.50000000, 57.50000000, 67.50000000], + [60.00000000, 70.00000000, 80.00000000], + ] + ), + decimal=7, + ) def test_interpolator_kwargs(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.\ + Test :func:`colour.continuous.multi_signals.MultiSignals.\ interpolator_kwargs` property. """ @@ -188,32 +235,40 @@ def test_interpolator_kwargs(self): np.testing.assert_almost_equal( multi_signals[np.linspace(0, 5, 5)], - np.array([[10.00000000, 20.00000000, - 30.00000000], [22.83489024, 32.80460562, 42.77432100], - [34.80044921, 44.74343470, - 54.68642018], [47.55353925, 57.52325463, 67.49297001], - [60.00000000, 70.00000000, 80.00000000]]), - decimal=7) + np.array( + [ + [10.00000000, 20.00000000, 30.00000000], + [22.83489024, 32.80460562, 42.77432100], + [34.80044921, 44.74343470, 54.68642018], + [47.55353925, 57.52325463, 67.49297001], + [60.00000000, 70.00000000, 80.00000000], + ] + ), + decimal=7, + ) multi_signals.interpolator_kwargs = { - 'window': 1, - 'kernel_kwargs': { - 'a': 1 - } + "window": 1, + "kernel_kwargs": {"a": 1}, } np.testing.assert_almost_equal( multi_signals[np.linspace(0, 5, 5)], - np.array([[10.00000000, 20.00000000, - 30.00000000], [18.91328761, 27.91961505, 36.92594248], - [28.36993142, 36.47562611, - 44.58132080], [44.13100443, 53.13733187, 62.14365930], - [60.00000000, 70.00000000, 80.00000000]]), - decimal=7) + np.array( + [ + [10.00000000, 20.00000000, 30.00000000], + [18.91328761, 27.91961505, 36.92594248], + [28.36993142, 36.47562611, 44.58132080], + [44.13100443, 53.13733187, 62.14365930], + [60.00000000, 70.00000000, 80.00000000], + ] + ), + decimal=7, + ) def test_extrapolator(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.extrapolator` + Test :func:`colour.continuous.multi_signals.MultiSignals.extrapolator` property. """ @@ -221,44 +276,45 @@ def test_extrapolator(self): def test_extrapolator_kwargs(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.\ + Test :func:`colour.continuous.multi_signals.MultiSignals.\ extrapolator_kwargs` property. """ multi_signals = self._multi_signals.copy() - assert np.all(np.isnan(multi_signals[np.array([-1000, 1000])])) + attest(np.all(np.isnan(multi_signals[np.array([-1000, 1000])]))) multi_signals.extrapolator_kwargs = { - 'method': 'Linear', + "method": "Linear", } np.testing.assert_almost_equal( multi_signals[np.array([-1000, 1000])], - np.array([[-9990.0, -9980.0, -9970.0], [10010.0, 10020.0, - 10030.0]]), - decimal=7) + np.array( + [[-9990.0, -9980.0, -9970.0], [10010.0, 10020.0, 10030.0]] + ), + decimal=7, + ) def test_function(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.function` + Test :func:`colour.continuous.multi_signals.MultiSignals.function` property. """ - assert hasattr(self._multi_signals.function, '__call__') + attest(hasattr(self._multi_signals.function, "__call__")) def test_raise_exception_function(self): """ - Tests :func:`colour.continuous.signal.multi_signals.MultiSignals` - property raised exception. + Test :func:`colour.continuous.signal.multi_signals.MultiSignals.\ +function` property raised exception. """ - self.assertRaises((RuntimeError, TypeError), - MultiSignals().function, 0) + self.assertRaises((ValueError, TypeError), MultiSignals().function, 0) def test_signals(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.signals` + Test :func:`colour.continuous.multi_signals.MultiSignals.signals` property. """ @@ -266,26 +322,27 @@ def test_signals(self): multi_signals.signals = self._range_1 np.testing.assert_array_equal(multi_signals.domain, self._domain_1) - np.testing.assert_array_equal(multi_signals.range, - self._range_1[:, np.newaxis]) + np.testing.assert_array_equal( + multi_signals.range, self._range_1[:, np.newaxis] + ) def test_labels(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.labels` + Test :func:`colour.continuous.multi_signals.MultiSignals.labels` property. """ - self.assertListEqual(self._multi_signals.labels, [0, 1, 2]) + self.assertListEqual(self._multi_signals.labels, ["0", "1", "2"]) multi_signals = self._multi_signals.copy() - multi_signals.labels = ['a', 'b', 'c'] + multi_signals.labels = ["a", "b", "c"] - self.assertListEqual(multi_signals.labels, ['a', 'b', 'c']) + self.assertListEqual(multi_signals.labels, ["a", "b", "c"]) def test_signal_type(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.signal_type` + Test :func:`colour.continuous.multi_signals.MultiSignals.signal_type` property. """ @@ -295,19 +352,21 @@ def test_signal_type(self): def test__init__(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.__init__` + Test :meth:`colour.continuous.multi_signals.MultiSignals.__init__` method. """ multi_signals = MultiSignals(self._range_1) np.testing.assert_array_equal(multi_signals.domain, self._domain_1) - np.testing.assert_array_equal(multi_signals.range, - self._range_1[:, np.newaxis]) + np.testing.assert_array_equal( + multi_signals.range, self._range_1[:, np.newaxis] + ) multi_signals = MultiSignals(self._range_1, self._domain_2) np.testing.assert_array_equal(multi_signals.domain, self._domain_2) - np.testing.assert_array_equal(multi_signals.range, - self._range_1[:, np.newaxis]) + np.testing.assert_array_equal( + multi_signals.range, self._range_1[:, np.newaxis] + ) multi_signals = MultiSignals(self._range_2, self._domain_2) np.testing.assert_array_equal(multi_signals.domain, self._domain_2) @@ -322,35 +381,36 @@ def test__init__(self): np.testing.assert_array_equal(multi_signals.range, self._range_2) class NotSignal(Signal): - """ - Not :class:`Signal` class. - """ + """Not :class:`Signal` class.""" pass multi_signals = MultiSignals(self._range_1, signal_type=NotSignal) - self.assertIsInstance(multi_signals.signals[0], NotSignal) + self.assertIsInstance(multi_signals.signals["0"], NotSignal) np.testing.assert_array_equal(multi_signals.domain, self._domain_1) - np.testing.assert_array_equal(multi_signals.range, - self._range_1[:, np.newaxis]) + np.testing.assert_array_equal( + multi_signals.range, self._range_1[:, np.newaxis] + ) if is_pandas_installed(): from pandas import DataFrame, Series multi_signals = MultiSignals( - Series(dict(zip(self._domain_2, self._range_1)))) + Series(dict(zip(self._domain_2, self._range_1))) + ) np.testing.assert_array_equal(multi_signals.domain, self._domain_2) - np.testing.assert_array_equal(multi_signals.range, - self._range_1[:, np.newaxis]) + np.testing.assert_array_equal( + multi_signals.range, self._range_1[:, np.newaxis] + ) - data = dict(zip(['a', 'b', 'c'], tsplit(self._range_2))) + data = dict(zip(["a", "b", "c"], tsplit(self._range_2))) multi_signals = MultiSignals(DataFrame(data, self._domain_2)) np.testing.assert_array_equal(multi_signals.domain, self._domain_2) np.testing.assert_array_equal(multi_signals.range, self._range_2) def test__hash__(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.__hash__` + Test :meth:`colour.continuous.multi_signals.MultiSignals.__hash__` method. """ @@ -358,13 +418,14 @@ def test__hash__(self): def test__str__(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.__str__` + Test :meth:`colour.continuous.multi_signals.MultiSignals.__str__` method. """ self.assertEqual( str(self._multi_signals), - textwrap.dedent(""" + textwrap.dedent( + """ [[ 0. 10. 20. 30.] [ 1. 20. 30. 40.] [ 2. 30. 40. 50.] @@ -374,20 +435,26 @@ def test__str__(self): [ 6. 70. 80. 90.] [ 7. 80. 90. 100.] [ 8. 90. 100. 110.] - [ 9. 100. 110. 120.]]""")[1:]) + [ 9. 100. 110. 120.]]""" + )[1:], + ) - self.assertIsInstance(str(MultiSignals()), string_types) + self.assertIsInstance(str(MultiSignals()), str) def test__repr__(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.__repr__` + Test :meth:`colour.continuous.multi_signals.MultiSignals.__repr__` method. """ self.assertEqual( - re.sub(r'extrapolator_kwargs={.*}', 'extrapolator_kwargs={...}', - repr(self._multi_signals)), - textwrap.dedent(""" + re.sub( + r"extrapolator_kwargs={.*}", + "extrapolator_kwargs={...}", + repr(self._multi_signals), + ), + textwrap.dedent( + """ MultiSignals([[ 0., 10., 20., 30.], [ 1., 20., 30., 40.], [ 2., 30., 40., 50.], @@ -398,107 +465,120 @@ def test__repr__(self): [ 7., 80., 90., 100.], [ 8., 90., 100., 110.], [ 9., 100., 110., 120.]], - labels=[0, 1, 2], + labels=['0', '1', '2'], interpolator=KernelInterpolator, interpolator_kwargs={}, extrapolator=Extrapolator, - extrapolator_kwargs={...})""")[1:]) + extrapolator_kwargs={...})""" + )[1:], + ) - self.assertIsInstance(repr(MultiSignals()), string_types) + self.assertIsInstance(repr(MultiSignals()), str) def test__getitem__(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.__getitem__` + Test :meth:`colour.continuous.multi_signals.MultiSignals.__getitem__` method. """ np.testing.assert_almost_equal( - self._multi_signals[0], np.array([10.0, 20.0, 30.0]), decimal=7) + self._multi_signals[0], np.array([10.0, 20.0, 30.0]), decimal=7 + ) np.testing.assert_almost_equal( self._multi_signals[np.array([0, 1, 2])], - np.array([ - [10.0, 20.0, 30.0], - [20.0, 30.0, 40.0], - [30.0, 40.0, 50.0], - ]), - decimal=7) + np.array( + [ + [10.0, 20.0, 30.0], + [20.0, 30.0, 40.0], + [30.0, 40.0, 50.0], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( self._multi_signals[np.linspace(0, 5, 5)], - np.array([ - [10.00000000, 20.00000000, 30.00000000], - [22.83489024, 32.80460562, 42.77432100], - [34.80044921, 44.74343470, 54.68642018], - [47.55353925, 57.52325463, 67.49297001], - [60.00000000, 70.00000000, 80.00000000], - ]), - decimal=7) - - assert np.all(np.isnan(self._multi_signals[np.array([-1000, 1000])])) + np.array( + [ + [10.00000000, 20.00000000, 30.00000000], + [22.83489024, 32.80460562, 42.77432100], + [34.80044921, 44.74343470, 54.68642018], + [47.55353925, 57.52325463, 67.49297001], + [60.00000000, 70.00000000, 80.00000000], + ] + ), + decimal=7, + ) + + attest(np.all(np.isnan(self._multi_signals[np.array([-1000, 1000])]))) np.testing.assert_almost_equal( - self._multi_signals[:], self._multi_signals.range, decimal=7) + self._multi_signals[:], self._multi_signals.range, decimal=7 + ) np.testing.assert_almost_equal( - self._multi_signals[:, :], self._multi_signals.range, decimal=7) + self._multi_signals[:, :], self._multi_signals.range, decimal=7 + ) np.testing.assert_almost_equal( self._multi_signals[0:3], - np.array([ - [10.0, 20.0, 30.0], - [20.0, 30.0, 40.0], - [30.0, 40.0, 50.0], - ]), - decimal=7) + np.array( + [ + [10.0, 20.0, 30.0], + [20.0, 30.0, 40.0], + [30.0, 40.0, 50.0], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( self._multi_signals[:, 0:2], - np.array([ - [10.0, 20.0], - [20.0, 30.0], - [30.0, 40.0], - [40.0, 50.0], - [50.0, 60.0], - [60.0, 70.0], - [70.0, 80.0], - [80.0, 90.0], - [90.0, 100.0], - [100.0, 110.0], - ]), - decimal=7) + np.array( + [ + [10.0, 20.0], + [20.0, 30.0], + [30.0, 40.0], + [40.0, 50.0], + [50.0, 60.0], + [60.0, 70.0], + [70.0, 80.0], + [80.0, 90.0], + [90.0, 100.0], + [100.0, 110.0], + ] + ), + decimal=7, + ) multi_signals = self._multi_signals.copy() multi_signals.extrapolator_kwargs = { - 'method': 'Linear', + "method": "Linear", } np.testing.assert_array_equal( multi_signals[np.array([-1000, 1000])], - np.array([ - [-9990.0, -9980.0, -9970.0], - [10010.0, 10020.0, 10030.0], - ])) + np.array( + [ + [-9990.0, -9980.0, -9970.0], + [10010.0, 10020.0, 10030.0], + ] + ), + ) multi_signals.extrapolator_kwargs = { - 'method': 'Constant', - 'left': 0, - 'right': 1 + "method": "Constant", + "left": 0, + "right": 1, } np.testing.assert_array_equal( multi_signals[np.array([-1000, 1000])], - np.array([[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]])) - - def test_raise_exception__getitem__(self): - """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.__getitem__` - method raised exception. - """ - - self.assertRaises(RuntimeError, operator.getitem, MultiSignals(), 0) + np.array([[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]]), + ) def test__setitem__(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.__setitem__` + Test :meth:`colour.continuous.multi_signals.MultiSignals.__setitem__` method. """ @@ -506,84 +586,113 @@ def test__setitem__(self): multi_signals[0] = 20 np.testing.assert_almost_equal( - multi_signals[0], np.array([20.0, 20.0, 20.0]), decimal=7) + multi_signals[0], np.array([20.0, 20.0, 20.0]), decimal=7 + ) multi_signals[np.array([0, 1, 2])] = 30 np.testing.assert_almost_equal( multi_signals[np.array([0, 1, 2])], - np.array([ - [30.0, 30.0, 30.0], - [30.0, 30.0, 30.0], - [30.0, 30.0, 30.0], - ]), - decimal=7) + np.array( + [ + [30.0, 30.0, 30.0], + [30.0, 30.0, 30.0], + [30.0, 30.0, 30.0], + ] + ), + decimal=7, + ) multi_signals[np.linspace(0, 5, 5)] = 50 np.testing.assert_almost_equal( multi_signals.domain, - np.array([ - 0.00, 1.00, 1.25, 2.00, 2.50, 3.00, 3.75, 4.00, 5.00, 6.00, - 7.00, 8.00, 9.00 - ]), - decimal=7) + np.array( + [ + 0.00, + 1.00, + 1.25, + 2.00, + 2.50, + 3.00, + 3.75, + 4.00, + 5.00, + 6.00, + 7.00, + 8.00, + 9.00, + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( multi_signals.range, - np.array([ - [50.0, 50.0, 50.0], - [30.0, 30.0, 30.0], - [50.0, 50.0, 50.0], - [30.0, 30.0, 30.0], - [50.0, 50.0, 50.0], - [40.0, 50.0, 60.0], - [50.0, 50.0, 50.0], - [50.0, 60.0, 70.0], - [50.0, 50.0, 50.0], - [70.0, 80.0, 90.0], - [80.0, 90.0, 100.0], - [90.0, 100.0, 110.0], - [100.0, 110.0, 120.0], - ]), - decimal=7) + np.array( + [ + [50.0, 50.0, 50.0], + [30.0, 30.0, 30.0], + [50.0, 50.0, 50.0], + [30.0, 30.0, 30.0], + [50.0, 50.0, 50.0], + [40.0, 50.0, 60.0], + [50.0, 50.0, 50.0], + [50.0, 60.0, 70.0], + [50.0, 50.0, 50.0], + [70.0, 80.0, 90.0], + [80.0, 90.0, 100.0], + [90.0, 100.0, 110.0], + [100.0, 110.0, 120.0], + ] + ), + decimal=7, + ) multi_signals[np.array([0, 1, 2])] = np.array([10, 20, 30]) np.testing.assert_almost_equal( multi_signals.range, - np.array([ - [10.0, 20.0, 30.0], - [10.0, 20.0, 30.0], - [50.0, 50.0, 50.0], - [10.0, 20.0, 30.0], - [50.0, 50.0, 50.0], - [40.0, 50.0, 60.0], - [50.0, 50.0, 50.0], - [50.0, 60.0, 70.0], - [50.0, 50.0, 50.0], - [70.0, 80.0, 90.0], - [80.0, 90.0, 100.0], - [90.0, 100.0, 110.0], - [100.0, 110.0, 120.0], - ]), - decimal=7) - - multi_signals[np.array([0, 1, 2])] = np.arange(1, 10, 1).reshape(3, 3) + np.array( + [ + [10.0, 20.0, 30.0], + [10.0, 20.0, 30.0], + [50.0, 50.0, 50.0], + [10.0, 20.0, 30.0], + [50.0, 50.0, 50.0], + [40.0, 50.0, 60.0], + [50.0, 50.0, 50.0], + [50.0, 60.0, 70.0], + [50.0, 50.0, 50.0], + [70.0, 80.0, 90.0], + [80.0, 90.0, 100.0], + [90.0, 100.0, 110.0], + [100.0, 110.0, 120.0], + ] + ), + decimal=7, + ) + + multi_signals[np.array([0, 1, 2])] = np.reshape( + np.arange(1, 10, 1), (3, 3) + ) np.testing.assert_almost_equal( multi_signals.range, - np.array([ - [1.0, 2.0, 3.0], - [4.0, 5.0, 6.0], - [50.0, 50.0, 50.0], - [7.0, 8.0, 9.0], - [50.0, 50.0, 50.0], - [40.0, 50.0, 60.0], - [50.0, 50.0, 50.0], - [50.0, 60.0, 70.0], - [50.0, 50.0, 50.0], - [70.0, 80.0, 90.0], - [80.0, 90.0, 100.0], - [90.0, 100.0, 110.0], - [100.0, 110.0, 120.0], - ]), - decimal=7) + np.array( + [ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [50.0, 50.0, 50.0], + [7.0, 8.0, 9.0], + [50.0, 50.0, 50.0], + [40.0, 50.0, 60.0], + [50.0, 50.0, 50.0], + [50.0, 60.0, 70.0], + [50.0, 50.0, 50.0], + [70.0, 80.0, 90.0], + [80.0, 90.0, 100.0], + [90.0, 100.0, 110.0], + [100.0, 110.0, 120.0], + ] + ), + decimal=7, + ) multi_signals[:] = 40 np.testing.assert_almost_equal(multi_signals.range, 40, decimal=7) @@ -595,33 +704,39 @@ def test__setitem__(self): multi_signals[0:3] = 40 np.testing.assert_almost_equal( multi_signals[0:3], - np.array([ - [40.0, 40.0, 40.0], - [40.0, 40.0, 40.0], - [40.0, 40.0, 40.0], - ]), - decimal=7) + np.array( + [ + [40.0, 40.0, 40.0], + [40.0, 40.0, 40.0], + [40.0, 40.0, 40.0], + ] + ), + decimal=7, + ) multi_signals[:, 0:2] = 50 np.testing.assert_almost_equal( multi_signals.range, - np.array([ - [50.0, 50.0, 40.0], - [50.0, 50.0, 40.0], - [50.0, 50.0, 40.0], - [50.0, 50.0, 60.0], - [50.0, 50.0, 70.0], - [50.0, 50.0, 80.0], - [50.0, 50.0, 90.0], - [50.0, 50.0, 100.0], - [50.0, 50.0, 110.0], - [50.0, 50.0, 120.0], - ]), - decimal=7) + np.array( + [ + [50.0, 50.0, 40.0], + [50.0, 50.0, 40.0], + [50.0, 50.0, 40.0], + [50.0, 50.0, 60.0], + [50.0, 50.0, 70.0], + [50.0, 50.0, 80.0], + [50.0, 50.0, 90.0], + [50.0, 50.0, 100.0], + [50.0, 50.0, 110.0], + [50.0, 50.0, 120.0], + ] + ), + decimal=7, + ) def test__contains__(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.__contains__` + Test :meth:`colour.continuous.multi_signals.MultiSignals.__contains__` method. """ @@ -629,17 +744,9 @@ def test__contains__(self): self.assertIn(0.5, self._multi_signals) self.assertNotIn(1000, self._multi_signals) - def test_raise_exception__contains__(self): - """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.__contains__` - method raised exception. - """ - - self.assertRaises(RuntimeError, operator.contains, MultiSignals(), 0) - def test__len__(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.__len__` + Test :meth:`colour.continuous.multi_signals.MultiSignals.__len__` method. """ @@ -647,7 +754,7 @@ def test__len__(self): def test__eq__(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.__eq__` + Test :meth:`colour.continuous.multi_signals.MultiSignals.__eq__` method. """ @@ -656,9 +763,11 @@ def test__eq__(self): self.assertEqual(signal_1, signal_2) + self.assertNotEqual(signal_1, None) + def test__ne__(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.__ne__` + Test :meth:`colour.continuous.multi_signals.MultiSignals.__ne__` method. """ @@ -677,16 +786,14 @@ def test__ne__(self): multi_signals_2.interpolator = KernelInterpolator self.assertEqual(multi_signals_1, multi_signals_2) - multi_signals_2.interpolator_kwargs = {'window': 1} + multi_signals_2.interpolator_kwargs = {"window": 1} self.assertNotEqual(multi_signals_1, multi_signals_2) multi_signals_2.interpolator_kwargs = {} self.assertEqual(multi_signals_1, multi_signals_2) class NotExtrapolator(Extrapolator): - """ - Not :class:`Extrapolator` class. - """ + """Not :class:`Extrapolator` class.""" pass @@ -700,101 +807,120 @@ class NotExtrapolator(Extrapolator): self.assertNotEqual(multi_signals_1, multi_signals_2) multi_signals_2.extrapolator_kwargs = { - 'method': 'Constant', - 'left': np.nan, - 'right': np.nan + "method": "Constant", + "left": np.nan, + "right": np.nan, } self.assertEqual(multi_signals_1, multi_signals_2) def test_arithmetical_operation(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.\ + Test :func:`colour.continuous.multi_signals.MultiSignals.\ arithmetical_operation` method. """ np.testing.assert_almost_equal( - self._multi_signals.arithmetical_operation(10, '+', False).range, + self._multi_signals.arithmetical_operation(10, "+", False).range, self._range_2 + 10, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - self._multi_signals.arithmetical_operation(10, '-', False).range, + self._multi_signals.arithmetical_operation(10, "-", False).range, self._range_2 - 10, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - self._multi_signals.arithmetical_operation(10, '*', False).range, + self._multi_signals.arithmetical_operation(10, "*", False).range, self._range_2 * 10, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - self._multi_signals.arithmetical_operation(10, '/', False).range, + self._multi_signals.arithmetical_operation(10, "/", False).range, self._range_2 / 10, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - self._multi_signals.arithmetical_operation(10, '**', False).range, - self._range_2 ** 10, - decimal=7) + self._multi_signals.arithmetical_operation(10, "**", False).range, + self._range_2**10, + decimal=7, + ) np.testing.assert_almost_equal( - (self._multi_signals + 10).range, self._range_2 + 10, decimal=7) + (self._multi_signals + 10).range, self._range_2 + 10, decimal=7 + ) np.testing.assert_almost_equal( - (self._multi_signals - 10).range, self._range_2 - 10, decimal=7) + (self._multi_signals - 10).range, self._range_2 - 10, decimal=7 + ) np.testing.assert_almost_equal( - (self._multi_signals * 10).range, self._range_2 * 10, decimal=7) + (self._multi_signals * 10).range, self._range_2 * 10, decimal=7 + ) np.testing.assert_almost_equal( - (self._multi_signals / 10).range, self._range_2 / 10, decimal=7) + (self._multi_signals / 10).range, self._range_2 / 10, decimal=7 + ) np.testing.assert_almost_equal( - (self._multi_signals ** 10).range, self._range_2 ** 10, decimal=7) + (self._multi_signals**10).range, self._range_2**10, decimal=7 + ) multi_signals = self._multi_signals.copy() np.testing.assert_almost_equal( - multi_signals.arithmetical_operation(10, '+', True).range, + multi_signals.arithmetical_operation(10, "+", True).range, self._range_2 + 10, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - multi_signals.arithmetical_operation(10, '-', True).range, + multi_signals.arithmetical_operation(10, "-", True).range, self._range_2, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - multi_signals.arithmetical_operation(10, '*', True).range, + multi_signals.arithmetical_operation(10, "*", True).range, self._range_2 * 10, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - multi_signals.arithmetical_operation(10, '/', True).range, + multi_signals.arithmetical_operation(10, "/", True).range, self._range_2, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - multi_signals.arithmetical_operation(10, '**', True).range, - self._range_2 ** 10, - decimal=7) + multi_signals.arithmetical_operation(10, "**", True).range, + self._range_2**10, + decimal=7, + ) multi_signals = self._multi_signals.copy() np.testing.assert_almost_equal( - multi_signals.arithmetical_operation(self._range_2, '+', - False).range, + multi_signals.arithmetical_operation( + self._range_2, "+", False + ).range, self._range_2 + self._range_2, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - multi_signals.arithmetical_operation(multi_signals, '+', - False).range, + multi_signals.arithmetical_operation( + multi_signals, "+", False + ).range, self._range_2 + self._range_2, - decimal=7) + decimal=7, + ) def test_is_uniform(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.is_uniform` + Test :meth:`colour.continuous.multi_signals.MultiSignals.is_uniform` method. """ @@ -805,74 +931,104 @@ def test_is_uniform(self): self.assertFalse(multi_signals.is_uniform()) def test_copy(self): - """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.copy` method. - """ + """Test :func:`colour.continuous.multi_signals.MultiSignals.copy` method.""" self.assertIsNot(self._multi_signals, self._multi_signals.copy()) self.assertEqual(self._multi_signals, self._multi_signals.copy()) def test_multi_signals_unpack_data(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.\ + Test :func:`colour.continuous.multi_signals.MultiSignals.\ multi_signals_unpack_data` method. """ signals = MultiSignals.multi_signals_unpack_data(self._range_1) - self.assertListEqual(list(signals.keys()), [0]) - np.testing.assert_array_equal(signals[0].domain, self._domain_1) - np.testing.assert_array_equal(signals[0].range, self._range_1) + self.assertListEqual(list(signals.keys()), ["0"]) + np.testing.assert_array_equal(signals["0"].domain, self._domain_1) + np.testing.assert_array_equal(signals["0"].range, self._range_1) + + signals = MultiSignals.multi_signals_unpack_data( + self._range_1, self._domain_2 + ) + self.assertListEqual(list(signals.keys()), ["0"]) + np.testing.assert_array_equal(signals["0"].domain, self._domain_2) + np.testing.assert_array_equal(signals["0"].range, self._range_1) + + signals = MultiSignals.multi_signals_unpack_data( + self._range_1, dict(zip(self._domain_2, self._range_1)).keys() + ) + np.testing.assert_array_equal(signals["0"].domain, self._domain_2) signals = MultiSignals.multi_signals_unpack_data( - self._range_1, self._domain_2) - self.assertListEqual(list(signals.keys()), [0]) - np.testing.assert_array_equal(signals[0].domain, self._domain_2) - np.testing.assert_array_equal(signals[0].range, self._range_1) + self._range_2, self._domain_2 + ) + self.assertListEqual(list(signals.keys()), ["0", "1", "2"]) + np.testing.assert_array_equal(signals["0"].range, self._range_1) + np.testing.assert_array_equal(signals["1"].range, self._range_1 + 10) + np.testing.assert_array_equal(signals["2"].range, self._range_1 + 20) signals = MultiSignals.multi_signals_unpack_data( - self._range_2, self._domain_2) - self.assertListEqual(list(signals.keys()), [0, 1, 2]) - np.testing.assert_array_equal(signals[0].range, self._range_1) - np.testing.assert_array_equal(signals[1].range, self._range_1 + 10) - np.testing.assert_array_equal(signals[2].range, self._range_1 + 20) + list( + MultiSignals.multi_signals_unpack_data( + dict(zip(self._domain_2, self._range_2)) + ).values() + )[0] + ) + np.testing.assert_array_equal(signals["0"].range, self._range_1) signals = MultiSignals.multi_signals_unpack_data( - dict(zip(self._domain_2, self._range_2))) - self.assertListEqual(list(signals.keys()), [0, 1, 2]) - np.testing.assert_array_equal(signals[0].range, self._range_1) - np.testing.assert_array_equal(signals[1].range, self._range_1 + 10) - np.testing.assert_array_equal(signals[2].range, self._range_1 + 20) + MultiSignals.multi_signals_unpack_data( + dict(zip(self._domain_2, self._range_2)) + ).values() + ) + np.testing.assert_array_equal(signals["0"].range, self._range_1) + np.testing.assert_array_equal(signals["1"].range, self._range_1 + 10) + np.testing.assert_array_equal(signals["2"].range, self._range_1 + 20) + + signals = MultiSignals.multi_signals_unpack_data( + dict(zip(self._domain_2, self._range_2)) + ) + self.assertListEqual(list(signals.keys()), ["0", "1", "2"]) + np.testing.assert_array_equal(signals["0"].range, self._range_1) + np.testing.assert_array_equal(signals["1"].range, self._range_1 + 10) + np.testing.assert_array_equal(signals["2"].range, self._range_1 + 20) signals = MultiSignals.multi_signals_unpack_data( MultiSignals.multi_signals_unpack_data( - dict(zip(self._domain_2, self._range_2)))) - self.assertListEqual(list(signals.keys()), [0, 1, 2]) - np.testing.assert_array_equal(signals[0].range, self._range_1) - np.testing.assert_array_equal(signals[1].range, self._range_1 + 10) - np.testing.assert_array_equal(signals[2].range, self._range_1 + 20) + dict(zip(self._domain_2, self._range_2)) + ) + ) + self.assertListEqual(list(signals.keys()), ["0", "1", "2"]) + np.testing.assert_array_equal(signals["0"].range, self._range_1) + np.testing.assert_array_equal(signals["1"].range, self._range_1 + 10) + np.testing.assert_array_equal(signals["2"].range, self._range_1 + 20) if is_pandas_installed(): from pandas import DataFrame, Series signals = MultiSignals.multi_signals_unpack_data( - Series(dict(zip(self._domain_1, self._range_1)))) - self.assertListEqual(list(signals.keys()), [0]) - np.testing.assert_array_equal(signals[0].domain, self._domain_1) - np.testing.assert_array_equal(signals[0].range, self._range_1) + Series(dict(zip(self._domain_1, self._range_1))) + ) + self.assertListEqual(list(signals.keys()), ["0"]) + np.testing.assert_array_equal(signals["0"].domain, self._domain_1) + np.testing.assert_array_equal(signals["0"].range, self._range_1) - data = dict(zip(['a', 'b', 'c'], tsplit(self._range_2))) + data = dict(zip(["a", "b", "c"], tsplit(self._range_2))) signals = MultiSignals.multi_signals_unpack_data( - DataFrame(data, self._domain_1)) - self.assertListEqual(list(signals.keys()), ['a', 'b', 'c']) - np.testing.assert_array_equal(signals['a'].range, self._range_1) - np.testing.assert_array_equal(signals['b'].range, - self._range_1 + 10) - np.testing.assert_array_equal(signals['c'].range, - self._range_1 + 20) + DataFrame(data, self._domain_1) + ) + self.assertListEqual(list(signals.keys()), ["a", "b", "c"]) + np.testing.assert_array_equal(signals["a"].range, self._range_1) + np.testing.assert_array_equal( + signals["b"].range, self._range_1 + 10 + ) + np.testing.assert_array_equal( + signals["c"].range, self._range_1 + 20 + ) def test_fill_nan(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.fill_nan` + Test :meth:`colour.continuous.multi_signals.MultiSignals.fill_nan` method. """ @@ -882,52 +1038,79 @@ def test_fill_nan(self): np.testing.assert_almost_equal( multi_signals.fill_nan().range, - np.array([[10.0, 20.0, 30.0], [20.0, 30.0, 40.0], [ - 30.0, 40.0, 50.0 - ], [40.0, 50.0, 60.0], [50.0, 60.0, 70.0], [60.0, 70.0, 80.0], - [70.0, 80.0, 90.0], [80.0, 90.0, 100.0], - [90.0, 100.0, 110.0], [100.0, 110.0, 120.0]]), - decimal=7) + np.array( + [ + [10.0, 20.0, 30.0], + [20.0, 30.0, 40.0], + [30.0, 40.0, 50.0], + [40.0, 50.0, 60.0], + [50.0, 60.0, 70.0], + [60.0, 70.0, 80.0], + [70.0, 80.0, 90.0], + [80.0, 90.0, 100.0], + [90.0, 100.0, 110.0], + [100.0, 110.0, 120.0], + ] + ), + decimal=7, + ) multi_signals[3:7] = np.nan np.testing.assert_almost_equal( - multi_signals.fill_nan(method='Constant').range, - np.array([[10.0, 20.0, 30.0], [20.0, 30.0, 40.0], - [30.0, 40.0, 50.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], - [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [80.0, 90.0, 100.0], - [90.0, 100.0, 110.0], [100.0, 110.0, 120.0]]), - decimal=7) + multi_signals.fill_nan(method="Constant").range, + np.array( + [ + [10.0, 20.0, 30.0], + [20.0, 30.0, 40.0], + [30.0, 40.0, 50.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [80.0, 90.0, 100.0], + [90.0, 100.0, 110.0], + [100.0, 110.0, 120.0], + ] + ), + decimal=7, + ) def test_domain_distance(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.\ + Test :func:`colour.continuous.multi_signals.MultiSignals.\ domain_distance` method. """ self.assertAlmostEqual( - self._multi_signals.domain_distance(0.5), 0.5, places=7) + self._multi_signals.domain_distance(0.5), 0.5, places=7 + ) np.testing.assert_almost_equal( self._multi_signals.domain_distance(np.linspace(0, 9, 10) + 0.5), np.array([0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]), - decimal=7) + decimal=7, + ) def test_to_dataframe(self): """ - Tests :func:`colour.continuous.multi_signals.MultiSignals.to_dataframe` + Test :meth:`colour.continuous.multi_signals.MultiSignals.to_dataframe` method. """ if is_pandas_installed(): from pandas import DataFrame - data = dict(zip(['a', 'b', 'c'], tsplit(self._range_2))) - assert MultiSignals( - self._range_2, self._domain_2, - labels=['a', 'b', 'c']).to_dataframe().equals( - DataFrame(data, self._domain_2)) + data = dict(zip(["a", "b", "c"], tsplit(self._range_2))) + + attest( + MultiSignals( + self._range_2, self._domain_2, labels=["a", "b", "c"] + ) + .to_dataframe() + .equals(DataFrame(data, self._domain_2)) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/continuous/tests/test_signal.py b/colour/continuous/tests/test_signal.py index bbbc78d56b..baca97d1b3 100644 --- a/colour/continuous/tests/test_signal.py +++ b/colour/continuous/tests/test_signal.py @@ -1,41 +1,36 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.continuous.signal` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.continuous.signal` module.""" import numpy as np import unittest import re import textwrap -from six import string_types -from colour.algebra import (CubicSplineInterpolator, Extrapolator, - KernelInterpolator) +from colour.algebra import ( + CubicSplineInterpolator, + Extrapolator, + KernelInterpolator, +) from colour.constants import DEFAULT_FLOAT_DTYPE from colour.continuous import Signal -from colour.utilities import is_pandas_installed +from colour.utilities import ColourRuntimeWarning, attest, is_pandas_installed -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestSignal'] +__all__ = [ + "TestSignal", +] class TestSignal(unittest.TestCase): - """ - Defines :class:`colour.continuous.signal.Signal` class unit tests methods. - """ + """Define :class:`colour.continuous.signal.Signal` class unit tests methods.""" def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._range = np.linspace(10, 100, 10) self._domain = np.arange(100, 1100, 100) @@ -43,35 +38,47 @@ def setUp(self): self._signal = Signal(self._range) def test_required_attributes(self): - """ - Tests presence of required attributes. - """ - - required_attributes = ('dtype', 'domain', 'range', 'interpolator', - 'interpolator_kwargs', 'extrapolator', - 'extrapolator_kwargs', 'function') + """Test the presence of required attributes.""" + + required_attributes = ( + "dtype", + "domain", + "range", + "interpolator", + "interpolator_kwargs", + "extrapolator", + "extrapolator_kwargs", + "function", + ) for attribute in required_attributes: self.assertIn(attribute, dir(Signal)) def test_required_methods(self): - """ - Tests presence of required methods. - """ - - required_methods = ('__init__', '__str__', '__repr__', '__hash__', - '__getitem__', '__setitem__', '__contains__', - '__eq__', '__ne__', 'arithmetical_operation', - 'signal_unpack_data', 'fill_nan', - 'domain_distance', 'to_series') + """Test the presence of required methods.""" + + required_methods = ( + "__init__", + "__str__", + "__repr__", + "__hash__", + "__getitem__", + "__setitem__", + "__contains__", + "__eq__", + "__ne__", + "arithmetical_operation", + "signal_unpack_data", + "fill_nan", + "domain_distance", + "to_series", + ) for method in required_methods: self.assertIn(method, dir(Signal)) def test_dtype(self): - """ - Tests :func:`colour.continuous.signal.Signal.dtype` property. - """ + """Test :func:`colour.continuous.signal.Signal.dtype` property.""" self.assertEqual(self._signal.dtype, DEFAULT_FLOAT_DTYPE) @@ -80,16 +87,15 @@ def test_dtype(self): self.assertEqual(signal.dtype, np.float32) def test_domain(self): - """ - Tests :func:`colour.continuous.signal.Signal.domain` property. - """ + """Test :func:`colour.continuous.signal.Signal.domain` property.""" signal = self._signal.copy() np.testing.assert_almost_equal( signal[np.array([0, 1, 2])], np.array([10.0, 20.0, 30.0]), - decimal=7) + decimal=7, + ) signal.domain = np.arange(0, 10, 1) * 10 @@ -98,24 +104,29 @@ def test_domain(self): np.testing.assert_almost_equal( signal[np.array([0, 1, 2]) * 10], np.array([10.0, 20.0, 30.0]), - decimal=7) + decimal=7, + ) - # TODO: Use "assertWarns" when dropping Python 2.7. domain = np.linspace(0, 1, 10) domain[0] = -np.inf - signal.domain = domain + + def assert_warns(): + """Help to test the runtime warning.""" + + signal.domain = domain + + self.assertWarns(ColourRuntimeWarning, assert_warns) def test_range(self): - """ - Tests :func:`colour.continuous.signal.Signal.range` property. - """ + """Test :func:`colour.continuous.signal.Signal.range` property.""" signal = self._signal.copy() np.testing.assert_almost_equal( signal[np.array([0, 1, 2])], np.array([10.0, 20.0, 30.0]), - decimal=7) + decimal=7, + ) signal.range = self._range * 10 @@ -124,32 +135,39 @@ def test_range(self): np.testing.assert_almost_equal( signal[np.array([0, 1, 2])], np.array([10.0, 20.0, 30.0]) * 10, - decimal=7) + decimal=7, + ) def test_interpolator(self): - """ - Tests :func:`colour.continuous.signal.Signal.interpolator` property. - """ + """Test :func:`colour.continuous.signal.Signal.interpolator` property.""" signal = self._signal.copy() np.testing.assert_almost_equal( signal[np.linspace(0, 5, 5)], - np.array([ - 10.00000000, 22.83489024, 34.80044921, 47.55353925, 60.00000000 - ]), - decimal=7) + np.array( + [ + 10.00000000, + 22.83489024, + 34.80044921, + 47.55353925, + 60.00000000, + ] + ), + decimal=7, + ) signal.interpolator = CubicSplineInterpolator np.testing.assert_almost_equal( signal[np.linspace(0, 5, 5)], np.array([10.0, 22.5, 35.0, 47.5, 60.0]), - decimal=7) + decimal=7, + ) def test_interpolator_kwargs(self): """ - Tests :func:`colour.continuous.signal.Signal.interpolator_kwargs` + Test :func:`colour.continuous.signal.Signal.interpolator_kwargs` property. """ @@ -157,65 +175,74 @@ def test_interpolator_kwargs(self): np.testing.assert_almost_equal( signal[np.linspace(0, 5, 5)], - np.array([ - 10.00000000, 22.83489024, 34.80044921, 47.55353925, 60.00000000 - ]), - decimal=7) + np.array( + [ + 10.00000000, + 22.83489024, + 34.80044921, + 47.55353925, + 60.00000000, + ] + ), + decimal=7, + ) - signal.interpolator_kwargs = {'window': 1, 'kernel_kwargs': {'a': 1}} + signal.interpolator_kwargs = {"window": 1, "kernel_kwargs": {"a": 1}} np.testing.assert_almost_equal( signal[np.linspace(0, 5, 5)], - np.array([ - 10.00000000, 18.91328761, 28.36993142, 44.13100443, 60.00000000 - ]), - decimal=7) + np.array( + [ + 10.00000000, + 18.91328761, + 28.36993142, + 44.13100443, + 60.00000000, + ] + ), + decimal=7, + ) def test_extrapolator(self): - """ - Tests :func:`colour.continuous.signal.Signal.extrapolator` property. - """ + """Test :func:`colour.continuous.signal.Signal.extrapolator` property.""" self.assertIsInstance(self._signal.extrapolator(), Extrapolator) def test_extrapolator_kwargs(self): """ - Tests :func:`colour.continuous.signal.Signal.extrapolator_kwargs` + Test :func:`colour.continuous.signal.Signal.extrapolator_kwargs` property. """ signal = self._signal.copy() - assert np.all(np.isnan(signal[np.array([-1000, 1000])])) + attest(np.all(np.isnan(signal[np.array([-1000, 1000])]))) signal.extrapolator_kwargs = { - 'method': 'Linear', + "method": "Linear", } np.testing.assert_almost_equal( signal[np.array([-1000, 1000])], np.array([-9990.0, 10010.0]), - decimal=7) + decimal=7, + ) def test_function(self): - """ - Tests :func:`colour.continuous.signal.Signal.function` property. - """ + """Test :func:`colour.continuous.signal.Signal.function` property.""" - assert hasattr(self._signal.function, '__call__') + attest(hasattr(self._signal.function, "__call__")) def test_raise_exception_function(self): """ - Tests :func:`colour.continuous.signal.Signal.function` property raised + Test :func:`colour.continuous.signal.Signal.function` property raised exception. """ - self.assertRaises(RuntimeError, Signal().function, 0) + self.assertRaises(ValueError, Signal().function, 0) def test__init__(self): - """ - Tests :func:`colour.continuous.signal.Signal.__init__` method. - """ + """Test :func:`colour.continuous.signal.Signal.__init__` method.""" signal = Signal(self._range) np.testing.assert_array_equal(signal.domain, np.arange(0, 10, 1)) @@ -241,20 +268,17 @@ def test__init__(self): np.testing.assert_array_equal(signal.range, self._range) def test__hash__(self): - """ - Tests :func:`colour.continuous.signal.Signal.__hash__` method. - """ + """Test :func:`colour.continuous.signal.Signal.__hash__` method.""" self.assertIsInstance(hash(self._signal), int) def test__str__(self): - """ - Tests :func:`colour.continuous.signal.Signal.__str__` method. - """ + """Test :func:`colour.continuous.signal.Signal.__str__` method.""" self.assertEqual( str(self._signal), - textwrap.dedent(""" + textwrap.dedent( + """ [[ 0. 10.] [ 1. 20.] [ 2. 30.] @@ -264,19 +288,23 @@ def test__str__(self): [ 6. 70.] [ 7. 80.] [ 8. 90.] - [ 9. 100.]]""")[1:]) + [ 9. 100.]]""" + )[1:], + ) - self.assertIsInstance(str(Signal()), string_types) + self.assertIsInstance(str(Signal()), str) def test__repr__(self): - """ - Tests :func:`colour.continuous.signal.Signal.__repr__` method. - """ + """Test :func:`colour.continuous.signal.Signal.__repr__` method.""" self.assertEqual( - re.sub(r'extrapolator_kwargs={.*}', 'extrapolator_kwargs={...}', - repr(self._signal)), - textwrap.dedent(""" + re.sub( + r"extrapolator_kwargs={.*}", + "extrapolator_kwargs={...}", + repr(self._signal), + ), + textwrap.dedent( + """ Signal([[ 0., 10.], [ 1., 20.], [ 2., 30.], @@ -290,50 +318,58 @@ def test__repr__(self): interpolator=KernelInterpolator, interpolator_kwargs={}, extrapolator=Extrapolator, - extrapolator_kwargs={...})""")[1:]) + extrapolator_kwargs={...})""" + )[1:], + ) - self.assertIsInstance(repr(Signal()), string_types) + self.assertIsInstance(repr(Signal()), str) def test__getitem__(self): - """ - Tests :func:`colour.continuous.signal.Signal.__getitem__` method. - """ + """Test :func:`colour.continuous.signal.Signal.__getitem__` method.""" self.assertEqual(self._signal[0], 10.0) np.testing.assert_almost_equal( self._signal[np.array([0, 1, 2])], np.array([10.0, 20.0, 30.0]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( self._signal[np.linspace(0, 5, 5)], - np.array([ - 10.00000000, 22.83489024, 34.80044921, 47.55353925, 60.00000000 - ]), - decimal=7) - - assert np.all(np.isnan(self._signal[np.array([-1000, 1000])])) + np.array( + [ + 10.00000000, + 22.83489024, + 34.80044921, + 47.55353925, + 60.00000000, + ] + ), + decimal=7, + ) + + attest(np.all(np.isnan(self._signal[np.array([-1000, 1000])]))) signal = self._signal.copy() signal.extrapolator_kwargs = { - 'method': 'Linear', + "method": "Linear", } - np.testing.assert_array_equal(signal[np.array([-1000, 1000])], - np.array([-9990.0, 10010.0])) + np.testing.assert_array_equal( + signal[np.array([-1000, 1000])], np.array([-9990.0, 10010.0]) + ) signal.extrapolator_kwargs = { - 'method': 'Constant', - 'left': 0, - 'right': 1 + "method": "Constant", + "left": 0, + "right": 1, } - np.testing.assert_array_equal(signal[np.array([-1000, 1000])], - np.array([0.0, 1.0])) + np.testing.assert_array_equal( + signal[np.array([-1000, 1000])], np.array([0.0, 1.0]) + ) def test__setitem__(self): - """ - Tests :func:`colour.continuous.signal.Signal.__setitem__` method. - """ + """Test :func:`colour.continuous.signal.Signal.__setitem__` method.""" signal = self._signal.copy() @@ -341,67 +377,109 @@ def test__setitem__(self): np.testing.assert_almost_equal( signal.range, np.array( - [20.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0])) + [20.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0] + ), + ) signal[np.array([0, 1, 2])] = 30 np.testing.assert_almost_equal( signal.range, np.array( - [30.0, 30.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0]), - decimal=7) + [30.0, 30.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0] + ), + decimal=7, + ) signal[0:3] = 40 np.testing.assert_almost_equal( signal.range, np.array( - [40.0, 40.0, 40.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0]), - decimal=7) + [40.0, 40.0, 40.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0] + ), + decimal=7, + ) signal[np.linspace(0, 5, 5)] = 50 np.testing.assert_almost_equal( signal.domain, - np.array([ - 0.00, 1.00, 1.25, 2.00, 2.50, 3.00, 3.75, 4.00, 5.00, 6.00, - 7.00, 8.00, 9.00 - ]), - decimal=7) + np.array( + [ + 0.00, + 1.00, + 1.25, + 2.00, + 2.50, + 3.00, + 3.75, + 4.00, + 5.00, + 6.00, + 7.00, + 8.00, + 9.00, + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( signal.range, - np.array([ - 50.0, 40.0, 50.0, 40.0, 50.0, 40.0, 50.0, 50.0, 50.0, 70.0, - 80.0, 90.0, 100.0 - ]), - decimal=7) + np.array( + [ + 50.0, + 40.0, + 50.0, + 40.0, + 50.0, + 40.0, + 50.0, + 50.0, + 50.0, + 70.0, + 80.0, + 90.0, + 100.0, + ] + ), + decimal=7, + ) signal[np.array([0, 1, 2])] = np.array([10, 20, 30]) np.testing.assert_almost_equal( signal.range, - np.array([ - 10.0, 20.0, 50.0, 30.0, 50.0, 40.0, 50.0, 50.0, 50.0, 70.0, - 80.0, 90.0, 100.0 - ]), - decimal=7) + np.array( + [ + 10.0, + 20.0, + 50.0, + 30.0, + 50.0, + 40.0, + 50.0, + 50.0, + 50.0, + 70.0, + 80.0, + 90.0, + 100.0, + ] + ), + decimal=7, + ) def test__contains__(self): - """ - Tests :func:`colour.continuous.signal.Signal.__contains__` method. - """ + """Test :func:`colour.continuous.signal.Signal.__contains__` method.""" self.assertIn(0, self._signal) self.assertIn(0.5, self._signal) self.assertNotIn(1000, self._signal) def test__len__(self): - """ - Tests :func:`colour.continuous.signal.Signal.__len__` method. - """ + """Test :func:`colour.continuous.signal.Signal.__len__` method.""" self.assertEqual(len(self._signal), 10) def test__eq__(self): - """ - Tests :func:`colour.continuous.signal.Signal.__eq__` method. - """ + """Test :func:`colour.continuous.signal.Signal.__eq__` method.""" signal_1 = self._signal.copy() signal_2 = self._signal.copy() @@ -409,9 +487,7 @@ def test__eq__(self): self.assertEqual(signal_1, signal_2) def test__ne__(self): - """ - Tests :func:`colour.continuous.signal.Signal.__ne__` method. - """ + """Test :func:`colour.continuous.signal.Signal.__ne__` method.""" signal_1 = self._signal.copy() signal_2 = self._signal.copy() @@ -428,16 +504,14 @@ def test__ne__(self): signal_2.interpolator = KernelInterpolator self.assertEqual(signal_1, signal_2) - signal_2.interpolator_kwargs = {'window': 1} + signal_2.interpolator_kwargs = {"window": 1} self.assertNotEqual(signal_1, signal_2) signal_2.interpolator_kwargs = {} self.assertEqual(signal_1, signal_2) class NotExtrapolator(Extrapolator): - """ - Not :class:`Extrapolator` class. - """ + """Not :class:`Extrapolator` class.""" pass @@ -451,101 +525,116 @@ class NotExtrapolator(Extrapolator): self.assertNotEqual(signal_1, signal_2) signal_2.extrapolator_kwargs = { - 'method': 'Constant', - 'left': np.nan, - 'right': np.nan + "method": "Constant", + "left": np.nan, + "right": np.nan, } self.assertEqual(signal_1, signal_2) def test_arithmetical_operation(self): """ - Tests :func:`colour.continuous.signal.Signal.arithmetical_operation` + Test :meth:`colour.continuous.signal.Signal.arithmetical_operation` method. """ np.testing.assert_almost_equal( - self._signal.arithmetical_operation(10, '+', False).range, + self._signal.arithmetical_operation(10, "+", False).range, self._range + 10, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - self._signal.arithmetical_operation(10, '-', False).range, + self._signal.arithmetical_operation(10, "-", False).range, self._range - 10, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - self._signal.arithmetical_operation(10, '*', False).range, + self._signal.arithmetical_operation(10, "*", False).range, self._range * 10, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - self._signal.arithmetical_operation(10, '/', False).range, + self._signal.arithmetical_operation(10, "/", False).range, self._range / 10, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - self._signal.arithmetical_operation(10, '**', False).range, - self._range ** 10, - decimal=7) + self._signal.arithmetical_operation(10, "**", False).range, + self._range**10, + decimal=7, + ) np.testing.assert_almost_equal( - (self._signal + 10).range, self._range + 10, decimal=7) + (self._signal + 10).range, self._range + 10, decimal=7 + ) np.testing.assert_almost_equal( - (self._signal - 10).range, self._range - 10, decimal=7) + (self._signal - 10).range, self._range - 10, decimal=7 + ) np.testing.assert_almost_equal( - (self._signal * 10).range, self._range * 10, decimal=7) + (self._signal * 10).range, self._range * 10, decimal=7 + ) np.testing.assert_almost_equal( - (self._signal / 10).range, self._range / 10, decimal=7) + (self._signal / 10).range, self._range / 10, decimal=7 + ) np.testing.assert_almost_equal( - (self._signal ** 10).range, self._range ** 10, decimal=7) + (self._signal**10).range, self._range**10, decimal=7 + ) signal = self._signal.copy() np.testing.assert_almost_equal( - signal.arithmetical_operation(10, '+', True).range, + signal.arithmetical_operation(10, "+", True).range, self._range + 10, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - signal.arithmetical_operation(10, '-', True).range, + signal.arithmetical_operation(10, "-", True).range, self._range, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - signal.arithmetical_operation(10, '*', True).range, + signal.arithmetical_operation(10, "*", True).range, self._range * 10, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - signal.arithmetical_operation(10, '/', True).range, + signal.arithmetical_operation(10, "/", True).range, self._range, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - signal.arithmetical_operation(10, '**', True).range, - self._range ** 10, - decimal=7) + signal.arithmetical_operation(10, "**", True).range, + self._range**10, + decimal=7, + ) signal = self._signal.copy() np.testing.assert_almost_equal( - signal.arithmetical_operation(self._range, '+', False).range, + signal.arithmetical_operation(self._range, "+", False).range, signal.range + self._range, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - signal.arithmetical_operation(signal, '+', False).range, + signal.arithmetical_operation(signal, "+", False).range, signal.range + signal._range, - decimal=7) + decimal=7, + ) def test_is_uniform(self): - """ - Tests :func:`colour.continuous.signal.Signal.is_uniform` method. - """ + """Test :func:`colour.continuous.signal.Signal.is_uniform` method.""" self.assertTrue(self._signal.is_uniform()) @@ -554,16 +643,14 @@ def test_is_uniform(self): self.assertFalse(signal.is_uniform()) def test_copy(self): - """ - Tests :func:`colour.continuous.signal.Signal.copy` method. - """ + """Test :func:`colour.continuous.signal.Signal.copy` method.""" self.assertIsNot(self._signal, self._signal.copy()) self.assertEqual(self._signal, self._signal.copy()) def test_signal_unpack_data(self): """ - Tests :func:`colour.continuous.signal.Signal.signal_unpack_data` + Test :meth:`colour.continuous.signal.Signal.signal_unpack_data` method. """ @@ -576,12 +663,19 @@ def test_signal_unpack_data(self): np.testing.assert_array_equal(domain, self._domain) domain, range_ = Signal.signal_unpack_data( - dict(zip(self._domain, self._range))) + self._range, dict(zip(self._domain, self._range)).keys() + ) + np.testing.assert_array_equal(domain, self._domain) + + domain, range_ = Signal.signal_unpack_data( + dict(zip(self._domain, self._range)) + ) np.testing.assert_array_equal(range_, self._range) np.testing.assert_array_equal(domain, self._domain) domain, range_ = Signal.signal_unpack_data( - Signal(self._range, self._domain)) + Signal(self._range, self._domain) + ) np.testing.assert_array_equal(range_, self._range) np.testing.assert_array_equal(domain, self._domain) @@ -589,14 +683,13 @@ def test_signal_unpack_data(self): from pandas import Series domain, range_ = Signal.signal_unpack_data( - Series(dict(zip(self._domain, self._range)))) + Series(dict(zip(self._domain, self._range))) + ) np.testing.assert_array_equal(range_, self._range) np.testing.assert_array_equal(domain, self._domain) def test_fill_nan(self): - """ - Tests :func:`colour.continuous.signal.Signal.fill_nan` method. - """ + """Test :func:`colour.continuous.signal.Signal.fill_nan` method.""" signal = self._signal.copy() @@ -605,42 +698,45 @@ def test_fill_nan(self): np.testing.assert_almost_equal( signal.fill_nan().range, np.array( - [10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0]), - decimal=7) + [10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0] + ), + decimal=7, + ) signal[3:7] = np.nan np.testing.assert_almost_equal( - signal.fill_nan(method='Constant').range, - np.array([10.0, 20.0, 30.0, 0.0, 0.0, 0.0, 0.0, 80.0, 90.0, - 100.0]), - decimal=7) + signal.fill_nan(method="Constant").range, + np.array( + [10.0, 20.0, 30.0, 0.0, 0.0, 0.0, 0.0, 80.0, 90.0, 100.0] + ), + decimal=7, + ) def test_domain_distance(self): - """ - Tests :func:`colour.continuous.signal.Signal.domain_distance` method. - """ + """Test :func:`colour.continuous.signal.Signal.domain_distance` method.""" self.assertAlmostEqual( - self._signal.domain_distance(0.5), 0.5, places=7) + self._signal.domain_distance(0.5), 0.5, places=7 + ) np.testing.assert_almost_equal( self._signal.domain_distance(np.linspace(0, 9, 10) + 0.5), np.array([0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]), - decimal=7) + decimal=7, + ) def test_to_series(self): - """ - Tests :func:`colour.continuous.signal.Signal.to_series` method. - """ + """Test :func:`colour.continuous.signal.Signal.to_series` method.""" if is_pandas_installed(): from pandas import Series self.assertEqual( Signal(self._range, self._domain).to_series().all(), - Series(dict(zip(self._domain, self._range))).all()) + Series(dict(zip(self._domain, self._range))).all(), + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/contrast/__init__.py b/colour/contrast/__init__.py index e2145c9586..b6a414da7f 100644 --- a/colour/contrast/__init__.py +++ b/colour/contrast/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ References ---------- @@ -17,23 +16,38 @@ https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2246-4-2015-PDF-E.pdf """ -from __future__ import absolute_import +from __future__ import annotations -from colour.utilities import CaseInsensitiveMapping, filter_kwargs -from .barten1999 import (optical_MTF_Barten1999, pupil_diameter_Barten1999, - sigma_Barten1999, retinal_illuminance_Barten1999, - maximum_angular_size_Barten1999, - contrast_sensitivity_function_Barten1999) +from colour.hints import FloatingOrNDArray, Literal, Union +from colour.utilities import ( + CaseInsensitiveMapping, + filter_kwargs, + validate_method, +) + +from .barten1999 import ( + optical_MTF_Barten1999, + pupil_diameter_Barten1999, + sigma_Barten1999, + retinal_illuminance_Barten1999, + maximum_angular_size_Barten1999, + contrast_sensitivity_function_Barten1999, +) __all__ = [ - 'optical_MTF_Barten1999', 'pupil_diameter_Barten1999', 'sigma_Barten1999', - 'retinal_illuminance_Barten1999', 'maximum_angular_size_Barten1999', - 'contrast_sensitivity_function_Barten1999' + "optical_MTF_Barten1999", + "pupil_diameter_Barten1999", + "sigma_Barten1999", + "retinal_illuminance_Barten1999", + "maximum_angular_size_Barten1999", + "contrast_sensitivity_function_Barten1999", ] -CONTRAST_SENSITIVITY_METHODS = CaseInsensitiveMapping({ - 'Barten 1999': contrast_sensitivity_function_Barten1999, -}) +CONTRAST_SENSITIVITY_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Barten 1999": contrast_sensitivity_function_Barten1999, + } +) CONTRAST_SENSITIVITY_METHODS.__doc__ = """ Supported contrast sensitivity methods. @@ -41,80 +55,78 @@ ---------- :cite:`Barten1999`, :cite:`Barten2003`, :cite:`Cowan2004`, :cite:`InternationalTelecommunicationUnion2015`, - -CONTRAST_SENSITIVITY_METHODS : CaseInsensitiveMapping - **{'Barten 1999'}** """ -def contrast_sensitivity_function(method='Barten 1999', **kwargs): +def contrast_sensitivity_function( + method: Union[Literal["Barten 1999"], str] = "Barten 1999", **kwargs +) -> FloatingOrNDArray: """ - Returns the contrast sensitivity :math:`S` of the human eye according to + Return the contrast sensitivity :math:`S` of the human eye according to the contrast sensitivity function (CSF) described by given method. Parameters ---------- - method : unicode, optional - **{'Barten 1999'}**, + method Computation method. Other Parameters ---------------- - E : numeric or array_like, optional + E {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, Retinal illuminance :math:`E` in Trolands. - N_max : numeric or array_like, optional - {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, - Maximum number of cycles :math:`N_{max}` over which the eye can - integrate the information. - T : numeric or array_like, optional - {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, - Integration time :math:`T` in seconds of the eye. - X_0 : numeric or array_like, optional - {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, - Angular size :math:`X_0` in degrees of the object in the x direction. - Y_0 : numeric or array_like, optional - {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, - Angular size :math:`Y_0` in degrees of the object in the y direction. - X_max : numeric or array_like, optional - {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, - Maximum angular size :math:`X_{max}` in degrees of the integration - area in the x direction. - Y_max : numeric or array_like, optional - {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, - Maximum angular size :math:`Y_{max}` in degrees of the integration - area in the y direction. - k : numeric or array_like, optional + k {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, Signal-to-noise (SNR) ratio :math:`k`. - n : numeric or array_like, optional + n {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, Quantum efficiency of the eye :math:`n`. - p : numeric or array_like, optional + N_max + {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, + Maximum number of cycles :math:`N_{max}` over which the eye can + integrate the information. + p {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, Photon conversion factor :math:`p` in :math:`photons\\div seconds\\div degrees^2\\div Trolands` that depends on the light source. - phi_0 : numeric or array_like, optional + phi_0 {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, Spectral density :math:`\\phi_0` in :math:`seconds degrees^2` of the neural noise. - sigma : numeric or array_like, optional + sigma {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, Standard deviation :math:`\\sigma` of the line-spread function resulting from the convolution of the different elements of the convolution process. - u : numeric + T + {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, + Integration time :math:`T` in seconds of the eye. + u {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, Spatial frequency :math:`u`, the cycles per degree. - u_0 : numeric or array_like, optional + u_0 {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, Spatial frequency :math:`u_0` in :math:`cycles\\div degrees` above which the lateral inhibition ceases. + X_0 + {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, + Angular size :math:`X_0` in degrees of the object in the x direction. + Y_0 + {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, + Angular size :math:`Y_0` in degrees of the object in the y direction. + X_max + {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, + Maximum angular size :math:`X_{max}` in degrees of the integration + area in the x direction. + Y_max + {:func:`colour.contrast.contrast_sensitivity_function_Barten1999`}, + Maximum angular size :math:`Y_{max}` in degrees of the integration + area in the y direction. Returns ------- - ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Contrast sensitivity :math:`S`. References @@ -130,6 +142,8 @@ def contrast_sensitivity_function(method='Barten 1999', **kwargs): 360.8691122... """ + method = validate_method(method, CONTRAST_SENSITIVITY_METHODS) + function = CONTRAST_SENSITIVITY_METHODS[method] S = function(**filter_kwargs(function, **kwargs)) @@ -137,4 +151,7 @@ def contrast_sensitivity_function(method='Barten 1999', **kwargs): return S -__all__ += ['CONTRAST_SENSITIVITY_METHODS', 'contrast_sensitivity_function'] +__all__ += [ + "CONTRAST_SENSITIVITY_METHODS", + "contrast_sensitivity_function", +] diff --git a/colour/contrast/barten1999.py b/colour/contrast/barten1999.py index 17f768248d..8d0b00826a 100644 --- a/colour/contrast/barten1999.py +++ b/colour/contrast/barten1999.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Barten (1999) Contrast Sensitivity Function =========================================== -Defines *Barten (1999)* contrast sensitivity function: +Defines the *Barten (1999)* contrast sensitivity function: - :func:`colour.contrast.contrast_sensitivity_function_Barten1999` @@ -24,43 +23,54 @@ https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2246-4-2015-PDF-E.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +from colour.hints import ( + Boolean, + FloatingOrArrayLike, + FloatingOrNDArray, + Optional, +) from colour.utilities import as_float_array, as_float -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'optical_MTF_Barten1999', 'pupil_diameter_Barten1999', 'sigma_Barten1999', - 'retinal_illuminance_Barten1999', 'maximum_angular_size_Barten1999', - 'contrast_sensitivity_function_Barten1999' + "optical_MTF_Barten1999", + "pupil_diameter_Barten1999", + "sigma_Barten1999", + "retinal_illuminance_Barten1999", + "maximum_angular_size_Barten1999", + "contrast_sensitivity_function_Barten1999", ] -def optical_MTF_Barten1999(u, sigma=0.01): +def optical_MTF_Barten1999( + u: FloatingOrArrayLike, sigma: FloatingOrArrayLike = 0.01 +) -> FloatingOrNDArray: """ - Returns the optical modulation transfer function (MTF) :math:`M_{opt}` of + Return the optical modulation transfer function (MTF) :math:`M_{opt}` of the eye using *Barten (1999)* method. Parameters ---------- - u : numeric or array_like + u Spatial frequency :math:`u`, the cycles per degree. - sigma : numeric or array_like, optional + sigma Standard deviation :math:`\\sigma` of the line-spread function resulting from the convolution of the different elements of the convolution process. Returns ------- - numeric or array_like + :class:`numpy.floating` or :class:`numpy.ndarray` Optical modulation transfer function (MTF) :math:`M_{opt}` of the eye. References @@ -77,23 +87,32 @@ def optical_MTF_Barten1999(u, sigma=0.01): u = as_float_array(u) sigma = as_float_array(sigma) - return as_float(np.exp(-2 * np.pi ** 2 * sigma ** 2 * u ** 2)) + return as_float(np.exp(-2 * np.pi**2 * sigma**2 * u**2)) -def pupil_diameter_Barten1999(L, X_0=60, Y_0=None): +def pupil_diameter_Barten1999( + L: FloatingOrArrayLike, + X_0: FloatingOrArrayLike = 60, + Y_0: Optional[FloatingOrArrayLike] = None, +) -> FloatingOrNDArray: """ - Returns the pupil diameter for given luminance and object or stimulus + Return the pupil diameter for given luminance and object or stimulus angular size using *Barten (1999)* method. Parameters ---------- - L : numeric or array_like + L Average luminance :math:`L` in :math:`cd/m^2`. - X_0 : numeric or array_like, optional + X_0 Angular size of the object :math:`X_0` in degrees in the x direction. - Y_0 : numeric or array_like, optional + Y_0 Angular size of the object :math:`X_0` in degrees in the y direction. + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + Pupil diameter. + References ---------- :cite:`Barten1999`, :cite:`Barten2003`, :cite:`Cowan2004`, @@ -109,12 +128,16 @@ def pupil_diameter_Barten1999(L, X_0=60, Y_0=None): X_0 = as_float_array(X_0) Y_0 = X_0 if Y_0 is None else as_float_array(Y_0) - return as_float(5 - 3 * np.tanh(0.4 * np.log(L * X_0 * Y_0 / 40 ** 2))) + return as_float(5 - 3 * np.tanh(0.4 * np.log(L * X_0 * Y_0 / 40**2))) -def sigma_Barten1999(sigma_0=0.5 / 60, C_ab=0.08 / 60, d=2.1): +def sigma_Barten1999( + sigma_0: FloatingOrArrayLike = 0.5 / 60, + C_ab: FloatingOrArrayLike = 0.08 / 60, + d: FloatingOrArrayLike = 2.1, +) -> FloatingOrNDArray: """ - Returns the standard deviation :math:`\\sigma` of the line-spread function + Return the standard deviation :math:`\\sigma` of the line-spread function resulting from the convolution of the different elements of the convolution process using *Barten (1999)* method. @@ -126,17 +149,17 @@ def sigma_Barten1999(sigma_0=0.5 / 60, C_ab=0.08 / 60, d=2.1): Parameters ---------- - sigma_0 : numeric or array_like, optional + sigma_0 Constant :math:`\\sigma_{0}` in degrees. - C_ab : numeric or array_like, optional + C_ab Spherical aberration of the eye :math:`C_{ab}` in :math:`degrees\\div mm`. - d : numeric or array_like, optional + d Pupil diameter :math:`d` in millimeters. Returns ------- - ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Standard deviation :math:`\\sigma` of the line-spread function resulting from the convolution of the different elements of the convolution process. @@ -164,28 +187,31 @@ def sigma_Barten1999(sigma_0=0.5 / 60, C_ab=0.08 / 60, d=2.1): C_ab = as_float_array(C_ab) d = as_float_array(d) - return as_float(np.sqrt(sigma_0 ** 2 + (C_ab * d) ** 2)) + return as_float(np.sqrt(sigma_0**2 + (C_ab * d) ** 2)) def retinal_illuminance_Barten1999( - L, d=2.1, apply_stiles_crawford_effect_correction=True): + L: FloatingOrArrayLike, + d: FloatingOrArrayLike = 2.1, + apply_stiles_crawford_effect_correction: Boolean = True, +) -> FloatingOrNDArray: """ - Returns the retinal illuminance :math:`E` in Trolands for given average + Return the retinal illuminance :math:`E` in Trolands for given average luminance :math:`L` and pupil diameter :math:`d` using *Barten (1999)* method. Parameters ---------- - L : numeric or array_like + L Average luminance :math:`L` in :math:`cd/m^2`. - d : numeric or array_like, optional + d Pupil diameter :math:`d` in millimeters. - apply_stiles_crawford_effect_correction : bool, optional + apply_stiles_crawford_effect_correction Whether to apply the correction for *Stiles-Crawford* effect. Returns ------- - ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Retinal illuminance :math:`E` in Trolands. Notes @@ -211,35 +237,40 @@ def retinal_illuminance_Barten1999( d = as_float_array(d) L = as_float_array(L) - E = (np.pi * d ** 2) / 4 * L + E = (np.pi * d**2) / 4 * L if apply_stiles_crawford_effect_correction: - E *= (1 - (d / 9.7) ** 2 + (d / 12.4) ** 4) + E *= 1 - (d / 9.7) ** 2 + (d / 12.4) ** 4 - return E + return as_float(E) -def maximum_angular_size_Barten1999(u, X_0=60, X_max=12, N_max=15): +def maximum_angular_size_Barten1999( + u: FloatingOrArrayLike, + X_0: FloatingOrArrayLike = 60, + X_max: FloatingOrArrayLike = 12, + N_max: FloatingOrArrayLike = 15, +) -> FloatingOrNDArray: """ - Returns the maximum angular size :math:`X` of the object considered using + Return the maximum angular size :math:`X` of the object considered using *Barten (1999)* method. Parameters ---------- - u : numeric + u Spatial frequency :math:`u`, the cycles per degree. - X_0 : numeric or array_like, optional + X_0 Angular size :math:`X_0` in degrees of the object in the x direction. - X_max : numeric or array_like, optional + X_max Maximum angular size :math:`X_{max}` in degrees of the integration area in the x direction. - N_max : numeric or array_like, optional + N_max Maximum number of cycles :math:`N_{max}` over which the eye can integrate the information. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Maximum angular size :math:`X` of the object considered. References @@ -258,27 +289,29 @@ def maximum_angular_size_Barten1999(u, X_0=60, X_max=12, N_max=15): X_max = as_float_array(X_max) N_max = as_float_array(N_max) - return (1 / X_0 ** 2 + 1 / X_max ** 2 + u ** 2 / N_max ** 2) ** -0.5 - - -def contrast_sensitivity_function_Barten1999(u, - sigma=sigma_Barten1999( - 0.5 / 60, 0.08 / 60, 2.1), - k=3.0, - T=0.1, - X_0=60, - Y_0=None, - X_max=12, - Y_max=None, - N_max=15, - n=0.03, - p=1.2274 * 10 ** 6, - E=retinal_illuminance_Barten1999( - 20, 2.1), - phi_0=3 * 10 ** -8, - u_0=7): + return as_float( + (1 / X_0**2 + 1 / X_max**2 + u**2 / N_max**2) ** -0.5 + ) + + +def contrast_sensitivity_function_Barten1999( + u: FloatingOrArrayLike, + sigma: FloatingOrArrayLike = sigma_Barten1999(0.5 / 60, 0.08 / 60, 2.1), + k: FloatingOrArrayLike = 3.0, + T: FloatingOrArrayLike = 0.1, + X_0: FloatingOrArrayLike = 60, + Y_0: Optional[FloatingOrArrayLike] = None, + X_max: FloatingOrArrayLike = 12, + Y_max: Optional[FloatingOrArrayLike] = None, + N_max: FloatingOrArrayLike = 15, + n: FloatingOrArrayLike = 0.03, + p: FloatingOrArrayLike = 1.2274 * 10**6, + E: FloatingOrArrayLike = retinal_illuminance_Barten1999(20, 2.1), + phi_0: FloatingOrArrayLike = 3 * 10**-8, + u_0: FloatingOrArrayLike = 7, +) -> FloatingOrNDArray: """ - Returns the contrast sensitivity :math:`S` of the human eye according to + Return the contrast sensitivity :math:`S` of the human eye according to the contrast sensitivity function (CSF) described by *Barten (1999)*. Contrast sensitivity is defined as the inverse of the modulation threshold @@ -292,47 +325,47 @@ def contrast_sensitivity_function_Barten1999(u, Parameters ---------- - u : numeric + u Spatial frequency :math:`u`, the cycles per degree. - sigma : numeric or array_like, optional + sigma Standard deviation :math:`\\sigma` of the line-spread function resulting from the convolution of the different elements of the convolution process. - k : numeric or array_like, optional + k Signal-to-noise (SNR) ratio :math:`k`. - T : numeric or array_like, optional + T Integration time :math:`T` in seconds of the eye. - X_0 : numeric or array_like, optional + X_0 Angular size :math:`X_0` in degrees of the object in the x direction. - Y_0 : numeric or array_like, optional + Y_0 Angular size :math:`Y_0` in degrees of the object in the y direction. - X_max : numeric or array_like, optional + X_max Maximum angular size :math:`X_{max}` in degrees of the integration area in the x direction. - Y_max : numeric or array_like, optional + Y_max Maximum angular size :math:`Y_{max}` in degrees of the integration area in the y direction. - N_max : numeric or array_like, optional + N_max Maximum number of cycles :math:`N_{max}` over which the eye can integrate the information. - n : numeric or array_like, optional + n Quantum efficiency of the eye :math:`n`. - p : numeric or array_like, optional + p Photon conversion factor :math:`p` in :math:`photons\\div seconds\\div degrees^2\\div Trolands` that depends on the light source. - E : numeric or array_like, optional + E Retinal illuminance :math:`E` in Trolands. - phi_0 : numeric or array_like, optional + phi_0 Spectral density :math:`\\phi_0` in :math:`seconds degrees^2` of the neural noise. - u_0 : numeric or array_like, optional + u_0 Spatial frequency :math:`u_0` in :math:`cycles\\div degrees` above which the lateral inhibition ceases. Returns ------- - ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Contrast sensitivity :math:`S`. Warnings @@ -412,7 +445,7 @@ def contrast_sensitivity_function_Barten1999(u, ... E=E, ... **settings_BT2246) ... ), 0, disp=False)[0]) - ... return as_float(np.array(maximised_spatial_frequency)) + ... return as_float(np.array(maximised_spatial_frequency)) >>> >>> L = np.logspace(np.log10(0.01), np.log10(100), 10) >>> X_0 = Y_0 = 60 @@ -424,10 +457,10 @@ def contrast_sensitivity_function_Barten1999(u, ... u=u, sigma=sigma, E=E, X_0=X_0, Y_0=Y_0, **settings_BT2246) ... * 2 * (1/ 1.27)) ... # doctest: +ELLIPSIS - array([ 0.0207396..., 0.0134885..., 0.0096063..., 0.0077299..., \ -0.0068983..., - 0.0065057..., 0.0062712..., 0.0061198..., 0.0060365..., \ -0.0059984...]) + array([ 0.0207396..., 0.0133019..., 0.0089256..., 0.0064202..., \ +0.0050275..., + 0.0041933..., 0.0035573..., 0.0030095..., 0.0025803..., \ +0.0022897...]) """ u = as_float_array(u) @@ -446,10 +479,16 @@ def contrast_sensitivity_function_Barten1999(u, M_opt = optical_MTF_Barten1999(u, sigma) - M_as = 1 / (maximum_angular_size_Barten1999(u, X_0, X_max, N_max) * - maximum_angular_size_Barten1999(u, Y_0, Y_max, N_max)) - - S = (M_opt / k) / np.sqrt(2 / T * M_as * (1 / (n * p * E) + phi_0 / - (1 - np.exp(-(u / u_0) ** 2)))) + M_as = 1 / ( + maximum_angular_size_Barten1999(u, X_0, X_max, N_max) + * maximum_angular_size_Barten1999(u, Y_0, Y_max, N_max) + ) + + S = (M_opt / k) / np.sqrt( + 2 + / T + * M_as + * (1 / (n * p * E) + phi_0 / (1 - np.exp(-((u / u_0) ** 2)))) + ) return as_float(S) diff --git a/colour/contrast/tests/__init__.py b/colour/contrast/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/contrast/tests/__init__.py +++ b/colour/contrast/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/contrast/tests/test_barten1999.py b/colour/contrast/tests/test_barten1999.py index 40e3be6c2a..f7e1e83658 100644 --- a/colour/contrast/tests/test_barten1999.py +++ b/colour/contrast/tests/test_barten1999.py @@ -1,59 +1,63 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.contrast.barten1999` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.contrast.barten1999` module.""" import numpy as np import unittest from itertools import permutations -from colour.contrast import (optical_MTF_Barten1999, pupil_diameter_Barten1999, - sigma_Barten1999, retinal_illuminance_Barten1999, - maximum_angular_size_Barten1999, - contrast_sensitivity_function_Barten1999) +from colour.contrast import ( + optical_MTF_Barten1999, + pupil_diameter_Barten1999, + sigma_Barten1999, + retinal_illuminance_Barten1999, + maximum_angular_size_Barten1999, + contrast_sensitivity_function_Barten1999, +) from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestOpticalMTFBarten1999', 'TestPupilDiameterBarten1999', - 'TestSigmaBarten1999', 'TestRetinalIlluminanceBarten1999', - 'TestMaximumAngularSizeBarten1999', - 'TestContrastSensitivityFunctionBarten1999' + "TestOpticalMTFBarten1999", + "TestPupilDiameterBarten1999", + "TestSigmaBarten1999", + "TestRetinalIlluminanceBarten1999", + "TestMaximumAngularSizeBarten1999", + "TestContrastSensitivityFunctionBarten1999", ] class TestOpticalMTFBarten1999(unittest.TestCase): """ - Defines :func:`colour.contrast.barten1999.optical_MTF_Barten1999` + Define :func:`colour.contrast.barten1999.optical_MTF_Barten1999` definition unit tests methods. """ def test_optical_MTF_Barten1999(self): """ - Tests :func:`colour.contrast.barten1999.optical_MTF_Barten1999` + Test :func:`colour.contrast.barten1999.optical_MTF_Barten1999` definition. """ np.testing.assert_almost_equal( - optical_MTF_Barten1999(4, 0.01), 0.968910791191297, decimal=7) + optical_MTF_Barten1999(4, 0.01), 0.968910791191297, decimal=7 + ) np.testing.assert_almost_equal( - optical_MTF_Barten1999(8, 0.01), 0.881323136669471, decimal=7) + optical_MTF_Barten1999(8, 0.01), 0.881323136669471, decimal=7 + ) np.testing.assert_almost_equal( - optical_MTF_Barten1999(4, 0.05), 0.454040738727245, decimal=7) + optical_MTF_Barten1999(4, 0.05), 0.454040738727245, decimal=7 + ) def test_n_dimensional_optical_MTF_Barten1999(self): """ - Tests :func:`colour.contrast.barten1999.optical_MTF_Barten1999` + Test :func:`colour.contrast.barten1999.optical_MTF_Barten1999` definition n-dimensional support. """ @@ -65,18 +69,20 @@ def test_n_dimensional_optical_MTF_Barten1999(self): sigma = np.tile(sigma, (6, 1)) M_opt = np.tile(M_opt, (6, 1)) np.testing.assert_almost_equal( - optical_MTF_Barten1999(u, sigma), M_opt, decimal=7) + optical_MTF_Barten1999(u, sigma), M_opt, decimal=7 + ) u = np.reshape(u, (2, 3, 3)) sigma = np.reshape(sigma, (2, 3, 3)) M_opt = np.reshape(M_opt, (2, 3, 3)) np.testing.assert_almost_equal( - optical_MTF_Barten1999(u, sigma), M_opt, decimal=7) + optical_MTF_Barten1999(u, sigma), M_opt, decimal=7 + ) @ignore_numpy_errors def test_nan_optical_MTF_Barten1999(self): """ - Tests :func:`colour.contrast.barten1999.optical_MTF_Barten1999` + Test :func:`colour.contrast.barten1999.optical_MTF_Barten1999` definition nan support. """ @@ -88,30 +94,31 @@ def test_nan_optical_MTF_Barten1999(self): class TestPupilDiameterBarten1999(unittest.TestCase): """ - Defines :func:`colour.contrast.barten1999.pupil_diameter_Barten1999` + Define :func:`colour.contrast.barten1999.pupil_diameter_Barten1999` definition unit tests methods. """ def test_pupil_diameter_Barten1999(self): """ - Tests :func:`colour.contrast.barten1999.pupil_diameter_Barten1999` + Test :func:`colour.contrast.barten1999.pupil_diameter_Barten1999` definition. """ np.testing.assert_almost_equal( - pupil_diameter_Barten1999(20, 60), 2.272517118855717, decimal=7) + pupil_diameter_Barten1999(20, 60), 2.272517118855717, decimal=7 + ) np.testing.assert_almost_equal( - pupil_diameter_Barten1999(0.2, 600), 2.272517118855717, decimal=7) + pupil_diameter_Barten1999(0.2, 600), 2.272517118855717, decimal=7 + ) np.testing.assert_almost_equal( - pupil_diameter_Barten1999(20, 60, 30), - 2.459028745178825, - decimal=7) + pupil_diameter_Barten1999(20, 60, 30), 2.459028745178825, decimal=7 + ) def test_n_dimensional_pupil_diameter_Barten1999(self): """ - Tests :func:`colour.contrast.barten1999.pupil_diameter_Barten1999` + Test :func:`colour.contrast.barten1999.pupil_diameter_Barten1999` definition n-dimensional support. """ @@ -124,18 +131,20 @@ def test_n_dimensional_pupil_diameter_Barten1999(self): X_0 = np.tile(X_0, (6, 1)) d = np.tile(d, (6, 1)) np.testing.assert_almost_equal( - pupil_diameter_Barten1999(L, X_0, Y_0), d, decimal=7) + pupil_diameter_Barten1999(L, X_0, Y_0), d, decimal=7 + ) L = np.reshape(L, (2, 3, 3)) X_0 = np.reshape(X_0, (2, 3, 3)) d = np.reshape(d, (2, 3, 3)) np.testing.assert_almost_equal( - pupil_diameter_Barten1999(L, X_0, Y_0), d, decimal=7) + pupil_diameter_Barten1999(L, X_0, Y_0), d, decimal=7 + ) @ignore_numpy_errors def test_nan_pupil_diameter_Barten1999(self): """ - Tests :func:`colour.contrast.barten1999.pupil_diameter_Barten1999` + Test :func:`colour.contrast.barten1999.pupil_diameter_Barten1999` definition nan support. """ @@ -143,43 +152,46 @@ def test_nan_pupil_diameter_Barten1999(self): cases = set(permutations(cases * 3, r=3)) for case in cases: pupil_diameter_Barten1999( - np.array(case), np.array(case), np.array(case)) + np.array(case), np.array(case), np.array(case) + ) class TestSigmaBarten1999(unittest.TestCase): """ - Defines :func:`colour.contrast.barten1999.sigma_Barten1999` definition unit + Define :func:`colour.contrast.barten1999.sigma_Barten1999` definition unit tests methods. """ def test_sigma_Barten1999(self): - """ - Tests :func:`colour.contrast.barten1999.sigma_Barten1999` definition. - """ + """Test :func:`colour.contrast.barten1999.sigma_Barten1999` definition.""" np.testing.assert_almost_equal( sigma_Barten1999(0.5 / 60, 0.08 / 60, 2.1), 0.008791157173231, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( sigma_Barten1999(0.75 / 60, 0.08 / 60, 2.1), 0.012809761902549, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( sigma_Barten1999(0.5 / 60, 0.16 / 60, 2.1), 0.010040141654601, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( sigma_Barten1999(0.5 / 60, 0.08 / 60, 2.5), 0.008975274678558, - decimal=7) + decimal=7, + ) def test_n_dimensional_sigma_Barten1999(self): """ - Tests :func:`colour.contrast.barten1999.sigma_Barten1999` definition + Test :func:`colour.contrast.barten1999.sigma_Barten1999` definition n-dimensional support. """ @@ -192,18 +204,20 @@ def test_n_dimensional_sigma_Barten1999(self): C_ab = np.tile(C_ab, (6, 1)) sigma = np.tile(sigma, (6, 1)) np.testing.assert_almost_equal( - sigma_Barten1999(sigma_0, C_ab, d), sigma, decimal=7) + sigma_Barten1999(sigma_0, C_ab, d), sigma, decimal=7 + ) sigma_0 = np.reshape(sigma_0, (2, 3, 3)) C_ab = np.reshape(C_ab, (2, 3, 3)) sigma = np.reshape(sigma, (2, 3, 3)) np.testing.assert_almost_equal( - sigma_Barten1999(sigma_0, C_ab, d), sigma, decimal=7) + sigma_Barten1999(sigma_0, C_ab, d), sigma, decimal=7 + ) @ignore_numpy_errors def test_nan_sigma_Barten1999(self): """ - Tests :func:`colour.contrast.barten1999.sigma_Barten1999` + Test :func:`colour.contrast.barten1999.sigma_Barten1999` definition nan support. """ @@ -215,34 +229,37 @@ def test_nan_sigma_Barten1999(self): class TestRetinalIlluminanceBarten1999(unittest.TestCase): """ - Defines :func:`colour.contrast.barten1999.retinal_illuminance_Barten1999` + Define :func:`colour.contrast.barten1999.retinal_illuminance_Barten1999` definition unit tests methods. """ def test_retinal_illuminance_Barten1999(self): """ - Tests :func:`colour.contrast.barten1999.retinal_illuminance_Barten1999` + Test :func:`colour.contrast.barten1999.retinal_illuminance_Barten1999` definition. """ np.testing.assert_almost_equal( retinal_illuminance_Barten1999(20, 2.1, True), 66.082316060529919, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( retinal_illuminance_Barten1999(20, 2.5, True), 91.815644777503664, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( retinal_illuminance_Barten1999(20, 2.1, False), 69.272118011654939, - decimal=7) + decimal=7, + ) def test_n_dimensional_retinal_illuminance_Barten1999(self): """ - Tests :func:`colour.contrast.barten1999.retinal_illuminance_Barten1999` + Test :func:`colour.contrast.barten1999.retinal_illuminance_Barten1999` definition n-dimensional support. """ @@ -254,18 +271,20 @@ def test_n_dimensional_retinal_illuminance_Barten1999(self): d = np.tile(d, (6, 1)) E = np.tile(E, (6, 1)) np.testing.assert_almost_equal( - retinal_illuminance_Barten1999(L, d), E, decimal=7) + retinal_illuminance_Barten1999(L, d), E, decimal=7 + ) L = np.reshape(L, (2, 3, 3)) d = np.reshape(d, (2, 3, 3)) E = np.reshape(E, (2, 3, 3)) np.testing.assert_almost_equal( - retinal_illuminance_Barten1999(L, d), E, decimal=7) + retinal_illuminance_Barten1999(L, d), E, decimal=7 + ) @ignore_numpy_errors def test_nan_retinal_illuminance_Barten1999(self): """ - Tests :func:`colour.contrast.barten1999.retinal_illuminance_Barten1999` + Test :func:`colour.contrast.barten1999.retinal_illuminance_Barten1999` definition nan support. """ @@ -277,44 +296,49 @@ def test_nan_retinal_illuminance_Barten1999(self): class TestMaximumAngularSizeBarten1999(unittest.TestCase): """ - Defines :func:`colour.contrast.barten1999.maximum_angular_size_Barten1999` + Define :func:`colour.contrast.barten1999.maximum_angular_size_Barten1999` definition unit tests methods. """ def test_maximum_angular_size_Barten1999(self): """ - Tests :func:`colour.contrast.barten1999.\ + Test :func:`colour.contrast.barten1999.\ maximum_angular_size_Barten1999` definition. """ np.testing.assert_almost_equal( maximum_angular_size_Barten1999(4, 60, 12, 15), 3.572948005052482, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( maximum_angular_size_Barten1999(8, 60, 12, 15), 1.851640199545103, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( maximum_angular_size_Barten1999(4, 120, 12, 15), 3.577708763999663, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( maximum_angular_size_Barten1999(4, 60, 24, 15), 3.698001308168194, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( maximum_angular_size_Barten1999(4, 60, 12, 30), 6.324555320336758, - decimal=7) + decimal=7, + ) def test_n_dimensional_maximum_angular_size_Barten1999(self): """ - Tests :func:`colour.contrast.barten1999.\ + Test :func:`colour.contrast.barten1999.\ maximum_angular_size_Barten1999` definition n-dimensional support. """ @@ -328,22 +352,20 @@ def test_n_dimensional_maximum_angular_size_Barten1999(self): X_0 = np.tile(X_0, (6, 1)) X = np.tile(X, (6, 1)) np.testing.assert_almost_equal( - maximum_angular_size_Barten1999(u, X_0, X_max, N_max), - X, - decimal=7) + maximum_angular_size_Barten1999(u, X_0, X_max, N_max), X, decimal=7 + ) u = np.reshape(u, (2, 3, 3)) X_0 = np.reshape(X_0, (2, 3, 3)) X = np.reshape(X, (2, 3, 3)) np.testing.assert_almost_equal( - maximum_angular_size_Barten1999(u, X_0, X_max, N_max), - X, - decimal=7) + maximum_angular_size_Barten1999(u, X_0, X_max, N_max), X, decimal=7 + ) @ignore_numpy_errors def test_nan_maximum_angular_size_Barten1999(self): """ - Tests :func:`colour.contrast.barten1999.\ + Test :func:`colour.contrast.barten1999.\ maximum_angular_size_Barten1999` definition nan support. """ @@ -351,18 +373,19 @@ def test_nan_maximum_angular_size_Barten1999(self): cases = set(permutations(cases * 3, r=3)) for case in cases: maximum_angular_size_Barten1999( - np.array(case), np.array(case), np.array(case), np.array(case)) + np.array(case), np.array(case), np.array(case), np.array(case) + ) class TestContrastSensitivityFunctionBarten1999(unittest.TestCase): """ - Defines :func:`colour.contrast.barten1999.\ + Define :func:`colour.contrast.barten1999.\ contrast_sensitivity_function_Barten1999` definition unit tests methods. """ def test_contrast_sensitivity_function_Barten1999(self): """ - Tests :func:`colour.contrast.barten1999.\ + Test :func:`colour.contrast.barten1999.\ contrast_sensitivity_function_Barten1999` definition. """ @@ -375,9 +398,11 @@ def test_contrast_sensitivity_function_Barten1999(self): X_max=12, Y_0=60, Y_max=12, - p=1.2 * 10 ** 6), + p=1.2 * 10**6, + ), 352.761342126727020, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( contrast_sensitivity_function_Barten1999( @@ -388,9 +413,11 @@ def test_contrast_sensitivity_function_Barten1999(self): X_max=12, Y_0=60, Y_max=12, - p=1.2 * 10 ** 6), + p=1.2 * 10**6, + ), 177.706338840717340, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( contrast_sensitivity_function_Barten1999( @@ -401,9 +428,11 @@ def test_contrast_sensitivity_function_Barten1999(self): X_max=12, Y_0=60, Y_max=12, - p=1.2 * 10 ** 6), + p=1.2 * 10**6, + ), 320.872401634215750, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( contrast_sensitivity_function_Barten1999( @@ -414,9 +443,11 @@ def test_contrast_sensitivity_function_Barten1999(self): X_max=12, Y_0=60, Y_max=12, - p=1.2 * 10 ** 6), + p=1.2 * 10**6, + ), 455.171315756946400, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( contrast_sensitivity_function_Barten1999( @@ -427,9 +458,11 @@ def test_contrast_sensitivity_function_Barten1999(self): X_max=12, Y_0=60, Y_max=12, - p=1.2 * 10 ** 6), + p=1.2 * 10**6, + ), 352.996281545740660, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( contrast_sensitivity_function_Barten1999( @@ -440,9 +473,11 @@ def test_contrast_sensitivity_function_Barten1999(self): X_max=24, Y_0=60, Y_max=12, - p=1.2 * 10 ** 6), + p=1.2 * 10**6, + ), 358.881580104493650, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( contrast_sensitivity_function_Barten1999( @@ -453,7 +488,8 @@ def test_contrast_sensitivity_function_Barten1999(self): X_max=12, Y_0=60, Y_max=12, - p=1.2 * 10 ** 6), + p=1.2 * 10**6, + ), contrast_sensitivity_function_Barten1999( u=4, sigma=0.01, @@ -462,8 +498,10 @@ def test_contrast_sensitivity_function_Barten1999(self): X_max=12, Y_0=240, Y_max=12, - p=1.2 * 10 ** 6), - decimal=7) + p=1.2 * 10**6, + ), + decimal=7, + ) np.testing.assert_almost_equal( contrast_sensitivity_function_Barten1999( @@ -474,13 +512,15 @@ def test_contrast_sensitivity_function_Barten1999(self): X_max=12, Y_0=60, Y_max=12, - p=1.4 * 10 ** 6), + p=1.4 * 10**6, + ), 374.791328640476140, - decimal=7) + decimal=7, + ) def test_n_dimensional_contrast_sensitivity_function_Barten1999(self): """ - Tests :func:`colour.contrast.barten1999.\ + Test :func:`colour.contrast.barten1999.\ contrast_sensitivity_function_Barten1999` definition n-dimensional support. """ @@ -489,30 +529,35 @@ def test_n_dimensional_contrast_sensitivity_function_Barten1999(self): E = np.array([0.65, 90, 1500]) X_0 = np.array([60, 120, 240]) S = contrast_sensitivity_function_Barten1999( - u=u, sigma=sigma, E=E, X_0=X_0) + u=u, sigma=sigma, E=E, X_0=X_0 + ) u = np.tile(u, (6, 1)) E = np.tile(E, (6, 1)) S = np.tile(S, (6, 1)) np.testing.assert_almost_equal( contrast_sensitivity_function_Barten1999( - u=u, sigma=sigma, E=E, X_0=X_0), + u=u, sigma=sigma, E=E, X_0=X_0 + ), S, - decimal=7) + decimal=7, + ) u = np.reshape(u, (2, 3, 3)) E = np.reshape(E, (2, 3, 3)) S = np.reshape(S, (2, 3, 3)) np.testing.assert_almost_equal( contrast_sensitivity_function_Barten1999( - u=u, sigma=sigma, E=E, X_0=X_0), + u=u, sigma=sigma, E=E, X_0=X_0 + ), S, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_contrast_sensitivity_function_Barten1999(self): """ - Tests :func:`colour.contrast.barten1999.\ + Test :func:`colour.contrast.barten1999.\ contrast_sensitivity_function_Barten1999` definition nan support. """ @@ -523,8 +568,9 @@ def test_nan_contrast_sensitivity_function_Barten1999(self): u=np.array(case), sigma=np.array(case), E=np.array(case), - X_0=np.array(case)) + X_0=np.array(case), + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/corresponding/__init__.py b/colour/corresponding/__init__.py index de963906a5..0934446768 100644 --- a/colour/corresponding/__init__.py +++ b/colour/corresponding/__init__.py @@ -1,26 +1,27 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .datasets import * # noqa from . import datasets -from .prediction import (CorrespondingColourDataset, - CorrespondingChromaticitiesPrediction, - corresponding_chromaticities_prediction_CIE1994, - corresponding_chromaticities_prediction_CMCCAT2000, - corresponding_chromaticities_prediction_Fairchild1990, - corresponding_chromaticities_prediction_VonKries, - CORRESPONDING_CHROMATICITIES_PREDICTION_MODELS, - corresponding_chromaticities_prediction) +from .prediction import ( + CorrespondingColourDataset, + CorrespondingChromaticitiesPrediction, + corresponding_chromaticities_prediction_CIE1994, + corresponding_chromaticities_prediction_CMCCAT2000, + corresponding_chromaticities_prediction_Fairchild1990, + corresponding_chromaticities_prediction_VonKries, + corresponding_chromaticities_prediction_Zhai2018, + CORRESPONDING_CHROMATICITIES_PREDICTION_MODELS, + corresponding_chromaticities_prediction, +) __all__ = [] __all__ += datasets.__all__ __all__ += [ - 'CorrespondingColourDataset', 'CorrespondingChromaticitiesPrediction', - 'corresponding_chromaticities_prediction_CIE1994', - 'corresponding_chromaticities_prediction_CMCCAT2000', - 'corresponding_chromaticities_prediction_Fairchild1990', - 'corresponding_chromaticities_prediction_VonKries', - 'CORRESPONDING_CHROMATICITIES_PREDICTION_MODELS', - 'corresponding_chromaticities_prediction' + "CorrespondingColourDataset", + "CorrespondingChromaticitiesPrediction", + "corresponding_chromaticities_prediction_CIE1994", + "corresponding_chromaticities_prediction_CMCCAT2000", + "corresponding_chromaticities_prediction_Fairchild1990", + "corresponding_chromaticities_prediction_VonKries", + "corresponding_chromaticities_prediction_Zhai2018", + "CORRESPONDING_CHROMATICITIES_PREDICTION_MODELS", + "corresponding_chromaticities_prediction", ] diff --git a/colour/corresponding/datasets/__init__.py b/colour/corresponding/datasets/__init__.py index 370dddee5f..2acba75657 100644 --- a/colour/corresponding/datasets/__init__.py +++ b/colour/corresponding/datasets/__init__.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - -from .breneman1987 import (BRENEMAN_EXPERIMENTS, - BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES) +from .breneman1987 import ( + BRENEMAN_EXPERIMENTS, + BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES, +) __all__ = [ - 'BRENEMAN_EXPERIMENTS', 'BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES' + "BRENEMAN_EXPERIMENTS", + "BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES", ] diff --git a/colour/corresponding/datasets/breneman1987.py b/colour/corresponding/datasets/breneman1987.py index 56121e82c5..bc03645ed6 100644 --- a/colour/corresponding/datasets/breneman1987.py +++ b/colour/corresponding/datasets/breneman1987.py @@ -1,9 +1,9 @@ -# -*- coding: utf-8 -*- """ Breneman Corresponding Chromaticities Dataset ============================================= -Defines *Breneman (1987)* results for corresponding chromaticities experiments. +Defines the *Breneman (1987)* results for corresponding chromaticities +experiments. References ---------- @@ -13,158 +13,175 @@ doi:10.1364/JOSAA.4.001115 """ -from __future__ import division, unicode_literals - import numpy as np from collections import namedtuple -from colour.utilities.documentation import (DocstringDict, - is_documentation_building) +from colour.utilities.documentation import ( + DocstringDict, + is_documentation_building, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'BrenemanExperimentResult', 'PrimariesChromaticityCoordinates', - 'BRENEMAN_EXPERIMENT_1_RESULTS', 'BRENEMAN_EXPERIMENT_2_RESULTS', - 'BRENEMAN_EXPERIMENT_3_RESULTS', 'BRENEMAN_EXPERIMENT_4_RESULTS', - 'BRENEMAN_EXPERIMENT_5_RESULTS', 'BRENEMAN_EXPERIMENT_6_RESULTS', - 'BRENEMAN_EXPERIMENT_7_RESULTS', 'BRENEMAN_EXPERIMENT_10_RESULTS', - 'BRENEMAN_EXPERIMENT_8_RESULTS', 'BRENEMAN_EXPERIMENT_9_RESULTS', - 'BRENEMAN_EXPERIMENT_11_RESULTS', 'BRENEMAN_EXPERIMENT_12_RESULTS', - 'BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES', 'BRENEMAN_EXPERIMENTS' + "BrenemanExperimentResult", + "PrimariesChromaticityCoordinates", + "BRENEMAN_EXPERIMENT_1_RESULTS", + "BRENEMAN_EXPERIMENT_2_RESULTS", + "BRENEMAN_EXPERIMENT_3_RESULTS", + "BRENEMAN_EXPERIMENT_4_RESULTS", + "BRENEMAN_EXPERIMENT_5_RESULTS", + "BRENEMAN_EXPERIMENT_6_RESULTS", + "BRENEMAN_EXPERIMENT_7_RESULTS", + "BRENEMAN_EXPERIMENT_10_RESULTS", + "BRENEMAN_EXPERIMENT_8_RESULTS", + "BRENEMAN_EXPERIMENT_9_RESULTS", + "BRENEMAN_EXPERIMENT_11_RESULTS", + "BRENEMAN_EXPERIMENT_12_RESULTS", + "BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES", + "BRENEMAN_EXPERIMENTS", ] class BrenemanExperimentResult( - namedtuple('BrenemanExperimentResult', - ('name', 'uv_t', 'uv_m', 's_uv', 'd_uv_i', 'd_uv_g'))): + namedtuple( + "BrenemanExperimentResult", + ("name", "uv_t", "uv_m", "s_uv", "d_uv_i", "d_uv_g"), + ) +): """ Experiment result. Parameters ---------- - name : unicode + name Test colour name. - uv_t : numeric + uv_t Chromaticity coordinates :math:`uv_t^p` of test colour. - uv_m : array_like, (2,) + uv_m Chromaticity coordinates :math:`uv_m^p` of matching colour. - s_uv : array_like, (2,), optional + s_uv Interobserver variation (:math:`x10^3`) :math:`\\sigma_uv^p`. - d_uv_i : array_like, (2,), optional + d_uv_i Deviation of individual linear transformation (:math:`x10^3`) :math:`\\delta_uv_i^p`. - d_uv_g : array_like, (2,), optional + d_uv_g Deviation of individual linear transformation (:math:`x10^3`) :math:`\\delta_uv_g^p`. """ def __new__(cls, name, uv_t, uv_m, s_uv=None, d_uv_i=None, d_uv_g=None): """ - Returns a new instance of the + Return a new instance of the :class:`colour.corresponding.datasets.corresponding_chromaticities.\ BrenemanExperimentResult` class. """ - return super(BrenemanExperimentResult, cls).__new__( - cls, name, np.array(uv_t), np.array(uv_m), np.array(s_uv), - np.array(d_uv_i), np.array(d_uv_g)) + return super().__new__( + cls, + name, + np.array(uv_t), + np.array(uv_m), + np.array(s_uv), + np.array(d_uv_i), + np.array(d_uv_g), + ) class PrimariesChromaticityCoordinates( - namedtuple( - 'PrimariesChromaticityCoordinates', - ('experiment', 'illuminants', 'Y', 'P_uvp', 'D_uvp', 'T_uvp'))): + namedtuple( + "PrimariesChromaticityCoordinates", + ("experiment", "illuminants", "Y", "P_uvp", "D_uvp", "T_uvp"), + ) +): """ - Chromaticity coordinates of primaries. + Chromaticity coordinates of the primaries. Parameters ---------- - experiment : integer + experiment Experiment. - illuminants : array_like, (2,) + illuminants Chromaticity coordinates :math:`uv_t^p` of test colour. - Y : numeric + Y White luminance :math:`Y` in :math:`cd/m^2`. - P_uvp : numeric + P_uvp Chromaticity coordinates :math:`uv^p` of primary :math:`P`. - D_uvp : numeric + D_uvp Chromaticity coordinates :math:`uv^p` of primary :math:`D`. - T_uvp : numeric + T_uvp Chromaticity coordinates :math:`uv^p` of primary :math:`T`. """ - def __new__(cls, - experiment, - illuminants, - Y, - P_uvp=None, - D_uvp=None, - T_uvp=None): + def __new__( + cls, experiment, illuminants, Y, P_uvp=None, D_uvp=None, T_uvp=None + ): """ - Returns a new instance of the + Return a new instance of the :class:`colour.corresponding.datasets.corresponding_chromaticities.\ PrimariesChromaticityCoordinates` class. """ - return super(PrimariesChromaticityCoordinates, cls).__new__( - cls, experiment, np.array(illuminants), np.array(Y), - np.array(P_uvp), np.array(D_uvp), np.array(T_uvp)) + return super().__new__( + cls, + experiment, + np.array(illuminants), + np.array(Y), + np.array(P_uvp), + np.array(D_uvp), + np.array(T_uvp), + ) -# yapf: disable BRENEMAN_EXPERIMENT_1_RESULTS = ( + BrenemanExperimentResult("Illuminant", (0.259, 0.526), (0.200, 0.475)), BrenemanExperimentResult( - 'Illuminant', - (0.259, 0.526), (0.200, 0.475)), - BrenemanExperimentResult( - 'Gray', - (0.259, 0.524), (0.199, 0.487), (4, 4), (2, 3), (0, 0)), + "Gray", (0.259, 0.524), (0.199, 0.487), (4, 4), (2, 3), (0, 0) + ), BrenemanExperimentResult( - 'Red', - (0.459, 0.522), (0.420, 0.509), (19, 4), (-10, -7), (-19, -3)), + "Red", (0.459, 0.522), (0.420, 0.509), (19, 4), (-10, -7), (-19, -3) + ), BrenemanExperimentResult( - 'Skin', - (0.307, 0.526), (0.249, 0.497), (7, 4), (-1, 1), (-6, -1)), + "Skin", (0.307, 0.526), (0.249, 0.497), (7, 4), (-1, 1), (-6, -1) + ), BrenemanExperimentResult( - 'Orange', - (0.360, 0.544), (0.302, 0.548), (12, 1), (1, -2), (-7, -6)), + "Orange", (0.360, 0.544), (0.302, 0.548), (12, 1), (1, -2), (-7, -6) + ), BrenemanExperimentResult( - 'Brown', - (0.350, 0.541), (0.290, 0.537), (11, 4), (3, 0), (-5, -3)), + "Brown", (0.350, 0.541), (0.290, 0.537), (11, 4), (3, 0), (-5, -3) + ), BrenemanExperimentResult( - 'Yellow', - (0.318, 0.550), (0.257, 0.554), (8, 2), (0, 2), (-5, -5)), + "Yellow", (0.318, 0.550), (0.257, 0.554), (8, 2), (0, 2), (-5, -5) + ), BrenemanExperimentResult( - 'Foliage', - (0.258, 0.542), (0.192, 0.529), (4, 6), (3, 2), (3, -6)), + "Foliage", (0.258, 0.542), (0.192, 0.529), (4, 6), (3, 2), (3, -6) + ), BrenemanExperimentResult( - 'Green', - (0.193, 0.542), (0.129, 0.521), (7, 5), (3, 2), (9, -7)), + "Green", (0.193, 0.542), (0.129, 0.521), (7, 5), (3, 2), (9, -7) + ), BrenemanExperimentResult( - 'Blue-green', - (0.180, 0.516), (0.133, 0.469), (4, 6), (-3, -2), (2, -5)), + "Blue-green", (0.180, 0.516), (0.133, 0.469), (4, 6), (-3, -2), (2, -5) + ), BrenemanExperimentResult( - 'Blue', - (0.186, 0.445), (0.158, 0.340), (13, 33), (2, 7), (1, 13)), + "Blue", (0.186, 0.445), (0.158, 0.340), (13, 33), (2, 7), (1, 13) + ), BrenemanExperimentResult( - 'Sky', - (0.226, 0.491), (0.178, 0.426), (3, 14), (1, -3), (0, -1)), + "Sky", (0.226, 0.491), (0.178, 0.426), (3, 14), (1, -3), (0, -1) + ), BrenemanExperimentResult( - 'Purple', - (0.278, 0.456), (0.231, 0.365), (4, 25), (0, 2), (-5, 7))) + "Purple", (0.278, 0.456), (0.231, 0.365), (4, 25), (0, 2), (-5, 7) + ), +) # yapf: enable """ *Breneman (1987)* experiment 1 results. -BRENEMAN_EXPERIMENT_1_RESULTS : tuple - Notes ----- - Illuminants : *A*, *D65* @@ -172,53 +189,50 @@ def __new__(cls, - Observers Count : 7 """ -# yapf: disable + BRENEMAN_EXPERIMENT_2_RESULTS = ( + BrenemanExperimentResult("Illuminant", (0.222, 0.521), (0.204, 0.479)), BrenemanExperimentResult( - 'Illuminant', - (0.222, 0.521), (0.204, 0.479)), - BrenemanExperimentResult( - 'Gray', - (0.227, 0.517), (0.207, 0.486), (2, 5), (-1, 0), (0, 0)), + "Gray", (0.227, 0.517), (0.207, 0.486), (2, 5), (-1, 0), (0, 0) + ), BrenemanExperimentResult( - 'Red', - (0.464, 0.520), (0.449, 0.511), (22, 3), (-8, -8), (-7, -2)), + "Red", (0.464, 0.520), (0.449, 0.511), (22, 3), (-8, -8), (-7, -2) + ), BrenemanExperimentResult( - 'Skin', - (0.286, 0.526), (0.263, 0.505), (7, 2), (0, -1), (0, -1)), + "Skin", (0.286, 0.526), (0.263, 0.505), (7, 2), (0, -1), (0, -1) + ), BrenemanExperimentResult( - 'Orange', - (0.348, 0.546), (0.322, 0.545), (13, 3), (3, -1), (3, -2)), + "Orange", (0.348, 0.546), (0.322, 0.545), (13, 3), (3, -1), (3, -2) + ), BrenemanExperimentResult( - 'Brown', - (0.340, 0.543), (0.316, 0.537), (11, 3), (1, 1), (0, 0)), + "Brown", (0.340, 0.543), (0.316, 0.537), (11, 3), (1, 1), (0, 0) + ), BrenemanExperimentResult( - 'Yellow', - (0.288, 0.554), (0.265, 0.553), (5, 2), (-2, 2), (-1, -2)), + "Yellow", (0.288, 0.554), (0.265, 0.553), (5, 2), (-2, 2), (-1, -2) + ), BrenemanExperimentResult( - 'Foliage', - (0.244, 0.547), (0.221, 0.538), (4, 3), (-2, 1), (0, -3)), + "Foliage", (0.244, 0.547), (0.221, 0.538), (4, 3), (-2, 1), (0, -3) + ), BrenemanExperimentResult( - 'Green', - (0.156, 0.548), (0.135, 0.532), (4, 3), (-1, 3), (3, -4)), + "Green", (0.156, 0.548), (0.135, 0.532), (4, 3), (-1, 3), (3, -4) + ), BrenemanExperimentResult( - 'Blue-green', - (0.159, 0.511), (0.145, 0.472), (9, 7), (-1, 2), (2, 1)), + "Blue-green", (0.159, 0.511), (0.145, 0.472), (9, 7), (-1, 2), (2, 1) + ), BrenemanExperimentResult( - 'Blue', - (0.160, 0.406), (0.163, 0.331), (23, 31), (2, -3), (-1, 3)), + "Blue", (0.160, 0.406), (0.163, 0.331), (23, 31), (2, -3), (-1, 3) + ), BrenemanExperimentResult( - 'Sky', - (0.190, 0.481), (0.176, 0.431), (5, 24), (2, -2), (2, 0)), + "Sky", (0.190, 0.481), (0.176, 0.431), (5, 24), (2, -2), (2, 0) + ), BrenemanExperimentResult( - 'Purple', - (0.258, 0.431), (0.244, 0.349), (4, 19), (-3, 13), (-4, 19))) + "Purple", (0.258, 0.431), (0.244, 0.349), (4, 19), (-3, 13), (-4, 19) + ), +) # yapf: enable """ *Breneman (1987)* experiment 2 results. -BRENEMAN_EXPERIMENT_2_RESULTS : tuple - Notes ----- - Illuminants : *Projector*, *D55* @@ -226,53 +240,55 @@ def __new__(cls, - Observers Count : 7 """ -# yapf: disable + BRENEMAN_EXPERIMENT_3_RESULTS = ( + BrenemanExperimentResult("Illuminant", (0.223, 0.521), (0.206, 0.478)), BrenemanExperimentResult( - 'Illuminant', - (0.223, 0.521), (0.206, 0.478)), - BrenemanExperimentResult( - 'Gray', - (0.228, 0.517), (0.211, 0.494), (1, 3), (0, 2), (0, 0)), + "Gray", (0.228, 0.517), (0.211, 0.494), (1, 3), (0, 2), (0, 0) + ), BrenemanExperimentResult( - 'Red', - (0.462, 0.519), (0.448, 0.505), (11, 4), (-3, 6), (-4, 6)), + "Red", (0.462, 0.519), (0.448, 0.505), (11, 4), (-3, 6), (-4, 6) + ), BrenemanExperimentResult( - 'Skin', - (0.285, 0.524), (0.267, 0.507), (6, 3), (-1, 1), (-2, 1)), + "Skin", (0.285, 0.524), (0.267, 0.507), (6, 3), (-1, 1), (-2, 1) + ), BrenemanExperimentResult( - 'Orange', - (0.346, 0.546), (0.325, 0.541), (11, 3), (1, -2), (2, 3)), + "Orange", (0.346, 0.546), (0.325, 0.541), (11, 3), (1, -2), (2, 3) + ), BrenemanExperimentResult( - 'Brown', - (0.338, 0.543), (0.321, 0.532), (9, 6), (-3, 2), (-3, 7)), + "Brown", (0.338, 0.543), (0.321, 0.532), (9, 6), (-3, 2), (-3, 7) + ), BrenemanExperimentResult( - 'Yellow', - (0.287, 0.554), (0.267, 0.548), (4, 5), (1, -2), (0, 5)), + "Yellow", (0.287, 0.554), (0.267, 0.548), (4, 5), (1, -2), (0, 5) + ), BrenemanExperimentResult( - 'Foliage', - (0.244, 0.547), (0.226, 0.531), (3, 6), (-1, 3), (-2, 8)), + "Foliage", (0.244, 0.547), (0.226, 0.531), (3, 6), (-1, 3), (-2, 8) + ), BrenemanExperimentResult( - 'Green', - (0.157, 0.548), (0.141, 0.528), (9, 6), (2, 2), (0, 6)), + "Green", (0.157, 0.548), (0.141, 0.528), (9, 6), (2, 2), (0, 6) + ), BrenemanExperimentResult( - 'Blue-green', - (0.160, 0.510), (0.151, 0.486), (8, 5), (-2, -1), (-2, -5)), + "Blue-green", + (0.160, 0.510), + (0.151, 0.486), + (8, 5), + (-2, -1), + (-2, -5), + ), BrenemanExperimentResult( - 'Blue', - (0.162, 0.407), (0.158, 0.375), (6, 7), (1, -6), (4, -23)), + "Blue", (0.162, 0.407), (0.158, 0.375), (6, 7), (1, -6), (4, -23) + ), BrenemanExperimentResult( - 'Sky', - (0.191, 0.482), (0.179, 0.452), (4, 5), (0, 1), (1, -7)), + "Sky", (0.191, 0.482), (0.179, 0.452), (4, 5), (0, 1), (1, -7) + ), BrenemanExperimentResult( - 'Purple', - (0.258, 0.432), (0.238, 0.396), (4, 8), (5, 3), (4, -11))) + "Purple", (0.258, 0.432), (0.238, 0.396), (4, 8), (5, 3), (4, -11) + ), +) # yapf: enable """ *Breneman (1987)* experiment 3 results. -BRENEMAN_EXPERIMENT_3_RESULTS : tuple - Notes ----- - Illuminants : *Projector*, *D55* @@ -280,53 +296,55 @@ def __new__(cls, - Observers Count : 7 """ -# yapf: disable + BRENEMAN_EXPERIMENT_4_RESULTS = ( + BrenemanExperimentResult("Illuminant", (0.258, 0.523), (0.199, 0.467)), BrenemanExperimentResult( - 'Illuminant', - (0.258, 0.523), (0.199, 0.467)), + "Gray", (0.257, 0.524), (0.205, 0.495), (2, 2), (0, 4), (0, 0) + ), BrenemanExperimentResult( - 'Gray', - (0.257, 0.524), (0.205, 0.495), (2, 2), (0, 4), (0, 0)), + "Red", (0.460, 0.521), (0.416, 0.501), (11, 6), (-6, 4), (-6, 9) + ), BrenemanExperimentResult( - 'Red', - (0.460, 0.521), (0.416, 0.501), (11, 6), (-6, 4), (-6, 9)), + "Skin", (0.308, 0.526), (0.253, 0.503), (7, 3), (-1, 1), (-1, 0) + ), BrenemanExperimentResult( - 'Skin', - (0.308, 0.526), (0.253, 0.503), (7, 3), (-1, 1), (-1, 0)), + "Orange", (0.360, 0.544), (0.303, 0.541), (14, 5), (1, -4), (1, 2) + ), BrenemanExperimentResult( - 'Orange', - (0.360, 0.544), (0.303, 0.541), (14, 5), (1, -4), (1, 2)), + "Brown", (0.350, 0.541), (0.296, 0.527), (11, 7), (-2, 4), (-3, 9) + ), BrenemanExperimentResult( - 'Brown', - (0.350, 0.541), (0.296, 0.527), (11, 7), (-2, 4), (-3, 9)), + "Yellow", (0.317, 0.550), (0.260, 0.547), (9, 5), (1, -3), (0, 3) + ), BrenemanExperimentResult( - 'Yellow', - (0.317, 0.550), (0.260, 0.547), (9, 5), (1, -3), (0, 3)), + "Foliage", (0.258, 0.543), (0.203, 0.520), (4, 6), (0, 8), (0, 9) + ), BrenemanExperimentResult( - 'Foliage', - (0.258, 0.543), (0.203, 0.520), (4, 6), (0, 8), (0, 9)), + "Green", (0.193, 0.543), (0.142, 0.516), (6, 9), (3, 8), (2, 6) + ), BrenemanExperimentResult( - 'Green', - (0.193, 0.543), (0.142, 0.516), (6, 9), (3, 8), (2, 6)), + "Blue-green", + (0.180, 0.516), + (0.140, 0.484), + (9, 5), + (-2, -1), + (-1, -9), + ), BrenemanExperimentResult( - 'Blue-green', - (0.180, 0.516), (0.140, 0.484), (9, 5), (-2, -1), (-1, -9)), + "Blue", (0.185, 0.445), (0.151, 0.394), (8, 10), (2, -8), (8, -24) + ), BrenemanExperimentResult( - 'Blue', - (0.185, 0.445), (0.151, 0.394), (8, 10), (2, -8), (8, -24)), + "Sky", (0.225, 0.490), (0.180, 0.448), (4, 8), (1, -1), (3, -11) + ), BrenemanExperimentResult( - 'Sky', - (0.225, 0.490), (0.180, 0.448), (4, 8), (1, -1), (3, -11)), - BrenemanExperimentResult( - 'Purple', - (0.278, 0.455), (0.229, 0.388), (6, 14), (1, 12), (3, 0))) + "Purple", (0.278, 0.455), (0.229, 0.388), (6, 14), (1, 12), (3, 0) + ), +) # yapf: enable """ *Breneman (1987)* experiment 4 results. -BRENEMAN_EXPERIMENT_4_RESULTS : tuple - Notes ----- - Illuminants : *A*, *D65* @@ -334,103 +352,81 @@ def __new__(cls, - Observers Count : 7 """ -# yapf: disable + BRENEMAN_EXPERIMENT_5_RESULTS = ( - BrenemanExperimentResult( - 'Gray', - (0.028, 0.480), (0.212, 0.491), (2, 2)), - BrenemanExperimentResult( - 'Red', - (0.449, 0.512), (0.408, 0.514), (11, 5)), - BrenemanExperimentResult( - 'Skin', - (0.269, 0.505), (0.262, 0.511), (4, 2)), - BrenemanExperimentResult( - 'Orange', - (0.331, 0.548), (0.303, 0.545), (4, 3)), - BrenemanExperimentResult( - 'Brown', - (0.322, 0.541), (0.303, 0.538), (4, 4)), - BrenemanExperimentResult( - 'Yellow', - (0.268, 0.555), (0.264, 0.550), (3, 2)), - BrenemanExperimentResult( - 'Foliage', - (0.224, 0.538), (0.227, 0.535), (3, 3)), - BrenemanExperimentResult( - 'Green', - (0.134, 0.531), (0.159, 0.530), (9, 3)), - BrenemanExperimentResult( - 'Blue-green', - (0.145, 0.474), (0.165, 0.490), (8, 3)), - BrenemanExperimentResult( - 'Blue', - (0.163, 0.329), (0.173, 0.378), (7, 12)), - BrenemanExperimentResult( - 'Sky', - (0.179, 0.438), (0.189, 0.462), (5, 4)), - BrenemanExperimentResult( - 'Purple', - (0.245, 0.364), (0.239, 0.401), (4, 16))) + BrenemanExperimentResult("Gray", (0.028, 0.480), (0.212, 0.491), (2, 2)), + BrenemanExperimentResult("Red", (0.449, 0.512), (0.408, 0.514), (11, 5)), + BrenemanExperimentResult("Skin", (0.269, 0.505), (0.262, 0.511), (4, 2)), + BrenemanExperimentResult("Orange", (0.331, 0.548), (0.303, 0.545), (4, 3)), + BrenemanExperimentResult("Brown", (0.322, 0.541), (0.303, 0.538), (4, 4)), + BrenemanExperimentResult("Yellow", (0.268, 0.555), (0.264, 0.550), (3, 2)), + BrenemanExperimentResult( + "Foliage", (0.224, 0.538), (0.227, 0.535), (3, 3) + ), + BrenemanExperimentResult("Green", (0.134, 0.531), (0.159, 0.530), (9, 3)), + BrenemanExperimentResult( + "Blue-green", (0.145, 0.474), (0.165, 0.490), (8, 3) + ), + BrenemanExperimentResult("Blue", (0.163, 0.329), (0.173, 0.378), (7, 12)), + BrenemanExperimentResult("Sky", (0.179, 0.438), (0.189, 0.462), (5, 4)), + BrenemanExperimentResult( + "Purple", (0.245, 0.364), (0.239, 0.401), (4, 16) + ), +) # yapf: enable """ *Breneman (1987)* experiment 5 results. -BRENEMAN_EXPERIMENT_5_RESULTS : tuple - Notes ----- - Effective White Levels : 130 and 2120 :math:`cd/m^2` - Observers Count : 7 """ -# yapf: disable + BRENEMAN_EXPERIMENT_6_RESULTS = ( + BrenemanExperimentResult("Illuminant", (0.257, 0.525), (0.201, 0.482)), BrenemanExperimentResult( - 'Illuminant', - (0.257, 0.525), (0.201, 0.482)), - BrenemanExperimentResult( - 'Gray', - (0.267, 0.521), (0.207, 0.485), (5, 3), (-1, 0), (0, 0)), + "Gray", (0.267, 0.521), (0.207, 0.485), (5, 3), (-1, 0), (0, 0) + ), BrenemanExperimentResult( - 'Red', - (0.457, 0.521), (0.398, 0.516), (9, 4), (-2, -5), (1, -9)), + "Red", (0.457, 0.521), (0.398, 0.516), (9, 4), (-2, -5), (1, -9) + ), BrenemanExperimentResult( - 'Skin', - (0.316, 0.526), (0.253, 0.503), (5, 3), (-3, -2), (-1, -3)), + "Skin", (0.316, 0.526), (0.253, 0.503), (5, 3), (-3, -2), (-1, -3) + ), BrenemanExperimentResult( - 'Orange', - (0.358, 0.545), (0.287, 0.550), (7, 3), (3, 0), (7, -6)), + "Orange", (0.358, 0.545), (0.287, 0.550), (7, 3), (3, 0), (7, -6) + ), BrenemanExperimentResult( - 'Brown', - (0.350, 0.541), (0.282, 0.540), (6, 3), (-1, 0), (2, -5)), + "Brown", (0.350, 0.541), (0.282, 0.540), (6, 3), (-1, 0), (2, -5) + ), BrenemanExperimentResult( - 'Yellow', - (0.318, 0.551), (0.249, 0.556), (7, 2), (-1, 1), (2, -5)), + "Yellow", (0.318, 0.551), (0.249, 0.556), (7, 2), (-1, 1), (2, -5) + ), BrenemanExperimentResult( - 'Foliage', - (0.256, 0.547), (0.188, 0.537), (5, 4), (3, 1), (4, -2)), + "Foliage", (0.256, 0.547), (0.188, 0.537), (5, 4), (3, 1), (4, -2) + ), BrenemanExperimentResult( - 'Green', - (0.193, 0.542), (0.133, 0.520), (13, 3), (5, -2), (5, -4)), + "Green", (0.193, 0.542), (0.133, 0.520), (13, 3), (5, -2), (5, -4) + ), BrenemanExperimentResult( - 'Blue-green', - (0.180, 0.516), (0.137, 0.466), (12, 10), (0, 0), (-2, 2)), + "Blue-green", (0.180, 0.516), (0.137, 0.466), (12, 10), (0, 0), (-2, 2) + ), BrenemanExperimentResult( - 'Blue', - (0.186, 0.445), (0.156, 0.353), (12, 45), (6, 1), (2, 6)), + "Blue", (0.186, 0.445), (0.156, 0.353), (12, 45), (6, 1), (2, 6) + ), BrenemanExperimentResult( - 'Sky', - (0.225, 0.492), (0.178, 0.428), (6, 14), (1, -1), (-1, 3)), + "Sky", (0.225, 0.492), (0.178, 0.428), (6, 14), (1, -1), (-1, 3) + ), BrenemanExperimentResult( - 'Purple', - (0.276, 0.456), (0.227, 0.369), (6, 27), (-2, 4), (-3, 9))) + "Purple", (0.276, 0.456), (0.227, 0.369), (6, 27), (-2, 4), (-3, 9) + ), +) # yapf: enable """ *Breneman (1987)* experiment 6 results. -BRENEMAN_EXPERIMENT_6_RESULTS : tuple - Notes ----- - Illuminants : *A*, *D55* @@ -438,103 +434,81 @@ def __new__(cls, - Observers Count : 8 """ -# yapf: disable + BRENEMAN_EXPERIMENT_7_RESULTS = ( - BrenemanExperimentResult( - 'Gray', - (0.208, 0.481), (0.211, 0.486), (2, 3)), - BrenemanExperimentResult( - 'Red', - (0.448, 0.512), (0.409, 0.516), (9, 2)), - BrenemanExperimentResult( - 'Skin', - (0.269, 0.505), (0.256, 0.506), (4, 3)), - BrenemanExperimentResult( - 'Orange', - (0.331, 0.549), (0.305, 0.547), (5, 4)), - BrenemanExperimentResult( - 'Brown', - (0.322, 0.541), (0.301, 0.539), (5, 2)), - BrenemanExperimentResult( - 'Yellow', - (0.268, 0.555), (0.257, 0.552), (3, 4)), - BrenemanExperimentResult( - 'Foliage', - (0.225, 0.538), (0.222, 0.536), (3, 2)), - BrenemanExperimentResult( - 'Green', - (0.135, 0.531), (0.153, 0.529), (8, 2)), - BrenemanExperimentResult( - 'Blue-green', - (0.145, 0.475), (0.160, 0.484), (3, 5)), - BrenemanExperimentResult( - 'Blue', - (0.163, 0.331), (0.171, 0.379), (4, 11)), - BrenemanExperimentResult( - 'Sky', - (0.179, 0.438), (0.187, 0.452), (4, 7)), - BrenemanExperimentResult( - 'Purple', - (0.245, 0.365), (0.240, 0.398), (4, 10))) + BrenemanExperimentResult("Gray", (0.208, 0.481), (0.211, 0.486), (2, 3)), + BrenemanExperimentResult("Red", (0.448, 0.512), (0.409, 0.516), (9, 2)), + BrenemanExperimentResult("Skin", (0.269, 0.505), (0.256, 0.506), (4, 3)), + BrenemanExperimentResult("Orange", (0.331, 0.549), (0.305, 0.547), (5, 4)), + BrenemanExperimentResult("Brown", (0.322, 0.541), (0.301, 0.539), (5, 2)), + BrenemanExperimentResult("Yellow", (0.268, 0.555), (0.257, 0.552), (3, 4)), + BrenemanExperimentResult( + "Foliage", (0.225, 0.538), (0.222, 0.536), (3, 2) + ), + BrenemanExperimentResult("Green", (0.135, 0.531), (0.153, 0.529), (8, 2)), + BrenemanExperimentResult( + "Blue-green", (0.145, 0.475), (0.160, 0.484), (3, 5) + ), + BrenemanExperimentResult("Blue", (0.163, 0.331), (0.171, 0.379), (4, 11)), + BrenemanExperimentResult("Sky", (0.179, 0.438), (0.187, 0.452), (4, 7)), + BrenemanExperimentResult( + "Purple", (0.245, 0.365), (0.240, 0.398), (4, 10) + ), +) # yapf: enable """ *Breneman (1987)* experiment 7 results. -BRENEMAN_EXPERIMENT_7_RESULTS : tuple - Notes ----- - Effective White Levels : 850 and 11100 :math:`cd/m^2` - Observers Count : 8 """ -# yapf: disable + BRENEMAN_EXPERIMENT_8_RESULTS = ( + BrenemanExperimentResult("Illuminant", (0.258, 0.524), (0.195, 0.469)), BrenemanExperimentResult( - 'Illuminant', - (0.258, 0.524), (0.195, 0.469)), + "Gray", (0.257, 0.525), (0.200, 0.494), (2, 3), (1, 2), (0, 0) + ), BrenemanExperimentResult( - 'Gray', - (0.257, 0.525), (0.200, 0.494), (2, 3), (1, 2), (0, 0)), + "Red", (0.458, 0.522), (0.410, 0.508), (12, 4), (-3, 5), (-7, 2) + ), BrenemanExperimentResult( - 'Red', - (0.458, 0.522), (0.410, 0.508), (12, 4), (-3, 5), (-7, 2)), + "Skin", (0.308, 0.526), (0.249, 0.502), (6, 2), (-1, 1), (-3, -1) + ), BrenemanExperimentResult( - 'Skin', - (0.308, 0.526), (0.249, 0.502), (6, 2), (-1, 1), (-3, -1)), + "Orange", (0.359, 0.545), (0.299, 0.545), (12, 4), (0, -2), (-3, 0) + ), BrenemanExperimentResult( - 'Orange', - (0.359, 0.545), (0.299, 0.545), (12, 4), (0, -2), (-3, 0)), + "Brown", (0.349, 0.540), (0.289, 0.532), (10, 4), (0, 1), (-2, 2) + ), BrenemanExperimentResult( - 'Brown', - (0.349, 0.540), (0.289, 0.532), (10, 4), (0, 1), (-2, 2)), + "Yellow", (0.317, 0.550), (0.256, 0.549), (9, 5), (0, -3), (-3, 1) + ), BrenemanExperimentResult( - 'Yellow', - (0.317, 0.550), (0.256, 0.549), (9, 5), (0, -3), (-3, 1)), + "Foliage", (0.260, 0.545), (0.198, 0.529), (5, 5), (3, 1), (0, 3) + ), BrenemanExperimentResult( - 'Foliage', - (0.260, 0.545), (0.198, 0.529), (5, 5), (3, 1), (0, 3)), + "Green", (0.193, 0.543), (0.137, 0.520), (9, 5), (3, 0), (2, 1) + ), BrenemanExperimentResult( - 'Green', - (0.193, 0.543), (0.137, 0.520), (9, 5), (3, 0), (2, 1)), + "Blue-green", (0.182, 0.516), (0.139, 0.477), (9, 4), (-3, 0), (-2, -4) + ), BrenemanExperimentResult( - 'Blue-green', - (0.182, 0.516), (0.139, 0.477), (9, 4), (-3, 0), (-2, -4)), + "Blue", (0.184, 0.444), (0.150, 0.387), (5, 11), (3, -10), (6, -22) + ), BrenemanExperimentResult( - 'Blue', - (0.184, 0.444), (0.150, 0.387), (5, 11), (3, -10), (6, -22)), + "Sky", (0.224, 0.489), (0.177, 0.439), (5, 6), (1, 1), (1, -7) + ), BrenemanExperimentResult( - 'Sky', - (0.224, 0.489), (0.177, 0.439), (5, 6), (1, 1), (1, -7)), - BrenemanExperimentResult( - 'Purple', - (0.277, 0.454), (0.226, 0.389), (4, 10), (1, 4), (1, -8))) + "Purple", (0.277, 0.454), (0.226, 0.389), (4, 10), (1, 4), (1, -8) + ), +) # yapf: enable """ *Breneman (1987)* experiment 8 results. -BRENEMAN_EXPERIMENT_8_RESULTS : tuple - Notes ----- - Illuminants : *A*, *D65* @@ -542,74 +516,69 @@ def __new__(cls, - Observers Count : 8 """ -# yapf: disable + BRENEMAN_EXPERIMENT_9_RESULTS = ( + BrenemanExperimentResult("Illuminant", (0.254, 0.525), (0.195, 0.465)), BrenemanExperimentResult( - 'Illuminant', - (0.254, 0.525), (0.195, 0.465)), - BrenemanExperimentResult( - 'Gray', - (0.256, 0.524), (0.207, 0.496), (4, 6), (3, 2), (0, 0)), - BrenemanExperimentResult( - 'Red', - (0.459, 0.521), (0.415, 0.489), (20, 14), (2, 12), (-2, 21)), + "Gray", (0.256, 0.524), (0.207, 0.496), (4, 6), (3, 2), (0, 0) + ), BrenemanExperimentResult( - 'Skin', - (0.307, 0.525), (0.261, 0.500), (7, 7), (0, 1), (-5, 2)), + "Red", (0.459, 0.521), (0.415, 0.489), (20, 14), (2, 12), (-2, 21) + ), BrenemanExperimentResult( - 'Orange', - (0.359, 0.545), (0.313, 0.532), (7, 5), (-2, -3), (-6, 13)), + "Skin", (0.307, 0.525), (0.261, 0.500), (7, 7), (0, 1), (-5, 2) + ), BrenemanExperimentResult( - 'Brown', - (0.349, 0.540), (0.302, 0.510), (11, 15), (0, 12), (-5, 24)), + "Orange", (0.359, 0.545), (0.313, 0.532), (7, 5), (-2, -3), (-6, 13) + ), BrenemanExperimentResult( - 'Yellow', - (0.317, 0.550), (0.268, 0.538), (7, 10), (1, -4), (-4, 12)), + "Brown", (0.349, 0.540), (0.302, 0.510), (11, 15), (0, 12), (-5, 24) + ), BrenemanExperimentResult( - 'Foliage', - (0.259, 0.544), (0.212, 0.510), (10, 11), (0, 14), (-4, 22)), + "Yellow", (0.317, 0.550), (0.268, 0.538), (7, 10), (1, -4), (-4, 12) + ), BrenemanExperimentResult( - 'Green', - (0.193, 0.542), (0.150, 0.506), (6, 10), (-1, 13), (-2, 15)), + "Foliage", (0.259, 0.544), (0.212, 0.510), (10, 11), (0, 14), (-4, 22) + ), BrenemanExperimentResult( - 'Blue-green', - (0.181, 0.517), (0.144, 0.487), (9, 6), (-3, 0), (-1, -9)), + "Green", (0.193, 0.542), (0.150, 0.506), (6, 10), (-1, 13), (-2, 15) + ), BrenemanExperimentResult( - 'Blue', - (0.184, 0.444), (0.155, 0.407), (4, 11), (-2, -6), (6, -36)), + "Blue-green", (0.181, 0.517), (0.144, 0.487), (9, 6), (-3, 0), (-1, -9) + ), BrenemanExperimentResult( - 'Sky', - (0.225, 0.490), (0.183, 0.458), (5, 8), (1, -3), (2, -19)), + "Blue", (0.184, 0.444), (0.155, 0.407), (4, 11), (-2, -6), (6, -36) + ), BrenemanExperimentResult( - 'Purple', - (0.276, 0.454), (0.233, 0.404), (7, 12), (2, 9), (0, -16)), + "Sky", (0.225, 0.490), (0.183, 0.458), (5, 8), (1, -3), (2, -19) + ), BrenemanExperimentResult( - '(Gray)h', - (0.256, 0.525), (0.208, 0.498)), + "Purple", (0.276, 0.454), (0.233, 0.404), (7, 12), (2, 9), (0, -16) + ), + BrenemanExperimentResult("(Gray)h", (0.256, 0.525), (0.208, 0.498)), BrenemanExperimentResult( - '(Red)h', - (0.456, 0.521), (0.416, 0.501), (15, 7), None, (-6, -9)), + "(Red)h", (0.456, 0.521), (0.416, 0.501), (15, 7), None, (-6, -9) + ), BrenemanExperimentResult( - '(Brown)h', - (0.349, 0.539), (0.306, 0.526), (11, 8), None, (-8, 7)), + "(Brown)h", (0.349, 0.539), (0.306, 0.526), (11, 8), None, (-8, 7) + ), BrenemanExperimentResult( - '(Foliage)h', - (0.260, 0.545), (0.213, 0.528), (7, 9), None, (-4, 5)), + "(Foliage)h", (0.260, 0.545), (0.213, 0.528), (7, 9), None, (-4, 5) + ), BrenemanExperimentResult( - '(Green)h', - (0.193, 0.543), (0.149, 0.525), (10, 8), None, (-1, -1)), + "(Green)h", (0.193, 0.543), (0.149, 0.525), (10, 8), None, (-1, -1) + ), BrenemanExperimentResult( - '(Blue)h', - (0.184, 0.444), (0.156, 0.419), (7, 8), None, (4, -45)), + "(Blue)h", (0.184, 0.444), (0.156, 0.419), (7, 8), None, (4, -45) + ), BrenemanExperimentResult( - '(Purple)h', - (0.277, 0.456), (0.236, 0.422), (6, 11), None, (-2, -29))) + "(Purple)h", (0.277, 0.456), (0.236, 0.422), (6, 11), None, (-2, -29) + ), +) # yapf: enable """ *Breneman (1987)* experiment 9 results. -BRENEMAN_EXPERIMENT_9_RESULTS : tuple - Notes ----- - Illuminants : *A*, *D65* @@ -619,103 +588,81 @@ def __new__(cls, luminescence level of the lighter colors. """ -# yapf: disable + BRENEMAN_EXPERIMENT_10_RESULTS = ( - BrenemanExperimentResult( - 'Gray', - (0.208, 0.482), (0.213, 0.494), (3, 3)), - BrenemanExperimentResult( - 'Red', - (0.447, 0.512), (0.411, 0.506), (15, 7)), - BrenemanExperimentResult( - 'Skin', - (0.269, 0.505), (0.269, 0.511), (4, 3)), - BrenemanExperimentResult( - 'Orange', - (0.331, 0.549), (0.315, 0.536), (7, 8)), - BrenemanExperimentResult( - 'Brown', - (0.323, 0.542), (0.310, 0.526), (6, 8)), - BrenemanExperimentResult( - 'Yellow', - (0.268, 0.556), (0.268, 0.541), (3, 6)), - BrenemanExperimentResult( - 'Foliage', - (0.226, 0.538), (0.230, 0.525), (4, 8)), - BrenemanExperimentResult( - 'Green', - (0.135, 0.531), (0.158, 0.524), (6, 3)), - BrenemanExperimentResult( - 'Blue-green', - (0.145, 0.476), (0.161, 0.491), (4, 4)), - BrenemanExperimentResult( - 'Blue', - (0.163, 0.330), (0.171, 0.377), (6, 19)), - BrenemanExperimentResult( - 'Sky', - (0.179, 0.439), (0.187, 0.465), (5, 5)), - BrenemanExperimentResult( - 'Purple', - (0.245, 0.366), (0.240, 0.402), (3, 12))) + BrenemanExperimentResult("Gray", (0.208, 0.482), (0.213, 0.494), (3, 3)), + BrenemanExperimentResult("Red", (0.447, 0.512), (0.411, 0.506), (15, 7)), + BrenemanExperimentResult("Skin", (0.269, 0.505), (0.269, 0.511), (4, 3)), + BrenemanExperimentResult("Orange", (0.331, 0.549), (0.315, 0.536), (7, 8)), + BrenemanExperimentResult("Brown", (0.323, 0.542), (0.310, 0.526), (6, 8)), + BrenemanExperimentResult("Yellow", (0.268, 0.556), (0.268, 0.541), (3, 6)), + BrenemanExperimentResult( + "Foliage", (0.226, 0.538), (0.230, 0.525), (4, 8) + ), + BrenemanExperimentResult("Green", (0.135, 0.531), (0.158, 0.524), (6, 3)), + BrenemanExperimentResult( + "Blue-green", (0.145, 0.476), (0.161, 0.491), (4, 4) + ), + BrenemanExperimentResult("Blue", (0.163, 0.330), (0.171, 0.377), (6, 19)), + BrenemanExperimentResult("Sky", (0.179, 0.439), (0.187, 0.465), (5, 5)), + BrenemanExperimentResult( + "Purple", (0.245, 0.366), (0.240, 0.402), (3, 12) + ), +) # yapf: enable """ *Breneman (1987)* experiment 10 results. -BRENEMAN_EXPERIMENT_10_RESULTS : tuple - Notes ----- - Effective White Levels : 15 and 270 :math:`cd/m^2` - Observers Count : 7 """ -# yapf: disable + BRENEMAN_EXPERIMENT_11_RESULTS = ( + BrenemanExperimentResult("Illuminant", (0.208, 0.482), (0.174, 0.520)), BrenemanExperimentResult( - 'Illuminant', - (0.208, 0.482), (0.174, 0.520)), - BrenemanExperimentResult( - 'Gray', - (0.209, 0.483), (0.176, 0.513), (3, 4), (2, 2), (0, 0)), + "Gray", (0.209, 0.483), (0.176, 0.513), (3, 4), (2, 2), (0, 0) + ), BrenemanExperimentResult( - 'Red', - (0.450, 0.512), (0.419, 0.524), (10, 2), (3, 2), (8, -1)), + "Red", (0.450, 0.512), (0.419, 0.524), (10, 2), (3, 2), (8, -1) + ), BrenemanExperimentResult( - 'Skin', - (0.268, 0.506), (0.240, 0.528), (6, 2), (-4, 0), (-3, 0)), + "Skin", (0.268, 0.506), (0.240, 0.528), (6, 2), (-4, 0), (-3, 0) + ), BrenemanExperimentResult( - 'Orange', - (0.331, 0.547), (0.293, 0.553), (6, 2), (3, -1), (5, 1)), + "Orange", (0.331, 0.547), (0.293, 0.553), (6, 2), (3, -1), (5, 1) + ), BrenemanExperimentResult( - 'Brown', - (0.323, 0.542), (0.290, 0.552), (5, 2), (-1, -3), (0, -1)), + "Brown", (0.323, 0.542), (0.290, 0.552), (5, 2), (-1, -3), (0, -1) + ), BrenemanExperimentResult( - 'Yellow', - (0.266, 0.549), (0.236, 0.557), (4, 2), (-3, -2), (-4, 2)), + "Yellow", (0.266, 0.549), (0.236, 0.557), (4, 2), (-3, -2), (-4, 2) + ), BrenemanExperimentResult( - 'Foliage', - (0.227, 0.538), (0.194, 0.552), (4, 2), (2, -3), (-1, 1)), + "Foliage", (0.227, 0.538), (0.194, 0.552), (4, 2), (2, -3), (-1, 1) + ), BrenemanExperimentResult( - 'Green', - (0.146, 0.534), (0.118, 0.551), (8, 3), (4, -2), (-6, 3)), + "Green", (0.146, 0.534), (0.118, 0.551), (8, 3), (4, -2), (-6, 3) + ), BrenemanExperimentResult( - 'Blue-green', - (0.160, 0.475), (0.130, 0.513), (9, 4), (1, -1), (-4, -3)), + "Blue-green", (0.160, 0.475), (0.130, 0.513), (9, 4), (1, -1), (-4, -3) + ), BrenemanExperimentResult( - 'Blue', - (0.177, 0.340), (0.133, 0.427), (6, 14), (4, -17), (11, -29)), + "Blue", (0.177, 0.340), (0.133, 0.427), (6, 14), (4, -17), (11, -29) + ), BrenemanExperimentResult( - 'Sky', - (0.179, 0.438), (0.146, 0.482), (6, 10), (1, 4), (0, -1)), + "Sky", (0.179, 0.438), (0.146, 0.482), (6, 10), (1, 4), (0, -1) + ), BrenemanExperimentResult( - 'Purple', - (0.245, 0.366), (0.216, 0.419), (4, 13), (-3, 8), (4, -2))) + "Purple", (0.245, 0.366), (0.216, 0.419), (4, 13), (-3, 8), (4, -2) + ), +) # yapf: enable """ *Breneman (1987)* experiment 1 results. -BRENEMAN_EXPERIMENT_11_RESULTS : tuple - Notes ----- - Illuminants : *green*, *D65* @@ -723,53 +670,50 @@ def __new__(cls, - Observers Count : 7 """ -# yapf: disable + BRENEMAN_EXPERIMENT_12_RESULTS = ( + BrenemanExperimentResult("Illuminant", (0.205, 0.482), (0.174, 0.519)), BrenemanExperimentResult( - 'Illuminant', - (0.205, 0.482), (0.174, 0.519)), - BrenemanExperimentResult( - 'Gray', - (0.208, 0.482), (0.181, 0.507), (4, 3), (0, 1), (0, 0)), + "Gray", (0.208, 0.482), (0.181, 0.507), (4, 3), (0, 1), (0, 0) + ), BrenemanExperimentResult( - 'Red', - (0.451, 0.512), (0.422, 0.526), (20, 3), (0, -5), (10, -5)), + "Red", (0.451, 0.512), (0.422, 0.526), (20, 3), (0, -5), (10, -5) + ), BrenemanExperimentResult( - 'Skin', - (0.268, 0.506), (0.244, 0.525), (5, 2), (-6, 0), (-2, -1)), + "Skin", (0.268, 0.506), (0.244, 0.525), (5, 2), (-6, 0), (-2, -1) + ), BrenemanExperimentResult( - 'Orange', - (0.331, 0.548), (0.292, 0.553), (10, 2), (5, 2), (11, 1)), + "Orange", (0.331, 0.548), (0.292, 0.553), (10, 2), (5, 2), (11, 1) + ), BrenemanExperimentResult( - 'Brown', - (0.324, 0.542), (0.286, 0.554), (8, 1), (5, -3), (10, -4)), + "Brown", (0.324, 0.542), (0.286, 0.554), (8, 1), (5, -3), (10, -4) + ), BrenemanExperimentResult( - 'Yellow', - (0.266, 0.548), (0.238, 0.558), (6, 2), (-3, -1), (-1, -2)), + "Yellow", (0.266, 0.548), (0.238, 0.558), (6, 2), (-3, -1), (-1, -2) + ), BrenemanExperimentResult( - 'Foliage', - (0.227, 0.538), (0.196, 0.555), (6, 3), (3, -4), (2, -5)), + "Foliage", (0.227, 0.538), (0.196, 0.555), (6, 3), (3, -4), (2, -5) + ), BrenemanExperimentResult( - 'Green', - (0.145, 0.534), (0.124, 0.551), (8, 6), (1, -1), (-8, -1)), + "Green", (0.145, 0.534), (0.124, 0.551), (8, 6), (1, -1), (-8, -1) + ), BrenemanExperimentResult( - 'Blue-green', - (0.160, 0.474), (0.135, 0.505), (5, 2), (1, -1), (-4, -3)), + "Blue-green", (0.160, 0.474), (0.135, 0.505), (5, 2), (1, -1), (-4, -3) + ), BrenemanExperimentResult( - 'Blue', - (0.178, 0.339), (0.149, 0.392), (4, 20), (-1, -5), (3, -7)), + "Blue", (0.178, 0.339), (0.149, 0.392), (4, 20), (-1, -5), (3, -7) + ), BrenemanExperimentResult( - 'Sky', - (0.179, 0.440), (0.150, 0.473), (4, 8), (3, 2), (2, 0)), + "Sky", (0.179, 0.440), (0.150, 0.473), (4, 8), (3, 2), (2, 0) + ), BrenemanExperimentResult( - 'Purple', - (0.246, 0.366), (0.222, 0.404), (5, 15), (-4, 2), (4, 2))) + "Purple", (0.246, 0.366), (0.222, 0.404), (5, 15), (-4, 2), (4, 2) + ), +) # yapf: enable """ *Breneman (1987)* experiment 12 results. -BRENEMAN_EXPERIMENT_12_RESULTS : tuple - Notes ----- - Illuminants : *D55*, *green* @@ -777,39 +721,61 @@ def __new__(cls, - Observers Count : 7 """ -# yapf: disable + BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES = { 1: PrimariesChromaticityCoordinates( - 1, ('A', 'D65'), 1500, - (0.671, 0.519), (-0.586, 0.627), (0.253, 0.016)), + 1, ("A", "D65"), 1500, (0.671, 0.519), (-0.586, 0.627), (0.253, 0.016) + ), 2: PrimariesChromaticityCoordinates( - 2, ('Projector', 'D55'), 1500, - (0.675, 0.523), (-0.466, 0.617), (0.255, 0.018)), + 2, + ("Projector", "D55"), + 1500, + (0.675, 0.523), + (-0.466, 0.617), + (0.255, 0.018), + ), 3: PrimariesChromaticityCoordinates( - 3, ('Projector', 'D55'), 75, - (0.664, 0.510), (-0.256, 0.729), (0.244, 0.003)), + 3, + ("Projector", "D55"), + 75, + (0.664, 0.510), + (-0.256, 0.729), + (0.244, 0.003), + ), 4: PrimariesChromaticityCoordinates( - 4, ('A', 'D65'), 75, - (0.674, 0.524), (-0.172, 0.628), (0.218, -0.026)), + 4, ("A", "D65"), 75, (0.674, 0.524), (-0.172, 0.628), (0.218, -0.026) + ), 6: PrimariesChromaticityCoordinates( - 6, ('A', 'D55'), 11100, - (0.659, 0.506), (-0.141, 0.615), (0.249, 0.009)), + 6, ("A", "D55"), 11100, (0.659, 0.506), (-0.141, 0.615), (0.249, 0.009) + ), 8: PrimariesChromaticityCoordinates( - 8, ('A', 'D65'), 350, - (0.659, 0.505), (-0.246, 0.672), (0.235, -0.006)), + 8, ("A", "D65"), 350, (0.659, 0.505), (-0.246, 0.672), (0.235, -0.006) + ), 9: PrimariesChromaticityCoordinates( - 9, ('A', 'D65'), 15, - (0.693, 0.546), (-0.446, 0.773), (0.221, -0.023)), + 9, ("A", "D65"), 15, (0.693, 0.546), (-0.446, 0.773), (0.221, -0.023) + ), 11: PrimariesChromaticityCoordinates( - 11, ('D55', 'green'), 1560, - (0.680, 0.529), (0.018, 0.576), (0.307, 0.080)), + 11, + ("D55", "green"), + 1560, + (0.680, 0.529), + (0.018, 0.576), + (0.307, 0.080), + ), 12: PrimariesChromaticityCoordinates( - 12, ('D55', 'green'), 75, - (0.661, 0.505), (0.039, 0.598), (0.345, 0.127))} + 12, + ("D55", "green"), + 75, + (0.661, 0.505), + (0.039, 0.598), + (0.345, 0.127), + ), +} # yapf: enable if is_documentation_building(): # pragma: no cover BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES = DocstringDict( - BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES) + BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES + ) BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES.__doc__ = """ *Breneman (1987)* experiments primaries chromaticities. @@ -832,7 +798,7 @@ def __new__(cls, 9: BRENEMAN_EXPERIMENT_9_RESULTS, 10: BRENEMAN_EXPERIMENT_10_RESULTS, 11: BRENEMAN_EXPERIMENT_11_RESULTS, - 12: BRENEMAN_EXPERIMENT_12_RESULTS + 12: BRENEMAN_EXPERIMENT_12_RESULTS, } if is_documentation_building(): # pragma: no cover BRENEMAN_EXPERIMENTS = DocstringDict(BRENEMAN_EXPERIMENTS) @@ -842,6 +808,4 @@ def __new__(cls, References ---------- :cite:`Breneman1987b` - -BRENEMAN_EXPERIMENTS : dict """ diff --git a/colour/corresponding/prediction.py b/colour/corresponding/prediction.py index 3c9806ec86..b644911391 100644 --- a/colour/corresponding/prediction.py +++ b/colour/corresponding/prediction.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Corresponding Chromaticities Prediction ======================================= -Defines objects to compute corresponding chromaticities prediction. +Defines the objects to compute corresponding chromaticities prediction. References ---------- @@ -32,73 +31,118 @@ - :cite:`Westland2012k` : Westland, S., Ripamonti, C., & Cheung, V. (2012). CMCCAT2000. In Computational Colour Science Using MATLAB (2nd ed., pp. 83-86). ISBN:978-0-470-66569-5 +- :cite:`Zhai2018` : Zhai, Q., & Luo, M. R. (2018). Study of chromatic + adaptation via neutral white matches on different viewing media. Optics + Express, 26(6), 7724. doi:10.1364/OE.26.007724 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from collections import namedtuple from colour.adaptation import ( - chromatic_adaptation_CIE1994, chromatic_adaptation_CMCCAT2000, - chromatic_adaptation_Fairchild1990, chromatic_adaptation_VonKries) -from colour.corresponding import (BRENEMAN_EXPERIMENTS, - BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES) -from colour.models import (Luv_to_uv, Luv_uv_to_xy, XYZ_to_Luv, XYZ_to_xy, - xy_to_XYZ, xyY_to_XYZ) -from colour.utilities import (CaseInsensitiveMapping, domain_range_scale, - filter_kwargs, full, is_numeric) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + chromatic_adaptation_CIE1994, + chromatic_adaptation_CMCCAT2000, + chromatic_adaptation_Fairchild1990, + chromatic_adaptation_VonKries, + chromatic_adaptation_Zhai2018, +) +from colour.corresponding import ( + BRENEMAN_EXPERIMENTS, + BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES, +) +from colour.hints import ( + Any, + ArrayLike, + FloatingOrArrayLike, + Literal, + Union, + Tuple, +) +from colour.models import ( + Luv_to_uv, + Luv_uv_to_xy, + XYZ_to_Luv, + XYZ_to_xy, + xy_to_XYZ, + xyY_to_XYZ, +) +from colour.utilities import ( + CaseInsensitiveMapping, + attest, + as_float, + as_float_scalar, + domain_range_scale, + filter_kwargs, + full, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'CorrespondingColourDataset', 'CorrespondingChromaticitiesPrediction', - 'convert_experiment_results_Breneman1987', - 'corresponding_chromaticities_prediction_Fairchild1990', - 'corresponding_chromaticities_prediction_CIE1994', - 'corresponding_chromaticities_prediction_CMCCAT2000', - 'corresponding_chromaticities_prediction_VonKries', - 'CORRESPONDING_CHROMATICITIES_PREDICTION_MODELS', - 'corresponding_chromaticities_prediction' + "CorrespondingColourDataset", + "CorrespondingChromaticitiesPrediction", + "convert_experiment_results_Breneman1987", + "corresponding_chromaticities_prediction_Fairchild1990", + "corresponding_chromaticities_prediction_CIE1994", + "corresponding_chromaticities_prediction_CMCCAT2000", + "corresponding_chromaticities_prediction_VonKries", + "corresponding_chromaticities_prediction_Zhai2018", + "CORRESPONDING_CHROMATICITIES_PREDICTION_MODELS", + "corresponding_chromaticities_prediction", ] class CorrespondingColourDataset( - namedtuple('CorrespondingColourDataset', - ('name', 'XYZ_r', 'XYZ_t', 'XYZ_cr', 'XYZ_ct', 'Y_r', 'Y_t', - 'B_r', 'B_t', 'metadata'))): + namedtuple( + "CorrespondingColourDataset", + ( + "name", + "XYZ_r", + "XYZ_t", + "XYZ_cr", + "XYZ_ct", + "Y_r", + "Y_t", + "B_r", + "B_t", + "metadata", + ), + ) +): """ - Defines a corresponding colour dataset. + Define a corresponding colour dataset. Parameters ---------- - name : unicode + name Corresponding colour dataset name. - XYZ_r : array_like + XYZ_r *CIE XYZ* tristimulus values of the reference illuminant. - XYZ_t : array_like + XYZ_t *CIE XYZ* tristimulus values of the test illuminant. - XYZ_cr : array_like + XYZ_cr Corresponding *CIE XYZ* tristimulus values under the reference illuminant. - XYZ_ct : array_like + XYZ_ct Corresponding *CIE XYZ* tristimulus values under the test illuminant. - Y_r : numeric + Y_r Reference white luminance :math:`Y_r` in :math:`cd/m^2`. - Y_t : numeric + Y_t Test white luminance :math:`Y_t` in :math:`cd/m^2`. - B_r : numeric + B_r Luminance factor :math:`B_r` of reference achromatic background as percentage. - B_t : numeric + B_t Luminance factor :math:`B_t` of test achromatic background as percentage. - metadata : dict + metadata Dataset metadata. Notes @@ -113,38 +157,42 @@ class CorrespondingColourDataset( class CorrespondingChromaticitiesPrediction( - namedtuple('CorrespondingChromaticitiesPrediction', - ('name', 'uv_t', 'uv_m', 'uv_p'))): + namedtuple( + "CorrespondingChromaticitiesPrediction", + ("name", "uv_t", "uv_m", "uv_p"), + ) +): """ - Defines a chromatic adaptation model prediction. + Define a chromatic adaptation model prediction. Parameters ---------- - name : unicode + name Test colour name. - uv_t : array_like, (2,) + uv_t Chromaticity coordinates :math:`uv_t^p` of test colour. - uv_m : array_like, (2,) + uv_m Chromaticity coordinates :math:`uv_m^p` of matching colour. - uv_p : array_like, (2,) + uv_p Chromaticity coordinates :math:`uv_p^p` of predicted colour. """ -def convert_experiment_results_Breneman1987(experiment): +def convert_experiment_results_Breneman1987( + experiment: Literal[1, 2, 3, 4, 6, 8, 9, 11, 12] +) -> CorrespondingColourDataset: """ - Converts *Breneman (1987)* experiment results to a + Convert *Breneman (1987)* experiment results to a :class:`colour.CorrespondingColourDataset` class instance. Parameters ---------- - experiment : integer - {1, 2, 3, 4, 6, 8, 9, 11, 12} + experiment *Breneman (1987)* experiment number. Returns ------- - CorrespondingColourDataset + :class:`colour.CorrespondingColourDataset` :class:`colour.CorrespondingColourDataset` class instance. Examples @@ -179,17 +227,19 @@ def convert_experiment_results_Breneman1987(experiment): [ 119.7044335..., 135. , 282.6354679...], [ 359.9532224..., 405. , 381.0031185...], [ 181.8271461..., 135. , 204.0661252...]]), - array(1500), - array(1500), + 1500.0, + 1500.0, 0.3, 0.3, {}) """ - valid_experiment_results = (1, 2, 3, 4, 6, 8, 9, 11, 12) - assert experiment in valid_experiment_results, ( - '"Breneman (1987)" experiment result must be one of "{0}"!'.format( - valid_experiment_results)) + valid_experiment_results = [1, 2, 3, 4, 6, 8, 9, 11, 12] + attest( + experiment in valid_experiment_results, + f'"Breneman (1987)" experiment result is invalid, it must be one of ' + f'"{valid_experiment_results}"!', + ) samples_luminance = [ 0.270, @@ -208,47 +258,68 @@ def convert_experiment_results_Breneman1987(experiment): experiment_results = list(BRENEMAN_EXPERIMENTS[experiment]) illuminant_chromaticities = experiment_results.pop(0) - Y_r = Y_t = BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES[experiment].Y + Y_r = Y_t = as_float( + BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES[experiment].Y + ) B_r = B_t = 0.3 - XYZ_t, XYZ_r = xy_to_XYZ( - np.hstack( - [Luv_uv_to_xy(illuminant_chromaticities[1:3]), - full([2, 1], Y_r)])) / Y_r + XYZ_t, XYZ_r = ( + xy_to_XYZ( + np.hstack( + [ + Luv_uv_to_xy(illuminant_chromaticities[1:3]), + full((2, 1), as_float_scalar(Y_r)), + ] + ) + ) + / Y_r + ) xyY_cr, xyY_ct = [], [] for i, experiment_result in enumerate(experiment_results): xyY_cr.append( - np.hstack([ - Luv_uv_to_xy(experiment_result[2]), samples_luminance[i] * Y_r - ])) + np.hstack( + [ + Luv_uv_to_xy(experiment_result[2]), + samples_luminance[i] * Y_r, + ] + ) + ) xyY_ct.append( - np.hstack([ - Luv_uv_to_xy(experiment_result[1]), samples_luminance[i] * Y_t - ])) + np.hstack( + [ + Luv_uv_to_xy(experiment_result[1]), + samples_luminance[i] * Y_t, + ] + ) + ) XYZ_cr = xyY_to_XYZ(xyY_cr) XYZ_ct = xyY_to_XYZ(xyY_ct) - return CorrespondingColourDataset(experiment, XYZ_r, XYZ_t, XYZ_cr, XYZ_ct, - Y_r, Y_t, B_r, B_t, {}) + return CorrespondingColourDataset( + experiment, XYZ_r, XYZ_t, XYZ_cr, XYZ_ct, Y_r, Y_t, B_r, B_t, {} + ) -def corresponding_chromaticities_prediction_Fairchild1990(experiment=1): +def corresponding_chromaticities_prediction_Fairchild1990( + experiment: Union[ + Literal[1, 2, 3, 4, 6, 8, 9, 11, 12], CorrespondingColourDataset + ] = 1 +) -> Tuple[CorrespondingChromaticitiesPrediction, ...]: """ - Returns the corresponding chromaticities prediction for *Fairchild (1990)* + Return the corresponding chromaticities prediction for *Fairchild (1990)* chromatic adaptation model. Parameters ---------- - experiment : integer or CorrespondingColourDataset, optional - {1, 2, 3, 4, 6, 8, 9, 11, 12} + experiment *Breneman (1987)* experiment number or :class:`colour.CorrespondingColourDataset` class instance. Returns ------- - tuple + :class:`tuple` Corresponding chromaticities prediction. References @@ -275,10 +346,13 @@ def corresponding_chromaticities_prediction_Fairchild1990(experiment=1): (array([ 0.244, 0.349]), array([ 0.2418905..., 0.3413401...]))] """ - experiment_results = (convert_experiment_results_Breneman1987(experiment) - if is_numeric(experiment) else experiment) + experiment_results = ( + experiment + if isinstance(experiment, CorrespondingColourDataset) + else convert_experiment_results_Breneman1987(experiment) + ) - with domain_range_scale(1): + with domain_range_scale("1"): XYZ_t, XYZ_r = experiment_results.XYZ_t, experiment_results.XYZ_r xy_t, xy_r = XYZ_to_xy([XYZ_t, XYZ_r]) @@ -291,26 +365,32 @@ def corresponding_chromaticities_prediction_Fairchild1990(experiment=1): XYZ_2 = chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_t, XYZ_r, Y_n) uv_p = Luv_to_uv(XYZ_to_Luv(XYZ_2, xy_r), xy_r) - return tuple([ - CorrespondingChromaticitiesPrediction(experiment_results.name, - uv_t[i], uv_m[i], uv_p[i]) + return tuple( + CorrespondingChromaticitiesPrediction( + experiment_results.name, uv_t[i], uv_m[i], uv_p[i] + ) for i in range(len(uv_t)) - ]) + ) -def corresponding_chromaticities_prediction_CIE1994(experiment=1): +def corresponding_chromaticities_prediction_CIE1994( + experiment: Union[ + Literal[1, 2, 3, 4, 6, 8, 9, 11, 12], CorrespondingColourDataset + ] = 1 +) -> Tuple[CorrespondingChromaticitiesPrediction, ...]: """ - Returns the corresponding chromaticities prediction for *CIE 1994* + Return the corresponding chromaticities prediction for *CIE 1994* chromatic adaptation model. Parameters ---------- - experiment : integer or CorrespondingColourDataset, optional - {1, 2, 3, 4, 6, 8, 9, 11, 12} + experiment *Breneman (1987)* experiment number or - :class:`colour.CorrespondingColourDataset` class instance. Returns + :class:`colour.CorrespondingColourDataset` class instance. + + Returns ------- - tuple + :class:`tuple` Corresponding chromaticities prediction. References @@ -337,10 +417,13 @@ def corresponding_chromaticities_prediction_CIE1994(experiment=1): (array([ 0.244, 0.349]), array([ 0.2560012..., 0.4546263...]))] """ - experiment_results = (convert_experiment_results_Breneman1987(experiment) - if is_numeric(experiment) else experiment) + experiment_results = ( + experiment + if isinstance(experiment, CorrespondingColourDataset) + else convert_experiment_results_Breneman1987(experiment) + ) - with domain_range_scale(1): + with domain_range_scale("1"): XYZ_t, XYZ_r = experiment_results.XYZ_t, experiment_results.XYZ_r xy_o1, xy_o2 = XYZ_to_xy([XYZ_t, XYZ_r]) @@ -351,32 +434,37 @@ def corresponding_chromaticities_prediction_CIE1994(experiment=1): E_o1, E_o2 = experiment_results.Y_t, experiment_results.Y_r XYZ_1 = experiment_results.XYZ_ct - XYZ_2 = chromatic_adaptation_CIE1994(XYZ_1, xy_o1, xy_o2, Y_r, E_o1, - E_o2) + XYZ_2 = chromatic_adaptation_CIE1994( + XYZ_1, xy_o1, xy_o2, Y_r, E_o1, E_o2 + ) uv_p = Luv_to_uv(XYZ_to_Luv(XYZ_2, xy_o2), xy_o2) - return tuple([ - CorrespondingChromaticitiesPrediction(experiment_results.name, - uv_t[i], uv_m[i], uv_p[i]) + return tuple( + CorrespondingChromaticitiesPrediction( + experiment_results.name, uv_t[i], uv_m[i], uv_p[i] + ) for i in range(len(uv_t)) - ]) + ) -def corresponding_chromaticities_prediction_CMCCAT2000(experiment=1): +def corresponding_chromaticities_prediction_CMCCAT2000( + experiment: Union[ + Literal[1, 2, 3, 4, 6, 8, 9, 11, 12], CorrespondingColourDataset + ] = 1 +) -> Tuple[CorrespondingChromaticitiesPrediction, ...]: """ - Returns the corresponding chromaticities prediction for *CMCCAT2000* + Return the corresponding chromaticities prediction for *CMCCAT2000* chromatic adaptation model. Parameters ---------- - experiment : integer or CorrespondingColourDataset, optional - {1, 2, 3, 4, 6, 8, 9, 11, 12} + experiment *Breneman (1987)* experiment number or :class:`colour.CorrespondingColourDataset` class instance. Returns ------- - tuple + :class:`tuple` Corresponding chromaticities prediction. References @@ -403,10 +491,13 @@ def corresponding_chromaticities_prediction_CMCCAT2000(experiment=1): (array([ 0.244, 0.349]), array([ 0.2287638..., 0.3499324...]))] """ - experiment_results = (convert_experiment_results_Breneman1987(experiment) - if is_numeric(experiment) else experiment) + experiment_results = ( + experiment + if isinstance(experiment, CorrespondingColourDataset) + else convert_experiment_results_Breneman1987(experiment) + ) - with domain_range_scale(1): + with domain_range_scale("1"): XYZ_w, XYZ_wr = experiment_results.XYZ_t, experiment_results.XYZ_r xy_w, xy_wr = XYZ_to_xy([XYZ_w, XYZ_wr]) @@ -417,38 +508,56 @@ def corresponding_chromaticities_prediction_CMCCAT2000(experiment=1): L_A2 = experiment_results.Y_r XYZ_1 = experiment_results.XYZ_ct - XYZ_2 = chromatic_adaptation_CMCCAT2000(XYZ_1, XYZ_w, XYZ_wr, L_A1, - L_A2) + XYZ_2 = chromatic_adaptation_CMCCAT2000( + XYZ_1, XYZ_w, XYZ_wr, L_A1, L_A2 + ) uv_p = Luv_to_uv(XYZ_to_Luv(XYZ_2, xy_wr), xy_wr) - return tuple([ - CorrespondingChromaticitiesPrediction(experiment_results.name, - uv_t[i], uv_m[i], uv_p[i]) + return tuple( + CorrespondingChromaticitiesPrediction( + experiment_results.name, uv_t[i], uv_m[i], uv_p[i] + ) for i in range(len(uv_t)) - ]) - - -def corresponding_chromaticities_prediction_VonKries(experiment=1, - transform='CAT02'): + ) + + +def corresponding_chromaticities_prediction_VonKries( + experiment: Union[ + Literal[1, 2, 3, 4, 6, 8, 9, 11, 12], CorrespondingColourDataset + ] = 1, + transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", +) -> Tuple[CorrespondingChromaticitiesPrediction, ...]: """ - Returns the corresponding chromaticities prediction for *Von Kries* + Return the corresponding chromaticities prediction for *Von Kries* chromatic adaptation model using given transform. Parameters ---------- - experiment : integer or CorrespondingColourDataset, optional - {1, 2, 3, 4, 6, 8, 9, 11, 12} + experiment *Breneman (1987)* experiment number or :class:`colour.CorrespondingColourDataset` class instance. - transform : unicode, optional - **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', - 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02 Brill 2008', - 'Bianco 2010', 'Bianco PC 2010'}**, + transform Chromatic adaptation transform. Returns ------- - tuple + :class:`tuple` Corresponding chromaticities prediction. References @@ -475,10 +584,13 @@ def corresponding_chromaticities_prediction_VonKries(experiment=1, (array([ 0.244, 0.349]), array([ 0.2259805..., 0.3465291...]))] """ - experiment_results = (convert_experiment_results_Breneman1987(experiment) - if is_numeric(experiment) else experiment) + experiment_results = ( + experiment + if isinstance(experiment, CorrespondingColourDataset) + else convert_experiment_results_Breneman1987(experiment) + ) - with domain_range_scale(1): + with domain_range_scale("1"): XYZ_w, XYZ_wr = experiment_results.XYZ_t, experiment_results.XYZ_r xy_w, xy_wr = XYZ_to_xy([XYZ_w, XYZ_wr]) @@ -489,19 +601,114 @@ def corresponding_chromaticities_prediction_VonKries(experiment=1, XYZ_2 = chromatic_adaptation_VonKries(XYZ_1, XYZ_w, XYZ_wr, transform) uv_p = Luv_to_uv(XYZ_to_Luv(XYZ_2, xy_wr), xy_wr) - return tuple([ - CorrespondingChromaticitiesPrediction(experiment_results.name, - uv_t[i], uv_m[i], uv_p[i]) + return tuple( + CorrespondingChromaticitiesPrediction( + experiment_results.name, uv_t[i], uv_m[i], uv_p[i] + ) for i in range(len(uv_t)) - ]) + ) + + +def corresponding_chromaticities_prediction_Zhai2018( + experiment: Union[ + Literal[1, 2, 3, 4, 6, 8, 9, 11, 12], CorrespondingColourDataset + ] = 1, + D_b: FloatingOrArrayLike = 1, + D_d: FloatingOrArrayLike = 1, + XYZ_wo: ArrayLike = np.array([1, 1, 1]), + transform: Union[ + Literal[ + "CAT02", + "CAT16", + ], + str, + ] = "CAT02", +) -> Tuple[CorrespondingChromaticitiesPrediction, ...]: + """ + Return the corresponding chromaticities prediction for + *Zhai and Luo (2018)* chromatic adaptation model using given transform. + + Parameters + ---------- + experiment + *Breneman (1987)* experiment number or + :class:`colour.CorrespondingColourDataset` class instance. + D_b + Degree of adaptation :math:`D_{\\beta}` of input illuminant + :math:`\\beta`. + D_d + Degree of adaptation :math:`D_{\\delta}` of output illuminant + :math:`\\delta`. + XYZ_wo + Baseline illuminant (:math:`BI`) :math:`o`. + transform + Chromatic adaptation transform. + + Returns + ------- + :class:`tuple` + Corresponding chromaticities prediction. + + References + ---------- + :cite:`Breneman1987b`, :cite:`Zhai2018` + + Examples + -------- + >>> from pprint import pprint + >>> pr = corresponding_chromaticities_prediction_Zhai2018(2) + >>> pr = [(p.uv_m, p.uv_p) for p in pr] + >>> pprint(pr) # doctest: +ELLIPSIS + [(array([ 0.207, 0.486]), array([ 0.2082238..., 0.4727943...])), + (array([ 0.449, 0.511]), array([ 0.4474691..., 0.5076681...])), + (array([ 0.263, 0.505]), array([ 0.2640379..., 0.4954003...])), + (array([ 0.322, 0.545]), array([ 0.3336937..., 0.5435500...])), + (array([ 0.316, 0.537]), array([ 0.3238490..., 0.5359889...])), + (array([ 0.265, 0.553]), array([ 0.2724846..., 0.5506939...])), + (array([ 0.221, 0.538]), array([ 0.2267596..., 0.5295259...])), + (array([ 0.135, 0.532]), array([ 0.1443208..., 0.5190035...])), + (array([ 0.145, 0.472]), array([ 0.1500723..., 0.4561352...])), + (array([ 0.163, 0.331]), array([ 0.1570902..., 0.3245137...])), + (array([ 0.176, 0.431]), array([ 0.1763887..., 0.4146000...])), + (array([ 0.244, 0.349]), array([ 0.2267005..., 0.3551480...]))] + """ + + experiment_results = ( + experiment + if isinstance(experiment, CorrespondingColourDataset) + else convert_experiment_results_Breneman1987(experiment) + ) + with domain_range_scale("1"): + XYZ_w, XYZ_wr = experiment_results.XYZ_t, experiment_results.XYZ_r + xy_w, xy_wr = XYZ_to_xy([XYZ_w, XYZ_wr]) + + uv_t = Luv_to_uv(XYZ_to_Luv(experiment_results.XYZ_ct, xy_w), xy_w) + uv_m = Luv_to_uv(XYZ_to_Luv(experiment_results.XYZ_cr, xy_wr), xy_wr) -CORRESPONDING_CHROMATICITIES_PREDICTION_MODELS = CaseInsensitiveMapping({ - 'CIE 1994': corresponding_chromaticities_prediction_CIE1994, - 'CMCCAT2000': corresponding_chromaticities_prediction_CMCCAT2000, - 'Fairchild 1990': corresponding_chromaticities_prediction_Fairchild1990, - 'Von Kries': corresponding_chromaticities_prediction_VonKries -}) + XYZ_1 = experiment_results.XYZ_ct + XYZ_2 = chromatic_adaptation_Zhai2018( + XYZ_1, XYZ_w, XYZ_wr, D_b, D_d, XYZ_wo, transform + ) + uv_p = Luv_to_uv(XYZ_to_Luv(XYZ_2, xy_wr), xy_wr) + + return tuple( + CorrespondingChromaticitiesPrediction( + experiment_results.name, uv_t[i], uv_m[i], uv_p[i] + ) + for i in range(len(uv_t)) + ) + + +CORRESPONDING_CHROMATICITIES_PREDICTION_MODELS = CaseInsensitiveMapping( + { + "CIE 1994": corresponding_chromaticities_prediction_CIE1994, + "CMCCAT2000": corresponding_chromaticities_prediction_CMCCAT2000, + "Fairchild 1990": corresponding_chromaticities_prediction_Fairchild1990, + "Von Kries": corresponding_chromaticities_prediction_VonKries, + "Zhai 2018": corresponding_chromaticities_prediction_Zhai2018, + } +) CORRESPONDING_CHROMATICITIES_PREDICTION_MODELS.__doc__ = """ Aggregated corresponding chromaticities prediction models. @@ -509,56 +716,78 @@ def corresponding_chromaticities_prediction_VonKries(experiment=1, ---------- :cite:`Breneman1987b`, :cite:`CIETC1-321994b`, :cite:`Fairchild1991a`, :cite:`Fairchild2013s`, :cite:`Fairchild2013t`, :cite:`Li2002a`, -:cite:`Westland2012k` - -CORRESPONDING_CHROMATICITIES_PREDICTION_MODELS : CaseInsensitiveMapping - **{'CIE 1994', 'CMCCAT2000', 'Fairchild 1990', 'Von Kries'}** +:cite:`Westland2012k`, :cite:`Zhai2018` Aliases: - 'vonkries': 'Von Kries' """ -CORRESPONDING_CHROMATICITIES_PREDICTION_MODELS['vonkries'] = ( - CORRESPONDING_CHROMATICITIES_PREDICTION_MODELS['Von Kries']) - - -def corresponding_chromaticities_prediction(experiment=1, - model='Von Kries', - **kwargs): +CORRESPONDING_CHROMATICITIES_PREDICTION_MODELS[ + "vonkries" +] = CORRESPONDING_CHROMATICITIES_PREDICTION_MODELS["Von Kries"] + + +def corresponding_chromaticities_prediction( + experiment: Union[ + Literal[1, 2, 3, 4, 6, 8, 9, 11, 12], CorrespondingColourDataset + ] = 1, + model: Union[ + Literal[ + "CIE 1994", + "CMCCAT2000", + "Fairchild 1990", + "Von Kries", + "Zhai 2018", + ], + str, + ] = "Von Kries", + **kwargs: Any, +) -> Tuple[CorrespondingChromaticitiesPrediction, ...]: """ - Returns the corresponding chromaticities prediction for given chromatic + Return the corresponding chromaticities prediction for given chromatic adaptation model. Parameters ---------- - experiment : integer or CorrespondingColourDataset, optional - {1, 2, 3, 4, 6, 8, 9, 11, 12} + experiment *Breneman (1987)* experiment number or :class:`colour.CorrespondingColourDataset` class instance. - model : unicode, optional - **{'Von Kries', 'CIE 1994', 'CMCCAT2000', 'Fairchild 1990'}**, + model Chromatic adaptation model. Other Parameters ---------------- - transform : unicode, optional + D_b + {:func:`colour.corresponding.\ +corresponding_chromaticities_prediction_Zhai2018`}, + Degree of adaptation :math:`D_{\\beta}` of input illuminant + :math:`\\beta`. + D_d {:func:`colour.corresponding.\ -corresponding_chromaticities_prediction_VonKries`}, - **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', - 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02 Brill 2008', - 'Bianco 2010', 'Bianco PC 2010'}**, +corresponding_chromaticities_prediction_Zhai2018`}, + Degree of adaptation :math:`D_{\\delta}` of output illuminant + :math:`\\delta`. + transform + {:func:`colour.corresponding.\ +corresponding_chromaticities_prediction_VonKries`, + :func:`colour.corresponding.\ +corresponding_chromaticities_prediction_Zhai2018`}, Chromatic adaptation transform. + XYZ_wo + {:func:`colour.corresponding.\ +corresponding_chromaticities_prediction_Zhai2018`}, + Baseline illuminant (:math:`BI`) :math:`o`. Returns ------- - tuple + :class:`tuple` Corresponding chromaticities prediction. References ---------- :cite:`Breneman1987b`, :cite:`CIETC1-321994b`, :cite:`Fairchild1991a`, :cite:`Fairchild2013s`, :cite:`Fairchild2013t`, :cite:`Li2002a`, - :cite:`Westland2012k` + :cite:`Westland2012k`, :cite:`Zhai2018` Examples -------- diff --git a/colour/corresponding/tests/__init__.py b/colour/corresponding/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/corresponding/tests/__init__.py +++ b/colour/corresponding/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/corresponding/tests/test_prediction.py b/colour/corresponding/tests/test_prediction.py index 0b4c26d5a1..85fd7a0eea 100644 --- a/colour/corresponding/tests/test_prediction.py +++ b/colour/corresponding/tests/test_prediction.py @@ -1,265 +1,404 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.corresponding.prediction` module. -""" +"""Defines the unit tests for the :mod:`colour.corresponding.prediction` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import unittest from colour.corresponding.prediction import ( - CorrespondingColourDataset, convert_experiment_results_Breneman1987) + CorrespondingColourDataset, + convert_experiment_results_Breneman1987, +) from colour.corresponding import ( - corresponding_chromaticities_prediction_VonKries, corresponding_chromaticities_prediction_CIE1994, corresponding_chromaticities_prediction_CMCCAT2000, - corresponding_chromaticities_prediction_Fairchild1990) + corresponding_chromaticities_prediction_Fairchild1990, + corresponding_chromaticities_prediction_VonKries, + corresponding_chromaticities_prediction_Zhai2018, +) +from colour.hints import NDArray -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'DATASET_CORRESPONDING_COLOUR_1', 'DATA_PREDICTION_FAIRCHILD1990', - 'DATA_PREDICTION_CIE1994', 'DATA_PREDICTION_CMCCAT2000', - 'DATA_PREDICTION_VONKRIES', - 'TestCorrespondingChromaticitiesPredictionFairchild1990', - 'TestCorrespondingChromaticitiesPredictionCIE1994', - 'TestCorrespondingChromaticitiesPredictionCMCCAT2000', - 'TestCorrespondingChromaticitiesPredictionVonKries' + "DATASET_CORRESPONDING_COLOUR_1", + "DATA_PREDICTION_FAIRCHILD1990", + "DATA_PREDICTION_CIE1994", + "DATA_PREDICTION_CMCCAT2000", + "DATA_PREDICTION_VONKRIES", + "DATA_PREDICTION_ZHAI2018", + "TestCorrespondingChromaticitiesPredictionFairchild1990", + "TestCorrespondingChromaticitiesPredictionCIE1994", + "TestCorrespondingChromaticitiesPredictionCMCCAT2000", + "TestCorrespondingChromaticitiesPredictionVonKries", + "TestCorrespondingChromaticitiesPredictionZhai2018", ] -DATASET_CORRESPONDING_COLOUR_1 = CorrespondingColourDataset( - name=1, - XYZ_r=np.array([0.947368421052632, 1.000000000000000, 1.000000000000000]), - XYZ_t=np.array([1.107889733840304, 1.000000000000000, 0.334125475285171]), - XYZ_cr=np.array([ - [372.358829568788565, 405.000000000000000, 345.746919917864489], - [250.638506876227865, 135.000000000000000, 37.131630648330052], - [456.541750503018136, 405.000000000000000, 267.487424547283638], - [502.185218978102114, 405.000000000000000, 24.758211678831920], - [164.036312849162016, 135.000000000000000, 24.511173184357535], - [422.727888086642622, 405.000000000000000, 27.231498194945619], - [110.245746691871460, 135.000000000000000, 53.846880907372366], - [75.208733205374273, 135.000000000000000, 77.281669865642954], - [258.414179104477626, 405.000000000000000, 479.480277185501166], - [141.154411764705856, 135.000000000000000, 469.124999999999943], - [380.757042253521092, 405.000000000000000, 700.193661971831261], - [192.236301369863071, 135.000000000000000, 370.510273972602761], - ]), - XYZ_ct=np.array([ - [450.407919847328230, 405.000000000000000, 143.566316793893037], - [267.090517241379359, 135.000000000000000, 11.831896551724059], - [531.851235741444839, 405.000000000000000, 107.602186311787023], - [603.033088235294031, 405.000000000000000, 7.444852941176350], - [196.511090573012893, 135.000000000000000, 8.109981515711597], - [526.868181818181711, 405.000000000000000, 8.468181818181574], - [144.589483394833962, 135.000000000000000, 24.035977859778562], - [108.161900369003689, 135.000000000000000, 36.178505535055272], - [317.877906976744100, 405.000000000000000, 223.691860465116235], - [126.960674157303373, 135.000000000000000, 192.792134831460686], - [419.434826883910489, 405.000000000000000, 309.730142566191489], - [185.180921052631589, 135.000000000000000, 151.430921052631589], - ]), - Y_r=np.array(1500), - Y_t=np.array(1500), - B_r=0.3, - B_t=0.3, - metadata={}) - -DATA_PREDICTION_FAIRCHILD1990 = np.array([ - [(0.199, 0.487), (0.200554934448681, 0.470155699619516)], - [(0.420, 0.509), (0.389214449896027, 0.514002881379267)], - [(0.249, 0.497), (0.241570029196649, 0.486033850388237)], - [(0.302, 0.548), (0.290619228590390, 0.550282593092481)], - [(0.290, 0.537), (0.280611339160018, 0.538348403477048)], - [(0.257, 0.554), (0.248379871541202, 0.555741679220336)], - [(0.192, 0.529), (0.194404361202511, 0.515869077684087)], - [(0.129, 0.521), (0.139197382975043, 0.499578426956623)], - [(0.133, 0.469), (0.140948349615191, 0.436038790232095)], - [(0.158, 0.340), (0.170240154737059, 0.311121250518367)], - [(0.178, 0.426), (0.184104505156227, 0.393143101221379)], - [(0.231, 0.365), (0.229252692804442, 0.338033995795458)], -]) - -DATA_PREDICTION_CIE1994 = np.array([ - [(0.199, 0.487), (0.261386136420622, 0.533662817418878)], - [(0.420, 0.509), (0.451546322781523, 0.521730655299867)], - [(0.249, 0.497), (0.310170032403198, 0.532080871352910)], - [(0.302, 0.548), (0.362647256496429, 0.543095116682339)], - [(0.290, 0.537), (0.342002205412566, 0.540654345195812)], - [(0.257, 0.554), (0.320554395869614, 0.549100152511420)], - [(0.192, 0.529), (0.249890733398992, 0.543032894366422)], - [(0.129, 0.521), (0.184477356124214, 0.545089114658268)], - [(0.133, 0.469), (0.179131489840910, 0.534176133801835)], - [(0.158, 0.340), (0.173452103066259, 0.480817508459012)], - [(0.178, 0.426), (0.226570643054285, 0.516988754071352)], - [(0.231, 0.365), (0.272300292824155, 0.482318190580811)], -]) - -DATA_PREDICTION_CMCCAT2000 = np.array([ - [(0.199, 0.487), (0.200011273633451, 0.470539230354398)], - [(0.420, 0.509), (0.407637147823942, 0.504672206472439)], - [(0.249, 0.497), (0.243303933122086, 0.483443483088972)], - [(0.302, 0.548), (0.302738872704423, 0.542771391372065)], - [(0.290, 0.537), (0.290517242292583, 0.531547633697717)], - [(0.257, 0.554), (0.258674719568568, 0.548764239788161)], - [(0.192, 0.529), (0.198993053394577, 0.512765194539863)], - [(0.129, 0.521), (0.143324176185281, 0.499673343018787)], - [(0.133, 0.469), (0.138424584031183, 0.442678688047099)], - [(0.158, 0.340), (0.153353027792519, 0.330121253315695)], - [(0.178, 0.426), (0.175382057838771, 0.401725889182425)], - [(0.231, 0.365), (0.213770919959778, 0.348685954116193)], -]) - -DATA_PREDICTION_VONKRIES = np.array([ - [(0.199, 0.487), (0.199994235295863, 0.470596132542110)], - [(0.420, 0.509), (0.414913855668385, 0.503766204685646)], - [(0.249, 0.497), (0.244202332779817, 0.483154861151019)], - [(0.302, 0.548), (0.307287743499555, 0.543174463393956)], - [(0.290, 0.537), (0.294129765202449, 0.531627707350365)], - [(0.257, 0.554), (0.261399171975815, 0.549476532253197)], - [(0.192, 0.529), (0.199113248438711, 0.512769667764083)], - [(0.129, 0.521), (0.142266217705415, 0.499812542997584)], - [(0.133, 0.469), (0.138134593378073, 0.443768079552099)], - [(0.158, 0.340), (0.154188271421900, 0.338322678880046)], - [(0.178, 0.426), (0.175297924104065, 0.404343935551269)], - [(0.231, 0.365), (0.213004721499844, 0.354595262694384)], -]) +DATASET_CORRESPONDING_COLOUR_1: CorrespondingColourDataset = ( + CorrespondingColourDataset( + name=1, + XYZ_r=np.array( + [0.947368421052632, 1.000000000000000, 1.000000000000000] + ), + XYZ_t=np.array( + [1.107889733840304, 1.000000000000000, 0.334125475285171] + ), + XYZ_cr=np.array( + [ + [ + 372.358829568788565, + 405.000000000000000, + 345.746919917864489, + ], + [250.638506876227865, 135.000000000000000, 37.131630648330052], + [ + 456.541750503018136, + 405.000000000000000, + 267.487424547283638, + ], + [502.185218978102114, 405.000000000000000, 24.758211678831920], + [164.036312849162016, 135.000000000000000, 24.511173184357535], + [422.727888086642622, 405.000000000000000, 27.231498194945619], + [110.245746691871460, 135.000000000000000, 53.846880907372366], + [75.208733205374273, 135.000000000000000, 77.281669865642954], + [ + 258.414179104477626, + 405.000000000000000, + 479.480277185501166, + ], + [ + 141.154411764705856, + 135.000000000000000, + 469.124999999999943, + ], + [ + 380.757042253521092, + 405.000000000000000, + 700.193661971831261, + ], + [ + 192.236301369863071, + 135.000000000000000, + 370.510273972602761, + ], + ] + ), + XYZ_ct=np.array( + [ + [ + 450.407919847328230, + 405.000000000000000, + 143.566316793893037, + ], + [267.090517241379359, 135.000000000000000, 11.831896551724059], + [ + 531.851235741444839, + 405.000000000000000, + 107.602186311787023, + ], + [603.033088235294031, 405.000000000000000, 7.444852941176350], + [196.511090573012893, 135.000000000000000, 8.109981515711597], + [526.868181818181711, 405.000000000000000, 8.468181818181574], + [144.589483394833962, 135.000000000000000, 24.035977859778562], + [108.161900369003689, 135.000000000000000, 36.178505535055272], + [ + 317.877906976744100, + 405.000000000000000, + 223.691860465116235, + ], + [ + 126.960674157303373, + 135.000000000000000, + 192.792134831460686, + ], + [ + 419.434826883910489, + 405.000000000000000, + 309.730142566191489, + ], + [ + 185.180921052631589, + 135.000000000000000, + 151.430921052631589, + ], + ] + ), + Y_r=np.array(1500), + Y_t=np.array(1500), + B_r=0.3, + B_t=0.3, + metadata={}, + ) +) + +DATA_PREDICTION_FAIRCHILD1990: NDArray = np.array( + [ + [(0.199, 0.487), (0.200554934448681, 0.470155699619516)], + [(0.420, 0.509), (0.389214449896027, 0.514002881379267)], + [(0.249, 0.497), (0.241570029196649, 0.486033850388237)], + [(0.302, 0.548), (0.290619228590390, 0.550282593092481)], + [(0.290, 0.537), (0.280611339160018, 0.538348403477048)], + [(0.257, 0.554), (0.248379871541202, 0.555741679220336)], + [(0.192, 0.529), (0.194404361202511, 0.515869077684087)], + [(0.129, 0.521), (0.139197382975043, 0.499578426956623)], + [(0.133, 0.469), (0.140948349615191, 0.436038790232095)], + [(0.158, 0.340), (0.170240154737059, 0.311121250518367)], + [(0.178, 0.426), (0.184104505156227, 0.393143101221379)], + [(0.231, 0.365), (0.229252692804442, 0.338033995795458)], + ] +) + +DATA_PREDICTION_CIE1994: NDArray = np.array( + [ + [(0.199, 0.487), (0.261386136420622, 0.533662817418878)], + [(0.420, 0.509), (0.451546322781523, 0.521730655299867)], + [(0.249, 0.497), (0.310170032403198, 0.532080871352910)], + [(0.302, 0.548), (0.362647256496429, 0.543095116682339)], + [(0.290, 0.537), (0.342002205412566, 0.540654345195812)], + [(0.257, 0.554), (0.320554395869614, 0.549100152511420)], + [(0.192, 0.529), (0.249890733398992, 0.543032894366422)], + [(0.129, 0.521), (0.184477356124214, 0.545089114658268)], + [(0.133, 0.469), (0.179131489840910, 0.534176133801835)], + [(0.158, 0.340), (0.173452103066259, 0.480817508459012)], + [(0.178, 0.426), (0.226570643054285, 0.516988754071352)], + [(0.231, 0.365), (0.272300292824155, 0.482318190580811)], + ] +) + +DATA_PREDICTION_CMCCAT2000: NDArray = np.array( + [ + [(0.199, 0.487), (0.200011273633451, 0.470539230354398)], + [(0.420, 0.509), (0.407637147823942, 0.504672206472439)], + [(0.249, 0.497), (0.243303933122086, 0.483443483088972)], + [(0.302, 0.548), (0.302738872704423, 0.542771391372065)], + [(0.290, 0.537), (0.290517242292583, 0.531547633697717)], + [(0.257, 0.554), (0.258674719568568, 0.548764239788161)], + [(0.192, 0.529), (0.198993053394577, 0.512765194539863)], + [(0.129, 0.521), (0.143324176185281, 0.499673343018787)], + [(0.133, 0.469), (0.138424584031183, 0.442678688047099)], + [(0.158, 0.340), (0.153353027792519, 0.330121253315695)], + [(0.178, 0.426), (0.175382057838771, 0.401725889182425)], + [(0.231, 0.365), (0.213770919959778, 0.348685954116193)], + ] +) + +DATA_PREDICTION_VONKRIES: NDArray = np.array( + [ + [(0.199, 0.487), (0.199994235295863, 0.470596132542110)], + [(0.420, 0.509), (0.414913855668385, 0.503766204685646)], + [(0.249, 0.497), (0.244202332779817, 0.483154861151019)], + [(0.302, 0.548), (0.307287743499555, 0.543174463393956)], + [(0.290, 0.537), (0.294129765202449, 0.531627707350365)], + [(0.257, 0.554), (0.261399171975815, 0.549476532253197)], + [(0.192, 0.529), (0.199113248438711, 0.512769667764083)], + [(0.129, 0.521), (0.142266217705415, 0.499812542997584)], + [(0.133, 0.469), (0.138134593378073, 0.443768079552099)], + [(0.158, 0.340), (0.154188271421900, 0.338322678880046)], + [(0.178, 0.426), (0.175297924104065, 0.404343935551269)], + [(0.231, 0.365), (0.213004721499844, 0.354595262694384)], + ] +) + +DATA_PREDICTION_ZHAI2018: NDArray = np.array( + [ + [(0.199, 0.487), (0.199994235295863, 0.470596132542110)], + [(0.420, 0.509), (0.414913855668385, 0.503766204685646)], + [(0.249, 0.497), (0.244202332779817, 0.483154861151019)], + [(0.302, 0.548), (0.307287743499555, 0.543174463393956)], + [(0.290, 0.537), (0.294129765202449, 0.531627707350365)], + [(0.257, 0.554), (0.261399171975815, 0.549476532253198)], + [(0.192, 0.529), (0.199113248438711, 0.512769667764083)], + [(0.129, 0.521), (0.142266217705415, 0.499812542997584)], + [(0.133, 0.469), (0.138134593378073, 0.443768079552098)], + [(0.158, 0.340), (0.154188271421900, 0.338322678880046)], + [(0.178, 0.426), (0.175297924104065, 0.404343935551269)], + [(0.231, 0.365), (0.213004721499844, 0.354595262694384)], + ] +) class TestConvertExperimentResultsBreneman1987(unittest.TestCase): """ - Defines :func:`colour.corresponding.prediction.\ + Define :func:`colour.corresponding.prediction.\ convert_experiment_results_Breneman1987` definition unit tests methods. """ def test_convert_experiment_results_Breneman1987(self): """ - Tests :func:`colour.corresponding.prediction.\ + Test :func:`colour.corresponding.prediction.\ convert_experiment_results_Breneman1987` definition. """ corresponding_colour_dataset = convert_experiment_results_Breneman1987( - 1) + 1 + ) np.testing.assert_almost_equal( corresponding_colour_dataset.XYZ_r, DATASET_CORRESPONDING_COLOUR_1.XYZ_r, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( corresponding_colour_dataset.XYZ_t, DATASET_CORRESPONDING_COLOUR_1.XYZ_t, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( corresponding_colour_dataset.XYZ_cr, DATASET_CORRESPONDING_COLOUR_1.XYZ_cr, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( corresponding_colour_dataset.XYZ_ct, DATASET_CORRESPONDING_COLOUR_1.XYZ_ct, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( corresponding_colour_dataset.Y_r, DATASET_CORRESPONDING_COLOUR_1.Y_r, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( corresponding_colour_dataset.Y_t, DATASET_CORRESPONDING_COLOUR_1.Y_t, - decimal=7) + decimal=7, + ) class TestCorrespondingChromaticitiesPredictionFairchild1990( - unittest.TestCase): # noqa + unittest.TestCase +): # noqa """ - Defines :func:`colour.corresponding.prediction.\ + Define :func:`colour.corresponding.prediction.\ corresponding_chromaticities_prediction_Fairchild1990` definition unit tests methods. """ def test_corresponding_chromaticities_prediction_Fairchild1990(self): """ - Tests :func:`colour.corresponding.prediction.\ + Test :func:`colour.corresponding.prediction.\ corresponding_chromaticities_prediction_Fairchild1990` definition. """ np.testing.assert_almost_equal( np.array( - [(p.uv_m, p.uv_p) for p in - corresponding_chromaticities_prediction_Fairchild1990()]), + [ + (p.uv_m, p.uv_p) + for p in corresponding_chromaticities_prediction_Fairchild1990() + ] + ), DATA_PREDICTION_FAIRCHILD1990, - decimal=7) + decimal=7, + ) class TestCorrespondingChromaticitiesPredictionCIE1994(unittest.TestCase): """ - Defines :func:`colour.corresponding.prediction.\ + Define :func:`colour.corresponding.prediction.\ corresponding_chromaticities_prediction_CIE1994` definition unit tests methods. """ def test_corresponding_chromaticities_prediction_CIE1994(self): """ - Tests :func:`colour.corresponding.prediction.\ + Test :func:`colour.corresponding.prediction.\ corresponding_chromaticities_prediction_CIE1994` definition. """ np.testing.assert_almost_equal( np.array( - [(p.uv_m, p.uv_p) - for p in corresponding_chromaticities_prediction_CIE1994()]), + [ + (p.uv_m, p.uv_p) + for p in corresponding_chromaticities_prediction_CIE1994() + ] + ), DATA_PREDICTION_CIE1994, - decimal=7) + decimal=7, + ) class TestCorrespondingChromaticitiesPredictionCMCCAT2000(unittest.TestCase): """ - Defines :func:`colour.corresponding.prediction.\ + Define :func:`colour.corresponding.prediction.\ corresponding_chromaticities_prediction_CMCCAT2000` definition unit tests methods. """ def test_corresponding_chromaticities_prediction_CMCCAT2000(self): """ - Tests :func:`colour.corresponding.prediction.\ + Test :func:`colour.corresponding.prediction.\ corresponding_chromaticities_prediction_CMCCAT2000` definition. """ np.testing.assert_almost_equal( - np.array([ - (p.uv_m, p.uv_p) - for p in corresponding_chromaticities_prediction_CMCCAT2000() - ]), + np.array( + [ + (p.uv_m, p.uv_p) + for p in corresponding_chromaticities_prediction_CMCCAT2000() + ] + ), DATA_PREDICTION_CMCCAT2000, - decimal=7) + decimal=7, + ) class TestCorrespondingChromaticitiesPredictionVonKries(unittest.TestCase): """ - Defines :func:`colour.corresponding.prediction.\ + Define :func:`colour.corresponding.prediction.\ corresponding_chromaticities_prediction_VonKries` definition unit tests methods. """ def test_corresponding_chromaticities_prediction_VonKries(self): """ - Tests :func:`colour.corresponding.prediction.\ + Test :func:`colour.corresponding.prediction.\ corresponding_chromaticities_prediction_VonKries` definition. """ np.testing.assert_almost_equal( np.array( - [(p.uv_m, p.uv_p) - for p in corresponding_chromaticities_prediction_VonKries()]), + [ + (p.uv_m, p.uv_p) + for p in corresponding_chromaticities_prediction_VonKries() + ] + ), DATA_PREDICTION_VONKRIES, - decimal=7) + decimal=7, + ) + + +class TestCorrespondingChromaticitiesPredictionZhai2018(unittest.TestCase): + """ + Define :func:`colour.corresponding.prediction.\ +corresponding_chromaticities_prediction_Zhai2018` definition unit tests + methods. + """ + + def test_corresponding_chromaticities_prediction_Zhai2018(self): + """ + Test :func:`colour.corresponding.prediction.\ +corresponding_chromaticities_prediction_Zhai2018` definition. + """ + + np.testing.assert_almost_equal( + np.array( + [ + (p.uv_m, p.uv_p) + for p in corresponding_chromaticities_prediction_Zhai2018() + ] + ), + DATA_PREDICTION_ZHAI2018, + decimal=7, + ) diff --git a/colour/difference/__init__.py b/colour/difference/__init__.py index d0225b16ec..1d52163328 100644 --- a/colour/difference/__init__.py +++ b/colour/difference/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ References ---------- @@ -31,37 +30,72 @@ August 29, 2014, from http://en.wikipedia.org/wiki/Color_difference """ -from __future__ import absolute_import +from __future__ import annotations -from colour.utilities import CaseInsensitiveMapping, filter_kwargs +from colour.hints import Any, ArrayLike, FloatingOrNDArray, Literal, Union +from colour.utilities import ( + CaseInsensitiveMapping, + filter_kwargs, + validate_method, +) from .cam02_ucs import delta_E_CAM02LCD, delta_E_CAM02SCD, delta_E_CAM02UCS from .cam16_ucs import delta_E_CAM16LCD, delta_E_CAM16SCD, delta_E_CAM16UCS -from .delta_e import (JND_CIE1976, delta_E_CIE1976, delta_E_CIE1994, - delta_E_CIE2000, delta_E_CMC) +from .delta_e import ( + JND_CIE1976, + delta_E_CIE1976, + delta_E_CIE1994, + delta_E_CIE2000, + delta_E_CMC, +) from .din99 import delta_E_DIN99 +from .huang2015 import power_function_Huang2015 +from .stress import index_stress_Garcia2007, INDEX_STRESS_METHODS, index_stress -__all__ = ['delta_E_CAM02LCD', 'delta_E_CAM02SCD', 'delta_E_CAM02UCS'] -__all__ += ['delta_E_CAM16LCD', 'delta_E_CAM16SCD', 'delta_E_CAM16UCS'] +__all__ = [ + "delta_E_CAM02LCD", + "delta_E_CAM02SCD", + "delta_E_CAM02UCS", +] +__all__ += [ + "delta_E_CAM16LCD", + "delta_E_CAM16SCD", + "delta_E_CAM16UCS", +] +__all__ += [ + "JND_CIE1976", + "delta_E_CIE1976", + "delta_E_CIE1994", + "delta_E_CIE2000", + "delta_E_CMC", +] +__all__ += [ + "delta_E_DIN99", +] +__all__ += [ + "power_function_Huang2015", +] __all__ += [ - 'JND_CIE1976', 'delta_E_CIE1976', 'delta_E_CIE1994', 'delta_E_CIE2000', - 'delta_E_CMC' + "index_stress_Garcia2007", + "INDEX_STRESS_METHODS", + "index_stress", ] -__all__ += ['delta_E_DIN99'] - -DELTA_E_METHODS = CaseInsensitiveMapping({ - 'CIE 1976': delta_E_CIE1976, - 'CIE 1994': delta_E_CIE1994, - 'CIE 2000': delta_E_CIE2000, - 'CMC': delta_E_CMC, - 'CAM02-LCD': delta_E_CAM02LCD, - 'CAM02-SCD': delta_E_CAM02SCD, - 'CAM02-UCS': delta_E_CAM02UCS, - 'CAM16-LCD': delta_E_CAM16LCD, - 'CAM16-SCD': delta_E_CAM16SCD, - 'CAM16-UCS': delta_E_CAM16UCS, - 'DIN99': delta_E_DIN99, -}) + +DELTA_E_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "CIE 1976": delta_E_CIE1976, + "CIE 1994": delta_E_CIE1994, + "CIE 2000": delta_E_CIE2000, + "CMC": delta_E_CMC, + "CAM02-LCD": delta_E_CAM02LCD, + "CAM02-SCD": delta_E_CAM02SCD, + "CAM02-UCS": delta_E_CAM02UCS, + "CAM16-LCD": delta_E_CAM16LCD, + "CAM16-SCD": delta_E_CAM16SCD, + "CAM16-UCS": delta_E_CAM16UCS, + "DIN99": delta_E_DIN99, + } +) DELTA_E_METHODS.__doc__ = """ Supported :math:`\\Delta E_{ab}` computation methods. @@ -71,40 +105,60 @@ :cite:`Lindbloom2011a`, :cite:`Lindbloom2009e`, :cite:`Lindbloom2009f`, :cite:`Luo2006b`, :cite:`Melgosa2013b`, :cite:`Wikipedia2008b` -DELTA_E_METHODS : CaseInsensitiveMapping - **{'CIE 1976', 'CIE 1994', 'CIE 2000', 'CMC', 'CAM02-LCD', 'CAM02-SCD', - 'CAM02-UCS', 'CAM16-LCD', 'CAM16-SCD', 'CAM16-UCS', 'DIN99'}** - Aliases: - 'cie1976': 'CIE 1976' - 'cie1994': 'CIE 1994' - 'cie2000': 'CIE 2000' """ -DELTA_E_METHODS['cie1976'] = DELTA_E_METHODS['CIE 1976'] -DELTA_E_METHODS['cie1994'] = DELTA_E_METHODS['CIE 1994'] -DELTA_E_METHODS['cie2000'] = DELTA_E_METHODS['CIE 2000'] - - -def delta_E(a, b, method='CIE 2000', **kwargs): +DELTA_E_METHODS["cie1976"] = DELTA_E_METHODS["CIE 1976"] +DELTA_E_METHODS["cie1994"] = DELTA_E_METHODS["CIE 1994"] +DELTA_E_METHODS["cie2000"] = DELTA_E_METHODS["CIE 2000"] + + +def delta_E( + a: ArrayLike, + b: ArrayLike, + method: Union[ + Literal[ + "CIE 1976", + "CIE 1994", + "CIE 2000", + "CMC", + "CAM02-LCD", + "CAM02-SCD", + "CAM02-UCS", + "CAM16-LCD", + "CAM16-SCD", + "CAM16-UCS", + "DIN99", + ], + str, + ] = "CIE 2000", + **kwargs: Any, +) -> FloatingOrNDArray: """ - Returns the difference :math:`\\Delta E_{ab}` between two given + Return the difference :math:`\\Delta E_{ab}` between two given *CIE L\\*a\\*b\\** or :math:`J'a'b'` colourspace arrays using given method. Parameters ---------- - a : array_like + a *CIE L\\*a\\*b\\** or :math:`J'a'b'` colourspace array :math:`a`. - b : array_like + b *CIE L\\*a\\*b\\** or :math:`J'a'b'` colourspace array :math:`b`. - method : unicode, optional - **{'CIE 2000', 'CIE 1976', 'CIE 1994', 'CMC', 'CAM02-LCD', 'CAM02-SCD', - 'CAM02-UCS', 'CAM16-LCD', 'CAM16-SCD', 'CAM16-UCS', 'DIN99'}** + method Computation method. Other Parameters ---------------- - textiles : bool, optional + c + {:func:`colour.difference.delta_E_CIE2000`}, + Chroma weighting factor. + l + {:func:`colour.difference.delta_E_CIE2000`}, + Lightness weighting factor. + textiles {:func:`colour.difference.delta_E_CIE1994`, :func:`colour.difference.delta_E_CIE2000`, :func:`colour.difference.delta_E_DIN99`}, @@ -112,16 +166,10 @@ def delta_E(a, b, method='CIE 2000', **kwargs): :math:`k_L=2,\\ k_C=k_H=1,\\ k_1=0.048,\\ k_2=0.014,\\ k_E=2,\ \\ k_CH=0.5` weights are used instead of :math:`k_L=k_C=k_H=1,\\ k_1=0.045,\\ k_2=0.015,\\ k_E=k_CH=1.0`. - l : numeric, optional - {:func:`colour.difference.delta_E_CIE2000`}, - Lightness weighting factor. - c : numeric, optional - {:func:`colour.difference.delta_E_CIE2000`}, - Chroma weighting factor. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Colour difference :math:`\\Delta E_{ab}`. References @@ -156,9 +204,14 @@ def delta_E(a, b, method='CIE 2000', **kwargs): 0.0001034... """ + method = validate_method(method, DELTA_E_METHODS) + function = DELTA_E_METHODS[method] return function(a, b, **filter_kwargs(function, **kwargs)) -__all__ += ['DELTA_E_METHODS', 'delta_E'] +__all__ += [ + "DELTA_E_METHODS", + "delta_E", +] diff --git a/colour/difference/cam02_ucs.py b/colour/difference/cam02_ucs.py index 88b2060621..156e018779 100644 --- a/colour/difference/cam02_ucs.py +++ b/colour/difference/cam02_ucs.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ :math:`\\Delta E'` - Delta E Colour Difference - Luo, Cui and Li (2006) -====================================================================== +======================================================================= -Defines :math:`\\Delta E'` colour difference computation objects based on *Luo -et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, and *CAM02-UCS* colourspaces: +Defines the :math:`\\Delta E'` colour difference computation objects based on +*Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, and *CAM02-UCS* colourspaces: - :func:`colour.difference.delta_E_CAM02LCD` - :func:`colour.difference.delta_E_CAM02SCD` @@ -17,52 +16,62 @@ Application, 31(4), 320-330. doi:10.1002/col.20227 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.utilities import tsplit from colour.models.cam02_ucs import COEFFICIENTS_UCS_LUO2006 +from colour.hints import ArrayLike, FloatingOrNDArray +from colour.utilities import as_float, tsplit -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'delta_E_Luo2006', 'delta_E_CAM02LCD', 'delta_E_CAM02SCD', - 'delta_E_CAM02UCS' + "delta_E_Luo2006", + "delta_E_CAM02LCD", + "delta_E_CAM02SCD", + "delta_E_CAM02UCS", ] -def delta_E_Luo2006(Jpapbp_1, Jpapbp_2, coefficients): +def delta_E_Luo2006( + Jpapbp_1: ArrayLike, Jpapbp_2: ArrayLike, coefficients: ArrayLike +) -> FloatingOrNDArray: """ - Returns the difference :math:`\\Delta E'` between two given + Return the difference :math:`\\Delta E'` between two given *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces :math:`J'a'b'` arrays. Parameters ---------- - Jpapbp_1 : array_like + Jpapbp_1 Standard / reference *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces :math:`J'a'b'` array. - Jpapbp_2 : array_like + Jpapbp_2 Sample / test *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces :math:`J'a'b'` array. - coefficients : array_like + coefficients Coefficients of one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Colour difference :math:`\\Delta E'`. + Warnings + -------- + The :math:`J'a'b'` array should have been computed with a + *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspace + and not with the *CIE L\\*a\\*b\\** colourspace. + Notes ----- - +--------------+------------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +==============+========================+====================+ @@ -93,33 +102,44 @@ def delta_E_Luo2006(Jpapbp_1, Jpapbp_2, coefficients): J_p_2, a_p_2, b_p_2 = tsplit(Jpapbp_2) K_L, _c_1, _c_2 = tsplit(coefficients) - d_E = np.sqrt(((J_p_1 - J_p_2) / K_L) ** 2 + (a_p_1 - a_p_2) ** 2 + - (b_p_1 - b_p_2) ** 2) - return d_E + d_E = np.sqrt( + ((J_p_1 - J_p_2) / K_L) ** 2 + + (a_p_1 - a_p_2) ** 2 + + (b_p_1 - b_p_2) ** 2 + ) + + return as_float(d_E) -def delta_E_CAM02LCD(Jpapbp_1, Jpapbp_2): +def delta_E_CAM02LCD( + Jpapbp_1: ArrayLike, Jpapbp_2: ArrayLike +) -> FloatingOrNDArray: """ - Returns the difference :math:`\\Delta E'` between two given + Return the difference :math:`\\Delta E'` between two given *Luo et al. (2006)* *CAM02-LCD* colourspaces :math:`J'a'b'` arrays. Parameters ---------- - Jpapbp_1 : array_like + Jpapbp_1 Standard / reference *Luo et al. (2006)* *CAM02-LCD* colourspaces :math:`J'a'b'` array. - Jpapbp_2 : array_like + Jpapbp_2 Sample / test *Luo et al. (2006)* *CAM02-LCD* colourspaces :math:`J'a'b'` array. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Colour difference :math:`\\Delta E'`. + Warnings + -------- + The :math:`J'a'b'` array should have been computed with a + *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspace + and not with the *CIE L\\*a\\*b\\** colourspace. + Notes ----- - +--------------+------------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +==============+========================+====================+ @@ -148,32 +168,40 @@ def delta_E_CAM02LCD(Jpapbp_1, Jpapbp_2): 14.0555464... """ - return delta_E_Luo2006(Jpapbp_1, Jpapbp_2, - COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) + return delta_E_Luo2006( + Jpapbp_1, Jpapbp_2, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ) -def delta_E_CAM02SCD(Jpapbp_1, Jpapbp_2): +def delta_E_CAM02SCD( + Jpapbp_1: ArrayLike, Jpapbp_2: ArrayLike +) -> FloatingOrNDArray: """ - Returns the difference :math:`\\Delta E'` between two given + Return the difference :math:`\\Delta E'` between two given *Luo et al. (2006)* *CAM02-SCD* colourspaces :math:`J'a'b'` arrays. Parameters ---------- - Jpapbp_1 : array_like + Jpapbp_1 Standard / reference *Luo et al. (2006)* *CAM02-SCD* colourspaces :math:`J'a'b'` array. - Jpapbp_2 : array_like + Jpapbp_2 Sample / test *Luo et al. (2006)* *CAM02-SCD* colourspaces :math:`J'a'b'` array. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Colour difference :math:`\\Delta E'`. + Warnings + -------- + The :math:`J'a'b'` array should have been computed with a + *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspace + and not with the *CIE L\\*a\\*b\\** colourspace. + Notes ----- - +--------------+------------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +==============+========================+====================+ @@ -202,32 +230,40 @@ def delta_E_CAM02SCD(Jpapbp_1, Jpapbp_2): 14.0551718... """ - return delta_E_Luo2006(Jpapbp_1, Jpapbp_2, - COEFFICIENTS_UCS_LUO2006['CAM02-SCD']) + return delta_E_Luo2006( + Jpapbp_1, Jpapbp_2, COEFFICIENTS_UCS_LUO2006["CAM02-SCD"] + ) -def delta_E_CAM02UCS(Jpapbp_1, Jpapbp_2): +def delta_E_CAM02UCS( + Jpapbp_1: ArrayLike, Jpapbp_2: ArrayLike +) -> FloatingOrNDArray: """ - Returns the difference :math:`\\Delta E'` between two given + Return the difference :math:`\\Delta E'` between two given *Luo et al. (2006)* *CAM02-UCS* colourspaces :math:`J'a'b'` arrays. Parameters ---------- - Jpapbp_1 : array_like + Jpapbp_1 Standard / reference *Luo et al. (2006)* *CAM02-UCS* colourspaces :math:`J'a'b'` array. - Jpapbp_2 : array_like + Jpapbp_2 Sample / test *Luo et al. (2006)* *CAM02-UCS* colourspaces :math:`J'a'b'` array. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Colour difference :math:`\\Delta E'`. + Warnings + -------- + The :math:`J'a'b'` array should have been computed with a + *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspace + and not with the *CIE L\\*a\\*b\\** colourspace. + Notes ----- - +--------------+------------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +==============+========================+====================+ @@ -256,5 +292,6 @@ def delta_E_CAM02UCS(Jpapbp_1, Jpapbp_2): 14.0552982... """ - return delta_E_Luo2006(Jpapbp_1, Jpapbp_2, - COEFFICIENTS_UCS_LUO2006['CAM02-UCS']) + return delta_E_Luo2006( + Jpapbp_1, Jpapbp_2, COEFFICIENTS_UCS_LUO2006["CAM02-UCS"] + ) diff --git a/colour/difference/cam16_ucs.py b/colour/difference/cam16_ucs.py index 19257f4069..e1001180ba 100644 --- a/colour/difference/cam16_ucs.py +++ b/colour/difference/cam16_ucs.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ :math:`\\Delta E'` - Delta E Colour Difference - Li et al. (2017) -================================================================ +================================================================= -Defines :math:`\\Delta E'` colour difference computation objects based on *Li, -Li, Wang, Zu, Luo, Cui, Melgosa, Brill and Pointer (2017)* *CAM16-LCD*, +Defines the :math:`\\Delta E'` colour difference computation objects based on +*Li, Li, Wang, Zu, Luo, Cui, Melgosa, Brill and Pointer (2017)* *CAM16-LCD*, *CAM16-SCD*, and *CAM16-UCS* colourspaces: - :func:`colour.difference.delta_E_CAM16LCD` @@ -19,38 +18,47 @@ 42(6), 703-718. doi:10.1002/col.22131 """ -from __future__ import division, unicode_literals - -from colour.difference.cam02_ucs import (delta_E_Luo2006, delta_E_CAM02LCD, - delta_E_CAM02SCD, delta_E_CAM02UCS) +from colour.difference.cam02_ucs import ( + delta_E_Luo2006, + delta_E_CAM02LCD, + delta_E_CAM02SCD, + delta_E_CAM02UCS, +) from colour.models.cam16_ucs import ( - _UCS_Luo2006_callable_to_UCS_Li2017_docstring) + _UCS_Luo2006_callable_to_UCS_Li2017_docstring, +) from colour.utilities import copy_definition -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'delta_E_Li2017', 'delta_E_CAM16LCD', 'delta_E_CAM16SCD', - 'delta_E_CAM16UCS' + "delta_E_Li2017", + "delta_E_CAM16LCD", + "delta_E_CAM16SCD", + "delta_E_CAM16UCS", ] -delta_E_Li2017 = copy_definition(delta_E_Luo2006, 'delta_E_Li2017') -delta_E_Li2017.__doc__ = ( - _UCS_Luo2006_callable_to_UCS_Li2017_docstring(delta_E_Luo2006)) +delta_E_Li2017 = copy_definition(delta_E_Luo2006, "delta_E_Li2017") +delta_E_Li2017.__doc__ = _UCS_Luo2006_callable_to_UCS_Li2017_docstring( + delta_E_Luo2006 +) -delta_E_CAM16LCD = copy_definition(delta_E_CAM02LCD, 'delta_E_CAM16LCD') -delta_E_CAM16LCD.__doc__ = ( - _UCS_Luo2006_callable_to_UCS_Li2017_docstring(delta_E_CAM02LCD)) +delta_E_CAM16LCD = copy_definition(delta_E_CAM02LCD, "delta_E_CAM16LCD") +delta_E_CAM16LCD.__doc__ = _UCS_Luo2006_callable_to_UCS_Li2017_docstring( + delta_E_CAM02LCD +) -delta_E_CAM16SCD = copy_definition(delta_E_CAM02SCD, 'delta_E_CAM16SCD') -delta_E_CAM16SCD.__doc__ = ( - _UCS_Luo2006_callable_to_UCS_Li2017_docstring(delta_E_CAM02SCD)) +delta_E_CAM16SCD = copy_definition(delta_E_CAM02SCD, "delta_E_CAM16SCD") +delta_E_CAM16SCD.__doc__ = _UCS_Luo2006_callable_to_UCS_Li2017_docstring( + delta_E_CAM02SCD +) -delta_E_CAM16UCS = copy_definition(delta_E_CAM02UCS, 'delta_E_CAM16UCS') -delta_E_CAM16UCS.__doc__ = ( - _UCS_Luo2006_callable_to_UCS_Li2017_docstring(delta_E_CAM02UCS)) +delta_E_CAM16UCS = copy_definition(delta_E_CAM02UCS, "delta_E_CAM16UCS") +delta_E_CAM16UCS.__doc__ = _UCS_Luo2006_callable_to_UCS_Li2017_docstring( + delta_E_CAM02UCS +) diff --git a/colour/difference/delta_e.py b/colour/difference/delta_e.py index ada6fa6cc3..dc86bd016d 100644 --- a/colour/difference/delta_e.py +++ b/colour/difference/delta_e.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ :math:`\\Delta E^*_{ab}` - Delta E Colour Difference ==================================================== -Defines :math:`\\Delta E^*_{ab}` colour difference computation objects: +Defines the :math:`\\Delta E^*_{ab}` colour difference computation objects: The following attributes and methods are available: @@ -33,24 +32,31 @@ Delta E - A survey. Machine Graphics and Vision, 20, 383-411. """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.algebra import euclidean_distance -from colour.utilities import to_domain_100, tsplit -from colour.utilities.documentation import (DocstringFloat, - is_documentation_building) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import ArrayLike, Boolean, Floating, FloatingOrNDArray +from colour.utilities import as_float, to_domain_100, tsplit +from colour.utilities.documentation import ( + DocstringFloat, + is_documentation_building, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'JND_CIE1976', 'delta_E_CIE1976', 'delta_E_CIE1994', 'delta_E_CIE2000', - 'delta_E_CMC' + "JND_CIE1976", + "delta_E_CIE1976", + "delta_E_CIE1994", + "delta_E_CIE2000", + "delta_E_CMC", ] JND_CIE1976 = 2.3 @@ -75,31 +81,28 @@ References ---------- :cite:`Mokrzycki2011` - -JND_CIE1976 : numeric """ -def delta_E_CIE1976(Lab_1, Lab_2): +def delta_E_CIE1976(Lab_1: ArrayLike, Lab_2: ArrayLike) -> FloatingOrNDArray: """ - Returns the difference :math:`\\Delta E_{76}` between two given + Return the difference :math:`\\Delta E_{76}` between two given *CIE L\\*a\\*b\\** colourspace arrays using *CIE 1976* recommendation. Parameters ---------- - Lab_1 : array_like + Lab_1 *CIE L\\*a\\*b\\** colourspace array 1. - Lab_2 : array_like + Lab_2 *CIE L\\*a\\*b\\** colourspace array 2. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Colour difference :math:`\\Delta E_{76}`. Notes ----- - +------------+-----------------------+-------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===================+ @@ -133,30 +136,31 @@ def delta_E_CIE1976(Lab_1, Lab_2): return d_E -def delta_E_CIE1994(Lab_1, Lab_2, textiles=False): +def delta_E_CIE1994( + Lab_1: ArrayLike, Lab_2: ArrayLike, textiles: Boolean = False +) -> FloatingOrNDArray: """ - Returns the difference :math:`\\Delta E_{94}` between two given + Return the difference :math:`\\Delta E_{94}` between two given *CIE L\\*a\\*b\\** colourspace arrays using *CIE 1994* recommendation. Parameters ---------- - Lab_1 : array_like + Lab_1 *CIE L\\*a\\*b\\** colourspace array 1. - Lab_2 : array_like + Lab_2 *CIE L\\*a\\*b\\** colourspace array 2. - textiles : bool, optional - Textiles application specific parametric factors + textiles + Textiles application specific parametric factors, :math:`k_L=2,\\ k_C=k_H=1,\\ k_1=0.048,\\ k_2=0.014` weights are used instead of :math:`k_L=k_C=k_H=1,\\ k_1=0.045,\\ k_2=0.015`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Colour difference :math:`\\Delta E_{94}`. Notes ----- - +------------+-----------------------+-------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===================+ @@ -213,7 +217,7 @@ def delta_E_CIE1994(Lab_1, Lab_2, textiles=False): delta_A = a_1 - a_2 delta_B = b_1 - b_2 - delta_H = np.sqrt(delta_A ** 2 + delta_B ** 2 - delta_C ** 2) + delta_H = np.sqrt(delta_A**2 + delta_B**2 - delta_C**2) L = (delta_L / (k_L * s_L)) ** 2 C = (delta_C / (k_C * s_C)) ** 2 @@ -221,33 +225,34 @@ def delta_E_CIE1994(Lab_1, Lab_2, textiles=False): d_E = np.sqrt(L + C + H) - return d_E + return as_float(d_E) -def delta_E_CIE2000(Lab_1, Lab_2, textiles=False): +def delta_E_CIE2000( + Lab_1: ArrayLike, Lab_2: ArrayLike, textiles: Boolean = False +) -> FloatingOrNDArray: """ - Returns the difference :math:`\\Delta E_{00}` between two given + Return the difference :math:`\\Delta E_{00}` between two given *CIE L\\*a\\*b\\** colourspace arrays using *CIE 2000* recommendation. Parameters ---------- - Lab_1 : array_like + Lab_1 *CIE L\\*a\\*b\\** colourspace array 1. - Lab_2 : array_like + Lab_2 *CIE L\\*a\\*b\\** colourspace array 2. - textiles : bool, optional - Textiles application specific parametric factors + textiles + Textiles application specific parametric factors. :math:`k_L=2,\\ k_C=k_H=1` weights are used instead of :math:`k_L=k_C=k_H=1`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Colour difference :math:`\\Delta E_{00}`. Notes ----- - +------------+-----------------------+-------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===================+ @@ -308,9 +313,9 @@ def delta_E_CIE2000(Lab_1, Lab_2, textiles=False): c_2 = np.hypot(a_2, b_2) c_bar = 0.5 * (c_1 + c_2) - c_bar7 = c_bar ** 7 + c_bar7 = c_bar**7 - g = 0.5 * (1 - np.sqrt(c_bar7 / (c_bar7 + 25 ** 7))) + g = 0.5 * (1 - np.sqrt(c_bar7 / (c_bar7 + 25**7))) a_1_prime = a_1 * (1 + g) a_2_prime = a_2 * (1 + g) @@ -327,10 +332,13 @@ def delta_E_CIE2000(Lab_1, Lab_2, textiles=False): (0.5 * (h_1_prime + h_2_prime + 360)), ) - t = (1 - 0.17 * np.cos(np.deg2rad(h_bar_prime - 30)) + - 0.24 * np.cos(np.deg2rad(2 * h_bar_prime)) + - 0.32 * np.cos(np.deg2rad(3 * h_bar_prime + 6)) - - 0.20 * np.cos(np.deg2rad(4 * h_bar_prime - 63))) + t = ( + 1 + - 0.17 * np.cos(np.deg2rad(h_bar_prime - 30)) + + 0.24 * np.cos(np.deg2rad(2 * h_bar_prime)) + + 0.32 * np.cos(np.deg2rad(3 * h_bar_prime + 6)) + - 0.20 * np.cos(np.deg2rad(4 * h_bar_prime - 63)) + ) h = h_2_prime - h_1_prime delta_h_prime = np.where(h_2_prime <= h_1_prime, h - 360, h + 360) @@ -338,34 +346,46 @@ def delta_E_CIE2000(Lab_1, Lab_2, textiles=False): delta_L_prime = L_2 - L_1 delta_C_prime = c_2_prime - c_1_prime - delta_H_prime = (2 * np.sqrt(c_1_prime * c_2_prime) * np.sin( - np.deg2rad(0.5 * delta_h_prime))) + delta_H_prime = ( + 2 + * np.sqrt(c_1_prime * c_2_prime) + * np.sin(np.deg2rad(0.5 * delta_h_prime)) + ) - s_L = 1 + ((0.015 * (l_bar_prime - 50) * (l_bar_prime - 50)) / - np.sqrt(20 + (l_bar_prime - 50) * (l_bar_prime - 50))) + s_L = 1 + ( + (0.015 * (l_bar_prime - 50) * (l_bar_prime - 50)) + / np.sqrt(20 + (l_bar_prime - 50) * (l_bar_prime - 50)) + ) s_C = 1 + 0.045 * c_bar_prime s_H = 1 + 0.015 * c_bar_prime * t - delta_theta = ( - 30 * np.exp(-((h_bar_prime - 275) / 25) * ((h_bar_prime - 275) / 25))) + delta_theta = 30 * np.exp( + -((h_bar_prime - 275) / 25) * ((h_bar_prime - 275) / 25) + ) - c_bar_prime7 = c_bar_prime ** 7 + c_bar_prime7 = c_bar_prime**7 - r_C = np.sqrt(c_bar_prime7 / (c_bar_prime7 + 25 ** 7)) + r_C = np.sqrt(c_bar_prime7 / (c_bar_prime7 + 25**7)) r_T = -2 * r_C * np.sin(np.deg2rad(2 * delta_theta)) - d_E = np.sqrt((delta_L_prime / (k_L * s_L)) ** 2 + - (delta_C_prime / (k_C * s_C)) ** 2 + - (delta_H_prime / (k_H * s_H)) ** 2 + - (delta_C_prime / (k_C * s_C)) * (delta_H_prime / - (k_H * s_H)) * r_T) + d_E = np.sqrt( + (delta_L_prime / (k_L * s_L)) ** 2 + + (delta_C_prime / (k_C * s_C)) ** 2 + + (delta_H_prime / (k_H * s_H)) ** 2 + + (delta_C_prime / (k_C * s_C)) * (delta_H_prime / (k_H * s_H)) * r_T + ) - return d_E + return as_float(d_E) -def delta_E_CMC(Lab_1, Lab_2, l=2, c=1): # noqa +def delta_E_CMC( + Lab_1: ArrayLike, + Lab_2: ArrayLike, + l: Floating = 2, # noqa + c: Floating = 1, +) -> FloatingOrNDArray: """ - Returns the difference :math:`\\Delta E_{CMC}` between two given + Return the difference :math:`\\Delta E_{CMC}` between two given *CIE L\\*a\\*b\\** colourspace arrays using *Colour Measurement Committee* recommendation. @@ -376,23 +396,22 @@ def delta_E_CMC(Lab_1, Lab_2, l=2, c=1): # noqa Parameters ---------- - Lab_1 : array_like + Lab_1 *CIE L\\*a\\*b\\** colourspace array 1. - Lab_2 : array_like + Lab_2 *CIE L\\*a\\*b\\** colourspace array 2. - l : numeric, optional + l Lightness weighting factor. - c : numeric, optional + c Chroma weighting factor. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Colour difference :math:`\\Delta E_{CMC}`. Notes ----- - +------------+-----------------------+-------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===================+ @@ -444,12 +463,12 @@ def delta_E_CMC(Lab_1, Lab_2, l=2, c=1): # noqa delta_C = c_1 - c_2 delta_A = a_1 - a_2 delta_B = b_1 - b_2 - delta_H2 = delta_A ** 2 + delta_B ** 2 - delta_C ** 2 + delta_H2 = delta_A**2 + delta_B**2 - delta_C**2 v_1 = delta_L / (l * s_l) v_2 = delta_C / (c * s_c) v_3 = s_h - d_E = np.sqrt(v_1 ** 2 + v_2 ** 2 + (delta_H2 / (v_3 * v_3))) + d_E = np.sqrt(v_1**2 + v_2**2 + (delta_H2 / (v_3 * v_3))) - return d_E + return as_float(d_E) diff --git a/colour/difference/din99.py b/colour/difference/din99.py index 757097c1e2..762e3e4731 100644 --- a/colour/difference/din99.py +++ b/colour/difference/din99.py @@ -1,7 +1,6 @@ -# -*- coding: utf-8 -*- """ :math:`\\Delta E_{99}` DIN99 - Colour Difference Formula -======================================================= +======================================================== Defines the :math:`\\Delta E_{99}` *DIN99* colour difference formula: @@ -15,42 +14,50 @@ doi:10.1520/D2244-16 """ -from __future__ import division, unicode_literals +from __future__ import annotations from colour.algebra import euclidean_distance +from colour.hints import ArrayLike, Boolean, FloatingOrNDArray from colour.models import Lab_to_DIN99 from colour.utilities import get_domain_range_scale -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['delta_E_DIN99'] +__all__ = [ + "delta_E_DIN99", +] -def delta_E_DIN99(Lab_1, Lab_2, textiles=False): +def delta_E_DIN99( + Lab_1: ArrayLike, Lab_2: ArrayLike, textiles: Boolean = False +) -> FloatingOrNDArray: """ - Returns the difference :math:`\\Delta E_{DIN99}` between two given + Return the difference :math:`\\Delta E_{DIN99}` between two given *CIE L\\*a\\*b\\** colourspace arrays using *DIN99* formula. Parameters ---------- - Lab_1 : array_like + Lab_1 *CIE L\\*a\\*b\\** colourspace array 1. - Lab_2 : array_like + Lab_2 *CIE L\\*a\\*b\\** colourspace array 2. + textiles + Textiles application specific parametric factors, + :math:`k_E=2,\\ k_{CH}=0.5` weights are used instead of + :math:`k_E=1,\\ k_{CH}=1`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Colour difference :math:`\\Delta E_{DIN99}`. Notes ----- - +------------+-----------------------+-------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===================+ @@ -83,10 +90,11 @@ def delta_E_DIN99(Lab_1, Lab_2, textiles=False): k_E = 2 if textiles else 1 k_CH = 0.5 if textiles else 1 - factor = 100 if get_domain_range_scale() == '1' else 1 + factor = 100 if get_domain_range_scale() == "1" else 1 d_E = euclidean_distance( Lab_to_DIN99(Lab_1, k_E, k_CH) * factor, - Lab_to_DIN99(Lab_2, k_E, k_CH) * factor) + Lab_to_DIN99(Lab_2, k_E, k_CH) * factor, + ) return d_E diff --git a/colour/difference/huang2015.py b/colour/difference/huang2015.py new file mode 100644 index 0000000000..59b3547382 --- /dev/null +++ b/colour/difference/huang2015.py @@ -0,0 +1,138 @@ +""" +Huang et al. (2015) Power-Functions +=================================== + +Defines the *Huang, Cui, Melgosa, Sanchez-Maranon, Li, Luo and Liu (2015)* +power-functions improving the performance of colour-difference formulas: + +- :func:`colour.difference.power_function_Huang2015` + +References +---------- +- :cite:`Huang2015` : Huang, M., Cui, G., Melgosa, M., Sanchez-Maranon, M., + Li, C., Luo, M. R., & Liu, H. (2015). Power functions improving the + performance of color-difference formulas. Optical Society of America, + 23(1), 597-610. doi:10.1364/OE.23.000597 +- :cite:`Li2017` : Li, C., Li, Z., Wang, Z., Xu, Y., Luo, M. R., Cui, G., + Melgosa, M., Brill, M. H., & Pointer, M. (2017). Comprehensive color + solutions: CAM16, CAT16, and CAM16-UCS. Color Research & Application, + 42(6), 703-718. doi:10.1002/col.22131 +""" + +from __future__ import annotations + +import numpy as np + +from colour.hints import FloatingOrArrayLike, FloatingOrNDArray, Literal, Union +from colour.utilities import CaseInsensitiveMapping, tsplit, validate_method + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "power_function_Huang2015", +] + +COEFFICIENTS_HUANG2015: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "CIE 1976": np.array([1.26, 0.55]), + "CIE 1994": np.array([1.41, 0.70]), + "CIE 2000": np.array([1.43, 0.70]), + "CMC": np.array([1.34, 0.66]), + "CAM02-LCD": np.array([1.00, 0.85]), + "CAM02-SCD": np.array([1.45, 0.75]), + "CAM02-UCS": np.array([1.30, 0.75]), + "CAM16-UCS": np.array([1.41, 0.63]), + "DIN99d": np.array([1.28, 0.74]), + "OSA": np.array([3.32, 0.62]), + "OSA-GP-Euclidean": np.array([1.52, 0.76]), + "ULAB": np.array([1.17, 0.69]), + } +) +COEFFICIENTS_HUANG2015.__doc__ = """ +*Huang et al. (2015)* power-functions coefficients. + +References +---------- +:cite:`Huang2015`, :cite:`Li2017` + +Notes +----- +- :cite:`Li2017` does not give the coefficients for the *CAM16-LCD* and + *CAM16-SCD* colourspaces. *Ronnie Luo* has been contacted to know if they + have been computed. + +Aliases: + +- 'cie1976': 'CIE 1976' +- 'cie1994': 'CIE 1994' +- 'cie2000': 'CIE 2000' +""" +COEFFICIENTS_HUANG2015["cie1976"] = COEFFICIENTS_HUANG2015["CIE 1976"] +COEFFICIENTS_HUANG2015["cie1994"] = COEFFICIENTS_HUANG2015["CIE 1994"] +COEFFICIENTS_HUANG2015["cie2000"] = COEFFICIENTS_HUANG2015["CIE 2000"] + + +def power_function_Huang2015( + d_E: FloatingOrArrayLike, + coefficients: Union[ + Literal[ + "CIE 1976", + "CIE 1994", + "CIE 2000", + "CMC", + "CAM02-LCD", + "CAM02-SCD", + "CAM16-UCS", + "DIN99d", + "OSA", + "OSA-GP-Euclidean", + "ULAB", + ], + str, + ] = "CIE 2000", +) -> FloatingOrNDArray: + """ + Improve the performance of the :math:`\\Delta E` value for given + coefficients using + *Huang, Cui, Melgosa, Sanchez-Maranon, Li, Luo and Liu (2015)* + power-function: :math:`d_E^{\\prime}=a*d_{E^b}`. + + Parameters + ---------- + d_E + Computed colour difference array :math:`\\Delta E`. + coefficients + Coefficients for the power-function. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + Improved math:`\\Delta E` value. + + References + ---------- + :cite:`Huang2015`, :cite:`Li2017` + + Examples + -------- + >>> d_E = np.array([2.0425, 2.8615, 3.4412]) + >>> power_function_Huang2015(d_E) # doctest: +ELLIPSIS + array([ 2.3574879..., 2.9850503..., 3.3965106...]) + """ + + coefficients = validate_method( + coefficients, + COEFFICIENTS_HUANG2015, + '"{0}" coefficients are invalid, ' "they must be one of {1}!", + ) + + a, b = tsplit(COEFFICIENTS_HUANG2015[coefficients]) + + d_E_p = a * d_E**b + + return d_E_p diff --git a/colour/difference/stress.py b/colour/difference/stress.py new file mode 100644 index 0000000000..bc1a207282 --- /dev/null +++ b/colour/difference/stress.py @@ -0,0 +1,145 @@ +""" +Standardized Residual Sum of Squares (STRESS) Index +=================================================== + +Defines the *Kruskal's Standardized Residual Sum of Squares (:math:`STRESS`)* +index: + +- :func:`colour.index_stress_Garcia2007`: :math:`STRESS` index computation + according to *García, Huertas, Melgosa and Cui (2007)* method. +- :func:`colour.index_stress`: *Lightness* :math:`STRESS` index computation + according to given method. + +References +---------- +- :cite:`Garcia2007` : García, P. A., Huertas, R., Melgosa, M., & Cui, G. + (2007). Measurement of the relationship between perceived and computed + color differences. Journal of the Optical Society of America A, 24(7), + 1823. doi:10.1364/JOSAA.24.001823 +""" + +from __future__ import annotations + +import numpy as np + +from colour.hints import FloatingOrArrayLike, FloatingOrNDArray, Literal, Union +from colour.utilities import ( + CaseInsensitiveMapping, + as_float, + as_float_array, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "index_stress_Garcia2007", + "INDEX_STRESS_METHODS", + "index_stress", +] + + +def index_stress_Garcia2007( + d_E: FloatingOrArrayLike, d_V: FloatingOrArrayLike +) -> FloatingOrNDArray: + """ + Compute the + *Kruskal's Standardized Residual Sum of Squares (:math:`STRESS`)* + index according to *García, Huertas, Melgosa and Cui (2007)* method. + + Parameters + ---------- + d_E + Computed colour difference array :math:`\\Delta E`. + d_V + Computed colour difference array :math:`\\Delta V`. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + :math:`STRESS` index. + + References + ---------- + :cite:`Garcia2007` + + Examples + -------- + >>> d_E = np.array([2.0425, 2.8615, 3.4412]) + >>> d_V = np.array([1.2644, 1.2630, 1.8731]) + >>> index_stress_Garcia2007(d_E, d_V) # doctest: +ELLIPSIS + 0.1211709... + """ + + d_E = as_float_array(d_E) + d_V = as_float_array(d_V) + + F_1 = np.sum(d_E**2) / np.sum(d_E * d_V) + + stress = np.sqrt( + np.sum((d_E - F_1 * d_V) ** 2) / np.sum(F_1**2 * d_V**2) + ) + + return as_float(stress) + + +INDEX_STRESS_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Garcia 2007": index_stress_Garcia2007, + } +) +INDEX_STRESS_METHODS.__doc__ = """ +Supported :math:`STRESS` index computation methods. + +References +---------- +:cite:`Garcia2007` +""" + + +def index_stress( + d_E: FloatingOrArrayLike, + d_V: FloatingOrArrayLike, + method: Union[Literal["Garcia 2007"], str] = "Garcia 2007", +) -> FloatingOrNDArray: + """ + Compute the + *Kruskal's Standardized Residual Sum of Squares (:math:`STRESS`)* + index according to given method. + + Parameters + ---------- + d_E + Computed colour difference array :math:`\\Delta E`. + d_V + Computed colour difference array :math:`\\Delta V`. + method + Computation method. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + :math:`STRESS` index. + + References + ---------- + :cite:`Garcia2007` + + Examples + -------- + >>> d_E = np.array([2.0425, 2.8615, 3.4412]) + >>> d_V = np.array([1.2644, 1.2630, 1.8731]) + >>> index_stress(d_E, d_V) # doctest: +ELLIPSIS + 0.1211709... + """ + + method = validate_method(method, INDEX_STRESS_METHODS) + + function = INDEX_STRESS_METHODS[method] + + return function(d_E, d_V) diff --git a/colour/difference/tests/__init__.py b/colour/difference/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/difference/tests/__init__.py +++ b/colour/difference/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/difference/tests/test__init__.py b/colour/difference/tests/test__init__.py index 69943d323e..39a6befb7a 100644 --- a/colour/difference/tests/test__init__.py +++ b/colour/difference/tests/test__init__.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.difference` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.difference` module.""" import numpy as np import unittest @@ -12,42 +7,43 @@ from colour.utilities import domain_range_scale -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestDelta_E'] +__all__ = [ + "TestDelta_E", +] class TestDelta_E(unittest.TestCase): - """ - Defines :func:`colour.difference.delta_E` definition unit tests methods. - """ + """Define :func:`colour.difference.delta_E` definition unit tests methods.""" def test_domain_range_scale_delta_E(self): """ - Tests :func:`colour.difference.delta_E` definition domain and range + Test :func:`colour.difference.delta_E` definition domain and range scale support. """ Lab_1 = np.array([100.00000000, 21.57210357, 272.22819350]) Lab_2 = np.array([100.00000000, 426.67945353, 72.39590835]) - m = ('CIE 1976', 'CIE 1994', 'CIE 2000', 'CMC', 'DIN99') + m = ("CIE 1976", "CIE 1994", "CIE 2000", "CMC", "DIN99") v = [delta_E(Lab_1, Lab_2, method) for method in m] - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for method, value in zip(m, v): for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( delta_E(Lab_1 * factor, Lab_2 * factor, method), value, - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/difference/tests/test_cam02_ucs.py b/colour/difference/tests/test_cam02_ucs.py index e2ad97c9d1..969045b1c1 100644 --- a/colour/difference/tests/test_cam02_ucs.py +++ b/colour/difference/tests/test_cam02_ucs.py @@ -1,112 +1,126 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.difference.cam02_ucs` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.difference.cam02_ucs` module.""" import numpy as np import unittest from itertools import permutations -from colour.difference import (delta_E_CAM02LCD, delta_E_CAM02SCD, - delta_E_CAM02UCS) +from colour.difference import ( + delta_E_CAM02LCD, + delta_E_CAM02SCD, + delta_E_CAM02UCS, +) from colour.difference.cam02_ucs import delta_E_Luo2006 from colour.models.cam02_ucs import COEFFICIENTS_UCS_LUO2006 from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestDelta_E_Luo2006'] +__all__ = [ + "TestDelta_E_Luo2006", +] class TestDelta_E_Luo2006(unittest.TestCase): """ - Defines :func:`colour.difference.cam02_ucs.delta_E_Luo2006` definition unit + Define :func:`colour.difference.cam02_ucs.delta_E_Luo2006` definition unit tests methods. """ def test_delta_E_Luo2006(self): - """ - Tests :func:`colour.difference.cam02_ucs.delta_E_Luo2006` definition. - """ + """Test :func:`colour.difference.cam02_ucs.delta_E_Luo2006` definition.""" self.assertAlmostEqual( delta_E_Luo2006( np.array([54.90433134, -0.08450395, -0.06854831]), np.array([54.80352754, -3.96940084, -13.57591013]), - COEFFICIENTS_UCS_LUO2006['CAM02-LCD']), + COEFFICIENTS_UCS_LUO2006["CAM02-LCD"], + ), 14.055546437777583, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_Luo2006( np.array([54.90433134, -0.08450395, -0.06854831]), np.array([54.80352754, -3.96940084, -13.57591013]), - COEFFICIENTS_UCS_LUO2006['CAM02-LCD']), + COEFFICIENTS_UCS_LUO2006["CAM02-LCD"], + ), delta_E_CAM02LCD( np.array([54.90433134, -0.08450395, -0.06854831]), - np.array([54.80352754, -3.96940084, -13.57591013])), - places=7) + np.array([54.80352754, -3.96940084, -13.57591013]), + ), + places=7, + ) self.assertAlmostEqual( delta_E_Luo2006( np.array([54.90433134, -0.08450395, -0.06854831]), np.array([54.80352754, -3.96940084, -13.57591013]), - COEFFICIENTS_UCS_LUO2006['CAM02-SCD']), + COEFFICIENTS_UCS_LUO2006["CAM02-SCD"], + ), delta_E_CAM02SCD( np.array([54.90433134, -0.08450395, -0.06854831]), - np.array([54.80352754, -3.96940084, -13.57591013])), - places=7) + np.array([54.80352754, -3.96940084, -13.57591013]), + ), + places=7, + ) self.assertAlmostEqual( delta_E_Luo2006( np.array([54.90433134, -0.08450395, -0.06854831]), np.array([54.80352754, -3.96940084, -13.57591013]), - COEFFICIENTS_UCS_LUO2006['CAM02-UCS']), + COEFFICIENTS_UCS_LUO2006["CAM02-UCS"], + ), delta_E_CAM02UCS( np.array([54.90433134, -0.08450395, -0.06854831]), - np.array([54.80352754, -3.96940084, -13.57591013])), - places=7) + np.array([54.80352754, -3.96940084, -13.57591013]), + ), + places=7, + ) def test_n_dimensional_delta_E_Luo2006(self): """ - Tests :func:`colour.difference.cam02_ucs.delta_E_Luo2006` definition + Test :func:`colour.difference.cam02_ucs.delta_E_Luo2006` definition n-dimensional arrays support. """ Jpapbp_1 = np.array([54.90433134, -0.08450395, -0.06854831]) Jpapbp_2 = np.array([54.80352754, -3.96940084, -13.57591013]) - delta_E_p = delta_E_Luo2006(Jpapbp_1, Jpapbp_2, - COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) + delta_E_p = delta_E_Luo2006( + Jpapbp_1, Jpapbp_2, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ) Jpapbp_1 = np.tile(Jpapbp_1, (6, 1)) Jpapbp_2 = np.tile(Jpapbp_2, (6, 1)) delta_E_p = np.tile(delta_E_p, 6) np.testing.assert_almost_equal( - delta_E_Luo2006(Jpapbp_1, Jpapbp_2, - COEFFICIENTS_UCS_LUO2006['CAM02-LCD']), + delta_E_Luo2006( + Jpapbp_1, Jpapbp_2, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ), delta_E_p, - decimal=7) + decimal=7, + ) Jpapbp_1 = np.reshape(Jpapbp_1, (2, 3, 3)) Jpapbp_2 = np.reshape(Jpapbp_2, (2, 3, 3)) delta_E_p = np.reshape(delta_E_p, (2, 3)) np.testing.assert_almost_equal( - delta_E_Luo2006(Jpapbp_1, Jpapbp_2, - COEFFICIENTS_UCS_LUO2006['CAM02-LCD']), + delta_E_Luo2006( + Jpapbp_1, Jpapbp_2, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ), delta_E_p, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_delta_E_Luo2006(self): """ - Tests :func:`colour.difference.cam02_ucs.delta_E_Luo2006` + Test :func:`colour.difference.cam02_ucs.delta_E_Luo2006` definition nan support. """ @@ -115,9 +129,10 @@ def test_nan_delta_E_Luo2006(self): for case in cases: Jpapbp_1 = np.array(case) Jpapbp_2 = np.array(case) - delta_E_Luo2006(Jpapbp_1, Jpapbp_2, - COEFFICIENTS_UCS_LUO2006['CAM02-LCD']), + delta_E_Luo2006( + Jpapbp_1, Jpapbp_2, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ), -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/difference/tests/test_cam16_ucs.py b/colour/difference/tests/test_cam16_ucs.py index f88a35201f..8341ab0e8d 100644 --- a/colour/difference/tests/test_cam16_ucs.py +++ b/colour/difference/tests/test_cam16_ucs.py @@ -1,27 +1,24 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.difference.cam16_ucs` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.difference.cam16_ucs` module.""" import unittest from colour.difference.tests.test_cam02_ucs import TestDelta_E_Luo2006 -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestDelta_E_Li2017'] +__all__ = [ + "TestDelta_E_Li2017", +] class TestDelta_E_Li2017(TestDelta_E_Luo2006): """ - Defines :func:`colour.difference.cam16_ucs.delta_E_Li2017` definition unit + Define :func:`colour.difference.cam16_ucs.delta_E_Li2017` definition unit tests methods. Notes @@ -32,5 +29,5 @@ class TestDelta_E_Li2017(TestDelta_E_Luo2006): """ -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/difference/tests/test_delta_e.py b/colour/difference/tests/test_delta_e.py index f40c14c47f..26aee34132 100644 --- a/colour/difference/tests/test_delta_e.py +++ b/colour/difference/tests/test_delta_e.py @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.difference.delta_e` module. +Defines the unit tests for the :mod:`colour.difference.delta_e` module. References ---------- @@ -10,34 +9,38 @@ 30(1), 21-30. doi:10.1002/col.20070 """ -from __future__ import division, unicode_literals - import numpy as np import unittest from itertools import permutations -from colour.difference import (delta_E_CIE1976, delta_E_CIE1994, - delta_E_CIE2000, delta_E_CMC) +from colour.difference import ( + delta_E_CIE1976, + delta_E_CIE1994, + delta_E_CIE2000, + delta_E_CMC, +) from colour.algebra import euclidean_distance from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestDelta_E_CIE1976', 'TestDelta_E_CIE1994', 'TestDelta_E_CIE2000', - 'TestDelta_E_CMC' + "TestDelta_E_CIE1976", + "TestDelta_E_CIE1994", + "TestDelta_E_CIE2000", + "TestDelta_E_CMC", ] class TestDelta_E_CIE1976(unittest.TestCase): """ - Defines :func:`colour.difference.delta_e.delta_E_CIE1976` definition unit + Define :func:`colour.difference.delta_e.delta_E_CIE1976` definition unit tests methods. Notes @@ -48,9 +51,7 @@ class TestDelta_E_CIE1976(unittest.TestCase): """ def test_delta_E_CIE1976(self): - """ - Tests :func:`colour.difference.delta_e.delta_E_CIE1976` definition. - """ + """Test :func:`colour.difference.delta_e.delta_E_CIE1976` definition.""" Lab_1 = np.array([100.00000000, 21.57210357, 272.22819350]) Lab_2 = np.array([100.00000000, 426.67945353, 72.39590835]) @@ -60,11 +61,12 @@ def test_delta_E_CIE1976(self): np.testing.assert_almost_equal( delta_E_CIE1976(Lab_1, Lab_2), euclidean_distance(Lab_1, Lab_2), - decimal=7) + decimal=7, + ) def test_n_dimensional_delta_E_CIE1976(self): """ - Tests :func:`colour.difference.delta_e.delta_E_CIE1976` definition + Test :func:`colour.difference.delta_e.delta_E_CIE1976` definition n-dimensional arrays support. """ @@ -72,25 +74,26 @@ def test_n_dimensional_delta_E_CIE1976(self): def test_domain_range_scale_delta_E_CIE1976(self): """ - Tests :func:`colour.difference.delta_e.delta_E_CIE1976` definition + Test :func:`colour.difference.delta_e.delta_E_CIE1976` definition domain and range scale support. """ Lab_1 = np.array([100.00000000, 21.57210357, 272.22819350]) Lab_2 = np.array([100.00000000, 426.67945353, 72.39590835]) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( delta_E_CIE1976(Lab_1 * factor, Lab_2 * factor), euclidean_distance(Lab_1, Lab_2), - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_delta_E_CIE1976(self): """ - Tests :func:`colour.difference.delta_e.delta_E_CIE1976` definition nan + Test :func:`colour.difference.delta_e.delta_E_CIE1976` definition nan support. """ @@ -99,63 +102,73 @@ def test_nan_delta_E_CIE1976(self): class TestDelta_E_CIE1994(unittest.TestCase): """ - Defines :func:`colour.difference.delta_e.delta_E_CIE1994` definition unit + Define :func:`colour.difference.delta_e.delta_E_CIE1994` definition unit tests methods. """ def test_delta_E_CIE1994(self): - """ - Tests :func:`colour.difference.delta_e.delta_E_CIE1994` definition. - """ + """Test :func:`colour.difference.delta_e.delta_E_CIE1994` definition.""" self.assertAlmostEqual( delta_E_CIE1994( np.array([100.00000000, 21.57210357, 272.22819350]), - np.array([100.00000000, 426.67945353, 72.39590835])), + np.array([100.00000000, 426.67945353, 72.39590835]), + ), 83.779225500887094, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_CIE1994( np.array([100.00000000, 21.57210357, 272.22819350]), - np.array([100.00000000, 74.05216981, 276.45318193])), + np.array([100.00000000, 74.05216981, 276.45318193]), + ), 10.053931954553839, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_CIE1994( np.array([100.00000000, 21.57210357, 272.22819350]), - np.array([100.00000000, 8.32281957, -73.58297716])), + np.array([100.00000000, 8.32281957, -73.58297716]), + ), 57.535453706667425, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_CIE1994( np.array([100.00000000, 21.57210357, 272.22819350]), np.array([100.00000000, 426.67945353, 72.39590835]), - textiles=True), + textiles=True, + ), 88.335553057506502, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_CIE1994( np.array([100.00000000, 21.57210357, 272.22819350]), np.array([100.00000000, 74.05216981, 276.45318193]), - textiles=True), + textiles=True, + ), 10.612657890048272, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_CIE1994( np.array([100.00000000, 21.57210357, 272.22819350]), np.array([100.00000000, 8.32281957, -73.58297716]), - textiles=True), + textiles=True, + ), 60.368687261063329, - places=7) + places=7, + ) def test_n_dimensional_delta_E_CIE1994(self): """ - Tests :func:`colour.difference.delta_e.delta_E_CIE1994` definition + Test :func:`colour.difference.delta_e.delta_E_CIE1994` definition n-dimensional arrays support. """ @@ -167,17 +180,19 @@ def test_n_dimensional_delta_E_CIE1994(self): Lab_2 = np.tile(Lab_2, (6, 1)) delta_E = np.tile(delta_E, 6) np.testing.assert_almost_equal( - delta_E_CIE1994(Lab_1, Lab_2), delta_E, decimal=7) + delta_E_CIE1994(Lab_1, Lab_2), delta_E, decimal=7 + ) Lab_1 = np.reshape(Lab_1, (2, 3, 3)) Lab_2 = np.reshape(Lab_2, (2, 3, 3)) delta_E = np.reshape(delta_E, (2, 3)) np.testing.assert_almost_equal( - delta_E_CIE1994(Lab_1, Lab_2), delta_E, decimal=7) + delta_E_CIE1994(Lab_1, Lab_2), delta_E, decimal=7 + ) def test_domain_range_scale_delta_E_CIE1994(self): """ - Tests :func:`colour.difference.delta_e.delta_E_CIE1994` definition + Test :func:`colour.difference.delta_e.delta_E_CIE1994` definition domain and range scale support. """ @@ -185,18 +200,19 @@ def test_domain_range_scale_delta_E_CIE1994(self): Lab_2 = np.array([100.00000000, 426.67945353, 72.39590835]) delta_E = delta_E_CIE1994(Lab_1, Lab_2) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( delta_E_CIE1994(Lab_1 * factor, Lab_2 * factor), delta_E, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_delta_E_CIE1994(self): """ - Tests :func:`colour.difference.delta_e.delta_E_CIE1994` definition nan + Test :func:`colour.difference.delta_e.delta_E_CIE1994` definition nan support. """ @@ -210,63 +226,73 @@ def test_nan_delta_E_CIE1994(self): class TestDelta_E_CIE2000(unittest.TestCase): """ - Defines :func:`colour.difference.delta_e.delta_E_CIE2000` definition unit + Define :func:`colour.difference.delta_e.delta_E_CIE2000` definition unit tests methods. """ def test_delta_E_CIE2000(self): - """ - Tests :func:`colour.difference.delta_e.delta_E_CIE2000` definition. - """ + """Test :func:`colour.difference.delta_e.delta_E_CIE2000` definition.""" self.assertAlmostEqual( delta_E_CIE2000( np.array([100.00000000, 21.57210357, 272.22819350]), - np.array([100.00000000, 426.67945353, 72.39590835])), + np.array([100.00000000, 426.67945353, 72.39590835]), + ), 94.03564903, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_CIE2000( np.array([100.00000000, 21.57210357, 272.22819350]), - np.array([100.00000000, 74.05216981, 276.45318193])), + np.array([100.00000000, 74.05216981, 276.45318193]), + ), 14.87906419, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_CIE2000( np.array([100.00000000, 21.57210357, 272.22819350]), - np.array([100.00000000, 8.32281957, -73.58297716])), + np.array([100.00000000, 8.32281957, -73.58297716]), + ), 68.23094879, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_CIE2000( np.array([100.00000000, 21.57210357, 272.22819350]), np.array([50.00000000, 426.67945353, 72.39590835]), - textiles=True), + textiles=True, + ), 95.79205352, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_CIE2000( np.array([100.00000000, 21.57210357, 272.22819350]), np.array([50.00000000, 74.05216981, 276.45318193]), - textiles=True), + textiles=True, + ), 23.55420943, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_CIE2000( np.array([100.00000000, 21.57210357, 272.22819350]), np.array([50.00000000, 8.32281957, -73.58297716]), - textiles=True), + textiles=True, + ), 70.63198003, - places=7) + places=7, + ) def test_n_dimensional_delta_E_CIE2000(self): """ - Tests :func:`colour.difference.delta_e.delta_E_CIE2000` definition + Test :func:`colour.difference.delta_e.delta_E_CIE2000` definition n-dimensional arrays support. """ @@ -278,17 +304,19 @@ def test_n_dimensional_delta_E_CIE2000(self): Lab_2 = np.tile(Lab_2, (6, 1)) delta_E = np.tile(delta_E, 6) np.testing.assert_almost_equal( - delta_E_CIE2000(Lab_1, Lab_2), delta_E, decimal=7) + delta_E_CIE2000(Lab_1, Lab_2), delta_E, decimal=7 + ) Lab_1 = np.reshape(Lab_1, (2, 3, 3)) Lab_2 = np.reshape(Lab_2, (2, 3, 3)) delta_E = np.reshape(delta_E, (2, 3)) np.testing.assert_almost_equal( - delta_E_CIE2000(Lab_1, Lab_2), delta_E, decimal=7) + delta_E_CIE2000(Lab_1, Lab_2), delta_E, decimal=7 + ) def test_domain_range_scale_delta_E_CIE2000(self): """ - Tests :func:`colour.difference.delta_e.delta_E_CIE2000` definition + Test :func:`colour.difference.delta_e.delta_E_CIE2000` definition domain and range scale support. """ @@ -296,18 +324,19 @@ def test_domain_range_scale_delta_E_CIE2000(self): Lab_2 = np.array([100.00000000, 426.67945353, 72.39590835]) delta_E = delta_E_CIE2000(Lab_1, Lab_2) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( delta_E_CIE2000(Lab_1 * factor, Lab_2 * factor), delta_E, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_delta_E_CIE2000(self): """ - Tests :func:`colour.difference.delta_e.delta_E_CIE2000` definition nan + Test :func:`colour.difference.delta_e.delta_E_CIE2000` definition nan support. """ @@ -320,7 +349,7 @@ def test_nan_delta_E_CIE2000(self): def test_delta_E_CIE2000_Sharma2004(self): """ - Tests :func:`colour.difference.delta_e.delta_E_CIE2000` definition + Test :func:`colour.difference.delta_e.delta_E_CIE2000` definition using Sharma (2004) dataset. References @@ -328,151 +357,197 @@ def test_delta_E_CIE2000_Sharma2004(self): :cite:`Sharma2005b` """ - Lab_1 = np.array([ - [50.0000, 2.6772, -79.7751], - [50.0000, 3.1571, -77.2803], - [50.0000, 2.8361, -74.0200], - [50.0000, -1.3802, -84.2814], - [50.0000, -1.1848, -84.8006], - [50.0000, -0.9009, -85.5211], - [50.0000, 0.0000, 0.0000], - [50.0000, -1.0000, 2.0000], - [50.0000, 2.4900, -0.0010], - [50.0000, 2.4900, -0.0010], - [50.0000, 2.4900, -0.0010], - [50.0000, 2.4900, -0.0010], - [50.0000, -0.0010, 2.4900], - [50.0000, -0.0010, 2.4900], - [50.0000, -0.0010, 2.4900], - [50.0000, 2.5000, 0.0000], - [50.0000, 2.5000, 0.0000], - [50.0000, 2.5000, 0.0000], - [50.0000, 2.5000, 0.0000], - [50.0000, 2.5000, 0.0000], - [50.0000, 2.5000, 0.0000], - [50.0000, 2.5000, 0.0000], - [50.0000, 2.5000, 0.0000], - [50.0000, 2.5000, 0.0000], - [60.2574, -34.0099, 36.2677], - [63.0109, -31.0961, -5.8663], - [61.2901, 3.7196, -5.3901], - [35.0831, -44.1164, 3.7933], - [22.7233, 20.0904, -46.6940], - [36.4612, 47.8580, 18.3852], - [90.8027, -2.0831, 1.4410], - [90.9257, -0.5406, -0.9208], - [6.7747, -0.2908, -2.4247], - [2.0776, 0.0795, -1.1350], - ]) - - Lab_2 = np.array([ - [50.0000, 0.0000, -82.7485], - [50.0000, 0.0000, -82.7485], - [50.0000, 0.0000, -82.7485], - [50.0000, 0.0000, -82.7485], - [50.0000, 0.0000, -82.7485], - [50.0000, 0.0000, -82.7485], - [50.0000, -1.0000, 2.0000], - [50.0000, 0.0000, 0.0000], - [50.0000, -2.4900, 0.0009], - [50.0000, -2.4900, 0.0010], - [50.0000, -2.4900, 0.0011], - [50.0000, -2.4900, 0.0012], - [50.0000, 0.0009, -2.4900], - [50.0000, 0.0010, -2.4900], - [50.0000, 0.0011, -2.4900], - [50.0000, 0.0000, -2.5000], - [73.0000, 25.0000, -18.0000], - [61.0000, -5.0000, 29.0000], - [56.0000, -27.0000, -3.0000], - [58.0000, 24.0000, 15.0000], - [50.0000, 3.1736, 0.5854], - [50.0000, 3.2972, 0.0000], - [50.0000, 1.8634, 0.5757], - [50.0000, 3.2592, 0.3350], - [60.4626, -34.1751, 39.4387], - [62.8187, -29.7946, -4.0864], - [61.4292, 2.2480, -4.9620], - [35.0232, -40.0716, 1.5901], - [23.0331, 14.9730, -42.5619], - [36.2715, 50.5065, 21.2231], - [91.1528, -1.6435, 0.0447], - [88.6381, -0.8985, -0.7239], - [5.8714, -0.0985, -2.2286], - [0.9033, -0.0636, -0.5514], - ]) - - d_E = np.array([ - 2.0425, 2.8615, 3.4412, 1.0000, 1.0000, 1.0000, 2.3669, 2.3669, - 7.1792, 7.1792, 7.2195, 7.2195, 4.8045, 4.8045, 4.7461, 4.3065, - 27.1492, 22.8977, 31.9030, 19.4535, 1.0000, 1.0000, 1.0000, 1.0000, - 1.2644, 1.2630, 1.8731, 1.8645, 2.0373, 1.4146, 1.4441, 1.5381, - 0.6377, 0.9082 - ]) + Lab_1 = np.array( + [ + [50.0000, 2.6772, -79.7751], + [50.0000, 3.1571, -77.2803], + [50.0000, 2.8361, -74.0200], + [50.0000, -1.3802, -84.2814], + [50.0000, -1.1848, -84.8006], + [50.0000, -0.9009, -85.5211], + [50.0000, 0.0000, 0.0000], + [50.0000, -1.0000, 2.0000], + [50.0000, 2.4900, -0.0010], + [50.0000, 2.4900, -0.0010], + [50.0000, 2.4900, -0.0010], + [50.0000, 2.4900, -0.0010], + [50.0000, -0.0010, 2.4900], + [50.0000, -0.0010, 2.4900], + [50.0000, -0.0010, 2.4900], + [50.0000, 2.5000, 0.0000], + [50.0000, 2.5000, 0.0000], + [50.0000, 2.5000, 0.0000], + [50.0000, 2.5000, 0.0000], + [50.0000, 2.5000, 0.0000], + [50.0000, 2.5000, 0.0000], + [50.0000, 2.5000, 0.0000], + [50.0000, 2.5000, 0.0000], + [50.0000, 2.5000, 0.0000], + [60.2574, -34.0099, 36.2677], + [63.0109, -31.0961, -5.8663], + [61.2901, 3.7196, -5.3901], + [35.0831, -44.1164, 3.7933], + [22.7233, 20.0904, -46.6940], + [36.4612, 47.8580, 18.3852], + [90.8027, -2.0831, 1.4410], + [90.9257, -0.5406, -0.9208], + [6.7747, -0.2908, -2.4247], + [2.0776, 0.0795, -1.1350], + ] + ) + + Lab_2 = np.array( + [ + [50.0000, 0.0000, -82.7485], + [50.0000, 0.0000, -82.7485], + [50.0000, 0.0000, -82.7485], + [50.0000, 0.0000, -82.7485], + [50.0000, 0.0000, -82.7485], + [50.0000, 0.0000, -82.7485], + [50.0000, -1.0000, 2.0000], + [50.0000, 0.0000, 0.0000], + [50.0000, -2.4900, 0.0009], + [50.0000, -2.4900, 0.0010], + [50.0000, -2.4900, 0.0011], + [50.0000, -2.4900, 0.0012], + [50.0000, 0.0009, -2.4900], + [50.0000, 0.0010, -2.4900], + [50.0000, 0.0011, -2.4900], + [50.0000, 0.0000, -2.5000], + [73.0000, 25.0000, -18.0000], + [61.0000, -5.0000, 29.0000], + [56.0000, -27.0000, -3.0000], + [58.0000, 24.0000, 15.0000], + [50.0000, 3.1736, 0.5854], + [50.0000, 3.2972, 0.0000], + [50.0000, 1.8634, 0.5757], + [50.0000, 3.2592, 0.3350], + [60.4626, -34.1751, 39.4387], + [62.8187, -29.7946, -4.0864], + [61.4292, 2.2480, -4.9620], + [35.0232, -40.0716, 1.5901], + [23.0331, 14.9730, -42.5619], + [36.2715, 50.5065, 21.2231], + [91.1528, -1.6435, 0.0447], + [88.6381, -0.8985, -0.7239], + [5.8714, -0.0985, -2.2286], + [0.9033, -0.0636, -0.5514], + ] + ) + + d_E = np.array( + [ + 2.0425, + 2.8615, + 3.4412, + 1.0000, + 1.0000, + 1.0000, + 2.3669, + 2.3669, + 7.1792, + 7.1792, + 7.2195, + 7.2195, + 4.8045, + 4.8045, + 4.7461, + 4.3065, + 27.1492, + 22.8977, + 31.9030, + 19.4535, + 1.0000, + 1.0000, + 1.0000, + 1.0000, + 1.2644, + 1.2630, + 1.8731, + 1.8645, + 2.0373, + 1.4146, + 1.4441, + 1.5381, + 0.6377, + 0.9082, + ] + ) np.testing.assert_almost_equal( - delta_E_CIE2000(Lab_1, Lab_2), d_E, decimal=4) + delta_E_CIE2000(Lab_1, Lab_2), d_E, decimal=4 + ) class TestDelta_E_CMC(unittest.TestCase): """ - Defines :func:`colour.difference.delta_e.delta_E_CMC` definition units + Define :func:`colour.difference.delta_e.delta_E_CMC` definition unit tests methods. """ def test_delta_E_CMC(self): - """ - Tests :func:`colour.difference.delta_e.delta_E_CMC` definition. - """ + """Test :func:`colour.difference.delta_e.delta_E_CMC` definition.""" self.assertAlmostEqual( delta_E_CMC( np.array([100.00000000, 21.57210357, 272.22819350]), - np.array([100.00000000, 426.67945353, 72.39590835])), + np.array([100.00000000, 426.67945353, 72.39590835]), + ), 172.70477129, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_CMC( np.array([100.00000000, 21.57210357, 272.22819350]), - np.array([100.00000000, 74.05216981, 276.45318193])), + np.array([100.00000000, 74.05216981, 276.45318193]), + ), 20.59732717, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_CMC( np.array([100.00000000, 21.57210357, 272.22819350]), - np.array([100.00000000, 8.32281957, -73.58297716])), + np.array([100.00000000, 8.32281957, -73.58297716]), + ), 121.71841479, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_CMC( np.array([100.00000000, 21.57210357, 272.22819350]), np.array([100.00000000, 426.67945353, 72.39590835]), - l=1), # noqa + l=1, + ), # noqa 172.70477129, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_CMC( np.array([100.00000000, 21.57210357, 272.22819350]), np.array([100.00000000, 74.05216981, 276.45318193]), - l=1), # noqa + l=1, + ), # noqa 20.59732717, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_CMC( np.array([100.00000000, 21.57210357, 272.22819350]), np.array([100.00000000, 8.32281957, -73.58297716]), - l=1), # noqa + l=1, + ), # noqa 121.71841479, - places=7) + places=7, + ) def test_n_dimensional_delta_E_CMC(self): """ - Tests :func:`colour.difference.delta_e.delta_E_CMC` definition + Test :func:`colour.difference.delta_e.delta_E_CMC` definition n-dimensional arrays support. """ @@ -484,17 +559,19 @@ def test_n_dimensional_delta_E_CMC(self): Lab_2 = np.tile(Lab_2, (6, 1)) delta_E = np.tile(delta_E, 6) np.testing.assert_almost_equal( - delta_E_CMC(Lab_1, Lab_2), delta_E, decimal=7) + delta_E_CMC(Lab_1, Lab_2), delta_E, decimal=7 + ) Lab_1 = np.reshape(Lab_1, (2, 3, 3)) Lab_2 = np.reshape(Lab_2, (2, 3, 3)) delta_E = np.reshape(delta_E, (2, 3)) np.testing.assert_almost_equal( - delta_E_CMC(Lab_1, Lab_2), delta_E, decimal=7) + delta_E_CMC(Lab_1, Lab_2), delta_E, decimal=7 + ) def test_domain_range_scale_delta_E_CMC(self): """ - Tests :func:`colour.difference.delta_e.delta_E_CMC` definition + Test :func:`colour.difference.delta_e.delta_E_CMC` definition domain and range scale support. """ @@ -502,18 +579,19 @@ def test_domain_range_scale_delta_E_CMC(self): Lab_2 = np.array([100.00000000, 426.67945353, 72.39590835]) delta_E = delta_E_CMC(Lab_1, Lab_2) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( delta_E_CMC(Lab_1 * factor, Lab_2 * factor), delta_E, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_delta_E_CMC(self): """ - Tests :func:`colour.difference.delta_e.delta_E_CMC` definition nan + Test :func:`colour.difference.delta_e.delta_E_CMC` definition nan support. """ @@ -525,5 +603,5 @@ def test_nan_delta_E_CMC(self): delta_E_CMC(Lab_1, Lab_2) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/difference/tests/test_din99.py b/colour/difference/tests/test_din99.py index da3ec872cc..bcb2c987e4 100644 --- a/colour/difference/tests/test_din99.py +++ b/colour/difference/tests/test_din99.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.difference.din99` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.difference.din99` module.""" import numpy as np import unittest @@ -13,75 +8,87 @@ from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestDelta_E_DIN99'] +__all__ = [ + "TestDelta_E_DIN99", +] class TestDelta_E_DIN99(unittest.TestCase): """ - Defines :func:`colour.difference.din99.delta_E_DIN99` definition unit + Define :func:`colour.difference.din99.delta_E_DIN99` definition unit tests methods. """ def test_delta_E_DIN99(self): - """ - Tests :func:`colour.difference.din99.delta_E_DIN99` definition. - """ + """Test :func:`colour.difference.din99.delta_E_DIN99` definition.""" self.assertAlmostEqual( delta_E_DIN99( np.array([60.25740000, -34.00990000, 36.26770000]), - np.array([60.46260000, -34.17510000, 39.43870000])), + np.array([60.46260000, -34.17510000, 39.43870000]), + ), 1.177216620111552, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_DIN99( np.array([63.01090000, -31.09610000, -5.86630000]), - np.array([62.81870000, -29.79460000, -4.08640000])), + np.array([62.81870000, -29.79460000, -4.08640000]), + ), 0.987529977993114, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_DIN99( np.array([35.08310000, -44.11640000, 3.79330000]), - np.array([35.02320000, -40.07160000, 1.59010000])), + np.array([35.02320000, -40.07160000, 1.59010000]), + ), 1.535894757971742, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_DIN99( np.array([60.25740000, -34.00990000, 36.26770000]), np.array([60.46260000, -34.17510000, 39.43870000]), - textiles=True), + textiles=True, + ), 1.215652775586509, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_DIN99( np.array([63.01090000, -31.09610000, -5.86630000]), np.array([62.81870000, -29.79460000, -4.08640000]), - textiles=True), + textiles=True, + ), 1.025997138865984, - places=7) + places=7, + ) self.assertAlmostEqual( delta_E_DIN99( np.array([35.08310000, -44.11640000, 3.79330000]), np.array([35.02320000, -40.07160000, 1.59010000]), - textiles=True), + textiles=True, + ), 1.539922810033725, - places=7) + places=7, + ) def test_n_dimensional_delta_E_DIN99(self): """ - Tests :func:`colour.difference.din99.delta_E_DIN99` definition + Test :func:`colour.difference.din99.delta_E_DIN99` definition n-dimensional arrays support. """ @@ -93,17 +100,19 @@ def test_n_dimensional_delta_E_DIN99(self): Lab_2 = np.tile(Lab_2, (6, 1)) delta_E = np.tile(delta_E, 6) np.testing.assert_almost_equal( - delta_E_DIN99(Lab_1, Lab_2), delta_E, decimal=7) + delta_E_DIN99(Lab_1, Lab_2), delta_E, decimal=7 + ) Lab_1 = np.reshape(Lab_1, (2, 3, 3)) Lab_2 = np.reshape(Lab_2, (2, 3, 3)) delta_E = np.reshape(delta_E, (2, 3)) np.testing.assert_almost_equal( - delta_E_DIN99(Lab_1, Lab_2), delta_E, decimal=7) + delta_E_DIN99(Lab_1, Lab_2), delta_E, decimal=7 + ) def test_domain_range_scale_delta_E_DIN99(self): """ - Tests :func:`colour.difference.din99.delta_E_DIN99` definition + Test :func:`colour.difference.din99.delta_E_DIN99` definition domain and range scale support. """ @@ -111,18 +120,19 @@ def test_domain_range_scale_delta_E_DIN99(self): Lab_2 = np.array([60.46260000, -34.17510000, 39.43870000]) delta_E = delta_E_DIN99(Lab_1, Lab_2) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( delta_E_DIN99(Lab_1 * factor, Lab_2 * factor), delta_E, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_delta_E_DIN99(self): """ - Tests :func:`colour.difference.din99.delta_E_DIN99` definition nan + Test :func:`colour.difference.din99.delta_E_DIN99` definition nan support. """ @@ -134,5 +144,5 @@ def test_nan_delta_E_DIN99(self): delta_E_DIN99(Lab_1, Lab_2) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/difference/tests/test_huang2015.py b/colour/difference/tests/test_huang2015.py new file mode 100644 index 0000000000..9e6bd01ac8 --- /dev/null +++ b/colour/difference/tests/test_huang2015.py @@ -0,0 +1,42 @@ +"""Defines the unit tests for the :mod:`colour.difference.huang2015` module.""" + +import numpy as np +import unittest + +from colour.difference import power_function_Huang2015 + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestPowerFunctionHuang2015", +] + + +class TestPowerFunctionHuang2015(unittest.TestCase): + """ + Define :func:`colour.difference.huang2015.power_function_Huang2015` + definition unit tests methods. + """ + + def test_power_function_Huang2015(self): + """ + Test :func:`colour.difference.huang2015.power_function_Huang2015` + definition. + """ + + d_E = np.array([2.0425, 2.8615, 3.4412]) + + np.testing.assert_almost_equal( + power_function_Huang2015(d_E), + np.array([2.35748796, 2.98505036, 3.39651062]), + decimal=7, + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/difference/tests/test_stress.py b/colour/difference/tests/test_stress.py new file mode 100644 index 0000000000..90b2e89383 --- /dev/null +++ b/colour/difference/tests/test_stress.py @@ -0,0 +1,41 @@ +"""Defines the unit tests for the :mod:`colour.difference.stress` module.""" + +import numpy as np +import unittest + +from colour.difference import index_stress + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestIndexStress", +] + + +class TestIndexStress(unittest.TestCase): + """ + Define :func:`colour.difference.stress.index_stress_Garcia2007` definition + unit tests methods. + """ + + def test_index_stress(self): + """ + Test :func:`colour.difference.stress.index_stress_Garcia2007` + definition. + """ + + d_E = np.array([2.0425, 2.8615, 3.4412]) + d_V = np.array([1.2644, 1.2630, 1.8731]) + + self.assertAlmostEqual( + index_stress(d_E, d_V), 0.121170939369957, places=7 + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/examples/adaptation/examples_cie1994.py b/colour/examples/adaptation/examples_cie1994.py index 38012b36ba..2273a99fdb 100644 --- a/colour/examples/adaptation/examples_cie1994.py +++ b/colour/examples/adaptation/examples_cie1994.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *CIE 1994* chromatic adaptation model computations. -""" +"""Showcases *CIE 1994* chromatic adaptation model computations.""" import numpy as np @@ -16,22 +13,30 @@ Y_o = 20 E_o1 = 1000 E_o2 = 1000 -message_box(('Computing chromatic adaptation using "CIE 1994" chromatic ' - 'adaptation model.\n' - '\n\t"XYZ_1":\n\t\t{0}\n\t"xy_o1":\n\t\t{1}\n\t"xy_o2":\n\t\t{2}' - '\n\t"Y_o":\n\t\t{3}\n\t"E_o1":\n\t\t{4}' - '\n\t"E_o2":\n\t\t{5}\n\n' - 'Warning: The input domain and output range of that definition ' - 'are non standard!'.format(XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2))) +message_box( + f'Computing chromatic adaptation using "CIE 1994" chromatic adaptation ' + f"model.\n\n" + f'\t"XYZ_1": {XYZ_1}\n' + f'\t"xy_o1": {xy_o1}\n' + f'\t"xy_o2": {xy_o2}\n' + f'\t"Y_o": {Y_o}\n' + f'\t"E_o1": {E_o1}\n' + f'\t"E_o2": {E_o2}' +) print( colour.chromatic_adaptation( XYZ_1, colour.xy_to_XYZ(xy_o1), colour.xy_to_XYZ(xy_o2), - method='CIE 1994', + method="CIE 1994", Y_o=Y_o, E_o1=E_o1, - E_o2=E_o2)) + E_o2=E_o2, + ) +) print( - colour.adaptation.chromatic_adaptation_CIE1994(XYZ_1 * 100.0, xy_o1, xy_o2, - Y_o, E_o1, E_o2) / 100.0) + colour.adaptation.chromatic_adaptation_CIE1994( + XYZ_1 * 100, xy_o1, xy_o2, Y_o, E_o1, E_o2 + ) + / 100 +) diff --git a/colour/examples/adaptation/examples_cmccat2000.py b/colour/examples/adaptation/examples_cmccat2000.py index f2cd73a24e..2e2f659117 100644 --- a/colour/examples/adaptation/examples_cmccat2000.py +++ b/colour/examples/adaptation/examples_cmccat2000.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *CMCCAT2000* chromatic adaptation model computations. -""" +"""Showcases *CMCCAT2000* chromatic adaptation model computations.""" import numpy as np @@ -15,37 +12,53 @@ XYZ_wr = np.array([0.9481, 1.0000, 1.0730]) L_A1 = 200 L_A2 = 200 -message_box(('Computing chromatic adaptation using "CMCCAT200" forward ' - 'chromatic adaptation model.\n' - '\n\t"XYZ":\n\t\t{0}\n\t"XYZ_w":\n\t\t{1}\n\t"XYZ_wr":\n\t\t{2}' - '\n\t"L_A1":\n\t\t{3}\n\t"L_A2":\n\t\t{4}\n\n' - 'Warning: The input domain and output range of that definition ' - 'are non standard!'.format(XYZ, XYZ_w, XYZ_wr, L_A1, L_A2))) +message_box( + f'Computing chromatic adaptation using "CMCCAT200" forward chromatic ' + f"adaptation model.\n\n" + f'\t"XYZ":\n\t\t{XYZ}\n' + f'\t"XYZ_w":\n\t\t{XYZ_w}\n' + f'\t"XYZ_wr":\n\t\t{XYZ_wr}\n' + f'\t"L_A1":\n\t\t{L_A1}\n' + f'\t"L_A2":\n\t\t{L_A2}' +) print( colour.chromatic_adaptation( - XYZ, XYZ_w, XYZ_wr, method='CMCCAT2000', L_A1=L_A1, L_A2=L_A2)) + XYZ, XYZ_w, XYZ_wr, method="CMCCAT2000", L_A1=L_A1, L_A2=L_A2 + ) +) print( colour.adaptation.chromatic_adaptation_CMCCAT2000( - XYZ * 100.0, XYZ_w, XYZ_wr, L_A1, L_A2) / 100.0) + XYZ * 100, XYZ_w, XYZ_wr, L_A1, L_A2 + ) + / 100 +) -print('\n') +print("\n") XYZ_c = np.array([0.19526983, 0.23068340, 0.24971752]) -message_box(('Computing chromatic adaptation using "CMCCAT200" inverse ' - 'chromatic adaptation model.\n' - '\n\t"XYZ_c":\n\t\t{0}\n\t"XYZ_w":\n\t\t{1}\n\t"XYZ_wr":\n\t\t{2}' - '\n\t"L_A1":\n\t\t{3}\n\t"L_A2":\n\t\t{4}\n\n' - 'Warning: The input domain and output range of that definition ' - 'are non standard!'.format(XYZ_c, XYZ_w, XYZ_wr, L_A1, L_A2))) +message_box( + f'Computing chromatic adaptation using "CMCCAT200" inverse chromatic ' + f"adaptation model.\n\n" + f'\t"XYZ_c": {XYZ_c}\n' + f'\t"XYZ_w": {XYZ_w}\n' + f'\t"XYZ_wr": {XYZ_wr}\n' + f'\t"L_A1": {L_A1}\n' + f'\t"L_A2": {L_A2}' +) print( colour.chromatic_adaptation( XYZ_c, XYZ_w, XYZ_wr, - method='CMCCAT2000', + method="CMCCAT2000", L_A1=L_A1, L_A2=L_A2, - direction='Inverse')) + direction="Inverse", + ) +) print( colour.adaptation.chromatic_adaptation_CMCCAT2000( - XYZ_c * 100.0, XYZ_w, XYZ_wr, L_A1, L_A2, direction='Inverse') / 100.0) + XYZ_c * 100, XYZ_w, XYZ_wr, L_A1, L_A2, direction="Inverse" + ) + / 100 +) diff --git a/colour/examples/adaptation/examples_fairchild1990.py b/colour/examples/adaptation/examples_fairchild1990.py index 55dbc077c8..ad3a05adae 100644 --- a/colour/examples/adaptation/examples_fairchild1990.py +++ b/colour/examples/adaptation/examples_fairchild1990.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *Fairchild (1990)* chromatic adaptation model computations. -""" +"""Showcases *Fairchild (1990)* chromatic adaptation model computations.""" import numpy as np @@ -14,15 +11,22 @@ XYZ_n = np.array([1.1115, 1.0000, 0.3520]) XYZ_r = np.array([0.9481, 1.0000, 1.0730]) Y_n = 200 -message_box(('Computing chromatic adaptation using "Fairchild (1990)" ' - 'chromatic adaptation model.\n' - '\n\t"XYZ_1":\n\t\t{0}\n\t"XYZ_n":\n\t\t{1}\n\t"XYZ_r":\n\t\t{2}' - '\n\t"Y_n":\n\t\t{3}\n\n' - 'Warning: The input domain and output range of that definition ' - 'are non standard!'.format(XYZ_1, XYZ_n, XYZ_r, Y_n))) +message_box( + f'Computing chromatic adaptation using "Fairchild (1990)" chromatic ' + f"adaptation model.\n\n" + f'\t"XYZ_1": {XYZ_1}\n' + f'\t"XYZ_n": {XYZ_n}\n' + f'\t"XYZ_r": {XYZ_r}\n' + f'\t"Y_n": {Y_n}' +) print( colour.chromatic_adaptation( - XYZ_1, XYZ_n, XYZ_r, method='Fairchild 1990', Y_n=Y_n)) + XYZ_1, XYZ_n, XYZ_r, method="Fairchild 1990", Y_n=Y_n + ) +) print( colour.adaptation.chromatic_adaptation_Fairchild1990( - XYZ_1 * 100.0, XYZ_n, XYZ_r, Y_n) / 100.0) + XYZ_1 * 100, XYZ_n, XYZ_r, Y_n + ) + / 100 +) diff --git a/colour/examples/adaptation/examples_vonkries.py b/colour/examples/adaptation/examples_vonkries.py index 9fccd28ae1..5986102e67 100644 --- a/colour/examples/adaptation/examples_vonkries.py +++ b/colour/examples/adaptation/examples_vonkries.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *Von Kries* chromatic adaptation model computations. -""" +"""Showcases *Von Kries* chromatic adaptation model computations.""" import numpy as np @@ -12,40 +9,54 @@ XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775]) XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460]) -message_box(('Computing the chromatic adaptation matrix from two source ' - '"CIE XYZ" tristimulus values arrays, default CAT is "CAT02".\n' - '\n\t"XYZ_w":\n\t\t{0}\n\t"XYZ_wr":\n\t\t{1}'.format( - XYZ_w, XYZ_wr))) +message_box( + f'Computing the chromatic adaptation matrix from two source "CIE XYZ" ' + f'tristimulus values arrays, default CAT is "CAT02".\n\n' + f'\t"XYZ_w": {XYZ_w}\n' + f'\t"XYZ_wr": {XYZ_wr}' +) print(colour.adaptation.matrix_chromatic_adaptation_VonKries(XYZ_w, XYZ_wr)) -print('\n') +print("\n") message_box('Using "Bradford" CAT.') print( colour.adaptation.matrix_chromatic_adaptation_VonKries( - XYZ_w, XYZ_wr, transform='Bradford')) - -print('\n') - -message_box(('Computing the chromatic adaptation matrix from ' - '"CIE Standard Illuminant A" to ' - '"CIE Standard Illuminant D Series D65" using "Von Kries" CAT.')) -A = colour.CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['A'] -D65 = colour.CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65'] + XYZ_w, XYZ_wr, transform="Bradford" + ) +) + +print("\n") + +message_box( + "Computing the chromatic adaptation matrix from " + 'the "CIE Standard Illuminant A" to ' + 'the "CIE Standard Illuminant D Series D65" using the "Von Kries" CAT.' +) +A = colour.CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["A"] +D65 = colour.CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"] print( colour.adaptation.matrix_chromatic_adaptation_VonKries( - colour.xy_to_XYZ(A), colour.xy_to_XYZ(D65), transform='Von Kries')) + colour.xy_to_XYZ(A), colour.xy_to_XYZ(D65), transform="Von Kries" + ) +) -print('\n') +print("\n") XYZ = np.array([1.14176346, 1.00000000, 0.49815206]) -message_box(('Adapting given "CIE XYZ" tristimulus values from ' - '"CIE Standard Illuminant A" to ' - '"CIE Standard Illuminant D Series D65" using "Sharp" CAT.\n' - '\n\t"XYZ":\n\t\t{0}'.format(XYZ))) +message_box( + f'Adapting given "CIE XYZ" tristimulus values from ' + f'the "CIE Standard Illuminant A" to the ' + f'"CIE Standard Illuminant D Series D65" using the "Sharp" CAT.\n\n' + f'\t"XYZ": {XYZ}' +) print( colour.chromatic_adaptation( - XYZ, colour.xy_to_XYZ(A), colour.xy_to_XYZ(D65), transform='Sharp')) + XYZ, colour.xy_to_XYZ(A), colour.xy_to_XYZ(D65), transform="Sharp" + ) +) print( colour.adaptation.chromatic_adaptation_VonKries( - XYZ, colour.xy_to_XYZ(A), colour.xy_to_XYZ(D65), transform='Sharp')) + XYZ, colour.xy_to_XYZ(A), colour.xy_to_XYZ(D65), transform="Sharp" + ) +) diff --git a/colour/examples/adaptation/examples_zhai2018.py b/colour/examples/adaptation/examples_zhai2018.py new file mode 100644 index 0000000000..39cd18e377 --- /dev/null +++ b/colour/examples/adaptation/examples_zhai2018.py @@ -0,0 +1,42 @@ +"""Showcases *Zhai and Luo (2018)* chromatic adaptation model computations.""" + +import numpy as np + +import colour +from colour.utilities import message_box + +message_box('"Zhai and Luo (2018)" Chromatic Adaptation Model Computations') + +XYZ_b = np.array([0.48900, 0.43620, 0.06250]) +XYZ_wb = np.array([1.09850, 1, 0.35585]) +XYZ_wd = np.array([0.95047, 1, 1.08883]) +D_b = 0.9407 +D_d = 0.9800 +XYZ_wo = np.array([1, 1, 1]) +message_box( + f'Computing chromatic adaptation using "Zhai and Luo (2018)" chromatic ' + f"adaptation model.\n\n" + f'\t"XYZ_b": {XYZ_b}\n' + f'\t"XYZ_wb": {XYZ_wb}\n' + f'\t"XYZ_wd": {XYZ_wd}\n' + f'\t"D_b": {D_b}\n' + f'\t"D_d": {D_d}\n' + f'\t"XYZ_wo": {XYZ_wo}' +) +print( + colour.chromatic_adaptation( + XYZ_b, + XYZ_wb, + XYZ_wd, + method="Zhai 2018", + D_b=D_b, + D_d=D_d, + XYZ_wo=XYZ_wo, + ) +) +print( + colour.adaptation.chromatic_adaptation_Zhai2018( + XYZ_b * 100, XYZ_wb * 100, XYZ_wd * 100, D_b, D_d, XYZ_wo * 100 + ) + / 100 +) diff --git a/colour/examples/algebra/examples_interpolation.py b/colour/examples/algebra/examples_interpolation.py index 9f289180aa..ddc584175e 100644 --- a/colour/examples/algebra/examples_interpolation.py +++ b/colour/examples/algebra/examples_interpolation.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases interpolation computations. -""" +"""Showcases interpolation computations.""" import matplotlib.pyplot as plt import numpy as np @@ -11,10 +8,12 @@ from colour.plotting import render from colour.utilities import message_box -message_box('Interpolation Computations') +message_box("Interpolation Computations") -message_box(('Comparing "Sprague (1880)" and "Cubic Spline" recommended ' - 'interpolation methods to "Pchip" method.')) +message_box( + 'Comparing the "Sprague (1880)" and "Cubic Spline" recommended ' + 'interpolation methods to the "Pchip" method.' +) data_uniform = { 340: 0.0000, @@ -41,7 +40,7 @@ 760: 0.0000, 780: 0.0000, 800: 0.0000, - 820: 0.0000 + 820: 0.0000, } data_non_uniform = { @@ -69,46 +68,67 @@ 760: 0.0000, 780: 0.0000, 800: 0.0000, - 820.9: 0.0000 + 820.9: 0.0000, } -sd_base = colour.SpectralDistribution(data_uniform, name='Reference') +sd_base = colour.SpectralDistribution(data_uniform, name="Reference") uniform_interpolated_sd = colour.SpectralDistribution( - data_uniform, name='Uniform - Sprague Interpolation') + data_uniform, name="Uniform - Sprague Interpolation" +) uniform_pchip_interpolated_sd = colour.SpectralDistribution( - data_uniform, name='Uniform - Pchip Interpolation') + data_uniform, name="Uniform - Pchip Interpolation" +) non_uniform_interpolated_sd = colour.SpectralDistribution( - data_non_uniform, name='Non Uniform - Cubic Spline Interpolation') - -uniform_interpolated_sd.interpolate(colour.SpectralShape(interval=1)) + data_non_uniform, name="Non Uniform - Cubic Spline Interpolation" +) + +uniform_interpolated_sd.interpolate( + colour.SpectralShape( + uniform_interpolated_sd.shape.start, + uniform_interpolated_sd.shape.end, + 1, + ) +) uniform_pchip_interpolated_sd.interpolate( - colour.SpectralShape(interval=1), interpolator=colour.PchipInterpolator) -non_uniform_interpolated_sd.interpolate(colour.SpectralShape(interval=1)) + colour.SpectralShape( + uniform_pchip_interpolated_sd.shape.start, + uniform_pchip_interpolated_sd.shape.end, + 1, + ), + interpolator=colour.PchipInterpolator, +) +non_uniform_interpolated_sd.interpolate( + colour.SpectralShape( + non_uniform_interpolated_sd.shape.start, + non_uniform_interpolated_sd.shape.end, + 1, + ) +) shape = sd_base.shape x_limit_min, x_limit_max, y_limit_min, y_limit_max = [], [], [], [] plt.plot( - sd_base.wavelengths, - sd_base.values, - 'ro-', - label=sd_base.name, - linewidth=1) + sd_base.wavelengths, sd_base.values, "ro-", label=sd_base.name, linewidth=1 +) plt.plot( uniform_interpolated_sd.wavelengths, uniform_interpolated_sd.values, label=uniform_interpolated_sd.name, - linewidth=1) + linewidth=1, +) plt.plot( uniform_pchip_interpolated_sd.wavelengths, uniform_pchip_interpolated_sd.values, label=uniform_pchip_interpolated_sd.name, - linewidth=1) + linewidth=1, +) plt.plot( non_uniform_interpolated_sd.wavelengths, non_uniform_interpolated_sd.values, label=non_uniform_interpolated_sd.name, - linewidth=1) + linewidth=1, +) x_limit_min.append(shape.start) x_limit_max.append(shape.end) @@ -116,41 +136,51 @@ y_limit_max.append(max(sd_base.values)) settings = { - 'x_label': - 'Wavelength $\\lambda$ (nm)', - 'y_label': - 'Spectral Distribution', - 'legend': - True, - 'legend_location': - 'upper left', - 'x_ticker': - True, - 'y_ticker': - True, - 'bounding_box': (min(x_limit_min), max(x_limit_max), min(y_limit_min), - max(y_limit_max)) + "x_label": "Wavelength $\\lambda$ (nm)", + "y_label": "Spectral Distribution", + "legend": True, + "legend_location": "upper left", + "x_ticker": True, + "y_ticker": True, + "bounding_box": ( + min(x_limit_min), + max(x_limit_max), + min(y_limit_min), + max(y_limit_max), + ), } render(**settings) -print('\n') +print("\n") V_xyz = np.random.random((6, 3)) -message_box(('Performing "trilinear" interpolation of given "xyz" values:\n' - '\n{0}\n' - '\nusing given interpolation table.'.format(V_xyz))) +message_box( + f'Performing "trilinear" interpolation of given "xyz" values:\n\n' + f"{V_xyz}\n\n" + f"using given interpolation table." +) path = os.path.join( - os.path.dirname(__file__), '..', '..', 'io', 'luts', 'tests', 'resources', - 'iridas_cube', 'Colour_Correct.cube') + os.path.dirname(__file__), + "..", + "..", + "io", + "luts", + "tests", + "resources", + "iridas_cube", + "Colour_Correct.cube", +) table = colour.read_LUT(path).table -print(colour.table_interpolation(V_xyz, table, method='Trilinear')) +print(colour.table_interpolation(V_xyz, table, method="Trilinear")) print(colour.algebra.table_interpolation_trilinear(V_xyz, table)) -print('\n') +print("\n") -message_box(('Performing "tetrahedral" interpolation of given "xyz" values:\n' - '\n{0}\n' - '\nusing given interpolation table.'.format(V_xyz))) -print(colour.table_interpolation(V_xyz, table, method='Tetrahedral')) +message_box( + f'Performing "tetrahedral" interpolation of given "xyz" values:\n\n' + f"{V_xyz}\n\n" + f"using given interpolation table." +) +print(colour.table_interpolation(V_xyz, table, method="Tetrahedral")) print(colour.algebra.table_interpolation_tetrahedral(V_xyz, table)) diff --git a/colour/examples/appearance/examples_atd95.py b/colour/examples/appearance/examples_atd95.py index 675a254109..8dd8f928bc 100644 --- a/colour/examples/appearance/examples_atd95.py +++ b/colour/examples/appearance/examples_atd95.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *ATD (1995)* colour appearance model computations. -""" +"""Showcases *ATD (1995)* colour appearance model computations.""" import numpy as np @@ -16,25 +13,28 @@ Y_0 = 318.31 k_1 = 0.0 k_2 = 50.0 -surround = colour.VIEWING_CONDITIONS_CIECAM02['Average'] message_box( - ('Converting to "ATD (1995)" colour appearance model ' - 'specification using given parameters:\n' - '\n\tXYZ: {0}\n\tXYZ_0: {1}\n\tY_0: {2}\n\tk_1: {3}' - '\n\tk_2: {4}\n\n' - 'Warning: The input domain of that definition is non standard!'.format( - XYZ, XYZ_0, Y_0, k_1, k_2))) + f'Converting to the "ATD (1995)" colour appearance model specification ' + f"using given parameters:\n\n" + f"\tXYZ: {XYZ}\n" + f"\tXYZ_0: {XYZ_0}\n" + f"\tY_0: {Y_0}\n" + f"\tk_1: {k_1}\n" + f"\tk_2: {k_2}" +) specification = colour.XYZ_to_ATD95(XYZ, XYZ_0, Y_0, k_1, k_2) print(specification) -print('\n') +print("\n") -message_box(('Broadcasting current output "ATD (1995)" colour appearance ' - 'model specification to reference specification.\n' - 'The intent of this reference specification is to provide names ' - 'as closest as possible to "Mark D. Fairchild" reference.\n' - 'The current output specification is meant to be consistent with ' - 'the other colour appearance model specification by using same ' - 'argument names for consistency wherever possible.')) +message_box( + 'Broadcasting the current output "ATD (1995)" colour appearance ' + "model specification to the reference specification.\n" + "The intent of this reference specification is to provide names " + 'as closest as possible to the "Mark D. Fairchild" reference.\n' + "The current output specification is meant to be consistent with " + "the other colour appearance model specification by using same " + "argument names for consistency wherever possible." +) -print(CAM_ReferenceSpecification_ATD95(*specification)) +print(CAM_ReferenceSpecification_ATD95(*specification.values)) diff --git a/colour/examples/appearance/examples_cam16.py b/colour/examples/appearance/examples_cam16.py index 627ed239f1..b57fa75579 100644 --- a/colour/examples/appearance/examples_cam16.py +++ b/colour/examples/appearance/examples_cam16.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *CAM16* colour appearance model computations. -""" +"""Showcases *CAM16* colour appearance model computations.""" import numpy as np @@ -14,28 +11,33 @@ XYZ_w = np.array([95.05, 100.00, 108.88]) L_A = 318.31 Y_b = 20.0 -surround = colour.VIEWING_CONDITIONS_CAM16['Average'] +surround = colour.VIEWING_CONDITIONS_CAM16["Average"] message_box( - ('Converting to "CAM16" colour appearance model specification ' - 'using given parameters:\n' - '\n\tXYZ: {0}\n\tXYZ_w: {1}\n\tL_A: {2}\n\tY_b: {3}' - '\n\tSurround: {4}\n\n' - 'Warning: The input domain of that definition is non standard!').format( - XYZ, XYZ_w, L_A, Y_b, surround)) + f'Converting to the "CAM16" colour appearance model specification ' + f"using given parameters:\n\n" + f"\tXYZ: {XYZ}\n" + f"\tXYZ_w: {XYZ_w}\n" + f"\tL_A: {L_A}\n" + f"\tY_b: {Y_b}\n" + f"\tSurround: {surround}" +) specification = colour.XYZ_to_CAM16(XYZ, XYZ_w, L_A, Y_b, surround) print(specification) -print('\n') +print("\n") J = 41.73120791 C = 0.10335574 h = 217.06795977 specification = colour.CAM_Specification_CAM16(J, C, h) message_box( - ('Converting to "CIE XYZ" tristimulus values using given ' - 'parameters:\n' - '\n\tJ: {0}\n\tC: {1}\n\th: {2}\n\tXYZ_w: {3}\n\tL_A: {4}' - '\n\tY_b: {5}\n\n' - 'Warning: The output range of that definition is non standard!').format( - J, C, h, XYZ_w, L_A, Y_b)) -print(colour.CAM16_to_XYZ(specification, XYZ_w, L_A, Y_b)) + f'Converting to "CIE XYZ" tristimulus values using given parameters:\n\n' + f"\tJ: {J}\n" + f"\tC: {C}\n" + f"\th: {h}\n" + f"\tXYZ_w: {XYZ_w}\n" + f"\tL_A: {L_A}\n" + f"\tY_b: {Y_b}\n" + f"\tSurround: {surround}" +) +print(colour.CAM16_to_XYZ(specification, XYZ_w, L_A, Y_b, surround)) diff --git a/colour/examples/appearance/examples_ciecam02.py b/colour/examples/appearance/examples_ciecam02.py index dc9946123f..3ef1c48586 100644 --- a/colour/examples/appearance/examples_ciecam02.py +++ b/colour/examples/appearance/examples_ciecam02.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *CIECAM02* colour appearance model computations. -""" +"""Showcases *CIECAM02* colour appearance model computations.""" import numpy as np @@ -14,28 +11,33 @@ XYZ_w = np.array([95.05, 100.00, 108.88]) L_A = 318.31 Y_b = 20.0 -surround = colour.VIEWING_CONDITIONS_CIECAM02['Average'] +surround = colour.VIEWING_CONDITIONS_CIECAM02["Average"] message_box( - ('Converting to "CIECAM02" colour appearance model specification ' - 'using given parameters:\n' - '\n\tXYZ: {0}\n\tXYZ_w: {1}\n\tL_A: {2}\n\tY_b: {3}' - '\n\tSurround: {4}\n\n' - 'Warning: The input domain of that definition is non standard!').format( - XYZ, XYZ_w, L_A, Y_b, surround)) + f'Converting to the "CIECAM02" colour appearance model specification ' + f"using given parameters:\n\n" + f"\tXYZ: {XYZ}\n" + f"\tXYZ_w: {XYZ_w}\n" + f"\tL_A: {L_A}\n" + f"\tY_b: {Y_b}\n" + f"\tSurround: {surround}" +) specification = colour.XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround) print(specification) -print('\n') +print("\n") J = 41.73109113 C = 0.10470776 h = 219.04843266 specification = colour.CAM_Specification_CIECAM02(J, C, h) message_box( - ('Converting to "CIE XYZ" tristimulus values using given ' - 'parameters:\n' - '\n\tJ: {0}\n\tC: {1}\n\th: {2}\n\tXYZ_w: {3}\n\tL_A: {4}' - '\n\tY_b: {5}\n\n' - 'Warning: The output range of that definition is non standard!').format( - J, C, h, XYZ_w, L_A, Y_b)) -print(colour.CIECAM02_to_XYZ(specification, XYZ_w, L_A, Y_b)) + f'Converting to "CIE XYZ" tristimulus values using given parameters:\n\n' + f"\tJ: {J}\n" + f"\tC: {C}\n" + f"\th: {h}\n" + f"\tXYZ_w: {XYZ_w}\n" + f"\tL_A: {L_A}\n" + f"\tY_b: {Y_b}\n" + f"\tSurround: {surround}" +) +print(colour.CIECAM02_to_XYZ(specification, XYZ_w, L_A, Y_b, surround)) diff --git a/colour/examples/appearance/examples_hke.py b/colour/examples/appearance/examples_hke.py new file mode 100644 index 0000000000..392c377a78 --- /dev/null +++ b/colour/examples/appearance/examples_hke.py @@ -0,0 +1,70 @@ +"""Showcases Helmholtz—Kohlrausch effect estimation computations.""" + +import colour +from colour.plotting import colour_style, plot_multi_colour_swatches +from colour.utilities import message_box + +wp = colour.xy_to_Luv_uv([0.31271, 0.32902]) + +average_luminance = 0.14 + +swatches = [ + [0.45079660, 0.52288689], + [0.19124902, 0.55444488], + [0.13128455, 0.51210591], + [0.14889223, 0.37091478], + [0.28992574, 0.30964533], +] +swatches_XYZ = [] +for patch in swatches: + in_XYZ = colour.Luv_to_XYZ(colour.uv_to_Luv(patch)) + swatches_XYZ.append(in_XYZ * (average_luminance / in_XYZ[1])) + +# Adapting Luminance, 250 cd/m^2 represents a typical modern computer +# display peak luminance. +L_a = 250 * average_luminance + +bg_grey = colour.xy_to_XYZ(colour.Luv_uv_to_xy(wp)) * average_luminance + +swatches_normal = [] +swatches_VCC = [] +swatches_VAC = [] + +for i in range(len(swatches)): + VCC = colour.HelmholtzKohlrausch_effect_luminous_Nayatani1997( + swatches[i], wp, L_a, method="VCC" + ) + VAC = colour.HelmholtzKohlrausch_effect_luminous_Nayatani1997( + swatches[i], wp, L_a, method="VAC" + ) + + swatches_normal.append(colour.XYZ_to_sRGB(bg_grey)) + swatches_normal.append(colour.XYZ_to_sRGB(swatches_XYZ[i])) + + swatches_VCC.append(colour.XYZ_to_sRGB(bg_grey)) + swatches_VCC.append(colour.XYZ_to_sRGB(swatches_XYZ[i] / VCC)) + + swatches_VAC.append(colour.XYZ_to_sRGB(bg_grey * VAC)) + swatches_VAC.append(colour.XYZ_to_sRGB(swatches_XYZ[i])) + +colour_style() + +message_box( + "Plotting swatches with the same luminance (Y).\n" + "The Helmholtz—Kohlrausch effect will be very noticeable." +) +plot_multi_colour_swatches(swatches_normal, compare_swatches="stacked") + +message_box( + "Plotting HKE-compensated swatches with VCC method.\n" + "The Helmholtz—Kohlrausch effect has been compensated using VCC" + "(variable chromatic colour) method." +) +plot_multi_colour_swatches(swatches_VCC, compare_swatches="stacked") + +message_box( + "Plotting HKE-compensated swatches with VAC method.\n" + "The Helmholtz—Kohlrausch effect has been compensated for using VAC" + "(variable achromatic colour) method." +) +plot_multi_colour_swatches(swatches_VAC, compare_swatches="stacked") diff --git a/colour/examples/appearance/examples_hunt.py b/colour/examples/appearance/examples_hunt.py index ef5f32d20b..da59e4baac 100644 --- a/colour/examples/appearance/examples_hunt.py +++ b/colour/examples/appearance/examples_hunt.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *Hunt* colour appearance model computations. -""" +"""Showcases *Hunt* colour appearance model computations.""" import numpy as np @@ -15,28 +12,34 @@ XYZ_w = np.array([95.05, 100.00, 108.88]) XYZ_b = np.array([95.05, 100.00, 108.88]) L_A = 318.31 -surround = colour.VIEWING_CONDITIONS_HUNT['Normal Scenes'] +surround = colour.VIEWING_CONDITIONS_HUNT["Normal Scenes"] CCT_w = 6504.0 message_box( - ('Converting to "Hunt" colour appearance model ' - 'specification using given parameters:\n' - '\n\tXYZ: {0}\n\tXYZ_w: {1}\n\tXYZ_b: {2}\n\tL_A: {3}' - '\n\tsurround: {4}\n\tCCT_w: {5}\n\n' - 'Warning: The input domain of that definition is non standard!'.format( - XYZ, XYZ_w, XYZ_b, L_A, surround, CCT_w))) + f'Converting to the "Hunt" colour appearance model specification using ' + f"given parameters:\n\n" + f"\tXYZ: {XYZ}\n" + f"\tXYZ_w: {XYZ_w}\n" + f"\tXYZ_b: {XYZ_b}\n" + f"\tL_A: {L_A}\n" + f"\tsurround: {surround}\n" + f"\tCCT_w: {CCT_w}" +) specification = colour.XYZ_to_Hunt( - XYZ, XYZ_w, XYZ_b, L_A, surround, CCT_w=CCT_w) + XYZ, XYZ_w, XYZ_b, L_A, surround, CCT_w=CCT_w +) print(specification) -print('\n') +print("\n") -message_box(('Broadcasting current output "Hunt" colour appearance ' - 'model specification to reference specification.\n' - 'The intent of this reference specification is to provide names ' - 'as closest as possible to "Mark D. Fairchild" reference.\n' - 'The current output specification is meant to be consistent with ' - 'the other colour appearance model specification by using same ' - 'argument names for consistency wherever possible.')) - -print(CAM_ReferenceSpecification_Hunt(*specification)) +message_box( + 'Broadcasting the current output "Hunt" colour appearance ' + "model specification to the reference specification.\n" + "The intent of this reference specification is to provide names " + 'as closest as possible to the "Mark D. Fairchild" reference.\n' + "The current output specification is meant to be consistent with " + "the other colour appearance model specification by using same " + "argument names for consistency wherever possible." +) + +print(CAM_ReferenceSpecification_Hunt(*specification.values)) diff --git a/colour/examples/appearance/examples_kim2009.py b/colour/examples/appearance/examples_kim2009.py new file mode 100644 index 0000000000..e52bea3217 --- /dev/null +++ b/colour/examples/appearance/examples_kim2009.py @@ -0,0 +1,45 @@ +"""Showcases *Kim, Weyrich and Kautz (2009)* colour appearance model computations.""" + +import numpy as np + +import colour +from colour.utilities import message_box + +message_box( + '"Kim, Weyrich and Kautz (2009)" Colour Appearance Model Computations' +) + +XYZ = np.array([19.01, 20.00, 21.78]) +XYZ_w = np.array([95.05, 100.00, 108.88]) +L_A = 318.31 +media = colour.MEDIA_PARAMETERS_KIM2009["CRT Displays"] +surround = colour.VIEWING_CONDITIONS_KIM2009["Average"] +message_box( + f'Converting to the "Kim, Weyrich and Kautz (2009)" colour appearance ' + f"model specification using given parameters:\n\n" + f"\tXYZ: {XYZ}\n" + f"\tXYZ_w: {XYZ_w}\n" + f"\tL_A: {L_A}\n" + f"\tMedia: {media}\n" + f"\tSurround: {surround}" +) +specification = colour.XYZ_to_Kim2009(XYZ, XYZ_w, L_A, media, surround) +print(specification) + +print("\n") + +J = 28.861908975839647 +C = 0.559245592437371 +h = 219.048066776629530 +specification = colour.CAM_Specification_Kim2009(J, C, h) +message_box( + f'Converting to "CIE XYZ" tristimulus values using given parameters:\n\n' + f"\tJ: {J}\n" + f"\tC: {C}\n" + f"\th: {h}\n" + f"\tXYZ_w: {XYZ_w}\n" + f"\tL_A: {L_A}\n" + f"\tMedia: {media}\n" + f"\tSurround: {surround}" +) +print(colour.Kim2009_to_XYZ(specification, XYZ_w, L_A, media, surround)) diff --git a/colour/examples/appearance/examples_llab.py b/colour/examples/appearance/examples_llab.py index 403ebde66a..3080111eff 100644 --- a/colour/examples/appearance/examples_llab.py +++ b/colour/examples/appearance/examples_llab.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *LLAB(l:c)* colour appearance model computations. -""" +"""Showcases *LLAB(l:c)* colour appearance model computations.""" import numpy as np @@ -15,25 +12,29 @@ XYZ_0 = np.array([95.05, 100.00, 108.88]) Y_b = 20.0 L = 318.31 -surround = colour.VIEWING_CONDITIONS_LLAB['ref_average_4_minus'] +surround = colour.VIEWING_CONDITIONS_LLAB["ref_average_4_minus"] message_box( - ('Converting to "LLAB(l:c)" colour appearance model ' - 'specification using given parameters:\n' - '\n\tXYZ: {0}\n\tXYZ_0: {1}\n\tY_b: {2}\n\tL: {3}' - '\n\tsurround: {4}\n\n' - 'Warning: The input domain of that definition is non standard!'.format( - XYZ, XYZ_0, Y_b, L, surround))) + f'Converting to the "LLAB(l:c)" colour appearance model specification ' + f"using given parameters:\n\n" + f"\tXYZ: {XYZ}\n" + f"\tXYZ_0: {XYZ_0}\n" + f"\tY_b: {Y_b}\n" + f"\tL: {L}\n" + f"\tsurround: {surround}" +) specification = colour.XYZ_to_LLAB(XYZ, XYZ_0, Y_b, L, surround) print(specification) -print('\n') +print("\n") -message_box(('Broadcasting current output "LLAB(l:c)" colour appearance ' - 'model specification to reference specification.\n' - 'The intent of this reference specification is to provide names ' - 'as closest as possible to "Mark D. Fairchild" reference.\n' - 'The current output specification is meant to be consistent with ' - 'the other colour appearance model specification by using same ' - 'argument names for consistency wherever possible.')) +message_box( + 'Broadcasting the current output "LLAB(l:c)" colour appearance ' + "model specification to the reference specification.\n" + "The intent of this reference specification is to provide names " + 'as closest as possible to the "Mark D. Fairchild" reference.\n' + "The current output specification is meant to be consistent with " + "the other colour appearance model specification by using same " + "argument names for consistency wherever possible." +) -print(CAM_ReferenceSpecification_LLAB(*specification)) +print(CAM_ReferenceSpecification_LLAB(*specification.values)) diff --git a/colour/examples/appearance/examples_nayatani95.py b/colour/examples/appearance/examples_nayatani95.py index 541da563df..1ec159478d 100644 --- a/colour/examples/appearance/examples_nayatani95.py +++ b/colour/examples/appearance/examples_nayatani95.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *Nayatani (1995)* colour appearance model computations. -""" +"""Showcases *Nayatani (1995)* colour appearance model computations.""" import numpy as np @@ -17,23 +14,27 @@ E_o = 5000.0 E_or = 1000.0 message_box( - ('Converting to "Nayatani (1995)" colour appearance model ' - 'specification using given parameters:\n' - '\n\tXYZ: {0}\n\tXYZ_n: {1}\n\tY_o: {2}\n\tE_o: {3}' - '\n\tE_or: {4}\n\n' - 'Warning: The input domain of that definition is non standard!'.format( - XYZ, XYZ_n, Y_o, E_o, E_or))) + f'Converting to the "Nayatani (1995)" colour appearance model ' + f"specification using given parameters:\n\n" + f"\tXYZ: {XYZ}\n" + f"\tXYZ_n: {XYZ_n}\n" + f"\tY_o: {Y_o}\n" + f"\tE_o: {E_o}\n" + f"\tE_or: {E_or}" +) specification = colour.XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or) print(specification) -print('\n') +print("\n") -message_box(('Broadcasting current output "Nayatani (1995)" colour appearance ' - 'model specification to reference specification.\n' - 'The intent of this reference specification is to provide names ' - 'as closest as possible to "Mark D. Fairchild" reference.\n' - 'The current output specification is meant to be consistent with ' - 'the other colour appearance model specification by using same ' - 'argument names for consistency wherever possible.')) +message_box( + 'Broadcasting the current output "Nayatani (1995)" colour appearance ' + "model specification to the reference specification.\n" + "The intent of this reference specification is to provide names " + 'as closest as possible to the "Mark D. Fairchild" reference.\n' + "The current output specification is meant to be consistent with " + "the other colour appearance model specification by using same " + "argument names for consistency wherever possible." +) -print(CAM_ReferenceSpecification_Nayatani95(*specification)) +print(CAM_ReferenceSpecification_Nayatani95(*specification.values)) diff --git a/colour/examples/appearance/examples_rlab.py b/colour/examples/appearance/examples_rlab.py index 2e1d8e8ff1..5571b54e97 100644 --- a/colour/examples/appearance/examples_rlab.py +++ b/colour/examples/appearance/examples_rlab.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *RLAB* colour appearance model computations. -""" +"""Showcases *RLAB* colour appearance model computations.""" import numpy as np @@ -14,26 +11,30 @@ XYZ = np.array([19.01, 20.00, 21.78]) XYZ_n = np.array([109.85, 100, 35.58]) Y_n = 31.83 -sigma = colour.VIEWING_CONDITIONS_RLAB['Average'] -D = colour.appearance.D_FACTOR_RLAB['Hard Copy Images'] +sigma = colour.VIEWING_CONDITIONS_RLAB["Average"] +D = colour.appearance.D_FACTOR_RLAB["Hard Copy Images"] message_box( - ('Converting to "RLAB" colour appearance model ' - 'specification using given parameters:\n' - '\n\tXYZ: {0}\n\tXYZ_n: {1}\n\tY_n: {2}\n\tsigma: {3}' - '\n\tD: {4}\n\n' - 'Warning: The input domain of that definition is non standard!'.format( - XYZ, XYZ_n, Y_n, sigma, D))) + f'Converting to the "RLAB" colour appearance model specification using ' + f"given parameters:\n\n" + f"\tXYZ: {XYZ}\n" + f"\tXYZ_n: {XYZ_n}\n" + f"\tY_n: {Y_n}\n" + f"\tsigma: {sigma}\n" + f"\tD: {D}" +) specification = colour.XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma, D) print(specification) -print('\n') +print("\n") -message_box(('Broadcasting current output "RLAB" colour appearance ' - 'model specification to reference specification.\n' - 'The intent of this reference specification is to provide names ' - 'as closest as possible to "Mark D. Fairchild" reference.\n' - 'The current output specification is meant to be consistent with ' - 'the other colour appearance model specification by using same ' - 'argument names for consistency wherever possible.')) +message_box( + 'Broadcasting the current output "RLAB" colour appearance ' + "model specification to the reference specification.\n" + "The intent of this reference specification is to provide names " + 'as closest as possible to the "Mark D. Fairchild" reference.\n' + "The current output specification is meant to be consistent with " + "the other colour appearance model specification by using same " + "argument names for consistency wherever possible." +) -print(CAM_ReferenceSpecification_RLAB(*specification)) +print(CAM_ReferenceSpecification_RLAB(*specification.values)) diff --git a/colour/examples/appearance/examples_zcam.py b/colour/examples/appearance/examples_zcam.py new file mode 100644 index 0000000000..27707f8193 --- /dev/null +++ b/colour/examples/appearance/examples_zcam.py @@ -0,0 +1,54 @@ +"""Showcases *ZCAM* colour appearance model computations.""" + +import numpy as np + +import colour +from colour.appearance.zcam import CAM_ReferenceSpecification_ZCAM +from colour.utilities import message_box + +message_box('"ZCAM" Colour Appearance Model Computations') + +XYZ = np.array([19.01, 20.00, 21.78]) +XYZ_w = np.array([95.05, 100.00, 108.88]) +L_A = 318.31 +Y_b = 20.0 +surround = colour.VIEWING_CONDITIONS_ZCAM["Average"] +message_box( + f'Converting to the "ZCAM" colour appearance model specification using ' + f"given parameters:\n\n" + f"\tXYZ: {XYZ}\n" + f"\tXYZ_w: {XYZ_w}\n" + f"\tL_A: {L_A}\n" + f"\tY_b: {Y_b}\n" + f"\tSurround: {surround}" +) +specification = colour.XYZ_to_ZCAM(XYZ, XYZ_w, L_A, Y_b, surround) +print(specification) + +print("\n") + +message_box( + 'Broadcasting the current output "ZCAM" colour appearance model ' + "specification to the reference specification." +) + +print(CAM_ReferenceSpecification_ZCAM(*specification.values)) + + +print("\n") + +J = 48.095883049811491 +C = 0.18427174878137914 +h = 219.74741565783773 +specification = colour.CAM_Specification_ZCAM(J, C, h) +message_box( + f'Converting to "CIE XYZ" tristimulus values using given parameters:\n\n' + f"\tJ: {J}\n" + f"\tC: {C}\n" + f"\th: {h}\n" + f"\tXYZ_w: {XYZ_w}\n" + f"\tL_A: {L_A}\n" + f"\tY_b: {Y_b}\n" + f"\tSurround: {surround}" +) +print(colour.ZCAM_to_XYZ(specification, XYZ_w, L_A, Y_b, surround)) diff --git a/colour/examples/blindness/examples_machado2009.py b/colour/examples/blindness/examples_machado2009.py index 4b5530593d..05b4766588 100644 --- a/colour/examples/blindness/examples_machado2009.py +++ b/colour/examples/blindness/examples_machado2009.py @@ -1,69 +1,67 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Showcases Machado (2009) simulation of colour vision deficiency. -""" - -from __future__ import division, unicode_literals +"""Showcases Machado (2009) simulation of colour vision deficiency.""" import numpy as np import colour from colour.utilities.verbose import message_box -message_box('Simulation of CVD - Machado (2009)') +message_box("Simulation of CVD - Machado (2009)") M_a = colour.matrix_anomalous_trichromacy_Machado2009( - colour.MSDS_CMFS['Stockman & Sharpe 2 Degree Cone Fundamentals'], - colour.MSDS_DISPLAY_PRIMARIES['Typical CRT Brainard 1997'], - np.array([10, 0, 0])) -message_box(('Computing a "Protanomaly" matrix using ' - '"Stockman & Sharpe 2 Degree Cone Fundamentals" and ' - '"Typical CRT Brainard 1997" "RGB" display primaries for a 10nm ' - 'shift:\n\n' - '{0}'.format(M_a))) + colour.MSDS_CMFS["Stockman & Sharpe 2 Degree Cone Fundamentals"], + colour.MSDS_DISPLAY_PRIMARIES["Typical CRT Brainard 1997"], + np.array([10, 0, 0]), +) +message_box( + f'Computing a "Protanomaly" matrix using ' + f'"Stockman & Sharpe 2 Degree Cone Fundamentals" and ' + f'"Typical CRT Brainard 1997" "RGB" display primaries for a 10nm shift:\n\n{M_a}' +) -print('\n') +print("\n") -M_a = colour.matrix_cvd_Machado2009('Protanomaly', 0.5) -message_box(('Retrieving a "Protanomaly" pre-computed matrix for a 50% ' - 'severity:\n\n' - '{0}'.format(M_a))) +M_a = colour.matrix_cvd_Machado2009("Protanomaly", 0.5) +message_box( + f'Retrieving a "Protanomaly" pre-computed matrix for a 50% severity:\n\n{M_a}' +) -print('\n') +print("\n") M_a = colour.matrix_anomalous_trichromacy_Machado2009( - colour.MSDS_CMFS['Stockman & Sharpe 2 Degree Cone Fundamentals'], - colour.MSDS_DISPLAY_PRIMARIES['Typical CRT Brainard 1997'], - np.array([0, 10, 0])) -message_box(('Computing a "Deuteranomaly" matrix using ' - '"Stockman & Sharpe 2 Degree Cone Fundamentals" and ' - '"Typical CRT Brainard 1997" "RGB" display primaries for a 10nm ' - 'shift:\n\n' - '{0}'.format(M_a))) + colour.MSDS_CMFS["Stockman & Sharpe 2 Degree Cone Fundamentals"], + colour.MSDS_DISPLAY_PRIMARIES["Typical CRT Brainard 1997"], + np.array([0, 10, 0]), +) +message_box( + f'Computing a "Deuteranomaly" matrix using ' + f'"Stockman & Sharpe 2 Degree Cone Fundamentals" and ' + f'"Typical CRT Brainard 1997" "RGB" display primaries for a 10nm shift:\n\n{M_a}' +) -print('\n') +print("\n") -M_a = colour.matrix_cvd_Machado2009('Deuteranomaly', 0.5) -message_box(('Retrieving a "Deuteranomaly" pre-computed matrix for a 50% ' - 'severity:\n\n' - '{0}'.format(M_a))) +M_a = colour.matrix_cvd_Machado2009("Deuteranomaly", 0.5) +message_box( + f'Retrieving a "Deuteranomaly" pre-computed matrix for a 50% severity:\n\n{M_a}' +) -print('\n') +print("\n") M_a = colour.matrix_anomalous_trichromacy_Machado2009( - colour.MSDS_CMFS['Stockman & Sharpe 2 Degree Cone Fundamentals'], - colour.MSDS_DISPLAY_PRIMARIES['Typical CRT Brainard 1997'], - np.array([0, 0, 27])) -message_box(('Computing a "Tritanomaly" matrix using ' - '"Stockman & Sharpe 2 Degree Cone Fundamentals" and ' - '"Typical CRT Brainard 1997" "RGB" display primaries for a 27nm ' - 'shift:\n\n' - '{0}'.format(M_a))) + colour.MSDS_CMFS["Stockman & Sharpe 2 Degree Cone Fundamentals"], + colour.MSDS_DISPLAY_PRIMARIES["Typical CRT Brainard 1997"], + np.array([0, 0, 27]), +) +message_box( + f'Computing a "Tritanomaly" matrix using ' + f'"Stockman & Sharpe 2 Degree Cone Fundamentals" and ' + f'"Typical CRT Brainard 1997" "RGB" display primaries for a 27nm shift:\n\n{M_a}' +) -print('\n') +print("\n") -M_a = colour.matrix_cvd_Machado2009('Tritanomaly', 0.5) -message_box(('Retrieving a "Tritanomaly" pre-computed matrix for a 50% ' - 'severity:\n\n' - '{0}'.format(M_a))) +M_a = colour.matrix_cvd_Machado2009("Tritanomaly", 0.5) +message_box( + f'Retrieving a "Tritanomaly" pre-computed matrix for a 50% severity:\n\n{M_a}' +) diff --git a/colour/examples/characterisation/examples_aces_it.py b/colour/examples/characterisation/examples_aces_it.py index a031ffa2f8..c77a87611a 100644 --- a/colour/examples/characterisation/examples_aces_it.py +++ b/colour/examples/characterisation/examples_aces_it.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Showcases *Academy Color Encoding System* *Input Transform* related computations. @@ -11,41 +10,54 @@ message_box('"ACES" "Input Transform" Computations') -message_box(('Computing "ACES" relative exposure ' - 'values for some colour rendition chart spectral ' - 'distributions:\n' - '\n\t("dark skin", \n\t"blue sky")')) +message_box( + 'Computing "ACES" relative exposure values for some colour rendition ' + "chart spectral distributions:\n\n" + '\t("dark skin", "blue sky")' +) print( colour.sd_to_aces_relative_exposure_values( - colour.SDS_COLOURCHECKERS['ColorChecker N Ohta']['dark skin'])) + colour.SDS_COLOURCHECKERS["ColorChecker N Ohta"]["dark skin"] + ) +) print( colour.sd_to_aces_relative_exposure_values( - colour.SDS_COLOURCHECKERS['ColorChecker N Ohta']['blue sky'])) + colour.SDS_COLOURCHECKERS["ColorChecker N Ohta"]["blue sky"] + ) +) -print('\n') +print("\n") -message_box(('Computing "ACES" relative exposure values for various ideal ' - 'reflectors:\n' - '\n\t("18%", \n\t"100%")')) +message_box( + 'Computing "ACES" relative exposure values for various ideal ' + "reflectors:\n\n" + '\t("18%", "100%")' +) wavelengths = colour.characterisation.MSDS_ACES_RICD.wavelengths gray_reflector = colour.SpectralDistribution( - dict(zip(wavelengths, [0.18] * len(wavelengths))), name='18%') + dict(zip(wavelengths, [0.18] * len(wavelengths))), name="18%" +) print(repr(colour.sd_to_aces_relative_exposure_values(gray_reflector))) perfect_reflector = colour.SpectralDistribution( - dict(zip(wavelengths, [1.] * len(wavelengths))), name='100%') + dict(zip(wavelengths, [1.0] * len(wavelengths))), name="100%" +) print(colour.sd_to_aces_relative_exposure_values(perfect_reflector)) -print('\n') +print("\n") -message_box('Computing an "ACES" input device transform for a ' - '"CANON EOS 5DMark II" and and *CIE Illuminant D Series* *D55*:') +message_box( + 'Computing an "ACES" input device transform for a "CANON EOS 5DMark II" ' + "and and *CIE Illuminant D Series* *D55*:" +) path = os.path.join( colour.characterisation.aces_it.RESOURCES_DIRECTORY_RAWTOACES, - 'CANON_EOS_5DMark_II_RGB_Sensitivities.csv') + "CANON_EOS_5DMark_II_RGB_Sensitivities.csv", +) sensitivities = colour.colorimetry.sds_and_msds_to_msds( - colour.io.read_sds_from_csv_file(path).values()) -illuminant = colour.SDS_ILLUMINANTS['D55'] + colour.io.read_sds_from_csv_file(path).values() +) +illuminant = colour.SDS_ILLUMINANTS["D55"] print(colour.matrix_idt(sensitivities, illuminant)) diff --git a/colour/examples/characterisation/examples_colour_checkers.py b/colour/examples/characterisation/examples_colour_checkers.py index ef3640c1ee..961ad522b2 100644 --- a/colour/examples/characterisation/examples_colour_checkers.py +++ b/colour/examples/characterisation/examples_colour_checkers.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases colour rendition charts computations. -""" +"""Showcases colour rendition charts computations.""" import numpy as np from pprint import pprint @@ -9,36 +6,42 @@ import colour from colour.utilities import message_box -message_box('Colour Rendition Charts Computations') +message_box("Colour Rendition Charts Computations") -message_box('Colour rendition charts chromaticity coordinates dataset.') +message_box("Colour rendition charts chromaticity coordinates dataset.") pprint(sorted(colour.CCS_COLOURCHECKERS.keys())) -print('\n') +print("\n") -message_box('Colour rendition charts spectral distributions dataset.') +message_box("Colour rendition charts spectral distributions dataset.") pprint(colour.SDS_COLOURCHECKERS.keys()) -print('\n') +print("\n") -message_box(('"ColorChecker 2005" colour rendition chart chromaticity ' - 'coordinates data:\n' - '\n\t("Patch Number", "Patch Name", "xyY")')) -name, data, illuminant = colour.CCS_COLOURCHECKERS['ColorChecker 2005'] +message_box( + '"ColorChecker 2005" colour rendition chart chromaticity coordinates data:\n\n' + '\t("Patch Number", "Patch Name", "xyY")' +) +name, data, illuminant = colour.CCS_COLOURCHECKERS["ColorChecker 2005"] for name, xyY in data.items(): print(name, xyY) -print('\n') +print("\n") -message_box(('Converting "ColorChecker 2005" colour rendition chart "CIE xyY" ' - 'colourspace values to "sRGB" colourspace "RGB" values:\n' - '\n\t("Patch Name", ["R", "G", "B"])')) +message_box( + 'Converting the "ColorChecker 2005" colour rendition chart "CIE xyY" ' + 'colourspace values to "sRGB" colourspace "RGB" values:\n\n' + '\t("Patch Name", ["R", "G", "B"])' +) for name, xyY in data.items(): RGB = colour.XYZ_to_RGB( - colour.xyY_to_XYZ(xyY), illuminant, - colour.CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65'], - colour.RGB_COLOURSPACES['sRGB'].matrix_XYZ_to_RGB, 'Bradford', - colour.RGB_COLOURSPACES['sRGB'].cctf_encoding) + colour.xyY_to_XYZ(xyY), + illuminant, + colour.CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"], + colour.RGB_COLOURSPACES["sRGB"].matrix_XYZ_to_RGB, + "Bradford", + colour.RGB_COLOURSPACES["sRGB"].cctf_encoding, + ) RGB = [int(round(x * 255)) if x >= 0 else 0 for x in np.ravel(RGB)] - print('"{0}": {1}'.format(name, RGB)) + print(f'"{name}": {RGB}') diff --git a/colour/examples/characterisation/examples_correction.py b/colour/examples/characterisation/examples_correction.py index abb44886ac..50dfe440cc 100644 --- a/colour/examples/characterisation/examples_correction.py +++ b/colour/examples/characterisation/examples_correction.py @@ -1,199 +1,218 @@ -# -*- coding: utf-8 -*- -""" -Showcases colour correction computations. -""" +"""Showcases colour correction computations.""" import numpy as np import colour from colour.utilities import message_box -message_box('Colour Correction Computations') - -M_T = np.array([ - [0.17290600, 0.08205715, 0.05711951], - [0.56807350, 0.29250361, 0.21942000], - [0.10437166, 0.19656122, 0.32946697], - [0.10089156, 0.14839029, 0.05324779], - [0.22306044, 0.21697008, 0.43151034], - [0.10718115, 0.51351274, 0.41399101], - [0.74644090, 0.20020391, 0.03077999], - [0.05948985, 0.10659048, 0.39884205], - [0.56735781, 0.08485298, 0.11940265], - [0.11178198, 0.04285385, 0.14161263], - [0.34254479, 0.50627811, 0.05571580], - [0.79268226, 0.35803827, 0.02544159], - [0.01865226, 0.05139666, 0.28876921], - [0.05440562, 0.29876767, 0.07183236], - [0.45631278, 0.03075616, 0.04089930], - [0.85385852, 0.56503529, 0.01470045], - [0.53537579, 0.09006281, 0.30467248], - [-0.03661893, 0.24753827, 0.39810356], - [0.91186287, 0.91497635, 0.89391370], - [0.57979860, 0.59203200, 0.59346914], - [0.35499180, 0.36538033, 0.36757315], - [0.19011528, 0.19180135, 0.19309001], - [0.08525591, 0.08890588, 0.09252104], - [0.03039192, 0.03118624, 0.03278316], -]) - -M_R = np.array([ - [0.15579559, 0.09715755, 0.07514556], - [0.39113140, 0.25943419, 0.21266708], - [0.12824821, 0.18463570, 0.31508023], - [0.12028974, 0.13455659, 0.07408400], - [0.19368988, 0.21158946, 0.37955964], - [0.19957425, 0.36085439, 0.40678123], - [0.48896605, 0.20691688, 0.05816533], - [0.09775522, 0.16710693, 0.47147724], - [0.39358649, 0.12233400, 0.10526425], - [0.10780332, 0.07258529, 0.16151473], - [0.27502671, 0.34705454, 0.09728099], - [0.43980441, 0.26880559, 0.05430533], - [0.05887212, 0.11126272, 0.38552469], - [0.12705825, 0.25787860, 0.13566464], - [0.35612929, 0.07933258, 0.05118732], - [0.48131976, 0.42082843, 0.07120612], - [0.34665585, 0.15170714, 0.24969804], - [0.08261116, 0.24588716, 0.48707733], - [0.66054904, 0.65941137, 0.66376412], - [0.48051509, 0.47870296, 0.48230082], - [0.33045354, 0.32904184, 0.33228886], - [0.18001305, 0.17978567, 0.18004416], - [0.10283975, 0.10424680, 0.10384975], - [0.04742204, 0.04772203, 0.04914226], -]) +message_box("Colour Correction Computations") + +M_T = np.array( + [ + [0.17290600, 0.08205715, 0.05711951], + [0.56807350, 0.29250361, 0.21942000], + [0.10437166, 0.19656122, 0.32946697], + [0.10089156, 0.14839029, 0.05324779], + [0.22306044, 0.21697008, 0.43151034], + [0.10718115, 0.51351274, 0.41399101], + [0.74644090, 0.20020391, 0.03077999], + [0.05948985, 0.10659048, 0.39884205], + [0.56735781, 0.08485298, 0.11940265], + [0.11178198, 0.04285385, 0.14161263], + [0.34254479, 0.50627811, 0.05571580], + [0.79268226, 0.35803827, 0.02544159], + [0.01865226, 0.05139666, 0.28876921], + [0.05440562, 0.29876767, 0.07183236], + [0.45631278, 0.03075616, 0.04089930], + [0.85385852, 0.56503529, 0.01470045], + [0.53537579, 0.09006281, 0.30467248], + [-0.03661893, 0.24753827, 0.39810356], + [0.91186287, 0.91497635, 0.89391370], + [0.57979860, 0.59203200, 0.59346914], + [0.35499180, 0.36538033, 0.36757315], + [0.19011528, 0.19180135, 0.19309001], + [0.08525591, 0.08890588, 0.09252104], + [0.03039192, 0.03118624, 0.03278316], + ] +) + +M_R = np.array( + [ + [0.15579559, 0.09715755, 0.07514556], + [0.39113140, 0.25943419, 0.21266708], + [0.12824821, 0.18463570, 0.31508023], + [0.12028974, 0.13455659, 0.07408400], + [0.19368988, 0.21158946, 0.37955964], + [0.19957425, 0.36085439, 0.40678123], + [0.48896605, 0.20691688, 0.05816533], + [0.09775522, 0.16710693, 0.47147724], + [0.39358649, 0.12233400, 0.10526425], + [0.10780332, 0.07258529, 0.16151473], + [0.27502671, 0.34705454, 0.09728099], + [0.43980441, 0.26880559, 0.05430533], + [0.05887212, 0.11126272, 0.38552469], + [0.12705825, 0.25787860, 0.13566464], + [0.35612929, 0.07933258, 0.05118732], + [0.48131976, 0.42082843, 0.07120612], + [0.34665585, 0.15170714, 0.24969804], + [0.08261116, 0.24588716, 0.48707733], + [0.66054904, 0.65941137, 0.66376412], + [0.48051509, 0.47870296, 0.48230082], + [0.33045354, 0.32904184, 0.33228886], + [0.18001305, 0.17978567, 0.18004416], + [0.10283975, 0.10424680, 0.10384975], + [0.04742204, 0.04772203, 0.04914226], + ] +) message_box( - ('Computing the colour correction matrix correcting a "M_T" "ColorChecker"' - 'colour rendition chart to a "M_R" one using ' - '"Cheung, Westland, Connah and Ripamonti (2004)" method ' - 'with 3 terms polynomial.')) + 'Computing the colour correction matrix correcting a "M_T" "ColorChecker"' + 'colour rendition chart to a "M_R" one using ' + '"Cheung, Westland, Connah and Ripamonti (2004)" method ' + "with 3 terms polynomial." +) print(colour.characterisation.matrix_colour_correction_Cheung2004(M_T, M_R)) -print(colour.matrix_colour_correction(M_T, M_R, method='Cheung 2004')) +print(colour.matrix_colour_correction(M_T, M_R, method="Cheung 2004")) -print('\n') +print("\n") message_box( - ('Computing the colour correction matrix correcting a "M_T" "ColorChecker"' - 'colour rendition chart to a "M_R" one using ' - '"Cheung, Westland, Connah and Ripamonti (2004)" method ' - 'with 7 terms polynomial.')) + 'Computing the colour correction matrix correcting a "M_T" "ColorChecker"' + 'colour rendition chart to a "M_R" one using ' + '"Cheung, Westland, Connah and Ripamonti (2004)" method ' + "with 7 terms polynomial." +) print(colour.characterisation.matrix_colour_correction_Cheung2004(M_T, M_R, 7)) -print(colour.matrix_colour_correction(M_T, M_R, method='Cheung 2004', terms=7)) +print(colour.matrix_colour_correction(M_T, M_R, method="Cheung 2004", terms=7)) -print('\n') +print("\n") message_box( - ('Computing the colour correction matrix correcting a "M_T" "ColorChecker"' - 'colour rendition chart to a "M_R" one using ' - '"Finlayson, MacKiewicz and Hurlbert (2015)" method ' - 'with polynomial of degree 1.')) + 'Computing the colour correction matrix correcting a "M_T" "ColorChecker"' + 'colour rendition chart to a "M_R" one using ' + '"Finlayson, MacKiewicz and Hurlbert (2015)" method ' + "with polynomial of degree 1." +) print(colour.characterisation.matrix_colour_correction_Finlayson2015(M_T, M_R)) -print(colour.matrix_colour_correction(M_T, M_R, method='Finlayson 2015')) +print(colour.matrix_colour_correction(M_T, M_R, method="Finlayson 2015")) -print('\n') +print("\n") message_box( - ('Computing the colour correction matrix correcting a "M_T" "ColorChecker"' - 'colour rendition chart to a "M_R" one using ' - '"Finlayson, MacKiewicz and Hurlbert (2015)" method ' - 'with polynomial of degree 3.')) + 'Computing the colour correction matrix correcting a "M_T" "ColorChecker"' + 'colour rendition chart to a "M_R" one using ' + '"Finlayson, MacKiewicz and Hurlbert (2015)" method ' + "with polynomial of degree 3." +) print( - colour.characterisation.matrix_colour_correction_Finlayson2015( - M_T, M_R, 3)) + colour.characterisation.matrix_colour_correction_Finlayson2015(M_T, M_R, 3) +) print( colour.matrix_colour_correction( - M_T, M_R, method='Finlayson 2015', degree=3)) + M_T, M_R, method="Finlayson 2015", degree=3 + ) +) -print('\n') +print("\n") message_box( - ('Computing the colour correction matrix correcting a "M_T" "ColorChecker"' - 'colour rendition chart to a "M_R" one using ' - '"Vandermonde" method with polynomial of degree 1.')) + 'Computing the colour correction matrix correcting a "M_T" "ColorChecker"' + 'colour rendition chart to a "M_R" one using ' + '"Vandermonde" method with polynomial of degree 1.' +) print(colour.characterisation.matrix_colour_correction_Vandermonde(M_T, M_R)) -print(colour.matrix_colour_correction(M_T, M_R, method='Vandermonde')) +print(colour.matrix_colour_correction(M_T, M_R, method="Vandermonde")) -print('\n') +print("\n") message_box( - ('Computing the colour correction matrix correcting a "M_T" "ColorChecker"' - 'colour rendition chart to a "M_R" one using ' - '"Vandermonde" method with polynomial of degree 3.')) + 'Computing the colour correction matrix correcting a "M_T" "ColorChecker"' + 'colour rendition chart to a "M_R" one using ' + '"Vandermonde" method with polynomial of degree 3.' +) print( - colour.characterisation.matrix_colour_correction_Vandermonde(M_T, M_R, 3)) + colour.characterisation.matrix_colour_correction_Vandermonde(M_T, M_R, 3) +) print( - colour.matrix_colour_correction(M_T, M_R, method='Vandermonde', degree=3)) + colour.matrix_colour_correction(M_T, M_R, method="Vandermonde", degree=3) +) -print('\n') +print("\n") RGB = np.array([0.17224810, 0.09170660, 0.06416938]) message_box( - ('Colour correct given "RGB" colourspace array with matrix mapping a ' - '"M_T" "ColorChecker" colour rendition chart to a "M_R" one using ' - '"Cheung, Westland, Connah and Ripamonti (2004)" method with 3 terms ' - 'polynomial.')) + 'Colour correct given "RGB" colourspace array with matrix mapping a ' + '"M_T" "ColorChecker" colour rendition chart to a "M_R" one using ' + '"Cheung, Westland, Connah and Ripamonti (2004)" method with 3 terms ' + "polynomial." +) print(colour.characterisation.colour_correction_Cheung2004(RGB, M_T, M_R)) -print(colour.colour_correction(RGB, M_T, M_R, method='Cheung 2004')) +print(colour.colour_correction(RGB, M_T, M_R, method="Cheung 2004")) -print('\n') +print("\n") message_box( - ('Colour correct given "RGB" colourspace array with matrix mapping a ' - '"M_T" "ColorChecker" colour rendition chart to a "M_R" one using ' - '"Cheung, Westland, Connah and Ripamonti (2004)" method with 7 terms ' - 'polynomial.')) + 'Colour correct given "RGB" colourspace array with matrix mapping a ' + '"M_T" "ColorChecker" colour rendition chart to a "M_R" one using ' + '"Cheung, Westland, Connah and Ripamonti (2004)" method with 7 terms ' + "polynomial." +) print(colour.characterisation.colour_correction_Cheung2004(RGB, M_T, M_R, 7)) -print(colour.colour_correction(RGB, M_T, M_R, method='Cheung 2004', terms=7)) +print(colour.colour_correction(RGB, M_T, M_R, method="Cheung 2004", terms=7)) -print('\n') +print("\n") message_box( - ('Colour correct given "RGB" colourspace array with matrix mapping a ' - '"M_T" "ColorChecker" colour rendition chart to a "M_R" one using ' - '"Finlayson, MacKiewicz and Hurlbert (2015)" method with polynomial of ' - 'degree 1.')) + 'Colour correct given "RGB" colourspace array with matrix mapping a ' + '"M_T" "ColorChecker" colour rendition chart to a "M_R" one using ' + '"Finlayson, MacKiewicz and Hurlbert (2015)" method with polynomial of ' + "degree 1." +) print(colour.characterisation.colour_correction_Finlayson2015(RGB, M_T, M_R)) -print(colour.colour_correction(RGB, M_T, M_R, method='Finlayson 2015')) +print(colour.colour_correction(RGB, M_T, M_R, method="Finlayson 2015")) -print('\n') +print("\n") message_box( - ('Colour correct given "RGB" colourspace array with matrix mapping a ' - '"M_T" "ColorChecker" colour rendition chart to a "M_R" one using ' - '"Finlayson, MacKiewicz and Hurlbert (2015)" method with polynomial of ' - 'degree 3.')) + 'Colour correct given "RGB" colourspace array with matrix mapping a ' + '"M_T" "ColorChecker" colour rendition chart to a "M_R" one using ' + '"Finlayson, MacKiewicz and Hurlbert (2015)" method with polynomial of ' + "degree 3." +) print( - colour.characterisation.colour_correction_Finlayson2015(RGB, M_T, M_R, 3)) + colour.characterisation.colour_correction_Finlayson2015(RGB, M_T, M_R, 3) +) print( - colour.colour_correction(RGB, M_T, M_R, method='Finlayson 2015', degree=3)) + colour.colour_correction(RGB, M_T, M_R, method="Finlayson 2015", degree=3) +) -print('\n') +print("\n") message_box( - ('Colour correct given "RGB" colourspace array with matrix mapping a ' - '"M_T" "ColorChecker" colour rendition chart to a "M_R" one using ' - '"Vandermonde" method with polynomial of degree 1.')) + 'Colour correct given "RGB" colourspace array with matrix mapping a ' + '"M_T" "ColorChecker" colour rendition chart to a "M_R" one using ' + '"Vandermonde" method with polynomial of degree 1.' +) print(colour.characterisation.colour_correction_Vandermonde(RGB, M_T, M_R)) -print(colour.colour_correction(RGB, M_T, M_R, method='Vandermonde')) +print(colour.colour_correction(RGB, M_T, M_R, method="Vandermonde")) -print('\n') +print("\n") message_box( - ('Colour correct given "RGB" colourspace array with matrix mapping a ' - '"M_T" "ColorChecker" colour rendition chart to a "M_R" one using ' - '"Vandermonde" method with polynomial of degree 3.')) + 'Colour correct given "RGB" colourspace array with matrix mapping a ' + '"M_T" "ColorChecker" colour rendition chart to a "M_R" one using ' + '"Vandermonde" method with polynomial of degree 3.' +) print(colour.characterisation.colour_correction_Vandermonde(RGB, M_T, M_R, 3)) -print(colour.colour_correction(RGB, M_T, M_R, method='Vandermonde', degree=3)) +print(colour.colour_correction(RGB, M_T, M_R, method="Vandermonde", degree=3)) diff --git a/colour/examples/colorimetry/examples_blackbody.py b/colour/examples/colorimetry/examples_blackbody.py index 455efc9bee..9078a43486 100644 --- a/colour/examples/colorimetry/examples_blackbody.py +++ b/colour/examples/colorimetry/examples_blackbody.py @@ -1,25 +1,25 @@ -# -*- coding: utf-8 -*- -""" -Showcases blackbody / planckian radiator computations. -""" +"""Showcases blackbody / planckian radiator computations.""" import colour from colour.utilities import message_box -message_box('Blackbody / Planckian Radiator Computations') +message_box("Blackbody / Planckian Radiator Computations") -message_box(('Computing the spectral distribution of a blackbody at ' - 'temperature 5500 kelvin degrees and converting to "CIE XYZ" ' - 'tristimulus values.')) -cmfs = colour.MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] +message_box( + "Computing the spectral distribution of a blackbody at temperature 5500K" + 'degrees and converting to "CIE XYZ" tristimulus values.' +) +cmfs = colour.MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] sd_blackbody = colour.sd_blackbody(5500, cmfs.shape) print(sd_blackbody) XYZ = colour.sd_to_XYZ(sd_blackbody, cmfs) print(XYZ) -print('\n') +print("\n") -message_box(('Computing the spectral radiance of a blackbody at wavelength ' - '500 nm and temperature 5500 kelvin degrees.')) +message_box( + "Computing the spectral radiance of a blackbody at wavelength 500nm and " + "temperature 5500K degrees." +) print(colour.colorimetry.blackbody_spectral_radiance(500 * 1e-9, 5500)) print(colour.colorimetry.planck_law(500 * 1e-9, 5500)) diff --git a/colour/examples/colorimetry/examples_cmfs.py b/colour/examples/colorimetry/examples_cmfs.py index 25577dbb9f..c3df80a517 100644 --- a/colour/examples/colorimetry/examples_cmfs.py +++ b/colour/examples/colorimetry/examples_cmfs.py @@ -1,67 +1,89 @@ -# -*- coding: utf-8 -*- -""" -Showcases colour matching functions computations. -""" +"""Showcases colour matching functions computations.""" from pprint import pprint import colour from colour.utilities import message_box -message_box('Colour Matching Functions Computations') +message_box("Colour Matching Functions Computations") -message_box('Colour matching functions dataset.') +message_box("Colour matching functions dataset.") pprint(sorted(colour.MSDS_CMFS.keys())) -print('\n') +print("\n") -message_box(('Converting from "Wright & Guild 1931 2 Degree RGB CMFs" colour ' - 'matching functions to "CIE 1931 2 Degree Standard Observer" at ' - 'wavelength 700 nm.')) -print(colour.MSDS_CMFS['CIE 1931 2 Degree Standard Observer'][700]) -print(colour.colorimetry.MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'][700]) +message_box( + 'Converting from the "Wright & Guild 1931 2 Degree RGB CMFs" colour ' + 'matching functions to the "CIE 1931 2 Degree Standard Observer" at ' + "wavelength 700 nm." +) +print(colour.MSDS_CMFS["CIE 1931 2 Degree Standard Observer"][700]) +print( + colour.colorimetry.MSDS_CMFS_STANDARD_OBSERVER[ + "CIE 1931 2 Degree Standard Observer" + ][700] +) print(colour.colorimetry.RGB_2_degree_cmfs_to_XYZ_2_degree_cmfs(700)) -print('\n') +print("\n") -message_box(('Converting from "Stiles & Burch 1959 10 Degree RGB CMFs" colour ' - 'matching functions to "CIE 1964 10 Degree Standard Observer" at ' - 'wavelength 700 nm.')) -print(colour.MSDS_CMFS['CIE 1964 10 Degree Standard Observer'][700]) -print(colour.colorimetry.MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1964 10 Degree Standard Observer'][700]) +message_box( + 'Converting from the "Stiles & Burch 1959 10 Degree RGB CMFs" colour ' + 'matching functions to the "CIE 1964 10 Degree Standard Observer" at ' + "wavelength 700 nm." +) +print(colour.MSDS_CMFS["CIE 1964 10 Degree Standard Observer"][700]) +print( + colour.colorimetry.MSDS_CMFS_STANDARD_OBSERVER[ + "CIE 1964 10 Degree Standard Observer" + ][700] +) print(colour.colorimetry.RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(700)) -print('\n') +print("\n") -message_box(('Converting from "Stiles & Burch 1959 10 Degree RGB CMFs" colour ' - 'matching functions to ' - '"Stockman & Sharpe 10 Degree Cone Fundamentals" colour matching' - ' functions at wavelength 700 nm.')) -print(colour.MSDS_CMFS['Stockman & Sharpe 10 Degree Cone Fundamentals'][700]) -print(colour.colorimetry.MSDS_CMFS_LMS[ - 'Stockman & Sharpe 10 Degree Cone Fundamentals'][700]) +message_box( + 'Converting from the "Stiles & Burch 1959 10 Degree RGB CMFs" colour ' + "matching functions to the " + '"Stockman & Sharpe 10 Degree Cone Fundamentals" colour matching' + " functions at wavelength 700 nm." +) +print(colour.MSDS_CMFS["Stockman & Sharpe 10 Degree Cone Fundamentals"][700]) +print( + colour.colorimetry.MSDS_CMFS_LMS[ + "Stockman & Sharpe 10 Degree Cone Fundamentals" + ][700] +) print(colour.colorimetry.RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(700)) -print('\n') +print("\n") -message_box(('Converting from "Stockman & Sharpe 2 Degree Cone Fundamentals" ' - 'colour matching functions functions to ' - '"CIE 2012 2 Degree Standard Observer" spectral sensitivity ' - 'functions at wavelength 700 nm.')) -print(colour.MSDS_CMFS['CIE 2012 2 Degree Standard Observer'][700]) -print(colour.colorimetry.MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 2012 2 Degree Standard Observer'][700]) +message_box( + 'Converting from the "Stockman & Sharpe 2 Degree Cone Fundamentals" ' + "colour matching functions functions to the " + '"CIE 2012 2 Degree Standard Observer" spectral sensitivity ' + "functions at wavelength 700 nm." +) +print(colour.MSDS_CMFS["CIE 2012 2 Degree Standard Observer"][700]) +print( + colour.colorimetry.MSDS_CMFS_STANDARD_OBSERVER[ + "CIE 2012 2 Degree Standard Observer" + ][700] +) print(colour.colorimetry.LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(700)) -print('\n') +print("\n") -message_box(('Converting from "Stockman & Sharpe 10 Degree Cone Fundamentals" ' - 'colour matching functions functions to ' - '"CIE 2012 10 Degree Standard Observer" spectral sensitivity ' - 'functions at wavelength 700 nm.')) -print(colour.MSDS_CMFS['CIE 2012 10 Degree Standard Observer'][700]) -print(colour.colorimetry.MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 2012 10 Degree Standard Observer'][700]) +message_box( + 'Converting from the "Stockman & Sharpe 10 Degree Cone Fundamentals" ' + "colour matching functions functions to the " + '"CIE 2012 10 Degree Standard Observer" spectral sensitivity ' + "functions at wavelength 700 nm." +) +print(colour.MSDS_CMFS["CIE 2012 10 Degree Standard Observer"][700]) +print( + colour.colorimetry.MSDS_CMFS_STANDARD_OBSERVER[ + "CIE 2012 10 Degree Standard Observer" + ][700] +) print(colour.colorimetry.LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(700)) diff --git a/colour/examples/colorimetry/examples_correction.py b/colour/examples/colorimetry/examples_correction.py index 4f481fd9b0..8f1677967d 100644 --- a/colour/examples/colorimetry/examples_correction.py +++ b/colour/examples/colorimetry/examples_correction.py @@ -1,19 +1,17 @@ -# -*- coding: utf-8 -*- -""" -Showcases colour spectral bandpass dependence correction computations. -""" +"""Showcases colour spectral bandpass dependence correction computations.""" import numpy as np import colour from colour.utilities import message_box -message_box('Spectral Bandpass Dependence Correction') +message_box("Spectral Bandpass Dependence Correction") -message_box(('Applying spectral bandpass dependence correction on a sample ' - 'spectral distribution using "Stearns and Stearns (1988)" ' - 'method:\n' - '\n\t("Measured Values", "Corrected Values")')) +message_box( + "Applying spectral bandpass dependence correction on a sample spectral " + 'distribution using "Stearns and Stearns (1988)" method:\n\n' + '\t("Measured Values", "Corrected Values")' +) data_sample = { 380: 0.048, 385: 0.051, @@ -95,12 +93,13 @@ 765: 0.465, 770: 0.448, 775: 0.432, - 780: 0.421 + 780: 0.421, } -sd_sample = colour.SpectralDistribution(data_sample, name='Sample') +sd_sample = colour.SpectralDistribution(data_sample, name="Sample") uncorrected_values = sd_sample.values print( np.dstack( - [uncorrected_values, - colour.bandpass_correction(sd_sample).values])) + [uncorrected_values, colour.bandpass_correction(sd_sample).values] + ) +) diff --git a/colour/examples/colorimetry/examples_dominant.py b/colour/examples/colorimetry/examples_dominant.py index 5da9ac9929..3b9fd1bd97 100644 --- a/colour/examples/colorimetry/examples_dominant.py +++ b/colour/examples/colorimetry/examples_dominant.py @@ -1,71 +1,85 @@ -# -*- coding: utf-8 -*- -""" -Showcases dominant wavelength and purity of a colour computations. -""" +"""Showcases dominant wavelength and purity of a colour computations.""" import numpy as np import colour from colour.utilities import message_box -message_box('Dominant Wavelength and Purity') +message_box("Dominant Wavelength and Purity") xy = np.array([0.54369557, 0.32107944]) xy_n = np.array([0.31270, 0.32900]) -cmfs = colour.MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] -message_box(('Computing the "dominant wavelength" for colour stimulus "xy" ' - 'and achromatic stimulus "xy_n" chromaticity coordinates:\n' - '\n\txy : {0}\n\txy_n : {1}'.format(xy, xy_n))) +cmfs = colour.MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] +message_box( + f'Computing the "dominant wavelength" for colour stimulus "xy" and ' + f'achromatic stimulus "xy_n" chromaticity coordinates:\n\n' + f"\txy: {xy}\n" + f"\txy_n: {xy_n}" +) print(colour.dominant_wavelength(xy, xy_n, cmfs)) -print('\n') +print("\n") xy = np.array([0.35000, 0.25000]) -message_box(('Computing the "dominant wavelength" for colour stimulus "xy" ' - 'and achromatic stimulus "xy_n" chromaticity coordinates:\n' - '\n\txy : {0}\n\txy_n : {1}\n\n' - 'In this case the "complementary dominant wavelength" indicated ' - 'by a negative sign is returned because the first intersection is' - ' located on the line of purples.'.format(xy, xy_n))) +message_box( + f'Computing the "dominant wavelength" for colour stimulus "xy" and ' + f'achromatic stimulus "xy_n" chromaticity coordinates:\n\n' + f"\txy: {xy}\n" + f"\txy_n: {xy_n}\n\n" + f'In this case the "complementary dominant wavelength" indicated by a ' + f"negative sign is returned because the first intersection is located on " + f"the line of purples." +) print(colour.dominant_wavelength(xy, xy_n, cmfs)) -print('\n') +print("\n") xy = np.array([0.54369557, 0.32107944]) -message_box(('Computing the "complementary wavelength" for colour stimulus ' - '"xy" and achromatic stimulus "xy_n" chromaticity coordinates:\n' - '\n\txy : {0}\n\txy_n : {1}\n\n' - 'In this case the "dominant wavelength" indicated by a negative ' - 'sign is returned because the first intersection is located on ' - 'the line of purples.'.format(xy, xy_n))) +message_box( + f'Computing the "complementary wavelength" for colour stimulus "xy" and ' + f'achromatic stimulus "xy_n" chromaticity coordinates:\n\n' + f"\txy: {xy}\n" + f"\txy_n: {xy_n}\n\n" + f'In this case the "dominant wavelength" indicated by a negative sign is ' + f"returned because the first intersection is located on the line of purples." +) print(colour.complementary_wavelength(xy, xy_n, cmfs)) -print('\n') +print("\n") xy = np.array([0.35000, 0.25000]) -message_box(('Computing the "complementary wavelength" for colour stimulus ' - '"xy" and achromatic stimulus "xy_n" chromaticity coordinates:\n' - '\n\txy : {0}\n\txy_n : {1}'.format(xy, xy_n))) +message_box( + f'Computing the "complementary wavelength" for colour stimulus "xy" and ' + f'achromatic stimulus "xy_n" chromaticity coordinates:\n\n' + f"\txy: {xy}\n" + f"\txy_n: {xy_n}" +) print(colour.complementary_wavelength(xy, xy_n, cmfs)) -print('\n') +print("\n") xy = np.array([0.54369557, 0.32107944]) -message_box(('Computing the "excitation purity" for colour stimulus "xy" ' - 'and achromatic stimulus "xy_n" chromaticity coordinates:\n' - '\n\txy : {0}\n\txy_n : {1}'.format(xy, xy_n))) +message_box( + f'Computing the "excitation purity" for colour stimulus "xy" and ' + f'achromatic stimulus "xy_n" chromaticity coordinates:\n\n' + f"\txy: {xy}\n" + f"\txy_n: {xy_n}" +) print(colour.excitation_purity(xy, xy_n, cmfs)) -print('\n') +print("\n") xy = np.array([0.54369557, 0.32107944]) -message_box(('Computing the "colorimetric purity" for colour stimulus "xy" ' - 'and achromatic stimulus "xy_n" chromaticity coordinates:\n' - '\n\txy : {0}\n\txy_n : {1}'.format(xy, xy_n))) +message_box( + f'Computing the "colorimetric purity" for colour stimulus "xy" and ' + f'achromatic stimulus "xy_n" chromaticity coordinates:\n\n' + f"\txy: {xy}\n" + f"\txy_n: {xy_n}" +) print(colour.colorimetric_purity(xy, xy_n, cmfs)) diff --git a/colour/examples/colorimetry/examples_illuminants.py b/colour/examples/colorimetry/examples_illuminants.py index e549d09221..5eb1e714c7 100644 --- a/colour/examples/colorimetry/examples_illuminants.py +++ b/colour/examples/colorimetry/examples_illuminants.py @@ -1,28 +1,26 @@ -# -*- coding: utf-8 -*- -""" -Showcases illuminants datasets. -""" +"""Showcases illuminants datasets.""" from pprint import pprint import colour from colour.utilities import message_box -message_box('Illuminants Dataset') +message_box("Illuminants Dataset") -message_box('Illuminants spectral distributions dataset.') +message_box("Illuminants spectral distributions dataset.") pprint(sorted(colour.SDS_ILLUMINANTS.keys())) -print('\n') +print("\n") -message_box('Illuminants chromaticity coordinates dataset.') +message_box("Illuminants chromaticity coordinates dataset.") # Filtering aliases. -observers = dict( - ((observer, dataset) - for observer, dataset in sorted(colour.CCS_ILLUMINANTS.items()) - if ' ' in observer)) +observers = { + observer: dataset + for observer, dataset in sorted(colour.CCS_ILLUMINANTS.items()) + if " " in observer +} for observer, illuminants in observers.items(): - print('"{0}".'.format(observer)) + print(f'"{observer}".') for illuminant, xy in sorted(illuminants.items()): - print('\t"{0}": {1}'.format(illuminant, xy)) - print('\n') + print(f'\t"{illuminant}": {xy}') + print("\n") diff --git a/colour/examples/colorimetry/examples_lefs.py b/colour/examples/colorimetry/examples_lefs.py index 5e81b25cd7..cbc2b6f6ae 100644 --- a/colour/examples/colorimetry/examples_lefs.py +++ b/colour/examples/colorimetry/examples_lefs.py @@ -1,20 +1,18 @@ -# -*- coding: utf-8 -*- -""" -Showcases luminous efficiency functions computations. -""" +"""Showcases luminous efficiency functions computations.""" from pprint import pprint import colour from colour.utilities import message_box -message_box('Luminous Efficiency Functions Computations') +message_box("Luminous Efficiency Functions Computations") -message_box('Luminous efficiency functions dataset.') +message_box("Luminous efficiency functions dataset.") pprint(sorted(colour.SDS_LEFS)) -print('\n') +print("\n") -message_box(('Computing the mesopic luminous efficiency function for factor:\n' - '\n\t0.2')) +message_box( + "Computing the mesopic luminous efficiency function for factor:\n\n\t0.2" +) print(colour.sd_mesopic_luminous_efficiency_function(0.2).values) diff --git a/colour/examples/colorimetry/examples_light_sources.py b/colour/examples/colorimetry/examples_light_sources.py index eb09570692..68cddba273 100644 --- a/colour/examples/colorimetry/examples_light_sources.py +++ b/colour/examples/colorimetry/examples_light_sources.py @@ -1,28 +1,26 @@ -# -*- coding: utf-8 -*- -""" -Showcases light sources datasets. -""" +"""Showcases light sources datasets.""" from pprint import pprint import colour from colour.utilities import message_box -message_box('Light Sources Dataset') +message_box("Light Sources Dataset") -message_box('Light sources spectral distributions datasets.') +message_box("Light sources spectral distributions datasets.") pprint(sorted(colour.SDS_LIGHT_SOURCES.keys())) -print('\n') +print("\n") -message_box('Light sources chromaticity coordinates datasets.') +message_box("Light sources chromaticity coordinates datasets.") # Filtering aliases. -observers = dict( - ((observer, dataset) - for observer, dataset in sorted(colour.CCS_LIGHT_SOURCES.items()) - if ' ' in observer)) +observers = { + observer: dataset + for observer, dataset in sorted(colour.CCS_LIGHT_SOURCES.items()) + if " " in observer +} for observer, light_source in observers.items(): - print('"{0}".'.format(observer)) + print(f'"{observer}".') for illuminant, xy in sorted(light_source.items()): - print('\t"{0}": {1}'.format(illuminant, xy)) - print('\n') + print(f'\t"{illuminant}": {xy}') + print("\n") diff --git a/colour/examples/colorimetry/examples_lightness.py b/colour/examples/colorimetry/examples_lightness.py index cfb96b0d6c..1b6d54f366 100644 --- a/colour/examples/colorimetry/examples_lightness.py +++ b/colour/examples/colorimetry/examples_lightness.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *Lightness* computations. -""" +"""Showcases *Lightness* computations.""" import numpy as np @@ -11,49 +8,55 @@ message_box('"Lightness" Computations') xyY = np.array([0.4316, 0.3777, 0.1008]) -message_box(('Computing "Lightness" "CIE L*a*b*" reference value for given ' - '"CIE xyY" colourspace values:\n' - '\n\t{0}'.format(xyY))) +message_box( + f'Computing "Lightness" "CIE L*a*b*" reference value for given "CIE xyY" ' + f"colourspace values:\n\n\t{xyY}" +) print(np.ravel(colour.XYZ_to_Lab(colour.xyY_to_XYZ(xyY)))[0]) -print('\n') +print("\n") Y = 12.19722535 -message_box(('Computing "Lightness" using ' - '"Glasser, Mckinney, Reilly and Schnelle (1958)" method for ' - 'given "luminance" value:\n' - '\n\t{0}'.format(Y))) -print(colour.lightness(Y, method='Glasser 1958')) +message_box( + f'Computing "Lightness" using ' + f'"Glasser, Mckinney, Reilly and Schnelle (1958)" method for given ' + f'"luminance" value:\n\n\t{Y}' +) +print(colour.lightness(Y, method="Glasser 1958")) print(colour.colorimetry.lightness_Glasser1958(Y)) -print('\n') +print("\n") -message_box(('Computing "Lightness" using "Wyszecki (1963)" method for ' - 'given "luminance" value:\n' - '\n\t{0}'.format(Y))) -print(colour.lightness(Y, method='Wyszecki 1963')) +message_box( + f'Computing "Lightness" using "Wyszecki (1963)" method for given ' + f'"luminance" value:\n\n\t{Y}' +) +print(colour.lightness(Y, method="Wyszecki 1963")) print(colour.colorimetry.lightness_Wyszecki1963(Y)) -print('\n') +print("\n") -message_box(('Computing "Lightness" using "Fairchild and Wyble (2010)" method ' - 'for given "luminance" value:\n' - '\n\t{0}'.format(Y))) -print(colour.lightness(Y, method='Fairchild 2010')) +message_box( + f'Computing "Lightness" using "Fairchild and Wyble (2010)" method for ' + f'given "luminance" value:\n\n\t{Y}' +) +print(colour.lightness(Y, method="Fairchild 2010")) print(colour.colorimetry.lightness_Fairchild2010(Y / 100.0)) -print('\n') +print("\n") -message_box(('Computing "Lightness" using "Fairchild and Chen (2011)" method ' - 'for given "luminance" value:\n' - '\n\t{0}'.format(Y))) -print(colour.lightness(Y, method='Fairchild 2011')) +message_box( + f'Computing "Lightness" using "Fairchild and Chen (2011)" method for ' + f'given "luminance" value:\n\n\t{Y}' +) +print(colour.lightness(Y, method="Fairchild 2011")) print(colour.colorimetry.lightness_Fairchild2011(Y / 100.0)) -print('\n') +print("\n") -message_box(('Computing "Lightness" using "CIE 1976" method for ' - 'given "luminance" value:\n' - '\n\t{0}'.format(Y))) -print(colour.lightness(Y, method='CIE 1976')) +message_box( + f'Computing "Lightness" using "CIE 1976" method for given "luminance" ' + f"value:\n\n\t{Y}" +) +print(colour.lightness(Y, method="CIE 1976")) print(colour.colorimetry.lightness_CIE1976(Y)) diff --git a/colour/examples/colorimetry/examples_luminance.py b/colour/examples/colorimetry/examples_luminance.py index e44dfa6a05..7ae3f9f7b8 100644 --- a/colour/examples/colorimetry/examples_luminance.py +++ b/colour/examples/colorimetry/examples_luminance.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *Luminance* computations. -""" +"""Showcases *Luminance* computations.""" import colour @@ -10,44 +7,48 @@ message_box('"Luminance" Computations') V = 4.08244375 -message_box(('Computing "luminance" using ' - '"Newhall, Nickerson, and Judd (1943)" method for given ' - '"Munsell" value:\n' - '\n\t{0}'.format(V))) -print(colour.luminance(V, method='Newhall 1943')) +message_box( + f'Computing "luminance" using "Newhall, Nickerson, and Judd (1943)" ' + f'method for given "Munsell" value:\n\n\t{V}' +) +print(colour.luminance(V, method="Newhall 1943")) print(colour.colorimetry.luminance_Newhall1943(V)) -print('\n') +print("\n") L = 41.527875844653451 -message_box(('Computing "luminance" using "CIE 1976" method for given ' - '"Lightness":\n' - '\n\t{0}'.format(L))) +message_box( + f'Computing "luminance" using "CIE 1976" method for given ' + f'"Lightness":\n\n\t{L}' +) print(colour.luminance(L)) print(colour.colorimetry.luminance_CIE1976(L)) -print('\n') +print("\n") L = 31.996390226262736 -message_box(('Computing "luminance" using "Fairchild and Wyble (2010)" method ' - 'for given "Lightness":\n' - '\n\t{0}'.format(L))) -print(colour.luminance(L, method='Fairchild 2010') * 100) +message_box( + f'Computing "luminance" using "Fairchild and Wyble (2010)" method for ' + f'given "Lightness":\n\n\t{L}' +) +print(colour.luminance(L, method="Fairchild 2010") * 100) print(colour.colorimetry.luminance_Fairchild2010(L) * 100) -print('\n') +print("\n") L = 51.852958445912506 -message_box(('Computing "luminance" using "Fairchild and Chen (2011)" method ' - 'for given "Lightness":\n' - '\n\t{0}'.format(L))) -print(colour.luminance(L, method='Fairchild 2011') * 100) +message_box( + f'Computing "luminance" using "Fairchild and Chen (2011)" method for ' + f'given "Lightness":\n\n\t{L}' +) +print(colour.luminance(L, method="Fairchild 2011") * 100) print(colour.colorimetry.luminance_Fairchild2011(L) * 100) -print('\n') +print("\n") -message_box(('Computing "luminance" using "ASTM D1535-08e1" method for given ' - '"Munsell" value:\n' - '\n\t{0}'.format(V))) -print(colour.luminance(V, method='ASTM D1535')) +message_box( + f'Computing "luminance" using "ASTM D1535-08e1" method for given ' + f'"Munsell" value:\n\n\t{V}' +) +print(colour.luminance(V, method="ASTM D1535")) print(colour.colorimetry.luminance_ASTMD1535(V)) diff --git a/colour/examples/colorimetry/examples_photometry.py b/colour/examples/colorimetry/examples_photometry.py index 1a933c8490..c3bae23671 100644 --- a/colour/examples/colorimetry/examples_photometry.py +++ b/colour/examples/colorimetry/examples_photometry.py @@ -1,29 +1,29 @@ -# -*- coding: utf-8 -*- -""" -Showcases *Photometry* computations. -""" +"""Showcases *Photometry* computations.""" import colour from colour.utilities import message_box message_box('"Photometry" Computations') -sd_light_source = colour.SDS_LIGHT_SOURCES['Neodimium Incandescent'] -message_box(('Computing "Luminous Flux" for given spectral ' - 'distribution:\n' - '\n\t{0}'.format(sd_light_source.name))) +sd_light_source = colour.SDS_LIGHT_SOURCES["Neodimium Incandescent"] +message_box( + f'Computing "Luminous Flux" for given spectral distribution:\n\n' + f"\t{sd_light_source.name}" +) print(colour.luminous_flux(sd_light_source)) -print('\n') +print("\n") -message_box(('Computing "Luminous Efficiency" for given spectral ' - 'distribution:\n' - '\n\t{0}'.format(sd_light_source.name))) +message_box( + f'Computing "Luminous Efficiency" for given spectral distribution:\n\n' + f"\t{sd_light_source.name}" +) print(colour.luminous_efficiency(sd_light_source)) -print('\n') +print("\n") -message_box(('Computing "Luminous Efficacy" for given spectral ' - 'distribution:\n' - '\n\t{0}'.format(sd_light_source.name))) +message_box( + f'Computing "Luminous Efficacy" for given spectral distribution:\n\n' + f"\t{sd_light_source.name}" +) print(colour.luminous_efficacy(sd_light_source)) diff --git a/colour/examples/colorimetry/examples_spectrum.py b/colour/examples/colorimetry/examples_spectrum.py index 4775715294..c080cf7119 100644 --- a/colour/examples/colorimetry/examples_spectrum.py +++ b/colour/examples/colorimetry/examples_spectrum.py @@ -1,14 +1,11 @@ -# -*- coding: utf-8 -*- -""" -Showcases colour spectrum computations. -""" +"""Showcases colour spectrum computations.""" import numpy as np import colour from colour.utilities import message_box -message_box('Spectrum Computations') +message_box("Spectrum Computations") data_sample = { 380: 0.048, @@ -91,95 +88,99 @@ 765: 0.465, 770: 0.448, 775: 0.432, - 780: 0.421 + 780: 0.421, } -sd_sample = colour.SpectralDistribution(data_sample, name='Sample') +sd_sample = colour.SpectralDistribution(data_sample, name="Sample") -message_box('Sample spectral distribution shape.') +message_box("Sample spectral distribution shape.") print(sd_sample.shape) -print('\n') +print("\n") -message_box('Sample spectral distribution uniformity.') +message_box("Sample spectral distribution uniformity.") print(sd_sample.is_uniform()) -print('\n') +print("\n") -message_box(('Sample spectral distribution cloning:\n' - '\n\t("Original Id", "Clone Id")\n' - '\nCloning is a convenient way to get a copy of the spectral ' - 'distribution, this an important feature because some ' - 'operations happen in place.')) +message_box( + "Sample spectral distribution cloning:\n\n" + '\t("Original Id", "Clone Id")\n' + "\nCloning is a convenient way to get a copy of the spectral " + "distribution, this an important feature because some operations happen " + "in place." +) sd_clone = sd_sample.copy() print(id(sd_sample), id(sd_clone)) -print('\n') +print("\n") -message_box('Sample spectral distribution arithmetical operations.') -message_box('Regular arithmetical operation: adding a constant.') +message_box("Sample spectral distribution arithmetical operations.") +message_box("Regular arithmetical operation: adding a constant.") sd_clone_alternate = sd_clone + 10 print(sd_clone[380], sd_clone_alternate[380]) -print('\n') +print("\n") -message_box('Regular arithmetical operation: adding an array.') +message_box("Regular arithmetical operation: adding an array.") print((sd_clone + np.linspace(0, 1, len(sd_clone.wavelengths))).values) -print('\n') +print("\n") -message_box('Regular arithmetical operation: adding a spectral ' - 'distribution.') +message_box( + "Regular arithmetical operation: adding a spectral " "distribution." +) print((sd_clone + sd_clone).values) -print('\n') +print("\n") -message_box('In-place arithmetical operation: adding a constant.') +message_box("In-place arithmetical operation: adding a constant.") sd_clone += 10 print(sd_clone[380]) -print('\n') +print("\n") -message_box('In-place arithmetical operation: adding an array.') +message_box("In-place arithmetical operation: adding an array.") sd_clone += np.linspace(0, 1, len(sd_clone.wavelengths)) print(sd_clone.values) -print('\n') +print("\n") -message_box('In-place arithmetical operation: adding a spectral ' - 'distribution.') +message_box( + "In-place arithmetical operation: adding a spectral " "distribution." +) sd_clone += sd_clone print(sd_clone.values) -print('\n') +print("\n") -message_box('Sample spectral distribution interpolation.') +message_box("Sample spectral distribution interpolation.") sd_clone.interpolate(colour.SpectralShape(360, 780, 1)) print(sd_clone[666]) -print('\n') +print("\n") -message_box('Sample spectral distribution extrapolation.') -sd_clone.extrapolate(colour.SpectralShape(340, 830)) +message_box("Sample spectral distribution extrapolation.") +sd_clone.extrapolate(colour.SpectralShape(340, 830, 1)) print(sd_clone[340], sd_clone[360]) -print('\n') +print("\n") -message_box('Sample spectral distribution align.') +message_box("Sample spectral distribution align.") sd_clone.align(colour.SpectralShape(400, 700, 5)) print(sd_clone[400], sd_clone[700]) -print('\n') +print("\n") -message_box('Constant value filled spectral distribution.') +message_box("Constant value filled spectral distribution.") print(colour.sd_constant(3.1415)[400]) -print('\n') +print("\n") -message_box('Zeros filled spectral distribution.') +message_box("Zeros filled spectral distribution.") print(colour.sd_zeros()[400]) -print('\n') +print("\n") -message_box('Ones filled spectral distribution.') +message_box("Ones filled spectral distribution.") print(colour.sd_ones()[400]) diff --git a/colour/examples/colorimetry/examples_tristimulus.py b/colour/examples/colorimetry/examples_tristimulus.py index 5520677298..0aea836538 100644 --- a/colour/examples/colorimetry/examples_tristimulus.py +++ b/colour/examples/colorimetry/examples_tristimulus.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *CIE XYZ* tristimulus values computations. -""" +"""Showcases *CIE XYZ* tristimulus values computations.""" import numpy as np @@ -91,52 +88,158 @@ 765: 0.465, 770: 0.448, 775: 0.432, - 780: 0.421 + 780: 0.421, } -sd_sample = colour.SpectralDistribution(data_sample, name='Sample') +sd_sample = colour.SpectralDistribution(data_sample, name="Sample") -cmfs = colour.MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] -illuminant = colour.SDS_ILLUMINANTS['A'] +cmfs = colour.MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] +illuminant = colour.SDS_ILLUMINANTS["A"] -message_box(('Computing *CIE XYZ* tristimulus values for sample spectral ' - 'distribution and "CIE Standard Illuminant A".')) +message_box( + "Computing *CIE XYZ* tristimulus values for sample spectral distribution " + 'and "CIE Standard Illuminant A".' +) print(colour.sd_to_XYZ(sd_sample, cmfs, illuminant)) -print('\n') +print("\n") -message_box(('Computing "CIE Standard Illuminant A" chromaticity coordinates ' - 'from its spectral distribution.')) +message_box( + 'Computing "CIE Standard Illuminant A" chromaticity coordinates from its ' + "spectral distribution." +) print(colour.XYZ_to_xy(colour.sd_to_XYZ(illuminant, cmfs) / 100)) -print('\n') +print("\n") -message_box(('Computing *CIE XYZ* tristimulus values for a single given ' - 'wavelength in nm.')) +message_box( + "Computing *CIE XYZ* tristimulus values for a single given wavelength in " + "nm." +) print( colour.wavelength_to_XYZ( - 546.1, colour.MSDS_CMFS['CIE 1931 2 Degree Standard Observer'])) + 546.1, colour.MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] + ) +) -message_box(('Computing *CIE XYZ* tristimulus values from given ' - 'multi-spectral image with shape (4, 3, 6).')) -msds = np.array([ - [[0.01367208, 0.09127947, 0.01524376, 0.02810712, 0.19176012, 0.04299992], - [0.00959792, 0.25822842, 0.41388571, 0.22275120, 0.00407416, 0.37439537], - [0.01791409, 0.29707789, 0.56295109, 0.23752193, 0.00236515, 0.58190280]], - [[0.01492332, 0.10421912, 0.02240025, 0.03735409, 0.57663846, 0.32416266], - [0.04180972, 0.26402685, 0.03572137, 0.00413520, 0.41808194, 0.24696727], - [0.00628672, 0.11454948, 0.02198825, 0.39906919, 0.63640803, 0.01139849]], - [[0.04325933, 0.26825359, 0.23732357, 0.05175860, 0.01181048, 0.08233768], - [0.02484169, 0.12027161, 0.00541695, 0.00654612, 0.18603799, 0.36247808], - [0.03102159, 0.16815442, 0.37186235, 0.08610666, 0.00413520, 0.78492409]], - [[0.11682307, 0.78883040, 0.74468607, 0.83375293, 0.90571451, 0.70054168], - [0.06321812, 0.41898224, 0.15190357, 0.24591440, 0.55301750, 0.00657664], - [0.00305180, 0.11288624, 0.11357290, 0.12924391, 0.00195315, 0.21771573]], -]) +message_box( + "Computing *CIE XYZ* tristimulus values from given multi-spectral image " + "with shape (4, 3, 6)." +) +msds = np.array( + [ + [ + [ + 0.01367208, + 0.09127947, + 0.01524376, + 0.02810712, + 0.19176012, + 0.04299992, + ], + [ + 0.00959792, + 0.25822842, + 0.41388571, + 0.22275120, + 0.00407416, + 0.37439537, + ], + [ + 0.01791409, + 0.29707789, + 0.56295109, + 0.23752193, + 0.00236515, + 0.58190280, + ], + ], + [ + [ + 0.01492332, + 0.10421912, + 0.02240025, + 0.03735409, + 0.57663846, + 0.32416266, + ], + [ + 0.04180972, + 0.26402685, + 0.03572137, + 0.00413520, + 0.41808194, + 0.24696727, + ], + [ + 0.00628672, + 0.11454948, + 0.02198825, + 0.39906919, + 0.63640803, + 0.01139849, + ], + ], + [ + [ + 0.04325933, + 0.26825359, + 0.23732357, + 0.05175860, + 0.01181048, + 0.08233768, + ], + [ + 0.02484169, + 0.12027161, + 0.00541695, + 0.00654612, + 0.18603799, + 0.36247808, + ], + [ + 0.03102159, + 0.16815442, + 0.37186235, + 0.08610666, + 0.00413520, + 0.78492409, + ], + ], + [ + [ + 0.11682307, + 0.78883040, + 0.74468607, + 0.83375293, + 0.90571451, + 0.70054168, + ], + [ + 0.06321812, + 0.41898224, + 0.15190357, + 0.24591440, + 0.55301750, + 0.00657664, + ], + [ + 0.00305180, + 0.11288624, + 0.11357290, + 0.12924391, + 0.00195315, + 0.21771573, + ], + ], + ] +) print( colour.msds_to_XYZ( msds, cmfs, illuminant, - method='Integration', - shape=colour.SpectralShape(400, 700, 60))) + method="Integration", + shape=colour.SpectralShape(400, 700, 60), + ) +) diff --git a/colour/examples/colorimetry/examples_uniformity.py b/colour/examples/colorimetry/examples_uniformity.py new file mode 100644 index 0000000000..819edfce14 --- /dev/null +++ b/colour/examples/colorimetry/examples_uniformity.py @@ -0,0 +1,24 @@ +"""Showcases spectral uniformity computations.""" + +import colour +from colour.quality.cfi2017 import load_TCS_CIE2017 +from colour.quality.datasets import SDS_TCS +from colour.utilities import message_box + +message_box("Spectral Uniformity (or Flatness) Computations") + +message_box( + 'Computing the spectral uniformity of the "CRI" test colour samples.' +) + +print(colour.spectral_uniformity(SDS_TCS.values())) + +print("\n") + +message_box( + 'Computing the spectral uniformity of the "CFI" test colour samples.' +) + +print( + colour.spectral_uniformity(load_TCS_CIE2017(colour.SPECTRAL_SHAPE_DEFAULT)) +) diff --git a/colour/examples/colorimetry/examples_whiteness.py b/colour/examples/colorimetry/examples_whiteness.py index 76b8eb08ec..6079897658 100644 --- a/colour/examples/colorimetry/examples_whiteness.py +++ b/colour/examples/colorimetry/examples_whiteness.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *whiteness* computations. -""" +"""Showcases *whiteness* computations.""" import numpy as np @@ -13,66 +10,69 @@ XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) XYZ_0 = np.array([94.80966767, 100.00000000, 107.30513595]) message_box( - ('Computing "whiteness" using "Berger (1959)" method for ' - 'given sample and reference white "CIE XYZ" tristimulus values ' - 'matrices:\n' - '\n\t{0}\n\t{1}\n\n' - 'Warning: The input domain of that definition is non standard!'.format( - XYZ, XYZ_0))) -print(colour.whiteness(XYZ, XYZ_0, method='Berger 1959')) + f'Computing "whiteness" using "Berger (1959)" method for given sample and ' + f'reference white "CIE XYZ" tristimulus values matrices:\n\n' + f"\t{XYZ}\n" + f"\t{XYZ_0}" +) +print(colour.whiteness(XYZ, XYZ_0, method="Berger 1959")) print(colour.colorimetry.whiteness_Berger1959(XYZ, XYZ_0)) -print('\n') +print("\n") -message_box(('Computing "whiteness" using "Taube (1960)" method for ' - 'given sample and reference white "CIE XYZ" tristimulus values ' - 'matrices:\n' - '\n\t{0}\n\t{1}'.format(XYZ, XYZ_0))) -print(colour.whiteness(XYZ, XYZ_0, method='Taube 1960')) +message_box( + f'Computing "whiteness" using "Taube (1960)" method for given sample and ' + f'reference white "CIE XYZ" tristimulus values matrices:\n\n' + f"\t{XYZ}\n" + f"\t{XYZ_0}" +) +print(colour.whiteness(XYZ, XYZ_0, method="Taube 1960")) print(colour.colorimetry.whiteness_Taube1960(XYZ, XYZ_0)) -print('\n') +print("\n") Lab = colour.XYZ_to_Lab(XYZ / 100, colour.XYZ_to_xy(XYZ_0 / 100)) -message_box(('Computing "whiteness" using "Stensby (1968)" method for ' - 'given sample "CIE L*a*b*" colourspace array:\n' - '\n\t{0}'.format(Lab))) -print(colour.whiteness(XYZ, XYZ_0, method='Stensby 1968')) +message_box( + f'Computing "whiteness" using "Stensby (1968)" method for given sample ' + f'"CIE L*a*b*" colourspace array:\n\n\t{Lab}' +) +print(colour.whiteness(XYZ, XYZ_0, method="Stensby 1968")) print(colour.colorimetry.whiteness_Stensby1968(Lab)) -print('\n') +print("\n") -message_box(('Computing "whiteness" using "ASTM E313" method for ' - 'given sample "CIE XYZ" tristimulus values:\n' - '\n\t{0}'.format(XYZ))) -print(colour.whiteness(XYZ, XYZ_0, method='ASTM E313')) +message_box( + f'Computing "whiteness" using "ASTM E313" method for given sample ' + f'"CIE XYZ" tristimulus values:\n\n\t{XYZ}' +) +print(colour.whiteness(XYZ, XYZ_0, method="ASTM E313")) print(colour.colorimetry.whiteness_ASTME313(XYZ)) -print('\n') +print("\n") xy = colour.XYZ_to_xy(XYZ / 100) Y = 100 message_box( - ('Computing "whiteness" using "Ganz and Griesser (1979)" method ' - 'for given sample "xy" chromaticity coordinates, "Y" tristimulus ' - 'value:\n' - '\n\t{0}\n\t{1}\n\n' - 'Warning: The input domain of that definition is non standard!'.format( - xy, Y))) -print(colour.whiteness(XYZ, XYZ_0, method='Ganz 1979')) + f'Computing "whiteness" using "Ganz and Griesser (1979)" method for given ' + f'sample "xy" chromaticity coordinates, "Y" tristimulus value:\n\n' + f"\t{xy}\n" + f"\t{Y}" +) +print(colour.whiteness(XYZ, XYZ_0, method="Ganz 1979")) print(colour.colorimetry.whiteness_Ganz1979(xy, Y)) -print('\n') +print("\n") xy = colour.XYZ_to_xy(XYZ / 100) Y = 100 xy_n = colour.XYZ_to_xy(XYZ_0 / 100) message_box( - ('Computing "whiteness" using "CIE 2004" method for ' - 'given sample "xy" chromaticity coordinates, "Y" tristimulus ' - 'value and "xy_n" chromaticity coordinates of perfect diffuser:\n' - '\n\t{0}\n\t{1}\n\t{2}\n\n' - 'Warning: The input domain of that definition is non standard!'.format( - xy, Y, xy_n))) + f'Computing "whiteness" using "CIE 2004" method for given sample "xy" ' + f'chromaticity coordinates, "Y" tristimulus value and "xy_n" chromaticity ' + f"coordinates of perfect diffuser:\n\n" + f"\t{xy}\n" + f"\t{Y}\n" + f"\t{xy_n}" +) print(colour.whiteness(XYZ, XYZ_0, xy_n=xy_n)) print(colour.colorimetry.whiteness_CIE2004(xy, Y, xy_n)) diff --git a/colour/examples/colorimetry/examples_yellowness.py b/colour/examples/colorimetry/examples_yellowness.py index 8674aa0396..b8a2e66c88 100644 --- a/colour/examples/colorimetry/examples_yellowness.py +++ b/colour/examples/colorimetry/examples_yellowness.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *yellowness* computations. -""" +"""Showcases *yellowness* computations.""" import numpy as np @@ -12,21 +9,24 @@ XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) message_box( - ('Computing "yellowness" using "ASTM D1925" method for ' - 'given sample "CIE XYZ" tristimulus values:\n' - '\n\t{0}\n\n' - 'Warning: The input domain of that definition is non standard!'.format( - XYZ))) -print(colour.yellowness(XYZ=XYZ, method='ASTM D1925')) + f'Computing "yellowness" using "ASTM D1925" method for given sample ' + f'"CIE XYZ" tristimulus values:\n\n\t{XYZ}' +) +print(colour.yellowness(XYZ=XYZ, method="ASTM D1925")) print(colour.colorimetry.yellowness_ASTMD1925(XYZ)) -print('\n') +print("\n") message_box( - ('Computing "yellowness" using "ASTM E313" method for ' - 'given sample "CIE XYZ" tristimulus values:\n' - '\n\t{0}\n\n' - 'Warning: The input domain of that definition is non standard!'.format( - XYZ))) -print(colour.yellowness(XYZ=XYZ, method='ASTM E313')) + f'Computing "yellowness" using "ASTM E313" method for given sample ' + f'"CIE XYZ" tristimulus values:\n\n\t{XYZ}' +) +print(colour.yellowness(XYZ=XYZ, method="ASTM E313")) print(colour.colorimetry.yellowness_ASTME313(XYZ)) + +message_box( + f'Computing "yellowness" using the alternative "ASTM E313" method for ' + f'given sample "CIE XYZ" tristimulus values:\n\n\t{XYZ}' +) +print(colour.yellowness(XYZ=XYZ, method="ASTM E313 Alternative")) +print(colour.colorimetry.yellowness_ASTME313_alternative(XYZ)) diff --git a/colour/examples/contrast/examples_contrast.py b/colour/examples/contrast/examples_contrast.py index 02f0c8a95e..142481d50f 100644 --- a/colour/examples/contrast/examples_contrast.py +++ b/colour/examples/contrast/examples_contrast.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases contrast sensitivity computations. -""" +"""Showcases contrast sensitivity computations.""" from pprint import pprint @@ -9,51 +6,56 @@ from scipy.optimize import fmin import colour +from colour.hints import FloatingOrArrayLike, FloatingOrNDArray from colour.utilities import as_float, message_box from colour.plotting import colour_style, plot_single_function -message_box('Contrast Sensitivity Computations') +message_box("Contrast Sensitivity Computations") colour_style() -message_box(('Computing the contrast sensitivity for a spatial frequency "u" ' - 'of 4, an angular size "X_0" of 60 and a retinal illuminance "E" ' - 'of 65 using "Barten (1999)" method.')) +message_box( + 'Computing the contrast sensitivity for a spatial frequency "u" of 4, an ' + 'angular size "X_0" of 60 and a retinal illuminance "E" of 65 using ' + '"Barten (1999)" method.' +) pprint(colour.contrast_sensitivity_function(u=4, X_0=60, E=65)) pprint( - colour.contrast.contrast_sensitivity_function_Barten1999( - u=4, X_0=60, E=65)) + colour.contrast.contrast_sensitivity_function_Barten1999(u=4, X_0=60, E=65) +) -print('\n') +print("\n") -message_box(('Computing the minimum detectable contrast with the assumed ' - 'conditions for UHDTV applications as given in "ITU-R BT.2246-4"' - '"Figure 31" and using "Barten (1999)" method.')) +message_box( + "Computing the minimum detectable contrast with the assumed conditions " + 'for UHDTV applications as given in the "ITU-R BT.2246-4" "Figure 31" and ' + 'using "Barten (1999)" method.' +) settings_BT2246 = { - 'k': 3.0, - 'T': 0.1, - 'X_max': 12, - 'N_max': 15, - 'n': 0.03, - 'p': 1.2274 * 10 ** 6, - 'phi_0': 3 * 10 ** -8, - 'u_0': 7, + "k": 3.0, + "T": 0.1, + "X_max": 12, + "N_max": 15, + "n": 0.03, + "p": 1.2274 * 10**6, + "phi_0": 3 * 10**-8, + "u_0": 7, } -def maximise_spatial_frequency(L): +def maximise_spatial_frequency(L: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Maximises the spatial frequency :math:`u` for given luminance value. + Maximise the spatial frequency :math:`u` for given luminance value. Parameters ---------- - L : numeric or array_like + L Luminance value at which to maximize the spatial frequency :math:`u`. Returns ------- - numeric or ndarray + :class:`np.floating` or :class:`numpy.ndarray` Maximised spatial frequency :math:`u`. """ @@ -64,14 +66,16 @@ def maximise_spatial_frequency(L): sigma = colour.contrast.sigma_Barten1999(0.5 / 60, 0.08 / 60, d) E = colour.contrast.retinal_illuminance_Barten1999(L_v, d, True) maximised_spatial_frequency.append( - fmin(lambda x: ( + fmin( + lambda x: ( -colour.contrast.contrast_sensitivity_function_Barten1999( - u=x, - sigma=sigma, - X_0=X_0, - E=E, - **settings_BT2246) - ), 0, disp=False)[0]) + u=x, sigma=sigma, X_0=X_0, E=E, **settings_BT2246 + ) + ), + 0, + disp=False, + )[0] + ) return as_float(np.array(maximised_spatial_frequency)) @@ -83,27 +87,38 @@ def maximise_spatial_frequency(L): E = colour.contrast.barten1999.retinal_illuminance_Barten1999(L, d) u = maximise_spatial_frequency(L) -pprint(1 / colour.contrast_sensitivity_function( - u=u, sigma=sigma, E=E, X_0=X_0, Y_0=Y_0, **settings_BT2246) * 2 * - (1 / 1.27)) -pprint(1 / colour.contrast.contrast_sensitivity_function_Barten1999( - u=u, sigma=sigma, E=E, X_0=X_0, Y_0=Y_0, **settings_BT2246) * 2 * - (1 / 1.27)) +pprint( + 1 + / colour.contrast_sensitivity_function( + u=u, sigma=sigma, E=E, X_0=X_0, Y_0=Y_0, **settings_BT2246 + ) + * 2 + * (1 / 1.27) +) +pprint( + 1 + / colour.contrast.contrast_sensitivity_function_Barten1999( + u=u, sigma=sigma, E=E, X_0=X_0, Y_0=Y_0, **settings_BT2246 + ) + * 2 + * (1 / 1.27) +) plot_single_function( lambda x: ( - 1 / colour.contrast.contrast_sensitivity_function_Barten1999( - u=u, sigma=sigma, E=E, X_0=X_0, Y_0=Y_0, **settings_BT2246) - * 2 * (1 / 1.27)), + 1 + / colour.contrast.contrast_sensitivity_function_Barten1999( + u=u, sigma=sigma, E=E, X_0=X_0, Y_0=Y_0, **settings_BT2246 + ) + * 2 + * (1 / 1.27) + ), samples=L, log_x=10, **{ - 'title': - 'Examples of HVS Minimum Detectable Contrast Characteristics', - 'x_label': - 'Luminance ($cd/m^2$)', - 'y_label': - 'Minimum Detectable Contrast', - 'axes.grid.which': - 'both' - }) + "title": "Examples of HVS Minimum Detectable Contrast Characteristics", + "x_label": "Luminance ($cd/m^2$)", + "y_label": "Minimum Detectable Contrast", + "axes.grid.which": "both", + } +) diff --git a/colour/examples/corresponding/examples_prediction.py b/colour/examples/corresponding/examples_prediction.py index 89bec0cd19..88a624ecf6 100644 --- a/colour/examples/corresponding/examples_prediction.py +++ b/colour/examples/corresponding/examples_prediction.py @@ -1,49 +1,59 @@ -# -*- coding: utf-8 -*- -""" -Showcases corresponding chromaticities prediction computations. -""" +"""Showcases corresponding chromaticities prediction computations.""" from pprint import pprint import colour from colour.utilities import message_box -message_box('Corresponding Chromaticities Prediction Computations') +message_box("Corresponding Chromaticities Prediction Computations") -message_box(('Computing corresponding chromaticities prediction with ' - '"Von Kries" chromatic adaptation model for "Breneman (1987)" ' - 'experiment number "3" and "Bianco" CAT.')) +message_box( + 'Computing corresponding chromaticities prediction with "Von Kries" ' + 'chromatic adaptation model for "Breneman (1987)" experiment number "3" ' + 'and "Bianco" CAT.' +) pprint( colour.corresponding_chromaticities_prediction( - 3, model='Von Kries', transform='Bianco 2010')) + 3, model="Von Kries", transform="Bianco 2010" + ) +) pprint( colour.corresponding.corresponding_chromaticities_prediction_VonKries( - 3, 'Bianco 2010')) + 3, "Bianco 2010" + ) +) -print('\n') +print("\n") -message_box(('Computing corresponding chromaticities prediction with ' - '"CIE 1994" chromatic adaptation model for "Breneman (1987)" ' - 'experiment number "1".')) -pprint(colour.corresponding_chromaticities_prediction(3, model='CIE 1994')) +message_box( + 'Computing corresponding chromaticities prediction with "CIE 1994" ' + 'chromatic adaptation model for "Breneman (1987)" experiment number "1".' +) +pprint(colour.corresponding_chromaticities_prediction(3, model="CIE 1994")) pprint(colour.corresponding.corresponding_chromaticities_prediction_CIE1994(1)) -print('\n') +print("\n") -message_box(('Computing corresponding chromaticities prediction with ' - '"CMCCAT2000" chromatic adaptation model for "Breneman (1987)" ' - 'experiment number "1".')) -pprint(colour.corresponding_chromaticities_prediction(3, model='CMCCAT2000')) +message_box( + 'Computing corresponding chromaticities prediction with "CMCCAT2000" ' + 'chromatic adaptation model for "Breneman (1987)" experiment number "1".' +) +pprint(colour.corresponding_chromaticities_prediction(3, model="CMCCAT2000")) pprint( - colour.corresponding.corresponding_chromaticities_prediction_CMCCAT2000(1)) + colour.corresponding.corresponding_chromaticities_prediction_CMCCAT2000(1) +) -print('\n') +print("\n") -message_box(('Computing corresponding chromaticities prediction with ' - '"Fairchild (1990)" chromatic adaptation model for ' - '"Breneman (1987)" experiment number "1".')) +message_box( + 'Computing corresponding chromaticities prediction with Fairchild (1990)" ' + 'chromatic adaptation model for "Breneman (1987)" experiment number "1".' +) pprint( - colour.corresponding_chromaticities_prediction(3, model='Fairchild 1990')) + colour.corresponding_chromaticities_prediction(3, model="Fairchild 1990") +) pprint( colour.corresponding.corresponding_chromaticities_prediction_Fairchild1990( - 1)) + 1 + ) +) diff --git a/colour/examples/difference/examples_delta_e.py b/colour/examples/difference/examples_delta_e.py index 04002f5890..206e79ea2a 100644 --- a/colour/examples/difference/examples_delta_e.py +++ b/colour/examples/difference/examples_delta_e.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *Delta E* colour difference computations. -""" +"""Showcases *Delta E* colour difference computations.""" import numpy as np @@ -12,93 +9,125 @@ Lab_1 = np.array([100.00000000, 21.57210357, 272.22819350]) Lab_2 = np.array([100.00000000, 426.67945353, 72.39590835]) -message_box(('Computing "Delta E" with "CIE 1976" method from given ' - '"CIE L*a*b*" colourspace matrices:\n' - '\n\t{0}\n\t{1}'.format(Lab_1, Lab_2))) -print(colour.delta_E(Lab_1, Lab_2, method='CIE 1976')) +message_box( + f'Computing "Delta E" with "CIE 1976" method from given "CIE L*a*b*" ' + f"colourspace matrices:\n\n" + f"\t{Lab_1}\n" + f"\t{Lab_2}" +) +print(colour.delta_E(Lab_1, Lab_2, method="CIE 1976")) print(colour.difference.delta_E_CIE1976(Lab_1, Lab_2)) -print('\n') +print("\n") -message_box(('Computing "Delta E" with "CIE 1994" method from given ' - '"CIE L*a*b*" colourspace matrices:\n' - '\n\t{0}\n\t{1}'.format(Lab_1, Lab_2))) -print(colour.delta_E(Lab_1, Lab_2, method='CIE 1994')) +message_box( + f'Computing "Delta E" with "CIE 1994" method from given "CIE L*a*b*" ' + f"colourspace matrices:\n\n" + f"\t{Lab_1}\n" + f"\t{Lab_2}" +) +print(colour.delta_E(Lab_1, Lab_2, method="CIE 1994")) print(colour.difference.delta_E_CIE1994(Lab_1, Lab_2)) -print('\n') +print("\n") -message_box(('Computing "Delta E" with "CIE 1994" method from given ' - '"CIE L*a*b*" colourspace matrices for "graphics arts" ' - 'applications:\n' - '\n\t{0}\n\t{1}'.format(Lab_1, Lab_2))) -print(colour.delta_E(Lab_1, Lab_2, method='CIE 1994', textiles=False)) +message_box( + f'Computing "Delta E" with "CIE 1994" method from given "CIE L*a*b*" ' + f'colourspace matrices for "graphics arts" applications:\n\n' + f"\t{Lab_1}\n" + f"\t{Lab_2}" +) +print(colour.delta_E(Lab_1, Lab_2, method="CIE 1994", textiles=False)) print(colour.difference.delta_E_CIE1994(Lab_1, Lab_2, textiles=False)) -print('\n') +print("\n") -message_box(('Computing "Delta E" with "CIE 2000" method from given ' - '"CIE L*a*b*" colourspace matrices:\n' - '\n\t{0}\n\t{1}'.format(Lab_1, Lab_2))) -print(colour.delta_E(Lab_1, Lab_2, method='CIE 2000')) +message_box( + f'Computing "Delta E" with "CIE 2000" method from given "CIE L*a*b*" ' + f"colourspace matrices:\n\n" + f"\t{Lab_1}\n" + f"\t{Lab_2}" +) +print(colour.delta_E(Lab_1, Lab_2, method="CIE 2000")) print(colour.difference.delta_E_CIE2000(Lab_1, Lab_2)) -print('\n') +print("\n") -message_box(('Computing "Delta E" with "CMC" method from given "CIE L*a*b*" ' - 'colourspace matrices:\n' - '\n\t{0}\n\t{1}'.format(Lab_1, Lab_2))) -print(colour.delta_E(Lab_1, Lab_2, method='CMC')) +message_box( + f'Computing "Delta E" with "CMC" method from given "CIE L*a*b*" ' + f"colourspace matrices:\n\n" + f"\t{Lab_1}\n" + f"\t{Lab_2}" +) +print(colour.delta_E(Lab_1, Lab_2, method="CMC")) print(colour.difference.delta_E_CMC(Lab_1, Lab_2)) -print('\n') +print("\n") -message_box(('Computing "Delta E" with "CMC" method from given "CIE L*a*b*" ' - 'colourspace matrices with imperceptibility threshold:\n' - '\n\t{0}\n\t{1}'.format(Lab_1, Lab_2))) -print(colour.delta_E(Lab_1, Lab_2, method='CMC', l=1)) # noqa +message_box( + f'Computing "Delta E" with "CMC" method from given "CIE L*a*b*" ' + f"colourspace matrices with imperceptibility threshold:\n\n" + f"\t{Lab_1}\n" + f"\t{Lab_2}" +) +print(colour.delta_E(Lab_1, Lab_2, method="CMC", l=1)) # noqa print(colour.difference.delta_E_CMC(Lab_1, Lab_2, l=1)) # noqa -print('\n') +print("\n") -message_box(('Computing "Delta E" with "DIN99" method from given ' - '"CIE L*a*b*" colourspace matrices:\n' - '\n\t{0}\n\t{1}'.format(Lab_1, Lab_2))) -print(colour.delta_E(Lab_1, Lab_2, method='DIN99')) +message_box( + f'Computing "Delta E" with "DIN99" method from given "CIE L*a*b*" ' + f"colourspace matrices:\n\n" + f"\t{Lab_1}\n" + f"\t{Lab_2}" +) +print(colour.delta_E(Lab_1, Lab_2, method="DIN99")) print(colour.difference.delta_E_DIN99(Lab_1, Lab_2)) -print('\n') +print("\n") Jpapbp_1 = np.array([54.90433134, -0.08450395, -0.06854831]) Jpapbp_2 = np.array([54.80352754, -3.96940084, -13.57591013]) -message_box(('Computing "Delta E" with "Luo et al. (2006)" "CAM02-LCD" method ' - 'from given "J\'a\'b\'" arrays:\n' - '\n\t{0}\n\t{1}'.format(Jpapbp_1, Jpapbp_2))) -print(colour.delta_E(Jpapbp_1, Jpapbp_2, method='CAM02-LCD')) +message_box( + f'Computing "Delta E" with "Luo et al. (2006)" "CAM02-LCD" method from ' + f"given \"J'a'b'\" arrays:\n\n" + f"\t{Jpapbp_1}\n" + f"\t{Jpapbp_2}" +) +print(colour.delta_E(Jpapbp_1, Jpapbp_2, method="CAM02-LCD")) print(colour.difference.delta_E_CAM02LCD(Jpapbp_1, Jpapbp_2)) -print('\n') +print("\n") -message_box(('Computing "Delta E" with "Luo et al. (2006)" "CAM02-SCD" method ' - 'from given "J\'a\'b\'" arrays:\n' - '\n\t{0}\n\t{1}'.format(Jpapbp_1, Jpapbp_2))) -print(colour.delta_E(Jpapbp_1, Jpapbp_2, method='CAM02-SCD')) +message_box( + f'Computing "Delta E" with "Luo et al. (2006)" "CAM02-SCD" method from ' + f"given \"J'a'b'\" arrays:\n\n" + f"\t{Jpapbp_1}\n" + f"\t{Jpapbp_2}" +) +print(colour.delta_E(Jpapbp_1, Jpapbp_2, method="CAM02-SCD")) print(colour.difference.delta_E_CAM02SCD(Jpapbp_1, Jpapbp_2)) -print('\n') +print("\n") Jpapbp_1 = np.array([54.89102616, -9.42910274, -5.52845976]) Jpapbp_2 = np.array([54.81983401, -13.21630207, -4.15161146]) -message_box(('Computing "Delta E" with "Li et al. (2016)" "CAM02-LCD" method ' - 'from given "J\'a\'b\'" arrays:\n' - '\n\t{0}\n\t{1}'.format(Jpapbp_1, Jpapbp_2))) -print(colour.delta_E(Jpapbp_1, Jpapbp_2, method='CAM16-LCD')) +message_box( + f'Computing "Delta E" with "Li et al. (2016)" "CAM02-LCD" method from ' + f"given \"J'a'b'\" arrays:\n\n" + f"\t{Jpapbp_1}\n" + f"\t{Jpapbp_2}" +) +print(colour.delta_E(Jpapbp_1, Jpapbp_2, method="CAM16-LCD")) print(colour.difference.delta_E_CAM16LCD(Jpapbp_1, Jpapbp_2)) -print('\n') +print("\n") -message_box(('Computing "Delta E" with "Li et al. (2016)" "CAM16-SCD" method ' - 'from given "J\'a\'b\'" arrays:\n' - '\n\t{0}\n\t{1}'.format(Jpapbp_1, Jpapbp_2))) -print(colour.delta_E(Jpapbp_1, Jpapbp_2, method='CAM16-SCD')) +message_box( + f'Computing "Delta E" with "Li et al. (2016)" "CAM16-SCD" method from ' + f"given \"J'a'b'\" arrays:\n\n" + f"\t{Jpapbp_1}\n" + f"\t{Jpapbp_2}" +) +print(colour.delta_E(Jpapbp_1, Jpapbp_2, method="CAM16-SCD")) print(colour.difference.delta_E_CAM16SCD(Jpapbp_1, Jpapbp_2)) diff --git a/colour/examples/examples_colour.py b/colour/examples/examples_colour.py index e1bb0b5660..cea108f979 100644 --- a/colour/examples/examples_colour.py +++ b/colour/examples/examples_colour.py @@ -1,184 +1,210 @@ -# -*- coding: utf-8 -*- -""" -Showcases overall *Colour* examples. -""" +"""Showcases overall *Colour* examples.""" import numpy as np import warnings import colour -from colour.utilities import (filter_warnings, message_box, warning, - runtime_warning, usage_warning) +from colour.utilities import ( + filter_warnings, + message_box, + warning, + runtime_warning, + usage_warning, +) -message_box('Automatic Colour Conversion Graph') +message_box("Automatic Colour Conversion Graph") -message_box('Starting with version "0.3.14", "Colour" implements an automatic ' - 'colour conversion graph enabling easier colour conversions.') +message_box( + 'Starting with version "0.3.14", "Colour" implements an automatic colour ' + "conversion graph enabling easier colour conversions." +) -message_box('Converting a "ColorChecker" "dark skin" sample spectral ' - 'distribution to "Output-Referred" "sRGB" colourspace.') +message_box( + 'Converting a "ColorChecker" "dark skin" sample spectral distribution to ' + '"Output-Referred" "sRGB" colourspace.' +) -sd = colour.SDS_COLOURCHECKERS['ColorChecker N Ohta']['dark skin'] +sd = colour.SDS_COLOURCHECKERS["ColorChecker N Ohta"]["dark skin"] -print(colour.convert(sd, 'Spectral Distribution', 'sRGB')) +print(colour.convert(sd, "Spectral Distribution", "sRGB")) -print('\n') +print("\n") RGB = np.array([0.45675795, 0.30986982, 0.24861924]) -message_box(('Converting to "CAM16-UCS" colourspace from given ' - '"Output-Referred" "sRGB" colourspace values:\n' - '\n\t{0}'.format(RGB))) -print(colour.convert(RGB, 'Output-Referred RGB', 'CAM16UCS')) +message_box( + f'Converting to the "CAM16-UCS" colourspace from given "Output-Referred" ' + f'"sRGB" colourspace values:\n\n\t{RGB}' +) +print(colour.convert(RGB, "Output-Referred RGB", "CAM16UCS")) -print('\n') +print("\n") Jpapbp = np.array([0.39994811, 0.09206558, 0.0812752]) -message_box(('Converting to "Output-Referred" "sRGB" colourspace from given ' - '"CAM16-UCS" colourspace colourspace values:\n' - '\n\t{0}'.format(RGB))) -print(colour.convert(Jpapbp, 'CAM16UCS', 'sRGB')) +message_box( + f'Converting to the "Output-Referred" "sRGB" colourspace from given ' + f'"CAM16-UCS" colourspace colourspace values:\n\n\t{RGB}' +) +print(colour.convert(Jpapbp, "CAM16UCS", "sRGB")) -print('\n') +print("\n") message_box('Filter "Colour" Warnings') -warning('This is a first warning and it can be filtered!') +warning("This is a first warning and it can be filtered!") filter_warnings() -warning('This is a second warning and it has been filtered!') +warning("This is a second warning and it has been filtered!") filter_warnings(False) -warning('This is a third warning and it has not been filtered!') +warning("This is a third warning and it has not been filtered!") -message_box('All Python can be filtered by setting the ' - '"colour.utilities.filter_warnings" definition "python_warnings" ' - 'argument.') +message_box( + "All Python can be filtered by setting the " + '"colour.utilities.filter_warnings" definition "python_warnings" ' + "argument." +) -warnings.warn('This is a fourth warning and it has not been filtered!') +warnings.warn("This is a fourth warning and it has not been filtered!") filter_warnings(python_warnings=False) -warning('This is a fifth warning and it has been filtered!') +warning("This is a fifth warning and it has been filtered!") filter_warnings(False, python_warnings=False) -warning('This is a sixth warning and it has not been filtered!') +warning("This is a sixth warning and it has not been filtered!") filter_warnings(False, python_warnings=False) filter_warnings(colour_warnings=False, colour_runtime_warnings=True) -runtime_warning('This is a first runtime warning and it has been filtered!') +runtime_warning("This is a first runtime warning and it has been filtered!") filter_warnings(colour_warnings=False, colour_usage_warnings=True) -usage_warning('This is a first usage warning and it has been filtered!') +usage_warning("This is a first usage warning and it has been filtered!") -print('\n') +print("\n") message_box('Overall "Colour" Examples') -message_box('N-Dimensional Arrays Support') +message_box("N-Dimensional Arrays Support") XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) illuminant = np.array([0.31270, 0.32900]) -message_box('Using 1d "array_like" parameter:\n' '\n{0}'.format(XYZ)) +message_box(f'Using 1d "ArrayLike" parameter:\n\n{XYZ}') print(colour.XYZ_to_Lab(XYZ, illuminant=illuminant)) -print('\n') +print("\n") XYZ = np.tile(XYZ, (6, 1)) illuminant = np.tile(illuminant, (6, 1)) -message_box('Using 2d "array_like" parameter:\n' '\n{0}'.format(XYZ)) +message_box(f'Using 2d "ArrayLike" parameter:\n\n{XYZ}') print(colour.XYZ_to_Lab(XYZ, illuminant=illuminant)) -print('\n') +print("\n") XYZ = np.reshape(XYZ, (2, 3, 3)) illuminant = np.reshape(illuminant, (2, 3, 2)) -message_box('Using 3d "array_like" parameter:\n' '\n{0}'.format(XYZ)) +message_box(f'Using 3d "ArrayLike" parameter:\n\n{XYZ}') print(colour.XYZ_to_Lab(XYZ, illuminant=illuminant)) -print('\n') +print("\n") XYZ = np.reshape(XYZ, (3, 2, 1, 3)) illuminant = np.reshape(illuminant, (3, 2, 1, 2)) -message_box('Using 4d "array_like" parameter:\n' '\n{0}'.format(XYZ)) +message_box(f'Using 4d "ArrayLike" parameter:\n\n{XYZ}') print(colour.XYZ_to_Lab(XYZ, illuminant=illuminant)) -print('\n') +print("\n") xy = np.tile((0.31270, 0.32900), (6, 1)) -message_box(('Definitions return value may lose a dimension with respect to ' - 'the parameter(s):\n' - '\n{0}'.format(xy))) +message_box( + f"Definitions return value may lose a dimension with respect to the " + f"parameter(s):\n\n{xy}" +) print(colour.xy_to_CCT(xy)) -print('\n') +print("\n") CCT = np.tile(6504.38938305, 6) -message_box(('Definitions return value may gain a dimension with respect to ' - 'the parameter(s):\n' - '\n{0}'.format(CCT))) +message_box( + f"Definitions return value may gain a dimension with respect to the " + f"parameter(s):\n\n{CCT}" +) print(colour.CCT_to_xy(CCT)) -print('\n') +print("\n") -message_box(('Definitions mixing "array_like" and "numeric" parameters ' - 'expect the "numeric" parameters to have a dimension less than ' - 'the "array_like" parameters.')) +message_box( + 'Definitions mixing "ArrayLike" and "Number" parameters expect the ' + '"Number" parameters to have a dimension less than the "ArrayLike" ' + "parameters." +) XYZ_1 = np.array([28.00, 21.26, 5.27]) xy_o1 = np.array([0.4476, 0.4074]) xy_o2 = np.array([0.3127, 0.3290]) Y_o = 20 E_o1 = 1000 E_o2 = 1000 -message_box(('Parameters:\n\n' - 'XYZ_1:\n\n{0}\n\n' - '\nxy_o1:\n\n{1}\n\n' - '\nxy_o2:\n\n{2}\n\n' - '\nY_o:\n\n{3}\n\n' - '\nE_o1:\n\n{4}\n\n' - '\nE_o2:\n\n{5}'.format(XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2))) +message_box( + f"Parameters:\n\n" + f"\tXYZ_1: {XYZ_1}\n" + f"\txy_o1: {xy_o1}\n" + f"\txy_o2: {xy_o2}\n" + f"\tY_o: {Y_o}\n" + f"\tE_o1: {E_o1}\n" + f"\tE_o2: {E_o2}" +) print( - colour.adaptation.chromatic_adaptation_CIE1994(XYZ_1, xy_o1, xy_o2, Y_o, - E_o1, E_o2)) + colour.adaptation.chromatic_adaptation_CIE1994( + XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2 + ) +) -print('\n') +print("\n") XYZ_1 = np.tile(XYZ_1, (6, 1)) -message_box(('Parameters:\n\n' - 'XYZ_1:\n\n{0}\n\n' - '\nxy_o1:\n\n{1}\n\n' - '\nxy_o2:\n\n{2}\n\n' - '\nY_o:\n\n{3}\n\n' - '\nE_o1:\n\n{4}\n\n' - '\nE_o2:\n\n{5}'.format(XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2))) +message_box( + f"Parameters:\n\n" + f"\tXYZ_1: {XYZ_1}\n" + f"\txy_o1: {xy_o1}\n" + f"\txy_o2: {xy_o2}\n" + f"\tY_o: {Y_o}\n" + f"\tE_o1: {E_o1}\n" + f"\tE_o2: {E_o2}" +) print( - colour.adaptation.chromatic_adaptation_CIE1994(XYZ_1, xy_o1, xy_o2, Y_o, - E_o1, E_o2)) + colour.adaptation.chromatic_adaptation_CIE1994( + XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2 + ) +) -print('\n') +print("\n") xy_o1 = np.tile(xy_o1, (6, 1)) xy_o2 = np.tile(xy_o2, (6, 1)) Y_o = np.tile(Y_o, 6) E_o1 = np.tile(E_o1, 6) E_o2 = np.tile(E_o2, 6) -message_box(('Parameters:\n\n' - 'XYZ_1:\n\n{0}\n\n' - '\nxy_o1:\n\n{1}\n\n' - '\nxy_o2:\n\n{2}\n\n' - '\nY_o:\n\n{3}\n\n' - '\nE_o1:\n\n{4}\n\n' - '\nE_o2:\n\n{5}'.format(XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2))) +message_box( + f"Parameters:\n\n" + f"\tXYZ_1: {XYZ_1}\n" + f"\txy_o1: {xy_o1}\n" + f"\txy_o2: {xy_o2}\n" + f"\tY_o: {Y_o}\n" + f"\tE_o1: {E_o1}\n" + f"\tE_o2: {E_o2}" +) print( - colour.adaptation.chromatic_adaptation_CIE1994(XYZ_1, xy_o1, xy_o2, Y_o, - E_o1, E_o2)) + colour.adaptation.chromatic_adaptation_CIE1994( + XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2 + ) +) -print('\n') +print("\n") XYZ_1 = np.reshape(XYZ_1, (2, 3, 3)) xy_o1 = np.reshape(xy_o1, (2, 3, 2)) @@ -186,36 +212,42 @@ Y_o = np.reshape(Y_o, (2, 3)) E_o1 = np.reshape(E_o1, (2, 3)) E_o2 = np.reshape(E_o2, (2, 3)) -message_box(('Parameters:\n\n' - 'XYZ_1:\n\n{0}\n\n' - '\nxy_o1:\n\n{1}\n\n' - '\nxy_o2:\n\n{2}\n\n' - '\nY_o:\n\n{3}\n\n' - '\nE_o1:\n\n{4}\n\n' - '\nE_o2:\n\n{5}'.format(XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2))) +message_box( + f"Parameters:\n\n" + f"\tXYZ_1: {XYZ_1}\n" + f"\txy_o1: {xy_o1}\n" + f"\txy_o2: {xy_o2}\n" + f"\tY_o: {Y_o}\n" + f"\tE_o1: {E_o1}\n" + f"\tE_o2: {E_o2}" +) print( - colour.adaptation.chromatic_adaptation_CIE1994(XYZ_1, xy_o1, xy_o2, Y_o, - E_o1, E_o2)) + colour.adaptation.chromatic_adaptation_CIE1994( + XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2 + ) +) -print('\n') +print("\n") -message_box('Domain-Range Scales') +message_box("Domain-Range Scales") -message_box(('"Colour" uses two different domain-range scales: \n\n' - '- "Reference"\n' - '- "1"')) +message_box( + '"Colour" uses two different domain-range scales: \n\n' + '- "Reference"\n' + '- "1"' +) -print('\n') +print("\n") -message_box('Printing the current domain-range scale:') +message_box("Printing the current domain-range scale:") print(colour.get_domain_range_scale()) -print('\n') +print("\n") message_box('Setting the current domain-range scale to "1":') -colour.set_domain_range_scale('1') +colour.set_domain_range_scale("1") XYZ_1 = np.array([0.2800, 0.2126, 0.0527]) xy_o1 = np.array([0.4476, 0.4074]) @@ -223,13 +255,17 @@ Y_o = 0.2 E_o1 = 1000 E_o2 = 1000 -message_box(('Parameters:\n\n' - 'XYZ_1:\n\n{0}\n\n' - '\nxy_o1:\n\n{1}\n\n' - '\nxy_o2:\n\n{2}\n\n' - '\nY_o:\n\n{3}\n\n' - '\nE_o1:\n\n{4}\n\n' - '\nE_o2:\n\n{5}'.format(XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2))) +message_box( + f"Parameters:\n\n" + f"\tXYZ_1: {XYZ_1}\n" + f"\txy_o1: {xy_o1}\n" + f"\txy_o2: {xy_o2}\n" + f"\tY_o: {Y_o}\n" + f"\tE_o1: {E_o1}\n" + f"\tE_o2: {E_o2}" +) print( - colour.adaptation.chromatic_adaptation_CIE1994(XYZ_1, xy_o1, xy_o2, Y_o, - E_o1, E_o2)) + colour.adaptation.chromatic_adaptation_CIE1994( + XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2 + ) +) diff --git a/colour/examples/geometry/examples_geometry.py b/colour/examples/geometry/examples_geometry.py index 4b2bcdccc2..353bb8d589 100644 --- a/colour/examples/geometry/examples_geometry.py +++ b/colour/examples/geometry/examples_geometry.py @@ -1,40 +1,43 @@ -# -*- coding: utf-8 -*- -""" -Showcases geometry primitives generation examples. -""" +"""Showcases geometry primitives generation examples.""" import numpy as np import colour from colour.utilities import message_box -message_box('Geometry Primitives Generation') +message_box("Geometry Primitives Generation") message_box('Generating a "grid":') print( colour.primitive( - 'Grid', + "Grid", width=0.2, height=0.4, width_segments=1, height_segments=2, - axis='+z')) + axis="+z", + ) +) print( colour.geometry.primitive_grid( - width=0.2, height=0.4, width_segments=1, height_segments=2, axis='+z')) + width=0.2, height=0.4, width_segments=1, height_segments=2, axis="+z" + ) +) -print('\n') +print("\n") message_box('Generating a "cube":') print( colour.primitive( - 'Cube', + "Cube", width=0.2, height=0.4, depth=0.6, width_segments=1, height_segments=2, - depth_segments=3)) + depth_segments=3, + ) +) print( colour.geometry.primitive_cube( width=0.2, @@ -42,38 +45,46 @@ depth=0.6, width_segments=1, height_segments=2, - depth_segments=3)) + depth_segments=3, + ) +) message_box('Generating the vertices of a "quad" for "Matplotlib":') print( colour.primitive_vertices( - 'Quad MPL', + "Quad MPL", width=0.2, height=0.4, depth=0.6, origin=np.array([0.2, 0.4]), - axis='+z')) + axis="+z", + ) +) print( colour.geometry.primitive_vertices_quad_mpl( width=0.2, height=0.4, depth=0.6, origin=np.array([0.2, 0.4]), - axis='+z')) + axis="+z", + ) +) -print('\n') +print("\n") message_box('Generating the vertices of a "grid" for "Matplotlib":') print( colour.primitive_vertices( - 'Grid MPL', + "Grid MPL", width=0.2, height=0.4, depth=0.6, width_segments=1, height_segments=2, origin=np.array([0.2, 0.4]), - axis='+z')) + axis="+z", + ) +) print( colour.geometry.primitive_vertices_grid_mpl( width=0.2, @@ -82,21 +93,25 @@ width_segments=1, height_segments=2, origin=np.array([0.2, 0.4]), - axis='+z')) + axis="+z", + ) +) -print('\n') +print("\n") message_box('Generating the vertices of a "cube" for "Matplotlib":') print( colour.primitive_vertices( - 'Cube MPL', + "Cube MPL", width=0.2, height=0.4, depth=0.6, width_segments=1, height_segments=2, depth_segments=3, - origin=np.array([0.2, 0.4, 0.6]))) + origin=np.array([0.2, 0.4, 0.6]), + ) +) print( colour.geometry.primitive_vertices_cube_mpl( width=0.2, @@ -105,19 +120,24 @@ width_segments=1, height_segments=2, depth_segments=3, - origin=np.array([0.2, 0.4, 0.6]))) + origin=np.array([0.2, 0.4, 0.6]), + ) +) -print('\n') +print("\n") message_box('Generating the vertices of a "sphere":') print( colour.primitive_vertices( - 'Sphere', + "Sphere", radius=100, segments=6, origin=np.array([-0.2, -0.4, -0.6]), - axis='+x')) + axis="+x", + ) +) print( colour.geometry.primitive_vertices_sphere( - radius=100, segments=6, origin=np.array([-0.2, -0.4, -0.6]), - axis='+x')) + radius=100, segments=6, origin=np.array([-0.2, -0.4, -0.6]), axis="+x" + ) +) diff --git a/colour/examples/graph/examples_graph.py b/colour/examples/graph/examples_graph.py index e3d9f8b9cd..d7ff10ac08 100644 --- a/colour/examples/graph/examples_graph.py +++ b/colour/examples/graph/examples_graph.py @@ -1,62 +1,73 @@ -# -*- coding: utf-8 -*- -""" -Showcases *Automatic Colour Conversion Graph* computations. -""" +"""Showcases *Automatic Colour Conversion Graph* computations.""" import numpy as np import colour from colour.utilities import message_box -message_box('Automatic Colour Conversion Graph') +message_box("Automatic Colour Conversion Graph") -message_box('Converting a "ColorChecker" "dark skin" sample spectral ' - 'distribution to "Output-Referred" "sRGB" colourspace.') +message_box( + 'Converting a "ColorChecker" "dark skin" sample spectral distribution to ' + '"Output-Referred" "sRGB" colourspace.' +) -sd_dark_skin = colour.SDS_COLOURCHECKERS['ColorChecker N Ohta']['dark skin'] -print(colour.convert(sd_dark_skin, 'Spectral Distribution', 'sRGB')) +sd_dark_skin = colour.SDS_COLOURCHECKERS["ColorChecker N Ohta"]["dark skin"] +print(colour.convert(sd_dark_skin, "Spectral Distribution", "sRGB")) print( colour.XYZ_to_sRGB( colour.sd_to_XYZ( - sd_dark_skin, illuminant=colour.SDS_ILLUMINANTS['D65']) / 100)) + sd_dark_skin, illuminant=colour.SDS_ILLUMINANTS["D65"] + ) + / 100 + ) +) -print('\n') +print("\n") RGB = np.array([0.45675795, 0.30986982, 0.24861924]) -message_box(('Converting to "CAM16-UCS" colourspace from given ' - '"Output-Referred" "sRGB" colourspace values:\n' - '\n\t{0}'.format(RGB))) -print(colour.convert(RGB, 'Output-Referred RGB', 'CAM16UCS')) +message_box( + f'Converting to the "CAM16-UCS" colourspace from given "Output-Referred" ' + f'"sRGB" colourspace values:\n\n\t{RGB}' +) +print(colour.convert(RGB, "Output-Referred RGB", "CAM16UCS")) specification = colour.XYZ_to_CAM16( colour.sRGB_to_XYZ(RGB) * 100, XYZ_w=colour.xy_to_XYZ( - colour.CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']) * - 100, + colour.CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"] + ) + * 100, L_A=64 / np.pi * 0.2, - Y_b=20) + Y_b=20, +) print( colour.JMh_CAM16_to_CAM16UCS( - colour.utilities.tstack([ - specification.J, - specification.M, - specification.h, - ])) / 100) + colour.utilities.tstack( + [ + specification.J, + specification.M, + specification.h, + ] + ) + ) + / 100 +) -print('\n') +print("\n") Jpapbp = np.array([0.39994811, 0.09206558, 0.0812752]) -message_box(('Converting to "Output-Referred" "sRGB" colourspace from given ' - '"CAM16-UCS" colourspace colourspace values:\n' - '\n\t{0}'.format(RGB))) +message_box( + f'Converting to the "Output-Referred" "sRGB" colourspace from given ' + f'"CAM16-UCS" colourspace colourspace values:\n\n\t{RGB}' +) print( colour.convert( Jpapbp, - 'CAM16UCS', - 'sRGB', - verbose_kwargs={ - 'describe': 'Extended', - 'width': 75 - })) + "CAM16UCS", + "sRGB", + verbose_kwargs={"describe": "Extended", "width": 75}, + ) +) J, M, h = colour.utilities.tsplit(colour.CAM16UCS_to_JMh_CAM16(Jpapbp * 100)) specification = colour.CAM_Specification_CAM16(J=J, M=M, h=h) print( @@ -64,7 +75,14 @@ colour.CAM16_to_XYZ( specification, XYZ_w=colour.xy_to_XYZ( - colour.CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'] - ['D65']) * 100, + colour.CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][ + "D65" + ] + ) + * 100, L_A=64 / np.pi * 0.2, - Y_b=20) / 100)) + Y_b=20, + ) + / 100 + ) +) diff --git a/colour/examples/io/examples_ies_tm2714.py b/colour/examples/io/examples_ies_tm2714.py index 6379eba744..e487b3d2e5 100644 --- a/colour/examples/io/examples_ies_tm2714.py +++ b/colour/examples/io/examples_ies_tm2714.py @@ -1,51 +1,45 @@ -# -*- coding: utf-8 -*- -""" -Showcases input / output *IES TM-27-14* spectral data XML files related -examples. -""" +"""Showcases *IES TM-27-14* spectral data *XML* files input / output examples.""" import os import colour from colour.utilities import message_box -RESOURCES_DIRECTORY = os.path.join(os.path.dirname(__file__), 'resources') +RESOURCES_DIRECTORY = os.path.join(os.path.dirname(__file__), "resources") message_box('"IES TM-27-14" Spectral Data "XML" File IO') message_box('Reading spectral data from "IES TM-27-14" "XML" file.') sd_tm2714 = colour.SpectralDistribution_IESTM2714( - os.path.join(RESOURCES_DIRECTORY, - 'TM27 Sample Spectral Data.spdx')).read() + os.path.join(RESOURCES_DIRECTORY, "TM27 Sample Spectral Data.spdx") +).read() print(sd_tm2714) -print('\n') +print("\n") message_box('"IES TM-27-14" spectral data "XML" file header:') -print('Manufacturer: {0}'.format(sd_tm2714.header.manufacturer)) -print('Catalog Number: {0}'.format(sd_tm2714.header.catalog_number)) -print('Description: {0}'.format(sd_tm2714.header.description)) -print('Document Creator: {0}'.format(sd_tm2714.header.document_creator)) -print('Unique Identifier: {0}'.format(sd_tm2714.header.unique_identifier)) -print('Measurement Equipment: {0}'.format( - sd_tm2714.header.measurement_equipment)) -print('Laboratory: {0}'.format(sd_tm2714.header.laboratory)) -print('Report Number: {0}'.format(sd_tm2714.header.report_number)) -print('Report Date: {0}'.format(sd_tm2714.header.report_date)) -print('Document Creation Date: {0}'.format( - sd_tm2714.header.document_creation_date)) -print('Comments: {0}'.format(sd_tm2714.header.comments)) - -print('\n') +print(f"Manufacturer: {sd_tm2714.header.manufacturer}") +print(f"Catalog Number: {sd_tm2714.header.catalog_number}") +print(f"Description: {sd_tm2714.header.description}") +print(f"Document Creator: {sd_tm2714.header.document_creator}") +print(f"Unique Identifier: {sd_tm2714.header.unique_identifier}") +print(f"Measurement Equipment: {sd_tm2714.header.measurement_equipment}") +print(f"Laboratory: {sd_tm2714.header.laboratory}") +print(f"Report Number: {sd_tm2714.header.report_number}") +print(f"Report Date: {sd_tm2714.header.report_date}") +print(f"Document Creation Date: {sd_tm2714.header.document_creation_date}") +print(f"Comments: {sd_tm2714.header.comments}") + +print("\n") message_box('"IES TM-27-14" spectral data "XML" file spectral distribution:') -print('Spectral Quantity: {0}'.format(sd_tm2714.spectral_quantity)) -print('Reflection Geometry: {0}'.format(sd_tm2714.reflection_geometry)) -print('Transmission Geometry: {0}'.format(sd_tm2714.transmission_geometry)) -print('Bandwidth FWHM: {0}'.format(sd_tm2714.bandwidth_FWHM)) -print('Bandwidth Corrected: {0}'.format(sd_tm2714.bandwidth_corrected)) +print(f"Spectral Quantity: {sd_tm2714.spectral_quantity}") +print(f"Reflection Geometry: {sd_tm2714.reflection_geometry}") +print(f"Transmission Geometry: {sd_tm2714.transmission_geometry}") +print(f"Bandwidth FWHM: {sd_tm2714.bandwidth_FWHM}") +print(f"Bandwidth Corrected: {sd_tm2714.bandwidth_corrected}") -print('\n') +print("\n") message_box('"IES TM-27-14" spectral data "XML" file spectral data:') print(sd_tm2714) diff --git a/colour/examples/io/examples_luts.py b/colour/examples/io/examples_luts.py index f393190575..816e342d4a 100644 --- a/colour/examples/io/examples_luts.py +++ b/colour/examples/io/examples_luts.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases Look Up Table (LUT) data related examples. -""" +"""Showcases Look Up Table (LUT) data related examples.""" import numpy as np import os @@ -10,61 +7,96 @@ from colour.utilities import message_box RESOURCES_DIRECTORY = os.path.join( - os.path.dirname(__file__), '..', '..', 'io', 'luts', 'tests', 'resources') + os.path.dirname(__file__), "..", "..", "io", "luts", "tests", "resources" +) -message_box('Look Up Table (LUT) Data') +message_box("Look Up Table (LUT) Data") -message_box('Reading "Iridas" ".cube" 3x1D LUT file.') -path = os.path.join(RESOURCES_DIRECTORY, 'iridas_cube', - 'ACES_Proxy_10_to_ACES.cube') +message_box('Reading a "Cinespace" ".csp" 3D LUT with Shaper file.') +path = os.path.join( + RESOURCES_DIRECTORY, "cinespace", "Three_Dimensional_Table_With_Shaper.csp" +) +print(colour.io.read_LUT_Cinespace(path)) +print("\n") +print(colour.read_LUT(path)) + +print("\n") + +message_box('Reading an "Iridas" ".cube" 3x1D LUT file.') +path = os.path.join( + RESOURCES_DIRECTORY, "iridas_cube", "ACES_Proxy_10_to_ACES.cube" +) print(colour.io.read_LUT_IridasCube(path)) -print('\n') +print("\n") print(colour.read_LUT(path)) -print('\n') +print("\n") -message_box('Reading "Iridas" ".cube" 3D LUT file.') -path = os.path.join(RESOURCES_DIRECTORY, 'iridas_cube', 'Colour_Correct.cube') +message_box('Reading an "Iridas" ".cube" 3D LUT file.') +path = os.path.join(RESOURCES_DIRECTORY, "iridas_cube", "Colour_Correct.cube") print(colour.io.read_LUT_IridasCube(path)) -print('\n') +print("\n") print(colour.read_LUT(path)) -print('\n') +print("\n") -message_box('Reading "Sony" ".spi1d" 1D LUT file.') -path = os.path.join(RESOURCES_DIRECTORY, 'sony_spi1d', 'eotf_sRGB_1D.spi1d') +message_box('Reading a "Sony" ".spi1d" 1D LUT file.') +path = os.path.join(RESOURCES_DIRECTORY, "sony_spi1d", "eotf_sRGB_1D.spi1d") print(colour.io.read_LUT_SonySPI1D(path)) -print('\n') +print("\n") print(colour.read_LUT(path)) -print('\n') +print("\n") -message_box('Reading "Sony" ".spi1d" 3x1D LUT file.') -path = os.path.join(RESOURCES_DIRECTORY, 'sony_spi1d', 'eotf_sRGB_3x1D.spi1d') +message_box('Reading a "Sony" ".spi1d" 3x1D LUT file.') +path = os.path.join(RESOURCES_DIRECTORY, "sony_spi1d", "eotf_sRGB_3x1D.spi1d") print(colour.io.read_LUT_SonySPI1D(path)) -print('\n') +print("\n") +print(colour.read_LUT(path)) + +print("\n") + +message_box('Reading a "Sony" ".spi3d" 3D LUT file.') +path = os.path.join(RESOURCES_DIRECTORY, "sony_spi3d", "Colour_Correct.spi3d") +print(colour.io.read_LUT_SonySPI3D(path)) +print("\n") print(colour.read_LUT(path)) -print('\n') +print("\n") + +message_box('Reading a "Sony" ".spimtx" LUT file.') +path = os.path.join(RESOURCES_DIRECTORY, "sony_spimtx", "dt.spimtx") +print(colour.io.read_LUT_SonySPImtx(path)) +print("\n") +print(colour.read_LUT(path)) + +print("\n") RGB = np.array([0.35521588, 0.41000000, 0.24177934]) -message_box(('Applying 1D LUT to given "RGB" values:\n' '\n\t{0}'.format(RGB))) -path = os.path.join(RESOURCES_DIRECTORY, 'sony_spi1d', 'eotf_sRGB_1D.spi1d') +message_box(f'Applying a 1D LUT to given "RGB" values:\n\n\t{RGB}') +path = os.path.join(RESOURCES_DIRECTORY, "sony_spi1d", "eotf_sRGB_1D.spi1d") LUT = colour.io.read_LUT(path) print(LUT.apply(RGB)) -print('\n') +print("\n") -message_box(('Applying 3x1D LUT to given "RGB" values:\n' - '\n\t{0}'.format(RGB))) -path = os.path.join(RESOURCES_DIRECTORY, 'iridas_cube', - 'ACES_Proxy_10_to_ACES.cube') +message_box(f'Applying a 3x1D LUT to given "RGB" values:\n\n\t{RGB}') +path = os.path.join( + RESOURCES_DIRECTORY, "iridas_cube", "ACES_Proxy_10_to_ACES.cube" +) LUT = colour.io.read_LUT(path) print(LUT.apply(RGB)) -print('\n') +print("\n") + +message_box(f'Applying a 3D LUT to given "RGB" values:\n\n\t{RGB}') +path = os.path.join(RESOURCES_DIRECTORY, "iridas_cube", "Colour_Correct.cube") +LUT = colour.io.read_LUT(path) +print(LUT.apply(RGB)) -message_box(('Applying 3D LUT to given "RGB" values:\n' '\n\t{0}'.format(RGB))) -path = os.path.join(RESOURCES_DIRECTORY, 'iridas_cube', 'Colour_Correct.cube') +message_box( + f'Applying a "Sony" ".spimtx" LUT to given "RGB" values:\n\n\t{RGB}' +) +path = os.path.join(RESOURCES_DIRECTORY, "sony_spimtx", "dt.spimtx") LUT = colour.io.read_LUT(path) print(LUT.apply(RGB)) diff --git a/colour/examples/io/examples_tabular.py b/colour/examples/io/examples_tabular.py index dc1c81d247..3d9b0d5026 100644 --- a/colour/examples/io/examples_tabular.py +++ b/colour/examples/io/examples_tabular.py @@ -1,33 +1,28 @@ -# -*- coding: utf-8 -*- -""" -Showcases input / output *CSV* tabular data related examples. -""" +"""Showcases input / output *CSV* tabular data related examples.""" import os from pprint import pprint import colour -from colour.io.common import format_spectral_data from colour.utilities import message_box -RESOURCES_DIRECTORY = os.path.join(os.path.dirname(__file__), 'resources') +RESOURCES_DIRECTORY = os.path.join(os.path.dirname(__file__), "resources") message_box('"CSV" Tabular Data IO') message_box('Reading tabular data from "CSV" file.') data_babelcolor_average = colour.read_spectral_data_from_csv_file( - os.path.join(RESOURCES_DIRECTORY, 'babelcolor_average.csv')) + os.path.join(RESOURCES_DIRECTORY, "babelcolor_average.csv") +) pprint(sorted(data_babelcolor_average.keys())) -print('\n') +print("\n") -message_box('Format spectral data for pretty printing.') -print(format_spectral_data(data_babelcolor_average)) - -print('\n') - -message_box(('Reading spectral data from a "CSV" file directly as spectral ' - 'distributions.')) +message_box( + 'Reading spectral data from a "CSV" file directly as spectral ' + "distributions." +) sds_babelcolor_average = colour.read_sds_from_csv_file( - os.path.join(RESOURCES_DIRECTORY, 'babelcolor_average.csv')) + os.path.join(RESOURCES_DIRECTORY, "babelcolor_average.csv") +) pprint(sds_babelcolor_average) diff --git a/colour/examples/models/examples_cmyk.py b/colour/examples/models/examples_cmyk.py index 6c1cfe5b79..03bb45a348 100644 --- a/colour/examples/models/examples_cmyk.py +++ b/colour/examples/models/examples_cmyk.py @@ -1,40 +1,41 @@ -# -*- coding: utf-8 -*- -""" -Showcases Cyan-Magenta-Yellow (Black) (CMY(K)) colour transformations. -""" +"""Showcases Cyan-Magenta-Yellow (Black) (CMY(K)) colour transformations.""" import numpy as np import colour from colour.utilities import message_box -message_box('Cyan-Magenta-Yellow (Black) (CMY(K)) Colour Transformations') +message_box("Cyan-Magenta-Yellow (Black) (CMY(K)) Colour Transformations") RGB = np.array([0.45620519, 0.03081071, 0.04091952]) -message_box(('Converting to "CMY" colourspace from given "RGB" colourspace ' - 'values:\n' - '\n\t{0}'.format(RGB))) +message_box( + f'Converting to the "CMY" colourspace from given "RGB" colourspace ' + f"values:\n\n\t{RGB}" +) print(colour.RGB_to_CMY(RGB)) -print('\n') +print("\n") CMY = np.array([0.54379481, 0.96918929, 0.95908048]) -message_box(('Converting to "RGB" colourspace from given "CMY" colourspace ' - 'values:\n' - '\n\t{0}'.format(CMY))) +message_box( + f'Converting to the "RGB" colourspace from given "CMY" colourspace ' + f"values:\n\n\t{CMY}" +) print(colour.CMY_to_RGB(CMY)) -print('\n') +print("\n") -message_box(('Converting to "CMYK" colourspace from given "CMY" colourspace ' - 'values:\n' - '\n\t{0}'.format(CMY))) +message_box( + f'Converting to the "CMYK" colourspace from given "CMY" colourspace ' + f"values:\n\n\t{CMY}" +) print(colour.CMY_to_CMYK(CMY)) -print('\n') +print("\n") CMYK = np.array([0.00000000, 0.93246304, 0.91030457, 0.54379481]) -message_box(('Converting to "CMY" colourspace from given "CMYK" colourspace ' - 'values:\n' - '\n\t{0}'.format(CMYK))) +message_box( + f'Converting to the "CMY" colourspace from given "CMYK" colourspace ' + f"values:\n\n\t{CMYK}" +) print(colour.CMYK_to_CMY(CMYK)) diff --git a/colour/examples/models/examples_cylindrical.py b/colour/examples/models/examples_cylindrical.py index 39f815b116..44333be5f9 100644 --- a/colour/examples/models/examples_cylindrical.py +++ b/colour/examples/models/examples_cylindrical.py @@ -1,42 +1,60 @@ -# -*- coding: utf-8 -*- -""" -Showcases cylindrical and spherical colour models computations. -""" +"""Showcases cylindrical and spherical colour models computations.""" import numpy as np import colour from colour.utilities import message_box -message_box('Cylindrical & Spherical Colour Models') +message_box("Cylindrical & Spherical Colour Models") RGB = np.array([0.45620519, 0.03081071, 0.04091952]) -message_box(('Converting to "HSV" colourspace from given "RGB" colourspace ' - 'values:\n' - '\n\t{0}'.format(RGB))) +message_box( + f'Converting to the "HSV" colourspace from given "RGB" colourspace ' + f"values:\n\n\t{RGB}" +) print(colour.RGB_to_HSV(RGB)) -print('\n') +print("\n") HSV = np.array([0.99603944, 0.93246304, 0.45620519]) -message_box(('Converting to "RGB" colourspace from given "HSV" colourspace ' - 'values:\n' - '\n\t{0}'.format(HSV))) +message_box( + f'Converting to the "RGB" colourspace from given "HSV" colourspace ' + f"values:\n\n\t{HSV}" +) print(colour.HSV_to_RGB(HSV)) -print('\n') +print("\n") -message_box(('Converting to "HSL" colourspace from given "RGB" colourspace ' - 'values:\n' - '\n\t{0}'.format(RGB))) +message_box( + f'Converting to the "HSL" colourspace from given "RGB" colourspace ' + f"values:\n\n\t{RGB}" +) print(colour.RGB_to_HSL(RGB)) -print('\n') +print("\n") HSL = np.array([0.99603944, 0.87347144, 0.24350795]) -message_box(('Converting to "RGB" colourspace from given "HSL" colourspace ' - 'values:\n' - '\n\t{0}'.format(HSL))) +message_box( + f'Converting to the "RGB" colourspace from given "HSL" colourspace ' + f"values:\n\n\t{HSL}" +) print(colour.HSL_to_RGB(HSL)) -print('\n') +print("\n") + +message_box( + f'Converting to the "HCL" colourspace from given "RGB" colourspace ' + f"values:\n\n\t{RGB}" +) +print(colour.RGB_to_HCL(RGB)) + +print("\n") + +HCL = np.array([0.99603944, 0.87347144, 0.24350795]) +message_box( + f'Converting to the "RGB" colourspace from given "HCL" colourspace ' + f"values:\n\n\t{HCL}" +) +print(colour.HCL_to_RGB(HCL)) + +print("\n") diff --git a/colour/examples/models/examples_derivation.py b/colour/examples/models/examples_derivation.py index 8cc3b40a5a..ae414ce643 100644 --- a/colour/examples/models/examples_derivation.py +++ b/colour/examples/models/examples_derivation.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *RGB* colourspace derivation. -""" +"""Showcases *RGB* colourspace derivation.""" import numpy as np @@ -10,85 +7,105 @@ message_box('"RGB" Colourspace Derivation') -primaries = np.array([ - [0.73470, 0.26530], - [0.00000, 1.00000], - [0.00010, -0.07700], -]) +primaries = np.array( + [ + [0.73470, 0.26530], + [0.00000, 1.00000], + [0.00010, -0.07700], + ] +) whitepoint = np.array([0.32168, 0.33767]) -message_box(('Computing the normalised primary matrix for "ACES2065-1" ' - 'colourspace transforming from "ACES2065-1" colourspace to ' - '"CIE XYZ" tristimulus values using user defined primaries ' - 'matrix and whitepoint:\n' - '\n\t{0}\n\t{1}\n\t{2}\n\n\t{3}'.format( - primaries[0], primaries[1], primaries[2], whitepoint))) +message_box( + f'Computing the normalised primary matrix for the "ACES2065-1" colourspace ' + f'transforming from the "ACES2065-1" colourspace to "CIE XYZ" tristimulus ' + f"values using user defined primaries matrix and whitepoint:\n\n" + f"\t{primaries[0]}\n" + f"\t{primaries[1]}\n" + f"\t{primaries[2]}\n" + f"\t{whitepoint}" +) print(colour.normalised_primary_matrix(primaries, whitepoint)) -print('\n') +print("\n") -message_box(('Computing the normalised primary matrix for "ACES2065-1" ' - 'colourspace transforming from "ACES2065-1" colourspace to ' - '"CIE XYZ" tristimulus values using colour models dataset.')) +message_box( + 'Computing the normalised primary matrix for the "ACES2065-1" colourspace ' + 'transforming from the "ACES2065-1" colourspace to "CIE XYZ" tristimulus ' + "values using colour models dataset." +) print( colour.normalised_primary_matrix( - colour.RGB_COLOURSPACES['ACES2065-1'].primaries, - colour.RGB_COLOURSPACES['ACES2065-1'].whitepoint, - )) - -print('\n') - -message_box(('Computing the normalised primary matrix for "ACES2065-1" ' - 'colourspace transforming from "CIE XYZ" tristimulus values to ' - '"ACES2065-1" colourspace using colour models dataset.')) + colour.RGB_COLOURSPACES["ACES2065-1"].primaries, + colour.RGB_COLOURSPACES["ACES2065-1"].whitepoint, + ) +) + +print("\n") + +message_box( + 'Computing the normalised primary matrix for the "ACES2065-1" colourspace ' + 'transforming from "CIE XYZ" tristimulus values to the "ACES2065-1" ' + "colourspace using colour models dataset." +) print( np.linalg.inv( colour.normalised_primary_matrix( - colour.RGB_COLOURSPACES['ACES2065-1'].primaries, - colour.RGB_COLOURSPACES['ACES2065-1'].whitepoint, - ))) - -print('\n') - -message_box(('Computing "sRGB" colourspace primaries chromatically adapted to ' - '"CIE Standard Illuminant D50":\n')) + colour.RGB_COLOURSPACES["ACES2065-1"].primaries, + colour.RGB_COLOURSPACES["ACES2065-1"].whitepoint, + ) + ) +) + +print("\n") + +message_box( + 'Computing the "sRGB" colourspace primaries chromatically adapted to the ' + '"CIE Standard Illuminant D50":\n' +) print( colour.chromatically_adapted_primaries( - colour.RGB_COLOURSPACES['sRGB'].primaries, - colour.RGB_COLOURSPACES['sRGB'].whitepoint, - colour.CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D50'], - )) - -print('\n') - -npm = np.array([ - [0.41240000, 0.35760000, 0.18050000], - [0.21260000, 0.71520000, 0.07220000], - [0.01930000, 0.11920000, 0.95050000], -]) -message_box(('Computing the primaries and whitepoint from given ' - 'normalised primary matrix:\n' - '\n{0}'.format(npm))) + colour.RGB_COLOURSPACES["sRGB"].primaries, + colour.RGB_COLOURSPACES["sRGB"].whitepoint, + colour.CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D50"], + ) +) + +print("\n") + +npm = np.array( + [ + [0.41240000, 0.35760000, 0.18050000], + [0.21260000, 0.71520000, 0.07220000], + [0.01930000, 0.11920000, 0.95050000], + ] +) +message_box( + f"Computing the primaries and whitepoint from given normalised primary " + f"matrix:\n\n{npm}" +) print(colour.primaries_whitepoint(npm)) -print('\n') +print("\n") RGB = np.array([0.45620519, 0.03081071, 0.04091952]) -message_box(('Computing "RGB" luminance of given "RGB" values:\n' - '\n\t{0}'.format(RGB))) +message_box(f'Computing the "RGB" luminance of given "RGB" values:\n\n\t{RGB}') print( colour.RGB_luminance( RGB, - colour.RGB_COLOURSPACES['sRGB'].primaries, - colour.RGB_COLOURSPACES['sRGB'].whitepoint, - )) + colour.RGB_COLOURSPACES["sRGB"].primaries, + colour.RGB_COLOURSPACES["sRGB"].whitepoint, + ) +) -print('\n') +print("\n") -message_box(('Computing "RGB" luminance equation of given "RGB" values:\n' - '\n\t{0}'.format(RGB))) +message_box( + f'Computing the "RGB" luminance equation of given "RGB" values:\n\n\t{RGB}' +) print( colour.RGB_luminance( RGB, - colour.RGB_COLOURSPACES['sRGB'].primaries, - colour.RGB_COLOURSPACES['sRGB'].whitepoint, - )) + colour.RGB_COLOURSPACES["sRGB"].primaries, + colour.RGB_COLOURSPACES["sRGB"].whitepoint, + ) +) diff --git a/colour/examples/models/examples_ictcp.py b/colour/examples/models/examples_ictcp.py index 6b13e6f646..ab1075d852 100644 --- a/colour/examples/models/examples_ictcp.py +++ b/colour/examples/models/examples_ictcp.py @@ -1,25 +1,24 @@ -# -*- coding: utf-8 -*- -""" -Showcases *ICTCP* *colour encoding* computations. -""" +"""Showcases *ICtCp* *colour encoding* computations.""" import numpy as np import colour from colour.utilities import message_box -message_box('"ICTCP" Colour Encoding Computations') +message_box('"ICtCp" Colour Encoding Computations') RGB = np.array([0.45620519, 0.03081071, 0.04091952]) -message_box(('Converting from "ITU-R BT.2020" colourspace to "ICTCP" colour ' - 'encoding given "RGB" values:\n' - '\n\t{0}'.format(RGB))) -print(colour.RGB_to_ICTCP(RGB)) +message_box( + f'Converting from the "ITU-R BT.2020" colourspace to the "ICtCp" colour ' + f'encoding given "RGB" values:\n\n\t{RGB}' +) +print(colour.RGB_to_ICtCp(RGB)) -print('\n') +print("\n") -ICTCP = np.array([0.07351364, 0.00475253, 0.09351596]) -message_box(('Converting from "ICTCP" colour encoding to "ITU-R BT.2020" ' - 'colourspace given "ICTCP" values:\n' - '\n\t{0}'.format(ICTCP))) -print(colour.ICTCP_to_RGB(ICTCP)) +ICtCp = np.array([0.07351364, 0.00475253, 0.09351596]) +message_box( + f'Converting from the "ICtCp" colour encoding to the "ITU-R BT.2020" ' + f'colourspace given "ICtCp" values:\n\n\t{ICtCp}' +) +print(colour.ICtCp_to_RGB(ICtCp)) diff --git a/colour/examples/models/examples_models.py b/colour/examples/models/examples_models.py index 048b9543d1..80dbd4117f 100644 --- a/colour/examples/models/examples_models.py +++ b/colour/examples/models/examples_models.py @@ -1,362 +1,488 @@ -# -*- coding: utf-8 -*- -""" -Showcases colour models computations. -""" +"""Showcases colour models computations.""" import numpy as np import colour from colour.utilities import message_box -message_box('Colour Models Computations') +message_box("Colour Models Computations") XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) -message_box(('Converting to "CIE xyY" colourspace from given "CIE XYZ" ' - 'tristimulus values:\n' - '\n\t{0}'.format(XYZ))) +message_box( + f'Converting to the "CIE xyY" colourspace from given "CIE XYZ" ' + f"tristimulus values:\n\n\t{XYZ}" +) print(colour.XYZ_to_xyY(XYZ)) -print('\n') +print("\n") -message_box(('The default illuminant if X == Y == Z == 0 is ' - '"CIE Standard Illuminant D Series D65".')) +message_box( + "The default illuminant if X == Y == Z == 0 is " + '"CIE Standard Illuminant D Series D65".' +) print(colour.XYZ_to_xyY(np.array([0.00000000, 0.00000000, 0.00000000]))) -print('\n') +print("\n") -message_box('Using an alternative illuminant.') +message_box("Using an alternative illuminant.") print( colour.XYZ_to_xyY( np.array([0.00000000, 0.00000000, 0.00000000]), - colour.CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['ACES'], - )) + colour.CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["ACES"], + ) +) -print('\n') +print("\n") xyY = np.array([0.26414772, 0.37770001, 0.10080000]) -message_box(('Converting to "CIE XYZ" tristimulus values from given "CIE xyY" ' - 'colourspace values:\n' - '\n\t{0}'.format(xyY))) +message_box( + f'Converting to "CIE XYZ" tristimulus values from given "CIE xyY" ' + f"colourspace values:\n\n\t{xyY}" +) print(colour.xyY_to_XYZ(xyY)) -print('\n') +print("\n") -message_box(('Converting to "xy" chromaticity coordinates from given ' - '"CIE XYZ" tristimulus values:\n' - '\n\t{0}'.format(XYZ))) +message_box( + f'Converting to "xy" chromaticity coordinates from given "CIE XYZ" ' + f"tristimulus values:\n\n\t{XYZ}" +) print(colour.XYZ_to_xy(XYZ)) -print('\n') +print("\n") xy = np.array([0.26414772, 0.37770001]) -message_box(('Converting to "CIE XYZ" tristimulus values from given "xy" ' - 'chromaticity coordinates:\n' - '\n\t{0}'.format(xy))) +message_box( + f'Converting to "CIE XYZ" tristimulus values from given "xy" chromaticity ' + f"coordinates:\n\n\t{xy}" +) print(colour.xy_to_XYZ(xy)) -print('\n') +print("\n") -message_box(('Converting to "RGB" colourspace from given "CIE XYZ" ' - 'tristimulus values:\n' - '\n\t{0}'.format(XYZ))) -D65 = colour.CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65'] +message_box( + f'Converting to "RGB" colourspace from given "CIE XYZ" tristimulus ' + f"values:\n\n\t{XYZ}" +) +D65 = colour.CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"] print( colour.XYZ_to_RGB( XYZ, D65, - colour.RGB_COLOURSPACES['sRGB'].whitepoint, - colour.RGB_COLOURSPACES['sRGB'].matrix_XYZ_to_RGB, - 'Bradford', - colour.RGB_COLOURSPACES['sRGB'].cctf_encoding, - )) + colour.RGB_COLOURSPACES["sRGB"].whitepoint, + colour.RGB_COLOURSPACES["sRGB"].matrix_XYZ_to_RGB, + "Bradford", + colour.RGB_COLOURSPACES["sRGB"].cctf_encoding, + ) +) -print('\n') +print("\n") RGB = np.array([0.45620519, 0.03081071, 0.04091952]) -message_box(('Converting to "CIE XYZ" tristimulus values from given "RGB" ' - 'colourspace values:\n' - '\n\t{0}'.format(RGB))) +message_box( + f'Converting to "CIE XYZ" tristimulus values from given "RGB" colourspace ' + f"values:\n\n\t{RGB}" +) print( colour.RGB_to_XYZ( RGB, - colour.RGB_COLOURSPACES['sRGB'].whitepoint, + colour.RGB_COLOURSPACES["sRGB"].whitepoint, D65, - colour.RGB_COLOURSPACES['sRGB'].matrix_RGB_to_XYZ, - 'Bradford', - colour.RGB_COLOURSPACES['sRGB'].cctf_decoding, - )) - -print('\n') - -message_box(('Converting to "sRGB" colourspace from given "CIE XYZ" ' - 'tristimulus values using convenient definition:\n' - '\n\t{0}'.format(XYZ))) + colour.RGB_COLOURSPACES["sRGB"].matrix_RGB_to_XYZ, + "Bradford", + colour.RGB_COLOURSPACES["sRGB"].cctf_decoding, + ) +) + +print("\n") + +message_box( + f'Converting to "sRGB" colourspace from given "CIE XYZ" tristimulus ' + f"values using convenient definition:\n\n\t{XYZ}" +) print(colour.XYZ_to_sRGB(XYZ, D65)) -print('\n') +print("\n") -message_box(('Converting to "CIE 1960 UCS" colourspace from given "CIE XYZ" ' - 'tristimulus values:\n' - '\n\t{0}'.format(XYZ))) +message_box( + f'Converting to "CIE 1960 UCS" colourspace from given "CIE XYZ" ' + f"tristimulus values:\n\n\t{XYZ}" +) print(colour.XYZ_to_UCS(XYZ)) -print('\n') +print("\n") UCS = np.array([0.07049533, 0.10080000, 0.09558313]) -message_box(('Converting to "CIE XYZ" tristimulus values from given' - '"CIE 1960 UCS" colourspace values:\n' - '\n\t{0}'.format(UCS))) +message_box( + f'Converting to "CIE XYZ" tristimulus values from given "CIE 1960 UCS" ' + f"colourspace values:\n\n\t{UCS}" +) print(colour.UCS_to_XYZ(UCS)) -print('\n') +print("\n") -message_box(('Converting to "uv" chromaticity coordinates from given ' - '"CIE UCS" colourspace values:\n' - '\n\t{0}'.format(UCS))) +message_box( + f'Converting to "uv" chromaticity coordinates from given "CIE UCS" ' + f"colourspace values:\n\n\t{UCS}" +) print(colour.UCS_to_uv(UCS)) -print('\n') +print("\n") uv = np.array([0.15085309, 0.32355314]) -message_box(('Converting to "xy" chromaticity coordinates from given ' - '"CIE UCS" colourspace "uv" chromaticity coordinates:\n' - '\n\t{0}'.format(uv))) +message_box( + f'Converting to "xy" chromaticity coordinates from given "CIE UCS" ' + f'colourspace "uv" chromaticity coordinates:\n\n\t{uv}' +) print(colour.UCS_uv_to_xy(uv)) -print('\n') +print("\n") xy = np.array([0.26414771, 0.37770001]) -message_box(('Converting to "CIE UCS" colourspace "uv" chromaticity ' - 'coordinates from given "xy" chromaticity coordinates:\n' - '\n\t{0}'.format(xy))) +message_box( + f'Converting to "CIE UCS" colourspace "uv" chromaticity coordinates from ' + f'given "xy" chromaticity coordinates:\n\n\t{xy}' +) print(colour.xy_to_UCS_uv(xy)) -print('\n') +print("\n") -message_box(('Converting to "CIE 1964 U*V*W*" colourspace from given' - '"CIE XYZ" tristimulus values:\n' - '\n\t{0}'.format(XYZ * 100))) +message_box( + f'Converting to "CIE 1964 U*V*W*" colourspace from given"CIE XYZ" ' + f"tristimulus values:\n\n\t{XYZ * 100}" +) print(colour.XYZ_to_UVW(XYZ * 100)) -print('\n') +print("\n") UVW = np.array([-22.59840563, 5.45505477, 37.00411491]) -message_box(('Converting to "CIE XYZ" tristimulus values from given' - '"CIE 1964 U*V*W*" colourspace values:\n' - '\n\t{0}'.format(UVW))) +message_box( + f'Converting to "CIE XYZ" tristimulus values from given"CIE 1964 U*V*W*" ' + f"colourspace values:\n\n\t{UVW}" +) print(colour.UVW_to_XYZ(UVW) / 100) -print('\n') +print("\n") -message_box(('Converting to "CIE L*u*v*" colourspace from given "CIE XYZ" ' - 'tristimulus values:\n' - '\n\t{0}'.format(XYZ))) +message_box( + f'Converting to "CIE L*u*v*" colourspace from given "CIE XYZ" tristimulus ' + f"values:\n\n\t{XYZ}" +) print(colour.XYZ_to_Luv(XYZ)) -print('\n') +print("\n") Luv = np.array([37.9856291, -23.19781615, 8.39962073]) -message_box(('Converting to "CIE XYZ" tristimulus values from given ' - '"CIE L*u*v*" colourspace values:\n' - '\n\t{0}'.format(Luv))) +message_box( + f'Converting to "CIE XYZ" tristimulus values from given "CIE L*u*v*" ' + f"colourspace values:\n\n\t{Luv}" +) print(colour.Luv_to_XYZ(Luv)) -print('\n') +print("\n") -message_box(('Converting to "u"v"" chromaticity coordinates from given ' - '"CIE L*u*v*" colourspace values:\n' - '\n\t{0}'.format(Luv))) +message_box( + f'Converting to "u"v"" chromaticity coordinates from given "CIE L*u*v*" ' + f"colourspace values:\n\n\t{Luv}" +) print(colour.Luv_to_uv(Luv)) -print('\n') +print("\n") uv = np.array([0.1508531, 0.48532971]) -message_box(('Converting to "xy" chromaticity coordinates from given ' - '"CIE L*u*v*" colourspace "u"v"" chromaticity coordinates:\n' - '\n\t{0}'.format(uv))) +message_box( + f'Converting to "xy" chromaticity coordinates from given "CIE L*u*v*" ' + f'colourspace "u"v"" chromaticity coordinates:\n\n\t{uv}' +) print(colour.Luv_uv_to_xy(uv)) -print('\n') +print("\n") xy = np.array([0.26414771, 0.37770001]) -message_box(('Converting to "CIE L*u*v*" colourspace "u"v"" chromaticity ' - 'coordinates from given "xy" chromaticity coordinates:\n' - '\n\t{0}'.format(xy))) +message_box( + f'Converting to "CIE L*u*v*" colourspace "u"v"" chromaticity coordinates ' + f'from given "xy" chromaticity coordinates:\n\n\t{xy}' +) print(colour.xy_to_Luv_uv(xy)) -print('\n') +print("\n") -message_box(('Converting to "CIE L*C*Huv" colourspace from given "CIE L*u*v*" ' - 'colourspace values:\n' - '\n\t{0}'.format(Luv))) +message_box( + f'Converting to "CIE L*C*Huv" colourspace from given "CIE L*u*v*" ' + f"colourspace values:\n\n\t{Luv}" +) print(colour.Luv_to_LCHuv(Luv)) -print('\n') +print("\n") LCHuv = np.array([37.9856291, 24.67169031, 160.09535205]) -message_box(('Converting to "CIE L*u*v*" colourspace from given "CIE L*C*Huv" ' - 'colourspace values:\n' - '\n\t{0}'.format(LCHuv))) +message_box( + f'Converting to "CIE L*u*v*" colourspace from given "CIE L*C*Huv" ' + f"colourspace values:\n\n\t{LCHuv}" +) print(colour.LCHuv_to_Luv(LCHuv)) -print('\n') +print("\n") -message_box(('Converting to "CIE L*a*b*" colourspace from given "CIE XYZ" ' - 'tristimulus values:\n' - '\n\t{0}'.format(XYZ))) +message_box( + f'Converting to "CIE L*a*b*" colourspace from given "CIE XYZ" tristimulus ' + f"values:\n\n\t{XYZ}" +) print(colour.XYZ_to_Lab(XYZ)) -print('\n') +print("\n") Lab = np.array([37.9856291, -22.61920654, 4.19811236]) -message_box(('Converting to "CIE XYZ" tristimulus values from given ' - '"CIE L*a*b*" colourspace values:\n' - '\n\t{0}'.format(Lab))) +message_box( + f'Converting to "CIE XYZ" tristimulus values from given "CIE L*a*b*" ' + f"colourspace values:\n\n\t{Lab}" +) print(colour.Lab_to_XYZ(Lab)) -print('\n') +print("\n") -message_box(('Converting to "CIE L*C*Hab" colourspace from given "CIE L*a*b*" ' - 'colourspace values:\n' - '\n\t{0}'.format(Lab))) +message_box( + f'Converting to "CIE L*C*Hab" colourspace from given "CIE L*a*b*" ' + f"colourspace values:\n\n\t{Lab}" +) print(colour.Lab_to_LCHab(Lab)) -print('\n') +print("\n") LCHab = np.array([37.9856291, 23.00549178, 169.48557589]) -message_box(('Converting to "CIE L*a*b*" colourspace from given "CIE L*C*Hab" ' - 'colourspace values:\n' - '\n\t{0}'.format(LCHab))) +message_box( + f'Converting to "CIE L*a*b*" colourspace from given "CIE L*C*Hab" ' + f"colourspace values:\n\n\t{LCHab}" +) print(colour.LCHab_to_Lab(LCHab)) -print('\n') +print("\n") XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) * 100 -message_box(('Converting to "Hunter L,a,b" colour scale from given "CIE XYZ" ' - 'tristimulus values:\n' - '\n\t{0}'.format(XYZ))) +message_box( + f'Converting to "Hunter L,a,b" colour scale from given "CIE XYZ" ' + f"tristimulus values:\n\n\t{XYZ}" +) print(colour.XYZ_to_Hunter_Lab(XYZ)) -print('\n') +print("\n") Lab = np.array([31.74901573, -14.44108591, 2.74396261]) -message_box(('Converting to "CIE XYZ" tristimulus values from given ' - '"Hunter L,a,b" colour scale values:\n' - '\n\t{0}'.format(Lab))) +message_box( + f'Converting to "CIE XYZ" tristimulus values from given "Hunter L,a,b" ' + f"colour scale values:\n\n\t{Lab}" +) print(colour.Hunter_Lab_to_XYZ(Lab)) -print('\n') +print("\n") -message_box(('Converting to "Hunter Rd,a,b" colour scale from given "CIE XYZ" ' - 'tristimulus values:\n' - '\n\t{0}'.format(XYZ))) +message_box( + f'Converting to "Hunter Rd,a,b" colour scale from given "CIE XYZ" ' + f"tristimulus values:\n\n\t{XYZ}" +) print(colour.XYZ_to_Hunter_Rdab(XYZ)) -print('\n') +print("\n") R_d_ab = np.array([10.08000000, -17.8442708, 3.39060457]) -message_box(('Converting to "CIE XYZ" tristimulus values from given' - '"Hunter Rd,a,b" colour scale values:\n' - '\n\t{0}'.format(R_d_ab))) +message_box( + f'Converting to "CIE XYZ" tristimulus values from given"Hunter Rd,a,b" ' + f"colour scale values:\n\n\t{R_d_ab}" +) print(colour.Hunter_Rdab_to_XYZ(R_d_ab)) -print('\n') +print("\n") + +XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) +message_box( + f'Converting to "ICaCb" colourspace from given "CIE XYZ" tristimulus ' + f"values:\n\n\t{XYZ}" +) +print(colour.XYZ_to_ICaCb(XYZ)) + +ICaCb = np.array([0.06875297, 0.05753352, 0.02081548]) +message_box( + f'Converting to "CIE XYZ" tristimulus values from given "ICaCb" ' + f"colourspace values:\n\n\t{ICaCb}" +) +print(colour.ICaCb_to_XYZ(ICaCb)) XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) -message_box(('Converting to "IPT" colourspace from given "CIE XYZ" ' - 'tristimulus values:\n' - '\n\t{0}'.format(XYZ))) +message_box( + f'Converting to "IgPgTg" colourspace from given "CIE XYZ" tristimulus ' + f"values:\n\n\t{XYZ}" +) +print(colour.XYZ_to_IgPgTg(XYZ)) + +print("\n") + +IgPgTg = np.array([0.42421258, 0.18632491, 0.10689223]) +message_box( + f'Converting to "CIE XYZ" tristimulus values from given "IgPgTg" ' + f"colourspace values:\n\n\t{IgPgTg}" +) +print(colour.IgPgTg_to_XYZ(IgPgTg)) + +print("\n") + +XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) +message_box( + f'Converting to "IPT" colourspace from given "CIE XYZ" tristimulus ' + f"values:\n\n\t{XYZ}" +) print(colour.XYZ_to_IPT(XYZ)) -print('\n') +print("\n") IPT = np.array([0.36571124, -0.11114798, 0.01594746]) -message_box(('Converting to "CIE XYZ" tristimulus values from given "IPT" ' - 'colourspace values:\n' - '\n\t{0}'.format(IPT))) +message_box( + f'Converting to "CIE XYZ" tristimulus values from given "IPT" colourspace ' + f"values:\n\n\t{IPT}" +) print(colour.IPT_to_XYZ(IPT)) -print('\n') +print("\n") -message_box(('Converting to "hdr-CIELab" colourspace from given "CIE XYZ" ' - 'tristimulus values:\n' - '\n\t{0}'.format(XYZ))) +message_box( + f'Converting to "hdr-CIELab" colourspace from given "CIE XYZ" tristimulus ' + f"values:\n\n\t{XYZ}" +) print(colour.XYZ_to_hdr_CIELab(XYZ)) -print('\n') +print("\n") Lab_hdr = np.array([48.26598942, -26.97517728, 4.99243377]) -message_box(('Converting to "CIE XYZ" tristimulus values from given ' - '"hdr-CIELab" colourspace values:\n' - '\n\t{0}'.format(Lab_hdr))) +message_box( + f'Converting to "CIE XYZ" tristimulus values from given "hdr-CIELab" ' + f"colourspace values:\n\n\t{Lab_hdr}" +) print(colour.hdr_CIELab_to_XYZ(Lab_hdr)) -print('\n') +print("\n") -message_box(('Converting to "hdr-IPT" colourspace from given "CIE XYZ" ' - 'tristimulus values:\n' - '\n\t{0}'.format(XYZ))) +message_box( + f'Converting to "hdr-IPT" colourspace from given "CIE XYZ" tristimulus ' + f"values:\n\n\t{XYZ}" +) print(colour.XYZ_to_hdr_IPT(XYZ)) -print('\n') +print("\n") IPT_hdr = np.array([46.4993815, -12.82251566, 1.85029518]) -message_box(('Converting to "CIE XYZ" tristimulus values from given "hdr-IPT" ' - 'colourspace values:\n' - '\n\t{0}'.format(IPT_hdr))) +message_box( + f'Converting to "CIE XYZ" tristimulus values from given "hdr-IPT" ' + f"colourspace values:\n\n\t{IPT_hdr}" +) print(colour.hdr_IPT_to_XYZ(IPT_hdr)) -print('\n') +print("\n") -message_box(('Converting to "JzAzBz" colourspace from given "CIE XYZ" ' - 'tristimulus values:\n' - '\n\t{0}'.format(XYZ))) -print(colour.XYZ_to_JzAzBz(XYZ)) +message_box( + f'Converting to "Jzazbz" colourspace from given "CIE XYZ" tristimulus ' + f"values:\n\n\t{XYZ}" +) +print(colour.XYZ_to_Jzazbz(XYZ)) -print('\n') +print("\n") -JzAzBz = np.array([0.00357804, -0.00295507, 0.00038998]) -message_box(('Converting to "CIE XYZ" tristimulus values from given "JzAzBz" ' - 'colourspace values:\n' - '\n\t{0}'.format(JzAzBz))) -print(colour.JzAzBz_to_XYZ(JzAzBz)) +Jzazbz = np.array([0.00357804, -0.00295507, 0.00038998]) +message_box( + f'Converting to "CIE XYZ" tristimulus values from given "Jzazbz" ' + f"colourspace values:\n\n\t{Jzazbz}" +) +print(colour.Jzazbz_to_XYZ(Jzazbz)) -print('\n') +print("\n") -message_box(('Converting to "OSA UCS" colourspace from given "CIE XYZ" ' - 'tristimulus values under the ' - '"CIE 1964 10 Degree Standard Observer":\n' - '\n\t{0}'.format(XYZ * 100))) +message_box( + f'Converting to "OSA UCS" colourspace from given "CIE XYZ" tristimulus ' + f'values under the "CIE 1964 10 Degree Standard Observer":\n\n' + f"\t{XYZ * 100}" +) print(colour.XYZ_to_OSA_UCS(XYZ * 100)) -print('\n') +print("\n") Ljg = np.array([-4.4900683, 0.70305936, 3.03463664]) -message_box(('Converting to "CIE XYZ" tristimulus values under the ' - '"CIE 1964 10 Degree Standard Observer" ' - 'from "OSA UCS" colourspace:\n' - '\n\t{0}'.format(Ljg))) +message_box( + f'Converting to "CIE XYZ" tristimulus values under the ' + f'"CIE 1964 10 Degree Standard Observer" from "OSA UCS" colourspace:\n\n' + f"\t{Ljg}" +) print(colour.OSA_UCS_to_XYZ(Ljg)) -print('\n') +print("\n") + +message_box( + f'Converting to "Oklab" colourspace from given "CIE XYZ" tristimulus ' + f"values:\n\n\t{XYZ}" +) +print(colour.XYZ_to_Oklab(XYZ)) + +print("\n") + +Lab = np.array([0.51634019, 0.15469500, 0.06289579]) +message_box( + f'Converting to "CIE XYZ" tristimulus values from "Oklab" colourspace:\n\n' + f"\t{Lab}" +) +print(colour.Oklab_to_XYZ(Lab)) + +print("\n") + +XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) +message_box( + 'Converting to the "ProLab" colourspace from given "CIE XYZ" ' + "tristimulus values:\n\n" + f"\t{XYZ}" +) +print(colour.XYZ_to_ProLab(XYZ)) + +print("\n") + +ProLab = np.array([0.36571124, -0.11114798, 0.01594746]) +message_box( + 'Converting to "CIE XYZ" tristimulus values from given "ProLab" ' + "colourspace values:\n\n" + f"\t{ProLab}" +) +print(colour.ProLab_to_XYZ(ProLab)) + +print("\n") XYZ = np.array([19.01, 20.00, 21.78]) XYZ_w = np.array([95.05, 100.00, 108.88]) L_A = 318.31 Y_b = 20.0 -surround = colour.VIEWING_CONDITIONS_CIECAM02['Average'] +surround = colour.VIEWING_CONDITIONS_CIECAM02["Average"] specification = colour.XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround) JMh = (specification.J, specification.M, specification.h) -message_box(('Converting to "CAM02-UCS" colourspace from given ' - '"CIECAM02" colour appearance model "JMh" correlates:\n' - '\n\t{0}'.format(JMh))) +message_box( + f'Converting to "CAM02-UCS" colourspace from given "CIECAM02" colour ' + f'appearance model "JMh" correlates:\n\n\t{JMh}' +) print(colour.JMh_CIECAM02_to_CAM02UCS(JMh)) -print('\n') +print("\n") + +message_box( + f'Converting to "CAM02-UCS" colourspace from given "CIE XYZ" tristimulus ' + f"values:\n\n\t{JMh}" +) +print(colour.XYZ_to_CAM02UCS(XYZ / 100, XYZ_w=XYZ_w / 100, L_A=L_A, Y_b=Y_b)) + +print("\n") specification = colour.XYZ_to_CAM16(XYZ, XYZ_w, L_A, Y_b, surround) JMh = (specification.J, specification.M, specification.h) -message_box(('Converting to "CAM16-UCS" colourspace from given ' - '"CAM16" colour appearance model "JMh" correlates:\n' - '\n\t{0}'.format(JMh))) +message_box( + f'Converting to "CAM16-UCS" colourspace from given "CAM16" colour ' + f'appearance model "JMh" correlates:\n\n\t{JMh}' +) print(colour.JMh_CAM16_to_CAM16UCS(JMh)) + +message_box( + f'Converting to "CAM16-UCS" colourspace from given "CIE XYZ" tristimulus ' + f"values:\n\n\t{JMh}" +) +print(colour.XYZ_to_CAM16UCS(XYZ / 100, XYZ_w=XYZ_w / 100, L_A=L_A, Y_b=Y_b)) diff --git a/colour/examples/models/examples_prismatic.py b/colour/examples/models/examples_prismatic.py index 7403365e1d..a04bf5a9d4 100644 --- a/colour/examples/models/examples_prismatic.py +++ b/colour/examples/models/examples_prismatic.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *Prismatic* colourspace computations. -""" +"""Showcases *Prismatic* colourspace computations.""" import numpy as np @@ -11,24 +8,27 @@ message_box('"Prismatic" Colourspace Computations') RGB = np.array([0.25, 0.50, 0.75]) -message_box(('Converting from "RGB" colourspace to "Prismatic" colourspace ' - 'given "RGB" values:\n' - '\n\t{0}'.format(RGB))) +message_box( + f'Converting from the "RGB" colourspace to the "Prismatic" colourspace ' + f'given "RGB" values:\n\n\t{RGB}' +) print(colour.RGB_to_Prismatic(RGB)) -print('\n') +print("\n") Lrgb = np.array([0.7500000, 0.1666667, 0.3333333, 0.5000000]) -message_box(('Converting from "Prismatic" colourspace to "RGB" colourspace ' - 'given "Lrgb" values:\n' - '\n\t{0}'.format(Lrgb))) +message_box( + f'Converting from the "Prismatic" colourspace to the "RGB" colourspace ' + f'given "Lrgb" values:\n\n\t{Lrgb}' +) print(colour.Prismatic_to_RGB(Lrgb)) -print('\n') +print("\n") -message_box(('Applying 50% desaturation in "Prismatic" colourspace to' - 'given "RGB" values:\n' - '\n\t{0}'.format(RGB))) +message_box( + f'Applying 50% desaturation in the "Prismatic" colourspace to the given ' + f'"RGB" values:\n\n\t{RGB}' +) saturation = 0.5 Lrgb = colour.RGB_to_Prismatic(RGB) Lrgb[..., 1:] = 1.0 / 3.0 + saturation * (Lrgb[..., 1:] - 1.0 / 3.0) diff --git a/colour/examples/models/examples_rgb.py b/colour/examples/models/examples_rgb.py index 15a704a310..175ee66532 100644 --- a/colour/examples/models/examples_rgb.py +++ b/colour/examples/models/examples_rgb.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *RGB* *colourspaces* computations. -""" +"""Showcases *RGB* *colourspaces* computations.""" import numpy as np from pprint import pprint @@ -14,43 +11,57 @@ message_box('"RGB" colourspaces dataset.') pprint(sorted(colour.RGB_COLOURSPACES.keys())) -print('\n') +print("\n") message_box('"ACES2065-1" colourspaces data.') -colourspace = colour.RGB_COLOURSPACES['ACES2065-1'] -print('Name:\n"{0}"'.format(colourspace.name)) -print('\nPrimaries:\n{0}'.format(colourspace.primaries)) -print(('\nNormalised primary matrix to "CIE XYZ" ' - 'tristimulus values:\n{0}').format(colourspace.matrix_RGB_to_XYZ)) -print('\nNormalised primary matrix to "ACES2065-1":\n{0}'.format( - colourspace.matrix_XYZ_to_RGB)) -print('\nOpto-electronic transfer function from ' - 'linear to colourspace:\n{0}'.format(colourspace.cctf_encoding)) -print('\nElectro-optical transfer function from ' - 'colourspace to linear:\n{0}'.format(colourspace.cctf_decoding)) - -print('\n') +colourspace = colour.RGB_COLOURSPACES["ACES2065-1"] +print(f'Name:\n"{colourspace.name}"') +print(f"\nPrimaries:\n{colourspace.primaries}") +print( + f'\nNormalised primary matrix to "CIE XYZ" tristimulus values:\n' + f"{colourspace.matrix_RGB_to_XYZ}" +) +print( + f'\nNormalised primary matrix to "ACES2065-1":\n' + f"{colourspace.matrix_XYZ_to_RGB}" +) +print( + f"\nOpto-electronic transfer function from linear to colourspace:\n" + f"{colourspace.cctf_encoding}" +) +print( + f"\nElectro-optical transfer function from colourspace to linear:\n" + f"{colourspace.cctf_decoding}" +) + +print("\n") message_box( - ('Computing "ACES2065-1" colourspace to "ITU-R BT.709" colourspace ' - 'matrix.')) + 'Computing the "ACES2065-1" colourspace to "ITU-R BT.709" colourspace ' + "matrix." +) cat = colour.adaptation.matrix_chromatic_adaptation_VonKries( colour.xy_to_XYZ(colourspace.whitepoint), - colour.xy_to_XYZ(colour.RGB_COLOURSPACES['ITU-R BT.709'].whitepoint)) + colour.xy_to_XYZ(colour.RGB_COLOURSPACES["ITU-R BT.709"].whitepoint), +) print( - np.dot(colour.RGB_COLOURSPACES['ITU-R BT.709'].matrix_XYZ_to_RGB, - np.dot(cat, colourspace.matrix_RGB_to_XYZ))) + np.dot( + colour.RGB_COLOURSPACES["ITU-R BT.709"].matrix_XYZ_to_RGB, + np.dot(cat, colourspace.matrix_RGB_to_XYZ), + ) +) -print('\n') +print("\n") RGB = np.array([0.45620519, 0.03081071, 0.04091952]) message_box( - ('Converting from "ITU-R BT.709" colourspace to "ACEScg" colourspace ' - 'given "RGB" values:\n' - '\n\t{0}'.format(RGB))) + f'Converting from the "ITU-R BT.709" colourspace to the "ACEScg" ' + f'colourspace given "RGB" values:\n\n\t{RGB}' +) print( colour.RGB_to_RGB( RGB, - colour.RGB_COLOURSPACES['ITU-R BT.709'], - colour.RGB_COLOURSPACES['ACEScg'], - )) + colour.RGB_COLOURSPACES["ITU-R BT.709"], + colour.RGB_COLOURSPACES["ACEScg"], + ) +) diff --git a/colour/examples/models/examples_transfer_functions.py b/colour/examples/models/examples_transfer_functions.py index 1ba0e430d3..33021aed68 100644 --- a/colour/examples/models/examples_transfer_functions.py +++ b/colour/examples/models/examples_transfer_functions.py @@ -1,71 +1,72 @@ -# -*- coding: utf-8 -*- -""" -Showcases colour component transfer functions (CCTF) relates computations. -""" +"""Showcases colour component transfer functions (CCTF) relates computations.""" import colour from colour.utilities import message_box -message_box('Colour Component Transfer Functions (CCTF) Computations') +message_box("Colour Component Transfer Functions (CCTF) Computations") C = 18 / 100 -message_box(('Encoding to video component signal value using "BT.709" OETF ' - 'and given linear-light value:\n' - '\n\t{0}'.format(C))) -print(colour.oetf(C, function='ITU-R BT.709')) +message_box( + f'Encoding to video component signal value using "BT.709" OETF and given ' + f"linear-light value:\n\n\t{C}" +) +print(colour.oetf(C, function="ITU-R BT.709")) print(colour.models.oetf_BT709(C)) -print('\n') +print("\n") N = 0.40900773 -message_box(('Decoding to linear-light value using "BT.1886" EOTF and given ' - ' video component signal value:\n' - '\n\t{0}'.format(N))) -print(colour.eotf(N, function='ITU-R BT.1886')) +message_box( + f'Decoding to linear-light value using "BT.1886" EOTF and given video ' + f"component signal value:\n\n\t{N}" +) +print(colour.eotf(N, function="ITU-R BT.1886")) print(colour.models.eotf_BT1886(N)) -print('\n') +print("\n") -message_box(('Encoding to "Cineon" using given linear-light value:\n' - '\n\t{0}'.format(C))) -print(colour.log_encoding(C, function='Cineon')) +message_box(f'Encoding to "Cineon" using given linear-light value:\n\n\t{C}') +print(colour.log_encoding(C, function="Cineon")) print(colour.models.log_encoding_Cineon(C)) -print('\n') +print("\n") N = 0.45731961 -message_box(('Decoding to linear-light using given "Cineon" code value:\n' - '\n\t{0}'.format(N))) -print(colour.log_decoding(N, function='Cineon')) +message_box( + f'Decoding to linear-light using given "Cineon" code value:\n\n\t{N}' +) +print(colour.log_decoding(N, function="Cineon")) print(colour.models.log_decoding_Cineon(N)) -print('\n') +print("\n") -message_box(('Encoding to "PLog" using given linear-light value:\n' - '\n\t{0}'.format(C))) -print(colour.log_encoding(C, function='PLog')) +message_box(f'Encoding to "PLog" using given linear-light value:\n\n\t{C}') +print(colour.log_encoding(C, function="PLog")) print(colour.models.log_encoding_PivotedLog(C)) -print('\n') +print("\n") N = 0.43499511 -message_box(('Decoding to linear-light value using given "PLog" code value:\n' - '\n\t{0}'.format(N))) -print(colour.log_decoding(N, function='PLog')) +message_box( + f'Decoding to linear-light value using given "PLog" code value:\n\n\t{N}' +) +print(colour.log_decoding(N, function="PLog")) print(colour.models.log_decoding_PivotedLog(N)) -print('\n') +print("\n") -message_box(('Encoding to video component signal value using a pure gamma ' - 'function and given linear-light value:\n' - '\n\t{0}'.format(C))) +message_box( + f"Encoding to video component signal value using a pure gamma function " + f"and given linear-light value:\n\n\t{C}" +) print(colour.gamma_function(C, 1 / 2.2)) -print('\n') +print("\n") N = 0.45865645 -message_box(('Decoding to linear-light value using a pure gamma function and ' - 'given video component signal value:\n' - '\n\t{0}'.format(N))) +message_box( + f"Decoding to linear-light value using a pure gamma function and given " + f"video component signal value:\n\n\t{N}" +) print(colour.gamma_function(N, 2.2)) diff --git a/colour/examples/models/examples_ycbcr.py b/colour/examples/models/examples_ycbcr.py index 048a9a1e3e..2cb0376dac 100644 --- a/colour/examples/models/examples_ycbcr.py +++ b/colour/examples/models/examples_ycbcr.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *Y'CbCr* *colour encoding* computations. -""" +"""Showcases *Y'CbCr* *colour encoding* computations.""" import numpy as np @@ -12,55 +9,59 @@ RGB = np.array([0.45620519, 0.03081071, 0.04091952]) message_box( - ('Converting to "Y\'CbCr" colour encoding from given "ITU-R BT.709" ' - 'colourspace values:\n' - '\n\t{0}'.format(RGB))) + f'Converting to the "Y\'CbCr" colour encoding from given "ITU-R BT.709" ' + f"colourspace values:\n\n\t{RGB}" +) print(colour.RGB_to_YCbCr(RGB)) -print('\n') +print("\n") -message_box(('Converting to "Y\'CbCr" colour encoding from given' - '"ITU-R BT.601" colourspace values using legal range and integer ' - 'output:\n\n\t{0}'.format(RGB))) +message_box( + f'Converting to the "Y\'CbCr" colour encoding from given"ITU-R BT.601" ' + f"colourspace values using legal range and integer output:\n\n\t{RGB}" +) print( colour.RGB_to_YCbCr( - RGB, - colour.WEIGHTS_YCBCR['ITU-R BT.601'], - out_legal=True, - out_int=True)) + RGB, colour.WEIGHTS_YCBCR["ITU-R BT.601"], out_legal=True, out_int=True + ) +) -print('\n') +print("\n") YCbCr = np.array([101, 111, 124]) -message_box(('Converting to "ITU-R BT.601" colourspace from given "Y\'CbCr" ' - 'values using legal range and integer input:\n' - '\n\t{0}'.format(RGB))) +message_box( + f'Converting to the "ITU-R BT.601" colourspace from given "Y\'CbCr" ' + f"values using legal range and integer input:\n\n\t{RGB}" +) print(colour.YCbCr_to_RGB(YCbCr, in_legal=True, in_int=True)) -print('\n') +print("\n") RGB = np.array([0.18, 0.18, 0.18]) -message_box(('Converting to "Yc\'Cbc\'Crc\'" colour encoding from given ' - '"ITU-R BT.2020" values using legal range, integer output on ' - 'a 10-bit system:\n\n\t{0}'.format(RGB))) +message_box( + f"Converting to the \"Yc'Cbc'Crc'\" colour encoding from given " + f'"ITU-R BT.2020" values using legal range, integer output on a 10-bit ' + f"system:\n\n\t{RGB}" +) print( colour.RGB_to_YcCbcCrc( - RGB, - out_bits=10, - out_legal=True, - out_int=True, - is_12_bits_system=False)) + RGB, out_bits=10, out_legal=True, out_int=True, is_12_bits_system=False + ) +) -print('\n') +print("\n") YcCbcCrc = np.array([422, 512, 512]) -message_box(('Converting to "ITU-R BT.2020" colourspace from given "RGB" ' - 'values using legal range, integer input on a 10-bit system:\n' - '\n\t{0}'.format(RGB))) +message_box( + f'Converting to the "ITU-R BT.2020" colourspace from given "RGB" values ' + f"using legal range, integer input on a 10-bit system:\n\n\t{RGB}" +) print( colour.YcCbcCrc_to_RGB( YcCbcCrc, in_bits=10, in_legal=True, in_int=True, - is_12_bits_system=False)) + is_12_bits_system=False, + ) +) diff --git a/colour/examples/models/examples_ycocg.py b/colour/examples/models/examples_ycocg.py index 92261f3437..5c0e243a7f 100644 --- a/colour/examples/models/examples_ycocg.py +++ b/colour/examples/models/examples_ycocg.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *YCoCg* *colour encoding* computations. -""" +"""Showcases *YCoCg* *colour encoding* computations.""" import numpy as np @@ -11,16 +8,17 @@ message_box('"YCoCg" Colour Encoding Computations') RGB = np.array([0.45620519, 0.03081071, 0.04091952]) -message_box(('Converting to "YCoCg" colour encoding from given "RGB" ' - 'colourspace values:\n' - '\n\t{0}'.format(RGB))) +message_box( + f'Converting to the "YCoCg" colour encoding from given "RGB" colourspace ' + f"values:\n\n\t{RGB}" +) print(colour.RGB_to_YCoCg(RGB)) -print('\n') +print("\n") YCoCg = np.array([0.13968653, 0.20764283, -0.10887582]) message_box( - ('Converting to "RGB" colourspace values from "YCoCg" colour encoding ' - 'values:\n' - '\n\t{0}'.format(YCoCg))) + f'Converting to the "RGB" colourspace values from "YCoCg" colour encoding ' + f"values:\n\n\t{YCoCg}" +) print(colour.YCoCg_to_RGB(YCoCg)) diff --git a/colour/examples/notation/examples_hexadecimal.py b/colour/examples/notation/examples_hexadecimal.py index 56cc0298fd..0324cb2e9e 100644 --- a/colour/examples/notation/examples_hexadecimal.py +++ b/colour/examples/notation/examples_hexadecimal.py @@ -1,25 +1,24 @@ -# -*- coding: utf-8 -*- -""" -Showcases hexadecimal computations. -""" +"""Showcases hexadecimal computations.""" import numpy as np import colour.notation.hexadecimal from colour.utilities import message_box -message_box('Hexadecimal Computations') +message_box("Hexadecimal Computations") RGB = np.array([0.45620519, 0.03081071, 0.04091952]) -message_box(('Converting to "hexadecimal" representation from given "RGB" ' - 'colourspace values:\n' - '\n\t{0}'.format(RGB))) +message_box( + f'Converting to the "hexadecimal" representation from given "RGB"' + f"colourspace values:\n\n\t{RGB}" +) print(colour.notation.hexadecimal.RGB_to_HEX(RGB)) -print('\n') +print("\n") -hex_triplet = '#74070a' -message_box(('Converting to "RGB" colourspace from given "hexadecimal" ' - 'representation:\n' - '\n\t{0}'.format(hex_triplet))) +hex_triplet = "#74070a" +message_box( + f'Converting to the "RGB" colourspace from given "hexadecimal" ' + f"representation:\n\n\t{hex_triplet}" +) print(colour.notation.hexadecimal.HEX_to_RGB(hex_triplet)) diff --git a/colour/examples/notation/examples_munsell.py b/colour/examples/notation/examples_munsell.py index ecc20d3c03..48d4cebd5b 100644 --- a/colour/examples/notation/examples_munsell.py +++ b/colour/examples/notation/examples_munsell.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *Munsell Renotation System* computations. -""" +"""Showcases *Munsell Renotation System* computations.""" import numpy as np @@ -11,75 +8,82 @@ message_box('"Munsell Renotation System" Computations') Y = 12.23634268 -message_box(('Computing "Munsell" value using ' - '"Priest, Gibson and MacNicholas (1920)" method for given ' - '"luminance" value:\n' - '\n\t{0}'.format(Y))) -print(colour.munsell_value(Y, method='Priest 1920')) +message_box( + f'Computing "Munsell" value using "Priest, Gibson and MacNicholas (1920)" ' + f'method for given "luminance" value:\n\n\t{Y}' +) +print(colour.munsell_value(Y, method="Priest 1920")) print(colour.notation.munsell_value_Priest1920(Y)) -print('\n') +print("\n") -message_box(('Computing "Munsell" value using ' - '"Munsell, Sloan and Godlove (1933)" method for given ' - '"luminance" value:\n' - '\n\t{0}'.format(Y))) -print(colour.munsell_value(Y, method='Munsell 1933')) +message_box( + f'Computing "Munsell" value using "Munsell, Sloan and Godlove (1933)" ' + f'method for given "luminance" value:\n\n\t{Y}' +) +print(colour.munsell_value(Y, method="Munsell 1933")) print(colour.notation.munsell_value_Munsell1933(Y)) -print('\n') +print("\n") -message_box(('Computing "Munsell" value using "Moon and Spencer (1943)" ' - 'method for given "luminance" value:\n' - '\n\t{0}'.format(Y))) -print(colour.munsell_value(Y, method='Moon 1943')) +message_box( + f'Computing "Munsell" value using "Moon and Spencer (1943)" method for ' + f'given "luminance" value:\n\n\t{Y}' +) +print(colour.munsell_value(Y, method="Moon 1943")) print(colour.notation.munsell_value_Moon1943(Y)) -print('\n') +print("\n") -message_box(('Computing "Munsell" value using "Saunderson and Milner (1944)" ' - 'method for given "luminance" value:\n' - '\n\t{0}'.format(Y))) -print(colour.munsell_value(Y, method='Saunderson 1944')) +message_box( + f'Computing "Munsell" value using "Saunderson and Milner (1944)" method ' + f'for given "luminance" value:\n\n\t{Y}' +) +print(colour.munsell_value(Y, method="Saunderson 1944")) print(colour.notation.munsell_value_Saunderson1944(Y)) -print('\n') +print("\n") -message_box(('Computing "Munsell" value using "Ladd and Pinney (1955)" method ' - 'for given "luminance" value:\n' - '\n\t{0}'.format(Y))) -print(colour.munsell_value(Y, method='Ladd 1955')) +message_box( + f'Computing "Munsell" value using "Ladd and Pinney (1955)" method for ' + f'given "luminance" value:\n\n\t{Y}' +) +print(colour.munsell_value(Y, method="Ladd 1955")) print(colour.notation.munsell_value_Ladd1955(Y)) -print('\n') +print("\n") -message_box(('Computing "Munsell" value using "McCamy (1987)" method for ' - 'given "luminance" value:\n' - '\n\t{0}'.format(Y))) -print(colour.munsell_value(Y, method='McCamy 1987')) +message_box( + f'Computing "Munsell" value using "McCamy (1987)" method for given ' + f'"luminance" value:\n\n\t{Y}' +) +print(colour.munsell_value(Y, method="McCamy 1987")) print(colour.notation.munsell_value_McCamy1987(Y)) -print('\n') +print("\n") -message_box(('Computing "Munsell" value using "ASTM D1535-08e1" method ' - 'for given "luminance" value:\n' - '\n\t{0}'.format(Y))) -print(colour.munsell_value(Y, method='ASTM D1535')) +message_box( + f'Computing "Munsell" value using "ASTM D1535-08e1" method for given ' + f'"luminance" value:\n\n\t{Y}' +) +print(colour.munsell_value(Y, method="ASTM D1535")) print(colour.notation.munsell_value_ASTMD1535(Y)) -print('\n') +print("\n") xyY = np.array([0.38736945, 0.35751656, 0.59362000]) -message_box(('Converting to "Munsell" colour from given "CIE xyY" ' - 'colourspace values:\n' - '\n\t{0}'.format(xyY))) +message_box( + f'Converting to the "Munsell" colour from given "CIE xyY" colourspace ' + f"values:\n\n\t{xyY}" +) print(colour.xyY_to_munsell_colour(xyY)) -print('\n') +print("\n") -for munsell_colour in ('4.2YR 8.1/5.3', 'N8.9'): - message_box(('Converting to "CIE xyY" colourspace from given "Munsell" ' - 'colour:\n' - '\n\t{0}'.format(munsell_colour))) +for munsell_colour in ("4.2YR 8.1/5.3", "N8.9"): + message_box( + f'Converting to the "CIE xyY" colourspace from given "Munsell" ' + f"colour:\n\n\t{munsell_colour}" + ) print(colour.munsell_colour_to_xyY(munsell_colour)) - print('\n') + print("\n") diff --git a/colour/examples/phenomena/examples_rayleigh.py b/colour/examples/phenomena/examples_rayleigh.py index c7436a1e01..8c41f110d0 100644 --- a/colour/examples/phenomena/examples_rayleigh.py +++ b/colour/examples/phenomena/examples_rayleigh.py @@ -1,30 +1,30 @@ -# -*- coding: utf-8 -*- -""" -Showcases *Rayleigh Optical Depth* computations examples. -""" +"""Showcases *Rayleigh Optical Depth* computations examples.""" import colour from colour.utilities import message_box message_box('"Rayleigh" Optical Depth Computations') -message_box(('Creating a "Rayleigh" spectral distribution with default ' - 'spectral shape:\n' - '\n\t{0}'.format(colour.SPECTRAL_SHAPE_DEFAULT))) +message_box( + f'Creating a "Rayleigh" spectral distribution with default spectral ' + f"shape:\n\n\t{colour.SPECTRAL_SHAPE_DEFAULT}" +) sd_rayleigh = colour.sd_rayleigh_scattering() print(sd_rayleigh[555]) -print('\n') +print("\n") wavelength = 555 * 10e-8 -message_box(('Computing the scattering cross section per molecule at given ' - 'wavelength in cm:\n' - '\n\tWavelength: {0} cm'.format(wavelength))) +message_box( + f"Computing the scattering cross section per molecule at given wavelength " + f"in cm:\n\n\tWavelength: {wavelength}cm" +) print(colour.phenomena.scattering_cross_section(wavelength)) -print('\n') +print("\n") -message_box(('Computing the "Rayleigh" optical depth as function of ' - 'wavelength in cm:\n' - '\n\tWavelength: {0} cm'.format(wavelength))) +message_box( + f'Computing the "Rayleigh" optical depth as function of wavelength in ' + f"cm:\n\n\tWavelength: {wavelength}cm" +) print(colour.phenomena.rayleigh_optical_depth(wavelength)) diff --git a/colour/examples/plotting/examples_blindness.py b/colour/examples/plotting/examples_blindness.py index 91719f3867..682ea2fc2f 100644 --- a/colour/examples/plotting/examples_blindness.py +++ b/colour/examples/plotting/examples_blindness.py @@ -1,83 +1,87 @@ -# -*- coding: utf-8 -*- -""" -Showcases corresponding colour blindness plotting examples. -""" +"""Showcases corresponding colour blindness plotting examples.""" import numpy as np import os import colour -from colour.plotting import (colour_style, plot_cvd_simulation_Machado2009, - plot_image) +from colour.plotting import ( + colour_style, + plot_cvd_simulation_Machado2009, + plot_image, +) from colour.utilities.verbose import message_box RESOURCES_DIRECTORY = os.path.join( - os.path.dirname(os.path.abspath(__file__)), 'resources') + os.path.dirname(os.path.abspath(__file__)), "resources" +) colour_style() ISHIHARA_CBT_3_IMAGE = colour.cctf_decoding( colour.read_image( - os.path.join(RESOURCES_DIRECTORY, - 'Ishihara_Colour_Blindness_Test_Plate_3.png')), - function='sRGB') + os.path.join( + RESOURCES_DIRECTORY, "Ishihara_Colour_Blindness_Test_Plate_3.png" + ) + ), + function="sRGB", +) -message_box('Colour Blindness Plots') +message_box("Colour Blindness Plots") message_box('Displaying "Ishihara Colour Blindness Test - Plate 3".') plot_image( colour.cctf_encoding(ISHIHARA_CBT_3_IMAGE), - text_kwargs={ - 'text': 'Normal Trichromat', - 'color': 'black' - }) + text_kwargs={"text": "Normal Trichromat", "color": "black"}, +) -print('\n') +print("\n") -message_box('Simulating average "Protanomaly" on ' - '"Ishihara Colour Blindness Test - Plate 3" with Machado (2010) ' - 'model and pre-computed matrix.') +message_box( + 'Simulating average "Protanomaly" on ' + '"Ishihara Colour Blindness Test - Plate 3" with Machado (2010) model and ' + "pre-computed matrix." +) plot_cvd_simulation_Machado2009( ISHIHARA_CBT_3_IMAGE, - 'Protanomaly', + "Protanomaly", 0.5, - text_kwargs={ - 'text': 'Protanomaly - 50%', - 'color': 'black' - }) + text_kwargs={"text": "Protanomaly - 50%", "color": "black"}, +) -print('\n') +print("\n") M_a = colour.matrix_anomalous_trichromacy_Machado2009( - colour.MSDS_CMFS['Stockman & Sharpe 2 Degree Cone Fundamentals'], - colour.MSDS_DISPLAY_PRIMARIES['Typical CRT Brainard 1997'], - np.array([10, 0, 0])) -message_box('Simulating average "Protanomaly" on ' - '"Ishihara Colour Blindness Test - Plate 3" with Machado (2010) ' - 'model using "Stockman & Sharpe 2 Degree Cone Fundamentals" and ' - '"Typical CRT Brainard 1997" "RGB" display primaries.') + colour.MSDS_CMFS["Stockman & Sharpe 2 Degree Cone Fundamentals"], + colour.MSDS_DISPLAY_PRIMARIES["Typical CRT Brainard 1997"], + np.array([10, 0, 0]), +) +message_box( + 'Simulating average "Protanomaly" on ' + '"Ishihara Colour Blindness Test - Plate 3" with Machado (2010) model ' + 'using "Stockman & Sharpe 2 Degree Cone Fundamentals" and ' + '"Typical CRT Brainard 1997" "RGB" display primaries.' +) plot_cvd_simulation_Machado2009( ISHIHARA_CBT_3_IMAGE, M_a=M_a, - text_kwargs={ - 'text': 'Average Protanomaly - 10nm', - 'color': 'black' - }) + text_kwargs={"text": "Average Protanomaly - 10nm", "color": "black"}, +) -print('\n') +print("\n") M_a = colour.matrix_anomalous_trichromacy_Machado2009( - colour.MSDS_CMFS['Stockman & Sharpe 2 Degree Cone Fundamentals'], - colour.MSDS_DISPLAY_PRIMARIES['Typical CRT Brainard 1997'], - np.array([20, 0, 0])) -message_box('Simulating "Protanopia" on ' - '"Ishihara Colour Blindness Test - Plate 3" with Machado (2010) ' - 'model using "Stockman & Sharpe 2 Degree Cone Fundamentals" and ' - '"Typical CRT Brainard 1997" "RGB" display primaries.') + colour.MSDS_CMFS["Stockman & Sharpe 2 Degree Cone Fundamentals"], + colour.MSDS_DISPLAY_PRIMARIES["Typical CRT Brainard 1997"], + np.array([20, 0, 0]), +) +message_box( + 'Simulating "Protanopia" on ' + '"Ishihara Colour Blindness Test - Plate 3" with Machado (2010) model ' + 'using "Stockman & Sharpe 2 Degree Cone Fundamentals" and ' + '"Typical CRT Brainard 1997" "RGB" display primaries.' +) plot_cvd_simulation_Machado2009( ISHIHARA_CBT_3_IMAGE, M_a=M_a, - text_kwargs={ - 'text': 'Protanopia - 20nm', - 'color': 'black' - }) + text_kwargs={"text": "Protanopia - 20nm", "color": "black"}, +) diff --git a/colour/examples/plotting/examples_characterisation_plots.py b/colour/examples/plotting/examples_characterisation_plots.py index 08693bc5d2..769d82bf94 100644 --- a/colour/examples/plotting/examples_characterisation_plots.py +++ b/colour/examples/plotting/examples_characterisation_plots.py @@ -1,35 +1,38 @@ -# -*- coding: utf-8 -*- -""" -Showcases characterisation plotting examples. -""" +"""Showcases characterisation plotting examples.""" from pprint import pprint import colour -from colour.plotting import (colour_style, plot_single_colour_checker, - plot_multi_sds) +from colour.plotting import ( + colour_style, + plot_single_colour_checker, + plot_multi_sds, +) from colour.utilities import message_box -message_box('Characterisation Plots') +message_box("Characterisation Plots") colour_style() -message_box('Plotting colour rendition charts.') +message_box("Plotting colour rendition charts.") pprint(sorted(colour.CCS_COLOURCHECKERS.keys())) -plot_single_colour_checker('ColorChecker 1976') +plot_single_colour_checker("ColorChecker 1976") plot_single_colour_checker( - 'BabelColor Average', text_kwargs={'visible': False}) -plot_single_colour_checker('ColorChecker 1976', text_kwargs={'visible': False}) -plot_single_colour_checker('ColorChecker 2005', text_kwargs={'visible': False}) + "BabelColor Average", text_kwargs={"visible": False} +) +plot_single_colour_checker("ColorChecker 1976", text_kwargs={"visible": False}) +plot_single_colour_checker("ColorChecker 2005", text_kwargs={"visible": False}) -print('\n') +print("\n") -message_box(('Plotting "BabelColor Average" colour rendition charts spectral ' - 'distributions.')) +message_box( + 'Plotting "BabelColor Average" colour rendition charts spectral ' + "distributions." +) plot_multi_sds( - colour.SDS_COLOURCHECKERS['BabelColor Average'].values(), - title=('BabelColor Average - ' - 'Spectral Distributions'), + colour.SDS_COLOURCHECKERS["BabelColor Average"].values(), + title=("BabelColor Average - " "Spectral Distributions"), plot_kwargs={ - 'use_sd_colours': True, - }) + "use_sd_colours": True, + }, +) diff --git a/colour/examples/plotting/examples_colorimetry_plots.py b/colour/examples/plotting/examples_colorimetry_plots.py index 1282a4c0c2..176e02ba7a 100644 --- a/colour/examples/plotting/examples_colorimetry_plots.py +++ b/colour/examples/plotting/examples_colorimetry_plots.py @@ -1,57 +1,68 @@ -# -*- coding: utf-8 -*- -""" -Showcases colorimetry plotting examples. -""" +"""Showcases colorimetry plotting examples.""" from pprint import pprint import colour from colour.plotting import ( - SD_ASTMG173_ETR, plot_blackbody_colours, plot_blackbody_spectral_radiance, - colour_style, plot_multi_cmfs, plot_multi_illuminant_sds, - plot_multi_lightness_functions, plot_multi_sds, plot_single_cmfs, - plot_single_illuminant_sd, plot_single_lightness_function, plot_single_sd, - plot_visible_spectrum) + SD_ASTMG173_ETR, + plot_blackbody_colours, + plot_blackbody_spectral_radiance, + colour_style, + plot_multi_cmfs, + plot_multi_illuminant_sds, + plot_multi_lightness_functions, + plot_multi_sds, + plot_single_cmfs, + plot_single_illuminant_sd, + plot_single_lightness_function, + plot_single_sd, + plot_visible_spectrum, +) from colour.utilities import message_box -message_box('Colorimetry Plots') +message_box("Colorimetry Plots") colour_style() -message_box('Plotting a single illuminant spectral ' 'distribution.') -plot_single_illuminant_sd('FL1') +message_box("Plotting a single illuminant spectral " "distribution.") +plot_single_illuminant_sd("FL1") -print('\n') +print("\n") -message_box('Plotting multiple illuminants spectral distributions.') +message_box("Plotting multiple illuminants spectral distributions.") pprint(sorted(colour.SDS_ILLUMINANTS.keys())) plot_multi_illuminant_sds( - ['A', 'B', 'C', 'D50', 'D55', 'D60', 'D65', 'D75', 'FL1']) + ["A", "B", "C", "D50", "D55", "D60", "D65", "D75", "FL1"] +) -print('\n') +print("\n") -message_box(('Plotting "CIE Standard Illuminant "A", "B", and "C" with their ' - 'normalised colours.')) +message_box( + 'Plotting "CIE Standard Illuminant "A", "B", and "C" with their ' + "normalised colours." +) plot_multi_illuminant_sds( - ['A', 'B', 'C'], - plot_kwargs={ - 'use_sd_colours': True, - 'normalise_sd_colours': True - }) + ["A", "B", "C"], + plot_kwargs={"use_sd_colours": True, "normalise_sd_colours": True}, +) -print('\n') +print("\n") -message_box(('Plotting "CIE Standard Illuminant D Series" "S" spectral ' - 'distributions.')) +message_box( + 'Plotting "CIE Standard Illuminant D Series" "S" spectral ' + "distributions." +) plot_multi_sds( [ - value for key, value in - sorted(colour.colorimetry.SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES. - items()) + value + for key, value in sorted( + colour.colorimetry.SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES.items() + ) ], - title='CIE Standard Illuminant D Series - S Distributions') + title="CIE Standard Illuminant D Series - S Distributions", +) -print('\n') +print("\n") data_sample = { 380: 0.048, @@ -134,7 +145,7 @@ 765: 0.465, 770: 0.448, 775: 0.432, - 780: 0.421 + 780: 0.421, } # http://speclib.jpl.nasa.gov/speclibdata/ @@ -361,7 +372,7 @@ 796: 9.31, 798: 9.26, 800: 9.21, - 820: 8.59 + 820: 8.59, } # http://speclib.jpl.nasa.gov/speclibdata/ @@ -588,26 +599,32 @@ 796: 84.31, 798: 84.34, 800: 84.34, - 820: 84.47 + 820: 84.47, } -message_box('Plotting various single spectral distributions.') -plot_single_sd(colour.SpectralDistribution(data_sample, name='Custom')) +message_box("Plotting various single spectral distributions.") +plot_single_sd(colour.SpectralDistribution(data_sample, name="Custom")) plot_single_sd( colour.SpectralDistribution( - data_galvanized_steel_metal, name='Galvanized Steel Metal')) + data_galvanized_steel_metal, name="Galvanized Steel Metal" + ) +) -print('\n') +print("\n") -message_box('Plotting multiple spectral distributions.') -plot_multi_sds((colour.SpectralDistribution( - data_galvanized_steel_metal, name='Galvanized Steel Metal'), - colour.SpectralDistribution( - data_white_marble, name='White Marble'))) +message_box("Plotting multiple spectral distributions.") +plot_multi_sds( + ( + colour.SpectralDistribution( + data_galvanized_steel_metal, name="Galvanized Steel Metal" + ), + colour.SpectralDistribution(data_white_marble, name="White Marble"), + ) +) -print('\n') +print("\n") -message_box('Plotting spectral bandpass dependence correction.') +message_box("Plotting spectral bandpass dependence correction.") data_street_light = { 380: 8.9770000e-003, 382: 5.8380000e-003, @@ -809,144 +826,172 @@ 774: 9.2280000e-002, 776: 9.0480000e-002, 778: 9.0020000e-002, - 780: 8.8190000e-002 + 780: 8.8190000e-002, } sd_street_light = colour.SpectralDistribution( - data_street_light, name='Street Light') + data_street_light, name="Street Light" +) sd_bandpass_corrected_street_light = sd_street_light.copy() -sd_bandpass_corrected_street_light.name = 'Street Light (Bandpass Corrected)' +sd_bandpass_corrected_street_light.name = "Street Light (Bandpass Corrected)" sd_bandpass_corrected_street_light = colour.bandpass_correction( - sd_bandpass_corrected_street_light, method='Stearns 1988') + sd_bandpass_corrected_street_light, method="Stearns 1988" +) plot_multi_sds( (sd_street_light, sd_bandpass_corrected_street_light), - title='Stearns Bandpass Correction') + title="Stearns Bandpass Correction", +) -print('\n') +print("\n") message_box('Plotting a single "cone fundamentals" colour matching functions.') plot_single_cmfs( - 'Stockman & Sharpe 2 Degree Cone Fundamentals', - y_label='Sensitivity', - bounding_box=(390, 870, 0, 1.1)) + "Stockman & Sharpe 2 Degree Cone Fundamentals", + y_label="Sensitivity", + bounding_box=(390, 870, 0, 1.1), +) -print('\n') +print("\n") message_box('Plotting multiple "cone fundamentals" colour matching functions.') plot_multi_cmfs( [ - 'Stockman & Sharpe 2 Degree Cone Fundamentals', - 'Stockman & Sharpe 10 Degree Cone Fundamentals' + "Stockman & Sharpe 2 Degree Cone Fundamentals", + "Stockman & Sharpe 10 Degree Cone Fundamentals", ], - y_label='Sensitivity', - bounding_box=(390, 870, 0, 1.1)) + y_label="Sensitivity", + bounding_box=(390, 870, 0, 1.1), +) -print('\n') +print("\n") -message_box('Plotting various single colour matching functions.') +message_box("Plotting various single colour matching functions.") pprint(sorted(colour.MSDS_CMFS.keys())) -plot_single_cmfs('CIE 1931 2 Degree Standard Observer') -plot_single_cmfs('CIE 1964 10 Degree Standard Observer') +plot_single_cmfs("CIE 1931 2 Degree Standard Observer") +plot_single_cmfs("CIE 1964 10 Degree Standard Observer") plot_single_cmfs( - 'Stiles & Burch 1955 2 Degree RGB CMFs', - bounding_box=(390, 830, -0.5, 3.5)) + "Stiles & Burch 1955 2 Degree RGB CMFs", bounding_box=(390, 830, -0.5, 3.5) +) plot_single_cmfs( - 'Stiles & Burch 1959 10 Degree RGB CMFs', - bounding_box=(390, 830, -0.5, 3.5)) - -print('\n') - -message_box('Comparing various colour matching functions.') -plot_multi_cmfs([ - 'CIE 1931 2 Degree Standard Observer', - 'CIE 1964 10 Degree Standard Observer' -]) -plot_multi_cmfs([ - 'CIE 2012 10 Degree Standard Observer', - 'CIE 1964 10 Degree Standard Observer' -]) -plot_multi_cmfs([ - 'Wright & Guild 1931 2 Degree RGB CMFs', - 'Stiles & Burch 1955 2 Degree RGB CMFs' -]) - -print('\n') - -message_box('Plotting visible colours under given standard observer.') -plot_visible_spectrum('CIE 1931 2 Degree Standard Observer') -plot_visible_spectrum('CIE 2012 2 Degree Standard Observer') - -print('\n') - -message_box('Plotting photopic luminous efficiency functions.') + "Stiles & Burch 1959 10 Degree RGB CMFs", + bounding_box=(390, 830, -0.5, 3.5), +) + +print("\n") + +message_box("Comparing various colour matching functions.") +plot_multi_cmfs( + [ + "CIE 1931 2 Degree Standard Observer", + "CIE 1964 10 Degree Standard Observer", + ] +) +plot_multi_cmfs( + [ + "CIE 2012 10 Degree Standard Observer", + "CIE 1964 10 Degree Standard Observer", + ] +) +plot_multi_cmfs( + [ + "Wright & Guild 1931 2 Degree RGB CMFs", + "Stiles & Burch 1955 2 Degree RGB CMFs", + ] +) + +print("\n") + +message_box("Plotting visible colours under given standard observer.") +plot_visible_spectrum("CIE 1931 2 Degree Standard Observer") +plot_visible_spectrum("CIE 2012 2 Degree Standard Observer") + +print("\n") + +message_box("Plotting photopic luminous efficiency functions.") plot_multi_sds( colour.colorimetry.SDS_LEFS_PHOTOPIC.values(), - title='Luminous Efficiency Functions', - y_label='Luminous Efficiency') + title="Luminous Efficiency Functions", + y_label="Luminous Efficiency", +) -print('\n') +print("\n") -message_box('Comparing photopic and scotopic luminous efficiency functions.') +message_box("Comparing photopic and scotopic luminous efficiency functions.") LEF_PHOTOPIC = colour.colorimetry.SDS_LEFS_PHOTOPIC[ - 'CIE 2008 2 Degree Physiologically Relevant LEF'] + "CIE 2008 2 Degree Physiologically Relevant LEF" +] LEF_SCOTOPIC = colour.colorimetry.SDS_LEFS_SCOTOPIC[ - 'CIE 1951 Scotopic Standard Observer'] + "CIE 1951 Scotopic Standard Observer" +] plot_multi_sds( (LEF_PHOTOPIC, LEF_SCOTOPIC), - title='Photopic & Scotopic Luminous Efficiency Functions', - y_label='Luminous Efficiency') + title="Photopic & Scotopic Luminous Efficiency Functions", + y_label="Luminous Efficiency", +) -print('\n') +print("\n") -message_box(('Plotting a mesopic luminous efficiency function with given ' - 'photopic luminance value:\n' - '\n\t0.2')) +message_box( + "Plotting a mesopic luminous efficiency function with given photopic " + "luminance value:\n\n\t0.2" +) sd_mesopic_luminous_efficiency_function = ( - colour.sd_mesopic_luminous_efficiency_function(0.2)) + colour.sd_mesopic_luminous_efficiency_function(0.2) +) plot_multi_sds( - (sd_mesopic_luminous_efficiency_function, colour.colorimetry. - SDS_LEFS_PHOTOPIC['CIE 1924 Photopic Standard Observer'], colour. - colorimetry.SDS_LEFS_SCOTOPIC['CIE 1951 Scotopic Standard Observer']), - y_label='Luminous Efficiency') - -print('\n') + ( + sd_mesopic_luminous_efficiency_function, + colour.colorimetry.SDS_LEFS_PHOTOPIC[ + "CIE 1924 Photopic Standard Observer" + ], + colour.colorimetry.SDS_LEFS_SCOTOPIC[ + "CIE 1951 Scotopic Standard Observer" + ], + ), + y_label="Luminous Efficiency", +) + +print("\n") message_box('Plotting a single "Lightness" function.') -plot_single_lightness_function('CIE 1976') +plot_single_lightness_function("CIE 1976") -print('\n') +print("\n") message_box('Plotting multiple "Lightness" functions.') -plot_multi_lightness_functions(['CIE 1976', 'Glasser 1958']) +plot_multi_lightness_functions(["CIE 1976", "Glasser 1958"]) -print('\n') +print("\n") -message_box('Plotting various blackbody spectral radiance.') +message_box("Plotting various blackbody spectral radiance.") plot_blackbody_spectral_radiance( - temperature=3500, blackbody='VY Canis Majoris') -plot_blackbody_spectral_radiance(temperature=5778, blackbody='The Sun') -plot_blackbody_spectral_radiance(temperature=12130, blackbody='Rigel') + temperature=3500, blackbody="VY Canis Majoris" +) +plot_blackbody_spectral_radiance(temperature=5778, blackbody="The Sun") +plot_blackbody_spectral_radiance(temperature=12130, blackbody="Rigel") -print('\n') +print("\n") message_box('Comparing theoretical and measured "Sun" spectral distributions.') sd_ASTMG173 = SD_ASTMG173_ETR.copy() sd_ASTMG173.interpolate( - colour.SpectralShape(interval=5), interpolator=colour.LinearInterpolator) + colour.SpectralShape(sd_ASTMG173.shape.start, sd_ASTMG173.shape.end, 5), + interpolator=colour.LinearInterpolator, +) sd_blackbody = colour.sd_blackbody(5778, sd_ASTMG173.shape) -sd_blackbody.name = 'The Sun - 5778K' +sd_blackbody.name = "The Sun - 5778K" sd_blackbody /= colour.sd_to_XYZ(sd_blackbody)[1] sd_blackbody *= colour.sd_to_XYZ(sd_ASTMG173)[1] -plot_multi_sds([sd_ASTMG173, sd_blackbody], y_label='W / (sr m$^2$) / m') +plot_multi_sds([sd_ASTMG173, sd_blackbody], y_label="W / (sr m$^2$) / m") -print('\n') +print("\n") message_box('Plotting various "blackbody" spectral distributions.') sds_blackbody = [ @@ -956,13 +1001,11 @@ plot_multi_sds( sds_blackbody, - y_label='W / (sr m$^2$) / m', - plot_kwargs={ - 'use_sd_colours': True, - 'normalise_sd_colours': True - }) + y_label="W / (sr m$^2$) / m", + plot_kwargs={"use_sd_colours": True, "normalise_sd_colours": True}, +) -print('\n') +print("\n") message_box('Plotting "blackbody" colours.') plot_blackbody_colours() diff --git a/colour/examples/plotting/examples_common_plots.py b/colour/examples/plotting/examples_common_plots.py index 260c2be9e1..7ad13a163a 100644 --- a/colour/examples/plotting/examples_common_plots.py +++ b/colour/examples/plotting/examples_common_plots.py @@ -1,27 +1,30 @@ -# -*- coding: utf-8 -*- -""" -Showcases common plotting examples. -""" +"""Showcases common plotting examples.""" -from colour.plotting import (ColourSwatch, colour_style, - plot_multi_colour_swatches, - plot_single_colour_swatch) +from colour.plotting import ( + ColourSwatch, + colour_style, + plot_multi_colour_swatches, + plot_single_colour_swatch, +) from colour.utilities import message_box -message_box('Common Plots') +message_box("Common Plots") colour_style() -message_box('Plotting a single colour.') +message_box("Plotting a single colour.") plot_single_colour_swatch( - ColourSwatch( - 'Neutral 5 (.70 D)', RGB=(0.32315746, 0.32983556, 0.33640183)), - text_size=32) + ColourSwatch((0.32315746, 0.32983556, 0.33640183), "Neutral 5 (.70 D)"), + text_size=32, +) -print('\n') +print("\n") -message_box('Plotting multiple colours.') +message_box("Plotting multiple colours.") plot_multi_colour_swatches( - (ColourSwatch('Dark Skin', RGB=(0.45293517, 0.31732158, 0.26414773)), - ColourSwatch('Light Skin', RGB=(0.77875824, 0.57726450, 0.50453169))), - text_size=32) + ( + ColourSwatch((0.45293517, 0.31732158, 0.26414773), "Dark Skin"), + ColourSwatch((0.77875824, 0.57726450, 0.50453169), "Light Skin"), + ), + text_size=32, +) diff --git a/colour/examples/plotting/examples_corresponding.py b/colour/examples/plotting/examples_corresponding.py index f35ddabb26..a54f4bcd5c 100644 --- a/colour/examples/plotting/examples_corresponding.py +++ b/colour/examples/plotting/examples_corresponding.py @@ -1,24 +1,33 @@ -# -*- coding: utf-8 -*- -""" -Showcases corresponding chromaticities prediction plotting examples. -""" +"""Showcases corresponding chromaticities prediction plotting examples.""" -from colour.plotting import (colour_style, - plot_corresponding_chromaticities_prediction) +from colour.plotting import ( + colour_style, + plot_corresponding_chromaticities_prediction, +) from colour.utilities import message_box -message_box('Corresponding Chromaticities Prediction Plots') +message_box("Corresponding Chromaticities Prediction Plots") colour_style() -message_box('Plotting corresponding chromaticities prediction with ' - '"Von Kries" chromatic adaptation model for "Breneman (1987)" ' - 'experiment number "2" using "Bianco" CAT.') -plot_corresponding_chromaticities_prediction(2, 'Von Kries', 'Bianco 2010') +message_box( + 'Plotting corresponding chromaticities prediction with "Von Kries" ' + 'chromatic adaptation model for "Breneman (1987)" experiment number "2" ' + 'using "Bianco" CAT.' +) +plot_corresponding_chromaticities_prediction( + 2, + "Von Kries", + corresponding_chromaticities_prediction_kwargs={ + "transform": "Bianco 2010" + }, +) -print('\n') +print("\n") -message_box('Plotting corresponding chromaticities prediction with ' - '"CMCCAT200" chromatic adaptation model for "Breneman (1987)" ' - 'experiment number "4" using "Bianco" CAT.') -plot_corresponding_chromaticities_prediction(4, 'CMCCAT2000') +message_box( + 'Plotting corresponding chromaticities prediction with "CMCCAT200" ' + 'chromatic adaptation model for "Breneman (1987)" experiment number "4" ' + 'using "Bianco" CAT.' +) +plot_corresponding_chromaticities_prediction(4, "CMCCAT2000") diff --git a/colour/examples/plotting/examples_diagrams_plots.py b/colour/examples/plotting/examples_diagrams_plots.py index 332bec8604..38edc31da2 100644 --- a/colour/examples/plotting/examples_diagrams_plots.py +++ b/colour/examples/plotting/examples_diagrams_plots.py @@ -1,15 +1,15 @@ -# -*- coding: utf-8 -*- -""" -Showcases *CIE* chromaticity diagrams plotting examples. -""" +"""Showcases *CIE* chromaticity diagrams plotting examples.""" from colour import SDS_ILLUMINANTS -from colour.plotting import (colour_style, plot_chromaticity_diagram_CIE1931, - plot_chromaticity_diagram_CIE1960UCS, - plot_chromaticity_diagram_CIE1976UCS, - plot_sds_in_chromaticity_diagram_CIE1931, - plot_sds_in_chromaticity_diagram_CIE1960UCS, - plot_sds_in_chromaticity_diagram_CIE1976UCS) +from colour.plotting import ( + colour_style, + plot_chromaticity_diagram_CIE1931, + plot_chromaticity_diagram_CIE1960UCS, + plot_chromaticity_diagram_CIE1976UCS, + plot_sds_in_chromaticity_diagram_CIE1931, + plot_sds_in_chromaticity_diagram_CIE1960UCS, + plot_sds_in_chromaticity_diagram_CIE1976UCS, +) from colour.utilities import message_box message_box('"CIE" Chromaticity Diagrams Plots') @@ -19,38 +19,41 @@ message_box('Plotting "CIE 1931 Chromaticity Diagram".') plot_chromaticity_diagram_CIE1931() -print('\n') +print("\n") message_box('Plotting "CIE 1960 UCS Chromaticity Diagram".') plot_chromaticity_diagram_CIE1960UCS() -print('\n') +print("\n") message_box('Plotting "CIE 1976 UCS Chromaticity Diagram".') plot_chromaticity_diagram_CIE1976UCS() -print('\n') +print("\n") -message_box(('Plotting "CIE Standard Illuminant A" and ' - '"CIE Standard Illuminant D65" spectral ' - 'distribution chromaticity coordinates in ' - '"CIE 1931 Chromaticity Diagram".')) -sd_A = SDS_ILLUMINANTS['A'] -sd_D65 = SDS_ILLUMINANTS['D65'] +message_box( + 'Plotting "CIE Standard Illuminant A" and "CIE Standard Illuminant D65" ' + "spectral distribution chromaticity coordinates in " + '"CIE 1931 Chromaticity Diagram".' +) +sd_A = SDS_ILLUMINANTS["A"] +sd_D65 = SDS_ILLUMINANTS["D65"] plot_sds_in_chromaticity_diagram_CIE1931((sd_A, sd_D65)) -print('\n') +print("\n") -message_box(('Plotting "CIE Standard Illuminant A" and ' - '"CIE Standard Illuminant D65" spectral ' - 'distribution chromaticity coordinates in ' - '"CIE 1960 UCS Chromaticity Diagram".')) +message_box( + 'Plotting "CIE Standard Illuminant A" and "CIE Standard Illuminant D65" ' + "spectral distribution chromaticity coordinates in " + '"CIE 1960 UCS Chromaticity Diagram".' +) plot_sds_in_chromaticity_diagram_CIE1960UCS((sd_A, sd_D65)) -print('\n') +print("\n") -message_box(('Plotting "CIE Standard Illuminant A" and ' - '"CIE Standard Illuminant D65" spectral ' - 'distribution chromaticity coordinates in ' - '"CIE 1976 UCS Chromaticity Diagram".')) +message_box( + 'Plotting "CIE Standard Illuminant A" and "CIE Standard Illuminant D65" ' + "spectral distribution chromaticity coordinates in " + '"CIE 1976 UCS Chromaticity Diagram".' +) plot_sds_in_chromaticity_diagram_CIE1976UCS((sd_A, sd_D65)) diff --git a/colour/examples/plotting/examples_models_plots.py b/colour/examples/plotting/examples_models_plots.py index 95e449b540..b8dee5f8a1 100644 --- a/colour/examples/plotting/examples_models_plots.py +++ b/colour/examples/plotting/examples_models_plots.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases colour models plotting examples. -""" +"""Showcases colour models plotting examples.""" import numpy as np from pprint import pprint @@ -13,95 +10,128 @@ plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS, plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931, plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS, - plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS, colour_style, - plot_multi_cctfs, plot_single_cctf) + plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS, + colour_style, + plot_multi_cctfs, + plot_single_cctf, +) from colour.utilities import message_box -message_box('Colour Models Plots') +message_box("Colour Models Plots") colour_style() -message_box('Plotting "RGB" colourspaces in "CIE 1931 Chromaticity Diagram".') +message_box( + 'Plotting "RGB" colourspaces in the ' '"CIE 1931 Chromaticity Diagram".' +) pprint(sorted(colour.RGB_COLOURSPACES.keys())) plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931( - ['ITU-R BT.709', 'ACEScg', 'S-Gamut'], show_pointer_gamut=True) + ["ITU-R BT.709", "ACEScg", "S-Gamut"], show_pointer_gamut=True +) -print('\n') +print("\n") -message_box(('Plotting "RGB" colourspaces in ' - '"CIE 1960 UCS Chromaticity Diagram".')) +message_box( + 'Plotting "RGB" colourspaces in the ' + '"CIE 1960 UCS Chromaticity Diagram".' +) pprint(sorted(colour.RGB_COLOURSPACES.keys())) plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS( - ['ITU-R BT.709', 'ACEScg', 'S-Gamut'], show_pointer_gamut=True) + ["ITU-R BT.709", "ACEScg", "S-Gamut"], show_pointer_gamut=True +) -print('\n') +print("\n") -message_box(('Plotting "RGB" colourspaces in ' - '"CIE 1976 UCS Chromaticity Diagram".')) +message_box( + 'Plotting "RGB" colourspaces in the ' + '"CIE 1976 UCS Chromaticity Diagram".' +) pprint(sorted(colour.RGB_COLOURSPACES.keys())) plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS( - ['ITU-R BT.709', 'ACEScg', 'S-Gamut'], show_pointer_gamut=True) + ["ITU-R BT.709", "ACEScg", "S-Gamut"], show_pointer_gamut=True +) -print('\n') +print("\n") RGB = np.random.random((32, 32, 3)) -message_box('Plotting "RGB" chromaticity coordinates in ' - '"CIE 1931 Chromaticity Diagram".') +message_box( + 'Plotting "RGB" chromaticity coordinates in the ' + '"CIE 1931 Chromaticity Diagram".' +) plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931( RGB, - 'ITU-R BT.709', - colourspaces=['ACEScg', 'S-Gamut'], - show_pointer_gamut=True) + "ITU-R BT.709", + colourspaces=["ACEScg", "S-Gamut"], + show_pointer_gamut=True, +) -print('\n') +print("\n") -message_box('Plotting "RGB" chromaticity coordinates in ' - '"CIE 1960 UCS Chromaticity Diagram".') +message_box( + 'Plotting "RGB" chromaticity coordinates in the ' + '"CIE 1960 UCS Chromaticity Diagram".' +) plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS( RGB, - 'ITU-R BT.709', - colourspaces=['ACEScg', 'S-Gamut'], - show_pointer_gamut=True) + "ITU-R BT.709", + colourspaces=["ACEScg", "S-Gamut"], + show_pointer_gamut=True, +) -print('\n') +print("\n") -message_box('Plotting "RGB" chromaticity coordinates in ' - '"CIE 1976 UCS Chromaticity Diagram".') +message_box( + 'Plotting "RGB" chromaticity coordinates in the ' + '"CIE 1976 UCS Chromaticity Diagram".' +) plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS( RGB, - 'ITU-R BT.709', - colourspaces=['ACEScg', 'S-Gamut'], - show_pointer_gamut=True) + "ITU-R BT.709", + colourspaces=["ACEScg", "S-Gamut"], + show_pointer_gamut=True, +) -print('\n') +print("\n") -message_box(('Plotting a single custom "RGB" colourspace in ' - '"CIE 1931 Chromaticity Diagram".')) +message_box( + 'Plotting a single custom "RGB" colourspace in the ' + '"CIE 1931 Chromaticity Diagram".' +) AWFUL_RGB = colour.RGB_Colourspace( - 'Awful RGB', - primaries=np.array([ - [0.10, 0.20], - [0.30, 0.15], - [0.05, 0.60], - ]), - whitepoint=np.array([1.0 / 3.0, 1.0 / 3.0])) + "Awful RGB", + primaries=np.array( + [ + [0.10, 0.20], + [0.30, 0.15], + [0.05, 0.60], + ] + ), + whitepoint=np.array([1.0 / 3.0, 1.0 / 3.0]), +) pprint(sorted(colour.RGB_COLOURSPACES.keys())) plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931( - ['ITU-R BT.709', AWFUL_RGB]) - -print('\n') - -message_box(('Plotting a single "RGB" colourspace encoding colour component ' - 'transfer function.')) -plot_single_cctf('ITU-R BT.709') - -print('\n') - -message_box(('Plotting multiple "RGB" colourspaces encoding colour component ' - 'transfer functions.')) -plot_multi_cctfs(['ITU-R BT.709', 'sRGB']) - -message_box(('Plotting multiple "RGB" colourspaces decoding colour component ' - 'transfer functions.')) -plot_multi_cctfs(['ACES2065-1', 'ProPhoto RGB'], cctf_decoding=True) + ["ITU-R BT.709", AWFUL_RGB] +) + +print("\n") + +message_box( + 'Plotting a single "RGB" colourspace encoding colour component transfer ' + "function." +) +plot_single_cctf("ITU-R BT.709") + +print("\n") + +message_box( + 'Plotting multiple "RGB" colourspaces encoding colour component transfer ' + "functions." +) +plot_multi_cctfs(["ITU-R BT.709", "sRGB"]) + +message_box( + 'Plotting multiple "RGB" colourspaces decoding colour component transfer ' + "functions." +) +plot_multi_cctfs(["ACES2065-1", "ProPhoto RGB"], cctf_decoding=True) diff --git a/colour/examples/plotting/examples_notation_plots.py b/colour/examples/plotting/examples_notation_plots.py index 0805bbc884..aae23a899d 100644 --- a/colour/examples/plotting/examples_notation_plots.py +++ b/colour/examples/plotting/examples_notation_plots.py @@ -1,20 +1,20 @@ -# -*- coding: utf-8 -*- -""" -Showcases colour notation systems plotting examples. -""" +"""Showcases colour notation systems plotting examples.""" -from colour.plotting import (colour_style, plot_multi_munsell_value_functions, - plot_single_munsell_value_function) +from colour.plotting import ( + colour_style, + plot_multi_munsell_value_functions, + plot_single_munsell_value_function, +) from colour.utilities import message_box -message_box('Colour Notation Systems Plots') +message_box("Colour Notation Systems Plots") colour_style() message_box('Plotting a single "Munsell" value function.') -plot_single_munsell_value_function('Ladd 1955') +plot_single_munsell_value_function("Ladd 1955") -print('\n') +print("\n") message_box('Plotting multiple "Munsell" value functions.') -plot_multi_munsell_value_functions(['Ladd 1955', 'Saunderson 1944']) +plot_multi_munsell_value_functions(["Ladd 1955", "Saunderson 1944"]) diff --git a/colour/examples/plotting/examples_phenomena_plots.py b/colour/examples/plotting/examples_phenomena_plots.py index 0f80d7c7dd..bddbe12740 100644 --- a/colour/examples/plotting/examples_phenomena_plots.py +++ b/colour/examples/plotting/examples_phenomena_plots.py @@ -1,27 +1,28 @@ -# -*- coding: utf-8 -*- -""" -Showcases optical phenomena plotting examples. -""" +"""Showcases optical phenomena plotting examples.""" from colour.phenomena import sd_rayleigh_scattering -from colour.plotting import (colour_style, plot_multi_sds, - plot_single_sd_rayleigh_scattering, - plot_the_blue_sky) +from colour.plotting import ( + colour_style, + plot_multi_sds, + plot_single_sd_rayleigh_scattering, + plot_the_blue_sky, +) from colour.utilities import message_box -message_box('Optical Phenomena Plots') +message_box("Optical Phenomena Plots") colour_style() -message_box(('Plotting a single "Rayleigh" scattering spectral ' - 'distribution.')) +message_box('Plotting a single "Rayleigh" scattering spectral "distribution."') plot_single_sd_rayleigh_scattering() -print('\n') +print("\n") -message_box(('Comparing multiple "Rayleigh" scattering spectral ' - 'distributions with different CO_2 concentrations.')) -name_template = 'Rayleigh Scattering - CO2: {0} ppm' +message_box( + 'Comparing multiple "Rayleigh" scattering spectral distributions with ' + "different CO_2 concentrations." +) +name_template = "Rayleigh Scattering - CO2: {0} ppm" sds_rayleigh = [] for ppm in (0, 50, 300): sd_rayleigh = sd_rayleigh_scattering(CO2_concentration=ppm) @@ -29,11 +30,13 @@ sds_rayleigh.append(sd_rayleigh) plot_multi_sds( sds_rayleigh, - title=('Rayleigh Optical Depth - ' - 'Comparing "C02" Concentration Influence'), - y_label='Optical Depth') + title=( + "Rayleigh Optical Depth - " 'Comparing "C02" Concentration Influence' + ), + y_label="Optical Depth", +) -print('\n') +print("\n") message_box('Plotting "The Blue Sky".') plot_the_blue_sky() diff --git a/colour/examples/plotting/examples_quality_plots.py b/colour/examples/plotting/examples_quality_plots.py index 881e93fcdf..1af266349a 100644 --- a/colour/examples/plotting/examples_quality_plots.py +++ b/colour/examples/plotting/examples_quality_plots.py @@ -1,42 +1,51 @@ -# -*- coding: utf-8 -*- -""" -Showcases colour quality plotting examples. -""" +"""Showcases colour quality plotting examples.""" import colour -from colour.plotting import (colour_style, - plot_multi_sds_colour_quality_scales_bars, - plot_multi_sds_colour_rendering_indexes_bars, - plot_single_sd_colour_quality_scale_bars, - plot_single_sd_colour_rendering_index_bars) +from colour.plotting import ( + colour_style, + plot_multi_sds_colour_quality_scales_bars, + plot_multi_sds_colour_rendering_indexes_bars, + plot_single_sd_colour_quality_scale_bars, + plot_single_sd_colour_rendering_index_bars, +) from colour.utilities import message_box -message_box('Colour Quality Plots') +message_box("Colour Quality Plots") colour_style() message_box('Plotting "F2" illuminant "Colour Rendering Index (CRI)".') -plot_single_sd_colour_rendering_index_bars(colour.SDS_ILLUMINANTS['FL2']) +plot_single_sd_colour_rendering_index_bars(colour.SDS_ILLUMINANTS["FL2"]) -print('\n') +print("\n") -message_box(('Plotting various illuminants and light sources ' - '"Colour Rendering Index (CRI)".')) +message_box( + "Plotting various illuminants and light sources " + '"Colour Rendering Index (CRI)".' +) plot_multi_sds_colour_rendering_indexes_bars( - (colour.SDS_ILLUMINANTS['FL2'], - colour.SDS_LIGHT_SOURCES['F32T8/TL841 (Triphosphor)'], - colour.SDS_LIGHT_SOURCES['Kinoton 75P'])) + ( + colour.SDS_ILLUMINANTS["FL2"], + colour.SDS_LIGHT_SOURCES["F32T8/TL841 (Triphosphor)"], + colour.SDS_LIGHT_SOURCES["Kinoton 75P"], + ) +) -print('\n') +print("\n") message_box('Plotting "F2" illuminant "Colour Quality Scale (CQS)".') -plot_single_sd_colour_quality_scale_bars(colour.SDS_ILLUMINANTS['FL2']) +plot_single_sd_colour_quality_scale_bars(colour.SDS_ILLUMINANTS["FL2"]) -print('\n') +print("\n") -message_box(('Plotting various illuminants and light sources ' - '"Colour Quality Scale (CQS)".')) +message_box( + "Plotting various illuminants and light sources " + '"Colour Quality Scale (CQS)".' +) plot_multi_sds_colour_quality_scales_bars( - (colour.SDS_ILLUMINANTS['FL2'], - colour.SDS_LIGHT_SOURCES['F32T8/TL841 (Triphosphor)'], - colour.SDS_LIGHT_SOURCES['Kinoton 75P'])) + ( + colour.SDS_ILLUMINANTS["FL2"], + colour.SDS_LIGHT_SOURCES["F32T8/TL841 (Triphosphor)"], + colour.SDS_LIGHT_SOURCES["Kinoton 75P"], + ) +) diff --git a/colour/examples/plotting/examples_section_plots.py b/colour/examples/plotting/examples_section_plots.py new file mode 100644 index 0000000000..6eb1775d79 --- /dev/null +++ b/colour/examples/plotting/examples_section_plots.py @@ -0,0 +1,138 @@ +"""Showcases gamut section plotting examples.""" + +import numpy as np +from matplotlib.lines import Line2D + +import colour.plotting +from colour.plotting import ( + colour_style, + plot_visible_spectrum_section, + plot_RGB_colourspace_section, +) +from colour.utilities import message_box + +message_box("Gamut Section Plots") + +colour_style() + +message_box( + 'Plotting a "Visible Spectrum" section at 50% "Lightness" in the ' + '"CIE Luv" colourspace.' +) + +plot_visible_spectrum_section( + model="CIE Luv", + origin=0.5, +) + +print("\n") + +message_box( + 'Plotting a "Visible Spectrum" section at 50% "Lightness" in the ' + '"CIE Luv" colourspace and customising the section styling.' +) + +plot_visible_spectrum_section( + model="CIE Luv", origin=0.5, section_colours="RGB", section_opacity=0.15 +) + +print("\n") + +message_box( + 'Plotting a "Visible Spectrum" section at 50% "Lightness" in the ' + '"CIE Luv" colourspace.' +) + +plot_visible_spectrum_section(model="CIE Luv", origin=0.5) + +print("\n") + +message_box( + 'Plotting a "Visible Spectrum" section at 25% along the "u" axis in the ' + '"CIE Luv" colourspace.' +) + +plot_visible_spectrum_section( + model="CIE Luv", + axis="+x", + origin=0.25, + section_colours="RGB", + section_opacity=0.15, +) + +print("\n") + +message_box( + 'Plotting a "sRGB" colourspace section at 50% "Lightness" in the ' + '"ICtCp" colourspace using section normalisation.' +) + +plot_RGB_colourspace_section( + colourspace="sRGB", model="ICtCp", origin=0.5, normalise=True +) + +print("\n") + +message_box( + 'Combining multiple hull sections together at 25% "Lightness" in the ' + '"Oklab" colourspace.' +) + +figure, axes = plot_visible_spectrum_section( + model="Oklab", origin=0.25, section_opacity=0.15, standalone=False +) +plot_RGB_colourspace_section( + colourspace="sRGB", + model="Oklab", + origin=0.25, + section_colours="RGB", + section_opacity=0.15, + contour_colours="RGB", + axes=axes, +) + +print("\n") + +message_box( + 'Combining multiple hull sections together at varying "Lightness" in the ' + '"DIN99" colourspace.' +) + +figure, axes = plot_visible_spectrum_section( + model="DIN99", origin=0.5, section_opacity=0.15, standalone=False +) + +bounding_box = [ + axes.get_xlim()[0], + axes.get_xlim()[1], + axes.get_ylim()[0], + axes.get_ylim()[1], +] + +section_colours = colour.notation.HEX_to_RGB( + colour.plotting.CONSTANTS_COLOUR_STYLE.colour.cycle[:4] +) + +origins = [] +legend_lines = [] +for i, RGB in zip(np.arange(0.5, 0.9, 0.1), section_colours): + origins.append(i * 100) + plot_RGB_colourspace_section( + colourspace="sRGB", + model="DIN99", + origin=i, + section_colours=RGB, + section_opacity=0.15, + contour_colours=RGB, + axes=axes, + standalone=False, + ) + legend_lines.append(Line2D([0], [0], color=RGB, label=f"{i * 100}%")) + +axes.legend(handles=legend_lines) + +colour.plotting.render( + title=f"Visible Spectrum - 50% - sRGB Sections - {origins}% - DIN99", + axes=axes, + bounding_box=bounding_box, +) diff --git a/colour/examples/plotting/examples_temperature_plots.py b/colour/examples/plotting/examples_temperature_plots.py index 2c3eccdce8..b6cddc3936 100644 --- a/colour/examples/plotting/examples_temperature_plots.py +++ b/colour/examples/plotting/examples_temperature_plots.py @@ -1,22 +1,25 @@ -# -*- coding: utf-8 -*- """ Showcases colour temperature and correlated colour temperature plotting examples. """ from colour.plotting import ( - colour_style, plot_planckian_locus_in_chromaticity_diagram_CIE1931, - plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS) + colour_style, + plot_planckian_locus_in_chromaticity_diagram_CIE1931, + plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS, +) from colour.utilities import message_box -message_box('Colour Temperature and Correlated Colour Temperature Plots') +message_box("Colour Temperature and Correlated Colour Temperature Plots") colour_style() -message_box('Plotting planckian locus in "CIE 1931 Chromaticity Diagram".') -plot_planckian_locus_in_chromaticity_diagram_CIE1931(['A', 'B', 'C']) +message_box('Plotting planckian locus in the "CIE 1931 Chromaticity Diagram".') +plot_planckian_locus_in_chromaticity_diagram_CIE1931(["A", "B", "C"]) -print('\n') +print("\n") -message_box('Plotting planckian locus in "CIE 1960 UCS Chromaticity Diagram".') -plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS(['A', 'B', 'C']) +message_box( + 'Plotting planckian locus in the"CIE 1960 UCS Chromaticity Diagram".' +) +plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS(["A", "B", "C"]) diff --git a/colour/examples/plotting/examples_tm3018.py b/colour/examples/plotting/examples_tm3018.py index bce18789c7..2780da4c7d 100644 --- a/colour/examples/plotting/examples_tm3018.py +++ b/colour/examples/plotting/examples_tm3018.py @@ -1,29 +1,29 @@ -# -*- coding: utf-8 -*- -""" -Showcases *ANSI/IES TM-30-18 Colour Rendition Report* plotting examples. -""" +"""Showcases *ANSI/IES TM-30-18 Colour Rendition Report* plotting examples.""" import colour -from colour.plotting import (plot_single_sd_colour_rendition_report, - colour_style) +from colour.plotting import ( + plot_single_sd_colour_rendition_report, + colour_style, +) from colour.utilities import message_box -message_box('ANSI/IES TM-30-18 Colour Rendition Report') +message_box("ANSI/IES TM-30-18 Colour Rendition Report") colour_style() -sd = colour.SDS_ILLUMINANTS['FL2'] +sd = colour.SDS_ILLUMINANTS["FL2"] message_box('Plotting a full "ANSI/IES TM-30-18 Colour Rendition Report".') plot_single_sd_colour_rendition_report(sd) -print('\n') +print("\n") message_box( - 'Plotting an intermediate "ANSI/IES TM-30-18 Colour Rendition Report".') -plot_single_sd_colour_rendition_report(sd, 'Intermediate') + 'Plotting an intermediate "ANSI/IES TM-30-18 Colour Rendition Report".' +) +plot_single_sd_colour_rendition_report(sd, "Intermediate") -print('\n') +print("\n") message_box('Plotting a simple "ANSI/IES TM-30-18 Colour Rendition Report".') -plot_single_sd_colour_rendition_report(sd, 'Simple') +plot_single_sd_colour_rendition_report(sd, "Simple") diff --git a/colour/examples/plotting/examples_volume_plots.py b/colour/examples/plotting/examples_volume_plots.py index fa042291b6..2d0e89b085 100644 --- a/colour/examples/plotting/examples_volume_plots.py +++ b/colour/examples/plotting/examples_volume_plots.py @@ -1,47 +1,54 @@ -# -*- coding: utf-8 -*- -""" -Showcases colour models volume and gamut plotting examples. -""" +"""Showcases colour models volume and gamut plotting examples.""" import numpy as np -from colour.plotting import (plot_RGB_colourspaces_gamuts, plot_RGB_scatter, - colour_style) +from colour.plotting import ( + plot_RGB_colourspaces_gamuts, + plot_RGB_scatter, + colour_style, +) from colour.utilities import message_box -message_box('Colour Models Volume and Gamut Plots') +message_box("Colour Models Volume and Gamut Plots") colour_style() -message_box(('Plotting "ITU-R BT.709" RGB colourspace volume in "CIE xyY" ' - 'colourspace.')) +message_box( + 'Plotting "ITU-R BT.709" RGB colourspace volume in the "CIE xyY" ' + "colourspace." +) plot_RGB_colourspaces_gamuts( - ('ITU-R BT.709', ), reference_colourspace='CIE xyY') + ("ITU-R BT.709",), reference_colourspace="CIE xyY" +) -print('\n') +print("\n") -message_box(('Comparing "ITU-R BT.709" and "ACEScg" RGB colourspaces volume ' - 'in "CIE L*a*b*" colourspace.')) +message_box( + 'Comparing "ITU-R BT.709" and "ACEScg" RGB colourspaces volume in the ' + '"CIE L*a*b*" colourspace.' +) plot_RGB_colourspaces_gamuts( - ('ITU-R BT.709', 'ACEScg'), - reference_colourspace='CIE Lab', + ("ITU-R BT.709", "ACEScg"), + reference_colourspace="CIE Lab", face_colours=(None, (0.25, 0.25, 0.25)), edge_colours=(None, (0.25, 0.25, 0.25)), edge_alpha=(1.0, 0.1), - face_alpha=(1.0, 0.0)) + face_alpha=(1.0, 0.0), +) -print('\n') +print("\n") -message_box(('Plotting "ACEScg" colourspaces values in "CIE L*a*b*" ' - 'colourspace.')) +message_box( + 'Plotting "ACEScg" colourspaces values in the "CIE L*a*b*" colourspace.' +) RGB = np.random.random((32, 32, 3)) plot_RGB_scatter( RGB, - 'ACEScg', - reference_colourspace='CIE Lab', - colourspaces=('ACEScg', 'ITU-R BT.709'), + "ACEScg", + reference_colourspace="CIE Lab", + colourspaces=("ACEScg", "ITU-R BT.709"), face_colours=((0.25, 0.25, 0.25), None), edge_colours=((0.25, 0.25, 0.25), None), edge_alpha=(0.1, 0.5), @@ -49,4 +56,5 @@ grid_face_colours=(0.1, 0.1, 0.1), grid_edge_colours=(0.1, 0.1, 0.1), grid_edge_alpha=0.5, - grid_face_alpha=0.1) + grid_face_alpha=0.1, +) diff --git a/colour/examples/quality/examples_cfi.py b/colour/examples/quality/examples_cfi.py index bf1851b1b4..75b38fa6e4 100644 --- a/colour/examples/quality/examples_cfi.py +++ b/colour/examples/quality/examples_cfi.py @@ -1,60 +1,75 @@ -# -*- coding: utf-8 -*- -""" -Showcases *Colour Fidelity Index* (CFI) computations. -""" +"""Showcases *Colour Fidelity Index* (CFI) computations.""" from pprint import pprint import colour from colour.utilities import message_box -message_box('Colour Fidelity Index Computations') +message_box("Colour Fidelity Index Computations") message_box('Computing "F2" illuminant "Colour Fidelity Index (CFI)".') -print(colour.colour_fidelity_index(colour.SDS_ILLUMINANTS['FL2'])) +print(colour.colour_fidelity_index(colour.SDS_ILLUMINANTS["FL2"])) print( colour.colour_fidelity_index( - colour.SDS_ILLUMINANTS['FL2'], method='CIE 2017')) + colour.SDS_ILLUMINANTS["FL2"], method="CIE 2017" + ) +) print( - colour.quality.colour_fidelity_index_CIE2017( - colour.SDS_ILLUMINANTS['FL2'])) + colour.quality.colour_fidelity_index_CIE2017(colour.SDS_ILLUMINANTS["FL2"]) +) print( colour.colour_fidelity_index( - colour.SDS_ILLUMINANTS['FL2'], method='ANSI/IES TM-30-18')) + colour.SDS_ILLUMINANTS["FL2"], method="ANSI/IES TM-30-18" + ) +) print( colour.quality.colour_fidelity_index_ANSIIESTM3018( - colour.SDS_ILLUMINANTS['FL2'])) + colour.SDS_ILLUMINANTS["FL2"] + ) +) -print('\n') +print("\n") -message_box(('Computing "F2" illuminant "Colour Fidelity Index" (CFI) with ' - 'detailed output data.')) +message_box( + 'Computing "F2" illuminant "Colour Fidelity Index" (CFI) with detailed ' + "output data." +) pprint( colour.colour_fidelity_index( - colour.SDS_ILLUMINANTS['FL2'], additional_data=True)) + colour.SDS_ILLUMINANTS["FL2"], additional_data=True + ) +) print( colour.colour_fidelity_index( - colour.SDS_ILLUMINANTS['FL2'], additional_data=True, - method='CIE 2017')) + colour.SDS_ILLUMINANTS["FL2"], additional_data=True, method="CIE 2017" + ) +) print( colour.quality.colour_fidelity_index_CIE2017( - colour.SDS_ILLUMINANTS['FL2'], additional_data=True)) + colour.SDS_ILLUMINANTS["FL2"], additional_data=True + ) +) print( colour.colour_fidelity_index( - colour.SDS_ILLUMINANTS['FL2'], + colour.SDS_ILLUMINANTS["FL2"], additional_data=True, - method='ANSI/IES TM-30-18')) + method="ANSI/IES TM-30-18", + ) +) print( colour.quality.colour_fidelity_index_ANSIIESTM3018( - colour.SDS_ILLUMINANTS['FL2'], additional_data=True)) + colour.SDS_ILLUMINANTS["FL2"], additional_data=True + ) +) -print('\n') +print("\n") -message_box('Computing "CIE Standard Illuminant A" ' - '"Colour Fidelity Index (CFI)".') -print(colour.colour_fidelity_index(colour.SDS_ILLUMINANTS['A'])) +message_box( + 'Computing "CIE Standard Illuminant A" "Colour Fidelity Index (CFI)".' +) +print(colour.colour_fidelity_index(colour.SDS_ILLUMINANTS["A"])) -print('\n') +print("\n") message_box('Computing sample light "Colour Fidelity Index (CFI)".') data_sample = { @@ -138,9 +153,11 @@ 765: 0.00340568, 770: 0.00261153, 775: 0.00258850, - 780: 0.00293663 + 780: 0.00293663, } print( colour.colour_fidelity_index( - colour.SpectralDistribution(data_sample, name='Sample'))) + colour.SpectralDistribution(data_sample, name="Sample") + ) +) diff --git a/colour/examples/quality/examples_cqs.py b/colour/examples/quality/examples_cqs.py index da407cc7a7..012aed1bd0 100644 --- a/colour/examples/quality/examples_cqs.py +++ b/colour/examples/quality/examples_cqs.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases *Colour Quality Scale* (CQS) computations. -""" +"""Showcases *Colour Quality Scale* (CQS) computations.""" from pprint import pprint @@ -10,28 +7,35 @@ message_box('"Colour Quality Scale (CQS)" Computations') -message_box('Computing "F2" illuminant "Colour Quality Scale (CQS)".') -print(colour.colour_quality_scale(colour.SDS_ILLUMINANTS['FL2'])) +message_box('Computing the "F2" illuminant "Colour Quality Scale (CQS)".') +print(colour.colour_quality_scale(colour.SDS_ILLUMINANTS["FL2"])) -print('\n') +print("\n") -message_box(('Computing "H38HT-100" mercury lamp "Colour Quality Scale (CQS)" ' - 'with detailed output data.')) +message_box( + 'Computing the "H38HT-100" mercury lamp "Colour Quality Scale (CQS)" with ' + "detailed output data." +) pprint( colour.colour_quality_scale( - colour.SDS_LIGHT_SOURCES['H38HT-100 (Mercury)'], additional_data=True)) + colour.SDS_LIGHT_SOURCES["H38HT-100 (Mercury)"], additional_data=True + ) +) -print('\n') +print("\n") -message_box('Computing "SDW-T 100W/LV Super HPS" lamp ' - '"Colour Quality Scale (CQS)".') +message_box( + 'Computing the "SDW-T 100W/LV Super HPS" lamp "Colour Quality Scale (CQS)".' +) print( colour.colour_quality_scale( - colour.SDS_LIGHT_SOURCES['SDW-T 100W/LV (Super HPS)'])) + colour.SDS_LIGHT_SOURCES["SDW-T 100W/LV (Super HPS)"] + ) +) -print('\n') +print("\n") -message_box('Computing sample light "Colour Quality Scale (CQS)".') +message_box('Computing the sample light "Colour Quality Scale (CQS)".') data_sample = { 380: 0.00588346, 385: 0.00315377, @@ -113,9 +117,11 @@ 765: 0.00340568, 770: 0.00261153, 775: 0.00258850, - 780: 0.00293663 + 780: 0.00293663, } print( colour.colour_quality_scale( - colour.SpectralDistribution(data_sample, name='Sample'))) + colour.SpectralDistribution(data_sample, name="Sample") + ) +) diff --git a/colour/examples/quality/examples_cri.py b/colour/examples/quality/examples_cri.py index 5391a19508..ce1ad5bdf1 100644 --- a/colour/examples/quality/examples_cri.py +++ b/colour/examples/quality/examples_cri.py @@ -1,35 +1,37 @@ -# -*- coding: utf-8 -*- -""" -Showcases *Colour Rendering Index* (CRI) computations. -""" +"""Showcases *Colour Rendering Index* (CRI) computations.""" from pprint import pprint import colour from colour.utilities import message_box -message_box('Colour Rendering Index Computations') +message_box("Colour Rendering Index Computations") -message_box('Computing "F2" illuminant "Colour Rendering Index (CRI)".') -print(colour.colour_rendering_index(colour.SDS_ILLUMINANTS['FL2'])) +message_box('Computing the "F2" illuminant "Colour Rendering Index (CRI)".') +print(colour.colour_rendering_index(colour.SDS_ILLUMINANTS["FL2"])) -print('\n') +print("\n") -message_box(('Computing "F2" illuminant "Colour Rendering Index" (CRI) with ' - 'detailed output data.')) +message_box( + 'Computing the "F2" illuminant "Colour Rendering Index" (CRI) with ' + "detailed output data." +) pprint( colour.colour_rendering_index( - colour.SDS_ILLUMINANTS['FL2'], additional_data=True)) + colour.SDS_ILLUMINANTS["FL2"], additional_data=True + ) +) -print('\n') +print("\n") -message_box('Computing "CIE Standard Illuminant A" ' - '"Colour Rendering Index (CRI)".') -print(colour.colour_rendering_index(colour.SDS_ILLUMINANTS['A'])) +message_box( + 'Computing the "CIE Standard Illuminant A" "Colour Rendering Index (CRI)".' +) +print(colour.colour_rendering_index(colour.SDS_ILLUMINANTS["A"])) -print('\n') +print("\n") -message_box('Computing sample light "Colour Rendering Index (CRI)".') +message_box('Computing the sample light "Colour Rendering Index (CRI)".') data_sample = { 380: 0.00588346, 385: 0.00315377, @@ -111,9 +113,11 @@ 765: 0.00340568, 770: 0.00261153, 775: 0.00258850, - 780: 0.00293663 + 780: 0.00293663, } print( colour.colour_rendering_index( - colour.SpectralDistribution(data_sample, name='Sample'))) + colour.SpectralDistribution(data_sample, name="Sample") + ) +) diff --git a/colour/examples/quality/examples_ssi.py b/colour/examples/quality/examples_ssi.py index db9406f985..a416ad78e8 100644 --- a/colour/examples/quality/examples_ssi.py +++ b/colour/examples/quality/examples_ssi.py @@ -1,16 +1,16 @@ -# -*- coding: utf-8 -*- -""" -Showcases *Academy Spectral Similarity Index* (SSI) computations. -""" +"""Showcases *Academy Spectral Similarity Index* (SSI) computations.""" import colour from colour.utilities import message_box -message_box('Academy Spectral Similarity Index Computations') +message_box("Academy Spectral Similarity Index Computations") message_box( - 'Computing "CIE Illuminant B" "Academy Spectral Similarity Index (SSI)" ' - 'against "CIE Standard Illuminant D65".') + 'Computing the "CIE Illuminant B" "Academy Spectral Similarity Index (SSI)" ' + 'with "CIE Standard Illuminant D65".' +) print( - colour.spectral_similarity_index(colour.SDS_ILLUMINANTS['B'], - colour.SDS_ILLUMINANTS['D65'])) + colour.spectral_similarity_index( + colour.SDS_ILLUMINANTS["B"], colour.SDS_ILLUMINANTS["D65"] + ) +) diff --git a/colour/examples/recovery/examples_jakob2019.py b/colour/examples/recovery/examples_jakob2019.py index f23a99d1a5..d9fe7a88b4 100644 --- a/colour/examples/recovery/examples_jakob2019.py +++ b/colour/examples/recovery/examples_jakob2019.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases reflectance recovery computations using *Jakob et al. (2019)* method. -""" +"""Showcases reflectance recovery computations using *Jakob et al. (2019)* method.""" import numpy as np @@ -10,21 +7,24 @@ message_box('"Jakob et al. (2019)" - Reflectance Recovery Computations') -illuminant = colour.SDS_ILLUMINANTS['D65'] +illuminant = colour.SDS_ILLUMINANTS["D65"] XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) -message_box('Recovering reflectance using "Jakob et al. (2019)" method from ' - 'given "XYZ" tristimulus values:\n' - '\n\tXYZ: {0}'.format(XYZ)) -sd = colour.XYZ_to_sd(XYZ, method='Jakob 2019') +message_box( + f'Recovering reflectance using "Jakob et al. (2019)" method from given ' + f'"XYZ" tristimulus values:\n\n\tXYZ: {XYZ}' +) +sd = colour.XYZ_to_sd(XYZ, method="Jakob 2019") print(sd) print(colour.recovery.XYZ_to_sd_Jakob2019(XYZ)) print(colour.sd_to_XYZ(sd, illuminant=illuminant) / 100) -print('\n') +print("\n") -message_box('Generating a LUT according to "Jakob et al. (2019)" method ' - 'for "sRGB" colourspace:') +message_box( + 'Generating a LUT according to the "Jakob et al. (2019)" method for the ' + '"sRGB" colourspace:' +) LUT = colour.recovery.LUT3D_Jakob2019() LUT.generate(colour.models.RGB_COLOURSPACE_sRGB, size=5) RGB = np.array([0.70573936, 0.19248266, 0.22354169]) diff --git a/colour/examples/recovery/examples_mallet2019.py b/colour/examples/recovery/examples_mallet2019.py index c3834c3a27..466cc635f3 100644 --- a/colour/examples/recovery/examples_mallet2019.py +++ b/colour/examples/recovery/examples_mallet2019.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Showcases reflectance recovery computations using *Mallett et al. (2019)* method. @@ -11,31 +10,37 @@ message_box('"Mallett et al. (2019)" - Reflectance Recovery Computations') -illuminant = colour.SDS_ILLUMINANTS['D65'] +illuminant = colour.SDS_ILLUMINANTS["D65"] XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) RGB = colour.XYZ_to_sRGB(XYZ, apply_cctf_encoding=False) -message_box('Recovering reflectance using "Mallett et al. (2019)" method ' - 'from given "XYZ" tristimulus values:\n' - '\n\tXYZ: {0}'.format(XYZ)) -sd = colour.XYZ_to_sd(XYZ, method='Mallett 2019') +message_box( + f'Recovering reflectance using "Mallett et al. (2019)" method from given ' + f'"XYZ" tristimulus values:\n\n\tXYZ: {XYZ}' +) +sd = colour.XYZ_to_sd(XYZ, method="Mallett 2019") print(sd) print(colour.recovery.RGB_to_sd_Mallett2019(RGB)) print(colour.sd_to_XYZ(sd, illuminant=illuminant) / 100) -print('\n') +print("\n") -message_box('Generating the "Mallett et al. (2019)" basis functions for the ' - '*Pal/Secam* colourspace:') -cmfs = (colour.MSDS_CMFS['CIE 1931 2 Degree Standard Observer'].copy().align( - colour.SpectralShape(360, 780, 10))) -illuminant = colour.SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) +message_box( + 'Generating the "Mallett et al. (2019)" basis functions for the ' + "*Pal/Secam* colourspace:" +) +cmfs = ( + colour.MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] + .copy() + .align(colour.SpectralShape(360, 780, 10)) +) +illuminant = colour.SDS_ILLUMINANTS["D65"].copy().align(cmfs.shape) print( colour.recovery.spectral_primary_decomposition_Mallett2019( colour.models.RGB_COLOURSPACE_PAL_SECAM, cmfs, illuminant, - optimisation_kwargs={'options': { - 'ftol': 1e-5 - }})) + optimisation_kwargs={"options": {"ftol": 1e-5}}, + ) +) diff --git a/colour/examples/recovery/examples_meng2015.py b/colour/examples/recovery/examples_meng2015.py index edc64c667b..06aa2f1902 100644 --- a/colour/examples/recovery/examples_meng2015.py +++ b/colour/examples/recovery/examples_meng2015.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases reflectance recovery computations using *Meng et al. (2015)* method. -""" +"""Showcases reflectance recovery computations using *Meng et al. (2015)* method.""" import numpy as np @@ -10,13 +7,14 @@ message_box('"Meng et al. (2015)" - Reflectance Recovery Computations') -illuminant = colour.SDS_ILLUMINANTS['D65'] +illuminant = colour.SDS_ILLUMINANTS["D65"] XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) -message_box('Recovering reflectance using "Meng et al. (2015)" method from ' - 'given "XYZ" tristimulus values:\n' - '\n\tXYZ: {0}'.format(XYZ)) -sd = colour.XYZ_to_sd(XYZ, method='Meng 2015') +message_box( + f'Recovering reflectance using "Meng et al. (2015)" method from given ' + f'"XYZ" tristimulus values:\n\n\tXYZ: {XYZ}' +) +sd = colour.XYZ_to_sd(XYZ, method="Meng 2015") print(sd) print(colour.recovery.XYZ_to_sd_Meng2015(XYZ)) print(colour.sd_to_XYZ(sd, illuminant=illuminant) / 100) diff --git a/colour/examples/recovery/examples_otsu2018.py b/colour/examples/recovery/examples_otsu2018.py index 769da3125b..0a6e99ecd5 100644 --- a/colour/examples/recovery/examples_otsu2018.py +++ b/colour/examples/recovery/examples_otsu2018.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases reflectance recovery computations using *Otsu et al. (2018)* method. -""" +"""Showcases reflectance recovery computations using *Otsu et al. (2018)* method.""" import numpy as np @@ -10,28 +7,29 @@ message_box('"Otsu et al. (2018)" - Reflectance Recovery Computations') -illuminant = colour.SDS_ILLUMINANTS['D65'] +illuminant = colour.SDS_ILLUMINANTS["D65"] XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) -message_box('Recovering reflectance using "Otsu et al. (2018)" method from ' - 'given "XYZ" tristimulus values:\n' - '\n\tXYZ: {0}'.format(XYZ)) -sd = colour.XYZ_to_sd(XYZ, method='Otsu 2018') +message_box( + f'Recovering reflectance using "Otsu et al. (2018)" method from given ' + f'"XYZ" tristimulus values:\n\n\tXYZ: {XYZ}' +) +sd = colour.XYZ_to_sd(XYZ, method="Otsu 2018") print(sd) print(colour.recovery.XYZ_to_sd_Otsu2018(XYZ)) print(colour.sd_to_XYZ(sd, illuminant=illuminant) / 100) -print('\n') +print("\n") -message_box('Generating a spectral dataset according to "Otsu et al. (2018)"' - 'method :') +message_box( + 'Generating a spectral dataset according to the "Otsu et al. (2018) "' + "method :" +) XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) -reflectances = [ - reflectance.copy().align(colour.recovery.SPECTRAL_SHAPE_OTSU2018).values - for reflectance in colour.SDS_COLOURCHECKERS['ColorChecker N Ohta'] - .values() -] -node_tree = colour.recovery.NodeTree_Otsu2018(reflectances) +reflectances = colour.colorimetry.sds_and_msds_to_msds( + colour.SDS_COLOURCHECKERS["ColorChecker N Ohta"].values() +).align(colour.recovery.SPECTRAL_SHAPE_OTSU2018) +node_tree = colour.recovery.Tree_Otsu2018(reflectances) node_tree.optimise() dataset = node_tree.to_dataset() print(colour.recovery.XYZ_to_sd_Otsu2018(XYZ, dataset=dataset)) diff --git a/colour/examples/recovery/examples_smits1999.py b/colour/examples/recovery/examples_smits1999.py index 4294ade107..7f73d739d5 100644 --- a/colour/examples/recovery/examples_smits1999.py +++ b/colour/examples/recovery/examples_smits1999.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Showcases reflectance recovery computations using *Smits (1999)* method. -""" +"""Showcases reflectance recovery computations using *Smits (1999)* method.""" import numpy as np @@ -13,18 +10,20 @@ XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) RGB = XYZ_to_RGB_Smits1999(XYZ) -message_box('Recovering reflectance using "Smits (1999)" method from ' - 'given "RGB" colourspace array:\n' - '\n\tRGB: {0}'.format(RGB)) -sd = colour.XYZ_to_sd(XYZ, method='Smits 1999') +message_box( + f'Recovering reflectance using "Smits (1999)" method from given "RGB" ' + f"colourspace array:\n\n\tRGB: {RGB}" +) +sd = colour.XYZ_to_sd(XYZ, method="Smits 1999") print(sd) print(colour.recovery.RGB_to_sd_Smits1999(XYZ)) print(colour.sd_to_XYZ(sd.align(colour.SPECTRAL_SHAPE_DEFAULT)) / 100) -print('\n') +print("\n") message_box( - ('An analysis of "Smits (1999)" method is available at the ' - 'following url : ' - 'http://nbviewer.jupyter.org/github/colour-science/colour-website/' - 'blob/master/ipython/about_reflectance_recovery.ipynb')) + 'An analysis of "Smits (1999)" method is available at the ' + "following url : " + "http://nbviewer.jupyter.org/github/colour-science/colour-website/" + "blob/master/ipython/about_reflectance_recovery.ipynb" +) diff --git a/colour/examples/temperature/examples_cct.py b/colour/examples/temperature/examples_cct.py index 85588790b0..2573f62326 100644 --- a/colour/examples/temperature/examples_cct.py +++ b/colour/examples/temperature/examples_cct.py @@ -1,97 +1,99 @@ -# -*- coding: utf-8 -*- -""" -Showcases correlated colour temperature computations. -""" +"""Showcases correlated colour temperature computations.""" import colour from colour.utilities import message_box -message_box('Correlated Colour Temperature Computations') +message_box("Correlated Colour Temperature Computations") -cmfs = colour.MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] -illuminant = colour.SDS_ILLUMINANTS['D65'] +cmfs = colour.MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] +illuminant = colour.SDS_ILLUMINANTS["D65"] xy = colour.XYZ_to_xy(colour.sd_to_XYZ(illuminant, cmfs) / 100) uv = colour.UCS_to_uv(colour.XYZ_to_UCS(colour.xy_to_XYZ(xy))) -message_box(('Converting to "CCT" and "D_uv" from given "CIE UCS" colourspace ' - '"uv" chromaticity coordinates using "Ohno (2013)" method:\n' - '\n\t{0}'.format(uv))) +message_box( + f'Converting to "CCT" and "D_uv" from given "CIE UCS" colourspace "uv" ' + f'chromaticity coordinates using "Ohno (2013)" method:\n\n\t{uv}' +) print(colour.uv_to_CCT(uv, cmfs=cmfs)) print(colour.temperature.uv_to_CCT_Ohno2013(uv, cmfs=cmfs)) -print('\n') +print("\n") -message_box('Faster computation with 3 iterations but a lot less precise.') +message_box("Faster computation with 3 iterations but a lot less precise.") print(colour.uv_to_CCT(uv, cmfs=cmfs, iterations=3)) print(colour.temperature.uv_to_CCT_Ohno2013(uv, cmfs=cmfs, iterations=3)) -print('\n') +print("\n") -message_box(('Converting to "CCT" and "D_uv" from given "CIE UCS" colourspace ' - '"uv" chromaticity coordinates using "Robertson (1968)" method:\n' - '\n\t{0}'.format(uv))) -print(colour.uv_to_CCT(uv, method='Robertson 1968')) +message_box( + f'Converting to "CCT" and "D_uv" from given "CIE UCS" colourspace "uv" ' + f'chromaticity coordinates using "Robertson (1968)" method:\n\n\t{uv}' +) +print(colour.uv_to_CCT(uv, method="Robertson 1968")) print(colour.temperature.uv_to_CCT_Robertson1968(uv)) -print('\n') +print("\n") CCT_D_uv = [6503.49254150, 0.00320598] -message_box(('Converting to "CIE UCS" colourspace "uv" chromaticity ' - 'coordinates from given "CCT" and "D_uv" using ' - '"Ohno (2013)" method:\n' - '\n\t{0}'.format(CCT_D_uv))) +message_box( + f'Converting to "CIE UCS" colourspace "uv" chromaticity coordinates from ' + f'given "CCT" and "D_uv" using "Ohno (2013)" method:\n\n\t{CCT_D_uv}' +) print(colour.CCT_to_uv(CCT_D_uv, cmfs=cmfs)) print(colour.temperature.CCT_to_uv_Ohno2013(CCT_D_uv, cmfs=cmfs)) -print('\n') +print("\n") -message_box(('Converting to "CIE UCS" colourspace "uv" chromaticity ' - 'coordinates from given "CCT" and "D_uv" using ' - '"Robertson (1968)" method:\n' - '\n\t{0}'.format(CCT_D_uv))) -print(colour.CCT_to_uv(CCT_D_uv, method='Robertson 1968')) +message_box( + f'Converting to "CIE UCS" colourspace "uv" chromaticity coordinates from ' + f'given "CCT" and "D_uv" using "Robertson (1968)" method:\n\n\t{CCT_D_uv}' +) +print(colour.CCT_to_uv(CCT_D_uv, method="Robertson 1968")) print(colour.temperature.CCT_to_uv_Robertson1968(CCT_D_uv)) -print('\n') +print("\n") CCT = 6503.49254150 -message_box(('Converting to "CIE UCS" colourspace "uv" chromaticity ' - 'coordinates from given "CCT" using "Krystek (1985)" method:\n' - '\n\t({0})'.format(CCT))) -print(colour.CCT_to_uv(CCT, method='Krystek 1985')) +message_box( + f'Converting to "CIE UCS" colourspace "uv" chromaticity coordinates from ' + f'given "CCT" using "Krystek (1985)" method:\n\n\t({CCT})' +) +print(colour.CCT_to_uv(CCT, method="Krystek 1985")) print(colour.temperature.CCT_to_uv_Krystek1985(CCT)) -print('\n') +print("\n") -xy = colour.CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65'] -message_box(('Converting to "CCT" from given "CIE xy" chromaticity ' - 'coordinates using "McCamy (1992)" method:\n' - '\n\t{0}'.format(xy))) -print(colour.xy_to_CCT(xy, method='McCamy 1992')) +xy = colour.CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"] +message_box( + f'Converting to "CCT" from given "CIE xy" chromaticity coordinates using ' + f'"McCamy (1992)" method:\n\n\t{xy}' +) +print(colour.xy_to_CCT(xy, method="McCamy 1992")) print(colour.temperature.xy_to_CCT_McCamy1992(xy)) -print('\n') +print("\n") -message_box(('Converting to "CCT" from given "CIE xy" chromaticity ' - 'coordinates using "Hernandez-Andres, Lee and Romero (1999)" ' - 'method:\n' - '\n\t{0}'.format(xy))) -print(colour.xy_to_CCT(xy, method='Hernandez 1999')) +message_box( + f'Converting to "CCT" from given "CIE xy" chromaticity coordinates using ' + f'"Hernandez-Andres, Lee and Romero (1999)" method:\n\n\t{xy}' +) +print(colour.xy_to_CCT(xy, method="Hernandez 1999")) print(colour.temperature.xy_to_CCT_Hernandez1999(xy)) -print('\n') +print("\n") CCT = 6503.49254150 -message_box(('Converting to "CIE xy" chromaticity coordinates from given ' - '"CCT" using "Kang, Moon, Hong, Lee, Cho and Kim (2002)" ' - 'method:\n' - '\n\t{0}'.format(CCT))) -print(colour.CCT_to_xy(CCT, method='Kang 2002')) +message_box( + f'Converting to "CIE xy" chromaticity coordinates from given "CCT" using ' + f'"Kang, Moon, Hong, Lee, Cho and Kim (2002)" method:\n\n\t{CCT}' +) +print(colour.CCT_to_xy(CCT, method="Kang 2002")) print(colour.temperature.CCT_to_xy_Kang2002(CCT)) -print('\n') +print("\n") -message_box(('Converting to "CIE xy" chromaticity coordinates from given ' - '"CCT" using "CIE Illuminant D Series" method:\n' - '\n\t{0}'.format(CCT))) -print(colour.CCT_to_xy(CCT, method='CIE Illuminant D Series')) +message_box( + f'Converting to "CIE xy" chromaticity coordinates from given "CCT" using ' + f'"CIE Illuminant D Series" method:\n\n\t{CCT}' +) +print(colour.CCT_to_xy(CCT, method="CIE Illuminant D Series")) print(colour.temperature.CCT_to_xy_CIE_D(CCT)) diff --git a/colour/examples/volume/examples_rgb.py b/colour/examples/volume/examples_rgb.py index ae63d168ac..024b3549fe 100644 --- a/colour/examples/volume/examples_rgb.py +++ b/colour/examples/volume/examples_rgb.py @@ -1,37 +1,42 @@ -# -*- coding: utf-8 -*- -""" -Showcases RGB colourspace volume computations. -""" +"""Showcases RGB colourspace volume computations.""" import colour from colour.utilities import message_box # NOTE: Because the MonteCarlo methods use multiprocessing, it is recommended # to wrap the execution in a definition or a *__main__* block. -if __name__ == '__main__': - message_box('RGB Colourspace Volume Computations') +if __name__ == "__main__": + message_box("RGB Colourspace Volume Computations") - message_box('Computing "ProPhoto RGB" RGB colourspace limits.') + message_box('Computing the "ProPhoto RGB" RGB colourspace limits.') limits = colour.RGB_colourspace_limits( - colour.RGB_COLOURSPACES['ProPhoto RGB']) + colour.RGB_COLOURSPACES["ProPhoto RGB"] + ) print(limits) - print('\n') + print("\n") samples = 10e4 - message_box(('Computing "ProPhoto RGB" RGB colourspace volume using ' - '{0} samples.'.format(samples))) + message_box( + f'Computing the"ProPhoto RGB" RGB colourspace volume using {samples} ' + f"samples." + ) print( colour.RGB_colourspace_volume_MonteCarlo( - colour.RGB_COLOURSPACES['ProPhoto RGB'], + colour.RGB_COLOURSPACES["ProPhoto RGB"], samples=samples, - limits=limits * 1.1)) + limits=limits * 1.1, + ) + ) - print('\n') + print("\n") message_box( - ('Computing "ProPhoto RGB" RGB colourspace coverage of Pointer\'s ' - 'Gamut using {0} samples.'.format(samples))) + f'Computing "ProPhoto RGB" RGB colourspace coverage of ' + f'"Pointer\'s Gamut" using {samples} samples.' + ) print( colour.RGB_colourspace_pointer_gamut_coverage_MonteCarlo( - colour.RGB_COLOURSPACES['ProPhoto RGB'], samples=samples)) + colour.RGB_COLOURSPACES["ProPhoto RGB"], samples=samples + ) + ) diff --git a/colour/geometry/__init__.py b/colour/geometry/__init__.py index a1f207af96..1a380e50ca 100644 --- a/colour/geometry/__init__.py +++ b/colour/geometry/__init__.py @@ -1,19 +1,71 @@ -# -*- coding: utf-8 -*- +import sys -from __future__ import absolute_import +from colour.utilities.deprecation import ModuleAPI, build_API_changes +from colour.utilities.documentation import is_documentation_building -from .primitives import PLANE_TO_AXIS_MAPPING, primitive_grid, primitive_cube -from .primitives import PRIMITIVE_METHODS, primitive +from colour.hints import Any -from .vertices import (primitive_vertices_quad_mpl, - primitive_vertices_grid_mpl, - primitive_vertices_cube_mpl, primitive_vertices_sphere) +from .primitives import MAPPING_PLANE_TO_AXIS, primitive_grid, primitive_cube +from .primitives import PRIMITIVE_METHODS, primitive +from .section import hull_section +from .vertices import ( + primitive_vertices_quad_mpl, + primitive_vertices_grid_mpl, + primitive_vertices_cube_mpl, + primitive_vertices_sphere, +) from .vertices import PRIMITIVE_VERTICES_METHODS, primitive_vertices -__all__ = ['PLANE_TO_AXIS_MAPPING', 'primitive_grid', 'primitive_cube'] -__all__ += ['PRIMITIVE_METHODS', 'primitive'] +__all__ = [ + "MAPPING_PLANE_TO_AXIS", + "primitive_grid", + "primitive_cube", +] +__all__ += [ + "hull_section", +] +__all__ += [ + "PRIMITIVE_METHODS", + "primitive", +] +__all__ += [ + "primitive_vertices_quad_mpl", + "primitive_vertices_grid_mpl", + "primitive_vertices_cube_mpl", + "primitive_vertices_sphere", +] __all__ += [ - 'primitive_vertices_quad_mpl', 'primitive_vertices_grid_mpl', - 'primitive_vertices_cube_mpl', 'primitive_vertices_sphere' + "PRIMITIVE_VERTICES_METHODS", + "primitive_vertices", ] -__all__ += ['PRIMITIVE_VERTICES_METHODS', 'primitive_vertices'] + + +# ----------------------------------------------------------------------------# +# --- API Changes and Deprecation Management ---# +# ----------------------------------------------------------------------------# +class geometry(ModuleAPI): + """Define a class acting like the *geometry* module.""" + + def __getattr__(self, attribute) -> Any: + """Return the value from the attribute with given name.""" + + return super().__getattr__(attribute) + + +# v0.4.0 +API_CHANGES = { + "ObjectRenamed": [ + [ + "colour.geometry.PLANE_TO_AXIS_MAPPING", + "colour.geometry.MAPPING_PLANE_TO_AXIS", + ], + ] +} +"""Defines the *colour.geometry* sub-package API changes.""" + +if not is_documentation_building(): + sys.modules["colour.geometry"] = geometry( # type:ignore[assignment] + sys.modules["colour.geometry"], build_API_changes(API_CHANGES) + ) + + del ModuleAPI, is_documentation_building, build_API_changes, sys diff --git a/colour/geometry/primitives.py b/colour/geometry/primitives.py index 1bde0458d5..80a796ff5b 100644 --- a/colour/geometry/primitives.py +++ b/colour/geometry/primitives.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- """ Geometry Primitives =================== Defines various geometry primitives and their generation methods: -- :func:`colour.geometry.PLANE_TO_AXIS_MAPPING` +- :attr:`colour.geometry.MAPPING_PLANE_TO_AXIS` - :func:`colour.geometry.primitive_grid` - :func:`colour.geometry.primitive_cube` - :func:`colour.PRIMITIVE_METHODS` @@ -18,73 +17,104 @@ https://github.com/mrdoob/three.js/blob/dev/src/geometries/PlaneGeometry.js """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.constants import DEFAULT_INT_DTYPE, DEFAULT_FLOAT_DTYPE -from colour.utilities import CaseInsensitiveMapping, filter_kwargs, ones, zeros - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import ( + Any, + DTypeFloating, + DTypeInteger, + Floating, + Integer, + Literal, + NDArray, + Optional, + Tuple, + Type, + Union, + cast, +) +from colour.utilities import ( + CaseInsensitiveMapping, + as_int_array, + filter_kwargs, + ones, + optional, + zeros, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'PLANE_TO_AXIS_MAPPING', - 'primitive_grid', - 'primitive_cube', - 'PRIMITIVE_METHODS', - 'primitive', + "MAPPING_PLANE_TO_AXIS", + "primitive_grid", + "primitive_cube", + "PRIMITIVE_METHODS", + "primitive", ] -PLANE_TO_AXIS_MAPPING = CaseInsensitiveMapping({ - 'yz': '+x', - 'zy': '-x', - 'xz': '+y', - 'zx': '-y', - 'xy': '+z', - 'yx': '-z', -}) -PLANE_TO_AXIS_MAPPING.__doc__ = """ -Plane to axis mapping. - -PLANE_TO_AXIS_MAPPING : CaseInsensitiveMapping - **{'-x', '+x', '-y', '+y', '-z', '+z'}** -""" - - -def primitive_grid(width=1, - height=1, - width_segments=1, - height_segments=1, - axis='+z'): +MAPPING_PLANE_TO_AXIS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "yz": "+x", + "zy": "-x", + "xz": "+y", + "zx": "-y", + "xy": "+z", + "yx": "-z", + } +) +MAPPING_PLANE_TO_AXIS.__doc__ = """Plane to axis mapping.""" + + +def primitive_grid( + width: Floating = 1, + height: Floating = 1, + width_segments: Integer = 1, + height_segments: Integer = 1, + axis: Literal[ + "-x", "+x", "-y", "+y", "-z", "+z", "xy", "xz", "yz", "yx", "zx", "zy" + ] = "+z", + dtype_vertices: Optional[Type[DTypeFloating]] = None, + dtype_indexes: Optional[Type[DTypeInteger]] = None, +) -> Tuple[NDArray, NDArray, NDArray]: """ - Generates vertices and indices for a filled and outlined grid primitive. + Generate vertices and indexes for a filled and outlined grid primitive. Parameters ---------- - width : float, optional + width Grid width. - height : float, optional + height Grid height. - width_segments : int, optional + width_segments Grid segments count along the width. - height_segments : float, optional + height_segments Grid segments count along the height. - axis : unicode, optional - **{'+z', '-x', '+x', '-y', '+y', '-z', - 'xy', 'xz', 'yz', 'yx', 'zx', 'zy'}**, + axis Axis the primitive will be normal to, or plane the primitive will be co-planar with. + dtype_vertices + :class:`numpy.dtype` to use for the grid vertices, default to + the :class:`numpy.dtype` defined by the + :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. + dtype_indexes + :class:`numpy.dtype` to use for the grid indexes, default to + the :class:`numpy.dtype` defined by the + :attr:`colour.constant.DEFAULT_INT_DTYPE` attribute. Returns ------- - tuple - Tuple of grid vertices, face indices to produce a filled grid and - outline indices to produce an outline of the faces of the grid. + :class:`tuple` + Tuple of grid vertices, face indexes to produce a filled grid and + outline indexes to produce an outline of the faces of the grid. References ---------- @@ -108,13 +138,20 @@ def primitive_grid(width=1, [1 0]] """ - axis = PLANE_TO_AXIS_MAPPING.get(axis, axis).lower() + axis = MAPPING_PLANE_TO_AXIS.get(axis, axis).lower() + + dtype_vertices = cast( + Type[DTypeFloating], optional(dtype_vertices, DEFAULT_FLOAT_DTYPE) + ) + dtype_indexes = cast( + Type[DTypeInteger], optional(dtype_indexes, DEFAULT_INT_DTYPE) + ) x_grid = width_segments y_grid = height_segments - x_grid1 = x_grid + 1 - y_grid1 = y_grid + 1 + x_grid1 = int(x_grid + 1) + y_grid1 = int(y_grid + 1) # Positions, normals and uvs. positions = zeros(x_grid1 * y_grid1 * 3) @@ -133,7 +170,8 @@ def primitive_grid(width=1, uvs[1::2] = np.repeat(1 - np.arange(y_grid1) / y_grid, x_grid1) # Faces and outline. - faces, outline = [], [] + faces_indexes = [] + outline_indexes = [] for i_y in range(y_grid): for i_x in range(x_grid): a = i_x + x_grid1 * i_y @@ -141,86 +179,120 @@ def primitive_grid(width=1, c = (i_x + 1) + x_grid1 * (i_y + 1) d = (i_x + 1) + x_grid1 * i_y - faces.extend([(a, b, d), (b, c, d)]) - outline.extend([(a, b), (b, c), (c, d), (d, a)]) + faces_indexes.extend([(a, b, d), (b, c, d)]) + outline_indexes.extend([(a, b), (b, c), (c, d), (d, a)]) + + faces = np.reshape(as_int_array(faces_indexes, dtype_indexes), (-1, 3)) + outline = np.reshape(as_int_array(outline_indexes, dtype_indexes), (-1, 2)) positions = np.reshape(positions, (-1, 3)) uvs = np.reshape(uvs, (-1, 2)) normals = np.reshape(normals, (-1, 3)) - faces = np.reshape(faces, (-1, 3)).astype(np.uint32) - outline = np.reshape(outline, (-1, 2)).astype(np.uint32) - - if axis in ('-x', '+x'): + if axis in ("-x", "+x"): shift, zero_axis = 1, 0 - elif axis in ('-y', '+y'): + elif axis in ("-y", "+y"): shift, zero_axis = -1, 1 - elif axis in ('-z', '+z'): + elif axis in ("-z", "+z"): shift, zero_axis = 0, 2 - sign = -1 if '-' in axis else 1 + sign = -1 if "-" in axis else 1 positions = np.roll(positions, shift, -1) normals = np.roll(normals, shift, -1) * sign vertex_colours = np.ravel(positions) - vertex_colours = np.hstack([ - np.reshape( - np.interp(vertex_colours, - (np.min(vertex_colours), np.max(vertex_colours)), - (0, 1)), positions.shape), - ones([positions.shape[0], 1]) - ]) + vertex_colours = np.hstack( + [ + np.reshape( + np.interp( + vertex_colours, + (np.min(vertex_colours), np.max(vertex_colours)), + (0, 1), + ), + positions.shape, + ), + ones((positions.shape[0], 1)), + ] + ) vertex_colours[..., zero_axis] = 0 - vertices = zeros(positions.shape[0], [ - ('position', DEFAULT_FLOAT_DTYPE, 3), - ('uv', DEFAULT_FLOAT_DTYPE, 2), - ('normal', DEFAULT_FLOAT_DTYPE, 3), - ('colour', DEFAULT_FLOAT_DTYPE, 4), - ]) - - vertices['position'] = positions - vertices['uv'] = uvs - vertices['normal'] = normals - vertices['colour'] = vertex_colours + vertices = zeros( + positions.shape[0], + [ + ("position", dtype_vertices, 3), + ("uv", dtype_vertices, 2), + ("normal", dtype_vertices, 3), + ("colour", dtype_vertices, 4), + ], # type: ignore[arg-type] + ) + + vertices["position"] = positions + vertices["uv"] = uvs + vertices["normal"] = normals + vertices["colour"] = vertex_colours return vertices, faces, outline -def primitive_cube(width=1, - height=1, - depth=1, - width_segments=1, - height_segments=1, - depth_segments=1, - planes=None): +def primitive_cube( + width: Floating = 1, + height: Floating = 1, + depth: Floating = 1, + width_segments: Integer = 1, + height_segments: Integer = 1, + depth_segments: Integer = 1, + planes: Optional[ + Literal[ + "-x", + "+x", + "-y", + "+y", + "-z", + "+z", + "xy", + "xz", + "yz", + "yx", + "zx", + "zy", + ] + ] = None, + dtype_vertices: Optional[Type[DTypeFloating]] = None, + dtype_indexes: Optional[Type[DTypeInteger]] = None, +) -> Tuple[NDArray, NDArray, NDArray]: """ - Generates vertices and indices for a filled and outlined cube primitive. + Generate vertices and indexes for a filled and outlined cube primitive. Parameters ---------- - width : float, optional + width Cube width. - height : float, optional + height Cube height. - depth : float, optional + depth Cube depth. - width_segments : int, optional + width_segments Cube segments count along the width. - height_segments : float, optional + height_segments Cube segments count along the height. - depth_segments : float, optional + depth_segments Cube segments count along the depth. - planes : array_like, optional - **{'-x', '+x', '-y', '+y', '-z', '+z', - 'xy', 'xz', 'yz', 'yx', 'zx', 'zy'}**, + planes Grid primitives to include in the cube construction. + dtype_vertices + :class:`numpy.dtype` to use for the grid vertices, default to + the :class:`numpy.dtype` defined by the + :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. + dtype_indexes + :class:`numpy.dtype` to use for the grid indexes, default to + the :class:`numpy.dtype` defined by the + :attr:`colour.constant.DEFAULT_INT_DTYPE` attribute. Returns ------- - tuple - Tuple of cube vertices, face indices to produce a filled cube and - outline indices to produce an outline of the faces of the cube. + :class:`tuple` + Tuple of cube vertices, face indexes to produce a filled cube and + outline indexes to produce an outline of the faces of the cube. Examples -------- @@ -290,133 +362,172 @@ def primitive_cube(width=1, [21 20]] """ - planes = (sorted(list( - PLANE_TO_AXIS_MAPPING.values())) if planes is None else [ - PLANE_TO_AXIS_MAPPING.get(plane, plane).lower() for plane in planes - ]) + axis = ( + sorted(list(MAPPING_PLANE_TO_AXIS.values())) + if planes is None + else [ + MAPPING_PLANE_TO_AXIS.get(plane, plane).lower() for plane in planes + ] + ) + + dtype_vertices = cast( + Type[DTypeFloating], optional(dtype_vertices, DEFAULT_FLOAT_DTYPE) + ) + dtype_indexes = cast( + Type[DTypeInteger], optional(dtype_indexes, DEFAULT_INT_DTYPE) + ) w_s, h_s, d_s = width_segments, height_segments, depth_segments - planes_m = [] - if '-z' in planes: - planes_m.append(list(primitive_grid(width, depth, w_s, d_s, '-z'))) - planes_m[-1][0]['position'][..., 2] -= height / 2 - planes_m[-1][1] = np.fliplr(planes_m[-1][1]) - if '+z' in planes: - planes_m.append(list(primitive_grid(width, depth, w_s, d_s, '+z'))) - planes_m[-1][0]['position'][..., 2] += height / 2 - - if '-y' in planes: - planes_m.append(list(primitive_grid(height, width, h_s, w_s, '-y'))) - planes_m[-1][0]['position'][..., 1] -= depth / 2 - planes_m[-1][1] = np.fliplr(planes_m[-1][1]) - if '+y' in planes: - planes_m.append(list(primitive_grid(height, width, h_s, w_s, '+y'))) - planes_m[-1][0]['position'][..., 1] += depth / 2 - - if '-x' in planes: - planes_m.append(list(primitive_grid(depth, height, d_s, h_s, '-x'))) - planes_m[-1][0]['position'][..., 0] -= width / 2 - planes_m[-1][1] = np.fliplr(planes_m[-1][1]) - if '+x' in planes: - planes_m.append(list(primitive_grid(depth, height, d_s, h_s, '+x'))) - planes_m[-1][0]['position'][..., 0] += width / 2 - - positions = zeros([0, 3]) - uvs = zeros([0, 2]) - normals = zeros([0, 3]) - - faces = zeros([0, 3], dtype=DEFAULT_INT_DTYPE) - outline = zeros([0, 2], dtype=DEFAULT_INT_DTYPE) + planes_p = [] + if "-z" in axis: + planes_p.append(list(primitive_grid(width, depth, w_s, d_s, "-z"))) + planes_p[-1][0]["position"][..., 2] -= height / 2 + planes_p[-1][1] = np.fliplr(planes_p[-1][1]) + if "+z" in axis: + planes_p.append(list(primitive_grid(width, depth, w_s, d_s, "+z"))) + planes_p[-1][0]["position"][..., 2] += height / 2 + + if "-y" in axis: + planes_p.append(list(primitive_grid(height, width, h_s, w_s, "-y"))) + planes_p[-1][0]["position"][..., 1] -= depth / 2 + planes_p[-1][1] = np.fliplr(planes_p[-1][1]) + if "+y" in axis: + planes_p.append(list(primitive_grid(height, width, h_s, w_s, "+y"))) + planes_p[-1][0]["position"][..., 1] += depth / 2 + + if "-x" in axis: + planes_p.append(list(primitive_grid(depth, height, d_s, h_s, "-x"))) + planes_p[-1][0]["position"][..., 0] -= width / 2 + planes_p[-1][1] = np.fliplr(planes_p[-1][1]) + if "+x" in axis: + planes_p.append(list(primitive_grid(depth, height, d_s, h_s, "+x"))) + planes_p[-1][0]["position"][..., 0] += width / 2 + + positions = zeros((0, 3)) + uvs = zeros((0, 2)) + normals = zeros((0, 3)) + + faces = zeros((0, 3), dtype=dtype_indexes) + outline = zeros((0, 2), dtype=dtype_indexes) offset = 0 - for vertices_p, faces_p, outline_p in planes_m: - positions = np.vstack([positions, vertices_p['position']]) - uvs = np.vstack([uvs, vertices_p['uv']]) - normals = np.vstack([normals, vertices_p['normal']]) + for vertices_p, faces_p, outline_p in planes_p: + positions = np.vstack([positions, vertices_p["position"]]) + uvs = np.vstack([uvs, vertices_p["uv"]]) + normals = np.vstack([normals, vertices_p["normal"]]) faces = np.vstack([faces, faces_p + offset]) outline = np.vstack([outline, outline_p + offset]) - offset += vertices_p['position'].shape[0] - - vertices = zeros(positions.shape[0], [('position', DEFAULT_FLOAT_DTYPE, 3), - ('uv', DEFAULT_FLOAT_DTYPE, 2), - ('normal', DEFAULT_FLOAT_DTYPE, 3), - ('colour', DEFAULT_FLOAT_DTYPE, 4)]) + offset += vertices_p["position"].shape[0] + + vertices = zeros( + positions.shape[0], + [ + ("position", dtype_vertices, 3), + ("uv", dtype_vertices, 2), + ("normal", dtype_vertices, 3), + ("colour", dtype_vertices, 4), + ], # type: ignore[arg-type] + ) vertex_colours = np.ravel(positions) - vertex_colours = np.hstack([ - np.reshape( - np.interp(vertex_colours, - (np.min(vertex_colours), np.max(vertex_colours)), - (0, 1)), positions.shape), - ones([positions.shape[0], 1]) - ]) - - vertices['position'] = positions - vertices['uv'] = uvs - vertices['normal'] = normals - vertices['colour'] = vertex_colours + vertex_colours = np.hstack( + [ + np.reshape( + np.interp( + vertex_colours, + (np.min(vertex_colours), np.max(vertex_colours)), + (0, 1), + ), + positions.shape, + ), + ones((positions.shape[0], 1)), + ] + ) + + vertices["position"] = positions + vertices["uv"] = uvs + vertices["normal"] = normals + vertices["colour"] = vertex_colours return vertices, faces, outline -PRIMITIVE_METHODS = CaseInsensitiveMapping({ - 'Grid': primitive_grid, - 'Cube': primitive_cube, -}) +PRIMITIVE_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Grid": primitive_grid, + "Cube": primitive_cube, + } +) PRIMITIVE_METHODS.__doc__ = """ Supported geometry primitive generation methods. - -PRIMITIVE_METHODS : CaseInsensitiveMapping - **{'Grid', 'Cube'}** """ -def primitive(method='Cube', **kwargs): +def primitive( + method: Union[Literal["Cube", "Grid"], str] = "Cube", **kwargs: Any +) -> Tuple[NDArray, NDArray, NDArray]: """ - Returns a geometry primitive using given method. + Return a geometry primitive using given method. Parameters ---------- - method : unicode, optional - **{'Cube', 'Grid'}**, + method Generation method. Other Parameters ---------------- - width : numeric, optional - {:func:`colour.geometry.primitive_grid_mpl`, - :func:`colour.geometry.primitive_cube_mpl`}, - Primitive width. - height : numeric, optional - {:func:`colour.geometry.primitive_grid_mpl`, - :func:`colour.geometry.primitive_cube_mpl`}, - Primitive height. - depth : numeric, optional - {:func:`colour.geometry.primitive_grid_mpl`, - :func:`colour.geometry.primitive_cube_mpl`}, + axis + {:func:`colour.geometry.primitive_grid`}, + Axis the primitive will be normal to, or plane the primitive will be + co-planar with. + depth + {:func:`colour.geometry.primitive_grid`, + :func:`colour.geometry.primitive_cube`}, Primitive depth. + depth_segments + {:func:`colour.geometry.primitive_grid`, + :func:`colour.geometry.primitive_cube`}, + Primitive segments count along the depth. + dtype_indexes + {:func:`colour.geometry.primitive_grid`, + :func:`colour.geometry.primitive_cube`}, + :class:`numpy.dtype` to use for the grid indexes, default to + the :class:`numpy.dtype` defined by the + :attr:`colour.constant.DEFAULT_INT_DTYPE` attribute. + dtype_vertices + {:func:`colour.geometry.primitive_grid`, + :func:`colour.geometry.primitive_cube`}, + :class:`numpy.dtype` to use for the grid vertices, default to + the :class:`numpy.dtype` defined by the + :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. + height + {:func:`colour.geometry.primitive_grid`, + :func:`colour.geometry.primitive_cube`}, + Primitive height. + planes + {:func:`colour.geometry.primitive_cube`}, + Included grid primitives in the cube construction. + width + {:func:`colour.geometry.primitive_grid`, + :func:`colour.geometry.primitive_cube`}, + Primitive width. width_segments - {:func:`colour.geometry.primitive_grid_mpl`, - :func:`colour.geometry.primitive_cube_mpl`}, + {:func:`colour.geometry.primitive_grid`, + :func:`colour.geometry.primitive_cube`}, Primitive segments count along the width. height_segments - {:func:`colour.geometry.primitive_grid_mpl`, - :func:`colour.geometry.primitive_cube_mpl`}, + {:func:`colour.geometry.primitive_grid`, + :func:`colour.geometry.primitive_cube`}, Primitive segments count along the height. - depth_segments - {:func:`colour.geometry.primitive_grid_mpl`, - :func:`colour.geometry.primitive_cube_mpl`}, - Primitive segments count along the depth. - planes : array_like, optional - {:func:`colour.geometry.primitive_cube_mpl`}, - **{'-x', '+x', '-y', '+y', '-z', '+z', - 'xy', 'xz', 'yz', 'yx', 'zx', 'zy'}**, - Included grid primitives in the cube construction. Returns ------- + :class:`tuple` + Tuple of primitive vertices, face indexes to produce a filled primitive + and outline indexes to produce an outline of the faces of the + primitive. References ---------- @@ -504,6 +615,8 @@ def primitive(method='Cube', **kwargs): [1 0]] """ + method = validate_method(method, PRIMITIVE_METHODS) + function = PRIMITIVE_METHODS[method] return function(**filter_kwargs(function, **kwargs)) diff --git a/colour/geometry/section.py b/colour/geometry/section.py new file mode 100644 index 0000000000..f3d3ebcc54 --- /dev/null +++ b/colour/geometry/section.py @@ -0,0 +1,259 @@ +""" +Geometry / Hull Section +======================= + +Defines various objects to compute hull sections: + +- :func:`colour.geometry.hull_section` +""" + +from __future__ import annotations + +import numpy as np + +from colour.algebra import linear_conversion +from colour.constants import DEFAULT_FLOAT_DTYPE +from colour.hints import ( + ArrayLike, + Boolean, + Floating, + Integer, + Literal, + NDArray, + Union, +) +from colour.utilities import ( + as_float_array, + as_float_scalar, + required, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "edges_to_chord", + "unique_vertices", + "close_chord", + "hull_section", +] + + +def edges_to_chord(edges: ArrayLike, index: Integer = 0) -> NDArray: + """ + Convert given edges to a chord, starting at given index. + + Parameters + ---------- + edges + Edges to convert to a chord. + index + Index to start forming the chord at. + + Returns + ------- + :class:`numpy.ndarray` + Chord. + + Examples + -------- + >>> edges = np.array([ + ... [[-0.0, -0.5, 0.0], [0.5, -0.5, 0.0]], + ... [[-0.5, -0.5, 0.0], [-0.0, -0.5, 0.0]], + ... [[0.5, 0.5, 0.0], [-0.0, 0.5, 0.0]], + ... [[-0.0, 0.5, 0.0], [-0.5, 0.5, 0.0]], + ... [[-0.5, 0.0, -0.0], [-0.5, -0.5, -0.0]], + ... [[-0.5, 0.5, -0.0], [-0.5, 0.0, -0.0]], + ... [[0.5, -0.5, -0.0], [0.5, 0.0, -0.0]], + ... [[0.5, 0.0, -0.0], [0.5, 0.5, -0.0]], + ... ]) + >>> edges_to_chord(edges) + array([[-0. , -0.5, 0. ], + [ 0.5, -0.5, 0. ], + [ 0.5, -0.5, -0. ], + [ 0.5, 0. , -0. ], + [ 0.5, 0. , -0. ], + [ 0.5, 0.5, -0. ], + [ 0.5, 0.5, 0. ], + [-0. , 0.5, 0. ], + [-0. , 0.5, 0. ], + [-0.5, 0.5, 0. ], + [-0.5, 0.5, -0. ], + [-0.5, 0. , -0. ], + [-0.5, 0. , -0. ], + [-0.5, -0.5, -0. ], + [-0.5, -0.5, 0. ], + [-0. , -0.5, 0. ]]) + """ + + edge_list = as_float_array(edges).tolist() + + edges_ordered = [edge_list.pop(index)] + segment = np.array(edges_ordered[0][1]) + + while len(edge_list) > 0: + edges_array = np.array(edge_list) + d_0 = np.linalg.norm(edges_array[:, 0, :] - segment, axis=1) + d_1 = np.linalg.norm(edges_array[:, 1, :] - segment, axis=1) + d_0_argmin, d_1_argmin = d_0.argmin(), d_1.argmin() + + if d_0[d_0_argmin] < d_1[d_1_argmin]: + edges_ordered.append(edge_list.pop(d_0_argmin)) + segment = np.array(edges_ordered[-1][1]) + else: + edges_ordered.append(edge_list.pop(d_1_argmin)) + segment = np.array(edges_ordered[-1][0]) + + return as_float_array(edges_ordered).reshape([-1, segment.shape[-1]]) + + +def close_chord(vertices: ArrayLike) -> NDArray: + """ + Close the chord. + + Parameters + ---------- + vertices + Vertices of the chord to close. + + Returns + ------- + :class:`numpy.ndarray` + Closed chord. + + Examples + -------- + >>> close_chord(np.array([[0.0, 0.5, 0.0], [0.0, 0.0, 0.5]])) + array([[ 0. , 0.5, 0. ], + [ 0. , 0. , 0.5], + [ 0. , 0.5, 0. ]]) + """ + + vertices = as_float_array(vertices) + + return np.vstack([vertices, vertices[0]]) + + +def unique_vertices( + vertices: ArrayLike, + decimals: Integer = np.finfo(DEFAULT_FLOAT_DTYPE).precision - 1, +) -> NDArray: + """ + Return the unique vertices from given vertices. + + Parameters + ---------- + vertices + Vertices to return the unique vertices from. + decimals + Decimals used when rounding the vertices prior to comparison. + + Returns + ------- + :class:`numpy.ndarray` + Unique vertices. + + Notes + ----- + - The vertices are rounded at given ``decimals``. + + Examples + -------- + >>> unique_vertices( + ... np.array([[0.0, 0.5, 0.0], [0.0, 0.0, 0.5], [0.0, 0.5, 0.0]])) + array([[ 0. , 0.5, 0. ], + [ 0. , 0. , 0.5]]) + """ + + vertices = as_float_array(vertices) + + unique, indexes = np.unique( + vertices.round(decimals=decimals), axis=0, return_index=True + ) + + return unique[np.argsort(indexes)] + + +@required("trimesh") +def hull_section( + hull: trimesh.Trimesh, # type: ignore[name-defined] # noqa + axis: Union[Literal["+z", "+x", "+y"], str] = "+z", + origin: Floating = 0.5, + normalise: Boolean = False, +) -> NDArray: + """ + Compute the hull section for given axis at given origin. + + Parameters + ---------- + hull + *Trimesh* hull. + axis + Axis the hull section will be normal to. + origin + Coordinate along ``axis`` at which to plot the hull section. + normalise + Whether to normalise ``axis`` to the extent of the hull along it. + + Returns + ------- + :class:`numpy.ndarray` + Hull section vertices. + + Examples + -------- + >>> from colour.geometry import primitive_cube + >>> from colour.utilities import is_trimesh_installed + >>> vertices, faces, outline = primitive_cube(1, 1, 1, 2, 2, 2) + >>> if is_trimesh_installed: + ... import trimesh + ... hull = trimesh.Trimesh(vertices['position'], faces, process=False) + ... hull_section(hull, origin=0) + array([[-0. , -0.5, 0. ], + [ 0.5, -0.5, 0. ], + [ 0.5, 0. , -0. ], + [ 0.5, 0.5, -0. ], + [-0. , 0.5, 0. ], + [-0.5, 0.5, 0. ], + [-0.5, 0. , -0. ], + [-0.5, -0.5, -0. ], + [-0. , -0.5, 0. ]]) + """ + + import trimesh + + axis = validate_method( + axis, + ["+z", "+x", "+y"], + '"{0}" axis is invalid, it must be one of {1}!', + ) + + if axis == "+x": + normal, plane = np.array([1, 0, 0]), np.array([origin, 0, 0]) + elif axis == "+y": + normal, plane = np.array([0, 1, 0]), np.array([0, origin, 0]) + elif axis == "+z": + normal, plane = np.array([0, 0, 1]), np.array([0, 0, origin]) + + if normalise: + vertices = hull.vertices * normal + origin = as_float_scalar( + linear_conversion( + origin, [0, 1], [np.min(vertices), np.max(vertices)] + ) + ) + plane[plane != 0] = origin + + section = trimesh.intersections.mesh_plane(hull, normal, plane) + if len(section) == 0: + raise ValueError( + f'No section exists on "{axis}" axis at {origin} origin!' + ) + section = close_chord(unique_vertices(edges_to_chord(section))) + + return section diff --git a/colour/geometry/tests/test_primitives.py b/colour/geometry/tests/test_primitives.py index 98aa45b490..c07571e4de 100644 --- a/colour/geometry/tests/test_primitives.py +++ b/colour/geometry/tests/test_primitives.py @@ -1,336 +1,386 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.geometry.primitives` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.geometry.primitives` module.""" import numpy as np import unittest -from colour.geometry import (PLANE_TO_AXIS_MAPPING, primitive_grid, - primitive_cube) +from colour.geometry import ( + MAPPING_PLANE_TO_AXIS, + primitive_grid, + primitive_cube, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestPrimitiveGrid', 'TestPrimitiveCube'] +__all__ = [ + "TestPrimitiveGrid", + "TestPrimitiveCube", +] class TestPrimitiveGrid(unittest.TestCase): """ - Defines :func:`colour.geometry.primitives.primitive_grid` + Define :func:`colour.geometry.primitives.primitive_grid` definition unit tests methods. """ def test_primitive_grid(self): """ - Tests :func:`colour.geometry.primitives.primitive_grid` + Test :func:`colour.geometry.primitives.primitive_grid` definition. """ vertices, faces, outline = primitive_grid() np.testing.assert_almost_equal( - vertices['position'], - np.array([ - [-0.5, 0.5, 0.0], - [0.5, 0.5, 0.0], - [-0.5, -0.5, 0.0], - [0.5, -0.5, 0.0], - ]), - decimal=7) + vertices["position"], + np.array( + [ + [-0.5, 0.5, 0.0], + [0.5, 0.5, 0.0], + [-0.5, -0.5, 0.0], + [0.5, -0.5, 0.0], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - vertices['uv'], + vertices["uv"], np.array([[0, 1], [1, 1], [0, 0], [1, 0]]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - vertices['normal'], + vertices["normal"], np.array([[0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1]]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - vertices['colour'], - np.array([ - [0, 1, 0, 1], - [1, 1, 0, 1], - [0, 0, 0, 1], - [1, 0, 0, 1], - ]), - decimal=7) + vertices["colour"], + np.array( + [ + [0, 1, 0, 1], + [1, 1, 0, 1], + [0, 0, 0, 1], + [1, 0, 0, 1], + ] + ), + decimal=7, + ) np.testing.assert_equal(faces, np.array([[0, 2, 1], [2, 3, 1]])) - np.testing.assert_equal(outline, - np.array([[0, 2], [2, 3], [3, 1], [1, 0]])) + np.testing.assert_equal( + outline, np.array([[0, 2], [2, 3], [3, 1], [1, 0]]) + ) vertices, faces, outline = primitive_grid( width=0.2, height=0.4, width_segments=1, height_segments=2, - axis='+z') + axis="+z", + ) np.testing.assert_almost_equal( - vertices['position'], - np.array([ - [-0.10000000, 0.20000000, 0.00000000], - [0.10000000, 0.20000000, 0.00000000], - [-0.10000000, -0.00000000, 0.00000000], - [0.10000000, -0.00000000, 0.00000000], - [-0.10000000, -0.20000000, 0.00000000], - [0.10000000, -0.20000000, 0.00000000], - ]), - decimal=7) + vertices["position"], + np.array( + [ + [-0.10000000, 0.20000000, 0.00000000], + [0.10000000, 0.20000000, 0.00000000], + [-0.10000000, -0.00000000, 0.00000000], + [0.10000000, -0.00000000, 0.00000000], + [-0.10000000, -0.20000000, 0.00000000], + [0.10000000, -0.20000000, 0.00000000], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - vertices['uv'], - np.array([ - [0.00000000, 1.00000000], - [1.00000000, 1.00000000], - [0.00000000, 0.50000000], - [1.00000000, 0.50000000], - [0.00000000, 0.00000000], - [1.00000000, 0.00000000], - ]), - decimal=7) + vertices["uv"], + np.array( + [ + [0.00000000, 1.00000000], + [1.00000000, 1.00000000], + [0.00000000, 0.50000000], + [1.00000000, 0.50000000], + [0.00000000, 0.00000000], + [1.00000000, 0.00000000], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - vertices['normal'], - np.array([ - [0, 0, 1], - [0, 0, 1], - [0, 0, 1], - [0, 0, 1], - [0, 0, 1], - [0, 0, 1], - ]), - decimal=7) + vertices["normal"], + np.array( + [ + [0, 0, 1], + [0, 0, 1], + [0, 0, 1], + [0, 0, 1], + [0, 0, 1], + [0, 0, 1], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - vertices['colour'], - np.array([ - [0.25000000, 1.00000000, 0.00000000, 1.00000000], - [0.75000000, 1.00000000, 0.00000000, 1.00000000], - [0.25000000, 0.50000000, 0.00000000, 1.00000000], - [0.75000000, 0.50000000, 0.00000000, 1.00000000], - [0.25000000, 0.00000000, 0.00000000, 1.00000000], - [0.75000000, 0.00000000, 0.00000000, 1.00000000], - ]), - decimal=7) + vertices["colour"], + np.array( + [ + [0.25000000, 1.00000000, 0.00000000, 1.00000000], + [0.75000000, 1.00000000, 0.00000000, 1.00000000], + [0.25000000, 0.50000000, 0.00000000, 1.00000000], + [0.75000000, 0.50000000, 0.00000000, 1.00000000], + [0.25000000, 0.00000000, 0.00000000, 1.00000000], + [0.75000000, 0.00000000, 0.00000000, 1.00000000], + ] + ), + decimal=7, + ) np.testing.assert_equal( - faces, np.array([ - [0, 2, 1], - [2, 3, 1], - [2, 4, 3], - [4, 5, 3], - ])) + faces, + np.array( + [ + [0, 2, 1], + [2, 3, 1], + [2, 4, 3], + [4, 5, 3], + ] + ), + ) np.testing.assert_equal( outline, - np.array([ - [0, 2], - [2, 3], - [3, 1], - [1, 0], - [2, 4], - [4, 5], - [5, 3], - [3, 2], - ])) - - for plane in PLANE_TO_AXIS_MAPPING.keys(): + np.array( + [ + [0, 2], + [2, 3], + [3, 1], + [1, 0], + [2, 4], + [4, 5], + [5, 3], + [3, 2], + ] + ), + ) + + for plane in MAPPING_PLANE_TO_AXIS.keys(): np.testing.assert_almost_equal( - primitive_grid(axis=plane)[0]['position'], - primitive_grid( - axis=PLANE_TO_AXIS_MAPPING[plane])[0]['position'], - decimal=7) + primitive_grid(axis=plane)[0]["position"], + primitive_grid(axis=MAPPING_PLANE_TO_AXIS[plane])[0][ + "position" + ], + decimal=7, + ) class TestPrimitiveCube(unittest.TestCase): """ - Defines :func:`colour.geometry.primitives.primitive_cube` + Define :func:`colour.geometry.primitives.primitive_cube` definition unit tests methods. """ def test_primitive_cube(self): """ - Tests :func:`colour.geometry.primitives.primitive_cube` + Test :func:`colour.geometry.primitives.primitive_cube` definition. """ vertices, faces, outline = primitive_cube() np.testing.assert_almost_equal( - vertices['position'], - np.array([ - [-0.5, 0.5, -0.5], - [0.5, 0.5, -0.5], - [-0.5, -0.5, -0.5], - [0.5, -0.5, -0.5], - [-0.5, 0.5, 0.5], - [0.5, 0.5, 0.5], - [-0.5, -0.5, 0.5], - [0.5, -0.5, 0.5], - [0.5, -0.5, -0.5], - [0.5, -0.5, 0.5], - [-0.5, -0.5, -0.5], - [-0.5, -0.5, 0.5], - [0.5, 0.5, -0.5], - [0.5, 0.5, 0.5], - [-0.5, 0.5, -0.5], - [-0.5, 0.5, 0.5], - [-0.5, -0.5, 0.5], - [-0.5, 0.5, 0.5], - [-0.5, -0.5, -0.5], - [-0.5, 0.5, -0.5], - [0.5, -0.5, 0.5], - [0.5, 0.5, 0.5], - [0.5, -0.5, -0.5], - [0.5, 0.5, -0.5], - ]), - decimal=7) + vertices["position"], + np.array( + [ + [-0.5, 0.5, -0.5], + [0.5, 0.5, -0.5], + [-0.5, -0.5, -0.5], + [0.5, -0.5, -0.5], + [-0.5, 0.5, 0.5], + [0.5, 0.5, 0.5], + [-0.5, -0.5, 0.5], + [0.5, -0.5, 0.5], + [0.5, -0.5, -0.5], + [0.5, -0.5, 0.5], + [-0.5, -0.5, -0.5], + [-0.5, -0.5, 0.5], + [0.5, 0.5, -0.5], + [0.5, 0.5, 0.5], + [-0.5, 0.5, -0.5], + [-0.5, 0.5, 0.5], + [-0.5, -0.5, 0.5], + [-0.5, 0.5, 0.5], + [-0.5, -0.5, -0.5], + [-0.5, 0.5, -0.5], + [0.5, -0.5, 0.5], + [0.5, 0.5, 0.5], + [0.5, -0.5, -0.5], + [0.5, 0.5, -0.5], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - vertices['uv'], - np.array([ - [0, 1], - [1, 1], - [0, 0], - [1, 0], - [0, 1], - [1, 1], - [0, 0], - [1, 0], - [0, 1], - [1, 1], - [0, 0], - [1, 0], - [0, 1], - [1, 1], - [0, 0], - [1, 0], - [0, 1], - [1, 1], - [0, 0], - [1, 0], - [0, 1], - [1, 1], - [0, 0], - [1, 0], - ]), - decimal=7) + vertices["uv"], + np.array( + [ + [0, 1], + [1, 1], + [0, 0], + [1, 0], + [0, 1], + [1, 1], + [0, 0], + [1, 0], + [0, 1], + [1, 1], + [0, 0], + [1, 0], + [0, 1], + [1, 1], + [0, 0], + [1, 0], + [0, 1], + [1, 1], + [0, 0], + [1, 0], + [0, 1], + [1, 1], + [0, 0], + [1, 0], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - vertices['normal'], - np.array([ - [0, 0, -1.], - [0, 0, -1], - [0, 0, -1], - [0, 0, -1], - [0, 0, 1], - [0, 0, 1], - [0, 0, 1], - [0, 0, 1], - [0, -1, 0], - [0, -1, 0], - [0, -1, 0], - [0, -1, 0], - [0, 1, 0], - [0, 1, 0], - [0, 1, 0], - [0, 1, 0], - [-1, 0, 0], - [-1, 0, 0], - [-1, 0, 0], - [-1, 0, 0], - [1, 0, 0], - [1, 0, 0], - [1, 0, 0], - [1, 0, 0], - ]), - decimal=7) + vertices["normal"], + np.array( + [ + [0, 0, -1.0], + [0, 0, -1], + [0, 0, -1], + [0, 0, -1], + [0, 0, 1], + [0, 0, 1], + [0, 0, 1], + [0, 0, 1], + [0, -1, 0], + [0, -1, 0], + [0, -1, 0], + [0, -1, 0], + [0, 1, 0], + [0, 1, 0], + [0, 1, 0], + [0, 1, 0], + [-1, 0, 0], + [-1, 0, 0], + [-1, 0, 0], + [-1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - vertices['colour'], - np.array([ - [0, 1, 0, 1], - [1, 1, 0, 1], - [0, 0, 0, 1], - [1, 0, 0, 1], - [0, 1, 1, 1], - [1, 1, 1, 1], - [0, 0, 1, 1], - [1, 0, 1, 1], - [1, 0, 0, 1], - [1, 0, 1, 1], - [0, 0, 0, 1], - [0, 0, 1, 1], - [1, 1, 0, 1], - [1, 1, 1, 1], - [0, 1, 0, 1], - [0, 1, 1, 1], - [0, 0, 1, 1], - [0, 1, 1, 1], - [0, 0, 0, 1], - [0, 1, 0, 1], - [1, 0, 1, 1], - [1, 1, 1, 1], - [1, 0, 0, 1], - [1, 1, 0, 1], - ]), - decimal=7) + vertices["colour"], + np.array( + [ + [0, 1, 0, 1], + [1, 1, 0, 1], + [0, 0, 0, 1], + [1, 0, 0, 1], + [0, 1, 1, 1], + [1, 1, 1, 1], + [0, 0, 1, 1], + [1, 0, 1, 1], + [1, 0, 0, 1], + [1, 0, 1, 1], + [0, 0, 0, 1], + [0, 0, 1, 1], + [1, 1, 0, 1], + [1, 1, 1, 1], + [0, 1, 0, 1], + [0, 1, 1, 1], + [0, 0, 1, 1], + [0, 1, 1, 1], + [0, 0, 0, 1], + [0, 1, 0, 1], + [1, 0, 1, 1], + [1, 1, 1, 1], + [1, 0, 0, 1], + [1, 1, 0, 1], + ] + ), + decimal=7, + ) np.testing.assert_equal( faces, - np.array([ - [1, 2, 0], - [1, 3, 2], - [4, 6, 5], - [6, 7, 5], - [9, 10, 8], - [9, 11, 10], - [12, 14, 13], - [14, 15, 13], - [17, 18, 16], - [17, 19, 18], - [20, 22, 21], - [22, 23, 21], - ])) + np.array( + [ + [1, 2, 0], + [1, 3, 2], + [4, 6, 5], + [6, 7, 5], + [9, 10, 8], + [9, 11, 10], + [12, 14, 13], + [14, 15, 13], + [17, 18, 16], + [17, 19, 18], + [20, 22, 21], + [22, 23, 21], + ] + ), + ) np.testing.assert_equal( outline, - np.array([ - [0, 2], - [2, 3], - [3, 1], - [1, 0], - [4, 6], - [6, 7], - [7, 5], - [5, 4], - [8, 10], - [10, 11], - [11, 9], - [9, 8], - [12, 14], - [14, 15], - [15, 13], - [13, 12], - [16, 18], - [18, 19], - [19, 17], - [17, 16], - [20, 22], - [22, 23], - [23, 21], - [21, 20], - ])) + np.array( + [ + [0, 2], + [2, 3], + [3, 1], + [1, 0], + [4, 6], + [6, 7], + [7, 5], + [5, 4], + [8, 10], + [10, 11], + [11, 9], + [9, 8], + [12, 14], + [14, 15], + [15, 13], + [13, 12], + [16, 18], + [18, 19], + [19, 17], + [17, 16], + [20, 22], + [22, 23], + [23, 21], + [21, 20], + ] + ), + ) vertices, faces, outline = primitive_cube( width=0.2, @@ -338,389 +388,410 @@ def test_primitive_cube(self): depth=0.6, width_segments=1, height_segments=2, - depth_segments=3) + depth_segments=3, + ) np.testing.assert_almost_equal( - vertices['position'], - np.array([ - [-0.10000000, 0.30000001, -0.20000000], - [0.10000000, 0.30000001, -0.20000000], - [-0.10000000, 0.10000000, -0.20000000], - [0.10000000, 0.10000000, -0.20000000], - [-0.10000000, -0.10000000, -0.20000000], - [0.10000000, -0.10000000, -0.20000000], - [-0.10000000, -0.30000001, -0.20000000], - [0.10000000, -0.30000001, -0.20000000], - [-0.10000000, 0.30000001, 0.20000000], - [0.10000000, 0.30000001, 0.20000000], - [-0.10000000, 0.10000000, 0.20000000], - [0.10000000, 0.10000000, 0.20000000], - [-0.10000000, -0.10000000, 0.20000000], - [0.10000000, -0.10000000, 0.20000000], - [-0.10000000, -0.30000001, 0.20000000], - [0.10000000, -0.30000001, 0.20000000], - [0.10000000, -0.30000001, -0.20000000], - [0.10000000, -0.30000001, 0.00000000], - [0.10000000, -0.30000001, 0.20000000], - [-0.10000000, -0.30000001, -0.20000000], - [-0.10000000, -0.30000001, 0.00000000], - [-0.10000000, -0.30000001, 0.20000000], - [0.10000000, 0.30000001, -0.20000000], - [0.10000000, 0.30000001, 0.00000000], - [0.10000000, 0.30000001, 0.20000000], - [-0.10000000, 0.30000001, -0.20000000], - [-0.10000000, 0.30000001, 0.00000000], - [-0.10000000, 0.30000001, 0.20000000], - [-0.10000000, -0.30000001, 0.20000000], - [-0.10000000, -0.10000000, 0.20000000], - [-0.10000000, 0.10000000, 0.20000000], - [-0.10000000, 0.30000001, 0.20000000], - [-0.10000000, -0.30000001, -0.00000000], - [-0.10000000, -0.10000000, -0.00000000], - [-0.10000000, 0.10000000, -0.00000000], - [-0.10000000, 0.30000001, -0.00000000], - [-0.10000000, -0.30000001, -0.20000000], - [-0.10000000, -0.10000000, -0.20000000], - [-0.10000000, 0.10000000, -0.20000000], - [-0.10000000, 0.30000001, -0.20000000], - [0.10000000, -0.30000001, 0.20000000], - [0.10000000, -0.10000000, 0.20000000], - [0.10000000, 0.10000000, 0.20000000], - [0.10000000, 0.30000001, 0.20000000], - [0.10000000, -0.30000001, -0.00000000], - [0.10000000, -0.10000000, -0.00000000], - [0.10000000, 0.10000000, -0.00000000], - [0.10000000, 0.30000001, -0.00000000], - [0.10000000, -0.30000001, -0.20000000], - [0.10000000, -0.10000000, -0.20000000], - [0.10000000, 0.10000000, -0.20000000], - [0.10000000, 0.30000001, -0.20000000], - ]), - decimal=7) + vertices["position"], + np.array( + [ + [-0.10000000, 0.30000001, -0.20000000], + [0.10000000, 0.30000001, -0.20000000], + [-0.10000000, 0.10000000, -0.20000000], + [0.10000000, 0.10000000, -0.20000000], + [-0.10000000, -0.10000000, -0.20000000], + [0.10000000, -0.10000000, -0.20000000], + [-0.10000000, -0.30000001, -0.20000000], + [0.10000000, -0.30000001, -0.20000000], + [-0.10000000, 0.30000001, 0.20000000], + [0.10000000, 0.30000001, 0.20000000], + [-0.10000000, 0.10000000, 0.20000000], + [0.10000000, 0.10000000, 0.20000000], + [-0.10000000, -0.10000000, 0.20000000], + [0.10000000, -0.10000000, 0.20000000], + [-0.10000000, -0.30000001, 0.20000000], + [0.10000000, -0.30000001, 0.20000000], + [0.10000000, -0.30000001, -0.20000000], + [0.10000000, -0.30000001, 0.00000000], + [0.10000000, -0.30000001, 0.20000000], + [-0.10000000, -0.30000001, -0.20000000], + [-0.10000000, -0.30000001, 0.00000000], + [-0.10000000, -0.30000001, 0.20000000], + [0.10000000, 0.30000001, -0.20000000], + [0.10000000, 0.30000001, 0.00000000], + [0.10000000, 0.30000001, 0.20000000], + [-0.10000000, 0.30000001, -0.20000000], + [-0.10000000, 0.30000001, 0.00000000], + [-0.10000000, 0.30000001, 0.20000000], + [-0.10000000, -0.30000001, 0.20000000], + [-0.10000000, -0.10000000, 0.20000000], + [-0.10000000, 0.10000000, 0.20000000], + [-0.10000000, 0.30000001, 0.20000000], + [-0.10000000, -0.30000001, -0.00000000], + [-0.10000000, -0.10000000, -0.00000000], + [-0.10000000, 0.10000000, -0.00000000], + [-0.10000000, 0.30000001, -0.00000000], + [-0.10000000, -0.30000001, -0.20000000], + [-0.10000000, -0.10000000, -0.20000000], + [-0.10000000, 0.10000000, -0.20000000], + [-0.10000000, 0.30000001, -0.20000000], + [0.10000000, -0.30000001, 0.20000000], + [0.10000000, -0.10000000, 0.20000000], + [0.10000000, 0.10000000, 0.20000000], + [0.10000000, 0.30000001, 0.20000000], + [0.10000000, -0.30000001, -0.00000000], + [0.10000000, -0.10000000, -0.00000000], + [0.10000000, 0.10000000, -0.00000000], + [0.10000000, 0.30000001, -0.00000000], + [0.10000000, -0.30000001, -0.20000000], + [0.10000000, -0.10000000, -0.20000000], + [0.10000000, 0.10000000, -0.20000000], + [0.10000000, 0.30000001, -0.20000000], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - vertices['uv'], - np.array([ - [0.00000000, 1.00000000], - [1.00000000, 1.00000000], - [0.00000000, 0.66666669], - [1.00000000, 0.66666669], - [0.00000000, 0.33333334], - [1.00000000, 0.33333334], - [0.00000000, 0.00000000], - [1.00000000, 0.00000000], - [0.00000000, 1.00000000], - [1.00000000, 1.00000000], - [0.00000000, 0.66666669], - [1.00000000, 0.66666669], - [0.00000000, 0.33333334], - [1.00000000, 0.33333334], - [0.00000000, 0.00000000], - [1.00000000, 0.00000000], - [0.00000000, 1.00000000], - [0.50000000, 1.00000000], - [1.00000000, 1.00000000], - [0.00000000, 0.00000000], - [0.50000000, 0.00000000], - [1.00000000, 0.00000000], - [0.00000000, 1.00000000], - [0.50000000, 1.00000000], - [1.00000000, 1.00000000], - [0.00000000, 0.00000000], - [0.50000000, 0.00000000], - [1.00000000, 0.00000000], - [0.00000000, 1.00000000], - [0.33333334, 1.00000000], - [0.66666669, 1.00000000], - [1.00000000, 1.00000000], - [0.00000000, 0.50000000], - [0.33333334, 0.50000000], - [0.66666669, 0.50000000], - [1.00000000, 0.50000000], - [0.00000000, 0.00000000], - [0.33333334, 0.00000000], - [0.66666669, 0.00000000], - [1.00000000, 0.00000000], - [0.00000000, 1.00000000], - [0.33333334, 1.00000000], - [0.66666669, 1.00000000], - [1.00000000, 1.00000000], - [0.00000000, 0.50000000], - [0.33333334, 0.50000000], - [0.66666669, 0.50000000], - [1.00000000, 0.50000000], - [0.00000000, 0.00000000], - [0.33333334, 0.00000000], - [0.66666669, 0.00000000], - [1.00000000, 0.00000000], - ]), - decimal=7) + vertices["uv"], + np.array( + [ + [0.00000000, 1.00000000], + [1.00000000, 1.00000000], + [0.00000000, 0.66666669], + [1.00000000, 0.66666669], + [0.00000000, 0.33333334], + [1.00000000, 0.33333334], + [0.00000000, 0.00000000], + [1.00000000, 0.00000000], + [0.00000000, 1.00000000], + [1.00000000, 1.00000000], + [0.00000000, 0.66666669], + [1.00000000, 0.66666669], + [0.00000000, 0.33333334], + [1.00000000, 0.33333334], + [0.00000000, 0.00000000], + [1.00000000, 0.00000000], + [0.00000000, 1.00000000], + [0.50000000, 1.00000000], + [1.00000000, 1.00000000], + [0.00000000, 0.00000000], + [0.50000000, 0.00000000], + [1.00000000, 0.00000000], + [0.00000000, 1.00000000], + [0.50000000, 1.00000000], + [1.00000000, 1.00000000], + [0.00000000, 0.00000000], + [0.50000000, 0.00000000], + [1.00000000, 0.00000000], + [0.00000000, 1.00000000], + [0.33333334, 1.00000000], + [0.66666669, 1.00000000], + [1.00000000, 1.00000000], + [0.00000000, 0.50000000], + [0.33333334, 0.50000000], + [0.66666669, 0.50000000], + [1.00000000, 0.50000000], + [0.00000000, 0.00000000], + [0.33333334, 0.00000000], + [0.66666669, 0.00000000], + [1.00000000, 0.00000000], + [0.00000000, 1.00000000], + [0.33333334, 1.00000000], + [0.66666669, 1.00000000], + [1.00000000, 1.00000000], + [0.00000000, 0.50000000], + [0.33333334, 0.50000000], + [0.66666669, 0.50000000], + [1.00000000, 0.50000000], + [0.00000000, 0.00000000], + [0.33333334, 0.00000000], + [0.66666669, 0.00000000], + [1.00000000, 0.00000000], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - vertices['normal'], - np.array([ - [-0., -0., -1.], - [0, 0, -1], - [0, 0, -1], - [0, 0, -1], - [0, 0, -1], - [0, 0, -1], - [0, 0, -1], - [0, 0, -1], - [0, 0, 1], - [0, 0, 1], - [0, 0, 1], - [0, 0, 1], - [0, 0, 1], - [0, 0, 1], - [0, 0, 1], - [0, 0, 1], - [0, -1, 0], - [0, -1, 0], - [0, -1, 0], - [0, -1, 0], - [0, -1, 0], - [0, -1, 0], - [0, 1, 0], - [0, 1, 0], - [0, 1, 0], - [0, 1, 0], - [0, 1, 0], - [0, 1, 0], - [-1, 0, 0], - [-1, 0, 0], - [-1, 0, 0], - [-1, 0, 0], - [-1, 0, 0], - [-1, 0, 0], - [-1, 0, 0], - [-1, 0, 0], - [-1, 0, 0], - [-1, 0, 0], - [-1, 0, 0], - [-1, 0, 0], - [1, 0, 0], - [1, 0, 0], - [1, 0, 0], - [1, 0, 0], - [1, 0, 0], - [1, 0, 0], - [1, 0, 0], - [1, 0, 0], - [1, 0, 0], - [1, 0, 0], - [1, 0, 0], - [1, 0, 0], - ]), - decimal=7) + vertices["normal"], + np.array( + [ + [-0.0, -0.0, -1.0], + [0, 0, -1], + [0, 0, -1], + [0, 0, -1], + [0, 0, -1], + [0, 0, -1], + [0, 0, -1], + [0, 0, -1], + [0, 0, 1], + [0, 0, 1], + [0, 0, 1], + [0, 0, 1], + [0, 0, 1], + [0, 0, 1], + [0, 0, 1], + [0, 0, 1], + [0, -1, 0], + [0, -1, 0], + [0, -1, 0], + [0, -1, 0], + [0, -1, 0], + [0, -1, 0], + [0, 1, 0], + [0, 1, 0], + [0, 1, 0], + [0, 1, 0], + [0, 1, 0], + [0, 1, 0], + [-1, 0, 0], + [-1, 0, 0], + [-1, 0, 0], + [-1, 0, 0], + [-1, 0, 0], + [-1, 0, 0], + [-1, 0, 0], + [-1, 0, 0], + [-1, 0, 0], + [-1, 0, 0], + [-1, 0, 0], + [-1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - vertices['colour'], - np.array([ - [0.33333334, 1.00000000, 0.16666667, 1.00000000], - [0.66666669, 1.00000000, 0.16666667, 1.00000000], - [0.33333334, 0.66666669, 0.16666667, 1.00000000], - [0.66666669, 0.66666669, 0.16666667, 1.00000000], - [0.33333334, 0.33333334, 0.16666667, 1.00000000], - [0.66666669, 0.33333334, 0.16666667, 1.00000000], - [0.33333334, 0.00000000, 0.16666667, 1.00000000], - [0.66666669, 0.00000000, 0.16666667, 1.00000000], - [0.33333334, 1.00000000, 0.83333331, 1.00000000], - [0.66666669, 1.00000000, 0.83333331, 1.00000000], - [0.33333334, 0.66666669, 0.83333331, 1.00000000], - [0.66666669, 0.66666669, 0.83333331, 1.00000000], - [0.33333334, 0.33333334, 0.83333331, 1.00000000], - [0.66666669, 0.33333334, 0.83333331, 1.00000000], - [0.33333334, 0.00000000, 0.83333331, 1.00000000], - [0.66666669, 0.00000000, 0.83333331, 1.00000000], - [0.66666669, 0.00000000, 0.16666667, 1.00000000], - [0.66666669, 0.00000000, 0.50000000, 1.00000000], - [0.66666669, 0.00000000, 0.83333331, 1.00000000], - [0.33333334, 0.00000000, 0.16666667, 1.00000000], - [0.33333334, 0.00000000, 0.50000000, 1.00000000], - [0.33333334, 0.00000000, 0.83333331, 1.00000000], - [0.66666669, 1.00000000, 0.16666667, 1.00000000], - [0.66666669, 1.00000000, 0.50000000, 1.00000000], - [0.66666669, 1.00000000, 0.83333331, 1.00000000], - [0.33333334, 1.00000000, 0.16666667, 1.00000000], - [0.33333334, 1.00000000, 0.50000000, 1.00000000], - [0.33333334, 1.00000000, 0.83333331, 1.00000000], - [0.33333334, 0.00000000, 0.83333331, 1.00000000], - [0.33333334, 0.33333334, 0.83333331, 1.00000000], - [0.33333334, 0.66666669, 0.83333331, 1.00000000], - [0.33333334, 1.00000000, 0.83333331, 1.00000000], - [0.33333334, 0.00000000, 0.50000000, 1.00000000], - [0.33333334, 0.33333334, 0.50000000, 1.00000000], - [0.33333334, 0.66666669, 0.50000000, 1.00000000], - [0.33333334, 1.00000000, 0.50000000, 1.00000000], - [0.33333334, 0.00000000, 0.16666667, 1.00000000], - [0.33333334, 0.33333334, 0.16666667, 1.00000000], - [0.33333334, 0.66666669, 0.16666667, 1.00000000], - [0.33333334, 1.00000000, 0.16666667, 1.00000000], - [0.66666669, 0.00000000, 0.83333331, 1.00000000], - [0.66666669, 0.33333334, 0.83333331, 1.00000000], - [0.66666669, 0.66666669, 0.83333331, 1.00000000], - [0.66666669, 1.00000000, 0.83333331, 1.00000000], - [0.66666669, 0.00000000, 0.50000000, 1.00000000], - [0.66666669, 0.33333334, 0.50000000, 1.00000000], - [0.66666669, 0.66666669, 0.50000000, 1.00000000], - [0.66666669, 1.00000000, 0.50000000, 1.00000000], - [0.66666669, 0.00000000, 0.16666667, 1.00000000], - [0.66666669, 0.33333334, 0.16666667, 1.00000000], - [0.66666669, 0.66666669, 0.16666667, 1.00000000], - [0.66666669, 1.00000000, 0.16666667, 1.00000000], - ]), - decimal=7) + vertices["colour"], + np.array( + [ + [0.33333334, 1.00000000, 0.16666667, 1.00000000], + [0.66666669, 1.00000000, 0.16666667, 1.00000000], + [0.33333334, 0.66666669, 0.16666667, 1.00000000], + [0.66666669, 0.66666669, 0.16666667, 1.00000000], + [0.33333334, 0.33333334, 0.16666667, 1.00000000], + [0.66666669, 0.33333334, 0.16666667, 1.00000000], + [0.33333334, 0.00000000, 0.16666667, 1.00000000], + [0.66666669, 0.00000000, 0.16666667, 1.00000000], + [0.33333334, 1.00000000, 0.83333331, 1.00000000], + [0.66666669, 1.00000000, 0.83333331, 1.00000000], + [0.33333334, 0.66666669, 0.83333331, 1.00000000], + [0.66666669, 0.66666669, 0.83333331, 1.00000000], + [0.33333334, 0.33333334, 0.83333331, 1.00000000], + [0.66666669, 0.33333334, 0.83333331, 1.00000000], + [0.33333334, 0.00000000, 0.83333331, 1.00000000], + [0.66666669, 0.00000000, 0.83333331, 1.00000000], + [0.66666669, 0.00000000, 0.16666667, 1.00000000], + [0.66666669, 0.00000000, 0.50000000, 1.00000000], + [0.66666669, 0.00000000, 0.83333331, 1.00000000], + [0.33333334, 0.00000000, 0.16666667, 1.00000000], + [0.33333334, 0.00000000, 0.50000000, 1.00000000], + [0.33333334, 0.00000000, 0.83333331, 1.00000000], + [0.66666669, 1.00000000, 0.16666667, 1.00000000], + [0.66666669, 1.00000000, 0.50000000, 1.00000000], + [0.66666669, 1.00000000, 0.83333331, 1.00000000], + [0.33333334, 1.00000000, 0.16666667, 1.00000000], + [0.33333334, 1.00000000, 0.50000000, 1.00000000], + [0.33333334, 1.00000000, 0.83333331, 1.00000000], + [0.33333334, 0.00000000, 0.83333331, 1.00000000], + [0.33333334, 0.33333334, 0.83333331, 1.00000000], + [0.33333334, 0.66666669, 0.83333331, 1.00000000], + [0.33333334, 1.00000000, 0.83333331, 1.00000000], + [0.33333334, 0.00000000, 0.50000000, 1.00000000], + [0.33333334, 0.33333334, 0.50000000, 1.00000000], + [0.33333334, 0.66666669, 0.50000000, 1.00000000], + [0.33333334, 1.00000000, 0.50000000, 1.00000000], + [0.33333334, 0.00000000, 0.16666667, 1.00000000], + [0.33333334, 0.33333334, 0.16666667, 1.00000000], + [0.33333334, 0.66666669, 0.16666667, 1.00000000], + [0.33333334, 1.00000000, 0.16666667, 1.00000000], + [0.66666669, 0.00000000, 0.83333331, 1.00000000], + [0.66666669, 0.33333334, 0.83333331, 1.00000000], + [0.66666669, 0.66666669, 0.83333331, 1.00000000], + [0.66666669, 1.00000000, 0.83333331, 1.00000000], + [0.66666669, 0.00000000, 0.50000000, 1.00000000], + [0.66666669, 0.33333334, 0.50000000, 1.00000000], + [0.66666669, 0.66666669, 0.50000000, 1.00000000], + [0.66666669, 1.00000000, 0.50000000, 1.00000000], + [0.66666669, 0.00000000, 0.16666667, 1.00000000], + [0.66666669, 0.33333334, 0.16666667, 1.00000000], + [0.66666669, 0.66666669, 0.16666667, 1.00000000], + [0.66666669, 1.00000000, 0.16666667, 1.00000000], + ] + ), + decimal=7, + ) np.testing.assert_equal( faces, - np.array([ - [1, 2, 0], - [1, 3, 2], - [3, 4, 2], - [3, 5, 4], - [5, 6, 4], - [5, 7, 6], - [8, 10, 9], - [10, 11, 9], - [10, 12, 11], - [12, 13, 11], - [12, 14, 13], - [14, 15, 13], - [17, 19, 16], - [17, 20, 19], - [18, 20, 17], - [18, 21, 20], - [22, 25, 23], - [25, 26, 23], - [23, 26, 24], - [26, 27, 24], - [29, 32, 28], - [29, 33, 32], - [30, 33, 29], - [30, 34, 33], - [31, 34, 30], - [31, 35, 34], - [33, 36, 32], - [33, 37, 36], - [34, 37, 33], - [34, 38, 37], - [35, 38, 34], - [35, 39, 38], - [40, 44, 41], - [44, 45, 41], - [41, 45, 42], - [45, 46, 42], - [42, 46, 43], - [46, 47, 43], - [44, 48, 45], - [48, 49, 45], - [45, 49, 46], - [49, 50, 46], - [46, 50, 47], - [50, 51, 47], - ])) + np.array( + [ + [1, 2, 0], + [1, 3, 2], + [3, 4, 2], + [3, 5, 4], + [5, 6, 4], + [5, 7, 6], + [8, 10, 9], + [10, 11, 9], + [10, 12, 11], + [12, 13, 11], + [12, 14, 13], + [14, 15, 13], + [17, 19, 16], + [17, 20, 19], + [18, 20, 17], + [18, 21, 20], + [22, 25, 23], + [25, 26, 23], + [23, 26, 24], + [26, 27, 24], + [29, 32, 28], + [29, 33, 32], + [30, 33, 29], + [30, 34, 33], + [31, 34, 30], + [31, 35, 34], + [33, 36, 32], + [33, 37, 36], + [34, 37, 33], + [34, 38, 37], + [35, 38, 34], + [35, 39, 38], + [40, 44, 41], + [44, 45, 41], + [41, 45, 42], + [45, 46, 42], + [42, 46, 43], + [46, 47, 43], + [44, 48, 45], + [48, 49, 45], + [45, 49, 46], + [49, 50, 46], + [46, 50, 47], + [50, 51, 47], + ] + ), + ) np.testing.assert_equal( outline, - np.array([ - [0, 2], - [2, 3], - [3, 1], - [1, 0], - [2, 4], - [4, 5], - [5, 3], - [3, 2], - [4, 6], - [6, 7], - [7, 5], - [5, 4], - [8, 10], - [10, 11], - [11, 9], - [9, 8], - [10, 12], - [12, 13], - [13, 11], - [11, 10], - [12, 14], - [14, 15], - [15, 13], - [13, 12], - [16, 19], - [19, 20], - [20, 17], - [17, 16], - [17, 20], - [20, 21], - [21, 18], - [18, 17], - [22, 25], - [25, 26], - [26, 23], - [23, 22], - [23, 26], - [26, 27], - [27, 24], - [24, 23], - [28, 32], - [32, 33], - [33, 29], - [29, 28], - [29, 33], - [33, 34], - [34, 30], - [30, 29], - [30, 34], - [34, 35], - [35, 31], - [31, 30], - [32, 36], - [36, 37], - [37, 33], - [33, 32], - [33, 37], - [37, 38], - [38, 34], - [34, 33], - [34, 38], - [38, 39], - [39, 35], - [35, 34], - [40, 44], - [44, 45], - [45, 41], - [41, 40], - [41, 45], - [45, 46], - [46, 42], - [42, 41], - [42, 46], - [46, 47], - [47, 43], - [43, 42], - [44, 48], - [48, 49], - [49, 45], - [45, 44], - [45, 49], - [49, 50], - [50, 46], - [46, 45], - [46, 50], - [50, 51], - [51, 47], - [47, 46], - ])) - - for plane in PLANE_TO_AXIS_MAPPING.keys(): + np.array( + [ + [0, 2], + [2, 3], + [3, 1], + [1, 0], + [2, 4], + [4, 5], + [5, 3], + [3, 2], + [4, 6], + [6, 7], + [7, 5], + [5, 4], + [8, 10], + [10, 11], + [11, 9], + [9, 8], + [10, 12], + [12, 13], + [13, 11], + [11, 10], + [12, 14], + [14, 15], + [15, 13], + [13, 12], + [16, 19], + [19, 20], + [20, 17], + [17, 16], + [17, 20], + [20, 21], + [21, 18], + [18, 17], + [22, 25], + [25, 26], + [26, 23], + [23, 22], + [23, 26], + [26, 27], + [27, 24], + [24, 23], + [28, 32], + [32, 33], + [33, 29], + [29, 28], + [29, 33], + [33, 34], + [34, 30], + [30, 29], + [30, 34], + [34, 35], + [35, 31], + [31, 30], + [32, 36], + [36, 37], + [37, 33], + [33, 32], + [33, 37], + [37, 38], + [38, 34], + [34, 33], + [34, 38], + [38, 39], + [39, 35], + [35, 34], + [40, 44], + [44, 45], + [45, 41], + [41, 40], + [41, 45], + [45, 46], + [46, 42], + [42, 41], + [42, 46], + [46, 47], + [47, 43], + [43, 42], + [44, 48], + [48, 49], + [49, 45], + [45, 44], + [45, 49], + [49, 50], + [50, 46], + [46, 45], + [46, 50], + [50, 51], + [51, 47], + [47, 46], + ] + ), + ) + + for plane in MAPPING_PLANE_TO_AXIS.keys(): np.testing.assert_almost_equal( - primitive_cube(planes=[plane])[0]['position'], - primitive_cube( - planes=[PLANE_TO_AXIS_MAPPING[plane]])[0]['position'], - decimal=7) + primitive_cube(planes=[plane])[0]["position"], + primitive_cube(planes=[MAPPING_PLANE_TO_AXIS[plane]])[0][ + "position" + ], + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/geometry/tests/test_section.py b/colour/geometry/tests/test_section.py new file mode 100644 index 0000000000..930d60aa26 --- /dev/null +++ b/colour/geometry/tests/test_section.py @@ -0,0 +1,232 @@ +"""Defines the unit tests for the :mod:`colour.geometry.section` module.""" + +import numpy as np +import unittest + +from colour.geometry.section import ( + edges_to_chord, + close_chord, + unique_vertices, +) +from colour.geometry import primitive_cube, hull_section +from colour.utilities import is_trimesh_installed + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestEdgesToChord", + "TestCloseChord", + "TestUniqueVertices", + "TestHullSection", +] + + +class TestEdgesToChord(unittest.TestCase): + """ + Define :func:`colour.geometry.section.edges_to_chord` definition unit + tests methods. + """ + + def test_edges_to_chord(self): + """Test :func:`colour.geometry.section.edges_to_chord` definition.""" + + edges = np.array( + [ + [[0.0, -0.5, 0.0], [0.5, -0.5, 0.0]], + [[-0.5, -0.5, 0.0], [0.0, -0.5, 0.0]], + [[0.5, 0.5, 0.0], [0.0, 0.5, 0.0]], + [[0.0, 0.5, 0.0], [-0.5, 0.5, 0.0]], + [[-0.5, 0.0, 0.0], [-0.5, -0.5, 0.0]], + [[-0.5, 0.5, 0.0], [-0.5, 0.0, 0.0]], + [[0.5, -0.5, 0.0], [0.5, 0.0, 0.0]], + [[0.5, 0.0, 0.0], [0.5, 0.5, 0.0]], + ] + ) + + np.testing.assert_almost_equal( + edges_to_chord(edges), + np.array( + [ + [0.0, -0.5, 0.0], + [0.5, -0.5, 0.0], + [0.5, -0.5, -0.0], + [0.5, 0.0, -0.0], + [0.5, 0.0, -0.0], + [0.5, 0.5, -0.0], + [0.5, 0.5, 0.0], + [0.0, 0.5, 0.0], + [0.0, 0.5, 0.0], + [-0.5, 0.5, 0.0], + [-0.5, 0.5, -0.0], + [-0.5, 0.0, -0.0], + [-0.5, 0.0, -0.0], + [-0.5, -0.5, -0.0], + [-0.5, -0.5, 0.0], + [0.0, -0.5, 0.0], + ] + ), + ) + + np.testing.assert_almost_equal( + edges_to_chord(edges, 5), + np.array( + [ + [-0.5, 0.5, 0.0], + [-0.5, 0.0, 0.0], + [-0.5, 0.0, 0.0], + [-0.5, -0.5, 0.0], + [-0.5, -0.5, 0.0], + [0.0, -0.5, 0.0], + [0.0, -0.5, 0.0], + [0.5, -0.5, 0.0], + [0.5, -0.5, 0.0], + [0.5, 0.0, 0.0], + [0.5, 0.0, 0.0], + [0.5, 0.5, 0.0], + [0.5, 0.5, 0.0], + [0.0, 0.5, 0.0], + [0.0, 0.5, 0.0], + [-0.5, 0.5, 0.0], + ] + ), + ) + + +class TestCloseChord(unittest.TestCase): + """ + Define :func:`colour.geometry.section.close_chord` definition unit tests + methods. + """ + + def test_close_chord(self): + """Test :func:`colour.geometry.section.close_chord` definition.""" + + np.testing.assert_almost_equal( + close_chord(np.array([[0.0, 0.5, 0.0], [0.0, 0.0, 0.5]])), + np.array([[0.0, 0.5, 0.0], [0.0, 0.0, 0.5], [0.0, 0.5, 0.0]]), + ) + + +class TestUniqueVertices(unittest.TestCase): + """ + Define :func:`colour.geometry.section.unique_vertices` definition unit + tests methods. + """ + + def test_unique_vertices(self): + """Test :func:`colour.geometry.section.unique_vertices` definition.""" + + np.testing.assert_almost_equal( + unique_vertices( + np.array([[0.0, 0.5, 0.0], [0.0, 0.0, 0.5], [0.0, 0.5, 0.0]]) + ), + np.array([[0.0, 0.5, 0.0], [0.0, 0.0, 0.5]]), + ) + + np.testing.assert_almost_equal( + unique_vertices( + np.array( + [[0.0, 0.51, 0.0], [0.0, 0.0, 0.51], [0.0, 0.52, 0.0]] + ), + 1, + ), + np.array([[0.0, 0.5, 0.0], [0.0, 0.0, 0.5]]), + ) + + +class TestHullSection(unittest.TestCase): + """ + Define :func:`colour.geometry.section.hull_section` definition unit tests + methods. + """ + + def test_hull_section(self): + """Test :func:`colour.geometry.section.hull_section` definition.""" + + if not is_trimesh_installed: # pragma: no cover + return + + import trimesh + + vertices, faces, _outline = primitive_cube(1, 1, 1, 2, 2, 2) + hull = trimesh.Trimesh(vertices["position"], faces, process=False) + + np.testing.assert_almost_equal( + hull_section(hull, origin=0), + np.array( + [ + [0.0, -0.5, 0.0], + [0.5, -0.5, 0.0], + [0.5, 0.0, 0.0], + [0.5, 0.5, 0.0], + [0.0, 0.5, 0.0], + [-0.5, 0.5, 0.0], + [-0.5, 0.0, 0.0], + [-0.5, -0.5, 0.0], + [0.0, -0.5, 0.0], + ] + ), + ) + + np.testing.assert_almost_equal( + hull_section(hull, axis="+x", origin=0), + np.array( + [ + [0.0, 0.0, -0.5], + [0.0, 0.5, -0.5], + [0.0, 0.5, 0.0], + [0.0, 0.5, 0.5], + [0.0, 0.0, 0.5], + [0.0, -0.5, 0.5], + [0.0, -0.5, 0.0], + [0.0, -0.5, -0.5], + [0.0, 0.0, -0.5], + ] + ), + ) + + np.testing.assert_almost_equal( + hull_section(hull, axis="+y", origin=0), + np.array( + [ + [0.0, 0.0, -0.5], + [-0.5, 0.0, -0.5], + [-0.5, 0.0, 0.0], + [-0.5, 0.0, 0.5], + [0.0, 0.0, 0.5], + [0.5, 0.0, 0.5], + [0.5, 0.0, 0.0], + [0.5, 0.0, -0.5], + [0.0, 0.0, -0.5], + ] + ), + ) + + hull.vertices = (hull.vertices + 0.5) * 2 + np.testing.assert_almost_equal( + hull_section(hull, origin=0.5, normalise=True), + np.array( + [ + [1.0, 0.0, 1.0], + [2.0, 0.0, 1.0], + [2.0, 1.0, 1.0], + [2.0, 2.0, 1.0], + [1.0, 2.0, 1.0], + [0.0, 2.0, 1.0], + [0.0, 1.0, 1.0], + [0.0, 0.0, 1.0], + [1.0, 0.0, 1.0], + ] + ), + ) + + self.assertRaises(ValueError, hull_section, hull, origin=-1) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/geometry/tests/test_vertices.py b/colour/geometry/tests/test_vertices.py index 51cc345e15..a4fd41a555 100644 --- a/colour/geometry/tests/test_vertices.py +++ b/colour/geometry/tests/test_vertices.py @@ -1,57 +1,60 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.geometry.vertices` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.geometry.vertices` module.""" import numpy as np import unittest from colour.geometry import ( - PLANE_TO_AXIS_MAPPING, primitive_vertices_quad_mpl, - primitive_vertices_grid_mpl, primitive_vertices_cube_mpl, - primitive_vertices_sphere) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + MAPPING_PLANE_TO_AXIS, + primitive_vertices_quad_mpl, + primitive_vertices_grid_mpl, + primitive_vertices_cube_mpl, + primitive_vertices_sphere, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'TestPrimitiveVerticesQuadMpl', 'TestPrimitiveVerticesGridMpl', - 'TestPrimitiveVerticesCubeMpl', 'TestPrimitiveVerticesSphere' + "TestPrimitiveVerticesQuadMpl", + "TestPrimitiveVerticesGridMpl", + "TestPrimitiveVerticesCubeMpl", + "TestPrimitiveVerticesSphere", ] class TestPrimitiveVerticesQuadMpl(unittest.TestCase): """ - Defines :func:`colour.geometry.vertices.primitive_vertices_quad_mpl` + Define :func:`colour.geometry.vertices.primitive_vertices_quad_mpl` definition unit tests methods. """ def test_primitive_vertices_quad_mpl(self): """ - Tests :func:`colour.geometry.vertices.primitive_vertices_quad_mpl` + Test :func:`colour.geometry.vertices.primitive_vertices_quad_mpl` definition. """ np.testing.assert_almost_equal( primitive_vertices_quad_mpl(), np.array([[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - primitive_vertices_quad_mpl(axis='+y'), + primitive_vertices_quad_mpl(axis="+y"), np.array([[0, 0, 0], [1, 0, 0], [1, 0, 1], [0, 0, 1]]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - primitive_vertices_quad_mpl(axis='+x'), + primitive_vertices_quad_mpl(axis="+x"), np.array([[0, 0, 0], [0, 1, 0], [0, 1, 1], [0, 0, 1]]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( primitive_vertices_quad_mpl( @@ -59,14 +62,18 @@ def test_primitive_vertices_quad_mpl(self): height=0.4, depth=0.6, origin=np.array([0.2, 0.4]), - axis='+z'), - np.array([ - [0.2, 0.4, 0.6], - [0.4, 0.4, 0.6], - [0.4, 0.8, 0.6], - [0.2, 0.8, 0.6], - ]), - decimal=7) + axis="+z", + ), + np.array( + [ + [0.2, 0.4, 0.6], + [0.4, 0.4, 0.6], + [0.4, 0.8, 0.6], + [0.2, 0.8, 0.6], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( primitive_vertices_quad_mpl( @@ -74,51 +81,60 @@ def test_primitive_vertices_quad_mpl(self): height=-0.4, depth=-0.6, origin=np.array([-0.2, -0.4]), - axis='+z'), - np.array([ - [-0.2, -0.4, -0.6], - [-0.4, -0.4, -0.6], - [-0.4, -0.8, -0.6], - [-0.2, -0.8, -0.6], - ]), - decimal=7) - - for plane in ('xy', 'xz', 'yz'): + axis="+z", + ), + np.array( + [ + [-0.2, -0.4, -0.6], + [-0.4, -0.4, -0.6], + [-0.4, -0.8, -0.6], + [-0.2, -0.8, -0.6], + ] + ), + decimal=7, + ) + + for plane in ("xy", "xz", "yz"): np.testing.assert_almost_equal( primitive_vertices_quad_mpl(axis=plane), - primitive_vertices_quad_mpl(axis=PLANE_TO_AXIS_MAPPING[plane]), - decimal=7) + primitive_vertices_quad_mpl(axis=MAPPING_PLANE_TO_AXIS[plane]), + decimal=7, + ) self.assertRaises( - ValueError, lambda: primitive_vertices_quad_mpl(axis='Undefined')) + ValueError, lambda: primitive_vertices_quad_mpl(axis="Undefined") + ) class TestPrimitiveVerticesGridMpl(unittest.TestCase): """ - Defines :func:`colour.geometry.vertices.primitive_vertices_grid_mpl` + Define :func:`colour.geometry.vertices.primitive_vertices_grid_mpl` definition unit tests methods. """ def test_primitive_vertices_grid_mpl(self): """ - Tests :func:`colour.geometry.vertices.primitive_vertices_grid_mpl` + Test :func:`colour.geometry.vertices.primitive_vertices_grid_mpl` definition. """ np.testing.assert_almost_equal( primitive_vertices_grid_mpl(), np.array([[[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]]]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - primitive_vertices_grid_mpl(axis='+y'), + primitive_vertices_grid_mpl(axis="+y"), np.array([[[0, 0, 0], [1, 0, 0], [1, 0, 1], [0, 0, 1]]]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - primitive_vertices_grid_mpl(axis='+x'), + primitive_vertices_grid_mpl(axis="+x"), np.array([[[0, 0, 0], [0, 1, 0], [0, 1, 1], [0, 0, 1]]]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( primitive_vertices_grid_mpl( @@ -128,22 +144,26 @@ def test_primitive_vertices_grid_mpl(self): width_segments=1, height_segments=2, origin=np.array([0.2, 0.4]), - axis='+z'), - np.array([ - [ - [0.20000000, 0.40000000, 0.60000000], - [0.40000000, 0.40000000, 0.60000000], - [0.40000000, 0.60000000, 0.60000000], - [0.20000000, 0.60000000, 0.60000000], - ], - [ - [0.20000000, 0.60000000, 0.60000000], - [0.40000000, 0.60000000, 0.60000000], - [0.40000000, 0.80000000, 0.60000000], - [0.20000000, 0.80000000, 0.60000000], - ], - ]), - decimal=7) + axis="+z", + ), + np.array( + [ + [ + [0.20000000, 0.40000000, 0.60000000], + [0.40000000, 0.40000000, 0.60000000], + [0.40000000, 0.60000000, 0.60000000], + [0.20000000, 0.60000000, 0.60000000], + ], + [ + [0.20000000, 0.60000000, 0.60000000], + [0.40000000, 0.60000000, 0.60000000], + [0.40000000, 0.80000000, 0.60000000], + [0.20000000, 0.80000000, 0.60000000], + ], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( primitive_vertices_grid_mpl( @@ -153,107 +173,120 @@ def test_primitive_vertices_grid_mpl(self): width_segments=1, height_segments=2, origin=np.array([-0.2, -0.4]), - axis='+z'), - np.array([ - [ - [-0.20000000, -0.40000000, -0.60000000], - [-0.40000000, -0.40000000, -0.60000000], - [-0.40000000, -0.60000000, -0.60000000], - [-0.20000000, -0.60000000, -0.60000000], - ], - [ - [-0.20000000, -0.60000000, -0.60000000], - [-0.40000000, -0.60000000, -0.60000000], - [-0.40000000, -0.80000000, -0.60000000], - [-0.20000000, -0.80000000, -0.60000000], - ], - ]), - decimal=7) + axis="+z", + ), + np.array( + [ + [ + [-0.20000000, -0.40000000, -0.60000000], + [-0.40000000, -0.40000000, -0.60000000], + [-0.40000000, -0.60000000, -0.60000000], + [-0.20000000, -0.60000000, -0.60000000], + ], + [ + [-0.20000000, -0.60000000, -0.60000000], + [-0.40000000, -0.60000000, -0.60000000], + [-0.40000000, -0.80000000, -0.60000000], + [-0.20000000, -0.80000000, -0.60000000], + ], + ] + ), + decimal=7, + ) class TestPrimitiveVerticesCubeMpl(unittest.TestCase): """ - Defines :func:`colour.geometry.vertices.primitive_vertices_cube_mpl` + Define :func:`colour.geometry.vertices.primitive_vertices_cube_mpl` definition unit tests methods. """ def test_primitive_vertices_cube_mpl(self): """ - Tests :func:`colour.geometry.vertices.primitive_vertices_cube_mpl` + Test :func:`colour.geometry.vertices.primitive_vertices_cube_mpl` definition. """ np.testing.assert_almost_equal( primitive_vertices_cube_mpl(), - np.array([ - [ - [0, 0, 0], - [1, 0, 0], - [1, 1, 0], - [0, 1, 0], - ], - [ - [0, 0, 1], - [1, 0, 1], - [1, 1, 1], - [0, 1, 1], - ], - [ - [0, 0, 0], - [1, 0, 0], - [1, 0, 1], - [0, 0, 1], - ], - [ - [0, 1, 0], - [1, 1, 0], - [1, 1, 1], - [0, 1, 1], - ], - [ - [0, 0, 0], - [0, 1, 0], - [0, 1, 1], - [0, 0, 1], - ], - [ - [1, 0, 0], - [1, 1, 0], - [1, 1, 1], - [1, 0, 1], - ], - ]), - decimal=7) + np.array( + [ + [ + [0, 0, 0], + [1, 0, 0], + [1, 1, 0], + [0, 1, 0], + ], + [ + [0, 0, 1], + [1, 0, 1], + [1, 1, 1], + [0, 1, 1], + ], + [ + [0, 0, 0], + [1, 0, 0], + [1, 0, 1], + [0, 0, 1], + ], + [ + [0, 1, 0], + [1, 1, 0], + [1, 1, 1], + [0, 1, 1], + ], + [ + [0, 0, 0], + [0, 1, 0], + [0, 1, 1], + [0, 0, 1], + ], + [ + [1, 0, 0], + [1, 1, 0], + [1, 1, 1], + [1, 0, 1], + ], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - primitive_vertices_cube_mpl(planes=['+x']), + primitive_vertices_cube_mpl(planes=["+x"]), np.array([[[1, 0, 0], [1, 1, 0], [1, 1, 1], [1, 0, 1]]]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - primitive_vertices_cube_mpl(planes=['-x']), + primitive_vertices_cube_mpl(planes=["-x"]), np.array([[[0, 0, 0], [0, 1, 0], [0, 1, 1], [0, 0, 1]]]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - primitive_vertices_cube_mpl(planes=['+y']), + primitive_vertices_cube_mpl(planes=["+y"]), np.array([[[0, 1, 0], [1, 1, 0], [1, 1, 1], [0, 1, 1]]]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - primitive_vertices_cube_mpl(planes=['-y']), + primitive_vertices_cube_mpl(planes=["-y"]), np.array([[[0, 0, 0], [1, 0, 0], [1, 0, 1], [0, 0, 1]]]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - primitive_vertices_cube_mpl(planes=['+z']), + primitive_vertices_cube_mpl(planes=["+z"]), np.array([[[0, 0, 1], [1, 0, 1], [1, 1, 1], [0, 1, 1]]]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - primitive_vertices_cube_mpl(planes=['-z']), + primitive_vertices_cube_mpl(planes=["-z"]), np.array([[[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]]]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( primitive_vertices_cube_mpl( @@ -263,142 +296,146 @@ def test_primitive_vertices_cube_mpl(self): width_segments=1, height_segments=2, depth_segments=3, - origin=np.array([0.2, 0.4, 0.6])), - np.array([ - [ - [0.20000000, 0.60000000, 0.40000000], - [0.40000000, 0.60000000, 0.40000000], - [0.40000000, 0.80000000, 0.40000000], - [0.20000000, 0.80000000, 0.40000000], - ], - [ - [0.20000000, 0.80000000, 0.40000000], - [0.40000000, 0.80000000, 0.40000000], - [0.40000000, 1.00000000, 0.40000000], - [0.20000000, 1.00000000, 0.40000000], - ], - [ - [0.20000000, 1.00000000, 0.40000000], - [0.40000000, 1.00000000, 0.40000000], - [0.40000000, 1.20000000, 0.40000000], - [0.20000000, 1.20000000, 0.40000000], - ], - [ - [0.20000000, 0.60000000, 0.80000000], - [0.40000000, 0.60000000, 0.80000000], - [0.40000000, 0.80000000, 0.80000000], - [0.20000000, 0.80000000, 0.80000000], - ], - [ - [0.20000000, 0.80000000, 0.80000000], - [0.40000000, 0.80000000, 0.80000000], - [0.40000000, 1.00000000, 0.80000000], - [0.20000000, 1.00000000, 0.80000000], - ], - [ - [0.20000000, 1.00000000, 0.80000000], - [0.40000000, 1.00000000, 0.80000000], - [0.40000000, 1.20000000, 0.80000000], - [0.20000000, 1.20000000, 0.80000000], - ], - [ - [0.20000000, 0.60000000, 0.40000000], - [0.40000000, 0.60000000, 0.40000000], - [0.40000000, 0.60000000, 0.60000000], - [0.20000000, 0.60000000, 0.60000000], - ], - [ - [0.20000000, 0.60000000, 0.60000000], - [0.40000000, 0.60000000, 0.60000000], - [0.40000000, 0.60000000, 0.80000000], - [0.20000000, 0.60000000, 0.80000000], - ], - [ - [0.20000000, 1.20000000, 0.40000000], - [0.40000000, 1.20000000, 0.40000000], - [0.40000000, 1.20000000, 0.60000000], - [0.20000000, 1.20000000, 0.60000000], - ], - [ - [0.20000000, 1.20000000, 0.60000000], - [0.40000000, 1.20000000, 0.60000000], - [0.40000000, 1.20000000, 0.80000000], - [0.20000000, 1.20000000, 0.80000000], - ], - [ - [0.20000000, 0.60000000, 0.40000000], - [0.20000000, 0.80000000, 0.40000000], - [0.20000000, 0.80000000, 0.60000000], - [0.20000000, 0.60000000, 0.60000000], - ], - [ - [0.20000000, 0.60000000, 0.60000000], - [0.20000000, 0.80000000, 0.60000000], - [0.20000000, 0.80000000, 0.80000000], - [0.20000000, 0.60000000, 0.80000000], - ], - [ - [0.20000000, 0.80000000, 0.40000000], - [0.20000000, 1.00000000, 0.40000000], - [0.20000000, 1.00000000, 0.60000000], - [0.20000000, 0.80000000, 0.60000000], - ], - [ - [0.20000000, 0.80000000, 0.60000000], - [0.20000000, 1.00000000, 0.60000000], - [0.20000000, 1.00000000, 0.80000000], - [0.20000000, 0.80000000, 0.80000000], - ], - [ - [0.20000000, 1.00000000, 0.40000000], - [0.20000000, 1.20000000, 0.40000000], - [0.20000000, 1.20000000, 0.60000000], - [0.20000000, 1.00000000, 0.60000000], - ], - [ - [0.20000000, 1.00000000, 0.60000000], - [0.20000000, 1.20000000, 0.60000000], - [0.20000000, 1.20000000, 0.80000000], - [0.20000000, 1.00000000, 0.80000000], - ], - [ - [0.40000000, 0.60000000, 0.40000000], - [0.40000000, 0.80000000, 0.40000000], - [0.40000000, 0.80000000, 0.60000000], - [0.40000000, 0.60000000, 0.60000000], - ], - [ - [0.40000000, 0.60000000, 0.60000000], - [0.40000000, 0.80000000, 0.60000000], - [0.40000000, 0.80000000, 0.80000000], - [0.40000000, 0.60000000, 0.80000000], - ], - [ - [0.40000000, 0.80000000, 0.40000000], - [0.40000000, 1.00000000, 0.40000000], - [0.40000000, 1.00000000, 0.60000000], - [0.40000000, 0.80000000, 0.60000000], - ], - [ - [0.40000000, 0.80000000, 0.60000000], - [0.40000000, 1.00000000, 0.60000000], - [0.40000000, 1.00000000, 0.80000000], - [0.40000000, 0.80000000, 0.80000000], - ], - [ - [0.40000000, 1.00000000, 0.40000000], - [0.40000000, 1.20000000, 0.40000000], - [0.40000000, 1.20000000, 0.60000000], - [0.40000000, 1.00000000, 0.60000000], - ], - [ - [0.40000000, 1.00000000, 0.60000000], - [0.40000000, 1.20000000, 0.60000000], - [0.40000000, 1.20000000, 0.80000000], - [0.40000000, 1.00000000, 0.80000000], - ], - ]), - decimal=7) + origin=np.array([0.2, 0.4, 0.6]), + ), + np.array( + [ + [ + [0.20000000, 0.60000000, 0.40000000], + [0.40000000, 0.60000000, 0.40000000], + [0.40000000, 0.80000000, 0.40000000], + [0.20000000, 0.80000000, 0.40000000], + ], + [ + [0.20000000, 0.80000000, 0.40000000], + [0.40000000, 0.80000000, 0.40000000], + [0.40000000, 1.00000000, 0.40000000], + [0.20000000, 1.00000000, 0.40000000], + ], + [ + [0.20000000, 1.00000000, 0.40000000], + [0.40000000, 1.00000000, 0.40000000], + [0.40000000, 1.20000000, 0.40000000], + [0.20000000, 1.20000000, 0.40000000], + ], + [ + [0.20000000, 0.60000000, 0.80000000], + [0.40000000, 0.60000000, 0.80000000], + [0.40000000, 0.80000000, 0.80000000], + [0.20000000, 0.80000000, 0.80000000], + ], + [ + [0.20000000, 0.80000000, 0.80000000], + [0.40000000, 0.80000000, 0.80000000], + [0.40000000, 1.00000000, 0.80000000], + [0.20000000, 1.00000000, 0.80000000], + ], + [ + [0.20000000, 1.00000000, 0.80000000], + [0.40000000, 1.00000000, 0.80000000], + [0.40000000, 1.20000000, 0.80000000], + [0.20000000, 1.20000000, 0.80000000], + ], + [ + [0.20000000, 0.60000000, 0.40000000], + [0.40000000, 0.60000000, 0.40000000], + [0.40000000, 0.60000000, 0.60000000], + [0.20000000, 0.60000000, 0.60000000], + ], + [ + [0.20000000, 0.60000000, 0.60000000], + [0.40000000, 0.60000000, 0.60000000], + [0.40000000, 0.60000000, 0.80000000], + [0.20000000, 0.60000000, 0.80000000], + ], + [ + [0.20000000, 1.20000000, 0.40000000], + [0.40000000, 1.20000000, 0.40000000], + [0.40000000, 1.20000000, 0.60000000], + [0.20000000, 1.20000000, 0.60000000], + ], + [ + [0.20000000, 1.20000000, 0.60000000], + [0.40000000, 1.20000000, 0.60000000], + [0.40000000, 1.20000000, 0.80000000], + [0.20000000, 1.20000000, 0.80000000], + ], + [ + [0.20000000, 0.60000000, 0.40000000], + [0.20000000, 0.80000000, 0.40000000], + [0.20000000, 0.80000000, 0.60000000], + [0.20000000, 0.60000000, 0.60000000], + ], + [ + [0.20000000, 0.60000000, 0.60000000], + [0.20000000, 0.80000000, 0.60000000], + [0.20000000, 0.80000000, 0.80000000], + [0.20000000, 0.60000000, 0.80000000], + ], + [ + [0.20000000, 0.80000000, 0.40000000], + [0.20000000, 1.00000000, 0.40000000], + [0.20000000, 1.00000000, 0.60000000], + [0.20000000, 0.80000000, 0.60000000], + ], + [ + [0.20000000, 0.80000000, 0.60000000], + [0.20000000, 1.00000000, 0.60000000], + [0.20000000, 1.00000000, 0.80000000], + [0.20000000, 0.80000000, 0.80000000], + ], + [ + [0.20000000, 1.00000000, 0.40000000], + [0.20000000, 1.20000000, 0.40000000], + [0.20000000, 1.20000000, 0.60000000], + [0.20000000, 1.00000000, 0.60000000], + ], + [ + [0.20000000, 1.00000000, 0.60000000], + [0.20000000, 1.20000000, 0.60000000], + [0.20000000, 1.20000000, 0.80000000], + [0.20000000, 1.00000000, 0.80000000], + ], + [ + [0.40000000, 0.60000000, 0.40000000], + [0.40000000, 0.80000000, 0.40000000], + [0.40000000, 0.80000000, 0.60000000], + [0.40000000, 0.60000000, 0.60000000], + ], + [ + [0.40000000, 0.60000000, 0.60000000], + [0.40000000, 0.80000000, 0.60000000], + [0.40000000, 0.80000000, 0.80000000], + [0.40000000, 0.60000000, 0.80000000], + ], + [ + [0.40000000, 0.80000000, 0.40000000], + [0.40000000, 1.00000000, 0.40000000], + [0.40000000, 1.00000000, 0.60000000], + [0.40000000, 0.80000000, 0.60000000], + ], + [ + [0.40000000, 0.80000000, 0.60000000], + [0.40000000, 1.00000000, 0.60000000], + [0.40000000, 1.00000000, 0.80000000], + [0.40000000, 0.80000000, 0.80000000], + ], + [ + [0.40000000, 1.00000000, 0.40000000], + [0.40000000, 1.20000000, 0.40000000], + [0.40000000, 1.20000000, 0.60000000], + [0.40000000, 1.00000000, 0.60000000], + ], + [ + [0.40000000, 1.00000000, 0.60000000], + [0.40000000, 1.20000000, 0.60000000], + [0.40000000, 1.20000000, 0.80000000], + [0.40000000, 1.00000000, 0.80000000], + ], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( primitive_vertices_cube_mpl( @@ -408,536 +445,560 @@ def test_primitive_vertices_cube_mpl(self): width_segments=1, height_segments=2, depth_segments=3, - origin=np.array([-0.2, -0.4, -0.6])), - np.array([ - [ - [-0.20000000, -0.60000000, -0.40000000], - [-0.40000000, -0.60000000, -0.40000000], - [-0.40000000, -0.80000000, -0.40000000], - [-0.20000000, -0.80000000, -0.40000000], - ], - [ - [-0.20000000, -0.80000000, -0.40000000], - [-0.40000000, -0.80000000, -0.40000000], - [-0.40000000, -1.00000000, -0.40000000], - [-0.20000000, -1.00000000, -0.40000000], - ], - [ - [-0.20000000, -1.00000000, -0.40000000], - [-0.40000000, -1.00000000, -0.40000000], - [-0.40000000, -1.20000000, -0.40000000], - [-0.20000000, -1.20000000, -0.40000000], - ], - [ - [-0.20000000, -0.60000000, -0.80000000], - [-0.40000000, -0.60000000, -0.80000000], - [-0.40000000, -0.80000000, -0.80000000], - [-0.20000000, -0.80000000, -0.80000000], - ], - [ - [-0.20000000, -0.80000000, -0.80000000], - [-0.40000000, -0.80000000, -0.80000000], - [-0.40000000, -1.00000000, -0.80000000], - [-0.20000000, -1.00000000, -0.80000000], - ], - [ - [-0.20000000, -1.00000000, -0.80000000], - [-0.40000000, -1.00000000, -0.80000000], - [-0.40000000, -1.20000000, -0.80000000], - [-0.20000000, -1.20000000, -0.80000000], - ], - [ - [-0.20000000, -0.60000000, -0.40000000], - [-0.40000000, -0.60000000, -0.40000000], - [-0.40000000, -0.60000000, -0.60000000], - [-0.20000000, -0.60000000, -0.60000000], - ], - [ - [-0.20000000, -0.60000000, -0.60000000], - [-0.40000000, -0.60000000, -0.60000000], - [-0.40000000, -0.60000000, -0.80000000], - [-0.20000000, -0.60000000, -0.80000000], - ], - [ - [-0.20000000, -1.20000000, -0.40000000], - [-0.40000000, -1.20000000, -0.40000000], - [-0.40000000, -1.20000000, -0.60000000], - [-0.20000000, -1.20000000, -0.60000000], - ], - [ - [-0.20000000, -1.20000000, -0.60000000], - [-0.40000000, -1.20000000, -0.60000000], - [-0.40000000, -1.20000000, -0.80000000], - [-0.20000000, -1.20000000, -0.80000000], - ], - [ - [-0.20000000, -0.60000000, -0.40000000], - [-0.20000000, -0.80000000, -0.40000000], - [-0.20000000, -0.80000000, -0.60000000], - [-0.20000000, -0.60000000, -0.60000000], - ], - [ - [-0.20000000, -0.60000000, -0.60000000], - [-0.20000000, -0.80000000, -0.60000000], - [-0.20000000, -0.80000000, -0.80000000], - [-0.20000000, -0.60000000, -0.80000000], - ], - [ - [-0.20000000, -0.80000000, -0.40000000], - [-0.20000000, -1.00000000, -0.40000000], - [-0.20000000, -1.00000000, -0.60000000], - [-0.20000000, -0.80000000, -0.60000000], - ], - [ - [-0.20000000, -0.80000000, -0.60000000], - [-0.20000000, -1.00000000, -0.60000000], - [-0.20000000, -1.00000000, -0.80000000], - [-0.20000000, -0.80000000, -0.80000000], - ], - [ - [-0.20000000, -1.00000000, -0.40000000], - [-0.20000000, -1.20000000, -0.40000000], - [-0.20000000, -1.20000000, -0.60000000], - [-0.20000000, -1.00000000, -0.60000000], - ], - [ - [-0.20000000, -1.00000000, -0.60000000], - [-0.20000000, -1.20000000, -0.60000000], - [-0.20000000, -1.20000000, -0.80000000], - [-0.20000000, -1.00000000, -0.80000000], - ], - [ - [-0.40000000, -0.60000000, -0.40000000], - [-0.40000000, -0.80000000, -0.40000000], - [-0.40000000, -0.80000000, -0.60000000], - [-0.40000000, -0.60000000, -0.60000000], - ], - [ - [-0.40000000, -0.60000000, -0.60000000], - [-0.40000000, -0.80000000, -0.60000000], - [-0.40000000, -0.80000000, -0.80000000], - [-0.40000000, -0.60000000, -0.80000000], - ], - [ - [-0.40000000, -0.80000000, -0.40000000], - [-0.40000000, -1.00000000, -0.40000000], - [-0.40000000, -1.00000000, -0.60000000], - [-0.40000000, -0.80000000, -0.60000000], - ], - [ - [-0.40000000, -0.80000000, -0.60000000], - [-0.40000000, -1.00000000, -0.60000000], - [-0.40000000, -1.00000000, -0.80000000], - [-0.40000000, -0.80000000, -0.80000000], - ], - [ - [-0.40000000, -1.00000000, -0.40000000], - [-0.40000000, -1.20000000, -0.40000000], - [-0.40000000, -1.20000000, -0.60000000], - [-0.40000000, -1.00000000, -0.60000000], - ], - [ - [-0.40000000, -1.00000000, -0.60000000], - [-0.40000000, -1.20000000, -0.60000000], - [-0.40000000, -1.20000000, -0.80000000], - [-0.40000000, -1.00000000, -0.80000000], - ], - ]), - decimal=7) - - for plane in PLANE_TO_AXIS_MAPPING.keys(): + origin=np.array([-0.2, -0.4, -0.6]), + ), + np.array( + [ + [ + [-0.20000000, -0.60000000, -0.40000000], + [-0.40000000, -0.60000000, -0.40000000], + [-0.40000000, -0.80000000, -0.40000000], + [-0.20000000, -0.80000000, -0.40000000], + ], + [ + [-0.20000000, -0.80000000, -0.40000000], + [-0.40000000, -0.80000000, -0.40000000], + [-0.40000000, -1.00000000, -0.40000000], + [-0.20000000, -1.00000000, -0.40000000], + ], + [ + [-0.20000000, -1.00000000, -0.40000000], + [-0.40000000, -1.00000000, -0.40000000], + [-0.40000000, -1.20000000, -0.40000000], + [-0.20000000, -1.20000000, -0.40000000], + ], + [ + [-0.20000000, -0.60000000, -0.80000000], + [-0.40000000, -0.60000000, -0.80000000], + [-0.40000000, -0.80000000, -0.80000000], + [-0.20000000, -0.80000000, -0.80000000], + ], + [ + [-0.20000000, -0.80000000, -0.80000000], + [-0.40000000, -0.80000000, -0.80000000], + [-0.40000000, -1.00000000, -0.80000000], + [-0.20000000, -1.00000000, -0.80000000], + ], + [ + [-0.20000000, -1.00000000, -0.80000000], + [-0.40000000, -1.00000000, -0.80000000], + [-0.40000000, -1.20000000, -0.80000000], + [-0.20000000, -1.20000000, -0.80000000], + ], + [ + [-0.20000000, -0.60000000, -0.40000000], + [-0.40000000, -0.60000000, -0.40000000], + [-0.40000000, -0.60000000, -0.60000000], + [-0.20000000, -0.60000000, -0.60000000], + ], + [ + [-0.20000000, -0.60000000, -0.60000000], + [-0.40000000, -0.60000000, -0.60000000], + [-0.40000000, -0.60000000, -0.80000000], + [-0.20000000, -0.60000000, -0.80000000], + ], + [ + [-0.20000000, -1.20000000, -0.40000000], + [-0.40000000, -1.20000000, -0.40000000], + [-0.40000000, -1.20000000, -0.60000000], + [-0.20000000, -1.20000000, -0.60000000], + ], + [ + [-0.20000000, -1.20000000, -0.60000000], + [-0.40000000, -1.20000000, -0.60000000], + [-0.40000000, -1.20000000, -0.80000000], + [-0.20000000, -1.20000000, -0.80000000], + ], + [ + [-0.20000000, -0.60000000, -0.40000000], + [-0.20000000, -0.80000000, -0.40000000], + [-0.20000000, -0.80000000, -0.60000000], + [-0.20000000, -0.60000000, -0.60000000], + ], + [ + [-0.20000000, -0.60000000, -0.60000000], + [-0.20000000, -0.80000000, -0.60000000], + [-0.20000000, -0.80000000, -0.80000000], + [-0.20000000, -0.60000000, -0.80000000], + ], + [ + [-0.20000000, -0.80000000, -0.40000000], + [-0.20000000, -1.00000000, -0.40000000], + [-0.20000000, -1.00000000, -0.60000000], + [-0.20000000, -0.80000000, -0.60000000], + ], + [ + [-0.20000000, -0.80000000, -0.60000000], + [-0.20000000, -1.00000000, -0.60000000], + [-0.20000000, -1.00000000, -0.80000000], + [-0.20000000, -0.80000000, -0.80000000], + ], + [ + [-0.20000000, -1.00000000, -0.40000000], + [-0.20000000, -1.20000000, -0.40000000], + [-0.20000000, -1.20000000, -0.60000000], + [-0.20000000, -1.00000000, -0.60000000], + ], + [ + [-0.20000000, -1.00000000, -0.60000000], + [-0.20000000, -1.20000000, -0.60000000], + [-0.20000000, -1.20000000, -0.80000000], + [-0.20000000, -1.00000000, -0.80000000], + ], + [ + [-0.40000000, -0.60000000, -0.40000000], + [-0.40000000, -0.80000000, -0.40000000], + [-0.40000000, -0.80000000, -0.60000000], + [-0.40000000, -0.60000000, -0.60000000], + ], + [ + [-0.40000000, -0.60000000, -0.60000000], + [-0.40000000, -0.80000000, -0.60000000], + [-0.40000000, -0.80000000, -0.80000000], + [-0.40000000, -0.60000000, -0.80000000], + ], + [ + [-0.40000000, -0.80000000, -0.40000000], + [-0.40000000, -1.00000000, -0.40000000], + [-0.40000000, -1.00000000, -0.60000000], + [-0.40000000, -0.80000000, -0.60000000], + ], + [ + [-0.40000000, -0.80000000, -0.60000000], + [-0.40000000, -1.00000000, -0.60000000], + [-0.40000000, -1.00000000, -0.80000000], + [-0.40000000, -0.80000000, -0.80000000], + ], + [ + [-0.40000000, -1.00000000, -0.40000000], + [-0.40000000, -1.20000000, -0.40000000], + [-0.40000000, -1.20000000, -0.60000000], + [-0.40000000, -1.00000000, -0.60000000], + ], + [ + [-0.40000000, -1.00000000, -0.60000000], + [-0.40000000, -1.20000000, -0.60000000], + [-0.40000000, -1.20000000, -0.80000000], + [-0.40000000, -1.00000000, -0.80000000], + ], + ] + ), + decimal=7, + ) + + for plane in MAPPING_PLANE_TO_AXIS.keys(): np.testing.assert_almost_equal( primitive_vertices_cube_mpl(planes=[plane]), primitive_vertices_cube_mpl( - planes=[PLANE_TO_AXIS_MAPPING[plane]]), - decimal=7) + planes=[MAPPING_PLANE_TO_AXIS[plane]] + ), + decimal=7, + ) class TestPrimitiveVerticesSphere(unittest.TestCase): """ - Defines :func:`colour.geometry.vertices.primitive_vertices_sphere` + Define :func:`colour.geometry.vertices.primitive_vertices_sphere` definition unit tests methods. """ def test_primitive_vertices_sphere(self): """ - Tests :func:`colour.geometry.vertices.primitive_vertices_sphere` + Test :func:`colour.geometry.vertices.primitive_vertices_sphere` definition. """ np.testing.assert_almost_equal( primitive_vertices_sphere(), - np.array([ - [ - [0.00000000, 0.00000000, 0.50000000], - [-0.19134172, -0.00000000, 0.46193977], - [-0.35355339, -0.00000000, 0.35355339], - [-0.46193977, -0.00000000, 0.19134172], - [-0.50000000, -0.00000000, 0.00000000], - [-0.46193977, -0.00000000, -0.19134172], - [-0.35355339, -0.00000000, -0.35355339], - [-0.19134172, -0.00000000, -0.46193977], - [-0.00000000, -0.00000000, -0.50000000], - ], - [ - [0.00000000, 0.00000000, 0.50000000], - [-0.13529903, -0.13529903, 0.46193977], - [-0.25000000, -0.25000000, 0.35355339], - [-0.32664074, -0.32664074, 0.19134172], - [-0.35355339, -0.35355339, 0.00000000], - [-0.32664074, -0.32664074, -0.19134172], - [-0.25000000, -0.25000000, -0.35355339], - [-0.13529903, -0.13529903, -0.46193977], - [-0.00000000, -0.00000000, -0.50000000], - ], - [ - [0.00000000, 0.00000000, 0.50000000], - [0.00000000, -0.19134172, 0.46193977], - [0.00000000, -0.35355339, 0.35355339], - [0.00000000, -0.46193977, 0.19134172], - [0.00000000, -0.50000000, 0.00000000], - [0.00000000, -0.46193977, -0.19134172], - [0.00000000, -0.35355339, -0.35355339], - [0.00000000, -0.19134172, -0.46193977], - [0.00000000, -0.00000000, -0.50000000], - ], - [ - [0.00000000, 0.00000000, 0.50000000], - [0.13529903, -0.13529903, 0.46193977], - [0.25000000, -0.25000000, 0.35355339], - [0.32664074, -0.32664074, 0.19134172], - [0.35355339, -0.35355339, 0.00000000], - [0.32664074, -0.32664074, -0.19134172], - [0.25000000, -0.25000000, -0.35355339], - [0.13529903, -0.13529903, -0.46193977], - [0.00000000, -0.00000000, -0.50000000], - ], - [ - [0.00000000, 0.00000000, 0.50000000], - [0.19134172, 0.00000000, 0.46193977], - [0.35355339, 0.00000000, 0.35355339], - [0.46193977, 0.00000000, 0.19134172], - [0.50000000, 0.00000000, 0.00000000], - [0.46193977, 0.00000000, -0.19134172], - [0.35355339, 0.00000000, -0.35355339], - [0.19134172, 0.00000000, -0.46193977], - [0.00000000, 0.00000000, -0.50000000], - ], - [ - [0.00000000, 0.00000000, 0.50000000], - [0.13529903, 0.13529903, 0.46193977], - [0.25000000, 0.25000000, 0.35355339], - [0.32664074, 0.32664074, 0.19134172], - [0.35355339, 0.35355339, 0.00000000], - [0.32664074, 0.32664074, -0.19134172], - [0.25000000, 0.25000000, -0.35355339], - [0.13529903, 0.13529903, -0.46193977], - [0.00000000, 0.00000000, -0.50000000], - ], - [ - [0.00000000, 0.00000000, 0.50000000], - [0.00000000, 0.19134172, 0.46193977], - [0.00000000, 0.35355339, 0.35355339], - [0.00000000, 0.46193977, 0.19134172], - [0.00000000, 0.50000000, 0.00000000], - [0.00000000, 0.46193977, -0.19134172], - [0.00000000, 0.35355339, -0.35355339], - [0.00000000, 0.19134172, -0.46193977], - [0.00000000, 0.00000000, -0.50000000], - ], - [ - [0.00000000, 0.00000000, 0.50000000], - [-0.13529903, 0.13529903, 0.46193977], - [-0.25000000, 0.25000000, 0.35355339], - [-0.32664074, 0.32664074, 0.19134172], - [-0.35355339, 0.35355339, 0.00000000], - [-0.32664074, 0.32664074, -0.19134172], - [-0.25000000, 0.25000000, -0.35355339], - [-0.13529903, 0.13529903, -0.46193977], - [-0.00000000, 0.00000000, -0.50000000], - ], - ]), - decimal=7) + np.array( + [ + [ + [0.00000000, 0.00000000, 0.50000000], + [-0.19134172, -0.00000000, 0.46193977], + [-0.35355339, -0.00000000, 0.35355339], + [-0.46193977, -0.00000000, 0.19134172], + [-0.50000000, -0.00000000, 0.00000000], + [-0.46193977, -0.00000000, -0.19134172], + [-0.35355339, -0.00000000, -0.35355339], + [-0.19134172, -0.00000000, -0.46193977], + [-0.00000000, -0.00000000, -0.50000000], + ], + [ + [0.00000000, 0.00000000, 0.50000000], + [-0.13529903, -0.13529903, 0.46193977], + [-0.25000000, -0.25000000, 0.35355339], + [-0.32664074, -0.32664074, 0.19134172], + [-0.35355339, -0.35355339, 0.00000000], + [-0.32664074, -0.32664074, -0.19134172], + [-0.25000000, -0.25000000, -0.35355339], + [-0.13529903, -0.13529903, -0.46193977], + [-0.00000000, -0.00000000, -0.50000000], + ], + [ + [0.00000000, 0.00000000, 0.50000000], + [0.00000000, -0.19134172, 0.46193977], + [0.00000000, -0.35355339, 0.35355339], + [0.00000000, -0.46193977, 0.19134172], + [0.00000000, -0.50000000, 0.00000000], + [0.00000000, -0.46193977, -0.19134172], + [0.00000000, -0.35355339, -0.35355339], + [0.00000000, -0.19134172, -0.46193977], + [0.00000000, -0.00000000, -0.50000000], + ], + [ + [0.00000000, 0.00000000, 0.50000000], + [0.13529903, -0.13529903, 0.46193977], + [0.25000000, -0.25000000, 0.35355339], + [0.32664074, -0.32664074, 0.19134172], + [0.35355339, -0.35355339, 0.00000000], + [0.32664074, -0.32664074, -0.19134172], + [0.25000000, -0.25000000, -0.35355339], + [0.13529903, -0.13529903, -0.46193977], + [0.00000000, -0.00000000, -0.50000000], + ], + [ + [0.00000000, 0.00000000, 0.50000000], + [0.19134172, 0.00000000, 0.46193977], + [0.35355339, 0.00000000, 0.35355339], + [0.46193977, 0.00000000, 0.19134172], + [0.50000000, 0.00000000, 0.00000000], + [0.46193977, 0.00000000, -0.19134172], + [0.35355339, 0.00000000, -0.35355339], + [0.19134172, 0.00000000, -0.46193977], + [0.00000000, 0.00000000, -0.50000000], + ], + [ + [0.00000000, 0.00000000, 0.50000000], + [0.13529903, 0.13529903, 0.46193977], + [0.25000000, 0.25000000, 0.35355339], + [0.32664074, 0.32664074, 0.19134172], + [0.35355339, 0.35355339, 0.00000000], + [0.32664074, 0.32664074, -0.19134172], + [0.25000000, 0.25000000, -0.35355339], + [0.13529903, 0.13529903, -0.46193977], + [0.00000000, 0.00000000, -0.50000000], + ], + [ + [0.00000000, 0.00000000, 0.50000000], + [0.00000000, 0.19134172, 0.46193977], + [0.00000000, 0.35355339, 0.35355339], + [0.00000000, 0.46193977, 0.19134172], + [0.00000000, 0.50000000, 0.00000000], + [0.00000000, 0.46193977, -0.19134172], + [0.00000000, 0.35355339, -0.35355339], + [0.00000000, 0.19134172, -0.46193977], + [0.00000000, 0.00000000, -0.50000000], + ], + [ + [0.00000000, 0.00000000, 0.50000000], + [-0.13529903, 0.13529903, 0.46193977], + [-0.25000000, 0.25000000, 0.35355339], + [-0.32664074, 0.32664074, 0.19134172], + [-0.35355339, 0.35355339, 0.00000000], + [-0.32664074, 0.32664074, -0.19134172], + [-0.25000000, 0.25000000, -0.35355339], + [-0.13529903, 0.13529903, -0.46193977], + [-0.00000000, 0.00000000, -0.50000000], + ], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( primitive_vertices_sphere(intermediate=True), - np.array([ - [ - [0.00000000, 0.00000000, 0.50000000], - [-0.25663998, -0.10630376, 0.41573481], - [-0.38408888, -0.15909482, 0.27778512], - [-0.45306372, -0.18766514, 0.09754516], - [-0.45306372, -0.18766514, -0.09754516], - [-0.38408888, -0.15909482, -0.27778512], - [-0.25663998, -0.10630376, -0.41573481], - [-0.00000000, -0.00000000, -0.50000000], - ], - [ - [0.00000000, 0.00000000, 0.50000000], - [-0.10630376, -0.25663998, 0.41573481], - [-0.15909482, -0.38408888, 0.27778512], - [-0.18766514, -0.45306372, 0.09754516], - [-0.18766514, -0.45306372, -0.09754516], - [-0.15909482, -0.38408888, -0.27778512], - [-0.10630376, -0.25663998, -0.41573481], - [-0.00000000, -0.00000000, -0.50000000], - ], - [ - [0.00000000, 0.00000000, 0.50000000], - [0.10630376, -0.25663998, 0.41573481], - [0.15909482, -0.38408888, 0.27778512], - [0.18766514, -0.45306372, 0.09754516], - [0.18766514, -0.45306372, -0.09754516], - [0.15909482, -0.38408888, -0.27778512], - [0.10630376, -0.25663998, -0.41573481], - [0.00000000, -0.00000000, -0.50000000], - ], - [ - [0.00000000, 0.00000000, 0.50000000], - [0.25663998, -0.10630376, 0.41573481], - [0.38408888, -0.15909482, 0.27778512], - [0.45306372, -0.18766514, 0.09754516], - [0.45306372, -0.18766514, -0.09754516], - [0.38408888, -0.15909482, -0.27778512], - [0.25663998, -0.10630376, -0.41573481], - [0.00000000, -0.00000000, -0.50000000], - ], - [ - [0.00000000, 0.00000000, 0.50000000], - [0.25663998, 0.10630376, 0.41573481], - [0.38408888, 0.15909482, 0.27778512], - [0.45306372, 0.18766514, 0.09754516], - [0.45306372, 0.18766514, -0.09754516], - [0.38408888, 0.15909482, -0.27778512], - [0.25663998, 0.10630376, -0.41573481], - [0.00000000, 0.00000000, -0.50000000], - ], - [ - [0.00000000, 0.00000000, 0.50000000], - [0.10630376, 0.25663998, 0.41573481], - [0.15909482, 0.38408888, 0.27778512], - [0.18766514, 0.45306372, 0.09754516], - [0.18766514, 0.45306372, -0.09754516], - [0.15909482, 0.38408888, -0.27778512], - [0.10630376, 0.25663998, -0.41573481], - [0.00000000, 0.00000000, -0.50000000], - ], - [ - [0.00000000, 0.00000000, 0.50000000], - [-0.10630376, 0.25663998, 0.41573481], - [-0.15909482, 0.38408888, 0.27778512], - [-0.18766514, 0.45306372, 0.09754516], - [-0.18766514, 0.45306372, -0.09754516], - [-0.15909482, 0.38408888, -0.27778512], - [-0.10630376, 0.25663998, -0.41573481], - [-0.00000000, 0.00000000, -0.50000000], - ], - [ - [0.00000000, 0.00000000, 0.50000000], - [-0.25663998, 0.10630376, 0.41573481], - [-0.38408888, 0.15909482, 0.27778512], - [-0.45306372, 0.18766514, 0.09754516], - [-0.45306372, 0.18766514, -0.09754516], - [-0.38408888, 0.15909482, -0.27778512], - [-0.25663998, 0.10630376, -0.41573481], - [-0.00000000, 0.00000000, -0.50000000], - ], - ]), - decimal=7) + np.array( + [ + [ + [0.00000000, 0.00000000, 0.50000000], + [-0.25663998, -0.10630376, 0.41573481], + [-0.38408888, -0.15909482, 0.27778512], + [-0.45306372, -0.18766514, 0.09754516], + [-0.45306372, -0.18766514, -0.09754516], + [-0.38408888, -0.15909482, -0.27778512], + [-0.25663998, -0.10630376, -0.41573481], + [-0.00000000, -0.00000000, -0.50000000], + ], + [ + [0.00000000, 0.00000000, 0.50000000], + [-0.10630376, -0.25663998, 0.41573481], + [-0.15909482, -0.38408888, 0.27778512], + [-0.18766514, -0.45306372, 0.09754516], + [-0.18766514, -0.45306372, -0.09754516], + [-0.15909482, -0.38408888, -0.27778512], + [-0.10630376, -0.25663998, -0.41573481], + [-0.00000000, -0.00000000, -0.50000000], + ], + [ + [0.00000000, 0.00000000, 0.50000000], + [0.10630376, -0.25663998, 0.41573481], + [0.15909482, -0.38408888, 0.27778512], + [0.18766514, -0.45306372, 0.09754516], + [0.18766514, -0.45306372, -0.09754516], + [0.15909482, -0.38408888, -0.27778512], + [0.10630376, -0.25663998, -0.41573481], + [0.00000000, -0.00000000, -0.50000000], + ], + [ + [0.00000000, 0.00000000, 0.50000000], + [0.25663998, -0.10630376, 0.41573481], + [0.38408888, -0.15909482, 0.27778512], + [0.45306372, -0.18766514, 0.09754516], + [0.45306372, -0.18766514, -0.09754516], + [0.38408888, -0.15909482, -0.27778512], + [0.25663998, -0.10630376, -0.41573481], + [0.00000000, -0.00000000, -0.50000000], + ], + [ + [0.00000000, 0.00000000, 0.50000000], + [0.25663998, 0.10630376, 0.41573481], + [0.38408888, 0.15909482, 0.27778512], + [0.45306372, 0.18766514, 0.09754516], + [0.45306372, 0.18766514, -0.09754516], + [0.38408888, 0.15909482, -0.27778512], + [0.25663998, 0.10630376, -0.41573481], + [0.00000000, 0.00000000, -0.50000000], + ], + [ + [0.00000000, 0.00000000, 0.50000000], + [0.10630376, 0.25663998, 0.41573481], + [0.15909482, 0.38408888, 0.27778512], + [0.18766514, 0.45306372, 0.09754516], + [0.18766514, 0.45306372, -0.09754516], + [0.15909482, 0.38408888, -0.27778512], + [0.10630376, 0.25663998, -0.41573481], + [0.00000000, 0.00000000, -0.50000000], + ], + [ + [0.00000000, 0.00000000, 0.50000000], + [-0.10630376, 0.25663998, 0.41573481], + [-0.15909482, 0.38408888, 0.27778512], + [-0.18766514, 0.45306372, 0.09754516], + [-0.18766514, 0.45306372, -0.09754516], + [-0.15909482, 0.38408888, -0.27778512], + [-0.10630376, 0.25663998, -0.41573481], + [-0.00000000, 0.00000000, -0.50000000], + ], + [ + [0.00000000, 0.00000000, 0.50000000], + [-0.25663998, 0.10630376, 0.41573481], + [-0.38408888, 0.15909482, 0.27778512], + [-0.45306372, 0.18766514, 0.09754516], + [-0.45306372, 0.18766514, -0.09754516], + [-0.38408888, 0.15909482, -0.27778512], + [-0.25663998, 0.10630376, -0.41573481], + [-0.00000000, 0.00000000, -0.50000000], + ], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - primitive_vertices_sphere(segments=6, axis='+y'), - np.array([ - [ - [0.00000000, 0.50000000, 0.00000000], - [-0.00000000, 0.43301270, -0.25000000], - [-0.00000000, 0.25000000, -0.43301270], - [-0.00000000, 0.00000000, -0.50000000], - [-0.00000000, -0.25000000, -0.43301270], - [-0.00000000, -0.43301270, -0.25000000], - [-0.00000000, -0.50000000, -0.00000000], - ], - [ - [0.00000000, 0.50000000, 0.00000000], - [-0.21650635, 0.43301270, -0.12500000], - [-0.37500000, 0.25000000, -0.21650635], - [-0.43301270, 0.00000000, -0.25000000], - [-0.37500000, -0.25000000, -0.21650635], - [-0.21650635, -0.43301270, -0.12500000], - [-0.00000000, -0.50000000, -0.00000000], - ], - [ - [0.00000000, 0.50000000, 0.00000000], - [-0.21650635, 0.43301270, 0.12500000], - [-0.37500000, 0.25000000, 0.21650635], - [-0.43301270, 0.00000000, 0.25000000], - [-0.37500000, -0.25000000, 0.21650635], - [-0.21650635, -0.43301270, 0.12500000], - [-0.00000000, -0.50000000, 0.00000000], - ], - [ - [0.00000000, 0.50000000, 0.00000000], - [0.00000000, 0.43301270, 0.25000000], - [0.00000000, 0.25000000, 0.43301270], - [0.00000000, 0.00000000, 0.50000000], - [0.00000000, -0.25000000, 0.43301270], - [0.00000000, -0.43301270, 0.25000000], - [0.00000000, -0.50000000, 0.00000000], - ], - [ - [0.00000000, 0.50000000, 0.00000000], - [0.21650635, 0.43301270, 0.12500000], - [0.37500000, 0.25000000, 0.21650635], - [0.43301270, 0.00000000, 0.25000000], - [0.37500000, -0.25000000, 0.21650635], - [0.21650635, -0.43301270, 0.12500000], - [0.00000000, -0.50000000, 0.00000000], - ], - [ - [0.00000000, 0.50000000, 0.00000000], - [0.21650635, 0.43301270, -0.12500000], - [0.37500000, 0.25000000, -0.21650635], - [0.43301270, 0.00000000, -0.25000000], - [0.37500000, -0.25000000, -0.21650635], - [0.21650635, -0.43301270, -0.12500000], - [0.00000000, -0.50000000, -0.00000000], - ], - ]), - decimal=7) + primitive_vertices_sphere(segments=6, axis="+y"), + np.array( + [ + [ + [0.00000000, 0.50000000, 0.00000000], + [-0.00000000, 0.43301270, -0.25000000], + [-0.00000000, 0.25000000, -0.43301270], + [-0.00000000, 0.00000000, -0.50000000], + [-0.00000000, -0.25000000, -0.43301270], + [-0.00000000, -0.43301270, -0.25000000], + [-0.00000000, -0.50000000, -0.00000000], + ], + [ + [0.00000000, 0.50000000, 0.00000000], + [-0.21650635, 0.43301270, -0.12500000], + [-0.37500000, 0.25000000, -0.21650635], + [-0.43301270, 0.00000000, -0.25000000], + [-0.37500000, -0.25000000, -0.21650635], + [-0.21650635, -0.43301270, -0.12500000], + [-0.00000000, -0.50000000, -0.00000000], + ], + [ + [0.00000000, 0.50000000, 0.00000000], + [-0.21650635, 0.43301270, 0.12500000], + [-0.37500000, 0.25000000, 0.21650635], + [-0.43301270, 0.00000000, 0.25000000], + [-0.37500000, -0.25000000, 0.21650635], + [-0.21650635, -0.43301270, 0.12500000], + [-0.00000000, -0.50000000, 0.00000000], + ], + [ + [0.00000000, 0.50000000, 0.00000000], + [0.00000000, 0.43301270, 0.25000000], + [0.00000000, 0.25000000, 0.43301270], + [0.00000000, 0.00000000, 0.50000000], + [0.00000000, -0.25000000, 0.43301270], + [0.00000000, -0.43301270, 0.25000000], + [0.00000000, -0.50000000, 0.00000000], + ], + [ + [0.00000000, 0.50000000, 0.00000000], + [0.21650635, 0.43301270, 0.12500000], + [0.37500000, 0.25000000, 0.21650635], + [0.43301270, 0.00000000, 0.25000000], + [0.37500000, -0.25000000, 0.21650635], + [0.21650635, -0.43301270, 0.12500000], + [0.00000000, -0.50000000, 0.00000000], + ], + [ + [0.00000000, 0.50000000, 0.00000000], + [0.21650635, 0.43301270, -0.12500000], + [0.37500000, 0.25000000, -0.21650635], + [0.43301270, 0.00000000, -0.25000000], + [0.37500000, -0.25000000, -0.21650635], + [0.21650635, -0.43301270, -0.12500000], + [0.00000000, -0.50000000, -0.00000000], + ], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - primitive_vertices_sphere(segments=6, axis='+x'), - np.array([ - [ - [0.50000000, 0.00000000, 0.00000000], - [0.43301270, -0.25000000, -0.00000000], - [0.25000000, -0.43301270, -0.00000000], - [0.00000000, -0.50000000, -0.00000000], - [-0.25000000, -0.43301270, -0.00000000], - [-0.43301270, -0.25000000, -0.00000000], - [-0.50000000, -0.00000000, -0.00000000], - ], - [ - [0.50000000, 0.00000000, 0.00000000], - [0.43301270, -0.12500000, -0.21650635], - [0.25000000, -0.21650635, -0.37500000], - [0.00000000, -0.25000000, -0.43301270], - [-0.25000000, -0.21650635, -0.37500000], - [-0.43301270, -0.12500000, -0.21650635], - [-0.50000000, -0.00000000, -0.00000000], - ], - [ - [0.50000000, 0.00000000, 0.00000000], - [0.43301270, 0.12500000, -0.21650635], - [0.25000000, 0.21650635, -0.37500000], - [0.00000000, 0.25000000, -0.43301270], - [-0.25000000, 0.21650635, -0.37500000], - [-0.43301270, 0.12500000, -0.21650635], - [-0.50000000, 0.00000000, -0.00000000], - ], - [ - [0.50000000, 0.00000000, 0.00000000], - [0.43301270, 0.25000000, 0.00000000], - [0.25000000, 0.43301270, 0.00000000], - [0.00000000, 0.50000000, 0.00000000], - [-0.25000000, 0.43301270, 0.00000000], - [-0.43301270, 0.25000000, 0.00000000], - [-0.50000000, 0.00000000, 0.00000000], - ], - [ - [0.50000000, 0.00000000, 0.00000000], - [0.43301270, 0.12500000, 0.21650635], - [0.25000000, 0.21650635, 0.37500000], - [0.00000000, 0.25000000, 0.43301270], - [-0.25000000, 0.21650635, 0.37500000], - [-0.43301270, 0.12500000, 0.21650635], - [-0.50000000, 0.00000000, 0.00000000], - ], - [ - [0.50000000, 0.00000000, 0.00000000], - [0.43301270, -0.12500000, 0.21650635], - [0.25000000, -0.21650635, 0.37500000], - [0.00000000, -0.25000000, 0.43301270], - [-0.25000000, -0.21650635, 0.37500000], - [-0.43301270, -0.12500000, 0.21650635], - [-0.50000000, -0.00000000, 0.00000000], - ], - ]), - decimal=7) + primitive_vertices_sphere(segments=6, axis="+x"), + np.array( + [ + [ + [0.50000000, 0.00000000, 0.00000000], + [0.43301270, -0.25000000, -0.00000000], + [0.25000000, -0.43301270, -0.00000000], + [0.00000000, -0.50000000, -0.00000000], + [-0.25000000, -0.43301270, -0.00000000], + [-0.43301270, -0.25000000, -0.00000000], + [-0.50000000, -0.00000000, -0.00000000], + ], + [ + [0.50000000, 0.00000000, 0.00000000], + [0.43301270, -0.12500000, -0.21650635], + [0.25000000, -0.21650635, -0.37500000], + [0.00000000, -0.25000000, -0.43301270], + [-0.25000000, -0.21650635, -0.37500000], + [-0.43301270, -0.12500000, -0.21650635], + [-0.50000000, -0.00000000, -0.00000000], + ], + [ + [0.50000000, 0.00000000, 0.00000000], + [0.43301270, 0.12500000, -0.21650635], + [0.25000000, 0.21650635, -0.37500000], + [0.00000000, 0.25000000, -0.43301270], + [-0.25000000, 0.21650635, -0.37500000], + [-0.43301270, 0.12500000, -0.21650635], + [-0.50000000, 0.00000000, -0.00000000], + ], + [ + [0.50000000, 0.00000000, 0.00000000], + [0.43301270, 0.25000000, 0.00000000], + [0.25000000, 0.43301270, 0.00000000], + [0.00000000, 0.50000000, 0.00000000], + [-0.25000000, 0.43301270, 0.00000000], + [-0.43301270, 0.25000000, 0.00000000], + [-0.50000000, 0.00000000, 0.00000000], + ], + [ + [0.50000000, 0.00000000, 0.00000000], + [0.43301270, 0.12500000, 0.21650635], + [0.25000000, 0.21650635, 0.37500000], + [0.00000000, 0.25000000, 0.43301270], + [-0.25000000, 0.21650635, 0.37500000], + [-0.43301270, 0.12500000, 0.21650635], + [-0.50000000, 0.00000000, 0.00000000], + ], + [ + [0.50000000, 0.00000000, 0.00000000], + [0.43301270, -0.12500000, 0.21650635], + [0.25000000, -0.21650635, 0.37500000], + [0.00000000, -0.25000000, 0.43301270], + [-0.25000000, -0.21650635, 0.37500000], + [-0.43301270, -0.12500000, 0.21650635], + [-0.50000000, -0.00000000, 0.00000000], + ], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( primitive_vertices_sphere( radius=100, segments=6, origin=np.array([-0.2, -0.4, -0.6]), - axis='+x'), - np.array([ - [ - [99.80000000, -0.40000000, -0.60000000], - [86.40254038, -50.40000000, -0.60000000], - [49.80000000, -87.00254038, -0.60000000], - [-0.20000000, -100.40000000, -0.60000000], - [-50.20000000, -87.00254038, -0.60000000], - [-86.80254038, -50.40000000, -0.60000000], - [-100.20000000, -0.40000000, -0.60000000], - ], - [ - [99.80000000, -0.40000000, -0.60000000], - [86.40254038, -25.40000000, -43.90127019], - [49.80000000, -43.70127019, -75.60000000], - [-0.20000000, -50.40000000, -87.20254038], - [-50.20000000, -43.70127019, -75.60000000], - [-86.80254038, -25.40000000, -43.90127019], - [-100.20000000, -0.40000000, -0.60000000], - ], - [ - [99.80000000, -0.40000000, -0.60000000], - [86.40254038, 24.60000000, -43.90127019], - [49.80000000, 42.90127019, -75.60000000], - [-0.20000000, 49.60000000, -87.20254038], - [-50.20000000, 42.90127019, -75.60000000], - [-86.80254038, 24.60000000, -43.90127019], - [-100.20000000, -0.40000000, -0.60000000], - ], - [ - [99.80000000, -0.40000000, -0.60000000], - [86.40254038, 49.60000000, -0.60000000], - [49.80000000, 86.20254038, -0.60000000], - [-0.20000000, 99.60000000, -0.60000000], - [-50.20000000, 86.20254038, -0.60000000], - [-86.80254038, 49.60000000, -0.60000000], - [-100.20000000, -0.40000000, -0.60000000], - ], - [ - [99.80000000, -0.40000000, -0.60000000], - [86.40254038, 24.60000000, 42.70127019], - [49.80000000, 42.90127019, 74.40000000], - [-0.20000000, 49.60000000, 86.00254038], - [-50.20000000, 42.90127019, 74.40000000], - [-86.80254038, 24.60000000, 42.70127019], - [-100.20000000, -0.40000000, -0.60000000], - ], - [ - [99.80000000, -0.40000000, -0.60000000], - [86.40254038, -25.40000000, 42.70127019], - [49.80000000, -43.70127019, 74.40000000], - [-0.20000000, -50.40000000, 86.00254038], - [-50.20000000, -43.70127019, 74.40000000], - [-86.80254038, -25.40000000, 42.70127019], - [-100.20000000, -0.40000000, -0.60000000], - ], - ]), - decimal=7) - - for plane in ('xy', 'xz', 'yz'): + axis="+x", + ), + np.array( + [ + [ + [99.80000000, -0.40000000, -0.60000000], + [86.40254038, -50.40000000, -0.60000000], + [49.80000000, -87.00254038, -0.60000000], + [-0.20000000, -100.40000000, -0.60000000], + [-50.20000000, -87.00254038, -0.60000000], + [-86.80254038, -50.40000000, -0.60000000], + [-100.20000000, -0.40000000, -0.60000000], + ], + [ + [99.80000000, -0.40000000, -0.60000000], + [86.40254038, -25.40000000, -43.90127019], + [49.80000000, -43.70127019, -75.60000000], + [-0.20000000, -50.40000000, -87.20254038], + [-50.20000000, -43.70127019, -75.60000000], + [-86.80254038, -25.40000000, -43.90127019], + [-100.20000000, -0.40000000, -0.60000000], + ], + [ + [99.80000000, -0.40000000, -0.60000000], + [86.40254038, 24.60000000, -43.90127019], + [49.80000000, 42.90127019, -75.60000000], + [-0.20000000, 49.60000000, -87.20254038], + [-50.20000000, 42.90127019, -75.60000000], + [-86.80254038, 24.60000000, -43.90127019], + [-100.20000000, -0.40000000, -0.60000000], + ], + [ + [99.80000000, -0.40000000, -0.60000000], + [86.40254038, 49.60000000, -0.60000000], + [49.80000000, 86.20254038, -0.60000000], + [-0.20000000, 99.60000000, -0.60000000], + [-50.20000000, 86.20254038, -0.60000000], + [-86.80254038, 49.60000000, -0.60000000], + [-100.20000000, -0.40000000, -0.60000000], + ], + [ + [99.80000000, -0.40000000, -0.60000000], + [86.40254038, 24.60000000, 42.70127019], + [49.80000000, 42.90127019, 74.40000000], + [-0.20000000, 49.60000000, 86.00254038], + [-50.20000000, 42.90127019, 74.40000000], + [-86.80254038, 24.60000000, 42.70127019], + [-100.20000000, -0.40000000, -0.60000000], + ], + [ + [99.80000000, -0.40000000, -0.60000000], + [86.40254038, -25.40000000, 42.70127019], + [49.80000000, -43.70127019, 74.40000000], + [-0.20000000, -50.40000000, 86.00254038], + [-50.20000000, -43.70127019, 74.40000000], + [-86.80254038, -25.40000000, 42.70127019], + [-100.20000000, -0.40000000, -0.60000000], + ], + ] + ), + decimal=7, + ) + + for plane in ("xy", "xz", "yz"): np.testing.assert_almost_equal( primitive_vertices_sphere(axis=plane), - primitive_vertices_sphere(axis=PLANE_TO_AXIS_MAPPING[plane]), - decimal=7) + primitive_vertices_sphere(axis=MAPPING_PLANE_TO_AXIS[plane]), + decimal=7, + ) self.assertRaises( - ValueError, lambda: primitive_vertices_quad_mpl(axis='Undefined')) + ValueError, lambda: primitive_vertices_quad_mpl(axis="Undefined") + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/geometry/vertices.py b/colour/geometry/vertices.py index 2b2d02461d..5c2a591743 100644 --- a/colour/geometry/vertices.py +++ b/colour/geometry/vertices.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Geometry Primitive Vertices =========================== @@ -13,56 +12,81 @@ - :func:`colour.primitive_vertices` """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.algebra import spherical_to_cartesian -from colour.geometry import PLANE_TO_AXIS_MAPPING -from colour.utilities import (CaseInsensitiveMapping, as_float_array, - filter_kwargs, full, ones, tsplit, tstack, zeros) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.geometry import MAPPING_PLANE_TO_AXIS +from colour.hints import ( + Any, + ArrayLike, + Boolean, + Floating, + Integer, + List, + Literal, + NDArray, + Optional, + Union, +) +from colour.utilities import ( + CaseInsensitiveMapping, + as_float_array, + filter_kwargs, + full, + ones, + tsplit, + tstack, + zeros, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'primitive_vertices_quad_mpl', 'primitive_vertices_grid_mpl', - 'primitive_vertices_cube_mpl', 'primitive_vertices_sphere', - 'PRIMITIVE_VERTICES_METHODS', 'primitive_vertices' + "primitive_vertices_quad_mpl", + "primitive_vertices_grid_mpl", + "primitive_vertices_cube_mpl", + "primitive_vertices_sphere", + "PRIMITIVE_VERTICES_METHODS", + "primitive_vertices", ] -def primitive_vertices_quad_mpl(width=1, - height=1, - depth=0, - origin=np.array([0, 0]), - axis='+z'): +def primitive_vertices_quad_mpl( + width: Floating = 1, + height: Floating = 1, + depth: Floating = 0, + origin: ArrayLike = np.array([0, 0]), + axis: Union[Literal["+z", "+x", "+y", "yz", "xz", "xy"], str] = "+z", +) -> NDArray: """ - Returns the vertices of a quad primitive for use with *Matplotlib* + Return the vertices of a quad primitive for use with *Matplotlib* :class:`mpl_toolkits.mplot3d.art3d.Poly3DCollection` class. Parameters ---------- - width: numeric, optional + width Quad width. - height: numeric, optional + height Quad height. - depth: numeric, optional + depth Quad depth. - origin: array_like, optional + origin Quad origin on the construction plane. - axis : array_like, optional - **{'+z', '+x', '+y', 'yz', 'xz', 'xy'}**, + axis Axis the quad will be normal to, or plane the quad will be co-planar with. Returns ------- - ndarray + :class:`numpy.ndarray` Quad primitive vertices. Examples @@ -74,60 +98,73 @@ def primitive_vertices_quad_mpl(width=1, [ 0., 1., 0.]]) """ - axis = PLANE_TO_AXIS_MAPPING.get(axis, axis).lower() + axis = MAPPING_PLANE_TO_AXIS.get(axis, axis).lower() + axis = validate_method( + axis, ["+x", "+y", "+z"], '"{0}" axis invalid, it must be one of {1}!' + ) u, v = tsplit(origin) - if axis == '+z': - vertices = ((u, v, depth), (u + width, v, depth), - (u + width, v + height, depth), (u, v + height, depth)) - elif axis == '+y': - vertices = ((u, depth, v), (u + width, depth, v), - (u + width, depth, v + height), (u, depth, v + height)) - elif axis == '+x': - vertices = ((depth, u, v), (depth, u + width, v), - (depth, u + width, v + height), (depth, u, v + height)) - else: - raise ValueError('Axis must be one of "{0}"!'.format( - ['+x', '+y', '+z'])) + if axis == "+z": + vertices = [ + (u, v, depth), + (u + width, v, depth), + (u + width, v + height, depth), + (u, v + height, depth), + ] + elif axis == "+y": + vertices = [ + (u, depth, v), + (u + width, depth, v), + (u + width, depth, v + height), + (u, depth, v + height), + ] + elif axis == "+x": + vertices = [ + (depth, u, v), + (depth, u + width, v), + (depth, u + width, v + height), + (depth, u, v + height), + ] return as_float_array(vertices) -def primitive_vertices_grid_mpl(width=1, - height=1, - depth=0, - width_segments=1, - height_segments=1, - origin=np.array([0, 0]), - axis='+z'): +def primitive_vertices_grid_mpl( + width: Floating = 1, + height: Floating = 1, + depth: Floating = 0, + width_segments: Integer = 1, + height_segments: Integer = 1, + origin: ArrayLike = np.array([0, 0]), + axis: Union[Literal["+z", "+x", "+y", "yz", "xz", "xy"], str] = "+z", +) -> NDArray: """ - Returns the vertices of a grid primitive made of quad primitives for use + Return the vertices of a grid primitive made of quad primitives for use with *Matplotlib* :class:`mpl_toolkits.mplot3d.art3d.Poly3DCollection` class. Parameters ---------- - width: numeric, optional + width Grid width. - height: numeric, optional + height Grid height. - depth: numeric, optional + depth Grid depth. - width_segments: int, optional + width_segments: Grid width segments, quad primitive counts along the width. - height_segments: int, optional + height_segments: Grid height segments, quad primitive counts along the height. - origin: array_like, optional + origin Grid origin on the construction plane. - axis : array_like, optional - **{'+z', '+x', '+y', 'yz', 'xz', 'xy'}**, + axis Axis the grid will be normal to, or plane the grid will be co-planar with. Returns ------- - ndarray + :class:`numpy.ndarray` Grid primitive vertices. Examples @@ -162,49 +199,66 @@ def primitive_vertices_grid_mpl(width=1, for i in range(width_segments): for j in range(height_segments): quads.append( - primitive_vertices_quad_mpl(w_x, h_y, depth, - (i * w_x + u, j * h_y + v), axis)) + primitive_vertices_quad_mpl( + w_x, h_y, depth, (i * w_x + u, j * h_y + v), axis + ) + ) return as_float_array(quads) -def primitive_vertices_cube_mpl(width=1, - height=1, - depth=1, - width_segments=1, - height_segments=1, - depth_segments=1, - origin=np.array([0, 0, 0]), - planes=None): +def primitive_vertices_cube_mpl( + width: Floating = 1, + height: Floating = 1, + depth: Floating = 1, + width_segments: Integer = 1, + height_segments: Integer = 1, + depth_segments: Integer = 1, + origin: ArrayLike = np.array([0, 0, 0]), + planes: Optional[ + Literal[ + "-x", + "+x", + "-y", + "+y", + "-z", + "+z", + "xy", + "xz", + "yz", + "yx", + "zx", + "zy", + ] + ] = None, +) -> NDArray: """ - Returns the vertices of a cube primitive made of grid primitives for use + Return the vertices of a cube primitive made of grid primitives for use with *Matplotlib* :class:`mpl_toolkits.mplot3d.art3d.Poly3DCollection` class. Parameters ---------- - width : float, optional + width Cube width. - height : float, optional + height Cube height. - depth : float, optional + depth Cube depth. - width_segments : int, optional + width_segments Cube segments count along the width. - height_segments : float, optional + height_segments Cube segments count along the height. - depth_segments : float, optional + depth_segments Cube segments count along the depth. - origin : array_like, optional + origin Cube origin. - planes : array_like, optional - **{'-x', '+x', '-y', '+y', '-z', '+z', - 'xy', 'xz', 'yz', 'yx', 'zx', 'zy'}**, + planes Grid primitives to include in the cube construction. Returns ------- - ndarray + :class:`numpy.ndarray` Cube primitive vertices. Examples @@ -241,76 +295,92 @@ def primitive_vertices_cube_mpl(width=1, [ 1., 0., 1.]]]) """ - planes = (sorted(list( - PLANE_TO_AXIS_MAPPING.values())) if planes is None else [ - PLANE_TO_AXIS_MAPPING.get(plane, plane).lower() for plane in planes - ]) + axis = ( + sorted(list(MAPPING_PLANE_TO_AXIS.values())) + if planes is None + else [ + MAPPING_PLANE_TO_AXIS.get(plane, plane).lower() for plane in planes + ] + ) u, v, w = tsplit(origin) w_s, h_s, d_s = width_segments, height_segments, depth_segments - grids = [] - if '-z' in planes: + grids: List = [] + if "-z" in axis: grids.extend( - primitive_vertices_grid_mpl(width, depth, v, w_s, d_s, (u, w), - '+z')) - if '+z' in planes: + primitive_vertices_grid_mpl( + width, depth, v, w_s, d_s, (u, w), "+z" + ) + ) + if "+z" in axis: grids.extend( - primitive_vertices_grid_mpl(width, depth, v + height, w_s, d_s, - (u, w), '+z')) + primitive_vertices_grid_mpl( + width, depth, v + height, w_s, d_s, (u, w), "+z" + ) + ) - if '-y' in planes: + if "-y" in axis: grids.extend( - primitive_vertices_grid_mpl(width, height, w, w_s, h_s, (u, v), - '+y')) - if '+y' in planes: + primitive_vertices_grid_mpl( + width, height, w, w_s, h_s, (u, v), "+y" + ) + ) + if "+y" in axis: grids.extend( - primitive_vertices_grid_mpl(width, height, w + depth, w_s, h_s, - (u, v), '+y')) + primitive_vertices_grid_mpl( + width, height, w + depth, w_s, h_s, (u, v), "+y" + ) + ) - if '-x' in planes: + if "-x" in axis: grids.extend( - primitive_vertices_grid_mpl(depth, height, u, d_s, h_s, (w, v), - '+x')) - if '+x' in planes: + primitive_vertices_grid_mpl( + depth, height, u, d_s, h_s, (w, v), "+x" + ) + ) + if "+x" in axis: grids.extend( - primitive_vertices_grid_mpl(depth, height, u + width, d_s, h_s, - (w, v), '+x')) + primitive_vertices_grid_mpl( + depth, height, u + width, d_s, h_s, (w, v), "+x" + ) + ) return as_float_array(grids) -def primitive_vertices_sphere(radius=0.5, - segments=8, - intermediate=False, - origin=np.array([0, 0, 0]), - axis='+z'): +def primitive_vertices_sphere( + radius: Floating = 0.5, + segments: Integer = 8, + intermediate: Boolean = False, + origin: ArrayLike = np.array([0, 0, 0]), + axis: Union[Literal["+z", "+x", "+y", "yz", "xz", "xy"], str] = "+z", +) -> NDArray: """ - Returns the vertices of a latitude-longitude sphere primitive. + Return the vertices of a latitude-longitude sphere primitive. Parameters ---------- - radius: numeric, optional + radius Sphere radius. - segments: numeric, optional + segments Latitude-longitude segments, if the ``intermediate`` argument is *True*, then the sphere will have one less segment along its longitude. - intermediate: bool, optional + intermediate Whether to generate the sphere vertices at the center of the faces outlined by the segments of a regular sphere generated without the ``intermediate`` argument set to *True*. The resulting sphere is inscribed on the regular sphere faces but possesses the same poles. - origin: array_like, optional + origin Sphere origin on the construction plane. - axis : array_like, optional - **{'+z', '+x', '+y', 'yz', 'xz', 'xy'}**, + axis Axis (or normal of the plane) the poles of the sphere will be aligned with. Returns ------- - ndarray + :class:`numpy.ndarray` Sphere primitive vertices. Notes @@ -345,28 +415,41 @@ def primitive_vertices_sphere(radius=0.5, [ 3.7493994...e-33, 6.1232340...e-17, -5.0000000...e-01]]]) """ - axis = PLANE_TO_AXIS_MAPPING.get(axis, axis).lower() + axis = MAPPING_PLANE_TO_AXIS.get(axis, axis).lower() + axis = validate_method( + axis, ["+x", "+y", "+z"], '"{0}" axis invalid, it must be one of {1}!' + ) if not intermediate: theta = np.tile( - np.radians(np.linspace(0, 180, segments + 1)), (segments + 1, 1)) + np.radians(np.linspace(0, 180, segments + 1)), + (int(segments) + 1, 1), + ) phi = np.transpose( np.tile( np.radians(np.linspace(-180, 180, segments + 1)), - (segments + 1, 1))) + (int(segments) + 1, 1), + ) + ) else: theta = np.tile( np.radians(np.linspace(0, 180, segments * 2 + 1)[1::2][1:-1]), - (segments + 1, 1)) - theta = np.hstack([ - zeros([segments + 1, 1]), - theta, - full([segments + 1, 1], np.pi), - ]) + (int(segments) + 1, 1), + ) + theta = np.hstack( + [ + zeros((segments + 1, 1)), + theta, + full((segments + 1, 1), np.pi), + ] + ) phi = np.transpose( np.tile( - np.radians(np.linspace(-180, 180, segments + 1)) + np.radians( - 360 / segments / 2), (segments, 1))) + np.radians(np.linspace(-180, 180, segments + 1)) + + np.radians(360 / segments / 2), + (int(segments), 1), + ) + ) rho = ones(phi.shape) * radius rho_theta_phi = tstack([rho, theta, phi]) @@ -376,109 +459,109 @@ def primitive_vertices_sphere(radius=0.5, # Removing extra longitude vertices. vertices = vertices[:-1, :, :] - if axis == '+z': + if axis == "+z": pass - elif axis == '+y': + elif axis == "+y": vertices = np.roll(vertices, 2, -1) - elif axis == '+x': + elif axis == "+x": vertices = np.roll(vertices, 1, -1) - else: - raise ValueError('Axis must be one of "{0}"!'.format( - ['+x', '+y', '+z'])) vertices += origin return vertices -PRIMITIVE_VERTICES_METHODS = CaseInsensitiveMapping({ - 'Quad MPL': primitive_vertices_quad_mpl, - 'Grid MPL': primitive_vertices_grid_mpl, - 'Cube MPL': primitive_vertices_cube_mpl, - 'Sphere': primitive_vertices_sphere, -}) +PRIMITIVE_VERTICES_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Quad MPL": primitive_vertices_quad_mpl, + "Grid MPL": primitive_vertices_grid_mpl, + "Cube MPL": primitive_vertices_cube_mpl, + "Sphere": primitive_vertices_sphere, + } +) PRIMITIVE_VERTICES_METHODS.__doc__ = """ Supported geometry primitive vertices generation methods. - -PRIMITIVE_VERTICES_METHODS : CaseInsensitiveMapping - **{'Cube MPL', 'Quad MPL', 'Grid MPL', 'Sphere'}** """ -def primitive_vertices(method='Cube MPL', **kwargs): +def primitive_vertices( + method: Union[ + Literal["Cube MPL", "Quad MPL", "Grid MPL", "Sphere"], str + ] = "Cube MPL", + **kwargs: Any, +) -> NDArray: """ - Returns the vertices of a geometry primitive using given method. + Return the vertices of a geometry primitive using given method. Parameters ---------- - method : unicode, optional - **{'Cube MPL', 'Quad MPL', 'Grid MPL', 'Sphere'}**, + method Vertices generation method. Other Parameters ---------------- - origin : unicode, optional - {:func:`colour.geometry.primitive_vertices_quad_mpl`, - :func:`colour.geometry.primitive_vertices_grid_mpl`, - :func:`colour.geometry.primitive_vertices_cube_mpl`, - :func:`colour.geometry.primitive_vertices_sphere`}, - Primitive origin on the construction plane. - axis : array_like, optional + axis {:func:`colour.geometry.primitive_vertices_quad_mpl`, :func:`colour.geometry.primitive_vertices_grid_mpl`, :func:`colour.geometry.primitive_vertices_sphere`}, **{'+z', '+x', '+y', 'yz', 'xz', 'xy'}**, Axis the primitive will be normal to, or plane the primitive will be co-planar with. - planes : array_like, optional - {:func:`colour.geometry.primitive_vertices_cube_mpl`}, - **{'-x', '+x', '-y', '+y', '-z', '+z', - 'xy', 'xz', 'yz', 'yx', 'zx', 'zy'}**, - Included grid primitives in the cube construction. - width : numeric, optional + depth {:func:`colour.geometry.primitive_vertices_quad_mpl`, :func:`colour.geometry.primitive_vertices_grid_mpl`, :func:`colour.geometry.primitive_vertices_cube_mpl`}, - Primitive width. - height : numeric, optional + Primitive depth. + depth_segments + {:func:`colour.geometry.primitive_vertices_grid_mpl`, + :func:`colour.geometry.primitive_vertices_cube_mpl`}, + Primitive depth segments, quad primitive counts along the depth. + height {:func:`colour.geometry.primitive_vertices_quad_mpl`, :func:`colour.geometry.primitive_vertices_grid_mpl`, :func:`colour.geometry.primitive_vertices_cube_mpl`}, Primitive height. - depth : numeric, optional + height_segments + {:func:`colour.geometry.primitive_vertices_grid_mpl`, + :func:`colour.geometry.primitive_vertices_cube_mpl`}, + Primitive height segments, quad primitive counts along the height. + intermediate + {:func:`colour.geometry.primitive_vertices_sphere`}, + Whether to generate the sphere vertices at the center of the faces + outlined by the segments of a regular sphere generated without + the ``intermediate`` argument set to *True*. The resulting sphere is + inscribed on the regular sphere faces but possesses the same poles. + origin {:func:`colour.geometry.primitive_vertices_quad_mpl`, :func:`colour.geometry.primitive_vertices_grid_mpl`, - :func:`colour.geometry.primitive_vertices_cube_mpl`}, - Primitive depth. - radius: numeric, optional + :func:`colour.geometry.primitive_vertices_cube_mpl`, + :func:`colour.geometry.primitive_vertices_sphere`}, + Primitive origin on the construction plane. + planes + {:func:`colour.geometry.primitive_vertices_cube_mpl`}, + **{'-x', '+x', '-y', '+y', '-z', '+z', + 'xy', 'xz', 'yz', 'yx', 'zx', 'zy'}**, + Included grid primitives in the cube construction. + radius {:func:`colour.geometry.primitive_vertices_sphere`}, Sphere radius. - segments : int, optional, + segments {:func:`colour.geometry.primitive_vertices_sphere`}, Latitude-longitude segments, if the ``intermediate`` argument is *True*, then the sphere will have one less segment along its longitude. - intermediate: bool, optional - {:func:`colour.geometry.primitive_vertices_sphere`}, - Whether to generate the sphere vertices at the center of the faces - outlined by the segments of a regular sphere generated without - the ``intermediate`` argument set to *True*. The resulting sphere is - inscribed on the regular sphere faces but possesses the same poles. + width + {:func:`colour.geometry.primitive_vertices_quad_mpl`, + :func:`colour.geometry.primitive_vertices_grid_mpl`, + :func:`colour.geometry.primitive_vertices_cube_mpl`}, + Primitive width. width_segments {:func:`colour.geometry.primitive_vertices_grid_mpl`, :func:`colour.geometry.primitive_vertices_cube_mpl`}, Primitive width segments, quad primitive counts along the width. - height_segments - {:func:`colour.geometry.primitive_vertices_grid_mpl`, - :func:`colour.geometry.primitive_vertices_cube_mpl`}, - Primitive height segments, quad primitive counts along the height. - depth_segments - {:func:`colour.geometry.primitive_vertices_grid_mpl`, - :func:`colour.geometry.primitive_vertices_cube_mpl`}, - Primitive depth segments, quad primitive counts along the depth. Returns ------- - ndarray + :class:`numpy.ndarray` Primitive vertices. Examples @@ -544,6 +627,8 @@ def primitive_vertices(method='Cube MPL', **kwargs): [ 3.7493994...e-33, 6.1232340...e-17, -5.0000000...e-01]]]) """ + method = validate_method(method, PRIMITIVE_VERTICES_METHODS) + function = PRIMITIVE_VERTICES_METHODS[method] return function(**filter_kwargs(function, **kwargs)) diff --git a/colour/graph/__init__.py b/colour/graph/__init__.py index 4f81be6e30..4f8b370216 100644 --- a/colour/graph/__init__.py +++ b/colour/graph/__init__.py @@ -1,11 +1,13 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - -from .conversion import (CONVERSION_GRAPH, CONVERSION_GRAPH_NODE_LABELS, - describe_conversion_path, convert) +from .conversion import ( + CONVERSION_GRAPH, + CONVERSION_GRAPH_NODE_LABELS, + describe_conversion_path, + convert, +) __all__ = [ - 'CONVERSION_GRAPH', 'CONVERSION_GRAPH_NODE_LABELS', - 'describe_conversion_path', 'convert' + "CONVERSION_GRAPH", + "CONVERSION_GRAPH_NODE_LABELS", + "describe_conversion_path", + "convert", ] diff --git a/colour/graph/conversion.py b/colour/graph/conversion.py index 742f1247e3..de920db154 100644 --- a/colour/graph/conversion.py +++ b/colour/graph/conversion.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Automatic Colour Conversion Graph ================================= @@ -9,7 +8,7 @@ - :func:`colour.convert` """ -from __future__ import division, print_function, unicode_literals +from __future__ import annotations import inspect import numpy as np @@ -19,133 +18,318 @@ from functools import partial from pprint import pformat -from colour.colorimetry import (CCS_ILLUMINANTS, SDS_ILLUMINANTS, - TVS_ILLUMINANTS_HUNTERLAB) -from colour.colorimetry import (colorimetric_purity, complementary_wavelength, - dominant_wavelength, excitation_purity, - lightness, luminance, luminous_efficacy, - luminous_efficiency, luminous_flux, sd_to_XYZ, - whiteness, yellowness, wavelength_to_XYZ) +import colour +from colour.colorimetry import ( + CCS_ILLUMINANTS, + MultiSpectralDistributions, + SDS_ILLUMINANTS, + SpectralDistribution, + TVS_ILLUMINANTS_HUNTERLAB, +) +from colour.colorimetry import ( + colorimetric_purity, + complementary_wavelength, + dominant_wavelength, + excitation_purity, + lightness, + luminance, + luminous_efficacy, + luminous_efficiency, + luminous_flux, + whiteness, + yellowness, + wavelength_to_XYZ, +) +from colour.hints import ( + Any, + ArrayLike, + Callable, + Dict, + FloatingOrArrayLike, + FloatingOrNDArray, + Integer, + List, + Literal, + NDArray, + Number, + Optional, + Union, + cast, +) from colour.recovery import XYZ_to_sd from colour.models import RGB_COLOURSPACE_sRGB from colour.models import ( - CAM02LCD_to_JMh_CIECAM02, CAM02SCD_to_JMh_CIECAM02, - CAM02UCS_to_JMh_CIECAM02, CAM16LCD_to_JMh_CAM16, CAM16SCD_to_JMh_CAM16, - CAM16UCS_to_JMh_CAM16, CMYK_to_CMY, CMY_to_CMYK, CMY_to_RGB, DIN99_to_Lab, - HSL_to_RGB, HSV_to_RGB, Hunter_Lab_to_XYZ, Hunter_Rdab_to_XYZ, - ICTCP_to_RGB, IGPGTG_to_XYZ, IPT_to_XYZ, JMh_CAM16_to_CAM16LCD, - JMh_CAM16_to_CAM16SCD, JMh_CAM16_to_CAM16UCS, JMh_CIECAM02_to_CAM02LCD, - JMh_CIECAM02_to_CAM02SCD, JMh_CIECAM02_to_CAM02UCS, JzAzBz_to_XYZ, - LCHab_to_Lab, LCHuv_to_Luv, Lab_to_DIN99, Lab_to_LCHab, Lab_to_XYZ, - Luv_to_LCHuv, Luv_to_XYZ, Luv_to_uv, Luv_uv_to_xy, OSA_UCS_to_XYZ, - Prismatic_to_RGB, RGB_luminance, RGB_to_CMY, RGB_to_HSL, RGB_to_HSV, - RGB_to_ICTCP, RGB_to_Prismatic, RGB_to_RGB, RGB_to_XYZ, RGB_to_YCbCr, - RGB_to_YCoCg, RGB_to_YcCbcCrc, UCS_to_XYZ, UCS_to_uv, UCS_uv_to_xy, - UVW_to_XYZ, XYZ_to_Hunter_Lab, XYZ_to_Hunter_Rdab, XYZ_to_IGPGTG, - XYZ_to_IPT, XYZ_to_JzAzBz, XYZ_to_Lab, XYZ_to_Luv, XYZ_to_OSA_UCS, - XYZ_to_RGB, XYZ_to_UCS, XYZ_to_UVW, XYZ_to_hdr_CIELab, XYZ_to_hdr_IPT, - XYZ_to_sRGB, XYZ_to_xy, XYZ_to_xyY, YCbCr_to_RGB, YCoCg_to_RGB, - YcCbcCrc_to_RGB, cctf_decoding, cctf_encoding, hdr_CIELab_to_XYZ, - hdr_IPT_to_XYZ, sRGB_to_XYZ, uv_to_Luv, uv_to_UCS, xyY_to_XYZ, xyY_to_xy, - xy_to_Luv_uv, xy_to_UCS_uv, xy_to_XYZ, xy_to_xyY) -from colour.notation import (HEX_to_RGB, RGB_to_HEX, munsell_value, - munsell_colour_to_xyY, xyY_to_munsell_colour) + CAM02LCD_to_JMh_CIECAM02, + CAM02SCD_to_JMh_CIECAM02, + CAM02UCS_to_JMh_CIECAM02, + CAM16LCD_to_JMh_CAM16, + CAM16SCD_to_JMh_CAM16, + CAM16UCS_to_JMh_CAM16, + CMYK_to_CMY, + CMY_to_CMYK, + CMY_to_RGB, + DIN99_to_XYZ, + HCL_to_RGB, + HSL_to_RGB, + HSV_to_RGB, + Hunter_Lab_to_XYZ, + Hunter_Rdab_to_XYZ, + ICaCb_to_XYZ, + ICtCp_to_XYZ, + IHLS_to_RGB, + IgPgTg_to_XYZ, + IPT_to_XYZ, + JMh_CAM16_to_CAM16LCD, + JMh_CAM16_to_CAM16SCD, + JMh_CAM16_to_CAM16UCS, + JMh_CIECAM02_to_CAM02LCD, + JMh_CIECAM02_to_CAM02SCD, + JMh_CIECAM02_to_CAM02UCS, + Jzazbz_to_XYZ, + LCHab_to_Lab, + LCHuv_to_Luv, + Lab_to_LCHab, + Lab_to_XYZ, + Luv_to_LCHuv, + Luv_to_XYZ, + Luv_to_uv, + Luv_uv_to_xy, + OSA_UCS_to_XYZ, + Oklab_to_XYZ, + Prismatic_to_RGB, + ProLab_to_XYZ, + RGB_Colourspace, + RGB_luminance, + RGB_to_CMY, + RGB_to_HCL, + RGB_to_HSL, + RGB_to_HSV, + RGB_to_IHLS, + RGB_to_Prismatic, + RGB_to_RGB, + RGB_to_XYZ, + RGB_to_YCbCr, + RGB_to_YCoCg, + RGB_to_YcCbcCrc, + UCS_to_XYZ, + UCS_to_uv, + UCS_uv_to_xy, + UVW_to_XYZ, + XYZ_to_DIN99, + XYZ_to_Hunter_Lab, + XYZ_to_Hunter_Rdab, + XYZ_to_ICaCb, + XYZ_to_ICtCp, + XYZ_to_IgPgTg, + XYZ_to_IPT, + XYZ_to_Jzazbz, + XYZ_to_Lab, + XYZ_to_Luv, + XYZ_to_OSA_UCS, + XYZ_to_Oklab, + XYZ_to_ProLab, + XYZ_to_RGB, + XYZ_to_UCS, + XYZ_to_UVW, + XYZ_to_hdr_CIELab, + XYZ_to_hdr_IPT, + XYZ_to_sRGB, + XYZ_to_xy, + XYZ_to_xyY, + YCbCr_to_RGB, + YCoCg_to_RGB, + YcCbcCrc_to_RGB, + cctf_decoding, + cctf_encoding, + hdr_CIELab_to_XYZ, + hdr_IPT_to_XYZ, + sRGB_to_XYZ, + uv_to_Luv, + uv_to_UCS, + xyY_to_XYZ, + xyY_to_xy, + xy_to_Luv_uv, + xy_to_UCS_uv, + xy_to_XYZ, + xy_to_xyY, +) +from colour.notation import ( + HEX_to_RGB, + RGB_to_HEX, + munsell_value, + munsell_colour_to_xyY, + xyY_to_munsell_colour, +) from colour.quality import colour_quality_scale, colour_rendering_index from colour.appearance import ( - CAM_Specification_CAM16, CAM16_to_XYZ, CAM_Specification_CIECAM02, - CIECAM02_to_XYZ, XYZ_to_ATD95, XYZ_to_CAM16, XYZ_to_CIECAM02, XYZ_to_Hunt, - XYZ_to_LLAB, XYZ_to_Nayatani95, XYZ_to_RLAB) -from colour.temperature import CCT_to_uv, CCT_to_xy, uv_to_CCT, xy_to_CCT -from colour.utilities import (domain_range_scale, filter_kwargs, message_box, - required, tsplit, tstack, usage_warning) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + CAM_Specification_CAM16, + CAM16_to_XYZ, + CAM_Specification_CIECAM02, + CIECAM02_to_XYZ, + Kim2009_to_XYZ, + XYZ_to_ATD95, + XYZ_to_CAM16, + XYZ_to_CIECAM02, + XYZ_to_Hunt, + XYZ_to_Kim2009, + XYZ_to_LLAB, + XYZ_to_Nayatani95, + XYZ_to_RLAB, + XYZ_to_ZCAM, + ZCAM_to_XYZ, +) +from colour.appearance.ciecam02 import CAM_KWARGS_CIECAM02_sRGB +from colour.temperature import CCT_to_uv, uv_to_CCT +from colour.utilities import ( + as_float_array, + domain_range_scale, + filter_kwargs, + message_box, + optional, + required, + tsplit, + tstack, + usage_warning, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'Conversion_Specification', 'CIECAM02_to_JMh_CIECAM02', - 'JMh_CIECAM02_to_CIECAM02', 'CAM16_to_JMh_CAM16', 'JMh_CAM16_to_CAM16', - 'XYZ_to_luminance', 'RGB_luminance_to_RGB', - 'CONVERSION_SPECIFICATIONS_DATA', 'CONVERSION_GRAPH_NODE_LABELS', - 'CONVERSION_SPECIFICATIONS', 'CONVERSION_GRAPH', - 'describe_conversion_path', 'convert' + "Conversion_Specification", + "sd_to_XYZ", + "CIECAM02_to_JMh_CIECAM02", + "JMh_CIECAM02_to_CIECAM02", + "CAM16_to_JMh_CAM16", + "JMh_CAM16_to_CAM16", + "XYZ_to_luminance", + "RGB_luminance_to_RGB", + "CONVERSION_SPECIFICATIONS_DATA", + "CONVERSION_GRAPH_NODE_LABELS", + "CONVERSION_SPECIFICATIONS", + "CONVERSION_GRAPH", + "describe_conversion_path", + "convert", ] class Conversion_Specification( - namedtuple('Conversion_Specification', - ('source', 'target', 'conversion_function'))): + namedtuple( + "Conversion_Specification", ("source", "target", "conversion_function") + ) +): """ Conversion specification for *Colour* graph for automatic colour conversion describing two nodes and the edge in the graph. Parameters ---------- - source : unicode + source Source node in the graph. - target : array_like + target Target node in the graph. - conversion_function : callable + conversion_function Callable converting from the ``source`` node to the ``target`` node. """ - def __new__(cls, source=None, target=None, conversion_function=None): - return super(Conversion_Specification, cls).__new__( - cls, source.lower(), target.lower(), conversion_function) + def __new__(cls, source: str, target: str, conversion_function: Callable): + """ + Return a new instance of the + :class:`colour.graph.conversion.Conversion_Specification` class. + """ + + return super().__new__( + cls, source.lower(), target.lower(), conversion_function + ) + + +def sd_to_XYZ( + sd: Union[ArrayLike, SpectralDistribution, MultiSpectralDistributions], + cmfs: Optional[MultiSpectralDistributions] = None, + illuminant: Optional[SpectralDistribution] = None, + k: Optional[Number] = None, + method: Union[Literal["ASTM E308", "Integration"], str] = "ASTM E308", + **kwargs: Any, +) -> NDArray: + """ + Convert given spectral distribution to *CIE XYZ* tristimulus values using + given colour matching functions, illuminant and method. + + This placeholder docstring is replaced with the modified + :func:`colour.sd_to_XYZ` definition docstring. + """ + + illuminant = cast( + SpectralDistribution, + optional(illuminant, SDS_ILLUMINANTS[_ILLUMINANT_DEFAULT]), + ) + return colour.sd_to_XYZ(sd, cmfs, illuminant, k, method, **kwargs) -def CIECAM02_to_JMh_CIECAM02(CAM_Specification_CIECAM02): + +# If-clause required for optimised python launch. +if colour.sd_to_XYZ.__doc__ is not None: + sd_to_XYZ.__doc__ = colour.sd_to_XYZ.__doc__.replace( + "CIE Illuminant E", + "CIE Standard Illuminant D65", + ).replace( + "sd_to_XYZ(sd)", + "sd_to_XYZ(sd) # doctest: +SKIP", + ) + + +def CIECAM02_to_JMh_CIECAM02( + specification: CAM_Specification_CIECAM02, +) -> NDArray: """ - Converts from *CIECAM02* specification to *CIECAM02* :math:`JMh` + Convert from *CIECAM02* specification to *CIECAM02* :math:`JMh` correlates. Parameters ---------- - CAM_Specification_CIECAM02 : CAM_Specification_CIECAM02 + specification *CIECAM02* colour appearance model specification. Returns ------- - ndarray + :class:`numpy.ndarray` *CIECAM02* :math:`JMh` correlates. Examples -------- >>> specification = CAM_Specification_CIECAM02(J=41.731091132513917, - ... M=0.108842175669226, - ... h=219.048432658311780) + ... M=0.108842175669226, + ... h=219.048432658311780) >>> CIECAM02_to_JMh_CIECAM02(specification) # doctest: +ELLIPSIS array([ 4.1731091...e+01, 1.0884217...e-01, 2.1904843...e+02]) """ - return tstack([ - CAM_Specification_CIECAM02.J, - CAM_Specification_CIECAM02.M, - CAM_Specification_CIECAM02.h, - ]) + return tstack([specification.J, specification.M, specification.h]) -def JMh_CIECAM02_to_CIECAM02(JMh): +def JMh_CIECAM02_to_CIECAM02(JMh: ArrayLike) -> CAM_Specification_CIECAM02: """ - Converts from *CIECAM02* :math:`JMh` correlates to *CIECAM02* + Convert from *CIECAM02* :math:`JMh` correlates to *CIECAM02* specification. Parameters ---------- - JMh : array_like + JMh *CIECAM02* :math:`JMh` correlates. Returns ------- - CAM_Specification_CIECAM02 + :class:`colour.CAM_Specification_CIECAM02` *CIECAM02* colour appearance model specification. Examples -------- + >>> import numpy as np >>> JMh = np.array([4.17310911e+01, 1.08842176e-01, 2.19048433e+02]) >>> JMh_CIECAM02_to_CIECAM02(JMh) # doctest: +ELLIPSIS CAM_Specification_CIECAM02(J=41.7310911..., C=None, h=219.0484329..., \ @@ -157,52 +341,49 @@ def JMh_CIECAM02_to_CIECAM02(JMh): return CAM_Specification_CIECAM02(J=J, M=M, h=h) -def CAM16_to_JMh_CAM16(CAM_Specification_CAM16): +def CAM16_to_JMh_CAM16(specification) -> NDArray: """ - Converts from *CAM16* specification to *CAM16* :math:`JMh` correlates. + Convert from *CAM16* specification to *CAM16* :math:`JMh` correlates. Parameters ---------- - CAM_Specification_CAM16 : CAM_Specification_CAM16 + specification *CAM16* colour appearance model specification. Returns ------- - ndarray + :class:`numpy.ndarray` *CAM16* :math:`JMh` correlates. Examples -------- >>> specification = CAM_Specification_CAM16(J=41.731207905126638, - ... M=0.107436772335905, - ... h=217.067959767393010) + ... M=0.107436772335905, + ... h=217.067959767393010) >>> CAM16_to_JMh_CAM16(specification) # doctest: +ELLIPSIS array([ 4.1731207...e+01, 1.0743677...e-01, 2.1706796...e+02]) """ - return tstack([ - CAM_Specification_CAM16.J, - CAM_Specification_CAM16.M, - CAM_Specification_CAM16.h, - ]) + return tstack([specification.J, specification.M, specification.h]) -def JMh_CAM16_to_CAM16(JMh): +def JMh_CAM16_to_CAM16(JMh: ArrayLike) -> CAM_Specification_CAM16: """ - Converts from *CAM6* :math:`JMh` correlates to *CAM6* specification. + Convert from *CAM6* :math:`JMh` correlates to *CAM6* specification. Parameters ---------- - JMh : array_like + JMh *CAM6* :math:`JMh` correlates. Returns ------- - CAM6_Specification + :class:`colour.CAM6_Specification` *CAM6* colour appearance model specification. Examples -------- + >>> import numpy as np >>> JMh = np.array([4.17312079e+01, 1.07436772e-01, 2.17067960e+02]) >>> JMh_CAM16_to_CAM16(JMh) # doctest: +ELLIPSIS CAM_Specification_CAM16(J=41.7312079..., C=None, h=217.06796..., s=None, \ @@ -214,22 +395,23 @@ def JMh_CAM16_to_CAM16(JMh): return CAM_Specification_CAM16(J=J, M=M, h=h) -def XYZ_to_luminance(XYZ): +def XYZ_to_luminance(XYZ: ArrayLike) -> FloatingOrNDArray: """ - Converts from *CIE XYZ* tristimulus values to *luminance* :math:`Y`. + Convert from *CIE XYZ* tristimulus values to *luminance* :math:`Y`. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. Returns ------- - array_like + :class:`numpy.floating` or :class:`numpy.ndarray` *Luminance* :math:`Y`. Examples -------- + >>> import numpy as np >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_to_luminance(XYZ) # doctest: +ELLIPSIS 0.1219722... @@ -240,18 +422,18 @@ def XYZ_to_luminance(XYZ): return Y -def RGB_luminance_to_RGB(Y): +def RGB_luminance_to_RGB(Y: FloatingOrArrayLike) -> NDArray: """ - Converts from *luminance* :math:`Y` to *RGB*. + Convert from *luminance* :math:`Y` to *RGB*. Parameters ---------- - Y : array_like + Y *Luminance* :math:`Y`. Returns ------- - array_like + :class:`numpy.ndarray` *RGB*. Examples @@ -260,308 +442,403 @@ def RGB_luminance_to_RGB(Y): array([ 0.1230145..., 0.1230145..., 0.1230145...]) """ - return tstack([Y, Y, Y]) + Y = as_float_array(Y) + return tstack([Y, Y, Y]) -_DEFAULT_ILLUMINANT = 'D65' -""" -Default automatic colour conversion graph illuminant name. - -_DEFAULT_ILLUMINANT : unicode -""" - -_SD_DEFAULT_ILLUMINANT = SDS_ILLUMINANTS[_DEFAULT_ILLUMINANT] -""" -Default automatic colour conversion graph illuminant spectral distribution. -_SD_DEFAULT_ILLUMINANT : SpectralDistribution -""" +_ILLUMINANT_DEFAULT: str = "D65" +"""Default automatic colour conversion graph illuminant name.""" -_CCS_DEFAULT_ILLUMINANT = CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][_DEFAULT_ILLUMINANT] +_CCS_ILLUMINANT_DEFAULT: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][_ILLUMINANT_DEFAULT] """ Default automatic colour conversion graph illuminant *CIE xy* chromaticity coordinates. - -_CCS_DEFAULT_ILLUMINANT : ndarray """ -_TVS_DEFAULT_ILLUMINANT = xy_to_XYZ(_CCS_DEFAULT_ILLUMINANT) +_TVS_ILLUMINANT_DEFAULT: NDArray = xy_to_XYZ(_CCS_ILLUMINANT_DEFAULT) """ Default automatic colour conversion graph illuminant *CIE XYZ* tristimulus values. - -_TVS_DEFAULT_ILLUMINANT : ndarray """ -_RGB_COLOURSPACE_DEFAULT = RGB_COLOURSPACE_sRGB +_RGB_COLOURSPACE_DEFAULT: RGB_Colourspace = RGB_COLOURSPACE_sRGB +"""Default automatic colour conversion graph *RGB* colourspace.""" + +_CAM_KWARGS_CIECAM02_sRGB: Dict = CAM_KWARGS_CIECAM02_sRGB.copy() """ -Default automatic colour conversion graph *RGB* colourspace. +Default parameter values for the *CIECAM02* colour appearance model usage in +the context of *sRGB*. -_RGB_COLOURSPACE_DEFAULT : RGB_COLOURSPACE_RGB +Warnings +-------- +The *CIE XYZ* tristimulus values of reference white :math:`XYZ_w` is adjusted +for the domain-range scale **'1'**. """ -CONVERSION_SPECIFICATIONS_DATA = [ +_CAM_KWARGS_CIECAM02_sRGB["XYZ_w"] = _CAM_KWARGS_CIECAM02_sRGB["XYZ_w"] / 100 + +CONVERSION_SPECIFICATIONS_DATA: List = [ # Colorimetry - ('Spectral Distribution', 'CIE XYZ', - partial(sd_to_XYZ, illuminant=_SD_DEFAULT_ILLUMINANT)), - ('CIE XYZ', 'Spectral Distribution', XYZ_to_sd), - ('Spectral Distribution', 'Luminous Flux', luminous_flux), - ('Spectral Distribution', 'Luminous Efficiency', luminous_efficiency), - ('Spectral Distribution', 'Luminous Efficacy', luminous_efficacy), - ('CIE XYZ', 'Luminance', XYZ_to_luminance), - ('Luminance', 'Lightness', lightness), - ('Lightness', 'Luminance', luminance), - ('CIE XYZ', 'Whiteness', partial(whiteness, - XYZ_0=_TVS_DEFAULT_ILLUMINANT)), - ('CIE XYZ', 'Yellowness', yellowness), - ('CIE xy', 'Colorimetric Purity', - partial(colorimetric_purity, xy_n=_CCS_DEFAULT_ILLUMINANT)), - ('CIE xy', 'Complementary Wavelength', - partial(complementary_wavelength, xy_n=_CCS_DEFAULT_ILLUMINANT)), - ('CIE xy', 'Dominant Wavelength', - partial(dominant_wavelength, xy_n=_CCS_DEFAULT_ILLUMINANT)), - ('CIE xy', 'Excitation Purity', - partial(excitation_purity, xy_n=_CCS_DEFAULT_ILLUMINANT)), - ('Wavelength', 'CIE XYZ', wavelength_to_XYZ), + ("Spectral Distribution", "CIE XYZ", sd_to_XYZ), + ("CIE XYZ", "Spectral Distribution", XYZ_to_sd), + ("Spectral Distribution", "Luminous Flux", luminous_flux), + ("Spectral Distribution", "Luminous Efficiency", luminous_efficiency), + ("Spectral Distribution", "Luminous Efficacy", luminous_efficacy), + ("CIE XYZ", "Luminance", XYZ_to_luminance), + ("Luminance", "Lightness", lightness), + ("Lightness", "Luminance", luminance), + ( + "CIE XYZ", + "Whiteness", + partial(whiteness, XYZ_0=_TVS_ILLUMINANT_DEFAULT), + ), + ("CIE XYZ", "Yellowness", yellowness), + ( + "CIE xy", + "Colorimetric Purity", + partial(colorimetric_purity, xy_n=_CCS_ILLUMINANT_DEFAULT), + ), + ( + "CIE xy", + "Complementary Wavelength", + partial(complementary_wavelength, xy_n=_CCS_ILLUMINANT_DEFAULT), + ), + ( + "CIE xy", + "Dominant Wavelength", + partial(dominant_wavelength, xy_n=_CCS_ILLUMINANT_DEFAULT), + ), + ( + "CIE xy", + "Excitation Purity", + partial(excitation_purity, xy_n=_CCS_ILLUMINANT_DEFAULT), + ), + ("Wavelength", "CIE XYZ", wavelength_to_XYZ), # Colour Models - ('CIE XYZ', 'CIE xyY', XYZ_to_xyY), - ('CIE xyY', 'CIE XYZ', xyY_to_XYZ), - ('CIE xyY', 'CIE xy', xyY_to_xy), - ('CIE xy', 'CIE xyY', xy_to_xyY), - ('CIE XYZ', 'CIE xy', XYZ_to_xy), - ('CIE xy', 'CIE XYZ', xy_to_XYZ), - ('CIE XYZ', 'CIE Lab', XYZ_to_Lab), - ('CIE Lab', 'CIE XYZ', Lab_to_XYZ), - ('CIE Lab', 'CIE LCHab', Lab_to_LCHab), - ('CIE LCHab', 'CIE Lab', LCHab_to_Lab), - ('CIE XYZ', 'CIE Luv', XYZ_to_Luv), - ('CIE Luv', 'CIE XYZ', Luv_to_XYZ), - ('CIE Luv', 'CIE Luv uv', Luv_to_uv), - ('CIE Luv uv', 'CIE Luv', uv_to_Luv), - ('CIE Luv uv', 'CIE xy', Luv_uv_to_xy), - ('CIE xy', 'CIE Luv uv', xy_to_Luv_uv), - ('CIE Luv', 'CIE LCHuv', Luv_to_LCHuv), - ('CIE LCHuv', 'CIE Luv', LCHuv_to_Luv), - ('CIE XYZ', 'CIE UCS', XYZ_to_UCS), - ('CIE UCS', 'CIE XYZ', UCS_to_XYZ), - ('CIE UCS', 'CIE UCS uv', UCS_to_uv), - ('CIE UCS uv', 'CIE UCS', uv_to_UCS), - ('CIE UCS uv', 'CIE xy', UCS_uv_to_xy), - ('CIE xy', 'CIE UCS uv', xy_to_UCS_uv), - ('CIE XYZ', 'CIE UVW', XYZ_to_UVW), - ('CIE UVW', 'CIE XYZ', UVW_to_XYZ), - ('CIE Lab', 'DIN99', Lab_to_DIN99), - ('DIN99', 'CIE Lab', DIN99_to_Lab), - ('CIE XYZ', 'hdr CIELab', XYZ_to_hdr_CIELab), - ('hdr CIELab', 'CIE XYZ', hdr_CIELab_to_XYZ), - ('CIE XYZ', 'Hunter Lab', - partial( - XYZ_to_Hunter_Lab, - XYZ_n=TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] - ['D65'].XYZ_n / 100)), - ('Hunter Lab', 'CIE XYZ', - partial( - Hunter_Lab_to_XYZ, - XYZ_n=TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] - ['D65'].XYZ_n / 100)), - ('CIE XYZ', 'Hunter Rdab', - partial( - XYZ_to_Hunter_Rdab, - XYZ_n=TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] - ['D65'].XYZ_n / 100)), - ('Hunter Rdab', 'CIE XYZ', - partial( - Hunter_Rdab_to_XYZ, - XYZ_n=TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] - ['D65'].XYZ_n / 100)), - ('CIE XYZ', 'IGPGTG', XYZ_to_IGPGTG), - ('IGPGTG', 'CIE XYZ', IGPGTG_to_XYZ), - ('CIE XYZ', 'IPT', XYZ_to_IPT), - ('IPT', 'CIE XYZ', IPT_to_XYZ), - ('CIE XYZ', 'JzAzBz', XYZ_to_JzAzBz), - ('JzAzBz', 'CIE XYZ', JzAzBz_to_XYZ), - ('CIE XYZ', 'hdr IPT', XYZ_to_hdr_IPT), - ('hdr IPT', 'CIE XYZ', hdr_IPT_to_XYZ), - ('CIE XYZ', 'OSA UCS', XYZ_to_OSA_UCS), - ('OSA UCS', 'CIE XYZ', OSA_UCS_to_XYZ), + ("CIE XYZ", "CIE xyY", XYZ_to_xyY), + ("CIE xyY", "CIE XYZ", xyY_to_XYZ), + ("CIE xyY", "CIE xy", xyY_to_xy), + ("CIE xy", "CIE xyY", xy_to_xyY), + ("CIE XYZ", "CIE xy", XYZ_to_xy), + ("CIE xy", "CIE XYZ", xy_to_XYZ), + ("CIE XYZ", "CIE Lab", XYZ_to_Lab), + ("CIE Lab", "CIE XYZ", Lab_to_XYZ), + ("CIE Lab", "CIE LCHab", Lab_to_LCHab), + ("CIE LCHab", "CIE Lab", LCHab_to_Lab), + ("CIE XYZ", "CIE Luv", XYZ_to_Luv), + ("CIE Luv", "CIE XYZ", Luv_to_XYZ), + ("CIE Luv", "CIE Luv uv", Luv_to_uv), + ("CIE Luv uv", "CIE Luv", uv_to_Luv), + ("CIE Luv uv", "CIE xy", Luv_uv_to_xy), + ("CIE xy", "CIE Luv uv", xy_to_Luv_uv), + ("CIE Luv", "CIE LCHuv", Luv_to_LCHuv), + ("CIE LCHuv", "CIE Luv", LCHuv_to_Luv), + ("CIE XYZ", "CIE UCS", XYZ_to_UCS), + ("CIE UCS", "CIE XYZ", UCS_to_XYZ), + ("CIE UCS", "CIE UCS uv", UCS_to_uv), + ("CIE UCS uv", "CIE UCS", uv_to_UCS), + ("CIE UCS uv", "CIE xy", UCS_uv_to_xy), + ("CIE xy", "CIE UCS uv", xy_to_UCS_uv), + ("CIE XYZ", "CIE UVW", XYZ_to_UVW), + ("CIE UVW", "CIE XYZ", UVW_to_XYZ), + ("CIE XYZ", "DIN99", XYZ_to_DIN99), + ("DIN99", "CIE XYZ", DIN99_to_XYZ), + ("CIE XYZ", "hdr-CIELAB", XYZ_to_hdr_CIELab), + ("hdr-CIELAB", "CIE XYZ", hdr_CIELab_to_XYZ), + ( + "CIE XYZ", + "Hunter Lab", + partial( + XYZ_to_Hunter_Lab, + XYZ_n=TVS_ILLUMINANTS_HUNTERLAB[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"].XYZ_n + / 100, + ), + ), + ( + "Hunter Lab", + "CIE XYZ", + partial( + Hunter_Lab_to_XYZ, + XYZ_n=TVS_ILLUMINANTS_HUNTERLAB[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"].XYZ_n + / 100, + ), + ), + ( + "CIE XYZ", + "Hunter Rdab", + partial( + XYZ_to_Hunter_Rdab, + XYZ_n=TVS_ILLUMINANTS_HUNTERLAB[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"].XYZ_n + / 100, + ), + ), + ( + "Hunter Rdab", + "CIE XYZ", + partial( + Hunter_Rdab_to_XYZ, + XYZ_n=TVS_ILLUMINANTS_HUNTERLAB[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"].XYZ_n + / 100, + ), + ), + ("CIE XYZ", "ICaCb", XYZ_to_ICaCb), + ("ICaCb", "CIE XYZ", ICaCb_to_XYZ), + ("CIE XYZ", "ICtCp", XYZ_to_ICtCp), + ("ICtCp", "CIE XYZ", ICtCp_to_XYZ), + ("CIE XYZ", "IgPgTg", XYZ_to_IgPgTg), + ("IgPgTg", "CIE XYZ", IgPgTg_to_XYZ), + ("CIE XYZ", "IPT", XYZ_to_IPT), + ("IPT", "CIE XYZ", IPT_to_XYZ), + ("CIE XYZ", "Jzazbz", XYZ_to_Jzazbz), + ("Jzazbz", "CIE XYZ", Jzazbz_to_XYZ), + ("CIE XYZ", "hdr-IPT", XYZ_to_hdr_IPT), + ("hdr-IPT", "CIE XYZ", hdr_IPT_to_XYZ), + ("CIE XYZ", "OSA UCS", XYZ_to_OSA_UCS), + ("OSA UCS", "CIE XYZ", OSA_UCS_to_XYZ), + ("CIE XYZ", "Oklab", XYZ_to_Oklab), + ("Oklab", "CIE XYZ", Oklab_to_XYZ), + ("CIE XYZ", "ProLab", XYZ_to_ProLab), + ("ProLab", "CIE XYZ", ProLab_to_XYZ), # RGB Colour Models - ('CIE XYZ', 'RGB', - partial( - XYZ_to_RGB, - illuminant_XYZ=_RGB_COLOURSPACE_DEFAULT.whitepoint, - illuminant_RGB=_RGB_COLOURSPACE_DEFAULT.whitepoint, - matrix_XYZ_to_RGB=_RGB_COLOURSPACE_DEFAULT.matrix_XYZ_to_RGB)), - ('RGB', 'CIE XYZ', - partial( - RGB_to_XYZ, - illuminant_RGB=_RGB_COLOURSPACE_DEFAULT.whitepoint, - illuminant_XYZ=_RGB_COLOURSPACE_DEFAULT.whitepoint, - matrix_RGB_to_XYZ=_RGB_COLOURSPACE_DEFAULT.matrix_RGB_to_XYZ)), - ('RGB', 'Scene-Referred RGB', - partial( - RGB_to_RGB, - input_colourspace=_RGB_COLOURSPACE_DEFAULT, - output_colourspace=_RGB_COLOURSPACE_DEFAULT)), - ('Scene-Referred RGB', 'RGB', - partial( - RGB_to_RGB, - input_colourspace=_RGB_COLOURSPACE_DEFAULT, - output_colourspace=_RGB_COLOURSPACE_DEFAULT)), - ('RGB', 'HSV', RGB_to_HSV), - ('HSV', 'RGB', HSV_to_RGB), - ('RGB', 'HSL', RGB_to_HSL), - ('HSL', 'RGB', HSL_to_RGB), - ('CMY', 'RGB', CMY_to_RGB), - ('RGB', 'CMY', RGB_to_CMY), - ('CMY', 'CMYK', CMY_to_CMYK), - ('CMYK', 'CMY', CMYK_to_CMY), - ('RGB', 'RGB Luminance', - partial( - RGB_luminance, - primaries=_RGB_COLOURSPACE_DEFAULT.primaries, - whitepoint=_RGB_COLOURSPACE_DEFAULT.whitepoint)), - ('RGB Luminance', 'RGB', RGB_luminance_to_RGB), - ('RGB', 'ICTCP', RGB_to_ICTCP), - ('ICTCP', 'RGB', ICTCP_to_RGB), - ('RGB', 'Prismatic', RGB_to_Prismatic), - ('Prismatic', 'RGB', Prismatic_to_RGB), - ('Output-Referred RGB', 'YCbCr', RGB_to_YCbCr), - ('YCbCr', 'Output-Referred RGB', YCbCr_to_RGB), - ('RGB', 'YcCbcCrc', RGB_to_YcCbcCrc), - ('YcCbcCrc', 'RGB', YcCbcCrc_to_RGB), - ('Output-Referred RGB', 'YCoCg', RGB_to_YCoCg), - ('YCoCg', 'Output-Referred RGB', YCoCg_to_RGB), - ('RGB', 'Output-Referred RGB', cctf_encoding), - ('Output-Referred RGB', 'RGB', cctf_decoding), - ('Scene-Referred RGB', 'Output-Referred RGB', cctf_encoding), - ('Output-Referred RGB', 'Scene-Referred RGB', cctf_decoding), - ('CIE XYZ', 'sRGB', XYZ_to_sRGB), - ('sRGB', 'CIE XYZ', sRGB_to_XYZ), + ( + "CIE XYZ", + "RGB", + partial( + XYZ_to_RGB, + illuminant_XYZ=_RGB_COLOURSPACE_DEFAULT.whitepoint, + illuminant_RGB=_RGB_COLOURSPACE_DEFAULT.whitepoint, + matrix_XYZ_to_RGB=_RGB_COLOURSPACE_DEFAULT.matrix_XYZ_to_RGB, + ), + ), + ( + "RGB", + "CIE XYZ", + partial( + RGB_to_XYZ, + illuminant_RGB=_RGB_COLOURSPACE_DEFAULT.whitepoint, + illuminant_XYZ=_RGB_COLOURSPACE_DEFAULT.whitepoint, + matrix_RGB_to_XYZ=_RGB_COLOURSPACE_DEFAULT.matrix_RGB_to_XYZ, + ), + ), + ( + "RGB", + "Scene-Referred RGB", + partial( + RGB_to_RGB, + input_colourspace=_RGB_COLOURSPACE_DEFAULT, + output_colourspace=_RGB_COLOURSPACE_DEFAULT, + ), + ), + ( + "Scene-Referred RGB", + "RGB", + partial( + RGB_to_RGB, + input_colourspace=_RGB_COLOURSPACE_DEFAULT, + output_colourspace=_RGB_COLOURSPACE_DEFAULT, + ), + ), + ("RGB", "HSV", RGB_to_HSV), + ("HSV", "RGB", HSV_to_RGB), + ("RGB", "HSL", RGB_to_HSL), + ("HSL", "RGB", HSL_to_RGB), + ("RGB", "HCL", RGB_to_HCL), + ("HCL", "RGB", HCL_to_RGB), + ("RGB", "IHLS", RGB_to_IHLS), + ("IHLS", "RGB", IHLS_to_RGB), + ("CMY", "RGB", CMY_to_RGB), + ("RGB", "CMY", RGB_to_CMY), + ("CMY", "CMYK", CMY_to_CMYK), + ("CMYK", "CMY", CMYK_to_CMY), + ( + "RGB", + "RGB Luminance", + partial( + RGB_luminance, + primaries=_RGB_COLOURSPACE_DEFAULT.primaries, + whitepoint=_RGB_COLOURSPACE_DEFAULT.whitepoint, + ), + ), + ("RGB Luminance", "RGB", RGB_luminance_to_RGB), + ("RGB", "Prismatic", RGB_to_Prismatic), + ("Prismatic", "RGB", Prismatic_to_RGB), + ("Output-Referred RGB", "YCbCr", RGB_to_YCbCr), + ("YCbCr", "Output-Referred RGB", YCbCr_to_RGB), + ("RGB", "YcCbcCrc", RGB_to_YcCbcCrc), + ("YcCbcCrc", "RGB", YcCbcCrc_to_RGB), + ("Output-Referred RGB", "YCoCg", RGB_to_YCoCg), + ("YCoCg", "Output-Referred RGB", YCoCg_to_RGB), + ("RGB", "Output-Referred RGB", cctf_encoding), + ("Output-Referred RGB", "RGB", cctf_decoding), + ("Scene-Referred RGB", "Output-Referred RGB", cctf_encoding), + ("Output-Referred RGB", "Scene-Referred RGB", cctf_decoding), + ("CIE XYZ", "sRGB", XYZ_to_sRGB), + ("sRGB", "CIE XYZ", sRGB_to_XYZ), # Colour Notation Systems - ('Output-Referred RGB', 'Hexadecimal', RGB_to_HEX), - ('Hexadecimal', 'Output-Referred RGB', HEX_to_RGB), - ('CIE xyY', 'Munsell Colour', xyY_to_munsell_colour), - ('Munsell Colour', 'CIE xyY', munsell_colour_to_xyY), - ('Luminance', 'Munsell Value', munsell_value), - ('Munsell Value', 'Luminance', partial(luminance, method='ASTM D1535')), + ("Output-Referred RGB", "Hexadecimal", RGB_to_HEX), + ("Hexadecimal", "Output-Referred RGB", HEX_to_RGB), + ("CIE xyY", "Munsell Colour", xyY_to_munsell_colour), + ("Munsell Colour", "CIE xyY", munsell_colour_to_xyY), + ("Luminance", "Munsell Value", munsell_value), + ("Munsell Value", "Luminance", partial(luminance, method="ASTM D1535")), # Colour Quality - ('Spectral Distribution', 'CRI', colour_rendering_index), - ('Spectral Distribution', 'CQS', colour_quality_scale), + ("Spectral Distribution", "CRI", colour_rendering_index), + ("Spectral Distribution", "CQS", colour_quality_scale), # Colour Temperature - ('CCT', 'CIE UCS uv', CCT_to_uv), - ('CCT', 'CIE xy', CCT_to_xy), - ('CIE UCS uv', 'CCT', uv_to_CCT), - ('CIE xy', 'CCT', xy_to_CCT), + ("CCT", "CIE UCS uv", CCT_to_uv), + ("CIE UCS uv", "CCT", uv_to_CCT), # Advanced Colorimetry - ('CIE XYZ', 'Hunt', - partial( - XYZ_to_Hunt, - XYZ_w=_TVS_DEFAULT_ILLUMINANT, - XYZ_b=_TVS_DEFAULT_ILLUMINANT, - L_A=80 * 0.2, - CCT_w=6504)), - ('CIE XYZ', 'ATD95', - partial( - XYZ_to_ATD95, - XYZ_0=_TVS_DEFAULT_ILLUMINANT, - Y_0=80 * 0.2, - k_1=0, - k_2=(15 + 50) / 2)), - ('CIE XYZ', 'CIECAM02', - partial( - XYZ_to_CIECAM02, - XYZ_w=_TVS_DEFAULT_ILLUMINANT, - L_A=64 / np.pi * 0.2, - Y_b=20)), - ('CIECAM02', 'CIE XYZ', - partial( - CIECAM02_to_XYZ, - XYZ_w=_TVS_DEFAULT_ILLUMINANT, - L_A=64 / np.pi * 0.2, - Y_b=20)), - ('CIECAM02', 'CIECAM02 JMh', CIECAM02_to_JMh_CIECAM02), - ('CIECAM02 JMh', 'CIECAM02', JMh_CIECAM02_to_CIECAM02), - ('CIE XYZ', 'CAM16', - partial( - XYZ_to_CAM16, - XYZ_w=_TVS_DEFAULT_ILLUMINANT, - L_A=64 / np.pi * 0.2, - Y_b=20)), - ('CAM16', 'CIE XYZ', - partial( - CAM16_to_XYZ, - XYZ_w=_TVS_DEFAULT_ILLUMINANT, - L_A=64 / np.pi * 0.2, - Y_b=20)), - ('CAM16', 'CAM16 JMh', CAM16_to_JMh_CAM16), - ('CAM16 JMh', 'CAM16', JMh_CAM16_to_CAM16), - ('CIE XYZ', 'LLAB', - partial(XYZ_to_LLAB, XYZ_0=_TVS_DEFAULT_ILLUMINANT, Y_b=80 * 0.2, L=80)), - ('CIE XYZ', 'Nayatani95', - partial( - XYZ_to_Nayatani95, - XYZ_n=_TVS_DEFAULT_ILLUMINANT, - Y_o=0.2, - E_o=1000, - E_or=1000)), - ('CIE XYZ', 'RLAB', - partial(XYZ_to_RLAB, XYZ_n=_TVS_DEFAULT_ILLUMINANT, Y_n=20)), - ('CIECAM02 JMh', 'CAM02LCD', JMh_CIECAM02_to_CAM02LCD), - ('CAM02LCD', 'CIECAM02 JMh', CAM02LCD_to_JMh_CIECAM02), - ('CIECAM02 JMh', 'CAM02SCD', JMh_CIECAM02_to_CAM02SCD), - ('CAM02SCD', 'CIECAM02 JMh', CAM02SCD_to_JMh_CIECAM02), - ('CIECAM02 JMh', 'CAM02UCS', JMh_CIECAM02_to_CAM02UCS), - ('CAM02UCS', 'CIECAM02 JMh', CAM02UCS_to_JMh_CIECAM02), - ('CAM16 JMh', 'CAM16LCD', JMh_CAM16_to_CAM16LCD), - ('CAM16LCD', 'CAM16 JMh', CAM16LCD_to_JMh_CAM16), - ('CAM16 JMh', 'CAM16SCD', JMh_CAM16_to_CAM16SCD), - ('CAM16SCD', 'CAM16 JMh', CAM16SCD_to_JMh_CAM16), - ('CAM16 JMh', 'CAM16UCS', JMh_CAM16_to_CAM16UCS), - ('CAM16UCS', 'CAM16 JMh', CAM16UCS_to_JMh_CAM16), + ( + "CIE XYZ", + "ATD95", + partial( + XYZ_to_ATD95, + XYZ_0=_TVS_ILLUMINANT_DEFAULT, + Y_0=80 * 0.2, + k_1=0, + k_2=(15 + 50) / 2, + ), + ), + ( + "CIE XYZ", + "CIECAM02", + partial(XYZ_to_CIECAM02, **_CAM_KWARGS_CIECAM02_sRGB), + ), + ( + "CIECAM02", + "CIE XYZ", + partial(CIECAM02_to_XYZ, **_CAM_KWARGS_CIECAM02_sRGB), + ), + ("CIECAM02", "CIECAM02 JMh", CIECAM02_to_JMh_CIECAM02), + ("CIECAM02 JMh", "CIECAM02", JMh_CIECAM02_to_CIECAM02), + ("CIE XYZ", "CAM16", partial(XYZ_to_CAM16, **_CAM_KWARGS_CIECAM02_sRGB)), + ("CAM16", "CIE XYZ", partial(CAM16_to_XYZ, **_CAM_KWARGS_CIECAM02_sRGB)), + ("CAM16", "CAM16 JMh", CAM16_to_JMh_CAM16), + ("CAM16 JMh", "CAM16", JMh_CAM16_to_CAM16), + ( + "CIE XYZ", + "Kim 2009", + partial(XYZ_to_Kim2009, XYZ_w=_TVS_ILLUMINANT_DEFAULT, L_A=80 * 0.2), + ), + ( + "Kim 2009", + "CIE XYZ", + partial(Kim2009_to_XYZ, XYZ_w=_TVS_ILLUMINANT_DEFAULT, L_A=80 * 0.2), + ), + ( + "CIE XYZ", + "Hunt", + partial( + XYZ_to_Hunt, + XYZ_w=_TVS_ILLUMINANT_DEFAULT, + XYZ_b=_TVS_ILLUMINANT_DEFAULT, + L_A=80 * 0.2, + CCT_w=6504, + ), + ), + ( + "CIE XYZ", + "LLAB", + partial( + XYZ_to_LLAB, XYZ_0=_TVS_ILLUMINANT_DEFAULT, Y_b=80 * 0.2, L=80 + ), + ), + ( + "CIE XYZ", + "Nayatani95", + partial( + XYZ_to_Nayatani95, + XYZ_n=_TVS_ILLUMINANT_DEFAULT, + Y_o=0.2, + E_o=1000, + E_or=1000, + ), + ), + ( + "CIE XYZ", + "RLAB", + partial(XYZ_to_RLAB, XYZ_n=_TVS_ILLUMINANT_DEFAULT, Y_n=20), + ), + ( + "CIE XYZ", + "ZCAM", + partial( + XYZ_to_ZCAM, + XYZ_w=_TVS_ILLUMINANT_DEFAULT, + L_A=64 / np.pi * 0.2, + Y_b=20, + ), + ), + ( + "ZCAM", + "CIE XYZ", + partial( + ZCAM_to_XYZ, + XYZ_w=_TVS_ILLUMINANT_DEFAULT, + L_A=64 / np.pi * 0.2, + Y_b=20, + ), + ), + ("CIECAM02 JMh", "CAM02LCD", JMh_CIECAM02_to_CAM02LCD), + ("CAM02LCD", "CIECAM02 JMh", CAM02LCD_to_JMh_CIECAM02), + ("CIECAM02 JMh", "CAM02SCD", JMh_CIECAM02_to_CAM02SCD), + ("CAM02SCD", "CIECAM02 JMh", CAM02SCD_to_JMh_CIECAM02), + ("CIECAM02 JMh", "CAM02UCS", JMh_CIECAM02_to_CAM02UCS), + ("CAM02UCS", "CIECAM02 JMh", CAM02UCS_to_JMh_CIECAM02), + ("CAM16 JMh", "CAM16LCD", JMh_CAM16_to_CAM16LCD), + ("CAM16LCD", "CAM16 JMh", CAM16LCD_to_JMh_CAM16), + ("CAM16 JMh", "CAM16SCD", JMh_CAM16_to_CAM16SCD), + ("CAM16SCD", "CAM16 JMh", CAM16SCD_to_JMh_CAM16), + ("CAM16 JMh", "CAM16UCS", JMh_CAM16_to_CAM16UCS), + ("CAM16UCS", "CAM16 JMh", CAM16UCS_to_JMh_CAM16), ] """ Automatic colour conversion graph specifications data describing two nodes and the edge in the graph. - -CONVERSION_SPECIFICATIONS_DATA : list """ -CONVERSION_SPECIFICATIONS = [ +CONVERSION_SPECIFICATIONS: List = [ Conversion_Specification(*specification) for specification in CONVERSION_SPECIFICATIONS_DATA ] """ Automatic colour conversion graph specifications describing two nodes and the edge in the graph. - -CONVERSION_SPECIFICATIONS : list """ -CONVERSION_GRAPH_NODE_LABELS = { +CONVERSION_GRAPH_NODE_LABELS: Dict = { specification[0].lower(): specification[0] for specification in CONVERSION_SPECIFICATIONS_DATA } -""" -Automatic colour conversion graph node labels. +"""Automatic colour conversion graph node labels.""" -CONVERSION_GRAPH_NODE_LABELS : dict -""" - -CONVERSION_GRAPH_NODE_LABELS.update({ - specification[1].lower(): specification[1] - for specification in CONVERSION_SPECIFICATIONS_DATA -}) +CONVERSION_GRAPH_NODE_LABELS.update( + { + specification[1].lower(): specification[1] + for specification in CONVERSION_SPECIFICATIONS_DATA + } +) -@required('NetworkX') -def _build_graph(): +@required("NetworkX") +def _build_graph() -> networkx.DiGraph: # type: ignore[name-defined] # noqa """ - Builds the automatic colour conversion graph. + Build the automatic colour conversion graph. Returns ------- - DiGraph + :class:`networkx.DiGraph` Automatic colour conversion graph. """ @@ -573,35 +850,34 @@ def _build_graph(): graph.add_edge( specification.source, specification.target, - conversion_function=specification.conversion_function) + conversion_function=specification.conversion_function, + ) return graph -CONVERSION_GRAPH = None -""" -Automatic colour conversion graph. +CONVERSION_GRAPH: ( # type: ignore[name-defined] + Optional[networkx.DiGraph] # noqa +) = None +"""Automatic colour conversion graph.""" -CONVERSION_GRAPH : DiGraph -""" - -@required('NetworkX') -def _conversion_path(source, target): +@required("NetworkX") +def _conversion_path(source: str, target: str) -> List[Callable]: """ - Returns the conversion path from the source node to the target node in the + Return the conversion path from the source node to the target node in the automatic colour conversion graph. Parameters ---------- - source : unicode + source Source node. - target : unicode + target Target node. Returns ------- - list + :class:`list` Conversion path from the source node to the target node, i.e. a list of conversion function callables. @@ -610,10 +886,9 @@ def _conversion_path(source, target): >>> _conversion_path('cie lab', 'cct') ... # doctest: +ELLIPSIS [, , \ -] +, ] """ - import colour import networkx as nx global CONVERSION_GRAPH @@ -625,24 +900,24 @@ def _conversion_path(source, target): path = nx.shortest_path(CONVERSION_GRAPH, source, target) return [ - CONVERSION_GRAPH.get_edge_data(a, b)['conversion_function'] + CONVERSION_GRAPH.get_edge_data(a, b)["conversion_function"] for a, b in zip(path[:-1], path[1:]) ] -def _lower_order_function(callable_): +def _lower_order_function(callable_: Callable) -> Callable: """ - Returns the lower order function associated with given callable, i.e. + Return the lower order function associated with given callable, i.e. the function wrapped by a partial object. Parameters ---------- - callable_ : callable + callable_ Callable to return the lower order function. Returns ------- - callable + Callable Lower order function or given callable if no lower order function exists. """ @@ -650,42 +925,43 @@ def _lower_order_function(callable_): return callable_.func if isinstance(callable_, partial) else callable_ -def describe_conversion_path(source, - target, - mode='Short', - width=79, - padding=3, - print_callable=print, - **kwargs): +def describe_conversion_path( + source: str, + target: str, + mode: Union[Literal["Short", "Long", "Extended"], str] = "Short", + width: Integer = 79, + padding: Integer = 3, + print_callable: Callable = print, + **kwargs: Any, +): """ - Describes the conversion path from source colour representation to target + Describe the conversion path from source colour representation to target colour representation using the automatic colour conversion graph. Parameters ---------- - source : unicode + source Source colour representation, i.e. the source node in the automatic colour conversion graph. - target : unicode + target Target colour representation, i.e. the target node in the automatic colour conversion graph. - mode : unicode, optional - **{'Short', 'Long', 'Extended'}**, + mode Verbose mode: *Short* describes the conversion path, *Long* provides details about the arguments, definitions signatures and output values, - *Extended* appends the definitions documentation. - width : int, optional + *Extended* appends the definitions' documentation. + width Message box width. - padding : unicode, optional - Padding on each sides of the message. - print_callable : callable, optional + padding + Padding on each side of the message. + print_callable Callable used to print the message box. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.convert`}, - Please refer to the documentation of the previously listed definition. + See the documentation of the previously listed definition. Examples -------- @@ -702,23 +978,37 @@ def describe_conversion_path(source, try: # pragma: no cover signature_inspection = inspect.signature except AttributeError: # pragma: no cover - signature_inspection = inspect.getargspec + signature_inspection = inspect.getargspec # type: ignore[assignment] + + source, target = source.lower(), target.lower() + mode = validate_method( + mode, + ["Short", "Long", "Extended"], + '"{0}" mode is invalid, it must be one of {1}!', + ) - source, target, mode = source.lower(), target.lower(), mode.lower() - width = (79 + 2 + 2 * 3 - 4) if mode == 'extended' else width + width = (79 + 2 + 2 * 3 - 4) if mode == "extended" else width conversion_path = _conversion_path(source, target) message_box( - '[ Conversion Path ]\n\n{0}'.format(' --> '.join([ - '"{0}"'.format( - _lower_order_function(conversion_function).__name__) - for conversion_function in conversion_path - ])), width, padding, print_callable) + "[ Conversion Path ]\n\n{}".format( + " --> ".join( + [ + f'"{_lower_order_function(conversion_function).__name__}"' + for conversion_function in conversion_path + ] + ) + ), + width, + padding, + print_callable, + ) for conversion_function in conversion_path: conversion_function_name = _lower_order_function( - conversion_function).__name__ + conversion_function + ).__name__ # Filtering compatible keyword arguments passed directly and # irrespective of any conversion function name. @@ -728,39 +1018,40 @@ def describe_conversion_path(source, # conversion function name. filtered_kwargs.update(kwargs.get(conversion_function_name, {})) - return_value = filtered_kwargs.pop('return', None) + return_value = filtered_kwargs.pop("return", None) - if mode in ('long', 'extended'): + if mode in ("long", "extended"): + signature = pformat( + signature_inspection( + _lower_order_function(conversion_function) + ) + ) message = ( - '[ "{0}" ]' - '\n\n[ Signature ]\n\n{1}').format( - _lower_order_function(conversion_function).__name__, - pformat( - signature_inspection( - _lower_order_function(conversion_function)))) + f'[ "{_lower_order_function(conversion_function).__name__}" ]\n\n' + f"[ Signature ]\n\n" + f"{signature}" + ) if filtered_kwargs: - message += '\n\n[ Filtered Arguments ]\n\n{0}'.format( - pformat(filtered_kwargs)) + message += ( + f"\n\n[ Filtered Arguments ]\n\n{pformat(filtered_kwargs)}" + ) - if mode in ('extended', ): - message += '\n\n[ Documentation ]\n\n{0}'.format( - textwrap.dedent( - str( - _lower_order_function(conversion_function) - .__doc__)).strip()) + if mode in ("extended",): + docstring = textwrap.dedent( + str(_lower_order_function(conversion_function).__doc__) + ).strip() + message += f"\n\n[ Documentation ]\n\n {docstring}" if return_value is not None: - message += '\n\n[ Conversion Output ]\n\n{0}'.format( - return_value) + message += f"\n\n[ Conversion Output ]\n\n{return_value}" message_box(message, width, padding, print_callable) -@domain_range_scale('1') -def convert(a, source, target, **kwargs): +def convert(a: Any, source: str, target: str, **kwargs: Any) -> Any: """ - Converts given object :math:`a` from source colour representation to target + Convert given object :math:`a` from source colour representation to target colour representation using the automatic colour conversion graph. The conversion is performed by finding the shortest path in a @@ -785,23 +1076,22 @@ def convert(a, source, target, **kwargs): Parameters ---------- - a : array_like or numeric or SpectralDistribution + a Object :math:`a` to convert. If :math:`a` represents a reflectance, transmittance or absorptance value, the expectation is that it is viewed under *CIE Standard Illuminant D Series* *D65*. The illuminant - can be changed on a per definition basis along the conversion path. - source : unicode + can be changed on a per-definition basis along the conversion path. + source Source colour representation, i.e. the source node in the automatic colour conversion graph. - target : unicode + target Target colour representation, i.e. the target node in the automatic colour conversion graph. Other Parameters ---------------- - \\**kwargs : dict, optional - {'\\*'}, - Please refer to the documentation of the supported conversion + kwargs + See the documentation of the supported conversion definitions. Arguments for the conversion definitions are passed as keyword @@ -815,12 +1105,8 @@ def convert(a, source, target, **kwargs): It is also possible to pass keyword arguments directly to the various conversion definitions irrespective of their name. This is - ``dangerous`` and could cause unexpected behaviour because of - unavoidable discrepancies with the underlying - :func:`colour.utilities.filter_kwargs` definition between Python 2.7 - and 3.x. Using this direct keyword arguments passing mechanism might - also ends up passing incompatible arguments to a given conversion - definition. Consider the following conversion:: + ``dangerous`` and could cause unexpected behaviour, consider the + following conversion:: convert(sd, 'Spectral Distribution', 'sRGB', 'illuminant': \ SDS_ILLUMINANTS['FL2']) @@ -870,7 +1156,7 @@ def convert(a, source, target, **kwargs): Returns ------- - ndarray or numeric or SpectralDistribution + Any Converted object :math:`a`. Warnings @@ -884,7 +1170,7 @@ def convert(a, source, target, **kwargs): representation. To encode such *RGB* values as *output-referred* (*display-referred*) imagery, i.e. encode the *RGB* values using an encoding colour component transfer function (Encoding CCTF) / - opto-electronic transfer function (OETF / OECF), the + opto-electronic transfer function (OETF), the **Output-Referred RGB** representation must be used:: convert(RGB, 'Scene-Referred RGB', 'Output-Referred RGB') @@ -899,7 +1185,7 @@ def convert(a, source, target, **kwargs): - The default illuminant for the computation is *CIE Standard Illuminant D Series* *D65*. It can be changed on a - per definition basis along the conversion path. + per-definition basis along the conversion path. - The default *RGB* colourspace primaries and whitepoint are that of the *BT.709*/*sRGB* colourspace. They can be changed on a per definition basis along the conversion path. @@ -909,12 +1195,13 @@ def convert(a, source, target, **kwargs): Thus, decoding and encoding using the sRGB electro-optical transfer function (EOTF) and its inverse will be applied by default. - Most of the colour appearance models have defaults set according to - *IEC 61966-2-1:1999* viewing conditions, i.e. *sRGB* 64 Lux ambiant + *IEC 61966-2-1:1999* viewing conditions, i.e. *sRGB* 64 Lux ambient illumination, 80 :math:`cd/m^2`, adapting field luminance about 20% of a white object in the scene. Examples -------- + >>> import numpy as np >>> from colour import SDS_COLOURCHECKERS >>> sd = SDS_COLOURCHECKERS['ColorChecker N Ohta']['dark skin'] >>> convert(sd, 'Spectral Distribution', 'sRGB', @@ -955,12 +1242,13 @@ def convert(a, source, target, **kwargs): # conversion graph implementation is considered stable. usage_warning( 'The "Automatic Colour Conversion Graph" is a beta feature, be ' - 'mindful of this when using it. Please report any unexpected ' - 'behaviour and do not hesitate to ask any questions should they arise.' - '\nThis warning can be disabled with the ' + "mindful of this when using it. Please report any unexpected " + "behaviour and do not hesitate to ask any questions should they arise." + "\nThis warning can be disabled with the " '"colour.utilities.suppress_warnings" context manager as follows:\n' - 'with colour.utilities.suppress_warnings(colour_usage_warnings=True): ' - '\n convert(*args, **kwargs)') + "with colour.utilities.suppress_warnings(colour_usage_warnings=True): " + "\n convert(*args, **kwargs)" + ) source, target = source.lower(), target.lower() @@ -969,7 +1257,8 @@ def convert(a, source, target, **kwargs): verbose_kwargs = copy(kwargs) for conversion_function in conversion_path: conversion_function_name = _lower_order_function( - conversion_function).__name__ + conversion_function + ).__name__ # Filtering compatible keyword arguments passed directly and # irrespective of any conversion function name. @@ -979,15 +1268,16 @@ def convert(a, source, target, **kwargs): # conversion function name. filtered_kwargs.update(kwargs.get(conversion_function_name, {})) - a = conversion_function(a, **filtered_kwargs) + with domain_range_scale("1"): + a = conversion_function(a, **filtered_kwargs) if conversion_function_name in verbose_kwargs: - verbose_kwargs[conversion_function_name]['return'] = a + verbose_kwargs[conversion_function_name]["return"] = a else: - verbose_kwargs[conversion_function_name] = {'return': a} + verbose_kwargs[conversion_function_name] = {"return": a} - if 'verbose' in verbose_kwargs: - verbose_kwargs.update(verbose_kwargs.pop('verbose')) + if "verbose" in verbose_kwargs: + verbose_kwargs.update(verbose_kwargs.pop("verbose")) describe_conversion_path(source, target, **verbose_kwargs) return a diff --git a/colour/graph/tests/__init__.py b/colour/graph/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/graph/tests/__init__.py +++ b/colour/graph/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/graph/tests/test_conversion.py b/colour/graph/tests/test_conversion.py index 7f9050d7b4..00f7107173 100644 --- a/colour/graph/tests/test_conversion.py +++ b/colour/graph/tests/test_conversion.py @@ -1,134 +1,161 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.graph.conversion` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.graph.conversion` module.""" import numpy as np -import six import unittest from colour.characterisation import SDS_COLOURCHECKERS from colour.colorimetry import CCS_ILLUMINANTS, SDS_ILLUMINANTS -from colour.models import RGB_COLOURSPACE_ACES2065_1 +from colour.models import COLOURSPACE_MODELS, RGB_COLOURSPACE_ACES2065_1 from colour.graph import describe_conversion_path, convert -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestDescribeConversionPath', 'TestConvert'] +__all__ = [ + "TestDescribeConversionPath", + "TestConvert", +] class TestDescribeConversionPath(unittest.TestCase): """ - Defines :func:`colour.graph.conversion.describe_conversion_path` definition + Define :func:`colour.graph.conversion.describe_conversion_path` definition unit tests methods. """ def test_describe_conversion_path(self): """ - Tests :func:`colour.graph.conversion.describe_conversion_path` + Test :func:`colour.graph.conversion.describe_conversion_path` definition. """ - describe_conversion_path('Spectral Distribution', 'sRGB') + describe_conversion_path("Spectral Distribution", "sRGB") - describe_conversion_path('Spectral Distribution', 'sRGB', mode='Long') + describe_conversion_path("Spectral Distribution", "sRGB", mode="Long") describe_conversion_path( - 'Spectral Distribution', - 'sRGB', - mode='Extended', + "Spectral Distribution", + "sRGB", + mode="Extended", sd_to_XYZ={ - 'illuminant': SDS_ILLUMINANTS['FL2'], - 'return': np.array([0.47924575, 0.31676968, 0.17362725]) - }) + "illuminant": SDS_ILLUMINANTS["FL2"], + "return": np.array([0.47924575, 0.31676968, 0.17362725]), + }, + ) class TestConvert(unittest.TestCase): """ - Defines :func:`colour.graph.conversion.convert` definition unit tests + Define :func:`colour.graph.conversion.convert` definition unit tests methods. """ def test_convert(self): - """ - Tests :func:`colour.graph.conversion.convert` definition. - """ + """Test :func:`colour.graph.conversion.convert` definition.""" - RGB_a = convert(SDS_COLOURCHECKERS['ColorChecker N Ohta']['dark skin'], - 'Spectral Distribution', 'sRGB') + RGB_a = convert( + SDS_COLOURCHECKERS["ColorChecker N Ohta"]["dark skin"], + "Spectral Distribution", + "sRGB", + ) np.testing.assert_almost_equal( - RGB_a, np.array([0.45675795, 0.30986982, 0.24861924]), decimal=7) + RGB_a, np.array([0.45675795, 0.30986982, 0.24861924]), decimal=7 + ) - Jpapbp = convert(RGB_a, 'Output-Referred RGB', 'CAM16UCS') + Jpapbp = convert(RGB_a, "Output-Referred RGB", "CAM16UCS") np.testing.assert_almost_equal( - Jpapbp, np.array([0.39994810, 0.09206557, 0.08127526]), decimal=7) + Jpapbp, np.array([0.39994810, 0.09206557, 0.08127526]), decimal=7 + ) RGB_b = convert( - Jpapbp, 'CAM16UCS', 'sRGB', verbose={'mode': 'Extended'}) + Jpapbp, "CAM16UCS", "sRGB", verbose={"mode": "Extended"} + ) # NOTE: The "CIE XYZ" tristimulus values to "sRGB" matrix is given # rounded at 4 decimals as per "IEC 61966-2-1:1999" and thus preventing # exact roundtrip. np.testing.assert_allclose(RGB_a, RGB_b, rtol=1e-5, atol=1e-5) np.testing.assert_almost_equal( - convert('#808080', 'Hexadecimal', 'Scene-Referred RGB'), + convert("#808080", "Hexadecimal", "Scene-Referred RGB"), np.array([0.21586050, 0.21586050, 0.21586050]), - decimal=7) + decimal=7, + ) self.assertAlmostEqual( - convert('#808080', 'Hexadecimal', 'RGB Luminance'), + convert("#808080", "Hexadecimal", "RGB Luminance"), 0.21586050, - places=7) + places=7, + ) np.testing.assert_almost_equal( convert( convert( - np.array([0.5, 0.5, 0.5]), 'Output-Referred RGB', - 'Scene-Referred RGB'), 'RGB', 'YCbCr'), + np.array([0.5, 0.5, 0.5]), + "Output-Referred RGB", + "Scene-Referred RGB", + ), + "RGB", + "YCbCr", + ), np.array([0.49215686, 0.50196078, 0.50196078]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( convert( RGB_a, - 'RGB', - 'Scene-Referred RGB', - RGB_to_RGB={'output_colourspace': RGB_COLOURSPACE_ACES2065_1}), + "RGB", + "Scene-Referred RGB", + RGB_to_RGB={"output_colourspace": RGB_COLOURSPACE_ACES2065_1}, + ), np.array([0.36364180, 0.31715308, 0.25888531]), - decimal=7) + decimal=7, + ) + + # Consistency check to verify that all the colour models are properly + # named in the graph: + for model in COLOURSPACE_MODELS: + convert( + np.array([0.20654008, 0.12197225, 0.05136952]), + "CIE XYZ", + model, + ) def test_convert_direct_keyword_argument_passing(self): """ - Tests :func:`colour.graph.conversion.convert` definition behaviour when + Test :func:`colour.graph.conversion.convert` definition behaviour when direct keyword arguments are passed. """ a = np.array([0.20654008, 0.12197225, 0.05136952]) - illuminant = CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'][ - 'D50'] + illuminant = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][ + "D50" + ] np.testing.assert_almost_equal( convert( - a, 'CIE XYZ', 'CIE xyY', - XYZ_to_xyY={'illuminant': illuminant}), - convert(a, 'CIE XYZ', 'CIE xyY', illuminant=illuminant), - decimal=7) - - if six.PY3: # pragma: no cover - # Illuminant "ndarray" is converted to tuple here so that it can - # be hashed by the "sd_to_XYZ" definition, this should never occur - # in practical application. - self.assertRaises(AttributeError, lambda: convert( - SDS_COLOURCHECKERS['ColorChecker N Ohta']['dark skin'], - 'Spectral Distribution', 'sRGB', - illuminant=tuple(illuminant))) - - -if __name__ == '__main__': + a, "CIE XYZ", "CIE xyY", XYZ_to_xyY={"illuminant": illuminant} + ), + convert(a, "CIE XYZ", "CIE xyY", illuminant=illuminant), + decimal=7, + ) + + # Illuminant "ndarray" is converted to tuple here so that it can + # be hashed by the "sd_to_XYZ" definition, this should never occur + # in practical application. + self.assertRaises( + AttributeError, + lambda: convert( + SDS_COLOURCHECKERS["ColorChecker N Ohta"]["dark skin"], + "Spectral Distribution", + "sRGB", + illuminant=tuple(illuminant), + ), + ) + + +if __name__ == "__main__": unittest.main() diff --git a/colour/hints/__init__.py b/colour/hints/__init__.py new file mode 100644 index 0000000000..65201b1a40 --- /dev/null +++ b/colour/hints/__init__.py @@ -0,0 +1,301 @@ +""" +Annotation Type Hints +===================== + +Defines the annotation type hints, the module exposes many aliases from +:mod:`typing` and :mod:`numpy.typing` to avoid having to handle multiple +imports. +""" + +from __future__ import annotations + +import numpy as np +import numpy.typing as npt +import re +from types import ModuleType +from typing import ( + Any, + Callable, + Dict, + Generator, + Iterable, + Iterator, + List, + Mapping, + NewType, + Optional, + Union, + Sequence, + TextIO, + Tuple, + TYPE_CHECKING, + Type, + TypeVar, + cast, +) +from typing_extensions import runtime_checkable + +try: + from typing import ( + Literal, + Protocol, + SupportsIndex, + TypedDict, + ) +# TODO: Drop "typing_extensions" when "Google Colab" uses Python >= 3.8. +except ImportError: # pragma: no cover + from typing_extensions import ( # type: ignore[misc] + Literal, + Protocol, + SupportsIndex, + TypedDict, + ) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "Any", + "Callable", + "Dict", + "Generator", + "Iterable", + "Iterator", + "List", + "Mapping", + "ModuleType", + "Optional", + "Union", + "Sequence", + "SupportsIndex", + "TextIO", + "Tuple", + "Type", + "TypedDict", + "TypeVar", + "RegexFlag", + "DTypeBoolean", + "DTypeInteger", + "DTypeFloating", + "DTypeNumber", + "DTypeComplex", + "DType", + "Integer", + "Floating", + "Number", + "Complex", + "Boolean", + "Literal", + "Dataclass", + "NestedSequence", + "ArrayLike", + "IntegerOrArrayLike", + "FloatingOrArrayLike", + "NumberOrArrayLike", + "ComplexOrArrayLike", + "BooleanOrArrayLike", + "ScalarType", + "StrOrArrayLike", + "NDArray", + "IntegerOrNDArray", + "FloatingOrNDArray", + "NumberOrNDArray", + "ComplexOrNDArray", + "BooleanOrNDArray", + "StrOrNDArray", + "TypeInterpolator", + "TypeExtrapolator", + "TypeLUTSequenceItem", + "LiteralWarning", + "cast", +] + +Any = Any +Callable = Callable +Dict = Dict +Generator = Generator +Iterable = Iterable +Iterator = Iterator +List = List +Mapping = Mapping +ModuleType = ModuleType +Optional = Optional +Union = Union +Sequence = Sequence +SupportsIndex = SupportsIndex +TextIO = TextIO +Tuple = Tuple +Type = Type +TypedDict = TypedDict +TypeVar = TypeVar + +RegexFlag = NewType("RegexFlag", re.RegexFlag) + +DTypeInteger = Union[ + np.int8, + np.int16, + np.int32, + np.int64, + np.uint8, + np.uint16, + np.uint32, + np.uint64, +] +DTypeFloating = Union[np.float16, np.float32, np.float64] +DTypeNumber = Union[DTypeInteger, DTypeFloating] +DTypeComplex = Union[np.csingle, np.cdouble] +DTypeBoolean = np.bool_ +DType = Union[DTypeBoolean, DTypeNumber, DTypeComplex] + +Integer = int +Floating = float +Number = Union[Integer, Floating] +Complex = complex +Boolean = bool + +# TODO: Use "typing.Literal" when minimal Python version is raised to 3.8. +Literal = Literal + +# TODO: Revisit to use Protocol. +Dataclass = Any + +NestedSequence = npt._NestedSequence +ArrayLike = npt.ArrayLike + +IntegerOrArrayLike = Union[Integer, ArrayLike] +FloatingOrArrayLike = Union[Floating, ArrayLike] +NumberOrArrayLike = Union[Number, ArrayLike] +ComplexOrArrayLike = Union[Complex, ArrayLike] + +BooleanOrArrayLike = Union[Boolean, ArrayLike] + +StrOrArrayLike = Union[str, ArrayLike] + +ScalarType = TypeVar("ScalarType", bound=np.generic, covariant=True) + +# TODO: Use "numpy.typing.NDArray" when minimal Numpy version is raised to +# 1.21. +if TYPE_CHECKING: # pragma: no cover + NDArray = np.ndarray[Any, np.dtype[ScalarType]] +else: + NDArray = np.ndarray + +# TODO: Drop when minimal Python is raised to 3.9. +if TYPE_CHECKING: # pragma: no cover + IntegerOrNDArray = Union[Integer, NDArray[DTypeInteger]] + FloatingOrNDArray = Union[Floating, NDArray[DTypeFloating]] + NumberOrNDArray = Union[ + Number, NDArray[Union[DTypeInteger, DTypeFloating]] + ] + ComplexOrNDArray = Union[Complex, NDArray[DTypeComplex]] + + BooleanOrNDArray = Union[Boolean, NDArray[DTypeBoolean]] + + StrOrNDArray = Union[str, NDArray[np.str_]] + +else: + IntegerOrNDArray = Union[Integer, NDArray] + FloatingOrNDArray = Union[Floating, NDArray] + NumberOrNDArray = Union[Number, NDArray] + ComplexOrNDArray = Union[Complex, NDArray] + + BooleanOrNDArray = Union[Boolean, NDArray] + + StrOrNDArray = Union[str, NDArray] + + +class TypeInterpolator(Protocol): # noqa: D101 + x: NDArray + y: NDArray + + def __init__(self, *args: Any, **kwargs: Any): # noqa: D102 + ... # pragma: no cover + + def __call__( + self, x: FloatingOrArrayLike + ) -> FloatingOrNDArray: # noqa: D102 + ... # pragma: no cover + + +class TypeExtrapolator(Protocol): # noqa: D101 + interpolator: TypeInterpolator + + def __init__(self, *args: Any, **kwargs: Any): # noqa: D102 + ... # pragma: no cover + + def __call__( + self, x: FloatingOrArrayLike + ) -> FloatingOrNDArray: # noqa: D102 + ... # pragma: no cover + + +@runtime_checkable +class TypeLUTSequenceItem(Protocol): # noqa: D101 + def apply(self, RGB: ArrayLike, **kwargs: Any) -> NDArray: # noqa: D102 + ... # pragma: no cover + + +LiteralWarning = Literal[ + "default", "error", "ignore", "always", "module", "once" +] + +cast = cast + + +def arraylike(a: ArrayLike | NestedSequence[ArrayLike]) -> NDArray: + ... + + +def number_or_arraylike( + a: NumberOrArrayLike | NestedSequence[ArrayLike], +) -> NDArray: + ... + + +a: DTypeFloating = np.float64(1) +b: float = 1 +c: Floating = 1 +d: ArrayLike = [c, c] +e: FloatingOrArrayLike = d +s_a: Sequence[DTypeFloating] = [a, a] +s_b: Sequence[float] = [b, b] +s_c: Sequence[Floating] = [c, c] + +arraylike(a) +arraylike(b) +arraylike(c) +arraylike(d) +arraylike([d, d]) +arraylike(e) +arraylike([e, e]) +arraylike(s_a) +arraylike(s_b) +arraylike(s_c) + +number_or_arraylike(a) +number_or_arraylike(b) +number_or_arraylike(c) +number_or_arraylike(d) +number_or_arraylike([d, d]) +number_or_arraylike(e) +number_or_arraylike([e, e]) +number_or_arraylike(s_a) +number_or_arraylike(s_b) +number_or_arraylike(s_c) + +np.atleast_1d(a) +np.atleast_1d(b) +np.atleast_1d(c) +np.atleast_1d(arraylike(d)) +np.atleast_1d(arraylike([d, d])) +np.atleast_1d(arraylike(e)) +np.atleast_1d(arraylike([e, e])) +np.atleast_1d(s_a) +np.atleast_1d(s_b) +np.atleast_1d(s_c) + +del a, b, c, d, e, s_a, s_b, s_c diff --git a/colour/io/__init__.py b/colour/io/__init__.py index 53969852c8..6c8df004c5 100644 --- a/colour/io/__init__.py +++ b/colour/io/__init__.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .luts import * # noqa from . import luts from .image import ImageAttribute_Specification, convert_bit_depth @@ -9,21 +5,56 @@ from .image import read_image_Imageio, write_image_Imageio from .image import READ_IMAGE_METHODS, WRITE_IMAGE_METHODS from .image import read_image, write_image -from .tabular import (read_spectral_data_from_csv_file, read_sds_from_csv_file, - write_sds_to_csv_file) +from .ocio import process_image_OpenColorIO +from .tabular import ( + read_spectral_data_from_csv_file, + read_sds_from_csv_file, + write_sds_to_csv_file, +) from .tm2714 import SpectralDistribution_IESTM2714 +from .uprtek_sekonic import ( + SpectralDistribution_UPRTek, + SpectralDistribution_Sekonic, +) from .xrite import read_sds_from_xrite_file __all__ = [] __all__ += luts.__all__ -__all__ += ['ImageAttribute_Specification', 'convert_bit_depth'] -__all__ += ['read_image_OpenImageIO', 'write_image_OpenImageIO'] -__all__ += ['read_image_Imageio', 'write_image_Imageio'] -__all__ += ['READ_IMAGE_METHODS', 'WRITE_IMAGE_METHODS'] -__all__ += ['read_image', 'write_image'] __all__ += [ - 'read_spectral_data_from_csv_file', 'read_sds_from_csv_file', - 'write_sds_to_csv_file' + "ImageAttribute_Specification", + "convert_bit_depth", +] +__all__ += [ + "read_image_OpenImageIO", + "write_image_OpenImageIO", +] +__all__ += [ + "read_image_Imageio", + "write_image_Imageio", +] +__all__ += [ + "READ_IMAGE_METHODS", + "WRITE_IMAGE_METHODS", +] +__all__ += [ + "read_image", + "write_image", +] +__all__ += [ + "process_image_OpenColorIO", +] +__all__ += [ + "read_spectral_data_from_csv_file", + "read_sds_from_csv_file", + "write_sds_to_csv_file", +] +__all__ += [ + "SpectralDistribution_UPRTek", + "SpectralDistribution_Sekonic", +] +__all__ += [ + "SpectralDistribution_IESTM2714", +] +__all__ += [ + "read_sds_from_xrite_file", ] -__all__ += ['SpectralDistribution_IESTM2714'] -__all__ += ['read_sds_from_xrite_file'] diff --git a/colour/io/common.py b/colour/io/common.py deleted file mode 100644 index 0e0c7060f3..0000000000 --- a/colour/io/common.py +++ /dev/null @@ -1,129 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Input / Output Common Utilities -=============================== - -Defines input / output common utilities objects that don't fall in any specific -category. -""" - -from __future__ import division, unicode_literals - -from pprint import pformat - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['format_spectral_data'] - - -def format_spectral_data(data): - """ - Pretty formats given spectral data. - - Parameters - ---------- - data : dict - Spectral data to pretty format. - - Returns - ------- - unicode - Spectral data pretty representation. - - Examples - -------- - >>> import os - >>> from colour import read_spectral_data_from_csv_file - >>> csv_file = os.path.join(os.path.dirname(__file__), 'tests', - ... 'resources', 'colorchecker_n_ohta.csv') - >>> sds_data = {'1': read_spectral_data_from_csv_file(csv_file)['1']} - >>> print(format_spectral_data(sds_data['1'])) # doctest: +ELLIPSIS - {380.0: 0.0..., - 385.0: 0.0..., - 390.0: 0.0..., - 395.0: 0.0..., - 400.0: 0.0..., - 405.0: 0.0..., - 410.0: 0.0..., - 415.0: 0.0..., - 420.0: 0.0..., - 425.0: 0.0..., - 430.0: 0.0..., - 435.0: 0.0..., - 440.0: 0.0..., - 445.0: 0.0..., - 450.0: 0.0..., - 455.0: 0.0..., - 460.0: 0.0..., - 465.0: 0.0..., - 470.0: 0.0..., - 475.0: 0.0..., - 480.0: 0.0..., - 485.0: 0.0..., - 490.0: 0.0..., - 495.0: 0.0..., - 500.0: 0.0..., - 505.0: 0.0..., - 510.0: 0.0..., - 515.0: 0.0..., - 520.0: 0.0..., - 525.0: 0.0..., - 530.0: 0.0..., - 535.0: 0.0..., - 540.0: 0.0..., - 545.0: 0.0..., - 550.0: 0.0..., - 555.0: 0.0..., - 560.0: 0.0..., - 565.0: 0.0..., - 570.0: 0.1..., - 575.0: 0.1..., - 580.0: 0.1..., - 585.0: 0.1..., - 590.0: 0.1..., - 595.0: 0.1..., - 600.0: 0.1..., - 605.0: 0.1..., - 610.0: 0.1..., - 615.0: 0.1..., - 620.0: 0.1..., - 625.0: 0.1..., - 630.0: 0.1..., - 635.0: 0.1..., - 640.0: 0.1..., - 645.0: 0.1..., - 650.0: 0.1..., - 655.0: 0.1..., - 660.0: 0.2..., - 665.0: 0.2..., - 670.0: 0.2..., - 675.0: 0.2..., - 680.0: 0.2..., - 685.0: 0.2..., - 690.0: 0.2..., - 695.0: 0.2..., - 700.0: 0.2..., - 705.0: 0.2..., - 710.0: 0.3..., - 715.0: 0.3..., - 720.0: 0.3..., - 725.0: 0.3..., - 730.0: 0.3..., - 735.0: 0.3..., - 740.0: 0.4..., - 745.0: 0.4..., - 750.0: 0.4..., - 755.0: 0.4..., - 760.0: 0.4..., - 765.0: 0.4..., - 770.0: 0.4..., - 775.0: 0.4..., - 780.0: 0.4...} - """ - - return pformat(data) diff --git a/colour/io/image.py b/colour/io/image.py index 5477793ff8..e6cc7899fa 100644 --- a/colour/io/image.py +++ b/colour/io/image.py @@ -1,119 +1,161 @@ -# -*- coding: utf-8 -*- """ Image Input / Output Utilities ============================== -Defines image related input / output utilities objects. +Defines the image related input / output utilities objects. """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -import platform -from collections import namedtuple -from six import string_types - -from colour.utilities import (CaseInsensitiveMapping, as_float_array, - is_openimageio_installed, filter_kwargs, - required, usage_warning) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from dataclasses import dataclass, field + +from colour.hints import ( + Any, + ArrayLike, + Boolean, + DTypeNumber, + List, + Literal, + NDArray, + Optional, + Sequence, + Tuple, + Type, + Union, + cast, +) +from colour.utilities import ( + CaseInsensitiveMapping, + as_float_array, + as_int_array, + attest, + is_openimageio_installed, + filter_kwargs, + optional, + required, + usage_warning, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'BitDepth_Specification', 'ImageAttribute_Specification', - 'convert_bit_depth', 'read_image_OpenImageIO', 'read_image_Imageio', - 'READ_IMAGE_METHODS', 'read_image', 'write_image_OpenImageIO', - 'write_image_Imageio', 'WRITE_IMAGE_METHODS', 'write_image' + "BitDepth_Specification", + "ImageAttribute_Specification", + "convert_bit_depth", + "read_image_OpenImageIO", + "read_image_Imageio", + "READ_IMAGE_METHODS", + "read_image", + "write_image_OpenImageIO", + "write_image_Imageio", + "WRITE_IMAGE_METHODS", + "write_image", ] -BitDepth_Specification = namedtuple( - 'BitDepth_Specification', - ('name', 'numpy', 'openimageio', 'domain', 'clip')) + +@dataclass(frozen=True) +class BitDepth_Specification: + """ + Define a bit depth specification. + + Parameters + ---------- + name + Attribute name. + numpy + Object representing the *Numpy* bit depth. + openimageio + Object representing the *OpenImageIO* bit depth. + """ + + name: str + numpy: Type[DTypeNumber] + openimageio: Any -class ImageAttribute_Specification( - namedtuple('ImageAttribute_Specification', - ('name', 'value', 'type_'))): +@dataclass +class ImageAttribute_Specification: """ - Defines the an image specification attribute. + Define an image specification attribute. Parameters ---------- - name : unicode + name Attribute name. - value : object + value Attribute value. - type_ : TypeDesc, optional + type_ Attribute type as an *OpenImageIO* :class:`TypeDesc` class instance. """ - def __new__(cls, name, value, type_=None): - """ - Returns a new instance of the - :class:`colour.ImageAttribute_Specification` class. - """ - - return super(ImageAttribute_Specification, cls).__new__( - cls, name, value, type_) + name: str + value: Any + type_: Optional[OpenImageIO.TypeDesc] = field( # type: ignore[name-defined] # noqa + default_factory=lambda: None + ) -# TODO: Overhaul by using "np.sctypeDict". if is_openimageio_installed(): # pragma: no cover - from OpenImageIO import UINT8, UINT16, HALF, FLOAT - - BIT_DEPTH_MAPPING = CaseInsensitiveMapping({ - 'uint8': - BitDepth_Specification('uint8', np.uint8, UINT8, 255, True), - 'uint16': - BitDepth_Specification('uint16', np.uint16, UINT16, 65535, True), - 'float16': - BitDepth_Specification('float16', np.float16, HALF, 1, False), - 'float32': - BitDepth_Specification('float32', np.float32, FLOAT, 1, False), - 'float64': - BitDepth_Specification('float64', np.float64, FLOAT, 1, False), - }) - if platform.system() not in ('Windows', 'Microsoft'): # pragma: no cover - BIT_DEPTH_MAPPING['float128'] = BitDepth_Specification( - 'float128', np.float128, FLOAT, 1, False) + from OpenImageIO import UINT8, UINT16, HALF, FLOAT, DOUBLE + + MAPPING_BIT_DEPTH: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "uint8": BitDepth_Specification("uint8", np.uint8, UINT8), + "uint16": BitDepth_Specification("uint16", np.uint16, UINT16), + "float16": BitDepth_Specification("float16", np.float16, HALF), + "float32": BitDepth_Specification("float32", np.float32, FLOAT), + "float64": BitDepth_Specification("float64", np.float64, DOUBLE), + } + ) + if hasattr(np, "float128"): # pragma: no cover + MAPPING_BIT_DEPTH["float128"] = BitDepth_Specification( + "float128", np.float128, DOUBLE # type: ignore[arg-type] + ) else: # pragma: no cover - BIT_DEPTH_MAPPING = CaseInsensitiveMapping({ - 'uint8': - BitDepth_Specification('uint8', np.uint8, None, 255, True), - 'uint16': - BitDepth_Specification('uint16', np.uint16, None, 65535, True), - 'float16': - BitDepth_Specification('float16', np.float16, None, 1, False), - 'float32': - BitDepth_Specification('float32', np.float32, None, 1, False), - 'float64': - BitDepth_Specification('float64', np.float64, None, 1, False), - }) - if platform.system() not in ('Windows', 'Microsoft'): # pragma: no cover - BIT_DEPTH_MAPPING['float128'] = BitDepth_Specification( - 'float128', np.float128, None, 1, False) - - -def convert_bit_depth(a, bit_depth='float32'): + MAPPING_BIT_DEPTH: CaseInsensitiveMapping = ( # type: ignore[no-redef] + CaseInsensitiveMapping( + { + "uint8": BitDepth_Specification("uint8", np.uint8, None), + "uint16": BitDepth_Specification("uint16", np.uint16, None), + "float16": BitDepth_Specification("float16", np.float16, None), + "float32": BitDepth_Specification("float32", np.float32, None), + "float64": BitDepth_Specification("float64", np.float64, None), + } + ) + ) + if hasattr(np, "float128"): # pragma: no cover + MAPPING_BIT_DEPTH["float128"] = BitDepth_Specification( + "float128", np.float128, None # type: ignore[arg-type] + ) + + +def convert_bit_depth( + a: ArrayLike, + bit_depth: Literal[ + "uint8", "uint16", "float16", "float32", "float64", "float128" + ] = "float32", +) -> NDArray: """ - Converts given array to given bit depth, the current bit depth of the array + Convert given array to given bit depth, the current bit depth of the array is used to determine the appropriate conversion path. Parameters ---------- - a : array_like + a Array to convert to given bit depth. - bit_depth : unicode + bit_depth Bit depth. Returns ------- - ndarray + :class`numpy.ndarray` Converted array. Examples @@ -134,66 +176,73 @@ def convert_bit_depth(a, bit_depth='float32'): a = np.asarray(a) - bit_depths = ', '.join(sorted(BIT_DEPTH_MAPPING.keys())) + bit_depths = ", ".join(sorted(MAPPING_BIT_DEPTH.keys())) - assert bit_depth in bit_depths, ( - 'Incorrect bit depth was specified, it must be one of: "{0}"!'.format( - bit_depths)) + attest( + bit_depth in bit_depths, + f'Incorrect bit depth was specified, it must be one of: "{bit_depths}"!', + ) - assert str(a.dtype) in bit_depths, ( - 'Image bit depth must be one of: "{0}"!'.format(bit_depths)) + attest( + str(a.dtype) in bit_depths, + f'Image bit depth must be one of: "{bit_depths}"!', + ) source_dtype = str(a.dtype) - target_dtype = BIT_DEPTH_MAPPING[bit_depth].numpy - - if source_dtype == 'uint8': - if bit_depth == 'uint8': - return a - elif bit_depth == 'uint16': - return (a * 257).astype(target_dtype) - elif bit_depth in ('float16', 'float32', 'float64', 'float128'): - return (a / 255).astype(target_dtype) - elif source_dtype == 'uint16': - if bit_depth == 'uint8': - return (a / 257).astype(target_dtype) - elif bit_depth == 'uint16': - return a - elif bit_depth in ('float16', 'float32', 'float64', 'float128'): - return (a / 65535).astype(target_dtype) - elif source_dtype in ('float16', 'float32', 'float64', 'float128'): - if bit_depth == 'uint8': - return np.around(a * 255).astype(target_dtype) - elif bit_depth == 'uint16': - return np.around(a * 65535).astype(target_dtype) - elif bit_depth in ('float16', 'float32', 'float64', 'float128'): - return a.astype(target_dtype) - - -@required('OpenImageIO') -def read_image_OpenImageIO(path, bit_depth='float32', attributes=False): + target_dtype = MAPPING_BIT_DEPTH[bit_depth].numpy + + if source_dtype == "uint8": + if bit_depth == "uint16": + a = (a * 257).astype(target_dtype) + elif bit_depth in ("float16", "float32", "float64", "float128"): + a = (a / 255).astype(target_dtype) + elif source_dtype == "uint16": + if bit_depth == "uint8": + a = (a / 257).astype(target_dtype) + elif bit_depth in ("float16", "float32", "float64", "float128"): + a = (a / 65535).astype(target_dtype) + elif source_dtype in ("float16", "float32", "float64", "float128"): + if bit_depth == "uint8": + a = np.around(a * 255).astype(target_dtype) + elif bit_depth == "uint16": + a = np.around(a * 65535).astype(target_dtype) + elif bit_depth in ("float16", "float32", "float64", "float128"): + a = a.astype(target_dtype) + + return a # type: ignore[return-value] + + +@required("OpenImageIO") +def read_image_OpenImageIO( + path: str, + bit_depth: Literal[ + "uint8", "uint16", "float16", "float32", "float64", "float128" + ] = "float32", + attributes: Boolean = False, +) -> Union[NDArray, Tuple[NDArray, List]]: # noqa: D405,D410,D407,D411 """ - Reads the image at given path using *OpenImageIO*. + Read the image at given path using *OpenImageIO*. Parameters ---------- - path : unicode + path Image path. - bit_depth : unicode, optional - **{'float32', 'uint8', 'uint16', 'float16'}**, + bit_depth Returned image bit depth, the bit depth conversion behaviour is driven directly by *OpenImageIO*, this definition only converts to the relevant data type after reading. - attributes : bool, optional + attributes Whether to return the image attributes. Returns ------- - ndarray or tuple - Image as a ndarray or tuple of image as ndarray and list of attributes + :class`numpy.ndarray` or :class:`tuple` + Image data or tuple of image data and list of + :class:`colour.io.ImageAttribute_Specification` class instances. Notes ----- - - For convenience, single channel images are squeezed to 2d arrays. + - For convenience, single channel images are squeezed to 2D arrays. Examples -------- @@ -201,66 +250,79 @@ def read_image_OpenImageIO(path, bit_depth='float32', attributes=False): >>> import colour >>> path = os.path.join(colour.__path__[0], 'io', 'tests', 'resources', ... 'CMS_Test_Pattern.exr') - >>> image = read_image(path) # doctest: +SKIP + >>> image = read_image_OpenImageIO(path) # doctest: +SKIP """ from OpenImageIO import ImageInput path = str(path) - bit_depth = BIT_DEPTH_MAPPING[bit_depth] + bit_depth_specification = MAPPING_BIT_DEPTH[bit_depth] image = ImageInput.open(path) specification = image.spec() - shape = (specification.height, specification.width, - specification.nchannels) + shape = ( + specification.height, + specification.width, + specification.nchannels, + ) - image_data = image.read_image(bit_depth.openimageio) + image_data = image.read_image(bit_depth_specification.openimageio) image.close() image = np.squeeze( - np.array(image_data, dtype=bit_depth.numpy).reshape(shape)) + np.array(image_data, dtype=bit_depth_specification.numpy).reshape( + shape + ) + ) if attributes: extra_attributes = [] for i in range(len(specification.extra_attribs)): attribute = specification.extra_attribs[i] extra_attributes.append( - ImageAttribute_Specification(attribute.name, attribute.value, - attribute.type)) + ImageAttribute_Specification( + attribute.name, attribute.value, attribute.type + ) + ) return image, extra_attributes else: return image -def read_image_Imageio(path, bit_depth='float32', **kwargs): +def read_image_Imageio( + path: str, + bit_depth: Literal[ + "uint8", "uint16", "float16", "float32", "float64", "float128" + ] = "float32", + **kwargs: Any, +) -> NDArray: """ - Reads the image at given path using *Imageio*. + Read the image at given path using *Imageio*. Parameters ---------- - path : unicode + path Image path. - bit_depth : unicode, optional - **{'float32', 'uint8', 'uint16', 'float16'}**, + bit_depth Returned image bit depth, the image data is converted with :func:`colour.io.convert_bit_depth` definition after reading the image. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs Keywords arguments. Returns ------- - ndarray - Image as a ndarray. + :class`numpy.ndarray` + Image data. Notes ----- - - For convenience, single channel images are squeezed to 2d arrays. + - For convenience, single channel images are squeezed to 2D arrays. Examples -------- @@ -277,52 +339,56 @@ def read_image_Imageio(path, bit_depth='float32', **kwargs): from imageio import imread - image = imread(path, **kwargs) + image = np.squeeze(imread(path, **kwargs)) return convert_bit_depth(image, bit_depth) -READ_IMAGE_METHODS = CaseInsensitiveMapping({ - 'Imageio': read_image_Imageio, - 'OpenImageIO': read_image_OpenImageIO, -}) +READ_IMAGE_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Imageio": read_image_Imageio, + "OpenImageIO": read_image_OpenImageIO, + } +) READ_IMAGE_METHODS.__doc__ = """ -Supported read image methods. - -READ_IMAGE_METHODS : CaseInsensitiveMapping - **{'Imageio', 'OpenImageIO'}** +Supported image read methods. """ -def read_image(path, bit_depth='float32', method='OpenImageIO', **kwargs): +def read_image( + path: str, + bit_depth: Literal[ + "uint8", "uint16", "float16", "float32", "float64", "float128" + ] = "float32", + method: Union[Literal["Imageio", "OpenImageIO"], str] = "OpenImageIO", + **kwargs: Any, +) -> NDArray: # noqa: D405,D407,D410,D411,D414 """ - Reads the image at given path using given method. + Read the image at given path using given method. Parameters ---------- - path : unicode + path Image path. - bit_depth : unicode, optional - **{'float32', 'uint8', 'uint16', 'float16'}**, + bit_depth Returned image bit depth, for the *Imageio* method, the image data is converted with :func:`colour.io.convert_bit_depth` definition after reading the image, for the *OpenImageIO* method, the bit depth conversion behaviour is driven directly by the library, this definition only converts to the relevant data type after reading. - method : unicode, optional - **{'OpenImageIO', 'Imageio'}**, + method Read method, i.e. the image library used for reading images. Other Parameters ---------------- - attributes : bool, optional + attributes {:func:`colour.io.read_image_OpenImageIO`}, Whether to return the image attributes. Returns ------- - ndarray - Image as a ndarray. + :class`numpy.ndarray` + Image data. Notes ----- @@ -330,7 +396,7 @@ def read_image(path, bit_depth='float32', method='OpenImageIO', **kwargs): writing will be performed by *Imageio*. - If the given method is *Imageio*, ``kwargs`` is passed directly to the wrapped definition. - - For convenience, single channel images are squeezed to 2d arrays. + - For convenience, single channel images are squeezed to 2D arrays. Examples -------- @@ -345,43 +411,52 @@ def read_image(path, bit_depth='float32', method='OpenImageIO', **kwargs): dtype('float32') """ - if method.lower() == 'openimageio': # pragma: no cover + method = validate_method(method, READ_IMAGE_METHODS) + + if method == "openimageio": # pragma: no cover if not is_openimageio_installed(): usage_warning( '"OpenImageIO" related API features are not available, ' - 'switching to "Imageio"!') - method = 'Imageio' + 'switching to "Imageio"!' + ) + method = "Imageio" function = READ_IMAGE_METHODS[method] - if method.lower() == 'openimageio': # pragma: no cover + if method == "openimageio": # pragma: no cover kwargs = filter_kwargs(function, **kwargs) return function(path, bit_depth, **kwargs) -@required('OpenImageIO') -def write_image_OpenImageIO(image, path, bit_depth='float32', attributes=None): +@required("OpenImageIO") +def write_image_OpenImageIO( + image: ArrayLike, + path: str, + bit_depth: Literal[ + "uint8", "uint16", "float16", "float32", "float64", "float128" + ] = "float32", + attributes: Optional[Sequence] = None, +) -> Boolean: # noqa: D405,D407,D410,D411 """ - Writes given image at given path using *OpenImageIO*. + Write given image at given path using *OpenImageIO*. Parameters ---------- - image : array_like + image Image data. - path : unicode + path Image path. - bit_depth : unicode, optional - **{'float32', 'uint8', 'uint16', 'float16'}**, + bit_depth Bit depth to write the image at, the bit depth conversion behaviour is ruled directly by *OpenImageIO*. - attributes : array_like, optional + attributes An array of :class:`colour.io.ImageAttribute_Specification` class instances used to set attributes of the image. Returns ------- - bool + :class:`bool` Definition success. Examples @@ -395,30 +470,45 @@ def write_image_OpenImageIO(image, path, bit_depth='float32', attributes=None): >>> image = read_image(path) # doctest: +SKIP >>> path = os.path.join(colour.__path__[0], 'io', 'tests', 'resources', ... 'CMSTestPattern.tif') - >>> write_image(image, path) # doctest: +SKIP + >>> write_image_OpenImageIO(image, path) # doctest: +SKIP True Advanced image writing while setting attributes: >>> compression = ImageAttribute_Specification('Compression', 'none') - >>> write_image(image, path, 'uint8', [compression]) # doctest: +SKIP + >>> write_image_OpenImageIO(image, path, 'uint8', [compression]) + ... # doctest: +SKIP True + + Writing an "ACES" compliant "EXR" file: + + >>> if is_openimageio_installed(): # doctest: +SKIP + ... from OpenImageIO import TypeDesc + ... chromaticities = ( + ... 0.7347, 0.2653, 0.0, 1.0, 0.0001, -0.077, 0.32168, 0.33767) + ... attributes = [ + ... ImageAttribute_Specification('acesImageContainerFlag', True), + ... ImageAttribute_Specification( + ... 'chromaticities', chromaticities, TypeDesc('float[8]')), + ... ImageAttribute_Specification('compression', 'none')] + ... write_image_OpenImageIO(image, path, attributes=attributes) """ - from OpenImageIO import VERSION_MAJOR, ImageOutput, ImageSpec + from OpenImageIO import ImageOutput, ImageSpec + image = as_float_array(image) path = str(path) - if attributes is None: - attributes = [] + attributes = cast(List, optional(attributes, [])) - bit_depth_specification = BIT_DEPTH_MAPPING[bit_depth] - bit_depth = bit_depth_specification.openimageio + bit_depth_specification = MAPPING_BIT_DEPTH[bit_depth] + + if bit_depth_specification.numpy in [np.uint8, np.uint16]: + mininum, maximum = np.iinfo(np.uint8).min, np.iinfo(np.uint8).max + image = np.clip(image * maximum, mininum, maximum) + + image = as_int_array(image, bit_depth_specification.numpy) - image = as_float_array(image) - image = image * bit_depth_specification.domain - if bit_depth_specification.clip: - image = np.clip(image, 0, bit_depth_specification.domain) image = image.astype(bit_depth_specification.numpy) if image.ndim == 2: @@ -427,12 +517,16 @@ def write_image_OpenImageIO(image, path, bit_depth='float32', attributes=None): else: height, width, channels = image.shape - specification = ImageSpec(width, height, channels, bit_depth) + specification = ImageSpec( + width, height, channels, bit_depth_specification.openimageio + ) for attribute in attributes: name = str(attribute.name) - value = (str(attribute.value) - if isinstance(attribute.value, string_types) else - attribute.value) + value = ( + str(attribute.value) + if isinstance(attribute.value, str) + else attribute.value + ) type_ = attribute.type_ if attribute.type_ is None: specification.attribute(name, value) @@ -441,44 +535,44 @@ def write_image_OpenImageIO(image, path, bit_depth='float32', attributes=None): image_output = ImageOutput.create(path) - if VERSION_MAJOR == 1: - from OpenImageIO import ImageOutputOpenMode - - image_output.open(path, specification, ImageOutputOpenMode.Create) - image_output.write_image(bit_depth, image.tostring()) - else: - image_output.open(path, specification) - image_output.write_image(image) + image_output.open(path, specification) + image_output.write_image(image) image_output.close() return True -def write_image_Imageio(image, path, bit_depth='float32', **kwargs): +def write_image_Imageio( + image: ArrayLike, + path: str, + bit_depth: Literal[ + "uint8", "uint16", "float16", "float32", "float64", "float128" + ] = "float32", + **kwargs: Any, +) -> Boolean: """ - Writes given image at given path using *Imageio*. + Write given image at given path using *Imageio*. Parameters ---------- - image : array_like + image Image data. - path : unicode + path Image path. - bit_depth : unicode, optional - **{'float32', 'uint8', 'uint16', 'float16'}**, + bit_depth Bit depth to write the image at, the image data is converted with :func:`colour.io.convert_bit_depth` definition prior to writing the image. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs Keywords arguments. Returns ------- - bool + :class:`bool` Definition success. Examples @@ -490,7 +584,7 @@ def write_image_Imageio(image, path, bit_depth='float32', **kwargs): >>> image = read_image(path) # doctest: +SKIP >>> path = os.path.join(colour.__path__[0], 'io', 'tests', 'resources', ... 'CMSTestPattern.tif') - >>> write_image(image, path) # doctest: +SKIP + >>> write_image_Imageio(image, path) # doctest: +SKIP True """ @@ -501,51 +595,52 @@ def write_image_Imageio(image, path, bit_depth='float32', **kwargs): return imwrite(path, image, **kwargs) -WRITE_IMAGE_METHODS = CaseInsensitiveMapping({ - 'Imageio': write_image_Imageio, - 'OpenImageIO': write_image_OpenImageIO, -}) +WRITE_IMAGE_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Imageio": write_image_Imageio, + "OpenImageIO": write_image_OpenImageIO, + } +) WRITE_IMAGE_METHODS.__doc__ = """ -Supported write image methods. - -WRITE_IMAGE_METHODS : CaseInsensitiveMapping - **{'Imageio', 'OpenImageIO'}** +Supported image write methods. """ -def write_image(image, - path, - bit_depth='float32', - method='OpenImageIO', - **kwargs): +def write_image( + image: ArrayLike, + path: str, + bit_depth: Literal[ + "uint8", "uint16", "float16", "float32", "float64", "float128" + ] = "float32", + method: Union[Literal["Imageio", "OpenImageIO"], str] = "OpenImageIO", + **kwargs: Any, +) -> Boolean: # noqa: D405,D407,D410,D411,D414 """ - Writes given image at given path using given method. + Write given image at given path using given method. Parameters ---------- - image : array_like + image Image data. - path : unicode + path Image path. - bit_depth : unicode, optional - **{'float32', 'uint8', 'uint16', 'float16'}**, + bit_depth Bit depth to write the image at, for the *Imageio* method, the image data is converted with :func:`colour.io.convert_bit_depth` definition prior to writing the image. - method : unicode, optional - **{'OpenImageIO', 'Imageio'}**, + method Write method, i.e. the image library used for writing images. Other Parameters ---------------- - attributes : array_like, optional + attributes {:func:`colour.io.write_image_OpenImageIO`}, An array of :class:`colour.io.ImageAttribute_Specification` class instances used to set attributes of the image. Returns ------- - bool + :class:`bool` Definition success. Notes @@ -577,16 +672,19 @@ def write_image(image, True """ - if method.lower() == 'openimageio': # pragma: no cover + method = validate_method(method, WRITE_IMAGE_METHODS) + + if method == "openimageio": # pragma: no cover if not is_openimageio_installed(): usage_warning( '"OpenImageIO" related API features are not available, ' - 'switching to "Imageio"!') - method = 'Imageio' + 'switching to "Imageio"!' + ) + method = "Imageio" function = WRITE_IMAGE_METHODS[method] - if method.lower() == 'openimageio': # pragma: no cover + if method == "openimageio": # pragma: no cover kwargs = filter_kwargs(function, **kwargs) return function(image, path, bit_depth, **kwargs) diff --git a/colour/io/luts/__init__.py b/colour/io/luts/__init__.py index 049d42396f..7db766fbed 100644 --- a/colour/io/luts/__init__.py +++ b/colour/io/luts/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ References ---------- @@ -12,79 +11,133 @@ https://sourceforge.net/projects/cinespacelutlib/ """ -from __future__ import absolute_import +from __future__ import annotations import os -from colour.utilities import CaseInsensitiveMapping, filter_kwargs -from .lut import (AbstractLUTSequenceOperator, LUT1D, LUT3x1D, LUT3D, - LUTSequence, LUT_to_LUT) +from colour.hints import Any, Boolean, Integer, Literal, Optional, Union, cast +from colour.utilities import ( + CaseInsensitiveMapping, + filter_kwargs, + optional, + validate_method, +) + +from .lut import LUT1D, LUT3x1D, LUT3D, LUT_to_LUT +from .operator import AbstractLUTSequenceOperator, LUTOperatorMatrix +from .sequence import LUTSequence from .iridas_cube import read_LUT_IridasCube, write_LUT_IridasCube from .resolve_cube import read_LUT_ResolveCube, write_LUT_ResolveCube from .sony_spi1d import read_LUT_SonySPI1D, write_LUT_SonySPI1D from .sony_spi3d import read_LUT_SonySPI3D, write_LUT_SonySPI3D +from .sony_spimtx import read_LUT_SonySPImtx, write_LUT_SonySPImtx from .cinespace_csp import read_LUT_Cinespace, write_LUT_Cinespace __all__ = [ - 'AbstractLUTSequenceOperator', 'LUT1D', 'LUT3x1D', 'LUT3D', 'LUTSequence', - 'LUT_to_LUT' + "LUT1D", + "LUT3x1D", + "LUT3D", + "LUT_to_LUT", +] +__all__ += [ + "AbstractLUTSequenceOperator", + "LUTOperatorMatrix", +] +__all__ += [ + "LUTSequence", +] +__all__ += [ + "read_LUT_IridasCube", + "write_LUT_IridasCube", +] +__all__ += [ + "read_LUT_ResolveCube", + "write_LUT_ResolveCube", +] +__all__ += [ + "read_LUT_SonySPI1D", + "write_LUT_SonySPI1D", +] +__all__ += [ + "read_LUT_SonySPI3D", + "write_LUT_SonySPI3D", +] +__all__ += [ + "read_LUT_SonySPImtx", + "write_LUT_SonySPImtx", +] +__all__ += [ + "read_LUT_Cinespace", + "write_LUT_Cinespace", ] -__all__ += ['read_LUT_IridasCube', 'write_LUT_IridasCube'] -__all__ += ['read_LUT_ResolveCube', 'write_LUT_ResolveCube'] -__all__ += ['read_LUT_SonySPI1D', 'write_LUT_SonySPI1D'] -__all__ += ['read_LUT_SonySPI3D', 'write_LUT_SonySPI3D'] -__all__ += ['read_LUT_Cinespace', 'write_LUT_Cinespace'] - -EXTENSION_TO_LUT_FORMAT_MAPPING = CaseInsensitiveMapping({ - '.cube': 'Iridas Cube', - '.spi1d': 'Sony SPI1D', - '.spi3d': 'Sony SPI3D', - '.csp': 'Cinespace' -}) -""" -Extension to *LUT* format. - -EXTENSION_TO_LUT_FORMAT_MAPPING : CaseInsensitiveMapping - **{'.cube', '.spi1d'}** -""" -LUT_READ_METHODS = CaseInsensitiveMapping({ - 'Cinespace': read_LUT_Cinespace, - 'Iridas Cube': read_LUT_IridasCube, - 'Resolve Cube': read_LUT_ResolveCube, - 'Sony SPI1D': read_LUT_SonySPI1D, - 'Sony SPI3D': read_LUT_SonySPI3D, -}) +MAPPING_EXTENSION_TO_LUT_FORMAT: CaseInsensitiveMapping = ( + CaseInsensitiveMapping( + { + ".cube": "Iridas Cube", + ".spi1d": "Sony SPI1D", + ".spi3d": "Sony SPI3D", + ".spimtx": "Sony SPImtx", + ".csp": "Cinespace", + } + ) +) +"""Extension to *LUT* format.""" + +LUT_READ_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Cinespace": read_LUT_Cinespace, + "Iridas Cube": read_LUT_IridasCube, + "Resolve Cube": read_LUT_ResolveCube, + "Sony SPI1D": read_LUT_SonySPI1D, + "Sony SPI3D": read_LUT_SonySPI3D, + "Sony SPImtx": read_LUT_SonySPImtx, + } +) LUT_READ_METHODS.__doc__ = """ Supported *LUT* reading methods. References ---------- :cite:`AdobeSystems2013b`, :cite:`Chamberlain2015` - -LUT_READ_METHODS : CaseInsensitiveMapping - **{'Cinespace', 'Iridas Cube', 'Resolve Cube', 'Sony SPI1D', - 'Sony SPI3D'}** """ -def read_LUT(path, method=None, **kwargs): +def read_LUT( + path: str, + method: Optional[ + Union[ + Literal[ + "Cinespace", + "Iridas Cube", + "Resolve Cube", + "Sony SPI1D", + "Sony SPI3D", + "Sony SPImtx", + ], + str, + ] + ] = None, + **kwargs: Any, +) -> Union[LUT1D, LUT3x1D, LUT3D, LUTSequence, LUTOperatorMatrix]: """ - Reads given *LUT* file using given method. + Read given *LUT* file using given method. Parameters ---------- - path : unicode + path *LUT* path. - method : unicode, optional - **{None, 'Cinespace', 'Iridas Cube', 'Resolve Cube', 'Sony SPI1D', - 'Sony SPI3D'}**, Reading method, if *None*, the method will be - auto-detected according to extension. + method + Reading method, if *None*, the method will be auto-detected according + to extension. Returns ------- - LUT1D or LUT3x1D or LUT3D - :class:`LUT1D`, :class:`LUT3x1D` or :class:`LUT3D` class instance. + :class:`colour.LUT1D` or :class:`colour.LUT3x1D` or :class:`colour.LUT3D` \ +or :class:`colour.LUTSequence` or :class:`colour.LUTOperatorMatrix` + :class:`colour.LUT1D` or :class:`colour.LUT3x1D` or + :class:`colour.LUT3D` or :class:`colour.LUTSequence` or + :class:`colour.LUTOperatorMatrix` class instance. References ---------- @@ -136,10 +189,31 @@ def read_LUT(path, method=None, **kwargs): [ 1. 1. 1.]] Size : (4, 4, 4, 3) Comment 01 : Adapted from a LUT generated by Foundry::LUT. + + Reading a *Sony* *.spimtx* *LUT*: + + >>> path = os.path.join( + ... os.path.dirname(__file__), 'tests', 'resources', 'sony_spimtx', + ... 'dt.spimtx') + >>> print(read_LUT(path)) + LUTOperatorMatrix - dt + ---------------------- + + Matrix : [[ 0.864274 0. 0. 0. ] + [ 0. 0.864274 0. 0. ] + [ 0. 0. 0.864274 0. ] + [ 0. 0. 0. 1. ]] + Offset : [ 0. 0. 0. 0.] """ - if method is None: - method = EXTENSION_TO_LUT_FORMAT_MAPPING[os.path.splitext(path)[-1]] + method = cast( + str, + optional( + method, MAPPING_EXTENSION_TO_LUT_FORMAT[os.path.splitext(path)[-1]] + ), + ) + + method = validate_method(method, LUT_READ_METHODS) function = LUT_READ_METHODS[method] @@ -148,54 +222,72 @@ def read_LUT(path, method=None, **kwargs): except ValueError as error: # Case where a "Resolve Cube" with "LUT3x1D" shaper was read as an # "Iridas Cube" "LUT". - if method == 'Iridas Cube': - function = LUT_READ_METHODS['Resolve Cube'] + if method == "iridas cube": + function = LUT_READ_METHODS["Resolve Cube"] return function(path, **filter_kwargs(function, **kwargs)) else: raise error -LUT_WRITE_METHODS = CaseInsensitiveMapping({ - 'Iridas Cube': write_LUT_IridasCube, - 'Resolve Cube': write_LUT_ResolveCube, - 'Sony SPI1D': write_LUT_SonySPI1D, - 'Sony SPI3D': write_LUT_SonySPI3D, - 'Cinespace': write_LUT_Cinespace, -}) +LUT_WRITE_METHODS = CaseInsensitiveMapping( + { + "Cinespace": write_LUT_Cinespace, + "Iridas Cube": write_LUT_IridasCube, + "Resolve Cube": write_LUT_ResolveCube, + "Sony SPI1D": write_LUT_SonySPI1D, + "Sony SPI3D": write_LUT_SonySPI3D, + "Sony SPImtx": write_LUT_SonySPImtx, + } +) LUT_WRITE_METHODS.__doc__ = """ Supported *LUT* reading methods. References ---------- :cite:`AdobeSystems2013b`, :cite:`Chamberlain2015` - -LUT_WRITE_METHODS : CaseInsensitiveMapping - **{'Cinespace', 'Iridas Cube', 'Resolve Cube', 'Sony SPI1D', - 'Sony SPI3D'}** """ -def write_LUT(LUT, path, decimals=7, method=None, **kwargs): +def write_LUT( + LUT: Union[LUT1D, LUT3x1D, LUT3D, LUTSequence, LUTOperatorMatrix], + path: str, + decimals: Integer = 7, + method: Optional[ + Union[ + Literal[ + "Cinespace", + "Iridas Cube", + "Resolve Cube", + "Sony SPI1D", + "Sony SPI3D", + "Sony SPImtx", + ], + str, + ] + ] = None, + **kwargs: Any, +) -> Boolean: """ - Writes given *LUT* to given file using given method. + Write given *LUT* to given file using given method. Parameters ---------- - LUT : LUT1D or LUT3x1D or LUT3D - :class:`LUT1D`, :class:`LUT3x1D` or :class:`LUT3D` class instance to - write at given path. - path : unicode + LUT + :class:`colour.LUT1D` or :class:`colour.LUT3x1D` or + :class:`colour.LUT3D` or :class:`colour.LUTSequence` or + :class:`colour.LUTOperatorMatrix` class instance to write at given + path. + path *LUT* path. - decimals : int, optional + decimals Formatting decimals. - method : unicode, optional - **{None, 'Cinespace', 'Iridas Cube', 'Resolve Cube', 'Sony SPI1D', - 'Sony SPI3D'}**, Writing method, if *None*, the method will be - auto-detected according to extension. + method + Writing method, if *None*, the method will be auto-detected according + to extension. Returns ------- - bool + :class:`bool` Definition success. References @@ -237,14 +329,26 @@ def write_LUT(LUT, path, decimals=7, method=None, **kwargs): >>> write_LUT(LUT, 'My_LUT.cube') # doctest: +SKIP """ - if method is None: - method = EXTENSION_TO_LUT_FORMAT_MAPPING[os.path.splitext(path)[-1]] - if method == 'Iridas Cube' and isinstance(LUT, LUTSequence): - method = 'Resolve Cube' + method = cast( + str, + optional( + method, MAPPING_EXTENSION_TO_LUT_FORMAT[os.path.splitext(path)[-1]] + ), + ) + + method = validate_method(method, LUT_WRITE_METHODS) + + if method == "iridas cube" and isinstance(LUT, LUTSequence): + method = "resolve cube" function = LUT_WRITE_METHODS[method] return function(LUT, path, decimals, **filter_kwargs(function, **kwargs)) -__all__ += ['LUT_READ_METHODS', 'read_LUT', 'LUT_WRITE_METHODS', 'write_LUT'] +__all__ += [ + "LUT_READ_METHODS", + "read_LUT", + "LUT_WRITE_METHODS", + "write_LUT", +] diff --git a/colour/io/luts/cinespace_csp.py b/colour/io/luts/cinespace_csp.py index 6c2ffc5f4b..72bb106050 100644 --- a/colour/io/luts/cinespace_csp.py +++ b/colour/io/luts/cinespace_csp.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ Cinespace .csp LUT Format Input / Output Utilities ================================================== -Defines *Cinespace* *.csp* *LUT* Format related input / output utilities -objects. +Defines the *Cinespace* *.csp* *LUT* format related input / output utilities +objects: - :func:`colour.io.read_LUT_Cinespace` - :func:`colour.io.write_LUT_Cinespace` @@ -16,35 +15,54 @@ https://sourceforge.net/projects/cinespacelutlib/ """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +from colour.hints import ( + ArrayLike, + Boolean, + Integer, + List, + NDArray, + Tuple, + Union, +) from colour.io.luts import LUT1D, LUT3x1D, LUT3D, LUTSequence -from colour.utilities import tsplit, tstack, as_float_array, as_int_array - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['read_LUT_Cinespace', 'write_LUT_Cinespace'] - - -def read_LUT_Cinespace(path): +from colour.utilities import ( + attest, + tsplit, + tstack, + as_float_array, + as_int_array, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "read_LUT_Cinespace", + "write_LUT_Cinespace", +] + + +def read_LUT_Cinespace(path: str) -> Union[LUT3x1D, LUT3D, LUTSequence]: """ - Reads given *Cinespace* *.csp* *LUT* file. + Read given *Cinespace* *.csp* *LUT* file. Parameters ---------- - path : unicode + path *LUT* path. Returns ------- - LUT3x1D or LUT3D or LUTSequence + :class:`colour.LUT3x1D` or :class:`colour.LUT3D` or \ +:class:`colour.LUTSequence` :class:`LUT3x1D` or :class:`LUT3D` or :class:`LUTSequence` class instance. @@ -84,43 +102,39 @@ def read_LUT_Cinespace(path): Size : (4, 4, 4, 3) """ - unity_range = np.array([[0., 0., 0.], [1., 1., 1.]]) + unity_range = np.array([[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]]) - def _parse_metadata_section(metadata): - """ - Parses the metadata at given lines. - """ + def _parse_metadata_section(metadata: List) -> Tuple: + """Parse the metadata at given lines.""" - return (metadata[0], metadata[1:]) if len(metadata) > 0 else ('', []) + return (metadata[0], metadata[1:]) if len(metadata) > 0 else ("", []) - def _parse_domain_section(lines): - """ - Parses the domain at given lines. - """ + def _parse_domain_section(lines: List[str]) -> NDArray: + """Parse the domain at given lines.""" - pre_LUT_size = max([int(lines[i]) for i in [0, 3, 6]]) + pre_LUT_size = max(int(lines[i]) for i in [0, 3, 6]) pre_LUT = [ as_float_array(lines[i].split()) for i in [1, 2, 4, 5, 7, 8] ] - pre_LUT_padded = [] + pre_LUT_padded = [] for row in pre_LUT: if len(row) != pre_LUT_size: pre_LUT_padded.append( np.pad( - row, (0, pre_LUT_size - row.shape[0]), - mode='constant', - constant_values=np.nan)) + row, + (0, pre_LUT_size - row.shape[0]), + mode="constant", + constant_values=np.nan, + ) + ) else: pre_LUT_padded.append(row) - pre_LUT = np.asarray(pre_LUT_padded) - return pre_LUT + return np.asarray(pre_LUT_padded) def _parse_table_section(lines): - """ - Parses the table at given lines. - """ + """Parse the table at given lines.""" size = as_int_array(lines[0].split()) table = as_float_array([line.split() for line in lines[1:]]) @@ -129,26 +143,26 @@ def _parse_table_section(lines): with open(path) as csp_file: lines = csp_file.readlines() - assert len(lines) > 0, 'LUT file empty!' + attest(len(lines) > 0, '"LUT" is empty!') lines = [line.strip() for line in lines if line.strip()] header = lines[0] - assert header == 'CSPLUTV100', 'Invalid header!' + attest(header == "CSPLUTV100", '"LUT" header is invalid!') kind = lines[1] - assert kind in ('1D', '3D'), 'Invalid kind!' + attest(kind in ("1D", "3D"), '"LUT" type must be "1D" or "3D"!') - is_3D = kind == '3D' + is_3D = kind == "3D" seek = 2 metadata = [] is_metadata = False for i, line in enumerate(lines[2:]): line = line.strip() - if line == 'BEGIN METADATA': + if line == "BEGIN METADATA": is_metadata = True continue - elif line == 'END METADATA': + elif line == "END METADATA": seek += i break @@ -158,81 +172,97 @@ def _parse_table_section(lines): title, comments = _parse_metadata_section(metadata) seek += 1 - pre_LUT = _parse_domain_section(lines[seek:seek + 9]) + pre_LUT = _parse_domain_section(lines[seek : seek + 9]) seek += 9 size, table = _parse_table_section(lines[seek:]) - assert np.product(size) == len(table), 'Invalid table size!' - - if (is_3D and pre_LUT.shape == (6, 2) and np.array_equal( - pre_LUT.reshape(3, 4).transpose()[2:4], unity_range)): - table = table.reshape([size[0], size[1], size[2], 3], order='F') - LUT = LUT3D( - domain=pre_LUT.reshape(3, 4).transpose()[0:2], - name=title, - comments=comments, - table=table) - return LUT - - if (not is_3D and pre_LUT.shape == (6, 2) and np.array_equal( - pre_LUT.reshape(3, 4).transpose()[2:4], unity_range)): - LUT = LUT3x1D( - domain=pre_LUT.reshape(3, 4).transpose()[0:2], - name=title, - comments=comments, - table=table) - - return LUT - - if is_3D: - pre_domain = tstack((pre_LUT[0], pre_LUT[2], pre_LUT[4])) - pre_table = tstack((pre_LUT[1], pre_LUT[3], pre_LUT[5])) - shaper_name = '{0} - Shaper'.format(title) - cube_name = '{0} - Cube'.format(title) - table = table.reshape([size[0], size[1], size[2], 3], order='F') - LUT_A = LUT3x1D(pre_table, shaper_name, pre_domain) - LUT_B = LUT3D(table, cube_name, comments=comments) - - return LUTSequence(LUT_A, LUT_B) - - if not is_3D: - pre_domain = tstack((pre_LUT[0], pre_LUT[2], pre_LUT[4])) - pre_table = tstack((pre_LUT[1], pre_LUT[3], pre_LUT[5])) - - if table.shape == (2, 3): - table_max = table[1] - table_min = table[0] - pre_table *= (table_max - table_min) - pre_table += table_min - - return LUT3x1D(pre_table, title, pre_domain, comments=comments) - else: - pre_name = '{0} - PreLUT'.format(title) - table_name = '{0} - Table'.format(title) - LUT_A = LUT3x1D(pre_table, pre_name, pre_domain) - LUT_B = LUT3x1D(table, table_name, comments=comments) + attest(np.product(size) == len(table), '"LUT" table size is invalid!') + + LUT: Union[LUT3x1D, LUT3D, LUTSequence] + if ( + is_3D + and pre_LUT.shape == (6, 2) + and np.array_equal( + np.transpose(np.reshape(pre_LUT, (3, 4)))[2:4], unity_range + ) + ): + table = table.reshape([size[0], size[1], size[2], 3], order="F") + LUT = LUT3D( + domain=np.transpose(np.reshape(pre_LUT, (3, 4)))[0:2], + name=title, + comments=comments, + table=table, + ) + + elif ( + not is_3D + and pre_LUT.shape == (6, 2) + and np.array_equal( + np.transpose(np.reshape(pre_LUT, (3, 4)))[2:4], unity_range + ) + ): + LUT = LUT3x1D( + domain=pre_LUT.reshape(3, 4).transpose()[0:2], + name=title, + comments=comments, + table=table, + ) + + elif is_3D: + pre_domain = tstack((pre_LUT[0], pre_LUT[2], pre_LUT[4])) + pre_table = tstack((pre_LUT[1], pre_LUT[3], pre_LUT[5])) + shaper_name = f"{title} - Shaper" + cube_name = f"{title} - Cube" + table = table.reshape([size[0], size[1], size[2], 3], order="F") + + LUT = LUTSequence( + LUT3x1D(pre_table, shaper_name, pre_domain), + LUT3D(table, cube_name, comments=comments), + ) + + elif not is_3D: + pre_domain = tstack((pre_LUT[0], pre_LUT[2], pre_LUT[4])) + pre_table = tstack((pre_LUT[1], pre_LUT[3], pre_LUT[5])) + + if table.shape == (2, 3): + table_max = table[1] + table_min = table[0] + pre_table *= table_max - table_min + pre_table += table_min + + LUT = LUT3x1D(pre_table, title, pre_domain, comments=comments) + else: + pre_name = f"{title} - PreLUT" + table_name = f"{title} - Table" + + LUT = LUTSequence( + LUT3x1D(pre_table, pre_name, pre_domain), + LUT3x1D(table, table_name, comments=comments), + ) - return LUTSequence(LUT_A, LUT_B) + return LUT -def write_LUT_Cinespace(LUT, path, decimals=7): +def write_LUT_Cinespace( + LUT: Union[LUT3x1D, LUT3D, LUTSequence], path: str, decimals: Integer = 7 +) -> Boolean: """ - Writes given *LUT* to given *Cinespace* *.csp* *LUT* file. + Write given *LUT* to given *Cinespace* *.csp* *LUT* file. Parameters ---------- - LUT : LUT1D or LUT3x1D or LUT3D or LUTSequence + LUT :class:`LUT1D`, :class:`LUT3x1D` or :class:`LUT3D` or :class:`LUTSequence` class instance to write at given path. - path : unicode + path *LUT* path. - decimals : int, optional + decimals Formatting decimals. Returns ------- - bool + :class:`bool` Definition success. References @@ -263,50 +293,52 @@ def write_LUT_Cinespace(LUT, path, decimals=7): >>> write_LUT_Cinespace(LUT, 'My_LUT.cube') # doctest: +SKIP """ - has_3D, has_3x1D, non_uniform = False, False, False + has_3D, has_3x1D = False, False if isinstance(LUT, LUTSequence): - assert (len(LUT) == 2 and - (isinstance(LUT[0], LUT1D) or isinstance(LUT[0], LUT3x1D)) and - isinstance(LUT[1], - LUT3D)), 'LUTSequence must be 1D+3D or 3x1D+3D!' + attest( + len(LUT) == 2 + and isinstance(LUT[0], (LUT1D, LUT3x1D)) + and isinstance(LUT[1], LUT3D), + '"LUTSequence" must be "1D + 3D" or "3x1D + 3D"!', + ) + LUT[0] = ( + LUT[0].as_LUT(LUT3x1D) if isinstance(LUT[0], LUT1D) else LUT[0] + ) + name = f"{LUT[0].name} - {LUT[1].name}" has_3x1D = True has_3D = True - name = LUT[1].name - if isinstance(LUT[0], LUT1D): - non_uniform = LUT[0].is_domain_explicit() - LUT[0] = LUT[0].as_LUT(LUT3x1D) elif isinstance(LUT, LUT1D): - non_uniform = LUT.is_domain_explicit() name = LUT.name - LUT = LUTSequence(LUT.as_LUT(LUT3x1D), LUT3D()) has_3x1D = True + LUT = LUTSequence(LUT.as_LUT(LUT3x1D), LUT3D()) elif isinstance(LUT, LUT3x1D): - non_uniform = LUT.is_domain_explicit() name = LUT.name - LUT = LUTSequence(LUT, LUT3D()) has_3x1D = True + LUT = LUTSequence(LUT, LUT3D()) elif isinstance(LUT, LUT3D): name = LUT.name - LUT = LUTSequence(LUT3x1D(), LUT) has_3D = True + LUT = LUTSequence(LUT3x1D(), LUT) else: - raise ValueError('LUT must be 1D, 3x1D, 3D, 1D + 3D or 3x1D + 3D!') + raise ValueError("LUT must be 1D, 3x1D, 3D, 1D + 3D or 3x1D + 3D!") if has_3x1D: - assert 2 <= LUT[0].size <= 65536, ( - 'Shaper size must be in domain [2, 65536]!') + attest( + 2 <= LUT[0].size <= 65536, + "Shaper size must be in domain [2, 65536]!", + ) if has_3D: - assert 2 <= LUT[1].size <= 256, 'Cube size must be in domain [2, 256]!' + attest( + 2 <= LUT[1].size <= 256, "Cube size must be in domain [2, 256]!" + ) - def _ragged_size(table): - """ - Return the ragged size of given table. - """ + def _ragged_size(table: ArrayLike) -> List: + """Return the ragged size of given table.""" R, G, B = tsplit(table) @@ -316,107 +348,104 @@ def _ragged_size(table): return [R_len, G_len, B_len] - def _format_array(array): - """ - Formats given array as a *Cinespace* *.cube* data row. - """ + def _format_array(array: Union[List, Tuple]) -> str: + """Format given array as a *Cinespace* *.cube* data row.""" - return '{1:0.{0}f} {2:0.{0}f} {3:0.{0}f}'.format(decimals, *array) + return "{1:0.{0}f} {2:0.{0}f} {3:0.{0}f}".format(decimals, *array) - def _format_tuple(array): + def _format_tuple(array: Union[List, Tuple]) -> str: """ - Formats given array as 2 space separated values to *decimals* + Format given array as 2 space separated values to *decimals* precision. """ - return '{1:0.{0}f} {2:0.{0}f}'.format(decimals, *array) + return "{1:0.{0}f} {2:0.{0}f}".format(decimals, *array) - with open(path, 'w') as csp_file: - csp_file.write('CSPLUTV100\n') + with open(path, "w") as csp_file: + csp_file.write("CSPLUTV100\n") if has_3D: - csp_file.write('3D\n\n') + csp_file.write("3D\n\n") else: - csp_file.write('1D\n\n') + csp_file.write("1D\n\n") - csp_file.write('BEGIN METADATA\n') - csp_file.write('{0}\n'.format(name)) + csp_file.write("BEGIN METADATA\n") + csp_file.write(f"{name}\n") if LUT[0].comments: for comment in LUT[0].comments: - csp_file.write('{0}\n'.format(comment)) + csp_file.write(f"{comment}\n") if LUT[1].comments: for comment in LUT[1].comments: - csp_file.write('{0}\n'.format(comment)) + csp_file.write(f"{comment}\n") - csp_file.write('END METADATA\n\n') + csp_file.write("END METADATA\n\n") - if has_3D or non_uniform: + if has_3D: if has_3x1D: for i in range(3): - if LUT[0].is_domain_explicit(): - size = _ragged_size(LUT[0].domain)[i] - table_min = np.nanmin(LUT[0].table) - table_max = np.nanmax(LUT[0].table) - else: - size = LUT[0].size + size = ( + _ragged_size(LUT[0].domain)[i] + if LUT[0].is_domain_explicit() + else LUT[0].size + ) - csp_file.write('{0}\n'.format(size)) + csp_file.write(f"{size}\n") for j in range(size): - if LUT[0].is_domain_explicit(): - entry = LUT[0].domain[j][i] - else: - entry = ( - LUT[0].domain[0][i] + j * - (LUT[0].domain[1][i] - LUT[0].domain[0][i]) / - (LUT[0].size - 1)) - csp_file.write('{0:.{1}f} '.format(entry, decimals)) + entry = ( + LUT[0].domain[j][i] + if LUT[0].is_domain_explicit() + else ( + LUT[0].domain[0][i] + + j + * (LUT[0].domain[1][i] - LUT[0].domain[0][i]) + / (LUT[0].size - 1) + ) + ) + + csp_file.write("{0:.{1}f} ".format(entry, decimals)) - csp_file.write('\n') + csp_file.write("\n") for j in range(size): entry = LUT[0].table[j][i] - if non_uniform: - entry -= table_min - entry /= (table_max - table_min) - csp_file.write('{0:.{1}f} '.format(entry, decimals)) + csp_file.write("{0:.{1}f} ".format(entry, decimals)) - csp_file.write('\n') + csp_file.write("\n") else: for i in range(3): - csp_file.write('2\n') - csp_file.write('{0}\n'.format( - _format_tuple( - [LUT[1].domain[0][i], LUT[1].domain[1][i]]))) - csp_file.write('{0:.{2}f} {1:.{2}f}\n'.format( - 0, 1, decimals)) - if non_uniform: - csp_file.write('\n{0}\n'.format(2)) - row = [table_min, table_min, table_min] - csp_file.write('{0}\n'.format(_format_array(row))) - row = [table_max, table_max, table_max] - csp_file.write('{0}\n'.format(_format_array(row))) - else: - csp_file.write('\n{0} {1} {2}\n'.format( - LUT[1].table.shape[0], LUT[1].table.shape[1], - LUT[1].table.shape[2])) - table = LUT[1].table.reshape([-1, 3], order='F') - - for row in table: - csp_file.write('{0}\n'.format(_format_array(row))) + csp_file.write("2\n") + domain = _format_tuple( + [LUT[1].domain[0][i], LUT[1].domain[1][i]] + ) + csp_file.write(f"{domain}\n") + csp_file.write( + "{0:.{2}f} {1:.{2}f}\n".format(0, 1, decimals) + ) + + csp_file.write( + f"\n{LUT[1].table.shape[0]} " + f"{LUT[1].table.shape[1]} " + f"{LUT[1].table.shape[2]}\n" + ) + table = LUT[1].table.reshape([-1, 3], order="F") + for row in table: + csp_file.write(f"{_format_array(row)}\n") else: for i in range(3): - csp_file.write('2\n') - csp_file.write('{0}\n'.format( - _format_tuple([LUT[0].domain[0][i], LUT[0].domain[1][i]]))) - csp_file.write('0.0 1.0\n') - csp_file.write('\n{0}\n'.format(LUT[0].size)) + csp_file.write("2\n") + domain = _format_tuple( + [LUT[0].domain[0][i], LUT[0].domain[1][i]] + ) + csp_file.write(f"{domain}\n") + csp_file.write("0.0 1.0\n") + csp_file.write(f"\n{LUT[0].size}\n") table = LUT[0].table for row in table: - csp_file.write('{0}\n'.format(_format_array(row))) + csp_file.write(f"{_format_array(row)}\n") return True diff --git a/colour/io/luts/common.py b/colour/io/luts/common.py index 2d3db5307e..72b2e45595 100644 --- a/colour/io/luts/common.py +++ b/colour/io/luts/common.py @@ -1,48 +1,48 @@ -# -*- coding: utf-8 -*- """ LUT Processing Common Utilities =============================== -Defines LUT Processing common utilities objects that don't fall in any specific -category. +Defines the *LUT* processing common utilities objects that don't fall in any +specific category. """ -from __future__ import division, unicode_literals +from __future__ import annotations import os import re -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['path_to_title'] +__all__ = [ + "path_to_title", +] -def path_to_title(path): +def path_to_title(path: str) -> str: """ - Converts given file path to title. + Convert given file path to title. Parameters ---------- - path : unicode + path File path to convert to title. Returns ------- - unicode + :class:`str` File path converted to title. Examples -------- - >>> # Doctests skip for Python 2.x compatibility. >>> path_to_title( ... 'colour/io/luts/tests/resources/sony_spi3d/Colour_Correct.spi3d' - ... ) # doctest: +SKIP - u'ColourCorrect' + ... ) + 'Colour Correct' """ - return re.sub('_|-|\\.', ' ', os.path.splitext(os.path.basename(path))[0]) + return re.sub("_|-|\\.", " ", os.path.splitext(os.path.basename(path))[0]) diff --git a/colour/io/luts/iridas_cube.py b/colour/io/luts/iridas_cube.py index 133fa51618..88c8527adb 100644 --- a/colour/io/luts/iridas_cube.py +++ b/colour/io/luts/iridas_cube.py @@ -1,9 +1,9 @@ -# -*- coding: utf-8 -*- """ Iridas .cube LUT Format Input / Output Utilities ================================================ -Defines *Iridas* *.cube* *LUT* Format related input / output utilities objects. +Defines the *Iridas* *.cube* *LUT* format related input / output utilities +objects: - :func:`colour.io.read_LUT_IridasCube` - :func:`colour.io.write_LUT_IridasCube` @@ -14,37 +14,45 @@ https://drive.google.com/open?id=143Eh08ZYncCAMwJ1q4gWxVOqR_OSWYvs """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.constants import DEFAULT_INT_DTYPE from colour.io.luts import LUT1D, LUT3x1D, LUT3D, LUTSequence from colour.io.luts.common import path_to_title -from colour.utilities import as_float_array, usage_warning - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['read_LUT_IridasCube', 'write_LUT_IridasCube'] - - -def read_LUT_IridasCube(path): +from colour.hints import Boolean, Integer, List, Tuple, Union +from colour.utilities import ( + as_float_array, + as_int_scalar, + attest, + usage_warning, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "read_LUT_IridasCube", + "write_LUT_IridasCube", +] + + +def read_LUT_IridasCube(path: str) -> Union[LUT3x1D, LUT3D]: """ - Reads given *Iridas* *.cube* *LUT* file. + Read given *Iridas* *.cube* *LUT* file. Parameters ---------- - path : unicode + path *LUT* path. Returns ------- - LUT3x1D or LUT3d + :class:`LUT3x1D` or :class:`LUT3D`. :class:`LUT3x1D` or :class:`LUT3D` class instance. References @@ -100,9 +108,9 @@ def read_LUT_IridasCube(path): title = path_to_title(path) domain_min, domain_max = np.array([0, 0, 0]), np.array([1, 1, 1]) - dimensions = 3 - size = 2 - table = [] + dimensions: Integer = 3 + size: Integer = 2 + data = [] comments = [] with open(path) as cube_file: @@ -113,63 +121,71 @@ def read_LUT_IridasCube(path): if len(line) == 0: continue - if line.startswith('#'): + if line.startswith("#"): comments.append(line[1:].strip()) continue tokens = line.split() - if tokens[0] == 'TITLE': - title = ' '.join(tokens[1:])[1:-1] - elif tokens[0] == 'DOMAIN_MIN': + if tokens[0] == "TITLE": + title = " ".join(tokens[1:])[1:-1] + elif tokens[0] == "DOMAIN_MIN": domain_min = as_float_array(tokens[1:]) - elif tokens[0] == 'DOMAIN_MAX': + elif tokens[0] == "DOMAIN_MAX": domain_max = as_float_array(tokens[1:]) - elif tokens[0] == 'LUT_1D_SIZE': + elif tokens[0] == "LUT_1D_SIZE": dimensions = 2 - size = DEFAULT_INT_DTYPE(tokens[1]) - elif tokens[0] == 'LUT_3D_SIZE': + size = as_int_scalar(tokens[1]) + elif tokens[0] == "LUT_3D_SIZE": dimensions = 3 - size = DEFAULT_INT_DTYPE(tokens[1]) + size = as_int_scalar(tokens[1]) else: - table.append(tokens) + data.append(tokens) - table = as_float_array(table) + table = as_float_array(data) + + LUT: Union[LUT3x1D, LUT3D] if dimensions == 2: - return LUT3x1D( + LUT = LUT3x1D( table, title, np.vstack([domain_min, domain_max]), - comments=comments) + comments=comments, + ) elif dimensions == 3: # The lines of table data shall be in ascending index order, # with the first component index (Red) changing most rapidly, # and the last component index (Blue) changing least rapidly. - table = table.reshape([size, size, size, 3], order='F') + table = table.reshape([size, size, size, 3], order="F") - return LUT3D( + LUT = LUT3D( table, title, np.vstack([domain_min, domain_max]), - comments=comments) + comments=comments, + ) + + return LUT -def write_LUT_IridasCube(LUT, path, decimals=7): +def write_LUT_IridasCube( + LUT: Union[LUT3x1D, LUT3D, LUTSequence], path: str, decimals: Integer = 7 +) -> Boolean: """ - Writes given *LUT* to given *Iridas* *.cube* *LUT* file. + Write given *LUT* to given *Iridas* *.cube* *LUT* file. Parameters ---------- - LUT : LUT3x1D or LUT3d or LUTSequence + LUT :class:`LUT3x1D`, :class:`LUT3D` or :class:`LUTSequence` class instance to write at given path. - path : unicode + path *LUT* path. - decimals : int, optional + decimals Formatting decimals. Returns ------- - bool + :class:`bool` Definition success. Warnings @@ -192,7 +208,7 @@ def write_LUT_IridasCube(LUT, path, decimals=7): ... 'My LUT', ... domain, ... comments=['A first comment.', 'A second comment.']) - >>> write_LUT_IridasCube(LUT, 'My_LUT.cube') # doctest: +SKIP + >>> write_LUT_IridasCube(LUTxD, 'My_LUT.cube') # doctest: +SKIP Writing a 3D *Iridas* *.cube* *LUT*: @@ -202,61 +218,62 @@ def write_LUT_IridasCube(LUT, path, decimals=7): ... 'My LUT', ... np.array([[-0.1, -0.2, -0.4], [1.5, 3.0, 6.0]]), ... comments=['A first comment.', 'A second comment.']) - >>> write_LUT_IridasCube(LUT, 'My_LUT.cube') # doctest: +SKIP + >>> write_LUT_IridasCube(LUTxD, 'My_LUT.cube') # doctest: +SKIP """ if isinstance(LUT, LUTSequence): - LUT = LUT[0] - usage_warning('"LUT" is a "LUTSequence" instance was passed, ' - 'using first sequence "LUT":\n' - '{0}'.format(LUT)) - - assert not LUT.is_domain_explicit(), '"LUT" domain must be implicit!' + usage_warning( + f'"LUT" is a "LUTSequence" instance was passed, ' + f'using first sequence "LUT":\n{LUT}' + ) + LUTxD = LUT[0] + elif isinstance(LUT, LUT1D): + LUTxD = LUT.as_LUT(LUT3x1D) + else: + LUTxD = LUT - if isinstance(LUT, LUT1D): - LUT = LUT.as_LUT(LUT3x1D) + attest( + isinstance(LUTxD, (LUT3x1D, LUT3D)), + '"LUT" must be a 1D, 3x1D or 3D "LUT"!', + ) - assert (isinstance(LUT, LUT3x1D) or - isinstance(LUT, LUT3D)), '"LUT" must be a 1D, 3x1D or 3D "LUT"!' + attest(not LUTxD.is_domain_explicit(), '"LUT" domain must be implicit!') - is_3x1D = isinstance(LUT, LUT3x1D) + is_3x1D = isinstance(LUTxD, LUT3x1D) - size = LUT.size + size = LUTxD.size if is_3x1D: - assert 2 <= size <= 65536, '"LUT" size must be in domain [2, 65536]!' + attest(2 <= size <= 65536, '"LUT" size must be in domain [2, 65536]!') else: - assert 2 <= size <= 256, '"LUT" size must be in domain [2, 256]!' + attest(2 <= size <= 256, '"LUT" size must be in domain [2, 256]!') - def _format_array(array): - """ - Formats given array as an *Iridas* *.cube* data row. - """ + def _format_array(array: Union[List, Tuple]) -> str: + """Format given array as an *Iridas* *.cube* data row.""" - return '{1:0.{0}f} {2:0.{0}f} {3:0.{0}f}'.format(decimals, *array) + return "{1:0.{0}f} {2:0.{0}f} {3:0.{0}f}".format(decimals, *array) - with open(path, 'w') as cube_file: - cube_file.write('TITLE "{0}"\n'.format(LUT.name)) + with open(path, "w") as cube_file: + cube_file.write(f'TITLE "{LUTxD.name}"\n') - if LUT.comments: - for comment in LUT.comments: - cube_file.write('# {0}\n'.format(comment)) + if LUTxD.comments: + for comment in LUTxD.comments: + cube_file.write(f"# {comment}\n") - cube_file.write('{0} {1}\n'.format( - 'LUT_1D_SIZE' if is_3x1D else 'LUT_3D_SIZE', LUT.table.shape[0])) + cube_file.write( + f"{'LUT_1D_SIZE' if is_3x1D else 'LUT_3D_SIZE'} {LUTxD.table.shape[0]}\n" + ) default_domain = np.array([[0, 0, 0], [1, 1, 1]]) - if not np.array_equal(LUT.domain, default_domain): - cube_file.write('DOMAIN_MIN {0}\n'.format( - _format_array(LUT.domain[0]))) - cube_file.write('DOMAIN_MAX {0}\n'.format( - _format_array(LUT.domain[1]))) + if not np.array_equal(LUTxD.domain, default_domain): + cube_file.write(f"DOMAIN_MIN {_format_array(LUTxD.domain[0])}\n") + cube_file.write(f"DOMAIN_MAX {_format_array(LUTxD.domain[1])}\n") if not is_3x1D: - table = LUT.table.reshape([-1, 3], order='F') + table = LUTxD.table.reshape([-1, 3], order="F") else: - table = LUT.table + table = LUTxD.table for row in table: - cube_file.write('{0}\n'.format(_format_array(row))) + cube_file.write(f"{_format_array(row)}\n") return True diff --git a/colour/io/luts/lut.py b/colour/io/luts/lut.py index fad491c4f4..48a1facf19 100644 --- a/colour/io/luts/lut.py +++ b/colour/io/luts/lut.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ LUT Processing ============== @@ -8,77 +7,108 @@ - :class:`colour.LUT1D` - :class:`colour.LUT3x1D` - :class:`colour.LUT3D` -- :class:`colour.LUTSequence` - :class:`colour.io.LUT_to_LUT` """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -import re -from abc import ABCMeta, abstractmethod -try: - from collections import MutableSequence -except ImportError: - from collections.abc import MutableSequence +from abc import ABC, abstractmethod from copy import deepcopy -# pylint: disable=W0622 -from operator import add, mul, pow, sub, iadd, imul, ipow, isub - -# Python 3 compatibility. -try: - from operator import div, idiv -except ImportError: - from operator import truediv, itruediv - - div = truediv - idiv = itruediv -from six import add_metaclass - -from colour.algebra import LinearInterpolator, table_interpolation_trilinear -from colour.constants import DEFAULT_INT_DTYPE -from colour.utilities import (as_float_array, is_numeric, is_iterable, - is_string, full, linear_conversion, - runtime_warning, tsplit, tstack, usage_warning) -from colour.utilities.deprecation import handle_arguments_deprecation - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from operator import ( + add, + mul, + pow, + sub, + truediv, + iadd, + imul, + ipow, + isub, + itruediv, +) + +from colour.algebra import ( + Extrapolator, + LinearInterpolator, + linear_conversion, + table_interpolation_trilinear, +) +from colour.hints import ( + Any, + ArrayLike, + Boolean, + FloatingOrArrayLike, + Integer, + IntegerOrArrayLike, + List, + Literal, + NDArray, + Optional, + Sequence, + Type, + Union, + cast, +) +from colour.utilities import ( + as_float_array, + as_int, + as_int_array, + as_int_scalar, + attest, + is_numeric, + is_iterable, + is_string, + full, + optional, + required, + runtime_warning, + tsplit, + tstack, + usage_warning, + validate_method, +) +from colour.utilities.deprecation import ObjectRenamed + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'AbstractLUT', 'LUT1D', 'LUT3x1D', 'LUT3D', 'LUT_to_LUT', - 'AbstractLUTSequenceOperator', 'LUTSequence' + "AbstractLUT", + "LUT1D", + "LUT3x1D", + "LUT3D", + "LUT_to_LUT", ] -@add_metaclass(ABCMeta) -class AbstractLUT: +class AbstractLUT(ABC): """ - Defines the base class for *LUT*. + Define the base class for *LUT*. This is an :class:`ABCMeta` abstract class that must be inherited by sub-classes. Parameters ---------- - table : array_like, optional + table Underlying *LUT* table. - name : unicode, optional + name *LUT* name. - dimensions : int, optional + dimensions *LUT* dimensions, typically, 1 for a 1D *LUT*, 2 for a 3x1D *LUT* and 3 for a 3D *LUT*. - domain : unicode, optional + domain *LUT* domain, also used to define the instantiation time default table domain. - size : int, optional + size *LUT* size, also used to define the instantiation time default table size. - comments : array_like, optional + comments Comments to add to the *LUT*. Attributes @@ -110,484 +140,514 @@ class AbstractLUT: - :meth:`~colour.io.luts.lut.AbstractLUT.arithmetical_operation` - :meth:`~colour.io.luts.lut.AbstractLUT.is_domain_explicit` - :meth:`~colour.io.luts.lut.AbstractLUT.linear_table` - - :meth:`~colour.io.luts.lut.AbstractLUT.apply` - :meth:`~colour.io.luts.lut.AbstractLUT.copy` - - :meth:`~colour.io.luts.lut.AbstractLUT.as_LUT` + - :meth:`~colour.io.luts.lut.AbstractLUT.invert` + - :meth:`~colour.io.luts.lut.AbstractLUT.apply` + - :meth:`~colour.io.luts.lut.AbstractLUT.convert` """ - def __init__(self, - table=None, - name=None, - dimensions=None, - domain=None, - size=None, - comments=None): - default_name = ('Unity {0}'.format(size) - if table is None else '{0}'.format(id(self))) - self._name = default_name - self.name = name - - self._dimensions = dimensions - - # TODO: Re-enable when dropping Python 2.7. - # pylint: disable=E1121 - self._table = self.linear_table(size, domain) - self.table = table - self._domain = None - self.domain = domain - self._comments = [] - self.comments = comments + def __init__( + self, + table: Optional[ArrayLike] = None, + name: Optional[str] = None, + dimensions: Optional[Integer] = None, + domain: Optional[ArrayLike] = None, + size: Optional[IntegerOrArrayLike] = None, + comments: Optional[Sequence] = None, + ): + self._name: str = f"Unity {size!r}" if table is None else f"{id(self)}" + self.name = optional(name, self._name) + self._dimensions = optional(dimensions, 0) + self._table: NDArray = self.linear_table( + cast(ArrayLike, optional(size, 0)), + cast(ArrayLike, optional(domain, np.array([]))), + ) + self.table = cast( + ArrayLike, optional(table, self._table) + ) # type: ignore[assignment] + # TODO: Remove pragma when https://github.com/python/mypy/issues/3004 + # is resolved. + self._domain: NDArray = np.array([]) + self.domain = cast( + ArrayLike, optional(domain, self._domain) + ) # type: ignore[assignment] + self._comments: List = [] + self.comments = cast( + ArrayLike, optional(comments, self._comments) + ) # type: ignore[assignment] @property - def table(self): + def table(self) -> NDArray: """ Getter and setter property for the underlying *LUT* table. Parameters ---------- - value : unicode - Value to set the the underlying *LUT* table with. + value + Value to set the underlying *LUT* table with. Returns ------- - unicode + :class:`numpy.ndarray` Underlying *LUT* table. """ return self._table @table.setter - def table(self, value): - """ - Setter for **self.table** property. - """ + def table(self, value: ArrayLike): + """Setter for the **self.table** property.""" - if value is not None: - # pylint: disable=E1121 - self._table = self._validate_table(value) + self._table = self._validate_table(value) @property - def name(self): + def name(self) -> str: """ Getter and setter property for the *LUT* name. Parameters ---------- - value : unicode + value Value to set the *LUT* name with. Returns ------- - unicode + :class:`str` *LUT* name. """ return self._name @name.setter - def name(self, value): - """ - Setter for **self.name** property. - """ + def name(self, value: str): + """Setter for the **self.name** property.""" - if value is not None: - assert is_string(value), ( - ('"{0}" attribute: "{1}" type is not "str" or "unicode"!' - ).format('name', value)) + attest( + is_string(value), + f'"name" property: "{value}" type is not "str"!', + ) - self._name = value + self._name = value @property - def domain(self): + def domain(self) -> NDArray: """ Getter and setter property for the *LUT* domain. Parameters ---------- - value : unicode + value Value to set the *LUT* domain with. Returns ------- - unicode + :class:`numpy.ndarray` *LUT* domain. """ return self._domain @domain.setter - def domain(self, value): - """ - Setter for **self.domain** property. - """ + def domain(self, value: ArrayLike): + """Setter for the **self.domain** property.""" - if value is not None: - # pylint: disable=E1121 - self._domain = self._validate_domain(value) + # pylint: disable=E1121 + self._domain = self._validate_domain(value) @property - def dimensions(self): + def dimensions(self) -> Integer: """ Getter property for the *LUT* dimensions. Returns ------- - unicode + :class:`numpy.integer` *LUT* dimensions. """ return self._dimensions @property - def size(self): + def size(self) -> Integer: """ Getter property for the *LUT* size. Returns ------- - unicode + :class:`numpy.integer` *LUT* size. """ return self._table.shape[0] @property - def comments(self): + def comments(self) -> List: """ Getter and setter property for the *LUT* comments. Parameters ---------- - value : unicode + value Value to set the *LUT* comments with. Returns ------- - unicode + :class:`list` *LUT* comments. """ return self._comments @comments.setter - def comments(self, value): - """ - Setter for **self.comments** property. - """ + def comments(self, value: Sequence): + """Setter for the **self.comments** property.""" - if value is not None: - assert is_iterable(value), (( - '"{0}" attribute: "{1}" must be an array like!').format( - 'comments', value)) - self._comments = value + attest( + is_iterable(value), + f'"comments" property: "{value}" must be a sequence!', + ) - def __str__(self): + self._comments = list(value) + + def __str__(self) -> str: """ - Returns a formatted string representation of the *LUT*. + Return a formatted string representation of the *LUT*. Returns ------- - unicode + :class:`str` Formatted string representation. """ - def _indent_array(a): - """ - Indents given array string representation. - """ + def _indent_array(a: ArrayLike) -> str: + """Indent given array string representation.""" - return str(a).replace(' [', ' ' * 14 + '[') + return str(a).replace(" [", " " * 14 + "[") - comments = [ - 'Comment {0} : {1}'.format(str(i + 1).zfill(2), comment) - for i, comment in enumerate(self.comments) - ] + comments = "\n".join( + [ + f"Comment {str(i + 1).zfill(2)} : {comment}" + for i, comment in enumerate(self.comments) + ] + ) + comments = f"\n{comments}" if comments else "" - return ('{0} - {1}\n' - '{2}\n\n' - 'Dimensions : {3}\n' - 'Domain : {4}\n' - 'Size : {5!s}{6}').format( - self.__class__.__name__, self.name, - '-' * (len(self.__class__.__name__) + 3 + len(self.name)), - self.dimensions, _indent_array(self.domain), - str(self.table.shape).replace("L", ""), '\n{0}'.format( - '\n'.join(comments)) if comments else '') + underline = "-" * (len(self.__class__.__name__) + 3 + len(self.name)) - def __repr__(self): + return ( + f"{self.__class__.__name__} - {self.name}\n" + f"{underline}\n\n" + f"Dimensions : {self.dimensions}\n" + f"Domain : {_indent_array(self.domain)}\n" + f'Size : {str(self.table.shape).replace("L", "")}' + f"{comments}" + ) + + def __repr__(self) -> str: """ - Returns an evaluable string representation of the *LUT*. + Return an evaluable string representation of the *LUT*. Returns ------- - unicode + :class:`str` Evaluable string representation. """ representation = repr(self.table) - representation = representation.replace('array', - self.__class__.__name__) representation = representation.replace( - ' [', - '{0}['.format(' ' * (len(self.__class__.__name__) + 2))) + "array", self.__class__.__name__ + ) + representation = representation.replace( + " [", f"{' ' * (len(self.__class__.__name__) + 2)}[" + ) - domain = repr(self.domain).replace('array(', '').replace(')', '') + domain = repr(self.domain).replace("array(", "").replace(")", "") domain = domain.replace( - ' [', - '{0}['.format(' ' * (len(self.__class__.__name__) + 9))) + " [", f"{' ' * (len(self.__class__.__name__) + 9)}[" + ) - indentation = ' ' * (len(self.__class__.__name__) + 1) - representation = ('{0},\n' - '{1}name=\'{2}\',\n' - '{1}domain={3}{4})').format( - representation[:-1], indentation, self.name, - domain, ',\n{0}comments={1}'.format( - indentation, repr(self.comments)) - if self.comments else '') + indentation = " " * (len(self.__class__.__name__) + 1) + comments = ( + f",\n\n{indentation}comments={repr(self.comments)}" + if self.comments + else "" + ) - return representation + return ( + f"{representation[:-1]},\n" + f"{indentation}name='{self.name}',\n" + f"{indentation}domain={domain}" + f"{comments})" + ) - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: """ - Returns whether the *LUT* is equal to given other object. + Return whether the *LUT* is equal to given other object. Parameters ---------- - other : object + other Object to test whether it is equal to the *LUT*. Returns ------- - bool - Is given object equal to the *LUT*. + :class:`bool` + Whether given object is equal to the *LUT*. """ if isinstance(other, AbstractLUT): - if all([ + if all( + [ np.array_equal(self.table, other.table), - np.array_equal(self.domain, other.domain) - ]): + np.array_equal(self.domain, other.domain), + ] + ): return True return False - def __ne__(self, other): + def __ne__(self, other: Any) -> bool: """ - Returns whether the *LUT* is not equal to given other object. + Return whether the *LUT* is not equal to given other object. Parameters ---------- - other : object + other Object to test whether it is not equal to the *LUT*. Returns ------- - bool - Is given object not equal to the *LUT*. + :class:`bool` + Whether given object is not equal to the *LUT*. """ return not (self == other) - def __add__(self, a): + def __add__( + self, a: Union[FloatingOrArrayLike, AbstractLUT] + ) -> AbstractLUT: """ - Implements support for addition. + Implement support for addition. Parameters ---------- - a : numeric or array_like or AbstractLUT + a :math:`a` variable to add. Returns ------- - AbstractLUT + :class:`colour.io.luts.lut.AbstractLUT` Variable added *LUT*. """ - return self.arithmetical_operation(a, '+') + return self.arithmetical_operation(a, "+") - def __iadd__(self, a): + def __iadd__( + self, a: Union[FloatingOrArrayLike, AbstractLUT] + ) -> AbstractLUT: """ - Implements support for in-place addition. + Implement support for in-place addition. Parameters ---------- - a : numeric or array_like or AbstractLUT + a :math:`a` variable to add in-place. Returns ------- - AbstractLUT + :class:`colour.io.luts.lut.AbstractLUT` In-place variable added *LUT*. """ - return self.arithmetical_operation(a, '+', True) + return self.arithmetical_operation(a, "+", True) - def __sub__(self, a): + def __sub__( + self, a: Union[FloatingOrArrayLike, AbstractLUT] + ) -> AbstractLUT: """ - Implements support for subtraction. + Implement support for subtraction. Parameters ---------- - a : numeric or array_like or AbstractLUT + a :math:`a` variable to subtract. Returns ------- - AbstractLUT + :class:`colour.io.luts.lut.AbstractLUT` Variable subtracted *LUT*. """ - return self.arithmetical_operation(a, '-') + return self.arithmetical_operation(a, "-") - def __isub__(self, a): + def __isub__( + self, a: Union[FloatingOrArrayLike, AbstractLUT] + ) -> AbstractLUT: """ - Implements support for in-place subtraction. + Implement support for in-place subtraction. Parameters ---------- - a : numeric or array_like or AbstractLUT + a :math:`a` variable to subtract in-place. Returns ------- - AbstractLUT + :class:`colour.io.luts.lut.AbstractLUT` In-place variable subtracted *LUT*. """ - return self.arithmetical_operation(a, '-', True) + return self.arithmetical_operation(a, "-", True) - def __mul__(self, a): + def __mul__( + self, a: Union[FloatingOrArrayLike, AbstractLUT] + ) -> AbstractLUT: """ - Implements support for multiplication. + Implement support for multiplication. Parameters ---------- - a : numeric or array_like or AbstractLUT + a :math:`a` variable to multiply by. Returns ------- - AbstractLUT + :class:`colour.io.luts.lut.AbstractLUT` Variable multiplied *LUT*. """ - return self.arithmetical_operation(a, '*') + return self.arithmetical_operation(a, "*") - def __imul__(self, a): + def __imul__( + self, a: Union[FloatingOrArrayLike, AbstractLUT] + ) -> AbstractLUT: """ - Implements support for in-place multiplication. + Implement support for in-place multiplication. Parameters ---------- - a : numeric or array_like or AbstractLUT + a :math:`a` variable to multiply by in-place. Returns ------- - AbstractLUT + :class:`colour.io.luts.lut.AbstractLUT` In-place variable multiplied *LUT*. """ - return self.arithmetical_operation(a, '*', True) + return self.arithmetical_operation(a, "*", True) - def __div__(self, a): + def __div__( + self, a: Union[FloatingOrArrayLike, AbstractLUT] + ) -> AbstractLUT: """ - Implements support for division. + Implement support for division. Parameters ---------- - a : numeric or array_like or AbstractLUT + a :math:`a` variable to divide by. Returns ------- - AbstractLUT + :class:`colour.io.luts.lut.AbstractLUT` Variable divided *LUT*. """ - return self.arithmetical_operation(a, '/') + return self.arithmetical_operation(a, "/") - def __idiv__(self, a): + def __idiv__( + self, a: Union[FloatingOrArrayLike, AbstractLUT] + ) -> AbstractLUT: """ - Implements support for in-place division. + Implement support for in-place division. Parameters ---------- - a : numeric or array_like or AbstractLUT + a :math:`a` variable to divide by in-place. Returns ------- - AbstractLUT + :class:`colour.io.luts.lut.AbstractLUT` In-place variable divided *LUT*. """ - return self.arithmetical_operation(a, '/', True) + return self.arithmetical_operation(a, "/", True) __itruediv__ = __idiv__ __truediv__ = __div__ - def __pow__(self, a): + def __pow__( + self, a: Union[FloatingOrArrayLike, AbstractLUT] + ) -> AbstractLUT: """ - Implements support for exponentiation. + Implement support for exponentiation. Parameters ---------- - a : numeric or array_like or AbstractLUT + a :math:`a` variable to exponentiate by. Returns ------- - AbstractLUT + :class:`colour.io.luts.lut.AbstractLUT` Variable exponentiated *LUT*. """ - return self.arithmetical_operation(a, '**') + return self.arithmetical_operation(a, "**") - def __ipow__(self, a): + def __ipow__( + self, a: Union[FloatingOrArrayLike, AbstractLUT] + ) -> AbstractLUT: """ - Implements support for in-place exponentiation. + Implement support for in-place exponentiation. Parameters ---------- - a : numeric or array_like or AbstractLUT + a :math:`a` variable to exponentiate by in-place. Returns ------- - AbstractLUT + :class:`colour.io.luts.lut.AbstractLUT` In-place variable exponentiated *LUT*. """ - return self.arithmetical_operation(a, '**', True) + return self.arithmetical_operation(a, "**", True) - def arithmetical_operation(self, a, operation, in_place=False): + def arithmetical_operation( + self, + a: Union[FloatingOrArrayLike, AbstractLUT], + operation: Literal["+", "-", "*", "/", "**"], + in_place: Boolean = False, + ) -> AbstractLUT: """ - Performs given arithmetical operation with :math:`a` operand, the + Perform given arithmetical operation with :math:`a` operand, the operation can be either performed on a copy or in-place, must be reimplemented by sub-classes. Parameters ---------- - a : numeric or ndarray or AbstractLUT + a Operand. - operation : object + operation Operation to perform. - in_place : bool, optional + in_place Operation happens in place. Returns ------- - AbstractLUT + :class:`colour.io.luts.lut.AbstractLUT` *LUT*. """ - operation, ioperator = { - '+': (add, iadd), - '-': (sub, isub), - '*': (mul, imul), - '/': (div, idiv), - '**': (pow, ipow) + operator, ioperator = { + "+": (add, iadd), + "-": (sub, isub), + "*": (mul, imul), + "/": (truediv, itruediv), + "**": (pow, ipow), }[operation] if in_place: @@ -596,7 +656,7 @@ def arithmetical_operation(self, a, operation, in_place=False): else: operand = as_float_array(a) - self.table = ioperator(self.table, operand) + self.table = operator(self.table, operand) return self else: @@ -605,45 +665,45 @@ def arithmetical_operation(self, a, operation, in_place=False): return copy @abstractmethod - def _validate_table(self, table): + def _validate_table(self, table: ArrayLike) -> NDArray: """ - Validates given table according to *LUT* dimensions. + Validate given table according to *LUT* dimensions. Parameters ---------- - table : array_like + table Table to validate. Returns ------- - ndarray + :class:`numpy.ndarray` Validated table as a :class:`ndarray` instance. """ pass @abstractmethod - def _validate_domain(self, domain): + def _validate_domain(self, domain: ArrayLike) -> NDArray: """ - Validates given domain according to *LUT* dimensions. + Validate given domain according to *LUT* dimensions. Parameters ---------- - domain : array_like + domain Domain to validate. Returns ------- - ndarray + :class:`numpy.ndarray` Validated domain as a :class:`ndarray` instance. """ pass @abstractmethod - def is_domain_explicit(self): + def is_domain_explicit(self) -> Boolean: """ - Returns whether the *LUT* domain is explicit (or implicit). + Return whether the *LUT* domain is explicit (or implicit). An implicit domain is defined by its shape only:: @@ -663,96 +723,134 @@ def is_domain_explicit(self): Returns ------- - bool + :class:`bool` Is *LUT* domain explicit. """ pass + @staticmethod @abstractmethod - def linear_table(size=None, domain=None): + def linear_table( + size: Optional[IntegerOrArrayLike] = None, + domain: Optional[ArrayLike] = None, + ) -> NDArray: """ - Returns a linear table of given size according to *LUT* dimensions. + Return a linear table of given size according to *LUT* dimensions. Parameters ---------- - size : int or array_like, optional + size Expected table size, for a 1D *LUT*, the number of output samples :math:`n` is equal to ``size``, for a 3x1D *LUT* :math:`n` is equal to ``size * 3`` or ``size[0] + size[1] + size[2]``, for a 3D *LUT* :math:`n` is equal to ``size**3 * 3`` or ``size[0] * size[1] * size[2] * 3``. - domain : array_like, optional + domain Domain of the table. Returns ------- - ndarray + :class:`numpy.ndarray` Linear table. """ pass + def copy(self) -> AbstractLUT: + """ + Return a copy of the sub-class instance. + + Returns + ------- + :class:`colour.io.luts.lut.AbstractLUT` + *LUT* copy. + """ + + return deepcopy(self) + @abstractmethod - def apply(self, RGB, interpolator, interpolator_kwargs): + def invert(self, **kwargs: Any) -> AbstractLUT: """ - Applies the *LUT* to given *RGB* colourspace array using given method. + Compute and returns an inverse copy of the *LUT*. - Parameters - ---------- - RGB : array_like - *RGB* colourspace array to apply the *LUT* onto. - interpolator : object, optional - Interpolator class type or object to use as interpolating function. - interpolator_kwargs : dict_like, optional - Arguments to use when instantiating or calling the interpolating - function. + Other Parameters + ---------------- + kwargs + Keywords arguments. Returns ------- - ndarray - Interpolated *RGB* colourspace array. + :class:`colour.io.luts.lut.AbstractLUT` + Inverse *LUT* class instance. """ pass - def copy(self): + @abstractmethod + def apply(self, RGB: ArrayLike, **kwargs: Any) -> NDArray: """ - Returns a copy of the sub-class instance. + Apply the *LUT* to given *RGB* colourspace array using given method. + + Parameters + ---------- + RGB + *RGB* colourspace array to apply the *LUT* onto. + + Other Parameters + ---------------- + direction + Whether the *LUT* should be applied in the forward or inverse + direction. + extrapolator + Extrapolator class type or object to use as extrapolating function. + extrapolator_kwargs + Arguments to use when instantiating or calling the extrapolating + function. + interpolator + Interpolator class type or object to use as interpolating function. + interpolator_kwargs + Arguments to use when instantiating or calling the interpolating + function. Returns ------- - AbstractLUT - *LUT* copy. + :class:`numpy.ndarray` + Interpolated *RGB* colourspace array. """ - return deepcopy(self) + pass @abstractmethod - def as_LUT(self, cls, force_conversion, **kwargs): + def convert( + self, + cls: Type[AbstractLUT], + force_conversion: Boolean = False, + **kwargs: Any, + ) -> AbstractLUT: """ - Converts the *LUT* to given ``cls`` class instance. + Convert the *LUT* to given ``cls`` class instance. Parameters ---------- - cls : LUT1D or LUT3x1D or LUT3D + cls *LUT* class instance. - force_conversion : bool, optional + force_conversion Whether to force the conversion as it might be destructive. Other Parameters ---------------- - interpolator : object, optional + interpolator Interpolator class type to use as interpolating function. - interpolator_kwargs : dict_like, optional + interpolator_kwargs Arguments to use when instantiating the interpolating function. - size : int, optional + size Expected table size in case of an upcast to or a downcast from a :class:`LUT3D` class instance. Returns ------- - LUT1D or LUT3x1D or LUT3D + :class:`colour.io.luts.lut.AbstractLUT` Converted *LUT* class instance. Warnings @@ -771,20 +869,20 @@ def as_LUT(self, cls, force_conversion, **kwargs): class LUT1D(AbstractLUT): """ - Defines the base class for a 1D *LUT*. + Define the base class for a 1D *LUT*. Parameters ---------- - table : array_like, optional + table Underlying *LUT* table. - name : unicode, optional + name *LUT* name. - domain : unicode, optional + domain *LUT* domain, also used to define the instantiation time default table domain. - size : int, optional - Size of the instantiation time default table. - comments : array_like, optional + size + Size of the instantiation time default table, default to 10. + comments Comments to add to the *LUT*. Methods @@ -792,8 +890,9 @@ class LUT1D(AbstractLUT): - :meth:`~colour.LUT1D.__init__` - :meth:`~colour.LUT1D.is_domain_explicit` - :meth:`~colour.LUT1D.linear_table` + - :meth:`~colour.LUT1D.invert` - :meth:`~colour.LUT1D.apply` - - :meth:`~colour.LUT1D.as_LUT` + - :meth:`~colour.LUT1D.convert` Examples -------- @@ -837,65 +936,72 @@ class LUT1D(AbstractLUT): Comment 02 : A second comment. """ - def __init__(self, - table=None, - name=None, - domain=None, - size=10, - comments=None): - if domain is None: - domain = np.array([0, 1]) + def __init__( + self, + table: Optional[ArrayLike] = None, + name: Optional[str] = None, + domain: Optional[ArrayLike] = None, + size: Optional[IntegerOrArrayLike] = None, + comments: Optional[Sequence] = None, + ): + + domain = as_float_array( + cast(ArrayLike, optional(domain, np.array([0, 1]))) + ) + size = cast(Integer, optional(size, 10)) - super(LUT1D, self).__init__(table, name, 1, domain, size, comments) + super().__init__(table, name, 1, domain, size, comments) - def _validate_table(self, table): + def _validate_table(self, table: ArrayLike) -> NDArray: """ - Validates given table is a 1D array. + Validate given table is a 1D array. Parameters ---------- - table : array_like + table Table to validate. Returns ------- - ndarray + :class:`numpy.ndarray` Validated table as a :class:`ndarray` instance. """ table = as_float_array(table) - assert len(table.shape) == 1, 'The table must be a 1D array!' + attest(len(table.shape) == 1, "The table must be a 1D array!") return table - def _validate_domain(self, domain): + def _validate_domain(self, domain: ArrayLike) -> NDArray: """ - Validates given domain. + Validate given domain. Parameters ---------- - domain : array_like + domain Domain to validate. Returns ------- - ndarray + :class:`numpy.ndarray` Validated domain as a :class:`ndarray` instance. """ domain = as_float_array(domain) - assert len(domain.shape) == 1, 'The domain must be a 1D array!' + attest(len(domain.shape) == 1, "The domain must be a 1D array!") - assert domain.shape[0] >= 2, ( - 'The domain column count must be equal or greater than 2!') + attest( + domain.shape[0] >= 2, + "The domain column count must be equal or greater than 2!", + ) return domain - def is_domain_explicit(self): + def is_domain_explicit(self) -> Boolean: """ - Returns whether the *LUT* domain is explicit (or implicit). + Return whether the *LUT* domain is explicit (or implicit). An implicit domain is defined by its shape only:: @@ -907,7 +1013,7 @@ def is_domain_explicit(self): Returns ------- - bool + :class:`bool` Is *LUT* domain explicit. Examples @@ -921,23 +1027,25 @@ def is_domain_explicit(self): return len(self.domain) != 2 - # pylint: disable=W0221 @staticmethod - def linear_table(size=10, domain=np.array([0, 1])): + def linear_table( + size: Optional[IntegerOrArrayLike] = None, + domain: Optional[ArrayLike] = None, + ) -> NDArray: """ - Returns a linear table, the number of output samples :math:`n` is equal + Return a linear table, the number of output samples :math:`n` is equal to ``size``. Parameters ---------- - size : int, optional - Expected table size. - domain : array_like, optional + size + Expected table size, default to 10. + domain Domain of the table. Returns ------- - ndarray + :class:`numpy.ndarray` Linear table with ``size`` samples. Examples @@ -948,93 +1056,170 @@ def linear_table(size=10, domain=np.array([0, 1])): array([-0.1, 0.3, 0.7, 1.1, 1.5]) """ - domain = as_float_array(domain) + size = cast(Integer, optional(size, 10)) + domain = as_float_array( + cast(ArrayLike, optional(domain, np.array([0, 1]))) + ) if len(domain) != 2: return domain else: - assert is_numeric(size), 'Linear table size must be a numeric!' + attest(is_numeric(size), "Linear table size must be a numeric!") + + return np.linspace(domain[0], domain[1], as_int_scalar(size)) + + def invert(self, **kwargs: Any) -> LUT1D: + """ + Compute and returns an inverse copy of the *LUT*. + + Other Parameters + ---------------- + kwargs + Keywords arguments, only given for signature compatibility with + the :meth:`AbstractLUT.invert` method. + + Returns + ------- + :class:`colour.LUT1D` + Inverse *LUT* class instance. + + Examples + -------- + >>> LUT = LUT1D(LUT1D.linear_table() ** (1 / 2.2)) + >>> print(LUT.table) # doctest: +ELLIPSIS + [ 0. ... 0.3683438... 0.5047603... 0.6069133... \ +0.6916988... 0.7655385... + 0.8316843... 0.8920493... 0.9478701... 1. ] + >>> print(LUT.invert()) # doctest: +ELLIPSIS + LUT1D - ... - Inverse + --------...---------- + + Dimensions : 1 + Domain : [ 0. 0.3683438... 0.5047603... 0.6069133... \ +0.6916988... 0.7655385... + 0.8316843... 0.8920493... 0.9478701... 1. ] + Size : (10,) + >>> print(LUT.invert().table) # doctest: +ELLIPSIS + [ 0. ... 0.1111111... 0.2222222... 0.3333333... \ +0.4444444... 0.5555555... + 0.6666666... 0.7777777... 0.8888888... 1. ] + """ + + if self.is_domain_explicit(): + domain = self.domain + else: + domain_min, domain_max = self.domain + domain = np.linspace(domain_min, domain_max, self.size) - return np.linspace(domain[0], domain[1], size) + LUT_i = LUT1D( + table=domain, + name=f"{self.name} - Inverse", + domain=self.table, + ) - def apply(self, - RGB, - interpolator=LinearInterpolator, - interpolator_kwargs=None, - **kwargs): + return LUT_i + + def apply(self, RGB: ArrayLike, **kwargs: Any) -> NDArray: """ - Applies the *LUT* to given *RGB* colourspace array using given method. + Apply the *LUT* to given *RGB* colourspace array using given method. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array to apply the *LUT* onto. - interpolator : object, optional - Interpolator class type to use as interpolating function. - interpolator_kwargs : dict_like, optional - Arguments to use when instantiating the interpolating function. Other Parameters ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. + direction + Whether the *LUT* should be applied in the forward or inverse + direction. + extrapolator + Extrapolator class type or object to use as extrapolating function. + extrapolator_kwargs + Arguments to use when instantiating or calling the extrapolating + function. + interpolator + Interpolator class type to use as interpolating function. + interpolator_kwargs + Arguments to use when instantiating the interpolating function. Returns ------- - ndarray + :class:`numpy.ndarray` Interpolated *RGB* colourspace array. Examples -------- >>> LUT = LUT1D(LUT1D.linear_table() ** (1 / 2.2)) >>> RGB = np.array([0.18, 0.18, 0.18]) + + *LUT* applied to the given *RGB* colourspace in the forward direction: + >>> LUT.apply(RGB) # doctest: +ELLIPSIS array([ 0.4529220..., 0.4529220..., 0.4529220...]) + + *LUT* applied to the modified *RGB* colourspace in the inverse + direction: + + >>> LUT.apply(LUT.apply(RGB), direction='Inverse') + ... # doctest: +ELLIPSIS + array([ 0.18..., 0.18..., 0.18...]) """ - interpolator_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['interpolator_args', 'interpolator_kwargs']], - }, **kwargs).get('interpolator_kwargs', interpolator_kwargs) + direction = validate_method( + kwargs.get("direction", "Forward"), ["Forward", "Inverse"] + ) - if interpolator_kwargs is None: - interpolator_kwargs = {} + interpolator = kwargs.get("interpolator", LinearInterpolator) + interpolator_kwargs = kwargs.get("interpolator_kwargs", {}) + extrapolator = kwargs.get("extrapolator", Extrapolator) + extrapolator_kwargs = kwargs.get("extrapolator_kwargs", {}) - if self.is_domain_explicit(): - samples = self.domain - else: - domain_min, domain_max = self.domain + LUT = self.invert() if direction == "inverse" else self - samples = np.linspace(domain_min, domain_max, self._table.size) + if LUT.is_domain_explicit(): + samples = LUT.domain + else: + domain_min, domain_max = LUT.domain + samples = np.linspace(domain_min, domain_max, LUT.size) - RGB_interpolator = interpolator(samples, self._table, - **interpolator_kwargs) + RGB_interpolator = extrapolator( + interpolator(samples, LUT.table, **interpolator_kwargs), + **extrapolator_kwargs, + ) return RGB_interpolator(RGB) - def as_LUT(self, cls, force_conversion=False, **kwargs): + def convert( + self, + cls: Type[AbstractLUT], + force_conversion: Boolean = False, + **kwargs: Any, + ) -> AbstractLUT: """ - Converts the *LUT* to given ``cls`` class instance. + Convert the *LUT* to given ``cls`` class instance. Parameters ---------- - cls : LUT1D or LUT3x1D or LUT3D + cls *LUT* class instance. - force_conversion : bool, optional + force_conversion Whether to force the conversion as it might be destructive. Other Parameters ---------------- - interpolator : object, optional + interpolator Interpolator class type to use as interpolating function. - interpolator_kwargs : dict_like, optional + interpolator_kwargs Arguments to use when instantiating the interpolating function. - size : int, optional + size Expected table size in case of an upcast to a :class:`LUT3D` class instance. Returns ------- - LUT1D or LUT3x1D or LUT3D + :class:`colour.LUT1D` or :class:`colour.LUT3x1D` or \ +:class:`colour.LUT3D` Converted *LUT* class instance. Warnings @@ -1050,14 +1235,14 @@ def as_LUT(self, cls, force_conversion=False, **kwargs): Examples -------- >>> LUT = LUT1D() - >>> print(LUT.as_LUT(LUT1D)) + >>> print(LUT.convert(LUT1D)) LUT1D - Unity 10 - Converted 1D to 1D ------------------------------------- Dimensions : 1 Domain : [ 0. 1.] Size : (10,) - >>> print(LUT.as_LUT(LUT3x1D)) + >>> print(LUT.convert(LUT3x1D)) LUT3x1D - Unity 10 - Converted 1D to 3x1D ----------------------------------------- @@ -1065,7 +1250,7 @@ def as_LUT(self, cls, force_conversion=False, **kwargs): Domain : [[ 0. 0. 0.] [ 1. 1. 1.]] Size : (10, 3) - >>> print(LUT.as_LUT(LUT3D, force_conversion=True)) + >>> print(LUT.convert(LUT3D, force_conversion=True)) LUT3D - Unity 10 - Converted 1D to 3D ------------------------------------- @@ -1077,23 +1262,44 @@ def as_LUT(self, cls, force_conversion=False, **kwargs): return LUT_to_LUT(self, cls, force_conversion, **kwargs) + # ------------------------------------------------------------------------# + # --- API Changes and Deprecation Management ---# + # ------------------------------------------------------------------------# + def as_LUT( + self, + cls: Type[AbstractLUT], + force_conversion: Boolean = False, + **kwargs: Any, + ) -> AbstractLUT: # pragma: no cover # noqa: D102 + # Docstrings are omitted for documentation purposes. + usage_warning( + str( + ObjectRenamed( + "LUT1D.as_LUT", + "LUT1D.convert", + ) + ) + ) + + return self.convert(cls, force_conversion, **kwargs) + class LUT3x1D(AbstractLUT): """ - Defines the base class for a 3x1D *LUT*. + Define the base class for a 3x1D *LUT*. Parameters ---------- - table : array_like, optional + table Underlying *LUT* table. - name : unicode, optional + name *LUT* name. - domain : unicode, optional + domain *LUT* domain, also used to define the instantiation time default table domain. - size : int, optional - Size of the instantiation time default table. - comments : array_like, optional + size + Size of the instantiation time default table, default to 10. + comments Comments to add to the *LUT*. Methods @@ -1101,8 +1307,9 @@ class LUT3x1D(AbstractLUT): - :meth:`~colour.LUT3x1D.__init__` - :meth:`~colour.LUT3x1D.is_domain_explicit` - :meth:`~colour.LUT3x1D.linear_table` + - :meth:`~colour.LUT3x1D.invert` - :meth:`~colour.LUT3x1D.apply` - - :meth:`~colour.LUT3x1D.as_LUT` + - :meth:`~colour.LUT3x1D.convert` Examples -------- @@ -1150,68 +1357,75 @@ class LUT3x1D(AbstractLUT): Comment 02 : A second comment. """ - def __init__(self, - table=None, - name=None, - domain=None, - size=10, - comments=None): - if domain is None: - domain = np.array([[0, 0, 0], [1, 1, 1]]) + def __init__( + self, + table: Optional[ArrayLike] = None, + name: Optional[str] = None, + domain: Optional[ArrayLike] = None, + size: Optional[IntegerOrArrayLike] = None, + comments: Optional[Sequence] = None, + ): + domain = cast( + ArrayLike, optional(domain, np.array([[0, 0, 0], [1, 1, 1]])) + ) + size = cast(Integer, optional(size, 10)) - super(LUT3x1D, self).__init__(table, name, 2, domain, size, comments) + super().__init__(table, name, 2, domain, size, comments) - def _validate_table(self, table): + def _validate_table(self, table: ArrayLike) -> NDArray: """ - Validates given table is a 3x1D array. + Validate given table is a 3x1D array. Parameters ---------- - table : array_like + table Table to validate. Returns ------- - ndarray + :class:`numpy.ndarray` Validated table as a :class:`ndarray` instance. """ table = as_float_array(table) - assert len(table.shape) == 2, 'The table must be a 2D array!' + attest(len(table.shape) == 2, "The table must be a 2D array!") return table - def _validate_domain(self, domain): + def _validate_domain(self, domain: ArrayLike) -> NDArray: """ - Validates given domain. + Validate given domain. Parameters ---------- - domain : array_like + domain Domain to validate. Returns ------- - ndarray + :class:`numpy.ndarray` Validated domain as a :class:`ndarray` instance. """ domain = as_float_array(domain) - assert len(domain.shape) == 2, 'The domain must be a 2D array!' + attest(len(domain.shape) == 2, "The domain must be a 2D array!") - assert domain.shape[0] >= 2, ( - 'The domain row count must be equal or greater than 2!') + attest( + domain.shape[0] >= 2, + "The domain row count must be equal or greater than 2!", + ) - assert domain.shape[1] == 3, ( - 'The domain column count must be equal to 3!') + attest( + domain.shape[1] == 3, "The domain column count must be equal to 3!" + ) return domain - def is_domain_explicit(self): + def is_domain_explicit(self) -> Boolean: """ - Returns whether the *LUT* domain is explicit (or implicit). + Return whether the *LUT* domain is explicit (or implicit). An implicit domain is defined by its shape only:: @@ -1231,7 +1445,7 @@ def is_domain_explicit(self): Returns ------- - bool + :class:`bool` Is *LUT* domain explicit. Examples @@ -1246,23 +1460,25 @@ def is_domain_explicit(self): return self.domain.shape != (2, 3) - # pylint: disable=W0221 @staticmethod - def linear_table(size=10, domain=np.array([[0, 0, 0], [1, 1, 1]])): + def linear_table( + size: Optional[IntegerOrArrayLike] = None, + domain: Optional[ArrayLike] = None, + ) -> NDArray: """ - Returns a linear table, the number of output samples :math:`n` is equal + Return a linear table, the number of output samples :math:`n` is equal to ``size * 3`` or ``size[0] + size[1] + size[2]``. Parameters ---------- - size : int or array_like, optional - Expected table size. - domain : array_like, optional + size + Expected table size, default to 10. + domain Domain of the table. Returns ------- - ndarray + :class:`numpy.ndarray` Linear table with ``size * 3`` or ``size[0] + size[1] + size[2]`` samples. @@ -1301,59 +1517,150 @@ def linear_table(size=10, domain=np.array([[0, 0, 0], [1, 1, 1]])): [ 1.5, nan, nan]]) """ - domain = as_float_array(domain) + size = cast(Integer, optional(size, 10)) + domain = as_float_array( + cast(ArrayLike, optional(domain, np.array([[0, 0, 0], [1, 1, 1]]))) + ) if domain.shape != (2, 3): return domain else: if is_numeric(size): - size = np.tile(size, 3) + size_array = np.tile(size, 3) + else: + size_array = as_int_array(size) R, G, B = tsplit(domain) samples = [ - np.linspace(a[0], a[1], size[i]) + np.linspace(a[0], a[1], size_array[i]) for i, a in enumerate([R, G, B]) ] - if not len(np.unique(size)) == 1: - runtime_warning('Table is non uniform, axis will be ' - 'padded with "NaNs" accordingly!') + if not len(np.unique(size_array)) == 1: + runtime_warning( + "Table is non uniform, axis will be " + 'padded with "NaNs" accordingly!' + ) samples = [ np.pad( - axis, (0, np.max(size) - len(axis)), - mode='constant', - constant_values=np.nan) for axis in samples + axis, + (0, np.max(size_array) - len(axis)), + mode="constant", + constant_values=np.nan, + ) + for axis in samples ] return tstack(samples) - def apply(self, - RGB, - interpolator=LinearInterpolator, - interpolator_kwargs=None, - **kwargs): + def invert(self, **kwargs: Any) -> LUT3x1D: + """ + Compute and returns an inverse copy of the *LUT*. + + Other Parameters + ---------------- + kwargs + Keywords arguments, only given for signature compatibility with + the :meth:`AbstractLUT.invert` method. + + Returns + ------- + :class:`colour.LUT3x1D` + Inverse *LUT* class instance. + + Examples + -------- + >>> LUT = LUT3x1D(LUT3x1D.linear_table() ** (1 / 2.2)) + >>> print(LUT.table) + [[ 0. 0. 0. ] + [ 0.36834383 0.36834383 0.36834383] + [ 0.50476034 0.50476034 0.50476034] + [ 0.60691337 0.60691337 0.60691337] + [ 0.69169882 0.69169882 0.69169882] + [ 0.76553851 0.76553851 0.76553851] + [ 0.83168433 0.83168433 0.83168433] + [ 0.89204934 0.89204934 0.89204934] + [ 0.94787016 0.94787016 0.94787016] + [ 1. 1. 1. ]] + >>> print(LUT.invert()) # doctest: +ELLIPSIS + LUT3x1D - ... - Inverse + ----------...---------- + + Dimensions : 2 + Domain : [[ 0. ... 0. ... 0. ...] + [ 0.3683438... 0.3683438... 0.3683438...] + [ 0.5047603... 0.5047603... 0.5047603...] + [ 0.6069133... 0.6069133... 0.6069133...] + [ 0.6916988... 0.6916988... 0.6916988...] + [ 0.7655385... 0.7655385... 0.7655385...] + [ 0.8316843... 0.8316843... 0.8316843...] + [ 0.8920493... 0.8920493... 0.8920493...] + [ 0.9478701... 0.9478701... 0.9478701...] + [ 1. ... 1. ... 1. ...]] + Size : (10, 3) + >>> print(LUT.invert().table) # doctest: +ELLIPSIS + [[ 0. ... 0. ... 0. ...] + [ 0.1111111... 0.1111111... 0.1111111...] + [ 0.2222222... 0.2222222... 0.2222222...] + [ 0.3333333... 0.3333333... 0.3333333...] + [ 0.4444444... 0.4444444... 0.4444444...] + [ 0.5555555... 0.5555555... 0.5555555...] + [ 0.6666666... 0.6666666... 0.6666666...] + [ 0.7777777... 0.7777777... 0.7777777...] + [ 0.8888888... 0.8888888... 0.8888888...] + [ 1. ... 1. ... 1. ...]] + """ + + size = self.table.size // 3 + if self.is_domain_explicit(): + domain = [ + axes[: (~np.isnan(axes)).cumsum().argmax() + 1] + for axes in np.transpose(self.domain) + ] + else: + domain_min, domain_max = self.domain + domain = [ + np.linspace(domain_min[i], domain_max[i], size) + for i in range(3) + ] + + LUT_i = LUT3x1D( + table=tstack(domain), + name=f"{self.name} - Inverse", + domain=self.table, + ) + + return LUT_i + + def apply(self, RGB: ArrayLike, **kwargs: Any) -> NDArray: """ - Applies the *LUT* to given *RGB* colourspace array using given method. + Apply the *LUT* to given *RGB* colourspace array using given method. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array to apply the *LUT* onto. - interpolator : object, optional - Interpolator class type to use as interpolating function. - interpolator_kwargs : dict_like, optional - Arguments to use when instantiating the interpolating function. Other Parameters ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. + direction + Whether the *LUT* should be applied in the forward or inverse + direction. + extrapolator + Extrapolator class type or object to use as extrapolating function. + extrapolator_kwargs + Arguments to use when instantiating or calling the extrapolating + function. + interpolator + Interpolator class type to use as interpolating function. + interpolator_kwargs + Arguments to use when instantiating the interpolating function. Returns ------- - ndarray + :class:`numpy.ndarray` Interpolated *RGB* colourspace array. Examples @@ -1362,6 +1669,9 @@ def apply(self, >>> RGB = np.array([0.18, 0.18, 0.18]) >>> LUT.apply(RGB) # doctest: +ELLIPSIS array([ 0.4529220..., 0.4529220..., 0.4529220...]) + >>> LUT.apply(LUT.apply(RGB), direction='Inverse') + ... # doctest: +ELLIPSIS + array([ 0.18..., 0.18..., 0.18...]) >>> from colour.algebra import spow >>> domain = np.array([[-0.1, -0.2, -0.4], [1.5, 3.0, 6.0]]) >>> table = spow(LUT3x1D.linear_table(domain=domain), 1 / 2.2) @@ -1381,67 +1691,79 @@ def apply(self, array([ 0.2996370..., -0.0901332..., -0.3949770...]) """ - interpolator_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['interpolator_args', 'interpolator_kwargs']], - }, **kwargs).get('interpolator_kwargs', interpolator_kwargs) + direction = validate_method( + kwargs.get("direction", "Forward"), ["Forward", "Inverse"] + ) - if interpolator_kwargs is None: - interpolator_kwargs = {} + interpolator = kwargs.get("interpolator", LinearInterpolator) + interpolator_kwargs = kwargs.get("interpolator_kwargs", {}) + extrapolator = kwargs.get("extrapolator", Extrapolator) + extrapolator_kwargs = kwargs.get("extrapolator_kwargs", {}) R, G, B = tsplit(RGB) - if self.is_domain_explicit(): + LUT = self.invert() if direction == "inverse" else self + + size = LUT.table.size // 3 + if LUT.is_domain_explicit(): samples = [ - axes[:(~np.isnan(axes)).cumsum().argmax() + 1] - for axes in np.transpose(self.domain) - ] - R_t, G_t, B_t = [ - axes[:len(samples[i])] - for i, axes in enumerate(np.transpose(self._table)) + axes[: (~np.isnan(axes)).cumsum().argmax() + 1] + for axes in np.transpose(LUT.domain) ] + R_t, G_t, B_t = ( + axes[: len(samples[i])] + for i, axes in enumerate(np.transpose(LUT.table)) + ) else: - domain_min, domain_max = self.domain - size = DEFAULT_INT_DTYPE(self._table.size / 3) + domain_min, domain_max = LUT.domain samples = [ np.linspace(domain_min[i], domain_max[i], size) for i in range(3) ] - - R_t, G_t, B_t = tsplit(self._table) + R_t, G_t, B_t = tsplit(LUT.table) s_R, s_G, s_B = samples RGB_i = [ - interpolator(a[0], a[1], **interpolator_kwargs)(a[2]) + extrapolator( + interpolator(a[0], a[1], **interpolator_kwargs), + **extrapolator_kwargs, + )(a[2]) for a in zip((s_R, s_G, s_B), (R_t, G_t, B_t), (R, G, B)) ] return tstack(RGB_i) - def as_LUT(self, cls, force_conversion=False, **kwargs): + def convert( + self, + cls: Type[AbstractLUT], + force_conversion: Boolean = False, + **kwargs: Any, + ) -> AbstractLUT: """ - Converts the *LUT* to given ``cls`` class instance. + Convert the *LUT* to given ``cls`` class instance. Parameters ---------- - cls : LUT1D or LUT3x1D or LUT3D + cls *LUT* class instance. - force_conversion : bool, optional + force_conversion Whether to force the conversion as it might be destructive. Other Parameters ---------------- - interpolator : object, optional + interpolator Interpolator class type to use as interpolating function. - interpolator_kwargs : dict_like, optional + interpolator_kwargs Arguments to use when instantiating the interpolating function. - size : int, optional + size Expected table size in case of an upcast to a :class:`LUT3D` class instance. Returns ------- - LUT1D or LUT3x1D or LUT3D + :class:`colour.LUT1D` or :class:`colour.LUT3x1D` or \ +:class:`colour.LUT3D` Converted *LUT* class instance. Warnings @@ -1457,14 +1779,14 @@ def as_LUT(self, cls, force_conversion=False, **kwargs): Examples -------- >>> LUT = LUT3x1D() - >>> print(LUT.as_LUT(LUT1D, force_conversion=True)) + >>> print(LUT.convert(LUT1D, force_conversion=True)) LUT1D - Unity 10 - Converted 3x1D to 1D --------------------------------------- Dimensions : 1 Domain : [ 0. 1.] Size : (10,) - >>> print(LUT.as_LUT(LUT3x1D)) + >>> print(LUT.convert(LUT3x1D)) LUT3x1D - Unity 10 - Converted 3x1D to 3x1D ------------------------------------------- @@ -1472,7 +1794,7 @@ def as_LUT(self, cls, force_conversion=False, **kwargs): Domain : [[ 0. 0. 0.] [ 1. 1. 1.]] Size : (10, 3) - >>> print(LUT.as_LUT(LUT3D, force_conversion=True)) + >>> print(LUT.convert(LUT3D, force_conversion=True)) LUT3D - Unity 10 - Converted 3x1D to 3D --------------------------------------- @@ -1484,23 +1806,44 @@ def as_LUT(self, cls, force_conversion=False, **kwargs): return LUT_to_LUT(self, cls, force_conversion, **kwargs) + # ------------------------------------------------------------------------# + # --- API Changes and Deprecation Management ---# + # ------------------------------------------------------------------------# + def as_LUT( + self, + cls: Type[AbstractLUT], + force_conversion: Boolean = False, + **kwargs: Any, + ) -> AbstractLUT: # pragma: no cover # noqa: D102 + # Docstrings are omitted for documentation purposes. + usage_warning( + str( + ObjectRenamed( + "LUT3x1D.as_LUT", + "LUT3x1D.convert", + ) + ) + ) + + return self.convert(cls, force_conversion, **kwargs) + class LUT3D(AbstractLUT): """ - Defines the base class for a 3D *LUT*. + Define the base class for a 3D *LUT*. Parameters ---------- - table : array_like, optional + table Underlying *LUT* table. - name : unicode, optional + name *LUT* name. - domain : unicode, optional + domain *LUT* domain, also used to define the instantiation time default table domain. - size : int, optional - Size of the instantiation time default table. - comments : array_like, optional + size + Size of the instantiation time default table, default to 33. + comments Comments to add to the *LUT*. Methods @@ -1508,8 +1851,9 @@ class LUT3D(AbstractLUT): - :meth:`~colour.LUT3D.__init__` - :meth:`~colour.LUT3D.is_domain_explicit` - :meth:`~colour.LUT3D.linear_table` + - :meth:`~colour.LUT3D.invert` - :meth:`~colour.LUT3D.apply` - - :meth:`~colour.LUT3D.as_LUT` + - :meth:`~colour.LUT3D.convert` Examples -------- @@ -1556,50 +1900,54 @@ class LUT3D(AbstractLUT): Comment 02 : A second comment. """ - def __init__(self, - table=None, - name=None, - domain=None, - size=33, - comments=None): - if domain is None: - domain = np.array([[0, 0, 0], [1, 1, 1]]) + def __init__( + self, + table: Optional[ArrayLike] = None, + name: Optional[str] = None, + domain: Optional[ArrayLike] = None, + size: Optional[IntegerOrArrayLike] = None, + comments: Optional[Sequence] = None, + ): + domain = cast( + ArrayLike, optional(domain, np.array([[0, 0, 0], [1, 1, 1]])) + ) + size = cast(Integer, optional(size, 33)) - super(LUT3D, self).__init__(table, name, 3, domain, size, comments) + super().__init__(table, name, 3, domain, size, comments) - def _validate_table(self, table): + def _validate_table(self, table: ArrayLike) -> NDArray: """ - Validates given table is a 4D array and that its dimensions are equal. + Validate given table is a 4D array and that its dimensions are equal. Parameters ---------- - table : array_like + table Table to validate. Returns ------- - ndarray + :class:`numpy.ndarray` Validated table as a :class:`ndarray` instance. """ table = as_float_array(table) - assert len(table.shape) == 4, 'The table must be a 4D array!' + attest(len(table.shape) == 4, "The table must be a 4D array!") return table - def _validate_domain(self, domain): + def _validate_domain(self, domain: ArrayLike) -> NDArray: """ - Validates given domain. + Validate given domain. Parameters ---------- - domain : array_like + domain Domain to validate. Returns ------- - ndarray + :class:`numpy.ndarray` Validated domain as a :class:`ndarray` instance. Notes @@ -1609,19 +1957,22 @@ def _validate_domain(self, domain): domain = as_float_array(domain) - assert len(domain.shape) == 2, 'The domain must be a 2D array!' + attest(len(domain.shape) == 2, "The domain must be a 2D array!") - assert domain.shape[0] >= 2, ( - 'The domain row count must be equal or greater than 2!') + attest( + domain.shape[0] >= 2, + "The domain row count must be equal or greater than 2!", + ) - assert domain.shape[1] == 3, ( - 'The domain column count must be equal to 3!') + attest( + domain.shape[1] == 3, "The domain column count must be equal to 3!" + ) return domain - def is_domain_explicit(self): + def is_domain_explicit(self) -> Boolean: """ - Returns whether the *LUT* domain is explicit (or implicit). + Return whether the *LUT* domain is explicit (or implicit). An implicit domain is defined by its shape only:: @@ -1640,7 +1991,7 @@ def is_domain_explicit(self): Returns ------- - bool + :class:`bool` Is *LUT* domain explicit. Examples @@ -1656,23 +2007,25 @@ def is_domain_explicit(self): return self.domain.shape != (2, 3) - # pylint: disable=W0221 @staticmethod - def linear_table(size=33, domain=np.array([[0, 0, 0], [1, 1, 1]])): + def linear_table( + size: Optional[IntegerOrArrayLike] = None, + domain: Optional[ArrayLike] = None, + ) -> NDArray: """ - Returns a linear table, the number of output samples :math:`n` is equal + Return a linear table, the number of output samples :math:`n` is equal to ``size**3 * 3`` or ``size[0] * size[1] * size[2] * 3``. Parameters ---------- - size : int or array_like, optional - Expected table size. - domain : array_like, optional + size + Expected table size, default to 33. + domain Domain of the table. Returns ------- - ndarray + :class:`numpy.ndarray` Linear table with ``size**3 * 3`` or ``size[0] * size[1] * size[2] * 3`` samples. @@ -1782,57 +2135,190 @@ def linear_table(size=33, domain=np.array([[0, 0, 0], [1, 1, 1]])): [ 1.5, 3. , 6. ]]]]) """ - domain = as_float_array(domain) + size = cast(Integer, optional(size, 33)) + domain = as_float_array( + cast(ArrayLike, optional(domain, np.array([[0, 0, 0], [1, 1, 1]]))) + ) if domain.shape != (2, 3): - samples = np.flip([ - axes[:(~np.isnan(axes)).cumsum().argmax() + 1] - for axes in np.transpose(domain) - ], -1) - size = [len(axes) for axes in samples] + samples = list( + np.flip( + [ + axes[: (~np.isnan(axes)).cumsum().argmax() + 1] + for axes in np.transpose(domain) + ], + -1, + ) + ) + size_array = as_int_array([len(axes) for axes in samples]) else: if is_numeric(size): - size = np.tile(size, 3) + size_array = np.tile(size, 3) + else: + size_array = as_int_array(size) R, G, B = tsplit(domain) - size = np.flip(size, -1) + size_array = np.flip(size_array, -1) samples = [ - np.linspace(a[0], a[1], size[i]) + np.linspace(a[0], a[1], size_array[i]) for i, a in enumerate([B, G, R]) ] - table = np.meshgrid(*samples, indexing='ij') table = np.flip( - np.transpose(table).reshape(np.hstack([np.flip(size, -1), 3])), -1) + np.transpose(np.meshgrid(*samples, indexing="ij")).reshape( + np.hstack([np.flip(size_array, -1), 3]) + ), + -1, + ) return table - def apply(self, - RGB, - interpolator=table_interpolation_trilinear, - interpolator_kwargs=None, - **kwargs): + @required("Scikit-Learn") + def invert(self, **kwargs: Any) -> LUT3D: + """ + Compute and returns an inverse copy of the *LUT*. + + Other Parameters + ---------------- + extrapolate + Whether to extrapolate the *LUT* when computing its inverse. + Extrapolation is performed by reflecting the *LUT* cube along its 8 + faces. Note that the domain is extended beyond [0, 1], thus the + *LUT* might not be handled properly in other software. + interpolator + Interpolator class type or object to use as interpolating function. + query_size + Number of points to query in the KDTree, their mean is computed, + resulting in a smoother result. + size + Size of the inverse *LUT*. With the given implementation, it is + good practise to double the size of the inverse *LUT* to provide a + smoother result. If ``size`` is not given, + :math:`2^{\\sqrt{size_{LUT}} + 1} + 1` will be used instead. + + Returns + ------- + :class:`colour.LUT3D` + Inverse *LUT* class instance. + + Examples + -------- + >>> LUT = LUT3D() + >>> print(LUT) + LUT3D - Unity 33 + ---------------- + + Dimensions : 3 + Domain : [[ 0. 0. 0.] + [ 1. 1. 1.]] + Size : (33, 33, 33, 3) + >>> print(LUT.invert()) + LUT3D - Unity 33 - Inverse + -------------------------- + + Dimensions : 3 + Domain : [[ 0. 0. 0.] + [ 1. 1. 1.]] + Size : (108, 108, 108, 3) + """ + + # TODO: Drop "sklearn" requirement whenever "Scipy" 1.7 can be + # defined as the minimal version. + from sklearn.neighbors import KDTree + + interpolator = kwargs.get( + "interpolator", table_interpolation_trilinear + ) + extrapolate = kwargs.get("extrapolate", False) + query_size = kwargs.get("query_size", 3) + + LUT = self.copy() + source_size = LUT.size + target_size = kwargs.get( + "size", (as_int(2 ** (np.sqrt(source_size) + 1) + 1)) + ) + + if target_size > 129: + usage_warning("LUT3D inverse computation time could be excessive!") + + if extrapolate: + LUT.table = np.pad( + LUT.table, + [(1, 1), (1, 1), (1, 1), (0, 0)], + "reflect", + reflect_type="odd", + ) + + LUT.domain[0] -= 1 / (source_size - 1) + LUT.domain[1] += 1 / (source_size - 1) + + # "LUT_t" is an intermediate LUT with a size equal to that of the + # final inverse LUT which is usually larger than the input LUT. + # The intent is to smooth the inverse LUT's table by increasing the + # resolution of the KDTree. + LUT_t = LUT3D(size=target_size, domain=LUT.domain) + table = np.reshape(LUT_t.table, (-1, 3)) + LUT_t.table = LUT.apply(LUT_t.table, interpolator=interpolator) + + tree = KDTree(np.reshape(LUT_t.table, (-1, 3))) + + # "LUT_q" stores the indexes of the KDTree query, i.e. the closest + # entry of "LUT_t" for any searched table sample. + LUT_q = LUT3D(size=target_size, domain=LUT.domain) + query = tree.query(table, query_size)[-1] + if query_size == 1: + LUT_q.table = table[query].reshape( + [target_size, target_size, target_size, 3] + ) + else: + LUT_q.table = np.mean(table[query], axis=-2).reshape( + [target_size, target_size, target_size, 3] + ) + + # "LUT_i" is the final inverse LUT generated by applying "LUT_q" on + # an identity LUT at the target size. + LUT_i = LUT3D(size=target_size, domain=LUT.domain) + LUT_i.table = LUT_q.apply(LUT_i.table, interpolator=interpolator) + + LUT_i.name = f"{self.name} - Inverse" + + return LUT_i + + def apply(self, RGB: ArrayLike, **kwargs: Any) -> NDArray: """ - Applies the *LUT* to given *RGB* colourspace array using given method. + Apply the *LUT* to given *RGB* colourspace array using given method. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array to apply the *LUT* onto. - interpolator : object, optional - Interpolator object to use as interpolating function. - interpolator_kwargs : dict_like, optional - Arguments to use when calling the interpolating function. Other Parameters ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. + direction + Whether the *LUT* should be applied in the forward or inverse + direction. + extrapolate + Whether to extrapolate the *LUT* when computing its inverse. + Extrapolation is performed by reflecting the *LUT* cube along its 8 + faces. + interpolator + Interpolator object to use as interpolating function. + interpolator_kwargs + Arguments to use when calling the interpolating function. + query_size + Number of points to query in the KDTree, their mean is computed, + resulting in a smoother result. + size + Size of the inverse *LUT*. With the given implementation, it is + good practise to double the size of the inverse *LUT* to provide a + smoother result. If ``size`` is not given, + :math:`2^{\\sqrt{size_{LUT}} + 1} + 1` will be used instead. Returns ------- - ndarray + :class:`numpy.ndarray` Interpolated *RGB* colourspace array. Examples @@ -1841,6 +2327,9 @@ def apply(self, >>> RGB = np.array([0.18, 0.18, 0.18]) >>> LUT.apply(RGB) # doctest: +ELLIPSIS array([ 0.4583277..., 0.4583277..., 0.4583277...]) + >>> LUT.apply(LUT.apply(RGB), direction='Inverse') + ... # doctest: +ELLIPSIS + array([ 0.1781995..., 0.1809414..., 0.1809513...]) >>> from colour.algebra import spow >>> domain = np.array([[-0.1, -0.2, -0.4], ... [0.3, 1.4, 6.0], @@ -1854,60 +2343,74 @@ def apply(self, array([ 0.2996370..., -0.0901332..., -0.3949770...]) """ - interpolator_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['interpolator_args', 'interpolator_kwargs']], - }, **kwargs).get('interpolator_kwargs', interpolator_kwargs) + direction = validate_method( + kwargs.get("direction", "Forward"), ["Forward", "Inverse"] + ) - if interpolator_kwargs is None: - interpolator_kwargs = {} + interpolator = kwargs.get( + "interpolator", table_interpolation_trilinear + ) + interpolator_kwargs = kwargs.get("interpolator_kwargs", {}) R, G, B = tsplit(RGB) - if self.is_domain_explicit(): - domain_min = self.domain[0, ...] + settings = {"interpolator": interpolator} + settings.update(**kwargs) + LUT = self.invert(**settings) if direction == "inverse" else self + + if LUT.is_domain_explicit(): + domain_min = LUT.domain[0, ...] domain_max = [ - axes[:(~np.isnan(axes)).cumsum().argmax() + 1][-1] - for axes in np.transpose(self.domain) + axes[: (~np.isnan(axes)).cumsum().argmax() + 1][-1] + for axes in np.transpose(LUT.domain) ] usage_warning( - '"LUT" was defined with an explicit domain but requires ' - 'an implicit domain to be applied. The following domain ' - 'will be used: {0}'.format( - np.vstack([domain_min, domain_max]))) + f'"LUT" was defined with an explicit domain but requires an ' + f"implicit domain to be applied. The following domain will be " + f"used: {np.vstack([domain_min, domain_max])}" + ) else: - domain_min, domain_max = self.domain + domain_min, domain_max = LUT.domain RGB_l = [ linear_conversion(j, (domain_min[i], domain_max[i]), (0, 1)) for i, j in enumerate((R, G, B)) ] - return interpolator(tstack(RGB_l), self._table, **interpolator_kwargs) + RGB_i = interpolator(tstack(RGB_l), LUT.table, **interpolator_kwargs) + + return RGB_i - def as_LUT(self, cls, force_conversion=False, **kwargs): + def convert( + self, + cls: Type[AbstractLUT], + force_conversion: Boolean = False, + **kwargs: Any, + ) -> AbstractLUT: """ - Converts the *LUT* to given ``cls`` class instance. + Convert the *LUT* to given ``cls`` class instance. Parameters ---------- - cls : LUT1D or LUT3x1D or LUT3D + cls *LUT* class instance. - force_conversion : bool, optional + force_conversion Whether to force the conversion as it might be destructive. Other Parameters ---------------- - interpolator : object, optional + interpolator Interpolator class type to use as interpolating function. - interpolator_kwargs : dict_like, optional + interpolator_kwargs Arguments to use when instantiating the interpolating function. - size : int, optional + size Expected table size in case of a downcast from a :class:`LUT3D` class instance. Returns ------- - LUT1D or LUT3x1D or LUT3D + :class:`colour.LUT1D` or :class:`colour.LUT3x1D` or \ +:class:`colour.LUT3D` Converted *LUT* class instance. Warnings @@ -1923,14 +2426,14 @@ class instance. Examples -------- >>> LUT = LUT3D() - >>> print(LUT.as_LUT(LUT1D, force_conversion=True)) + >>> print(LUT.convert(LUT1D, force_conversion=True)) LUT1D - Unity 33 - Converted 3D to 1D ------------------------------------- Dimensions : 1 Domain : [ 0. 1.] Size : (10,) - >>> print(LUT.as_LUT(LUT3x1D, force_conversion=True)) + >>> print(LUT.convert(LUT3x1D, force_conversion=True)) LUT3x1D - Unity 33 - Converted 3D to 3x1D ----------------------------------------- @@ -1938,7 +2441,7 @@ class instance. Domain : [[ 0. 0. 0.] [ 1. 1. 1.]] Size : (10, 3) - >>> print(LUT.as_LUT(LUT3D)) + >>> print(LUT.convert(LUT3D)) LUT3D - Unity 33 - Converted 3D to 3D ------------------------------------- @@ -1950,34 +2453,60 @@ class instance. return LUT_to_LUT(self, cls, force_conversion, **kwargs) - -def LUT_to_LUT(LUT, cls, force_conversion=False, **kwargs): + # ------------------------------------------------------------------------# + # --- API Changes and Deprecation Management ---# + # ------------------------------------------------------------------------# + def as_LUT( + self, + cls: Type[AbstractLUT], + force_conversion: Boolean = False, + **kwargs: Any, + ) -> AbstractLUT: # pragma: no cover # noqa: D102 + # Docstrings are omitted for documentation purposes. + usage_warning( + str( + ObjectRenamed( + "LUT3D.as_LUT", + "LUT3D.convert", + ) + ) + ) + + return self.convert(cls, force_conversion, **kwargs) + + +def LUT_to_LUT( + LUT, + cls: Type[AbstractLUT], + force_conversion: Boolean = False, + **kwargs: Any, +) -> AbstractLUT: """ - Converts given *LUT* to given ``cls`` class instance. + Convert given *LUT* to given ``cls`` class instance. Parameters ---------- - cls : LUT1D or LUT3x1D or LUT3D + cls *LUT* class instance. - force_conversion : bool, optional + force_conversion Whether to force the conversion if destructive. Other Parameters ---------------- - interpolator : object, optional + channel_weights + Channel weights in case of a downcast from a :class:`LUT3x1D` or + :class:`LUT3D` class instance. + interpolator Interpolator class type to use as interpolating function. - interpolator_kwargs : dict_like, optional + interpolator_kwargs Arguments to use when instantiating the interpolating function. - size : int, optional + size Expected table size in case of an upcast to or a downcast from a :class:`LUT3D` class instance. - channel_weights : array_like, optional - Channel weights in case of a downcast from a :class:`LUT3x1D` or - :class:`LUT3D` class instance. Returns ------- - LUT1D or LUT3x1D or LUT3D + :class:`colour.LUT1D` or :class:`colour.LUT3x1D` or :class:`colour.LUT3D` Converted *LUT* class instance. Warnings @@ -2019,31 +2548,33 @@ def LUT_to_LUT(LUT, cls, force_conversion=False, **kwargs): ranks = {LUT1D: 1, LUT3x1D: 2, LUT3D: 3} path = (ranks[LUT.__class__], ranks[cls]) path_verbose = [ - '{0}D'.format(element) if element != 2 else '3x1D' for element in path + f"{element}D" if element != 2 else "3x1D" for element in path ] if path in ((1, 3), (2, 1), (2, 3), (3, 1), (3, 2)): if not force_conversion: raise ValueError( - 'Conversion of a "LUT" {0} to a "LUT" {1} is destructive, ' - 'please use the "force_conversion" argument to proceed.'. - format(*path_verbose)) + f'Conversion of a "LUT" {path_verbose[0]} to a "LUT" ' + f"{path_verbose[1]} is destructive, please use the " + f'"force_conversion" argument to proceed!' + ) - suffix = ' - Converted {0} to {1}'.format(*path_verbose) - name = '{0}{1}'.format(LUT.name, suffix) + suffix = f" - Converted {path_verbose[0]} to {path_verbose[1]}" + name = f"{LUT.name}{suffix}" # Same dimension conversion, returning a copy. if len(set(path)) == 1: LUT = LUT.copy() LUT.name = name else: - size = kwargs.get('size', 33 if cls is LUT3D else 10) - if 'size' in kwargs: - del kwargs['size'] + size = kwargs.get("size", 33 if cls is LUT3D else 10) + if "size" in kwargs: + del kwargs["size"] channel_weights = as_float_array( - kwargs.get('channel_weights', full(3, 1 / 3))) - if 'channel_weights' in kwargs: - del kwargs['channel_weights'] + kwargs.get("channel_weights", full(3, 1 / 3)) + ) + if "channel_weights" in kwargs: + del kwargs["channel_weights"] if isinstance(LUT, LUT1D): if cls is LUT3x1D: @@ -2072,405 +2603,12 @@ def LUT_to_LUT(LUT, cls, force_conversion=False, **kwargs): table = LUT3x1D.linear_table(size, domain) table = LUT.apply(table, **kwargs) - LUT = cls(table, name, domain, table.shape[0], LUT.comments) + LUT = cls( + table=table, + name=name, + domain=domain, + size=table.shape[0], + comments=LUT.comments, + ) return LUT - - -@add_metaclass(ABCMeta) -class AbstractLUTSequenceOperator: - """ - Defines the base class for *LUT* sequence operators. - - This is an :class:`ABCMeta` abstract class that must be inherited by - sub-classes. - - Methods - ------- - - :meth:`~colour.io.luts.lut.AbstractLUTSequenceOperator.apply` - """ - - @abstractmethod - def apply(self, RGB, *args): - """ - Applies the *LUT* sequence operator to given *RGB* colourspace array. - - Parameters - ---------- - RGB : array_like - *RGB* colourspace array to apply the *LUT* sequence operator onto. - - Returns - ------- - ndarray - Processed *RGB* colourspace array. - """ - - pass - - -class LUTSequence(MutableSequence): - """ - Defines the base class for a *LUT* sequence, i.e. a series of *LUTs*. - - The `colour.LUTSequence` class can be used to model series of *LUTs* such - as when a shaper *LUT* is combined with a 3D *LUT*. - - Other Parameters - ---------------- - \\*args : list, optional - Sequence of `colour.LUT1D`, `colour.LUT3x1D`, `colour.LUT3D` or - `colour.io.lut.l.AbstractLUTSequenceOperator` class instances. - - Attributes - ---------- - - :attr:`~colour.LUTSequence.sequence` - - Methods - ------- - - :meth:`~colour.LUTSequence.__init__` - - :meth:`~colour.LUTSequence.__getitem__` - - :meth:`~colour.LUTSequence.__setitem__` - - :meth:`~colour.LUTSequence.__delitem__` - - :meth:`~colour.LUTSequence.__len__` - - :meth:`~colour.LUTSequence.__str__` - - :meth:`~colour.LUTSequence.__repr__` - - :meth:`~colour.LUTSequence.__eq__` - - :meth:`~colour.LUTSequence.__ne__` - - :meth:`~colour.LUTSequence.insert` - - :meth:`~colour.LUTSequence.apply` - - :meth:`~colour.LUTSequence.copy` - - Examples - -------- - >>> LUT_1 = LUT1D() - >>> LUT_2 = LUT3D(size=3) - >>> LUT_3 = LUT3x1D() - >>> print(LUTSequence(LUT_1, LUT_2, LUT_3)) - LUT Sequence - ------------ - - Overview - - LUT1D ---> LUT3D ---> LUT3x1D - - Operations - - LUT1D - Unity 10 - ---------------- - - Dimensions : 1 - Domain : [ 0. 1.] - Size : (10,) - - LUT3D - Unity 3 - --------------- - - Dimensions : 3 - Domain : [[ 0. 0. 0.] - [ 1. 1. 1.]] - Size : (3, 3, 3, 3) - - LUT3x1D - Unity 10 - ------------------ - - Dimensions : 2 - Domain : [[ 0. 0. 0.] - [ 1. 1. 1.]] - Size : (10, 3) - """ - - def __init__(self, *args): - for arg in args: - assert isinstance( - arg, (LUT1D, LUT3x1D, LUT3D, AbstractLUTSequenceOperator)), ( - '"args" elements must be instances of "LUT1D", ' - '"LUT3x1D", "LUT3D" or "AbstractLUTSequenceOperator"!') - - self._sequence = list(args) - - @property - def sequence(self): - """ - Getter and setter property for the underlying *LUT* sequence. - - Parameters - ---------- - value : list - Value to set the the underlying *LUT* sequence with. - - Returns - ------- - list - Underlying *LUT* sequence. - """ - - return self._sequence - - @sequence.setter - def sequence(self, value): - """ - Setter for **self.sequence** property. - """ - - if value is not None: - self._sequence = list(value) - - def __getitem__(self, index): - """ - Returns the *LUT* sequence item at given index. - - Parameters - ---------- - index : int - *LUT* sequence item index. - - Returns - ------- - LUT1D or LUT3x1D or LUT3D or AbstractLUTSequenceOperator - *LUT* sequence item at given index. - """ - - return self._sequence[index] - - def __setitem__(self, index, value): - """ - Sets given the *LUT* sequence item at given index with given value. - - Parameters - ---------- - index : int - *LUT* sequence item index. - value : LUT1D or LUT3x1D or LUT3D or AbstractLUTSequenceOperator - Value. - """ - - self._sequence[index] = value - - def __delitem__(self, index): - """ - Deletes the *LUT* sequence item at given index. - - Parameters - ---------- - index : int - *LUT* sequence item index. - """ - - del self._sequence[index] - - def __len__(self): - """ - Returns the *LUT* sequence items count. - - Returns - ------- - int - *LUT* sequence items count. - """ - - return len(self._sequence) - - def __str__(self): - """ - Returns a formatted string representation of the *LUT* sequence. - - Returns - ------- - unicode - Formatted string representation. - """ - - operations = re.sub( - '^', - ' ' * 4, - '\n\n'.join([str(a) for a in self._sequence]), - flags=re.MULTILINE) - operations = re.sub('^\\s+$', '', operations, flags=re.MULTILINE) - - return ('LUT Sequence\n' - '------------\n\n' - 'Overview\n\n' - ' {0}\n\n' - 'Operations\n\n' - '{1}').format( - ' ---> '.join( - [a.__class__.__name__ for a in self._sequence]), - operations) - - def __repr__(self): - """ - Returns an evaluable string representation of the *LUT* sequence. - - Returns - ------- - unicode - Evaluable string representation. - """ - - operations = re.sub( - '^', - ' ' * 4, - ',\n'.join([repr(a) for a in self._sequence]), - flags=re.MULTILINE) - operations = re.sub('^\\s+$', '', operations, flags=re.MULTILINE) - - return '{0}(\n{1}\n)'.format(self.__class__.__name__, operations) - - def __eq__(self, other): - """ - Returns whether the *LUT* sequence is equal to given other object. - - Parameters - ---------- - other : object - Object to test whether it is equal to the *LUT* sequence. - - Returns - ------- - bool - Is given object equal to the *LUT* sequence. - """ - - if not isinstance(other, LUTSequence): - return False - - if len(self) != len(other): - return False - - # pylint: disable=C0200 - for i in range(len(self)): - if self[i] != other[i]: - return False - - return True - - def __ne__(self, other): - """ - Returns whether the *LUT* sequence is not equal to given other object. - - Parameters - ---------- - other : object - Object to test whether it is not equal to the *LUT* sequence. - - Returns - ------- - bool - Is given object not equal to the *LUT* sequence. - """ - - return not (self == other) - - # pylint: disable=W0221 - def insert(self, index, LUT): - """ - Inserts given *LUT* at given index into the *LUT* sequence. - - Parameters - ---------- - index : index - Index to insert the *LUT* at into the *LUT* sequence. - LUT : LUT1D or LUT3x1D or LUT3D or AbstractLUTSequenceOperator - *LUT* to insert into the *LUT* sequence. - """ - - assert isinstance( - LUT, (LUT1D, LUT3x1D, LUT3D, AbstractLUTSequenceOperator)), ( - '"LUT" must be an instance of "LUT1D", "LUT3x1D", "LUT3D" or ' - '"AbstractLUTSequenceOperator"!') - - self._sequence.insert(index, LUT) - - def apply(self, - RGB, - interpolator_1D=LinearInterpolator, - interpolator_1D_kwargs=None, - interpolator_3D=table_interpolation_trilinear, - interpolator_3D_kwargs=None, - **kwargs): - """ - Applies the *LUT* sequence sequentially to given *RGB* colourspace - array. - - Parameters - ---------- - RGB : array_like - *RGB* colourspace array to apply the *LUT* sequence sequentially - onto. - interpolator_1D : object, optional - Interpolator object to use as interpolating function for - :class:`colour.LUT1D` (and :class:`colour.LUT3x1D`) class - instances. - interpolator_1D_kwargs : dict_like, optional - Arguments to use when calling the interpolating function for - :class:`colour.LUT1D` (and :class:`colour.LUT3x1D`) class - instances. - interpolator_3D : object, optional - Interpolator object to use as interpolating function for - :class:`colour.LUT3D` class instances. - interpolator_3D_kwargs : dict_like, optional - Arguments to use when calling the interpolating function for - :class:`colour.LUT3D` class instances. - - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - - Returns - ------- - ndarray - Processed *RGB* colourspace array. - - Examples - -------- - >>> LUT_1 = LUT1D(LUT1D.linear_table(16) + 0.125) - >>> LUT_2 = LUT3D(LUT3D.linear_table(16) ** (1 / 2.2)) - >>> LUT_3 = LUT3x1D(LUT3x1D.linear_table(16) * 0.750) - >>> LUT_sequence = LUTSequence(LUT_1, LUT_2, LUT_3) - >>> samples = np.linspace(0, 1, 5) - >>> RGB = tstack([samples, samples, samples]) - >>> LUT_sequence.apply(RGB) # doctest: +ELLIPSIS - array([[ 0.2899886..., 0.2899886..., 0.2899886...], - [ 0.4797662..., 0.4797662..., 0.4797662...], - [ 0.6055328..., 0.6055328..., 0.6055328...], - [ 0.7057779..., 0.7057779..., 0.7057779...], - [ 0.75 ..., 0.75 ..., 0.75 ...]]) - """ - - interpolator_1D_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [[ - 'interpolator_1D_args', 'interpolator_1D_kwargs' - ]], - }, **kwargs).get('interpolator_1D_kwargs', interpolator_1D_kwargs) - - interpolator_3D_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [[ - 'interpolator_3D_args', 'interpolator_3D_kwargs' - ]], - }, **kwargs).get('interpolator_3D_kwargs', interpolator_3D_kwargs) - - for operation in self: - if isinstance(operation, (LUT1D, LUT3x1D)): - RGB = operation.apply(RGB, interpolator_1D, - interpolator_1D_kwargs) - elif isinstance(operation, LUT3D): - RGB = operation.apply(RGB, interpolator_3D, - interpolator_3D_kwargs) - else: - RGB = operation.apply(RGB) - - return RGB - - def copy(self): - """ - Returns a copy of the *LUT* sequence. - - Returns - ------- - LUTSequence - *LUT* sequence copy. - """ - - return deepcopy(self) diff --git a/colour/io/luts/operator.py b/colour/io/luts/operator.py new file mode 100644 index 0000000000..0b4d4bc61a --- /dev/null +++ b/colour/io/luts/operator.py @@ -0,0 +1,524 @@ +""" +LUT Operator +============ + +Defines the *LUT* operator classes: + +- :class:`colour.io.AbstractLUTSequenceOperator` +- :class:`colour.LUTOperatorMatrix` +""" + +from __future__ import annotations + +import numpy as np +from abc import ABC, abstractmethod + +from colour.algebra import vector_dot +from colour.hints import ( + Any, + ArrayLike, + List, + NDArray, + Optional, + Sequence, + cast, +) +from colour.utilities import ( + as_float_array, + attest, + is_iterable, + is_string, + ones, + optional, + zeros, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "AbstractLUTSequenceOperator", + "LUTOperatorMatrix", +] + + +class AbstractLUTSequenceOperator(ABC): + """ + Define the base class for *LUT* sequence operators. + + This is an :class:`ABCMeta` abstract class that must be inherited by + sub-classes. + + Parameters + ---------- + name + *LUT* sequence operator name. + comments + Comments to add to the *LUT* sequence operator. + + Attributes + ---------- + - :attr:`~colour.io.AbstractLUTSequenceOperator.name` + - :attr:`~colour.io.AbstractLUTSequenceOperator.comments` + + Methods + ------- + - :meth:`~colour.io.AbstractLUTSequenceOperator.apply` + """ + + def __init__( + self, + name: Optional[str] = None, + comments: Optional[Sequence[str]] = None, + ): + self._name = f"LUT Sequence Operator {id(self)}" + self.name = optional(name, self._name) + # TODO: Remove pragma when https://github.com/python/mypy/issues/3004 + # is resolved. + self._comments: List[str] = [] + self.comments = optional(comments, self._comments) # type: ignore[arg-type] + + @property + def name(self) -> str: + """ + Getter and setter property for the *LUT* name. + + Parameters + ---------- + value + Value to set the *LUT* name with. + + Returns + ------- + :class:`str` + *LUT* name. + """ + + return self._name + + @name.setter + def name(self, value: str): + """Setter for the **self.name** property.""" + + attest( + is_string(value), + f'"name" property: "{value}" type is not "str"!', + ) + + self._name = value + + @property + def comments(self) -> List[str]: + """ + Getter and setter property for the *LUT* comments. + + Parameters + ---------- + value + Value to set the *LUT* comments with. + + Returns + ------- + :class:`list` + *LUT* comments. + """ + + return self._comments + + @comments.setter + def comments(self, value: Sequence[str]): + """Setter for the **self.comments** property.""" + + attest( + is_iterable(value), + f'"comments" property: "{value}" must be a sequence!', + ) + + self._comments = list(value) + + @abstractmethod + def apply(self, RGB: ArrayLike, *args: Any, **kwargs: Any) -> NDArray: + """ + Apply the *LUT* sequence operator to given *RGB* colourspace array. + + Parameters + ---------- + RGB + *RGB* colourspace array to apply the *LUT* sequence operator onto. + + Other Parameters + ---------------- + args + Arguments. + kwargs + Keywords arguments. + + Returns + ------- + :class:`numpy.ndarray` + Processed *RGB* colourspace array. + """ + + pass + + +class LUTOperatorMatrix(AbstractLUTSequenceOperator): + """ + Define the *LUT* operator supporting a 3x3 or 4x4 matrix and an offset + vector. + + Parameters + ---------- + matrix + 3x3 or 4x4 matrix for the operator. + offset + Offset for the operator. + name + *LUT* operator name. + comments + Comments to add to the *LUT* operator. + + Attributes + ---------- + - :meth:`~colour.LUTOperatorMatrix.matrix` + - :meth:`~colour.LUTOperatorMatrix.offset` + + Methods + ------- + - :meth:`~colour.LUTOperatorMatrix.__str__` + - :meth:`~colour.LUTOperatorMatrix.__repr__` + - :meth:`~colour.LUTOperatorMatrix.__eq__` + - :meth:`~colour.LUTOperatorMatrix.__ne__` + - :meth:`~colour.LUTOperatorMatrix.apply` + + Notes + ----- + - The internal :attr:`colour.io.Matrix.matrix` and + :attr:`colour.io.Matrix.offset` properties are reshaped to (4, 4) and + (4, ) respectively. + + Examples + -------- + Instantiating an identity matrix: + + >>> print(LUTOperatorMatrix(name='Identity')) + LUTOperatorMatrix - Identity + ---------------------------- + + Matrix : [[ 1. 0. 0. 0.] + [ 0. 1. 0. 0.] + [ 0. 0. 1. 0.] + [ 0. 0. 0. 1.]] + Offset : [ 0. 0. 0. 0.] + + Instantiating a matrix with comments: + + >>> matrix = np.array([[ 1.45143932, -0.23651075, -0.21492857], + ... [-0.07655377, 1.1762297 , -0.09967593], + ... [ 0.00831615, -0.00603245, 0.9977163 ]]) + >>> print(LUTOperatorMatrix( + ... matrix, + ... name='AP0 to AP1', + ... comments=['A first comment.', 'A second comment.'])) + LUTOperatorMatrix - AP0 to AP1 + ------------------------------ + + Matrix : [[ 1.45143932 -0.23651075 -0.21492857 0. ] + [-0.07655377 1.1762297 -0.09967593 0. ] + [ 0.00831615 -0.00603245 0.9977163 0. ] + [ 0. 0. 0. 1. ]] + Offset : [ 0. 0. 0. 0.] + + A first comment. + A second comment. + """ + + def __init__( + self, + matrix: Optional[ArrayLike] = None, + offset: Optional[ArrayLike] = None, + *args: Any, + **kwargs: Any, + ): + super().__init__(*args, **kwargs) + + # TODO: Remove pragma when https://github.com/python/mypy/issues/3004 + # is resolved. + self._matrix: NDArray = np.diag(ones(4)) + self.matrix = cast( + ArrayLike, optional(matrix, self._matrix) + ) # type: ignore[assignment] + self._offset: NDArray = zeros(4) + self.offset = cast( + ArrayLike, optional(offset, self._offset) + ) # type: ignore[assignment] + + @property + def matrix(self) -> NDArray: + """ + Getter and setter property for the *LUT* operator matrix. + + Parameters + ---------- + value + Value to set the *LUT* operator matrix with. + + Returns + ------- + :class:`numpy.ndarray` + Operator matrix. + """ + + return self._matrix + + @matrix.setter + def matrix(self, value: ArrayLike): + """Setter for the **self.matrix** property.""" + + value = as_float_array(value) + + shape_t = value.shape[-1] + + value = value.reshape([shape_t, shape_t]) + + attest( + value.shape in [(3, 3), (4, 4)], + f'"matrix" property: "{value}" shape is not (3, 3) or (4, 4)!', + ) + + M = np.identity(4) + M[:shape_t, :shape_t] = value + + self._matrix = M + + @property + def offset(self) -> NDArray: + """ + Getter and setter property for the *LUT* operator offset. + + Parameters + ---------- + value + Value to set the *LUT* operator offset with. + + Returns + ------- + :class:`numpy.ndarray` + Operator offset. + """ + + return self._offset + + @offset.setter + def offset(self, value: ArrayLike): + """Setter for the **self.offset** property.""" + + value = as_float_array(value) + + shape_t = value.shape[-1] + + attest( + value.shape in [(3,), (4,)], + f'"offset" property: "{value}" shape is not (3, ) or (4, )!', + ) + + offset = zeros(4) + offset[:shape_t] = value + + self._offset = offset + + def __str__(self) -> str: + """ + Return a formatted string representation of the *LUT* operator. + + Returns + ------- + :class:`str` + Formatted string representation. + + Examples + -------- + >>> print(LUTOperatorMatrix()) # doctest: +ELLIPSIS + LUTOperatorMatrix - LUT Sequence Operator ... + ------------------------------------------... + + Matrix : [[ 1. 0. 0. 0.] + [ 0. 1. 0. 0.] + [ 0. 0. 1. 0.] + [ 0. 0. 0. 1.]] + Offset : [ 0. 0. 0. 0.] + """ + + def _indent_array(a: ArrayLike) -> str: + """Indent given array string representation.""" + + return str(a).replace(" [", " " * 14 + "[") + + comments = "\n".join(self._comments) + comments = f"\n\n{comments}" if self._comments else "" + + underline = "-" * (len(self.__class__.__name__) + 3 + len(self._name)) + + return ( + f"{self.__class__.__name__} - {self._name}\n" + f"{underline}\n\n" + f"Matrix : {_indent_array(self._matrix)}\n" + f"Offset : {_indent_array(self._offset)}" + f"{comments}" + ) + + def __repr__(self) -> str: + """ + Return an evaluable string representation of the *LUT* operator. + + Returns + ------- + :class:`str` + Evaluable string representation. + + Examples + -------- + >>> LUTOperatorMatrix( + ... comments=['A first comment.', 'A second comment.']) + ... # doctest: +ELLIPSIS + LUTOperatorMatrix([[ 1., 0., 0., 0.], + [ 0., 1., 0., 0.], + [ 0., 0., 1., 0.], + [ 0., 0., 0., 1.]], + [ 0., 0., 0., 0.], + name='LUT Sequence Operator ...', + comments=['A first comment.', 'A second comment.']) + """ + + representation = repr(self._matrix) + representation = representation.replace( + "array", self.__class__.__name__ + ) + representation = representation.replace( + " [", f"{' ' * (len(self.__class__.__name__) + 2)}[" + ) + + indentation = " " * (len(self.__class__.__name__) + 1) + + comments = ( + f",\n{indentation}comments={repr(self._comments)}" + if self._comments + else "" + ) + + return ( + f"{representation[:-1]},\n" + f"{indentation}" + f'{repr(self._offset).replace("array(", "").replace(")", "")},\n' + f"{indentation}name='{self._name}'" + f"{comments})" + ) + + def __eq__(self, other: Any) -> bool: + """ + Return whether the *LUT* operator is equal to given other object. + + Parameters + ---------- + other + Object to test whether it is equal to the *LUT* operator. + + Returns + ------- + :class:`bool` + Whether given object equal to the *LUT* operator. + + Examples + -------- + >>> LUTOperatorMatrix() == LUTOperatorMatrix() + True + """ + + if isinstance(other, LUTOperatorMatrix): + if all( + [ + np.array_equal(self._matrix, other._matrix), + np.array_equal(self._offset, other._offset), + ] + ): + return True + + return False + + def __ne__(self, other: Any) -> bool: + """ + Return whether the *LUT* operator is not equal to given other object. + + Parameters + ---------- + other + Object to test whether it is not equal to the *LUT* operator. + + Returns + ------- + :class:`bool` + Whether given object is not equal to the *LUT* operator. + + Examples + -------- + >>> LUTOperatorMatrix() != LUTOperatorMatrix( + ... np.linspace(0, 1, 16).reshape([4, 4])) + True + """ + + return not (self == other) + + def apply(self, RGB: ArrayLike, *args: Any, **kwargs: Any) -> NDArray: + """ + Apply the *LUT* operator to given *RGB* array. + + Parameters + ---------- + RGB + *RGB* array to apply the *LUT* operator transform to. + + Other Parameters + ---------------- + apply_offset_first + Whether to apply the offset first and then the matrix. + + Returns + ------- + :class:`numpy.ndarray` + Transformed *RGB* array. + + Examples + -------- + >>> matrix = np.array([[ 1.45143932, -0.23651075, -0.21492857], + ... [-0.07655377, 1.1762297 , -0.09967593], + ... [ 0.00831615, -0.00603245, 0.9977163 ]]) + >>> M = LUTOperatorMatrix(matrix) + >>> RGB = np.array([0.3, 0.4, 0.5]) + >>> M.apply(RGB) # doctest: +ELLIPSIS + array([ 0.2333632..., 0.3976877..., 0.4989400...]) + """ + + RGB = as_float_array(RGB) + apply_offset_first = kwargs.get("apply_offset_first", False) + + has_alpha_channel = RGB.shape[-1] == 4 + M = self._matrix + offset = self._offset + + if not has_alpha_channel: + M = M[:3, :3] + offset = offset[:3] + + if apply_offset_first: + RGB += offset + + RGB = vector_dot(M, RGB) + + if not apply_offset_first: + RGB += offset + + return RGB diff --git a/colour/io/luts/resolve_cube.py b/colour/io/luts/resolve_cube.py index 7e826db14b..8782d835ff 100644 --- a/colour/io/luts/resolve_cube.py +++ b/colour/io/luts/resolve_cube.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ Resolve .cube LUT Format Input / Output Utilities ================================================= -Defines *Resolve* *.cube* *LUT* Format related input / output utilities -objects. +Defines the *Resolve* *.cube* *LUT* format related input / output utilities +objects: - :func:`colour.io.read_LUT_ResolveCube` - :func:`colour.io.write_LUT_ResolveCube` @@ -16,36 +15,41 @@ https://forum.blackmagicdesign.com/viewtopic.php?f=21&t=40284#p232952 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +from colour.hints import Boolean, Integer, List, Tuple, Union from colour.io.luts import LUT1D, LUT3x1D, LUT3D, LUTSequence from colour.io.luts.common import path_to_title -from colour.utilities import as_float_array, tstack +from colour.utilities import as_float_array, as_int_scalar, attest, tstack -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['read_LUT_ResolveCube', 'write_LUT_ResolveCube'] +__all__ = [ + "read_LUT_ResolveCube", + "write_LUT_ResolveCube", +] -def read_LUT_ResolveCube(path): +def read_LUT_ResolveCube(path: str) -> Union[LUT3x1D, LUT3D, LUTSequence]: """ - Reads given *Resolve* *.cube* *LUT* file. + Read given *Resolve* *.cube* *LUT* file. Parameters ---------- - path : unicode + path *LUT* path. Returns ------- - LUT3x1D or LUT3D or LUTSequence + :class:`colour.LUT3x1D` or :class:`colour.LUT3D` or \ +:class:`colour.LUTSequence` :class:`LUT3x1D` or :class:`LUT3D` or :class:`LUTSequence` class instance. @@ -110,7 +114,7 @@ def read_LUT_ResolveCube(path): Overview - LUT3x1D ---> LUT3D + LUT3x1D --> LUT3D Operations @@ -136,87 +140,98 @@ def read_LUT_ResolveCube(path): """ title = path_to_title(path) - size_3x1D = size_3D = 2 - table = [] + domain_3x1D, domain_3D = None, None + size_3x1D: Integer = 2 + size_3D: Integer = 2 + data = [] comments = [] has_3x1D, has_3D = False, False with open(path) as cube_file: lines = cube_file.readlines() - LUT = LUTSequence(LUT3x1D(), LUT3D()) for line in lines: line = line.strip() if len(line) == 0: continue - if line.startswith('#'): + if line.startswith("#"): comments.append(line[1:].strip()) continue tokens = line.split() - if tokens[0] == 'TITLE': - title = ' '.join(tokens[1:])[1:-1] - elif tokens[0] == 'LUT_1D_INPUT_RANGE': - domain = as_float_array(tokens[1:]) - LUT[0].domain = tstack([domain, domain, domain]) - elif tokens[0] == 'LUT_3D_INPUT_RANGE': - domain = as_float_array(tokens[1:]) - LUT[1].domain = tstack([domain, domain, domain]) - elif tokens[0] == 'LUT_1D_SIZE': + if tokens[0] == "TITLE": + title = " ".join(tokens[1:])[1:-1] + elif tokens[0] == "LUT_1D_INPUT_RANGE": + domain_3x1D = tstack([tokens[1:], tokens[1:], tokens[1:]]) + elif tokens[0] == "LUT_3D_INPUT_RANGE": + domain_3D = tstack([tokens[1:], tokens[1:], tokens[1:]]) + elif tokens[0] == "LUT_1D_SIZE": has_3x1D = True - size_3x1D = np.int_(tokens[1]) - elif tokens[0] == 'LUT_3D_SIZE': + size_3x1D = as_int_scalar(tokens[1]) + elif tokens[0] == "LUT_3D_SIZE": has_3D = True - size_3D = np.int_(tokens[1]) + size_3D = as_int_scalar(tokens[1]) else: - table.append(tokens) + data.append(tokens) - table = as_float_array(table) + table = as_float_array(data) + + LUT: Union[LUT3x1D, LUT3D, LUTSequence] if has_3x1D and has_3D: - LUT[0].name = '{0} - Shaper'.format(title) - LUT[1].name = '{0} - Cube'.format(title) - LUT[1].comments = comments - LUT[0].table = table[:size_3x1D] + table_1D = table[: int(size_3x1D)] # The lines of table data shall be in ascending index order, # with the first component index (Red) changing most rapidly, # and the last component index (Blue) changing least rapidly. - LUT[1].table = table[size_3x1D:].reshape( - (size_3D, size_3D, size_3D, 3), order='F') - return LUT + table_3D = table[int(size_3x1D) :].reshape( + (size_3D, size_3D, size_3D, 3), order="F" + ) + LUT = LUTSequence( + LUT3x1D( + table_1D, + f"{title} - Shaper", + domain_3x1D, + ), + LUT3D( + table_3D, + f"{title} - Cube", + domain_3D, + comments=comments, + ), + ) elif has_3x1D: - LUT[0].name = title - LUT[0].comments = comments - LUT[0].table = table - return LUT[0] + LUT = LUT3x1D(table, title, domain_3x1D, comments=comments) elif has_3D: - LUT[1].name = title - LUT[1].comments = comments # The lines of table data shall be in ascending index order, # with the first component index (Red) changing most rapidly, # and the last component index (Blue) changing least rapidly. - table = table.reshape([size_3D, size_3D, size_3D, 3], order='F') - LUT[1].table = table - return LUT[1] + table = table.reshape([size_3D, size_3D, size_3D, 3], order="F") + LUT = LUT3D(table, title, domain_3D, comments=comments) + + return LUT -def write_LUT_ResolveCube(LUT, path, decimals=7): +def write_LUT_ResolveCube( + LUT: Union[LUT1D, LUT3x1D, LUT3D, LUTSequence], + path: str, + decimals: Integer = 7, +) -> Boolean: """ - Writes given *LUT* to given *Resolve* *.cube* *LUT* file. + Write given *LUT* to given *Resolve* *.cube* *LUT* file. Parameters ---------- - LUT : LUT1D or LUT3x1D or LUT3D or LUTSequence + LUT :class:`LUT1D`, :class:`LUT3x1D` or :class:`LUT3D` or :class:`LUTSequence` class instance to write at given path. - path : unicode + path *LUT* path. - decimals : int, optional + decimals Formatting decimals. Returns ------- - bool + :class:`bool` Definition success. References @@ -275,95 +290,108 @@ def write_LUT_ResolveCube(LUT, path, decimals=7): has_3D, has_3x1D = False, False if isinstance(LUT, LUTSequence): - assert (len(LUT) == 2 and isinstance(LUT[0], (LUT1D, LUT3x1D)) and - isinstance(LUT[1], LUT3D)), ( - 'LUTSequence must be 1D + 3D or 3x1D + 3D!') + attest( + len(LUT) == 2 + and isinstance(LUT[0], (LUT1D, LUT3x1D)) + and isinstance(LUT[1], LUT3D), + "LUTSequence must be 1D + 3D or 3x1D + 3D!", + ) if isinstance(LUT[0], LUT1D): LUT[0] = LUT[0].as_LUT(LUT3x1D) + name = f"{LUT[0].name} - {LUT[1].name}" has_3x1D = True has_3D = True - name = LUT[1].name elif isinstance(LUT, LUT1D): name = LUT.name - LUT = LUTSequence(LUT.as_LUT(LUT3x1D), LUT3D()) has_3x1D = True + LUT = LUTSequence(LUT.as_LUT(LUT3x1D), LUT3D()) elif isinstance(LUT, LUT3x1D): name = LUT.name - LUT = LUTSequence(LUT, LUT3D()) has_3x1D = True + LUT = LUTSequence(LUT, LUT3D()) elif isinstance(LUT, LUT3D): name = LUT.name - LUT = LUTSequence(LUT3x1D(), LUT) has_3D = True + LUT = LUTSequence(LUT3x1D(), LUT) else: - raise ValueError('LUT must be 1D, 3x1D, 3D, 1D + 3D or 3x1D + 3D!') + raise ValueError("LUT must be 1D, 3x1D, 3D, 1D + 3D or 3x1D + 3D!") for i in range(2): - assert not LUT[i].is_domain_explicit(), ( - '"LUT" domain must be implicit!') - - assert (len(np.unique(LUT[0].domain)) == 2 and - len(np.unique(LUT[1].domain)) == 2), 'LUT domain must be 1D!' + attest( + not LUT[i].is_domain_explicit(), '"LUT" domain must be implicit!' + ) + + attest( + ( + len(np.unique(LUT[0].domain)) == 2 + and len(np.unique(LUT[1].domain)) == 2 + ), + '"LUT" domain must be 1D!', + ) if has_3x1D: - assert 2 <= LUT[0].size <= 65536, ( - 'Shaper size must be in domain [2, 65536]!') + attest( + 2 <= LUT[0].size <= 65536, + "Shaper size must be in domain [2, 65536]!", + ) if has_3D: - assert 2 <= LUT[1].size <= 256, 'Cube size must be in domain [2, 256]!' + attest( + 2 <= LUT[1].size <= 256, "Cube size must be in domain [2, 256]!" + ) - def _format_array(array): - """ - Formats given array as a *Resolve* *.cube* data row. - """ + def _format_array(array: Union[List, Tuple]) -> str: + """Format given array as a *Resolve* *.cube* data row.""" - return '{1:0.{0}f} {2:0.{0}f} {3:0.{0}f}'.format(decimals, *array) + return "{1:0.{0}f} {2:0.{0}f} {3:0.{0}f}".format(decimals, *array) - def _format_tuple(array): + def _format_tuple(array: Union[List, Tuple]) -> str: """ - Formats given array as 2 space separated values to *decimals* + Format given array as 2 space separated values to *decimals* precision. """ - return '{1:0.{0}f} {2:0.{0}f}'.format(decimals, *array) + return "{1:0.{0}f} {2:0.{0}f}".format(decimals, *array) - with open(path, 'w') as cube_file: - cube_file.write('TITLE "{0}"\n'.format(name)) + with open(path, "w") as cube_file: + cube_file.write(f'TITLE "{name}"\n') if LUT[0].comments: for comment in LUT[0].comments: - cube_file.write('# {0}\n'.format(comment)) + cube_file.write(f"# {comment}\n") if LUT[1].comments: for comment in LUT[1].comments: - cube_file.write('# {0}\n'.format(comment)) + cube_file.write(f"# {comment}\n") default_domain = np.array([[0, 0, 0], [1, 1, 1]]) if has_3x1D: - cube_file.write('{0} {1}\n'.format('LUT_1D_SIZE', - LUT[0].table.shape[0])) + cube_file.write(f"LUT_1D_SIZE {LUT[0].table.shape[0]}\n") if not np.array_equal(LUT[0].domain, default_domain): - cube_file.write('LUT_1D_INPUT_RANGE {0}\n'.format( - _format_tuple([LUT[0].domain[0][0], LUT[0].domain[1][0]]))) + input_range = _format_tuple( + [LUT[0].domain[0][0], LUT[0].domain[1][0]] + ) + cube_file.write(f"LUT_1D_INPUT_RANGE {input_range}\n") if has_3D: - cube_file.write('{0} {1}\n'.format('LUT_3D_SIZE', - LUT[1].table.shape[0])) + cube_file.write(f"LUT_3D_SIZE {LUT[1].table.shape[0]}\n") if not np.array_equal(LUT[1].domain, default_domain): - cube_file.write('LUT_3D_INPUT_RANGE {0}\n'.format( - _format_tuple([LUT[1].domain[0][0], LUT[1].domain[1][0]]))) + input_range = _format_tuple( + [LUT[1].domain[0][0], LUT[1].domain[1][0]] + ) + cube_file.write(f"LUT_3D_INPUT_RANGE {input_range}\n") if has_3x1D: table = LUT[0].table for row in table: - cube_file.write('{0}\n'.format(_format_array(row))) - cube_file.write('\n') + cube_file.write(f"{_format_array(row)}\n") + cube_file.write("\n") if has_3D: - table = LUT[1].table.reshape([-1, 3], order='F') + table = LUT[1].table.reshape([-1, 3], order="F") for row in table: - cube_file.write('{0}\n'.format(_format_array(row))) + cube_file.write(f"{_format_array(row)}\n") return True diff --git a/colour/io/luts/sequence.py b/colour/io/luts/sequence.py new file mode 100644 index 0000000000..38f5caadbe --- /dev/null +++ b/colour/io/luts/sequence.py @@ -0,0 +1,393 @@ +""" +LUT Operator +============ + +Defines the *LUT* sequence class: + +- :class:`colour.LUTSequence` +""" + +from __future__ import annotations + +import re +from collections.abc import MutableSequence +from copy import deepcopy + +from colour.hints import ( + Any, + ArrayLike, + Integer, + List, + NDArray, + Sequence, + TypeLUTSequenceItem, + Union, +) +from colour.utilities import as_float_array, attest, is_iterable + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "LUTSequence", +] + + +class LUTSequence(MutableSequence): + """ + Define the base class for a *LUT* sequence, i.e. a series of *LUTs*, + *LUT* operators or objects implementing the + :class:`colour.hints.TypeLUTSequenceItem` protocol. + + The `colour.LUTSequence` class can be used to model series of *LUTs* such + as when a shaper *LUT* is combined with a 3D *LUT*. + + Other Parameters + ---------------- + args + Sequence of objects implementing the + :class:`colour.hints.TypeLUTSequenceItem` protocol. + + Attributes + ---------- + - :attr:`~colour.LUTSequence.sequence` + + Methods + ------- + - :meth:`~colour.LUTSequence.__init__` + - :meth:`~colour.LUTSequence.__getitem__` + - :meth:`~colour.LUTSequence.__setitem__` + - :meth:`~colour.LUTSequence.__delitem__` + - :meth:`~colour.LUTSequence.__len__` + - :meth:`~colour.LUTSequence.__str__` + - :meth:`~colour.LUTSequence.__repr__` + - :meth:`~colour.LUTSequence.__eq__` + - :meth:`~colour.LUTSequence.__ne__` + - :meth:`~colour.LUTSequence.insert` + - :meth:`~colour.LUTSequence.apply` + - :meth:`~colour.LUTSequence.copy` + + Examples + -------- + >>> from colour.io.luts import LUT1D, LUT3x1D, LUT3D + >>> LUT_1 = LUT1D() + >>> LUT_2 = LUT3D(size=3) + >>> LUT_3 = LUT3x1D() + >>> print(LUTSequence(LUT_1, LUT_2, LUT_3)) + LUT Sequence + ------------ + + Overview + + LUT1D --> LUT3D --> LUT3x1D + + Operations + + LUT1D - Unity 10 + ---------------- + + Dimensions : 1 + Domain : [ 0. 1.] + Size : (10,) + + LUT3D - Unity 3 + --------------- + + Dimensions : 3 + Domain : [[ 0. 0. 0.] + [ 1. 1. 1.]] + Size : (3, 3, 3, 3) + + LUT3x1D - Unity 10 + ------------------ + + Dimensions : 2 + Domain : [[ 0. 0. 0.] + [ 1. 1. 1.]] + Size : (10, 3) + """ + + def __init__(self, *args: TypeLUTSequenceItem): + # TODO: Remove pragma when https://github.com/python/mypy/issues/3004 + # is resolved. + self._sequence: List[TypeLUTSequenceItem] = [] + self.sequence = args # type: ignore[assignment] + + @property + def sequence(self) -> List[TypeLUTSequenceItem]: + """ + Getter and setter property for the underlying *LUT* sequence. + + Parameters + ---------- + value + Value to set the underlying *LUT* sequence with. + + Returns + ------- + :class:`list` + Underlying *LUT* sequence. + """ + + return self._sequence + + @sequence.setter + def sequence(self, value: Sequence[TypeLUTSequenceItem]): + """Setter for the **self.sequence** property.""" + + for item in value: + attest( + isinstance(item, TypeLUTSequenceItem), + '"value" items must implement the "TypeLUTSequenceItem" ' + "protocol!", + ) + + self._sequence = list(value) + + def __getitem__(self, index: Union[Integer, slice]) -> Any: + """ + Return the *LUT* sequence item(s) at given index (or slice). + + Parameters + ---------- + index + Index (or slice) to return the *LUT* sequence item(s) at. + + Returns + ------- + TypeLUTSequenceItem + *LUT* sequence item(s) at given index (or slice). + """ + + return self._sequence[index] + + def __setitem__(self, index: Union[Integer, slice], value: Any): + """ + Set the *LUT* sequence at given index (or slice) with given value. + + Parameters + ---------- + index + Index (or slice) to set the *LUT* sequence value at. + value + Value to set the *LUT* sequence with. + """ + + for item in value if is_iterable(value) else [value]: + attest( + isinstance(item, TypeLUTSequenceItem), + '"value" items must implement the "TypeLUTSequenceItem" ' + "protocol!", + ) + + self._sequence[index] = value + + def __delitem__(self, index: Union[Integer, slice]): + """ + Delete the *LUT* sequence item(s) at given index (or slice). + + Parameters + ---------- + index + Index (or slice) to delete the *LUT* sequence items at. + """ + + del self._sequence[index] + + def __len__(self) -> Integer: + """ + Return the *LUT* sequence items count. + + Returns + ------- + :class:`numpy.integer` + *LUT* sequence items count. + """ + + return len(self._sequence) + + def __str__(self) -> str: + """ + Return a formatted string representation of the *LUT* sequence. + + Returns + ------- + :class:`str` + Formatted string representation. + """ + + sequence = " --> ".join([a.__class__.__name__ for a in self._sequence]) + + operations = re.sub( + "^", + " " * 4, + "\n\n".join([str(a) for a in self._sequence]), + flags=re.MULTILINE, + ) + operations = re.sub("^\\s+$", "", operations, flags=re.MULTILINE) + + return ( + "LUT Sequence\n" + "------------\n\n" + "Overview\n\n" + f" {sequence}\n\n" + "Operations\n\n" + f"{operations}" + ) + + def __repr__(self) -> str: + """ + Return an evaluable string representation of the *LUT* sequence. + + Returns + ------- + :class:`str` + Evaluable string representation. + """ + + operations = re.sub( + "^", + " " * 4, + ",\n".join([repr(a) for a in self._sequence]), + flags=re.MULTILINE, + ) + operations = re.sub("^\\s+$", "", operations, flags=re.MULTILINE) + + return f"{self.__class__.__name__}(\n{operations}\n)" + + def __eq__(self, other) -> bool: + """ + Return whether the *LUT* sequence is equal to given other object. + + Parameters + ---------- + other + Object to test whether it is equal to the *LUT* sequence. + + Returns + ------- + :class:`bool` + Whether given object is equal to the *LUT* sequence. + """ + + if not isinstance(other, LUTSequence): + return False + + if len(self) != len(other): + return False + + # pylint: disable=C0200 + for i in range(len(self)): + if self[i] != other[i]: + return False + + return True + + def __ne__(self, other) -> bool: + """ + Return whether the *LUT* sequence is not equal to given other object. + + Parameters + ---------- + other + Object to test whether it is not equal to the *LUT* sequence. + + Returns + ------- + :class:`bool` + Whether given object is not equal to the *LUT* sequence. + """ + + return not (self == other) + + def insert(self, index: Integer, item: TypeLUTSequenceItem): + """ + Insert given *LUT* at given index into the *LUT* sequence. + + Parameters + ---------- + index + Index to insert the item at into the *LUT* sequence. + item + *LUT* to insert into the *LUT* sequence. + """ + + attest( + isinstance(item, TypeLUTSequenceItem), + '"value" items must implement the "TypeLUTSequenceItem" ' + "protocol!", + ) + + self._sequence.insert(index, item) + + def apply(self, RGB: ArrayLike, **kwargs: Any) -> NDArray: + """ + Apply the *LUT* sequence sequentially to given *RGB* colourspace + array. + + Parameters + ---------- + RGB + *RGB* colourspace array to apply the *LUT* sequence sequentially + onto. + + Other Parameters + ---------------- + kwargs + Keywords arguments, the keys must be the class type names for which + they are intended to be used with. There is no implemented way to + discriminate which class instance the keyword arguments should be + used with, thus if many class instances of the same type are + members of the sequence, any matching keyword arguments will be + used with all the class instances. + + Returns + ------- + :class:`numpy.ndarray` + Processed *RGB* colourspace array. + + Examples + -------- + >>> import numpy as np + >>> from colour.io.luts import LUT1D, LUT3x1D, LUT3D + >>> from colour.utilities import tstack + >>> LUT_1 = LUT1D(LUT1D.linear_table(16) + 0.125) + >>> LUT_2 = LUT3D(LUT3D.linear_table(16) ** (1 / 2.2)) + >>> LUT_3 = LUT3x1D(LUT3x1D.linear_table(16) * 0.750) + >>> LUT_sequence = LUTSequence(LUT_1, LUT_2, LUT_3) + >>> samples = np.linspace(0, 1, 5) + >>> RGB = tstack([samples, samples, samples]) + >>> LUT_sequence.apply(RGB, LUT1D={'direction': 'Inverse'}) + ... # doctest: +ELLIPSIS + array([[ 0. ..., 0. ..., 0. ...], + [ 0.2899886..., 0.2899886..., 0.2899886...], + [ 0.4797662..., 0.4797662..., 0.4797662...], + [ 0.6055328..., 0.6055328..., 0.6055328...], + [ 0.7057779..., 0.7057779..., 0.7057779...]]) + """ + + RGB = as_float_array(RGB) + + RGB_o = RGB + for operator in self: + RGB_o = operator.apply( + RGB_o, **kwargs.get(operator.__class__.__name__, {}) + ) + + return RGB_o + + def copy(self) -> LUTSequence: + """ + Return a copy of the *LUT* sequence. + + Returns + ------- + :class:`colour.LUTSequence` + *LUT* sequence copy. + """ + + return deepcopy(self) diff --git a/colour/io/luts/sony_spi1d.py b/colour/io/luts/sony_spi1d.py index 9a8dfc279e..4648961309 100644 --- a/colour/io/luts/sony_spi1d.py +++ b/colour/io/luts/sony_spi1d.py @@ -1,45 +1,53 @@ -# -*- coding: utf-8 -*- """ Sony .spi1d LUT Format Input / Output Utilities =============================================== -Defines *Sony* *.spi1d* *LUT* Format related input / output utilities objects. +Defines the *Sony* *.spi1d* *LUT* format related input / output utilities +objects: - :func:`colour.io.read_LUT_SonySPI1D` - :func:`colour.io.write_LUT_SonySPI1D` """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.constants import DEFAULT_INT_DTYPE from colour.io.luts import LUT1D, LUT3x1D, LUTSequence from colour.io.luts.common import path_to_title -from colour.utilities import as_float_array, usage_warning - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['read_LUT_SonySPI1D', 'write_LUT_SonySPI1D'] - - -def read_LUT_SonySPI1D(path): +from colour.hints import Boolean, Integer, List, Tuple, Union +from colour.utilities import ( + as_float_array, + as_int_scalar, + attest, + usage_warning, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "read_LUT_SonySPI1D", + "write_LUT_SonySPI1D", +] + + +def read_LUT_SonySPI1D(path: str) -> Union[LUT1D, LUT3x1D]: """ - Reads given *Sony* *.spi1d* *LUT* file. + Read given *Sony* *.spi1d* *LUT* file. Parameters ---------- - path : unicode + path *LUT* path. Returns ------- - LUT1D or LUT3x1D + :class:`colour.LUT1D` or :class:`colour.LUT3x1D` :class:`LUT1D` or :class:`LUT3x1D` class instance. Examples @@ -80,68 +88,82 @@ def read_LUT_SonySPI1D(path): title = path_to_title(path) domain_min, domain_max = np.array([0, 1]) dimensions = 1 - table = [] + data = [] comments = [] with open(path) as spi1d_file: lines = filter(None, (line.strip() for line in spi1d_file.readlines())) for line in lines: - if line.startswith('#'): + if line.startswith("#"): comments.append(line[1:].strip()) continue tokens = line.split() - if tokens[0] == 'Version': + if tokens[0] == "Version": continue - if tokens[0] == 'From': + if tokens[0] == "From": domain_min, domain_max = as_float_array(tokens[1:]) - elif tokens[0] == 'Length': + elif tokens[0] == "Length": continue - elif tokens[0] == 'Components': - component = DEFAULT_INT_DTYPE(tokens[1]) - assert component in (1, 3), ( - 'Only 1 or 3 components are supported!') + elif tokens[0] == "Components": + component = as_int_scalar(tokens[1]) + attest( + component in (1, 3), + "Only 1 or 3 components are supported!", + ) dimensions = 1 if component == 1 else 2 - elif tokens[0] in ('{', '}'): + elif tokens[0] in ("{", "}"): continue else: - table.append(tokens) + data.append(tokens) - table = as_float_array(table) + table = as_float_array(data) + + LUT: Union[LUT1D, LUT3x1D] if dimensions == 1: - return LUT1D( + LUT = LUT1D( np.squeeze(table), title, np.array([domain_min, domain_max]), - comments=comments) + comments=comments, + ) elif dimensions == 2: - return LUT3x1D( + LUT = LUT3x1D( table, title, - np.array([[domain_min, domain_min, domain_min], - [domain_max, domain_max, domain_max]]), - comments=comments) + np.array( + [ + [domain_min, domain_min, domain_min], + [domain_max, domain_max, domain_max], + ] + ), + comments=comments, + ) + + return LUT -def write_LUT_SonySPI1D(LUT, path, decimals=7): +def write_LUT_SonySPI1D( + LUT: Union[LUT1D, LUT3x1D, LUTSequence], path: str, decimals: Integer = 7 +) -> Boolean: """ - Writes given *LUT* to given *Sony* *.spi1d* *LUT* file. + Write given *LUT* to given *Sony* *.spi1d* *LUT* file. Parameters ---------- - LUT : LUT1D or LUT2d + LUT :class:`LUT1D`, :class:`LUT3x1D` or :class:`LUTSequence` class instance to write at given path. - path : unicode + path *LUT* path. - decimals : int, optional + decimals Formatting decimals. Returns ------- - bool + :class:`bool` Definition success. Warnings @@ -174,53 +196,58 @@ def write_LUT_SonySPI1D(LUT, path, decimals=7): """ if isinstance(LUT, LUTSequence): - LUT = LUT[0] - usage_warning('"LUT" is a "LUTSequence" instance was passed, ' - 'using first sequence "LUT":\n' - '{0}'.format(LUT)) + usage_warning( + f'"LUT" is a "LUTSequence" instance was passed, using first ' + f'sequence "LUT":\n{LUT}' + ) + LUTxD = LUT[0] + else: + LUTxD = LUT - assert not LUT.is_domain_explicit(), '"LUT" domain must be implicit!' + attest(not LUTxD.is_domain_explicit(), '"LUT" domain must be implicit!') - assert (isinstance(LUT, LUT1D) or isinstance( - LUT, LUT3x1D)), ('"LUT" must be either a 1D or 3x1D "LUT"!') + attest( + isinstance(LUTxD, LUT1D) or isinstance(LUTxD, LUT3x1D), + '"LUT" must be either a 1D or 3x1D "LUT"!', + ) - is_1D = isinstance(LUT, LUT1D) + is_1D = isinstance(LUTxD, LUT1D) if is_1D: - domain = LUT.domain + domain = LUTxD.domain else: - domain = np.unique(LUT.domain) + domain = np.unique(LUTxD.domain) - assert len(domain) == 2, 'Non-uniform "LUT" domain is unsupported!' + attest(len(domain) == 2, 'Non-uniform "LUT" domain is unsupported!') - def _format_array(array): - """ - Formats given array as a *Sony* *.spi1d* data row. - """ + def _format_array(array: Union[List, Tuple]) -> str: + """Format given array as a *Sony* *.spi1d* data row.""" - return ' {1:0.{0}f} {2:0.{0}f} {3:0.{0}f}'.format(decimals, *array) + return " {1:0.{0}f} {2:0.{0}f} {3:0.{0}f}".format(decimals, *array) - with open(path, 'w') as spi1d_file: - spi1d_file.write('Version 1\n') + with open(path, "w") as spi1d_file: + spi1d_file.write("Version 1\n") - spi1d_file.write('From {1:0.{0}f} {2:0.{0}f}\n'.format( - decimals, *domain)) + spi1d_file.write( + "From {1:0.{0}f} {2:0.{0}f}\n".format(decimals, *domain) + ) - spi1d_file.write('Length {0}\n'.format(LUT.table.size if is_1D else - LUT.table.shape[0])) + spi1d_file.write( + f"Length {LUTxD.table.size if is_1D else LUTxD.table.shape[0]}\n" + ) - spi1d_file.write('Components {0}\n'.format(1 if is_1D else 3)) + spi1d_file.write(f"Components {1 if is_1D else 3}\n") - spi1d_file.write('{\n') - for row in LUT.table: + spi1d_file.write("{\n") + for row in LUTxD.table: if is_1D: - spi1d_file.write(' {1:0.{0}f}\n'.format(decimals, row)) + spi1d_file.write(" {1:0.{0}f}\n".format(decimals, row)) else: - spi1d_file.write('{0}\n'.format(_format_array(row))) - spi1d_file.write('}\n') + spi1d_file.write(f"{_format_array(row)}\n") + spi1d_file.write("}\n") - if LUT.comments: - for comment in LUT.comments: - spi1d_file.write('# {0}\n'.format(comment)) + if LUTxD.comments: + for comment in LUTxD.comments: + spi1d_file.write(f"# {comment}\n") return True diff --git a/colour/io/luts/sony_spi3d.py b/colour/io/luts/sony_spi3d.py index d1ac11d3bd..fb29094922 100644 --- a/colour/io/luts/sony_spi3d.py +++ b/colour/io/luts/sony_spi3d.py @@ -1,46 +1,55 @@ -# -*- coding: utf-8 -*- """ Sony .spi3d LUT Format Input / Output Utilities =============================================== -Defines *Sony* *.spi3d* *LUT* Format related input / output utilities objects. +Defines the *Sony* *.spi3d* *LUT* format related input / output utilities +objects: - :func:`colour.io.read_LUT_SonySPI3D` - :func:`colour.io.write_LUT_SonySPI3D` """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.constants import DEFAULT_INT_DTYPE from colour.io.luts import LUT3D, LUTSequence from colour.io.luts.common import path_to_title -from colour.utilities import as_int_array, usage_warning, as_float_array - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['read_LUT_SonySPI3D', 'write_LUT_SonySPI3D'] - - -def read_LUT_SonySPI3D(path): +from colour.hints import Boolean, Integer, List, Tuple, Union +from colour.utilities import ( + as_float_array, + as_int_array, + as_int_scalar, + attest, + usage_warning, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "read_LUT_SonySPI3D", + "write_LUT_SonySPI3D", +] + + +def read_LUT_SonySPI3D(path: str) -> LUT3D: """ - Reads given *Sony* *.spi3d* *LUT* file. + Read given *Sony* *.spi3d* *LUT* file. Parameters ---------- - path : unicode + path *LUT* path. Returns ------- - LUT3D or LUT3x1D - :class:`LUT3D` or :class:`LUT3x1D` class instance. + :class:`colour.LUT3D` + :class:`LUT3D` class instance. Examples -------- @@ -75,61 +84,71 @@ def read_LUT_SonySPI3D(path): title = path_to_title(path) domain_min, domain_max = np.array([0, 0, 0]), np.array([1, 1, 1]) - size = 2 - indexes = [] - table = [] + size: Integer = 2 + data_table = [] + data_indexes = [] comments = [] with open(path) as spi3d_file: lines = filter(None, (line.strip() for line in spi3d_file.readlines())) for line in lines: - if line.startswith('#'): + if line.startswith("#"): comments.append(line[1:].strip()) continue tokens = line.split() if len(tokens) == 3: - assert len(set(tokens)) == 1, ( - 'Non-uniform "LUT" shape is unsupported!') + attest( + len(set(tokens)) == 1, + 'Non-uniform "LUT" shape is unsupported!', + ) - size = DEFAULT_INT_DTYPE(tokens[0]) + size = as_int_scalar(tokens[0]) if len(tokens) == 6: - indexes.append(as_int_array(tokens[:3])) - table.append(as_float_array(tokens[3:])) + data_table.append(as_float_array(tokens[3:])) + data_indexes.append(as_int_array(tokens[:3])) - indexes = as_int_array(indexes) + indexes = as_int_array(data_indexes) sorting_indexes = np.lexsort((indexes[:, 2], indexes[:, 1], indexes[:, 0])) - assert np.array_equal( - indexes[sorting_indexes], - DEFAULT_INT_DTYPE(np.around( - LUT3D.linear_table(size) * (size - 1))).reshape( - (-1, 3))), 'Indexes do not match expected "LUT3D" indexes!' + attest( + np.array_equal( + indexes[sorting_indexes], + as_int_array( + np.around(LUT3D.linear_table(size) * (size - 1)) + ).reshape((-1, 3)), + ), + 'Indexes do not match expected "LUT3D" indexes!', + ) - table = as_float_array(table)[sorting_indexes].reshape( - [size, size, size, 3]) + table = as_float_array(data_table)[sorting_indexes].reshape( + [size, size, size, 3] + ) return LUT3D( - table, title, np.vstack([domain_min, domain_max]), comments=comments) + table, title, np.vstack([domain_min, domain_max]), comments=comments + ) -def write_LUT_SonySPI3D(LUT, path, decimals=7): +def write_LUT_SonySPI3D( + LUT: Union[LUT3D, LUTSequence], path: str, decimals: Integer = 7 +) -> Boolean: """ - Writes given *LUT* to given *Sony* *.spi3d* *LUT* file. + Write given *LUT* to given *Sony* *.spi3d* *LUT* file. Parameters ---------- - LUT : LUT3D + LUT :class:`LUT3D` or :class:`LUTSequence` class instance to write at given path. - path : unicode + path *LUT* path. - decimals : int, optional + decimals Formatting decimals. Returns ------- - bool + :class:`bool` Definition success. Warnings @@ -150,46 +169,55 @@ def write_LUT_SonySPI3D(LUT, path, decimals=7): """ if isinstance(LUT, LUTSequence): - LUT = LUT[0] - usage_warning('"LUT" is a "LUTSequence" instance was passed, ' - 'using first sequence "LUT":\n' - '{0}'.format(LUT)) - - assert not LUT.is_domain_explicit(), '"LUT" domain must be implicit!' - - assert isinstance(LUT, LUT3D), '"LUT" must be either a 3D "LUT"!' - - assert np.array_equal(LUT.domain, np.array([ - [0, 0, 0], - [1, 1, 1], - ])), '"LUT" domain must be [[0, 0, 0], [1, 1, 1]]!' - - def _format_array(array): - """ - Formats given array as a *Sony* *.spi3d* data row. - """ - - return '{1:d} {2:d} {3:d} {4:0.{0}f} {5:0.{0}f} {6:0.{0}f}'.format( - decimals, *array) - - with open(path, 'w') as spi3d_file: - spi3d_file.write('SPILUT 1.0\n') - - spi3d_file.write('3 3\n') - - spi3d_file.write('{0} {0} {0}\n'.format(LUT.size)) - - indexes = DEFAULT_INT_DTYPE( - np.around(LUT.linear_table(LUT.size) * (LUT.size - 1))).reshape( - [-1, 3]) - table = LUT.table.reshape([-1, 3]) + usage_warning( + f'"LUT" is a "LUTSequence" instance was passed, using first ' + f'sequence "LUT":\n{LUT}' + ) + LUTxD = LUT[0] + else: + LUTxD = LUT + + attest(not LUTxD.is_domain_explicit(), '"LUT" domain must be implicit!') + + attest(isinstance(LUTxD, LUT3D), '"LUT" must be either a 3D "LUT"!') + + attest( + np.array_equal( + LUTxD.domain, + np.array( + [ + [0, 0, 0], + [1, 1, 1], + ] + ), + ), + '"LUT" domain must be [[0, 0, 0], [1, 1, 1]]!', + ) + + def _format_array(array: Union[List, Tuple]) -> str: + """Format given array as a *Sony* *.spi3d* data row.""" + + return "{1:d} {2:d} {3:d} {4:0.{0}f} {5:0.{0}f} {6:0.{0}f}".format( + decimals, *array + ) + + with open(path, "w") as spi3d_file: + spi3d_file.write("SPILUT 1.0\n") + + spi3d_file.write("3 3\n") + + spi3d_file.write("{0} {0} {0}\n".format(LUTxD.size)) + + indexes = as_int_array( + np.around(LUTxD.linear_table(LUTxD.size) * (LUTxD.size - 1)) + ).reshape([-1, 3]) + table = LUTxD.table.reshape([-1, 3]) for i, row in enumerate(indexes): - spi3d_file.write('{0}\n'.format( - _format_array(list(row) + list(table[i])))) + spi3d_file.write(f"{_format_array(list(row) + list(table[i]))}\n") - if LUT.comments: - for comment in LUT.comments: - spi3d_file.write('# {0}\n'.format(comment)) + if LUTxD.comments: + for comment in LUTxD.comments: + spi3d_file.write(f"# {comment}\n") return True diff --git a/colour/io/luts/sony_spimtx.py b/colour/io/luts/sony_spimtx.py new file mode 100644 index 0000000000..851caa4cc7 --- /dev/null +++ b/colour/io/luts/sony_spimtx.py @@ -0,0 +1,117 @@ +""" +Sony .spimtx LUT Format Input / Output Utilities +================================================ + +Defines *Sony* *.spimtx* *LUT* format related input / output utilities objects. + +- :func:`colour.io.read_LUT_SonySPImtx` +- :func:`colour.io.write_LUT_SonySPImtx` +""" + +from __future__ import annotations + +import numpy as np + +from colour.constants import DEFAULT_FLOAT_DTYPE +from colour.hints import Integer +from colour.io.luts.common import path_to_title +from colour.io.luts import LUTOperatorMatrix +from colour.hints import Boolean + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "read_LUT_SonySPImtx", + "write_LUT_SonySPImtx", +] + + +def read_LUT_SonySPImtx(path: str) -> LUTOperatorMatrix: + """ + Read given *Sony* *.spimtx* *LUT* file. + + Parameters + ---------- + path + *LUT* path. + + Returns + ------- + :class:`colour.LUTOperatorMatrix` + :class:`colour.io.Matrix` class instance. + + Examples + -------- + >>> import os + >>> path = os.path.join( + ... os.path.dirname(__file__), 'tests', 'resources', 'sony_spimtx', + ... 'dt.spimtx') + >>> print(read_LUT_SonySPImtx(path)) + LUTOperatorMatrix - dt + ---------------------- + + Matrix : [[ 0.864274 0. 0. 0. ] + [ 0. 0.864274 0. 0. ] + [ 0. 0. 0.864274 0. ] + [ 0. 0. 0. 1. ]] + Offset : [ 0. 0. 0. 0.] + """ + + matrix = np.loadtxt(path, dtype=DEFAULT_FLOAT_DTYPE) + matrix = np.reshape(matrix, (3, 4)) + offset = matrix[:, 3] / 65535 + matrix = matrix[:3, :3] + + title = path_to_title(path) + + return LUTOperatorMatrix(matrix, offset, name=title) + + +def write_LUT_SonySPImtx( + LUT: LUTOperatorMatrix, path: str, decimals: Integer = 7 +) -> Boolean: + """ + Write given *LUT* to given *Sony* *.spimtx* *LUT* file. + + Parameters + ---------- + LUT + :class:`colour.LUTOperatorMatrix` class instance to write at given + path. + path + *LUT* path. + decimals + Formatting decimals. + + Returns + ------- + :class:`bool` + Definition success. + + Examples + -------- + >>> matrix = np.array([[ 1.45143932, -0.23651075, -0.21492857], + ... [-0.07655377, 1.1762297 , -0.09967593], + ... [ 0.00831615, -0.00603245, 0.9977163 ]]) + >>> M = LUTOperatorMatrix(matrix) + >>> write_LUT_SonySPI1D(M, 'My_LUT.spimtx') # doctest: +SKIP + """ + + matrix, offset = LUT.matrix, LUT.offset + offset *= 65535 + + array = np.hstack( + [ + np.reshape(matrix, (4, 4))[:3, :3], + np.transpose(np.array([offset[:3]])), + ] + ) + + np.savetxt(path, array, fmt=f"%.{decimals}f") + + return True diff --git a/colour/io/luts/tests/__init__.py b/colour/io/luts/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/io/luts/tests/__init__.py +++ b/colour/io/luts/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/io/luts/tests/resources/cinespace/Non_Uniform.csp b/colour/io/luts/tests/resources/cinespace/Explicit_Domain.csp similarity index 100% rename from colour/io/luts/tests/resources/cinespace/Non_Uniform.csp rename to colour/io/luts/tests/resources/cinespace/Explicit_Domain.csp diff --git a/colour/io/luts/tests/resources/cinespace/Ragged.csp b/colour/io/luts/tests/resources/cinespace/Ragged_Domain.csp similarity index 100% rename from colour/io/luts/tests/resources/cinespace/Ragged.csp rename to colour/io/luts/tests/resources/cinespace/Ragged_Domain.csp diff --git a/colour/io/luts/tests/resources/cinespace/Three_Dimensional_Table.csp b/colour/io/luts/tests/resources/cinespace/Three_Dimensional_Table.csp index accd108de8..72b538ab4a 100644 --- a/colour/io/luts/tests/resources/cinespace/Three_Dimensional_Table.csp +++ b/colour/io/luts/tests/resources/cinespace/Three_Dimensional_Table.csp @@ -2,7 +2,7 @@ CSPLUTV100 3D BEGIN METADATA -ThreeDimensionalTable +LUT3D without My Shaper END METADATA 2 diff --git a/colour/io/luts/tests/resources/cinespace/Three_Dimensional_Table_With_Shaper.csp b/colour/io/luts/tests/resources/cinespace/Three_Dimensional_Table_With_Shaper.csp index 4d75f78077..23c8fa4af7 100644 --- a/colour/io/luts/tests/resources/cinespace/Three_Dimensional_Table_With_Shaper.csp +++ b/colour/io/luts/tests/resources/cinespace/Three_Dimensional_Table_With_Shaper.csp @@ -2,7 +2,7 @@ CSPLUTV100 3D BEGIN METADATA -LUT3D with My Shaper - Cube +LUT3D with My Shaper A first "Shaper" comment. A second "Shaper" comment. A first "LUT3D" comment. diff --git a/colour/io/luts/tests/resources/cinespace/Uncommon_3x1D_With_Pre_Lut.csp b/colour/io/luts/tests/resources/cinespace/Uncommon_3x1D_With_Pre_Lut.csp index 4947cea71f..d9bc5c1466 100644 --- a/colour/io/luts/tests/resources/cinespace/Uncommon_3x1D_With_Pre_Lut.csp +++ b/colour/io/luts/tests/resources/cinespace/Uncommon_3x1D_With_Pre_Lut.csp @@ -2,8 +2,8 @@ CSPLUTV100 1D BEGIN METADATA -lin to LogC -LogC to lin +Linear to LogC +LogC to Linear END METADATA 5 diff --git a/colour/io/luts/tests/resources/sony_spimtx/Matrix_Offset.spimtx b/colour/io/luts/tests/resources/sony_spimtx/Matrix_Offset.spimtx new file mode 100644 index 0000000000..800a200a58 --- /dev/null +++ b/colour/io/luts/tests/resources/sony_spimtx/Matrix_Offset.spimtx @@ -0,0 +1,3 @@ +1. 0. 0. 0. +0. 1. 0. 0. +0. 0. 1. 65535. \ No newline at end of file diff --git a/colour/io/luts/tests/resources/sony_spimtx/dt.spimtx b/colour/io/luts/tests/resources/sony_spimtx/dt.spimtx new file mode 100644 index 0000000000..e9530f0e7b --- /dev/null +++ b/colour/io/luts/tests/resources/sony_spimtx/dt.spimtx @@ -0,0 +1 @@ +0.864274 0 0 0 0 0.864274 0 0 0 0 0.864274 0 diff --git a/colour/io/luts/tests/resources/sony_spimtx/p3_to_xyz16.spimtx b/colour/io/luts/tests/resources/sony_spimtx/p3_to_xyz16.spimtx new file mode 100644 index 0000000000..03e316e3e0 --- /dev/null +++ b/colour/io/luts/tests/resources/sony_spimtx/p3_to_xyz16.spimtx @@ -0,0 +1,4 @@ +.44488 .27717 .17237 0 +.20936 .7217 .06895 0 +0 .04707 .9078 0 + diff --git a/colour/io/luts/tests/test__init__.py b/colour/io/luts/tests/test__init__.py index 0645797a20..4bd7f48c2b 100644 --- a/colour/io/luts/tests/test__init__.py +++ b/colour/io/luts/tests/test__init__.py @@ -1,9 +1,6 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.io.luts.__init__` module. -""" +"""Defines the unit tests for the :mod:`colour.io.luts.__init__` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import os @@ -13,144 +10,176 @@ from colour.io import LUTSequence, read_LUT, write_LUT -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['LUTS_DIRECTORY', 'TestReadLUT', 'TestWriteLUT'] +__all__ = [ + "LUTS_DIRECTORY", + "TestReadLUT", + "TestWriteLUT", +] -LUTS_DIRECTORY = os.path.join(os.path.dirname(__file__), 'resources') +LUTS_DIRECTORY: str = os.path.join(os.path.dirname(__file__), "resources") class TestReadLUT(unittest.TestCase): """ - Defines :func:`colour.io.luts.__init__.read_LUT` definition unit tests + Define :func:`colour.io.luts.__init__.read_LUT` definition unit tests methods. """ def test_read_LUT(self): - """ - Tests :func:`colour.io.luts.__init__.read_LUT` definition. - """ + """Test :func:`colour.io.luts.__init__.read_LUT` definition.""" LUT_1 = read_LUT( - os.path.join(LUTS_DIRECTORY, 'sony_spi1d', 'eotf_sRGB_1D.spi1d')) + os.path.join(LUTS_DIRECTORY, "sony_spi1d", "eotf_sRGB_1D.spi1d") + ) np.testing.assert_almost_equal( LUT_1.table, - np.array([ - -7.73990000e-03, 5.16000000e-04, 1.22181000e-02, - 3.96819000e-02, 8.71438000e-02, 1.57439400e-01, 2.52950100e-01, - 3.75757900e-01, 5.27729400e-01, 7.10566500e-01, 9.25840600e-01, - 1.17501630e+00, 1.45946870e+00, 1.78049680e+00, 2.13933380e+00, - 2.53715520e+00 - ])) - self.assertEqual(LUT_1.name, 'eotf sRGB 1D') + np.array( + [ + -7.73990000e-03, + 5.16000000e-04, + 1.22181000e-02, + 3.96819000e-02, + 8.71438000e-02, + 1.57439400e-01, + 2.52950100e-01, + 3.75757900e-01, + 5.27729400e-01, + 7.10566500e-01, + 9.25840600e-01, + 1.17501630e00, + 1.45946870e00, + 1.78049680e00, + 2.13933380e00, + 2.53715520e00, + ] + ), + ) + self.assertEqual(LUT_1.name, "eotf sRGB 1D") self.assertEqual(LUT_1.dimensions, 1) np.testing.assert_array_equal(LUT_1.domain, np.array([-0.1, 1.5])) self.assertEqual(LUT_1.size, 16) self.assertListEqual( LUT_1.comments, - ['Generated by "Colour 0.3.11".', '"colour.models.eotf_sRGB".']) + ['Generated by "Colour 0.3.11".', '"colour.models.eotf_sRGB".'], + ) LUT_2 = read_LUT( - os.path.join(LUTS_DIRECTORY, 'resolve_cube', 'LogC_Video.cube')) + os.path.join(LUTS_DIRECTORY, "resolve_cube", "LogC_Video.cube") + ) np.testing.assert_almost_equal( LUT_2[0].table, - np.array([ - [0.00000000, 0.00000000, 0.00000000], - [0.02708500, 0.02708500, 0.02708500], - [0.06304900, 0.06304900, 0.06304900], - [0.11314900, 0.11314900, 0.11314900], - [0.18304900, 0.18304900, 0.18304900], - [0.28981100, 0.28981100, 0.28981100], - [0.41735300, 0.41735300, 0.41735300], - [0.54523100, 0.54523100, 0.54523100], - [0.67020500, 0.67020500, 0.67020500], - [0.78963000, 0.78963000, 0.78963000], - [0.88646800, 0.88646800, 0.88646800], - [0.94549100, 0.94549100, 0.94549100], - [0.97644900, 0.97644900, 0.97644900], - [0.98924800, 0.98924800, 0.98924800], - [0.99379700, 0.99379700, 0.99379700], - [1.00000000, 1.00000000, 1.00000000], - ]), + np.array( + [ + [0.00000000, 0.00000000, 0.00000000], + [0.02708500, 0.02708500, 0.02708500], + [0.06304900, 0.06304900, 0.06304900], + [0.11314900, 0.11314900, 0.11314900], + [0.18304900, 0.18304900, 0.18304900], + [0.28981100, 0.28981100, 0.28981100], + [0.41735300, 0.41735300, 0.41735300], + [0.54523100, 0.54523100, 0.54523100], + [0.67020500, 0.67020500, 0.67020500], + [0.78963000, 0.78963000, 0.78963000], + [0.88646800, 0.88646800, 0.88646800], + [0.94549100, 0.94549100, 0.94549100], + [0.97644900, 0.97644900, 0.97644900], + [0.98924800, 0.98924800, 0.98924800], + [0.99379700, 0.99379700, 0.99379700], + [1.00000000, 1.00000000, 1.00000000], + ] + ), ) self.assertEqual(LUT_2[1].size, 4) def test_raise_exception_read_LUT(self): """ - Tests :func:`colour.io.luts.__init__.read_LUT` definition raised + Test :func:`colour.io.luts.__init__.read_LUT` definition raised exception. """ self.assertRaises( - ValueError, read_LUT, - os.path.join(LUTS_DIRECTORY, 'sony_spi1d', - 'Exception_Raising.spi1d')) + ValueError, + read_LUT, + os.path.join( + LUTS_DIRECTORY, "sony_spi1d", "Exception_Raising.spi1d" + ), + ) class TestWriteLUT(unittest.TestCase): """ - Defines :func:`colour.io.luts.__init__.write_LUT` definition unit tests + Define :func:`colour.io.luts.__init__.write_LUT` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._temporary_directory = tempfile.mkdtemp() def tearDown(self): - """ - After tests actions. - """ + """After tests actions.""" shutil.rmtree(self._temporary_directory) def test_write_LUT(self): - """ - Tests :func:`colour.io.luts.__init__.write_LUT` definition. - """ + """Test :func:`colour.io.luts.__init__.write_LUT` definition.""" LUT_1_r = read_LUT( - os.path.join(LUTS_DIRECTORY, 'sony_spi1d', 'eotf_sRGB_1D.spi1d')) + os.path.join(LUTS_DIRECTORY, "sony_spi1d", "eotf_sRGB_1D.spi1d") + ) write_LUT( LUT_1_r, - os.path.join(self._temporary_directory, 'eotf_sRGB_1D.spi1d')) + os.path.join(self._temporary_directory, "eotf_sRGB_1D.spi1d"), + ) LUT_1_t = read_LUT( - os.path.join(self._temporary_directory, 'eotf_sRGB_1D.spi1d')) + os.path.join(self._temporary_directory, "eotf_sRGB_1D.spi1d") + ) self.assertEqual(LUT_1_r, LUT_1_t) write_LUT( LUTSequence(LUT_1_r), - os.path.join(self._temporary_directory, 'eotf_sRGB_1D.spi1d')) + os.path.join(self._temporary_directory, "eotf_sRGB_1D.spi1d"), + ) self.assertEqual(LUT_1_r, LUT_1_t) LUT_2_r = read_LUT( - os.path.join(LUTS_DIRECTORY, 'resolve_cube', - 'Three_Dimensional_Table_With_Shaper.cube')) + os.path.join( + LUTS_DIRECTORY, + "resolve_cube", + "Three_Dimensional_Table_With_Shaper.cube", + ) + ) write_LUT( LUT_2_r, - os.path.join(self._temporary_directory, - 'Three_Dimensional_Table_With_Shaper.cube')) + os.path.join( + self._temporary_directory, + "Three_Dimensional_Table_With_Shaper.cube", + ), + ) LUT_2_t = read_LUT( - os.path.join(self._temporary_directory, - 'Three_Dimensional_Table_With_Shaper.cube')) + os.path.join( + self._temporary_directory, + "Three_Dimensional_Table_With_Shaper.cube", + ) + ) self.assertEqual(LUT_2_r, LUT_2_t) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/io/luts/tests/test_cinespace_csp.py b/colour/io/luts/tests/test_cinespace_csp.py index 6bd58a32d5..553f96ab76 100644 --- a/colour/io/luts/tests/test_cinespace_csp.py +++ b/colour/io/luts/tests/test_cinespace_csp.py @@ -1,9 +1,6 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.io.luts.cinespace_csp` module. -""" +"""Defines the unit tests for the :mod:`colour.io.luts.cinespace_csp` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import os @@ -15,206 +12,279 @@ from colour.io import read_LUT_Cinespace, write_LUT_Cinespace from colour.utilities import tstack -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['LUTS_DIRECTORY', 'TestReadLUTCinespace', 'TestWriteLUTCinespace'] +__all__ = [ + "LUTS_DIRECTORY", + "TestReadLUTCinespace", + "TestWriteLUTCinespace", +] -LUTS_DIRECTORY = os.path.join( - os.path.dirname(__file__), 'resources', 'cinespace') +LUTS_DIRECTORY: str = os.path.join( + os.path.dirname(__file__), "resources", "cinespace" +) class TestReadLUTCinespace(unittest.TestCase): """ - Defines :func:`colour.io.luts.cinespace_csp.read_LUT_Cinespace` definition + Define :func:`colour.io.luts.cinespace_csp.read_LUT_Cinespace` definition unit tests methods. """ def test_read_LUT_Cinespace(self): """ - Tests :func:`colour.io.luts.cinespace_csp.read_LUT_Cinespace` + Test :func:`colour.io.luts.cinespace_csp.read_LUT_Cinespace` definition. """ LUT_1 = read_LUT_Cinespace( - os.path.join(LUTS_DIRECTORY, 'ACES_Proxy_10_to_ACES.csp')) + os.path.join(LUTS_DIRECTORY, "ACES_Proxy_10_to_ACES.csp") + ) np.testing.assert_almost_equal( LUT_1.table, - np.array([ - [4.88300000e-04, 4.88300000e-04, 4.88300000e-04], - [7.71400000e-04, 7.71400000e-04, 7.71400000e-04], - [1.21900000e-03, 1.21900000e-03, 1.21900000e-03], - [1.92600000e-03, 1.92600000e-03, 1.92600000e-03], - [3.04400000e-03, 3.04400000e-03, 3.04400000e-03], - [4.80900000e-03, 4.80900000e-03, 4.80900000e-03], - [7.59900000e-03, 7.59900000e-03, 7.59900000e-03], - [1.20100000e-02, 1.20100000e-02, 1.20100000e-02], - [1.89700000e-02, 1.89700000e-02, 1.89700000e-02], - [2.99800000e-02, 2.99800000e-02, 2.99800000e-02], - [4.73700000e-02, 4.73700000e-02, 4.73700000e-02], - [7.48400000e-02, 7.48400000e-02, 7.48400000e-02], - [1.18300000e-01, 1.18300000e-01, 1.18300000e-01], - [1.86900000e-01, 1.86900000e-01, 1.86900000e-01], - [2.95200000e-01, 2.95200000e-01, 2.95200000e-01], - [4.66500000e-01, 4.66500000e-01, 4.66500000e-01], - [7.37100000e-01, 7.37100000e-01, 7.37100000e-01], - [1.16500000e+00, 1.16500000e+00, 1.16500000e+00], - [1.84000000e+00, 1.84000000e+00, 1.84000000e+00], - [2.90800000e+00, 2.90800000e+00, 2.90800000e+00], - [4.59500000e+00, 4.59500000e+00, 4.59500000e+00], - [7.26000000e+00, 7.26000000e+00, 7.26000000e+00], - [1.14700000e+01, 1.14700000e+01, 1.14700000e+01], - [1.81300000e+01, 1.81300000e+01, 1.81300000e+01], - [2.86400000e+01, 2.86400000e+01, 2.86400000e+01], - [4.52500000e+01, 4.52500000e+01, 4.52500000e+01], - [7.15100000e+01, 7.15100000e+01, 7.15100000e+01], - [1.13000000e+02, 1.13000000e+02, 1.13000000e+02], - [1.78500000e+02, 1.78500000e+02, 1.78500000e+02], - [2.82100000e+02, 2.82100000e+02, 2.82100000e+02], - [4.45700000e+02, 4.45700000e+02, 4.45700000e+02], - [7.04300000e+02, 7.04300000e+02, 7.04300000e+02], - ])) - self.assertEqual(LUT_1.name, 'ACES Proxy 10 to ACES') + np.array( + [ + [4.88300000e-04, 4.88300000e-04, 4.88300000e-04], + [7.71400000e-04, 7.71400000e-04, 7.71400000e-04], + [1.21900000e-03, 1.21900000e-03, 1.21900000e-03], + [1.92600000e-03, 1.92600000e-03, 1.92600000e-03], + [3.04400000e-03, 3.04400000e-03, 3.04400000e-03], + [4.80900000e-03, 4.80900000e-03, 4.80900000e-03], + [7.59900000e-03, 7.59900000e-03, 7.59900000e-03], + [1.20100000e-02, 1.20100000e-02, 1.20100000e-02], + [1.89700000e-02, 1.89700000e-02, 1.89700000e-02], + [2.99800000e-02, 2.99800000e-02, 2.99800000e-02], + [4.73700000e-02, 4.73700000e-02, 4.73700000e-02], + [7.48400000e-02, 7.48400000e-02, 7.48400000e-02], + [1.18300000e-01, 1.18300000e-01, 1.18300000e-01], + [1.86900000e-01, 1.86900000e-01, 1.86900000e-01], + [2.95200000e-01, 2.95200000e-01, 2.95200000e-01], + [4.66500000e-01, 4.66500000e-01, 4.66500000e-01], + [7.37100000e-01, 7.37100000e-01, 7.37100000e-01], + [1.16500000e00, 1.16500000e00, 1.16500000e00], + [1.84000000e00, 1.84000000e00, 1.84000000e00], + [2.90800000e00, 2.90800000e00, 2.90800000e00], + [4.59500000e00, 4.59500000e00, 4.59500000e00], + [7.26000000e00, 7.26000000e00, 7.26000000e00], + [1.14700000e01, 1.14700000e01, 1.14700000e01], + [1.81300000e01, 1.81300000e01, 1.81300000e01], + [2.86400000e01, 2.86400000e01, 2.86400000e01], + [4.52500000e01, 4.52500000e01, 4.52500000e01], + [7.15100000e01, 7.15100000e01, 7.15100000e01], + [1.13000000e02, 1.13000000e02, 1.13000000e02], + [1.78500000e02, 1.78500000e02, 1.78500000e02], + [2.82100000e02, 2.82100000e02, 2.82100000e02], + [4.45700000e02, 4.45700000e02, 4.45700000e02], + [7.04300000e02, 7.04300000e02, 7.04300000e02], + ] + ), + ) + self.assertEqual(LUT_1.name, "ACES Proxy 10 to ACES") self.assertEqual(LUT_1.dimensions, 2) - np.testing.assert_array_equal(LUT_1.domain, - np.array([[0, 0, 0], [1, 1, 1]])) + np.testing.assert_array_equal( + LUT_1.domain, np.array([[0, 0, 0], [1, 1, 1]]) + ) self.assertEqual(LUT_1.size, 32) self.assertListEqual(LUT_1.comments, []) - LUT_2 = read_LUT_Cinespace(os.path.join(LUTS_DIRECTORY, 'Demo.csp')) - self.assertListEqual(LUT_2.comments, - ['Comments are ignored by most parsers']) - np.testing.assert_array_equal(LUT_2.domain, - np.array([[0, 0, 0], [1, 2, 3]])) + LUT_2 = read_LUT_Cinespace(os.path.join(LUTS_DIRECTORY, "Demo.csp")) + self.assertListEqual( + LUT_2.comments, ["Comments are ignored by most parsers"] + ) + np.testing.assert_array_equal( + LUT_2.domain, np.array([[0, 0, 0], [1, 2, 3]]) + ) LUT_3 = read_LUT_Cinespace( - os.path.join(LUTS_DIRECTORY, 'Three_Dimensional_Table.csp')) + os.path.join(LUTS_DIRECTORY, "Three_Dimensional_Table.csp") + ) self.assertEqual(LUT_3.dimensions, 3) self.assertEqual(LUT_3.size, 2) LUT_4 = read_LUT_Cinespace( - os.path.join(LUTS_DIRECTORY, 'Non_Uniform.csp')) + os.path.join(LUTS_DIRECTORY, "Explicit_Domain.csp") + ) self.assertEqual(LUT_4[0].is_domain_explicit(), True) self.assertEqual(LUT_4[1].table.shape, (2, 3, 4, 3)) LUT_5 = read_LUT_Cinespace( - os.path.join(LUTS_DIRECTORY, 'Uncommon_3x1D_With_Pre_Lut.csp')) + os.path.join(LUTS_DIRECTORY, "Uncommon_3x1D_With_Pre_Lut.csp") + ) self.assertIsInstance(LUT_5[0], LUT3x1D) self.assertIsInstance(LUT_5[1], LUT3x1D) class TestWriteLUTCinespace(unittest.TestCase): """ - Defines :func:`colour.io.luts.cinespace_csp.write_LUT_Cinespace` definition + Define :func:`colour.io.luts.cinespace_csp.write_LUT_Cinespace` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._temporary_directory = tempfile.mkdtemp() def tearDown(self): - """ - After tests actions. - """ + """After tests actions.""" shutil.rmtree(self._temporary_directory) def test_write_LUT_Cinespace(self): """ - Tests :func:`colour.io.luts.cinespace_csp.write_LUT_Cinespace` + Test :func:`colour.io.luts.cinespace_csp.write_LUT_Cinespace` definition. """ LUT_1_r = read_LUT_Cinespace( - os.path.join(LUTS_DIRECTORY, 'ACES_Proxy_10_to_ACES.csp')) + os.path.join(LUTS_DIRECTORY, "ACES_Proxy_10_to_ACES.csp") + ) write_LUT_Cinespace( LUT_1_r, - os.path.join(self._temporary_directory, - 'ACES_Proxy_10_to_ACES.csp')) + os.path.join( + self._temporary_directory, "ACES_Proxy_10_to_ACES.csp" + ), + ) LUT_1_t = read_LUT_Cinespace( - os.path.join(self._temporary_directory, - 'ACES_Proxy_10_to_ACES.csp')) + os.path.join( + self._temporary_directory, "ACES_Proxy_10_to_ACES.csp" + ) + ) self.assertEqual(LUT_1_r, LUT_1_t) self.assertEqual(LUT_1_r, LUT_1_t) - LUT_2_r = read_LUT_Cinespace(os.path.join(LUTS_DIRECTORY, 'Demo.csp')) + LUT_2_r = read_LUT_Cinespace(os.path.join(LUTS_DIRECTORY, "Demo.csp")) write_LUT_Cinespace( - LUT_2_r, os.path.join(self._temporary_directory, 'Demo.csp')) + LUT_2_r, os.path.join(self._temporary_directory, "Demo.csp") + ) LUT_2_t = read_LUT_Cinespace( - os.path.join(self._temporary_directory, 'Demo.csp')) + os.path.join(self._temporary_directory, "Demo.csp") + ) self.assertEqual(LUT_2_r, LUT_2_t) self.assertListEqual(LUT_2_r.comments, LUT_2_t.comments) LUT_3_r = read_LUT_Cinespace( - os.path.join(LUTS_DIRECTORY, 'Three_Dimensional_Table.csp')) + os.path.join(LUTS_DIRECTORY, "Three_Dimensional_Table.csp") + ) write_LUT_Cinespace( LUT_3_r, - os.path.join(self._temporary_directory, - 'Three_Dimensional_Table.csp')) + os.path.join( + self._temporary_directory, "Three_Dimensional_Table.csp" + ), + ) LUT_3_t = read_LUT_Cinespace( - os.path.join(self._temporary_directory, - 'Three_Dimensional_Table.csp')) + os.path.join( + self._temporary_directory, "Three_Dimensional_Table.csp" + ) + ) self.assertEqual(LUT_3_r, LUT_3_t) - domain = tstack(( - np.array([0.0, 0.1, 0.2, 0.4, 0.8, 1.2]), - np.array([-0.1, 0.5, 1.0, np.nan, np.nan, np.nan]), - np.array([-1.0, -0.5, 0.0, 0.5, 1.0, np.nan]), - )) - LUT_4_t = LUT3x1D(domain=domain, table=domain * 2) + domain = tstack( + ( + np.array([0.0, 0.1, 0.2, 0.4, 0.8, 1.2]), + np.array([-0.1, 0.5, 1.0, np.nan, np.nan, np.nan]), + np.array([-1.0, -0.5, 0.0, 0.5, 1.0, np.nan]), + ) + ) + LUT_4_t = LUT3x1D( + domain=domain, table=domain * 2, name="Ragged Domain" + ) write_LUT_Cinespace( - LUT_4_t, os.path.join(self._temporary_directory, - 'RaggedDomain.csp')) + LUT_4_t, + os.path.join(self._temporary_directory, "Ragged_Domain.csp"), + ) LUT_4_r = read_LUT_Cinespace( - os.path.join(self._temporary_directory, 'RaggedDomain.csp')) + os.path.join(LUTS_DIRECTORY, "Ragged_Domain.csp") + ) np.testing.assert_almost_equal(LUT_4_t.domain, LUT_4_r.domain) np.testing.assert_almost_equal(LUT_4_t.table, LUT_4_r.table, decimal=6) LUT_5_r = read_LUT_Cinespace( - os.path.join(LUTS_DIRECTORY, - 'Three_Dimensional_Table_With_Shaper.csp')) + os.path.join( + LUTS_DIRECTORY, "Three_Dimensional_Table_With_Shaper.csp" + ) + ) LUT_5_r.sequence[0] = LUT_5_r.sequence[0].as_LUT( - LUT3x1D, force_conversion=True) + LUT1D, force_conversion=True + ) write_LUT_Cinespace( LUT_5_r, - os.path.join(self._temporary_directory, - 'Three_Dimensional_Table_With_Shaper.csp')) - LUT_5_t = read_LUT_Cinespace( - os.path.join(self._temporary_directory, - 'Three_Dimensional_Table_With_Shaper.csp')) + os.path.join( + self._temporary_directory, + "Three_Dimensional_Table_With_Shaper.csp", + ), + ) LUT_5_r = read_LUT_Cinespace( - os.path.join(LUTS_DIRECTORY, - 'Three_Dimensional_Table_With_Shaper.csp')) + os.path.join( + LUTS_DIRECTORY, "Three_Dimensional_Table_With_Shaper.csp" + ) + ) + LUT_5_t = read_LUT_Cinespace( + os.path.join( + self._temporary_directory, + "Three_Dimensional_Table_With_Shaper.csp", + ) + ) self.assertEqual(LUT_5_r, LUT_5_t) LUT_6_r = read_LUT_Cinespace( - os.path.join(LUTS_DIRECTORY, 'ACES_Proxy_10_to_ACES.csp')) + os.path.join( + LUTS_DIRECTORY, "Three_Dimensional_Table_With_Shaper.csp" + ) + ) + LUT_6_r.sequence[0] = LUT_6_r.sequence[0].as_LUT( + LUT3x1D, force_conversion=True + ) write_LUT_Cinespace( - LUT_6_r.as_LUT(LUT1D, force_conversion=True), - os.path.join(self._temporary_directory, - 'ACES_Proxy_10_to_ACES.csp')) + LUT_6_r, + os.path.join( + self._temporary_directory, + "Three_Dimensional_Table_With_Shaper.csp", + ), + ) + LUT_6_r = read_LUT_Cinespace( + os.path.join( + LUTS_DIRECTORY, "Three_Dimensional_Table_With_Shaper.csp" + ) + ) LUT_6_t = read_LUT_Cinespace( - os.path.join(self._temporary_directory, - 'ACES_Proxy_10_to_ACES.csp')) + os.path.join( + self._temporary_directory, + "Three_Dimensional_Table_With_Shaper.csp", + ) + ) self.assertEqual(LUT_6_r, LUT_6_t) + LUT_7_r = read_LUT_Cinespace( + os.path.join(LUTS_DIRECTORY, "ACES_Proxy_10_to_ACES.csp") + ) + write_LUT_Cinespace( + LUT_7_r.as_LUT(LUT1D, force_conversion=True), + os.path.join( + self._temporary_directory, "ACES_Proxy_10_to_ACES.csp" + ), + ) + LUT_7_t = read_LUT_Cinespace( + os.path.join( + self._temporary_directory, "ACES_Proxy_10_to_ACES.csp" + ) + ) + self.assertEqual(LUT_7_r, LUT_7_t) + def test_raise_exception_write_LUT_Cinespace(self): """ - Tests :func:`colour.io.luts.cinespace_csp.write_LUT_Cinespace` + Test :func:`colour.io.luts.cinespace_csp.write_LUT_Cinespace` definition raised exception. """ - self.assertRaises(ValueError, write_LUT_Cinespace, object(), '') + self.assertRaises(ValueError, write_LUT_Cinespace, object(), "") -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/io/luts/tests/test_common.py b/colour/io/luts/tests/test_common.py index bf4036bc5f..01018e6c1d 100644 --- a/colour/io/luts/tests/test_common.py +++ b/colour/io/luts/tests/test_common.py @@ -1,40 +1,37 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.io.luts.common` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.io.luts.common` module.""" import unittest from colour.io.luts.common import path_to_title -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestPathToTitle'] +__all__ = [ + "TestPathToTitle", +] class TestPathToTitle(unittest.TestCase): """ - Defines :func:`colour.io.luts.common.path_to_title` definition unit tests + Define :func:`colour.io.luts.common.path_to_title` definition unit tests methods. """ def test_path_to_title(self): - """ - Tests :func:`colour.io.luts.common.path_to_title` definition. - """ + """Test :func:`colour.io.luts.common.path_to_title` definition.""" self.assertEqual( path_to_title( - 'colour/io/luts/tests/resources/cinespace/RGB_1_0.5_0.25.csp'), - 'RGB 1 0 5 0 25') + "colour/io/luts/tests/resources/cinespace/RGB_1_0.5_0.25.csp" + ), + "RGB 1 0 5 0 25", + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/io/luts/tests/test_iridas_cube.py b/colour/io/luts/tests/test_iridas_cube.py index 88ff16899d..84202fae14 100644 --- a/colour/io/luts/tests/test_iridas_cube.py +++ b/colour/io/luts/tests/test_iridas_cube.py @@ -1,9 +1,6 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.io.luts.iridas_cube` module. -""" +"""Defines the unit tests for the :mod:`colour.io.luts.iridas_cube` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import os @@ -11,165 +8,197 @@ import shutil import tempfile -from colour.io import (LUT1D, LUTSequence, read_LUT_IridasCube, - write_LUT_IridasCube) +from colour.io import ( + LUT1D, + LUTSequence, + read_LUT_IridasCube, + write_LUT_IridasCube, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['LUTS_DIRECTORY', 'TestReadLUTIridasCube', 'TestWriteLUTIridasCube'] +__all__ = [ + "LUTS_DIRECTORY", + "TestReadLUTIridasCube", + "TestWriteLUTIridasCube", +] -LUTS_DIRECTORY = os.path.join( - os.path.dirname(__file__), 'resources', 'iridas_cube') +LUTS_DIRECTORY: str = os.path.join( + os.path.dirname(__file__), "resources", "iridas_cube" +) class TestReadLUTIridasCube(unittest.TestCase): """ - Defines :func:`colour.io.luts.iridas_cube.read_LUT_IridasCube` definition + Define :func:`colour.io.luts.iridas_cube.read_LUT_IridasCube` definition unit tests methods. """ def test_read_LUT_IridasCube(self): """ - Tests :func:`colour.io.luts.iridas_cube.read_LUT_IridasCube` + Test :func:`colour.io.luts.iridas_cube.read_LUT_IridasCube` definition. """ LUT_1 = read_LUT_IridasCube( - os.path.join(LUTS_DIRECTORY, 'ACES_Proxy_10_to_ACES.cube')) + os.path.join(LUTS_DIRECTORY, "ACES_Proxy_10_to_ACES.cube") + ) np.testing.assert_almost_equal( LUT_1.table, - np.array([ - [4.88300000e-04, 4.88300000e-04, 4.88300000e-04], - [7.71400000e-04, 7.71400000e-04, 7.71400000e-04], - [1.21900000e-03, 1.21900000e-03, 1.21900000e-03], - [1.92600000e-03, 1.92600000e-03, 1.92600000e-03], - [3.04400000e-03, 3.04400000e-03, 3.04400000e-03], - [4.80900000e-03, 4.80900000e-03, 4.80900000e-03], - [7.59900000e-03, 7.59900000e-03, 7.59900000e-03], - [1.20100000e-02, 1.20100000e-02, 1.20100000e-02], - [1.89700000e-02, 1.89700000e-02, 1.89700000e-02], - [2.99800000e-02, 2.99800000e-02, 2.99800000e-02], - [4.73700000e-02, 4.73700000e-02, 4.73700000e-02], - [7.48400000e-02, 7.48400000e-02, 7.48400000e-02], - [1.18300000e-01, 1.18300000e-01, 1.18300000e-01], - [1.86900000e-01, 1.86900000e-01, 1.86900000e-01], - [2.95200000e-01, 2.95200000e-01, 2.95200000e-01], - [4.66500000e-01, 4.66500000e-01, 4.66500000e-01], - [7.37100000e-01, 7.37100000e-01, 7.37100000e-01], - [1.16500000e+00, 1.16500000e+00, 1.16500000e+00], - [1.84000000e+00, 1.84000000e+00, 1.84000000e+00], - [2.90800000e+00, 2.90800000e+00, 2.90800000e+00], - [4.59500000e+00, 4.59500000e+00, 4.59500000e+00], - [7.26000000e+00, 7.26000000e+00, 7.26000000e+00], - [1.14700000e+01, 1.14700000e+01, 1.14700000e+01], - [1.81300000e+01, 1.81300000e+01, 1.81300000e+01], - [2.86400000e+01, 2.86400000e+01, 2.86400000e+01], - [4.52500000e+01, 4.52500000e+01, 4.52500000e+01], - [7.15100000e+01, 7.15100000e+01, 7.15100000e+01], - [1.13000000e+02, 1.13000000e+02, 1.13000000e+02], - [1.78500000e+02, 1.78500000e+02, 1.78500000e+02], - [2.82100000e+02, 2.82100000e+02, 2.82100000e+02], - [4.45700000e+02, 4.45700000e+02, 4.45700000e+02], - [7.04300000e+02, 7.04300000e+02, 7.04300000e+02], - ])) - self.assertEqual(LUT_1.name, 'ACES Proxy 10 to ACES') + np.array( + [ + [4.88300000e-04, 4.88300000e-04, 4.88300000e-04], + [7.71400000e-04, 7.71400000e-04, 7.71400000e-04], + [1.21900000e-03, 1.21900000e-03, 1.21900000e-03], + [1.92600000e-03, 1.92600000e-03, 1.92600000e-03], + [3.04400000e-03, 3.04400000e-03, 3.04400000e-03], + [4.80900000e-03, 4.80900000e-03, 4.80900000e-03], + [7.59900000e-03, 7.59900000e-03, 7.59900000e-03], + [1.20100000e-02, 1.20100000e-02, 1.20100000e-02], + [1.89700000e-02, 1.89700000e-02, 1.89700000e-02], + [2.99800000e-02, 2.99800000e-02, 2.99800000e-02], + [4.73700000e-02, 4.73700000e-02, 4.73700000e-02], + [7.48400000e-02, 7.48400000e-02, 7.48400000e-02], + [1.18300000e-01, 1.18300000e-01, 1.18300000e-01], + [1.86900000e-01, 1.86900000e-01, 1.86900000e-01], + [2.95200000e-01, 2.95200000e-01, 2.95200000e-01], + [4.66500000e-01, 4.66500000e-01, 4.66500000e-01], + [7.37100000e-01, 7.37100000e-01, 7.37100000e-01], + [1.16500000e00, 1.16500000e00, 1.16500000e00], + [1.84000000e00, 1.84000000e00, 1.84000000e00], + [2.90800000e00, 2.90800000e00, 2.90800000e00], + [4.59500000e00, 4.59500000e00, 4.59500000e00], + [7.26000000e00, 7.26000000e00, 7.26000000e00], + [1.14700000e01, 1.14700000e01, 1.14700000e01], + [1.81300000e01, 1.81300000e01, 1.81300000e01], + [2.86400000e01, 2.86400000e01, 2.86400000e01], + [4.52500000e01, 4.52500000e01, 4.52500000e01], + [7.15100000e01, 7.15100000e01, 7.15100000e01], + [1.13000000e02, 1.13000000e02, 1.13000000e02], + [1.78500000e02, 1.78500000e02, 1.78500000e02], + [2.82100000e02, 2.82100000e02, 2.82100000e02], + [4.45700000e02, 4.45700000e02, 4.45700000e02], + [7.04300000e02, 7.04300000e02, 7.04300000e02], + ] + ), + ) + self.assertEqual(LUT_1.name, "ACES Proxy 10 to ACES") self.assertEqual(LUT_1.dimensions, 2) - np.testing.assert_array_equal(LUT_1.domain, - np.array([[0, 0, 0], [1, 1, 1]])) + np.testing.assert_array_equal( + LUT_1.domain, np.array([[0, 0, 0], [1, 1, 1]]) + ) self.assertEqual(LUT_1.size, 32) self.assertListEqual(LUT_1.comments, []) - LUT_2 = read_LUT_IridasCube(os.path.join(LUTS_DIRECTORY, 'Demo.cube')) - self.assertListEqual(LUT_2.comments, ['Comments can go anywhere']) - np.testing.assert_array_equal(LUT_2.domain, - np.array([[0, 0, 0], [1, 2, 3]])) + LUT_2 = read_LUT_IridasCube(os.path.join(LUTS_DIRECTORY, "Demo.cube")) + self.assertListEqual(LUT_2.comments, ["Comments can go anywhere"]) + np.testing.assert_array_equal( + LUT_2.domain, np.array([[0, 0, 0], [1, 2, 3]]) + ) LUT_3 = read_LUT_IridasCube( - os.path.join(LUTS_DIRECTORY, 'Three_Dimensional_Table.cube')) + os.path.join(LUTS_DIRECTORY, "Three_Dimensional_Table.cube") + ) self.assertEqual(LUT_3.dimensions, 3) self.assertEqual(LUT_3.size, 2) class TestWriteLUTIridasCube(unittest.TestCase): """ - Defines :func:`colour.io.luts.iridas_cube.write_LUT_IridasCube` definition + Define :func:`colour.io.luts.iridas_cube.write_LUT_IridasCube` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._temporary_directory = tempfile.mkdtemp() def tearDown(self): - """ - After tests actions. - """ + """After tests actions.""" shutil.rmtree(self._temporary_directory) def test_write_LUT_IridasCube(self): """ - Tests :func:`colour.io.luts.iridas_cube.write_LUT_IridasCube` + Test :func:`colour.io.luts.iridas_cube.write_LUT_IridasCube` definition. """ LUT_1_r = read_LUT_IridasCube( - os.path.join(LUTS_DIRECTORY, 'ACES_Proxy_10_to_ACES.cube')) + os.path.join(LUTS_DIRECTORY, "ACES_Proxy_10_to_ACES.cube") + ) write_LUT_IridasCube( LUT_1_r, - os.path.join(self._temporary_directory, - 'ACES_Proxy_10_to_ACES.cube')) + os.path.join( + self._temporary_directory, "ACES_Proxy_10_to_ACES.cube" + ), + ) LUT_1_t = read_LUT_IridasCube( - os.path.join(self._temporary_directory, - 'ACES_Proxy_10_to_ACES.cube')) + os.path.join( + self._temporary_directory, "ACES_Proxy_10_to_ACES.cube" + ) + ) self.assertEqual(LUT_1_r, LUT_1_t) write_LUT_IridasCube( LUTSequence(LUT_1_r), - os.path.join(self._temporary_directory, - 'ACES_Proxy_10_to_ACES.cube')) + os.path.join( + self._temporary_directory, "ACES_Proxy_10_to_ACES.cube" + ), + ) self.assertEqual(LUT_1_r, LUT_1_t) LUT_2_r = read_LUT_IridasCube( - os.path.join(LUTS_DIRECTORY, 'Demo.cube')) + os.path.join(LUTS_DIRECTORY, "Demo.cube") + ) write_LUT_IridasCube( - LUT_2_r, os.path.join(self._temporary_directory, 'Demo.cube')) + LUT_2_r, os.path.join(self._temporary_directory, "Demo.cube") + ) LUT_2_t = read_LUT_IridasCube( - os.path.join(self._temporary_directory, 'Demo.cube')) + os.path.join(self._temporary_directory, "Demo.cube") + ) self.assertEqual(LUT_2_r, LUT_2_t) self.assertListEqual(LUT_2_r.comments, LUT_2_t.comments) LUT_3_r = read_LUT_IridasCube( - os.path.join(LUTS_DIRECTORY, 'Three_Dimensional_Table.cube')) + os.path.join(LUTS_DIRECTORY, "Three_Dimensional_Table.cube") + ) write_LUT_IridasCube( LUT_3_r, - os.path.join(self._temporary_directory, - 'Three_Dimensional_Table.cube')) + os.path.join( + self._temporary_directory, "Three_Dimensional_Table.cube" + ), + ) LUT_3_t = read_LUT_IridasCube( - os.path.join(self._temporary_directory, - 'Three_Dimensional_Table.cube')) + os.path.join( + self._temporary_directory, "Three_Dimensional_Table.cube" + ) + ) self.assertEqual(LUT_3_r, LUT_3_t) LUT_4_r = read_LUT_IridasCube( - os.path.join(LUTS_DIRECTORY, 'ACES_Proxy_10_to_ACES.cube')) + os.path.join(LUTS_DIRECTORY, "ACES_Proxy_10_to_ACES.cube") + ) write_LUT_IridasCube( LUT_4_r.as_LUT(LUT1D, force_conversion=True), - os.path.join(self._temporary_directory, - 'ACES_Proxy_10_to_ACES.cube')) + os.path.join( + self._temporary_directory, "ACES_Proxy_10_to_ACES.cube" + ), + ) LUT_4_t = read_LUT_IridasCube( - os.path.join(self._temporary_directory, - 'ACES_Proxy_10_to_ACES.cube')) + os.path.join( + self._temporary_directory, "ACES_Proxy_10_to_ACES.cube" + ) + ) self.assertEqual(LUT_4_r, LUT_4_t) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/io/luts/tests/test_lut.py b/colour/io/luts/tests/test_lut.py index 4430ce40b9..1a033df4f5 100644 --- a/colour/io/luts/tests/test_lut.py +++ b/colour/io/luts/tests/test_lut.py @@ -1,118 +1,172 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.io.luts.lut` module. -""" +"""Defines the unit tests for the :mod:`colour.io.luts.lut` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import os import textwrap import unittest -from colour.algebra import random_triplet_generator, spow +from colour.algebra import ( + CubicSplineInterpolator, + LinearInterpolator, + random_triplet_generator, + spow, + table_interpolation_trilinear, + table_interpolation_tetrahedral, +) from colour.io.luts.lut import AbstractLUT -from colour.io.luts import (AbstractLUTSequenceOperator, LUT1D, LUT3x1D, LUT3D, - LUTSequence, LUT_to_LUT) -from colour.models import gamma_function -from colour.utilities import tsplit, tstack - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.io.luts import LUT1D, LUT3x1D, LUT3D, LUT_to_LUT +from colour.hints import ( + Any, + Callable, + Dict, + Integer, + NDArray, + Optional, + Type, + TypeInterpolator, + Union, +) +from colour.utilities import as_float_array, tsplit, tstack + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'RESOURCES_DIRECTORY', 'TestAbstractLUT', 'TestLUT', 'TestLUT1D', - 'TestLUT3x1D', 'TestLUT3D', 'TestAbstractLUTSequenceOperator', - 'TestLUTSequence' + "RESOURCES_DIRECTORY", + "RANDOM_TRIPLETS", + "TestAbstractLUT", + "AbstractLUTTest", + "TestLUT1D", + "TestLUT3x1D", + "TestLUT3D", ] -RESOURCES_DIRECTORY = os.path.join(os.path.dirname(__file__), 'resources') +RESOURCES_DIRECTORY: str = os.path.join(os.path.dirname(__file__), "resources") -RANDOM_TRIPLETS = np.reshape( +RANDOM_TRIPLETS: NDArray = np.reshape( random_triplet_generator(8, random_state=np.random.RandomState(4)), - (4, 2, 3)) + (4, 2, 3), +) class TestAbstractLUT(unittest.TestCase): - """ - Defines :class:`colour.io.luts.lut.AbstractLUT` class unit tests methods. - """ + """Define :class:`colour.io.luts.lut.AbstractLUT` class unit tests methods.""" def test_required_attributes(self): - """ - Tests presence of required attributes. - """ + """Test the presence of required attributes.""" - required_attributes = ('table', 'name', 'dimensions', 'domain', 'size', - 'comments') + required_attributes = ( + "table", + "name", + "dimensions", + "domain", + "size", + "comments", + ) for attribute in required_attributes: self.assertIn(attribute, dir(AbstractLUT)) def test_required_methods(self): - """ - Tests presence of required methods. - """ - - required_methods = ('__init__', '__str__', '__repr__', '__eq__', - '__ne__', '__add__', '__iadd__', '__sub__', - '__isub__', '__mul__', '__imul__', '__div__', - '__idiv__', '__pow__', '__ipow__', - 'arithmetical_operation', 'is_domain_explicit', - 'linear_table', 'apply', 'copy', 'as_LUT') + """Test the presence of required methods.""" + + required_methods = ( + "__init__", + "__str__", + "__repr__", + "__eq__", + "__ne__", + "__add__", + "__iadd__", + "__sub__", + "__isub__", + "__mul__", + "__imul__", + "__div__", + "__idiv__", + "__pow__", + "__ipow__", + "arithmetical_operation", + "is_domain_explicit", + "linear_table", + "copy", + "invert", + "apply", + "convert", + ) for method in required_methods: self.assertIn(method, dir(AbstractLUT)) -class TestLUT(unittest.TestCase): +class AbstractLUTTest(unittest.TestCase): """ - Defines :class:`colour.io.luts.lut.LUT1D`, + Define :class:`colour.io.luts.lut.LUT1D`, :class:`colour.io.luts.lut.LUT3x1D` and :class:`colour.io.luts.lut.LUT3D` classes common unit tests methods. """ - def __init__(self, *args): + def __init__(self, *args: Any): """ Create an instance of the class. Other Parameters ---------------- - \\*args : list, optional + args Arguments. """ - super(TestLUT, self).__init__(*args) - - self._LUT_factory = None - - self._domain_1 = None - self._domain_2 = None - self._domain_3 = None - self._table_1 = None - self._table_2 = None - self._table_3 = None - self._table_1_kwargs = None - self._table_2_kwargs = None - self._table_3_kwargs = None - self._dimensions = None - self._str = None - self._repr = None - self._applied_1 = None - self._applied_2 = None - self._applied_3 = None + super().__init__(*args) + + self._LUT_factory: Any = None + + self._size: Optional[Integer] = None + self._dimensions: Optional[Integer] = None + self._domain_1: Optional[NDArray] = None + self._domain_2: Optional[NDArray] = None + self._domain_3: Optional[NDArray] = None + self._table_1: Optional[NDArray] = None + self._table_2: Optional[NDArray] = None + self._table_3: Optional[NDArray] = None + self._table_1_kwargs: Optional[Dict] = None + self._table_2_kwargs: Optional[Dict] = None + self._table_3_kwargs: Optional[Dict] = None + self._interpolator_1: Optional[ + Union[Callable, Type[TypeInterpolator]] + ] = None + self._interpolator_kwargs_1: Dict = {} + self._interpolator_2: Optional[ + Union[Callable, Type[TypeInterpolator]] + ] = None + self._interpolator_kwargs_2: Dict = {} + self._invert_kwargs_1: Dict = {} + self._invert_kwargs_2: Dict = {} + self._str: Optional[str] = None + self._repr: Optional[str] = None + self._inverted_apply_1: Optional[NDArray] = None + self._inverted_apply_2: Optional[NDArray] = None + self._applied_1: Optional[NDArray] = None + self._applied_2: Optional[NDArray] = None + self._applied_3: Optional[NDArray] = None + self._applied_4: Optional[NDArray] = None def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__init__', 'is_domain_explicit', 'linear_table', - 'apply', 'as_LUT') + required_methods = ( + "__init__", + "is_domain_explicit", + "linear_table", + "invert", + "apply", + "convert", + ) for class_ in (LUT1D, LUT3x1D, LUT3D): for method in required_methods: @@ -120,7 +174,7 @@ def test_required_methods(self): def test__init__(self): """ - Tests :class:`colour.io.luts.lut.LUT1D.__init__`, + Test :class:`colour.io.luts.lut.LUT1D.__init__`, :class:`colour.io.luts.lut.LUT3x1D.__init__` and :class:`colour.io.luts.lut.LUT3D.__init__` methods. """ @@ -141,11 +195,12 @@ def test__init__(self): self.assertIsInstance( self._LUT_factory(self._table_3, domain=self._domain_3), - self._LUT_factory) + self._LUT_factory, + ) def test_table(self): """ - Tests :class:`colour.io.luts.lut.LUT1D.table`, + Test :class:`colour.io.luts.lut.LUT1D.table`, :class:`colour.io.luts.lut.LUT3x1D.table` and :class:`colour.io.luts.lut.LUT3D.table` properties. """ @@ -156,7 +211,7 @@ def test_table(self): # pylint: disable=E1102 LUT = self._LUT_factory() - np.testing.assert_array_equal(LUT.table, LUT.linear_table()) + np.testing.assert_array_equal(LUT.table, LUT.linear_table(self._size)) table_1 = self._table_1 * 0.8 + 0.1 LUT.table = table_1 @@ -164,7 +219,7 @@ def test_table(self): def test_name(self): """ - Tests :class:`colour.io.luts.lut.LUT1D.name`, + Test :class:`colour.io.luts.lut.LUT1D.name`, :class:`colour.io.luts.lut.LUT3x1D.name` and :class:`colour.io.luts.lut.LUT3D.name` properties. """ @@ -180,11 +235,11 @@ def test_name(self): # pylint: disable=E1102 LUT = self._LUT_factory() - self.assertEqual(LUT.name, 'Unity {0}'.format(self._table_1.shape[0])) + self.assertEqual(LUT.name, f"Unity {self._table_1.shape[0]}") def test_domain(self): """ - Tests :class:`colour.io.luts.lut.LUT1D.domain`, + Test :class:`colour.io.luts.lut.LUT1D.domain`, :class:`colour.io.luts.lut.LUT3x1D.domain` and :class:`colour.io.luts.lut.LUT3D.domain` properties. """ @@ -203,7 +258,7 @@ def test_domain(self): def test_size(self): """ - Tests :class:`colour.io.luts.lut.LUT1D.size`, + Test :class:`colour.io.luts.lut.LUT1D.size`, :class:`colour.io.luts.lut.LUT3x1D.size` and :class:`colour.io.luts.lut.LUT3D.size` properties. """ @@ -218,7 +273,7 @@ def test_size(self): def test_dimensions(self): """ - Tests :class:`colour.io.luts.lut.LUT1D.dimensions`, + Test :class:`colour.io.luts.lut.LUT1D.dimensions`, :class:`colour.io.luts.lut.LUT3x1D.dimensions` and :class:`colour.io.luts.lut.LUT3D.dimensions` properties. """ @@ -233,7 +288,7 @@ def test_dimensions(self): def test_comments(self): """ - Tests :class:`colour.io.luts.lut.LUT1D.comments`, + Test :class:`colour.io.luts.lut.LUT1D.comments`, :class:`colour.io.luts.lut.LUT3x1D.comments` and :class:`colour.io.luts.lut.LUT3D.comments` properties. """ @@ -245,7 +300,7 @@ def test_comments(self): LUT = self._LUT_factory() self.assertListEqual(LUT.comments, []) - comments = ['A first comment.', 'A second comment.'] + comments = ["A first comment.", "A second comment."] # pylint: disable=E1102 LUT = self._LUT_factory(comments=comments) @@ -253,7 +308,7 @@ def test_comments(self): def test__str__(self): """ - Tests :class:`colour.io.luts.lut.LUT1D.__str__`, + Test :class:`colour.io.luts.lut.LUT1D.__str__`, :class:`colour.io.luts.lut.LUT3x1D.__str__` and :class:`colour.io.luts.lut.LUT3D.__str__` methods. """ @@ -262,13 +317,13 @@ def test__str__(self): return # pylint: disable=E1102 - LUT = self._LUT_factory(name='Nemo') + LUT = self._LUT_factory(name="Nemo") self.assertEqual(str(LUT), self._str) def test__repr__(self): """ - Tests :class:`colour.io.luts.lut.LUT1D.__repr__`, + Test :class:`colour.io.luts.lut.LUT1D.__repr__`, :class:`colour.io.luts.lut.LUT3x1D.__repr__` and :class:`colour.io.luts.lut.LUT3D.__repr__` methods. """ @@ -277,7 +332,7 @@ def test__repr__(self): return # pylint: disable=E1102 - LUT = self._LUT_factory(name='Nemo') + LUT = self._LUT_factory(name="Nemo") # The default LUT representation is too large to be embedded, given # that :class:`colour.io.luts.lut.LUT3D.__str__` method is defined by @@ -290,7 +345,7 @@ def test__repr__(self): def test__eq__(self): """ - Tests :class:`colour.io.luts.lut.LUT1D.__eq__`, + Test :class:`colour.io.luts.lut.LUT1D.__eq__`, :class:`colour.io.luts.lut.LUT3x1D.__eq__` and :class:`colour.io.luts.lut.LUT3D.__eq__` methods. """ @@ -307,7 +362,7 @@ def test__eq__(self): def test__ne__(self): """ - Tests :class:`colour.io.luts.lut.LUT1D.__ne__`, + Test :class:`colour.io.luts.lut.LUT1D.__ne__`, :class:`colour.io.luts.lut.LUT3x1D.__ne__` and :class:`colour.io.luts.lut.LUT3D.__ne__` methods. """ @@ -330,7 +385,7 @@ def test__ne__(self): def test_is_domain_explicit(self): """ - Tests :class:`colour.io.luts.lut.LUT1D.is_domain_explicit`, + Test :class:`colour.io.luts.lut.LUT1D.is_domain_explicit`, :class:`colour.io.luts.lut.LUT3x1D.is_domain_explicit` and :class:`colour.io.luts.lut.LUT3D.is_domain_explicit` methods. """ @@ -343,12 +398,14 @@ def test_is_domain_explicit(self): # pylint: disable=E1102 self.assertTrue( - self._LUT_factory(self._table_3, - domain=self._domain_3).is_domain_explicit()) + self._LUT_factory( + self._table_3, domain=self._domain_3 + ).is_domain_explicit() + ) def test_arithmetical_operation(self): """ - Tests :class:`colour.io.luts.lut.LUT1D.arithmetical_operation`, + Test :class:`colour.io.luts.lut.LUT1D.arithmetical_operation`, :class:`colour.io.luts.lut.LUT3x1D.arithmetical_operation` and :class:`colour.io.luts.lut.LUT3D.arithmetical_operation` methods. """ @@ -362,86 +419,103 @@ def test_arithmetical_operation(self): LUT_2 = self._LUT_factory() np.testing.assert_almost_equal( - LUT_1.arithmetical_operation(10, '+', False).table, + LUT_1.arithmetical_operation(10, "+", False).table, self._table_1 + 10, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - LUT_1.arithmetical_operation(10, '-', False).table, + LUT_1.arithmetical_operation(10, "-", False).table, self._table_1 - 10, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - LUT_1.arithmetical_operation(10, '*', False).table, + LUT_1.arithmetical_operation(10, "*", False).table, self._table_1 * 10, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - LUT_1.arithmetical_operation(10, '/', False).table, + LUT_1.arithmetical_operation(10, "/", False).table, self._table_1 / 10, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - LUT_1.arithmetical_operation(10, '**', False).table, - self._table_1 ** 10, - decimal=7) + LUT_1.arithmetical_operation(10, "**", False).table, + self._table_1**10, + decimal=7, + ) np.testing.assert_almost_equal( - (LUT_1 + 10).table, self._table_1 + 10, decimal=7) + (LUT_1 + 10).table, self._table_1 + 10, decimal=7 + ) np.testing.assert_almost_equal( - (LUT_1 - 10).table, self._table_1 - 10, decimal=7) + (LUT_1 - 10).table, self._table_1 - 10, decimal=7 + ) np.testing.assert_almost_equal( - (LUT_1 * 10).table, self._table_1 * 10, decimal=7) + (LUT_1 * 10).table, self._table_1 * 10, decimal=7 + ) np.testing.assert_almost_equal( - (LUT_1 / 10).table, self._table_1 / 10, decimal=7) + (LUT_1 / 10).table, self._table_1 / 10, decimal=7 + ) np.testing.assert_almost_equal( - (LUT_1 ** 10).table, self._table_1 ** 10, decimal=7) + (LUT_1**10).table, self._table_1**10, decimal=7 + ) np.testing.assert_almost_equal( - LUT_2.arithmetical_operation(10, '+', True).table, + LUT_2.arithmetical_operation(10, "+", True).table, self._table_1 + 10, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - LUT_2.arithmetical_operation(10, '-', True).table, + LUT_2.arithmetical_operation(10, "-", True).table, self._table_1, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - LUT_2.arithmetical_operation(10, '*', True).table, + LUT_2.arithmetical_operation(10, "*", True).table, self._table_1 * 10, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - LUT_2.arithmetical_operation(10, '/', True).table, + LUT_2.arithmetical_operation(10, "/", True).table, self._table_1, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - LUT_2.arithmetical_operation(10, '**', True).table, - self._table_1 ** 10, - decimal=7) + LUT_2.arithmetical_operation(10, "**", True).table, + self._table_1**10, + decimal=7, + ) # pylint: disable=E1102 LUT_2 = self._LUT_factory() np.testing.assert_almost_equal( - LUT_2.arithmetical_operation(self._table_1, '+', False).table, + LUT_2.arithmetical_operation(self._table_1, "+", False).table, LUT_2.table + self._table_1, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - LUT_2.arithmetical_operation(LUT_2, '+', False).table, + LUT_2.arithmetical_operation(LUT_2, "+", False).table, LUT_2.table + LUT_2.table, - decimal=7) + decimal=7, + ) def test_linear_table(self): """ - Tests :class:`colour.io.luts.lut.LUT1D.linear_table`, + Test :class:`colour.io.luts.lut.LUT1D.linear_table`, :class:`colour.io.luts.lut.LUT3x1D.linear_table` and :class:`colour.io.luts.lut.LUT3D.linear_table` methods. """ @@ -453,18 +527,64 @@ def test_linear_table(self): LUT_1 = self._LUT_factory() np.testing.assert_almost_equal( - LUT_1.linear_table(), self._table_1, decimal=7) + LUT_1.linear_table(self._size), self._table_1, decimal=7 + ) np.testing.assert_almost_equal( spow( - self._LUT_factory.linear_table(**self._table_3_kwargs), - 1 / 2.6), + self._LUT_factory.linear_table(**self._table_3_kwargs), 1 / 2.6 + ), self._table_3, - decimal=7) + decimal=7, + ) + + def test_copy(self): + """ + Test :class:`colour.io.luts.lut.LUT1D.copy`, + :class:`colour.io.luts.lut.LUT3x1D.copy` and + :class:`colour.io.luts.lut.LUT3D.copy` methods. + """ + + if self._LUT_factory is None: + return + + # pylint: disable=E1102 + LUT_1 = self._LUT_factory() + + self.assertIsNot(LUT_1, LUT_1.copy()) + self.assertEqual(LUT_1, LUT_1.copy()) + + def test_invert(self): + """ + Test :class:`colour.io.luts.lut.LUT1D.invert`, + :class:`colour.io.luts.lut.LUT3x1D.invert` and + :class:`colour.io.luts.lut.LUT3D.invert` methods. + """ + + if self._LUT_factory is None: + return + + # pylint: disable=E1102 + LUT_i = self._LUT_factory(self._table_2).invert( + interpolator=self._interpolator_1, **self._invert_kwargs_1 + ) + + np.testing.assert_almost_equal( + LUT_i.apply(RANDOM_TRIPLETS), self._inverted_apply_1, decimal=7 + ) + + # pylint: disable=E1102 + LUT_i = self._LUT_factory(self._table_2).invert( + interpolator=self._interpolator_2, **self._invert_kwargs_2 + ) + + np.testing.assert_almost_equal( + LUT_i.apply(RANDOM_TRIPLETS), self._inverted_apply_2, decimal=7 + ) def test_apply(self): """ - Tests :class:`colour.io.luts.lut.LUT1D.apply`, + Test :class:`colour.io.luts.lut.LUT1D.apply`, :class:`colour.io.luts.lut.LUT3x1D.apply` and :class:`colour.io.luts.lut.LUT3D.apply` methods. """ @@ -476,165 +596,245 @@ def test_apply(self): LUT_1 = self._LUT_factory(self._table_2) np.testing.assert_almost_equal( - LUT_1.apply(RANDOM_TRIPLETS), self._applied_1, decimal=7) + LUT_1.apply(RANDOM_TRIPLETS), self._applied_1, decimal=7 + ) # pylint: disable=E1102 LUT_2 = self._LUT_factory(domain=self._domain_2) LUT_2.table = spow(LUT_2.table, 1 / 2.2) np.testing.assert_almost_equal( - LUT_2.apply(RANDOM_TRIPLETS), self._applied_2, decimal=7) + LUT_2.apply(RANDOM_TRIPLETS), self._applied_2, decimal=7 + ) # pylint: disable=E1102 LUT_3 = self._LUT_factory(self._table_3, domain=self._domain_3) np.testing.assert_almost_equal( - LUT_3.apply(RANDOM_TRIPLETS), self._applied_3, decimal=7) - - def test_copy(self): - """ - Tests :class:`colour.io.luts.lut.LUT1D.copy`, - :class:`colour.io.luts.lut.LUT3x1D.copy` and - :class:`colour.io.luts.lut.LUT3D.copy` methods. - """ - - if self._LUT_factory is None: - return + LUT_3.apply(RANDOM_TRIPLETS), self._applied_3, decimal=7 + ) # pylint: disable=E1102 - LUT_1 = self._LUT_factory() + LUT_4 = self._LUT_factory(self._table_2) - self.assertIsNot(LUT_1, LUT_1.copy()) - self.assertEqual(LUT_1, LUT_1.copy()) + np.testing.assert_almost_equal( + LUT_4.apply( + RANDOM_TRIPLETS, + direction="Inverse", + interpolator=self._interpolator_1, + interpolator_kwargs=self._interpolator_kwargs_1, + **self._invert_kwargs_1, + ), + self._applied_4, + decimal=7, + ) -class TestLUT1D(TestLUT): - """ - Defines :class:`colour.io.luts.lut.LUT1D` class unit tests methods. - """ +class TestLUT1D(AbstractLUTTest): + """Define :class:`colour.io.luts.lut.LUT1D` class unit tests methods.""" - def __init__(self, *args): + def __init__(self, *args: Any): """ Create an instance of the class. Other Parameters ---------------- - \\*args : list, optional + args Arguments. """ - super(TestLUT1D, self).__init__(*args) + super().__init__(*args) self._LUT_factory = LUT1D + self._size = 10 + self._dimensions = 1 self._domain_1 = np.array([0, 1]) self._domain_2 = np.array([-0.1, 1.5]) self._domain_3 = np.linspace(-0.1, 1.5, 10) self._table_1 = np.linspace(0, 1, 10) self._table_2 = self._table_1 ** (1 / 2.2) - self._table_3 = spow(np.linspace(-0.1, 1.5, 10), (1 / 2.6)) - self._table_1_kwargs = {'size': 10, 'domain': self._domain_1} - self._table_2_kwargs = {'size': 10, 'domain': self._domain_2} - self._table_3_kwargs = {'size': 10, 'domain': self._domain_3} - self._dimensions = 1 - self._str = textwrap.dedent(""" + self._table_3 = as_float_array( + spow(np.linspace(-0.1, 1.5, self._size), (1 / 2.6)) + ) + self._table_1_kwargs = {"size": self._size, "domain": self._domain_1} + self._table_2_kwargs = {"size": self._size, "domain": self._domain_2} + self._table_3_kwargs = {"size": self._size, "domain": self._domain_3} + self._interpolator_1 = LinearInterpolator + self._interpolator_kwargs_1 = {} + self._interpolator_2 = CubicSplineInterpolator + self._interpolator_kwargs_2 = {} + self._invert_kwargs_1 = {} + self._invert_kwargs_2 = {} + self._str = textwrap.dedent( + """ LUT1D - Nemo ------------ Dimensions : 1 Domain : [ 0. 1.] - Size : (10,)""")[1:] - self._repr = textwrap.dedent(""" + Size : (10,)""" + )[1:] + self._repr = textwrap.dedent( + """ LUT1D([ 0. , 0.11111111, 0.22222222, 0.33333333, 0.44444444, 0.55555556, 0.66666667, 0.77777778, 0.88888889, 1. ], name='Nemo', - domain=[ 0., 1.])""")[1:] - self._applied_1 = np.array([ - [[0.98453144, 0.53304051, 0.02978976], - [0.76000720, 0.68433298, 0.64753760]], - [[0.98718436, 0.89285575, 0.14639477], - [0.85784314, 0.47463489, 0.97966294]], - [[0.84855994, 0.93486051, 0.68536703], - [0.49723089, 0.99221212, 0.97606176]], - [[0.98886872, 0.43308440, 0.89633381], - [0.02065388, 0.79040970, 0.93651642]], - ]) - self._applied_2 = np.array([ - [[0.98486877, 0.53461565, 0.05614915], - [0.75787807, 0.68473291, 0.64540281]], - [[0.98736681, 0.89255862, 0.18759013], - [0.85682563, 0.46473837, 0.97981413]], - [[0.84736915, 0.93403795, 0.68561444], - [0.48799540, 0.99210103, 0.97606266]], - [[0.98895283, 0.42197234, 0.89639002], - [0.04585089, 0.79047033, 0.93564890]], - ]) - - self._applied_3 = np.array([ - [[0.98718085, 0.58856660, 0.06995805], - [0.79062078, 0.72580416, 0.68991332]], - [[0.98928698, 0.90826591, 0.22565356], - [0.87725399, 0.52099138, 0.98286533]], - [[0.86904691, 0.94376678, 0.72658532], - [0.54348223, 0.99327846, 0.97966110]], - [[0.99062417, 0.47963425, 0.91159110], - [0.05775947, 0.81950198, 0.94514273]], - ]) - - -class TestLUT3x1D(TestLUT): - """ - Defines :class:`colour.io.luts.lut.LUT3x1D` class unit tests methods. - """ - - def __init__(self, *args): + domain=[ 0., 1.])""" + )[1:] + self._inverted_apply_1 = np.array( + [ + [ + [0.92972640, 0.07631226, 0.00271066], + [0.26841861, 0.16523270, 0.12595735], + ], + [ + [0.94177862, 0.57881126, 0.01332090], + [0.47923027, 0.05963181, 0.90760882], + ], + [ + [0.45351816, 0.72429553, 0.16633644], + [0.06518351, 0.96461970, 0.89124869], + ], + [ + [0.94943065, 0.04942310, 0.59044056], + [0.00187936, 0.32291386, 0.73036245], + ], + ] + ) + self._inverted_apply_2 = self._inverted_apply_1 + self._applied_1 = np.array( + [ + [ + [0.98453144, 0.53304051, 0.02978976], + [0.76000720, 0.68433298, 0.64753760], + ], + [ + [0.98718436, 0.89285575, 0.14639477], + [0.85784314, 0.47463489, 0.97966294], + ], + [ + [0.84855994, 0.93486051, 0.68536703], + [0.49723089, 0.99221212, 0.97606176], + ], + [ + [0.98886872, 0.43308440, 0.89633381], + [0.02065388, 0.79040970, 0.93651642], + ], + ] + ) + self._applied_2 = np.array( + [ + [ + [0.98486877, 0.53461565, 0.05614915], + [0.75787807, 0.68473291, 0.64540281], + ], + [ + [0.98736681, 0.89255862, 0.18759013], + [0.85682563, 0.46473837, 0.97981413], + ], + [ + [0.84736915, 0.93403795, 0.68561444], + [0.48799540, 0.99210103, 0.97606266], + ], + [ + [0.98895283, 0.42197234, 0.89639002], + [0.04585089, 0.79047033, 0.93564890], + ], + ] + ) + self._applied_3 = np.array( + [ + [ + [0.98718085, 0.58856660, 0.06995805], + [0.79062078, 0.72580416, 0.68991332], + ], + [ + [0.98928698, 0.90826591, 0.22565356], + [0.87725399, 0.52099138, 0.98286533], + ], + [ + [0.86904691, 0.94376678, 0.72658532], + [0.54348223, 0.99327846, 0.97966110], + ], + [ + [0.99062417, 0.47963425, 0.91159110], + [0.05775947, 0.81950198, 0.94514273], + ], + ] + ) + self._applied_4 = self._inverted_apply_1 + + +class TestLUT3x1D(AbstractLUTTest): + """Define :class:`colour.io.luts.lut.LUT3x1D` class unit tests methods.""" + + def __init__(self, *args: Any): """ Create an instance of the class. Other Parameters ---------------- - \\*args : list, optional + args Arguments. """ - super(TestLUT3x1D, self).__init__(*args) + super().__init__(*args) self._LUT_factory = LUT3x1D + self._size = 10 + self._dimensions = 2 samples_1 = np.linspace(0, 1, 10) samples_2 = np.linspace(-0.1, 1.5, 15) samples_3 = np.linspace(-0.1, 3.0, 20) self._domain_1 = np.array([[0, 0, 0], [1, 1, 1]]) self._domain_2 = np.array([[0, -0.1, -0.2], [1, 1.5, 3.0]]) - self._domain_3 = tstack([ - np.hstack([samples_1, np.full(10, np.nan)]), - np.hstack([samples_2, np.full(5, np.nan)]), - samples_3, - ]) - self._table_1 = tstack([samples_1, samples_1, samples_1]) - self._table_2 = self._table_1 ** (1 / 2.2) - self._table_3 = spow( - tstack([ + self._domain_3 = tstack( + [ np.hstack([samples_1, np.full(10, np.nan)]), np.hstack([samples_2, np.full(5, np.nan)]), samples_3, - ]), 1 / 2.6) - self._table_1_kwargs = {'size': 10, 'domain': self._domain_1} - self._table_2_kwargs = {'size': 10, 'domain': self._domain_2} + ] + ) + self._table_1 = tstack([samples_1, samples_1, samples_1]) + self._table_2 = self._table_1 ** (1 / 2.2) + self._table_3 = as_float_array( + spow( + tstack( + [ + np.hstack([samples_1, np.full(10, np.nan)]), + np.hstack([samples_2, np.full(5, np.nan)]), + samples_3, + ] + ), + 1 / 2.6, + ) + ) + self._table_1_kwargs = {"size": self._size, "domain": self._domain_1} + self._table_2_kwargs = {"size": self._size, "domain": self._domain_2} self._table_3_kwargs = { - 'size': np.array([10, 15, 20]), - 'domain': self._domain_3 + "size": np.array([10, 15, 20]), + "domain": self._domain_3, } - self._dimensions = 2 - self._str = textwrap.dedent(""" + self._interpolator_1 = LinearInterpolator + self._interpolator_kwargs_1 = {} + self._interpolator_2 = CubicSplineInterpolator + self._interpolator_kwargs_2 = {} + self._invert_kwargs_1 = {} + self._invert_kwargs_2 = {} + self._str = textwrap.dedent( + """ LUT3x1D - Nemo -------------- Dimensions : 2 Domain : [[ 0. 0. 0.] [ 1. 1. 1.]] - Size : (10, 3)""")[1:] - self._repr = textwrap.dedent(""" + Size : (10, 3)""" + )[1:] + self._repr = textwrap.dedent( + """ LUT3x1D([[ 0. , 0. , 0. ], [ 0.11111111, 0.11111111, 0.11111111], [ 0.22222222, 0.22222222, 0.22222222], @@ -647,608 +847,305 @@ def __init__(self, *args): [ 1. , 1. , 1. ]], name='Nemo', domain=[[ 0., 0., 0.], - [ 1., 1., 1.]])""")[1:] - self._applied_1 = np.array([ - [[0.98453144, 0.53304051, 0.02978976], - [0.76000720, 0.68433298, 0.64753760]], - [[0.98718436, 0.89285575, 0.14639477], - [0.85784314, 0.47463489, 0.97966294]], - [[0.84855994, 0.93486051, 0.68536703], - [0.49723089, 0.99221212, 0.97606176]], - [[0.98886872, 0.43308440, 0.89633381], - [0.02065388, 0.79040970, 0.93651642]], - ]) - self._applied_2 = np.array([ - [[0.98453144, 0.53461565, 0.05393585], - [0.76000720, 0.68473291, 0.62923633]], - [[0.98718436, 0.89255862, 0.14399599], - [0.85784314, 0.46473837, 0.97713337]], - [[0.84855994, 0.93403795, 0.67216031], - [0.49723089, 0.99210103, 0.97371216]], - [[0.98886872, 0.42197234, 0.89183123], - [0.02065388, 0.79047033, 0.93681229]], - ]) - self._applied_3 = np.array([ - [[0.98685765, 0.58844468, 0.09393531], - [0.79274650, 0.72453018, 0.69347904]], - [[0.98911162, 0.90807837, 0.25736920], - [0.87825083, 0.53046097, 0.98225775]], - [[0.87021380, 0.94442819, 0.72448386], - [0.55350090, 0.99318691, 0.97922787]], - [[0.99054268, 0.49317779, 0.91055390], - [0.02408419, 0.81991814, 0.94597809]], - ]) - - -class TestLUT3D(TestLUT): - """ - Defines :class:`colour.io.luts.lut.LUT3D` class unit tests methods. - """ - - def __init__(self, *args): + [ 1., 1., 1.]])""" + )[1:] + self._inverted_apply_1 = np.array( + [ + [ + [0.92972640, 0.07631226, 0.00271066], + [0.26841861, 0.16523270, 0.12595735], + ], + [ + [0.94177862, 0.57881126, 0.01332090], + [0.47923027, 0.05963181, 0.90760882], + ], + [ + [0.45351816, 0.72429553, 0.16633644], + [0.06518351, 0.96461970, 0.89124869], + ], + [ + [0.94943065, 0.04942310, 0.59044056], + [0.00187936, 0.32291386, 0.73036245], + ], + ] + ) + self._inverted_apply_2 = self._inverted_apply_1 + self._applied_1 = np.array( + [ + [ + [0.98453144, 0.53304051, 0.02978976], + [0.76000720, 0.68433298, 0.64753760], + ], + [ + [0.98718436, 0.89285575, 0.14639477], + [0.85784314, 0.47463489, 0.97966294], + ], + [ + [0.84855994, 0.93486051, 0.68536703], + [0.49723089, 0.99221212, 0.97606176], + ], + [ + [0.98886872, 0.43308440, 0.89633381], + [0.02065388, 0.79040970, 0.93651642], + ], + ] + ) + self._applied_2 = np.array( + [ + [ + [0.98453144, 0.53461565, 0.05393585], + [0.76000720, 0.68473291, 0.62923633], + ], + [ + [0.98718436, 0.89255862, 0.14399599], + [0.85784314, 0.46473837, 0.97713337], + ], + [ + [0.84855994, 0.93403795, 0.67216031], + [0.49723089, 0.99210103, 0.97371216], + ], + [ + [0.98886872, 0.42197234, 0.89183123], + [0.02065388, 0.79047033, 0.93681229], + ], + ] + ) + self._applied_3 = np.array( + [ + [ + [0.98685765, 0.58844468, 0.09393531], + [0.79274650, 0.72453018, 0.69347904], + ], + [ + [0.98911162, 0.90807837, 0.25736920], + [0.87825083, 0.53046097, 0.98225775], + ], + [ + [0.87021380, 0.94442819, 0.72448386], + [0.55350090, 0.99318691, 0.97922787], + ], + [ + [0.99054268, 0.49317779, 0.91055390], + [0.02408419, 0.81991814, 0.94597809], + ], + ] + ) + self._applied_4 = self._inverted_apply_1 + + +class TestLUT3D(AbstractLUTTest): + """Define :class:`colour.io.luts.lut.LUT3D` class unit tests methods.""" + + def __init__(self, *args: Any): """ Create an instance of the class. Other Parameters ---------------- - \\*args : list, optional + args Arguments. """ - super(TestLUT3D, self).__init__(*args) + super().__init__(*args) self._LUT_factory = LUT3D + self._size = 33 + self._dimensions = 3 samples_1 = np.linspace(0, 1, 10) samples_2 = np.linspace(-0.1, 1.5, 15) samples_3 = np.linspace(-0.1, 3.0, 20) self._domain_1 = np.array([[0, 0, 0], [1, 1, 1]]) self._domain_2 = np.array([[0, -0.1, -0.2], [1, 1.5, 3.0]]) - self._domain_3 = tstack([ - np.hstack([samples_1, np.full(10, np.nan)]), - np.hstack([samples_2, np.full(5, np.nan)]), - samples_3, - ]) - self._table_1 = np.meshgrid( - *[ - np.linspace(axes[0], axes[1], 33) - for axes in reversed(tsplit(self._domain_1)) - ], - indexing='ij') - self._table_1 = np.flip( - np.transpose(self._table_1).reshape([33, 33, 33, 3]), -1) + self._domain_3 = tstack( + [ + np.hstack([samples_1, np.full(10, np.nan)]), + np.hstack([samples_2, np.full(5, np.nan)]), + samples_3, + ] + ) + self._table_1 = as_float_array( + np.flip( + np.transpose( + np.meshgrid( + *[ + np.linspace(axes[0], axes[1], 33) + for axes in reversed(tsplit(self._domain_1)) + ], + indexing="ij", + ) + ).reshape([33, 33, 33, 3]), + -1, + ) + ) self._table_2 = self._table_1 ** (1 / 2.2) - self._table_3 = np.meshgrid( - *[ - axes[:(~np.isnan(axes)).cumsum().argmax() + 1] - for axes in reversed(tsplit(self._domain_3)) - ], - indexing='ij') - self._table_3 = spow( - np.flip(np.transpose(self._table_3).reshape([10, 15, 20, 3]), -1), - 1 / 2.6) - self._table_1_kwargs = {'size': 33, 'domain': self._domain_1} - self._table_2_kwargs = {'size': 33, 'domain': self._domain_2} + self._table_3 = as_float_array( + spow( + np.flip( + np.transpose( + np.meshgrid( + *[ + axes[: (~np.isnan(axes)).cumsum().argmax() + 1] + for axes in reversed(tsplit(self._domain_3)) + ], + indexing="ij", + ) + ).reshape([10, 15, 20, 3]), + -1, + ), + 1 / 2.6, + ) + ) + self._table_1_kwargs = {"size": self._size, "domain": self._domain_1} + self._table_2_kwargs = {"size": self._size, "domain": self._domain_2} self._table_3_kwargs = { - 'size': np.array([10, 15, 20]), - 'domain': self._domain_3 + "size": np.array([10, 15, 20]), + "domain": self._domain_3, } - self._dimensions = 3 - self._str = textwrap.dedent(""" + self._interpolator_1 = table_interpolation_trilinear + self._interpolator_kwargs_1 = {} + self._interpolator_2 = table_interpolation_tetrahedral + self._interpolator_kwargs_2 = {} + self._invert_kwargs_1 = {"extrapolate": False, "query_size": 1} + self._invert_kwargs_2 = {"extrapolate": True, "query_size": 3} + self._str = textwrap.dedent( + """ LUT3D - Nemo ------------ Dimensions : 3 Domain : [[ 0. 0. 0.] [ 1. 1. 1.]] - Size : (33, 33, 33, 3)""")[1:] + Size : (33, 33, 33, 3)""" + )[1:] self._repr = None - self._applied_1 = np.array([ - [[0.98486974, 0.53531556, 0.05950617], - [0.76022687, 0.68479344, 0.64907649]], - [[0.98747624, 0.89287549, 0.23859990], - [0.85844632, 0.47829965, 0.98002765]], - [[0.84903362, 0.93518100, 0.68577990], - [0.49827272, 0.99238949, 0.97644600]], - [[0.98912224, 0.43911364, 0.89645863], - [0.04125691, 0.79116284, 0.93680839]], - ]) - - self._applied_2 = np.array([ - [[0.98486974, 0.53526504, 0.03155191], - [0.76022687, 0.68458573, 0.64850011]], - [[0.98747624, 0.89277461, 0.15505443], - [0.85844632, 0.47842591, 0.97972986]], - [[0.84903362, 0.93514331, 0.68479574], - [0.49827272, 0.99234923, 0.97614054]], - [[0.98912224, 0.43850620, 0.89625878], - [0.04125691, 0.79115345, 0.93648599]], - ]) - - self._applied_3 = np.array([ - [[0.98685765, 0.58844468, 0.09393531], - [0.79274650, 0.72453018, 0.69347904]], - [[0.98911162, 0.90807837, 0.25736920], - [0.87825083, 0.53046097, 0.98225775]], - [[0.87021380, 0.94442819, 0.72448386], - [0.55350090, 0.99318691, 0.97922787]], - [[0.99054268, 0.49317779, 0.91055390], - [0.02408419, 0.81991814, 0.94597809]], - ]) - - -class TestAbstractLUTSequenceOperator(unittest.TestCase): - """ - Defines :class:`colour.io.luts.lut.AbstractLUTSequenceOperator` class unit - tests methods. - """ - - def test_required_methods(self): - """ - Tests presence of required methods. - """ - - required_methods = ('apply', ) - - for method in required_methods: - self.assertIn(method, dir(AbstractLUTSequenceOperator)) - - -class TestLUTSequence(unittest.TestCase): - """ - Defines :class:`colour.io.luts.lut.LUTSequence` class unit tests methods. - """ - - def setUp(self): - """ - Initialises common tests attributes. - """ - - self._LUT_1 = LUT1D(LUT1D.linear_table(16) + 0.125, 'Nemo 1D') - self._LUT_2 = LUT3D(LUT3D.linear_table(16) ** (1 / 2.2), 'Nemo 3D') - self._LUT_3 = LUT3x1D(LUT3x1D.linear_table(16) * 0.750, 'Nemo 3x1D') - self._LUT_sequence = LUTSequence(self._LUT_1, self._LUT_2, self._LUT_3) - - samples = np.linspace(0, 1, 5) - - self._RGB = tstack([samples, samples, samples]) - - def test_required_attributes(self): - """ - Tests presence of required attributes. - """ - - required_attributes = ('sequence', ) - - for attribute in required_attributes: - self.assertIn(attribute, dir(LUTSequence)) - - def test_required_methods(self): - """ - Tests presence of required methods. - """ - - required_methods = ('__init__', '__getitem__', '__setitem__', - '__delitem__', '__len__', '__str__', '__repr__', - '__eq__', '__ne__', 'insert', 'apply', 'copy') - - for method in required_methods: - self.assertIn(method, dir(LUTSequence)) - - def test_sequence(self): - """ - Tests :class:`colour.io.luts.lut.LUTSequence.sequence` property. - """ - - sequence = [self._LUT_1, self._LUT_2, self._LUT_3] - LUT_sequence = LUTSequence() - LUT_sequence.sequence = sequence - self.assertListEqual(self._LUT_sequence.sequence, sequence) - - def test__init__(self): - """ - Tests :class:`colour.io.luts.lut.LUTSequence.__init__` method. - """ - - self.assertEqual( - LUTSequence(self._LUT_1, self._LUT_2, self._LUT_3), - self._LUT_sequence) - - def test__getitem__(self): - """ - Tests :class:`colour.io.luts.lut.LUTSequence.__getitem__` method. - """ - - self.assertEqual(self._LUT_sequence[0], self._LUT_1) - self.assertEqual(self._LUT_sequence[1], self._LUT_2) - self.assertEqual(self._LUT_sequence[2], self._LUT_3) - - def test__setitem__(self): - """ - Tests :class:`colour.io.luts.lut.LUTSequence.__setitem__` method. - """ - - LUT_sequence = self._LUT_sequence.copy() - LUT_sequence[0] = self._LUT_3 - LUT_sequence[1] = self._LUT_1 - LUT_sequence[2] = self._LUT_2 - - self.assertEqual(LUT_sequence[1], self._LUT_1) - self.assertEqual(LUT_sequence[2], self._LUT_2) - self.assertEqual(LUT_sequence[0], self._LUT_3) - - def test__delitem__(self): - """ - Tests :class:`colour.io.luts.lut.LUTSequence.__delitem__` method. - """ - - LUT_sequence = self._LUT_sequence.copy() - - del LUT_sequence[0] - del LUT_sequence[0] - - self.assertEqual(LUT_sequence[0], self._LUT_3) - - def test__len__(self): - """ - Tests :class:`colour.io.luts.lut.LUTSequence.__len__` method. - """ - - self.assertEqual(len(self._LUT_sequence), 3) - - def test__str__(self): - """ - Tests :class:`colour.io.luts.lut.LUTSequence.__str__` method. - """ - - self.assertEqual( - str(self._LUT_sequence), - textwrap.dedent(""" - LUT Sequence - ------------ - - Overview - - LUT1D ---> LUT3D ---> LUT3x1D - - Operations - - LUT1D - Nemo 1D - --------------- - - Dimensions : 1 - Domain : [ 0. 1.] - Size : (16,) - - LUT3D - Nemo 3D - --------------- - - Dimensions : 3 - Domain : [[ 0. 0. 0.] - [ 1. 1. 1.]] - Size : (16, 16, 16, 3) - - LUT3x1D - Nemo 3x1D - ------------------- - - Dimensions : 2 - Domain : [[ 0. 0. 0.] - [ 1. 1. 1.]] - Size : (16, 3)""")[1:]) - - def test__repr__(self): - """ - Tests :class:`colour.io.luts.lut.LUTSequence.__repr__` method. - """ - - LUT_sequence = self._LUT_sequence.copy() - LUT_sequence[1].table = LUT3D.linear_table(5) - - self.assertEqual( - repr(LUT_sequence), - textwrap.dedent(""" - LUTSequence( - LUT1D([ 0.125 , 0.19166667, 0.25833333, 0.325 , \ -0.39166667, - 0.45833333, 0.525 , 0.59166667, 0.65833333, \ -0.725 , - 0.79166667, 0.85833333, 0.925 , 0.99166667, \ -1.05833333, - 1.125 ], - name='Nemo 1D', - domain=[ 0., 1.]), - LUT3D([[[[ 0. , 0. , 0. ], - [ 0. , 0. , 0.25], - [ 0. , 0. , 0.5 ], - [ 0. , 0. , 0.75], - [ 0. , 0. , 1. ]], - - [[ 0. , 0.25, 0. ], - [ 0. , 0.25, 0.25], - [ 0. , 0.25, 0.5 ], - [ 0. , 0.25, 0.75], - [ 0. , 0.25, 1. ]], - - [[ 0. , 0.5 , 0. ], - [ 0. , 0.5 , 0.25], - [ 0. , 0.5 , 0.5 ], - [ 0. , 0.5 , 0.75], - [ 0. , 0.5 , 1. ]], - - [[ 0. , 0.75, 0. ], - [ 0. , 0.75, 0.25], - [ 0. , 0.75, 0.5 ], - [ 0. , 0.75, 0.75], - [ 0. , 0.75, 1. ]], - - [[ 0. , 1. , 0. ], - [ 0. , 1. , 0.25], - [ 0. , 1. , 0.5 ], - [ 0. , 1. , 0.75], - [ 0. , 1. , 1. ]]], - - [[[ 0.25, 0. , 0. ], - [ 0.25, 0. , 0.25], - [ 0.25, 0. , 0.5 ], - [ 0.25, 0. , 0.75], - [ 0.25, 0. , 1. ]], - - [[ 0.25, 0.25, 0. ], - [ 0.25, 0.25, 0.25], - [ 0.25, 0.25, 0.5 ], - [ 0.25, 0.25, 0.75], - [ 0.25, 0.25, 1. ]], - - [[ 0.25, 0.5 , 0. ], - [ 0.25, 0.5 , 0.25], - [ 0.25, 0.5 , 0.5 ], - [ 0.25, 0.5 , 0.75], - [ 0.25, 0.5 , 1. ]], - - [[ 0.25, 0.75, 0. ], - [ 0.25, 0.75, 0.25], - [ 0.25, 0.75, 0.5 ], - [ 0.25, 0.75, 0.75], - [ 0.25, 0.75, 1. ]], - - [[ 0.25, 1. , 0. ], - [ 0.25, 1. , 0.25], - [ 0.25, 1. , 0.5 ], - [ 0.25, 1. , 0.75], - [ 0.25, 1. , 1. ]]], - - [[[ 0.5 , 0. , 0. ], - [ 0.5 , 0. , 0.25], - [ 0.5 , 0. , 0.5 ], - [ 0.5 , 0. , 0.75], - [ 0.5 , 0. , 1. ]], - - [[ 0.5 , 0.25, 0. ], - [ 0.5 , 0.25, 0.25], - [ 0.5 , 0.25, 0.5 ], - [ 0.5 , 0.25, 0.75], - [ 0.5 , 0.25, 1. ]], - - [[ 0.5 , 0.5 , 0. ], - [ 0.5 , 0.5 , 0.25], - [ 0.5 , 0.5 , 0.5 ], - [ 0.5 , 0.5 , 0.75], - [ 0.5 , 0.5 , 1. ]], - - [[ 0.5 , 0.75, 0. ], - [ 0.5 , 0.75, 0.25], - [ 0.5 , 0.75, 0.5 ], - [ 0.5 , 0.75, 0.75], - [ 0.5 , 0.75, 1. ]], - - [[ 0.5 , 1. , 0. ], - [ 0.5 , 1. , 0.25], - [ 0.5 , 1. , 0.5 ], - [ 0.5 , 1. , 0.75], - [ 0.5 , 1. , 1. ]]], - - [[[ 0.75, 0. , 0. ], - [ 0.75, 0. , 0.25], - [ 0.75, 0. , 0.5 ], - [ 0.75, 0. , 0.75], - [ 0.75, 0. , 1. ]], - - [[ 0.75, 0.25, 0. ], - [ 0.75, 0.25, 0.25], - [ 0.75, 0.25, 0.5 ], - [ 0.75, 0.25, 0.75], - [ 0.75, 0.25, 1. ]], - - [[ 0.75, 0.5 , 0. ], - [ 0.75, 0.5 , 0.25], - [ 0.75, 0.5 , 0.5 ], - [ 0.75, 0.5 , 0.75], - [ 0.75, 0.5 , 1. ]], - - [[ 0.75, 0.75, 0. ], - [ 0.75, 0.75, 0.25], - [ 0.75, 0.75, 0.5 ], - [ 0.75, 0.75, 0.75], - [ 0.75, 0.75, 1. ]], - - [[ 0.75, 1. , 0. ], - [ 0.75, 1. , 0.25], - [ 0.75, 1. , 0.5 ], - [ 0.75, 1. , 0.75], - [ 0.75, 1. , 1. ]]], - - [[[ 1. , 0. , 0. ], - [ 1. , 0. , 0.25], - [ 1. , 0. , 0.5 ], - [ 1. , 0. , 0.75], - [ 1. , 0. , 1. ]], - - [[ 1. , 0.25, 0. ], - [ 1. , 0.25, 0.25], - [ 1. , 0.25, 0.5 ], - [ 1. , 0.25, 0.75], - [ 1. , 0.25, 1. ]], - - [[ 1. , 0.5 , 0. ], - [ 1. , 0.5 , 0.25], - [ 1. , 0.5 , 0.5 ], - [ 1. , 0.5 , 0.75], - [ 1. , 0.5 , 1. ]], - - [[ 1. , 0.75, 0. ], - [ 1. , 0.75, 0.25], - [ 1. , 0.75, 0.5 ], - [ 1. , 0.75, 0.75], - [ 1. , 0.75, 1. ]], - - [[ 1. , 1. , 0. ], - [ 1. , 1. , 0.25], - [ 1. , 1. , 0.5 ], - [ 1. , 1. , 0.75], - [ 1. , 1. , 1. ]]]], - name='Nemo 3D', - domain=[[ 0., 0., 0.], - [ 1., 1., 1.]]), - LUT3x1D([[ 0. , 0. , 0. ], - [ 0.05, 0.05, 0.05], - [ 0.1 , 0.1 , 0.1 ], - [ 0.15, 0.15, 0.15], - [ 0.2 , 0.2 , 0.2 ], - [ 0.25, 0.25, 0.25], - [ 0.3 , 0.3 , 0.3 ], - [ 0.35, 0.35, 0.35], - [ 0.4 , 0.4 , 0.4 ], - [ 0.45, 0.45, 0.45], - [ 0.5 , 0.5 , 0.5 ], - [ 0.55, 0.55, 0.55], - [ 0.6 , 0.6 , 0.6 ], - [ 0.65, 0.65, 0.65], - [ 0.7 , 0.7 , 0.7 ], - [ 0.75, 0.75, 0.75]], - name='Nemo 3x1D', - domain=[[ 0., 0., 0.], - [ 1., 1., 1.]]) - )""" [1:])) - - def test__eq__(self): - """ - Tests :class:`colour.io.luts.lut.LUTSequence.__eq__` method. - """ - - LUT_sequence_1 = LUTSequence(self._LUT_1, self._LUT_2, self._LUT_3) - LUT_sequence_2 = LUTSequence(self._LUT_1, self._LUT_2) - - self.assertEqual(self._LUT_sequence, LUT_sequence_1) - - self.assertNotEqual(self._LUT_sequence, self._LUT_sequence[0]) - - self.assertNotEqual(LUT_sequence_1, LUT_sequence_2) - - def test__neq__(self): - """ - Tests :class:`colour.io.luts.lut.LUTSequence.__neq__` method. - """ - - self.assertNotEqual( - self._LUT_sequence, - LUTSequence(self._LUT_1, - self._LUT_2.copy() * 0.75, self._LUT_3)) - - def test_insert(self): - """ - Tests :class:`colour.io.luts.lut.LUTSequence.insert` method. - """ - - LUT_sequence = self._LUT_sequence.copy() - - LUT_sequence.insert(1, self._LUT_2.copy()) - - self.assertEqual( - LUT_sequence, - LUTSequence( - self._LUT_1, - self._LUT_2, - self._LUT_2, - self._LUT_3, - )) - - def test_apply(self): - """ - Tests :class:`colour.io.luts.lut.LUTSequence.apply` method. - """ - - class GammaOperator(AbstractLUTSequenceOperator): - """ - Gamma operator for unit tests. - - Parameters - ---------- - gamma : numeric or array_like - Gamma value. - """ - - def __init__(self, gamma=1.0): - self._gamma = gamma - - def apply(self, RGB, *args): - """ - Applies the *LUT* sequence operator to given *RGB* colourspace - array. - - Parameters - ---------- - RGB : array_like - *RGB* colourspace array to apply the *LUT* sequence - operator onto. - - Returns - ------- - ndarray - Processed *RGB* colourspace array. - """ - - return gamma_function(RGB, self._gamma) - - LUT_sequence = self._LUT_sequence.copy() - LUT_sequence.insert(1, GammaOperator(1 / 2.2)) - samples = np.linspace(0, 1, 5) - RGB = tstack([samples, samples, samples]) - - np.testing.assert_almost_equal( - LUT_sequence.apply(RGB), - np.array([ - [0.48779047, 0.48779047, 0.48779047], - [0.61222338, 0.61222338, 0.61222338], - [0.68053686, 0.68053686, 0.68053686], - [0.72954547, 0.72954547, 0.72954547], - [0.75000000, 0.75000000, 0.75000000], - ])) + self._inverted_apply_1 = np.array( + [ + [ + [0.92912690, 0.04737489, 0.00000000], + [0.26685842, 0.16376350, 0.12488904], + ], + [ + [0.94536872, 0.57745743, 0.00934579], + [0.47636096, 0.02946078, 0.90396014], + ], + [ + [0.45473817, 0.72598647, 0.16511861], + [0.03738318, 0.96680135, 0.88860882], + ], + [ + [0.95254891, 0.02803738, 0.59004430], + [0.00000000, 0.32550901, 0.73257860], + ], + ] + ) + + self._inverted_apply_2 = np.array( + [ + [ + [0.93259940, 0.04818925, -0.00146028], + [0.26593731, 0.15743488, 0.12472549], + ], + [ + [0.94081323, 0.57648311, 0.00846963], + [0.48024921, 0.02887666, 0.90683979], + ], + [ + [0.45415635, 0.72121622, 0.15810926], + [0.03825935, 0.96203111, 0.88987440], + ], + [ + [0.94880272, 0.02832944, 0.58872560], + [-0.00146028, 0.32119161, 0.72922327], + ], + ] + ) + self._applied_1 = np.array( + [ + [ + [0.98486974, 0.53531556, 0.05950617], + [0.76022687, 0.68479344, 0.64907649], + ], + [ + [0.98747624, 0.89287549, 0.23859990], + [0.85844632, 0.47829965, 0.98002765], + ], + [ + [0.84903362, 0.93518100, 0.68577990], + [0.49827272, 0.99238949, 0.97644600], + ], + [ + [0.98912224, 0.43911364, 0.89645863], + [0.04125691, 0.79116284, 0.93680839], + ], + ] + ) + + self._applied_2 = np.array( + [ + [ + [0.98486974, 0.53526504, 0.03155191], + [0.76022687, 0.68458573, 0.64850011], + ], + [ + [0.98747624, 0.89277461, 0.15505443], + [0.85844632, 0.47842591, 0.97972986], + ], + [ + [0.84903362, 0.93514331, 0.68479574], + [0.49827272, 0.99234923, 0.97614054], + ], + [ + [0.98912224, 0.43850620, 0.89625878], + [0.04125691, 0.79115345, 0.93648599], + ], + ] + ) + self._applied_3 = np.array( + [ + [ + [0.98685765, 0.58844468, 0.09393531], + [0.79274650, 0.72453018, 0.69347904], + ], + [ + [0.98911162, 0.90807837, 0.25736920], + [0.87825083, 0.53046097, 0.98225775], + ], + [ + [0.87021380, 0.94442819, 0.72448386], + [0.55350090, 0.99318691, 0.97922787], + ], + [ + [0.99054268, 0.49317779, 0.91055390], + [0.02408419, 0.81991814, 0.94597809], + ], + ] + ) + self._applied_4 = self._inverted_apply_1 class TestLUT_to_LUT(unittest.TestCase): """ - Defines :func:`colour.io.luts.lut.LUT_to_LUT` definition unit tests + Define :func:`colour.io.luts.lut.LUT_to_LUT` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._domain = np.array([[0.0, -0.1, -0.2], [1.0, 1.5, 3.0]]) self._LUT_1 = LUT1D(LUT1D.linear_table(16) ** (1 / 2.2)) self._LUT_2 = LUT3x1D( LUT3x1D.linear_table(16) ** (1 / 2.2) * (1.0, 0.75, 0.5), - domain=self._domain) + domain=self._domain, + ) self._LUT_3 = LUT3D( - LUT3D.linear_table(16) ** (1 / 2.2), domain=self._domain) + LUT3D.linear_table(16) ** (1 / 2.2), domain=self._domain + ) def test_LUT_to_LUT(self): - """ - Tests :func:`colour.io.luts.lut.LUT_to_LUT` definition. - """ + """Test :func:`colour.io.luts.lut.LUT_to_LUT` definition.""" # "LUT" 1D to "LUT" 1D. LUT = LUT_to_LUT(self._LUT_1, LUT1D) @@ -1268,158 +1165,197 @@ def test_LUT_to_LUT(self): np.testing.assert_almost_equal( LUT.table, - np.array([[[ - [0.00000000, 0.00000000, 0.00000000], - [0.00000000, 0.00000000, 0.53156948], - [0.00000000, 0.00000000, 0.72933741], - [0.00000000, 0.00000000, 0.87726669], - [0.00000000, 0.00000000, 1.00000000], - ], [ - [0.00000000, 0.53156948, 0.00000000], - [0.00000000, 0.53156948, 0.53156948], - [0.00000000, 0.53156948, 0.72933741], - [0.00000000, 0.53156948, 0.87726669], - [0.00000000, 0.53156948, 1.00000000], - ], [ - [0.00000000, 0.72933741, 0.00000000], - [0.00000000, 0.72933741, 0.53156948], - [0.00000000, 0.72933741, 0.72933741], - [0.00000000, 0.72933741, 0.87726669], - [0.00000000, 0.72933741, 1.00000000], - ], [ - [0.00000000, 0.87726669, 0.00000000], - [0.00000000, 0.87726669, 0.53156948], - [0.00000000, 0.87726669, 0.72933741], - [0.00000000, 0.87726669, 0.87726669], - [0.00000000, 0.87726669, 1.00000000], - ], [ - [0.00000000, 1.00000000, 0.00000000], - [0.00000000, 1.00000000, 0.53156948], - [0.00000000, 1.00000000, 0.72933741], - [0.00000000, 1.00000000, 0.87726669], - [0.00000000, 1.00000000, 1.00000000], - ]], [[ - [0.53156948, 0.00000000, 0.00000000], - [0.53156948, 0.00000000, 0.53156948], - [0.53156948, 0.00000000, 0.72933741], - [0.53156948, 0.00000000, 0.87726669], - [0.53156948, 0.00000000, 1.00000000], - ], [ - [0.53156948, 0.53156948, 0.00000000], - [0.53156948, 0.53156948, 0.53156948], - [0.53156948, 0.53156948, 0.72933741], - [0.53156948, 0.53156948, 0.87726669], - [0.53156948, 0.53156948, 1.00000000], - ], [ - [0.53156948, 0.72933741, 0.00000000], - [0.53156948, 0.72933741, 0.53156948], - [0.53156948, 0.72933741, 0.72933741], - [0.53156948, 0.72933741, 0.87726669], - [0.53156948, 0.72933741, 1.00000000], - ], [ - [0.53156948, 0.87726669, 0.00000000], - [0.53156948, 0.87726669, 0.53156948], - [0.53156948, 0.87726669, 0.72933741], - [0.53156948, 0.87726669, 0.87726669], - [0.53156948, 0.87726669, 1.00000000], - ], [ - [0.53156948, 1.00000000, 0.00000000], - [0.53156948, 1.00000000, 0.53156948], - [0.53156948, 1.00000000, 0.72933741], - [0.53156948, 1.00000000, 0.87726669], - [0.53156948, 1.00000000, 1.00000000], - ]], [[ - [0.72933741, 0.00000000, 0.00000000], - [0.72933741, 0.00000000, 0.53156948], - [0.72933741, 0.00000000, 0.72933741], - [0.72933741, 0.00000000, 0.87726669], - [0.72933741, 0.00000000, 1.00000000], - ], [ - [0.72933741, 0.53156948, 0.00000000], - [0.72933741, 0.53156948, 0.53156948], - [0.72933741, 0.53156948, 0.72933741], - [0.72933741, 0.53156948, 0.87726669], - [0.72933741, 0.53156948, 1.00000000], - ], [ - [0.72933741, 0.72933741, 0.00000000], - [0.72933741, 0.72933741, 0.53156948], - [0.72933741, 0.72933741, 0.72933741], - [0.72933741, 0.72933741, 0.87726669], - [0.72933741, 0.72933741, 1.00000000], - ], [ - [0.72933741, 0.87726669, 0.00000000], - [0.72933741, 0.87726669, 0.53156948], - [0.72933741, 0.87726669, 0.72933741], - [0.72933741, 0.87726669, 0.87726669], - [0.72933741, 0.87726669, 1.00000000], - ], [ - [0.72933741, 1.00000000, 0.00000000], - [0.72933741, 1.00000000, 0.53156948], - [0.72933741, 1.00000000, 0.72933741], - [0.72933741, 1.00000000, 0.87726669], - [0.72933741, 1.00000000, 1.00000000], - ]], [[ - [0.87726669, 0.00000000, 0.00000000], - [0.87726669, 0.00000000, 0.53156948], - [0.87726669, 0.00000000, 0.72933741], - [0.87726669, 0.00000000, 0.87726669], - [0.87726669, 0.00000000, 1.00000000], - ], [ - [0.87726669, 0.53156948, 0.00000000], - [0.87726669, 0.53156948, 0.53156948], - [0.87726669, 0.53156948, 0.72933741], - [0.87726669, 0.53156948, 0.87726669], - [0.87726669, 0.53156948, 1.00000000], - ], [ - [0.87726669, 0.72933741, 0.00000000], - [0.87726669, 0.72933741, 0.53156948], - [0.87726669, 0.72933741, 0.72933741], - [0.87726669, 0.72933741, 0.87726669], - [0.87726669, 0.72933741, 1.00000000], - ], [ - [0.87726669, 0.87726669, 0.00000000], - [0.87726669, 0.87726669, 0.53156948], - [0.87726669, 0.87726669, 0.72933741], - [0.87726669, 0.87726669, 0.87726669], - [0.87726669, 0.87726669, 1.00000000], - ], [ - [0.87726669, 1.00000000, 0.00000000], - [0.87726669, 1.00000000, 0.53156948], - [0.87726669, 1.00000000, 0.72933741], - [0.87726669, 1.00000000, 0.87726669], - [0.87726669, 1.00000000, 1.00000000], - ]], [[ - [1.00000000, 0.00000000, 0.00000000], - [1.00000000, 0.00000000, 0.53156948], - [1.00000000, 0.00000000, 0.72933741], - [1.00000000, 0.00000000, 0.87726669], - [1.00000000, 0.00000000, 1.00000000], - ], [ - [1.00000000, 0.53156948, 0.00000000], - [1.00000000, 0.53156948, 0.53156948], - [1.00000000, 0.53156948, 0.72933741], - [1.00000000, 0.53156948, 0.87726669], - [1.00000000, 0.53156948, 1.00000000], - ], [ - [1.00000000, 0.72933741, 0.00000000], - [1.00000000, 0.72933741, 0.53156948], - [1.00000000, 0.72933741, 0.72933741], - [1.00000000, 0.72933741, 0.87726669], - [1.00000000, 0.72933741, 1.00000000], - ], [ - [1.00000000, 0.87726669, 0.00000000], - [1.00000000, 0.87726669, 0.53156948], - [1.00000000, 0.87726669, 0.72933741], - [1.00000000, 0.87726669, 0.87726669], - [1.00000000, 0.87726669, 1.00000000], - ], [ - [1.00000000, 1.00000000, 0.00000000], - [1.00000000, 1.00000000, 0.53156948], - [1.00000000, 1.00000000, 0.72933741], - [1.00000000, 1.00000000, 0.87726669], - [1.00000000, 1.00000000, 1.00000000], - ]]]), - decimal=7) + np.array( + [ + [ + [ + [0.00000000, 0.00000000, 0.00000000], + [0.00000000, 0.00000000, 0.53156948], + [0.00000000, 0.00000000, 0.72933741], + [0.00000000, 0.00000000, 0.87726669], + [0.00000000, 0.00000000, 1.00000000], + ], + [ + [0.00000000, 0.53156948, 0.00000000], + [0.00000000, 0.53156948, 0.53156948], + [0.00000000, 0.53156948, 0.72933741], + [0.00000000, 0.53156948, 0.87726669], + [0.00000000, 0.53156948, 1.00000000], + ], + [ + [0.00000000, 0.72933741, 0.00000000], + [0.00000000, 0.72933741, 0.53156948], + [0.00000000, 0.72933741, 0.72933741], + [0.00000000, 0.72933741, 0.87726669], + [0.00000000, 0.72933741, 1.00000000], + ], + [ + [0.00000000, 0.87726669, 0.00000000], + [0.00000000, 0.87726669, 0.53156948], + [0.00000000, 0.87726669, 0.72933741], + [0.00000000, 0.87726669, 0.87726669], + [0.00000000, 0.87726669, 1.00000000], + ], + [ + [0.00000000, 1.00000000, 0.00000000], + [0.00000000, 1.00000000, 0.53156948], + [0.00000000, 1.00000000, 0.72933741], + [0.00000000, 1.00000000, 0.87726669], + [0.00000000, 1.00000000, 1.00000000], + ], + ], + [ + [ + [0.53156948, 0.00000000, 0.00000000], + [0.53156948, 0.00000000, 0.53156948], + [0.53156948, 0.00000000, 0.72933741], + [0.53156948, 0.00000000, 0.87726669], + [0.53156948, 0.00000000, 1.00000000], + ], + [ + [0.53156948, 0.53156948, 0.00000000], + [0.53156948, 0.53156948, 0.53156948], + [0.53156948, 0.53156948, 0.72933741], + [0.53156948, 0.53156948, 0.87726669], + [0.53156948, 0.53156948, 1.00000000], + ], + [ + [0.53156948, 0.72933741, 0.00000000], + [0.53156948, 0.72933741, 0.53156948], + [0.53156948, 0.72933741, 0.72933741], + [0.53156948, 0.72933741, 0.87726669], + [0.53156948, 0.72933741, 1.00000000], + ], + [ + [0.53156948, 0.87726669, 0.00000000], + [0.53156948, 0.87726669, 0.53156948], + [0.53156948, 0.87726669, 0.72933741], + [0.53156948, 0.87726669, 0.87726669], + [0.53156948, 0.87726669, 1.00000000], + ], + [ + [0.53156948, 1.00000000, 0.00000000], + [0.53156948, 1.00000000, 0.53156948], + [0.53156948, 1.00000000, 0.72933741], + [0.53156948, 1.00000000, 0.87726669], + [0.53156948, 1.00000000, 1.00000000], + ], + ], + [ + [ + [0.72933741, 0.00000000, 0.00000000], + [0.72933741, 0.00000000, 0.53156948], + [0.72933741, 0.00000000, 0.72933741], + [0.72933741, 0.00000000, 0.87726669], + [0.72933741, 0.00000000, 1.00000000], + ], + [ + [0.72933741, 0.53156948, 0.00000000], + [0.72933741, 0.53156948, 0.53156948], + [0.72933741, 0.53156948, 0.72933741], + [0.72933741, 0.53156948, 0.87726669], + [0.72933741, 0.53156948, 1.00000000], + ], + [ + [0.72933741, 0.72933741, 0.00000000], + [0.72933741, 0.72933741, 0.53156948], + [0.72933741, 0.72933741, 0.72933741], + [0.72933741, 0.72933741, 0.87726669], + [0.72933741, 0.72933741, 1.00000000], + ], + [ + [0.72933741, 0.87726669, 0.00000000], + [0.72933741, 0.87726669, 0.53156948], + [0.72933741, 0.87726669, 0.72933741], + [0.72933741, 0.87726669, 0.87726669], + [0.72933741, 0.87726669, 1.00000000], + ], + [ + [0.72933741, 1.00000000, 0.00000000], + [0.72933741, 1.00000000, 0.53156948], + [0.72933741, 1.00000000, 0.72933741], + [0.72933741, 1.00000000, 0.87726669], + [0.72933741, 1.00000000, 1.00000000], + ], + ], + [ + [ + [0.87726669, 0.00000000, 0.00000000], + [0.87726669, 0.00000000, 0.53156948], + [0.87726669, 0.00000000, 0.72933741], + [0.87726669, 0.00000000, 0.87726669], + [0.87726669, 0.00000000, 1.00000000], + ], + [ + [0.87726669, 0.53156948, 0.00000000], + [0.87726669, 0.53156948, 0.53156948], + [0.87726669, 0.53156948, 0.72933741], + [0.87726669, 0.53156948, 0.87726669], + [0.87726669, 0.53156948, 1.00000000], + ], + [ + [0.87726669, 0.72933741, 0.00000000], + [0.87726669, 0.72933741, 0.53156948], + [0.87726669, 0.72933741, 0.72933741], + [0.87726669, 0.72933741, 0.87726669], + [0.87726669, 0.72933741, 1.00000000], + ], + [ + [0.87726669, 0.87726669, 0.00000000], + [0.87726669, 0.87726669, 0.53156948], + [0.87726669, 0.87726669, 0.72933741], + [0.87726669, 0.87726669, 0.87726669], + [0.87726669, 0.87726669, 1.00000000], + ], + [ + [0.87726669, 1.00000000, 0.00000000], + [0.87726669, 1.00000000, 0.53156948], + [0.87726669, 1.00000000, 0.72933741], + [0.87726669, 1.00000000, 0.87726669], + [0.87726669, 1.00000000, 1.00000000], + ], + ], + [ + [ + [1.00000000, 0.00000000, 0.00000000], + [1.00000000, 0.00000000, 0.53156948], + [1.00000000, 0.00000000, 0.72933741], + [1.00000000, 0.00000000, 0.87726669], + [1.00000000, 0.00000000, 1.00000000], + ], + [ + [1.00000000, 0.53156948, 0.00000000], + [1.00000000, 0.53156948, 0.53156948], + [1.00000000, 0.53156948, 0.72933741], + [1.00000000, 0.53156948, 0.87726669], + [1.00000000, 0.53156948, 1.00000000], + ], + [ + [1.00000000, 0.72933741, 0.00000000], + [1.00000000, 0.72933741, 0.53156948], + [1.00000000, 0.72933741, 0.72933741], + [1.00000000, 0.72933741, 0.87726669], + [1.00000000, 0.72933741, 1.00000000], + ], + [ + [1.00000000, 0.87726669, 0.00000000], + [1.00000000, 0.87726669, 0.53156948], + [1.00000000, 0.87726669, 0.72933741], + [1.00000000, 0.87726669, 0.87726669], + [1.00000000, 0.87726669, 1.00000000], + ], + [ + [1.00000000, 1.00000000, 0.00000000], + [1.00000000, 1.00000000, 0.53156948], + [1.00000000, 1.00000000, 0.72933741], + [1.00000000, 1.00000000, 0.87726669], + [1.00000000, 1.00000000, 1.00000000], + ], + ], + ] + ), + decimal=7, + ) # "LUT" 3x1D to "LUT" 1D. self.assertRaises(ValueError, lambda: LUT_to_LUT(self._LUT_2, LUT1D)) @@ -1429,7 +1365,8 @@ def test_LUT_to_LUT(self): self._LUT_2, LUT1D, force_conversion=True, - channel_weights=channel_weights) + channel_weights=channel_weights, + ) channel_weights = np.array([1 / 3, 1 / 3, 1 / 3]) @@ -1439,13 +1376,16 @@ def test_LUT_to_LUT(self): self._LUT_2, LUT1D, force_conversion=True, - channel_weights=channel_weights) + channel_weights=channel_weights, + ) self.assertEqual( LUT, LUT1D( np.sum(self._LUT_2.table * channel_weights, axis=-1), - domain=domain)) + domain=domain, + ), + ) # "LUT" 3x1D to "LUT" 3x1D. LUT = LUT_to_LUT(self._LUT_2, LUT3x1D) @@ -1459,158 +1399,197 @@ def test_LUT_to_LUT(self): np.testing.assert_almost_equal( LUT.table, - np.array([[[ - [0.00000000, 0.00000000, 0.00000000], - [0.00000000, 0.00000000, 0.26578474], - [0.00000000, 0.00000000, 0.36466870], - [0.00000000, 0.00000000, 0.43863334], - [0.00000000, 0.00000000, 0.50000000], - ], [ - [0.00000000, 0.39867711, 0.00000000], - [0.00000000, 0.39867711, 0.26578474], - [0.00000000, 0.39867711, 0.36466870], - [0.00000000, 0.39867711, 0.43863334], - [0.00000000, 0.39867711, 0.50000000], - ], [ - [0.00000000, 0.54700305, 0.00000000], - [0.00000000, 0.54700305, 0.26578474], - [0.00000000, 0.54700305, 0.36466870], - [0.00000000, 0.54700305, 0.43863334], - [0.00000000, 0.54700305, 0.50000000], - ], [ - [0.00000000, 0.65795001, 0.00000000], - [0.00000000, 0.65795001, 0.26578474], - [0.00000000, 0.65795001, 0.36466870], - [0.00000000, 0.65795001, 0.43863334], - [0.00000000, 0.65795001, 0.50000000], - ], [ - [0.00000000, 0.75000000, 0.00000000], - [0.00000000, 0.75000000, 0.26578474], - [0.00000000, 0.75000000, 0.36466870], - [0.00000000, 0.75000000, 0.43863334], - [0.00000000, 0.75000000, 0.50000000], - ]], [[ - [0.53156948, 0.00000000, 0.00000000], - [0.53156948, 0.00000000, 0.26578474], - [0.53156948, 0.00000000, 0.36466870], - [0.53156948, 0.00000000, 0.43863334], - [0.53156948, 0.00000000, 0.50000000], - ], [ - [0.53156948, 0.39867711, 0.00000000], - [0.53156948, 0.39867711, 0.26578474], - [0.53156948, 0.39867711, 0.36466870], - [0.53156948, 0.39867711, 0.43863334], - [0.53156948, 0.39867711, 0.50000000], - ], [ - [0.53156948, 0.54700305, 0.00000000], - [0.53156948, 0.54700305, 0.26578474], - [0.53156948, 0.54700305, 0.36466870], - [0.53156948, 0.54700305, 0.43863334], - [0.53156948, 0.54700305, 0.50000000], - ], [ - [0.53156948, 0.65795001, 0.00000000], - [0.53156948, 0.65795001, 0.26578474], - [0.53156948, 0.65795001, 0.36466870], - [0.53156948, 0.65795001, 0.43863334], - [0.53156948, 0.65795001, 0.50000000], - ], [ - [0.53156948, 0.75000000, 0.00000000], - [0.53156948, 0.75000000, 0.26578474], - [0.53156948, 0.75000000, 0.36466870], - [0.53156948, 0.75000000, 0.43863334], - [0.53156948, 0.75000000, 0.50000000], - ]], [[ - [0.72933741, 0.00000000, 0.00000000], - [0.72933741, 0.00000000, 0.26578474], - [0.72933741, 0.00000000, 0.36466870], - [0.72933741, 0.00000000, 0.43863334], - [0.72933741, 0.00000000, 0.50000000], - ], [ - [0.72933741, 0.39867711, 0.00000000], - [0.72933741, 0.39867711, 0.26578474], - [0.72933741, 0.39867711, 0.36466870], - [0.72933741, 0.39867711, 0.43863334], - [0.72933741, 0.39867711, 0.50000000], - ], [ - [0.72933741, 0.54700305, 0.00000000], - [0.72933741, 0.54700305, 0.26578474], - [0.72933741, 0.54700305, 0.36466870], - [0.72933741, 0.54700305, 0.43863334], - [0.72933741, 0.54700305, 0.50000000], - ], [ - [0.72933741, 0.65795001, 0.00000000], - [0.72933741, 0.65795001, 0.26578474], - [0.72933741, 0.65795001, 0.36466870], - [0.72933741, 0.65795001, 0.43863334], - [0.72933741, 0.65795001, 0.50000000], - ], [ - [0.72933741, 0.75000000, 0.00000000], - [0.72933741, 0.75000000, 0.26578474], - [0.72933741, 0.75000000, 0.36466870], - [0.72933741, 0.75000000, 0.43863334], - [0.72933741, 0.75000000, 0.50000000], - ]], [[ - [0.87726669, 0.00000000, 0.00000000], - [0.87726669, 0.00000000, 0.26578474], - [0.87726669, 0.00000000, 0.36466870], - [0.87726669, 0.00000000, 0.43863334], - [0.87726669, 0.00000000, 0.50000000], - ], [ - [0.87726669, 0.39867711, 0.00000000], - [0.87726669, 0.39867711, 0.26578474], - [0.87726669, 0.39867711, 0.36466870], - [0.87726669, 0.39867711, 0.43863334], - [0.87726669, 0.39867711, 0.50000000], - ], [ - [0.87726669, 0.54700305, 0.00000000], - [0.87726669, 0.54700305, 0.26578474], - [0.87726669, 0.54700305, 0.36466870], - [0.87726669, 0.54700305, 0.43863334], - [0.87726669, 0.54700305, 0.50000000], - ], [ - [0.87726669, 0.65795001, 0.00000000], - [0.87726669, 0.65795001, 0.26578474], - [0.87726669, 0.65795001, 0.36466870], - [0.87726669, 0.65795001, 0.43863334], - [0.87726669, 0.65795001, 0.50000000], - ], [ - [0.87726669, 0.75000000, 0.00000000], - [0.87726669, 0.75000000, 0.26578474], - [0.87726669, 0.75000000, 0.36466870], - [0.87726669, 0.75000000, 0.43863334], - [0.87726669, 0.75000000, 0.50000000], - ]], [[ - [1.00000000, 0.00000000, 0.00000000], - [1.00000000, 0.00000000, 0.26578474], - [1.00000000, 0.00000000, 0.36466870], - [1.00000000, 0.00000000, 0.43863334], - [1.00000000, 0.00000000, 0.50000000], - ], [ - [1.00000000, 0.39867711, 0.00000000], - [1.00000000, 0.39867711, 0.26578474], - [1.00000000, 0.39867711, 0.36466870], - [1.00000000, 0.39867711, 0.43863334], - [1.00000000, 0.39867711, 0.50000000], - ], [ - [1.00000000, 0.54700305, 0.00000000], - [1.00000000, 0.54700305, 0.26578474], - [1.00000000, 0.54700305, 0.36466870], - [1.00000000, 0.54700305, 0.43863334], - [1.00000000, 0.54700305, 0.50000000], - ], [ - [1.00000000, 0.65795001, 0.00000000], - [1.00000000, 0.65795001, 0.26578474], - [1.00000000, 0.65795001, 0.36466870], - [1.00000000, 0.65795001, 0.43863334], - [1.00000000, 0.65795001, 0.50000000], - ], [ - [1.00000000, 0.75000000, 0.00000000], - [1.00000000, 0.75000000, 0.26578474], - [1.00000000, 0.75000000, 0.36466870], - [1.00000000, 0.75000000, 0.43863334], - [1.00000000, 0.75000000, 0.50000000], - ]]]), - decimal=7) + np.array( + [ + [ + [ + [0.00000000, 0.00000000, 0.00000000], + [0.00000000, 0.00000000, 0.26578474], + [0.00000000, 0.00000000, 0.36466870], + [0.00000000, 0.00000000, 0.43863334], + [0.00000000, 0.00000000, 0.50000000], + ], + [ + [0.00000000, 0.39867711, 0.00000000], + [0.00000000, 0.39867711, 0.26578474], + [0.00000000, 0.39867711, 0.36466870], + [0.00000000, 0.39867711, 0.43863334], + [0.00000000, 0.39867711, 0.50000000], + ], + [ + [0.00000000, 0.54700305, 0.00000000], + [0.00000000, 0.54700305, 0.26578474], + [0.00000000, 0.54700305, 0.36466870], + [0.00000000, 0.54700305, 0.43863334], + [0.00000000, 0.54700305, 0.50000000], + ], + [ + [0.00000000, 0.65795001, 0.00000000], + [0.00000000, 0.65795001, 0.26578474], + [0.00000000, 0.65795001, 0.36466870], + [0.00000000, 0.65795001, 0.43863334], + [0.00000000, 0.65795001, 0.50000000], + ], + [ + [0.00000000, 0.75000000, 0.00000000], + [0.00000000, 0.75000000, 0.26578474], + [0.00000000, 0.75000000, 0.36466870], + [0.00000000, 0.75000000, 0.43863334], + [0.00000000, 0.75000000, 0.50000000], + ], + ], + [ + [ + [0.53156948, 0.00000000, 0.00000000], + [0.53156948, 0.00000000, 0.26578474], + [0.53156948, 0.00000000, 0.36466870], + [0.53156948, 0.00000000, 0.43863334], + [0.53156948, 0.00000000, 0.50000000], + ], + [ + [0.53156948, 0.39867711, 0.00000000], + [0.53156948, 0.39867711, 0.26578474], + [0.53156948, 0.39867711, 0.36466870], + [0.53156948, 0.39867711, 0.43863334], + [0.53156948, 0.39867711, 0.50000000], + ], + [ + [0.53156948, 0.54700305, 0.00000000], + [0.53156948, 0.54700305, 0.26578474], + [0.53156948, 0.54700305, 0.36466870], + [0.53156948, 0.54700305, 0.43863334], + [0.53156948, 0.54700305, 0.50000000], + ], + [ + [0.53156948, 0.65795001, 0.00000000], + [0.53156948, 0.65795001, 0.26578474], + [0.53156948, 0.65795001, 0.36466870], + [0.53156948, 0.65795001, 0.43863334], + [0.53156948, 0.65795001, 0.50000000], + ], + [ + [0.53156948, 0.75000000, 0.00000000], + [0.53156948, 0.75000000, 0.26578474], + [0.53156948, 0.75000000, 0.36466870], + [0.53156948, 0.75000000, 0.43863334], + [0.53156948, 0.75000000, 0.50000000], + ], + ], + [ + [ + [0.72933741, 0.00000000, 0.00000000], + [0.72933741, 0.00000000, 0.26578474], + [0.72933741, 0.00000000, 0.36466870], + [0.72933741, 0.00000000, 0.43863334], + [0.72933741, 0.00000000, 0.50000000], + ], + [ + [0.72933741, 0.39867711, 0.00000000], + [0.72933741, 0.39867711, 0.26578474], + [0.72933741, 0.39867711, 0.36466870], + [0.72933741, 0.39867711, 0.43863334], + [0.72933741, 0.39867711, 0.50000000], + ], + [ + [0.72933741, 0.54700305, 0.00000000], + [0.72933741, 0.54700305, 0.26578474], + [0.72933741, 0.54700305, 0.36466870], + [0.72933741, 0.54700305, 0.43863334], + [0.72933741, 0.54700305, 0.50000000], + ], + [ + [0.72933741, 0.65795001, 0.00000000], + [0.72933741, 0.65795001, 0.26578474], + [0.72933741, 0.65795001, 0.36466870], + [0.72933741, 0.65795001, 0.43863334], + [0.72933741, 0.65795001, 0.50000000], + ], + [ + [0.72933741, 0.75000000, 0.00000000], + [0.72933741, 0.75000000, 0.26578474], + [0.72933741, 0.75000000, 0.36466870], + [0.72933741, 0.75000000, 0.43863334], + [0.72933741, 0.75000000, 0.50000000], + ], + ], + [ + [ + [0.87726669, 0.00000000, 0.00000000], + [0.87726669, 0.00000000, 0.26578474], + [0.87726669, 0.00000000, 0.36466870], + [0.87726669, 0.00000000, 0.43863334], + [0.87726669, 0.00000000, 0.50000000], + ], + [ + [0.87726669, 0.39867711, 0.00000000], + [0.87726669, 0.39867711, 0.26578474], + [0.87726669, 0.39867711, 0.36466870], + [0.87726669, 0.39867711, 0.43863334], + [0.87726669, 0.39867711, 0.50000000], + ], + [ + [0.87726669, 0.54700305, 0.00000000], + [0.87726669, 0.54700305, 0.26578474], + [0.87726669, 0.54700305, 0.36466870], + [0.87726669, 0.54700305, 0.43863334], + [0.87726669, 0.54700305, 0.50000000], + ], + [ + [0.87726669, 0.65795001, 0.00000000], + [0.87726669, 0.65795001, 0.26578474], + [0.87726669, 0.65795001, 0.36466870], + [0.87726669, 0.65795001, 0.43863334], + [0.87726669, 0.65795001, 0.50000000], + ], + [ + [0.87726669, 0.75000000, 0.00000000], + [0.87726669, 0.75000000, 0.26578474], + [0.87726669, 0.75000000, 0.36466870], + [0.87726669, 0.75000000, 0.43863334], + [0.87726669, 0.75000000, 0.50000000], + ], + ], + [ + [ + [1.00000000, 0.00000000, 0.00000000], + [1.00000000, 0.00000000, 0.26578474], + [1.00000000, 0.00000000, 0.36466870], + [1.00000000, 0.00000000, 0.43863334], + [1.00000000, 0.00000000, 0.50000000], + ], + [ + [1.00000000, 0.39867711, 0.00000000], + [1.00000000, 0.39867711, 0.26578474], + [1.00000000, 0.39867711, 0.36466870], + [1.00000000, 0.39867711, 0.43863334], + [1.00000000, 0.39867711, 0.50000000], + ], + [ + [1.00000000, 0.54700305, 0.00000000], + [1.00000000, 0.54700305, 0.26578474], + [1.00000000, 0.54700305, 0.36466870], + [1.00000000, 0.54700305, 0.43863334], + [1.00000000, 0.54700305, 0.50000000], + ], + [ + [1.00000000, 0.65795001, 0.00000000], + [1.00000000, 0.65795001, 0.26578474], + [1.00000000, 0.65795001, 0.36466870], + [1.00000000, 0.65795001, 0.43863334], + [1.00000000, 0.65795001, 0.50000000], + ], + [ + [1.00000000, 0.75000000, 0.00000000], + [1.00000000, 0.75000000, 0.26578474], + [1.00000000, 0.75000000, 0.36466870], + [1.00000000, 0.75000000, 0.43863334], + [1.00000000, 0.75000000, 0.50000000], + ], + ], + ] + ), + decimal=7, + ) # "LUT" 3D to "LUT" 1D. self.assertRaises(ValueError, lambda: LUT_to_LUT(self._LUT_3, LUT1D)) @@ -1621,16 +1600,32 @@ def test_LUT_to_LUT(self): LUT1D, force_conversion=True, size=16, - channel_weights=channel_weights) + channel_weights=channel_weights, + ) np.testing.assert_almost_equal( LUT.table, - np.array([ - 0.00000000, 0.29202031, 0.40017033, 0.48115651, 0.54837380, - 0.60691337, 0.65935329, 0.70721023, 0.75146458, 0.79279273, - 0.83168433, 0.86850710, 0.90354543, 0.93702451, 0.96912624, - 1.00000000 - ])) + np.array( + [ + 0.00000000, + 0.29202031, + 0.40017033, + 0.48115651, + 0.54837380, + 0.60691337, + 0.65935329, + 0.70721023, + 0.75146458, + 0.79279273, + 0.83168433, + 0.86850710, + 0.90354543, + 0.93702451, + 0.96912624, + 1.00000000, + ] + ), + ) channel_weights = np.array([1 / 3, 1 / 3, 1 / 3]) LUT = LUT_to_LUT( @@ -1638,16 +1633,32 @@ def test_LUT_to_LUT(self): LUT1D, force_conversion=True, size=16, - channel_weights=channel_weights) + channel_weights=channel_weights, + ) np.testing.assert_almost_equal( LUT.table, - np.array([ - 0.04562817, 0.24699999, 0.40967557, 0.50401689, 0.57985117, - 0.64458830, 0.70250077, 0.75476586, 0.80317708, 0.83944710, - 0.86337188, 0.88622285, 0.90786039, 0.92160338, 0.92992641, - 0.93781796 - ])) + np.array( + [ + 0.04562817, + 0.24699999, + 0.40967557, + 0.50401689, + 0.57985117, + 0.64458830, + 0.70250077, + 0.75476586, + 0.80317708, + 0.83944710, + 0.86337188, + 0.88622285, + 0.90786039, + 0.92160338, + 0.92992641, + 0.93781796, + ] + ), + ) # "LUT" 3D to "LUT" 3x1D. self.assertRaises(ValueError, lambda: LUT_to_LUT(self._LUT_3, LUT3x1D)) @@ -1656,24 +1667,27 @@ def test_LUT_to_LUT(self): np.testing.assert_almost_equal( LUT.table, - np.array([ - [0.00000000, 0.00000000, 0.00000000], - [0.29202031, 0.29202031, 0.29202031], - [0.40017033, 0.40017033, 0.40017033], - [0.48115651, 0.48115651, 0.48115651], - [0.54837380, 0.54837380, 0.54837380], - [0.60691337, 0.60691337, 0.60691337], - [0.65935329, 0.65935329, 0.65935329], - [0.70721023, 0.70721023, 0.70721023], - [0.75146458, 0.75146458, 0.75146458], - [0.79279273, 0.79279273, 0.79279273], - [0.83168433, 0.83168433, 0.83168433], - [0.86850710, 0.86850710, 0.86850710], - [0.90354543, 0.90354543, 0.90354543], - [0.93702451, 0.93702451, 0.93702451], - [0.96912624, 0.96912624, 0.96912624], - [1.00000000, 1.00000000, 1.00000000], - ])) + np.array( + [ + [0.00000000, 0.00000000, 0.00000000], + [0.29202031, 0.29202031, 0.29202031], + [0.40017033, 0.40017033, 0.40017033], + [0.48115651, 0.48115651, 0.48115651], + [0.54837380, 0.54837380, 0.54837380], + [0.60691337, 0.60691337, 0.60691337], + [0.65935329, 0.65935329, 0.65935329], + [0.70721023, 0.70721023, 0.70721023], + [0.75146458, 0.75146458, 0.75146458], + [0.79279273, 0.79279273, 0.79279273], + [0.83168433, 0.83168433, 0.83168433], + [0.86850710, 0.86850710, 0.86850710], + [0.90354543, 0.90354543, 0.90354543], + [0.93702451, 0.93702451, 0.93702451], + [0.96912624, 0.96912624, 0.96912624], + [1.00000000, 1.00000000, 1.00000000], + ] + ), + ) # "LUT" 3D to "LUT" 3D. LUT = LUT_to_LUT(self._LUT_3, LUT3D) @@ -1681,5 +1695,5 @@ def test_LUT_to_LUT(self): self.assertEqual(LUT, self._LUT_3) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/io/luts/tests/test_operator.py b/colour/io/luts/tests/test_operator.py new file mode 100644 index 0000000000..0a1dc54771 --- /dev/null +++ b/colour/io/luts/tests/test_operator.py @@ -0,0 +1,236 @@ +"""Defines the unit tests for the :mod:`colour.io.luts.operator` module.""" + +import numpy as np +import textwrap +import unittest + +from colour.io.luts import AbstractLUTSequenceOperator, LUTOperatorMatrix +from colour.utilities import tstack, zeros + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestAbstractLUTSequenceOperator", + "TestLUTOperatorMatrix", +] + + +class TestAbstractLUTSequenceOperator(unittest.TestCase): + """ + Define :class:`colour.io.luts.operator.AbstractLUTSequenceOperator` class + unit tests methods. + """ + + def test_required_attributes(self): + """Test the presence of required attributes.""" + + required_attributes = ("name", "comments") + + for method in required_attributes: + self.assertIn(method, dir(AbstractLUTSequenceOperator)) + + def test_required_methods(self): + """Test the presence of required methods.""" + + required_methods = ("apply",) + + for method in required_methods: + self.assertIn(method, dir(AbstractLUTSequenceOperator)) + + +class TestLUTOperatorMatrix(unittest.TestCase): + """ + Define :class:`colour.io.luts.operator.LUTOperatorMatrix` class unit tests + methods. + """ + + def setUp(self): + """Initialise the common tests attributes.""" + + self._lut_operator_matrix = LUTOperatorMatrix( + np.linspace(0, 1, 16).reshape([4, 4]), + offset=np.array([0.25, 0.5, 0.75, 1.0]), + name="Nemo Matrix", + comments=["A first comment.", "A second comment."], + ) + + def test_required_attributes(self): + """Test the presence of required attributes.""" + + required_attributes = ("matrix", "offset") + + for method in required_attributes: + self.assertIn(method, dir(LUTOperatorMatrix)) + + def test_required_methods(self): + """Test the presence of required methods.""" + + required_methods = ("__str__", "__repr__", "__eq__", "__ne__", "apply") + + for method in required_methods: + self.assertIn(method, dir(LUTOperatorMatrix)) + + def test_matrix(self): + """ + Test :class:`colour.io.luts.operator.LUTOperatorMatrix.matrix` + property. + """ + + M = np.identity(3) + + lut_operator_matrix = LUTOperatorMatrix(M) + np.testing.assert_array_equal( + lut_operator_matrix.matrix, np.identity(4) + ) + + def test_offset(self): + """ + Test :class:`colour.io.luts.operator.LUTOperatorMatrix.offset` + property. + """ + + offset = zeros(3) + + lut_operator_matrix = LUTOperatorMatrix(np.identity(3), offset) + np.testing.assert_array_equal(lut_operator_matrix.offset, zeros(4)) + + def test__str__(self): + """ + Test :class:`colour.io.luts.operator.LUTOperatorMatrix.__str__` + method. + """ + + self.assertEqual( + str(self._lut_operator_matrix), + textwrap.dedent( + """ + LUTOperatorMatrix - Nemo Matrix + ------------------------------- + + Matrix : [[ 0. 0.06666667 0.13333333 0.2 ] + [ 0.26666667 0.33333333 0.4 0.46666667] + [ 0.53333333 0.6 0.66666667 0.73333333] + [ 0.8 0.86666667 0.93333333 1. ]] + Offset : [ 0.25 0.5 0.75 1. ] + + A first comment. + A second comment.""" + )[1:], + ) + + def test__repr__(self): + """ + Test :class:`colour.io.luts.operator.LUTOperatorMatrix.__repr__` + method. + """ + + self.assertEqual( + repr(self._lut_operator_matrix), + textwrap.dedent( + """ +LUTOperatorMatrix([[ 0. , 0.06666667, 0.13333333, 0.2 ], + [ 0.26666667, 0.33333333, 0.4 , 0.46666667], + [ 0.53333333, 0.6 , 0.66666667, 0.73333333], + [ 0.8 , 0.86666667, 0.93333333, 1. ]], + [ 0.25, 0.5 , 0.75, 1. ], + name='Nemo Matrix', + comments=['A first comment.', 'A second comment.'])"""[ + 1: + ] + ), + ) + + def test__eq__(self): + """Test :class:`colour.io.luts.operator.LUTOperatorMatrix.__eq__` method.""" + + matrix = LUTOperatorMatrix( + np.linspace(0, 1, 16).reshape([4, 4]), + np.array([0.25, 0.5, 0.75, 1.0]), + ) + + self.assertEqual(self._lut_operator_matrix, matrix) + + def test__neq__(self): + """ + Test :class:`colour.io.luts.operator.LUTOperatorMatrix.__neq__` + method. + """ + + matrix = LUTOperatorMatrix( + np.linspace(0, 1, 16).reshape([4, 4]) * 0.75 + ) + + self.assertNotEqual(self._lut_operator_matrix, matrix) + + def test_apply(self): + """Test :class:`colour.io.luts.operator.LUTOperatorMatrix.apply` method.""" + + samples = np.linspace(0, 1, 5) + RGB = tstack([samples, samples, samples]) + + np.testing.assert_array_equal(LUTOperatorMatrix().apply(RGB), RGB) + + np.testing.assert_almost_equal( + self._lut_operator_matrix.apply(RGB), + np.array( + [ + [0.25000000, 0.50000000, 0.75000000], + [0.30000000, 0.75000000, 1.20000000], + [0.35000000, 1.00000000, 1.65000000], + [0.40000000, 1.25000000, 2.10000000], + [0.45000000, 1.50000000, 2.55000000], + ] + ), + ) + + np.testing.assert_almost_equal( + self._lut_operator_matrix.apply(RGB, apply_offset_first=True), + np.array( + [ + [0.13333333, 0.53333333, 0.93333333], + [0.18333333, 0.78333333, 1.38333333], + [0.23333333, 1.03333333, 1.83333333], + [0.28333333, 1.28333333, 2.28333333], + [0.33333333, 1.53333333, 2.73333333], + ] + ), + ) + + RGBA = tstack([samples, samples, samples, samples]) + + np.testing.assert_array_equal(LUTOperatorMatrix().apply(RGBA), RGBA) + + np.testing.assert_almost_equal( + self._lut_operator_matrix.apply(RGBA), + np.array( + [ + [0.25000000, 0.50000000, 0.75000000, 1.00000000], + [0.35000000, 0.86666667, 1.38333333, 1.90000000], + [0.45000000, 1.23333333, 2.01666667, 2.80000000], + [0.55000000, 1.60000000, 2.65000000, 3.70000000], + [0.65000000, 1.96666667, 3.28333333, 4.60000000], + ] + ), + ) + + np.testing.assert_almost_equal( + self._lut_operator_matrix.apply(RGBA, apply_offset_first=True), + np.array( + [ + [0.33333333, 1.00000000, 1.66666667, 2.33333333], + [0.43333333, 1.36666667, 2.30000000, 3.23333333], + [0.53333333, 1.73333333, 2.93333333, 4.13333333], + [0.63333333, 2.10000000, 3.56666667, 5.03333333], + [0.73333333, 2.46666667, 4.20000000, 5.93333333], + ], + ), + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/io/luts/tests/test_resolve_cube.py b/colour/io/luts/tests/test_resolve_cube.py index 67f6aa45cf..342f4413aa 100644 --- a/colour/io/luts/tests/test_resolve_cube.py +++ b/colour/io/luts/tests/test_resolve_cube.py @@ -1,9 +1,6 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.io.luts.resolve_cube` module. -""" +"""Defines the unit tests for the :mod:`colour.io.luts.resolve_cube` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import os @@ -13,225 +10,263 @@ from colour.io import LUT1D, read_LUT_ResolveCube, write_LUT_ResolveCube -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'LUTS_DIRECTORY', 'TestReadLUTResolveCube', 'TestWriteLUTResolveCube' + "LUTS_DIRECTORY", + "TestReadLUTResolveCube", + "TestWriteLUTResolveCube", ] -LUTS_DIRECTORY = os.path.join( - os.path.dirname(__file__), 'resources', 'resolve_cube') +LUTS_DIRECTORY: str = os.path.join( + os.path.dirname(__file__), "resources", "resolve_cube" +) class TestReadLUTResolveCube(unittest.TestCase): """ - Defines :func:`colour.io.luts.resolve_cube.read_LUT_ResolveCube` definition + Define :func:`colour.io.luts.resolve_cube.read_LUT_ResolveCube` definition unit tests methods. """ def test_read_LUT_ResolveCube(self): """ - Tests :func:`colour.io.luts.resolve_cube.read_LUT_ResolveCube` + Test :func:`colour.io.luts.resolve_cube.read_LUT_ResolveCube` definition. """ LUT_1 = read_LUT_ResolveCube( - os.path.join(LUTS_DIRECTORY, 'ACES_Proxy_10_to_ACES.cube')) + os.path.join(LUTS_DIRECTORY, "ACES_Proxy_10_to_ACES.cube") + ) np.testing.assert_almost_equal( LUT_1.table, - np.array([ - [4.88300000e-04, 4.88300000e-04, 4.88300000e-04], - [7.71400000e-04, 7.71400000e-04, 7.71400000e-04], - [1.21900000e-03, 1.21900000e-03, 1.21900000e-03], - [1.92600000e-03, 1.92600000e-03, 1.92600000e-03], - [3.04400000e-03, 3.04400000e-03, 3.04400000e-03], - [4.80900000e-03, 4.80900000e-03, 4.80900000e-03], - [7.59900000e-03, 7.59900000e-03, 7.59900000e-03], - [1.20100000e-02, 1.20100000e-02, 1.20100000e-02], - [1.89700000e-02, 1.89700000e-02, 1.89700000e-02], - [2.99800000e-02, 2.99800000e-02, 2.99800000e-02], - [4.73700000e-02, 4.73700000e-02, 4.73700000e-02], - [7.48400000e-02, 7.48400000e-02, 7.48400000e-02], - [1.18300000e-01, 1.18300000e-01, 1.18300000e-01], - [1.86900000e-01, 1.86900000e-01, 1.86900000e-01], - [2.95200000e-01, 2.95200000e-01, 2.95200000e-01], - [4.66500000e-01, 4.66500000e-01, 4.66500000e-01], - [7.37100000e-01, 7.37100000e-01, 7.37100000e-01], - [1.16500000e+00, 1.16500000e+00, 1.16500000e+00], - [1.84000000e+00, 1.84000000e+00, 1.84000000e+00], - [2.90800000e+00, 2.90800000e+00, 2.90800000e+00], - [4.59500000e+00, 4.59500000e+00, 4.59500000e+00], - [7.26000000e+00, 7.26000000e+00, 7.26000000e+00], - [1.14700000e+01, 1.14700000e+01, 1.14700000e+01], - [1.81300000e+01, 1.81300000e+01, 1.81300000e+01], - [2.86400000e+01, 2.86400000e+01, 2.86400000e+01], - [4.52500000e+01, 4.52500000e+01, 4.52500000e+01], - [7.15100000e+01, 7.15100000e+01, 7.15100000e+01], - [1.13000000e+02, 1.13000000e+02, 1.13000000e+02], - [1.78500000e+02, 1.78500000e+02, 1.78500000e+02], - [2.82100000e+02, 2.82100000e+02, 2.82100000e+02], - [4.45700000e+02, 4.45700000e+02, 4.45700000e+02], - [7.04300000e+02, 7.04300000e+02, 7.04300000e+02], - ])) - self.assertEqual(LUT_1.name, 'ACES Proxy 10 to ACES') + np.array( + [ + [4.88300000e-04, 4.88300000e-04, 4.88300000e-04], + [7.71400000e-04, 7.71400000e-04, 7.71400000e-04], + [1.21900000e-03, 1.21900000e-03, 1.21900000e-03], + [1.92600000e-03, 1.92600000e-03, 1.92600000e-03], + [3.04400000e-03, 3.04400000e-03, 3.04400000e-03], + [4.80900000e-03, 4.80900000e-03, 4.80900000e-03], + [7.59900000e-03, 7.59900000e-03, 7.59900000e-03], + [1.20100000e-02, 1.20100000e-02, 1.20100000e-02], + [1.89700000e-02, 1.89700000e-02, 1.89700000e-02], + [2.99800000e-02, 2.99800000e-02, 2.99800000e-02], + [4.73700000e-02, 4.73700000e-02, 4.73700000e-02], + [7.48400000e-02, 7.48400000e-02, 7.48400000e-02], + [1.18300000e-01, 1.18300000e-01, 1.18300000e-01], + [1.86900000e-01, 1.86900000e-01, 1.86900000e-01], + [2.95200000e-01, 2.95200000e-01, 2.95200000e-01], + [4.66500000e-01, 4.66500000e-01, 4.66500000e-01], + [7.37100000e-01, 7.37100000e-01, 7.37100000e-01], + [1.16500000e00, 1.16500000e00, 1.16500000e00], + [1.84000000e00, 1.84000000e00, 1.84000000e00], + [2.90800000e00, 2.90800000e00, 2.90800000e00], + [4.59500000e00, 4.59500000e00, 4.59500000e00], + [7.26000000e00, 7.26000000e00, 7.26000000e00], + [1.14700000e01, 1.14700000e01, 1.14700000e01], + [1.81300000e01, 1.81300000e01, 1.81300000e01], + [2.86400000e01, 2.86400000e01, 2.86400000e01], + [4.52500000e01, 4.52500000e01, 4.52500000e01], + [7.15100000e01, 7.15100000e01, 7.15100000e01], + [1.13000000e02, 1.13000000e02, 1.13000000e02], + [1.78500000e02, 1.78500000e02, 1.78500000e02], + [2.82100000e02, 2.82100000e02, 2.82100000e02], + [4.45700000e02, 4.45700000e02, 4.45700000e02], + [7.04300000e02, 7.04300000e02, 7.04300000e02], + ] + ), + ) + self.assertEqual(LUT_1.name, "ACES Proxy 10 to ACES") self.assertEqual(LUT_1.dimensions, 2) - np.testing.assert_array_equal(LUT_1.domain, - np.array([[0, 0, 0], [1, 1, 1]])) + np.testing.assert_array_equal( + LUT_1.domain, np.array([[0, 0, 0], [1, 1, 1]]) + ) self.assertEqual(LUT_1.size, 32) self.assertListEqual(LUT_1.comments, []) - LUT_2 = read_LUT_ResolveCube(os.path.join(LUTS_DIRECTORY, 'Demo.cube')) + LUT_2 = read_LUT_ResolveCube(os.path.join(LUTS_DIRECTORY, "Demo.cube")) self.assertListEqual(LUT_2.comments, ["Comments can't go anywhere"]) - np.testing.assert_array_equal(LUT_2.domain, - np.array([[0, 0, 0], [3, 3, 3]])) + np.testing.assert_array_equal( + LUT_2.domain, np.array([[0, 0, 0], [3, 3, 3]]) + ) LUT_3 = read_LUT_ResolveCube( - os.path.join(LUTS_DIRECTORY, 'Three_Dimensional_Table.cube')) + os.path.join(LUTS_DIRECTORY, "Three_Dimensional_Table.cube") + ) self.assertEqual(LUT_3.dimensions, 3) self.assertEqual(LUT_3.size, 2) LUT_4 = read_LUT_ResolveCube( - os.path.join(LUTS_DIRECTORY, 'LogC_Video.cube')) + os.path.join(LUTS_DIRECTORY, "LogC_Video.cube") + ) np.testing.assert_almost_equal( LUT_4[0].table, - np.array([ - [0.00000000, 0.00000000, 0.00000000], - [0.02708500, 0.02708500, 0.02708500], - [0.06304900, 0.06304900, 0.06304900], - [0.11314900, 0.11314900, 0.11314900], - [0.18304900, 0.18304900, 0.18304900], - [0.28981100, 0.28981100, 0.28981100], - [0.41735300, 0.41735300, 0.41735300], - [0.54523100, 0.54523100, 0.54523100], - [0.67020500, 0.67020500, 0.67020500], - [0.78963000, 0.78963000, 0.78963000], - [0.88646800, 0.88646800, 0.88646800], - [0.94549100, 0.94549100, 0.94549100], - [0.97644900, 0.97644900, 0.97644900], - [0.98924800, 0.98924800, 0.98924800], - [0.99379700, 0.99379700, 0.99379700], - [1.00000000, 1.00000000, 1.00000000], - ]), + np.array( + [ + [0.00000000, 0.00000000, 0.00000000], + [0.02708500, 0.02708500, 0.02708500], + [0.06304900, 0.06304900, 0.06304900], + [0.11314900, 0.11314900, 0.11314900], + [0.18304900, 0.18304900, 0.18304900], + [0.28981100, 0.28981100, 0.28981100], + [0.41735300, 0.41735300, 0.41735300], + [0.54523100, 0.54523100, 0.54523100], + [0.67020500, 0.67020500, 0.67020500], + [0.78963000, 0.78963000, 0.78963000], + [0.88646800, 0.88646800, 0.88646800], + [0.94549100, 0.94549100, 0.94549100], + [0.97644900, 0.97644900, 0.97644900], + [0.98924800, 0.98924800, 0.98924800], + [0.99379700, 0.99379700, 0.99379700], + [1.00000000, 1.00000000, 1.00000000], + ] + ), ) self.assertEqual(LUT_4[1].size, 4) class TestWriteLUTResolveCube(unittest.TestCase): """ - Defines :func:`colour.io.luts.resolve_cube.write_LUT_ResolveCube` + Define :func:`colour.io.luts.resolve_cube.write_LUT_ResolveCube` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._temporary_directory = tempfile.mkdtemp() def tearDown(self): - """ - After tests actions. - """ + """After tests actions.""" shutil.rmtree(self._temporary_directory) def test_write_LUT_ResolveCube(self): """ - Tests :func:`colour.io.luts.resolve_cube.write_LUT_ResolveCube` + Test :func:`colour.io.luts.resolve_cube.write_LUT_ResolveCube` definition. """ LUT_1_r = read_LUT_ResolveCube( - os.path.join(LUTS_DIRECTORY, 'ACES_Proxy_10_to_ACES.cube')) + os.path.join(LUTS_DIRECTORY, "ACES_Proxy_10_to_ACES.cube") + ) write_LUT_ResolveCube( LUT_1_r, - os.path.join(self._temporary_directory, - 'ACES_Proxy_10_to_ACES.cube')) + os.path.join( + self._temporary_directory, "ACES_Proxy_10_to_ACES.cube" + ), + ) LUT_1_t = read_LUT_ResolveCube( - os.path.join(self._temporary_directory, - 'ACES_Proxy_10_to_ACES.cube')) + os.path.join( + self._temporary_directory, "ACES_Proxy_10_to_ACES.cube" + ) + ) self.assertEqual(LUT_1_r, LUT_1_t) LUT_2_r = read_LUT_ResolveCube( - os.path.join(LUTS_DIRECTORY, 'Demo.cube')) + os.path.join(LUTS_DIRECTORY, "Demo.cube") + ) write_LUT_ResolveCube( - LUT_2_r, os.path.join(self._temporary_directory, 'Demo.cube')) + LUT_2_r, os.path.join(self._temporary_directory, "Demo.cube") + ) LUT_2_t = read_LUT_ResolveCube( - os.path.join(self._temporary_directory, 'Demo.cube')) + os.path.join(self._temporary_directory, "Demo.cube") + ) self.assertEqual(LUT_2_r, LUT_2_t) self.assertListEqual(LUT_2_r.comments, LUT_2_t.comments) LUT_3_r = read_LUT_ResolveCube( - os.path.join(LUTS_DIRECTORY, 'Three_Dimensional_Table.cube')) + os.path.join(LUTS_DIRECTORY, "Three_Dimensional_Table.cube") + ) write_LUT_ResolveCube( LUT_3_r, - os.path.join(self._temporary_directory, - 'Three_Dimensional_Table.cube')) + os.path.join( + self._temporary_directory, "Three_Dimensional_Table.cube" + ), + ) LUT_3_t = read_LUT_ResolveCube( - os.path.join(self._temporary_directory, - 'Three_Dimensional_Table.cube')) + os.path.join( + self._temporary_directory, "Three_Dimensional_Table.cube" + ) + ) self.assertEqual(LUT_3_r, LUT_3_t) LUT_4_r = read_LUT_ResolveCube( - os.path.join(LUTS_DIRECTORY, - 'Three_Dimensional_Table_With_Shaper.cube')) + os.path.join( + LUTS_DIRECTORY, "Three_Dimensional_Table_With_Shaper.cube" + ) + ) LUT_4_r.sequence[0] = LUT_4_r.sequence[0].as_LUT( - LUT1D, force_conversion=True) + LUT1D, force_conversion=True + ) write_LUT_ResolveCube( LUT_4_r, - os.path.join(self._temporary_directory, - 'Three_Dimensional_Table_With_Shaper.cube')) + os.path.join( + self._temporary_directory, + "Three_Dimensional_Table_With_Shaper.cube", + ), + ) LUT_4_t = read_LUT_ResolveCube( - os.path.join(self._temporary_directory, - 'Three_Dimensional_Table_With_Shaper.cube')) + os.path.join( + self._temporary_directory, + "Three_Dimensional_Table_With_Shaper.cube", + ) + ) LUT_4_r = read_LUT_ResolveCube( - os.path.join(LUTS_DIRECTORY, - 'Three_Dimensional_Table_With_Shaper.cube')) + os.path.join( + LUTS_DIRECTORY, "Three_Dimensional_Table_With_Shaper.cube" + ) + ) self.assertEqual(LUT_4_r, LUT_4_t) LUT_5_r = read_LUT_ResolveCube( - os.path.join(LUTS_DIRECTORY, 'ACES_Proxy_10_to_ACES.cube')) + os.path.join(LUTS_DIRECTORY, "ACES_Proxy_10_to_ACES.cube") + ) write_LUT_ResolveCube( LUT_5_r.as_LUT(LUT1D, force_conversion=True), - os.path.join(self._temporary_directory, - 'ACES_Proxy_10_to_ACES.cube')) + os.path.join( + self._temporary_directory, "ACES_Proxy_10_to_ACES.cube" + ), + ) LUT_5_t = read_LUT_ResolveCube( - os.path.join(self._temporary_directory, - 'ACES_Proxy_10_to_ACES.cube')) + os.path.join( + self._temporary_directory, "ACES_Proxy_10_to_ACES.cube" + ) + ) self.assertEqual(LUT_5_r, LUT_5_t) def test_raise_exception_write_LUT_ResolveCube(self): """ - Tests :func:`colour.io.luts.resolve_cube.write_LUT_ResolveCube` + Test :func:`colour.io.luts.resolve_cube.write_LUT_ResolveCube` definition raised exception. """ - self.assertRaises(ValueError, write_LUT_ResolveCube, object(), '') + self.assertRaises(ValueError, write_LUT_ResolveCube, object(), "") -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/io/luts/tests/test_sequence.py b/colour/io/luts/tests/test_sequence.py new file mode 100644 index 0000000000..4eff7de612 --- /dev/null +++ b/colour/io/luts/tests/test_sequence.py @@ -0,0 +1,467 @@ +"""Defines the unit tests for the :mod:`colour.io.luts.sequence` module.""" + +from __future__ import annotations + +import numpy as np +import textwrap +import unittest + +from colour.io.luts import ( + AbstractLUTSequenceOperator, + LUT1D, + LUT3x1D, + LUT3D, + LUTSequence, +) +from colour.hints import Any, ArrayLike, FloatingOrNDArray, NDArray +from colour.models import gamma_function +from colour.utilities import as_float_array, tstack + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestLUTSequence", +] + + +class TestLUTSequence(unittest.TestCase): + """ + Define :class:`colour.io.luts.sequence.LUTSequence` class unit tests + methods. + """ + + def setUp(self): + """Initialise the common tests attributes.""" + + self._LUT_1 = LUT1D(LUT1D.linear_table(16) + 0.125, "Nemo 1D") + self._LUT_2 = LUT3D(LUT3D.linear_table(16) ** (1 / 2.2), "Nemo 3D") + self._LUT_3 = LUT3x1D(LUT3x1D.linear_table(16) * 0.750, "Nemo 3x1D") + self._LUT_sequence = LUTSequence(self._LUT_1, self._LUT_2, self._LUT_3) + + samples = np.linspace(0, 1, 5) + + self._RGB = tstack([samples, samples, samples]) + + def test_required_attributes(self): + """Test the presence of required attributes.""" + + required_attributes = ("sequence",) + + for attribute in required_attributes: + self.assertIn(attribute, dir(LUTSequence)) + + def test_required_methods(self): + """Test the presence of required methods.""" + + required_methods = ( + "__init__", + "__getitem__", + "__setitem__", + "__delitem__", + "__len__", + "__str__", + "__repr__", + "__eq__", + "__ne__", + "insert", + "apply", + "copy", + ) + + for method in required_methods: + self.assertIn(method, dir(LUTSequence)) + + def test_sequence(self): + """Test :class:`colour.io.luts.sequence.LUTSequence.sequence` property.""" + + sequence = [self._LUT_1, self._LUT_2, self._LUT_3] + LUT_sequence = LUTSequence() + LUT_sequence.sequence = sequence + self.assertListEqual(self._LUT_sequence.sequence, sequence) + + def test__init__(self): + """Test :class:`colour.io.luts.sequence.LUTSequence.__init__` method.""" + + self.assertEqual( + LUTSequence(self._LUT_1, self._LUT_2, self._LUT_3), + self._LUT_sequence, + ) + + def test__getitem__(self): + """Test :class:`colour.io.luts.sequence.LUTSequence.__getitem__` method.""" + + self.assertEqual(self._LUT_sequence[0], self._LUT_1) + self.assertEqual(self._LUT_sequence[1], self._LUT_2) + self.assertEqual(self._LUT_sequence[2], self._LUT_3) + + def test__setitem__(self): + """Test :class:`colour.io.luts.sequence.LUTSequence.__setitem__` method.""" + + LUT_sequence = self._LUT_sequence.copy() + LUT_sequence[0] = self._LUT_3 + LUT_sequence[1] = self._LUT_1 + LUT_sequence[2] = self._LUT_2 + + self.assertEqual(LUT_sequence[1], self._LUT_1) + self.assertEqual(LUT_sequence[2], self._LUT_2) + self.assertEqual(LUT_sequence[0], self._LUT_3) + + def test__delitem__(self): + """Test :class:`colour.io.luts.sequence.LUTSequence.__delitem__` method.""" + + LUT_sequence = self._LUT_sequence.copy() + + del LUT_sequence[0] + del LUT_sequence[0] + + self.assertEqual(LUT_sequence[0], self._LUT_3) + + def test__len__(self): + """Test :class:`colour.io.luts.sequence.LUTSequence.__len__` method.""" + + self.assertEqual(len(self._LUT_sequence), 3) + + def test__str__(self): + """Test :class:`colour.io.luts.sequence.LUTSequence.__str__` method.""" + + self.assertEqual( + str(self._LUT_sequence), + textwrap.dedent( + """ + LUT Sequence + ------------ + + Overview + + LUT1D --> LUT3D --> LUT3x1D + + Operations + + LUT1D - Nemo 1D + --------------- + + Dimensions : 1 + Domain : [ 0. 1.] + Size : (16,) + + LUT3D - Nemo 3D + --------------- + + Dimensions : 3 + Domain : [[ 0. 0. 0.] + [ 1. 1. 1.]] + Size : (16, 16, 16, 3) + + LUT3x1D - Nemo 3x1D + ------------------- + + Dimensions : 2 + Domain : [[ 0. 0. 0.] + [ 1. 1. 1.]] + Size : (16, 3)""" + )[1:], + ) + + def test__repr__(self): + """Test :class:`colour.io.luts.sequence.LUTSequence.__repr__` method.""" + + LUT_sequence = self._LUT_sequence.copy() + LUT_sequence[1].table = LUT3D.linear_table(5) + + self.assertEqual( + repr(LUT_sequence), + textwrap.dedent( + """ + LUTSequence( + LUT1D([ 0.125 , 0.19166667, 0.25833333, 0.325 , \ +0.39166667, + 0.45833333, 0.525 , 0.59166667, 0.65833333, \ +0.725 , + 0.79166667, 0.85833333, 0.925 , 0.99166667, \ +1.05833333, + 1.125 ], + name='Nemo 1D', + domain=[ 0., 1.]), + LUT3D([[[[ 0. , 0. , 0. ], + [ 0. , 0. , 0.25], + [ 0. , 0. , 0.5 ], + [ 0. , 0. , 0.75], + [ 0. , 0. , 1. ]], + + [[ 0. , 0.25, 0. ], + [ 0. , 0.25, 0.25], + [ 0. , 0.25, 0.5 ], + [ 0. , 0.25, 0.75], + [ 0. , 0.25, 1. ]], + + [[ 0. , 0.5 , 0. ], + [ 0. , 0.5 , 0.25], + [ 0. , 0.5 , 0.5 ], + [ 0. , 0.5 , 0.75], + [ 0. , 0.5 , 1. ]], + + [[ 0. , 0.75, 0. ], + [ 0. , 0.75, 0.25], + [ 0. , 0.75, 0.5 ], + [ 0. , 0.75, 0.75], + [ 0. , 0.75, 1. ]], + + [[ 0. , 1. , 0. ], + [ 0. , 1. , 0.25], + [ 0. , 1. , 0.5 ], + [ 0. , 1. , 0.75], + [ 0. , 1. , 1. ]]], + + [[[ 0.25, 0. , 0. ], + [ 0.25, 0. , 0.25], + [ 0.25, 0. , 0.5 ], + [ 0.25, 0. , 0.75], + [ 0.25, 0. , 1. ]], + + [[ 0.25, 0.25, 0. ], + [ 0.25, 0.25, 0.25], + [ 0.25, 0.25, 0.5 ], + [ 0.25, 0.25, 0.75], + [ 0.25, 0.25, 1. ]], + + [[ 0.25, 0.5 , 0. ], + [ 0.25, 0.5 , 0.25], + [ 0.25, 0.5 , 0.5 ], + [ 0.25, 0.5 , 0.75], + [ 0.25, 0.5 , 1. ]], + + [[ 0.25, 0.75, 0. ], + [ 0.25, 0.75, 0.25], + [ 0.25, 0.75, 0.5 ], + [ 0.25, 0.75, 0.75], + [ 0.25, 0.75, 1. ]], + + [[ 0.25, 1. , 0. ], + [ 0.25, 1. , 0.25], + [ 0.25, 1. , 0.5 ], + [ 0.25, 1. , 0.75], + [ 0.25, 1. , 1. ]]], + + [[[ 0.5 , 0. , 0. ], + [ 0.5 , 0. , 0.25], + [ 0.5 , 0. , 0.5 ], + [ 0.5 , 0. , 0.75], + [ 0.5 , 0. , 1. ]], + + [[ 0.5 , 0.25, 0. ], + [ 0.5 , 0.25, 0.25], + [ 0.5 , 0.25, 0.5 ], + [ 0.5 , 0.25, 0.75], + [ 0.5 , 0.25, 1. ]], + + [[ 0.5 , 0.5 , 0. ], + [ 0.5 , 0.5 , 0.25], + [ 0.5 , 0.5 , 0.5 ], + [ 0.5 , 0.5 , 0.75], + [ 0.5 , 0.5 , 1. ]], + + [[ 0.5 , 0.75, 0. ], + [ 0.5 , 0.75, 0.25], + [ 0.5 , 0.75, 0.5 ], + [ 0.5 , 0.75, 0.75], + [ 0.5 , 0.75, 1. ]], + + [[ 0.5 , 1. , 0. ], + [ 0.5 , 1. , 0.25], + [ 0.5 , 1. , 0.5 ], + [ 0.5 , 1. , 0.75], + [ 0.5 , 1. , 1. ]]], + + [[[ 0.75, 0. , 0. ], + [ 0.75, 0. , 0.25], + [ 0.75, 0. , 0.5 ], + [ 0.75, 0. , 0.75], + [ 0.75, 0. , 1. ]], + + [[ 0.75, 0.25, 0. ], + [ 0.75, 0.25, 0.25], + [ 0.75, 0.25, 0.5 ], + [ 0.75, 0.25, 0.75], + [ 0.75, 0.25, 1. ]], + + [[ 0.75, 0.5 , 0. ], + [ 0.75, 0.5 , 0.25], + [ 0.75, 0.5 , 0.5 ], + [ 0.75, 0.5 , 0.75], + [ 0.75, 0.5 , 1. ]], + + [[ 0.75, 0.75, 0. ], + [ 0.75, 0.75, 0.25], + [ 0.75, 0.75, 0.5 ], + [ 0.75, 0.75, 0.75], + [ 0.75, 0.75, 1. ]], + + [[ 0.75, 1. , 0. ], + [ 0.75, 1. , 0.25], + [ 0.75, 1. , 0.5 ], + [ 0.75, 1. , 0.75], + [ 0.75, 1. , 1. ]]], + + [[[ 1. , 0. , 0. ], + [ 1. , 0. , 0.25], + [ 1. , 0. , 0.5 ], + [ 1. , 0. , 0.75], + [ 1. , 0. , 1. ]], + + [[ 1. , 0.25, 0. ], + [ 1. , 0.25, 0.25], + [ 1. , 0.25, 0.5 ], + [ 1. , 0.25, 0.75], + [ 1. , 0.25, 1. ]], + + [[ 1. , 0.5 , 0. ], + [ 1. , 0.5 , 0.25], + [ 1. , 0.5 , 0.5 ], + [ 1. , 0.5 , 0.75], + [ 1. , 0.5 , 1. ]], + + [[ 1. , 0.75, 0. ], + [ 1. , 0.75, 0.25], + [ 1. , 0.75, 0.5 ], + [ 1. , 0.75, 0.75], + [ 1. , 0.75, 1. ]], + + [[ 1. , 1. , 0. ], + [ 1. , 1. , 0.25], + [ 1. , 1. , 0.5 ], + [ 1. , 1. , 0.75], + [ 1. , 1. , 1. ]]]], + name='Nemo 3D', + domain=[[ 0., 0., 0.], + [ 1., 1., 1.]]), + LUT3x1D([[ 0. , 0. , 0. ], + [ 0.05, 0.05, 0.05], + [ 0.1 , 0.1 , 0.1 ], + [ 0.15, 0.15, 0.15], + [ 0.2 , 0.2 , 0.2 ], + [ 0.25, 0.25, 0.25], + [ 0.3 , 0.3 , 0.3 ], + [ 0.35, 0.35, 0.35], + [ 0.4 , 0.4 , 0.4 ], + [ 0.45, 0.45, 0.45], + [ 0.5 , 0.5 , 0.5 ], + [ 0.55, 0.55, 0.55], + [ 0.6 , 0.6 , 0.6 ], + [ 0.65, 0.65, 0.65], + [ 0.7 , 0.7 , 0.7 ], + [ 0.75, 0.75, 0.75]], + name='Nemo 3x1D', + domain=[[ 0., 0., 0.], + [ 1., 1., 1.]]) + )"""[ + 1: + ] + ), + ) + + def test__eq__(self): + """Test :class:`colour.io.luts.sequence.LUTSequence.__eq__` method.""" + + LUT_sequence_1 = LUTSequence(self._LUT_1, self._LUT_2, self._LUT_3) + LUT_sequence_2 = LUTSequence(self._LUT_1, self._LUT_2) + + self.assertEqual(self._LUT_sequence, LUT_sequence_1) + + self.assertNotEqual(self._LUT_sequence, self._LUT_sequence[0]) + + self.assertNotEqual(LUT_sequence_1, LUT_sequence_2) + + def test__neq__(self): + """Test :class:`colour.io.luts.sequence.LUTSequence.__neq__` method.""" + + self.assertNotEqual( + self._LUT_sequence, + LUTSequence(self._LUT_1, self._LUT_2.copy() * 0.75, self._LUT_3), + ) + + def test_insert(self): + """Test :class:`colour.io.luts.sequence.LUTSequence.insert` method.""" + + LUT_sequence = self._LUT_sequence.copy() + + LUT_sequence.insert(1, self._LUT_2.copy()) + + self.assertEqual( + LUT_sequence, + LUTSequence( + self._LUT_1, + self._LUT_2, + self._LUT_2, + self._LUT_3, + ), + ) + + def test_apply(self): + """Test :class:`colour.io.luts.sequence.LUTSequence.apply` method.""" + + class GammaOperator(AbstractLUTSequenceOperator): + """ + Gamma operator for unit tests. + + Parameters + ---------- + gamma + Gamma value. + """ + + def __init__(self, gamma: FloatingOrNDArray = 1.0): + self._gamma = gamma + + def apply( + self, RGB: ArrayLike, *args: Any, **kwargs: Any + ) -> NDArray: + """ + Apply the *LUT* sequence operator to given *RGB* colourspace + array. + + Parameters + ---------- + RGB + *RGB* colourspace array to apply the *LUT* sequence + operator onto. + + Returns + ------- + :class:`numpy.ndarray` + Processed *RGB* colourspace array. + """ + + direction = kwargs.get("direction", "Forward") + + gamma = ( + self._gamma if direction == "Forward" else 1 / self._gamma + ) + + return as_float_array(gamma_function(RGB, gamma)) + + LUT_sequence = self._LUT_sequence.copy() + LUT_sequence.insert(1, GammaOperator(1 / 2.2)) + samples = np.linspace(0, 1, 5) + RGB = tstack([samples, samples, samples]) + + np.testing.assert_almost_equal( + LUT_sequence.apply(RGB, GammaOperator={"direction": "Inverse"}), + np.array( + [ + [0.03386629, 0.03386629, 0.03386629], + [0.27852298, 0.27852298, 0.27852298], + [0.46830881, 0.46830881, 0.46830881], + [0.65615595, 0.65615595, 0.65615595], + [0.75000000, 0.75000000, 0.75000000], + ] + ), + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/io/luts/tests/test_sony_spi1d.py b/colour/io/luts/tests/test_sony_spi1d.py index cba89aab33..a0be06c569 100644 --- a/colour/io/luts/tests/test_sony_spi1d.py +++ b/colour/io/luts/tests/test_sony_spi1d.py @@ -1,9 +1,6 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.io.luts.sony_spi1d` module. -""" +"""Defines the unit tests for the :mod:`colour.io.luts.sony_spi1d` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import os @@ -13,103 +10,125 @@ from colour.io import read_LUT_SonySPI1D, write_LUT_SonySPI1D -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['LUTS_DIRECTORY', 'TestReadLUTSonySPI1D', 'TestWriteLUTSonySPI1D'] +__all__ = [ + "LUTS_DIRECTORY", + "TestReadLUTSonySPI1D", + "TestWriteLUTSonySPI1D", +] -LUTS_DIRECTORY = os.path.join( - os.path.dirname(__file__), 'resources', 'sony_spi1d') +LUTS_DIRECTORY: str = os.path.join( + os.path.dirname(__file__), "resources", "sony_spi1d" +) class TestReadLUTSonySPI1D(unittest.TestCase): """ - Defines :func:`colour.io.luts.sony_spi1d.read_LUT_SonySPI1D` definition + Define :func:`colour.io.luts.sony_spi1d.read_LUT_SonySPI1D` definition unit tests methods. """ def test_read_LUT_SonySPI1D(self): - """ - Tests :func:`colour.io.luts.sony_spi1d.read_LUT_SonySPI1D` definition. - """ + """Test :func:`colour.io.luts.sony_spi1d.read_LUT_SonySPI1D` definition.""" LUT_1 = read_LUT_SonySPI1D( - os.path.join(LUTS_DIRECTORY, 'eotf_sRGB_1D.spi1d')) + os.path.join(LUTS_DIRECTORY, "eotf_sRGB_1D.spi1d") + ) np.testing.assert_almost_equal( LUT_1.table, - np.array([ - -7.73990000e-03, 5.16000000e-04, 1.22181000e-02, - 3.96819000e-02, 8.71438000e-02, 1.57439400e-01, 2.52950100e-01, - 3.75757900e-01, 5.27729400e-01, 7.10566500e-01, 9.25840600e-01, - 1.17501630e+00, 1.45946870e+00, 1.78049680e+00, 2.13933380e+00, - 2.53715520e+00 - ])) - self.assertEqual(LUT_1.name, 'eotf sRGB 1D') + np.array( + [ + -7.73990000e-03, + 5.16000000e-04, + 1.22181000e-02, + 3.96819000e-02, + 8.71438000e-02, + 1.57439400e-01, + 2.52950100e-01, + 3.75757900e-01, + 5.27729400e-01, + 7.10566500e-01, + 9.25840600e-01, + 1.17501630e00, + 1.45946870e00, + 1.78049680e00, + 2.13933380e00, + 2.53715520e00, + ] + ), + ) + self.assertEqual(LUT_1.name, "eotf sRGB 1D") self.assertEqual(LUT_1.dimensions, 1) np.testing.assert_array_equal(LUT_1.domain, np.array([-0.1, 1.5])) self.assertEqual(LUT_1.size, 16) self.assertListEqual( LUT_1.comments, - ['Generated by "Colour 0.3.11".', '"colour.models.eotf_sRGB".']) + ['Generated by "Colour 0.3.11".', '"colour.models.eotf_sRGB".'], + ) LUT_2 = read_LUT_SonySPI1D( - os.path.join(LUTS_DIRECTORY, 'eotf_sRGB_3x1D.spi1d')) + os.path.join(LUTS_DIRECTORY, "eotf_sRGB_3x1D.spi1d") + ) self.assertListEqual( LUT_2.comments, - ['Generated by "Colour 0.3.11".', '"colour.models.eotf_sRGB".']) + ['Generated by "Colour 0.3.11".', '"colour.models.eotf_sRGB".'], + ) np.testing.assert_array_equal( - LUT_2.domain, np.array([[-0.1, -0.1, -0.1], [1.5, 1.5, 1.5]])) + LUT_2.domain, np.array([[-0.1, -0.1, -0.1], [1.5, 1.5, 1.5]]) + ) class TestWriteLUTSonySPI1D(unittest.TestCase): """ - Defines :func:`colour.io.luts.sony_spi1d.write_LUT_SonySPI1D` definition + Define :func:`colour.io.luts.sony_spi1d.write_LUT_SonySPI1D` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._temporary_directory = tempfile.mkdtemp() def tearDown(self): - """ - After tests actions. - """ + """After tests actions.""" shutil.rmtree(self._temporary_directory) def test_write_LUT_SonySPI1D(self): - """ - Tests :func:`colour.io.luts.sony_spi1d.write_LUT_SonySPI1D` definition. - """ + """Test :func:`colour.io.luts.sony_spi1d.write_LUT_SonySPI1D` definition.""" LUT_1_r = read_LUT_SonySPI1D( - os.path.join(LUTS_DIRECTORY, 'eotf_sRGB_1D.spi1d')) + os.path.join(LUTS_DIRECTORY, "eotf_sRGB_1D.spi1d") + ) write_LUT_SonySPI1D( LUT_1_r, - os.path.join(self._temporary_directory, 'eotf_sRGB_1D.spi1d')) + os.path.join(self._temporary_directory, "eotf_sRGB_1D.spi1d"), + ) LUT_1_t = read_LUT_SonySPI1D( - os.path.join(self._temporary_directory, 'eotf_sRGB_1D.spi1d')) + os.path.join(self._temporary_directory, "eotf_sRGB_1D.spi1d") + ) self.assertEqual(LUT_1_r, LUT_1_t) LUT_2_r = read_LUT_SonySPI1D( - os.path.join(LUTS_DIRECTORY, 'eotf_sRGB_3x1D.spi1d')) + os.path.join(LUTS_DIRECTORY, "eotf_sRGB_3x1D.spi1d") + ) write_LUT_SonySPI1D( LUT_2_r, - os.path.join(self._temporary_directory, 'eotf_sRGB_3x1D.spi1d')) + os.path.join(self._temporary_directory, "eotf_sRGB_3x1D.spi1d"), + ) LUT_2_t = read_LUT_SonySPI1D( - os.path.join(self._temporary_directory, 'eotf_sRGB_3x1D.spi1d')) + os.path.join(self._temporary_directory, "eotf_sRGB_3x1D.spi1d") + ) self.assertEqual(LUT_2_r, LUT_2_t) self.assertListEqual(LUT_2_r.comments, LUT_2_t.comments) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/io/luts/tests/test_sony_spi3d.py b/colour/io/luts/tests/test_sony_spi3d.py index ff68886d4d..ebb4ecb504 100644 --- a/colour/io/luts/tests/test_sony_spi3d.py +++ b/colour/io/luts/tests/test_sony_spi3d.py @@ -1,9 +1,6 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.io.luts.sony_spi3d` module. -""" +"""Defines the unit tests for the :mod:`colour.io.luts.sony_spi3d` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import os @@ -11,425 +8,439 @@ import tempfile import unittest -from colour.io import (LUT3D, LUTSequence, read_LUT_SonySPI3D, - write_LUT_SonySPI3D) +from colour.io import ( + LUT3D, + LUTSequence, + read_LUT_SonySPI3D, + write_LUT_SonySPI3D, +) from colour.utilities import as_int_array -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['LUTS_DIRECTORY', 'TestReadLUTSonySPI3D', 'TestWriteLUTSonySPI3D'] +__all__ = [ + "LUTS_DIRECTORY", + "TestReadLUTSonySPI3D", + "TestWriteLUTSonySPI3D", +] -LUTS_DIRECTORY = os.path.join( - os.path.dirname(__file__), 'resources', 'sony_spi3d') +LUTS_DIRECTORY: str = os.path.join( + os.path.dirname(__file__), "resources", "sony_spi3d" +) class TestReadLUTSonySPI3D(unittest.TestCase): """ - Defines :func:`colour.io.luts.sony_spi3d.read_LUT_SonySPI3D` definition + Define :func:`colour.io.luts.sony_spi3d.read_LUT_SonySPI3D` definition unit tests methods. """ def test_read_LUT_SonySPI3D(self): - """ - Tests :func:`colour.io.luts.sony_spi3d.read_LUT_SonySPI3D` definition. - """ + """Test :func:`colour.io.luts.sony_spi3d.read_LUT_SonySPI3D` definition.""" LUT_1 = read_LUT_SonySPI3D( - os.path.join(LUTS_DIRECTORY, 'Colour_Correct.spi3d')) + os.path.join(LUTS_DIRECTORY, "Colour_Correct.spi3d") + ) np.testing.assert_almost_equal( LUT_1.table, - np.array([ + np.array( [ [ - [0.00000000e+00, 0.00000000e+00, 0.00000000e+00], - [0.00000000e+00, 0.00000000e+00, 4.16653000e-01], - [0.00000000e+00, 0.00000000e+00, 8.33306000e-01], - [1.00000000e-06, 1.00000000e-06, 1.24995900e+00], + [ + [0.00000000e00, 0.00000000e00, 0.00000000e00], + [0.00000000e00, 0.00000000e00, 4.16653000e-01], + [0.00000000e00, 0.00000000e00, 8.33306000e-01], + [1.00000000e-06, 1.00000000e-06, 1.24995900e00], + ], + [ + [-2.62310000e-02, 3.77102000e-01, -2.62310000e-02], + [1.96860000e-02, 2.44702000e-01, 2.44702000e-01], + [1.43270000e-02, 3.30993000e-01, 6.47660000e-01], + [9.02200000e-03, 3.72791000e-01, 1.10033100e00], + ], + [ + [-5.24630000e-02, 7.54204000e-01, -5.24630000e-02], + [0.00000000e00, 6.16667000e-01, 3.08333000e-01], + [3.93720000e-02, 4.89403000e-01, 4.89403000e-01], + [3.57730000e-02, 5.78763000e-01, 8.50258000e-01], + ], + [ + [-7.86940000e-02, 1.13130600e00, -7.86940000e-02], + [-3.59270000e-02, 1.02190800e00, 3.16685000e-01], + [3.09040000e-02, 8.31171000e-01, 5.64415000e-01], + [5.90590000e-02, 7.34105000e-01, 7.34105000e-01], + ], ], [ - [-2.62310000e-02, 3.77102000e-01, -2.62310000e-02], - [1.96860000e-02, 2.44702000e-01, 2.44702000e-01], - [1.43270000e-02, 3.30993000e-01, 6.47660000e-01], - [9.02200000e-03, 3.72791000e-01, 1.10033100e+00], + [ + [3.98947000e-01, -1.77060000e-02, -1.77060000e-02], + [3.33333000e-01, 0.00000000e00, 3.33333000e-01], + [3.90623000e-01, 0.00000000e00, 7.81246000e-01], + [4.04320000e-01, 0.00000000e00, 1.21296000e00], + ], + [ + [2.94597000e-01, 2.94597000e-01, 6.95820000e-02], + [4.16655000e-01, 4.16655000e-01, 4.16655000e-01], + [4.16655000e-01, 4.16655000e-01, 8.33308000e-01], + [4.16656000e-01, 4.16656000e-01, 1.24996100e00], + ], + [ + [3.49416000e-01, 6.57749000e-01, 4.10830000e-02], + [3.40435000e-01, 7.43769000e-01, 3.40435000e-01], + [2.69700000e-01, 4.94715000e-01, 4.94715000e-01], + [3.47660000e-01, 6.64327000e-01, 9.80993000e-01], + ], + [ + [3.44991000e-01, 1.05021300e00, -7.62100000e-03], + [3.14204000e-01, 1.12087100e00, 3.14204000e-01], + [3.08333000e-01, 9.25000000e-01, 6.16667000e-01], + [2.89386000e-01, 7.39417000e-01, 7.39417000e-01], + ], ], [ - [-5.24630000e-02, 7.54204000e-01, -5.24630000e-02], - [0.00000000e+00, 6.16667000e-01, 3.08333000e-01], - [3.93720000e-02, 4.89403000e-01, 4.89403000e-01], - [3.57730000e-02, 5.78763000e-01, 8.50258000e-01], + [ + [7.97894000e-01, -3.54120000e-02, -3.54120000e-02], + [7.52767000e-01, -2.84790000e-02, 3.62144000e-01], + [6.66667000e-01, 0.00000000e00, 6.66667000e-01], + [7.46911000e-01, 0.00000000e00, 1.12036600e00], + ], + [ + [6.33333000e-01, 3.16667000e-01, 0.00000000e00], + [7.32278000e-01, 3.15626000e-01, 3.15626000e-01], + [6.66667000e-01, 3.33333000e-01, 6.66667000e-01], + [7.81246000e-01, 3.90623000e-01, 1.17186900e00], + ], + [ + [5.89195000e-01, 5.89195000e-01, 1.39164000e-01], + [5.94601000e-01, 5.94601000e-01, 3.69586000e-01], + [8.33311000e-01, 8.33311000e-01, 8.33311000e-01], + [8.33311000e-01, 8.33311000e-01, 1.24996300e00], + ], + [ + [6.63432000e-01, 9.30188000e-01, 1.29920000e-01], + [6.82749000e-01, 9.91082000e-01, 3.74416000e-01], + [7.07102000e-01, 1.11043500e00, 7.07102000e-01], + [5.19714000e-01, 7.44729000e-01, 7.44729000e-01], + ], ], [ - [-7.86940000e-02, 1.13130600e+00, -7.86940000e-02], - [-3.59270000e-02, 1.02190800e+00, 3.16685000e-01], - [3.09040000e-02, 8.31171000e-01, 5.64415000e-01], - [5.90590000e-02, 7.34105000e-01, 7.34105000e-01], + [ + [1.19684100e00, -5.31170000e-02, -5.31170000e-02], + [1.16258800e00, -5.03720000e-02, 3.53948000e-01], + [1.08900300e00, -3.13630000e-02, 7.15547000e-01], + [1.00000000e00, 0.00000000e00, 1.00000000e00], + ], + [ + [1.03843900e00, 3.10899000e-01, -5.28700000e-02], + [1.13122500e00, 2.97920000e-01, 2.97920000e-01], + [1.08610100e00, 3.04855000e-01, 6.95478000e-01], + [1.00000000e00, 3.33333000e-01, 1.00000000e00], + ], + [ + [8.91318000e-01, 6.19823000e-01, 7.68330000e-02], + [9.50000000e-01, 6.33333000e-01, 3.16667000e-01], + [1.06561000e00, 6.48957000e-01, 6.48957000e-01], + [1.00000000e00, 6.66667000e-01, 1.00000000e00], + ], + [ + [8.83792000e-01, 8.83792000e-01, 2.08746000e-01], + [8.89199000e-01, 8.89199000e-01, 4.39168000e-01], + [8.94606000e-01, 8.94606000e-01, 6.69590000e-01], + [1.24996600e00, 1.24996600e00, 1.24996600e00], + ], ], - ], - [ - [ - [3.98947000e-01, -1.77060000e-02, -1.77060000e-02], - [3.33333000e-01, 0.00000000e+00, 3.33333000e-01], - [3.90623000e-01, 0.00000000e+00, 7.81246000e-01], - [4.04320000e-01, 0.00000000e+00, 1.21296000e+00], - ], - [ - [2.94597000e-01, 2.94597000e-01, 6.95820000e-02], - [4.16655000e-01, 4.16655000e-01, 4.16655000e-01], - [4.16655000e-01, 4.16655000e-01, 8.33308000e-01], - [4.16656000e-01, 4.16656000e-01, 1.24996100e+00], - ], - [ - [3.49416000e-01, 6.57749000e-01, 4.10830000e-02], - [3.40435000e-01, 7.43769000e-01, 3.40435000e-01], - [2.69700000e-01, 4.94715000e-01, 4.94715000e-01], - [3.47660000e-01, 6.64327000e-01, 9.80993000e-01], - ], - [ - [3.44991000e-01, 1.05021300e+00, -7.62100000e-03], - [3.14204000e-01, 1.12087100e+00, 3.14204000e-01], - [3.08333000e-01, 9.25000000e-01, 6.16667000e-01], - [2.89386000e-01, 7.39417000e-01, 7.39417000e-01], - ], - ], - [ - [ - [7.97894000e-01, -3.54120000e-02, -3.54120000e-02], - [7.52767000e-01, -2.84790000e-02, 3.62144000e-01], - [6.66667000e-01, 0.00000000e+00, 6.66667000e-01], - [7.46911000e-01, 0.00000000e+00, 1.12036600e+00], - ], - [ - [6.33333000e-01, 3.16667000e-01, 0.00000000e+00], - [7.32278000e-01, 3.15626000e-01, 3.15626000e-01], - [6.66667000e-01, 3.33333000e-01, 6.66667000e-01], - [7.81246000e-01, 3.90623000e-01, 1.17186900e+00], - ], - [ - [5.89195000e-01, 5.89195000e-01, 1.39164000e-01], - [5.94601000e-01, 5.94601000e-01, 3.69586000e-01], - [8.33311000e-01, 8.33311000e-01, 8.33311000e-01], - [8.33311000e-01, 8.33311000e-01, 1.24996300e+00], - ], - [ - [6.63432000e-01, 9.30188000e-01, 1.29920000e-01], - [6.82749000e-01, 9.91082000e-01, 3.74416000e-01], - [7.07102000e-01, 1.11043500e+00, 7.07102000e-01], - [5.19714000e-01, 7.44729000e-01, 7.44729000e-01], - ], - ], - [ - [ - [1.19684100e+00, -5.31170000e-02, -5.31170000e-02], - [1.16258800e+00, -5.03720000e-02, 3.53948000e-01], - [1.08900300e+00, -3.13630000e-02, 7.15547000e-01], - [1.00000000e+00, 0.00000000e+00, 1.00000000e+00], - ], - [ - [1.03843900e+00, 3.10899000e-01, -5.28700000e-02], - [1.13122500e+00, 2.97920000e-01, 2.97920000e-01], - [1.08610100e+00, 3.04855000e-01, 6.95478000e-01], - [1.00000000e+00, 3.33333000e-01, 1.00000000e+00], - ], - [ - [8.91318000e-01, 6.19823000e-01, 7.68330000e-02], - [9.50000000e-01, 6.33333000e-01, 3.16667000e-01], - [1.06561000e+00, 6.48957000e-01, 6.48957000e-01], - [1.00000000e+00, 6.66667000e-01, 1.00000000e+00], - ], - [ - [8.83792000e-01, 8.83792000e-01, 2.08746000e-01], - [8.89199000e-01, 8.89199000e-01, 4.39168000e-01], - [8.94606000e-01, 8.94606000e-01, 6.69590000e-01], - [1.24996600e+00, 1.24996600e+00, 1.24996600e+00], - ], - ], - ])) - self.assertEqual(LUT_1.name, 'Colour Correct') + ] + ), + ) + self.assertEqual(LUT_1.name, "Colour Correct") self.assertEqual(LUT_1.dimensions, 3) - np.testing.assert_array_equal(LUT_1.domain, - np.array([[0, 0, 0], [1, 1, 1]])) + np.testing.assert_array_equal( + LUT_1.domain, np.array([[0, 0, 0], [1, 1, 1]]) + ) self.assertEqual(LUT_1.size, 4) - self.assertListEqual(LUT_1.comments, - ['Adapted from a LUT generated by Foundry::LUT.']) + self.assertListEqual( + LUT_1.comments, ["Adapted from a LUT generated by Foundry::LUT."] + ) LUT_2 = read_LUT_SonySPI3D( - os.path.join(LUTS_DIRECTORY, 'Colour_Correct_Unordered.spi3d')) + os.path.join(LUTS_DIRECTORY, "Colour_Correct_Unordered.spi3d") + ) self.assertEqual(LUT_2, LUT_1) - self.assertEqual(LUT_2.name, 'Colour Correct Unordered') + self.assertEqual(LUT_2.name, "Colour Correct Unordered") self.assertEqual(LUT_2.dimensions, 3) self.assertEqual(LUT_2.size, 4) - self.assertListEqual(LUT_2.comments, - ['Adapted from a LUT generated by Foundry::LUT.']) + self.assertListEqual( + LUT_2.comments, ["Adapted from a LUT generated by Foundry::LUT."] + ) class TestWriteLUTSonySPI3D(unittest.TestCase): """ - Defines :func:`colour.io.luts.sony_spi3d.write_LUT_SonySPI3D` definition + Define :func:`colour.io.luts.sony_spi3d.write_LUT_SonySPI3D` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._temporary_directory = tempfile.mkdtemp() def tearDown(self): - """ - After tests actions. - """ + """After tests actions.""" shutil.rmtree(self._temporary_directory) def test_write_LUT_SonySPI3D(self): - """ - Tests :func:`colour.io.luts.sony_spi3d.write_LUT_SonySPI3D` definition. - """ + """Test :func:`colour.io.luts.sony_spi3d.write_LUT_SonySPI3D` definition.""" LUT_r = read_LUT_SonySPI3D( - os.path.join(LUTS_DIRECTORY, 'Colour_Correct.spi3d')) + os.path.join(LUTS_DIRECTORY, "Colour_Correct.spi3d") + ) write_LUT_SonySPI3D( LUT_r, - os.path.join(self._temporary_directory, 'Colour_Correct.spi3d')) + os.path.join(self._temporary_directory, "Colour_Correct.spi3d"), + ) LUT_t = read_LUT_SonySPI3D( - os.path.join(self._temporary_directory, 'Colour_Correct.spi3d')) + os.path.join(self._temporary_directory, "Colour_Correct.spi3d") + ) self.assertEqual(LUT_r, LUT_t) write_LUT_SonySPI3D( LUTSequence(LUT_r), - os.path.join(self._temporary_directory, 'Colour_Correct.spi3d')) + os.path.join(self._temporary_directory, "Colour_Correct.spi3d"), + ) self.assertEqual(LUT_r, LUT_t) # Test for proper indexes sequentiality. - path = os.path.join(self._temporary_directory, 'Size_10_Indexes.spi3d') + path = os.path.join(self._temporary_directory, "Size_10_Indexes.spi3d") write_LUT_SonySPI3D(LUT3D(size=10), path) indexes = [] with open(path) as spi3d_file: - lines = filter(None, - (line.strip() for line in spi3d_file.readlines())) + lines = filter( + None, (line.strip() for line in spi3d_file.readlines()) + ) for line in lines: - if line.startswith('#'): - continue - tokens = line.split() if len(tokens) == 6: indexes.append(as_int_array(tokens[:3])) np.testing.assert_array_equal( as_int_array(indexes)[:200, ...], - np.array([ - [0, 0, 0], - [0, 0, 1], - [0, 0, 2], - [0, 0, 3], - [0, 0, 4], - [0, 0, 5], - [0, 0, 6], - [0, 0, 7], - [0, 0, 8], - [0, 0, 9], - [0, 1, 0], - [0, 1, 1], - [0, 1, 2], - [0, 1, 3], - [0, 1, 4], - [0, 1, 5], - [0, 1, 6], - [0, 1, 7], - [0, 1, 8], - [0, 1, 9], - [0, 2, 0], - [0, 2, 1], - [0, 2, 2], - [0, 2, 3], - [0, 2, 4], - [0, 2, 5], - [0, 2, 6], - [0, 2, 7], - [0, 2, 8], - [0, 2, 9], - [0, 3, 0], - [0, 3, 1], - [0, 3, 2], - [0, 3, 3], - [0, 3, 4], - [0, 3, 5], - [0, 3, 6], - [0, 3, 7], - [0, 3, 8], - [0, 3, 9], - [0, 4, 0], - [0, 4, 1], - [0, 4, 2], - [0, 4, 3], - [0, 4, 4], - [0, 4, 5], - [0, 4, 6], - [0, 4, 7], - [0, 4, 8], - [0, 4, 9], - [0, 5, 0], - [0, 5, 1], - [0, 5, 2], - [0, 5, 3], - [0, 5, 4], - [0, 5, 5], - [0, 5, 6], - [0, 5, 7], - [0, 5, 8], - [0, 5, 9], - [0, 6, 0], - [0, 6, 1], - [0, 6, 2], - [0, 6, 3], - [0, 6, 4], - [0, 6, 5], - [0, 6, 6], - [0, 6, 7], - [0, 6, 8], - [0, 6, 9], - [0, 7, 0], - [0, 7, 1], - [0, 7, 2], - [0, 7, 3], - [0, 7, 4], - [0, 7, 5], - [0, 7, 6], - [0, 7, 7], - [0, 7, 8], - [0, 7, 9], - [0, 8, 0], - [0, 8, 1], - [0, 8, 2], - [0, 8, 3], - [0, 8, 4], - [0, 8, 5], - [0, 8, 6], - [0, 8, 7], - [0, 8, 8], - [0, 8, 9], - [0, 9, 0], - [0, 9, 1], - [0, 9, 2], - [0, 9, 3], - [0, 9, 4], - [0, 9, 5], - [0, 9, 6], - [0, 9, 7], - [0, 9, 8], - [0, 9, 9], - [1, 0, 0], - [1, 0, 1], - [1, 0, 2], - [1, 0, 3], - [1, 0, 4], - [1, 0, 5], - [1, 0, 6], - [1, 0, 7], - [1, 0, 8], - [1, 0, 9], - [1, 1, 0], - [1, 1, 1], - [1, 1, 2], - [1, 1, 3], - [1, 1, 4], - [1, 1, 5], - [1, 1, 6], - [1, 1, 7], - [1, 1, 8], - [1, 1, 9], - [1, 2, 0], - [1, 2, 1], - [1, 2, 2], - [1, 2, 3], - [1, 2, 4], - [1, 2, 5], - [1, 2, 6], - [1, 2, 7], - [1, 2, 8], - [1, 2, 9], - [1, 3, 0], - [1, 3, 1], - [1, 3, 2], - [1, 3, 3], - [1, 3, 4], - [1, 3, 5], - [1, 3, 6], - [1, 3, 7], - [1, 3, 8], - [1, 3, 9], - [1, 4, 0], - [1, 4, 1], - [1, 4, 2], - [1, 4, 3], - [1, 4, 4], - [1, 4, 5], - [1, 4, 6], - [1, 4, 7], - [1, 4, 8], - [1, 4, 9], - [1, 5, 0], - [1, 5, 1], - [1, 5, 2], - [1, 5, 3], - [1, 5, 4], - [1, 5, 5], - [1, 5, 6], - [1, 5, 7], - [1, 5, 8], - [1, 5, 9], - [1, 6, 0], - [1, 6, 1], - [1, 6, 2], - [1, 6, 3], - [1, 6, 4], - [1, 6, 5], - [1, 6, 6], - [1, 6, 7], - [1, 6, 8], - [1, 6, 9], - [1, 7, 0], - [1, 7, 1], - [1, 7, 2], - [1, 7, 3], - [1, 7, 4], - [1, 7, 5], - [1, 7, 6], - [1, 7, 7], - [1, 7, 8], - [1, 7, 9], - [1, 8, 0], - [1, 8, 1], - [1, 8, 2], - [1, 8, 3], - [1, 8, 4], - [1, 8, 5], - [1, 8, 6], - [1, 8, 7], - [1, 8, 8], - [1, 8, 9], - [1, 9, 0], - [1, 9, 1], - [1, 9, 2], - [1, 9, 3], - [1, 9, 4], - [1, 9, 5], - [1, 9, 6], - [1, 9, 7], - [1, 9, 8], - [1, 9, 9], - ])) + np.array( + [ + [0, 0, 0], + [0, 0, 1], + [0, 0, 2], + [0, 0, 3], + [0, 0, 4], + [0, 0, 5], + [0, 0, 6], + [0, 0, 7], + [0, 0, 8], + [0, 0, 9], + [0, 1, 0], + [0, 1, 1], + [0, 1, 2], + [0, 1, 3], + [0, 1, 4], + [0, 1, 5], + [0, 1, 6], + [0, 1, 7], + [0, 1, 8], + [0, 1, 9], + [0, 2, 0], + [0, 2, 1], + [0, 2, 2], + [0, 2, 3], + [0, 2, 4], + [0, 2, 5], + [0, 2, 6], + [0, 2, 7], + [0, 2, 8], + [0, 2, 9], + [0, 3, 0], + [0, 3, 1], + [0, 3, 2], + [0, 3, 3], + [0, 3, 4], + [0, 3, 5], + [0, 3, 6], + [0, 3, 7], + [0, 3, 8], + [0, 3, 9], + [0, 4, 0], + [0, 4, 1], + [0, 4, 2], + [0, 4, 3], + [0, 4, 4], + [0, 4, 5], + [0, 4, 6], + [0, 4, 7], + [0, 4, 8], + [0, 4, 9], + [0, 5, 0], + [0, 5, 1], + [0, 5, 2], + [0, 5, 3], + [0, 5, 4], + [0, 5, 5], + [0, 5, 6], + [0, 5, 7], + [0, 5, 8], + [0, 5, 9], + [0, 6, 0], + [0, 6, 1], + [0, 6, 2], + [0, 6, 3], + [0, 6, 4], + [0, 6, 5], + [0, 6, 6], + [0, 6, 7], + [0, 6, 8], + [0, 6, 9], + [0, 7, 0], + [0, 7, 1], + [0, 7, 2], + [0, 7, 3], + [0, 7, 4], + [0, 7, 5], + [0, 7, 6], + [0, 7, 7], + [0, 7, 8], + [0, 7, 9], + [0, 8, 0], + [0, 8, 1], + [0, 8, 2], + [0, 8, 3], + [0, 8, 4], + [0, 8, 5], + [0, 8, 6], + [0, 8, 7], + [0, 8, 8], + [0, 8, 9], + [0, 9, 0], + [0, 9, 1], + [0, 9, 2], + [0, 9, 3], + [0, 9, 4], + [0, 9, 5], + [0, 9, 6], + [0, 9, 7], + [0, 9, 8], + [0, 9, 9], + [1, 0, 0], + [1, 0, 1], + [1, 0, 2], + [1, 0, 3], + [1, 0, 4], + [1, 0, 5], + [1, 0, 6], + [1, 0, 7], + [1, 0, 8], + [1, 0, 9], + [1, 1, 0], + [1, 1, 1], + [1, 1, 2], + [1, 1, 3], + [1, 1, 4], + [1, 1, 5], + [1, 1, 6], + [1, 1, 7], + [1, 1, 8], + [1, 1, 9], + [1, 2, 0], + [1, 2, 1], + [1, 2, 2], + [1, 2, 3], + [1, 2, 4], + [1, 2, 5], + [1, 2, 6], + [1, 2, 7], + [1, 2, 8], + [1, 2, 9], + [1, 3, 0], + [1, 3, 1], + [1, 3, 2], + [1, 3, 3], + [1, 3, 4], + [1, 3, 5], + [1, 3, 6], + [1, 3, 7], + [1, 3, 8], + [1, 3, 9], + [1, 4, 0], + [1, 4, 1], + [1, 4, 2], + [1, 4, 3], + [1, 4, 4], + [1, 4, 5], + [1, 4, 6], + [1, 4, 7], + [1, 4, 8], + [1, 4, 9], + [1, 5, 0], + [1, 5, 1], + [1, 5, 2], + [1, 5, 3], + [1, 5, 4], + [1, 5, 5], + [1, 5, 6], + [1, 5, 7], + [1, 5, 8], + [1, 5, 9], + [1, 6, 0], + [1, 6, 1], + [1, 6, 2], + [1, 6, 3], + [1, 6, 4], + [1, 6, 5], + [1, 6, 6], + [1, 6, 7], + [1, 6, 8], + [1, 6, 9], + [1, 7, 0], + [1, 7, 1], + [1, 7, 2], + [1, 7, 3], + [1, 7, 4], + [1, 7, 5], + [1, 7, 6], + [1, 7, 7], + [1, 7, 8], + [1, 7, 9], + [1, 8, 0], + [1, 8, 1], + [1, 8, 2], + [1, 8, 3], + [1, 8, 4], + [1, 8, 5], + [1, 8, 6], + [1, 8, 7], + [1, 8, 8], + [1, 8, 9], + [1, 9, 0], + [1, 9, 1], + [1, 9, 2], + [1, 9, 3], + [1, 9, 4], + [1, 9, 5], + [1, 9, 6], + [1, 9, 7], + [1, 9, 8], + [1, 9, 9], + ] + ), + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/io/luts/tests/test_sony_spimtx.py b/colour/io/luts/tests/test_sony_spimtx.py new file mode 100644 index 0000000000..af81904f9d --- /dev/null +++ b/colour/io/luts/tests/test_sony_spimtx.py @@ -0,0 +1,148 @@ +"""Defines the unit tests for the :mod:`colour.io.luts.sony_spimtx` module.""" + +from __future__ import annotations + +import numpy as np +import os +import shutil +import tempfile +import unittest + +from colour.io import read_LUT_SonySPImtx, write_LUT_SonySPImtx + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "LUTS_DIRECTORY", + "TestReadLUTSonySPImtx", + "TestWriteLUTSonySPImtx", +] + +LUTS_DIRECTORY: str = os.path.join( + os.path.dirname(__file__), "resources", "sony_spimtx" +) + + +class TestReadLUTSonySPImtx(unittest.TestCase): + """ + Define :func:`colour.io.luts.sony_spimtx.read_LUT_SonySPImtx` definition + unit tests methods. + """ + + def test_read_LUT_SonySPImtx(self): + """ + Test :func:`colour.io.luts.sony_spimtx.read_LUT_SonySPImtx` + definition. + """ + + LUT_1 = read_LUT_SonySPImtx(os.path.join(LUTS_DIRECTORY, "dt.spimtx")) + + np.testing.assert_almost_equal( + LUT_1.matrix, + np.array( + [ + [0.864274, 0.000000, 0.000000, 0.000000], + [0.000000, 0.864274, 0.000000, 0.000000], + [0.000000, 0.000000, 0.864274, 0.000000], + [0.000000, 0.000000, 0.000000, 1.000000], + ] + ), + ) + np.testing.assert_almost_equal( + LUT_1.offset, np.array([0.000000, 0.000000, 0.000000, 0.000000]) + ) + self.assertEqual(LUT_1.name, "dt") + + LUT_2 = read_LUT_SonySPImtx( + os.path.join(LUTS_DIRECTORY, "p3_to_xyz16.spimtx") + ) + np.testing.assert_almost_equal( + LUT_2.matrix, + np.array( + [ + [0.44488, 0.27717, 0.17237, 0.00000], + [0.20936, 0.72170, 0.06895, 0.00000], + [0.00000, 0.04707, 0.90780, 0.00000], + [0.00000, 0.00000, 0.00000, 1.00000], + ] + ), + ) + np.testing.assert_almost_equal( + LUT_2.offset, np.array([0.000000, 0.000000, 0.000000, 0.000000]) + ) + self.assertEqual(LUT_2.name, "p3 to xyz16") + + LUT_3 = read_LUT_SonySPImtx( + os.path.join(LUTS_DIRECTORY, "Matrix_Offset.spimtx") + ) + np.testing.assert_almost_equal( + LUT_3.matrix, + np.array( + [ + [1.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + ] + ), + ) + np.testing.assert_almost_equal( + LUT_3.offset, np.array([0.0, 0.0, 1.0, 0.0]) + ) + self.assertEqual(LUT_3.name, "Matrix Offset") + + +class TestWriteLUTSonySPImtx(unittest.TestCase): + """ + Define :func:`colour.io.luts.sony_spimtx.write_LUT_SonySPImtx` definition + unit tests methods. + """ + + def setUp(self): + """Initialise the common tests attributes.""" + + self._temporary_directory = tempfile.mkdtemp() + + def tearDown(self): + """After tests actions.""" + + shutil.rmtree(self._temporary_directory) + + def test_write_LUT_SonySPImtx(self): + """ + Test :func:`colour.io.luts.sony_spimtx.write_LUT_SonySPImtx` + definition. + """ + + LUT_1_r = read_LUT_SonySPImtx( + os.path.join(LUTS_DIRECTORY, "dt.spimtx") + ) + write_LUT_SonySPImtx( + LUT_1_r, os.path.join(self._temporary_directory, "dt.spimtx") + ) + LUT_1_t = read_LUT_SonySPImtx( + os.path.join(self._temporary_directory, "dt.spimtx") + ) + self.assertEqual(LUT_1_r, LUT_1_t) + + LUT_2_r = read_LUT_SonySPImtx( + os.path.join(LUTS_DIRECTORY, "p3_to_xyz16.spimtx") + ) + write_LUT_SonySPImtx( + LUT_2_r, + os.path.join(self._temporary_directory, "p3_to_xyz16.spimtx"), + ) + LUT_2_t = read_LUT_SonySPImtx( + os.path.join(self._temporary_directory, "p3_to_xyz16.spimtx") + ) + self.assertEqual(LUT_2_r, LUT_2_t) + self.assertListEqual(LUT_2_r.comments, LUT_2_t.comments) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/io/ocio.py b/colour/io/ocio.py new file mode 100644 index 0000000000..5748e3a7ed --- /dev/null +++ b/colour/io/ocio.py @@ -0,0 +1,117 @@ +""" +OpenColorIO Processing +====================== + +Defines the object for *OpenColorIO* processing: + +- :func:`colour.io.process_image_OpenColorIO` +""" + +from __future__ import annotations + +import numpy as np + +from colour.hints import Any, ArrayLike, NDArray +from colour.utilities import as_float_array, required + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "process_image_OpenColorIO", +] + + +@required("OpenColorIO") +def process_image_OpenColorIO( + a: ArrayLike, *args: Any, **kwargs: Any +) -> NDArray: + """ + Process given image with *OpenColorIO*. + + Parameters + ---------- + a + Image to process with *OpenColorIO*. + + Other Parameters + ---------------- + config + *OpenColorIO* config to use for processing. If not defined, the + *OpenColorIO* set defined by the ``$OCIO`` environment variable is + used. + args + Arguments for `Config.getProcessor` method. + See https://opencolorio.readthedocs.io/en/latest/api/config.html for + more information. + + Returns + ------- + :class:`numpy.ndarray` + Processed image. + + Examples + -------- + # TODO: Reinstate when "Pypi" wheel compatible with "ARM" on "macOS" is + # released. + + >>> import os + >>> import PyOpenColorIO as ocio # doctest: +SKIP + >>> from colour.utilities import full + >>> config = os.path.join( + ... os.path.dirname(__file__), 'tests', 'resources', + ... 'config-aces-reference.ocio.yaml') + >>> a = full([4, 2, 3], 0.18) + >>> process_image_OpenColorIO( # doctest: +SKIP + ... a, 'ACES - ACES2065-1', 'ACES - ACEScct', config=config) + array([[[ 0.4135878..., 0.4135878..., 0.4135878...], + [ 0.4135878..., 0.4135878..., 0.4135878...]], + + [[ 0.4135878..., 0.4135878..., 0.4135878...], + [ 0.4135878..., 0.4135878..., 0.4135878...]], + + [[ 0.4135878..., 0.4135878..., 0.4135878...], + [ 0.4135878..., 0.4135878..., 0.4135878...]], + + [[ 0.4135878..., 0.4135878..., 0.4135878...], + [ 0.4135878..., 0.4135878..., 0.4135878...]]], dtype=float32) + >>> process_image_OpenColorIO( # doctest: +SKIP + ... a, 'ACES - ACES2065-1', 'Display - sRGB', + ... 'Output - SDR Video - ACES 1.0', ocio.TRANSFORM_DIR_FORWARD, + ... config=config) + array([[[ 0.3559523..., 0.3559525..., 0.3559525...], + [ 0.3559523..., 0.3559525..., 0.3559525...]], + + [[ 0.3559523..., 0.3559525..., 0.3559525...], + [ 0.3559523..., 0.3559525..., 0.3559525...]], + + [[ 0.3559523..., 0.3559525..., 0.3559525...], + [ 0.3559523..., 0.3559525..., 0.3559525...]], + + [[ 0.3559523..., 0.3559525..., 0.3559525...], + [ 0.3559523..., 0.3559525..., 0.3559525...]]], dtype=float32) + """ + + import PyOpenColorIO as ocio + + config = kwargs.get("config") + config = ( + ocio.Config.CreateFromEnv() + if config is None + else ocio.Config.CreateFromFile(config) + ) + + a = as_float_array(np.atleast_3d(a), dtype=np.float32) + height, width, channels = a.shape + + processor = config.getProcessor(*args).getDefaultCPUProcessor() + + image_desc = ocio.PackedImageDesc(a, width, height, channels) + + processor.apply(image_desc) + + return image_desc.getData().reshape([height, width, channels]) diff --git a/colour/io/tabular.py b/colour/io/tabular.py index 568f28da48..0f5a0f5862 100644 --- a/colour/io/tabular.py +++ b/colour/io/tabular.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ CSV Tabular Data Input / Output =============================== @@ -10,33 +9,37 @@ - :func:`colour.write_sds_to_csv_file` """ -from __future__ import division, unicode_literals +from __future__ import annotations -from collections import OrderedDict import csv +import numpy as np +import os +import tempfile from colour.colorimetry import SpectralDistribution from colour.constants import DEFAULT_FLOAT_DTYPE +from colour.hints import Any, Boolean, Dict, NDArray +from colour.utilities import filter_kwargs -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'read_spectral_data_from_csv_file', 'read_sds_from_csv_file', - 'write_sds_to_csv_file' + "read_spectral_data_from_csv_file", + "read_sds_from_csv_file", + "write_sds_to_csv_file", ] -def read_spectral_data_from_csv_file(path, - delimiter=',', - fields=None, - default=0): +def read_spectral_data_from_csv_file( + path: str, **kwargs: Any +) -> Dict[str, NDArray]: """ - Reads the spectral data from given *CSV* file in the following form:: + Read the spectral data from given *CSV* file in the following form:: 390, 4.15003E-04, 3.68349E-04, 9.54729E-03 395, 1.05192E-03, 9.58658E-04, 2.38250E-02 @@ -44,42 +47,36 @@ def read_spectral_data_from_csv_file(path, ... 830, 9.74306E-07, 9.53411E-08, 0.00000 - and returns it as an *OrderedDict* of *dict* as follows:: + and returns it as an *dict* as follows:: - OrderedDict([ - ('field', {'wavelength': 'value', ..., 'wavelength': 'value'}), - ..., - ('field', {'wavelength': 'value', ..., 'wavelength': 'value'})]) + { + 'wavelength': ndarray, + 'field 1': ndarray, + 'field 2': ndarray, + ..., + 'field n': ndarray + } Parameters ---------- - path : unicode - Absolute *CSV* file path. - delimiter : unicode, optional - *CSV* file content delimiter. - fields : array_like, optional - *CSV* file spectral data fields names. If no value is provided the - first line of the file will be used as spectral data fields names. - default : numeric, optional - Default value for fields row with missing value. + path + *CSV* file path. + + Other Parameters + ---------------- + kwargs + Keywords arguments passed to :func:`numpy.recfromcsv` definition. Returns ------- - OrderedDict + :class:`dict` *CSV* file content. - Raises - ------ - RuntimeError - If the *CSV* spectral data file doesn't define the appropriate fields. - Notes ----- - A *CSV* spectral data file should define at least define two fields: one for the wavelengths and one for the associated values of one spectral distribution. - - If no value is provided for the fields names, the first line of the - file will be used as spectral data fields names. Examples -------- @@ -89,7 +86,8 @@ def read_spectral_data_from_csv_file(path, ... 'resources', 'colorchecker_n_ohta.csv') >>> sds_data = read_spectral_data_from_csv_file(csv_file) >>> pprint(list(sds_data.keys())) - ['1', + ['wavelength', + '1', '2', '3', '4', @@ -115,50 +113,59 @@ def read_spectral_data_from_csv_file(path, '24'] """ - with open(path, 'rU') as csv_file: - reader = csv.DictReader( - csv_file, delimiter=str(delimiter), fieldnames=fields) - if len(reader.fieldnames) == 1: - raise RuntimeError(('A "CSV" spectral data file should define ' - 'the following fields: ' - '("wavelength", "field 1", ..., "field n")!')) + settings = { + "case_sensitive": True, + "deletechars": "", + "replace_space": " ", + "dtype": DEFAULT_FLOAT_DTYPE, + } + settings.update(**kwargs) + + transpose = settings.get("transpose") + if transpose: + delimiter = settings.get("delimiter", ",") + if settings.get("delimiter") is not None: + del settings["delimiter"] - wavelength = reader.fieldnames[0] - fields = reader.fieldnames[1:] + with open(path) as csv_file: + content = zip(*csv.reader(csv_file, delimiter=delimiter)) - data = OrderedDict(zip(fields, ({} for _ in range(len(fields))))) - for line in reader: - for field in fields: - try: - value = DEFAULT_FLOAT_DTYPE(line[field]) - except ValueError: - value = default + transposed_csv_file = tempfile.NamedTemporaryFile( + mode="w", delete=False + ) + path = transposed_csv_file.name + csv.writer(transposed_csv_file).writerows(content) + transposed_csv_file.close() - data[field][DEFAULT_FLOAT_DTYPE(line[wavelength])] = value - return data + data = np.recfromcsv(path, **filter_kwargs(np.genfromtxt, **settings)) + if transpose: + os.unlink(transposed_csv_file.name) -def read_sds_from_csv_file(path, delimiter=',', fields=None, default=0): + return {name: data[name] for name in data.dtype.names} + + +def read_sds_from_csv_file( + path: str, **kwargs: Any +) -> Dict[str, SpectralDistribution]: """ - Reads the spectral data from given *CSV* file and return its content as an - *OrderedDict* of :class:`colour.SpectralDistribution` classes. + Read the spectral data from given *CSV* file and returns its content as a + *dict* of :class:`colour.SpectralDistribution` class instances. Parameters ---------- - path : unicode - Absolute *CSV* file path. - delimiter : unicode, optional - *CSV* file content delimiter. - fields : array_like, optional - *CSV* file spectral data fields names. If no value is provided the - first line of the file will be used for as spectral data fields names. - default : numeric - Default value for fields row with missing value. + path + *CSV* file path. + + Other Parameters + ---------------- + kwargs + Keywords arguments passed to :func:`numpy.recfromcsv` definition. Returns ------- - OrderedDict - :class:`colour.SpectralDistribution` classes of given *CSV* file. + :class:`dict` + *Dict* of :class:`colour.SpectralDistribution` class instances. Examples -------- @@ -259,62 +266,68 @@ def read_sds_from_csv_file(path, delimiter=',', fields=None, default=0): extrapolator_kwargs={...}) """ - data = read_spectral_data_from_csv_file(path, delimiter, fields, default) + data = read_spectral_data_from_csv_file(path, **kwargs) + + fields = list(data.keys()) + wavelength_field, sd_fields = fields[0], fields[1:] + + sds = { + sd_field: SpectralDistribution( + data[sd_field], data[wavelength_field], name=sd_field + ) + for sd_field in sd_fields + } - sds = OrderedDict(((key, SpectralDistribution(value, name=key)) - for key, value in data.items())) return sds -def write_sds_to_csv_file(sds, path, delimiter=',', fields=None): +def write_sds_to_csv_file( + sds: Dict[str, SpectralDistribution], path: str +) -> Boolean: """ - Writes the given spectral distributions to given *CSV* file. + Write the given spectral distributions to given *CSV* file. Parameters ---------- - sds : dict - Spectral distributions to write. - path : unicode - Absolute *CSV* file path. - delimiter : unicode, optional - *CSV* file content delimiter. - fields : array_like, optional - *CSV* file spectral data fields names. If no value is provided the - order of fields will be the one defined by the sorted spectral - distributions *dict*. + sds + Spectral distributions to write to given *CSV* file. + path + *CSV* file path. Returns ------- - bool + :class:`bool` Definition success. Raises ------ - RuntimeError + ValueError If the given spectral distributions have different shapes. """ if len(sds) != 1: shapes = [sd.shape for sd in sds.values()] if not all(shape == shapes[0] for shape in shapes): - raise RuntimeError(('Cannot write spectral distributions ' - 'with different shapes to "CSV" file!')) + raise ValueError( + "Cannot write spectral distributions " + 'with different shapes to "CSV" file!' + ) wavelengths = tuple(sds.values())[0].wavelengths - with open(path, 'w') as csv_file: - fields = list(fields) if fields is not None else sorted(sds.keys()) + with open(path, "w") as csv_file: + fields = sorted(sds.keys()) writer = csv.DictWriter( csv_file, - delimiter=str(delimiter), - fieldnames=['wavelength'] + fields, - lineterminator='\n') + delimiter=",", + fieldnames=["wavelength"] + fields, + lineterminator="\n", + ) writer.writeheader() for wavelength in wavelengths: - row = {'wavelength': wavelength} - row.update( - dict((field, sds[field][wavelength]) for field in fields)) + row = {"wavelength": wavelength} + row.update({field: sds[field][wavelength] for field in fields}) writer.writerow(row) return True diff --git a/colour/io/tests/__init__.py b/colour/io/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/io/tests/__init__.py +++ b/colour/io/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/io/tests/resources/ESPD2021_0104_231446.xls b/colour/io/tests/resources/ESPD2021_0104_231446.xls new file mode 100644 index 0000000000..11f61f3567 --- /dev/null +++ b/colour/io/tests/resources/ESPD2021_0104_231446.xls @@ -0,0 +1,441 @@ +Model Name CV600 +Serial Number 19J00789 +Time 2021/01/04_23:14:46 +Memo +LUX 695.154907 +fc 64.605476 +CCT 5198.000000 +Duv -0.000620 +I-Time 12000.000000 +X 682.470886 +Y 695.154907 +Z 631.635071 +x 0.339663 +y 0.345975 +u' 0.209915 +v' 0.481087 +LambdaP 456.000000 +LambdaPValue 18.404581 +CRI 92.956993 +R1 91.651062 +R2 93.014732 +R3 97.032013 +R4 93.513229 +R5 92.482590 +R6 91.486870 +R7 93.016129 +R8 91.459312 +R9 77.613075 +R10 86.981613 +R11 94.841324 +R12 74.139542 +R13 91.073837 +R14 97.064323 +R15 88.615669 +TLCI 97.495056 +TLMF-A 1.270032 +SSI-A 44.881924 +Rf 87.234917 +Rg 98.510712 +IRR 2.607891 +380nm 0.030267 +381nm 0.030267 +382nm 0.030267 +383nm 0.029822 +384nm 0.028978 +385nm 0.028623 +386nm 0.030845 +387nm 0.035596 +388nm 0.039231 +389nm 0.039064 +390nm 0.035223 +391nm 0.031580 +392nm 0.029181 +393nm 0.027808 +394nm 0.026256 +395nm 0.024526 +396nm 0.022557 +397nm 0.020419 +398nm 0.018521 +399nm 0.018149 +400nm 0.019325 +401nm 0.021666 +402nm 0.024045 +403nm 0.026473 +404nm 0.029076 +405nm 0.031840 +406nm 0.033884 +407nm 0.034038 +408nm 0.032302 +409nm 0.030383 +410nm 0.029426 +411nm 0.029979 +412nm 0.032614 +413nm 0.037204 +414nm 0.042279 +415nm 0.046029 +416nm 0.048698 +417nm 0.053064 +418nm 0.059530 +419nm 0.070840 +420nm 0.087678 +421nm 0.110043 +422nm 0.136705 +423nm 0.165180 +424nm 0.199071 +425nm 0.241976 +426nm 0.293837 +427nm 0.359177 +428nm 0.434192 +429nm 0.523828 +430nm 0.632578 +431nm 0.758893 +432nm 0.915528 +433nm 1.096489 +434nm 1.307487 +435nm 1.557125 +436nm 1.838779 +437nm 2.183382 +438nm 2.586251 +439nm 3.054022 +440nm 3.625659 +441nm 4.279538 +442nm 5.055838 +443nm 5.919301 +444nm 6.869926 +445nm 7.940298 +446nm 9.090219 +447nm 10.336670 +448nm 11.619895 +449nm 12.939739 +450nm 14.206918 +451nm 15.396660 +452nm 16.430536 +453nm 17.267374 +454nm 17.912292 +455nm 18.261185 +456nm 18.404581 +457nm 18.288025 +458nm 18.002302 +459nm 17.570372 +460nm 17.011297 +461nm 16.411137 +462nm 15.779440 +463nm 15.168951 +464nm 14.585364 +465nm 14.057872 +466nm 13.575768 +467nm 13.144953 +468nm 12.737307 +469nm 12.346188 +470nm 11.967313 +471nm 11.590308 +472nm 11.209807 +473nm 10.815372 +474nm 10.406748 +475nm 10.007284 +476nm 9.627886 +477nm 9.279286 +478nm 8.958391 +479nm 8.663115 +480nm 8.427362 +481nm 8.238759 +482nm 8.110200 +483nm 8.011048 +484nm 7.939125 +485nm 7.900343 +486nm 7.880703 +487nm 7.887271 +488nm 7.907047 +489nm 7.939895 +490nm 7.977298 +491nm 8.013443 +492nm 8.056756 +493nm 8.112617 +494nm 8.181398 +495nm 8.256148 +496nm 8.332609 +497nm 8.418014 +498nm 8.513148 +499nm 8.616785 +500nm 8.719036 +501nm 8.817776 +502nm 8.914417 +503nm 9.011255 +504nm 9.105255 +505nm 9.193217 +506nm 9.274889 +507nm 9.350751 +508nm 9.423820 +509nm 9.490992 +510nm 9.553215 +511nm 9.608335 +512nm 9.653841 +513nm 9.691347 +514nm 9.727146 +515nm 9.767722 +516nm 9.809064 +517nm 9.842565 +518nm 9.867527 +519nm 9.887219 +520nm 9.906105 +521nm 9.920433 +522nm 9.929304 +523nm 9.932856 +524nm 9.935204 +525nm 9.937991 +526nm 9.938448 +527nm 9.936127 +528nm 9.930192 +529nm 9.922665 +530nm 9.913944 +531nm 9.905774 +532nm 9.898767 +533nm 9.894219 +534nm 9.891479 +535nm 9.883711 +536nm 9.862693 +537nm 9.829168 +538nm 9.795257 +539nm 9.767633 +540nm 9.747380 +541nm 9.729669 +542nm 9.714886 +543nm 9.701355 +544nm 9.688311 +545nm 9.673670 +546nm 9.657027 +547nm 9.633310 +548nm 9.603127 +549nm 9.567823 +550nm 9.534049 +551nm 9.504526 +552nm 9.484178 +553nm 9.471739 +554nm 9.455969 +555nm 9.429557 +556nm 9.396450 +557nm 9.368848 +558nm 9.344832 +559nm 9.313942 +560nm 9.273922 +561nm 9.240767 +562nm 9.220987 +563nm 9.210749 +564nm 9.195800 +565nm 9.173392 +566nm 9.143906 +567nm 9.109710 +568nm 9.078232 +569nm 9.052593 +570nm 9.023234 +571nm 8.984895 +572nm 8.950663 +573nm 8.935179 +574nm 8.936305 +575nm 8.937272 +576nm 8.931671 +577nm 8.921451 +578nm 8.910289 +579nm 8.908619 +580nm 8.917888 +581nm 8.934530 +582nm 8.946784 +583nm 8.958764 +584nm 8.979334 +585nm 9.007913 +586nm 9.033543 +587nm 9.051113 +588nm 9.067842 +589nm 9.089899 +590nm 9.114546 +591nm 9.136106 +592nm 9.164270 +593nm 9.207536 +594nm 9.264211 +595nm 9.321528 +596nm 9.371778 +597nm 9.411209 +598nm 9.443729 +599nm 9.490623 +600nm 9.557871 +601nm 9.626752 +602nm 9.674832 +603nm 9.705856 +604nm 9.739429 +605nm 9.784062 +606nm 9.841268 +607nm 9.907084 +608nm 9.971845 +609nm 10.026823 +610nm 10.060076 +611nm 10.076903 +612nm 10.105914 +613nm 10.161287 +614nm 10.230108 +615nm 10.285982 +616nm 10.336598 +617nm 10.396016 +618nm 10.449015 +619nm 10.478296 +620nm 10.484620 +621nm 10.487537 +622nm 10.498996 +623nm 10.519572 +624nm 10.541495 +625nm 10.549863 +626nm 10.543288 +627nm 10.538241 +628nm 10.546865 +629nm 10.560687 +630nm 10.567954 +631nm 10.564369 +632nm 10.555919 +633nm 10.542054 +634nm 10.527417 +635nm 10.513332 +636nm 10.500641 +637nm 10.493341 +638nm 10.491714 +639nm 10.477033 +640nm 10.435987 +641nm 10.374922 +642nm 10.317416 +643nm 10.269583 +644nm 10.220937 +645nm 10.168004 +646nm 10.115719 +647nm 10.061740 +648nm 9.998492 +649nm 9.919030 +650nm 9.821223 +651nm 9.716800 +652nm 9.619915 +653nm 9.531602 +654nm 9.435769 +655nm 9.326644 +656nm 9.215940 +657nm 9.111384 +658nm 9.005102 +659nm 8.892046 +660nm 8.775783 +661nm 8.659118 +662nm 8.537835 +663nm 8.413469 +664nm 8.292587 +665nm 8.175849 +666nm 8.055606 +667nm 7.931369 +668nm 7.812479 +669nm 7.695505 +670nm 7.564718 +671nm 7.422195 +672nm 7.286375 +673nm 7.166087 +674nm 7.050159 +675nm 6.925609 +676nm 6.792675 +677nm 6.659946 +678nm 6.534333 +679nm 6.416044 +680nm 6.298086 +681nm 6.182296 +682nm 6.073105 +683nm 5.965933 +684nm 5.853682 +685nm 5.729931 +686nm 5.599877 +687nm 5.480670 +688nm 5.376213 +689nm 5.273221 +690nm 5.156234 +691nm 5.027091 +692nm 4.900242 +693nm 4.777046 +694nm 4.658288 +695nm 4.547010 +696nm 4.443560 +697nm 4.347722 +698nm 4.252159 +699nm 4.152643 +700nm 4.053906 +701nm 3.961853 +702nm 3.865061 +703nm 3.755302 +704nm 3.634861 +705nm 3.519360 +706nm 3.418803 +707nm 3.328571 +708nm 3.246458 +709nm 3.160225 +710nm 3.066386 +711nm 2.970290 +712nm 2.878098 +713nm 2.790311 +714nm 2.701265 +715nm 2.607646 +716nm 2.515490 +717nm 2.435313 +718nm 2.361505 +719nm 2.282271 +720nm 2.192500 +721nm 2.101594 +722nm 2.027356 +723nm 1.966553 +724nm 1.912948 +725nm 1.855193 +726nm 1.785138 +727nm 1.710667 +728nm 1.638785 +729nm 1.582385 +730nm 1.539228 +731nm 1.498548 +732nm 1.455407 +733nm 1.413034 +734nm 1.372021 +735nm 1.324772 +736nm 1.277157 +737nm 1.238888 +738nm 1.211113 +739nm 1.182541 +740nm 1.149382 +741nm 1.118490 +742nm 1.091204 +743nm 1.065539 +744nm 1.039564 +745nm 1.013148 +746nm 0.990818 +747nm 0.976522 +748nm 0.960074 +749nm 0.935639 +750nm 0.905095 +751nm 0.878893 +752nm 0.862828 +753nm 0.847588 +754nm 0.829938 +755nm 0.808772 +756nm 0.786338 +757nm 0.761752 +758nm 0.735873 +759nm 0.711232 +760nm 0.690947 +761nm 0.673476 +762nm 0.659236 +763nm 0.646735 +764nm 0.633802 +765nm 0.612864 +766nm 0.589102 +767nm 0.567989 +768nm 0.551288 +769nm 0.533479 +770nm 0.508426 +771nm 0.487143 +772nm 0.474126 +773nm 0.465145 +774nm 0.455158 +775nm 0.442994 +776nm 0.429114 +777nm 0.419402 +778nm 0.411766 +779nm 0.411766 +780nm 0.411766 diff --git a/colour/io/tests/resources/Fluorescent.spdx b/colour/io/tests/resources/Fluorescent.spdx index 2a1b870fc3..47e652eb8a 100755 --- a/colour/io/tests/resources/Fluorescent.spdx +++ b/colour/io/tests/resources/Fluorescent.spdx @@ -16,6 +16,8 @@ relative 2.0 true + other + other 0.034 0.037 0.069 diff --git a/colour/io/tests/resources/Invalid.csv b/colour/io/tests/resources/Invalid.csv deleted file mode 100644 index 99f0fe0183..0000000000 --- a/colour/io/tests/resources/Invalid.csv +++ /dev/null @@ -1,32 +0,0 @@ -wavelength -400 -410 -420 -430 -440 -450 -460 -470 -480 -490 -500 -510 -520 -530 -540 -550 -560 -570 -580 -590 -600 -610 -620 -630 -640 -650 -660 -670 -680 -690 -700 diff --git a/colour/io/tests/resources/Invalid.spdx b/colour/io/tests/resources/Invalid.spdx new file mode 100755 index 0000000000..db71b1e79e --- /dev/null +++ b/colour/io/tests/resources/Invalid.spdx @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/colour/io/tests/resources/Overflowing_Gradient.png b/colour/io/tests/resources/Overflowing_Gradient.png new file mode 100644 index 0000000000..faaaafd5bf Binary files /dev/null and b/colour/io/tests/resources/Overflowing_Gradient.png differ diff --git a/colour/io/tests/resources/RANDOM_001_02._3262K.csv b/colour/io/tests/resources/RANDOM_001_02._3262K.csv new file mode 100644 index 0000000000..c779cd87ca --- /dev/null +++ b/colour/io/tests/resources/RANDOM_001_02._3262K.csv @@ -0,0 +1,547 @@ +Date Saved,15/03/2021 3:44:14 p.m. +Title,RANDOM_001_02°_3262K + +Measuring Mode,Ambient +Viewing Angle [°],2 +Tcp [K],3262 +⊿uv,-0.0029 +Illuminance [lx],30.1 +Illuminance [fc],2.79 +Peak Wavelength [nm],608 +Tristimulus Value X,32.1626 +Tristimulus Value Y,30.0794 +Tristimulus Value Z,15.0951 +CIE1931 x,0.4159 +CIE1931 y,0.3889 +CIE1931 z,0.1952 +CIE1976 u',0.2434 +CIE1976 v',0.5121 +Dominant Wavelength [nm],583 +Purity [%],41.5 +PPFD [umolm⁻²s⁻¹],0.4 +CRI Ra,87.5 +CRI R1,87.6 +CRI R2,94.5 +CRI R3,96.8 +CRI R4,85.8 +CRI R5,87.3 +CRI R6,92.3 +CRI R7,86.4 +CRI R8,69.8 +CRI R9,31.2 +CRI R10,85.6 +CRI R11,85.1 +CRI R12,75.6 +CRI R13,89.6 +CRI R14,98.8 +CRI R15,82.5 +TM-30 Rf,87 +TM-30 Rg,98 +SSIt,76 +SSId,59 +SSI1,--- +SSI2,--- +TLCI,79 +TLMF,--- + +Spectral Data 380[nm],0.000000000000 +Spectral Data 385[nm],0.000000000000 +Spectral Data 390[nm],0.000001241384 +Spectral Data 395[nm],0.000009549279 +Spectral Data 400[nm],0.000012219248 +Spectral Data 405[nm],0.000014717889 +Spectral Data 410[nm],0.000019805240 +Spectral Data 415[nm],0.000024765639 +Spectral Data 420[nm],0.000030628980 +Spectral Data 425[nm],0.000043541328 +Spectral Data 430[nm],0.000065267952 +Spectral Data 435[nm],0.000105050320 +Spectral Data 440[nm],0.000170654966 +Spectral Data 445[nm],0.000254058628 +Spectral Data 450[nm],0.000327921705 +Spectral Data 455[nm],0.000356870936 +Spectral Data 460[nm],0.000330946583 +Spectral Data 465[nm],0.000278670836 +Spectral Data 470[nm],0.000231046899 +Spectral Data 475[nm],0.000194364664 +Spectral Data 480[nm],0.000168378494 +Spectral Data 485[nm],0.000158944778 +Spectral Data 490[nm],0.000165003628 +Spectral Data 495[nm],0.000179804862 +Spectral Data 500[nm],0.000202082141 +Spectral Data 505[nm],0.000230180638 +Spectral Data 510[nm],0.000257534470 +Spectral Data 515[nm],0.000281763205 +Spectral Data 520[nm],0.000304359623 +Spectral Data 525[nm],0.000323822256 +Spectral Data 530[nm],0.000341474341 +Spectral Data 535[nm],0.000356402656 +Spectral Data 540[nm],0.000363348285 +Spectral Data 545[nm],0.000370280904 +Spectral Data 550[nm],0.000386978383 +Spectral Data 555[nm],0.000405657454 +Spectral Data 560[nm],0.000421844015 +Spectral Data 565[nm],0.000439213240 +Spectral Data 570[nm],0.000458202092 +Spectral Data 575[nm],0.000478220201 +Spectral Data 580[nm],0.000501049974 +Spectral Data 585[nm],0.000522196002 +Spectral Data 590[nm],0.000540709822 +Spectral Data 595[nm],0.000556560291 +Spectral Data 600[nm],0.000571069017 +Spectral Data 605[nm],0.000582406239 +Spectral Data 610[nm],0.000582838256 +Spectral Data 615[nm],0.000572920195 +Spectral Data 620[nm],0.000557220716 +Spectral Data 625[nm],0.000537657819 +Spectral Data 630[nm],0.000516130880 +Spectral Data 635[nm],0.000483979762 +Spectral Data 640[nm],0.000440809410 +Spectral Data 645[nm],0.000400088611 +Spectral Data 650[nm],0.000363167142 +Spectral Data 655[nm],0.000327863469 +Spectral Data 660[nm],0.000296237296 +Spectral Data 665[nm],0.000268036878 +Spectral Data 670[nm],0.000243845148 +Spectral Data 675[nm],0.000222554067 +Spectral Data 680[nm],0.000204679847 +Spectral Data 685[nm],0.000188804188 +Spectral Data 690[nm],0.000175491703 +Spectral Data 695[nm],0.000164392884 +Spectral Data 700[nm],0.000152852212 +Spectral Data 705[nm],0.000142118937 +Spectral Data 710[nm],0.000129854467 +Spectral Data 715[nm],0.000115937015 +Spectral Data 720[nm],0.000103409096 +Spectral Data 725[nm],0.000093400769 +Spectral Data 730[nm],0.000087911932 +Spectral Data 735[nm],0.000084191641 +Spectral Data 740[nm],0.000076264798 +Spectral Data 745[nm],0.000070139104 +Spectral Data 750[nm],0.000068490612 +Spectral Data 755[nm],0.000050868475 +Spectral Data 760[nm],0.000039099319 +Spectral Data 765[nm],0.000042047446 +Spectral Data 770[nm],0.000037225029 +Spectral Data 775[nm],0.000038605547 +Spectral Data 780[nm],0.000038278766 + +Spectral Data 380[nm],0.000000000000 +Spectral Data 381[nm],0.000000000000 +Spectral Data 382[nm],0.000000000000 +Spectral Data 383[nm],0.000000000000 +Spectral Data 384[nm],0.000000000000 +Spectral Data 385[nm],0.000000000000 +Spectral Data 386[nm],0.000000000000 +Spectral Data 387[nm],0.000000000000 +Spectral Data 388[nm],0.000000000000 +Spectral Data 389[nm],0.000000000000 +Spectral Data 390[nm],0.000000000000 +Spectral Data 391[nm],0.000000000000 +Spectral Data 392[nm],0.000002927853 +Spectral Data 393[nm],0.000006502053 +Spectral Data 394[nm],0.000009265275 +Spectral Data 395[nm],0.000011032038 +Spectral Data 396[nm],0.000011953731 +Spectral Data 397[nm],0.000012279555 +Spectral Data 398[nm],0.000012258756 +Spectral Data 399[nm],0.000012112181 +Spectral Data 400[nm],0.000011981365 +Spectral Data 401[nm],0.000011995159 +Spectral Data 402[nm],0.000012281144 +Spectral Data 403[nm],0.000012880828 +Spectral Data 404[nm],0.000013697349 +Spectral Data 405[nm],0.000014621435 +Spectral Data 406[nm],0.000015547508 +Spectral Data 407[nm],0.000016454918 +Spectral Data 408[nm],0.000017407952 +Spectral Data 409[nm],0.000018474588 +Spectral Data 410[nm],0.000019711053 +Spectral Data 411[nm],0.000021048536 +Spectral Data 412[nm],0.000022339967 +Spectral Data 413[nm],0.000023436902 +Spectral Data 414[nm],0.000024226094 +Spectral Data 415[nm],0.000024806555 +Spectral Data 416[nm],0.000025354624 +Spectral Data 417[nm],0.000026046688 +Spectral Data 418[nm],0.000027027134 +Spectral Data 419[nm],0.000028330132 +Spectral Data 420[nm],0.000029966144 +Spectral Data 421[nm],0.000031945645 +Spectral Data 422[nm],0.000034267265 +Spectral Data 423[nm],0.000036904559 +Spectral Data 424[nm],0.000039828374 +Spectral Data 425[nm],0.000043010186 +Spectral Data 426[nm],0.000046453275 +Spectral Data 427[nm],0.000050200390 +Spectral Data 428[nm],0.000054296306 +Spectral Data 429[nm],0.000058792350 +Spectral Data 430[nm],0.000063819272 +Spectral Data 431[nm],0.000069569738 +Spectral Data 432[nm],0.000076238801 +Spectral Data 433[nm],0.000084002051 +Spectral Data 434[nm],0.000092899616 +Spectral Data 435[nm],0.000102907434 +Spectral Data 436[nm],0.000114000723 +Spectral Data 437[nm],0.000126147745 +Spectral Data 438[nm],0.000139350668 +Spectral Data 439[nm],0.000153605943 +Spectral Data 440[nm],0.000168909683 +Spectral Data 441[nm],0.000185196404 +Spectral Data 442[nm],0.000202212090 +Spectral Data 443[nm],0.000219666821 +Spectral Data 444[nm],0.000237270768 +Spectral Data 445[nm],0.000254752871 +Spectral Data 446[nm],0.000271882804 +Spectral Data 447[nm],0.000288435520 +Spectral Data 448[nm],0.000304183195 +Spectral Data 449[nm],0.000318816456 +Spectral Data 450[nm],0.000331902935 +Spectral Data 451[nm],0.000342996238 +Spectral Data 452[nm],0.000351659779 +Spectral Data 453[nm],0.000357679965 +Spectral Data 454[nm],0.000361089711 +Spectral Data 455[nm],0.000361937127 +Spectral Data 456[nm],0.000360277918 +Spectral Data 457[nm],0.000356289936 +Spectral Data 458[nm],0.000350250222 +Spectral Data 459[nm],0.000342438580 +Spectral Data 460[nm],0.000333143020 +Spectral Data 461[nm],0.000322732056 +Spectral Data 462[nm],0.000311622134 +Spectral Data 463[nm],0.000300230284 +Spectral Data 464[nm],0.000288942829 +Spectral Data 465[nm],0.000277946005 +Spectral Data 466[nm],0.000267342635 +Spectral Data 467[nm],0.000257235020 +Spectral Data 468[nm],0.000247702759 +Spectral Data 469[nm],0.000238719338 +Spectral Data 470[nm],0.000230227481 +Spectral Data 471[nm],0.000222169925 +Spectral Data 472[nm],0.000214497733 +Spectral Data 473[nm],0.000207189034 +Spectral Data 474[nm],0.000200227427 +Spectral Data 475[nm],0.000193596818 +Spectral Data 476[nm],0.000187307058 +Spectral Data 477[nm],0.000181425072 +Spectral Data 478[nm],0.000176026821 +Spectral Data 479[nm],0.000171187712 +Spectral Data 480[nm],0.000166981976 +Spectral Data 481[nm],0.000163483521 +Spectral Data 482[nm],0.000160765063 +Spectral Data 483[nm],0.000158896932 +Spectral Data 484[nm],0.000157875169 +Spectral Data 485[nm],0.000157608956 +Spectral Data 486[nm],0.000158002527 +Spectral Data 487[nm],0.000158960844 +Spectral Data 488[nm],0.000160401178 +Spectral Data 489[nm],0.000162251439 +Spectral Data 490[nm],0.000164439844 +Spectral Data 491[nm],0.000166898695 +Spectral Data 492[nm],0.000169602441 +Spectral Data 493[nm],0.000172551969 +Spectral Data 494[nm],0.000175748704 +Spectral Data 495[nm],0.000179197523 +Spectral Data 496[nm],0.000182933160 +Spectral Data 497[nm],0.000187002632 +Spectral Data 498[nm],0.000191452826 +Spectral Data 499[nm],0.000196314068 +Spectral Data 500[nm],0.000201534538 +Spectral Data 501[nm],0.000207037185 +Spectral Data 502[nm],0.000212744897 +Spectral Data 503[nm],0.000218581801 +Spectral Data 504[nm],0.000224477379 +Spectral Data 505[nm],0.000230361940 +Spectral Data 506[nm],0.000236165870 +Spectral Data 507[nm],0.000241834379 +Spectral Data 508[nm],0.000247346645 +Spectral Data 509[nm],0.000252687139 +Spectral Data 510[nm],0.000257840526 +Spectral Data 511[nm],0.000262814428 +Spectral Data 512[nm],0.000267655065 +Spectral Data 513[nm],0.000272412435 +Spectral Data 514[nm],0.000277135783 +Spectral Data 515[nm],0.000281845685 +Spectral Data 516[nm],0.000286527647 +Spectral Data 517[nm],0.000291164964 +Spectral Data 518[nm],0.000295740523 +Spectral Data 519[nm],0.000300232059 +Spectral Data 520[nm],0.000304612651 +Spectral Data 521[nm],0.000308855029 +Spectral Data 522[nm],0.000312933233 +Spectral Data 523[nm],0.000316833000 +Spectral Data 524[nm],0.000320547697 +Spectral Data 525[nm],0.000324070978 +Spectral Data 526[nm],0.000327409187 +Spectral Data 527[nm],0.000330665527 +Spectral Data 528[nm],0.000333987991 +Spectral Data 529[nm],0.000337524747 +Spectral Data 530[nm],0.000341368344 +Spectral Data 531[nm],0.000345327600 +Spectral Data 532[nm],0.000349117006 +Spectral Data 533[nm],0.000352450879 +Spectral Data 534[nm],0.000355126103 +Spectral Data 535[nm],0.000357231562 +Spectral Data 536[nm],0.000358921068 +Spectral Data 537[nm],0.000360348407 +Spectral Data 538[nm],0.000361620390 +Spectral Data 539[nm],0.000362726772 +Spectral Data 540[nm],0.000363639323 +Spectral Data 541[nm],0.000364331092 +Spectral Data 542[nm],0.000364891835 +Spectral Data 543[nm],0.000365620159 +Spectral Data 544[nm],0.000366836379 +Spectral Data 545[nm],0.000368854904 +Spectral Data 546[nm],0.000371746690 +Spectral Data 547[nm],0.000375265605 +Spectral Data 548[nm],0.000379145116 +Spectral Data 549[nm],0.000383122213 +Spectral Data 550[nm],0.000387050648 +Spectral Data 551[nm],0.000390928035 +Spectral Data 552[nm],0.000394761097 +Spectral Data 553[nm],0.000398556062 +Spectral Data 554[nm],0.000402294856 +Spectral Data 555[nm],0.000405925355 +Spectral Data 556[nm],0.000409392873 +Spectral Data 557[nm],0.000412643829 +Spectral Data 558[nm],0.000415688555 +Spectral Data 559[nm],0.000418639625 +Spectral Data 560[nm],0.000421619130 +Spectral Data 561[nm],0.000424748578 +Spectral Data 562[nm],0.000428094878 +Spectral Data 563[nm],0.000431627472 +Spectral Data 564[nm],0.000435305585 +Spectral Data 565[nm],0.000439088471 +Spectral Data 566[nm],0.000442934950 +Spectral Data 567[nm],0.000446803198 +Spectral Data 568[nm],0.000450651161 +Spectral Data 569[nm],0.000454437046 +Spectral Data 570[nm],0.000458150520 +Spectral Data 571[nm],0.000461855903 +Spectral Data 572[nm],0.000465628196 +Spectral Data 573[nm],0.000469542429 +Spectral Data 574[nm],0.000473651045 +Spectral Data 575[nm],0.000477944268 +Spectral Data 576[nm],0.000482402043 +Spectral Data 577[nm],0.000487004407 +Spectral Data 578[nm],0.000491718296 +Spectral Data 579[nm],0.000496469554 +Spectral Data 580[nm],0.000501176575 +Spectral Data 581[nm],0.000505757635 +Spectral Data 582[nm],0.000510152080 +Spectral Data 583[nm],0.000514372950 +Spectral Data 584[nm],0.000518449873 +Spectral Data 585[nm],0.000522412360 +Spectral Data 586[nm],0.000526284566 +Spectral Data 587[nm],0.000530070101 +Spectral Data 588[nm],0.000533766986 +Spectral Data 589[nm],0.000537373126 +Spectral Data 590[nm],0.000540883630 +Spectral Data 591[nm],0.000544285693 +Spectral Data 592[nm],0.000547563192 +Spectral Data 593[nm],0.000550700177 +Spectral Data 594[nm],0.000553691818 +Spectral Data 595[nm],0.000556585495 +Spectral Data 596[nm],0.000559442851 +Spectral Data 597[nm],0.000562325818 +Spectral Data 598[nm],0.000565279392 +Spectral Data 599[nm],0.000568273535 +Spectral Data 600[nm],0.000571256795 +Spectral Data 601[nm],0.000574177830 +Spectral Data 602[nm],0.000576974649 +Spectral Data 603[nm],0.000579536776 +Spectral Data 604[nm],0.000581740285 +Spectral Data 605[nm],0.000583461253 +Spectral Data 606[nm],0.000584599038 +Spectral Data 607[nm],0.000585157890 +Spectral Data 608[nm],0.000585171976 +Spectral Data 609[nm],0.000584675174 +Spectral Data 610[nm],0.000583703280 +Spectral Data 611[nm],0.000582299544 +Spectral Data 612[nm],0.000580509542 +Spectral Data 613[nm],0.000578378676 +Spectral Data 614[nm],0.000575953862 +Spectral Data 615[nm],0.000573287893 +Spectral Data 616[nm],0.000570435368 +Spectral Data 617[nm],0.000567450887 +Spectral Data 618[nm],0.000564369780 +Spectral Data 619[nm],0.000561140885 +Spectral Data 620[nm],0.000557688472 +Spectral Data 621[nm],0.000553937047 +Spectral Data 622[nm],0.000549851626 +Spectral Data 623[nm],0.000545581162 +Spectral Data 624[nm],0.000541326357 +Spectral Data 625[nm],0.000537287910 +Spectral Data 626[nm],0.000533593295 +Spectral Data 627[nm],0.000530039892 +Spectral Data 628[nm],0.000526331889 +Spectral Data 629[nm],0.000522173534 +Spectral Data 630[nm],0.000517328095 +Spectral Data 631[nm],0.000511825143 +Spectral Data 632[nm],0.000505769160 +Spectral Data 633[nm],0.000499264686 +Spectral Data 634[nm],0.000492379884 +Spectral Data 635[nm],0.000485043478 +Spectral Data 636[nm],0.000477139401 +Spectral Data 637[nm],0.000468551356 +Spectral Data 638[nm],0.000459251489 +Spectral Data 639[nm],0.000449585932 +Spectral Data 640[nm],0.000439994939 +Spectral Data 641[nm],0.000430918619 +Spectral Data 642[nm],0.000422663987 +Spectral Data 643[nm],0.000415024377 +Spectral Data 644[nm],0.000407667656 +Spectral Data 645[nm],0.000400261633 +Spectral Data 646[nm],0.000392578833 +Spectral Data 647[nm],0.000384767627 +Spectral Data 648[nm],0.000377058517 +Spectral Data 649[nm],0.000369681919 +Spectral Data 650[nm],0.000362766819 +Spectral Data 651[nm],0.000356107164 +Spectral Data 652[nm],0.000349425798 +Spectral Data 653[nm],0.000342445448 +Spectral Data 654[nm],0.000335026474 +Spectral Data 655[nm],0.000327456160 +Spectral Data 656[nm],0.000320101273 +Spectral Data 657[nm],0.000313328317 +Spectral Data 658[nm],0.000307335460 +Spectral Data 659[nm],0.000301838503 +Spectral Data 660[nm],0.000296465587 +Spectral Data 661[nm],0.000290844997 +Spectral Data 662[nm],0.000284782291 +Spectral Data 663[nm],0.000278556399 +Spectral Data 664[nm],0.000272522098 +Spectral Data 665[nm],0.000267032796 +Spectral Data 666[nm],0.000262254383 +Spectral Data 667[nm],0.000257897831 +Spectral Data 668[nm],0.000253598962 +Spectral Data 669[nm],0.000248999364 +Spectral Data 670[nm],0.000243966802 +Spectral Data 671[nm],0.000238797031 +Spectral Data 672[nm],0.000233855622 +Spectral Data 673[nm],0.000229498852 +Spectral Data 674[nm],0.000225782627 +Spectral Data 675[nm],0.000222411400 +Spectral Data 676[nm],0.000219070076 +Spectral Data 677[nm],0.000215468172 +Spectral Data 678[nm],0.000211623279 +Spectral Data 679[nm],0.000207766803 +Spectral Data 680[nm],0.000204134776 +Spectral Data 681[nm],0.000200916242 +Spectral Data 682[nm],0.000197999922 +Spectral Data 683[nm],0.000195158325 +Spectral Data 684[nm],0.000192163920 +Spectral Data 685[nm],0.000188884194 +Spectral Data 686[nm],0.000185509256 +Spectral Data 687[nm],0.000182299933 +Spectral Data 688[nm],0.000179515657 +Spectral Data 689[nm],0.000177253518 +Spectral Data 690[nm],0.000175304012 +Spectral Data 691[nm],0.000173423585 +Spectral Data 692[nm],0.000171374879 +Spectral Data 693[nm],0.000169089981 +Spectral Data 694[nm],0.000166684200 +Spectral Data 695[nm],0.000164281839 +Spectral Data 696[nm],0.000161995718 +Spectral Data 697[nm],0.000159809686 +Spectral Data 698[nm],0.000157624905 +Spectral Data 699[nm],0.000155341069 +Spectral Data 700[nm],0.000152887544 +Spectral Data 701[nm],0.000150368738 +Spectral Data 702[nm],0.000147950719 +Spectral Data 703[nm],0.000145799495 +Spectral Data 704[nm],0.000143992351 +Spectral Data 705[nm],0.000142327044 +Spectral Data 706[nm],0.000140546414 +Spectral Data 707[nm],0.000138393327 +Spectral Data 708[nm],0.000135762792 +Spectral Data 709[nm],0.000132830304 +Spectral Data 710[nm],0.000129795619 +Spectral Data 711[nm],0.000126856787 +Spectral Data 712[nm],0.000124101163 +Spectral Data 713[nm],0.000121442732 +Spectral Data 714[nm],0.000118780568 +Spectral Data 715[nm],0.000116016025 +Spectral Data 716[nm],0.000113144888 +Spectral Data 717[nm],0.000110295317 +Spectral Data 718[nm],0.000107605832 +Spectral Data 719[nm],0.000105211519 +Spectral Data 720[nm],0.000103122693 +Spectral Data 721[nm],0.000101195699 +Spectral Data 722[nm],0.000099277633 +Spectral Data 723[nm],0.000097221695 +Spectral Data 724[nm],0.000095040108 +Spectral Data 725[nm],0.000092921349 +Spectral Data 726[nm],0.000091063630 +Spectral Data 727[nm],0.000089657653 +Spectral Data 728[nm],0.000088729350 +Spectral Data 729[nm],0.000088144145 +Spectral Data 730[nm],0.000087760782 +Spectral Data 731[nm],0.000087439126 +Spectral Data 732[nm],0.000087065731 +Spectral Data 733[nm],0.000086550441 +Spectral Data 734[nm],0.000085803600 +Spectral Data 735[nm],0.000084741441 +Spectral Data 736[nm],0.000083366656 +Spectral Data 737[nm],0.000081748578 +Spectral Data 738[nm],0.000079958285 +Spectral Data 739[nm],0.000078067504 +Spectral Data 740[nm],0.000076152413 +Spectral Data 741[nm],0.000074292504 +Spectral Data 742[nm],0.000072567469 +Spectral Data 743[nm],0.000071058574 +Spectral Data 744[nm],0.000069874128 +Spectral Data 745[nm],0.000069137976 +Spectral Data 746[nm],0.000068973786 +Spectral Data 747[nm],0.000069459609 +Spectral Data 748[nm],0.000070268186 +Spectral Data 749[nm],0.000070849754 +Spectral Data 750[nm],0.000070651688 +Spectral Data 751[nm],0.000069174901 +Spectral Data 752[nm],0.000066329500 +Spectral Data 753[nm],0.000062221166 +Spectral Data 754[nm],0.000056957157 +Spectral Data 755[nm],0.000050740506 +Spectral Data 756[nm],0.000044398927 +Spectral Data 757[nm],0.000039030732 +Spectral Data 758[nm],0.000035736208 +Spectral Data 759[nm],0.000035360736 +Spectral Data 760[nm],0.000037219921 +Spectral Data 761[nm],0.000040070787 +Spectral Data 762[nm],0.000042669857 +Spectral Data 763[nm],0.000043976099 +Spectral Data 764[nm],0.000044012377 +Spectral Data 765[nm],0.000043148953 +Spectral Data 766[nm],0.000041756259 +Spectral Data 767[nm],0.000040175455 +Spectral Data 768[nm],0.000038621456 +Spectral Data 769[nm],0.000037272272 +Spectral Data 770[nm],0.000036305886 +Spectral Data 771[nm],0.000035866044 +Spectral Data 772[nm],0.000035955240 +Spectral Data 773[nm],0.000036541740 +Spectral Data 774[nm],0.000037593938 +Spectral Data 775[nm],0.000038985072 +Spectral Data 776[nm],0.000040247214 +Spectral Data 777[nm],0.000040842820 +Spectral Data 778[nm],0.000040234852 +Spectral Data 779[nm],0.000038216305 +Spectral Data 780[nm],0.000035575547 + +TM-30 Color Vector Graphic,Reference Illuminant x,Reference Illuminant y,Measured Illuminant x,Measured Illuminant y +bin1,0.9764469,0.2157578,0.8882475,0.2021859 +bin2,0.7906278,0.6122971,0.7113284,0.6248878 +bin3,0.5509713,0.8345242,0.4676899,0.8666077 +bin4,0.1428891,0.9897387,0.0935279,1.002316 +bin5,-0.176162,0.9843612,-0.2043247,0.9795201 +bin6,-0.5853095,0.81081,-0.5838909,0.8375309 +bin7,-0.7960986,0.6051669,-0.7457092,0.6149487 +bin8,-0.951027,0.309108,-0.9191595,0.309686 +bin9,-0.9854512,-0.1699584,-0.9329426,-0.2097975 +bin10,-0.8461911,-0.5328795,-0.7660208,-0.6001526 +bin11,-0.5824577,-0.812861,-0.4902966,-0.8897363 +bin12,-0.2939128,-0.9558322,-0.2872024,-1.03006 +bin13,0.1462545,-0.989247,0.1026697,-1.040349 +bin14,0.508388,-0.8611281,0.4397461,-0.9682071 +bin15,0.8469644,-0.5316497,0.7729813,-0.6153884 +bin16,0.9788596,-0.2045332,0.9110764,-0.2976203 diff --git a/colour/io/tests/resources/colorchecker_n_ohta_transposed.csv b/colour/io/tests/resources/colorchecker_n_ohta_transposed.csv new file mode 100644 index 0000000000..aee1f4440a --- /dev/null +++ b/colour/io/tests/resources/colorchecker_n_ohta_transposed.csv @@ -0,0 +1,25 @@ +wavelength 380 385 390 395 400 405 410 415 420 425 430 435 440 445 450 455 460 465 470 475 480 485 490 495 500 505 510 515 520 525 530 535 540 545 550 555 560 565 570 575 580 585 590 595 600 605 610 615 620 625 630 635 640 645 650 655 660 665 670 675 680 685 690 695 700 705 710 715 720 725 730 735 740 745 750 755 760 765 770 775 780 +1 0.048 0.051 0.055 0.06 0.065 0.068 0.068 0.067 0.064 0.062 0.059 0.057 0.055 0.054 0.053 0.053 0.052 0.052 0.052 0.053 0.054 0.055 0.057 0.059 0.061 0.062 0.065 0.067 0.07 0.072 0.074 0.075 0.076 0.078 0.079 0.082 0.087 0.092 0.1 0.107 0.115 0.122 0.129 0.134 0.138 0.142 0.146 0.15 0.154 0.158 0.163 0.167 0.173 0.18 0.188 0.196 0.204 0.213 0.222 0.231 0.242 0.251 0.261 0.271 0.282 0.294 0.305 0.318 0.334 0.354 0.372 0.392 0.409 0.42 0.436 0.45 0.462 0.465 0.448 0.432 0.421 +2 0.103 0.12 0.141 0.163 0.182 0.192 0.197 0.199 0.201 0.203 0.205 0.208 0.212 0.217 0.224 0.231 0.24 0.251 0.262 0.273 0.282 0.289 0.293 0.296 0.301 0.31 0.321 0.326 0.322 0.31 0.298 0.291 0.292 0.297 0.3 0.298 0.295 0.295 0.305 0.326 0.358 0.397 0.435 0.468 0.494 0.514 0.53 0.541 0.55 0.557 0.564 0.569 0.574 0.582 0.59 0.597 0.605 0.614 0.624 0.637 0.652 0.668 0.682 0.697 0.713 0.728 0.745 0.753 0.762 0.774 0.783 0.788 0.791 0.787 0.789 0.794 0.801 0.799 0.771 0.747 0.734 +3 0.113 0.138 0.174 0.219 0.266 0.3 0.32 0.33 0.336 0.337 0.337 0.337 0.335 0.334 0.331 0.327 0.322 0.316 0.31 0.302 0.293 0.285 0.276 0.268 0.26 0.251 0.243 0.234 0.225 0.215 0.208 0.203 0.198 0.195 0.191 0.188 0.183 0.177 0.172 0.167 0.163 0.16 0.157 0.153 0.15 0.147 0.144 0.141 0.137 0.133 0.13 0.126 0.123 0.12 0.118 0.115 0.112 0.11 0.108 0.106 0.105 0.104 0.104 0.103 0.103 0.102 0.102 0.102 0.102 0.102 0.102 0.104 0.104 0.104 0.104 0.106 0.106 0.107 0.11 0.115 0.12 +4 0.048 0.049 0.049 0.049 0.05 0.049 0.049 0.05 0.05 0.051 0.052 0.053 0.054 0.056 0.058 0.06 0.061 0.063 0.064 0.065 0.067 0.068 0.07 0.072 0.078 0.088 0.106 0.13 0.155 0.173 0.181 0.182 0.177 0.168 0.157 0.147 0.137 0.129 0.126 0.125 0.122 0.119 0.115 0.109 0.104 0.1 0.098 0.097 0.098 0.1 0.1 0.099 0.097 0.096 0.095 0.095 0.095 0.097 0.101 0.11 0.125 0.147 0.174 0.21 0.247 0.283 0.311 0.329 0.343 0.353 0.358 0.362 0.364 0.36 0.362 0.364 0.368 0.368 0.355 0.346 0.341 +5 0.123 0.152 0.197 0.258 0.328 0.385 0.418 0.437 0.446 0.448 0.448 0.447 0.444 0.44 0.434 0.428 0.421 0.413 0.405 0.394 0.381 0.372 0.362 0.352 0.342 0.33 0.314 0.294 0.271 0.249 0.231 0.219 0.211 0.209 0.209 0.207 0.201 0.196 0.196 0.199 0.206 0.215 0.223 0.229 0.235 0.241 0.245 0.245 0.243 0.243 0.247 0.254 0.269 0.291 0.318 0.351 0.384 0.417 0.446 0.47 0.49 0.504 0.511 0.517 0.52 0.522 0.523 0.522 0.521 0.521 0.522 0.521 0.521 0.516 0.514 0.514 0.517 0.515 0.5 0.491 0.487 +6 0.11 0.133 0.167 0.208 0.252 0.284 0.303 0.314 0.322 0.329 0.336 0.344 0.353 0.363 0.375 0.39 0.408 0.433 0.46 0.492 0.523 0.548 0.566 0.577 0.582 0.583 0.58 0.576 0.569 0.56 0.549 0.535 0.519 0.501 0.48 0.458 0.436 0.414 0.392 0.369 0.346 0.324 0.302 0.279 0.26 0.245 0.234 0.226 0.221 0.217 0.215 0.212 0.21 0.209 0.208 0.209 0.211 0.215 0.22 0.227 0.233 0.239 0.244 0.249 0.252 0.252 0.25 0.248 0.244 0.245 0.245 0.251 0.26 0.269 0.278 0.288 0.297 0.301 0.297 0.296 0.296 +7 0.053 0.054 0.054 0.054 0.054 0.054 0.053 0.053 0.052 0.052 0.052 0.052 0.052 0.052 0.052 0.052 0.052 0.052 0.053 0.054 0.055 0.056 0.057 0.059 0.061 0.064 0.068 0.076 0.086 0.101 0.12 0.143 0.17 0.198 0.228 0.26 0.297 0.338 0.38 0.418 0.452 0.481 0.503 0.52 0.532 0.543 0.552 0.56 0.566 0.572 0.578 0.583 0.587 0.593 0.599 0.602 0.604 0.606 0.608 0.611 0.615 0.619 0.622 0.625 0.628 0.63 0.633 0.633 0.633 0.636 0.637 0.639 0.638 0.633 0.633 0.636 0.641 0.639 0.616 0.598 0.582 +8 0.099 0.12 0.15 0.189 0.231 0.268 0.293 0.311 0.324 0.335 0.348 0.361 0.373 0.383 0.387 0.383 0.374 0.361 0.345 0.325 0.301 0.275 0.247 0.223 0.202 0.184 0.167 0.152 0.137 0.125 0.116 0.11 0.106 0.103 0.099 0.094 0.09 0.086 0.083 0.083 0.083 0.085 0.086 0.087 0.087 0.086 0.085 0.084 0.084 0.085 0.088 0.092 0.098 0.105 0.111 0.118 0.123 0.126 0.126 0.124 0.12 0.117 0.115 0.115 0.116 0.118 0.12 0.124 0.128 0.133 0.139 0.149 0.162 0.178 0.197 0.219 0.242 0.259 0.275 0.294 0.316 +9 0.096 0.108 0.123 0.135 0.144 0.145 0.144 0.141 0.138 0.134 0.132 0.132 0.131 0.131 0.129 0.128 0.126 0.126 0.125 0.123 0.119 0.114 0.109 0.105 0.103 0.102 0.1 0.097 0.094 0.091 0.089 0.09 0.092 0.096 0.102 0.106 0.108 0.109 0.112 0.126 0.157 0.208 0.274 0.346 0.415 0.473 0.517 0.547 0.567 0.582 0.591 0.597 0.601 0.604 0.607 0.608 0.607 0.606 0.605 0.605 0.605 0.604 0.605 0.606 0.606 0.604 0.602 0.601 0.599 0.598 0.596 0.595 0.593 0.587 0.584 0.584 0.586 0.584 0.566 0.551 0.54 +10 0.101 0.115 0.135 0.157 0.177 0.191 0.199 0.203 0.206 0.198 0.19 0.179 0.168 0.156 0.144 0.132 0.12 0.11 0.101 0.093 0.086 0.08 0.075 0.07 0.067 0.063 0.061 0.059 0.058 0.056 0.054 0.053 0.052 0.052 0.053 0.054 0.055 0.055 0.054 0.053 0.052 0.052 0.053 0.055 0.059 0.065 0.074 0.086 0.099 0.113 0.126 0.138 0.149 0.161 0.172 0.182 0.193 0.205 0.217 0.232 0.248 0.266 0.282 0.301 0.319 0.338 0.355 0.371 0.388 0.406 0.422 0.436 0.451 0.46 0.471 0.481 0.492 0.495 0.482 0.471 0.467 +11 0.056 0.058 0.059 0.059 0.06 0.061 0.061 0.061 0.062 0.063 0.064 0.066 0.068 0.071 0.075 0.079 0.085 0.093 0.104 0.118 0.135 0.157 0.185 0.221 0.269 0.326 0.384 0.44 0.484 0.516 0.534 0.542 0.545 0.541 0.533 0.524 0.513 0.501 0.487 0.472 0.454 0.436 0.416 0.394 0.374 0.358 0.346 0.337 0.331 0.328 0.325 0.322 0.32 0.319 0.319 0.32 0.324 0.33 0.337 0.345 0.354 0.362 0.368 0.375 0.379 0.381 0.379 0.376 0.373 0.372 0.375 0.382 0.392 0.401 0.412 0.422 0.433 0.436 0.426 0.413 0.404 +12 0.06 0.061 0.063 0.064 0.065 0.065 0.064 0.064 0.064 0.064 0.064 0.065 0.065 0.066 0.067 0.068 0.069 0.073 0.077 0.084 0.092 0.1 0.107 0.115 0.123 0.133 0.146 0.166 0.193 0.229 0.273 0.323 0.374 0.418 0.456 0.487 0.512 0.534 0.554 0.57 0.584 0.598 0.609 0.617 0.624 0.63 0.635 0.64 0.645 0.65 0.654 0.658 0.662 0.667 0.672 0.675 0.676 0.677 0.678 0.681 0.685 0.688 0.69 0.693 0.696 0.698 0.698 0.698 0.698 0.7 0.701 0.701 0.701 0.695 0.694 0.696 0.7 0.698 0.673 0.653 0.639 +13 0.069 0.081 0.096 0.114 0.136 0.156 0.175 0.193 0.208 0.224 0.244 0.265 0.29 0.316 0.335 0.342 0.338 0.324 0.302 0.273 0.239 0.205 0.172 0.144 0.12 0.101 0.086 0.074 0.066 0.059 0.054 0.051 0.048 0.046 0.045 0.044 0.043 0.042 0.041 0.041 0.04 0.04 0.04 0.04 0.039 0.039 0.04 0.04 0.04 0.04 0.041 0.041 0.042 0.042 0.042 0.043 0.043 0.043 0.044 0.044 0.044 0.044 0.045 0.046 0.048 0.05 0.051 0.053 0.056 0.06 0.064 0.07 0.079 0.091 0.104 0.12 0.138 0.154 0.168 0.186 0.204 +14 0.055 0.056 0.057 0.058 0.058 0.058 0.059 0.059 0.059 0.06 0.062 0.063 0.065 0.067 0.07 0.074 0.078 0.084 0.091 0.101 0.113 0.125 0.14 0.157 0.18 0.208 0.244 0.286 0.324 0.351 0.363 0.363 0.355 0.342 0.323 0.303 0.281 0.26 0.238 0.217 0.196 0.177 0.158 0.14 0.124 0.111 0.101 0.094 0.089 0.086 0.084 0.082 0.08 0.078 0.077 0.076 0.075 0.075 0.075 0.077 0.078 0.08 0.082 0.085 0.088 0.089 0.089 0.09 0.09 0.09 0.089 0.092 0.094 0.097 0.102 0.106 0.11 0.111 0.112 0.112 0.112 +15 0.052 0.052 0.052 0.052 0.051 0.051 0.05 0.05 0.049 0.049 0.049 0.049 0.049 0.049 0.049 0.048 0.048 0.047 0.047 0.046 0.045 0.045 0.044 0.044 0.044 0.044 0.044 0.044 0.044 0.044 0.044 0.044 0.045 0.046 0.047 0.048 0.05 0.053 0.057 0.063 0.072 0.086 0.109 0.143 0.192 0.256 0.332 0.413 0.486 0.55 0.598 0.631 0.654 0.672 0.686 0.694 0.7 0.704 0.707 0.712 0.718 0.721 0.724 0.727 0.729 0.73 0.73 0.729 0.727 0.728 0.729 0.729 0.727 0.723 0.721 0.724 0.728 0.727 0.702 0.68 0.664 +16 0.054 0.053 0.054 0.053 0.053 0.053 0.053 0.052 0.052 0.052 0.053 0.053 0.053 0.054 0.055 0.056 0.059 0.065 0.075 0.093 0.121 0.157 0.202 0.252 0.303 0.351 0.394 0.436 0.475 0.512 0.544 0.572 0.597 0.615 0.63 0.645 0.66 0.673 0.686 0.698 0.708 0.718 0.726 0.732 0.737 0.742 0.746 0.749 0.753 0.757 0.761 0.765 0.768 0.772 0.777 0.779 0.78 0.78 0.781 0.782 0.785 0.785 0.787 0.789 0.792 0.792 0.793 0.792 0.79 0.792 0.792 0.79 0.787 0.782 0.778 0.78 0.782 0.781 0.752 0.728 0.71 +17 0.118 0.142 0.179 0.228 0.283 0.322 0.343 0.354 0.359 0.357 0.35 0.339 0.327 0.313 0.298 0.282 0.267 0.253 0.239 0.225 0.209 0.195 0.182 0.172 0.163 0.155 0.146 0.135 0.124 0.113 0.106 0.102 0.102 0.105 0.107 0.107 0.106 0.107 0.112 0.123 0.141 0.166 0.198 0.235 0.279 0.333 0.394 0.46 0.522 0.58 0.628 0.666 0.696 0.722 0.742 0.756 0.766 0.774 0.78 0.785 0.791 0.794 0.798 0.801 0.804 0.806 0.807 0.807 0.807 0.81 0.813 0.814 0.813 0.81 0.808 0.811 0.814 0.813 0.785 0.765 0.752 +18 0.093 0.11 0.134 0.164 0.195 0.22 0.238 0.249 0.258 0.27 0.281 0.296 0.315 0.334 0.352 0.37 0.391 0.414 0.434 0.449 0.458 0.461 0.457 0.447 0.433 0.414 0.392 0.366 0.339 0.31 0.282 0.255 0.228 0.204 0.18 0.159 0.141 0.126 0.114 0.104 0.097 0.092 0.088 0.083 0.08 0.077 0.075 0.074 0.073 0.073 0.073 0.073 0.073 0.073 0.074 0.075 0.076 0.076 0.077 0.076 0.075 0.074 0.074 0.073 0.072 0.072 0.071 0.073 0.075 0.078 0.082 0.09 0.1 0.116 0.133 0.154 0.176 0.191 0.2 0.208 0.214 +19 0.153 0.189 0.245 0.319 0.409 0.536 0.671 0.772 0.84 0.868 0.878 0.882 0.883 0.885 0.886 0.886 0.887 0.888 0.888 0.888 0.888 0.888 0.888 0.888 0.887 0.887 0.887 0.887 0.887 0.887 0.887 0.887 0.887 0.886 0.886 0.887 0.887 0.887 0.888 0.888 0.887 0.886 0.886 0.886 0.887 0.888 0.889 0.89 0.891 0.891 0.891 0.891 0.89 0.889 0.889 0.889 0.889 0.889 0.888 0.888 0.888 0.888 0.888 0.888 0.888 0.887 0.886 0.886 0.886 0.885 0.885 0.885 0.884 0.884 0.883 0.882 0.882 0.881 0.88 0.88 0.879 +20 0.15 0.184 0.235 0.299 0.372 0.459 0.529 0.564 0.58 0.584 0.585 0.587 0.587 0.588 0.588 0.587 0.586 0.585 0.583 0.582 0.581 0.58 0.58 0.58 0.58 0.58 0.58 0.581 0.581 0.582 0.582 0.582 0.583 0.583 0.583 0.584 0.584 0.585 0.586 0.587 0.588 0.588 0.588 0.588 0.588 0.587 0.586 0.586 0.585 0.584 0.583 0.581 0.58 0.579 0.578 0.577 0.576 0.575 0.574 0.573 0.572 0.571 0.57 0.569 0.568 0.567 0.566 0.565 0.564 0.562 0.562 0.56 0.56 0.558 0.557 0.556 0.555 0.554 0.553 0.551 0.55 +21 0.138 0.167 0.206 0.249 0.289 0.324 0.346 0.354 0.357 0.358 0.359 0.36 0.361 0.362 0.362 0.361 0.361 0.359 0.358 0.358 0.357 0.356 0.356 0.356 0.356 0.356 0.356 0.356 0.357 0.357 0.357 0.358 0.358 0.358 0.358 0.358 0.359 0.359 0.36 0.361 0.361 0.361 0.361 0.361 0.36 0.36 0.359 0.358 0.357 0.356 0.355 0.354 0.353 0.352 0.351 0.35 0.349 0.348 0.346 0.346 0.345 0.344 0.343 0.342 0.341 0.34 0.339 0.338 0.337 0.336 0.335 0.334 0.333 0.332 0.331 0.33 0.329 0.328 0.327 0.326 0.325 +22 0.113 0.131 0.15 0.169 0.183 0.193 0.199 0.201 0.202 0.203 0.203 0.204 0.205 0.205 0.205 0.205 0.204 0.204 0.203 0.203 0.202 0.202 0.202 0.202 0.202 0.202 0.202 0.202 0.202 0.202 0.203 0.203 0.203 0.203 0.203 0.203 0.203 0.203 0.204 0.204 0.205 0.205 0.205 0.205 0.204 0.204 0.204 0.203 0.203 0.202 0.201 0.201 0.2 0.199 0.198 0.198 0.197 0.197 0.196 0.195 0.195 0.194 0.194 0.193 0.192 0.192 0.191 0.191 0.19 0.189 0.189 0.188 0.188 0.187 0.187 0.186 0.185 0.185 0.184 0.184 0.183 +23 0.074 0.079 0.084 0.088 0.091 0.093 0.094 0.094 0.094 0.094 0.094 0.095 0.095 0.095 0.095 0.094 0.094 0.094 0.094 0.093 0.093 0.093 0.093 0.092 0.092 0.093 0.093 0.093 0.093 0.093 0.093 0.093 0.093 0.093 0.093 0.092 0.093 0.093 0.093 0.093 0.093 0.093 0.093 0.092 0.092 0.092 0.092 0.091 0.091 0.091 0.09 0.09 0.09 0.09 0.089 0.089 0.089 0.088 0.088 0.088 0.087 0.087 0.087 0.087 0.086 0.086 0.086 0.086 0.085 0.085 0.085 0.085 0.085 0.084 0.084 0.084 0.084 0.084 0.083 0.083 0.083 +24 0.032 0.033 0.033 0.034 0.035 0.035 0.036 0.036 0.036 0.036 0.036 0.036 0.035 0.035 0.035 0.035 0.035 0.035 0.035 0.035 0.034 0.034 0.034 0.034 0.034 0.034 0.034 0.034 0.034 0.034 0.034 0.034 0.034 0.034 0.034 0.034 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.033 0.032 0.032 0.032 0.032 0.032 0.032 0.032 0.032 0.032 0.032 0.032 0.032 0.032 0.032 0.032 0.032 0.032 0.032 0.032 \ No newline at end of file diff --git a/colour/io/tests/resources/config-aces-reference.ocio.yaml b/colour/io/tests/resources/config-aces-reference.ocio.yaml new file mode 100644 index 0000000000..52169e293d --- /dev/null +++ b/colour/io/tests/resources/config-aces-reference.ocio.yaml @@ -0,0 +1,587 @@ +ocio_profile_version: 2.1 + +environment: + {} +search_path: "" +strictparsing: true +luma: [0.2126, 0.7152, 0.0722] +description: | + The "Academy Color Encoding System" (ACES) "Reference Config". + + This "OpenColorIO" config is a strict and quasi-analytical implementation of "aces-dev" and is designed as a reference for software developers. It is not a replacement for the previous "ACES" configs nor the "ACES Studio Config". + + Generated with "OpenColorIO-Config-ACES" v0.1.1-5-g48ca436 on the 2021/07/17 at 15:31. + +roles: + aces_interchange: ACES - ACES2065-1 + cie_xyz_d65_interchange: CIE-XYZ-D65 + color_timing: ACES - ACEScct + compositing_log: ACES - ACEScct + data: Utility - Raw + default: ACES - ACES2065-1 + reference: ACES - ACES2065-1 + rendering: ACES - ACEScg + scene_linear: ACES - ACEScg + +file_rules: + - ! {name: Default, colorspace: ACES - ACES2065-1} + +shared_views: + - ! {name: Output - SDR Video - ACES 1.0, view_transform: Output - SDR Video - ACES 1.0, display_colorspace: } + - ! {name: Output - SDR Video (D60 sim on D65) - ACES 1.0, view_transform: Output - SDR Video (D60 sim on D65) - ACES 1.0, display_colorspace: } + - ! {name: Output - SDR Video (P3 lim) - ACES 1.1, view_transform: Output - SDR Video (P3 lim) - ACES 1.1, display_colorspace: } + - ! {name: Output - SDR Video (Rec.709 lim) - ACES 1.1, view_transform: Output - SDR Video (Rec.709 lim) - ACES 1.1, display_colorspace: } + - ! {name: Output - HDR Video (1000 nits & Rec.2020 lim) - ACES 1.1, view_transform: Output - HDR Video (1000 nits & Rec.2020 lim) - ACES 1.1, display_colorspace: } + - ! {name: Output - HDR Video (2000 nits & Rec.2020 lim) - ACES 1.1, view_transform: Output - HDR Video (2000 nits & Rec.2020 lim) - ACES 1.1, display_colorspace: } + - ! {name: Output - HDR Video (4000 nits & Rec.2020 lim) - ACES 1.1, view_transform: Output - HDR Video (4000 nits & Rec.2020 lim) - ACES 1.1, display_colorspace: } + - ! {name: Output - HDR Video (1000 nits & P3 lim) - ACES 1.1, view_transform: Output - HDR Video (1000 nits & P3 lim) - ACES 1.1, display_colorspace: } + - ! {name: Output - HDR Video (2000 nits & P3 lim) - ACES 1.1, view_transform: Output - HDR Video (2000 nits & P3 lim) - ACES 1.1, display_colorspace: } + - ! {name: Output - HDR Video (4000 nits & P3 lim) - ACES 1.1, view_transform: Output - HDR Video (4000 nits & P3 lim) - ACES 1.1, display_colorspace: } + - ! {name: Output - SDR Cinema - ACES 1.0, view_transform: Output - SDR Cinema - ACES 1.0, display_colorspace: } + - ! {name: Output - SDR Cinema (D60 sim on D65) - ACES 1.1, view_transform: Output - SDR Cinema (D60 sim on D65) - ACES 1.1, display_colorspace: } + - ! {name: Output - SDR Cinema (Rec.709 lim) - ACES 1.1, view_transform: Output - SDR Cinema (Rec.709 lim) - ACES 1.1, display_colorspace: } + - ! {name: Output - SDR Cinema (D60 sim on DCI) - ACES 1.0, view_transform: Output - SDR Cinema (D60 sim on DCI) - ACES 1.0, display_colorspace: } + - ! {name: Output - SDR Cinema (D65 sim on DCI) - ACES 1.1, view_transform: Output - SDR Cinema (D65 sim on DCI) - ACES 1.1, display_colorspace: } + - ! {name: Output - HDR Cinema (108 nits & P3 lim) - ACES 1.1, view_transform: Output - HDR Cinema (108 nits & P3 lim) - ACES 1.1, display_colorspace: } + - ! {name: Un-tone-mapped, view_transform: Un-tone-mapped, display_colorspace: } + +displays: + Display - sRGB: + - ! {name: Raw, colorspace: Utility - Raw} + - ! [Output - SDR Video - ACES 1.0, Output - SDR Video (D60 sim on D65) - ACES 1.0, Un-tone-mapped] + Display - Rec.1886 / Rec.709 Video: + - ! {name: Raw, colorspace: Utility - Raw} + - ! [Output - SDR Video - ACES 1.0, Output - SDR Video (D60 sim on D65) - ACES 1.0, Un-tone-mapped] + Display - Rec.1886 / Rec.2020 Video: + - ! {name: Raw, colorspace: Utility - Raw} + - ! [Output - SDR Video - ACES 1.0, Output - SDR Video (P3 lim) - ACES 1.1, Output - SDR Video (Rec.709 lim) - ACES 1.1, Un-tone-mapped] + Display - Rec.2100-HLG: + - ! {name: Raw, colorspace: Utility - Raw} + - ! [Output - HDR Video (1000 nits & Rec.2020 lim) - ACES 1.1, Un-tone-mapped] + Display - Rec.2100-PQ: + - ! {name: Raw, colorspace: Utility - Raw} + - ! [Output - HDR Video (1000 nits & Rec.2020 lim) - ACES 1.1, Output - HDR Video (2000 nits & Rec.2020 lim) - ACES 1.1, Output - HDR Video (4000 nits & Rec.2020 lim) - ACES 1.1, Un-tone-mapped] + Display - ST2084-P3-D65: + - ! {name: Raw, colorspace: Utility - Raw} + - ! [Output - HDR Video (1000 nits & P3 lim) - ACES 1.1, Output - HDR Video (2000 nits & P3 lim) - ACES 1.1, Output - HDR Video (4000 nits & P3 lim) - ACES 1.1, Output - HDR Cinema (108 nits & P3 lim) - ACES 1.1, Un-tone-mapped] + Display - P3-D60: + - ! {name: Raw, colorspace: Utility - Raw} + - ! [Output - SDR Cinema - ACES 1.0, Un-tone-mapped] + Display - P3-D65: + - ! {name: Raw, colorspace: Utility - Raw} + - ! [Output - SDR Cinema - ACES 1.0, Output - SDR Cinema (D60 sim on D65) - ACES 1.1, Output - SDR Cinema (Rec.709 lim) - ACES 1.1, Un-tone-mapped] + Display - P3-DCI: + - ! {name: Raw, colorspace: Utility - Raw} + - ! [Output - SDR Cinema (D60 sim on DCI) - ACES 1.0, Output - SDR Cinema (D65 sim on DCI) - ACES 1.1, Un-tone-mapped] + +active_displays: [Display - sRGB, Display - Rec.1886 / Rec.709 Video, Display - Rec.1886 / Rec.2020 Video, Display - Rec.2100-HLG, Display - Rec.2100-PQ, Display - ST2084-P3-D65, Display - P3-D60, Display - P3-D65, Display - P3-DCI] +active_views: [Output - SDR Video - ACES 1.0, Output - SDR Video (D60 sim on D65) - ACES 1.0, Output - SDR Video (P3 lim) - ACES 1.1, Output - SDR Video (Rec.709 lim) - ACES 1.1, Output - HDR Video (1000 nits & Rec.2020 lim) - ACES 1.1, Output - HDR Video (2000 nits & Rec.2020 lim) - ACES 1.1, Output - HDR Video (4000 nits & Rec.2020 lim) - ACES 1.1, Output - HDR Video (1000 nits & P3 lim) - ACES 1.1, Output - HDR Video (2000 nits & P3 lim) - ACES 1.1, Output - HDR Video (4000 nits & P3 lim) - ACES 1.1, Output - SDR Cinema - ACES 1.0, Output - SDR Cinema (D60 sim on D65) - ACES 1.1, Output - SDR Cinema (Rec.709 lim) - ACES 1.1, Output - SDR Cinema (D60 sim on DCI) - ACES 1.0, Output - SDR Cinema (D65 sim on DCI) - ACES 1.1, Output - HDR Cinema (108 nits & P3 lim) - ACES 1.1, Raw] +inactive_colorspaces: [CIE-XYZ-D65] + +looks: + - ! + name: LMT - Blue Light Artifact Fix + process_space: ACES - ACES2065-1 + description: | + LMT for desaturating blue hues to reduce clipping artifacts + + ACEStransformID: urn:ampas:aces:transformId:v1.5:LMT.Academy.BlueLightArtifactFix.a1.1.0 + transform: ! {style: ACES-LMT - BLUE_LIGHT_ARTIFACT_FIX} + + - ! + name: LMT - ACES 1.3 Reference Gamut Compression + process_space: ACES - ACES2065-1 + description: | + LMT (applied in ACES2065-1) to compress scene-referred values from common cameras into the AP1 gamut + + ACEStransformID: urn:ampas:aces:transformId:v1.5:LMT.Academy.GamutCompress.a1.3.0 + transform: ! {style: ACES-LMT - ACES 1.3 Reference Gamut Compression} + + +default_view_transform: Un-tone-mapped + +view_transforms: + - ! + name: Output - SDR Video - ACES 1.0 + description: | + Component of ACES Output Transforms for SDR D65 video + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ODT.Academy.RGBmonitor_100nits_dim.a1.0.3 + ACEStransformID: urn:ampas:aces:transformId:v1.5:ODT.Academy.Rec709_100nits_dim.a1.0.3 + ACEStransformID: urn:ampas:aces:transformId:v1.5:ODT.Academy.Rec2020_100nits_dim.a1.0.3 + from_scene_reference: ! {style: ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-VIDEO_1.0} + + - ! + name: Output - SDR Video (D60 sim on D65) - ACES 1.0 + description: | + Component of ACES Output Transforms for SDR D65 video simulating D60 white + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ODT.Academy.RGBmonitor_D60sim_100nits_dim.a1.0.3 + ACEStransformID: urn:ampas:aces:transformId:v1.5:ODT.Academy.Rec709_D60sim_100nits_dim.a1.0.3 + from_scene_reference: ! {style: ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-VIDEO-D60sim-D65_1.0} + + - ! + name: Output - SDR Video (P3 lim) - ACES 1.1 + description: | + Component of ACES Output Transforms for SDR D65 video + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ODT.Academy.Rec2020_P3D65limited_100nits_dim.a1.1.0 + from_scene_reference: ! {style: ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-VIDEO-P3lim_1.1} + + - ! + name: Output - SDR Video (Rec.709 lim) - ACES 1.1 + description: | + Component of ACES Output Transforms for SDR D65 video + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ODT.Academy.Rec2020_Rec709limited_100nits_dim.a1.1.0 + from_scene_reference: ! {style: ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-VIDEO-REC709lim_1.1} + + - ! + name: Output - HDR Video (1000 nits & Rec.2020 lim) - ACES 1.1 + description: | + Component of ACES Output Transforms for 1000 nit HDR D65 video + + ACEStransformID: urn:ampas:aces:transformId:v1.5:RRTODT.Academy.Rec2020_1000nits_15nits_HLG.a1.1.0 + ACEStransformID: urn:ampas:aces:transformId:v1.5:RRTODT.Academy.Rec2020_1000nits_15nits_ST2084.a1.1.0 + from_scene_reference: ! {style: ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-VIDEO-1000nit-15nit-REC2020lim_1.1} + + - ! + name: Output - HDR Video (2000 nits & Rec.2020 lim) - ACES 1.1 + description: | + Component of ACES Output Transforms for 2000 nit HDR D65 video + + ACEStransformID: urn:ampas:aces:transformId:v1.5:RRTODT.Academy.Rec2020_2000nits_15nits_ST2084.a1.1.0 + from_scene_reference: ! {style: ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-VIDEO-2000nit-15nit-REC2020lim_1.1} + + - ! + name: Output - HDR Video (4000 nits & Rec.2020 lim) - ACES 1.1 + description: | + Component of ACES Output Transforms for 4000 nit HDR D65 video + + ACEStransformID: urn:ampas:aces:transformId:v1.5:RRTODT.Academy.Rec2020_4000nits_15nits_ST2084.a1.1.0 + from_scene_reference: ! {style: ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-VIDEO-4000nit-15nit-REC2020lim_1.1} + + - ! + name: Output - HDR Video (1000 nits & P3 lim) - ACES 1.1 + description: | + Component of ACES Output Transforms for 1000 nit HDR D65 video + + ACEStransformID: urn:ampas:aces:transformId:v1.5:RRTODT.Academy.P3D65_1000nits_15nits_ST2084.a1.1.0 + from_scene_reference: ! {style: ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-VIDEO-1000nit-15nit-P3lim_1.1} + + - ! + name: Output - HDR Video (2000 nits & P3 lim) - ACES 1.1 + description: | + Component of ACES Output Transforms for 2000 nit HDR D65 video + + ACEStransformID: urn:ampas:aces:transformId:v1.5:RRTODT.Academy.P3D65_2000nits_15nits_ST2084.a1.1.0 + from_scene_reference: ! {style: ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-VIDEO-2000nit-15nit-P3lim_1.1} + + - ! + name: Output - HDR Video (4000 nits & P3 lim) - ACES 1.1 + description: | + Component of ACES Output Transforms for 4000 nit HDR D65 video + + ACEStransformID: urn:ampas:aces:transformId:v1.5:RRTODT.Academy.P3D65_4000nits_15nits_ST2084.a1.1.0 + from_scene_reference: ! {style: ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-VIDEO-4000nit-15nit-P3lim_1.1} + + - ! + name: Output - SDR Cinema - ACES 1.0 + description: | + Component of ACES Output Transforms for SDR cinema + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ODT.Academy.P3D60_48nits.a1.0.3 + ACEStransformID: urn:ampas:aces:transformId:v1.5:ODT.Academy.P3D65_48nits.a1.1.0 + from_scene_reference: ! {style: ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-CINEMA_1.0} + + - ! + name: Output - SDR Cinema (D60 sim on D65) - ACES 1.1 + description: | + Component of ACES Output Transforms for SDR D65 cinema simulating D60 white + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ODT.Academy.P3D65_D60sim_48nits.a1.1.0 + from_scene_reference: ! {style: ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-CINEMA-D60sim-D65_1.1} + + - ! + name: Output - SDR Cinema (Rec.709 lim) - ACES 1.1 + description: | + Component of ACES Output Transforms for SDR cinema + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ODT.Academy.P3D65_Rec709limited_48nits.a1.1.0 + from_scene_reference: ! {style: ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-CINEMA-REC709lim_1.1} + + - ! + name: Output - SDR Cinema (D60 sim on DCI) - ACES 1.0 + description: | + Component of ACES Output Transforms for SDR DCI cinema simulating D60 white + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ODT.Academy.P3DCI_48nits.a1.0.3 + from_scene_reference: ! {style: ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-CINEMA-D60sim-DCI_1.0} + + - ! + name: Output - SDR Cinema (D65 sim on DCI) - ACES 1.1 + description: | + Component of ACES Output Transforms for SDR DCI cinema simulating D65 white + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ODT.Academy.P3DCI_D65sim_48nits.a1.1.0 + from_scene_reference: ! {style: ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-CINEMA-D65sim-DCI_1.1} + + - ! + name: Output - HDR Cinema (108 nits & P3 lim) - ACES 1.1 + description: | + Component of ACES Output Transforms for 108 nit HDR D65 cinema + + ACEStransformID: urn:ampas:aces:transformId:v1.5:RRTODT.Academy.P3D65_108nits_7point2nits_ST2084.a1.1.0 + from_scene_reference: ! {style: ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-CINEMA-108nit-7.2nit-P3lim_1.1} + + - ! + name: Un-tone-mapped + from_scene_reference: ! {style: UTILITY - ACES-AP0_to_CIE-XYZ-D65_BFD} + +display_colorspaces: + - ! + name: CIE-XYZ-D65 + family: "" + equalitygroup: "" + bitdepth: 32f + description: The "CIE XYZ (D65)" display connection colorspace. + isdata: false + allocation: uniform + + - ! + name: Display - sRGB + family: Display + equalitygroup: "" + bitdepth: 32f + description: Convert CIE XYZ (D65 white) to sRGB (piecewise EOTF) + isdata: false + categories: [file-io] + encoding: sdr-video + allocation: uniform + from_display_reference: ! {style: DISPLAY - CIE-XYZ-D65_to_sRGB} + + - ! + name: Display - Rec.1886 / Rec.709 Video + family: Display + equalitygroup: "" + bitdepth: 32f + description: Convert CIE XYZ (D65 white) to Rec.1886/Rec.709 (HD video) + isdata: false + categories: [file-io] + encoding: sdr-video + allocation: uniform + from_display_reference: ! {style: DISPLAY - CIE-XYZ-D65_to_REC.1886-REC.709} + + - ! + name: Display - Rec.1886 / Rec.2020 Video + family: Display + equalitygroup: "" + bitdepth: 32f + description: Convert CIE XYZ (D65 white) to Rec.1886/Rec.2020 (UHD video) + isdata: false + categories: [file-io] + encoding: sdr-video + allocation: uniform + from_display_reference: ! {style: DISPLAY - CIE-XYZ-D65_to_REC.1886-REC.2020} + + - ! + name: Display - Rec.2100-HLG + family: Display + equalitygroup: "" + bitdepth: 32f + description: Convert CIE XYZ (D65 white) to Rec.2100-HLG, 1000 nit + isdata: false + categories: [file-io] + encoding: hdr-video + allocation: uniform + from_display_reference: ! {style: DISPLAY - CIE-XYZ-D65_to_REC.2100-HLG-1000nit} + + - ! + name: Display - Rec.2100-PQ + family: Display + equalitygroup: "" + bitdepth: 32f + description: Convert CIE XYZ (D65 white) to Rec.2100-PQ + isdata: false + categories: [file-io] + encoding: hdr-video + allocation: uniform + from_display_reference: ! {style: DISPLAY - CIE-XYZ-D65_to_REC.2100-PQ} + + - ! + name: Display - ST2084-P3-D65 + family: Display + equalitygroup: "" + bitdepth: 32f + description: Convert CIE XYZ (D65 white) to ST-2084 (PQ), P3-D65 primaries + isdata: false + categories: [file-io] + encoding: sdr-video + allocation: uniform + from_display_reference: ! {style: DISPLAY - CIE-XYZ-D65_to_ST2084-P3-D65} + + - ! + name: Display - P3-D60 + family: Display + equalitygroup: "" + bitdepth: 32f + description: Convert CIE XYZ (D65 white) to Gamma 2.6, P3-D60 (Bradford adaptation) + isdata: false + categories: [file-io] + encoding: sdr-video + allocation: uniform + from_display_reference: ! {style: DISPLAY - CIE-XYZ-D65_to_G2.6-P3-D60-BFD} + + - ! + name: Display - P3-D65 + family: Display + equalitygroup: "" + bitdepth: 32f + description: Convert CIE XYZ (D65 white) to Gamma 2.6, P3-D65 + isdata: false + categories: [file-io] + encoding: sdr-video + allocation: uniform + from_display_reference: ! {style: DISPLAY - CIE-XYZ-D65_to_G2.6-P3-D65} + + - ! + name: Display - P3-DCI + family: Display + equalitygroup: "" + bitdepth: 32f + description: Convert CIE XYZ (D65 white) to Gamma 2.6, P3-DCI (DCI white with Bradford adaptation) + isdata: false + categories: [file-io] + encoding: sdr-video + allocation: uniform + from_display_reference: ! {style: DISPLAY - CIE-XYZ-D65_to_G2.6-P3-DCI-BFD} + +colorspaces: + - ! + name: ACES - ACES2065-1 + family: ACES + equalitygroup: "" + bitdepth: 32f + description: The "Academy Color Encoding System" reference colorspace. + isdata: false + encoding: scene-linear + allocation: uniform + + - ! + name: Utility - Raw + family: Utility + equalitygroup: "" + bitdepth: 32f + description: The utility "Raw" colorspace. + isdata: true + allocation: uniform + + - ! + name: ACES - ACEScc + family: ACES + equalitygroup: "" + bitdepth: 32f + description: | + Convert ACEScc to ACES2065-1 + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.ACEScc_to_ACES.a1.0.3 + isdata: false + categories: [file-io] + encoding: log + allocation: uniform + to_scene_reference: ! {style: ACEScc_to_ACES2065-1} + + - ! + name: ACES - ACEScct + family: ACES + equalitygroup: "" + bitdepth: 32f + description: | + Convert ACEScct to ACES2065-1 + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.ACEScct_to_ACES.a1.0.3 + isdata: false + categories: [file-io, working-space] + encoding: log + allocation: uniform + to_scene_reference: ! {style: ACEScct_to_ACES2065-1} + + - ! + name: ACES - ACEScg + family: ACES + equalitygroup: "" + bitdepth: 32f + description: | + Convert ACEScg to ACES2065-1 + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.ACEScg_to_ACES.a1.0.3 + isdata: false + categories: [file-io, working-space] + encoding: scene-linear + allocation: uniform + to_scene_reference: ! {style: ACEScg_to_ACES2065-1} + + - ! + name: ACES - ADX10 + family: ACES + equalitygroup: "" + bitdepth: 32f + description: | + Convert ADX10 to ACES2065-1 + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.ADX10_to_ACES.a1.0.3 + isdata: false + categories: [file-io] + encoding: log + allocation: uniform + to_scene_reference: ! {style: ADX10_to_ACES2065-1} + + - ! + name: ACES - ADX16 + family: ACES + equalitygroup: "" + bitdepth: 32f + description: | + Convert ADX16 to ACES2065-1 + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.ADX16_to_ACES.a1.0.3 + isdata: false + categories: [file-io] + encoding: log + allocation: uniform + to_scene_reference: ! {style: ADX16_to_ACES2065-1} + + - ! + name: Input - Canon - CLog2 CGamut + family: Input/Canon + equalitygroup: "" + bitdepth: 32f + description: | + Convert Canon Log 2 Cinema Gamut to ACES2065-1 + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.CLog2_CGamut_to_ACES.a1.1.0 + isdata: false + categories: [file-io] + encoding: log + allocation: uniform + to_scene_reference: ! {style: CANON_CLOG2-CGAMUT_to_ACES2065-1} + + - ! + name: Input - Canon - CLog3 CGamut + family: Input/Canon + equalitygroup: "" + bitdepth: 32f + description: | + Convert Canon Log 3 Cinema Gamut to ACES2065-1 + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.CLog3_CGamut_to_ACES.a1.1.0 + isdata: false + categories: [file-io] + encoding: log + allocation: uniform + to_scene_reference: ! {style: CANON_CLOG3-CGAMUT_to_ACES2065-1} + + - ! + name: Input - RED - Log3G10 RWG + family: Input/RED + equalitygroup: "" + bitdepth: 32f + description: | + Convert RED Log3G10 RED Wide Gamut to ACES2065-1 + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.Log3G10_RWG_to_ACES.a1.1.0 + isdata: false + categories: [file-io] + encoding: log + allocation: uniform + to_scene_reference: ! {style: RED_LOG3G10-RWG_to_ACES2065-1} + + - ! + name: Input - ARRI - LogC EI800 AWG + family: Input/ARRI + equalitygroup: "" + bitdepth: 32f + description: | + Convert ARRI ALEXA LogC (EI800) ALEXA Wide Gamut to ACES2065-1 + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.LogC_EI800_AWG_to_ACES.a1.1.0 + isdata: false + categories: [file-io] + encoding: log + allocation: uniform + to_scene_reference: ! {style: ARRI_ALEXA-LOGC-EI800-AWG_to_ACES2065-1} + + - ! + name: Input - Sony - SLog3 SGamut3 + family: Input/Sony + equalitygroup: "" + bitdepth: 32f + description: | + Convert Sony S-Log3 S-Gamut3 to ACES2065-1 + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.SLog3_SGamut3_to_ACES.a1.1.0 + isdata: false + categories: [file-io] + encoding: log + allocation: uniform + to_scene_reference: ! {style: SONY_SLOG3-SGAMUT3_to_ACES2065-1} + + - ! + name: Input - Sony - SLog3 SGamut3Cine + family: Input/Sony + equalitygroup: "" + bitdepth: 32f + description: | + Convert Sony S-Log3 S-Gamut3.Cine to ACES2065-1 + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.SLog3_SGamut3Cine_to_ACES.a1.1.0 + isdata: false + categories: [file-io] + encoding: log + allocation: uniform + to_scene_reference: ! {style: SONY_SLOG3-SGAMUT3.CINE_to_ACES2065-1} + + - ! + name: Input - Sony - SLog3 Venice SGamut3 + family: Input/Sony + equalitygroup: "" + bitdepth: 32f + description: | + Convert Sony S-Log3 S-Gamut3 for the Venice camera to ACES2065-1 + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.SLog3_Venice_SGamut3_to_ACES.a1.1.0 + isdata: false + categories: [file-io] + encoding: log + allocation: uniform + to_scene_reference: ! {style: SONY_SLOG3-SGAMUT3-VENICE_to_ACES2065-1} + + - ! + name: Input - Sony - SLog3 Venice SGamut3Cine + family: Input/Sony + equalitygroup: "" + bitdepth: 32f + description: | + Convert Sony S-Log3 S-Gamut3.Cine for the Venice camera to ACES2065-1 + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.SLog3_Venice_SGamut3Cine_to_ACES.a1.1.0 + isdata: false + categories: [file-io] + encoding: log + allocation: uniform + to_scene_reference: ! {style: SONY_SLOG3-SGAMUT3.CINE-VENICE_to_ACES2065-1} + + - ! + name: Input - Panasonic - VLog VGamut + family: Input/Panasonic + equalitygroup: "" + bitdepth: 32f + description: | + Convert Panasonic Varicam V-Log V-Gamut to ACES2065-1 + + ACEStransformID: urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.VLog_VGamut_to_ACES.a1.1.0 + isdata: false + categories: [file-io] + encoding: log + allocation: uniform + to_scene_reference: ! {style: PANASONIC_VLOG-VGAMUT_to_ACES2065-1} \ No newline at end of file diff --git a/colour/io/tests/resources/uprtek.xls.txt b/colour/io/tests/resources/uprtek.xls.txt new file mode 100644 index 0000000000..17a2c64275 --- /dev/null +++ b/colour/io/tests/resources/uprtek.xls.txt @@ -0,0 +1,441 @@ +Model Name CV600 +Serial Number 19J00789 +Time 2021/01/04_23:14:46 +Memo +LUX 695.154907 +fc 64.605476 +CCT 5198.000000 +Duv -0.000620 +I-Time 12000.000000 +X 682.470886 +Y 695.154907 +Z 631.635071 +x 0.339663 +y 0.345975 +u' 0.209915 +v' 0.481087 +LambdaP 456.000000 +LambdaPValue 18.404581 +CRI 92.956993 +R1 91.651062 +R2 93.014732 +R3 97.032013 +R4 93.513229 +R5 92.482590 +R6 91.486870 +R7 93.016129 +R8 91.459312 +R9 77.613075 +R10 86.981613 +R11 94.841324 +R12 74.139542 +R13 91.073837 +R14 97.064323 +R15 88.615669 +TLCI 97.495056 +TLMF-A 1.270032 +SSI-A 44.881924 +Rf 87.234917 +Rg 98.510712 +IRR 2.607891 +380nm 0.030267 +381nm 0.030267 +382nm 0.030267 +383nm 0.029822 +384nm 0.028978 +385nm 0.028623 +386nm 0.030845 +387nm 0.035596 +388nm 0.039231 +389nm 0.039064 +390nm 0.035223 +391nm 0.031580 +392nm 0.029181 +393nm 0.027808 +394nm 0.026256 +395nm 0.024526 +396nm 0.022557 +397nm 0.020419 +398nm 0.018521 +399nm 0.018149 +400nm 0.019325 +401nm 0.021666 +402nm 0.024045 +403nm 0.026473 +404nm 0.029076 +405nm 0.031840 +406nm 0.033884 +407nm 0.034038 +408nm 0.032302 +409nm 0.030383 +410nm 0.029426 +411nm 0.029979 +412nm 0.032614 +413nm 0.037204 +414nm 0.042279 +415nm 0.046029 +416nm 0.048698 +417nm 0.053064 +418nm 0.059530 +419nm 0.070840 +420nm 0.087678 +421nm 0.110043 +422nm 0.136705 +423nm 0.165180 +424nm 0.199071 +425nm 0.241976 +426nm 0.293837 +427nm 0.359177 +428nm 0.434192 +429nm 0.523828 +430nm 0.632578 +431nm 0.758893 +432nm 0.915528 +433nm 1.096489 +434nm 1.307487 +435nm 1.557125 +436nm 1.838779 +437nm 2.183382 +438nm 2.586251 +439nm 3.054022 +440nm 3.625659 +441nm 4.279538 +442nm 5.055838 +443nm 5.919301 +444nm 6.869926 +445nm 7.940298 +446nm 9.090219 +447nm 10.336670 +448nm 11.619895 +449nm 12.939739 +450nm 14.206918 +451nm 15.396660 +452nm 16.430536 +453nm 17.267374 +454nm 17.912292 +455nm 18.261185 +456nm 18.404581 +457nm 18.288025 +458nm 18.002302 +459nm 17.570372 +460nm 17.011297 +461nm 16.411137 +462nm 15.779440 +463nm 15.168951 +464nm 14.585364 +465nm 14.057872 +466nm 13.575768 +467nm 13.144953 +468nm 12.737307 +469nm 12.346188 +470nm 11.967313 +471nm 11.590308 +472nm 11.209807 +473nm 10.815372 +474nm 10.406748 +475nm 10.007284 +476nm 9.627886 +477nm 9.279286 +478nm 8.958391 +479nm 8.663115 +480nm 8.427362 +481nm 8.238759 +482nm 8.110200 +483nm 8.011048 +484nm 7.939125 +485nm 7.900343 +486nm 7.880703 +487nm 7.887271 +488nm 7.907047 +489nm 7.939895 +490nm 7.977298 +491nm 8.013443 +492nm 8.056756 +493nm 8.112617 +494nm 8.181398 +495nm 8.256148 +496nm 8.332609 +497nm 8.418014 +498nm 8.513148 +499nm 8.616785 +500nm 8.719036 +501nm 8.817776 +502nm 8.914417 +503nm 9.011255 +504nm 9.105255 +505nm 9.193217 +506nm 9.274889 +507nm 9.350751 +508nm 9.423820 +509nm 9.490992 +510nm 9.553215 +511nm 9.608335 +512nm 9.653841 +513nm 9.691347 +514nm 9.727146 +515nm 9.767722 +516nm 9.809064 +517nm 9.842565 +518nm 9.867527 +519nm 9.887219 +520nm 9.906105 +521nm 9.920433 +522nm 9.929304 +523nm 9.932856 +524nm 9.935204 +525nm 9.937991 +526nm 9.938448 +527nm 9.936127 +528nm 9.930192 +529nm 9.922665 +530nm 9.913944 +531nm 9.905774 +532nm 9.898767 +533nm 9.894219 +534nm 9.891479 +535nm 9.883711 +536nm 9.862693 +537nm 9.829168 +538nm 9.795257 +539nm 9.767633 +540nm 9.747380 +541nm 9.729669 +542nm 9.714886 +543nm 9.701355 +544nm 9.688311 +545nm 9.673670 +546nm 9.657027 +547nm 9.633310 +548nm 9.603127 +549nm 9.567823 +550nm 9.534049 +551nm 9.504526 +552nm 9.484178 +553nm 9.471739 +554nm 9.455969 +555nm 9.429557 +556nm 9.396450 +557nm 9.368848 +558nm 9.344832 +559nm 9.313942 +560nm 9.273922 +561nm 9.240767 +562nm 9.220987 +563nm 9.210749 +564nm 9.195800 +565nm 9.173392 +566nm 9.143906 +567nm 9.109710 +568nm 9.078232 +569nm 9.052593 +570nm 9.023234 +571nm 8.984895 +572nm 8.950663 +573nm 8.935179 +574nm 8.936305 +575nm 8.937272 +576nm 8.931671 +577nm 8.921451 +578nm 8.910289 +579nm 8.908619 +580nm 8.917888 +581nm 8.934530 +582nm 8.946784 +583nm 8.958764 +584nm 8.979334 +585nm 9.007913 +586nm 9.033543 +587nm 9.051113 +588nm 9.067842 +589nm 9.089899 +590nm 9.114546 +591nm 9.136106 +592nm 9.164270 +593nm 9.207536 +594nm 9.264211 +595nm 9.321528 +596nm 9.371778 +597nm 9.411209 +598nm 9.443729 +599nm 9.490623 +600nm 9.557871 +601nm 9.626752 +602nm 9.674832 +603nm 9.705856 +604nm 9.739429 +605nm 9.784062 +606nm 9.841268 +607nm 9.907084 +608nm 9.971845 +609nm 10.026823 +610nm 10.060076 +611nm 10.076903 +612nm 10.105914 +613nm 10.161287 +614nm 10.230108 +615nm 10.285982 +616nm 10.336598 +617nm 10.396016 +618nm 10.449015 +619nm 10.478296 +620nm 10.484620 +621nm 10.487537 +622nm 10.498996 +623nm 10.519572 +624nm 10.541495 +625nm 10.549863 +626nm 10.543288 +627nm 10.538241 +628nm 10.546865 +629nm 10.560687 +630nm 10.567954 +631nm 10.564369 +632nm 10.555919 +633nm 10.542054 +634nm 10.527417 +635nm 10.513332 +636nm 10.500641 +637nm 10.493341 +638nm 10.491714 +639nm 10.477033 +640nm 10.435987 +641nm 10.374922 +642nm 10.317416 +643nm 10.269583 +644nm 10.220937 +645nm 10.168004 +646nm 10.115719 +647nm 10.061740 +648nm 9.998492 +649nm 9.919030 +650nm 9.821223 +651nm 9.716800 +652nm 9.619915 +653nm 9.531602 +654nm 9.435769 +655nm 9.326644 +656nm 9.215940 +657nm 9.111384 +658nm 9.005102 +659nm 8.892046 +660nm 8.775783 +661nm 8.659118 +662nm 8.537835 +663nm 8.413469 +664nm 8.292587 +665nm 8.175849 +666nm 8.055606 +667nm 7.931369 +668nm 7.812479 +669nm 7.695505 +670nm 7.564718 +671nm 7.422195 +672nm 7.286375 +673nm 7.166087 +674nm 7.050159 +675nm 6.925609 +676nm 6.792675 +677nm 6.659946 +678nm 6.534333 +679nm 6.416044 +680nm 6.298086 +681nm 6.182296 +682nm 6.073105 +683nm 5.965933 +684nm 5.853682 +685nm 5.729931 +686nm 5.599877 +687nm 5.480670 +688nm 5.376213 +689nm 5.273221 +690nm 5.156234 +691nm 5.027091 +692nm 4.900242 +693nm 4.777046 +694nm 4.658288 +695nm 4.547010 +696nm 4.443560 +697nm 4.347722 +698nm 4.252159 +699nm 4.152643 +700nm 4.053906 +701nm 3.961853 +702nm 3.865061 +703nm 3.755302 +704nm 3.634861 +705nm 3.519360 +706nm 3.418803 +707nm 3.328571 +708nm 3.246458 +709nm 3.160225 +710nm 3.066386 +711nm 2.970290 +712nm 2.878098 +713nm 2.790311 +714nm 2.701265 +715nm 2.607646 +716nm 2.515490 +717nm 2.435313 +718nm 2.361505 +719nm 2.282271 +720nm 2.192500 +721nm 2.101594 +722nm 2.027356 +723nm 1.966553 +724nm 1.912948 +725nm 1.855193 +726nm 1.785138 +727nm 1.710667 +728nm 1.638785 +729nm 1.582385 +730nm 1.539228 +731nm 1.498548 +732nm 1.455407 +733nm 1.413034 +734nm 1.372021 +735nm 1.324772 +736nm 1.277157 +737nm 1.238888 +738nm 1.211113 +739nm 1.182541 +740nm 1.149382 +741nm 1.118490 +742nm 1.091204 +743nm 1.065539 +744nm 1.039564 +745nm 1.013148 +746nm 0.990818 +747nm 0.976522 +748nm 0.960074 +749nm 0.935639 +750nm 0.905095 +751nm 0.878893 +752nm 0.862828 +753nm 0.847588 +754nm 0.829938 +755nm 0.808772 +756nm 0.786338 +757nm 0.761752 +758nm 0.735873 +759nm 0.711232 +760nm 0.690947 +761nm 0.673476 +762nm 0.659236 +763nm 0.646735 +764nm 0.633802 +765nm 0.612864 +766nm 0.589102 +767nm 0.567989 +768nm 0.551288 +769nm 0.533479 +770nm 0.508426 +771nm 0.487143 +772nm 0.474126 +773nm 0.465145 +774nm 0.455158 +775nm 0.442994 +776nm 0.429114 +777nm 0.419402 +778nm 0.411766 +779nm 0.411766 +780nm 0.411766 diff --git a/colour/io/tests/test_image.py b/colour/io/tests/test_image.py index f2dc54aae7..d276187aa3 100644 --- a/colour/io/tests/test_image.py +++ b/colour/io/tests/test_image.py @@ -1,13 +1,9 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.io.image` module. -""" +"""Defines the unit tests for the :mod:`colour.io.image` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import os -import platform import shutil import unittest import tempfile @@ -17,194 +13,291 @@ from colour.io import read_image_Imageio, write_image_Imageio from colour.io import read_image, write_image from colour.io import ImageAttribute_Specification -from colour.utilities import is_openimageio_installed +from colour.utilities import attest, is_openimageio_installed -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'RESOURCES_DIRECTORY', 'TestReadImageOpenImageIO', - 'TestWriteImageOpenImageIO', 'TestReadImageImageio', - 'TestWriteImageImageio', 'TestReadImage', 'TestWriteImage' + "RESOURCES_DIRECTORY", + "TestReadImageOpenImageIO", + "TestWriteImageOpenImageIO", + "TestReadImageImageio", + "TestWriteImageImageio", + "TestReadImage", + "TestWriteImage", ] -RESOURCES_DIRECTORY = os.path.join(os.path.dirname(__file__), 'resources') +RESOURCES_DIRECTORY: str = os.path.join(os.path.dirname(__file__), "resources") class TestConvertBitDepth(unittest.TestCase): """ - Defines :func:`colour.io.image.convert_bit_depth` definition units tests + Define :func:`colour.io.image.convert_bit_depth` definition unit tests methods. """ def test_convert_bit_depth(self): - """ - Tests :func:`colour.io.image.convert_bit_depth` definition. - """ + """Test :func:`colour.io.image.convert_bit_depth` definition.""" - a = np.around(np.linspace(0, 1, 10) * 255).astype('uint8') - self.assertIs(convert_bit_depth(a, 'uint8').dtype, np.dtype('uint8')) - np.testing.assert_equal(convert_bit_depth(a, 'uint8'), a) + a = np.around(np.linspace(0, 1, 10) * 255).astype("uint8") + self.assertIs(convert_bit_depth(a, "uint8").dtype, np.dtype("uint8")) + np.testing.assert_equal(convert_bit_depth(a, "uint8"), a) - self.assertIs(convert_bit_depth(a, 'uint16').dtype, np.dtype('uint16')) + self.assertIs(convert_bit_depth(a, "uint16").dtype, np.dtype("uint16")) np.testing.assert_equal( - convert_bit_depth(a, 'uint16'), - np.array([ - 0, 7196, 14649, 21845, 29041, 36494, 43690, 50886, 58339, 65535 - ])) + convert_bit_depth(a, "uint16"), + np.array( + [ + 0, + 7196, + 14649, + 21845, + 29041, + 36494, + 43690, + 50886, + 58339, + 65535, + ] + ), + ) self.assertIs( - convert_bit_depth(a, 'float16').dtype, np.dtype('float16')) + convert_bit_depth(a, "float16").dtype, np.dtype("float16") + ) np.testing.assert_almost_equal( - convert_bit_depth(a, 'float16'), - np.array([ - 0.0000, 0.1098, 0.2235, 0.3333, 0.443, 0.5566, 0.6665, 0.7764, - 0.8900, 1.0000 - ]), - decimal=3) + convert_bit_depth(a, "float16"), + np.array( + [ + 0.0000, + 0.1098, + 0.2235, + 0.3333, + 0.443, + 0.5566, + 0.6665, + 0.7764, + 0.8900, + 1.0000, + ] + ), + decimal=3, + ) self.assertIs( - convert_bit_depth(a, 'float32').dtype, np.dtype('float32')) + convert_bit_depth(a, "float32").dtype, np.dtype("float32") + ) np.testing.assert_almost_equal( - convert_bit_depth(a, 'float32'), - np.array([ - 0.00000000, 0.10980392, 0.22352941, 0.33333334, 0.44313726, - 0.55686277, 0.66666669, 0.77647060, 0.89019608, 1.00000000 - ]), - decimal=7) + convert_bit_depth(a, "float32"), + np.array( + [ + 0.00000000, + 0.10980392, + 0.22352941, + 0.33333334, + 0.44313726, + 0.55686277, + 0.66666669, + 0.77647060, + 0.89019608, + 1.00000000, + ] + ), + decimal=7, + ) self.assertIs( - convert_bit_depth(a, 'float64').dtype, np.dtype('float64')) + convert_bit_depth(a, "float64").dtype, np.dtype("float64") + ) - if platform.system() not in ('Windows', - 'Microsoft'): # pragma: no cover + if hasattr(np, "float128"): # pragma: no cover self.assertIs( - convert_bit_depth(a, 'float128').dtype, np.dtype('float128')) + convert_bit_depth(a, "float128").dtype, np.dtype("float128") + ) - a = np.around(np.linspace(0, 1, 10) * 65535).astype('uint16') - self.assertIs(convert_bit_depth(a, 'uint8').dtype, np.dtype('uint8')) + a = np.around(np.linspace(0, 1, 10) * 65535).astype("uint16") + self.assertIs(convert_bit_depth(a, "uint8").dtype, np.dtype("uint8")) np.testing.assert_equal( - convert_bit_depth(a, 'uint8'), - np.array([0, 28, 56, 85, 113, 141, 170, 198, 226, 255])) + convert_bit_depth(a, "uint8"), + np.array([0, 28, 56, 85, 113, 141, 170, 198, 226, 255]), + ) - self.assertIs(convert_bit_depth(a, 'uint16').dtype, np.dtype('uint16')) - np.testing.assert_equal(convert_bit_depth(a, 'uint16'), a) + self.assertIs(convert_bit_depth(a, "uint16").dtype, np.dtype("uint16")) + np.testing.assert_equal(convert_bit_depth(a, "uint16"), a) self.assertIs( - convert_bit_depth(a, 'float16').dtype, np.dtype('float16')) + convert_bit_depth(a, "float16").dtype, np.dtype("float16") + ) np.testing.assert_almost_equal( - convert_bit_depth(a, 'float16'), - np.array([ - 0.0000, 0.1098, 0.2235, 0.3333, 0.443, 0.5566, 0.6665, 0.7764, - 0.8900, 1.0000 - ]), - decimal=3) + convert_bit_depth(a, "float16"), + np.array( + [ + 0.0000, + 0.1098, + 0.2235, + 0.3333, + 0.443, + 0.5566, + 0.6665, + 0.7764, + 0.8900, + 1.0000, + ] + ), + decimal=3, + ) self.assertIs( - convert_bit_depth(a, 'float32').dtype, np.dtype('float32')) + convert_bit_depth(a, "float32").dtype, np.dtype("float32") + ) np.testing.assert_almost_equal( - convert_bit_depth(a, 'float32'), - np.array([ - 0.00000000, 0.11111620, 0.22221714, 0.33333334, 0.44444954, - 0.55555046, 0.66666669, 0.77778286, 0.88888383, 1.00000000 - ]), - decimal=7) + convert_bit_depth(a, "float32"), + np.array( + [ + 0.00000000, + 0.11111620, + 0.22221714, + 0.33333334, + 0.44444954, + 0.55555046, + 0.66666669, + 0.77778286, + 0.88888383, + 1.00000000, + ] + ), + decimal=7, + ) self.assertIs( - convert_bit_depth(a, 'float64').dtype, np.dtype('float64')) + convert_bit_depth(a, "float64").dtype, np.dtype("float64") + ) - if platform.system() not in ('Windows', - 'Microsoft'): # pragma: no cover + if hasattr(np, "float128"): # pragma: no cover self.assertIs( - convert_bit_depth(a, 'float128').dtype, np.dtype('float128')) + convert_bit_depth(a, "float128").dtype, np.dtype("float128") + ) a = np.linspace(0, 1, 10, dtype=np.float64) - self.assertIs(convert_bit_depth(a, 'uint8').dtype, np.dtype('uint8')) + self.assertIs(convert_bit_depth(a, "uint8").dtype, np.dtype("uint8")) np.testing.assert_equal( - convert_bit_depth(a, 'uint8'), - np.array([0, 28, 57, 85, 113, 142, 170, 198, 227, 255])) + convert_bit_depth(a, "uint8"), + np.array([0, 28, 57, 85, 113, 142, 170, 198, 227, 255]), + ) - self.assertIs(convert_bit_depth(a, 'uint16').dtype, np.dtype('uint16')) + self.assertIs(convert_bit_depth(a, "uint16").dtype, np.dtype("uint16")) np.testing.assert_equal( - convert_bit_depth(a, 'uint16'), - np.array([ - 0, 7282, 14563, 21845, 29127, 36408, 43690, 50972, 58253, 65535 - ])) + convert_bit_depth(a, "uint16"), + np.array( + [ + 0, + 7282, + 14563, + 21845, + 29127, + 36408, + 43690, + 50972, + 58253, + 65535, + ] + ), + ) self.assertIs( - convert_bit_depth(a, 'float16').dtype, np.dtype('float16')) + convert_bit_depth(a, "float16").dtype, np.dtype("float16") + ) np.testing.assert_almost_equal( - convert_bit_depth(a, 'float16'), - np.array([ - 0.0000, 0.1111, 0.2222, 0.3333, 0.4443, 0.5557, 0.6665, 0.7780, - 0.8887, 1.0000 - ]), - decimal=3) + convert_bit_depth(a, "float16"), + np.array( + [ + 0.0000, + 0.1111, + 0.2222, + 0.3333, + 0.4443, + 0.5557, + 0.6665, + 0.7780, + 0.8887, + 1.0000, + ] + ), + decimal=3, + ) self.assertIs( - convert_bit_depth(a, 'float32').dtype, np.dtype('float32')) + convert_bit_depth(a, "float32").dtype, np.dtype("float32") + ) np.testing.assert_almost_equal( - convert_bit_depth(a, 'float32'), a, decimal=7) + convert_bit_depth(a, "float32"), a, decimal=7 + ) self.assertIs( - convert_bit_depth(a, 'float64').dtype, np.dtype('float64')) + convert_bit_depth(a, "float64").dtype, np.dtype("float64") + ) - if platform.system() not in ('Windows', - 'Microsoft'): # pragma: no cover + if hasattr(np, "float128"): # pragma: no cover self.assertIs( - convert_bit_depth(a, 'float128').dtype, np.dtype('float128')) + convert_bit_depth(a, "float128").dtype, np.dtype("float128") + ) class TestReadImageOpenImageIO(unittest.TestCase): """ - Defines :func:`colour.io.image.read_image_OpenImageIO` definition units + Define :func:`colour.io.image.read_image_OpenImageIO` definition unit tests methods. """ def test_read_image_OpenImageIO(self): # pragma: no cover - """ - Tests :func:`colour.io.image.read_image_OpenImageIO` definition. - """ + """Test :func:`colour.io.image.read_image_OpenImageIO` definition.""" if not is_openimageio_installed(): return image = read_image_OpenImageIO( - os.path.join(RESOURCES_DIRECTORY, 'CMS_Test_Pattern.exr')) + os.path.join(RESOURCES_DIRECTORY, "CMS_Test_Pattern.exr") + ) self.assertTupleEqual(image.shape, (1267, 1274, 3)) - self.assertIs(image.dtype, np.dtype('float32')) + self.assertIs(image.dtype, np.dtype("float32")) image = read_image_OpenImageIO( - os.path.join(RESOURCES_DIRECTORY, 'CMS_Test_Pattern.exr'), - 'float16') - self.assertIs(image.dtype, np.dtype('float16')) + os.path.join(RESOURCES_DIRECTORY, "CMS_Test_Pattern.exr"), + "float16", + ) + self.assertIs(image.dtype, np.dtype("float16")) image, attributes = read_image_OpenImageIO( - os.path.join(RESOURCES_DIRECTORY, 'CMS_Test_Pattern.exr'), - attributes=True) + os.path.join(RESOURCES_DIRECTORY, "CMS_Test_Pattern.exr"), + attributes=True, + ) self.assertTupleEqual(image.shape, (1267, 1274, 3)) - self.assertEqual(attributes[0].name, 'oiio:ColorSpace') - self.assertEqual(attributes[0].value, 'Linear') + self.assertEqual(attributes[0].name, "oiio:ColorSpace") + self.assertEqual(attributes[0].value, "Linear") image = read_image_OpenImageIO( - os.path.join(RESOURCES_DIRECTORY, 'Single_Channel.exr')) + os.path.join(RESOURCES_DIRECTORY, "Single_Channel.exr") + ) self.assertTupleEqual(image.shape, (256, 256)) image = read_image_OpenImageIO( - os.path.join(RESOURCES_DIRECTORY, 'Colour_Logo.png'), 'uint8') + os.path.join(RESOURCES_DIRECTORY, "Colour_Logo.png"), "uint8" + ) self.assertTupleEqual(image.shape, (128, 256, 4)) - self.assertIs(image.dtype, np.dtype('uint8')) + self.assertIs(image.dtype, np.dtype("uint8")) self.assertEqual(np.min(image), 0) self.assertEqual(np.max(image), 255) image = read_image_OpenImageIO( - os.path.join(RESOURCES_DIRECTORY, 'Colour_Logo.png'), 'uint16') + os.path.join(RESOURCES_DIRECTORY, "Colour_Logo.png"), "uint16" + ) self.assertTupleEqual(image.shape, (128, 256, 4)) - self.assertIs(image.dtype, np.dtype('uint16')) + self.assertIs(image.dtype, np.dtype("uint16")) self.assertEqual(np.min(image), 0) self.assertEqual(np.max(image), 65535) @@ -216,205 +309,255 @@ def test_read_image_OpenImageIO(self): # pragma: no cover # self.assertEqual(np.max(image), 1.0) image = read_image_OpenImageIO( - os.path.join(RESOURCES_DIRECTORY, 'Colour_Logo.png'), 'float32') - self.assertIs(image.dtype, np.dtype('float32')) + os.path.join(RESOURCES_DIRECTORY, "Colour_Logo.png"), "float32" + ) + self.assertIs(image.dtype, np.dtype("float32")) self.assertEqual(np.min(image), 0.0) self.assertEqual(np.max(image), 1.0) class TestWriteImageOpenImageIO(unittest.TestCase): """ - Defines :func:`colour.io.image.write_image_OpenImageIO` definition units + Define :func:`colour.io.image.write_image_OpenImageIO` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._temporary_directory = tempfile.mkdtemp() def tearDown(self): - """ - After tests actions. - """ + """After tests actions.""" shutil.rmtree(self._temporary_directory) def test_write_image_OpenImageIO(self): # pragma: no cover - """ - Tests :func:`colour.io.image.write_image_OpenImageIO` definition. - """ + """Test :func:`colour.io.image.write_image_OpenImageIO` definition.""" if not is_openimageio_installed(): return - source_image_path = os.path.join(RESOURCES_DIRECTORY, - 'CMS_Test_Pattern.exr') - target_image_path = os.path.join(self._temporary_directory, - 'CMS_Test_Pattern.exr') + from OpenImageIO import TypeDesc + + source_image_path = os.path.join( + RESOURCES_DIRECTORY, "Overflowing_Gradient.png" + ) + target_image_path = os.path.join( + self._temporary_directory, "Overflowing_Gradient.png" + ) + RGB = np.arange(0, 256, 1, dtype=np.uint8)[np.newaxis] * 2 + write_image_OpenImageIO(RGB, target_image_path, bit_depth="uint8") + image = read_image_OpenImageIO(source_image_path, bit_depth="uint8") + np.testing.assert_equal(np.squeeze(RGB), image) + + source_image_path = os.path.join( + RESOURCES_DIRECTORY, "CMS_Test_Pattern.exr" + ) + target_image_path = os.path.join( + self._temporary_directory, "CMS_Test_Pattern.exr" + ) image = read_image_OpenImageIO(source_image_path) write_image_OpenImageIO(image, target_image_path) image = read_image_OpenImageIO(target_image_path) self.assertTupleEqual(image.shape, (1267, 1274, 3)) - self.assertIs(image.dtype, np.dtype('float32')) - + self.assertIs(image.dtype, np.dtype("float32")) + + chromaticities = ( + 0.73470, + 0.26530, + 0.00000, + 1.00000, + 0.00010, + -0.07700, + 0.32168, + 0.33767, + ) + write_attributes = [ + ImageAttribute_Specification("acesImageContainerFlag", True), + ImageAttribute_Specification( + "chromaticities", chromaticities, TypeDesc("float[8]") + ), + ImageAttribute_Specification("compression", "none"), + ] write_image_OpenImageIO( - image, - target_image_path, - attributes=[ImageAttribute_Specification('John', 'Doe')]) - image, attributes = read_image_OpenImageIO( - target_image_path, attributes=True) - for attribute in attributes: - if attribute.name == 'John': - self.assertEqual(attribute.value, 'Doe') + image, target_image_path, attributes=write_attributes + ) + image, read_attributes = read_image_OpenImageIO( + target_image_path, attributes=True + ) + for write_attribute in write_attributes: + attribute_exists = False + for read_attribute in read_attributes: + if write_attribute.name == read_attribute.name: + attribute_exists = True + if isinstance(write_attribute.value, tuple): + np.testing.assert_almost_equal( + write_attribute.value, + read_attribute.value, + decimal=5, + ) + else: + self.assertEqual( + write_attribute.value, read_attribute.value + ) + + attest( + attribute_exists, + f'"{write_attribute.name}" attribute was not found on image!', + ) class TestReadImageImageio(unittest.TestCase): """ - Defines :func:`colour.io.image.read_image_Imageio` definition units tests + Define :func:`colour.io.image.read_image_Imageio` definition unit tests methods. """ def test_read_image_Imageio(self): - """ - Tests :func:`colour.io.image.read_image_Imageio` definition. - """ + """Test :func:`colour.io.image.read_image_Imageio` definition.""" image = read_image_Imageio( - os.path.join(RESOURCES_DIRECTORY, 'CMS_Test_Pattern.exr')) + os.path.join(RESOURCES_DIRECTORY, "CMS_Test_Pattern.exr") + ) self.assertTupleEqual(image.shape, (1267, 1274, 3)) - self.assertIs(image.dtype, np.dtype('float32')) + self.assertIs(image.dtype, np.dtype("float32")) image = read_image_Imageio( - os.path.join(RESOURCES_DIRECTORY, 'CMS_Test_Pattern.exr'), - 'float16') + os.path.join(RESOURCES_DIRECTORY, "CMS_Test_Pattern.exr"), + "float16", + ) self.assertTupleEqual(image.shape, (1267, 1274, 3)) - self.assertIs(image.dtype, np.dtype('float16')) + self.assertIs(image.dtype, np.dtype("float16")) image = read_image_Imageio( - os.path.join(RESOURCES_DIRECTORY, 'Single_Channel.exr')) + os.path.join(RESOURCES_DIRECTORY, "Single_Channel.exr") + ) self.assertTupleEqual(image.shape, (256, 256)) image = read_image_Imageio( - os.path.join(RESOURCES_DIRECTORY, 'Colour_Logo.png'), 'uint8') + os.path.join(RESOURCES_DIRECTORY, "Colour_Logo.png"), "uint8" + ) self.assertTupleEqual(image.shape, (128, 256, 4)) - self.assertIs(image.dtype, np.dtype('uint8')) + self.assertIs(image.dtype, np.dtype("uint8")) self.assertEqual(np.min(image), 0) self.assertEqual(np.max(image), 255) image = read_image_Imageio( - os.path.join(RESOURCES_DIRECTORY, 'Colour_Logo.png'), 'uint16') + os.path.join(RESOURCES_DIRECTORY, "Colour_Logo.png"), "uint16" + ) self.assertTupleEqual(image.shape, (128, 256, 4)) - self.assertIs(image.dtype, np.dtype('uint16')) + self.assertIs(image.dtype, np.dtype("uint16")) self.assertEqual(np.min(image), 0) self.assertEqual(np.max(image), 65535) image = read_image_Imageio( - os.path.join(RESOURCES_DIRECTORY, 'Colour_Logo.png'), 'float16') - self.assertIs(image.dtype, np.dtype('float16')) + os.path.join(RESOURCES_DIRECTORY, "Colour_Logo.png"), "float16" + ) + self.assertIs(image.dtype, np.dtype("float16")) self.assertEqual(np.min(image), 0.0) self.assertEqual(np.max(image), 1.0) image = read_image_Imageio( - os.path.join(RESOURCES_DIRECTORY, 'Colour_Logo.png'), 'float32') - self.assertIs(image.dtype, np.dtype('float32')) + os.path.join(RESOURCES_DIRECTORY, "Colour_Logo.png"), "float32" + ) + self.assertIs(image.dtype, np.dtype("float32")) self.assertEqual(np.min(image), 0.0) self.assertEqual(np.max(image), 1.0) class TestWriteImageImageio(unittest.TestCase): """ - Defines :func:`colour.io.image.write_image_Imageio` definition units + Define :func:`colour.io.image.write_image_Imageio` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._temporary_directory = tempfile.mkdtemp() def tearDown(self): - """ - After tests actions. - """ + """After tests actions.""" shutil.rmtree(self._temporary_directory) def test_write_image_Imageio(self): - """ - Tests :func:`colour.io.image.write_image_Imageio` definition. - """ - - source_image_path = os.path.join(RESOURCES_DIRECTORY, - 'CMS_Test_Pattern.exr') - target_image_path = os.path.join(self._temporary_directory, - 'CMS_Test_Pattern.exr') + """Test :func:`colour.io.image.write_image_Imageio` definition.""" + + source_image_path = os.path.join( + RESOURCES_DIRECTORY, "Overflowing_Gradient.png" + ) + target_image_path = os.path.join( + self._temporary_directory, "Overflowing_Gradient.png" + ) + RGB = np.arange(0, 256, 1, dtype=np.uint8)[np.newaxis] * 2 + write_image_Imageio(RGB, target_image_path, bit_depth="uint8") + image = read_image_Imageio(source_image_path, bit_depth="uint8") + np.testing.assert_equal(np.squeeze(RGB), image) + + source_image_path = os.path.join( + RESOURCES_DIRECTORY, "CMS_Test_Pattern.exr" + ) + target_image_path = os.path.join( + self._temporary_directory, "CMS_Test_Pattern.exr" + ) image = read_image_Imageio(source_image_path) write_image_Imageio(image, target_image_path) image = read_image_Imageio(target_image_path) self.assertTupleEqual(image.shape, (1267, 1274, 3)) - self.assertIs(image.dtype, np.dtype('float32')) + self.assertIs(image.dtype, np.dtype("float32")) class TestReadImage(unittest.TestCase): """ - Defines :func:`colour.io.image.read_image` definition units tests + Define :func:`colour.io.image.read_image` definition unit tests methods. """ def test_read_image(self): - """ - Tests :func:`colour.io.image.read_image` definition. - """ + """Test :func:`colour.io.image.read_image` definition.""" image = read_image( - os.path.join(RESOURCES_DIRECTORY, 'CMS_Test_Pattern.exr')) + os.path.join(RESOURCES_DIRECTORY, "CMS_Test_Pattern.exr") + ) self.assertTupleEqual(image.shape, (1267, 1274, 3)) - self.assertIs(image.dtype, np.dtype('float32')) + self.assertIs(image.dtype, np.dtype("float32")) image = read_image( - os.path.join(RESOURCES_DIRECTORY, 'Single_Channel.exr')) + os.path.join(RESOURCES_DIRECTORY, "Single_Channel.exr") + ) self.assertTupleEqual(image.shape, (256, 256)) class TestWriteImage(unittest.TestCase): - """ - Defines :func:`colour.io.image.write_image` definition units tests methods. - """ + """Define :func:`colour.io.image.write_image` definition unit tests methods.""" def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._temporary_directory = tempfile.mkdtemp() def tearDown(self): - """ - After tests actions. - """ + """After tests actions.""" shutil.rmtree(self._temporary_directory) def test_write_image(self): - """ - Tests :func:`colour.io.image.write_image` definition. - """ - - source_image_path = os.path.join(RESOURCES_DIRECTORY, - 'CMS_Test_Pattern.exr') - target_image_path = os.path.join(self._temporary_directory, - 'CMS_Test_Pattern.exr') + """Test :func:`colour.io.image.write_image` definition.""" + + source_image_path = os.path.join( + RESOURCES_DIRECTORY, "CMS_Test_Pattern.exr" + ) + target_image_path = os.path.join( + self._temporary_directory, "CMS_Test_Pattern.exr" + ) image = read_image(source_image_path) write_image(image, target_image_path) image = read_image(target_image_path) self.assertTupleEqual(image.shape, (1267, 1274, 3)) - self.assertIs(image.dtype, np.dtype('float32')) + self.assertIs(image.dtype, np.dtype("float32")) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/io/tests/test_ocio.py b/colour/io/tests/test_ocio.py new file mode 100644 index 0000000000..3dd0e2fc99 --- /dev/null +++ b/colour/io/tests/test_ocio.py @@ -0,0 +1,110 @@ +"""Defines the unit tests for the :mod:`colour.io.ocio` module.""" + +from __future__ import annotations + +import numpy as np +import os +import unittest + +from colour.io import process_image_OpenColorIO +from colour.utilities import full, is_opencolorio_installed + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "RESOURCES_DIRECTORY", + "TestProcessImageOpenColorIO", +] + +RESOURCES_DIRECTORY: str = os.path.join(os.path.dirname(__file__), "resources") + + +class TestProcessImageOpenColorIO(unittest.TestCase): + """ + Define :func:`colour.io.ocio.process_image_OpenColorIO` definition unit + tests methods. + """ + + def test_process_image_OpenColorIO(self): + """Test :func:`colour.io.ocio.process_image_OpenColorIO` definition.""" + + # TODO: Remove when "Pypi" wheel compatible with "ARM" on "macOS" is + # released. + if not is_opencolorio_installed(): # pragma: no cover + return + + import PyOpenColorIO as ocio + + config = os.path.join( + RESOURCES_DIRECTORY, "config-aces-reference.ocio.yaml" + ) + + a = full([4, 2, 3], 0.18) + + np.testing.assert_almost_equal( + process_image_OpenColorIO( + a, "ACES - ACES2065-1", "ACES - ACEScct", config=config + ), + np.array( + [ + [ + [0.41358781, 0.41358781, 0.41358781], + [0.41358781, 0.41358781, 0.41358781], + ], + [ + [0.41358781, 0.41358781, 0.41358781], + [0.41358781, 0.41358781, 0.41358781], + ], + [ + [0.41358781, 0.41358781, 0.41358781], + [0.41358781, 0.41358781, 0.41358781], + ], + [ + [0.41358781, 0.41358781, 0.41358781], + [0.41358781, 0.41358781, 0.41358781], + ], + ] + ), + decimal=5, + ) + + np.testing.assert_almost_equal( + process_image_OpenColorIO( + a, + "ACES - ACES2065-1", + "Display - sRGB", + "Output - SDR Video - ACES 1.0", + ocio.TRANSFORM_DIR_FORWARD, + config=config, + ), + np.array( + [ + [ + [0.35595229, 0.35595256, 0.35595250], + [0.35595229, 0.35595256, 0.35595250], + ], + [ + [0.35595229, 0.35595256, 0.35595250], + [0.35595229, 0.35595256, 0.35595250], + ], + [ + [0.35595229, 0.35595256, 0.35595250], + [0.35595229, 0.35595256, 0.35595250], + ], + [ + [0.35595229, 0.35595256, 0.35595250], + [0.35595229, 0.35595256, 0.35595250], + ], + ] + ), + decimal=5, + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/io/tests/test_tabular.py b/colour/io/tests/test_tabular.py index 53062e3283..9d356acc0b 100644 --- a/colour/io/tests/test_tabular.py +++ b/colour/io/tests/test_tabular.py @@ -1,37 +1,38 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.io.tabular` module. -""" +"""Defines the unit tests for the :mod:`colour.io.tabular` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations -import numpy as np import os import shutil import unittest import tempfile -from six import PY2, text_type from colour.colorimetry import SpectralDistribution, SpectralShape -from colour.io import (read_spectral_data_from_csv_file, - read_sds_from_csv_file, write_sds_to_csv_file) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import Dict +from colour.io import ( + read_spectral_data_from_csv_file, + read_sds_from_csv_file, + write_sds_to_csv_file, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'RESOURCES_DIRECTORY', 'COLOURCHECKER_N_OHTA_1', - 'TestReadSpectralDataFromCsvFile', 'TestReadSdsFromCsvFile', - 'TestWriteSdsToCsvFile' + "RESOURCES_DIRECTORY", + "COLOURCHECKER_N_OHTA_1", + "TestReadSpectralDataFromCsvFile", + "TestReadSdsFromCsvFile", + "TestWriteSdsToCsvFile", ] -RESOURCES_DIRECTORY = os.path.join(os.path.dirname(__file__), 'resources') +RESOURCES_DIRECTORY: str = os.path.join(os.path.dirname(__file__), "resources") -COLOURCHECKER_N_OHTA_1 = { +COLOURCHECKER_N_OHTA_1: Dict = { 380.0: 0.048, 385.0: 0.051, 390.0: 0.055, @@ -112,134 +113,131 @@ 765.0: 0.465, 770.0: 0.448, 775.0: 0.432, - 780.0: 0.421 + 780.0: 0.421, } class TestReadSpectralDataFromCsvFile(unittest.TestCase): """ - Defines :func:`colour.io.tabular.read_spectral_data_from_csv_file` + Define :func:`colour.io.tabular.read_spectral_data_from_csv_file` definition unit tests methods. """ def test_read_spectral_data_from_csv_file(self): """ - Tests :func:`colour.io.tabular.read_spectral_data_from_csv_file` + Test :func:`colour.io.tabular.read_spectral_data_from_csv_file` definition. """ - colour_checker_n_ohta = os.path.join(RESOURCES_DIRECTORY, - 'colorchecker_n_ohta.csv') + colour_checker_n_ohta = os.path.join( + RESOURCES_DIRECTORY, "colorchecker_n_ohta.csv" + ) data = read_spectral_data_from_csv_file(colour_checker_n_ohta) self.assertListEqual( - sorted(data), sorted([text_type(x) for x in range(1, 25)])) - self.assertDictEqual(data['1'], COLOURCHECKER_N_OHTA_1) + list(data.keys()), ["wavelength"] + [str(x) for x in range(1, 25)] + ) + self.assertDictEqual( + dict(zip(data["wavelength"], data["1"])), COLOURCHECKER_N_OHTA_1 + ) + + colour_checker_n_ohta_transposed = os.path.join( + RESOURCES_DIRECTORY, "colorchecker_n_ohta_transposed.csv" + ) + data = read_spectral_data_from_csv_file( + colour_checker_n_ohta_transposed, transpose=True, delimiter="\t" + ) + self.assertListEqual( + list(data.keys()), ["wavelength"] + [str(x) for x in range(1, 25)] + ) + self.assertDictEqual( + dict(zip(data["wavelength"], data["1"])), COLOURCHECKER_N_OHTA_1 + ) - linss2_10e_5 = os.path.join(RESOURCES_DIRECTORY, 'linss2_10e_5.csv') + linss2_10e_5 = os.path.join(RESOURCES_DIRECTORY, "linss2_10e_5.csv") data = read_spectral_data_from_csv_file( - linss2_10e_5, fields=['wavelength', 'l_bar', 'm_bar', 's_bar']) - self.assertListEqual(sorted(data), ['l_bar', 'm_bar', 's_bar']) - self.assertEqual(data['s_bar'][760], 0) + linss2_10e_5, + names=["wavelength", "l_bar", "m_bar", "s_bar"], + filling_values=0, + ) + self.assertListEqual( + list(data.keys()), ["wavelength", "l_bar", "m_bar", "s_bar"] + ) + self.assertEqual(data["s_bar"][77], 0) data = read_spectral_data_from_csv_file( linss2_10e_5, - fields=['wavelength', 'l_bar', 'm_bar', 's_bar'], - default=-1) - self.assertEqual(data['s_bar'][760], -1) - - def test_raise_exception_read_spectral_data_from_csv_file(self): - """ - Tests :attr:`colour.io.tabular.read_spectral_data_from_csv_file` - definition raised exception. - """ - - self.assertRaises(RuntimeError, read_spectral_data_from_csv_file, - os.path.join(RESOURCES_DIRECTORY, 'Invalid.csv')) + names=["wavelength", "l_bar", "m_bar", "s_bar"], + filling_values=-1, + ) + self.assertEqual(data["s_bar"][77], -1) class TestReadSdsFromCsvFile(unittest.TestCase): """ - Defines :func:`colour.io.tabular.read_sds_from_csv_file` definition units + Define :func:`colour.io.tabular.read_sds_from_csv_file` definition unit tests methods. """ def test_read_sds_from_csv_file(self): - """ - Tests :func:`colour.io.tabular.read_sds_from_csv_file` definition. - """ + """Test :func:`colour.io.tabular.read_sds_from_csv_file` definition.""" - colour_checker_n_ohta = os.path.join(RESOURCES_DIRECTORY, - 'colorchecker_n_ohta.csv') + colour_checker_n_ohta = os.path.join( + RESOURCES_DIRECTORY, "colorchecker_n_ohta.csv" + ) sds = read_sds_from_csv_file(colour_checker_n_ohta) for sd in sds.values(): self.assertIsInstance(sd, SpectralDistribution) self.assertEqual( - sds['1'], SpectralDistribution(COLOURCHECKER_N_OHTA_1, name='1')) + sds["1"], SpectralDistribution(COLOURCHECKER_N_OHTA_1, name="1") + ) class TestWriteSdsToCsvFile(unittest.TestCase): """ - Defines :func:`colour.io.tabular.write_sds_to_csv_file` definition units + Define :func:`colour.io.tabular.write_sds_to_csv_file` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._temporary_directory = tempfile.mkdtemp() def tearDown(self): - """ - After tests actions. - """ + """After tests actions.""" shutil.rmtree(self._temporary_directory) def test_write_sds_to_csv_file(self): - """ - Tests :func:`colour.io.tabular.write_sds_to_csv_file` definition. - """ + """Test :func:`colour.io.tabular.write_sds_to_csv_file` definition.""" - colour_checker_n_ohta = os.path.join(RESOURCES_DIRECTORY, - 'colorchecker_n_ohta.csv') + colour_checker_n_ohta = os.path.join( + RESOURCES_DIRECTORY, "colorchecker_n_ohta.csv" + ) sds = read_sds_from_csv_file(colour_checker_n_ohta) - colour_checker_n_ohta_test = os.path.join(self._temporary_directory, - 'colorchecker_n_ohta.csv') + colour_checker_n_ohta_test = os.path.join( + self._temporary_directory, "colorchecker_n_ohta.csv" + ) write_sds_to_csv_file(sds, colour_checker_n_ohta_test) sds_test = read_sds_from_csv_file(colour_checker_n_ohta_test) for key, value in sds.items(): - if PY2: # pragma: no cover - # Running into precision issues with Python 2.x, applying - # conservative rounding. - value.wavelengths = np.around(value.wavelengths, decimals=7) - value.values = np.around(value.values, decimals=7) - sds_test[key].wavelengths = np.around( - sds_test[key].wavelengths, decimals=7) - sds_test[key].values = np.around( - sds_test[key].values, decimals=7) - self.assertEqual(value, sds_test[key]) - write_sds_to_csv_file(sds, colour_checker_n_ohta_test, fields=['1']) - sds_test = read_sds_from_csv_file(colour_checker_n_ohta_test) - self.assertEqual(len(sds_test), 1) - def test_raise_exception_write_sds_to_csv_file(self): """ - Tests :func:`colour.io.tabular.write_sds_to_csv_file` definition + Test :func:`colour.io.tabular.write_sds_to_csv_file` definition raised exception. """ - colour_checker_n_ohta = os.path.join(RESOURCES_DIRECTORY, - 'colorchecker_n_ohta.csv') + colour_checker_n_ohta = os.path.join( + RESOURCES_DIRECTORY, "colorchecker_n_ohta.csv" + ) sds = read_sds_from_csv_file(colour_checker_n_ohta) key = list(sds.keys())[0] sds[key] = sds[key].align(SpectralShape(400, 700, 10)) - self.assertRaises(RuntimeError, write_sds_to_csv_file, sds, '') + self.assertRaises(ValueError, write_sds_to_csv_file, sds, "") -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/io/tests/test_tm2714.py b/colour/io/tests/test_tm2714.py index 8e54c28993..de0915ac8d 100644 --- a/colour/io/tests/test_tm2714.py +++ b/colour/io/tests/test_tm2714.py @@ -1,54 +1,57 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.io.tm2714` module. -""" +"""Defines the unit tests for the :mod:`colour.io.tm2714` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import os import shutil import unittest import tempfile +from copy import deepcopy from colour.colorimetry import SpectralDistribution +from colour.hints import Dict, List, Optional, Tuple, Union, cast from colour.io.tm2714 import Header_IESTM2714, SpectralDistribution_IESTM2714 +from colour.utilities import optional -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'RESOURCES_DIRECTORY', 'FLUORESCENT_FILE_HEADER', - 'FLUORESCENT_FILE_SPECTRAL_DESCRIPTION', 'FLUORESCENT_FILE_SPECTRAL_DATA', - 'TestIES_TM2714_Header', 'TestIES_TM2714_Sd' + "RESOURCES_DIRECTORY", + "FLUORESCENT_FILE_HEADER", + "FLUORESCENT_FILE_SPECTRAL_DESCRIPTION", + "FLUORESCENT_FILE_SPECTRAL_DATA", + "TestIES_TM2714_Header", + "TestIES_TM2714_Sd", ] -RESOURCES_DIRECTORY = os.path.join(os.path.dirname(__file__), 'resources') - -FLUORESCENT_FILE_HEADER = { - 'Manufacturer': 'Unknown', - 'CatalogNumber': 'N/A', - 'Description': 'Rare earth fluorescent lamp', - 'DocumentCreator': 'byHeart Consultants', - 'Laboratory': 'N/A', - 'UniqueIdentifier': 'C3567553-C75B-4354-961E-35CEB9FEB42C', - 'ReportNumber': 'N/A', - 'ReportDate': 'N/A', - 'DocumentCreationDate': '2014-06-23', - 'Comments': 'Ambient temperature 25 degrees C.' +RESOURCES_DIRECTORY: str = os.path.join(os.path.dirname(__file__), "resources") + +FLUORESCENT_FILE_HEADER: Dict = { + "Manufacturer": "Unknown", + "CatalogNumber": "N/A", + "Description": "Rare earth fluorescent lamp", + "DocumentCreator": "byHeart Consultants", + "Laboratory": "N/A", + "UniqueIdentifier": "C3567553-C75B-4354-961E-35CEB9FEB42C", + "ReportNumber": "N/A", + "ReportDate": "N/A", + "DocumentCreationDate": "2014-06-23", + "Comments": "Ambient temperature 25 degrees C.", } -FLUORESCENT_FILE_SPECTRAL_DESCRIPTION = { - 'SpectralQuantity': 'relative', - 'BandwidthFWHM': 2.0, - 'BandwidthCorrected': True +FLUORESCENT_FILE_SPECTRAL_DESCRIPTION: Dict = { + "SpectralQuantity": "relative", + "BandwidthFWHM": 2.0, + "BandwidthCorrected": True, } -FLUORESCENT_FILE_SPECTRAL_DATA = { +FLUORESCENT_FILE_SPECTRAL_DATA: Dict = { 400.0: 0.034, 403.1: 0.037, 405.5: 0.069, @@ -133,118 +136,265 @@ 808.8: 0.029, 810.7: 0.039, 812.7: 0.030, - 850.1: 0.030 + 850.1: 0.030, } class TestIES_TM2714_Header(unittest.TestCase): """ - Defines :class:`colour.io.tm2714.Header_IESTM2714` class unit tests + Define :class:`colour.io.tm2714.Header_IESTM2714` class unit tests methods. """ def test_required_attributes(self): - """ - Tests presence of required attributes. - """ - - required_attributes = ('mapping', 'manufacturer', 'catalog_number', - 'description', 'document_creator', - 'unique_identifier', 'measurement_equipment', - 'laboratory', 'report_number', 'report_date', - 'document_creation_date', 'comments') + """Test the presence of required attributes.""" + + required_attributes = ( + "mapping", + "manufacturer", + "catalog_number", + "description", + "document_creator", + "unique_identifier", + "measurement_equipment", + "laboratory", + "report_number", + "report_date", + "document_creation_date", + "comments", + ) for attribute in required_attributes: self.assertIn(attribute, dir(Header_IESTM2714)) + def test_required_methods(self): + """Test the presence of required methods.""" + + required_methods = ( + "__init__", + "__hash__", + "__eq__", + "__ne__", + ) + + for method in required_methods: + self.assertIn(method, dir(Header_IESTM2714)) + + def test__eq__(self): + """Test :meth:`colour.io.tm2714.Header_IESTM2714.__eq__` method.""" + + h0 = Header_IESTM2714( + manufacturer="a", + catalog_number="b", + description="c", + document_creator="d", + unique_identifier="e", + measurement_equipment="f", + laboratory="g", + report_number="h", + report_date="i", + document_creation_date="j", + comments="k", + ) + + h1 = deepcopy(h0) + self.assertEqual(h0, h1) + + def test__ne__(self): + """Test :meth:`colour.io.tm2714.Header_IESTM2714.__ne__` method.""" + + h0 = Header_IESTM2714( + manufacturer="a", + catalog_number="b", + description="c", + document_creator="d", + unique_identifier="e", + measurement_equipment="f", + laboratory="g", + report_number="h", + report_date="i", + document_creation_date="j", + comments="k", + ) + h1 = deepcopy(h0) + + h1.manufacturer = "aa" + self.assertNotEqual(h0, h1) + h1.manufacturer = "a" + self.assertEqual(h0, h1) + + def test__hash__(self): + """Test :meth:`colour.io.tm2714.Header_IESTM2714.__hash__` method.""" + + h0 = Header_IESTM2714( + manufacturer="a", + catalog_number="b", + description="c", + document_creator="d", + unique_identifier="e", + measurement_equipment="f", + laboratory="g", + report_number="h", + report_date="i", + document_creation_date="j", + comments="k", + ) + + self.assertIsInstance(hash(h0), int) + class TestIES_TM2714_Sd(unittest.TestCase): """ - Defines :class:`colour.io.tm2714.SpectralDistribution_IESTM2714` class unit + Define :class:`colour.io.tm2714.SpectralDistribution_IESTM2714` class unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._temporary_directory = tempfile.mkdtemp() def tearDown(self): - """ - After tests actions. - """ + """After tests actions.""" shutil.rmtree(self._temporary_directory) def test_required_attributes(self): - """ - Tests presence of required attributes. - """ - - required_attributes = ('mapping', 'path', 'header', - 'spectral_quantity', 'reflection_geometry', - 'transmission_geometry', 'bandwidth_FWHM', - 'bandwidth_corrected') + """Test the presence of required attributes.""" + + required_attributes = ( + "mapping", + "path", + "header", + "spectral_quantity", + "reflection_geometry", + "transmission_geometry", + "bandwidth_FWHM", + "bandwidth_corrected", + ) for attribute in required_attributes: self.assertIn(attribute, dir(SpectralDistribution_IESTM2714)) def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__init__', 'read', 'write') + required_methods = ("__init__", "read", "write") for method in required_methods: self.assertIn(method, dir(SpectralDistribution_IESTM2714)) - def test_read(self, sd=None): + def test_read(self, sd: Optional[SpectralDistribution] = None): """ - Tests :attr:`colour.io.tm2714.SpectralDistribution_IESTM2714.read` + Test :meth:`colour.io.tm2714.SpectralDistribution_IESTM2714.read` method. -_temporary_directory_temporary_directory + Parameters ---------- - sd : SpectralDistribution_IESTM2714, optional + sd Optional *IES TM-27-14* spectral distribution for read tests. """ - if sd is None: - sd = SpectralDistribution_IESTM2714( - os.path.join(RESOURCES_DIRECTORY, 'Fluorescent.spdx')).read() + sd = cast( + SpectralDistribution_IESTM2714, + optional( + sd, + SpectralDistribution_IESTM2714( + os.path.join(RESOURCES_DIRECTORY, "Fluorescent.spdx") + ).read(), + ), + ) sd_r = SpectralDistribution(FLUORESCENT_FILE_SPECTRAL_DATA) np.testing.assert_array_equal(sd_r.domain, sd.domain) np.testing.assert_almost_equal(sd_r.values, sd.values, decimal=7) - for test, read in ((FLUORESCENT_FILE_HEADER, sd.header), - (FLUORESCENT_FILE_SPECTRAL_DESCRIPTION, sd)): + test_read: List[ + Tuple[ + Dict, Union[Header_IESTM2714, SpectralDistribution_IESTM2714] + ] + ] = [ + (FLUORESCENT_FILE_HEADER, sd.header), + (FLUORESCENT_FILE_SPECTRAL_DESCRIPTION, sd), + ] + for test, read in test_read: for key, value in test.items(): for specification in read.mapping.elements: if key == specification.element: - self.assertEquals( - getattr(read, specification.attribute), value) + self.assertEqual( + getattr(read, specification.attribute), value + ) + + def test_raise_exception_read(self): + """ + Test :func:`colour.io.tm2714.SpectralDistribution_IESTM2714.read` + method raised exception. + """ + + sd = SpectralDistribution_IESTM2714() + self.assertRaises(ValueError, sd.read) + + sd = SpectralDistribution_IESTM2714( + os.path.join(RESOURCES_DIRECTORY, "Invalid.spdx") + ) + self.assertRaises(ValueError, sd.read) def test_write(self): """ - Tests :attr:`colour.io.tm2714.SpectralDistribution_IESTM2714.write` + Test :meth:`colour.io.tm2714.SpectralDistribution_IESTM2714.write` method. """ sd_r = SpectralDistribution_IESTM2714( - os.path.join(RESOURCES_DIRECTORY, 'Fluorescent.spdx')).read() + os.path.join(RESOURCES_DIRECTORY, "Fluorescent.spdx") + ).read() - sd_r.path = os.path.join(self._temporary_directory, 'Fluorescent.spdx') + sd_r.path = os.path.join(self._temporary_directory, "Fluorescent.spdx") self.assertTrue(sd_r.write()) sd_t = SpectralDistribution_IESTM2714(sd_r.path).read() self.test_read(sd_t) - self.assertEquals(sd_r, sd_t) + self.assertEqual(sd_r, sd_t) + + for attribute in ( + "manufacturer", + "catalog_number", + "description", + "document_creator", + "unique_identifier", + "measurement_equipment", + "laboratory", + "report_number", + "report_date", + "document_creation_date", + "comments", + ): + self.assertEqual( + getattr(sd_r.header, attribute), + getattr(sd_t.header, attribute), + ) + + for attribute in ( + "spectral_quantity", + "reflection_geometry", + "transmission_geometry", + "bandwidth_FWHM", + "bandwidth_corrected", + ): + self.assertEqual( + getattr(sd_r, attribute), getattr(sd_t, attribute) + ) + + def test_raise_exception_write(self): + """ + Test :func:`colour.io.tm2714.SpectralDistribution_IESTM2714.write` + method raised exception. + """ + + sd = SpectralDistribution_IESTM2714() + self.assertRaises(ValueError, sd.write) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/io/tests/test_uprtek_sekonic.py b/colour/io/tests/test_uprtek_sekonic.py new file mode 100644 index 0000000000..e45529cc2b --- /dev/null +++ b/colour/io/tests/test_uprtek_sekonic.py @@ -0,0 +1,1100 @@ +"""Defines unit tests for :mod:`colour.io.uprtek_sekonic` module.""" + +from __future__ import annotations + +import json +import numpy as np +import os +import unittest + +from colour.colorimetry import SpectralDistribution +from colour.hints import Any, Dict, Optional +from colour.io import ( + SpectralDistribution_UPRTek, + SpectralDistribution_Sekonic, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "RESOURCES_DIRECTORY", + "AbstractSpectralDistributionTest", + "TestSpectralDistributionUprTek", + "TestSpectralDistributionSekonic", +] + +RESOURCES_DIRECTORY: str = os.path.join(os.path.dirname(__file__), "resources") + + +class AbstractSpectralDistributionTest(unittest.TestCase): + """ + Define :class:`colour.SpectralDistribution_UPRTek`, + :class:`colour.SpectralDistribution_Sekonic` classes common unit tests + methods. + """ + + def __init__(self, *args: Any): + """ + Create an instance of the class. + + Other Parameters + ---------------- + args + Arguments. + """ + + super().__init__(*args) + + self._sd_factory: Any = None + self._path: Optional[str] = None + self._spectral_data: Optional[Dict] = None + + def test_required_attributes(self): + """Test the presence of required attributes.""" + + required_attributes = ( + "mapping", + "path", + "header", + "spectral_quantity", + "reflection_geometry", + "transmission_geometry", + "bandwidth_FWHM", + "bandwidth_corrected", + "metadata", + ) + + for attribute in required_attributes: + self.assertIn(attribute, dir(SpectralDistribution_UPRTek)) + + def test_required_methods(self): + """Test the presence of required methods.""" + + required_methods = ("__init__", "read", "write") + + for method in required_methods: + self.assertIn(method, dir(SpectralDistribution_UPRTek)) + + def test_read(self): + """ + Test :meth:`colour.SpectralDistribution_UPRTek.read` and + :meth:`colour.SpectralDistribution_Sekonic.read` methods. + """ + + if self._sd_factory is None: + return + + sd = self._sd_factory( + os.path.join(RESOURCES_DIRECTORY, self._path) + ).read() + + sd_r = SpectralDistribution(self._spectral_data) + + np.testing.assert_array_equal(sd_r.domain, sd.domain) + np.testing.assert_almost_equal(sd_r.values, sd.values, decimal=6) + + for key, value in self._header.items(): + for specification in sd.header.mapping.elements: + if key == specification.element: + if key == "Comments": + self.assertDictEqual( + json.loads(sd.header.comments), value + ) + else: + self.assertEqual( + getattr(sd.header, specification.attribute), value + ) + + +class TestSpectralDistributionUprTek(AbstractSpectralDistributionTest): + """ + Define :class:`colour.SpectralDistribution_UPRTek` class unit tests + methods. + """ + + def __init__(self, *args: Any): + """ + Create an instance of the class. + + Other Parameters + ---------------- + args + Arguments. + """ + + super().__init__(*args) + + self._sd_factory = SpectralDistribution_UPRTek + self._path = "ESPD2021_0104_231446.xls" + self._spectral_data = { + 380: 0.030267, + 381: 0.030267, + 382: 0.030267, + 383: 0.029822, + 384: 0.028978, + 385: 0.028623, + 386: 0.030845, + 387: 0.035596, + 388: 0.039231, + 389: 0.039064, + 390: 0.035223, + 391: 0.031580, + 392: 0.029181, + 393: 0.027808, + 394: 0.026256, + 395: 0.024526, + 396: 0.022557, + 397: 0.020419, + 398: 0.018521, + 399: 0.018149, + 400: 0.019325, + 401: 0.021666, + 402: 0.024045, + 403: 0.026473, + 404: 0.029076, + 405: 0.031840, + 406: 0.033884, + 407: 0.034038, + 408: 0.032302, + 409: 0.030383, + 410: 0.029426, + 411: 0.029979, + 412: 0.032614, + 413: 0.037204, + 414: 0.042279, + 415: 0.046029, + 416: 0.048698, + 417: 0.053064, + 418: 0.059530, + 419: 0.070840, + 420: 0.087678, + 421: 0.110043, + 422: 0.136705, + 423: 0.165180, + 424: 0.199071, + 425: 0.241976, + 426: 0.293837, + 427: 0.359177, + 428: 0.434192, + 429: 0.523828, + 430: 0.632578, + 431: 0.758893, + 432: 0.915528, + 433: 1.096489, + 434: 1.307487, + 435: 1.557125, + 436: 1.838779, + 437: 2.183382, + 438: 2.586251, + 439: 3.054022, + 440: 3.625659, + 441: 4.279538, + 442: 5.055838, + 443: 5.919301, + 444: 6.869926, + 445: 7.940298, + 446: 9.090219, + 447: 10.336670, + 448: 11.619895, + 449: 12.939739, + 450: 14.206918, + 451: 15.396660, + 452: 16.430536, + 453: 17.267374, + 454: 17.912292, + 455: 18.261185, + 456: 18.404581, + 457: 18.288025, + 458: 18.002302, + 459: 17.570372, + 460: 17.011297, + 461: 16.411137, + 462: 15.779440, + 463: 15.168951, + 464: 14.585364, + 465: 14.057872, + 466: 13.575768, + 467: 13.144953, + 468: 12.737307, + 469: 12.346188, + 470: 11.967313, + 471: 11.590308, + 472: 11.209807, + 473: 10.815372, + 474: 10.406748, + 475: 10.007284, + 476: 9.627886, + 477: 9.279286, + 478: 8.958391, + 479: 8.663115, + 480: 8.427362, + 481: 8.238759, + 482: 8.110200, + 483: 8.011048, + 484: 7.939125, + 485: 7.900343, + 486: 7.880703, + 487: 7.887271, + 488: 7.907047, + 489: 7.939895, + 490: 7.977298, + 491: 8.013443, + 492: 8.056756, + 493: 8.112617, + 494: 8.181398, + 495: 8.256148, + 496: 8.332609, + 497: 8.418014, + 498: 8.513148, + 499: 8.616785, + 500: 8.719036, + 501: 8.817776, + 502: 8.914417, + 503: 9.011255, + 504: 9.105255, + 505: 9.193217, + 506: 9.274889, + 507: 9.350751, + 508: 9.423820, + 509: 9.490992, + 510: 9.553215, + 511: 9.608335, + 512: 9.653841, + 513: 9.691347, + 514: 9.727146, + 515: 9.767722, + 516: 9.809064, + 517: 9.842565, + 518: 9.867527, + 519: 9.887219, + 520: 9.906105, + 521: 9.920433, + 522: 9.929304, + 523: 9.932856, + 524: 9.935204, + 525: 9.937991, + 526: 9.938448, + 527: 9.936127, + 528: 9.930192, + 529: 9.922665, + 530: 9.913944, + 531: 9.905774, + 532: 9.898767, + 533: 9.894219, + 534: 9.891479, + 535: 9.883711, + 536: 9.862693, + 537: 9.829168, + 538: 9.795257, + 539: 9.767633, + 540: 9.747380, + 541: 9.729669, + 542: 9.714886, + 543: 9.701355, + 544: 9.688311, + 545: 9.673670, + 546: 9.657027, + 547: 9.633310, + 548: 9.603127, + 549: 9.567823, + 550: 9.534049, + 551: 9.504526, + 552: 9.484178, + 553: 9.471739, + 554: 9.455969, + 555: 9.429557, + 556: 9.396450, + 557: 9.368848, + 558: 9.344832, + 559: 9.313942, + 560: 9.273922, + 561: 9.240767, + 562: 9.220987, + 563: 9.210749, + 564: 9.195800, + 565: 9.173392, + 566: 9.143906, + 567: 9.109710, + 568: 9.078232, + 569: 9.052593, + 570: 9.023234, + 571: 8.984895, + 572: 8.950663, + 573: 8.935179, + 574: 8.936305, + 575: 8.937272, + 576: 8.931671, + 577: 8.921451, + 578: 8.910289, + 579: 8.908619, + 580: 8.917888, + 581: 8.934530, + 582: 8.946784, + 583: 8.958764, + 584: 8.979334, + 585: 9.007913, + 586: 9.033543, + 587: 9.051113, + 588: 9.067842, + 589: 9.089899, + 590: 9.114546, + 591: 9.136106, + 592: 9.164270, + 593: 9.207536, + 594: 9.264211, + 595: 9.321528, + 596: 9.371778, + 597: 9.411209, + 598: 9.443729, + 599: 9.490623, + 600: 9.557871, + 601: 9.626752, + 602: 9.674832, + 603: 9.705856, + 604: 9.739429, + 605: 9.784062, + 606: 9.841268, + 607: 9.907084, + 608: 9.971845, + 609: 10.026823, + 610: 10.060076, + 611: 10.076903, + 612: 10.105914, + 613: 10.161287, + 614: 10.230108, + 615: 10.285982, + 616: 10.336598, + 617: 10.396016, + 618: 10.449015, + 619: 10.478296, + 620: 10.484620, + 621: 10.487537, + 622: 10.498996, + 623: 10.519572, + 624: 10.541495, + 625: 10.549863, + 626: 10.543288, + 627: 10.538241, + 628: 10.546865, + 629: 10.560687, + 630: 10.567954, + 631: 10.564369, + 632: 10.555919, + 633: 10.542054, + 634: 10.527417, + 635: 10.513332, + 636: 10.500641, + 637: 10.493341, + 638: 10.491714, + 639: 10.477033, + 640: 10.435987, + 641: 10.374922, + 642: 10.317416, + 643: 10.269583, + 644: 10.220937, + 645: 10.168004, + 646: 10.115719, + 647: 10.061740, + 648: 9.998492, + 649: 9.919030, + 650: 9.821223, + 651: 9.716800, + 652: 9.619915, + 653: 9.531602, + 654: 9.435769, + 655: 9.326644, + 656: 9.215940, + 657: 9.111384, + 658: 9.005102, + 659: 8.892046, + 660: 8.775783, + 661: 8.659118, + 662: 8.537835, + 663: 8.413469, + 664: 8.292587, + 665: 8.175849, + 666: 8.055606, + 667: 7.931369, + 668: 7.812479, + 669: 7.695505, + 670: 7.564718, + 671: 7.422195, + 672: 7.286375, + 673: 7.166087, + 674: 7.050159, + 675: 6.925609, + 676: 6.792675, + 677: 6.659946, + 678: 6.534333, + 679: 6.416044, + 680: 6.298086, + 681: 6.182296, + 682: 6.073105, + 683: 5.965933, + 684: 5.853682, + 685: 5.729931, + 686: 5.599877, + 687: 5.480670, + 688: 5.376213, + 689: 5.273221, + 690: 5.156234, + 691: 5.027091, + 692: 4.900242, + 693: 4.777046, + 694: 4.658288, + 695: 4.547010, + 696: 4.443560, + 697: 4.347722, + 698: 4.252159, + 699: 4.152643, + 700: 4.053906, + 701: 3.961853, + 702: 3.865061, + 703: 3.755302, + 704: 3.634861, + 705: 3.519360, + 706: 3.418803, + 707: 3.328571, + 708: 3.246458, + 709: 3.160225, + 710: 3.066386, + 711: 2.970290, + 712: 2.878098, + 713: 2.790311, + 714: 2.701265, + 715: 2.607646, + 716: 2.515490, + 717: 2.435313, + 718: 2.361505, + 719: 2.282271, + 720: 2.192500, + 721: 2.101594, + 722: 2.027356, + 723: 1.966553, + 724: 1.912948, + 725: 1.855193, + 726: 1.785138, + 727: 1.710667, + 728: 1.638785, + 729: 1.582385, + 730: 1.539228, + 731: 1.498548, + 732: 1.455407, + 733: 1.413034, + 734: 1.372021, + 735: 1.324772, + 736: 1.277157, + 737: 1.238888, + 738: 1.211113, + 739: 1.182541, + 740: 1.149382, + 741: 1.118490, + 742: 1.091204, + 743: 1.065539, + 744: 1.039564, + 745: 1.013148, + 746: 0.990818, + 747: 0.976522, + 748: 0.960074, + 749: 0.935639, + 750: 0.905095, + 751: 0.878893, + 752: 0.862828, + 753: 0.847588, + 754: 0.829938, + 755: 0.808772, + 756: 0.786338, + 757: 0.761752, + 758: 0.735873, + 759: 0.711232, + 760: 0.690947, + 761: 0.673476, + 762: 0.659236, + 763: 0.646735, + 764: 0.633802, + 765: 0.612864, + 766: 0.589102, + 767: 0.567989, + 768: 0.551288, + 769: 0.533479, + 770: 0.508426, + 771: 0.487143, + 772: 0.474126, + 773: 0.465145, + 774: 0.455158, + 775: 0.442994, + 776: 0.429114, + 777: 0.419402, + 778: 0.411766, + 779: 0.411766, + 780: 0.411766, + } + + self._header = { + "Manufacturer": "UPRTek", + "CatalogNumber": None, + "Description": None, + "DocumentCreator": None, + "UniqueIdentifier": None, + "MeasurementEquipment": "CV600", + "Laboratory": None, + "ReportNumber": None, + "ReportDate": "2021/01/04_23:14:46", + "DocumentCreationDate": None, + "Comments": { + "Model Name": "CV600", + "Serial Number": "19J00789", + "Time": "2021/01/04_23:14:46", + "Memo": [], + "LUX": 695.154907, + "fc": 64.605476, + "CCT": 5198.0, + "Duv": -0.00062, + "I-Time": 12000.0, + "X": 682.470886, + "Y": 695.154907, + "Z": 631.635071, + "x": 0.339663, + "y": 0.345975, + "u'": 0.209915, + "v'": 0.481087, + "LambdaP": 456.0, + "LambdaPValue": 18.404581, + "CRI": 92.956993, + "R1": 91.651062, + "R2": 93.014732, + "R3": 97.032013, + "R4": 93.513229, + "R5": 92.48259, + "R6": 91.48687, + "R7": 93.016129, + "R8": 91.459312, + "R9": 77.613075, + "R10": 86.981613, + "R11": 94.841324, + "R12": 74.139542, + "R13": 91.073837, + "R14": 97.064323, + "R15": 88.615669, + "TLCI": 97.495056, + "TLMF-A": 1.270032, + "SSI-A": 44.881924, + "Rf": 87.234917, + "Rg": 98.510712, + "IRR": 2.607891, + }, + } + + +class TestSpectralDistributionSekonic(AbstractSpectralDistributionTest): + """ + Define :class:`colour.SpectralDistribution_Sekonic` class unit tests + methods. + """ + + def __init__(self, *args: Any): + """ + Create an instance of the class. + + Other Parameters + ---------------- + args + Arguments. + """ + + super().__init__(*args) + + self._sd_factory = SpectralDistribution_Sekonic + self._path = "RANDOM_001_02._3262K.csv" + self._spectral_data = { + 380: 0.000000000000, + 381: 0.000000000000, + 382: 0.000000000000, + 383: 0.000000000000, + 384: 0.000000000000, + 385: 0.000000000000, + 386: 0.000000000000, + 387: 0.000000000000, + 388: 0.000000000000, + 389: 0.000000000000, + 390: 0.000000000000, + 391: 0.000000000000, + 392: 0.000002927853, + 393: 0.000006502053, + 394: 0.000009265275, + 395: 0.000011032038, + 396: 0.000011953731, + 397: 0.000012279555, + 398: 0.000012258756, + 399: 0.000012112181, + 400: 0.000011981365, + 401: 0.000011995159, + 402: 0.000012281144, + 403: 0.000012880828, + 404: 0.000013697349, + 405: 0.000014621435, + 406: 0.000015547508, + 407: 0.000016454918, + 408: 0.000017407952, + 409: 0.000018474588, + 410: 0.000019711053, + 411: 0.000021048536, + 412: 0.000022339967, + 413: 0.000023436902, + 414: 0.000024226094, + 415: 0.000024806555, + 416: 0.000025354624, + 417: 0.000026046688, + 418: 0.000027027134, + 419: 0.000028330132, + 420: 0.000029966144, + 421: 0.000031945645, + 422: 0.000034267265, + 423: 0.000036904559, + 424: 0.000039828374, + 425: 0.000043010186, + 426: 0.000046453275, + 427: 0.000050200390, + 428: 0.000054296306, + 429: 0.000058792350, + 430: 0.000063819272, + 431: 0.000069569738, + 432: 0.000076238801, + 433: 0.000084002051, + 434: 0.000092899616, + 435: 0.000102907434, + 436: 0.000114000723, + 437: 0.000126147745, + 438: 0.000139350668, + 439: 0.000153605943, + 440: 0.000168909683, + 441: 0.000185196404, + 442: 0.000202212090, + 443: 0.000219666821, + 444: 0.000237270768, + 445: 0.000254752871, + 446: 0.000271882804, + 447: 0.000288435520, + 448: 0.000304183195, + 449: 0.000318816456, + 450: 0.000331902935, + 451: 0.000342996238, + 452: 0.000351659779, + 453: 0.000357679965, + 454: 0.000361089711, + 455: 0.000361937127, + 456: 0.000360277918, + 457: 0.000356289936, + 458: 0.000350250222, + 459: 0.000342438580, + 460: 0.000333143020, + 461: 0.000322732056, + 462: 0.000311622134, + 463: 0.000300230284, + 464: 0.000288942829, + 465: 0.000277946005, + 466: 0.000267342635, + 467: 0.000257235020, + 468: 0.000247702759, + 469: 0.000238719338, + 470: 0.000230227481, + 471: 0.000222169925, + 472: 0.000214497733, + 473: 0.000207189034, + 474: 0.000200227427, + 475: 0.000193596818, + 476: 0.000187307058, + 477: 0.000181425072, + 478: 0.000176026821, + 479: 0.000171187712, + 480: 0.000166981976, + 481: 0.000163483521, + 482: 0.000160765063, + 483: 0.000158896932, + 484: 0.000157875169, + 485: 0.000157608956, + 486: 0.000158002527, + 487: 0.000158960844, + 488: 0.000160401178, + 489: 0.000162251439, + 490: 0.000164439844, + 491: 0.000166898695, + 492: 0.000169602441, + 493: 0.000172551969, + 494: 0.000175748704, + 495: 0.000179197523, + 496: 0.000182933160, + 497: 0.000187002632, + 498: 0.000191452826, + 499: 0.000196314068, + 500: 0.000201534538, + 501: 0.000207037185, + 502: 0.000212744897, + 503: 0.000218581801, + 504: 0.000224477379, + 505: 0.000230361940, + 506: 0.000236165870, + 507: 0.000241834379, + 508: 0.000247346645, + 509: 0.000252687139, + 510: 0.000257840526, + 511: 0.000262814428, + 512: 0.000267655065, + 513: 0.000272412435, + 514: 0.000277135783, + 515: 0.000281845685, + 516: 0.000286527647, + 517: 0.000291164964, + 518: 0.000295740523, + 519: 0.000300232059, + 520: 0.000304612651, + 521: 0.000308855029, + 522: 0.000312933233, + 523: 0.000316833000, + 524: 0.000320547697, + 525: 0.000324070978, + 526: 0.000327409187, + 527: 0.000330665527, + 528: 0.000333987991, + 529: 0.000337524747, + 530: 0.000341368344, + 531: 0.000345327600, + 532: 0.000349117006, + 533: 0.000352450879, + 534: 0.000355126103, + 535: 0.000357231562, + 536: 0.000358921068, + 537: 0.000360348407, + 538: 0.000361620390, + 539: 0.000362726772, + 540: 0.000363639323, + 541: 0.000364331092, + 542: 0.000364891835, + 543: 0.000365620159, + 544: 0.000366836379, + 545: 0.000368854904, + 546: 0.000371746690, + 547: 0.000375265605, + 548: 0.000379145116, + 549: 0.000383122213, + 550: 0.000387050648, + 551: 0.000390928035, + 552: 0.000394761097, + 553: 0.000398556062, + 554: 0.000402294856, + 555: 0.000405925355, + 556: 0.000409392873, + 557: 0.000412643829, + 558: 0.000415688555, + 559: 0.000418639625, + 560: 0.000421619130, + 561: 0.000424748578, + 562: 0.000428094878, + 563: 0.000431627472, + 564: 0.000435305585, + 565: 0.000439088471, + 566: 0.000442934950, + 567: 0.000446803198, + 568: 0.000450651161, + 569: 0.000454437046, + 570: 0.000458150520, + 571: 0.000461855903, + 572: 0.000465628196, + 573: 0.000469542429, + 574: 0.000473651045, + 575: 0.000477944268, + 576: 0.000482402043, + 577: 0.000487004407, + 578: 0.000491718296, + 579: 0.000496469554, + 580: 0.000501176575, + 581: 0.000505757635, + 582: 0.000510152080, + 583: 0.000514372950, + 584: 0.000518449873, + 585: 0.000522412360, + 586: 0.000526284566, + 587: 0.000530070101, + 588: 0.000533766986, + 589: 0.000537373126, + 590: 0.000540883630, + 591: 0.000544285693, + 592: 0.000547563192, + 593: 0.000550700177, + 594: 0.000553691818, + 595: 0.000556585495, + 596: 0.000559442851, + 597: 0.000562325818, + 598: 0.000565279392, + 599: 0.000568273535, + 600: 0.000571256795, + 601: 0.000574177830, + 602: 0.000576974649, + 603: 0.000579536776, + 604: 0.000581740285, + 605: 0.000583461253, + 606: 0.000584599038, + 607: 0.000585157890, + 608: 0.000585171976, + 609: 0.000584675174, + 610: 0.000583703280, + 611: 0.000582299544, + 612: 0.000580509542, + 613: 0.000578378676, + 614: 0.000575953862, + 615: 0.000573287893, + 616: 0.000570435368, + 617: 0.000567450887, + 618: 0.000564369780, + 619: 0.000561140885, + 620: 0.000557688472, + 621: 0.000553937047, + 622: 0.000549851626, + 623: 0.000545581162, + 624: 0.000541326357, + 625: 0.000537287910, + 626: 0.000533593295, + 627: 0.000530039892, + 628: 0.000526331889, + 629: 0.000522173534, + 630: 0.000517328095, + 631: 0.000511825143, + 632: 0.000505769160, + 633: 0.000499264686, + 634: 0.000492379884, + 635: 0.000485043478, + 636: 0.000477139401, + 637: 0.000468551356, + 638: 0.000459251489, + 639: 0.000449585932, + 640: 0.000439994939, + 641: 0.000430918619, + 642: 0.000422663987, + 643: 0.000415024377, + 644: 0.000407667656, + 645: 0.000400261633, + 646: 0.000392578833, + 647: 0.000384767627, + 648: 0.000377058517, + 649: 0.000369681919, + 650: 0.000362766819, + 651: 0.000356107164, + 652: 0.000349425798, + 653: 0.000342445448, + 654: 0.000335026474, + 655: 0.000327456160, + 656: 0.000320101273, + 657: 0.000313328317, + 658: 0.000307335460, + 659: 0.000301838503, + 660: 0.000296465587, + 661: 0.000290844997, + 662: 0.000284782291, + 663: 0.000278556399, + 664: 0.000272522098, + 665: 0.000267032796, + 666: 0.000262254383, + 667: 0.000257897831, + 668: 0.000253598962, + 669: 0.000248999364, + 670: 0.000243966802, + 671: 0.000238797031, + 672: 0.000233855622, + 673: 0.000229498852, + 674: 0.000225782627, + 675: 0.000222411400, + 676: 0.000219070076, + 677: 0.000215468172, + 678: 0.000211623279, + 679: 0.000207766803, + 680: 0.000204134776, + 681: 0.000200916242, + 682: 0.000197999922, + 683: 0.000195158325, + 684: 0.000192163920, + 685: 0.000188884194, + 686: 0.000185509256, + 687: 0.000182299933, + 688: 0.000179515657, + 689: 0.000177253518, + 690: 0.000175304012, + 691: 0.000173423585, + 692: 0.000171374879, + 693: 0.000169089981, + 694: 0.000166684200, + 695: 0.000164281839, + 696: 0.000161995718, + 697: 0.000159809686, + 698: 0.000157624905, + 699: 0.000155341069, + 700: 0.000152887544, + 701: 0.000150368738, + 702: 0.000147950719, + 703: 0.000145799495, + 704: 0.000143992351, + 705: 0.000142327044, + 706: 0.000140546414, + 707: 0.000138393327, + 708: 0.000135762792, + 709: 0.000132830304, + 710: 0.000129795619, + 711: 0.000126856787, + 712: 0.000124101163, + 713: 0.000121442732, + 714: 0.000118780568, + 715: 0.000116016025, + 716: 0.000113144888, + 717: 0.000110295317, + 718: 0.000107605832, + 719: 0.000105211519, + 720: 0.000103122693, + 721: 0.000101195699, + 722: 0.000099277633, + 723: 0.000097221695, + 724: 0.000095040108, + 725: 0.000092921349, + 726: 0.000091063630, + 727: 0.000089657653, + 728: 0.000088729350, + 729: 0.000088144145, + 730: 0.000087760782, + 731: 0.000087439126, + 732: 0.000087065731, + 733: 0.000086550441, + 734: 0.000085803600, + 735: 0.000084741441, + 736: 0.000083366656, + 737: 0.000081748578, + 738: 0.000079958285, + 739: 0.000078067504, + 740: 0.000076152413, + 741: 0.000074292504, + 742: 0.000072567469, + 743: 0.000071058574, + 744: 0.000069874128, + 745: 0.000069137976, + 746: 0.000068973786, + 747: 0.000069459609, + 748: 0.000070268186, + 749: 0.000070849754, + 750: 0.000070651688, + 751: 0.000069174901, + 752: 0.000066329500, + 753: 0.000062221166, + 754: 0.000056957157, + 755: 0.000050740506, + 756: 0.000044398927, + 757: 0.000039030732, + 758: 0.000035736208, + 759: 0.000035360736, + 760: 0.000037219921, + 761: 0.000040070787, + 762: 0.000042669857, + 763: 0.000043976099, + 764: 0.000044012377, + 765: 0.000043148953, + 766: 0.000041756259, + 767: 0.000040175455, + 768: 0.000038621456, + 769: 0.000037272272, + 770: 0.000036305886, + 771: 0.000035866044, + 772: 0.000035955240, + 773: 0.000036541740, + 774: 0.000037593938, + 775: 0.000038985072, + 776: 0.000040247214, + 777: 0.000040842820, + 778: 0.000040234852, + 779: 0.000038216305, + 780: 0.000035575547, + } + self._header = { + "Manufacturer": "Sekonic", + "CatalogNumber": None, + "Description": None, + "DocumentCreator": None, + "UniqueIdentifier": None, + "MeasurementEquipment": None, + "Laboratory": None, + "ReportNumber": None, + "ReportDate": "15/03/2021 3:44:14 p.m.", + "DocumentCreationDate": None, + "Comments": { + "Date Saved": "15/03/2021 3:44:14 p.m.", + "Title": "RANDOM_001_02°_3262K", + "Measuring Mode": "Ambient", + "Viewing Angle [°]": 2, + "Tcp [K]": 3262, + "⊿uv": -0.0029, + "Illuminance [lx]": 30.1, + "Illuminance [fc]": 2.79, + "Peak Wavelength [nm]": 608, + "Tristimulus Value X": 32.1626, + "Tristimulus Value Y": 30.0794, + "Tristimulus Value Z": 15.0951, + "CIE1931 x": 0.4159, + "CIE1931 y": 0.3889, + "CIE1931 z": 0.1952, + "CIE1976 u'": 0.2434, + "CIE1976 v'": 0.5121, + "Dominant Wavelength [nm]": 583, + "Purity [%]": 41.5, + "PPFD [umolm⁻²s⁻¹]": 0.4, + "CRI Ra": 87.5, + "CRI R1": 87.6, + "CRI R2": 94.5, + "CRI R3": 96.8, + "CRI R4": 85.8, + "CRI R5": 87.3, + "CRI R6": 92.3, + "CRI R7": 86.4, + "CRI R8": 69.8, + "CRI R9": 31.2, + "CRI R10": 85.6, + "CRI R11": 85.1, + "CRI R12": 75.6, + "CRI R13": 89.6, + "CRI R14": 98.8, + "CRI R15": 82.5, + "TM-30 Rf": 87, + "TM-30 Rg": 98, + "SSIt": 76, + "SSId": 59, + "SSI1": "---", + "SSI2": "---", + "TLCI": 79, + "TLMF": "---", + "TM-30 Color Vector Graphic": [ + "Reference Illuminant x", + "Reference Illuminant y", + "Measured Illuminant x", + "Measured Illuminant y", + ], + "bin1": [0.9764469, 0.2157578, 0.8882475, 0.2021859], + "bin2": [0.7906278, 0.6122971, 0.7113284, 0.6248878], + "bin3": [0.5509713, 0.8345242, 0.4676899, 0.8666077], + "bin4": [0.1428891, 0.9897387, 0.0935279, 1.002316], + "bin5": [-0.176162, 0.9843612, -0.2043247, 0.9795201], + "bin6": [-0.5853095, 0.81081, -0.5838909, 0.8375309], + "bin7": [-0.7960986, 0.6051669, -0.7457092, 0.6149487], + "bin8": [-0.951027, 0.309108, -0.9191595, 0.309686], + "bin9": [-0.9854512, -0.1699584, -0.9329426, -0.2097975], + "bin10": [-0.8461911, -0.5328795, -0.7660208, -0.6001526], + "bin11": [-0.5824577, -0.812861, -0.4902966, -0.8897363], + "bin12": [-0.2939128, -0.9558322, -0.2872024, -1.03006], + "bin13": [0.1462545, -0.989247, 0.1026697, -1.040349], + "bin14": [0.508388, -0.8611281, 0.4397461, -0.9682071], + "bin15": [0.8469644, -0.5316497, 0.7729813, -0.6153884], + "bin16": [0.9788596, -0.2045332, 0.9110764, -0.2976203], + }, + "SpectralQuantity": "Irradiance", + } + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/io/tests/test_xrite.py b/colour/io/tests/test_xrite.py index feb5eddfbf..d9fa8eb814 100644 --- a/colour/io/tests/test_xrite.py +++ b/colour/io/tests/test_xrite.py @@ -1,30 +1,30 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.io.xrite` module. -""" +"""Defines the unit tests for the :mod:`colour.io.xrite` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import os import unittest from colour.colorimetry import SpectralDistribution +from colour.hints import Dict from colour.io import read_sds_from_xrite_file -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'RESOURCES_DIRECTORY', 'COLOURCHECKER_XRITE_1', 'TestReadSdsFromXRiteFile' + "RESOURCES_DIRECTORY", + "COLOURCHECKER_XRITE_1", + "TestReadSdsFromXRiteFile", ] -RESOURCES_DIRECTORY = os.path.join(os.path.dirname(__file__), 'resources') +RESOURCES_DIRECTORY: str = os.path.join(os.path.dirname(__file__), "resources") -COLOURCHECKER_XRITE_1 = { +COLOURCHECKER_XRITE_1: Dict = { 380.0: 0.0069, 390.0: 0.0069, 400.0: 0.0068, @@ -60,30 +60,30 @@ 700.0: 0.0066, 710.0: 0.0066, 720.0: 0.0066, - 730.0: 0.0065 + 730.0: 0.0065, } class TestReadSdsFromXRiteFile(unittest.TestCase): """ - Defines :func:`colour.io.xrite.read_sds_from_xrite_file` definition units + Define :func:`colour.io.xrite.read_sds_from_xrite_file` definition unit tests methods. """ def test_read_sds_from_xrite_file(self): - """ - Tests :func:`colour.io.xrite.read_sds_from_xrite_file` definition. - """ + """Test :func:`colour.io.xrite.read_sds_from_xrite_file` definition.""" colour_checker_xrite = os.path.join( - RESOURCES_DIRECTORY, 'X-Rite_Digital_Colour_Checker.txt') + RESOURCES_DIRECTORY, "X-Rite_Digital_Colour_Checker.txt" + ) sds = read_sds_from_xrite_file(colour_checker_xrite) for sd in sds.values(): self.assertIsInstance(sd, SpectralDistribution) self.assertEqual( - sds['X1'], SpectralDistribution(COLOURCHECKER_XRITE_1, name='X1')) + sds["X1"], SpectralDistribution(COLOURCHECKER_XRITE_1, name="X1") + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/io/tm2714.py b/colour/io/tm2714.py index e44d1c264c..4d80575bbb 100644 --- a/colour/io/tm2714.py +++ b/colour/io/tm2714.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ IES TM-27-14 Data Input / Output ================================ Defines the :class:`colour.SpectralDistribution_IESTM2714` class handling *IES -TM-27-14* spectral data XML files. +TM-27-14* spectral data *XML* files. References ---------- @@ -14,108 +13,120 @@ Engineering Society. ISBN:978-0-87995-295-2 """ -from __future__ import division, unicode_literals +from __future__ import annotations import os import re -from collections import namedtuple +from dataclasses import dataclass, field from xml.etree import ElementTree # nosec from xml.dom import minidom # nosec from colour.colorimetry import SpectralDistribution -from colour.constants import DEFAULT_FLOAT_DTYPE -from colour.utilities import Structure, is_numeric, is_string, tstack - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import ( + Any, + ArrayLike, + Boolean, + Callable, + Floating, + Integer, + Literal, + Optional, + cast, +) +from colour.utilities import ( + Structure, + as_float_array, + as_float_scalar, + attest, + optional, + is_numeric, + is_string, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'VERSION_IESTM2714', 'NAMESPACE_IESTM2714', - 'Element_Specification_IESTM2714', 'Header_IESTM2714', - 'SpectralDistribution_IESTM2714' + "VERSION_IESTM2714", + "NAMESPACE_IESTM2714", + "Element_Specification_IESTM2714", + "Header_IESTM2714", + "SpectralDistribution_IESTM2714", ] -VERSION_IESTM2714 = '1.0' -NAMESPACE_IESTM2714 = 'http://www.ies.org/iestm2714' +VERSION_IESTM2714: str = "1.0" + +NAMESPACE_IESTM2714: str = "http://www.ies.org/iestm2714" -class Element_Specification_IESTM2714( - namedtuple('Element_Specification_IESTM2714', - ('element', 'attribute', 'type', 'required', - 'read_conversion', 'write_conversion'))): +@dataclass +class Element_Specification_IESTM2714: """ - *IES TM-27-14* spectral data XML file element specification. + *IES TM-27-14* spectral data *XML* file element specification. Parameters ---------- - element : unicode + element Element name. - attribute : unicode + attribute Associated attribute name. - type_ : unicode + type_ Element type. - required : bool + required Is element required. - read_conversion : object - Method to convert from XML to type on reading. - write_conversion : object - Method to convert from type to XML on writing. + read_conversion + Method to convert from *XML* to type on reading. + write_conversion + Method to convert from type to *XML* on writing. """ - def __new__(cls, - element, - attribute, - type_=str, - required=False, - read_conversion=format, - write_conversion=( - lambda x: format(x) if x is not None else 'N/A')): - """ - Returns a new instance of the - :class:`colour.io.ies_tm2714.IES_TM2714_Element` class. - """ - - return super(Element_Specification_IESTM2714, cls).__new__( - cls, element, attribute, type_, required, read_conversion, - write_conversion) + element: str + attribute: str + type_: Any = field(default_factory=str) + required: Boolean = field(default_factory=lambda: False) + read_conversion: Callable = field( + default_factory=lambda: lambda x: None if x == "None" else str(x) + ) + write_conversion: Callable = field(default_factory=lambda: str) -class Header_IESTM2714(object): +class Header_IESTM2714: """ - Defines the header object for a *IES TM-27-14* spectral distribution. + Define the header object for a *IES TM-27-14* spectral distribution. Parameters ---------- - manufacturer : unicode, optional + manufacturer Manufacturer of the device under test. - catalog_number : unicode, optional + catalog_number Manufacturer's product catalog number. - description : unicode, optional - Description of the spectral data in the spectral data XML file. - document_creator : unicode, optional - Creator of the spectral data XML file, which may be a + description + Description of the spectral data in the spectral data *XML* file. + document_creator + Creator of the spectral data *XML* file, which may be a test lab, a research group, a standard body, a company or an individual. - unique_identifier : unicode, optional + unique_identifier Unique identifier to the product under test or the spectral data in the document. - measurement_equipment : unicode, optional + measurement_equipment Description of the equipment used to measure the spectral data. - laboratory : unicode, optional + laboratory Testing laboratory name that performed the spectral data measurements. - report_number : unicode, optional + report_number Testing laboratory report number. - report_date : unicode, optional + report_date Testing laboratory report date using the *XML DateTime Data Type*, *YYYY-MM-DDThh:mm:ss*. - document_creation_date : unicode, optional - Spectral data XML file creation date using the + document_creation_date + Spectral data *XML* file creation date using the *XML DateTime Data Type*, *YYYY-MM-DDThh:mm:ss*. - comments : unicode, optional + comments Additional information relating to the tested and reported data. Attributes @@ -136,6 +147,9 @@ class Header_IESTM2714(object): Methods ------- - :meth:`~colour.io.ies_tm2714.Header_IESTM2714.__init__` + - :meth:`~colour.io.ies_tm2714.Header_IESTM2714.__hash__` + - :meth:`~colour.io.ies_tm2714.Header_IESTM2714.__eq__` + - :meth:`~colour.io.ies_tm2714.Header_IESTM2714.__ne__` Examples -------- @@ -145,471 +159,569 @@ class Header_IESTM2714(object): 'colour-science' """ - def __init__(self, - manufacturer=None, - catalog_number=None, - description=None, - document_creator=None, - unique_identifier=None, - measurement_equipment=None, - laboratory=None, - report_number=None, - report_date=None, - document_creation_date=None, - comments=None): - - self._mapping = Structure( + def __init__( + self, + manufacturer: Optional[str] = None, + catalog_number: Optional[str] = None, + description: Optional[str] = None, + document_creator: Optional[str] = None, + unique_identifier: Optional[str] = None, + measurement_equipment: Optional[str] = None, + laboratory: Optional[str] = None, + report_number: Optional[str] = None, + report_date: Optional[str] = None, + document_creation_date: Optional[str] = None, + comments: Optional[str] = None, + ): + + self._mapping: Structure = Structure( **{ - 'element': - 'Header', - 'elements': - (Element_Specification_IESTM2714('Manufacturer', - 'manufacturer'), - Element_Specification_IESTM2714('CatalogNumber', - 'catalog_number'), - Element_Specification_IESTM2714( - 'Description', 'description', required=True), - Element_Specification_IESTM2714( - 'DocumentCreator', 'document_creator', required=True), - Element_Specification_IESTM2714('UniqueIdentifier', - 'unique_identifier'), - Element_Specification_IESTM2714('MeasurementEquipment', - 'measurement_equipment'), - Element_Specification_IESTM2714('Laboratory', - 'laboratory'), - Element_Specification_IESTM2714('ReportNumber', - 'report_number'), - Element_Specification_IESTM2714('ReportDate', - 'report_date'), - Element_Specification_IESTM2714( - 'DocumentCreationDate', - 'document_creation_date', - required=True), - Element_Specification_IESTM2714('Comments', 'comments', - False)) - }) - - self._manufacturer = None + "element": "Header", + "elements": ( + Element_Specification_IESTM2714( + "Manufacturer", "manufacturer" + ), + Element_Specification_IESTM2714( + "CatalogNumber", "catalog_number" + ), + Element_Specification_IESTM2714( + "Description", "description", required=True + ), + Element_Specification_IESTM2714( + "DocumentCreator", "document_creator", required=True + ), + Element_Specification_IESTM2714( + "UniqueIdentifier", "unique_identifier" + ), + Element_Specification_IESTM2714( + "MeasurementEquipment", "measurement_equipment" + ), + Element_Specification_IESTM2714( + "Laboratory", "laboratory" + ), + Element_Specification_IESTM2714( + "ReportNumber", "report_number" + ), + Element_Specification_IESTM2714( + "ReportDate", "report_date" + ), + Element_Specification_IESTM2714( + "DocumentCreationDate", + "document_creation_date", + required=True, + ), + Element_Specification_IESTM2714( + "Comments", "comments", False + ), + ), + } + ) + + self._manufacturer: Optional[str] = None self.manufacturer = manufacturer - self._catalog_number = None + self._catalog_number: Optional[str] = None self.catalog_number = catalog_number - self._description = None + self._description: Optional[str] = None self.description = description - self._document_creator = None + self._document_creator: Optional[str] = None self.document_creator = document_creator - self._unique_identifier = None + self._unique_identifier: Optional[str] = None self.unique_identifier = unique_identifier - self._measurement_equipment = None + self._measurement_equipment: Optional[str] = None self.measurement_equipment = measurement_equipment - self._laboratory = None + self._laboratory: Optional[str] = None self.laboratory = laboratory - self._report_number = None + self._report_number: Optional[str] = None self.report_number = report_number - self._report_date = None + self._report_date: Optional[str] = None self.report_date = report_date - self._document_creation_date = None + self._document_creation_date: Optional[str] = None self.document_creation_date = document_creation_date - self._comments = None + self._comments: Optional[str] = None self.comments = comments @property - def mapping(self): + def mapping(self) -> Structure: """ Getter property for the mapping structure. Returns ------- - Structure + :class:`colour.utilities.Structure` Mapping structure. """ return self._mapping @property - def manufacturer(self): + def manufacturer(self) -> Optional[str]: """ Getter and setter property for the manufacturer. Parameters ---------- - value : unicode + value Value to set the manufacturer with. Returns ------- - unicode + :py:data:`None` or :class:`str` Manufacturer. """ return self._manufacturer @manufacturer.setter - def manufacturer(self, value): - """ - Setter for the **self.manufacturer** property. - """ + def manufacturer(self, value: Optional[str]): + """Setter for the **self.manufacturer** property.""" if value is not None: - assert is_string(value), ( - '"{0}" attribute: "{1}" is not a "string" like object!'.format( - 'manufacturer', value)) + attest( + is_string(value), + f'"manufacturer" property: "{value}" type is not "str"!', + ) + self._manufacturer = value @property - def catalog_number(self): + def catalog_number(self) -> Optional[str]: """ Getter and setter property for the catalog number. Parameters ---------- - value : unicode + value Value to set the catalog number with. Returns ------- - unicode + :py:data:`None` or :class:`str` Catalog number. """ return self._catalog_number @catalog_number.setter - def catalog_number(self, value): - """ - Setter for the **self.catalog_number** property. - """ + def catalog_number(self, value: Optional[str]): + """Setter for the **self.catalog_number** property.""" if value is not None: - assert is_string(value), ( - '"{0}" attribute: "{1}" is not a "string" like object!'.format( - 'catalog_number', value)) + attest( + is_string(value), + f'"catalog_number" property: "{value}" type is not "str"!', + ) + self._catalog_number = value @property - def description(self): + def description(self) -> Optional[str]: """ Getter and setter property for the description. Parameters ---------- - value : unicode + value Value to set the description with. Returns ------- - unicode + :py:data:`None` or :class:`str` Description. """ return self._description @description.setter - def description(self, value): - """ - Setter for the **self.description** property. - """ + def description(self, value: Optional[str]): + """Setter for the **self.description** property.""" if value is not None: - assert is_string(value), ( - '"{0}" attribute: "{1}" is not a "string" like object!'.format( - 'description', value)) + attest( + is_string(value), + f'"description" property: "{value}" type is not "str"!', + ) + self._description = value @property - def document_creator(self): + def document_creator(self) -> Optional[str]: """ Getter and setter property for the document creator. Parameters ---------- - value : unicode + value Value to set the document creator with. Returns ------- - unicode + :py:data:`None` or :class:`str` Document creator. """ return self._document_creator @document_creator.setter - def document_creator(self, value): - """ - Setter for the **self.document_creator** property. - """ + def document_creator(self, value: Optional[str]): + """Setter for the **self.document_creator** property.""" if value is not None: - assert is_string(value), ( - '"{0}" attribute: "{1}" is not a "string" like object!'.format( - 'document_creator', value)) + attest( + is_string(value), + f'"document_creator" property: "{value}" type is not "str"!', + ) + self._document_creator = value @property - def unique_identifier(self): + def unique_identifier(self) -> Optional[str]: """ Getter and setter property for the unique identifier. Parameters ---------- - value : unicode + value Value to set the unique identifier with. Returns ------- - unicode + :py:data:`None` or :class:`str` Unique identifier. """ return self._unique_identifier @unique_identifier.setter - def unique_identifier(self, value): - """ - Setter for the **self.unique_identifier** property. - """ + def unique_identifier(self, value: Optional[str]): + """Setter for the **self.unique_identifier** property.""" if value is not None: - assert is_string(value), ( - '"{0}" attribute: "{1}" is not a "string" like object!'.format( - 'unique_identifier', value)) + attest( + is_string(value), + f'"unique_identifier" property: "{value}" type is not "str"!', + ) + self._unique_identifier = value @property - def measurement_equipment(self): + def measurement_equipment(self) -> Optional[str]: """ Getter and setter property for the measurement equipment. Parameters ---------- - value : unicode + value Value to set the measurement equipment with. Returns ------- - unicode + :py:data:`None` or :class:`str` Measurement equipment. """ return self._measurement_equipment @measurement_equipment.setter - def measurement_equipment(self, value): - """ - Setter for the **self.measurement_equipment** property. - """ + def measurement_equipment(self, value: Optional[str]): + """Setter for the **self.measurement_equipment** property.""" if value is not None: - assert is_string(value), ( - '"{0}" attribute: "{1}" is not a "string" like object!'.format( - 'measurement_equipment', value)) + attest( + is_string(value), + f'"measurement_equipment" property: "{value}" type is not "str"!', + ) + self._measurement_equipment = value @property - def laboratory(self): + def laboratory(self) -> Optional[str]: """ Getter and setter property for the laboratory. Parameters ---------- - value : unicode + value Value to set the laboratory with. Returns ------- - unicode + :py:data:`None` or :class:`str` Laboratory. """ return self._laboratory @laboratory.setter - def laboratory(self, value): - """ - Setter for the **self.measurement_equipment** property. - """ + def laboratory(self, value: Optional[str]): + """Setter for the **self.measurement_equipment** property.""" if value is not None: - assert is_string(value), ( - '"{0}" attribute: "{1}" is not a "string" like object!'.format( - 'laboratory', value)) + attest( + is_string(value), + f'"laboratory" property: "{value}" type is not "str"!', + ) + self._laboratory = value @property - def report_number(self): + def report_number(self) -> Optional[str]: """ Getter and setter property for the report number. Parameters ---------- - value : unicode + value Value to set the report number with. Returns ------- - unicode + :py:data:`None` or :class:`str` Report number. """ return self._report_number @report_number.setter - def report_number(self, value): - """ - Setter for the **self.report_number** property. - """ + def report_number(self, value: Optional[str]): + """Setter for the **self.report_number** property.""" if value is not None: - assert is_string(value), ( - '"{0}" attribute: "{1}" is not a "string" like object!'.format( - 'report_number', value)) + attest( + is_string(value), + f'"report_number" property: "{value}" type is not "str"!', + ) + self._report_number = value @property - def report_date(self): + def report_date(self) -> Optional[str]: """ Getter and setter property for the report date. Parameters ---------- - value : unicode + value Value to set the report date with. Returns ------- - unicode + :py:data:`None` or :class:`str` Report date. """ return self._report_date @report_date.setter - def report_date(self, value): - """ - Setter for the **self.report_date** property. - """ + def report_date(self, value: Optional[str]): + """Setter for the **self.report_date** property.""" if value is not None: - assert is_string(value), ( - '"{0}" attribute: "{1}" is not a "string" like object!'.format( - 'report_date', value)) + attest( + is_string(value), + f'"report_date" property: "{value}" type is not "str"!', + ) + self._report_date = value @property - def document_creation_date(self): + def document_creation_date(self) -> Optional[str]: """ Getter and setter property for the document creation date. Parameters ---------- - value : unicode + value Value to set the document creation date with. Returns ------- - unicode + :py:data:`None` or :class:`str` Document creation date. """ return self._document_creation_date @document_creation_date.setter - def document_creation_date(self, value): - """ - Setter for the **self.document_creation_date** property. - """ + def document_creation_date(self, value: Optional[str]): + """Setter for the **self.document_creation_date** property.""" if value is not None: - assert is_string(value), ( - '"{0}" attribute: "{1}" is not a "string" like object!'.format( - 'document_creation_date', value)) + attest( + is_string(value), + f'"document_creation_date" property: "{value}" type is not "str"!', + ) + self._document_creation_date = value @property - def comments(self): + def comments(self) -> Optional[str]: """ Getter and setter property for the comments. Parameters ---------- - value : unicode + value Value to set the comments with. Returns ------- - unicode + :py:data:`None` or :class:`str` Comments. """ return self._comments @comments.setter - def comments(self, value): - """ - Setter for the **self.comments** property. - """ + def comments(self, value: Optional[str]): + """Setter for the **self.comments** property.""" if value is not None: - assert is_string(value), ( - '"{0}" attribute: "{1}" is not a "string" like object!'.format( - 'comments', value)) + attest( + is_string(value), + f'"comments" property: "{value}" type is not "str"!', + ) + self._comments = value + def __hash__(self) -> Integer: + """ + Return the header hash. + + Returns + ------- + :class:`numpy.integer` + Object hash. + """ + + return hash( + ( + self._manufacturer, + self._catalog_number, + self._description, + self._document_creator, + self._unique_identifier, + self._measurement_equipment, + self._laboratory, + self._report_number, + self._report_date, + self._document_creation_date, + self._comments, + ) + ) + + def __eq__(self, other: Any) -> Boolean: + """ + Return whether the header is equal to given other object. + + Parameters + ---------- + other + Object to test whether it is equal to the header. + + Returns + ------- + :class:`bool` + Whether given object is equal to the header. + + Examples + -------- + >>> Header_IESTM2714('Foo') == Header_IESTM2714('Foo') + True + >>> Header_IESTM2714('Foo') == Header_IESTM2714('Bar') + False + """ + + if isinstance(other, Header_IESTM2714): + return all( + [ + self._manufacturer == other.manufacturer, + self._catalog_number == other.catalog_number, + self._description == other.description, + self._document_creator == other.document_creator, + self._unique_identifier == other.unique_identifier, + self._measurement_equipment == other.measurement_equipment, + self._laboratory == other.laboratory, + self._report_number == other.report_number, + self._report_date == other.report_date, + self._document_creation_date + == other.document_creation_date, + self._comments == other.comments, + ] + ) + return False + + def __ne__(self, other: Any) -> Boolean: + """ + Return whether the header is not equal to given other object. + + Parameters + ---------- + other + Object to test whether it is not equal to the header. + + Returns + ------- + :class:`bool` + Whether given object is not equal to the header. + + Examples + -------- + >>> Header_IESTM2714('Foo') != Header_IESTM2714('Foo') + False + >>> Header_IESTM2714('Foo') != Header_IESTM2714('Bar') + True + """ + + return not (self == other) + class SpectralDistribution_IESTM2714(SpectralDistribution): """ - Defines a *IES TM-27-14* spectral distribution. + Define a *IES TM-27-14* spectral distribution. - This class can read and write *IES TM-27-14* spectral data XML files. + This class can read and write *IES TM-27-14* spectral data *XML* files. Parameters ---------- - path : unicode, optional - Spectral data XML file path. - header : Header_IESTM2714, optional + path + Spectral data *XML* file path. + header *IES TM-27-14* spectral distribution header. - spectral_quantity : unicode, optional - **{'flux', 'absorptance', 'transmittance', 'reflectance', 'intensity', - 'irradiance', 'radiance', 'exitance', 'R-Factor', 'T-Factor', - 'relative', 'other'}**, + spectral_quantity Quantity of measurement for each element of the spectral data. - reflection_geometry : unicode, optional - **{'di:8', 'de:8', '8:di', '8:de', 'd:d', 'd:0', '45a:0', '45c:0', - '0:45a', '45x:0', '0:45x', 'other'}**, + reflection_geometry Spectral reflectance factors geometric conditions. - transmission_geometry : unicode, optional - **{'0:0', 'di:0', 'de:0', '0:di', '0:de', 'd:d', 'other'}**, + transmission_geometry Spectral transmittance factors geometric conditions. - bandwidth_FWHM : numeric, optional + bandwidth_FWHM Spectroradiometer full-width half-maximum bandwidth in nanometers. - bandwidth_corrected : bool, optional + bandwidth_corrected Specifies if bandwidth correction has been applied to the measured data. Other Parameters ---------------- - data : Series or Signal, SpectralDistribution or array_like or \ -dict_like, optional + data Data to be stored in the spectral distribution. - domain : array_like, optional + domain Values to initialise the - :attr:`colour.SpectralDistribution.wavelength` attribute with. + :attr:`colour.SpectralDistribution.wavelength` property with. If both ``data`` and ``domain`` arguments are defined, the latter will be used to initialise the - :attr:`colour.SpectralDistribution.wavelength` attribute. - name : unicode, optional - Spectral distribution name. - interpolator : object, optional - Interpolator class type to use as interpolating function. - interpolator_kwargs : dict_like, optional - Arguments to use when instantiating the interpolating function. - extrapolator : object, optional + :attr:`colour.SpectralDistribution.wavelength` property. + extrapolator Extrapolator class type to use as extrapolating function. - extrapolator_kwargs : dict_like, optional + extrapolator_kwargs Arguments to use when instantiating the extrapolating function. - strict_name : unicode, optional + interpolator + Interpolator class type to use as interpolating function. + interpolator_kwargs + Arguments to use when instantiating the interpolating function. + name + Spectral distribution name. + strict_name Spectral distribution name for figures, default to - :attr:`colour.SpectralDistribution.name` attribute value. + :attr:`colour.SpectralDistribution.name` property value. Notes ----- @@ -669,300 +781,455 @@ class SpectralDistribution_IESTM2714(SpectralDistribution): 'Unknown - N/A - Rare earth fluorescent lamp' >>> sd.header.comments 'Ambient temperature 25 degrees C.' - >>> # Doctests ellipsis for Python 2.x compatibility. >>> sd[501.7] # doctest: +ELLIPSIS 0.0950000... """ - def __init__(self, - path=None, - header=None, - spectral_quantity=None, - reflection_geometry=None, - transmission_geometry=None, - bandwidth_FWHM=None, - bandwidth_corrected=None, - **kwargs): - - super(SpectralDistribution_IESTM2714, self).__init__(**kwargs) - - self._mapping = Structure( + def __init__( + self, + path: Optional[str] = None, + header: Optional[Header_IESTM2714] = None, + spectral_quantity: Optional[ + Literal[ + "absorptance", + "exitance", + "flux", + "intensity", + "irradiance", + "radiance", + "reflectance", + "relative", + "transmittance", + "R-Factor", + "T-Factor", + "other", + ] + ] = None, + reflection_geometry: Optional[ + Literal[ + "di:8", + "de:8", + "8:di", + "8:de", + "d:d", + "d:0", + "45a:0", + "45c:0", + "0:45a", + "45x:0", + "0:45x", + "other", + ] + ] = None, + transmission_geometry: Optional[ + Literal["0:0", "di:0", "de:0", "0:di", "0:de", "d:d", "other"] + ] = None, + bandwidth_FWHM: Optional[Floating] = None, + bandwidth_corrected: Optional[Boolean] = None, + **kwargs, + ): + + super().__init__(**kwargs) + + self._mapping: Structure = Structure( **{ - 'element': - 'SpectralDistribution', - 'elements': - (Element_Specification_IESTM2714( - 'SpectralQuantity', 'spectral_quantity', - required=True), - Element_Specification_IESTM2714('ReflectionGeometry', - 'reflection_geometry'), - Element_Specification_IESTM2714('TransmissionGeometry', - 'transmission_geometry'), - Element_Specification_IESTM2714( - 'BandwidthFWHM', - 'bandwidth_FWHM', - read_conversion=DEFAULT_FLOAT_DTYPE), - Element_Specification_IESTM2714( - 'BandwidthCorrected', - 'bandwidth_corrected', - read_conversion=( - lambda x: True if x == 'true' else False), - write_conversion=( - lambda x: 'true' if x is True else 'False'))), - 'data': + "element": "SpectralDistribution", + "elements": ( + Element_Specification_IESTM2714( + "SpectralQuantity", "spectral_quantity", required=True + ), + Element_Specification_IESTM2714( + "ReflectionGeometry", "reflection_geometry" + ), Element_Specification_IESTM2714( - 'SpectralData', 'wavelength', required=True) - }) + "TransmissionGeometry", "transmission_geometry" + ), + Element_Specification_IESTM2714( + "BandwidthFWHM", + "bandwidth_FWHM", + read_conversion=as_float_scalar, + ), + Element_Specification_IESTM2714( + "BandwidthCorrected", + "bandwidth_corrected", + read_conversion=( + lambda x: True if x == "true" else False + ), + write_conversion=( + lambda x: "true" if x is True else "False" + ), + ), + ), + "data": Element_Specification_IESTM2714( + "SpectralData", "wavelength", required=True + ), + } + ) - self._path = None + self._path: Optional[str] = None self.path = path - self._header = None - self.header = header if header is not None else Header_IESTM2714() - self._spectral_quantity = None + self._header: Header_IESTM2714 = Header_IESTM2714() + self.header = optional(header, self._header) + self._spectral_quantity: Optional[ + Literal[ + "absorptance", + "exitance", + "flux", + "intensity", + "irradiance", + "radiance", + "reflectance", + "relative", + "transmittance", + "R-Factor", + "T-Factor", + "other", + ] + ] = None self.spectral_quantity = spectral_quantity - self._reflection_geometry = None + self._reflection_geometry: Optional[ + Literal[ + "di:8", + "de:8", + "8:di", + "8:de", + "d:d", + "d:0", + "45a:0", + "45c:0", + "0:45a", + "45x:0", + "0:45x", + "other", + ] + ] = None self.reflection_geometry = reflection_geometry - self._transmission_geometry = None + self._transmission_geometry: Optional[ + Literal["0:0", "di:0", "de:0", "0:di", "0:de", "d:d", "other"] + ] = None self.transmission_geometry = transmission_geometry - self._bandwidth_FWHM = None + self._bandwidth_FWHM: Optional[Floating] = None self.bandwidth_FWHM = bandwidth_FWHM - self._bandwidth_corrected = None + self._bandwidth_corrected: Optional[Boolean] = None self.bandwidth_corrected = bandwidth_corrected @property - def mapping(self): + def mapping(self) -> Structure: """ Getter property for the mapping structure. Returns ------- - Structure + :class:`colour.utilities.Structure` Mapping structure. """ return self._mapping @property - def path(self): + def path(self) -> Optional[str]: """ Getter and setter property for the path. Parameters ---------- - value : unicode + value Value to set the path with. Returns ------- - unicode + :py:data:`None` or :class:`str` Path. """ return self._path @path.setter - def path(self, value): - """ - Setter for the **self.path** property. - """ + def path(self, value: Optional[str]): + """Setter for the **self.path** property.""" if value is not None: - assert is_string(value), ( - '"{0}" attribute: "{1}" is not a "string" like object!'.format( - 'path', value)) + attest( + is_string(value), + f'"path" property: "{value}" type is not "str"!', + ) + self._path = value @property - def header(self): + def header(self) -> Header_IESTM2714: """ Getter and setter property for the header. Parameters ---------- - value : Header_IESTM2714 + value Value to set the header with. Returns ------- - Header_IESTM2714 + :class:`colour.io.tm2714.Header_IESTM2714` Header. """ return self._header @header.setter - def header(self, value): - """ - Setter for the **self.header** property. - """ + def header(self, value: Header_IESTM2714): + """Setter for the **self.header** property.""" + + attest( + isinstance(value, Header_IESTM2714), + f'"header" property: "{value}" type is not "Header_IESTM2714"!', + ) - if value is not None: - assert isinstance(value, Header_IESTM2714), ( - '"{0}" attribute: "{1}" is not a "Header_IESTM2714" ' - 'instance!'.format('header', value)) self._header = value @property - def spectral_quantity(self): + def spectral_quantity( + self, + ) -> Optional[ + Literal[ + "absorptance", + "exitance", + "flux", + "intensity", + "irradiance", + "radiance", + "reflectance", + "relative", + "transmittance", + "R-Factor", + "T-Factor", + "other", + ] + ]: """ Getter and setter property for the spectral quantity. Parameters ---------- - value : unicode + value Value to set the spectral quantity with. Returns ------- - unicode + :py:data:`None` or :class:`str` Spectral quantity. """ return self._spectral_quantity @spectral_quantity.setter - def spectral_quantity(self, value): - """ - Setter for the **self.spectral_quantity** property. - """ + def spectral_quantity( + self, + value: Optional[ + Literal[ + "absorptance", + "exitance", + "flux", + "intensity", + "irradiance", + "radiance", + "reflectance", + "relative", + "transmittance", + "R-Factor", + "T-Factor", + "other", + ] + ], + ): + """Setter for the **self.spectral_quantity** property.""" if value is not None: - assert is_string(value), ( - '"{0}" attribute: "{1}" is not a "string" like object!'.format( - 'spectral_quantity', value)) + attest( + is_string(value), + f'"spectral_quantity" property: "{value}" type is not "str"!', + ) + self._spectral_quantity = value @property - def reflection_geometry(self): + def reflection_geometry( + self, + ) -> Optional[ + Literal[ + "di:8", + "de:8", + "8:di", + "8:de", + "d:d", + "d:0", + "45a:0", + "45c:0", + "0:45a", + "45x:0", + "0:45x", + "other", + ] + ]: """ Getter and setter property for the reflection geometry. Parameters ---------- - value : unicode + value Value to set the reflection geometry with. Returns ------- - unicode + :py:data:`None` or :class:`str` Reflection geometry. """ return self._reflection_geometry @reflection_geometry.setter - def reflection_geometry(self, value): - """ - Setter for the **self.reflection_geometry** property. - """ + def reflection_geometry( + self, + value: Optional[ + Literal[ + "di:8", + "de:8", + "8:di", + "8:de", + "d:d", + "d:0", + "45a:0", + "45c:0", + "0:45a", + "45x:0", + "0:45x", + "other", + ] + ], + ): + """Setter for the **self.reflection_geometry** property.""" if value is not None: - assert is_string(value), ( - '"{0}" attribute: "{1}" is not a "string" like object!'.format( - 'reflection_geometry', value)) + attest( + is_string(value), + f'"reflection_geometry" property: "{value}" type is not "str"!', + ) + self._reflection_geometry = value @property - def transmission_geometry(self): + def transmission_geometry( + self, + ) -> Optional[ + Literal["0:0", "di:0", "de:0", "0:di", "0:de", "d:d", "other"] + ]: """ Getter and setter property for the transmission geometry. Parameters ---------- - value : unicode + value Value to set the transmission geometry with. Returns ------- - unicode + :py:data:`None` or :class:`str` Transmission geometry. """ return self._transmission_geometry @transmission_geometry.setter - def transmission_geometry(self, value): - """ - Setter for the **self.transmission_geometry** property. - """ + def transmission_geometry( + self, + value: Optional[ + Literal["0:0", "di:0", "de:0", "0:di", "0:de", "d:d", "other"] + ], + ): + """Setter for the **self.transmission_geometry** property.""" if value is not None: - assert is_string(value), ( - '"{0}" attribute: "{1}" is not a "string" like object!'.format( - 'transmission_geometry', value)) + attest( + is_string(value), + f'"transmission_geometry" property: "{value}" type is not "str"!', + ) + self._transmission_geometry = value @property - def bandwidth_FWHM(self): + def bandwidth_FWHM(self) -> Optional[Floating]: """ Getter and setter property for the full-width half-maximum bandwidth. Parameters ---------- - value : numeric + value Value to set the full-width half-maximum bandwidth with. Returns ------- - numeric + :py:data:`None` or :class:`numpy.floating` Full-width half-maximum bandwidth. """ return self._bandwidth_FWHM @bandwidth_FWHM.setter - def bandwidth_FWHM(self, value): - """ - Setter for the **self.bandwidth_FWHM** property. - """ + def bandwidth_FWHM(self, value: Optional[Floating]): + """Setter for the **self.bandwidth_FWHM** property.""" if value is not None: - assert is_numeric(value), ( - '"{0}" attribute: "{1}" is not a "numeric"!'.format( - 'bandwidth_FWHM', value)) + attest( + is_numeric(value), + f'"bandwidth_FWHM" property: "{value}" is not a "number"!', + ) + + value = as_float_scalar(value) self._bandwidth_FWHM = value @property - def bandwidth_corrected(self): + def bandwidth_corrected(self) -> Optional[Boolean]: """ Getter and setter property for whether bandwidth correction has been applied to the measured data. Parameters ---------- - value : bool + value Whether bandwidth correction has been applied to the measured data. Returns ------- - bool + :py:data:`None` or :class:`bool` Whether bandwidth correction has been applied to the measured data. """ return self._bandwidth_corrected @bandwidth_corrected.setter - def bandwidth_corrected(self, value): - """ - Setter for the **self.bandwidth_corrected** property. - """ + def bandwidth_corrected(self, value: Optional[Boolean]): + """Setter for the **self.bandwidth_corrected** property.""" if value is not None: - assert isinstance(value, bool), ( - '"{0}" attribute: "{1}" is not a "bool" instance!'.format( - 'bandwidth_corrected', value)) + attest( + isinstance(value, bool), + f'"bandwidth_corrected" property: "{value}" type is not "bool"!', + ) self._bandwidth_corrected = value - def read(self): + def read(self) -> SpectralDistribution_IESTM2714: """ - Reads and parses the spectral data XML file path. + Read and parses the spectral data *XML* file path. Returns ------- - bool - Definition success. + :class:`colour.SpectralDistribution_IESTM2714` + *IES TM-27-14* spectral distribution. + + Raises + ------ + ValueError + If the *IES TM-27-14* spectral distribution path is undefined. Examples -------- @@ -974,59 +1241,84 @@ def read(self): 'Unknown - N/A - Rare earth fluorescent lamp' >>> sd.header.comments 'Ambient temperature 25 degrees C.' - >>> # Doctests ellipsis for Python 2.x compatibility. >>> sd[400] # doctest: +ELLIPSIS - 0.0339999... - """ - - formatter = './{{{0}}}{1}/{{{0}}}{2}' - - tree = ElementTree.parse(self._path) # nosec - root = tree.getroot() - - namespace = re.match('{(.*)}', root.tag).group(1) - - self.name = os.path.splitext(os.path.basename(self._path))[0] - - iterator = root.iter - - for header_element in (self.header, self): - mapping = header_element.mapping - for specification in mapping.elements: - element = root.find( - formatter.format(namespace, mapping.element, - specification.element)) - if element is not None: - setattr(header_element, specification.attribute, - specification.read_conversion(element.text)) - - # Reading spectral data. - wavelengths = [] - values = [] - for spectral_data in iterator('{{{0}}}{1}'.format( - namespace, self.mapping.data.element)): - wavelengths.append( - DEFAULT_FLOAT_DTYPE( - spectral_data.attrib[self.mapping.data.attribute])) - values.append(DEFAULT_FLOAT_DTYPE(spectral_data.text)) - - self.name = ' - '.join([ - self.header.manufacturer, - self.header.catalog_number, - self.header.description, - ]) - self.wavelengths = wavelengths - self.values = values - - return self - - def write(self): - """ - Write the spectral distribution spectral data to XML file path. + 0.0340000... + """ + + if self._path is not None: + formatter = "./{{{0}}}{1}/{{{0}}}{2}" + + tree = ElementTree.parse(self._path) # nosec + root = tree.getroot() + + match = re.match("{(.*)}", root.tag) + if match: + namespace = match.group(1) + else: + raise ValueError( + 'The "IES TM-27-14" spectral distribution namespace ' + "was not found!" + ) + + self.name = os.path.splitext(os.path.basename(self._path))[0] + + iterator = root.iter + + for header_element in (self.header, self): + mapping = header_element.mapping # type: ignore[attr-defined] + for specification in mapping.elements: + element = root.find( + formatter.format( + namespace, mapping.element, specification.element + ) + ) + if element is not None: + setattr( + header_element, + specification.attribute, + specification.read_conversion(element.text), + ) + + # Reading spectral data. + wavelengths = [] + values = [] + for spectral_data in iterator( + f"{{{namespace}}}{self.mapping.data.element}" + ): + wavelengths.append( + spectral_data.attrib[self.mapping.data.attribute] + ) + values.append(spectral_data.text) + + components = [ + component + for component in ( + self.header.manufacturer, + self.header.catalog_number, + self.header.description, + ) + if component is not None + ] + self.name = ( + "Undefined" if len(components) == 0 else " - ".join(components) + ) + + self.wavelengths = as_float_array(wavelengths) + self.values = as_float_array(cast(ArrayLike, values)) + + return self + else: + raise ValueError( + 'The "IES TM-27-14" spectral distribution path is undefined!' + ) + + def write(self) -> Boolean: + """ + Write the spectral distribution spectral data to *XML* file path. Returns ------- - bool + :class:`bool` Definition success. Examples @@ -1044,39 +1336,48 @@ def write(self): >>> rmtree(temporary_directory) """ - root = ElementTree.Element('IESTM2714') - root.attrib = { - 'xmlns': NAMESPACE_IESTM2714, - 'version': VERSION_IESTM2714 - } - - spectral_distribution = None - for header_element in (self.header, self): - mapping = header_element.mapping - element = ElementTree.SubElement(root, mapping.element) - for specification in mapping.elements: - element_child = ElementTree.SubElement(element, - specification.element) - value = getattr(header_element, specification.attribute) - element_child.text = specification.write_conversion(value) - - if header_element is self: - spectral_distribution = element - - # Writing spectral data. - for (wavelength, value) in tstack([self.wavelengths, self.values]): - element_child = ElementTree.SubElement(spectral_distribution, - mapping.data.element) - element_child.text = mapping.data.write_conversion(value) - element_child.attrib = { - mapping.data.attribute: - mapping.data.write_conversion(wavelength) + if self._path is not None: + root = ElementTree.Element("IESTM2714") + root.attrib = { + "xmlns": NAMESPACE_IESTM2714, + "version": VERSION_IESTM2714, } - xml = minidom.parseString( - ElementTree.tostring(root)).toprettyxml() # nosec - - with open(self._path, 'w') as file: - file.write(xml) - - return True + spectral_distribution = ElementTree.Element("") + for header_element in (self.header, self): + mapping = header_element.mapping # type: ignore[attr-defined] + element = ElementTree.SubElement(root, mapping.element) + for specification in mapping.elements: + element_child = ElementTree.SubElement( + element, specification.element + ) + value = getattr(header_element, specification.attribute) + element_child.text = specification.write_conversion(value) + + if header_element is self: + spectral_distribution = element + + # Writing spectral data. + for (wavelength, value) in tstack([self.wavelengths, self.values]): + element_child = ElementTree.SubElement( + spectral_distribution, mapping.data.element + ) + element_child.text = mapping.data.write_conversion(value) + element_child.attrib = { + mapping.data.attribute: mapping.data.write_conversion( + wavelength + ) + } + + xml = minidom.parseString( + ElementTree.tostring(root) + ).toprettyxml() # nosec + + with open(self._path, "w") as file: + file.write(xml) + + return True + else: + raise ValueError( + 'The "IES TM-27-14" spectral distribution path is undefined!' + ) diff --git a/colour/io/uprtek_sekonic.py b/colour/io/uprtek_sekonic.py new file mode 100644 index 0000000000..0bbbb0e267 --- /dev/null +++ b/colour/io/uprtek_sekonic.py @@ -0,0 +1,416 @@ +""" +UPRTek and Sekonic Spectral Data +================================ + +Defines the input and output objects for *UPRTek* and *Sekonic* +*Pseudo-XLS*/*CSV* spectral data files. + +- :class:`colour.SpectralDistribution_UPRTek` +- :class:`colour.SpectralDistribution_Sekonic` +""" + +from __future__ import annotations + +import csv +import json +import os +import re +from collections import defaultdict + +from colour.io import SpectralDistribution_IESTM2714 +from colour.hints import Any, Dict, List, cast +from colour.utilities import as_float_array + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "SpectralDistribution_UPRTek", + "SpectralDistribution_Sekonic", +] + + +class SpectralDistribution_UPRTek(SpectralDistribution_IESTM2714): + """ + Implement support to read and write *IES TM-27-14* spectral data XML file + from a *UPRTek* *Pseudo-XLS* file. + + Parameters + ---------- + path + Path for *UPRTek* *Pseudo-XLS* file. + + Attributes + ---------- + - :attr:`~colour.SpectralDistribution_UPRTek.metadata` + + Methods + ------- + - :meth:`~colour.SpectralDistribution_UPRTek.__init__` + - :meth:`~colour.SpectralDistribution_UPRTek.read` + + Examples + -------- + >>> from os.path import dirname, join + >>> from colour import SpectralShape + >>> directory = join(dirname(__file__), 'tests', 'resources') + >>> sd = SpectralDistribution_UPRTek( + ... join(directory, 'ESPD2021_0104_231446.xls')) + >>> print(sd.read().align(SpectralShape(380, 780, 10))) + [[ 3.80000000e+02 3.02670000e-02] + [ 3.90000000e+02 3.52230000e-02] + [ 4.00000000e+02 1.93250000e-02] + [ 4.10000000e+02 2.94260000e-02] + [ 4.20000000e+02 8.76780000e-02] + [ 4.30000000e+02 6.32578000e-01] + [ 4.40000000e+02 3.62565900e+00] + [ 4.50000000e+02 1.42069180e+01] + [ 4.60000000e+02 1.70112970e+01] + [ 4.70000000e+02 1.19673130e+01] + [ 4.80000000e+02 8.42736200e+00] + [ 4.90000000e+02 7.97729800e+00] + [ 5.00000000e+02 8.71903600e+00] + [ 5.10000000e+02 9.55321500e+00] + [ 5.20000000e+02 9.90610500e+00] + [ 5.30000000e+02 9.91394400e+00] + [ 5.40000000e+02 9.74738000e+00] + [ 5.50000000e+02 9.53404900e+00] + [ 5.60000000e+02 9.27392200e+00] + [ 5.70000000e+02 9.02323400e+00] + [ 5.80000000e+02 8.91788800e+00] + [ 5.90000000e+02 9.11454600e+00] + [ 6.00000000e+02 9.55787100e+00] + [ 6.10000000e+02 1.00600760e+01] + [ 6.20000000e+02 1.04846200e+01] + [ 6.30000000e+02 1.05679540e+01] + [ 6.40000000e+02 1.04359870e+01] + [ 6.50000000e+02 9.82122300e+00] + [ 6.60000000e+02 8.77578300e+00] + [ 6.70000000e+02 7.56471800e+00] + [ 6.80000000e+02 6.29808600e+00] + [ 6.90000000e+02 5.15623400e+00] + [ 7.00000000e+02 4.05390600e+00] + [ 7.10000000e+02 3.06638600e+00] + [ 7.20000000e+02 2.19250000e+00] + [ 7.30000000e+02 1.53922800e+00] + [ 7.40000000e+02 1.14938200e+00] + [ 7.50000000e+02 9.05095000e-01] + [ 7.60000000e+02 6.90947000e-01] + [ 7.70000000e+02 5.08426000e-01] + [ 7.80000000e+02 4.11766000e-01]] + >>> sd.header.comments + '{"Model Name": "CV600", "Serial Number": "19J00789", \ +"Time": "2021/01/04_23:14:46", "Memo": [], "LUX": 695.154907, \ +"fc": 64.605476, "CCT": 5198.0, "Duv": -0.00062, "I-Time": 12000.0, \ +"X": 682.470886, "Y": 695.154907, "Z": 631.635071, "x": 0.339663, \ +"y": 0.345975, "u\\'": 0.209915, "v\\'": 0.481087, "LambdaP": 456.0, \ +"LambdaPValue": 18.404581, "CRI": 92.956993, "R1": 91.651062, \ +"R2": 93.014732, "R3": 97.032013, "R4": 93.513229, "R5": 92.48259, \ +"R6": 91.48687, "R7": 93.016129, "R8": 91.459312, "R9": 77.613075, \ +"R10": 86.981613, "R11": 94.841324, "R12": 74.139542, "R13": 91.073837, \ +"R14": 97.064323, "R15": 88.615669, "TLCI": 97.495056, "TLMF-A": 1.270032, \ +"SSI-A": 44.881924, "Rf": 87.234917, "Rg": 98.510712, "IRR": 2.607891}' + >>> sd.metadata.keys() + dict_keys(['Model Name', 'Serial Number', 'Time', 'Memo', 'LUX', 'fc', \ +'CCT', 'Duv', 'I-Time', 'X', 'Y', 'Z', 'x', 'y', "u'", "v'", 'LambdaP', \ +'LambdaPValue', 'CRI', 'R1', 'R2', 'R3', 'R4', 'R5', 'R6', 'R7', 'R8', 'R9', \ +'R10', 'R11', 'R12', 'R13', 'R14', 'R15', 'TLCI', 'TLMF-A', 'SSI-A', 'Rf', \ +'Rg', 'IRR']) + >>> sd.write(join(directory, 'ESPD2021_0104_231446.spdx')) + ... # doctest: +SKIP + """ + + def __init__(self, path: str, **kwargs: Any): + super().__init__(path, **kwargs) + + self._delimiter: str = "\t" + self._spectral_section: str = "380" + self._spectral_data_pattern: str = "(\\d{3})nm" + + self._metadata: Dict = {} + + @property + def metadata(self) -> Dict: + """ + Getter property for the metadata. + + Returns + ------- + :class:`dict` + Metadata. + """ + + return self._metadata + + def read(self) -> SpectralDistribution_UPRTek: + """ + Read and parses the spectral data from a given *UPRTek* *CSV* file. + + Returns + ------- + :class:`colour.SpectralDistribution_UPRTek` + *UPRTek* spectral distribution. + + Examples + -------- + >>> from os.path import dirname, join + >>> from colour import SpectralShape + >>> directory = join(dirname(__file__), 'tests', 'resources') + >>> sd = SpectralDistribution_UPRTek( + ... join(directory, 'ESPD2021_0104_231446.xls')) + >>> print(sd.read().align(SpectralShape(380, 780, 10))) + [[ 3.80000000e+02 3.02670000e-02] + [ 3.90000000e+02 3.52230000e-02] + [ 4.00000000e+02 1.93250000e-02] + [ 4.10000000e+02 2.94260000e-02] + [ 4.20000000e+02 8.76780000e-02] + [ 4.30000000e+02 6.32578000e-01] + [ 4.40000000e+02 3.62565900e+00] + [ 4.50000000e+02 1.42069180e+01] + [ 4.60000000e+02 1.70112970e+01] + [ 4.70000000e+02 1.19673130e+01] + [ 4.80000000e+02 8.42736200e+00] + [ 4.90000000e+02 7.97729800e+00] + [ 5.00000000e+02 8.71903600e+00] + [ 5.10000000e+02 9.55321500e+00] + [ 5.20000000e+02 9.90610500e+00] + [ 5.30000000e+02 9.91394400e+00] + [ 5.40000000e+02 9.74738000e+00] + [ 5.50000000e+02 9.53404900e+00] + [ 5.60000000e+02 9.27392200e+00] + [ 5.70000000e+02 9.02323400e+00] + [ 5.80000000e+02 8.91788800e+00] + [ 5.90000000e+02 9.11454600e+00] + [ 6.00000000e+02 9.55787100e+00] + [ 6.10000000e+02 1.00600760e+01] + [ 6.20000000e+02 1.04846200e+01] + [ 6.30000000e+02 1.05679540e+01] + [ 6.40000000e+02 1.04359870e+01] + [ 6.50000000e+02 9.82122300e+00] + [ 6.60000000e+02 8.77578300e+00] + [ 6.70000000e+02 7.56471800e+00] + [ 6.80000000e+02 6.29808600e+00] + [ 6.90000000e+02 5.15623400e+00] + [ 7.00000000e+02 4.05390600e+00] + [ 7.10000000e+02 3.06638600e+00] + [ 7.20000000e+02 2.19250000e+00] + [ 7.30000000e+02 1.53922800e+00] + [ 7.40000000e+02 1.14938200e+00] + [ 7.50000000e+02 9.05095000e-01] + [ 7.60000000e+02 6.90947000e-01] + [ 7.70000000e+02 5.08426000e-01] + [ 7.80000000e+02 4.11766000e-01]] + """ + + path = cast(str, self.path) + + def as_array(a: Any) -> List: + """ + Input list of numbers and converts each element to + float data type. + """ + + return [float(e) for e in a] + + spectral_sections = defaultdict(list) + with open(path, encoding="utf-8") as csv_file: + content = csv.reader(csv_file, delimiter=self._delimiter) + + spectral_section = 0 + for row in content: + if not "".join(row).strip(): + continue + + attribute, tokens = row[0], row[1:] + value = tokens[0] if len(tokens) == 1 else tokens + + match = re.match(self._spectral_data_pattern, attribute) + if match: + wavelength = match.group(1) + + if wavelength == self._spectral_section: + spectral_section += 1 + + spectral_sections[spectral_section].append( + [wavelength, value] + ) + else: + for method in (int, float, as_array): + try: + self._metadata[attribute] = method( + value + ) # type: ignore[operator] + break + except Exception: + self._metadata[attribute] = value + + self.name = os.path.splitext(os.path.basename(path))[0] + spectral_data = as_float_array( + spectral_sections[sorted(spectral_sections.keys())[-1]] + ) + + self.wavelengths = spectral_data[..., 0] + self.values = spectral_data[..., 1] + + self.header.comments = json.dumps(self._metadata) + + self.header.report_date = self._metadata.get("Time") + self.header.measurement_equipment = self._metadata.get("Model Name") + self.header.manufacturer = "UPRTek" + self.spectral_quantity = "irradiance" + + return self + + +class SpectralDistribution_Sekonic(SpectralDistribution_UPRTek): + """ + Implement support to read and write *IES TM-27-14* spectral data XML file + from a *Sekonic* *CSV* file. + + Parameters + ---------- + path + Path for *Sekonic* *CSV* file. + + Attributes + ---------- + - :attr:`~colour.SpectralDistribution_UPRTek.metadata` + + Methods + ------- + - :meth:`~colour.SpectralDistribution_Sekonic.__init__` + - :meth:`~colour.SpectralDistribution_Sekonic.read` + + Examples + -------- + >>> from os.path import dirname, join + >>> from colour import SpectralShape + >>> directory = join(dirname(__file__), 'tests', 'resources') + >>> sd = SpectralDistribution_Sekonic( + ... join(directory, 'RANDOM_001_02._3262K.csv')) + >>> print(sd.read().align(SpectralShape(380, 780, 10))) + [[ 3.80000000e+02 1.69406589e-21] + [ 3.90000000e+02 2.11758237e-22] + [ 4.00000000e+02 1.19813650e-05] + [ 4.10000000e+02 1.97110530e-05] + [ 4.20000000e+02 2.99661440e-05] + [ 4.30000000e+02 6.38192720e-05] + [ 4.40000000e+02 1.68909683e-04] + [ 4.50000000e+02 3.31902935e-04] + [ 4.60000000e+02 3.33143020e-04] + [ 4.70000000e+02 2.30227481e-04] + [ 4.80000000e+02 1.66981976e-04] + [ 4.90000000e+02 1.64439844e-04] + [ 5.00000000e+02 2.01534538e-04] + [ 5.10000000e+02 2.57840526e-04] + [ 5.20000000e+02 3.04612651e-04] + [ 5.30000000e+02 3.41368344e-04] + [ 5.40000000e+02 3.63639323e-04] + [ 5.50000000e+02 3.87050648e-04] + [ 5.60000000e+02 4.21619130e-04] + [ 5.70000000e+02 4.58150520e-04] + [ 5.80000000e+02 5.01176575e-04] + [ 5.90000000e+02 5.40883630e-04] + [ 6.00000000e+02 5.71256795e-04] + [ 6.10000000e+02 5.83703280e-04] + [ 6.20000000e+02 5.57688472e-04] + [ 6.30000000e+02 5.17328095e-04] + [ 6.40000000e+02 4.39994939e-04] + [ 6.50000000e+02 3.62766819e-04] + [ 6.60000000e+02 2.96465587e-04] + [ 6.70000000e+02 2.43966802e-04] + [ 6.80000000e+02 2.04134776e-04] + [ 6.90000000e+02 1.75304012e-04] + [ 7.00000000e+02 1.52887544e-04] + [ 7.10000000e+02 1.29795619e-04] + [ 7.20000000e+02 1.03122693e-04] + [ 7.30000000e+02 8.77607820e-05] + [ 7.40000000e+02 7.61524130e-05] + [ 7.50000000e+02 7.06516880e-05] + [ 7.60000000e+02 3.72199210e-05] + [ 7.70000000e+02 3.63058860e-05] + [ 7.80000000e+02 3.55755470e-05]] + >>> sd.header.comments # doctest: +SKIP + >>> sd.metadata.keys() # doctest: +SKIP + >>> sd.write(join(directory, 'RANDOM_001_02._3262K.spdx') + ... # doctest: +SKIP + """ + + def __init__(self, path: str, **kwargs: Any): + super().__init__(path, **kwargs) + + self._delimiter: str = "," + self._spectral_section: str = "380" + self._spectral_data_pattern: str = "Spectral Data (\\d{3})\\[nm\\]" + + def read(self) -> SpectralDistribution_Sekonic: + """ + Read and parses the spectral data from a given *Sekonic* *Pseudo-XLS* + file. + + Returns + ------- + :class:`colour.SpectralDistribution_Sekonic` + *Sekonic* spectral distribution. + + Examples + -------- + >>> from os.path import dirname, join + >>> from colour import SpectralShape + >>> directory = join(dirname(__file__), 'tests', 'resources') + >>> sd = SpectralDistribution_Sekonic( + ... join(directory, 'RANDOM_001_02._3262K.csv')) + >>> print(sd.read().align(SpectralShape(380, 780, 10))) + [[ 3.80000000e+02 1.69406589e-21] + [ 3.90000000e+02 2.11758237e-22] + [ 4.00000000e+02 1.19813650e-05] + [ 4.10000000e+02 1.97110530e-05] + [ 4.20000000e+02 2.99661440e-05] + [ 4.30000000e+02 6.38192720e-05] + [ 4.40000000e+02 1.68909683e-04] + [ 4.50000000e+02 3.31902935e-04] + [ 4.60000000e+02 3.33143020e-04] + [ 4.70000000e+02 2.30227481e-04] + [ 4.80000000e+02 1.66981976e-04] + [ 4.90000000e+02 1.64439844e-04] + [ 5.00000000e+02 2.01534538e-04] + [ 5.10000000e+02 2.57840526e-04] + [ 5.20000000e+02 3.04612651e-04] + [ 5.30000000e+02 3.41368344e-04] + [ 5.40000000e+02 3.63639323e-04] + [ 5.50000000e+02 3.87050648e-04] + [ 5.60000000e+02 4.21619130e-04] + [ 5.70000000e+02 4.58150520e-04] + [ 5.80000000e+02 5.01176575e-04] + [ 5.90000000e+02 5.40883630e-04] + [ 6.00000000e+02 5.71256795e-04] + [ 6.10000000e+02 5.83703280e-04] + [ 6.20000000e+02 5.57688472e-04] + [ 6.30000000e+02 5.17328095e-04] + [ 6.40000000e+02 4.39994939e-04] + [ 6.50000000e+02 3.62766819e-04] + [ 6.60000000e+02 2.96465587e-04] + [ 6.70000000e+02 2.43966802e-04] + [ 6.80000000e+02 2.04134776e-04] + [ 6.90000000e+02 1.75304012e-04] + [ 7.00000000e+02 1.52887544e-04] + [ 7.10000000e+02 1.29795619e-04] + [ 7.20000000e+02 1.03122693e-04] + [ 7.30000000e+02 8.77607820e-05] + [ 7.40000000e+02 7.61524130e-05] + [ 7.50000000e+02 7.06516880e-05] + [ 7.60000000e+02 3.72199210e-05] + [ 7.70000000e+02 3.63058860e-05] + [ 7.80000000e+02 3.55755470e-05]] + """ + + super().read() + + self.header.report_date = self._metadata.get("Date Saved") + self.header.manufacturer = "Sekonic" + + return self diff --git a/colour/io/xrite.py b/colour/io/xrite.py index f234c4456a..f8d06df210 100644 --- a/colour/io/xrite.py +++ b/colour/io/xrite.py @@ -1,53 +1,53 @@ -# -*- coding: utf-8 -*- """ X-Rite Data Input ================= -Defines input object for *X-Rite* spectral data files: +Defines the input object for *X-Rite* spectral data files: - :func:`colour.read_sds_from_xrite_file` """ -from __future__ import division, unicode_literals +from __future__ import annotations import codecs import re -from collections import OrderedDict from colour.colorimetry import SpectralDistribution -from colour.constants import DEFAULT_FLOAT_DTYPE +from colour.hints import Dict -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['XRITE_FILE_ENCODING', 'read_sds_from_xrite_file'] +__all__ = [ + "XRITE_FILE_ENCODING", + "read_sds_from_xrite_file", +] -XRITE_FILE_ENCODING = 'utf-8' +XRITE_FILE_ENCODING: str = "utf-8" -def read_sds_from_xrite_file(path): +def read_sds_from_xrite_file(path: str) -> Dict[str, SpectralDistribution]: """ - Reads the spectral data from given *X-Rite* file and returns it as an - *OrderedDict* of :class:`colour.SpectralDistribution` classes. + Read the spectral data from given *X-Rite* file and returns it as a + *dict* of :class:`colour.SpectralDistribution` class instances. Parameters ---------- - path : unicode + path Absolute *X-Rite* file path. Returns ------- - OrderedDict - :class:`colour.SpectralDistribution` classes of given *X-Rite* - file. + :class:`dict` + *Dict* of :class:`colour.SpectralDistribution` class instances. Notes ----- - - This parser is minimalistic and absolutely not bullet proof. + - This parser is minimalistic and absolutely not bullet-proof. Examples -------- @@ -62,36 +62,33 @@ def read_sds_from_xrite_file(path): """ with codecs.open(path, encoding=XRITE_FILE_ENCODING) as xrite_file: - lines = xrite_file.read().strip().split('\n') + lines = xrite_file.read().strip().split("\n") - xrite_sds = OrderedDict() + xrite_sds = {} is_spectral_data_format, is_spectral_data = False, False for line in lines: line = line.strip() - if line == 'END_DATA_FORMAT': + if line == "END_DATA_FORMAT": is_spectral_data_format = False - if line == 'END_DATA': + if line == "END_DATA": is_spectral_data = False if is_spectral_data_format: - wavelengths = [ - DEFAULT_FLOAT_DTYPE(x) - for x in re.findall('nm(\\d+)', line) - ] + wavelengths = [x for x in re.findall("nm(\\d+)", line)] index = len(wavelengths) if is_spectral_data: tokens = line.split() - values = [DEFAULT_FLOAT_DTYPE(x) for x in tokens[-index:]] - xrite_sds[tokens[1]] = (SpectralDistribution( - dict(zip(wavelengths, values)), name=tokens[1])) + xrite_sds[tokens[1]] = SpectralDistribution( + tokens[-index:], wavelengths, name=tokens[1] + ) - if line == 'BEGIN_DATA_FORMAT': + if line == "BEGIN_DATA_FORMAT": is_spectral_data_format = True - if line == 'BEGIN_DATA': + if line == "BEGIN_DATA": is_spectral_data = True return xrite_sds diff --git a/colour/models/__init__.py b/colour/models/__init__.py index 4bc8cb7af8..5c25ab60b3 100644 --- a/colour/models/__init__.py +++ b/colour/models/__init__.py @@ -1,513 +1,755 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - import sys from colour.utilities.deprecation import ModuleAPI, build_API_changes from colour.utilities.documentation import is_documentation_building -from .common import (Jab_to_JCh, JCh_to_Jab, COLOURSPACE_MODELS, - COLOURSPACE_MODELS_AXIS_LABELS, XYZ_to_colourspace_model) -from .cam02_ucs import (JMh_CIECAM02_to_CAM02LCD, CAM02LCD_to_JMh_CIECAM02, - JMh_CIECAM02_to_CAM02SCD, CAM02SCD_to_JMh_CIECAM02, - JMh_CIECAM02_to_CAM02UCS, CAM02UCS_to_JMh_CIECAM02) -from .cam16_ucs import (JMh_CAM16_to_CAM16LCD, CAM16LCD_to_JMh_CAM16, - JMh_CAM16_to_CAM16SCD, CAM16SCD_to_JMh_CAM16, - JMh_CAM16_to_CAM16UCS, CAM16UCS_to_JMh_CAM16) -from .cie_xyy import (XYZ_to_xyY, xyY_to_XYZ, xy_to_xyY, xyY_to_xy, xy_to_XYZ, - XYZ_to_xy) +from colour.hints import Any + +from .common import ( + Jab_to_JCh, + JCh_to_Jab, + COLOURSPACE_MODELS, + COLOURSPACE_MODELS_AXIS_LABELS, + COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE, +) +from .cam02_ucs import ( + JMh_CIECAM02_to_CAM02LCD, + CAM02LCD_to_JMh_CIECAM02, + JMh_CIECAM02_to_CAM02SCD, + CAM02SCD_to_JMh_CIECAM02, + JMh_CIECAM02_to_CAM02UCS, + CAM02UCS_to_JMh_CIECAM02, + XYZ_to_CAM02LCD, + CAM02LCD_to_XYZ, + XYZ_to_CAM02SCD, + CAM02SCD_to_XYZ, + XYZ_to_CAM02UCS, + CAM02UCS_to_XYZ, +) +from .cam16_ucs import ( + JMh_CAM16_to_CAM16LCD, + CAM16LCD_to_JMh_CAM16, + JMh_CAM16_to_CAM16SCD, + CAM16SCD_to_JMh_CAM16, + JMh_CAM16_to_CAM16UCS, + CAM16UCS_to_JMh_CAM16, + XYZ_to_CAM16LCD, + CAM16LCD_to_XYZ, + XYZ_to_CAM16SCD, + CAM16SCD_to_XYZ, + XYZ_to_CAM16UCS, + CAM16UCS_to_XYZ, +) +from .cie_xyy import ( + XYZ_to_xyY, + xyY_to_XYZ, + xy_to_xyY, + xyY_to_xy, + xy_to_XYZ, + XYZ_to_xy, +) from .cie_lab import XYZ_to_Lab, Lab_to_XYZ, Lab_to_LCHab, LCHab_to_Lab -from .cie_luv import (XYZ_to_Luv, Luv_to_XYZ, Luv_to_uv, uv_to_Luv, - Luv_uv_to_xy, xy_to_Luv_uv, Luv_to_LCHuv, LCHuv_to_Luv) -from .cie_ucs import (XYZ_to_UCS, UCS_to_XYZ, UCS_to_uv, uv_to_UCS, - UCS_uv_to_xy, xy_to_UCS_uv) +from .cie_luv import ( + XYZ_to_Luv, + Luv_to_XYZ, + Luv_to_uv, + uv_to_Luv, + Luv_uv_to_xy, + xy_to_Luv_uv, + Luv_to_LCHuv, + LCHuv_to_Luv, +) +from .cie_ucs import ( + XYZ_to_UCS, + UCS_to_XYZ, + UCS_to_uv, + uv_to_UCS, + UCS_uv_to_xy, + xy_to_UCS_uv, +) from .cie_uvw import XYZ_to_UVW, UVW_to_XYZ -from .din99 import Lab_to_DIN99, DIN99_to_Lab -from .hdr_cie_lab import (HDR_CIELAB_METHODS, XYZ_to_hdr_CIELab, - hdr_CIELab_to_XYZ) -from .hunter_lab import (XYZ_to_K_ab_HunterLab1966, XYZ_to_Hunter_Lab, - Hunter_Lab_to_XYZ) +from .din99 import Lab_to_DIN99, DIN99_to_Lab, XYZ_to_DIN99, DIN99_to_XYZ +from .hdr_cie_lab import ( + HDR_CIELAB_METHODS, + XYZ_to_hdr_CIELab, + hdr_CIELab_to_XYZ, +) +from .hunter_lab import ( + XYZ_to_K_ab_HunterLab1966, + XYZ_to_Hunter_Lab, + Hunter_Lab_to_XYZ, +) from .hunter_rdab import XYZ_to_Hunter_Rdab, Hunter_Rdab_to_XYZ -from .igpgtg import XYZ_to_IGPGTG, IGPGTG_to_XYZ +from .icacb import XYZ_to_ICaCb, ICaCb_to_XYZ +from .igpgtg import XYZ_to_IgPgTg, IgPgTg_to_XYZ from .ipt import XYZ_to_IPT, IPT_to_XYZ, IPT_hue_angle -from .jzazbz import XYZ_to_JzAzBz, JzAzBz_to_XYZ +from .jzazbz import ( + IZAZBZ_METHODS, + XYZ_to_Izazbz, + XYZ_to_Jzazbz, + Izazbz_to_XYZ, + Jzazbz_to_XYZ, +) from .hdr_ipt import HDR_IPT_METHODS, XYZ_to_hdr_IPT, hdr_IPT_to_XYZ +from .oklab import XYZ_to_Oklab, Oklab_to_XYZ from .osa_ucs import XYZ_to_OSA_UCS, OSA_UCS_to_XYZ -from .datasets import * # noqa -from . import datasets -from .rgb import * # noqa -from . import rgb +from .prolab import XYZ_to_ProLab, ProLab_to_XYZ +from .datasets import ( + DATA_MACADAM_1942_ELLIPSES, + CCS_ILLUMINANT_POINTER_GAMUT, + DATA_POINTER_GAMUT_VOLUME, + CCS_POINTER_GAMUT_BOUNDARY, +) +from .rgb import ( + normalised_primary_matrix, + chromatically_adapted_primaries, + primaries_whitepoint, + RGB_luminance_equation, + RGB_luminance, +) +from .rgb import RGB_Colourspace +from .rgb import XYZ_to_RGB, RGB_to_XYZ +from .rgb import matrix_RGB_to_RGB, RGB_to_RGB +from .rgb import ( + CV_range, + legal_to_full, + full_to_legal, + gamma_function, + log_encoding_ACESproxy, + log_decoding_ACESproxy, + log_encoding_ACEScc, + log_decoding_ACEScc, + log_encoding_ACEScct, + log_decoding_ACEScct, + oetf_ARIBSTDB67, + oetf_inverse_ARIBSTDB67, + log_encoding_ALEXALogC, + log_decoding_ALEXALogC, + oetf_BlackmagicFilmGeneration5, + oetf_inverse_BlackmagicFilmGeneration5, + log_encoding_CanonLog, + log_decoding_CanonLog, + log_encoding_CanonLog2, + log_decoding_CanonLog2, + log_encoding_CanonLog3, + log_decoding_CanonLog3, + log_encoding_Cineon, + log_decoding_Cineon, + oetf_DaVinciIntermediate, + oetf_inverse_DaVinciIntermediate, + eotf_inverse_DCDM, + eotf_DCDM, + eotf_inverse_DICOMGSDF, + eotf_DICOMGSDF, + log_encoding_DJIDLog, + log_decoding_DJIDLog, + exponent_function_basic, + exponent_function_monitor_curve, + log_encoding_FilmicPro6, + log_decoding_FilmicPro6, + log_encoding_FilmLightTLog, + log_decoding_FilmLightTLog, + log_encoding_Protune, + log_decoding_Protune, + oetf_BT601, + oetf_inverse_BT601, + oetf_BT709, + oetf_inverse_BT709, + eotf_inverse_BT1886, + eotf_BT1886, + eotf_inverse_BT2020, + eotf_BT2020, + eotf_inverse_ST2084, + eotf_ST2084, + oetf_PQ_BT2100, + oetf_inverse_PQ_BT2100, + eotf_PQ_BT2100, + eotf_inverse_PQ_BT2100, + ootf_PQ_BT2100, + ootf_inverse_PQ_BT2100, + oetf_HLG_BT2100, + oetf_inverse_HLG_BT2100, + BT2100_HLG_EOTF_METHODS, + eotf_HLG_BT2100, + BT2100_HLG_EOTF_INVERSE_METHODS, + eotf_inverse_HLG_BT2100, + BT2100_HLG_OOTF_METHODS, + ootf_HLG_BT2100, + BT2100_HLG_OOTF_INVERSE_METHODS, + ootf_inverse_HLG_BT2100, + linear_function, + logarithmic_function_basic, + logarithmic_function_quasilog, + logarithmic_function_camera, + log_encoding_Log2, + log_decoding_Log2, + log_encoding_Panalog, + log_decoding_Panalog, + log_encoding_VLog, + log_decoding_VLog, + log_encoding_FLog, + log_decoding_FLog, + log_encoding_NLog, + log_decoding_NLog, + log_encoding_PivotedLog, + log_decoding_PivotedLog, + log_encoding_REDLog, + log_decoding_REDLog, + log_encoding_REDLogFilm, + log_decoding_REDLogFilm, + LOG3G10_ENCODING_METHODS, + LOG3G10_DECODING_METHODS, + log_encoding_Log3G10, + log_decoding_Log3G10, + log_encoding_Log3G12, + log_decoding_Log3G12, + cctf_encoding_ROMMRGB, + cctf_decoding_ROMMRGB, + cctf_encoding_ProPhotoRGB, + cctf_decoding_ProPhotoRGB, + cctf_encoding_RIMMRGB, + cctf_decoding_RIMMRGB, + log_encoding_ERIMMRGB, + log_decoding_ERIMMRGB, + oetf_SMPTE240M, + eotf_SMPTE240M, + log_encoding_SLog, + log_decoding_SLog, + log_encoding_SLog2, + log_decoding_SLog2, + log_encoding_SLog3, + log_decoding_SLog3, + eotf_inverse_sRGB, + eotf_sRGB, + log_encoding_ViperLog, + log_decoding_ViperLog, +) +from .rgb import ( + LOG_ENCODINGS, + log_encoding, + LOG_DECODINGS, + log_decoding, + OETFS, + oetf, + OETF_INVERSES, + oetf_inverse, + EOTFS, + eotf, + EOTF_INVERSES, + eotf_inverse, + CCTF_ENCODINGS, + cctf_encoding, + CCTF_DECODINGS, + cctf_decoding, + OOTFS, + ootf, + OOTF_INVERSES, + ootf_inverse, +) +from .rgb import ( + RGB_COLOURSPACES, + RGB_COLOURSPACE_ACES2065_1, + RGB_COLOURSPACE_ACESCC, + RGB_COLOURSPACE_ACESCCT, + RGB_COLOURSPACE_ACESPROXY, + RGB_COLOURSPACE_ACESCG, + RGB_COLOURSPACE_ADOBE_RGB1998, + RGB_COLOURSPACE_ADOBE_WIDE_GAMUT_RGB, + RGB_COLOURSPACE_ALEXA_WIDE_GAMUT, + RGB_COLOURSPACE_APPLE_RGB, + RGB_COLOURSPACE_BEST_RGB, + RGB_COLOURSPACE_BETA_RGB, + RGB_COLOURSPACE_BLACKMAGIC_WIDE_GAMUT, + RGB_COLOURSPACE_BT470_525, + RGB_COLOURSPACE_BT470_625, + RGB_COLOURSPACE_BT709, + RGB_COLOURSPACE_BT2020, + RGB_COLOURSPACE_CIE_RGB, + RGB_COLOURSPACE_CINEMA_GAMUT, + RGB_COLOURSPACE_COLOR_MATCH_RGB, + RGB_COLOURSPACE_DAVINCI_WIDE_GAMUT, + RGB_COLOURSPACE_DCDM_XYZ, + RGB_COLOURSPACE_DCI_P3, + RGB_COLOURSPACE_DCI_P3_P, + RGB_COLOURSPACE_DISPLAY_P3, + RGB_COLOURSPACE_DJI_D_GAMUT, + RGB_COLOURSPACE_DON_RGB_4, + RGB_COLOURSPACE_ECI_RGB_V2, + RGB_COLOURSPACE_EKTA_SPACE_PS_5, + RGB_COLOURSPACE_FILMLIGHT_E_GAMUT, + RGB_COLOURSPACE_PROTUNE_NATIVE, + RGB_COLOURSPACE_MAX_RGB, + RGB_COLOURSPACE_N_GAMUT, + RGB_COLOURSPACE_P3_D65, + RGB_COLOURSPACE_PAL_SECAM, + RGB_COLOURSPACE_RED_COLOR, + RGB_COLOURSPACE_RED_COLOR_2, + RGB_COLOURSPACE_RED_COLOR_3, + RGB_COLOURSPACE_RED_COLOR_4, + RGB_COLOURSPACE_DRAGON_COLOR, + RGB_COLOURSPACE_DRAGON_COLOR_2, + RGB_COLOURSPACE_RED_WIDE_GAMUT_RGB, + RGB_COLOURSPACE_ROMM_RGB, + RGB_COLOURSPACE_RIMM_RGB, + RGB_COLOURSPACE_ERIMM_RGB, + RGB_COLOURSPACE_PROPHOTO_RGB, + RGB_COLOURSPACE_RUSSELL_RGB, + RGB_COLOURSPACE_SHARP_RGB, + RGB_COLOURSPACE_SMPTE_240M, + RGB_COLOURSPACE_SMPTE_C, + RGB_COLOURSPACE_NTSC1953, + RGB_COLOURSPACE_NTSC1987, + RGB_COLOURSPACE_S_GAMUT, + RGB_COLOURSPACE_S_GAMUT3, + RGB_COLOURSPACE_S_GAMUT3_CINE, + RGB_COLOURSPACE_VENICE_S_GAMUT3, + RGB_COLOURSPACE_VENICE_S_GAMUT3_CINE, + RGB_COLOURSPACE_sRGB, + RGB_COLOURSPACE_V_GAMUT, + RGB_COLOURSPACE_XTREME_RGB, + RGB_COLOURSPACE_F_GAMUT, +) + +from .rgb import XYZ_to_sRGB, sRGB_to_XYZ +from .rgb import ( + RGB_to_HSV, + HSV_to_RGB, + RGB_to_HSL, + HSL_to_RGB, + RGB_to_HCL, + HCL_to_RGB, +) +from .rgb import RGB_to_CMY, CMY_to_RGB, CMY_to_CMYK, CMYK_to_CMY +from .rgb import RGB_to_IHLS, IHLS_to_RGB +from .rgb import RGB_to_Prismatic, Prismatic_to_RGB +from .rgb import ( + WEIGHTS_YCBCR, + matrix_YCbCr, + offset_YCbCr, + RGB_to_YCbCr, + YCbCr_to_RGB, + RGB_to_YcCbcCrc, + YcCbcCrc_to_RGB, +) +from .rgb import RGB_to_YCoCg, YCoCg_to_RGB +from .rgb import RGB_to_ICtCp, ICtCp_to_RGB, XYZ_to_ICtCp, ICtCp_to_XYZ __all__ = [ - 'Jab_to_JCh', 'JCh_to_Jab', 'COLOURSPACE_MODELS', - 'COLOURSPACE_MODELS_AXIS_LABELS', 'XYZ_to_colourspace_model' + "Jab_to_JCh", + "JCh_to_Jab", + "COLOURSPACE_MODELS", + "COLOURSPACE_MODELS_AXIS_LABELS", + "COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE", +] +__all__ += [ + "JMh_CIECAM02_to_CAM02LCD", + "CAM02LCD_to_JMh_CIECAM02", + "JMh_CIECAM02_to_CAM02SCD", + "CAM02SCD_to_JMh_CIECAM02", + "JMh_CIECAM02_to_CAM02UCS", + "CAM02UCS_to_JMh_CIECAM02", + "XYZ_to_CAM02LCD", + "CAM02LCD_to_XYZ", + "XYZ_to_CAM02SCD", + "CAM02SCD_to_XYZ", + "XYZ_to_CAM02UCS", + "CAM02UCS_to_XYZ", +] +__all__ += [ + "JMh_CAM16_to_CAM16LCD", + "CAM16LCD_to_JMh_CAM16", + "JMh_CAM16_to_CAM16SCD", + "CAM16SCD_to_JMh_CAM16", + "JMh_CAM16_to_CAM16UCS", + "CAM16UCS_to_JMh_CAM16", + "XYZ_to_CAM16LCD", + "CAM16LCD_to_XYZ", + "XYZ_to_CAM16SCD", + "CAM16SCD_to_XYZ", + "XYZ_to_CAM16UCS", + "CAM16UCS_to_XYZ", +] +__all__ += [ + "XYZ_to_xyY", + "xyY_to_XYZ", + "xy_to_xyY", + "xyY_to_xy", + "xy_to_XYZ", + "XYZ_to_xy", +] +__all__ += [ + "XYZ_to_Lab", + "Lab_to_XYZ", + "Lab_to_LCHab", + "LCHab_to_Lab", +] +__all__ += [ + "XYZ_to_Luv", + "Luv_to_XYZ", + "Luv_to_uv", + "uv_to_Luv", + "Luv_uv_to_xy", + "xy_to_Luv_uv", + "Luv_to_LCHuv", + "LCHuv_to_Luv", +] +__all__ += [ + "XYZ_to_UCS", + "UCS_to_XYZ", + "UCS_to_uv", + "uv_to_UCS", + "UCS_uv_to_xy", + "xy_to_UCS_uv", +] +__all__ += [ + "XYZ_to_UVW", + "UVW_to_XYZ", +] +__all__ += [ + "Lab_to_DIN99", + "DIN99_to_Lab", + "XYZ_to_DIN99", + "DIN99_to_XYZ", +] +__all__ += [ + "HDR_CIELAB_METHODS", + "XYZ_to_hdr_CIELab", + "hdr_CIELab_to_XYZ", +] +__all__ += [ + "XYZ_to_K_ab_HunterLab1966", + "XYZ_to_Hunter_Lab", + "Hunter_Lab_to_XYZ", + "XYZ_to_Hunter_Rdab", +] +__all__ += [ + "XYZ_to_Hunter_Rdab", + "Hunter_Rdab_to_XYZ", +] +__all__ += [ + "XYZ_to_ICaCb", + "ICaCb_to_XYZ", ] __all__ += [ - 'JMh_CIECAM02_to_CAM02LCD', 'CAM02LCD_to_JMh_CIECAM02', - 'JMh_CIECAM02_to_CAM02SCD', 'CAM02SCD_to_JMh_CIECAM02', - 'JMh_CIECAM02_to_CAM02UCS', 'CAM02UCS_to_JMh_CIECAM02' + "XYZ_to_IgPgTg", + "IgPgTg_to_XYZ", ] __all__ += [ - 'JMh_CAM16_to_CAM16LCD', 'CAM16LCD_to_JMh_CAM16', 'JMh_CAM16_to_CAM16SCD', - 'CAM16SCD_to_JMh_CAM16', 'JMh_CAM16_to_CAM16UCS', 'CAM16UCS_to_JMh_CAM16' + "XYZ_to_IPT", + "IPT_to_XYZ", + "IPT_hue_angle", ] __all__ += [ - 'XYZ_to_xyY', 'xyY_to_XYZ', 'xy_to_xyY', 'xyY_to_xy', 'xy_to_XYZ', - 'XYZ_to_xy' + "IZAZBZ_METHODS", + "XYZ_to_Izazbz", + "XYZ_to_Jzazbz", + "Izazbz_to_XYZ", + "Jzazbz_to_XYZ", ] -__all__ += ['XYZ_to_Lab', 'Lab_to_XYZ', 'Lab_to_LCHab', 'LCHab_to_Lab'] __all__ += [ - 'XYZ_to_Luv', 'Luv_to_XYZ', 'Luv_to_uv', 'uv_to_Luv', 'Luv_uv_to_xy', - 'xy_to_Luv_uv', 'Luv_to_LCHuv', 'LCHuv_to_Luv' + "HDR_IPT_METHODS", + "XYZ_to_hdr_IPT", + "hdr_IPT_to_XYZ", ] __all__ += [ - 'XYZ_to_UCS', 'UCS_to_XYZ', 'UCS_to_uv', 'uv_to_UCS', 'UCS_uv_to_xy', - 'xy_to_UCS_uv' + "XYZ_to_Oklab", + "Oklab_to_XYZ", ] -__all__ += ['XYZ_to_UVW', 'UVW_to_XYZ'] -__all__ += ['Lab_to_DIN99', 'DIN99_to_Lab'] -__all__ += ['HDR_CIELAB_METHODS', 'XYZ_to_hdr_CIELab', 'hdr_CIELab_to_XYZ'] __all__ += [ - 'XYZ_to_K_ab_HunterLab1966', 'XYZ_to_Hunter_Lab', 'Hunter_Lab_to_XYZ', - 'XYZ_to_Hunter_Rdab' + "XYZ_to_OSA_UCS", + "OSA_UCS_to_XYZ", +] +__all__ += [ + "XYZ_to_ProLab", + "ProLab_to_XYZ", +] +__all__ += [ + "DATA_MACADAM_1942_ELLIPSES", + "CCS_ILLUMINANT_POINTER_GAMUT", + "DATA_POINTER_GAMUT_VOLUME", + "CCS_POINTER_GAMUT_BOUNDARY", +] +__all__ += [ + "normalised_primary_matrix", + "chromatically_adapted_primaries", + "primaries_whitepoint", + "RGB_luminance_equation", + "RGB_luminance", +] +__all__ += ["RGB_Colourspace"] +__all__ += ["XYZ_to_RGB", "RGB_to_XYZ"] +__all__ += ["matrix_RGB_to_RGB", "RGB_to_RGB"] +__all__ += [ + "CV_range", + "legal_to_full", + "full_to_legal", + "gamma_function", + "log_encoding_ACESproxy", + "log_decoding_ACESproxy", + "log_encoding_ACEScc", + "log_decoding_ACEScc", + "log_encoding_ACEScct", + "log_decoding_ACEScct", + "oetf_ARIBSTDB67", + "oetf_inverse_ARIBSTDB67", + "log_encoding_ALEXALogC", + "log_decoding_ALEXALogC", + "oetf_BlackmagicFilmGeneration5", + "oetf_inverse_BlackmagicFilmGeneration5", + "log_encoding_CanonLog", + "log_decoding_CanonLog", + "log_encoding_CanonLog2", + "log_decoding_CanonLog2", + "log_encoding_CanonLog3", + "log_decoding_CanonLog3", + "log_encoding_Cineon", + "log_decoding_Cineon", + "oetf_DaVinciIntermediate", + "oetf_inverse_DaVinciIntermediate", + "eotf_inverse_DCDM", + "eotf_DCDM", + "eotf_inverse_DICOMGSDF", + "eotf_DICOMGSDF", + "log_encoding_DJIDLog", + "log_decoding_DJIDLog", + "exponent_function_basic", + "exponent_function_monitor_curve", + "log_encoding_FilmicPro6", + "log_decoding_FilmicPro6", + "log_encoding_FilmLightTLog", + "log_decoding_FilmLightTLog", + "log_encoding_Protune", + "log_decoding_Protune", + "oetf_BT601", + "oetf_inverse_BT601", + "oetf_BT709", + "oetf_inverse_BT709", + "eotf_inverse_BT1886", + "eotf_BT1886", + "eotf_inverse_BT2020", + "eotf_BT2020", + "eotf_inverse_ST2084", + "eotf_ST2084", + "oetf_PQ_BT2100", + "oetf_inverse_PQ_BT2100", + "eotf_PQ_BT2100", + "eotf_inverse_PQ_BT2100", + "ootf_PQ_BT2100", + "ootf_inverse_PQ_BT2100", + "oetf_HLG_BT2100", + "oetf_inverse_HLG_BT2100", + "BT2100_HLG_EOTF_METHODS", + "eotf_HLG_BT2100", + "BT2100_HLG_EOTF_INVERSE_METHODS", + "eotf_inverse_HLG_BT2100", + "BT2100_HLG_OOTF_METHODS", + "ootf_HLG_BT2100", + "BT2100_HLG_OOTF_INVERSE_METHODS", + "ootf_inverse_HLG_BT2100", + "linear_function", + "logarithmic_function_basic", + "logarithmic_function_quasilog", + "logarithmic_function_camera", + "log_encoding_Log2", + "log_decoding_Log2", + "log_encoding_Panalog", + "log_decoding_Panalog", + "log_encoding_VLog", + "log_decoding_VLog", + "log_encoding_FLog", + "log_decoding_FLog", + "log_encoding_NLog", + "log_decoding_NLog", + "log_encoding_PivotedLog", + "log_decoding_PivotedLog", + "log_encoding_REDLog", + "log_decoding_REDLog", + "log_encoding_REDLogFilm", + "log_decoding_REDLogFilm", + "LOG3G10_ENCODING_METHODS", + "LOG3G10_DECODING_METHODS", + "log_encoding_Log3G10", + "log_decoding_Log3G10", + "log_encoding_Log3G12", + "log_decoding_Log3G12", + "cctf_encoding_ROMMRGB", + "cctf_decoding_ROMMRGB", + "cctf_encoding_ProPhotoRGB", + "cctf_decoding_ProPhotoRGB", + "cctf_encoding_RIMMRGB", + "cctf_decoding_RIMMRGB", + "log_encoding_ERIMMRGB", + "log_decoding_ERIMMRGB", + "oetf_SMPTE240M", + "eotf_SMPTE240M", + "log_encoding_SLog", + "log_decoding_SLog", + "log_encoding_SLog2", + "log_decoding_SLog2", + "log_encoding_SLog3", + "log_decoding_SLog3", + "eotf_inverse_sRGB", + "eotf_sRGB", + "log_encoding_ViperLog", + "log_decoding_ViperLog", +] +__all__ += [ + "LOG_ENCODINGS", + "log_encoding", + "LOG_DECODINGS", + "log_decoding", + "OETFS", + "oetf", + "OETF_INVERSES", + "oetf_inverse", + "EOTFS", + "eotf", + "EOTF_INVERSES", + "eotf_inverse", + "CCTF_ENCODINGS", + "cctf_encoding", + "CCTF_DECODINGS", + "cctf_decoding", + "OOTFS", + "ootf", + "OOTF_INVERSES", + "ootf_inverse", +] +__all__ += [ + "RGB_COLOURSPACES", + "RGB_COLOURSPACE_ACES2065_1", + "RGB_COLOURSPACE_ACESCC", + "RGB_COLOURSPACE_ACESCCT", + "RGB_COLOURSPACE_ACESPROXY", + "RGB_COLOURSPACE_ACESCG", + "RGB_COLOURSPACE_ADOBE_RGB1998", + "RGB_COLOURSPACE_ADOBE_WIDE_GAMUT_RGB", + "RGB_COLOURSPACE_ALEXA_WIDE_GAMUT", + "RGB_COLOURSPACE_APPLE_RGB", + "RGB_COLOURSPACE_BEST_RGB", + "RGB_COLOURSPACE_BETA_RGB", + "RGB_COLOURSPACE_BLACKMAGIC_WIDE_GAMUT", + "RGB_COLOURSPACE_BT470_525", + "RGB_COLOURSPACE_BT470_625", + "RGB_COLOURSPACE_BT709", + "RGB_COLOURSPACE_BT2020", + "RGB_COLOURSPACE_CIE_RGB", + "RGB_COLOURSPACE_CINEMA_GAMUT", + "RGB_COLOURSPACE_COLOR_MATCH_RGB", + "RGB_COLOURSPACE_DAVINCI_WIDE_GAMUT", + "RGB_COLOURSPACE_DCDM_XYZ", + "RGB_COLOURSPACE_DCI_P3", + "RGB_COLOURSPACE_DCI_P3_P", + "RGB_COLOURSPACE_DISPLAY_P3", + "RGB_COLOURSPACE_DJI_D_GAMUT", + "RGB_COLOURSPACE_DON_RGB_4", + "RGB_COLOURSPACE_ECI_RGB_V2", + "RGB_COLOURSPACE_EKTA_SPACE_PS_5", + "RGB_COLOURSPACE_FILMLIGHT_E_GAMUT", + "RGB_COLOURSPACE_PROTUNE_NATIVE", + "RGB_COLOURSPACE_MAX_RGB", + "RGB_COLOURSPACE_N_GAMUT", + "RGB_COLOURSPACE_P3_D65", + "RGB_COLOURSPACE_PAL_SECAM", + "RGB_COLOURSPACE_RED_COLOR", + "RGB_COLOURSPACE_RED_COLOR_2", + "RGB_COLOURSPACE_RED_COLOR_3", + "RGB_COLOURSPACE_RED_COLOR_4", + "RGB_COLOURSPACE_DRAGON_COLOR", + "RGB_COLOURSPACE_DRAGON_COLOR_2", + "RGB_COLOURSPACE_RED_WIDE_GAMUT_RGB", + "RGB_COLOURSPACE_ROMM_RGB", + "RGB_COLOURSPACE_RIMM_RGB", + "RGB_COLOURSPACE_ERIMM_RGB", + "RGB_COLOURSPACE_PROPHOTO_RGB", + "RGB_COLOURSPACE_RUSSELL_RGB", + "RGB_COLOURSPACE_SHARP_RGB", + "RGB_COLOURSPACE_SMPTE_240M", + "RGB_COLOURSPACE_SMPTE_C", + "RGB_COLOURSPACE_NTSC1953", + "RGB_COLOURSPACE_NTSC1987", + "RGB_COLOURSPACE_S_GAMUT", + "RGB_COLOURSPACE_S_GAMUT3", + "RGB_COLOURSPACE_S_GAMUT3_CINE", + "RGB_COLOURSPACE_VENICE_S_GAMUT3", + "RGB_COLOURSPACE_VENICE_S_GAMUT3_CINE", + "RGB_COLOURSPACE_sRGB", + "RGB_COLOURSPACE_V_GAMUT", + "RGB_COLOURSPACE_XTREME_RGB", + "RGB_COLOURSPACE_F_GAMUT", ] -__all__ += ['XYZ_to_Hunter_Rdab', 'Hunter_Rdab_to_XYZ'] -__all__ += ['XYZ_to_IGPGTG', 'IGPGTG_to_XYZ'] -__all__ += ['XYZ_to_IPT', 'IPT_to_XYZ', 'IPT_hue_angle'] -__all__ += ['XYZ_to_JzAzBz', 'JzAzBz_to_XYZ'] -__all__ += ['HDR_IPT_METHODS', 'XYZ_to_hdr_IPT', 'hdr_IPT_to_XYZ'] -__all__ += ['XYZ_to_OSA_UCS', 'OSA_UCS_to_XYZ'] -__all__ += datasets.__all__ -__all__ += rgb.__all__ + +__all__ += ["XYZ_to_sRGB", "sRGB_to_XYZ"] +__all__ += [ + "RGB_to_HSV", + "HSV_to_RGB", + "RGB_to_HSL", + "HSL_to_RGB", + "RGB_to_HCL", + "HCL_to_RGB", +] +__all__ += ["RGB_to_CMY", "CMY_to_RGB", "CMY_to_CMYK", "CMYK_to_CMY"] +__all__ += ["RGB_to_IHLS", "IHLS_to_RGB"] +__all__ += ["RGB_to_Prismatic", "Prismatic_to_RGB"] +__all__ += [ + "WEIGHTS_YCBCR", + "matrix_YCbCr", + "offset_YCbCr", + "RGB_to_YCbCr", + "YCbCr_to_RGB", + "RGB_to_YcCbcCrc", + "YcCbcCrc_to_RGB", +] +__all__ += ["RGB_to_YCoCg", "YCoCg_to_RGB"] +__all__ += ["RGB_to_ICtCp", "ICtCp_to_RGB", "XYZ_to_ICtCp", "ICtCp_to_XYZ"] # ----------------------------------------------------------------------------# # --- API Changes and Deprecation Management ---# # ----------------------------------------------------------------------------# class models(ModuleAPI): - def __getattr__(self, attribute): - return super(models, self).__getattr__(attribute) + """Define a class acting like the *models* module.""" + + def __getattr__(self, attribute) -> Any: + """Return the value from the attribute with given name.""" + return super().__getattr__(attribute) -# v0.3.14 + +# v0.4.0 API_CHANGES = { - 'ObjectFutureRemove': ['colour.models.XYZ_to_colourspace_model', ], - 'ObjectRenamed': [ - [ - 'colour.models.decoding_cctf', - 'colour.models.cctf_decoding', - ], - [ - 'colour.models.DECODING_CCTFS', - 'colour.models.CCTF_DECODINGS', - ], - [ - 'colour.models.encoding_cctf', - 'colour.models.cctf_encoding', - ], - [ - 'colour.models.ENCODING_CCTFS', - 'colour.models.CCTF_ENCODINGS', - ], + "ObjectRenamed": [ [ - 'colour.models.log_decoding_curve', - 'colour.models.log_decoding', + "colour.models.RGB_to_ICTCP", + "colour.models.RGB_to_ICtCp", ], [ - 'colour.models.LOG_DECODING_CURVES', - 'colour.models.LOG_DECODINGS', + "colour.models.ICTCP_to_RGB", + "colour.models.ICtCp_to_RGB", ], [ - 'colour.models.log_encoding_curve', - 'colour.models.log_encoding', + "colour.models.RGB_to_IGPGTG", + "colour.models.RGB_to_IgPgTg", ], [ - 'colour.models.LOG_ENCODING_CURVES', - 'colour.models.LOG_ENCODINGS', + "colour.models.IGPGTG_to_RGB", + "colour.models.IgPgTg_to_RGB", ], [ - 'colour.models.oetf_ROMMRGB', - 'colour.models.cctf_encoding_ROMMRGB', + "colour.models.XYZ_to_JzAzBz", + "colour.models.XYZ_to_Jzazbz", ], [ - 'colour.models.oetf_RIMMRGB', - 'colour.models.cctf_encoding_RIMMRGB', - ], - [ - 'colour.models.oetf_ProPhotoRGB', - 'colour.models.cctf_encoding_ProPhotoRGB', - ], - [ - 'colour.models.oetf_ST2084', - 'colour.models.eotf_inverse_ST2084', - ], - [ - 'colour.models.oetf_sRGB', - 'colour.models.eotf_inverse_sRGB', - ], - [ - 'colour.models.oetf_inverse_sRGB', - 'colour.models.eotf_sRGB', - ], - [ - 'colour.models.oetf_BT2100_HLG', - 'colour.models.oetf_HLG_BT2100', - ], - [ - 'colour.models.oetf_reverse_ARIBSTDB67', - 'colour.models.oetf_inverse_ARIBSTDB67' - ], - [ - 'colour.models.oetf_reverse_BT2100_HLG', - 'colour.models.oetf_inverse_HLG_BT2100', - ], - [ - 'colour.models.oetf_reverse_BT601', - 'colour.models.oetf_inverse_BT601', - ], - [ - 'colour.models.oetf_reverse_BT709', - 'colour.models.oetf_inverse_BT709', - ], - [ - 'colour.models.eotf_ROMMRGB', - 'colour.models.cctf_decoding_ROMMRGB', - ], - [ - 'colour.models.eotf_RIMMRGB', - 'colour.models.cctf_decoding_RIMMRGB', - ], - [ - 'colour.models.eotf_ProPhotoRGB', - 'colour.models.cctf_decoding_ProPhotoRGB', - ], - [ - 'colour.models.eotf_reverse_BT1886', - 'colour.models.eotf_inverse_BT1886', - ], - [ - 'colour.models.eotf_BT2100_HLG', - 'colour.models.eotf_HLG_BT2100', - ], - [ - 'colour.models.eotf_reverse_BT2100_HLG', - 'colour.models.eotf_inverse_HLG_BT2100', - ], - [ - 'colour.models.eotf_reverse_DCDM', - 'colour.models.eotf_inverse_DCDM', - ], - [ - 'colour.models.eotf_reverse_sRGB', - 'colour.models.eotf_inverse_sRGB', - ], - [ - 'colour.models.eotf_reverse_ST2084', - 'colour.models.eotf_inverse_ST2084', - ], - [ - 'colour.models.ootf_BT2100_HLG', - 'colour.models.ootf_HLG_BT2100', - ], - [ - 'colour.models.ootf_reverse_BT2100_HLG', - 'colour.models.ootf_inverse_HLG_BT2100', - ], - [ - 'colour.models.oetf_BT2100_PQ', - 'colour.models.oetf_PQ_BT2100', - ], - [ - 'colour.models.oetf_reverse_BT2100_PQ', - 'colour.models.oetf_inverse_PQ_BT2100', - ], - [ - 'colour.models.eotf_BT2100_PQ', - 'colour.models.eotf_PQ_BT2100', - ], - [ - 'colour.models.eotf_reverse_BT2100_PQ', - 'colour.models.eotf_inverse_PQ_BT2100', - ], - [ - 'colour.models.ootf_BT2100_PQ', - 'colour.models.ootf_PQ_BT2100', - ], - [ - 'colour.models.ootf_reverse_BT2100_PQ', - 'colour.models.ootf_inverse_PQ_BT2100', + "colour.models.JzAzBz_to_XYZ", + "colour.models.Jzazbz_to_XYZ", ], ] } -""" -Defines *colour.models* sub-package API changes. - -API_CHANGES : dict -""" - -# v0.3.16 -API_CHANGES['ObjectRenamed'] = API_CHANGES['ObjectRenamed'] + [ - [ - 'colour.models.ACES_2065_1_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ACES2065_1', - ], - [ - 'colour.models.ACES_CC_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ACESCC', - ], - [ - 'colour.models.ACES_CCT_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ACESCCT', - ], - [ - 'colour.models.ACES_CG_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ACESCG', - ], - [ - 'colour.models.ACES_PROXY_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ACESPROXY', - ], - [ - 'colour.models.ADOBE_RGB_1998_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ADOBE_RGB1998', - ], - [ - 'colour.models.ADOBE_WIDE_GAMUT_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ADOBE_WIDE_GAMUT_RGB', - ], - [ - 'colour.models.APPLE_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_APPLE_RGB', - ], - [ - 'colour.models.ALEXA_WIDE_GAMUT_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ALEXA_WIDE_GAMUT', - ], - [ - 'colour.models.BEST_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_BEST_RGB', - ], - [ - 'colour.models.BETA_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_BETA_RGB', - ], - [ - 'colour.models.CIE_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_CIE_RGB', - ], - [ - 'colour.models.CINEMA_GAMUT_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_CINEMA_GAMUT', - ], - [ - 'colour.models.COLOR_MATCH_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_COLOR_MATCH_RGB', - ], - [ - 'colour.models.DCDM_XYZ_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_DCDM_XYZ', - ], - [ - 'colour.models.DCI_P3_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_DCI_P3_P', - ], - [ - 'colour.models.DCI_P3_P_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_DCI_P3', - ], - [ - 'colour.models.DISPLAY_P3_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_DISPLAY_P3', - ], - [ - 'colour.models.P3_D65_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_P3_D65', - ], - [ - 'colour.models.DON_RGB_4_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_DON_RGB_4', - ], - [ - 'colour.models.DJI_D_GAMUT_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_DJI_D_GAMUT', - ], - [ - 'colour.models.ECI_RGB_V2_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ECI_RGB_V2', - ], - [ - 'colour.models.EKTA_SPACE_PS_5_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_EKTA_SPACE_PS_5', - ], - [ - 'colour.models.F_GAMUT_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_F_GAMUT', - ], - [ - 'colour.models.FILMLIGHT_E_GAMUT_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_FILMLIGHT_E_GAMUT', - ], - [ - 'colour.models.PROTUNE_NATIVE_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_PROTUNE_NATIVE', - ], - [ - 'colour.models.BT470_525_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_BT470_525', - ], - [ - 'colour.models.BT470_625_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_BT470_625', - ], - [ - 'colour.models.BT709_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_BT709', - ], - [ - 'colour.models.BT2020_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_BT2020', - ], - [ - 'colour.models.MAX_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_MAX_RGB', - ], - [ - 'colour.models.PAL_SECAM_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_PAL_SECAM', - ], - [ - 'colour.models.RED_COLOR_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_RED_COLOR', - ], - [ - 'colour.models.RED_COLOR_2_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_RED_COLOR_2', - ], - [ - 'colour.models.RED_COLOR_3_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_RED_COLOR_3', - ], - [ - 'colour.models.RED_COLOR_4_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_RED_COLOR_4', - ], - [ - 'colour.models.DRAGON_COLOR_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_DRAGON_COLOR', - ], - [ - 'colour.models.DRAGON_COLOR_2_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_DRAGON_COLOR_2', - ], - [ - 'colour.models.RED_WIDE_GAMUT_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_RED_WIDE_GAMUT_RGB', - ], - [ - 'colour.models.ROMM_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ROMM_RGB', - ], - [ - 'colour.models.RIMM_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_RIMM_RGB', - ], - [ - 'colour.models.ERIMM_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_ERIMM_RGB', - ], - [ - 'colour.models.PROPHOTO_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_PROPHOTO_RGB', - ], - [ - 'colour.models.RUSSELL_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_RUSSELL_RGB', - ], - [ - 'colour.models.SHARP_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_SHARP_RGB', - ], - [ - 'colour.models.SMPTE_240M_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_SMPTE_240M', - ], - [ - 'colour.models.SMPTE_C_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_SMPTE_C', - ], - [ - 'colour.models.NTSC_1953_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_NTSC1953', - ], - [ - 'colour.models.NTSC_1987_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_NTSC1987', - ], - [ - 'colour.models.S_GAMUT_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_S_GAMUT', - ], - [ - 'colour.models.S_GAMUT3_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_S_GAMUT3', - ], - [ - 'colour.models.S_GAMUT3_CINE_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_S_GAMUT3_CINE', - ], - [ - 'colour.models.VENICE_S_GAMUT3_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_VENICE_S_GAMUT3', - ], - [ - 'colour.models.VENICE_S_GAMUT3_CINE_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_VENICE_S_GAMUT3_CINE', - ], - [ - 'colour.models.sRGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_sRGB', - ], - [ - 'colour.models.V_GAMUT_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_V_GAMUT', - ], - [ - 'colour.models.XTREME_RGB_COLOURSPACE', - 'colour.models.RGB_COLOURSPACE_XTREME_RGB', - ], - [ - 'colour.models.ACES_RICD', - 'colour.characterisation.MSDS_ACES_RICD', - ], - [ - 'colour.models.oetf_BT2020', - 'colour.models.eotf_inverse_BT2020', - ], - [ - 'colour.models.POINTER_GAMUT_BOUNDARIES', - 'colour.models.CCS_POINTER_GAMUT_BOUNDARY', - ], - [ - 'colour.models.POINTER_GAMUT_DATA', - 'colour.models.DATA_POINTER_GAMUT_VOLUME', - ], - [ - 'colour.models.POINTER_GAMUT_ILLUMINANT', - 'colour.models.CCS_ILLUMINANT_POINTER_GAMUT', - ], - [ - 'colour.models.YCBCR_WEIGHTS', - 'colour.models.WEIGHTS_YCBCR', - ], - [ - 'colour.models.RGB_to_RGB_matrix', - 'colour.models.matrix_RGB_to_RGB', - ], -] +"""Defines the *colour.models* sub-package API changes.""" if not is_documentation_building(): - sys.modules['colour.models'] = models(sys.modules['colour.models'], - build_API_changes(API_CHANGES)) + sys.modules["colour.models"] = models( # type: ignore[assignment] + sys.modules["colour.models"], build_API_changes(API_CHANGES) + ) del ModuleAPI, is_documentation_building, build_API_changes, sys diff --git a/colour/models/cam02_ucs.py b/colour/models/cam02_ucs.py index 31654d23a3..8a7496a38b 100644 --- a/colour/models/cam02_ucs.py +++ b/colour/models/cam02_ucs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ CAM02-LCD, CAM02-SCD, and CAM02-UCS Colourspaces - Luo, Cui and Li (2006) ========================================================================= @@ -12,6 +11,12 @@ - :func:`colour.CAM02SCD_to_JMh_CIECAM02` - :func:`colour.JMh_CIECAM02_to_CAM02UCS` - :func:`colour.CAM02UCS_to_JMh_CIECAM02` +- :func:`colour.XYZ_to_CAM02LCD` +- :func:`colour.CAM02LCD_to_XYZ` +- :func:`colour.XYZ_to_CAM02SCD` +- :func:`colour.CAM02SCD_to_XYZ` +- :func:`colour.XYZ_to_CAM02UCS` +- :func:`colour.CAM02UCS_to_XYZ` References ---------- @@ -20,57 +25,81 @@ Application, 31(4), 320-330. doi:10.1002/col.20227 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from collections import namedtuple from colour.algebra import cartesian_to_polar, polar_to_cartesian -from colour.utilities import (CaseInsensitiveMapping, from_range_100, - from_range_degrees, to_domain_100, - to_domain_degrees, tsplit, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import Any, ArrayLike, NDArray +from colour.utilities import ( + CaseInsensitiveMapping, + as_float_array, + from_range_100, + from_range_degrees, + get_domain_range_scale, + to_domain_100, + to_domain_degrees, + tsplit, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'Coefficients_UCS_Luo2006', 'COEFFICIENTS_UCS_LUO2006', - 'JMh_CIECAM02_to_UCS_Luo2006', 'UCS_Luo2006_to_JMh_CIECAM02', - 'JMh_CIECAM02_to_CAM02LCD', 'CAM02LCD_to_JMh_CIECAM02', - 'JMh_CIECAM02_to_CAM02SCD', 'CAM02SCD_to_JMh_CIECAM02', - 'JMh_CIECAM02_to_CAM02UCS', 'CAM02UCS_to_JMh_CIECAM02' + "Coefficients_UCS_Luo2006", + "COEFFICIENTS_UCS_LUO2006", + "JMh_CIECAM02_to_UCS_Luo2006", + "UCS_Luo2006_to_JMh_CIECAM02", + "JMh_CIECAM02_to_CAM02LCD", + "CAM02LCD_to_JMh_CIECAM02", + "JMh_CIECAM02_to_CAM02SCD", + "CAM02SCD_to_JMh_CIECAM02", + "JMh_CIECAM02_to_CAM02UCS", + "CAM02UCS_to_JMh_CIECAM02", + "XYZ_to_UCS_Luo2006", + "UCS_Luo2006_to_XYZ", + "XYZ_to_CAM02LCD", + "CAM02LCD_to_XYZ", + "XYZ_to_CAM02SCD", + "CAM02SCD_to_XYZ", + "XYZ_to_CAM02UCS", + "CAM02UCS_to_XYZ", ] class Coefficients_UCS_Luo2006( - namedtuple('Coefficients_UCS_Luo2006', ('K_L', 'c_1', 'c_2'))): + namedtuple("Coefficients_UCS_Luo2006", ("K_L", "c_1", "c_2")) +): """ - Defines the the class storing *Luo et al. (2006)* fitting coefficients for + Define the class storing *Luo et al. (2006)* fitting coefficients for the *CAM02-LCD*, *CAM02-SCD*, and *CAM02-UCS* colourspaces. """ -COEFFICIENTS_UCS_LUO2006 = CaseInsensitiveMapping({ - 'CAM02-LCD': Coefficients_UCS_Luo2006(0.77, 0.007, 0.0053), - 'CAM02-SCD': Coefficients_UCS_Luo2006(1.24, 0.007, 0.0363), - 'CAM02-UCS': Coefficients_UCS_Luo2006(1.00, 0.007, 0.0228) -}) +COEFFICIENTS_UCS_LUO2006: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "CAM02-LCD": Coefficients_UCS_Luo2006(0.77, 0.007, 0.0053), + "CAM02-SCD": Coefficients_UCS_Luo2006(1.24, 0.007, 0.0363), + "CAM02-UCS": Coefficients_UCS_Luo2006(1.00, 0.007, 0.0228), + } +) """ *Luo et al. (2006)* fitting coefficients for the *CAM02-LCD*, *CAM02-SCD*, and *CAM02-UCS* colourspaces. - -COEFFICIENTS_UCS_LUO2006 : CaseInsensitiveMapping - **{'CAM02-LCD', 'CAM02-SCD', 'CAM02-UCS'}** """ -def JMh_CIECAM02_to_UCS_Luo2006(JMh, coefficients): +def JMh_CIECAM02_to_UCS_Luo2006( + JMh: ArrayLike, coefficients: ArrayLike +) -> NDArray: """ - Converts from *CIECAM02* :math:`JMh` correlates array to one of the + Convert from *CIECAM02* :math:`JMh` correlates array to one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces :math:`J'a'b'` array. @@ -81,40 +110,39 @@ def JMh_CIECAM02_to_UCS_Luo2006(JMh, coefficients): Parameters ---------- - JMh : array_like + JMh *CIECAM02* correlates array :math:`JMh`. - coefficients : array_like + coefficients Coefficients of one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces. Returns ------- - ndarray + :class:`numpy.ndarray` *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces :math:`J'a'b'` array. Notes ----- - - +------------+------------------------+--------------------+ - | **Domain** | **Scale - Reference** | **Scale - 1** | - +============+========================+====================+ - | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | - | | | | - | | ``M`` : [0, 100] | ``M`` : [0, 1] | - | | | | - | | ``h`` : [0, 360] | ``h`` : [0, 1] | - +------------+------------------------+--------------------+ - - +------------+------------------------+--------------------+ - | **Range** | **Scale - Reference** | **Scale - 1** | - +============+========================+====================+ - | ``Jpapbp`` | ``Jp_1`` : [0, 100] | ``Jp_1`` : [0, 1] | - | | | | - | | ``ap_1`` : [-100, 100] | ``ap_1`` : [-1, 1] | - | | | | - | | ``bp_1`` : [-100, 100] | ``bp_1`` : [-1, 1] | - +------------+------------------------+--------------------+ + +------------+------------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | + | | | | + | | ``M`` : [0, 100] | ``M`` : [0, 1] | + | | | | + | | ``h`` : [0, 360] | ``h`` : [0, 1] | + +------------+------------------------+------------------+ + + +------------+------------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | + | | | | + | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | + | | | | + | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | + +------------+------------------------+------------------+ Examples -------- @@ -142,7 +170,7 @@ def JMh_CIECAM02_to_UCS_Luo2006(JMh, coefficients): _K_L, c_1, c_2 = tsplit(coefficients) J_p = ((1 + 100 * c_1) * J) / (1 + c_1 * J) - M_p = (1 / c_2) * np.log(1 + c_2 * M) + M_p = (1 / c_2) * np.log1p(c_2 * M) a_p, b_p = tsplit(polar_to_cartesian(tstack([M_p, np.radians(h)]))) @@ -151,48 +179,49 @@ def JMh_CIECAM02_to_UCS_Luo2006(JMh, coefficients): return from_range_100(Jpapbp) -def UCS_Luo2006_to_JMh_CIECAM02(Jpapbp, coefficients): +def UCS_Luo2006_to_JMh_CIECAM02( + Jpapbp: ArrayLike, coefficients: ArrayLike +) -> NDArray: """ - Converts from one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or + Convert from one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces :math:`J'a'b'` array to *CIECAM02* :math:`JMh` correlates array. Parameters ---------- - Jpapbp : array_like + Jpapbp *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces :math:`J'a'b'` array. - coefficients : array_like + coefficients Coefficients of one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces. Returns ------- - ndarray + :class:`numpy.ndarray` *CIECAM02* correlates array :math:`JMh`. Notes ----- - - +------------+------------------------+--------------------+ - | **Domain** | **Scale - Reference** | **Scale - 1** | - +============+========================+====================+ - | ``Jpapbp`` | ``Jp_1`` : [0, 100] | ``Jp_1`` : [0, 1] | - | | | | - | | ``ap_1`` : [-100, 100] | ``ap_1`` : [-1, 1] | - | | | | - | | ``bp_1`` : [-100, 100] | ``bp_1`` : [-1, 1] | - +------------+------------------------+--------------------+ - - +------------+------------------------+--------------------+ - | **Range** | **Scale - Reference** | **Scale - 1** | - +============+========================+====================+ - | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | - | | | | - | | ``M`` : [0, 100] | ``M`` : [0, 1] | - | | | | - | | ``h`` : [0, 360] | ``h`` : [0, 1] | - +------------+------------------------+--------------------+ + +------------+------------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | + | | | | + | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | + | | | | + | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | + +------------+------------------------+------------------+ + + +------------+------------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | + | | | | + | | ``M`` : [0, 100] | ``M`` : [0, 1] | + | | | | + | | ``h`` : [0, 360] | ``h`` : [0, 1] | + +------------+------------------------+------------------+ Examples -------- @@ -210,54 +239,57 @@ def UCS_Luo2006_to_JMh_CIECAM02(Jpapbp, coefficients): M_p, h = tsplit(cartesian_to_polar(tstack([a_p, b_p]))) - M = (np.exp(M_p / (1 / c_2)) - 1) / c_2 + M = np.expm1(M_p / (1 / c_2)) / c_2 - JMh = tstack([ - from_range_100(J), - from_range_100(M), - from_range_degrees(np.degrees(h) % 360) - ]) + JMh = tstack( + [ + from_range_100(J), + from_range_100(M), + from_range_degrees(np.degrees(h) % 360), + ] + ) return JMh -def JMh_CIECAM02_to_CAM02LCD(JMh): +def JMh_CIECAM02_to_CAM02LCD(JMh: ArrayLike) -> NDArray: """ - Converts from *CIECAM02* :math:`JMh` correlates array to + Convert from *CIECAM02* :math:`JMh` correlates array to *Luo et al. (2006)* *CAM02-LCD* colourspace :math:`J'a'b'` array. Parameters ---------- - JMh : array_like + JMh *CIECAM02* correlates array :math:`JMh`. Returns ------- - ndarray + :class:`numpy.ndarray` *Luo et al. (2006)* *CAM02-LCD* colourspace :math:`J'a'b'` array. Notes ----- - - +------------+------------------------+--------------------+ - | **Domain** | **Scale - Reference** | **Scale - 1** | - +============+========================+====================+ - | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | - | | | | - | | ``M`` : [0, 100] | ``M`` : [0, 1] | - | | | | - | | ``h`` : [0, 360] | ``h`` : [0, 1] | - +------------+------------------------+--------------------+ - - +------------+------------------------+--------------------+ - | **Range** | **Scale - Reference** | **Scale - 1** | - +============+========================+====================+ - | ``Jpapbp`` | ``Jp_1`` : [0, 100] | ``Jp_1`` : [0, 1] | - | | | | - | | ``ap_1`` : [-100, 100] | ``ap_1`` : [-1, 1] | - | | | | - | | ``bp_1`` : [-100, 100] | ``bp_1`` : [-1, 1] | - +------------+------------------------+--------------------+ + - *LCD* in *CAM02-LCD* stands for *Large Colour Differences*. + + +------------+------------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | + | | | | + | | ``M`` : [0, 100] | ``M`` : [0, 1] | + | | | | + | | ``h`` : [0, 360] | ``h`` : [0, 1] | + +------------+------------------------+------------------+ + + +------------+------------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | + | | | | + | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | + | | | | + | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | + +------------+------------------------+------------------+ References ---------- @@ -281,46 +313,48 @@ def JMh_CIECAM02_to_CAM02LCD(JMh): """ return JMh_CIECAM02_to_UCS_Luo2006( - JMh, coefficients=COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) + JMh, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ) -def CAM02LCD_to_JMh_CIECAM02(Jpapbp): +def CAM02LCD_to_JMh_CIECAM02(Jpapbp: ArrayLike) -> NDArray: """ - Converts from *Luo et al. (2006)* *CAM02-LCD* colourspace :math:`J'a'b'` + Convert from *Luo et al. (2006)* *CAM02-LCD* colourspace :math:`J'a'b'` array to *CIECAM02* :math:`JMh` correlates array. Parameters ---------- - Jpapbp : array_like + Jpapbp *Luo et al. (2006)* *CAM02-LCD* colourspace :math:`J'a'b'` array. Returns ------- - ndarray + :class:`numpy.ndarray` *CIECAM02* correlates array :math:`JMh`. Notes ----- - - +------------+------------------------+--------------------+ - | **Domain** | **Scale - Reference** | **Scale - 1** | - +============+========================+====================+ - | ``Jpapbp`` | ``Jp_1`` : [0, 100] | ``Jp_1`` : [0, 1] | - | | | | - | | ``ap_1`` : [-100, 100] | ``ap_1`` : [-1, 1] | - | | | | - | | ``bp_1`` : [-100, 100] | ``bp_1`` : [-1, 1] | - +------------+------------------------+--------------------+ - - +------------+------------------------+--------------------+ - | **Range** | **Scale - Reference** | **Scale - 1** | - +============+========================+====================+ - | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | - | | | | - | | ``M`` : [0, 100] | ``M`` : [0, 1] | - | | | | - | | ``h`` : [0, 360] | ``h`` : [0, 1] | - +------------+------------------------+--------------------+ + - *LCD* in *CAM02-LCD* stands for *Large Colour Differences*. + + +------------+------------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | + | | | | + | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | + | | | | + | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | + +------------+------------------------+------------------+ + + +------------+------------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | + | | | | + | | ``M`` : [0, 100] | ``M`` : [0, 1] | + | | | | + | | ``h`` : [0, 360] | ``h`` : [0, 1] | + +------------+------------------------+------------------+ References ---------- @@ -334,46 +368,48 @@ def CAM02LCD_to_JMh_CIECAM02(Jpapbp): """ return UCS_Luo2006_to_JMh_CIECAM02( - Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) + Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ) -def JMh_CIECAM02_to_CAM02SCD(JMh): +def JMh_CIECAM02_to_CAM02SCD(JMh: ArrayLike) -> NDArray: """ - Converts from *CIECAM02* :math:`JMh` correlates array to + Convert from *CIECAM02* :math:`JMh` correlates array to *Luo et al. (2006)* *CAM02-SCD* colourspace :math:`J'a'b'` array. Parameters ---------- - JMh : array_like + JMh *CIECAM02* correlates array :math:`JMh`. Returns ------- - ndarray + :class:`numpy.ndarray` *Luo et al. (2006)* *CAM02-SCD* colourspace :math:`J'a'b'` array. Notes ----- - - +------------+------------------------+--------------------+ - | **Domain** | **Scale - Reference** | **Scale - 1** | - +============+========================+====================+ - | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | - | | | | - | | ``M`` : [0, 100] | ``M`` : [0, 1] | - | | | | - | | ``h`` : [0, 360] | ``h`` : [0, 1] | - +------------+------------------------+--------------------+ - - +------------+------------------------+--------------------+ - | **Range** | **Scale - Reference** | **Scale - 1** | - +============+========================+====================+ - | ``Jpapbp`` | ``Jp_1`` : [0, 100] | ``Jp_1`` : [0, 1] | - | | | | - | | ``ap_1`` : [-100, 100] | ``ap_1`` : [-1, 1] | - | | | | - | | ``bp_1`` : [-100, 100] | ``bp_1`` : [-1, 1] | - +------------+------------------------+--------------------+ + - *SCD* in *CAM02-SCD* stands for *Small Colour Differences*. + + +------------+------------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | + | | | | + | | ``M`` : [0, 100] | ``M`` : [0, 1] | + | | | | + | | ``h`` : [0, 360] | ``h`` : [0, 1] | + +------------+------------------------+------------------+ + + +------------+------------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | + | | | | + | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | + | | | | + | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | + +------------+------------------------+------------------+ References ---------- @@ -397,46 +433,48 @@ def JMh_CIECAM02_to_CAM02SCD(JMh): """ return JMh_CIECAM02_to_UCS_Luo2006( - JMh, coefficients=COEFFICIENTS_UCS_LUO2006['CAM02-SCD']) + JMh, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-SCD"] + ) -def CAM02SCD_to_JMh_CIECAM02(Jpapbp): +def CAM02SCD_to_JMh_CIECAM02(Jpapbp: ArrayLike) -> NDArray: """ - Converts from *Luo et al. (2006)* *CAM02-SCD* colourspace :math:`J'a'b'` + Convert from *Luo et al. (2006)* *CAM02-SCD* colourspace :math:`J'a'b'` array to *CIECAM02* :math:`JMh` correlates array. Parameters ---------- - Jpapbp : array_like + Jpapbp *Luo et al. (2006)* *CAM02-SCD* colourspace :math:`J'a'b'` array. Returns ------- - ndarray + :class:`numpy.ndarray` *CIECAM02* correlates array :math:`JMh`. Notes ----- - - +------------+------------------------+--------------------+ - | **Domain** | **Scale - Reference** | **Scale - 1** | - +============+========================+====================+ - | ``Jpapbp`` | ``Jp_1`` : [0, 100] | ``Jp_1`` : [0, 1] | - | | | | - | | ``ap_1`` : [-100, 100] | ``ap_1`` : [-1, 1] | - | | | | - | | ``bp_1`` : [-100, 100] | ``bp_1`` : [-1, 1] | - +------------+------------------------+--------------------+ - - +------------+------------------------+--------------------+ - | **Range** | **Scale - Reference** | **Scale - 1** | - +============+========================+====================+ - | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | - | | | | - | | ``M`` : [0, 100] | ``M`` : [0, 1] | - | | | | - | | ``h`` : [0, 360] | ``h`` : [0, 1] | - +------------+------------------------+--------------------+ + - *SCD* in *CAM02-SCD* stands for *Small Colour Differences*. + + +------------+------------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | + | | | | + | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | + | | | | + | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | + +------------+------------------------+------------------+ + + +------------+------------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | + | | | | + | | ``M`` : [0, 100] | ``M`` : [0, 1] | + | | | | + | | ``h`` : [0, 360] | ``h`` : [0, 1] | + +------------+------------------------+------------------+ References ---------- @@ -450,46 +488,48 @@ def CAM02SCD_to_JMh_CIECAM02(Jpapbp): """ return UCS_Luo2006_to_JMh_CIECAM02( - Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006['CAM02-SCD']) + Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-SCD"] + ) -def JMh_CIECAM02_to_CAM02UCS(JMh): +def JMh_CIECAM02_to_CAM02UCS(JMh: ArrayLike) -> NDArray: """ - Converts from *CIECAM02* :math:`JMh` correlates array to + Convert from *CIECAM02* :math:`JMh` correlates array to *Luo et al. (2006)* *CAM02-UCS* colourspace :math:`J'a'b'` array. Parameters ---------- - JMh : array_like + JMh *CIECAM02* correlates array :math:`JMh`. Returns ------- - ndarray + :class:`numpy.ndarray` *Luo et al. (2006)* *CAM02-UCS* colourspace :math:`J'a'b'` array. Notes ----- - - +------------+------------------------+--------------------+ - | **Domain** | **Scale - Reference** | **Scale - 1** | - +============+========================+====================+ - | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | - | | | | - | | ``M`` : [0, 100] | ``M`` : [0, 1] | - | | | | - | | ``h`` : [0, 360] | ``h`` : [0, 1] | - +------------+------------------------+--------------------+ - - +------------+------------------------+--------------------+ - | **Range** | **Scale - Reference** | **Scale - 1** | - +============+========================+====================+ - | ``Jpapbp`` | ``Jp_1`` : [0, 100] | ``Jp_1`` : [0, 1] | - | | | | - | | ``ap_1`` : [-100, 100] | ``ap_1`` : [-1, 1] | - | | | | - | | ``bp_1`` : [-100, 100] | ``bp_1`` : [-1, 1] | - +------------+------------------------+--------------------+ + - *UCS* in *CAM02-UCS* stands for *Uniform Colour Colourspace*. + + +------------+------------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | + | | | | + | | ``M`` : [0, 100] | ``M`` : [0, 1] | + | | | | + | | ``h`` : [0, 360] | ``h`` : [0, 1] | + +------------+------------------------+------------------+ + + +------------+------------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | + | | | | + | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | + | | | | + | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | + +------------+------------------------+------------------+ References ---------- @@ -513,46 +553,48 @@ def JMh_CIECAM02_to_CAM02UCS(JMh): """ return JMh_CIECAM02_to_UCS_Luo2006( - JMh, coefficients=COEFFICIENTS_UCS_LUO2006['CAM02-UCS']) + JMh, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-UCS"] + ) -def CAM02UCS_to_JMh_CIECAM02(Jpapbp): +def CAM02UCS_to_JMh_CIECAM02(Jpapbp: ArrayLike) -> NDArray: """ - Converts from *Luo et al. (2006)* *CAM02-UCS* colourspace :math:`J'a'b'` + Convert from *Luo et al. (2006)* *CAM02-UCS* colourspace :math:`J'a'b'` array to *CIECAM02* :math:`JMh` correlates array. Parameters ---------- - Jpapbp : array_like + Jpapbp *Luo et al. (2006)* *CAM02-UCS* colourspace :math:`J'a'b'` array. Returns ------- - ndarray + :class:`numpy.ndarray` *CIECAM02* correlates array :math:`JMh`. Notes ----- - - +------------+------------------------+--------------------+ - | **Domain** | **Scale - Reference** | **Scale - 1** | - +============+========================+====================+ - | ``Jpapbp`` | ``Jp_1`` : [0, 100] | ``Jp_1`` : [0, 1] | - | | | | - | | ``ap_1`` : [-100, 100] | ``ap_1`` : [-1, 1] | - | | | | - | | ``bp_1`` : [-100, 100] | ``bp_1`` : [-1, 1] | - +------------+------------------------+--------------------+ - - +------------+------------------------+--------------------+ - | **Range** | **Scale - Reference** | **Scale - 1** | - +============+========================+====================+ - | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | - | | | | - | | ``M`` : [0, 100] | ``M`` : [0, 1] | - | | | | - | | ``h`` : [0, 360] | ``h`` : [0, 1] | - +------------+------------------------+--------------------+ + - *UCS* in *CAM02-UCS* stands for *Uniform Colour Colourspace*. + + +------------+------------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | + | | | | + | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | + | | | | + | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | + +------------+------------------------+------------------+ + + +------------+------------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | + | | | | + | | ``M`` : [0, 100] | ``M`` : [0, 1] | + | | | | + | | ``h`` : [0, 360] | ``h`` : [0, 1] | + +------------+------------------------+------------------+ References ---------- @@ -566,4 +608,566 @@ def CAM02UCS_to_JMh_CIECAM02(Jpapbp): """ return UCS_Luo2006_to_JMh_CIECAM02( - Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006['CAM02-UCS']) + Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-UCS"] + ) + + +def XYZ_to_UCS_Luo2006( + XYZ: ArrayLike, coefficients: ArrayLike, **kwargs: Any +) -> NDArray: + """ + Convert from *CIE XYZ* tristimulus values to one of the + *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces + :math:`J'a'b'` array. + + Parameters + ---------- + XYZ + *CIE XYZ* tristimulus values. + coefficients + Coefficients of one of the *Luo et al. (2006)* *CAM02-LCD*, + *CAM02-SCD*, or *CAM02-UCS* colourspaces. + + Other Parameters + ---------------- + kwargs + {:func:`colour.XYZ_to_CIECAM02`}, + See the documentation of the previously listed definition. The default + viewing conditions are that of *IEC 61966-2-1:1999*, i.e. *sRGB* 64 Lux + ambient illumination, 80 :math:`cd/m^2`, adapting field luminance about + 20% of a white object in the scene. + + Returns + ------- + :class:`numpy.ndarray` + *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* + colourspaces :math:`J'a'b'` array. + + Warnings + -------- + The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition must be + given in the same domain-range scale than the ``XYZ`` parameter. + + Notes + ----- + +------------+------------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``XYZ`` | [0, 1] | [0, 1] | + +------------+------------------------+------------------+ + + +------------+------------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | + | | | | + | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | + | | | | + | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | + +------------+------------------------+------------------+ + + Examples + -------- + >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + >>> XYZ_to_UCS_Luo2006(XYZ, COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) + ... # doctest: +ELLIPSIS + array([ 46.6138615..., 39.3576023..., 15.9673043...]) + """ + + from colour.appearance import CAM_KWARGS_CIECAM02_sRGB, XYZ_to_CIECAM02 + + domain_range_reference = get_domain_range_scale() == "reference" + + settings = CAM_KWARGS_CIECAM02_sRGB.copy() + settings.update(**kwargs) + XYZ_w = kwargs.get("XYZ_w") + if XYZ_w is not None and domain_range_reference: + settings["XYZ_w"] = XYZ_w * 100 + + if domain_range_reference: + XYZ = as_float_array(XYZ) * 100 + + specification = XYZ_to_CIECAM02(XYZ, **settings) + JMh = tstack([specification.J, specification.M, specification.h]) + + return JMh_CIECAM02_to_UCS_Luo2006(JMh, coefficients) + + +def UCS_Luo2006_to_XYZ( + Jpapbp: ArrayLike, coefficients: ArrayLike, **kwargs: Any +) -> NDArray: + """ + Convert from one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or + *CAM02-UCS* colourspaces :math:`J'a'b'` array to *CIE XYZ* tristimulus + values. + + Parameters + ---------- + Jpapbp + *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* + colourspaces :math:`J'a'b'` array. + coefficients + Coefficients of one of the *Luo et al. (2006)* *CAM02-LCD*, + *CAM02-SCD*, or *CAM02-UCS* colourspaces. + + Other Parameters + ---------------- + kwargs + {:func:`colour.CIECAM02_to_XYZ`}, + See the documentation of the previously listed definition. The default + viewing conditions are that of *IEC 61966-2-1:1999*, i.e. *sRGB* 64 Lux + ambient illumination, 80 :math:`cd/m^2`, adapting field luminance about + 20% of a white object in the scene. + + Returns + ------- + :class:`numpy.ndarray` + *CIE XYZ* tristimulus values. + + Warnings + -------- + The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition must be + given in the same domain-range scale than the ``XYZ`` parameter. + + Notes + ----- + +------------+------------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | + | | | | + | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | + | | | | + | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | + +------------+------------------------+------------------+ + + +------------+------------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``XYZ`` | [0, 1] | [0, 1] | + +------------+------------------------+------------------+ + + Examples + -------- + >>> Jpapbp = np.array([46.61386154, 39.35760236, 15.96730435]) + >>> UCS_Luo2006_to_XYZ( + ... Jpapbp, COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) + ... # doctest: +ELLIPSIS + array([ 0.2065400..., 0.1219722..., 0.0513695...]) + """ + + from colour.appearance import ( + CAM_KWARGS_CIECAM02_sRGB, + CAM_Specification_CIECAM02, + CIECAM02_to_XYZ, + ) + + domain_range_reference = get_domain_range_scale() == "reference" + + settings = CAM_KWARGS_CIECAM02_sRGB.copy() + settings.update(**kwargs) + XYZ_w = kwargs.get("XYZ_w") + + if XYZ_w is not None and domain_range_reference: + settings["XYZ_w"] = XYZ_w * 100 + + J, M, h = tsplit(UCS_Luo2006_to_JMh_CIECAM02(Jpapbp, coefficients)) + + specification = CAM_Specification_CIECAM02(J=J, M=M, h=h) + + XYZ = CIECAM02_to_XYZ(specification, **settings) + + if domain_range_reference: + XYZ /= 100 + + return XYZ + + +def XYZ_to_CAM02LCD(XYZ: ArrayLike, **kwargs: Any) -> NDArray: + """ + Convert from *CIE XYZ* tristimulus values to *Luo et al. (2006)* + *CAM02-LCD* colourspace :math:`J'a'b'` array. + + Parameters + ---------- + XYZ + *CIE XYZ* tristimulus values. + + Other Parameters + ---------------- + kwargs + {:func:`colour.XYZ_to_CIECAM02`}, + See the documentation of the previously listed definition. The default + viewing conditions are that of *IEC 61966-2-1:1999*, i.e. *sRGB* 64 Lux + ambient illumination, 80 :math:`cd/m^2`, adapting field luminance about + 20% of a white object in the scene. + + Returns + ------- + :class:`numpy.ndarray` + *Luo et al. (2006)* *CAM02-LCD* colourspace :math:`J'a'b'` array. + + Warnings + -------- + The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition must be + given in the same domain-range scale than the ``XYZ`` parameter. + + Notes + ----- + - *LCD* in *CAM02-LCD* stands for *Large Colour Differences*. + + +------------+------------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``XYZ`` | [0, 1] | [0, 1] | + +------------+------------------------+------------------+ + + +------------+------------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | + | | | | + | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | + | | | | + | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | + +------------+------------------------+------------------+ + + References + ---------- + :cite:`Luo2006b` + + Examples + -------- + >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + >>> XYZ_to_CAM02LCD(XYZ) # doctest: +ELLIPSIS + array([ 46.6138615..., 39.3576023..., 15.9673043...]) + """ + + return XYZ_to_UCS_Luo2006( + XYZ, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-LCD"], **kwargs + ) + + +def CAM02LCD_to_XYZ(Jpapbp: ArrayLike, **kwargs: Any) -> NDArray: + """ + Convert from *Luo et al. (2006)* *CAM02-LCD* colourspace :math:`J'a'b'` + array to *CIE XYZ* tristimulus values. + + Parameters + ---------- + Jpapbp + *Luo et al. (2006)* *CAM02-LCD* colourspace :math:`J'a'b'` array. + + Other Parameters + ---------------- + kwargs + {:func:`colour.CIECAM02_to_XYZ`}, + See the documentation of the previously listed definition. The default + viewing conditions are that of *IEC 61966-2-1:1999*, i.e. *sRGB* 64 Lux + ambient illumination, 80 :math:`cd/m^2`, adapting field luminance about + 20% of a white object in the scene. + + Returns + ------- + :class:`numpy.ndarray` + *CIE XYZ* tristimulus values. + + Warnings + -------- + The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition must be + given in the same domain-range scale than the ``XYZ`` parameter. + + Notes + ----- + - *LCD* in *CAM02-LCD* stands for *Large Colour Differences*. + + +------------+------------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | + | | | | + | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | + | | | | + | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | + +------------+------------------------+------------------+ + + +------------+------------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``XYZ`` | [0, 1] | [0, 1] | + +------------+------------------------+------------------+ + + References + ---------- + :cite:`Luo2006b` + + Examples + -------- + >>> Jpapbp = np.array([46.61386154, 39.35760236, 15.96730435]) + >>> CAM02LCD_to_XYZ(Jpapbp) # doctest: +ELLIPSIS + array([ 0.2065400..., 0.1219722..., 0.0513695...]) + """ + + return UCS_Luo2006_to_XYZ( + Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-LCD"], **kwargs + ) + + +def XYZ_to_CAM02SCD(XYZ: ArrayLike, **kwargs: Any) -> NDArray: + """ + Convert from *CIE XYZ* tristimulus values to *Luo et al. (2006)* + *CAM02-SCD* colourspace :math:`J'a'b'` array. + + Parameters + ---------- + XYZ + *CIE XYZ* tristimulus values. + + Other Parameters + ---------------- + kwargs + {:func:`colour.XYZ_to_CIECAM02`}, + See the documentation of the previously listed definition. The default + viewing conditions are that of *IEC 61966-2-1:1999*, i.e. *sRGB* 64 Lux + ambient illumination, 80 :math:`cd/m^2`, adapting field luminance about + 20% of a white object in the scene. + + Returns + ------- + :class:`numpy.ndarray` + *Luo et al. (2006)* *CAM02-SCD* colourspace :math:`J'a'b'` array. + + Warnings + -------- + The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition must be + given in the same domain-range scale than the ``XYZ`` parameter. + + Notes + ----- + - *SCD* in *CAM02-SCD* stands for *Small Colour Differences*. + + +------------+------------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``XYZ`` | [0, 1] | [0, 1] | + +------------+------------------------+------------------+ + + +------------+------------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | + | | | | + | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | + | | | | + | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | + +------------+------------------------+------------------+ + + References + ---------- + :cite:`Luo2006b` + + Examples + -------- + >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + >>> XYZ_to_CAM02SCD(XYZ) # doctest: +ELLIPSIS + array([ 46.6138615..., 25.6287988..., 10.3975548...]) + """ + + return XYZ_to_UCS_Luo2006( + XYZ, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-SCD"], **kwargs + ) + + +def CAM02SCD_to_XYZ(Jpapbp: ArrayLike, **kwargs: Any) -> NDArray: + """ + Convert from *Luo et al. (2006)* *CAM02-SCD* colourspace :math:`J'a'b'` + array to *CIE XYZ* tristimulus values. + + Parameters + ---------- + Jpapbp + *Luo et al. (2006)* *CAM02-SCD* colourspace :math:`J'a'b'` array. + + Other Parameters + ---------------- + kwargs + {:func:`colour.CIECAM02_to_XYZ`}, + See the documentation of the previously listed definition. The default + viewing conditions are that of *IEC 61966-2-1:1999*, i.e. *sRGB* 64 Lux + ambient illumination, 80 :math:`cd/m^2`, adapting field luminance about + 20% of a white object in the scene. + + Returns + ------- + :class:`numpy.ndarray` + *CIE XYZ* tristimulus values. + + Warnings + -------- + The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition must be + given in the same domain-range scale than the ``XYZ`` parameter. + + Notes + ----- + - *SCD* in *CAM02-SCD* stands for *Small Colour Differences*. + + +------------+------------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | + | | | | + | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | + | | | | + | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | + +------------+------------------------+------------------+ + + +------------+------------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``XYZ`` | [0, 1] | [0, 1] | + +------------+------------------------+------------------+ + + References + ---------- + :cite:`Luo2006b` + + Examples + -------- + >>> Jpapbp = np.array([46.61386154, 25.62879882, 10.39755489]) + >>> CAM02SCD_to_XYZ(Jpapbp) # doctest: +ELLIPSIS + array([ 0.2065400..., 0.1219722..., 0.0513695...]) + """ + + return UCS_Luo2006_to_XYZ( + Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-SCD"], **kwargs + ) + + +def XYZ_to_CAM02UCS(XYZ: ArrayLike, **kwargs: Any) -> NDArray: + """ + Convert from *CIE XYZ* tristimulus values to *Luo et al. (2006)* + *CAM02-UCS* colourspace :math:`J'a'b'` array. + + Parameters + ---------- + XYZ + *CIE XYZ* tristimulus values. + + Other Parameters + ---------------- + kwargs + {:func:`colour.XYZ_to_CIECAM02`}, + See the documentation of the previously listed definition. The default + viewing conditions are that of *IEC 61966-2-1:1999*, i.e. *sRGB* 64 Lux + ambient illumination, 80 :math:`cd/m^2`, adapting field luminance about + 20% of a white object in the scene. + + Returns + ------- + :class:`numpy.ndarray` + *Luo et al. (2006)* *CAM02-UCS* colourspace :math:`J'a'b'` array. + + Warnings + -------- + The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition must be + given in the same domain-range scale than the ``XYZ`` parameter. + + Notes + ----- + - *UCS* in *CAM02-UCS* stands for *Uniform Colour Space*. + + +------------+------------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``XYZ`` | [0, 1] | [0, 1] | + +------------+------------------------+------------------+ + + +------------+------------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | + | | | | + | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | + | | | | + | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | + +------------+------------------------+------------------+ + + References + ---------- + :cite:`Luo2006b` + + Examples + -------- + >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + >>> XYZ_to_CAM02UCS(XYZ) # doctest: +ELLIPSIS + array([ 46.6138615..., 29.8831001..., 12.1235168...]) + """ + + return XYZ_to_UCS_Luo2006( + XYZ, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-UCS"], **kwargs + ) + + +def CAM02UCS_to_XYZ(Jpapbp: ArrayLike, **kwargs: Any) -> NDArray: + """ + Convert from *Luo et al. (2006)* *CAM02-UCS* colourspace :math:`J'a'b'` + array to *CIE XYZ* tristimulus values. + + Parameters + ---------- + Jpapbp + *Luo et al. (2006)* *CAM02-UCS* colourspace :math:`J'a'b'` array. + + Other Parameters + ---------------- + kwargs + {:func:`colour.CIECAM02_to_XYZ`}, + See the documentation of the previously listed definition. The default + viewing conditions are that of *IEC 61966-2-1:1999*, i.e. *sRGB* 64 Lux + ambient illumination, 80 :math:`cd/m^2`, adapting field luminance about + 20% of a white object in the scene. + + Returns + ------- + :class:`numpy.ndarray` + *CIE XYZ* tristimulus values. + + Warnings + -------- + The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition must be + given in the same domain-range scale than the ``XYZ`` parameter. + + Notes + ----- + - *UCS* in *CAM02-UCS* stands for *Uniform Colour Space*. + + +------------+------------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | + | | | | + | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | + | | | | + | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | + +------------+------------------------+------------------+ + + +------------+------------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``XYZ`` | [0, 1] | [0, 1] | + +------------+------------------------+------------------+ + + References + ---------- + :cite:`Luo2006b` + + Examples + -------- + >>> Jpapbp = np.array([46.61386154, 29.88310013, 12.12351683]) + >>> CAM02UCS_to_XYZ(Jpapbp) # doctest: +ELLIPSIS + array([ 0.2065400..., 0.1219722..., 0.0513695...]) + """ + + return UCS_Luo2006_to_XYZ( + Jpapbp, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-UCS"], **kwargs + ) diff --git a/colour/models/cam16_ucs.py b/colour/models/cam16_ucs.py index 8321913273..5680950935 100644 --- a/colour/models/cam16_ucs.py +++ b/colour/models/cam16_ucs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ CAM16-LCD, CAM16-SCD, and CAM16-UCS Colourspaces - Li et al. (2017) =================================================================== @@ -12,6 +11,12 @@ - :func:`colour.CAM16SCD_to_JMh_CAM16` - :func:`colour.JMh_CAM16_to_CAM16UCS` - :func:`colour.CAM16UCS_to_JMh_CAM16` +- :func:`colour.XYZ_to_CAM16LCD` +- :func:`colour.CAM16LCD_to_XYZ` +- :func:`colour.XYZ_to_CAM16SCD` +- :func:`colour.CAM16SCD_to_XYZ` +- :func:`colour.XYZ_to_CAM16UCS` +- :func:`colour.CAM16UCS_to_XYZ` References ---------- @@ -21,102 +26,378 @@ 42(6), 703-718. doi:10.1002/col.22131 """ -from __future__ import division, unicode_literals +from __future__ import annotations import re from functools import partial +from colour.hints import Callable, Any, ArrayLike, NDArray from colour.models.cam02_ucs import ( - COEFFICIENTS_UCS_LUO2006, JMh_CIECAM02_to_UCS_Luo2006, - UCS_Luo2006_to_JMh_CIECAM02, JMh_CIECAM02_to_CAM02LCD, - CAM02LCD_to_JMh_CIECAM02, JMh_CIECAM02_to_CAM02SCD, - CAM02SCD_to_JMh_CIECAM02, JMh_CIECAM02_to_CAM02UCS, - CAM02UCS_to_JMh_CIECAM02) -from colour.utilities import copy_definition - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + COEFFICIENTS_UCS_LUO2006, + JMh_CIECAM02_to_UCS_Luo2006, + UCS_Luo2006_to_JMh_CIECAM02, + JMh_CIECAM02_to_CAM02LCD, + CAM02LCD_to_JMh_CIECAM02, + JMh_CIECAM02_to_CAM02SCD, + CAM02SCD_to_JMh_CIECAM02, + JMh_CIECAM02_to_CAM02UCS, + CAM02UCS_to_JMh_CIECAM02, + XYZ_to_CAM02LCD, + CAM02LCD_to_XYZ, + XYZ_to_CAM02SCD, + CAM02SCD_to_XYZ, + XYZ_to_CAM02UCS, + CAM02UCS_to_XYZ, +) +from colour.utilities import ( + as_float_array, + copy_definition, + get_domain_range_scale, + optional, + tsplit, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'JMh_CAM16_to_UCS_Li2017', 'UCS_Li2017_to_JMh_CAM16', - 'JMh_CAM16_to_CAM16LCD', 'CAM16LCD_to_JMh_CAM16', 'JMh_CAM16_to_CAM16SCD', - 'CAM16SCD_to_JMh_CAM16', 'JMh_CAM16_to_CAM16UCS', 'CAM16UCS_to_JMh_CAM16' + "JMh_CAM16_to_UCS_Li2017", + "UCS_Li2017_to_JMh_CAM16", + "JMh_CAM16_to_CAM16LCD", + "CAM16LCD_to_JMh_CAM16", + "JMh_CAM16_to_CAM16SCD", + "CAM16SCD_to_JMh_CAM16", + "JMh_CAM16_to_CAM16UCS", + "CAM16UCS_to_JMh_CAM16", + "XYZ_to_UCS_Li2017", + "UCS_Li2017_to_XYZ", + "XYZ_to_CAM16LCD", + "CAM16LCD_to_XYZ", + "XYZ_to_CAM16SCD", + "CAM16SCD_to_XYZ", + "XYZ_to_CAM16UCS", + "CAM16UCS_to_XYZ", ] -def _UCS_Luo2006_callable_to_UCS_Li2017_docstring(callable_): +def _UCS_Luo2006_callable_to_UCS_Li2017_docstring(callable_: Callable) -> str: """ - Converts given *Luo et al. (2006)* callable docstring to + Convert given *Luo et al. (2006)* callable docstring to *Li et al. (2017)* docstring. Parameters ---------- - callable_ : callable + callable_ Callable to use the docstring from. Returns ------- - unicode + :class:`str` Docstring. """ docstring = callable_.__doc__ - docstring = docstring.replace('Luo et al. (2006)', 'Li et al. (2017)') - docstring = docstring.replace('CIECAM02', 'CAM16') - docstring = docstring.replace('CAM02', 'CAM16') - docstring = docstring.replace('Luo2006b', 'Li2017') + # NOTE: Required for optimised python launch. + docstring = optional(docstring, "") + + docstring = docstring.replace("Luo et al. (2006)", "Li et al. (2017)") + docstring = docstring.replace("CIECAM02", "CAM16") + docstring = docstring.replace("CAM02", "CAM16") + docstring = docstring.replace("Luo2006b", "Li2017") - docstring = re.match('(.*)Examples', docstring, re.DOTALL).group(1) + match = re.match("(.*)Examples", docstring, re.DOTALL) + if match is not None: + docstring = match.group(1) return docstring -JMh_CAM16_to_UCS_Li2017 = copy_definition(JMh_CIECAM02_to_UCS_Luo2006, - 'JMh_CAM16_to_UCS_Li2017') +JMh_CAM16_to_UCS_Li2017 = copy_definition( + JMh_CIECAM02_to_UCS_Luo2006, "JMh_CAM16_to_UCS_Li2017" +) JMh_CAM16_to_UCS_Li2017.__doc__ = ( - _UCS_Luo2006_callable_to_UCS_Li2017_docstring(JMh_CIECAM02_to_UCS_Luo2006)) + _UCS_Luo2006_callable_to_UCS_Li2017_docstring(JMh_CIECAM02_to_UCS_Luo2006) +) -UCS_Li2017_to_JMh_CAM16 = copy_definition(UCS_Luo2006_to_JMh_CIECAM02, - 'UCS_Li2017_to_JMh_CAM16') +UCS_Li2017_to_JMh_CAM16 = copy_definition( + UCS_Luo2006_to_JMh_CIECAM02, "UCS_Li2017_to_JMh_CAM16" +) UCS_Li2017_to_JMh_CAM16.__doc__ = ( - _UCS_Luo2006_callable_to_UCS_Li2017_docstring(UCS_Luo2006_to_JMh_CIECAM02)) + _UCS_Luo2006_callable_to_UCS_Li2017_docstring(UCS_Luo2006_to_JMh_CIECAM02) +) JMh_CAM16_to_CAM16LCD = partial( - JMh_CAM16_to_UCS_Li2017, - coefficients=COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) -JMh_CAM16_to_CAM16LCD.__doc__ = ( - _UCS_Luo2006_callable_to_UCS_Li2017_docstring(JMh_CIECAM02_to_CAM02LCD)) + JMh_CAM16_to_UCS_Li2017, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] +) +JMh_CAM16_to_CAM16LCD.__doc__ = _UCS_Luo2006_callable_to_UCS_Li2017_docstring( + JMh_CIECAM02_to_CAM02LCD +) CAM16LCD_to_JMh_CAM16 = partial( - UCS_Li2017_to_JMh_CAM16, - coefficients=COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) -CAM16LCD_to_JMh_CAM16.__doc__ = ( - _UCS_Luo2006_callable_to_UCS_Li2017_docstring(CAM02LCD_to_JMh_CIECAM02)) + UCS_Li2017_to_JMh_CAM16, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] +) +CAM16LCD_to_JMh_CAM16.__doc__ = _UCS_Luo2006_callable_to_UCS_Li2017_docstring( + CAM02LCD_to_JMh_CIECAM02 +) JMh_CAM16_to_CAM16SCD = partial( - JMh_CAM16_to_UCS_Li2017, - coefficients=COEFFICIENTS_UCS_LUO2006['CAM02-SCD']) -JMh_CAM16_to_CAM16SCD.__doc__ = ( - _UCS_Luo2006_callable_to_UCS_Li2017_docstring(JMh_CIECAM02_to_CAM02SCD)) + JMh_CAM16_to_UCS_Li2017, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-SCD"] +) +JMh_CAM16_to_CAM16SCD.__doc__ = _UCS_Luo2006_callable_to_UCS_Li2017_docstring( + JMh_CIECAM02_to_CAM02SCD +) CAM16SCD_to_JMh_CAM16 = partial( - UCS_Li2017_to_JMh_CAM16, - coefficients=COEFFICIENTS_UCS_LUO2006['CAM02-SCD']) -CAM16SCD_to_JMh_CAM16.__doc__ = ( - _UCS_Luo2006_callable_to_UCS_Li2017_docstring(CAM02SCD_to_JMh_CIECAM02)) + UCS_Li2017_to_JMh_CAM16, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-SCD"] +) +CAM16SCD_to_JMh_CAM16.__doc__ = _UCS_Luo2006_callable_to_UCS_Li2017_docstring( + CAM02SCD_to_JMh_CIECAM02 +) JMh_CAM16_to_CAM16UCS = partial( - JMh_CAM16_to_UCS_Li2017, - coefficients=COEFFICIENTS_UCS_LUO2006['CAM02-UCS']) -JMh_CAM16_to_CAM16UCS.__doc__ = ( - _UCS_Luo2006_callable_to_UCS_Li2017_docstring(JMh_CIECAM02_to_CAM02UCS)) + JMh_CAM16_to_UCS_Li2017, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-UCS"] +) +JMh_CAM16_to_CAM16UCS.__doc__ = _UCS_Luo2006_callable_to_UCS_Li2017_docstring( + JMh_CIECAM02_to_CAM02UCS +) CAM16UCS_to_JMh_CAM16 = partial( - UCS_Li2017_to_JMh_CAM16, - coefficients=COEFFICIENTS_UCS_LUO2006['CAM02-UCS']) -CAM16UCS_to_JMh_CAM16.__doc__ = ( - _UCS_Luo2006_callable_to_UCS_Li2017_docstring(CAM02UCS_to_JMh_CIECAM02)) + UCS_Li2017_to_JMh_CAM16, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-UCS"] +) +CAM16UCS_to_JMh_CAM16.__doc__ = _UCS_Luo2006_callable_to_UCS_Li2017_docstring( + CAM02UCS_to_JMh_CIECAM02 +) + + +def XYZ_to_UCS_Li2017( + XYZ: ArrayLike, coefficients: ArrayLike, **kwargs: Any +) -> NDArray: + """ + Convert from *CIE XYZ* tristimulus values to one of the *Li et al. (2017)* + *CAM16-LCD*, *CAM16-SCD*, or *CAM16-UCS* colourspaces :math:`J'a'b'` array. + + Parameters + ---------- + XYZ + *CIE XYZ* tristimulus values. + coefficients + Coefficients of one of the *Li et al. (2017)* *CAM16-LCD*, *CAM16-SCD*, + or *CAM16-UCS* colourspaces. + + Other Parameters + ---------------- + kwargs + {:func:`colour.XYZ_to_CAM16`}, + See the documentation of the previously listed definition. The default + viewing conditions are that of *IEC 61966-2-1:1999*, i.e. *sRGB* 64 Lux + ambient illumination, 80 :math:`cd/m^2`, adapting field luminance about + 20% of a white object in the scene. + + Returns + ------- + :class:`numpy.ndarray` + *Li et al. (2017)* *CAM16-LCD*, *CAM16-SCD*, or *CAM16-UCS* + colourspaces :math:`J'a'b'` array. + + Warnings + -------- + The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition must be + given in the same domain-range scale than the ``XYZ`` parameter. + + Notes + ----- + +------------+------------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``XYZ`` | [0, 1] | [0, 1] | + +------------+------------------------+------------------+ + + +------------+------------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | + | | | | + | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | + | | | | + | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | + +------------+------------------------+------------------+ + + Examples + -------- + >>> import numpy as np + >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + >>> XYZ_to_UCS_Li2017(XYZ, COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) + ... # doctest: +ELLIPSIS + array([ 46.0658603..., 41.0758649..., 14.5102582...]) + + >>> from colour.appearance import CAM_KWARGS_CIECAM02_sRGB + >>> XYZ_w = CAM_KWARGS_CIECAM02_sRGB['XYZ_w'] + >>> XYZ_to_UCS_Li2017( + ... XYZ, COEFFICIENTS_UCS_LUO2006['CAM02-LCD'], XYZ_w=XYZ_w / 100) + ... # doctest: +ELLIPSIS + array([ 46.0658603..., 41.0758649..., 14.5102582...]) + """ + + from colour.appearance import CAM_KWARGS_CIECAM02_sRGB, XYZ_to_CAM16 + + domain_range_reference = get_domain_range_scale() == "reference" + + settings = CAM_KWARGS_CIECAM02_sRGB.copy() + settings.update(**kwargs) + XYZ_w = kwargs.get("XYZ_w") + if XYZ_w is not None and domain_range_reference: + settings["XYZ_w"] = XYZ_w * 100 + + if domain_range_reference: + XYZ = as_float_array(XYZ) * 100 + + specification = XYZ_to_CAM16(XYZ, **settings) + JMh = tstack([specification.J, specification.M, specification.h]) + + return JMh_CAM16_to_UCS_Li2017(JMh, coefficients) + + +def UCS_Li2017_to_XYZ( + Jpapbp: ArrayLike, coefficients: ArrayLike, **kwargs: Any +) -> NDArray: + """ + Convert from one of the *Li et al. (2017)* *CAM16-LCD*, *CAM16-SCD*, or + *CAM16-UCS* colourspaces :math:`J'a'b'` array to *CIE XYZ* tristimulus + values. + + Parameters + ---------- + Jpapbp + *Li et al. (2017)* *CAM16-LCD*, *CAM16-SCD*, or *CAM16-UCS* + colourspaces :math:`J'a'b'` array. + coefficients + Coefficients of one of the *Li et al. (2017)* *CAM16-LCD*, *CAM16-SCD*, + or *CAM16-UCS* colourspaces. + + Other Parameters + ---------------- + kwargs + {:func:`colour.CAM16_to_XYZ`}, + See the documentation of the previously listed definition. The default + viewing conditions are that of *IEC 61966-2-1:1999*, i.e. *sRGB* 64 Lux + ambient illumination, 80 :math:`cd/m^2`, adapting field luminance about + 20% of a white object in the scene. + + Returns + ------- + :class:`numpy.ndarray` + *CIE XYZ* tristimulus values. + + Warnings + -------- + The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition must be + given in the same domain-range scale than the ``XYZ`` parameter. + + Notes + ----- + +------------+------------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | + | | | | + | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | + | | | | + | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | + +------------+------------------------+------------------+ + + +------------+------------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+==================+ + | ``XYZ`` | [0, 1] | [0, 1] | + +------------+------------------------+------------------+ + + Examples + -------- + >>> import numpy as np + >>> Jpapbp = np.array([46.06586037, 41.07586491, 14.51025828]) + >>> UCS_Li2017_to_XYZ( + ... Jpapbp, COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) + ... # doctest: +ELLIPSIS + array([ 0.2065400..., 0.1219722..., 0.0513695...]) + + >>> from colour.appearance import CAM_KWARGS_CIECAM02_sRGB + >>> XYZ_w = CAM_KWARGS_CIECAM02_sRGB['XYZ_w'] + >>> UCS_Li2017_to_XYZ( + ... Jpapbp, COEFFICIENTS_UCS_LUO2006['CAM02-LCD'], XYZ_w=XYZ_w / 100) + ... # doctest: +ELLIPSIS + array([ 0.2065400..., 0.1219722..., 0.0513695...]) + """ + + from colour.appearance import ( + CAM_KWARGS_CIECAM02_sRGB, + CAM_Specification_CAM16, + CAM16_to_XYZ, + ) + + domain_range_reference = get_domain_range_scale() == "reference" + + settings = CAM_KWARGS_CIECAM02_sRGB.copy() + settings.update(**kwargs) + XYZ_w = kwargs.get("XYZ_w") + + if XYZ_w is not None and domain_range_reference: + settings["XYZ_w"] = XYZ_w * 100 + + J, M, h = tsplit(UCS_Li2017_to_JMh_CAM16(Jpapbp, coefficients)) + + specification = CAM_Specification_CAM16(J=J, M=M, h=h) + + XYZ = CAM16_to_XYZ(specification, **settings) + + if domain_range_reference: + XYZ /= 100 + + return XYZ + + +XYZ_to_CAM16LCD = partial( + XYZ_to_UCS_Li2017, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] +) +XYZ_to_CAM16LCD.__doc__ = _UCS_Luo2006_callable_to_UCS_Li2017_docstring( + XYZ_to_CAM02LCD +) + +CAM16LCD_to_XYZ = partial( + UCS_Li2017_to_XYZ, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] +) +CAM16LCD_to_XYZ.__doc__ = _UCS_Luo2006_callable_to_UCS_Li2017_docstring( + CAM02LCD_to_XYZ +) + +XYZ_to_CAM16SCD = partial( + XYZ_to_UCS_Li2017, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-SCD"] +) +XYZ_to_CAM16SCD.__doc__ = _UCS_Luo2006_callable_to_UCS_Li2017_docstring( + XYZ_to_CAM02SCD +) + +CAM16SCD_to_XYZ = partial( + UCS_Li2017_to_XYZ, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-SCD"] +) +CAM16SCD_to_XYZ.__doc__ = _UCS_Luo2006_callable_to_UCS_Li2017_docstring( + CAM02SCD_to_XYZ +) + +XYZ_to_CAM16UCS = partial( + XYZ_to_UCS_Li2017, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-UCS"] +) +XYZ_to_CAM16UCS.__doc__ = _UCS_Luo2006_callable_to_UCS_Li2017_docstring( + XYZ_to_CAM02UCS +) + +CAM16UCS_to_XYZ = partial( + UCS_Li2017_to_XYZ, coefficients=COEFFICIENTS_UCS_LUO2006["CAM02-UCS"] +) +CAM16UCS_to_XYZ.__doc__ = _UCS_Luo2006_callable_to_UCS_Li2017_docstring( + CAM02UCS_to_XYZ +) diff --git a/colour/models/cie_lab.py b/colour/models/cie_lab.py index f1e4fcf427..13ceaac1e1 100644 --- a/colour/models/cie_lab.py +++ b/colour/models/cie_lab.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ CIE L*a*b* Colourspace ====================== @@ -17,48 +16,64 @@ ISBN:978-3-901906-33-6 """ -from __future__ import division, unicode_literals +from __future__ import annotations -from colour.colorimetry import (CCS_ILLUMINANTS, - intermediate_lightness_function_CIE1976, - intermediate_luminance_function_CIE1976) +from colour.colorimetry import ( + CCS_ILLUMINANTS, + intermediate_lightness_function_CIE1976, + intermediate_luminance_function_CIE1976, +) +from colour.hints import ArrayLike, NDArray from colour.models import xy_to_xyY, xyY_to_XYZ, Jab_to_JCh, JCh_to_Jab -from colour.utilities import (from_range_1, from_range_100, to_domain_1, - to_domain_100, tsplit, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['XYZ_to_Lab', 'Lab_to_XYZ', 'Lab_to_LCHab', 'LCHab_to_Lab'] - - -def XYZ_to_Lab(XYZ, - illuminant=CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer']['D65']): +from colour.utilities import ( + from_range_1, + from_range_100, + to_domain_1, + to_domain_100, + tsplit, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "XYZ_to_Lab", + "Lab_to_XYZ", + "Lab_to_LCHab", + "LCHab_to_Lab", +] + + +def XYZ_to_Lab( + XYZ: ArrayLike, + illuminant: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], +) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to *CIE L\\*a\\*b\\** + Convert from *CIE XYZ* tristimulus values to *CIE L\\*a\\*b\\** colourspace. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. - illuminant : array_like, optional + illuminant Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE L\\*a\\*b\\** colourspace array. Notes ----- - +----------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ @@ -90,7 +105,6 @@ def XYZ_to_Lab(XYZ, """ X, Y, Z = tsplit(to_domain_1(XYZ)) - X_n, Y_n, Z_n = tsplit(xyY_to_XYZ(xy_to_xyY(illuminant))) f_X_X_n = intermediate_lightness_function_CIE1976(X, X_n) @@ -106,29 +120,31 @@ def XYZ_to_Lab(XYZ, return from_range_100(Lab) -def Lab_to_XYZ(Lab, - illuminant=CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer']['D65']): +def Lab_to_XYZ( + Lab: ArrayLike, + illuminant: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], +) -> NDArray: """ - Converts from *CIE L\\*a\\*b\\** colourspace to *CIE XYZ* tristimulus + Convert from *CIE L\\*a\\*b\\** colourspace to *CIE XYZ* tristimulus values. Parameters ---------- - Lab : array_like + Lab *CIE L\\*a\\*b\\** colourspace array. - illuminant : array_like, optional + illuminant Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Notes ----- - +----------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ @@ -176,24 +192,23 @@ def Lab_to_XYZ(Lab, return from_range_1(XYZ) -def Lab_to_LCHab(Lab): +def Lab_to_LCHab(Lab: ArrayLike) -> NDArray: """ - Converts from *CIE L\\*a\\*b\\** colourspace to *CIE L\\*C\\*Hab* + Convert from *CIE L\\*a\\*b\\** colourspace to *CIE L\\*C\\*Hab* colourspace. Parameters ---------- - Lab : array_like + Lab *CIE L\\*a\\*b\\** colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE L\\*C\\*Hab* colourspace array. Notes ----- - +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ @@ -229,24 +244,23 @@ def Lab_to_LCHab(Lab): return Jab_to_JCh(Lab) -def LCHab_to_Lab(LCHab): +def LCHab_to_Lab(LCHab: ArrayLike) -> NDArray: """ - Converts from *CIE L\\*C\\*Hab* colourspace to *CIE L\\*a\\*b\\** + Convert from *CIE L\\*C\\*Hab* colourspace to *CIE L\\*a\\*b\\** colourspace. Parameters ---------- - LCHab : array_like + LCHab *CIE L\\*C\\*Hab* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE L\\*a\\*b\\** colourspace array. Notes ----- - +-------------+-----------------------+------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +=============+=======================+==================+ diff --git a/colour/models/cie_luv.py b/colour/models/cie_luv.py index 245242b2e2..3893377b3b 100644 --- a/colour/models/cie_luv.py +++ b/colour/models/cie_luv.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ CIE L*u*v* Colourspace ====================== @@ -29,50 +28,71 @@ http://en.wikipedia.org/wiki/CIELUV#The_reverse_transformation """ -from __future__ import division, unicode_literals +from __future__ import annotations -from colour.colorimetry import (CCS_ILLUMINANTS, lightness_CIE1976, - luminance_CIE1976) +from colour.colorimetry import ( + CCS_ILLUMINANTS, + lightness_CIE1976, + luminance_CIE1976, +) +from colour.hints import ArrayLike, Floating, NDArray from colour.models import xy_to_xyY, xyY_to_XYZ, Jab_to_JCh, JCh_to_Jab -from colour.utilities import (domain_range_scale, from_range_1, from_range_100, - full, to_domain_1, to_domain_100, tsplit, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.utilities import ( + domain_range_scale, + as_float_scalar, + from_range_1, + from_range_100, + full, + to_domain_1, + to_domain_100, + tsplit, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'XYZ_to_Luv', 'Luv_to_XYZ', 'Luv_to_uv', 'uv_to_Luv', 'Luv_uv_to_xy', - 'xy_to_Luv_uv', 'Luv_to_LCHuv', 'LCHuv_to_Luv' + "XYZ_to_Luv", + "Luv_to_XYZ", + "Luv_to_uv", + "uv_to_Luv", + "Luv_uv_to_xy", + "xy_to_Luv_uv", + "Luv_to_LCHuv", + "LCHuv_to_Luv", ] -def XYZ_to_Luv(XYZ, - illuminant=CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer']['D65']): +def XYZ_to_Luv( + XYZ: ArrayLike, + illuminant: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], +) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to *CIE L\\*u\\*v\\** + Convert from *CIE XYZ* tristimulus values to *CIE L\\*u\\*v\\** colourspace. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. - illuminant : array_like, optional + illuminant Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE L\\*u\\*v\\** colourspace array. Notes ----- - +----------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ @@ -107,43 +127,45 @@ def XYZ_to_Luv(XYZ, X_r, Y_r, Z_r = tsplit(xyY_to_XYZ(xy_to_xyY(illuminant))) - with domain_range_scale('100'): + with domain_range_scale("100"): L = lightness_CIE1976(Y, Y_r) X_Y_Z = X + 15 * Y + 3 * Z X_r_Y_r_Z_r = X_r + 15 * Y_r + 3 * Z_r - u = (13 * L * ((4 * X / X_Y_Z) - (4 * X_r / X_r_Y_r_Z_r))) - v = (13 * L * ((9 * Y / X_Y_Z) - (9 * Y_r / X_r_Y_r_Z_r))) + u = 13 * L * ((4 * X / X_Y_Z) - (4 * X_r / X_r_Y_r_Z_r)) + v = 13 * L * ((9 * Y / X_Y_Z) - (9 * Y_r / X_r_Y_r_Z_r)) Luv = tstack([L, u, v]) return from_range_100(Luv) -def Luv_to_XYZ(Luv, - illuminant=CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer']['D65']): +def Luv_to_XYZ( + Luv: ArrayLike, + illuminant: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], +) -> NDArray: """ - Converts from *CIE L\\*u\\*v\\** colourspace to *CIE XYZ* tristimulus + Convert from *CIE L\\*u\\*v\\** colourspace to *CIE XYZ* tristimulus values. Parameters ---------- - Luv : array_like + Luv *CIE L\\*u\\*v\\** colourspace array. - illuminant : array_like, optional + illuminant Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Notes ----- - +----------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ @@ -178,15 +200,22 @@ def Luv_to_XYZ(Luv, X_r, Y_r, Z_r = tsplit(xyY_to_XYZ(xy_to_xyY(illuminant))) - with domain_range_scale('100'): + with domain_range_scale("100"): Y = luminance_CIE1976(L, Y_r) - a = 1 / 3 * ( - (52 * L / (u + 13 * L * (4 * X_r / (X_r + 15 * Y_r + 3 * Z_r)))) - 1) + a = ( + 1 + / 3 + * ( + (52 * L / (u + 13 * L * (4 * X_r / (X_r + 15 * Y_r + 3 * Z_r)))) + - 1 + ) + ) b = -5 * Y c = -1 / 3.0 - d = Y * (39 * L / (v + 13 * L * (9 * Y_r / - (X_r + 15 * Y_r + 3 * Z_r))) - 5) + d = Y * ( + 39 * L / (v + 13 * L * (9 * Y_r / (X_r + 15 * Y_r + 3 * Z_r))) - 5 + ) X = (d - b) / (a - c) Z = X * a + b @@ -196,29 +225,31 @@ def Luv_to_XYZ(Luv, return from_range_1(XYZ) -def Luv_to_uv(Luv, - illuminant=CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'] - ['D65']): +def Luv_to_uv( + Luv: ArrayLike, + illuminant: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], +) -> NDArray: """ - Returns the :math:`uv^p` chromaticity coordinates from given + Return the :math:`uv^p` chromaticity coordinates from given *CIE L\\*u\\*v\\** colourspace array. Parameters ---------- - Luv : array_like + Luv *CIE L\\*u\\*v\\** colourspace array. - illuminant : array_like, optional + illuminant Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` :math:`uv^p` chromaticity coordinates. Notes ----- - +----------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ @@ -254,35 +285,37 @@ def Luv_to_uv(Luv, return uv -def uv_to_Luv(uv, - illuminant=CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'] - ['D65'], - Y=1): +def uv_to_Luv( + uv: ArrayLike, + illuminant: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], + Y: Floating = 1, +) -> NDArray: """ - Returns the *CIE L\\*u\\*v\\** colourspace array from given :math:`uv^p` + Return the *CIE L\\*u\\*v\\** colourspace array from given :math:`uv^p` chromaticity coordinates by extending the array last dimension with given :math:`L` *Lightness*. Parameters ---------- - uv : array_like + uv :math:`uv^p` chromaticity coordinates. - illuminant : array_like, optional + illuminant Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array. - Y : numeric, optional + Y Optional :math:`Y` *luminance* value used to construct the intermediate *CIE XYZ* colourspace array, the default :math:`Y` *luminance* value is 1. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE L\\*u\\*v\\** colourspace array. Notes ----- - +----------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ @@ -308,28 +341,29 @@ def uv_to_Luv(uv, """ u, v = tsplit(uv) - Y = to_domain_1(Y) + Y = as_float_scalar(to_domain_1(Y)) X = 9 * u / (4 * v) Z = (-5 * Y * v - 3 * u / 4 + 3) / v - Y = full(u.shape, Y) - return XYZ_to_Luv(from_range_1(tstack([X, Y, Z])), illuminant) + XYZ = tstack([X, full(u.shape, Y), Z]) + + return XYZ_to_Luv(from_range_1(XYZ), illuminant) -def Luv_uv_to_xy(uv): +def Luv_uv_to_xy(uv: ArrayLike) -> NDArray: """ - Returns the *CIE xy* chromaticity coordinates from given *CIE L\\*u\\*v\\** + Return the *CIE xy* chromaticity coordinates from given *CIE L\\*u\\*v\\** colourspace :math:`uv^p` chromaticity coordinates. Parameters ---------- - uv : array_like + uv *CIE L\\*u\\*v\\* u"v"* chromaticity coordinates. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE xy* chromaticity coordinates. References @@ -352,19 +386,19 @@ def Luv_uv_to_xy(uv): return xy -def xy_to_Luv_uv(xy): +def xy_to_Luv_uv(xy: ArrayLike) -> NDArray: """ - Returns the *CIE L\\*u\\*v\\** colourspace :math:`uv^p` chromaticity + Return the *CIE L\\*u\\*v\\** colourspace :math:`uv^p` chromaticity coordinates from given *CIE xy* chromaticity coordinates. Parameters ---------- - xy : array_like + xy *CIE xy* chromaticity coordinates. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE L\\*u\\*v\\* u"v"* chromaticity coordinates. References @@ -387,24 +421,23 @@ def xy_to_Luv_uv(xy): return uv -def Luv_to_LCHuv(Luv): +def Luv_to_LCHuv(Luv: ArrayLike) -> NDArray: """ - Converts from *CIE L\\*u\\*v\\** colourspace to *CIE L\\*C\\*Huv* + Convert from *CIE L\\*u\\*v\\** colourspace to *CIE L\\*C\\*Huv* colourspace. Parameters ---------- - Luv : array_like + Luv *CIE L\\*u\\*v\\** colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE L\\*C\\*Huv* colourspace array. Notes ----- - +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ @@ -440,24 +473,23 @@ def Luv_to_LCHuv(Luv): return Jab_to_JCh(Luv) -def LCHuv_to_Luv(LCHuv): +def LCHuv_to_Luv(LCHuv: ArrayLike) -> NDArray: """ - Converts from *CIE L\\*C\\*Huv* colourspace to *CIE L\\*u\\*v\\** + Convert from *CIE L\\*C\\*Huv* colourspace to *CIE L\\*u\\*v\\** colourspace. Parameters ---------- - LCHuv : array_like + LCHuv *CIE L\\*C\\*Huv* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE L\\*u\\*v\\** colourspace array. Notes ----- - +------------+-----------------------+------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ diff --git a/colour/models/cie_ucs.py b/colour/models/cie_ucs.py index b1313fc9df..7feb7a191b 100644 --- a/colour/models/cie_ucs.py +++ b/colour/models/cie_ucs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ CIE 1960 UCS Colourspace ======================== @@ -21,40 +20,51 @@ http://en.wikipedia.org/wiki/CIE_1960_color_space#Relation_to_CIE_XYZ """ -from __future__ import division, unicode_literals - -from colour.utilities import (from_range_1, full, to_domain_1, tsplit, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from __future__ import annotations + +from colour.hints import ArrayLike, Floating, NDArray +from colour.utilities import ( + as_float_scalar, + from_range_1, + full, + to_domain_1, + tsplit, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'XYZ_to_UCS', 'UCS_to_XYZ', 'UCS_to_uv', 'uv_to_UCS', 'UCS_uv_to_xy', - 'xy_to_UCS_uv' + "XYZ_to_UCS", + "UCS_to_XYZ", + "UCS_to_uv", + "uv_to_UCS", + "UCS_uv_to_xy", + "xy_to_UCS_uv", ] -def XYZ_to_UCS(XYZ): +def XYZ_to_UCS(XYZ: ArrayLike) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to *CIE 1960 UCS* colourspace. + Convert from *CIE XYZ* tristimulus values to *CIE 1960 UCS* colourspace. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE 1960 UCS* colourspace array. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -86,23 +96,22 @@ def XYZ_to_UCS(XYZ): return from_range_1(UVW) -def UCS_to_XYZ(UVW): +def UCS_to_XYZ(UVW: ArrayLike) -> NDArray: """ - Converts from *CIE 1960 UCS* colourspace to *CIE XYZ* tristimulus values. + Convert from *CIE 1960 UCS* colourspace to *CIE XYZ* tristimulus values. Parameters ---------- - UVW : array_like + UVW *CIE 1960 UCS* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -134,24 +143,23 @@ def UCS_to_XYZ(UVW): return from_range_1(XYZ) -def UCS_to_uv(UVW): +def UCS_to_uv(UVW: ArrayLike) -> NDArray: """ - Returns the *uv* chromaticity coordinates from given *CIE 1960 UCS* + Return the *uv* chromaticity coordinates from given *CIE 1960 UCS* colourspace array. Parameters ---------- - UVW : array_like + UVW *CIE 1960 UCS* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *uv* chromaticity coordinates. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -179,23 +187,23 @@ def UCS_to_uv(UVW): return uv -def uv_to_UCS(uv, V=1): +def uv_to_UCS(uv: ArrayLike, V: Floating = 1) -> NDArray: """ - Returns the *CIE 1960 UCS* colourspace array from given *uv* chromaticity + Return the *CIE 1960 UCS* colourspace array from given *uv* chromaticity coordinates. Parameters ---------- - uv : array_like + uv *uv* chromaticity coordinates. - V : numeric, optional + V Optional :math:`V` *luminance* value used to construct the *CIE 1960 UCS* colourspace array, the default :math:`V` *luminance* is set to 1. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE 1960 UCS* colourspace array. References @@ -211,29 +219,26 @@ def uv_to_UCS(uv, V=1): """ u, v = tsplit(uv) - V = full(u.shape, V) - - U = V * u / v - W = -V * (u + v - 1) / v + V = as_float_scalar(to_domain_1(V)) - UVW = tstack([U, V, W]) + UVW = tstack([V * u / v, full(u.shape, V), -V * (u + v - 1) / v]) return from_range_1(UVW) -def UCS_uv_to_xy(uv): +def UCS_uv_to_xy(uv: ArrayLike) -> NDArray: """ - Returns the *CIE xy* chromaticity coordinates from given *CIE 1960 UCS* + Return the *CIE xy* chromaticity coordinates from given *CIE 1960 UCS* colourspace *uv* chromaticity coordinates. Parameters ---------- - uv : array_like + uv *CIE UCS uv* chromaticity coordinates. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE xy* chromaticity coordinates. References @@ -256,19 +261,19 @@ def UCS_uv_to_xy(uv): return xy -def xy_to_UCS_uv(xy): +def xy_to_UCS_uv(xy: ArrayLike) -> NDArray: """ - Returns the *CIE 1960 UCS* colourspace *uv* chromaticity coordinates from + Return the *CIE 1960 UCS* colourspace *uv* chromaticity coordinates from given *CIE xy* chromaticity coordinates. Parameters ---------- - xy : array_like + xy *CIE xy* chromaticity coordinates. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE UCS uv* chromaticity coordinates. References diff --git a/colour/models/cie_uvw.py b/colour/models/cie_uvw.py index 88ef11e66b..2d7ba30bcf 100644 --- a/colour/models/cie_uvw.py +++ b/colour/models/cie_uvw.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ CIE 1964 U*V*W* Colourspace =========================== @@ -15,55 +14,64 @@ http://en.wikipedia.org/wiki/CIE_1964_color_space """ -from __future__ import division, unicode_literals +from __future__ import annotations from colour.algebra import spow from colour.colorimetry import CCS_ILLUMINANTS -from colour.models import (UCS_to_uv, UCS_uv_to_xy, XYZ_to_UCS, XYZ_to_xyY, - xy_to_UCS_uv, xyY_to_XYZ, xyY_to_xy) +from colour.hints import ArrayLike, NDArray +from colour.models import ( + UCS_to_uv, + UCS_uv_to_xy, + XYZ_to_UCS, + XYZ_to_xyY, + xy_to_UCS_uv, + xyY_to_XYZ, + xyY_to_xy, +) from colour.utilities import from_range_100, to_domain_100, tsplit, tstack -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['XYZ_to_UVW', 'UVW_to_XYZ'] - - -def XYZ_to_UVW(XYZ, - illuminant=CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer']['D65']): +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "XYZ_to_UVW", + "UVW_to_XYZ", +] + + +def XYZ_to_UVW( + XYZ: ArrayLike, + illuminant: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], +) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to *CIE 1964 U\\*V\\*W\\** + Convert from *CIE XYZ* tristimulus values to *CIE 1964 U\\*V\\*W\\** colourspace. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. - illuminant : array_like, optional + illuminant Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE 1964 U\\*V\\*W\\** colourspace array. - Warnings - -------- - The input domain and output range of that definition are non standard! - Notes ----- - +----------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ - | ``XYZ`` | [0, 1] | [0, 1] | + | ``XYZ`` | [0, 100] | [0, 1] | +----------------+-----------------------+-----------------+ | ``illuminant`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ @@ -96,7 +104,7 @@ def XYZ_to_UVW(XYZ, xyY = XYZ_to_xyY(XYZ, xy) _x, _y, Y = tsplit(xyY) - u, v = tsplit(UCS_to_uv(XYZ_to_UCS(XYZ))) + u, v = tsplit(UCS_to_uv(XYZ_to_UCS(XYZ / 100))) u_0, v_0 = tsplit(xy_to_UCS_uv(xy)) W = 25 * spow(Y, 1 / 3) - 17 @@ -108,33 +116,31 @@ def XYZ_to_UVW(XYZ, return from_range_100(UVW) -def UVW_to_XYZ(UVW, - illuminant=CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer']['D65']): +def UVW_to_XYZ( + UVW: ArrayLike, + illuminant: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], +) -> NDArray: """ - Converts *CIE 1964 U\\*V\\*W\\** colourspace to *CIE XYZ* tristimulus + Convert *CIE 1964 U\\*V\\*W\\** colourspace to *CIE XYZ* tristimulus values. Parameters ---------- - UVW : array_like + UVW *CIE 1964 U\\*V\\*W\\** colourspace array. - illuminant : array_like, optional + illuminant Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. - Warnings - -------- - The input domain and output range of that definition are non standard! - Notes ----- - +----------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ @@ -150,7 +156,7 @@ def UVW_to_XYZ(UVW, +----------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ - | ``XYZ`` | [0, 1] | [0, 1] | + | ``XYZ`` | [0, 100 | [0, 1] | +----------------+-----------------------+-----------------+ References diff --git a/colour/models/cie_xyy.py b/colour/models/cie_xyy.py index fa85c05355..6165873c10 100644 --- a/colour/models/cie_xyy.py +++ b/colour/models/cie_xyy.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Tristimulus Values, CIE xyY Colourspace and Chromaticity Coordinates ==================================================================== @@ -22,49 +21,64 @@ February 24, 2014, from http://en.wikipedia.org/wiki/CIE_1931_color_space """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS -from colour.utilities import (as_float_array, from_range_1, full, to_domain_1, - tsplit, tstack, zeros) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import ArrayLike, Floating, NDArray +from colour.utilities import ( + as_float_array, + as_float_scalar, + from_range_1, + full, + to_domain_1, + tsplit, + tstack, + zeros, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'XYZ_to_xyY', 'xyY_to_XYZ', 'xy_to_xyY', 'xyY_to_xy', 'XYZ_to_xy', - 'xy_to_XYZ' + "XYZ_to_xyY", + "xyY_to_XYZ", + "xy_to_xyY", + "xyY_to_xy", + "XYZ_to_xy", + "xy_to_XYZ", ] -def XYZ_to_xyY(XYZ, - illuminant=CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer']['D65']): +def XYZ_to_xyY( + XYZ: ArrayLike, + illuminant: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], +) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to *CIE xyY* colourspace and + Convert from *CIE XYZ* tristimulus values to *CIE xyY* colourspace and reference *illuminant*. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. - illuminant : array_like, optional + illuminant Reference *illuminant* chromaticity coordinates. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE xyY* colourspace array. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -98,30 +112,34 @@ def XYZ_to_xyY(XYZ, xyY = np.where( np.all(XYZ == 0, axis=-1)[..., np.newaxis], XYZ_n, - tstack([X / (X + Y + Z), Y / (X + Y + Z), - from_range_1(Y)]), + tstack( + [ + X / (X + Y + Z), + Y / (X + Y + Z), + from_range_1(Y), + ] + ), ) return xyY -def xyY_to_XYZ(xyY): +def xyY_to_XYZ(xyY: ArrayLike) -> NDArray: """ - Converts from *CIE xyY* colourspace to *CIE XYZ* tristimulus values. + Convert from *CIE xyY* colourspace to *CIE XYZ* tristimulus values. Parameters ---------- - xyY : array_like + xyY *CIE xyY* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -157,9 +175,9 @@ def xyY_to_XYZ(xyY): return from_range_1(XYZ) -def xyY_to_xy(xyY): +def xyY_to_xy(xyY: ArrayLike) -> NDArray: """ - Converts from *CIE xyY* colourspace to *CIE xy* chromaticity coordinates. + Convert from *CIE xyY* colourspace to *CIE xy* chromaticity coordinates. ``xyY`` argument with last dimension being equal to 2 will be assumed to be a *CIE xy* chromaticity coordinates argument and will be returned directly @@ -167,17 +185,16 @@ def xyY_to_xy(xyY): Parameters ---------- - xyY : array_like + xyY *CIE xyY* colourspace array or *CIE xy* chromaticity coordinates. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE xy* chromaticity coordinates. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -210,9 +227,9 @@ def xyY_to_xy(xyY): return xy -def xy_to_xyY(xy, Y=1): +def xy_to_xyY(xy: ArrayLike, Y: Floating = 1) -> NDArray: """ - Converts from *CIE xy* chromaticity coordinates to *CIE xyY* colourspace by + Convert from *CIE xy* chromaticity coordinates to *CIE xyY* colourspace by extending the array last dimension with given :math:`Y` *luminance*. ``xy`` argument with last dimension being equal to 3 will be assumed to be @@ -221,20 +238,19 @@ def xy_to_xyY(xy, Y=1): Parameters ---------- - xy : array_like + xy *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array. - Y : numeric, optional + Y Optional :math:`Y` *luminance* value used to construct the *CIE xyY* colourspace array, the default :math:`Y` *luminance* value is 1. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE xyY* colourspace array. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -270,44 +286,44 @@ def xy_to_xyY(xy, Y=1): """ xy = as_float_array(xy) - Y = to_domain_1(Y) + Y = as_float_scalar(to_domain_1(Y)) - shape = xy.shape # Assuming ``xy`` is actually a *CIE xyY* colourspace array argument and # returning it directly. - if shape[-1] == 3: + if xy.shape[-1] == 3: return xy x, y = tsplit(xy) - Y = full(x.shape, from_range_1(Y)) - xyY = tstack([x, y, Y]) + xyY = tstack([x, y, full(x.shape, Y)]) - return xyY + return from_range_1(xyY, np.array([1, 1, 100])) -def XYZ_to_xy(XYZ, - illuminant=CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'] - ['D65']): +def XYZ_to_xy( + XYZ: ArrayLike, + illuminant: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], +) -> NDArray: """ - Returns the *CIE xy* chromaticity coordinates from given *CIE XYZ* + Return the *CIE xy* chromaticity coordinates from given *CIE XYZ* tristimulus values. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. - illuminant : array_like, optional + illuminant Reference *illuminant* chromaticity coordinates. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE xy* chromaticity coordinates. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -328,24 +344,23 @@ def XYZ_to_xy(XYZ, return xyY_to_xy(XYZ_to_xyY(XYZ, illuminant)) -def xy_to_XYZ(xy): +def xy_to_XYZ(xy: ArrayLike) -> NDArray: """ - Returns the *CIE XYZ* tristimulus values from given *CIE xy* chromaticity + Return the *CIE XYZ* tristimulus values from given *CIE xy* chromaticity coordinates. Parameters ---------- - xy : array_like + xy *CIE xy* chromaticity coordinates. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ diff --git a/colour/models/common.py b/colour/models/common.py index 3d1e1a3174..39b1d28382 100644 --- a/colour/models/common.py +++ b/colour/models/common.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Common Colour Models Utilities ============================== @@ -7,6 +6,7 @@ - :func:`colour.models.Jab_to_JCh` - :func:`colour.models.JCh_to_Jab` +- :attr:`colour.COLOURSPACE_MODELS` References ---------- @@ -15,30 +15,44 @@ ISBN:978-3-901906-33-6 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.algebra import cartesian_to_polar, polar_to_cartesian -from colour.utilities import (domain_range_scale, from_range_degrees, - to_domain_degrees, tsplit, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import ArrayLike, NDArray, Tuple +from colour.utilities import ( + CaseInsensitiveMapping, + attest, + from_range_degrees, + to_domain_degrees, + tsplit, + tstack, +) +from colour.utilities.documentation import ( + DocstringTuple, + is_documentation_building, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'Jab_to_JCh', 'JCh_to_Jab', 'COLOURSPACE_MODELS', - 'COLOURSPACE_MODELS_AXIS_LABELS', 'XYZ_to_colourspace_model' + "Jab_to_JCh", + "JCh_to_Jab", + "COLOURSPACE_MODELS", + "COLOURSPACE_MODELS_AXIS_LABELS", + "COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE", ] -def Jab_to_JCh(Jab): +def Jab_to_JCh(Jab: ArrayLike) -> NDArray: """ - Converts from *Jab** colour representation to *JCh* colour representation. + Convert from *Jab* colour representation to *JCh* colour representation. This definition is used to perform conversion from *CIE L\\*a\\*b\\** colourspace to *CIE L\\*C\\*Hab* colourspace and for other similar @@ -49,17 +63,16 @@ def Jab_to_JCh(Jab): Parameters ---------- - Jab : array_like - *Jab** colour representation array. + Jab + *Jab* colour representation array. Returns ------- - ndarray + :class:`numpy.ndarray` *JCh* colour representation array. Notes ----- - +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ @@ -100,9 +113,9 @@ def Jab_to_JCh(Jab): return JCh -def JCh_to_Jab(JCh): +def JCh_to_Jab(JCh: ArrayLike) -> NDArray: """ - Converts from *JCh* colour representation to *Jab** colour representation. + Convert from *JCh* colour representation to *Jab* colour representation. This definition is used to perform conversion from *CIE L\\*C\\*Hab* colourspace to *CIE L\\*a\\*b\\** colourspace and for other similar @@ -112,17 +125,16 @@ def JCh_to_Jab(JCh): Parameters ---------- - JCh : array_like + JCh *JCh* colour representation array. Returns ------- - ndarray - *Jab** colour representation array. + :class:`numpy.ndarray` + *Jab* colour representation array. Notes ----- - +-------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +=============+=======================+=================+ @@ -157,210 +169,109 @@ def JCh_to_Jab(JCh): L, C, H = tsplit(JCh) a, b = tsplit( - polar_to_cartesian(tstack([C, np.radians(to_domain_degrees(H))]))) + polar_to_cartesian(tstack([C, np.radians(to_domain_degrees(H))])) + ) Jab = tstack([L, a, b]) return Jab -COLOURSPACE_MODELS = ('CIE XYZ', 'CIE xyY', 'CIE Lab', 'CIE LCHab', 'CIE Luv', - 'CIE Luv uv', 'CIE LCHuv', 'CIE UCS', 'CIE UCS uv', - 'CIE UVW', 'DIN 99', 'Hunter Lab', 'Hunter Rdab', 'IPT', - 'JzAzBz', 'OSA UCS', 'hdr-CIELAB', 'hdr-IPT') - -COLOURSPACE_MODELS_AXIS_LABELS = { - 'CIE XYZ': ('X', 'Y', 'Z'), - 'CIE xyY': ('x', 'y', 'Y'), - 'CIE Lab': ('$L^*$', '$a^*$', '$b^*$'), - 'CIE LCHab': ('$L^*$', 'CH', 'ab'), - 'CIE Luv': ('$L^*$', '$u^\\prime$', '$v^\\prime$'), - 'CIE Luv uv': ('$u^\\prime$', '$v^\\prime$'), - 'CIE LCHuv': ('$L^*$', 'CH', 'uv'), - 'CIE UCS': ('U', 'V', 'W'), - 'CIE UCS uv': ('u', 'v'), - 'CIE UVW': ('U', 'V', 'W'), - 'DIN 99': ('L99', 'a99', 'b99'), - 'Hunter Lab': ('$L^*$', '$a^*$', '$b^*$'), - 'Hunter Rdab': ('Rd', 'a', 'b'), - 'IGPGTG': ('$I_G$', '$P_G$', '$T_G$'), - 'IPT': ('I', 'P', 'T'), - 'JzAzBz': ('$J_z$', '$A_z$', '$B_z$'), - 'OSA UCS': ('L', 'j', 'g'), - 'hdr-CIELAB': ('L hdr', 'a hdr', 'b hdr'), - 'hdr-IPT': ('I hdr', 'P hdr', 'T hdr'), -} -""" -Colourspace models labels mapping. - -COLOURSPACE_MODELS_AXIS_LABELS : dict - **{'CIE XYZ', 'CIE xyY', 'CIE Lab', 'CIE LCHab, 'CIE Luv', 'CIE Luv uv', - 'CIE LCHuv', 'CIE UCS', 'CIE UCS uv', 'CIE UVW', 'DIN 99', 'Hunter Lab', - 'Hunter Rdab', 'IGPGTG', 'IPT', 'JzAzBz', 'OSA UCS', 'hdr-CIELAB', - 'hdr-IPT'}** +COLOURSPACE_MODELS: Tuple = ( + "CAM02LCD", + "CAM02SCD", + "CAM02UCS", + "CAM16LCD", + "CAM16SCD", + "CAM16UCS", + "CIE XYZ", + "CIE xyY", + "CIE Lab", + "CIE Luv", + "CIE UCS", + "CIE UVW", + "DIN99", + "Hunter Lab", + "Hunter Rdab", + "ICaCb", + "ICtCp", + "IPT", + "IgPgTg", + "Jzazbz", + "OSA UCS", + "Oklab", + "hdr-CIELAB", + "hdr-IPT", +) +if is_documentation_building(): # pragma: no cover + COLOURSPACE_MODELS = DocstringTuple(COLOURSPACE_MODELS) + COLOURSPACE_MODELS.__doc__ = """ +Colourspace models supporting a direct conversion to *CIE XYZ* tristimulus +values. """ - -def XYZ_to_colourspace_model(XYZ, illuminant, model, **kwargs): - """ - Converts from *CIE XYZ* tristimulus values to given colourspace model. - - Parameters - ---------- - XYZ : array_like - *CIE XYZ* tristimulus values. - illuminant : array_like - Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* - colourspace array. - model : unicode - **{'CIE XYZ', 'CIE xyY', 'CIE xy', 'CIE Lab', 'CIE LCHab', 'CIE Luv', - 'CIE Luv uv', 'CIE LCHuv', 'CIE UCS', 'CIE UCS uv', 'CIE UVW', - 'DIN 99', 'Hunter Lab', 'Hunter Rdab', 'IGPGTG', 'IPT', 'JzAzBz, - 'OSA UCS', 'hdr-CIELAB', 'hdr-IPT'}**, - Colourspace model to convert the *CIE XYZ* tristimulus values to. - - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments. - - Returns - ------- - ndarray - Colourspace model values. - - Warnings - -------- - This definition is is deprecated and will be removed in a future release. - :func:`colour.convert` definition should be used instead. - - Examples - -------- - >>> import numpy as np - >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) - >>> W = np.array([0.31270, 0.32900]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'CIE XYZ') - array([ 0.2065400..., 0.1219722..., 0.0513695...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'CIE xyY') - array([ 0.5436955..., 0.3210794..., 0.1219722...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'CIE xy') - array([ 0.5436955..., 0.3210794...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'CIE Lab') - array([ 0.4152787..., 0.5263858..., 0.2692317...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'CIE LCHab') - array([ 0.4152787..., 0.5912425..., 0.0752458...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'CIE Luv') - array([ 0.4152787..., 0.9683626..., 0.1775210...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'CIE Luv uv') - array([ 0.3772021..., 0.5012026...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'CIE LCHuv') - array([ 0.4152787..., 0.9844997..., 0.0288560...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'CIE UCS') - array([ 0.1376933..., 0.1219722..., 0.1053731...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'CIE UCS uv') - array([ 0.3772021..., 0.3341350...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'CIE UVW') - array([ 0.9455035..., 0.1155536..., 0.4054757...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'DIN 99') - array([ 0.5322822..., 0.2841634..., 0.0389839...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'Hunter Lab') - array([ 0.3492452..., 0.4703302..., 0.1439330...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'Hunter Rdab') - array([ 0.1219722..., 0.5709032..., 0.1747109...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'IGPGTG') - array([ 0.4242125..., 0.1863249..., 0.1068922...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'IPT') - array([ 0.3842619..., 0.3848730..., 0.1888683...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'JzAzBz') - array([ 0.0053504..., 0.0092430..., 0.0052600...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'OSA UCS') - array([-0.0300499..., 0.0299713..., -0.0966784...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'hdr-CIELAB') - array([ 0.5187002..., 0.6047633..., 0.3214551...]) - >>> XYZ_to_colourspace_model( # doctest: +ELLIPSIS - ... XYZ, W, 'hdr-IPT') - array([ 0.4839376..., 0.4244990..., 0.2201954...]) - >>> try: - ... XYZ_to_colourspace_model(XYZ, W, 'Undefined') - ... except ValueError as error: - ... print(error) - "Undefined" not found in colourspace models: "CIE XYZ, CIE xyY, CIE Lab, \ -CIE LCHab, CIE Luv, CIE Luv uv, CIE LCHuv, CIE UCS, CIE UCS uv, CIE UVW, \ -DIN 99, Hunter Lab, Hunter Rdab, IPT, JzAzBz, OSA UCS, hdr-CIELAB, hdr-IPT". - """ - - from colour.models import ( - Lab_to_DIN99, Lab_to_LCHab, Luv_to_LCHuv, Luv_to_uv, UCS_to_uv, - XYZ_to_Hunter_Lab, XYZ_to_Hunter_Rdab, XYZ_to_IGPGTG, XYZ_to_IPT, - XYZ_to_Lab, XYZ_to_Luv, XYZ_to_OSA_UCS, XYZ_to_UCS, XYZ_to_UVW, - XYZ_to_hdr_CIELab, XYZ_to_hdr_IPT, XYZ_to_JzAzBz, XYZ_to_xy, - XYZ_to_xyY, xy_to_XYZ) - - with domain_range_scale(1): - values = None - if model == 'CIE XYZ': - values = XYZ - elif model == 'CIE xyY': - values = XYZ_to_xyY(XYZ, illuminant) - elif model == 'CIE xy': - values = XYZ_to_xy(XYZ, illuminant) - elif model == 'CIE Lab': - values = XYZ_to_Lab(XYZ, illuminant) - elif model == 'CIE LCHab': - values = Lab_to_LCHab(XYZ_to_Lab(XYZ, illuminant)) - elif model == 'CIE Luv': - values = XYZ_to_Luv(XYZ, illuminant) - elif model == 'CIE Luv uv': - values = Luv_to_uv(XYZ_to_Luv(XYZ, illuminant), illuminant) - elif model == 'CIE LCHuv': - values = Luv_to_LCHuv(XYZ_to_Luv(XYZ, illuminant)) - elif model == 'CIE UCS': - values = XYZ_to_UCS(XYZ) - elif model == 'CIE UCS uv': - values = UCS_to_uv(XYZ_to_UCS(XYZ)) - elif model == 'CIE UVW': - values = XYZ_to_UVW(XYZ, illuminant) - elif model == 'DIN 99': - values = Lab_to_DIN99(XYZ_to_Lab(XYZ, illuminant)) - elif model == 'Hunter Lab': - values = XYZ_to_Hunter_Lab(XYZ, xy_to_XYZ(illuminant)) - elif model == 'Hunter Rdab': - values = XYZ_to_Hunter_Rdab(XYZ, xy_to_XYZ(illuminant)) - elif model == 'IGPGTG': - values = XYZ_to_IGPGTG(XYZ) - elif model == 'IPT': - values = XYZ_to_IPT(XYZ) - elif model == 'JzAzBz': - values = XYZ_to_JzAzBz(XYZ) - elif model == 'OSA UCS': - values = XYZ_to_OSA_UCS(XYZ) - elif model == 'hdr-CIELAB': - values = XYZ_to_hdr_CIELab(XYZ, illuminant, **kwargs) - elif model == 'hdr-IPT': - values = XYZ_to_hdr_IPT(XYZ, **kwargs) - - if values is None: - raise ValueError( - '"{0}" not found in colourspace models: "{1}".'.format( - model, ', '.join(COLOURSPACE_MODELS))) - - return values +COLOURSPACE_MODELS_AXIS_LABELS: CaseInsensitiveMapping = ( + CaseInsensitiveMapping( + { + "CAM02LCD": ("$J^\\prime$", "$a^\\prime$", "$b^\\prime$"), + "CAM02SCD": ("$J^\\prime$", "$a^\\prime$", "$b^\\prime$"), + "CAM02UCS": ("$J^\\prime$", "$a^\\prime$", "$b^\\prime$"), + "CAM16LCD": ("$J^\\prime$", "$a^\\prime$", "$b^\\prime$"), + "CAM16SCD": ("$J^\\prime$", "$a^\\prime$", "$b^\\prime$"), + "CAM16UCS": ("$J^\\prime$", "$a^\\prime$", "$b^\\prime$"), + "CIE XYZ": ("X", "Y", "Z"), + "CIE xyY": ("x", "y", "Y"), + "CIE Lab": ("$L^*$", "$a^*$", "$b^*$"), + "CIE Luv": ("$L^*$", "$u^\\prime$", "$v^\\prime$"), + "CIE UCS": ("U", "V", "W"), + "CIE UVW": ("U", "V", "W"), + "DIN99": ("$L_{99}$", "$a_{99}$", "$b_{99}$"), + "Hunter Lab": ("$L^*$", "$a^*$", "$b^*$"), + "Hunter Rdab": ("Rd", "a", "b"), + "ICaCb": ("$I$", "$C_a$", "$C_b$"), + "ICtCp": ("$I$", "$C_T$", "$C_P$"), + "IPT": ("I", "P", "T"), + "IgPgTg": ("$I_G$", "$P_G$", "$T_G$"), + "Jzazbz": ("$J_z$", "$a_z$", "$b_z$"), + "OSA UCS": ("L", "j", "g"), + "Oklab": ("$L$", "$a$", "$b$"), + "hdr-CIELAB": ("L hdr", "a hdr", "b hdr"), + "hdr-IPT": ("I hdr", "P hdr", "T hdr"), + } + ) +) +"""Colourspace models labels mapping.""" + +attest(COLOURSPACE_MODELS == tuple(COLOURSPACE_MODELS_AXIS_LABELS.keys())) + +COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE: ( + CaseInsensitiveMapping +) = CaseInsensitiveMapping( + { + "CAM02LCD": np.array([100, 100, 100]), + "CAM02SCD": np.array([100, 100, 100]), + "CAM02UCS": np.array([100, 100, 100]), + "CAM16LCD": np.array([100, 100, 100]), + "CAM16SCD": np.array([100, 100, 100]), + "CAM16UCS": np.array([100, 100, 100]), + "CIE XYZ": np.array([1, 1, 1]), + "CIE xyY": np.array([1, 1, 1]), + "CIE Lab": np.array([100, 100, 100]), + "CIE Luv": np.array([100, 100, 100]), + "CIE UCS": np.array([1, 1, 1]), + "CIE UVW": np.array([100, 100, 100]), + "DIN99": np.array([100, 100, 100]), + "Hunter Lab": np.array([100, 100, 100]), + "Hunter Rdab": np.array([100, 100, 100]), + "ICaCb": np.array([1, 1, 1]), + "ICtCp": np.array([1, 1, 1]), + "IPT": np.array([1, 1, 1]), + "IgPgTg": np.array([1, 1, 1]), + "Jzazbz": np.array([1, 1, 1]), + "OSA UCS": np.array([100, 100, 100]), + "Oklab": np.array([1, 1, 1]), + "hdr-CIELAB": np.array([100, 100, 100]), + "hdr-IPT": np.array([100, 100, 100]), + } +) +"""Colourspace models domain-range scale **'1'** to **'Reference'** mapping.""" diff --git a/colour/models/datasets/__init__.py b/colour/models/datasets/__init__.py index 4d88ebc3c7..31bdd4e024 100644 --- a/colour/models/datasets/__init__.py +++ b/colour/models/datasets/__init__.py @@ -1,14 +1,15 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .macadam_ellipses import DATA_MACADAM_1942_ELLIPSES -from .pointer_gamut import (CCS_ILLUMINANT_POINTER_GAMUT, - DATA_POINTER_GAMUT_VOLUME, - CCS_POINTER_GAMUT_BOUNDARY) +from .pointer_gamut import ( + CCS_ILLUMINANT_POINTER_GAMUT, + DATA_POINTER_GAMUT_VOLUME, + CCS_POINTER_GAMUT_BOUNDARY, +) -__all__ = ['DATA_MACADAM_1942_ELLIPSES'] +__all__ = [ + "DATA_MACADAM_1942_ELLIPSES", +] __all__ += [ - 'CCS_ILLUMINANT_POINTER_GAMUT', 'DATA_POINTER_GAMUT_VOLUME', - 'CCS_POINTER_GAMUT_BOUNDARY' + "CCS_ILLUMINANT_POINTER_GAMUT", + "DATA_POINTER_GAMUT_VOLUME", + "CCS_POINTER_GAMUT_BOUNDARY", ] diff --git a/colour/models/datasets/macadam_ellipses.py b/colour/models/datasets/macadam_ellipses.py index b33e9b05c3..65ca303aab 100644 --- a/colour/models/datasets/macadam_ellipses.py +++ b/colour/models/datasets/macadam_ellipses.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ MacAdam (1942) Ellipses (Observer PGN) ====================================== -Defines *MacAdam (1942) Ellipses (Observer PGN)* ellipses data. +Defines the *MacAdam (1942) Ellipses (Observer PGN)* ellipses data. References ---------- @@ -17,46 +16,52 @@ Quantitative Data and Formulae (p. 309). Wiley. ISBN:978-0-471-39918-6 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import NDArray -__all__ = ['DATA_MACADAM_1942_ELLIPSES'] +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" -DATA_MACADAM_1942_ELLIPSES = np.array([ - [0.160, 0.057, 0.85, 0.35, 62.5, 0.94, 0.30, 62.3], - [0.187, 0.118, 2.20, 0.55, 77.0, 2.31, 0.44, 74.8], - [0.253, 0.125, 2.50, 0.50, 55.5, 2.49, 0.49, 54.8], - [0.150, 0.680, 9.60, 2.30, 105.0, 9.09, 2.21, 102.9], - [0.131, 0.521, 4.70, 2.00, 112.5, 4.67, 2.10, 110.5], - [0.212, 0.550, 5.80, 2.30, 100.0, 5.63, 2.30, 100.0], - [0.258, 0.450, 5.00, 2.00, 92.0, 4.54, 2.08, 88.5], - [0.152, 0.365, 3.80, 1.90, 110.0, 3.81, 1.86, 111.0], - [0.280, 0.385, 4.00, 1.50, 75.5, 4.26, 1.46, 74.6], - [0.380, 0.498, 4.40, 1.20, 70.0, 4.23, 1.32, 69.4], - [0.160, 0.200, 2.10, 0.95, 104.0, 2.08, 0.94, 95.4], - [0.228, 0.250, 3.10, 0.90, 72.0, 3.09, 0.82, 70.9], - [0.305, 0.323, 2.30, 0.90, 58.0, 2.55, 0.68, 57.2], - [0.385, 0.393, 3.80, 1.60, 65.5, 3.70, 1.48, 65.5], - [0.472, 0.399, 3.20, 1.40, 51.0, 3.21, 1.30, 54.0], - [0.527, 0.350, 2.60, 1.30, 20.0, 2.56, 1.27, 22.8], - [0.475, 0.300, 2.90, 1.10, 28.5, 2.89, 0.99, 29.1], - [0.510, 0.236, 2.40, 1.20, 29.5, 2.40, 1.15, 30.7], - [0.596, 0.283, 2.60, 1.30, 13.0, 2.49, 1.15, 11.1], - [0.344, 0.284, 2.30, 0.90, 60.0, 2.24, 0.97, 65.7], - [0.390, 0.237, 2.50, 1.00, 47.0, 2.43, 0.98, 44.2], - [0.441, 0.198, 2.80, 0.95, 34.5, 2.73, 0.90, 33.7], - [0.278, 0.223, 2.40, 0.55, 57.5, 2.34, 0.61, 60.3], - [0.300, 0.163, 2.90, 0.60, 54.0, 3.01, 0.60, 53.4], - [0.365, 0.153, 3.60, 0.95, 40.0, 4.12, 0.90, 38.6], -]) +__all__ = [ + "DATA_MACADAM_1942_ELLIPSES", +] + +DATA_MACADAM_1942_ELLIPSES: NDArray = np.array( + [ + [0.160, 0.057, 0.85, 0.35, 62.5, 0.94, 0.30, 62.3], + [0.187, 0.118, 2.20, 0.55, 77.0, 2.31, 0.44, 74.8], + [0.253, 0.125, 2.50, 0.50, 55.5, 2.49, 0.49, 54.8], + [0.150, 0.680, 9.60, 2.30, 105.0, 9.09, 2.21, 102.9], + [0.131, 0.521, 4.70, 2.00, 112.5, 4.67, 2.10, 110.5], + [0.212, 0.550, 5.80, 2.30, 100.0, 5.63, 2.30, 100.0], + [0.258, 0.450, 5.00, 2.00, 92.0, 4.54, 2.08, 88.5], + [0.152, 0.365, 3.80, 1.90, 110.0, 3.81, 1.86, 111.0], + [0.280, 0.385, 4.00, 1.50, 75.5, 4.26, 1.46, 74.6], + [0.380, 0.498, 4.40, 1.20, 70.0, 4.23, 1.32, 69.4], + [0.160, 0.200, 2.10, 0.95, 104.0, 2.08, 0.94, 95.4], + [0.228, 0.250, 3.10, 0.90, 72.0, 3.09, 0.82, 70.9], + [0.305, 0.323, 2.30, 0.90, 58.0, 2.55, 0.68, 57.2], + [0.385, 0.393, 3.80, 1.60, 65.5, 3.70, 1.48, 65.5], + [0.472, 0.399, 3.20, 1.40, 51.0, 3.21, 1.30, 54.0], + [0.527, 0.350, 2.60, 1.30, 20.0, 2.56, 1.27, 22.8], + [0.475, 0.300, 2.90, 1.10, 28.5, 2.89, 0.99, 29.1], + [0.510, 0.236, 2.40, 1.20, 29.5, 2.40, 1.15, 30.7], + [0.596, 0.283, 2.60, 1.30, 13.0, 2.49, 1.15, 11.1], + [0.344, 0.284, 2.30, 0.90, 60.0, 2.24, 0.97, 65.7], + [0.390, 0.237, 2.50, 1.00, 47.0, 2.43, 0.98, 44.2], + [0.441, 0.198, 2.80, 0.95, 34.5, 2.73, 0.90, 33.7], + [0.278, 0.223, 2.40, 0.55, 57.5, 2.34, 0.61, 60.3], + [0.300, 0.163, 2.90, 0.60, 54.0, 3.01, 0.60, 53.4], + [0.365, 0.153, 3.60, 0.95, 40.0, 4.12, 0.90, 38.6], + ] +) """ *MacAdam (1942) Ellipses (Observer PGN)* ellipses data. @@ -79,6 +84,4 @@ References ---------- :cite:`Wyszecki2000`, :cite:`Macadam1942` - -DATA_MACADAM_1942_ELLIPSES : ndarray """ diff --git a/colour/models/datasets/pointer_gamut.py b/colour/models/datasets/pointer_gamut.py index 7e4127c390..ac17ff37dc 100644 --- a/colour/models/datasets/pointer_gamut.py +++ b/colour/models/datasets/pointer_gamut.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Pointer's Gamut =============== @@ -11,664 +10,665 @@ http://www.cis.rit.edu/research/mcsl2/online/PointerData.xls """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_LIGHT_SOURCES +from colour.hints import NDArray -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'CCS_ILLUMINANT_POINTER_GAMUT', 'DATA_POINTER_GAMUT_VOLUME', - 'CCS_POINTER_GAMUT_BOUNDARY' + "CCS_ILLUMINANT_POINTER_GAMUT", + "DATA_POINTER_GAMUT_VOLUME", + "CCS_POINTER_GAMUT_BOUNDARY", ] -CCS_ILLUMINANT_POINTER_GAMUT = ( - CCS_LIGHT_SOURCES['CIE 1931 2 Degree Standard Observer']['SC']) +CCS_ILLUMINANT_POINTER_GAMUT: NDArray = CCS_LIGHT_SOURCES[ + "CIE 1931 2 Degree Standard Observer" +]["SC"] """ *Pointer's Gamut* illuminant, i.e. *SC*. References ---------- :cite:`Pointer1980a` - -CCS_ILLUMINANT_POINTER_GAMUT : ndarray """ -DATA_POINTER_GAMUT_VOLUME = np.array([ - [15, 10, 0], - [15, 15, 10], - [15, 14, 20], - [15, 35, 30], - [15, 27, 40], - [15, 10, 50], - [15, 4, 60], - [15, 5, 70], - [15, 6, 80], - [15, 4, 90], - [15, 9, 100], - [15, 9, 110], - [15, 4, 120], - [15, 5, 130], - [15, 7, 140], - [15, 7, 150], - [15, 8, 160], - [15, 13, 170], - [15, 10, 180], - [15, 7, 190], - [15, 5, 200], - [15, 0, 210], - [15, 2, 220], - [15, 10, 230], - [15, 8, 240], - [15, 9, 250], - [15, 12, 260], - [15, 14, 270], - [15, 10, 280], - [15, 20, 290], - [15, 30, 300], - [15, 62, 310], - [15, 60, 320], - [15, 20, 330], - [15, 26, 340], - [15, 15, 350], - [20, 30, 0], - [20, 30, 10], - [20, 34, 20], - [20, 48, 30], - [20, 40, 40], - [20, 21, 50], - [20, 15, 60], - [20, 15, 70], - [20, 15, 80], - [20, 12, 90], - [20, 16, 100], - [20, 18, 110], - [20, 14, 120], - [20, 18, 130], - [20, 20, 140], - [20, 21, 150], - [20, 24, 160], - [20, 25, 170], - [20, 25, 180], - [20, 19, 190], - [20, 19, 200], - [20, 12, 210], - [20, 12, 220], - [20, 20, 230], - [20, 16, 240], - [20, 21, 250], - [20, 24, 260], - [20, 31, 270], - [20, 29, 280], - [20, 40, 290], - [20, 55, 300], - [20, 76, 310], - [20, 71, 320], - [20, 50, 330], - [20, 49, 340], - [20, 37, 350], - [25, 43, 0], - [25, 45, 10], - [25, 49, 20], - [25, 59, 30], - [25, 53, 40], - [25, 34, 50], - [25, 26, 60], - [25, 25, 70], - [25, 24, 80], - [25, 20, 90], - [25, 23, 100], - [25, 27, 110], - [25, 23, 120], - [25, 30, 130], - [25, 32, 140], - [25, 34, 150], - [25, 36, 160], - [25, 36, 170], - [25, 38, 180], - [25, 30, 190], - [25, 29, 200], - [25, 17, 210], - [25, 20, 220], - [25, 29, 230], - [25, 26, 240], - [25, 32, 250], - [25, 34, 260], - [25, 42, 270], - [25, 45, 280], - [25, 60, 290], - [25, 72, 300], - [25, 85, 310], - [25, 79, 320], - [25, 72, 330], - [25, 63, 340], - [25, 52, 350], - [30, 56, 0], - [30, 56, 10], - [30, 61, 20], - [30, 68, 30], - [30, 66, 40], - [30, 45, 50], - [30, 37, 60], - [30, 36, 70], - [30, 32, 80], - [30, 28, 90], - [30, 30, 100], - [30, 35, 110], - [30, 32, 120], - [30, 40, 130], - [30, 42, 140], - [30, 45, 150], - [30, 48, 160], - [30, 47, 170], - [30, 48, 180], - [30, 40, 190], - [30, 37, 200], - [30, 26, 210], - [30, 28, 220], - [30, 36, 230], - [30, 34, 240], - [30, 40, 250], - [30, 41, 260], - [30, 50, 270], - [30, 55, 280], - [30, 69, 290], - [30, 81, 300], - [30, 88, 310], - [30, 84, 320], - [30, 86, 330], - [30, 73, 340], - [30, 65, 350], - [35, 68, 0], - [35, 64, 10], - [35, 69, 20], - [35, 75, 30], - [35, 79, 40], - [35, 60, 50], - [35, 48, 60], - [35, 46, 70], - [35, 40, 80], - [35, 36, 90], - [35, 37, 100], - [35, 44, 110], - [35, 41, 120], - [35, 48, 130], - [35, 52, 140], - [35, 57, 150], - [35, 58, 160], - [35, 57, 170], - [35, 57, 180], - [35, 48, 190], - [35, 42, 200], - [35, 34, 210], - [35, 35, 220], - [35, 42, 230], - [35, 41, 240], - [35, 49, 250], - [35, 46, 260], - [35, 55, 270], - [35, 60, 280], - [35, 71, 290], - [35, 79, 300], - [35, 85, 310], - [35, 85, 320], - [35, 89, 330], - [35, 82, 340], - [35, 73, 350], - [40, 77, 0], - [40, 70, 10], - [40, 74, 20], - [40, 82, 30], - [40, 90, 40], - [40, 75, 50], - [40, 59, 60], - [40, 56, 70], - [40, 48, 80], - [40, 44, 90], - [40, 45, 100], - [40, 52, 110], - [40, 49, 120], - [40, 56, 130], - [40, 60, 140], - [40, 68, 150], - [40, 68, 160], - [40, 65, 170], - [40, 64, 180], - [40, 55, 190], - [40, 45, 200], - [40, 43, 210], - [40, 40, 220], - [40, 46, 230], - [40, 47, 240], - [40, 54, 250], - [40, 51, 260], - [40, 60, 270], - [40, 61, 280], - [40, 69, 290], - [40, 72, 300], - [40, 80, 310], - [40, 86, 320], - [40, 89, 330], - [40, 87, 340], - [40, 79, 350], - [45, 79, 0], - [45, 73, 10], - [45, 76, 20], - [45, 84, 30], - [45, 94, 40], - [45, 90, 50], - [45, 70, 60], - [45, 67, 70], - [45, 55, 80], - [45, 53, 90], - [45, 51, 100], - [45, 59, 110], - [45, 57, 120], - [45, 64, 130], - [45, 69, 140], - [45, 75, 150], - [45, 76, 160], - [45, 70, 170], - [45, 69, 180], - [45, 59, 190], - [45, 46, 200], - [45, 49, 210], - [45, 45, 220], - [45, 49, 230], - [45, 49, 240], - [45, 55, 250], - [45, 55, 260], - [45, 60, 270], - [45, 60, 280], - [45, 65, 290], - [45, 64, 300], - [45, 71, 310], - [45, 82, 320], - [45, 86, 330], - [45, 87, 340], - [45, 82, 350], - [50, 77, 0], - [50, 73, 10], - [50, 76, 20], - [50, 83, 30], - [50, 93, 40], - [50, 100, 50], - [50, 82, 60], - [50, 76, 70], - [50, 64, 80], - [50, 60, 90], - [50, 58, 100], - [50, 66, 110], - [50, 64, 120], - [50, 70, 130], - [50, 76, 140], - [50, 81, 150], - [50, 82, 160], - [50, 75, 170], - [50, 71, 180], - [50, 62, 190], - [50, 46, 200], - [50, 51, 210], - [50, 48, 220], - [50, 51, 230], - [50, 50, 240], - [50, 55, 250], - [50, 56, 260], - [50, 57, 270], - [50, 57, 280], - [50, 58, 290], - [50, 57, 300], - [50, 62, 310], - [50, 74, 320], - [50, 80, 330], - [50, 83, 340], - [50, 84, 350], - [55, 72, 0], - [55, 71, 10], - [55, 74, 20], - [55, 80, 30], - [55, 88, 40], - [55, 102, 50], - [55, 93, 60], - [55, 85, 70], - [55, 72, 80], - [55, 68, 90], - [55, 65, 100], - [55, 74, 110], - [55, 71, 120], - [55, 77, 130], - [55, 82, 140], - [55, 84, 150], - [55, 85, 160], - [55, 76, 170], - [55, 72, 180], - [55, 62, 190], - [55, 45, 200], - [55, 54, 210], - [55, 51, 220], - [55, 52, 230], - [55, 50, 240], - [55, 52, 250], - [55, 51, 260], - [55, 50, 270], - [55, 53, 280], - [55, 50, 290], - [55, 50, 300], - [55, 55, 310], - [55, 66, 320], - [55, 72, 330], - [55, 78, 340], - [55, 79, 350], - [60, 65, 0], - [60, 65, 10], - [60, 68, 20], - [60, 75, 30], - [60, 82, 40], - [60, 99, 50], - [60, 103, 60], - [60, 94, 70], - [60, 82, 80], - [60, 75, 90], - [60, 72, 100], - [60, 82, 110], - [60, 78, 120], - [60, 82, 130], - [60, 87, 140], - [60, 84, 150], - [60, 83, 160], - [60, 75, 170], - [60, 69, 180], - [60, 60, 190], - [60, 43, 200], - [60, 50, 210], - [60, 49, 220], - [60, 50, 230], - [60, 47, 240], - [60, 48, 250], - [60, 46, 260], - [60, 45, 270], - [60, 46, 280], - [60, 43, 290], - [60, 42, 300], - [60, 47, 310], - [60, 57, 320], - [60, 63, 330], - [60, 71, 340], - [60, 73, 350], - [65, 57, 0], - [65, 57, 10], - [65, 61, 20], - [65, 67, 30], - [65, 72, 40], - [65, 88, 50], - [65, 106, 60], - [65, 102, 70], - [65, 94, 80], - [65, 83, 90], - [65, 80, 100], - [65, 87, 110], - [65, 84, 120], - [65, 85, 130], - [65, 89, 140], - [65, 83, 150], - [65, 78, 160], - [65, 71, 170], - [65, 64, 180], - [65, 55, 190], - [65, 39, 200], - [65, 46, 210], - [65, 45, 220], - [65, 45, 230], - [65, 42, 240], - [65, 43, 250], - [65, 40, 260], - [65, 39, 270], - [65, 40, 280], - [65, 36, 290], - [65, 35, 300], - [65, 41, 310], - [65, 48, 320], - [65, 54, 330], - [65, 62, 340], - [65, 63, 350], - [70, 50, 0], - [70, 48, 10], - [70, 51, 20], - [70, 56, 30], - [70, 60, 40], - [70, 75, 50], - [70, 98, 60], - [70, 108, 70], - [70, 105, 80], - [70, 90, 90], - [70, 86, 100], - [70, 92, 110], - [70, 90, 120], - [70, 88, 130], - [70, 90, 140], - [70, 80, 150], - [70, 69, 160], - [70, 65, 170], - [70, 60, 180], - [70, 49, 190], - [70, 35, 200], - [70, 40, 210], - [70, 38, 220], - [70, 39, 230], - [70, 36, 240], - [70, 36, 250], - [70, 33, 260], - [70, 33, 270], - [70, 34, 280], - [70, 29, 290], - [70, 30, 300], - [70, 34, 310], - [70, 40, 320], - [70, 45, 330], - [70, 51, 340], - [70, 53, 350], - [75, 40, 0], - [75, 39, 10], - [75, 40, 20], - [75, 45, 30], - [75, 47, 40], - [75, 59, 50], - [75, 85, 60], - [75, 103, 70], - [75, 115, 80], - [75, 98, 90], - [75, 94, 100], - [75, 95, 110], - [75, 94, 120], - [75, 89, 130], - [75, 83, 140], - [75, 72, 150], - [75, 59, 160], - [75, 57, 170], - [75, 51, 180], - [75, 41, 190], - [75, 30, 200], - [75, 32, 210], - [75, 32, 220], - [75, 32, 230], - [75, 29, 240], - [75, 29, 250], - [75, 27, 260], - [75, 26, 270], - [75, 25, 280], - [75, 24, 290], - [75, 24, 300], - [75, 27, 310], - [75, 31, 320], - [75, 36, 330], - [75, 40, 340], - [75, 40, 350], - [80, 30, 0], - [80, 30, 10], - [80, 30, 20], - [80, 33, 30], - [80, 35, 40], - [80, 45, 50], - [80, 66, 60], - [80, 82, 70], - [80, 115, 80], - [80, 106, 90], - [80, 100, 100], - [80, 100, 110], - [80, 95, 120], - [80, 84, 130], - [80, 71, 140], - [80, 58, 150], - [80, 49, 160], - [80, 45, 170], - [80, 41, 180], - [80, 32, 190], - [80, 22, 200], - [80, 24, 210], - [80, 23, 220], - [80, 24, 230], - [80, 21, 240], - [80, 21, 250], - [80, 20, 260], - [80, 20, 270], - [80, 18, 280], - [80, 18, 290], - [80, 17, 300], - [80, 20, 310], - [80, 24, 320], - [80, 27, 330], - [80, 28, 340], - [80, 30, 350], - [85, 19, 0], - [85, 18, 10], - [85, 19, 20], - [85, 21, 30], - [85, 22, 40], - [85, 30, 50], - [85, 45, 60], - [85, 58, 70], - [85, 83, 80], - [85, 111, 90], - [85, 106, 100], - [85, 96, 110], - [85, 83, 120], - [85, 64, 130], - [85, 54, 140], - [85, 44, 150], - [85, 34, 160], - [85, 30, 170], - [85, 29, 180], - [85, 23, 190], - [85, 14, 200], - [85, 14, 210], - [85, 15, 220], - [85, 15, 230], - [85, 12, 240], - [85, 13, 250], - [85, 13, 260], - [85, 13, 270], - [85, 11, 280], - [85, 12, 290], - [85, 12, 300], - [85, 14, 310], - [85, 16, 320], - [85, 18, 330], - [85, 18, 340], - [85, 17, 350], - [90, 8, 0], - [90, 7, 10], - [90, 9, 20], - [90, 10, 30], - [90, 10, 40], - [90, 15, 50], - [90, 23, 60], - [90, 34, 70], - [90, 48, 80], - [90, 90, 90], - [90, 108, 100], - [90, 84, 110], - [90, 50, 120], - [90, 35, 130], - [90, 30, 140], - [90, 20, 150], - [90, 15, 160], - [90, 15, 170], - [90, 16, 180], - [90, 13, 190], - [90, 7, 200], - [90, 4, 210], - [90, 6, 220], - [90, 7, 230], - [90, 4, 240], - [90, 4, 250], - [90, 6, 260], - [90, 6, 270], - [90, 4, 280], - [90, 5, 290], - [90, 5, 300], - [90, 6, 310], - [90, 8, 320], - [90, 9, 330], - [90, 4, 340], - [90, 6, 350], -]) +DATA_POINTER_GAMUT_VOLUME: NDArray = np.array( + [ + [15, 10, 0], + [15, 15, 10], + [15, 14, 20], + [15, 35, 30], + [15, 27, 40], + [15, 10, 50], + [15, 4, 60], + [15, 5, 70], + [15, 6, 80], + [15, 4, 90], + [15, 9, 100], + [15, 9, 110], + [15, 4, 120], + [15, 5, 130], + [15, 7, 140], + [15, 7, 150], + [15, 8, 160], + [15, 13, 170], + [15, 10, 180], + [15, 7, 190], + [15, 5, 200], + [15, 0, 210], + [15, 2, 220], + [15, 10, 230], + [15, 8, 240], + [15, 9, 250], + [15, 12, 260], + [15, 14, 270], + [15, 10, 280], + [15, 20, 290], + [15, 30, 300], + [15, 62, 310], + [15, 60, 320], + [15, 20, 330], + [15, 26, 340], + [15, 15, 350], + [20, 30, 0], + [20, 30, 10], + [20, 34, 20], + [20, 48, 30], + [20, 40, 40], + [20, 21, 50], + [20, 15, 60], + [20, 15, 70], + [20, 15, 80], + [20, 12, 90], + [20, 16, 100], + [20, 18, 110], + [20, 14, 120], + [20, 18, 130], + [20, 20, 140], + [20, 21, 150], + [20, 24, 160], + [20, 25, 170], + [20, 25, 180], + [20, 19, 190], + [20, 19, 200], + [20, 12, 210], + [20, 12, 220], + [20, 20, 230], + [20, 16, 240], + [20, 21, 250], + [20, 24, 260], + [20, 31, 270], + [20, 29, 280], + [20, 40, 290], + [20, 55, 300], + [20, 76, 310], + [20, 71, 320], + [20, 50, 330], + [20, 49, 340], + [20, 37, 350], + [25, 43, 0], + [25, 45, 10], + [25, 49, 20], + [25, 59, 30], + [25, 53, 40], + [25, 34, 50], + [25, 26, 60], + [25, 25, 70], + [25, 24, 80], + [25, 20, 90], + [25, 23, 100], + [25, 27, 110], + [25, 23, 120], + [25, 30, 130], + [25, 32, 140], + [25, 34, 150], + [25, 36, 160], + [25, 36, 170], + [25, 38, 180], + [25, 30, 190], + [25, 29, 200], + [25, 17, 210], + [25, 20, 220], + [25, 29, 230], + [25, 26, 240], + [25, 32, 250], + [25, 34, 260], + [25, 42, 270], + [25, 45, 280], + [25, 60, 290], + [25, 72, 300], + [25, 85, 310], + [25, 79, 320], + [25, 72, 330], + [25, 63, 340], + [25, 52, 350], + [30, 56, 0], + [30, 56, 10], + [30, 61, 20], + [30, 68, 30], + [30, 66, 40], + [30, 45, 50], + [30, 37, 60], + [30, 36, 70], + [30, 32, 80], + [30, 28, 90], + [30, 30, 100], + [30, 35, 110], + [30, 32, 120], + [30, 40, 130], + [30, 42, 140], + [30, 45, 150], + [30, 48, 160], + [30, 47, 170], + [30, 48, 180], + [30, 40, 190], + [30, 37, 200], + [30, 26, 210], + [30, 28, 220], + [30, 36, 230], + [30, 34, 240], + [30, 40, 250], + [30, 41, 260], + [30, 50, 270], + [30, 55, 280], + [30, 69, 290], + [30, 81, 300], + [30, 88, 310], + [30, 84, 320], + [30, 86, 330], + [30, 73, 340], + [30, 65, 350], + [35, 68, 0], + [35, 64, 10], + [35, 69, 20], + [35, 75, 30], + [35, 79, 40], + [35, 60, 50], + [35, 48, 60], + [35, 46, 70], + [35, 40, 80], + [35, 36, 90], + [35, 37, 100], + [35, 44, 110], + [35, 41, 120], + [35, 48, 130], + [35, 52, 140], + [35, 57, 150], + [35, 58, 160], + [35, 57, 170], + [35, 57, 180], + [35, 48, 190], + [35, 42, 200], + [35, 34, 210], + [35, 35, 220], + [35, 42, 230], + [35, 41, 240], + [35, 49, 250], + [35, 46, 260], + [35, 55, 270], + [35, 60, 280], + [35, 71, 290], + [35, 79, 300], + [35, 85, 310], + [35, 85, 320], + [35, 89, 330], + [35, 82, 340], + [35, 73, 350], + [40, 77, 0], + [40, 70, 10], + [40, 74, 20], + [40, 82, 30], + [40, 90, 40], + [40, 75, 50], + [40, 59, 60], + [40, 56, 70], + [40, 48, 80], + [40, 44, 90], + [40, 45, 100], + [40, 52, 110], + [40, 49, 120], + [40, 56, 130], + [40, 60, 140], + [40, 68, 150], + [40, 68, 160], + [40, 65, 170], + [40, 64, 180], + [40, 55, 190], + [40, 45, 200], + [40, 43, 210], + [40, 40, 220], + [40, 46, 230], + [40, 47, 240], + [40, 54, 250], + [40, 51, 260], + [40, 60, 270], + [40, 61, 280], + [40, 69, 290], + [40, 72, 300], + [40, 80, 310], + [40, 86, 320], + [40, 89, 330], + [40, 87, 340], + [40, 79, 350], + [45, 79, 0], + [45, 73, 10], + [45, 76, 20], + [45, 84, 30], + [45, 94, 40], + [45, 90, 50], + [45, 70, 60], + [45, 67, 70], + [45, 55, 80], + [45, 53, 90], + [45, 51, 100], + [45, 59, 110], + [45, 57, 120], + [45, 64, 130], + [45, 69, 140], + [45, 75, 150], + [45, 76, 160], + [45, 70, 170], + [45, 69, 180], + [45, 59, 190], + [45, 46, 200], + [45, 49, 210], + [45, 45, 220], + [45, 49, 230], + [45, 49, 240], + [45, 55, 250], + [45, 55, 260], + [45, 60, 270], + [45, 60, 280], + [45, 65, 290], + [45, 64, 300], + [45, 71, 310], + [45, 82, 320], + [45, 86, 330], + [45, 87, 340], + [45, 82, 350], + [50, 77, 0], + [50, 73, 10], + [50, 76, 20], + [50, 83, 30], + [50, 93, 40], + [50, 100, 50], + [50, 82, 60], + [50, 76, 70], + [50, 64, 80], + [50, 60, 90], + [50, 58, 100], + [50, 66, 110], + [50, 64, 120], + [50, 70, 130], + [50, 76, 140], + [50, 81, 150], + [50, 82, 160], + [50, 75, 170], + [50, 71, 180], + [50, 62, 190], + [50, 46, 200], + [50, 51, 210], + [50, 48, 220], + [50, 51, 230], + [50, 50, 240], + [50, 55, 250], + [50, 56, 260], + [50, 57, 270], + [50, 57, 280], + [50, 58, 290], + [50, 57, 300], + [50, 62, 310], + [50, 74, 320], + [50, 80, 330], + [50, 83, 340], + [50, 84, 350], + [55, 72, 0], + [55, 71, 10], + [55, 74, 20], + [55, 80, 30], + [55, 88, 40], + [55, 102, 50], + [55, 93, 60], + [55, 85, 70], + [55, 72, 80], + [55, 68, 90], + [55, 65, 100], + [55, 74, 110], + [55, 71, 120], + [55, 77, 130], + [55, 82, 140], + [55, 84, 150], + [55, 85, 160], + [55, 76, 170], + [55, 72, 180], + [55, 62, 190], + [55, 45, 200], + [55, 54, 210], + [55, 51, 220], + [55, 52, 230], + [55, 50, 240], + [55, 52, 250], + [55, 51, 260], + [55, 50, 270], + [55, 53, 280], + [55, 50, 290], + [55, 50, 300], + [55, 55, 310], + [55, 66, 320], + [55, 72, 330], + [55, 78, 340], + [55, 79, 350], + [60, 65, 0], + [60, 65, 10], + [60, 68, 20], + [60, 75, 30], + [60, 82, 40], + [60, 99, 50], + [60, 103, 60], + [60, 94, 70], + [60, 82, 80], + [60, 75, 90], + [60, 72, 100], + [60, 82, 110], + [60, 78, 120], + [60, 82, 130], + [60, 87, 140], + [60, 84, 150], + [60, 83, 160], + [60, 75, 170], + [60, 69, 180], + [60, 60, 190], + [60, 43, 200], + [60, 50, 210], + [60, 49, 220], + [60, 50, 230], + [60, 47, 240], + [60, 48, 250], + [60, 46, 260], + [60, 45, 270], + [60, 46, 280], + [60, 43, 290], + [60, 42, 300], + [60, 47, 310], + [60, 57, 320], + [60, 63, 330], + [60, 71, 340], + [60, 73, 350], + [65, 57, 0], + [65, 57, 10], + [65, 61, 20], + [65, 67, 30], + [65, 72, 40], + [65, 88, 50], + [65, 106, 60], + [65, 102, 70], + [65, 94, 80], + [65, 83, 90], + [65, 80, 100], + [65, 87, 110], + [65, 84, 120], + [65, 85, 130], + [65, 89, 140], + [65, 83, 150], + [65, 78, 160], + [65, 71, 170], + [65, 64, 180], + [65, 55, 190], + [65, 39, 200], + [65, 46, 210], + [65, 45, 220], + [65, 45, 230], + [65, 42, 240], + [65, 43, 250], + [65, 40, 260], + [65, 39, 270], + [65, 40, 280], + [65, 36, 290], + [65, 35, 300], + [65, 41, 310], + [65, 48, 320], + [65, 54, 330], + [65, 62, 340], + [65, 63, 350], + [70, 50, 0], + [70, 48, 10], + [70, 51, 20], + [70, 56, 30], + [70, 60, 40], + [70, 75, 50], + [70, 98, 60], + [70, 108, 70], + [70, 105, 80], + [70, 90, 90], + [70, 86, 100], + [70, 92, 110], + [70, 90, 120], + [70, 88, 130], + [70, 90, 140], + [70, 80, 150], + [70, 69, 160], + [70, 65, 170], + [70, 60, 180], + [70, 49, 190], + [70, 35, 200], + [70, 40, 210], + [70, 38, 220], + [70, 39, 230], + [70, 36, 240], + [70, 36, 250], + [70, 33, 260], + [70, 33, 270], + [70, 34, 280], + [70, 29, 290], + [70, 30, 300], + [70, 34, 310], + [70, 40, 320], + [70, 45, 330], + [70, 51, 340], + [70, 53, 350], + [75, 40, 0], + [75, 39, 10], + [75, 40, 20], + [75, 45, 30], + [75, 47, 40], + [75, 59, 50], + [75, 85, 60], + [75, 103, 70], + [75, 115, 80], + [75, 98, 90], + [75, 94, 100], + [75, 95, 110], + [75, 94, 120], + [75, 89, 130], + [75, 83, 140], + [75, 72, 150], + [75, 59, 160], + [75, 57, 170], + [75, 51, 180], + [75, 41, 190], + [75, 30, 200], + [75, 32, 210], + [75, 32, 220], + [75, 32, 230], + [75, 29, 240], + [75, 29, 250], + [75, 27, 260], + [75, 26, 270], + [75, 25, 280], + [75, 24, 290], + [75, 24, 300], + [75, 27, 310], + [75, 31, 320], + [75, 36, 330], + [75, 40, 340], + [75, 40, 350], + [80, 30, 0], + [80, 30, 10], + [80, 30, 20], + [80, 33, 30], + [80, 35, 40], + [80, 45, 50], + [80, 66, 60], + [80, 82, 70], + [80, 115, 80], + [80, 106, 90], + [80, 100, 100], + [80, 100, 110], + [80, 95, 120], + [80, 84, 130], + [80, 71, 140], + [80, 58, 150], + [80, 49, 160], + [80, 45, 170], + [80, 41, 180], + [80, 32, 190], + [80, 22, 200], + [80, 24, 210], + [80, 23, 220], + [80, 24, 230], + [80, 21, 240], + [80, 21, 250], + [80, 20, 260], + [80, 20, 270], + [80, 18, 280], + [80, 18, 290], + [80, 17, 300], + [80, 20, 310], + [80, 24, 320], + [80, 27, 330], + [80, 28, 340], + [80, 30, 350], + [85, 19, 0], + [85, 18, 10], + [85, 19, 20], + [85, 21, 30], + [85, 22, 40], + [85, 30, 50], + [85, 45, 60], + [85, 58, 70], + [85, 83, 80], + [85, 111, 90], + [85, 106, 100], + [85, 96, 110], + [85, 83, 120], + [85, 64, 130], + [85, 54, 140], + [85, 44, 150], + [85, 34, 160], + [85, 30, 170], + [85, 29, 180], + [85, 23, 190], + [85, 14, 200], + [85, 14, 210], + [85, 15, 220], + [85, 15, 230], + [85, 12, 240], + [85, 13, 250], + [85, 13, 260], + [85, 13, 270], + [85, 11, 280], + [85, 12, 290], + [85, 12, 300], + [85, 14, 310], + [85, 16, 320], + [85, 18, 330], + [85, 18, 340], + [85, 17, 350], + [90, 8, 0], + [90, 7, 10], + [90, 9, 20], + [90, 10, 30], + [90, 10, 40], + [90, 15, 50], + [90, 23, 60], + [90, 34, 70], + [90, 48, 80], + [90, 90, 90], + [90, 108, 100], + [90, 84, 110], + [90, 50, 120], + [90, 35, 130], + [90, 30, 140], + [90, 20, 150], + [90, 15, 160], + [90, 15, 170], + [90, 16, 180], + [90, 13, 190], + [90, 7, 200], + [90, 4, 210], + [90, 6, 220], + [90, 7, 230], + [90, 4, 240], + [90, 4, 250], + [90, 6, 260], + [90, 6, 270], + [90, 4, 280], + [90, 5, 290], + [90, 5, 300], + [90, 6, 310], + [90, 8, 320], + [90, 9, 330], + [90, 4, 340], + [90, 6, 350], + ] +) """ *CIE L\\*C\\*Hab* colourspace array of *Pointer's Gamut* volume. References ---------- :cite:`Pointer1980a` - -DATA_POINTER_GAMUT_VOLUME : ndarray """ -CCS_POINTER_GAMUT_BOUNDARY = np.array([ - [0.659, 0.316], - [0.634, 0.351], - [0.594, 0.391], - [0.557, 0.427], - [0.523, 0.462], - [0.482, 0.491], - [0.444, 0.515], - [0.409, 0.546], - [0.371, 0.558], - [0.332, 0.573], - [0.288, 0.584], - [0.242, 0.576], - [0.202, 0.530], - [0.177, 0.454], - [0.151, 0.389], - [0.151, 0.330], - [0.162, 0.295], - [0.157, 0.266], - [0.159, 0.245], - [0.142, 0.214], - [0.141, 0.195], - [0.129, 0.168], - [0.138, 0.141], - [0.145, 0.129], - [0.145, 0.106], - [0.161, 0.094], - [0.188, 0.084], - [0.252, 0.104], - [0.324, 0.127], - [0.393, 0.165], - [0.451, 0.199], - [0.508, 0.226], -]) +CCS_POINTER_GAMUT_BOUNDARY: NDArray = np.array( + [ + [0.659, 0.316], + [0.634, 0.351], + [0.594, 0.391], + [0.557, 0.427], + [0.523, 0.462], + [0.482, 0.491], + [0.444, 0.515], + [0.409, 0.546], + [0.371, 0.558], + [0.332, 0.573], + [0.288, 0.584], + [0.242, 0.576], + [0.202, 0.530], + [0.177, 0.454], + [0.151, 0.389], + [0.151, 0.330], + [0.162, 0.295], + [0.157, 0.266], + [0.159, 0.245], + [0.142, 0.214], + [0.141, 0.195], + [0.129, 0.168], + [0.138, 0.141], + [0.145, 0.129], + [0.145, 0.106], + [0.161, 0.094], + [0.188, 0.084], + [0.252, 0.104], + [0.324, 0.127], + [0.393, 0.165], + [0.451, 0.199], + [0.508, 0.226], + ] +) """ *CIE xy* chromaticity coordinates of *Pointer's Gamut* boundaries. References ---------- :cite:`Pointer1980a` - -CCS_POINTER_GAMUT_BOUNDARY : ndarray """ diff --git a/colour/models/din99.py b/colour/models/din99.py index 26f7079607..02110df8ad 100644 --- a/colour/models/din99.py +++ b/colour/models/din99.py @@ -1,12 +1,14 @@ -# -*- coding: utf-8 -*- """ -DIN99 Colourspace -================= +DIN99 Colourspace and DIN99b, DIN99c, DIN99d Refined Formulas +============================================================= -Defines the *DIN99* colourspace transformations: +Defines the *DIN99* colourspace and *DIN99b*, *DIN99c*, *DIN99d* refined +formulas transformations: - :func:`colour.Lab_to_DIN99` - :func:`colour.DIN99_to_Lab` +- :func:`colour.XYZ_to_DIN99` +- :func:`colour.DIN99_to_XYZ` References ---------- @@ -14,48 +16,100 @@ Standard Practice for Calculation of Color Tolerances and Color Differences from Instrumentally Measured Color Coordinates: Vol. i (pp. 1-10). doi:10.1520/D2244-16 +- :cite:`Cui2002` : Cui, G., Luo, M. R., Rigg, B., Roesler, G., & Witt, K. + (2002). Uniform colour spaces based on the DIN99 colour-difference formula. + Color Research & Application, 27(4), 282-290. doi:10.1002/col.10066 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.algebra import spow -from colour.utilities import from_range_100, tsplit, tstack, to_domain_100 - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.colorimetry import CCS_ILLUMINANTS +from colour.hints import ArrayLike, Floating, Literal, NDArray, Union +from colour.models import Lab_to_XYZ, XYZ_to_Lab +from colour.utilities import ( + CaseInsensitiveMapping, + from_range_100, + tsplit, + tstack, + to_domain_100, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "DIN99_METHODS", + "Lab_to_DIN99", + "DIN99_to_Lab", + "XYZ_to_DIN99", + "DIN99_to_XYZ", +] + +DIN99_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "ASTMD2244-07": np.array( + [105.509, 0.0158, 16.0, 0.7, 1, 9 / 200, 0.0, 9 / 200] + ), + "DIN99": np.array( + [105.509, 0.0158, 16.0, 0.7, 1, 9 / 200, 0.0, 9 / 200] + ), + "DIN99b": np.array([303.67, 0.0039, 26.0, 0.83, 23.0, 0.075, 26.0, 1]), + "DIN99c": np.array([317.65, 0.0037, 0.0, 0.94, 23.0, 0.066, 0.0, 1]), + "DIN99d": np.array([325.22, 0.0036, 50.0, 1.14, 22.5, 0.06, 50.0, 1]), + } +) +""" +*DIN99* colourspace methods, i.e. the coefficients for the *DIN99b*, *DIN99c*, +and *DIN99d* refined formulas according to *Cui et al. (2002)*. -__all__ = ['Lab_to_DIN99', 'DIN99_to_Lab'] +References +---------- +:cite:`ASTMInternational2007`, :cite:`Cui2002` +""" -def Lab_to_DIN99(Lab, k_E=1, k_CH=1): +def Lab_to_DIN99( + Lab: ArrayLike, + k_E: Floating = 1, + k_CH: Floating = 1, + method: Union[ + Literal["ASTMD2244-07", "DIN99", "DIN99b", "DIN99c", "DIN99d"], str + ] = "DIN99", +) -> NDArray: """ - Converts from *CIE L\\*a\\*b\\** colourspace to *DIN99* colourspace. + Convert from *CIE L\\*a\\*b\\** colourspace to *DIN99* colourspace or + one of the *DIN99b*, *DIN99c*, *DIN99d* refined formulas according + to *Cui et al. (2002)*. Parameters ---------- - Lab : array_like + Lab *CIE L\\*a\\*b\\** colourspace array. - k_E : numeric, optional + k_E Parametric factor :math:`K_E` used to compensate for texture and other specimen presentation effects. - k_CH : numeric, optional + k_CH Parametric factor :math:`K_{CH}` used to compensate for texture and other specimen presentation effects. + method + Computation method to choose between the :cite:`ASTMInternational2007` + formula and the refined formulas according to *Cui et al. (2002)*. Returns ------- - ndarray + :class:`numpy.ndarray` *DIN99* colourspace array. Notes ----- - +------------+------------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ @@ -78,7 +132,7 @@ def Lab_to_DIN99(Lab, k_E=1, k_CH=1): References ---------- - :cite:`ASTMInternational2007` + :cite:`ASTMInternational2007`, :cite:`Cui2002` Examples -------- @@ -88,51 +142,66 @@ def Lab_to_DIN99(Lab, k_E=1, k_CH=1): array([ 53.2282198..., 28.4163465..., 3.8983955...]) """ + c_1, c_2, c_3, c_4, c_5, c_6, c_7, c_8 = DIN99_METHODS[ + validate_method(str(method), DIN99_METHODS) + ] + L, a, b = tsplit(to_domain_100(Lab)) - cos_16 = np.cos(np.radians(16)) - sin_16 = np.sin(np.radians(16)) + cos_c = np.cos(np.radians(c_3)) + sin_c = np.sin(np.radians(c_3)) - e = cos_16 * a + sin_16 * b - f = 0.7 * (-sin_16 * a + cos_16 * b) - G = spow(e ** 2 + f ** 2, 0.5) - h_ef = np.arctan2(f, e) + e = cos_c * a + sin_c * b + f = c_4 * (-sin_c * a + cos_c * b) + G = spow(e**2 + f**2, 0.5) + h_ef = np.arctan2(f, e) + np.radians(c_7) - C_99 = (np.log(1 + 0.045 * G)) / (0.045 * k_CH * k_E) + C_99 = c_5 * (np.log1p(c_6 * G)) / (c_8 * k_CH * k_E) # Hue angle is unused currently. # h_99 = np.degrees(h_ef) a_99 = C_99 * np.cos(h_ef) b_99 = C_99 * np.sin(h_ef) - L_99 = 105.509 * (np.log(1 + 0.0158 * L)) * k_E + L_99 = c_1 * (np.log1p(c_2 * L)) * k_E Lab_99 = tstack([L_99, a_99, b_99]) return from_range_100(Lab_99) -def DIN99_to_Lab(Lab_99, k_E=1, k_CH=1): +def DIN99_to_Lab( + Lab_99: ArrayLike, + k_E: Floating = 1, + k_CH: Floating = 1, + method: Union[ + Literal["ASTMD2244-07", "DIN99", "DIN99b", "DIN99c", "DIN99d"], str + ] = "DIN99", +) -> NDArray: """ - Converts from *DIN99* colourspace to *CIE L\\*a\\*b\\** colourspace. + Convert from *DIN99* colourspace or one of the *DIN99b*, *DIN99c*, + *DIN99d* refined formulas according to *Cui et al. (2002)* to + *CIE L\\*a\\*b\\** colourspace. Parameters ---------- - Lab_99 : array_like + Lab_99 *DIN99* colourspace array. - k_E : numeric, optional + k_E Parametric factor :math:`K_E` used to compensate for texture and other specimen presentation effects. - k_CH : numeric, optional + k_CH Parametric factor :math:`K_{CH}` used to compensate for texture and other specimen presentation effects. + method + Computation method to choose between the :cite:`ASTMInternational2007` + formula and the refined formulas according to *Cui et al. (2002)*. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE L\\*a\\*b\\** colourspace array. Notes ----- - +------------+------------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ @@ -155,7 +224,7 @@ def DIN99_to_Lab(Lab_99, k_E=1, k_CH=1): References ---------- - :cite:`ASTMInternational2007` + :cite:`ASTMInternational2007`, :cite:`Cui2002` Examples -------- @@ -165,23 +234,177 @@ def DIN99_to_Lab(Lab_99, k_E=1, k_CH=1): array([ 41.5278752..., 52.6385830..., 26.9231792...]) """ + c_1, c_2, c_3, c_4, c_5, c_6, c_7, c_8 = DIN99_METHODS[ + validate_method(str(method), DIN99_METHODS) + ] + L_99, a_99, b_99 = tsplit(to_domain_100(Lab_99)) - cos_16 = np.cos(np.radians(16)) - sin_16 = np.sin(np.radians(16)) + cos = np.cos(np.radians(c_3)) + sin = np.sin(np.radians(c_3)) - h_99 = np.arctan2(b_99, a_99) + h_99 = np.arctan2(b_99, a_99) - np.radians(c_7) - C_99 = np.sqrt(a_99 ** 2 + b_99 ** 2) - G = (np.exp(0.045 * C_99 * k_CH * k_E) - 1) / 0.045 + C_99 = np.sqrt(a_99**2 + b_99**2) + G = np.expm1((c_8 / c_5) * (C_99) * k_CH * k_E) / c_6 e = G * np.cos(h_99) f = G * np.sin(h_99) - a = e * cos_16 - (f / 0.7) * sin_16 - b = e * sin_16 + (f / 0.7) * cos_16 - L = (np.exp(L_99 * k_E / 105.509) - 1) / 0.0158 + a = e * cos - (f / c_4) * sin + b = e * sin + (f / c_4) * cos + L = np.expm1(L_99 * k_E / c_1) / c_2 Lab = tstack([L, a, b]) return from_range_100(Lab) + + +def XYZ_to_DIN99( + XYZ: ArrayLike, + illuminant: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], + k_E: Floating = 1, + k_CH: Floating = 1, + method: Union[ + Literal["ASTMD2244-07", "DIN99", "DIN99b", "DIN99c", "DIN99d"], str + ] = "DIN99", +) -> NDArray: + """ + Convert from *CIE XYZ* tristimulus values to *DIN99* colourspace or + one of the *DIN99b*, *DIN99c*, *DIN99d* refined formulas according + to *Cui et al. (2002)*. + + Parameters + ---------- + XYZ + *CIE XYZ* tristimulus values. + illuminant + Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* + colourspace array. + k_E + Parametric factor :math:`K_E` used to compensate for texture and other + specimen presentation effects. + k_CH + Parametric factor :math:`K_{CH}` used to compensate for texture and + other specimen presentation effects. + method + Computation method to choose between the :cite:`ASTMInternational2007` + formula and the refined formulas according to *Cui et al. (2002)*. + + Returns + ------- + :class:`numpy.ndarray` + *DIN99* colourspace array. + + Notes + ----- + +----------------+-----------------------+-----------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +================+=======================+=================+ + | ``XYZ`` | [0, 1] | [0, 1] | + +----------------+-----------------------+-----------------+ + | ``illuminant`` | [0, 1] | [0, 1] | + +----------------+-----------------------+-----------------+ + + +------------+------------------------+--------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+========================+====================+ + | ``Lab_99`` | ``L_99`` : [0, 100] | ``L_99`` : [0, 1] | + | | | | + | | ``a_99`` : [-100, 100] | ``a_99`` : [-1, 1] | + | | | | + | | ``b_99`` : [-100, 100] | ``b_99`` : [-1, 1] | + +------------+------------------------+--------------------+ + + References + ---------- + :cite:`ASTMInternational2007` + + Examples + -------- + >>> import numpy as np + >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + >>> XYZ_to_DIN99(XYZ) # doctest: +ELLIPSIS + array([ 53.2282198..., 28.4163465..., 3.8983955...]) + """ + + Lab = XYZ_to_Lab(XYZ, illuminant) + + return Lab_to_DIN99(Lab, k_E, k_CH, method) + + +def DIN99_to_XYZ( + Lab_99: ArrayLike, + illuminant: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], + k_E: Floating = 1, + k_CH: Floating = 1, + method: Union[ + Literal["ASTMD2244-07", "DIN99", "DIN99b", "DIN99c", "DIN99d"], str + ] = "DIN99", +) -> NDArray: + """ + Convert from *DIN99* colourspace or one of the *DIN99b*, *DIN99c*, + *DIN99d* refined formulas according to *Cui et al. (2002)* to *CIE XYZ* + tristimulus values. + + Parameters + ---------- + Lab_99 + *DIN99* colourspace array. + illuminant + Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* + colourspace array. + k_E + Parametric factor :math:`K_E` used to compensate for texture and other + specimen presentation effects. + k_CH + Parametric factor :math:`K_{CH}` used to compensate for texture and + other specimen presentation effects. + method + Computation method to choose between the :cite:`ASTMInternational2007` + formula and the refined formulas according to *Cui et al. (2002)*. + + Returns + ------- + :class:`numpy.ndarray` + *CIE XYZ* tristimulus values. + + Notes + ----- + +----------------+------------------------+--------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +================+========================+====================+ + | ``Lab_99`` | ``L_99`` : [0, 100] | ``L_99`` : [0, 1] | + | | | | + | | ``a_99`` : [-100, 100] | ``a_99`` : [-1, 1] | + | | | | + | | ``b_99`` : [-100, 100] | ``b_99`` : [-1, 1] | + +----------------+------------------------+--------------------+ + | ``illuminant`` | [0, 1] | [0, 1] | + +----------------+------------------------+--------------------+ + + +----------------+-----------------------+---------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +================+=======================+=====================+ + | ``XYZ`` | [0, 1] | [0, 1] | + +----------------+-----------------------+---------------------+ + + References + ---------- + :cite:`ASTMInternational2007` + + Examples + -------- + >>> import numpy as np + >>> Lab_99 = np.array([53.22821989, 28.41634656, 3.89839552]) + >>> DIN99_to_XYZ(Lab_99) # doctest: +ELLIPSIS + array([ 0.2065400..., 0.1219722..., 0.0513695...]) + """ + + Lab = DIN99_to_Lab(Lab_99, k_E, k_CH, method) + + return Lab_to_XYZ(Lab, illuminant) diff --git a/colour/models/hdr_cie_lab.py b/colour/models/hdr_cie_lab.py index 4ca655ae4d..097e0a7dc4 100644 --- a/colour/models/hdr_cie_lab.py +++ b/colour/models/hdr_cie_lab.py @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- """ -hdr-CIELAB Colourspace +Hdr-CIELAB Colourspace ====================== Defines the *hdr-CIELAB* colourspace transformations: @@ -22,33 +21,58 @@ System Performance VIII (p. 78670O). doi:10.1117/12.872075 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import ( - CCS_ILLUMINANTS, lightness_Fairchild2010, lightness_Fairchild2011, - luminance_Fairchild2010, luminance_Fairchild2011) + CCS_ILLUMINANTS, + lightness_Fairchild2010, + lightness_Fairchild2011, + luminance_Fairchild2010, + luminance_Fairchild2011, +) +from colour.hints import ( + ArrayLike, + FloatingOrArrayLike, + FloatingOrNDArray, + Literal, + NDArray, + Tuple, + Union, +) from colour.models import xy_to_xyY, xyY_to_XYZ -from colour.utilities import (as_float_array, domain_range_scale, from_range_1, - from_range_100, to_domain_1, to_domain_100, - tsplit, tstack) -from colour.utilities.documentation import (DocstringTuple, - is_documentation_building) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.utilities import ( + as_float_array, + domain_range_scale, + from_range_1, + from_range_100, + to_domain_1, + to_domain_100, + tsplit, + tstack, + validate_method, +) +from colour.utilities.documentation import ( + DocstringTuple, + is_documentation_building, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'HDR_CIELAB_METHODS', 'exponent_hdr_CIELab', 'XYZ_to_hdr_CIELab', - 'hdr_CIELab_to_XYZ' + "HDR_CIELAB_METHODS", + "exponent_hdr_CIELab", + "XYZ_to_hdr_CIELab", + "hdr_CIELab_to_XYZ", ] -HDR_CIELAB_METHODS = ('Fairchild 2010', 'Fairchild 2011') +HDR_CIELAB_METHODS: Tuple = ("Fairchild 2010", "Fairchild 2011") if is_documentation_building(): # pragma: no cover HDR_CIELAB_METHODS = DocstringTuple(HDR_CIELAB_METHODS) HDR_CIELAB_METHODS.__doc__ = """ @@ -57,36 +81,37 @@ References ---------- :cite:`Fairchild2010`, :cite:`Fairchild2011` - -HDR_CIELAB_METHODS : tuple - **{'Fairchild 2011', 'Fairchild 2010'}** """ -def exponent_hdr_CIELab(Y_s, Y_abs, method='Fairchild 2011'): +def exponent_hdr_CIELab( + Y_s: FloatingOrArrayLike, + Y_abs: FloatingOrArrayLike, + method: Union[ + Literal["Fairchild 2011", "Fairchild 2010"], str + ] = "Fairchild 2011", +) -> FloatingOrNDArray: """ - Computes *hdr-CIELAB* colourspace *Lightness* :math:`\\epsilon` exponent + Compute *hdr-CIELAB* colourspace *Lightness* :math:`\\epsilon` exponent using *Fairchild and Wyble (2010)* or *Fairchild and Chen (2011)* method. Parameters ---------- - Y_s : numeric or array_like + Y_s Relative luminance :math:`Y_s` of the surround. - Y_abs : numeric or array_like + Y_abs Absolute luminance :math:`Y_{abs}` of the scene diffuse white in :math:`cd/m^2`. - method : unicode, optional - **{'Fairchild 2011', 'Fairchild 2010'}**, + method Computation method. Returns ------- - array_like + :class:`numpy.floating` or :class:`numpy.ndarray` *hdr-CIELAB* colourspace *Lightness* :math:`\\epsilon` exponent. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -104,21 +129,16 @@ def exponent_hdr_CIELab(Y_s, Y_abs, method='Fairchild 2011'): Y_s = to_domain_1(Y_s) Y_abs = as_float_array(Y_abs) + method = validate_method(method, HDR_CIELAB_METHODS) - method_l = method.lower() - assert method.lower() in [ - m.lower() for m in HDR_CIELAB_METHODS - ], ('"{0}" method is invalid, must be one of {1}!'.format( - method, HDR_CIELAB_METHODS)) - - if method_l == 'fairchild 2010': + if method == "fairchild 2010": epsilon = 1.50 else: epsilon = 0.58 sf = 1.25 - 0.25 * (Y_s / 0.184) lf = np.log(318) / np.log(Y_abs) - if method_l == 'fairchild 2010': + if method == "fairchild 2010": epsilon *= sf * lf else: epsilon /= sf * lf @@ -126,39 +146,42 @@ def exponent_hdr_CIELab(Y_s, Y_abs, method='Fairchild 2011'): return epsilon -def XYZ_to_hdr_CIELab(XYZ, - illuminant=CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer']['D65'], - Y_s=0.2, - Y_abs=100, - method='Fairchild 2011'): +def XYZ_to_hdr_CIELab( + XYZ: ArrayLike, + illuminant: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], + Y_s: FloatingOrArrayLike = 0.2, + Y_abs: FloatingOrArrayLike = 100, + method: Union[ + Literal["Fairchild 2011", "Fairchild 2010"], str + ] = "Fairchild 2011", +) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to *hdr-CIELAB* colourspace. + Convert from *CIE XYZ* tristimulus values to *hdr-CIELAB* colourspace. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. - illuminant : array_like, optional + illuminant Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array. - Y_s : numeric or array_like + Y_s Relative luminance :math:`Y_s` of the surround. - Y_abs : numeric or array_like + Y_abs Absolute luminance :math:`Y_{abs}` of the scene diffuse white in :math:`cd/m^2`. - method : unicode, optional - **{'Fairchild 2011', 'Fairchild 2010'}**, + method Computation method. Returns ------- - ndarray + :class:`numpy.ndarray` *hdr-CIELAB* colourspace array. Notes ----- - +----------------+-------------------------+---------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +================+=========================+=====================+ @@ -201,16 +224,11 @@ def XYZ_to_hdr_CIELab(XYZ, """ X, Y, Z = tsplit(to_domain_1(XYZ)) + method = validate_method(method, HDR_CIELAB_METHODS) X_n, Y_n, Z_n = tsplit(xyY_to_XYZ(xy_to_xyY(illuminant))) - method_l = method.lower() - assert method.lower() in [ - m.lower() for m in HDR_CIELAB_METHODS - ], ('"{0}" method is invalid, must be one of {1}!'.format( - method, HDR_CIELAB_METHODS)) - - if method_l == 'fairchild 2010': + if method == "fairchild 2010": lightness_callable = lightness_Fairchild2010 else: lightness_callable = lightness_Fairchild2011 @@ -218,7 +236,7 @@ def XYZ_to_hdr_CIELab(XYZ, e = exponent_hdr_CIELab(Y_s, Y_abs, method) # Domain and range scaling has already be handled. - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): L_hdr = lightness_callable(Y / Y_n, e) a_hdr = 5 * (lightness_callable(X / X_n, e) - L_hdr) b_hdr = 2 * (L_hdr - lightness_callable(Z / Z_n, e)) @@ -228,39 +246,42 @@ def XYZ_to_hdr_CIELab(XYZ, return from_range_100(Lab_hdr) -def hdr_CIELab_to_XYZ(Lab_hdr, - illuminant=CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer']['D65'], - Y_s=0.2, - Y_abs=100, - method='Fairchild 2011'): +def hdr_CIELab_to_XYZ( + Lab_hdr: ArrayLike, + illuminant: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], + Y_s: FloatingOrArrayLike = 0.2, + Y_abs: FloatingOrArrayLike = 100, + method: Union[ + Literal["Fairchild 2011", "Fairchild 2010"], str + ] = "Fairchild 2011", +) -> NDArray: """ - Converts from *hdr-CIELAB* colourspace to *CIE XYZ* tristimulus values. + Convert from *hdr-CIELAB* colourspace to *CIE XYZ* tristimulus values. Parameters ---------- - Lab_hdr : array_like + Lab_hdr *hdr-CIELAB* colourspace array. - illuminant : array_like, optional + illuminant Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array. - Y_s : numeric or array_like + Y_s Relative luminance :math:`Y_s` of the surround. - Y_abs : numeric or array_like + Y_abs Absolute luminance :math:`Y_{abs}` of the scene diffuse white in :math:`cd/m^2`. - method : unicode, optional - **{'Fairchild 2011', 'Fairchild 2010'}**, + method Computation method. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Notes ----- - +----------------+-------------------------+---------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +================+=========================+=====================+ @@ -297,24 +318,19 @@ def hdr_CIELab_to_XYZ(Lab_hdr, """ L_hdr, a_hdr, b_hdr = tsplit(to_domain_100(Lab_hdr)) + method = validate_method(method, HDR_CIELAB_METHODS) X_n, Y_n, Z_n = tsplit(xyY_to_XYZ(xy_to_xyY(illuminant))) - method_l = method.lower() - assert method.lower() in [ - m.lower() for m in HDR_CIELAB_METHODS - ], ('"{0}" method is invalid, must be one of {1}!'.format( - method, HDR_CIELAB_METHODS)) - - if method_l == 'fairchild 2010': + if method == "fairchild 2010": luminance_callable = luminance_Fairchild2010 else: luminance_callable = luminance_Fairchild2011 e = exponent_hdr_CIELab(Y_s, Y_abs, method) - # Domain and range scaling has already be handled. - with domain_range_scale('ignore'): + # Domain and range scaling has already been handled. + with domain_range_scale("ignore"): Y = luminance_callable(L_hdr, e) * Y_n X = luminance_callable((a_hdr + 5 * L_hdr) / 5, e) * X_n Z = luminance_callable((-b_hdr + 2 * L_hdr) / 2, e) * Z_n diff --git a/colour/models/hdr_ipt.py b/colour/models/hdr_ipt.py index 27c1b62138..909c035054 100644 --- a/colour/models/hdr_ipt.py +++ b/colour/models/hdr_ipt.py @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- """ -hdr-IPT Colourspace +Hdr-IPT Colourspace =================== Defines the *hdr-IPT* colourspace transformations: @@ -22,33 +21,61 @@ System Performance VIII (p. 78670O). doi:10.1117/12.872075 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +from colour.algebra import vector_dot from colour.colorimetry import ( - lightness_Fairchild2010, lightness_Fairchild2011, luminance_Fairchild2010, - luminance_Fairchild2011) -from colour.models.ipt import (MATRIX_IPT_XYZ_TO_LMS, MATRIX_IPT_LMS_TO_XYZ, - MATRIX_IPT_LMS_TO_IPT, MATRIX_IPT_IPT_TO_LMS) -from colour.utilities import (as_float_array, domain_range_scale, from_range_1, - from_range_100, to_domain_1, to_domain_100, - vector_dot) -from colour.utilities.documentation import (DocstringTuple, - is_documentation_building) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + lightness_Fairchild2010, + lightness_Fairchild2011, + luminance_Fairchild2010, + luminance_Fairchild2011, +) +from colour.hints import ( + ArrayLike, + FloatingOrArrayLike, + FloatingOrNDArray, + Literal, + NDArray, + Tuple, + Union, +) +from colour.models.ipt import ( + MATRIX_IPT_XYZ_TO_LMS, + MATRIX_IPT_LMS_TO_XYZ, + MATRIX_IPT_LMS_P_TO_IPT, + MATRIX_IPT_IPT_TO_LMS_P, +) +from colour.utilities import ( + as_float_array, + domain_range_scale, + from_range_1, + from_range_100, + to_domain_1, + to_domain_100, + validate_method, +) +from colour.utilities.documentation import ( + DocstringTuple, + is_documentation_building, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'HDR_IPT_METHODS', 'exponent_hdr_IPT', 'XYZ_to_hdr_IPT', 'hdr_IPT_to_XYZ' + "HDR_IPT_METHODS", + "exponent_hdr_IPT", + "XYZ_to_hdr_IPT", + "hdr_IPT_to_XYZ", ] -HDR_IPT_METHODS = ('Fairchild 2010', 'Fairchild 2011') +HDR_IPT_METHODS: Tuple = ("Fairchild 2010", "Fairchild 2011") if is_documentation_building(): # pragma: no cover HDR_IPT_METHODS = DocstringTuple(HDR_IPT_METHODS) HDR_IPT_METHODS.__doc__ = """ @@ -57,36 +84,37 @@ References ---------- :cite:`Fairchild2010`, :cite:`Fairchild2011` - -HDR_IPT_METHODS : tuple - **{'Fairchild 2011', 'Fairchild 2010'}** """ -def exponent_hdr_IPT(Y_s, Y_abs, method='Fairchild 2011'): +def exponent_hdr_IPT( + Y_s: FloatingOrArrayLike, + Y_abs: FloatingOrArrayLike, + method: Union[ + Literal["Fairchild 2011", "Fairchild 2010"], str + ] = "Fairchild 2011", +) -> FloatingOrNDArray: """ - Computes *hdr-IPT* colourspace *Lightness* :math:`\\epsilon` exponent using + Compute *hdr-IPT* colourspace *Lightness* :math:`\\epsilon` exponent using *Fairchild and Wyble (2010)* or *Fairchild and Chen (2011)* method. Parameters ---------- - Y_s : numeric or array_like + Y_s Relative luminance :math:`Y_s` of the surround. - Y_abs : numeric or array_like + Y_abs Absolute luminance :math:`Y_{abs}` of the scene diffuse white in :math:`cd/m^2`. - method : unicode, optional - **{'Fairchild 2011', 'Fairchild 2010'}**, + method Computation method. Returns ------- - array_like + :class:`numpy.floating` or :class:`numpy.ndarray` *hdr-IPT* colourspace *Lightness* :math:`\\epsilon` exponent. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -104,21 +132,16 @@ def exponent_hdr_IPT(Y_s, Y_abs, method='Fairchild 2011'): Y_s = to_domain_1(Y_s) Y_abs = as_float_array(Y_abs) + method = validate_method(method, HDR_IPT_METHODS) - method_l = method.lower() - assert method.lower() in [ - m.lower() for m in HDR_IPT_METHODS - ], ('"{0}" method is invalid, must be one of {1}!'.format( - method, HDR_IPT_METHODS)) - - if method_l == 'fairchild 2010': + if method == "fairchild 2010": epsilon = 1.38 else: epsilon = 0.59 lf = np.log(318) / np.log(Y_abs) sf = 1.25 - 0.25 * (Y_s / 0.184) - if method_l == 'fairchild 2010': + if method == "fairchild 2010": epsilon *= sf * lf else: epsilon /= sf * lf @@ -126,31 +149,36 @@ def exponent_hdr_IPT(Y_s, Y_abs, method='Fairchild 2011'): return epsilon -def XYZ_to_hdr_IPT(XYZ, Y_s=0.2, Y_abs=100, method='Fairchild 2011'): +def XYZ_to_hdr_IPT( + XYZ: ArrayLike, + Y_s: FloatingOrArrayLike = 0.2, + Y_abs: FloatingOrArrayLike = 100, + method: Union[ + Literal["Fairchild 2011", "Fairchild 2010"], str + ] = "Fairchild 2011", +) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to *hdr-IPT* colourspace. + Convert from *CIE XYZ* tristimulus values to *hdr-IPT* colourspace. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. - Y_s : numeric or array_like + Y_s Relative luminance :math:`Y_s` of the surround. - Y_abs : numeric or array_like + Y_abs Absolute luminance :math:`Y_{abs}` of the scene diffuse white in :math:`cd/m^2`. - method : unicode, optional - **{'Fairchild 2011', 'Fairchild 2010'}**, + method Computation method. Returns ------- - ndarray + :class:`numpy.ndarray` *hdr-IPT* colourspace array. Notes ----- - +-------------+-------------------------+---------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +=============+=========================+=====================+ @@ -186,56 +214,56 @@ def XYZ_to_hdr_IPT(XYZ, Y_s=0.2, Y_abs=100, method='Fairchild 2011'): """ XYZ = to_domain_1(XYZ) + method = validate_method(method, HDR_IPT_METHODS) - method_l = method.lower() - assert method.lower() in [ - m.lower() for m in HDR_IPT_METHODS - ], ('"{0}" method is invalid, must be one of {1}!'.format( - method, HDR_IPT_METHODS)) - - if method_l == 'fairchild 2010': + if method == "fairchild 2010": lightness_callable = lightness_Fairchild2010 else: lightness_callable = lightness_Fairchild2011 - e = exponent_hdr_IPT(Y_s, Y_abs, method)[..., np.newaxis] + e = as_float_array(exponent_hdr_IPT(Y_s, Y_abs, method))[..., np.newaxis] LMS = vector_dot(MATRIX_IPT_XYZ_TO_LMS, XYZ) - # Domain and range scaling has already be handled. - with domain_range_scale('ignore'): + # Domain and range scaling has already been handled. + with domain_range_scale("ignore"): LMS_prime = np.sign(LMS) * np.abs(lightness_callable(LMS, e)) - IPT_hdr = vector_dot(MATRIX_IPT_LMS_TO_IPT, LMS_prime) + IPT_hdr = vector_dot(MATRIX_IPT_LMS_P_TO_IPT, LMS_prime) return from_range_100(IPT_hdr) -def hdr_IPT_to_XYZ(IPT_hdr, Y_s=0.2, Y_abs=100, method='Fairchild 2011'): +def hdr_IPT_to_XYZ( + IPT_hdr: ArrayLike, + Y_s: FloatingOrArrayLike = 0.2, + Y_abs: FloatingOrArrayLike = 100, + method: Union[ + Literal["Fairchild 2011", "Fairchild 2010"], str + ] = "Fairchild 2011", +) -> NDArray: """ - Converts from *hdr-IPT* colourspace to *CIE XYZ* tristimulus values. + Convert from *hdr-IPT* colourspace to *CIE XYZ* tristimulus values. Parameters ---------- - IPT_hdr : array_like + IPT_hdr *hdr-IPT* colourspace array. - Y_s : numeric or array_like + Y_s Relative luminance :math:`Y_s` of the surround. - Y_abs : numeric or array_like + Y_abs Absolute luminance :math:`Y_{abs}` of the scene diffuse white in :math:`cd/m^2`. - method : unicode, optional - **{'Fairchild 2011', 'Fairchild 2010'}**, + method Computation method. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Notes ----- - +-------------+-------------------------+---------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +=============+=========================+=====================+ @@ -270,24 +298,19 @@ def hdr_IPT_to_XYZ(IPT_hdr, Y_s=0.2, Y_abs=100, method='Fairchild 2011'): """ IPT_hdr = to_domain_100(IPT_hdr) + method = validate_method(method, HDR_IPT_METHODS) - method_l = method.lower() - assert method.lower() in [ - m.lower() for m in HDR_IPT_METHODS - ], ('"{0}" method is invalid, must be one of {1}!'.format( - method, HDR_IPT_METHODS)) - - if method_l == 'fairchild 2010': + if method == "fairchild 2010": luminance_callable = luminance_Fairchild2010 else: luminance_callable = luminance_Fairchild2011 - e = exponent_hdr_IPT(Y_s, Y_abs, method)[..., np.newaxis] + e = as_float_array(exponent_hdr_IPT(Y_s, Y_abs, method))[..., np.newaxis] - LMS = vector_dot(MATRIX_IPT_IPT_TO_LMS, IPT_hdr) + LMS = vector_dot(MATRIX_IPT_IPT_TO_LMS_P, IPT_hdr) # Domain and range scaling has already be handled. - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): LMS_prime = np.sign(LMS) * np.abs(luminance_callable(LMS, e)) XYZ = vector_dot(MATRIX_IPT_LMS_TO_XYZ, LMS_prime) diff --git a/colour/models/hunter_lab.py b/colour/models/hunter_lab.py index 61470b3d24..512d8294e8 100644 --- a/colour/models/hunter_lab.py +++ b/colour/models/hunter_lab.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Hunter L,a,b Colour Scale ========================= @@ -19,39 +18,42 @@ an02_02.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import TVS_ILLUMINANTS_HUNTERLAB +from colour.hints import ArrayLike, NDArray from colour.utilities import from_range_100, to_domain_100, tsplit, tstack -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'XYZ_to_K_ab_HunterLab1966', 'XYZ_to_Hunter_Lab', 'Hunter_Lab_to_XYZ' + "XYZ_to_K_ab_HunterLab1966", + "XYZ_to_Hunter_Lab", + "Hunter_Lab_to_XYZ", ] -def XYZ_to_K_ab_HunterLab1966(XYZ): +def XYZ_to_K_ab_HunterLab1966(XYZ: ArrayLike) -> NDArray: """ - Converts from *whitepoint* *CIE XYZ* tristimulus values to + Convert from *whitepoint* *CIE XYZ* tristimulus values to *Hunter L,a,b* :math:`K_{a}` and :math:`K_{b}` chromaticity coefficients. Parameters ---------- - XYZ : array_like + XYZ *Whitepoint* *CIE XYZ* tristimulus values. Returns ------- - ndarray + :class:`numpy.ndarray` *Hunter L,a,b* :math:`K_{a}` and :math:`K_{b}` chromaticity coefficients. @@ -76,33 +78,36 @@ def XYZ_to_K_ab_HunterLab1966(XYZ): return K_ab -def XYZ_to_Hunter_Lab(XYZ, - XYZ_n=TVS_ILLUMINANTS_HUNTERLAB[ - 'CIE 1931 2 Degree Standard Observer']['D65'].XYZ_n, - K_ab=TVS_ILLUMINANTS_HUNTERLAB[ - 'CIE 1931 2 Degree Standard Observer']['D65'].K_ab): +def XYZ_to_Hunter_Lab( + XYZ: ArrayLike, + XYZ_n: ArrayLike = TVS_ILLUMINANTS_HUNTERLAB[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"].XYZ_n, + K_ab: ArrayLike = TVS_ILLUMINANTS_HUNTERLAB[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"].K_ab, +) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to *Hunter L,a,b* colour scale. + Convert from *CIE XYZ* tristimulus values to *Hunter L,a,b* colour scale. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. - XYZ_n : array_like, optional + XYZ_n Reference *illuminant* tristimulus values. - K_ab : array_like, optional + K_ab Reference *illuminant* chromaticity coefficients, if ``K_ab`` is set to *None* it will be computed using :func:`colour.XYZ_to_K_ab_HunterLab1966`. Returns ------- - ndarray + :class:`numpy.ndarray` *Hunter L,a,b* colour scale array. Notes ----- - +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ @@ -136,8 +141,11 @@ def XYZ_to_Hunter_Lab(XYZ, X, Y, Z = tsplit(to_domain_100(XYZ)) X_n, Y_n, Z_n = tsplit(to_domain_100(XYZ_n)) - K_a, K_b = (tsplit(XYZ_to_K_ab_HunterLab1966(XYZ_n)) - if K_ab is None else tsplit(K_ab)) + K_a, K_b = ( + tsplit(XYZ_to_K_ab_HunterLab1966(XYZ_n)) + if K_ab is None + else tsplit(K_ab) + ) Y_Y_n = Y / Y_n sqrt_Y_Y_n = np.sqrt(Y_Y_n) @@ -151,33 +159,36 @@ def XYZ_to_Hunter_Lab(XYZ, return from_range_100(Lab) -def Hunter_Lab_to_XYZ(Lab, - XYZ_n=TVS_ILLUMINANTS_HUNTERLAB[ - 'CIE 1931 2 Degree Standard Observer']['D65'].XYZ_n, - K_ab=TVS_ILLUMINANTS_HUNTERLAB[ - 'CIE 1931 2 Degree Standard Observer']['D65'].K_ab): +def Hunter_Lab_to_XYZ( + Lab: ArrayLike, + XYZ_n: ArrayLike = TVS_ILLUMINANTS_HUNTERLAB[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"].XYZ_n, + K_ab: ArrayLike = TVS_ILLUMINANTS_HUNTERLAB[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"].K_ab, +) -> NDArray: """ - Converts from *Hunter L,a,b* colour scale to *CIE XYZ* tristimulus values. + Convert from *Hunter L,a,b* colour scale to *CIE XYZ* tristimulus values. Parameters ---------- - Lab : array_like + Lab *Hunter L,a,b* colour scale array. - XYZ_n : array_like, optional + XYZ_n Reference *illuminant* tristimulus values. - K_ab : array_like, optional + K_ab Reference *illuminant* chromaticity coefficients, if ``K_ab`` is set to *None* it will be computed using :func:`colour.XYZ_to_K_ab_HunterLab1966`. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Notes ----- - +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ @@ -211,11 +222,14 @@ def Hunter_Lab_to_XYZ(Lab, L, a, b = tsplit(to_domain_100(Lab)) X_n, Y_n, Z_n = tsplit(to_domain_100(XYZ_n)) - K_a, K_b = (tsplit(XYZ_to_K_ab_HunterLab1966(XYZ_n)) - if K_ab is None else tsplit(K_ab)) + K_a, K_b = ( + tsplit(XYZ_to_K_ab_HunterLab1966(XYZ_n)) + if K_ab is None + else tsplit(K_ab) + ) L_100 = L / 100 - L_100_2 = L_100 ** 2 + L_100_2 = L_100**2 Y = L_100_2 * Y_n X = ((a / K_a) * L_100 + L_100_2) * X_n diff --git a/colour/models/hunter_rdab.py b/colour/models/hunter_rdab.py index bd0d378d61..dad8fcdb4c 100644 --- a/colour/models/hunter_rdab.py +++ b/colour/models/hunter_rdab.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Hunter Rd,a,b Colour Scale ========================== @@ -16,49 +15,56 @@ an-1016-hunter-rd-a-b-color-scale-update-12-07-03.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations from colour.colorimetry import TVS_ILLUMINANTS_HUNTERLAB +from colour.hints import ArrayLike, NDArray from colour.models import XYZ_to_K_ab_HunterLab1966 from colour.utilities import from_range_100, to_domain_100, tsplit, tstack -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['XYZ_to_Hunter_Rdab', 'Hunter_Rdab_to_XYZ'] - - -def XYZ_to_Hunter_Rdab(XYZ, - XYZ_n=TVS_ILLUMINANTS_HUNTERLAB[ - 'CIE 1931 2 Degree Standard Observer']['D65'].XYZ_n, - K_ab=TVS_ILLUMINANTS_HUNTERLAB[ - 'CIE 1931 2 Degree Standard Observer']['D65'].K_ab): +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "XYZ_to_Hunter_Rdab", + "Hunter_Rdab_to_XYZ", +] + + +def XYZ_to_Hunter_Rdab( + XYZ: ArrayLike, + XYZ_n: ArrayLike = TVS_ILLUMINANTS_HUNTERLAB[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"].XYZ_n, + K_ab: ArrayLike = TVS_ILLUMINANTS_HUNTERLAB[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"].K_ab, +) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to *Hunter Rd,a,b* colour scale. + Convert from *CIE XYZ* tristimulus values to *Hunter Rd,a,b* colour scale. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. - XYZ_n : array_like, optional + XYZ_n Reference *illuminant* tristimulus values. - K_ab : array_like, optional + K_ab Reference *illuminant* chromaticity coefficients, if ``K_ab`` is set to *None* it will be computed using :func:`colour.XYZ_to_K_ab_HunterLab1966`. Returns ------- - ndarray + :class:`numpy.ndarray` *Hunter Rd,a,b* colour scale array. Notes ----- - +------------+------------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ @@ -94,8 +100,11 @@ def XYZ_to_Hunter_Rdab(XYZ, X, Y, Z = tsplit(to_domain_100(XYZ)) X_n, Y_n, Z_n = tsplit(to_domain_100(XYZ_n)) - K_a, K_b = (tsplit(XYZ_to_K_ab_HunterLab1966(XYZ_n)) - if K_ab is None else tsplit(K_ab)) + K_a, K_b = ( + tsplit(XYZ_to_K_ab_HunterLab1966(XYZ_n)) + if K_ab is None + else tsplit(K_ab) + ) f = 0.51 * ((21 + 0.2 * Y) / (1 + 0.2 * Y)) Y_Yn = Y / Y_n @@ -109,33 +118,36 @@ def XYZ_to_Hunter_Rdab(XYZ, return from_range_100(R_d_ab) -def Hunter_Rdab_to_XYZ(R_d_ab, - XYZ_n=TVS_ILLUMINANTS_HUNTERLAB[ - 'CIE 1931 2 Degree Standard Observer']['D65'].XYZ_n, - K_ab=TVS_ILLUMINANTS_HUNTERLAB[ - 'CIE 1931 2 Degree Standard Observer']['D65'].K_ab): +def Hunter_Rdab_to_XYZ( + R_d_ab: ArrayLike, + XYZ_n: ArrayLike = TVS_ILLUMINANTS_HUNTERLAB[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"].XYZ_n, + K_ab: ArrayLike = TVS_ILLUMINANTS_HUNTERLAB[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"].K_ab, +) -> NDArray: """ - Converts from *Hunter Rd,a,b* colour scale to *CIE XYZ* tristimulus values. + Convert from *Hunter Rd,a,b* colour scale to *CIE XYZ* tristimulus values. Parameters ---------- - R_d_ab : array_like + R_d_ab *Hunter Rd,a,b* colour scale array. - XYZ_n : array_like, optional + XYZ_n Reference *illuminant* tristimulus values. - K_ab : array_like, optional + K_ab Reference *illuminant* chromaticity coefficients, if ``K_ab`` is set to *None* it will be computed using :func:`colour.XYZ_to_K_ab_HunterLab1966`. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Notes ----- - +------------+------------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ @@ -170,8 +182,11 @@ def Hunter_Rdab_to_XYZ(R_d_ab, R_d, a_Rd, b_Rd = tsplit(to_domain_100(R_d_ab)) X_n, Y_n, Z_n = tsplit(to_domain_100(XYZ_n)) - K_a, K_b = (tsplit(XYZ_to_K_ab_HunterLab1966(XYZ_n)) - if K_ab is None else tsplit(K_ab)) + K_a, K_b = ( + tsplit(XYZ_to_K_ab_HunterLab1966(XYZ_n)) + if K_ab is None + else tsplit(K_ab) + ) f = 0.51 * ((21 + 0.2 * R_d) / (1 + 0.2 * R_d)) Rd_Yn = R_d / Y_n diff --git a/colour/models/icacb.py b/colour/models/icacb.py new file mode 100644 index 0000000000..b1a91b2101 --- /dev/null +++ b/colour/models/icacb.py @@ -0,0 +1,177 @@ +""" +:math:`IC_AC_B` Colourspace +=========================== + +Defines the :math:`IC_AC_B` colourspace transformations: + +- :func:`colour.XYZ_to_ICaCb` +- :func:`colour.ICaCb_to_XYZ` + +References +---------- +- :cite:`Frohlich2017` : Fröhlich, J. (2017). Encoding high dynamic range + and wide color gamut imagery. doi:10.18419/OPUS-9664 +""" + +from __future__ import annotations + +import numpy as np + +from colour.algebra import vector_dot +from colour.hints import ArrayLike, NDArray +from colour.models.rgb.transfer_functions import ( + eotf_ST2084, + eotf_inverse_ST2084, +) +from colour.utilities import ( + domain_range_scale, + from_range_1, + to_domain_1, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "MATRIX_ICACB_XYZ_TO_LMS", + "MATRIX_ICACB_LMS_TO_XYZ", + "MATRIX_ICACB_XYZ_TO_LMS_2", + "MATRIX_ICACB_LMS_TO_XYZ_2", + "XYZ_to_ICaCb", + "ICaCb_to_XYZ", +] + +MATRIX_ICACB_XYZ_TO_LMS: NDArray = np.array( + [ + [0.37613, 0.70431, -0.05675], + [-0.21649, 1.14744, 0.05356], + [0.02567, 0.16713, 0.74235], + ] +) +"""*CIE XYZ* tristimulus values to normalised cone responses matrix.""" + +MATRIX_ICACB_LMS_TO_XYZ: NDArray = np.linalg.inv(MATRIX_ICACB_XYZ_TO_LMS) +"""Normalised cone responses to *CIE XYZ* tristimulus values matrix.""" + +MATRIX_ICACB_XYZ_TO_LMS_2: NDArray = np.array( + [ + [0.4949, 0.5037, 0.0015], + [4.2854, -4.5462, 0.2609], + [0.3605, 1.1499, -1.5105], + ] +) +"""Normalised non-linear cone responses to :math:`IC_AC_B` colourspace matrix.""" + +MATRIX_ICACB_LMS_TO_XYZ_2: NDArray = np.linalg.inv(MATRIX_ICACB_XYZ_TO_LMS_2) +""":math:`IC_AC_B` to normalised non-linear cone responses colourspace matrix.""" + + +def XYZ_to_ICaCb(XYZ: ArrayLike) -> NDArray: + """ + Convert from *CIE XYZ* tristimulus values to :math:`IC_AC_B` colourspace. + + Parameters + ---------- + XYZ + *CIE XYZ* tristimulus values. + + Returns + ------- + :class:`numpy.ndarray` + :math:`IC_AC_B` colourspace array. + + Notes + ----- + +------------+-----------------------+-----------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+=================+ + | ``XYZ`` | [0, 1] | [0, 1] | + +------------+-----------------------+-----------------+ + + +------------+-----------------------+-----------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+=================+ + | ``ICaCb`` | ``I`` : [0, 1] | ``I`` : [0, 1] | + | | | | + | | ``Ca`` : [-1, 1] | ``Ca``: [-1, 1] | + | | | | + | | ``Cb`` : [-1, 1] | ``Cb``: [-1, 1] | + +------------+-----------------------+-----------------+ + + - Input *CIE XYZ* tristimulus values must be adapted to + *CIE Standard Illuminant D Series* *D65*. + + References + ---------- + :cite:`Frohlich2017` + + Examples + -------- + >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + >>> XYZ_to_ICaCb(XYZ) + array([ 0.06875297, 0.05753352, 0.02081548]) + """ + + XYZ = to_domain_1(XYZ) + LMS = vector_dot(MATRIX_ICACB_XYZ_TO_LMS, XYZ) + + with domain_range_scale("ignore"): + LMS_prime = eotf_inverse_ST2084(LMS) + + return from_range_1(vector_dot(MATRIX_ICACB_XYZ_TO_LMS_2, LMS_prime)) + + +def ICaCb_to_XYZ(ICaCb: ArrayLike) -> NDArray: + """ + Convert from :math:`IC_AC_B` tristimulus values to *CIE XYZ* colourspace. + + Parameters + ---------- + ICaCb + :math:`IC_AC_B` tristimulus values. + + Returns + ------- + :class:`numpy.ndarray` + *CIE XYZ* colourspace array. + + Notes + ----- + +------------+-----------------------+-----------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+=================+ + | ``ICaCb`` | ``I`` : [0, 1] | ``I`` : [0, 1] | + | | | | + | | ``Ca`` : [-1, 1] | ``Ca``: [-1, 1] | + | | | | + | | ``Cb`` : [-1, 1] | ``Cb``: [-1, 1] | + +------------+-----------------------+-----------------+ + + +------------+-----------------------+-----------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+=================+ + | ``XYZ`` | [0, 1] | [0, 1] | + +------------+-----------------------+-----------------+ + + References + ---------- + :cite:`Frohlich2017` + + Examples + -------- + >>> XYZ = np.array([0.06875297, 0.05753352, 0.02081548]) + >>> ICaCb_to_XYZ(XYZ) + array([ 0.20654008, 0.12197225, 0.05136951]) + """ + + ICaCb = to_domain_1(ICaCb) + LMS_prime = vector_dot(MATRIX_ICACB_LMS_TO_XYZ_2, ICaCb) + + with domain_range_scale("ignore"): + LMS = eotf_ST2084(LMS_prime) + + return from_range_1(vector_dot(MATRIX_ICACB_LMS_TO_XYZ, LMS)) diff --git a/colour/models/igpgtg.py b/colour/models/igpgtg.py index f0b0301deb..2e9c04af55 100644 --- a/colour/models/igpgtg.py +++ b/colour/models/igpgtg.py @@ -1,12 +1,11 @@ -# -*- coding: utf-8 -*- """ :math:`I_GP_GT_G` Colourspace ============================= Defines the :math:`I_GP_GT_G` colourspace transformations: -- :func:`colour.XYZ_to_IGPGTG` -- :func:`colour.IGPGTG_to_XYZ` +- :func:`colour.XYZ_to_IgPgTg` +- :func:`colour.IgPgTg_to_XYZ` References ---------- @@ -15,81 +14,74 @@ Imaging. doi:10.2352/J.Percept.Imaging.2020.3.2.020401 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.algebra import spow -from colour.utilities import (from_range_1, to_domain_1, vector_dot) +from colour.algebra import spow, vector_dot +from colour.hints import ArrayLike, NDArray +from colour.utilities import from_range_1, to_domain_1 -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'MATRIX_IGPGTG_XYZ_TO_LMS', 'MATRIX_IGPGTG_LMS_TO_XYZ', - 'MATRIX_IGPGTG_LMS_TO_IGPGTG', 'MATRIX_IGPGTG_IGPGTG_TO_LMS', - 'XYZ_to_IGPGTG', 'IGPGTG_to_XYZ' + "MATRIX_IGPGTG_XYZ_TO_LMS", + "MATRIX_IGPGTG_LMS_TO_XYZ", + "MATRIX_IGPGTG_LMS_P_TO_IGPGTG", + "MATRIX_IGPGTG_IGPGTG_TO_LMS_P", + "XYZ_to_IgPgTg", + "IgPgTg_to_XYZ", ] -MATRIX_IGPGTG_XYZ_TO_LMS = np.array([ - [2.968, 2.741, -0.649], - [1.237, 5.969, -0.173], - [-0.318, 0.387, 2.311], -]) -""" -*CIE XYZ* tristimulus values to normalised cone responses matrix. - -MATRIX_IGPGTG_XYZ_TO_LMS : array_like, (3, 3) -""" - -MATRIX_IGPGTG_LMS_TO_XYZ = np.linalg.inv(MATRIX_IGPGTG_XYZ_TO_LMS) -""" -Normalised cone responses to *CIE XYZ* tristimulus values matrix. - -MATRIX_IGPGTG_LMS_TO_XYZ : array_like, (3, 3) -""" - -MATRIX_IGPGTG_LMS_TO_IGPGTG = np.array([ - [0.117, 1.464, 0.130], - [8.285, -8.361, 21.400], - [-1.208, 2.412, -36.530], -]) -""" -Normalised cone responses to :math:`I_GP_GT_G` colourspace matrix. - -MATRIX_IGPGTG_LMS_TO_IGPGTG : array_like, (3, 3) -""" - -MATRIX_IGPGTG_IGPGTG_TO_LMS = np.linalg.inv(MATRIX_IGPGTG_LMS_TO_IGPGTG) -""" -:math:`I_GP_GT_G` colourspace to normalised cone responses matrix. - -MATRIX_IGPGTG_IGPGTG_TO_LMS : array_like, (3, 3) -""" - - -def XYZ_to_IGPGTG(XYZ): +MATRIX_IGPGTG_XYZ_TO_LMS: NDArray = np.array( + [ + [2.968, 2.741, -0.649], + [1.237, 5.969, -0.173], + [-0.318, 0.387, 2.311], + ] +) +"""*CIE XYZ* tristimulus values to normalised cone responses matrix.""" + +MATRIX_IGPGTG_LMS_TO_XYZ: NDArray = np.linalg.inv(MATRIX_IGPGTG_XYZ_TO_LMS) +"""Normalised cone responses to *CIE XYZ* tristimulus values matrix.""" + +MATRIX_IGPGTG_LMS_P_TO_IGPGTG: NDArray = np.array( + [ + [0.117, 1.464, 0.130], + [8.285, -8.361, 21.400], + [-1.208, 2.412, -36.530], + ] +) +"""Normalised non-linear cone responses to :math:`I_GP_GT_G` colourspace matrix.""" + +MATRIX_IGPGTG_IGPGTG_TO_LMS_P: NDArray = np.linalg.inv( + MATRIX_IGPGTG_LMS_P_TO_IGPGTG +) +""":math:`I_GP_GT_G` colourspace to normalised non-linear cone responses matrix.""" + + +def XYZ_to_IgPgTg(XYZ: ArrayLike) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to :math:`I_GP_GT_G` + Convert from *CIE XYZ* tristimulus values to :math:`I_GP_GT_G` colourspace. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. Returns ------- - ndarray + :class:`numpy.ndarray` :math:`I_GP_GT_G` colourspace array. Notes ----- - +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ @@ -99,7 +91,7 @@ def XYZ_to_IGPGTG(XYZ): +------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ - | ``IGPGTG`` | ``IG`` : [0, 1] | ``IG`` : [0, 1] | + | ``IgPgTg`` | ``IG`` : [0, 1] | ``IG`` : [0, 1] | | | | | | | ``PG`` : [-1, 1] | ``PG`` : [-1, 1]| | | | | @@ -116,7 +108,7 @@ def XYZ_to_IGPGTG(XYZ): Examples -------- >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) - >>> XYZ_to_IGPGTG(XYZ) # doctest: +ELLIPSIS + >>> XYZ_to_IgPgTg(XYZ) # doctest: +ELLIPSIS array([ 0.4242125..., 0.1863249..., 0.1068922...]) """ @@ -124,33 +116,32 @@ def XYZ_to_IGPGTG(XYZ): LMS = vector_dot(MATRIX_IGPGTG_XYZ_TO_LMS, XYZ) LMS_prime = spow(LMS / np.array([18.36, 21.46, 19435]), 0.427) - IGPGTG = vector_dot(MATRIX_IGPGTG_LMS_TO_IGPGTG, LMS_prime) + IgPgTg = vector_dot(MATRIX_IGPGTG_LMS_P_TO_IGPGTG, LMS_prime) - return from_range_1(IGPGTG) + return from_range_1(IgPgTg) -def IGPGTG_to_XYZ(IGPGTG): +def IgPgTg_to_XYZ(IgPgTg: ArrayLike) -> NDArray: """ - Converts from :math:`I_GP_GT_G` colourspace to *CIE XYZ* tristimulus + Convert from :math:`I_GP_GT_G` colourspace to *CIE XYZ* tristimulus values. Parameters ---------- - IGPGTG : array_like + IgPgTg :math:`I_GP_GT_G` colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Notes ----- - +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ - | ``IGPGTG`` | ``IG`` : [0, 1] | ``IG`` : [0, 1] | + | ``IgPgTg`` | ``IG`` : [0, 1] | ``IG`` : [0, 1] | | | | | | | ``PG`` : [-1, 1] | ``PG`` : [-1, 1]| | | | | @@ -169,14 +160,14 @@ def IGPGTG_to_XYZ(IGPGTG): Examples -------- - >>> IGPGTG = np.array([0.42421258, 0.18632491, 0.10689223]) - >>> IGPGTG_to_XYZ(IGPGTG) # doctest: +ELLIPSIS + >>> IgPgTg = np.array([0.42421258, 0.18632491, 0.10689223]) + >>> IgPgTg_to_XYZ(IgPgTg) # doctest: +ELLIPSIS array([ 0.2065400..., 0.1219722..., 0.0513695...]) """ - IGPGTG = to_domain_1(IGPGTG) + IgPgTg = to_domain_1(IgPgTg) - LMS = vector_dot(MATRIX_IGPGTG_IGPGTG_TO_LMS, IGPGTG) + LMS = vector_dot(MATRIX_IGPGTG_IGPGTG_TO_LMS_P, IgPgTg) LMS_prime = spow(LMS, 1 / 0.427) * np.array([18.36, 21.46, 19435]) XYZ = vector_dot(MATRIX_IGPGTG_LMS_TO_XYZ, LMS_prime) diff --git a/colour/models/ipt.py b/colour/models/ipt.py index e5d549f3e5..d34271f148 100644 --- a/colour/models/ipt.py +++ b/colour/models/ipt.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ IPT Colourspace =============== @@ -18,80 +17,78 @@ Color Appearance Models (3rd ed., pp. 6197-6223). Wiley. ISBN:B00DAYO8E2 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.algebra import spow -from colour.utilities import (from_range_1, from_range_degrees, to_domain_1, - vector_dot, tsplit) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.algebra import spow, vector_dot +from colour.hints import ArrayLike, FloatingOrNDArray, NDArray +from colour.utilities import ( + as_float, + from_range_1, + from_range_degrees, + to_domain_1, + tsplit, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'MATRIX_IPT_XYZ_TO_LMS', 'MATRIX_IPT_LMS_TO_XYZ', 'MATRIX_IPT_LMS_TO_IPT', - 'MATRIX_IPT_IPT_TO_LMS', 'XYZ_to_IPT', 'IPT_to_XYZ', 'IPT_hue_angle' + "MATRIX_IPT_XYZ_TO_LMS", + "MATRIX_IPT_LMS_TO_XYZ", + "MATRIX_IPT_LMS_P_TO_IPT", + "MATRIX_IPT_IPT_TO_LMS_P", + "XYZ_to_IPT", + "IPT_to_XYZ", + "IPT_hue_angle", ] -MATRIX_IPT_XYZ_TO_LMS = np.array([ - [0.4002, 0.7075, -0.0807], - [-0.2280, 1.1500, 0.0612], - [0.0000, 0.0000, 0.9184], -]) -""" -*CIE XYZ* tristimulus values to normalised cone responses matrix. - -MATRIX_IPT_XYZ_TO_LMS : array_like, (3, 3) -""" - -MATRIX_IPT_LMS_TO_XYZ = np.linalg.inv(MATRIX_IPT_XYZ_TO_LMS) -""" -Normalised cone responses to *CIE XYZ* tristimulus values matrix. - -MATRIX_IPT_LMS_TO_XYZ : array_like, (3, 3) -""" - -MATRIX_IPT_LMS_TO_IPT = np.array([ - [0.4000, 0.4000, 0.2000], - [4.4550, -4.8510, 0.3960], - [0.8056, 0.3572, -1.1628], -]) -""" -Normalised cone responses to *IPT* colourspace matrix. +MATRIX_IPT_XYZ_TO_LMS: NDArray = np.array( + [ + [0.4002, 0.7075, -0.0807], + [-0.2280, 1.1500, 0.0612], + [0.0000, 0.0000, 0.9184], + ] +) +"""*CIE XYZ* tristimulus values to normalised cone responses matrix.""" -MATRIX_IPT_LMS_TO_IPT : array_like, (3, 3) -""" +MATRIX_IPT_LMS_TO_XYZ: NDArray = np.linalg.inv(MATRIX_IPT_XYZ_TO_LMS) +"""Normalised cone responses to *CIE XYZ* tristimulus values matrix.""" -MATRIX_IPT_IPT_TO_LMS = np.linalg.inv(MATRIX_IPT_LMS_TO_IPT) -""" -*IPT* colourspace to normalised cone responses matrix. +MATRIX_IPT_LMS_P_TO_IPT: NDArray = np.array( + [ + [0.4000, 0.4000, 0.2000], + [4.4550, -4.8510, 0.3960], + [0.8056, 0.3572, -1.1628], + ] +) +"""Normalised non-linear cone responses to *IPT* colourspace matrix.""" -MATRIX_IPT_IPT_TO_LMS : array_like, (3, 3) -""" +MATRIX_IPT_IPT_TO_LMS_P: NDArray = np.linalg.inv(MATRIX_IPT_LMS_P_TO_IPT) +"""*IPT* colourspace to normalised non-linear cone responses matrix.""" -def XYZ_to_IPT(XYZ): +def XYZ_to_IPT(XYZ: ArrayLike) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to *IPT* colourspace. + Convert from *CIE XYZ* tristimulus values to *IPT* colourspace. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. Returns ------- - ndarray + :class:`numpy.ndarray` *IPT* colourspace array. Notes ----- - +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ @@ -126,28 +123,27 @@ def XYZ_to_IPT(XYZ): LMS = vector_dot(MATRIX_IPT_XYZ_TO_LMS, XYZ) LMS_prime = spow(LMS, 0.43) - IPT = vector_dot(MATRIX_IPT_LMS_TO_IPT, LMS_prime) + IPT = vector_dot(MATRIX_IPT_LMS_P_TO_IPT, LMS_prime) return from_range_1(IPT) -def IPT_to_XYZ(IPT): +def IPT_to_XYZ(IPT: ArrayLike) -> NDArray: """ - Converts from *IPT* colourspace to *CIE XYZ* tristimulus values. + Convert from *IPT* colourspace to *CIE XYZ* tristimulus values. Parameters ---------- - IPT : array_like + IPT *IPT* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Notes ----- - +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ @@ -177,30 +173,29 @@ def IPT_to_XYZ(IPT): IPT = to_domain_1(IPT) - LMS = vector_dot(MATRIX_IPT_IPT_TO_LMS, IPT) + LMS = vector_dot(MATRIX_IPT_IPT_TO_LMS_P, IPT) LMS_prime = spow(LMS, 1 / 0.43) XYZ = vector_dot(MATRIX_IPT_LMS_TO_XYZ, LMS_prime) return from_range_1(XYZ) -def IPT_hue_angle(IPT): +def IPT_hue_angle(IPT: ArrayLike) -> FloatingOrNDArray: """ - Computes the hue angle in degrees from *IPT* colourspace. + Compute the hue angle in degrees from *IPT* colourspace. Parameters ---------- - IPT : array_like + IPT *IPT* colourspace array. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Hue angle in degrees. Notes ----- - +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ @@ -232,4 +227,4 @@ def IPT_hue_angle(IPT): hue = np.degrees(np.arctan2(T, P)) % 360 - return from_range_degrees(hue) + return as_float(from_range_degrees(hue)) diff --git a/colour/models/jzazbz.py b/colour/models/jzazbz.py index 2545a05815..326f8931ed 100644 --- a/colour/models/jzazbz.py +++ b/colour/models/jzazbz.py @@ -1,12 +1,14 @@ -# -*- coding: utf-8 -*- """ -:math:`J_zA_zB_z` Colourspace +:math:`J_za_zb_z` Colourspace ============================= -Defines the :math:`J_zA_zB_z` colourspace: +Defines the :math:`J_za_zb_z` colourspace: -- :func:`colour.XYZ_to_JzAzBz` -- :func:`colour.JzAzBz_to_XYZ` +- :func:`colour.models.IZAZBZ_METHODS` +- :func:`colour.models.XYZ_to_Izazbz` +- :func:`colour.models.Izazbz_to_XYZ` +- :func:`colour.XYZ_to_Jzazbz` +- :func:`colour.Jzazbz_to_XYZ` References ---------- @@ -14,106 +16,191 @@ Perceptually uniform color space for image signals including high dynamic range and wide gamut. Optics Express, 25(13), 15131. doi:10.1364/OE.25.015131 +- :cite:`Safdar2021` : Safdar, M., Hardeberg, J. Y., & Ronnier Luo, M. + (2021). ZCAM, a colour appearance model based on a high dynamic range + uniform colour space. Optics Express, 29(4), 6036. doi:10.1364/OE.413659 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.models.rgb.transfer_functions import (eotf_inverse_ST2084, - eotf_ST2084) +from colour.algebra import vector_dot +from colour.hints import ( + ArrayLike, + Literal, + NDArray, + Optional, + Tuple, + Union, +) +from colour.models.rgb.transfer_functions import ( + eotf_inverse_ST2084, + eotf_ST2084, +) from colour.models.rgb.transfer_functions.st_2084 import CONSTANTS_ST2084 -from colour.utilities import (Structure, domain_range_scale, vector_dot, - from_range_1, to_domain_1, tsplit, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.utilities import ( + Structure, + as_float_array, + domain_range_scale, + optional, + tsplit, + tstack, + validate_method, +) +from colour.utilities.documentation import ( + DocstringTuple, + is_documentation_building, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'CONSTANTS_JZAZBZ', 'MATRIX_JZAZBZ_XYZ_TO_LMS', 'MATRIX_JZAZBZ_LMS_TO_XYZ', - 'MATRIX_JZAZBZ_LMS_P_TO_IZAZBZ', 'MATRIX_JZAZBZ_IZAZBZ_TO_LMS_P', - 'XYZ_to_JzAzBz', 'JzAzBz_to_XYZ' + "CONSTANTS_JZAZBZ_SAFDAR2017", + "CONSTANTS_JZAZBZ_SAFDAR2021", + "MATRIX_JZAZBZ_XYZ_TO_LMS", + "MATRIX_JZAZBZ_LMS_TO_XYZ", + "MATRIX_JZAZBZ_LMS_P_TO_IZAZBZ_SAFDAR2017", + "MATRIX_JZAZBZ_IZAZBZ_TO_LMS_P_SAFDAR2017", + "MATRIX_JZAZBZ_LMS_P_TO_IZAZBZ_SAFDAR2021", + "MATRIX_JZAZBZ_IZAZBZ_TO_LMS_P_SAFDAR2021", + "IZAZBZ_METHODS", + "XYZ_to_Izazbz", + "Izazbz_to_XYZ", + "XYZ_to_Jzazbz", + "Jzazbz_to_XYZ", ] -CONSTANTS_JZAZBZ = Structure( - b=1.15, g=0.66, d=-0.56, d_0=1.6295499532821566 * 10 ** -11) -CONSTANTS_JZAZBZ.update(CONSTANTS_ST2084) -CONSTANTS_JZAZBZ.m_2 = 1.7 * 2523 / 2 ** 5 +CONSTANTS_JZAZBZ_SAFDAR2017: Structure = Structure( + b=1.15, g=0.66, d=-0.56, d_0=1.6295499532821566 * 10**-11 +) +CONSTANTS_JZAZBZ_SAFDAR2017.update(CONSTANTS_ST2084) +CONSTANTS_JZAZBZ_SAFDAR2017.m_2 = 1.7 * 2523 / 2**5 """ -Constants for :math:`J_zA_zB_z` colourspace and its variant of the perceptual +Constants for :math:`J_za_zb_z` colourspace and its variant of the perceptual quantizer (PQ) from Dolby Laboratories. Notes ----- - The :math:`m2` constant, i.e. the power factor has been re-optimized during - the development of the :math:`J_zA_zB_z` colourspace. - -CONSTANTS_JZAZBZ : Structure + the development of the :math:`J_za_zb_z` colourspace. """ -MATRIX_JZAZBZ_XYZ_TO_LMS = np.array([ - [0.41478972, 0.579999, 0.0146480], - [-0.2015100, 1.120649, 0.0531008], - [-0.0166008, 0.264800, 0.6684799], -]) +CONSTANTS_JZAZBZ_SAFDAR2021: Structure = Structure( + **CONSTANTS_JZAZBZ_SAFDAR2017 +) +CONSTANTS_JZAZBZ_SAFDAR2021.d_0 = 3.7035226210190005 * 10**-11 +""":math:`J_za_zb_z` colourspace constants for the *ZCAM* colour appearance model.""" + +MATRIX_JZAZBZ_XYZ_TO_LMS: NDArray = np.array( + [ + [0.41478972, 0.579999, 0.0146480], + [-0.2015100, 1.120649, 0.0531008], + [-0.0166008, 0.264800, 0.6684799], + ] +) """ -:math:`J_zA_zB_z` *CIE XYZ* tristimulus values to normalised cone responses +:math:`J_za_zb_z` *CIE XYZ* tristimulus values to normalised cone responses matrix. - -MATRIX_JZAZBZ_XYZ_TO_LMS : array_like, (3, 3) """ -MATRIX_JZAZBZ_LMS_TO_XYZ = np.linalg.inv(MATRIX_JZAZBZ_XYZ_TO_LMS) +MATRIX_JZAZBZ_LMS_TO_XYZ: NDArray = np.linalg.inv(MATRIX_JZAZBZ_XYZ_TO_LMS) """ -:math:`J_zA_zB_z` normalised cone responses to *CIE XYZ* tristimulus values +:math:`J_za_zb_z` normalised cone responses to *CIE XYZ* tristimulus values matrix. +""" -MATRIX_JZAZBZ_LMS_TO_XYZ : array_like, (3, 3) +MATRIX_JZAZBZ_LMS_P_TO_IZAZBZ_SAFDAR2017: NDArray = np.array( + [ + [0.500000, 0.500000, 0.000000], + [3.524000, -4.066708, 0.542708], + [0.199076, 1.096799, -1.295875], + ] +) +""" +:math:`LMS_p` *SMPTE ST 2084:2014* encoded normalised cone responses to +:math:`I_za_zb_z` intermediate colourspace matrix. +""" + +MATRIX_JZAZBZ_IZAZBZ_TO_LMS_P_SAFDAR2017: NDArray = np.linalg.inv( + MATRIX_JZAZBZ_LMS_P_TO_IZAZBZ_SAFDAR2017 +) +""" +:math:`I_za_zb_z` intermediate colourspace to :math:`LMS_p` +*SMPTE ST 2084:2014* encoded normalised cone responses matrix. """ -MATRIX_JZAZBZ_LMS_P_TO_IZAZBZ = np.array([ - [0.500000, 0.500000, 0.000000], - [3.524000, -4.066708, 0.542708], - [0.199076, 1.096799, -1.295875], -]) +MATRIX_JZAZBZ_LMS_P_TO_IZAZBZ_SAFDAR2021: NDArray = np.array( + [ + [0.000000, 1.000000, 0.000000], + [3.524000, -4.066708, 0.542708], + [0.199076, 1.096799, -1.295875], + ] +) """ :math:`LMS_p` *SMPTE ST 2084:2014* encoded normalised cone responses to -:math:`I_zA_zB_z` intermediate colourspace matrix. +:math:`I_za_zb_z` intermediate colourspace matrix. -MATRIX_JZAZBZ_LMS_P_TO_IZAZBZ : array_like, (3, 3) +References +---------- +:cite:`Safdar2021` """ -MATRIX_JZAZBZ_IZAZBZ_TO_LMS_P = np.linalg.inv(MATRIX_JZAZBZ_LMS_P_TO_IZAZBZ) +MATRIX_JZAZBZ_IZAZBZ_TO_LMS_P_SAFDAR2021: NDArray = np.linalg.inv( + MATRIX_JZAZBZ_LMS_P_TO_IZAZBZ_SAFDAR2021 +) """ -:math:`I_zA_zB_z` intermediate colourspace to :math:`LMS_p` +:math:`I_za_zb_z` intermediate colourspace to :math:`LMS_p` *SMPTE ST 2084:2014* encoded normalised cone responses matrix. -MATRIX_JZAZBZ_IZAZBZ_TO_LMS_P : array_like, (3, 3) +References +---------- +:cite:`Safdar2021` """ +IZAZBZ_METHODS: Tuple = ("Safdar 2017", "Safdar 2021", "ZCAM") +if is_documentation_building(): # pragma: no cover + IZAZBZ_METHODS = DocstringTuple(IZAZBZ_METHODS) + IZAZBZ_METHODS.__doc__ = """ +Supported :math:`I_za_zb_z` computation methods. + +References +---------- +:cite:`Safdar2017`, :cite:`Safdar2021` +""" -def XYZ_to_JzAzBz(XYZ_D65, constants=CONSTANTS_JZAZBZ): + +def XYZ_to_Izazbz( + XYZ_D65: ArrayLike, + constants: Optional[Structure] = None, + method: Union[ + Literal["Safdar 2017", "Safdar 2021", "ZCAM"], str + ] = "Safdar 2017", +) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to :math:`J_zA_zB_z` + Convert from *CIE XYZ* tristimulus values to :math:`I_za_zb_z` colourspace. Parameters ---------- - XYZ_D65 : array_like + XYZ_D65 *CIE XYZ* tristimulus values under *CIE Standard Illuminant D Series D65*. - constants : Structure, optional - :math:`J_zA_zB_z` colourspace constants. + constants + :math:`J_za_zb_z` colourspace constants. + method + Computation methods, *Safdar 2021* and *ZCAM* methods are equivalent. Returns ------- - ndarray - :math:`J_zA_zB_z` colourspace array where :math:`J_z` is Lightness, - :math:`A_z` is redness-greenness and :math:`B_z` is + :class:`numpy.ndarray` + :math:`I_za_zb_z` colourspace array where :math:`I_z` is the achromatic + response, :math:`a_z` is redness-greenness and :math:`b_z` is yellowness-blueness. Warnings @@ -123,12 +210,11 @@ def XYZ_to_JzAzBz(XYZ_D65, constants=CONSTANTS_JZAZBZ): Notes ----- - - The underlying *SMPTE ST 2084:2014* transfer function is an absolute transfer function, thus the domain and range values for the *Reference* and *1* scales are only indicative that the data is not affected by scale transformations. The effective domain of *SMPTE ST 2084:2014* - inverse electro-optical transfer function (EOTF / EOCF) is + inverse electro-optical transfer function (EOTF) is [0.0001, 10000]. +------------+-----------------------+------------------+ @@ -140,25 +226,34 @@ def XYZ_to_JzAzBz(XYZ_D65, constants=CONSTANTS_JZAZBZ): +------------+-----------------------+------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ - | ``JzAzBz`` | ``Jz`` : [0, 1] | ``Jz`` : [0, 1] | + | ``Izazbz`` | ``Iz`` : [0, 1] | ``Iz`` : [0, 1] | | | | | - | | ``Az`` : [-1, 1] | ``Az`` : [-1, 1] | + | | ``az`` : [-1, 1] | ``az`` : [-1, 1] | | | | | - | | ``Bz`` : [-1, 1] | ``Bz`` : [-1, 1] | + | | ``bz`` : [-1, 1] | ``bz`` : [-1, 1] | +------------+-----------------------+------------------+ References ---------- - :cite:`Safdar2017` + :cite:`Safdar2017`, :cite:`Safdar2021` Examples -------- >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) - >>> XYZ_to_JzAzBz(XYZ) # doctest: +ELLIPSIS - array([ 0.0053504..., 0.0092430..., 0.0052600...]) + >>> XYZ_to_Izazbz(XYZ) # doctest: +ELLIPSIS + array([ 0.0120779..., 0.0092430..., 0.0052600...]) """ - X_D65, Y_D65, Z_D65 = tsplit(to_domain_1(XYZ_D65)) + X_D65, Y_D65, Z_D65 = tsplit(as_float_array(XYZ_D65)) + + method = validate_method(method, IZAZBZ_METHODS) + + constants = optional( + constants, + CONSTANTS_JZAZBZ_SAFDAR2017 + if method == "safdar 2017" + else CONSTANTS_JZAZBZ_SAFDAR2021, + ) X_p_D65 = constants.b * X_D65 - (constants.b - 1) * Z_D65 Y_p_D65 = constants.g * Y_D65 - (constants.g - 1) * X_D65 @@ -167,35 +262,43 @@ def XYZ_to_JzAzBz(XYZ_D65, constants=CONSTANTS_JZAZBZ): LMS = vector_dot(MATRIX_JZAZBZ_XYZ_TO_LMS, XYZ_p_D65) - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): LMS_p = eotf_inverse_ST2084(LMS, 10000, constants) - I_z, A_z, B_z = tsplit(vector_dot(MATRIX_JZAZBZ_LMS_P_TO_IZAZBZ, LMS_p)) - - J_z = ((1 + constants.d) * I_z) / (1 + constants.d * I_z) - constants.d_0 - - JzAzBz = tstack([J_z, A_z, B_z]) + if method == "safdar 2017": + Izazbz = vector_dot(MATRIX_JZAZBZ_LMS_P_TO_IZAZBZ_SAFDAR2017, LMS_p) + else: + Izazbz = vector_dot(MATRIX_JZAZBZ_LMS_P_TO_IZAZBZ_SAFDAR2021, LMS_p) + Izazbz[..., 0] -= constants.d_0 - return from_range_1(JzAzBz) + return Izazbz -def JzAzBz_to_XYZ(JzAzBz, constants=CONSTANTS_JZAZBZ): +def Izazbz_to_XYZ( + Izazbz: ArrayLike, + constants: Optional[Structure] = None, + method: Union[ + Literal["Safdar 2017", "Safdar 2021", "ZCAM"], str + ] = "Safdar 2017", +) -> NDArray: """ - Converts from :math:`J_zA_zB_z` colourspace to *CIE XYZ* tristimulus + Convert from :math:`I_za_zb_z` colourspace to *CIE XYZ* tristimulus values. Parameters ---------- - JzAzBz : array_like - :math:`J_zA_zB_z` colourspace array where :math:`J_z` is Lightness, - :math:`A_z` is redness-greenness and :math:`B_z` is - yellowness-blueness. - constants : Structure, optional - :math:`J_zA_zB_z` colourspace constants. + Izazbz + :math:`I_za_zb_z` colourspace array where :math:`I_z` is the + achromatic response, :math:`a_z` is redness-greenness and + :math:`b_z` is yellowness-blueness. + constants + :math:`J_za_zb_z` colourspace constants. + method + Computation methods, *Safdar 2021* and *ZCAM* methods are equivalent. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values under *CIE Standard Illuminant D Series D65*. @@ -206,7 +309,6 @@ def JzAzBz_to_XYZ(JzAzBz, constants=CONSTANTS_JZAZBZ): Notes ----- - - The underlying *SMPTE ST 2084:2014* transfer function is an absolute transfer function, thus the domain and range values for the *Reference* and *1* scales are only indicative that the data is not affected by @@ -215,11 +317,11 @@ def JzAzBz_to_XYZ(JzAzBz, constants=CONSTANTS_JZAZBZ): +------------+-----------------------+------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ - | ``JzAzBz`` | ``Jz`` : [0, 1] | ``Jz`` : [0, 1] | + | ``Izazbz`` | ``Iz`` : [0, 1] | ``Iz`` : [0, 1] | | | | | - | | ``Az`` : [-1, 1] | ``Az`` : [-1, 1] | + | | ``az`` : [-1, 1] | ``az`` : [-1, 1] | | | | | - | | ``Bz`` : [-1, 1] | ``Bz`` : [-1, 1] | + | | ``bz`` : [-1, 1] | ``bz`` : [-1, 1] | +------------+-----------------------+------------------+ +------------+-----------------------+------------------+ @@ -230,30 +332,194 @@ def JzAzBz_to_XYZ(JzAzBz, constants=CONSTANTS_JZAZBZ): References ---------- - :cite:`Safdar2017` + :cite:`Safdar2017`, :cite:`Safdar2021` Examples -------- - >>> JzAzBz = np.array([0.00535048, 0.00924302, 0.00526007]) - >>> JzAzBz_to_XYZ(JzAzBz) # doctest: +ELLIPSIS - array([ 0.2065402..., 0.1219723..., 0.0513696...]) + >>> Izazbz = np.array([0.01207793, 0.00924302, 0.00526007]) + >>> Izazbz_to_XYZ(Izazbz) # doctest: +ELLIPSIS + array([ 0.2065401..., 0.1219723..., 0.0513696...]) """ - J_z, A_z, B_z = tsplit(to_domain_1(JzAzBz)) + Izazbz = as_float_array(Izazbz) - I_z = ((J_z + constants.d_0) / (1 + constants.d - constants.d * - (J_z + constants.d_0))) - LMS_p = vector_dot(MATRIX_JZAZBZ_IZAZBZ_TO_LMS_P, tstack([I_z, A_z, B_z])) + method = validate_method(method, IZAZBZ_METHODS) - with domain_range_scale('ignore'): + constants = optional( + constants, + CONSTANTS_JZAZBZ_SAFDAR2017 + if method == "safdar 2017" + else CONSTANTS_JZAZBZ_SAFDAR2021, + ) + + if method == "safdar 2017": + LMS_p = vector_dot(MATRIX_JZAZBZ_IZAZBZ_TO_LMS_P_SAFDAR2017, Izazbz) + else: + Izazbz[..., 0] += constants.d_0 + LMS_p = vector_dot(MATRIX_JZAZBZ_IZAZBZ_TO_LMS_P_SAFDAR2021, Izazbz) + + with domain_range_scale("ignore"): LMS = eotf_ST2084(LMS_p, 10000, constants) X_p_D65, Y_p_D65, Z_p_D65 = tsplit( - vector_dot(MATRIX_JZAZBZ_LMS_TO_XYZ, LMS)) + vector_dot(MATRIX_JZAZBZ_LMS_TO_XYZ, LMS) + ) X_D65 = (X_p_D65 + (constants.b - 1) * Z_p_D65) / constants.b Y_D65 = (Y_p_D65 + (constants.g - 1) * X_D65) / constants.g XYZ_D65 = tstack([X_D65, Y_D65, Z_p_D65]) - return from_range_1(XYZ_D65) + return XYZ_D65 + + +def XYZ_to_Jzazbz( + XYZ_D65: ArrayLike, constants: Structure = CONSTANTS_JZAZBZ_SAFDAR2017 +) -> NDArray: + """ + Convert from *CIE XYZ* tristimulus values to :math:`J_za_zb_z` + colourspace. + + Parameters + ---------- + XYZ_D65 + *CIE XYZ* tristimulus values under + *CIE Standard Illuminant D Series D65*. + constants + :math:`J_za_zb_z` colourspace constants. + + Returns + ------- + :class:`numpy.ndarray` + :math:`J_za_zb_z` colourspace array where :math:`J_z` is Lightness, + :math:`a_z` is redness-greenness and :math:`b_z` is + yellowness-blueness. + + Warnings + -------- + The underlying *SMPTE ST 2084:2014* transfer function is an absolute + transfer function. + + Notes + ----- + - The underlying *SMPTE ST 2084:2014* transfer function is an absolute + transfer function, thus the domain and range values for the *Reference* + and *1* scales are only indicative that the data is not affected by + scale transformations. The effective domain of *SMPTE ST 2084:2014* + inverse electro-optical transfer function (EOTF) is + [0.0001, 10000]. + + +------------+-----------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+==================+ + | ``XYZ`` | ``UN`` | ``UN`` | + +------------+-----------------------+------------------+ + + +------------+-----------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+==================+ + | ``Jzazbz`` | ``Jz`` : [0, 1] | ``Jz`` : [0, 1] | + | | | | + | | ``az`` : [-1, 1] | ``az`` : [-1, 1] | + | | | | + | | ``bz`` : [-1, 1] | ``bz`` : [-1, 1] | + +------------+-----------------------+------------------+ + + References + ---------- + :cite:`Safdar2017` + + Examples + -------- + >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + >>> XYZ_to_Jzazbz(XYZ) # doctest: +ELLIPSIS + array([ 0.0053504..., 0.0092430..., 0.0052600...]) + """ + + XYZ_D65 = as_float_array(XYZ_D65) + + with domain_range_scale("ignore"): + I_z, a_z, b_z = tsplit( + XYZ_to_Izazbz(XYZ_D65, CONSTANTS_JZAZBZ_SAFDAR2017, "Safdar 2017") + ) + + J_z = ((1 + constants.d) * I_z) / (1 + constants.d * I_z) - constants.d_0 + + Jzazbz = tstack([J_z, a_z, b_z]) + + return Jzazbz + + +def Jzazbz_to_XYZ( + Jzazbz: ArrayLike, constants: Structure = CONSTANTS_JZAZBZ_SAFDAR2017 +) -> NDArray: + """ + Convert from :math:`J_za_zb_z` colourspace to *CIE XYZ* tristimulus + values. + + Parameters + ---------- + Jzazbz + :math:`J_za_zb_z` colourspace array where :math:`J_z` is Lightness, + :math:`a_z` is redness-greenness and :math:`b_z` is + yellowness-blueness. + constants + :math:`J_za_zb_z` colourspace constants. + + Returns + ------- + :class:`numpy.ndarray` + *CIE XYZ* tristimulus values under + *CIE Standard Illuminant D Series D65*. + + Warnings + -------- + The underlying *SMPTE ST 2084:2014* transfer function is an absolute + transfer function. + + Notes + ----- + - The underlying *SMPTE ST 2084:2014* transfer function is an absolute + transfer function, thus the domain and range values for the *Reference* + and *1* scales are only indicative that the data is not affected by + scale transformations. + + +------------+-----------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+==================+ + | ``Jzazbz`` | ``Jz`` : [0, 1] | ``Jz`` : [0, 1] | + | | | | + | | ``az`` : [-1, 1] | ``az`` : [-1, 1] | + | | | | + | | ``bz`` : [-1, 1] | ``bz`` : [-1, 1] | + +------------+-----------------------+------------------+ + + +------------+-----------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+==================+ + | ``XYZ`` | ``UN`` | ``UN`` | + +------------+-----------------------+------------------+ + + References + ---------- + :cite:`Safdar2017` + + Examples + -------- + >>> Jzazbz = np.array([0.00535048, 0.00924302, 0.00526007]) + >>> Jzazbz_to_XYZ(Jzazbz) # doctest: +ELLIPSIS + array([ 0.2065402..., 0.1219723..., 0.0513696...]) + """ + + J_z, a_z, b_z = tsplit(as_float_array(Jzazbz)) + + I_z = (J_z + constants.d_0) / ( + 1 + constants.d - constants.d * (J_z + constants.d_0) + ) + + with domain_range_scale("ignore"): + XYZ_D65 = Izazbz_to_XYZ( + tstack([I_z, a_z, b_z]), CONSTANTS_JZAZBZ_SAFDAR2017, "Safdar 2017" + ) + + return XYZ_D65 diff --git a/colour/models/oklab.py b/colour/models/oklab.py new file mode 100644 index 0000000000..5fdfc4623a --- /dev/null +++ b/colour/models/oklab.py @@ -0,0 +1,170 @@ +""" +Oklab Colourspace +================= + +Defines the *Oklab* colourspace transformations: + +- :func:`colour.XYZ_to_Oklab` +- :func:`colour.Oklab_to_XYZ` + +References +---------- +- :cite:`Ottosson2020` : Ottosson, B. (2020). A perceptual color space for + image processing. Retrieved December 24, 2020, from + https://bottosson.github.io/posts/oklab/ +""" + +from __future__ import annotations + +import numpy as np + +from colour.algebra import spow, vector_dot +from colour.hints import ArrayLike, NDArray +from colour.utilities import from_range_1, to_domain_1 + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "MATRIX_1_XYZ_TO_LMS", + "MATRIX_1_LMS_TO_XYZ", + "MATRIX_2_LMS_TO_LAB", + "MATRIX_2_LAB_TO_LMS", + "XYZ_to_Oklab", + "Oklab_to_XYZ", +] + +MATRIX_1_XYZ_TO_LMS: NDArray = np.array( + [ + [0.8189330101, 0.3618667424, -0.1288597137], + [0.0329845436, 0.9293118715, 0.0361456387], + [0.0482003018, 0.2643662691, 0.6338517070], + ] +) +"""*CIE XYZ* tristimulus values to normalised cone responses matrix.""" + +MATRIX_1_LMS_TO_XYZ: NDArray = np.linalg.inv(MATRIX_1_XYZ_TO_LMS) +"""Normalised cone responses to *CIE XYZ* tristimulus values matrix.""" + +MATRIX_2_LMS_TO_LAB: NDArray = np.array( + [ + [0.2104542553, 0.7936177850, -0.0040720468], + [1.9779984951, -2.4285922050, 0.4505937099], + [0.0259040371, 0.7827717662, -0.8086757660], + ] +) +"""Normalised cone responses to *Oklab* colourspace matrix.""" + +MATRIX_2_LAB_TO_LMS: NDArray = np.linalg.inv(MATRIX_2_LMS_TO_LAB) +"""*Oklab* colourspace to normalised cone responses matrix.""" + + +def XYZ_to_Oklab(XYZ: ArrayLike) -> NDArray: + """ + Convert from *CIE XYZ* tristimulus values to *Oklab* colourspace. + + Parameters + ---------- + XYZ + *CIE XYZ* tristimulus values. + + Returns + ------- + :class:`numpy.ndarray` + *Oklab* colourspace array. + + Notes + ----- + +------------+-----------------------+-----------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+=================+ + | ``XYZ`` | [0, 1] | [0, 1] | + +------------+-----------------------+-----------------+ + + +------------+-----------------------+-----------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+=================+ + | ``Lab`` | ``L`` : [0, 1] | ``L`` : [0, 1] | + | | | | + | | ``a`` : [-1, 1] | ``a`` : [-1, 1] | + | | | | + | | ``b`` : [-1, 1] | ``b`` : [-1, 1] | + +------------+-----------------------+-----------------+ + + - Input *CIE XYZ* tristimulus values must be adapted to + *CIE Standard Illuminant D Series* *D65*. + + References + ---------- + :cite:`Ottosson2020` + + Examples + -------- + >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + >>> XYZ_to_Oklab(XYZ) # doctest: +ELLIPSIS + array([ 0.5163401..., 0.154695 ..., 0.0628957...]) + """ + + XYZ = to_domain_1(XYZ) + + LMS = vector_dot(MATRIX_1_XYZ_TO_LMS, XYZ) + LMS_prime = spow(LMS, 1 / 3) + Lab = vector_dot(MATRIX_2_LMS_TO_LAB, LMS_prime) + + return from_range_1(Lab) + + +def Oklab_to_XYZ(Lab: ArrayLike) -> NDArray: + """ + Convert from *Oklab* colourspace to *CIE XYZ* tristimulus values. + + Parameters + ---------- + Lab + *Oklab* colourspace array. + + Returns + ------- + :class:`numpy.ndarray` + *CIE XYZ* tristimulus values. + + Notes + ----- + +------------+-----------------------+-----------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+=================+ + | ``Lab`` | ``L`` : [0, 1] | ``L`` : [0, 1] | + | | | | + | | ``a`` : [-1, 1] | ``a`` : [-1, 1] | + | | | | + | | ``b`` : [-1, 1] | ``b`` : [-1, 1] | + +------------+-----------------------+-----------------+ + + +------------+-----------------------+-----------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+=================+ + | ``XYZ`` | [0, 1] | [0, 1] | + +------------+-----------------------+-----------------+ + + References + ---------- + :cite:`Ottosson2020` + + Examples + -------- + >>> Lab = np.array([0.51634019, 0.15469500, 0.06289579]) + >>> Oklab_to_XYZ(Lab) # doctest: +ELLIPSIS + array([ 0.2065400..., 0.1219722..., 0.0513695...]) + """ + + Lab = to_domain_1(Lab) + + LMS = vector_dot(MATRIX_2_LAB_TO_LMS, Lab) + LMS_prime = spow(LMS, 3) + XYZ = vector_dot(MATRIX_1_LMS_TO_XYZ, LMS_prime) + + return from_range_1(XYZ) diff --git a/colour/models/osa_ucs.py b/colour/models/osa_ucs.py index 7526bc091d..e7009f138e 100644 --- a/colour/models/osa_ucs.py +++ b/colour/models/osa_ucs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Optical Society of America Uniform Colour Scales (OSA UCS) ========================================================== @@ -19,42 +18,52 @@ ISSN:2166-9635 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from scipy.optimize import fmin -from colour.algebra import spow +from colour.algebra import spow, vector_dot +from colour.hints import ArrayLike, Dict, FloatingOrNDArray, NDArray, Optional from colour.models import XYZ_to_xyY -from colour.utilities import (as_float_array, domain_range_scale, vector_dot, - from_range_100, to_domain_100, tsplit, tstack) -from colour.utilities.deprecation import handle_arguments_deprecation - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['XYZ_to_OSA_UCS', 'OSA_UCS_to_XYZ'] - -MATRIX_XYZ_TO_RGB_OSA_UCS = np.array([ - [0.799, 0.4194, -0.1648], - [-0.4493, 1.3265, 0.0927], - [-0.1149, 0.3394, 0.717], -]) +from colour.utilities import ( + as_float, + as_float_array, + domain_range_scale, + from_range_100, + to_domain_100, + tsplit, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "XYZ_to_OSA_UCS", + "OSA_UCS_to_XYZ", +] + +MATRIX_XYZ_TO_RGB_OSA_UCS: NDArray = np.array( + [ + [0.799, 0.4194, -0.1648], + [-0.4493, 1.3265, 0.0927], + [-0.1149, 0.3394, 0.717], + ] +) """ *OSA UCS* matrix converting from *CIE XYZ* tristimulus values to *RGB* colourspace. - -MATRIX_XYZ_TO_RGB_OSA_UCS : array_like, (3, 3) """ -def XYZ_to_OSA_UCS(XYZ): +def XYZ_to_OSA_UCS(XYZ: ArrayLike) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values under the + Convert from *CIE XYZ* tristimulus values under the *CIE 1964 10 Degree Standard Observer* to *OSA UCS* colourspace. The lightness axis, *L* is usually in range [-9, 5] and centered around @@ -63,18 +72,17 @@ def XYZ_to_OSA_UCS(XYZ): Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values under the *CIE 1964 10 Degree Standard Observer*. Returns ------- - ndarray + :class:`numpy.ndarray` *OSA UCS* :math:`Ljg` lightness, jaune (yellowness), and greenness. Notes ----- - +------------+-----------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+====================+ @@ -108,8 +116,14 @@ def XYZ_to_OSA_UCS(XYZ): XYZ = to_domain_100(XYZ) x, y, Y = tsplit(XYZ_to_xyY(XYZ)) - Y_0 = Y * (4.4934 * x ** 2 + 4.3034 * y ** 2 - 4.276 * x * y - 1.3744 * x - - 2.5643 * y + 1.8103) + Y_0 = Y * ( + 4.4934 * x**2 + + 4.3034 * y**2 + - 4.276 * x * y + - 1.3744 * x + - 2.5643 * y + + 1.8103 + ) o_3 = 1 / 3 Y_0_es = spow(Y_0, o_3) - 2 / 3 @@ -130,26 +144,23 @@ def XYZ_to_OSA_UCS(XYZ): return from_range_100(Ljg) -def OSA_UCS_to_XYZ(Ljg, optimisation_kwargs=None, **kwargs): +def OSA_UCS_to_XYZ( + Ljg: ArrayLike, optimisation_kwargs: Optional[Dict] = None +) -> NDArray: """ - Converts from *OSA UCS* colourspace to *CIE XYZ* tristimulus values under + Convert from *OSA UCS* colourspace to *CIE XYZ* tristimulus values under the *CIE 1964 10 Degree Standard Observer*. Parameters ---------- - Ljg : array_like + Ljg *OSA UCS* :math:`Ljg` lightness, jaune (yellowness), and greenness. - optimisation_kwargs : dict_like, optional + optimisation_kwargs Parameters for :func:`scipy.optimize.fmin` definition. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values under the *CIE 1964 10 Degree Standard Observer*. @@ -163,7 +174,6 @@ def OSA_UCS_to_XYZ(Ljg, optimisation_kwargs=None, **kwargs): Notes ----- - +------------+-----------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+====================+ @@ -194,34 +204,29 @@ def OSA_UCS_to_XYZ(Ljg, optimisation_kwargs=None, **kwargs): array([ 20.6540240..., 12.1972369..., 5.1369372...]) """ - optimisation_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['optimisation_parameters', 'optimisation_kwargs'] - ], - }, **kwargs).get('optimisation_kwargs', optimisation_kwargs) - Ljg = to_domain_100(Ljg) shape = Ljg.shape Ljg = np.atleast_1d(Ljg.reshape([-1, 3])) - optimisation_settings = {'disp': False} + optimisation_settings = {"disp": False} if optimisation_kwargs is not None: optimisation_settings.update(optimisation_kwargs) - def error_function(XYZ, Ljg): - """ - Error function. - """ + def error_function(XYZ: ArrayLike, Ljg: ArrayLike) -> FloatingOrNDArray: + """Error function.""" # Error must be computed in "reference" domain and range. - with domain_range_scale('ignore'): - error = np.linalg.norm(XYZ_to_OSA_UCS(XYZ) - Ljg) + with domain_range_scale("ignore"): + error = np.linalg.norm(XYZ_to_OSA_UCS(XYZ) - as_float_array(Ljg)) - return error + return as_float(error) x_0 = np.array([30, 30, 30]) - XYZ = as_float_array([ - fmin(error_function, x_0, (Ljg_i, ), **optimisation_settings) - for Ljg_i in Ljg - ]) - - return from_range_100(XYZ.reshape(shape)) + XYZ = as_float_array( + [ + fmin(error_function, x_0, (Ljg_i,), **optimisation_settings) + for Ljg_i in as_float_array(Ljg) + ] + ) + + return from_range_100(np.reshape(XYZ, shape)) diff --git a/colour/models/prolab.py b/colour/models/prolab.py new file mode 100644 index 0000000000..bfcdd2b3e7 --- /dev/null +++ b/colour/models/prolab.py @@ -0,0 +1,204 @@ +""" +ProLab Colourspace +================== + +Defines the *ProLab* colourspace transformations: + +- :func:`colour.XYZ_to_ProLab` +- :func:`colour.ProLab_to_XYZ` + +References +---------- +- :cite:`Ivan2021` : Ivan A. Konovalenko, Anna A. Smagina, Dmitry P. + Nikolaev, Petr P. Nikolaev. ProLab: perceptually uniform projective colour + coordinate system. doi:10.1109/ACCESS.2017 +""" + +from __future__ import annotations + +import numpy as np + +from colour.colorimetry import CCS_ILLUMINANTS +from colour.hints import ArrayLike, NDArray +from colour.models import xy_to_xyY, xyY_to_XYZ +from colour.utilities import as_float_array, from_range_1, ones, to_domain_1 + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "MATRIX_Q", + "MATRIX_INVERSE_Q", + "ProLab_to_XYZ", + "XYZ_to_ProLab", +] + +MATRIX_Q: NDArray = np.array( + [ + [75.54, 486.66, 167.39, 0], + [617.72, -595.45, -22.27, 0], + [48.34, 194.94, -243.28, 0], + [0.7554, 3.8666, 1.6739, 1], + ] +) +"""Normalised cone responses to *CIE XYZ* tristimulus values matrix.""" + +MATRIX_INVERSE_Q: NDArray = np.linalg.inv(MATRIX_Q) +"""Normalised cone responses to *ProLab* colourspace matrix.""" + + +def projective_transformation(a: ArrayLike, Q: ArrayLike) -> NDArray: + """ + Transform given array :math:`a` with the projective transformation matrix + :math:`Q`. + + Parameters + ---------- + a + Array :math:`a` to apply the projective transformation matrix onto. + Q + Projective transformation matrix :math:`Q`. + + Returns + ------- + :class:`numpy.ndarray` + Transformed array :math:`a`. + """ + + a = as_float_array(a) + Q = as_float_array(Q) + + shape = list(a.shape) + shape[-1] = shape[-1] + 1 + + M = ones(tuple(shape)) + M[..., :-1] = a + + homography = np.dot(M, np.transpose(Q)) + homography[..., 0:-1] /= homography[..., -1][..., np.newaxis] + + return homography[..., 0:-1] + + +def XYZ_to_ProLab( + XYZ: ArrayLike, + illuminant: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], +) -> NDArray: + """ + Convert from *CIE XYZ* tristimulus values to *ProLab* colourspace. + + Parameters + ---------- + XYZ + *CIE XYZ* tristimulus values. + illuminant + Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* + colourspace array. + + Returns + ------- + :class:`numpy.ndarray` + *ProLab* colourspace array. + + Notes + ----- + +------------+-----------------------+-----------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+=================+ + | ``XYZ`` | [0, 1] | [0, 1] | + +------------+-----------------------+-----------------+ + + +------------+-----------------------+-----------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+=================+ + | ``ProLab`` | ``L`` : [0, 1] | ``L`` : [0, 1] | + | | | | + | | ``a`` : [-1, 1] | ``a`` : [-1, 1] | + | | | | + | | ``b`` : [-1, 1] | ``b`` : [-1, 1] | + +------------+-----------------------+-----------------+ + + References + ---------- + :cite:`Ivan2021` + + Examples + -------- + >>> Lab = np.array([0.51634019, 0.15469500, 0.06289579]) + >>> XYZ_to_ProLab(Lab) # doctest: +ELLIPSIS + array([ 59.846628... , 115.039635... , 20.1251035...]) + """ + + XYZ = to_domain_1(XYZ) + XYZ_n = xyY_to_XYZ(xy_to_xyY(illuminant)) + + ProLab = projective_transformation(XYZ / XYZ_n, MATRIX_Q) + + return from_range_1(ProLab) + + +def ProLab_to_XYZ( + ProLab: ArrayLike, + illuminant: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], +) -> NDArray: + """ + Convert from *ProLab* colourspace to *CIE XYZ* tristimulus values. + + Parameters + ---------- + ProLab + *ProLab* colourspace array. + illuminant + Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* + colourspace array. + + Returns + ------- + :class:`numpy.ndarray` + *CIE XYZ* tristimulus values. + + Notes + ----- + +------------+-----------------------+-----------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+=================+ + | ``Lab`` | ``L`` : [0, 1] | ``L`` : [0, 1] | + | | | | + | | ``a`` : [-1, 1] | ``a`` : [-1, 1] | + | | | | + | | ``b`` : [-1, 1] | ``b`` : [-1, 1] | + +------------+-----------------------+-----------------+ + + +------------+-----------------------+-----------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+=================+ + | ``XYZ`` | [0, 1] | [0, 1] | + +------------+-----------------------+-----------------+ + + References + ---------- + :cite:`Ivan2021` + + Examples + -------- + >>> ProLab = np.array([59.8466286, 115.0396354, 20.12510352]) + >>> ProLab_to_XYZ(ProLab) # doctest: +ELLIPSIS + array([ 0.5163401..., 0.154695 ..., 0.0628957...]) + """ + + ProLab = to_domain_1(ProLab) + XYZ_n = xyY_to_XYZ(xy_to_xyY(illuminant)) + + XYZ = projective_transformation(ProLab, MATRIX_INVERSE_Q) + + XYZ *= XYZ_n + + return from_range_1(XYZ) diff --git a/colour/models/rgb/__init__.py b/colour/models/rgb/__init__.py index e118430b34..87fbe2153b 100644 --- a/colour/models/rgb/__init__.py +++ b/colour/models/rgb/__init__.py @@ -1,42 +1,496 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - -from .derivation import (normalised_primary_matrix, - chromatically_adapted_primaries, primaries_whitepoint, - RGB_luminance_equation, RGB_luminance) +from .derivation import ( + normalised_primary_matrix, + chromatically_adapted_primaries, + primaries_whitepoint, + RGB_luminance_equation, + RGB_luminance, +) from .rgb_colourspace import RGB_Colourspace from .rgb_colourspace import XYZ_to_RGB, RGB_to_XYZ from .rgb_colourspace import matrix_RGB_to_RGB, RGB_to_RGB -from .transfer_functions import * # noqa -from . import transfer_functions -from .datasets import * # noqa -from . import datasets +from .transfer_functions import ( + CV_range, + legal_to_full, + full_to_legal, + gamma_function, + log_encoding_ACESproxy, + log_decoding_ACESproxy, + log_encoding_ACEScc, + log_decoding_ACEScc, + log_encoding_ACEScct, + log_decoding_ACEScct, + oetf_ARIBSTDB67, + oetf_inverse_ARIBSTDB67, + log_encoding_ALEXALogC, + log_decoding_ALEXALogC, + oetf_BlackmagicFilmGeneration5, + oetf_inverse_BlackmagicFilmGeneration5, + log_encoding_CanonLog, + log_decoding_CanonLog, + log_encoding_CanonLog2, + log_decoding_CanonLog2, + log_encoding_CanonLog3, + log_decoding_CanonLog3, + log_encoding_Cineon, + log_decoding_Cineon, + oetf_DaVinciIntermediate, + oetf_inverse_DaVinciIntermediate, + eotf_inverse_DCDM, + eotf_DCDM, + eotf_inverse_DICOMGSDF, + eotf_DICOMGSDF, + log_encoding_DJIDLog, + log_decoding_DJIDLog, + exponent_function_basic, + exponent_function_monitor_curve, + log_encoding_FilmicPro6, + log_decoding_FilmicPro6, + log_encoding_FilmLightTLog, + log_decoding_FilmLightTLog, + log_encoding_Protune, + log_decoding_Protune, + oetf_BT601, + oetf_inverse_BT601, + oetf_BT709, + oetf_inverse_BT709, + eotf_inverse_BT1886, + eotf_BT1886, + eotf_inverse_BT2020, + eotf_BT2020, + eotf_inverse_ST2084, + eotf_ST2084, + oetf_PQ_BT2100, + oetf_inverse_PQ_BT2100, + eotf_PQ_BT2100, + eotf_inverse_PQ_BT2100, + ootf_PQ_BT2100, + ootf_inverse_PQ_BT2100, + oetf_HLG_BT2100, + oetf_inverse_HLG_BT2100, + BT2100_HLG_EOTF_METHODS, + eotf_HLG_BT2100, + BT2100_HLG_EOTF_INVERSE_METHODS, + eotf_inverse_HLG_BT2100, + BT2100_HLG_OOTF_METHODS, + ootf_HLG_BT2100, + BT2100_HLG_OOTF_INVERSE_METHODS, + ootf_inverse_HLG_BT2100, + linear_function, + logarithmic_function_basic, + logarithmic_function_quasilog, + logarithmic_function_camera, + log_encoding_Log2, + log_decoding_Log2, + log_encoding_Panalog, + log_decoding_Panalog, + log_encoding_VLog, + log_decoding_VLog, + log_encoding_FLog, + log_decoding_FLog, + log_encoding_NLog, + log_decoding_NLog, + log_encoding_PivotedLog, + log_decoding_PivotedLog, + log_encoding_REDLog, + log_decoding_REDLog, + log_encoding_REDLogFilm, + log_decoding_REDLogFilm, + LOG3G10_ENCODING_METHODS, + LOG3G10_DECODING_METHODS, + log_encoding_Log3G10, + log_decoding_Log3G10, + log_encoding_Log3G12, + log_decoding_Log3G12, + cctf_encoding_ROMMRGB, + cctf_decoding_ROMMRGB, + cctf_encoding_ProPhotoRGB, + cctf_decoding_ProPhotoRGB, + cctf_encoding_RIMMRGB, + cctf_decoding_RIMMRGB, + log_encoding_ERIMMRGB, + log_decoding_ERIMMRGB, + oetf_SMPTE240M, + eotf_SMPTE240M, + log_encoding_SLog, + log_decoding_SLog, + log_encoding_SLog2, + log_decoding_SLog2, + log_encoding_SLog3, + log_decoding_SLog3, + eotf_inverse_sRGB, + eotf_sRGB, + log_encoding_ViperLog, + log_decoding_ViperLog, +) +from .transfer_functions import ( + LOG_ENCODINGS, + log_encoding, + LOG_DECODINGS, + log_decoding, + OETFS, + oetf, + OETF_INVERSES, + oetf_inverse, + EOTFS, + eotf, + EOTF_INVERSES, + eotf_inverse, + CCTF_ENCODINGS, + cctf_encoding, + CCTF_DECODINGS, + cctf_decoding, + OOTFS, + ootf, + OOTF_INVERSES, + ootf_inverse, +) +from .datasets import ( + RGB_COLOURSPACES, + RGB_COLOURSPACE_ACES2065_1, + RGB_COLOURSPACE_ACESCC, + RGB_COLOURSPACE_ACESCCT, + RGB_COLOURSPACE_ACESPROXY, + RGB_COLOURSPACE_ACESCG, + RGB_COLOURSPACE_ADOBE_RGB1998, + RGB_COLOURSPACE_ADOBE_WIDE_GAMUT_RGB, + RGB_COLOURSPACE_ALEXA_WIDE_GAMUT, + RGB_COLOURSPACE_APPLE_RGB, + RGB_COLOURSPACE_BEST_RGB, + RGB_COLOURSPACE_BETA_RGB, + RGB_COLOURSPACE_BLACKMAGIC_WIDE_GAMUT, + RGB_COLOURSPACE_BT470_525, + RGB_COLOURSPACE_BT470_625, + RGB_COLOURSPACE_BT709, + RGB_COLOURSPACE_BT2020, + RGB_COLOURSPACE_CIE_RGB, + RGB_COLOURSPACE_CINEMA_GAMUT, + RGB_COLOURSPACE_COLOR_MATCH_RGB, + RGB_COLOURSPACE_DAVINCI_WIDE_GAMUT, + RGB_COLOURSPACE_DCDM_XYZ, + RGB_COLOURSPACE_DCI_P3, + RGB_COLOURSPACE_DCI_P3_P, + RGB_COLOURSPACE_DISPLAY_P3, + RGB_COLOURSPACE_DJI_D_GAMUT, + RGB_COLOURSPACE_DON_RGB_4, + RGB_COLOURSPACE_ECI_RGB_V2, + RGB_COLOURSPACE_EKTA_SPACE_PS_5, + RGB_COLOURSPACE_FILMLIGHT_E_GAMUT, + RGB_COLOURSPACE_PROTUNE_NATIVE, + RGB_COLOURSPACE_MAX_RGB, + RGB_COLOURSPACE_N_GAMUT, + RGB_COLOURSPACE_P3_D65, + RGB_COLOURSPACE_PAL_SECAM, + RGB_COLOURSPACE_RED_COLOR, + RGB_COLOURSPACE_RED_COLOR_2, + RGB_COLOURSPACE_RED_COLOR_3, + RGB_COLOURSPACE_RED_COLOR_4, + RGB_COLOURSPACE_DRAGON_COLOR, + RGB_COLOURSPACE_DRAGON_COLOR_2, + RGB_COLOURSPACE_RED_WIDE_GAMUT_RGB, + RGB_COLOURSPACE_ROMM_RGB, + RGB_COLOURSPACE_RIMM_RGB, + RGB_COLOURSPACE_ERIMM_RGB, + RGB_COLOURSPACE_PROPHOTO_RGB, + RGB_COLOURSPACE_RUSSELL_RGB, + RGB_COLOURSPACE_SHARP_RGB, + RGB_COLOURSPACE_SMPTE_240M, + RGB_COLOURSPACE_SMPTE_C, + RGB_COLOURSPACE_NTSC1953, + RGB_COLOURSPACE_NTSC1987, + RGB_COLOURSPACE_S_GAMUT, + RGB_COLOURSPACE_S_GAMUT3, + RGB_COLOURSPACE_S_GAMUT3_CINE, + RGB_COLOURSPACE_VENICE_S_GAMUT3, + RGB_COLOURSPACE_VENICE_S_GAMUT3_CINE, + RGB_COLOURSPACE_sRGB, + RGB_COLOURSPACE_V_GAMUT, + RGB_COLOURSPACE_XTREME_RGB, + RGB_COLOURSPACE_F_GAMUT, +) + from .common import XYZ_to_sRGB, sRGB_to_XYZ -from .cylindrical import RGB_to_HSV, HSV_to_RGB, RGB_to_HSL, HSL_to_RGB +from .cylindrical import ( + RGB_to_HSV, + HSV_to_RGB, + RGB_to_HSL, + HSL_to_RGB, + RGB_to_HCL, + HCL_to_RGB, +) from .cmyk import RGB_to_CMY, CMY_to_RGB, CMY_to_CMYK, CMYK_to_CMY +from .hanbury2003 import RGB_to_IHLS, IHLS_to_RGB from .prismatic import RGB_to_Prismatic, Prismatic_to_RGB -from .ycbcr import (WEIGHTS_YCBCR, RGB_to_YCbCr, YCbCr_to_RGB, RGB_to_YcCbcCrc, - YcCbcCrc_to_RGB) +from .ycbcr import ( + WEIGHTS_YCBCR, + matrix_YCbCr, + offset_YCbCr, + RGB_to_YCbCr, + YCbCr_to_RGB, + RGB_to_YcCbcCrc, + YcCbcCrc_to_RGB, +) from .ycocg import RGB_to_YCoCg, YCoCg_to_RGB -from .ictcp import RGB_to_ICTCP, ICTCP_to_RGB +from .ictcp import RGB_to_ICtCp, ICtCp_to_RGB, XYZ_to_ICtCp, ICtCp_to_XYZ __all__ = [ - 'normalised_primary_matrix', 'chromatically_adapted_primaries', - 'primaries_whitepoint', 'RGB_luminance_equation', 'RGB_luminance' -] -__all__ += ['RGB_Colourspace'] -__all__ += ['XYZ_to_RGB', 'RGB_to_XYZ'] -__all__ += ['matrix_RGB_to_RGB', 'RGB_to_RGB'] -__all__ += transfer_functions.__all__ -__all__ += datasets.__all__ -__all__ += ['XYZ_to_sRGB', 'sRGB_to_XYZ'] -__all__ += ['RGB_to_HSV', 'HSV_to_RGB', 'RGB_to_HSL', 'HSL_to_RGB'] -__all__ += ['RGB_to_CMY', 'CMY_to_RGB', 'CMY_to_CMYK', 'CMYK_to_CMY'] -__all__ += ['RGB_to_Prismatic', 'Prismatic_to_RGB'] -__all__ += [ - 'WEIGHTS_YCBCR', 'RGB_to_YCbCr', 'YCbCr_to_RGB', 'RGB_to_YcCbcCrc', - 'YcCbcCrc_to_RGB' -] -__all__ += ['RGB_to_YCoCg', 'YCoCg_to_RGB'] -__all__ += ['RGB_to_ICTCP', 'ICTCP_to_RGB'] + "normalised_primary_matrix", + "chromatically_adapted_primaries", + "primaries_whitepoint", + "RGB_luminance_equation", + "RGB_luminance", +] +__all__ += [ + "RGB_Colourspace", +] +__all__ += [ + "XYZ_to_RGB", + "RGB_to_XYZ", +] +__all__ += [ + "matrix_RGB_to_RGB", + "RGB_to_RGB", +] +__all__ += [ + "CV_range", + "legal_to_full", + "full_to_legal", + "gamma_function", + "log_encoding_ACESproxy", + "log_decoding_ACESproxy", + "log_encoding_ACEScc", + "log_decoding_ACEScc", + "log_encoding_ACEScct", + "log_decoding_ACEScct", + "oetf_ARIBSTDB67", + "oetf_inverse_ARIBSTDB67", + "log_encoding_ALEXALogC", + "log_decoding_ALEXALogC", + "oetf_BlackmagicFilmGeneration5", + "oetf_inverse_BlackmagicFilmGeneration5", + "log_encoding_CanonLog", + "log_decoding_CanonLog", + "log_encoding_CanonLog2", + "log_decoding_CanonLog2", + "log_encoding_CanonLog3", + "log_decoding_CanonLog3", + "log_encoding_Cineon", + "log_decoding_Cineon", + "oetf_DaVinciIntermediate", + "oetf_inverse_DaVinciIntermediate", + "eotf_inverse_DCDM", + "eotf_DCDM", + "eotf_inverse_DICOMGSDF", + "eotf_DICOMGSDF", + "log_encoding_DJIDLog", + "log_decoding_DJIDLog", + "exponent_function_basic", + "exponent_function_monitor_curve", + "log_encoding_FilmicPro6", + "log_decoding_FilmicPro6", + "log_encoding_FilmLightTLog", + "log_decoding_FilmLightTLog", + "log_encoding_Protune", + "log_decoding_Protune", + "oetf_BT601", + "oetf_inverse_BT601", + "oetf_BT709", + "oetf_inverse_BT709", + "eotf_inverse_BT1886", + "eotf_BT1886", + "eotf_inverse_BT2020", + "eotf_BT2020", + "eotf_inverse_ST2084", + "eotf_ST2084", + "oetf_PQ_BT2100", + "oetf_inverse_PQ_BT2100", + "eotf_PQ_BT2100", + "eotf_inverse_PQ_BT2100", + "ootf_PQ_BT2100", + "ootf_inverse_PQ_BT2100", + "oetf_HLG_BT2100", + "oetf_inverse_HLG_BT2100", + "BT2100_HLG_EOTF_METHODS", + "eotf_HLG_BT2100", + "BT2100_HLG_EOTF_INVERSE_METHODS", + "eotf_inverse_HLG_BT2100", + "BT2100_HLG_OOTF_METHODS", + "ootf_HLG_BT2100", + "BT2100_HLG_OOTF_INVERSE_METHODS", + "ootf_inverse_HLG_BT2100", + "linear_function", + "logarithmic_function_basic", + "logarithmic_function_quasilog", + "logarithmic_function_camera", + "log_encoding_Log2", + "log_decoding_Log2", + "log_encoding_Panalog", + "log_decoding_Panalog", + "log_encoding_VLog", + "log_decoding_VLog", + "log_encoding_FLog", + "log_decoding_FLog", + "log_encoding_NLog", + "log_decoding_NLog", + "log_encoding_PivotedLog", + "log_decoding_PivotedLog", + "log_encoding_REDLog", + "log_decoding_REDLog", + "log_encoding_REDLogFilm", + "log_decoding_REDLogFilm", + "LOG3G10_ENCODING_METHODS", + "LOG3G10_DECODING_METHODS", + "log_encoding_Log3G10", + "log_decoding_Log3G10", + "log_encoding_Log3G12", + "log_decoding_Log3G12", + "cctf_encoding_ROMMRGB", + "cctf_decoding_ROMMRGB", + "cctf_encoding_ProPhotoRGB", + "cctf_decoding_ProPhotoRGB", + "cctf_encoding_RIMMRGB", + "cctf_decoding_RIMMRGB", + "log_encoding_ERIMMRGB", + "log_decoding_ERIMMRGB", + "oetf_SMPTE240M", + "eotf_SMPTE240M", + "log_encoding_SLog", + "log_decoding_SLog", + "log_encoding_SLog2", + "log_decoding_SLog2", + "log_encoding_SLog3", + "log_decoding_SLog3", + "eotf_inverse_sRGB", + "eotf_sRGB", + "log_encoding_ViperLog", + "log_decoding_ViperLog", +] +__all__ += [ + "LOG_ENCODINGS", + "log_encoding", + "LOG_DECODINGS", + "log_decoding", + "OETFS", + "oetf", + "OETF_INVERSES", + "oetf_inverse", + "EOTFS", + "eotf", + "EOTF_INVERSES", + "eotf_inverse", + "CCTF_ENCODINGS", + "cctf_encoding", + "CCTF_DECODINGS", + "cctf_decoding", + "OOTFS", + "ootf", + "OOTF_INVERSES", + "ootf_inverse", +] +__all__ += [ + "RGB_COLOURSPACES", + "RGB_COLOURSPACE_ACES2065_1", + "RGB_COLOURSPACE_ACESCC", + "RGB_COLOURSPACE_ACESCCT", + "RGB_COLOURSPACE_ACESPROXY", + "RGB_COLOURSPACE_ACESCG", + "RGB_COLOURSPACE_ADOBE_RGB1998", + "RGB_COLOURSPACE_ADOBE_WIDE_GAMUT_RGB", + "RGB_COLOURSPACE_ALEXA_WIDE_GAMUT", + "RGB_COLOURSPACE_APPLE_RGB", + "RGB_COLOURSPACE_BEST_RGB", + "RGB_COLOURSPACE_BETA_RGB", + "RGB_COLOURSPACE_BLACKMAGIC_WIDE_GAMUT", + "RGB_COLOURSPACE_BT470_525", + "RGB_COLOURSPACE_BT470_625", + "RGB_COLOURSPACE_BT709", + "RGB_COLOURSPACE_BT2020", + "RGB_COLOURSPACE_CIE_RGB", + "RGB_COLOURSPACE_CINEMA_GAMUT", + "RGB_COLOURSPACE_COLOR_MATCH_RGB", + "RGB_COLOURSPACE_DAVINCI_WIDE_GAMUT", + "RGB_COLOURSPACE_DCDM_XYZ", + "RGB_COLOURSPACE_DCI_P3", + "RGB_COLOURSPACE_DCI_P3_P", + "RGB_COLOURSPACE_DISPLAY_P3", + "RGB_COLOURSPACE_DJI_D_GAMUT", + "RGB_COLOURSPACE_DON_RGB_4", + "RGB_COLOURSPACE_ECI_RGB_V2", + "RGB_COLOURSPACE_EKTA_SPACE_PS_5", + "RGB_COLOURSPACE_FILMLIGHT_E_GAMUT", + "RGB_COLOURSPACE_PROTUNE_NATIVE", + "RGB_COLOURSPACE_MAX_RGB", + "RGB_COLOURSPACE_N_GAMUT", + "RGB_COLOURSPACE_P3_D65", + "RGB_COLOURSPACE_PAL_SECAM", + "RGB_COLOURSPACE_RED_COLOR", + "RGB_COLOURSPACE_RED_COLOR_2", + "RGB_COLOURSPACE_RED_COLOR_3", + "RGB_COLOURSPACE_RED_COLOR_4", + "RGB_COLOURSPACE_DRAGON_COLOR", + "RGB_COLOURSPACE_DRAGON_COLOR_2", + "RGB_COLOURSPACE_RED_WIDE_GAMUT_RGB", + "RGB_COLOURSPACE_ROMM_RGB", + "RGB_COLOURSPACE_RIMM_RGB", + "RGB_COLOURSPACE_ERIMM_RGB", + "RGB_COLOURSPACE_PROPHOTO_RGB", + "RGB_COLOURSPACE_RUSSELL_RGB", + "RGB_COLOURSPACE_SHARP_RGB", + "RGB_COLOURSPACE_SMPTE_240M", + "RGB_COLOURSPACE_SMPTE_C", + "RGB_COLOURSPACE_NTSC1953", + "RGB_COLOURSPACE_NTSC1987", + "RGB_COLOURSPACE_S_GAMUT", + "RGB_COLOURSPACE_S_GAMUT3", + "RGB_COLOURSPACE_S_GAMUT3_CINE", + "RGB_COLOURSPACE_VENICE_S_GAMUT3", + "RGB_COLOURSPACE_VENICE_S_GAMUT3_CINE", + "RGB_COLOURSPACE_sRGB", + "RGB_COLOURSPACE_V_GAMUT", + "RGB_COLOURSPACE_XTREME_RGB", + "RGB_COLOURSPACE_F_GAMUT", +] +__all__ += [ + "XYZ_to_sRGB", + "sRGB_to_XYZ", +] +__all__ += [ + "RGB_to_HSV", + "HSV_to_RGB", + "RGB_to_HSL", + "HSL_to_RGB", + "RGB_to_HCL", + "HCL_to_RGB", +] +__all__ += [ + "RGB_to_CMY", + "CMY_to_RGB", + "CMY_to_CMYK", + "CMYK_to_CMY", +] +__all__ += [ + "RGB_to_IHLS", + "IHLS_to_RGB", +] +__all__ += [ + "RGB_to_Prismatic", + "Prismatic_to_RGB", +] +__all__ += [ + "WEIGHTS_YCBCR", + "matrix_YCbCr", + "offset_YCbCr", + "RGB_to_YCbCr", + "YCbCr_to_RGB", + "RGB_to_YcCbcCrc", + "YcCbcCrc_to_RGB", +] +__all__ += [ + "RGB_to_YCoCg", + "YCoCg_to_RGB", +] +__all__ += [ + "RGB_to_ICtCp", + "ICtCp_to_RGB", + "XYZ_to_ICtCp", + "ICtCp_to_XYZ", +] diff --git a/colour/models/rgb/cmyk.py b/colour/models/rgb/cmyk.py index 578f5bcd8f..e3d50ccd07 100644 --- a/colour/models/rgb/cmyk.py +++ b/colour/models/rgb/cmyk.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ CMYK Colour Transformations =========================== @@ -22,40 +21,50 @@ from http://www.easyrgb.com/index.php?X=MATH&H=13#text13 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.utilities import (as_float_array, from_range_1, to_domain_1, - tsplit, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['RGB_to_CMY', 'CMY_to_RGB', 'CMY_to_CMYK', 'CMYK_to_CMY'] - - -def RGB_to_CMY(RGB): +from colour.hints import ArrayLike, NDArray +from colour.utilities import ( + as_float_array, + from_range_1, + to_domain_1, + tsplit, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "RGB_to_CMY", + "CMY_to_RGB", + "CMY_to_CMYK", + "CMYK_to_CMY", +] + + +def RGB_to_CMY(RGB: ArrayLike) -> NDArray: """ - Converts from *RGB* colourspace to *CMY* colourspace. + Convert from *RGB* colourspace to *CMY* colourspace. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *CMY* array. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -84,23 +93,22 @@ def RGB_to_CMY(RGB): return from_range_1(CMY) -def CMY_to_RGB(CMY): +def CMY_to_RGB(CMY: ArrayLike) -> NDArray: """ - Converts from *CMY* colourspace to *CMY* colourspace. + Convert from *CMY* colourspace to *CMY* colourspace. Parameters ---------- - CMY : array_like + CMY *CMY* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *RGB* colourspace array. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -129,23 +137,22 @@ def CMY_to_RGB(CMY): return from_range_1(RGB) -def CMY_to_CMYK(CMY): +def CMY_to_CMYK(CMY: ArrayLike) -> NDArray: """ - Converts from *CMY* colourspace to *CMYK* colourspace. + Convert from *CMY* colourspace to *CMYK* colourspace. Parameters ---------- - CMY : array_like + CMY *CMY* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *CMYK* array. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -188,23 +195,22 @@ def CMY_to_CMYK(CMY): return from_range_1(CMYK) -def CMYK_to_CMY(CMYK): +def CMYK_to_CMY(CMYK: ArrayLike) -> NDArray: """ - Converts from *CMYK* colourspace to *CMY* colourspace. + Convert from *CMYK* colourspace to *CMY* colourspace. Parameters ---------- - CMYK : array_like + CMYK *CMYK* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *CMY* array. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ diff --git a/colour/models/rgb/common.py b/colour/models/rgb/common.py index f79c6bfd4b..19fb7e6d72 100644 --- a/colour/models/rgb/common.py +++ b/colour/models/rgb/common.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Common RGB Colour Models Utilities ================================== @@ -6,59 +5,71 @@ Defines various RGB colour models common utilities. """ -from __future__ import division, unicode_literals +from __future__ import annotations from colour.colorimetry import CCS_ILLUMINANTS +from colour.hints import ArrayLike, Boolean, Literal, NDArray, Union from colour.models.rgb import RGB_COLOURSPACES, RGB_to_XYZ, XYZ_to_RGB -from colour.utilities.deprecation import handle_arguments_deprecation -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['XYZ_to_sRGB', 'sRGB_to_XYZ'] - - -def XYZ_to_sRGB(XYZ, - illuminant=CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer']['D65'], - chromatic_adaptation_transform='CAT02', - apply_cctf_encoding=True, - **kwargs): +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "XYZ_to_sRGB", + "sRGB_to_XYZ", +] + + +def XYZ_to_sRGB( + XYZ: ArrayLike, + illuminant: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], + chromatic_adaptation_transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", + apply_cctf_encoding: Boolean = True, +) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to *sRGB* colourspace. + Convert from *CIE XYZ* tristimulus values to *sRGB* colourspace. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. - illuminant : array_like, optional + illuminant Source illuminant chromaticity coordinates. - chromatic_adaptation_transform : unicode, optional - **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', - 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02 Brill 2008', - 'Bianco 2010', 'Bianco PC 2010'}**, + chromatic_adaptation_transform *Chromatic adaptation* transform. - apply_cctf_encoding : bool, optional - Apply *sRGB* encoding colour component transfer function / - opto-electronic transfer function. - - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. + apply_cctf_encoding + Whether to apply the *sRGB* encoding colour component transfer function + / inverse electro-optical transfer function. Returns ------- - ndarray + :class:`numpy.ndarray` *sRGB* colour array. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -79,11 +90,7 @@ def XYZ_to_sRGB(XYZ, array([ 0.7057393..., 0.1924826..., 0.2235416...]) """ - apply_cctf_encoding = handle_arguments_deprecation({ - 'ArgumentRenamed': [['apply_encoding_cctf', 'apply_cctf_encoding']], - }, **kwargs).get('apply_cctf_encoding', apply_cctf_encoding) - - sRGB = RGB_COLOURSPACES['sRGB'] + sRGB = RGB_COLOURSPACES["sRGB"] return XYZ_to_RGB( XYZ, @@ -95,43 +102,52 @@ def XYZ_to_sRGB(XYZ, ) -def sRGB_to_XYZ(RGB, - illuminant=CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer']['D65'], - chromatic_adaptation_method='CAT02', - apply_cctf_decoding=True, - **kwargs): +def sRGB_to_XYZ( + RGB: ArrayLike, + illuminant: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], + chromatic_adaptation_transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", + apply_cctf_decoding: Boolean = True, +) -> NDArray: """ - Converts from *sRGB* colourspace to *CIE XYZ* tristimulus values. + Convert from *sRGB* colourspace to *CIE XYZ* tristimulus values. Parameters ---------- - RGB : array_like + RGB *sRGB* colourspace array. - illuminant : array_like, optional + illuminant Source illuminant chromaticity coordinates. - chromatic_adaptation_method : unicode, optional - **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', - 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02 Brill 2008', - 'Bianco 2010', 'Bianco PC 2010'}**, - *Chromatic adaptation* method. - apply_cctf_decoding : bool, optional - Apply *sRGB* decoding colour component transfer function / - electro-optical transfer function. - - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. + chromatic_adaptation_transform + *Chromatic adaptation* transform. + apply_cctf_decoding + Whether to apply the *sRGB* decoding colour component transfer function + / electro-optical transfer function. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -152,17 +168,13 @@ def sRGB_to_XYZ(RGB, array([ 0.2065429..., 0.1219794..., 0.0513714...]) """ - apply_cctf_decoding = handle_arguments_deprecation({ - 'ArgumentRenamed': [['apply_decoding_cctf', 'apply_cctf_decoding']], - }, **kwargs).get('apply_cctf_decoding', apply_cctf_decoding) - - sRGB = RGB_COLOURSPACES['sRGB'] + sRGB = RGB_COLOURSPACES["sRGB"] return RGB_to_XYZ( RGB, sRGB.whitepoint, illuminant, sRGB.matrix_RGB_to_XYZ, - chromatic_adaptation_method, + chromatic_adaptation_transform, sRGB.cctf_decoding if apply_cctf_decoding else None, ) diff --git a/colour/models/rgb/cylindrical.py b/colour/models/rgb/cylindrical.py index 6bf9ec1693..988118ccd8 100644 --- a/colour/models/rgb/cylindrical.py +++ b/colour/models/rgb/cylindrical.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Cylindrical & Spherical Colour Models ===================================== @@ -9,6 +8,8 @@ - :func:`colour.HSV_to_RGB` - :func:`colour.RGB_to_HSL` - :func:`colour.HSL_to_RGB` +- :func:`colour.RGB_to_HCL` +- :func:`colour.HCL_to_RGB` These colour models trade off perceptual relevance for computation speed. They should not be used in the colour science domain although they are useful @@ -31,42 +32,59 @@ Interactive Techniques - SIGGRAPH "78, 12-19. doi:10.1145/800248.807361 - :cite:`Wikipedia2003` : Wikipedia. (2003). HSL and HSV. Retrieved September 10, 2014, from http://en.wikipedia.org/wiki/HSL_and_HSV +- :cite:`Sarifuddin2005` : Sarifuddin, M., & Missaoui, R. (2005). A New + Perceptually Uniform Color Space with Associated Color Similarity Measure + for ContentBased Image and Video Retrieval. +- :cite:`Wikipedia2015` : Wikipedia. (2015). HCL color space. Retrieved + April 4, 2021, from https://en.wikipedia.org/wiki/HCL_color_space """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.utilities import (as_float_array, from_range_1, to_domain_1, - tsplit, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['RGB_to_HSV', 'HSV_to_RGB', 'RGB_to_HSL', 'HSL_to_RGB'] - - -def RGB_to_HSV(RGB): +from colour.hints import ArrayLike, Floating, NDArray +from colour.utilities import ( + as_float_array, + from_range_1, + to_domain_1, + tsplit, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "RGB_to_HSV", + "HSV_to_RGB", + "RGB_to_HSL", + "HSL_to_RGB", + "RGB_to_HCL", + "HCL_to_RGB", +] + + +def RGB_to_HSV(RGB: ArrayLike) -> NDArray: """ - Converts from *RGB* colourspace to *HSV* colourspace. + Convert from *RGB* colourspace to *HSV* colourspace. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *HSV* array. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -118,23 +136,22 @@ def RGB_to_HSV(RGB): return from_range_1(HSV) -def HSV_to_RGB(HSV): +def HSV_to_RGB(HSV: ArrayLike) -> NDArray: """ - Converts from *HSV* colourspace to *RGB* colourspace. + Convert from *HSV* colourspace to *RGB* colourspace. Parameters ---------- - HSV : array_like + HSV *HSV* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *RGB* colourspace array. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -171,7 +188,8 @@ def HSV_to_RGB(HSV): i = tstack([i, i, i]).astype(np.uint8) RGB = np.choose( - i, [ + i, + [ tstack([V, l, j]), tstack([k, V, j]), tstack([j, V, l]), @@ -179,28 +197,28 @@ def HSV_to_RGB(HSV): tstack([l, j, V]), tstack([V, j, k]), ], - mode='clip') + mode="clip", + ) return from_range_1(RGB) -def RGB_to_HSL(RGB): +def RGB_to_HSL(RGB: ArrayLike) -> NDArray: """ - Converts from *RGB* colourspace to *HSL* colourspace. + Convert from *RGB* colourspace to *HSL* colourspace. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *HSL* array. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -257,23 +275,22 @@ def RGB_to_HSL(RGB): return from_range_1(HSL) -def HSL_to_RGB(HSL): +def HSL_to_RGB(HSL: ArrayLike) -> NDArray: """ - Converts from *HSL* colourspace to *RGB* colourspace. + Convert from *HSL* colourspace to *RGB* colourspace. Parameters ---------- - HSL : array_like + HSL *HSL* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *RGB* colourspace array. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -299,10 +316,8 @@ def HSL_to_RGB(HSL): H, S, L = tsplit(to_domain_1(HSL)) - def H_to_RGB(vi, vj, vH): - """ - Converts *hue* value to *RGB* colourspace. - """ + def H_to_RGB(vi: NDArray, vj: NDArray, vH: NDArray) -> NDArray: + """Convert *hue* value to *RGB* colourspace.""" vH = as_float_array(vH) @@ -338,3 +353,212 @@ def H_to_RGB(vi, vj, vH): RGB = tstack([R, G, B]) return from_range_1(RGB) + + +def RGB_to_HCL( + RGB: ArrayLike, gamma: Floating = 3, Y_0: Floating = 100 +) -> NDArray: + """ + Convert from *RGB* colourspace to *HCL* colourspace according to + *Sarifuddin and Missaoui (2005)* method. + + Parameters + ---------- + RGB + *RGB* colourspace array. + gamma + Non-linear lightness exponent matching *Lightness* :math:`L^*`. + Y_0 + White reference luminance :math:`Y_0`. + + Returns + ------- + :class:`numpy.ndarray` + *HCL* array. + + Notes + ----- + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``RGB`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + +------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``HCL`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + References + ---------- + :cite:`Sarifuddin2005`, :cite:`Wikipedia2015` + + Examples + -------- + >>> RGB = np.array([0.45620519, 0.03081071, 0.04091952]) + >>> RGB_to_HCL(RGB) # doctest: +ELLIPSIS + array([-0.0316785..., 0.2841715..., 0.2285964...]) + """ + + R, G, B = tsplit(to_domain_1(RGB)) + + Min = np.minimum(np.minimum(R, G), B) + Max = np.maximum(np.maximum(R, G), B) + + alpha = (Min / Max) / Y_0 + + Q = np.exp(alpha * gamma) + + L = (Q * Max + (Q - 1) * Min) / 2 + + R_G = R - G + G_B = G - B + B_R = B - R + + C = Q * (np.abs(R_G) + np.abs(G_B) + np.abs(B_R)) / 3 + + H = np.arctan(G_B / R_G) + + _2_3_H = 2 / 3 * H + _4_3_H = 4 / 3 * H + + H = np.select( + [ + np.logical_and(R_G >= 0, G_B >= 0), + np.logical_and(R_G >= 0, G_B < 0), + np.logical_and(R_G < 0, G_B >= 0), + np.logical_and(R_G < 0, G_B < 0), + ], + [ + _2_3_H, + _4_3_H, + np.pi + _4_3_H, + _2_3_H - np.pi, + ], + ) + + HCL = tstack([H, C, L]) + + return from_range_1(HCL) + + +def HCL_to_RGB( + HCL: ArrayLike, gamma: Floating = 3, Y_0: Floating = 100 +) -> NDArray: + """ + Convert from *HCL* colourspace to *RGB* colourspace according to + *Sarifuddin and Missaoui (2005)* method. + + Parameters + ---------- + HCL + *HCL* colourspace array. + gamma + Non-linear lightness exponent matching *Lightness* :math:`L^*`. + Y_0 + White reference luminance :math:`Y_0`. + + Returns + ------- + :class:`numpy.ndarray` + *RGB* colourspace array. + + Notes + ----- + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``HCL`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + +------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``RGB`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + References + ---------- + :cite:`Sarifuddin2005`, :cite:`Wikipedia2015` + + Examples + -------- + >>> HCL = np.array([-0.03167854, 0.28417150, 0.22859647]) + >>> HCL_to_RGB(HCL) # doctest: +ELLIPSIS + array([ 0.4562033..., 0.0308104..., 0.0409192...]) + """ + + H, C, L = tsplit(to_domain_1(HCL)) + + Q = np.exp((1 - (3 * C) / (4 * L)) * (gamma / Y_0)) + + Min = (4 * L - 3 * C) / (4 * Q - 2) + Max = Min + (3 * C) / (2 * Q) + + def _1_2_3(a: ArrayLike) -> NDArray: + """Tail-stack given :math:`a` array as a *bool* dtype.""" + + return tstack([a, a, a], dtype=np.bool_) + + tan_3_2_H = np.tan(3 / 2 * H) + tan_3_4_H_MP = np.tan(3 / 4 * (H - np.pi)) + tan_3_4_H = np.tan(3 / 4 * H) + tan_3_2_H_PP = np.tan(3 / 2 * (H + np.pi)) + + RGB = np.select( + [ + _1_2_3(np.logical_and(0 <= H, H <= np.radians(60))), + _1_2_3(np.logical_and(np.radians(60) < H, H <= np.radians(120))), + _1_2_3(np.logical_and(np.radians(120) < H, H <= np.pi)), + _1_2_3(np.logical_and(np.radians(-60) <= H, H < 0)), + _1_2_3(np.logical_and(np.radians(-120) <= H, H < np.radians(-60))), + _1_2_3(np.logical_and(-np.pi < H, H < np.radians(-120))), + ], + [ + tstack( + [ + Max, + (Max * tan_3_2_H + Min) / (1 + tan_3_2_H), + Min, + ] + ), + tstack( + [ + (Max * (1 + tan_3_4_H_MP) - Min) / tan_3_4_H_MP, + Max, + Min, + ] + ), + tstack( + [ + Min, + Max, + Max * (1 + tan_3_4_H_MP) - Min * tan_3_4_H_MP, + ] + ), + tstack( + [ + Max, + Min, + Min * (1 + tan_3_4_H) - Max * tan_3_4_H, + ] + ), + tstack( + [ + (Min * (1 + tan_3_4_H) - Max) / tan_3_4_H, + Min, + Max, + ] + ), + tstack( + [ + Min, + (Min * tan_3_2_H_PP + Max) / (1 + tan_3_2_H_PP), + Max, + ] + ), + ], + ) + + return from_range_1(RGB) diff --git a/colour/models/rgb/datasets/__init__.py b/colour/models/rgb/datasets/__init__.py index 741b445a2a..28617a8e3d 100644 --- a/colour/models/rgb/datasets/__init__.py +++ b/colour/models/rgb/datasets/__init__.py @@ -1,17 +1,20 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import +from __future__ import annotations from colour.utilities import CaseInsensitiveMapping -from .aces import (RGB_COLOURSPACE_ACES2065_1, RGB_COLOURSPACE_ACESCC, - RGB_COLOURSPACE_ACESCCT, RGB_COLOURSPACE_ACESCG, - RGB_COLOURSPACE_ACESPROXY) +from .aces import ( + RGB_COLOURSPACE_ACES2065_1, + RGB_COLOURSPACE_ACESCC, + RGB_COLOURSPACE_ACESCCT, + RGB_COLOURSPACE_ACESCG, + RGB_COLOURSPACE_ACESPROXY, +) from .adobe_rgb_1998 import RGB_COLOURSPACE_ADOBE_RGB1998 from .adobe_wide_gamut_rgb import RGB_COLOURSPACE_ADOBE_WIDE_GAMUT_RGB from .apple_rgb import RGB_COLOURSPACE_APPLE_RGB from .arri_alexa_wide_gamut import RGB_COLOURSPACE_ALEXA_WIDE_GAMUT from .best_rgb import RGB_COLOURSPACE_BEST_RGB from .beta_rgb import RGB_COLOURSPACE_BETA_RGB +from .blackmagic_design import RGB_COLOURSPACE_BLACKMAGIC_WIDE_GAMUT from .cie_rgb import RGB_COLOURSPACE_CIE_RGB from .canon_cinema_gamut import RGB_COLOURSPACE_CINEMA_GAMUT from .color_match_rgb import RGB_COLOURSPACE_COLOR_MATCH_RGB @@ -31,194 +34,131 @@ from .itur_bt_709 import RGB_COLOURSPACE_BT709 from .itur_bt_2020 import RGB_COLOURSPACE_BT2020 from .max_rgb import RGB_COLOURSPACE_MAX_RGB +from .nikon_n_gamut import RGB_COLOURSPACE_N_GAMUT from .pal_secam import RGB_COLOURSPACE_PAL_SECAM -from .red import (RGB_COLOURSPACE_RED_COLOR, RGB_COLOURSPACE_RED_COLOR_2, - RGB_COLOURSPACE_RED_COLOR_3, RGB_COLOURSPACE_RED_COLOR_4, - RGB_COLOURSPACE_DRAGON_COLOR, RGB_COLOURSPACE_DRAGON_COLOR_2, - RGB_COLOURSPACE_RED_WIDE_GAMUT_RGB) -from .rimm_romm_rgb import (RGB_COLOURSPACE_ROMM_RGB, RGB_COLOURSPACE_RIMM_RGB, - RGB_COLOURSPACE_ERIMM_RGB, - RGB_COLOURSPACE_PROPHOTO_RGB) +from .red import ( + RGB_COLOURSPACE_RED_COLOR, + RGB_COLOURSPACE_RED_COLOR_2, + RGB_COLOURSPACE_RED_COLOR_3, + RGB_COLOURSPACE_RED_COLOR_4, + RGB_COLOURSPACE_DRAGON_COLOR, + RGB_COLOURSPACE_DRAGON_COLOR_2, + RGB_COLOURSPACE_RED_WIDE_GAMUT_RGB, +) +from .rimm_romm_rgb import ( + RGB_COLOURSPACE_ROMM_RGB, + RGB_COLOURSPACE_RIMM_RGB, + RGB_COLOURSPACE_ERIMM_RGB, + RGB_COLOURSPACE_PROPHOTO_RGB, +) from .russell_rgb import RGB_COLOURSPACE_RUSSELL_RGB from .sharp import RGB_COLOURSPACE_SHARP_RGB from .smpte_240m import RGB_COLOURSPACE_SMPTE_240M from .smpte_c import RGB_COLOURSPACE_SMPTE_C from .ntsc import RGB_COLOURSPACE_NTSC1953, RGB_COLOURSPACE_NTSC1987 -from .sony import (RGB_COLOURSPACE_S_GAMUT, RGB_COLOURSPACE_S_GAMUT3, - RGB_COLOURSPACE_S_GAMUT3_CINE, - RGB_COLOURSPACE_VENICE_S_GAMUT3, - RGB_COLOURSPACE_VENICE_S_GAMUT3_CINE) +from .sony import ( + RGB_COLOURSPACE_S_GAMUT, + RGB_COLOURSPACE_S_GAMUT3, + RGB_COLOURSPACE_S_GAMUT3_CINE, + RGB_COLOURSPACE_VENICE_S_GAMUT3, + RGB_COLOURSPACE_VENICE_S_GAMUT3_CINE, +) from .srgb import RGB_COLOURSPACE_sRGB from .panasonic_v_gamut import RGB_COLOURSPACE_V_GAMUT from .xtreme_rgb import RGB_COLOURSPACE_XTREME_RGB -RGB_COLOURSPACES = CaseInsensitiveMapping({ - RGB_COLOURSPACE_ACES2065_1.name: - RGB_COLOURSPACE_ACES2065_1, - RGB_COLOURSPACE_ACESCC.name: - RGB_COLOURSPACE_ACESCC, - RGB_COLOURSPACE_ACESCCT.name: - RGB_COLOURSPACE_ACESCCT, - RGB_COLOURSPACE_ACESPROXY.name: - RGB_COLOURSPACE_ACESPROXY, - RGB_COLOURSPACE_ACESCG.name: - RGB_COLOURSPACE_ACESCG, - RGB_COLOURSPACE_ADOBE_RGB1998.name: - RGB_COLOURSPACE_ADOBE_RGB1998, - RGB_COLOURSPACE_ADOBE_WIDE_GAMUT_RGB.name: - RGB_COLOURSPACE_ADOBE_WIDE_GAMUT_RGB, - RGB_COLOURSPACE_APPLE_RGB.name: - RGB_COLOURSPACE_APPLE_RGB, - RGB_COLOURSPACE_ALEXA_WIDE_GAMUT.name: - RGB_COLOURSPACE_ALEXA_WIDE_GAMUT, - RGB_COLOURSPACE_BEST_RGB.name: - RGB_COLOURSPACE_BEST_RGB, - RGB_COLOURSPACE_BETA_RGB.name: - RGB_COLOURSPACE_BETA_RGB, - RGB_COLOURSPACE_BT470_525.name: - RGB_COLOURSPACE_BT470_525, - RGB_COLOURSPACE_BT470_625.name: - RGB_COLOURSPACE_BT470_625, - RGB_COLOURSPACE_BT709.name: - RGB_COLOURSPACE_BT709, - RGB_COLOURSPACE_BT2020.name: - RGB_COLOURSPACE_BT2020, - RGB_COLOURSPACE_CIE_RGB.name: - RGB_COLOURSPACE_CIE_RGB, - RGB_COLOURSPACE_CINEMA_GAMUT.name: - RGB_COLOURSPACE_CINEMA_GAMUT, - RGB_COLOURSPACE_COLOR_MATCH_RGB.name: - RGB_COLOURSPACE_COLOR_MATCH_RGB, - RGB_COLOURSPACE_DAVINCI_WIDE_GAMUT.name: - RGB_COLOURSPACE_DAVINCI_WIDE_GAMUT, - RGB_COLOURSPACE_DCDM_XYZ.name: - RGB_COLOURSPACE_DCDM_XYZ, - RGB_COLOURSPACE_DCI_P3.name: - RGB_COLOURSPACE_DCI_P3, - RGB_COLOURSPACE_DCI_P3_P.name: - RGB_COLOURSPACE_DCI_P3_P, - RGB_COLOURSPACE_DISPLAY_P3.name: - RGB_COLOURSPACE_DISPLAY_P3, - RGB_COLOURSPACE_DJI_D_GAMUT.name: - RGB_COLOURSPACE_DJI_D_GAMUT, - RGB_COLOURSPACE_DON_RGB_4.name: - RGB_COLOURSPACE_DON_RGB_4, - RGB_COLOURSPACE_ECI_RGB_V2.name: - RGB_COLOURSPACE_ECI_RGB_V2, - RGB_COLOURSPACE_EKTA_SPACE_PS_5.name: - RGB_COLOURSPACE_EKTA_SPACE_PS_5, - RGB_COLOURSPACE_FILMLIGHT_E_GAMUT.name: - RGB_COLOURSPACE_FILMLIGHT_E_GAMUT, - RGB_COLOURSPACE_PROTUNE_NATIVE.name: - RGB_COLOURSPACE_PROTUNE_NATIVE, - RGB_COLOURSPACE_MAX_RGB.name: - RGB_COLOURSPACE_MAX_RGB, - RGB_COLOURSPACE_P3_D65.name: - RGB_COLOURSPACE_P3_D65, - RGB_COLOURSPACE_PAL_SECAM.name: - RGB_COLOURSPACE_PAL_SECAM, - RGB_COLOURSPACE_RED_COLOR.name: - RGB_COLOURSPACE_RED_COLOR, - RGB_COLOURSPACE_RED_COLOR_2.name: - RGB_COLOURSPACE_RED_COLOR_2, - RGB_COLOURSPACE_RED_COLOR_3.name: - RGB_COLOURSPACE_RED_COLOR_3, - RGB_COLOURSPACE_RED_COLOR_4.name: - RGB_COLOURSPACE_RED_COLOR_4, - RGB_COLOURSPACE_RED_WIDE_GAMUT_RGB.name: - RGB_COLOURSPACE_RED_WIDE_GAMUT_RGB, - RGB_COLOURSPACE_DRAGON_COLOR.name: - RGB_COLOURSPACE_DRAGON_COLOR, - RGB_COLOURSPACE_DRAGON_COLOR_2.name: - RGB_COLOURSPACE_DRAGON_COLOR_2, - RGB_COLOURSPACE_ROMM_RGB.name: - RGB_COLOURSPACE_ROMM_RGB, - RGB_COLOURSPACE_RIMM_RGB.name: - RGB_COLOURSPACE_RIMM_RGB, - RGB_COLOURSPACE_ERIMM_RGB.name: - RGB_COLOURSPACE_ERIMM_RGB, - RGB_COLOURSPACE_F_GAMUT.name: - RGB_COLOURSPACE_F_GAMUT, - RGB_COLOURSPACE_PROPHOTO_RGB.name: - RGB_COLOURSPACE_PROPHOTO_RGB, - RGB_COLOURSPACE_RUSSELL_RGB.name: - RGB_COLOURSPACE_RUSSELL_RGB, - RGB_COLOURSPACE_SHARP_RGB.name: - RGB_COLOURSPACE_SHARP_RGB, - RGB_COLOURSPACE_SMPTE_240M.name: - RGB_COLOURSPACE_SMPTE_240M, - RGB_COLOURSPACE_SMPTE_C.name: - RGB_COLOURSPACE_SMPTE_C, - RGB_COLOURSPACE_NTSC1953.name: - RGB_COLOURSPACE_NTSC1953, - RGB_COLOURSPACE_NTSC1987.name: - RGB_COLOURSPACE_NTSC1987, - RGB_COLOURSPACE_S_GAMUT.name: - RGB_COLOURSPACE_S_GAMUT, - RGB_COLOURSPACE_S_GAMUT3.name: - RGB_COLOURSPACE_S_GAMUT3, - RGB_COLOURSPACE_S_GAMUT3_CINE.name: - RGB_COLOURSPACE_S_GAMUT3_CINE, - RGB_COLOURSPACE_VENICE_S_GAMUT3.name: - RGB_COLOURSPACE_VENICE_S_GAMUT3, - RGB_COLOURSPACE_VENICE_S_GAMUT3_CINE.name: - RGB_COLOURSPACE_VENICE_S_GAMUT3_CINE, - RGB_COLOURSPACE_sRGB.name: - RGB_COLOURSPACE_sRGB, - RGB_COLOURSPACE_V_GAMUT.name: - RGB_COLOURSPACE_V_GAMUT, - RGB_COLOURSPACE_XTREME_RGB.name: - RGB_COLOURSPACE_XTREME_RGB -}) +from colour.models.rgb import RGB_Colourspace + +RGB_COLOURSPACES: CaseInsensitiveMapping = CaseInsensitiveMapping( + dict( + sorted( + (colourspace.name, colourspace) + for colourspace in locals().values() + if isinstance(colourspace, RGB_Colourspace) + ) + ) +) RGB_COLOURSPACES.__doc__ = """ Aggregated *RGB* colourspaces. -RGB_COLOURSPACES : CaseInsensitiveMapping - Aliases: - 'aces': RGB_COLOURSPACE_ACES2065_1.name - 'adobe1998': RGB_COLOURSPACE_ADOBE_RGB1998.name - 'prophoto': RGB_COLOURSPACE_PROPHOTO_RGB.name """ -# yapf: disable -RGB_COLOURSPACES['aces'] = ( - RGB_COLOURSPACES[RGB_COLOURSPACE_ACES2065_1.name]) -RGB_COLOURSPACES['adobe1998'] = ( - RGB_COLOURSPACES[RGB_COLOURSPACE_ADOBE_RGB1998.name]) -RGB_COLOURSPACES['prophoto'] = ( - RGB_COLOURSPACES[RGB_COLOURSPACE_PROPHOTO_RGB.name]) + +RGB_COLOURSPACES["aces"] = RGB_COLOURSPACES[RGB_COLOURSPACE_ACES2065_1.name] +RGB_COLOURSPACES["adobe1998"] = RGB_COLOURSPACES[ + RGB_COLOURSPACE_ADOBE_RGB1998.name +] +RGB_COLOURSPACES["prophoto"] = RGB_COLOURSPACES[ + RGB_COLOURSPACE_PROPHOTO_RGB.name +] # yapf: enable -__all__ = ['RGB_COLOURSPACES'] +__all__ = [ + "RGB_COLOURSPACES", +] __all__ += [ - 'RGB_COLOURSPACE_ACES2065_1', 'RGB_COLOURSPACE_ACESCC', - 'RGB_COLOURSPACE_ACESCCT', 'RGB_COLOURSPACE_ACESPROXY', - 'RGB_COLOURSPACE_ACESCG', 'RGB_COLOURSPACE_ADOBE_RGB1998', - 'RGB_COLOURSPACE_ADOBE_WIDE_GAMUT_RGB', 'RGB_COLOURSPACE_ALEXA_WIDE_GAMUT', - 'RGB_COLOURSPACE_APPLE_RGB', 'RGB_COLOURSPACE_BEST_RGB', - 'RGB_COLOURSPACE_BETA_RGB', 'RGB_COLOURSPACE_BT470_525', - 'RGB_COLOURSPACE_BT470_625', 'RGB_COLOURSPACE_BT709', - 'RGB_COLOURSPACE_BT2020', 'RGB_COLOURSPACE_CIE_RGB', - 'RGB_COLOURSPACE_CINEMA_GAMUT', 'RGB_COLOURSPACE_COLOR_MATCH_RGB', - 'RGB_COLOURSPACE_DAVINCI_WIDE_GAMUT', 'RGB_COLOURSPACE_DCDM_XYZ', - 'RGB_COLOURSPACE_DCI_P3', 'RGB_COLOURSPACE_DCI_P3_P', - 'RGB_COLOURSPACE_DISPLAY_P3', 'RGB_COLOURSPACE_DJI_D_GAMUT', - 'RGB_COLOURSPACE_DON_RGB_4', 'RGB_COLOURSPACE_ECI_RGB_V2', - 'RGB_COLOURSPACE_EKTA_SPACE_PS_5', 'RGB_COLOURSPACE_FILMLIGHT_E_GAMUT', - 'RGB_COLOURSPACE_PROTUNE_NATIVE', 'RGB_COLOURSPACE_MAX_RGB', - 'RGB_COLOURSPACE_P3_D65', 'RGB_COLOURSPACE_PAL_SECAM', - 'RGB_COLOURSPACE_RED_COLOR', 'RGB_COLOURSPACE_RED_COLOR_2', - 'RGB_COLOURSPACE_RED_COLOR_3', 'RGB_COLOURSPACE_RED_COLOR_4', - 'RGB_COLOURSPACE_DRAGON_COLOR', 'RGB_COLOURSPACE_DRAGON_COLOR_2', - 'RGB_COLOURSPACE_RED_WIDE_GAMUT_RGB', 'RGB_COLOURSPACE_ROMM_RGB', - 'RGB_COLOURSPACE_RIMM_RGB', 'RGB_COLOURSPACE_ERIMM_RGB', - 'RGB_COLOURSPACE_PROPHOTO_RGB', 'RGB_COLOURSPACE_RUSSELL_RGB', - 'RGB_COLOURSPACE_SHARP_RGB', 'RGB_COLOURSPACE_SMPTE_240M', - 'RGB_COLOURSPACE_SMPTE_C', 'RGB_COLOURSPACE_NTSC1953', - 'RGB_COLOURSPACE_NTSC1987', 'RGB_COLOURSPACE_S_GAMUT', - 'RGB_COLOURSPACE_S_GAMUT3', 'RGB_COLOURSPACE_S_GAMUT3_CINE', - 'RGB_COLOURSPACE_VENICE_S_GAMUT3', 'RGB_COLOURSPACE_VENICE_S_GAMUT3_CINE', - 'RGB_COLOURSPACE_sRGB', 'RGB_COLOURSPACE_V_GAMUT', - 'RGB_COLOURSPACE_XTREME_RGB', 'RGB_COLOURSPACE_F_GAMUT' + "RGB_COLOURSPACE_ACES2065_1", + "RGB_COLOURSPACE_ACESCC", + "RGB_COLOURSPACE_ACESCCT", + "RGB_COLOURSPACE_ACESPROXY", + "RGB_COLOURSPACE_ACESCG", + "RGB_COLOURSPACE_ADOBE_RGB1998", + "RGB_COLOURSPACE_ADOBE_WIDE_GAMUT_RGB", + "RGB_COLOURSPACE_ALEXA_WIDE_GAMUT", + "RGB_COLOURSPACE_APPLE_RGB", + "RGB_COLOURSPACE_BEST_RGB", + "RGB_COLOURSPACE_BETA_RGB", + "RGB_COLOURSPACE_BLACKMAGIC_WIDE_GAMUT", + "RGB_COLOURSPACE_BT470_525", + "RGB_COLOURSPACE_BT470_625", + "RGB_COLOURSPACE_BT709", + "RGB_COLOURSPACE_BT2020", + "RGB_COLOURSPACE_CIE_RGB", + "RGB_COLOURSPACE_CINEMA_GAMUT", + "RGB_COLOURSPACE_COLOR_MATCH_RGB", + "RGB_COLOURSPACE_DAVINCI_WIDE_GAMUT", + "RGB_COLOURSPACE_DCDM_XYZ", + "RGB_COLOURSPACE_DCI_P3", + "RGB_COLOURSPACE_DCI_P3_P", + "RGB_COLOURSPACE_DISPLAY_P3", + "RGB_COLOURSPACE_DJI_D_GAMUT", + "RGB_COLOURSPACE_DON_RGB_4", + "RGB_COLOURSPACE_ECI_RGB_V2", + "RGB_COLOURSPACE_EKTA_SPACE_PS_5", + "RGB_COLOURSPACE_FILMLIGHT_E_GAMUT", + "RGB_COLOURSPACE_PROTUNE_NATIVE", + "RGB_COLOURSPACE_MAX_RGB", + "RGB_COLOURSPACE_N_GAMUT", + "RGB_COLOURSPACE_P3_D65", + "RGB_COLOURSPACE_PAL_SECAM", + "RGB_COLOURSPACE_RED_COLOR", + "RGB_COLOURSPACE_RED_COLOR_2", + "RGB_COLOURSPACE_RED_COLOR_3", + "RGB_COLOURSPACE_RED_COLOR_4", + "RGB_COLOURSPACE_DRAGON_COLOR", + "RGB_COLOURSPACE_DRAGON_COLOR_2", + "RGB_COLOURSPACE_RED_WIDE_GAMUT_RGB", + "RGB_COLOURSPACE_ROMM_RGB", + "RGB_COLOURSPACE_RIMM_RGB", + "RGB_COLOURSPACE_ERIMM_RGB", + "RGB_COLOURSPACE_PROPHOTO_RGB", + "RGB_COLOURSPACE_RUSSELL_RGB", + "RGB_COLOURSPACE_SHARP_RGB", + "RGB_COLOURSPACE_SMPTE_240M", + "RGB_COLOURSPACE_SMPTE_C", + "RGB_COLOURSPACE_NTSC1953", + "RGB_COLOURSPACE_NTSC1987", + "RGB_COLOURSPACE_S_GAMUT", + "RGB_COLOURSPACE_S_GAMUT3", + "RGB_COLOURSPACE_S_GAMUT3_CINE", + "RGB_COLOURSPACE_VENICE_S_GAMUT3", + "RGB_COLOURSPACE_VENICE_S_GAMUT3_CINE", + "RGB_COLOURSPACE_sRGB", + "RGB_COLOURSPACE_V_GAMUT", + "RGB_COLOURSPACE_XTREME_RGB", + "RGB_COLOURSPACE_F_GAMUT", ] diff --git a/colour/models/rgb/datasets/aces.py b/colour/models/rgb/datasets/aces.py index 63e3fbea88..bb3eae8abb 100644 --- a/colour/models/rgb/datasets/aces.py +++ b/colour/models/rgb/datasets/aces.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Academy Color Encoding System ============================= @@ -52,106 +51,104 @@ http://www.oscars.org/science-technology/council/projects/aces.html """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS +from colour.hints import NDArray from colour.models.rgb import ( - RGB_Colourspace, linear_function, normalised_primary_matrix, - log_encoding_ACEScc, log_decoding_ACEScc, log_encoding_ACEScct, - log_decoding_ACEScct, log_encoding_ACESproxy, log_decoding_ACESproxy) + RGB_Colourspace, + linear_function, + normalised_primary_matrix, + log_encoding_ACEScc, + log_decoding_ACEScc, + log_encoding_ACEScct, + log_decoding_ACEScct, + log_encoding_ACESproxy, + log_decoding_ACESproxy, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'AP0', 'AP1', 'WHITEPOINT_NAME_ACES', 'CCS_WHITEPOINT_ACES', - 'MATRIX_AP0_TO_XYZ', 'MATRIX_XYZ_TO_AP0', 'MATRIX_AP1_TO_XYZ', - 'MATRIX_XYZ_TO_AP1', 'RGB_COLOURSPACE_ACES2065_1', - 'RGB_COLOURSPACE_ACESCG', 'RGB_COLOURSPACE_ACESCC', - 'RGB_COLOURSPACE_ACESCCT', 'RGB_COLOURSPACE_ACESPROXY' + "AP0", + "AP1", + "WHITEPOINT_NAME_ACES", + "CCS_WHITEPOINT_ACES", + "MATRIX_AP0_TO_XYZ", + "MATRIX_XYZ_TO_AP0", + "MATRIX_AP1_TO_XYZ", + "MATRIX_XYZ_TO_AP1", + "RGB_COLOURSPACE_ACES2065_1", + "RGB_COLOURSPACE_ACESCG", + "RGB_COLOURSPACE_ACESCC", + "RGB_COLOURSPACE_ACESCCT", + "RGB_COLOURSPACE_ACESPROXY", ] -AP0 = np.array([ - [0.73470, 0.26530], - [0.00000, 1.00000], - [0.00010, -0.07700], -]) -""" -*ACES Primaries 0* or *AP0* primaries. - -AP0 : ndarray, (3, 2) -""" - -AP1 = np.array([ - [0.71300, 0.29300], - [0.16500, 0.83000], - [0.12800, 0.04400], -]) +AP0: NDArray = np.array( + [ + [0.73470, 0.26530], + [0.00000, 1.00000], + [0.00010, -0.07700], + ] +) +"""*ACES Primaries 0* or *AP0* primaries.""" + +AP1: NDArray = np.array( + [ + [0.71300, 0.29300], + [0.16500, 0.83000], + [0.12800, 0.04400], + ] +) """ *ACES Primaries 1* or *AP1* primaries (known as *ITU-R BT.2020+* primaries prior to *ACES* 1.0 release). - -AP1 : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_ACES = 'ACES' """ -*ACES2065-1* colourspace whitepoint name. -WHITEPOINT_NAME_ACES : unicode -""" +WHITEPOINT_NAME_ACES: str = "ACES" +"""*ACES2065-1* colourspace whitepoint name.""" -CCS_WHITEPOINT_ACES = (CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'][ - WHITEPOINT_NAME_ACES]) -""" -*ACES2065-1* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_ACES : ndarray -""" +CCS_WHITEPOINT_ACES: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_ACES] +"""*ACES2065-1* colourspace whitepoint chromaticity coordinates.""" -MATRIX_AP0_TO_XYZ = np.array([ - [0.9525523959, 0.0000000000, 0.0000936786], - [0.3439664498, 0.7281660966, -0.0721325464], - [0.0000000000, 0.0000000000, 1.0088251844], -]) -""" -*ACES Primaries 0* to *CIE XYZ* tristimulus values matrix defined as per [2]. -MATRIX_AP0_TO_XYZ : array_like, (3, 3) -""" - -MATRIX_XYZ_TO_AP0 = np.array([ - [1.0498110175, 0.0000000000, -0.0000974845], - [-0.4959030231, 1.3733130458, 0.0982400361], - [0.0000000000, 0.0000000000, 0.9912520182], -]) -""" -*CIE XYZ* tristimulus values to *ACES Primaries 0* matrix. - -MATRIX_XYZ_TO_AP0 : array_like, (3, 3) -""" - -MATRIX_AP1_TO_XYZ = normalised_primary_matrix(AP1, CCS_WHITEPOINT_ACES) -""" -*ACES Primaries 1* to *CIE XYZ* tristimulus values matrix. - -MATRIX_AP1_TO_XYZ : array_like, (3, 3) -""" +MATRIX_AP0_TO_XYZ: NDArray = np.array( + [ + [0.9525523959, 0.0000000000, 0.0000936786], + [0.3439664498, 0.7281660966, -0.0721325464], + [0.0000000000, 0.0000000000, 1.0088251844], + ] +) +"""*ACES Primaries 0* to *CIE XYZ* tristimulus values matrix defined as per [2].""" + +MATRIX_XYZ_TO_AP0: NDArray = np.array( + [ + [1.0498110175, 0.0000000000, -0.0000974845], + [-0.4959030231, 1.3733130458, 0.0982400361], + [0.0000000000, 0.0000000000, 0.9912520182], + ] +) +"""*CIE XYZ* tristimulus values to *ACES Primaries 0* matrix.""" -MATRIX_XYZ_TO_AP1 = np.linalg.inv(MATRIX_AP1_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *ACES Primaries 1* matrix. +MATRIX_AP1_TO_XYZ: NDArray = normalised_primary_matrix( + AP1, CCS_WHITEPOINT_ACES +) +"""*ACES Primaries 1* to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_AP1 : array_like, (3, 3) -""" +MATRIX_XYZ_TO_AP1: NDArray = np.linalg.inv(MATRIX_AP1_TO_XYZ) +"""*CIE XYZ* tristimulus values to *ACES Primaries 1* matrix.""" -RGB_COLOURSPACE_ACES2065_1 = RGB_Colourspace( - 'ACES2065-1', +RGB_COLOURSPACE_ACES2065_1: RGB_Colourspace = RGB_Colourspace( + "ACES2065-1", AP0, CCS_WHITEPOINT_ACES, WHITEPOINT_NAME_ACES, @@ -169,12 +166,10 @@ :cite:`TheAcademyofMotionPictureArtsandSciences2014q`, :cite:`TheAcademyofMotionPictureArtsandSciences2014r`, :cite:`TheAcademyofMotionPictureArtsandSciencese` - -RGB_COLOURSPACE_ACES2065_1 : RGB_Colourspace """ -RGB_COLOURSPACE_ACESCG = RGB_Colourspace( - 'ACEScg', +RGB_COLOURSPACE_ACESCG: RGB_Colourspace = RGB_Colourspace( + "ACEScg", AP1, CCS_WHITEPOINT_ACES, WHITEPOINT_NAME_ACES, @@ -193,12 +188,10 @@ :cite:`TheAcademyofMotionPictureArtsandSciences2014r`, :cite:`TheAcademyofMotionPictureArtsandSciences2015b`, :cite:`TheAcademyofMotionPictureArtsandSciencese` - -RGB_COLOURSPACE_ACESCG : RGB_Colourspace """ -RGB_COLOURSPACE_ACESCC = RGB_Colourspace( - 'ACEScc', +RGB_COLOURSPACE_ACESCC: RGB_Colourspace = RGB_Colourspace( + "ACEScc", AP1, CCS_WHITEPOINT_ACES, WHITEPOINT_NAME_ACES, @@ -217,12 +210,10 @@ :cite:`TheAcademyofMotionPictureArtsandSciences2014r`, :cite:`TheAcademyofMotionPictureArtsandSciences2014t`, :cite:`TheAcademyofMotionPictureArtsandSciencese` - -RGB_COLOURSPACE_ACESCC : RGB_Colourspace """ -RGB_COLOURSPACE_ACESCCT = RGB_Colourspace( - 'ACEScct', +RGB_COLOURSPACE_ACESCCT: RGB_Colourspace = RGB_Colourspace( + "ACEScct", AP1, CCS_WHITEPOINT_ACES, WHITEPOINT_NAME_ACES, @@ -242,12 +233,10 @@ :cite:`TheAcademyofMotionPictureArtsandSciences2014r`, :cite:`TheAcademyofMotionPictureArtsandSciences2016c`, :cite:`TheAcademyofMotionPictureArtsandSciencese` - -RGB_COLOURSPACE_ACESCCT : RGB_Colourspace """ -RGB_COLOURSPACE_ACESPROXY = RGB_Colourspace( - 'ACESproxy', +RGB_COLOURSPACE_ACESPROXY: RGB_Colourspace = RGB_Colourspace( + "ACESproxy", AP1, CCS_WHITEPOINT_ACES, WHITEPOINT_NAME_ACES, @@ -268,6 +257,4 @@ :cite:`TheAcademyofMotionPictureArtsandSciences2014r`, :cite:`TheAcademyofMotionPictureArtsandSciences2014s`, :cite:`TheAcademyofMotionPictureArtsandSciencese` - -RGB_COLOURSPACE_ACESPROXY : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/adobe_rgb_1998.py b/colour/models/rgb/datasets/adobe_rgb_1998.py index 7e285c7f30..6a8e71f166 100644 --- a/colour/models/rgb/datasets/adobe_rgb_1998.py +++ b/colour/models/rgb/datasets/adobe_rgb_1998.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Adobe RGB (1998) Colourspace ============================ @@ -13,77 +12,68 @@ Image Encoding. http://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from functools import partial from colour.colorimetry import CCS_ILLUMINANTS +from colour.hints import NDArray from colour.models.rgb import RGB_Colourspace, gamma_function -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_ADOBE_RGB1998', 'WHITEPOINT_NAME_ADOBE_RGB1998', - 'CCS_WHITEPOINT_ADOBE_RGB1998', 'MATRIX_ADOBE_RGB1998_TO_XYZ', - 'MATRIX_XYZ_TO_ADOBE_RGB1998', 'RGB_COLOURSPACE_ADOBE_RGB1998' + "PRIMARIES_ADOBE_RGB1998", + "WHITEPOINT_NAME_ADOBE_RGB1998", + "CCS_WHITEPOINT_ADOBE_RGB1998", + "MATRIX_ADOBE_RGB1998_TO_XYZ", + "MATRIX_XYZ_TO_ADOBE_RGB1998", + "RGB_COLOURSPACE_ADOBE_RGB1998", ] -PRIMARIES_ADOBE_RGB1998 = np.array([ - [0.6400, 0.3300], - [0.2100, 0.7100], - [0.1500, 0.0600], -]) -""" -*Adobe RGB (1998)* colourspace primaries. - -PRIMARIES_ADOBE_RGB1998 : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_ADOBE_RGB1998 = 'D65' -""" -*Adobe RGB (1998)* colourspace whitepoint name. - -WHITEPOINT_NAME_ADOBE_RGB1998 : unicode -""" - -CCS_WHITEPOINT_ADOBE_RGB1998 = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_ADOBE_RGB1998]) -""" -*Adobe RGB (1998)* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_ADOBE_RGB1998 : ndarray -""" - -MATRIX_ADOBE_RGB1998_TO_XYZ = np.array([ - [0.57667, 0.18556, 0.18823], - [0.29734, 0.62736, 0.07529], - [0.02703, 0.07069, 0.99134], -]) -""" -*Adobe RGB (1998)* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_ADOBE_RGB1998_TO_XYZ : array_like, (3, 3) -""" - -MATRIX_XYZ_TO_ADOBE_RGB1998 = np.array([ - [2.04159, -0.56501, -0.34473], - [-0.96924, 1.87597, 0.04156], - [0.01344, -0.11836, 1.01517], -]) -""" -*CIE XYZ* tristimulus values to *Adobe RGB (1998)* colourspace matrix. - -MATRIX_XYZ_TO_ADOBE_RGB1998 : array_like, (3, 3) -""" +PRIMARIES_ADOBE_RGB1998: NDArray = np.array( + [ + [0.6400, 0.3300], + [0.2100, 0.7100], + [0.1500, 0.0600], + ] +) +"""*Adobe RGB (1998)* colourspace primaries.""" + +WHITEPOINT_NAME_ADOBE_RGB1998: str = "D65" +"""*Adobe RGB (1998)* colourspace whitepoint name.""" + +CCS_WHITEPOINT_ADOBE_RGB1998: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_ADOBE_RGB1998] +"""*Adobe RGB (1998)* colourspace whitepoint chromaticity coordinates.""" + +MATRIX_ADOBE_RGB1998_TO_XYZ: NDArray = np.array( + [ + [0.57667, 0.18556, 0.18823], + [0.29734, 0.62736, 0.07529], + [0.02703, 0.07069, 0.99134], + ] +) +"""*Adobe RGB (1998)* colourspace to *CIE XYZ* tristimulus values matrix.""" + +MATRIX_XYZ_TO_ADOBE_RGB1998: NDArray = np.array( + [ + [2.04159, -0.56501, -0.34473], + [-0.96924, 1.87597, 0.04156], + [0.01344, -0.11836, 1.01517], + ] +) +"""*CIE XYZ* tristimulus values to *Adobe RGB (1998)* colourspace matrix.""" -RGB_COLOURSPACE_ADOBE_RGB1998 = RGB_Colourspace( - 'Adobe RGB (1998)', +RGB_COLOURSPACE_ADOBE_RGB1998: RGB_Colourspace = RGB_Colourspace( + "Adobe RGB (1998)", PRIMARIES_ADOBE_RGB1998, CCS_WHITEPOINT_ADOBE_RGB1998, WHITEPOINT_NAME_ADOBE_RGB1998, @@ -98,6 +88,4 @@ References ---------- :cite:`AdobeSystems2005a` - -RGB_COLOURSPACE_ADOBE_RGB1998 : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/adobe_wide_gamut_rgb.py b/colour/models/rgb/datasets/adobe_wide_gamut_rgb.py index 225b227196..8dc9942a6e 100644 --- a/colour/models/rgb/datasets/adobe_wide_gamut_rgb.py +++ b/colour/models/rgb/datasets/adobe_wide_gamut_rgb.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Adobe Wide Gamut RGB Colourspace ================================ @@ -14,74 +13,64 @@ http://en.wikipedia.org/wiki/Wide-gamut_RGB_color_space """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from functools import partial from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, gamma_function, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + gamma_function, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_ADOBE_WIDE_GAMUT_RGB', 'WHITEPOINT_NAME_ADOBE_WIDE_GAMUT_RGB', - 'CCS_WHITEPOINT_ADOBE_WIDE_GAMUT_RGB', - 'MATRIX_ADOBE_WIDE_GAMUT_RGB_TO_XYZ', 'MATRIX_XYZ_TO_ADOBE_WIDE_GAMUT_RGB', - 'RGB_COLOURSPACE_ADOBE_WIDE_GAMUT_RGB' + "PRIMARIES_ADOBE_WIDE_GAMUT_RGB", + "WHITEPOINT_NAME_ADOBE_WIDE_GAMUT_RGB", + "CCS_WHITEPOINT_ADOBE_WIDE_GAMUT_RGB", + "MATRIX_ADOBE_WIDE_GAMUT_RGB_TO_XYZ", + "MATRIX_XYZ_TO_ADOBE_WIDE_GAMUT_RGB", + "RGB_COLOURSPACE_ADOBE_WIDE_GAMUT_RGB", ] -PRIMARIES_ADOBE_WIDE_GAMUT_RGB = np.array([ - [0.7347, 0.2653], - [0.1152, 0.8264], - [0.1566, 0.0177], -]) -""" -*Adobe Wide Gamut RGB* colourspace primaries. - -PRIMARIES_ADOBE_WIDE_GAMUT_RGB : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_ADOBE_WIDE_GAMUT_RGB = 'D50' -""" -*Adobe Wide Gamut RGB* colourspace whitepoint name. - -WHITEPOINT_NAME_ADOBE_WIDE_GAMUT_RGB : unicode -""" - -CCS_WHITEPOINT_ADOBE_WIDE_GAMUT_RGB = ( - CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'][ - WHITEPOINT_NAME_ADOBE_WIDE_GAMUT_RGB]) -""" -*Adobe Wide Gamut RGB* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_ADOBE_WIDE_GAMUT_RGB : ndarray -""" +PRIMARIES_ADOBE_WIDE_GAMUT_RGB: NDArray = np.array( + [ + [0.7347, 0.2653], + [0.1152, 0.8264], + [0.1566, 0.0177], + ] +) +"""*Adobe Wide Gamut RGB* colourspace primaries.""" -MATRIX_ADOBE_WIDE_GAMUT_RGB_TO_XYZ = normalised_primary_matrix( - PRIMARIES_ADOBE_WIDE_GAMUT_RGB, CCS_WHITEPOINT_ADOBE_WIDE_GAMUT_RGB) -""" -*Adobe Wide Gamut RGB* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_ADOBE_WIDE_GAMUT_RGB: str = "D50" +"""*Adobe Wide Gamut RGB* colourspace whitepoint name.""" -MATRIX_ADOBE_WIDE_GAMUT_RGB_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_ADOBE_WIDE_GAMUT_RGB: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_ADOBE_WIDE_GAMUT_RGB] +"""*Adobe Wide Gamut RGB* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_ADOBE_WIDE_GAMUT_RGB = np.linalg.inv( - MATRIX_ADOBE_WIDE_GAMUT_RGB_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *Adobe Wide Gamut RGB* colourspace matrix. +MATRIX_ADOBE_WIDE_GAMUT_RGB_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_ADOBE_WIDE_GAMUT_RGB, CCS_WHITEPOINT_ADOBE_WIDE_GAMUT_RGB +) +"""*Adobe Wide Gamut RGB* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_ADOBE_WIDE_GAMUT_RGB : array_like, (3, 3) -""" +MATRIX_XYZ_TO_ADOBE_WIDE_GAMUT_RGB: NDArray = np.linalg.inv( + MATRIX_ADOBE_WIDE_GAMUT_RGB_TO_XYZ +) +"""*CIE XYZ* tristimulus values to *Adobe Wide Gamut RGB* colourspace matrix.""" -RGB_COLOURSPACE_ADOBE_WIDE_GAMUT_RGB = RGB_Colourspace( - 'Adobe Wide Gamut RGB', +RGB_COLOURSPACE_ADOBE_WIDE_GAMUT_RGB: RGB_Colourspace = RGB_Colourspace( + "Adobe Wide Gamut RGB", PRIMARIES_ADOBE_WIDE_GAMUT_RGB, CCS_WHITEPOINT_ADOBE_WIDE_GAMUT_RGB, WHITEPOINT_NAME_ADOBE_WIDE_GAMUT_RGB, @@ -96,6 +85,4 @@ References ---------- :cite:`Wikipedia2004c` - -RGB_COLOURSPACE_ADOBE_WIDE_GAMUT_RGB : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/apple_rgb.py b/colour/models/rgb/datasets/apple_rgb.py index fd95acb9c3..cc251ef6ba 100644 --- a/colour/models/rgb/datasets/apple_rgb.py +++ b/colour/models/rgb/datasets/apple_rgb.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Apple RGB Colourspace ===================== @@ -13,71 +12,62 @@ Standard RGB Color Spaces. """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from functools import partial from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, gamma_function, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + gamma_function, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_APPLE_RGB', 'WHITEPOINT_NAME_APPLE_RGB', - 'CCS_WHITEPOINT_APPLE_RGB', 'MATRIX_APPLE_RGB_TO_XYZ', - 'MATRIX_XYZ_TO_APPLE_RGB', 'RGB_COLOURSPACE_APPLE_RGB' + "PRIMARIES_APPLE_RGB", + "WHITEPOINT_NAME_APPLE_RGB", + "CCS_WHITEPOINT_APPLE_RGB", + "MATRIX_APPLE_RGB_TO_XYZ", + "MATRIX_XYZ_TO_APPLE_RGB", + "RGB_COLOURSPACE_APPLE_RGB", ] -PRIMARIES_APPLE_RGB = np.array([ - [0.6250, 0.3400], - [0.2800, 0.5950], - [0.1550, 0.0700], -]) -""" -*Apple RGB* colourspace primaries. - -PRIMARIES_APPLE_RGB : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_APPLE_RGB = 'D65' -""" -*Apple RGB* colourspace whitepoint name. - -WHITEPOINT_NAME_APPLE_RGB : unicode -""" - -CCS_WHITEPOINT_APPLE_RGB = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_APPLE_RGB]) -""" -*Apple RGB* colourspace whitepoint chromaticity coordinates. +PRIMARIES_APPLE_RGB: NDArray = np.array( + [ + [0.6250, 0.3400], + [0.2800, 0.5950], + [0.1550, 0.0700], + ] +) +"""*Apple RGB* colourspace primaries.""" -CCS_WHITEPOINT_APPLE_RGB : ndarray -""" +WHITEPOINT_NAME_APPLE_RGB: str = "D65" +"""*Apple RGB* colourspace whitepoint name.""" -MATRIX_APPLE_RGB_TO_XYZ = normalised_primary_matrix(PRIMARIES_APPLE_RGB, - CCS_WHITEPOINT_APPLE_RGB) -""" -*Apple RGB* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_APPLE_RGB_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_APPLE_RGB: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_APPLE_RGB] +"""*Apple RGB* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_APPLE_RGB = np.linalg.inv(MATRIX_APPLE_RGB_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *Apple RGB* colourspace matrix. +MATRIX_APPLE_RGB_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_APPLE_RGB, CCS_WHITEPOINT_APPLE_RGB +) +"""*Apple RGB* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_APPLE_RGB : array_like, (3, 3) -""" +MATRIX_XYZ_TO_APPLE_RGB: NDArray = np.linalg.inv(MATRIX_APPLE_RGB_TO_XYZ) +"""*CIE XYZ* tristimulus values to *Apple RGB* colourspace matrix.""" -RGB_COLOURSPACE_APPLE_RGB = RGB_Colourspace( - 'Apple RGB', +RGB_COLOURSPACE_APPLE_RGB: RGB_Colourspace = RGB_Colourspace( + "Apple RGB", PRIMARIES_APPLE_RGB, CCS_WHITEPOINT_APPLE_RGB, WHITEPOINT_NAME_APPLE_RGB, @@ -92,6 +82,4 @@ References ---------- :cite:`Susstrunk1999a` - -RGB_COLOURSPACE_APPLE_RGB : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/arri_alexa_wide_gamut.py b/colour/models/rgb/datasets/arri_alexa_wide_gamut.py index 7a699b74ec..af0da06aaf 100644 --- a/colour/models/rgb/datasets/arri_alexa_wide_gamut.py +++ b/colour/models/rgb/datasets/arri_alexa_wide_gamut.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ ARRI ALEXA Wide Gamut Colourspace ================================= @@ -13,77 +12,71 @@ https://drive.google.com/open?id=1t73fAG_QpV7hJxoQPYZDWvOojYkYDgvn """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, log_encoding_ALEXALogC, - log_decoding_ALEXALogC) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + log_encoding_ALEXALogC, + log_decoding_ALEXALogC, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_ALEXA_WIDE_GAMUT', 'WHITEPOINT_NAME_ALEXA_WIDE_GAMUT', - 'CCS_WHITEPOINT_ALEXA_WIDE_GAMUT', 'MATRIX_ALEXA_WIDE_GAMUT_TO_XYZ', - 'MATRIX_XYZ_TO_ALEXA_WIDE_GAMUT', 'RGB_COLOURSPACE_ALEXA_WIDE_GAMUT' + "PRIMARIES_ALEXA_WIDE_GAMUT", + "WHITEPOINT_NAME_ALEXA_WIDE_GAMUT", + "CCS_WHITEPOINT_ALEXA_WIDE_GAMUT", + "MATRIX_ALEXA_WIDE_GAMUT_TO_XYZ", + "MATRIX_XYZ_TO_ALEXA_WIDE_GAMUT", + "RGB_COLOURSPACE_ALEXA_WIDE_GAMUT", ] -PRIMARIES_ALEXA_WIDE_GAMUT = np.array([ - [0.6840, 0.3130], - [0.2210, 0.8480], - [0.0861, -0.1020], -]) -""" -*ARRI ALEXA Wide Gamut* colourspace primaries. - -PRIMARIES_ALEXA_WIDE_GAMUT : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_ALEXA_WIDE_GAMUT = 'D65' -""" -*ARRI ALEXA Wide Gamut* colourspace whitepoint name. - -CCS_WHITEPOINT_ALEXA_WIDE_GAMUT : unicode -""" - -CCS_WHITEPOINT_ALEXA_WIDE_GAMUT = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_ALEXA_WIDE_GAMUT]) -""" -*ARRI ALEXA Wide Gamut* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_ALEXA_WIDE_GAMUT : ndarray -""" - -MATRIX_ALEXA_WIDE_GAMUT_TO_XYZ = np.array([ - [0.638008, 0.214704, 0.097744], - [0.291954, 0.823841, -0.115795], - [0.002798, -0.067034, 1.153294], -]) -""" -*ARRI ALEXA Wide Gamut* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_ALEXA_WIDE_GAMUT_TO_XYZ : array_like, (3, 3) -""" - -MATRIX_XYZ_TO_ALEXA_WIDE_GAMUT = np.array([ - [1.789066, -0.482534, -0.200076], - [-0.639849, 1.396400, 0.194432], - [-0.041532, 0.082335, 0.878868], -]) -""" -*CIE XYZ* tristimulus values to *ARRI ALEXA Wide Gamut* colourspace matrix. - -MATRIX_XYZ_TO_ALEXA_WIDE_GAMUT : array_like, (3, 3) -""" +PRIMARIES_ALEXA_WIDE_GAMUT: NDArray = np.array( + [ + [0.6840, 0.3130], + [0.2210, 0.8480], + [0.0861, -0.1020], + ] +) +"""*ARRI ALEXA Wide Gamut* colourspace primaries.""" + +WHITEPOINT_NAME_ALEXA_WIDE_GAMUT: str = "D65" +"""*ARRI ALEXA Wide Gamut* colourspace whitepoint name.""" + +CCS_WHITEPOINT_ALEXA_WIDE_GAMUT: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_ALEXA_WIDE_GAMUT] +"""*ARRI ALEXA Wide Gamut* colourspace whitepoint chromaticity coordinates.""" + +MATRIX_ALEXA_WIDE_GAMUT_TO_XYZ: NDArray = np.array( + [ + [0.638008, 0.214704, 0.097744], + [0.291954, 0.823841, -0.115795], + [0.002798, -0.067034, 1.153294], + ] +) +"""*ARRI ALEXA Wide Gamut* colourspace to *CIE XYZ* tristimulus values matrix.""" + +MATRIX_XYZ_TO_ALEXA_WIDE_GAMUT: NDArray = np.array( + [ + [1.789066, -0.482534, -0.200076], + [-0.639849, 1.396400, 0.194432], + [-0.041532, 0.082335, 0.878868], + ] +) +"""*CIE XYZ* tristimulus values to *ARRI ALEXA Wide Gamut* colourspace matrix.""" -RGB_COLOURSPACE_ALEXA_WIDE_GAMUT = RGB_Colourspace( - 'ALEXA Wide Gamut', +RGB_COLOURSPACE_ALEXA_WIDE_GAMUT: RGB_Colourspace = RGB_Colourspace( + "ALEXA Wide Gamut", PRIMARIES_ALEXA_WIDE_GAMUT, CCS_WHITEPOINT_ALEXA_WIDE_GAMUT, WHITEPOINT_NAME_ALEXA_WIDE_GAMUT, @@ -98,6 +91,4 @@ References ---------- :cite:`ARRI2012a` - -RGB_COLOURSPACE_ALEXA_WIDE_GAMUT : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/best_rgb.py b/colour/models/rgb/datasets/best_rgb.py index 91c37fa475..f55c5529e1 100644 --- a/colour/models/rgb/datasets/best_rgb.py +++ b/colour/models/rgb/datasets/best_rgb.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Best RGB Colourspace ==================== @@ -13,71 +12,62 @@ http://www.hutchcolor.com/profiles/BestRGB.zip """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from functools import partial from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, gamma_function, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + gamma_function, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_BEST_RGB', 'WHITEPOINT_NAME_BEST_RGB', - 'CCS_WHITEPOINT_BEST_RGB', 'MATRIX_BEST_RGB_TO_XYZ', - 'MATRIX_XYZ_TO_BEST_RGB', 'RGB_COLOURSPACE_BEST_RGB' + "PRIMARIES_BEST_RGB", + "WHITEPOINT_NAME_BEST_RGB", + "CCS_WHITEPOINT_BEST_RGB", + "MATRIX_BEST_RGB_TO_XYZ", + "MATRIX_XYZ_TO_BEST_RGB", + "RGB_COLOURSPACE_BEST_RGB", ] -PRIMARIES_BEST_RGB = np.array([ - [0.735191637630662, 0.264808362369338], - [0.215336134453781, 0.774159663865546], - [0.130122950819672, 0.034836065573770], -]) -""" -*Best RGB* colourspace primaries. - -PRIMARIES_BEST_RGB : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_BEST_RGB = 'D50' -""" -*Best RGB* colourspace whitepoint name. - -WHITEPOINT_NAME_BEST_RGB : unicode -""" - -CCS_WHITEPOINT_BEST_RGB = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_BEST_RGB]) -""" -*Best RGB* colourspace whitepoint chromaticity coordinates. +PRIMARIES_BEST_RGB: NDArray = np.array( + [ + [0.735191637630662, 0.264808362369338], + [0.215336134453781, 0.774159663865546], + [0.130122950819672, 0.034836065573770], + ] +) +"""*Best RGB* colourspace primaries.""" -CCS_WHITEPOINT_BEST_RGB : ndarray -""" +WHITEPOINT_NAME_BEST_RGB: str = "D50" +"""*Best RGB* colourspace whitepoint name.""" -MATRIX_BEST_RGB_TO_XYZ = normalised_primary_matrix(PRIMARIES_BEST_RGB, - CCS_WHITEPOINT_BEST_RGB) -""" -*Best RGB* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_BEST_RGB_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_BEST_RGB: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_BEST_RGB] +"""*Best RGB* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_BEST_RGB = np.linalg.inv(MATRIX_BEST_RGB_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *Best RGB* colourspace matrix. +MATRIX_BEST_RGB_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_BEST_RGB, CCS_WHITEPOINT_BEST_RGB +) +"""*Best RGB* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_BEST_RGB : array_like, (3, 3) -""" +MATRIX_XYZ_TO_BEST_RGB: NDArray = np.linalg.inv(MATRIX_BEST_RGB_TO_XYZ) +"""*CIE XYZ* tristimulus values to *Best RGB* colourspace matrix.""" -RGB_COLOURSPACE_BEST_RGB = RGB_Colourspace( - 'Best RGB', +RGB_COLOURSPACE_BEST_RGB: RGB_Colourspace = RGB_Colourspace( + "Best RGB", PRIMARIES_BEST_RGB, CCS_WHITEPOINT_BEST_RGB, WHITEPOINT_NAME_BEST_RGB, @@ -92,6 +82,4 @@ References ---------- :cite:`HutchColord` - -RGB_COLOURSPACE_BEST_RGB : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/beta_rgb.py b/colour/models/rgb/datasets/beta_rgb.py index 886edbb701..05c17b4005 100644 --- a/colour/models/rgb/datasets/beta_rgb.py +++ b/colour/models/rgb/datasets/beta_rgb.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Beta RGB Colourspace ==================== @@ -14,71 +13,62 @@ http://www.brucelindbloom.com/WorkingSpaceInfo.html """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from functools import partial from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, gamma_function, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + gamma_function, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_BETA_RGB', 'WHITEPOINT_NAME_BETA_RGB', - 'CCS_WHITEPOINT_BETA_RGB', 'MATRIX_BETA_RGB_TO_XYZ', - 'MATRIX_XYZ_TO_BETA_RGB', 'RGB_COLOURSPACE_BETA_RGB' + "PRIMARIES_BETA_RGB", + "WHITEPOINT_NAME_BETA_RGB", + "CCS_WHITEPOINT_BETA_RGB", + "MATRIX_BETA_RGB_TO_XYZ", + "MATRIX_XYZ_TO_BETA_RGB", + "RGB_COLOURSPACE_BETA_RGB", ] -PRIMARIES_BETA_RGB = np.array([ - [0.6888, 0.3112], - [0.1986, 0.7551], - [0.1265, 0.0352], -]) -""" -*Beta RGB* colourspace primaries. - -PRIMARIES_BETA_RGB : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_BETA_RGB = 'D50' -""" -*Beta RGB* colourspace whitepoint name. - -WHITEPOINT_NAME_BETA_RGB : unicode -""" - -CCS_WHITEPOINT_BETA_RGB = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_BETA_RGB]) -""" -*Beta RGB* colourspace whitepoint chromaticity coordinates. +PRIMARIES_BETA_RGB: NDArray = np.array( + [ + [0.6888, 0.3112], + [0.1986, 0.7551], + [0.1265, 0.0352], + ] +) +"""*Beta RGB* colourspace primaries.""" -CCS_WHITEPOINT_BETA_RGB : ndarray -""" +WHITEPOINT_NAME_BETA_RGB: str = "D50" +"""*Beta RGB* colourspace whitepoint name.""" -MATRIX_BETA_RGB_TO_XYZ = normalised_primary_matrix(PRIMARIES_BETA_RGB, - CCS_WHITEPOINT_BETA_RGB) -""" -*Beta RGB* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_BETA_RGB_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_BETA_RGB: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_BETA_RGB] +"""*Beta RGB* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_BETA_RGB = np.linalg.inv(MATRIX_BETA_RGB_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *Beta RGB* colourspace matrix. +MATRIX_BETA_RGB_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_BETA_RGB, CCS_WHITEPOINT_BETA_RGB +) +"""*Beta RGB* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_BETA_RGB : array_like, (3, 3) -""" +MATRIX_XYZ_TO_BETA_RGB: NDArray = np.linalg.inv(MATRIX_BETA_RGB_TO_XYZ) +"""*CIE XYZ* tristimulus values to *Beta RGB* colourspace matrix.""" -RGB_COLOURSPACE_BETA_RGB = RGB_Colourspace( - 'Beta RGB', +RGB_COLOURSPACE_BETA_RGB: RGB_Colourspace = RGB_Colourspace( + "Beta RGB", PRIMARIES_BETA_RGB, CCS_WHITEPOINT_BETA_RGB, WHITEPOINT_NAME_BETA_RGB, @@ -93,6 +83,4 @@ References ---------- :cite:`Lindbloom2014a` - -RGB_COLOURSPACE_BETA_RGB : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/blackmagic_design.py b/colour/models/rgb/datasets/blackmagic_design.py new file mode 100644 index 0000000000..81753bef3e --- /dev/null +++ b/colour/models/rgb/datasets/blackmagic_design.py @@ -0,0 +1,104 @@ +""" +Blackmagic Design Colourspaces +============================== + +Defines the *Blackmagic Design* *RGB* colourspaces: + +- :attr:`colour.models.RGB_COLOURSPACE_BLACKMAGIC_WIDE_GAMUT`. + +References +---------- +- :cite:`BlackmagicDesign2021` : Blackmagic Design. (2021). Blackmagic + Generation 5 Color Science. https://drive.google.com/file/d/\ +1FF5WO2nvI9GEWb4_EntrBoV9ZIuFToZd/view +""" + +from __future__ import annotations + +import numpy as np + +from colour.colorimetry import CCS_ILLUMINANTS +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + oetf_BlackmagicFilmGeneration5, + oetf_inverse_BlackmagicFilmGeneration5, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "PRIMARIES_BLACKMAGIC_WIDE_GAMUT", + "WHITEPOINT_NAME_BLACKMAGIC_WIDE_GAMUT", + "CCS_WHITEPOINT_BLACKMAGIC_WIDE_GAMUT", + "MATRIX_BLACKMAGIC_WIDE_GAMUT_TO_XYZ", + "MATRIX_XYZ_TO_BLACKMAGIC_WIDE_GAMUT", + "RGB_COLOURSPACE_BLACKMAGIC_WIDE_GAMUT", +] + +PRIMARIES_BLACKMAGIC_WIDE_GAMUT: NDArray = np.array( + [ + [0.7177215, 0.3171181], + [0.2280410, 0.8615690], + [0.1005841, -0.0820452], + ] +) +"""*Blackmagic Wide Gamut* colourspace primaries.""" + +WHITEPOINT_NAME_BLACKMAGIC_WIDE_GAMUT: str = "Blackmagic Wide Gamut" +""" +*Blackmagic Wide Gamut* colourspace whitepoint name. + +Notes +----- +- *Blackmagic Wide Gamut* colourspace whitepoint is an uncommonly rounded + *D65* variant at 7 decimals: [0.3127170, 0.3290312] +""" + +CCS_WHITEPOINT_BLACKMAGIC_WIDE_GAMUT: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_BLACKMAGIC_WIDE_GAMUT] +"""*Blackmagic Wide Gamut* colourspace whitepoint chromaticity coordinates.""" + +MATRIX_BLACKMAGIC_WIDE_GAMUT_TO_XYZ: NDArray = np.array( + [ + [0.606530, 0.220408, 0.123479], + [0.267989, 0.832731, -0.100720], + [-0.029442, -0.086611, 1.204861], + ] +) +"""*Blackmagic Wide Gamut* colourspace to *CIE XYZ* tristimulus values matrix.""" + +MATRIX_XYZ_TO_BLACKMAGIC_WIDE_GAMUT: NDArray = np.array( + [ + [1.866382, -0.518397, -0.234610], + [-0.600342, 1.378149, 0.176732], + [0.002452, 0.086400, 0.836943], + ] +) +"""*CIE XYZ* tristimulus values to *Blackmagic Wide Gamut* colourspace matrix.""" + +RGB_COLOURSPACE_BLACKMAGIC_WIDE_GAMUT: RGB_Colourspace = RGB_Colourspace( + "Blackmagic Wide Gamut", + PRIMARIES_BLACKMAGIC_WIDE_GAMUT, + CCS_WHITEPOINT_BLACKMAGIC_WIDE_GAMUT, + WHITEPOINT_NAME_BLACKMAGIC_WIDE_GAMUT, + MATRIX_BLACKMAGIC_WIDE_GAMUT_TO_XYZ, + MATRIX_XYZ_TO_BLACKMAGIC_WIDE_GAMUT, + oetf_BlackmagicFilmGeneration5, + oetf_inverse_BlackmagicFilmGeneration5, + use_derived_matrix_RGB_to_XYZ=True, + use_derived_matrix_XYZ_to_RGB=True, +) +RGB_COLOURSPACE_BLACKMAGIC_WIDE_GAMUT.__doc__ = """ +*Blackmagic Wide Gamut* colourspace. + +References +---------- +:cite:`BlackmagicDesign2021` +""" diff --git a/colour/models/rgb/datasets/canon_cinema_gamut.py b/colour/models/rgb/datasets/canon_cinema_gamut.py index 378ad4fe02..4829b0eadd 100644 --- a/colour/models/rgb/datasets/canon_cinema_gamut.py +++ b/colour/models/rgb/datasets/canon_cinema_gamut.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Canon Cinema Gamut Colourspace ============================== @@ -15,70 +14,61 @@ product-showcases/cameras-and-lenses/cinema-eos-firmware/c500 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, linear_function, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + linear_function, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_CINEMA_GAMUT', 'WHITEPOINT_NAME_CINEMA_GAMUT', - 'CCS_WHITEPOINT_CINEMA_GAMUT', 'MATRIX_CINEMA_GAMUT_TO_XYZ', - 'MATRIX_XYZ_TO_CINEMA_GAMUT', 'RGB_COLOURSPACE_CINEMA_GAMUT' + "PRIMARIES_CINEMA_GAMUT", + "WHITEPOINT_NAME_CINEMA_GAMUT", + "CCS_WHITEPOINT_CINEMA_GAMUT", + "MATRIX_CINEMA_GAMUT_TO_XYZ", + "MATRIX_XYZ_TO_CINEMA_GAMUT", + "RGB_COLOURSPACE_CINEMA_GAMUT", ] -PRIMARIES_CINEMA_GAMUT = np.array([ - [0.7400, 0.2700], - [0.1700, 1.1400], - [0.0800, -0.1000], -]) -""" -*Canon Cinema Gamut* colourspace primaries. - -PRIMARIES_CINEMA_GAMUT : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_CINEMA_GAMUT = 'D65' -""" -*Canon Cinema Gamut* colourspace whitepoint name. - -WHITEPOINT_NAME_CINEMA_GAMUT : unicode -""" - -CCS_WHITEPOINT_CINEMA_GAMUT = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_CINEMA_GAMUT]) -""" -*Canon Cinema Gamut* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_CINEMA_GAMUT : ndarray -""" +PRIMARIES_CINEMA_GAMUT: NDArray = np.array( + [ + [0.7400, 0.2700], + [0.1700, 1.1400], + [0.0800, -0.1000], + ] +) +"""*Canon Cinema Gamut* colourspace primaries.""" -MATRIX_CINEMA_GAMUT_TO_XYZ = normalised_primary_matrix( - PRIMARIES_CINEMA_GAMUT, CCS_WHITEPOINT_CINEMA_GAMUT) -""" -*Canon Cinema Gamut* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_CINEMA_GAMUT: str = "D65" +"""*Canon Cinema Gamut* colourspace whitepoint name.""" -MATRIX_CINEMA_GAMUT_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_CINEMA_GAMUT: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_CINEMA_GAMUT] +"""*Canon Cinema Gamut* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_CINEMA_GAMUT = np.linalg.inv(MATRIX_CINEMA_GAMUT_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *Canon Cinema Gamut* colourspace matrix. +MATRIX_CINEMA_GAMUT_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_CINEMA_GAMUT, CCS_WHITEPOINT_CINEMA_GAMUT +) +"""*Canon Cinema Gamut* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_CINEMA_GAMUT : array_like, (3, 3) -""" +MATRIX_XYZ_TO_CINEMA_GAMUT: NDArray = np.linalg.inv(MATRIX_CINEMA_GAMUT_TO_XYZ) +"""*CIE XYZ* tristimulus values to *Canon Cinema Gamut* colourspace matrix.""" -RGB_COLOURSPACE_CINEMA_GAMUT = RGB_Colourspace( - 'Cinema Gamut', +RGB_COLOURSPACE_CINEMA_GAMUT: RGB_Colourspace = RGB_Colourspace( + "Cinema Gamut", PRIMARIES_CINEMA_GAMUT, CCS_WHITEPOINT_CINEMA_GAMUT, WHITEPOINT_NAME_CINEMA_GAMUT, @@ -93,6 +83,4 @@ References ---------- :cite:`Canon2014a` - -RGB_COLOURSPACE_CINEMA_GAMUT : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/cie_rgb.py b/colour/models/rgb/datasets/cie_rgb.py index 78fdc3b1f8..47157a5f1c 100644 --- a/colour/models/rgb/datasets/cie_rgb.py +++ b/colour/models/rgb/datasets/cie_rgb.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ CIE RGB Colourspace =================== @@ -15,36 +14,41 @@ doi:10.1002/(SICI)1520-6378(199702)22:1<11::AID-COL4>3.0.CO;2-7 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from functools import partial from colour.colorimetry import CCS_ILLUMINANTS +from colour.hints import NDArray from colour.models.rgb import RGB_Colourspace, gamma_function -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_CIE_RGB', 'WHITEPOINT_NAME_CIE_RGB', 'CCS_WHITEPOINT_CIE_RGB', - 'MATRIX_CIE_RGB_TO_XYZ', 'MATRIX_XYZ_TO_CIE_RGB', 'RGB_COLOURSPACE_CIE_RGB' + "PRIMARIES_CIE_RGB", + "WHITEPOINT_NAME_CIE_RGB", + "CCS_WHITEPOINT_CIE_RGB", + "MATRIX_CIE_RGB_TO_XYZ", + "MATRIX_XYZ_TO_CIE_RGB", + "RGB_COLOURSPACE_CIE_RGB", ] -PRIMARIES_CIE_RGB = np.array([ - [0.734742840005998, 0.265257159994002], - [0.273779033824958, 0.717477700256116], - [0.166555629580280, 0.008910726182545], -]) +PRIMARIES_CIE_RGB: NDArray = np.array( + [ + [0.734742840005998, 0.265257159994002], + [0.273779033824958, 0.717477700256116], + [0.166555629580280, 0.008910726182545], + ] +) """ *CIE RGB* colourspace primaries. -PRIMARIES_CIE_RGB : ndarray, (3, 2) - Notes ----- - *CIE RGB* colourspace primaries were computed using @@ -52,41 +56,28 @@ and :func:`colour.primaries_whitepoint` definition. """ -WHITEPOINT_NAME_CIE_RGB = 'E' -""" -*CIE RGB* colourspace whitepoint name. - -WHITEPOINT_NAME_CIE_RGB : unicode -""" - -CCS_WHITEPOINT_CIE_RGB = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_CIE_RGB]) -""" -*CIE RGB* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_CIE_RGB : ndarray -""" - -MATRIX_CIE_RGB_TO_XYZ = np.array([ - [0.4900, 0.3100, 0.2000], - [0.1769, 0.8124, 0.0107], - [0.0000, 0.0099, 0.9901], -]) -""" -*CIE RGB* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_CIE_RGB: str = "E" +"""*CIE RGB* colourspace whitepoint name.""" -MATRIX_CIE_RGB_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_CIE_RGB: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_CIE_RGB] +"""*CIE RGB* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_CIE_RGB = np.linalg.inv(MATRIX_CIE_RGB_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *CIE RGB* colourspace matrix. +MATRIX_CIE_RGB_TO_XYZ: NDArray = np.array( + [ + [0.4900, 0.3100, 0.2000], + [0.1769, 0.8124, 0.0107], + [0.0000, 0.0099, 0.9901], + ] +) +"""*CIE RGB* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_CIE_RGB : array_like, (3, 3) -""" +MATRIX_XYZ_TO_CIE_RGB: NDArray = np.linalg.inv(MATRIX_CIE_RGB_TO_XYZ) +"""*CIE XYZ* tristimulus values to *CIE RGB* colourspace matrix.""" -RGB_COLOURSPACE_CIE_RGB = RGB_Colourspace( - 'CIE RGB', +RGB_COLOURSPACE_CIE_RGB: RGB_Colourspace = RGB_Colourspace( + "CIE RGB", PRIMARIES_CIE_RGB, CCS_WHITEPOINT_CIE_RGB, WHITEPOINT_NAME_CIE_RGB, @@ -101,6 +92,4 @@ References ---------- :cite:`Fairman1997` - -RGB_COLOURSPACE_CIE_RGB : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/color_match_rgb.py b/colour/models/rgb/datasets/color_match_rgb.py index 9219113588..339a1bd7a9 100644 --- a/colour/models/rgb/datasets/color_match_rgb.py +++ b/colour/models/rgb/datasets/color_match_rgb.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ ColorMatch RGB Colourspace ========================== @@ -14,71 +13,64 @@ http://www.brucelindbloom.com/WorkingSpaceInfo.html """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from functools import partial from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, gamma_function, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + gamma_function, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_COLOR_MATCH_RGB', 'WHITEPOINT_NAME_COLOR_MATCH_RGB', - 'CCS_WHITEPOINT_COLOR_MATCH_RGB', 'MATRIX_COLOR_MATCH_RGB_TO_XYZ', - 'MATRIX_XYZ_TO_COLOR_MATCH_RGB', 'RGB_COLOURSPACE_COLOR_MATCH_RGB' + "PRIMARIES_COLOR_MATCH_RGB", + "WHITEPOINT_NAME_COLOR_MATCH_RGB", + "CCS_WHITEPOINT_COLOR_MATCH_RGB", + "MATRIX_COLOR_MATCH_RGB_TO_XYZ", + "MATRIX_XYZ_TO_COLOR_MATCH_RGB", + "RGB_COLOURSPACE_COLOR_MATCH_RGB", ] -PRIMARIES_COLOR_MATCH_RGB = np.array([ - [0.6300, 0.3400], - [0.2950, 0.6050], - [0.1500, 0.0750], -]) -""" -*ColorMatch RGB* colourspace primaries. - -PRIMARIES_COLOR_MATCH_RGB : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_COLOR_MATCH_RGB = 'D50' -""" -*ColorMatch RGB* colourspace whitepoint name. - -WHITEPOINT_NAME_COLOR_MATCH_RGB : unicode -""" - -CCS_WHITEPOINT_COLOR_MATCH_RGB = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_COLOR_MATCH_RGB]) -""" -*ColorMatch RGB* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_COLOR_MATCH_RGB : ndarray -""" +PRIMARIES_COLOR_MATCH_RGB: NDArray = np.array( + [ + [0.6300, 0.3400], + [0.2950, 0.6050], + [0.1500, 0.0750], + ] +) +"""*ColorMatch RGB* colourspace primaries.""" -MATRIX_COLOR_MATCH_RGB_TO_XYZ = normalised_primary_matrix( - PRIMARIES_COLOR_MATCH_RGB, CCS_WHITEPOINT_COLOR_MATCH_RGB) -""" -*ColorMatch RGB* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_COLOR_MATCH_RGB: str = "D50" +"""*ColorMatch RGB* colourspace whitepoint name.""" -MATRIX_COLOR_MATCH_RGB_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_COLOR_MATCH_RGB: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_COLOR_MATCH_RGB] +"""*ColorMatch RGB* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_COLOR_MATCH_RGB = np.linalg.inv(MATRIX_COLOR_MATCH_RGB_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *ColorMatch RGB* colourspace matrix. +MATRIX_COLOR_MATCH_RGB_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_COLOR_MATCH_RGB, CCS_WHITEPOINT_COLOR_MATCH_RGB +) +"""*ColorMatch RGB* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_COLOR_MATCH_RGB : array_like, (3, 3) -""" +MATRIX_XYZ_TO_COLOR_MATCH_RGB: NDArray = np.linalg.inv( + MATRIX_COLOR_MATCH_RGB_TO_XYZ +) +"""*CIE XYZ* tristimulus values to *ColorMatch RGB* colourspace matrix.""" -RGB_COLOURSPACE_COLOR_MATCH_RGB = RGB_Colourspace( - 'ColorMatch RGB', +RGB_COLOURSPACE_COLOR_MATCH_RGB: RGB_Colourspace = RGB_Colourspace( + "ColorMatch RGB", PRIMARIES_COLOR_MATCH_RGB, CCS_WHITEPOINT_COLOR_MATCH_RGB, WHITEPOINT_NAME_COLOR_MATCH_RGB, @@ -93,6 +85,4 @@ References ---------- :cite:`Lindbloom2014a` - -RGB_COLOURSPACE_COLOR_MATCH_RGB : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/davinci_wide_gamut.py b/colour/models/rgb/datasets/davinci_wide_gamut.py index 85cb794f95..534b2d92c9 100644 --- a/colour/models/rgb/datasets/davinci_wide_gamut.py +++ b/colour/models/rgb/datasets/davinci_wide_gamut.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ DaVinci Wide Gamut Colourspace ============================== @@ -11,87 +10,91 @@ ---------- - :cite:`BlackmagicDesign2020` : Blackmagic Design. (2020). DaVinci Wide Gamut - DaVinci Resolve Studio 17 Public Beta 1. +- :cite:`BlackmagicDesign2020a` : Blackmagic Design. (2020). Wide Gamut + Intermediate DaVinci Resolve. Retrieved December 12, 2020, from + https://documents.blackmagicdesign.com/InformationNotes/\ +DaVinci_Resolve_17_Wide_Gamut_Intermediate.pdf?_v=1607414410000 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, linear_function, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + oetf_DaVinciIntermediate, + oetf_inverse_DaVinciIntermediate, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_DAVINCI_WIDE_GAMUT', 'WHITEPOINT_NAME_DAVINCI_WIDE_GAMUT', - 'CCS_WHITEPOINT_DAVINCI_WIDE_GAMUT', 'MATRIX_DAVINCI_WIDE_GAMUT_TO_XYZ', - 'MATRIX_XYZ_TO_DAVINCI_WIDE_GAMUT', 'RGB_COLOURSPACE_DAVINCI_WIDE_GAMUT' + "PRIMARIES_DAVINCI_WIDE_GAMUT", + "WHITEPOINT_NAME_DAVINCI_WIDE_GAMUT", + "CCS_WHITEPOINT_DAVINCI_WIDE_GAMUT", + "MATRIX_DAVINCI_WIDE_GAMUT_TO_XYZ", + "MATRIX_XYZ_TO_DAVINCI_WIDE_GAMUT", + "RGB_COLOURSPACE_DAVINCI_WIDE_GAMUT", ] -PRIMARIES_DAVINCI_WIDE_GAMUT = np.array([ - [0.8000, 0.3130], - [0.1682, 0.9877], - [0.0790, -0.1155], -]) -""" -*DaVinci Wide Gamut* colourspace primaries. - -PRIMARIES_DAVINCI_WIDE_GAMUT : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_DAVINCI_WIDE_GAMUT = 'D65' -""" -*DaVinci Wide Gamut* colourspace whitepoint name. - -WHITEPOINT_NAME_DAVINCI_WIDE_GAMUT : unicode -""" - -CCS_WHITEPOINT_DAVINCI_WIDE_GAMUT = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_DAVINCI_WIDE_GAMUT]) -""" -*DaVinci Wide Gamut* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_DAVINCI_WIDE_GAMUT : ndarray -""" - -MATRIX_DAVINCI_WIDE_GAMUT_TO_XYZ = normalised_primary_matrix( - PRIMARIES_DAVINCI_WIDE_GAMUT, CCS_WHITEPOINT_DAVINCI_WIDE_GAMUT) -""" -*DaVinci Wide Gamut* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_DAVINCI_WIDE_GAMUT_TO_XYZ : array_like, (3, 3) -""" - -MATRIX_XYZ_TO_DAVINCI_WIDE_GAMUT = np.linalg.inv( - MATRIX_DAVINCI_WIDE_GAMUT_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *DaVinci Wide Gamut* colourspace matrix. - -MATRIX_XYZ_TO_DAVINCI_WIDE_GAMUT : array_like, (3, 3) -""" +PRIMARIES_DAVINCI_WIDE_GAMUT: NDArray = np.array( + [ + [0.8000, 0.3130], + [0.1682, 0.9877], + [0.0790, -0.1155], + ] +) +"""*DaVinci Wide Gamut* colourspace primaries.""" + +WHITEPOINT_NAME_DAVINCI_WIDE_GAMUT: str = "D65" +"""*DaVinci Wide Gamut* colourspace whitepoint name.""" + +CCS_WHITEPOINT_DAVINCI_WIDE_GAMUT: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_DAVINCI_WIDE_GAMUT] +"""*DaVinci Wide Gamut* colourspace whitepoint chromaticity coordinates.""" + +MATRIX_DAVINCI_WIDE_GAMUT_TO_XYZ: NDArray = np.array( + [ + [0.70062239, 0.14877482, 0.10105872], + [0.27411851, 0.87363190, -0.14775041], + [-0.09896291, -0.13789533, 1.32591599], + ] +) +"""*DaVinci Wide Gamut* colourspace to *CIE XYZ* tristimulus values matrix.""" + +MATRIX_XYZ_TO_DAVINCI_WIDE_GAMUT: NDArray = np.array( + [ + [1.51667204, -0.28147805, -0.14696363], + [-0.46491710, 1.25142378, 0.17488461], + [0.06484905, 0.10913934, 0.76141462], + ] +) +"""*CIE XYZ* tristimulus values to *DaVinci Wide Gamut* colourspace matrix.""" -RGB_COLOURSPACE_DAVINCI_WIDE_GAMUT = RGB_Colourspace( - 'DaVinci Wide Gamut', +RGB_COLOURSPACE_DAVINCI_WIDE_GAMUT: RGB_Colourspace = RGB_Colourspace( + "DaVinci Wide Gamut", PRIMARIES_DAVINCI_WIDE_GAMUT, CCS_WHITEPOINT_DAVINCI_WIDE_GAMUT, WHITEPOINT_NAME_DAVINCI_WIDE_GAMUT, MATRIX_DAVINCI_WIDE_GAMUT_TO_XYZ, MATRIX_XYZ_TO_DAVINCI_WIDE_GAMUT, - linear_function, - linear_function, + oetf_DaVinciIntermediate, + oetf_inverse_DaVinciIntermediate, + use_derived_matrix_RGB_to_XYZ=True, + use_derived_matrix_XYZ_to_RGB=True, ) RGB_COLOURSPACE_DAVINCI_WIDE_GAMUT.__doc__ = """ *DaVinci Wide Gamut* colourspace. References ---------- -:cite:`BlackmagicDesign2020` - -RGB_COLOURSPACE_DAVINCI_WIDE_GAMUT : RGB_Colourspace +:cite:`BlackmagicDesign2020`, :cite:`BlackmagicDesign2020a` """ diff --git a/colour/models/rgb/datasets/dcdm_xyz.py b/colour/models/rgb/datasets/dcdm_xyz.py index 725262788d..c5df50ab14 100644 --- a/colour/models/rgb/datasets/dcdm_xyz.py +++ b/colour/models/rgb/datasets/dcdm_xyz.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Digital Cinema Distribution Master (DCDM) XYZ Colourspace ========================================================= @@ -15,70 +14,62 @@ DCI_DCinema_System_Spec_v1_1.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, eotf_DCDM, - normalised_primary_matrix, eotf_inverse_DCDM) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + eotf_DCDM, + normalised_primary_matrix, + eotf_inverse_DCDM, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_DCDM_XYZ', 'WHITEPOINT_NAME_DCDM_XYZ', - 'CCS_WHITEPOINT_DCDM_XYZ', 'MATRIX_DCDM_XYZ_TO_XYZ', - 'MATRIX_XYZ_TO_DCDM_XYZ', 'RGB_COLOURSPACE_DCDM_XYZ' + "PRIMARIES_DCDM_XYZ", + "WHITEPOINT_NAME_DCDM_XYZ", + "CCS_WHITEPOINT_DCDM_XYZ", + "MATRIX_DCDM_XYZ_TO_XYZ", + "MATRIX_XYZ_TO_DCDM_XYZ", + "RGB_COLOURSPACE_DCDM_XYZ", ] -PRIMARIES_DCDM_XYZ = np.array([ - [1.0, 0.0], - [0.0, 1.0], - [0.0, 0.0], -]) -""" -*DCDM XYZ* colourspace primaries. - -PRIMARIES_DCDM_XYZ : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_DCDM_XYZ = 'E' -""" -*DCDM XYZ* colourspace whitepoint name. - -WHITEPOINT_NAME_DCDM_XYZ : unicode -""" - -CCS_WHITEPOINT_DCDM_XYZ = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_DCDM_XYZ]) -""" -*DCDM XYZ* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_DCDM_XYZ : ndarray -""" +PRIMARIES_DCDM_XYZ: NDArray = np.array( + [ + [1.0, 0.0], + [0.0, 1.0], + [0.0, 0.0], + ] +) +"""*DCDM XYZ* colourspace primaries.""" -MATRIX_DCDM_XYZ_TO_XYZ = normalised_primary_matrix(PRIMARIES_DCDM_XYZ, - CCS_WHITEPOINT_DCDM_XYZ) -""" -*DCDM XYZ* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_DCDM_XYZ: str = "E" +"""*DCDM XYZ* colourspace whitepoint name.""" -MATRIX_DCDM_XYZ_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_DCDM_XYZ: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_DCDM_XYZ] +"""*DCDM XYZ* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_DCDM_XYZ = np.linalg.inv(MATRIX_DCDM_XYZ_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *DCDM XYZ* colourspace matrix. +MATRIX_DCDM_XYZ_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_DCDM_XYZ, CCS_WHITEPOINT_DCDM_XYZ +) +"""*DCDM XYZ* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_DCDM_XYZ : array_like, (3, 3) -""" +MATRIX_XYZ_TO_DCDM_XYZ: NDArray = np.linalg.inv(MATRIX_DCDM_XYZ_TO_XYZ) +"""*CIE XYZ* tristimulus values to *DCDM XYZ* colourspace matrix.""" -RGB_COLOURSPACE_DCDM_XYZ = RGB_Colourspace( - 'DCDM XYZ', +RGB_COLOURSPACE_DCDM_XYZ: RGB_Colourspace = RGB_Colourspace( + "DCDM XYZ", PRIMARIES_DCDM_XYZ, CCS_WHITEPOINT_DCDM_XYZ, WHITEPOINT_NAME_DCDM_XYZ, @@ -93,6 +84,4 @@ References ---------- :cite:`DigitalCinemaInitiatives2007b` - -RGB_COLOURSPACE_DCDM_XYZ : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/dci_p3.py b/colour/models/rgb/datasets/dci_p3.py index 15a92619ee..8aca4485cb 100644 --- a/colour/models/rgb/datasets/dci_p3.py +++ b/colour/models/rgb/datasets/dci_p3.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ DCI-P3 & DCI-P3+ Colourspaces ============================= @@ -25,57 +24,61 @@ lp2480zx-dci--p3-emulation.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from functools import partial from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, gamma_function, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + gamma_function, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_DCI_P3', 'PRIMARIES_DCI_P3_P', 'WHITEPOINT_NAME_DCI_P3', - 'CCS_WHITEPOINT_DCI_P3', 'MATRIX_DCI_P3_TO_XYZ', 'MATRIX_XYZ_TO_DCI_P3', - 'MATRIX_DCI_P3_P_TO_XYZ', 'MATRIX_XYZ_TO_DCI_P3_P', - 'RGB_COLOURSPACE_DCI_P3', 'RGB_COLOURSPACE_DCI_P3_P' + "PRIMARIES_DCI_P3", + "PRIMARIES_DCI_P3_P", + "WHITEPOINT_NAME_DCI_P3", + "CCS_WHITEPOINT_DCI_P3", + "MATRIX_DCI_P3_TO_XYZ", + "MATRIX_XYZ_TO_DCI_P3", + "MATRIX_DCI_P3_P_TO_XYZ", + "MATRIX_XYZ_TO_DCI_P3_P", + "RGB_COLOURSPACE_DCI_P3", + "RGB_COLOURSPACE_DCI_P3_P", ] -PRIMARIES_DCI_P3 = np.array([ - [0.6800, 0.3200], - [0.2650, 0.6900], - [0.1500, 0.0600], -]) -""" -*DCI-P3* colourspace primaries. - -PRIMARIES_DCI_P3 : ndarray, (3, 2) -""" - -PRIMARIES_DCI_P3_P = np.array([ - [0.7400, 0.2700], - [0.2200, 0.7800], - [0.0900, -0.0900], -]) -""" -*DCI-P3+* colourspace primaries. - -PRIMARIES_DCI_P3_P : ndarray, (3, 2) -""" +PRIMARIES_DCI_P3: NDArray = np.array( + [ + [0.6800, 0.3200], + [0.2650, 0.6900], + [0.1500, 0.0600], + ] +) +"""*DCI-P3* colourspace primaries.""" + +PRIMARIES_DCI_P3_P: NDArray = np.array( + [ + [0.7400, 0.2700], + [0.2200, 0.7800], + [0.0900, -0.0900], + ] +) +"""*DCI-P3+* colourspace primaries.""" -WHITEPOINT_NAME_DCI_P3 = 'DCI-P3' +WHITEPOINT_NAME_DCI_P3: str = "DCI-P3" """ *DCI-P3* colourspace whitepoint name. -WHITEPOINT_NAME_DCI_P3 : unicode - Warnings -------- DCI-P3 illuminant has no associated spectral distribution. DCI has no @@ -83,46 +86,29 @@ matching spectral distribution is Kinoton 75P projector. """ -CCS_WHITEPOINT_DCI_P3 = (CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'] - [WHITEPOINT_NAME_DCI_P3]) -""" -*DCI-P3* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_DCI_P3 : ndarray -""" - -MATRIX_DCI_P3_TO_XYZ = normalised_primary_matrix(PRIMARIES_DCI_P3, - CCS_WHITEPOINT_DCI_P3) -""" -*DCI-P3* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_DCI_P3_TO_XYZ : array_like, (3, 3) -""" - -MATRIX_XYZ_TO_DCI_P3 = np.linalg.inv(MATRIX_DCI_P3_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *DCI-P3* colourspace matrix. - -MATRIX_XYZ_TO_DCI_P3 : array_like, (3, 3) -""" +CCS_WHITEPOINT_DCI_P3: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_DCI_P3] +"""*DCI-P3* colourspace whitepoint chromaticity coordinates.""" -MATRIX_DCI_P3_P_TO_XYZ = normalised_primary_matrix(PRIMARIES_DCI_P3_P, - CCS_WHITEPOINT_DCI_P3) -""" -*DCI-P3+* colourspace to *CIE XYZ* tristimulus values matrix. +MATRIX_DCI_P3_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_DCI_P3, CCS_WHITEPOINT_DCI_P3 +) +"""*DCI-P3* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_DCI_P3_P_TO_XYZ : array_like, (3, 3) -""" +MATRIX_XYZ_TO_DCI_P3: NDArray = np.linalg.inv(MATRIX_DCI_P3_TO_XYZ) +"""*CIE XYZ* tristimulus values to *DCI-P3* colourspace matrix.""" -MATRIX_XYZ_TO_DCI_P3_P = np.linalg.inv(MATRIX_DCI_P3_P_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *DCI-P3+* colourspace matrix. +MATRIX_DCI_P3_P_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_DCI_P3_P, CCS_WHITEPOINT_DCI_P3 +) +"""*DCI-P3+* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_DCI_P3_P : array_like, (3, 3) -""" +MATRIX_XYZ_TO_DCI_P3_P: NDArray = np.linalg.inv(MATRIX_DCI_P3_P_TO_XYZ) +"""*CIE XYZ* tristimulus values to *DCI-P3+* colourspace matrix.""" -RGB_COLOURSPACE_DCI_P3 = RGB_Colourspace( - 'DCI-P3', +RGB_COLOURSPACE_DCI_P3: RGB_Colourspace = RGB_Colourspace( + "DCI-P3", PRIMARIES_DCI_P3, CCS_WHITEPOINT_DCI_P3, WHITEPOINT_NAME_DCI_P3, @@ -138,12 +124,10 @@ ---------- :cite:`DigitalCinemaInitiatives2007b`, :cite:`Hewlett-PackardDevelopmentCompany2009a` - -RGB_COLOURSPACE_DCI_P3 : RGB_Colourspace """ -RGB_COLOURSPACE_DCI_P3_P = RGB_Colourspace( - 'DCI-P3+', +RGB_COLOURSPACE_DCI_P3_P: RGB_Colourspace = RGB_Colourspace( + "DCI-P3+", PRIMARIES_DCI_P3_P, CCS_WHITEPOINT_DCI_P3, WHITEPOINT_NAME_DCI_P3, @@ -158,6 +142,4 @@ References ---------- :cite:`Canon2014a` - -RGB_COLOURSPACE_DCI_P3_P : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/display_p3.py b/colour/models/rgb/datasets/display_p3.py index c761faaadb..96f2be5d27 100644 --- a/colour/models/rgb/datasets/display_p3.py +++ b/colour/models/rgb/datasets/display_p3.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Display P3 Colourspace ====================== @@ -14,67 +13,57 @@ documentation/coregraphics/cgcolorspace/1408916-displayp3 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, eotf_inverse_sRGB, eotf_sRGB, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + eotf_inverse_sRGB, + eotf_sRGB, + normalised_primary_matrix, +) from colour.models.rgb.datasets import RGB_COLOURSPACE_DCI_P3 -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-science@googlegroups.com' -__status__ = 'Production' +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-science@googlegroups.com" +__status__ = "Production" __all__ = [ - 'PRIMARIES_DISPLAY_P3', 'WHITEPOINT_NAME_DISPLAY_P3', - 'CCS_WHITEPOINT_DISPLAY_P3', 'MATRIX_DISPLAY_P3_TO_XYZ', - 'MATRIX_XYZ_TO_DISPLAY_P3', 'RGB_COLOURSPACE_DISPLAY_P3' + "PRIMARIES_DISPLAY_P3", + "WHITEPOINT_NAME_DISPLAY_P3", + "CCS_WHITEPOINT_DISPLAY_P3", + "MATRIX_DISPLAY_P3_TO_XYZ", + "MATRIX_XYZ_TO_DISPLAY_P3", + "RGB_COLOURSPACE_DISPLAY_P3", ] -PRIMARIES_DISPLAY_P3 = RGB_COLOURSPACE_DCI_P3.primaries -""" -*Display P3* colourspace primaries. - -PRIMARIES_DISPLAY_P3 : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_DISPLAY_P3 = 'D65' -""" -*Display P3* colourspace whitepoint name. - -CCS_WHITEPOINT_DISPLAY_P3 : unicode -""" - -CCS_WHITEPOINT_DISPLAY_P3 = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_DISPLAY_P3]) -""" -*Display P3* colourspace whitepoint chromaticity coordinates. +PRIMARIES_DISPLAY_P3: NDArray = RGB_COLOURSPACE_DCI_P3.primaries +"""*Display P3* colourspace primaries.""" -CCS_WHITEPOINT_DISPLAY_P3 : ndarray -""" - -MATRIX_DISPLAY_P3_TO_XYZ = (normalised_primary_matrix( - PRIMARIES_DISPLAY_P3, CCS_WHITEPOINT_DISPLAY_P3)) -""" -*Display P3* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_DISPLAY_P3: str = "D65" +"""*Display P3* colourspace whitepoint name.""" -MATRIX_DISPLAY_P3_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_DISPLAY_P3: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_DISPLAY_P3] +"""*Display P3* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_DISPLAY_P3 = np.linalg.inv(MATRIX_DISPLAY_P3_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *Display P3* colourspace matrix. +MATRIX_DISPLAY_P3_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_DISPLAY_P3, CCS_WHITEPOINT_DISPLAY_P3 +) +"""*Display P3* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_DISPLAY_P3 : array_like, (3, 3) -""" +MATRIX_XYZ_TO_DISPLAY_P3: NDArray = np.linalg.inv(MATRIX_DISPLAY_P3_TO_XYZ) +"""*CIE XYZ* tristimulus values to *Display P3* colourspace matrix.""" -RGB_COLOURSPACE_DISPLAY_P3 = RGB_Colourspace( - 'Display P3', +RGB_COLOURSPACE_DISPLAY_P3: RGB_Colourspace = RGB_Colourspace( + "Display P3", PRIMARIES_DISPLAY_P3, CCS_WHITEPOINT_DISPLAY_P3, WHITEPOINT_NAME_DISPLAY_P3, @@ -89,6 +78,4 @@ References ---------- :cite:`AppleInc.2019` - -RGB_COLOURSPACE_DISPLAY_P3 : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/dji_dgamut.py b/colour/models/rgb/datasets/dji_dgamut.py index acf8e63909..52e5f1cad7 100644 --- a/colour/models/rgb/datasets/dji_dgamut.py +++ b/colour/models/rgb/datasets/dji_dgamut.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ DJI D-Gamut Colourspace ======================= @@ -15,73 +14,71 @@ D-Log_D-Gamut_Whitepaper.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, log_encoding_DJIDLog, - log_decoding_DJIDLog) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + log_encoding_DJIDLog, + log_decoding_DJIDLog, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_DJI_D_GAMUT', 'WHITEPOINT_NAME_DJI_D_GAMUT', - 'CCS_WHITEPOINT_DJI_D_GAMUT', 'MATRIX_DJI_D_GAMUT_TO_XYZ', - 'MATRIX_XYZ_TO_DJI_D_GAMUT', 'RGB_COLOURSPACE_DJI_D_GAMUT' + "PRIMARIES_DJI_D_GAMUT", + "WHITEPOINT_NAME_DJI_D_GAMUT", + "CCS_WHITEPOINT_DJI_D_GAMUT", + "MATRIX_DJI_D_GAMUT_TO_XYZ", + "MATRIX_XYZ_TO_DJI_D_GAMUT", + "RGB_COLOURSPACE_DJI_D_GAMUT", ] -PRIMARIES_DJI_D_GAMUT = np.array([ - [0.71, 0.31], - [0.21, 0.88], - [0.09, -0.08], -]) -""" -*DJI D-Gamut* colourspace primaries. - -PRIMARIES_DJI_D_GAMUT : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_DJI_D_GAMUT = 'D65' -""" -*DJI D-Gamut* colourspace whitepoint name. - -CCS_WHITEPOINT_DJI_D_GAMUT : unicode -""" - -CCS_WHITEPOINT_DJI_D_GAMUT = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_DJI_D_GAMUT]) -""" -*DJI D-Gamut* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_DJI_D_GAMUT : ndarray -""" - -MATRIX_DJI_D_GAMUT_TO_XYZ = np.array([[0.6482, 0.1940, - 0.1082], [0.2830, 0.8132, -0.0962], - [-0.0183, -0.0832, 1.1903]]) -""" -*DJI D-Gamut* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_DJI_D_GAMUT_TO_XYZ : array_like, (3, 3) -""" - -MATRIX_XYZ_TO_DJI_D_GAMUT = np.array([[1.7257, -0.4314, - -0.1917], [-0.6025, 1.3906, 0.1671], - [-0.0156, 0.0905, 0.8489]]) -""" -*CIE XYZ* tristimulus values to *DJI D-Gamut* colourspace matrix. - -MATRIX_XYZ_TO_DJI_D_GAMUT : array_like, (3, 3) -""" +PRIMARIES_DJI_D_GAMUT: NDArray = np.array( + [ + [0.71, 0.31], + [0.21, 0.88], + [0.09, -0.08], + ] +) +"""*DJI D-Gamut* colourspace primaries.""" + +WHITEPOINT_NAME_DJI_D_GAMUT: str = "D65" +"""*DJI D-Gamut* colourspace whitepoint name.""" + +CCS_WHITEPOINT_DJI_D_GAMUT: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_DJI_D_GAMUT] +"""*DJI D-Gamut* colourspace whitepoint chromaticity coordinates.""" + +MATRIX_DJI_D_GAMUT_TO_XYZ: NDArray = np.array( + [ + [0.6482, 0.1940, 0.1082], + [0.2830, 0.8132, -0.0962], + [-0.0183, -0.0832, 1.1903], + ] +) +"""*DJI D-Gamut* colourspace to *CIE XYZ* tristimulus values matrix.""" + +MATRIX_XYZ_TO_DJI_D_GAMUT: NDArray = np.array( + [ + [1.7257, -0.4314, -0.1917], + [-0.6025, 1.3906, 0.1671], + [-0.0156, 0.0905, 0.8489], + ] +) +"""*CIE XYZ* tristimulus values to *DJI D-Gamut* colourspace matrix.""" -RGB_COLOURSPACE_DJI_D_GAMUT = RGB_Colourspace( - 'DJI D-Gamut', +RGB_COLOURSPACE_DJI_D_GAMUT: RGB_Colourspace = RGB_Colourspace( + "DJI D-Gamut", PRIMARIES_DJI_D_GAMUT, CCS_WHITEPOINT_DJI_D_GAMUT, WHITEPOINT_NAME_DJI_D_GAMUT, @@ -93,9 +90,7 @@ RGB_COLOURSPACE_DJI_D_GAMUT.__doc__ = """ *DJI_D-Gamut* colourspace. - References - ---------- - :cite:`DJI2017` - -RGB_COLOURSPACE_DJI_D_GAMUT : RGB_Colourspace +References +---------- +:cite:`DJI2017` """ diff --git a/colour/models/rgb/datasets/don_rgb_4.py b/colour/models/rgb/datasets/don_rgb_4.py index d21cacb5be..0a57a05177 100644 --- a/colour/models/rgb/datasets/don_rgb_4.py +++ b/colour/models/rgb/datasets/don_rgb_4.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Don RGB 4 Colourspace ===================== @@ -13,71 +12,62 @@ http://www.hutchcolor.com/profiles/DonRGB4.zip """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from functools import partial from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, gamma_function, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + gamma_function, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_DON_RGB_4', 'WHITEPOINT_NAME_DON_RGB_4', - 'CCS_WHITEPOINT_DON_RGB_4', 'MATRIX_DON_RGB_4_TO_XYZ', - 'MATRIX_XYZ_TO_DON_RGB_4', 'RGB_COLOURSPACE_DON_RGB_4' + "PRIMARIES_DON_RGB_4", + "WHITEPOINT_NAME_DON_RGB_4", + "CCS_WHITEPOINT_DON_RGB_4", + "MATRIX_DON_RGB_4_TO_XYZ", + "MATRIX_XYZ_TO_DON_RGB_4", + "RGB_COLOURSPACE_DON_RGB_4", ] -PRIMARIES_DON_RGB_4 = np.array([ - [0.696120689655172, 0.299568965517241], - [0.214682981090100, 0.765294771968854], - [0.129937629937630, 0.035343035343035], -]) -""" -*Don RGB 4* colourspace primaries. - -PRIMARIES_DON_RGB_4 : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_DON_RGB_4 = 'D50' -""" -*Don RGB 4* colourspace whitepoint name. - -WHITEPOINT_NAME_DON_RGB_4 : unicode -""" - -CCS_WHITEPOINT_DON_RGB_4 = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_DON_RGB_4]) -""" -*Don RGB 4* colourspace whitepoint chromaticity coordinates. +PRIMARIES_DON_RGB_4: NDArray = np.array( + [ + [0.696120689655172, 0.299568965517241], + [0.214682981090100, 0.765294771968854], + [0.129937629937630, 0.035343035343035], + ] +) +"""*Don RGB 4* colourspace primaries.""" -CCS_WHITEPOINT_DON_RGB_4 : ndarray -""" +WHITEPOINT_NAME_DON_RGB_4: str = "D50" +"""*Don RGB 4* colourspace whitepoint name.""" -MATRIX_DON_RGB_4_TO_XYZ = normalised_primary_matrix(PRIMARIES_DON_RGB_4, - CCS_WHITEPOINT_DON_RGB_4) -""" -*Don RGB 4* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_DON_RGB_4_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_DON_RGB_4: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_DON_RGB_4] +"""*Don RGB 4* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_DON_RGB_4 = np.linalg.inv(MATRIX_DON_RGB_4_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *Don RGB 4* colourspace matrix. +MATRIX_DON_RGB_4_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_DON_RGB_4, CCS_WHITEPOINT_DON_RGB_4 +) +"""*Don RGB 4* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_DON_RGB_4 : array_like, (3, 3) -""" +MATRIX_XYZ_TO_DON_RGB_4: NDArray = np.linalg.inv(MATRIX_DON_RGB_4_TO_XYZ) +"""*CIE XYZ* tristimulus values to *Don RGB 4* colourspace matrix.""" -RGB_COLOURSPACE_DON_RGB_4 = RGB_Colourspace( - 'Don RGB 4', +RGB_COLOURSPACE_DON_RGB_4: RGB_Colourspace = RGB_Colourspace( + "Don RGB 4", PRIMARIES_DON_RGB_4, CCS_WHITEPOINT_DON_RGB_4, WHITEPOINT_NAME_DON_RGB_4, @@ -92,6 +82,4 @@ References ---------- :cite:`HutchColorg` - -RGB_COLOURSPACE_DON_RGB_4 : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/eci_rgb_v2.py b/colour/models/rgb/datasets/eci_rgb_v2.py index 00e6bfce91..013661e333 100644 --- a/colour/models/rgb/datasets/eci_rgb_v2.py +++ b/colour/models/rgb/datasets/eci_rgb_v2.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ ECI RGB v2 Colourspace ====================== @@ -14,82 +13,80 @@ http://www.eci.org/_media/downloads/icc_profiles_from_eci/ecirgbv20.zip """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from functools import partial -from colour.colorimetry import (CCS_ILLUMINANTS, lightness_CIE1976, - luminance_CIE1976) +from colour.colorimetry import ( + CCS_ILLUMINANTS, + lightness_CIE1976, + luminance_CIE1976, +) +from colour.hints import ( + Callable, + NDArray, + FloatingOrArrayLike, + FloatingOrNDArray, +) from colour.models.rgb import RGB_Colourspace, normalised_primary_matrix from colour.utilities import as_float_array -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_ECI_RGB_V2', 'WHITEPOINT_NAME_ECI_RGB_V', - 'CCS_WHITEPOINT_ECI_RGB_V2', 'MATRIX_ECI_RGB_V2_TO_XYZ', - 'MATRIX_XYZ_TO_ECI_RGB_V2', 'RGB_COLOURSPACE_ECI_RGB_V2' + "PRIMARIES_ECI_RGB_V2", + "WHITEPOINT_NAME_ECI_RGB_V", + "CCS_WHITEPOINT_ECI_RGB_V2", + "MATRIX_ECI_RGB_V2_TO_XYZ", + "MATRIX_XYZ_TO_ECI_RGB_V2", + "RGB_COLOURSPACE_ECI_RGB_V2", ] -PRIMARIES_ECI_RGB_V2 = np.array([ - [0.670103092783505, 0.329896907216495], - [0.209905660377358, 0.709905660377358], - [0.140061791967044, 0.080329557157570], -]) -""" -*ECI RGB v2* colourspace primaries. - -PRIMARIES_ECI_RGB_V2 : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_ECI_RGB_V = 'D50' -""" -*ECI RGB v2* colourspace whitepoint name. - -WHITEPOINT_NAME_ECI_RGB_V : unicode -""" - -CCS_WHITEPOINT_ECI_RGB_V2 = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_ECI_RGB_V]) -""" -*ECI RGB v2* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_ECI_RGB_V2 : ndarray -""" +PRIMARIES_ECI_RGB_V2: NDArray = np.array( + [ + [0.670103092783505, 0.329896907216495], + [0.209905660377358, 0.709905660377358], + [0.140061791967044, 0.080329557157570], + ] +) +"""*ECI RGB v2* colourspace primaries.""" -MATRIX_ECI_RGB_V2_TO_XYZ = normalised_primary_matrix( - PRIMARIES_ECI_RGB_V2, CCS_WHITEPOINT_ECI_RGB_V2) -""" -*ECI RGB v2* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_ECI_RGB_V: str = "D50" +"""*ECI RGB v2* colourspace whitepoint name.""" -MATRIX_ECI_RGB_V2_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_ECI_RGB_V2: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_ECI_RGB_V] +"""*ECI RGB v2* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_ECI_RGB_V2 = np.linalg.inv(MATRIX_ECI_RGB_V2_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *ECI RGB v2* colourspace matrix. +MATRIX_ECI_RGB_V2_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_ECI_RGB_V2, CCS_WHITEPOINT_ECI_RGB_V2 +) +"""*ECI RGB v2* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_ECI_RGB_V2 : array_like, (3, 3) -""" +MATRIX_XYZ_TO_ECI_RGB_V2: NDArray = np.linalg.inv(MATRIX_ECI_RGB_V2_TO_XYZ) +"""*CIE XYZ* tristimulus values to *ECI RGB v2* colourspace matrix.""" -def _scale_domain_0_100_range_0_1(a, callable_): +def _scale_domain_0_100_range_0_1( + a: FloatingOrArrayLike, callable_: Callable +) -> FloatingOrNDArray: """ - Scales the input domain of given *luminance* :math:`Y` or *Lightness* + Scale the input domain of given *luminance* :math:`Y` or *Lightness* :math:`L^*` array to [0, 100], call the given callable, and scales the output range to [0, 1]. Parameters ---------- - a : numeric or array_like + a *Luminance* :math:`Y` or *Lightness* :math:`L^*` array. - callable_ : callable + callable_ *Luminance* :math:`Y` or *Lightness* :math:`L^*` computation definition, i.e., :func:`colour.colorimetry.lightness_CIE1976` or :func:`colour.colorimetry.luminance_CIE1976`. Reference white @@ -97,7 +94,7 @@ def _scale_domain_0_100_range_0_1(a, callable_): Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Scaled *luminance* :math:`Y` or *Lightness* :math:`L^*` array. """ @@ -106,8 +103,8 @@ def _scale_domain_0_100_range_0_1(a, callable_): return callable_(a * 100, Y_n=100) / 100 -RGB_COLOURSPACE_ECI_RGB_V2 = RGB_Colourspace( - 'ECI RGB v2', +RGB_COLOURSPACE_ECI_RGB_V2: RGB_Colourspace = RGB_Colourspace( + "ECI RGB v2", PRIMARIES_ECI_RGB_V2, CCS_WHITEPOINT_ECI_RGB_V2, WHITEPOINT_NAME_ECI_RGB_V, @@ -122,6 +119,4 @@ def _scale_domain_0_100_range_0_1(a, callable_): References ---------- :cite:`EuropeanColorInitiative2002a` - -RGB_COLOURSPACE_ECI_RGB_V2 : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/ekta_space_ps5.py b/colour/models/rgb/datasets/ekta_space_ps5.py index b2a5096fed..65bc04172f 100644 --- a/colour/models/rgb/datasets/ekta_space_ps5.py +++ b/colour/models/rgb/datasets/ekta_space_ps5.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Ekta Space PS 5 Colourspace =========================== @@ -13,70 +12,64 @@ https://www.josephholmes.com/userfiles/Ekta_Space_PS5_JHolmes.zip """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from functools import partial + from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, gamma_function, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + gamma_function, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_EKTA_SPACE_PS_5', 'WHITEPOINT_NAME_EKTA_SPACE_PS_5_V', - 'CCS_WHITEPOINT_EKTA_SPACE_PS_5', 'MATRIX_EKTA_SPACE_PS_5_TO_XYZ', - 'MATRIX_XYZ_TO_EKTA_SPACE_PS_5', 'RGB_COLOURSPACE_EKTA_SPACE_PS_5' + "PRIMARIES_EKTA_SPACE_PS_5", + "WHITEPOINT_NAME_EKTA_SPACE_PS_5_V", + "CCS_WHITEPOINT_EKTA_SPACE_PS_5", + "MATRIX_EKTA_SPACE_PS_5_TO_XYZ", + "MATRIX_XYZ_TO_EKTA_SPACE_PS_5", + "RGB_COLOURSPACE_EKTA_SPACE_PS_5", ] -PRIMARIES_EKTA_SPACE_PS_5 = np.array([ - [0.694736842105263, 0.305263157894737], - [0.260000000000000, 0.700000000000000], - [0.109728506787330, 0.004524886877828], -]) -""" -*Ekta Space PS 5* colourspace primaries. - -PRIMARIES_EKTA_SPACE_PS_5 : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_EKTA_SPACE_PS_5_V = 'D50' -""" -*Ekta Space PS 5* colourspace whitepoint name. - -WHITEPOINT_NAME_EKTA_SPACE_PS_5_V : unicode -""" - -CCS_WHITEPOINT_EKTA_SPACE_PS_5 = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_EKTA_SPACE_PS_5_V]) -""" -*Ekta Space PS 5* colourspace whitepoint chromaticity coordinates. +PRIMARIES_EKTA_SPACE_PS_5: NDArray = np.array( + [ + [0.694736842105263, 0.305263157894737], + [0.260000000000000, 0.700000000000000], + [0.109728506787330, 0.004524886877828], + ] +) +"""*Ekta Space PS 5* colourspace primaries.""" -CCS_WHITEPOINT_EKTA_SPACE_PS_5 : ndarray -""" +WHITEPOINT_NAME_EKTA_SPACE_PS_5_V: str = "D50" +"""*Ekta Space PS 5* colourspace whitepoint name.""" -MATRIX_EKTA_SPACE_PS_5_TO_XYZ = normalised_primary_matrix( - PRIMARIES_EKTA_SPACE_PS_5, CCS_WHITEPOINT_EKTA_SPACE_PS_5) -""" -*Ekta Space PS 5* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_EKTA_SPACE_PS_5_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_EKTA_SPACE_PS_5: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_EKTA_SPACE_PS_5_V] +"""*Ekta Space PS 5* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_EKTA_SPACE_PS_5 = np.linalg.inv(MATRIX_EKTA_SPACE_PS_5_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *Ekta Space PS 5* colourspace matrix. +MATRIX_EKTA_SPACE_PS_5_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_EKTA_SPACE_PS_5, CCS_WHITEPOINT_EKTA_SPACE_PS_5 +) +"""*Ekta Space PS 5* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_EKTA_SPACE_PS_5 : array_like, (3, 3) -""" +MATRIX_XYZ_TO_EKTA_SPACE_PS_5: NDArray = np.linalg.inv( + MATRIX_EKTA_SPACE_PS_5_TO_XYZ +) +"""*CIE XYZ* tristimulus values to *Ekta Space PS 5* colourspace matrix.""" -RGB_COLOURSPACE_EKTA_SPACE_PS_5 = RGB_Colourspace( - 'Ekta Space PS 5', +RGB_COLOURSPACE_EKTA_SPACE_PS_5: RGB_Colourspace = RGB_Colourspace( + "Ekta Space PS 5", PRIMARIES_EKTA_SPACE_PS_5, CCS_WHITEPOINT_EKTA_SPACE_PS_5, WHITEPOINT_NAME_EKTA_SPACE_PS_5_V, @@ -91,6 +84,4 @@ References ---------- :cite:`Holmesa` - -RGB_COLOURSPACE_EKTA_SPACE_PS_5 : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/filmlight_egamut.py b/colour/models/rgb/datasets/filmlight_egamut.py index 2ba9b3d967..8008f0db82 100755 --- a/colour/models/rgb/datasets/filmlight_egamut.py +++ b/colour/models/rgb/datasets/filmlight_egamut.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ FilmLight E-Gamut Colourspace ============================= @@ -13,72 +12,64 @@ Shaw, Nick. """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, log_encoding_FilmLightTLog, - log_decoding_FilmLightTLog, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + log_encoding_FilmLightTLog, + log_decoding_FilmLightTLog, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_FILMLIGHT_E_GAMUT', 'WHITEPOINT_NAME_FILMLIGHT_E_GAMUT', - 'CCS_WHITEPOINT_FILMLIGHT_E_GAMUT', 'MATRIX_FILMLIGHT_E_GAMUT_TO_XYZ', - 'MATRIX_XYZ_TO_FILMLIGHT_E_GAMUT', 'RGB_COLOURSPACE_FILMLIGHT_E_GAMUT' + "PRIMARIES_FILMLIGHT_E_GAMUT", + "WHITEPOINT_NAME_FILMLIGHT_E_GAMUT", + "CCS_WHITEPOINT_FILMLIGHT_E_GAMUT", + "MATRIX_FILMLIGHT_E_GAMUT_TO_XYZ", + "MATRIX_XYZ_TO_FILMLIGHT_E_GAMUT", + "RGB_COLOURSPACE_FILMLIGHT_E_GAMUT", ] -PRIMARIES_FILMLIGHT_E_GAMUT = np.array([ - [0.8000, 0.3177], - [0.1800, 0.9000], - [0.0650, -0.0805], -]) -""" -*FilmLight E-Gamut* colourspace primaries. - -PRIMARIES_FILMLIGHT_E_GAMUT : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_FILMLIGHT_E_GAMUT = 'D65' -""" -*FilmLight E-Gamut* colourspace whitepoint name. - -CCS_WHITEPOINT_FILMLIGHT_E_GAMUT : unicode -""" - -CCS_WHITEPOINT_FILMLIGHT_E_GAMUT = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_FILMLIGHT_E_GAMUT]) -""" -*FilmLight E-Gamut* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_FILMLIGHT_E_GAMUT : ndarray -""" +PRIMARIES_FILMLIGHT_E_GAMUT: NDArray = np.array( + [ + [0.8000, 0.3177], + [0.1800, 0.9000], + [0.0650, -0.0805], + ] +) +"""*FilmLight E-Gamut* colourspace primaries.""" -MATRIX_FILMLIGHT_E_GAMUT_TO_XYZ = (normalised_primary_matrix( - PRIMARIES_FILMLIGHT_E_GAMUT, CCS_WHITEPOINT_FILMLIGHT_E_GAMUT)) -""" -*FilmLight E-Gamut* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_FILMLIGHT_E_GAMUT: str = "D65" +"""*FilmLight E-Gamut* colourspace whitepoint name.""" -MATRIX_FILMLIGHT_E_GAMUT_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_FILMLIGHT_E_GAMUT: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_FILMLIGHT_E_GAMUT] +"""*FilmLight E-Gamut* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_FILMLIGHT_E_GAMUT = ( - np.linalg.inv(MATRIX_FILMLIGHT_E_GAMUT_TO_XYZ)) -""" -*CIE XYZ* tristimulus values to *FilmLight E-Gamut* colourspace matrix. +MATRIX_FILMLIGHT_E_GAMUT_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_FILMLIGHT_E_GAMUT, CCS_WHITEPOINT_FILMLIGHT_E_GAMUT +) +"""*FilmLight E-Gamut* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_FILMLIGHT_E_GAMUT : array_like, (3, 3) -""" +MATRIX_XYZ_TO_FILMLIGHT_E_GAMUT: NDArray = np.linalg.inv( + MATRIX_FILMLIGHT_E_GAMUT_TO_XYZ +) +"""*CIE XYZ* tristimulus values to *FilmLight E-Gamut* colourspace matrix.""" -RGB_COLOURSPACE_FILMLIGHT_E_GAMUT = RGB_Colourspace( - 'FilmLight E-Gamut', +RGB_COLOURSPACE_FILMLIGHT_E_GAMUT: RGB_Colourspace = RGB_Colourspace( + "FilmLight E-Gamut", PRIMARIES_FILMLIGHT_E_GAMUT, CCS_WHITEPOINT_FILMLIGHT_E_GAMUT, WHITEPOINT_NAME_FILMLIGHT_E_GAMUT, @@ -90,9 +81,7 @@ RGB_COLOURSPACE_FILMLIGHT_E_GAMUT.__doc__ = """ *FilmLight E-Gamut* colourspace. - References - ---------- - :cite:`Siragusano2018a` - -RGB_COLOURSPACE_FILMLIGHT_E_GAMUT : RGB_Colourspace +References +---------- +:cite:`Siragusano2018a` """ diff --git a/colour/models/rgb/datasets/fujifilm_f_gamut.py b/colour/models/rgb/datasets/fujifilm_f_gamut.py index 8329f00241..9a5a4a5c42 100644 --- a/colour/models/rgb/datasets/fujifilm_f_gamut.py +++ b/colour/models/rgb/datasets/fujifilm_f_gamut.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Fujifilm F-Gamut Colourspace ============================ @@ -14,69 +13,62 @@ F-Log_DataSheet_E_Ver.1.0.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, log_encoding_FLog, - normalised_primary_matrix, log_decoding_FLog) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + log_encoding_FLog, + normalised_primary_matrix, + log_decoding_FLog, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - http://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 Colour Developers" +__license__ = "New BSD License - http://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" __all__ = [ - 'PRIMARIES_F_GAMUT', 'WHITEPOINT_NAME_F_GAMUT', 'CCS_WHITEPOINT_F_GAMUT', - 'MATRIX_F_GAMUT_TO_XYZ', 'MATRIX_XYZ_TO_F_GAMUT', 'RGB_COLOURSPACE_F_GAMUT' + "PRIMARIES_F_GAMUT", + "WHITEPOINT_NAME_F_GAMUT", + "CCS_WHITEPOINT_F_GAMUT", + "MATRIX_F_GAMUT_TO_XYZ", + "MATRIX_XYZ_TO_F_GAMUT", + "RGB_COLOURSPACE_F_GAMUT", ] -PRIMARIES_F_GAMUT = np.array([ - [0.70800, 0.29200], - [0.17000, 0.79700], - [0.13100, 0.04600], -]) -""" -*Fujifilm F-Gamut* colourspace primaries. - -PRIMARIES_F_GAMUT : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_F_GAMUT = 'D65' -""" -*Fujifilm F-Gamut* colourspace whitepoint name. - -CCS_WHITEPOINT_F_GAMUT : unicode -""" - -CCS_WHITEPOINT_F_GAMUT = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_F_GAMUT]) -""" -*Fujifilm F-Gamut* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_F_GAMUT : ndarray -""" +PRIMARIES_F_GAMUT: NDArray = np.array( + [ + [0.70800, 0.29200], + [0.17000, 0.79700], + [0.13100, 0.04600], + ] +) +"""*Fujifilm F-Gamut* colourspace primaries.""" -MATRIX_F_GAMUT_TO_XYZ = normalised_primary_matrix(PRIMARIES_F_GAMUT, - CCS_WHITEPOINT_F_GAMUT) -""" -*Fujifilm F-Gamut* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_F_GAMUT: str = "D65" +"""*Fujifilm F-Gamut* colourspace whitepoint name.""" -MATRIX_F_GAMUT_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_F_GAMUT: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_F_GAMUT] +"""*Fujifilm F-Gamut* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_F_GAMUT = np.linalg.inv(MATRIX_F_GAMUT_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *Fujifilm F-Gamut* colourspace matrix. +MATRIX_F_GAMUT_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_F_GAMUT, CCS_WHITEPOINT_F_GAMUT +) +"""*Fujifilm F-Gamut* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_F_GAMUT : array_like, (3, 3) -""" +MATRIX_XYZ_TO_F_GAMUT: NDArray = np.linalg.inv(MATRIX_F_GAMUT_TO_XYZ) +"""*CIE XYZ* tristimulus values to *Fujifilm F-Gamut* colourspace matrix.""" -RGB_COLOURSPACE_F_GAMUT = RGB_Colourspace( - 'F-Gamut', +RGB_COLOURSPACE_F_GAMUT: RGB_Colourspace = RGB_Colourspace( + "F-Gamut", PRIMARIES_F_GAMUT, CCS_WHITEPOINT_F_GAMUT, WHITEPOINT_NAME_F_GAMUT, @@ -91,6 +83,4 @@ References ---------- :cite:`Fujifilm2016` - -RGB_COLOURSPACE_F_GAMUT : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/gopro.py b/colour/models/rgb/datasets/gopro.py index 56affbe632..485f56dda8 100644 --- a/colour/models/rgb/datasets/gopro.py +++ b/colour/models/rgb/datasets/gopro.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ GoPro Colourspaces ================== @@ -25,70 +24,64 @@ https://www.colour-science.org/posts/red-colourspaces-derivation """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, log_decoding_Protune, - log_encoding_Protune, normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + log_decoding_Protune, + log_encoding_Protune, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_PROTUNE_NATIVE', 'WHITEPOINT_NAME_PROTUNE_NATIVE', - 'CCS_WHITEPOINT_PROTUNE_NATIVE', 'MATRIX_PROTUNE_NATIVE_TO_XYZ', - 'MATRIX_XYZ_TO_PROTUNE_NATIVE', 'RGB_COLOURSPACE_PROTUNE_NATIVE' + "PRIMARIES_PROTUNE_NATIVE", + "WHITEPOINT_NAME_PROTUNE_NATIVE", + "CCS_WHITEPOINT_PROTUNE_NATIVE", + "MATRIX_PROTUNE_NATIVE_TO_XYZ", + "MATRIX_XYZ_TO_PROTUNE_NATIVE", + "RGB_COLOURSPACE_PROTUNE_NATIVE", ] -PRIMARIES_PROTUNE_NATIVE = np.array([ - [0.698480461493841, 0.193026445370121], - [0.329555378387345, 1.024596624134644], - [0.108442631407675, -0.034678569754016], -]) -""" -*Protune Native* colourspace primaries. - -PRIMARIES_PROTUNE_NATIVE : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_PROTUNE_NATIVE = 'D65' -""" -*Protune Native* colourspace whitepoint name. - -WHITEPOINT_NAME_PROTUNE_NATIVE : unicode -""" - -CCS_WHITEPOINT_PROTUNE_NATIVE = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_PROTUNE_NATIVE]) -""" -*Protune Native* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_PROTUNE_NATIVE : ndarray -""" +PRIMARIES_PROTUNE_NATIVE: NDArray = np.array( + [ + [0.698480461493841, 0.193026445370121], + [0.329555378387345, 1.024596624134644], + [0.108442631407675, -0.034678569754016], + ] +) +"""*Protune Native* colourspace primaries.""" -MATRIX_PROTUNE_NATIVE_TO_XYZ = normalised_primary_matrix( - PRIMARIES_PROTUNE_NATIVE, CCS_WHITEPOINT_PROTUNE_NATIVE) -""" -*Protune Native* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_PROTUNE_NATIVE: str = "D65" +"""*Protune Native* colourspace whitepoint name.""" -MATRIX_PROTUNE_NATIVE_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_PROTUNE_NATIVE: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_PROTUNE_NATIVE] +"""*Protune Native* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_PROTUNE_NATIVE = np.linalg.inv(MATRIX_PROTUNE_NATIVE_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *Protune Native* colourspace matrix. +MATRIX_PROTUNE_NATIVE_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_PROTUNE_NATIVE, CCS_WHITEPOINT_PROTUNE_NATIVE +) +"""*Protune Native* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_PROTUNE_NATIVE : array_like, (3, 3) -""" +MATRIX_XYZ_TO_PROTUNE_NATIVE: NDArray = np.linalg.inv( + MATRIX_PROTUNE_NATIVE_TO_XYZ +) +"""*CIE XYZ* tristimulus values to *Protune Native* colourspace matrix.""" -RGB_COLOURSPACE_PROTUNE_NATIVE = RGB_Colourspace( - 'Protune Native', +RGB_COLOURSPACE_PROTUNE_NATIVE: RGB_Colourspace = RGB_Colourspace( + "Protune Native", PRIMARIES_PROTUNE_NATIVE, CCS_WHITEPOINT_PROTUNE_NATIVE, WHITEPOINT_NAME_PROTUNE_NATIVE, @@ -103,6 +96,4 @@ References ---------- :cite:`GoPro2016a`, :cite:`Mansencal2015d` - -RGB_COLOURSPACE_PROTUNE_NATIVE : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/itur_bt_2020.py b/colour/models/rgb/datasets/itur_bt_2020.py index 01222c0997..5fcfbc0d4f 100644 --- a/colour/models/rgb/datasets/itur_bt_2020.py +++ b/colour/models/rgb/datasets/itur_bt_2020.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ ITU-R BT.2020 Colourspace ========================= @@ -17,69 +16,62 @@ R-REC-BT.2020-2-201510-I!!PDF-E.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, normalised_primary_matrix, - eotf_inverse_BT2020, eotf_BT2020) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + normalised_primary_matrix, + eotf_inverse_BT2020, + eotf_BT2020, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_BT2020', 'WHITEPOINT_NAME_BT2020', 'CCS_WHITEPOINT_BT2020', - 'MATRIX_BT2020_TO_XYZ', 'MATRIX_XYZ_TO_BT2020', 'RGB_COLOURSPACE_BT2020' + "PRIMARIES_BT2020", + "WHITEPOINT_NAME_BT2020", + "CCS_WHITEPOINT_BT2020", + "MATRIX_BT2020_TO_XYZ", + "MATRIX_XYZ_TO_BT2020", + "RGB_COLOURSPACE_BT2020", ] -PRIMARIES_BT2020 = np.array([ - [0.7080, 0.2920], - [0.1700, 0.7970], - [0.1310, 0.0460], -]) -""" -*ITU-R BT.2020* colourspace primaries. - -PRIMARIES_BT2020 : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_BT2020 = 'D65' -""" -*ITU-R BT.2020* colourspace whitepoint name. - -WHITEPOINT_NAME_BT2020 : unicode -""" - -CCS_WHITEPOINT_BT2020 = (CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'] - [WHITEPOINT_NAME_BT2020]) -""" -*ITU-R BT.2020* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_BT2020 : ndarray -""" +PRIMARIES_BT2020: NDArray = np.array( + [ + [0.7080, 0.2920], + [0.1700, 0.7970], + [0.1310, 0.0460], + ] +) +"""*ITU-R BT.2020* colourspace primaries.""" -MATRIX_BT2020_TO_XYZ = normalised_primary_matrix(PRIMARIES_BT2020, - CCS_WHITEPOINT_BT2020) -""" -*ITU-R BT.2020* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_BT2020: str = "D65" +"""*ITU-R BT.2020* colourspace whitepoint name.""" -MATRIX_BT2020_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_BT2020: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_BT2020] +"""*ITU-R BT.2020* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_BT2020 = np.linalg.inv(MATRIX_BT2020_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *ITU-R BT.2020* colourspace matrix. +MATRIX_BT2020_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_BT2020, CCS_WHITEPOINT_BT2020 +) +"""*ITU-R BT.2020* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_BT2020 : array_like, (3, 3) -""" +MATRIX_XYZ_TO_BT2020: NDArray = np.linalg.inv(MATRIX_BT2020_TO_XYZ) +"""*CIE XYZ* tristimulus values to *ITU-R BT.2020* colourspace matrix.""" -RGB_COLOURSPACE_BT2020 = RGB_Colourspace( - 'ITU-R BT.2020', +RGB_COLOURSPACE_BT2020: RGB_Colourspace = RGB_Colourspace( + "ITU-R BT.2020", PRIMARIES_BT2020, CCS_WHITEPOINT_BT2020, WHITEPOINT_NAME_BT2020, @@ -100,6 +92,4 @@ References ---------- :cite:`InternationalTelecommunicationUnion2015h` - -RGB_COLOURSPACE_BT2020 : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/itur_bt_470.py b/colour/models/rgb/datasets/itur_bt_470.py index be05a5bbb3..ba623ba466 100644 --- a/colour/models/rgb/datasets/itur_bt_470.py +++ b/colour/models/rgb/datasets/itur_bt_470.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ ITU-R BT.470 Colourspaces ========================= @@ -17,74 +16,68 @@ R-REC-BT.470-6-199811-S!!PDF-E.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from functools import partial from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, gamma_function, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + gamma_function, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_BT470_525', 'CCS_WHITEPOINT_BT470_525', - 'WHITEPOINT_NAME_BT470_525', 'MATRIX_BT470_525_TO_XYZ', - 'MATRIX_XYZ_TO_BT470_525', 'RGB_COLOURSPACE_BT470_525', - 'PRIMARIES_BT470_625', 'CCS_WHITEPOINT_BT470_625', - 'WHITEPOINT_NAME_BT470_625', 'MATRIX_BT470_625_TO_XYZ', - 'MATRIX_XYZ_TO_BT470_625', 'RGB_COLOURSPACE_BT470_625' + "PRIMARIES_BT470_525", + "CCS_WHITEPOINT_BT470_525", + "WHITEPOINT_NAME_BT470_525", + "MATRIX_BT470_525_TO_XYZ", + "MATRIX_XYZ_TO_BT470_525", + "RGB_COLOURSPACE_BT470_525", + "PRIMARIES_BT470_625", + "CCS_WHITEPOINT_BT470_625", + "WHITEPOINT_NAME_BT470_625", + "MATRIX_BT470_625_TO_XYZ", + "MATRIX_XYZ_TO_BT470_625", + "RGB_COLOURSPACE_BT470_625", ] -PRIMARIES_BT470_525 = np.array([ - [0.6700, 0.3300], - [0.2100, 0.7100], - [0.1400, 0.0800], -]) -""" -*ITU-R BT.470 - 525* colourspace primaries. - -PRIMARIES_BT470_525 : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_BT470_525 = 'C' -""" -*ITU-R BT.470 - 525* colourspace whitepoint name. - -WHITEPOINT_NAME_BT470_525 : unicode -""" - -CCS_WHITEPOINT_BT470_525 = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_BT470_525]) -""" -*ITU-R BT.470 - 525* colourspace whitepoint chromaticity coordinates. +PRIMARIES_BT470_525: NDArray = np.array( + [ + [0.6700, 0.3300], + [0.2100, 0.7100], + [0.1400, 0.0800], + ] +) +"""*ITU-R BT.470 - 525* colourspace primaries.""" -CCS_WHITEPOINT_BT470_525 : ndarray -""" +WHITEPOINT_NAME_BT470_525: str = "C" +"""*ITU-R BT.470 - 525* colourspace whitepoint name.""" -MATRIX_BT470_525_TO_XYZ = normalised_primary_matrix(PRIMARIES_BT470_525, - CCS_WHITEPOINT_BT470_525) -""" -*ITU-R BT.470 - 525* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_BT470_525_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_BT470_525: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_BT470_525] +"""*ITU-R BT.470 - 525* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_BT470_525 = np.linalg.inv(MATRIX_BT470_525_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *ITU-R BT.470 - 525* colourspace matrix. +MATRIX_BT470_525_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_BT470_525, CCS_WHITEPOINT_BT470_525 +) +"""*ITU-R BT.470 - 525* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_BT470_525 : array_like, (3, 3) -""" +MATRIX_XYZ_TO_BT470_525: NDArray = np.linalg.inv(MATRIX_BT470_525_TO_XYZ) +"""*CIE XYZ* tristimulus values to *ITU-R BT.470 - 525* colourspace matrix.""" -RGB_COLOURSPACE_BT470_525 = RGB_Colourspace( - 'ITU-R BT.470 - 525', +RGB_COLOURSPACE_BT470_525: RGB_Colourspace = RGB_Colourspace( + "ITU-R BT.470 - 525", PRIMARIES_BT470_525, CCS_WHITEPOINT_BT470_525, WHITEPOINT_NAME_BT470_525, @@ -99,53 +92,35 @@ References ---------- :cite:`InternationalTelecommunicationUnion1998a` - -RGB_COLOURSPACE_BT470_525 : RGB_Colourspace """ -PRIMARIES_BT470_625 = np.array([ - [0.6400, 0.3300], - [0.2900, 0.6000], - [0.1500, 0.0600], -]) -""" -*ITU-R BT.470 - 625* colourspace primaries. - -PRIMARIES_BT470_625 : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_BT470_625 = 'D65' -""" -*ITU-R BT.470 - 625* colourspace whitepoint name. - -WHITEPOINT_NAME_BT470_625 : unicode -""" - -CCS_WHITEPOINT_BT470_625 = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_BT470_625]) -""" -*ITU-R BT.470 - 625* colourspace whitepoint chromaticity coordinates. +PRIMARIES_BT470_625: NDArray = np.array( + [ + [0.6400, 0.3300], + [0.2900, 0.6000], + [0.1500, 0.0600], + ] +) +"""*ITU-R BT.470 - 625* colourspace primaries.""" -CCS_WHITEPOINT_BT470_625 : ndarray -""" +WHITEPOINT_NAME_BT470_625: str = "D65" +"""*ITU-R BT.470 - 625* colourspace whitepoint name.""" -MATRIX_BT470_625_TO_XYZ = normalised_primary_matrix(PRIMARIES_BT470_625, - CCS_WHITEPOINT_BT470_625) -""" -*ITU-R BT.470 - 625* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_BT470_625_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_BT470_625: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_BT470_625] +"""*ITU-R BT.470 - 625* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_BT470_625 = np.linalg.inv(MATRIX_BT470_625_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *ITU-R BT.470 - 625* colourspace matrix. +MATRIX_BT470_625_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_BT470_625, CCS_WHITEPOINT_BT470_625 +) +"""*ITU-R BT.470 - 625* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_BT470_625 : array_like, (3, 3) -""" +MATRIX_XYZ_TO_BT470_625: NDArray = np.linalg.inv(MATRIX_BT470_625_TO_XYZ) +"""*CIE XYZ* tristimulus values to *ITU-R BT.470 - 625* colourspace matrix.""" -RGB_COLOURSPACE_BT470_625 = RGB_Colourspace( - 'ITU-R BT.470 - 625', +RGB_COLOURSPACE_BT470_625: RGB_Colourspace = RGB_Colourspace( + "ITU-R BT.470 - 625", PRIMARIES_BT470_625, CCS_WHITEPOINT_BT470_625, WHITEPOINT_NAME_BT470_625, @@ -160,6 +135,4 @@ References ---------- :cite:`InternationalTelecommunicationUnion1998a` - -RGB_COLOURSPACE_BT470_625 : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/itur_bt_709.py b/colour/models/rgb/datasets/itur_bt_709.py index ae6f40f88f..5ca499cf5b 100644 --- a/colour/models/rgb/datasets/itur_bt_709.py +++ b/colour/models/rgb/datasets/itur_bt_709.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ ITU-R BT.709 Colourspace ======================== @@ -17,69 +16,62 @@ R-REC-BT.709-6-201506-I!!PDF-E.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, oetf_BT709, oetf_inverse_BT709, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + oetf_BT709, + oetf_inverse_BT709, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_BT709', 'CCS_WHITEPOINT_BT709', 'WHITEPOINT_NAME_BT709', - 'MATRIX_BT709_TO_XYZ', 'MATRIX_XYZ_TO_BT709', 'RGB_COLOURSPACE_BT709' + "PRIMARIES_BT709", + "CCS_WHITEPOINT_BT709", + "WHITEPOINT_NAME_BT709", + "MATRIX_BT709_TO_XYZ", + "MATRIX_XYZ_TO_BT709", + "RGB_COLOURSPACE_BT709", ] -PRIMARIES_BT709 = np.array([ - [0.6400, 0.3300], - [0.3000, 0.6000], - [0.1500, 0.0600], -]) -""" -*ITU-R BT.709* colourspace primaries. - -PRIMARIES_BT709 : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_BT709 = 'D65' -""" -*ITU-R BT.709* colourspace whitepoint name. - -WHITEPOINT_NAME_BT709 : unicode -""" - -CCS_WHITEPOINT_BT709 = (CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'][ - WHITEPOINT_NAME_BT709]) -""" -*ITU-R BT.709* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_BT709 : ndarray -""" +PRIMARIES_BT709: NDArray = np.array( + [ + [0.6400, 0.3300], + [0.3000, 0.6000], + [0.1500, 0.0600], + ] +) +"""*ITU-R BT.709* colourspace primaries.""" -MATRIX_BT709_TO_XYZ = normalised_primary_matrix(PRIMARIES_BT709, - CCS_WHITEPOINT_BT709) -""" -*ITU-R BT.709* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_BT709: str = "D65" +"""*ITU-R BT.709* colourspace whitepoint name.""" -MATRIX_BT709_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_BT709: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_BT709] +"""*ITU-R BT.709* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_BT709 = np.linalg.inv(MATRIX_BT709_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *ITU-R BT.709* colourspace matrix. +MATRIX_BT709_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_BT709, CCS_WHITEPOINT_BT709 +) +"""*ITU-R BT.709* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_BT709 : array_like, (3, 3) -""" +MATRIX_XYZ_TO_BT709: NDArray = np.linalg.inv(MATRIX_BT709_TO_XYZ) +"""*CIE XYZ* tristimulus values to *ITU-R BT.709* colourspace matrix.""" -RGB_COLOURSPACE_BT709 = RGB_Colourspace( - 'ITU-R BT.709', +RGB_COLOURSPACE_BT709: RGB_Colourspace = RGB_Colourspace( + "ITU-R BT.709", PRIMARIES_BT709, CCS_WHITEPOINT_BT709, WHITEPOINT_NAME_BT709, @@ -94,6 +86,4 @@ References ---------- :cite:`InternationalTelecommunicationUnion2015i` - -RGB_COLOURSPACE_BT709 : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/max_rgb.py b/colour/models/rgb/datasets/max_rgb.py index 59279f7686..3b264abbf5 100644 --- a/colour/models/rgb/datasets/max_rgb.py +++ b/colour/models/rgb/datasets/max_rgb.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Max RGB Colourspace =================== @@ -13,70 +12,62 @@ http://www.hutchcolor.com/profiles/MaxRGB.zip """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from functools import partial from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, gamma_function, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + gamma_function, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_MAX_RGB', 'WHITEPOINT_NAME_MAX_RGB', 'CCS_WHITEPOINT_MAX_RGB', - 'MATRIX_MAX_RGB_TO_XYZ', 'MATRIX_XYZ_TO_MAX_RGB', 'RGB_COLOURSPACE_MAX_RGB' + "PRIMARIES_MAX_RGB", + "WHITEPOINT_NAME_MAX_RGB", + "CCS_WHITEPOINT_MAX_RGB", + "MATRIX_MAX_RGB_TO_XYZ", + "MATRIX_XYZ_TO_MAX_RGB", + "RGB_COLOURSPACE_MAX_RGB", ] -PRIMARIES_MAX_RGB = np.array([ - [0.73413379, 0.26586621], - [0.10039113, 0.89960887], - [0.03621495, 0.00000000], -]) -""" -*Max RGB* colourspace primaries. - -PRIMARIES_MAX_RGB : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_MAX_RGB = 'D50' -""" -*Max RGB* colourspace whitepoint name. - -WHITEPOINT_NAME_MAX_RGB : unicode -""" - -CCS_WHITEPOINT_MAX_RGB = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_MAX_RGB]) -""" -*Max RGB* colourspace whitepoint chromaticity coordinates. +PRIMARIES_MAX_RGB: NDArray = np.array( + [ + [0.73413379, 0.26586621], + [0.10039113, 0.89960887], + [0.03621495, 0.00000000], + ] +) +"""*Max RGB* colourspace primaries.""" -CCS_WHITEPOINT_MAX_RGB : ndarray -""" +WHITEPOINT_NAME_MAX_RGB: str = "D50" +"""*Max RGB* colourspace whitepoint name.""" -MATRIX_MAX_RGB_TO_XYZ = normalised_primary_matrix(PRIMARIES_MAX_RGB, - CCS_WHITEPOINT_MAX_RGB) -""" -*Max RGB* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_MAX_RGB_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_MAX_RGB: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_MAX_RGB] +"""*Max RGB* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_MAX_RGB = np.linalg.inv(MATRIX_MAX_RGB_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *Max RGB* colourspace matrix. +MATRIX_MAX_RGB_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_MAX_RGB, CCS_WHITEPOINT_MAX_RGB +) +"""*Max RGB* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_MAX_RGB : array_like, (3, 3) -""" +MATRIX_XYZ_TO_MAX_RGB: NDArray = np.linalg.inv(MATRIX_MAX_RGB_TO_XYZ) +"""*CIE XYZ* tristimulus values to *Max RGB* colourspace matrix.""" -RGB_COLOURSPACE_MAX_RGB = RGB_Colourspace( - 'Max RGB', +RGB_COLOURSPACE_MAX_RGB: RGB_Colourspace = RGB_Colourspace( + "Max RGB", PRIMARIES_MAX_RGB, CCS_WHITEPOINT_MAX_RGB, WHITEPOINT_NAME_MAX_RGB, @@ -91,6 +82,4 @@ References ---------- :cite:`HutchColorf` - -RGB_COLOURSPACE_MAX_RGB : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/nikon_n_gamut.py b/colour/models/rgb/datasets/nikon_n_gamut.py new file mode 100644 index 0000000000..24de222d93 --- /dev/null +++ b/colour/models/rgb/datasets/nikon_n_gamut.py @@ -0,0 +1,87 @@ +""" +Nikon N-Gamut Colourspace +========================= + +Defines the *Nikon N-Gamut* colourspace: + +- :attr:`colour.models.RGB_COLOURSPACE_N_GAMUT`. + +References +---------- +- :cite:`Nikon2018` : Nikon. (2018). N-Log Specification Document - Version + 1.0.0 (pp. 1-5). Retrieved September 9, 2019, from + http://download.nikonimglib.com/archive3/hDCmK00m9JDI03RPruD74xpoU905/\ +N-Log_Specification_(En)01.pdf +""" + +from __future__ import annotations + +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + log_encoding_NLog, + log_decoding_NLog, +) +from colour.models.rgb.datasets.itur_bt_2020 import ( + PRIMARIES_BT2020, + WHITEPOINT_NAME_BT2020, + CCS_WHITEPOINT_BT2020, + MATRIX_BT2020_TO_XYZ, + MATRIX_XYZ_TO_BT2020, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 Colour Developers" +__license__ = "New BSD License - http://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" + +__all__ = [ + "PRIMARIES_N_GAMUT", + "WHITEPOINT_NAME_N_GAMUT", + "CCS_WHITEPOINT_N_GAMUT", + "MATRIX_N_GAMUT_TO_XYZ", + "MATRIX_XYZ_TO_N_GAMUT", + "RGB_COLOURSPACE_N_GAMUT", +] + +PRIMARIES_N_GAMUT: NDArray = PRIMARIES_BT2020 +""" +*Nikon N-Gamut* colourspace primaries. + +Notes +----- +The *Nikon N-Gamut* colourspace gamut is same as the "ITU-R BT.2020" wide +colour gamut. +""" + +WHITEPOINT_NAME_N_GAMUT: str = WHITEPOINT_NAME_BT2020 +"""*Nikon N-Gamut* colourspace whitepoint name.""" + +CCS_WHITEPOINT_N_GAMUT: NDArray = CCS_WHITEPOINT_BT2020 +"""*Nikon N-Gamut* colourspace whitepoint.""" + +MATRIX_N_GAMUT_TO_XYZ: NDArray = MATRIX_BT2020_TO_XYZ +"""*Nikon N-Gamut* colourspace to *CIE XYZ* tristimulus values matrix.""" + +MATRIX_XYZ_TO_N_GAMUT: NDArray = MATRIX_XYZ_TO_BT2020 +"""*CIE XYZ* tristimulus values to *Nikon N-Gamut* colourspace matrix.""" + +RGB_COLOURSPACE_N_GAMUT: RGB_Colourspace = RGB_Colourspace( + "N-Gamut", + PRIMARIES_N_GAMUT, + CCS_WHITEPOINT_N_GAMUT, + WHITEPOINT_NAME_N_GAMUT, + MATRIX_N_GAMUT_TO_XYZ, + MATRIX_XYZ_TO_N_GAMUT, + log_encoding_NLog, + log_decoding_NLog, +) +RGB_COLOURSPACE_N_GAMUT.__doc__ = """ +*Nikon N-Gamut* colourspace. + +References +---------- +:cite:`Nikon2018` +""" diff --git a/colour/models/rgb/datasets/ntsc.py b/colour/models/rgb/datasets/ntsc.py index ae86465722..1b20774c7d 100644 --- a/colour/models/rgb/datasets/ntsc.py +++ b/colour/models/rgb/datasets/ntsc.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ NTSC Colourspaces ================= @@ -21,70 +20,66 @@ Picture and Television Engineers. doi:10.5594/S9781614821649 """ -from __future__ import division, unicode_literals +from __future__ import annotations +from colour.hints import NDArray from colour.models.rgb import RGB_Colourspace from colour.models.rgb.datasets.itur_bt_470 import ( - PRIMARIES_BT470_525, CCS_WHITEPOINT_BT470_525, WHITEPOINT_NAME_BT470_525, - MATRIX_BT470_525_TO_XYZ, MATRIX_XYZ_TO_BT470_525, - RGB_COLOURSPACE_BT470_525) + PRIMARIES_BT470_525, + CCS_WHITEPOINT_BT470_525, + WHITEPOINT_NAME_BT470_525, + MATRIX_BT470_525_TO_XYZ, + MATRIX_XYZ_TO_BT470_525, + RGB_COLOURSPACE_BT470_525, +) from colour.models.rgb.datasets.smpte_c import ( - PRIMARIES_SMPTE_C, WHITEPOINT_NAME_SMPTE_C, CCS_WHITEPOINT_SMPTE_C, - MATRIX_SMPTE_C_TO_XYZ, MATRIX_XYZ_TO_SMPTE_C, RGB_COLOURSPACE_SMPTE_C) + PRIMARIES_SMPTE_C, + WHITEPOINT_NAME_SMPTE_C, + CCS_WHITEPOINT_SMPTE_C, + MATRIX_SMPTE_C_TO_XYZ, + MATRIX_XYZ_TO_SMPTE_C, + RGB_COLOURSPACE_SMPTE_C, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_NTSC1953', 'WHITEPOINT_NAME_NTSC1953', - 'CCS_WHITEPOINT_NTSC1953', 'MATRIX_NTSC1953_TO_XYZ', - 'MATRIX_XYZ_TO_NTSC1953', 'RGB_COLOURSPACE_NTSC1953', 'PRIMARIES_NTSC1987', - 'WHITEPOINT_NAME_NTSC1987', 'CCS_WHITEPOINT_NTSC1987', - 'MATRIX_NTSC1987_TO_XYZ', 'MATRIX_XYZ_TO_NTSC1987', - 'RGB_COLOURSPACE_NTSC1987' + "PRIMARIES_NTSC1953", + "WHITEPOINT_NAME_NTSC1953", + "CCS_WHITEPOINT_NTSC1953", + "MATRIX_NTSC1953_TO_XYZ", + "MATRIX_XYZ_TO_NTSC1953", + "RGB_COLOURSPACE_NTSC1953", + "PRIMARIES_NTSC1987", + "WHITEPOINT_NAME_NTSC1987", + "CCS_WHITEPOINT_NTSC1987", + "MATRIX_NTSC1987_TO_XYZ", + "MATRIX_XYZ_TO_NTSC1987", + "RGB_COLOURSPACE_NTSC1987", ] -PRIMARIES_NTSC1953 = PRIMARIES_BT470_525 -""" -*NTSC (1953)* colourspace primaries. +PRIMARIES_NTSC1953: NDArray = PRIMARIES_BT470_525 +"""*NTSC (1953)* colourspace primaries.""" -PRIMARIES_NTSC1953 : ndarray, (3, 2) -""" +WHITEPOINT_NAME_NTSC1953: str = WHITEPOINT_NAME_BT470_525 +"""*NTSC (1953)* colourspace whitepoint name.""" -WHITEPOINT_NAME_NTSC1953 = WHITEPOINT_NAME_BT470_525 -""" -*NTSC (1953)* colourspace whitepoint name. +CCS_WHITEPOINT_NTSC1953: NDArray = CCS_WHITEPOINT_BT470_525 +"""*NTSC (1953)* colourspace whitepoint chromaticity coordinates.""" -WHITEPOINT_NAME_NTSC1953 : unicode -""" - -CCS_WHITEPOINT_NTSC1953 = CCS_WHITEPOINT_BT470_525 -""" -*NTSC (1953)* colourspace whitepoint chromaticity coordinates. +MATRIX_NTSC1953_TO_XYZ: NDArray = MATRIX_BT470_525_TO_XYZ +"""*NTSC (1953)* colourspace to *CIE XYZ* tristimulus values matrix.""" -CCS_WHITEPOINT_NTSC1953 : ndarray -""" - -MATRIX_NTSC1953_TO_XYZ = MATRIX_BT470_525_TO_XYZ -""" -*NTSC (1953)* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_NTSC1953_TO_XYZ : array_like, (3, 3) -""" - -MATRIX_XYZ_TO_NTSC1953 = MATRIX_XYZ_TO_BT470_525 -""" -*CIE XYZ* tristimulus values to *NTSC (1953)* colourspace matrix. - -MATRIX_XYZ_TO_NTSC1953 : array_like, (3, 3) -""" +MATRIX_XYZ_TO_NTSC1953: NDArray = MATRIX_XYZ_TO_BT470_525 +"""*CIE XYZ* tristimulus values to *NTSC (1953)* colourspace matrix.""" -RGB_COLOURSPACE_NTSC1953 = RGB_Colourspace( - 'NTSC (1953)', +RGB_COLOURSPACE_NTSC1953: RGB_Colourspace = RGB_Colourspace( + "NTSC (1953)", PRIMARIES_NTSC1953, CCS_WHITEPOINT_NTSC1953, WHITEPOINT_NAME_NTSC1953, @@ -99,47 +94,25 @@ References ---------- :cite:`InternationalTelecommunicationUnion1998a` - -RGB_COLOURSPACE_NTSC1953 : RGB_Colourspace """ -PRIMARIES_NTSC1987 = PRIMARIES_SMPTE_C -""" -*NTSC (1987)* colourspace primaries. +PRIMARIES_NTSC1987: NDArray = PRIMARIES_SMPTE_C +"""*NTSC (1987)* colourspace primaries.""" -PRIMARIES_NTSC1987 : ndarray, (3, 2) -""" +WHITEPOINT_NAME_NTSC1987: str = WHITEPOINT_NAME_SMPTE_C +"""*NTSC (1987)* colourspace whitepoint name.""" -WHITEPOINT_NAME_NTSC1987 = WHITEPOINT_NAME_SMPTE_C -""" -*NTSC (1987)* colourspace whitepoint name. +CCS_WHITEPOINT_NTSC1987: NDArray = CCS_WHITEPOINT_SMPTE_C +"""*NTSC (1987)* colourspace whitepoint chromaticity coordinates.""" -WHITEPOINT_NAME_NTSC1987 : unicode -""" +MATRIX_NTSC1987_TO_XYZ: NDArray = MATRIX_SMPTE_C_TO_XYZ +"""*NTSC (1987)* colourspace to *CIE XYZ* tristimulus values matrix.""" -CCS_WHITEPOINT_NTSC1987 = CCS_WHITEPOINT_SMPTE_C -""" -*NTSC (1987)* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_NTSC1987 : ndarray -""" +MATRIX_XYZ_TO_NTSC1987: NDArray = MATRIX_XYZ_TO_SMPTE_C +"""*CIE XYZ* tristimulus values to *NTSC (1987)* colourspace matrix.""" -MATRIX_NTSC1987_TO_XYZ = MATRIX_SMPTE_C_TO_XYZ -""" -*NTSC (1987)* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_NTSC1987_TO_XYZ : array_like, (3, 3) -""" - -MATRIX_XYZ_TO_NTSC1987 = MATRIX_XYZ_TO_SMPTE_C -""" -*CIE XYZ* tristimulus values to *NTSC (1987)* colourspace matrix. - -MATRIX_XYZ_TO_NTSC1987 : array_like, (3, 3) -""" - -RGB_COLOURSPACE_NTSC1987 = RGB_Colourspace( - 'NTSC (1987)', +RGB_COLOURSPACE_NTSC1987: RGB_Colourspace = RGB_Colourspace( + "NTSC (1987)", PRIMARIES_NTSC1987, CCS_WHITEPOINT_NTSC1987, WHITEPOINT_NAME_NTSC1987, @@ -154,6 +127,4 @@ References ---------- :cite:`SocietyofMotionPictureandTelevisionEngineers2004a` - -RGB_COLOURSPACE_NTSC1987 : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/p3_d65.py b/colour/models/rgb/datasets/p3_d65.py index 497a222c5a..60dc97e02d 100644 --- a/colour/models/rgb/datasets/p3_d65.py +++ b/colour/models/rgb/datasets/p3_d65.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ P3-D65 Colourspace ================== @@ -8,70 +7,62 @@ - :attr:`colour.models.RGB_COLOURSPACE_P3_D65`. """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from functools import partial from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, gamma_function, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + gamma_function, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_P3_D65', 'WHITEPOINT_NAME_P3_D65', 'CCS_WHITEPOINT_P3_D65', - 'MATRIX_P3_D65_TO_XYZ', 'MATRIX_XYZ_TO_P3_D65', 'RGB_COLOURSPACE_P3_D65' + "PRIMARIES_P3_D65", + "WHITEPOINT_NAME_P3_D65", + "CCS_WHITEPOINT_P3_D65", + "MATRIX_P3_D65_TO_XYZ", + "MATRIX_XYZ_TO_P3_D65", + "RGB_COLOURSPACE_P3_D65", ] -PRIMARIES_P3_D65 = np.array([ - [0.6800, 0.3200], - [0.2650, 0.6900], - [0.1500, 0.0600], -]) -""" -*P3-D65* colourspace primaries. - -PRIMARIES_P3_D65 : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_P3_D65 = 'D65' -""" -*P3-D65* colourspace whitepoint name. - -WHITEPOINT_NAME_P3_D65 : unicode -""" - -CCS_WHITEPOINT_P3_D65 = (CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'] - [WHITEPOINT_NAME_P3_D65]) -""" -*P3-D65* colourspace whitepoint chromaticity coordinates. +PRIMARIES_P3_D65: NDArray = np.array( + [ + [0.6800, 0.3200], + [0.2650, 0.6900], + [0.1500, 0.0600], + ] +) +"""*P3-D65* colourspace primaries.""" -CCS_WHITEPOINT_P3_D65 : ndarray -""" +WHITEPOINT_NAME_P3_D65: str = "D65" +"""*P3-D65* colourspace whitepoint name.""" -MATRIX_P3_D65_TO_XYZ = normalised_primary_matrix(PRIMARIES_P3_D65, - CCS_WHITEPOINT_P3_D65) -""" -*P3-D65* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_P3_D65_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_P3_D65: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_P3_D65] +"""*P3-D65* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_P3_D65 = np.linalg.inv(MATRIX_P3_D65_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *P3-D65* colourspace matrix. +MATRIX_P3_D65_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_P3_D65, CCS_WHITEPOINT_P3_D65 +) +"""*P3-D65* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_P3_D65 : array_like, (3, 3) -""" +MATRIX_XYZ_TO_P3_D65: NDArray = np.linalg.inv(MATRIX_P3_D65_TO_XYZ) +"""*CIE XYZ* tristimulus values to *P3-D65* colourspace matrix.""" -RGB_COLOURSPACE_P3_D65 = RGB_Colourspace( - 'P3-D65', +RGB_COLOURSPACE_P3_D65: RGB_Colourspace = RGB_Colourspace( + "P3-D65", PRIMARIES_P3_D65, CCS_WHITEPOINT_P3_D65, WHITEPOINT_NAME_P3_D65, @@ -82,6 +73,4 @@ ) RGB_COLOURSPACE_P3_D65.__doc__ = """ *P3-D65* colourspace. - -RGB_COLOURSPACE_P3_D65 : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/pal_secam.py b/colour/models/rgb/datasets/pal_secam.py index 9959dad573..d0d74f91ea 100644 --- a/colour/models/rgb/datasets/pal_secam.py +++ b/colour/models/rgb/datasets/pal_secam.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Pal/Secam Colourspace ===================== @@ -16,64 +15,52 @@ R-REC-BT.470-6-199811-S!!PDF-E.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations +from colour.hints import NDArray from colour.models.rgb import RGB_Colourspace from colour.models.rgb.datasets.itur_bt_470 import ( - PRIMARIES_BT470_625, CCS_WHITEPOINT_BT470_625, WHITEPOINT_NAME_BT470_625, - MATRIX_BT470_625_TO_XYZ, MATRIX_XYZ_TO_BT470_625, - RGB_COLOURSPACE_BT470_625) + PRIMARIES_BT470_625, + CCS_WHITEPOINT_BT470_625, + WHITEPOINT_NAME_BT470_625, + MATRIX_BT470_625_TO_XYZ, + MATRIX_XYZ_TO_BT470_625, + RGB_COLOURSPACE_BT470_625, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_PAL_SECAM', 'WHITEPOINT_NAME_PAL_SECAM', - 'CCS_WHITEPOINT_PAL_SECAM', 'MATRIX_PAL_SECAM_TO_XYZ', - 'MATRIX_XYZ_TO_PAL_SECAM', 'RGB_COLOURSPACE_PAL_SECAM' + "PRIMARIES_PAL_SECAM", + "WHITEPOINT_NAME_PAL_SECAM", + "CCS_WHITEPOINT_PAL_SECAM", + "MATRIX_PAL_SECAM_TO_XYZ", + "MATRIX_XYZ_TO_PAL_SECAM", + "RGB_COLOURSPACE_PAL_SECAM", ] -PRIMARIES_PAL_SECAM = PRIMARIES_BT470_625 -""" -*Pal/Secam* colourspace primaries. +PRIMARIES_PAL_SECAM: NDArray = PRIMARIES_BT470_625 +"""*Pal/Secam* colourspace primaries.""" -PRIMARIES_PAL_SECAM : ndarray, (3, 2) -""" +WHITEPOINT_NAME_PAL_SECAM: str = WHITEPOINT_NAME_BT470_625 +"""*Pal/Secam* colourspace whitepoint name.""" -WHITEPOINT_NAME_PAL_SECAM = WHITEPOINT_NAME_BT470_625 -""" -*Pal/Secam* colourspace whitepoint name. +CCS_WHITEPOINT_PAL_SECAM: NDArray = CCS_WHITEPOINT_BT470_625 +"""*Pal/Secam* colourspace whitepoint chromaticity coordinates.""" -WHITEPOINT_NAME_PAL_SECAM : unicode -""" +MATRIX_PAL_SECAM_TO_XYZ: NDArray = MATRIX_BT470_625_TO_XYZ +"""*Pal/Secam* colourspace to *CIE XYZ* tristimulus values matrix.""" -CCS_WHITEPOINT_PAL_SECAM = CCS_WHITEPOINT_BT470_625 -""" -*Pal/Secam* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_PAL_SECAM : ndarray -""" +MATRIX_XYZ_TO_PAL_SECAM: NDArray = MATRIX_XYZ_TO_BT470_625 +"""*CIE XYZ* tristimulus values to *Pal/Secam* colourspace matrix.""" -MATRIX_PAL_SECAM_TO_XYZ = MATRIX_BT470_625_TO_XYZ -""" -*Pal/Secam* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_PAL_SECAM_TO_XYZ : array_like, (3, 3) -""" - -MATRIX_XYZ_TO_PAL_SECAM = MATRIX_XYZ_TO_BT470_625 -""" -*CIE XYZ* tristimulus values to *Pal/Secam* colourspace matrix. - -MATRIX_XYZ_TO_PAL_SECAM : array_like, (3, 3) -""" - -RGB_COLOURSPACE_PAL_SECAM = RGB_Colourspace( - 'Pal/Secam', +RGB_COLOURSPACE_PAL_SECAM: RGB_Colourspace = RGB_Colourspace( + "Pal/Secam", PRIMARIES_PAL_SECAM, CCS_WHITEPOINT_PAL_SECAM, WHITEPOINT_NAME_PAL_SECAM, @@ -88,6 +75,4 @@ References ---------- :cite:`InternationalTelecommunicationUnion1998a` - -RGB_COLOURSPACE_PAL_SECAM : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/panasonic_v_gamut.py b/colour/models/rgb/datasets/panasonic_v_gamut.py index ba7aa4cbc3..5881b0a0da 100644 --- a/colour/models/rgb/datasets/panasonic_v_gamut.py +++ b/colour/models/rgb/datasets/panasonic_v_gamut.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Panasonic V-Gamut Colourspace ============================= @@ -14,76 +13,71 @@ http://pro-av.panasonic.net/en/varicam/common/pdf/VARICAM_V-Log_V-Gamut.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, log_encoding_VLog, - log_decoding_VLog) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + log_encoding_VLog, + log_decoding_VLog, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_V_GAMUT', 'WHITEPOINT_NAME_V_GAMUT', 'CCS_WHITEPOINT_V_GAMUT', - 'MATRIX_V_GAMUT_TO_XYZ', 'MATRIX_XYZ_TO_V_GAMUT', 'RGB_COLOURSPACE_V_GAMUT' + "PRIMARIES_V_GAMUT", + "WHITEPOINT_NAME_V_GAMUT", + "CCS_WHITEPOINT_V_GAMUT", + "MATRIX_V_GAMUT_TO_XYZ", + "MATRIX_XYZ_TO_V_GAMUT", + "RGB_COLOURSPACE_V_GAMUT", ] -PRIMARIES_V_GAMUT = np.array([ - [0.7300, 0.2800], - [0.1650, 0.8400], - [0.1000, -0.0300], -]) -""" -*Panasonic V-Gamut* colourspace primaries. - -PRIMARIES_V_GAMUT : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_V_GAMUT = 'D65' -""" -*Panasonic V-Gamut* colourspace whitepoint name. - -CCS_WHITEPOINT_V_GAMUT : unicode -""" - -CCS_WHITEPOINT_V_GAMUT = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_V_GAMUT]) -""" -*Panasonic V-Gamut* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_V_GAMUT : ndarray -""" - -MATRIX_V_GAMUT_TO_XYZ = np.array([ - [0.679644, 0.152211, 0.118600], - [0.260686, 0.774894, -0.035580], - [-0.009310, -0.004612, 1.102980], -]) -""" -*Panasonic V-Gamut* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_V_GAMUT_TO_XYZ : array_like, (3, 3) -""" - -MATRIX_XYZ_TO_V_GAMUT = np.array([ - [1.589012, -0.313204, -0.180965], - [-0.534053, 1.396011, 0.102458], - [0.011179, 0.003194, 0.905535], -]) -""" -*CIE XYZ* tristimulus values to *Panasonic V-Gamut* colourspace matrix. - -MATRIX_XYZ_TO_V_GAMUT : array_like, (3, 3) -""" +PRIMARIES_V_GAMUT: NDArray = np.array( + [ + [0.7300, 0.2800], + [0.1650, 0.8400], + [0.1000, -0.0300], + ] +) +"""*Panasonic V-Gamut* colourspace primaries.""" + +WHITEPOINT_NAME_V_GAMUT: str = "D65" +"""*Panasonic V-Gamut* colourspace whitepoint name.""" + +CCS_WHITEPOINT_V_GAMUT: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_V_GAMUT] +"""*Panasonic V-Gamut* colourspace whitepoint chromaticity coordinates.""" + +MATRIX_V_GAMUT_TO_XYZ: NDArray = np.array( + [ + [0.679644, 0.152211, 0.118600], + [0.260686, 0.774894, -0.035580], + [-0.009310, -0.004612, 1.102980], + ] +) +"""*Panasonic V-Gamut* colourspace to *CIE XYZ* tristimulus values matrix.""" + +MATRIX_XYZ_TO_V_GAMUT: NDArray = np.array( + [ + [1.589012, -0.313204, -0.180965], + [-0.534053, 1.396011, 0.102458], + [0.011179, 0.003194, 0.905535], + ] +) +"""*CIE XYZ* tristimulus values to *Panasonic V-Gamut* colourspace matrix.""" -RGB_COLOURSPACE_V_GAMUT = RGB_Colourspace( - 'V-Gamut', +RGB_COLOURSPACE_V_GAMUT: RGB_Colourspace = RGB_Colourspace( + "V-Gamut", PRIMARIES_V_GAMUT, CCS_WHITEPOINT_V_GAMUT, WHITEPOINT_NAME_V_GAMUT, @@ -98,6 +92,4 @@ References ---------- :cite:`Panasonic2014a` - -RGB_COLOURSPACE_V_GAMUT : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/red.py b/colour/models/rgb/datasets/red.py index 52102a78e0..3396759331 100644 --- a/colour/models/rgb/datasets/red.py +++ b/colour/models/rgb/datasets/red.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ RED Colourspaces ================ @@ -26,89 +25,100 @@ nuke-default/make.py """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS +from colour.hints import NDArray from colour.models.rgb import ( - RGB_Colourspace, normalised_primary_matrix, log_encoding_REDLogFilm, - log_decoding_REDLogFilm, log_encoding_Log3G10, log_decoding_Log3G10) + RGB_Colourspace, + normalised_primary_matrix, + log_encoding_REDLogFilm, + log_decoding_REDLogFilm, + log_encoding_Log3G10, + log_decoding_Log3G10, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_RED_COLOR', 'WHITEPOINT_NAME_RED_COLOR', - 'CCS_WHITEPOINT_RED_COLOR', 'MATRIX_RED_COLOR_TO_XYZ', - 'MATRIX_XYZ_TO_RED_COLOR', 'RGB_COLOURSPACE_RED_COLOR', - 'PRIMARIES_RED_COLOR_2', 'WHITEPOINT_NAME_RED_COLOR_2', - 'CCS_WHITEPOINT_RED_COLOR_2', 'MATRIX_RED_COLOR_2_TO_XYZ', - 'MATRIX_XYZ_TO_RED_COLOR_2', 'RGB_COLOURSPACE_RED_COLOR_2', - 'PRIMARIES_RED_COLOR_3', 'WHITEPOINT_NAME_RED_COLOR_3', - 'CCS_WHITEPOINT_RED_COLOR_3', 'MATRIX_RED_COLOR_3_TO_XYZ', - 'MATRIX_XYZ_TO_RED_COLOR_3', 'RGB_COLOURSPACE_RED_COLOR_3', - 'PRIMARIES_RED_COLOR_4', 'WHITEPOINT_NAME_RED_COLOR_4', - 'CCS_WHITEPOINT_RED_COLOR_4', 'MATRIX_RED_COLOR_4_TO_XYZ', - 'MATRIX_XYZ_TO_RED_COLOR_4', 'RGB_COLOURSPACE_RED_COLOR_4', - 'PRIMARIES_DRAGON_COLOR', 'WHITEPOINT_NAME_DRAGON_COLOR', - 'CCS_WHITEPOINT_DRAGON_COLOR', 'MATRIX_DRAGON_COLOR_TO_XYZ', - 'MATRIX_XYZ_TO_DRAGON_COLOR', 'RGB_COLOURSPACE_DRAGON_COLOR', - 'PRIMARIES_DRAGON_COLOR_2', 'WHITEPOINT_NAME_DRAGON_COLOR_2', - 'CCS_WHITEPOINT_DRAGON_COLOR_2', 'MATRIX_DRAGON_COLOR_2_TO_XYZ', - 'MATRIX_XYZ_TO_DRAGON_COLOR_2', 'RGB_COLOURSPACE_DRAGON_COLOR_2', - 'PRIMARIES_RED_WIDE_GAMUT_RGB', 'WHITEPOINT_NAME_RED_WIDE_GAMUT_RGB', - 'CCS_WHITEPOINT_RED_WIDE_GAMUT_RGB', 'MATRIX_RED_WIDE_GAMUT_RGB_TO_XYZ', - 'MATRIX_XYZ_TO_RED_WIDE_GAMUT_RGB', 'RGB_COLOURSPACE_RED_WIDE_GAMUT_RGB' + "PRIMARIES_RED_COLOR", + "WHITEPOINT_NAME_RED_COLOR", + "CCS_WHITEPOINT_RED_COLOR", + "MATRIX_RED_COLOR_TO_XYZ", + "MATRIX_XYZ_TO_RED_COLOR", + "RGB_COLOURSPACE_RED_COLOR", + "PRIMARIES_RED_COLOR_2", + "WHITEPOINT_NAME_RED_COLOR_2", + "CCS_WHITEPOINT_RED_COLOR_2", + "MATRIX_RED_COLOR_2_TO_XYZ", + "MATRIX_XYZ_TO_RED_COLOR_2", + "RGB_COLOURSPACE_RED_COLOR_2", + "PRIMARIES_RED_COLOR_3", + "WHITEPOINT_NAME_RED_COLOR_3", + "CCS_WHITEPOINT_RED_COLOR_3", + "MATRIX_RED_COLOR_3_TO_XYZ", + "MATRIX_XYZ_TO_RED_COLOR_3", + "RGB_COLOURSPACE_RED_COLOR_3", + "PRIMARIES_RED_COLOR_4", + "WHITEPOINT_NAME_RED_COLOR_4", + "CCS_WHITEPOINT_RED_COLOR_4", + "MATRIX_RED_COLOR_4_TO_XYZ", + "MATRIX_XYZ_TO_RED_COLOR_4", + "RGB_COLOURSPACE_RED_COLOR_4", + "PRIMARIES_DRAGON_COLOR", + "WHITEPOINT_NAME_DRAGON_COLOR", + "CCS_WHITEPOINT_DRAGON_COLOR", + "MATRIX_DRAGON_COLOR_TO_XYZ", + "MATRIX_XYZ_TO_DRAGON_COLOR", + "RGB_COLOURSPACE_DRAGON_COLOR", + "PRIMARIES_DRAGON_COLOR_2", + "WHITEPOINT_NAME_DRAGON_COLOR_2", + "CCS_WHITEPOINT_DRAGON_COLOR_2", + "MATRIX_DRAGON_COLOR_2_TO_XYZ", + "MATRIX_XYZ_TO_DRAGON_COLOR_2", + "RGB_COLOURSPACE_DRAGON_COLOR_2", + "PRIMARIES_RED_WIDE_GAMUT_RGB", + "WHITEPOINT_NAME_RED_WIDE_GAMUT_RGB", + "CCS_WHITEPOINT_RED_WIDE_GAMUT_RGB", + "MATRIX_RED_WIDE_GAMUT_RGB_TO_XYZ", + "MATRIX_XYZ_TO_RED_WIDE_GAMUT_RGB", + "RGB_COLOURSPACE_RED_WIDE_GAMUT_RGB", ] -PRIMARIES_RED_COLOR = np.array([ - [0.701058563171395, 0.330180975940326], - [0.298811317306316, 0.625169245953133], - [0.135038675201355, 0.035261776551191], -]) -""" -*REDcolor* colourspace primaries. - -PRIMARIES_RED_COLOR : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_RED_COLOR = 'D65' -""" -*REDcolor* colourspace whitepoint name. - -WHITEPOINT_NAME_RED_COLOR : unicode -""" - -CCS_WHITEPOINT_RED_COLOR = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_RED_COLOR]) -""" -*REDcolor* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_RED_COLOR : ndarray -""" +PRIMARIES_RED_COLOR: NDArray = np.array( + [ + [0.701058563171395, 0.330180975940326], + [0.298811317306316, 0.625169245953133], + [0.135038675201355, 0.035261776551191], + ] +) +"""*REDcolor* colourspace primaries.""" -MATRIX_RED_COLOR_TO_XYZ = normalised_primary_matrix(PRIMARIES_RED_COLOR, - CCS_WHITEPOINT_RED_COLOR) -""" -*REDcolor* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_RED_COLOR: str = "D65" +"""*REDcolor* colourspace whitepoint name.""" -MATRIX_RED_COLOR_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_RED_COLOR: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_RED_COLOR] +"""*REDcolor* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_RED_COLOR = np.linalg.inv(MATRIX_RED_COLOR_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *REDcolor* colourspace matrix. +MATRIX_RED_COLOR_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_RED_COLOR, CCS_WHITEPOINT_RED_COLOR +) +"""*REDcolor* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_RED_COLOR : array_like, (3, 3) -""" +MATRIX_XYZ_TO_RED_COLOR: NDArray = np.linalg.inv(MATRIX_RED_COLOR_TO_XYZ) +"""*CIE XYZ* tristimulus values to *REDcolor* colourspace matrix.""" -RGB_COLOURSPACE_RED_COLOR = RGB_Colourspace( - 'REDcolor', +RGB_COLOURSPACE_RED_COLOR: RGB_Colourspace = RGB_Colourspace( + "REDcolor", PRIMARIES_RED_COLOR, CCS_WHITEPOINT_RED_COLOR, WHITEPOINT_NAME_RED_COLOR, @@ -123,52 +133,33 @@ References ---------- :cite:`Mansencal2015d`, :cite:`SonyImageworks2012a` - -RGB_COLOURSPACE_RED_COLOR : RGB_Colourspace -""" - -PRIMARIES_RED_COLOR_2 = np.array([ - [0.897407221929776, 0.330776225980398], - [0.296022094516625, 0.684635550900945], - [0.099799512883393, -0.023000513177992], -]) """ -*REDcolor2* colourspace primaries. -PRIMARIES_RED_COLOR_2 : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_RED_COLOR_2 = WHITEPOINT_NAME_RED_COLOR -""" -*REDcolor2* colourspace whitepoint name. - -WHITEPOINT_NAME_RED_COLOR_2 : unicode -""" - -CCS_WHITEPOINT_RED_COLOR_2 = CCS_WHITEPOINT_RED_COLOR -""" -*REDcolor2* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_RED_COLOR_2 : ndarray -""" +PRIMARIES_RED_COLOR_2: NDArray = np.array( + [ + [0.897407221929776, 0.330776225980398], + [0.296022094516625, 0.684635550900945], + [0.099799512883393, -0.023000513177992], + ] +) +"""*REDcolor2* colourspace primaries.""" -MATRIX_RED_COLOR_2_TO_XYZ = normalised_primary_matrix( - PRIMARIES_RED_COLOR_2, CCS_WHITEPOINT_RED_COLOR_2) -""" -*REDcolor2* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_RED_COLOR_2: str = WHITEPOINT_NAME_RED_COLOR +"""*REDcolor2* colourspace whitepoint name.""" -MATRIX_RED_COLOR_2_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_RED_COLOR_2: NDArray = CCS_WHITEPOINT_RED_COLOR +"""*REDcolor2* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_RED_COLOR_2 = np.linalg.inv(MATRIX_RED_COLOR_2_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *REDcolor2* colourspace matrix. +MATRIX_RED_COLOR_2_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_RED_COLOR_2, CCS_WHITEPOINT_RED_COLOR_2 +) +"""*REDcolor2* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_RED_COLOR_2 : array_like, (3, 3) -""" +MATRIX_XYZ_TO_RED_COLOR_2: NDArray = np.linalg.inv(MATRIX_RED_COLOR_2_TO_XYZ) +"""*CIE XYZ* tristimulus values to *REDcolor2* colourspace matrix.""" -RGB_COLOURSPACE_RED_COLOR_2 = RGB_Colourspace( - 'REDcolor2', +RGB_COLOURSPACE_RED_COLOR_2: RGB_Colourspace = RGB_Colourspace( + "REDcolor2", PRIMARIES_RED_COLOR_2, CCS_WHITEPOINT_RED_COLOR_2, WHITEPOINT_NAME_RED_COLOR_2, @@ -183,52 +174,33 @@ References ---------- :cite:`Mansencal2015d`, :cite:`SonyImageworks2012a` - -RGB_COLOURSPACE_RED_COLOR_2 : RGB_Colourspace -""" - -PRIMARIES_RED_COLOR_3 = np.array([ - [0.702598658589917, 0.330185588938484], - [0.295782235737268, 0.689748258397534], - [0.111090529079787, -0.004332320984771], -]) -""" -*REDcolor3* colourspace primaries. - -PRIMARIES_RED_COLOR_3 : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_RED_COLOR_3 = WHITEPOINT_NAME_RED_COLOR -""" -*REDcolor3* colourspace whitepoint name. - -WHITEPOINT_NAME_RED_COLOR_3 : unicode -""" - -CCS_WHITEPOINT_RED_COLOR_3 = CCS_WHITEPOINT_RED_COLOR """ -*REDcolor3* colourspace whitepoint chromaticity coordinates. -CCS_WHITEPOINT_RED_COLOR_3 : ndarray -""" +PRIMARIES_RED_COLOR_3: NDArray = np.array( + [ + [0.702598658589917, 0.330185588938484], + [0.295782235737268, 0.689748258397534], + [0.111090529079787, -0.004332320984771], + ] +) +"""*REDcolor3* colourspace primaries.""" -MATRIX_RED_COLOR_3_TO_XYZ = normalised_primary_matrix( - PRIMARIES_RED_COLOR_3, CCS_WHITEPOINT_RED_COLOR_3) -""" -*REDcolor3* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_RED_COLOR_3: str = WHITEPOINT_NAME_RED_COLOR +"""*REDcolor3* colourspace whitepoint name.""" -MATRIX_RED_COLOR_3_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_RED_COLOR_3: NDArray = CCS_WHITEPOINT_RED_COLOR +"""*REDcolor3* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_RED_COLOR_3 = np.linalg.inv(MATRIX_RED_COLOR_3_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *REDcolor3* colourspace matrix. +MATRIX_RED_COLOR_3_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_RED_COLOR_3, CCS_WHITEPOINT_RED_COLOR_3 +) +"""*REDcolor3* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_RED_COLOR_3 : array_like, (3, 3) -""" +MATRIX_XYZ_TO_RED_COLOR_3: NDArray = np.linalg.inv(MATRIX_RED_COLOR_3_TO_XYZ) +"""*CIE XYZ* tristimulus values to *REDcolor3* colourspace matrix.""" -RGB_COLOURSPACE_RED_COLOR_3 = RGB_Colourspace( - 'REDcolor3', +RGB_COLOURSPACE_RED_COLOR_3: RGB_Colourspace = RGB_Colourspace( + "REDcolor3", PRIMARIES_RED_COLOR_3, CCS_WHITEPOINT_RED_COLOR_3, WHITEPOINT_NAME_RED_COLOR_3, @@ -243,52 +215,33 @@ References ---------- :cite:`Mansencal2015d`, :cite:`SonyImageworks2012a` - -RGB_COLOURSPACE_RED_COLOR_3 : RGB_Colourspace -""" - -PRIMARIES_RED_COLOR_4 = np.array([ - [0.702598154635438, 0.330185096210515], - [0.295782328047083, 0.689748253964859], - [0.144459236489795, 0.050837720977386], -]) -""" -*REDcolor4* colourspace primaries. - -PRIMARIES_RED_COLOR_4 : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_RED_COLOR_4 = WHITEPOINT_NAME_RED_COLOR -""" -*REDcolor4* colourspace whitepoint name. - -WHITEPOINT_NAME_RED_COLOR_4 : unicode """ -CCS_WHITEPOINT_RED_COLOR_4 = CCS_WHITEPOINT_RED_COLOR -""" -*REDcolor4* colourspace whitepoint chromaticity coordinates. +PRIMARIES_RED_COLOR_4: NDArray = np.array( + [ + [0.702598154635438, 0.330185096210515], + [0.295782328047083, 0.689748253964859], + [0.144459236489795, 0.050837720977386], + ] +) +"""*REDcolor4* colourspace primaries.""" -CCS_WHITEPOINT_RED_COLOR_4 : ndarray -""" +WHITEPOINT_NAME_RED_COLOR_4: str = WHITEPOINT_NAME_RED_COLOR +"""*REDcolor4* colourspace whitepoint name.""" -MATRIX_RED_COLOR_4_TO_XYZ = normalised_primary_matrix( - PRIMARIES_RED_COLOR_4, CCS_WHITEPOINT_RED_COLOR_4) -""" -*REDcolor4* colourspace to *CIE XYZ* tristimulus values matrix. +CCS_WHITEPOINT_RED_COLOR_4: NDArray = CCS_WHITEPOINT_RED_COLOR +"""*REDcolor4* colourspace whitepoint chromaticity coordinates.""" -MATRIX_RED_COLOR_4_TO_XYZ : array_like, (3, 3) -""" - -MATRIX_XYZ_TO_RED_COLOR_4 = np.linalg.inv(MATRIX_RED_COLOR_4_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *REDcolor4* colourspace matrix. +MATRIX_RED_COLOR_4_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_RED_COLOR_4, CCS_WHITEPOINT_RED_COLOR_4 +) +"""*REDcolor4* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_RED_COLOR_4 : array_like, (3, 3) -""" +MATRIX_XYZ_TO_RED_COLOR_4: NDArray = np.linalg.inv(MATRIX_RED_COLOR_4_TO_XYZ) +"""*CIE XYZ* tristimulus values to *REDcolor4* colourspace matrix.""" -RGB_COLOURSPACE_RED_COLOR_4 = RGB_Colourspace( - 'REDcolor4', +RGB_COLOURSPACE_RED_COLOR_4: RGB_Colourspace = RGB_Colourspace( + "REDcolor4", PRIMARIES_RED_COLOR_4, CCS_WHITEPOINT_RED_COLOR_4, WHITEPOINT_NAME_RED_COLOR_4, @@ -303,52 +256,33 @@ References ---------- :cite:`Mansencal2015d`, :cite:`SonyImageworks2012a` - -RGB_COLOURSPACE_RED_COLOR_4 : RGB_Colourspace -""" - -PRIMARIES_DRAGON_COLOR = np.array([ - [0.758655892599321, 0.330355348611293], - [0.294923619810175, 0.708053242065117], - [0.085961601167585, -0.045879436983969], -]) -""" -*DRAGONcolor* colourspace primaries. - -PRIMARIES_DRAGON_COLOR : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_DRAGON_COLOR = WHITEPOINT_NAME_RED_COLOR -""" -*DRAGONcolor* colourspace whitepoint name. - -WHITEPOINT_NAME_DRAGON_COLOR : unicode """ -CCS_WHITEPOINT_DRAGON_COLOR = CCS_WHITEPOINT_RED_COLOR -""" -*DRAGONcolor* colourspace whitepoint chromaticity coordinates. +PRIMARIES_DRAGON_COLOR: NDArray = np.array( + [ + [0.758655892599321, 0.330355348611293], + [0.294923619810175, 0.708053242065117], + [0.085961601167585, -0.045879436983969], + ] +) +"""*DRAGONcolor* colourspace primaries.""" -CCS_WHITEPOINT_DRAGON_COLOR : ndarray -""" +WHITEPOINT_NAME_DRAGON_COLOR: str = WHITEPOINT_NAME_RED_COLOR +"""*DRAGONcolor* colourspace whitepoint name.""" -MATRIX_DRAGON_COLOR_TO_XYZ = normalised_primary_matrix( - PRIMARIES_DRAGON_COLOR, CCS_WHITEPOINT_DRAGON_COLOR) -""" -*DRAGONcolor* colourspace to *CIE XYZ* tristimulus values matrix. +CCS_WHITEPOINT_DRAGON_COLOR: NDArray = CCS_WHITEPOINT_RED_COLOR +"""*DRAGONcolor* colourspace whitepoint chromaticity coordinates.""" -MATRIX_DRAGON_COLOR_TO_XYZ : array_like, (3, 3) -""" +MATRIX_DRAGON_COLOR_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_DRAGON_COLOR, CCS_WHITEPOINT_DRAGON_COLOR +) +"""*DRAGONcolor* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_DRAGON_COLOR = np.linalg.inv(MATRIX_DRAGON_COLOR_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *DRAGONcolor* colourspace matrix. +MATRIX_XYZ_TO_DRAGON_COLOR: NDArray = np.linalg.inv(MATRIX_DRAGON_COLOR_TO_XYZ) +"""*CIE XYZ* tristimulus values to *DRAGONcolor* colourspace matrix.""" -MATRIX_XYZ_TO_DRAGON_COLOR : array_like, (3, 3) -""" - -RGB_COLOURSPACE_DRAGON_COLOR = RGB_Colourspace( - 'DRAGONcolor', +RGB_COLOURSPACE_DRAGON_COLOR: RGB_Colourspace = RGB_Colourspace( + "DRAGONcolor", PRIMARIES_DRAGON_COLOR, CCS_WHITEPOINT_DRAGON_COLOR, WHITEPOINT_NAME_DRAGON_COLOR, @@ -363,52 +297,35 @@ References ---------- :cite:`Mansencal2015d`, :cite:`SonyImageworks2012a` - -RGB_COLOURSPACE_DRAGON_COLOR : RGB_Colourspace -""" - -PRIMARIES_DRAGON_COLOR_2 = np.array([ - [0.758656214177604, 0.330355835762678], - [0.294923887732982, 0.708053363192126], - [0.144168726866337, 0.050357384587121], -]) -""" -*DRAGONcolor2* colourspace primaries. - -PRIMARIES_DRAGON_COLOR_2 : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_DRAGON_COLOR_2 = WHITEPOINT_NAME_RED_COLOR """ -*DRAGONcolor2* colourspace whitepoint name. -WHITEPOINT_NAME_DRAGON_COLOR_2 : unicode -""" - -CCS_WHITEPOINT_DRAGON_COLOR_2 = CCS_WHITEPOINT_RED_COLOR -""" -*DRAGONcolor2* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_DRAGON_COLOR_2 : ndarray -""" +PRIMARIES_DRAGON_COLOR_2: NDArray = np.array( + [ + [0.758656214177604, 0.330355835762678], + [0.294923887732982, 0.708053363192126], + [0.144168726866337, 0.050357384587121], + ] +) +"""*DRAGONcolor2* colourspace primaries.""" -MATRIX_DRAGON_COLOR_2_TO_XYZ = normalised_primary_matrix( - PRIMARIES_DRAGON_COLOR_2, CCS_WHITEPOINT_DRAGON_COLOR_2) -""" -*DRAGONcolor2* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_DRAGON_COLOR_2: str = WHITEPOINT_NAME_RED_COLOR +"""*DRAGONcolor2* colourspace whitepoint name.""" -MATRIX_DRAGON_COLOR_2_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_DRAGON_COLOR_2: NDArray = CCS_WHITEPOINT_RED_COLOR +"""*DRAGONcolor2* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_DRAGON_COLOR_2 = np.linalg.inv(MATRIX_DRAGON_COLOR_2_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *DRAGONcolor2* colourspace matrix. +MATRIX_DRAGON_COLOR_2_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_DRAGON_COLOR_2, CCS_WHITEPOINT_DRAGON_COLOR_2 +) +"""*DRAGONcolor2* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_DRAGON_COLOR_2 : array_like, (3, 3) -""" +MATRIX_XYZ_TO_DRAGON_COLOR_2: NDArray = np.linalg.inv( + MATRIX_DRAGON_COLOR_2_TO_XYZ +) +"""*CIE XYZ* tristimulus values to *DRAGONcolor2* colourspace matrix.""" -RGB_COLOURSPACE_DRAGON_COLOR_2 = RGB_Colourspace( - 'DRAGONcolor2', +RGB_COLOURSPACE_DRAGON_COLOR_2: RGB_Colourspace = RGB_Colourspace( + "DRAGONcolor2", PRIMARIES_DRAGON_COLOR_2, CCS_WHITEPOINT_DRAGON_COLOR_2, WHITEPOINT_NAME_DRAGON_COLOR_2, @@ -423,56 +340,39 @@ References ---------- :cite:`Mansencal2015d`, :cite:`SonyImageworks2012a` - -RGB_COLOURSPACE_DRAGON_COLOR_2 : RGB_Colourspace -""" - -PRIMARIES_RED_WIDE_GAMUT_RGB = np.array([ - [0.780308, 0.304253], - [0.121595, 1.493994], - [0.095612, -0.084589], -]) -""" -*REDWideGamutRGB* colourspace primaries. - -PRIMARIES_RED_WIDE_GAMUT_RGB : ndarray, (3, 2) """ -WHITEPOINT_NAME_RED_WIDE_GAMUT_RGB = WHITEPOINT_NAME_RED_COLOR -""" -*REDWideGamutRGB* colourspace whitepoint name. +PRIMARIES_RED_WIDE_GAMUT_RGB: NDArray = np.array( + [ + [0.780308, 0.304253], + [0.121595, 1.493994], + [0.095612, -0.084589], + ] +) +"""*REDWideGamutRGB* colourspace primaries.""" -WHITEPOINT_NAME_RED_WIDE_GAMUT_RGB : unicode -""" +WHITEPOINT_NAME_RED_WIDE_GAMUT_RGB: str = WHITEPOINT_NAME_RED_COLOR +"""*REDWideGamutRGB* colourspace whitepoint name.""" CCS_WHITEPOINT_RED_WIDE_GAMUT_RGB = CCS_WHITEPOINT_RED_COLOR -""" -*REDWideGamutRGB* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_RED_WIDE_GAMUT_RGB : ndarray -""" - -MATRIX_RED_WIDE_GAMUT_RGB_TO_XYZ = np.array([ - [0.735275, 0.068609, 0.146571], - [0.286694, 0.842979, -0.129673], - [-0.079681, -0.347343, 1.516082], -]) -""" -*REDWideGamutRGB* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_RED_WIDE_GAMUT_RGB_TO_XYZ : array_like, (3, 3) -""" - -MATRIX_XYZ_TO_RED_WIDE_GAMUT_RGB = np.linalg.inv( - MATRIX_RED_WIDE_GAMUT_RGB_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *REDWideGamutRGB* colourspace matrix. +"""*REDWideGamutRGB* colourspace whitepoint chromaticity coordinates.""" + +MATRIX_RED_WIDE_GAMUT_RGB_TO_XYZ: NDArray = np.array( + [ + [0.735275, 0.068609, 0.146571], + [0.286694, 0.842979, -0.129673], + [-0.079681, -0.347343, 1.516082], + ] +) +"""*REDWideGamutRGB* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_RED_WIDE_GAMUT_RGB : array_like, (3, 3) -""" +MATRIX_XYZ_TO_RED_WIDE_GAMUT_RGB: NDArray = np.linalg.inv( + MATRIX_RED_WIDE_GAMUT_RGB_TO_XYZ +) +"""*CIE XYZ* tristimulus values to *REDWideGamutRGB* colourspace matrix.""" -RGB_COLOURSPACE_RED_WIDE_GAMUT_RGB = RGB_Colourspace( - 'REDWideGamutRGB', +RGB_COLOURSPACE_RED_WIDE_GAMUT_RGB: RGB_Colourspace = RGB_Colourspace( + "REDWideGamutRGB", PRIMARIES_RED_WIDE_GAMUT_RGB, CCS_WHITEPOINT_RED_WIDE_GAMUT_RGB, WHITEPOINT_NAME_RED_WIDE_GAMUT_RGB, @@ -487,6 +387,4 @@ References ---------- :cite:`Mansencal2015d`, :cite:`Nattress2016a`, :cite:`SonyImageworks2012a` - -RGB_COLOURSPACE_RED_WIDE_GAMUT_RGB : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/rimm_romm_rgb.py b/colour/models/rgb/datasets/rimm_romm_rgb.py index ad38e8c577..d4b532aaa9 100644 --- a/colour/models/rgb/datasets/rimm_romm_rgb.py +++ b/colour/models/rgb/datasets/rimm_romm_rgb.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ RIMM, ROMM and ERIMM Encodings ============================== @@ -19,89 +18,94 @@ (RIMM/ROMM RGB) (pp. 1-8). http://www.photo-lovers.org/pdf/color/romm.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS +from colour.hints import NDArray from colour.models.rgb import ( - RGB_Colourspace, cctf_encoding_ROMMRGB, cctf_decoding_ROMMRGB, - cctf_encoding_RIMMRGB, cctf_decoding_RIMMRGB, log_encoding_ERIMMRGB, - log_decoding_ERIMMRGB, cctf_encoding_ProPhotoRGB, - cctf_decoding_ProPhotoRGB) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = [ - 'PRIMARIES_ROMM_RGB', 'WHITEPOINT_NAME_ROMM_RGB', - 'CCS_WHITEPOINT_ROMM_RGB', 'MATRIX_ROMM_RGB_TO_XYZ', - 'MATRIX_XYZ_TO_ROMM_RGB', 'RGB_COLOURSPACE_ROMM_RGB', 'PRIMARIES_RIMM_RGB', - 'WHITEPOINT_NAME_RIMM_RGB', 'CCS_WHITEPOINT_RIMM_RGB', - 'MATRIX_RIMM_RGB_TO_XYZ', 'MATRIX_XYZ_TO_RIMM_RGB', - 'RGB_COLOURSPACE_RIMM_RGB', 'PRIMARIES_ERIMM_RGB', - 'WHITEPOINT_NAME_ERIMM_RGB', 'CCS_WHITEPOINT_ERIMM_RGB', - 'MATRIX_ERIMM_RGB_TO_XYZ', 'MATRIX_XYZ_TO_ERIMM_RGB', - 'RGB_COLOURSPACE_ERIMM_RGB', 'PRIMARIES_PROPHOTO_RGB', - 'WHITEPOINT_NAME_PROPHOTO_RGB', 'CCS_WHITEPOINT_PROPHOTO_RGB', - 'MATRIX_PROPHOTO_RGB_TO_XYZ', 'MATRIX_XYZ_TO_PROPHOTO_RGB', - 'RGB_COLOURSPACE_PROPHOTO_RGB' -] - -PRIMARIES_ROMM_RGB = np.array([ - [0.7347, 0.2653], - [0.1596, 0.8404], - [0.0366, 0.0001], -]) -""" -*ROMM RGB* colourspace primaries. - -PRIMARIES_ROMM_RGB : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_ROMM_RGB = 'D50' -""" -*ROMM RGB* colourspace whitepoint name. - -WHITEPOINT_NAME_ROMM_RGB : unicode -""" - -CCS_WHITEPOINT_ROMM_RGB = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_ROMM_RGB]) -""" -*ROMM RGB* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_ROMM_RGB : ndarray -""" + RGB_Colourspace, + cctf_encoding_ROMMRGB, + cctf_decoding_ROMMRGB, + cctf_encoding_RIMMRGB, + cctf_decoding_RIMMRGB, + log_encoding_ERIMMRGB, + log_decoding_ERIMMRGB, +) -MATRIX_ROMM_RGB_TO_XYZ = np.array([ - [0.7977, 0.1352, 0.0313], - [0.2880, 0.7119, 0.0001], - [0.0000, 0.0000, 0.8249], -]) -""" -*ROMM RGB* colourspace to *CIE XYZ* tristimulus values matrix. -MATRIX_ROMM_RGB_TO_XYZ : array_like, (3, 3) -""" +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" -MATRIX_XYZ_TO_ROMM_RGB = np.array([ - [1.3460, -0.2556, -0.0511], - [-0.5446, 1.5082, 0.0205], - [0.0000, 0.0000, 1.2123], -]) -""" -*CIE XYZ* tristimulus values to *ROMM RGB* colourspace matrix. +__all__ = [ + "PRIMARIES_ROMM_RGB", + "WHITEPOINT_NAME_ROMM_RGB", + "CCS_WHITEPOINT_ROMM_RGB", + "MATRIX_ROMM_RGB_TO_XYZ", + "MATRIX_XYZ_TO_ROMM_RGB", + "RGB_COLOURSPACE_ROMM_RGB", + "PRIMARIES_RIMM_RGB", + "WHITEPOINT_NAME_RIMM_RGB", + "CCS_WHITEPOINT_RIMM_RGB", + "MATRIX_RIMM_RGB_TO_XYZ", + "MATRIX_XYZ_TO_RIMM_RGB", + "RGB_COLOURSPACE_RIMM_RGB", + "PRIMARIES_ERIMM_RGB", + "WHITEPOINT_NAME_ERIMM_RGB", + "CCS_WHITEPOINT_ERIMM_RGB", + "MATRIX_ERIMM_RGB_TO_XYZ", + "MATRIX_XYZ_TO_ERIMM_RGB", + "RGB_COLOURSPACE_ERIMM_RGB", + "PRIMARIES_PROPHOTO_RGB", + "WHITEPOINT_NAME_PROPHOTO_RGB", + "CCS_WHITEPOINT_PROPHOTO_RGB", + "MATRIX_PROPHOTO_RGB_TO_XYZ", + "MATRIX_XYZ_TO_PROPHOTO_RGB", + "RGB_COLOURSPACE_PROPHOTO_RGB", +] -MATRIX_XYZ_TO_ROMM_RGB : array_like, (3, 3) -""" +PRIMARIES_ROMM_RGB: NDArray = np.array( + [ + [0.7347, 0.2653], + [0.1596, 0.8404], + [0.0366, 0.0001], + ] +) +"""*ROMM RGB* colourspace primaries.""" + +WHITEPOINT_NAME_ROMM_RGB: str = "D50" +"""*ROMM RGB* colourspace whitepoint name.""" + +CCS_WHITEPOINT_ROMM_RGB: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_ROMM_RGB] +"""*ROMM RGB* colourspace whitepoint chromaticity coordinates.""" + +MATRIX_ROMM_RGB_TO_XYZ: NDArray = np.array( + [ + [0.7977, 0.1352, 0.0313], + [0.2880, 0.7119, 0.0001], + [0.0000, 0.0000, 0.8249], + ] +) +"""*ROMM RGB* colourspace to *CIE XYZ* tristimulus values matrix.""" + +MATRIX_XYZ_TO_ROMM_RGB: NDArray = np.array( + [ + [1.3460, -0.2556, -0.0511], + [-0.5446, 1.5082, 0.0205], + [0.0000, 0.0000, 1.2123], + ] +) +"""*CIE XYZ* tristimulus values to *ROMM RGB* colourspace matrix.""" -RGB_COLOURSPACE_ROMM_RGB = RGB_Colourspace( - 'ROMM RGB', +RGB_COLOURSPACE_ROMM_RGB: RGB_Colourspace = RGB_Colourspace( + "ROMM RGB", PRIMARIES_ROMM_RGB, CCS_WHITEPOINT_ROMM_RGB, WHITEPOINT_NAME_ROMM_RGB, @@ -116,47 +120,25 @@ References ---------- :cite:`ANSI2003a`, :cite:`Spaulding2000b` - -RGB_COLOURSPACE_ROMM_RGB : RGB_Colourspace -""" - -PRIMARIES_RIMM_RGB = PRIMARIES_ROMM_RGB -""" -*RIMM RGB* colourspace primaries. - -PRIMARIES_RIMM_RGB : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_RIMM_RGB = WHITEPOINT_NAME_ROMM_RGB -""" -*RIMM RGB* colourspace whitepoint name. - -WHITEPOINT_NAME_RIMM_RGB : unicode -""" - -CCS_WHITEPOINT_RIMM_RGB = CCS_WHITEPOINT_ROMM_RGB """ -*RIMM RGB* colourspace whitepoint chromaticity coordinates. -CCS_WHITEPOINT_RIMM_RGB : ndarray -""" +PRIMARIES_RIMM_RGB: NDArray = PRIMARIES_ROMM_RGB +"""*RIMM RGB* colourspace primaries.""" -MATRIX_RIMM_RGB_TO_XYZ = MATRIX_ROMM_RGB_TO_XYZ -""" -*RIMM RGB* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_RIMM_RGB: str = WHITEPOINT_NAME_ROMM_RGB +"""*RIMM RGB* colourspace whitepoint name.""" -MATRIX_RIMM_RGB_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_RIMM_RGB: NDArray = CCS_WHITEPOINT_ROMM_RGB +"""*RIMM RGB* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_RIMM_RGB = MATRIX_XYZ_TO_ROMM_RGB -""" -*CIE XYZ* tristimulus values to *RIMM RGB* colourspace matrix. +MATRIX_RIMM_RGB_TO_XYZ: NDArray = MATRIX_ROMM_RGB_TO_XYZ +"""*RIMM RGB* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_RIMM_RGB : array_like, (3, 3) -""" +MATRIX_XYZ_TO_RIMM_RGB: NDArray = MATRIX_XYZ_TO_ROMM_RGB +"""*CIE XYZ* tristimulus values to *RIMM RGB* colourspace matrix.""" -RGB_COLOURSPACE_RIMM_RGB = RGB_Colourspace( - 'RIMM RGB', +RGB_COLOURSPACE_RIMM_RGB: RGB_Colourspace = RGB_Colourspace( + "RIMM RGB", PRIMARIES_RIMM_RGB, CCS_WHITEPOINT_RIMM_RGB, WHITEPOINT_NAME_RIMM_RGB, @@ -173,47 +155,25 @@ References ---------- :cite:`Spaulding2000b` - -RGB_COLOURSPACE_RIMM_RGB : RGB_Colourspace """ -PRIMARIES_ERIMM_RGB = PRIMARIES_ROMM_RGB -""" -*ERIMM RGB* colourspace primaries. +PRIMARIES_ERIMM_RGB: NDArray = PRIMARIES_ROMM_RGB +"""*ERIMM RGB* colourspace primaries.""" -PRIMARIES_ERIMM_RGB : ndarray, (3, 2) -""" +WHITEPOINT_NAME_ERIMM_RGB: str = WHITEPOINT_NAME_ROMM_RGB +"""*ERIMM RGB* colourspace whitepoint name.""" -WHITEPOINT_NAME_ERIMM_RGB = WHITEPOINT_NAME_ROMM_RGB -""" -*ERIMM RGB* colourspace whitepoint name. +CCS_WHITEPOINT_ERIMM_RGB: NDArray = CCS_WHITEPOINT_ROMM_RGB +"""*ERIMM RGB* colourspace whitepoint chromaticity coordinates.""" -WHITEPOINT_NAME_ERIMM_RGB : unicode -""" +MATRIX_ERIMM_RGB_TO_XYZ: NDArray = MATRIX_ROMM_RGB_TO_XYZ +"""*ERIMM RGB* colourspace to *CIE XYZ* tristimulus values matrix.""" -CCS_WHITEPOINT_ERIMM_RGB = CCS_WHITEPOINT_ROMM_RGB -""" -*ERIMM RGB* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_ERIMM_RGB : ndarray -""" - -MATRIX_ERIMM_RGB_TO_XYZ = MATRIX_ROMM_RGB_TO_XYZ -""" -*ERIMM RGB* colourspace to *CIE XYZ* tristimulus values matrix. +MATRIX_XYZ_TO_ERIMM_RGB: NDArray = MATRIX_XYZ_TO_ROMM_RGB +"""*CIE XYZ* tristimulus values to *ERIMM RGB* colourspace matrix.""" -MATRIX_ERIMM_RGB_TO_XYZ : array_like, (3, 3) -""" - -MATRIX_XYZ_TO_ERIMM_RGB = MATRIX_XYZ_TO_ROMM_RGB -""" -*CIE XYZ* tristimulus values to *ERIMM RGB* colourspace matrix. - -MATRIX_XYZ_TO_ERIMM_RGB : array_like, (3, 3) -""" - -RGB_COLOURSPACE_ERIMM_RGB = RGB_Colourspace( - 'ERIMM RGB', +RGB_COLOURSPACE_ERIMM_RGB: RGB_Colourspace = RGB_Colourspace( + "ERIMM RGB", PRIMARIES_ERIMM_RGB, CCS_WHITEPOINT_ERIMM_RGB, WHITEPOINT_NAME_ERIMM_RGB, @@ -228,54 +188,35 @@ References ---------- :cite:`Spaulding2000b` - -RGB_COLOURSPACE_ERIMM_RGB : RGB_Colourspace -""" - -PRIMARIES_PROPHOTO_RGB = PRIMARIES_ROMM_RGB -""" -*ProPhoto RGB* colourspace primaries. - -PRIMARIES_PROPHOTO_RGB : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_PROPHOTO_RGB = WHITEPOINT_NAME_ROMM_RGB -""" -*ProPhoto RGB* colourspace whitepoint name. - -WHITEPOINT_NAME_PROPHOTO_RGB : unicode """ -CCS_WHITEPOINT_PROPHOTO_RGB = CCS_WHITEPOINT_ROMM_RGB -""" -*ProPhoto RGB* colourspace whitepoint chromaticity coordinates. +PRIMARIES_PROPHOTO_RGB: NDArray = PRIMARIES_ROMM_RGB +"""*ProPhoto RGB* colourspace primaries.""" -CCS_WHITEPOINT_PROPHOTO_RGB : ndarray -""" +WHITEPOINT_NAME_PROPHOTO_RGB: str = WHITEPOINT_NAME_ROMM_RGB +"""*ProPhoto RGB* colourspace whitepoint name.""" -MATRIX_PROPHOTO_RGB_TO_XYZ = MATRIX_ROMM_RGB_TO_XYZ -""" -*ProPhoto RGB* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_PROPHOTO_RGB_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_PROPHOTO_RGB: NDArray = CCS_WHITEPOINT_ROMM_RGB +"""*ProPhoto RGB* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_PROPHOTO_RGB = MATRIX_XYZ_TO_ROMM_RGB -""" -*CIE XYZ* tristimulus values to *ProPhoto RGB* colourspace matrix. +MATRIX_PROPHOTO_RGB_TO_XYZ: NDArray = MATRIX_ROMM_RGB_TO_XYZ +"""*ProPhoto RGB* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_PROPHOTO_RGB : array_like, (3, 3) -""" +MATRIX_XYZ_TO_PROPHOTO_RGB: NDArray = MATRIX_XYZ_TO_ROMM_RGB +"""*CIE XYZ* tristimulus values to *ProPhoto RGB* colourspace matrix.""" -RGB_COLOURSPACE_PROPHOTO_RGB = RGB_Colourspace( - 'ProPhoto RGB', +RGB_COLOURSPACE_PROPHOTO_RGB: RGB_Colourspace = RGB_Colourspace( + "ProPhoto RGB", PRIMARIES_PROPHOTO_RGB, CCS_WHITEPOINT_PROPHOTO_RGB, WHITEPOINT_NAME_PROPHOTO_RGB, MATRIX_PROPHOTO_RGB_TO_XYZ, MATRIX_XYZ_TO_PROPHOTO_RGB, - cctf_encoding_ProPhotoRGB, - cctf_decoding_ProPhotoRGB, + # NOTE: The "colour.models.cctf_encoding_ProPhotoRGB" and + # "colour.models.cctf_decoding_ProPhotoRGB" definitions cannot be pickled + # properly within that class. + cctf_encoding_ROMMRGB, + cctf_decoding_ROMMRGB, ) RGB_COLOURSPACE_PROPHOTO_RGB.__doc__ = """ *ProPhoto RGB* colourspace, an alias colourspace for *ROMM RGB*. @@ -283,6 +224,4 @@ References ---------- :cite:`ANSI2003a`, :cite:`Spaulding2000b` - -RGB_COLOURSPACE_PROPHOTO_RGB : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/russell_rgb.py b/colour/models/rgb/datasets/russell_rgb.py index 653ece177f..b9c1769bbb 100644 --- a/colour/models/rgb/datasets/russell_rgb.py +++ b/colour/models/rgb/datasets/russell_rgb.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Russell RGB Colourspace ======================= @@ -13,71 +12,62 @@ space. http://www.russellcottrell.com/photo/downloads/RussellRGB.icc """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from functools import partial from colour.colorimetry.datasets import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, gamma_function, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + gamma_function, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_RUSSELL_RGB', 'WHITEPOINT_NAME_RUSSELL_RGB', - 'CCS_WHITEPOINT_RUSSELL_RGB', 'MATRIX_RUSSELL_RGB_TO_XYZ', - 'MATRIX_XYZ_TO_RUSSELL_RGB', 'RGB_COLOURSPACE_RUSSELL_RGB' + "PRIMARIES_RUSSELL_RGB", + "WHITEPOINT_NAME_RUSSELL_RGB", + "CCS_WHITEPOINT_RUSSELL_RGB", + "MATRIX_RUSSELL_RGB_TO_XYZ", + "MATRIX_XYZ_TO_RUSSELL_RGB", + "RGB_COLOURSPACE_RUSSELL_RGB", ] -PRIMARIES_RUSSELL_RGB = np.array([ - [0.6900, 0.3100], - [0.1800, 0.7700], - [0.1000, 0.0200], -]) -""" -*Russell RGB* colourspace primaries. - -PRIMARIES_RUSSELL_RGB : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_RUSSELL_RGB = 'D55' -""" -*Russell RGB* colourspace whitepoint name. - -WHITEPOINT_NAME_RUSSELL_RGB : unicode -""" - -CCS_WHITEPOINT_RUSSELL_RGB = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_RUSSELL_RGB]) -""" -*Russell RGB* colourspace whitepoint chromaticity coordinates. +PRIMARIES_RUSSELL_RGB: NDArray = np.array( + [ + [0.6900, 0.3100], + [0.1800, 0.7700], + [0.1000, 0.0200], + ] +) +"""*Russell RGB* colourspace primaries.""" -CCS_WHITEPOINT_RUSSELL_RGB : ndarray -""" +WHITEPOINT_NAME_RUSSELL_RGB: str = "D55" +"""*Russell RGB* colourspace whitepoint name.""" -MATRIX_RUSSELL_RGB_TO_XYZ = normalised_primary_matrix( - PRIMARIES_RUSSELL_RGB, CCS_WHITEPOINT_RUSSELL_RGB) -""" -*Russell RGB* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_RUSSELL_RGB_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_RUSSELL_RGB: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_RUSSELL_RGB] +"""*Russell RGB* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_RUSSELL_RGB = np.linalg.inv(MATRIX_RUSSELL_RGB_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *Russell RGB* colourspace matrix. +MATRIX_RUSSELL_RGB_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_RUSSELL_RGB, CCS_WHITEPOINT_RUSSELL_RGB +) +"""*Russell RGB* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_RUSSELL_RGB : array_like, (3, 3) -""" +MATRIX_XYZ_TO_RUSSELL_RGB: NDArray = np.linalg.inv(MATRIX_RUSSELL_RGB_TO_XYZ) +"""*CIE XYZ* tristimulus values to *Russell RGB* colourspace matrix.""" -RGB_COLOURSPACE_RUSSELL_RGB = RGB_Colourspace( - 'Russell RGB', +RGB_COLOURSPACE_RUSSELL_RGB: RGB_Colourspace = RGB_Colourspace( + "Russell RGB", PRIMARIES_RUSSELL_RGB, CCS_WHITEPOINT_RUSSELL_RGB, WHITEPOINT_NAME_RUSSELL_RGB, @@ -92,6 +82,4 @@ References ---------- :cite:`Cottrella` - -RGB_COLOURSPACE_RUSSELL_RGB : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/sharp.py b/colour/models/rgb/datasets/sharp.py index 2963c786e0..2e5b1f4a6e 100644 --- a/colour/models/rgb/datasets/sharp.py +++ b/colour/models/rgb/datasets/sharp.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Sharp RGB Colourspace ===================== @@ -21,32 +20,41 @@ and Shaw, N. """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, linear_function, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + linear_function, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_SHARP_RGB', 'WHITEPOINT_NAME_SHARP_RGB', - 'CCS_WHITEPOINT_SHARP_RGB', 'MATRIX_SHARP_RGB_TO_XYZ', - 'MATRIX_XYZ_TO_SHARP_RGB', 'RGB_COLOURSPACE_SHARP_RGB' + "PRIMARIES_SHARP_RGB", + "WHITEPOINT_NAME_SHARP_RGB", + "CCS_WHITEPOINT_SHARP_RGB", + "MATRIX_SHARP_RGB_TO_XYZ", + "MATRIX_XYZ_TO_SHARP_RGB", + "RGB_COLOURSPACE_SHARP_RGB", ] -PRIMARIES_SHARP_RGB = np.array([ - [0.6898, 0.3206], - [0.0736, 0.9003], - [0.1166, 0.0374], -]) +PRIMARIES_SHARP_RGB: NDArray = np.array( + [ + [0.6898, 0.3206], + [0.0736, 0.9003], + [0.1166, 0.0374], + ] +) """ *Sharp RGB* colourspace primaries. @@ -70,42 +78,26 @@ and whitepoint: [0.6898, 0.3206, 0.0736, 0.9003, 0.1166, 0.0374, 1 / 3, 1 / 3] - -PRIMARIES_SHARP_RGB : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_SHARP_RGB = 'E' -""" -*Sharp RGB* colourspace whitepoint name. - -WHITEPOINT_NAME_SHARP_RGB : unicode -""" - -CCS_WHITEPOINT_SHARP_RGB = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_SHARP_RGB]) """ -*Sharp RGB* colourspace whitepoint chromaticity coordinates. -CCS_WHITEPOINT_SHARP_RGB : ndarray -""" +WHITEPOINT_NAME_SHARP_RGB: str = "E" +"""*Sharp RGB* colourspace whitepoint name.""" -MATRIX_SHARP_RGB_TO_XYZ = normalised_primary_matrix(PRIMARIES_SHARP_RGB, - CCS_WHITEPOINT_SHARP_RGB) -""" -*Sharp RGB* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_SHARP_RGB_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_SHARP_RGB: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_SHARP_RGB] +"""*Sharp RGB* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_SHARP_RGB = np.linalg.inv(MATRIX_SHARP_RGB_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *Sharp RGB* colourspace matrix. +MATRIX_SHARP_RGB_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_SHARP_RGB, CCS_WHITEPOINT_SHARP_RGB +) +"""*Sharp RGB* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_SHARP_RGB : array_like, (3, 3) -""" +MATRIX_XYZ_TO_SHARP_RGB: NDArray = np.linalg.inv(MATRIX_SHARP_RGB_TO_XYZ) +"""*CIE XYZ* tristimulus values to *Sharp RGB* colourspace matrix.""" -RGB_COLOURSPACE_SHARP_RGB = RGB_Colourspace( - 'Sharp RGB', +RGB_COLOURSPACE_SHARP_RGB: RGB_Colourspace = RGB_Colourspace( + "Sharp RGB", PRIMARIES_SHARP_RGB, CCS_WHITEPOINT_SHARP_RGB, WHITEPOINT_NAME_SHARP_RGB, @@ -120,6 +112,4 @@ References ---------- :cite:`Susstrunk2000`, :cite:`Ward2002`, :cite:`Ward2016` - -RGB_COLOURSPACE_SHARP_RGB : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/smpte_240m.py b/colour/models/rgb/datasets/smpte_240m.py index 85b32dfedf..ebeca43f4d 100644 --- a/colour/models/rgb/datasets/smpte_240m.py +++ b/colour/models/rgb/datasets/smpte_240m.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ SMPTE 240M Colourspace ====================== @@ -16,70 +15,62 @@ SMPTE%20normes%20et%20confs/s240m.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, normalised_primary_matrix, - oetf_SMPTE240M, eotf_SMPTE240M) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + normalised_primary_matrix, + oetf_SMPTE240M, + eotf_SMPTE240M, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_SMPTE_240M', 'WHITEPOINT_NAME_SMPTE_240M', - 'CCS_WHITEPOINT_SMPTE_240M', 'MATRIX_SMPTE_240M_TO_XYZ', - 'MATRIX_XYZ_TO_SMPTE_240M', 'RGB_COLOURSPACE_SMPTE_240M' + "PRIMARIES_SMPTE_240M", + "WHITEPOINT_NAME_SMPTE_240M", + "CCS_WHITEPOINT_SMPTE_240M", + "MATRIX_SMPTE_240M_TO_XYZ", + "MATRIX_XYZ_TO_SMPTE_240M", + "RGB_COLOURSPACE_SMPTE_240M", ] -PRIMARIES_SMPTE_240M = np.array([ - [0.6300, 0.3400], - [0.3100, 0.5950], - [0.1550, 0.0700], -]) -""" -*SMPTE 240M* colourspace primaries. - -PRIMARIES_SMPTE_240M : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_SMPTE_240M = 'D65' -""" -*SMPTE 240M* colourspace whitepoint name. - -WHITEPOINT_NAME_SMPTE_240M : unicode -""" - -CCS_WHITEPOINT_SMPTE_240M = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_SMPTE_240M]) -""" -*SMPTE 240M* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_SMPTE_240M : ndarray -""" +PRIMARIES_SMPTE_240M: NDArray = np.array( + [ + [0.6300, 0.3400], + [0.3100, 0.5950], + [0.1550, 0.0700], + ] +) +"""*SMPTE 240M* colourspace primaries.""" -MATRIX_SMPTE_240M_TO_XYZ = normalised_primary_matrix( - PRIMARIES_SMPTE_240M, CCS_WHITEPOINT_SMPTE_240M) -""" -*SMPTE 240M* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_SMPTE_240M: str = "D65" +"""*SMPTE 240M* colourspace whitepoint name.""" -MATRIX_SMPTE_240M_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_SMPTE_240M: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_SMPTE_240M] +"""*SMPTE 240M* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_SMPTE_240M = np.linalg.inv(MATRIX_SMPTE_240M_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *SMPTE 240M* colourspace matrix. +MATRIX_SMPTE_240M_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_SMPTE_240M, CCS_WHITEPOINT_SMPTE_240M +) +"""*SMPTE 240M* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_SMPTE_240M : array_like, (3, 3) -""" +MATRIX_XYZ_TO_SMPTE_240M: NDArray = np.linalg.inv(MATRIX_SMPTE_240M_TO_XYZ) +"""*CIE XYZ* tristimulus values to *SMPTE 240M* colourspace matrix.""" -RGB_COLOURSPACE_SMPTE_240M = RGB_Colourspace( - 'SMPTE 240M', +RGB_COLOURSPACE_SMPTE_240M: RGB_Colourspace = RGB_Colourspace( + "SMPTE 240M", PRIMARIES_SMPTE_240M, CCS_WHITEPOINT_SMPTE_240M, WHITEPOINT_NAME_SMPTE_240M, @@ -94,6 +85,4 @@ References ---------- :cite:`SocietyofMotionPictureandTelevisionEngineers1999b`, - -RGB_COLOURSPACE_SMPTE_240M : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/smpte_c.py b/colour/models/rgb/datasets/smpte_c.py index 09a4079828..8691d250cd 100644 --- a/colour/models/rgb/datasets/smpte_c.py +++ b/colour/models/rgb/datasets/smpte_c.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ SMPTE C Colourspace =================== @@ -15,70 +14,62 @@ Picture and Television Engineers. doi:10.5594/S9781614821649 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from functools import partial from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, gamma_function, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + gamma_function, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_SMPTE_C', 'WHITEPOINT_NAME_SMPTE_C', 'CCS_WHITEPOINT_SMPTE_C', - 'MATRIX_SMPTE_C_TO_XYZ', 'MATRIX_XYZ_TO_SMPTE_C', 'RGB_COLOURSPACE_SMPTE_C' + "PRIMARIES_SMPTE_C", + "WHITEPOINT_NAME_SMPTE_C", + "CCS_WHITEPOINT_SMPTE_C", + "MATRIX_SMPTE_C_TO_XYZ", + "MATRIX_XYZ_TO_SMPTE_C", + "RGB_COLOURSPACE_SMPTE_C", ] -PRIMARIES_SMPTE_C = np.array([ - [0.630, 0.340], - [0.310, 0.595], - [0.155, 0.070], -]) -""" -*SMPTE C* colourspace primaries. - -PRIMARIES_SMPTE_C : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_SMPTE_C = 'D65' -""" -*SMPTE C* colourspace whitepoint name. - -WHITEPOINT_NAME_SMPTE_C : unicode -""" - -CCS_WHITEPOINT_SMPTE_C = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_SMPTE_C]) -""" -*SMPTE C* colourspace whitepoint chromaticity coordinates. +PRIMARIES_SMPTE_C: NDArray = np.array( + [ + [0.630, 0.340], + [0.310, 0.595], + [0.155, 0.070], + ] +) +"""*SMPTE C* colourspace primaries.""" -CCS_WHITEPOINT_SMPTE_C : ndarray -""" +WHITEPOINT_NAME_SMPTE_C: str = "D65" +"""*SMPTE C* colourspace whitepoint name.""" -MATRIX_SMPTE_C_TO_XYZ = normalised_primary_matrix(PRIMARIES_SMPTE_C, - CCS_WHITEPOINT_SMPTE_C) -""" -*SMPTE C* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_SMPTE_C_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_SMPTE_C: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_SMPTE_C] +"""*SMPTE C* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_SMPTE_C = np.linalg.inv(MATRIX_SMPTE_C_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *SMPTE C* colourspace matrix. +MATRIX_SMPTE_C_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_SMPTE_C, CCS_WHITEPOINT_SMPTE_C +) +"""*SMPTE C* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_SMPTE_C : array_like, (3, 3) -""" +MATRIX_XYZ_TO_SMPTE_C: NDArray = np.linalg.inv(MATRIX_SMPTE_C_TO_XYZ) +"""*CIE XYZ* tristimulus values to *SMPTE C* colourspace matrix.""" -RGB_COLOURSPACE_SMPTE_C = RGB_Colourspace( - 'SMPTE C', +RGB_COLOURSPACE_SMPTE_C: RGB_Colourspace = RGB_Colourspace( + "SMPTE C", PRIMARIES_SMPTE_C, CCS_WHITEPOINT_SMPTE_C, WHITEPOINT_NAME_SMPTE_C, @@ -93,6 +84,4 @@ References ---------- :cite:`SocietyofMotionPictureandTelevisionEngineers2004a` - -RGB_COLOURSPACE_SMPTE_C : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/sony.py b/colour/models/rgb/datasets/sony.py index ed1b14ee73..84580e93f4 100644 --- a/colour/models/rgb/datasets/sony.py +++ b/colour/models/rgb/datasets/sony.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Sony Colourspaces ================= @@ -44,91 +43,98 @@ vendorSupplied/sony/IDT.Sony.Venice_SLog3_SGamut3Cine.ctl """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, log_encoding_SLog2, - log_decoding_SLog2, log_encoding_SLog3, - log_decoding_SLog3, normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + log_encoding_SLog2, + log_decoding_SLog2, + log_encoding_SLog3, + log_decoding_SLog3, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_S_GAMUT', 'WHITEPOINT_NAME_S_GAMUT', 'CCS_WHITEPOINT_S_GAMUT', - 'MATRIX_S_GAMUT_TO_XYZ', 'MATRIX_XYZ_TO_S_GAMUT', - 'RGB_COLOURSPACE_S_GAMUT', 'PRIMARIES_S_GAMUT3', - 'WHITEPOINT_NAME_S_GAMUT3', 'CCS_WHITEPOINT_S_GAMUT3', - 'MATRIX_S_GAMUT3_TO_XYZ', 'MATRIX_XYZ_TO_S_GAMUT3', - 'RGB_COLOURSPACE_S_GAMUT3', 'PRIMARIES_S_GAMUT3_CINE', - 'WHITEPOINT_NAME_S_GAMUT3_CINE', 'CCS_WHITEPOINT_S_GAMUT3_CINE', - 'MATRIX_S_GAMUT3_CINE_TO_XYZ', 'MATRIX_XYZ_TO_S_GAMUT3_CINE', - 'RGB_COLOURSPACE_S_GAMUT3_CINE', 'PRIMARIES_VENICE_S_GAMUT3', - 'WHITEPOINT_NAME_VENICE_S_GAMUT3', 'CCS_WHITEPOINT_VENICE_S_GAMUT3', - 'MATRIX_VENICE_S_GAMUT3_TO_XYZ', 'MATRIX_XYZ_TO_VENICE_S_GAMUT3', - 'RGB_COLOURSPACE_VENICE_S_GAMUT3', 'PRIMARIES_VENICE_S_GAMUT3_CINE', - 'WHITEPOINT_NAME_VENICE_S_GAMUT3_CINE', - 'CCS_WHITEPOINT_VENICE_S_GAMUT3_CINE', - 'MATRIX_VENICE_S_GAMUT3_CINE_TO_XYZ', 'MATRIX_XYZ_TO_VENICE_S_GAMUT3_CINE', - 'RGB_COLOURSPACE_VENICE_S_GAMUT3_CINE' + "PRIMARIES_S_GAMUT", + "WHITEPOINT_NAME_S_GAMUT", + "CCS_WHITEPOINT_S_GAMUT", + "MATRIX_S_GAMUT_TO_XYZ", + "MATRIX_XYZ_TO_S_GAMUT", + "RGB_COLOURSPACE_S_GAMUT", + "PRIMARIES_S_GAMUT3", + "WHITEPOINT_NAME_S_GAMUT3", + "CCS_WHITEPOINT_S_GAMUT3", + "MATRIX_S_GAMUT3_TO_XYZ", + "MATRIX_XYZ_TO_S_GAMUT3", + "RGB_COLOURSPACE_S_GAMUT3", + "PRIMARIES_S_GAMUT3_CINE", + "WHITEPOINT_NAME_S_GAMUT3_CINE", + "CCS_WHITEPOINT_S_GAMUT3_CINE", + "MATRIX_S_GAMUT3_CINE_TO_XYZ", + "MATRIX_XYZ_TO_S_GAMUT3_CINE", + "RGB_COLOURSPACE_S_GAMUT3_CINE", + "PRIMARIES_VENICE_S_GAMUT3", + "WHITEPOINT_NAME_VENICE_S_GAMUT3", + "CCS_WHITEPOINT_VENICE_S_GAMUT3", + "MATRIX_VENICE_S_GAMUT3_TO_XYZ", + "MATRIX_XYZ_TO_VENICE_S_GAMUT3", + "RGB_COLOURSPACE_VENICE_S_GAMUT3", + "PRIMARIES_VENICE_S_GAMUT3_CINE", + "WHITEPOINT_NAME_VENICE_S_GAMUT3_CINE", + "CCS_WHITEPOINT_VENICE_S_GAMUT3_CINE", + "MATRIX_VENICE_S_GAMUT3_CINE_TO_XYZ", + "MATRIX_XYZ_TO_VENICE_S_GAMUT3_CINE", + "RGB_COLOURSPACE_VENICE_S_GAMUT3_CINE", ] -PRIMARIES_S_GAMUT = np.array([ - [0.7300, 0.2800], - [0.1400, 0.8550], - [0.1000, -0.0500], -]) -""" -*S-Gamut* colourspace primaries. - -PRIMARIES_S_GAMUT : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_S_GAMUT = 'D65' -""" -*S-Gamut* colourspace whitepoint name. - -WHITEPOINT_NAME_S_GAMUT : unicode -""" - -CCS_WHITEPOINT_S_GAMUT = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_S_GAMUT]) -""" -*S-Gamut* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_S_GAMUT : ndarray -""" - -MATRIX_S_GAMUT_TO_XYZ = np.array([ - [0.7064827132, 0.1288010498, 0.1151721641], - [0.2709796708, 0.7866064112, -0.0575860820], - [-0.0096778454, 0.0046000375, 1.0941355587], -]) -""" -*S-Gamut* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_S_GAMUT_TO_XYZ : array_like, (3, 3) -""" - -MATRIX_XYZ_TO_S_GAMUT = np.array([ - [1.5073998991, -0.2458221374, -0.1716116808], - [-0.5181517271, 1.3553912409, 0.1258786682], - [0.0155116982, -0.0078727714, 0.9119163656], -]) -""" -*CIE XYZ* tristimulus values to *S-Gamut* colourspace matrix. - -MATRIX_XYZ_TO_S_GAMUT : array_like, (3, 3) -""" +PRIMARIES_S_GAMUT: NDArray = np.array( + [ + [0.7300, 0.2800], + [0.1400, 0.8550], + [0.1000, -0.0500], + ] +) +"""*S-Gamut* colourspace primaries.""" + +WHITEPOINT_NAME_S_GAMUT: str = "D65" +"""*S-Gamut* colourspace whitepoint name.""" + +CCS_WHITEPOINT_S_GAMUT: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_S_GAMUT] +"""*S-Gamut* colourspace whitepoint chromaticity coordinates.""" + +MATRIX_S_GAMUT_TO_XYZ: NDArray = np.array( + [ + [0.7064827132, 0.1288010498, 0.1151721641], + [0.2709796708, 0.7866064112, -0.0575860820], + [-0.0096778454, 0.0046000375, 1.0941355587], + ] +) +"""*S-Gamut* colourspace to *CIE XYZ* tristimulus values matrix.""" + +MATRIX_XYZ_TO_S_GAMUT: NDArray = np.array( + [ + [1.5073998991, -0.2458221374, -0.1716116808], + [-0.5181517271, 1.3553912409, 0.1258786682], + [0.0155116982, -0.0078727714, 0.9119163656], + ] +) +"""*CIE XYZ* tristimulus values to *S-Gamut* colourspace matrix.""" -RGB_COLOURSPACE_S_GAMUT = RGB_Colourspace( - 'S-Gamut', +RGB_COLOURSPACE_S_GAMUT: RGB_Colourspace = RGB_Colourspace( + "S-Gamut", PRIMARIES_S_GAMUT, CCS_WHITEPOINT_S_GAMUT, WHITEPOINT_NAME_S_GAMUT, @@ -143,47 +149,25 @@ References ---------- :cite:`Gaggioni`, :cite:`SonyCorporation` - -RGB_COLOURSPACE_S_GAMUT : RGB_Colourspace """ -PRIMARIES_S_GAMUT3 = PRIMARIES_S_GAMUT -""" -*S-Gamut3* colourspace primaries. +PRIMARIES_S_GAMUT3: NDArray = PRIMARIES_S_GAMUT +"""*S-Gamut3* colourspace primaries.""" -PRIMARIES_S_GAMUT3 : ndarray, (3, 2) -""" +WHITEPOINT_NAME_S_GAMUT3: str = WHITEPOINT_NAME_S_GAMUT +"""*S-Gamut3* colourspace whitepoint name.""" -WHITEPOINT_NAME_S_GAMUT3 = WHITEPOINT_NAME_S_GAMUT -""" -*S-Gamut3* colourspace whitepoint name. +CCS_WHITEPOINT_S_GAMUT3: NDArray = CCS_WHITEPOINT_S_GAMUT +"""*S-Gamut3* colourspace whitepoint chromaticity coordinates.""" -WHITEPOINT_NAME_S_GAMUT3 : unicode -""" +MATRIX_S_GAMUT3_TO_XYZ: NDArray = MATRIX_S_GAMUT_TO_XYZ +"""*S-Gamut3* colourspace to *CIE XYZ* tristimulus values matrix.""" -CCS_WHITEPOINT_S_GAMUT3 = CCS_WHITEPOINT_S_GAMUT -""" -*S-Gamut3* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_S_GAMUT3 : ndarray -""" - -MATRIX_S_GAMUT3_TO_XYZ = MATRIX_S_GAMUT_TO_XYZ -""" -*S-Gamut3* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_S_GAMUT3_TO_XYZ : array_like, (3, 3) -""" +MATRIX_XYZ_TO_S_GAMUT3: NDArray = MATRIX_XYZ_TO_S_GAMUT +"""*CIE XYZ* tristimulus values to *S-Gamut3* colourspace matrix.""" -MATRIX_XYZ_TO_S_GAMUT3 = MATRIX_XYZ_TO_S_GAMUT -""" -*CIE XYZ* tristimulus values to *S-Gamut3* colourspace matrix. - -MATRIX_XYZ_TO_S_GAMUT3 : array_like, (3, 3) -""" - -RGB_COLOURSPACE_S_GAMUT3 = RGB_Colourspace( - 'S-Gamut3', +RGB_COLOURSPACE_S_GAMUT3: RGB_Colourspace = RGB_Colourspace( + "S-Gamut3", PRIMARIES_S_GAMUT3, CCS_WHITEPOINT_S_GAMUT3, WHITEPOINT_NAME_S_GAMUT3, @@ -198,59 +182,43 @@ References ---------- :cite:`SonyCorporationd` - -RGB_COLOURSPACE_S_GAMUT3 : RGB_Colourspace -""" - -PRIMARIES_S_GAMUT3_CINE = np.array([ - [0.76600, 0.27500], - [0.22500, 0.80000], - [0.08900, -0.08700], -]) -""" -*S-Gamut3.Cine* colourspace primaries. - -PRIMARIES_S_GAMUT3_CINE : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_S_GAMUT3_CINE = WHITEPOINT_NAME_S_GAMUT -""" -*S-Gamut3.Cine* colourspace whitepoint name. - -WHITEPOINT_NAME_S_GAMUT3_CINE : unicode -""" - -CCS_WHITEPOINT_S_GAMUT3_CINE = CCS_WHITEPOINT_S_GAMUT """ -*S-Gamut3.Cine* colourspace whitepoint chromaticity coordinates. -CCS_WHITEPOINT_S_GAMUT3_CINE : ndarray -""" +PRIMARIES_S_GAMUT3_CINE: NDArray = np.array( + [ + [0.76600, 0.27500], + [0.22500, 0.80000], + [0.08900, -0.08700], + ] +) +"""*S-Gamut3.Cine* colourspace primaries.""" -MATRIX_S_GAMUT3_CINE_TO_XYZ = np.array([ - [0.5990839208, 0.2489255161, 0.1024464902], - [0.2150758201, 0.8850685017, -0.1001443219], - [-0.0320658495, -0.0276583907, 1.1487819910], -]) -""" -*S-Gamut3.Cine* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_S_GAMUT3_CINE: str = WHITEPOINT_NAME_S_GAMUT +"""*S-Gamut3.Cine* colourspace whitepoint name.""" -MATRIX_S_GAMUT3_CINE_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_S_GAMUT3_CINE: NDArray = CCS_WHITEPOINT_S_GAMUT +"""*S-Gamut3.Cine* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_S_GAMUT3_CINE = np.array([ - [1.8467789693, -0.5259861230, -0.2105452114], - [-0.4441532629, 1.2594429028, 0.1493999729], - [0.0408554212, 0.0156408893, 0.8682072487], -]) -""" -*CIE XYZ* tristimulus values to *S-Gamut3.Cine* colourspace matrix. - -MATRIX_XYZ_TO_S_GAMUT3_CINE : array_like, (3, 3) -""" +MATRIX_S_GAMUT3_CINE_TO_XYZ: NDArray = np.array( + [ + [0.5990839208, 0.2489255161, 0.1024464902], + [0.2150758201, 0.8850685017, -0.1001443219], + [-0.0320658495, -0.0276583907, 1.1487819910], + ] +) +"""*S-Gamut3.Cine* colourspace to *CIE XYZ* tristimulus values matrix.""" + +MATRIX_XYZ_TO_S_GAMUT3_CINE: NDArray = np.array( + [ + [1.8467789693, -0.5259861230, -0.2105452114], + [-0.4441532629, 1.2594429028, 0.1493999729], + [0.0408554212, 0.0156408893, 0.8682072487], + ] +) +"""*CIE XYZ* tristimulus values to *S-Gamut3.Cine* colourspace matrix.""" -RGB_COLOURSPACE_S_GAMUT3_CINE = RGB_Colourspace( - 'S-Gamut3.Cine', +RGB_COLOURSPACE_S_GAMUT3_CINE: RGB_Colourspace = RGB_Colourspace( + "S-Gamut3.Cine", PRIMARIES_S_GAMUT3_CINE, CCS_WHITEPOINT_S_GAMUT3_CINE, WHITEPOINT_NAME_S_GAMUT3_CINE, @@ -265,52 +233,35 @@ References ---------- :cite:`SonyCorporatione` - -RGB_COLOURSPACE_S_GAMUT3_CINE : RGB_Colourspace """ -PRIMARIES_VENICE_S_GAMUT3 = np.array([ - [0.740464264304292, 0.279364374750660], - [0.089241145423286, 0.893809528608105], - [0.110488236673827, -0.052579333080476], -]) -""" -*Venice S-Gamut3* colourspace primaries. - -PRIMARIES_VENICE_S_GAMUT3 : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_VENICE_S_GAMUT3 = WHITEPOINT_NAME_S_GAMUT -""" -*Venice S-Gamut3* colourspace whitepoint name. - -WHITEPOINT_NAME_VENICE_S_GAMUT3 : unicode -""" - -CCS_WHITEPOINT_VENICE_S_GAMUT3 = CCS_WHITEPOINT_S_GAMUT -""" -*Venice S-Gamut3* colourspace whitepoint chromaticity coordinates. +PRIMARIES_VENICE_S_GAMUT3: NDArray = np.array( + [ + [0.740464264304292, 0.279364374750660], + [0.089241145423286, 0.893809528608105], + [0.110488236673827, -0.052579333080476], + ] +) +"""*Venice S-Gamut3* colourspace primaries.""" -CCS_WHITEPOINT_VENICE_S_GAMUT3 : ndarray -""" +WHITEPOINT_NAME_VENICE_S_GAMUT3: str = WHITEPOINT_NAME_S_GAMUT +"""*Venice S-Gamut3* colourspace whitepoint name.""" -MATRIX_VENICE_S_GAMUT3_TO_XYZ = normalised_primary_matrix( - PRIMARIES_VENICE_S_GAMUT3, CCS_WHITEPOINT_VENICE_S_GAMUT3) -""" -*Venice S-Gamut3* colourspace to *CIE XYZ* tristimulus values matrix. +CCS_WHITEPOINT_VENICE_S_GAMUT3: NDArray = CCS_WHITEPOINT_S_GAMUT +"""*Venice S-Gamut3* colourspace whitepoint chromaticity coordinates.""" -MATRIX_VENICE_S_GAMUT3_TO_XYZ : array_like, (3, 3) -""" - -MATRIX_XYZ_TO_VENICE_S_GAMUT3 = np.linalg.inv(MATRIX_VENICE_S_GAMUT3_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *Venice S-Gamut3* colourspace matrix. +MATRIX_VENICE_S_GAMUT3_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_VENICE_S_GAMUT3, CCS_WHITEPOINT_VENICE_S_GAMUT3 +) +"""*Venice S-Gamut3* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_VENICE_S_GAMUT3 : array_like, (3, 3) -""" +MATRIX_XYZ_TO_VENICE_S_GAMUT3: NDArray = np.linalg.inv( + MATRIX_VENICE_S_GAMUT3_TO_XYZ +) +"""*CIE XYZ* tristimulus values to *Venice S-Gamut3* colourspace matrix.""" -RGB_COLOURSPACE_VENICE_S_GAMUT3 = RGB_Colourspace( - 'Venice S-Gamut3', +RGB_COLOURSPACE_VENICE_S_GAMUT3: RGB_Colourspace = RGB_Colourspace( + "Venice S-Gamut3", PRIMARIES_VENICE_S_GAMUT3, CCS_WHITEPOINT_VENICE_S_GAMUT3, WHITEPOINT_NAME_VENICE_S_GAMUT3, @@ -325,53 +276,35 @@ References ---------- :cite:`SonyElectronicsCorporation2020` - -RGB_COLOURSPACE_VENICE_S_GAMUT3 : RGB_Colourspace """ -PRIMARIES_VENICE_S_GAMUT3_CINE = np.array([ - [0.775901871567345, 0.274502392854799], - [0.188682902773355, 0.828684937020288], - [0.101337382499301, -0.089187517306263], -]) -""" -*Venice S-Gamut3.Cine* colourspace primaries. - -PRIMARIES_VENICE_S_GAMUT3_CINE : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_VENICE_S_GAMUT3_CINE = WHITEPOINT_NAME_S_GAMUT -""" -*Venice S-Gamut3.Cine* colourspace whitepoint name. - -WHITEPOINT_NAME_VENICE_S_GAMUT3_CINE : unicode -""" - -CCS_WHITEPOINT_VENICE_S_GAMUT3_CINE = CCS_WHITEPOINT_S_GAMUT -""" -*Venice S-Gamut3.Cine* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_VENICE_S_GAMUT3_CINE : ndarray -""" +PRIMARIES_VENICE_S_GAMUT3_CINE: NDArray = np.array( + [ + [0.775901871567345, 0.274502392854799], + [0.188682902773355, 0.828684937020288], + [0.101337382499301, -0.089187517306263], + ] +) +"""*Venice S-Gamut3.Cine* colourspace primaries.""" -MATRIX_VENICE_S_GAMUT3_CINE_TO_XYZ = normalised_primary_matrix( - PRIMARIES_VENICE_S_GAMUT3_CINE, CCS_WHITEPOINT_VENICE_S_GAMUT3_CINE) -""" -*Venice S-Gamut3.Cine* colourspace to *CIE XYZ* tristimulus values matrix. +WHITEPOINT_NAME_VENICE_S_GAMUT3_CINE: str = WHITEPOINT_NAME_S_GAMUT +"""*Venice S-Gamut3.Cine* colourspace whitepoint name.""" -MATRIX_VENICE_S_GAMUT3_CINE_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_VENICE_S_GAMUT3_CINE: NDArray = CCS_WHITEPOINT_S_GAMUT +"""*Venice S-Gamut3.Cine* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_VENICE_S_GAMUT3_CINE = np.linalg.inv( - MATRIX_VENICE_S_GAMUT3_CINE_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *Venice S-Gamut3.Cine* colourspace matrix. +MATRIX_VENICE_S_GAMUT3_CINE_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_VENICE_S_GAMUT3_CINE, CCS_WHITEPOINT_VENICE_S_GAMUT3_CINE +) +"""*Venice S-Gamut3.Cine* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_VENICE_S_GAMUT3_CINE : array_like, (3, 3) -""" +MATRIX_XYZ_TO_VENICE_S_GAMUT3_CINE: NDArray = np.linalg.inv( + MATRIX_VENICE_S_GAMUT3_CINE_TO_XYZ +) +"""*CIE XYZ* tristimulus values to *Venice S-Gamut3.Cine* colourspace matrix.""" -RGB_COLOURSPACE_VENICE_S_GAMUT3_CINE = RGB_Colourspace( - 'Venice S-Gamut3.Cine', +RGB_COLOURSPACE_VENICE_S_GAMUT3_CINE: RGB_Colourspace = RGB_Colourspace( + "Venice S-Gamut3.Cine", PRIMARIES_VENICE_S_GAMUT3_CINE, CCS_WHITEPOINT_VENICE_S_GAMUT3_CINE, WHITEPOINT_NAME_VENICE_S_GAMUT3_CINE, @@ -386,6 +319,4 @@ References ---------- :cite:`SonyElectronicsCorporation2020a` - -RGB_COLOURSPACE_VENICE_S_GAMUT3_CINE : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/srgb.py b/colour/models/rgb/datasets/srgb.py index 6ac9d2e23e..e8b8514afb 100644 --- a/colour/models/rgb/datasets/srgb.py +++ b/colour/models/rgb/datasets/srgb.py @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- """ -sRGB Colourspace +SRGB Colourspace ================ Defines the *sRGB* colourspace: @@ -22,75 +21,67 @@ R-REC-BT.709-6-201506-I!!PDF-E.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import CCS_ILLUMINANTS +from colour.hints import NDArray from colour.models.rgb import RGB_Colourspace, eotf_inverse_sRGB, eotf_sRGB -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_sRGB', 'WHITEPOINT_NAME_sRGB', 'CCS_WHITEPOINT_sRGB', - 'MATRIX_sRGB_TO_XYZ', 'MATRIX_XYZ_TO_sRGB', 'RGB_COLOURSPACE_sRGB' + "PRIMARIES_sRGB", + "WHITEPOINT_NAME_sRGB", + "CCS_WHITEPOINT_sRGB", + "MATRIX_sRGB_TO_XYZ", + "MATRIX_XYZ_TO_sRGB", + "RGB_COLOURSPACE_sRGB", ] -PRIMARIES_sRGB = np.array([ - [0.6400, 0.3300], - [0.3000, 0.6000], - [0.1500, 0.0600], -]) -""" -*sRGB* colourspace primaries. - -PRIMARIES_sRGB : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_sRGB = 'D65' -""" -*sRGB* colourspace whitepoint name. - -CCS_WHITEPOINT_sRGB : unicode -""" - -CCS_WHITEPOINT_sRGB = (CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'][ - WHITEPOINT_NAME_sRGB]) -""" -*sRGB* colourspace whitepoint chromaticity coordinates. - -CCS_WHITEPOINT_sRGB : ndarray -""" - -MATRIX_sRGB_TO_XYZ = np.array([ - [0.4124, 0.3576, 0.1805], - [0.2126, 0.7152, 0.0722], - [0.0193, 0.1192, 0.9505], -]) -""" -*sRGB* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_sRGB_TO_XYZ : array_like, (3, 3) -""" - -MATRIX_XYZ_TO_sRGB = np.array([ - [3.2406, -1.5372, -0.4986], - [-0.9689, 1.8758, 0.0415], - [0.0557, -0.2040, 1.0570], -]) -""" -*CIE XYZ* tristimulus values to *sRGB* colourspace matrix. - -MATRIX_XYZ_TO_sRGB : array_like, (3, 3) -""" +PRIMARIES_sRGB: NDArray = np.array( + [ + [0.6400, 0.3300], + [0.3000, 0.6000], + [0.1500, 0.0600], + ] +) +"""*sRGB* colourspace primaries.""" + +WHITEPOINT_NAME_sRGB: str = "D65" +"""*sRGB* colourspace whitepoint name.""" + +CCS_WHITEPOINT_sRGB: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_sRGB] +"""*sRGB* colourspace whitepoint chromaticity coordinates.""" + +MATRIX_sRGB_TO_XYZ: NDArray = np.array( + [ + [0.4124, 0.3576, 0.1805], + [0.2126, 0.7152, 0.0722], + [0.0193, 0.1192, 0.9505], + ] +) +"""*sRGB* colourspace to *CIE XYZ* tristimulus values matrix.""" + +MATRIX_XYZ_TO_sRGB: NDArray = np.array( + [ + [3.2406, -1.5372, -0.4986], + [-0.9689, 1.8758, 0.0415], + [0.0557, -0.2040, 1.0570], + ] +) +"""*CIE XYZ* tristimulus values to *sRGB* colourspace matrix.""" -RGB_COLOURSPACE_sRGB = RGB_Colourspace( - 'sRGB', +RGB_COLOURSPACE_sRGB: RGB_Colourspace = RGB_Colourspace( + "sRGB", PRIMARIES_sRGB, CCS_WHITEPOINT_sRGB, WHITEPOINT_NAME_sRGB, @@ -106,6 +97,4 @@ ---------- :cite:`InternationalElectrotechnicalCommission1999a`, :cite:`InternationalTelecommunicationUnion2015i` - -RGB_COLOURSPACE_sRGB : RGB_Colourspace """ diff --git a/colour/models/rgb/datasets/tests/__init__.py b/colour/models/rgb/datasets/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/colour/models/rgb/datasets/tests/test__init__.py b/colour/models/rgb/datasets/tests/test__init__.py new file mode 100644 index 0000000000..dbdf4d0655 --- /dev/null +++ b/colour/models/rgb/datasets/tests/test__init__.py @@ -0,0 +1,173 @@ +"""Defines the unit tests for the :mod:`colour.models.rgb.datasets` module.""" + +import numpy as np +import pickle +import unittest +from copy import deepcopy + +from colour.models import ( + RGB_COLOURSPACES, + normalised_primary_matrix, +) +from colour.utilities import as_int, ignore_numpy_errors + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestRGB_COLOURSPACES", +] + + +class TestRGB_COLOURSPACES(unittest.TestCase): + """ + Define :attr:`colour.models.rgb.datasets.RGB_COLOURSPACES` + attribute unit tests methods. + """ + + def test_transformation_matrices(self): + """ + Test the transformations matrices from the + :attr:`colour.models.rgb.datasets.RGB_COLOURSPACES` attribute + colourspace models. + """ + + tolerances = { + "Adobe RGB (1998)": 1e-5, + "ALEXA Wide Gamut": 1e-6, + "DJI D-Gamut": 1e-4, + "ERIMM RGB": 1e-3, + "ProPhoto RGB": 1e-3, + "REDWideGamutRGB": 1e-6, + "RIMM RGB": 1e-3, + "ROMM RGB": 1e-3, + "sRGB": 1e-4, + "V-Gamut": 1e-6, + } + XYZ_r = np.array([0.5, 0.5, 0.5]).reshape([3, 1]) + for colourspace in RGB_COLOURSPACES.values(): + M = normalised_primary_matrix( + colourspace.primaries, colourspace.whitepoint + ) + + tolerance = tolerances.get(colourspace.name, 1e-7) + np.testing.assert_allclose( + colourspace.matrix_RGB_to_XYZ, + M, + rtol=tolerance, + atol=tolerance, + verbose=False, + ) + + RGB = np.dot(colourspace.matrix_XYZ_to_RGB, XYZ_r) + XYZ = np.dot(colourspace.matrix_RGB_to_XYZ, RGB) + np.testing.assert_allclose( + XYZ_r, XYZ, rtol=tolerance, atol=tolerance, verbose=False + ) + + # Derived transformation matrices. + colourspace = deepcopy(colourspace) + colourspace.use_derived_transformation_matrices(True) + RGB = np.dot(colourspace.matrix_XYZ_to_RGB, XYZ_r) + XYZ = np.dot(colourspace.matrix_RGB_to_XYZ, RGB) + np.testing.assert_almost_equal(XYZ_r, XYZ, decimal=7) + + def test_cctf(self): + """ + Test colour component transfer functions from the + :attr:`colour.models.rgb.datasets.RGB_COLOURSPACES` attribute + colourspace models. + """ + + ignored_colourspaces = ("ACESproxy",) + + decimals = {"DJI D-Gamut": 1, "F-Gamut": 4, "N-Gamut": 3} + + samples = np.hstack( + [np.linspace(0, 1, as_int(1e5)), np.linspace(0, 65504, 65504 * 10)] + ) + + for colourspace in RGB_COLOURSPACES.values(): + if colourspace.name in ignored_colourspaces: + continue + + cctf_encoding_s = colourspace.cctf_encoding(samples) + cctf_decoding_s = colourspace.cctf_decoding(cctf_encoding_s) + + np.testing.assert_almost_equal( + samples, + cctf_decoding_s, + decimal=decimals.get(colourspace.name, 7), + ) + + def test_n_dimensional_cctf(self): + """ + Test colour component transfer functions from the + :attr:`colour.models.rgb.datasets.RGB_COLOURSPACES` attribute + colourspace models n-dimensional arrays support. + """ + + decimals = {"DJI D-Gamut": 6, "F-Gamut": 4} + + for colourspace in RGB_COLOURSPACES.values(): + value_cctf_encoding = 0.5 + value_cctf_decoding = colourspace.cctf_decoding( + colourspace.cctf_encoding(value_cctf_encoding) + ) + np.testing.assert_almost_equal( + value_cctf_encoding, + value_cctf_decoding, + decimal=decimals.get(colourspace.name, 7), + ) + + value_cctf_encoding = np.tile(value_cctf_encoding, 6) + value_cctf_decoding = np.tile(value_cctf_decoding, 6) + np.testing.assert_almost_equal( + value_cctf_encoding, + value_cctf_decoding, + decimal=decimals.get(colourspace.name, 7), + ) + + value_cctf_encoding = np.reshape(value_cctf_encoding, (3, 2)) + value_cctf_decoding = np.reshape(value_cctf_decoding, (3, 2)) + np.testing.assert_almost_equal( + value_cctf_encoding, + value_cctf_decoding, + decimal=decimals.get(colourspace.name, 7), + ) + + value_cctf_encoding = np.reshape(value_cctf_encoding, (3, 2, 1)) + value_cctf_decoding = np.reshape(value_cctf_decoding, (3, 2, 1)) + np.testing.assert_almost_equal( + value_cctf_encoding, + value_cctf_decoding, + decimal=decimals.get(colourspace.name, 7), + ) + + @ignore_numpy_errors + def test_nan_cctf(self): + """ + Test colour component transfer functions from the + :attr:`colour.models.rgb.datasets.RGB_COLOURSPACES` attribute + colourspace models nan support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + for colourspace in RGB_COLOURSPACES.values(): + for case in cases: + colourspace.cctf_encoding(case) + colourspace.cctf_decoding(case) + + def test_pickle(self): + """Test the "pickle-ability" of the *RGB* colourspaces.""" + + for colourspace in RGB_COLOURSPACES.values(): + pickle.dumps(colourspace) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/models/rgb/datasets/xtreme_rgb.py b/colour/models/rgb/datasets/xtreme_rgb.py index 3790279fd3..a60f631c5d 100644 --- a/colour/models/rgb/datasets/xtreme_rgb.py +++ b/colour/models/rgb/datasets/xtreme_rgb.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Xtreme RGB Colourspace ====================== @@ -13,71 +12,62 @@ http://www.hutchcolor.com/profiles/XtremeRGB.zip """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from functools import partial from colour.colorimetry import CCS_ILLUMINANTS -from colour.models.rgb import (RGB_Colourspace, gamma_function, - normalised_primary_matrix) +from colour.hints import NDArray +from colour.models.rgb import ( + RGB_Colourspace, + gamma_function, + normalised_primary_matrix, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_XTREME_RGB', 'WHITEPOINT_NAME_XTREME_RGB', - 'CCS_WHITEPOINT_XTREME_RGB', 'MATRIX_XTREME_RGB_TO_XYZ', - 'MATRIX_XYZ_TO_XTREME_RGB', 'RGB_COLOURSPACE_XTREME_RGB' + "PRIMARIES_XTREME_RGB", + "WHITEPOINT_NAME_XTREME_RGB", + "CCS_WHITEPOINT_XTREME_RGB", + "MATRIX_XTREME_RGB_TO_XYZ", + "MATRIX_XYZ_TO_XTREME_RGB", + "RGB_COLOURSPACE_XTREME_RGB", ] -PRIMARIES_XTREME_RGB = np.array([ - [1.0, 0.0], - [0.0, 1.0], - [0.0, 0.0], -]) -""" -*Xtreme RGB* colourspace primaries. - -PRIMARIES_XTREME_RGB : ndarray, (3, 2) -""" - -WHITEPOINT_NAME_XTREME_RGB = 'D50' -""" -*Xtreme RGB* colourspace whitepoint name. - -CCS_WHITEPOINT_XTREME_RGB : unicode -""" - -CCS_WHITEPOINT_XTREME_RGB = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][WHITEPOINT_NAME_XTREME_RGB]) -""" -*Xtreme RGB* colourspace whitepoint chromaticity coordinates. +PRIMARIES_XTREME_RGB: NDArray = np.array( + [ + [1.0, 0.0], + [0.0, 1.0], + [0.0, 0.0], + ] +) +"""*Xtreme RGB* colourspace primaries.""" -CCS_WHITEPOINT_XTREME_RGB : ndarray -""" +WHITEPOINT_NAME_XTREME_RGB: str = "D50" +"""*Xtreme RGB* colourspace whitepoint name.""" -MATRIX_XTREME_RGB_TO_XYZ = normalised_primary_matrix( - PRIMARIES_XTREME_RGB, CCS_WHITEPOINT_XTREME_RGB) -""" -*Xtreme RGB* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_XTREME_RGB_TO_XYZ : array_like, (3, 3) -""" +CCS_WHITEPOINT_XTREME_RGB: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][WHITEPOINT_NAME_XTREME_RGB] +"""*Xtreme RGB* colourspace whitepoint chromaticity coordinates.""" -MATRIX_XYZ_TO_XTREME_RGB = np.linalg.inv(MATRIX_XTREME_RGB_TO_XYZ) -""" -*CIE XYZ* tristimulus values to *Xtreme RGB* colourspace matrix. +MATRIX_XTREME_RGB_TO_XYZ: NDArray = normalised_primary_matrix( + PRIMARIES_XTREME_RGB, CCS_WHITEPOINT_XTREME_RGB +) +"""*Xtreme RGB* colourspace to *CIE XYZ* tristimulus values matrix.""" -MATRIX_XYZ_TO_XTREME_RGB : array_like, (3, 3) -""" +MATRIX_XYZ_TO_XTREME_RGB: NDArray = np.linalg.inv(MATRIX_XTREME_RGB_TO_XYZ) +"""*CIE XYZ* tristimulus values to *Xtreme RGB* colourspace matrix.""" -RGB_COLOURSPACE_XTREME_RGB = RGB_Colourspace( - 'Xtreme RGB', +RGB_COLOURSPACE_XTREME_RGB: RGB_Colourspace = RGB_Colourspace( + "Xtreme RGB", PRIMARIES_XTREME_RGB, CCS_WHITEPOINT_XTREME_RGB, WHITEPOINT_NAME_XTREME_RGB, @@ -92,6 +82,4 @@ References ---------- :cite:`HutchColore` - -RGB_COLOURSPACE_XTREME_RGB : RGB_Colourspace """ diff --git a/colour/models/rgb/derivation.py b/colour/models/rgb/derivation.py index 23bb4f62f1..6020252b8e 100644 --- a/colour/models/rgb/derivation.py +++ b/colour/models/rgb/derivation.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ RGB Colourspace Derivation ========================== -Defines objects related to *RGB* colourspace derivation, essentially +Defines the objects related to *RGB* colourspace derivation, essentially calculating the normalised primary matrix for given *RGB* colourspace primaries and whitepoint: @@ -24,39 +23,52 @@ T. and Shaw, N. """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.adaptation import chromatic_adaptation_VonKries +from colour.hints import ( + Floating, + FloatingOrNDArray, + ArrayLike, + Literal, + NDArray, + Tuple, + Union, +) from colour.models import XYZ_to_xy, XYZ_to_xyY, xy_to_XYZ -from colour.utilities import as_numeric, ones, tsplit +from colour.utilities import as_float, as_float_array, ones, tsplit -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'xy_to_z', 'normalised_primary_matrix', 'chromatically_adapted_primaries', - 'primaries_whitepoint', 'RGB_luminance_equation', 'RGB_luminance' + "xy_to_z", + "normalised_primary_matrix", + "chromatically_adapted_primaries", + "primaries_whitepoint", + "RGB_luminance_equation", + "RGB_luminance", ] -def xy_to_z(xy): +def xy_to_z(xy: ArrayLike) -> Floating: """ - Returns the *z* coordinate using given :math:`xy` chromaticity coordinates. + Return the *z* coordinate using given :math:`xy` chromaticity coordinates. Parameters ---------- - xy : array_like + xy :math:`xy` chromaticity coordinates. Returns ------- - numeric + :class:`numpy.floating` *z* coordinate. Examples @@ -72,22 +84,24 @@ def xy_to_z(xy): return z -def normalised_primary_matrix(primaries, whitepoint): +def normalised_primary_matrix( + primaries: ArrayLike, whitepoint: ArrayLike +) -> NDArray: """ - Computes the *Normalised Primary Matrix* (NPM) converting a *RGB* + Compute the *Normalised Primary Matrix* (NPM) converting a *RGB* colourspace array to *CIE XYZ* tristimulus values using given *primaries* and *whitepoint* :math:`xy` chromaticity coordinates. Parameters ---------- - primaries : array_like, (3, 2) + primaries Primaries :math:`xy` chromaticity coordinates. - whitepoint : array_like + whitepoint Illuminant / whitepoint :math:`xy` chromaticity coordinates. Returns ------- - ndarray, (3, 3) + :class:`numpy.ndarray` *Normalised Primary Matrix* (NPM). References @@ -106,7 +120,7 @@ def normalised_primary_matrix(primaries, whitepoint): primaries = np.reshape(primaries, (3, 2)) - z = xy_to_z(primaries)[..., np.newaxis] + z = as_float_array(xy_to_z(primaries))[..., np.newaxis] primaries = np.transpose(np.hstack([primaries, z])) whitepoint = xy_to_XYZ(whitepoint) @@ -119,31 +133,46 @@ def normalised_primary_matrix(primaries, whitepoint): return npm -def chromatically_adapted_primaries(primaries, - whitepoint_t, - whitepoint_r, - chromatic_adaptation_transform='CAT02'): +def chromatically_adapted_primaries( + primaries: ArrayLike, + whitepoint_t: ArrayLike, + whitepoint_r: ArrayLike, + chromatic_adaptation_transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", +) -> NDArray: """ - Chromatically adapts given *primaries* :math:`xy` chromaticity coordinates + Chromatically adapt given *primaries* :math:`xy` chromaticity coordinates from test ``whitepoint_t`` to reference ``whitepoint_r``. Parameters ---------- - primaries : array_like, (3, 2) + primaries Primaries :math:`xy` chromaticity coordinates. - whitepoint_t : array_like + whitepoint_t Test illuminant / whitepoint :math:`xy` chromaticity coordinates. - whitepoint_r : array_like + whitepoint_r Reference illuminant / whitepoint :math:`xy` chromaticity coordinates. - chromatic_adaptation_transform : unicode, optional - **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', - 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02 Brill 2008', - 'Bianco 2010', 'Bianco PC 2010'}**, + chromatic_adaptation_transform *Chromatic adaptation* transform. Returns ------- - ndarray + :class:`numpy.ndarray` Chromatically adapted primaries :math:`xy` chromaticity coordinates. Examples @@ -163,27 +192,30 @@ def chromatically_adapted_primaries(primaries, primaries = np.reshape(primaries, (3, 2)) XYZ_a = chromatic_adaptation_VonKries( - xy_to_XYZ(primaries), xy_to_XYZ(whitepoint_t), xy_to_XYZ(whitepoint_r), - chromatic_adaptation_transform) + xy_to_XYZ(primaries), + xy_to_XYZ(whitepoint_t), + xy_to_XYZ(whitepoint_r), + chromatic_adaptation_transform, + ) P_a = XYZ_to_xyY(XYZ_a)[..., 0:2] return P_a -def primaries_whitepoint(npm): +def primaries_whitepoint(npm: ArrayLike) -> Tuple[NDArray, NDArray]: """ - Computes the *primaries* and *whitepoint* :math:`xy` chromaticity + Compute the *primaries* and *whitepoint* :math:`xy` chromaticity coordinates using given *Normalised Primary Matrix* (NPM). Parameters ---------- - npm : array_like, (3, 3) + npm *Normalised Primary Matrix*. Returns ------- - tuple + :class:`tuple` *Primaries* and *whitepoint* :math:`xy` chromaticity coordinates. References @@ -204,62 +236,64 @@ def primaries_whitepoint(npm): array([ 0.32168, 0.33767]) """ - npm = npm.reshape([3, 3]) + npm = np.reshape(npm, (3, 3)) primaries = XYZ_to_xy(np.transpose(np.dot(npm, np.identity(3)))) - whitepoint = np.squeeze(XYZ_to_xy(np.transpose(np.dot(npm, ones([3, 1]))))) + whitepoint = np.squeeze(XYZ_to_xy(np.transpose(np.dot(npm, ones((3, 1)))))) # TODO: Investigate if we return an ndarray here with primaries and # whitepoint stacked together. return primaries, whitepoint -def RGB_luminance_equation(primaries, whitepoint): +def RGB_luminance_equation(primaries: ArrayLike, whitepoint: ArrayLike) -> str: """ - Returns the *luminance equation* from given *primaries* and *whitepoint*. + Return the *luminance equation* from given *primaries* and *whitepoint*. Parameters ---------- - primaries : array_like, (3, 2) + primaries Primaries chromaticity coordinates. - whitepoint : array_like + whitepoint Illuminant / whitepoint chromaticity coordinates. Returns ------- - unicode + :class:`str` *Luminance* equation. Examples -------- >>> p = np.array([0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700]) >>> whitepoint = np.array([0.32168, 0.33767]) - >>> # Doctests skip for Python 2.x compatibility. - >>> RGB_luminance_equation(p, whitepoint) # doctest: +SKIP + >>> RGB_luminance_equation(p, whitepoint) # doctest: +ELLIPSIS 'Y = 0.3439664...(R) + 0.7281660...(G) + -0.0721325...(B)' """ - return 'Y = {0}(R) + {1}(G) + {2}(B)'.format( - *np.ravel(normalised_primary_matrix(primaries, whitepoint))[3:6]) + return "Y = {}(R) + {}(G) + {}(B)".format( + *np.ravel(normalised_primary_matrix(primaries, whitepoint))[3:6] + ) -def RGB_luminance(RGB, primaries, whitepoint): +def RGB_luminance( + RGB: ArrayLike, primaries: ArrayLike, whitepoint: ArrayLike +) -> FloatingOrNDArray: """ - Returns the *luminance* :math:`Y` of given *RGB* components from given + Return the *luminance* :math:`Y` of given *RGB* components from given *primaries* and *whitepoint*. Parameters ---------- - RGB : array_like + RGB *RGB* chromaticity coordinate matrix. - primaries : array_like, (3, 2) + primaries Primaries chromaticity coordinate matrix. - whitepoint : array_like + whitepoint Illuminant / whitepoint chromaticity coordinates. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Luminance* :math:`Y`. Examples @@ -272,6 +306,7 @@ def RGB_luminance(RGB, primaries, whitepoint): """ Y = np.sum( - normalised_primary_matrix(primaries, whitepoint)[1] * RGB, axis=-1) + normalised_primary_matrix(primaries, whitepoint)[1] * RGB, axis=-1 + ) - return as_numeric(Y) + return as_float(Y) diff --git a/colour/models/rgb/hanbury2003.py b/colour/models/rgb/hanbury2003.py new file mode 100644 index 0000000000..94fd2a2e43 --- /dev/null +++ b/colour/models/rgb/hanbury2003.py @@ -0,0 +1,166 @@ +""" +IHLS Colour Encoding +==================== + +Defines the :math:`IHLS` (Improved HLS) colourspace related transformations: + +- :func:`colour.RGB_to_IHLS` +- :func:`colour.IHLS_to_RGB` + +References +---------- +- :cite:`Hanbury2003` : Hanbury, A. (2003). A 3D-Polar Coordinate Colour + Representation Well Adapted to Image Analysis. In J. Bigun & T. Gustavsson + (Eds.), Image Analysis (pp. 804-811). Springer Berlin Heidelberg. + ISBN:978-3-540-45103-7 +""" + +from __future__ import annotations + +import numpy as np + +from colour.algebra import vector_dot +from colour.hints import ArrayLike, NDArray +from colour.utilities import ( + from_range_1, + to_domain_1, + tstack, + tsplit, + zeros, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "RGB_to_IHLS", + "IHLS_to_RGB", +] + +MATRIX_RGB_TO_YC_1_C_2: NDArray = np.array( + [ + [0.2126, 0.7152, 0.0722], + [1, -0.5, -0.5], + [0, -np.sqrt(3) / 2, np.sqrt(3) / 2], + ] +) +"""*RGB* colourspace to *YC_1C_2* colourspace matrix.""" + +MATRIX_YC_1_C_2_TO_RGB: NDArray = np.linalg.inv(MATRIX_RGB_TO_YC_1_C_2) +"""*YC_1C_2* colourspace to *RGB* colourspace matrix.""" + + +def RGB_to_IHLS(RGB: ArrayLike) -> NDArray: + """ + Convert from *RGB* colourspace to *IHLS* (Improved HLS) colourspace. + + Parameters + ---------- + RGB + *RGB* colourspace array. + + Returns + ------- + :class:`numpy.ndarray` + *HYS* colourspace array. + + Notes + ----- + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``RGB`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + +------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``HYS`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + References + ---------- + :cite:`Hanbury2003` + + Examples + -------- + >>> RGB = np.array([0.45595571, 0.03039702, 0.04087245]) + >>> RGB_to_IHLS(RGB) # doctest: +ELLIPSIS + array([ 6.2616051..., 0.1216271..., 0.4255586...]) + """ + + RGB = to_domain_1(RGB) + R, G, B = tsplit(RGB) + + Y, C_1, C_2 = tsplit(vector_dot(MATRIX_RGB_TO_YC_1_C_2, RGB)) + + C = np.sqrt(C_1**2 + C_2**2) + + acos_C_1_C_2 = zeros(C.shape) + acos_C_1_C_2[C != 0] = np.arccos(C_1[C != 0] / C[C != 0]) + H = np.where(C_2 <= 0, acos_C_1_C_2, (np.pi * 2) - acos_C_1_C_2) + + S = np.maximum(np.maximum(R, G), B) - np.minimum(np.minimum(R, G), B) + + HYS = tstack([H, Y, S]) + + return from_range_1(HYS) + + +def IHLS_to_RGB(HYS: ArrayLike) -> NDArray: + """ + Convert from *IHLS* (Improved HLS) colourspace to *RGB* colourspace. + + Parameters + ---------- + HYS + *IHLS* colourspace array. + + Returns + ------- + :class:`numpy.ndarray` + *RGB* colourspace array. + + Notes + ----- + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``HYS`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + +------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``RGB`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + References + ---------- + :cite:`Hanbury2003` + + Examples + -------- + >>> HYS = np.array([6.26160518, 0.12162712, 0.42555869]) + >>> IHLS_to_RGB(HYS) # doctest: +ELLIPSIS + array([ 0.4559557..., 0.0303970..., 0.0408724...]) + """ + + H, Y, S = tsplit(to_domain_1(HYS)) + + pi_3 = np.pi / 3 + + k = np.floor(H / (pi_3)) + H_s = H - k * (pi_3) + C = (np.sqrt(3) * S) / (2 * np.sin((2 * pi_3) - H_s)) + + C_1 = C * np.cos(H) + C_2 = -C * np.sin(H) + + RGB = vector_dot(MATRIX_YC_1_C_2_TO_RGB, tstack([Y, C_1, C_2])) + + return from_range_1(RGB) diff --git a/colour/models/rgb/ictcp.py b/colour/models/rgb/ictcp.py index dd0932a099..5204d87ee7 100644 --- a/colour/models/rgb/ictcp.py +++ b/colour/models/rgb/ictcp.py @@ -1,95 +1,183 @@ -# -*- coding: utf-8 -*- """ :math:`IC_TC_P` Colour Encoding =============================== Defines the :math:`IC_TC_P` colour encoding related transformations: -- :func:`colour.RGB_to_ICTCP` -- :func:`colour.ICTCP_to_RGB` +- :func:`colour.RGB_to_ICtCp` +- :func:`colour.ICtCp_to_RGB` +- :func:`colour.XYZ_to_ICtCp` +- :func:`colour.ICtCp_to_XYZ` References ---------- -- :cite:`Dolby2016a` : Dolby. (2016). WHAT IS ICTCP? - INTRODUCTION. +- :cite:`Dolby2016a` : Dolby. (2016). WHAT IS ICtCp? - INTRODUCTION. https://www.dolby.com/us/en/technologies/dolby-vision/ICtCp-white-paper.pdf +- :cite:`InternationalTelecommunicationUnion2018` : International + Telecommunication Union. (2018). Recommendation ITU-R BT.2100-2 - Image + parameter values for high dynamic range television for use in production + and international programme exchange. + https://www.itu.int/dms_pubrec/itu-r/rec/bt/\ +R-REC-BT.2100-2-201807-I!!PDF-E.pdf - :cite:`Lu2016c` : Lu, T., Pu, F., Yin, P., Chen, T., Husak, W., Pytlarz, J., Atkins, R., Froehlich, J., & Su, G.-M. (2016). ITP Colour Space and Its Compression Performance for High Dynamic Range and Wide Colour Gamut Video Distribution. ZTE Communications, 14(1), 32-38. """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.models.rgb.transfer_functions import (eotf_inverse_ST2084, - eotf_ST2084) -from colour.utilities import (domain_range_scale, vector_dot, from_range_1, - to_domain_1) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.algebra import vector_dot +from colour.colorimetry import CCS_ILLUMINANTS +from colour.hints import ArrayLike, Floating, Literal, NDArray, Union +from colour.models.rgb import RGB_COLOURSPACES, RGB_to_XYZ, XYZ_to_RGB +from colour.models.rgb.transfer_functions import ( + eotf_ST2084, + eotf_inverse_ST2084, + oetf_HLG_BT2100, + oetf_inverse_HLG_BT2100, +) +from colour.utilities import ( + as_float_array, + domain_range_scale, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'MATRIX_ICTCP_RGB_TO_LMS', 'MATRIX_ICTCP_LMS_TO_RGB', - 'MATRIX_ICTCP_LMS_P_TO_ICTCP', 'MATRIX_ICTCP_ICTCP_TO_LMS_P', - 'RGB_to_ICTCP', 'ICTCP_to_RGB' + "MATRIX_ICTCP_RGB_TO_LMS", + "MATRIX_ICTCP_LMS_TO_RGB", + "MATRIX_ICTCP_LMS_P_TO_ICTCP", + "MATRIX_ICTCP_ICTCP_TO_LMS_P", + "MATRIX_ICTCP_LMS_P_TO_ICTCP_HLG_BT2100_2", + "MATRIX_ICTCP_ICTCP_TO_LMS_P_HLG_BT2100_2", + "RGB_to_ICtCp", + "ICtCp_to_RGB", + "XYZ_to_ICtCp", + "ICtCp_to_XYZ", ] -MATRIX_ICTCP_RGB_TO_LMS = np.array([ - [1688, 2146, 262], - [683, 2951, 462], - [99, 309, 3688], -]) / 4096 -""" -*ITU-R BT.2020* colourspace to normalised cone responses matrix. - -MATRIX_ICTCP_RGB_TO_LMS : array_like, (3, 3) -""" - -MATRIX_ICTCP_LMS_TO_RGB = np.linalg.inv(MATRIX_ICTCP_RGB_TO_LMS) +MATRIX_ICTCP_RGB_TO_LMS: NDArray = ( + np.array( + [ + [1688, 2146, 262], + [683, 2951, 462], + [99, 309, 3688], + ] + ) + / 4096 +) +"""*ITU-R BT.2020* colourspace to normalised cone responses matrix.""" + +MATRIX_ICTCP_LMS_TO_RGB: NDArray = np.linalg.inv(MATRIX_ICTCP_RGB_TO_LMS) """ :math:`IC_TC_P` colourspace normalised cone responses to *ITU-R BT.2020* colourspace matrix. - -MATRIX_ICTCP_LMS_TO_RGB : array_like, (3, 3) """ -MATRIX_ICTCP_LMS_P_TO_ICTCP = np.array([ - [2048, 2048, 0], - [6610, -13613, 7003], - [17933, -17390, -543], -]) / 4096 +MATRIX_ICTCP_LMS_P_TO_ICTCP: NDArray = ( + np.array( + [ + [2048, 2048, 0], + [6610, -13613, 7003], + [17933, -17390, -543], + ] + ) + / 4096 +) """ :math:`LMS_p` *SMPTE ST 2084:2014* encoded normalised cone responses to :math:`IC_TC_P` colour encoding matrix. - -MATRIX_ICTCP_LMS_P_TO_ICTCP : array_like, (3, 3) """ -MATRIX_ICTCP_ICTCP_TO_LMS_P = np.linalg.inv(MATRIX_ICTCP_LMS_P_TO_ICTCP) +MATRIX_ICTCP_ICTCP_TO_LMS_P: NDArray = np.linalg.inv( + MATRIX_ICTCP_LMS_P_TO_ICTCP +) """ :math:`IC_TC_P` colour encoding to :math:`LMS_p` *SMPTE ST 2084:2014* encoded normalised cone responses matrix. +""" -MATRIX_ICTCP_ICTCP_TO_LMS_P : array_like, (3, 3) +MATRIX_ICTCP_LMS_P_TO_ICTCP_HLG_BT2100_2: NDArray = ( + np.array( + [ + [2048, 2048, 0], + [3625, -7465, 3840], + [9500, -9212, -288], + ] + ) + / 4096 +) +""" +:math:`LMS_p` *SMPTE ST 2084:2014* encoded normalised cone responses to +:math:`IC_TC_P` colour encoding matrix as given in *ITU-R BT.2100-2*. +""" + +MATRIX_ICTCP_ICTCP_TO_LMS_P_HLG_BT2100_2: NDArray = np.linalg.inv( + MATRIX_ICTCP_LMS_P_TO_ICTCP_HLG_BT2100_2 +) +""" +:math:`IC_TC_P` colour encoding to :math:`LMS_p` *SMPTE ST 2084:2014* encoded +normalised cone responses matrix as given in *ITU-R BT.2100-2*. """ -def RGB_to_ICTCP(RGB, L_p=10000): +def RGB_to_ICtCp( + RGB: ArrayLike, + method: Union[ + Literal[ + "Dolby 2016", + "ITU-R BT.2100-1 HLG", + "ITU-R BT.2100-1 PQ", + "ITU-R BT.2100-2 HLG", + "ITU-R BT.2100-2 PQ", + ], + str, + ] = "Dolby 2016", + L_p: Floating = 10000, +) -> NDArray: """ - Converts from *ITU-R BT.2020* colourspace to :math:`IC_TC_P` colour + Convert from *ITU-R BT.2020* colourspace to :math:`IC_TC_P` colour encoding. Parameters ---------- - RGB : array_like + RGB *ITU-R BT.2020* colourspace array. - L_p : numeric, optional + method + Computation method. *Recommendation ITU-R BT.2100* defines multiple + variants of the :math:`IC_TC_P` colour encoding: + + - *ITU-R BT.2100-1* + + - *SMPTE ST 2084:2014* inverse electro-optical transfer + function (EOTF) and the :math:`IC_TC_P` matrix from + :cite:`Dolby2016a`: *Dolby 2016*, *ITU-R BT.2100-1 PQ*, + *ITU-R BT.2100-2 PQ* methods. + - *Recommendation ITU-R BT.2100* *Reference HLG* opto-electrical + transfer function (OETF) and the :math:`IC_TC_P` matrix + from :cite:`Dolby2016a`: *ITU-R BT.2100-1 HLG* method. + + - *ITU-R BT.2100-2* + + - *SMPTE ST 2084:2014* inverse electro-optical transfer + function (EOTF) and the :math:`IC_TC_P` matrix from + :cite:`Dolby2016a`: *Dolby 2016*, *ITU-R BT.2100-1 PQ*, + *ITU-R BT.2100-2 PQ* methods. + - *Recommendation ITU-R BT.2100* *Reference HLG* opto-electrical + transfer function (OETF) and a custom :math:`IC_TC_P` + matrix from :cite:`InternationalTelecommunicationUnion2018`: + *ITU-R BT.2100-2 HLG* method. + + L_p Display peak luminance :math:`cd/m^2` for *SMPTE ST 2084:2014* non-linear encoding. This parameter should stay at its default :math:`10000 cd/m^2` value for practical applications. It is exposed so @@ -97,7 +185,7 @@ def RGB_to_ICTCP(RGB, L_p=10000): Returns ------- - ndarray + :class:`numpy.ndarray` :math:`IC_TC_P` colour encoding array. Warnings @@ -107,12 +195,13 @@ def RGB_to_ICTCP(RGB, L_p=10000): Notes ----- - + - The *ITU-R BT.2100-1 PQ* and *ITU-R BT.2100-2 PQ* methods are aliases + for the *Dolby 2016* method. - The underlying *SMPTE ST 2084:2014* transfer function is an absolute transfer function, thus the domain and range values for the *Reference* and *1* scales are only indicative that the data is not affected by scale transformations. The effective domain of *SMPTE ST 2084:2014* - inverse electro-optical transfer function (EOTF / EOCF) is + inverse electro-optical transfer function (EOTF) is [0.0001, 10000]. +------------+-----------------------+------------------+ @@ -124,7 +213,7 @@ def RGB_to_ICTCP(RGB, L_p=10000): +------------+-----------------------+------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ - | ``ICTCP`` | ``I`` : [0, 1] | ``I`` : [0, 1] | + | ``ICtCp`` | ``I`` : [0, 1] | ``I`` : [0, 1] | | | | | | | ``CT`` : [-1, 1] | ``CT`` : [-1, 1] | | | | | @@ -138,32 +227,93 @@ def RGB_to_ICTCP(RGB, L_p=10000): Examples -------- >>> RGB = np.array([0.45620519, 0.03081071, 0.04091952]) - >>> RGB_to_ICTCP(RGB) # doctest: +ELLIPSIS + >>> RGB_to_ICtCp(RGB) # doctest: +ELLIPSIS array([ 0.0735136..., 0.0047525..., 0.0935159...]) + >>> RGB_to_ICtCp(RGB, method='ITU-R BT.2100-2 HLG') # doctest: +ELLIPSIS + array([ 0.6256789..., -0.0198449..., 0.3591125...]) """ - RGB = to_domain_1(RGB) + RGB = as_float_array(RGB) + method = validate_method( + method, + [ + "Dolby 2016", + "ITU-R BT.2100-1 HLG", + "ITU-R BT.2100-1 PQ", + "ITU-R BT.2100-2 HLG", + "ITU-R BT.2100-2 PQ", + ], + ) + + is_hlg_method = "hlg" in method + is_BT2100_2_method = "2100-2" in method LMS = vector_dot(MATRIX_ICTCP_RGB_TO_LMS, RGB) - with domain_range_scale('ignore'): - LMS_p = eotf_inverse_ST2084(LMS, L_p) - - ICTCP = vector_dot(MATRIX_ICTCP_LMS_P_TO_ICTCP, LMS_p) - - return from_range_1(ICTCP) - - -def ICTCP_to_RGB(ICTCP, L_p=10000): + with domain_range_scale("ignore"): + LMS_p = ( + oetf_HLG_BT2100(LMS) + if is_hlg_method + else eotf_inverse_ST2084(LMS, L_p) + ) + + ICtCp = ( + vector_dot(MATRIX_ICTCP_LMS_P_TO_ICTCP_HLG_BT2100_2, LMS_p) + if (is_hlg_method and is_BT2100_2_method) + else vector_dot(MATRIX_ICTCP_LMS_P_TO_ICTCP, LMS_p) + ) + + return ICtCp + + +def ICtCp_to_RGB( + ICtCp: ArrayLike, + method: Union[ + Literal[ + "Dolby 2016", + "ITU-R BT.2100-1 HLG", + "ITU-R BT.2100-1 PQ", + "ITU-R BT.2100-2 HLG", + "ITU-R BT.2100-2 PQ", + ], + str, + ] = "Dolby 2016", + L_p: Floating = 10000, +) -> NDArray: """ - Converts from :math:`IC_TC_P` colour encoding to *ITU-R BT.2020* + Convert from :math:`IC_TC_P` colour encoding to *ITU-R BT.2020* colourspace. Parameters ---------- - ICTCP : array_like + ICtCp :math:`IC_TC_P` colour encoding array. - L_p : numeric, optional + method + Computation method. *Recommendation ITU-R BT.2100* defines multiple + variants of the :math:`IC_TC_P` colour encoding: + + - *ITU-R BT.2100-1* + + - *SMPTE ST 2084:2014* inverse electro-optical transfer + function (EOTF) and the :math:`IC_TC_P` matrix from + :cite:`Dolby2016a`: *Dolby 2016*, *ITU-R BT.2100-1 PQ*, + *ITU-R BT.2100-2 PQ* methods. + - *Recommendation ITU-R BT.2100* *Reference HLG* opto-electrical + transfer function (OETF) and the :math:`IC_TC_P` matrix + from :cite:`Dolby2016a`: *ITU-R BT.2100-1 HLG* method. + + - *ITU-R BT.2100-2* + + - *SMPTE ST 2084:2014* inverse electro-optical transfer + function (EOTF) and the :math:`IC_TC_P` matrix from + :cite:`Dolby2016a`: *Dolby 2016*, *ITU-R BT.2100-1 PQ*, + *ITU-R BT.2100-2 PQ* methods. + - *Recommendation ITU-R BT.2100* *Reference HLG* opto-electrical + transfer function (OETF) and a custom :math:`IC_TC_P` + matrix from :cite:`InternationalTelecommunicationUnion2018`: + *ITU-R BT.2100-2 HLG* method. + + L_p Display peak luminance :math:`cd/m^2` for *SMPTE ST 2084:2014* non-linear encoding. This parameter should stay at its default :math:`10000 cd/m^2` value for practical applications. It is exposed so @@ -171,7 +321,7 @@ def ICTCP_to_RGB(ICTCP, L_p=10000): Returns ------- - ndarray + :class:`numpy.ndarray` *ITU-R BT.2020* colourspace array. Warnings @@ -181,7 +331,8 @@ def ICTCP_to_RGB(ICTCP, L_p=10000): Notes ----- - + - The *ITU-R BT.2100-1 PQ* and *ITU-R BT.2100-2 PQ* methods are aliases + for the *Dolby 2016* method. - The underlying *SMPTE ST 2084:2014* transfer function is an absolute transfer function, thus the domain and range values for the *Reference* and *1* scales are only indicative that the data is not affected by @@ -190,7 +341,7 @@ def ICTCP_to_RGB(ICTCP, L_p=10000): +------------+-----------------------+------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ - | ``ICTCP`` | ``I`` : [0, 1] | ``I`` : [0, 1] | + | ``ICtCp`` | ``I`` : [0, 1] | ``I`` : [0, 1] | | | | | | | ``CT`` : [-1, 1] | ``CT`` : [-1, 1] | | | | | @@ -209,18 +360,319 @@ def ICTCP_to_RGB(ICTCP, L_p=10000): Examples -------- - >>> ICTCP = np.array([0.07351364, 0.00475253, 0.09351596]) - >>> ICTCP_to_RGB(ICTCP) # doctest: +ELLIPSIS + >>> ICtCp = np.array([0.07351364, 0.00475253, 0.09351596]) + >>> ICtCp_to_RGB(ICtCp) # doctest: +ELLIPSIS + array([ 0.4562052..., 0.0308107..., 0.0409195...]) + >>> ICtCp = np.array([0.62567899, -0.01984490, 0.35911259]) + >>> ICtCp_to_RGB(ICtCp, method='ITU-R BT.2100-2 HLG') # doctest: +ELLIPSIS array([ 0.4562052..., 0.0308107..., 0.0409195...]) """ - ICTCP = to_domain_1(ICTCP) + ICtCp = as_float_array(ICtCp) + method = validate_method( + method, + [ + "Dolby 2016", + "ITU-R BT.2100-1 HLG", + "ITU-R BT.2100-1 PQ", + "ITU-R BT.2100-2 HLG", + "ITU-R BT.2100-2 PQ", + ], + ) + + is_hlg_method = "hlg" in method + is_BT2100_2_method = "2100-2" in method + + LMS_p = ( + vector_dot(MATRIX_ICTCP_ICTCP_TO_LMS_P_HLG_BT2100_2, ICtCp) + if (is_hlg_method and is_BT2100_2_method) + else vector_dot(MATRIX_ICTCP_ICTCP_TO_LMS_P, ICtCp) + ) + + with domain_range_scale("ignore"): + LMS = ( + oetf_inverse_HLG_BT2100(LMS_p) + if is_hlg_method + else eotf_ST2084(LMS_p, L_p) + ) - LMS_p = vector_dot(MATRIX_ICTCP_ICTCP_TO_LMS_P, ICTCP) + RGB = vector_dot(MATRIX_ICTCP_LMS_TO_RGB, LMS) - with domain_range_scale('ignore'): - LMS = eotf_ST2084(LMS_p, L_p) + return RGB + + +def XYZ_to_ICtCp( + XYZ: ArrayLike, + illuminant=CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"], + chromatic_adaptation_transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", + method: Union[ + Literal[ + "Dolby 2016", + "ITU-R BT.2100-1 HLG", + "ITU-R BT.2100-1 PQ", + "ITU-R BT.2100-2 HLG", + "ITU-R BT.2100-2 PQ", + ], + str, + ] = "Dolby 2016", + L_p: Floating = 10000, +) -> NDArray: + """ + Convert from *CIE XYZ* tristimulus values to :math:`IC_TC_P` colour + encoding. - RGB = vector_dot(MATRIX_ICTCP_LMS_TO_RGB, LMS) + Parameters + ---------- + XYZ + *CIE XYZ* tristimulus values. + illuminant + Source illuminant chromaticity coordinates. + chromatic_adaptation_transform + *Chromatic adaptation* transform. + method + Computation method. *Recommendation ITU-R BT.2100* defines multiple + variants of the :math:`IC_TC_P` colour encoding: + + - *ITU-R BT.2100-1* + + - *SMPTE ST 2084:2014* inverse electro-optical transfer + function (EOTF) and the :math:`IC_TC_P` matrix from + :cite:`Dolby2016a`: *Dolby 2016*, *ITU-R BT.2100-1 PQ*, + *ITU-R BT.2100-2 PQ* methods. + - *Recommendation ITU-R BT.2100* *Reference HLG* opto-electrical + transfer function (OETF) and the :math:`IC_TC_P` matrix + from :cite:`Dolby2016a`: *ITU-R BT.2100-1 HLG* method. + + - *ITU-R BT.2100-2* + + - *SMPTE ST 2084:2014* inverse electro-optical transfer + function (EOTF) and the :math:`IC_TC_P` matrix from + :cite:`Dolby2016a`: *Dolby 2016*, *ITU-R BT.2100-1 PQ*, + *ITU-R BT.2100-2 PQ* methods. + - *Recommendation ITU-R BT.2100* *Reference HLG* opto-electrical + transfer function (OETF) and a custom :math:`IC_TC_P` + matrix from :cite:`InternationalTelecommunicationUnion2018`: + *ITU-R BT.2100-2 HLG* method. + + L_p + Display peak luminance :math:`cd/m^2` for *SMPTE ST 2084:2014* + non-linear encoding. This parameter should stay at its default + :math:`10000 cd/m^2` value for practical applications. It is exposed so + that the definition can be used as a fitting function. + + Returns + ------- + :class:`numpy.ndarray` + :math:`IC_TC_P` colour encoding array. + + Warnings + -------- + The underlying *SMPTE ST 2084:2014* transfer function is an absolute + transfer function. + + Notes + ----- + - The underlying *SMPTE ST 2084:2014* transfer function is an absolute + transfer function, thus the domain and range values for the *Reference* + - The *ITU-R BT.2100-1 PQ* and *ITU-R BT.2100-2 PQ* methods are aliases + for the *Dolby 2016* method. + and *1* scales are only indicative that the data is not affected by + scale transformations. The effective domain of *SMPTE ST 2084:2014* + inverse electro-optical transfer function (EOTF) is + [0.0001, 10000]. + + +------------+-----------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+==================+ + | ``XYZ`` | ``UN`` | ``UN`` | + +------------+-----------------------+------------------+ + + +------------+-----------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+==================+ + | ``ICtCp`` | ``I`` : [0, 1] | ``I`` : [0, 1] | + | | | | + | | ``CT`` : [-1, 1] | ``CT`` : [-1, 1] | + | | | | + | | ``CP`` : [-1, 1] | ``CP`` : [-1, 1] | + +------------+-----------------------+------------------+ + + References + ---------- + :cite:`Dolby2016a`, :cite:`Lu2016c` + + Examples + -------- + >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + >>> XYZ_to_ICtCp(XYZ) # doctest: +ELLIPSIS + array([ 0.0685809..., -0.0028384..., 0.0602098...]) + >>> XYZ_to_ICtCp(XYZ, method='ITU-R BT.2100-2 HLG') # doctest: +ELLIPSIS + array([ 0.5924279..., -0.0374073..., 0.2512267...]) + """ + + BT2020 = RGB_COLOURSPACES["ITU-R BT.2020"] + + RGB = XYZ_to_RGB( + XYZ, + illuminant, + BT2020.whitepoint, + BT2020.matrix_XYZ_to_RGB, + chromatic_adaptation_transform, + ) + + return RGB_to_ICtCp(RGB, method, L_p) + + +def ICtCp_to_XYZ( + ICtCp: ArrayLike, + illuminant=CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"], + chromatic_adaptation_transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", + method: Union[ + Literal[ + "Dolby 2016", + "ITU-R BT.2100-1 HLG", + "ITU-R BT.2100-1 PQ", + "ITU-R BT.2100-2 HLG", + "ITU-R BT.2100-2 PQ", + ], + str, + ] = "Dolby 2016", + L_p: Floating = 10000, +) -> NDArray: + """ + Convert from :math:`IC_TC_P` colour encoding to *CIE XYZ* tristimulus + values. + + Parameters + ---------- + ICtCp + :math:`IC_TC_P` colour encoding array. + illuminant + Source illuminant chromaticity coordinates. + chromatic_adaptation_transform + *Chromatic adaptation* transform. + method + Computation method. *Recommendation ITU-R BT.2100* defines multiple + variants of the :math:`IC_TC_P` colour encoding: + + - *ITU-R BT.2100-1* + + - *SMPTE ST 2084:2014* inverse electro-optical transfer + function (EOTF) and the :math:`IC_TC_P` matrix from + :cite:`Dolby2016a`: *Dolby 2016*, *ITU-R BT.2100-1 PQ*, + *ITU-R BT.2100-2 PQ* methods. + - *Recommendation ITU-R BT.2100* *Reference HLG* opto-electrical + transfer function (OETF) and the :math:`IC_TC_P` matrix + from :cite:`Dolby2016a`: *ITU-R BT.2100-1 HLG* method. + + - *ITU-R BT.2100-2* + + - *SMPTE ST 2084:2014* inverse electro-optical transfer + function (EOTF) and the :math:`IC_TC_P` matrix from + :cite:`Dolby2016a`: *Dolby 2016*, *ITU-R BT.2100-1 PQ*, + *ITU-R BT.2100-2 PQ* methods. + - *Recommendation ITU-R BT.2100* *Reference HLG* opto-electrical + transfer function (OETF) and a custom :math:`IC_TC_P` + matrix from :cite:`InternationalTelecommunicationUnion2018`: + *ITU-R BT.2100-2 HLG* method. + + L_p + Display peak luminance :math:`cd/m^2` for *SMPTE ST 2084:2014* + non-linear encoding. This parameter should stay at its default + :math:`10000 cd/m^2` value for practical applications. It is exposed so + that the definition can be used as a fitting function. + + Returns + ------- + :class:`numpy.ndarray` + *CIE XYZ* tristimulus values. + + Warnings + -------- + The underlying *SMPTE ST 2084:2014* transfer function is an absolute + transfer function. + + Notes + ----- + - The *ITU-R BT.2100-1 PQ* and *ITU-R BT.2100-2 PQ* methods are aliases + for the *Dolby 2016* method. + - The underlying *SMPTE ST 2084:2014* transfer function is an absolute + transfer function, thus the domain and range values for the *Reference* + and *1* scales are only indicative that the data is not affected by + scale transformations. + + +------------+-----------------------+------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+==================+ + | ``ICtCp`` | ``I`` : [0, 1] | ``I`` : [0, 1] | + | | | | + | | ``CT`` : [-1, 1] | ``CT`` : [-1, 1] | + | | | | + | | ``CP`` : [-1, 1] | ``CP`` : [-1, 1] | + +------------+-----------------------+------------------+ + + +------------+-----------------------+------------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+==================+ + | ``XYZ`` | ``UN`` | ``UN`` | + +------------+-----------------------+------------------+ + + References + ---------- + :cite:`Dolby2016a`, :cite:`Lu2016c` + + Examples + -------- + >>> ICtCp = np.array([0.06858097, -0.00283842, 0.06020983]) + >>> ICtCp_to_XYZ(ICtCp) # doctest: +ELLIPSIS + array([ 0.2065400..., 0.1219722..., 0.0513695...]) + >>> ICtCp = np.array([0.59242792, -0.03740730, 0.25122675]) + >>> ICtCp_to_XYZ(ICtCp, method='ITU-R BT.2100-2 HLG') # doctest: +ELLIPSIS + array([ 0.2065400..., 0.1219722..., 0.0513695...]) + """ + + RGB = ICtCp_to_RGB(ICtCp, method, L_p) + + BT2020 = RGB_COLOURSPACES["ITU-R BT.2020"] + + XYZ = RGB_to_XYZ( + RGB, + BT2020.whitepoint, + illuminant, + BT2020.matrix_RGB_to_XYZ, + chromatic_adaptation_transform, + ) - return from_range_1(RGB) + return XYZ diff --git a/colour/models/rgb/prismatic.py b/colour/models/rgb/prismatic.py index 83d2899d23..484c4e05eb 100644 --- a/colour/models/rgb/prismatic.py +++ b/colour/models/rgb/prismatic.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Prismatic Colourspace ===================== @@ -14,40 +13,43 @@ space for rgb computations (pp. 2-7). """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +from colour.hints import ArrayLike, NDArray from colour.utilities import from_range_1, to_domain_1, tsplit, tstack -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['RGB_to_Prismatic', 'Prismatic_to_RGB'] +__all__ = [ + "RGB_to_Prismatic", + "Prismatic_to_RGB", +] -def RGB_to_Prismatic(RGB): +def RGB_to_Prismatic(RGB: ArrayLike) -> NDArray: """ - Converts from *RGB* colourspace to *Prismatic* :math:`L\\rho\\gamma\\beta` + Convert from *RGB* colourspace to *Prismatic* :math:`L\\rho\\gamma\\beta` colourspace array. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *Prismatic* :math:`L\\rho\\gamma\\beta` colourspace array. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -92,24 +94,23 @@ def RGB_to_Prismatic(RGB): return from_range_1(Lrgb) -def Prismatic_to_RGB(Lrgb): +def Prismatic_to_RGB(Lrgb: ArrayLike) -> NDArray: """ - Converts from *Prismatic* :math:`L\\rho\\gamma\\beta` colourspace array to + Convert from *Prismatic* :math:`L\\rho\\gamma\\beta` colourspace array to *RGB* colourspace. Parameters ---------- - Lrgb : array_like + Lrgb *Prismatic* :math:`L\\rho\\gamma\\beta` colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *RGB* colourspace array. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ diff --git a/colour/models/rgb/rgb_colourspace.py b/colour/models/rgb/rgb_colourspace.py index 1877eaa3e7..2d93a646e0 100644 --- a/colour/models/rgb/rgb_colourspace.py +++ b/colour/models/rgb/rgb_colourspace.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ RGB Colourspace and Transformations =================================== @@ -24,37 +23,59 @@ http://pro-av.panasonic.net/en/varicam/common/pdf/VARICAM_V-Log_V-Gamut.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from copy import deepcopy -from colour.models import xy_to_XYZ, xy_to_xyY, xyY_to_XYZ -from colour.models.rgb import (chromatically_adapted_primaries, - normalised_primary_matrix) from colour.adaptation import matrix_chromatic_adaptation_VonKries -from colour.utilities import (as_float_array, domain_range_scale, matrix_dot, - vector_dot, filter_kwargs, from_range_1, - to_domain_1, is_string, usage_warning) -from colour.utilities.deprecation import (ObjectRenamed, - handle_arguments_deprecation) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.algebra import matrix_dot, vector_dot +from colour.hints import ( + Any, + ArrayLike, + Boolean, + Callable, + Literal, + NDArray, + Optional, + Union, + cast, +) +from colour.models import xy_to_XYZ, xy_to_xyY, xyY_to_XYZ +from colour.models.rgb import ( + chromatically_adapted_primaries, + normalised_primary_matrix, +) +from colour.utilities import ( + as_float_array, + attest, + domain_range_scale, + filter_kwargs, + from_range_1, + optional, + to_domain_1, + is_string, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'RGB_Colourspace', 'XYZ_to_RGB', 'RGB_to_XYZ', 'matrix_RGB_to_RGB', - 'RGB_to_RGB' + "RGB_Colourspace", + "XYZ_to_RGB", + "RGB_to_XYZ", + "matrix_RGB_to_RGB", + "RGB_to_RGB", ] -class RGB_Colourspace(object): +class RGB_Colourspace: """ - Implements support for the *RGB* colourspaces datasets from + Implement support for the *RGB* colourspaces datasets from :mod:`colour.models.datasets.aces_rgb`, etc.... Colour science literature related to *RGB* colourspaces and encodings @@ -107,40 +128,35 @@ class RGB_Colourspace(object): Parameters ---------- - name : unicode + name *RGB* colourspace name. - primaries : array_like + primaries *RGB* colourspace primaries. - whitepoint : array_like + whitepoint *RGB* colourspace whitepoint. - whitepoint_name : unicode, optional + whitepoint_name *RGB* colourspace whitepoint name. - matrix_RGB_to_XYZ : array_like, optional + matrix_RGB_to_XYZ Transformation matrix from colourspace to *CIE XYZ* tristimulus values. - matrix_XYZ_to_RGB : array_like, optional + matrix_XYZ_to_RGB Transformation matrix from *CIE XYZ* tristimulus values to colourspace. - cctf_encoding : object, optional + cctf_encoding Encoding colour component transfer function (Encoding CCTF) / - opto-electronic transfer function (OETF / OECF) that maps estimated + opto-electronic transfer function (OETF) that maps estimated tristimulus values in a scene to :math:`R'G'B'` video component signal value. - cctf_decoding : object, optional + cctf_decoding Decoding colour component transfer function (Decoding CCTF) / - electro-optical transfer function (EOTF / EOCF) that maps an + electro-optical transfer function (EOTF) that maps an :math:`R'G'B'` video component signal value to tristimulus values at the display. - use_derived_matrix_RGB_to_XYZ : bool, optional + use_derived_matrix_RGB_to_XYZ Whether to use the instantiation time normalised primary matrix or to use a computed derived normalised primary matrix. - use_derived_matrix_XYZ_to_RGB : bool, optional + use_derived_matrix_XYZ_to_RGB Whether to use the instantiation time inverse normalised primary matrix or to use a computed derived inverse normalised primary matrix. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Attributes ---------- - :attr:`~colour.RGB_Colourspace.name` @@ -166,7 +182,7 @@ class RGB_Colourspace(object): Notes ----- - The normalised primary matrix defined by - :attr:`colour.RGB_Colourspace.matrix_RGB_to_XYZ` attribute is treated + :attr:`colour.RGB_Colourspace.matrix_RGB_to_XYZ` property is treated as the prime matrix from which the inverse will be calculated as required by the internal derivation mechanism. This behaviour has been chosen in accordance with literature where commonly a *RGB* colourspace @@ -195,7 +211,6 @@ class RGB_Colourspace(object): [ 0., 1., 0.], [ 0., 0., 1.]]) >>> colourspace.use_derived_transformation_matrices(True) - True >>> colourspace.matrix_RGB_to_XYZ # doctest: +ELLIPSIS array([[ 9.5255239...e-01, 0.0000000...e+00, 9.3678631...e-05], [ 3.4396645...e-01, 7.2816609...e-01, -7.2132546...e-02], @@ -216,321 +231,326 @@ class RGB_Colourspace(object): [ 0., 0., 1.]]) """ - def __init__(self, - name, - primaries, - whitepoint, - whitepoint_name=None, - matrix_RGB_to_XYZ=None, - matrix_XYZ_to_RGB=None, - cctf_encoding=None, - cctf_decoding=None, - use_derived_matrix_RGB_to_XYZ=False, - use_derived_matrix_XYZ_to_RGB=False, - **kwargs): - self._derived_matrix_RGB_to_XYZ = None - self._derived_matrix_XYZ_to_RGB = None - - use_derived_matrix_RGB_to_XYZ = handle_arguments_deprecation({ - 'ArgumentRenamed': [[ - 'use_derived_RGB_to_XYZ_matrix', - 'use_derived_matrix_RGB_to_XYZ' - ]], - }, **kwargs).get('use_derived_matrix_RGB_to_XYZ', - use_derived_matrix_RGB_to_XYZ) - - use_derived_matrix_XYZ_to_RGB = handle_arguments_deprecation({ - 'ArgumentRenamed': [[ - 'use_derived_XYZ_to_RGB_matrix', - 'use_derived_matrix_XYZ_to_RGB' - ]], - }, **kwargs).get('use_derived_matrix_XYZ_to_RGB', - use_derived_matrix_XYZ_to_RGB) - - self._name = None + def __init__( + self, + name: str, + primaries: ArrayLike, + whitepoint: ArrayLike, + whitepoint_name: Optional[str] = None, + matrix_RGB_to_XYZ: Optional[ArrayLike] = None, + matrix_XYZ_to_RGB: Optional[ArrayLike] = None, + cctf_encoding: Optional[Callable] = None, + cctf_decoding: Optional[Callable] = None, + use_derived_matrix_RGB_to_XYZ: Boolean = False, + use_derived_matrix_XYZ_to_RGB: Boolean = False, + ): + self._derived_matrix_RGB_to_XYZ: NDArray = np.array([]) + self._derived_matrix_XYZ_to_RGB: NDArray = np.array([]) + + self._name: str = f"{self.__class__.__name__} ({id(self)})" self.name = name - self._primaries = None - self.primaries = primaries - self._whitepoint = None - self.whitepoint = whitepoint - self._whitepoint_name = None + self._primaries: NDArray = np.array([]) + self.primaries = primaries # type: ignore[assignment] + self._whitepoint: NDArray = np.array([]) + self.whitepoint = whitepoint # type: ignore[assignment] + self._whitepoint_name: Optional[str] = None self.whitepoint_name = whitepoint_name - self._matrix_RGB_to_XYZ = None - self.matrix_RGB_to_XYZ = matrix_RGB_to_XYZ - self._matrix_XYZ_to_RGB = None - self.matrix_XYZ_to_RGB = matrix_XYZ_to_RGB - self._cctf_encoding = None + self._matrix_RGB_to_XYZ: Optional[NDArray] = None + self.matrix_RGB_to_XYZ = matrix_RGB_to_XYZ # type: ignore[assignment] + self._matrix_XYZ_to_RGB: Optional[NDArray] = None + self.matrix_XYZ_to_RGB = matrix_XYZ_to_RGB # type: ignore[assignment] + self._cctf_encoding: Optional[Callable] = None self.cctf_encoding = cctf_encoding - self._cctf_decoding = None + self._cctf_decoding: Optional[Callable] = None self.cctf_decoding = cctf_decoding - self._use_derived_matrix_RGB_to_XYZ = False + self._use_derived_matrix_RGB_to_XYZ: Boolean = False self.use_derived_matrix_RGB_to_XYZ = use_derived_matrix_RGB_to_XYZ - self._use_derived_matrix_XYZ_to_RGB = False + self._use_derived_matrix_XYZ_to_RGB: Boolean = False self.use_derived_matrix_XYZ_to_RGB = use_derived_matrix_XYZ_to_RGB @property - def name(self): + def name(self) -> str: """ Getter and setter property for the name. Parameters ---------- - value : unicode + value Value to set the name with. Returns ------- - unicode + :class:`str` *RGB* colourspace name. """ return self._name @name.setter - def name(self, value): - """ - Setter for the **self.name** property. - """ + def name(self, value: str): + """Setter for the **self.name** property.""" + + attest( + is_string(value), + f'"name" property: "{value}" type is not "str"!', + ) - if value is not None: - assert is_string(value), ( - '"{0}" attribute: "{1}" is not a "string" like object!'.format( - 'name', value)) self._name = value @property - def primaries(self): + def primaries(self) -> NDArray: """ Getter and setter property for the primaries. Parameters ---------- - value : array_like + value Value to set the primaries with. Returns ------- - array_like + :class:`numpy.ndarray` *RGB* colourspace primaries. """ return self._primaries @primaries.setter - def primaries(self, value): - """ - Setter for the **self.primaries** property. - """ + def primaries(self, value: ArrayLike): + """Setter for the **self.primaries** property.""" + + attest( + isinstance(value, (tuple, list, np.ndarray, np.matrix)), + f'"matrix_XYZ_to_RGB" property: "{value!r}" is not a "tuple", ' + f'"list", "ndarray" or "matrix" instance!', + ) + + value = as_float_array(value) + + value = np.reshape(value, (3, 2)) - if value is not None: - value = np.reshape(value, (3, 2)) self._primaries = value self._derive_transformation_matrices() @property - def whitepoint(self): + def whitepoint(self) -> NDArray: """ Getter and setter property for the whitepoint. Parameters ---------- - value : array_like + value Value to set the whitepoint with. Returns ------- - array_like + :class:`numpy.ndarray` *RGB* colourspace whitepoint. """ return self._whitepoint @whitepoint.setter - def whitepoint(self, value): - """ - Setter for the **self.whitepoint** property. - """ + def whitepoint(self, value: ArrayLike): + """Setter for the **self.whitepoint** property.""" + + attest( + isinstance(value, (tuple, list, np.ndarray, np.matrix)), + f'"matrix_XYZ_to_RGB" property: "{value!r}" is not a "tuple", ' + f'"list", "ndarray" or "matrix" instance!', + ) + + value = as_float_array(value) - if value is not None: - assert isinstance(value, (tuple, list, np.ndarray, np.matrix)), ( - '"{0}" attribute: "{1}" is not a "tuple", "list", "ndarray" ' - 'or "matrix" instance!'.format('whitepoint', value)) - value = as_float_array(value) self._whitepoint = value self._derive_transformation_matrices() @property - def whitepoint_name(self): + def whitepoint_name(self) -> Optional[str]: """ Getter and setter property for the whitepoint_name. Parameters ---------- - value : unicode + value Value to set the whitepoint_name with. Returns ------- - unicode + :py:data:`None` or :class:`str` *RGB* colourspace whitepoint name. """ return self._whitepoint_name @whitepoint_name.setter - def whitepoint_name(self, value): - """ - Setter for the **self.whitepoint_name** property. - """ + def whitepoint_name(self, value: Optional[str]): + """Setter for the **self.whitepoint_name** property.""" if value is not None: - assert is_string(value), ( - '"{0}" attribute: "{1}" is not a "string" like object!'.format( - 'whitepoint_name', value)) + attest( + is_string(value), + f'"whitepoint_name" property: "{value}" type is not "str"!', + ) + self._whitepoint_name = value @property - def matrix_RGB_to_XYZ(self): + def matrix_RGB_to_XYZ(self) -> NDArray: """ Getter and setter property for the transformation matrix from colourspace to *CIE XYZ* tristimulus values. Parameters ---------- - value : array_like + value Transformation matrix from colourspace to *CIE XYZ* tristimulus values. Returns ------- - array_like + :class:`numpy.ndarray` Transformation matrix from colourspace to *CIE XYZ* tristimulus values. """ - if not self._use_derived_matrix_RGB_to_XYZ: - return self._matrix_RGB_to_XYZ - else: + if ( + self._matrix_RGB_to_XYZ is None + or self._use_derived_matrix_RGB_to_XYZ + ): return self._derived_matrix_RGB_to_XYZ + else: + return self._matrix_RGB_to_XYZ @matrix_RGB_to_XYZ.setter - def matrix_RGB_to_XYZ(self, value): - """ - Setter for the **self.matrix_RGB_to_XYZ** property. - """ + def matrix_RGB_to_XYZ(self, value: Optional[ArrayLike]): + """Setter for the **self.matrix_RGB_to_XYZ** property.""" if value is not None: + attest( + isinstance(value, (tuple, list, np.ndarray, np.matrix)), + f'"matrix_RGB_to_XYZ" property: "{value!r}" is not a "tuple", ' + f'"list", "ndarray" or "matrix" instance!', + ) + value = as_float_array(value) + self._matrix_RGB_to_XYZ = value @property - def matrix_XYZ_to_RGB(self): + def matrix_XYZ_to_RGB(self) -> NDArray: """ Getter and setter property for the transformation matrix from *CIE XYZ* tristimulus values to colourspace. Parameters ---------- - value : array_like + value Transformation matrix from *CIE XYZ* tristimulus values to colourspace. Returns ------- - array_like + :class:`numpy.ndarray` Transformation matrix from *CIE XYZ* tristimulus values to colourspace. """ - if not self._use_derived_matrix_XYZ_to_RGB: - return self._matrix_XYZ_to_RGB - else: + if ( + self._matrix_XYZ_to_RGB is None + or self._use_derived_matrix_XYZ_to_RGB + ): return self._derived_matrix_XYZ_to_RGB + else: + return self._matrix_XYZ_to_RGB @matrix_XYZ_to_RGB.setter - def matrix_XYZ_to_RGB(self, value): - """ - Setter for the **self.matrix_XYZ_to_RGB** property. - """ + def matrix_XYZ_to_RGB(self, value: Optional[ArrayLike]): + """Setter for the **self.matrix_XYZ_to_RGB** property.""" if value is not None: + attest( + isinstance(value, (tuple, list, np.ndarray, np.matrix)), + f'"matrix_XYZ_to_RGB" property: "{value!r}" is not a "tuple", ' + f'"list", "ndarray" or "matrix" instance!', + ) + value = as_float_array(value) + self._matrix_XYZ_to_RGB = value @property - def cctf_encoding(self): + def cctf_encoding(self) -> Optional[Callable]: """ Getter and setter property for the encoding colour component transfer function (Encoding CCTF) / opto-electronic transfer function - (OETF / OECF). + (OETF). Parameters ---------- - value : callable + value Encoding colour component transfer function (Encoding CCTF) / - opto-electronic transfer function (OETF / OECF). + opto-electronic transfer function (OETF). Returns ------- - callable + :py:data:`None` or Callable Encoding colour component transfer function (Encoding CCTF) / - opto-electronic transfer function (OETF / OECF). + opto-electronic transfer function (OETF). """ return self._cctf_encoding @cctf_encoding.setter - def cctf_encoding(self, value): - """ - Setter for the **self.cctf_encoding** property. - """ + def cctf_encoding(self, value: Optional[Callable]): + """Setter for the **self.cctf_encoding** property.""" if value is not None: - assert hasattr( - value, - '__call__'), ('"{0}" attribute: "{1}" is not callable!'.format( - 'cctf_encoding', value)) + attest( + hasattr(value, "__call__"), + f'"cctf_encoding" property: "{value}" is not callable!', + ) + self._cctf_encoding = value @property - def cctf_decoding(self): + def cctf_decoding(self) -> Optional[Callable]: """ Getter and setter property for the decoding colour component transfer function (Decoding CCTF) / electro-optical transfer function - (EOTF / EOCF). + (EOTF). Parameters ---------- - value : callable + value Decoding colour component transfer function (Decoding CCTF) / - electro-optical transfer function (EOTF / EOCF). + electro-optical transfer function (EOTF). Returns ------- - callable + :py:data:`None` or Callable Decoding colour component transfer function (Decoding CCTF) / - electro-optical transfer function (EOTF / EOCF). + electro-optical transfer function (EOTF). """ return self._cctf_decoding @cctf_decoding.setter - def cctf_decoding(self, value): - """ - Setter for the **self.cctf_decoding** property. - """ + def cctf_decoding(self, value: Optional[Callable]): + """Setter for the **self.cctf_decoding** property.""" if value is not None: - assert hasattr( - value, - '__call__'), ('"{0}" attribute: "{1}" is not callable!'.format( - 'cctf_decoding', value)) + attest( + hasattr(value, "__call__"), + f'"cctf_decoding" property: "{value}" is not callable!', + ) + self._cctf_decoding = value @property - def use_derived_matrix_RGB_to_XYZ(self): + def use_derived_matrix_RGB_to_XYZ(self) -> Boolean: """ Getter and setter property for whether to use the instantiation time normalised primary matrix or to use a computed derived normalised @@ -538,13 +558,13 @@ def use_derived_matrix_RGB_to_XYZ(self): Parameters ---------- - value : bool + value Whether to use the instantiation time normalised primary matrix or to use a computed derived normalised primary matrix. Returns ------- - bool + :class:`bool` Whether to use the instantiation time normalised primary matrix or to use a computed derived normalised primary matrix. """ @@ -552,16 +572,19 @@ def use_derived_matrix_RGB_to_XYZ(self): return self._use_derived_matrix_RGB_to_XYZ @use_derived_matrix_RGB_to_XYZ.setter - def use_derived_matrix_RGB_to_XYZ(self, value): - """ - Setter for the **self.use_derived_matrix_RGB_to_XYZ** property. - """ + def use_derived_matrix_RGB_to_XYZ(self, value: Boolean): + """Setter for the **self.use_derived_matrix_RGB_to_XYZ** property.""" + + attest( + isinstance(value, (bool, np.bool_)), + f'"use_derived_matrix_RGB_to_XYZ" property: "{value}" is not a ' + f'"bool"!', + ) - # TODO: Revisit for potential behaviour / type checking. self._use_derived_matrix_RGB_to_XYZ = value @property - def use_derived_matrix_XYZ_to_RGB(self): + def use_derived_matrix_XYZ_to_RGB(self) -> Boolean: """ Getter and setter property for Whether to use the instantiation time inverse normalised primary matrix or to use a computed derived inverse @@ -569,14 +592,14 @@ def use_derived_matrix_XYZ_to_RGB(self): Parameters ---------- - value : bool + value Whether to use the instantiation time inverse normalised primary matrix or to use a computed derived inverse normalised primary matrix. Returns ------- - bool + :class:`bool` Whether to use the instantiation time inverse normalised primary matrix or to use a computed derived inverse normalised primary matrix. @@ -585,21 +608,24 @@ def use_derived_matrix_XYZ_to_RGB(self): return self._use_derived_matrix_XYZ_to_RGB @use_derived_matrix_XYZ_to_RGB.setter - def use_derived_matrix_XYZ_to_RGB(self, value): - """ - Setter for the **self.use_derived_matrix_XYZ_to_RGB** property. - """ + def use_derived_matrix_XYZ_to_RGB(self, value: Boolean): + """Setter for the **self.use_derived_matrix_XYZ_to_RGB** property.""" + + attest( + isinstance(value, (bool, np.bool_)), + f'"use_derived_matrix_XYZ_to_RGB" property: "{value}" is not a ' + f'"bool"!', + ) - # TODO: Revisit for potential behaviour / type checking. self._use_derived_matrix_XYZ_to_RGB = value - def __str__(self): + def __str__(self) -> str: """ - Returns a formatted string representation of the *RGB* colourspace. + Return a formatted string representation of the *RGB* colourspace. Returns ------- - unicode + :class:`str` Formatted string representation. Examples @@ -647,49 +673,35 @@ def __str__(self): Use Derived NPM -1 : False """ - def _indent_array(a): - """ - Indents given array string representation. - """ - - return str(a).replace(' [', ' ' * 22 + '[') - - return ('{0}\n' - '{1}\n\n' - 'Primaries : {2}\n' - 'Whitepoint : {3}\n' - 'Whitepoint Name : {4}\n' - 'Encoding CCTF : {5}\n' - 'Decoding CCTF : {6}\n' - 'NPM : {7}\n' - 'NPM -1 : {8}\n' - 'Derived NPM : {9}\n' - 'Derived NPM -1 : {10}\n' - 'Use Derived NPM : {11}\n' - 'Use Derived NPM -1 : {12}').format( - self.name, - '-' * len(self.name), - _indent_array(self.primaries), - self.whitepoint, - self.whitepoint_name, - self.cctf_encoding, - self.cctf_decoding, - _indent_array(self._matrix_RGB_to_XYZ), - _indent_array(self._matrix_XYZ_to_RGB), - _indent_array(self._derived_matrix_RGB_to_XYZ), - _indent_array(self._derived_matrix_XYZ_to_RGB), - self.use_derived_matrix_RGB_to_XYZ, - self.use_derived_matrix_XYZ_to_RGB, - ) - - def __repr__(self): + def _indent_array(a: Optional[NDArray]) -> str: + """Indent given array string representation.""" + + return str(a).replace(" [", " " * 22 + "[") + + return ( + f"{self.name}\n" + f'{"-" * len(self.name)}\n\n' + f"Primaries : {_indent_array(self.primaries)}\n" + f"Whitepoint : {self.whitepoint}\n" + f"Whitepoint Name : {self.whitepoint_name}\n" + f"Encoding CCTF : {self.cctf_encoding}\n" + f"Decoding CCTF : {self.cctf_decoding}\n" + f"NPM : {_indent_array(self._matrix_RGB_to_XYZ)}\n" + f"NPM -1 : {_indent_array(self._matrix_XYZ_to_RGB)}\n" + f"Derived NPM : {_indent_array(self._derived_matrix_RGB_to_XYZ)}\n" + f"Derived NPM -1 : {_indent_array(self._derived_matrix_XYZ_to_RGB)}\n" + f"Use Derived NPM : {self.use_derived_matrix_RGB_to_XYZ}\n" + f"Use Derived NPM -1 : {self.use_derived_matrix_XYZ_to_RGB}" + ) + + def __repr__(self) -> str: """ - Returns an (almost) evaluable string representation of the *RGB* + Return an (almost) evaluable string representation of the *RGB* colourspace. Returns ------- - unicode + :class`str` (Almost) evaluable string representation. Examples @@ -723,99 +735,98 @@ def __repr__(self): False) """ - def _indent_array(a): - """ - Indents given array evaluable string representation. - """ - - representation = repr(a).replace(' [', '{0}['.format(' ' * 11)) - representation = representation.replace('array(', ' ' * 16) - return representation.replace(')', '') - - return ('RGB_Colourspace({0},\n' - '{2},\n' - '{3},\n' - '{1}{4},\n' - '{5},\n' - '{6},\n' - '{1}{7},\n' - '{1}{8},\n' - '{1}{9},\n' - '{1}{10})').format( - self.name, - ' ' * 16, - _indent_array(self.primaries), - _indent_array(self.whitepoint), - self.whitepoint_name, - _indent_array(self.matrix_RGB_to_XYZ), - _indent_array(self.matrix_XYZ_to_RGB), - self.cctf_encoding, - self.cctf_decoding, - self.use_derived_matrix_RGB_to_XYZ, - self.use_derived_matrix_XYZ_to_RGB, - ) + def _indent_array(a: Optional[NDArray]) -> str: + """Indent given array evaluable string representation.""" + + representation = repr(a).replace(" [", f"{' ' * 11}[") + representation = representation.replace("array(", " " * 16) + return representation.replace(")", "") + + indentation = " " * 16 + return ( + f"RGB_Colourspace({self.name},\n" + f"{_indent_array(self.primaries)},\n" + f"{_indent_array(self.whitepoint)},\n" + f"{indentation}{self.whitepoint_name},\n" + f"{_indent_array(self.matrix_RGB_to_XYZ)},\n" + f"{_indent_array(self.matrix_XYZ_to_RGB)},\n" + f"{indentation}{self.cctf_encoding},\n" + f"{indentation}{self.cctf_decoding},\n" + f"{indentation}{self.use_derived_matrix_RGB_to_XYZ},\n" + f"{indentation}{self.use_derived_matrix_XYZ_to_RGB})" + ) def _derive_transformation_matrices(self): """ - Computes the derived transformations matrices, the normalised primary + Compute the derived transformations matrices, the normalised primary matrix and its inverse. """ - if hasattr(self, '_primaries') and hasattr(self, '_whitepoint'): + if hasattr(self, "_primaries") and hasattr(self, "_whitepoint"): if self._primaries is not None and self._whitepoint is not None: - npm = normalised_primary_matrix(self._primaries, - self._whitepoint) + npm = normalised_primary_matrix( + self._primaries, self._whitepoint + ) self._derived_matrix_RGB_to_XYZ = npm self._derived_matrix_XYZ_to_RGB = np.linalg.inv(npm) - def use_derived_transformation_matrices(self, usage=True): + def use_derived_transformation_matrices(self, usage: Boolean = True): """ - Enables or disables usage of both derived transformations matrices, + Enable or disables usage of both derived transformations matrices, the normalised primary matrix and its inverse in subsequent computations. Parameters ---------- - usage : bool, optional + usage Whether to use the derived transformations matrices. - - Returns - ------- - bool - Definition success. """ self.use_derived_matrix_RGB_to_XYZ = usage self.use_derived_matrix_XYZ_to_RGB = usage - return True - - def chromatically_adapt(self, - whitepoint, - whitepoint_name=None, - chromatic_adaptation_transform='CAT02'): + def chromatically_adapt( + self, + whitepoint: ArrayLike, + whitepoint_name: Optional[str] = None, + chromatic_adaptation_transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", + ) -> RGB_Colourspace: """ - Chromatically adapts the *RGB* colourspace *primaries* :math:`xy` + Chromatically adapt the *RGB* colourspace *primaries* :math:`xy` chromaticity coordinates from *RGB* colourspace whitepoint to reference ``whitepoint``. Parameters ---------- - whitepoint : array_like + whitepoint Reference illuminant / whitepoint :math:`xy` chromaticity coordinates. - whitepoint_name : unicode, optional + whitepoint_name Reference illuminant / whitepoint name. - chromatic_adaptation_transform : unicode, optional - **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', - 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02 Brill 2008', - 'Bianco 2010', 'Bianco PC 2010'}**, + chromatic_adaptation_transform *Chromatic adaptation* transform. Returns ------- - Chromatically adapted *RGB* colourspace. + :class:`colour.RGB_Colourspace` + Chromatically adapted *RGB* colourspace. Examples -------- @@ -826,8 +837,8 @@ def chromatically_adapt(self, >>> colourspace = RGB_Colourspace('RGB Colourspace', p, w_t, 'D65') >>> print(colourspace.chromatically_adapt(w_r, 'D50', 'Bradford')) ... # doctest: +ELLIPSIS - RGB Colourspace - Chromatically Adapted to D50 - ---------------------------------------------- + RGB Colourspace - Chromatically Adapted to 'D50' + ------------------------------------------------ Primaries : [[ 0.73485524 0.26422533] [-0.00617091 1.01131496] @@ -851,9 +862,12 @@ def chromatically_adapt(self, colourspace = self.copy() colourspace.primaries = chromatically_adapted_primaries( - colourspace.primaries, colourspace.whitepoint, whitepoint, - chromatic_adaptation_transform) - colourspace.whitepoint = whitepoint + colourspace.primaries, + colourspace.whitepoint, + whitepoint, + chromatic_adaptation_transform, + ) + colourspace.whitepoint = whitepoint # type: ignore[assignment] colourspace.whitepoint_name = whitepoint_name colourspace._matrix_RGB_to_XYZ = None @@ -861,218 +875,80 @@ def chromatically_adapt(self, colourspace._derive_transformation_matrices() colourspace.use_derived_transformation_matrices() - colourspace.name = '{0} - Chromatically Adapted to {1}'.format( - colourspace.name, whitepoint - if whitepoint_name is None else whitepoint_name) + colourspace.name = ( + f"{colourspace.name} - Chromatically Adapted to " + f"{cast(str, optional(whitepoint_name, whitepoint))!r}" + ) return colourspace - def copy(self): + def copy(self) -> RGB_Colourspace: """ - Returns a copy of the *RGB* colourspace. + Return a copy of the *RGB* colourspace. Returns ------- - RGB_Colourspace + :class:`colour.RGB_Colourspace` *RGB* colourspace copy. """ return deepcopy(self) - # ------------------------------------------------------------------------# - # --- API Changes and Deprecation Management ---# - # ------------------------------------------------------------------------# - @property - def illuminant(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('RGB_Colourspace.illuminant', - 'RGB_Colourspace.whitepoint_name'))) - - return self.whitepoint_name - - @illuminant.setter - def illuminant(self, value): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('RGB_Colourspace.illuminant', - 'RGB_Colourspace.whitepoint_name'))) - - self.whitepoint_name = value - - @property - def encoding_cctf(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('RGB_Colourspace.encoding_cctf', - 'RGB_Colourspace.cctf_encoding'))) - - return self.cctf_encoding - - @encoding_cctf.setter - def encoding_cctf(self, value): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('RGB_Colourspace.encoding_cctf', - 'RGB_Colourspace.cctf_encoding'))) - - self.cctf_encoding = value - - @property - def decoding_cctf(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('RGB_Colourspace.decoding_cctf', - 'RGB_Colourspace.cctf_decoding'))) - - return self.cctf_decoding - - @decoding_cctf.setter - def decoding_cctf(self, value): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('RGB_Colourspace.decoding_cctf', - 'RGB_Colourspace.cctf_decoding'))) - - self.cctf_decoding = value - - @property - def RGB_to_XYZ_matrix(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('RGB_Colourspace.RGB_to_XYZ_matrix', - 'RGB_Colourspace.matrix_RGB_to_XYZ'))) - - return self.matrix_RGB_to_XYZ - @RGB_to_XYZ_matrix.setter - def RGB_to_XYZ_matrix(self, value): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('RGB_Colourspace.RGB_to_XYZ_matrix', - 'RGB_Colourspace.matrix_RGB_to_XYZ'))) - - self.matrix_RGB_to_XYZ = value - - @property - def XYZ_to_RGB_matrix(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('RGB_Colourspace.XYZ_to_RGB_matrix', - 'RGB_Colourspace.matrix_XYZ_to_RGB'))) - - return self.matrix_XYZ_to_RGB - - @XYZ_to_RGB_matrix.setter - def XYZ_to_RGB_matrix(self, value): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed('RGB_Colourspace.XYZ_to_RGB_matrix', - 'RGB_Colourspace.matrix_XYZ_to_RGB'))) - - self.XYZ_to_RGB_matrix = value - - @property - def use_derived_RGB_to_XYZ_matrix(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed( - 'RGB_Colourspace.use_derived_RGB_to_XYZ_matrix', - 'RGB_Colourspace.use_derived_matrix_RGB_to_XYZ'))) - - return self.use_derived_matrix_RGB_to_XYZ - - @use_derived_RGB_to_XYZ_matrix.setter - def use_derived_RGB_to_XYZ_matrix(self, value): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed( - 'RGB_Colourspace.use_derived_RGB_to_XYZ_matrix', - 'RGB_Colourspace.use_derived_matrix_RGB_to_XYZ'))) - - self.use_derived_matrix_RGB_to_XYZ = value - - @property - def use_derived_XYZ_to_RGB_matrix(self): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed( - 'RGB_Colourspace.use_derived_XYZ_to_RGB_matrix', - 'RGB_Colourspace.use_derived_matrix_XYZ_to_RGB'))) - - return self.use_derived_matrix_XYZ_to_RGB - - @use_derived_XYZ_to_RGB_matrix.setter - def use_derived_XYZ_to_RGB_matrix(self, value): # pragma: no cover - # Docstrings are omitted for documentation purposes. - usage_warning( - str( - ObjectRenamed( - 'RGB_Colourspace.use_derived_XYZ_to_RGB_matrix', - 'RGB_Colourspace.use_derived_matrix_XYZ_to_RGB'))) - - self.use_derived_XYZ_to_RGB_matrix = value - - -def XYZ_to_RGB(XYZ, - illuminant_XYZ, - illuminant_RGB, - matrix_XYZ_to_RGB, - chromatic_adaptation_transform='CAT02', - cctf_encoding=None, - **kwargs): +def XYZ_to_RGB( + XYZ: ArrayLike, + illuminant_XYZ: ArrayLike, + illuminant_RGB: ArrayLike, + matrix_XYZ_to_RGB: ArrayLike, + chromatic_adaptation_transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", + cctf_encoding: Optional[Callable] = None, +) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to *RGB* colourspace array. + Convert from *CIE XYZ* tristimulus values to *RGB* colourspace array. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. - illuminant_XYZ : array_like + illuminant_XYZ *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array of the *illuminant* for the input *CIE XYZ* tristimulus values. - illuminant_RGB : array_like + illuminant_RGB *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array of the *illuminant* for the output *RGB* colourspace array. - matrix_XYZ_to_RGB : array_like + matrix_XYZ_to_RGB Matrix converting the *CIE XYZ* tristimulus values to *RGB* colourspace array, i.e. the inverse *Normalised Primary Matrix* (NPM). - chromatic_adaptation_transform : unicode, optional - **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', - 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02 Brill 2008', - 'Bianco 2010', 'Bianco PC 2010', None}**, + chromatic_adaptation_transform *Chromatic adaptation* transform, if *None* no chromatic adaptation is performed. - cctf_encoding : object, optional + cctf_encoding Encoding colour component transfer function (Encoding CCTF) or - opto-electronic transfer function (OETF / OECF). - - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. + opto-electronic transfer function (OETF). Returns ------- - ndarray + :class:`numpy.ndarray` *RGB* colourspace array. Notes ----- - +--------------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +====================+=======================+===============+ @@ -1105,79 +981,80 @@ def XYZ_to_RGB(XYZ, array([ 0.4559557..., 0.0303970..., 0.0408724...]) """ - matrix_XYZ_to_RGB = handle_arguments_deprecation({ - 'ArgumentRenamed': [['XYZ_to_RGB_matrix', 'matrix_XYZ_to_RGB']], - }, **kwargs).get('matrix_XYZ_to_RGB', matrix_XYZ_to_RGB) - - cctf_encoding = handle_arguments_deprecation({ - 'ArgumentRenamed': [['encoding_cctf', 'cctf_encoding']], - }, **kwargs).get('cctf_encoding', cctf_encoding) - XYZ = to_domain_1(XYZ) if chromatic_adaptation_transform is not None: M_CAT = matrix_chromatic_adaptation_VonKries( xyY_to_XYZ(xy_to_xyY(illuminant_XYZ)), xyY_to_XYZ(xy_to_xyY(illuminant_RGB)), - transform=chromatic_adaptation_transform) + transform=chromatic_adaptation_transform, + ) XYZ = vector_dot(M_CAT, XYZ) RGB = vector_dot(matrix_XYZ_to_RGB, XYZ) if cctf_encoding is not None: - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): RGB = cctf_encoding(RGB) return from_range_1(RGB) -def RGB_to_XYZ(RGB, - illuminant_RGB, - illuminant_XYZ, - matrix_RGB_to_XYZ, - chromatic_adaptation_transform='CAT02', - cctf_decoding=None, - **kwargs): +def RGB_to_XYZ( + RGB: ArrayLike, + illuminant_RGB: ArrayLike, + illuminant_XYZ: ArrayLike, + matrix_RGB_to_XYZ: ArrayLike, + chromatic_adaptation_transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", + cctf_decoding: Optional[Callable] = None, +) -> NDArray: """ - Converts given *RGB* colourspace array to *CIE XYZ* tristimulus values. + Convert given *RGB* colourspace array to *CIE XYZ* tristimulus values. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array. - illuminant_RGB : array_like + illuminant_RGB *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array of the *illuminant* for the input *RGB* colourspace array. - illuminant_XYZ : array_like + illuminant_XYZ *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array of the *illuminant* for the output *CIE XYZ* tristimulus values. - matrix_RGB_to_XYZ : array_like + matrix_RGB_to_XYZ Matrix converting the *RGB* colourspace array to *CIE XYZ* tristimulus values, i.e. the *Normalised Primary Matrix* (NPM). - chromatic_adaptation_transform : unicode, optional - **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', - 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02 Brill 2008', - 'Bianco 2010', 'Bianco PC 2010', None}**, + chromatic_adaptation_transform *Chromatic adaptation* transform, if *None* no chromatic adaptation is performed. - cctf_decoding : object, optional + cctf_decoding Decoding colour component transfer function (Decoding CCTF) or - electro-optical transfer function (EOTF / EOCF). - - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. + electro-optical transfer function (EOTF). Returns ------- - ndarray + :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Notes ----- - +--------------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +====================+=======================+===============+ @@ -1210,18 +1087,10 @@ def RGB_to_XYZ(RGB, array([ 0.2163881..., 0.1257 , 0.0384749...]) """ - matrix_RGB_to_XYZ = handle_arguments_deprecation({ - 'ArgumentRenamed': [['RGB_to_XYZ_matrix', 'matrix_RGB_to_XYZ']], - }, **kwargs).get('matrix_RGB_to_XYZ', matrix_RGB_to_XYZ) - - cctf_decoding = handle_arguments_deprecation({ - 'ArgumentRenamed': [['decoding_cctf', 'cctf_decoding']], - }, **kwargs).get('cctf_decoding', cctf_decoding) - RGB = to_domain_1(RGB) if cctf_decoding is not None: - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): RGB = cctf_decoding(RGB) XYZ = vector_dot(matrix_RGB_to_XYZ, RGB) @@ -1230,37 +1099,53 @@ def RGB_to_XYZ(RGB, M_CAT = matrix_chromatic_adaptation_VonKries( xyY_to_XYZ(xy_to_xyY(illuminant_RGB)), xyY_to_XYZ(xy_to_xyY(illuminant_XYZ)), - transform=chromatic_adaptation_transform) + transform=chromatic_adaptation_transform, + ) XYZ = vector_dot(M_CAT, XYZ) return from_range_1(XYZ) -def matrix_RGB_to_RGB(input_colourspace, - output_colourspace, - chromatic_adaptation_transform='CAT02'): +def matrix_RGB_to_RGB( + input_colourspace: RGB_Colourspace, + output_colourspace: RGB_Colourspace, + chromatic_adaptation_transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", +) -> NDArray: """ - Computes the matrix :math:`M` converting from given input *RGB* + Compute the matrix :math:`M` converting from given input *RGB* colourspace to output *RGB* colourspace using given *chromatic adaptation* method. Parameters ---------- - input_colourspace : RGB_Colourspace + input_colourspace *RGB* input colourspace. - output_colourspace : RGB_Colourspace + output_colourspace *RGB* output colourspace. - chromatic_adaptation_transform : unicode, optional - **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', - 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02 Brill 2008', - 'Bianco 2010', 'Bianco PC 2010', None}**, + chromatic_adaptation_transform *Chromatic adaptation* transform, if *None* no chromatic adaptation is performed. Returns ------- - ndarray + :class:`numpy.ndarray` Conversion matrix :math:`M`. Examples @@ -1280,7 +1165,8 @@ def matrix_RGB_to_RGB(input_colourspace, M_CAT = matrix_chromatic_adaptation_VonKries( xy_to_XYZ(input_colourspace.whitepoint), xy_to_XYZ(output_colourspace.whitepoint), - chromatic_adaptation_transform) + chromatic_adaptation_transform, + ) M = matrix_dot(M_CAT, input_colourspace.matrix_RGB_to_XYZ) @@ -1289,51 +1175,65 @@ def matrix_RGB_to_RGB(input_colourspace, return M -def RGB_to_RGB(RGB, - input_colourspace, - output_colourspace, - chromatic_adaptation_transform='CAT02', - apply_cctf_decoding=False, - apply_cctf_encoding=False, - **kwargs): +def RGB_to_RGB( + RGB: ArrayLike, + input_colourspace: RGB_Colourspace, + output_colourspace: RGB_Colourspace, + chromatic_adaptation_transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", + apply_cctf_decoding: Boolean = False, + apply_cctf_encoding: Boolean = False, + **kwargs: Any, +) -> NDArray: """ - Converts given *RGB* colourspace array from given input *RGB* colourspace + Convert given *RGB* colourspace array from given input *RGB* colourspace to output *RGB* colourspace using given *chromatic adaptation* method. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array. - input_colourspace : RGB_Colourspace + input_colourspace *RGB* input colourspace. - output_colourspace : RGB_Colourspace + output_colourspace *RGB* output colourspace. - chromatic_adaptation_transform : unicode, optional - **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', - 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02 Brill 2008', - 'Bianco 2010', 'Bianco PC 2010', None}**, + chromatic_adaptation_transform *Chromatic adaptation* transform, if *None* no chromatic adaptation is performed. - apply_cctf_decoding : bool, optional + apply_cctf_decoding Apply input colourspace decoding colour component transfer function / electro-optical transfer function. - apply_cctf_encoding : bool, optional + apply_cctf_encoding Apply output colourspace encoding colour component transfer function / opto-electronic transfer function. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs Keywords arguments for the colour component transfer functions. Returns ------- - ndarray + :class:`numpy.ndarray` *RGB* colourspace array. Notes ----- - +--------------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +====================+=======================+===============+ @@ -1356,31 +1256,25 @@ def RGB_to_RGB(RGB, array([ 0.2568891..., 0.0721446..., 0.0465553...]) """ - apply_cctf_decoding = handle_arguments_deprecation({ - 'ArgumentRenamed': [['apply_decoding_cctf', 'apply_cctf_decoding']], - }, **kwargs).get('apply_cctf_decoding', apply_cctf_decoding) - - apply_cctf_encoding = handle_arguments_deprecation({ - 'ArgumentRenamed': [['apply_encoding_cctf', 'apply_cctf_encoding']], - }, **kwargs).get('apply_cctf_encoding', apply_cctf_encoding) - RGB = to_domain_1(RGB) - if apply_cctf_decoding: - with domain_range_scale('ignore'): + if apply_cctf_decoding and input_colourspace.cctf_decoding is not None: + with domain_range_scale("ignore"): RGB = input_colourspace.cctf_decoding( - RGB, **filter_kwargs(input_colourspace.cctf_decoding, - **kwargs)) + RGB, **filter_kwargs(input_colourspace.cctf_decoding, **kwargs) + ) - M = matrix_RGB_to_RGB(input_colourspace, output_colourspace, - chromatic_adaptation_transform) + M = matrix_RGB_to_RGB( + input_colourspace, output_colourspace, chromatic_adaptation_transform + ) RGB = vector_dot(M, RGB) - if apply_cctf_encoding: - with domain_range_scale('ignore'): + if apply_cctf_encoding and output_colourspace.cctf_encoding is not None: + with domain_range_scale("ignore"): RGB = output_colourspace.cctf_encoding( - RGB, **filter_kwargs(output_colourspace.cctf_encoding, - **kwargs)) + RGB, + **filter_kwargs(output_colourspace.cctf_encoding, **kwargs), + ) return from_range_1(RGB) diff --git a/colour/models/rgb/tests/__init__.py b/colour/models/rgb/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/models/rgb/tests/__init__.py +++ b/colour/models/rgb/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/models/rgb/tests/test_cmyk.py b/colour/models/rgb/tests/test_cmyk.py index d6b9901e99..68326c4056 100644 --- a/colour/models/rgb/tests/test_cmyk.py +++ b/colour/models/rgb/tests/test_cmyk.py @@ -1,59 +1,62 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.rgb.cmyk` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.rgb.cmyk` module.""" import numpy as np import unittest from itertools import permutations -from colour.models.rgb.cmyk import (RGB_to_CMY, CMY_to_RGB, CMY_to_CMYK, - CMYK_to_CMY) +from colour.models.rgb.cmyk import ( + RGB_to_CMY, + CMY_to_RGB, + CMY_to_CMYK, + CMYK_to_CMY, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestRGB_to_CMY', 'TestCMY_to_RGB', 'TestCMY_to_CMYK', 'TestCMYK_to_CMY' + "TestRGB_to_CMY", + "TestCMY_to_RGB", + "TestCMY_to_CMYK", + "TestCMYK_to_CMY", ] class TestRGB_to_CMY(unittest.TestCase): """ - Defines :func:`colour.models.rgb.cmyk.RGB_to_CMY` definition unit tests + Define :func:`colour.models.rgb.cmyk.RGB_to_CMY` definition unit tests methods. """ def test_RGB_to_CMY(self): - """ - Tests :func:`colour.models.rgb.cmyk.RGB_to_CMY` definition. - """ + """Test :func:`colour.models.rgb.cmyk.RGB_to_CMY` definition.""" np.testing.assert_almost_equal( RGB_to_CMY(np.array([0.45620519, 0.03081071, 0.04091952])), np.array([0.54379481, 0.96918929, 0.95908048]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_CMY(np.array([0.00000000, 0.00000000, 0.00000000])), np.array([1.00000000, 1.00000000, 1.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_CMY(np.array([1.00000000, 1.00000000, 1.00000000])), np.array([0.00000000, 0.00000000, 0.00000000]), - decimal=7) + decimal=7, + ) def test_n_dimensional_RGB_to_CMY(self): """ - Tests :func:`colour.models.rgb.cmyk.RGB_to_CMY` definition + Test :func:`colour.models.rgb.cmyk.RGB_to_CMY` definition n-dimensional arrays support. """ @@ -70,23 +73,24 @@ def test_n_dimensional_RGB_to_CMY(self): def test_domain_range_scale_RGB_to_CMY(self): """ - Tests :func:`colour.models.rgb.cmyk.RGB_to_CMY` definition domain and + Test :func:`colour.models.rgb.cmyk.RGB_to_CMY` definition domain and range scale support. """ RGB = np.array([0.45620519, 0.03081071, 0.04091952]) CMY = RGB_to_CMY(RGB) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - RGB_to_CMY(RGB * factor), CMY * factor, decimal=7) + RGB_to_CMY(RGB * factor), CMY * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_RGB_to_CMY(self): """ - Tests :func:`colour.models.rgb.cmyk.RGB_to_CMY` definition nan + Test :func:`colour.models.rgb.cmyk.RGB_to_CMY` definition nan support. """ @@ -99,33 +103,34 @@ def test_nan_RGB_to_CMY(self): class TestCMY_to_RGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.cmyk.CMY_to_RGB` definition unit tests + Define :func:`colour.models.rgb.cmyk.CMY_to_RGB` definition unit tests methods. """ def test_CMY_to_RGB(self): - """ - Tests :func:`colour.models.rgb.cmyk.CMY_to_RGB` definition. - """ + """Test :func:`colour.models.rgb.cmyk.CMY_to_RGB` definition.""" np.testing.assert_almost_equal( CMY_to_RGB(np.array([0.54379481, 0.96918929, 0.95908048])), np.array([0.45620519, 0.03081071, 0.04091952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( CMY_to_RGB(np.array([1.00000000, 1.00000000, 1.00000000])), np.array([0.00000000, 0.00000000, 0.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( CMY_to_RGB(np.array([0.00000000, 0.00000000, 0.00000000])), np.array([1.00000000, 1.00000000, 1.00000000]), - decimal=7) + decimal=7, + ) def test_n_dimensional_CMY_to_RGB(self): """ - Tests :func:`colour.models.rgb.cmyk.CMY_to_RGB` definition + Test :func:`colour.models.rgb.cmyk.CMY_to_RGB` definition n-dimensional arrays support. """ @@ -142,24 +147,23 @@ def test_n_dimensional_CMY_to_RGB(self): def test_domain_range_scale_CMY_to_RGB(self): """ - Tests :func:`colour.models.rgb.cmyk.CMY_to_RGB` definition domain and + Test :func:`colour.models.rgb.cmyk.CMY_to_RGB` definition domain and range scale support. """ CMY = np.array([0.54379481, 0.96918929, 0.95908048]) RGB = CMY_to_RGB(CMY) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - CMY_to_RGB(CMY * factor), RGB * factor, decimal=7) + CMY_to_RGB(CMY * factor), RGB * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_CMY_to_RGB(self): - """ - Tests :func:`colour.models.rgb.cmyk.CMY_to_RGB` definition nan support. - """ + """Test :func:`colour.models.rgb.cmyk.CMY_to_RGB` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -170,33 +174,34 @@ def test_nan_CMY_to_RGB(self): class TestCMY_to_CMYK(unittest.TestCase): """ - Defines :func:`colour.models.rgb.cmyk.CMY_to_CMYK` definition unit tests + Define :func:`colour.models.rgb.cmyk.CMY_to_CMYK` definition unit tests methods. """ def test_CMY_to_CMYK(self): - """ - Tests :func:`colour.models.rgb.cmyk.CMY_to_CMYK` definition. - """ + """Test :func:`colour.models.rgb.cmyk.CMY_to_CMYK` definition.""" np.testing.assert_almost_equal( CMY_to_CMYK(np.array([0.54379481, 0.96918929, 0.95908048])), np.array([0.00000000, 0.93246304, 0.91030457, 0.54379481]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( CMY_to_CMYK(np.array([0.15000000, 1.00000000, 1.00000000])), np.array([0.00000000, 1.00000000, 1.00000000, 0.15000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( CMY_to_CMYK(np.array([0.15000000, 0.00000000, 0.00000000])), np.array([0.15000000, 0.00000000, 0.00000000, 0.00000000]), - decimal=7) + decimal=7, + ) def test_n_dimensional_CMY_to_CMYK(self): """ - Tests :func:`colour.models.rgb.cmyk.CMY_to_CMYK` definition + Test :func:`colour.models.rgb.cmyk.CMY_to_CMYK` definition n-dimensional arrays support. """ @@ -213,23 +218,24 @@ def test_n_dimensional_CMY_to_CMYK(self): def test_domain_range_scale_CMY_to_CMYK(self): """ - Tests :func:`colour.models.rgb.cmyk.CMY_to_CMYK` definition domain and + Test :func:`colour.models.rgb.cmyk.CMY_to_CMYK` definition domain and range scale support. """ CMY = np.array([0.54379481, 0.96918929, 0.95908048]) CMYK = CMY_to_CMYK(CMY) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - CMY_to_CMYK(CMY * factor), CMYK * factor, decimal=7) + CMY_to_CMYK(CMY * factor), CMYK * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_CMY_to_CMYK(self): """ - Tests :func:`colour.models.rgb.cmyk.CMY_to_CMYK` definition nan + Test :func:`colour.models.rgb.cmyk.CMY_to_CMYK` definition nan support. """ @@ -242,36 +248,40 @@ def test_nan_CMY_to_CMYK(self): class TestCMYK_to_CMY(unittest.TestCase): """ - Defines :func:`colour.models.rgb.cmyk.CMYK_to_CMY` definition unit tests + Define :func:`colour.models.rgb.cmyk.CMYK_to_CMY` definition unit tests methods. """ def test_CMYK_to_CMY(self): - """ - Tests :func:`colour.models.rgb.cmyk.CMYK_to_CMY` definition. - """ + """Test :func:`colour.models.rgb.cmyk.CMYK_to_CMY` definition.""" np.testing.assert_almost_equal( CMYK_to_CMY( - np.array([0.00000000, 0.93246304, 0.91030457, 0.54379481])), + np.array([0.00000000, 0.93246304, 0.91030457, 0.54379481]) + ), np.array([0.54379481, 0.96918929, 0.95908048]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( CMYK_to_CMY( - np.array([0.00000000, 1.00000000, 1.00000000, 0.15000000])), + np.array([0.00000000, 1.00000000, 1.00000000, 0.15000000]) + ), np.array([0.15000000, 1.00000000, 1.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( CMYK_to_CMY( - np.array([0.15000000, 0.00000000, 0.00000000, 0.00000000])), + np.array([0.15000000, 0.00000000, 0.00000000, 0.00000000]) + ), np.array([0.15000000, 0.00000000, 0.00000000]), - decimal=7) + decimal=7, + ) def test_n_dimensional_CMYK_to_CMY(self): """ - Tests :func:`colour.models.rgb.cmyk.CMYK_to_CMY` definition + Test :func:`colour.models.rgb.cmyk.CMYK_to_CMY` definition n-dimensional arrays support. """ @@ -288,23 +298,24 @@ def test_n_dimensional_CMYK_to_CMY(self): def test_domain_range_scale_CMYK_to_CMY(self): """ - Tests :func:`colour.models.rgb.cmyk.CMYK_to_CMY` definition domain and + Test :func:`colour.models.rgb.cmyk.CMYK_to_CMY` definition domain and range scale support. """ CMYK = np.array([0.00000000, 0.93246304, 0.91030457, 0.54379481]) CMY = CMYK_to_CMY(CMYK) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - CMYK_to_CMY(CMYK * factor), CMY * factor, decimal=7) + CMYK_to_CMY(CMYK * factor), CMY * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_CMYK_to_CMY(self): """ - Tests :func:`colour.models.rgb.cmyk.CMYK_to_CMY` definition nan + Test :func:`colour.models.rgb.cmyk.CMYK_to_CMY` definition nan support. """ @@ -315,5 +326,5 @@ def test_nan_CMYK_to_CMY(self): CMYK_to_CMY(CMYK) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/tests/test_common.py b/colour/models/rgb/tests/test_common.py index 8e16b58085..859c2dca74 100644 --- a/colour/models/rgb/tests/test_common.py +++ b/colour/models/rgb/tests/test_common.py @@ -1,124 +1,140 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.rgb.common` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.rgb.common` module.""" import numpy as np import unittest from colour.models import XYZ_to_sRGB, sRGB_to_XYZ -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestXYZ_to_sRGB', 'TestsRGB_to_XYZ'] +__all__ = [ + "TestXYZ_to_sRGB", + "TestsRGB_to_XYZ", +] class TestXYZ_to_sRGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.common.XYZ_to_sRGB` definition unit tests + Define :func:`colour.models.rgb.common.XYZ_to_sRGB` definition unit tests methods. """ def test_XYZ_to_sRGB(self): - """ - Tests :func:`colour.models.rgb.common.XYZ_to_sRGB` definition. - """ + """Test :func:`colour.models.rgb.common.XYZ_to_sRGB` definition.""" np.testing.assert_almost_equal( XYZ_to_sRGB(np.array([0.20654008, 0.12197225, 0.05136952])), np.array([0.70573936, 0.19248266, 0.22354169]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_sRGB(np.array([0.14222010, 0.23042768, 0.10495772])), np.array([0.25847003, 0.58276102, 0.29718877]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_sRGB( np.array([0.07818780, 0.06157201, 0.28099326]), - np.array([0.34570, 0.35850])), + np.array([0.34570, 0.35850]), + ), np.array([0.09838967, 0.25404426, 0.65130925]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_sRGB( np.array([0.00000000, 0.00000000, 0.00000000]), - np.array([0.44757, 0.40745])), + np.array([0.44757, 0.40745]), + ), np.array([0.00000000, 0.00000000, 0.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_sRGB( np.array([0.20654008, 0.12197225, 0.05136952]), - chromatic_adaptation_transform='Bradford'), - np.array([0.70573936, 0.19248266, 0.22354169]), - decimal=7) + np.array([0.44757, 0.40745]), + chromatic_adaptation_transform="Bradford", + ), + np.array([0.60873814, 0.23259548, 0.43714892]), + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_sRGB( np.array([0.20654008, 0.12197225, 0.05136952]), - apply_cctf_encoding=False), + apply_cctf_encoding=False, + ), np.array([0.45620520, 0.03081070, 0.04091953]), - decimal=7) + decimal=7, + ) class TestsRGB_to_XYZ(unittest.TestCase): """ - Defines :func:`colour.models.rgb.common.sRGB_to_XYZ` definition unit tests + Define :func:`colour.models.rgb.common.sRGB_to_XYZ` definition unit tests methods. """ def test_sRGB_to_XYZ(self): - """ - Tests :func:`colour.models.rgb.common.sRGB_to_XYZ` definition. - """ + """Test :func:`colour.models.rgb.common.sRGB_to_XYZ` definition.""" np.testing.assert_almost_equal( sRGB_to_XYZ(np.array([0.70573936, 0.19248266, 0.22354169])), np.array([0.20654290, 0.12197943, 0.05137140]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( sRGB_to_XYZ(np.array([0.25847003, 0.58276102, 0.29718877])), np.array([0.14222582, 0.23043727, 0.10496290]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( sRGB_to_XYZ( np.array([0.09838967, 0.25404426, 0.65130925]), - np.array([0.34570, 0.35850])), + np.array([0.34570, 0.35850]), + ), np.array([0.07819162, 0.06157356, 0.28099475]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( sRGB_to_XYZ( np.array([0.00000000, 0.00000000, 0.00000000]), - np.array([0.44757, 0.40745])), + np.array([0.44757, 0.40745]), + ), np.array([0.00000000, 0.00000000, 0.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( sRGB_to_XYZ( - np.array([0.17498172, 0.38818743, 0.32159978]), - chromatic_adaptation_method='Bradford'), - np.array([0.07049534, 0.10080000, 0.09558313]), - decimal=7) + np.array([0.60873814, 0.23259548, 0.43714892]), + np.array([0.44757, 0.40745]), + chromatic_adaptation_transform="Bradford", + ), + np.array([0.20654449, 0.12197792, 0.05137030]), + decimal=7, + ) np.testing.assert_almost_equal( sRGB_to_XYZ( np.array([0.45620520, 0.03081070, 0.04091953]), - apply_cctf_decoding=False), + apply_cctf_decoding=False, + ), np.array([0.20654291, 0.12197943, 0.05137141]), - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/tests/test_cylindrical.py b/colour/models/rgb/tests/test_cylindrical.py index beac7b6c35..a89b4bb109 100644 --- a/colour/models/rgb/tests/test_cylindrical.py +++ b/colour/models/rgb/tests/test_cylindrical.py @@ -1,64 +1,72 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.rgb.cylindrical` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.rgb.cylindrical` module.""" import numpy as np import unittest from itertools import permutations -from colour.models.rgb.cylindrical import (RGB_to_HSV, HSV_to_RGB, RGB_to_HSL, - HSL_to_RGB) +from colour.models.rgb.cylindrical import ( + RGB_to_HSV, + HSV_to_RGB, + RGB_to_HSL, + HSL_to_RGB, + RGB_to_HCL, + HCL_to_RGB, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestRGB_to_HSV', 'TestHSV_to_RGB', 'TestRGB_to_HSL', 'TestHSL_to_RGB' + "TestRGB_to_HSV", + "TestHSV_to_RGB", + "TestRGB_to_HSL", + "TestHSL_to_RGB", + "TestRGB_to_HCL", + "TestHCL_to_RGB", ] class TestRGB_to_HSV(unittest.TestCase): """ - Defines :func:`colour.models.rgb.cylindrical.RGB_to_HSV` definition unit + Define :func:`colour.models.rgb.cylindrical.RGB_to_HSV` definition unit tests methods. """ def test_RGB_to_HSV(self): - """ - Tests :func:`colour.models.rgb.cylindrical.RGB_to_HSV` definition. - """ + """Test :func:`colour.models.rgb.cylindrical.RGB_to_HSV` definition.""" np.testing.assert_almost_equal( RGB_to_HSV(np.array([0.45620519, 0.03081071, 0.04091952])), np.array([0.99603944, 0.93246304, 0.45620519]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_HSV(np.array([0.00000000, 0.00000000, 0.00000000])), np.array([0.00000000, 0.00000000, 0.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_HSV(np.array([1.00000000, 1.00000000, 1.00000000])), np.array([0.00000000, 0.00000000, 1.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_HSV(np.array([0.00000000, 1.00000000, 1.00000000])), np.array([0.50000000, 1.00000000, 1.00000000]), - decimal=7) + decimal=7, + ) def test_n_dimensional_RGB_to_HSV(self): """ - Tests :func:`colour.models.rgb.cylindrical.RGB_to_HSV` definition + Test :func:`colour.models.rgb.cylindrical.RGB_to_HSV` definition n-dimensional arrays support. """ @@ -75,23 +83,24 @@ def test_n_dimensional_RGB_to_HSV(self): def test_domain_range_scale_RGB_to_HSV(self): """ - Tests :func:`colour.models.rgb.cylindrical.RGB_to_HSV` definition + Test :func:`colour.models.rgb.cylindrical.RGB_to_HSV` definition domain and range scale support. """ RGB = np.array([0.45620519, 0.03081071, 0.04091952]) HSV = RGB_to_HSV(RGB) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - RGB_to_HSV(RGB * factor), HSV * factor, decimal=7) + RGB_to_HSV(RGB * factor), HSV * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_RGB_to_HSV(self): """ - Tests :func:`colour.models.rgb.cylindrical.RGB_to_HSV` definition nan + Test :func:`colour.models.rgb.cylindrical.RGB_to_HSV` definition nan support. """ @@ -104,38 +113,40 @@ def test_nan_RGB_to_HSV(self): class TestHSV_to_RGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.cylindrical.HSV_to_RGB` definition unit + Define :func:`colour.models.rgb.cylindrical.HSV_to_RGB` definition unit tests methods. """ def test_HSV_to_RGB(self): - """ - Tests :func:`colour.models.rgb.cylindrical.HSV_to_RGB` definition. - """ + """Test :func:`colour.models.rgb.cylindrical.HSV_to_RGB` definition.""" np.testing.assert_almost_equal( HSV_to_RGB(np.array([0.99603944, 0.93246304, 0.45620519])), np.array([0.45620519, 0.03081071, 0.04091952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( HSV_to_RGB(np.array([0.00000000, 0.00000000, 0.00000000])), np.array([0.00000000, 0.00000000, 0.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( HSV_to_RGB(np.array([0.00000000, 0.00000000, 1.00000000])), np.array([1.00000000, 1.00000000, 1.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( HSV_to_RGB(np.array([0.50000000, 1.00000000, 1.00000000])), np.array([0.00000000, 1.00000000, 1.00000000]), - decimal=7) + decimal=7, + ) def test_n_dimensional_HSV_to_RGB(self): """ - Tests :func:`colour.models.rgb.cylindrical.HSV_to_RGB` definition + Test :func:`colour.models.rgb.cylindrical.HSV_to_RGB` definition n-dimensional arrays support. """ @@ -152,23 +163,24 @@ def test_n_dimensional_HSV_to_RGB(self): def test_domain_range_scale_HSV_to_RGB(self): """ - Tests :func:`colour.models.rgb.cylindrical.HSV_to_RGB` definition + Test :func:`colour.models.rgb.cylindrical.HSV_to_RGB` definition domain and range scale support. """ HSV = np.array([0.99603944, 0.93246304, 0.45620519]) RGB = HSV_to_RGB(HSV) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - HSV_to_RGB(HSV * factor), RGB * factor, decimal=7) + HSV_to_RGB(HSV * factor), RGB * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_HSV_to_RGB(self): """ - Tests :func:`colour.models.rgb.cylindrical.HSV_to_RGB` definition nan + Test :func:`colour.models.rgb.cylindrical.HSV_to_RGB` definition nan support. """ @@ -181,38 +193,40 @@ def test_nan_HSV_to_RGB(self): class TestRGB_to_HSL(unittest.TestCase): """ - Defines :func:`colour.models.rgb.cylindrical.RGB_to_HSL` definition unit + Define :func:`colour.models.rgb.cylindrical.RGB_to_HSL` definition unit tests methods. """ def test_RGB_to_HSL(self): - """ - Tests :func:`colour.models.rgb.cylindrical.RGB_to_HSL` definition. - """ + """Test :func:`colour.models.rgb.cylindrical.RGB_to_HSL` definition.""" np.testing.assert_almost_equal( RGB_to_HSL(np.array([0.45620519, 0.03081071, 0.04091952])), np.array([0.99603944, 0.87347144, 0.24350795]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_HSL(np.array([0.00000000, 0.00000000, 0.00000000])), np.array([0.00000000, 0.00000000, 0.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_HSL(np.array([1.00000000, 1.00000000, 1.00000000])), np.array([0.00000000, 0.00000000, 1.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_HSL(np.array([1.00000000, 0.00000000, 0.00000000])), np.array([0.00000000, 1.00000000, 0.50000000]), - decimal=7) + decimal=7, + ) def test_n_dimensional_RGB_to_HSL(self): """ - Tests :func:`colour.models.rgb.cylindrical.RGB_to_HSL` definition + Test :func:`colour.models.rgb.cylindrical.RGB_to_HSL` definition n-dimensional arrays support. """ @@ -229,23 +243,24 @@ def test_n_dimensional_RGB_to_HSL(self): def test_domain_range_scale_RGB_to_HSL(self): """ - Tests :func:`colour.models.rgb.cylindrical.RGB_to_HSL` definition + Test :func:`colour.models.rgb.cylindrical.RGB_to_HSL` definition domain and range scale support. """ RGB = np.array([0.45620519, 0.03081071, 0.04091952]) HSL = RGB_to_HSL(RGB) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - RGB_to_HSL(RGB * factor), HSL * factor, decimal=7) + RGB_to_HSL(RGB * factor), HSL * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_RGB_to_HSL(self): """ - Tests :func:`colour.models.rgb.cylindrical.RGB_to_HSL` definition nan + Test :func:`colour.models.rgb.cylindrical.RGB_to_HSL` definition nan support. """ @@ -258,38 +273,40 @@ def test_nan_RGB_to_HSL(self): class TestHSL_to_RGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.cylindrical.HSL_to_RGB` definition unit + Define :func:`colour.models.rgb.cylindrical.HSL_to_RGB` definition unit tests methods. """ def test_HSL_to_RGB(self): - """ - Tests :func:`colour.models.rgb.cylindrical.HSL_to_RGB` definition. - """ + """Test :func:`colour.models.rgb.cylindrical.HSL_to_RGB` definition.""" np.testing.assert_almost_equal( HSL_to_RGB(np.array([0.99603944, 0.87347144, 0.24350795])), np.array([0.45620519, 0.03081071, 0.04091952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( HSL_to_RGB(np.array([0.00000000, 0.00000000, 0.00000000])), np.array([0.00000000, 0.00000000, 0.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( HSL_to_RGB(np.array([0.00000000, 0.00000000, 1.00000000])), np.array([1.00000000, 1.00000000, 1.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( HSL_to_RGB(np.array([0.00000000, 1.00000000, 0.50000000])), np.array([1.00000000, 0.00000000, 0.00000000]), - decimal=7) + decimal=7, + ) def test_n_dimensional_HSL_to_RGB(self): """ - Tests :func:`colour.models.rgb.cylindrical.HSL_to_RGB` definition + Test :func:`colour.models.rgb.cylindrical.HSL_to_RGB` definition n-dimensional arrays support. """ @@ -306,23 +323,24 @@ def test_n_dimensional_HSL_to_RGB(self): def test_domain_range_scale_HSL_to_RGB(self): """ - Tests :func:`colour.models.rgb.cylindrical.HSL_to_RGB` definition + Test :func:`colour.models.rgb.cylindrical.HSL_to_RGB` definition domain and range scale support. """ HSL = np.array([0.99603944, 0.87347144, 0.24350795]) RGB = HSL_to_RGB(HSL) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - HSL_to_RGB(HSL * factor), RGB * factor, decimal=7) + HSL_to_RGB(HSL * factor), RGB * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_HSL_to_RGB(self): """ - Tests :func:`colour.models.rgb.cylindrical.HSL_to_RGB` definition nan + Test :func:`colour.models.rgb.cylindrical.HSL_to_RGB` definition nan support. """ @@ -333,5 +351,165 @@ def test_nan_HSL_to_RGB(self): HSL_to_RGB(HSL) -if __name__ == '__main__': +class TestRGB_to_HCL(unittest.TestCase): + """ + Define :func:`colour.models.rgb.cylindrical.RGB_to_HCL` definition unit + tests methods. + """ + + def test_RGB_to_HCL(self): + """Test :func:`colour.models.rgb.cylindrical.RGB_to_HCL` definition.""" + + np.testing.assert_almost_equal( + RGB_to_HCL(np.array([0.45620519, 0.03081071, 0.04091952])), + np.array([-0.03167854, 0.2841715, 0.22859647]), + decimal=7, + ) + + np.testing.assert_almost_equal( + RGB_to_HCL(np.array([1.00000000, 2.00000000, 0.50000000])), + np.array([1.83120102, 1.0075282, 1.00941024]), + decimal=7, + ) + + np.testing.assert_almost_equal( + RGB_to_HCL(np.array([2.00000000, 1.00000000, 0.50000000])), + np.array([0.30909841, 1.0075282, 1.00941024]), + decimal=7, + ) + + np.testing.assert_almost_equal( + RGB_to_HCL(np.array([0.50000000, 1.00000000, 2.00000000])), + np.array([-2.40349351, 1.0075282, 1.00941024]), + decimal=7, + ) + + def test_n_dimensional_RGB_to_HCL(self): + """ + Test :func:`colour.models.rgb.cylindrical.RGB_to_HCL` definition + n-dimensional arrays support. + """ + + RGB = np.array([0.45620519, 0.03081071, 0.04091952]) + HCL = RGB_to_HCL(RGB) + + RGB = np.tile(RGB, (6, 1)) + HCL = np.tile(HCL, (6, 1)) + np.testing.assert_almost_equal(RGB_to_HCL(RGB), HCL, decimal=7) + + RGB = np.reshape(RGB, (2, 3, 3)) + HCL = np.reshape(HCL, (2, 3, 3)) + np.testing.assert_almost_equal(RGB_to_HCL(RGB), HCL, decimal=7) + + def test_domain_range_scale_RGB_to_HCL(self): + """ + Test :func:`colour.models.rgb.cylindrical.RGB_to_HCL` definition + domain and range scale support. + """ + + RGB = np.array([0.45620519, 0.03081071, 0.04091952]) + HCL = RGB_to_HCL(RGB) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + RGB_to_HCL(RGB * factor), HCL * factor, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_RGB_to_HCL(self): + """ + Test :func:`colour.models.rgb.cylindrical.RGB_to_HCL` definition nan + support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + RGB = np.array(case) + RGB_to_HCL(RGB) + + +class TestHCL_to_RGB(unittest.TestCase): + """ + Define :func:`colour.models.rgb.cylindrical.HCL_to_RGB` definition unit + tests methods. + """ + + def test_HCL_to_RGB(self): + """Test :func:`colour.models.rgb.cylindrical.HCL_to_RGB` definition.""" + + np.testing.assert_almost_equal( + HCL_to_RGB(np.array([-0.03167854, 0.28417150, 0.22859647])), + np.array([0.45620333, 0.03081048, 0.04091925]), + decimal=7, + ) + + np.testing.assert_almost_equal( + HCL_to_RGB(np.array([1.00000000, 2.00000000, 0.50000000])), + np.array([0.92186029, 0.71091922, -2.26364935]), + decimal=7, + ) + + np.testing.assert_almost_equal( + HCL_to_RGB(np.array([2.00000000, 1.00000000, 0.50000000])), + np.array([-0.31368585, 1.00732462, -0.51534497]), + decimal=7, + ) + + np.testing.assert_almost_equal( + HCL_to_RGB(np.array([0.50000000, 1.00000000, 2.00000000])), + np.array([3.88095422, 3.11881934, 2.40881719]), + decimal=7, + ) + + def test_n_dimensional_HCL_to_RGB(self): + """ + Test :func:`colour.models.rgb.cylindrical.HCL_to_RGB` definition + n-dimensional arrays support. + """ + + HCL = np.array([0.99603944, 0.87347144, 0.24350795]) + RGB = HCL_to_RGB(HCL) + + HCL = np.tile(HCL, (6, 1)) + RGB = np.tile(RGB, (6, 1)) + np.testing.assert_almost_equal(HCL_to_RGB(HCL), RGB, decimal=7) + + HCL = np.reshape(HCL, (2, 3, 3)) + RGB = np.reshape(RGB, (2, 3, 3)) + np.testing.assert_almost_equal(HCL_to_RGB(HCL), RGB, decimal=7) + + def test_domain_range_scale_HCL_to_RGB(self): + """ + Test :func:`colour.models.rgb.cylindrical.HCL_to_RGB` definition + domain and range scale support. + """ + + HCL = np.array([0.99603944, 0.87347144, 0.24350795]) + RGB = HCL_to_RGB(HCL) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + HCL_to_RGB(HCL * factor), RGB * factor, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_HCL_to_RGB(self): + """ + Test :func:`colour.models.rgb.cylindrical.HCL_to_RGB` definition nan + support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + HCL = np.array(case) + HCL_to_RGB(HCL) + + +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/tests/test_derivation.py b/colour/models/rgb/tests/test_derivation.py index ddf6fcffff..dfdd9afe96 100644 --- a/colour/models/rgb/tests/test_derivation.py +++ b/colour/models/rgb/tests/test_derivation.py @@ -1,59 +1,61 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.rgb.derivation` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.rgb.derivation` module.""" import numpy as np import re import unittest from itertools import permutations -from six import text_type from colour.models import ( - normalised_primary_matrix, chromatically_adapted_primaries, - primaries_whitepoint, RGB_luminance_equation, RGB_luminance) + normalised_primary_matrix, + chromatically_adapted_primaries, + primaries_whitepoint, + RGB_luminance_equation, + RGB_luminance, +) from colour.models.rgb.derivation import xy_to_z from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'Testxy_to_z', 'TestNormalisedPrimaryMatrix', - 'TestChromaticallyAdaptedPrimaries', 'TestPrimariesWhitepoint', - 'TestRGBLuminanceEquation', 'TestRGBLuminance' + "Testxy_to_z", + "TestNormalisedPrimaryMatrix", + "TestChromaticallyAdaptedPrimaries", + "TestPrimariesWhitepoint", + "TestRGBLuminanceEquation", + "TestRGBLuminance", ] class Testxy_to_z(unittest.TestCase): """ - Defines :func:`colour.models.rgb.derivation.xy_to_z` definition unit + Define :func:`colour.models.rgb.derivation.xy_to_z` definition unit tests methods. """ def test_xy_to_z(self): - """ - Tests :func:`colour.models.rgb.derivation.xy_to_z` definition. - """ + """Test :func:`colour.models.rgb.derivation.xy_to_z` definition.""" np.testing.assert_almost_equal( - xy_to_z(np.array([0.2500, 0.2500])), 0.50000000, decimal=7) + xy_to_z(np.array([0.2500, 0.2500])), 0.50000000, decimal=7 + ) np.testing.assert_almost_equal( - xy_to_z(np.array([0.0001, -0.0770])), 1.07690000, decimal=7) + xy_to_z(np.array([0.0001, -0.0770])), 1.07690000, decimal=7 + ) np.testing.assert_almost_equal( - xy_to_z(np.array([0.0000, 1.0000])), 0.00000000, decimal=7) + xy_to_z(np.array([0.0000, 1.0000])), 0.00000000, decimal=7 + ) def test_n_dimensional_xy_to_z(self): """ - Tests :func:`colour.models.rgb.derivation.xy_to_z` definition + Test :func:`colour.models.rgb.derivation.xy_to_z` definition n-dimensional arrays support. """ @@ -74,7 +76,7 @@ def test_n_dimensional_xy_to_z(self): @ignore_numpy_errors def test_nan_xy_to_z(self): """ - Tests :func:`colour.models.rgb.derivation.xy_to_z` definition nan + Test :func:`colour.models.rgb.derivation.xy_to_z` definition nan support. """ @@ -86,43 +88,52 @@ def test_nan_xy_to_z(self): class TestNormalisedPrimaryMatrix(unittest.TestCase): """ - Defines :func:`colour.models.rgb.derivation.normalised_primary_matrix` + Define :func:`colour.models.rgb.derivation.normalised_primary_matrix` definition unit tests methods. """ def test_normalised_primary_matrix(self): """ - Tests :func:`colour.models.rgb.derivation.normalised_primary_matrix` + Test :func:`colour.models.rgb.derivation.normalised_primary_matrix` definition. """ np.testing.assert_almost_equal( normalised_primary_matrix( np.array( - [0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700]), - np.array([0.32168, 0.33767])), - np.array([ - [0.95255240, 0.00000000, 0.00009368], - [0.34396645, 0.72816610, -0.07213255], - [0.00000000, 0.00000000, 1.00882518], - ]), - decimal=7) + [0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700] + ), + np.array([0.32168, 0.33767]), + ), + np.array( + [ + [0.95255240, 0.00000000, 0.00009368], + [0.34396645, 0.72816610, -0.07213255], + [0.00000000, 0.00000000, 1.00882518], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( normalised_primary_matrix( np.array([0.640, 0.330, 0.300, 0.600, 0.150, 0.060]), - np.array([0.3127, 0.3290])), - np.array([ - [0.41239080, 0.35758434, 0.18048079], - [0.21263901, 0.71516868, 0.07219232], - [0.01933082, 0.11919478, 0.95053215], - ]), - decimal=7) + np.array([0.3127, 0.3290]), + ), + np.array( + [ + [0.41239080, 0.35758434, 0.18048079], + [0.21263901, 0.71516868, 0.07219232], + [0.01933082, 0.11919478, 0.95053215], + ] + ), + decimal=7, + ) @ignore_numpy_errors def test_nan_normalised_primary_matrix(self): """ - Tests :func:`colour.models.rgb.derivation.normalised_primary_matrix` + Test :func:`colour.models.rgb.derivation.normalised_primary_matrix` definition nan support. """ @@ -139,55 +150,71 @@ def test_nan_normalised_primary_matrix(self): class TestChromaticallyAdaptedPrimaries(unittest.TestCase): """ - Defines :func:`colour.models.rgb.derivation.\ + Define :func:`colour.models.rgb.derivation.\ chromatically_adapted_primaries` definition unit tests methods. """ def test_chromatically_adapted_primaries(self): """ - Tests :func:`colour.models.rgb.derivation.\ + Test :func:`colour.models.rgb.derivation.\ chromatically_adapted_primaries` definition. """ np.testing.assert_almost_equal( chromatically_adapted_primaries( np.array( - [0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700]), - np.array([0.32168, 0.33767]), np.array([0.34570, 0.35850])), - np.array([ - [0.73431182, 0.26694964], - [0.02211963, 0.98038009], - [-0.05880375, -0.12573056], - ]), - decimal=7) + [0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700] + ), + np.array([0.32168, 0.33767]), + np.array([0.34570, 0.35850]), + ), + np.array( + [ + [0.73431182, 0.26694964], + [0.02211963, 0.98038009], + [-0.05880375, -0.12573056], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( chromatically_adapted_primaries( np.array([0.640, 0.330, 0.300, 0.600, 0.150, 0.060]), - np.array([0.31270, 0.32900]), np.array([0.34570, 0.35850])), - np.array([ - [0.64922534, 0.33062196], - [0.32425276, 0.60237128], - [0.15236177, 0.06118676], - ]), - decimal=7) + np.array([0.31270, 0.32900]), + np.array([0.34570, 0.35850]), + ), + np.array( + [ + [0.64922534, 0.33062196], + [0.32425276, 0.60237128], + [0.15236177, 0.06118676], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( chromatically_adapted_primaries( np.array([0.640, 0.330, 0.300, 0.600, 0.150, 0.060]), - np.array([0.31270, 0.32900]), np.array([0.34570, 0.35850]), - 'Bradford'), - np.array([ - [0.64844144, 0.33085331], - [0.32119518, 0.59784434], - [0.15589322, 0.06604921], - ]), - decimal=7) + np.array([0.31270, 0.32900]), + np.array([0.34570, 0.35850]), + "Bradford", + ), + np.array( + [ + [0.64844144, 0.33085331], + [0.32119518, 0.59784434], + [0.15589322, 0.06604921], + ] + ), + decimal=7, + ) @ignore_numpy_errors def test_nan_chromatically_adapted_primaries(self): """ - Tests :func:`colour.models.rgb.derivation.\ + Test :func:`colour.models.rgb.derivation.\ chromatically_adapted_primaries` definition nan support. """ @@ -201,54 +228,68 @@ def test_nan_chromatically_adapted_primaries(self): class TestPrimariesWhitepoint(unittest.TestCase): """ - Defines :func:`colour.models.rgb.derivation.primaries_whitepoint` + Define :func:`colour.models.rgb.derivation.primaries_whitepoint` definition unit tests methods. """ def test_primaries_whitepoint(self): """ - Tests :func:`colour.models.rgb.derivation.primaries_whitepoint` + Test :func:`colour.models.rgb.derivation.primaries_whitepoint` definition. """ P, W = primaries_whitepoint( - np.array([ - [0.95255240, 0.00000000, 0.00009368], - [0.34396645, 0.72816610, -0.07213255], - [0.00000000, 0.00000000, 1.00882518], - ])) + np.array( + [ + [0.95255240, 0.00000000, 0.00009368], + [0.34396645, 0.72816610, -0.07213255], + [0.00000000, 0.00000000, 1.00882518], + ] + ) + ) np.testing.assert_almost_equal( P, - np.array([ - [0.73470, 0.26530], - [0.00000, 1.00000], - [0.00010, -0.07700], - ]), - decimal=7) + np.array( + [ + [0.73470, 0.26530], + [0.00000, 1.00000], + [0.00010, -0.07700], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - W, np.array([0.32168, 0.33767]), decimal=7) + W, np.array([0.32168, 0.33767]), decimal=7 + ) P, W = primaries_whitepoint( - np.array([ - [0.41240000, 0.35760000, 0.18050000], - [0.21260000, 0.71520000, 0.07220000], - [0.01930000, 0.11920000, 0.95050000], - ])) + np.array( + [ + [0.41240000, 0.35760000, 0.18050000], + [0.21260000, 0.71520000, 0.07220000], + [0.01930000, 0.11920000, 0.95050000], + ] + ) + ) np.testing.assert_almost_equal( P, - np.array([ - [0.64007450, 0.32997051], - [0.30000000, 0.60000000], - [0.15001662, 0.06000665], - ]), - decimal=7) + np.array( + [ + [0.64007450, 0.32997051], + [0.30000000, 0.60000000], + [0.15001662, 0.06000665], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - W, np.array([0.31271591, 0.32900148]), decimal=7) + W, np.array([0.31271591, 0.32900148]), decimal=7 + ) @ignore_numpy_errors def test_nan_primaries_whitepoint(self): """ - Tests :func:`colour.models.rgb.derivation.primaries_whitepoint` + Test :func:`colour.models.rgb.derivation.primaries_whitepoint` definition nan support. """ @@ -261,43 +302,51 @@ def test_nan_primaries_whitepoint(self): class TestRGBLuminanceEquation(unittest.TestCase): """ - Defines :func:`colour.models.rgb.derivation.RGB_luminance_equation` + Define :func:`colour.models.rgb.derivation.RGB_luminance_equation` definition unit tests methods. """ def test_RGB_luminance_equation(self): """ - Tests :func:`colour.models.rgb.derivation.RGB_luminance_equation` + Test :func:`colour.models.rgb.derivation.RGB_luminance_equation` definition. """ self.assertIsInstance( RGB_luminance_equation( np.array( - [0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700]), - np.array([0.32168, 0.33767])), text_type) + [0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700] + ), + np.array([0.32168, 0.33767]), + ), + str, + ) # TODO: Simplify that monster. pattern = ( - 'Y\\s?=\\s?[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?.' - '\\(R\\)\\s?[+-]\\s?[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?.' - '\\(G\\)\\s?[+-]\\s?[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?.' - '\\(B\\)') + "Y\\s?=\\s?[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?." + "\\(R\\)\\s?[+-]\\s?[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?." + "\\(G\\)\\s?[+-]\\s?[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?." + "\\(B\\)" + ) P = np.array([0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700]) self.assertTrue( - re.match(pattern, - RGB_luminance_equation(P, np.array([0.32168, 0.33767])))) + re.match( + pattern, + RGB_luminance_equation(P, np.array([0.32168, 0.33767])), + ) + ) class TestRGBLuminance(unittest.TestCase): """ - Defines :func:`colour.models.rgb.derivation.RGB_luminance` definition + Define :func:`colour.models.rgb.derivation.RGB_luminance` definition unit tests methods. """ def test_RGB_luminance(self): """ - Tests :func:`colour.models.rgb.derivation.RGB_luminance` + Test :func:`colour.models.rgb.derivation.RGB_luminance` definition. """ @@ -305,36 +354,46 @@ def test_RGB_luminance(self): RGB_luminance( np.array([0.18, 0.18, 0.18]), np.array( - [0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700]), - np.array([0.32168, 0.33767])), + [0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700] + ), + np.array([0.32168, 0.33767]), + ), 0.18000000, - places=7) + places=7, + ) self.assertAlmostEqual( RGB_luminance( np.array([0.21959402, 0.06986677, 0.04703877]), np.array( - [0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700]), - np.array([0.32168, 0.33767])), + [0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700] + ), + np.array([0.32168, 0.33767]), + ), 0.123014562384318, - places=7) + places=7, + ) self.assertAlmostEqual( RGB_luminance( np.array([0.45620519, 0.03081071, 0.04091952]), np.array([0.6400, 0.3300, 0.3000, 0.6000, 0.1500, 0.0600]), - np.array([0.31270, 0.32900])), + np.array([0.31270, 0.32900]), + ), 0.121995947729870, - places=7) + places=7, + ) def test_n_dimensional_RGB_luminance(self): """ - Tests :func:`colour.models.rgb.derivation.RGB_luminance` definition + Test :func:`colour.models.rgb.derivation.RGB_luminance` definition n_dimensional arrays support. """ - RGB = np.array([0.18, 0.18, 0.18]), - P = np.array([0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700]), + RGB = (np.array([0.18, 0.18, 0.18]),) + P = ( + np.array([0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700]), + ) W = np.array([0.32168, 0.33767]) Y = RGB_luminance(RGB, P, W) @@ -349,7 +408,7 @@ def test_n_dimensional_RGB_luminance(self): @ignore_numpy_errors def test_nan_RGB_luminance(self): """ - Tests :func:`colour.models.rgb.derivation.RGB_luminance` + Test :func:`colour.models.rgb.derivation.RGB_luminance` definition nan support. """ @@ -365,5 +424,5 @@ def test_nan_RGB_luminance(self): pass -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/tests/test_hanbury2003.py b/colour/models/rgb/tests/test_hanbury2003.py new file mode 100644 index 0000000000..89b6aa5ace --- /dev/null +++ b/colour/models/rgb/tests/test_hanbury2003.py @@ -0,0 +1,172 @@ +"""Defines unit tests for :mod:`colour.models.rgb.hanbury2003` module.""" + +import numpy as np +import unittest +from itertools import permutations + +from colour.models.rgb import RGB_to_IHLS, IHLS_to_RGB +from colour.utilities import domain_range_scale, ignore_numpy_errors + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestRGB_to_IHLS", + "TestIHLS_to_RGB", +] + + +class TestRGB_to_IHLS(unittest.TestCase): + """ + Define :func:`colour.models.rgb.hanbury2003.RGB_to_IHLS` definition unit + tests methods. + """ + + def test_RGB_to_IHLS(self): + """Test :func:`colour.models.rgb.hanbury2003.RGB_to_IHLS` definition.""" + + np.testing.assert_almost_equal( + RGB_to_IHLS(np.array([0.45620519, 0.03081071, 0.04091952])), + np.array([6.26236117, 0.12197943, 0.42539448]), + decimal=7, + ) + + np.testing.assert_almost_equal( + RGB_to_IHLS(np.array([0.00000000, 0.00000000, 0.00000000])), + np.array([0.00000000, 0.00000000, 0.00000000]), + decimal=7, + ) + + np.testing.assert_almost_equal( + RGB_to_IHLS(np.array([1.00000000, 1.00000000, 1.00000000])), + np.array([0.00000000, 1.00000000, 0.00000000]), + decimal=7, + ) + + def test_n_dimensional_RGB_to_IHLS(self): + """ + Test :func:`colour.models.rgb.hanbury2003.RGB_to_IHLS` definition + n-dimensional arrays support. + """ + + RGB = np.array([0.45620519, 0.03081071, 0.04091952]) + HYS = RGB_to_IHLS(RGB) + + RGB = np.tile(RGB, (6, 1)) + HYS = np.tile(HYS, (6, 1)) + np.testing.assert_almost_equal(RGB_to_IHLS(RGB), HYS, decimal=7) + + RGB = np.reshape(RGB, (2, 3, 3)) + HYS = np.reshape(HYS, (2, 3, 3)) + np.testing.assert_almost_equal(RGB_to_IHLS(RGB), HYS, decimal=7) + + def test_domain_range_scale_RGB_to_IHLS(self): + """ + Test :func:`colour.models.rgb.hanbury2003.RGB_to_IHLS` definition + domain and range scale support. + """ + + RGB = np.array([0.45620519, 0.03081071, 0.04091952]) + HYS = RGB_to_IHLS(RGB) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + RGB_to_IHLS(RGB * factor), HYS * factor, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_RGB_to_IHLS(self): + """ + Test :func:`colour.models.rgb.hanbury2003.RGB_to_IHLS` definition nan + support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + RGB = np.array(case) + RGB_to_IHLS(RGB) + + +class TestIHLS_to_RGB(unittest.TestCase): + """ + Define :func:`colour.models.rgb.hanbury2003.RGB_to_IHLS` definition unit + tests methods. + """ + + def test_IHLS_to_RGB(self): + """Test :func:`colour.models.rgb.hanbury2003.IHLS_to_RGB` definition.""" + + np.testing.assert_almost_equal( + IHLS_to_RGB(np.array([6.26236117, 0.12197943, 0.42539448])), + np.array([0.45620519, 0.03081071, 0.04091952]), + decimal=7, + ) + + np.testing.assert_almost_equal( + IHLS_to_RGB(np.array([0.00000000, 0.00000000, 0.00000000])), + np.array([0.00000000, 0.00000000, 0.00000000]), + decimal=7, + ) + + np.testing.assert_almost_equal( + IHLS_to_RGB(np.array([0.00000000, 1.00000000, 0.00000000])), + np.array([1.00000000, 1.00000000, 1.00000000]), + decimal=7, + ) + + def test_n_dimensional_IHLS_to_RGB(self): + """ + Test :func:`colour.models.rgb.hanbury2003.IHLS_to_RGB` definition + n-dimensional arrays support. + """ + + HYS = np.array([6.26236117, 0.12197943, 0.42539448]) + RGB = IHLS_to_RGB(HYS) + + HYS = np.tile(HYS, (6, 1)) + RGB = np.tile(RGB, (6, 1)) + np.testing.assert_almost_equal(IHLS_to_RGB(HYS), RGB, decimal=7) + + HYS = np.reshape(HYS, (2, 3, 3)) + RGB = np.reshape(RGB, (2, 3, 3)) + np.testing.assert_almost_equal(IHLS_to_RGB(HYS), RGB, decimal=7) + + def test_domain_range_scale_IHLS_to_RGB(self): + """ + Test :func:`colour.models.rgb.hanbury2003.IHLS_to_RGB` definition + domain and range scale support. + """ + + HYS = np.array([6.26236117, 0.12197943, 0.42539448]) + RGB = IHLS_to_RGB(HYS) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + IHLS_to_RGB(HYS * factor), RGB * factor, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_IHLS_to_RGB(self): + """ + Test :func:`colour.models.rgb.hanbury2003.IHLS_to_RGB` definition nan + support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + HYS = np.array(case) + IHLS_to_RGB(HYS) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/models/rgb/tests/test_ictcp.py b/colour/models/rgb/tests/test_ictcp.py index 0a9218c9df..924818c4ec 100644 --- a/colour/models/rgb/tests/test_ictcp.py +++ b/colour/models/rgb/tests/test_ictcp.py @@ -1,89 +1,136 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.rgb.ictpt` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.rgb.ictcp` module.""" import numpy as np import unittest from itertools import permutations -from colour.models.rgb import RGB_to_ICTCP, ICTCP_to_RGB +from colour.models.rgb import ( + RGB_to_ICtCp, + ICtCp_to_RGB, + XYZ_to_ICtCp, + ICtCp_to_XYZ, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestRGB_to_ICTCP', 'TestICTCP_to_RGB'] +__all__ = [ + "TestRGB_to_ICtCp", + "TestICtCp_to_RGB", + "TestXYZ_to_ICtCp", + "TestICtCp_to_XYZ", +] -class TestRGB_to_ICTCP(unittest.TestCase): +class TestRGB_to_ICtCp(unittest.TestCase): """ - Defines :func:`colour.models.rgb.ictpt.TestRGB_to_ICTCP` definition unit + Define :func:`colour.models.rgb.ictcp.TestRGB_to_ICtCp` definition unit tests methods. """ - def test_RGB_to_ICTCP(self): - """ - Tests :func:`colour.models.rgb.ictpt.RGB_to_ICTCP` definition. - """ + def test_RGB_to_ICtCp(self): + """Test :func:`colour.models.rgb.ictcp.RGB_to_ICtCp` definition.""" np.testing.assert_almost_equal( - RGB_to_ICTCP(np.array([0.45620519, 0.03081071, 0.04091952])), + RGB_to_ICtCp(np.array([0.45620519, 0.03081071, 0.04091952])), np.array([0.07351364, 0.00475253, 0.09351596]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - RGB_to_ICTCP(np.array([0.45620519, 0.03081071, 0.04091952]), 4000), + RGB_to_ICtCp( + np.array([0.45620519, 0.03081071, 0.04091952]), L_p=4000 + ), np.array([0.10516931, 0.00514031, 0.12318730]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - RGB_to_ICTCP(np.array([0.45620519, 0.03081071, 0.04091952]), 1000), + RGB_to_ICtCp( + np.array([0.45620519, 0.03081071, 0.04091952]), L_p=1000 + ), np.array([0.17079612, 0.00485580, 0.17431356]), - decimal=7) + decimal=7, + ) + + np.testing.assert_almost_equal( + RGB_to_ICtCp( + np.array([0.45620519, 0.03081071, 0.04091952]), + method="ITU-R BT.2100-1 PQ", + ), + np.array([0.07351364, 0.00475253, 0.09351596]), + decimal=7, + ) + + np.testing.assert_almost_equal( + RGB_to_ICtCp( + np.array([0.45620519, 0.03081071, 0.04091952]), + method="ITU-R BT.2100-2 PQ", + ), + np.array([0.07351364, 0.00475253, 0.09351596]), + decimal=7, + ) + + np.testing.assert_almost_equal( + RGB_to_ICtCp( + np.array([0.45620519, 0.03081071, 0.04091952]), + method="ITU-R BT.2100-1 HLG", + ), + np.array([0.62567899, -0.03622422, 0.67786522]), + decimal=7, + ) - def test_n_dimensional_RGB_to_ICTCP(self): + np.testing.assert_almost_equal( + RGB_to_ICtCp( + np.array([0.45620519, 0.03081071, 0.04091952]), + method="ITU-R BT.2100-2 HLG", + ), + np.array([0.62567899, -0.01984490, 0.35911259]), + decimal=7, + ) + + def test_n_dimensional_RGB_to_ICtCp(self): """ - Tests :func:`colour.models.rgb.ictpt.RGB_to_ICTCP` definition + Test :func:`colour.models.rgb.ictcp.RGB_to_ICtCp` definition n-dimensional support. """ RGB = np.array([0.45620519, 0.03081071, 0.04091952]) - ICTCP = RGB_to_ICTCP(RGB) + ICtCp = RGB_to_ICtCp(RGB) RGB = np.tile(RGB, (6, 1)) - ICTCP = np.tile(ICTCP, (6, 1)) - np.testing.assert_almost_equal(RGB_to_ICTCP(RGB), ICTCP, decimal=7) + ICtCp = np.tile(ICtCp, (6, 1)) + np.testing.assert_almost_equal(RGB_to_ICtCp(RGB), ICtCp, decimal=7) RGB = np.reshape(RGB, (2, 3, 3)) - ICTCP = np.reshape(ICTCP, (2, 3, 3)) - np.testing.assert_almost_equal(RGB_to_ICTCP(RGB), ICTCP, decimal=7) + ICtCp = np.reshape(ICtCp, (2, 3, 3)) + np.testing.assert_almost_equal(RGB_to_ICtCp(RGB), ICtCp, decimal=7) - def test_domain_range_scale_RGB_to_ICTCP(self): + def test_domain_range_scale_RGB_to_ICtCp(self): """ - Tests :func:`colour.models.rgb.ictpt.RGB_to_ICTCP` definition domain + Test :func:`colour.models.rgb.ictcp.RGB_to_ICtCp` definition domain and range scale support. """ RGB = np.array([0.45620519, 0.03081071, 0.04091952]) - ICTCP = RGB_to_ICTCP(RGB) + ICtCp = RGB_to_ICtCp(RGB) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - RGB_to_ICTCP(RGB * factor), ICTCP * factor, decimal=7) + RGB_to_ICtCp(RGB * factor), ICtCp * factor, decimal=7 + ) @ignore_numpy_errors - def test_nan_RGB_to_ICTCP(self): + def test_nan_RGB_to_ICtCp(self): """ - Tests :func:`colour.models.rgb.ictpt.RGB_to_ICTCP` definition nan + Test :func:`colour.models.rgb.ictcp.RGB_to_ICtCp` definition nan support. """ @@ -91,80 +138,388 @@ def test_nan_RGB_to_ICTCP(self): cases = set(permutations(cases * 3, r=3)) for case in cases: RGB = np.array(case) - RGB_to_ICTCP(RGB) + RGB_to_ICtCp(RGB) -class TestICTCP_to_RGB(unittest.TestCase): +class TestICtCp_to_RGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.ictpt.ICTCP_to_RGB` definition unit tests + Define :func:`colour.models.rgb.ictcp.ICtCp_to_RGB` definition unit tests methods. """ - def test_ICTCP_to_RGB(self): - """ - Tests :func:`colour.models.rgb.ictpt.ICTCP_to_RGB` definition. - """ + def test_ICtCp_to_RGB(self): + """Test :func:`colour.models.rgb.ictcp.ICtCp_to_RGB` definition.""" np.testing.assert_almost_equal( - ICTCP_to_RGB(np.array([0.07351364, 0.00475253, 0.09351596])), + ICtCp_to_RGB(np.array([0.07351364, 0.00475253, 0.09351596])), np.array([0.45620519, 0.03081071, 0.04091952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - ICTCP_to_RGB(np.array([0.10516931, 0.00514031, 0.12318730]), 4000), + ICtCp_to_RGB( + np.array([0.10516931, 0.00514031, 0.12318730]), L_p=4000 + ), np.array([0.45620519, 0.03081071, 0.04091952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - ICTCP_to_RGB(np.array([0.17079612, 0.00485580, 0.17431356]), 1000), + ICtCp_to_RGB( + np.array([0.17079612, 0.00485580, 0.17431356]), L_p=1000 + ), np.array([0.45620519, 0.03081071, 0.04091952]), - decimal=7) + decimal=7, + ) - def test_n_dimensional_ICTCP_to_RGB(self): + np.testing.assert_almost_equal( + ICtCp_to_RGB( + np.array([0.07351364, 0.00475253, 0.09351596]), + method="ITU-R BT.2100-1 PQ", + ), + np.array([0.45620519, 0.03081071, 0.04091952]), + decimal=7, + ) + + np.testing.assert_almost_equal( + ICtCp_to_RGB( + np.array([0.07351364, 0.00475253, 0.09351596]), + method="ITU-R BT.2100-2 PQ", + ), + np.array([0.45620519, 0.03081071, 0.04091952]), + decimal=7, + ) + + np.testing.assert_almost_equal( + ICtCp_to_RGB( + np.array([0.62567899, -0.03622422, 0.67786522]), + method="ITU-R BT.2100-1 HLG", + ), + np.array([0.45620519, 0.03081071, 0.04091952]), + decimal=7, + ) + + np.testing.assert_almost_equal( + ICtCp_to_RGB( + np.array([0.62567899, -0.01984490, 0.35911259]), + method="ITU-R BT.2100-2 HLG", + ), + np.array([0.45620519, 0.03081071, 0.04091952]), + decimal=7, + ) + + def test_n_dimensional_ICtCp_to_RGB(self): """ - Tests :func:`colour.models.rgb.ictpt.ICTCP_to_RGB` definition + Test :func:`colour.models.rgb.ictcp.ICtCp_to_RGB` definition n-dimensional support. """ - ICTCP = np.array([0.07351364, 0.00475253, 0.09351596]) - RGB = ICTCP_to_RGB(ICTCP) + ICtCp = np.array([0.07351364, 0.00475253, 0.09351596]) + RGB = ICtCp_to_RGB(ICtCp) - ICTCP = np.tile(ICTCP, (6, 1)) + ICtCp = np.tile(ICtCp, (6, 1)) RGB = np.tile(RGB, (6, 1)) - np.testing.assert_almost_equal(ICTCP_to_RGB(ICTCP), RGB, decimal=7) + np.testing.assert_almost_equal(ICtCp_to_RGB(ICtCp), RGB, decimal=7) - ICTCP = np.reshape(ICTCP, (2, 3, 3)) + ICtCp = np.reshape(ICtCp, (2, 3, 3)) RGB = np.reshape(RGB, (2, 3, 3)) - np.testing.assert_almost_equal(ICTCP_to_RGB(ICTCP), RGB, decimal=7) + np.testing.assert_almost_equal(ICtCp_to_RGB(ICtCp), RGB, decimal=7) + + def test_domain_range_scale_ICtCp_to_RGB(self): + """ + Test :func:`colour.models.rgb.ictcp.ICtCp_to_RGB` definition domain + and range scale support. + """ + + ICtCp = np.array([0.07351364, 0.00475253, 0.09351596]) + RGB = ICtCp_to_RGB(ICtCp) + + d_r = (("reference", 1), ("1", 1), ("100", 1)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + ICtCp_to_RGB(ICtCp * factor), RGB * factor, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_ICtCp_to_RGB(self): + """ + Test :func:`colour.models.rgb.ictcp.ICtCp_to_RGB` definition nan + support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + ICtCp = np.array(case) + ICtCp_to_RGB(ICtCp) + + +class TestXYZ_to_ICtCp(unittest.TestCase): + """ + Define :func:`colour.models.rgb.ictcp.TestXYZ_to_ICtCp` definition unit + tests methods. + """ + + def test_XYZ_to_ICtCp(self): + """Test :func:`colour.models.rgb.ictcp.XYZ_to_ICtCp` definition.""" + + np.testing.assert_almost_equal( + XYZ_to_ICtCp(np.array([0.20654008, 0.12197225, 0.05136952])), + np.array([0.06858097, -0.00283842, 0.06020983]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_ICtCp( + np.array([0.20654008, 0.12197225, 0.05136952]), + np.array([0.34570, 0.35850]), + ), + np.array([0.06792437, 0.00452089, 0.05514480]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_ICtCp( + np.array([0.20654008, 0.12197225, 0.05136952]), + np.array([0.34570, 0.35850]), + chromatic_adaptation_transform="Bradford", + ), + np.array([0.06783951, 0.00476111, 0.05523093]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_ICtCp( + np.array([0.20654008, 0.12197225, 0.05136952]), L_p=4000 + ), + np.array([0.09871102, -0.00447247, 0.07984812]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_ICtCp( + np.array([0.20654008, 0.12197225, 0.05136952]), L_p=1000 + ), + np.array([0.16173872, -0.00792543, 0.11409458]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_ICtCp( + np.array([0.20654008, 0.12197225, 0.05136952]), + method="ITU-R BT.2100-1 PQ", + ), + np.array([0.06858097, -0.00283842, 0.06020983]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_ICtCp( + np.array([0.20654008, 0.12197225, 0.05136952]), + method="ITU-R BT.2100-2 PQ", + ), + np.array([0.06858097, -0.00283842, 0.06020983]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_ICtCp( + np.array([0.20654008, 0.12197225, 0.05136952]), + method="ITU-R BT.2100-1 HLG", + ), + np.array([0.59242792, -0.06824263, 0.47421473]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_ICtCp( + np.array([0.20654008, 0.12197225, 0.05136952]), + method="ITU-R BT.2100-2 HLG", + ), + np.array([0.59242792, -0.03740730, 0.25122675]), + decimal=7, + ) + + def test_n_dimensional_XYZ_to_ICtCp(self): + """ + Test :func:`colour.models.rgb.ictcp.XYZ_to_ICtCp` definition + n-dimensional support. + """ + + XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + ICtCp = XYZ_to_ICtCp(XYZ) + + XYZ = np.tile(XYZ, (6, 1)) + ICtCp = np.tile(ICtCp, (6, 1)) + np.testing.assert_almost_equal(XYZ_to_ICtCp(XYZ), ICtCp, decimal=7) + + XYZ = np.reshape(XYZ, (2, 3, 3)) + ICtCp = np.reshape(ICtCp, (2, 3, 3)) + np.testing.assert_almost_equal(XYZ_to_ICtCp(XYZ), ICtCp, decimal=7) + + def test_domain_range_scale_XYZ_to_ICtCp(self): + """ + Test :func:`colour.models.rgb.ictcp.XYZ_to_ICtCp` definition domain + and range scale support. + """ + + XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + ICtCp = XYZ_to_ICtCp(XYZ) + + d_r = (("reference", 1), ("1", 1), ("100", 1)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + XYZ_to_ICtCp(XYZ * factor), ICtCp * factor, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_XYZ_to_ICtCp(self): + """ + Test :func:`colour.models.rgb.ictcp.XYZ_to_ICtCp` definition nan + support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + XYZ = np.array(case) + XYZ_to_ICtCp(XYZ) + + +class TestICtCp_to_XYZ(unittest.TestCase): + """ + Define :func:`colour.models.rgb.ictcp.ICtCp_to_XYZ` definition unit tests + methods. + """ + + def test_ICtCp_to_XYZ(self): + """Test :func:`colour.models.rgb.ictcp.ICtCp_to_XYZ` definition.""" + + np.testing.assert_almost_equal( + ICtCp_to_XYZ(np.array([0.06858097, -0.00283842, 0.06020983])), + np.array([0.20654008, 0.12197225, 0.05136952]), + decimal=7, + ) + + np.testing.assert_almost_equal( + ICtCp_to_XYZ( + np.array([0.06792437, 0.00452089, 0.05514480]), + np.array([0.34570, 0.35850]), + ), + np.array([0.20654008, 0.12197225, 0.05136952]), + decimal=7, + ) + + np.testing.assert_almost_equal( + ICtCp_to_XYZ( + np.array([0.06783951, 0.00476111, 0.05523093]), + np.array([0.34570, 0.35850]), + chromatic_adaptation_transform="Bradford", + ), + np.array([0.20654008, 0.12197225, 0.05136952]), + decimal=7, + ) + + np.testing.assert_almost_equal( + ICtCp_to_XYZ( + np.array([0.09871102, -0.00447247, 0.07984812]), L_p=4000 + ), + np.array([0.20654008, 0.12197225, 0.05136952]), + decimal=7, + ) + + np.testing.assert_almost_equal( + ICtCp_to_XYZ( + np.array([0.16173872, -0.00792543, 0.11409458]), L_p=1000 + ), + np.array([0.20654008, 0.12197225, 0.05136952]), + decimal=7, + ) + + np.testing.assert_almost_equal( + ICtCp_to_XYZ( + np.array([0.06858097, -0.00283842, 0.06020983]), + method="ITU-R BT.2100-1 PQ", + ), + np.array([0.20654008, 0.12197225, 0.05136952]), + decimal=7, + ) + + np.testing.assert_almost_equal( + ICtCp_to_XYZ( + np.array([0.06858097, -0.00283842, 0.06020983]), + method="ITU-R BT.2100-2 PQ", + ), + np.array([0.20654008, 0.12197225, 0.05136952]), + decimal=7, + ) + + np.testing.assert_almost_equal( + ICtCp_to_XYZ( + np.array([0.59242792, -0.06824263, 0.47421473]), + method="ITU-R BT.2100-1 HLG", + ), + np.array([0.20654008, 0.12197225, 0.05136952]), + decimal=7, + ) + + np.testing.assert_almost_equal( + ICtCp_to_XYZ( + np.array([0.59242792, -0.03740730, 0.25122675]), + method="ITU-R BT.2100-2 HLG", + ), + np.array([0.20654008, 0.12197225, 0.05136952]), + decimal=7, + ) + + def test_n_dimensional_ICtCp_to_XYZ(self): + """ + Test :func:`colour.models.rgb.ictcp.ICtCp_to_XYZ` definition + n-dimensional support. + """ + + ICtCp = np.array([0.06858097, -0.00283842, 0.06020983]) + XYZ = ICtCp_to_XYZ(ICtCp) + + ICtCp = np.tile(ICtCp, (6, 1)) + XYZ = np.tile(XYZ, (6, 1)) + np.testing.assert_almost_equal(ICtCp_to_XYZ(ICtCp), XYZ, decimal=7) + + ICtCp = np.reshape(ICtCp, (2, 3, 3)) + XYZ = np.reshape(XYZ, (2, 3, 3)) + np.testing.assert_almost_equal(ICtCp_to_XYZ(ICtCp), XYZ, decimal=7) - def test_domain_range_scale_ICTCP_to_RGB(self): + def test_domain_range_scale_ICtCp_to_XYZ(self): """ - Tests :func:`colour.models.rgb.ictpt.ICTCP_to_RGB` definition domain + Test :func:`colour.models.rgb.ictcp.ICtCp_to_XYZ` definition domain and range scale support. """ - ICTCP = np.array([0.07351364, 0.00475253, 0.09351596]) - RGB = ICTCP_to_RGB(ICTCP) + ICtCp = np.array([0.06858097, -0.00283842, 0.06020983]) + XYZ = ICtCp_to_XYZ(ICtCp) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - ICTCP_to_RGB(ICTCP * factor), RGB * factor, decimal=7) + ICtCp_to_XYZ(ICtCp * factor), XYZ * factor, decimal=7 + ) @ignore_numpy_errors - def test_nan_ICTCP_to_RGB(self): + def test_nan_ICtCp_to_XYZ(self): """ - Tests :func:`colour.models.rgb.ictpt.ICTCP_to_RGB` definition nan + Test :func:`colour.models.rgb.ictcp.ICtCp_to_XYZ` definition nan support. """ cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) for case in cases: - ICTCP = np.array(case) - ICTCP_to_RGB(ICTCP) + ICtCp = np.array(case) + ICtCp_to_XYZ(ICtCp) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/tests/test_prismatic.py b/colour/models/rgb/tests/test_prismatic.py index e7d3c7cc58..0a1af08cb3 100644 --- a/colour/models/rgb/tests/test_prismatic.py +++ b/colour/models/rgb/tests/test_prismatic.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.rgb.prismatic` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.rgb.prismatic` module.""" import numpy as np import unittest @@ -12,40 +7,43 @@ from colour.models.rgb import RGB_to_Prismatic, Prismatic_to_RGB from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestRGB_to_Prismatic', 'TestPrismatic_to_RGB'] +__all__ = [ + "TestRGB_to_Prismatic", + "TestPrismatic_to_RGB", +] class TestRGB_to_Prismatic(unittest.TestCase): """ - Defines :func:`colour.models.rgb.prismatic.TestRGB_to_Prismatic` definition + Define :func:`colour.models.rgb.prismatic.TestRGB_to_Prismatic` definition unit tests methods. """ def test_RGB_to_Prismatic(self): - """ - Tests :func:`colour.models.rgb.prismatic.RGB_to_Prismatic` definition. - """ + """Test :func:`colour.models.rgb.prismatic.RGB_to_Prismatic` definition.""" np.testing.assert_almost_equal( RGB_to_Prismatic(np.array([0.0, 0.0, 0.0])), np.array([0.0, 0.0, 0.0, 0.0]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_Prismatic(np.array([0.25, 0.50, 0.75])), np.array([0.7500000, 0.1666667, 0.3333333, 0.5000000]), - decimal=7) + decimal=7, + ) def test_n_dimensional_RGB_to_Prismatic(self): """ - Tests :func:`colour.models.rgb.prismatic.RGB_to_Prismatic` definition + Test :func:`colour.models.rgb.prismatic.RGB_to_Prismatic` definition n-dimensional support. """ @@ -62,23 +60,24 @@ def test_n_dimensional_RGB_to_Prismatic(self): def test_domain_range_scale_RGB_to_Prismatic(self): """ - Tests :func:`colour.models.rgb.prismatic.RGB_to_Prismatic` definition + Test :func:`colour.models.rgb.prismatic.RGB_to_Prismatic` definition domain and range scale support. """ RGB = np.array([0.25, 0.50, 0.75]) Lrgb = RGB_to_Prismatic(RGB) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - RGB_to_Prismatic(RGB * factor), Lrgb * factor, decimal=7) + RGB_to_Prismatic(RGB * factor), Lrgb * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_RGB_to_Prismatic(self): """ - Tests :func:`colour.models.rgb.prismatic.RGB_to_Prismatic` definition + Test :func:`colour.models.rgb.prismatic.RGB_to_Prismatic` definition nan support. """ @@ -91,29 +90,30 @@ def test_nan_RGB_to_Prismatic(self): class TestPrismatic_to_RGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.prismatic.Prismatic_to_RGB` definition + Define :func:`colour.models.rgb.prismatic.Prismatic_to_RGB` definition unit tests methods. """ def test_Prismatic_to_RGB(self): - """ - Tests :func:`colour.models.rgb.prismatic.Prismatic_to_RGB` definition. - """ + """Test :func:`colour.models.rgb.prismatic.Prismatic_to_RGB` definition.""" np.testing.assert_almost_equal( Prismatic_to_RGB(np.array([0.0, 0.0, 0.0, 0.0])), np.array([0.0, 0.0, 0.0]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Prismatic_to_RGB( - np.array([0.7500000, 0.1666667, 0.3333333, 0.5000000])), + np.array([0.7500000, 0.1666667, 0.3333333, 0.5000000]) + ), np.array([0.25, 0.50, 0.75]), - decimal=7) + decimal=7, + ) def test_n_dimensional_Prismatic_to_RGB(self): """ - Tests :func:`colour.models.rgb.prismatic.Prismatic_to_RGB` definition + Test :func:`colour.models.rgb.prismatic.Prismatic_to_RGB` definition n-dimensional support. """ @@ -130,23 +130,24 @@ def test_n_dimensional_Prismatic_to_RGB(self): def test_domain_range_scale_Prismatic_to_RGB(self): """ - Tests :func:`colour.models.rgb.prismatic.Prismatic_to_RGB` definition + Test :func:`colour.models.rgb.prismatic.Prismatic_to_RGB` definition domain and range scale support. """ Lrgb = np.array([0.7500000, 0.1666667, 0.3333333, 0.5000000]) RGB = Prismatic_to_RGB(Lrgb) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - Prismatic_to_RGB(Lrgb * factor), RGB * factor, decimal=7) + Prismatic_to_RGB(Lrgb * factor), RGB * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_Prismatic_to_RGB(self): """ - Tests :func:`colour.models.rgb.prismatic.Prismatic_to_RGB` definition + Test :func:`colour.models.rgb.prismatic.Prismatic_to_RGB` definition nan support. """ @@ -157,5 +158,5 @@ def test_nan_Prismatic_to_RGB(self): Prismatic_to_RGB(Prismatic) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/tests/test_rgb_colourspace.py b/colour/models/rgb/tests/test_rgb_colourspace.py index e812d2c842..f0a7ad7f05 100644 --- a/colour/models/rgb/tests/test_rgb_colourspace.py +++ b/colour/models/rgb/tests/test_rgb_colourspace.py @@ -1,237 +1,109 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.rgb.rgb_colourspace` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.rgb.rgb_colourspace` module.""" import numpy as np -import pickle import re -import six import textwrap import unittest -from copy import deepcopy from itertools import permutations from colour.models import ( - RGB_COLOURSPACES, RGB_Colourspace, XYZ_to_RGB, RGB_to_XYZ, - matrix_RGB_to_RGB, RGB_to_RGB, chromatically_adapted_primaries, - normalised_primary_matrix, eotf_inverse_sRGB, eotf_sRGB) -from colour.utilities import as_int, domain_range_scale, ignore_numpy_errors - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + RGB_COLOURSPACES, + RGB_Colourspace, + XYZ_to_RGB, + RGB_to_XYZ, + matrix_RGB_to_RGB, + RGB_to_RGB, + chromatically_adapted_primaries, + normalised_primary_matrix, + eotf_inverse_sRGB, + eotf_sRGB, +) +from colour.utilities import domain_range_scale, ignore_numpy_errors + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'TestRGB_COLOURSPACES', 'TestRGB_Colourspace', 'TestXYZ_to_RGB', - 'TestRGB_to_XYZ', 'TestMatrix_RGB_to_RGB', 'TestRGB_to_RGB' + "TestRGB_Colourspace", + "TestXYZ_to_RGB", + "TestRGB_to_XYZ", + "TestMatrix_RGB_to_RGB", + "TestRGB_to_RGB", ] -class TestRGB_COLOURSPACES(unittest.TestCase): - """ - Defines :attr:`colour.models.rgb.rgb_colourspace.RGB_COLOURSPACES` - attribute unit tests methods. - """ - - def test_transformation_matrices(self): - """ - Tests the transformations matrices from the - :attr:`colour.models.rgb.rgb_colourspace.RGB_COLOURSPACES` attribute - colourspace models. - """ - - tolerances = { - 'Adobe RGB (1998)': 1e-5, - 'ALEXA Wide Gamut': 1e-6, - 'DJI D-Gamut': 1e-4, - 'ERIMM RGB': 1e-3, - 'ProPhoto RGB': 1e-3, - 'REDWideGamutRGB': 1e-6, - 'RIMM RGB': 1e-3, - 'ROMM RGB': 1e-3, - 'sRGB': 1e-4, - 'V-Gamut': 1e-6, - } - XYZ_r = np.array([0.5, 0.5, 0.5]).reshape([3, 1]) - for colourspace in RGB_COLOURSPACES.values(): - M = normalised_primary_matrix(colourspace.primaries, - colourspace.whitepoint) - - tolerance = tolerances.get(colourspace.name, 1e-7) - np.testing.assert_allclose( - colourspace.matrix_RGB_to_XYZ, - M, - rtol=tolerance, - atol=tolerance, - verbose=False) - - RGB = np.dot(colourspace.matrix_XYZ_to_RGB, XYZ_r) - XYZ = np.dot(colourspace.matrix_RGB_to_XYZ, RGB) - np.testing.assert_allclose( - XYZ_r, XYZ, rtol=tolerance, atol=tolerance, verbose=False) - - # Derived transformation matrices. - colourspace = deepcopy(colourspace) - colourspace.use_derived_transformation_matrices(True) - RGB = np.dot(colourspace.matrix_XYZ_to_RGB, XYZ_r) - XYZ = np.dot(colourspace.matrix_RGB_to_XYZ, RGB) - np.testing.assert_almost_equal(XYZ_r, XYZ, decimal=7) - - def test_cctf(self): - """ - Tests colour component transfer functions from the - :attr:`colour.models.rgb.rgb_colourspace.RGB_COLOURSPACES` attribute - colourspace models. - """ - - ignored_colourspaces = ('ACESproxy', ) - - decimals = {'DJI D-Gamut': 1, 'F-Gamut': 4} - - samples = np.hstack([ - np.linspace(0, 1, as_int(1e5)), - np.linspace(0, 65504, 65504 * 10) - ]) - - for colourspace in RGB_COLOURSPACES.values(): - if colourspace.name in ignored_colourspaces: - continue - - cctf_encoding_s = colourspace.cctf_encoding(samples) - cctf_decoding_s = colourspace.cctf_decoding(cctf_encoding_s) - - np.testing.assert_almost_equal( - samples, - cctf_decoding_s, - decimal=decimals.get(colourspace.name, 7)) - - def test_n_dimensional_cctf(self): - """ - Tests colour component transfer functions from the - :attr:`colour.models.rgb.rgb_colourspace.RGB_COLOURSPACES` attribute - colourspace models n-dimensional arrays support. - """ - - decimals = {'DJI D-Gamut': 6, 'F-Gamut': 4} - - for colourspace in RGB_COLOURSPACES.values(): - value_cctf_encoding = 0.5 - value_cctf_decoding = colourspace.cctf_decoding( - colourspace.cctf_encoding(value_cctf_encoding)) - np.testing.assert_almost_equal( - value_cctf_encoding, - value_cctf_decoding, - decimal=decimals.get(colourspace.name, 7)) - - value_cctf_encoding = np.tile(value_cctf_encoding, 6) - value_cctf_decoding = np.tile(value_cctf_decoding, 6) - np.testing.assert_almost_equal( - value_cctf_encoding, - value_cctf_decoding, - decimal=decimals.get(colourspace.name, 7)) - - value_cctf_encoding = np.reshape(value_cctf_encoding, (3, 2)) - value_cctf_decoding = np.reshape(value_cctf_decoding, (3, 2)) - np.testing.assert_almost_equal( - value_cctf_encoding, - value_cctf_decoding, - decimal=decimals.get(colourspace.name, 7)) - - value_cctf_encoding = np.reshape(value_cctf_encoding, (3, 2, 1)) - value_cctf_decoding = np.reshape(value_cctf_decoding, (3, 2, 1)) - np.testing.assert_almost_equal( - value_cctf_encoding, - value_cctf_decoding, - decimal=decimals.get(colourspace.name, 7)) - - @ignore_numpy_errors - def test_nan_cctf(self): - """ - Tests colour component transfer functions from the - :attr:`colour.models.rgb.rgb_colourspace.RGB_COLOURSPACES` attribute - colourspace models nan support. - """ - - cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] - for colourspace in RGB_COLOURSPACES.values(): - for case in cases: - colourspace.cctf_encoding(case) - colourspace.cctf_decoding(case) - - def test_pickle(self): - """ - Tests the ability of colourspace models to be pickled. - """ - - for colourspace in RGB_COLOURSPACES: - pickle.dumps(colourspace) - - class TestRGB_Colourspace(unittest.TestCase): """ - Defines :class:`colour.colour.models.RGB_Colourspace` class units + Define :class:`colour.colour.models.RGB_Colourspace` class unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" p = np.array([0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700]) whitepoint = np.array([0.32168, 0.33767]) matrix_RGB_to_XYZ = np.identity(3) matrix_XYZ_to_RGB = np.identity(3) self._colourspace = RGB_Colourspace( - 'RGB Colourspace', p, whitepoint, 'ACES', matrix_RGB_to_XYZ, - matrix_XYZ_to_RGB, lambda x: x, lambda x: x) + "RGB Colourspace", + p, + whitepoint, + "ACES", + matrix_RGB_to_XYZ, + matrix_XYZ_to_RGB, + lambda x: x, + lambda x: x, + ) def test_required_attributes(self): - """ - Tests presence of required attributes. - """ - - required_attributes = ('name', 'primaries', 'whitepoint', - 'whitepoint_name', 'matrix_RGB_to_XYZ', - 'matrix_XYZ_to_RGB', 'cctf_encoding', - 'cctf_decoding', - 'use_derived_matrix_RGB_to_XYZ', - 'use_derived_matrix_XYZ_to_RGB') + """Test the presence of required attributes.""" + + required_attributes = ( + "name", + "primaries", + "whitepoint", + "whitepoint_name", + "matrix_RGB_to_XYZ", + "matrix_XYZ_to_RGB", + "cctf_encoding", + "cctf_decoding", + "use_derived_matrix_RGB_to_XYZ", + "use_derived_matrix_XYZ_to_RGB", + ) for attribute in required_attributes: self.assertIn(attribute, dir(RGB_Colourspace)) def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__init__', '__str__', '__repr__', - 'use_derived_transformation_matrices', - 'chromatically_adapt', 'copy') + required_methods = ( + "__init__", + "__str__", + "__repr__", + "use_derived_transformation_matrices", + "chromatically_adapt", + "copy", + ) for method in required_methods: self.assertIn(method, dir(RGB_Colourspace)) def test__str__(self): """ - Tests :func:`colour.models.rgb.rgb_colourspace.RGB_Colourspace.__str__` + Test :meth:`colour.models.rgb.rgb_colourspace.RGB_Colourspace.__str__` method. """ - # Skipping unit test on Python 2.7. - if six.PY2: # pragma: no cover - return - self.assertEqual( - re.sub(' at 0x\\w+>', '', str(self._colourspace)), - textwrap.dedent(""" + re.sub(" at 0x\\w+>", "", str(self._colourspace)), + textwrap.dedent( + """ RGB Colourspace --------------- @@ -255,21 +127,20 @@ def test__str__(self): [ -4.95903023e-01 1.37331305e+00 9.82400361e-02] [ 0.00000000e+00 0.00000000e+00 9.91252018e-01]] Use Derived NPM : False - Use Derived NPM -1 : False""")[1:]) + Use Derived NPM -1 : False""" + )[1:], + ) def test__repr__(self): """ - Tests :func:`colour.models.rgb.rgb_colourspace.RGB_Colourspace.\ + Test :func:`colour.models.rgb.rgb_colourspace.RGB_Colourspace.\ __repr__` method. """ - # Skipping unit test on Python 2.7. - if six.PY2: # pragma: no cover - return - self.assertEqual( - re.sub(' at 0x\\w+>', '', repr(self._colourspace)), - textwrap.dedent(""" + re.sub(" at 0x\\w+>", "", repr(self._colourspace)), + textwrap.dedent( + """ RGB_Colourspace(RGB Colourspace, [[ 7.34700000e-01, 2.65300000e-01], [ 0.00000000e+00, 1.00000000e+00], @@ -285,92 +156,117 @@ def test__repr__(self): ., ., False, - False)""")[1:]) + False)""" + )[1:], + ) def test_use_derived_transformation_matrices(self): """ - Tests :func:`colour.models.rgb.rgb_colourspace.RGB_Colourspace.\ + Test :func:`colour.models.rgb.rgb_colourspace.RGB_Colourspace.\ use_derived_transformation_matrices` method. """ - np.testing.assert_array_equal(self._colourspace.matrix_RGB_to_XYZ, - np.identity(3)) - np.testing.assert_array_equal(self._colourspace.matrix_XYZ_to_RGB, - np.identity(3)) + np.testing.assert_array_equal( + self._colourspace.matrix_RGB_to_XYZ, np.identity(3) + ) + np.testing.assert_array_equal( + self._colourspace.matrix_XYZ_to_RGB, np.identity(3) + ) - self.assertTrue( - self._colourspace.use_derived_transformation_matrices()) + self._colourspace.use_derived_transformation_matrices() np.testing.assert_almost_equal( self._colourspace.matrix_RGB_to_XYZ, - np.array([ - [0.95255240, 0.00000000, 0.00009368], - [0.34396645, 0.72816610, -0.07213255], - [0.00000000, 0.00000000, 1.00882518], - ]), - decimal=7) + np.array( + [ + [0.95255240, 0.00000000, 0.00009368], + [0.34396645, 0.72816610, -0.07213255], + [0.00000000, 0.00000000, 1.00882518], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( self._colourspace.matrix_XYZ_to_RGB, - np.array([ - [1.04981102, 0.00000000, -0.00009748], - [-0.49590302, 1.37331305, 0.09824004], - [0.00000000, 0.00000000, 0.99125202], - ]), - decimal=7) + np.array( + [ + [1.04981102, 0.00000000, -0.00009748], + [-0.49590302, 1.37331305, 0.09824004], + [0.00000000, 0.00000000, 0.99125202], + ] + ), + decimal=7, + ) self._colourspace.use_derived_matrix_RGB_to_XYZ = False - np.testing.assert_array_equal(self._colourspace.matrix_RGB_to_XYZ, - np.identity(3)) + np.testing.assert_array_equal( + self._colourspace.matrix_RGB_to_XYZ, np.identity(3) + ) self._colourspace.use_derived_matrix_XYZ_to_RGB = False - np.testing.assert_array_equal(self._colourspace.matrix_XYZ_to_RGB, - np.identity(3)) + np.testing.assert_array_equal( + self._colourspace.matrix_XYZ_to_RGB, np.identity(3) + ) def test_chromatically_adapt(self): """ - Tests :func:`colour.models.rgb.rgb_colourspace.RGB_Colourspace.\ + Test :func:`colour.models.rgb.rgb_colourspace.RGB_Colourspace.\ chromatically_adapt` method. """ whitepoint_t = np.array([0.31270, 0.32900]) colourspace = self._colourspace.chromatically_adapt( - whitepoint_t, 'D50', 'Bradford') + whitepoint_t, "D50", "Bradford" + ) np.testing.assert_array_almost_equal( colourspace.primaries, - np.array([ - [0.73485524, 0.26422533], - [-0.00617091, 1.01131496], - [0.01596756, -0.06423550], - ]), - decimal=7) + np.array( + [ + [0.73485524, 0.26422533], + [-0.00617091, 1.01131496], + [0.01596756, -0.06423550], + ] + ), + decimal=7, + ) np.testing.assert_array_almost_equal( - colourspace.whitepoint, whitepoint_t, decimal=7) + colourspace.whitepoint, whitepoint_t, decimal=7 + ) - self.assertEqual(colourspace.whitepoint_name, 'D50') + self.assertEqual(colourspace.whitepoint_name, "D50") np.testing.assert_array_almost_equal( colourspace.primaries, - chromatically_adapted_primaries(self._colourspace.primaries, - self._colourspace.whitepoint, - whitepoint_t, 'Bradford'), - decimal=7) + chromatically_adapted_primaries( + self._colourspace.primaries, + self._colourspace.whitepoint, + whitepoint_t, + "Bradford", + ), + decimal=7, + ) np.testing.assert_array_almost_equal( colourspace.matrix_RGB_to_XYZ, - normalised_primary_matrix(colourspace.primaries, - colourspace.whitepoint), - decimal=7) + normalised_primary_matrix( + colourspace.primaries, colourspace.whitepoint + ), + decimal=7, + ) np.testing.assert_array_almost_equal( colourspace.matrix_XYZ_to_RGB, np.linalg.inv( - normalised_primary_matrix(colourspace.primaries, - colourspace.whitepoint)), - decimal=7) + normalised_primary_matrix( + colourspace.primaries, colourspace.whitepoint + ) + ), + decimal=7, + ) def test_copy(self): """ - Tests :func:`colour.models.rgb.rgb_colourspace.RGB_Colourspace.copy` + Test :meth:`colour.models.rgb.rgb_colourspace.RGB_Colourspace.copy` method. """ @@ -379,131 +275,160 @@ def test_copy(self): class TestXYZ_to_RGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.rgb_colourspace.XYZ_to_RGB` definition + Define :func:`colour.models.rgb.rgb_colourspace.XYZ_to_RGB` definition unit tests methods. """ def test_XYZ_to_RGB(self): - """ - Tests :func:`colour.models.rgb.rgb_colourspace.XYZ_to_RGB` definition. - """ + """Test :func:`colour.models.rgb.rgb_colourspace.XYZ_to_RGB` definition.""" np.testing.assert_almost_equal( XYZ_to_RGB( np.array([0.21638819, 0.12570000, 0.03847493]), - np.array([0.34570, 0.35850]), np.array([0.31270, 0.32900]), - np.array([ - [3.24062548, -1.53720797, -0.49862860], - [-0.96893071, 1.87575606, 0.04151752], - [0.05571012, -0.20402105, 1.05699594], - ]), 'Bradford', eotf_inverse_sRGB), + np.array([0.34570, 0.35850]), + np.array([0.31270, 0.32900]), + np.array( + [ + [3.24062548, -1.53720797, -0.49862860], + [-0.96893071, 1.87575606, 0.04151752], + [0.05571012, -0.20402105, 1.05699594], + ] + ), + "Bradford", + eotf_inverse_sRGB, + ), np.array([0.70556599, 0.19109268, 0.22340812]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_RGB( np.array([0.21638819, 0.12570000, 0.03847493]), - np.array([0.34570, 0.35850]), np.array([0.31270, 0.32900]), - np.array([ - [3.24062548, -1.53720797, -0.49862860], - [-0.96893071, 1.87575606, 0.04151752], - [0.05571012, -0.20402105, 1.05699594], - ]), None, eotf_inverse_sRGB), + np.array([0.34570, 0.35850]), + np.array([0.31270, 0.32900]), + np.array( + [ + [3.24062548, -1.53720797, -0.49862860], + [-0.96893071, 1.87575606, 0.04151752], + [0.05571012, -0.20402105, 1.05699594], + ] + ), + None, + eotf_inverse_sRGB, + ), np.array([0.72794579, 0.18180021, 0.17951580]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_RGB( np.array([0.21638819, 0.12570000, 0.03847493]), - np.array([0.34570, 0.35850]), np.array([0.32168, 0.33767]), - np.array([ - [1.04981102, 0.00000000, -0.00009748], - [-0.49590302, 1.37331305, 0.09824004], - [0.00000000, 0.00000000, 0.99125202], - ])), + np.array([0.34570, 0.35850]), + np.array([0.32168, 0.33767]), + np.array( + [ + [1.04981102, 0.00000000, -0.00009748], + [-0.49590302, 1.37331305, 0.09824004], + [0.00000000, 0.00000000, 0.99125202], + ] + ), + ), np.array([0.21959099, 0.06985815, 0.04703704]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_RGB( np.array([0.21638819, 0.12570000, 0.03847493]), np.array([0.34570, 0.35850]), np.array([0.31270, 0.32900, 1.00000]), - np.array([ - [3.24062548, -1.53720797, -0.49862860], - [-0.96893071, 1.87575606, 0.04151752], - [0.05571012, -0.20402105, 1.05699594], - ])), + np.array( + [ + [3.24062548, -1.53720797, -0.49862860], + [-0.96893071, 1.87575606, 0.04151752], + [0.05571012, -0.20402105, 1.05699594], + ] + ), + ), np.array([0.45620801, 0.03079991, 0.04091883]), - decimal=7) + decimal=7, + ) def test_n_dimensional_XYZ_to_RGB(self): """ - Tests :func:`colour.models.rgb.rgb_colourspace.XYZ_to_RGB` definition + Test :func:`colour.models.rgb.rgb_colourspace.XYZ_to_RGB` definition n-dimensional support. """ XYZ = np.array([0.21638819, 0.12570000, 0.03847493]) W_R = np.array([0.34570, 0.35850]) W_T = np.array([0.31270, 0.32900]) - M = np.array([ - [3.24062548, -1.53720797, -0.49862860], - [-0.96893071, 1.87575606, 0.04151752], - [0.05571012, -0.20402105, 1.05699594], - ]) - RGB = XYZ_to_RGB(XYZ, W_R, W_T, M, 'Bradford', eotf_inverse_sRGB) + M = np.array( + [ + [3.24062548, -1.53720797, -0.49862860], + [-0.96893071, 1.87575606, 0.04151752], + [0.05571012, -0.20402105, 1.05699594], + ] + ) + RGB = XYZ_to_RGB(XYZ, W_R, W_T, M, "Bradford", eotf_inverse_sRGB) XYZ = np.tile(XYZ, (6, 1)) RGB = np.tile(RGB, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_RGB(XYZ, W_R, W_T, M, 'Bradford', eotf_inverse_sRGB), + XYZ_to_RGB(XYZ, W_R, W_T, M, "Bradford", eotf_inverse_sRGB), RGB, - decimal=7) + decimal=7, + ) W_R = np.tile(W_R, (6, 1)) W_T = np.tile(W_T, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_RGB(XYZ, W_R, W_T, M, 'Bradford', eotf_inverse_sRGB), + XYZ_to_RGB(XYZ, W_R, W_T, M, "Bradford", eotf_inverse_sRGB), RGB, - decimal=7) + decimal=7, + ) XYZ = np.reshape(XYZ, (2, 3, 3)) W_R = np.reshape(W_R, (2, 3, 2)) W_T = np.reshape(W_T, (2, 3, 2)) RGB = np.reshape(RGB, (2, 3, 3)) np.testing.assert_almost_equal( - XYZ_to_RGB(XYZ, W_R, W_T, M, 'Bradford', eotf_inverse_sRGB), + XYZ_to_RGB(XYZ, W_R, W_T, M, "Bradford", eotf_inverse_sRGB), RGB, - decimal=7) + decimal=7, + ) def test_domain_range_scale_XYZ_to_RGB(self): """ - Tests :func:`colour.models.rgb.rgb_colourspace.XYZ_to_RGB` definition + Test :func:`colour.models.rgb.rgb_colourspace.XYZ_to_RGB` definition domain and range scale support. """ XYZ = np.array([0.21638819, 0.12570000, 0.03847493]) W_R = np.array([0.34570, 0.35850]) W_T = np.array([0.31270, 0.32900]) - M = np.array([ - [3.24062548, -1.53720797, -0.49862860], - [-0.96893071, 1.87575606, 0.04151752], - [0.05571012, -0.20402105, 1.05699594], - ]) + M = np.array( + [ + [3.24062548, -1.53720797, -0.49862860], + [-0.96893071, 1.87575606, 0.04151752], + [0.05571012, -0.20402105, 1.05699594], + ] + ) RGB = XYZ_to_RGB(XYZ, W_R, W_T, M) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( XYZ_to_RGB(XYZ * factor, W_R, W_T, M), RGB * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_XYZ_to_RGB(self): """ - Tests :func:`colour.models.rgb.rgb_colourspace.XYZ_to_RGB` definition + Test :func:`colour.models.rgb.rgb_colourspace.XYZ_to_RGB` definition nan support. """ @@ -519,131 +444,154 @@ def test_nan_XYZ_to_RGB(self): class TestRGB_to_XYZ(unittest.TestCase): """ - Defines :func:`colour.models.rgb.rgb_colourspace.RGB_to_XYZ` definition + Define :func:`colour.models.rgb.rgb_colourspace.RGB_to_XYZ` definition unit tests methods. """ def test_RGB_to_XYZ(self): - """ - Tests :func:`colour.models.rgb.rgb_colourspace.RGB_to_XYZ` definition. - """ + """Test :func:`colour.models.rgb.rgb_colourspace.RGB_to_XYZ` definition.""" np.testing.assert_almost_equal( RGB_to_XYZ( np.array([0.70556599, 0.19109268, 0.22340812]), - np.array([0.31270, 0.32900]), np.array([0.34570, 0.35850]), - np.array([ - [0.41240000, 0.35760000, 0.18050000], - [0.21260000, 0.71520000, 0.07220000], - [0.01930000, 0.11920000, 0.95050000], - ]), 'Bradford', eotf_sRGB), + np.array([0.31270, 0.32900]), + np.array([0.34570, 0.35850]), + np.array( + [ + [0.41240000, 0.35760000, 0.18050000], + [0.21260000, 0.71520000, 0.07220000], + [0.01930000, 0.11920000, 0.95050000], + ] + ), + "Bradford", + eotf_sRGB, + ), np.array([0.21638819, 0.12570000, 0.03847493]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_XYZ( np.array([0.72794579, 0.18180021, 0.17951580]), - np.array([0.31270, 0.32900]), np.array([0.34570, 0.35850]), - np.array([ - [0.41240000, 0.35760000, 0.18050000], - [0.21260000, 0.71520000, 0.07220000], - [0.01930000, 0.11920000, 0.95050000], - ]), None, eotf_sRGB), + np.array([0.31270, 0.32900]), + np.array([0.34570, 0.35850]), + np.array( + [ + [0.41240000, 0.35760000, 0.18050000], + [0.21260000, 0.71520000, 0.07220000], + [0.01930000, 0.11920000, 0.95050000], + ] + ), + None, + eotf_sRGB, + ), np.array([0.21638819, 0.12570000, 0.03847493]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_XYZ( np.array([0.21959099, 0.06985815, 0.04703704]), - np.array([0.32168, 0.33767]), np.array([0.34570, 0.35850]), - np.array([ - [0.95255240, 0.00000000, 0.00009368], - [0.34396645, 0.72816610, -0.07213255], - [0.00000000, 0.00000000, 1.00882518], - ])), + np.array([0.32168, 0.33767]), + np.array([0.34570, 0.35850]), + np.array( + [ + [0.95255240, 0.00000000, 0.00009368], + [0.34396645, 0.72816610, -0.07213255], + [0.00000000, 0.00000000, 1.00882518], + ] + ), + ), np.array([0.21638819, 0.12570000, 0.03847493]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_XYZ( np.array([0.45620801, 0.03079991, 0.04091883]), np.array([0.31270, 0.32900, 1.00000]), np.array([0.34570, 0.35850]), - np.array([ - [0.41240000, 0.35760000, 0.18050000], - [0.21260000, 0.71520000, 0.07220000], - [0.01930000, 0.11920000, 0.95050000], - ])), + np.array( + [ + [0.41240000, 0.35760000, 0.18050000], + [0.21260000, 0.71520000, 0.07220000], + [0.01930000, 0.11920000, 0.95050000], + ] + ), + ), np.array([0.21638819, 0.12570000, 0.03847493]), - decimal=7) + decimal=7, + ) def test_n_dimensional_RGB_to_XYZ(self): """ - Tests :func:`colour.models.rgb.rgb_colourspace.RGB_to_XYZ` definition + Test :func:`colour.models.rgb.rgb_colourspace.RGB_to_XYZ` definition n-dimensional support. """ RGB = np.array([0.70556599, 0.19109268, 0.22340812]) W_R = np.array([0.31270, 0.32900]) W_T = np.array([0.34570, 0.35850]) - M = np.array([ - [0.41240000, 0.35760000, 0.18050000], - [0.21260000, 0.71520000, 0.07220000], - [0.01930000, 0.11920000, 0.95050000], - ]) - XYZ = RGB_to_XYZ(RGB, W_R, W_T, M, 'Bradford', eotf_sRGB) + M = np.array( + [ + [0.41240000, 0.35760000, 0.18050000], + [0.21260000, 0.71520000, 0.07220000], + [0.01930000, 0.11920000, 0.95050000], + ] + ) + XYZ = RGB_to_XYZ(RGB, W_R, W_T, M, "Bradford", eotf_sRGB) RGB = np.tile(RGB, (6, 1)) XYZ = np.tile(XYZ, (6, 1)) np.testing.assert_almost_equal( - RGB_to_XYZ(RGB, W_R, W_T, M, 'Bradford', eotf_sRGB), - XYZ, - decimal=7) + RGB_to_XYZ(RGB, W_R, W_T, M, "Bradford", eotf_sRGB), XYZ, decimal=7 + ) W_R = np.tile(W_R, (6, 1)) W_T = np.tile(W_T, (6, 1)) np.testing.assert_almost_equal( - RGB_to_XYZ(RGB, W_R, W_T, M, 'Bradford', eotf_sRGB), - XYZ, - decimal=7) + RGB_to_XYZ(RGB, W_R, W_T, M, "Bradford", eotf_sRGB), XYZ, decimal=7 + ) RGB = np.reshape(RGB, (2, 3, 3)) W_R = np.reshape(W_R, (2, 3, 2)) W_T = np.reshape(W_T, (2, 3, 2)) XYZ = np.reshape(XYZ, (2, 3, 3)) np.testing.assert_almost_equal( - RGB_to_XYZ(RGB, W_R, W_T, M, 'Bradford', eotf_sRGB), - XYZ, - decimal=7) + RGB_to_XYZ(RGB, W_R, W_T, M, "Bradford", eotf_sRGB), XYZ, decimal=7 + ) def test_domain_range_scale_XYZ_to_RGB(self): """ - Tests :func:`colour.models.rgb.rgb_colourspace.RGB_to_XYZ` definition + Test :func:`colour.models.rgb.rgb_colourspace.RGB_to_XYZ` definition domain and range scale support. """ RGB = np.array([0.45620801, 0.03079991, 0.04091883]) W_R = np.array([0.31270, 0.32900]) W_T = np.array([0.34570, 0.35850]) - M = np.array([ - [3.24062548, -1.53720797, -0.49862860], - [-0.96893071, 1.87575606, 0.04151752], - [0.05571012, -0.20402105, 1.05699594], - ]) + M = np.array( + [ + [3.24062548, -1.53720797, -0.49862860], + [-0.96893071, 1.87575606, 0.04151752], + [0.05571012, -0.20402105, 1.05699594], + ] + ) XYZ = RGB_to_XYZ(RGB, W_R, W_T, M) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( RGB_to_XYZ(RGB * factor, W_R, W_T, M), XYZ * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_RGB_to_XYZ(self): """ - Tests :func:`colour.models.rgb.rgb_colourspace.RGB_to_XYZ` definition + Test :func:`colour.models.rgb.rgb_colourspace.RGB_to_XYZ` definition nan support. """ @@ -659,112 +607,141 @@ def test_nan_RGB_to_XYZ(self): class TestMatrix_RGB_to_RGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.rgb_colourspace.matrix_RGB_to_RGB` + Define :func:`colour.models.rgb.rgb_colourspace.matrix_RGB_to_RGB` definition unit tests methods. """ def test_matrix_RGB_to_RGB(self): """ - Tests :func:`colour.models.rgb.rgb_colourspace.matrix_RGB_to_RGB` + Test :func:`colour.models.rgb.rgb_colourspace.matrix_RGB_to_RGB` definition. """ - aces_2065_1_colourspace = RGB_COLOURSPACES['ACES2065-1'] - aces_cg_colourspace = RGB_COLOURSPACES['ACEScg'] - sRGB_colourspace = RGB_COLOURSPACES['sRGB'] + aces_2065_1_colourspace = RGB_COLOURSPACES["ACES2065-1"] + aces_cg_colourspace = RGB_COLOURSPACES["ACEScg"] + sRGB_colourspace = RGB_COLOURSPACES["sRGB"] np.testing.assert_almost_equal( matrix_RGB_to_RGB(aces_2065_1_colourspace, sRGB_colourspace), - np.array([ - [2.52164943, -1.13688855, -0.38491759], - [-0.27521355, 1.36970515, -0.09439245], - [-0.01592501, -0.14780637, 1.16380582], - ]), - decimal=7) + np.array( + [ + [2.52164943, -1.13688855, -0.38491759], + [-0.27521355, 1.36970515, -0.09439245], + [-0.01592501, -0.14780637, 1.16380582], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( matrix_RGB_to_RGB(sRGB_colourspace, aces_2065_1_colourspace), - np.array([ - [0.43958564, 0.38392940, 0.17653274], - [0.08953957, 0.81474984, 0.09568361], - [0.01738718, 0.10873911, 0.87382059], - ]), - decimal=7) + np.array( + [ + [0.43958564, 0.38392940, 0.17653274], + [0.08953957, 0.81474984, 0.09568361], + [0.01738718, 0.10873911, 0.87382059], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - matrix_RGB_to_RGB(aces_2065_1_colourspace, aces_cg_colourspace, - 'Bradford'), - np.array([ - [1.45143932, -0.23651075, -0.21492857], - [-0.07655377, 1.17622970, -0.09967593], - [0.00831615, -0.00603245, 0.99771630], - ]), - decimal=7) + matrix_RGB_to_RGB( + aces_2065_1_colourspace, aces_cg_colourspace, "Bradford" + ), + np.array( + [ + [1.45143932, -0.23651075, -0.21492857], + [-0.07655377, 1.17622970, -0.09967593], + [0.00831615, -0.00603245, 0.99771630], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - matrix_RGB_to_RGB(aces_2065_1_colourspace, sRGB_colourspace, - 'Bradford'), - np.array([ - [2.52140089, -1.13399575, -0.38756186], - [-0.27621406, 1.37259557, -0.09628236], - [-0.01532020, -0.15299256, 1.16838720], - ]), - decimal=7) + matrix_RGB_to_RGB( + aces_2065_1_colourspace, sRGB_colourspace, "Bradford" + ), + np.array( + [ + [2.52140089, -1.13399575, -0.38756186], + [-0.27621406, 1.37259557, -0.09628236], + [-0.01532020, -0.15299256, 1.16838720], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( matrix_RGB_to_RGB(aces_2065_1_colourspace, sRGB_colourspace, None), - np.array([ - [2.55809607, -1.11933692, -0.39181451], - [-0.27771575, 1.36589396, -0.09353075], - [-0.01711199, -0.14854588, 1.08104848], - ]), - decimal=7) + np.array( + [ + [2.55809607, -1.11933692, -0.39181451], + [-0.27771575, 1.36589396, -0.09353075], + [-0.01711199, -0.14854588, 1.08104848], + ] + ), + decimal=7, + ) class TestRGB_to_RGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.rgb_colourspace.RGB_to_RGB` definition + Define :func:`colour.models.rgb.rgb_colourspace.RGB_to_RGB` definition unit tests methods. """ def test_RGB_to_RGB(self): - """ - Tests :func:`colour.models.rgb.rgb_colourspace.RGB_to_RGB` definition. - """ + """Test :func:`colour.models.rgb.rgb_colourspace.RGB_to_RGB` definition.""" - aces_2065_1_colourspace = RGB_COLOURSPACES['ACES2065-1'] - sRGB_colourspace = RGB_COLOURSPACES['sRGB'] + aces_2065_1_colourspace = RGB_COLOURSPACES["ACES2065-1"] + sRGB_colourspace = RGB_COLOURSPACES["sRGB"] np.testing.assert_almost_equal( RGB_to_RGB( np.array([0.21931722, 0.06950287, 0.04694832]), - aces_2065_1_colourspace, sRGB_colourspace), + aces_2065_1_colourspace, + sRGB_colourspace, + ), np.array([0.45595289, 0.03040780, 0.04087313]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_RGB( np.array([0.45595571, 0.03039702, 0.04087245]), - sRGB_colourspace, aces_2065_1_colourspace), + sRGB_colourspace, + aces_2065_1_colourspace, + ), np.array([0.21931722, 0.06950287, 0.04694832]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_RGB( np.array([0.21931722, 0.06950287, 0.04694832]), - aces_2065_1_colourspace, sRGB_colourspace, 'Bradford'), + aces_2065_1_colourspace, + sRGB_colourspace, + "Bradford", + ), np.array([0.45597530, 0.03030054, 0.04086041]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_RGB( np.array([0.21931722, 0.06950287, 0.04694832]), - aces_2065_1_colourspace, sRGB_colourspace, None), + aces_2065_1_colourspace, + sRGB_colourspace, + None, + ), np.array([0.46484236, 0.02963459, 0.03667609]), - decimal=7) + decimal=7, + ) - aces_cg_colourspace = RGB_COLOURSPACES['ACEScg'] - aces_cc_colourspace = RGB_COLOURSPACES['ACEScc'] + aces_cg_colourspace = RGB_COLOURSPACES["ACEScg"] + aces_cc_colourspace = RGB_COLOURSPACES["ACEScc"] np.testing.assert_almost_equal( RGB_to_RGB( @@ -772,9 +749,11 @@ def test_RGB_to_RGB(self): aces_cg_colourspace, aces_cc_colourspace, apply_cctf_decoding=True, - apply_cctf_encoding=True), + apply_cctf_encoding=True, + ), np.array([0.42985679, 0.33522924, 0.30292336]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_RGB( @@ -782,26 +761,31 @@ def test_RGB_to_RGB(self): aces_cc_colourspace, sRGB_colourspace, apply_cctf_decoding=True, - apply_cctf_encoding=True), + apply_cctf_encoding=True, + ), np.array([0.60983062, 0.67896356, 0.50435764]), - decimal=7) + decimal=7, + ) np.testing.assert_equal( RGB_to_RGB( np.array([0.21931722, 0.06950287, 0.04694832]), aces_2065_1_colourspace, - RGB_COLOURSPACES['ProPhoto RGB'], + RGB_COLOURSPACES["ProPhoto RGB"], apply_cctf_encoding=True, - out_int=True), np.array([120, 59, 46])) + out_int=True, + ), + np.array([120, 59, 46]), + ) def test_n_dimensional_RGB_to_RGB(self): """ - Tests :func:`colour.models.rgb.rgb_colourspace.RGB_to_RGB` definition + Test :func:`colour.models.rgb.rgb_colourspace.RGB_to_RGB` definition n-dimensional support. """ - aces_2065_1_colourspace = RGB_COLOURSPACES['ACES2065-1'] - sRGB_colourspace = RGB_COLOURSPACES['sRGB'] + aces_2065_1_colourspace = RGB_COLOURSPACES["ACES2065-1"] + sRGB_colourspace = RGB_COLOURSPACES["sRGB"] RGB_i = np.array([0.21931722, 0.06950287, 0.04694832]) RGB_o = RGB_to_RGB(RGB_i, aces_2065_1_colourspace, sRGB_colourspace) @@ -810,44 +794,50 @@ def test_n_dimensional_RGB_to_RGB(self): np.testing.assert_almost_equal( RGB_to_RGB(RGB_i, aces_2065_1_colourspace, sRGB_colourspace), RGB_o, - decimal=7) + decimal=7, + ) RGB_i = np.reshape(RGB_i, (2, 3, 3)) RGB_o = np.reshape(RGB_o, (2, 3, 3)) np.testing.assert_almost_equal( RGB_to_RGB(RGB_i, aces_2065_1_colourspace, sRGB_colourspace), RGB_o, - decimal=7) + decimal=7, + ) def test_domain_range_scale_XYZ_to_RGB(self): """ - Tests :func:`colour.models.rgb.rgb_colourspace.RGB_to_RGB` definition + Test :func:`colour.models.rgb.rgb_colourspace.RGB_to_RGB` definition domain and range scale support. """ - aces_2065_1_colourspace = RGB_COLOURSPACES['ACES2065-1'] - sRGB_colourspace = RGB_COLOURSPACES['sRGB'] + aces_2065_1_colourspace = RGB_COLOURSPACES["ACES2065-1"] + sRGB_colourspace = RGB_COLOURSPACES["sRGB"] RGB_i = np.array([0.21931722, 0.06950287, 0.04694832]) RGB_o = RGB_to_RGB(RGB_i, aces_2065_1_colourspace, sRGB_colourspace) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - RGB_to_RGB(RGB_i * factor, aces_2065_1_colourspace, - sRGB_colourspace), + RGB_to_RGB( + RGB_i * factor, + aces_2065_1_colourspace, + sRGB_colourspace, + ), RGB_o * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_RGB_to_RGB(self): """ - Tests :func:`colour.models.rgb.rgb_colourspace.RGB_to_RGB` definition + Test :func:`colour.models.rgb.rgb_colourspace.RGB_to_RGB` definition nan support. """ - aces_2065_1_colourspace = RGB_COLOURSPACES['ACES2065-1'] - sRGB_colourspace = RGB_COLOURSPACES['sRGB'] + aces_2065_1_colourspace = RGB_COLOURSPACES["ACES2065-1"] + sRGB_colourspace = RGB_COLOURSPACES["sRGB"] cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -856,5 +846,5 @@ def test_nan_RGB_to_RGB(self): RGB_to_RGB(RGB, aces_2065_1_colourspace, sRGB_colourspace) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/tests/test_ycbcr.py b/colour/models/rgb/tests/test_ycbcr.py index 9a6435aaba..305cb81728 100644 --- a/colour/models/rgb/tests/test_ycbcr.py +++ b/colour/models/rgb/tests/test_ycbcr.py @@ -1,78 +1,192 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.rgb.ycbcr` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.rgb.ycbcr` module.""" import numpy as np import unittest from itertools import permutations -from colour.models.rgb.ycbcr import (RGB_to_YCbCr, YCbCr_to_RGB, - RGB_to_YcCbcCrc, YcCbcCrc_to_RGB, - WEIGHTS_YCBCR) +from colour.models.rgb.ycbcr import ( + matrix_YCbCr, + offset_YCbCr, + RGB_to_YCbCr, + YCbCr_to_RGB, + RGB_to_YcCbcCrc, + YcCbcCrc_to_RGB, + WEIGHTS_YCBCR, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Development' +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Development" __all__ = [ - 'TestRGB_to_YCbCr', 'TestYCbCr_to_RGB', 'TestRGB_to_YcCbcCrc', - 'TestYcCbcCrc_to_RGB' + "TestMatrixYCbCr", + "TestOffsetYCbCr", + "TestRGB_to_YCbCr", + "TestYCbCr_to_RGB", + "TestRGB_to_YcCbcCrc", + "TestYcCbcCrc_to_RGB", ] +class TestMatrixYCbCr(unittest.TestCase): + """ + Define :func:`colour.models.rgb.ycbcr.matrix_YCbCr` definition unit tests + methods. + """ + + def test_matrix_YCbCr(self): + """Test :func:`colour.models.rgb.ycbcr.matrix_YCbCr` definition.""" + + np.testing.assert_almost_equal( + matrix_YCbCr(), + np.array( + [ + [1.00000000, 0.00000000, 1.57480000], + [1.00000000, -0.18732427, -0.46812427], + [1.00000000, 1.85560000, 0.00000000], + ] + ), + decimal=7, + ) + + np.testing.assert_almost_equal( + matrix_YCbCr(K=WEIGHTS_YCBCR["ITU-R BT.601"]), + np.array( + [ + [1.00000000, 0.00000000, 1.40200000], + [1.00000000, -0.34413629, -0.71413629], + [1.00000000, 1.77200000, -0.00000000], + ] + ), + decimal=7, + ) + + np.testing.assert_almost_equal( + matrix_YCbCr(is_legal=True), + np.array( + [ + [1.16438356, 0.00000000, 1.79274107], + [1.16438356, -0.21324861, -0.53290933], + [1.16438356, 2.11240179, -0.00000000], + ] + ), + decimal=7, + ) + + np.testing.assert_almost_equal( + matrix_YCbCr(bits=10), + np.array( + [ + [1.00000000, 0.00000000, 1.57480000], + [1.00000000, -0.18732427, -0.46812427], + [1.00000000, 1.85560000, 0.00000000], + ] + ), + decimal=7, + ) + + np.testing.assert_almost_equal( + matrix_YCbCr(bits=10, is_int=True), + np.array( + [ + [0.00097752, 0.00000000, 0.00153789], + [0.00097752, -0.00018293, -0.00045715], + [0.00097752, 0.00181211, 0.00000000], + ] + ), + decimal=7, + ) + + +class TestOffsetYCbCr(unittest.TestCase): + """ + Define :func:`colour.models.rgb.ycbcr.offset_YCbCr` definition unit tests + methods. + """ + + def test_offset_YCbCr(self): + """Test :func:`colour.models.rgb.ycbcr.offset_YCbCr` definition.""" + + np.testing.assert_almost_equal( + offset_YCbCr(), + np.array([0.00000000, 0.00000000, 0.00000000]), + decimal=7, + ) + + np.testing.assert_almost_equal( + offset_YCbCr(is_legal=True), + np.array([0.06274510, 0.50196078, 0.50196078]), + decimal=7, + ) + + np.testing.assert_almost_equal( + offset_YCbCr(bits=10), + np.array([0.00000000, 0.00000000, 0.00000000]), + decimal=7, + ) + + np.testing.assert_almost_equal( + offset_YCbCr(bits=10, is_int=True), + np.array([0.00000000, 512.00000000, 512.00000000]), + decimal=7, + ) + + class TestRGB_to_YCbCr(unittest.TestCase): """ - Defines :func:`colour.models.rgb.ycbcr.RGB_to_YCbCr` definition unit tests + Define :func:`colour.models.rgb.ycbcr.RGB_to_YCbCr` definition unit tests methods. """ def test_RGB_to_YCbCr(self): - """ - Tests :func:`colour.models.rgb.ycbcr.RGB_to_YCbCr` definition. - """ + """Test :func:`colour.models.rgb.ycbcr.RGB_to_YCbCr` definition.""" np.testing.assert_almost_equal( RGB_to_YCbCr(np.array([0.75, 0.75, 0.0])), np.array([0.66035745, 0.17254902, 0.53216593]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_YCbCr( np.array([0.25, 0.5, 0.75]), - K=WEIGHTS_YCBCR['ITU-R BT.601'], + K=WEIGHTS_YCBCR["ITU-R BT.601"], out_int=True, out_legal=True, - out_bits=10), + out_bits=10, + ), np.array([461, 662, 382]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_YCbCr( np.array([0.0, 0.75, 0.75]), - K=WEIGHTS_YCBCR['ITU-R BT.2020'], + K=WEIGHTS_YCBCR["ITU-R BT.2020"], out_int=False, - out_legal=False), + out_legal=False, + ), np.array([0.55297500, 0.10472255, -0.37500000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_YCbCr( np.array([0.75, 0.0, 0.75]), - K=WEIGHTS_YCBCR['ITU-R BT.709'], - out_range=(16 / 255, 235 / 255, 15.5 / 255, 239.5 / 255)), + K=WEIGHTS_YCBCR["ITU-R BT.709"], + out_range=(16 / 255, 235 / 255, 15.5 / 255, 239.5 / 255), + ), np.array([0.24618980, 0.75392897, 0.79920662]), - decimal=7) + decimal=7, + ) def test_n_dimensional_RGB_to_YCbCr(self): """ - Tests :func:`colour.models.rgb.ycbcr.RGB_to_YCbCr` definition + Test :func:`colour.models.rgb.ycbcr.RGB_to_YCbCr` definition n-dimensional arrays support. """ @@ -99,23 +213,24 @@ def test_n_dimensional_RGB_to_YCbCr(self): def test_domain_range_scale_RGB_to_YCbCr(self): """ - Tests :func:`colour.models.rgb.prismatic.RGB_to_YCbCr` definition + Test :func:`colour.models.rgb.prismatic.RGB_to_YCbCr` definition domain and range scale support. """ RGB = np.array([0.75, 0.5, 0.25]) YCbCr = RGB_to_YCbCr(RGB) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - RGB_to_YCbCr(RGB * factor), YCbCr * factor, decimal=7) + RGB_to_YCbCr(RGB * factor), YCbCr * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_RGB_to_YCbCr(self): """ - Tests :func:`colour.models.rgb.ycbcr.RGB_to_YCbCr` definition nan + Test :func:`colour.models.rgb.ycbcr.RGB_to_YCbCr` definition nan support. """ @@ -128,28 +243,29 @@ def test_nan_RGB_to_YCbCr(self): class TestYCbCr_to_RGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition unit tests + Define :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition unit tests methods. """ def test_YCbCr_to_RGB(self): - """ - Tests :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition. - """ + """Test :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition.""" np.testing.assert_almost_equal( YCbCr_to_RGB(np.array([0.66035745, 0.17254902, 0.53216593])), np.array([0.75, 0.75, 0.0]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( YCbCr_to_RGB( np.array([471, 650, 390]), in_bits=10, in_legal=True, - in_int=True), + in_int=True, + ), np.array([0.25018598, 0.49950072, 0.75040741]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( YCbCr_to_RGB( @@ -159,13 +275,15 @@ def test_YCbCr_to_RGB(self): in_int=True, out_bits=8, out_legal=True, - out_int=True), + out_int=True, + ), np.array([208, 131, 99]), - decimal=7) + decimal=7, + ) def test_n_dimensional_YCbCr_to_RGB(self): """ - Tests :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition + Test :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition n-dimensional arrays support. """ @@ -192,23 +310,24 @@ def test_n_dimensional_YCbCr_to_RGB(self): def test_domain_range_scale_YCbCr_to_RGB(self): """ - Tests :func:`colour.models.rgb.prismatic.YCbCr_to_RGB` definition + Test :func:`colour.models.rgb.prismatic.YCbCr_to_RGB` definition domain and range scale support. """ YCbCr = np.array([0.52230157, 0.36699593, 0.62183309]) RGB = YCbCr_to_RGB(YCbCr) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - YCbCr_to_RGB(YCbCr * factor), RGB * factor, decimal=7) + YCbCr_to_RGB(YCbCr * factor), RGB * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_YCbCr_to_RGB(self): """ - Tests :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition nan + Test :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition nan support. """ @@ -221,19 +340,18 @@ def test_nan_YCbCr_to_RGB(self): class TestRGB_to_YcCbcCrc(unittest.TestCase): """ - Defines :func:`colour.models.rgb.ycbcr.RGB_to_YcCbcCrc` definition unit + Define :func:`colour.models.rgb.ycbcr.RGB_to_YcCbcCrc` definition unit tests methods. """ def test_RGB_to_YcCbcCrc(self): - """ - Tests :func:`colour.models.rgb.ycbcr.RGB_to_YcCbcCrc` definition. - """ + """Test :func:`colour.models.rgb.ycbcr.RGB_to_YcCbcCrc` definition.""" np.testing.assert_almost_equal( RGB_to_YcCbcCrc(np.array([0.45620519, 0.03081071, 0.04091952])), np.array([0.37020379, 0.41137200, 0.77704674]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_YcCbcCrc( @@ -241,13 +359,15 @@ def test_RGB_to_YcCbcCrc(self): out_bits=10, out_legal=True, out_int=True, - is_12_bits_system=False), + is_12_bits_system=False, + ), np.array([422, 512, 512]), - decimal=7) + decimal=7, + ) def test_n_dimensional_RGB_to_YcCbcCrc(self): """ - Tests :func:`colour.models.rgb.ycbcr.RGB_to_YcCbcCrc` definition + Test :func:`colour.models.rgb.ycbcr.RGB_to_YcCbcCrc` definition n-dimensional arrays support. """ @@ -259,43 +379,45 @@ def test_n_dimensional_RGB_to_YcCbcCrc(self): YcCbcCrc = np.tile(YcCbcCrc, 4) YcCbcCrc = np.reshape(YcCbcCrc, (4, 3)) np.testing.assert_almost_equal( - RGB_to_YcCbcCrc(RGB), YcCbcCrc, decimal=7) + RGB_to_YcCbcCrc(RGB), YcCbcCrc, decimal=7 + ) RGB = np.tile(RGB, 4) RGB = np.reshape(RGB, (4, 4, 3)) YcCbcCrc = np.tile(YcCbcCrc, 4) YcCbcCrc = np.reshape(YcCbcCrc, (4, 4, 3)) np.testing.assert_almost_equal( - RGB_to_YcCbcCrc(RGB), YcCbcCrc, decimal=7) + RGB_to_YcCbcCrc(RGB), YcCbcCrc, decimal=7 + ) RGB = np.tile(RGB, 4) RGB = np.reshape(RGB, (4, 4, 4, 3)) YcCbcCrc = np.tile(YcCbcCrc, 4) YcCbcCrc = np.reshape(YcCbcCrc, (4, 4, 4, 3)) np.testing.assert_almost_equal( - RGB_to_YcCbcCrc(RGB), YcCbcCrc, decimal=7) + RGB_to_YcCbcCrc(RGB), YcCbcCrc, decimal=7 + ) def test_domain_range_scale_RGB_to_YcCbcCrc(self): """ - Tests :func:`colour.models.rgb.prismatic.RGB_to_YcCbcCrc` definition + Test :func:`colour.models.rgb.prismatic.RGB_to_YcCbcCrc` definition domain and range scale support. """ RGB = np.array([0.45620519, 0.03081071, 0.04091952]) YcCbcCrc = RGB_to_YcCbcCrc(RGB) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - RGB_to_YcCbcCrc(RGB * factor), - YcCbcCrc * factor, - decimal=7) + RGB_to_YcCbcCrc(RGB * factor), YcCbcCrc * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_RGB_to_YcCbcCrc(self): """ - Tests :func:`colour.models.rgb.ycbcr.RGB_to_YcCbcCrc` definition nan + Test :func:`colour.models.rgb.ycbcr.RGB_to_YcCbcCrc` definition nan support. """ @@ -308,19 +430,18 @@ def test_nan_RGB_to_YcCbcCrc(self): class TestYcCbcCrc_to_RGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition unit tests + Define :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition unit tests methods. """ def test_YcCbcCrc_to_RGB(self): - """ - Tests :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition. - """ + """Test :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition.""" np.testing.assert_almost_equal( YcCbcCrc_to_RGB(np.array([0.37020379, 0.41137200, 0.77704674])), np.array([0.45620519, 0.03081071, 0.04091952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( YcCbcCrc_to_RGB( @@ -328,13 +449,15 @@ def test_YcCbcCrc_to_RGB(self): in_bits=12, in_legal=True, in_int=True, - is_12_bits_system=True), + is_12_bits_system=True, + ), np.array([0.18009037, 0.18009037, 0.18009037]), - decimal=7) + decimal=7, + ) def test_n_dimensional_YcCbcCrc_to_RGB(self): """ - Tests :func:`colour.models.rgb.ycbcr.YcCbcCrc_to_RGB` definition + Test :func:`colour.models.rgb.ycbcr.YcCbcCrc_to_RGB` definition n-dimensional arrays support. """ @@ -346,43 +469,45 @@ def test_n_dimensional_YcCbcCrc_to_RGB(self): YcCbcCrc = np.tile(YcCbcCrc, 4) YcCbcCrc = np.reshape(YcCbcCrc, (4, 3)) np.testing.assert_almost_equal( - YcCbcCrc_to_RGB(YcCbcCrc), RGB, decimal=7) + YcCbcCrc_to_RGB(YcCbcCrc), RGB, decimal=7 + ) RGB = np.tile(RGB, 4) RGB = np.reshape(RGB, (4, 4, 3)) YcCbcCrc = np.tile(YcCbcCrc, 4) YcCbcCrc = np.reshape(YcCbcCrc, (4, 4, 3)) np.testing.assert_almost_equal( - YcCbcCrc_to_RGB(YcCbcCrc), RGB, decimal=7) + YcCbcCrc_to_RGB(YcCbcCrc), RGB, decimal=7 + ) RGB = np.tile(RGB, 4) RGB = np.reshape(RGB, (4, 4, 4, 3)) YcCbcCrc = np.tile(YcCbcCrc, 4) YcCbcCrc = np.reshape(YcCbcCrc, (4, 4, 4, 3)) np.testing.assert_almost_equal( - YcCbcCrc_to_RGB(YcCbcCrc), RGB, decimal=7) + YcCbcCrc_to_RGB(YcCbcCrc), RGB, decimal=7 + ) def test_domain_range_scale_YcCbcCrc_to_RGB(self): """ - Tests :func:`colour.models.rgb.prismatic.YcCbcCrc_to_RGB` definition + Test :func:`colour.models.rgb.prismatic.YcCbcCrc_to_RGB` definition domain and range scale support. """ YcCbcCrc = np.array([0.69943807, 0.38814348, 0.61264549]) RGB = YcCbcCrc_to_RGB(YcCbcCrc) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - YcCbcCrc_to_RGB(YcCbcCrc * factor), - RGB * factor, - decimal=7) + YcCbcCrc_to_RGB(YcCbcCrc * factor), RGB * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_YcCbcCrc_to_RGB(self): """ - Tests :func:`colour.models.rgb.ycbcr.YcCbcCrc_to_RGB` definition nan + Test :func:`colour.models.rgb.ycbcr.YcCbcCrc_to_RGB` definition nan support. """ @@ -393,5 +518,5 @@ def test_nan_YcCbcCrc_to_RGB(self): YcCbcCrc_to_RGB(YcCbcCrc) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/tests/test_ycocg.py b/colour/models/rgb/tests/test_ycocg.py index 6bb8fefd7d..cb2f80292c 100644 --- a/colour/models/rgb/tests/test_ycocg.py +++ b/colour/models/rgb/tests/test_ycocg.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.rgb.ycocg` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.rgb.ycocg` module.""" import numpy as np import unittest @@ -12,42 +7,46 @@ from colour.models.rgb import RGB_to_YCoCg, YCoCg_to_RGB from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Development' +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Development" -__all__ = ['TestRGB_to_YCoCg', 'TestYCoCg_to_RGB'] +__all__ = [ + "TestRGB_to_YCoCg", + "TestYCoCg_to_RGB", +] class TestRGB_to_YCoCg(unittest.TestCase): """ - Defines :func:`colour.models.rgb.ycocg.RGB_to_YCoCg` definition unit tests + Define :func:`colour.models.rgb.ycocg.RGB_to_YCoCg` definition unit tests methods. """ def test_RGB_to_YCoCg(self): - """ - Tests :func:`colour.models.rgb.ycocg.RGB_to_YCoCg` definition. - """ + """Test :func:`colour.models.rgb.ycocg.RGB_to_YCoCg` definition.""" np.testing.assert_array_equal( RGB_to_YCoCg(np.array([0.75, 0.75, 0.0])), - np.array([0.5625, 0.375, 0.1875])) + np.array([0.5625, 0.375, 0.1875]), + ) np.testing.assert_array_equal( RGB_to_YCoCg(np.array([0.25, 0.5, 0.75])), - np.array([0.5, -0.25, 0.0])) + np.array([0.5, -0.25, 0.0]), + ) np.testing.assert_array_equal( RGB_to_YCoCg(np.array([0.0, 0.75, 0.75])), - np.array([0.5625, -0.375, 0.1875])) + np.array([0.5625, -0.375, 0.1875]), + ) def test_n_dimensional_RGB_to_YCoCg(self): """ - Tests :func:`colour.models.rgb.ycocg.RGB_to_YCoCg` definition + Test :func:`colour.models.rgb.ycocg.RGB_to_YCoCg` definition n-dimensional arrays support. """ @@ -75,7 +74,7 @@ def test_n_dimensional_RGB_to_YCoCg(self): @ignore_numpy_errors def test_nan_RGB_to_YCoCg(self): """ - Tests :func:`colour.models.rgb.ycocg.RGB_to_YCoCg` definition nan + Test :func:`colour.models.rgb.ycocg.RGB_to_YCoCg` definition nan support. """ @@ -88,30 +87,31 @@ def test_nan_RGB_to_YCoCg(self): class TestYCoCg_to_RGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.ycocg.YCoCg_to_RGB` definition unit tests + Define :func:`colour.models.rgb.ycocg.YCoCg_to_RGB` definition unit tests methods. """ def test_YCoCg_to_RGB(self): - """ - Tests :func:`colour.models.rgb.ycocg.YCoCg_to_RGB` definition. - """ + """Test :func:`colour.models.rgb.ycocg.YCoCg_to_RGB` definition.""" np.testing.assert_array_equal( YCoCg_to_RGB(np.array([0.5625, 0.375, 0.1875])), - np.array([0.75, 0.75, 0.0])) + np.array([0.75, 0.75, 0.0]), + ) np.testing.assert_array_equal( YCoCg_to_RGB(np.array([0.5, -0.25, 0.0])), - np.array([0.25, 0.5, 0.75])) + np.array([0.25, 0.5, 0.75]), + ) np.testing.assert_array_equal( YCoCg_to_RGB(np.array([0.5625, -0.375, 0.1875])), - np.array([0.0, 0.75, 0.75])) + np.array([0.0, 0.75, 0.75]), + ) def test_n_dimensional_YCoCg_to_RGB(self): """ - Tests :func:`colour.models.rgb.ycocg.YCoCg_to_RGB` definition + Test :func:`colour.models.rgb.ycocg.YCoCg_to_RGB` definition n-dimensional arrays support. """ @@ -139,7 +139,7 @@ def test_n_dimensional_YCoCg_to_RGB(self): @ignore_numpy_errors def test_nan_YCoCg_to_RGB(self): """ - Tests :func:`colour.models.rgb.ycocg.YCoCg_to_RGB` definition nan + Test :func:`colour.models.rgb.ycocg.YCoCg_to_RGB` definition nan support. """ @@ -150,5 +150,5 @@ def test_nan_YCoCg_to_RGB(self): YCoCg_to_RGB(YCoCg) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/__init__.py b/colour/models/rgb/transfer_functions/__init__.py index 723394611b..c29453ea09 100644 --- a/colour/models/rgb/transfer_functions/__init__.py +++ b/colour/models/rgb/transfer_functions/__init__.py @@ -1,31 +1,59 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from functools import partial -from colour.utilities import (CaseInsensitiveMapping, filter_kwargs, - usage_warning) -from colour.utilities.deprecation import handle_arguments_deprecation +from colour.hints import ( + Any, + FloatingOrArrayLike, + FloatingOrNDArray, + IntegerOrArrayLike, + IntegerOrNDArray, + Literal, + Union, +) +from colour.utilities import ( + CaseInsensitiveMapping, + filter_kwargs, + usage_warning, + validate_method, +) from .common import CV_range, legal_to_full, full_to_legal from .gamma import gamma_function -from .aces import (log_encoding_ACESproxy, log_decoding_ACESproxy, - log_encoding_ACEScc, log_decoding_ACEScc, - log_encoding_ACEScct, log_decoding_ACEScct) +from .aces import ( + log_encoding_ACESproxy, + log_decoding_ACESproxy, + log_encoding_ACEScc, + log_decoding_ACEScc, + log_encoding_ACEScct, + log_decoding_ACEScct, +) from .arib_std_b67 import oetf_ARIBSTDB67, oetf_inverse_ARIBSTDB67 from .arri_alexa_log_c import log_encoding_ALEXALogC, log_decoding_ALEXALogC -from .canon_log import (log_encoding_CanonLog, log_decoding_CanonLog, - log_encoding_CanonLog2, log_decoding_CanonLog2, - log_encoding_CanonLog3, log_decoding_CanonLog3) +from .blackmagic_design import ( + oetf_BlackmagicFilmGeneration5, + oetf_inverse_BlackmagicFilmGeneration5, +) +from .canon_log import ( + log_encoding_CanonLog, + log_decoding_CanonLog, + log_encoding_CanonLog2, + log_decoding_CanonLog2, + log_encoding_CanonLog3, + log_decoding_CanonLog3, +) from .cineon import log_encoding_Cineon, log_decoding_Cineon +from .davinci_intermediate import ( + oetf_DaVinciIntermediate, + oetf_inverse_DaVinciIntermediate, +) from .dcdm import eotf_inverse_DCDM, eotf_DCDM from .dicom_gsdf import eotf_inverse_DICOMGSDF, eotf_DICOMGSDF from .dji_dlog import log_encoding_DJIDLog, log_decoding_DJIDLog from .exponent import exponent_function_basic, exponent_function_monitor_curve from .filmic_pro import log_encoding_FilmicPro6, log_decoding_FilmicPro6 -from .filmlight_tlog import (log_encoding_FilmLightTLog, - log_decoding_FilmLightTLog) +from .filmlight_tlog import ( + log_encoding_FilmLightTLog, + log_decoding_FilmLightTLog, +) from .gopro import log_encoding_Protune, log_decoding_Protune from .itur_bt_601 import oetf_BT601, oetf_inverse_BT601 from .itur_bt_709 import oetf_BT709, oetf_inverse_BT709 @@ -33,221 +61,372 @@ from .itur_bt_2020 import eotf_inverse_BT2020, eotf_BT2020 from .st_2084 import eotf_inverse_ST2084, eotf_ST2084 from .itur_bt_2100 import ( - oetf_PQ_BT2100, oetf_inverse_PQ_BT2100, eotf_PQ_BT2100, - eotf_inverse_PQ_BT2100, ootf_PQ_BT2100, ootf_inverse_PQ_BT2100, - oetf_HLG_BT2100, oetf_inverse_HLG_BT2100, BT2100_HLG_EOTF_METHODS, - eotf_HLG_BT2100, BT2100_HLG_EOTF_INVERSE_METHODS, eotf_inverse_HLG_BT2100, - BT2100_HLG_OOTF_METHODS, ootf_HLG_BT2100, BT2100_HLG_OOTF_INVERSE_METHODS, - ootf_inverse_HLG_BT2100) + oetf_PQ_BT2100, + oetf_inverse_PQ_BT2100, + eotf_PQ_BT2100, + eotf_inverse_PQ_BT2100, + ootf_PQ_BT2100, + ootf_inverse_PQ_BT2100, + oetf_HLG_BT2100, + oetf_inverse_HLG_BT2100, + BT2100_HLG_EOTF_METHODS, + eotf_HLG_BT2100, + BT2100_HLG_EOTF_INVERSE_METHODS, + eotf_inverse_HLG_BT2100, + BT2100_HLG_OOTF_METHODS, + ootf_HLG_BT2100, + BT2100_HLG_OOTF_INVERSE_METHODS, + ootf_inverse_HLG_BT2100, +) from .linear import linear_function -from .log import (logarithmic_function_basic, logarithmic_function_quasilog, - logarithmic_function_camera, log_encoding_Log2, - log_decoding_Log2) +from .log import ( + logarithmic_function_basic, + logarithmic_function_quasilog, + logarithmic_function_camera, + log_encoding_Log2, + log_decoding_Log2, +) from .panalog import log_encoding_Panalog, log_decoding_Panalog from .panasonic_vlog import log_encoding_VLog, log_decoding_VLog from .fujifilm_flog import log_encoding_FLog, log_decoding_FLog +from .nikon_nlog import log_encoding_NLog, log_decoding_NLog from .pivoted_log import log_encoding_PivotedLog, log_decoding_PivotedLog -from .red_log import (log_encoding_REDLog, log_decoding_REDLog, - log_encoding_REDLogFilm, log_decoding_REDLogFilm, - LOG3G10_ENCODING_METHODS, LOG3G10_DECODING_METHODS, - log_encoding_Log3G10, log_decoding_Log3G10, - log_encoding_Log3G12, log_decoding_Log3G12) +from .red_log import ( + log_encoding_REDLog, + log_decoding_REDLog, + log_encoding_REDLogFilm, + log_decoding_REDLogFilm, + LOG3G10_ENCODING_METHODS, + LOG3G10_DECODING_METHODS, + log_encoding_Log3G10, + log_decoding_Log3G10, + log_encoding_Log3G12, + log_decoding_Log3G12, +) from .rimm_romm_rgb import ( - cctf_encoding_ROMMRGB, cctf_decoding_ROMMRGB, cctf_encoding_ProPhotoRGB, - cctf_decoding_ProPhotoRGB, cctf_encoding_RIMMRGB, cctf_decoding_RIMMRGB, - log_encoding_ERIMMRGB, log_decoding_ERIMMRGB) + cctf_encoding_ROMMRGB, + cctf_decoding_ROMMRGB, + cctf_encoding_ProPhotoRGB, + cctf_decoding_ProPhotoRGB, + cctf_encoding_RIMMRGB, + cctf_decoding_RIMMRGB, + log_encoding_ERIMMRGB, + log_decoding_ERIMMRGB, +) from .smpte_240m import oetf_SMPTE240M, eotf_SMPTE240M -from .sony_slog import (log_encoding_SLog, log_decoding_SLog, - log_encoding_SLog2, log_decoding_SLog2, - log_encoding_SLog3, log_decoding_SLog3) +from .sony_slog import ( + log_encoding_SLog, + log_decoding_SLog, + log_encoding_SLog2, + log_decoding_SLog2, + log_encoding_SLog3, + log_decoding_SLog3, +) from .srgb import eotf_inverse_sRGB, eotf_sRGB from .viper_log import log_encoding_ViperLog, log_decoding_ViperLog -__all__ = ['CV_range', 'legal_to_full', 'full_to_legal'] -__all__ += ['gamma_function'] -__all__ += [ - 'log_encoding_ACESproxy', 'log_decoding_ACESproxy', 'log_encoding_ACEScc', - 'log_decoding_ACEScc', 'log_encoding_ACEScct', 'log_decoding_ACEScct' -] -__all__ += ['oetf_ARIBSTDB67', 'oetf_inverse_ARIBSTDB67'] -__all__ += ['log_encoding_ALEXALogC', 'log_decoding_ALEXALogC'] -__all__ += [ - 'log_encoding_CanonLog', 'log_decoding_CanonLog', 'log_encoding_CanonLog2', - 'log_decoding_CanonLog2', 'log_encoding_CanonLog3', - 'log_decoding_CanonLog3' -] -__all__ += ['log_encoding_Cineon', 'log_decoding_Cineon'] -__all__ += ['eotf_inverse_DCDM', 'eotf_DCDM'] -__all__ += ['eotf_inverse_DICOMGSDF', 'eotf_DICOMGSDF'] -__all__ += ['log_encoding_DJIDLog', 'log_decoding_DJIDLog'] -__all__ += ['exponent_function_basic', 'exponent_function_monitor_curve'] -__all__ += ['log_encoding_FilmicPro6', 'log_decoding_FilmicPro6'] -__all__ += ['log_encoding_FilmLightTLog', 'log_decoding_FilmLightTLog'] -__all__ += ['log_encoding_Protune', 'log_decoding_Protune'] -__all__ += ['oetf_BT601', 'oetf_inverse_BT601'] -__all__ += ['oetf_BT709', 'oetf_inverse_BT709'] -__all__ += ['eotf_inverse_BT1886', 'eotf_BT1886'] -__all__ += ['eotf_inverse_BT2020', 'eotf_BT2020'] -__all__ += ['eotf_inverse_ST2084', 'eotf_ST2084'] -__all__ += [ - 'oetf_PQ_BT2100', 'oetf_inverse_PQ_BT2100', 'eotf_PQ_BT2100', - 'eotf_inverse_PQ_BT2100', 'ootf_PQ_BT2100', 'ootf_inverse_PQ_BT2100', - 'oetf_HLG_BT2100', 'oetf_inverse_HLG_BT2100', 'BT2100_HLG_EOTF_METHODS', - 'eotf_HLG_BT2100', 'BT2100_HLG_EOTF_INVERSE_METHODS', - 'eotf_inverse_HLG_BT2100', 'BT2100_HLG_OOTF_METHODS', 'ootf_HLG_BT2100', - 'BT2100_HLG_OOTF_INVERSE_METHODS', 'ootf_inverse_HLG_BT2100' -] -__all__ += ['linear_function'] -__all__ += [ - 'logarithmic_function_basic', 'logarithmic_function_quasilog', - 'logarithmic_function_camera', 'log_encoding_Log2', 'log_decoding_Log2' -] -__all__ += ['log_encoding_Panalog', 'log_decoding_Panalog'] -__all__ += ['log_encoding_VLog', 'log_decoding_VLog'] -__all__ += ['log_encoding_FLog', 'log_decoding_FLog'] -__all__ += ['log_encoding_PivotedLog', 'log_decoding_PivotedLog'] -__all__ += [ - 'log_encoding_REDLog', 'log_decoding_REDLog', 'log_encoding_REDLogFilm', - 'log_decoding_REDLogFilm', 'LOG3G10_ENCODING_METHODS', - 'LOG3G10_DECODING_METHODS', 'log_encoding_Log3G10', 'log_decoding_Log3G10', - 'log_encoding_Log3G12', 'log_decoding_Log3G12' -] -__all__ += [ - 'cctf_encoding_ROMMRGB', 'cctf_decoding_ROMMRGB', - 'cctf_encoding_ProPhotoRGB', 'cctf_decoding_ProPhotoRGB', - 'cctf_encoding_RIMMRGB', 'cctf_decoding_RIMMRGB', 'log_encoding_ERIMMRGB', - 'log_decoding_ERIMMRGB' -] -__all__ += ['oetf_SMPTE240M', 'eotf_SMPTE240M'] -__all__ += [ - 'log_encoding_SLog', 'log_decoding_SLog', 'log_encoding_SLog2', - 'log_decoding_SLog2', 'log_encoding_SLog3', 'log_decoding_SLog3' -] -__all__ += ['eotf_inverse_sRGB', 'eotf_sRGB'] -__all__ += ['log_encoding_ViperLog', 'log_decoding_ViperLog'] - -LOG_ENCODINGS = CaseInsensitiveMapping({ - 'ACEScc': log_encoding_ACEScc, - 'ACEScct': log_encoding_ACEScct, - 'ACESproxy': log_encoding_ACESproxy, - 'ALEXA Log C': log_encoding_ALEXALogC, - 'Canon Log 2': log_encoding_CanonLog2, - 'Canon Log 3': log_encoding_CanonLog3, - 'Canon Log': log_encoding_CanonLog, - 'Cineon': log_encoding_Cineon, - 'D-Log': log_encoding_DJIDLog, - 'ERIMM RGB': log_encoding_ERIMMRGB, - 'F-Log': log_encoding_FLog, - 'Filmic Pro 6': log_encoding_FilmicPro6, - 'Log2': log_encoding_Log2, - 'Log3G10': log_encoding_Log3G10, - 'Log3G12': log_encoding_Log3G12, - 'Panalog': log_encoding_Panalog, - 'PLog': log_encoding_PivotedLog, - 'Protune': log_encoding_Protune, - 'REDLog': log_encoding_REDLog, - 'REDLogFilm': log_encoding_REDLogFilm, - 'S-Log': log_encoding_SLog, - 'S-Log2': log_encoding_SLog2, - 'S-Log3': log_encoding_SLog3, - 'T-Log': log_encoding_FilmLightTLog, - 'V-Log': log_encoding_VLog, - 'ViperLog': log_encoding_ViperLog -}) +__all__ = [ + "CV_range", + "legal_to_full", + "full_to_legal", +] +__all__ += [ + "gamma_function", +] +__all__ += [ + "log_encoding_ACESproxy", + "log_decoding_ACESproxy", + "log_encoding_ACEScc", + "log_decoding_ACEScc", + "log_encoding_ACEScct", + "log_decoding_ACEScct", +] +__all__ += [ + "oetf_ARIBSTDB67", + "oetf_inverse_ARIBSTDB67", +] +__all__ += [ + "log_encoding_ALEXALogC", + "log_decoding_ALEXALogC", +] +__all__ += [ + "oetf_BlackmagicFilmGeneration5", + "oetf_inverse_BlackmagicFilmGeneration5", +] +__all__ += [ + "log_encoding_CanonLog", + "log_decoding_CanonLog", + "log_encoding_CanonLog2", + "log_decoding_CanonLog2", + "log_encoding_CanonLog3", + "log_decoding_CanonLog3", +] +__all__ += [ + "log_encoding_Cineon", + "log_decoding_Cineon", +] +__all__ += [ + "oetf_DaVinciIntermediate", + "oetf_inverse_DaVinciIntermediate", +] +__all__ += [ + "eotf_inverse_DCDM", + "eotf_DCDM", +] +__all__ += [ + "eotf_inverse_DICOMGSDF", + "eotf_DICOMGSDF", +] +__all__ += [ + "log_encoding_DJIDLog", + "log_decoding_DJIDLog", +] +__all__ += [ + "exponent_function_basic", + "exponent_function_monitor_curve", +] +__all__ += [ + "log_encoding_FilmicPro6", + "log_decoding_FilmicPro6", +] +__all__ += [ + "log_encoding_FilmLightTLog", + "log_decoding_FilmLightTLog", +] +__all__ += [ + "log_encoding_Protune", + "log_decoding_Protune", +] +__all__ += [ + "oetf_BT601", + "oetf_inverse_BT601", +] +__all__ += [ + "oetf_BT709", + "oetf_inverse_BT709", +] +__all__ += [ + "eotf_inverse_BT1886", + "eotf_BT1886", +] +__all__ += [ + "eotf_inverse_BT2020", + "eotf_BT2020", +] +__all__ += [ + "eotf_inverse_ST2084", + "eotf_ST2084", +] +__all__ += [ + "oetf_PQ_BT2100", + "oetf_inverse_PQ_BT2100", + "eotf_PQ_BT2100", + "eotf_inverse_PQ_BT2100", + "ootf_PQ_BT2100", + "ootf_inverse_PQ_BT2100", + "oetf_HLG_BT2100", + "oetf_inverse_HLG_BT2100", + "BT2100_HLG_EOTF_METHODS", + "eotf_HLG_BT2100", + "BT2100_HLG_EOTF_INVERSE_METHODS", + "eotf_inverse_HLG_BT2100", + "BT2100_HLG_OOTF_METHODS", + "ootf_HLG_BT2100", + "BT2100_HLG_OOTF_INVERSE_METHODS", + "ootf_inverse_HLG_BT2100", +] +__all__ += [ + "linear_function", +] +__all__ += [ + "logarithmic_function_basic", + "logarithmic_function_quasilog", + "logarithmic_function_camera", + "log_encoding_Log2", + "log_decoding_Log2", +] +__all__ += [ + "log_encoding_Panalog", + "log_decoding_Panalog", +] +__all__ += [ + "log_encoding_VLog", + "log_decoding_VLog", +] +__all__ += [ + "log_encoding_FLog", + "log_decoding_FLog", +] +__all__ += [ + "log_encoding_NLog", + "log_decoding_NLog", +] +__all__ += [ + "log_encoding_PivotedLog", + "log_decoding_PivotedLog", +] +__all__ += [ + "log_encoding_REDLog", + "log_decoding_REDLog", + "log_encoding_REDLogFilm", + "log_decoding_REDLogFilm", + "LOG3G10_ENCODING_METHODS", + "LOG3G10_DECODING_METHODS", + "log_encoding_Log3G10", + "log_decoding_Log3G10", + "log_encoding_Log3G12", + "log_decoding_Log3G12", +] +__all__ += [ + "cctf_encoding_ROMMRGB", + "cctf_decoding_ROMMRGB", + "cctf_encoding_ProPhotoRGB", + "cctf_decoding_ProPhotoRGB", + "cctf_encoding_RIMMRGB", + "cctf_decoding_RIMMRGB", + "log_encoding_ERIMMRGB", + "log_decoding_ERIMMRGB", +] +__all__ += [ + "oetf_SMPTE240M", + "eotf_SMPTE240M", +] +__all__ += [ + "log_encoding_SLog", + "log_decoding_SLog", + "log_encoding_SLog2", + "log_decoding_SLog2", + "log_encoding_SLog3", + "log_decoding_SLog3", +] +__all__ += [ + "eotf_inverse_sRGB", + "eotf_sRGB", +] +__all__ += [ + "log_encoding_ViperLog", + "log_decoding_ViperLog", +] + +LOG_ENCODINGS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "ACEScc": log_encoding_ACEScc, + "ACEScct": log_encoding_ACEScct, + "ACESproxy": log_encoding_ACESproxy, + "ALEXA Log C": log_encoding_ALEXALogC, + "Canon Log 2": log_encoding_CanonLog2, + "Canon Log 3": log_encoding_CanonLog3, + "Canon Log": log_encoding_CanonLog, + "Cineon": log_encoding_Cineon, + "D-Log": log_encoding_DJIDLog, + "ERIMM RGB": log_encoding_ERIMMRGB, + "F-Log": log_encoding_FLog, + "Filmic Pro 6": log_encoding_FilmicPro6, + "Log2": log_encoding_Log2, + "Log3G10": log_encoding_Log3G10, + "Log3G12": log_encoding_Log3G12, + "N-Log": log_encoding_NLog, + "PLog": log_encoding_PivotedLog, + "Panalog": log_encoding_Panalog, + "Protune": log_encoding_Protune, + "REDLog": log_encoding_REDLog, + "REDLogFilm": log_encoding_REDLogFilm, + "S-Log": log_encoding_SLog, + "S-Log2": log_encoding_SLog2, + "S-Log3": log_encoding_SLog3, + "T-Log": log_encoding_FilmLightTLog, + "V-Log": log_encoding_VLog, + "ViperLog": log_encoding_ViperLog, + } +) LOG_ENCODINGS.__doc__ = """ Supported *log* encoding functions. - -LOG_ENCODINGS : CaseInsensitiveMapping - **{'ACEScc', 'ACEScct', 'ACESproxy', 'ALEXA Log C', 'Canon Log 2', - 'Canon Log 3', 'Canon Log', 'Cineon', 'D-Log', 'ERIMM RGB', 'F-Log', - 'Filmic Pro 6', 'Log2', 'Log3G10', 'Log3G12', 'Panalog', 'PLog', - 'Protune', 'REDLog', 'REDLogFilm', 'S-Log', 'S-Log2', 'S-Log3', - 'T-Log', 'V-Log', 'ViperLog'}** """ -def log_encoding(value, function='Cineon', **kwargs): +def log_encoding( + value: FloatingOrArrayLike, + function: Union[ + Literal[ + "ACEScc", + "ACEScct", + "ACESproxy", + "ALEXA Log C", + "Canon Log 2", + "Canon Log 3", + "Canon Log", + "Cineon", + "D-Log", + "ERIMM RGB", + "F-Log", + "Filmic Pro 6", + "Log2", + "Log3G10", + "Log3G12", + "N-Log", + "PLog", + "Panalog", + "Protune", + "REDLog", + "REDLogFilm", + "S-Log", + "S-Log2", + "S-Log3", + "T-Log", + "V-Log", + "ViperLog", + ], + str, + ] = "Cineon", + **kwargs: Any +) -> Union[FloatingOrNDArray, IntegerOrNDArray]: """ - Encodes linear-light values to :math:`R'G'B'` video component signal - value using given *log* function. + Encode *scene-referred* exposure values to :math:`R'G'B'` video component + signal value using given *log* encoding function. Parameters ---------- - value : numeric or array_like - Value. - function : unicode, optional - **{'ACEScc', 'ACEScct', 'ACESproxy', 'ALEXA Log C', 'Canon Log 2', - 'Canon Log 3', 'Canon Log', 'Cineon', 'D-Log', 'ERIMM RGB', 'F-Log', - 'Filmic Pro 6', 'Log2', 'Log3G10', 'Log3G12', 'Panalog', 'PLog', - 'Protune', 'REDLog', 'REDLogFilm', 'S-Log', 'S-Log2', 'S-Log3', - 'T-Log', 'V-Log', 'ViperLog'}**, - Computation function. + value + *Scene-referred* exposure values. + function + *Log* encoding function. Other Parameters ---------------- - EI : int, optional - {:func:`colour.models.log_encoding_ALEXALogC`}, - Ei. - E_clip : numeric, optional - {:func:`colour.models.log_encoding_ERIMMRGB`}, - Maximum exposure limit. - E_min : numeric, optional - {:func:`colour.models.log_encoding_ERIMMRGB`}, - Minimum exposure limit. - I_max : numeric, optional - {:func:`colour.models.log_encoding_ERIMMRGB`}, - Maximum code value: 255, 4095 and 650535 for respectively 8-bit, - 12-bit and 16-bit per channel. - bit_depth : unicode, optional - {:func:`colour.models.log_encoding_ACESproxy`, - :func:`colour.models.log_encoding_SLog`, - :func:`colour.models.log_encoding_SLog2`}, - **{8, 10, 12}**, - Bit depth used for conversion, *ACESproxy* uses **{10, 12}**. - black_offset : numeric or array_like - {:func:`colour.models.log_encoding_Cineon`, + kwargs + {:func:`colour.models.log_encoding_ACEScc`, + :func:`colour.models.log_encoding_ACEScct`, + :func:`colour.models.log_encoding_ACESproxy`, + :func:`colour.models.log_encoding_ALEXALogC`, + :func:`colour.models.log_encoding_CanonLog2`, + :func:`colour.models.log_encoding_CanonLog3`, + :func:`colour.models.log_encoding_CanonLog`, + :func:`colour.models.log_encoding_Cineon`, + :func:`colour.models.log_encoding_DJIDLog`, + :func:`colour.models.log_encoding_ERIMMRGB`, + :func:`colour.models.log_encoding_FLog`, + :func:`colour.models.log_encoding_FilmicPro6`, + :func:`colour.models.log_encoding_Log2`, + :func:`colour.models.log_encoding_Log3G10`, + :func:`colour.models.log_encoding_Log3G12`, + :func:`colour.models.log_encoding_NLog`, + :func:`colour.models.log_encoding_PivotedLog`, :func:`colour.models.log_encoding_Panalog`, + :func:`colour.models.log_encoding_Protune`, :func:`colour.models.log_encoding_REDLog`, - :func:`colour.models.log_encoding_REDLogFilm`}, - Black offset. - density_per_code_value : numeric or array_like - {:func:`colour.models.log_encoding_PivotedLog`}, - Density per code value. - firmware : unicode, optional - {:func:`colour.models.log_encoding_ALEXALogC`}, - **{'SUP 3.x', 'SUP 2.x'}**, - Alexa firmware version. - in_reflection : bool, optional - {:func:`colour.models.log_encoding_SLog`, - :func:`colour.models.log_encoding_SLog2`}, - Whether the light level :math:`x` to a camera is reflection. - linear_reference : numeric or array_like - {:func:`colour.models.log_encoding_PivotedLog`}, - Linear reference. - log_reference : numeric or array_like - {:func:`colour.models.log_encoding_PivotedLog`}, - Log reference. - method : unicode, optional - {:func:`colour.models.log_encoding_Log3G10`}, - Whether to use the *Log3G10* *v1* or *v2* log encoding curve. - out_normalised_code_value : bool, optional - {:func:`colour.models.log_encoding_SLog`, + :func:`colour.models.log_encoding_REDLogFilm`, + :func:`colour.models.log_encoding_SLog`, :func:`colour.models.log_encoding_SLog2`, - :func:`colour.models.log_encoding_SLog3`}, - Whether the non-linear *Sony S-Log*, *Sony S-Log2* or *Sony S-Log3* - data :math:`y` is encoded as normalised code values. - negative_gamma : numeric or array_like - {:func:`colour.models.log_encoding_PivotedLog`}, - Negative gamma. - method : unicode, optional - {:func:`colour.models.log_encoding_ALEXALogC`}, - **{'Linear Scene Exposure Factor', 'Normalised Sensor Signal'}**, - Conversion method. + :func:`colour.models.log_encoding_SLog3`, + :func:`colour.models.log_encoding_FilmLightTLog`, + :func:`colour.models.log_encoding_VLog`, + :func:`colour.models.log_encoding_ViperLog`}, + See the documentation of the previously listed definitions. Returns ------- - numeric or ndarray - *Log* value. + :class:`numpy.floating` or :class:`numpy.integer` or :class:`numpy.ndarray` + *Log* values. Examples -------- @@ -262,137 +441,136 @@ def log_encoding(value, function='Cineon', **kwargs): 0.3849708... """ - function = handle_arguments_deprecation({ - 'ArgumentRenamed': [['curve', 'function']], - }, **kwargs).get('function', function) - - function = LOG_ENCODINGS[function] - - return function(value, **filter_kwargs(function, **kwargs)) - - -LOG_DECODINGS = CaseInsensitiveMapping({ - 'ACEScc': log_decoding_ACEScc, - 'ACEScct': log_decoding_ACEScct, - 'ACESproxy': log_decoding_ACESproxy, - 'ALEXA Log C': log_decoding_ALEXALogC, - 'Canon Log 2': log_decoding_CanonLog2, - 'Canon Log 3': log_decoding_CanonLog3, - 'Canon Log': log_decoding_CanonLog, - 'Cineon': log_decoding_Cineon, - 'D-Log': log_decoding_DJIDLog, - 'ERIMM RGB': log_decoding_ERIMMRGB, - 'F-Log': log_decoding_FLog, - 'Filmic Pro 6': log_decoding_FilmicPro6, - 'Log2': log_decoding_Log2, - 'Log3G10': log_decoding_Log3G10, - 'Log3G12': log_decoding_Log3G12, - 'Panalog': log_decoding_Panalog, - 'PLog': log_decoding_PivotedLog, - 'Protune': log_decoding_Protune, - 'REDLog': log_decoding_REDLog, - 'REDLogFilm': log_decoding_REDLogFilm, - 'S-Log': log_decoding_SLog, - 'S-Log2': log_decoding_SLog2, - 'S-Log3': log_decoding_SLog3, - 'T-Log': log_decoding_FilmLightTLog, - 'V-Log': log_decoding_VLog, - 'ViperLog': log_decoding_ViperLog -}) + function = validate_method( + function, + LOG_ENCODINGS, + '"{0}" "log" encoding function is invalid, it must be one of {1}!', + ) + + callable_ = LOG_ENCODINGS[function] + + return callable_(value, **filter_kwargs(callable_, **kwargs)) + + +LOG_DECODINGS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "ACEScc": log_decoding_ACEScc, + "ACEScct": log_decoding_ACEScct, + "ACESproxy": log_decoding_ACESproxy, + "ALEXA Log C": log_decoding_ALEXALogC, + "Canon Log 2": log_decoding_CanonLog2, + "Canon Log 3": log_decoding_CanonLog3, + "Canon Log": log_decoding_CanonLog, + "Cineon": log_decoding_Cineon, + "D-Log": log_decoding_DJIDLog, + "ERIMM RGB": log_decoding_ERIMMRGB, + "F-Log": log_decoding_FLog, + "Filmic Pro 6": log_decoding_FilmicPro6, + "Log2": log_decoding_Log2, + "Log3G10": log_decoding_Log3G10, + "Log3G12": log_decoding_Log3G12, + "N-Log": log_decoding_NLog, + "PLog": log_decoding_PivotedLog, + "Panalog": log_decoding_Panalog, + "Protune": log_decoding_Protune, + "REDLog": log_decoding_REDLog, + "REDLogFilm": log_decoding_REDLogFilm, + "S-Log": log_decoding_SLog, + "S-Log2": log_decoding_SLog2, + "S-Log3": log_decoding_SLog3, + "T-Log": log_decoding_FilmLightTLog, + "V-Log": log_decoding_VLog, + "ViperLog": log_decoding_ViperLog, + } +) LOG_DECODINGS.__doc__ = """ Supported *log* decoding functions. - -LOG_DECODINGS : CaseInsensitiveMapping - **{'ACEScc', 'ACEScct', 'ACESproxy', 'ALEXA Log C', 'Canon Log 2', - 'Canon Log 3', 'Canon Log', 'Cineon', 'D-Log', 'ERIMM RGB', 'F-Log', - 'Filmic Pro 6', 'Log2', 'Log3G10', 'Log3G12', 'Panalog', 'PLog', - 'Protune', 'REDLog', 'REDLogFilm', 'S-Log', 'S-Log2', 'S-Log3', - 'T-Log', 'V-Log', 'ViperLog'}** """ -def log_decoding(value, function='Cineon', **kwargs): +def log_decoding( + value: Union[FloatingOrArrayLike, IntegerOrArrayLike], + function: Union[ + Literal[ + "ACEScc", + "ACEScct", + "ACESproxy", + "ALEXA Log C", + "Canon Log 2", + "Canon Log 3", + "Canon Log", + "Cineon", + "D-Log", + "ERIMM RGB", + "F-Log", + "Filmic Pro 6", + "Log2", + "Log3G10", + "Log3G12", + "N-Log", + "PLog", + "Panalog", + "Protune", + "REDLog", + "REDLogFilm", + "S-Log", + "S-Log2", + "S-Log3", + "T-Log", + "V-Log", + "ViperLog", + ], + str, + ] = "Cineon", + **kwargs: Any +) -> FloatingOrNDArray: """ - Decodes :math:`R'G'B'` video component signal value to linear-light values - using given *log* function. + Decode :math:`R'G'B'` video component signal value to *scene-referred* + exposure values using given *log* decoding function. Parameters ---------- - value : numeric or array_like - Value. - function : unicode, optional - **{'ACEScc', 'ACEScct', 'ACESproxy', 'ALEXA Log C', 'Canon Log 2', - 'Canon Log 3', 'Canon Log', 'Cineon', 'D-Log', 'ERIMM RGB', 'F-Log', - 'Filmic Pro 6', 'Log2', 'Log3G10', 'Log3G12', 'Panalog', 'PLog', - 'Protune', 'REDLog', 'REDLogFilm', 'S-Log', 'S-Log2', 'S-Log3', - 'T-Log', 'V-Log', 'ViperLog'}**, - Computation function. + value + *Log* values. + function + *Log* decoding function. Other Parameters ---------------- - EI : int, optional - {:func:`colour.models.log_decoding_ALEXALogC`}, - Ei. - E_clip : numeric, optional - {:func:`colour.models.log_decoding_ERIMMRGB`}, - Maximum exposure limit. - E_min : numeric, optional - {:func:`colour.models.log_decoding_ERIMMRGB`}, - Minimum exposure limit. - I_max : numeric, optional - {:func:`colour.models.log_decoding_ERIMMRGB`}, - Maximum code value: 255, 4095 and 650535 for respectively 8-bit, - 12-bit and 16-bit per channel. - bit_depth : int, optional - {:func:`colour.models.log_decoding_ACESproxy`, - :func:`colour.models.log_decoding_SLog`, - :func:`colour.models.log_decoding_SLog2`}, - **{8, 10, 12}**, - Bit depth used for conversion, *ACESproxy* uses **{10, 12}**. - black_offset : numeric or array_like - {:func:`colour.models.log_decoding_Cineon`, + kwargs + {:func:`colour.models.log_decoding_ACEScc`, + :func:`colour.models.log_decoding_ACEScct`, + :func:`colour.models.log_decoding_ACESproxy`, + :func:`colour.models.log_decoding_ALEXALogC`, + :func:`colour.models.log_decoding_CanonLog2`, + :func:`colour.models.log_decoding_CanonLog3`, + :func:`colour.models.log_decoding_CanonLog`, + :func:`colour.models.log_decoding_Cineon`, + :func:`colour.models.log_decoding_DJIDLog`, + :func:`colour.models.log_decoding_ERIMMRGB`, + :func:`colour.models.log_decoding_FLog`, + :func:`colour.models.log_decoding_FilmicPro6`, + :func:`colour.models.log_decoding_Log2`, + :func:`colour.models.log_decoding_Log3G10`, + :func:`colour.models.log_decoding_Log3G12`, + :func:`colour.models.log_decoding_NLog`, + :func:`colour.models.log_decoding_PivotedLog`, :func:`colour.models.log_decoding_Panalog`, + :func:`colour.models.log_decoding_Protune`, :func:`colour.models.log_decoding_REDLog`, - :func:`colour.models.log_decoding_REDLogFilm`}, - Black offset. - density_per_code_value : numeric or array_like - {:func:`colour.models.log_decoding_PivotedLog`}, - Density per code value. - firmware : unicode, optional - {:func:`colour.models.log_decoding_ALEXALogC`}, - **{'SUP 3.x', 'SUP 2.x'}**, - Alexa firmware version. - in_normalised_code_value : bool, optional - {:func:`colour.models.log_decoding_SLog`, + :func:`colour.models.log_decoding_REDLogFilm`, + :func:`colour.models.log_decoding_SLog`, :func:`colour.models.log_decoding_SLog2`, - :func:`colour.models.log_decoding_SLog3`}, - Whether the non-linear *Sony S-Log*, *Sony S-Log2* or *Sony S-Log3* - data :math:`y` is encoded as normalised code values. - linear_reference : numeric or array_like - {:func:`colour.models.log_decoding_PivotedLog`}, - Linear reference. - log_reference : numeric or array_like - {:func:`colour.models.log_decoding_PivotedLog`}, - Log reference. - method : unicode, optional - {:func:`colour.models.log_decoding_Log3G10`}, - Whether to use the *Log3G10* *v1* or *v2* log encoding curve. - negative_gamma : numeric or array_like - {:func:`colour.models.log_decoding_PivotedLog`}, - Negative gamma. - out_reflection : bool, optional - {:func:`colour.models.log_decoding_SLog`, - :func:`colour.models.log_decoding_SLog2`}, - Whether the light level :math:`x` to a camera is reflection. - method : unicode, optional - {:func:`colour.models.log_decoding_ALEXALogC`}, - **{'Linear Scene Exposure Factor', 'Normalised Sensor Signal'}**, - Conversion method. + :func:`colour.models.log_decoding_SLog3`, + :func:`colour.models.log_decoding_FilmLightTLog`, + :func:`colour.models.log_decoding_VLog`, + :func:`colour.models.log_decoding_ViperLog`}, + See the documentation of the previously listed definitions. Returns ------- - numeric or ndarray - *Log* value. + :class:`numpy.floating` or :class:`numpy.ndarray` + *Scene-referred* exposure values. Examples -------- @@ -409,67 +587,88 @@ def log_decoding(value, function='Cineon', **kwargs): 0.1... """ - function = handle_arguments_deprecation({ - 'ArgumentRenamed': [['curve', 'function']], - }, **kwargs).get('function', function) + function = validate_method( + function, + LOG_DECODINGS, + '"{0}" "log" decoding function is invalid, it must be one of {1}!', + ) - function = LOG_DECODINGS[function] + callable_ = LOG_DECODINGS[function] - return function(value, **filter_kwargs(function, **kwargs)) + return callable_(value, **filter_kwargs(callable_, **kwargs)) -__all__ += ['LOG_ENCODINGS', 'LOG_DECODINGS'] -__all__ += ['log_encoding', 'log_decoding'] +__all__ += [ + "LOG_ENCODINGS", + "LOG_DECODINGS", +] +__all__ += [ + "log_encoding", + "log_decoding", +] -OETFS = CaseInsensitiveMapping({ - 'ARIB STD-B67': oetf_ARIBSTDB67, - 'ITU-R BT.2100 HLG': oetf_HLG_BT2100, - 'ITU-R BT.2100 PQ': oetf_PQ_BT2100, - 'ITU-R BT.601': oetf_BT601, - 'ITU-R BT.709': oetf_BT709, - 'SMPTE 240M': oetf_SMPTE240M, -}) +OETFS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "ARIB STD-B67": oetf_ARIBSTDB67, + "Blackmagic Film Generation 5": oetf_BlackmagicFilmGeneration5, + "DaVinci Intermediate": oetf_DaVinciIntermediate, + "ITU-R BT.2100 HLG": oetf_HLG_BT2100, + "ITU-R BT.2100 PQ": oetf_PQ_BT2100, + "ITU-R BT.601": oetf_BT601, + "ITU-R BT.709": oetf_BT709, + "SMPTE 240M": oetf_SMPTE240M, + } +) OETFS.__doc__ = """ Supported opto-electrical transfer functions (OETFs / OECFs). - -OETFS : CaseInsensitiveMapping - **{'sRGB', 'ARIB STD-B67', 'ITU-R BT.2100 HLG', 'ITU-R BT.2100 PQ', - 'ITU-R BT.601', 'ITU-R BT.709', 'SMPTE 240M', 'ST 2084'}** """ -def oetf(value, function='ITU-R BT.709', **kwargs): +def oetf( + value: FloatingOrArrayLike, + function: Union[ + Literal[ + "ARIB STD-B67", + "Blackmagic Film Generation 5", + "DaVinci Intermediate", + "ITU-R BT.2100 HLG", + "ITU-R BT.2100 PQ", + "ITU-R BT.601", + "ITU-R BT.709", + "SMPTE 240M", + ], + str, + ] = "ITU-R BT.709", + **kwargs: Any +) -> FloatingOrNDArray: """ - Encodes estimated tristimulus values in a scene to :math:`R'G'B'` video + Encode estimated tristimulus values in a scene to :math:`R'G'B'` video component signal value using given opto-electronic transfer function - (OETF / OECF). + (OETF). Parameters ---------- - value : numeric or array_like + value Value. - function : unicode, optional - **{'ITU-R BT.709', 'ARIB STD-B67', 'ITU-R BT.2100 HLG', - 'ITU-R BT.2100 PQ', 'ITU-R BT.601', 'SMPTE 240M', 'ST 2084'}**, - Opto-electronic transfer function (OETF / OECF). + function + Opto-electronic transfer function (OETF). Other Parameters ---------------- - E_clip : numeric, optional - {:func:`colour.models.cctf_encoding_RIMMRGB`}, - Maximum exposure level. - I_max : numeric, optional - {:func:`colour.models.cctf_encoding_ROMMRGB`, - :func:`colour.models.cctf_encoding_RIMMRGB`}, - Maximum code value: 255, 4095 and 650535 for respectively 8-bit, - 12-bit and 16-bit per channel. - r : numeric, optional - {:func:`colour.models.oetf_ARIBSTDB67`}, - Video level corresponding to reference white level. + kwargs + {:func:`colour.models.oetf_ARIBSTDB67`, + :func:`colour.models.oetf_BlackmagicFilmGeneration5`, + :func:`colour.models.oetf_DaVinciIntermediate`, + :func:`colour.models.oetf_HLG_BT2100`, + :func:`colour.models.oetf_PQ_BT2100`, + :func:`colour.models.oetf_BT601`, + :func:`colour.models.oetf_BT709`, + :func:`colour.models.oetf_SMPTE240M`}, + See the documentation of the previously listed definitions. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` :math:`R'G'B'` video component signal value. Examples @@ -480,51 +679,75 @@ def oetf(value, function='ITU-R BT.709', **kwargs): 0.4090077... """ - function = OETFS[function] + function = validate_method( + function, OETFS, '"{0}" "OETF" is invalid, it must be one of {1}!' + ) - return function(value, **filter_kwargs(function, **kwargs)) + callable_ = OETFS[function] + return callable_(value, **filter_kwargs(callable_, **kwargs)) -OETF_INVERSES = CaseInsensitiveMapping({ - 'ARIB STD-B67': oetf_inverse_ARIBSTDB67, - 'ITU-R BT.2100 HLG': oetf_inverse_HLG_BT2100, - 'ITU-R BT.2100 PQ': oetf_inverse_PQ_BT2100, - 'ITU-R BT.601': oetf_inverse_BT601, - 'ITU-R BT.709': oetf_inverse_BT709, -}) + +OETF_INVERSES: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "ARIB STD-B67": oetf_inverse_ARIBSTDB67, + "Blackmagic Film Generation 5": oetf_inverse_BlackmagicFilmGeneration5, + "DaVinci Intermediate": oetf_inverse_DaVinciIntermediate, + "ITU-R BT.2100 HLG": oetf_inverse_HLG_BT2100, + "ITU-R BT.2100 PQ": oetf_inverse_PQ_BT2100, + "ITU-R BT.601": oetf_inverse_BT601, + "ITU-R BT.709": oetf_inverse_BT709, + } +) OETF_INVERSES.__doc__ = """ Supported inverse opto-electrical transfer functions (OETFs / OECFs). - -OETF_INVERSES : CaseInsensitiveMapping - **{'ARIB STD-B67', 'ITU-R BT.2100 HLG', 'ITU-R BT.2100 PQ', - 'ITU-R BT.601', 'ITU-R BT.709'}** """ -def oetf_inverse(value, function='ITU-R BT.709', **kwargs): +def oetf_inverse( + value: FloatingOrArrayLike, + function: Union[ + Literal[ + "ARIB STD-B67", + "Blackmagic Film Generation 5", + "DaVinci Intermediate", + "ITU-R BT.2100 HLG", + "ITU-R BT.2100 PQ", + "ITU-R BT.601", + "ITU-R BT.709", + ], + str, + ] = "ITU-R BT.709", + **kwargs: Any +) -> FloatingOrNDArray: """ - Decodes :math:`R'G'B'` video component signal value to tristimulus values + Decode :math:`R'G'B'` video component signal value to tristimulus values at the display using given inverse opto-electronic transfer function - (OETF / OECF). + (OETF). Parameters ---------- - value : numeric or array_like + value Value. - function : unicode, optional - **{'ITU-R BT.709', 'ARIB STD-B67', 'ITU-R BT.2100 HLG', - 'ITU-R BT.2100 PQ', 'ITU-R BT.601', }**, - Inverse opto-electronic transfer function (OETF / OECF). + function + Inverse opto-electronic transfer function (OETF). Other Parameters ---------------- - r : numeric, optional - {:func:`colour.models.oetf_ARIBSTDB67`}, - Video level corresponding to reference white level. + kwargs + {:func:`colour.models.oetf_inverse_ARIBSTDB67`, + :func:`colour.models.oetf_inverse_BlackmagicFilmGeneration5`, + :func:`colour.models.oetf_inverse_DaVinciIntermediate`, + :func:`colour.models.oetf_inverse_HLG_BT2100`, + :func:`colour.models.oetf_inverse_PQ_BT2100`, + :func:`colour.models.oetf_inverse_BT601`, + :func:`colour.models.oetf_inverse_BT709`}, + See the documentation of the previously listed definitions. + Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Tristimulus values at the display. Examples @@ -536,87 +759,81 @@ def oetf_inverse(value, function='ITU-R BT.709', **kwargs): 0.1... """ - function = OETF_INVERSES[function] - - return function(value, **filter_kwargs(function, **kwargs)) - - -EOTFS = CaseInsensitiveMapping({ - 'DCDM': eotf_DCDM, - 'DICOM GSDF': eotf_DICOMGSDF, - 'ITU-R BT.1886': eotf_BT1886, - 'ITU-R BT.2020': eotf_BT2020, - 'ITU-R BT.2100 HLG': eotf_HLG_BT2100, - 'ITU-R BT.2100 PQ': eotf_PQ_BT2100, - 'SMPTE 240M': eotf_SMPTE240M, - 'ST 2084': eotf_ST2084, - 'sRGB': eotf_sRGB, -}) + function = validate_method( + function, + OETF_INVERSES, + '"{0}" inverse "OETF" is invalid, it must be one of {1}!', + ) + + callable_ = OETF_INVERSES[function] + + return callable_(value, **filter_kwargs(callable_, **kwargs)) + + +EOTFS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "DCDM": eotf_DCDM, + "DICOM GSDF": eotf_DICOMGSDF, + "ITU-R BT.1886": eotf_BT1886, + "ITU-R BT.2020": eotf_BT2020, + "ITU-R BT.2100 HLG": eotf_HLG_BT2100, + "ITU-R BT.2100 PQ": eotf_PQ_BT2100, + "SMPTE 240M": eotf_SMPTE240M, + "ST 2084": eotf_ST2084, + "sRGB": eotf_sRGB, + } +) EOTFS.__doc__ = """ Supported electro-optical transfer functions (EOTFs / EOCFs). - -EOTFS : CaseInsensitiveMapping - **{'DCDM', 'DICOM GSDF', 'ITU-R BT.1886', 'ITU-R BT.2020', - 'ITU-R BT.2100 HLG', 'ITU-R BT.2100 PQ', 'SMPTE 240M', 'ST 2084', 'sRGB'}** """ -def eotf(value, function='ITU-R BT.1886', **kwargs): +def eotf( + value: Union[FloatingOrArrayLike, IntegerOrArrayLike], + function: Union[ + Literal[ + "DCDM", + "DICOM GSDF", + "ITU-R BT.1886", + "ITU-R BT.2020", + "ITU-R BT.2100 HLG", + "ITU-R BT.2100 PQ", + "SMPTE 240M", + "ST 2084", + "sRGB", + ], + str, + ] = "ITU-R BT.1886", + **kwargs: Any +) -> FloatingOrNDArray: """ - Decodes :math:`R'G'B'` video component signal value to tristimulus values - at the display using given electro-optical transfer function (EOTF / EOCF). + Decode :math:`R'G'B'` video component signal value to tristimulus values + at the display using given electro-optical transfer function (EOTF). Parameters ---------- - value : numeric or array_like + value Value. - function : unicode, optional - **{'ITU-R BT.1886', 'DCDM', 'DICOM GSDF', 'ITU-R BT.2020', - 'ITU-R BT.2100 HLG', 'ITU-R BT.2100 PQ', 'SMPTE 240M', 'ST 2084', - 'sRGB'}**, - Electro-optical transfer function (EOTF / EOCF). + function + Electro-optical transfer function (EOTF). Other Parameters ---------------- - E_clip : numeric, optional - {:func:`colour.models.cctf_decoding_RIMMRGB`}, - Maximum exposure level. - I_max : numeric, optional - {:func:`colour.models.cctf_decoding_ROMMRGB`, - :func:`colour.models.cctf_decoding_RIMMRGB`}, - Maximum code value: 255, 4095 and 650535 for respectively 8-bit, - 12-bit and 16-bit per channel. - L_B : numeric, optional - {:func:`colour.models.eotf_BT1886`, - :func:`colour.models.eotf_HLG_BT2100`}, - Screen luminance for black. - L_W : numeric, optional - {:func:`colour.models.eotf_BT1886`, - :func:`colour.models.eotf_HLG_BT2100`}, - Screen luminance for white. - L_p : numeric, optional - {:func:`colour.models.eotf_ST2084`}, - Display peak luminance :math:`cd/m^2`. - gamma : numeric, optional - {:func:`colour.models.eotf_HLG_BT2100`}, - System gamma value, 1.2 at the nominal display peak luminance of - :math:`1000 cd/m^2`. - is_12_bits_system : bool, optional - {:func:`colour.models.eotf_BT2020`}, - *ITU-R BT.2020* *alpha* and *beta* constants are used if system is not - 12-bit. - method : unicode, optional - {:func:`colour.models.eotf_HLG_BT2100`}, - **{'ITU-R BT.2100-1', 'ITU-R BT.2100-2'}** - out_int : bool, optional + kwargs {:func:`colour.models.eotf_DCDM`, - :func:`colour.models.eotf_DICOMGSDF`}, - Whether to return value as integer code value or float equivalent of a - code value at a given bit depth. + :func:`colour.models.eotf_DICOMGSDF`, + :func:`colour.models.eotf_BT1886`, + :func:`colour.models.eotf_BT2020`, + :func:`colour.models.eotf_HLG_BT2100`, + :func:`colour.models.eotf_PQ_BT2100`, + :func:`colour.models.eotf_SMPTE240M`, + :func:`colour.models.eotf_ST2084`, + :func:`colour.models.eotf_sRGB`}, + See the documentation of the previously listed definitions. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Tristimulus values at the display. Examples @@ -631,79 +848,77 @@ def eotf(value, function='ITU-R BT.1886', **kwargs): 0.1... """ - function = EOTFS[function] + function = validate_method( + function, EOTFS, '"{0}" "EOTF" is invalid, it must be one of {1}!' + ) - return function(value, **filter_kwargs(function, **kwargs)) + callable_ = EOTFS[function] + return callable_(value, **filter_kwargs(callable_, **kwargs)) -EOTF_INVERSES = CaseInsensitiveMapping({ - 'DCDM': eotf_inverse_DCDM, - 'DICOM GSDF': eotf_inverse_DICOMGSDF, - 'ITU-R BT.1886': eotf_inverse_BT1886, - 'ITU-R BT.2020': eotf_inverse_BT2020, - 'ITU-R BT.2100 HLG': eotf_inverse_HLG_BT2100, - 'ITU-R BT.2100 PQ': eotf_inverse_PQ_BT2100, - 'ST 2084': eotf_inverse_ST2084, - 'sRGB': eotf_inverse_sRGB, -}) + +EOTF_INVERSES: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "DCDM": eotf_inverse_DCDM, + "DICOM GSDF": eotf_inverse_DICOMGSDF, + "ITU-R BT.1886": eotf_inverse_BT1886, + "ITU-R BT.2020": eotf_inverse_BT2020, + "ITU-R BT.2100 HLG": eotf_inverse_HLG_BT2100, + "ITU-R BT.2100 PQ": eotf_inverse_PQ_BT2100, + "ST 2084": eotf_inverse_ST2084, + "sRGB": eotf_inverse_sRGB, + } +) EOTF_INVERSES.__doc__ = """ Supported inverse electro-optical transfer functions (EOTFs / EOCFs). - -EOTF_INVERSES : CaseInsensitiveMapping - **{'DCDM', 'DICOM GSDF', 'ITU-R BT.1886', 'ITU-R BT.2020', - 'ITU-R BT.2100 HLG', 'ITU-R BT.2100 PQ', 'ST 2084', 'sRGB'}** """ -def eotf_inverse(value, function='ITU-R BT.1886', **kwargs): +def eotf_inverse( + value: FloatingOrArrayLike, + function: Union[ + Literal[ + "DCDM", + "DICOM GSDF", + "ITU-R BT.1886", + "ITU-R BT.2020", + "ITU-R BT.2100 HLG", + "ITU-R BT.2100 PQ", + "ST 2084", + "sRGB", + ], + str, + ] = "ITU-R BT.1886", + **kwargs +) -> Union[FloatingOrNDArray, IntegerOrNDArray]: """ - Encodes estimated tristimulus values in a scene to :math:`R'G'B'` video + Encode estimated tristimulus values in a scene to :math:`R'G'B'` video component signal value using given inverse electro-optical transfer - function (EOTF / EOCF). + function (EOTF). Parameters ---------- - value : numeric or array_like + value Value. - function : unicode, optional - **{'ITU-R BT.1886', 'DCDM', 'DICOM GSDF', 'ITU-R BT.2020', - 'ITU-R BT.2100 HLG', 'ITU-R BT.2100 PQ', 'ST 2084', 'sRGB'}**, - Inverse electro-optical transfer function (EOTF / EOCF). + function + Inverse electro-optical transfer function (EOTF). Other Parameters ---------------- - L_B : numeric, optional - {:func:`colour.models.eotf_inverse_BT1886`, - :func:`colour.models.eotf_inverse_HLG_BT2100`}, - Screen luminance for black. - L_W : numeric, optional - {:func:`colour.models.eotf_inverse_BT1886`, - :func:`colour.models.eotf_inverse_HLG_BT2100`}, - Screen luminance for white. - gamma : numeric, optional - {:func:`colour.models.eotf_HLG_BT2100`}, - System gamma value, 1.2 at the nominal display peak luminance of - :math:`1000 cd/m^2`. - is_12_bits_system : bool, optional - {:func:`colour.models.eotf_inverse_BT2020`}, - *ITU-R BT.2020* *alpha* and *beta* constants are used - if system is not - 12-bit. - L_p : numeric, optional - {:func:`colour.models.eotf_inverse_ST2084`}, - Display peak luminance :math:`cd/m^2`. - method : unicode, optional - {:func:`colour.models.eotf_inverse_HLG_BT2100`}, - **{'ITU-R BT.2100-1', 'ITU-R BT.2100-2'}** - out_int : bool, optional + kwargs {:func:`colour.models.eotf_inverse_DCDM`, - :func:`colour.models.eotf_inverse_DICOMGSDF`}, - Whether to return value as integer code value or float equivalent of a - code value at a given bit depth. + :func:`colour.models.eotf_inverse_DICOMGSDF`, + :func:`colour.models.eotf_inverse_BT1886`, + :func:`colour.models.eotf_inverse_BT2020`, + :func:`colour.models.eotf_inverse_HLG_BT2100`, + :func:`colour.models.eotf_inverse_PQ_BT2100`, + :func:`colour.models.eotf_inverse_ST2084`, + :func:`colour.models.eotf_inverse_sRGB`}, + See the documentation of the previously listed definitions. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.integer` or :class:`numpy.ndarray` :math:`R'G'B'` video component signal value. Examples @@ -715,22 +930,40 @@ def eotf_inverse(value, function='ITU-R BT.1886', **kwargs): 0.4090077... """ - function = EOTF_INVERSES[function] + function = validate_method( + function, + EOTF_INVERSES, + '"{0}" inverse "EOTF" is invalid, it must be one of {1}!', + ) - return function(value, **filter_kwargs(function, **kwargs)) + callable_ = EOTF_INVERSES[function] + return callable_(value, **filter_kwargs(callable_, **kwargs)) -__all__ += ['OETFS', 'OETF_INVERSES', 'EOTFS', 'EOTF_INVERSES'] -__all__ += ['oetf', 'oetf_inverse', 'eotf', 'eotf_inverse'] -CCTF_ENCODINGS = CaseInsensitiveMapping({ - 'Gamma 2.2': partial(gamma_function, exponent=1 / 2.2), - 'Gamma 2.4': partial(gamma_function, exponent=1 / 2.4), - 'Gamma 2.6': partial(gamma_function, exponent=1 / 2.6), - 'ProPhoto RGB': cctf_encoding_ProPhotoRGB, - 'RIMM RGB': cctf_encoding_RIMMRGB, - 'ROMM RGB': cctf_encoding_ROMMRGB, -}) +__all__ += [ + "OETFS", + "OETF_INVERSES", + "EOTFS", + "EOTF_INVERSES", +] +__all__ += [ + "oetf", + "oetf_inverse", + "eotf", + "eotf_inverse", +] + +CCTF_ENCODINGS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Gamma 2.2": partial(gamma_function, exponent=1 / 2.2), + "Gamma 2.4": partial(gamma_function, exponent=1 / 2.4), + "Gamma 2.6": partial(gamma_function, exponent=1 / 2.6), + "ProPhoto RGB": cctf_encoding_ProPhotoRGB, + "RIMM RGB": cctf_encoding_RIMMRGB, + "ROMM RGB": cctf_encoding_ROMMRGB, + } +) CCTF_ENCODINGS.update(LOG_ENCODINGS) CCTF_ENCODINGS.update(OETFS) CCTF_ENCODINGS.update(EOTF_INVERSES) @@ -746,46 +979,97 @@ def eotf_inverse(value, function='ITU-R BT.1886', **kwargs): Warnings -------- For *ITU-R BT.2100*, only the inverse electro-optical transfer functions -(EOTFs / EOCFs) are exposed by this attribute, please refer to the +(EOTFs / EOCFs) are exposed by this attribute, See the :attr:`colour.OETFS` attribute for the opto-electronic transfer functions -(OETF / OECF). - -CCTF_ENCODINGS : CaseInsensitiveMapping - {:attr:`colour.LOG_ENCODINGS`, :attr:`colour.OETFS`, - :attr:`colour.EOTF_INVERSES`} +(OETF). """ -def cctf_encoding(value, function='sRGB', **kwargs): +def cctf_encoding( + value: FloatingOrArrayLike, + function: Union[ + Literal[ + "ACEScc", + "ACEScct", + "ACESproxy", + "ALEXA Log C", + "ARIB STD-B67", + "Blackmagic Film Generation 5", + "Canon Log 2", + "Canon Log 3", + "Canon Log", + "Cineon", + "D-Log", + "DCDM", + "DICOM GSDF", + "DaVinci Intermediate", + "ERIMM RGB", + "F-Log", + "Filmic Pro 6", + "Gamma 2.2", + "Gamma 2.4", + "Gamma 2.6", + "ITU-R BT.1886", + "ITU-R BT.2020", + "ITU-R BT.2100 HLG", + "ITU-R BT.2100 PQ", + "ITU-R BT.601", + "ITU-R BT.709", + "Log2", + "Log3G10", + "Log3G12", + "N-Log", + "PLog", + "Panalog", + "ProPhoto RGB", + "Protune", + "REDLog", + "REDLogFilm", + "RIMM RGB", + "ROMM RGB", + "S-Log", + "S-Log2", + "S-Log3", + "SMPTE 240M", + "ST 2084", + "T-Log", + "V-Log", + "ViperLog", + "sRGB", + ], + str, + ] = "sRGB", + **kwargs: Any +) -> Union[FloatingOrNDArray, IntegerOrNDArray]: """ - Encodes linear :math:`RGB` values to non linear :math:`R'G'B'` values using + Encode linear :math:`RGB` values to non-linear :math:`R'G'B'` values using given encoding colour component transfer function (Encoding CCTF). Parameters ---------- - value : numeric or array_like + value Linear :math:`RGB` values. - function : unicode, optional + function {:attr:`colour.CCTF_ENCODINGS`}, - Computation function. + Encoding colour component transfer function. Other Parameters ---------------- - \\**kwargs : dict, optional - Keywords arguments for the relevant encoding CCTF of the + kwargs + Keywords arguments for the relevant encoding *CCTF* of the :attr:`colour.CCTF_ENCODINGS` attribute collection. Warnings -------- For *ITU-R BT.2100*, only the inverse electro-optical transfer functions - (EOTFs / EOCFs) are exposed by this definition, please refer to the + (EOTFs / EOCFs) are exposed by this definition, See the :func:`colour.oetf` definition for the opto-electronic transfer functions - (OETF / OECF). + (OETF). Returns ------- - numeric or ndarray - Non linear :math:`R'G'B'` values. + :class:`numpy.floating` or :class:`numpy.ndarray` + Non-linear :math:`R'G'B'` values. Examples -------- @@ -800,26 +1084,35 @@ def cctf_encoding(value, function='sRGB', **kwargs): 0.4090077... """ - if 'itu-r bt.2100' in function.lower(): + function = validate_method( + function, + CCTF_ENCODINGS, + '"{0}" encoding "CCTF" is invalid, it must be one of {1}!', + ) + + if "itu-r bt.2100" in function: usage_warning( 'With the "ITU-R BT.2100" method, only the inverse ' - 'electro-optical transfer functions (EOTFs / EOCFs) are exposed ' - 'by this definition, please refer to the "colour.oetf" definition ' - 'for the opto-electronic transfer functions (OETF / OECF).') + "electro-optical transfer functions (EOTFs / EOCFs) are exposed " + 'by this definition, See the "colour.oetf" definition ' + "for the opto-electronic transfer functions (OETF)." + ) - function = CCTF_ENCODINGS[function] + callable_ = CCTF_ENCODINGS[function] - return function(value, **filter_kwargs(function, **kwargs)) + return callable_(value, **filter_kwargs(callable_, **kwargs)) -CCTF_DECODINGS = CaseInsensitiveMapping({ - 'Gamma 2.2': partial(gamma_function, exponent=2.2), - 'Gamma 2.4': partial(gamma_function, exponent=2.4), - 'Gamma 2.6': partial(gamma_function, exponent=2.6), - 'ProPhoto RGB': cctf_decoding_ProPhotoRGB, - 'RIMM RGB': cctf_decoding_RIMMRGB, - 'ROMM RGB': cctf_decoding_ROMMRGB, -}) +CCTF_DECODINGS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Gamma 2.2": partial(gamma_function, exponent=2.2), + "Gamma 2.4": partial(gamma_function, exponent=2.4), + "Gamma 2.6": partial(gamma_function, exponent=2.6), + "ProPhoto RGB": cctf_decoding_ProPhotoRGB, + "RIMM RGB": cctf_decoding_RIMMRGB, + "ROMM RGB": cctf_decoding_ROMMRGB, + } +) CCTF_DECODINGS.update(LOG_DECODINGS) CCTF_DECODINGS.update(OETF_INVERSES) CCTF_DECODINGS.update(EOTFS) @@ -835,50 +1128,101 @@ def cctf_encoding(value, function='sRGB', **kwargs): Warnings -------- For *ITU-R BT.2100*, only the electro-optical transfer functions -(EOTFs / EOCFs) are exposed by this attribute, please refer to the +(EOTFs / EOCFs) are exposed by this attribute, See the :attr:`colour.OETF_INVERSES` attribute for the inverse opto-electronic -transfer functions (OETF / OECF). +transfer functions (OETF). Notes ----- - The order by which this attribute is defined and updated is critically important to ensure that *ITU-R BT.2100* definitions are reciprocal. - -CCTF_DECODINGS : CaseInsensitiveMapping - {:attr:`colour.LOG_DECODINGS`, :attr:`colour.EOTFS`, - :attr:`colour.OETF_INVERSES`} """ -def cctf_decoding(value, function='sRGB', **kwargs): +def cctf_decoding( + value: Union[FloatingOrArrayLike, IntegerOrArrayLike], + function: Union[ + Literal[ + "ACEScc", + "ACEScct", + "ACESproxy", + "ALEXA Log C", + "ARIB STD-B67", + "Blackmagic Film Generation 5", + "Canon Log 2", + "Canon Log 3", + "Canon Log", + "Cineon", + "D-Log", + "DCDM", + "DICOM GSDF", + "DaVinci Intermediate", + "ERIMM RGB", + "F-Log", + "Filmic Pro 6", + "Gamma 2.2", + "Gamma 2.4", + "Gamma 2.6", + "ITU-R BT.1886", + "ITU-R BT.2020", + "ITU-R BT.2100 HLG", + "ITU-R BT.2100 PQ", + "ITU-R BT.601", + "ITU-R BT.709", + "Log2", + "Log3G10", + "Log3G12", + "N-Log", + "PLog", + "Panalog", + "ProPhoto RGB", + "Protune", + "REDLog", + "REDLogFilm", + "RIMM RGB", + "ROMM RGB", + "S-Log", + "S-Log2", + "S-Log3", + "SMPTE 240M", + "ST 2084", + "T-Log", + "V-Log", + "ViperLog", + "sRGB", + ], + str, + ] = "sRGB", + **kwargs: Any +) -> FloatingOrNDArray: """ - Decodes non-linear :math:`R'G'B'` values to linear :math:`RGB` values using + Decode non-linear :math:`R'G'B'` values to linear :math:`RGB` values using given decoding colour component transfer function (Decoding CCTF). Parameters ---------- - value : numeric or array_like + value Non-linear :math:`R'G'B'` values. - function : unicode, optional + function {:attr:`colour.CCTF_DECODINGS`}, - Computation function. + Decoding colour component transfer function. Other Parameters ---------------- - \\**kwargs : dict, optional - Keywords arguments for the relevant decoding CCTF of the + kwargs + Keywords arguments for the relevant decoding *CCTF* of the :attr:`colour.CCTF_DECODINGS` attribute collection. Warnings -------- For *ITU-R BT.2100*, only the electro-optical transfer functions - (EOTFs / EOCFs) are exposed by this definition, please refer to the + (EOTFs / EOCFs) are exposed by this definition, See the :func:`colour.oetf_inverse` definition for the inverse opto-electronic - transfer functions (OETF / OECF). + transfer functions (OETF). Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear :math:`RGB` values. Examples @@ -894,50 +1238,73 @@ def cctf_decoding(value, function='sRGB', **kwargs): 0.1... """ - if 'itu-r bt.2100' in function.lower(): + function = validate_method( + function, + CCTF_DECODINGS, + '"{0}" decoding "CCTF" is invalid, it must be one of {1}!', + ) + + if "itu-r bt.2100" in function: usage_warning( 'With the "ITU-R BT.2100" method, only the electro-optical ' - 'transfer functions (EOTFs / EOCFs) are exposed by this ' - 'definition, please refer to the "colour.oetf_inverse" definition ' - 'for the inverse opto-electronic transfer functions (OETF / OECF).' + "transfer functions (EOTFs / EOCFs) are exposed by this " + 'definition, See the "colour.oetf_inverse" definition ' + "for the inverse opto-electronic transfer functions (OETF)." ) - function = CCTF_DECODINGS[function] + callable_ = CCTF_DECODINGS[function] - return function(value, **filter_kwargs(function, **kwargs)) + return callable_(value, **filter_kwargs(callable_, **kwargs)) -__all__ += ['CCTF_ENCODINGS', 'CCTF_DECODINGS'] -__all__ += ['cctf_encoding', 'cctf_decoding'] +__all__ += [ + "CCTF_ENCODINGS", + "CCTF_DECODINGS", +] +__all__ += [ + "cctf_encoding", + "cctf_decoding", +] -OOTFS = CaseInsensitiveMapping({ - 'ITU-R BT.2100 HLG': ootf_HLG_BT2100, - 'ITU-R BT.2100 PQ': ootf_PQ_BT2100, -}) +OOTFS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "ITU-R BT.2100 HLG": ootf_HLG_BT2100, + "ITU-R BT.2100 PQ": ootf_PQ_BT2100, + } +) OOTFS.__doc__ = """ Supported opto-optical transfer functions (OOTFs / OOCFs). - -OOTFS : CaseInsensitiveMapping - **{'ITU-R BT.2100 HLG', 'ITU-R BT.2100 PQ'}** """ -def ootf(value, function='ITU-R BT.2100 PQ', **kwargs): +def ootf( + value: FloatingOrArrayLike, + function: Union[ + Literal["ITU-R BT.2100 HLG", "ITU-R BT.2100 PQ"], str + ] = "ITU-R BT.2100 PQ", + **kwargs: Any +) -> FloatingOrNDArray: """ - Maps relative scene linear light to display linear light using given + Map relative scene linear light to display linear light using given opto-optical transfer function (OOTF / OOCF). Parameters ---------- - value : numeric or array_like + value Value. - function : unicode, optional - **{'ITU-R BT.2100 HLG', 'ITU-R BT.2100 PQ'}** + function Opto-optical transfer function (OOTF / OOCF). + Other Parameters + ---------------- + kwargs + {:func:`colour.models.ootf_HLG_BT2100`, + :func:`colour.models.ootf_PQ_BT2100`}, + See the documentation of the previously listed definitions. + Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Luminance of a displayed linear component. Examples @@ -948,53 +1315,54 @@ def ootf(value, function='ITU-R BT.2100 PQ', **kwargs): 63.0957344... """ - function = OOTFS[function] + function = validate_method( + function, OOTFS, '"{0}" "OOTF" is invalid, it must be one of {1}!' + ) - return function(value, **filter_kwargs(function, **kwargs)) + callable_ = OOTFS[function] + return callable_(value, **filter_kwargs(callable_, **kwargs)) -OOTF_INVERSES = CaseInsensitiveMapping({ - 'ITU-R BT.2100 HLG': ootf_inverse_HLG_BT2100, - 'ITU-R BT.2100 PQ': ootf_inverse_PQ_BT2100, -}) + +OOTF_INVERSES: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "ITU-R BT.2100 HLG": ootf_inverse_HLG_BT2100, + "ITU-R BT.2100 PQ": ootf_inverse_PQ_BT2100, + } +) OOTF_INVERSES.__doc__ = """ Supported inverse opto-optical transfer functions (OOTFs / OOCFs). - -OOTF_INVERSES : CaseInsensitiveMapping - **{'ITU-R BT.2100 HLG', 'ITU-R BT.2100 PQ'}** """ -def ootf_inverse(value, function='ITU-R BT.2100 PQ', **kwargs): +def ootf_inverse( + value: FloatingOrArrayLike, + function: Union[ + Literal["ITU-R BT.2100 HLG", "ITU-R BT.2100 PQ"], str + ] = "ITU-R BT.2100 PQ", + **kwargs: Any +) -> FloatingOrNDArray: """ - Maps relative display linear light to scene linear light using given + Map relative display linear light to scene linear light using given inverse opto-optical transfer function (OOTF / OOCF). Parameters ---------- - value : numeric or array_like + value Value. - function : unicode, optional - **{'ITU-R BT.2100 HLG', 'ITU-R BT.2100 PQ'}** + function Inverse opto-optical transfer function (OOTF / OOCF). Other Parameters ---------------- - L_B : numeric, optional - {:func:`colour.models.ootf_inverse_HLG_BT2100`}, - :math:`L_B` is the display luminance for black in :math:`cd/m^2`. - L_W : numeric, optional - {:func:`colour.models.ootf_inverse_HLG_BT2100`}, - :math:`L_W` is nominal peak luminance of the display in :math:`cd/m^2` - for achromatic pixels. - gamma : numeric, optional - {:func:`colour.models.ootf_inverse_HLG_BT2100`}, - System gamma value, 1.2 at the nominal display peak luminance of - :math:`1000 cd/m^2`. + kwargs + {:func:`colour.models.ootf_inverse_HLG_BT2100`, + :func:`colour.models.ootf_inverse_PQ_BT2100`}, + See the documentation of the previously listed definitions. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Luminance of scene linear light. Examples @@ -1006,10 +1374,22 @@ def ootf_inverse(value, function='ITU-R BT.2100 PQ', **kwargs): 0.1000000... """ - function = OOTF_INVERSES[function] + function = validate_method( + function, + OOTF_INVERSES, + '"{0}" inverse "OOTF" is invalid, it must be one of {1}!', + ) - return function(value, **filter_kwargs(function, **kwargs)) + callable_ = OOTF_INVERSES[function] + return callable_(value, **filter_kwargs(callable_, **kwargs)) -__all__ += ['OOTFS', 'OOTF_INVERSES'] -__all__ += ['ootf', 'ootf_inverse'] + +__all__ += [ + "OOTFS", + "OOTF_INVERSES", +] +__all__ += [ + "ootf", + "ootf_inverse", +] diff --git a/colour/models/rgb/transfer_functions/aces.py b/colour/models/rgb/transfer_functions/aces.py index dc8d9dbc99..99ece2d7f0 100644 --- a/colour/models/rgb/transfer_functions/aces.py +++ b/colour/models/rgb/transfer_functions/aces.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Academy Color Encoding System - Log Encodings ============================================= @@ -48,104 +47,112 @@ http://www.oscars.org/science-technology/council/projects/aces.html """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.utilities import (Structure, as_float, as_int, from_range_1, - to_domain_1) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import ( + Boolean, + Dict, + Floating, + FloatingOrArrayLike, + FloatingOrNDArray, + IntegerOrArrayLike, + IntegerOrNDArray, + Literal, + Union, +) +from colour.utilities import ( + Structure, + as_float, + as_int, + from_range_1, + to_domain_1, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'CONSTANTS_ACES_PROXY_10', 'CONSTANTS_ACES_PROXY_12', - 'CONSTANTS_ACES_PROXY', 'CONSTANTS_ACES_CCT', 'log_encoding_ACESproxy', - 'log_decoding_ACESproxy', 'log_encoding_ACEScc', 'log_decoding_ACEScc', - 'log_encoding_ACEScct', 'log_decoding_ACEScct' + "CONSTANTS_ACES_PROXY_10", + "CONSTANTS_ACES_PROXY_12", + "CONSTANTS_ACES_PROXY", + "CONSTANTS_ACES_CCT", + "log_encoding_ACESproxy", + "log_decoding_ACESproxy", + "log_encoding_ACEScc", + "log_decoding_ACEScc", + "log_encoding_ACEScct", + "log_decoding_ACEScct", ] -CONSTANTS_ACES_PROXY_10 = Structure( +CONSTANTS_ACES_PROXY_10: Structure = Structure( CV_min=64, CV_max=940, steps_per_stop=50, mid_CV_offset=425, - mid_log_offset=2.5) -""" -*ACESproxy* 10 bit colourspace constants. - -CONSTANTS_ACES_PROXY_10 : Structure -""" + mid_log_offset=2.5, +) +"""*ACESproxy* 10 bit colourspace constants.""" -CONSTANTS_ACES_PROXY_12 = Structure( +CONSTANTS_ACES_PROXY_12: Structure = Structure( CV_min=256, CV_max=3760, steps_per_stop=200, mid_CV_offset=1700, - mid_log_offset=2.5) -""" -*ACESproxy* 12 bit colourspace constants. + mid_log_offset=2.5, +) +"""*ACESproxy* 12 bit colourspace constants.""" -CONSTANTS_ACES_PROXY_12 : Structure -""" - -CONSTANTS_ACES_PROXY = { +CONSTANTS_ACES_PROXY: Dict = { 10: CONSTANTS_ACES_PROXY_10, - 12: CONSTANTS_ACES_PROXY_12 + 12: CONSTANTS_ACES_PROXY_12, } -""" -Aggregated *ACESproxy* colourspace constants. +"""Aggregated *ACESproxy* colourspace constants.""" -CONSTANTS_ACES_PROXY : dict - **{10, 12}** -""" - -CONSTANTS_ACES_CCT = Structure( +CONSTANTS_ACES_CCT: Structure = Structure( X_BRK=0.0078125, Y_BRK=0.155251141552511, A=10.5402377416545, - B=0.0729055341958355) -""" -*ACEScct* colourspace constants. - -CONSTANTS_ACES_CCT : Structure -""" + B=0.0729055341958355, +) +"""*ACEScct* colourspace constants.""" # pylint: disable=W0102 -def log_encoding_ACESproxy(lin_AP1, - bit_depth=10, - out_int=False, - constants=CONSTANTS_ACES_PROXY): +def log_encoding_ACESproxy( + lin_AP1: FloatingOrArrayLike, + bit_depth: Literal[10, 12] = 10, + out_int: Boolean = False, + constants: Dict = CONSTANTS_ACES_PROXY, +) -> Union[FloatingOrNDArray, IntegerOrNDArray]: """ - Defines the *ACESproxy* colourspace log encoding curve / opto-electronic + Define the *ACESproxy* colourspace log encoding curve / opto-electronic transfer function. Parameters ---------- - lin_AP1 : numeric or array_like + lin_AP1 *lin_AP1* value. - bit_depth : int, optional - **{10, 12}**, + bit_depth *ACESproxy* bit depth. - out_int : bool, optional + out_in Whether to return value as integer code value or float equivalent of a code value at a given bit depth. - constants : Structure, optional + constants *ACESproxy* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.integer` or :class:`numpy.ndarray` *ACESproxy* non-linear value. Notes ----- - +----------------+-----------------------+---------------+ | **Domain \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ @@ -178,61 +185,62 @@ def log_encoding_ACESproxy(lin_AP1, lin_AP1 = to_domain_1(lin_AP1) - constants = constants[bit_depth] + CV_min = constants[bit_depth].CV_min + CV_max = constants[bit_depth].CV_max + mid_CV_offset = constants[bit_depth].mid_CV_offset + mid_log_offset = constants[bit_depth].mid_log_offset + steps_per_stop = constants[bit_depth].steps_per_stop - CV_min = constants.CV_min - CV_max = constants.CV_max - - def float_2_cv(x): - """ - Converts given numeric to code value. - """ + def float_2_cv(x: Floating) -> Floating: + """Convert given numeric to code value.""" return np.maximum(CV_min, np.minimum(CV_max, np.round(x))) ACESproxy = np.where( - lin_AP1 > 2 ** -9.72, - float_2_cv((np.log2(lin_AP1) + constants.mid_log_offset) * - constants.steps_per_stop + constants.mid_CV_offset), + lin_AP1 > 2**-9.72, + float_2_cv( + (np.log2(lin_AP1) + mid_log_offset) * steps_per_stop + + mid_CV_offset + ), np.resize(CV_min, lin_AP1.shape), ) if out_int: return as_int(np.round(ACESproxy)) else: - return as_float(from_range_1(ACESproxy / (2 ** bit_depth - 1))) + return as_float(from_range_1(ACESproxy / (2**bit_depth - 1))) # pylint: disable=W0102 -def log_decoding_ACESproxy(ACESproxy, - bit_depth=10, - in_int=False, - constants=CONSTANTS_ACES_PROXY): +def log_decoding_ACESproxy( + ACESproxy: Union[FloatingOrArrayLike, IntegerOrArrayLike], + bit_depth: Literal[10, 12] = 10, + in_int: Boolean = False, + constants: Dict = CONSTANTS_ACES_PROXY, +) -> FloatingOrNDArray: """ - Defines the *ACESproxy* colourspace log decoding curve / electro-optical + Define the *ACESproxy* colourspace log decoding curve / electro-optical transfer function. Parameters ---------- - ACESproxy : numeric or array_like + ACESproxy *ACESproxy* non-linear value. - bit_depth : int, optional - **{10, 12}**, + bit_depth *ACESproxy* bit depth. - in_int : bool, optional + in_int Whether to treat the input value as integer code value or float equivalent of a code value at a given bit depth. - constants : Structure, optional + constants *ACESproxy* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *lin_AP1* value. Notes ----- - +----------------+-----------------------+---------------+ | **Domain \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ @@ -265,36 +273,37 @@ def log_decoding_ACESproxy(ACESproxy, ACESproxy = to_domain_1(ACESproxy) - constants = constants[bit_depth] + mid_CV_offset = constants[bit_depth].mid_CV_offset + mid_log_offset = constants[bit_depth].mid_log_offset + steps_per_stop = constants[bit_depth].steps_per_stop if not in_int: - ACESproxy = ACESproxy * (2 ** bit_depth - 1) + ACESproxy = ACESproxy * (2**bit_depth - 1) - lin_AP1 = ( - 2 ** (((ACESproxy - constants.mid_CV_offset) / constants.steps_per_stop - - constants.mid_log_offset))) + lin_AP1 = 2 ** ( + (ACESproxy - mid_CV_offset) / steps_per_stop - mid_log_offset + ) - return from_range_1(lin_AP1) + return as_float(from_range_1(lin_AP1)) -def log_encoding_ACEScc(lin_AP1): +def log_encoding_ACEScc(lin_AP1: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the *ACEScc* colourspace log encoding / opto-electronic transfer + Define the *ACEScc* colourspace log encoding / opto-electronic transfer function. Parameters ---------- - lin_AP1 : numeric or array_like + lin_AP1 *lin_AP1* value. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *ACEScc* non-linear value. Notes ----- - +-------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +=============+=======================+===============+ @@ -324,11 +333,11 @@ def log_encoding_ACEScc(lin_AP1): ACEScc = np.where( lin_AP1 < 0, - (np.log2(2 ** -16) + 9.72) / 17.52, - (np.log2(2 ** -16 + lin_AP1 * 0.5) + 9.72) / 17.52, + (np.log2(2**-16) + 9.72) / 17.52, + (np.log2(2**-16 + lin_AP1 * 0.5) + 9.72) / 17.52, ) ACEScc = np.where( - lin_AP1 >= 2 ** -15, + lin_AP1 >= 2**-15, (np.log2(lin_AP1) + 9.72) / 17.52, ACEScc, ) @@ -336,24 +345,23 @@ def log_encoding_ACEScc(lin_AP1): return as_float(from_range_1(ACEScc)) -def log_decoding_ACEScc(ACEScc): +def log_decoding_ACEScc(ACEScc: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the *ACEScc* colourspace log decoding / electro-optical transfer + Define the *ACEScc* colourspace log decoding / electro-optical transfer function. Parameters ---------- - ACEScc : numeric or array_like + ACEScc *ACEScc* non-linear value. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *lin_AP1* value. Notes ----- - +-------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +=============+=======================+===============+ @@ -383,7 +391,7 @@ def log_decoding_ACEScc(ACEScc): lin_AP1 = np.where( ACEScc < (9.72 - 15) / 17.52, - (2 ** (ACEScc * 17.52 - 9.72) - 2 ** -16) * 2, + (2 ** (ACEScc * 17.52 - 9.72) - 2**-16) * 2, 2 ** (ACEScc * 17.52 - 9.72), ) lin_AP1 = np.where( @@ -396,26 +404,27 @@ def log_decoding_ACEScc(ACEScc): # pylint: disable=W0102 -def log_encoding_ACEScct(lin_AP1, constants=CONSTANTS_ACES_CCT): +def log_encoding_ACEScct( + lin_AP1: FloatingOrArrayLike, constants: Structure = CONSTANTS_ACES_CCT +) -> FloatingOrNDArray: """ - Defines the *ACEScct* colourspace log encoding / opto-electronic transfer + Define the *ACEScct* colourspace log encoding / opto-electronic transfer function. Parameters ---------- - lin_AP1 : numeric or array_like + lin_AP1 *lin_AP1* value. - constants : Structure, optional + constants *ACEScct* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *ACEScct* non-linear value. Notes ----- - +-------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +=============+=======================+===============+ @@ -453,21 +462,23 @@ def log_encoding_ACEScct(lin_AP1, constants=CONSTANTS_ACES_CCT): # pylint: disable=W0102 -def log_decoding_ACEScct(ACEScct, constants=CONSTANTS_ACES_CCT): +def log_decoding_ACEScct( + ACEScct: FloatingOrArrayLike, constants: Structure = CONSTANTS_ACES_CCT +) -> FloatingOrNDArray: """ - Defines the *ACEScct* colourspace log decoding / electro-optical transfer + Define the *ACEScct* colourspace log decoding / electro-optical transfer function. Parameters ---------- - ACEScct : numeric or array_like + ACEScct *ACEScct* non-linear value. - constants : Structure, optional + constants *ACEScct* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *lin_AP1* value. References @@ -479,7 +490,6 @@ def log_decoding_ACEScct(ACEScct, constants=CONSTANTS_ACES_CCT): Notes ----- - +-------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +=============+=======================+===============+ diff --git a/colour/models/rgb/transfer_functions/arib_std_b67.py b/colour/models/rgb/transfer_functions/arib_std_b67.py index 408b2f279d..d2355df021 100644 --- a/colour/models/rgb/transfer_functions/arib_std_b67.py +++ b/colour/models/rgb/transfer_functions/arib_std_b67.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ ARIB STD-B67 (Hybrid Log-Gamma) =============================== -Defines *ARIB STD-B67 (Hybrid Log-Gamma)* opto-electrical transfer function -(OETF / OECF) and its inverse: +Defines the *ARIB STD-B67 (Hybrid Log-Gamma)* opto-electrical transfer function +(OETF) and its inverse: - :func:`colour.models.oetf_ARIBSTDB67` - :func:`colour.models.oetf_inverse_ARIBSTDB67` @@ -18,57 +17,67 @@ https://www.arib.or.jp/english/std_tr/broadcasting/desc/std-b67.html """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +from colour.hints import FloatingOrArrayLike, FloatingOrNDArray from colour.models.rgb.transfer_functions import gamma_function -from colour.utilities import (Structure, as_float, domain_range_scale, - from_range_1, to_domain_1) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.utilities import ( + Structure, + as_float, + as_float_array, + domain_range_scale, + from_range_1, + to_domain_1, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'CONSTANTS_ARIBSTDB67', 'oetf_ARIBSTDB67', 'oetf_inverse_ARIBSTDB67' + "CONSTANTS_ARIBSTDB67", + "oetf_ARIBSTDB67", + "oetf_inverse_ARIBSTDB67", ] -CONSTANTS_ARIBSTDB67 = Structure(a=0.17883277, b=0.28466892, c=0.55991073) -""" -*ARIB STD-B67 (Hybrid Log-Gamma)* constants. - -CONSTANTS_ARIBSTDB67 : Structure -""" +CONSTANTS_ARIBSTDB67: Structure = Structure( + a=0.17883277, b=0.28466892, c=0.55991073 +) +"""*ARIB STD-B67 (Hybrid Log-Gamma)* constants.""" -def oetf_ARIBSTDB67(E, r=0.5, constants=CONSTANTS_ARIBSTDB67): +def oetf_ARIBSTDB67( + E: FloatingOrArrayLike, + r: FloatingOrArrayLike = 0.5, + constants: Structure = CONSTANTS_ARIBSTDB67, +) -> FloatingOrNDArray: """ - Defines *ARIB STD-B67 (Hybrid Log-Gamma)* opto-electrical transfer - function (OETF / OECF). + Define *ARIB STD-B67 (Hybrid Log-Gamma)* opto-electrical transfer + function (OETF). Parameters ---------- - E : numeric or array_like + E Voltage normalised by the reference white level and proportional to the implicit light intensity that would be detected with a reference camera color channel R, G, B. - r : numeric, optional + r Video level corresponding to reference white level. - constants : Structure, optional + constants *ARIB STD-B67 (Hybrid Log-Gamma)* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Resulting non-linear signal :math:`E'`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -96,41 +105,46 @@ def oetf_ARIBSTDB67(E, r=0.5, constants=CONSTANTS_ARIBSTDB67): """ E = to_domain_1(E) + r = as_float_array(r) a = constants.a b = constants.b c = constants.c - E_p = np.where(E <= 1, r * gamma_function(E, 0.5, 'mirror'), - a * np.log(E - b) + c) + E_p = np.where( + E <= 1, r * gamma_function(E, 0.5, "mirror"), a * np.log(E - b) + c + ) return as_float(from_range_1(E_p)) -def oetf_inverse_ARIBSTDB67(E_p, r=0.5, constants=CONSTANTS_ARIBSTDB67): +def oetf_inverse_ARIBSTDB67( + E_p: FloatingOrArrayLike, + r: FloatingOrArrayLike = 0.5, + constants: Structure = CONSTANTS_ARIBSTDB67, +) -> FloatingOrNDArray: """ - Defines *ARIB STD-B67 (Hybrid Log-Gamma)* inverse opto-electrical transfer - function (OETF / OECF). + Define *ARIB STD-B67 (Hybrid Log-Gamma)* inverse opto-electrical transfer + function (OETF). Parameters ---------- - E_p : numeric or array_like + E_p Non-linear signal :math:`E'`. - r : numeric, optional + r Video level corresponding to reference white level. - constants : Structure, optional + constants *ARIB STD-B67 (Hybrid Log-Gamma)* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Voltage :math:`E` normalised by the reference white level and proportional to the implicit light intensity that would be detected with a reference camera color channel R, G, B. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -163,10 +177,10 @@ def oetf_inverse_ARIBSTDB67(E_p, r=0.5, constants=CONSTANTS_ARIBSTDB67): b = constants.b c = constants.c - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): E = np.where( E_p <= oetf_ARIBSTDB67(1), - gamma_function((E_p / r), 2, 'mirror'), + gamma_function((E_p / r), 2, "mirror"), np.exp((E_p - c) / a) + b, ) diff --git a/colour/models/rgb/transfer_functions/arri_alexa_log_c.py b/colour/models/rgb/transfer_functions/arri_alexa_log_c.py index 2454b5d1f8..f02eb4e429 100644 --- a/colour/models/rgb/transfer_functions/arri_alexa_log_c.py +++ b/colour/models/rgb/transfer_functions/arri_alexa_log_c.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ ARRI ALEXA Log C Log Encoding ============================= @@ -14,198 +13,566 @@ https://drive.google.com/open?id=1t73fAG_QpV7hJxoQPYZDWvOojYkYDgvn """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.utilities import (CaseInsensitiveMapping, as_float, from_range_1, - to_domain_1) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import FloatingOrArrayLike, FloatingOrNDArray, Literal, Union +from colour.utilities import ( + CaseInsensitiveMapping, + as_float, + from_range_1, + to_domain_1, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'DATA_ALEXA_LOG_C_CURVE_BCL', 'DATA_ALEXA_LOG_C_CURVE_CONVERSION', - 'log_encoding_ALEXALogC', 'log_decoding_ALEXALogC' + "DATA_ALEXA_LOG_C_CURVE_BCL", + "DATA_ALEXA_LOG_C_CURVE_CONVERSION", + "log_encoding_ALEXALogC", + "log_decoding_ALEXALogC", ] -DATA_ALEXA_LOG_C_CURVE_BCL = CaseInsensitiveMapping({ - 'SUP 3.x': { - 160: (0.0928, 0.8128), - 200: (0.0928, 0.8341), - 250: (0.0928, 0.8549), - 320: (0.0928, 0.8773), - 400: (0.0928, 0.8968), - 500: (0.0928, 0.9158), - 640: (0.0928, 0.9362), - 800: (0.0928, 0.9539), - 1000: (0.0928, 0.9711), - 1280: (0.0928, 0.9895), - 1600: (0.0928, 1.0000), - 2000: (0.0928, 1.0000), - 2560: (0.0928, 1.0000), - 3200: (0.0928, 1.0000) - }, - 'SUP 2.x': { - 160: (0.1083, 0.8110), - 200: (0.1115, 0.8320), - 250: (0.1146, 0.8524), - 320: (0.1181, 0.8743), - 400: (0.1213, 0.8935), - 500: (0.1245, 0.9121), - 640: (0.1280, 0.9320), - 800: (0.1311, 0.9494), - 1000: (0.1343, 0.9662), - 1280: (0.1378, 0.9841), - 1600: (0.1409, 0.9997) - } -}) -""" -*ARRI ALEXA Log C* curve *Ei, Black, Clipping Level* data. - -DATA_ALEXA_LOG_C_CURVE_BCL : CaseInsensitiveMapping - **{'SUP 3.x', 'SUP 2.x'}** -""" - -DATA_ALEXA_LOG_C_CURVE_CONVERSION = CaseInsensitiveMapping({ - 'SUP 3.x': CaseInsensitiveMapping({ - 'Normalised Sensor Signal': { - 160: (0.004680, 40.0, -0.076072, 0.269036, - 0.381991, 42.062665, -0.071569, 0.125266), - 200: (0.004597, 50.0, -0.118740, 0.266007, - 0.382478, 51.986387, -0.110339, 0.128643), - 250: (0.004518, 62.5, -0.171260, 0.262978, - 0.382966, 64.243053, -0.158224, 0.132021), - 320: (0.004436, 80.0, -0.243808, 0.259627, - 0.383508, 81.183335, -0.224409, 0.135761), - 400: (0.004369, 100.0, -0.325820, 0.256598, - 0.383999, 100.295280, -0.299079, 0.139142), - 500: (0.004309, 125.0, -0.427461, 0.253569, - 0.384493, 123.889239, -0.391261, 0.142526), - 640: (0.004249, 160.0, -0.568709, 0.250219, - 0.385040, 156.482680, -0.518605, 0.146271), - 800: (0.004201, 200.0, -0.729169, 0.247190, - 0.385537, 193.235573, -0.662201, 0.149658), - 1000: (0.004160, 250.0, -0.928805, 0.244161, - 0.386036, 238.584745, -0.839385, 0.153047), - 1280: (0.004120, 320.0, -1.207168, 0.240810, - 0.386590, 301.197380, -1.084020, 0.156799), - 1600: (0.004088, 400.0, -1.524256, 0.237781, - 0.387093, 371.761171, -1.359723, 0.160192) +DATA_ALEXA_LOG_C_CURVE_BCL: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "SUP 3.x": { + 160: (0.0928, 0.8128), + 200: (0.0928, 0.8341), + 250: (0.0928, 0.8549), + 320: (0.0928, 0.8773), + 400: (0.0928, 0.8968), + 500: (0.0928, 0.9158), + 640: (0.0928, 0.9362), + 800: (0.0928, 0.9539), + 1000: (0.0928, 0.9711), + 1280: (0.0928, 0.9895), + 1600: (0.0928, 1.0000), + 2000: (0.0928, 1.0000), + 2560: (0.0928, 1.0000), + 3200: (0.0928, 1.0000), }, - 'Linear Scene Exposure Factor': { - 160: (0.005561, 5.555556, 0.080216, 0.269036, - 0.381991, 5.842037, 0.092778, 0.125266), - 200: (0.006208, 5.555556, 0.076621, 0.266007, - 0.382478, 5.776265, 0.092782, 0.128643), - 250: (0.006871, 5.555556, 0.072941, 0.262978, - 0.382966, 5.710494, 0.092786, 0.132021), - 320: (0.007622, 5.555556, 0.068768, 0.259627, - 0.383508, 5.637732, 0.092791, 0.135761), - 400: (0.008318, 5.555556, 0.064901, 0.256598, - 0.383999, 5.571960, 0.092795, 0.139142), - 500: (0.009031, 5.555556, 0.060939, 0.253569, - 0.384493, 5.506188, 0.092800, 0.142526), - 640: (0.009840, 5.555556, 0.056443, 0.250219, - 0.385040, 5.433426, 0.092805, 0.146271), - 800: (0.010591, 5.555556, 0.052272, 0.247190, - 0.385537, 5.367655, 0.092809, 0.149658), - 1000: (0.011361, 5.555556, 0.047996, 0.244161, - 0.386036, 5.301883, 0.092814, 0.153047), - 1280: (0.012235, 5.555556, 0.043137, 0.240810, - 0.386590, 5.229121, 0.092819, 0.156799), - 1600: (0.013047, 5.555556, 0.038625, 0.237781, - 0.387093, 5.163350, 0.092824, 0.16019) - } - }), - 'SUP 2.x': CaseInsensitiveMapping({ - 'Normalised Sensor Signal': { - 160: (0.003907, 36.439829, -0.053366, 0.269035, - 0.391007, 45.593473, -0.069772, 0.10836), - 200: (0.003907, 45.549786, -0.088959, 0.266007, - 0.391007, 55.709581, -0.106114, 0.11154), - 250: (0.003907, 56.937232, -0.133449, 0.262978, - 0.391007, 67.887153, -0.150510, 0.11472), - 320: (0.003907, 72.879657, -0.195737, 0.259627, - 0.391007, 84.167616, -0.210597, 0.11824), - 400: (0.003907, 91.099572, -0.266922, 0.256598, - 0.391007, 101.811426, -0.276349, 0.12142), - 500: (0.003907, 113.874465, -0.355903, 0.253569, - 0.391007, 122.608379, -0.354421, 0.12461), - 640: (0.003907, 145.759315, -0.480477, 0.250218, - 0.391007, 149.703304, -0.456760, 0.12813), - 800: (0.003907, 182.199144, -0.622848, 0.247189, - 0.391007, 178.216873, -0.564981, 0.13131), - 1000: (0.003907, 227.748930, -0.800811, 0.244161, - 0.391007, 210.785040, -0.689043, 0.13449), - 1280: (0.003907, 291.518630, -1.049959, 0.240810, - 0.391007, 251.689459, -0.845336, 0.13801), - 1600: (0.003907, 364.398287, -1.334700, 0.237781, - 0.391007, 293.073575, -1.003841, 0.14119) + "SUP 2.x": { + 160: (0.1083, 0.8110), + 200: (0.1115, 0.8320), + 250: (0.1146, 0.8524), + 320: (0.1181, 0.8743), + 400: (0.1213, 0.8935), + 500: (0.1245, 0.9121), + 640: (0.1280, 0.9320), + 800: (0.1311, 0.9494), + 1000: (0.1343, 0.9662), + 1280: (0.1378, 0.9841), + 1600: (0.1409, 0.9997), }, - 'Linear Scene Exposure Factor': { - 160: (0.000000, 5.061087, 0.089004, 0.269035, - 0.391007, 6.332427, 0.108361, 0.108361), - 200: (0.000000, 5.061087, 0.089004, 0.266007, - 0.391007, 6.189953, 0.111543, 0.111543), - 250: (0.000000, 5.061087, 0.089004, 0.262978, - 0.391007, 6.034414, 0.114725, 0.114725), - 320: (0.000000, 5.061087, 0.089004, 0.259627, - 0.391007, 5.844973, 0.118246, 0.118246), - 400: (0.000000, 5.061087, 0.089004, 0.256598, - 0.391007, 5.656190, 0.121428, 0.121428), - 500: (0.000000, 5.061087, 0.089004, 0.253569, - 0.391007, 5.449261, 0.124610, 0.124610), - 640: (0.000000, 5.061087, 0.089004, 0.250218, - 0.391007, 5.198031, 0.128130, 0.128130), - 800: (0.000000, 5.061087, 0.089004, 0.247189, - 0.391007, 4.950469, 0.131313, 0.131313), - 1000: (0.000000, 5.061087, 0.089004, 0.244161, - 0.391007, 4.684112, 0.134495, 0.134495), - 1280: (0.000000, 5.061087, 0.089004, 0.240810, - 0.391007, 4.369609, 0.138015, 0.138015), - 1600: (0.000000, 5.061087, 0.089004, 0.237781, - 0.391007, 4.070466, 0.141197, 0.14119)} - }) -}) # yapf: disable + } +) +"""*ARRI ALEXA Log C* curve *Ei, Black, Clipping Level* data.""" + +DATA_ALEXA_LOG_C_CURVE_CONVERSION: CaseInsensitiveMapping = ( + CaseInsensitiveMapping( + { + "SUP 3.x": CaseInsensitiveMapping( + { + "Normalised Sensor Signal": { + 160: ( + 0.004680, + 40.0, + -0.076072, + 0.269036, + 0.381991, + 42.062665, + -0.071569, + 0.125266, + ), + 200: ( + 0.004597, + 50.0, + -0.118740, + 0.266007, + 0.382478, + 51.986387, + -0.110339, + 0.128643, + ), + 250: ( + 0.004518, + 62.5, + -0.171260, + 0.262978, + 0.382966, + 64.243053, + -0.158224, + 0.132021, + ), + 320: ( + 0.004436, + 80.0, + -0.243808, + 0.259627, + 0.383508, + 81.183335, + -0.224409, + 0.135761, + ), + 400: ( + 0.004369, + 100.0, + -0.325820, + 0.256598, + 0.383999, + 100.295280, + -0.299079, + 0.139142, + ), + 500: ( + 0.004309, + 125.0, + -0.427461, + 0.253569, + 0.384493, + 123.889239, + -0.391261, + 0.142526, + ), + 640: ( + 0.004249, + 160.0, + -0.568709, + 0.250219, + 0.385040, + 156.482680, + -0.518605, + 0.146271, + ), + 800: ( + 0.004201, + 200.0, + -0.729169, + 0.247190, + 0.385537, + 193.235573, + -0.662201, + 0.149658, + ), + 1000: ( + 0.004160, + 250.0, + -0.928805, + 0.244161, + 0.386036, + 238.584745, + -0.839385, + 0.153047, + ), + 1280: ( + 0.004120, + 320.0, + -1.207168, + 0.240810, + 0.386590, + 301.197380, + -1.084020, + 0.156799, + ), + 1600: ( + 0.004088, + 400.0, + -1.524256, + 0.237781, + 0.387093, + 371.761171, + -1.359723, + 0.160192, + ), + }, + "Linear Scene Exposure Factor": { + 160: ( + 0.005561, + 5.555556, + 0.080216, + 0.269036, + 0.381991, + 5.842037, + 0.092778, + 0.125266, + ), + 200: ( + 0.006208, + 5.555556, + 0.076621, + 0.266007, + 0.382478, + 5.776265, + 0.092782, + 0.128643, + ), + 250: ( + 0.006871, + 5.555556, + 0.072941, + 0.262978, + 0.382966, + 5.710494, + 0.092786, + 0.132021, + ), + 320: ( + 0.007622, + 5.555556, + 0.068768, + 0.259627, + 0.383508, + 5.637732, + 0.092791, + 0.135761, + ), + 400: ( + 0.008318, + 5.555556, + 0.064901, + 0.256598, + 0.383999, + 5.571960, + 0.092795, + 0.139142, + ), + 500: ( + 0.009031, + 5.555556, + 0.060939, + 0.253569, + 0.384493, + 5.506188, + 0.092800, + 0.142526, + ), + 640: ( + 0.009840, + 5.555556, + 0.056443, + 0.250219, + 0.385040, + 5.433426, + 0.092805, + 0.146271, + ), + 800: ( + 0.010591, + 5.555556, + 0.052272, + 0.247190, + 0.385537, + 5.367655, + 0.092809, + 0.149658, + ), + 1000: ( + 0.011361, + 5.555556, + 0.047996, + 0.244161, + 0.386036, + 5.301883, + 0.092814, + 0.153047, + ), + 1280: ( + 0.012235, + 5.555556, + 0.043137, + 0.240810, + 0.386590, + 5.229121, + 0.092819, + 0.156799, + ), + 1600: ( + 0.013047, + 5.555556, + 0.038625, + 0.237781, + 0.387093, + 5.163350, + 0.092824, + 0.16019, + ), + }, + } + ), + "SUP 2.x": CaseInsensitiveMapping( + { + "Normalised Sensor Signal": { + 160: ( + 0.003907, + 36.439829, + -0.053366, + 0.269035, + 0.391007, + 45.593473, + -0.069772, + 0.10836, + ), + 200: ( + 0.003907, + 45.549786, + -0.088959, + 0.266007, + 0.391007, + 55.709581, + -0.106114, + 0.11154, + ), + 250: ( + 0.003907, + 56.937232, + -0.133449, + 0.262978, + 0.391007, + 67.887153, + -0.150510, + 0.11472, + ), + 320: ( + 0.003907, + 72.879657, + -0.195737, + 0.259627, + 0.391007, + 84.167616, + -0.210597, + 0.11824, + ), + 400: ( + 0.003907, + 91.099572, + -0.266922, + 0.256598, + 0.391007, + 101.811426, + -0.276349, + 0.12142, + ), + 500: ( + 0.003907, + 113.874465, + -0.355903, + 0.253569, + 0.391007, + 122.608379, + -0.354421, + 0.12461, + ), + 640: ( + 0.003907, + 145.759315, + -0.480477, + 0.250218, + 0.391007, + 149.703304, + -0.456760, + 0.12813, + ), + 800: ( + 0.003907, + 182.199144, + -0.622848, + 0.247189, + 0.391007, + 178.216873, + -0.564981, + 0.13131, + ), + 1000: ( + 0.003907, + 227.748930, + -0.800811, + 0.244161, + 0.391007, + 210.785040, + -0.689043, + 0.13449, + ), + 1280: ( + 0.003907, + 291.518630, + -1.049959, + 0.240810, + 0.391007, + 251.689459, + -0.845336, + 0.13801, + ), + 1600: ( + 0.003907, + 364.398287, + -1.334700, + 0.237781, + 0.391007, + 293.073575, + -1.003841, + 0.14119, + ), + }, + "Linear Scene Exposure Factor": { + 160: ( + 0.000000, + 5.061087, + 0.089004, + 0.269035, + 0.391007, + 6.332427, + 0.108361, + 0.108361, + ), + 200: ( + 0.000000, + 5.061087, + 0.089004, + 0.266007, + 0.391007, + 6.189953, + 0.111543, + 0.111543, + ), + 250: ( + 0.000000, + 5.061087, + 0.089004, + 0.262978, + 0.391007, + 6.034414, + 0.114725, + 0.114725, + ), + 320: ( + 0.000000, + 5.061087, + 0.089004, + 0.259627, + 0.391007, + 5.844973, + 0.118246, + 0.118246, + ), + 400: ( + 0.000000, + 5.061087, + 0.089004, + 0.256598, + 0.391007, + 5.656190, + 0.121428, + 0.121428, + ), + 500: ( + 0.000000, + 5.061087, + 0.089004, + 0.253569, + 0.391007, + 5.449261, + 0.124610, + 0.124610, + ), + 640: ( + 0.000000, + 5.061087, + 0.089004, + 0.250218, + 0.391007, + 5.198031, + 0.128130, + 0.128130, + ), + 800: ( + 0.000000, + 5.061087, + 0.089004, + 0.247189, + 0.391007, + 4.950469, + 0.131313, + 0.131313, + ), + 1000: ( + 0.000000, + 5.061087, + 0.089004, + 0.244161, + 0.391007, + 4.684112, + 0.134495, + 0.134495, + ), + 1280: ( + 0.000000, + 5.061087, + 0.089004, + 0.240810, + 0.391007, + 4.369609, + 0.138015, + 0.138015, + ), + 1600: ( + 0.000000, + 5.061087, + 0.089004, + 0.237781, + 0.391007, + 4.070466, + 0.141197, + 0.14119, + ), + }, + } + ), + } + ) +) """ *ARRI ALEXA Log C* curve conversion data between signal and linear scene exposure factor for *SUP 3.x* and signal and normalised sensor signal for *SUP 2.x*. - -DATA_ALEXA_LOG_C_CURVE_CONVERSION : CaseInsensitiveMapping - **{'SUP 3.x', 'SUP 2.x'}** """ -def log_encoding_ALEXALogC(x, - firmware='SUP 3.x', - method='Linear Scene Exposure Factor', - EI=800): +def log_encoding_ALEXALogC( + x: FloatingOrArrayLike, + firmware: Union[Literal["SUP 2.x", "SUP 3.x"], str] = "SUP 3.x", + method: Union[ + Literal["Linear Scene Exposure Factor", "Normalised Sensor Signal"], + str, + ] = "Linear Scene Exposure Factor", + EI: Literal[ + 160, 200, 250, 320, 400, 500, 640, 800, 1000, 1280, 1600 + ] = 800, +) -> FloatingOrNDArray: """ - Defines the *ARRI ALEXA Log C* log encoding curve / opto-electronic + Define the *ARRI ALEXA Log C* log encoding curve / opto-electronic transfer function. Parameters ---------- - x : numeric or array_like + x Linear data :math:`x`. - firmware : unicode, optional - **{'SUP 3.x', 'SUP 2.x'}**, + firmware Alexa firmware version. - method : unicode, optional - **{'Linear Scene Exposure Factor', 'Normalised Sensor Signal'}**, + method Conversion method. - EI : int, optional - Ei. + EI + Exposure Index :math:`EI`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *ARRI ALEXA Log C* encoded data :math:`t`. References @@ -214,7 +581,6 @@ def log_encoding_ALEXALogC(x, Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -234,44 +600,53 @@ def log_encoding_ALEXALogC(x, """ x = to_domain_1(x) + firmware = validate_method(firmware, ["SUP 3.x", "SUP 2.x"]) + method = validate_method( + method, ["Linear Scene Exposure Factor", "Normalised Sensor Signal"] + ) - cut, a, b, c, d, e, f, _e_cut_f = ( - DATA_ALEXA_LOG_C_CURVE_CONVERSION[firmware][method][EI]) + cut, a, b, c, d, e, f, _e_cut_f = DATA_ALEXA_LOG_C_CURVE_CONVERSION[ + firmware + ][method][EI] t = np.where(x > cut, c * np.log10(a * x + b) + d, e * x + f) return as_float(from_range_1(t)) -def log_decoding_ALEXALogC(t, - firmware='SUP 3.x', - method='Linear Scene Exposure Factor', - EI=800): +def log_decoding_ALEXALogC( + t: FloatingOrArrayLike, + firmware: Union[Literal["SUP 2.x", "SUP 3.x"], str] = "SUP 3.x", + method: Union[ + Literal["Linear Scene Exposure Factor", "Normalised Sensor Signal"], + str, + ] = "Linear Scene Exposure Factor", + EI: Literal[ + 160, 200, 250, 320, 400, 500, 640, 800, 1000, 1280, 1600 + ] = 800, +) -> FloatingOrNDArray: """ - Defines the *ARRI ALEXA Log C* log decoding curve / electro-optical + Define the *ARRI ALEXA Log C* log decoding curve / electro-optical transfer function. Parameters ---------- - t : numeric or array_like + t *ARRI ALEXA Log C* encoded data :math:`t`. - firmware : unicode, optional - **{'SUP 3.x', 'SUP 2.x'}**, + firmware Alexa firmware version. - method : unicode, optional - **{'Linear Scene Exposure Factor', 'Normalised Sensor Signal'}**, + method Conversion method. - EI : int, optional - Ei. + EI + Exposure Index :math:`EI`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`x`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -295,9 +670,13 @@ def log_decoding_ALEXALogC(t, """ t = to_domain_1(t) + method = validate_method( + method, ["Linear Scene Exposure Factor", "Normalised Sensor Signal"] + ) - cut, a, b, c, d, e, f, _e_cut_f = ( - DATA_ALEXA_LOG_C_CURVE_CONVERSION[firmware][method][EI]) + cut, a, b, c, d, e, f, _e_cut_f = DATA_ALEXA_LOG_C_CURVE_CONVERSION[ + firmware + ][method][EI] x = np.where(t > e * cut + f, (10 ** ((t - d) / c) - b) / a, (t - f) / e) diff --git a/colour/models/rgb/transfer_functions/blackmagic_design.py b/colour/models/rgb/transfer_functions/blackmagic_design.py new file mode 100644 index 0000000000..5077825702 --- /dev/null +++ b/colour/models/rgb/transfer_functions/blackmagic_design.py @@ -0,0 +1,171 @@ +""" +Blackmagic Design Transfer Functions +==================================== + +Defines the *Blackmagic Design* colour component transfer functions: + +- :func:`colour.models.oetf_BlackmagicFilmGeneration5` +- :func:`colour.models.oetf_inverse_BlackmagicFilmGeneration5` + +References +---------- +- :cite:`BlackmagicDesign2021` : Blackmagic Design. (2021). Blackmagic + Generation 5 Color Science. https://drive.google.com/file/d/\ +1FF5WO2nvI9GEWb4_EntrBoV9ZIuFToZd/view +""" + +from __future__ import annotations + +import numpy as np + +from colour.hints import FloatingOrArrayLike, FloatingOrNDArray +from colour.utilities import Structure, as_float, from_range_1, to_domain_1 + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "CONSTANTS_BLACKMAGIC_FILM_GENERATION_5", + "oetf_BlackmagicFilmGeneration5", + "oetf_inverse_BlackmagicFilmGeneration5", +] + +CONSTANTS_BLACKMAGIC_FILM_GENERATION_5: Structure = Structure( + A=0.08692876065491224, + B=0.005494072432257808, + C=0.5300133392291939, + D=8.283605932402494, + E=0.09246575342465753, + LIN_CUT=0.005, +) +"""*Blackmagic Film Generation 5* colour component transfer functions constants.""" + + +def oetf_BlackmagicFilmGeneration5( + x: FloatingOrArrayLike, + constants: Structure = CONSTANTS_BLACKMAGIC_FILM_GENERATION_5, +) -> FloatingOrNDArray: + """ + Define the *Blackmagic Film Generation 5* opto-electronic transfer + function. + + Parameters + ---------- + x + Linear light value :math`x`. + constants + *Blackmagic Film Generation 5* constants. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + Encoded value :math:`y`. + + Notes + ----- + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``x`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + +------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``y`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + References + ---------- + :cite:`BlackmagicDesign2021` + + Examples + -------- + >>> oetf_BlackmagicFilmGeneration5(0.18) # doctest: +ELLIPSIS + 0.3835616... + """ + + x = to_domain_1(x) + + A = constants.A + B = constants.B + C = constants.C + D = constants.D + E = constants.E + LIN_CUT = constants.LIN_CUT + + V_out = np.where( + x < LIN_CUT, + D * x + E, + A * np.log(x + B) + C, + ) + + return as_float(from_range_1(V_out)) + + +def oetf_inverse_BlackmagicFilmGeneration5( + y: FloatingOrArrayLike, + constants: Structure = CONSTANTS_BLACKMAGIC_FILM_GENERATION_5, +) -> FloatingOrNDArray: + """ + Define the *Blackmagic Film Generation 5* inverse opto-electronic transfer + function (OETF). + + Parameters + ---------- + y + Encoded value :math:`y`. + constants + *Blackmagic Film Generation 5* constants. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + Linear light value :math`x`. + + Notes + ----- + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``y`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + +------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``x`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + References + ---------- + :cite:`BlackmagicDesign2021` + + Examples + -------- + >>> oetf_inverse_BlackmagicFilmGeneration5(0.38356164383561653) + ... # doctest: +ELLIPSIS + 0.1799999... + """ + + y = to_domain_1(y) + + A = constants.A + B = constants.B + C = constants.C + D = constants.D + E = constants.E + LIN_CUT = constants.LIN_CUT + + LOG_CUT = D * LIN_CUT + E + + x = np.where( + y < LOG_CUT, + (y - E) / D, + np.exp((y - C) / A) - B, + ) + return as_float(from_range_1(x)) diff --git a/colour/models/rgb/transfer_functions/canon_log.py b/colour/models/rgb/transfer_functions/canon_log.py index 2d14d429ee..065223434c 100644 --- a/colour/models/rgb/transfer_functions/canon_log.py +++ b/colour/models/rgb/transfer_functions/canon_log.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Canon Log Encodings =================== @@ -29,58 +28,66 @@ http://downloads.canon.com/CDLC/Canon-Log_Transfer_Characteristic_6-20-2012.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +from colour.hints import ( + Boolean, + FloatingOrArrayLike, + FloatingOrNDArray, + Integer, +) from colour.models.rgb.transfer_functions import full_to_legal, legal_to_full -from colour.utilities import (as_float, domain_range_scale, from_range_1, - to_domain_1) -from colour.utilities.deprecation import handle_arguments_deprecation - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.utilities import ( + as_float, + domain_range_scale, + from_range_1, + to_domain_1, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'log_encoding_CanonLog', 'log_decoding_CanonLog', 'log_encoding_CanonLog2', - 'log_decoding_CanonLog2', 'log_encoding_CanonLog3', - 'log_decoding_CanonLog3' + "log_encoding_CanonLog", + "log_decoding_CanonLog", + "log_encoding_CanonLog2", + "log_decoding_CanonLog2", + "log_encoding_CanonLog3", + "log_decoding_CanonLog3", ] -def log_encoding_CanonLog(x, - bit_depth=10, - out_normalised_code_value=True, - in_reflection=True, - **kwargs): +def log_encoding_CanonLog( + x: FloatingOrArrayLike, + bit_depth: Integer = 10, + out_normalised_code_value: Boolean = True, + in_reflection: Boolean = True, +) -> FloatingOrNDArray: """ - Defines the *Canon Log* log encoding curve / opto-electronic transfer + Define the *Canon Log* log encoding curve / opto-electronic transfer function. Parameters ---------- - x : numeric or array_like + x Linear data :math:`x`. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - out_normalised_code_value : bool, optional + out_normalised_code_value Whether the *Canon Log* non-linear data is encoded as normalised code values. - in_reflection : bool, optional + in_reflection Whether the light level :math:`x` to a camera is reflection. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Canon Log* non-linear data. References @@ -89,7 +96,6 @@ def log_encoding_CanonLog(x, Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -117,62 +123,54 @@ def log_encoding_CanonLog(x, array([ 7.3, 12. , 32.8, 62.7, 108.7]) """ - out_normalised_code_value = handle_arguments_deprecation({ - 'ArgumentRenamed': [['out_legal', 'out_normalised_code_value']], - }, **kwargs).get('out_normalised_code_value', out_normalised_code_value) - x = to_domain_1(x) if in_reflection: x = x / 0.9 - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): clog = np.where( x < log_decoding_CanonLog(0.0730597, bit_depth, False), -(0.529136 * (np.log10(-x * 10.1596 + 1)) - 0.0730597), 0.529136 * np.log10(10.1596 * x + 1) + 0.0730597, ) - clog = (full_to_legal(clog, bit_depth) - if out_normalised_code_value else clog) + clog_cv = ( + full_to_legal(clog, bit_depth) if out_normalised_code_value else clog + ) - return as_float(from_range_1(clog)) + return as_float(from_range_1(clog_cv)) -def log_decoding_CanonLog(clog, - bit_depth=10, - in_normalised_code_value=True, - out_reflection=True, - **kwargs): +def log_decoding_CanonLog( + clog: FloatingOrArrayLike, + bit_depth: Integer = 10, + in_normalised_code_value: Boolean = True, + out_reflection: Boolean = True, +) -> FloatingOrNDArray: """ - Defines the *Canon Log* log decoding curve / electro-optical transfer + Define the *Canon Log* log decoding curve / electro-optical transfer function. Parameters ---------- - clog : numeric or array_like + clog *Canon Log* non-linear data. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - in_normalised_code_value : bool, optional + in_normalised_code_value Whether the *Canon Log* non-linear data is encoded with normalised code values. - out_reflection : bool, optional + out_reflection Whether the light level :math:`x` to a camera is reflection. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`x`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -195,14 +193,9 @@ def log_decoding_CanonLog(clog, 0.17999999... """ - in_normalised_code_value = handle_arguments_deprecation({ - 'ArgumentRenamed': [['in_legal', 'in_normalised_code_value']], - }, **kwargs).get('in_normalised_code_value', in_normalised_code_value) - clog = to_domain_1(clog) - clog = (legal_to_full(clog, bit_depth) - if in_normalised_code_value else clog) + clog = legal_to_full(clog, bit_depth) if in_normalised_code_value else clog x = np.where( clog < 0.0730597, @@ -216,40 +209,35 @@ def log_decoding_CanonLog(clog, return as_float(from_range_1(x)) -def log_encoding_CanonLog2(x, - bit_depth=10, - out_normalised_code_value=True, - in_reflection=True, - **kwargs): +def log_encoding_CanonLog2( + x: FloatingOrArrayLike, + bit_depth: Integer = 10, + out_normalised_code_value: Boolean = True, + in_reflection: Boolean = True, +) -> FloatingOrNDArray: """ - Defines the *Canon Log 2* log encoding curve / opto-electronic transfer + Define the *Canon Log 2* log encoding curve / opto-electronic transfer function. Parameters ---------- - x : numeric or array_like + x Linear data :math:`x`. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - out_normalised_code_value : bool, optional + out_normalised_code_value Whether the *Canon Log 2* non-linear data is encoded as normalised code values. - in_reflection : bool, optional + in_reflection Whether the light level :math:`x` to a camera is reflection. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Canon Log 2* non-linear data. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -272,62 +260,54 @@ def log_encoding_CanonLog2(x, 39.8254694... """ - out_normalised_code_value = handle_arguments_deprecation({ - 'ArgumentRenamed': [['out_legal', 'out_normalised_code_value']], - }, **kwargs).get('out_normalised_code_value', out_normalised_code_value) - x = to_domain_1(x) if in_reflection: x = x / 0.9 - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): clog2 = np.where( x < log_decoding_CanonLog2(0.035388128, bit_depth, False), -(0.281863093 * (np.log10(-x * 87.09937546 + 1)) - 0.035388128), 0.281863093 * np.log10(x * 87.09937546 + 1) + 0.035388128, ) - clog2 = (full_to_legal(clog2, bit_depth) - if out_normalised_code_value else clog2) + clog2_cv = ( + full_to_legal(clog2, bit_depth) if out_normalised_code_value else clog2 + ) - return as_float(from_range_1(clog2)) + return as_float(from_range_1(clog2_cv)) -def log_decoding_CanonLog2(clog2, - bit_depth=10, - in_normalised_code_value=True, - out_reflection=True, - **kwargs): +def log_decoding_CanonLog2( + clog2: FloatingOrArrayLike, + bit_depth: Integer = 10, + in_normalised_code_value: Boolean = True, + out_reflection: Boolean = True, +) -> FloatingOrNDArray: """ - Defines the *Canon Log 2* log decoding curve / electro-optical transfer + Define the *Canon Log 2* log decoding curve / electro-optical transfer function. Parameters ---------- - clog2 : numeric or array_like + clog2 *Canon Log 2* non-linear data. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - in_normalised_code_value : bool, optional + in_normalised_code_value Whether the *Canon Log 2* non-linear data is encoded with normalised code values. - out_reflection : bool, optional + out_reflection Whether the light level :math:`x` to a camera is reflection. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`x`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -350,14 +330,11 @@ def log_decoding_CanonLog2(clog2, 0.1799999... """ - in_normalised_code_value = handle_arguments_deprecation({ - 'ArgumentRenamed': [['in_legal', 'in_normalised_code_value']], - }, **kwargs).get('in_normalised_code_value', in_normalised_code_value) - clog2 = to_domain_1(clog2) - clog2 = (legal_to_full(clog2, bit_depth) - if in_normalised_code_value else clog2) + clog2 = ( + legal_to_full(clog2, bit_depth) if in_normalised_code_value else clog2 + ) x = np.where( clog2 < 0.035388128, @@ -371,35 +348,31 @@ def log_decoding_CanonLog2(clog2, return as_float(from_range_1(x)) -def log_encoding_CanonLog3(x, - bit_depth=10, - out_normalised_code_value=True, - in_reflection=True, - **kwargs): +def log_encoding_CanonLog3( + x: FloatingOrArrayLike, + bit_depth: Integer = 10, + out_normalised_code_value: Boolean = True, + in_reflection: Boolean = True, +) -> FloatingOrNDArray: """ - Defines the *Canon Log 3* log encoding curve / opto-electronic transfer + Define the *Canon Log 3* log encoding curve / opto-electronic transfer function. Parameters ---------- - x : numeric or array_like + x Linear data :math:`x`. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - out_normalised_code_value : bool, optional + out_normalised_code_value Whether the *Canon Log 3* non-linear data is encoded as normalised code values. - in_reflection : bool, optional + in_reflection Whether the light level :math:`x` to a camera is reflection. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Canon Log 3* non-linear data. Notes @@ -437,64 +410,66 @@ def log_encoding_CanonLog3(x, 34.3389369... """ - out_normalised_code_value = handle_arguments_deprecation({ - 'ArgumentRenamed': [['out_legal', 'out_normalised_code_value']], - }, **kwargs).get('out_normalised_code_value', out_normalised_code_value) - x = to_domain_1(x) if in_reflection: x = x / 0.9 - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): clog3 = np.select( - (x < log_decoding_CanonLog3(0.04076162, bit_depth, False, False), - x <= log_decoding_CanonLog3(0.105357102, bit_depth, False, False), - x > log_decoding_CanonLog3(0.105357102, bit_depth, False, False)), - (-0.42889912 * np.log10(-x * 14.98325 + 1) + 0.07623209, - 2.3069815 * x + 0.073059361, - 0.42889912 * np.log10(x * 14.98325 + 1) + 0.069886632)) + ( + x + < log_decoding_CanonLog3(0.04076162, bit_depth, False, False), + x + <= log_decoding_CanonLog3( + 0.105357102, bit_depth, False, False + ), + x + > log_decoding_CanonLog3(0.105357102, bit_depth, False, False), + ), + ( + -0.42889912 * np.log10(-x * 14.98325 + 1) + 0.07623209, + 2.3069815 * x + 0.073059361, + 0.42889912 * np.log10(x * 14.98325 + 1) + 0.069886632, + ), + ) - clog3 = (full_to_legal(clog3, bit_depth) - if out_normalised_code_value else clog3) + clog3_cv = ( + full_to_legal(clog3, bit_depth) if out_normalised_code_value else clog3 + ) - return as_float(from_range_1(clog3)) + return as_float(from_range_1(clog3_cv)) -def log_decoding_CanonLog3(clog3, - bit_depth=10, - in_normalised_code_value=True, - out_reflection=True, - **kwargs): +def log_decoding_CanonLog3( + clog3: FloatingOrArrayLike, + bit_depth: Integer = 10, + in_normalised_code_value: Boolean = True, + out_reflection: Boolean = True, +) -> FloatingOrNDArray: """ - Defines the *Canon Log 3* log decoding curve / electro-optical transfer + Define the *Canon Log 3* log decoding curve / electro-optical transfer function. Parameters ---------- - clog3 : numeric or array_like + clog3 *Canon Log 3* non-linear data. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - in_normalised_code_value : bool, optional + in_normalised_code_value Whether the *Canon Log 3* non-linear data is encoded with normalised code values. - out_reflection : bool, optional + out_reflection Whether the light level :math:`x` to a camera is reflection. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`x`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -517,20 +492,20 @@ def log_decoding_CanonLog3(clog3, 0.1800000... """ - in_normalised_code_value = handle_arguments_deprecation({ - 'ArgumentRenamed': [['in_legal', 'in_normalised_code_value']], - }, **kwargs).get('in_normalised_code_value', in_normalised_code_value) - clog3 = to_domain_1(clog3) - clog3 = (legal_to_full(clog3, bit_depth) - if in_normalised_code_value else clog3) + clog3 = ( + legal_to_full(clog3, bit_depth) if in_normalised_code_value else clog3 + ) x = np.select( (clog3 < 0.04076162, clog3 <= 0.105357102, clog3 > 0.105357102), - (-(10 ** ((0.07623209 - clog3) / 0.42889912) - 1) / 14.98325, - (clog3 - 0.073059361) / 2.3069815, - (10 ** ((clog3 - 0.069886632) / 0.42889912) - 1) / 14.98325)) + ( + -(10 ** ((0.07623209 - clog3) / 0.42889912) - 1) / 14.98325, + (clog3 - 0.073059361) / 2.3069815, + (10 ** ((clog3 - 0.069886632) / 0.42889912) - 1) / 14.98325, + ), + ) if out_reflection: x = x * 0.9 diff --git a/colour/models/rgb/transfer_functions/cineon.py b/colour/models/rgb/transfer_functions/cineon.py index df743d6347..986ff74270 100644 --- a/colour/models/rgb/transfer_functions/cineon.py +++ b/colour/models/rgb/transfer_functions/cineon.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Kodak Cineon Encoding ===================== @@ -16,42 +15,53 @@ nuke-default/make.py """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.utilities import from_range_1, to_domain_1 - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['log_encoding_Cineon', 'log_decoding_Cineon'] - - -def log_encoding_Cineon(x, black_offset=10 ** ((95 - 685) / 300)): +from colour.hints import FloatingOrArrayLike, FloatingOrNDArray +from colour.utilities import ( + as_float, + as_float_array, + from_range_1, + to_domain_1, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "log_encoding_Cineon", + "log_decoding_Cineon", +] + + +def log_encoding_Cineon( + x: FloatingOrArrayLike, + black_offset: FloatingOrArrayLike = 10 ** ((95 - 685) / 300), +) -> FloatingOrNDArray: """ - Defines the *Cineon* log encoding curve / opto-electronic transfer + Define the *Cineon* log encoding curve / opto-electronic transfer function. Parameters ---------- - x : numeric or array_like + x Linear data :math:`x`. - black_offset : numeric or array_like + black_offset Black offset. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Non-linear data :math:`y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -75,32 +85,35 @@ def log_encoding_Cineon(x, black_offset=10 ** ((95 - 685) / 300)): """ x = to_domain_1(x) + black_offset = as_float_array(black_offset) - y = ((685 + 300 * np.log10(x * (1 - black_offset) + black_offset)) / 1023) + y = (685 + 300 * np.log10(x * (1 - black_offset) + black_offset)) / 1023 - return from_range_1(y) + return as_float(from_range_1(y)) -def log_decoding_Cineon(y, black_offset=10 ** ((95 - 685) / 300)): +def log_decoding_Cineon( + y: FloatingOrArrayLike, + black_offset: FloatingOrArrayLike = 10 ** ((95 - 685) / 300), +) -> FloatingOrNDArray: """ - Defines the *Cineon* log decoding curve / electro-optical transfer + Define the *Cineon* log decoding curve / electro-optical transfer function. Parameters ---------- - y : numeric or array_like + y Non-linear data :math:`y`. - black_offset : numeric or array_like + black_offset Black offset. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`x`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -124,7 +137,8 @@ def log_decoding_Cineon(y, black_offset=10 ** ((95 - 685) / 300)): """ y = to_domain_1(y) + black_offset = as_float_array(black_offset) - x = ((10 ** ((1023 * y - 685) / 300) - black_offset) / (1 - black_offset)) + x = (10 ** ((1023 * y - 685) / 300) - black_offset) / (1 - black_offset) - return from_range_1(x) + return as_float(from_range_1(x)) diff --git a/colour/models/rgb/transfer_functions/common.py b/colour/models/rgb/transfer_functions/common.py index de0e51d76e..939ec53a9d 100644 --- a/colour/models/rgb/transfer_functions/common.py +++ b/colour/models/rgb/transfer_functions/common.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Common Transfer Functions Utilities =================================== @@ -6,40 +5,55 @@ Defines various transfer functions common utilities. """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.constants import DEFAULT_FLOAT_DTYPE, DEFAULT_INT_DTYPE -from colour.utilities import as_float_array - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['CV_range', 'legal_to_full', 'full_to_legal'] - - -def CV_range(bit_depth=10, is_legal=False, is_int=False): +from colour.hints import ( + Boolean, + FloatingOrArrayLike, + FloatingOrNDArray, + Integer, + IntegerOrArrayLike, + IntegerOrNDArray, + NDArray, + Union, +) +from colour.utilities import as_float, as_int, as_float_array, as_int_array + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "CV_range", + "legal_to_full", + "full_to_legal", +] + + +def CV_range( + bit_depth: Integer = 10, is_legal: Boolean = False, is_int: Boolean = False +) -> NDArray: """ - Returns the code value :math:`CV` range for given bit depth, range legality + Return the code value :math:`CV` range for given bit depth, range legality and representation. Parameters ---------- - bit_depth : int, optional + bit_depth Bit depth of the code value :math:`CV` range. - is_legal : bool, optional + is_legal Whether the code value :math:`CV` range is legal. - is_int : bool, optional + is_int Whether the code value :math:`CV` range represents integer code values. Returns ------- - ndarray + :class:`numpy.ndarray` Code value :math:`CV` range. Examples @@ -56,37 +70,42 @@ def CV_range(bit_depth=10, is_legal=False, is_int=False): ranges = np.array([16, 235]) ranges *= 2 ** (bit_depth - 8) else: - ranges = np.array([0, 2 ** bit_depth - 1]) + ranges = np.array([0, 2**bit_depth - 1]) if not is_int: - ranges = ranges.astype(DEFAULT_FLOAT_DTYPE) / (2 ** bit_depth - 1) + ranges = as_float_array(ranges) / (2**bit_depth - 1) return ranges -def legal_to_full(CV, bit_depth=10, in_int=False, out_int=False): +def legal_to_full( + CV: Union[FloatingOrArrayLike, IntegerOrArrayLike], + bit_depth: Integer = 10, + in_int: Boolean = False, + out_int: Boolean = False, +) -> Union[FloatingOrNDArray, IntegerOrNDArray]: """ - Converts given code value :math:`CV` or float equivalent of a code value at + Convert given code value :math:`CV` or float equivalent of a code value at a given bit depth from legal range (studio swing) to full range (full swing). Parameters ---------- - CV : array_like + CV Legal range code value :math:`CV` or float equivalent of a code value at a given bit depth. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - in_int : bool, optional + in_int Whether to treat the input value as integer code value or float equivalent of a code value at a given bit depth. - out_int : bool, optional + out_int Whether to return value as integer code value or float equivalent of a code value at a given bit depth. Returns ------- - ndarray + :class:`numpy.floating` or :class:`numpy.integer` or :class:`numpy.ndarray` Full range code value :math:`CV` or float equivalent of a code value at a given bit depth. @@ -112,40 +131,48 @@ def legal_to_full(CV, bit_depth=10, in_int=False, out_int=False): CV = as_float_array(CV) - MV = 2 ** bit_depth - 1 + MV = 2**bit_depth - 1 - CV = np.round(CV).astype(DEFAULT_INT_DTYPE) if in_int else CV * MV + CV_full = as_int_array(np.round(CV)) if in_int else CV * MV B, W = CV_range(bit_depth, True, True) - CV = (CV - B) / (W - B) + CV_full = (CV_full - B) / (W - B) - return np.round(CV * MV).astype(DEFAULT_INT_DTYPE) if out_int else CV + if out_int: + return as_int(np.round(CV_full * MV)) + else: + return as_float(CV_full) -def full_to_legal(CV, bit_depth=10, in_int=False, out_int=False): +def full_to_legal( + CV: Union[FloatingOrArrayLike, IntegerOrArrayLike], + bit_depth: Integer = 10, + in_int: Boolean = False, + out_int: Boolean = False, +) -> Union[FloatingOrNDArray, IntegerOrNDArray]: """ - Converts given code value :math:`CV` or float equivalent of a code value at + Convert given code value :math:`CV` or float equivalent of a code value at a given bit depth from full range (full swing) to legal range (studio swing). Parameters ---------- - CV : array_like + CV Full range code value :math:`CV` or float equivalent of a code value at a given bit depth. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - in_int : bool, optional + in_int Whether to treat the input value as integer code value or float equivalent of a code value at a given bit depth. - out_int : bool, optional + out_int Whether to return value as integer code value or float equivalent of a code value at a given bit depth. Returns ------- - ndarray + :class:`numpy.floating` or :class:`numpy.integer` or :class:`numpy.ndarray` Legal range code value :math:`CV` or float equivalent of a code value at a given bit depth. @@ -171,12 +198,15 @@ def full_to_legal(CV, bit_depth=10, in_int=False, out_int=False): CV = as_float_array(CV) - MV = 2 ** bit_depth - 1 + MV = 2**bit_depth - 1 - CV = np.round(CV / MV).astype(DEFAULT_INT_DTYPE) if in_int else CV + CV_legal = as_int_array(np.round(CV / MV)) if in_int else CV B, W = CV_range(bit_depth, True, True) - CV = (W - B) * CV + B + CV_legal = (W - B) * CV_legal + B - return np.round(CV).astype(DEFAULT_INT_DTYPE) if out_int else CV / MV + if out_int: + return as_int(np.round(CV_legal)) + else: + return as_float(CV_legal / MV) diff --git a/colour/models/rgb/transfer_functions/davinci_intermediate.py b/colour/models/rgb/transfer_functions/davinci_intermediate.py new file mode 100644 index 0000000000..527d0d76b5 --- /dev/null +++ b/colour/models/rgb/transfer_functions/davinci_intermediate.py @@ -0,0 +1,168 @@ +""" +DaVinci Intermediate +==================== + +Defines the *DaVinci Intermediate* opto-electrical transfer function +(OETF) and its inverse: + +- :func:`colour.models.oetf_DaVinciIntermediate` +- :func:`colour.models.oetf_inverse_DaVinciIntermediate` + +References +---------- +- :cite:`BlackmagicDesign2020a` : Blackmagic Design. (2020). Wide Gamut + Intermediate DaVinci Resolve. Retrieved December 12, 2020, from + https://documents.blackmagicdesign.com/InformationNotes/\ +DaVinci_Resolve_17_Wide_Gamut_Intermediate.pdf?_v=1607414410000 +""" + +from __future__ import annotations + +import numpy as np + +from colour.hints import FloatingOrArrayLike, FloatingOrNDArray +from colour.utilities import Structure, as_float, from_range_1, to_domain_1 + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "CONSTANTS_DAVINCI_INTERMEDIATE", + "oetf_DaVinciIntermediate", + "oetf_inverse_DaVinciIntermediate", +] + +CONSTANTS_DAVINCI_INTERMEDIATE: Structure = Structure( + DI_A=0.0075, + DI_B=7.0, + DI_C=0.07329248, + DI_M=10.44426855, + DI_LIN_CUT=0.00262409, + DI_LOG_CUT=0.02740668, +) +"""*DaVinci Intermediate* colour component transfer functions constants.""" + + +def oetf_DaVinciIntermediate( + L: FloatingOrArrayLike, + constants: Structure = CONSTANTS_DAVINCI_INTERMEDIATE, +) -> FloatingOrNDArray: + """ + Define the *DaVinci Intermediate* opto-electronic transfer function. + + Parameters + ---------- + L + Linear light value :math`L`. + constants + *DaVinci Intermediate* colour component transfer function constants. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + Encoded value :math:`V`. + + Notes + ----- + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``L`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + +------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``V`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + References + ---------- + :cite:`BlackmagicDesign2020a` + + Examples + -------- + >>> oetf_DaVinciIntermediate(0.18) # doctest: +ELLIPSIS + 0.3360432... + """ + + L = to_domain_1(L) + + DI_LIN_CUT = constants.DI_LIN_CUT + DI_A = constants.DI_A + DI_B = constants.DI_B + DI_C = constants.DI_C + DI_M = constants.DI_M + + V_out = np.where( + L <= DI_LIN_CUT, + L * DI_M, + DI_C * (np.log2(L + DI_A) + DI_B), + ) + + return as_float(from_range_1(V_out)) + + +def oetf_inverse_DaVinciIntermediate( + V: FloatingOrArrayLike, + constants: Structure = CONSTANTS_DAVINCI_INTERMEDIATE, +) -> FloatingOrNDArray: + """ + Define the *DaVinci Intermediate* inverse opto-electronic transfer + function (OETF). + + Parameters + ---------- + V + Encoded value :math:`V`. + constants + *DaVinci Intermediate* colour component transfer function constants. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + Linear light value :math`L`. + + Notes + ----- + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``y`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + +------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``x`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + References + ---------- + :cite:`BlackmagicDesign2020a` + + Examples + -------- + >>> oetf_inverse_DaVinciIntermediate(0.336043272384855) + ... # doctest: +ELLIPSIS + 0.1799999... + """ + + V = to_domain_1(V) + + DI_LOG_CUT = constants.DI_LOG_CUT + DI_A = constants.DI_A + DI_B = constants.DI_B + DI_C = constants.DI_C + DI_M = constants.DI_M + + L_out = np.where( + V <= DI_LOG_CUT, + V / DI_M, + 2 ** ((V / DI_C) - DI_B) - DI_A, + ) + return as_float(from_range_1(L_out)) diff --git a/colour/models/rgb/transfer_functions/dcdm.py b/colour/models/rgb/transfer_functions/dcdm.py index 483c0fde08..a0b5cc4866 100644 --- a/colour/models/rgb/transfer_functions/dcdm.py +++ b/colour/models/rgb/transfer_functions/dcdm.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Digital Cinema Distribution Master (DCDM) ========================================= -Defines the *DCDM* electro-optical transfer function (EOTF / EOCF) and its +Defines the *DCDM* electro-optical transfer function (EOTF) and its inverse: - :func:`colour.models.eotf_inverse_DCDM` @@ -17,39 +16,51 @@ DCI_DCinema_System_Spec_v1_1.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.algebra import spow -from colour.constants import DEFAULT_INT_DTYPE -from colour.utilities import from_range_1, to_domain_1 - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['eotf_inverse_DCDM', 'eotf_DCDM'] - - -def eotf_inverse_DCDM(XYZ, out_int=False): +from colour.hints import ( + Boolean, + FloatingOrArrayLike, + FloatingOrNDArray, + IntegerOrArrayLike, + IntegerOrNDArray, + Union, +) +from colour.utilities import as_float_array, as_float, as_int + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "eotf_inverse_DCDM", + "eotf_DCDM", +] + + +def eotf_inverse_DCDM( + XYZ: FloatingOrArrayLike, out_int: Boolean = False +) -> Union[FloatingOrNDArray, IntegerOrNDArray]: """ - Defines the *DCDM* inverse electro-optical transfer function (EOTF / EOCF). + Define the *DCDM* inverse electro-optical transfer function (EOTF). Parameters ---------- - XYZ : numeric or array_like + XYZ *CIE XYZ* tristimulus values. - out_int : bool, optional + out_int Whether to return value as integer code value or float equivalent of a code value at a given bit depth. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.integer` or :class:`numpy.ndarray` Non-linear *CIE XYZ'* tristimulus values. Warnings @@ -58,7 +69,6 @@ def eotf_inverse_DCDM(XYZ, out_int=False): Notes ----- - - *DCDM* is an absolute transfer function, thus the domain and range values for the *Reference* and *1* scales are only indicative that the data is not affected by scale transformations. @@ -90,31 +100,34 @@ def eotf_inverse_DCDM(XYZ, out_int=False): 462 """ - XYZ = to_domain_1(XYZ) + XYZ = as_float_array(XYZ) XYZ_p = spow(XYZ / 52.37, 1 / 2.6) if out_int: - return np.round(4095 * XYZ_p).astype(DEFAULT_INT_DTYPE) + return as_int(np.round(4095 * XYZ_p)) else: - return from_range_1(XYZ_p) + return as_float(XYZ_p) -def eotf_DCDM(XYZ_p, in_int=False): +def eotf_DCDM( + XYZ_p: Union[FloatingOrArrayLike, IntegerOrArrayLike], + in_int: Boolean = False, +) -> FloatingOrNDArray: """ - Defines the *DCDM* electro-optical transfer function (EOTF / EOCF). + Define the *DCDM* electro-optical transfer function (EOTF). Parameters ---------- - XYZ_p : numeric or array_like + XYZ_p Non-linear *CIE XYZ'* tristimulus values. - in_int : bool, optional + in_int Whether to treat the input value as integer code value or float equivalent of a code value at a given bit depth. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Warnings @@ -123,7 +136,6 @@ def eotf_DCDM(XYZ_p, in_int=False): Notes ----- - - *DCDM* is an absolute transfer function, thus the domain and range values for the *Reference* and *1* scales are only indicative that the data is not affected by scale transformations. @@ -155,11 +167,11 @@ def eotf_DCDM(XYZ_p, in_int=False): 0.18... """ - XYZ_p = to_domain_1(XYZ_p) + XYZ_p = as_float_array(XYZ_p) if in_int: XYZ_p = XYZ_p / 4095 XYZ = 52.37 * spow(XYZ_p, 2.6) - return from_range_1(XYZ) + return as_float(XYZ) diff --git a/colour/models/rgb/transfer_functions/dicom_gsdf.py b/colour/models/rgb/transfer_functions/dicom_gsdf.py index 156bbb54b6..c6c4924272 100644 --- a/colour/models/rgb/transfer_functions/dicom_gsdf.py +++ b/colour/models/rgb/transfer_functions/dicom_gsdf.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ DICOM - Grayscale Standard Display Function =========================================== Defines the *DICOM - Grayscale Standard Display Function* electro-optical -transfer function (EOTF / EOCF) and its inverse: +transfer function (EOTF) and its inverse: - :func:`colour.models.eotf_inverse_DICOMGSDF` - :func:`colour.models.eotf_DICOMGSDF` @@ -24,23 +23,40 @@ Function. http://medical.nema.org/dicom/2004/04_14PU.PDF """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.utilities import (Structure, as_float, as_int, from_range_1, - to_domain_1) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['CONSTANTS_DICOMGSDF', 'eotf_inverse_DICOMGSDF', 'eotf_DICOMGSDF'] - -CONSTANTS_DICOMGSDF = Structure( +from colour.hints import ( + Boolean, + FloatingOrArrayLike, + FloatingOrNDArray, + IntegerOrArrayLike, + IntegerOrNDArray, + Union, +) +from colour.utilities import ( + Structure, + as_float, + as_int, + from_range_1, + to_domain_1, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "CONSTANTS_DICOMGSDF", + "eotf_inverse_DICOMGSDF", + "eotf_DICOMGSDF", +] + +CONSTANTS_DICOMGSDF: Structure = Structure( a=-1.3011877, b=-2.5840191e-2, c=8.0242636e-2, @@ -59,35 +75,37 @@ F=-1.1878455, G=-0.18014349, H=0.14710899, - I=-0.017046845) # noqa -""" -*DICOM Grayscale Standard Display Function* constants. + I=-0.017046845, +) # noqa +"""*DICOM Grayscale Standard Display Function* constants.""" -CONSTANTS_DICOMGSDF : Structure -""" - -def eotf_inverse_DICOMGSDF(L, out_int=False): +def eotf_inverse_DICOMGSDF( + L: FloatingOrArrayLike, + out_int: Boolean = False, + constants: Structure = CONSTANTS_DICOMGSDF, +) -> Union[FloatingOrNDArray, IntegerOrNDArray]: """ - Defines the *DICOM - Grayscale Standard Display Function* inverse - electro-optical transfer function (EOTF / EOCF). + Define the *DICOM - Grayscale Standard Display Function* inverse + electro-optical transfer function (EOTF). Parameters ---------- - L : numeric or array_like + L *Luminance* :math:`L`. - out_int : bool, optional + out_int Whether to return value as integer code value or float equivalent of a code value at a given bit depth. + constants + *DICOM - Grayscale Standard Display Function* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.integer` or :class:`numpy.ndarray` Just-Noticeable Difference (JND) Index, :math:`j`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -116,18 +134,27 @@ def eotf_inverse_DICOMGSDF(L, out_int=False): L_lg = np.log10(L) - A = CONSTANTS_DICOMGSDF.A - B = CONSTANTS_DICOMGSDF.B - C = CONSTANTS_DICOMGSDF.C - D = CONSTANTS_DICOMGSDF.D - E = CONSTANTS_DICOMGSDF.E - F = CONSTANTS_DICOMGSDF.F - G = CONSTANTS_DICOMGSDF.G - H = CONSTANTS_DICOMGSDF.H - I = CONSTANTS_DICOMGSDF.I # noqa - - J = (A + B * L_lg + C * L_lg ** 2 + D * L_lg ** 3 + E * L_lg ** 4 + - F * L_lg ** 5 + G * L_lg ** 6 + H * L_lg ** 7 + I * L_lg ** 8) + A = constants.A + B = constants.B + C = constants.C + D = constants.D + E = constants.E + F = constants.F + G = constants.G + H = constants.H + I = constants.I # noqa + + J = ( + A + + B * L_lg + + C * L_lg**2 + + D * L_lg**3 + + E * L_lg**4 + + F * L_lg**5 + + G * L_lg**6 + + H * L_lg**7 + + I * L_lg**8 + ) if out_int: return as_int(np.round(J)) @@ -135,27 +162,32 @@ def eotf_inverse_DICOMGSDF(L, out_int=False): return as_float(from_range_1(J / 1023)) -def eotf_DICOMGSDF(J, in_int=False): +def eotf_DICOMGSDF( + J: Union[FloatingOrArrayLike, IntegerOrArrayLike], + in_int: Boolean = False, + constants: Structure = CONSTANTS_DICOMGSDF, +) -> FloatingOrNDArray: """ - Defines the *DICOM - Grayscale Standard Display Function* electro-optical - transfer function (EOTF / EOCF). + Define the *DICOM - Grayscale Standard Display Function* electro-optical + transfer function (EOTF). Parameters ---------- - J : numeric or array_like + J Just-Noticeable Difference (JND) Index, :math:`j`. - in_int : bool, optional + in_int Whether to treat the input value as integer code value or float equivalent of a code value at a given bit depth. + constants + *DICOM - Grayscale Standard Display Function* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Corresponding *luminance* :math:`L`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -185,25 +217,26 @@ def eotf_DICOMGSDF(J, in_int=False): if not in_int: J = J * 1023 - a = CONSTANTS_DICOMGSDF.a - b = CONSTANTS_DICOMGSDF.b - c = CONSTANTS_DICOMGSDF.c - d = CONSTANTS_DICOMGSDF.d - e = CONSTANTS_DICOMGSDF.e - f = CONSTANTS_DICOMGSDF.f - g = CONSTANTS_DICOMGSDF.g - h = CONSTANTS_DICOMGSDF.h - k = CONSTANTS_DICOMGSDF.k - m = CONSTANTS_DICOMGSDF.m + a = constants.a + b = constants.b + c = constants.c + d = constants.d + e = constants.e + f = constants.f + g = constants.g + h = constants.h + k = constants.k + m = constants.m J_ln = np.log(J) - J_ln2 = J_ln ** 2 - J_ln3 = J_ln ** 3 - J_ln4 = J_ln ** 4 - J_ln5 = J_ln ** 5 - - L = ((a + c * J_ln + e * J_ln2 + g * J_ln3 + m * J_ln4) / - (1 + b * J_ln + d * J_ln2 + f * J_ln3 + h * J_ln4 + k * J_ln5)) - L = 10 ** L + J_ln2 = J_ln**2 + J_ln3 = J_ln**3 + J_ln4 = J_ln**4 + J_ln5 = J_ln**5 + + L = (a + c * J_ln + e * J_ln2 + g * J_ln3 + m * J_ln4) / ( + 1 + b * J_ln + d * J_ln2 + f * J_ln3 + h * J_ln4 + k * J_ln5 + ) + L = 10**L return as_float(from_range_1(L)) diff --git a/colour/models/rgb/transfer_functions/dji_dlog.py b/colour/models/rgb/transfer_functions/dji_dlog.py index faba3530bf..445e474233 100644 --- a/colour/models/rgb/transfer_functions/dji_dlog.py +++ b/colour/models/rgb/transfer_functions/dji_dlog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ DJI D-Log Log Encoding ====================== @@ -16,34 +15,38 @@ D-Log_D-Gamut_Whitepaper.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +from colour.hints import FloatingOrArrayLike, FloatingOrNDArray from colour.utilities import as_float, from_range_1, to_domain_1 -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['log_encoding_DJIDLog', 'log_decoding_DJIDLog'] +__all__ = [ + "log_encoding_DJIDLog", + "log_decoding_DJIDLog", +] -def log_encoding_DJIDLog(x): +def log_encoding_DJIDLog(x: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the *DJI D-Log* log encoding curve. + Define the *DJI D-Log* log encoding curve. Parameters ---------- - x : numeric or array_like + x Linear reflection data :math`x`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *DJI D-Log* encoded data :math:`y`. References @@ -52,7 +55,6 @@ def log_encoding_DJIDLog(x): Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -73,24 +75,27 @@ def log_encoding_DJIDLog(x): x = to_domain_1(x) - y = np.where(x <= 0.0078, 6.025 * x + 0.0929, - (np.log10(x * 0.9892 + 0.0108)) * 0.256663 + 0.584555) + y = np.where( + x <= 0.0078, + 6.025 * x + 0.0929, + (np.log10(x * 0.9892 + 0.0108)) * 0.256663 + 0.584555, + ) return as_float(from_range_1(y)) -def log_decoding_DJIDLog(y): +def log_decoding_DJIDLog(y: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the *DJI D-Log* log decoding curve. + Define the *DJI D-Log* log decoding curve. Parameters ---------- - y : numeric or array_like - Non-linear data :math:`t`. + y + *DJI D-Log* encoded data :math:`y`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear reflection data :math`x`. References @@ -99,7 +104,6 @@ def log_decoding_DJIDLog(y): Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -120,7 +124,10 @@ def log_decoding_DJIDLog(y): y = to_domain_1(y) - x = np.where(y <= 0.14, (y - 0.0929) / 6.025, - (10 ** (3.89616 * y - 2.27752) - 0.0108) / 0.9892) + x = np.where( + y <= 0.14, + (y - 0.0929) / 6.025, + (10 ** (3.89616 * y - 2.27752) - 0.0108) / 0.9892, + ) return as_float(from_range_1(x)) diff --git a/colour/models/rgb/transfer_functions/exponent.py b/colour/models/rgb/transfer_functions/exponent.py index 4e0a2c4f7a..6a4ece2c32 100644 --- a/colour/models/rgb/transfer_functions/exponent.py +++ b/colour/models/rgb/transfer_functions/exponent.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Basic and Monitor-Curve Exponent Transfer Functions =================================================== @@ -17,35 +16,62 @@ Tables. Retrieved June 24, 2020, from http://j.mp/S-2014-006 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.utilities import as_float, as_float_array, suppress_warnings - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['exponent_function_basic', 'exponent_function_monitor_curve'] - - -def exponent_function_basic(x, exponent=1, style='basicFwd'): +from colour.hints import ( + FloatingOrArrayLike, + FloatingOrNDArray, + Literal, + NDArray, + Union, +) +from colour.utilities import ( + as_float, + as_float_array, + suppress_warnings, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "exponent_function_basic", + "exponent_function_monitor_curve", +] + + +def exponent_function_basic( + x: FloatingOrArrayLike, + exponent: FloatingOrArrayLike = 1, + style: Union[ + Literal[ + "basicFwd", + "basicRev", + "basicMirrorFwd", + "basicMirrorRev", + "basicPassThruFwd", + "basicPassThruRev", + ], + str, + ] = "basicFwd", +) -> FloatingOrNDArray: """ - Defines the *basic* exponent transfer function. + Define the *basic* exponent transfer function. Parameters ---------- - x : numeric or array_like + x Data to undergo the basic exponent conversion. - exponent : numeric or array_like, optional + exponent Exponent value used for the conversion. - style : unicode, optional - **{'basicFwd', 'basicRev', 'basicMirrorFwd', 'basicMirrorRev', - 'basicPassThruFwd', 'basicPassThruRev'}**, + style Defines the behaviour for the transfer function to operate: - *basicFwd*: *Basic Forward* exponential behaviour where the @@ -75,14 +101,9 @@ def exponent_function_basic(x, exponent=1, style='basicFwd'): Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Exponentially converted data. - Raises - ------ - ValueError - If the *style* is not defined. - Examples -------- >>> exponent_function_basic(0.18, 2.2) # doctest: +ELLIPSIS @@ -121,64 +142,73 @@ def exponent_function_basic(x, exponent=1, style='basicFwd'): x = as_float_array(x) exponent = as_float_array(exponent) - - def exponent_forward(x): - """ - Returns the input raised to the exponent value. - """ - - return x ** exponent - - def exponent_reverse(y): - """ - Returns the input raised to the inverse exponent value. - """ - - return y ** (1 / exponent) - - style = style.lower() - if style == 'basicfwd': + style = validate_method( + style, + [ + "basicFwd", + "basicRev", + "basicMirrorFwd", + "basicMirrorRev", + "basicPassThruFwd", + "basicPassThruRev", + ], + '"{0}" style is invalid, it must be one of {1}!', + ) + + def exponent_forward(x: NDArray) -> NDArray: + """Return the input raised to the exponent value.""" + + return x**exponent + + def exponent_reverse(y: NDArray) -> NDArray: + """Return the input raised to the inverse exponent value.""" + + return y ** (as_float_array(1) / exponent) + + if style == "basicfwd": return as_float(np.where(x >= 0, exponent_forward(x), 0)) - elif style == 'basicrev': + elif style == "basicrev": return as_float(np.where(x >= 0, exponent_reverse(x), 0)) - elif style == 'basicmirrorfwd': + elif style == "basicmirrorfwd": return as_float( - np.where(x >= 0, exponent_forward(x), -exponent_forward(-x))) - elif style == 'basicmirrorrev': + np.where(x >= 0, exponent_forward(x), -exponent_forward(-x)) + ) + elif style == "basicmirrorrev": return as_float( - np.where(x >= 0, exponent_reverse(x), -exponent_reverse(-x))) - elif style == 'basicpassthrufwd': + np.where(x >= 0, exponent_reverse(x), -exponent_reverse(-x)) + ) + elif style == "basicpassthrufwd": return as_float(np.where(x >= 0, exponent_forward(x), x)) - elif style == 'basicpassthrurev': + else: # style == 'basicpassthrurev' return as_float(np.where(x >= 0, exponent_reverse(x), x)) - else: - raise ValueError( - 'Undefined style used: "{0}", must be one of the following: ' - '"{1}".'.format( - style, ', '.join([ - 'basicFwd', 'basicRev', 'basicMirrorFwd', 'basicMirrorRev', - 'basicPassThruFwd', 'basicPassThruRev' - ]))) - - -def exponent_function_monitor_curve(x, - exponent=1, - offset=0, - style='monCurveFwd'): + + +def exponent_function_monitor_curve( + x: FloatingOrArrayLike, + exponent: FloatingOrArrayLike = 1, + offset: FloatingOrArrayLike = 0, + style: Union[ + Literal[ + "monCurveFwd", + "monCurveRev", + "monCurveMirrorFwd", + "monCurveMirrorRev", + ], + str, + ] = "monCurveFwd", +) -> FloatingOrNDArray: """ - Defines the *Monitor Curve* exponent transfer function. + Define the *Monitor Curve* exponent transfer function. Parameters ---------- - x : numeric or array_like + x Data to undergo the monitor curve exponential conversion. - exponent : numeric or array_like, optional + exponent Exponent value used for the conversion. - offset: numeric or array_like, optional + offset Offset value used for the conversion. - style : unicode, optional - **{'monCurveFwd', 'monCurveRev', 'monCurveMirrorFwd', - 'monCurveMirrorRev'}**, + style Defines the behaviour for the transfer function to operate: - *monCurveFwd*: *Monitor Curve Forward* exponential behaviour @@ -198,14 +228,9 @@ def exponent_function_monitor_curve(x, Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Exponentially converted data. - Raises - ------ - ValueError - If the *style* is not defined. - Examples -------- >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS @@ -237,17 +262,30 @@ def exponent_function_monitor_curve(x, x = as_float_array(x) exponent = as_float_array(exponent) offset = as_float_array(offset) + style = validate_method( + style, + [ + "monCurveFwd", + "monCurveRev", + "monCurveMirrorFwd", + "monCurveMirrorRev", + ], + '"{0}" style is invalid, it must be one of {1}!', + ) with suppress_warnings(python_warnings=True): - s = as_float_array(((exponent - 1) / offset) * ((exponent * offset) / ( - (exponent - 1) * (offset + 1))) ** exponent) + s = as_float_array( + ((exponent - 1) / offset) + * ((exponent * offset) / ((exponent - 1) * (offset + 1))) + ** exponent + ) s[np.isnan(s)] = 1 - def monitor_curve_forward(x): - """ - Defines the *Monitor Curve Forward* function. - """ + def monitor_curve_forward( + x: NDArray, offset: NDArray, exponent: NDArray + ) -> NDArray: + """Define the *Monitor Curve Forward* function.""" x_break = offset / (exponent - 1) @@ -257,13 +295,13 @@ def monitor_curve_forward(x): x * s, ) - def monitor_curve_reverse(y): - """ - Defines the *Monitor Curve Reverse* function. - """ - - y_break = ((exponent * offset) / ( - (exponent - 1) * (1 + offset))) ** exponent + def monitor_curve_reverse( + y: NDArray, offset: NDArray, exponent: NDArray + ) -> NDArray: + """Define the *Monitor Curve Reverse* function.""" + y_break = ( + (exponent * offset) / ((exponent - 1) * (1 + offset)) + ) ** exponent return np.where( y >= y_break, @@ -271,30 +309,23 @@ def monitor_curve_reverse(y): y / s, ) - style = style.lower() - if style == 'moncurvefwd': - return as_float(monitor_curve_forward(x)) - elif style == 'moncurverev': - return as_float(monitor_curve_reverse(x)) - elif style == 'moncurvemirrorfwd': + if style == "moncurvefwd": + return as_float(monitor_curve_forward(x, offset, exponent)) + elif style == "moncurverev": + return as_float(monitor_curve_reverse(x, offset, exponent)) + elif style == "moncurvemirrorfwd": return as_float( np.where( x >= 0, - monitor_curve_forward(x), - -monitor_curve_forward(-x), - )) - elif style == 'moncurvemirrorrev': + monitor_curve_forward(x, offset, exponent), + -monitor_curve_forward(-x, offset, exponent), + ) + ) + else: # style == 'moncurvemirrorrev' return as_float( np.where( x >= 0, - monitor_curve_reverse(x), - -monitor_curve_reverse(-x), - )) - else: - raise ValueError( - 'Undefined style used: "{0}", must be one of the following: ' - '"{1}".'.format( - style, ', '.join([ - 'monCurveFwd', 'monCurveRev', 'monCurveMirrorFwd', - 'monCurveMirrorRev' - ]))) + monitor_curve_reverse(x, offset, exponent), + -monitor_curve_reverse(-x, offset, exponent), + ) + ) diff --git a/colour/models/rgb/transfer_functions/filmic_pro.py b/colour/models/rgb/transfer_functions/filmic_pro.py index 2c89c7b16a..c56e569b24 100644 --- a/colour/models/rgb/transfer_functions/filmic_pro.py +++ b/colour/models/rgb/transfer_functions/filmic_pro.py @@ -1,7 +1,6 @@ -# -*- coding: utf-8 -*- """ FiLMiC Pro 6 Encoding -=============--====== +===================== Defines the *FiLMiC Pro 6* encoding: @@ -14,41 +13,48 @@ Revision 1 (pp. 1-46). http://www.filmicpro.com/FilmicProUserManualv6.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.algebra import Extrapolator, LinearInterpolator -from colour.utilities import from_range_1, to_domain_1 - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['log_encoding_FilmicPro6', 'log_decoding_FilmicPro6'] - - -def log_encoding_FilmicPro6(t): +from colour.hints import ( + FloatingOrArrayLike, + FloatingOrNDArray, + Optional, +) +from colour.utilities import as_float, from_range_1, to_domain_1 + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "log_encoding_FilmicPro6", + "log_decoding_FilmicPro6", +] + + +def log_encoding_FilmicPro6(t: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the *FiLMiC Pro 6* log encoding curve / opto-electronic transfer + Define the *FiLMiC Pro 6* log encoding curve / opto-electronic transfer function. Parameters ---------- - t : numeric or array_like + t Linear data :math:`t`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Non-linear data :math:`y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -85,20 +91,20 @@ def log_encoding_FilmicPro6(t): y = 0.371 * (np.sqrt(t) + 0.28257 * np.log(t) + 1.69542) - return from_range_1(y) + return as_float(from_range_1(y)) -_LOG_DECODING_FILMICPRO_INTERPOLATOR_CACHE = None +_LOG_DECODING_FILMICPRO_INTERPOLATOR_CACHE: Optional[Extrapolator] = None -def _log_decoding_FilmicPro6_interpolator(): +def _log_decoding_FilmicPro6_interpolator() -> Extrapolator: """ - Returns the *FiLMiC Pro 6* log decoding curve / electro-optical transfer + Return the *FiLMiC Pro 6* log decoding curve / electro-optical transfer function interpolator and caches it if not existing. Returns ------- - Extrapolator + :class:`colour.Extrapolator` *FiLMiC Pro 6* log decoding curve / electro-optical transfer function interpolator. """ @@ -108,29 +114,29 @@ def _log_decoding_FilmicPro6_interpolator(): t = np.arange(0, 1, 0.0001) if _LOG_DECODING_FILMICPRO_INTERPOLATOR_CACHE is None: _LOG_DECODING_FILMICPRO_INTERPOLATOR_CACHE = Extrapolator( - LinearInterpolator(log_encoding_FilmicPro6(t), t)) + LinearInterpolator(log_encoding_FilmicPro6(t), t) + ) return _LOG_DECODING_FILMICPRO_INTERPOLATOR_CACHE -def log_decoding_FilmicPro6(y): +def log_decoding_FilmicPro6(y: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the *FiLMiC Pro 6* log decoding curve / electro-optical transfer + Define the *FiLMiC Pro 6* log decoding curve / electro-optical transfer function. Parameters ---------- - y : numeric or array_like + y Non-linear data :math:`y`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`t`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -165,4 +171,4 @@ def log_decoding_FilmicPro6(y): t = _log_decoding_FilmicPro6_interpolator()(y) - return from_range_1(t) + return as_float(from_range_1(t)) diff --git a/colour/models/rgb/transfer_functions/filmlight_tlog.py b/colour/models/rgb/transfer_functions/filmlight_tlog.py index f30de04a7e..a8f49bb611 100644 --- a/colour/models/rgb/transfer_functions/filmlight_tlog.py +++ b/colour/models/rgb/transfer_functions/filmlight_tlog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ FilmLight T-Log Log Encoding ============================ @@ -14,40 +13,49 @@ Shaw, Nick. """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +from colour.hints import Floating, FloatingOrArrayLike, FloatingOrNDArray from colour.utilities import as_float, from_range_1, to_domain_1 -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['log_encoding_FilmLightTLog', 'log_decoding_FilmLightTLog'] - - -def log_encoding_FilmLightTLog(x, w=128.0, g=16.0, o=0.075): +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "log_encoding_FilmLightTLog", + "log_decoding_FilmLightTLog", +] + + +def log_encoding_FilmLightTLog( + x: FloatingOrArrayLike, + w: Floating = 128.0, + g: Floating = 16.0, + o: Floating = 0.075, +) -> FloatingOrNDArray: """ - Defines the *FilmLight T-Log* log encoding curve. + Define the *FilmLight T-Log* log encoding curve. Parameters ---------- - x : numeric or array_like + x Linear reflection data :math`x`. - w : numeric, optional + w Value of :math:`x` for :math:`t = 1.0`. - g : numeric, optional + g Gradient at :math:`x = 0.0`. - o : numeric, optional + o Value of :math:`t` for :math:`x = 0.0`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *FilmLight T-Log* encoded data :math:`t`. References @@ -56,7 +64,6 @@ def log_encoding_FilmLightTLog(x, w=128.0, g=16.0, o=0.075): Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -120,24 +127,29 @@ def log_encoding_FilmLightTLog(x, w=128.0, g=16.0, o=0.075): return as_float(from_range_1(t)) -def log_decoding_FilmLightTLog(t, w=128.0, g=16.0, o=0.075): +def log_decoding_FilmLightTLog( + t: FloatingOrArrayLike, + w: Floating = 128.0, + g: Floating = 16.0, + o: Floating = 0.075, +) -> FloatingOrNDArray: """ - Defines the *FilmLight T-Log* log decoding curve. + Define the *FilmLight T-Log* log decoding curve. Parameters ---------- - t : numeric or array_like + t Non-linear data :math:`t`. - w : numeric, optional + w Value of :math:`x` for :math:`t = 1.0`. - g : numeric, optional + g Gradient at :math:`x = 0.0`. - o : numeric, optional + o Value of :math:`t` for :math:`x = 0.0`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear reflection data :math`x`. References @@ -146,7 +158,6 @@ def log_decoding_FilmLightTLog(t, w=128.0, g=16.0, o=0.075): Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ diff --git a/colour/models/rgb/transfer_functions/fujifilm_flog.py b/colour/models/rgb/transfer_functions/fujifilm_flog.py index 1770a73695..25261370a0 100644 --- a/colour/models/rgb/transfer_functions/fujifilm_flog.py +++ b/colour/models/rgb/transfer_functions/fujifilm_flog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Fujifilm F-Log Log Encoding =========================== @@ -15,23 +14,33 @@ F-Log_DataSheet_E_Ver.1.0.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +from colour.hints import ( + Boolean, + FloatingOrArrayLike, + FloatingOrNDArray, + Integer, +) from colour.models.rgb.transfer_functions import full_to_legal, legal_to_full from colour.utilities import Structure, as_float, from_range_1, to_domain_1 -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - http://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 Colour Developers" +__license__ = "New BSD License - http://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" -__all__ = ['CONSTANTS_FLOG', 'log_encoding_FLog', 'log_decoding_FLog'] +__all__ = [ + "CONSTANTS_FLOG", + "log_encoding_FLog", + "log_decoding_FLog", +] -CONSTANTS_FLOG = Structure( +CONSTANTS_FLOG: Structure = Structure( cut1=0.00089, cut2=0.100537775223865, a=0.555556, @@ -39,45 +48,43 @@ c=0.344676, d=0.790453, e=8.735631, - f=0.092864) -""" -*Fujifilm F-Log* colourspace constants. - -CONSTANTS_FLOG : Structure -""" - - -def log_encoding_FLog(in_r, - bit_depth=10, - out_normalised_code_value=True, - in_reflection=True, - constants=CONSTANTS_FLOG): + f=0.092864, +) +"""*Fujifilm F-Log* colourspace constants.""" + + +def log_encoding_FLog( + in_r: FloatingOrArrayLike, + bit_depth: Integer = 10, + out_normalised_code_value: Boolean = True, + in_reflection: Boolean = True, + constants: Structure = CONSTANTS_FLOG, +) -> FloatingOrNDArray: """ - Defines the *Fujifilm F-Log* log encoding curve / opto-electronic transfer + Define the *Fujifilm F-Log* log encoding curve / opto-electronic transfer function. Parameters ---------- - in_r : numeric or array_like + in_r Linear reflection data :math`in`. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - out_normalised_code_value : bool, optional + out_normalised_code_value Whether the non-linear *Fujifilm F-Log* data :math:`out` is encoded as normalised code values. - in_reflection : bool, optional + in_reflection Whether the light level :math`in` to a camera is reflection. - constants : Structure, optional + constants *Fujifilm F-Log* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Non-linear data :math:`out`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -128,43 +135,45 @@ def log_encoding_FLog(in_r, c * np.log10(a * in_r + b) + d, ) - out_r = (out_r - if out_normalised_code_value else legal_to_full(out_r, bit_depth)) + out_r_cv = ( + out_r if out_normalised_code_value else legal_to_full(out_r, bit_depth) + ) - return as_float(from_range_1(out_r)) + return as_float(from_range_1(out_r_cv)) -def log_decoding_FLog(out_r, - bit_depth=10, - in_normalised_code_value=True, - out_reflection=True, - constants=CONSTANTS_FLOG): +def log_decoding_FLog( + out_r: FloatingOrArrayLike, + bit_depth: Integer = 10, + in_normalised_code_value: Boolean = True, + out_reflection: Boolean = True, + constants: Structure = CONSTANTS_FLOG, +) -> FloatingOrNDArray: """ - Defines the *Fujifilm F-Log* log decoding curve / electro-optical transfer + Define the *Fujifilm F-Log* log decoding curve / electro-optical transfer function. Parameters ---------- - out_r : numeric or array_like + out_r Non-linear data :math:`out`. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - in_normalised_code_value : bool, optional + in_normalised_code_value Whether the non-linear *Fujifilm F-Log* data :math:`out` is encoded as normalised code values. - out_reflection : bool, optional + out_reflection Whether the light level :math`in` to a camera is reflection. - constants : Structure, optional + constants *Fujifilm F-Log* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear reflection data :math`in`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -189,8 +198,9 @@ def log_decoding_FLog(out_r, out_r = to_domain_1(out_r) - out_r = (out_r - if in_normalised_code_value else full_to_legal(out_r, bit_depth)) + out_r = ( + out_r if in_normalised_code_value else full_to_legal(out_r, bit_depth) + ) cut2 = constants.cut2 a = constants.a diff --git a/colour/models/rgb/transfer_functions/gamma.py b/colour/models/rgb/transfer_functions/gamma.py index f32e4797a2..21e17c76b5 100644 --- a/colour/models/rgb/transfer_functions/gamma.py +++ b/colour/models/rgb/transfer_functions/gamma.py @@ -1,43 +1,55 @@ -# -*- coding: utf-8 -*- """ Gamma Colour Component Transfer Function ======================================== -Defines gamma encoding / decoding colour component transfer function related -objects: +Defines the gamma encoding / decoding colour component transfer function +related objects: - :func:`colour.gamma_function` """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.algebra import spow -from colour.utilities import as_float_array, as_float - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['gamma_function'] - - -def gamma_function(a, exponent=1, negative_number_handling='Indeterminate'): +from colour.hints import ( + FloatingOrArrayLike, + FloatingOrNDArray, + Literal, + Union, +) +from colour.utilities import as_float_array, as_float, validate_method + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "gamma_function", +] + + +def gamma_function( + a: FloatingOrArrayLike, + exponent: FloatingOrArrayLike = 1, + negative_number_handling: Union[ + Literal["Clamp", "Indeterminate", "Mirror", "Preserve"], str + ] = "Indeterminate", +) -> FloatingOrNDArray: """ - Defines a typical gamma encoding / decoding function. + Define a typical gamma encoding / decoding function. Parameters ---------- - a : numeric or array_like + a Array to encode / decode. - exponent : numeric or array_like, optional + exponent Encoding / decoding exponent. - negative_number_handling : unicode, optional - **{'Indeterminate', 'Mirror', 'Preserve', 'Clamp'}**, + negative_number_handling Defines the behaviour for ``a`` negative numbers and / or the definition return value: @@ -53,14 +65,9 @@ def gamma_function(a, exponent=1, negative_number_handling='Indeterminate'): Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Encoded / decoded array. - Raises - ------ - ValueError - If the negative number handling method is not defined. - Examples -------- >>> gamma_function(0.18, 2.2) # doctest: +ELLIPSIS @@ -79,19 +86,17 @@ def gamma_function(a, exponent=1, negative_number_handling='Indeterminate'): a = as_float_array(a) exponent = as_float_array(exponent) - - negative_number_handling = negative_number_handling.lower() - if negative_number_handling == 'indeterminate': - return as_float(a ** exponent) - elif negative_number_handling == 'mirror': + negative_number_handling = validate_method( + negative_number_handling, + ["Indeterminate", "Mirror", "Preserve", "Clamp"], + '"{0}" negative number handling is invalid, it must be one of {1}!', + ) + + if negative_number_handling == "indeterminate": + return as_float(a**exponent) + elif negative_number_handling == "mirror": return spow(a, exponent) - elif negative_number_handling == 'preserve': - return as_float(np.where(a <= 0, a, a ** exponent)) - elif negative_number_handling == 'clamp': - return as_float(np.where(a <= 0, 0, a ** exponent)) - else: - raise ValueError( - 'Undefined negative number handling method: "{0}", must be one of ' - '"{1}".'.format( - negative_number_handling, - ', '.join(['Indeterminate', 'Mirror', 'Preserve', 'Clamp']))) + elif negative_number_handling == "preserve": + return as_float(np.where(a <= 0, a, a**exponent)) + else: # negative_number_handling == 'clamp': + return as_float(np.where(a <= 0, 0, a**exponent)) diff --git a/colour/models/rgb/transfer_functions/gopro.py b/colour/models/rgb/transfer_functions/gopro.py index 1b8bae1950..9754d808cd 100644 --- a/colour/models/rgb/transfer_functions/gopro.py +++ b/colour/models/rgb/transfer_functions/gopro.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ GoPro Encoding ============== @@ -16,40 +15,43 @@ aces_ocio/colorspaces/gopro.py """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.utilities import from_range_1, to_domain_1 +from colour.hints import FloatingOrArrayLike, FloatingOrNDArray +from colour.utilities import as_float, from_range_1, to_domain_1 -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['log_encoding_Protune', 'log_decoding_Protune'] +__all__ = [ + "log_encoding_Protune", + "log_decoding_Protune", +] -def log_encoding_Protune(x): +def log_encoding_Protune(x: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the *Protune* log encoding curve / opto-electronic transfer + Define the *Protune* log encoding curve / opto-electronic transfer function. Parameters ---------- - x : numeric or array_like + x Linear data :math:`x`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Non-linear data :math:`y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -74,29 +76,28 @@ def log_encoding_Protune(x): x = to_domain_1(x) - y = np.log(x * 112 + 1) / np.log(113) + y = np.log1p(x * 112) / np.log(113) - return from_range_1(y) + return as_float(from_range_1(y)) -def log_decoding_Protune(y): +def log_decoding_Protune(y: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the *Protune* log decoding curve / electro-optical transfer + Define the *Protune* log decoding curve / electro-optical transfer function. Parameters ---------- - y : numeric or array_like + y Non-linear data :math:`y`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`x`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -121,6 +122,6 @@ def log_decoding_Protune(y): y = to_domain_1(y) - x = (113 ** y - 1) / 112 + x = (113**y - 1) / 112 - return from_range_1(x) + return as_float(from_range_1(x)) diff --git a/colour/models/rgb/transfer_functions/itur_bt_1886.py b/colour/models/rgb/transfer_functions/itur_bt_1886.py index fe9253d17d..51de879d46 100644 --- a/colour/models/rgb/transfer_functions/itur_bt_1886.py +++ b/colour/models/rgb/transfer_functions/itur_bt_1886.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ ITU-R BT.1886 ============= -Defines *Recommendation ITU-R BT.1886* electro-optical transfer function (EOTF -/ EOCF) and its inverse: +Defines the *Recommendation ITU-R BT.1886* electro-optical transfer function +(EOTF) and its inverse: - :func:`colour.models.eotf_inverse_BT1886` - :func:`colour.models.eotf_BT1886` @@ -19,45 +18,50 @@ R-REC-BT.1886-0-201103-I!!PDF-E.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.utilities import from_range_1, to_domain_1 +from colour.hints import Floating, FloatingOrArrayLike, FloatingOrNDArray +from colour.utilities import as_float, from_range_1, to_domain_1 -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['eotf_inverse_BT1886', 'eotf_BT1886'] +__all__ = [ + "eotf_inverse_BT1886", + "eotf_BT1886", +] -def eotf_inverse_BT1886(L, L_B=0, L_W=1): +def eotf_inverse_BT1886( + L: FloatingOrArrayLike, L_B: Floating = 0, L_W: Floating = 1 +) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.1886* inverse electro-optical transfer - function (EOTF / EOCF). + Define *Recommendation ITU-R BT.1886* inverse electro-optical transfer + function (EOTF). Parameters ---------- - L : numeric or array_like + L Screen luminance in :math:`cd/m^2`. - L_B : numeric, optional + L_B Screen luminance for black. - L_W : numeric, optional + L_W Screen luminance for white. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Input video signal level (normalised, black at :math:`V = 0`, to white at :math:`V = 1`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -85,41 +89,42 @@ def eotf_inverse_BT1886(L, L_B=0, L_W=1): gamma = 2.40 gamma_d = 1 / gamma - n = L_W ** gamma_d - L_B ** gamma_d - a = n ** gamma - b = L_B ** gamma_d / n + n = L_W**gamma_d - L_B**gamma_d + a = n**gamma + b = L_B**gamma_d / n V = (L / a) ** gamma_d - b - return from_range_1(V) + return as_float(from_range_1(V)) -def eotf_BT1886(V, L_B=0, L_W=1): +def eotf_BT1886( + V: FloatingOrArrayLike, L_B: Floating = 0, L_W: Floating = 1 +) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.1886* electro-optical transfer function - (EOTF / EOCF). + Define *Recommendation ITU-R BT.1886* electro-optical transfer function + (EOTF). Parameters ---------- - V : numeric or array_like + V Input video signal level (normalised, black at :math:`V = 0`, to white at :math:`V = 1`. For content mastered per *Recommendation ITU-R BT.709*, 10-bit digital code values :math:`D` map into values of :math:`V` per the following equation: :math:`V = (D-64)/876` - L_B : numeric, optional + L_B Screen luminance for black. - L_W : numeric, optional + L_W Screen luminance for white. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Screen luminance in :math:`cd/m^2`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -147,9 +152,9 @@ def eotf_BT1886(V, L_B=0, L_W=1): gamma = 2.40 gamma_d = 1 / gamma - n = L_W ** gamma_d - L_B ** gamma_d - a = n ** gamma - b = L_B ** gamma_d / n + n = L_W**gamma_d - L_B**gamma_d + a = n**gamma + b = L_B**gamma_d / n L = a * np.maximum(V + b, 0) ** gamma - return from_range_1(L) + return as_float(from_range_1(L)) diff --git a/colour/models/rgb/transfer_functions/itur_bt_2020.py b/colour/models/rgb/transfer_functions/itur_bt_2020.py index b46a7fd64c..a2bd20fc3a 100644 --- a/colour/models/rgb/transfer_functions/itur_bt_2020.py +++ b/colour/models/rgb/transfer_functions/itur_bt_2020.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ ITU-R BT.2020 ============= -Defines *ITU-R BT.2020* opto-electrical transfer function (OETF / OECF) and -electro-optical transfer function (EOTF / EOCF): +Defines the *ITU-R BT.2020* opto-electrical transfer function (OETF) and +electro-optical transfer function (EOTF): - :func:`colour.models.eotf_inverse_BT2020` - :func:`colour.models.eotf_BT2020` @@ -19,37 +18,43 @@ R-REC-BT.2020-2-201510-I!!PDF-E.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.algebra import spow -from colour.utilities import (Structure, as_float, domain_range_scale, - from_range_1, to_domain_1) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import Boolean, FloatingOrArrayLike, FloatingOrNDArray +from colour.utilities import ( + Structure, + as_float, + domain_range_scale, + from_range_1, + to_domain_1, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'CONSTANTS_BT2020', 'CONSTANTS_BT2020_PRECISE', 'eotf_inverse_BT2020', - 'eotf_BT2020' + "CONSTANTS_BT2020", + "CONSTANTS_BT2020_PRECISE", + "eotf_inverse_BT2020", + "eotf_BT2020", ] -CONSTANTS_BT2020 = Structure( +CONSTANTS_BT2020: Structure = Structure( alpha=lambda x: 1.0993 if x else 1.099, - beta=lambda x: 0.0181 if x else 0.018) -""" -*BT.2020* colourspace constants. - -CONSTANTS_BT2020 : Structure -""" + beta=lambda x: 0.0181 if x else 0.018, +) +"""*BT.2020* colourspace constants.""" -CONSTANTS_BT2020_PRECISE = Structure( - alpha=lambda x: 1.09929682680944, beta=lambda x: 0.018053968510807) +CONSTANTS_BT2020_PRECISE: Structure = Structure( + alpha=lambda x: 1.09929682680944, beta=lambda x: 0.018053968510807 +) """ *BT.2020* colourspace constants at double precision to connect the two curve segments smoothly. @@ -57,36 +62,36 @@ References ---------- :cite:`InternationalTelecommunicationUnion2015h` - -CONSTANTS_BT2020_PRECISE : Structure """ -def eotf_inverse_BT2020(E, is_12_bits_system=False, - constants=CONSTANTS_BT2020): +def eotf_inverse_BT2020( + E: FloatingOrArrayLike, + is_12_bits_system: Boolean = False, + constants: Structure = CONSTANTS_BT2020, +) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2020* inverse electro-optical transfer - function (EOTF / EOCF). + Define *Recommendation ITU-R BT.2020* inverse electro-optical transfer + function (EOTF). Parameters ---------- - E : numeric or array_like + E Voltage :math:`E` normalised by the reference white level and proportional to the implicit light intensity that would be detected with a reference camera colour channel R, G, B. - is_12_bits_system : bool, optional + is_12_bits_system *BT.709* *alpha* and *beta* constants are used if system is not 12-bit. - constants : Structure, optional + constants *Recommendation ITU-R BT.2020* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Resulting non-linear signal :math:`E'`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -119,28 +124,31 @@ def eotf_inverse_BT2020(E, is_12_bits_system=False, return as_float(from_range_1(E_p)) -def eotf_BT2020(E_p, is_12_bits_system=False, constants=CONSTANTS_BT2020): +def eotf_BT2020( + E_p: FloatingOrArrayLike, + is_12_bits_system: Boolean = False, + constants: Structure = CONSTANTS_BT2020, +) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2020* electro-optical transfer function - (EOTF / EOCF). + Define *Recommendation ITU-R BT.2020* electro-optical transfer function + (EOTF). Parameters ---------- - E_p : numeric or array_like + E_p Non-linear signal :math:`E'`. - is_12_bits_system : bool, optional + is_12_bits_system *BT.709* *alpha* and *beta* constants are used if system is not 12-bit. - constants : Structure, optional + constants *Recommendation ITU-R BT.2020* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Resulting voltage :math:`E`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -168,7 +176,7 @@ def eotf_BT2020(E_p, is_12_bits_system=False, constants=CONSTANTS_BT2020): a = constants.alpha(is_12_bits_system) b = constants.beta(is_12_bits_system) - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): E = np.where( E_p < eotf_inverse_BT2020(b), E_p / 4.5, diff --git a/colour/models/rgb/transfer_functions/itur_bt_2100.py b/colour/models/rgb/transfer_functions/itur_bt_2100.py index 6aac9bf052..525f01b755 100644 --- a/colour/models/rgb/transfer_functions/itur_bt_2100.py +++ b/colour/models/rgb/transfer_functions/itur_bt_2100.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- """ ITU-R BT.2100 ============= -Defines *ITU-R BT.2100* opto-electrical transfer functions (OETF / OECF), +Defines the *ITU-R BT.2100* opto-electrical transfer functions (OETF), opto-optical transfer functions (OOTF / OOCF) and electro-optical transfer -functions (EOTF / EOCF) and their inverse: +functions (EOTF) and their inverse: - :func:`colour.models.oetf_PQ_BT2100` - :func:`colour.models.oetf_inverse_PQ_BT2100` @@ -52,75 +51,121 @@ R-REC-BT.2100-2-201807-I!!PDF-E.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.algebra import spow +from colour.hints import ( + Floating, + FloatingOrArrayLike, + FloatingOrNDArray, + Literal, + NDArray, + Optional, + Union, +) from colour.models.rgb.transfer_functions import ( - eotf_BT1886, eotf_ST2084, eotf_inverse_BT1886, oetf_ARIBSTDB67, oetf_BT709, - eotf_inverse_ST2084, oetf_inverse_ARIBSTDB67, oetf_inverse_BT709) + eotf_BT1886, + eotf_ST2084, + eotf_inverse_BT1886, + oetf_ARIBSTDB67, + oetf_BT709, + eotf_inverse_ST2084, + oetf_inverse_ARIBSTDB67, + oetf_inverse_BT709, +) from colour.models.rgb.transfer_functions.arib_std_b67 import ( - CONSTANTS_ARIBSTDB67) + CONSTANTS_ARIBSTDB67, +) from colour.utilities import ( - CaseInsensitiveMapping, Structure, as_float_array, as_float, filter_kwargs, - from_range_1, to_domain_1, tsplit, tstack, usage_warning) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + CaseInsensitiveMapping, + Structure, + as_float, + as_float_array, + as_float_scalar, + domain_range_scale, + filter_kwargs, + from_range_1, + optional, + to_domain_1, + tsplit, + tstack, + usage_warning, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'oetf_PQ_BT2100', 'oetf_inverse_PQ_BT2100', 'eotf_PQ_BT2100', - 'eotf_inverse_PQ_BT2100', 'ootf_PQ_BT2100', 'ootf_inverse_PQ_BT2100', - 'WEIGHTS_BT2100_HLG', 'CONSTANTS_BT2100_HLG', 'gamma_function_HLG_BT2100', - 'oetf_HLG_BT2100', 'oetf_inverse_HLG_BT2100', - 'black_level_lift_HLG_BT2100', 'eotf_HLG_BT2100_1', 'eotf_HLG_BT2100_2', - 'BT2100_HLG_EOTF_METHODS', 'eotf_HLG_BT2100', 'eotf_inverse_HLG_BT2100_1', - 'eotf_inverse_HLG_BT2100_2', 'BT2100_HLG_EOTF_INVERSE_METHODS', - 'eotf_inverse_HLG_BT2100', 'ootf_HLG_BT2100_1', 'ootf_HLG_BT2100_2', - 'BT2100_HLG_OOTF_METHODS', 'ootf_HLG_BT2100', 'ootf_inverse_HLG_BT2100_1', - 'ootf_inverse_HLG_BT2100_2', 'BT2100_HLG_OOTF_INVERSE_METHODS', - 'ootf_inverse_HLG_BT2100' + "oetf_PQ_BT2100", + "oetf_inverse_PQ_BT2100", + "eotf_PQ_BT2100", + "eotf_inverse_PQ_BT2100", + "ootf_PQ_BT2100", + "ootf_inverse_PQ_BT2100", + "WEIGHTS_BT2100_HLG", + "CONSTANTS_BT2100_HLG", + "gamma_function_HLG_BT2100", + "oetf_HLG_BT2100", + "oetf_inverse_HLG_BT2100", + "black_level_lift_HLG_BT2100", + "eotf_HLG_BT2100_1", + "eotf_HLG_BT2100_2", + "BT2100_HLG_EOTF_METHODS", + "eotf_HLG_BT2100", + "eotf_inverse_HLG_BT2100_1", + "eotf_inverse_HLG_BT2100_2", + "BT2100_HLG_EOTF_INVERSE_METHODS", + "eotf_inverse_HLG_BT2100", + "ootf_HLG_BT2100_1", + "ootf_HLG_BT2100_2", + "BT2100_HLG_OOTF_METHODS", + "ootf_HLG_BT2100", + "ootf_inverse_HLG_BT2100_1", + "ootf_inverse_HLG_BT2100_2", + "BT2100_HLG_OOTF_INVERSE_METHODS", + "ootf_inverse_HLG_BT2100", ] -def oetf_PQ_BT2100(E): +def oetf_PQ_BT2100(E: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference PQ* opto-electrical - transfer function (OETF / OECF). + Define *Recommendation ITU-R BT.2100* *Reference PQ* opto-electrical + transfer function (OETF). The OETF maps relative scene linear light into the non-linear *PQ* signal value. Parameters ---------- - E : numeric or array_like + E :math:`E = {R_S, G_S, B_S; Y_S; or I_S}` is the signal determined by scene light and scaled by camera exposure. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` :math:`E'` is the resulting non-linear signal (:math:`R'`, :math:`G'`, :math:`B'`). Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ - | ``E`` | [0, 1] | [0, 1] | + | ``E`` | ``UN`` | ``UN`` | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ - | ``E_p`` | [0, 1] | [0, 1] | + | ``E_p`` | ``UN`` | ``UN`` | +------------+-----------------------+---------------+ References @@ -136,36 +181,35 @@ def oetf_PQ_BT2100(E): return eotf_inverse_ST2084(ootf_PQ_BT2100(E), 10000) -def oetf_inverse_PQ_BT2100(E_p): +def oetf_inverse_PQ_BT2100(E_p: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference PQ* inverse - opto-electrical transfer function (OETF / OECF). + Define *Recommendation ITU-R BT.2100* *Reference PQ* inverse + opto-electrical transfer function (OETF). Parameters ---------- - E_p : numeric or array_like + E_p :math:`E'` is the resulting non-linear signal (:math:`R'`, :math:`G'`, :math:`B'`). Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` :math:`E = {R_S, G_S, B_S; Y_S; or I_S}` is the signal determined by scene light and scaled by camera exposure. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ - | ``E_p`` | [0, 1] | [0, 1] | + | ``E_p`` | ``UN`` | ``UN`` | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ - | ``E`` | [0, 1] | [0, 1] | + | ``E`` | ``UN`` | ``UN`` | +------------+-----------------------+---------------+ References @@ -181,39 +225,38 @@ def oetf_inverse_PQ_BT2100(E_p): return ootf_inverse_PQ_BT2100(eotf_ST2084(E_p, 10000)) -def eotf_PQ_BT2100(E_p): +def eotf_PQ_BT2100(E_p: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference PQ* electro-optical - transfer function (EOTF / EOCF). + Define *Recommendation ITU-R BT.2100* *Reference PQ* electro-optical + transfer function (EOTF). The EOTF maps the non-linear *PQ* signal into display light. Parameters ---------- - E_p : numeric or array_like + E_p :math:`E'` denotes a non-linear colour value :math:`{R', G', B'}` or :math:`{L', M', S'}` in *PQ* space [0, 1]. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` :math:`F_D` is the luminance of a displayed linear component :math:`{R_D, G_D, B_D}` or :math:`Y_D` or :math:`I_D`, in :math:`cd/m^2`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ - | ``E_p`` | [0, 1] | [0, 1] | + | ``E_p`` | ``UN`` | ``UN`` | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ - | ``F_D`` | [0, 1] | [0, 1] | + | ``F_D`` | ``UN`` | ``UN`` | +------------+-----------------------+---------------+ References @@ -229,37 +272,36 @@ def eotf_PQ_BT2100(E_p): return eotf_ST2084(E_p, 10000) -def eotf_inverse_PQ_BT2100(F_D): +def eotf_inverse_PQ_BT2100(F_D: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference PQ* inverse - electro-optical transfer function (EOTF / EOCF). + Define *Recommendation ITU-R BT.2100* *Reference PQ* inverse + electro-optical transfer function (EOTF). Parameters ---------- - F_D : numeric or array_like + F_D :math:`F_D` is the luminance of a displayed linear component :math:`{R_D, G_D, B_D}` or :math:`Y_D` or :math:`I_D`, in :math:`cd/m^2`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` :math:`E'` denotes a non-linear colour value :math:`{R', G', B'}` or :math:`{L', M', S'}` in *PQ* space [0, 1]. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ - | ``F_D`` | [0, 1] | [0, 1] | + | ``F_D`` | ``UN`` | ``UN`` | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ - | ``E_p`` | [0, 1] | [0, 1] | + | ``E_p`` | ``UN`` | ``UN`` | +------------+-----------------------+---------------+ References @@ -275,38 +317,37 @@ def eotf_inverse_PQ_BT2100(F_D): return eotf_inverse_ST2084(F_D, 10000) -def ootf_PQ_BT2100(E): +def ootf_PQ_BT2100(E: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference PQ* opto-optical transfer + Define *Recommendation ITU-R BT.2100* *Reference PQ* opto-optical transfer function (OOTF / OOCF). The OOTF maps relative scene linear light to display linear light. Parameters ---------- - E : numeric or array_like + E :math:`E = {R_S, G_S, B_S; Y_S; or I_S}` is the signal determined by scene light and scaled by camera exposure. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` :math:`F_D` is the luminance of a displayed linear component (:math:`R_D`, :math:`G_D`, :math:`B_D`; :math:`Y_D`; or :math:`I_D`). Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ - | ``E`` | [0, 1] | [0, 1] | + | ``E`` | ``UN`` | ``UN`` | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ - | ``F_D`` | [0, 1] | [0, 1] | + | ``F_D`` | ``UN`` | ``UN`` | +------------+-----------------------+---------------+ References @@ -321,39 +362,39 @@ def ootf_PQ_BT2100(E): E = as_float_array(E) - return 100 * eotf_BT1886(oetf_BT709(59.5208 * E)) + with domain_range_scale("ignore"): + return 100 * eotf_BT1886(oetf_BT709(59.5208 * E)) -def ootf_inverse_PQ_BT2100(F_D): +def ootf_inverse_PQ_BT2100(F_D: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference PQ* inverse opto-optical + Define *Recommendation ITU-R BT.2100* *Reference PQ* inverse opto-optical transfer function (OOTF / OOCF). Parameters ---------- - F_D : numeric or array_like + F_D :math:`F_D` is the luminance of a displayed linear component (:math:`R_D`, :math:`G_D`, :math:`B_D`; :math:`Y_D`; or :math:`I_D`). Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` :math:`E = {R_S, G_S, B_S; Y_S; or I_S}` is the signal determined by scene light and scaled by camera exposure. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ - | ``F_D`` | [0, 1] | [0, 1] | + | ``F_D`` | ``UN`` | ``UN`` | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ - | ``E`` | [0, 1] | [0, 1] | + | ``E`` | ``UN`` | ``UN`` | +------------+-----------------------+---------------+ References @@ -368,20 +409,18 @@ def ootf_inverse_PQ_BT2100(F_D): F_D = as_float_array(F_D) - return oetf_inverse_BT709(eotf_inverse_BT1886(F_D / 100)) / 59.5208 + with domain_range_scale("ignore"): + return oetf_inverse_BT709(eotf_inverse_BT1886(F_D / 100)) / 59.5208 -WEIGHTS_BT2100_HLG = np.array([0.2627, 0.6780, 0.0593]) -""" -Luminance weights for *Recommendation ITU-R BT.2100* *Reference HLG*. - -WEIGHTS_BT2100_HLG : ndarray -""" +WEIGHTS_BT2100_HLG: NDArray = np.array([0.2627, 0.6780, 0.0593]) +"""Luminance weights for *Recommendation ITU-R BT.2100* *Reference HLG*.""" -CONSTANTS_BT2100_HLG = Structure( +CONSTANTS_BT2100_HLG: Structure = Structure( a=CONSTANTS_ARIBSTDB67.a, b=1 - 4 * CONSTANTS_ARIBSTDB67.a, - c=0.5 - CONSTANTS_ARIBSTDB67.a * np.log(4 * CONSTANTS_ARIBSTDB67.a)) + c=0.5 - CONSTANTS_ARIBSTDB67.a * np.log(4 * CONSTANTS_ARIBSTDB67.a), +) """ *Recommendation ITU-R BT.2100* *Reference HLG* constants expressed in their analytical form in contrast to the *ARIB STD-B67 (Hybrid Log-Gamma)* numerical @@ -390,25 +429,23 @@ def ootf_inverse_PQ_BT2100(F_D): References ---------- :cite:`InternationalTelecommunicationUnion2017` - -CONSTANTS_BT2100_HLG : Structure """ -def gamma_function_HLG_BT2100(L_W=1000): +def gamma_function_HLG_BT2100(L_W: Floating = 1000) -> Floating: """ - Returns the *Reference HLG* system gamma value for given display nominal + Return the *Reference HLG* system gamma value for given display nominal peak luminance. Parameters ---------- - L_W : numeric, optional + L_W :math:`L_W` is nominal peak luminance of the display in :math:`cd/m^2` for achromatic pixels. Returns ------- - numeric + :class:`numpy.floating` *Reference HLG* system gamma value. Examples @@ -423,34 +460,35 @@ def gamma_function_HLG_BT2100(L_W=1000): gamma = 1.2 + 0.42 * np.log10(L_W / 1000) - return gamma + return as_float_scalar(gamma) -def oetf_HLG_BT2100(E, constants=CONSTANTS_BT2100_HLG): +def oetf_HLG_BT2100( + E: FloatingOrArrayLike, constants: Structure = CONSTANTS_BT2100_HLG +) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference HLG* opto-electrical - transfer function (OETF / OECF). + Define *Recommendation ITU-R BT.2100* *Reference HLG* opto-electrical + transfer function (OETF). The OETF maps relative scene linear light into the non-linear *HLG* signal value. Parameters ---------- - E : numeric or array_like + E :math:`E` is the signal for each colour component :math:`{R_S, G_S, B_S}` proportional to scene linear light and scaled by camera exposure. - constants : Structure, optional + constants *Recommendation ITU-R BT.2100* *Reference HLG* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` :math:`E'` is the resulting non-linear signal :math:`{R', G', B'}`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -473,31 +511,34 @@ def oetf_HLG_BT2100(E, constants=CONSTANTS_BT2100_HLG): 0.2121320... """ + E = as_float_array(E) + return oetf_ARIBSTDB67(12 * E, constants=constants) -def oetf_inverse_HLG_BT2100(E_p, constants=CONSTANTS_BT2100_HLG): +def oetf_inverse_HLG_BT2100( + E_p: FloatingOrArrayLike, constants: Structure = CONSTANTS_BT2100_HLG +) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference HLG* inverse - opto-electrical transfer function (OETF / OECF). + Define *Recommendation ITU-R BT.2100* *Reference HLG* inverse + opto-electrical transfer function (OETF). Parameters ---------- - E_p : numeric or array_like + E_p :math:`E'` is the resulting non-linear signal :math:`{R', G', B'}`. - constants : Structure, optional + constants *Recommendation ITU-R BT.2100* *Reference HLG* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` :math:`E` is the signal for each colour component :math:`{R_S, G_S, B_S}` proportional to scene linear light and scaled by camera exposure. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -523,26 +564,28 @@ def oetf_inverse_HLG_BT2100(E_p, constants=CONSTANTS_BT2100_HLG): return oetf_inverse_ARIBSTDB67(E_p, constants=constants) / 12 -def black_level_lift_HLG_BT2100(L_B=0, L_W=1000, gamma=None): +def black_level_lift_HLG_BT2100( + L_B: Floating = 0, L_W: Floating = 1000, gamma: Optional[Floating] = None +) -> Floating: """ - Returns the *Reference HLG* black level lift :math:`\\Beta` for given + Return the *Reference HLG* black level lift :math:`\\beta` for given display luminance for black, nominal peak luminance and system gamma value. Parameters ---------- - L_B : numeric, optional + L_B :math:`L_B` is the display luminance for black in :math:`cd/m^2`. - L_W : numeric, optional + L_W :math:`L_W` is nominal peak luminance of the display in :math:`cd/m^2` for achromatic pixels. - gamma : numeric, optional + gamma System gamma value, 1.2 at the nominal display peak luminance of :math:`1000 cd/m^2`. Returns ------- - numeric - *Reference HLG* black level lift :math:`\\Beta`. + :class:`numpy.floating` + *Reference HLG* black level lift :math:`\\beta`. Examples -------- @@ -556,51 +599,51 @@ def black_level_lift_HLG_BT2100(L_B=0, L_W=1000, gamma=None): 0.0283691... """ - if gamma is None: - gamma = gamma_function_HLG_BT2100(L_W) + gamma = optional(gamma, gamma_function_HLG_BT2100(L_W)) beta = np.sqrt(3 * spow((L_B / L_W), 1 / gamma)) - return beta + return as_float_scalar(beta) -def eotf_HLG_BT2100_1(E_p, - L_B=0, - L_W=1000, - gamma=None, - constants=CONSTANTS_BT2100_HLG): +def eotf_HLG_BT2100_1( + E_p: FloatingOrArrayLike, + L_B: Floating = 0, + L_W: Floating = 1000, + gamma: Optional[Floating] = None, + constants: Structure = CONSTANTS_BT2100_HLG, +) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference HLG* electro-optical - transfer function (EOTF / EOCF) as given in *ITU-R BT.2100-1*. + Define *Recommendation ITU-R BT.2100* *Reference HLG* electro-optical + transfer function (EOTF) as given in *ITU-R BT.2100-1*. The EOTF maps the non-linear *HLG* signal into display light. Parameters ---------- - E_p : numeric or array_like + E_p :math:`E'` is the non-linear signal :math:`{R', G', B'}` as defined for the OETF. - L_B : numeric, optional + L_B :math:`L_B` is the display luminance for black in :math:`cd/m^2`. - L_W : numeric, optional + L_W :math:`L_W` is nominal peak luminance of the display in :math:`cd/m^2` for achromatic pixels. - gamma : numeric, optional + gamma System gamma value, 1.2 at the nominal display peak luminance of :math:`1000 cd/m^2`. - constants : Structure, optional + constants *Recommendation ITU-R BT.2100* *Reference HLG* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Luminance :math:`F_D` of a displayed linear component :math:`{R_D, G_D, B_D}` or :math:`Y_D` or :math:`I_D`, in :math:`cd/m^2`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -626,48 +669,49 @@ def eotf_HLG_BT2100_1(E_p, """ return ootf_HLG_BT2100_1( - oetf_inverse_ARIBSTDB67(E_p, constants=constants) / 12, L_B, L_W, - gamma) + oetf_inverse_ARIBSTDB67(E_p, constants=constants) / 12, L_B, L_W, gamma + ) -def eotf_HLG_BT2100_2(E_p, - L_B=0, - L_W=1000, - gamma=None, - constants=CONSTANTS_BT2100_HLG): +def eotf_HLG_BT2100_2( + E_p: FloatingOrArrayLike, + L_B: Floating = 0, + L_W: Floating = 1000, + gamma: Optional[Floating] = None, + constants: Structure = CONSTANTS_BT2100_HLG, +) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference HLG* electro-optical - transfer function (EOTF / EOCF) as given in *ITU-R BT.2100-2* with the + Define *Recommendation ITU-R BT.2100* *Reference HLG* electro-optical + transfer function (EOTF) as given in *ITU-R BT.2100-2* with the modified black level behaviour. The EOTF maps the non-linear *HLG* signal into display light. Parameters ---------- - E_p : numeric or array_like + E_p :math:`E'` is the non-linear signal :math:`{R', G', B'}` as defined for the *HLG Reference* OETF. - L_B : numeric, optional + L_B :math:`L_B` is the display luminance for black in :math:`cd/m^2`. - L_W : numeric, optional + L_W :math:`L_W` is nominal peak luminance of the display in :math:`cd/m^2` for achromatic pixels. - gamma : numeric, optional + gamma System gamma value, 1.2 at the nominal display peak luminance of :math:`1000 cd/m^2`. - constants : Structure, optional + constants *Recommendation ITU-R BT.2100* *Reference HLG* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Luminance :math:`F_D` of a displayed linear component :math:`{R_D, G_D, B_D}` or :math:`Y_D` or :math:`I_D`, in :math:`cd/m^2`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -692,72 +736,78 @@ def eotf_HLG_BT2100_2(E_p, 7.3321975... """ + E_p = as_float_array(E_p) + beta = black_level_lift_HLG_BT2100(L_B, L_W, gamma) return ootf_HLG_BT2100_2( - oetf_inverse_ARIBSTDB67( - (1 - beta) * E_p + beta, constants=constants) / 12, L_W, gamma) + oetf_inverse_ARIBSTDB67((1 - beta) * E_p + beta, constants=constants) + / 12, + L_W, + gamma, + ) -BT2100_HLG_EOTF_METHODS = CaseInsensitiveMapping({ - 'ITU-R BT.2100-1': eotf_HLG_BT2100_1, - 'ITU-R BT.2100-2': eotf_HLG_BT2100_2, -}) +BT2100_HLG_EOTF_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "ITU-R BT.2100-1": eotf_HLG_BT2100_1, + "ITU-R BT.2100-2": eotf_HLG_BT2100_2, + } +) BT2100_HLG_EOTF_METHODS.__doc__ = """ Supported *Recommendation ITU-R BT.2100* *Reference HLG* electro-optical -transfer function (EOTF / EOCF). +transfer function (EOTF). References ---------- :cite:`Borer2017a`, :cite:`InternationalTelecommunicationUnion2017`, :cite:`InternationalTelecommunicationUnion2018` - -BT2100_HLG_EOTF_METHODS : CaseInsensitiveMapping - **{'ITU-R BT.2100-1', 'ITU-R BT.2100-2'}** """ -def eotf_HLG_BT2100(E_p, - L_B=0, - L_W=1000, - gamma=None, - constants=CONSTANTS_BT2100_HLG, - method='ITU-R BT.2100-2'): +def eotf_HLG_BT2100( + E_p: FloatingOrArrayLike, + L_B: Floating = 0, + L_W: Floating = 1000, + gamma: Optional[Floating] = None, + constants: Structure = CONSTANTS_BT2100_HLG, + method: Union[ + Literal["ITU-R BT.2100-1", "ITU-R BT.2100-2"], str + ] = "ITU-R BT.2100-2", +) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference HLG* electro-optical - transfer function (EOTF / EOCF). + Define *Recommendation ITU-R BT.2100* *Reference HLG* electro-optical + transfer function (EOTF). The EOTF maps the non-linear *HLG* signal into display light. Parameters ---------- - E_p : numeric or array_like + E_p :math:`E'` denotes a non-linear colour value :math:`{R', G', B'}` or :math:`{L', M', S'}` in *HLG* space. - L_B : numeric, optional + L_B :math:`L_B` is the display luminance for black in :math:`cd/m^2`. - L_W : numeric, optional + L_W :math:`L_W` is nominal peak luminance of the display in :math:`cd/m^2` for achromatic pixels. - gamma : numeric, optional + gamma System gamma value, 1.2 at the nominal display peak luminance of :math:`1000 cd/m^2`. - constants : Structure, optional + constants *Recommendation ITU-R BT.2100* *Reference HLG* constants. - method : unicode, optional - **{'ITU-R BT.2100-1', 'ITU-R BT.2100-2'}**, + method Computation method. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Luminance :math:`F_D` of a displayed linear component :math:`{R_D, G_D, B_D}` or :math:`Y_D` or :math:`I_D`, in :math:`cd/m^2`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -787,45 +837,48 @@ def eotf_HLG_BT2100(E_p, 7.3321975... """ + method = validate_method(method, BT2100_HLG_EOTF_METHODS) + return BT2100_HLG_EOTF_METHODS[method](E_p, L_B, L_W, gamma, constants) -def eotf_inverse_HLG_BT2100_1(F_D, - L_B=0, - L_W=1000, - gamma=None, - constants=CONSTANTS_BT2100_HLG): +def eotf_inverse_HLG_BT2100_1( + F_D: FloatingOrArrayLike, + L_B: Floating = 0, + L_W: Floating = 1000, + gamma: Optional[Floating] = None, + constants: Structure = CONSTANTS_BT2100_HLG, +) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference HLG* inverse - electro-optical transfer function (EOTF / EOCF) as given in + Define *Recommendation ITU-R BT.2100* *Reference HLG* inverse + electro-optical transfer function (EOTF) as given in *ITU-R BT.2100-1*. Parameters ---------- - F_D : numeric or array_like + F_D Luminance :math:`F_D` of a displayed linear component :math:`{R_D, G_D, B_D}` or :math:`Y_D` or :math:`I_D`, in :math:`cd/m^2`. - L_B : numeric, optional + L_B :math:`L_B` is the display luminance for black in :math:`cd/m^2`. - L_W : numeric, optional + L_W :math:`L_W` is nominal peak luminance of the display in :math:`cd/m^2` for achromatic pixels. - gamma : numeric, optional + gamma System gamma value, 1.2 at the nominal display peak luminance of :math:`1000 cd/m^2`. - constants : Structure, optional + constants *Recommendation ITU-R BT.2100* *Reference HLG* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` :math:`E'` denotes a non-linear colour value :math:`{R', G', B'}` or :math:`{L', M', S'}` in *HLG* space. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -853,45 +906,47 @@ def eotf_inverse_HLG_BT2100_1(F_D, return oetf_ARIBSTDB67( ootf_inverse_HLG_BT2100_1(F_D, L_B, L_W, gamma) * 12, - constants=constants) + constants=constants, + ) -def eotf_inverse_HLG_BT2100_2(F_D, - L_B=0, - L_W=1000, - gamma=None, - constants=CONSTANTS_BT2100_HLG): +def eotf_inverse_HLG_BT2100_2( + F_D: FloatingOrArrayLike, + L_B: Floating = 0, + L_W: Floating = 1000, + gamma: Optional[Floating] = None, + constants: Structure = CONSTANTS_BT2100_HLG, +) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference HLG* inverse - electro-optical transfer function (EOTF / EOCF) as given in + Define *Recommendation ITU-R BT.2100* *Reference HLG* inverse + electro-optical transfer function (EOTF) as given in *ITU-R BT.2100-2* with the modified black level behaviour. Parameters ---------- - F_D : numeric or array_like + F_D Luminance :math:`F_D` of a displayed linear component :math:`{R_D, G_D, B_D}` or :math:`Y_D` or :math:`I_D`, in :math:`cd/m^2`. - L_B : numeric, optional + L_B :math:`L_B` is the display luminance for black in :math:`cd/m^2`. - L_W : numeric, optional + L_W :math:`L_W` is nominal peak luminance of the display in :math:`cd/m^2` for achromatic pixels. - gamma : numeric, optional + gamma System gamma value, 1.2 at the nominal display peak luminance of :math:`1000 cd/m^2`. - constants : Structure, optional + constants *Recommendation ITU-R BT.2100* *Reference HLG* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` :math:`E'` denotes a non-linear colour value :math:`{R', G', B'}` or :math:`{L', M', S'}` in *HLG* space. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -919,68 +974,75 @@ def eotf_inverse_HLG_BT2100_2(F_D, beta = black_level_lift_HLG_BT2100(L_B, L_W, gamma) - return (oetf_ARIBSTDB67( - ootf_inverse_HLG_BT2100_2(F_D, L_W, gamma) * 12, constants=constants) - - beta) / (1 - beta) - - -BT2100_HLG_EOTF_INVERSE_METHODS = CaseInsensitiveMapping({ - 'ITU-R BT.2100-1': eotf_inverse_HLG_BT2100_1, - 'ITU-R BT.2100-2': eotf_inverse_HLG_BT2100_2, -}) + return ( + oetf_ARIBSTDB67( + ootf_inverse_HLG_BT2100_2(F_D, L_W, gamma) * 12, + constants=constants, + ) + - beta + ) / (1 - beta) + + +BT2100_HLG_EOTF_INVERSE_METHODS: CaseInsensitiveMapping = ( + CaseInsensitiveMapping( + { + "ITU-R BT.2100-1": eotf_inverse_HLG_BT2100_1, + "ITU-R BT.2100-2": eotf_inverse_HLG_BT2100_2, + } + ) +) BT2100_HLG_EOTF_INVERSE_METHODS.__doc__ = """ Supported *Recommendation ITU-R BT.2100* *Reference HLG* inverse -electro-optical transfer function (EOTF / EOCF). +electro-optical transfer function (EOTF). References ---------- :cite:`Borer2017a`, :cite:`InternationalTelecommunicationUnion2017`, :cite:`InternationalTelecommunicationUnion2018` - -BT2100_HLG_EOTF_INVERSE_METHODS : CaseInsensitiveMapping - **{'ITU-R BT.2100-1', 'ITU-R BT.2100-2'}** """ -def eotf_inverse_HLG_BT2100(F_D, - L_B=0, - L_W=1000, - gamma=None, - constants=CONSTANTS_BT2100_HLG, - method='ITU-R BT.2100-2'): +def eotf_inverse_HLG_BT2100( + F_D: FloatingOrArrayLike, + L_B: Floating = 0, + L_W: Floating = 1000, + gamma: Optional[Floating] = None, + constants: Structure = CONSTANTS_BT2100_HLG, + method: Union[ + Literal["ITU-R BT.2100-1", "ITU-R BT.2100-2"], str + ] = "ITU-R BT.2100-2", +) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference HLG* inverse - electro-optical transfer function (EOTF / EOCF). + Define *Recommendation ITU-R BT.2100* *Reference HLG* inverse + electro-optical transfer function (EOTF). Parameters ---------- - F_D : numeric or array_like + F_D Luminance :math:`F_D` of a displayed linear component :math:`{R_D, G_D, B_D}` or :math:`Y_D` or :math:`I_D`, in :math:`cd/m^2`. - L_B : numeric, optional + L_B :math:`L_B` is the display luminance for black in :math:`cd/m^2`. - L_W : numeric, optional + L_W :math:`L_W` is nominal peak luminance of the display in :math:`cd/m^2` for achromatic pixels. - gamma : numeric, optional + gamma System gamma value, 1.2 at the nominal display peak luminance of :math:`1000 cd/m^2`. - constants : Structure, optional + constants *Recommendation ITU-R BT.2100* *Reference HLG* constants. - method : unicode, optional - **{'ITU-R BT.2100-1', 'ITU-R BT.2100-2'}**, + method Computation method. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` :math:`E'` denotes a non-linear colour value :math:`{R', G', B'}` or :math:`{L', M', S'}` in *HLG* space. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -1009,41 +1071,48 @@ def eotf_inverse_HLG_BT2100(F_D, 0.2121320... """ - return BT2100_HLG_EOTF_INVERSE_METHODS[method](F_D, L_B, L_W, gamma, - constants) + method = validate_method(method, BT2100_HLG_EOTF_INVERSE_METHODS) + return BT2100_HLG_EOTF_INVERSE_METHODS[method]( + F_D, L_B, L_W, gamma, constants + ) -def ootf_HLG_BT2100_1(E, L_B=0, L_W=1000, gamma=None): + +def ootf_HLG_BT2100_1( + E: FloatingOrArrayLike, + L_B: Floating = 0, + L_W: Floating = 1000, + gamma: Optional[Floating] = None, +) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference HLG* opto-optical + Define *Recommendation ITU-R BT.2100* *Reference HLG* opto-optical transfer function (OOTF / OOCF) as given in *ITU-R BT.2100-1*. The OOTF maps relative scene linear light to display linear light. Parameters ---------- - E : numeric or array_like + E :math:`E` is the signal for each colour component :math:`{R_S, G_S, B_S}` proportional to scene linear light and scaled by camera exposure. - L_B : numeric, optional + L_B :math:`L_B` is the display luminance for black in :math:`cd/m^2`. - L_W : numeric, optional + L_W :math:`L_W` is nominal peak luminance of the display in :math:`cd/m^2` for achromatic pixels. - gamma : numeric, optional + gamma System gamma value, 1.2 at the nominal display peak luminance of :math:`1000 cd/m^2`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` :math:`F_D` is the luminance of a displayed linear component :math:`{R_D, G_D, or B_D}`, in :math:`cd/m^2`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -1069,14 +1138,15 @@ def ootf_HLG_BT2100_1(E, L_B=0, L_W=1000, gamma=None): 63.1051034... """ - E = np.atleast_1d(to_domain_1(E)) + E = as_float_array(np.atleast_1d(to_domain_1(E))) if E.shape[-1] != 3: usage_warning( '"Recommendation ITU-R BT.2100" "Reference HLG OOTF" uses ' - 'RGB Luminance in computations and expects a vector input, thus ' - 'the given input array will be stacked to compose a vector for ' - 'internal computations but a single component will be output.') + "RGB Luminance in computations and expects a vector input, thus " + "the given input array will be stacked to compose a vector for " + "internal computations but a single component will be output." + ) R_S = G_S = B_S = E else: R_S, G_S, B_S = tsplit(E) @@ -1086,8 +1156,7 @@ def ootf_HLG_BT2100_1(E, L_B=0, L_W=1000, gamma=None): Y_S = np.sum(WEIGHTS_BT2100_HLG * tstack([R_S, G_S, B_S]), axis=-1) - if gamma is None: - gamma = gamma_function_HLG_BT2100(L_W) + gamma = optional(gamma, gamma_function_HLG_BT2100(L_W)) R_D = alpha * R_S * np.abs(Y_S) ** (gamma - 1) + beta G_D = alpha * G_S * np.abs(Y_S) ** (gamma - 1) + beta @@ -1101,35 +1170,38 @@ def ootf_HLG_BT2100_1(E, L_B=0, L_W=1000, gamma=None): return from_range_1(RGB_D) -def ootf_HLG_BT2100_2(E, L_W=1000, gamma=None): +def ootf_HLG_BT2100_2( + E: FloatingOrArrayLike, + L_W: Floating = 1000, + gamma: Optional[Floating] = None, +) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference HLG* opto-optical + Define *Recommendation ITU-R BT.2100* *Reference HLG* opto-optical transfer function (OOTF / OOCF) as given in *ITU-R BT.2100-2*. The OOTF maps relative scene linear light to display linear light. Parameters ---------- - E : numeric or array_like + E :math:`E` is the signal for each colour component :math:`{R_S, G_S, B_S}` proportional to scene linear light and scaled by camera exposure. - L_W : numeric, optional + L_W :math:`L_W` is nominal peak luminance of the display in :math:`cd/m^2` for achromatic pixels. - gamma : numeric, optional + gamma System gamma value, 1.2 at the nominal display peak luminance of :math:`1000 cd/m^2`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` :math:`F_D` is the luminance of a displayed linear component :math:`{R_D, G_D, or B_D}`, in :math:`cd/m^2`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -1152,14 +1224,15 @@ def ootf_HLG_BT2100_2(E, L_W=1000, gamma=None): 63.0957344... """ - E = np.atleast_1d(to_domain_1(E)) + E = as_float_array(np.atleast_1d(to_domain_1(E))) if E.shape[-1] != 3: usage_warning( '"Recommendation ITU-R BT.2100" "Reference HLG OOTF" uses ' - 'RGB Luminance in computations and expects a vector input, thus ' - 'the given input array will be stacked to compose a vector for ' - 'internal computations but a single component will be output.') + "RGB Luminance in computations and expects a vector input, thus " + "the given input array will be stacked to compose a vector for " + "internal computations but a single component will be output." + ) R_S = G_S = B_S = E else: R_S, G_S, B_S = tsplit(E) @@ -1168,8 +1241,7 @@ def ootf_HLG_BT2100_2(E, L_W=1000, gamma=None): Y_S = np.sum(WEIGHTS_BT2100_HLG * tstack([R_S, G_S, B_S]), axis=-1) - if gamma is None: - gamma = gamma_function_HLG_BT2100(L_W) + gamma = optional(gamma, gamma_function_HLG_BT2100(L_W)) R_D = alpha * R_S * np.abs(Y_S) ** (gamma - 1) G_D = alpha * G_S * np.abs(Y_S) ** (gamma - 1) @@ -1183,10 +1255,12 @@ def ootf_HLG_BT2100_2(E, L_W=1000, gamma=None): return from_range_1(RGB_D) -BT2100_HLG_OOTF_METHODS = CaseInsensitiveMapping({ - 'ITU-R BT.2100-1': ootf_HLG_BT2100_1, - 'ITU-R BT.2100-2': ootf_HLG_BT2100_2, -}) +BT2100_HLG_OOTF_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "ITU-R BT.2100-1": ootf_HLG_BT2100_1, + "ITU-R BT.2100-2": ootf_HLG_BT2100_2, + } +) BT2100_HLG_OOTF_METHODS.__doc__ = """ Supported *Recommendation ITU-R BT.2100* *Reference HLG* opto-optical transfer function (OOTF / OOCF). @@ -1195,46 +1269,49 @@ def ootf_HLG_BT2100_2(E, L_W=1000, gamma=None): ---------- :cite:`Borer2017a`, :cite:`InternationalTelecommunicationUnion2017`, :cite:`InternationalTelecommunicationUnion2018` - -BT2100_HLG_OOTF_METHODS : CaseInsensitiveMapping - **{'ITU-R BT.2100-1', 'ITU-R BT.2100-2'}** """ -def ootf_HLG_BT2100(E, L_B=0, L_W=1000, gamma=None, method='ITU-R BT.2100-2'): +def ootf_HLG_BT2100( + E: FloatingOrArrayLike, + L_B: Floating = 0, + L_W: Floating = 1000, + gamma: Optional[Floating] = None, + method: Union[ + Literal["ITU-R BT.2100-1", "ITU-R BT.2100-2"], str + ] = "ITU-R BT.2100-2", +) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference HLG* opto-optical + Define *Recommendation ITU-R BT.2100* *Reference HLG* opto-optical transfer function (OOTF / OOCF). The OOTF maps relative scene linear light to display linear light. Parameters ---------- - E : numeric or array_like + E :math:`E` is the signal for each colour component :math:`{R_S, G_S, B_S}` proportional to scene linear light and scaled by camera exposure. - L_B : numeric, optional + L_B :math:`L_B` is the display luminance for black in :math:`cd/m^2`. - L_W : numeric, optional + L_W :math:`L_W` is nominal peak luminance of the display in :math:`cd/m^2` for achromatic pixels. - gamma : numeric, optional + gamma System gamma value, 1.2 at the nominal display peak luminance of :math:`1000 cd/m^2`. - method : unicode, optional - **{'ITU-R BT.2100-1', 'ITU-R BT.2100-2'}**, + method Computation method. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` :math:`F_D` is the luminance of a displayed linear component :math:`{R_D, G_D, or B_D}`, in :math:`cd/m^2`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -1260,45 +1337,49 @@ def ootf_HLG_BT2100(E, L_B=0, L_W=1000, gamma=None, method='ITU-R BT.2100-2'): 63.1051034... """ + method = validate_method(method, BT2100_HLG_OOTF_METHODS) + function = BT2100_HLG_OOTF_METHODS[method] return function( - E, **filter_kwargs(function, **{ - 'L_B': L_B, - 'L_W': L_W, - 'gamma': gamma - })) + E, + **filter_kwargs(function, **{"L_B": L_B, "L_W": L_W, "gamma": gamma}), + ) -def ootf_inverse_HLG_BT2100_1(F_D, L_B=0, L_W=1000, gamma=None): +def ootf_inverse_HLG_BT2100_1( + F_D: FloatingOrArrayLike, + L_B: Floating = 0, + L_W: Floating = 1000, + gamma: Optional[Floating] = None, +) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference HLG* inverse opto-optical + Define *Recommendation ITU-R BT.2100* *Reference HLG* inverse opto-optical transfer function (OOTF / OOCF) as given in *ITU-R BT.2100-1*. Parameters ---------- - F_D : numeric or array_like + F_D :math:`F_D` is the luminance of a displayed linear component :math:`{R_D, G_D, or B_D}`, in :math:`cd/m^2`. - L_B : numeric, optional + L_B :math:`L_B` is the display luminance for black in :math:`cd/m^2`. - L_W : numeric, optional + L_W :math:`L_W` is nominal peak luminance of the display in :math:`cd/m^2` for achromatic pixels. - gamma : numeric, optional + gamma System gamma value, 1.2 at the nominal display peak luminance of :math:`1000 cd/m^2`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` :math:`E` is the signal for each colour component :math:`{R_S, G_S, B_S}` proportional to scene linear light and scaled by camera exposure. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -1324,14 +1405,15 @@ def ootf_inverse_HLG_BT2100_1(F_D, L_B=0, L_W=1000, gamma=None): 0.0999999... """ - F_D = np.atleast_1d(to_domain_1(F_D)) + F_D = as_float_array(np.atleast_1d(to_domain_1(F_D))) if F_D.shape[-1] != 3: usage_warning( '"Recommendation ITU-R BT.2100" "Reference HLG OOTF" uses ' - 'RGB Luminance in computations and expects a vector input, thus ' - 'the given input array will be stacked to compose a vector for ' - 'internal computations but a single component will be output.') + "RGB Luminance in computations and expects a vector input, thus " + "the given input array will be stacked to compose a vector for " + "internal computations but a single component will be output." + ) R_D = G_D = B_D = F_D else: R_D, G_D, B_D = tsplit(F_D) @@ -1341,10 +1423,9 @@ def ootf_inverse_HLG_BT2100_1(F_D, L_B=0, L_W=1000, gamma=None): alpha = L_W - L_B beta = L_B - if gamma is None: - gamma = gamma_function_HLG_BT2100(L_W) + gamma = optional(gamma, gamma_function_HLG_BT2100(L_W)) - Y_D_beta = (np.abs((Y_D - beta) / alpha) ** ((1 - gamma) / gamma)) + Y_D_beta = np.abs((Y_D - beta) / alpha) ** ((1 - gamma) / gamma) R_S = np.where( Y_D == beta, @@ -1370,33 +1451,36 @@ def ootf_inverse_HLG_BT2100_1(F_D, L_B=0, L_W=1000, gamma=None): return from_range_1(RGB_S) -def ootf_inverse_HLG_BT2100_2(F_D, L_W=1000, gamma=None): +def ootf_inverse_HLG_BT2100_2( + F_D: FloatingOrArrayLike, + L_W: Floating = 1000, + gamma: Optional[Floating] = None, +) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference HLG* inverse opto-optical + Define *Recommendation ITU-R BT.2100* *Reference HLG* inverse opto-optical transfer function (OOTF / OOCF) as given in *ITU-R BT.2100-2*. Parameters ---------- - F_D : numeric or array_like + F_D :math:`F_D` is the luminance of a displayed linear component :math:`{R_D, G_D, or B_D}`, in :math:`cd/m^2`. - L_W : numeric, optional + L_W :math:`L_W` is nominal peak luminance of the display in :math:`cd/m^2` for achromatic pixels. - gamma : numeric, optional + gamma System gamma value, 1.2 at the nominal display peak luminance of :math:`1000 cd/m^2`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` :math:`E` is the signal for each colour component :math:`{R_S, G_S, B_S}` proportional to scene linear light and scaled by camera exposure. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -1419,14 +1503,15 @@ def ootf_inverse_HLG_BT2100_2(F_D, L_W=1000, gamma=None): 0.1000000... """ - F_D = np.atleast_1d(to_domain_1(F_D)) + F_D = as_float_array(np.atleast_1d(to_domain_1(F_D))) if F_D.shape[-1] != 3: usage_warning( '"Recommendation ITU-R BT.2100" "Reference HLG OOTF" uses ' - 'RGB Luminance in computations and expects a vector input, thus ' - 'the given input array will be stacked to compose a vector for ' - 'internal computations but a single component will be output.') + "RGB Luminance in computations and expects a vector input, thus " + "the given input array will be stacked to compose a vector for " + "internal computations but a single component will be output." + ) R_D = G_D = B_D = F_D else: R_D, G_D, B_D = tsplit(F_D) @@ -1435,10 +1520,9 @@ def ootf_inverse_HLG_BT2100_2(F_D, L_W=1000, gamma=None): alpha = L_W - if gamma is None: - gamma = gamma_function_HLG_BT2100(L_W) + gamma = optional(gamma, gamma_function_HLG_BT2100(L_W)) - Y_D_alpha = (np.abs(Y_D / alpha) ** ((1 - gamma) / gamma)) + Y_D_alpha = np.abs(Y_D / alpha) ** ((1 - gamma) / gamma) R_S = np.where( Y_D == 0, @@ -1464,10 +1548,14 @@ def ootf_inverse_HLG_BT2100_2(F_D, L_W=1000, gamma=None): return from_range_1(RGB_S) -BT2100_HLG_OOTF_INVERSE_METHODS = CaseInsensitiveMapping({ - 'ITU-R BT.2100-1': ootf_inverse_HLG_BT2100_1, - 'ITU-R BT.2100-2': ootf_inverse_HLG_BT2100_2, -}) +BT2100_HLG_OOTF_INVERSE_METHODS: CaseInsensitiveMapping = ( + CaseInsensitiveMapping( + { + "ITU-R BT.2100-1": ootf_inverse_HLG_BT2100_1, + "ITU-R BT.2100-2": ootf_inverse_HLG_BT2100_2, + } + ) +) BT2100_HLG_OOTF_INVERSE_METHODS.__doc__ = """ Supported *Recommendation ITU-R BT.2100* *Reference HLG* inverse opto-optical transfer function (OOTF / OOCF). @@ -1476,48 +1564,47 @@ def ootf_inverse_HLG_BT2100_2(F_D, L_W=1000, gamma=None): ---------- :cite:`Borer2017a`, :cite:`InternationalTelecommunicationUnion2017`, :cite:`InternationalTelecommunicationUnion2018` - -BT2100_HLG_OOTF_INVERSE_METHODS : CaseInsensitiveMapping - **{'ITU-R BT.2100-1', 'ITU-R BT.2100-2'}** """ -def ootf_inverse_HLG_BT2100(F_D, - L_B=0, - L_W=1000, - gamma=None, - method='ITU-R BT.2100-2'): +def ootf_inverse_HLG_BT2100( + F_D: FloatingOrArrayLike, + L_B: Floating = 0, + L_W: Floating = 1000, + gamma: Optional[Floating] = None, + method: Union[ + Literal["ITU-R BT.2100-1", "ITU-R BT.2100-2"], str + ] = "ITU-R BT.2100-2", +) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.2100* *Reference HLG* inverse opto-optical + Define *Recommendation ITU-R BT.2100* *Reference HLG* inverse opto-optical transfer function (OOTF / OOCF). Parameters ---------- - F_D : numeric or array_like + F_D :math:`F_D` is the luminance of a displayed linear component :math:`{R_D, G_D, or B_D}`, in :math:`cd/m^2`. - L_B : numeric, optional + L_B :math:`L_B` is the display luminance for black in :math:`cd/m^2`. - L_W : numeric, optional + L_W :math:`L_W` is nominal peak luminance of the display in :math:`cd/m^2` for achromatic pixels. - gamma : numeric, optional + gamma System gamma value, 1.2 at the nominal display peak luminance of :math:`1000 cd/m^2`. - method : unicode, optional - **{'ITU-R BT.2100-1', 'ITU-R BT.2100-2'}**, + method Computation method. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` :math:`E` is the signal for each colour component :math:`{R_S, G_S, B_S}` proportional to scene linear light and scaled by camera exposure. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -1545,12 +1632,11 @@ def ootf_inverse_HLG_BT2100(F_D, 0.0999999... """ + method = validate_method(method, BT2100_HLG_OOTF_INVERSE_METHODS) + function = BT2100_HLG_OOTF_INVERSE_METHODS[method] return function( F_D, - **filter_kwargs(function, **{ - 'L_B': L_B, - 'L_W': L_W, - 'gamma': gamma - })) + **filter_kwargs(function, **{"L_B": L_B, "L_W": L_W, "gamma": gamma}), + ) diff --git a/colour/models/rgb/transfer_functions/itur_bt_601.py b/colour/models/rgb/transfer_functions/itur_bt_601.py index abf0b3d184..66681fa534 100644 --- a/colour/models/rgb/transfer_functions/itur_bt_601.py +++ b/colour/models/rgb/transfer_functions/itur_bt_601.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ ITU-R BT.601-7 ============== -Defines *ITU-R BT.601-7* opto-electrical transfer function (OETF / OECF) and -its inverse: +Defines the *ITU-R BT.601-7* opto-electrical transfer function (OETF) +and its inverse: - :func:`colour.models.oetf_BT601` - :func:`colour.models.oetf_inverse_BT601` @@ -19,42 +18,49 @@ R-REC-BT.601-7-201103-I!!PDF-E.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.algebra import spow -from colour.utilities import (as_float, domain_range_scale, from_range_1, - to_domain_1) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['oetf_BT601', 'oetf_inverse_BT601'] - - -def oetf_BT601(L): +from colour.hints import FloatingOrArrayLike, FloatingOrNDArray +from colour.utilities import ( + as_float, + domain_range_scale, + from_range_1, + to_domain_1, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "oetf_BT601", + "oetf_inverse_BT601", +] + + +def oetf_BT601(L: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.601-7* opto-electronic transfer function - (OETF / OECF). + Define *Recommendation ITU-R BT.601-7* opto-electronic transfer function + (OETF). Parameters ---------- - L : numeric or array_like + L *Luminance* :math:`L` of the image. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Corresponding electrical signal :math:`E`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -84,24 +90,23 @@ def oetf_BT601(L): return as_float(from_range_1(E)) -def oetf_inverse_BT601(E): +def oetf_inverse_BT601(E: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.601-7* inverse opto-electronic transfer - function (OETF / OECF). + Define *Recommendation ITU-R BT.601-7* inverse opto-electronic transfer + function (OETF). Parameters ---------- - E : numeric or array_like + E Electrical signal :math:`E`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Corresponding *luminance* :math:`L` of the image. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -126,7 +131,7 @@ def oetf_inverse_BT601(E): E = to_domain_1(E) - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): L = np.where( E < oetf_BT601(0.018), E / 4.5, diff --git a/colour/models/rgb/transfer_functions/itur_bt_709.py b/colour/models/rgb/transfer_functions/itur_bt_709.py index 4a2b7bb01b..f36685187c 100644 --- a/colour/models/rgb/transfer_functions/itur_bt_709.py +++ b/colour/models/rgb/transfer_functions/itur_bt_709.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ ITU-R BT.709-6 ============== -Defines *ITU-R BT.709-6* opto-electrical transfer function (OETF / OECF) and -its inverse: +Defines the *ITU-R BT.709-6* opto-electrical transfer function (OETF) +and its inverse: - :func:`colour.models.oetf_BT709` - :func:`colour.models.oetf_inverse_BT709` @@ -19,38 +18,41 @@ R-REC-BT.709-6-201506-I!!PDF-E.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations +from colour.hints import FloatingOrArrayLike, FloatingOrNDArray from colour.models.rgb.transfer_functions import oetf_BT601, oetf_inverse_BT601 -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['oetf_BT709', 'oetf_inverse_BT709'] +__all__ = [ + "oetf_BT709", + "oetf_inverse_BT709", +] -def oetf_BT709(L): +def oetf_BT709(L: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.709-6* opto-electronic transfer function - (OETF / OECF). + Define *Recommendation ITU-R BT.709-6* opto-electronic transfer function + (OETF). Parameters ---------- - L : numeric or array_like + L *Luminance* :math:`L` of the image. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Corresponding electrical signal :math:`V`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -76,24 +78,23 @@ def oetf_BT709(L): return oetf_BT601(L) -def oetf_inverse_BT709(V): +def oetf_inverse_BT709(V: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines *Recommendation ITU-R BT.709-6* inverse opto-electronic transfer - function (OETF / OECF). + Define *Recommendation ITU-R BT.709-6* inverse opto-electronic transfer + function (OETF). Parameters ---------- - V : numeric or array_like + V Electrical signal :math:`V`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Corresponding *luminance* :math:`L` of the image. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ diff --git a/colour/models/rgb/transfer_functions/linear.py b/colour/models/rgb/transfer_functions/linear.py index c351fbec38..ae22eb95f9 100644 --- a/colour/models/rgb/transfer_functions/linear.py +++ b/colour/models/rgb/transfer_functions/linear.py @@ -1,45 +1,49 @@ -# -*- coding: utf-8 -*- """ Linear Colour Component Transfer Function ========================================= -Defines linear encoding / decoding colour component transfer function related -objects: +Defines the linear encoding / decoding colour component transfer function +related objects: - :func:`colour.linear_function` """ -from __future__ import division, unicode_literals +from __future__ import annotations -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import FloatingOrArrayLike, FloatingOrNDArray +from colour.utilities import as_float -__all__ = ['linear_function'] +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "linear_function", +] -def linear_function(a): + +def linear_function(a: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines a typical linear encoding / decoding function, essentially a + Define a typical linear encoding / decoding function, essentially a pass-through function. Parameters ---------- - a : numeric or array_like + a Array to encode / decode. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Encoded / decoded array. Examples -------- - >>> linear_function(0.18) - 0.18 + >>> linear_function(0.18) # doctest: +ELLIPSIS + 0.1799999... """ - return a + return as_float(a) diff --git a/colour/models/rgb/transfer_functions/log.py b/colour/models/rgb/transfer_functions/log.py index 3c58c4bafa..a28cf8ab1a 100644 --- a/colour/models/rgb/transfer_functions/log.py +++ b/colour/models/rgb/transfer_functions/log.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Common Log Encodings ==================== @@ -36,38 +35,63 @@ Tables. Retrieved June 24, 2020, from http://j.mp/S-2014-006 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.utilities import (as_float, as_float_array, from_range_1, - to_domain_1) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import ( + Floating, + FloatingOrArrayLike, + FloatingOrNDArray, + Integer, + Literal, + Optional, + Union, + cast, +) +from colour.utilities import ( + as_float, + as_float_array, + from_range_1, + optional, + to_domain_1, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'logarithmic_function_basic', 'logarithmic_function_quasilog', - 'logarithmic_function_camera', 'log_encoding_Log2', 'log_decoding_Log2' + "logarithmic_function_basic", + "logarithmic_function_quasilog", + "logarithmic_function_camera", + "log_encoding_Log2", + "log_decoding_Log2", ] FLT_MIN = 1.175494e-38 -def logarithmic_function_basic(x, style='log2', base=2): +def logarithmic_function_basic( + x: FloatingOrArrayLike, + style: Union[ + Literal["log10", "antiLog10", "log2", "antiLog2", "logB", "antiLogB"], + str, + ] = "log2", + base: Integer = 2, +) -> FloatingOrNDArray: """ - Defines the basic logarithmic function. + Define the basic logarithmic function. Parameters ---------- - x : numeric + x The data to undergo basic logarithmic conversion. - style : unicode, optional - **{'log10', 'antiLog10', 'log2', 'antiLog2', 'logB', 'antiLogB'}**, + style Defines the behaviour for the logarithmic function to operate: - *log10*: Applies a base 10 logarithm to the passed value. @@ -77,19 +101,14 @@ def logarithmic_function_basic(x, style='log2', base=2): - *logB*: Applies an arbitrary base logarithm to the passed value. - *antiLogB*: Applies an arbitrary base anti-logarithm to the passed value. - base : numeric, optional + base Logarithmic base used for the conversion. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Logarithmically converted data. - Raises - ------ - ValueError - If the *style* is not defined. - Examples -------- The basic logarithmic function *styles* operate as follows: @@ -113,77 +132,69 @@ def logarithmic_function_basic(x, style='log2', base=2): """ x = as_float_array(x) + style = validate_method( + style, + ["log10", "antiLog10", "log2", "antiLog2", "logB", "antiLogB"], + '"{0}" style is invalid, it must be one of {1}!', + ) - style = style.lower() - if style == 'log10': + if style == "log10": return as_float(np.where(x >= FLT_MIN, np.log10(x), np.log10(FLT_MIN))) - elif style == 'antilog10': - return as_float(10 ** x) - elif style == 'log2': + elif style == "antilog10": + return as_float(10**x) + elif style == "log2": return as_float(np.where(x >= FLT_MIN, np.log2(x), np.log2(FLT_MIN))) - elif style == 'antilog2': - return as_float(2 ** x) - elif style == 'logb': + elif style == "antilog2": + return as_float(2**x) + elif style == "logb": return as_float(np.log(x) / np.log(base)) - elif style == 'antilogb': - return as_float(base ** x) - else: - raise ValueError( - 'Undefined style used: "{0}", must be one of the following: ' - '"{1}".'.format( - style, ', '.join([ - 'log10', 'antiLog10', 'log2', 'antiLog2', 'logB', - 'antiLogB' - ]))) - - -def logarithmic_function_quasilog(x, - style='linToLog', - base=2, - log_side_slope=1, - lin_side_slope=1, - log_side_offset=0, - lin_side_offset=0): + else: # style == 'antilogb' + return as_float(base**x) + + +def logarithmic_function_quasilog( + x: FloatingOrArrayLike, + style: Union[Literal["linToLog", "logToLin"], str] = "linToLog", + base: Integer = 2, + log_side_slope: Floating = 1, + lin_side_slope: Floating = 1, + log_side_offset: Floating = 0, + lin_side_offset: Floating = 0, +) -> FloatingOrNDArray: """ - Defines the quasilog logarithmic function. + Define the quasilog logarithmic function. Parameters ---------- - x : numeric + x Linear/non-linear data to undergo encoding/decoding. - style : unicode, optional - **{'linToLog', 'logToLin'}**, + style Defines the behaviour for the logarithmic function to operate: - *linToLog*: Applies a logarithm to convert linear data to logarithmic data. - *logToLin*: Applies an anti-logarithm to convert logarithmic data to linear data. - base : numeric, optional + base Logarithmic base used for the conversion. - log_side_slope : numeric, optional + log_side_slope Slope (or gain) applied to the log side of the logarithmic function. The default value is 1. - lin_side_slope : numeric, optional + lin_side_slope Slope of the linear side of the logarithmic function. The default value is 1. - log_side_offset : numeric, optional + log_side_offset Offset applied to the log side of the logarithmic function. The default value is 0. - lin_side_offset : numeric, optional + lin_side_offset Offset applied to the linear side of the logarithmic function. The default value is 0. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Encoded/Decoded data. - Raises - ------ - ValueError - If the *style* is not defined. - Examples -------- >>> logarithmic_function_quasilog( # doctest: +ELLIPSIS @@ -195,42 +206,54 @@ def logarithmic_function_quasilog(x, """ x = as_float_array(x) + style = validate_method( + style, + ["lintolog", "logtolin"], + '"{0}" style is invalid, it must be one of {1}!', + ) - style = style.lower() - if style == 'lintolog': - return as_float(( - log_side_slope * - (np.log(np.maximum(lin_side_slope * x + lin_side_offset, FLT_MIN)) - / np.log(base)) + log_side_offset)) - elif style == 'logtolin': + if style == "lintolog": return as_float( - ((base ** - ((x - log_side_offset) / log_side_slope) - lin_side_offset) / - lin_side_slope)) - else: - raise ValueError( - 'Undefined style used: "{0}", must be one of the following: ' - '"{1}".'.format(style, ', '.join(['linToLog', 'logToLin']))) - - -def logarithmic_function_camera(x, - style='cameraLinToLog', - base=2, - log_side_slope=1, - lin_side_slope=1, - log_side_offset=0, - lin_side_offset=0, - lin_side_break=0.005, - linear_slope=None): + log_side_slope + * ( + np.log( + np.maximum(lin_side_slope * x + lin_side_offset, FLT_MIN) + ) + / np.log(base) + ) + + log_side_offset + ) + else: # style == 'logtolin' + return as_float( + ( + base ** ((x - log_side_offset) / log_side_slope) + - lin_side_offset + ) + / lin_side_slope + ) + + +def logarithmic_function_camera( + x: FloatingOrArrayLike, + style: Union[ + Literal["cameraLinToLog", "cameraLogToLin"], str + ] = "cameraLinToLog", + base: Integer = 2, + log_side_slope: Floating = 1, + lin_side_slope: Floating = 1, + log_side_offset: Floating = 0, + lin_side_offset: Floating = 0, + lin_side_break: Floating = 0.005, + linear_slope: Optional[Floating] = None, +) -> FloatingOrNDArray: """ - Defines the camera logarithmic function. + Define the camera logarithmic function. Parameters ---------- - x : numeric + x Linear/non-linear data to undergo encoding/decoding. - style : unicode, optional - **{'cameraLinToLog', 'cameraLogToLin'}**, + style Defines the behaviour for the logarithmic function to operate: - *cameraLinToLog*: Applies a piece-wise function with logarithmic @@ -239,36 +262,31 @@ def logarithmic_function_camera(x, - *cameraLogToLin*: Applies a piece-wise function with logarithmic and linear segments on non-linear values, converting them to linear values. - base : numeric, optional + base Logarithmic base used for the conversion. - log_side_slope : numeric, optional + log_side_slope Slope (or gain) applied to the log side of the logarithmic segment. The default value is 1. - lin_side_slope : numeric, optional + lin_side_slope Slope of the linear side of the logarithmic segment. The default value is 1. - log_side_offset : numeric, optional + log_side_offset Offset applied to the log side of the logarithmic segment. The default value is 0. - lin_side_offset : numeric, optional + lin_side_offset Offset applied to the linear side of the logarithmic segment. The default value is 0. - lin_side_break : numeric + lin_side_break Break-point, defined in linear space, at which the piece-wise function transitions between the logarithmic and linear segments. - linear_slope : numeric, optional + linear_slope Slope of the linear portion of the curve. The default value is *None*. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Encoded/Decoded data. - Raises - ------ - ValueError - If the *style* is not defined. - Examples -------- >>> logarithmic_function_camera( # doctest: +ELLIPSIS @@ -280,64 +298,97 @@ def logarithmic_function_camera(x, """ x = as_float_array(x) + style = validate_method( + style, + ["cameraLinToLog", "cameraLogToLin"], + '"{0}" style is invalid, it must be one of {1}!', + ) log_side_break = ( - log_side_slope * - (np.log(lin_side_slope * lin_side_break + lin_side_offset) / - np.log(base)) + log_side_offset) - - if linear_slope is None: - linear_slope = (log_side_slope * (lin_side_slope / ( - (lin_side_slope * lin_side_break + lin_side_offset) * np.log(base)) - )) + log_side_slope + * ( + np.log(lin_side_slope * lin_side_break + lin_side_offset) + / np.log(base) + ) + + log_side_offset + ) + + linear_slope = cast( + Floating, + optional( + linear_slope, + ( + log_side_slope + * ( + lin_side_slope + / ( + (lin_side_slope * lin_side_break + lin_side_offset) + * np.log(base) + ) + ) + ), + ), + ) linear_offset = log_side_break - linear_slope * lin_side_break - style = style.lower() - if style == 'cameralintolog': + if style == "cameralintolog": return as_float( np.where( - x <= lin_side_break, linear_slope * x + linear_offset, + x <= lin_side_break, + linear_slope * x + linear_offset, logarithmic_function_quasilog( - x, 'linToLog', base, log_side_slope, lin_side_slope, - log_side_offset, lin_side_offset))) - elif style == 'cameralogtolin': + x, + "linToLog", + base, + log_side_slope, + lin_side_slope, + log_side_offset, + lin_side_offset, + ), + ) + ) + else: # style == 'cameralogtolin' return as_float( np.where( x <= log_side_break, (x - linear_offset) / linear_slope, logarithmic_function_quasilog( - x, 'logToLin', base, log_side_slope, lin_side_slope, - log_side_offset, lin_side_offset), - )) - else: - raise ValueError( - 'Undefined style used: "{0}", must be one of the following: ' - '"{1}".'.format(style, - ', '.join(['cameraLinToLog', 'cameraLogToLin']))) - - -def log_encoding_Log2(lin, - middle_grey=0.18, - min_exposure=-6.5, - max_exposure=6.5): + x, + "logToLin", + base, + log_side_slope, + lin_side_slope, + log_side_offset, + lin_side_offset, + ), + ) + ) + + +def log_encoding_Log2( + lin: FloatingOrArrayLike, + middle_grey: Floating = 0.18, + min_exposure: Floating = -6.5, + max_exposure: Floating = 6.5, +) -> FloatingOrNDArray: """ - Defines the common *Log2* encoding function. + Define the common *Log2* encoding function. Parameters ---------- - lin : numeric or array_like + lin Linear data to undergo encoding. - middle_grey : numeric, optional + middle_grey *Middle Grey* exposure value. - min_exposure : numeric, optional + min_exposure Minimum exposure level. - max_exposure : numeric, optional + max_exposure Maximum exposure level. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Non-linear *Log2* encoded data. Notes @@ -371,27 +422,29 @@ def log_encoding_Log2(lin, return as_float(from_range_1(log_norm)) -def log_decoding_Log2(log_norm, - middle_grey=0.18, - min_exposure=-6.5, - max_exposure=6.5): +def log_decoding_Log2( + log_norm: FloatingOrArrayLike, + middle_grey: Floating = 0.18, + min_exposure: Floating = -6.5, + max_exposure: Floating = 6.5, +) -> FloatingOrNDArray: """ - Defines the common *Log2* decoding function. + Define the common *Log2* decoding function. Parameters ---------- - log_norm : numeric or array_like + log_norm Logarithmic data to undergo decoding. - middle_grey : numeric, optional + middle_grey *Middle Grey* exposure value. - min_exposure : numeric, optional + min_exposure Minimum exposure level. - max_exposure : numeric, optional + max_exposure Maximum exposure level. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear *Log2* decoded data. Notes @@ -420,6 +473,6 @@ def log_decoding_Log2(log_norm, log_norm = to_domain_1(log_norm) lg2 = log_norm * (max_exposure - min_exposure) + min_exposure - lin = (2 ** lg2) * middle_grey + lin = (2**lg2) * middle_grey return as_float(from_range_1(lin)) diff --git a/colour/models/rgb/transfer_functions/nikon_nlog.py b/colour/models/rgb/transfer_functions/nikon_nlog.py new file mode 100644 index 0000000000..21e0c13e10 --- /dev/null +++ b/colour/models/rgb/transfer_functions/nikon_nlog.py @@ -0,0 +1,209 @@ +""" +Nikon N-Log Log Encoding +======================== + +Defines the *Nikon N-Log* log encoding: + +- :func:`colour.models.log_encoding_NLog` +- :func:`colour.models.log_decoding_NLog` + +References +---------- +- :cite:`Nikon2018` : Nikon. (2018). N-Log Specification Document - Version + 1.0.0 (pp. 1-5). Retrieved September 9, 2019, from + http://download.nikonimglib.com/archive3/hDCmK00m9JDI03RPruD74xpoU905/\ +N-Log_Specification_(En)01.pdf +""" + +from __future__ import annotations + +import numpy as np + +from colour.algebra import spow +from colour.hints import ( + Boolean, + FloatingOrArrayLike, + FloatingOrNDArray, + Integer, +) +from colour.models.rgb.transfer_functions import full_to_legal, legal_to_full +from colour.utilities import Structure, as_float, from_range_1, to_domain_1 + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 Colour Developers" +__license__ = "New BSD License - http://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" + +__all__ = [ + "NLOG_CONSTANTS", + "log_encoding_NLog", + "log_decoding_NLog", +] + +NLOG_CONSTANTS: Structure = Structure( + cut1=0.328, + cut2=(452 / 1023), + a=(650 / 1023), + b=0.0075, + c=(150 / 1023), + d=(619 / 1023), +) +"""*Nikon N-Log* colourspace constants.""" + + +def log_encoding_NLog( + in_r: FloatingOrArrayLike, + bit_depth: Integer = 10, + out_normalised_code_value: Boolean = True, + in_reflection: Boolean = True, + constants: Structure = NLOG_CONSTANTS, +) -> FloatingOrNDArray: + """ + Define the *Nikon N-Log* log encoding curve / opto-electronic transfer + function. + + Parameters + ---------- + in_r + Linear reflection data :math`in`. + bit_depth + Bit depth used for conversion. + out_normalised_code_value + Whether the non-linear *Nikon N-Log* data :math:`out` is encoded as + normalised code values. + in_reflection + Whether the light level :math`in` to a camera is reflection. + constants + *Nikon N-Log* constants. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + Non-linear data :math:`out`. + + Notes + ----- + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``in_r`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + +------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``out_r`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + References + ---------- + :cite:`Nikon2018` + + Examples + -------- + >>> log_encoding_NLog(0.18) # doctest: +ELLIPSIS + 0.3636677... + """ + + in_r = to_domain_1(in_r) + + if not in_reflection: + in_r = in_r * 0.9 + + cut1 = constants.cut1 + a = constants.a + b = constants.b + c = constants.c + d = constants.d + + out_r = np.where( + in_r < cut1, + a * spow(in_r + b, 1 / 3), + c * np.log(in_r) + d, + ) + + out_r_cv = ( + out_r if out_normalised_code_value else legal_to_full(out_r, bit_depth) + ) + + return as_float(from_range_1(out_r_cv)) + + +def log_decoding_NLog( + out_r: FloatingOrArrayLike, + bit_depth: Integer = 10, + in_normalised_code_value: Boolean = True, + out_reflection: Boolean = True, + constants: Structure = NLOG_CONSTANTS, +) -> FloatingOrNDArray: + """ + Define the *Nikon N-Log* log decoding curve / electro-optical transfer + function. + + Parameters + ---------- + out_r + Non-linear data :math:`out`. + bit_depth + Bit depth used for conversion. + in_normalised_code_value + Whether the non-linear *Nikon N-Log* data :math:`out` is encoded as + normalised code values. + out_reflection + Whether the light level :math`in` to a camera is reflection. + constants + *Nikon N-Log* constants. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + Linear reflection data :math`in`. + + Notes + ----- + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``out_r`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + +------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``in_r`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + References + ---------- + :cite:`Nikon2018` + + Examples + -------- + >>> log_decoding_NLog(0.36366777011713869) # doctest: +ELLIPSIS + 0.1799999... + """ + + out_r = to_domain_1(out_r) + + out_r = ( + out_r if in_normalised_code_value else full_to_legal(out_r, bit_depth) + ) + + cut2 = constants.cut2 + a = constants.a + b = constants.b + c = constants.c + d = constants.d + + in_r = np.where( + out_r < cut2, + spow(out_r / a, 3) - b, + np.exp((out_r - d) / c), + ) + + if not out_reflection: + in_r = in_r / 0.9 + + return as_float(from_range_1(in_r)) diff --git a/colour/models/rgb/transfer_functions/panalog.py b/colour/models/rgb/transfer_functions/panalog.py index 3d9f0931f7..5bbdf20178 100644 --- a/colour/models/rgb/transfer_functions/panalog.py +++ b/colour/models/rgb/transfer_functions/panalog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Panalog Encoding ================ @@ -16,37 +15,49 @@ nuke-default/make.py """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.utilities import from_range_1, to_domain_1 - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['log_encoding_Panalog', 'log_decoding_Panalog'] - - -def log_encoding_Panalog(x, black_offset=10 ** ((64 - 681) / 444)): +from colour.hints import FloatingOrArrayLike, FloatingOrNDArray +from colour.utilities import ( + as_float, + as_float_array, + from_range_1, + to_domain_1, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "log_encoding_Panalog", + "log_decoding_Panalog", +] + + +def log_encoding_Panalog( + x: FloatingOrArrayLike, + black_offset: FloatingOrArrayLike = 10 ** ((64 - 681) / 444), +) -> FloatingOrNDArray: """ - Defines the *Panalog* log encoding curve / opto-electronic transfer + Define the *Panalog* log encoding curve / opto-electronic transfer function. Parameters ---------- - x : numeric or array_like + x Linear data :math:`x`. - black_offset : numeric or array_like + black_offset Black offset. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Non-linear data :math:`y`. Warnings @@ -56,7 +67,6 @@ def log_encoding_Panalog(x, black_offset=10 ** ((64 - 681) / 444)): Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -80,27 +90,31 @@ def log_encoding_Panalog(x, black_offset=10 ** ((64 - 681) / 444)): """ x = to_domain_1(x) + black_offset = as_float_array(black_offset) y = (681 + 444 * np.log10(x * (1 - black_offset) + black_offset)) / 1023 - return from_range_1(y) + return as_float(from_range_1(y)) -def log_decoding_Panalog(y, black_offset=10 ** ((64 - 681) / 444)): +def log_decoding_Panalog( + y: FloatingOrArrayLike, + black_offset: FloatingOrArrayLike = 10 ** ((64 - 681) / 444), +) -> FloatingOrNDArray: """ - Defines the *Panalog* log decoding curve / electro-optical transfer + Define the *Panalog* log decoding curve / electro-optical transfer function. Parameters ---------- - y : numeric or array_like + y Non-linear data :math:`y`. - black_offset : numeric or array_like + black_offset Black offset. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`x`. Warnings @@ -110,7 +124,6 @@ def log_decoding_Panalog(y, black_offset=10 ** ((64 - 681) / 444)): Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -134,7 +147,8 @@ def log_decoding_Panalog(y, black_offset=10 ** ((64 - 681) / 444)): """ y = to_domain_1(y) + black_offset = as_float_array(black_offset) x = (10 ** ((1023 * y - 681) / 444) - black_offset) / (1 - black_offset) - return from_range_1(x) + return as_float(from_range_1(x)) diff --git a/colour/models/rgb/transfer_functions/panasonic_vlog.py b/colour/models/rgb/transfer_functions/panasonic_vlog.py index e37fe55a81..1e870a1044 100644 --- a/colour/models/rgb/transfer_functions/panasonic_vlog.py +++ b/colour/models/rgb/transfer_functions/panasonic_vlog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Panasonic V-Log Log Encoding ============================ @@ -15,69 +14,70 @@ http://pro-av.panasonic.net/en/varicam/common/pdf/VARICAM_V-Log_V-Gamut.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +from colour.hints import ( + Boolean, + FloatingOrArrayLike, + FloatingOrNDArray, + Integer, +) from colour.models.rgb.transfer_functions import full_to_legal, legal_to_full from colour.utilities import Structure, as_float, from_range_1, to_domain_1 -from colour.utilities.deprecation import handle_arguments_deprecation -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['CONSTANTS_VLOG', 'log_encoding_VLog', 'log_decoding_VLog'] - -CONSTANTS_VLOG = Structure( - cut1=0.01, cut2=0.181, b=0.00873, c=0.241514, d=0.598206) -""" -*Panasonic V-Log* colourspace constants. - -CONSTANTS_VLOG : Structure -""" - - -def log_encoding_VLog(L_in, - bit_depth=10, - out_normalised_code_value=True, - in_reflection=True, - constants=CONSTANTS_VLOG, - **kwargs): +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "CONSTANTS_VLOG", + "log_encoding_VLog", + "log_decoding_VLog", +] + +CONSTANTS_VLOG: Structure = Structure( + cut1=0.01, cut2=0.181, b=0.00873, c=0.241514, d=0.598206 +) +"""*Panasonic V-Log* colourspace constants.""" + + +def log_encoding_VLog( + L_in: FloatingOrArrayLike, + bit_depth: Integer = 10, + out_normalised_code_value: Boolean = True, + in_reflection: Boolean = True, + constants: Structure = CONSTANTS_VLOG, +) -> FloatingOrNDArray: """ - Defines the *Panasonic V-Log* log encoding curve / opto-electronic transfer + Define the *Panasonic V-Log* log encoding curve / opto-electronic transfer function. Parameters ---------- - L_in : numeric or array_like + L_in Linear reflection data :math`L_{in}`. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - out_normalised_code_value : bool, optional + out_normalised_code_value Whether the non-linear *Panasonic V-Log* data :math:`V_{out}` is encoded as normalised code values. - in_reflection : bool, optional + in_reflection Whether the light level :math`L_{in}` to a camera is reflection. - constants : Structure, optional + constants *Panasonic V-Log* constants. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Non-linear data :math:`V_{out}`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -115,10 +115,6 @@ def log_encoding_VLog(L_in, by a code: [512, 1732, 2408]. """ - out_normalised_code_value = handle_arguments_deprecation({ - 'ArgumentRenamed': [['out_legal', 'out_normalised_code_value']], - }, **kwargs).get('out_normalised_code_value', out_normalised_code_value) - L_in = to_domain_1(L_in) if not in_reflection: @@ -135,49 +131,45 @@ def log_encoding_VLog(L_in, c * np.log10(L_in + b) + d, ) - V_out = (V_out - if out_normalised_code_value else legal_to_full(V_out, bit_depth)) + V_out_cv = ( + V_out if out_normalised_code_value else legal_to_full(V_out, bit_depth) + ) - return as_float(from_range_1(V_out)) + return as_float(from_range_1(V_out_cv)) -def log_decoding_VLog(V_out, - bit_depth=10, - in_normalised_code_value=True, - out_reflection=True, - constants=CONSTANTS_VLOG, - **kwargs): +def log_decoding_VLog( + V_out: FloatingOrArrayLike, + bit_depth: Integer = 10, + in_normalised_code_value: Boolean = True, + out_reflection: Boolean = True, + constants: Structure = CONSTANTS_VLOG, +) -> FloatingOrNDArray: """ - Defines the *Panasonic V-Log* log decoding curve / electro-optical transfer + Define the *Panasonic V-Log* log decoding curve / electro-optical transfer function. Parameters ---------- - V_out : numeric or array_like + V_out Non-linear data :math:`V_{out}`. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - in_normalised_code_value : bool, optional + in_normalised_code_value Whether the non-linear *Panasonic V-Log* data :math:`V_{out}` is encoded as normalised code values. - out_reflection : bool, optional + out_reflection Whether the light level :math`L_{in}` to a camera is reflection. - constants : Structure, optional + constants *Panasonic V-Log* constants. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear reflection data :math`L_{in}`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -200,14 +192,11 @@ def log_decoding_VLog(V_out, 0.1799999... """ - in_normalised_code_value = handle_arguments_deprecation({ - 'ArgumentRenamed': [['in_legal', 'in_normalised_code_value']], - }, **kwargs).get('in_normalised_code_value', in_normalised_code_value) - V_out = to_domain_1(V_out) - V_out = (V_out - if in_normalised_code_value else full_to_legal(V_out, bit_depth)) + V_out = ( + V_out if in_normalised_code_value else full_to_legal(V_out, bit_depth) + ) cut2 = constants.cut2 b = constants.b diff --git a/colour/models/rgb/transfer_functions/pivoted_log.py b/colour/models/rgb/transfer_functions/pivoted_log.py index 79aa9a37a4..acbccb9c61 100644 --- a/colour/models/rgb/transfer_functions/pivoted_log.py +++ b/colour/models/rgb/transfer_functions/pivoted_log.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Pivoted Log Encoding ==================== @@ -16,52 +15,57 @@ nuke-default/make.py """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.utilities import from_range_1, to_domain_1 - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['log_encoding_PivotedLog', 'log_decoding_PivotedLog'] - - -def log_encoding_PivotedLog(x, - log_reference=445, - linear_reference=0.18, - negative_gamma=0.6, - density_per_code_value=0.002): +from colour.hints import Floating, FloatingOrArrayLike, FloatingOrNDArray +from colour.utilities import as_float, from_range_1, to_domain_1 + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "log_encoding_PivotedLog", + "log_decoding_PivotedLog", +] + + +def log_encoding_PivotedLog( + x: FloatingOrArrayLike, + log_reference: Floating = 445, + linear_reference: Floating = 0.18, + negative_gamma: Floating = 0.6, + density_per_code_value: Floating = 0.002, +) -> FloatingOrNDArray: """ - Defines the *Josh Pines* style *Pivoted Log* log encoding curve / + Define the *Josh Pines* style *Pivoted Log* log encoding curve / opto-electronic transfer function. Parameters ---------- - x : numeric or array_like + x Linear data :math:`x`. - log_reference : numeric or array_like + log_reference Log reference. - linear_reference : numeric or array_like + linear_reference Linear reference. - negative_gamma : numeric or array_like + negative_gamma Negative gamma. - density_per_code_value : numeric or array_like + density_per_code_value Density per code value. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Non-linear data :math:`y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -86,42 +90,46 @@ def log_encoding_PivotedLog(x, x = to_domain_1(x) - y = ((log_reference + np.log10(x / linear_reference) / - (density_per_code_value / negative_gamma)) / 1023) + y = ( + log_reference + + np.log10(x / linear_reference) + / (density_per_code_value / negative_gamma) + ) / 1023 - return from_range_1(y) + return as_float(from_range_1(y)) -def log_decoding_PivotedLog(y, - log_reference=445, - linear_reference=0.18, - negative_gamma=0.6, - density_per_code_value=0.002): +def log_decoding_PivotedLog( + y: FloatingOrArrayLike, + log_reference: Floating = 445, + linear_reference: Floating = 0.18, + negative_gamma: Floating = 0.6, + density_per_code_value: Floating = 0.002, +) -> FloatingOrNDArray: """ - Defines the *Josh Pines* style *Pivoted Log* log decoding curve / + Define the *Josh Pines* style *Pivoted Log* log decoding curve / electro-optical transfer function. Parameters ---------- - y : numeric or array_like + y Non-linear data :math:`y`. - log_reference : numeric or array_like + log_reference Log reference. - linear_reference : numeric or array_like + linear_reference Linear reference. - negative_gamma : numeric or array_like + negative_gamma Negative gamma. - density_per_code_value : numeric or array_like + density_per_code_value Density per code value. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`x`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -146,7 +154,13 @@ def log_decoding_PivotedLog(y, y = to_domain_1(y) - x = (10 ** ((y * 1023 - log_reference) * - (density_per_code_value / negative_gamma)) * linear_reference) + x = ( + 10 + ** ( + (y * 1023 - log_reference) + * (density_per_code_value / negative_gamma) + ) + * linear_reference + ) - return from_range_1(x) + return as_float(from_range_1(x)) diff --git a/colour/models/rgb/transfer_functions/red_log.py b/colour/models/rgb/transfer_functions/red_log.py index dd5c5c1cd3..cab02efcd2 100644 --- a/colour/models/rgb/transfer_functions/red_log.py +++ b/colour/models/rgb/transfer_functions/red_log.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ RED Log Encodings ================= @@ -13,6 +12,8 @@ - :func:`colour.models.log_decoding_Log3G10_v1` - :func:`colour.models.log_encoding_Log3G10_v2` - :func:`colour.models.log_decoding_Log3G10_v2` +- :func:`colour.models.log_encoding_Log3G10_v3` +- :func:`colour.models.log_decoding_Log3G10_v3` - :attr:`colour.models.LOG3G10_ENCODING_METHODS` - :func:`colour.models.log_encoding_Log3G10` - :attr:`colour.models.LOG3G10_DECODING_METHODS` @@ -24,59 +25,87 @@ ---------- - :cite:`Nattress2016a` : Nattress, G. (2016). Private Discussion with Shaw, N. +- :cite:`REDDigitalCinema2017` : RED Digital Cinema. (2017). White Paper on + REDWideGamutRGB and Log3G10. Retrieved January 16, 2021, from + https://www.red.com/download/white-paper-on-redwidegamutrgb-and-log3g10 - :cite:`SonyImageworks2012a` : Sony Imageworks. (2012). make.py. Retrieved November 27, 2014, from https://github.com/imageworks/OpenColorIO-Configs/blob/master/\ nuke-default/make.py """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.models.rgb.transfer_functions import (log_encoding_Cineon, - log_decoding_Cineon) - -from colour.utilities import CaseInsensitiveMapping, from_range_1, to_domain_1 -from colour.utilities.deprecation import handle_arguments_deprecation - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import ( + FloatingOrArrayLike, + FloatingOrNDArray, + Literal, + Union, +) +from colour.models.rgb.transfer_functions import ( + log_encoding_Cineon, + log_decoding_Cineon, +) +from colour.utilities import ( + CaseInsensitiveMapping, + as_float, + as_float_array, + from_range_1, + to_domain_1, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'log_encoding_REDLog', 'log_decoding_REDLog', 'log_encoding_REDLogFilm', - 'log_decoding_REDLogFilm', 'log_encoding_Log3G10_v1', - 'log_decoding_Log3G10_v1', 'log_encoding_Log3G10_v2', - 'log_decoding_Log3G10_v2', 'LOG3G10_ENCODING_METHODS', - 'log_encoding_Log3G10', 'LOG3G10_DECODING_METHODS', 'log_decoding_Log3G10', - 'log_encoding_Log3G12', 'log_decoding_Log3G12' + "log_encoding_REDLog", + "log_decoding_REDLog", + "log_encoding_REDLogFilm", + "log_decoding_REDLogFilm", + "log_encoding_Log3G10_v1", + "log_decoding_Log3G10_v1", + "log_encoding_Log3G10_v2", + "log_decoding_Log3G10_v2", + "log_encoding_Log3G10_v3", + "log_decoding_Log3G10_v3", + "LOG3G10_ENCODING_METHODS", + "log_encoding_Log3G10", + "LOG3G10_DECODING_METHODS", + "log_decoding_Log3G10", + "log_encoding_Log3G12", + "log_decoding_Log3G12", ] -def log_encoding_REDLog(x, black_offset=10 ** ((0 - 1023) / 511)): +def log_encoding_REDLog( + x: FloatingOrArrayLike, + black_offset: FloatingOrArrayLike = 10 ** ((0 - 1023) / 511), +) -> FloatingOrNDArray: """ - Defines the *REDLog* log encoding curve / opto-electronic transfer + Define the *REDLog* log encoding curve / opto-electronic transfer function. Parameters ---------- - x : numeric or array_like + x Linear data :math:`x`. - black_offset : numeric or array_like + black_offset Black offset. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Non-linear data :math:`y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -100,32 +129,35 @@ def log_encoding_REDLog(x, black_offset=10 ** ((0 - 1023) / 511)): """ x = to_domain_1(x) + black_offset = as_float_array(black_offset) y = (1023 + 511 * np.log10(x * (1 - black_offset) + black_offset)) / 1023 - return from_range_1(y) + return as_float(from_range_1(y)) -def log_decoding_REDLog(y, black_offset=10 ** ((0 - 1023) / 511)): +def log_decoding_REDLog( + y: FloatingOrArrayLike, + black_offset: FloatingOrArrayLike = 10 ** ((0 - 1023) / 511), +) -> FloatingOrNDArray: """ - Defines the *REDLog* log decoding curve / electro-optical transfer + Define the *REDLog* log decoding curve / electro-optical transfer function. Parameters ---------- - y : numeric or array_like + y Non-linear data :math:`y`. - black_offset : numeric or array_like + black_offset Black offset. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`x`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -149,32 +181,35 @@ def log_decoding_REDLog(y, black_offset=10 ** ((0 - 1023) / 511)): """ y = to_domain_1(y) + black_offset = as_float_array(black_offset) x = ((10 ** ((1023 * y - 1023) / 511)) - black_offset) / (1 - black_offset) - return from_range_1(x) + return as_float(from_range_1(x)) -def log_encoding_REDLogFilm(x, black_offset=10 ** ((95 - 685) / 300)): +def log_encoding_REDLogFilm( + x: FloatingOrArrayLike, + black_offset: FloatingOrArrayLike = 10 ** ((95 - 685) / 300), +) -> FloatingOrNDArray: """ - Defines the *REDLogFilm* log encoding curve / opto-electronic transfer + Define the *REDLogFilm* log encoding curve / opto-electronic transfer function. Parameters ---------- - x : numeric or array_like + x Linear data :math:`x`. - black_offset : numeric or array_like + black_offset Black offset. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Non-linear data :math:`y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -200,26 +235,28 @@ def log_encoding_REDLogFilm(x, black_offset=10 ** ((95 - 685) / 300)): return log_encoding_Cineon(x, black_offset) -def log_decoding_REDLogFilm(y, black_offset=10 ** ((95 - 685) / 300)): +def log_decoding_REDLogFilm( + y: FloatingOrArrayLike, + black_offset: FloatingOrArrayLike = 10 ** ((95 - 685) / 300), +) -> FloatingOrNDArray: """ - Defines the *REDLogFilm* log decoding curve / electro-optical transfer + Define the *REDLogFilm* log decoding curve / electro-optical transfer function. Parameters ---------- - y : numeric or array_like + y Non-linear data :math:`y`. - black_offset : numeric or array_like + black_offset Black offset. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`x`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -245,24 +282,23 @@ def log_decoding_REDLogFilm(y, black_offset=10 ** ((95 - 685) / 300)): return log_decoding_Cineon(y, black_offset) -def log_encoding_Log3G10_v1(x): +def log_encoding_Log3G10_v1(x: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the *Log3G10* *v1* log encoding curve / opto-electronic transfer + Define the *Log3G10* *v1* log encoding curve / opto-electronic transfer function, the curve used in *REDCINE-X PRO Beta 42* and *Resolve 12.5.2*. Parameters ---------- - x : numeric or array_like + x Linear data :math:`x`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Non-linear data :math:`y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -289,27 +325,26 @@ def log_encoding_Log3G10_v1(x): y = np.sign(x) * 0.222497 * np.log10((np.abs(x) * 169.379333) + 1) - return from_range_1(y) + return as_float(from_range_1(y)) -def log_decoding_Log3G10_v1(y): +def log_decoding_Log3G10_v1(y: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the *Log3G10* *v1* log decoding curve / electro-optical transfer + Define the *Log3G10* *v1* log decoding curve / electro-optical transfer function, the curve used in *REDCINE-X PRO Beta 42* and *Resolve 12.5.2*. Parameters ---------- - y : numeric or array_like + y Non-linear data :math:`y`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`x`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -334,29 +369,28 @@ def log_decoding_Log3G10_v1(y): y = to_domain_1(y) - x = (np.sign(y) * (10.0 ** (np.abs(y) / 0.222497) - 1) / 169.379333) + x = np.sign(y) * (10.0 ** (np.abs(y) / 0.222497) - 1) / 169.379333 - return from_range_1(x) + return as_float(from_range_1(x)) -def log_encoding_Log3G10_v2(x): +def log_encoding_Log3G10_v2(x: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the *Log3G10* *v2* log encoding curve / opto-electronic transfer + Define the *Log3G10* *v2* log encoding curve / opto-electronic transfer function, the current curve in *REDCINE-X PRO*. Parameters ---------- - x : numeric or array_like + x Linear data :math:`x`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Non-linear data :math:`y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -381,30 +415,32 @@ def log_encoding_Log3G10_v2(x): x = to_domain_1(x) - y = (np.sign(x + 0.01) * 0.224282 * - np.log10((np.abs(x + 0.01) * 155.975327) + 1)) + y = ( + np.sign(x + 0.01) + * 0.224282 + * np.log10((np.abs(x + 0.01) * 155.975327) + 1) + ) - return from_range_1(y) + return as_float(from_range_1(y)) -def log_decoding_Log3G10_v2(y): +def log_decoding_Log3G10_v2(y: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the *Log3G10* *v2* log decoding curve / electro-optical transfer + Define the *Log3G10* *v2* log decoding curve / electro-optical transfer function, the current curve in *REDCINE-X PRO*. Parameters ---------- - y : numeric or array_like + y Non-linear data :math:`y`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`x`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -431,52 +467,158 @@ def log_decoding_Log3G10_v2(y): x = (np.sign(y) * (10.0 ** (np.abs(y) / 0.224282) - 1) / 155.975327) - 0.01 - return from_range_1(x) + return as_float(from_range_1(x)) + + +def log_encoding_Log3G10_v3(x: FloatingOrArrayLike) -> FloatingOrNDArray: + """ + Define the *Log3G10* *v3* log encoding curve / opto-electronic transfer + function, the curve described in the *RedLog3G10* Whitepaper. + + Parameters + ---------- + x + Linear data :math:`x`. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + Non-linear data :math:`y`. + + Notes + ----- + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``x`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + +------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``y`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + References + ---------- + :cite:`REDDigitalCinema2017` + + Examples + -------- + >>> log_encoding_Log3G10_v3(0.0) # doctest: +ELLIPSIS + 0.09155148... + """ + + a = 0.224282 + b = 155.975327 + c = 0.01 + g = 15.1927 + + x = to_domain_1(x) + + x = x + c + + y = np.where( + x < 0.0, x * g, np.sign(x) * a * np.log10((np.abs(x) * b) + 1.0) + ) + + return as_float(from_range_1(y)) + +def log_decoding_Log3G10_v3(y: FloatingOrArrayLike) -> FloatingOrNDArray: + """ + Define the *Log3G10* *v3* log decoding curve / electro-optical transfer + function, the curve described in the *RedLog3G10* whitepaper. -LOG3G10_ENCODING_METHODS = CaseInsensitiveMapping({ - 'v1': log_encoding_Log3G10_v1, - 'v2': log_encoding_Log3G10_v2, -}) + Parameters + ---------- + y + Non-linear data :math:`y`. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + Linear data :math:`x`. + + Notes + ----- + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``y`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + +------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``x`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + References + ---------- + :cite:`REDDigitalCinema2017` + + Examples + -------- + >>> log_decoding_Log3G10_v3(1.0) # doctest: +ELLIPSIS + 184.32234764... + """ + + a = 0.224282 + b = 155.975327 + c = 0.01 + g = 15.1927 + + y = to_domain_1(y) + + x = np.where( + y < 0.0, + (y / g) - c, + np.sign(y) * (10 ** (np.abs(y) / a) - 1.0) / b - c, + ) + + return as_float(from_range_1(x)) + + +LOG3G10_ENCODING_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "v1": log_encoding_Log3G10_v1, + "v2": log_encoding_Log3G10_v2, + "v3": log_encoding_Log3G10_v3, + } +) LOG3G10_ENCODING_METHODS.__doc__ = """ Supported *Log3G10* log encoding curve / opto-electronic transfer function methods. References ---------- -:cite:`Nattress2016a` - -LOG3G10_ENCODING_METHODS : CaseInsensitiveMapping - **{'v1', 'v2'}** +:cite:`Nattress2016a`, :cite:`REDDigitalCinema2017` """ -def log_encoding_Log3G10(x, method='v2', **kwargs): +def log_encoding_Log3G10( + x: FloatingOrArrayLike, + method: Union[Literal["v1", "v2", "v3"], str] = "v3", +) -> FloatingOrNDArray: """ - Defines the *Log3G10* log encoding curve / opto-electronic transfer + Define the *Log3G10* log encoding curve / opto-electronic transfer function. Parameters ---------- - x : numeric or array_like + x Linear data :math:`x`. - method : unicode, optional - **{'v1', 'v2'}**, + method Computation method. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Non-linear data :math:`y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -521,67 +663,59 @@ def log_encoding_Log3G10(x, method='v2', **kwargs): References ---------- - :cite:`Nattress2016a` + :cite:`Nattress2016a`, :cite:`REDDigitalCinema2017` Examples -------- >>> log_encoding_Log3G10(0.0) # doctest: +ELLIPSIS - 0.0915514... + 0.09155148... >>> log_encoding_Log3G10(0.18, method='v1') # doctest: +ELLIPSIS 0.3333336... """ - method = handle_arguments_deprecation({ - 'ArgumentRenamed': [['legacy_curve', 'method']], - }, **kwargs).get('method', method) - method = {True: 'v1', False: 'v2'}.get(method, method) + method = validate_method(method, LOG3G10_ENCODING_METHODS) return LOG3G10_ENCODING_METHODS[method](x) -LOG3G10_DECODING_METHODS = CaseInsensitiveMapping({ - 'v1': log_decoding_Log3G10_v1, - 'v2': log_decoding_Log3G10_v2, -}) +LOG3G10_DECODING_METHODS = CaseInsensitiveMapping( + { + "v1": log_decoding_Log3G10_v1, + "v2": log_decoding_Log3G10_v2, + "v3": log_decoding_Log3G10_v3, + } +) LOG3G10_DECODING_METHODS.__doc__ = """ Supported *Log3G10* log decoding curve / electro-optical transfer function methods. References ---------- -:cite:`Nattress2016a` - -LOG3G10_DECODING_METHODS : CaseInsensitiveMapping - **{'v1', 'v2'}** +:cite:`Nattress2016a`, :cite:`REDDigitalCinema2017` """ -def log_decoding_Log3G10(y, method='v2', **kwargs): +def log_decoding_Log3G10( + y, method: Union[Literal["v1", "v2", "v3"], str] = "v3" +) -> FloatingOrNDArray: """ - Defines the *Log3G10* log decoding curve / electro-optical transfer + Define the *Log3G10* log decoding curve / electro-optical transfer function. Parameters ---------- - y : numeric or array_like + y Non-linear data :math:`y`. - method : unicode, optional - **{'v1', 'v2'}**, + method Computation method. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`x`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -596,7 +730,7 @@ def log_decoding_Log3G10(y, method='v2', **kwargs): References ---------- - :cite:`Nattress2016a` + :cite:`Nattress2016a`, :cite:`REDDigitalCinema2017` Examples -------- @@ -606,32 +740,28 @@ def log_decoding_Log3G10(y, method='v2', **kwargs): 0.1799994... """ - method = handle_arguments_deprecation({ - 'ArgumentRenamed': [['legacy_curve', 'method']], - }, **kwargs).get('method', method) - method = {True: 'v1', False: 'v2'}.get(method, method) + method = validate_method(method, LOG3G10_DECODING_METHODS) return LOG3G10_DECODING_METHODS[method](y) -def log_encoding_Log3G12(x): +def log_encoding_Log3G12(x: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the *Log3G12* log encoding curve / opto-electronic transfer + Define the *Log3G12* log encoding curve / opto-electronic transfer function. Parameters ---------- - x : numeric or array_like + x Linear data :math:`x`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Non-linear data :math:`y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -646,7 +776,7 @@ def log_encoding_Log3G12(x): References ---------- - :cite:`Nattress2016a` + :cite:`Nattress2016a`, :cite:`REDDigitalCinema2017` Examples -------- @@ -658,27 +788,26 @@ def log_encoding_Log3G12(x): y = np.sign(x) * 0.184904 * np.log10((np.abs(x) * 347.189667) + 1) - return from_range_1(y) + return as_float(from_range_1(y)) -def log_decoding_Log3G12(y): +def log_decoding_Log3G12(y: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the *Log3G12* log decoding curve / electro-optical transfer + Define the *Log3G12* log decoding curve / electro-optical transfer function. Parameters ---------- - y : numeric or array_like + y Non-linear data :math:`y`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`x`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -693,7 +822,7 @@ def log_decoding_Log3G12(y): References ---------- - :cite:`Nattress2016a` + :cite:`Nattress2016a`, :cite:`REDDigitalCinema2017` Examples -------- @@ -705,4 +834,4 @@ def log_decoding_Log3G12(y): x = np.sign(y) * (10.0 ** (np.abs(y) / 0.184904) - 1) / 347.189667 - return from_range_1(x) + return as_float(from_range_1(x)) diff --git a/colour/models/rgb/transfer_functions/rimm_romm_rgb.py b/colour/models/rgb/transfer_functions/rimm_romm_rgb.py index e9fed1fcaa..90d552b1fd 100644 --- a/colour/models/rgb/transfer_functions/rimm_romm_rgb.py +++ b/colour/models/rgb/transfer_functions/rimm_romm_rgb.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ RIMM, ROMM and ERIMM Encodings ============================== Defines the *RIMM, ROMM and ERIMM* encodings opto-electrical transfer functions -(OETF / OECF) and electro-optical transfer functions (EOTF / EOCF): +(OETF) and electro-optical transfer functions (EOTF): - :func:`colour.models.cctf_encoding_ROMMRGB` - :func:`colour.models.cctf_decoding_ROMMRGB` @@ -24,52 +23,74 @@ (RIMM/ROMM RGB) (pp. 1-8). http://www.photo-lovers.org/pdf/color/romm.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.algebra import spow -from colour.utilities import (as_float, as_int, domain_range_scale, - from_range_1, to_domain_1) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import ( + Boolean, + Floating, + FloatingOrArrayLike, + FloatingOrNDArray, + Integer, + IntegerOrArrayLike, + IntegerOrNDArray, + Union, +) +from colour.utilities import ( + as_float, + as_float_scalar, + as_int, + copy_definition, + domain_range_scale, + from_range_1, + to_domain_1, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'cctf_encoding_ROMMRGB', 'cctf_decoding_ROMMRGB', - 'cctf_encoding_ProPhotoRGB', 'cctf_decoding_ProPhotoRGB', - 'cctf_encoding_RIMMRGB', 'cctf_decoding_RIMMRGB', 'log_encoding_ERIMMRGB', - 'log_decoding_ERIMMRGB' + "cctf_encoding_ROMMRGB", + "cctf_decoding_ROMMRGB", + "cctf_encoding_ProPhotoRGB", + "cctf_decoding_ProPhotoRGB", + "cctf_encoding_RIMMRGB", + "cctf_decoding_RIMMRGB", + "log_encoding_ERIMMRGB", + "log_decoding_ERIMMRGB", ] -def cctf_encoding_ROMMRGB(X, bit_depth=8, out_int=False): +def cctf_encoding_ROMMRGB( + X: FloatingOrArrayLike, bit_depth: Integer = 8, out_int: Boolean = False +) -> Union[FloatingOrNDArray, IntegerOrNDArray]: """ - Defines the *ROMM RGB* encoding colour component transfer function + Define the *ROMM RGB* encoding colour component transfer function (Encoding CCTF). Parameters ---------- - X : numeric or array_like + X Linear data :math:`X_{ROMM}`. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - out_int : bool, optional + out_int Whether to return value as integer code value or float equivalent of a code value at a given bit depth. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.integer` or :class:`numpy.ndarray` Non-linear data :math:`X'_{ROMM}`. Notes ----- - +----------------+-----------------------+---------------+ | **Domain \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ @@ -99,7 +120,7 @@ def cctf_encoding_ROMMRGB(X, bit_depth=8, out_int=False): X = to_domain_1(X) - I_max = 2 ** bit_depth - 1 + I_max = 2**bit_depth - 1 E_t = 16 ** (1.8 / (1 - 1.8)) @@ -111,29 +132,32 @@ def cctf_encoding_ROMMRGB(X, bit_depth=8, out_int=False): return as_float(from_range_1(X_p / I_max)) -def cctf_decoding_ROMMRGB(X_p, bit_depth=8, in_int=False): +def cctf_decoding_ROMMRGB( + X_p: Union[FloatingOrArrayLike, IntegerOrArrayLike], + bit_depth: Integer = 8, + in_int: Boolean = False, +) -> FloatingOrNDArray: """ - Defines the *ROMM RGB* decoding colour component transfer function + Define the *ROMM RGB* decoding colour component transfer function (Encoding CCTF). Parameters ---------- - X_p : numeric or array_like + X_p Non-linear data :math:`X'_{ROMM}`. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - in_int : bool, optional + in_int Whether to treat the input value as integer code value or float equivalent of a code value at a given bit depth. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`X_{ROMM}`. Notes ----- - +----------------+-----------------------+---------------+ | **Domain \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ @@ -163,7 +187,7 @@ def cctf_decoding_ROMMRGB(X_p, bit_depth=8, in_int=False): X_p = to_domain_1(X_p) - I_max = 2 ** bit_depth - 1 + I_max = 2**bit_depth - 1 if not in_int: X_p = X_p * I_max @@ -179,17 +203,36 @@ def cctf_decoding_ROMMRGB(X_p, bit_depth=8, in_int=False): return as_float(from_range_1(X)) -cctf_encoding_ProPhotoRGB = cctf_encoding_ROMMRGB -cctf_encoding_ProPhotoRGB.__doc__ = cctf_encoding_ProPhotoRGB.__doc__.replace( - '*ROMM RGB*', '*ProPhoto RGB*') -cctf_decoding_ProPhotoRGB = cctf_decoding_ROMMRGB -cctf_decoding_ProPhotoRGB.__doc__ = cctf_decoding_ROMMRGB.__doc__.replace( - '*ROMM RGB*', '*ProPhoto RGB*') +cctf_encoding_ProPhotoRGB = copy_definition( + cctf_encoding_ROMMRGB, "cctf_encoding_ProPhotoRGB" +) +# If-clause required for optimised python launch. +if cctf_encoding_ProPhotoRGB.__doc__ is not None: + cctf_encoding_ProPhotoRGB.__doc__ = ( + cctf_encoding_ProPhotoRGB.__doc__.replace( + "*ROMM RGB*", "*ProPhoto RGB*" + ) + ) +cctf_decoding_ProPhotoRGB = copy_definition( + cctf_decoding_ROMMRGB, "cctf_decoding_ProPhotoRGB" +) +# If-clause required for optimised python launch. +if cctf_decoding_ProPhotoRGB.__doc__ is not None: + cctf_decoding_ProPhotoRGB.__doc__ = ( + cctf_decoding_ProPhotoRGB.__doc__.replace( + "*ROMM RGB*", "*ProPhoto RGB*" + ) + ) -def cctf_encoding_RIMMRGB(X, bit_depth=8, out_int=False, E_clip=2.0): +def cctf_encoding_RIMMRGB( + X: FloatingOrArrayLike, + bit_depth: Integer = 8, + out_int: Boolean = False, + E_clip: Floating = 2.0, +) -> Union[FloatingOrNDArray, IntegerOrNDArray]: """ - Defines the *RIMM RGB* encoding colour component transfer function + Define the *RIMM RGB* encoding colour component transfer function (Encoding CCTF). *RIMM RGB* encoding non-linearity is based on that specified by @@ -197,24 +240,23 @@ def cctf_encoding_RIMMRGB(X, bit_depth=8, out_int=False, E_clip=2.0): Parameters ---------- - X : numeric or array_like + X Linear data :math:`X_{RIMM}`. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - out_int : bool, optional + out_int Whether to return value as integer code value or float equivalent of a code value at a given bit depth. - E_clip : numeric, optional + E_clip Maximum exposure level. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.integer` or :class:`numpy.ndarray` Non-linear data :math:`X'_{RIMM}`. Notes ----- - +----------------+-----------------------+---------------+ | **Domain \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ @@ -244,13 +286,15 @@ def cctf_encoding_RIMMRGB(X, bit_depth=8, out_int=False, E_clip=2.0): X = to_domain_1(X) - I_max = 2 ** bit_depth - 1 + I_max = 2**bit_depth - 1 V_clip = 1.099 * spow(E_clip, 0.45) - 0.099 q = I_max / V_clip - X_p = q * np.select([X < 0.0, X < 0.018, X >= 0.018, X > E_clip], - [0, 4.5 * X, 1.099 * spow(X, 0.45) - 0.099, I_max]) + X_p = q * np.select( + [X < 0.0, X < 0.018, X >= 0.018, X > E_clip], + [0, 4.5 * X, 1.099 * spow(X, 0.45) - 0.099, I_max], + ) if out_int: return as_int(np.round(X_p)) @@ -258,31 +302,35 @@ def cctf_encoding_RIMMRGB(X, bit_depth=8, out_int=False, E_clip=2.0): return as_float(from_range_1(X_p / I_max)) -def cctf_decoding_RIMMRGB(X_p, bit_depth=8, in_int=False, E_clip=2.0): +def cctf_decoding_RIMMRGB( + X_p: Union[FloatingOrArrayLike, IntegerOrArrayLike], + bit_depth: Integer = 8, + in_int: Boolean = False, + E_clip: Floating = 2.0, +) -> FloatingOrNDArray: """ - Defines the *RIMM RGB* decoding colour component transfer function + Define the *RIMM RGB* decoding colour component transfer function (Encoding CCTF). Parameters ---------- - X_p : numeric or array_like + X_p Non-linear data :math:`X'_{RIMM}`. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - in_int : bool, optional + in_int Whether to treat the input value as integer code value or float equivalent of a code value at a given bit depth. - E_clip : numeric, optional + E_clip Maximum exposure level. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`X_{RIMM}`. Notes ----- - +----------------+-----------------------+---------------+ | **Domain \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ @@ -312,7 +360,7 @@ def cctf_decoding_RIMMRGB(X_p, bit_depth=8, in_int=False, E_clip=2.0): X_p = to_domain_1(X_p) - I_max = 2 ** bit_depth - 1 + I_max = as_float_scalar(2**bit_depth - 1) if not in_int: X_p = X_p * I_max @@ -321,10 +369,10 @@ def cctf_decoding_RIMMRGB(X_p, bit_depth=8, in_int=False, E_clip=2.0): m = V_clip * X_p / I_max - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): X = np.where( - X_p / I_max < cctf_encoding_RIMMRGB( - 0.018, bit_depth, E_clip=E_clip), + X_p / I_max + < cctf_encoding_RIMMRGB(0.018, bit_depth, E_clip=E_clip), m / 4.5, spow((m + 0.099) / 1.099, 1 / 0.45), ) @@ -332,37 +380,38 @@ def cctf_decoding_RIMMRGB(X_p, bit_depth=8, in_int=False, E_clip=2.0): return as_float(from_range_1(X)) -def log_encoding_ERIMMRGB(X, - bit_depth=8, - out_int=False, - E_min=0.001, - E_clip=316.2): +def log_encoding_ERIMMRGB( + X: FloatingOrArrayLike, + bit_depth: Integer = 8, + out_int: Boolean = False, + E_min: Floating = 0.001, + E_clip: Floating = 316.2, +) -> Union[FloatingOrNDArray, IntegerOrNDArray]: """ - Defines the *ERIMM RGB* log encoding curve / opto-electronic transfer - function (OETF / OECF). + Define the *ERIMM RGB* log encoding curve / opto-electronic transfer + function (OETF). Parameters ---------- - X : numeric or array_like + X Linear data :math:`X_{ERIMM}`. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - out_int : bool, optional + out_int Whether to return value as integer code value or float equivalent of a code value at a given bit depth. - E_min : numeric, optional + E_min Minimum exposure limit. - E_clip : numeric, optional + E_clip Maximum exposure limit. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.integer` or :class:`numpy.ndarray` Non-linear data :math:`X'_{ERIMM}`. Notes ----- - +----------------+-----------------------+---------------+ | **Domain \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ @@ -392,23 +441,30 @@ def log_encoding_ERIMMRGB(X, X = to_domain_1(X) - I_max = 2 ** bit_depth - 1 + I_max = 2**bit_depth - 1 E_t = np.exp(1) * E_min - X_p = np.select([ - X < 0.0, - X <= E_t, - X > E_t, - X > E_clip, - ], [ - 0, - I_max * ((np.log(E_t) - np.log(E_min)) / - (np.log(E_clip) - np.log(E_min))) * (X / E_t), - I_max * ( - (np.log(X) - np.log(E_min)) / (np.log(E_clip) - np.log(E_min))), - I_max, - ]) + X_p = np.select( + [ + X < 0.0, + X <= E_t, + X > E_t, + X > E_clip, + ], + [ + 0, + I_max + * ( + (np.log(E_t) - np.log(E_min)) + / (np.log(E_clip) - np.log(E_min)) + ) + * (X / E_t), + I_max + * ((np.log(X) - np.log(E_min)) / (np.log(E_clip) - np.log(E_min))), + I_max, + ], + ) if out_int: return as_int(np.round(X_p)) @@ -416,37 +472,38 @@ def log_encoding_ERIMMRGB(X, return as_float(from_range_1(X_p / I_max)) -def log_decoding_ERIMMRGB(X_p, - bit_depth=8, - in_int=False, - E_min=0.001, - E_clip=316.2): +def log_decoding_ERIMMRGB( + X_p: Union[FloatingOrArrayLike, IntegerOrArrayLike], + bit_depth: Integer = 8, + in_int: Boolean = False, + E_min: Floating = 0.001, + E_clip: Floating = 316.2, +) -> FloatingOrNDArray: """ - Defines the *ERIMM RGB* log decoding curve / electro-optical transfer - function (EOTF / EOCF). + Define the *ERIMM RGB* log decoding curve / electro-optical transfer + function (EOTF). Parameters ---------- - X_p : numeric or array_like + X_p Non-linear data :math:`X'_{ERIMM}`. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - in_int : bool, optional + in_int Whether to treat the input value as integer code value or float equivalent of a code value at a given bit depth. - E_min : numeric, optional + E_min Minimum exposure limit. - E_clip : numeric, optional + E_clip Maximum exposure limit. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`X_{ERIMM}`. Notes ----- - +----------------+-----------------------+---------------+ | **Domain \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ @@ -476,7 +533,7 @@ def log_decoding_ERIMMRGB(X_p, X_p = to_domain_1(X_p) - I_max = 2 ** bit_depth - 1 + I_max = 2**bit_depth - 1 if not in_int: X_p = X_p * I_max @@ -484,12 +541,14 @@ def log_decoding_ERIMMRGB(X_p, E_t = np.exp(1) * E_min X = np.where( - X_p <= I_max * ( - (np.log(E_t) - np.log(E_min)) / (np.log(E_clip) - np.log(E_min))), - ((np.log(E_clip) - np.log(E_min)) / (np.log(E_t) - np.log(E_min))) * ( - (X_p * E_t) / I_max), - np.exp((X_p / I_max) * (np.log(E_clip) - np.log(E_min)) + - np.log(E_min)), + X_p + <= I_max + * ((np.log(E_t) - np.log(E_min)) / (np.log(E_clip) - np.log(E_min))), + ((np.log(E_clip) - np.log(E_min)) / (np.log(E_t) - np.log(E_min))) + * ((X_p * E_t) / I_max), + np.exp( + (X_p / I_max) * (np.log(E_clip) - np.log(E_min)) + np.log(E_min) + ), ) return as_float(from_range_1(X)) diff --git a/colour/models/rgb/transfer_functions/smpte_240m.py b/colour/models/rgb/transfer_functions/smpte_240m.py index 1eb531fcb7..82b0cef4d2 100644 --- a/colour/models/rgb/transfer_functions/smpte_240m.py +++ b/colour/models/rgb/transfer_functions/smpte_240m.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ SMPTE 240M ========== -Defines *SMPTE 240M* opto-electrical transfer function (OETF / OECF) and -electro-optical transfer function (EOTF / EOCF): +Defines the *SMPTE 240M* opto-electrical transfer function (OETF) and +electro-optical transfer function (EOTF): - :func:`colour.models.oetf_SMPTE240M` - :func:`colour.models.eotf_SMPTE240M` @@ -18,43 +17,50 @@ SMPTE%20normes%20et%20confs/s240m.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.algebra import spow -from colour.utilities import (as_float, domain_range_scale, from_range_1, - to_domain_1) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['oetf_SMPTE240M', 'eotf_SMPTE240M'] - - -def oetf_SMPTE240M(L_c): +from colour.hints import FloatingOrArrayLike, FloatingOrNDArray +from colour.utilities import ( + as_float, + domain_range_scale, + from_range_1, + to_domain_1, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "oetf_SMPTE240M", + "eotf_SMPTE240M", +] + + +def oetf_SMPTE240M(L_c: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines *SMPTE 240M* opto-electrical transfer function (OETF / OECF). + Define *SMPTE 240M* opto-electrical transfer function (OETF). Parameters ---------- - L_c : numeric or array_like + L_c Light input :math:`L_c` to the reference camera normalised to the system reference white. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Video signal output :math:`V_c` of the reference camera normalised to the system reference white. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -84,25 +90,24 @@ def oetf_SMPTE240M(L_c): return as_float(from_range_1(V_c)) -def eotf_SMPTE240M(V_r): +def eotf_SMPTE240M(V_r: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines *SMPTE 240M* electro-optical transfer function (EOTF / EOCF). + Define *SMPTE 240M* electro-optical transfer function (EOTF). Parameters ---------- - V_r : numeric or array_like + V_r Video signal level :math:`V_r` driving the reference reproducer normalised to the system reference white. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Light output :math:`L_r` from the reference reproducer normalised to the system reference white. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -127,7 +132,7 @@ def eotf_SMPTE240M(V_r): V_r = to_domain_1(V_r) - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): L_r = np.where( V_r < oetf_SMPTE240M(0.0228), V_r / 4, diff --git a/colour/models/rgb/transfer_functions/sony_slog.py b/colour/models/rgb/transfer_functions/sony_slog.py index 59fd81ec71..5ca484f3ec 100644 --- a/colour/models/rgb/transfer_functions/sony_slog.py +++ b/colour/models/rgb/transfer_functions/sony_slog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Sony S-Log Encodings ==================== @@ -23,63 +22,72 @@ large-sensor-camera-F5-F55/12359/2/TechnicalSummary_for_S-Gamut3Cine_S-Gamut3_S-Log3_V1_00.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +from colour.hints import ( + Boolean, + FloatingOrArrayLike, + FloatingOrNDArray, + Integer, +) from colour.models.rgb.transfer_functions import full_to_legal, legal_to_full -from colour.utilities import (as_float, domain_range_scale, from_range_1, - to_domain_1) -from colour.utilities.deprecation import handle_arguments_deprecation - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.utilities import ( + as_float, + as_float_array, + domain_range_scale, + from_range_1, + to_domain_1, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'log_encoding_SLog', 'log_decoding_SLog', 'log_encoding_SLog2', - 'log_decoding_SLog2', 'log_encoding_SLog3', 'log_decoding_SLog3' + "log_encoding_SLog", + "log_decoding_SLog", + "log_encoding_SLog2", + "log_decoding_SLog2", + "log_encoding_SLog3", + "log_decoding_SLog3", ] -def log_encoding_SLog(x, - bit_depth=10, - out_normalised_code_value=True, - in_reflection=True, - **kwargs): +def log_encoding_SLog( + x: FloatingOrArrayLike, + bit_depth: Integer = 10, + out_normalised_code_value: Boolean = True, + in_reflection: Boolean = True, +) -> FloatingOrNDArray: """ - Defines the *Sony S-Log* log encoding curve / opto-electronic transfer + Define the *Sony S-Log* log encoding curve / opto-electronic transfer function. Parameters ---------- - x : numeric or array_like + x Reflection or :math:`IRE / 100` input light level :math:`x` to a camera. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - out_normalised_code_value : bool, optional + out_normalised_code_value Whether the non-linear *Sony S-Log* data :math:`y` is encoded as normalised code values. - in_reflection : bool, optional + in_reflection Whether the light level :math:`x` to a camera is reflection. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Non-linear *Sony S-Log* data :math:`y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -111,10 +119,6 @@ def log_encoding_SLog(x, array([ 90, 394, 636]) """ - out_normalised_code_value = handle_arguments_deprecation({ - 'ArgumentRenamed': [['out_legal', 'out_normalised_code_value']], - }, **kwargs).get('out_normalised_code_value', out_normalised_code_value) - x = to_domain_1(x) if in_reflection: @@ -126,46 +130,41 @@ def log_encoding_SLog(x, x * 5 + 0.030001222851889303, ) - y = full_to_legal(y, bit_depth) if out_normalised_code_value else y + y_cv = full_to_legal(y, bit_depth) if out_normalised_code_value else y - return as_float(from_range_1(y)) + return as_float(from_range_1(y_cv)) -def log_decoding_SLog(y, - bit_depth=10, - in_normalised_code_value=True, - out_reflection=True, - **kwargs): +def log_decoding_SLog( + y: FloatingOrArrayLike, + bit_depth: Integer = 10, + in_normalised_code_value: Boolean = True, + out_reflection: Boolean = True, +) -> FloatingOrNDArray: """ - Defines the *Sony S-Log* log decoding curve / electro-optical transfer + Define the *Sony S-Log* log decoding curve / electro-optical transfer function. Parameters ---------- - y : numeric or array_like + y Non-linear *Sony S-Log* data :math:`y`. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - in_normalised_code_value : bool, optional + in_normalised_code_value Whether the non-linear *Sony S-Log* data :math:`y` is encoded as normalised code values. - out_reflection : bool, optional + out_reflection Whether the light level :math:`x` to a camera is reflection. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Reflection or :math:`IRE / 100` input light level :math:`x` to a camera. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -188,15 +187,11 @@ def log_decoding_SLog(y, 0.1... """ - in_normalised_code_value = handle_arguments_deprecation({ - 'ArgumentRenamed': [['in_legal', 'in_normalised_code_value']], - }, **kwargs).get('in_normalised_code_value', in_normalised_code_value) - y = to_domain_1(y) x = legal_to_full(y, bit_depth) if in_normalised_code_value else y - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): x = np.where( y >= log_encoding_SLog(0.0, bit_depth, in_normalised_code_value), 10 ** ((x - 0.616596 - 0.03) / 0.432699) - 0.037584, @@ -209,41 +204,36 @@ def log_decoding_SLog(y, return as_float(from_range_1(x)) -def log_encoding_SLog2(x, - bit_depth=10, - out_normalised_code_value=True, - in_reflection=True, - **kwargs): +def log_encoding_SLog2( + x: FloatingOrArrayLike, + bit_depth: Integer = 10, + out_normalised_code_value: Boolean = True, + in_reflection: Boolean = True, +) -> FloatingOrNDArray: """ - Defines the *Sony S-Log2* log encoding curve / opto-electronic transfer + Define the *Sony S-Log2* log encoding curve / opto-electronic transfer function. Parameters ---------- - x : numeric or array_like + x Reflection or :math:`IRE / 100` input light level :math:`x` to a camera. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - out_normalised_code_value : bool, optional + out_normalised_code_value Whether the non-linear *Sony S-Log2* data :math:`y` is encoded as normalised code values. - in_reflection : bool, optional + in_reflection Whether the light level :math:`x` to a camera is reflection. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Non-linear *Sony S-Log2* data :math:`y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -275,49 +265,43 @@ def log_encoding_SLog2(x, array([ 90, 347, 582]) """ - out_normalised_code_value = handle_arguments_deprecation({ - 'ArgumentRenamed': [['out_legal', 'out_normalised_code_value']], - }, **kwargs).get('out_normalised_code_value', out_normalised_code_value) + x = as_float_array(x) - return log_encoding_SLog(x * 155 / 219, bit_depth, - out_normalised_code_value, in_reflection) + return log_encoding_SLog( + x * 155 / 219, bit_depth, out_normalised_code_value, in_reflection + ) -def log_decoding_SLog2(y, - bit_depth=10, - in_normalised_code_value=True, - out_reflection=True, - **kwargs): +def log_decoding_SLog2( + y: FloatingOrArrayLike, + bit_depth: Integer = 10, + in_normalised_code_value: Boolean = True, + out_reflection: Boolean = True, +) -> FloatingOrNDArray: """ - Defines the *Sony S-Log2* log decoding curve / electro-optical transfer + Define the *Sony S-Log2* log decoding curve / electro-optical transfer function. Parameters ---------- - y : numeric or array_like + y Non-linear *Sony S-Log2* data :math:`y`. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - in_normalised_code_value : bool, optional + in_normalised_code_value Whether the non-linear *Sony S-Log2* data :math:`y` is encoded as normalised code values. - out_reflection : bool, optional + out_reflection Whether the light level :math:`x` to a camera is reflection. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Reflection or :math:`IRE / 100` input light level :math:`x` to a camera. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -340,49 +324,45 @@ def log_decoding_SLog2(y, 0.1... """ - in_normalised_code_value = handle_arguments_deprecation({ - 'ArgumentRenamed': [['in_legal', 'in_normalised_code_value']], - }, **kwargs).get('in_normalised_code_value', in_normalised_code_value) - - return 219 * log_decoding_SLog(y, bit_depth, in_normalised_code_value, - out_reflection) / 155 + return ( + 219 + * log_decoding_SLog( + y, bit_depth, in_normalised_code_value, out_reflection + ) + / 155 + ) -def log_encoding_SLog3(x, - bit_depth=10, - out_normalised_code_value=True, - in_reflection=True, - **kwargs): +def log_encoding_SLog3( + x: FloatingOrArrayLike, + bit_depth: Integer = 10, + out_normalised_code_value: Boolean = True, + in_reflection: Boolean = True, +) -> FloatingOrNDArray: """ - Defines the *Sony S-Log3* log encoding curve / opto-electronic transfer + Define the *Sony S-Log3* log encoding curve / opto-electronic transfer function. Parameters ---------- - x : numeric or array_like + x Reflection or :math:`IRE / 100` input light level :math:`x` to a camera. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - out_normalised_code_value : bool, optional + out_normalised_code_value Whether the non-linear *Sony S-Log3* data :math:`y` is encoded as normalised code values. - in_reflection : bool, optional + in_reflection Whether the light level :math:`x` to a camera is reflection. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Non-linear *Sony S-Log3* data :math:`y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -414,10 +394,6 @@ def log_encoding_SLog3(x, array([ 95, 420, 598]) """ - out_normalised_code_value = handle_arguments_deprecation({ - 'ArgumentRenamed': [['out_legal', 'out_normalised_code_value']], - }, **kwargs).get('out_normalised_code_value', out_normalised_code_value) - x = to_domain_1(x) if not in_reflection: @@ -429,46 +405,41 @@ def log_encoding_SLog3(x, (x * (171.2102946929 - 95) / 0.01125000 + 95) / 1023, ) - y = y if out_normalised_code_value else legal_to_full(y, bit_depth) + y_cv = y if out_normalised_code_value else legal_to_full(y, bit_depth) - return as_float(from_range_1(y)) + return as_float(from_range_1(y_cv)) -def log_decoding_SLog3(y, - bit_depth=10, - in_normalised_code_value=True, - out_reflection=True, - **kwargs): +def log_decoding_SLog3( + y: FloatingOrArrayLike, + bit_depth: Integer = 10, + in_normalised_code_value: Boolean = True, + out_reflection: Boolean = True, +) -> FloatingOrNDArray: """ - Defines the *Sony S-Log3* log decoding curve / electro-optical transfer + Define the *Sony S-Log3* log decoding curve / electro-optical transfer function. Parameters ---------- - y : numeric or array_like + y Non-linear *Sony S-Log3* data :math:`y`. - bit_depth : int, optional + bit_depth Bit depth used for conversion. - in_normalised_code_value : bool, optional + in_normalised_code_value Whether the non-linear *Sony S-Log3* data :math:`y` is encoded as normalised code values. - out_reflection : bool, optional + out_reflection Whether the light level :math:`x` to a camera is reflection. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Reflection or :math:`IRE / 100` input light level :math:`x` to a camera. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -491,10 +462,6 @@ def log_decoding_SLog3(y, 0.1... """ - in_normalised_code_value = handle_arguments_deprecation({ - 'ArgumentRenamed': [['in_legal', 'in_normalised_code_value']], - }, **kwargs).get('in_normalised_code_value', in_normalised_code_value) - y = to_domain_1(y) y = y if in_normalised_code_value else full_to_legal(y, bit_depth) diff --git a/colour/models/rgb/transfer_functions/srgb.py b/colour/models/rgb/transfer_functions/srgb.py index fb3ec490b3..669daf5a69 100644 --- a/colour/models/rgb/transfer_functions/srgb.py +++ b/colour/models/rgb/transfer_functions/srgb.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ -sRGB +SRGB ==== -Defines the *sRGB* electro-optical transfer function (EOTF / EOCF) and its +Defines the *sRGB* electro-optical transfer function (EOTF) and its inverse: - :func:`colour.models.eotf_inverse_sRGB` @@ -24,42 +23,49 @@ R-REC-BT.709-6-201506-I!!PDF-E.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.algebra import spow -from colour.utilities import (as_float, domain_range_scale, from_range_1, - to_domain_1) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['eotf_inverse_sRGB', 'eotf_sRGB'] - - -def eotf_inverse_sRGB(L): +from colour.hints import FloatingOrArrayLike, FloatingOrNDArray +from colour.utilities import ( + as_float, + domain_range_scale, + from_range_1, + to_domain_1, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "eotf_inverse_sRGB", + "eotf_sRGB", +] + + +def eotf_inverse_sRGB(L: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the *IEC 61966-2-1:1999* *sRGB* inverse electro-optical transfer - function (EOTF / EOCF). + Define the *IEC 61966-2-1:1999* *sRGB* inverse electro-optical transfer + function (EOTF). Parameters ---------- - L : numeric or array_like + L *Luminance* :math:`L` of the image. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Corresponding electrical signal :math:`V`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -90,24 +96,23 @@ def eotf_inverse_sRGB(L): return as_float(from_range_1(V)) -def eotf_sRGB(V): +def eotf_sRGB(V: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the *IEC 61966-2-1:1999* *sRGB* electro-optical transfer function - (EOTF / EOCF). + Define the *IEC 61966-2-1:1999* *sRGB* electro-optical transfer function + (EOTF). Parameters ---------- - V : numeric or array_like + V Electrical signal :math:`V`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Corresponding *luminance* :math:`L` of the image. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -133,7 +138,7 @@ def eotf_sRGB(V): V = to_domain_1(V) - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): L = np.where( V <= eotf_inverse_sRGB(0.0031308), V / 12.92, diff --git a/colour/models/rgb/transfer_functions/st_2084.py b/colour/models/rgb/transfer_functions/st_2084.py index dad9f87bc4..c638ac3242 100644 --- a/colour/models/rgb/transfer_functions/st_2084.py +++ b/colour/models/rgb/transfer_functions/st_2084.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ SMPTE ST 2084:2014 ================== -Defines *SMPTE ST 2084:2014* opto-electrical transfer function (OETF / OECF) -and electro-optical transfer function (EOTF / EOCF): +Defines the *SMPTE ST 2084:2014* opto-electrical transfer function +(OETF) and electro-optical transfer function (EOTF): - :func:`colour.models.eotf_ST2084` - :func:`colour.models.eotf_inverse_ST2084` @@ -21,56 +20,64 @@ Displays (pp. 1-14). doi:10.5594/SMPTE.ST2084.2014 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from colour.algebra import spow -from colour.utilities import Structure, from_range_1, to_domain_1 - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['CONSTANTS_ST2084', 'eotf_inverse_ST2084', 'eotf_ST2084'] - -CONSTANTS_ST2084 = Structure( +from colour.hints import Floating, FloatingOrArrayLike, FloatingOrNDArray +from colour.utilities import Structure, as_float_array, as_float + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "CONSTANTS_ST2084", + "eotf_inverse_ST2084", + "eotf_ST2084", +] + +CONSTANTS_ST2084: Structure = Structure( m_1=2610 / 4096 * (1 / 4), m_2=2523 / 4096 * 128, c_1=3424 / 4096, c_2=2413 / 4096 * 32, - c_3=2392 / 4096 * 32) + c_3=2392 / 4096 * 32, +) """ Constants for *SMPTE ST 2084:2014* inverse electro-optical transfer function -(EOTF / EOCF) and electro-optical transfer function (EOTF / EOCF). - -CONSTANTS_ST2084 : Structure +(EOTF) and electro-optical transfer function (EOTF). """ -def eotf_inverse_ST2084(C, L_p=10000, constants=CONSTANTS_ST2084): +def eotf_inverse_ST2084( + C: FloatingOrArrayLike, + L_p: Floating = 10000, + constants: Structure = CONSTANTS_ST2084, +) -> FloatingOrNDArray: """ - Defines *SMPTE ST 2084:2014* optimised perceptual inverse electro-optical - transfer function (EOTF / EOCF). + Define *SMPTE ST 2084:2014* optimised perceptual inverse electro-optical + transfer function (EOTF). Parameters ---------- - C : numeric or array_like + C Target optical output :math:`C` in :math:`cd/m^2` of the ideal reference display. - L_p : numeric, optional + L_p System peak luminance :math:`cd/m^2`, this parameter should stay at its default :math:`10000 cd/m^2` value for practical applications. It is exposed so that the definition can be used as a fitting function. - constants : Structure, optional + constants *SMPTE ST 2084:2014* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Color value abbreviated as :math:`N`, that is directly proportional to the encoded signal representation, and which is not directly proportional to the optical output of a display device. @@ -81,12 +88,11 @@ def eotf_inverse_ST2084(C, L_p=10000, constants=CONSTANTS_ST2084): Notes ----- - - *SMPTE ST 2084:2014* is an absolute transfer function, thus the domain and range values for the *Reference* and *1* scales are only indicative that the data is not affected by scale transformations. The effective domain of *SMPTE ST 2084:2014* inverse electro-optical - transfer function (EOTF / EOCF) is [0.0001, 10000]. + transfer function (EOTF) is [0.0001, 10000]. +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | @@ -111,40 +117,46 @@ def eotf_inverse_ST2084(C, L_p=10000, constants=CONSTANTS_ST2084): 0.5080784... """ - C = to_domain_1(C) + C = as_float_array(C) Y_p = spow(C / L_p, constants.m_1) - N = spow((constants.c_1 + constants.c_2 * Y_p) / (constants.c_3 * Y_p + 1), - constants.m_2) + N = spow( + (constants.c_1 + constants.c_2 * Y_p) / (constants.c_3 * Y_p + 1), + constants.m_2, + ) - return from_range_1(N) + return as_float(N) -def eotf_ST2084(N, L_p=10000, constants=CONSTANTS_ST2084): +def eotf_ST2084( + N: FloatingOrArrayLike, + L_p: Floating = 10000, + constants: Structure = CONSTANTS_ST2084, +) -> FloatingOrNDArray: """ - Defines *SMPTE ST 2084:2014* optimised perceptual electro-optical transfer - function (EOTF / EOCF). + Define *SMPTE ST 2084:2014* optimised perceptual electro-optical transfer + function (EOTF). This perceptual quantizer (PQ) has been modeled by Dolby Laboratories using *Barten (1999)* contrast sensitivity function. Parameters ---------- - N : numeric or array_like + N Color value abbreviated as :math:`N`, that is directly proportional to the encoded signal representation, and which is not directly proportional to the optical output of a display device. - L_p : numeric, optional + L_p System peak luminance :math:`cd/m^2`, this parameter should stay at its default :math:`10000 cd/m^2` value for practical applications. It is exposed so that the definition can be used as a fitting function. - constants : Structure, optional + constants *SMPTE ST 2084:2014* constants. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Target optical output :math:`C` in :math:`cd/m^2` of the ideal reference display. @@ -154,7 +166,6 @@ def eotf_ST2084(N, L_p=10000, constants=CONSTANTS_ST2084): Notes ----- - - *SMPTE ST 2084:2014* is an absolute transfer function, thus the domain and range values for the *Reference* and *1* scales are only indicative that the data is not affected by scale transformations. @@ -182,7 +193,7 @@ def eotf_ST2084(N, L_p=10000, constants=CONSTANTS_ST2084): 100.0000000... """ - N = to_domain_1(N) + N = as_float_array(N) m_1_d = 1 / constants.m_1 m_2_d = 1 / constants.m_2 @@ -196,4 +207,4 @@ def eotf_ST2084(N, L_p=10000, constants=CONSTANTS_ST2084): L = spow((n / (constants.c_2 - constants.c_3 * V_p)), m_1_d) C = L_p * L - return from_range_1(C) + return as_float(C) diff --git a/colour/models/rgb/transfer_functions/tests/__init__.py b/colour/models/rgb/transfer_functions/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/models/rgb/transfer_functions/tests/__init__.py +++ b/colour/models/rgb/transfer_functions/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/models/rgb/transfer_functions/tests/test__init__.py b/colour/models/rgb/transfer_functions/tests/test__init__.py index 3eab00d642..9243bf3a37 100644 --- a/colour/models/rgb/transfer_functions/tests/test__init__.py +++ b/colour/models/rgb/transfer_functions/tests/test__init__.py @@ -1,78 +1,106 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.common` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.common` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from colour.models.rgb.transfer_functions import ( - CCTF_DECODINGS, CCTF_ENCODINGS, EOTFS, EOTF_INVERSES, LOG_DECODINGS, - LOG_ENCODINGS, OETFS, OETF_INVERSES, OOTFS, OOTF_INVERSES, cctf_encoding, - cctf_decoding) -from colour.utilities import as_int - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Development' - -__all__ = ['TestCctfEncoding', 'TestCctfDecoding', 'TestTransferFunctions'] + CCTF_DECODINGS, + CCTF_ENCODINGS, + EOTFS, + EOTF_INVERSES, + LOG_DECODINGS, + LOG_ENCODINGS, + OETFS, + OETF_INVERSES, + OOTFS, + OOTF_INVERSES, + cctf_encoding, + cctf_decoding, +) +from colour.utilities import ColourUsageWarning, as_int + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Development" + +__all__ = [ + "TestCctfEncoding", + "TestCctfDecoding", + "TestTransferFunctions", +] class TestCctfEncoding(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.cctf_encoding` + Define :func:`colour.models.rgb.transfer_functions.cctf_encoding` definition unit tests methods. """ def test_raise_exception_cctf_encoding(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_encoding_ACESproxy` definition raised exception. """ - # TODO: Use "assertWarns" when dropping Python 2.7. - cctf_encoding(0.18, 'ITU-R BT.2100 HLG') - cctf_encoding(0.18, 'ITU-R BT.2100 PQ') + self.assertWarns( + ColourUsageWarning, + cctf_encoding, + 0.18, + function="ITU-R BT.2100 HLG", + ) + self.assertWarns( + ColourUsageWarning, + cctf_encoding, + 0.18, + function="ITU-R BT.2100 PQ", + ) class TestCctfDecoding(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.cctf_decoding` + Define :func:`colour.models.rgb.transfer_functions.cctf_decoding` definition unit tests methods. """ def test_raise_exception_cctf_decoding(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_encoding_ACESproxy` definition raised exception. """ - # TODO: Use "assertWarns" when dropping Python 2.7. - cctf_decoding(0.18, 'ITU-R BT.2100 HLG') - cctf_decoding(0.18, 'ITU-R BT.2100 PQ') + self.assertWarns( + ColourUsageWarning, + cctf_decoding, + 0.18, + function="ITU-R BT.2100 HLG", + ) + self.assertWarns( + ColourUsageWarning, + cctf_decoding, + 0.18, + function="ITU-R BT.2100 PQ", + ) class TestTransferFunctions(unittest.TestCase): - """ - Defines transfer functions unit tests methods. - """ + """Define the transfer functions unit tests methods.""" def test_transfer_functions(self): - """ - Tests transfer functions reciprocity. - """ + """Test the transfer functions reciprocity.""" - ignored_transfer_functions = ('ACESproxy', 'DICOM GSDF', - 'Filmic Pro 6') + ignored_transfer_functions = ( + "ACESproxy", + "DICOM GSDF", + "Filmic Pro 6", + ) - decimals = {'D-Log': 1, 'F-Log': 4} + decimals = {"D-Log": 1, "F-Log": 4, "N-Log": 3} reciprocal_mappings = [ (LOG_ENCODINGS, LOG_DECODINGS), @@ -82,10 +110,9 @@ def test_transfer_functions(self): (OOTFS, OOTF_INVERSES), ] - samples = np.hstack([ - np.linspace(0, 1, as_int(1e5)), - np.linspace(0, 65504, 65504 * 10) - ]) + samples = np.hstack( + [np.linspace(0, 1, as_int(1e5)), np.linspace(0, 65504, 65504 * 10)] + ) for encoding_mapping, _decoding_mapping in reciprocal_mappings: for name in encoding_mapping: @@ -96,8 +123,9 @@ def test_transfer_functions(self): decoded_s = CCTF_DECODINGS[name](encoded_s) np.testing.assert_almost_equal( - samples, decoded_s, decimal=decimals.get(name, 7)) + samples, decoded_s, decimal=decimals.get(name, 7) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_aces.py b/colour/models/rgb/transfer_functions/tests/test_aces.py index 40f7732358..e40647d8b9 100644 --- a/colour/models/rgb/transfer_functions/tests/test_aces.py +++ b/colour/models/rgb/transfer_functions/tests/test_aces.py @@ -1,63 +1,71 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.aces` +Defines the unit tests for the :mod:`colour.models.rgb.transfer_functions.aces` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from colour.models.rgb.transfer_functions import ( - log_encoding_ACESproxy, log_decoding_ACESproxy, log_encoding_ACEScc, - log_decoding_ACEScc, log_encoding_ACEScct, log_decoding_ACEScct) + log_encoding_ACESproxy, + log_decoding_ACESproxy, + log_encoding_ACEScc, + log_decoding_ACEScc, + log_encoding_ACEScct, + log_decoding_ACEScct, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestLogEncoding_ACESproxy', 'TestLogDecoding_ACESproxy', - 'TestLogEncoding_ACEScc', 'TestLogDecoding_ACEScc', - 'TestLogDecoding_ACEScct' + "TestLogEncoding_ACESproxy", + "TestLogDecoding_ACESproxy", + "TestLogEncoding_ACEScc", + "TestLogDecoding_ACEScc", + "TestLogDecoding_ACEScct", ] class TestLogEncoding_ACESproxy(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.aces.\ + Define :func:`colour.models.rgb.transfer_functions.aces.\ log_encoding_ACESproxy` definition unit tests methods. """ def test_log_encoding_ACESproxy(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_encoding_ACESproxy` definition. """ self.assertAlmostEqual( - log_encoding_ACESproxy(0.0), 0.062561094819159, places=7) + log_encoding_ACESproxy(0.0), 0.062561094819159, places=7 + ) self.assertAlmostEqual( - log_encoding_ACESproxy(0.18), 0.416422287390029, places=7) + log_encoding_ACESproxy(0.18), 0.416422287390029, places=7 + ) self.assertAlmostEqual( - log_encoding_ACESproxy(0.18, 12), 0.416361416361416, places=7) + log_encoding_ACESproxy(0.18, 12), 0.416361416361416, places=7 + ) self.assertAlmostEqual( - log_encoding_ACESproxy(1.0), 0.537634408602151, places=7) + log_encoding_ACESproxy(1.0), 0.537634408602151, places=7 + ) self.assertEqual(log_encoding_ACESproxy(0.18, out_int=True), 426) def test_n_dimensional_log_encoding_ACESproxy(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_encoding_ACESproxy` definition n-dimensional arrays support. """ @@ -67,56 +75,61 @@ def test_n_dimensional_log_encoding_ACESproxy(self): lin_AP1 = np.tile(lin_AP1, 6) ACESproxy = np.tile(ACESproxy, 6) np.testing.assert_almost_equal( - log_encoding_ACESproxy(lin_AP1), ACESproxy, decimal=7) + log_encoding_ACESproxy(lin_AP1), ACESproxy, decimal=7 + ) lin_AP1 = np.reshape(lin_AP1, (2, 3)) ACESproxy = np.reshape(ACESproxy, (2, 3)) np.testing.assert_almost_equal( - log_encoding_ACESproxy(lin_AP1), ACESproxy, decimal=7) + log_encoding_ACESproxy(lin_AP1), ACESproxy, decimal=7 + ) lin_AP1 = np.reshape(lin_AP1, (2, 3, 1)) ACESproxy = np.reshape(ACESproxy, (2, 3, 1)) np.testing.assert_almost_equal( - log_encoding_ACESproxy(lin_AP1), ACESproxy, decimal=7) + log_encoding_ACESproxy(lin_AP1), ACESproxy, decimal=7 + ) def test_domain_range_scale_log_encoding_ACESproxy(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_encoding_ACESproxy` definition domain and range scale support. """ lin_AP1 = 0.18 ACESproxy = log_encoding_ACESproxy(lin_AP1) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( log_encoding_ACESproxy(lin_AP1 * factor), ACESproxy * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_log_encoding_ACESproxy(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_encoding_ACESproxy` definition nan support. """ log_encoding_ACESproxy( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_ACESproxy(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.aces.\ + Define :func:`colour.models.rgb.transfer_functions.aces.\ log_decoding_ACESproxy` definition unit tests methods. """ def test_log_decoding_ACESproxy(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_decoding_ACESproxy` definition. """ @@ -124,35 +137,40 @@ def test_log_decoding_ACESproxy(self): log_decoding_ACESproxy(0.062561094819159), 0.0, atol=0.01, - rtol=0.01) + rtol=0.01, + ) np.testing.assert_allclose( log_decoding_ACESproxy(0.416422287390029), 0.18, atol=0.01, - rtol=0.01) + rtol=0.01, + ) np.testing.assert_allclose( log_decoding_ACESproxy(0.416361416361416, 12), 0.18, atol=0.01, - rtol=0.01) + rtol=0.01, + ) np.testing.assert_allclose( log_decoding_ACESproxy(0.537634408602151), 1.0, atol=0.01, - rtol=0.01) + rtol=0.01, + ) np.testing.assert_allclose( log_decoding_ACESproxy(426, in_int=True), 0.18, atol=0.01, - rtol=0.01) + rtol=0.01, + ) def test_n_dimensional_log_decoding_ACESproxy(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_decoding_ACESproxy` definition n-dimensional arrays support. """ @@ -162,70 +180,78 @@ def test_n_dimensional_log_decoding_ACESproxy(self): ACESproxy = np.tile(ACESproxy, 6) lin_AP1 = np.tile(lin_AP1, 6) np.testing.assert_almost_equal( - log_decoding_ACESproxy(ACESproxy), lin_AP1, decimal=7) + log_decoding_ACESproxy(ACESproxy), lin_AP1, decimal=7 + ) ACESproxy = np.reshape(ACESproxy, (2, 3)) lin_AP1 = np.reshape(lin_AP1, (2, 3)) np.testing.assert_almost_equal( - log_decoding_ACESproxy(ACESproxy), lin_AP1, decimal=7) + log_decoding_ACESproxy(ACESproxy), lin_AP1, decimal=7 + ) ACESproxy = np.reshape(ACESproxy, (2, 3, 1)) lin_AP1 = np.reshape(lin_AP1, (2, 3, 1)) np.testing.assert_almost_equal( - log_decoding_ACESproxy(ACESproxy), lin_AP1, decimal=7) + log_decoding_ACESproxy(ACESproxy), lin_AP1, decimal=7 + ) def test_domain_range_scale_log_decoding_ACESproxy(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_decoding_ACESproxy` definition domain and range scale support. """ ACESproxy = 426.0 lin_AP1 = log_decoding_ACESproxy(ACESproxy) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( log_decoding_ACESproxy(ACESproxy * factor), lin_AP1 * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_log_decoding_ACESproxy(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_decoding_ACESproxy` definition nan support. """ log_decoding_ACESproxy( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogEncoding_ACEScc(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.aces.\ + Define :func:`colour.models.rgb.transfer_functions.aces.\ log_encoding_ACEScc` definition unit tests methods. """ def test_log_encoding_ACEScc(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_encoding_ACEScc` definition. """ self.assertAlmostEqual( - log_encoding_ACEScc(0.0), -0.358447488584475, places=7) + log_encoding_ACEScc(0.0), -0.358447488584475, places=7 + ) self.assertAlmostEqual( - log_encoding_ACEScc(0.18), 0.413588402492442, places=7) + log_encoding_ACEScc(0.18), 0.413588402492442, places=7 + ) self.assertAlmostEqual( - log_encoding_ACEScc(1.0), 0.554794520547945, places=7) + log_encoding_ACEScc(1.0), 0.554794520547945, places=7 + ) def test_n_dimensional_log_encoding_ACEScc(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_encoding_ACEScc` definition n-dimensional arrays support. """ @@ -235,70 +261,78 @@ def test_n_dimensional_log_encoding_ACEScc(self): lin_AP1 = np.tile(lin_AP1, 6) ACEScc = np.tile(ACEScc, 6) np.testing.assert_almost_equal( - log_encoding_ACEScc(lin_AP1), ACEScc, decimal=7) + log_encoding_ACEScc(lin_AP1), ACEScc, decimal=7 + ) lin_AP1 = np.reshape(lin_AP1, (2, 3)) ACEScc = np.reshape(ACEScc, (2, 3)) np.testing.assert_almost_equal( - log_encoding_ACEScc(lin_AP1), ACEScc, decimal=7) + log_encoding_ACEScc(lin_AP1), ACEScc, decimal=7 + ) lin_AP1 = np.reshape(lin_AP1, (2, 3, 1)) ACEScc = np.reshape(ACEScc, (2, 3, 1)) np.testing.assert_almost_equal( - log_encoding_ACEScc(lin_AP1), ACEScc, decimal=7) + log_encoding_ACEScc(lin_AP1), ACEScc, decimal=7 + ) def test_domain_range_scale_log_encoding_ACEScc(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_encoding_ACEScc` definition domain and range scale support. """ lin_AP1 = 0.18 ACEScc = log_encoding_ACEScc(lin_AP1) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( log_encoding_ACEScc(lin_AP1 * factor), ACEScc * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_log_encoding_ACEScc(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_encoding_ACEScc` definition nan support. """ log_encoding_ACEScc( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_ACEScc(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.aces.\ + Define :func:`colour.models.rgb.transfer_functions.aces.\ log_decoding_ACEScc` definition unit tests methods. """ def test_log_decoding_ACEScc(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_decoding_ACEScc` definition. """ self.assertAlmostEqual( - log_decoding_ACEScc(-0.358447488584475), 0.0, places=7) + log_decoding_ACEScc(-0.358447488584475), 0.0, places=7 + ) self.assertAlmostEqual( - log_decoding_ACEScc(0.413588402492442), 0.18, places=7) + log_decoding_ACEScc(0.413588402492442), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_ACEScc(0.554794520547945), 1.0, places=7) + log_decoding_ACEScc(0.554794520547945), 1.0, places=7 + ) def test_n_dimensional_log_decoding_ACEScc(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_decoding_ACEScc` definition n-dimensional arrays support. """ @@ -308,70 +342,78 @@ def test_n_dimensional_log_decoding_ACEScc(self): ACEScc = np.tile(ACEScc, 6) lin_AP1 = np.tile(lin_AP1, 6) np.testing.assert_almost_equal( - log_decoding_ACEScc(ACEScc), lin_AP1, decimal=7) + log_decoding_ACEScc(ACEScc), lin_AP1, decimal=7 + ) ACEScc = np.reshape(ACEScc, (2, 3)) lin_AP1 = np.reshape(lin_AP1, (2, 3)) np.testing.assert_almost_equal( - log_decoding_ACEScc(ACEScc), lin_AP1, decimal=7) + log_decoding_ACEScc(ACEScc), lin_AP1, decimal=7 + ) ACEScc = np.reshape(ACEScc, (2, 3, 1)) lin_AP1 = np.reshape(lin_AP1, (2, 3, 1)) np.testing.assert_almost_equal( - log_decoding_ACEScc(ACEScc), lin_AP1, decimal=7) + log_decoding_ACEScc(ACEScc), lin_AP1, decimal=7 + ) def test_domain_range_scale_log_decoding_ACEScc(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_decoding_ACEScc` definition domain and range scale support. """ ACEScc = 0.413588402492442 lin_AP1 = log_decoding_ACEScc(ACEScc) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( log_decoding_ACEScc(ACEScc * factor), lin_AP1 * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_log_decoding_ACEScc(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_decoding_ACEScc` definition nan support. """ log_decoding_ACEScc( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogEncoding_ACEScct(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.aces.\ + Define :func:`colour.models.rgb.transfer_functions.aces.\ log_encoding_ACEScct` definition unit tests methods. """ def test_log_encoding_ACEScct(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_encoding_ACEScct` definition. """ self.assertAlmostEqual( - log_encoding_ACEScct(0.0), 0.072905534195835495, places=7) + log_encoding_ACEScct(0.0), 0.072905534195835495, places=7 + ) self.assertAlmostEqual( - log_encoding_ACEScct(0.18), 0.413588402492442, places=7) + log_encoding_ACEScct(0.18), 0.413588402492442, places=7 + ) self.assertAlmostEqual( - log_encoding_ACEScct(1.0), 0.554794520547945, places=7) + log_encoding_ACEScct(1.0), 0.554794520547945, places=7 + ) def test_n_dimensional_log_encoding_ACEScct(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_encoding_ACEScct` definition n-dimensional arrays support. """ @@ -381,82 +423,92 @@ def test_n_dimensional_log_encoding_ACEScct(self): lin_AP1 = np.tile(lin_AP1, 6) ACEScct = np.tile(ACEScct, 6) np.testing.assert_almost_equal( - log_encoding_ACEScct(lin_AP1), ACEScct, decimal=7) + log_encoding_ACEScct(lin_AP1), ACEScct, decimal=7 + ) lin_AP1 = np.reshape(lin_AP1, (2, 3)) ACEScct = np.reshape(ACEScct, (2, 3)) np.testing.assert_almost_equal( - log_encoding_ACEScct(lin_AP1), ACEScct, decimal=7) + log_encoding_ACEScct(lin_AP1), ACEScct, decimal=7 + ) lin_AP1 = np.reshape(lin_AP1, (2, 3, 1)) ACEScct = np.reshape(ACEScct, (2, 3, 1)) np.testing.assert_almost_equal( - log_encoding_ACEScct(lin_AP1), ACEScct, decimal=7) + log_encoding_ACEScct(lin_AP1), ACEScct, decimal=7 + ) def test_domain_range_scale_log_encoding_ACEScct(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_encoding_ACEScct` definition domain and range scale support. """ lin_AP1 = 0.18 ACEScct = log_encoding_ACEScct(lin_AP1) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( log_encoding_ACEScct(lin_AP1 * factor), ACEScct * factor, - decimal=7) + decimal=7, + ) def test_ACEScc_equivalency_log_encoding_ACEScct(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_encoding_ACEScct` definition ACEScc equivalency, and explicit requirement -specified by AMPAS ACES specification S-2016-001 (https://github.com/ampas/\ -aces-dev/blob/v1.0.3/documents/LaTeX/S-2016-001/introduction.tex#L14) + specified by AMPAS ACES specification S-2016-001 + (https://github.com/ampas/aces-dev/blob/v1.0.3/documents/LaTeX/\ +S-2016-001/introduction.tex#L14) """ equiv = np.linspace(0.0078125, 222.86094420380761, 100) np.testing.assert_almost_equal( - log_encoding_ACEScct(equiv), log_encoding_ACEScc(equiv), decimal=7) + log_encoding_ACEScct(equiv), log_encoding_ACEScc(equiv), decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_ACEScct(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_encoding_ACEScct` definition nan support. """ log_encoding_ACEScct( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_ACEScct(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.aces.\ + Define :func:`colour.models.rgb.transfer_functions.aces.\ log_decoding_ACEScct` definition unit tests methods. """ def test_log_decoding_ACEScct(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_decoding_ACEScct` definition. """ self.assertAlmostEqual( - log_decoding_ACEScct(0.072905534195835495), 0.0, places=7) + log_decoding_ACEScct(0.072905534195835495), 0.0, places=7 + ) self.assertAlmostEqual( - log_decoding_ACEScct(0.41358840249244228), 0.18, places=7) + log_decoding_ACEScct(0.41358840249244228), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_ACEScct(0.554794520547945), 1.0, places=7) + log_decoding_ACEScct(0.554794520547945), 1.0, places=7 + ) def test_n_dimensional_log_decoding_ACEScct(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_decoding_ACEScct` definition n-dimensional arrays support. """ @@ -466,57 +518,64 @@ def test_n_dimensional_log_decoding_ACEScct(self): ACEScct = np.tile(ACEScct, 6) lin_AP1 = np.tile(lin_AP1, 6) np.testing.assert_almost_equal( - log_decoding_ACEScct(ACEScct), lin_AP1, decimal=7) + log_decoding_ACEScct(ACEScct), lin_AP1, decimal=7 + ) ACEScct = np.reshape(ACEScct, (2, 3)) lin_AP1 = np.reshape(lin_AP1, (2, 3)) np.testing.assert_almost_equal( - log_decoding_ACEScct(ACEScct), lin_AP1, decimal=7) + log_decoding_ACEScct(ACEScct), lin_AP1, decimal=7 + ) ACEScct = np.reshape(ACEScct, (2, 3, 1)) lin_AP1 = np.reshape(lin_AP1, (2, 3, 1)) np.testing.assert_almost_equal( - log_decoding_ACEScct(ACEScct), lin_AP1, decimal=7) + log_decoding_ACEScct(ACEScct), lin_AP1, decimal=7 + ) def test_domain_range_scale_log_decoding_ACEScct(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_decoding_ACEScct` definition domain and range scale support. """ ACEScc = 0.413588402492442 lin_AP1 = log_decoding_ACEScct(ACEScc) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( log_decoding_ACEScct(ACEScc * factor), lin_AP1 * factor, - decimal=7) + decimal=7, + ) def test_ACEScc_equivalency_log_decoding_ACEScct(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_decoding_ACEScct` definition ACEScc equivalency, and explicit requirement -specified by AMPAS ACES specification S-2016-001 (https://github.com/ampas/\ -aces-dev/blob/v1.0.3/documents/LaTeX/S-2016-001/introduction.tex#L14) + specified by AMPAS ACES specification S-2016-001 + (https://github.com/ampas/aces-dev/blob/v1.0.3/documents/LaTeX/\ +S-2016-001/introduction.tex#L14) """ equiv = np.linspace(0.15525114155251146, 1.0, 100) np.testing.assert_almost_equal( - log_decoding_ACEScct(equiv), log_decoding_ACEScc(equiv), decimal=7) + log_decoding_ACEScct(equiv), log_decoding_ACEScc(equiv), decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_ACEScct(self): """ - Tests :func:`colour.models.rgb.transfer_functions.aces.\ + Test :func:`colour.models.rgb.transfer_functions.aces.\ log_decoding_ACEScct` definition nan support. """ log_decoding_ACEScct( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_arib_std_b67.py b/colour/models/rgb/transfer_functions/tests/test_arib_std_b67.py index 1e1ddeb014..69af7c5fde 100644 --- a/colour/models/rgb/transfer_functions/tests/test_arib_std_b67.py +++ b/colour/models/rgb/transfer_functions/tests/test_arib_std_b67.py @@ -1,37 +1,39 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.arib_std_b67` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.arib_std_b67` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.models.rgb.transfer_functions import (oetf_ARIBSTDB67, - oetf_inverse_ARIBSTDB67) +from colour.models.rgb.transfer_functions import ( + oetf_ARIBSTDB67, + oetf_inverse_ARIBSTDB67, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestOetf_ARIBSTDB67', 'TestOetf_inverse_ARIBSTDB67'] +__all__ = [ + "TestOetf_ARIBSTDB67", + "TestOetf_inverse_ARIBSTDB67", +] class TestOetf_ARIBSTDB67(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ + Define :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ oetf_ARIBSTDB67` definition unit tests methods. """ def test_oetf_ARIBSTDB67(self): """ - Tests :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ + Test :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ oetf_ARIBSTDB67` definition. """ @@ -40,16 +42,18 @@ def test_oetf_ARIBSTDB67(self): self.assertAlmostEqual(oetf_ARIBSTDB67(0.0), 0.0, places=7) self.assertAlmostEqual( - oetf_ARIBSTDB67(0.18), 0.212132034355964, places=7) + oetf_ARIBSTDB67(0.18), 0.212132034355964, places=7 + ) self.assertAlmostEqual(oetf_ARIBSTDB67(1.0), 0.5, places=7) self.assertAlmostEqual( - oetf_ARIBSTDB67(64.0), 1.302858098046995, places=7) + oetf_ARIBSTDB67(64.0), 1.302858098046995, places=7 + ) def test_n_dimensional_oetf_ARIBSTDB67(self): """ - Tests :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ + Test :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ oetf_ARIBSTDB67` definition n-dimensional arrays support. """ @@ -70,23 +74,24 @@ def test_n_dimensional_oetf_ARIBSTDB67(self): def test_domain_range_scale_oetf_ARIBSTDB67(self): """ - Tests :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ + Test :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ oetf_ARIBSTDB67` definition domain and range scale support. """ E = 0.18 E_p = oetf_ARIBSTDB67(E) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - oetf_ARIBSTDB67(E * factor), E_p * factor, decimal=7) + oetf_ARIBSTDB67(E * factor), E_p * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_oetf_ARIBSTDB67(self): """ - Tests :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ + Test :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ oetf_ARIBSTDB67` definition nan support. """ @@ -95,13 +100,13 @@ def test_nan_oetf_ARIBSTDB67(self): class TestOetf_inverse_ARIBSTDB67(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ + Define :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ oetf_inverse_ARIBSTDB67` definition unit tests methods. """ def test_oetf_inverse_ARIBSTDB67(self): """ - Tests :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ + Test :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ oetf_inverse_ARIBSTDB67` definition. """ @@ -110,16 +115,18 @@ def test_oetf_inverse_ARIBSTDB67(self): self.assertAlmostEqual(oetf_inverse_ARIBSTDB67(0.0), 0.0, places=7) self.assertAlmostEqual( - oetf_inverse_ARIBSTDB67(0.212132034355964), 0.18, places=7) + oetf_inverse_ARIBSTDB67(0.212132034355964), 0.18, places=7 + ) self.assertAlmostEqual(oetf_inverse_ARIBSTDB67(0.5), 1.0, places=7) self.assertAlmostEqual( - oetf_inverse_ARIBSTDB67(1.302858098046995), 64.0, places=7) + oetf_inverse_ARIBSTDB67(1.302858098046995), 64.0, places=7 + ) def test_n_dimensional_oetf_inverse_ARIBSTDB67(self): """ - Tests :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ + Test :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ oetf_inverse_ARIBSTDB67` definition n-dimensional arrays support. """ @@ -129,45 +136,50 @@ def test_n_dimensional_oetf_inverse_ARIBSTDB67(self): E_p = np.tile(E_p, 6) E = np.tile(E, 6) np.testing.assert_almost_equal( - oetf_inverse_ARIBSTDB67(E_p), E, decimal=7) + oetf_inverse_ARIBSTDB67(E_p), E, decimal=7 + ) E_p = np.reshape(E_p, (2, 3)) E = np.reshape(E, (2, 3)) np.testing.assert_almost_equal( - oetf_inverse_ARIBSTDB67(E_p), E, decimal=7) + oetf_inverse_ARIBSTDB67(E_p), E, decimal=7 + ) E_p = np.reshape(E_p, (2, 3, 1)) E = np.reshape(E, (2, 3, 1)) np.testing.assert_almost_equal( - oetf_inverse_ARIBSTDB67(E_p), E, decimal=7) + oetf_inverse_ARIBSTDB67(E_p), E, decimal=7 + ) def test_domain_range_scale_oetf_inverse_ARIBSTDB67(self): """ - Tests :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ + Test :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ oetf_inverse_ARIBSTDB67` definition domain and range scale support. """ E_p = 0.212132034355964 E = oetf_inverse_ARIBSTDB67(E_p) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( oetf_inverse_ARIBSTDB67(E_p * factor), E * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_oetf_inverse_ARIBSTDB67(self): """ - Tests :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ + Test :func:`colour.models.rgb.transfer_functions.arib_std_b67.\ oetf_inverse_ARIBSTDB67` definition nan support. """ oetf_inverse_ARIBSTDB67( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_arri_alexa_log_c.py b/colour/models/rgb/transfer_functions/tests/test_arri_alexa_log_c.py index fa17229ec8..aa5b86fa09 100644 --- a/colour/models/rgb/transfer_functions/tests/test_arri_alexa_log_c.py +++ b/colour/models/rgb/transfer_functions/tests/test_arri_alexa_log_c.py @@ -1,52 +1,57 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for +Defines the unit tests for the :mod:`colour.models.rgb.transfer_functions.arri_alexa_log_c` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.models.rgb.transfer_functions import (log_encoding_ALEXALogC, - log_decoding_ALEXALogC) +from colour.models.rgb.transfer_functions import ( + log_encoding_ALEXALogC, + log_decoding_ALEXALogC, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestLogEncoding_ALEXALogC', 'TestLogDecoding_ALEXALogC'] +__all__ = [ + "TestLogEncoding_ALEXALogC", + "TestLogDecoding_ALEXALogC", +] class TestLogEncoding_ALEXALogC(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ + Define :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ log_encoding_ALEXALogC` definition unit tests methods. """ def test_log_encoding_ALEXALogC(self): """ - Tests :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ + Test :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ log_encoding_ALEXALogC` definition. """ self.assertAlmostEqual( - log_encoding_ALEXALogC(0.0), 0.092809000000000, places=7) + log_encoding_ALEXALogC(0.0), 0.092809000000000, places=7 + ) self.assertAlmostEqual( - log_encoding_ALEXALogC(0.18), 0.391006832034084, places=7) + log_encoding_ALEXALogC(0.18), 0.391006832034084, places=7 + ) self.assertAlmostEqual( - log_encoding_ALEXALogC(1.0), 0.570631558120417, places=7) + log_encoding_ALEXALogC(1.0), 0.570631558120417, places=7 + ) def test_n_dimensional_log_encoding_ALEXALogC(self): """ - Tests :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ + Test :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ log_encoding_ALEXALogC` definition n-dimensional arrays support. """ @@ -67,53 +72,57 @@ def test_n_dimensional_log_encoding_ALEXALogC(self): def test_domain_range_scale_log_encoding_ALEXALogC(self): """ - Tests :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ + Test :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ log_encoding_ALEXALogC` definition domain and range scale support. """ x = 0.18 t = log_encoding_ALEXALogC(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_ALEXALogC(x * factor), t * factor, decimal=7) + log_encoding_ALEXALogC(x * factor), t * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_ALEXALogC(self): """ - Tests :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ + Test :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ log_encoding_ALEXALogC` definition nan support. """ log_encoding_ALEXALogC( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_ALEXALogC(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ + Define :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ log_decoding_ALEXALogC` definition unit tests methods. """ def test_log_decoding_ALEXALogC(self): """ - Tests :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ + Test :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ log_decoding_ALEXALogC` definition. """ self.assertAlmostEqual(log_decoding_ALEXALogC(0.092809), 0.0, places=7) self.assertAlmostEqual( - log_decoding_ALEXALogC(0.391006832034084), 0.18, places=7) + log_decoding_ALEXALogC(0.391006832034084), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_ALEXALogC(0.570631558120417), 1.0, places=7) + log_decoding_ALEXALogC(0.570631558120417), 1.0, places=7 + ) def test_n_dimensional_log_decoding_ALEXALogC(self): """ - Tests :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ + Test :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ log_decoding_ALEXALogC` definition n-dimensional arrays support. """ @@ -134,29 +143,31 @@ def test_n_dimensional_log_decoding_ALEXALogC(self): def test_domain_range_scale_log_decoding_ALEXALogC(self): """ - Tests :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ + Test :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ log_decoding_ALEXALogC` definition domain and range scale support. """ t = 0.391006832034084 x = log_decoding_ALEXALogC(t) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_ALEXALogC(t * factor), x * factor, decimal=7) + log_decoding_ALEXALogC(t * factor), x * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_ALEXALogC(self): """ - Tests :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ + Test :func:`colour.models.rgb.transfer_functions.arri_alexa_log_c.\ log_decoding_ALEXALogC` definition nan support. """ log_decoding_ALEXALogC( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_blackmagic_design.py b/colour/models/rgb/transfer_functions/tests/test_blackmagic_design.py new file mode 100644 index 0000000000..78a6bd3095 --- /dev/null +++ b/colour/models/rgb/transfer_functions/tests/test_blackmagic_design.py @@ -0,0 +1,223 @@ +""" +Defines the unit tests for the :mod:`colour.models.rgb.transfer_functions.\ +blackmagic_design` module. +""" + +import numpy as np +import unittest + +from colour.models.rgb.transfer_functions import ( + oetf_BlackmagicFilmGeneration5, + oetf_inverse_BlackmagicFilmGeneration5, +) +from colour.utilities import domain_range_scale, ignore_numpy_errors + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestOetf_BlackmagicFilmGeneration5", + "TestOetf_inverse_BlackmagicFilmGeneration5", +] + + +class TestOetf_BlackmagicFilmGeneration5(unittest.TestCase): + """ + Define :func:`colour.models.rgb.transfer_functions.blackmagic_design.\ +oetf_BlackmagicFilmGeneration5` definition unit tests methods. + """ + + def test_oetf_BlackmagicFilmGeneration5(self): + """ + Test :func:`colour.models.rgb.transfer_functions.\ +blackmagic_design.oetf_BlackmagicFilmGeneration5` definition. + """ + + self.assertAlmostEqual( + oetf_BlackmagicFilmGeneration5(0.0), 0.092465753424658, places=7 + ) + + self.assertAlmostEqual( + oetf_BlackmagicFilmGeneration5(0.18), 0.383561643835617, places=7 + ) + + self.assertAlmostEqual( + oetf_BlackmagicFilmGeneration5(1.0), 0.530489624957305, places=7 + ) + + self.assertAlmostEqual( + oetf_BlackmagicFilmGeneration5(100.0), 0.930339851899973, places=7 + ) + + self.assertAlmostEqual( + oetf_BlackmagicFilmGeneration5(222.86), 0.999999631713769, places=7 + ) + + def test_n_dimensional_oetf_BlackmagicFilmGeneration5(self): + """ + Test :func:`colour.models.rgb.transfer_functions.\ +blackmagic_design.oetf_BlackmagicFilmGeneration5` definition n-dimensional + arrays support. + """ + + L = 0.18 + V = oetf_BlackmagicFilmGeneration5(L) + + L = np.tile(L, 6) + V = np.tile(V, 6) + np.testing.assert_almost_equal( + oetf_BlackmagicFilmGeneration5(L), V, decimal=7 + ) + + L = np.reshape(L, (2, 3)) + V = np.reshape(V, (2, 3)) + np.testing.assert_almost_equal( + oetf_BlackmagicFilmGeneration5(L), V, decimal=7 + ) + + L = np.reshape(L, (2, 3, 1)) + V = np.reshape(V, (2, 3, 1)) + np.testing.assert_almost_equal( + oetf_BlackmagicFilmGeneration5(L), V, decimal=7 + ) + + def test_domain_range_scale_oetf_BlackmagicFilmGeneration5(self): + """ + Test :func:`colour.models.rgb.transfer_functions.\ +blackmagic_design.oetf_BlackmagicFilmGeneration5` definition domain and range + scale support. + """ + + L = 0.18 + V = oetf_BlackmagicFilmGeneration5(L) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + oetf_BlackmagicFilmGeneration5(L * factor), + V * factor, + decimal=7, + ) + + @ignore_numpy_errors + def test_nan_oetf_BlackmagicFilmGeneration5(self): + """ + Test :func:`colour.models.rgb.transfer_functions.\ +blackmagic_design.oetf_BlackmagicFilmGeneration5` definition nan support. + """ + + oetf_BlackmagicFilmGeneration5( + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) + + +class TestOetf_inverse_BlackmagicFilmGeneration5(unittest.TestCase): + """ + Define :func:`colour.models.rgb.transfer_functions.\ +blackmagic_design.oetf_inverse_BlackmagicFilmGeneration5` definition unit tests + methods. + """ + + def test_oetf_inverse_BlackmagicFilmGeneration5(self): + """ + Test :func:`colour.models.rgb.transfer_functions.\ +blackmagic_design.oetf_inverse_BlackmagicFilmGeneration5` definition. + """ + + self.assertAlmostEqual( + oetf_inverse_BlackmagicFilmGeneration5(0.092465753424658), + 0.0, + places=7, + ) + + self.assertAlmostEqual( + oetf_inverse_BlackmagicFilmGeneration5(0.383561643835617), + 0.18, + places=7, + ) + + self.assertAlmostEqual( + oetf_inverse_BlackmagicFilmGeneration5(0.530489624957305), + 1.0, + places=7, + ) + + self.assertAlmostEqual( + oetf_inverse_BlackmagicFilmGeneration5(0.930339851899973), + 100.0, + places=7, + ) + + self.assertAlmostEqual( + oetf_inverse_BlackmagicFilmGeneration5(0.999999631713769), + 222.86, + places=7, + ) + + def test_n_dimensional_oetf_inverse_BlackmagicFilmGeneration5(self): + """ + Test :func:`colour.models.rgb.transfer_functions.\ +blackmagic_design.oetf_inverse_BlackmagicFilmGeneration5` definition + n-dimensional arrays support. + """ + + V = 0.383561643835617 + L = oetf_inverse_BlackmagicFilmGeneration5(V) + + V = np.tile(V, 6) + L = np.tile(L, 6) + np.testing.assert_almost_equal( + oetf_inverse_BlackmagicFilmGeneration5(V), L, decimal=7 + ) + + V = np.reshape(V, (2, 3)) + L = np.reshape(L, (2, 3)) + np.testing.assert_almost_equal( + oetf_inverse_BlackmagicFilmGeneration5(V), L, decimal=7 + ) + + V = np.reshape(V, (2, 3, 1)) + L = np.reshape(L, (2, 3, 1)) + np.testing.assert_almost_equal( + oetf_inverse_BlackmagicFilmGeneration5(V), L, decimal=7 + ) + + def test_domain_range_scale_oetf_inverse_BlackmagicFilmGeneration5(self): + """ + Test :func:`colour.models.rgb.transfer_functions.\ +blackmagic_design.oetf_inverse_BlackmagicFilmGeneration5` definition domain and + range scale support. + """ + + V = 0.383561643835617 + L = oetf_inverse_BlackmagicFilmGeneration5(V) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + oetf_inverse_BlackmagicFilmGeneration5(V * factor), + L * factor, + decimal=7, + ) + + @ignore_numpy_errors + def test_nan_oetf_inverse_BlackmagicFilmGeneration5(self): + """ + Test :func:`colour.models.rgb.transfer_functions.\ +blackmagic_design.oetf_inverse_BlackmagicFilmGeneration5` definition nan + support. + """ + + oetf_inverse_BlackmagicFilmGeneration5( + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_canon_log.py b/colour/models/rgb/transfer_functions/tests/test_canon_log.py index b313c455b6..aa928e11ba 100644 --- a/colour/models/rgb/transfer_functions/tests/test_canon_log.py +++ b/colour/models/rgb/transfer_functions/tests/test_canon_log.py @@ -1,74 +1,84 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.canon_log` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.canon_log` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from colour.models.rgb.transfer_functions import ( - log_encoding_CanonLog, log_decoding_CanonLog, log_encoding_CanonLog2, - log_decoding_CanonLog2, log_encoding_CanonLog3, log_decoding_CanonLog3) + log_encoding_CanonLog, + log_decoding_CanonLog, + log_encoding_CanonLog2, + log_decoding_CanonLog2, + log_encoding_CanonLog3, + log_decoding_CanonLog3, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestLogEncoding_CanonLog', 'TestLogDecoding_CanonLog', - 'TestLogEncoding_CanonLog2', 'TestLogDecoding_CanonLog2', - 'TestLogEncoding_CanonLog3', 'TestLogDecoding_CanonLog3' + "TestLogEncoding_CanonLog", + "TestLogDecoding_CanonLog", + "TestLogEncoding_CanonLog2", + "TestLogDecoding_CanonLog2", + "TestLogEncoding_CanonLog3", + "TestLogDecoding_CanonLog3", ] class TestLogEncoding_CanonLog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.canon_log.\ + Define :func:`colour.models.rgb.transfer_functions.canon_log.\ log_encoding_CanonLog` definition unit tests methods. """ def test_log_encoding_CanonLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_encoding_CanonLog` definition. """ self.assertAlmostEqual( - log_encoding_CanonLog(-0.1), -0.023560122781997, places=7) + log_encoding_CanonLog(-0.1), -0.023560122781997, places=7 + ) self.assertAlmostEqual( - log_encoding_CanonLog(0.0), 0.125122480156403, places=7) + log_encoding_CanonLog(0.0), 0.125122480156403, places=7 + ) self.assertAlmostEqual( - log_encoding_CanonLog(0.18), 0.343389651726069, places=7) + log_encoding_CanonLog(0.18), 0.343389651726069, places=7 + ) self.assertAlmostEqual( - log_encoding_CanonLog(0.18, 12), 0.343138084215647, places=7) + log_encoding_CanonLog(0.18, 12), 0.343138084215647, places=7 + ) self.assertAlmostEqual( - log_encoding_CanonLog(0.18, 10, False), - 0.327953896935809, - places=7) + log_encoding_CanonLog(0.18, 10, False), 0.327953896935809, places=7 + ) self.assertAlmostEqual( log_encoding_CanonLog(0.18, 10, False, False), 0.312012855550395, - places=7) + places=7, + ) self.assertAlmostEqual( - log_encoding_CanonLog(1.0), 0.618775485598649, places=7) + log_encoding_CanonLog(1.0), 0.618775485598649, places=7 + ) def test_n_dimensional_log_encoding_CanonLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_encoding_CanonLog` definition n-dimensional arrays support. """ @@ -78,86 +88,94 @@ def test_n_dimensional_log_encoding_CanonLog(self): x = np.tile(x, 6) clog = np.tile(clog, 6) np.testing.assert_almost_equal( - log_encoding_CanonLog(x), clog, decimal=7) + log_encoding_CanonLog(x), clog, decimal=7 + ) x = np.reshape(x, (2, 3)) clog = np.reshape(clog, (2, 3)) np.testing.assert_almost_equal( - log_encoding_CanonLog(x), clog, decimal=7) + log_encoding_CanonLog(x), clog, decimal=7 + ) x = np.reshape(x, (2, 3, 1)) clog = np.reshape(clog, (2, 3, 1)) np.testing.assert_almost_equal( - log_encoding_CanonLog(x), clog, decimal=7) + log_encoding_CanonLog(x), clog, decimal=7 + ) def test_domain_range_scale_log_encoding_CanonLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_encoding_CanonLog` definition domain and range scale support. """ x = 0.18 clog = log_encoding_CanonLog(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_CanonLog(x * factor), - clog * factor, - decimal=7) + log_encoding_CanonLog(x * factor), clog * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_CanonLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_encoding_CanonLog` definition nan support. """ log_encoding_CanonLog( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_CanonLog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.canon_log.\ + Define :func:`colour.models.rgb.transfer_functions.canon_log.\ log_decoding_CanonLog` definition unit tests methods. """ def test_log_decoding_CanonLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_decoding_CanonLog` definition. """ self.assertAlmostEqual( - log_decoding_CanonLog(-0.023560122781997), -0.1, places=7) + log_decoding_CanonLog(-0.023560122781997), -0.1, places=7 + ) self.assertAlmostEqual( - log_decoding_CanonLog(0.125122480156403), 0.0, places=7) + log_decoding_CanonLog(0.125122480156403), 0.0, places=7 + ) self.assertAlmostEqual( - log_decoding_CanonLog(0.343389651726069), 0.18, places=7) + log_decoding_CanonLog(0.343389651726069), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_CanonLog(0.343138084215647, 12), 0.18, places=7) + log_decoding_CanonLog(0.343138084215647, 12), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_CanonLog(0.327953896935809, 10, False), - 0.18, - places=7) + log_decoding_CanonLog(0.327953896935809, 10, False), 0.18, places=7 + ) self.assertAlmostEqual( log_decoding_CanonLog(0.312012855550395, 10, False, False), 0.18, - places=7) + places=7, + ) self.assertAlmostEqual( - log_decoding_CanonLog(0.618775485598649), 1.0, places=7) + log_decoding_CanonLog(0.618775485598649), 1.0, places=7 + ) def test_n_dimensional_log_decoding_CanonLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_decoding_CanonLog` definition n-dimensional arrays support. """ @@ -167,86 +185,96 @@ def test_n_dimensional_log_decoding_CanonLog(self): clog = np.tile(clog, 6) x = np.tile(x, 6) np.testing.assert_almost_equal( - log_decoding_CanonLog(clog), x, decimal=7) + log_decoding_CanonLog(clog), x, decimal=7 + ) clog = np.reshape(clog, (2, 3)) x = np.reshape(x, (2, 3)) np.testing.assert_almost_equal( - log_decoding_CanonLog(clog), x, decimal=7) + log_decoding_CanonLog(clog), x, decimal=7 + ) clog = np.reshape(clog, (2, 3, 1)) x = np.reshape(x, (2, 3, 1)) np.testing.assert_almost_equal( - log_decoding_CanonLog(clog), x, decimal=7) + log_decoding_CanonLog(clog), x, decimal=7 + ) def test_domain_range_scale_log_decoding_CanonLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_decoding_CanonLog` definition domain and range scale support. """ clog = 0.343389651726069 x = log_decoding_CanonLog(clog) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_CanonLog(clog * factor), - x * factor, - decimal=7) + log_decoding_CanonLog(clog * factor), x * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_CanonLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_decoding_CanonLog` definition nan support. """ log_decoding_CanonLog( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogEncoding_CanonLog2(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.canon_log.\ + Define :func:`colour.models.rgb.transfer_functions.canon_log.\ log_encoding_CanonLog2` definition unit tests methods. """ def test_log_encoding_CanonLog2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_encoding_CanonLog2` definition. """ self.assertAlmostEqual( - log_encoding_CanonLog2(-0.1), -0.155370131996824, places=7) + log_encoding_CanonLog2(-0.1), -0.155370131996824, places=7 + ) self.assertAlmostEqual( - log_encoding_CanonLog2(0.0), 0.092864125247312, places=7) + log_encoding_CanonLog2(0.0), 0.092864125247312, places=7 + ) self.assertAlmostEqual( - log_encoding_CanonLog2(0.18), 0.398254694983167, places=7) + log_encoding_CanonLog2(0.18), 0.398254694983167, places=7 + ) self.assertAlmostEqual( - log_encoding_CanonLog2(0.18, 12), 0.397962933301861, places=7) + log_encoding_CanonLog2(0.18, 12), 0.397962933301861, places=7 + ) self.assertAlmostEqual( log_encoding_CanonLog2(0.18, 10, False), 0.392025745397009, - places=7) + places=7, + ) self.assertAlmostEqual( log_encoding_CanonLog2(0.18, 10, False, False), 0.379864582222983, - places=7) + places=7, + ) self.assertAlmostEqual( - log_encoding_CanonLog2(1.0), 0.573229282897641, places=7) + log_encoding_CanonLog2(1.0), 0.573229282897641, places=7 + ) def test_n_dimensional_log_encoding_CanonLog2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_encoding_CanonLog2` definition n-dimensional arrays support. """ @@ -256,86 +284,98 @@ def test_n_dimensional_log_encoding_CanonLog2(self): x = np.tile(x, 6) clog2 = np.tile(clog2, 6) np.testing.assert_almost_equal( - log_encoding_CanonLog2(x), clog2, decimal=7) + log_encoding_CanonLog2(x), clog2, decimal=7 + ) x = np.reshape(x, (2, 3)) clog2 = np.reshape(clog2, (2, 3)) np.testing.assert_almost_equal( - log_encoding_CanonLog2(x), clog2, decimal=7) + log_encoding_CanonLog2(x), clog2, decimal=7 + ) x = np.reshape(x, (2, 3, 1)) clog2 = np.reshape(clog2, (2, 3, 1)) np.testing.assert_almost_equal( - log_encoding_CanonLog2(x), clog2, decimal=7) + log_encoding_CanonLog2(x), clog2, decimal=7 + ) def test_domain_range_scale_log_encoding_CanonLog2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_encoding_CanonLog2` definition domain and range scale support. """ x = 0.18 clog2 = log_encoding_CanonLog2(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( log_encoding_CanonLog2(x * factor), clog2 * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_log_encoding_CanonLog2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_encoding_CanonLog2` definition nan support. """ log_encoding_CanonLog2( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_CanonLog2(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.canon_log.\ + Define :func:`colour.models.rgb.transfer_functions.canon_log.\ log_decoding_CanonLog2` definition unit tests methods. """ def test_log_decoding_CanonLog2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_decoding_CanonLog2` definition. """ self.assertAlmostEqual( - log_decoding_CanonLog2(-0.155370131996824), -0.1, places=7) + log_decoding_CanonLog2(-0.155370131996824), -0.1, places=7 + ) self.assertAlmostEqual( - log_decoding_CanonLog2(0.092864125247312), 0.0, places=7) + log_decoding_CanonLog2(0.092864125247312), 0.0, places=7 + ) self.assertAlmostEqual( - log_decoding_CanonLog2(0.398254694983167), 0.18, places=7) + log_decoding_CanonLog2(0.398254694983167), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_CanonLog2(0.397962933301861, 12), 0.18, places=7) + log_decoding_CanonLog2(0.397962933301861, 12), 0.18, places=7 + ) self.assertAlmostEqual( log_decoding_CanonLog2(0.392025745397009, 10, False), 0.18, - places=7) + places=7, + ) self.assertAlmostEqual( log_decoding_CanonLog2(0.379864582222983, 10, False, False), 0.18, - places=7) + places=7, + ) self.assertAlmostEqual( - log_decoding_CanonLog2(0.573229282897641), 1.0, places=7) + log_decoding_CanonLog2(0.573229282897641), 1.0, places=7 + ) def test_n_dimensional_log_decoding_CanonLog2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_decoding_CanonLog2` definition n-dimensional arrays support. """ @@ -345,86 +385,98 @@ def test_n_dimensional_log_decoding_CanonLog2(self): clog2 = np.tile(clog2, 6) x = np.tile(x, 6) np.testing.assert_almost_equal( - log_decoding_CanonLog2(clog2), x, decimal=7) + log_decoding_CanonLog2(clog2), x, decimal=7 + ) clog2 = np.reshape(clog2, (2, 3)) x = np.reshape(x, (2, 3)) np.testing.assert_almost_equal( - log_decoding_CanonLog2(clog2), x, decimal=7) + log_decoding_CanonLog2(clog2), x, decimal=7 + ) clog2 = np.reshape(clog2, (2, 3, 1)) x = np.reshape(x, (2, 3, 1)) np.testing.assert_almost_equal( - log_decoding_CanonLog2(clog2), x, decimal=7) + log_decoding_CanonLog2(clog2), x, decimal=7 + ) def test_domain_range_scale_log_decoding_CanonLog2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_decoding_CanonLog2` definition domain and range scale support. """ clog = 0.398254694983167 x = log_decoding_CanonLog2(clog) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( log_decoding_CanonLog2(clog * factor), x * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_log_decoding_CanonLog2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_decoding_CanonLog2` definition nan support. """ log_decoding_CanonLog2( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogEncoding_CanonLog3(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.canon_log.\ + Define :func:`colour.models.rgb.transfer_functions.canon_log.\ log_encoding_CanonLog3` definition unit tests methods. """ def test_log_encoding_CanonLog3(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_encoding_CanonLog3` definition. """ self.assertAlmostEqual( - log_encoding_CanonLog3(-0.1), -0.028494506076432, places=7) + log_encoding_CanonLog3(-0.1), -0.028494506076432, places=7 + ) self.assertAlmostEqual( - log_encoding_CanonLog3(0.0), 0.125122189869013, places=7) + log_encoding_CanonLog3(0.0), 0.125122189869013, places=7 + ) self.assertAlmostEqual( - log_encoding_CanonLog3(0.18), 0.343389369388687, places=7) + log_encoding_CanonLog3(0.18), 0.343389369388687, places=7 + ) self.assertAlmostEqual( - log_encoding_CanonLog3(0.18, 12), 0.343137802085105, places=7) + log_encoding_CanonLog3(0.18, 12), 0.343137802085105, places=7 + ) self.assertAlmostEqual( log_encoding_CanonLog3(0.18, 10, False), 0.327953567219893, - places=7) + places=7, + ) self.assertAlmostEqual( log_encoding_CanonLog3(0.18, 10, False, False), 0.313436005886328, - places=7) + places=7, + ) self.assertAlmostEqual( - log_encoding_CanonLog3(1.0), 0.580277796238604, places=7) + log_encoding_CanonLog3(1.0), 0.580277796238604, places=7 + ) def test_n_dimensional_log_encoding_CanonLog3(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_encoding_CanonLog3` definition n-dimensional arrays support. """ @@ -434,86 +486,98 @@ def test_n_dimensional_log_encoding_CanonLog3(self): x = np.tile(x, 6) clog3 = np.tile(clog3, 6) np.testing.assert_almost_equal( - log_encoding_CanonLog3(x), clog3, decimal=7) + log_encoding_CanonLog3(x), clog3, decimal=7 + ) x = np.reshape(x, (2, 3)) clog3 = np.reshape(clog3, (2, 3)) np.testing.assert_almost_equal( - log_encoding_CanonLog3(x), clog3, decimal=7) + log_encoding_CanonLog3(x), clog3, decimal=7 + ) x = np.reshape(x, (2, 3, 1)) clog3 = np.reshape(clog3, (2, 3, 1)) np.testing.assert_almost_equal( - log_encoding_CanonLog3(x), clog3, decimal=7) + log_encoding_CanonLog3(x), clog3, decimal=7 + ) def test_domain_range_scale_log_encoding_CanonLog3(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_encoding_CanonLog3` definition domain and range scale support. """ x = 0.18 clog3 = log_encoding_CanonLog3(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( log_encoding_CanonLog3(x * factor), clog3 * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_log_encoding_CanonLog3(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_encoding_CanonLog3` definition nan support. """ log_encoding_CanonLog3( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_CanonLog3(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.canon_log.\ + Define :func:`colour.models.rgb.transfer_functions.canon_log.\ log_decoding_CanonLog3` definition unit tests methods. """ def test_log_decoding_CanonLog3(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_decoding_CanonLog3` definition. """ self.assertAlmostEqual( - log_decoding_CanonLog3(-0.028494506076432), -0.1, places=7) + log_decoding_CanonLog3(-0.028494506076432), -0.1, places=7 + ) self.assertAlmostEqual( - log_decoding_CanonLog3(0.125122189869013), 0.0, places=7) + log_decoding_CanonLog3(0.125122189869013), 0.0, places=7 + ) self.assertAlmostEqual( - log_decoding_CanonLog3(0.343389369388687), 0.18, places=7) + log_decoding_CanonLog3(0.343389369388687), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_CanonLog3(0.343137802085105, 12), 0.18, places=7) + log_decoding_CanonLog3(0.343137802085105, 12), 0.18, places=7 + ) self.assertAlmostEqual( log_decoding_CanonLog3(0.327953567219893, 10, False), 0.18, - places=7) + places=7, + ) self.assertAlmostEqual( log_decoding_CanonLog3(0.313436005886328, 10, False, False), 0.18, - places=7) + places=7, + ) self.assertAlmostEqual( - log_decoding_CanonLog3(0.580277796238604), 1.0, places=7) + log_decoding_CanonLog3(0.580277796238604), 1.0, places=7 + ) def test_n_dimensional_log_decoding_CanonLog3(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_decoding_CanonLog3` definition n-dimensional arrays support. """ @@ -523,45 +587,50 @@ def test_n_dimensional_log_decoding_CanonLog3(self): clog3 = np.tile(clog3, 6) x = np.tile(x, 6) np.testing.assert_almost_equal( - log_decoding_CanonLog3(clog3), x, decimal=7) + log_decoding_CanonLog3(clog3), x, decimal=7 + ) clog3 = np.reshape(clog3, (2, 3)) x = np.reshape(x, (2, 3)) np.testing.assert_almost_equal( - log_decoding_CanonLog3(clog3), x, decimal=7) + log_decoding_CanonLog3(clog3), x, decimal=7 + ) clog3 = np.reshape(clog3, (2, 3, 1)) x = np.reshape(x, (2, 3, 1)) np.testing.assert_almost_equal( - log_decoding_CanonLog3(clog3), x, decimal=7) + log_decoding_CanonLog3(clog3), x, decimal=7 + ) def test_domain_range_scale_log_decoding_CanonLog3(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_decoding_CanonLog3` definition domain and range scale support. """ clog = 0.343389369388687 x = log_decoding_CanonLog3(clog) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( log_decoding_CanonLog3(clog * factor), x * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_log_decoding_CanonLog3(self): """ - Tests :func:`colour.models.rgb.transfer_functions.canon_log.\ + Test :func:`colour.models.rgb.transfer_functions.canon_log.\ log_decoding_CanonLog3` definition nan support. """ log_decoding_CanonLog3( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_cineon.py b/colour/models/rgb/transfer_functions/tests/test_cineon.py index 4b680481d4..a8920b8e54 100644 --- a/colour/models/rgb/transfer_functions/tests/test_cineon.py +++ b/colour/models/rgb/transfer_functions/tests/test_cineon.py @@ -1,52 +1,57 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.cineon` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.cineon` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.models.rgb.transfer_functions import (log_encoding_Cineon, - log_decoding_Cineon) +from colour.models.rgb.transfer_functions import ( + log_encoding_Cineon, + log_decoding_Cineon, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestLogEncoding_Cineon', 'TestLogDecoding_Cineon'] +__all__ = [ + "TestLogEncoding_Cineon", + "TestLogDecoding_Cineon", +] class TestLogEncoding_Cineon(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.cineon.\ + Define :func:`colour.models.rgb.transfer_functions.cineon.\ log_encoding_Cineon` definition unit tests methods. """ def test_log_encoding_Cineon(self): """ - Tests :func:`colour.models.rgb.transfer_functions.cineon.\ + Test :func:`colour.models.rgb.transfer_functions.cineon.\ log_encoding_Cineon` definition. """ self.assertAlmostEqual( - log_encoding_Cineon(0.0), 0.092864125122190, places=7) + log_encoding_Cineon(0.0), 0.092864125122190, places=7 + ) self.assertAlmostEqual( - log_encoding_Cineon(0.18), 0.457319613085418, places=7) + log_encoding_Cineon(0.18), 0.457319613085418, places=7 + ) self.assertAlmostEqual( - log_encoding_Cineon(1.0), 0.669599217986315, places=7) + log_encoding_Cineon(1.0), 0.669599217986315, places=7 + ) def test_n_dimensional_log_encoding_Cineon(self): """ - Tests :func:`colour.models.rgb.transfer_functions.cineon.\ + Test :func:`colour.models.rgb.transfer_functions.cineon.\ log_encoding_Cineon` definition n-dimensional arrays support. """ @@ -67,54 +72,59 @@ def test_n_dimensional_log_encoding_Cineon(self): def test_domain_range_scale_log_encoding_Cineon(self): """ - Tests :func:`colour.models.rgb.transfer_functions.cineon.\ + Test :func:`colour.models.rgb.transfer_functions.cineon.\ log_encoding_Cineon` definition domain and range scale support. """ x = 0.18 y = log_encoding_Cineon(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_Cineon(x * factor), y * factor, decimal=7) + log_encoding_Cineon(x * factor), y * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_Cineon(self): """ - Tests :func:`colour.models.rgb.transfer_functions.cineon.\ + Test :func:`colour.models.rgb.transfer_functions.cineon.\ log_encoding_Cineon` definition nan support. """ log_encoding_Cineon( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_Cineon(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.cineon.\ + Define :func:`colour.models.rgb.transfer_functions.cineon.\ log_decoding_Cineon` definition unit tests methods. """ def test_log_decoding_Cineon(self): """ - Tests :func:`colour.models.rgb.transfer_functions.cineon.\ + Test :func:`colour.models.rgb.transfer_functions.cineon.\ log_decoding_Cineon` definition. """ self.assertAlmostEqual( - log_decoding_Cineon(0.092864125122190), 0.0, places=7) + log_decoding_Cineon(0.092864125122190), 0.0, places=7 + ) self.assertAlmostEqual( - log_decoding_Cineon(0.457319613085418), 0.18, places=7) + log_decoding_Cineon(0.457319613085418), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_Cineon(0.669599217986315), 1.0, places=7) + log_decoding_Cineon(0.669599217986315), 1.0, places=7 + ) def test_n_dimensional_log_decoding_Cineon(self): """ - Tests :func:`colour.models.rgb.transfer_functions.cineon.\ + Test :func:`colour.models.rgb.transfer_functions.cineon.\ log_decoding_Cineon` definition n-dimensional arrays support. """ @@ -135,29 +145,31 @@ def test_n_dimensional_log_decoding_Cineon(self): def test_domain_range_scale_log_decoding_Cineon(self): """ - Tests :func:`colour.models.rgb.transfer_functions.cineon.\ + Test :func:`colour.models.rgb.transfer_functions.cineon.\ log_decoding_Cineon` definition domain and range scale support. """ y = 0.457319613085418 x = log_decoding_Cineon(y) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_Cineon(y * factor), x * factor, decimal=7) + log_decoding_Cineon(y * factor), x * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_Cineon(self): """ - Tests :func:`colour.models.rgb.transfer_functions.cineon.\ + Test :func:`colour.models.rgb.transfer_functions.cineon.\ log_decoding_Cineon` definition nan support. """ log_decoding_Cineon( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_common.py b/colour/models/rgb/transfer_functions/tests/test_common.py index b1d4edaec6..1fc07d6951 100644 --- a/colour/models/rgb/transfer_functions/tests/test_common.py +++ b/colour/models/rgb/transfer_functions/tests/test_common.py @@ -1,82 +1,90 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.common` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.common` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.models.rgb.transfer_functions import (CV_range, legal_to_full, - full_to_legal) +from colour.models.rgb.transfer_functions import ( + CV_range, + legal_to_full, + full_to_legal, +) from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Development' +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Development" __all__ = [ - 'TestCV_range', - 'TestLegalToFull', - 'TestFullToLegal', + "TestCV_range", + "TestLegalToFull", + "TestFullToLegal", ] class TestCV_range(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.common.CV_range` + Define :func:`colour.models.rgb.transfer_functions.common.CV_range` definition unit tests methods. """ def test_CV_range(self): """ - Tests :func:`colour.models.rgb.transfer_functions.common.CV_range` + Test :func:`colour.models.rgb.transfer_functions.common.CV_range` definition. """ np.testing.assert_array_equal( - CV_range(8, True, True), np.array([16, 235])) + CV_range(8, True, True), np.array([16, 235]) + ) np.testing.assert_array_equal( - CV_range(8, False, True), np.array([0, 255])) + CV_range(8, False, True), np.array([0, 255]) + ) np.testing.assert_almost_equal( CV_range(8, True, False), np.array([0.06274510, 0.92156863]), - decimal=7) + decimal=7, + ) np.testing.assert_array_equal( - CV_range(8, False, False), np.array([0, 1])) + CV_range(8, False, False), np.array([0, 1]) + ) np.testing.assert_array_equal( - CV_range(10, True, True), np.array([64, 940])) + CV_range(10, True, True), np.array([64, 940]) + ) np.testing.assert_array_equal( - CV_range(10, False, True), np.array([0, 1023])) + CV_range(10, False, True), np.array([0, 1023]) + ) np.testing.assert_almost_equal( CV_range(10, True, False), np.array([0.06256109, 0.91886608]), - decimal=7) + decimal=7, + ) np.testing.assert_array_equal( - CV_range(10, False, False), np.array([0, 1])) + CV_range(10, False, False), np.array([0, 1]) + ) class TestLegalToFull(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.common.legal_to_full` + Define :func:`colour.models.rgb.transfer_functions.common.legal_to_full` definition unit tests methods. """ def test_legal_to_full(self): """ - Tests :func:`colour.models.rgb.transfer_functions.common.legal_to_full` + Test :func:`colour.models.rgb.transfer_functions.common.legal_to_full` definition. """ @@ -95,11 +103,12 @@ def test_legal_to_full(self): self.assertAlmostEqual(legal_to_full(64, in_int=True, out_int=True), 0) self.assertAlmostEqual( - legal_to_full(940, in_int=True, out_int=True), 1023) + legal_to_full(940, in_int=True, out_int=True), 1023 + ) def test_n_dimensional_legal_to_full(self): """ - Tests :func:`colour.models.rgb.transfer_functions.common.legal_to_full` + Test :func:`colour.models.rgb.transfer_functions.common.legal_to_full` definition n-dimensional arrays support. """ @@ -109,22 +118,25 @@ def test_n_dimensional_legal_to_full(self): CV_l = np.tile(CV_l, 6) CV_f = np.tile(CV_f, 6) np.testing.assert_almost_equal( - legal_to_full(CV_l, 10), CV_f, decimal=7) + legal_to_full(CV_l, 10), CV_f, decimal=7 + ) CV_l = np.reshape(CV_l, (2, 3)) CV_f = np.reshape(CV_f, (2, 3)) np.testing.assert_almost_equal( - legal_to_full(CV_l, 10), CV_f, decimal=7) + legal_to_full(CV_l, 10), CV_f, decimal=7 + ) CV_l = np.reshape(CV_l, (2, 3, 1)) CV_f = np.reshape(CV_f, (2, 3, 1)) np.testing.assert_almost_equal( - legal_to_full(CV_l, 10), CV_f, decimal=7) + legal_to_full(CV_l, 10), CV_f, decimal=7 + ) @ignore_numpy_errors def test_nan_legal_to_full(self): """ - Tests :func:`colour.models.rgb.transfer_functions.common.legal_to_full` + Test :func:`colour.models.rgb.transfer_functions.common.legal_to_full` definition nan support. """ @@ -133,13 +145,13 @@ def test_nan_legal_to_full(self): class TestFullToLegal(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.common.full_to_legal` + Define :func:`colour.models.rgb.transfer_functions.common.full_to_legal` definition unit tests methods. """ def test_full_to_legal(self): """ - Tests :func:`colour.models.rgb.transfer_functions.common.full_to_legal` + Test :func:`colour.models.rgb.transfer_functions.common.full_to_legal` definition. """ @@ -152,19 +164,22 @@ def test_full_to_legal(self): self.assertAlmostEqual(full_to_legal(1.0, out_int=True), 940) self.assertAlmostEqual( - full_to_legal(0, in_int=True), 0.062561094819159) + full_to_legal(0, in_int=True), 0.062561094819159 + ) self.assertAlmostEqual( - full_to_legal(1023, in_int=True), 0.918866080156403) + full_to_legal(1023, in_int=True), 0.918866080156403 + ) self.assertAlmostEqual(full_to_legal(0, in_int=True, out_int=True), 64) self.assertAlmostEqual( - full_to_legal(1023, in_int=True, out_int=True), 940) + full_to_legal(1023, in_int=True, out_int=True), 940 + ) def test_n_dimensional_full_to_legal(self): """ - Tests :func:`colour.models.rgb.transfer_functions.common.full_to_legal` + Test :func:`colour.models.rgb.transfer_functions.common.full_to_legal` definition n-dimensional arrays support. """ @@ -174,27 +189,30 @@ def test_n_dimensional_full_to_legal(self): CF_f = np.tile(CF_f, 6) CV_l = np.tile(CV_l, 6) np.testing.assert_almost_equal( - full_to_legal(CF_f, 10), CV_l, decimal=7) + full_to_legal(CF_f, 10), CV_l, decimal=7 + ) CF_f = np.reshape(CF_f, (2, 3)) CV_l = np.reshape(CV_l, (2, 3)) np.testing.assert_almost_equal( - full_to_legal(CF_f, 10), CV_l, decimal=7) + full_to_legal(CF_f, 10), CV_l, decimal=7 + ) CF_f = np.reshape(CF_f, (2, 3, 1)) CV_l = np.reshape(CV_l, (2, 3, 1)) np.testing.assert_almost_equal( - full_to_legal(CF_f, 10), CV_l, decimal=7) + full_to_legal(CF_f, 10), CV_l, decimal=7 + ) @ignore_numpy_errors def test_nan_full_to_legal(self): """ - Tests :func:`colour.models.rgb.transfer_functions.common.full_to_legal` + Test :func:`colour.models.rgb.transfer_functions.common.full_to_legal` definition nan support. """ full_to_legal(np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]), 10) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_davinci_intermediate.py b/colour/models/rgb/transfer_functions/tests/test_davinci_intermediate.py new file mode 100644 index 0000000000..26814a2a18 --- /dev/null +++ b/colour/models/rgb/transfer_functions/tests/test_davinci_intermediate.py @@ -0,0 +1,212 @@ +""" +Defines the unit tests for the :mod:`colour.models.rgb.transfer_functions.\ +davinci_intermediate` module. +""" + +import numpy as np +import unittest + +from colour.models.rgb.transfer_functions import ( + oetf_DaVinciIntermediate, + oetf_inverse_DaVinciIntermediate, +) +from colour.utilities import domain_range_scale, ignore_numpy_errors + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestOetf_DaVinciIntermediate", + "TestOetf_inverse_DaVinciIntermediate", +] + + +class TestOetf_DaVinciIntermediate(unittest.TestCase): + """ + Define :func:`colour.models.rgb.transfer_functions.davinci_intermediate.\ +oetf_DaVinciIntermediate` definition unit tests methods. + """ + + def test_oetf_DaVinciIntermediate(self): + """ + Test :func:`colour.models.rgb.transfer_functions.\ +davinci_intermediate.oetf_DaVinciIntermediate` definition. + """ + + self.assertAlmostEqual( + oetf_DaVinciIntermediate(-0.01), -0.104442685500000, places=7 + ) + + self.assertAlmostEqual(oetf_DaVinciIntermediate(0.0), 0.0, places=7) + + self.assertAlmostEqual( + oetf_DaVinciIntermediate(0.18), 0.336043272384855, places=7 + ) + + self.assertAlmostEqual( + oetf_DaVinciIntermediate(1.0), 0.513837441116225, places=7 + ) + + self.assertAlmostEqual( + oetf_DaVinciIntermediate(100.0), 0.999999987016872, places=7 + ) + + def test_n_dimensional_oetf_DaVinciIntermediate(self): + """ + Test :func:`colour.models.rgb.transfer_functions.\ +davinci_intermediate.oetf_DaVinciIntermediate` definition n-dimensional arrays + support. + """ + + L = 0.18 + V = oetf_DaVinciIntermediate(L) + + L = np.tile(L, 6) + V = np.tile(V, 6) + np.testing.assert_almost_equal( + oetf_DaVinciIntermediate(L), V, decimal=7 + ) + + L = np.reshape(L, (2, 3)) + V = np.reshape(V, (2, 3)) + np.testing.assert_almost_equal( + oetf_DaVinciIntermediate(L), V, decimal=7 + ) + + L = np.reshape(L, (2, 3, 1)) + V = np.reshape(V, (2, 3, 1)) + np.testing.assert_almost_equal( + oetf_DaVinciIntermediate(L), V, decimal=7 + ) + + def test_domain_range_scale_oetf_DaVinciIntermediate(self): + """ + Test :func:`colour.models.rgb.transfer_functions.\ +davinci_intermediate.oetf_DaVinciIntermediate` definition domain and range + scale support. + """ + + L = 0.18 + V = oetf_DaVinciIntermediate(L) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + oetf_DaVinciIntermediate(L * factor), V * factor, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_oetf_DaVinciIntermediate(self): + """ + Test :func:`colour.models.rgb.transfer_functions.\ +davinci_intermediate.oetf_DaVinciIntermediate` definition nan support. + """ + + oetf_DaVinciIntermediate( + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) + + +class TestOetf_inverse_DaVinciIntermediate(unittest.TestCase): + """ + Define :func:`colour.models.rgb.transfer_functions.\ +davinci_intermediate.oetf_inverse_DaVinciIntermediate` definition unit tests + methods. + """ + + def test_oetf_inverse_DaVinciIntermediate(self): + """ + Test :func:`colour.models.rgb.transfer_functions.\ +davinci_intermediate.oetf_inverse_DaVinciIntermediate` definition. + """ + + self.assertAlmostEqual( + oetf_inverse_DaVinciIntermediate(-0.104442685500000), + -0.01, + places=7, + ) + + self.assertAlmostEqual( + oetf_inverse_DaVinciIntermediate(0.0), 0.0, places=7 + ) + + self.assertAlmostEqual( + oetf_inverse_DaVinciIntermediate(0.336043272384855), 0.18, places=7 + ) + + self.assertAlmostEqual( + oetf_inverse_DaVinciIntermediate(0.513837441116225), 1.0, places=7 + ) + + self.assertAlmostEqual( + oetf_inverse_DaVinciIntermediate(0.999999987016872), + 100.0, + places=7, + ) + + def test_n_dimensional_oetf_inverse_DaVinciIntermediate(self): + """ + Test :func:`colour.models.rgb.transfer_functions.\ +davinci_intermediate.oetf_inverse_DaVinciIntermediate` definition n-dimensional + arrays support. + """ + + V = 0.336043272384855 + L = oetf_inverse_DaVinciIntermediate(V) + + V = np.tile(V, 6) + L = np.tile(L, 6) + np.testing.assert_almost_equal( + oetf_inverse_DaVinciIntermediate(V), L, decimal=7 + ) + + V = np.reshape(V, (2, 3)) + L = np.reshape(L, (2, 3)) + np.testing.assert_almost_equal( + oetf_inverse_DaVinciIntermediate(V), L, decimal=7 + ) + + V = np.reshape(V, (2, 3, 1)) + L = np.reshape(L, (2, 3, 1)) + np.testing.assert_almost_equal( + oetf_inverse_DaVinciIntermediate(V), L, decimal=7 + ) + + def test_domain_range_scale_oetf_inverse_DaVinciIntermediate(self): + """ + Test :func:`colour.models.rgb.transfer_functions.\ +davinci_intermediate.oetf_inverse_DaVinciIntermediate` definition domain and + range scale support. + """ + + V = 0.336043272384855 + L = oetf_inverse_DaVinciIntermediate(V) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + oetf_inverse_DaVinciIntermediate(V * factor), + L * factor, + decimal=7, + ) + + @ignore_numpy_errors + def test_nan_oetf_inverse_DaVinciIntermediate(self): + """ + Test :func:`colour.models.rgb.transfer_functions.\ +davinci_intermediate.oetf_inverse_DaVinciIntermediate` definition nan support. + """ + + oetf_inverse_DaVinciIntermediate( + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_dcdm.py b/colour/models/rgb/transfer_functions/tests/test_dcdm.py index dd7d853e94..3ff42bdaff 100644 --- a/colour/models/rgb/transfer_functions/tests/test_dcdm.py +++ b/colour/models/rgb/transfer_functions/tests/test_dcdm.py @@ -1,36 +1,36 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.dcdm` +Defines the unit tests for the :mod:`colour.models.rgb.transfer_functions.dcdm` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from colour.models.rgb.transfer_functions import eotf_inverse_DCDM, eotf_DCDM from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestEotf_inverse_DCDM', 'TestEotf_DCDM'] +__all__ = [ + "TestEotf_inverse_DCDM", + "TestEotf_DCDM", +] class TestEotf_inverse_DCDM(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.dcdm.eotf_inverse_DCDM` + Define :func:`colour.models.rgb.transfer_functions.dcdm.eotf_inverse_DCDM` definition unit tests methods. """ def test_eotf_inverse_DCDM(self): """ - Tests :func:`colour.models.rgb.transfer_functions.\ + Test :func:`colour.models.rgb.transfer_functions.\ dcdm.eotf_inverse_DCDM` definition. """ @@ -44,7 +44,7 @@ def test_eotf_inverse_DCDM(self): def test_n_dimensional_eotf_inverse_DCDM(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dcdm.\ + Test :func:`colour.models.rgb.transfer_functions.dcdm.\ eotf_inverse_DCDM` definition n-dimensional arrays support. """ @@ -54,37 +54,41 @@ def test_n_dimensional_eotf_inverse_DCDM(self): XYZ = np.tile(XYZ, 6) XYZ_p = np.tile(XYZ_p, 6) np.testing.assert_almost_equal( - eotf_inverse_DCDM(XYZ), XYZ_p, decimal=7) + eotf_inverse_DCDM(XYZ), XYZ_p, decimal=7 + ) XYZ = np.reshape(XYZ, (2, 3)) XYZ_p = np.reshape(XYZ_p, (2, 3)) np.testing.assert_almost_equal( - eotf_inverse_DCDM(XYZ), XYZ_p, decimal=7) + eotf_inverse_DCDM(XYZ), XYZ_p, decimal=7 + ) XYZ = np.reshape(XYZ, (2, 3, 1)) XYZ_p = np.reshape(XYZ_p, (2, 3, 1)) np.testing.assert_almost_equal( - eotf_inverse_DCDM(XYZ), XYZ_p, decimal=7) + eotf_inverse_DCDM(XYZ), XYZ_p, decimal=7 + ) def test_domain_range_scale_eotf_inverse_DCDM(self): """ - Tests :func:`colour.models.rgb.transfer_functions.\ + Test :func:`colour.models.rgb.transfer_functions.\ dcdm.eotf_inverse_DCDM` definition domain and range scale support. """ XYZ = 0.18 XYZ_p = eotf_inverse_DCDM(XYZ) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - eotf_inverse_DCDM(XYZ * factor), XYZ_p * factor, decimal=7) + eotf_inverse_DCDM(XYZ * factor), XYZ_p * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_eotf_inverse_DCDM(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dcdm.\ + Test :func:`colour.models.rgb.transfer_functions.dcdm.\ eotf_inverse_DCDM` definition nan support. """ @@ -93,13 +97,13 @@ def test_nan_eotf_inverse_DCDM(self): class TestEotf_DCDM(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.dcdm.eotf_DCDM` + Define :func:`colour.models.rgb.transfer_functions.dcdm.eotf_DCDM` definition unit tests methods. """ def test_eotf_DCDM(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dcdm.eotf_DCDM` + Test :func:`colour.models.rgb.transfer_functions.dcdm.eotf_DCDM` definition. """ @@ -110,11 +114,12 @@ def test_eotf_DCDM(self): self.assertAlmostEqual(eotf_DCDM(0.21817973), 1.0, places=7) np.testing.assert_allclose( - eotf_DCDM(462, in_int=True), 0.18, atol=0.00001, rtol=0.00001) + eotf_DCDM(462, in_int=True), 0.18, atol=0.00001, rtol=0.00001 + ) def test_n_dimensional_eotf_DCDM(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dcdm.eotf_DCDM` + Test :func:`colour.models.rgb.transfer_functions.dcdm.eotf_DCDM` definition n-dimensional arrays support. """ @@ -135,28 +140,29 @@ def test_n_dimensional_eotf_DCDM(self): def test_domain_range_scale_eotf_DCDM(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dcdm.eotf_DCDM` + Test :func:`colour.models.rgb.transfer_functions.dcdm.eotf_DCDM` definition domain and range scale support. """ XYZ_p = 0.11281861 XYZ = eotf_DCDM(XYZ_p) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - eotf_DCDM(XYZ_p * factor), XYZ * factor, decimal=7) + eotf_DCDM(XYZ_p * factor), XYZ * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_eotf_DCDM(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dcdm.eotf_DCDM` + Test :func:`colour.models.rgb.transfer_functions.dcdm.eotf_DCDM` definition nan support. """ eotf_DCDM(np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_dicom_gsdf.py b/colour/models/rgb/transfer_functions/tests/test_dicom_gsdf.py index 8df04fdae7..23051a0256 100644 --- a/colour/models/rgb/transfer_functions/tests/test_dicom_gsdf.py +++ b/colour/models/rgb/transfer_functions/tests/test_dicom_gsdf.py @@ -1,55 +1,61 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.dicom_gsdf` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.dicom_gsdf` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.models.rgb.transfer_functions import (eotf_inverse_DICOMGSDF, - eotf_DICOMGSDF) +from colour.models.rgb.transfer_functions import ( + eotf_inverse_DICOMGSDF, + eotf_DICOMGSDF, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestEotf_inverse_DICOMGSDF', 'TestEotf_DICOMGSDF'] +__all__ = [ + "TestEotf_inverse_DICOMGSDF", + "TestEotf_DICOMGSDF", +] class TestEotf_inverse_DICOMGSDF(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.dicom_gsdf.\ + Define :func:`colour.models.rgb.transfer_functions.dicom_gsdf.\ eotf_inverse_DICOMGSDF` definition unit tests methods. """ def test_eotf_inverse_DICOMGSDF(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dicom_gsdf.\ + Test :func:`colour.models.rgb.transfer_functions.dicom_gsdf.\ eotf_inverse_DICOMGSDF` definition. """ self.assertAlmostEqual( - eotf_inverse_DICOMGSDF(0.05), 0.001007281350787, places=7) + eotf_inverse_DICOMGSDF(0.05), 0.001007281350787, places=7 + ) self.assertAlmostEqual( - eotf_inverse_DICOMGSDF(130.0662), 0.500486263438448, places=7) + eotf_inverse_DICOMGSDF(130.0662), 0.500486263438448, places=7 + ) self.assertAlmostEqual( - eotf_inverse_DICOMGSDF(4000), 1.000160314715578, places=7) + eotf_inverse_DICOMGSDF(4000), 1.000160314715578, places=7 + ) self.assertAlmostEqual( - eotf_inverse_DICOMGSDF(130.0662, out_int=True), 512, places=7) + eotf_inverse_DICOMGSDF(130.0662, out_int=True), 512, places=7 + ) def test_n_dimensional_eotf_inverse_DICOMGSDF(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dicom_gsdf.\ + Test :func:`colour.models.rgb.transfer_functions.dicom_gsdf.\ eotf_inverse_DICOMGSDF` definition n-dimensional arrays support. """ @@ -70,57 +76,63 @@ def test_n_dimensional_eotf_inverse_DICOMGSDF(self): def test_domain_range_scale_eotf_inverse_DICOMGSDF(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dicom_gsdf.\ + Test :func:`colour.models.rgb.transfer_functions.dicom_gsdf.\ eotf_inverse_DICOMGSDF` definition domain and range scale support. """ L = 130.0662 J = eotf_inverse_DICOMGSDF(L) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - eotf_inverse_DICOMGSDF(L * factor), J * factor, decimal=7) + eotf_inverse_DICOMGSDF(L * factor), J * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_eotf_inverse_DICOMGSDF(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dicom_gsdf.\ + Test :func:`colour.models.rgb.transfer_functions.dicom_gsdf.\ eotf_inverse_DICOMGSDF` definition nan support. """ eotf_inverse_DICOMGSDF( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestEotf_DICOMGSDF(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.dicom_gsdf. -eotf_DICOMGSDF` definition unit tests methods. + Define :func:`colour.models.rgb.transfer_functions.dicom_gsdf. + eotf_DICOMGSDF` definition unit tests methods. """ def test_eotf_DICOMGSDF(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dicom_gsdf.\ + Test :func:`colour.models.rgb.transfer_functions.dicom_gsdf.\ eotf_DICOMGSDF` definition. """ self.assertAlmostEqual( - eotf_DICOMGSDF(0.001007281350787), 0.050143440671692, places=7) + eotf_DICOMGSDF(0.001007281350787), 0.050143440671692, places=7 + ) self.assertAlmostEqual( - eotf_DICOMGSDF(0.500486263438448), 130.062864706476550, places=7) + eotf_DICOMGSDF(0.500486263438448), 130.062864706476550, places=7 + ) self.assertAlmostEqual( - eotf_DICOMGSDF(1.000160314715578), 3997.586161113322300, places=7) + eotf_DICOMGSDF(1.000160314715578), 3997.586161113322300, places=7 + ) self.assertAlmostEqual( - eotf_DICOMGSDF(512, in_int=True), 130.065284012159790, places=7) + eotf_DICOMGSDF(512, in_int=True), 130.065284012159790, places=7 + ) def test_n_dimensional_eotf_DICOMGSDF(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dicom_gsdf.\ + Test :func:`colour.models.rgb.transfer_functions.dicom_gsdf.\ eotf_DICOMGSDF` definition n-dimensional arrays support. """ @@ -141,28 +153,29 @@ def test_n_dimensional_eotf_DICOMGSDF(self): def test_domain_range_scale_eotf_DICOMGSDF(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dicom_gsdf.\ + Test :func:`colour.models.rgb.transfer_functions.dicom_gsdf.\ eotf_DICOMGSDF` definition domain and range scale support. """ J = 0.500486263438448 L = eotf_DICOMGSDF(J) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - eotf_DICOMGSDF(J * factor), L * factor, decimal=7) + eotf_DICOMGSDF(J * factor), L * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_eotf_DICOMGSDF(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dicom_gsdf.\ + Test :func:`colour.models.rgb.transfer_functions.dicom_gsdf.\ eotf_DICOMGSDF` definition nan support. """ eotf_DICOMGSDF(np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_dji_dlog.py b/colour/models/rgb/transfer_functions/tests/test_dji_dlog.py index d622acaba2..65da83cf13 100644 --- a/colour/models/rgb/transfer_functions/tests/test_dji_dlog.py +++ b/colour/models/rgb/transfer_functions/tests/test_dji_dlog.py @@ -1,50 +1,53 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.\ +Defines the unit tests for the :mod:`colour.models.rgb.transfer_functions.\ dji_dlog` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.models.rgb.transfer_functions import (log_encoding_DJIDLog, - log_decoding_DJIDLog) +from colour.models.rgb.transfer_functions import ( + log_encoding_DJIDLog, + log_decoding_DJIDLog, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestLogEncoding_DJIDLog', 'TestLogDecoding_DJIDLog'] +__all__ = [ + "TestLogEncoding_DJIDLog", + "TestLogDecoding_DJIDLog", +] class TestLogEncoding_DJIDLog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.dji_dlog.\ + Define :func:`colour.models.rgb.transfer_functions.dji_dlog.\ log_encoding_DJIDLog` definition unit tests methods. """ def test_log_encoding_DJIDLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dji_dlog.\ + Test :func:`colour.models.rgb.transfer_functions.dji_dlog.\ log_encoding_DJIDLog` definition. """ self.assertAlmostEqual(log_encoding_DJIDLog(0.0), 0.0929, places=7) self.assertAlmostEqual( - log_encoding_DJIDLog(0.18), 0.398764556189331, places=7) + log_encoding_DJIDLog(0.18), 0.398764556189331, places=7 + ) self.assertAlmostEqual(log_encoding_DJIDLog(1.0), 0.584555, places=7) def test_n_dimensional_log_encoding_DLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dji_dlog.\ + Test :func:`colour.models.rgb.transfer_functions.dji_dlog.\ log_encoding_DJIDLog` definition n-dimensional arrays support. """ @@ -65,52 +68,55 @@ def test_n_dimensional_log_encoding_DLog(self): def test_domain_range_scale_log_encoding_DLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dji_dlog.\ + Test :func:`colour.models.rgb.transfer_functions.dji_dlog.\ log_encoding_DJIDLog` definition domain and range scale support. """ x = 0.18 y = log_encoding_DJIDLog(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_DJIDLog(x * factor), y * factor, decimal=7) + log_encoding_DJIDLog(x * factor), y * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_DLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dji_dlog.\ + Test :func:`colour.models.rgb.transfer_functions.dji_dlog.\ log_encoding_DJIDLog` definition nan support. """ log_encoding_DJIDLog( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_DJIDLog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.dji_dlog.\ + Define :func:`colour.models.rgb.transfer_functions.dji_dlog.\ log_decoding_DJIDLog` definition unit tests methods. """ def test_log_decoding_DJIDLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dji_dlog.\ + Test :func:`colour.models.rgb.transfer_functions.dji_dlog.\ log_decoding_DJIDLog` definition. """ self.assertAlmostEqual(log_decoding_DJIDLog(0.0929), 0.0, places=7) self.assertAlmostEqual( - log_decoding_DJIDLog(0.398764556189331), 0.18, places=6) + log_decoding_DJIDLog(0.398764556189331), 0.18, places=6 + ) self.assertAlmostEqual(log_decoding_DJIDLog(0.584555), 1.0, places=6) def test_n_dimensional_log_decoding_DLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dji_dlog.\ + Test :func:`colour.models.rgb.transfer_functions.dji_dlog.\ log_decoding_DJIDLog` definition n-dimensional arrays support. """ @@ -131,29 +137,31 @@ def test_n_dimensional_log_decoding_DLog(self): def test_domain_range_scale_log_decoding_DLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dji_dlog.\ + Test :func:`colour.models.rgb.transfer_functions.dji_dlog.\ log_decoding_DJIDLog` definition domain and range scale support. """ y = 0.398764556189331 x = log_decoding_DJIDLog(y) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_DJIDLog(y * factor), x * factor, decimal=7) + log_decoding_DJIDLog(y * factor), x * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_DLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.dji_dlog.\ + Test :func:`colour.models.rgb.transfer_functions.dji_dlog.\ log_decoding_DJIDLog` definition nan support. """ log_decoding_DJIDLog( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_exponent.py b/colour/models/rgb/transfer_functions/tests/test_exponent.py index e965a7f69e..cca8b36402 100644 --- a/colour/models/rgb/transfer_functions/tests/test_exponent.py +++ b/colour/models/rgb/transfer_functions/tests/test_exponent.py @@ -1,37 +1,39 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.exponent` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.exponent` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from colour.models.rgb.transfer_functions import ( - exponent_function_basic, exponent_function_monitor_curve) + exponent_function_basic, + exponent_function_monitor_curve, +) from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestBasicExponentFunction', 'TestMonitorCurveExponentFunction'] +__all__ = [ + "TestExponentFunctionBasic", + "TestExponentFunctionMonitorCurve", +] -class TestBasicExponentFunction(unittest.TestCase): +class TestExponentFunctionBasic(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.exponent.\ + Define :func:`colour.models.rgb.transfer_functions.exponent.\ exponent_function_basic` definition unit tests methods. """ def test_exponent_function_basic(self): """ - Tests :func:`colour.models.rgb.transfer_functions.exponent.\ + Test :func:`colour.models.rgb.transfer_functions.exponent.\ exponent_function_basic` definition. """ @@ -40,50 +42,60 @@ def test_exponent_function_basic(self): self.assertAlmostEqual(exponent_function_basic(a, 2.2), a_p, places=7) self.assertAlmostEqual( - exponent_function_basic(a, 2.2, 'basicMirrorFwd'), a_p, places=7) + exponent_function_basic(a, 2.2, "basicMirrorFwd"), a_p, places=7 + ) self.assertAlmostEqual( - exponent_function_basic(a, 2.2, 'basicPassThruFwd'), a_p, places=7) + exponent_function_basic(a, 2.2, "basicPassThruFwd"), a_p, places=7 + ) a = 0.0229932049927 a_p = 0.18 self.assertAlmostEqual( - exponent_function_basic(a, 2.2, 'basicRev'), a_p, places=7) + exponent_function_basic(a, 2.2, "basicRev"), a_p, places=7 + ) self.assertAlmostEqual( - exponent_function_basic(a, 2.2, 'basicMirrorRev'), a_p, places=7) + exponent_function_basic(a, 2.2, "basicMirrorRev"), a_p, places=7 + ) self.assertAlmostEqual( - exponent_function_basic(a, 2.2, 'basicPassThruRev'), a_p, places=7) + exponent_function_basic(a, 2.2, "basicPassThruRev"), a_p, places=7 + ) a = -0.18 self.assertAlmostEqual(exponent_function_basic(a, 2.2), 0.0, places=7) self.assertAlmostEqual( - exponent_function_basic(a, 2.2, 'basicMirrorFwd'), + exponent_function_basic(a, 2.2, "basicMirrorFwd"), -0.0229932049927, - places=7) + places=7, + ) self.assertAlmostEqual( - exponent_function_basic(a, 2.2, 'basicPassThruFwd'), + exponent_function_basic(a, 2.2, "basicPassThruFwd"), -0.18, - places=7) + places=7, + ) a = -0.0229932049927 self.assertAlmostEqual( - exponent_function_basic(a, 2.2, 'basicRev'), 0.0, places=7) + exponent_function_basic(a, 2.2, "basicRev"), 0.0, places=7 + ) self.assertAlmostEqual( - exponent_function_basic(a, 2.2, 'basicMirrorRev'), -0.18, places=7) + exponent_function_basic(a, 2.2, "basicMirrorRev"), -0.18, places=7 + ) self.assertAlmostEqual( - exponent_function_basic(a, 2.2, 'basicPassThruRev'), + exponent_function_basic(a, 2.2, "basicPassThruRev"), -0.0229932049927, - places=7) + places=7, + ) def test_n_dimensional_exponent_function_basic(self): """ - Tests :func:`colour.models.rgb.transfer_functions.exponent.\ + Test :func:`colour.models.rgb.transfer_functions.exponent.\ exponent_function_basic` definition n-dimensional arrays support. """ @@ -93,35 +105,38 @@ def test_n_dimensional_exponent_function_basic(self): a = np.tile(a, 6) a_p = np.tile(a_p, 6) np.testing.assert_almost_equal( - exponent_function_basic(a, 2.2), a_p, decimal=7) + exponent_function_basic(a, 2.2), a_p, decimal=7 + ) np.testing.assert_almost_equal( - exponent_function_basic(a, 2.2, 'basicMirrorFwd'), a_p, decimal=7) + exponent_function_basic(a, 2.2, "basicMirrorFwd"), a_p, decimal=7 + ) np.testing.assert_almost_equal( - exponent_function_basic(a, 2.2, 'basicPassThruFwd'), - a_p, - decimal=7) + exponent_function_basic(a, 2.2, "basicPassThruFwd"), a_p, decimal=7 + ) a = np.reshape(a, (2, 3)) a_p = np.reshape(a_p, (2, 3)) np.testing.assert_almost_equal( - exponent_function_basic(a, 2.2), a_p, decimal=7) + exponent_function_basic(a, 2.2), a_p, decimal=7 + ) np.testing.assert_almost_equal( - exponent_function_basic(a, 2.2, 'basicMirrorFwd'), a_p, decimal=7) + exponent_function_basic(a, 2.2, "basicMirrorFwd"), a_p, decimal=7 + ) np.testing.assert_almost_equal( - exponent_function_basic(a, 2.2, 'basicPassThruFwd'), - a_p, - decimal=7) + exponent_function_basic(a, 2.2, "basicPassThruFwd"), a_p, decimal=7 + ) a = np.reshape(a, (2, 3, 1)) a_p = np.reshape(a_p, (2, 3, 1)) np.testing.assert_almost_equal( - exponent_function_basic(a, 2.2), a_p, decimal=7) + exponent_function_basic(a, 2.2), a_p, decimal=7 + ) np.testing.assert_almost_equal( - exponent_function_basic(a, 2.2, 'basicMirrorFwd'), a_p, decimal=7) + exponent_function_basic(a, 2.2, "basicMirrorFwd"), a_p, decimal=7 + ) np.testing.assert_almost_equal( - exponent_function_basic(a, 2.2, 'basicPassThruFwd'), - a_p, - decimal=7) + exponent_function_basic(a, 2.2, "basicPassThruFwd"), a_p, decimal=7 + ) a = 0.0229932049927 a_p = 0.18 @@ -129,49 +144,43 @@ def test_n_dimensional_exponent_function_basic(self): a = np.tile(a, 6) a_p = np.tile(a_p, 6) np.testing.assert_almost_equal( - exponent_function_basic(a, 2.2, 'basicRev'), a_p, decimal=7) + exponent_function_basic(a, 2.2, "basicRev"), a_p, decimal=7 + ) np.testing.assert_almost_equal( - exponent_function_basic(a, 2.2, 'basicMirrorRev'), a_p, decimal=7) + exponent_function_basic(a, 2.2, "basicMirrorRev"), a_p, decimal=7 + ) np.testing.assert_almost_equal( - exponent_function_basic(a, 2.2, 'basicPassThruRev'), - a_p, - decimal=7) + exponent_function_basic(a, 2.2, "basicPassThruRev"), a_p, decimal=7 + ) a = np.reshape(a, (2, 3)) a_p = np.reshape(a_p, (2, 3)) np.testing.assert_almost_equal( - exponent_function_basic(a, 2.2, 'basicRev'), a_p, decimal=7) + exponent_function_basic(a, 2.2, "basicRev"), a_p, decimal=7 + ) np.testing.assert_almost_equal( - exponent_function_basic(a, 2.2, 'basicMirrorRev'), a_p, decimal=7) + exponent_function_basic(a, 2.2, "basicMirrorRev"), a_p, decimal=7 + ) np.testing.assert_almost_equal( - exponent_function_basic(a, 2.2, 'basicPassThruRev'), - a_p, - decimal=7) + exponent_function_basic(a, 2.2, "basicPassThruRev"), a_p, decimal=7 + ) a = np.reshape(a, (2, 3, 1)) a_p = np.reshape(a_p, (2, 3, 1)) np.testing.assert_almost_equal( - exponent_function_basic(a, 2.2, 'basicRev'), a_p, decimal=7) + exponent_function_basic(a, 2.2, "basicRev"), a_p, decimal=7 + ) np.testing.assert_almost_equal( - exponent_function_basic(a, 2.2, 'basicMirrorRev'), a_p, decimal=7) + exponent_function_basic(a, 2.2, "basicMirrorRev"), a_p, decimal=7 + ) np.testing.assert_almost_equal( - exponent_function_basic(a, 2.2, 'basicPassThruRev'), - a_p, - decimal=7) - - def test_raise_exception_exponent_function_basic(self): - """ - Tests :func:`colour.models.rgb.transfer_functions.exponent.\ -exponent_function_basic` definition raised exception. - """ - - self.assertRaises(ValueError, exponent_function_basic, 0.18, 1, - 'Undefined') + exponent_function_basic(a, 2.2, "basicPassThruRev"), a_p, decimal=7 + ) @ignore_numpy_errors def test_nan_exponent_function_basic(self): """ - Tests :func:`colour.models.rgb.transfer_functions.exponent.\ + Test :func:`colour.models.rgb.transfer_functions.exponent.\ exponent_function_basic` definition nan support. """ @@ -181,69 +190,81 @@ def test_nan_exponent_function_basic(self): exponent_function_basic(case, case) -class TestMonitorCurveExponentFunction(unittest.TestCase): +class TestExponentFunctionMonitorCurve(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.exponent.\ + Define :func:`colour.models.rgb.transfer_functions.exponent.\ exponent_function_monitor_curve` definition unit tests methods. """ def test_exponent_function_monitor_curve(self): """ - Tests :func:`colour.models.rgb.transfer_functions.exponent.\ + Test :func:`colour.models.rgb.transfer_functions.exponent.\ exponent_function_monitor_curve` definition. """ a = 0.18 a_p = 0.0232240466001 self.assertAlmostEqual( - exponent_function_monitor_curve(a, 2.2, 0.001), a_p, places=7) + exponent_function_monitor_curve(a, 2.2, 0.001), a_p, places=7 + ) self.assertAlmostEqual( - exponent_function_monitor_curve(a, 2.2, 0.001, - 'monCurveMirrorFwd'), + exponent_function_monitor_curve( + a, 2.2, 0.001, "monCurveMirrorFwd" + ), a_p, - places=7) + places=7, + ) a = 0.0232240466001 a_p = 0.18 self.assertAlmostEqual( - exponent_function_monitor_curve(a, 2.2, 0.001, 'monCurveRev'), + exponent_function_monitor_curve(a, 2.2, 0.001, "monCurveRev"), a_p, - places=7) + places=7, + ) self.assertAlmostEqual( - exponent_function_monitor_curve(a, 2.2, 0.001, - 'monCurveMirrorRev'), + exponent_function_monitor_curve( + a, 2.2, 0.001, "monCurveMirrorRev" + ), a_p, - places=7) + places=7, + ) a = -0.18 self.assertAlmostEqual( exponent_function_monitor_curve(a, 2.2, 0.001), -0.000205413951, - places=7) + places=7, + ) self.assertAlmostEqual( - exponent_function_monitor_curve(a, 2.2, 0.001, - 'monCurveMirrorFwd'), + exponent_function_monitor_curve( + a, 2.2, 0.001, "monCurveMirrorFwd" + ), -0.0232240466001, - places=7) + places=7, + ) a = -0.000205413951 self.assertAlmostEqual( - exponent_function_monitor_curve(a, 2.2, 0.001, 'monCurveRev'), + exponent_function_monitor_curve(a, 2.2, 0.001, "monCurveRev"), -0.18, - places=7) + places=7, + ) self.assertAlmostEqual( - exponent_function_monitor_curve(a, 2.2, 0.001, - 'monCurveMirrorRev'), + exponent_function_monitor_curve( + a, 2.2, 0.001, "monCurveMirrorRev" + ), -0.0201036111565, - places=7) + places=7, + ) def test_n_dimensional_exponent_function_monitor_curve(self): """ - Tests :func:`colour.models.rgb.transfer_functions.exponent.\ + Test :func:`colour.models.rgb.transfer_functions.exponent.\ exponent_function_monitor_curve` definition n-dimensional arrays support. """ @@ -253,32 +274,41 @@ def test_n_dimensional_exponent_function_monitor_curve(self): a = np.tile(a, 6) a_p = np.tile(a_p, 6) np.testing.assert_almost_equal( - exponent_function_monitor_curve(a, 2.2, 0.001), a_p, decimal=7) + exponent_function_monitor_curve(a, 2.2, 0.001), a_p, decimal=7 + ) np.testing.assert_almost_equal( - exponent_function_monitor_curve(a, 2.2, 0.001, - 'monCurveMirrorFwd'), + exponent_function_monitor_curve( + a, 2.2, 0.001, "monCurveMirrorFwd" + ), a_p, - decimal=7) + decimal=7, + ) a = np.reshape(a, (2, 3)) a_p = np.reshape(a_p, (2, 3)) np.testing.assert_almost_equal( - exponent_function_monitor_curve(a, 2.2, 0.001), a_p, decimal=7) + exponent_function_monitor_curve(a, 2.2, 0.001), a_p, decimal=7 + ) np.testing.assert_almost_equal( - exponent_function_monitor_curve(a, 2.2, 0.001, - 'monCurveMirrorFwd'), + exponent_function_monitor_curve( + a, 2.2, 0.001, "monCurveMirrorFwd" + ), a_p, - decimal=7) + decimal=7, + ) a = np.reshape(a, (2, 3, 1)) a_p = np.reshape(a_p, (2, 3, 1)) np.testing.assert_almost_equal( - exponent_function_monitor_curve(a, 2.2, 0.001), a_p, decimal=7) + exponent_function_monitor_curve(a, 2.2, 0.001), a_p, decimal=7 + ) np.testing.assert_almost_equal( - exponent_function_monitor_curve(a, 2.2, 0.001, - 'monCurveMirrorFwd'), + exponent_function_monitor_curve( + a, 2.2, 0.001, "monCurveMirrorFwd" + ), a_p, - decimal=7) + decimal=7, + ) a = 0.0232240466001 a_p = 0.18 @@ -286,52 +316,52 @@ def test_n_dimensional_exponent_function_monitor_curve(self): a = np.tile(a, 6) a_p = np.tile(a_p, 6) np.testing.assert_almost_equal( - exponent_function_monitor_curve(a, 2.2, 0.001, 'monCurveRev'), + exponent_function_monitor_curve(a, 2.2, 0.001, "monCurveRev"), a_p, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - exponent_function_monitor_curve(a, 2.2, 0.001, - 'monCurveMirrorRev'), + exponent_function_monitor_curve( + a, 2.2, 0.001, "monCurveMirrorRev" + ), a_p, - decimal=7) + decimal=7, + ) a = np.reshape(a, (2, 3)) a_p = np.reshape(a_p, (2, 3)) np.testing.assert_almost_equal( - exponent_function_monitor_curve(a, 2.2, 0.001, 'monCurveRev'), + exponent_function_monitor_curve(a, 2.2, 0.001, "monCurveRev"), a_p, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - exponent_function_monitor_curve(a, 2.2, 0.001, - 'monCurveMirrorRev'), + exponent_function_monitor_curve( + a, 2.2, 0.001, "monCurveMirrorRev" + ), a_p, - decimal=7) + decimal=7, + ) a = np.reshape(a, (2, 3, 1)) a_p = np.reshape(a_p, (2, 3, 1)) np.testing.assert_almost_equal( - exponent_function_monitor_curve(a, 2.2, 0.001, 'monCurveRev'), + exponent_function_monitor_curve(a, 2.2, 0.001, "monCurveRev"), a_p, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - exponent_function_monitor_curve(a, 2.2, 0.001, - 'monCurveMirrorRev'), + exponent_function_monitor_curve( + a, 2.2, 0.001, "monCurveMirrorRev" + ), a_p, - decimal=7) - - def test_raise_exception_exponent_function_monitor_curve(self): - """ - Tests :func:`colour.models.rgb.transfer_functions.exponent.\ -exponent_function_monitor_curve` definition raised exception. - """ - - self.assertRaises(ValueError, exponent_function_monitor_curve, 0.18, 1, - 'Undefined') + decimal=7, + ) @ignore_numpy_errors def test_nan_exponent_function_monitor_curve(self): """ - Tests :func:`colour.models.rgb.transfer_functions.exponent.\ + Test :func:`colour.models.rgb.transfer_functions.exponent.\ exponent_function_monitor_curve` definition nan support. """ @@ -341,5 +371,5 @@ def test_nan_exponent_function_monitor_curve(self): exponent_function_monitor_curve(case, case, case) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_filmic_pro.py b/colour/models/rgb/transfer_functions/tests/test_filmic_pro.py index 1ad3fa2ad3..92c765b6d1 100644 --- a/colour/models/rgb/transfer_functions/tests/test_filmic_pro.py +++ b/colour/models/rgb/transfer_functions/tests/test_filmic_pro.py @@ -1,51 +1,55 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.filmic_pro` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.filmic_pro` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.models.rgb.transfer_functions import (log_encoding_FilmicPro6, - log_decoding_FilmicPro6) +from colour.models.rgb.transfer_functions import ( + log_encoding_FilmicPro6, + log_decoding_FilmicPro6, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestLogEncoding_FilmicPro6', 'TestLogDecoding_FilmicPro6'] +__all__ = [ + "TestLogEncoding_FilmicPro6", + "TestLogDecoding_FilmicPro6", +] class TestLogEncoding_FilmicPro6(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.filmic_pro.\ + Define :func:`colour.models.rgb.transfer_functions.filmic_pro.\ log_encoding_FilmicPro6` definition unit tests methods. """ def test_log_encoding_FilmicPro6(self): """ - Tests :func:`colour.models.rgb.transfer_functions.filmic_pro.\ + Test :func:`colour.models.rgb.transfer_functions.filmic_pro.\ log_encoding_FilmicPro6` definition. """ self.assertAlmostEqual(log_encoding_FilmicPro6(0.0), -np.inf, places=7) self.assertAlmostEqual( - log_encoding_FilmicPro6(0.18), 0.606634519924703, places=7) + log_encoding_FilmicPro6(0.18), 0.606634519924703, places=7 + ) self.assertAlmostEqual( - log_encoding_FilmicPro6(1.0), 1.000000819999999, places=7) + log_encoding_FilmicPro6(1.0), 1.000000819999999, places=7 + ) def test_n_dimensional_log_encoding_FilmicPro6(self): """ - Tests :func:`colour.models.rgb.transfer_functions.filmic_pro.\ + Test :func:`colour.models.rgb.transfer_functions.filmic_pro.\ log_encoding_FilmicPro6` definition n-dimensional arrays support. """ @@ -55,67 +59,74 @@ def test_n_dimensional_log_encoding_FilmicPro6(self): x = np.tile(x, 6) y = np.tile(y, 6) np.testing.assert_almost_equal( - log_encoding_FilmicPro6(x), y, decimal=7) + log_encoding_FilmicPro6(x), y, decimal=7 + ) x = np.reshape(x, (2, 3)) y = np.reshape(y, (2, 3)) np.testing.assert_almost_equal( - log_encoding_FilmicPro6(x), y, decimal=7) + log_encoding_FilmicPro6(x), y, decimal=7 + ) x = np.reshape(x, (2, 3, 1)) y = np.reshape(y, (2, 3, 1)) np.testing.assert_almost_equal( - log_encoding_FilmicPro6(x), y, decimal=7) + log_encoding_FilmicPro6(x), y, decimal=7 + ) def test_domain_range_scale_log_encoding_FilmicPro6(self): """ - Tests :func:`colour.models.rgb.transfer_functions.filmic_pro.\ + Test :func:`colour.models.rgb.transfer_functions.filmic_pro.\ log_encoding_FilmicPro6` definition domain and range scale support. """ x = 0.18 y = log_encoding_FilmicPro6(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_FilmicPro6(x * factor), y * factor, decimal=7) + log_encoding_FilmicPro6(x * factor), y * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_FilmicPro6(self): """ - Tests :func:`colour.models.rgb.transfer_functions.filmic_pro.\ + Test :func:`colour.models.rgb.transfer_functions.filmic_pro.\ log_encoding_FilmicPro6` definition nan support. """ log_encoding_FilmicPro6( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_FilmicPro6(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.filmic_pro.\ + Define :func:`colour.models.rgb.transfer_functions.filmic_pro.\ log_decoding_FilmicPro6` definition unit tests methods. """ def test_log_decoding_FilmicPro6(self): """ - Tests :func:`colour.models.rgb.transfer_functions.filmic_pro.\ + Test :func:`colour.models.rgb.transfer_functions.filmic_pro.\ log_decoding_FilmicPro6` definition. """ np.testing.assert_array_equal(log_decoding_FilmicPro6(-np.inf), 0.0) self.assertAlmostEqual( - log_decoding_FilmicPro6(0.606634519924703), 0.18, places=7) + log_decoding_FilmicPro6(0.606634519924703), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_FilmicPro6(1.000000819999999), 1.0, places=7) + log_decoding_FilmicPro6(1.000000819999999), 1.0, places=7 + ) def test_n_dimensional_log_decoding_FilmicPro6(self): """ - Tests :func:`colour.models.rgb.transfer_functions.filmic_pro.\ + Test :func:`colour.models.rgb.transfer_functions.filmic_pro.\ log_decoding_FilmicPro6` definition n-dimensional arrays support. """ @@ -125,43 +136,48 @@ def test_n_dimensional_log_decoding_FilmicPro6(self): y = np.tile(y, 6) x = np.tile(x, 6) np.testing.assert_almost_equal( - log_decoding_FilmicPro6(y), x, decimal=7) + log_decoding_FilmicPro6(y), x, decimal=7 + ) y = np.reshape(y, (2, 3)) x = np.reshape(x, (2, 3)) np.testing.assert_almost_equal( - log_decoding_FilmicPro6(y), x, decimal=7) + log_decoding_FilmicPro6(y), x, decimal=7 + ) y = np.reshape(y, (2, 3, 1)) x = np.reshape(x, (2, 3, 1)) np.testing.assert_almost_equal( - log_decoding_FilmicPro6(y), x, decimal=7) + log_decoding_FilmicPro6(y), x, decimal=7 + ) def test_domain_range_scale_log_decoding_FilmicPro6(self): """ - Tests :func:`colour.models.rgb.transfer_functions.filmic_pro.\ + Test :func:`colour.models.rgb.transfer_functions.filmic_pro.\ log_decoding_FilmicPro6` definition domain and range scale support. """ y = 0.606634519924703 x = log_decoding_FilmicPro6(y) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_FilmicPro6(y * factor), x * factor, decimal=7) + log_decoding_FilmicPro6(y * factor), x * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_FilmicPro6(self): """ - Tests :func:`colour.models.rgb.transfer_functions.filmic_pro.\ + Test :func:`colour.models.rgb.transfer_functions.filmic_pro.\ log_decoding_FilmicPro6` definition nan support. """ log_decoding_FilmicPro6( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_filmlight_tlog.py b/colour/models/rgb/transfer_functions/tests/test_filmlight_tlog.py index 609cafcfcd..e9875a6965 100644 --- a/colour/models/rgb/transfer_functions/tests/test_filmlight_tlog.py +++ b/colour/models/rgb/transfer_functions/tests/test_filmlight_tlog.py @@ -1,52 +1,57 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.\ +Defines the unit tests for the :mod:`colour.models.rgb.transfer_functions.\ filmlight_tlog` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.models.rgb.transfer_functions import (log_encoding_FilmLightTLog, - log_decoding_FilmLightTLog) +from colour.models.rgb.transfer_functions import ( + log_encoding_FilmLightTLog, + log_decoding_FilmLightTLog, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestLogEncoding_FilmLightTLog', 'TestLogDecoding_FilmLightTLog'] +__all__ = [ + "TestLogEncoding_FilmLightTLog", + "TestLogDecoding_FilmLightTLog", +] class TestLogEncoding_FilmLightTLog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ + Define :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ log_encoding_FilmLightTLog` definition unit tests methods. """ def test_log_encoding_FilmLightTLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ + Test :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ log_encoding_FilmLightTLog` definition. """ self.assertAlmostEqual( - log_encoding_FilmLightTLog(0.0), 0.075, places=7) + log_encoding_FilmLightTLog(0.0), 0.075, places=7 + ) self.assertAlmostEqual( - log_encoding_FilmLightTLog(0.18), 0.396567801298332, places=7) + log_encoding_FilmLightTLog(0.18), 0.396567801298332, places=7 + ) self.assertAlmostEqual( - log_encoding_FilmLightTLog(1.0), 0.552537881005859, places=7) + log_encoding_FilmLightTLog(1.0), 0.552537881005859, places=7 + ) def test_n_dimensional_log_encoding_TLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ + Test :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ log_encoding_FilmLightTLog` definition n-dimensional arrays support. """ @@ -56,70 +61,78 @@ def test_n_dimensional_log_encoding_TLog(self): x = np.tile(x, 6) t = np.tile(t, 6) np.testing.assert_almost_equal( - log_encoding_FilmLightTLog(x), t, decimal=7) + log_encoding_FilmLightTLog(x), t, decimal=7 + ) x = np.reshape(x, (2, 3)) t = np.reshape(t, (2, 3)) np.testing.assert_almost_equal( - log_encoding_FilmLightTLog(x), t, decimal=7) + log_encoding_FilmLightTLog(x), t, decimal=7 + ) x = np.reshape(x, (2, 3, 1)) t = np.reshape(t, (2, 3, 1)) np.testing.assert_almost_equal( - log_encoding_FilmLightTLog(x), t, decimal=7) + log_encoding_FilmLightTLog(x), t, decimal=7 + ) def test_domain_range_scale_log_encoding_TLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ + Test :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ log_encoding_FilmLightTLog` definition domain and range scale support. """ x = 0.18 t = log_encoding_FilmLightTLog(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( log_encoding_FilmLightTLog(x * factor), t * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_log_encoding_TLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ + Test :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ log_encoding_FilmLightTLog` definition nan support. """ log_encoding_FilmLightTLog( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_FilmLightTLog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ + Define :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ log_decoding_FilmLightTLog` definition unit tests methods. """ def test_log_decoding_FilmLightTLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ + Test :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ log_decoding_FilmLightTLog` definition. """ self.assertAlmostEqual( - log_decoding_FilmLightTLog(0.075), 0.0, places=7) + log_decoding_FilmLightTLog(0.075), 0.0, places=7 + ) self.assertAlmostEqual( - log_decoding_FilmLightTLog(0.396567801298332), 0.18, places=7) + log_decoding_FilmLightTLog(0.396567801298332), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_FilmLightTLog(0.552537881005859), 1.0, places=7) + log_decoding_FilmLightTLog(0.552537881005859), 1.0, places=7 + ) def test_n_dimensional_log_decoding_TLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ + Test :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ log_decoding_FilmLightTLog` definition n-dimensional arrays support. """ @@ -129,45 +142,50 @@ def test_n_dimensional_log_decoding_TLog(self): t = np.tile(t, 6) x = np.tile(x, 6) np.testing.assert_almost_equal( - log_decoding_FilmLightTLog(t), x, decimal=7) + log_decoding_FilmLightTLog(t), x, decimal=7 + ) t = np.reshape(t, (2, 3)) x = np.reshape(x, (2, 3)) np.testing.assert_almost_equal( - log_decoding_FilmLightTLog(t), x, decimal=7) + log_decoding_FilmLightTLog(t), x, decimal=7 + ) t = np.reshape(t, (2, 3, 1)) x = np.reshape(x, (2, 3, 1)) np.testing.assert_almost_equal( - log_decoding_FilmLightTLog(t), x, decimal=7) + log_decoding_FilmLightTLog(t), x, decimal=7 + ) def test_domain_range_scale_log_decoding_TLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ + Test :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ log_decoding_FilmLightTLog` definition domain and range scale support. """ t = 0.396567801298332 x = log_decoding_FilmLightTLog(t) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( log_decoding_FilmLightTLog(t * factor), x * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_log_decoding_TLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ + Test :func:`colour.models.rgb.transfer_functions.filmlight_tlog.\ log_decoding_FilmLightTLog` definition nan support. """ log_decoding_FilmLightTLog( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_fujifilm_flog.py b/colour/models/rgb/transfer_functions/tests/test_fujifilm_flog.py index dbe7971601..094c7cb3d7 100644 --- a/colour/models/rgb/transfer_functions/tests/test_fujifilm_flog.py +++ b/colour/models/rgb/transfer_functions/tests/test_fujifilm_flog.py @@ -1,63 +1,71 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.\ +Defines the unit tests for the :mod:`colour.models.rgb.transfer_functions.\ fujifilm_flog` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.models.rgb.transfer_functions import (log_encoding_FLog, - log_decoding_FLog) +from colour.models.rgb.transfer_functions import ( + log_encoding_FLog, + log_decoding_FLog, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestLogEncoding_VLog', 'TestLogDecoding_VLog'] +__all__ = [ + "TestLogEncoding_FLog", + "TestLogDecoding_FLog", +] -class TestLogEncoding_VLog(unittest.TestCase): +class TestLogEncoding_FLog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ + Define :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ log_encoding_FLog` definition unit tests methods. """ def test_log_encoding_FLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ + Test :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ log_encoding_FLog` definition. """ self.assertAlmostEqual( - log_encoding_FLog(0.0), 0.092864000000000, places=7) + log_encoding_FLog(0.0), 0.092864000000000, places=7 + ) self.assertAlmostEqual( - log_encoding_FLog(0.18), 0.459318458661621, places=7) + log_encoding_FLog(0.18), 0.459318458661621, places=7 + ) self.assertAlmostEqual( - log_encoding_FLog(0.18, 12), 0.459318458661621, places=7) + log_encoding_FLog(0.18, 12), 0.459318458661621, places=7 + ) self.assertAlmostEqual( - log_encoding_FLog(0.18, 10, False), 0.463336510514656, places=7) + log_encoding_FLog(0.18, 10, False), 0.463336510514656, places=7 + ) self.assertAlmostEqual( log_encoding_FLog(0.18, 10, False, False), 0.446590337236003, - places=7) + places=7, + ) self.assertAlmostEqual( - log_encoding_FLog(1.0), 0.704996409216428, places=7) + log_encoding_FLog(1.0), 0.704996409216428, places=7 + ) def test_n_dimensional_log_encoding_FLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ + Test :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ log_encoding_FLog` definition n-dimensional arrays support. """ @@ -67,80 +75,88 @@ def test_n_dimensional_log_encoding_FLog(self): L_in = np.tile(L_in, 6) V_out = np.tile(V_out, 6) np.testing.assert_almost_equal( - log_encoding_FLog(L_in), V_out, decimal=7) + log_encoding_FLog(L_in), V_out, decimal=7 + ) L_in = np.reshape(L_in, (2, 3)) V_out = np.reshape(V_out, (2, 3)) np.testing.assert_almost_equal( - log_encoding_FLog(L_in), V_out, decimal=7) + log_encoding_FLog(L_in), V_out, decimal=7 + ) L_in = np.reshape(L_in, (2, 3, 1)) V_out = np.reshape(V_out, (2, 3, 1)) np.testing.assert_almost_equal( - log_encoding_FLog(L_in), V_out, decimal=7) + log_encoding_FLog(L_in), V_out, decimal=7 + ) def test_domain_range_scale_log_encoding_FLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ + Test :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ log_encoding_FLog` definition domain and range scale support. """ L_in = 0.18 V_out = log_encoding_FLog(L_in) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_FLog(L_in * factor), - V_out * factor, - decimal=7) + log_encoding_FLog(L_in * factor), V_out * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_FLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ + Test :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ log_encoding_FLog` definition nan support. """ log_encoding_FLog(np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) -class TestLogDecoding_VLog(unittest.TestCase): +class TestLogDecoding_FLog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ + Define :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ log_decoding_FLog` definition unit tests methods. """ def test_log_decoding_FLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ + Test :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ log_decoding_FLog` definition. """ self.assertAlmostEqual( - log_decoding_FLog(0.092864000000000), 0.0, places=7) + log_decoding_FLog(0.092864000000000), 0.0, places=7 + ) self.assertAlmostEqual( - log_decoding_FLog(0.459318458661621), 0.18, places=7) + log_decoding_FLog(0.459318458661621), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_FLog(0.459318458661621, 12), 0.18, places=7) + log_decoding_FLog(0.459318458661621, 12), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_FLog(0.463336510514656, 10, False), 0.18, places=7) + log_decoding_FLog(0.463336510514656, 10, False), 0.18, places=7 + ) self.assertAlmostEqual( log_decoding_FLog(0.446590337236003, 10, False, False), 0.18, - places=7) + places=7, + ) self.assertAlmostEqual( - log_decoding_FLog(0.704996409216428), 1.0, places=7) + log_decoding_FLog(0.704996409216428), 1.0, places=7 + ) def test_n_dimensional_log_decoding_FLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ + Test :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ log_decoding_FLog` definition n-dimensional arrays support. """ @@ -150,44 +166,46 @@ def test_n_dimensional_log_decoding_FLog(self): V_out = np.tile(V_out, 6) L_in = np.tile(L_in, 6) np.testing.assert_almost_equal( - log_decoding_FLog(V_out), L_in, decimal=7) + log_decoding_FLog(V_out), L_in, decimal=7 + ) V_out = np.reshape(V_out, (2, 3)) L_in = np.reshape(L_in, (2, 3)) np.testing.assert_almost_equal( - log_decoding_FLog(V_out), L_in, decimal=7) + log_decoding_FLog(V_out), L_in, decimal=7 + ) V_out = np.reshape(V_out, (2, 3, 1)) L_in = np.reshape(L_in, (2, 3, 1)) np.testing.assert_almost_equal( - log_decoding_FLog(V_out), L_in, decimal=7) + log_decoding_FLog(V_out), L_in, decimal=7 + ) def test_domain_range_scale_log_decoding_FLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ + Test :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ log_decoding_FLog` definition domain and range scale support. """ V_out = 0.459318458661621 L_in = log_decoding_FLog(V_out) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_FLog(V_out * factor), - L_in * factor, - decimal=7) + log_decoding_FLog(V_out * factor), L_in * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_FLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ + Test :func:`colour.models.rgb.transfer_functions.fujifilm_flog.\ log_decoding_FLog` definition nan support. """ log_decoding_FLog(np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_gamma.py b/colour/models/rgb/transfer_functions/tests/test_gamma.py index 9b8ddee573..e7c0ec1011 100644 --- a/colour/models/rgb/transfer_functions/tests/test_gamma.py +++ b/colour/models/rgb/transfer_functions/tests/test_gamma.py @@ -1,74 +1,81 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.gamma` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.gamma` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from colour.models.rgb.transfer_functions import gamma_function from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestGammaFunction'] +__all__ = [ + "TestGammaFunction", +] class TestGammaFunction(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.gamma.gamma_function` + Define :func:`colour.models.rgb.transfer_functions.gamma.gamma_function` definition unit tests methods. """ def test_gamma_function(self): """ - Tests :func:`colour.models.rgb.transfer_functions.gamma.\ + Test :func:`colour.models.rgb.transfer_functions.gamma.\ gamma_function` definition. """ self.assertAlmostEqual(gamma_function(0.0, 2.2), 0.0, places=7) self.assertAlmostEqual( - gamma_function(0.18, 2.2), 0.022993204992707, places=7) + gamma_function(0.18, 2.2), 0.022993204992707, places=7 + ) self.assertAlmostEqual( - gamma_function(0.022993204992707, 1.0 / 2.2), 0.18, places=7) + gamma_function(0.022993204992707, 1.0 / 2.2), 0.18, places=7 + ) self.assertAlmostEqual( - gamma_function(-0.18, 2.0), 0.0323999999999998, places=7) + gamma_function(-0.18, 2.0), 0.0323999999999998, places=7 + ) np.testing.assert_array_equal(gamma_function(-0.18, 2.2), np.nan) self.assertAlmostEqual( - gamma_function(-0.18, 2.2, 'Mirror'), -0.022993204992707, places=7) + gamma_function(-0.18, 2.2, "Mirror"), -0.022993204992707, places=7 + ) self.assertAlmostEqual( - gamma_function(-0.18, 2.2, 'Preserve'), -0.18, places=7) + gamma_function(-0.18, 2.2, "Preserve"), -0.18, places=7 + ) self.assertAlmostEqual( - gamma_function(-0.18, 2.2, 'Clamp'), 0, places=7) + gamma_function(-0.18, 2.2, "Clamp"), 0, places=7 + ) np.testing.assert_array_equal(gamma_function(-0.18, -2.2), np.nan) self.assertAlmostEqual( - gamma_function(0.0, -2.2, 'Mirror'), 0.0, places=7) + gamma_function(0.0, -2.2, "Mirror"), 0.0, places=7 + ) self.assertAlmostEqual( - gamma_function(0.0, 2.2, 'Preserve'), 0.0, places=7) + gamma_function(0.0, 2.2, "Preserve"), 0.0, places=7 + ) - self.assertAlmostEqual(gamma_function(0.0, 2.2, 'Clamp'), 0, places=7) + self.assertAlmostEqual(gamma_function(0.0, 2.2, "Clamp"), 0, places=7) def test_n_dimensional_gamma_function(self): """ - Tests :func:`colour.models.rgb.transfer_functions.gamma.\ + Test :func:`colour.models.rgb.transfer_functions.gamma.\ gamma_function` definition n-dimensional arrays support. """ @@ -90,75 +97,79 @@ def test_n_dimensional_gamma_function(self): a = -0.18 a_p = -0.022993204992707 np.testing.assert_almost_equal( - gamma_function(a, 2.2, 'Mirror'), a_p, decimal=7) + gamma_function(a, 2.2, "Mirror"), a_p, decimal=7 + ) a = np.tile(a, 6) a_p = np.tile(a_p, 6) np.testing.assert_almost_equal( - gamma_function(a, 2.2, 'Mirror'), a_p, decimal=7) + gamma_function(a, 2.2, "Mirror"), a_p, decimal=7 + ) a = np.reshape(a, (2, 3)) a_p = np.reshape(a_p, (2, 3)) np.testing.assert_almost_equal( - gamma_function(a, 2.2, 'Mirror'), a_p, decimal=7) + gamma_function(a, 2.2, "Mirror"), a_p, decimal=7 + ) a = np.reshape(a, (2, 3, 1)) a_p = np.reshape(a_p, (2, 3, 1)) np.testing.assert_almost_equal( - gamma_function(a, 2.2, 'Mirror'), a_p, decimal=7) + gamma_function(a, 2.2, "Mirror"), a_p, decimal=7 + ) a = -0.18 a_p = -0.18 np.testing.assert_almost_equal( - gamma_function(a, 2.2, 'Preserve'), a_p, decimal=7) + gamma_function(a, 2.2, "Preserve"), a_p, decimal=7 + ) a = np.tile(a, 6) a_p = np.tile(a_p, 6) np.testing.assert_almost_equal( - gamma_function(a, 2.2, 'Preserve'), a_p, decimal=7) + gamma_function(a, 2.2, "Preserve"), a_p, decimal=7 + ) a = np.reshape(a, (2, 3)) a_p = np.reshape(a_p, (2, 3)) np.testing.assert_almost_equal( - gamma_function(a, 2.2, 'Preserve'), a_p, decimal=7) + gamma_function(a, 2.2, "Preserve"), a_p, decimal=7 + ) a = np.reshape(a, (2, 3, 1)) a_p = np.reshape(a_p, (2, 3, 1)) np.testing.assert_almost_equal( - gamma_function(a, 2.2, 'Preserve'), a_p, decimal=7) + gamma_function(a, 2.2, "Preserve"), a_p, decimal=7 + ) a = -0.18 a_p = 0.0 np.testing.assert_almost_equal( - gamma_function(a, 2.2, 'Clamp'), a_p, decimal=7) + gamma_function(a, 2.2, "Clamp"), a_p, decimal=7 + ) a = np.tile(a, 6) a_p = np.tile(a_p, 6) np.testing.assert_almost_equal( - gamma_function(a, 2.2, 'Clamp'), a_p, decimal=7) + gamma_function(a, 2.2, "Clamp"), a_p, decimal=7 + ) a = np.reshape(a, (2, 3)) a_p = np.reshape(a_p, (2, 3)) np.testing.assert_almost_equal( - gamma_function(a, 2.2, 'Clamp'), a_p, decimal=7) + gamma_function(a, 2.2, "Clamp"), a_p, decimal=7 + ) a = np.reshape(a, (2, 3, 1)) a_p = np.reshape(a_p, (2, 3, 1)) np.testing.assert_almost_equal( - gamma_function(a, 2.2, 'Clamp'), a_p, decimal=7) - - def test_raise_exception_gamma_function(self): - """ - Tests :func:`colour.models.rgb.transfer_functions.gamma.\ -gamma_function` definition raised exception. - """ - - self.assertRaises(ValueError, gamma_function, 0.18, 1, 'Undefined') + gamma_function(a, 2.2, "Clamp"), a_p, decimal=7 + ) @ignore_numpy_errors def test_nan_gamma_function(self): """ - Tests :func:`colour.models.rgb.transfer_functions.gamma.\ + Test :func:`colour.models.rgb.transfer_functions.gamma.\ gamma_function` definition nan support. """ @@ -168,5 +179,5 @@ def test_nan_gamma_function(self): gamma_function(case, case) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_gopro.py b/colour/models/rgb/transfer_functions/tests/test_gopro.py index b4b0aec0a0..0dd5c8f925 100644 --- a/colour/models/rgb/transfer_functions/tests/test_gopro.py +++ b/colour/models/rgb/transfer_functions/tests/test_gopro.py @@ -1,50 +1,53 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.gopro` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.gopro` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.models.rgb.transfer_functions import (log_encoding_Protune, - log_decoding_Protune) +from colour.models.rgb.transfer_functions import ( + log_encoding_Protune, + log_decoding_Protune, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestLogEncoding_Protune', 'TestLogDecoding_Protune'] +__all__ = [ + "TestLogEncoding_Protune", + "TestLogDecoding_Protune", +] class TestLogEncoding_Protune(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.gopro.\ + Define :func:`colour.models.rgb.transfer_functions.gopro.\ log_encoding_Protune` definition unit tests methods. """ def test_log_encoding_Protune(self): """ - Tests :func:`colour.models.rgb.transfer_functions.gopro.\ + Test :func:`colour.models.rgb.transfer_functions.gopro.\ log_encoding_Protune` definition. """ self.assertAlmostEqual(log_encoding_Protune(0.0), 0.0, places=7) self.assertAlmostEqual( - log_encoding_Protune(0.18), 0.645623486803636, places=7) + log_encoding_Protune(0.18), 0.645623486803636, places=7 + ) self.assertAlmostEqual(log_encoding_Protune(1.0), 1.0, places=7) def test_n_dimensional_log_encoding_Protune(self): """ - Tests :func:`colour.models.rgb.transfer_functions.gopro.\ + Test :func:`colour.models.rgb.transfer_functions.gopro.\ log_encoding_Protune` definition n-dimensional arrays support. """ @@ -65,52 +68,55 @@ def test_n_dimensional_log_encoding_Protune(self): def test_domain_range_scale_log_encoding_Protune(self): """ - Tests :func:`colour.models.rgb.transfer_functions.gopro.\ + Test :func:`colour.models.rgb.transfer_functions.gopro.\ log_encoding_Protune` definition domain and range scale support. """ x = 0.18 y = log_encoding_Protune(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_Protune(x * factor), y * factor, decimal=7) + log_encoding_Protune(x * factor), y * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_Protune(self): """ - Tests :func:`colour.models.rgb.transfer_functions.gopro.\ + Test :func:`colour.models.rgb.transfer_functions.gopro.\ log_encoding_Protune` definition nan support. """ log_encoding_Protune( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_Protune(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.gopro.\ + Define :func:`colour.models.rgb.transfer_functions.gopro.\ log_decoding_Protune` definition unit tests methods. """ def test_log_decoding_Protune(self): """ - Tests :func:`colour.models.rgb.transfer_functions.gopro.\ + Test :func:`colour.models.rgb.transfer_functions.gopro.\ log_decoding_Protune` definition. """ self.assertAlmostEqual(log_decoding_Protune(0.0), 0.0, places=7) self.assertAlmostEqual( - log_decoding_Protune(0.645623486803636), 0.18, places=7) + log_decoding_Protune(0.645623486803636), 0.18, places=7 + ) self.assertAlmostEqual(log_decoding_Protune(1.0), 1.0, places=7) def test_n_dimensional_log_decoding_Protune(self): """ - Tests :func:`colour.models.rgb.transfer_functions.gopro.\ + Test :func:`colour.models.rgb.transfer_functions.gopro.\ log_decoding_Protune` definition n-dimensional arrays support. """ @@ -131,29 +137,31 @@ def test_n_dimensional_log_decoding_Protune(self): def test_domain_range_scale_log_decoding_Protune(self): """ - Tests :func:`colour.models.rgb.transfer_functions.gopro.\ + Test :func:`colour.models.rgb.transfer_functions.gopro.\ log_decoding_Protune` definition domain and range scale support. """ y = 0.645623486803636 x = log_decoding_Protune(y) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_Protune(y * factor), x * factor, decimal=7) + log_decoding_Protune(y * factor), x * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_Protune(self): """ - Tests :func:`colour.models.rgb.transfer_functions.gopro.\ + Test :func:`colour.models.rgb.transfer_functions.gopro.\ log_decoding_Protune` definition nan support. """ log_decoding_Protune( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_itur_bt_1886.py b/colour/models/rgb/transfer_functions/tests/test_itur_bt_1886.py index 9e525a56ae..542118877e 100644 --- a/colour/models/rgb/transfer_functions/tests/test_itur_bt_1886.py +++ b/colour/models/rgb/transfer_functions/tests/test_itur_bt_1886.py @@ -1,50 +1,53 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.itur_bt_1886` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.itur_bt_1886` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.models.rgb.transfer_functions import (eotf_inverse_BT1886, - eotf_BT1886) +from colour.models.rgb.transfer_functions import ( + eotf_inverse_BT1886, + eotf_BT1886, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestEotf_inverse_BT1886', 'TestEotf_BT1886'] +__all__ = [ + "TestEotf_inverse_BT1886", + "TestEotf_BT1886", +] class TestEotf_inverse_BT1886(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ eotf_inverse_BT1886` definition unit tests methods. """ def test_eotf_inverse_BT1886(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ eotf_inverse_BT1886` definition. """ self.assertAlmostEqual(eotf_inverse_BT1886(0.0), 0.0, places=7) self.assertAlmostEqual( - eotf_inverse_BT1886(0.016317514686316), 0.18, places=7) + eotf_inverse_BT1886(0.016317514686316), 0.18, places=7 + ) self.assertAlmostEqual(eotf_inverse_BT1886(1.0), 1.0, places=7) def test_n_dimensional_eotf_inverse_BT1886(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ eotf_inverse_BT1886` definition n-dimensional arrays support. """ @@ -65,39 +68,41 @@ def test_n_dimensional_eotf_inverse_BT1886(self): def test_domain_range_scale_eotf_inverse_BT1886(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ eotf_inverse_BT1886` definition domain and range scale support. """ L = 0.18 V = eotf_inverse_BT1886(L) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - eotf_inverse_BT1886(L * factor), V * factor, decimal=7) + eotf_inverse_BT1886(L * factor), V * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_eotf_inverse_BT1886(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ eotf_inverse_BT1886` definition nan support. """ eotf_inverse_BT1886( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestEotf_BT1886(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ eotf_BT1886` definition unit tests methods. """ def test_eotf_BT1886(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ eotf_BT1886` definition. """ @@ -109,7 +114,7 @@ def test_eotf_BT1886(self): def test_n_dimensional_eotf_BT1886(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ eotf_BT1886` definition n-dimensional arrays support. """ @@ -130,28 +135,29 @@ def test_n_dimensional_eotf_BT1886(self): def test_domain_range_scale_eotf_BT1886(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ eotf_BT1886` definition domain and range scale support. """ V = 0.016317514686316 L = eotf_BT1886(V) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - eotf_BT1886(V * factor), L * factor, decimal=7) + eotf_BT1886(V * factor), L * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_eotf_BT1886(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_1886.\ eotf_BT1886` definition nan support. """ eotf_BT1886(np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_itur_bt_2020.py b/colour/models/rgb/transfer_functions/tests/test_itur_bt_2020.py index e4e7a56e99..f292e98a5c 100644 --- a/colour/models/rgb/transfer_functions/tests/test_itur_bt_2020.py +++ b/colour/models/rgb/transfer_functions/tests/test_itur_bt_2020.py @@ -1,50 +1,53 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.itur_bt_2020` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.itur_bt_2020` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.models.rgb.transfer_functions import (eotf_inverse_BT2020, - eotf_BT2020) +from colour.models.rgb.transfer_functions import ( + eotf_inverse_BT2020, + eotf_BT2020, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestEotfInverse_BT2020', 'TestEotf_BT2020'] +__all__ = [ + "TestEotfInverse_BT2020", + "TestEotf_BT2020", +] class TestEotfInverse_BT2020(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ eotf_inverse_BT2020` definition unit tests methods. """ def test_eotf_inverse_BT2020(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ eotf_inverse_BT2020` definition. """ self.assertAlmostEqual(eotf_inverse_BT2020(0.0), 0.0, places=7) self.assertAlmostEqual( - eotf_inverse_BT2020(0.18), 0.409007728864150, places=7) + eotf_inverse_BT2020(0.18), 0.409007728864150, places=7 + ) self.assertAlmostEqual(eotf_inverse_BT2020(1.0), 1.0, places=7) def test_n_dimensional_eotf_inverse_BT2020(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ eotf_inverse_BT2020` definition n-dimensional arrays support. """ @@ -65,39 +68,41 @@ def test_n_dimensional_eotf_inverse_BT2020(self): def test_domain_range_scale_eotf_inverse_BT2020(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ eotf_inverse_BT2020` definition domain and range scale support. """ E = 0.18 E_p = eotf_inverse_BT2020(E) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - eotf_inverse_BT2020(E * factor), E_p * factor, decimal=7) + eotf_inverse_BT2020(E * factor), E_p * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_eotf_inverse_BT2020(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ eotf_inverse_BT2020` definition nan support. """ eotf_inverse_BT2020( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestEotf_BT2020(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ eotf_BT2020` definition unit tests methods. """ def test_eotf_BT2020(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ eotf_BT2020` definition. """ @@ -109,7 +114,7 @@ def test_eotf_BT2020(self): def test_n_dimensional_eotf_BT2020(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ eotf_BT2020` definition n-dimensional arrays support. """ @@ -130,28 +135,29 @@ def test_n_dimensional_eotf_BT2020(self): def test_domain_range_scale_eotf_BT2020(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ eotf_BT2020` definition domain and range scale support. """ E_p = 0.409007728864150 E = eotf_BT2020(E_p) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - eotf_BT2020(E_p * factor), E * factor, decimal=7) + eotf_BT2020(E_p * factor), E * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_eotf_BT2020(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2020.\ eotf_BT2020` definition nan support. """ eotf_BT2020(np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_itur_bt_2100.py b/colour/models/rgb/transfer_functions/tests/test_itur_bt_2100.py index b5dec0afa0..ecdf6841e0 100644 --- a/colour/models/rgb/transfer_functions/tests/test_itur_bt_2100.py +++ b/colour/models/rgb/transfer_functions/tests/test_itur_bt_2100.py @@ -1,69 +1,91 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.itur_bt_2100` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.itur_bt_2100` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from colour.models.rgb.transfer_functions import ( - oetf_PQ_BT2100, oetf_inverse_PQ_BT2100, eotf_PQ_BT2100, - eotf_inverse_PQ_BT2100, ootf_PQ_BT2100, ootf_inverse_PQ_BT2100, - oetf_HLG_BT2100, oetf_inverse_HLG_BT2100) + oetf_PQ_BT2100, + oetf_inverse_PQ_BT2100, + eotf_PQ_BT2100, + eotf_inverse_PQ_BT2100, + ootf_PQ_BT2100, + ootf_inverse_PQ_BT2100, + oetf_HLG_BT2100, + oetf_inverse_HLG_BT2100, +) from colour.models.rgb.transfer_functions.itur_bt_2100 import ( - eotf_HLG_BT2100_1, eotf_HLG_BT2100_2, eotf_inverse_HLG_BT2100_1, - eotf_inverse_HLG_BT2100_2, ootf_HLG_BT2100_1, ootf_HLG_BT2100_2, - ootf_inverse_HLG_BT2100_1, ootf_inverse_HLG_BT2100_2) + eotf_HLG_BT2100_1, + eotf_HLG_BT2100_2, + eotf_inverse_HLG_BT2100_1, + eotf_inverse_HLG_BT2100_2, + ootf_HLG_BT2100_1, + ootf_HLG_BT2100_2, + ootf_inverse_HLG_BT2100_1, + ootf_inverse_HLG_BT2100_2, +) from colour.models.rgb.transfer_functions.itur_bt_2100 import ( - gamma_function_HLG_BT2100) + gamma_function_HLG_BT2100, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestOetf_PQ_BT2100', 'TestOetf_inverse_PQ_BT2100', 'TestEotf_PQ_BT2100', - 'TestEotf_inverse_PQ_BT2100', 'TestOotf_PQ_BT2100', - 'TestOotf_inverse_PQ_BT2100', 'TestGamma_function_HLG_BT2100', - 'TestOetf_HLG_BT2100', 'TestOetf_inverse_HLG_BT2100', - 'TestEotf_HLG_BT2100_1', 'TestEotf_HLG_BT2100_2', - 'TestEotf_inverse_HLG_BT2100_1', 'TestEotf_inverse_HLG_BT2100_2', - 'TestOotf_HLG_BT2100_1', 'TestOotf_HLG_BT2100_2', - 'TestOotf_inverse_HLG_BT2100_1', 'TestOotf_inverse_HLG_BT2100_2' + "TestOetf_PQ_BT2100", + "TestOetf_inverse_PQ_BT2100", + "TestEotf_PQ_BT2100", + "TestEotf_inverse_PQ_BT2100", + "TestOotf_PQ_BT2100", + "TestOotf_inverse_PQ_BT2100", + "TestGamma_function_HLG_BT2100", + "TestOetf_HLG_BT2100", + "TestOetf_inverse_HLG_BT2100", + "TestEotf_HLG_BT2100_1", + "TestEotf_HLG_BT2100_2", + "TestEotf_inverse_HLG_BT2100_1", + "TestEotf_inverse_HLG_BT2100_2", + "TestOotf_HLG_BT2100_1", + "TestOotf_HLG_BT2100_2", + "TestOotf_inverse_HLG_BT2100_1", + "TestOotf_inverse_HLG_BT2100_2", ] class TestOetf_PQ_BT2100(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_PQ_BT2100` definition unit tests methods. """ def test_oetf_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_PQ_BT2100` definition. """ self.assertAlmostEqual( - oetf_PQ_BT2100(0.0), 0.000000730955903, places=7) + oetf_PQ_BT2100(0.0), 0.000000730955903, places=7 + ) self.assertAlmostEqual( - oetf_PQ_BT2100(0.1), 0.724769816665726, places=7) + oetf_PQ_BT2100(0.1), 0.724769816665726, places=7 + ) self.assertAlmostEqual( - oetf_PQ_BT2100(1.0), 0.999999934308041, places=7) + oetf_PQ_BT2100(1.0), 0.999999934308041, places=7 + ) def test_n_dimensional_oetf_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_PQ_BT2100` definition n-dimensional arrays support. """ @@ -84,23 +106,24 @@ def test_n_dimensional_oetf_PQ_BT2100(self): def test_domain_range_scale_oetf_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_PQ_BT2100` definition domain and range scale support. """ E = 0.1 E_p = oetf_PQ_BT2100(E) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - oetf_PQ_BT2100(E * factor), E_p * factor, decimal=7) + oetf_PQ_BT2100(E * factor), E_p * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_oetf_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_PQ_BT2100` definition nan support. """ @@ -109,28 +132,31 @@ def test_nan_oetf_PQ_BT2100(self): class TestOetf_inverse_PQ_BT2100(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_inverse_PQ_BT2100` definition unit tests methods. """ def test_oetf_inverse_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_inverse_PQ_BT2100` definition. """ self.assertAlmostEqual( - oetf_inverse_PQ_BT2100(0.000000730955903), 0.0, places=7) + oetf_inverse_PQ_BT2100(0.000000730955903), 0.0, places=7 + ) self.assertAlmostEqual( - oetf_inverse_PQ_BT2100(0.724769816665726), 0.1, places=7) + oetf_inverse_PQ_BT2100(0.724769816665726), 0.1, places=7 + ) self.assertAlmostEqual( - oetf_inverse_PQ_BT2100(0.999999934308041), 1.0, places=7) + oetf_inverse_PQ_BT2100(0.999999934308041), 1.0, places=7 + ) def test_n_dimensional_oetf_inverse_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_inverse_PQ_BT2100` definition n-dimensional arrays support. """ @@ -140,68 +166,72 @@ def test_n_dimensional_oetf_inverse_PQ_BT2100(self): E_p = np.tile(E_p, 6) E = np.tile(E, 6) np.testing.assert_almost_equal( - oetf_inverse_PQ_BT2100(E_p), E, decimal=7) + oetf_inverse_PQ_BT2100(E_p), E, decimal=7 + ) E_p = np.reshape(E_p, (2, 3)) E = np.reshape(E, (2, 3)) np.testing.assert_almost_equal( - oetf_inverse_PQ_BT2100(E_p), E, decimal=7) + oetf_inverse_PQ_BT2100(E_p), E, decimal=7 + ) E_p = np.reshape(E_p, (2, 3, 1)) E = np.reshape(E, (2, 3, 1)) np.testing.assert_almost_equal( - oetf_inverse_PQ_BT2100(E_p), E, decimal=7) + oetf_inverse_PQ_BT2100(E_p), E, decimal=7 + ) def test_domain_range_scale_oetf_inverse_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_inverse_PQ_BT2100` definition domain and range scale support. """ E_p = 0.724769816665726 E = oetf_inverse_PQ_BT2100(E_p) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - oetf_inverse_PQ_BT2100(E_p * factor), - E * factor, - decimal=7) + oetf_inverse_PQ_BT2100(E_p * factor), E * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_oetf_inverse_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_inverse_PQ_BT2100` definition nan support. """ oetf_inverse_PQ_BT2100( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestEotf_PQ_BT2100(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_PQ_BT2100` definition unit tests methods. """ def test_eotf_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_PQ_BT2100` definition. """ self.assertAlmostEqual(eotf_PQ_BT2100(0.0), 0.0, places=7) self.assertAlmostEqual( - eotf_PQ_BT2100(0.724769816665726), 779.98836083408537, places=7) + eotf_PQ_BT2100(0.724769816665726), 779.98836083408537, places=7 + ) self.assertAlmostEqual(eotf_PQ_BT2100(1.0), 10000.0, places=7) def test_n_dimensional_eotf_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_PQ_BT2100` definition n-dimensional arrays support. """ @@ -222,23 +252,24 @@ def test_n_dimensional_eotf_PQ_BT2100(self): def test_domain_range_scale_eotf_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_PQ_BT2100` definition domain and range scale support. """ E_p = 0.724769816665726 F_D = eotf_PQ_BT2100(E_p) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - eotf_PQ_BT2100(E_p * factor), F_D * factor, decimal=7) + eotf_PQ_BT2100(E_p * factor), F_D * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_eotf_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_PQ_BT2100` definition nan support. """ @@ -247,29 +278,31 @@ def test_nan_eotf_PQ_BT2100(self): class TestEotf_inverse_PQ_BT2100(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_inverse_PQ_BT2100` definition unit tests methods. """ def test_eotf_inverse_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_inverse_PQ_BT2100` definition. """ self.assertAlmostEqual( - eotf_inverse_PQ_BT2100(0.0), 0.000000730955903, places=7) + eotf_inverse_PQ_BT2100(0.0), 0.000000730955903, places=7 + ) self.assertAlmostEqual( eotf_inverse_PQ_BT2100(779.98836083408537), 0.724769816665726, - places=7) + places=7, + ) self.assertAlmostEqual(eotf_inverse_PQ_BT2100(10000.0), 1.0, places=7) def test_n_dimensional_eotf_inverse_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_inverse_PQ_BT2100` definition n-dimensional arrays support. """ @@ -279,69 +312,76 @@ def test_n_dimensional_eotf_inverse_PQ_BT2100(self): F_D = np.tile(F_D, 6) E_p = np.tile(E_p, 6) np.testing.assert_almost_equal( - eotf_inverse_PQ_BT2100(F_D), E_p, decimal=7) + eotf_inverse_PQ_BT2100(F_D), E_p, decimal=7 + ) F_D = np.reshape(F_D, (2, 3)) E_p = np.reshape(E_p, (2, 3)) np.testing.assert_almost_equal( - eotf_inverse_PQ_BT2100(F_D), E_p, decimal=7) + eotf_inverse_PQ_BT2100(F_D), E_p, decimal=7 + ) F_D = np.reshape(F_D, (2, 3, 1)) E_p = np.reshape(E_p, (2, 3, 1)) np.testing.assert_almost_equal( - eotf_inverse_PQ_BT2100(F_D), E_p, decimal=7) + eotf_inverse_PQ_BT2100(F_D), E_p, decimal=7 + ) def test_domain_range_scale_eotf_inverse_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_inverse_PQ_BT2100` definition domain and range scale support. """ F_D = 779.98836083408537 E_p = eotf_inverse_PQ_BT2100(F_D) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( eotf_inverse_PQ_BT2100(F_D * factor), E_p * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_eotf_inverse_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_inverse_PQ_BT2100` definition nan support. """ eotf_inverse_PQ_BT2100( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestOotf_PQ_BT2100(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_PQ_BT2100` definition unit tests methods. """ def test_ootf_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_PQ_BT2100` definition. """ self.assertAlmostEqual(ootf_PQ_BT2100(0.0), 0.0, places=7) self.assertAlmostEqual( - ootf_PQ_BT2100(0.1), 779.98836083411584, places=7) + ootf_PQ_BT2100(0.1), 779.98836083411584, places=7 + ) self.assertAlmostEqual( - ootf_PQ_BT2100(1.0), 9999.993723673924300, places=7) + ootf_PQ_BT2100(1.0), 9999.993723673924300, places=7 + ) def test_n_dimensional_ootf_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_PQ_BT2100` definition n-dimensional arrays support. """ @@ -362,23 +402,24 @@ def test_n_dimensional_ootf_PQ_BT2100(self): def test_domain_range_scale_ootf_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_PQ_BT2100` definition domain and range scale support. """ E = 0.1 F_D = ootf_PQ_BT2100(E) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - ootf_PQ_BT2100(E * factor), F_D * factor, decimal=7) + ootf_PQ_BT2100(E * factor), F_D * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_ootf_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_PQ_BT2100` definition nan support. """ @@ -387,27 +428,29 @@ def test_nan_ootf_PQ_BT2100(self): class TestOotf_inverse_PQ_BT2100(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_inverse_PQ_BT2100` definition unit tests methods. """ def test_ootf_inverse_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_inverse_PQ_BT2100` definition. """ self.assertAlmostEqual(ootf_inverse_PQ_BT2100(0.0), 0.0, places=7) self.assertAlmostEqual( - ootf_inverse_PQ_BT2100(779.98836083411584), 0.1, places=7) + ootf_inverse_PQ_BT2100(779.98836083411584), 0.1, places=7 + ) self.assertAlmostEqual( - ootf_inverse_PQ_BT2100(9999.993723673924300), 1.0, places=7) + ootf_inverse_PQ_BT2100(9999.993723673924300), 1.0, places=7 + ) def test_n_dimensional_ootf_inverse_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_inverse_PQ_BT2100` definition n-dimensional arrays support. """ @@ -417,94 +460,103 @@ def test_n_dimensional_ootf_inverse_PQ_BT2100(self): F_D = np.tile(F_D, 6) E = np.tile(E, 6) np.testing.assert_almost_equal( - ootf_inverse_PQ_BT2100(F_D), E, decimal=7) + ootf_inverse_PQ_BT2100(F_D), E, decimal=7 + ) F_D = np.reshape(F_D, (2, 3)) E = np.reshape(E, (2, 3)) np.testing.assert_almost_equal( - ootf_inverse_PQ_BT2100(F_D), E, decimal=7) + ootf_inverse_PQ_BT2100(F_D), E, decimal=7 + ) F_D = np.reshape(F_D, (2, 3, 1)) E = np.reshape(E, (2, 3, 1)) np.testing.assert_almost_equal( - ootf_inverse_PQ_BT2100(F_D), E, decimal=7) + ootf_inverse_PQ_BT2100(F_D), E, decimal=7 + ) def test_domain_range_scale_ootf_inverse_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_inverse_PQ_BT2100` definition domain and range scale support. """ F_D = 779.98836083411584 E = ootf_inverse_PQ_BT2100(F_D) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - ootf_inverse_PQ_BT2100(F_D * factor), - E * factor, - decimal=7) + ootf_inverse_PQ_BT2100(F_D * factor), E * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_ootf_inverse_PQ_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_inverse_PQ_BT2100` definition nan support. """ ootf_inverse_PQ_BT2100( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestGamma_function_HLG_BT2100(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ gamma_function_HLG_BT2100` definition unit tests methods. """ def test_gamma_function_HLG_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ gamma_function_HLG_BT2100` definition. """ self.assertAlmostEqual( - gamma_function_HLG_BT2100(1000.0), 1.2, places=7) + gamma_function_HLG_BT2100(1000.0), 1.2, places=7 + ) self.assertAlmostEqual( - gamma_function_HLG_BT2100(2000.0), 1.326432598178872, places=7) + gamma_function_HLG_BT2100(2000.0), 1.326432598178872, places=7 + ) self.assertAlmostEqual( - gamma_function_HLG_BT2100(4000.0), 1.452865196357744, places=7) + gamma_function_HLG_BT2100(4000.0), 1.452865196357744, places=7 + ) self.assertAlmostEqual( - gamma_function_HLG_BT2100(10000.0), 1.619999999999999, places=7) + gamma_function_HLG_BT2100(10000.0), 1.619999999999999, places=7 + ) class TestOetf_HLG_BT2100(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_HLG_BT2100` definition unit tests methods. """ def test_oetf_HLG_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_HLG_BT2100` definition. """ self.assertAlmostEqual(oetf_HLG_BT2100(0.0), 0.0, places=7) self.assertAlmostEqual( - oetf_HLG_BT2100(0.18 / 12), 0.212132034355964, places=7) + oetf_HLG_BT2100(0.18 / 12), 0.212132034355964, places=7 + ) self.assertAlmostEqual( - oetf_HLG_BT2100(1.0), 0.999999995536569, places=7) + oetf_HLG_BT2100(1.0), 0.999999995536569, places=7 + ) def test_n_dimensional_oetf_HLG_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_HLG_BT2100` definition n-dimensional arrays support. """ @@ -525,23 +577,24 @@ def test_n_dimensional_oetf_HLG_BT2100(self): def test_domain_range_scale_oetf_HLG_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_HLG_BT2100` definition domain and range scale support. """ E = 0.18 / 12 E_p = oetf_HLG_BT2100(E) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - oetf_HLG_BT2100(E * factor), E_p * factor, decimal=7) + oetf_HLG_BT2100(E * factor), E_p * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_oetf_HLG_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_HLG_BT2100` definition nan support. """ @@ -550,27 +603,29 @@ def test_nan_oetf_HLG_BT2100(self): class TestOetf_inverse_HLG_BT2100(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_inverse_HLG_BT2100` definition unit tests methods. """ def test_oetf_inverse_HLG_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_inverse_HLG_BT2100` definition. """ self.assertAlmostEqual(oetf_inverse_HLG_BT2100(0.0), 0.0, places=7) self.assertAlmostEqual( - oetf_inverse_HLG_BT2100(0.212132034355964), 0.18 / 12, places=7) + oetf_inverse_HLG_BT2100(0.212132034355964), 0.18 / 12, places=7 + ) self.assertAlmostEqual( - oetf_inverse_HLG_BT2100(0.999999995536569), 1.0, places=7) + oetf_inverse_HLG_BT2100(0.999999995536569), 1.0, places=7 + ) def test_n_dimensional_oetf_inverse_HLG_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_inverse_HLG_BT2100` definition n-dimensional arrays support. """ @@ -580,74 +635,82 @@ def test_n_dimensional_oetf_inverse_HLG_BT2100(self): E_p = np.tile(E_p, 6) E = np.tile(E, 6) np.testing.assert_almost_equal( - oetf_inverse_HLG_BT2100(E_p), E, decimal=7) + oetf_inverse_HLG_BT2100(E_p), E, decimal=7 + ) E_p = np.reshape(E_p, (2, 3)) E = np.reshape(E, (2, 3)) np.testing.assert_almost_equal( - oetf_inverse_HLG_BT2100(E_p), E, decimal=7) + oetf_inverse_HLG_BT2100(E_p), E, decimal=7 + ) E_p = np.reshape(E_p, (2, 3, 1)) E = np.reshape(E, (2, 3, 1)) np.testing.assert_almost_equal( - oetf_inverse_HLG_BT2100(E_p), E, decimal=7) + oetf_inverse_HLG_BT2100(E_p), E, decimal=7 + ) def test_domain_range_scale_oetf_inverse_HLG_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_inverse_HLG_BT2100` definition domain and range scale support. """ E_p = 0.212132034355964 E = oetf_inverse_HLG_BT2100(E_p) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( oetf_inverse_HLG_BT2100(E_p * factor), E * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_oetf_inverse_HLG_BT2100(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ oetf_inverse_HLG_BT2100` definition nan support. """ oetf_inverse_HLG_BT2100( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestEotf_HLG_BT2100_1(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_HLG_BT2100_1` definition unit tests methods. """ def test_eotf_HLG_BT2100_1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_HLG_BT2100_1` definition. """ self.assertAlmostEqual(eotf_HLG_BT2100_1(0.0), 0.0, places=7) self.assertAlmostEqual( - eotf_HLG_BT2100_1(0.212132034355964), 6.476039825649814, places=7) + eotf_HLG_BT2100_1(0.212132034355964), 6.476039825649814, places=7 + ) self.assertAlmostEqual( - eotf_HLG_BT2100_1(1.0), 1000.000032321769100, places=7) + eotf_HLG_BT2100_1(1.0), 1000.000032321769100, places=7 + ) self.assertAlmostEqual( eotf_HLG_BT2100_1(0.212132034355964, 0.001, 10000, 1.4), 27.96039175299561, - places=7) + places=7, + ) def test_n_dimensional_eotf_HLG_BT2100_1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_HLG_BT2100_1` definition n-dimensional arrays support. """ @@ -684,23 +747,24 @@ def test_n_dimensional_eotf_HLG_BT2100_1(self): def test_domain_range_scale_eotf_HLG_BT2100_1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_HLG_BT2100_1` definition domain and range scale support. """ E_p = 0.212132034355964 F_D = eotf_HLG_BT2100_1(E_p) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - eotf_HLG_BT2100_1(E_p * factor), F_D * factor, decimal=7) + eotf_HLG_BT2100_1(E_p * factor), F_D * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_eotf_HLG_BT2100_1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_HLG_BT2100_1` definition nan support. """ @@ -709,32 +773,35 @@ def test_nan_eotf_HLG_BT2100_1(self): class TestEotf_HLG_BT2100_2(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_HLG_BT2100_2` definition unit tests methods. """ def test_eotf_HLG_BT2100_2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_HLG_BT2100_2` definition. """ self.assertAlmostEqual(eotf_HLG_BT2100_2(0.0), 0.0, places=7) self.assertAlmostEqual( - eotf_HLG_BT2100_2(0.212132034355964), 6.476039825649814, places=7) + eotf_HLG_BT2100_2(0.212132034355964), 6.476039825649814, places=7 + ) self.assertAlmostEqual( - eotf_HLG_BT2100_2(1.0), 1000.000032321769100, places=7) + eotf_HLG_BT2100_2(1.0), 1000.000032321769100, places=7 + ) self.assertAlmostEqual( eotf_HLG_BT2100_2(0.212132034355964, 0.001, 10000, 1.4), 29.581261576946076, - places=7) + places=7, + ) def test_n_dimensional_eotf_HLG_BT2100_2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_HLG_BT2100_2` definition n-dimensional arrays support. """ @@ -771,23 +838,24 @@ def test_n_dimensional_eotf_HLG_BT2100_2(self): def test_domain_range_scale_eotf_HLG_BT2100_2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_HLG_BT2100_2` definition domain and range scale support. """ E_p = 0.212132034355964 F_D = eotf_HLG_BT2100_2(E_p) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - eotf_HLG_BT2100_2(E_p * factor), F_D * factor, decimal=7) + eotf_HLG_BT2100_2(E_p * factor), F_D * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_eotf_HLG_BT2100_2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_HLG_BT2100_2` definition nan support. """ @@ -796,13 +864,13 @@ def test_nan_eotf_HLG_BT2100_2(self): class TestEotf_inverse_HLG_BT2100_1(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_inverse_HLG_BT2100_1` definition unit tests methods. """ def test_eotf_inverse_HLG_BT2100_1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_inverse_HLG_BT2100_1` definition. """ @@ -811,19 +879,22 @@ def test_eotf_inverse_HLG_BT2100_1(self): self.assertAlmostEqual( eotf_inverse_HLG_BT2100_1(6.476039825649814), 0.212132034355964, - places=7) + places=7, + ) self.assertAlmostEqual( - eotf_inverse_HLG_BT2100_1(1000.000032321769100), 1.0, places=7) + eotf_inverse_HLG_BT2100_1(1000.000032321769100), 1.0, places=7 + ) self.assertAlmostEqual( eotf_inverse_HLG_BT2100_1(27.96039175299561, 0.001, 10000, 1.4), 0.212132034355964, - places=7) + places=7, + ) def test_n_dimensional_eotf_inverse_HLG_BT2100_1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_inverse_HLG_BT2100_1` definition n-dimensional arrays support. """ @@ -833,75 +904,84 @@ def test_n_dimensional_eotf_inverse_HLG_BT2100_1(self): F_D = np.tile(F_D, 6) E_p = np.tile(E_p, 6) np.testing.assert_almost_equal( - eotf_inverse_HLG_BT2100_1(F_D), E_p, decimal=7) + eotf_inverse_HLG_BT2100_1(F_D), E_p, decimal=7 + ) F_D = np.reshape(F_D, (2, 3)) E_p = np.reshape(E_p, (2, 3)) np.testing.assert_almost_equal( - eotf_inverse_HLG_BT2100_1(F_D), E_p, decimal=7) + eotf_inverse_HLG_BT2100_1(F_D), E_p, decimal=7 + ) F_D = np.reshape(F_D, (2, 3, 1)) E_p = np.reshape(E_p, (2, 3, 1)) np.testing.assert_almost_equal( - eotf_inverse_HLG_BT2100_1(F_D), E_p, decimal=7) + eotf_inverse_HLG_BT2100_1(F_D), E_p, decimal=7 + ) F_D = np.reshape(F_D, (6, 1)) E_p = np.reshape(E_p, (6, 1)) np.testing.assert_almost_equal( - eotf_inverse_HLG_BT2100_1(F_D), E_p, decimal=7) + eotf_inverse_HLG_BT2100_1(F_D), E_p, decimal=7 + ) F_D = np.array([12.49759413, 49.99037650, 158.94693786]) E_p = np.array([0.25, 0.50, 0.75]) np.testing.assert_almost_equal( - eotf_inverse_HLG_BT2100_1(F_D), E_p, decimal=7) + eotf_inverse_HLG_BT2100_1(F_D), E_p, decimal=7 + ) F_D = np.tile(F_D, (6, 1)) E_p = np.tile(E_p, (6, 1)) np.testing.assert_almost_equal( - eotf_inverse_HLG_BT2100_1(F_D), E_p, decimal=7) + eotf_inverse_HLG_BT2100_1(F_D), E_p, decimal=7 + ) F_D = np.reshape(F_D, (2, 3, 3)) E_p = np.reshape(E_p, (2, 3, 3)) np.testing.assert_almost_equal( - eotf_inverse_HLG_BT2100_1(F_D), E_p, decimal=7) + eotf_inverse_HLG_BT2100_1(F_D), E_p, decimal=7 + ) def test_domain_range_scale_eotf_inverse_HLG_BT2100_1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_inverse_HLG_BT2100_1` definition domain and range scale support. """ F_D = 6.476039825649814 E_p = eotf_inverse_HLG_BT2100_1(F_D) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( eotf_inverse_HLG_BT2100_1(F_D * factor), E_p * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_eotf_inverse_HLG_BT2100_1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_inverse_HLG_BT2100_1` definition nan support. """ eotf_inverse_HLG_BT2100_1( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestEotf_inverse_HLG_BT2100_2(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_inverse_HLG_BT2100_2` definition unit tests methods. """ def test_eotf_inverse_HLG_BT2100_2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_inverse_HLG_BT2100_2` definition. """ @@ -910,19 +990,22 @@ def test_eotf_inverse_HLG_BT2100_2(self): self.assertAlmostEqual( eotf_inverse_HLG_BT2100_2(6.476039825649814), 0.212132034355964, - places=7) + places=7, + ) self.assertAlmostEqual( - eotf_inverse_HLG_BT2100_2(1000.000032321769100), 1.0, places=7) + eotf_inverse_HLG_BT2100_2(1000.000032321769100), 1.0, places=7 + ) self.assertAlmostEqual( eotf_inverse_HLG_BT2100_2(29.581261576946076, 0.001, 10000, 1.4), 0.212132034355964, - places=7) + places=7, + ) def test_n_dimensional_eotf_inverse_HLG_BT2100_2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_inverse_HLG_BT2100_2` definition n-dimensional arrays support. """ @@ -932,105 +1015,131 @@ def test_n_dimensional_eotf_inverse_HLG_BT2100_2(self): F_D = np.tile(F_D, 6) E_p = np.tile(E_p, 6) np.testing.assert_almost_equal( - eotf_inverse_HLG_BT2100_2(F_D), E_p, decimal=7) + eotf_inverse_HLG_BT2100_2(F_D), E_p, decimal=7 + ) F_D = np.reshape(F_D, (2, 3)) E_p = np.reshape(E_p, (2, 3)) np.testing.assert_almost_equal( - eotf_inverse_HLG_BT2100_2(F_D), E_p, decimal=7) + eotf_inverse_HLG_BT2100_2(F_D), E_p, decimal=7 + ) F_D = np.reshape(F_D, (2, 3, 1)) E_p = np.reshape(E_p, (2, 3, 1)) np.testing.assert_almost_equal( - eotf_inverse_HLG_BT2100_2(F_D), E_p, decimal=7) + eotf_inverse_HLG_BT2100_2(F_D), E_p, decimal=7 + ) F_D = np.reshape(F_D, (6, 1)) E_p = np.reshape(E_p, (6, 1)) np.testing.assert_almost_equal( - eotf_inverse_HLG_BT2100_2(F_D), E_p, decimal=7) + eotf_inverse_HLG_BT2100_2(F_D), E_p, decimal=7 + ) F_D = np.array([12.49759413, 49.99037650, 158.94693786]) E_p = np.array([0.25, 0.50, 0.75]) np.testing.assert_almost_equal( - eotf_inverse_HLG_BT2100_2(F_D), E_p, decimal=7) + eotf_inverse_HLG_BT2100_2(F_D), E_p, decimal=7 + ) F_D = np.tile(F_D, (6, 1)) E_p = np.tile(E_p, (6, 1)) np.testing.assert_almost_equal( - eotf_inverse_HLG_BT2100_2(F_D), E_p, decimal=7) + eotf_inverse_HLG_BT2100_2(F_D), E_p, decimal=7 + ) F_D = np.reshape(F_D, (2, 3, 3)) E_p = np.reshape(E_p, (2, 3, 3)) np.testing.assert_almost_equal( - eotf_inverse_HLG_BT2100_2(F_D), E_p, decimal=7) + eotf_inverse_HLG_BT2100_2(F_D), E_p, decimal=7 + ) def test_domain_range_scale_eotf_inverse_HLG_BT2100_2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_inverse_HLG_BT2100_2` definition domain and range scale support. """ F_D = 6.476039825649814 E_p = eotf_inverse_HLG_BT2100_2(F_D) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( eotf_inverse_HLG_BT2100_2(F_D * factor), E_p * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_eotf_inverse_HLG_BT2100_2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ eotf_inverse_HLG_BT2100_2` definition nan support. """ eotf_inverse_HLG_BT2100_2( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestOotf_HLG_BT2100_1(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_HLG_BT2100_1` definition unit tests methods. """ def test_ootf_HLG_BT2100_1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_HLG_BT2100_1` definition. """ self.assertAlmostEqual(ootf_HLG_BT2100_1(0.0), 0.0, places=7) self.assertAlmostEqual( - ootf_HLG_BT2100_1(0.1), 63.095734448019336, places=7) + ootf_HLG_BT2100_1(0.1), 63.095734448019336, places=7 + ) self.assertAlmostEqual(ootf_HLG_BT2100_1(1.0), 1000.0, places=7) self.assertAlmostEqual( ootf_HLG_BT2100_1(0.1, 0.001, 10000, 1.4), 398.108130742780300, - places=7) + places=7, + ) a = np.array( - [[45.884942278760597, 0.000000000000000, -45.884942278760597], [ - -63.095734448019336, -63.095734448019336, -63.095734448019336 - ], [63.095734448019336, 63.095734448019336, 63.095734448019336], - [51.320396090100672, -51.320396090100672, 51.320396090100672]], ) + [ + [45.884942278760597, 0.000000000000000, -45.884942278760597], + [ + -63.095734448019336, + -63.095734448019336, + -63.095734448019336, + ], + [63.095734448019336, 63.095734448019336, 63.095734448019336], + [51.320396090100672, -51.320396090100672, 51.320396090100672], + ], + ) np.testing.assert_almost_equal( ootf_HLG_BT2100_1( - np.array([[0.1, 0.0, -0.1], [-0.1, -0.1, -0.1], - [0.1, 0.1, 0.1], [0.1, -0.1, 0.1]])), + np.array( + [ + [0.1, 0.0, -0.1], + [-0.1, -0.1, -0.1], + [0.1, 0.1, 0.1], + [0.1, -0.1, 0.1], + ] + ) + ), a, - decimal=7) + decimal=7, + ) def test_n_dimensional_ootf_HLG_BT2100_1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_HLG_BT2100_1` definition n-dimensional arrays support. """ @@ -1067,23 +1176,24 @@ def test_n_dimensional_ootf_HLG_BT2100_1(self): def test_domain_range_scale_ootf_HLG_BT2100_1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_HLG_BT2100_1` definition domain and range scale support. """ E = 0.1 F_D = ootf_HLG_BT2100_1(E) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - ootf_HLG_BT2100_1(E * factor), F_D * factor, decimal=7) + ootf_HLG_BT2100_1(E * factor), F_D * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_ootf_HLG_BT2100_1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_HLG_BT2100_1` definition nan support. """ @@ -1092,42 +1202,58 @@ def test_nan_ootf_HLG_BT2100_1(self): class TestOotf_HLG_BT2100_2(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_HLG_BT2100_2` definition unit tests methods. """ def test_ootf_HLG_BT2100_2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_HLG_BT2100_2` definition. """ self.assertAlmostEqual(ootf_HLG_BT2100_2(0.0), 0.0, places=7) self.assertAlmostEqual( - ootf_HLG_BT2100_2(0.1), 63.095734448019336, places=7) + ootf_HLG_BT2100_2(0.1), 63.095734448019336, places=7 + ) self.assertAlmostEqual(ootf_HLG_BT2100_2(1.0), 1000.0, places=7) self.assertAlmostEqual( - ootf_HLG_BT2100_2(0.1, 10000, 1.4), 398.107170553497380, places=7) - - a = np.array([ - [45.884942278760597, 0.000000000000000, -45.884942278760597], - [-63.095734448019336, -63.095734448019336, -63.095734448019336], - [63.095734448019336, 63.095734448019336, 63.095734448019336], - [51.320396090100672, -51.320396090100672, 51.320396090100672], - ], ) + ootf_HLG_BT2100_2(0.1, 10000, 1.4), 398.107170553497380, places=7 + ) + + a = np.array( + [ + [45.884942278760597, 0.000000000000000, -45.884942278760597], + [ + -63.095734448019336, + -63.095734448019336, + -63.095734448019336, + ], + [63.095734448019336, 63.095734448019336, 63.095734448019336], + [51.320396090100672, -51.320396090100672, 51.320396090100672], + ], + ) np.testing.assert_almost_equal( ootf_HLG_BT2100_2( - np.array([[0.1, 0.0, -0.1], [-0.1, -0.1, -0.1], - [0.1, 0.1, 0.1], [0.1, -0.1, 0.1]])), + np.array( + [ + [0.1, 0.0, -0.1], + [-0.1, -0.1, -0.1], + [0.1, 0.1, 0.1], + [0.1, -0.1, 0.1], + ] + ) + ), a, - decimal=7) + decimal=7, + ) def test_n_dimensional_ootf_HLG_BT2100_2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_HLG_BT2100_2` definition n-dimensional arrays support. """ @@ -1164,23 +1290,24 @@ def test_n_dimensional_ootf_HLG_BT2100_2(self): def test_domain_range_scale_ootf_HLG_BT2100_2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_HLG_BT2100_2` definition domain and range scale support. """ E = 0.1 F_D = ootf_HLG_BT2100_1(E) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - ootf_HLG_BT2100_1(E * factor), F_D * factor, decimal=7) + ootf_HLG_BT2100_1(E * factor), F_D * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_ootf_HLG_BT2100_1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_HLG_BT2100_1` definition nan support. """ @@ -1189,43 +1316,60 @@ def test_nan_ootf_HLG_BT2100_1(self): class TestOotf_inverse_HLG_BT2100_1(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_inverse_HLG_BT2100_1` definition unit tests methods. """ def test_ootf_inverse_HLG_BT2100_1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_inverse_HLG_BT2100_1` definition. """ self.assertAlmostEqual(ootf_inverse_HLG_BT2100_1(0.0), 0.0, places=7) self.assertAlmostEqual( - ootf_inverse_HLG_BT2100_1(63.095734448019336), 0.1, places=7) + ootf_inverse_HLG_BT2100_1(63.095734448019336), 0.1, places=7 + ) self.assertAlmostEqual( - ootf_inverse_HLG_BT2100_1(1000.0), 1.0, places=7) + ootf_inverse_HLG_BT2100_1(1000.0), 1.0, places=7 + ) self.assertAlmostEqual( ootf_inverse_HLG_BT2100_1(398.108130742780300, 0.001, 10000, 1.4), 0.1, - places=7) + places=7, + ) a = np.array( - [[45.884942278760597, 0.000000000000000, -45.884942278760597], [ - -63.095734448019336, -63.095734448019336, -63.095734448019336 - ], [63.095734448019336, 63.095734448019336, 63.095734448019336], - [51.320396090100672, -51.320396090100672, 51.320396090100672]]) + [ + [45.884942278760597, 0.000000000000000, -45.884942278760597], + [ + -63.095734448019336, + -63.095734448019336, + -63.095734448019336, + ], + [63.095734448019336, 63.095734448019336, 63.095734448019336], + [51.320396090100672, -51.320396090100672, 51.320396090100672], + ] + ) np.testing.assert_almost_equal( ootf_inverse_HLG_BT2100_1(a), - np.array([[0.1, 0.0, -0.1], [-0.1, -0.1, -0.1], [0.1, 0.1, 0.1], - [0.1, -0.1, 0.1]]), - decimal=7) + np.array( + [ + [0.1, 0.0, -0.1], + [-0.1, -0.1, -0.1], + [0.1, 0.1, 0.1], + [0.1, -0.1, 0.1], + ] + ), + decimal=7, + ) def test_n_dimensional_ootf_inverse_HLG_BT2100_1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_inverse_HLG_BT2100_1` definition n-dimensional arrays support. """ @@ -1235,105 +1379,131 @@ def test_n_dimensional_ootf_inverse_HLG_BT2100_1(self): F_D = np.tile(F_D, 6) E = np.tile(E, 6) np.testing.assert_almost_equal( - ootf_inverse_HLG_BT2100_1(F_D), E, decimal=7) + ootf_inverse_HLG_BT2100_1(F_D), E, decimal=7 + ) F_D = np.reshape(F_D, (2, 3)) E = np.reshape(E, (2, 3)) np.testing.assert_almost_equal( - ootf_inverse_HLG_BT2100_1(F_D), E, decimal=7) + ootf_inverse_HLG_BT2100_1(F_D), E, decimal=7 + ) F_D = np.reshape(F_D, (2, 3, 1)) E = np.reshape(E, (2, 3, 1)) np.testing.assert_almost_equal( - ootf_inverse_HLG_BT2100_1(F_D), E, decimal=7) + ootf_inverse_HLG_BT2100_1(F_D), E, decimal=7 + ) F_D = np.reshape(F_D, (6, 1)) E = np.reshape(E, (6, 1)) np.testing.assert_almost_equal( - ootf_inverse_HLG_BT2100_1(F_D), E, decimal=7) + ootf_inverse_HLG_BT2100_1(F_D), E, decimal=7 + ) F_D = np.array([213.01897444, 426.03794887, 639.05692331]) E = np.array([0.25, 0.50, 0.75]) np.testing.assert_almost_equal( - ootf_inverse_HLG_BT2100_1(F_D), E, decimal=7) + ootf_inverse_HLG_BT2100_1(F_D), E, decimal=7 + ) F_D = np.tile(F_D, (6, 1)) E = np.tile(E, (6, 1)) np.testing.assert_almost_equal( - ootf_inverse_HLG_BT2100_1(F_D), E, decimal=7) + ootf_inverse_HLG_BT2100_1(F_D), E, decimal=7 + ) F_D = np.reshape(F_D, (2, 3, 3)) E = np.reshape(E, (2, 3, 3)) np.testing.assert_almost_equal( - ootf_inverse_HLG_BT2100_1(F_D), E, decimal=7) + ootf_inverse_HLG_BT2100_1(F_D), E, decimal=7 + ) def test_domain_range_scale_ootf_inverse_HLG_BT2100_1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_inverse_HLG_BT2100_1` definition domain and range scale support. """ F_D = 63.095734448019336 E = ootf_inverse_HLG_BT2100_1(F_D) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( ootf_inverse_HLG_BT2100_1(F_D * factor), E * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_ootf_inverse_HLG_BT2100_1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_inverse_HLG_BT2100_1` definition nan support. """ ootf_inverse_HLG_BT2100_1( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestOotf_inverse_HLG_BT2100_2(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_inverse_HLG_BT2100_2` definition unit tests methods. """ def test_ootf_inverse_HLG_BT2100_2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_inverse_HLG_BT2100_2` definition. """ self.assertAlmostEqual(ootf_inverse_HLG_BT2100_2(0.0), 0.0, places=7) self.assertAlmostEqual( - ootf_inverse_HLG_BT2100_2(63.095734448019336), 0.1, places=7) + ootf_inverse_HLG_BT2100_2(63.095734448019336), 0.1, places=7 + ) self.assertAlmostEqual( - ootf_inverse_HLG_BT2100_2(1000.0), 1.0, places=7) + ootf_inverse_HLG_BT2100_2(1000.0), 1.0, places=7 + ) self.assertAlmostEqual( ootf_inverse_HLG_BT2100_2(398.107170553497380, 10000, 1.4), 0.1, - places=7) + places=7, + ) a = np.array( - [[45.884942278760597, 0.000000000000000, -45.884942278760597], [ - -63.095734448019336, -63.095734448019336, -63.095734448019336 - ], [63.095734448019336, 63.095734448019336, 63.095734448019336], - [51.320396090100672, -51.320396090100672, 51.320396090100672]]) + [ + [45.884942278760597, 0.000000000000000, -45.884942278760597], + [ + -63.095734448019336, + -63.095734448019336, + -63.095734448019336, + ], + [63.095734448019336, 63.095734448019336, 63.095734448019336], + [51.320396090100672, -51.320396090100672, 51.320396090100672], + ] + ) np.testing.assert_almost_equal( ootf_inverse_HLG_BT2100_2(a), - np.array([[0.1, 0.0, -0.1], [-0.1, -0.1, -0.1], [0.1, 0.1, 0.1], - [0.1, -0.1, 0.1]]), - decimal=7) + np.array( + [ + [0.1, 0.0, -0.1], + [-0.1, -0.1, -0.1], + [0.1, 0.1, 0.1], + [0.1, -0.1, 0.1], + ] + ), + decimal=7, + ) def test_n_dimensional_ootf_inverse_HLG_BT2100_2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_inverse_HLG_BT2100_2` definition n-dimensional arrays support. """ @@ -1343,65 +1513,74 @@ def test_n_dimensional_ootf_inverse_HLG_BT2100_2(self): F_D = np.tile(F_D, 6) E = np.tile(E, 6) np.testing.assert_almost_equal( - ootf_inverse_HLG_BT2100_2(F_D), E, decimal=7) + ootf_inverse_HLG_BT2100_2(F_D), E, decimal=7 + ) F_D = np.reshape(F_D, (2, 3)) E = np.reshape(E, (2, 3)) np.testing.assert_almost_equal( - ootf_inverse_HLG_BT2100_2(F_D), E, decimal=7) + ootf_inverse_HLG_BT2100_2(F_D), E, decimal=7 + ) F_D = np.reshape(F_D, (2, 3, 1)) E = np.reshape(E, (2, 3, 1)) np.testing.assert_almost_equal( - ootf_inverse_HLG_BT2100_2(F_D), E, decimal=7) + ootf_inverse_HLG_BT2100_2(F_D), E, decimal=7 + ) F_D = np.reshape(F_D, (6, 1)) E = np.reshape(E, (6, 1)) np.testing.assert_almost_equal( - ootf_inverse_HLG_BT2100_2(F_D), E, decimal=7) + ootf_inverse_HLG_BT2100_2(F_D), E, decimal=7 + ) F_D = np.array([213.01897444, 426.03794887, 639.05692331]) E = np.array([0.25, 0.50, 0.75]) np.testing.assert_almost_equal( - ootf_inverse_HLG_BT2100_2(F_D), E, decimal=7) + ootf_inverse_HLG_BT2100_2(F_D), E, decimal=7 + ) F_D = np.tile(F_D, (6, 1)) E = np.tile(E, (6, 1)) np.testing.assert_almost_equal( - ootf_inverse_HLG_BT2100_2(F_D), E, decimal=7) + ootf_inverse_HLG_BT2100_2(F_D), E, decimal=7 + ) F_D = np.reshape(F_D, (2, 3, 3)) E = np.reshape(E, (2, 3, 3)) np.testing.assert_almost_equal( - ootf_inverse_HLG_BT2100_2(F_D), E, decimal=7) + ootf_inverse_HLG_BT2100_2(F_D), E, decimal=7 + ) def test_domain_range_scale_ootf_inverse_HLG_BT2100_2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_inverse_HLG_BT2100_2` definition domain and range scale support. """ F_D = 63.095734448019336 E = ootf_inverse_HLG_BT2100_2(F_D) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( ootf_inverse_HLG_BT2100_2(F_D * factor), E * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_ootf_inverse_HLG_BT2100_2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_2100.\ ootf_inverse_HLG_BT2100_2` definition nan support. """ ootf_inverse_HLG_BT2100_2( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_itur_bt_601.py b/colour/models/rgb/transfer_functions/tests/test_itur_bt_601.py index fdd431616d..d9a071a22a 100644 --- a/colour/models/rgb/transfer_functions/tests/test_itur_bt_601.py +++ b/colour/models/rgb/transfer_functions/tests/test_itur_bt_601.py @@ -1,36 +1,36 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.itur_bt_601` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.itur_bt_601` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from colour.models.rgb.transfer_functions import oetf_BT601, oetf_inverse_BT601 from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestOetf_BT601', 'TestOetf_inverse_BT601'] +__all__ = [ + "TestOetf_BT601", + "TestOetf_inverse_BT601", +] class TestOetf_BT601(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_601.oetf_BT601` + Define :func:`colour.models.rgb.transfer_functions.itur_bt_601.oetf_BT601` definition unit tests methods. """ def test_oetf_BT601(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_601.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_601.\ oetf_BT601` definition. """ @@ -44,7 +44,7 @@ def test_oetf_BT601(self): def test_n_dimensional_oetf_BT601(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_601.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_601.\ oetf_BT601` definition n-dimensional arrays support. """ @@ -65,23 +65,24 @@ def test_n_dimensional_oetf_BT601(self): def test_domain_range_scale_oetf_BT601(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_601.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_601.\ oetf_BT601` definition domain and range scale support. """ L = 0.18 E = oetf_BT601(L) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - oetf_BT601(L * factor), E * factor, decimal=7) + oetf_BT601(L * factor), E * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_oetf_BT601(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_601.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_601.\ oetf_BT601` definition nan support. """ @@ -90,29 +91,31 @@ def test_nan_oetf_BT601(self): class TestOetf_inverse_BT601(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_601.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_601.\ oetf_inverse_BT601` definition unit tests methods. """ def test_oetf_inverse_BT601(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_601.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_601.\ oetf_inverse_BT601` definition. """ self.assertAlmostEqual(oetf_inverse_BT601(0.0), 0.0, places=7) self.assertAlmostEqual( - oetf_inverse_BT601(0.067500000000000), 0.015, places=7) + oetf_inverse_BT601(0.067500000000000), 0.015, places=7 + ) self.assertAlmostEqual( - oetf_inverse_BT601(0.409007728864150), 0.18, places=7) + oetf_inverse_BT601(0.409007728864150), 0.18, places=7 + ) self.assertAlmostEqual(oetf_inverse_BT601(1.0), 1.0, places=7) def test_n_dimensional_oetf_inverse_BT601(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_601.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_601.\ oetf_inverse_BT601` definition n-dimensional arrays support. """ @@ -133,28 +136,29 @@ def test_n_dimensional_oetf_inverse_BT601(self): def test_domain_range_scale_oetf_inverse_BT601(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_601.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_601.\ oetf_inverse_BT601` definition domain and range scale support. """ E = 0.409007728864150 L = oetf_inverse_BT601(E) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - oetf_inverse_BT601(E * factor), L * factor, decimal=7) + oetf_inverse_BT601(E * factor), L * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_oetf_inverse_BT601(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_601.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_601.\ oetf_inverse_BT601` definition nan support. """ oetf_inverse_BT601(np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_itur_bt_709.py b/colour/models/rgb/transfer_functions/tests/test_itur_bt_709.py index e424e7a886..c34d4ea0f1 100644 --- a/colour/models/rgb/transfer_functions/tests/test_itur_bt_709.py +++ b/colour/models/rgb/transfer_functions/tests/test_itur_bt_709.py @@ -1,36 +1,36 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.itur_bt_709` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.itur_bt_709` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from colour.models.rgb.transfer_functions import oetf_BT709, oetf_inverse_BT709 from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestOetf_BT709', 'TestOetf_inverse_BT709'] +__all__ = [ + "TestOetf_BT709", + "TestOetf_inverse_BT709", +] class TestOetf_BT709(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_709.oetf_BT709` + Define :func:`colour.models.rgb.transfer_functions.itur_bt_709.oetf_BT709` definition unit tests methods. """ def test_oetf_BT709(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_709.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_709.\ oetf_BT709` definition. """ @@ -44,7 +44,7 @@ def test_oetf_BT709(self): def test_n_dimensional_oetf_BT709(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_709.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_709.\ oetf_BT709` definition n-dimensional arrays support. """ @@ -65,23 +65,24 @@ def test_n_dimensional_oetf_BT709(self): def test_domain_range_scale_oetf_BT709(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_709.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_709.\ oetf_BT709` definition domain and range scale support. """ L = 0.18 V = oetf_BT709(L) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - oetf_BT709(L * factor), V * factor, decimal=7) + oetf_BT709(L * factor), V * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_oetf_BT709(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_709.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_709.\ oetf_BT709` definition nan support. """ @@ -90,29 +91,31 @@ def test_nan_oetf_BT709(self): class TestOetf_inverse_BT709(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.itur_bt_709.\ + Define :func:`colour.models.rgb.transfer_functions.itur_bt_709.\ oetf_inverse_BT709` definition unit tests methods. """ def test_oetf_inverse_BT709(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_709.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_709.\ oetf_inverse_BT709` definition. """ self.assertAlmostEqual(oetf_inverse_BT709(0.0), 0.0, places=7) self.assertAlmostEqual( - oetf_inverse_BT709(0.067500000000000), 0.015, places=7) + oetf_inverse_BT709(0.067500000000000), 0.015, places=7 + ) self.assertAlmostEqual( - oetf_inverse_BT709(0.409007728864150), 0.18, places=7) + oetf_inverse_BT709(0.409007728864150), 0.18, places=7 + ) self.assertAlmostEqual(oetf_inverse_BT709(1.0), 1.0, places=7) def test_n_dimensional_oetf_inverse_BT709(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_709.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_709.\ oetf_inverse_BT709` definition n-dimensional arrays support. """ @@ -133,28 +136,29 @@ def test_n_dimensional_oetf_inverse_BT709(self): def test_domain_range_scale_oetf_inverse_BT709(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_709.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_709.\ oetf_inverse_BT709` definition domain and range scale support. """ V = 0.409007728864150 L = oetf_inverse_BT709(V) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - oetf_inverse_BT709(V * factor), L * factor, decimal=7) + oetf_inverse_BT709(V * factor), L * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_oetf_inverse_BT709(self): """ - Tests :func:`colour.models.rgb.transfer_functions.itur_bt_709.\ + Test :func:`colour.models.rgb.transfer_functions.itur_bt_709.\ oetf_inverse_BT709` definition nan support. """ oetf_inverse_BT709(np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_linear.py b/colour/models/rgb/transfer_functions/tests/test_linear.py index 35e1383d03..7cb839997f 100644 --- a/colour/models/rgb/transfer_functions/tests/test_linear.py +++ b/colour/models/rgb/transfer_functions/tests/test_linear.py @@ -1,36 +1,35 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.linear` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.linear` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from colour.models.rgb.transfer_functions import linear_function from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestLinearFunction'] +__all__ = [ + "TestLinearFunction", +] class TestLinearFunction(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.linear.\ + Define :func:`colour.models.rgb.transfer_functions.linear.\ linear_function` definition unit tests methods. """ def test_linear_function(self): """ - Tests :func:`colour.models.rgb.transfer_functions.linear.\ + Test :func:`colour.models.rgb.transfer_functions.linear.\ linear_function` definition. """ @@ -42,7 +41,7 @@ def test_linear_function(self): def test_n_dimensional_linear_function(self): """ - Tests :func:`colour.models.rgb.transfer_functions.linear.\ + Test :func:`colour.models.rgb.transfer_functions.linear.\ linear_function` definition n-dimensional arrays support. """ @@ -64,7 +63,7 @@ def test_n_dimensional_linear_function(self): @ignore_numpy_errors def test_nan_linear_function(self): """ - Tests :func:`colour.models.rgb.transfer_functions.linear.\ + Test :func:`colour.models.rgb.transfer_functions.linear.\ linear_function` definition nan support. """ @@ -74,5 +73,5 @@ def test_nan_linear_function(self): linear_function(case) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_log.py b/colour/models/rgb/transfer_functions/tests/test_log.py index aaeb89d47e..6e1b1e7e3a 100644 --- a/colour/models/rgb/transfer_functions/tests/test_log.py +++ b/colour/models/rgb/transfer_functions/tests/test_log.py @@ -1,79 +1,89 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.log` module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.log` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from colour.models.rgb.transfer_functions import ( - logarithmic_function_basic, logarithmic_function_quasilog, - logarithmic_function_camera, log_encoding_Log2, log_decoding_Log2) + logarithmic_function_basic, + logarithmic_function_quasilog, + logarithmic_function_camera, + log_encoding_Log2, + log_decoding_Log2, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestLogarithmFunction_Basic', 'TestLogarithmFunction_Quasilog', - 'TestLogarithmFunction_Camera', 'TestLogEncoding_Log2', - 'TestLogDecoding_Log2' + "TestLogarithmFunction_Basic", + "TestLogarithmFunction_Quasilog", + "TestLogarithmFunction_Camera", + "TestLogEncoding_Log2", + "TestLogDecoding_Log2", ] class TestLogarithmFunction_Basic(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.log.\ + Define :func:`colour.models.rgb.transfer_functions.log.\ logarithmic_function_basic` definition unit tests methods. """ def test_logarithmic_function_basic(self): """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ + Test :func:`colour.models.rgb.transfer_functions.log.\ logarithmic_function_basic` definition. """ self.assertAlmostEqual( - logarithmic_function_basic(0.18), -2.473931188332412, places=7) + logarithmic_function_basic(0.18), -2.473931188332412, places=7 + ) self.assertAlmostEqual( - logarithmic_function_basic(-2.473931188332412, 'antiLog2'), + logarithmic_function_basic(-2.473931188332412, "antiLog2"), 0.180000000000000, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_basic(0.18, 'log10'), + logarithmic_function_basic(0.18, "log10"), -0.744727494896694, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_basic(-0.744727494896694, 'antiLog10'), + logarithmic_function_basic(-0.744727494896694, "antiLog10"), 0.179999999999999, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_basic(0.18, 'logB', 3), + logarithmic_function_basic(0.18, "logB", 3), -1.560876795007312, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_basic(-1.560876795007312, 'antiLogB', 3), + logarithmic_function_basic(-1.560876795007312, "antiLogB", 3), 0.180000000000000, - places=7) + places=7, + ) def test_n_dimensional_logarithmic_function_basic(self): """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ + Test :func:`colour.models.rgb.transfer_functions.log.\ logarithmic_function_basic` definition n-dimensional arrays support. """ - styles = ['log10', 'antiLog10', 'log2', 'antiLog2', 'logB', 'antiLogB'] + styles = ["log10", "antiLog10", "log2", "antiLog2", "logB", "antiLogB"] for style in styles: a = 0.18 @@ -82,36 +92,30 @@ def test_n_dimensional_logarithmic_function_basic(self): a = np.tile(a, 6) a_p = np.tile(a_p, 6) np.testing.assert_almost_equal( - logarithmic_function_basic(a, style), a_p, decimal=7) + logarithmic_function_basic(a, style), a_p, decimal=7 + ) a = np.reshape(a, (2, 3)) a_p = np.reshape(a_p, (2, 3)) np.testing.assert_almost_equal( - logarithmic_function_basic(a, style), a_p, decimal=7) + logarithmic_function_basic(a, style), a_p, decimal=7 + ) a = np.reshape(a, (2, 3, 1)) a_p = np.reshape(a_p, (2, 3, 1)) np.testing.assert_almost_equal( - logarithmic_function_basic(a, style), a_p, decimal=7) - - def test_raise_exception_logarithmic_function_basic(self): - """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ -logarithmic_function_basic` definition raised exception. - """ - - self.assertRaises(ValueError, logarithmic_function_basic, 0.18, - 'Undefined') + logarithmic_function_basic(a, style), a_p, decimal=7 + ) @ignore_numpy_errors def test_nan_logarithmic_function_basic(self): """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ + Test :func:`colour.models.rgb.transfer_functions.log.\ logarithmic_function_basic` definition nan support. """ cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] - styles = ['log10', 'antiLog10', 'log2', 'antiLog2', 'logB', 'antiLogB'] + styles = ["log10", "antiLog10", "log2", "antiLog2", "logB", "antiLogB"] for case in cases: for style in styles: @@ -120,87 +124,105 @@ def test_nan_logarithmic_function_basic(self): class TestLogarithmFunction_Quasilog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.log.\ + Define :func:`colour.models.rgb.transfer_functions.log.\ logarithmic_function_quasilog` definition unit tests methods. """ def test_logarithmic_function_quasilog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ + Test :func:`colour.models.rgb.transfer_functions.log.\ logarithmic_function_quasilog` definition. """ self.assertAlmostEqual( - logarithmic_function_quasilog(0.18), -2.473931188332412, places=7) + logarithmic_function_quasilog(0.18), -2.473931188332412, places=7 + ) self.assertAlmostEqual( - logarithmic_function_quasilog(-2.473931188332412, 'logToLin'), + logarithmic_function_quasilog(-2.473931188332412, "logToLin"), 0.18, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_quasilog(0.18, 'linToLog', 10), + logarithmic_function_quasilog(0.18, "linToLog", 10), -0.744727494896694, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_quasilog(-0.744727494896694, 'logToLin', 10), + logarithmic_function_quasilog(-0.744727494896694, "logToLin", 10), 0.18, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_quasilog(0.18, 'linToLog', 10, 0.75), + logarithmic_function_quasilog(0.18, "linToLog", 10, 0.75), -0.558545621172520, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_quasilog(-0.558545621172520, 'logToLin', 10, - 0.75), + logarithmic_function_quasilog( + -0.558545621172520, "logToLin", 10, 0.75 + ), 0.18, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_quasilog(0.18, 'linToLog', 10, 0.75, 0.75), + logarithmic_function_quasilog(0.18, "linToLog", 10, 0.75, 0.75), -0.652249673628745, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_quasilog(-0.652249673628745, 'logToLin', 10, - 0.75, 0.75), + logarithmic_function_quasilog( + -0.652249673628745, "logToLin", 10, 0.75, 0.75 + ), 0.18, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_quasilog(0.18, 'linToLog', 10, 0.75, 0.75, - 0.001), + logarithmic_function_quasilog( + 0.18, "linToLog", 10, 0.75, 0.75, 0.001 + ), -0.651249673628745, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_quasilog(-0.651249673628745, 'logToLin', 10, - 0.75, 0.75, 0.001), + logarithmic_function_quasilog( + -0.651249673628745, "logToLin", 10, 0.75, 0.75, 0.001 + ), 0.18, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_quasilog(0.18, 'linToLog', 10, 0.75, 0.75, - 0.001, 0.01), + logarithmic_function_quasilog( + 0.18, "linToLog", 10, 0.75, 0.75, 0.001, 0.01 + ), -0.627973998323769, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_quasilog(-0.627973998323769, 'logToLin', 10, - 0.75, 0.75, 0.001, 0.01), + logarithmic_function_quasilog( + -0.627973998323769, "logToLin", 10, 0.75, 0.75, 0.001, 0.01 + ), 0.18, - places=7) + places=7, + ) def test_n_dimensional_logarithmic_function_quasilog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ + Test :func:`colour.models.rgb.transfer_functions.log.\ logarithmic_function_quasilog` definition n-dimensional arrays support. """ - styles = ['lintolog', 'logtolin'] + styles = ["lintolog", "logtolin"] for style in styles: a = 0.18 @@ -209,36 +231,30 @@ def test_n_dimensional_logarithmic_function_quasilog(self): a = np.tile(a, 6) a_p = np.tile(a_p, 6) np.testing.assert_almost_equal( - logarithmic_function_quasilog(a, style), a_p, decimal=7) + logarithmic_function_quasilog(a, style), a_p, decimal=7 + ) a = np.reshape(a, (2, 3)) a_p = np.reshape(a_p, (2, 3)) np.testing.assert_almost_equal( - logarithmic_function_quasilog(a, style), a_p, decimal=7) + logarithmic_function_quasilog(a, style), a_p, decimal=7 + ) a = np.reshape(a, (2, 3, 1)) a_p = np.reshape(a_p, (2, 3, 1)) np.testing.assert_almost_equal( - logarithmic_function_quasilog(a, style), a_p, decimal=7) - - def test_raise_exception_logarithmic_function_quasilog(self): - """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ -logarithmic_function_quasilog` definition raised exception. - """ - - self.assertRaises(ValueError, logarithmic_function_quasilog, 0.18, - 'Undefined') + logarithmic_function_quasilog(a, style), a_p, decimal=7 + ) @ignore_numpy_errors def test_nan_logarithmic_function_quasilog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ + Test :func:`colour.models.rgb.transfer_functions.log.\ logarithmic_function_quasilog` definition nan support. """ cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] - styles = ['lintolog', 'logtolin'] + styles = ["lintolog", "logtolin"] for case in cases: for style in styles: @@ -247,135 +263,182 @@ def test_nan_logarithmic_function_quasilog(self): class TestLogarithmFunction_Camera(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.log.\ + Define :func:`colour.models.rgb.transfer_functions.log.\ logarithmic_function_camera` definition unit tests methods. """ def test_logarithmic_function_camera(self): """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ + Test :func:`colour.models.rgb.transfer_functions.log.\ logarithmic_function_camera` definition. """ self.assertAlmostEqual( - logarithmic_function_camera(0, 'cameraLinToLog'), + logarithmic_function_camera(0, "cameraLinToLog"), -9.08655123066369, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_camera(-9.08655123066369, 'cameraLogToLin'), + logarithmic_function_camera(-9.08655123066369, "cameraLogToLin"), 0.000000000000000, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_camera(0.18, 'cameraLinToLog'), + logarithmic_function_camera(0.18, "cameraLinToLog"), -2.473931188332412, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_camera(-2.473931188332412, 'cameraLogToLin'), + logarithmic_function_camera(-2.473931188332412, "cameraLogToLin"), 0.180000000000000, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_camera(1, 'cameraLinToLog'), + logarithmic_function_camera(1, "cameraLinToLog"), 0.000000000000000, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_camera(0, 'cameraLogToLin'), + logarithmic_function_camera(0, "cameraLogToLin"), 1.000000000000000, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_camera(0.18, 'cameraLinToLog', 10), + logarithmic_function_camera(0.18, "cameraLinToLog", 10), -0.744727494896693, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_camera(-0.744727494896693, 'cameraLogToLin', - 10), + logarithmic_function_camera( + -0.744727494896693, "cameraLogToLin", 10 + ), 0.180000000000000, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_camera(0.18, 'cameraLinToLog', 10, 0.25), + logarithmic_function_camera(0.18, "cameraLinToLog", 10, 0.25), -0.186181873724173, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_camera(-0.186181873724173, 'cameraLogToLin', - 10, 0.25), + logarithmic_function_camera( + -0.186181873724173, "cameraLogToLin", 10, 0.25 + ), 0.180000000000000, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_camera(0.18, 'cameraLinToLog', 10, 0.25, - 0.95), + logarithmic_function_camera( + 0.18, "cameraLinToLog", 10, 0.25, 0.95 + ), -0.191750972401961, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_camera(-0.191750972401961, 'cameraLogToLin', - 10, 0.25, 0.95), + logarithmic_function_camera( + -0.191750972401961, "cameraLogToLin", 10, 0.25, 0.95 + ), 0.180000000000000, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_camera(0.18, 'cameraLinToLog', 10, 0.25, 0.95, - 0.6), + logarithmic_function_camera( + 0.18, "cameraLinToLog", 10, 0.25, 0.95, 0.6 + ), 0.408249027598038, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_camera(0.408249027598038, 'cameraLogToLin', - 10, 0.25, 0.95, 0.6), + logarithmic_function_camera( + 0.408249027598038, "cameraLogToLin", 10, 0.25, 0.95, 0.6 + ), 0.179999999999999, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_camera(0.18, 'cameraLinToLog', 10, 0.25, 0.95, - 0.6, 0.01), + logarithmic_function_camera( + 0.18, "cameraLinToLog", 10, 0.25, 0.95, 0.6, 0.01 + ), 0.414419643717296, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_camera(0.414419643717296, 'cameraLogToLin', - 10, 0.25, 0.95, 0.6, 0.01), + logarithmic_function_camera( + 0.414419643717296, "cameraLogToLin", 10, 0.25, 0.95, 0.6, 0.01 + ), 0.180000000000000, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_camera(0.005, 'cameraLinToLog', 10, 0.25, - 0.95, 0.6, 0.01, 0.01), + logarithmic_function_camera( + 0.005, "cameraLinToLog", 10, 0.25, 0.95, 0.6, 0.01, 0.01 + ), 0.146061232468316, - places=7) - - self.assertAlmostEqual( - logarithmic_function_camera(0.146061232468316, 'cameraLogToLin', - 10, 0.25, 0.95, 0.6, 0.01, 0.01), + places=7, + ) + + self.assertAlmostEqual( + logarithmic_function_camera( + 0.146061232468316, + "cameraLogToLin", + 10, + 0.25, + 0.95, + 0.6, + 0.01, + 0.01, + ), 0.005000000000000, - places=7) + places=7, + ) self.assertAlmostEqual( - logarithmic_function_camera(0.005, 'cameraLinToLog', 10, 0.25, - 0.95, 0.6, 0.01, 0.01, 6), + logarithmic_function_camera( + 0.005, "cameraLinToLog", 10, 0.25, 0.95, 0.6, 0.01, 0.01, 6 + ), 0.142508652840630, - places=7) - - self.assertAlmostEqual( - logarithmic_function_camera(0.142508652840630, 'cameraLogToLin', - 10, 0.25, 0.95, 0.6, 0.01, 0.01, 6), + places=7, + ) + + self.assertAlmostEqual( + logarithmic_function_camera( + 0.142508652840630, + "cameraLogToLin", + 10, + 0.25, + 0.95, + 0.6, + 0.01, + 0.01, + 6, + ), 0.005000000000000, - places=7) + places=7, + ) def test_n_dimensional_logarithmic_function_camera(self): """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ + Test :func:`colour.models.rgb.transfer_functions.log.\ logarithmic_function_camera` definition n-dimensional arrays support. """ - styles = ['cameraLinToLog', 'cameraLogToLin'] + styles = ["cameraLinToLog", "cameraLogToLin"] for style in styles: a = 0.18 @@ -384,36 +447,30 @@ def test_n_dimensional_logarithmic_function_camera(self): a = np.tile(a, 6) a_p = np.tile(a_p, 6) np.testing.assert_almost_equal( - logarithmic_function_camera(a, style), a_p, decimal=7) + logarithmic_function_camera(a, style), a_p, decimal=7 + ) a = np.reshape(a, (2, 3)) a_p = np.reshape(a_p, (2, 3)) np.testing.assert_almost_equal( - logarithmic_function_camera(a, style), a_p, decimal=7) + logarithmic_function_camera(a, style), a_p, decimal=7 + ) a = np.reshape(a, (2, 3, 1)) a_p = np.reshape(a_p, (2, 3, 1)) np.testing.assert_almost_equal( - logarithmic_function_camera(a, style), a_p, decimal=7) - - def test_raise_exception_logarithmic_function_camera(self): - """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ -logarithmic_function_camera` definition raised exception. - """ - - self.assertRaises(ValueError, logarithmic_function_camera, 0.18, - 'Undefined') + logarithmic_function_camera(a, style), a_p, decimal=7 + ) @ignore_numpy_errors def test_nan_logarithmic_function_camera(self): """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ + Test :func:`colour.models.rgb.transfer_functions.log.\ logarithmic_function_camera` definition nan support. """ cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] - styles = ['cameraLinToLog', 'cameraLogToLin'] + styles = ["cameraLinToLog", "cameraLogToLin"] for case in cases: for style in styles: @@ -422,13 +479,13 @@ def test_nan_logarithmic_function_camera(self): class TestLogEncoding_Log2(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.log.\ + Define :func:`colour.models.rgb.transfer_functions.log.\ log_encoding_Log2` definition unit tests methods. """ def test_log_encoding_Log2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ + Test :func:`colour.models.rgb.transfer_functions.log.\ log_encoding_Log2` definition. """ @@ -437,24 +494,28 @@ def test_log_encoding_Log2(self): self.assertAlmostEqual(log_encoding_Log2(0.18), 0.5, places=7) self.assertAlmostEqual( - log_encoding_Log2(1.0), 0.690302399102493, places=7) + log_encoding_Log2(1.0), 0.690302399102493, places=7 + ) self.assertAlmostEqual( - log_encoding_Log2(0.18, 0.12), 0.544997115440089, places=7) + log_encoding_Log2(0.18, 0.12), 0.544997115440089, places=7 + ) self.assertAlmostEqual( - log_encoding_Log2(0.18, 0.12, 2 ** -10), + log_encoding_Log2(0.18, 0.12, 2**-10), 0.089857490719529, - places=7) + places=7, + ) self.assertAlmostEqual( - log_encoding_Log2(0.18, 0.12, 2 ** -10, 2 ** 10), + log_encoding_Log2(0.18, 0.12, 2**-10, 2**10), 0.000570299311674, - places=7) + places=7, + ) def test_n_dimensional_log_encoding_Log2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ + Test :func:`colour.models.rgb.transfer_functions.log.\ log_encoding_Log2` definition n-dimensional arrays support. """ @@ -475,23 +536,24 @@ def test_n_dimensional_log_encoding_Log2(self): def test_domain_range_scale_log_encoding_Log2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ + Test :func:`colour.models.rgb.transfer_functions.log.\ log_encoding_Log2` definition domain and range scale support. """ x = 0.18 y = log_encoding_Log2(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_Log2(x * factor), y * factor, decimal=7) + log_encoding_Log2(x * factor), y * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_Log2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ + Test :func:`colour.models.rgb.transfer_functions.log.\ log_encoding_Log2` definition nan support. """ @@ -500,40 +562,45 @@ def test_nan_log_encoding_Log2(self): class TestLogDecoding_Log2(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.log.\ + Define :func:`colour.models.rgb.transfer_functions.log.\ log_decoding_Log2` definition unit tests methods. """ def test_log_decoding_Log2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ + Test :func:`colour.models.rgb.transfer_functions.log.\ log_decoding_Log2` definition. """ self.assertAlmostEqual( - log_decoding_Log2(0.0), 0.001988737822087, places=7) + log_decoding_Log2(0.0), 0.001988737822087, places=7 + ) self.assertAlmostEqual(log_decoding_Log2(0.5), 0.18, places=7) self.assertAlmostEqual( - log_decoding_Log2(0.690302399102493), 1.0, places=7) + log_decoding_Log2(0.690302399102493), 1.0, places=7 + ) self.assertAlmostEqual( - log_decoding_Log2(0.544997115440089, 0.12), 0.18, places=7) + log_decoding_Log2(0.544997115440089, 0.12), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_Log2(0.089857490719529, 0.12, 2 ** -10), + log_decoding_Log2(0.089857490719529, 0.12, 2**-10), 0.180000000000000, - places=7) + places=7, + ) self.assertAlmostEqual( - log_decoding_Log2(0.000570299311674, 0.12, 2 ** -10, 2 ** 10), + log_decoding_Log2(0.000570299311674, 0.12, 2**-10, 2**10), 0.180000000000000, - places=7) + places=7, + ) def test_n_dimensional_log_decoding_Log2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ + Test :func:`colour.models.rgb.transfer_functions.log.\ log_decoding_Log2` definition n-dimensional arrays support. """ @@ -554,28 +621,29 @@ def test_n_dimensional_log_decoding_Log2(self): def test_domain_range_scale_log_decoding_Log2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ + Test :func:`colour.models.rgb.transfer_functions.log.\ log_decoding_Log2` definition domain and range scale support. """ y = 0.5 x = log_decoding_Log2(y) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_Log2(y * factor), x * factor, decimal=7) + log_decoding_Log2(y * factor), x * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_Log2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.log.\ + Test :func:`colour.models.rgb.transfer_functions.log.\ log_decoding_Log2` definition nan support. """ log_decoding_Log2(np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_nikon_nlog.py b/colour/models/rgb/transfer_functions/tests/test_nikon_nlog.py new file mode 100644 index 0000000000..5bb913d749 --- /dev/null +++ b/colour/models/rgb/transfer_functions/tests/test_nikon_nlog.py @@ -0,0 +1,211 @@ +""" +Defines the unit tests for the :mod:`colour.models.rgb.transfer_functions.\ +nikon_nlog` module. +""" + +import numpy as np +import unittest + +from colour.models.rgb.transfer_functions import ( + log_encoding_NLog, + log_decoding_NLog, +) +from colour.utilities import domain_range_scale, ignore_numpy_errors + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestLogEncoding_VLog", + "TestLogDecoding_VLog", +] + + +class TestLogEncoding_VLog(unittest.TestCase): + """ + Define :func:`colour.models.rgb.transfer_functions.nikon_nlog.\ +log_encoding_NLog` definition unit tests methods. + """ + + def test_log_encoding_NLog(self): + """ + Test :func:`colour.models.rgb.transfer_functions.nikon_nlog.\ +log_encoding_NLog` definition. + """ + + self.assertAlmostEqual( + log_encoding_NLog(0.0), 0.124372627896372, places=7 + ) + + self.assertAlmostEqual( + log_encoding_NLog(0.18), 0.363667770117139, places=7 + ) + + self.assertAlmostEqual( + log_encoding_NLog(0.18, 12), 0.363667770117139, places=7 + ) + + self.assertAlmostEqual( + log_encoding_NLog(0.18, 10, False), 0.351634850262366, places=7 + ) + + self.assertAlmostEqual( + log_encoding_NLog(0.18, 10, False, False), + 0.337584957293328, + places=7, + ) + + self.assertAlmostEqual( + log_encoding_NLog(1.0), 0.605083088954056, places=7 + ) + + def test_n_dimensional_log_encoding_NLog(self): + """ + Test :func:`colour.models.rgb.transfer_functions.nikon_nlog.\ +log_encoding_NLog` definition n-dimensional arrays support. + """ + + L_in = 0.18 + V_out = log_encoding_NLog(L_in) + + L_in = np.tile(L_in, 6) + V_out = np.tile(V_out, 6) + np.testing.assert_almost_equal( + log_encoding_NLog(L_in), V_out, decimal=7 + ) + + L_in = np.reshape(L_in, (2, 3)) + V_out = np.reshape(V_out, (2, 3)) + np.testing.assert_almost_equal( + log_encoding_NLog(L_in), V_out, decimal=7 + ) + + L_in = np.reshape(L_in, (2, 3, 1)) + V_out = np.reshape(V_out, (2, 3, 1)) + np.testing.assert_almost_equal( + log_encoding_NLog(L_in), V_out, decimal=7 + ) + + def test_domain_range_scale_log_encoding_NLog(self): + """ + Test :func:`colour.models.rgb.transfer_functions.nikon_nlog.\ +log_encoding_NLog` definition domain and range scale support. + """ + + L_in = 0.18 + V_out = log_encoding_NLog(L_in) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + log_encoding_NLog(L_in * factor), V_out * factor, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_log_encoding_NLog(self): + """ + Test :func:`colour.models.rgb.transfer_functions.nikon_nlog.\ +log_encoding_NLog` definition nan support. + """ + + log_encoding_NLog(np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + + +class TestLogDecoding_VLog(unittest.TestCase): + """ + Define :func:`colour.models.rgb.transfer_functions.nikon_nlog.\ +log_decoding_NLog` definition unit tests methods. + """ + + def test_log_decoding_NLog(self): + """ + Test :func:`colour.models.rgb.transfer_functions.nikon_nlog.\ +log_decoding_NLog` definition. + """ + + self.assertAlmostEqual( + log_decoding_NLog(0.124372627896372), 0.0, places=7 + ) + + self.assertAlmostEqual( + log_decoding_NLog(0.363667770117139), 0.18, places=7 + ) + + self.assertAlmostEqual( + log_decoding_NLog(0.363667770117139, 12), 0.18, places=7 + ) + + self.assertAlmostEqual( + log_decoding_NLog(0.351634850262366, 10, False), 0.18, places=7 + ) + + self.assertAlmostEqual( + log_decoding_NLog(0.337584957293328, 10, False, False), + 0.18, + places=7, + ) + + self.assertAlmostEqual( + log_decoding_NLog(0.605083088954056), 1.0, places=7 + ) + + def test_n_dimensional_log_decoding_NLog(self): + """ + Test :func:`colour.models.rgb.transfer_functions.nikon_nlog.\ +log_decoding_NLog` definition n-dimensional arrays support. + """ + + V_out = 0.363667770117139 + L_in = log_decoding_NLog(V_out) + + V_out = np.tile(V_out, 6) + L_in = np.tile(L_in, 6) + np.testing.assert_almost_equal( + log_decoding_NLog(V_out), L_in, decimal=7 + ) + + V_out = np.reshape(V_out, (2, 3)) + L_in = np.reshape(L_in, (2, 3)) + np.testing.assert_almost_equal( + log_decoding_NLog(V_out), L_in, decimal=7 + ) + + V_out = np.reshape(V_out, (2, 3, 1)) + L_in = np.reshape(L_in, (2, 3, 1)) + np.testing.assert_almost_equal( + log_decoding_NLog(V_out), L_in, decimal=7 + ) + + def test_domain_range_scale_log_decoding_NLog(self): + """ + Test :func:`colour.models.rgb.transfer_functions.nikon_nlog.\ +log_decoding_NLog` definition domain and range scale support. + """ + + V_out = 0.363667770117139 + L_in = log_decoding_NLog(V_out) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + log_decoding_NLog(V_out * factor), L_in * factor, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_log_decoding_NLog(self): + """ + Test :func:`colour.models.rgb.transfer_functions.nikon_nlog.\ +log_decoding_NLog` definition nan support. + """ + + log_decoding_NLog(np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_panalog.py b/colour/models/rgb/transfer_functions/tests/test_panalog.py index 1b1dcb296f..ea876702cf 100644 --- a/colour/models/rgb/transfer_functions/tests/test_panalog.py +++ b/colour/models/rgb/transfer_functions/tests/test_panalog.py @@ -1,52 +1,57 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.panalog` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.panalog` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.models.rgb.transfer_functions import (log_encoding_Panalog, - log_decoding_Panalog) +from colour.models.rgb.transfer_functions import ( + log_encoding_Panalog, + log_decoding_Panalog, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestLogEncoding_Panalog', 'TestLogDecoding_Panalog'] +__all__ = [ + "TestLogEncoding_Panalog", + "TestLogDecoding_Panalog", +] class TestLogEncoding_Panalog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.panalog.\ + Define :func:`colour.models.rgb.transfer_functions.panalog.\ log_encoding_Panalog` definition unit tests methods. """ def test_log_encoding_Panalog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.panalog.\ + Test :func:`colour.models.rgb.transfer_functions.panalog.\ log_encoding_Panalog` definition. """ self.assertAlmostEqual( - log_encoding_Panalog(0.0), 0.062561094819159, places=7) + log_encoding_Panalog(0.0), 0.062561094819159, places=7 + ) self.assertAlmostEqual( - log_encoding_Panalog(0.18), 0.374576791382298, places=7) + log_encoding_Panalog(0.18), 0.374576791382298, places=7 + ) self.assertAlmostEqual( - log_encoding_Panalog(1.0), 0.665689149560117, places=7) + log_encoding_Panalog(1.0), 0.665689149560117, places=7 + ) def test_n_dimensional_log_encoding_Panalog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.panalog.\ + Test :func:`colour.models.rgb.transfer_functions.panalog.\ log_encoding_Panalog` definition n-dimensional arrays support. """ @@ -67,54 +72,59 @@ def test_n_dimensional_log_encoding_Panalog(self): def test_domain_range_scale_log_encoding_Panalog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.panalog.\ + Test :func:`colour.models.rgb.transfer_functions.panalog.\ log_encoding_Panalog` definition domain and range scale support. """ x = 0.18 y = log_encoding_Panalog(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_Panalog(x * factor), y * factor, decimal=7) + log_encoding_Panalog(x * factor), y * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_Panalog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.panalog.\ + Test :func:`colour.models.rgb.transfer_functions.panalog.\ log_encoding_Panalog` definition nan support. """ log_encoding_Panalog( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_Panalog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.panalog.\ + Define :func:`colour.models.rgb.transfer_functions.panalog.\ log_decoding_Panalog` definition unit tests methods. """ def test_log_decoding_Panalog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.panalog.\ + Test :func:`colour.models.rgb.transfer_functions.panalog.\ log_decoding_Panalog` definition. """ self.assertAlmostEqual( - log_decoding_Panalog(0.062561094819159), 0.0, places=7) + log_decoding_Panalog(0.062561094819159), 0.0, places=7 + ) self.assertAlmostEqual( - log_decoding_Panalog(0.374576791382298), 0.18, places=7) + log_decoding_Panalog(0.374576791382298), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_Panalog(0.665689149560117), 1.0, places=7) + log_decoding_Panalog(0.665689149560117), 1.0, places=7 + ) def test_n_dimensional_log_decoding_Panalog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.panalog.\ + Test :func:`colour.models.rgb.transfer_functions.panalog.\ log_decoding_Panalog` definition n-dimensional arrays support. """ @@ -135,29 +145,31 @@ def test_n_dimensional_log_decoding_Panalog(self): def test_domain_range_scale_log_decoding_Panalog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.panalog.\ + Test :func:`colour.models.rgb.transfer_functions.panalog.\ log_decoding_Panalog` definition domain and range scale support. """ y = 0.374576791382298 x = log_decoding_Panalog(y) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_Panalog(y * factor), x * factor, decimal=7) + log_decoding_Panalog(y * factor), x * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_Panalog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.panalog.\ + Test :func:`colour.models.rgb.transfer_functions.panalog.\ log_decoding_Panalog` definition nan support. """ log_decoding_Panalog( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_panasonic_vlog.py b/colour/models/rgb/transfer_functions/tests/test_panasonic_vlog.py index 3b648b6171..5f9a5e477f 100644 --- a/colour/models/rgb/transfer_functions/tests/test_panasonic_vlog.py +++ b/colour/models/rgb/transfer_functions/tests/test_panasonic_vlog.py @@ -1,62 +1,69 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.\ +Defines the unit tests for the :mod:`colour.models.rgb.transfer_functions.\ panasonic_vlog` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.models.rgb.transfer_functions import (log_encoding_VLog, - log_decoding_VLog) +from colour.models.rgb.transfer_functions import ( + log_encoding_VLog, + log_decoding_VLog, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestLogEncoding_VLog', 'TestLogDecoding_VLog'] +__all__ = [ + "TestLogEncoding_VLog", + "TestLogDecoding_VLog", +] class TestLogEncoding_VLog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ + Define :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ log_encoding_VLog` definition unit tests methods. """ def test_log_encoding_VLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ + Test :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ log_encoding_VLog` definition. """ self.assertAlmostEqual(log_encoding_VLog(0.0), 0.125, places=7) self.assertAlmostEqual( - log_encoding_VLog(0.18), 0.423311448760136, places=7) + log_encoding_VLog(0.18), 0.423311448760136, places=7 + ) self.assertAlmostEqual( - log_encoding_VLog(0.18, 12), 0.423311448760136, places=7) + log_encoding_VLog(0.18, 12), 0.423311448760136, places=7 + ) self.assertAlmostEqual( - log_encoding_VLog(0.18, 10, False), 0.421287228403675, places=7) + log_encoding_VLog(0.18, 10, False), 0.421287228403675, places=7 + ) self.assertAlmostEqual( log_encoding_VLog(0.18, 10, False, False), 0.409009628526078, - places=7) + places=7, + ) self.assertAlmostEqual( - log_encoding_VLog(1.0), 0.599117700158146, places=7) + log_encoding_VLog(1.0), 0.599117700158146, places=7 + ) def test_n_dimensional_log_encoding_VLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ + Test :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ log_encoding_VLog` definition n-dimensional arrays support. """ @@ -66,39 +73,41 @@ def test_n_dimensional_log_encoding_VLog(self): L_in = np.tile(L_in, 6) V_out = np.tile(V_out, 6) np.testing.assert_almost_equal( - log_encoding_VLog(L_in), V_out, decimal=7) + log_encoding_VLog(L_in), V_out, decimal=7 + ) L_in = np.reshape(L_in, (2, 3)) V_out = np.reshape(V_out, (2, 3)) np.testing.assert_almost_equal( - log_encoding_VLog(L_in), V_out, decimal=7) + log_encoding_VLog(L_in), V_out, decimal=7 + ) L_in = np.reshape(L_in, (2, 3, 1)) V_out = np.reshape(V_out, (2, 3, 1)) np.testing.assert_almost_equal( - log_encoding_VLog(L_in), V_out, decimal=7) + log_encoding_VLog(L_in), V_out, decimal=7 + ) def test_domain_range_scale_log_encoding_VLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ + Test :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ log_encoding_VLog` definition domain and range scale support. """ L_in = 0.18 V_out = log_encoding_VLog(L_in) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_VLog(L_in * factor), - V_out * factor, - decimal=7) + log_encoding_VLog(L_in * factor), V_out * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_VLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ + Test :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ log_encoding_VLog` definition nan support. """ @@ -107,38 +116,43 @@ def test_nan_log_encoding_VLog(self): class TestLogDecoding_VLog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ + Define :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ log_decoding_VLog` definition unit tests methods. """ def test_log_decoding_VLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ + Test :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ log_decoding_VLog` definition. """ self.assertAlmostEqual(log_decoding_VLog(0.125), 0.0, places=7) self.assertAlmostEqual( - log_decoding_VLog(0.423311448760136), 0.18, places=7) + log_decoding_VLog(0.423311448760136), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_VLog(0.423311448760136, 12), 0.18, places=7) + log_decoding_VLog(0.423311448760136, 12), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_VLog(0.421287228403675, 10, False), 0.18, places=7) + log_decoding_VLog(0.421287228403675, 10, False), 0.18, places=7 + ) self.assertAlmostEqual( log_decoding_VLog(0.409009628526078, 10, False, False), 0.18, - places=7) + places=7, + ) self.assertAlmostEqual( - log_decoding_VLog(0.599117700158146), 1.0, places=7) + log_decoding_VLog(0.599117700158146), 1.0, places=7 + ) def test_n_dimensional_log_decoding_VLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ + Test :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ log_decoding_VLog` definition n-dimensional arrays support. """ @@ -148,44 +162,46 @@ def test_n_dimensional_log_decoding_VLog(self): V_out = np.tile(V_out, 6) L_in = np.tile(L_in, 6) np.testing.assert_almost_equal( - log_decoding_VLog(V_out), L_in, decimal=7) + log_decoding_VLog(V_out), L_in, decimal=7 + ) V_out = np.reshape(V_out, (2, 3)) L_in = np.reshape(L_in, (2, 3)) np.testing.assert_almost_equal( - log_decoding_VLog(V_out), L_in, decimal=7) + log_decoding_VLog(V_out), L_in, decimal=7 + ) V_out = np.reshape(V_out, (2, 3, 1)) L_in = np.reshape(L_in, (2, 3, 1)) np.testing.assert_almost_equal( - log_decoding_VLog(V_out), L_in, decimal=7) + log_decoding_VLog(V_out), L_in, decimal=7 + ) def test_domain_range_scale_log_decoding_VLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ + Test :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ log_decoding_VLog` definition domain and range scale support. """ V_out = 0.423311448760136 L_in = log_decoding_VLog(V_out) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_VLog(V_out * factor), - L_in * factor, - decimal=7) + log_decoding_VLog(V_out * factor), L_in * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_VLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ + Test :func:`colour.models.rgb.transfer_functions.panasonic_vlog.\ log_decoding_VLog` definition nan support. """ log_decoding_VLog(np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_pivoted_log.py b/colour/models/rgb/transfer_functions/tests/test_pivoted_log.py index 1e45b247b9..b135443b0d 100644 --- a/colour/models/rgb/transfer_functions/tests/test_pivoted_log.py +++ b/colour/models/rgb/transfer_functions/tests/test_pivoted_log.py @@ -1,51 +1,55 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.\ +Defines the unit tests for the :mod:`colour.models.rgb.transfer_functions.\ pivoted_log` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.models.rgb.transfer_functions import (log_encoding_PivotedLog, - log_decoding_PivotedLog) +from colour.models.rgb.transfer_functions import ( + log_encoding_PivotedLog, + log_decoding_PivotedLog, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestLogEncoding_PivotedLog', 'TestLogDecoding_PivotedLog'] +__all__ = [ + "TestLogEncoding_PivotedLog", + "TestLogDecoding_PivotedLog", +] class TestLogEncoding_PivotedLog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.pivoted_log.\ + Define :func:`colour.models.rgb.transfer_functions.pivoted_log.\ log_encoding_PivotedLog` definition unit tests methods. """ def test_log_encoding_PivotedLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.pivoted_log.\ + Test :func:`colour.models.rgb.transfer_functions.pivoted_log.\ log_encoding_PivotedLog` definition. """ self.assertAlmostEqual(log_encoding_PivotedLog(0.0), -np.inf, places=7) self.assertAlmostEqual( - log_encoding_PivotedLog(0.18), 0.434995112414467, places=7) + log_encoding_PivotedLog(0.18), 0.434995112414467, places=7 + ) self.assertAlmostEqual( - log_encoding_PivotedLog(1.0), 0.653390272208219, places=7) + log_encoding_PivotedLog(1.0), 0.653390272208219, places=7 + ) def test_n_dimensional_log_encoding_PivotedLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.pivoted_log.\ + Test :func:`colour.models.rgb.transfer_functions.pivoted_log.\ log_encoding_PivotedLog` definition n-dimensional arrays support. """ @@ -55,67 +59,74 @@ def test_n_dimensional_log_encoding_PivotedLog(self): x = np.tile(x, 6) y = np.tile(y, 6) np.testing.assert_almost_equal( - log_encoding_PivotedLog(x), y, decimal=7) + log_encoding_PivotedLog(x), y, decimal=7 + ) x = np.reshape(x, (2, 3)) y = np.reshape(y, (2, 3)) np.testing.assert_almost_equal( - log_encoding_PivotedLog(x), y, decimal=7) + log_encoding_PivotedLog(x), y, decimal=7 + ) x = np.reshape(x, (2, 3, 1)) y = np.reshape(y, (2, 3, 1)) np.testing.assert_almost_equal( - log_encoding_PivotedLog(x), y, decimal=7) + log_encoding_PivotedLog(x), y, decimal=7 + ) def test_domain_range_scale_log_encoding_PivotedLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.pivoted_log.\ + Test :func:`colour.models.rgb.transfer_functions.pivoted_log.\ log_encoding_PivotedLog` definition domain and range scale support. """ x = 0.18 y = log_encoding_PivotedLog(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_PivotedLog(x * factor), y * factor, decimal=7) + log_encoding_PivotedLog(x * factor), y * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_PivotedLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.pivoted_log.\ + Test :func:`colour.models.rgb.transfer_functions.pivoted_log.\ log_encoding_PivotedLog` definition nan support. """ log_encoding_PivotedLog( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_PivotedLog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.pivoted_log.\ + Define :func:`colour.models.rgb.transfer_functions.pivoted_log.\ log_decoding_PivotedLog` definition unit tests methods. """ def test_log_decoding_PivotedLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.pivoted_log.\ + Test :func:`colour.models.rgb.transfer_functions.pivoted_log.\ log_decoding_PivotedLog` definition. """ self.assertAlmostEqual(log_decoding_PivotedLog(-np.inf), 0.0, places=7) self.assertAlmostEqual( - log_decoding_PivotedLog(0.434995112414467), 0.18, places=7) + log_decoding_PivotedLog(0.434995112414467), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_PivotedLog(0.653390272208219), 1.0, places=7) + log_decoding_PivotedLog(0.653390272208219), 1.0, places=7 + ) def test_n_dimensional_log_decoding_PivotedLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.pivoted_log.\ + Test :func:`colour.models.rgb.transfer_functions.pivoted_log.\ log_decoding_PivotedLog` definition n-dimensional arrays support. """ @@ -125,43 +136,48 @@ def test_n_dimensional_log_decoding_PivotedLog(self): y = np.tile(y, 6) x = np.tile(x, 6) np.testing.assert_almost_equal( - log_decoding_PivotedLog(y), x, decimal=7) + log_decoding_PivotedLog(y), x, decimal=7 + ) y = np.reshape(y, (2, 3)) x = np.reshape(x, (2, 3)) np.testing.assert_almost_equal( - log_decoding_PivotedLog(y), x, decimal=7) + log_decoding_PivotedLog(y), x, decimal=7 + ) y = np.reshape(y, (2, 3, 1)) x = np.reshape(x, (2, 3, 1)) np.testing.assert_almost_equal( - log_decoding_PivotedLog(y), x, decimal=7) + log_decoding_PivotedLog(y), x, decimal=7 + ) def test_domain_range_scale_log_decoding_PivotedLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.pivoted_log.\ + Test :func:`colour.models.rgb.transfer_functions.pivoted_log.\ log_decoding_PivotedLog` definition domain and range scale support. """ y = 0.434995112414467 x = log_decoding_PivotedLog(y) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_PivotedLog(y * factor), x * factor, decimal=7) + log_decoding_PivotedLog(y * factor), x * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_PivotedLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.pivoted_log.\ + Test :func:`colour.models.rgb.transfer_functions.pivoted_log.\ log_decoding_PivotedLog` definition nan support. """ log_decoding_PivotedLog( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_red_log.py b/colour/models/rgb/transfer_functions/tests/test_red_log.py index 16de81af17..714395e9f1 100644 --- a/colour/models/rgb/transfer_functions/tests/test_red_log.py +++ b/colour/models/rgb/transfer_functions/tests/test_red_log.py @@ -1,60 +1,75 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.red_log` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.red_log` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from colour.models.rgb.transfer_functions import ( - log_encoding_REDLog, log_decoding_REDLog, log_encoding_REDLogFilm, - log_decoding_REDLogFilm, log_encoding_Log3G12, log_decoding_Log3G12) + log_encoding_REDLog, + log_decoding_REDLog, + log_encoding_REDLogFilm, + log_decoding_REDLogFilm, + log_encoding_Log3G12, + log_decoding_Log3G12, +) from colour.models.rgb.transfer_functions.red_log import ( - log_encoding_Log3G10_v1, log_decoding_Log3G10_v1, log_encoding_Log3G10_v2, - log_decoding_Log3G10_v2) + log_encoding_Log3G10_v1, + log_decoding_Log3G10_v1, + log_encoding_Log3G10_v2, + log_decoding_Log3G10_v2, + log_encoding_Log3G10_v3, + log_decoding_Log3G10_v3, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Development' +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Development" __all__ = [ - 'TestLogEncoding_REDLog', 'TestLogDecoding_REDLog', - 'TestLogEncoding_REDLogFilm', 'TestLogDecoding_REDLogFilm', - 'TestLogEncoding_Log3G10_v1', 'TestLogDecoding_Log3G10_v1', - 'TestLogEncoding_Log3G10_v2', 'TestLogDecoding_Log3G10_v2', - 'TestLogEncoding_Log3G12', 'TestLogDecoding_Log3G12' + "TestLogEncoding_REDLog", + "TestLogDecoding_REDLog", + "TestLogEncoding_REDLogFilm", + "TestLogDecoding_REDLogFilm", + "TestLogEncoding_Log3G10_v1", + "TestLogDecoding_Log3G10_v1", + "TestLogEncoding_Log3G10_v2", + "TestLogDecoding_Log3G10_v2", + "TestLogEncoding_Log3G10_v3", + "TestLogDecoding_Log3G10_v3", + "TestLogEncoding_Log3G12", + "TestLogDecoding_Log3G12", ] class TestLogEncoding_REDLog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.red_log.\ + Define :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_REDLog` definition unit tests methods. """ def test_log_encoding_REDLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_REDLog` definition. """ self.assertAlmostEqual(log_encoding_REDLog(0.0), 0.0, places=7) self.assertAlmostEqual( - log_encoding_REDLog(0.18), 0.637621845988175, places=7) + log_encoding_REDLog(0.18), 0.637621845988175, places=7 + ) self.assertAlmostEqual(log_encoding_REDLog(1.0), 1.0, places=7) def test_n_dimensional_log_encoding_REDLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_REDLog` definition n-dimensional arrays support. """ @@ -75,52 +90,55 @@ def test_n_dimensional_log_encoding_REDLog(self): def test_domain_range_scale_log_encoding_REDLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_REDLog` definition domain and range scale support. """ x = 0.18 y = log_encoding_REDLog(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_REDLog(x * factor), y * factor, decimal=7) + log_encoding_REDLog(x * factor), y * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_REDLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_REDLog` definition nan support. """ log_encoding_REDLog( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_REDLog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.red_log.\ + Define :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_REDLog` definition unit tests methods. """ def test_log_decoding_REDLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_REDLog` definition. """ self.assertAlmostEqual(log_decoding_REDLog(0.0), 0.0, places=7) self.assertAlmostEqual( - log_decoding_REDLog(0.637621845988175), 0.18, places=7) + log_decoding_REDLog(0.637621845988175), 0.18, places=7 + ) self.assertAlmostEqual(log_decoding_REDLog(1.0), 1.0, places=7) def test_n_dimensional_log_decoding_REDLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_REDLog` definition n-dimensional arrays support. """ @@ -141,54 +159,59 @@ def test_n_dimensional_log_decoding_REDLog(self): def test_domain_range_scale_log_decoding_REDLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_REDLog` definition domain and range scale support. """ y = 0.637621845988175 x = log_decoding_REDLog(y) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_REDLog(y * factor), x * factor, decimal=7) + log_decoding_REDLog(y * factor), x * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_REDLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_REDLog` definition nan support. """ log_decoding_REDLog( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogEncoding_REDLogFilm(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.red_log.\ + Define :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_REDLogFilm` definition unit tests methods. """ def test_log_encoding_REDLogFilm(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_REDLogFilm` definition. """ self.assertAlmostEqual( - log_encoding_REDLogFilm(0.0), 0.092864125122190, places=7) + log_encoding_REDLogFilm(0.0), 0.092864125122190, places=7 + ) self.assertAlmostEqual( - log_encoding_REDLogFilm(0.18), 0.457319613085418, places=7) + log_encoding_REDLogFilm(0.18), 0.457319613085418, places=7 + ) self.assertAlmostEqual( - log_encoding_REDLogFilm(1.0), 0.669599217986315, places=7) + log_encoding_REDLogFilm(1.0), 0.669599217986315, places=7 + ) def test_n_dimensional_log_encoding_REDLogFilm(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_REDLogFilm` definition n-dimensional arrays support. """ @@ -198,68 +221,76 @@ def test_n_dimensional_log_encoding_REDLogFilm(self): x = np.tile(x, 6) y = np.tile(y, 6) np.testing.assert_almost_equal( - log_encoding_REDLogFilm(x), y, decimal=7) + log_encoding_REDLogFilm(x), y, decimal=7 + ) x = np.reshape(x, (2, 3)) y = np.reshape(y, (2, 3)) np.testing.assert_almost_equal( - log_encoding_REDLogFilm(x), y, decimal=7) + log_encoding_REDLogFilm(x), y, decimal=7 + ) x = np.reshape(x, (2, 3, 1)) y = np.reshape(y, (2, 3, 1)) np.testing.assert_almost_equal( - log_encoding_REDLogFilm(x), y, decimal=7) + log_encoding_REDLogFilm(x), y, decimal=7 + ) def test_domain_range_scale_log_encoding_REDLogFilm(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_REDLogFilm` definition domain and range scale support. """ x = 0.18 y = log_encoding_REDLogFilm(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_REDLogFilm(x * factor), y * factor, decimal=7) + log_encoding_REDLogFilm(x * factor), y * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_REDLogFilm(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_REDLogFilm` definition nan support. """ log_encoding_REDLogFilm( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_REDLogFilm(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.red_log.\ + Define :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_REDLogFilm` definition unit tests methods. """ def test_log_decoding_REDLogFilm(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_REDLogFilm` definition. """ self.assertAlmostEqual( - log_decoding_REDLogFilm(0.092864125122190), 0.0, places=7) + log_decoding_REDLogFilm(0.092864125122190), 0.0, places=7 + ) self.assertAlmostEqual( - log_decoding_REDLogFilm(0.457319613085418), 0.18, places=7) + log_decoding_REDLogFilm(0.457319613085418), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_REDLogFilm(0.669599217986315), 1.0, places=7) + log_decoding_REDLogFilm(0.669599217986315), 1.0, places=7 + ) def test_n_dimensional_log_decoding_REDLogFilm(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_REDLogFilm` definition n-dimensional arrays support. """ @@ -269,67 +300,74 @@ def test_n_dimensional_log_decoding_REDLogFilm(self): y = np.tile(y, 6) x = np.tile(x, 6) np.testing.assert_almost_equal( - log_decoding_REDLogFilm(y), x, decimal=7) + log_decoding_REDLogFilm(y), x, decimal=7 + ) y = np.reshape(y, (2, 3)) x = np.reshape(x, (2, 3)) np.testing.assert_almost_equal( - log_decoding_REDLogFilm(y), x, decimal=7) + log_decoding_REDLogFilm(y), x, decimal=7 + ) y = np.reshape(y, (2, 3, 1)) x = np.reshape(x, (2, 3, 1)) np.testing.assert_almost_equal( - log_decoding_REDLogFilm(y), x, decimal=7) + log_decoding_REDLogFilm(y), x, decimal=7 + ) def test_domain_range_scale_log_decoding_REDLogFilm(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_REDLogFilm` definition domain and range scale support. """ y = 0.457319613085418 x = log_decoding_REDLogFilm(y) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_REDLogFilm(y * factor), x * factor, decimal=7) + log_decoding_REDLogFilm(y * factor), x * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_REDLogFilm(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_REDLogFilm` definition nan support. """ log_decoding_REDLogFilm( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogEncoding_Log3G10_v1(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.red_log.\ + Define :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_Log3G10_v1` definition unit tests methods. """ def test_log_encoding_Log3G10_v1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_Log3G10_v1` definition. """ self.assertAlmostEqual( - log_encoding_Log3G10_v1(-1.0), -0.496483569056003, places=7) + log_encoding_Log3G10_v1(-1.0), -0.496483569056003, places=7 + ) self.assertAlmostEqual(log_encoding_Log3G10_v1(0.0), 0.0, places=7) self.assertAlmostEqual( - log_encoding_Log3G10_v1(0.18), 0.333333644207707, places=7) + log_encoding_Log3G10_v1(0.18), 0.333333644207707, places=7 + ) def test_n_dimensional_log_encoding_Log3G10_v1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_Log3G10_v1` definition n-dimensional arrays support. """ @@ -339,67 +377,74 @@ def test_n_dimensional_log_encoding_Log3G10_v1(self): x = np.tile(x, 6) y = np.tile(y, 6) np.testing.assert_almost_equal( - log_encoding_Log3G10_v1(x), y, decimal=7) + log_encoding_Log3G10_v1(x), y, decimal=7 + ) x = np.reshape(x, (2, 3)) y = np.reshape(y, (2, 3)) np.testing.assert_almost_equal( - log_encoding_Log3G10_v1(x), y, decimal=7) + log_encoding_Log3G10_v1(x), y, decimal=7 + ) x = np.reshape(x, (2, 3, 1)) y = np.reshape(y, (2, 3, 1)) np.testing.assert_almost_equal( - log_encoding_Log3G10_v1(x), y, decimal=7) + log_encoding_Log3G10_v1(x), y, decimal=7 + ) def test_domain_range_scale_log_encoding_Log3G10_v1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_Log3G10_v1` definition domain and range scale support. """ x = 0.18 y = log_encoding_Log3G10_v1(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_Log3G10_v1(x * factor), y * factor, decimal=7) + log_encoding_Log3G10_v1(x * factor), y * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_Log3G10_v1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_Log3G10_v1` definition nan support. """ log_encoding_Log3G10_v1( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_Log3G10_v1(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.red_log.\ + Define :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_Log3G10_v1` definition unit tests methods. """ def test_log_decoding_Log3G10_v1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_Log3G10_v1` definition. """ self.assertAlmostEqual( - log_decoding_Log3G10_v1(-0.496483569056003), -1.0, places=7) + log_decoding_Log3G10_v1(-0.496483569056003), -1.0, places=7 + ) self.assertAlmostEqual(log_decoding_Log3G10_v1(0.0), 0.0, places=7) self.assertAlmostEqual( - log_decoding_Log3G10_v1(0.333333644207707), 0.18, places=7) + log_decoding_Log3G10_v1(0.333333644207707), 0.18, places=7 + ) def test_n_dimensional_log_decoding_Log3G10_v1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_Log3G10_v1` definition n-dimensional arrays support. """ @@ -409,68 +454,76 @@ def test_n_dimensional_log_decoding_Log3G10_v1(self): y = np.tile(y, 6) x = np.tile(x, 6) np.testing.assert_almost_equal( - log_decoding_Log3G10_v1(y), x, decimal=7) + log_decoding_Log3G10_v1(y), x, decimal=7 + ) y = np.reshape(y, (2, 3)) x = np.reshape(x, (2, 3)) np.testing.assert_almost_equal( - log_decoding_Log3G10_v1(y), x, decimal=7) + log_decoding_Log3G10_v1(y), x, decimal=7 + ) y = np.reshape(y, (2, 3, 1)) x = np.reshape(x, (2, 3, 1)) np.testing.assert_almost_equal( - log_decoding_Log3G10_v1(y), x, decimal=7) + log_decoding_Log3G10_v1(y), x, decimal=7 + ) def test_domain_range_scale_log_decoding_Log3G10_v1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_Log3G10_v1` definition domain and range scale support. """ y = 0.333333644207707 x = log_decoding_Log3G10_v1(y) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_Log3G10_v1(y * factor), x * factor, decimal=7) + log_decoding_Log3G10_v1(y * factor), x * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_Log3G10_v1(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_Log3G10_v1` definition nan support. """ log_decoding_Log3G10_v1( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogEncoding_Log3G10_v2(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.red_log.\ + Define :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_Log3G10_v2` definition unit tests methods. """ def test_log_encoding_Log3G10_v2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_Log3G10_v2` definition. """ self.assertAlmostEqual( - log_encoding_Log3G10_v2(-1.0), -0.491512777522511, places=7) + log_encoding_Log3G10_v2(-1.0), -0.491512777522511, places=7 + ) self.assertAlmostEqual( - log_encoding_Log3G10_v2(0.0), 0.091551487714745, places=7) + log_encoding_Log3G10_v2(0.0), 0.091551487714745, places=7 + ) self.assertAlmostEqual( - log_encoding_Log3G10_v2(0.18), 0.333332912025992, places=7) + log_encoding_Log3G10_v2(0.18), 0.333332912025992, places=7 + ) def test_n_dimensional_log_encoding_Log3G10_v2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_Log3G10_v2` definition n-dimensional arrays support. """ @@ -480,68 +533,76 @@ def test_n_dimensional_log_encoding_Log3G10_v2(self): x = np.tile(x, 6) y = np.tile(y, 6) np.testing.assert_almost_equal( - log_encoding_Log3G10_v2(x), y, decimal=7) + log_encoding_Log3G10_v2(x), y, decimal=7 + ) x = np.reshape(x, (2, 3)) y = np.reshape(y, (2, 3)) np.testing.assert_almost_equal( - log_encoding_Log3G10_v2(x), y, decimal=7) + log_encoding_Log3G10_v2(x), y, decimal=7 + ) x = np.reshape(x, (2, 3, 1)) y = np.reshape(y, (2, 3, 1)) np.testing.assert_almost_equal( - log_encoding_Log3G10_v2(x), y, decimal=7) + log_encoding_Log3G10_v2(x), y, decimal=7 + ) def test_domain_range_scale_log_encoding_Log3G10_v2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_Log3G10_v2` definition domain and range scale support. """ x = 0.18 y = log_encoding_Log3G10_v2(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_Log3G10_v2(x * factor), y * factor, decimal=7) + log_encoding_Log3G10_v2(x * factor), y * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_Log3G10_v2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_Log3G10_v2` definition nan support. """ log_encoding_Log3G10_v2( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_Log3G10_v2(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.red_log.\ + Define :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_Log3G10_v2` definition unit tests methods. """ def test_log_decoding_Log3G10_v2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_Log3G10_v2` definition. """ self.assertAlmostEqual( - log_decoding_Log3G10_v2(-0.491512777522511), -1.0, places=7) + log_decoding_Log3G10_v2(-0.491512777522511), -1.0, places=7 + ) self.assertAlmostEqual( - log_decoding_Log3G10_v2(0.091551487714745), 0.0, places=7) + log_decoding_Log3G10_v2(0.091551487714745), 0.0, places=7 + ) self.assertAlmostEqual( - log_decoding_Log3G10_v2(0.333332912025992), 0.18, places=7) + log_decoding_Log3G10_v2(0.333332912025992), 0.18, places=7 + ) def test_n_dimensional_log_decoding_Log3G10_v2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_Log3G10_v2` definition n-dimensional arrays support. """ @@ -551,70 +612,236 @@ def test_n_dimensional_log_decoding_Log3G10_v2(self): y = np.tile(y, 6) x = np.tile(x, 6) np.testing.assert_almost_equal( - log_decoding_Log3G10_v2(y), x, decimal=7) + log_decoding_Log3G10_v2(y), x, decimal=7 + ) y = np.reshape(y, (2, 3)) x = np.reshape(x, (2, 3)) np.testing.assert_almost_equal( - log_decoding_Log3G10_v2(y), x, decimal=7) + log_decoding_Log3G10_v2(y), x, decimal=7 + ) y = np.reshape(y, (2, 3, 1)) x = np.reshape(x, (2, 3, 1)) np.testing.assert_almost_equal( - log_decoding_Log3G10_v2(y), x, decimal=7) + log_decoding_Log3G10_v2(y), x, decimal=7 + ) def test_domain_range_scale_log_decoding_Log3G10_v2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_Log3G10_v2` definition domain and range scale support. """ y = 0.333333644207707 x = log_decoding_Log3G10_v2(y) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_Log3G10_v2(y * factor), x * factor, decimal=7) + log_decoding_Log3G10_v2(y * factor), x * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_Log3G10_v2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_Log3G10_v2` definition nan support. """ log_decoding_Log3G10_v2( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) + + +class TestLogEncoding_Log3G10_v3(unittest.TestCase): + """ + Define :func:`colour.models.rgb.transfer_functions.red_log.\ +log_encoding_Log3G10_v3` definition unit tests methods. + """ + + def test_log_encoding_Log3G10_v3(self): + """ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ +log_encoding_Log3G10_v3` definition. + """ + + self.assertAlmostEqual( + log_encoding_Log3G10_v3(-1.0), -15.040773, places=7 + ) + + self.assertAlmostEqual( + log_encoding_Log3G10_v3(0.0), 0.091551487714745, places=7 + ) + + self.assertAlmostEqual( + log_encoding_Log3G10_v3(0.18), 0.333332912025992, places=7 + ) + + def test_n_dimensional_log_encoding_Log3G10_v3(self): + """ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ +log_encoding_Log3G10_v3` definition n-dimensional arrays support. + """ + + x = 0.18 + y = log_encoding_Log3G10_v3(x) + + x = np.tile(x, 6) + y = np.tile(y, 6) + np.testing.assert_almost_equal( + log_encoding_Log3G10_v3(x), y, decimal=7 + ) + + x = np.reshape(x, (2, 3)) + y = np.reshape(y, (2, 3)) + np.testing.assert_almost_equal( + log_encoding_Log3G10_v3(x), y, decimal=7 + ) + + x = np.reshape(x, (2, 3, 1)) + y = np.reshape(y, (2, 3, 1)) + np.testing.assert_almost_equal( + log_encoding_Log3G10_v3(x), y, decimal=7 + ) + + def test_domain_range_scale_log_encoding_Log3G10_v3(self): + """ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ +log_encoding_Log3G10_v3` definition domain and range scale support. + """ + + x = 0.18 + y = log_encoding_Log3G10_v3(x) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + log_encoding_Log3G10_v3(x * factor), y * factor, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_log_encoding_Log3G10_v3(self): + """ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ +log_encoding_Log3G10_v3` definition nan support. + """ + + log_encoding_Log3G10_v3( + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) + + +class TestLogDecoding_Log3G10_v3(unittest.TestCase): + """ + Define :func:`colour.models.rgb.transfer_functions.red_log.\ +log_decoding_Log3G10_v3` definition unit tests methods. + """ + + def test_log_decoding_Log3G10_v3(self): + """ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ +log_decoding_Log3G10_v3` definition. + """ + + self.assertAlmostEqual( + log_decoding_Log3G10_v3(-15.040773), -1.0, places=7 + ) + + self.assertAlmostEqual( + log_decoding_Log3G10_v3(0.091551487714745), 0.0, places=7 + ) + + self.assertAlmostEqual( + log_decoding_Log3G10_v3(0.333332912025992), 0.18, places=7 + ) + + def test_n_dimensional_log_decoding_Log3G10_v3(self): + """ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ +log_decoding_Log3G10_v3` definition n-dimensional arrays support. + """ + + y = 0.333332912025992 + x = log_decoding_Log3G10_v3(y) + + y = np.tile(y, 6) + x = np.tile(x, 6) + np.testing.assert_almost_equal( + log_decoding_Log3G10_v3(y), x, decimal=7 + ) + + y = np.reshape(y, (2, 3)) + x = np.reshape(x, (2, 3)) + np.testing.assert_almost_equal( + log_decoding_Log3G10_v3(y), x, decimal=7 + ) + + y = np.reshape(y, (2, 3, 1)) + x = np.reshape(x, (2, 3, 1)) + np.testing.assert_almost_equal( + log_decoding_Log3G10_v3(y), x, decimal=7 + ) + + def test_domain_range_scale_log_decoding_Log3G10_v3(self): + """ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ +log_decoding_Log3G10_v3` definition domain and range scale support. + """ + + y = 0.333333644207707 + x = log_decoding_Log3G10_v3(y) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + log_decoding_Log3G10_v3(y * factor), x * factor, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_log_decoding_Log3G10_v3(self): + """ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ +log_decoding_Log3G10_v3` definition nan support. + """ + + log_decoding_Log3G10_v3( + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogEncoding_Log3G12(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.red_log.\ + Define :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_Log3G12` definition unit tests methods. """ def test_log_encoding_Log3G12(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_Log3G12` definition. """ self.assertAlmostEqual(log_encoding_Log3G12(0.0), 0.0, places=7) self.assertAlmostEqual( - log_encoding_Log3G12(0.18), 0.333332662015923, places=7) + log_encoding_Log3G12(0.18), 0.333332662015923, places=7 + ) self.assertAlmostEqual( - log_encoding_Log3G12(1.0), 0.469991923234319, places=7) + log_encoding_Log3G12(1.0), 0.469991923234319, places=7 + ) self.assertAlmostEqual( - log_encoding_Log3G12(0.18 * 2 ** 12), 0.999997986792394, places=7) + log_encoding_Log3G12(0.18 * 2**12), 0.999997986792394, places=7 + ) def test_n_dimensional_log_encoding_Log3G12(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_Log3G12` definition n-dimensional arrays support. """ @@ -635,56 +862,61 @@ def test_n_dimensional_log_encoding_Log3G12(self): def test_domain_range_scale_log_encoding_Log3G12(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_Log3G12` definition domain and range scale support. """ x = 0.18 y = log_encoding_Log3G12(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_Log3G12(x * factor), y * factor, decimal=7) + log_encoding_Log3G12(x * factor), y * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_Log3G12(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_encoding_Log3G12` definition nan support. """ log_encoding_Log3G12( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_Log3G12(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.red_log.\ + Define :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_Log3G12` definition unit tests methods. """ def test_log_decoding_Log3G12(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_Log3G12` definition. """ self.assertAlmostEqual(log_decoding_Log3G12(0.0), 0.0, places=7) self.assertAlmostEqual( - log_decoding_Log3G12(0.333332662015923), 0.18, places=7) + log_decoding_Log3G12(0.333332662015923), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_Log3G12(0.469991923234319), 1.0, places=7) + log_decoding_Log3G12(0.469991923234319), 1.0, places=7 + ) self.assertAlmostEqual( - log_decoding_Log3G12(1.0), 737.29848406719, places=7) + log_decoding_Log3G12(1.0), 737.29848406719, places=7 + ) def test_n_dimensional_log_decoding_Log3G12(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_Log3G12` definition n-dimensional arrays support. """ @@ -705,29 +937,31 @@ def test_n_dimensional_log_decoding_Log3G12(self): def test_domain_range_scale_log_decoding_Log3G12(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_Log3G12` definition domain and range scale support. """ y = 0.18 x = log_decoding_Log3G12(y) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_Log3G12(y * factor), x * factor, decimal=7) + log_decoding_Log3G12(y * factor), x * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_Log3G12(self): """ - Tests :func:`colour.models.rgb.transfer_functions.red_log.\ + Test :func:`colour.models.rgb.transfer_functions.red_log.\ log_decoding_Log3G12` definition nan support. """ log_decoding_Log3G12( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_rimm_romm_rgb.py b/colour/models/rgb/transfer_functions/tests/test_rimm_romm_rgb.py index 6ca61afca9..63962e4a0b 100644 --- a/colour/models/rgb/transfer_functions/tests/test_rimm_romm_rgb.py +++ b/colour/models/rgb/transfer_functions/tests/test_rimm_romm_rgb.py @@ -1,60 +1,67 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for +Defines the unit tests for the :mod:`colour.models.rgb.transfer_functions.rimm_romm_rgb` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from colour.models.rgb.transfer_functions import ( - cctf_encoding_ROMMRGB, cctf_decoding_ROMMRGB, cctf_encoding_RIMMRGB, - cctf_decoding_RIMMRGB, log_encoding_ERIMMRGB, log_decoding_ERIMMRGB) + cctf_encoding_ROMMRGB, + cctf_decoding_ROMMRGB, + cctf_encoding_RIMMRGB, + cctf_decoding_RIMMRGB, + log_encoding_ERIMMRGB, + log_decoding_ERIMMRGB, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestCctfEncoding_ROMMRGB', 'TestCctfDecoding_ROMMRGB', - 'TestCctfEncoding_RIMMRGB', 'TestCctfDecoding_RIMMRGB', - 'TestLog_encoding_ERIMMRGB', 'TestLog_decoding_ERIMMRGB' + "TestCctfEncoding_ROMMRGB", + "TestCctfDecoding_ROMMRGB", + "TestCctfEncoding_RIMMRGB", + "TestCctfDecoding_RIMMRGB", + "TestLog_encoding_ERIMMRGB", + "TestLog_decoding_ERIMMRGB", ] class TestCctfEncoding_ROMMRGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Define :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ cctf_encoding_ROMMRGB` definition unit tests methods. """ def test_cctf_encoding_ROMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ cctf_encoding_ROMMRGB` definition. """ self.assertAlmostEqual(cctf_encoding_ROMMRGB(0.0), 0.0, places=7) self.assertAlmostEqual( - cctf_encoding_ROMMRGB(0.18), 0.385711424751138, places=7) + cctf_encoding_ROMMRGB(0.18), 0.385711424751138, places=7 + ) self.assertAlmostEqual(cctf_encoding_ROMMRGB(1.0), 1.0, places=7) self.assertEqual(cctf_encoding_ROMMRGB(0.18, out_int=True), 98) self.assertEqual( - cctf_encoding_ROMMRGB(0.18, bit_depth=12, out_int=True), 1579) + cctf_encoding_ROMMRGB(0.18, bit_depth=12, out_int=True), 1579 + ) def test_n_dimensional_cctf_encoding_ROMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ cctf_encoding_ROMMRGB` definition n-dimensional arrays support. """ @@ -64,60 +71,66 @@ def test_n_dimensional_cctf_encoding_ROMMRGB(self): X = np.tile(X, 6) X_ROMM = np.tile(X_ROMM, 6) np.testing.assert_almost_equal( - cctf_encoding_ROMMRGB(X), X_ROMM, decimal=7) + cctf_encoding_ROMMRGB(X), X_ROMM, decimal=7 + ) X = np.reshape(X, (2, 3)) X_ROMM = np.reshape(X_ROMM, (2, 3)) np.testing.assert_almost_equal( - cctf_encoding_ROMMRGB(X), X_ROMM, decimal=7) + cctf_encoding_ROMMRGB(X), X_ROMM, decimal=7 + ) X = np.reshape(X, (2, 3, 1)) X_ROMM = np.reshape(X_ROMM, (2, 3, 1)) np.testing.assert_almost_equal( - cctf_encoding_ROMMRGB(X), X_ROMM, decimal=7) + cctf_encoding_ROMMRGB(X), X_ROMM, decimal=7 + ) def test_domain_range_scale_cctf_encoding_ROMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ cctf_encoding_ROMMRGB` definition domain and range scale support. """ X = 0.18 X_p = cctf_encoding_ROMMRGB(X) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - cctf_encoding_ROMMRGB(X * factor), X_p * factor, decimal=7) + cctf_encoding_ROMMRGB(X * factor), X_p * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_cctf_encoding_ROMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ cctf_encoding_ROMMRGB` definition nan support. """ cctf_encoding_ROMMRGB( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestCctfDecoding_ROMMRGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb. -cctf_decoding_ROMMRGB` definition unit tests methods. + Define :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb. + cctf_decoding_ROMMRGB` definition unit tests methods. """ def test_cctf_decoding_ROMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ cctf_decoding_ROMMRGB` definition. """ self.assertAlmostEqual(cctf_decoding_ROMMRGB(0.0), 0.0, places=7) self.assertAlmostEqual( - cctf_decoding_ROMMRGB(0.385711424751138), 0.18, places=7) + cctf_decoding_ROMMRGB(0.385711424751138), 0.18, places=7 + ) self.assertAlmostEqual(cctf_decoding_ROMMRGB(1.0), 1.0, places=7) @@ -125,17 +138,19 @@ def test_cctf_decoding_ROMMRGB(self): cctf_decoding_ROMMRGB(98, in_int=True), 0.18, atol=0.001, - rtol=0.001) + rtol=0.001, + ) np.testing.assert_allclose( cctf_decoding_ROMMRGB(1579, bit_depth=12, in_int=True), 0.18, atol=0.001, - rtol=0.001) + rtol=0.001, + ) def test_n_dimensional_cctf_decoding_ROMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ cctf_decoding_ROMMRGB` definition n-dimensional arrays support. """ @@ -145,72 +160,80 @@ def test_n_dimensional_cctf_decoding_ROMMRGB(self): X_p = np.tile(X_p, 6) X = np.tile(X, 6) np.testing.assert_almost_equal( - cctf_decoding_ROMMRGB(X_p), X, decimal=7) + cctf_decoding_ROMMRGB(X_p), X, decimal=7 + ) X_p = np.reshape(X_p, (2, 3)) X = np.reshape(X, (2, 3)) np.testing.assert_almost_equal( - cctf_decoding_ROMMRGB(X_p), X, decimal=7) + cctf_decoding_ROMMRGB(X_p), X, decimal=7 + ) X_p = np.reshape(X_p, (2, 3, 1)) X = np.reshape(X, (2, 3, 1)) np.testing.assert_almost_equal( - cctf_decoding_ROMMRGB(X_p), X, decimal=7) + cctf_decoding_ROMMRGB(X_p), X, decimal=7 + ) def test_domain_range_scale_cctf_decoding_ROMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ cctf_decoding_ROMMRGB` definition domain and range scale support. """ X_p = 0.385711424751138 X = cctf_decoding_ROMMRGB(X_p) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - cctf_decoding_ROMMRGB(X_p * factor), X * factor, decimal=7) + cctf_decoding_ROMMRGB(X_p * factor), X * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_cctf_decoding_ROMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ cctf_decoding_ROMMRGB` definition nan support. """ cctf_decoding_ROMMRGB( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestCctfEncoding_RIMMRGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Define :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ cctf_encoding_RIMMRGB` definition unit tests methods. """ def test_cctf_encoding_RIMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ cctf_encoding_RIMMRGB` definition. """ self.assertAlmostEqual(cctf_encoding_RIMMRGB(0.0), 0.0, places=7) self.assertAlmostEqual( - cctf_encoding_RIMMRGB(0.18), 0.291673732475746, places=7) + cctf_encoding_RIMMRGB(0.18), 0.291673732475746, places=7 + ) self.assertAlmostEqual( - cctf_encoding_RIMMRGB(1.0), 0.713125234297525, places=7) + cctf_encoding_RIMMRGB(1.0), 0.713125234297525, places=7 + ) self.assertEqual(cctf_encoding_RIMMRGB(0.18, out_int=True), 74) self.assertEqual( - cctf_encoding_RIMMRGB(0.18, bit_depth=12, out_int=True), 1194) + cctf_encoding_RIMMRGB(0.18, bit_depth=12, out_int=True), 1194 + ) def test_n_dimensional_cctf_encoding_RIMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ cctf_encoding_RIMMRGB` definition n-dimensional arrays support. """ @@ -220,79 +243,88 @@ def test_n_dimensional_cctf_encoding_RIMMRGB(self): X = np.tile(X, 6) X_p = np.tile(X_p, 6) np.testing.assert_almost_equal( - cctf_encoding_RIMMRGB(X), X_p, decimal=7) + cctf_encoding_RIMMRGB(X), X_p, decimal=7 + ) X = np.reshape(X, (2, 3)) X_p = np.reshape(X_p, (2, 3)) np.testing.assert_almost_equal( - cctf_encoding_RIMMRGB(X), X_p, decimal=7) + cctf_encoding_RIMMRGB(X), X_p, decimal=7 + ) X = np.reshape(X, (2, 3, 1)) X_p = np.reshape(X_p, (2, 3, 1)) np.testing.assert_almost_equal( - cctf_encoding_RIMMRGB(X), X_p, decimal=7) + cctf_encoding_RIMMRGB(X), X_p, decimal=7 + ) def test_domain_range_scale_cctf_encoding_RIMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ cctf_encoding_RIMMRGB` definition domain and range scale support. """ X = 0.18 X_p = cctf_encoding_RIMMRGB(X) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - cctf_encoding_RIMMRGB(X * factor), X_p * factor, decimal=7) + cctf_encoding_RIMMRGB(X * factor), X_p * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_cctf_encoding_RIMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ cctf_encoding_RIMMRGB` definition nan support. """ cctf_encoding_RIMMRGB( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestCctfDecoding_RIMMRGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb. -cctf_decoding_RIMMRGB` definition unit tests methods. + Define :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb. + cctf_decoding_RIMMRGB` definition unit tests methods. """ def test_cctf_decoding_RIMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ cctf_decoding_RIMMRGB` definition. """ self.assertAlmostEqual(cctf_decoding_RIMMRGB(0.0), 0.0, places=7) self.assertAlmostEqual( - cctf_decoding_RIMMRGB(0.291673732475746), 0.18, places=7) + cctf_decoding_RIMMRGB(0.291673732475746), 0.18, places=7 + ) self.assertAlmostEqual( - cctf_decoding_RIMMRGB(0.713125234297525), 1.0, places=7) + cctf_decoding_RIMMRGB(0.713125234297525), 1.0, places=7 + ) np.testing.assert_allclose( cctf_decoding_RIMMRGB(74, in_int=True), 0.18, atol=0.005, - rtol=0.005) + rtol=0.005, + ) np.testing.assert_allclose( cctf_decoding_RIMMRGB(1194, bit_depth=12, in_int=True), 0.18, atol=0.005, - rtol=0.005) + rtol=0.005, + ) def test_n_dimensional_cctf_decoding_RIMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ cctf_decoding_RIMMRGB` definition n-dimensional arrays support. """ @@ -302,72 +334,80 @@ def test_n_dimensional_cctf_decoding_RIMMRGB(self): X_p = np.tile(X_p, 6) X = np.tile(X, 6) np.testing.assert_almost_equal( - cctf_decoding_RIMMRGB(X_p), X, decimal=7) + cctf_decoding_RIMMRGB(X_p), X, decimal=7 + ) X_p = np.reshape(X_p, (2, 3)) X = np.reshape(X, (2, 3)) np.testing.assert_almost_equal( - cctf_decoding_RIMMRGB(X_p), X, decimal=7) + cctf_decoding_RIMMRGB(X_p), X, decimal=7 + ) X_p = np.reshape(X_p, (2, 3, 1)) X = np.reshape(X, (2, 3, 1)) np.testing.assert_almost_equal( - cctf_decoding_RIMMRGB(X_p), X, decimal=7) + cctf_decoding_RIMMRGB(X_p), X, decimal=7 + ) def test_domain_range_scale_cctf_decoding_RIMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ cctf_decoding_RIMMRGB` definition domain and range scale support. """ X_p = 0.291673732475746 X = cctf_decoding_RIMMRGB(X_p) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - cctf_decoding_RIMMRGB(X_p * factor), X * factor, decimal=7) + cctf_decoding_RIMMRGB(X_p * factor), X * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_cctf_decoding_RIMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ cctf_decoding_RIMMRGB` definition nan support. """ cctf_decoding_RIMMRGB( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLog_encoding_ERIMMRGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Define :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ log_encoding_ERIMMRGB` definition unit tests methods. """ def test_log_encoding_ERIMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ log_encoding_ERIMMRGB` definition. """ self.assertAlmostEqual(log_encoding_ERIMMRGB(0.0), 0.0, places=7) self.assertAlmostEqual( - log_encoding_ERIMMRGB(0.18), 0.410052389492129, places=7) + log_encoding_ERIMMRGB(0.18), 0.410052389492129, places=7 + ) self.assertAlmostEqual( - log_encoding_ERIMMRGB(1.0), 0.545458327405113, places=7) + log_encoding_ERIMMRGB(1.0), 0.545458327405113, places=7 + ) self.assertEqual(log_encoding_ERIMMRGB(0.18, out_int=True), 105) self.assertEqual( - log_encoding_ERIMMRGB(0.18, bit_depth=12, out_int=True), 1679) + log_encoding_ERIMMRGB(0.18, bit_depth=12, out_int=True), 1679 + ) def test_n_dimensional_log_encoding_ERIMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ log_encoding_ERIMMRGB` definition n-dimensional arrays support. """ @@ -377,79 +417,88 @@ def test_n_dimensional_log_encoding_ERIMMRGB(self): X = np.tile(X, 6) X_p = np.tile(X_p, 6) np.testing.assert_almost_equal( - log_encoding_ERIMMRGB(X), X_p, decimal=7) + log_encoding_ERIMMRGB(X), X_p, decimal=7 + ) X = np.reshape(X, (2, 3)) X_p = np.reshape(X_p, (2, 3)) np.testing.assert_almost_equal( - log_encoding_ERIMMRGB(X), X_p, decimal=7) + log_encoding_ERIMMRGB(X), X_p, decimal=7 + ) X = np.reshape(X, (2, 3, 1)) X_p = np.reshape(X_p, (2, 3, 1)) np.testing.assert_almost_equal( - log_encoding_ERIMMRGB(X), X_p, decimal=7) + log_encoding_ERIMMRGB(X), X_p, decimal=7 + ) def test_domain_range_scale_log_encoding_ERIMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ log_encoding_ERIMMRGB` definition domain and range scale support. """ X = 0.18 X_p = log_encoding_ERIMMRGB(X) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_ERIMMRGB(X * factor), X_p * factor, decimal=7) + log_encoding_ERIMMRGB(X * factor), X_p * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_ERIMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ log_encoding_ERIMMRGB` definition nan support. """ log_encoding_ERIMMRGB( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLog_decoding_ERIMMRGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb. -log_decoding_ERIMMRGB` definition unit tests methods. + Define :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb. + log_decoding_ERIMMRGB` definition unit tests methods. """ def test_log_decoding_ERIMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ log_decoding_ERIMMRGB` definition. """ self.assertAlmostEqual(log_decoding_ERIMMRGB(0.0), 0.0, places=7) self.assertAlmostEqual( - log_decoding_ERIMMRGB(0.410052389492129), 0.18, places=7) + log_decoding_ERIMMRGB(0.410052389492129), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_ERIMMRGB(0.545458327405113), 1.0, places=7) + log_decoding_ERIMMRGB(0.545458327405113), 1.0, places=7 + ) np.testing.assert_allclose( log_decoding_ERIMMRGB(105, in_int=True), 0.18, atol=0.005, - rtol=0.005) + rtol=0.005, + ) np.testing.assert_allclose( log_decoding_ERIMMRGB(1679, bit_depth=12, in_int=True), 0.18, atol=0.005, - rtol=0.005) + rtol=0.005, + ) def test_n_dimensional_log_decoding_ERIMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ log_decoding_ERIMMRGB` definition n-dimensional arrays support. """ @@ -459,43 +508,48 @@ def test_n_dimensional_log_decoding_ERIMMRGB(self): X_p = np.tile(X_p, 6) X = np.tile(X, 6) np.testing.assert_almost_equal( - log_decoding_ERIMMRGB(X_p), X, decimal=7) + log_decoding_ERIMMRGB(X_p), X, decimal=7 + ) X_p = np.reshape(X_p, (2, 3)) X = np.reshape(X, (2, 3)) np.testing.assert_almost_equal( - log_decoding_ERIMMRGB(X_p), X, decimal=7) + log_decoding_ERIMMRGB(X_p), X, decimal=7 + ) X_p = np.reshape(X_p, (2, 3, 1)) X = np.reshape(X, (2, 3, 1)) np.testing.assert_almost_equal( - log_decoding_ERIMMRGB(X_p), X, decimal=7) + log_decoding_ERIMMRGB(X_p), X, decimal=7 + ) def test_domain_range_scale_log_decoding_ERIMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ log_decoding_ERIMMRGB` definition domain and range scale support. """ X_p = 0.410052389492129 X = log_decoding_ERIMMRGB(X_p) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_ERIMMRGB(X_p * factor), X * factor, decimal=7) + log_decoding_ERIMMRGB(X_p * factor), X * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_ERIMMRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ + Test :func:`colour.models.rgb.transfer_functions.rimm_romm_rgb.\ log_decoding_ERIMMRGB` definition nan support. """ log_decoding_ERIMMRGB( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_smpte_240m.py b/colour/models/rgb/transfer_functions/tests/test_smpte_240m.py index 0e762172d4..ba60c4188e 100644 --- a/colour/models/rgb/transfer_functions/tests/test_smpte_240m.py +++ b/colour/models/rgb/transfer_functions/tests/test_smpte_240m.py @@ -1,52 +1,54 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.smpte_240m` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.smpte_240m` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from colour.models.rgb.transfer_functions import oetf_SMPTE240M, eotf_SMPTE240M from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestOetf_SMPTE240M', 'TestEotf_SMPTE240M'] +__all__ = [ + "TestOetf_SMPTE240M", + "TestEotf_SMPTE240M", +] class TestOetf_SMPTE240M(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.smpte_240m.\ + Define :func:`colour.models.rgb.transfer_functions.smpte_240m.\ oetf_SMPTE240M` definition unit tests methods. """ def test_oetf_SMPTE240M(self): """ - Tests :func:`colour.models.rgb.transfer_functions.smpte_240m.\ + Test :func:`colour.models.rgb.transfer_functions.smpte_240m.\ oetf_SMPTE240M` definition. """ self.assertAlmostEqual(oetf_SMPTE240M(0.0), 0.0, places=7) self.assertAlmostEqual( - oetf_SMPTE240M(0.02), 0.080000000000000, places=7) + oetf_SMPTE240M(0.02), 0.080000000000000, places=7 + ) self.assertAlmostEqual( - oetf_SMPTE240M(0.18), 0.402285796753870, places=7) + oetf_SMPTE240M(0.18), 0.402285796753870, places=7 + ) self.assertAlmostEqual(oetf_SMPTE240M(1.0), 1.0, places=7) def test_n_dimensional_oetf_SMPTE240M(self): """ - Tests :func:`colour.models.rgb.transfer_functions.smpte_240m.\ + Test :func:`colour.models.rgb.transfer_functions.smpte_240m.\ oetf_SMPTE240M` definition n-dimensional arrays support. """ @@ -67,23 +69,24 @@ def test_n_dimensional_oetf_SMPTE240M(self): def test_domain_range_scale_oetf_SMPTE240M(self): """ - Tests :func:`colour.models.rgb.transfer_functions.smpte_240m.\ + Test :func:`colour.models.rgb.transfer_functions.smpte_240m.\ oetf_SMPTE240M` definition domain and range scale support. """ L_c = 0.18 V_c = oetf_SMPTE240M(L_c) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - oetf_SMPTE240M(L_c * factor), V_c * factor, decimal=7) + oetf_SMPTE240M(L_c * factor), V_c * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_oetf_SMPTE240M(self): """ - Tests :func:`colour.models.rgb.transfer_functions.smpte_240m.\ + Test :func:`colour.models.rgb.transfer_functions.smpte_240m.\ oetf_SMPTE240M` definition nan support. """ @@ -92,29 +95,31 @@ def test_nan_oetf_SMPTE240M(self): class TestEotf_SMPTE240M(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.smpte_240m.\ + Define :func:`colour.models.rgb.transfer_functions.smpte_240m.\ eotf_SMPTE240M` definition unit tests methods. """ def test_eotf_SMPTE240M(self): """ - Tests :func:`colour.models.rgb.transfer_functions.smpte_240m.\ + Test :func:`colour.models.rgb.transfer_functions.smpte_240m.\ eotf_SMPTE240M` definition. """ self.assertAlmostEqual(eotf_SMPTE240M(0.0), 0.0, places=7) self.assertAlmostEqual( - eotf_SMPTE240M(0.080000000000000), 0.02, places=7) + eotf_SMPTE240M(0.080000000000000), 0.02, places=7 + ) self.assertAlmostEqual( - eotf_SMPTE240M(0.402285796753870), 0.18, places=7) + eotf_SMPTE240M(0.402285796753870), 0.18, places=7 + ) self.assertAlmostEqual(eotf_SMPTE240M(1.0), 1.0, places=7) def test_n_dimensional_eotf_SMPTE240M(self): """ - Tests :func:`colour.models.rgb.transfer_functions.smpte_240m.\ + Test :func:`colour.models.rgb.transfer_functions.smpte_240m.\ eotf_SMPTE240M` definition n-dimensional arrays support. """ @@ -135,28 +140,29 @@ def test_n_dimensional_eotf_SMPTE240M(self): def test_domain_range_scale_eotf_SMPTE240M(self): """ - Tests :func:`colour.models.rgb.transfer_functions.smpte_240m.\ + Test :func:`colour.models.rgb.transfer_functions.smpte_240m.\ eotf_SMPTE240M` definition domain and range scale support. """ V_r = 0.402285796753870 L_r = eotf_SMPTE240M(V_r) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - eotf_SMPTE240M(V_r * factor), L_r * factor, decimal=7) + eotf_SMPTE240M(V_r * factor), L_r * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_eotf_SMPTE240M(self): """ - Tests :func:`colour.models.rgb.transfer_functions.smpte_240m.\ + Test :func:`colour.models.rgb.transfer_functions.smpte_240m.\ eotf_SMPTE240M` definition nan support. """ eotf_SMPTE240M(np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_sony_slog.py b/colour/models/rgb/transfer_functions/tests/test_sony_slog.py index 56200e541c..1806879158 100644 --- a/colour/models/rgb/transfer_functions/tests/test_sony_slog.py +++ b/colour/models/rgb/transfer_functions/tests/test_sony_slog.py @@ -1,71 +1,79 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.sony_slog` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.sony_slog` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from colour.models.rgb.transfer_functions import ( - log_encoding_SLog, log_decoding_SLog, log_encoding_SLog2, - log_decoding_SLog2, log_encoding_SLog3, log_decoding_SLog3) + log_encoding_SLog, + log_decoding_SLog, + log_encoding_SLog2, + log_decoding_SLog2, + log_encoding_SLog3, + log_decoding_SLog3, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestLogEncoding_SLog', - 'TestLogDecoding_SLog', - 'TestLogEncoding_SLog2', - 'TestLogDecoding_SLog2', - 'TestLogEncoding_SLog3', - 'TestLogDecoding_SLog3', + "TestLogEncoding_SLog", + "TestLogDecoding_SLog", + "TestLogEncoding_SLog2", + "TestLogDecoding_SLog2", + "TestLogEncoding_SLog3", + "TestLogDecoding_SLog3", ] class TestLogEncoding_SLog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Define :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_encoding_SLog` definition unit tests methods. """ def test_log_encoding_SLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_encoding_SLog` definition. """ self.assertAlmostEqual( - log_encoding_SLog(0.0), 0.088251291513446, places=7) + log_encoding_SLog(0.0), 0.088251291513446, places=7 + ) self.assertAlmostEqual( - log_encoding_SLog(0.18), 0.384970815928670, places=7) + log_encoding_SLog(0.18), 0.384970815928670, places=7 + ) self.assertAlmostEqual( - log_encoding_SLog(0.18, 12), 0.384688786026891, places=7) + log_encoding_SLog(0.18, 12), 0.384688786026891, places=7 + ) self.assertAlmostEqual( - log_encoding_SLog(0.18, 10, False), 0.376512722254600, places=7) + log_encoding_SLog(0.18, 10, False), 0.376512722254600, places=7 + ) self.assertAlmostEqual( log_encoding_SLog(0.18, 10, False, False), 0.359987846422154, - places=7) + places=7, + ) self.assertAlmostEqual( - log_encoding_SLog(1.0), 0.638551684622532, places=7) + log_encoding_SLog(1.0), 0.638551684622532, places=7 + ) def test_n_dimensional_log_encoding_SLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_encoding_SLog` definition n-dimensional arrays support. """ @@ -86,23 +94,24 @@ def test_n_dimensional_log_encoding_SLog(self): def test_domain_range_scale_log_encoding_SLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_encoding_SLog` definition domain and range scale support. """ x = 0.18 y = log_encoding_SLog(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_SLog(x * factor), y * factor, decimal=7) + log_encoding_SLog(x * factor), y * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_SLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_encoding_SLog` definition nan support. """ @@ -111,39 +120,45 @@ def test_nan_log_encoding_SLog(self): class TestLogDecoding_SLog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Define :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_decoding_SLog` definition unit tests methods. """ def test_log_decoding_SLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_decoding_SLog` definition. """ self.assertAlmostEqual( - log_decoding_SLog(0.088251291513446), 0.0, places=7) + log_decoding_SLog(0.088251291513446), 0.0, places=7 + ) self.assertAlmostEqual( - log_decoding_SLog(0.384970815928670), 0.18, places=7) + log_decoding_SLog(0.384970815928670), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_SLog(0.384688786026891, 12), 0.18, places=7) + log_decoding_SLog(0.384688786026891, 12), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_SLog(0.376512722254600, 10, False), 0.18, places=7) + log_decoding_SLog(0.376512722254600, 10, False), 0.18, places=7 + ) self.assertAlmostEqual( log_decoding_SLog(0.359987846422154, 10, False, False), 0.18, - places=7) + places=7, + ) self.assertAlmostEqual( - log_decoding_SLog(0.638551684622532), 1.0, places=7) + log_decoding_SLog(0.638551684622532), 1.0, places=7 + ) def test_n_dimensional_log_decoding_SLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_decoding_SLog` definition n-dimensional arrays support. """ @@ -164,23 +179,24 @@ def test_n_dimensional_log_decoding_SLog(self): def test_domain_range_scale_log_decoding_SLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_decoding_SLog` definition domain and range scale support. """ y = 0.384970815928670 x = log_decoding_SLog(y) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_SLog(y * factor), x * factor, decimal=7) + log_decoding_SLog(y * factor), x * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_SLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_decoding_SLog` definition nan support. """ @@ -189,39 +205,45 @@ def test_nan_log_decoding_SLog(self): class TestLogEncoding_SLog2(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Define :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_encoding_SLog2` definition unit tests methods. """ def test_log_encoding_SLog2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_encoding_SLog2` definition. """ self.assertAlmostEqual( - log_encoding_SLog2(0.0), 0.088251291513446, places=7) + log_encoding_SLog2(0.0), 0.088251291513446, places=7 + ) self.assertAlmostEqual( - log_encoding_SLog2(0.18), 0.339532524633774, places=7) + log_encoding_SLog2(0.18), 0.339532524633774, places=7 + ) self.assertAlmostEqual( - log_encoding_SLog2(0.18, 12), 0.339283782857486, places=7) + log_encoding_SLog2(0.18, 12), 0.339283782857486, places=7 + ) self.assertAlmostEqual( - log_encoding_SLog2(0.18, 10, False), 0.323449512215013, places=7) + log_encoding_SLog2(0.18, 10, False), 0.323449512215013, places=7 + ) self.assertAlmostEqual( log_encoding_SLog2(0.18, 10, False, False), 0.307980741258647, - places=7) + places=7, + ) self.assertAlmostEqual( - log_encoding_SLog2(1.0), 0.585091059564112, places=7) + log_encoding_SLog2(1.0), 0.585091059564112, places=7 + ) def test_n_dimensional_log_encoding_SLog2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_encoding_SLog2` definition n-dimensional arrays support. """ @@ -242,23 +264,24 @@ def test_n_dimensional_log_encoding_SLog2(self): def test_domain_range_scale_log_encoding_SLog2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_encoding_SLog2` definition domain and range scale support. """ x = 0.18 y = log_encoding_SLog2(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_SLog2(x * factor), y * factor, decimal=7) + log_encoding_SLog2(x * factor), y * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_SLog2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_encoding_SLog2` definition nan support. """ @@ -267,39 +290,45 @@ def test_nan_log_encoding_SLog2(self): class TestLogDecoding_SLog2(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Define :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_decoding_SLog2` definition unit tests methods. """ def test_log_decoding_SLog2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_decoding_SLog2` definition. """ self.assertAlmostEqual( - log_decoding_SLog2(0.088251291513446), 0.0, places=7) + log_decoding_SLog2(0.088251291513446), 0.0, places=7 + ) self.assertAlmostEqual( - log_decoding_SLog2(0.339532524633774), 0.18, places=7) + log_decoding_SLog2(0.339532524633774), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_SLog2(0.339283782857486, 12), 0.18, places=7) + log_decoding_SLog2(0.339283782857486, 12), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_SLog2(0.323449512215013, 10, False), 0.18, places=7) + log_decoding_SLog2(0.323449512215013, 10, False), 0.18, places=7 + ) self.assertAlmostEqual( log_decoding_SLog2(0.307980741258647, 10, False, False), 0.18, - places=7) + places=7, + ) self.assertAlmostEqual( - log_decoding_SLog2(0.585091059564112), 1.0, places=7) + log_decoding_SLog2(0.585091059564112), 1.0, places=7 + ) def test_n_dimensional_log_decoding_SLog2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_decoding_SLog2` definition n-dimensional arrays support. """ @@ -320,23 +349,24 @@ def test_n_dimensional_log_decoding_SLog2(self): def test_domain_range_scale_log_decoding_SLog2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_decoding_SLog2` definition domain and range scale support. """ y = 0.339532524633774 x = log_decoding_SLog2(y) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_SLog2(y * factor), x * factor, decimal=7) + log_decoding_SLog2(y * factor), x * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_SLog2(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_decoding_SLog2` definition nan support. """ @@ -345,39 +375,45 @@ def test_nan_log_decoding_SLog2(self): class TestLogEncoding_SLog3(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Define :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_encoding_SLog3` definition unit tests methods. """ def test_log_encoding_SLog3(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_encoding_SLog3` definition. """ self.assertAlmostEqual( - log_encoding_SLog3(0.0), 0.092864125122190, places=7) + log_encoding_SLog3(0.0), 0.092864125122190, places=7 + ) self.assertAlmostEqual( - log_encoding_SLog3(0.18), 0.41055718475073, places=7) + log_encoding_SLog3(0.18), 0.41055718475073, places=7 + ) self.assertAlmostEqual( - log_encoding_SLog3(0.18, 12), 0.410557184750733, places=7) + log_encoding_SLog3(0.18, 12), 0.410557184750733, places=7 + ) self.assertAlmostEqual( - log_encoding_SLog3(0.18, 10, False), 0.406392694063927, places=7) + log_encoding_SLog3(0.18, 10, False), 0.406392694063927, places=7 + ) self.assertAlmostEqual( log_encoding_SLog3(0.18, 10, False, False), 0.393489294768447, - places=7) + places=7, + ) self.assertAlmostEqual( - log_encoding_SLog3(1.0), 0.596027343690123, places=7) + log_encoding_SLog3(1.0), 0.596027343690123, places=7 + ) def test_n_dimensional_log_encoding_SLog3(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_encoding_SLog3` definition n-dimensional arrays support. """ @@ -398,23 +434,24 @@ def test_n_dimensional_log_encoding_SLog3(self): def test_domain_range_scale_log_encoding_SLog3(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_encoding_SLog3` definition domain and range scale support. """ x = 0.18 y = log_encoding_SLog3(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_SLog3(x * factor), y * factor, decimal=7) + log_encoding_SLog3(x * factor), y * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_SLog3(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_encoding_SLog3` definition nan support. """ @@ -423,39 +460,45 @@ def test_nan_log_encoding_SLog3(self): class TestLogDecoding_SLog3(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Define :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_decoding_SLog3` definition unit tests methods. """ def test_log_decoding_SLog3(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_decoding_SLog3` definition. """ self.assertAlmostEqual( - log_decoding_SLog3(0.092864125122190), 0.0, places=7) + log_decoding_SLog3(0.092864125122190), 0.0, places=7 + ) self.assertAlmostEqual( - log_decoding_SLog3(0.41055718475073), 0.18, places=7) + log_decoding_SLog3(0.41055718475073), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_SLog3(0.410557184750733, 12), 0.18, places=7) + log_decoding_SLog3(0.410557184750733, 12), 0.18, places=7 + ) self.assertAlmostEqual( - log_decoding_SLog3(0.406392694063927, 10, False), 0.18, places=7) + log_decoding_SLog3(0.406392694063927, 10, False), 0.18, places=7 + ) self.assertAlmostEqual( log_decoding_SLog3(0.393489294768447, 10, False, False), 0.18, - places=7) + places=7, + ) self.assertAlmostEqual( - log_decoding_SLog3(0.596027343690123), 1.0, places=7) + log_decoding_SLog3(0.596027343690123), 1.0, places=7 + ) def test_n_dimensional_log_decoding_SLog3(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_decoding_SLog3` definition n-dimensional arrays support. """ @@ -476,28 +519,29 @@ def test_n_dimensional_log_decoding_SLog3(self): def test_domain_range_scale_log_decoding_SLog3(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_decoding_SLog3` definition domain and range scale support. """ y = 0.41055718475073 x = log_decoding_SLog3(y) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_SLog3(y * factor), x * factor, decimal=7) + log_decoding_SLog3(y * factor), x * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_SLog3(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sony_slog.\ + Test :func:`colour.models.rgb.transfer_functions.sony_slog.\ log_decoding_SLog3` definition nan support. """ log_decoding_SLog3(np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_srgb.py b/colour/models/rgb/transfer_functions/tests/test_srgb.py index e57f2ca8ac..55b1498dda 100644 --- a/colour/models/rgb/transfer_functions/tests/test_srgb.py +++ b/colour/models/rgb/transfer_functions/tests/test_srgb.py @@ -1,49 +1,50 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.sRGB` +Defines the unit tests for the :mod:`colour.models.rgb.transfer_functions.sRGB` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from colour.models.rgb.transfer_functions import eotf_inverse_sRGB, eotf_sRGB from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestEotf_inverse_sRGB', 'TestEotf_sRGB'] +__all__ = [ + "TestEotf_inverse_sRGB", + "TestEotf_sRGB", +] class TestEotf_inverse_sRGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.sRGB.eotf_inverse_sRGB` + Define :func:`colour.models.rgb.transfer_functions.sRGB.eotf_inverse_sRGB` definition unit tests methods. """ def test_eotf_inverse_sRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sRGB.\ + Test :func:`colour.models.rgb.transfer_functions.sRGB.\ eotf_inverse_sRGB` definition. """ self.assertAlmostEqual(eotf_inverse_sRGB(0.0), 0.0, places=7) self.assertAlmostEqual( - eotf_inverse_sRGB(0.18), 0.461356129500442, places=7) + eotf_inverse_sRGB(0.18), 0.461356129500442, places=7 + ) self.assertAlmostEqual(eotf_inverse_sRGB(1.0), 1.0, places=7) def test_n_dimensional_eotf_inverse_sRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sRGB.\ + Test :func:`colour.models.rgb.transfer_functions.sRGB.\ eotf_inverse_sRGB` definition n-dimensional arrays support. """ @@ -64,23 +65,24 @@ def test_n_dimensional_eotf_inverse_sRGB(self): def test_domain_range_scale_eotf_inverse_sRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sRGB.\ + Test :func:`colour.models.rgb.transfer_functions.sRGB.\ eotf_inverse_sRGB` definition domain and range scale support. """ L = 0.18 V = eotf_inverse_sRGB(L) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - eotf_inverse_sRGB(L * factor), V * factor, decimal=7) + eotf_inverse_sRGB(L * factor), V * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_eotf_inverse_sRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sRGB.\ + Test :func:`colour.models.rgb.transfer_functions.sRGB.\ eotf_inverse_sRGB` definition nan support. """ @@ -89,13 +91,13 @@ def test_nan_eotf_inverse_sRGB(self): class TestEotf_sRGB(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.sRGB.eotf_sRGB` + Define :func:`colour.models.rgb.transfer_functions.sRGB.eotf_sRGB` definition unit tests methods. """ def test_eotf_sRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sRGB.\ + Test :func:`colour.models.rgb.transfer_functions.sRGB.\ eotf_sRGB` definition. """ @@ -107,7 +109,7 @@ def test_eotf_sRGB(self): def test_n_dimensional_eotf_sRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sRGB.\ + Test :func:`colour.models.rgb.transfer_functions.sRGB.\ eotf_sRGB` definition n-dimensional arrays support. """ @@ -128,28 +130,29 @@ def test_n_dimensional_eotf_sRGB(self): def test_domain_range_scale_eotf_sRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sRGB.\ + Test :func:`colour.models.rgb.transfer_functions.sRGB.\ eotf_sRGB` definition domain and range scale support. """ V = 0.461356129500442 L = eotf_sRGB(V) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - eotf_sRGB(V * factor), L * factor, decimal=7) + eotf_sRGB(V * factor), L * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_eotf_sRGB(self): """ - Tests :func:`colour.models.rgb.transfer_functions.sRGB.\ + Test :func:`colour.models.rgb.transfer_functions.sRGB.\ eotf_sRGB` definition nan support. """ eotf_sRGB(np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_st_2084.py b/colour/models/rgb/transfer_functions/tests/test_st_2084.py index 09cf6cd9c8..04fb2e757a 100644 --- a/colour/models/rgb/transfer_functions/tests/test_st_2084.py +++ b/colour/models/rgb/transfer_functions/tests/test_st_2084.py @@ -1,54 +1,59 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.st_2084` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.st_2084` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.models.rgb.transfer_functions import (eotf_inverse_ST2084, - eotf_ST2084) +from colour.models.rgb.transfer_functions import ( + eotf_inverse_ST2084, + eotf_ST2084, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestEotf_inverse_ST2084', 'TestEotf_ST2084'] +__all__ = [ + "TestEotf_inverse_ST2084", + "TestEotf_ST2084", +] class TestEotf_inverse_ST2084(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.st_2084.\ + Define :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_inverse_ST2084` definition unit tests methods. """ def test_eotf_inverse_ST2084(self): """ - Tests :func:`colour.models.rgb.transfer_functions.st_2084.\ + Test :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_inverse_ST2084` definition. """ self.assertAlmostEqual( - eotf_inverse_ST2084(0.0), 0.000000730955903, places=7) + eotf_inverse_ST2084(0.0), 0.000000730955903, places=7 + ) self.assertAlmostEqual( - eotf_inverse_ST2084(100), 0.508078421517399, places=7) + eotf_inverse_ST2084(100), 0.508078421517399, places=7 + ) self.assertAlmostEqual( - eotf_inverse_ST2084(400), 0.652578597563067, places=7) + eotf_inverse_ST2084(400), 0.652578597563067, places=7 + ) self.assertAlmostEqual(eotf_inverse_ST2084(5000, 5000), 1.0, places=7) def test_n_dimensional_eotf_inverse_ST2084(self): """ - Tests :func:`colour.models.rgb.transfer_functions.st_2084.\ + Test :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_inverse_ST2084` definition n-dimensional arrays support. """ @@ -69,39 +74,41 @@ def test_n_dimensional_eotf_inverse_ST2084(self): def test_domain_range_scale_eotf_inverse_ST2084(self): """ - Tests :func:`colour.models.rgb.transfer_functions.st_2084.\ + Test :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_inverse_ST2084` definition domain and range scale support. """ C = 100 N = eotf_inverse_ST2084(C) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - eotf_inverse_ST2084(C * factor), N * factor, decimal=7) + eotf_inverse_ST2084(C * factor), N * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_eotf_inverse_ST2084(self): """ - Tests :func:`colour.models.rgb.transfer_functions.st_2084.\ + Test :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_inverse_ST2084` definition nan support. """ eotf_inverse_ST2084( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestEotf_ST2084(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.st_2084.eotf_ST2084` + Define :func:`colour.models.rgb.transfer_functions.st_2084.eotf_ST2084` definition unit tests methods. """ def test_eotf_ST2084(self): """ - Tests :func:`colour.models.rgb.transfer_functions.st_2084.\ + Test :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_ST2084` definition. """ @@ -115,7 +122,7 @@ def test_eotf_ST2084(self): def test_n_dimensional_eotf_ST2084(self): """ - Tests :func:`colour.models.rgb.transfer_functions.st_2084.\ + Test :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_ST2084` definition n-dimensional arrays support. """ @@ -136,28 +143,29 @@ def test_n_dimensional_eotf_ST2084(self): def test_domain_range_scale_eotf_ST2084(self): """ - Tests :func:`colour.models.rgb.transfer_functions.st_2084.\ + Test :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_ST2084` definition domain and range scale support. """ N = 0.508078421517399 C = eotf_ST2084(N) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - eotf_ST2084(N * factor), C * factor, decimal=7) + eotf_ST2084(N * factor), C * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_eotf_ST2084(self): """ - Tests :func:`colour.models.rgb.transfer_functions.st_2084.\ + Test :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_ST2084` definition nan support. """ eotf_ST2084(np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/tests/test_viper_log.py b/colour/models/rgb/transfer_functions/tests/test_viper_log.py index 703a387c2f..a4f557a5ce 100644 --- a/colour/models/rgb/transfer_functions/tests/test_viper_log.py +++ b/colour/models/rgb/transfer_functions/tests/test_viper_log.py @@ -1,50 +1,53 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.models.rgb.transfer_functions.viper_log` -module. +Defines the unit tests for the +:mod:`colour.models.rgb.transfer_functions.viper_log` module. """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.models.rgb.transfer_functions import (log_encoding_ViperLog, - log_decoding_ViperLog) +from colour.models.rgb.transfer_functions import ( + log_encoding_ViperLog, + log_decoding_ViperLog, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestLogEncoding_ViperLog', 'TestLogDecoding_ViperLog'] +__all__ = [ + "TestLogEncoding_ViperLog", + "TestLogDecoding_ViperLog", +] class TestLogEncoding_ViperLog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.viper_log.\ + Define :func:`colour.models.rgb.transfer_functions.viper_log.\ log_encoding_ViperLog` definition unit tests methods. """ def test_log_encoding_ViperLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.viper_log.\ + Test :func:`colour.models.rgb.transfer_functions.viper_log.\ log_encoding_ViperLog` definition. """ self.assertAlmostEqual(log_encoding_ViperLog(0.0), -np.inf, places=7) self.assertAlmostEqual( - log_encoding_ViperLog(0.18), 0.636008067010413, places=7) + log_encoding_ViperLog(0.18), 0.636008067010413, places=7 + ) self.assertAlmostEqual(log_encoding_ViperLog(1.0), 1.0, places=7) def test_n_dimensional_log_encoding_ViperLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.viper_log.\ + Test :func:`colour.models.rgb.transfer_functions.viper_log.\ log_encoding_ViperLog` definition n-dimensional arrays support. """ @@ -65,52 +68,55 @@ def test_n_dimensional_log_encoding_ViperLog(self): def test_domain_range_scale_log_encoding_ViperLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.viper_log.\ + Test :func:`colour.models.rgb.transfer_functions.viper_log.\ log_encoding_ViperLog` definition domain and range scale support. """ x = 0.18 y = log_encoding_ViperLog(x) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_encoding_ViperLog(x * factor), y * factor, decimal=7) + log_encoding_ViperLog(x * factor), y * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_encoding_ViperLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.viper_log.\ + Test :func:`colour.models.rgb.transfer_functions.viper_log.\ log_encoding_ViperLog` definition nan support. """ log_encoding_ViperLog( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestLogDecoding_ViperLog(unittest.TestCase): """ - Defines :func:`colour.models.rgb.transfer_functions.viper_log.\ + Define :func:`colour.models.rgb.transfer_functions.viper_log.\ log_decoding_ViperLog` definition unit tests methods. """ def test_log_decoding_ViperLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.viper_log.\ + Test :func:`colour.models.rgb.transfer_functions.viper_log.\ log_decoding_ViperLog` definition. """ self.assertAlmostEqual(log_decoding_ViperLog(-np.inf), 0.0, places=7) self.assertAlmostEqual( - log_decoding_ViperLog(0.636008067010413), 0.18, places=7) + log_decoding_ViperLog(0.636008067010413), 0.18, places=7 + ) self.assertAlmostEqual(log_decoding_ViperLog(1.0), 1.0, places=7) def test_n_dimensional_log_decoding_ViperLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.viper_log.\ + Test :func:`colour.models.rgb.transfer_functions.viper_log.\ log_decoding_ViperLog` definition n-dimensional arrays support. """ @@ -131,29 +137,31 @@ def test_n_dimensional_log_decoding_ViperLog(self): def test_domain_range_scale_log_decoding_ViperLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.viper_log.\ + Test :func:`colour.models.rgb.transfer_functions.viper_log.\ log_decoding_ViperLog` definition domain and range scale support. """ y = 0.636008067010413 x = log_decoding_ViperLog(y) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - log_decoding_ViperLog(y * factor), x * factor, decimal=7) + log_decoding_ViperLog(y * factor), x * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_log_decoding_ViperLog(self): """ - Tests :func:`colour.models.rgb.transfer_functions.viper_log.\ + Test :func:`colour.models.rgb.transfer_functions.viper_log.\ log_decoding_ViperLog` definition nan support. """ log_decoding_ViperLog( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/rgb/transfer_functions/viper_log.py b/colour/models/rgb/transfer_functions/viper_log.py index 6a33637c72..41504de2a0 100644 --- a/colour/models/rgb/transfer_functions/viper_log.py +++ b/colour/models/rgb/transfer_functions/viper_log.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Viper Log Encoding ================== @@ -16,40 +15,43 @@ nuke-default/make.py """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.utilities import from_range_1, to_domain_1 +from colour.hints import FloatingOrArrayLike, FloatingOrNDArray +from colour.utilities import as_float, from_range_1, to_domain_1 -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['log_encoding_ViperLog', 'log_decoding_ViperLog'] +__all__ = [ + "log_encoding_ViperLog", + "log_decoding_ViperLog", +] -def log_encoding_ViperLog(x): +def log_encoding_ViperLog(x: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the *Viper Log* log encoding curve / opto-electronic transfer + Define the *Viper Log* log encoding curve / opto-electronic transfer function. Parameters ---------- - x : numeric or array_like + x Linear data :math:`x`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Non-linear data :math:`y`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -76,27 +78,26 @@ def log_encoding_ViperLog(x): y = (1023 + 500 * np.log10(x)) / 1023 - return from_range_1(y) + return as_float(from_range_1(y)) -def log_decoding_ViperLog(y): +def log_decoding_ViperLog(y: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Defines the *Viper Log* log decoding curve / electro-optical transfer + Define the *Viper Log* log decoding curve / electro-optical transfer function. Parameters ---------- - y : numeric or array_like + y Non-linear data :math:`y`. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`x`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -123,4 +124,4 @@ def log_decoding_ViperLog(y): x = 10 ** ((1023 * y - 1023) / 500) - return from_range_1(x) + return as_float(from_range_1(x)) diff --git a/colour/models/rgb/ycbcr.py b/colour/models/rgb/ycbcr.py index b9d26d97ec..8ee0c0e942 100644 --- a/colour/models/rgb/ycbcr.py +++ b/colour/models/rgb/ycbcr.py @@ -1,10 +1,12 @@ -# -*- coding: utf-8 -*- """ Y'CbCr Colour Encoding ====================== -Defines the *Y'CbCr* colour encoding related transformations: +Defines the *Y'CbCr* colour encoding related attributes and objects: +- :attr:`colour.WEIGHTS_YCBCR` +- :func:`colour.matrix_YCbCr` +- :func:`colour.offset_YCbCr` - :func:`colour.RGB_to_YCbCr` - :func:`colour.YCbCr_to_RGB` - :func:`colour.RGB_to_YcCbcCrc` @@ -43,35 +45,53 @@ 2016, from https://en.wikipedia.org/wiki/YCbCr """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.constants import DEFAULT_FLOAT_DTYPE, DEFAULT_INT_DTYPE +from colour.hints import Any, ArrayLike, Boolean, Integer, NDArray from colour.models.rgb.transfer_functions import ( - CV_range, eotf_inverse_BT2020, eotf_BT2020) -from colour.utilities import (CaseInsensitiveMapping, as_float_array, - domain_range_scale, from_range_1, to_domain_1, - tsplit, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Development' + CV_range, + eotf_inverse_BT2020, + eotf_BT2020, +) +from colour.utilities import ( + CaseInsensitiveMapping, + as_float_array, + as_int_array, + domain_range_scale, + from_range_1, + to_domain_1, + tsplit, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Development" __all__ = [ - 'WEIGHTS_YCBCR', 'YCbCr_ranges', 'RGB_to_YCbCr', 'YCbCr_to_RGB', - 'RGB_to_YcCbcCrc', 'YcCbcCrc_to_RGB' + "WEIGHTS_YCBCR", + "ranges_YCbCr", + "matrix_YCbCr", + "offset_YCbCr", + "RGB_to_YCbCr", + "YCbCr_to_RGB", + "RGB_to_YcCbcCrc", + "YcCbcCrc_to_RGB", ] -WEIGHTS_YCBCR = CaseInsensitiveMapping({ - 'ITU-R BT.601': np.array([0.2990, 0.1140]), - 'ITU-R BT.709': np.array([0.2126, 0.0722]), - 'ITU-R BT.2020': np.array([0.2627, 0.0593]), - 'SMPTE-240M': np.array([0.2122, 0.0865]) -}) +WEIGHTS_YCBCR: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "ITU-R BT.601": np.array([0.2990, 0.1140]), + "ITU-R BT.709": np.array([0.2126, 0.0722]), + "ITU-R BT.2020": np.array([0.2627, 0.0593]), + "SMPTE-240M": np.array([0.2122, 0.0865]), + } +) """ Luma weightings presets. @@ -82,39 +102,36 @@ :cite:`InternationalTelecommunicationUnion2015h`, :cite:`SocietyofMotionPictureandTelevisionEngineers1999b`, :cite:`Wikipedia2004d` - -WEIGHTS_YCBCR : dict - **{'ITU-R BT.601', 'ITU-R BT.709', 'ITU-R BT.2020', 'SMPTE-240M}** """ -def YCbCr_ranges(bits, is_legal, is_int): - """" - Returns the *Y'CbCr* colour encoding ranges array for given bit depth, +def ranges_YCbCr(bits: Integer, is_legal: Boolean, is_int: Boolean) -> NDArray: + """ + Return the *Y'CbCr* colour encoding ranges array for given bit depth, range legality and representation. Parameters ---------- - bits : int + bits Bit depth of the *Y'CbCr* colour encoding ranges array. - is_legal : bool + is_legal Whether the *Y'CbCr* colour encoding ranges array is legal. - is_int : bool + is_int Whether the *Y'CbCr* colour encoding ranges array represents integer code values. Returns ------- - ndarray + :class:`numpy.ndarray` *Y'CbCr* colour encoding ranges array. Examples -------- - >>> YCbCr_ranges(8, True, True) + >>> ranges_YCbCr(8, True, True) array([ 16, 235, 16, 240]) - >>> YCbCr_ranges(8, True, False) # doctest: +ELLIPSIS + >>> ranges_YCbCr(8, True, False) # doctest: +ELLIPSIS array([ 0.0627451..., 0.9215686..., 0.0627451..., 0.9411764...]) - >>> YCbCr_ranges(10, False, False) + >>> ranges_YCbCr(10, False, False) array([ 0. , 1. , -0.5, 0.5]) """ @@ -122,13 +139,13 @@ def YCbCr_ranges(bits, is_legal, is_int): ranges = np.array([16, 235, 16, 240]) ranges *= 2 ** (bits - 8) else: - ranges = np.array([0, 2 ** bits - 1, 0, 2 ** bits - 1]) + ranges = np.array([0, 2**bits - 1, 0, 2**bits - 1]) if not is_int: - ranges = ranges.astype(DEFAULT_FLOAT_DTYPE) / (2 ** bits - 1) + ranges = as_int_array(ranges) / (2**bits - 1) if is_int and not is_legal: - ranges[3] = 2 ** bits + ranges[3] = 2**bits if not is_int and not is_legal: ranges[2] = -0.5 @@ -137,63 +154,193 @@ def YCbCr_ranges(bits, is_legal, is_int): return ranges -def RGB_to_YCbCr(RGB, - K=WEIGHTS_YCBCR['ITU-R BT.709'], - in_bits=10, - in_legal=False, - in_int=False, - out_bits=8, - out_legal=True, - out_int=False, - **kwargs): +def matrix_YCbCr( + K: NDArray = WEIGHTS_YCBCR["ITU-R BT.709"], + bits: Integer = 8, + is_legal: Boolean = False, + is_int: Boolean = False, +) -> NDArray: + """ + Compute the *R'G'B'* to *Y'CbCr* matrix for given weights, bit depth, + range legality and representation. + + The related offset for the *R'G'B'* to *Y'CbCr* matrix can be computed with + the :func:`colour.offset_YCbCr` definition. + + Parameters + ---------- + K + Luma weighting coefficients of red and blue. See + :attr:`colour.WEIGHTS_YCBCR` for presets. Default is + *(0.2126, 0.0722)*, the weightings for *ITU-R BT.709*. + bits + Bit depth of the *Y'CbCr* colour encoding ranges array. + is_legal + Whether the *Y'CbCr* colour encoding ranges array is legal. + is_int + Whether the *Y'CbCr* colour encoding ranges array represents integer + code values. + + Returns + ------- + :class:`numpy.ndarray` + *Y'CbCr* matrix. + + Examples + -------- + >>> matrix_YCbCr() # doctest: +ELLIPSIS + array([[ 1.0000000...e+00, ..., 1.5748000...e+00], + [ 1.0000000...e+00, -1.8732427...e-01, -4.6812427...e-01], + [ 1.0000000...e+00, 1.8556000...e+00, ...]]) + >>> matrix_YCbCr(K=WEIGHTS_YCBCR['ITU-R BT.601']) # doctest: +ELLIPSIS + array([[ 1.0000000...e+00, ..., 1.4020000...e+00], + [ 1.0000000...e+00, -3.4413628...e-01, -7.1413628...e-01], + [ 1.0000000...e+00, 1.7720000...e+00, ...]]) + >>> matrix_YCbCr(is_legal=True) # doctest: +ELLIPSIS + array([[ 1.1643835...e+00, ..., 1.7927410...e+00], + [ 1.1643835...e+00, -2.1324861...e-01, -5.3290932...e-01], + [ 1.1643835...e+00, 2.1124017...e+00, ...]]) + + Matching the default output of the :func:`colour.RGB_to_YCbCr` is done as + follows: + + >>> from colour.algebra import vector_dot + >>> from colour.utilities import as_int_array + >>> RGB = np.array([1.0, 1.0, 1.0]) + >>> RGB_to_YCbCr(RGB) # doctest: +ELLIPSIS + array([ 0.9215686..., 0.5019607..., 0.5019607...]) + >>> YCbCr = vector_dot(np.linalg.inv(matrix_YCbCr(is_legal=True)), RGB) + >>> YCbCr += offset_YCbCr(is_legal=True) + >>> YCbCr # doctest: +ELLIPSIS + array([ 0.9215686..., 0.5019607..., 0.5019607...]) + + Matching the int output of the :func:`colour.RGB_to_YCbCr` is done as + follows: + + >>> RGB = np.array([102, 0, 51]) + >>> RGB_to_YCbCr(RGB, in_bits=8, in_int=True, out_bits=8, out_int=True) + ... # doctest: +SKIP + array([ 38, 140, 171]) + >>> YCbCr = vector_dot(np.linalg.inv(matrix_YCbCr(is_legal=True)), RGB) + >>> YCbCr += offset_YCbCr(is_legal=True, is_int=True) + >>> as_int_array(np.around(YCbCr)) + ... # doctest: +SKIP + array([ 38, 140, 171]) + """ + + Kr, Kb = K + Y_min, Y_max, C_min, C_max = ranges_YCbCr(bits, is_legal, is_int) + + Y = np.array([Kr, (1 - Kr - Kb), Kb]) + Cb = 0.5 * (np.array([0, 0, 1]) - Y) / (1 - Kb) + Cr = 0.5 * (np.array([1, 0, 0]) - Y) / (1 - Kr) + Y *= Y_max - Y_min + Cb *= C_max - C_min + Cr *= C_max - C_min + + return np.linalg.inv(np.vstack([Y, Cb, Cr])) + + +def offset_YCbCr( + bits: Integer = 8, is_legal: Boolean = False, is_int: Boolean = False +) -> NDArray: + """ + Compute the *R'G'B'* to *Y'CbCr* offsets for given bit depth, range + legality and representation. + + The related *R'G'B'* to *Y'CbCr* matrix can be computed with the + :func:`colour.matrix_YCbCr` definition. + + Parameters + ---------- + bits + Bit depth of the *Y'CbCr* colour encoding ranges array. + is_legal + Whether the *Y'CbCr* colour encoding ranges array is legal. + is_int + Whether the *Y'CbCr* colour encoding ranges array represents integer + code values. + + Returns + ------- + :class:`numpy.ndarray` + *Y'CbCr* matrix. + + Examples + -------- + >>> offset_YCbCr() + array([ 0., 0., 0.]) + >>> offset_YCbCr(is_legal=True) # doctest: +ELLIPSIS + array([ 0.0627451..., 0.5019607..., 0.5019607...]) + """ + + Y_min, _Y_max, C_min, C_max = ranges_YCbCr(bits, is_legal, is_int) + + Y_offset = Y_min + C_offset = (C_min + C_max) / 2 + + return np.array([Y_offset, C_offset, C_offset]) + + +def RGB_to_YCbCr( + RGB: ArrayLike, + K: NDArray = WEIGHTS_YCBCR["ITU-R BT.709"], + in_bits: Integer = 10, + in_legal: Boolean = False, + in_int: Boolean = False, + out_bits: Integer = 8, + out_legal: Boolean = True, + out_int: Boolean = False, + **kwargs: Any, +) -> NDArray: """ - Converts an array of *R'G'B'* values to the corresponding *Y'CbCr* colour + Convert an array of *R'G'B'* values to the corresponding *Y'CbCr* colour encoding values array. Parameters ---------- - RGB : array_like + RGB Input *R'G'B'* array of floats or integer values. - K : array_like, optional + K Luma weighting coefficients of red and blue. See :attr:`colour.WEIGHTS_YCBCR` for presets. Default is *(0.2126, 0.0722)*, the weightings for *ITU-R BT.709*. - in_bits : int, optional + in_bits Bit depth for integer input, or used in the calculation of the denominator for legal range float values, i.e. 8-bit means the float value for legal white is *235 / 255*. Default is *10*. - in_legal : bool, optional + in_legal Whether to treat the input values as legal range. Default is *False*. - in_int : bool, optional + in_int Whether to treat the input values as ``in_bits`` integer code values. Default is *False*. - out_bits : int, optional + out_bits Bit depth for integer output, or used in the calculation of the denominator for legal range float values, i.e. 8-bit means the float value for legal white is *235 / 255*. Ignored if ``out_legal`` and ``out_int`` are both *False*. Default is *8*. - out_legal : bool, optional + out_legal Whether to return legal range values. Default is *True*. - out_int : bool, optional + out_int Whether to return values as ``out_bits`` integer code values. Default is *False*. Other Parameters ---------------- - in_range : array_like, optional + in_range Array overriding the computed range such as *in_range = (RGB_min, RGB_max)*. If ``in_range`` is undefined, *RGB_min* and *RGB_max* will be computed using :func:`colour.CV_range` definition. - out_range : array_like, optional + out_range Array overriding the computed range such as *out_range = (Y_min, Y_max, C_min, C_max)`. If ``out_range`` is undefined, *Y_min*, *Y_max*, *C_min* and *C_max* will be computed - using :func:`colour.models.rgb.ycbcr.YCbCr_ranges` definition. + using :func:`colour.models.rgb.ycbcr.ranges_YCbCr` definition. Returns ------- - ndarray + :class:`numpy.ndarray` *Y'CbCr* colour encoding array of integer or float values. Warnings @@ -205,7 +352,6 @@ def RGB_to_YCbCr(RGB, Notes ----- - +----------------+-----------------------+---------------+ | **Domain \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ @@ -243,27 +389,28 @@ def RGB_to_YCbCr(RGB, >>> RGB_to_YCbCr(RGB) # doctest: +ELLIPSIS array([ 0.9215686..., 0.5019607..., 0.5019607...]) - Matching float output of The Foundry Nuke's Colorspace node set to YCbCr: + Matching the float output of *The Foundry Nuke*'s *Colorspace* node set to + *YCbCr*: >>> RGB_to_YCbCr(RGB, ... out_range=(16 / 255, 235 / 255, 15.5 / 255, 239.5 / 255)) ... # doctest: +ELLIPSIS array([ 0.9215686..., 0.5 , 0.5 ]) - Matching float output of The Foundry Nuke's Colorspace node set to YPbPr: + Matching the float output of *The Foundry Nuke*'s *Colorspace* node set to + *YPbPr*: >>> RGB_to_YCbCr(RGB, out_legal=False, out_int=False) ... # doctest: +ELLIPSIS array([ 1., 0., 0.]) - Creating integer code values as per standard 10-bit SDI: + Creating integer code values as per standard *10-bit SDI*: >>> RGB_to_YCbCr(RGB, out_legal=True, out_bits=10, out_int=True) ... # doctest: +ELLIPSIS array([940, 512, 512]...) - For JFIF JPEG conversion as per ITU-T T.871 - :cite:`InternationalTelecommunicationUnion2011e`: + For *JFIF JPEG* conversion as per *Recommendation ITU-T T.871* >>> RGB = np.array([102, 0, 51]) >>> RGB_to_YCbCr(RGB, K=WEIGHTS_YCBCR['ITU-R BT.601'], in_range=(0, 255), @@ -276,10 +423,10 @@ def RGB_to_YCbCr(RGB, about 127.5, meaning that there is no integer code value to represent achromatic colours. This does however create the possibility of output integer codes with value of 256, which cannot be stored in 8-bit integer - representation. Recommendation ITU-T T.871 specifies these should be + representation. *Recommendation ITU-T T.871* specifies these should be clamped to 255. - These JFIF JPEG ranges are also obtained as follows: + These *JFIF JPEG* ranges are also obtained as follows: >>> RGB_to_YCbCr(RGB, K=WEIGHTS_YCBCR['ITU-R BT.601'], in_bits=8, ... in_int=True, out_legal=False, out_int=True) @@ -293,12 +440,14 @@ def RGB_to_YCbCr(RGB, RGB = to_domain_1(RGB) Kr, Kb = K - RGB_min, RGB_max = kwargs.get('in_range', - CV_range(in_bits, in_legal, in_int)) + RGB_min, RGB_max = kwargs.get( + "in_range", CV_range(in_bits, in_legal, in_int) + ) Y_min, Y_max, C_min, C_max = kwargs.get( - 'out_range', YCbCr_ranges(out_bits, out_legal, out_int)) + "out_range", ranges_YCbCr(out_bits, out_legal, out_int) + ) - RGB_float = RGB.astype(DEFAULT_FLOAT_DTYPE) - RGB_min + RGB_float = as_float_array(RGB) - RGB_min RGB_float *= 1 / (RGB_max - RGB_min) R, G, B = tsplit(RGB_float) @@ -313,61 +462,64 @@ def RGB_to_YCbCr(RGB, Cr += (C_max + C_min) / 2 YCbCr = tstack([Y, Cb, Cr]) - YCbCr = np.round(YCbCr).astype( - DEFAULT_INT_DTYPE) if out_int else from_range_1(YCbCr) - return YCbCr - - -def YCbCr_to_RGB(YCbCr, - K=WEIGHTS_YCBCR['ITU-R BT.709'], - in_bits=8, - in_legal=True, - in_int=False, - out_bits=10, - out_legal=False, - out_int=False, - **kwargs): + if out_int: + return as_int_array(np.round(YCbCr)) + else: + return from_range_1(YCbCr) + + +def YCbCr_to_RGB( + YCbCr: ArrayLike, + K: NDArray = WEIGHTS_YCBCR["ITU-R BT.709"], + in_bits: Integer = 8, + in_legal: Boolean = True, + in_int: Boolean = False, + out_bits: Integer = 10, + out_legal: Boolean = False, + out_int: Boolean = False, + **kwargs: Any, +) -> NDArray: """ - Converts an array of *Y'CbCr* colour encoding values to the corresponding + Convert an array of *Y'CbCr* colour encoding values to the corresponding *R'G'B'* values array. Parameters ---------- - YCbCr : array_like + YCbCr Input *Y'CbCr* colour encoding array of integer or float values. - K : array_like, optional + K Luma weighting coefficients of red and blue. See :attr:`colour.WEIGHTS_YCBCR` for presets. Default is *(0.2126, 0.0722)*, the weightings for *ITU-R BT.709*. - in_bits : int, optional + in_bits Bit depth for integer input, or used in the calculation of the denominator for legal range float values, i.e. 8-bit means the float value for legal white is *235 / 255*. Default is *8*. - in_legal : bool, optional + in_legal Whether to treat the input values as legal range. Default is *True*. - in_int : bool, optional + in_int Whether to treat the input values as ``in_bits`` integer code values. Default is *False*. - out_bits : int, optional + out_bits Bit depth for integer output, or used in the calculation of the denominator for legal range float values, i.e. 8-bit means the float value for legal white is *235 / 255*. Ignored if ``out_legal`` and ``out_int`` are both *False*. Default is *10*. - out_legal : bool, optional + out_legal Whether to return legal range values. Default is *False*. - out_int : bool, optional + out_int Whether to return values as ``out_bits`` integer code values. Default is *False*. Other Parameters ---------------- - in_range : array_like, optional + in_range Array overriding the computed range such as *in_range = (Y_min, Y_max, C_min, C_max)*. If ``in_range`` is undefined, *Y_min*, *Y_max*, *C_min* and *C_max* will be computed using - :func:`colour.models.rgb.ycbcr.YCbCr_ranges` definition. - out_range : array_like, optional + :func:`colour.models.rgb.ycbcr.ranges_YCbCr` definition. + out_range Array overriding the computed range such as *out_range = (RGB_min, RGB_max)*. If ``out_range`` is undefined, *RGB_min* and *RGB_max* will be computed using :func:`colour.CV_range` @@ -375,12 +527,11 @@ def YCbCr_to_RGB(YCbCr, Returns ------- - ndarray + :class:`numpy.ndarray` *R'G'B'* array of integer or float values. Notes ----- - +----------------+-----------------------+---------------+ | **Domain \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ @@ -422,12 +573,14 @@ def YCbCr_to_RGB(YCbCr, else: YCbCr = to_domain_1(YCbCr) - Y, Cb, Cr = tsplit(YCbCr.astype(DEFAULT_FLOAT_DTYPE)) + Y, Cb, Cr = tsplit(YCbCr) Kr, Kb = K Y_min, Y_max, C_min, C_max = kwargs.get( - 'in_range', YCbCr_ranges(in_bits, in_legal, in_int)) - RGB_min, RGB_max = kwargs.get('out_range', - CV_range(out_bits, out_legal, out_int)) + "in_range", ranges_YCbCr(in_bits, in_legal, in_int) + ) + RGB_min, RGB_max = kwargs.get( + "out_range", CV_range(out_bits, out_legal, out_int) + ) Y -= Y_min Cb -= (C_max + C_min) / 2 @@ -442,56 +595,57 @@ def YCbCr_to_RGB(YCbCr, RGB = tstack([R, G, B]) RGB *= RGB_max - RGB_min RGB += RGB_min - RGB = np.round(RGB).astype(DEFAULT_INT_DTYPE) if out_int else from_range_1( - RGB) + + RGB = as_int_array(np.round(RGB)) if out_int else from_range_1(RGB) return RGB -def RGB_to_YcCbcCrc(RGB, - out_bits=10, - out_legal=True, - out_int=False, - is_12_bits_system=False, - **kwargs): +def RGB_to_YcCbcCrc( + RGB: ArrayLike, + out_bits: Integer = 10, + out_legal: Boolean = True, + out_int: Boolean = False, + is_12_bits_system: Boolean = False, + **kwargs: Any, +) -> NDArray: """ - Converts an array of *RGB* linear values to the corresponding *Yc'Cbc'Crc'* + Convert an array of *RGB* linear values to the corresponding *Yc'Cbc'Crc'* colour encoding values array. Parameters ---------- - RGB : array_like + RGB Input *RGB* array of linear float values. - out_bits : int, optional + out_bits Bit depth for integer output, or used in the calculation of the denominator for legal range float values, i.e. 8-bit means the float value for legal white is *235 / 255*. Ignored if ``out_legal`` and ``out_int`` are both *False*. Default is *10*. - out_legal : bool, optional + out_legal Whether to return legal range values. Default is *True*. - out_int : bool, optional + out_int Whether to return values as ``out_bits`` integer code values. Default is *False*. - is_12_bits_system : bool, optional + is_12_bits_system *Recommendation ITU-R BT.2020* OETF (OECF) adopts different parameters for 10 and 12 bit systems. Default is *False*. Other Parameters ---------------- - out_range : array_like, optional + out_range Array overriding the computed range such as *out_range = (Y_min, Y_max, C_min, C_max)*. If ``out_range`` is undefined, *Y_min*, *Y_max*, *C_min* and *C_max* will be computed - using :func:`colour.models.rgb.ycbcr.YCbCr_ranges` definition. + using :func:`colour.models.rgb.ycbcr.ranges_YCbCr` definition. Returns ------- - ndarray + :class:`numpy.ndarray` *Yc'Cbc'Crc'* colour encoding array of integer or float values. Notes ----- - +----------------+-----------------------+---------------+ | **Domain \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ @@ -528,11 +682,12 @@ def RGB_to_YcCbcCrc(RGB, R, G, B = tsplit(to_domain_1(RGB)) Y_min, Y_max, C_min, C_max = kwargs.get( - 'out_range', YCbCr_ranges(out_bits, out_legal, out_int)) + "out_range", ranges_YCbCr(out_bits, out_legal, out_int) + ) Yc = 0.2627 * R + 0.6780 * G + 0.0593 * B - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): Yc = eotf_inverse_BT2020(Yc, is_12_bits_system=is_12_bits_system) R = eotf_inverse_BT2020(R, is_12_bits_system=is_12_bits_system) B = eotf_inverse_BT2020(B, is_12_bits_system=is_12_bits_system) @@ -547,55 +702,57 @@ def RGB_to_YcCbcCrc(RGB, Crc += (C_max + C_min) / 2 YcCbcCrc = tstack([Yc, Cbc, Crc]) - YcCbcCrc = (np.round(YcCbcCrc).astype(DEFAULT_INT_DTYPE) - if out_int else from_range_1(YcCbcCrc)) - return YcCbcCrc + if out_int: + return as_int_array(np.round(YcCbcCrc)) + else: + return from_range_1(YcCbcCrc) -def YcCbcCrc_to_RGB(YcCbcCrc, - in_bits=10, - in_legal=True, - in_int=False, - is_12_bits_system=False, - **kwargs): +def YcCbcCrc_to_RGB( + YcCbcCrc: ArrayLike, + in_bits: Integer = 10, + in_legal: Boolean = True, + in_int: Boolean = False, + is_12_bits_system: Boolean = False, + **kwargs: Any, +) -> NDArray: """ - Converts an array of *Yc'Cbc'Crc'* colour encoding values to the + Convert an array of *Yc'Cbc'Crc'* colour encoding values to the corresponding *RGB* array of linear values. Parameters ---------- - YcCbcCrc : array_like + YcCbcCrc Input *Yc'Cbc'Crc'* colour encoding array of linear float values. - in_bits : int, optional + in_bits Bit depth for integer input, or used in the calculation of the denominator for legal range float values, i.e. 8-bit means the float value for legal white is *235 / 255*. Default is *10*. - in_legal : bool, optional + in_legal Whether to treat the input values as legal range. Default is *False*. - in_int : bool, optional + in_int Whether to treat the input values as ``in_bits`` integer code values. Default is *False*. - is_12_bits_system : bool, optional + is_12_bits_system *Recommendation ITU-R BT.2020* EOTF (EOCF) adopts different parameters for 10 and 12 bit systems. Default is *False*. Other Parameters ---------------- - in_range : array_like, optional + in_range Array overriding the computed range such as *in_range = (Y_min, Y_max, C_min, C_max)*. If ``in_range`` is undefined, *Y_min*, *Y_max*, *C_min* and *C_max* will be computed using - :func:`colour.models.rgb.ycbcr.YCbCr_ranges` definition. + :func:`colour.models.rgb.ycbcr.ranges_YCbCr` definition. Returns ------- - ndarray + :class:`numpy.ndarray` *RGB* array of linear float values. Notes ----- - +----------------+-----------------------+---------------+ | **Domain \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ @@ -636,9 +793,10 @@ def YcCbcCrc_to_RGB(YcCbcCrc, else: YcCbcCrc = to_domain_1(YcCbcCrc) - Yc, Cbc, Crc = tsplit(YcCbcCrc.astype(DEFAULT_FLOAT_DTYPE)) + Yc, Cbc, Crc = tsplit(YcCbcCrc) Y_min, Y_max, C_min, C_max = kwargs.get( - 'in_range', YCbCr_ranges(in_bits, in_legal, in_int)) + "in_range", ranges_YCbCr(in_bits, in_legal, in_int) + ) Yc -= Y_min Cbc -= (C_max + C_min) / 2 @@ -649,10 +807,10 @@ def YcCbcCrc_to_RGB(YcCbcCrc, B = np.where(Cbc <= 0, Cbc * 1.9404 + Yc, Cbc * 1.5816 + Yc) R = np.where(Crc <= 0, Crc * 1.7184 + Yc, Crc * 0.9936 + Yc) - with domain_range_scale('ignore'): - Yc = eotf_BT2020(Yc, is_12_bits_system=is_12_bits_system) - B = eotf_BT2020(B, is_12_bits_system=is_12_bits_system) - R = eotf_BT2020(R, is_12_bits_system=is_12_bits_system) + with domain_range_scale("ignore"): + Yc = as_float_array(eotf_BT2020(Yc, is_12_bits_system)) + B = as_float_array(eotf_BT2020(B, is_12_bits_system)) + R = as_float_array(eotf_BT2020(R, is_12_bits_system)) G = (Yc - 0.0593 * B - 0.2627 * R) / 0.6780 diff --git a/colour/models/rgb/ycocg.py b/colour/models/rgb/ycocg.py index 2266dea290..1d85097750 100644 --- a/colour/models/rgb/ycocg.py +++ b/colour/models/rgb/ycocg.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ YCoCg Colour Encoding ====================== @@ -16,62 +15,59 @@ Malvar_Sullivan_YCoCg-R_JVT-I014r3-2.pdf """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.utilities import vector_dot +from colour.algebra import vector_dot +from colour.hints import ArrayLike, NDArray -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Development' +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Development" __all__ = [ - 'MATRIX_RGB_TO_YCOCG', - 'MATRIX_YCOCG_TO_RGB', - 'RGB_to_YCoCg', - 'YCoCg_to_RGB', + "MATRIX_RGB_TO_YCOCG", + "MATRIX_YCOCG_TO_RGB", + "RGB_to_YCoCg", + "YCoCg_to_RGB", ] -MATRIX_RGB_TO_YCOCG = np.array([ - [1 / 4, 1 / 2, 1 / 4], - [1 / 2, 0, -1 / 2], - [-1 / 4, 1 / 2, -1 / 4], -]) -""" -*R'G'B'* colourspace to *YCoCg* colour encoding matrix. - -MATRIX_RGB_TO_YCOCG : array_like, (3, 3) -""" - -MATRIX_YCOCG_TO_RGB = np.array([ - [1, 1, -1], - [1, 0, 1], - [1, -1, -1], -]) -""" -*YCoCg* colour encoding to *R'G'B'* colourspace matrix. - -MATRIX_YCOCG_TO_RGB : array_like, (3, 3) -""" - - -def RGB_to_YCoCg(RGB): +MATRIX_RGB_TO_YCOCG: NDArray = np.array( + [ + [1 / 4, 1 / 2, 1 / 4], + [1 / 2, 0, -1 / 2], + [-1 / 4, 1 / 2, -1 / 4], + ] +) +"""*R'G'B'* colourspace to *YCoCg* colour encoding matrix.""" + +MATRIX_YCOCG_TO_RGB: NDArray = np.array( + [ + [1, 1, -1], + [1, 0, 1], + [1, -1, -1], + ] +) +"""*YCoCg* colour encoding to *R'G'B'* colourspace matrix.""" + + +def RGB_to_YCoCg(RGB: ArrayLike) -> NDArray: """ - Converts an array of *R'G'B'* values to the corresponding *YCoCg* colour + Convert an array of *R'G'B'* values to the corresponding *YCoCg* colour encoding values array. Parameters ---------- - RGB : array_like + RGB Input *R'G'B'* array. Returns ------- - ndarray + :class:`numpy.ndarray` *YCoCg* colour encoding array. References @@ -89,19 +85,19 @@ def RGB_to_YCoCg(RGB): return vector_dot(MATRIX_RGB_TO_YCOCG, RGB) -def YCoCg_to_RGB(YCoCg): +def YCoCg_to_RGB(YCoCg: ArrayLike) -> NDArray: """ - Converts an array of *YCoCg* colour encoding values to the corresponding + Convert an array of *YCoCg* colour encoding values to the corresponding *R'G'B'* values array. Parameters ---------- - YCoCg : array_like + YCoCg *YCoCg* colour encoding array. Returns ------- - ndarray + :class:`numpy.ndarray` Output *R'G'B'* array. References diff --git a/colour/models/tests/__init__.py b/colour/models/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/models/tests/__init__.py +++ b/colour/models/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/models/tests/test_cam02_ucs.py b/colour/models/tests/test_cam02_ucs.py index af1a4b88e0..8373209264 100644 --- a/colour/models/tests/test_cam02_ucs.py +++ b/colour/models/tests/test_cam02_ucs.py @@ -1,148 +1,187 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.cam02_ucs` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.cam02_ucs` module.""" import numpy as np import unittest from itertools import permutations -from colour.appearance import (VIEWING_CONDITIONS_CIECAM02, XYZ_to_CIECAM02) -from colour.models.cam02_ucs import (COEFFICIENTS_UCS_LUO2006, - JMh_CIECAM02_to_UCS_Luo2006, - UCS_Luo2006_to_JMh_CIECAM02) -from colour.models import (JMh_CIECAM02_to_CAM02LCD, CAM02LCD_to_JMh_CIECAM02, - JMh_CIECAM02_to_CAM02SCD, CAM02SCD_to_JMh_CIECAM02, - JMh_CIECAM02_to_CAM02UCS, CAM02UCS_to_JMh_CIECAM02) -from colour.utilities import domain_range_scale, ignore_numpy_errors - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.appearance import ( + CAM_KWARGS_CIECAM02_sRGB, + VIEWING_CONDITIONS_CIECAM02, + XYZ_to_CIECAM02, +) +from colour.models.cam02_ucs import ( + COEFFICIENTS_UCS_LUO2006, + JMh_CIECAM02_to_UCS_Luo2006, + UCS_Luo2006_to_JMh_CIECAM02, + XYZ_to_UCS_Luo2006, + UCS_Luo2006_to_XYZ, +) +from colour.models import ( + JMh_CIECAM02_to_CAM02LCD, + CAM02LCD_to_JMh_CIECAM02, + JMh_CIECAM02_to_CAM02SCD, + CAM02SCD_to_JMh_CIECAM02, + JMh_CIECAM02_to_CAM02UCS, + CAM02UCS_to_JMh_CIECAM02, + XYZ_to_CAM02LCD, + CAM02LCD_to_XYZ, + XYZ_to_CAM02SCD, + CAM02SCD_to_XYZ, + XYZ_to_CAM02UCS, + CAM02UCS_to_XYZ, +) +from colour.utilities import attest, domain_range_scale, ignore_numpy_errors + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'TestJMh_CIECAM02_to_UCS_Luo2006', 'TestUCS_Luo2006_to_JMh_CIECAM02' + "TestJMh_CIECAM02_to_UCS_Luo2006", + "TestUCS_Luo2006_to_JMh_CIECAM02", + "TestXYZ_to_UCS_Luo2006", + "TestUCS_Luo2006_to_XYZ", ] class TestJMh_CIECAM02_to_UCS_Luo2006(unittest.TestCase): """ - Defines :func:`colour.models.cam02_ucs.TestJMh_CIECAM02_to_UCS_Luo2006` + Define :func:`colour.models.cam02_ucs.JMh_CIECAM02_to_UCS_Luo2006` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" XYZ = np.array([19.01, 20.00, 21.78]) XYZ_w = np.array([95.05, 100.00, 108.88]) L_A = 318.31 Y_b = 20.0 - surround = VIEWING_CONDITIONS_CIECAM02['Average'] + surround = VIEWING_CONDITIONS_CIECAM02["Average"] specification = XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround) self._JMh = np.array( - [specification.J, specification.M, specification.h]) + [specification.J, specification.M, specification.h] + ) def test_JMh_CIECAM02_to_UCS_Luo2006(self): """ - Tests :func:`colour.models.cam02_ucs.JMh_CIECAM02_to_UCS_Luo2006` + Test :func:`colour.models.cam02_ucs.JMh_CIECAM02_to_UCS_Luo2006` definition. """ np.testing.assert_almost_equal( - JMh_CIECAM02_to_UCS_Luo2006(self._JMh, - COEFFICIENTS_UCS_LUO2006['CAM02-LCD']), + JMh_CIECAM02_to_UCS_Luo2006( + self._JMh, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ), np.array([54.90433134, -0.08450395, -0.06854831]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - JMh_CIECAM02_to_UCS_Luo2006(self._JMh, - COEFFICIENTS_UCS_LUO2006['CAM02-LCD']), + JMh_CIECAM02_to_UCS_Luo2006( + self._JMh, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ), JMh_CIECAM02_to_CAM02LCD(self._JMh), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - JMh_CIECAM02_to_UCS_Luo2006(self._JMh, - COEFFICIENTS_UCS_LUO2006['CAM02-SCD']), + JMh_CIECAM02_to_UCS_Luo2006( + self._JMh, COEFFICIENTS_UCS_LUO2006["CAM02-SCD"] + ), np.array([54.90433134, -0.08436178, -0.06843298]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - JMh_CIECAM02_to_UCS_Luo2006(self._JMh, - COEFFICIENTS_UCS_LUO2006['CAM02-SCD']), + JMh_CIECAM02_to_UCS_Luo2006( + self._JMh, COEFFICIENTS_UCS_LUO2006["CAM02-SCD"] + ), JMh_CIECAM02_to_CAM02SCD(self._JMh), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - JMh_CIECAM02_to_UCS_Luo2006(self._JMh, - COEFFICIENTS_UCS_LUO2006['CAM02-UCS']), + JMh_CIECAM02_to_UCS_Luo2006( + self._JMh, COEFFICIENTS_UCS_LUO2006["CAM02-UCS"] + ), np.array([54.90433134, -0.08442362, -0.06848314]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - JMh_CIECAM02_to_UCS_Luo2006(self._JMh, - COEFFICIENTS_UCS_LUO2006['CAM02-UCS']), + JMh_CIECAM02_to_UCS_Luo2006( + self._JMh, COEFFICIENTS_UCS_LUO2006["CAM02-UCS"] + ), JMh_CIECAM02_to_CAM02UCS(self._JMh), - decimal=7) + decimal=7, + ) def test_n_dimensional_JMh_CIECAM02_to_UCS_Luo2006(self): """ - Tests :func:`colour.models.cam02_ucs.JMh_CIECAM02_to_UCS_Luo2006` + Test :func:`colour.models.cam02_ucs.JMh_CIECAM02_to_UCS_Luo2006` definition n-dimensional support. """ JMh = self._JMh Jpapbp = JMh_CIECAM02_to_UCS_Luo2006( - JMh, COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) + JMh, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ) JMh = np.tile(JMh, (6, 1)) Jpapbp = np.tile(Jpapbp, (6, 1)) np.testing.assert_almost_equal( - JMh_CIECAM02_to_UCS_Luo2006(JMh, - COEFFICIENTS_UCS_LUO2006['CAM02-LCD']), + JMh_CIECAM02_to_UCS_Luo2006( + JMh, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ), Jpapbp, - decimal=7) + decimal=7, + ) JMh = np.reshape(JMh, (2, 3, 3)) Jpapbp = np.reshape(Jpapbp, (2, 3, 3)) np.testing.assert_almost_equal( - JMh_CIECAM02_to_UCS_Luo2006(JMh, - COEFFICIENTS_UCS_LUO2006['CAM02-LCD']), + JMh_CIECAM02_to_UCS_Luo2006( + JMh, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ), Jpapbp, - decimal=7) + decimal=7, + ) def test_domain_range_scale_JMh_CIECAM02_to_UCS_Luo2006(self): """ - Tests :func:`colour.models.cam02_ucs.JMh_CIECAM02_to_UCS_Luo2006` + Test :func:`colour.models.cam02_ucs.JMh_CIECAM02_to_UCS_Luo2006` definition domain and range scale support. """ JMh = self._JMh Jpapbp = JMh_CIECAM02_to_UCS_Luo2006( - JMh, COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) - - d_r = (('reference', 1, 1), (1, np.array([0.01, 0.01, 1 / 360]), 0.01), - (100, np.array([1, 1, 1 / 3.6]), 1)) + JMh, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ) + + d_r = ( + ("reference", 1, 1), + ("1", np.array([0.01, 0.01, 1 / 360]), 0.01), + ("100", np.array([1, 1, 1 / 3.6]), 1), + ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( JMh_CIECAM02_to_UCS_Luo2006( - JMh * factor_a, COEFFICIENTS_UCS_LUO2006['CAM02-LCD']), + JMh * factor_a, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ), Jpapbp * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_JMh_CIECAM02_to_UCS_Luo2006(self): """ - Tests :func:`colour.models.cam02_ucs.JMh_CIECAM02_to_UCS_Luo2006` + Test :func:`colour.models.cam02_ucs.JMh_CIECAM02_to_UCS_Luo2006` definition nan support. """ @@ -150,118 +189,145 @@ def test_nan_JMh_CIECAM02_to_UCS_Luo2006(self): cases = set(permutations(cases * 3, r=3)) for case in cases: JMh = np.array(case) - JMh_CIECAM02_to_UCS_Luo2006(JMh, - COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) + JMh_CIECAM02_to_UCS_Luo2006( + JMh, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ) class TestUCS_Luo2006_to_JMh_CIECAM02(unittest.TestCase): """ - Defines :func:`colour.models.cam02_ucs.TestUCS_Luo2006_to_JMh_CIECAM02` + Define :func:`colour.models.cam02_ucs.UCS_Luo2006_to_JMh_CIECAM02` definition unit tests methods. """ def test_UCS_Luo2006_to_JMh_CIECAM02(self): """ - Tests :func:`colour.models.cam02_ucs.UCS_Luo2006_to_JMh_CIECAM02` + Test :func:`colour.models.cam02_ucs.UCS_Luo2006_to_JMh_CIECAM02` definition. """ np.testing.assert_almost_equal( UCS_Luo2006_to_JMh_CIECAM02( np.array([54.90433134, -0.08442362, -0.06848314]), - COEFFICIENTS_UCS_LUO2006['CAM02-LCD']), + COEFFICIENTS_UCS_LUO2006["CAM02-LCD"], + ), np.array([41.73109113, 0.10873867, 219.04843202]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( UCS_Luo2006_to_JMh_CIECAM02( np.array([54.90433134, -0.08442362, -0.06848314]), - COEFFICIENTS_UCS_LUO2006['CAM02-LCD']), + COEFFICIENTS_UCS_LUO2006["CAM02-LCD"], + ), CAM02LCD_to_JMh_CIECAM02( - np.array([54.90433134, -0.08442362, -0.06848314])), - decimal=7) + np.array([54.90433134, -0.08442362, -0.06848314]) + ), + decimal=7, + ) np.testing.assert_almost_equal( UCS_Luo2006_to_JMh_CIECAM02( np.array([54.90433134, -0.08442362, -0.06848314]), - COEFFICIENTS_UCS_LUO2006['CAM02-SCD']), + COEFFICIENTS_UCS_LUO2006["CAM02-SCD"], + ), np.array([41.73109113, 0.10892212, 219.04843202]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( UCS_Luo2006_to_JMh_CIECAM02( np.array([54.90433134, -0.08442362, -0.06848314]), - COEFFICIENTS_UCS_LUO2006['CAM02-SCD']), + COEFFICIENTS_UCS_LUO2006["CAM02-SCD"], + ), CAM02SCD_to_JMh_CIECAM02( - np.array([54.90433134, -0.08442362, -0.06848314])), - decimal=7) + np.array([54.90433134, -0.08442362, -0.06848314]) + ), + decimal=7, + ) np.testing.assert_almost_equal( UCS_Luo2006_to_JMh_CIECAM02( np.array([54.90433134, -0.08442362, -0.06848314]), - COEFFICIENTS_UCS_LUO2006['CAM02-UCS']), + COEFFICIENTS_UCS_LUO2006["CAM02-UCS"], + ), np.array([41.73109113, 0.10884218, 219.04843202]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( UCS_Luo2006_to_JMh_CIECAM02( np.array([54.90433134, -0.08442362, -0.06848314]), - COEFFICIENTS_UCS_LUO2006['CAM02-UCS']), + COEFFICIENTS_UCS_LUO2006["CAM02-UCS"], + ), CAM02UCS_to_JMh_CIECAM02( - np.array([54.90433134, -0.08442362, -0.06848314])), - decimal=7) + np.array([54.90433134, -0.08442362, -0.06848314]) + ), + decimal=7, + ) def test_n_dimensional_UCS_Luo2006_to_JMh_CIECAM02(self): """ - Tests :func:`colour.models.cam02_ucs.UCS_Luo2006_to_JMh_CIECAM02` + Test :func:`colour.models.cam02_ucs.UCS_Luo2006_to_JMh_CIECAM02` definition n-dimensional support. """ Jpapbp = np.array([54.90433134, -0.08442362, -0.06848314]) JMh = UCS_Luo2006_to_JMh_CIECAM02( - Jpapbp, COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) + Jpapbp, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ) Jpapbp = np.tile(Jpapbp, (6, 1)) JMh = np.tile(JMh, (6, 1)) np.testing.assert_almost_equal( - UCS_Luo2006_to_JMh_CIECAM02(Jpapbp, - COEFFICIENTS_UCS_LUO2006['CAM02-LCD']), + UCS_Luo2006_to_JMh_CIECAM02( + Jpapbp, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ), JMh, - decimal=7) + decimal=7, + ) Jpapbp = np.reshape(Jpapbp, (2, 3, 3)) JMh = np.reshape(JMh, (2, 3, 3)) np.testing.assert_almost_equal( - UCS_Luo2006_to_JMh_CIECAM02(Jpapbp, - COEFFICIENTS_UCS_LUO2006['CAM02-LCD']), + UCS_Luo2006_to_JMh_CIECAM02( + Jpapbp, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ), JMh, - decimal=7) + decimal=7, + ) def test_domain_range_scale_UCS_Luo2006_to_JMh_CIECAM02(self): """ - Tests :func:`colour.models.cam02_ucs.UCS_Luo2006_to_JMh_CIECAM02` + Test :func:`colour.models.cam02_ucs.UCS_Luo2006_to_JMh_CIECAM02` definition domain and range scale support. """ Jpapbp = np.array([54.90433134, -0.08442362, -0.06848314]) JMh = UCS_Luo2006_to_JMh_CIECAM02( - Jpapbp, COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) - - d_r = (('reference', 1, 1), (1, 0.01, np.array([0.01, 0.01, 1 / 360])), - (100, 1, np.array([1, 1, 1 / 3.6]))) + Jpapbp, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ) + + d_r = ( + ("reference", 1, 1), + ("1", 0.01, np.array([0.01, 0.01, 1 / 360])), + ("100", 1, np.array([1, 1, 1 / 3.6])), + ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( UCS_Luo2006_to_JMh_CIECAM02( Jpapbp * factor_a, - COEFFICIENTS_UCS_LUO2006['CAM02-LCD']), + COEFFICIENTS_UCS_LUO2006["CAM02-LCD"], + ), JMh * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_UCS_Luo2006_to_JMh_CIECAM02(self): """ - Tests :func:`colour.models.cam02_ucs.UCS_Luo2006_to_JMh_CIECAM02` + Test :func:`colour.models.cam02_ucs.UCS_Luo2006_to_JMh_CIECAM02` definition nan support. """ @@ -269,9 +335,265 @@ def test_nan_UCS_Luo2006_to_JMh_CIECAM02(self): cases = set(permutations(cases * 3, r=3)) for case in cases: Jpapbp = np.array(case) - UCS_Luo2006_to_JMh_CIECAM02(Jpapbp, - COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) + UCS_Luo2006_to_JMh_CIECAM02( + Jpapbp, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ) + + +class TestXYZ_to_UCS_Luo2006(unittest.TestCase): + """ + Define :func:`colour.models.cam02_ucs.XYZ_to_UCS_Luo2006` definition + unit tests methods. + """ + + def test_XYZ_to_UCS_Luo2006(self): + """Test :func:`colour.models.cam02_ucs.XYZ_to_UCS_Luo2006` definition.""" + + np.testing.assert_almost_equal( + XYZ_to_UCS_Luo2006( + np.array([0.20654008, 0.12197225, 0.05136952]), + COEFFICIENTS_UCS_LUO2006["CAM02-LCD"], + ), + np.array([46.61386154, 39.35760236, 15.96730435]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_UCS_Luo2006( + np.array([0.20654008, 0.12197225, 0.05136952]), + COEFFICIENTS_UCS_LUO2006["CAM02-LCD"], + ), + XYZ_to_CAM02LCD(np.array([0.20654008, 0.12197225, 0.05136952])), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_UCS_Luo2006( + np.array([0.20654008, 0.12197225, 0.05136952]), + COEFFICIENTS_UCS_LUO2006["CAM02-SCD"], + ), + np.array([46.61386154, 25.62879882, 10.39755489]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_UCS_Luo2006( + np.array([0.20654008, 0.12197225, 0.05136952]), + COEFFICIENTS_UCS_LUO2006["CAM02-SCD"], + ), + XYZ_to_CAM02SCD(np.array([0.20654008, 0.12197225, 0.05136952])), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_UCS_Luo2006( + np.array([0.20654008, 0.12197225, 0.05136952]), + COEFFICIENTS_UCS_LUO2006["CAM02-UCS"], + ), + np.array([46.61386154, 29.88310013, 12.12351683]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_UCS_Luo2006( + np.array([0.20654008, 0.12197225, 0.05136952]), + COEFFICIENTS_UCS_LUO2006["CAM02-UCS"], + ), + XYZ_to_CAM02UCS(np.array([0.20654008, 0.12197225, 0.05136952])), + decimal=7, + ) + + def test_n_dimensional_XYZ_to_UCS_Luo2006(self): + """ + Test :func:`colour.models.cam02_ucs.XYZ_to_UCS_Luo2006` definition + n-dimensional support. + """ + + XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + Jpapbp = XYZ_to_UCS_Luo2006(XYZ, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"]) + + XYZ = np.tile(XYZ, (6, 1)) + Jpapbp = np.tile(Jpapbp, (6, 1)) + np.testing.assert_almost_equal( + XYZ_to_UCS_Luo2006(XYZ, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"]), + Jpapbp, + decimal=7, + ) + + XYZ = np.reshape(XYZ, (2, 3, 3)) + Jpapbp = np.reshape(Jpapbp, (2, 3, 3)) + np.testing.assert_almost_equal( + XYZ_to_UCS_Luo2006(XYZ, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"]), + Jpapbp, + decimal=7, + ) + + def test_domain_range_scale_XYZ_to_UCS_Luo2006(self): + """ + Test :func:`colour.models.cam02_ucs.XYZ_to_UCS_Luo2006` definition + domain and range scale support. + """ + + XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + XYZ_w = CAM_KWARGS_CIECAM02_sRGB["XYZ_w"] / 100 + Jpapbp = XYZ_to_UCS_Luo2006(XYZ, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"]) + + d_r = (("reference", 1, 1), ("1", 1, 0.01), ("100", 100, 1)) + for scale, factor_a, factor_b in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + XYZ_to_UCS_Luo2006( + XYZ * factor_a, + COEFFICIENTS_UCS_LUO2006["CAM02-LCD"], + XYZ_w=XYZ_w * factor_a, + ), + Jpapbp * factor_b, + decimal=7, + ) + + @ignore_numpy_errors + def test_nan_XYZ_to_UCS_Luo2006(self): + """ + Test :func:`colour.models.cam02_ucs.XYZ_to_UCS_Luo2006` definition nan + support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + XYZ = np.array(case) + XYZ_to_UCS_Luo2006(XYZ, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"]) + + +class TestUCS_Luo2006_to_XYZ(unittest.TestCase): + """ + Define :func:`colour.models.cam02_ucs.UCS_Luo2006_to_XYZ` definition + unit tests methods. + """ + + def test_UCS_Luo2006_to_XYZ(self): + """Test :func:`colour.models.cam02_ucs.UCS_Luo2006_to_XYZ` definition.""" + + np.testing.assert_almost_equal( + UCS_Luo2006_to_XYZ( + np.array([46.61386154, 39.35760236, 15.96730435]), + COEFFICIENTS_UCS_LUO2006["CAM02-LCD"], + ), + np.array([0.20654008, 0.12197225, 0.05136952]), + decimal=7, + ) + + np.testing.assert_almost_equal( + UCS_Luo2006_to_XYZ( + np.array([46.61386154, 39.35760236, 15.96730435]), + COEFFICIENTS_UCS_LUO2006["CAM02-LCD"], + ), + CAM02LCD_to_XYZ(np.array([46.61386154, 39.35760236, 15.96730435])), + decimal=7, + ) + + np.testing.assert_almost_equal( + UCS_Luo2006_to_XYZ( + np.array([46.61386154, 39.35760236, 15.96730435]), + COEFFICIENTS_UCS_LUO2006["CAM02-SCD"], + ), + np.array([0.28264475, 0.11036927, 0.00824593]), + decimal=7, + ) + + np.testing.assert_almost_equal( + UCS_Luo2006_to_XYZ( + np.array([46.61386154, 39.35760236, 15.96730435]), + COEFFICIENTS_UCS_LUO2006["CAM02-SCD"], + ), + CAM02SCD_to_XYZ(np.array([46.61386154, 39.35760236, 15.96730435])), + decimal=7, + ) + + np.testing.assert_almost_equal( + UCS_Luo2006_to_XYZ( + np.array([46.61386154, 39.35760236, 15.96730435]), + COEFFICIENTS_UCS_LUO2006["CAM02-UCS"], + ), + np.array([0.24229809, 0.11573005, 0.02517649]), + decimal=7, + ) + + np.testing.assert_almost_equal( + UCS_Luo2006_to_XYZ( + np.array([46.61386154, 39.35760236, 15.96730435]), + COEFFICIENTS_UCS_LUO2006["CAM02-UCS"], + ), + CAM02UCS_to_XYZ(np.array([46.61386154, 39.35760236, 15.96730435])), + decimal=7, + ) + + def test_n_dimensional_UCS_Luo2006_to_XYZ(self): + """ + Test :func:`colour.models.cam02_ucs.UCS_Luo2006_to_XYZ` definition + n-dimensional support. + """ + + Jpapbp = np.array([46.61386154, 39.35760236, 15.96730435]) + XYZ = UCS_Luo2006_to_XYZ(Jpapbp, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"]) + + Jpapbp = np.tile(Jpapbp, (6, 1)) + XYZ = np.tile(XYZ, (6, 1)) + np.testing.assert_almost_equal( + UCS_Luo2006_to_XYZ(Jpapbp, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"]), + XYZ, + decimal=7, + ) + + Jpapbp = np.reshape(Jpapbp, (2, 3, 3)) + XYZ = np.reshape(XYZ, (2, 3, 3)) + np.testing.assert_almost_equal( + UCS_Luo2006_to_XYZ(Jpapbp, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"]), + XYZ, + decimal=7, + ) + + def test_domain_range_scale_UCS_Luo2006_to_XYZ(self): + """ + Test :func:`colour.models.cam02_ucs.UCS_Luo2006_to_XYZ` definition + domain and range scale support. + """ + + Jpapbp = np.array([46.61386154, 39.35760236, 15.96730435]) + XYZ = UCS_Luo2006_to_XYZ(Jpapbp, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"]) + XYZ_w = CAM_KWARGS_CIECAM02_sRGB["XYZ_w"] / 100 + + d_r = (("reference", 1, 1, 1), ("1", 0.01, 1, 1), ("100", 1, 100, 100)) + for scale, factor_a, factor_b, factor_c in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + UCS_Luo2006_to_XYZ( + Jpapbp * factor_a, + COEFFICIENTS_UCS_LUO2006["CAM02-LCD"], + XYZ_w=XYZ_w * factor_c, + ), + XYZ * factor_b, + decimal=7, + ) + + @ignore_numpy_errors + def test_nan_UCS_Luo2006_to_XYZ(self): + """ + Test :func:`colour.models.cam02_ucs.UCS_Luo2006_to_XYZ` definition nan + support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + Jpapbp = np.array(case) + try: + UCS_Luo2006_to_XYZ( + Jpapbp, COEFFICIENTS_UCS_LUO2006["CAM02-LCD"] + ) + except ValueError as error: + attest("CAM_Specification_CIECAM02" in str(error)) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/tests/test_cam16_ucs.py b/colour/models/tests/test_cam16_ucs.py index cfba115813..5ec299ad92 100644 --- a/colour/models/tests/test_cam16_ucs.py +++ b/colour/models/tests/test_cam16_ucs.py @@ -1,28 +1,32 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.cam16_ucs` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.cam16_ucs` module.""" import unittest from colour.models.tests.test_cam02_ucs import ( - TestJMh_CIECAM02_to_UCS_Luo2006, TestUCS_Luo2006_to_JMh_CIECAM02) + TestJMh_CIECAM02_to_UCS_Luo2006, + TestUCS_Luo2006_to_JMh_CIECAM02, + TestXYZ_to_UCS_Luo2006, + TestUCS_Luo2006_to_XYZ, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestJMh_CAM16_to_UCS_Li2017', 'TestUCS_Li2017_to_JMh_CAM16'] +__all__ = [ + "TestJMh_CAM16_to_UCS_Li2017", + "TestUCS_Li2017_to_JMh_CAM16", + "TestXYZ_to_UCS_Li2017", + "TestUCS_Li2017_to_XYZ", +] class TestJMh_CAM16_to_UCS_Li2017(TestJMh_CIECAM02_to_UCS_Luo2006): """ - Defines :func:`colour.models.cam16_ucs.JMh_CAM16_to_UCS_Li2017` + Define :func:`colour.models.cam16_ucs.JMh_CAM16_to_UCS_Li2017` definition unit tests methods. Notes @@ -35,7 +39,7 @@ class TestJMh_CAM16_to_UCS_Li2017(TestJMh_CIECAM02_to_UCS_Luo2006): class TestUCS_Li2017_to_JMh_CAM16(TestUCS_Luo2006_to_JMh_CIECAM02): """ - Defines :func:`colour.models.cam16_ucs.UCS_Li2017_to_JMh_CAM16` + Define :func:`colour.models.cam16_ucs.UCS_Li2017_to_JMh_CAM16` definition unit tests methods. Notes @@ -46,5 +50,23 @@ class TestUCS_Li2017_to_JMh_CAM16(TestUCS_Luo2006_to_JMh_CIECAM02): """ -if __name__ == '__main__': +class TestXYZ_to_UCS_Li2017(TestXYZ_to_UCS_Luo2006): + """ + Define :func:`colour.models.cam16_ucs.XYZ_to_UCS_Li2017` + definition unit tests methods. + """ + + pass + + +class TestUCS_Li2017_to_XYZ(TestUCS_Luo2006_to_XYZ): + """ + Define :func:`colour.models.cam16_ucs.UCS_Li2017_to_XYZ` + definition unit tests methods. + """ + + pass + + +if __name__ == "__main__": unittest.main() diff --git a/colour/models/tests/test_cie_lab.py b/colour/models/tests/test_cie_lab.py index 4b8353a727..c5721f064e 100644 --- a/colour/models/tests/test_cie_lab.py +++ b/colour/models/tests/test_cie_lab.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.cie_lab` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.cie_lab` module.""" import numpy as np import unittest @@ -12,68 +7,78 @@ from colour.models import XYZ_to_Lab, Lab_to_XYZ, Lab_to_LCHab, LCHab_to_Lab from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestXYZ_to_Lab', 'TestLab_to_XYZ', 'TestLab_to_LCHab', 'TestLCHab_to_Lab' + "TestXYZ_to_Lab", + "TestLab_to_XYZ", + "TestLab_to_LCHab", + "TestLCHab_to_Lab", ] class TestXYZ_to_Lab(unittest.TestCase): """ - Defines :func:`colour.models.cie_lab.XYZ_to_Lab` definition unit tests + Define :func:`colour.models.cie_lab.XYZ_to_Lab` definition unit tests methods. """ def test_XYZ_to_Lab(self): - """ - Tests :func:`colour.models.cie_lab.XYZ_to_Lab` definition. - """ + """Test :func:`colour.models.cie_lab.XYZ_to_Lab` definition.""" np.testing.assert_almost_equal( XYZ_to_Lab(np.array([0.20654008, 0.12197225, 0.05136952])), np.array([41.52787529, 52.63858304, 26.92317922]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_Lab(np.array([0.14222010, 0.23042768, 0.10495772])), np.array([55.11636304, -41.08791787, 30.91825778]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_Lab(np.array([0.07818780, 0.06157201, 0.28099326])), np.array([29.80565520, 20.01830466, -48.34913874]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_Lab( np.array([0.20654008, 0.12197225, 0.05136952]), - np.array([0.44757, 0.40745])), + np.array([0.44757, 0.40745]), + ), np.array([41.52787529, 38.48089305, -5.73295122]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_Lab( np.array([0.20654008, 0.12197225, 0.05136952]), - np.array([0.34570, 0.35850])), + np.array([0.34570, 0.35850]), + ), np.array([41.52787529, 51.19354174, 19.91843098]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_Lab( np.array([0.20654008, 0.12197225, 0.05136952]), - np.array([0.34570, 0.35850, 1.00000])), + np.array([0.34570, 0.35850, 1.00000]), + ), np.array([41.52787529, 51.19354174, 19.91843098]), - decimal=7) + decimal=7, + ) def test_n_dimensional_XYZ_to_Lab(self): """ - Tests :func:`colour.models.cie_lab.XYZ_to_Lab` definition n-dimensional + Test :func:`colour.models.cie_lab.XYZ_to_Lab` definition n-dimensional support. """ @@ -84,21 +89,24 @@ def test_n_dimensional_XYZ_to_Lab(self): XYZ = np.tile(XYZ, (6, 1)) Lab = np.tile(Lab, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_Lab(XYZ, illuminant), Lab, decimal=7) + XYZ_to_Lab(XYZ, illuminant), Lab, decimal=7 + ) illuminant = np.tile(illuminant, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_Lab(XYZ, illuminant), Lab, decimal=7) + XYZ_to_Lab(XYZ, illuminant), Lab, decimal=7 + ) XYZ = np.reshape(XYZ, (2, 3, 3)) illuminant = np.reshape(illuminant, (2, 3, 2)) Lab = np.reshape(Lab, (2, 3, 3)) np.testing.assert_almost_equal( - XYZ_to_Lab(XYZ, illuminant), Lab, decimal=7) + XYZ_to_Lab(XYZ, illuminant), Lab, decimal=7 + ) def test_domain_range_scale_XYZ_to_Lab(self): """ - Tests :func:`colour.models.cie_lab.XYZ_to_Lab` definition + Test :func:`colour.models.cie_lab.XYZ_to_Lab` definition domain and range scale support. """ @@ -106,19 +114,18 @@ def test_domain_range_scale_XYZ_to_Lab(self): illuminant = np.array([0.31270, 0.32900]) Lab = XYZ_to_Lab(XYZ, illuminant) - d_r = (('reference', 1, 1), (1, 1, 0.01), (100, 100, 1)) + d_r = (("reference", 1, 1), ("1", 1, 0.01), ("100", 100, 1)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( XYZ_to_Lab(XYZ * factor_a, illuminant), Lab * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_XYZ_to_Lab(self): - """ - Tests :func:`colour.models.cie_lab.XYZ_to_Lab` definition nan support. - """ + """Test :func:`colour.models.cie_lab.XYZ_to_Lab` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -130,54 +137,61 @@ def test_nan_XYZ_to_Lab(self): class TestLab_to_XYZ(unittest.TestCase): """ - Defines :func:`colour.models.cie_lab.Lab_to_XYZ` definition unit tests + Define :func:`colour.models.cie_lab.Lab_to_XYZ` definition unit tests methods. """ def test_Lab_to_XYZ(self): - """ - Tests :func:`colour.models.cie_lab.Lab_to_XYZ` definition. - """ + """Test :func:`colour.models.cie_lab.Lab_to_XYZ` definition.""" np.testing.assert_almost_equal( Lab_to_XYZ(np.array([41.52787529, 52.63858304, 26.92317922])), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Lab_to_XYZ(np.array([55.11636304, -41.08791787, 30.91825778])), np.array([0.14222010, 0.23042768, 0.10495772]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Lab_to_XYZ(np.array([29.80565520, 20.01830466, -48.34913874])), np.array([0.07818780, 0.06157201, 0.28099326]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Lab_to_XYZ( np.array([41.52787529, 38.48089305, -5.73295122]), - np.array([0.44757, 0.40745])), + np.array([0.44757, 0.40745]), + ), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Lab_to_XYZ( np.array([41.52787529, 51.19354174, 19.91843098]), - np.array([0.34570, 0.35850])), + np.array([0.34570, 0.35850]), + ), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Lab_to_XYZ( np.array([41.52787529, 51.19354174, 19.91843098]), - np.array([0.34570, 0.35850, 1.00000])), + np.array([0.34570, 0.35850, 1.00000]), + ), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) def test_n_dimensional_Lab_to_XYZ(self): """ - Tests :func:`colour.models.cie_lab.Lab_to_XYZ` definition n-dimensional + Test :func:`colour.models.cie_lab.Lab_to_XYZ` definition n-dimensional support. """ @@ -188,21 +202,24 @@ def test_n_dimensional_Lab_to_XYZ(self): Lab = np.tile(Lab, (6, 1)) XYZ = np.tile(XYZ, (6, 1)) np.testing.assert_almost_equal( - Lab_to_XYZ(Lab, illuminant), XYZ, decimal=7) + Lab_to_XYZ(Lab, illuminant), XYZ, decimal=7 + ) illuminant = np.tile(illuminant, (6, 1)) np.testing.assert_almost_equal( - Lab_to_XYZ(Lab, illuminant), XYZ, decimal=7) + Lab_to_XYZ(Lab, illuminant), XYZ, decimal=7 + ) Lab = np.reshape(Lab, (2, 3, 3)) illuminant = np.reshape(illuminant, (2, 3, 2)) XYZ = np.reshape(XYZ, (2, 3, 3)) np.testing.assert_almost_equal( - Lab_to_XYZ(Lab, illuminant), XYZ, decimal=7) + Lab_to_XYZ(Lab, illuminant), XYZ, decimal=7 + ) def test_domain_range_scale_Lab_to_XYZ(self): """ - Tests :func:`colour.models.cie_lab.Lab_to_XYZ` definition + Test :func:`colour.models.cie_lab.Lab_to_XYZ` definition domain and range scale support. """ @@ -210,19 +227,18 @@ def test_domain_range_scale_Lab_to_XYZ(self): illuminant = np.array([0.31270, 0.32900]) XYZ = Lab_to_XYZ(Lab, illuminant) - d_r = (('reference', 1, 1), (1, 0.01, 1), (100, 1, 100)) + d_r = (("reference", 1, 1), ("1", 0.01, 1), ("100", 1, 100)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( Lab_to_XYZ(Lab * factor_a, illuminant), XYZ * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_Lab_to_XYZ(self): - """ - Tests :func:`colour.models.cie_lab.Lab_to_XYZ` definition nan support. - """ + """Test :func:`colour.models.cie_lab.Lab_to_XYZ` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -234,33 +250,34 @@ def test_nan_Lab_to_XYZ(self): class TestLab_to_LCHab(unittest.TestCase): """ - Defines :func:`colour.models.cie_lab.Lab_to_LCHab` definition unit tests + Define :func:`colour.models.cie_lab.Lab_to_LCHab` definition unit tests methods. """ def test_Lab_to_LCHab(self): - """ - Tests :func:`colour.models.cie_lab.Lab_to_LCHab` definition. - """ + """Test :func:`colour.models.cie_lab.Lab_to_LCHab` definition.""" np.testing.assert_almost_equal( Lab_to_LCHab(np.array([41.52787529, 52.63858304, 26.92317922])), np.array([41.52787529, 59.12425901, 27.08848784]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Lab_to_LCHab(np.array([55.11636304, -41.08791787, 30.91825778])), np.array([55.11636304, 51.42135412, 143.03889556]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Lab_to_LCHab(np.array([29.80565520, 20.01830466, -48.34913874])), np.array([29.80565520, 52.32945383, 292.49133666]), - decimal=7) + decimal=7, + ) def test_n_dimensional_Lab_to_LCHab(self): """ - Tests :func:`colour.models.cie_lab.Lab_to_LCHab` definition + Test :func:`colour.models.cie_lab.Lab_to_LCHab` definition n-dimensional arrays support. """ @@ -277,24 +294,28 @@ def test_n_dimensional_Lab_to_LCHab(self): def test_domain_range_scale_Lab_to_LCHab(self): """ - Tests :func:`colour.models.cie_lab.Lab_to_LCHab` definition domain and + Test :func:`colour.models.cie_lab.Lab_to_LCHab` definition domain and range scale support. """ Lab = np.array([41.52787529, 52.63858304, 26.92317922]) LCHab = Lab_to_LCHab(Lab) - d_r = (('reference', 1, 1), (1, 0.01, np.array([0.01, 0.01, 1 / 360])), - (100, 1, np.array([1, 1, 1 / 3.6]))) + d_r = ( + ("reference", 1, 1), + ("1", 0.01, np.array([0.01, 0.01, 1 / 360])), + ("100", 1, np.array([1, 1, 1 / 3.6])), + ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - Lab_to_LCHab(Lab * factor_a), LCHab * factor_b, decimal=7) + Lab_to_LCHab(Lab * factor_a), LCHab * factor_b, decimal=7 + ) @ignore_numpy_errors def test_nan_Lab_to_LCHab(self): """ - Tests :func:`colour.models.cie_lab.Lab_to_LCHab` definition nan + Test :func:`colour.models.cie_lab.Lab_to_LCHab` definition nan support. """ @@ -307,33 +328,34 @@ def test_nan_Lab_to_LCHab(self): class TestLCHab_to_Lab(unittest.TestCase): """ - Defines :func:`colour.models.cie_lab.LCHab_to_Lab` definition unit tests + Define :func:`colour.models.cie_lab.LCHab_to_Lab` definition unit tests methods. """ def test_LCHab_to_Lab(self): - """ - Tests :func:`colour.models.cie_lab.LCHab_to_Lab` definition. - """ + """Test :func:`colour.models.cie_lab.LCHab_to_Lab` definition.""" np.testing.assert_almost_equal( LCHab_to_Lab(np.array([41.52787529, 59.12425901, 27.08848784])), np.array([41.52787529, 52.63858304, 26.92317922]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( LCHab_to_Lab(np.array([55.11636304, 51.42135412, 143.03889556])), np.array([55.11636304, -41.08791787, 30.91825778]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( LCHab_to_Lab(np.array([29.80565520, 52.32945383, 292.49133666])), np.array([29.80565520, 20.01830466, -48.34913874]), - decimal=7) + decimal=7, + ) def test_n_dimensional_LCHab_to_Lab(self): """ - Tests :func:`colour.models.cie_lab.LCHab_to_Lab` definition + Test :func:`colour.models.cie_lab.LCHab_to_Lab` definition n-dimensional arrays support. """ @@ -350,24 +372,28 @@ def test_n_dimensional_LCHab_to_Lab(self): def test_domain_range_scale_LCHab_to_Lab(self): """ - Tests :func:`colour.models.cie_lab.LCHab_to_Lab` definition domain and + Test :func:`colour.models.cie_lab.LCHab_to_Lab` definition domain and range scale support. """ LCHab = np.array([41.52787529, 59.12425901, 27.08848784]) Lab = LCHab_to_Lab(LCHab) - d_r = (('reference', 1, 1), (1, np.array([0.01, 0.01, 1 / 360]), 0.01), - (100, np.array([1, 1, 1 / 3.6]), 1)) + d_r = ( + ("reference", 1, 1), + ("1", np.array([0.01, 0.01, 1 / 360]), 0.01), + ("100", np.array([1, 1, 1 / 3.6]), 1), + ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - LCHab_to_Lab(LCHab * factor_a), Lab * factor_b, decimal=7) + LCHab_to_Lab(LCHab * factor_a), Lab * factor_b, decimal=7 + ) @ignore_numpy_errors def test_nan_LCHab_to_Lab(self): """ - Tests :func:`colour.models.cie_lab.LCHab_to_Lab` definition nan + Test :func:`colour.models.cie_lab.LCHab_to_Lab` definition nan support. """ @@ -378,5 +404,5 @@ def test_nan_LCHab_to_Lab(self): LCHab_to_Lab(LCHab) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/tests/test_cie_luv.py b/colour/models/tests/test_cie_luv.py index 9a52aec738..329343e7bb 100644 --- a/colour/models/tests/test_cie_luv.py +++ b/colour/models/tests/test_cie_luv.py @@ -1,83 +1,97 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.cie_luv` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.cie_luv` module.""" import numpy as np import unittest from itertools import permutations -from colour.models import (XYZ_to_Luv, Luv_to_XYZ, Luv_to_uv, uv_to_Luv, - Luv_uv_to_xy, xy_to_Luv_uv, Luv_to_LCHuv, - LCHuv_to_Luv) +from colour.models import ( + XYZ_to_Luv, + Luv_to_XYZ, + Luv_to_uv, + uv_to_Luv, + Luv_uv_to_xy, + xy_to_Luv_uv, + Luv_to_LCHuv, + LCHuv_to_Luv, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestXYZ_to_Luv', 'TestLuv_to_XYZ', 'TestLuv_to_uv', 'Testuv_to_Luv', - 'TestLuv_uv_to_xy', 'TestXy_to_Luv_uv', 'TestLuv_to_LCHuv', - 'TestLCHuv_to_Luv' + "TestXYZ_to_Luv", + "TestLuv_to_XYZ", + "TestLuv_to_uv", + "Testuv_to_Luv", + "TestLuv_uv_to_xy", + "TestXy_to_Luv_uv", + "TestLuv_to_LCHuv", + "TestLCHuv_to_Luv", ] class TestXYZ_to_Luv(unittest.TestCase): """ - Defines :func:`colour.models.cie_luv.XYZ_to_Luv` definition unit tests + Define :func:`colour.models.cie_luv.XYZ_to_Luv` definition unit tests methods. """ def test_XYZ_to_Luv(self): - """ - Tests :func:`colour.models.cie_luv.XYZ_to_Luv` definition. - """ + """Test :func:`colour.models.cie_luv.XYZ_to_Luv` definition.""" np.testing.assert_almost_equal( XYZ_to_Luv(np.array([0.20654008, 0.12197225, 0.05136952])), np.array([41.52787529, 96.83626054, 17.75210149]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_Luv(np.array([0.14222010, 0.23042768, 0.10495772])), np.array([55.11636304, -37.59308176, 44.13768458]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_Luv(np.array([0.07818780, 0.06157201, 0.28099326])), np.array([29.80565520, -10.96316802, -65.06751860]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_Luv( np.array([0.20654008, 0.12197225, 0.05136952]), - np.array([0.44757, 0.40745])), + np.array([0.44757, 0.40745]), + ), np.array([41.52787529, 65.45180940, -12.46626977]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_Luv( np.array([0.20654008, 0.12197225, 0.05136952]), - np.array([0.34570, 0.35850])), + np.array([0.34570, 0.35850]), + ), np.array([41.52787529, 90.70925962, 7.08455273]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_Luv( np.array([0.20654008, 0.12197225, 0.05136952]), - np.array([0.34570, 0.35850, 1.00000])), + np.array([0.34570, 0.35850, 1.00000]), + ), np.array([41.52787529, 90.70925962, 7.08455273]), - decimal=7) + decimal=7, + ) def test_n_dimensional_XYZ_to_Luv(self): """ - Tests :func:`colour.models.cie_luv.XYZ_to_Luv` definition n-dimensional + Test :func:`colour.models.cie_luv.XYZ_to_Luv` definition n-dimensional support. """ @@ -88,21 +102,24 @@ def test_n_dimensional_XYZ_to_Luv(self): XYZ = np.tile(XYZ, (6, 1)) Luv = np.tile(Luv, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_Luv(XYZ, illuminant), Luv, decimal=7) + XYZ_to_Luv(XYZ, illuminant), Luv, decimal=7 + ) illuminant = np.tile(illuminant, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_Luv(XYZ, illuminant), Luv, decimal=7) + XYZ_to_Luv(XYZ, illuminant), Luv, decimal=7 + ) XYZ = np.reshape(XYZ, (2, 3, 3)) illuminant = np.reshape(illuminant, (2, 3, 2)) Luv = np.reshape(Luv, (2, 3, 3)) np.testing.assert_almost_equal( - XYZ_to_Luv(XYZ, illuminant), Luv, decimal=7) + XYZ_to_Luv(XYZ, illuminant), Luv, decimal=7 + ) def test_domain_range_scale_XYZ_to_Luv(self): """ - Tests :func:`colour.models.cie_luv.XYZ_to_Luv` definition + Test :func:`colour.models.cie_luv.XYZ_to_Luv` definition domain and range scale support. """ @@ -110,19 +127,18 @@ def test_domain_range_scale_XYZ_to_Luv(self): illuminant = np.array([0.31270, 0.32900]) Luv = XYZ_to_Luv(XYZ, illuminant) - d_r = (('reference', 1, 1), (1, 1, 0.01), (100, 100, 1)) + d_r = (("reference", 1, 1), ("1", 1, 0.01), ("100", 100, 1)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( XYZ_to_Luv(XYZ * factor_a, illuminant), Luv * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_XYZ_to_Luv(self): - """ - Tests :func:`colour.models.cie_luv.XYZ_to_Luv` definition nan support. - """ + """Test :func:`colour.models.cie_luv.XYZ_to_Luv` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -134,54 +150,61 @@ def test_nan_XYZ_to_Luv(self): class TestLuv_to_XYZ(unittest.TestCase): """ - Defines :func:`colour.models.cie_luv.Luv_to_XYZ` definition unit tests + Define :func:`colour.models.cie_luv.Luv_to_XYZ` definition unit tests methods. """ def test_Luv_to_XYZ(self): - """ - Tests :func:`colour.models.cie_luv.Luv_to_XYZ` definition. - """ + """Test :func:`colour.models.cie_luv.Luv_to_XYZ` definition.""" np.testing.assert_almost_equal( Luv_to_XYZ(np.array([41.52787529, 96.83626054, 17.75210149])), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Luv_to_XYZ(np.array([55.11636304, -37.59308176, 44.13768458])), np.array([0.14222010, 0.23042768, 0.10495772]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Luv_to_XYZ(np.array([29.80565520, -10.96316802, -65.06751860])), np.array([0.07818780, 0.06157201, 0.28099326]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Luv_to_XYZ( np.array([41.52787529, 65.45180940, -12.46626977]), - np.array([0.44757, 0.40745])), + np.array([0.44757, 0.40745]), + ), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Luv_to_XYZ( np.array([41.52787529, 90.70925962, 7.08455273]), - np.array([0.34570, 0.35850])), + np.array([0.34570, 0.35850]), + ), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Luv_to_XYZ( np.array([41.52787529, 90.70925962, 7.08455273]), - np.array([0.34570, 0.35850, 1.00000])), + np.array([0.34570, 0.35850, 1.00000]), + ), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) def test_n_dimensional_Luv_to_XYZ(self): """ - Tests :func:`colour.models.cie_luv.Luv_to_XYZ` definition n-dimensional + Test :func:`colour.models.cie_luv.Luv_to_XYZ` definition n-dimensional support. """ @@ -192,21 +215,24 @@ def test_n_dimensional_Luv_to_XYZ(self): Luv = np.tile(Luv, (6, 1)) XYZ = np.tile(XYZ, (6, 1)) np.testing.assert_almost_equal( - Luv_to_XYZ(Luv, illuminant), XYZ, decimal=7) + Luv_to_XYZ(Luv, illuminant), XYZ, decimal=7 + ) illuminant = np.tile(illuminant, (6, 1)) np.testing.assert_almost_equal( - Luv_to_XYZ(Luv, illuminant), XYZ, decimal=7) + Luv_to_XYZ(Luv, illuminant), XYZ, decimal=7 + ) Luv = np.reshape(Luv, (2, 3, 3)) illuminant = np.reshape(illuminant, (2, 3, 2)) XYZ = np.reshape(XYZ, (2, 3, 3)) np.testing.assert_almost_equal( - Luv_to_XYZ(Luv, illuminant), XYZ, decimal=7) + Luv_to_XYZ(Luv, illuminant), XYZ, decimal=7 + ) def test_domain_range_scale_Luv_to_XYZ(self): """ - Tests :func:`colour.models.cie_luv.Luv_to_XYZ` definition + Test :func:`colour.models.cie_luv.Luv_to_XYZ` definition domain and range scale support. """ @@ -214,19 +240,18 @@ def test_domain_range_scale_Luv_to_XYZ(self): illuminant = np.array([0.31270, 0.32900]) XYZ = Luv_to_XYZ(Luv, illuminant) - d_r = (('reference', 1, 1), (1, 0.01, 1), (100, 1, 100)) + d_r = (("reference", 1, 1), ("1", 0.01, 1), ("100", 1, 100)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( Luv_to_XYZ(Luv * factor_a, illuminant), XYZ * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_Luv_to_XYZ(self): - """ - Tests :func:`colour.models.cie_luv.Luv_to_XYZ` definition nan support. - """ + """Test :func:`colour.models.cie_luv.Luv_to_XYZ` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -238,54 +263,61 @@ def test_nan_Luv_to_XYZ(self): class TestLuv_to_uv(unittest.TestCase): """ - Defines :func:`colour.models.cie_luv.Luv_to_uv` definition unit tests + Define :func:`colour.models.cie_luv.Luv_to_uv` definition unit tests methods. """ def test_Luv_to_uv(self): - """ - Tests :func:`colour.models.cie_luv.Luv_to_uv` definition. - """ + """Test :func:`colour.models.cie_luv.Luv_to_uv` definition.""" np.testing.assert_almost_equal( Luv_to_uv(np.array([41.52787529, 96.83626054, 17.75210149])), np.array([0.37720213, 0.50120264]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Luv_to_uv(np.array([55.11636304, -37.59308176, 44.13768458])), np.array([0.14536327, 0.52992069]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Luv_to_uv(np.array([29.80565520, -10.96316802, -65.06751860])), np.array([0.16953603, 0.30039234]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Luv_to_uv( np.array([41.52787529, 65.45180940, -12.46626977]), - np.array([0.44757, 0.40745])), + np.array([0.44757, 0.40745]), + ), np.array([0.37720213, 0.50120264]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Luv_to_uv( np.array([41.52787529, 90.70925962, 7.08455273]), - np.array([0.34570, 0.35850])), + np.array([0.34570, 0.35850]), + ), np.array([0.37720213, 0.50120264]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Luv_to_uv( np.array([41.52787529, 90.70925962, 7.08455273]), - np.array([0.34570, 0.35850, 1.00000])), + np.array([0.34570, 0.35850, 1.00000]), + ), np.array([0.37720213, 0.50120264]), - decimal=7) + decimal=7, + ) def test_n_dimensional_Luv_to_uv(self): """ - Tests :func:`colour.models.cie_luv.Luv_to_uv` definition n-dimensional + Test :func:`colour.models.cie_luv.Luv_to_uv` definition n-dimensional support. """ @@ -296,21 +328,24 @@ def test_n_dimensional_Luv_to_uv(self): Luv = np.tile(Luv, (6, 1)) uv = np.tile(uv, (6, 1)) np.testing.assert_almost_equal( - Luv_to_uv(Luv, illuminant), uv, decimal=7) + Luv_to_uv(Luv, illuminant), uv, decimal=7 + ) illuminant = np.tile(illuminant, (6, 1)) np.testing.assert_almost_equal( - Luv_to_uv(Luv, illuminant), uv, decimal=7) + Luv_to_uv(Luv, illuminant), uv, decimal=7 + ) Luv = np.reshape(Luv, (2, 3, 3)) illuminant = np.reshape(illuminant, (2, 3, 2)) uv = np.reshape(uv, (2, 3, 2)) np.testing.assert_almost_equal( - Luv_to_uv(Luv, illuminant), uv, decimal=7) + Luv_to_uv(Luv, illuminant), uv, decimal=7 + ) def test_domain_range_scale_Luv_to_uv(self): """ - Tests :func:`colour.models.cie_luv.Luv_to_uv` definition + Test :func:`colour.models.cie_luv.Luv_to_uv` definition domain and range scale support. """ @@ -318,17 +353,16 @@ def test_domain_range_scale_Luv_to_uv(self): illuminant = np.array([0.31270, 0.32900]) uv = Luv_to_uv(Luv, illuminant) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - Luv_to_uv(Luv * factor, illuminant), uv, decimal=7) + Luv_to_uv(Luv * factor, illuminant), uv, decimal=7 + ) @ignore_numpy_errors def test_nan_Luv_to_uv(self): - """ - Tests :func:`colour.models.cie_luv.Luv_to_uv` definition nan support. - """ + """Test :func:`colour.models.cie_luv.Luv_to_uv` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -340,59 +374,67 @@ def test_nan_Luv_to_uv(self): class Testuv_to_Luv(unittest.TestCase): """ - Defines :func:`colour.models.cie_luv.uv_to_Luv` definition unit tests + Define :func:`colour.models.cie_luv.uv_to_Luv` definition unit tests methods. """ def test_uv_to_Luv(self): - """ - Tests :func:`colour.models.cie_luv.uv_to_Luv` definition. - """ + """Test :func:`colour.models.cie_luv.uv_to_Luv` definition.""" np.testing.assert_almost_equal( uv_to_Luv(np.array([0.37720213, 0.50120264])), np.array([100.00000000, 233.18376036, 42.74743858]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( uv_to_Luv(np.array([0.14536327, 0.52992069])), np.array([100.00000000, -68.20675764, 80.08090358]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( uv_to_Luv(np.array([0.16953603, 0.30039234])), np.array([100.00000000, -36.78216964, -218.3059514]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( uv_to_Luv( np.array([0.37720213, 0.50120264]), - np.array([0.44757, 0.40745])), + np.array([0.44757, 0.40745]), + ), np.array([100.00000000, 157.60933976, -30.01903705]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( uv_to_Luv( np.array([0.37720213, 0.50120264]), - np.array([0.34570, 0.35850])), + np.array([0.34570, 0.35850]), + ), np.array([100.00000000, 218.42981284, 17.05975609]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( uv_to_Luv( np.array([0.37720213, 0.50120264]), - np.array([0.34570, 0.35850, 1.00000])), + np.array([0.34570, 0.35850, 1.00000]), + ), np.array([100.00000000, 218.42981284, 17.05975609]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( uv_to_Luv(np.array([0.37720213, 0.50120264]), Y=0.18), np.array([49.49610761, 115.41688496, -243.29048251]), - decimal=7) + decimal=7, + ) def test_n_dimensional_uv_to_Luv(self): """ - Tests :func:`colour.models.cie_luv.uv_to_Luv` definition n-dimensional + Test :func:`colour.models.cie_luv.uv_to_Luv` definition n-dimensional support. """ @@ -403,21 +445,24 @@ def test_n_dimensional_uv_to_Luv(self): uv = np.tile(uv, (6, 1)) Luv = np.tile(Luv, (6, 1)) np.testing.assert_almost_equal( - uv_to_Luv(uv, illuminant), Luv, decimal=7) + uv_to_Luv(uv, illuminant), Luv, decimal=7 + ) illuminant = np.tile(illuminant, (6, 1)) np.testing.assert_almost_equal( - uv_to_Luv(uv, illuminant), Luv, decimal=7) + uv_to_Luv(uv, illuminant), Luv, decimal=7 + ) uv = np.reshape(uv, (2, 3, 2)) illuminant = np.reshape(illuminant, (2, 3, 2)) Luv = np.reshape(Luv, (2, 3, 3)) np.testing.assert_almost_equal( - uv_to_Luv(uv, illuminant), Luv, decimal=7) + uv_to_Luv(uv, illuminant), Luv, decimal=7 + ) def test_domain_range_scale_uv_to_Luv(self): """ - Tests :func:`colour.models.cie_luv.uv_to_Luv` definition + Test :func:`colour.models.cie_luv.uv_to_Luv` definition domain and range scale support. """ @@ -426,19 +471,18 @@ def test_domain_range_scale_uv_to_Luv(self): Y = 1 Luv = uv_to_Luv(uv, illuminant, Y) - d_r = (('reference', 1, 1), (1, 1, 0.01), (100, 100, 1)) + d_r = (("reference", 1, 1), ("1", 1, 0.01), ("100", 100, 1)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( uv_to_Luv(uv, illuminant, Y * factor_a), Luv * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_uv_to_Luv(self): - """ - Tests :func:`colour.models.cie_luv.uv_to_Luv` definition nan support. - """ + """Test :func:`colour.models.cie_luv.uv_to_Luv` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=2)) @@ -450,33 +494,34 @@ def test_nan_uv_to_Luv(self): class TestLuv_uv_to_xy(unittest.TestCase): """ - Defines :func:`colour.models.cie_luv.Luv_uv_to_xy` definition unit tests + Define :func:`colour.models.cie_luv.Luv_uv_to_xy` definition unit tests methods. """ def test_Luv_uv_to_xy(self): - """ - Tests :func:`colour.models.cie_luv.Luv_uv_to_xy` definition. - """ + """Test :func:`colour.models.cie_luv.Luv_uv_to_xy` definition.""" np.testing.assert_almost_equal( Luv_uv_to_xy(np.array([0.37720213, 0.50120264])), np.array([0.54369558, 0.32107944]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Luv_uv_to_xy(np.array([0.14536327, 0.52992069])), np.array([0.29777734, 0.48246445]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Luv_uv_to_xy(np.array([0.16953603, 0.30039234])), np.array([0.18582824, 0.14633764]), - decimal=7) + decimal=7, + ) def test_n_dimensional_Luv_uv_to_xy(self): """ - Tests :func:`colour.models.cie_luv.Luv_uv_to_xy` definition + Test :func:`colour.models.cie_luv.Luv_uv_to_xy` definition n-dimensional arrays support. """ @@ -494,7 +539,7 @@ def test_n_dimensional_Luv_uv_to_xy(self): @ignore_numpy_errors def test_nan_Luv_uv_to_xy(self): """ - Tests :func:`colour.models.cie_luv.Luv_uv_to_xy` definition nan + Test :func:`colour.models.cie_luv.Luv_uv_to_xy` definition nan support. """ @@ -507,33 +552,34 @@ def test_nan_Luv_uv_to_xy(self): class TestXy_to_Luv_uv(unittest.TestCase): """ - Defines :func:`colour.models.cie_luv.xy_to_Luv_uv` definition unit tests + Define :func:`colour.models.cie_luv.xy_to_Luv_uv` definition unit tests methods. """ def test_xy_to_Luv_uv(self): - """ - Tests :func:`colour.models.cie_luv.xy_to_Luv_uv` definition. - """ + """Test :func:`colour.models.cie_luv.xy_to_Luv_uv` definition.""" np.testing.assert_almost_equal( xy_to_Luv_uv(np.array([0.54369558, 0.32107944])), np.array([0.37720213, 0.50120264]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( xy_to_Luv_uv(np.array([0.29777734, 0.48246445])), np.array([0.14536327, 0.52992069]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( xy_to_Luv_uv(np.array([0.18582824, 0.14633764])), np.array([0.16953603, 0.30039234]), - decimal=7) + decimal=7, + ) def test_n_dimensional_xy_to_Luv_uv(self): """ - Tests :func:`colour.models.cie_luv.xy_to_Luv_uv` definition + Test :func:`colour.models.cie_luv.xy_to_Luv_uv` definition n-dimensional arrays support. """ @@ -551,7 +597,7 @@ def test_n_dimensional_xy_to_Luv_uv(self): @ignore_numpy_errors def test_nan_xy_to_Luv_uv(self): """ - Tests :func:`colour.models.cie_luv.xy_to_Luv_uv` definition nan + Test :func:`colour.models.cie_luv.xy_to_Luv_uv` definition nan support. """ @@ -564,33 +610,34 @@ def test_nan_xy_to_Luv_uv(self): class TestLuv_to_LCHuv(unittest.TestCase): """ - Defines :func:`colour.models.cie_luv.Luv_to_LCHuv` definition unit tests + Define :func:`colour.models.cie_luv.Luv_to_LCHuv` definition unit tests methods. """ def test_Luv_to_LCHuv(self): - """ - Tests :func:`colour.models.cie_luv.Luv_to_LCHuv` definition. - """ + """Test :func:`colour.models.cie_luv.Luv_to_LCHuv` definition.""" np.testing.assert_almost_equal( Luv_to_LCHuv(np.array([41.52787529, 96.83626054, 17.75210149])), np.array([41.52787529, 98.44997950, 10.38816348]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Luv_to_LCHuv(np.array([55.11636304, -37.59308176, 44.13768458])), np.array([55.11636304, 57.97736624, 130.42180076]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Luv_to_LCHuv(np.array([29.80565520, -10.96316802, -65.06751860])), np.array([29.80565520, 65.98464238, 260.43611196]), - decimal=7) + decimal=7, + ) def test_n_dimensional_Luv_to_LCHuv(self): """ - Tests :func:`colour.models.cie_luv.Luv_to_LCHuv` definition + Test :func:`colour.models.cie_luv.Luv_to_LCHuv` definition n-dimensional arrays support. """ @@ -607,24 +654,28 @@ def test_n_dimensional_Luv_to_LCHuv(self): def test_domain_range_scale_Luv_to_LCHuv(self): """ - Tests :func:`colour.models.cie_luv.Luv_to_LCHuv` definition domain and + Test :func:`colour.models.cie_luv.Luv_to_LCHuv` definition domain and range scale support. """ Luv = np.array([41.52787529, 96.83626054, 17.75210149]) LCHuv = Luv_to_LCHuv(Luv) - d_r = (('reference', 1, 1), (1, 0.01, np.array([0.01, 0.01, 1 / 360])), - (100, 1, np.array([1, 1, 1 / 3.6]))) + d_r = ( + ("reference", 1, 1), + ("1", 0.01, np.array([0.01, 0.01, 1 / 360])), + ("100", 1, np.array([1, 1, 1 / 3.6])), + ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - Luv_to_LCHuv(Luv * factor_a), LCHuv * factor_b, decimal=7) + Luv_to_LCHuv(Luv * factor_a), LCHuv * factor_b, decimal=7 + ) @ignore_numpy_errors def test_nan_Luv_to_LCHuv(self): """ - Tests :func:`colour.models.cie_luv.Luv_to_LCHuv` definition nan + Test :func:`colour.models.cie_luv.Luv_to_LCHuv` definition nan support. """ @@ -637,33 +688,34 @@ def test_nan_Luv_to_LCHuv(self): class TestLCHuv_to_Luv(unittest.TestCase): """ - Defines :func:`colour.models.cie_luv.LCHuv_to_Luv` definition unit tests + Define :func:`colour.models.cie_luv.LCHuv_to_Luv` definition unit tests methods. """ def test_LCHuv_to_Luv(self): - """ - Tests :func:`colour.models.cie_luv.LCHuv_to_Luv` definition. - """ + """Test :func:`colour.models.cie_luv.LCHuv_to_Luv` definition.""" np.testing.assert_almost_equal( LCHuv_to_Luv(np.array([41.52787529, 98.44997950, 10.38816348])), np.array([41.52787529, 96.83626054, 17.75210149]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( LCHuv_to_Luv(np.array([55.11636304, 57.97736624, 130.42180076])), np.array([55.11636304, -37.59308176, 44.13768458]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( LCHuv_to_Luv(np.array([29.80565520, 65.98464238, 260.43611196])), np.array([29.80565520, -10.96316802, -65.06751860]), - decimal=7) + decimal=7, + ) def test_n_dimensional_LCHuv_to_Luv(self): """ - Tests :func:`colour.models.cie_luv.LCHuv_to_Luv` definition + Test :func:`colour.models.cie_luv.LCHuv_to_Luv` definition n-dimensional arrays support. """ @@ -680,24 +732,28 @@ def test_n_dimensional_LCHuv_to_Luv(self): def test_domain_range_scale_LCHuv_to_Lab(self): """ - Tests :func:`colour.models.cie_luv.LCHuv_to_Luv` definition domain and + Test :func:`colour.models.cie_luv.LCHuv_to_Luv` definition domain and range scale support. """ LCHuv = np.array([41.52787529, 98.44997950, 10.38816348]) Luv = LCHuv_to_Luv(LCHuv) - d_r = (('reference', 1, 1), (1, np.array([0.01, 0.01, 1 / 360]), 0.01), - (100, np.array([1, 1, 1 / 3.6]), 1)) + d_r = ( + ("reference", 1, 1), + ("1", np.array([0.01, 0.01, 1 / 360]), 0.01), + ("100", np.array([1, 1, 1 / 3.6]), 1), + ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - LCHuv_to_Luv(LCHuv * factor_a), Luv * factor_b, decimal=7) + LCHuv_to_Luv(LCHuv * factor_a), Luv * factor_b, decimal=7 + ) @ignore_numpy_errors def test_nan_LCHuv_to_Luv(self): """ - Tests :func:`colour.models.cie_luv.LCHuv_to_Luv` definition nan + Test :func:`colour.models.cie_luv.LCHuv_to_Luv` definition nan support. """ @@ -708,5 +764,5 @@ def test_nan_LCHuv_to_Luv(self): LCHuv_to_Luv(LCHuv) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/tests/test_cie_ucs.py b/colour/models/tests/test_cie_ucs.py index 61f66e148d..c042ec3266 100644 --- a/colour/models/tests/test_cie_ucs.py +++ b/colour/models/tests/test_cie_ucs.py @@ -1,60 +1,66 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.cie_ucs` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.cie_ucs` module.""" import numpy as np import unittest from itertools import permutations -from colour.models import (XYZ_to_UCS, UCS_to_XYZ, UCS_to_uv, uv_to_UCS, - UCS_uv_to_xy, xy_to_UCS_uv) +from colour.models import ( + XYZ_to_UCS, + UCS_to_XYZ, + UCS_to_uv, + uv_to_UCS, + UCS_uv_to_xy, + xy_to_UCS_uv, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestXYZ_to_UCS', 'TestUCS_to_XYZ', 'TestUCS_to_uv', 'Testuv_to_UCS', - 'TestUCS_uv_to_xy', 'TestXy_to_UCS_uv' + "TestXYZ_to_UCS", + "TestUCS_to_XYZ", + "TestUCS_to_uv", + "Testuv_to_UCS", + "TestUCS_uv_to_xy", + "TestXy_to_UCS_uv", ] class TestXYZ_to_UCS(unittest.TestCase): """ - Defines :func:`colour.models.cie_ucs.XYZ_to_UCS` definition unit tests + Define :func:`colour.models.cie_ucs.XYZ_to_UCS` definition unit tests methods. """ def test_XYZ_to_UCS(self): - """ - Tests :func:`colour.models.cie_ucs.XYZ_to_UCS` definition. - """ + """Test :func:`colour.models.cie_ucs.XYZ_to_UCS` definition.""" np.testing.assert_almost_equal( XYZ_to_UCS(np.array([0.20654008, 0.12197225, 0.05136952])), np.array([0.13769339, 0.12197225, 0.10537310]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_UCS(np.array([0.14222010, 0.23042768, 0.10495772])), np.array([0.09481340, 0.23042768, 0.32701033]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_UCS(np.array([0.07818780, 0.06157201, 0.28099326])), np.array([0.05212520, 0.06157201, 0.19376075]), - decimal=7) + decimal=7, + ) def test_n_dimensional_XYZ_to_UCS(self): """ - Tests :func:`colour.models.cie_ucs.XYZ_to_UCS` definition n-dimensional + Test :func:`colour.models.cie_ucs.XYZ_to_UCS` definition n-dimensional support. """ @@ -71,24 +77,23 @@ def test_n_dimensional_XYZ_to_UCS(self): def test_domain_range_scale_XYZ_to_UCS(self): """ - Tests :func:`colour.models.cie_ucs.XYZ_to_UCS` definition domain and + Test :func:`colour.models.cie_ucs.XYZ_to_UCS` definition domain and range scale support. """ XYZ = np.array([0.0704953400, 0.1008000000, 0.0955831300]) UCS = XYZ_to_UCS(XYZ) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - XYZ_to_UCS(XYZ * factor), UCS * factor, decimal=7) + XYZ_to_UCS(XYZ * factor), UCS * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_XYZ_to_UCS(self): - """ - Tests :func:`colour.models.cie_ucs.XYZ_to_UCS` definition nan support. - """ + """Test :func:`colour.models.cie_ucs.XYZ_to_UCS` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -99,33 +104,34 @@ def test_nan_XYZ_to_UCS(self): class TestUCS_to_XYZ(unittest.TestCase): """ - Defines :func:`colour.models.cie_ucs.UCS_to_XYZ` definition unit tests + Define :func:`colour.models.cie_ucs.UCS_to_XYZ` definition unit tests methods. """ def test_UCS_to_XYZ(self): - """ - Tests :func:`colour.models.cie_ucs.UCS_to_XYZ` definition. - """ + """Test :func:`colour.models.cie_ucs.UCS_to_XYZ` definition.""" np.testing.assert_almost_equal( UCS_to_XYZ(np.array([0.13769339, 0.12197225, 0.10537310])), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( UCS_to_XYZ(np.array([0.09481340, 0.23042768, 0.32701033])), np.array([0.14222010, 0.23042768, 0.10495772]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( UCS_to_XYZ(np.array([0.05212520, 0.06157201, 0.19376075])), np.array([0.07818780, 0.06157201, 0.28099326]), - decimal=7) + decimal=7, + ) def test_n_dimensional_UCS_to_XYZ(self): """ - Tests :func:`colour.models.cie_ucs.UCS_to_XYZ` definition n-dimensional + Test :func:`colour.models.cie_ucs.UCS_to_XYZ` definition n-dimensional support. """ @@ -142,24 +148,23 @@ def test_n_dimensional_UCS_to_XYZ(self): def test_domain_range_scale_UCS_to_XYZ(self): """ - Tests :func:`colour.models.cie_ucs.UCS_to_XYZ` definition domain and + Test :func:`colour.models.cie_ucs.UCS_to_XYZ` definition domain and range scale support. """ UCS = np.array([0.0469968933, 0.1008000000, 0.1637438950]) XYZ = UCS_to_XYZ(UCS) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - UCS_to_XYZ(UCS * factor), XYZ * factor, decimal=7) + UCS_to_XYZ(UCS * factor), XYZ * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_UCS_to_XYZ(self): - """ - Tests :func:`colour.models.cie_ucs.UCS_to_XYZ` definition nan support. - """ + """Test :func:`colour.models.cie_ucs.UCS_to_XYZ` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -170,33 +175,34 @@ def test_nan_UCS_to_XYZ(self): class TestUCS_to_uv(unittest.TestCase): """ - Defines :func:`colour.models.cie_ucs.UCS_to_uv` definition unit tests + Define :func:`colour.models.cie_ucs.UCS_to_uv` definition unit tests methods. """ def test_UCS_to_uv(self): - """ - Tests :func:`colour.models.cie_ucs.UCS_to_uv` definition. - """ + """Test :func:`colour.models.cie_ucs.UCS_to_uv` definition.""" np.testing.assert_almost_equal( UCS_to_uv(np.array([0.13769339, 0.12197225, 0.10537310])), np.array([0.37720213, 0.33413508]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( UCS_to_uv(np.array([0.09481340, 0.23042768, 0.32701033])), np.array([0.14536327, 0.35328046]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( UCS_to_uv(np.array([0.05212520, 0.06157201, 0.19376075])), np.array([0.16953602, 0.20026156]), - decimal=7) + decimal=7, + ) def test_n_dimensional_UCS_to_uv(self): """ - Tests :func:`colour.models.cie_ucs.UCS_to_uv` definition n-dimensional + Test :func:`colour.models.cie_ucs.UCS_to_uv` definition n-dimensional support. """ @@ -213,24 +219,23 @@ def test_n_dimensional_UCS_to_uv(self): def test_domain_range_scale_UCS_to_uv(self): """ - Tests :func:`colour.models.cie_ucs.UCS_to_uv` definition domain and + Test :func:`colour.models.cie_ucs.UCS_to_uv` definition domain and range scale support. """ UCS = np.array([0.0469968933, 0.1008000000, 0.1637438950]) uv = UCS_to_uv(UCS) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - UCS_to_uv(UCS * factor), uv, decimal=7) + UCS_to_uv(UCS * factor), uv, decimal=7 + ) @ignore_numpy_errors def test_nan_UCS_to_uv(self): - """ - Tests :func:`colour.models.cie_ucs.UCS_to_uv` definition nan support. - """ + """Test :func:`colour.models.cie_ucs.UCS_to_uv` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -241,38 +246,40 @@ def test_nan_UCS_to_uv(self): class Testuv_to_UCS(unittest.TestCase): """ - Defines :func:`colour.models.cie_ucs.uv_to_UCS` definition unit tests + Define :func:`colour.models.cie_ucs.uv_to_UCS` definition unit tests methods. """ def test_uv_to_UCS(self): - """ - Tests :func:`colour.models.cie_ucs.uv_to_UCS` definition. - """ + """Test :func:`colour.models.cie_ucs.uv_to_UCS` definition.""" np.testing.assert_almost_equal( uv_to_UCS(np.array([0.37720213, 0.33413508])), np.array([1.12889114, 1.00000000, 0.86391046]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( uv_to_UCS(np.array([0.14536327, 0.35328046])), np.array([0.41146705, 1.00000000, 1.41914520]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( uv_to_UCS(np.array([0.16953602, 0.20026156])), np.array([0.84657295, 1.00000000, 3.14689659]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( uv_to_UCS(np.array([0.37720213, 0.33413508]), V=0.18), np.array([0.20320040, 0.18000000, 0.15550388]), - decimal=7) + decimal=7, + ) def test_n_dimensional_uv_to_UCS(self): """ - Tests :func:`colour.models.cie_ucs.uv_to_UCS` definition n-dimensional + Test :func:`colour.models.cie_ucs.uv_to_UCS` definition n-dimensional support. """ @@ -289,24 +296,24 @@ def test_n_dimensional_uv_to_UCS(self): def test_domain_range_scale_uv_to_UCS(self): """ - Tests :func:`colour.models.cie_ucs.uv_to_UCS` definition domain and + Test :func:`colour.models.cie_ucs.uv_to_UCS` definition domain and range scale support. """ uv = np.array([0.37720213, 0.33413508]) - UCS = uv_to_UCS(uv) + V = 1 + UCS = uv_to_UCS(uv, V) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - uv_to_UCS(uv), UCS * factor, decimal=7) + uv_to_UCS(uv, V * factor), UCS * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_uv_to_UCS(self): - """ - Tests :func:`colour.models.cie_ucs.uv_to_UCS` definition nan support. - """ + """Test :func:`colour.models.cie_ucs.uv_to_UCS` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=2)) @@ -317,33 +324,34 @@ def test_nan_uv_to_UCS(self): class TestUCS_uv_to_xy(unittest.TestCase): """ - Defines :func:`colour.models.cie_ucs.UCS_uv_to_xy` definition unit tests + Define :func:`colour.models.cie_ucs.UCS_uv_to_xy` definition unit tests methods. """ def test_UCS_uv_to_xy(self): - """ - Tests :func:`colour.models.cie_ucs.UCS_uv_to_xy` definition. - """ + """Test :func:`colour.models.cie_ucs.UCS_uv_to_xy` definition.""" np.testing.assert_almost_equal( UCS_uv_to_xy(np.array([0.37720213, 0.33413508])), np.array([0.54369555, 0.32107941]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( UCS_uv_to_xy(np.array([0.14536327, 0.35328046])), np.array([0.29777734, 0.48246445]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( UCS_uv_to_xy(np.array([0.16953602, 0.20026156])), np.array([0.18582823, 0.14633764]), - decimal=7) + decimal=7, + ) def test_n_dimensional_UCS_uv_to_xy(self): """ - Tests :func:`colour.models.cie_ucs.UCS_uv_to_xy` definition + Test :func:`colour.models.cie_ucs.UCS_uv_to_xy` definition n-dimensional arrays support. """ @@ -361,7 +369,7 @@ def test_n_dimensional_UCS_uv_to_xy(self): @ignore_numpy_errors def test_nan_UCS_uv_to_xy(self): """ - Tests :func:`colour.models.cie_ucs.UCS_uv_to_xy` definition nan + Test :func:`colour.models.cie_ucs.UCS_uv_to_xy` definition nan support. """ @@ -374,33 +382,34 @@ def test_nan_UCS_uv_to_xy(self): class TestXy_to_UCS_uv(unittest.TestCase): """ - Defines :func:`colour.models.cie_ucs.xy_to_UCS_uv` definition unit tests + Define :func:`colour.models.cie_ucs.xy_to_UCS_uv` definition unit tests methods. """ def test_xy_to_UCS_uv(self): - """ - Tests :func:`colour.models.cie_ucs.xy_to_UCS_uv` definition. - """ + """Test :func:`colour.models.cie_ucs.xy_to_UCS_uv` definition.""" np.testing.assert_almost_equal( xy_to_UCS_uv(np.array([0.54369555, 0.32107941])), np.array([0.37720213, 0.33413508]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( xy_to_UCS_uv(np.array([0.29777734, 0.48246445])), np.array([0.14536327, 0.35328046]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( xy_to_UCS_uv(np.array([0.18582823, 0.14633764])), np.array([0.16953602, 0.20026156]), - decimal=7) + decimal=7, + ) def test_n_dimensional_xy_to_UCS_uv(self): """ - Tests :func:`colour.models.cie_ucs.xy_to_UCS_uv` definition + Test :func:`colour.models.cie_ucs.xy_to_UCS_uv` definition n-dimensional arrays support. """ @@ -418,7 +427,7 @@ def test_n_dimensional_xy_to_UCS_uv(self): @ignore_numpy_errors def test_nan_xy_to_UCS_uv(self): """ - Tests :func:`colour.models.cie_ucs.xy_to_UCS_uv` definition nan + Test :func:`colour.models.cie_ucs.xy_to_UCS_uv` definition nan support. """ @@ -429,5 +438,5 @@ def test_nan_xy_to_UCS_uv(self): xy_to_UCS_uv(xy) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/tests/test_cie_uvw.py b/colour/models/tests/test_cie_uvw.py index 85d9c18443..128ffe3e4b 100644 --- a/colour/models/tests/test_cie_uvw.py +++ b/colour/models/tests/test_cie_uvw.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.cie_uvw` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.cie_uvw` module.""" import numpy as np import unittest @@ -12,66 +7,76 @@ from colour.models import UVW_to_XYZ, XYZ_to_UVW from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestXYZ_to_UVW', 'TestUVW_to_XYZ'] +__all__ = [ + "TestXYZ_to_UVW", + "TestUVW_to_XYZ", +] class TestXYZ_to_UVW(unittest.TestCase): """ - Defines :func:`colour.models.cie_uvw.XYZ_to_UVW` definition unit tests + Define :func:`colour.models.cie_uvw.XYZ_to_UVW` definition unit tests methods. """ def test_XYZ_to_UVW(self): - """ - Tests :func:`colour.models.cie_uvw.XYZ_to_UVW` definition. - """ + """Test :func:`colour.models.cie_uvw.XYZ_to_UVW` definition.""" np.testing.assert_almost_equal( XYZ_to_UVW(np.array([0.20654008, 0.12197225, 0.05136952]) * 100), np.array([94.55035725, 11.55536523, 40.54757405]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_UVW(np.array([0.14222010, 0.23042768, 0.10495772]) * 100), np.array([-36.92762376, 28.90425105, 54.14071478]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_UVW(np.array([0.07818780, 0.06157201, 0.28099326]) * 100), np.array([-10.60111550, -41.94580000, 28.82134002]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_UVW( np.array([0.20654008, 0.12197225, 0.05136952]) * 100, - np.array([0.44757, 0.40745])), + np.array([0.44757, 0.40745]), + ), np.array([63.90676310, -8.11466183, 40.54757405]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_UVW( np.array([0.20654008, 0.12197225, 0.05136952]) * 100, - np.array([0.34570, 0.35850])), + np.array([0.34570, 0.35850]), + ), np.array([88.56798946, 4.61154385, 40.54757405]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_UVW( np.array([0.20654008, 0.12197225, 0.05136952]) * 100, - np.array([0.34570, 0.35850, 1.00000])), + np.array([0.34570, 0.35850, 1.00000]), + ), np.array([88.56798946, 4.61154385, 40.54757405]), - decimal=7) + decimal=7, + ) def test_n_dimensional_XYZ_to_UVW(self): """ - Tests :func:`colour.models.cie_uvw.XYZ_to_UVW` definition n-dimensional + Test :func:`colour.models.cie_uvw.XYZ_to_UVW` definition n-dimensional support. """ @@ -82,21 +87,24 @@ def test_n_dimensional_XYZ_to_UVW(self): XYZ = np.tile(XYZ, (6, 1)) UVW = np.tile(UVW, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_UVW(XYZ, illuminant), UVW, decimal=7) + XYZ_to_UVW(XYZ, illuminant), UVW, decimal=7 + ) illuminant = np.tile(illuminant, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_UVW(XYZ, illuminant), UVW, decimal=7) + XYZ_to_UVW(XYZ, illuminant), UVW, decimal=7 + ) XYZ = np.reshape(XYZ, (2, 3, 3)) illuminant = np.reshape(illuminant, (2, 3, 2)) UVW = np.reshape(UVW, (2, 3, 3)) np.testing.assert_almost_equal( - XYZ_to_UVW(XYZ, illuminant), UVW, decimal=7) + XYZ_to_UVW(XYZ, illuminant), UVW, decimal=7 + ) def test_domain_range_scale_XYZ_to_UVW(self): """ - Tests :func:`colour.models.cie_uvw.XYZ_to_UVW` definition domain and + Test :func:`colour.models.cie_uvw.XYZ_to_UVW` definition domain and range scale support. """ @@ -104,19 +112,18 @@ def test_domain_range_scale_XYZ_to_UVW(self): illuminant = np.array([0.31270, 0.32900]) UVW = XYZ_to_UVW(XYZ, illuminant) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( XYZ_to_UVW(XYZ * factor, illuminant), UVW * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_XYZ_to_UVW(self): - """ - Tests :func:`colour.models.cie_uvw.XYZ_to_UVW` definition nan support. - """ + """Test :func:`colour.models.cie_uvw.XYZ_to_UVW` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -128,54 +135,61 @@ def test_nan_XYZ_to_UVW(self): class TestUVW_to_XYZ(unittest.TestCase): """ - Defines :func:`colour.models.cie_uvw.UVW_to_XYZ` definition unit tests + Define :func:`colour.models.cie_uvw.UVW_to_XYZ` definition unit tests methods. """ def test_UVW_to_XYZ(self): - """ - Tests :func:`colour.models.cie_uvw.UVW_to_XYZ` definition. - """ + """Test :func:`colour.models.cie_uvw.UVW_to_XYZ` definition.""" np.testing.assert_almost_equal( UVW_to_XYZ(np.array([94.55035725, 11.55536523, 40.54757405])), np.array([0.20654008, 0.12197225, 0.05136952]) * 100, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( UVW_to_XYZ(np.array([-36.92762376, 28.90425105, 54.14071478])), np.array([0.14222010, 0.23042768, 0.10495772]) * 100, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( UVW_to_XYZ(np.array([-10.60111550, -41.94580000, 28.82134002])), np.array([0.07818780, 0.06157201, 0.28099326]) * 100, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( UVW_to_XYZ( np.array([63.90676310, -8.11466183, 40.54757405]), - np.array([0.44757, 0.40745])), + np.array([0.44757, 0.40745]), + ), np.array([0.20654008, 0.12197225, 0.05136952]) * 100, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( UVW_to_XYZ( np.array([88.56798946, 4.61154385, 40.54757405]), - np.array([0.34570, 0.35850])), + np.array([0.34570, 0.35850]), + ), np.array([0.20654008, 0.12197225, 0.05136952]) * 100, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( UVW_to_XYZ( np.array([88.56798946, 4.61154385, 40.54757405]), - np.array([0.34570, 0.35850, 1.00000])), + np.array([0.34570, 0.35850, 1.00000]), + ), np.array([0.20654008, 0.12197225, 0.05136952]) * 100, - decimal=7) + decimal=7, + ) def test_n_dimensional_UVW_to_XYZ(self): """ - Tests :func:`colour.models.cie_uvw.UVW_to_XYZ` definition n-dimensional + Test :func:`colour.models.cie_uvw.UVW_to_XYZ` definition n-dimensional support. """ @@ -186,21 +200,24 @@ def test_n_dimensional_UVW_to_XYZ(self): XYZ = np.tile(XYZ, (6, 1)) UVW = np.tile(UVW, (6, 1)) np.testing.assert_almost_equal( - UVW_to_XYZ(UVW, illuminant), XYZ, decimal=7) + UVW_to_XYZ(UVW, illuminant), XYZ, decimal=7 + ) illuminant = np.tile(illuminant, (6, 1)) np.testing.assert_almost_equal( - UVW_to_XYZ(UVW, illuminant), XYZ, decimal=7) + UVW_to_XYZ(UVW, illuminant), XYZ, decimal=7 + ) XYZ = np.reshape(XYZ, (2, 3, 3)) illuminant = np.reshape(illuminant, (2, 3, 2)) UVW = np.reshape(UVW, (2, 3, 3)) np.testing.assert_almost_equal( - UVW_to_XYZ(UVW, illuminant), XYZ, decimal=7) + UVW_to_XYZ(UVW, illuminant), XYZ, decimal=7 + ) def test_domain_range_scale_UVW_to_XYZ(self): """ - Tests :func:`colour.models.cie_uvw.UVW_to_XYZ` definition domain and + Test :func:`colour.models.cie_uvw.UVW_to_XYZ` definition domain and range scale support. """ @@ -208,19 +225,18 @@ def test_domain_range_scale_UVW_to_XYZ(self): illuminant = np.array([0.31270, 0.32900]) XYZ = UVW_to_XYZ(UVW, illuminant) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( UVW_to_XYZ(UVW * factor, illuminant), XYZ * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_UVW_to_XYZ(self): - """ - Tests :func:`colour.models.cie_uvw.UVW_to_XYZ` definition nan support. - """ + """Test :func:`colour.models.cie_uvw.UVW_to_XYZ` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -230,5 +246,5 @@ def test_nan_UVW_to_XYZ(self): UVW_to_XYZ(UVW, illuminant) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/tests/test_cie_xyy.py b/colour/models/tests/test_cie_xyy.py index 5309d39f14..d91521a3e7 100644 --- a/colour/models/tests/test_cie_xyy.py +++ b/colour/models/tests/test_cie_xyy.py @@ -1,79 +1,92 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.cie_xyy` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.cie_xyy` module.""" import numpy as np import unittest from itertools import permutations -from colour.models import (XYZ_to_xyY, xyY_to_XYZ, xy_to_xyY, xyY_to_xy, - xy_to_XYZ, XYZ_to_xy) +from colour.models import ( + XYZ_to_xyY, + xyY_to_XYZ, + xy_to_xyY, + xyY_to_xy, + xy_to_XYZ, + XYZ_to_xy, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestXYZ_to_xyY', 'TestxyY_to_XYZ', 'TestxyY_to_xy', 'Testxy_to_xyY', - 'TestXYZ_to_xy', 'Testxy_to_XYZ' + "TestXYZ_to_xyY", + "TestxyY_to_XYZ", + "TestxyY_to_xy", + "Testxy_to_xyY", + "TestXYZ_to_xy", + "Testxy_to_XYZ", ] class TestXYZ_to_xyY(unittest.TestCase): """ - Defines :func:`colour.models.cie_xyy.XYZ_to_xyY` definition unit tests + Define :func:`colour.models.cie_xyy.XYZ_to_xyY` definition unit tests methods. """ def test_XYZ_to_xyY(self): - """ - Tests :func:`colour.models.cie_xyy.XYZ_to_xyY` definition. - """ + """Test :func:`colour.models.cie_xyy.XYZ_to_xyY` definition.""" np.testing.assert_almost_equal( XYZ_to_xyY(np.array([0.20654008, 0.12197225, 0.05136952])), np.array([0.54369557, 0.32107944, 0.12197225]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_xyY(np.array([0.14222010, 0.23042768, 0.10495772])), np.array([0.29777735, 0.48246446, 0.23042768]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_xyY(np.array([0.07818780, 0.06157201, 0.28099326])), np.array([0.18582823, 0.14633764, 0.06157201]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_xyY(np.array([0.00000000, 0.00000000, 1.00000000])), np.array([0.00000000, 0.00000000, 0.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_xyY( - np.array([ - [0.20654008, 0.12197225, 0.05136952], - [0.00000000, 0.00000000, 0.00000000], - [0.00000000, 1.00000000, 0.00000000], - ])), - np.array([ - [0.54369557, 0.32107944, 0.12197225], - [0.31270000, 0.32900000, 0.00000000], - [0.00000000, 1.00000000, 1.00000000], - ]), - decimal=7) + np.array( + [ + [0.20654008, 0.12197225, 0.05136952], + [0.00000000, 0.00000000, 0.00000000], + [0.00000000, 1.00000000, 0.00000000], + ] + ) + ), + np.array( + [ + [0.54369557, 0.32107944, 0.12197225], + [0.31270000, 0.32900000, 0.00000000], + [0.00000000, 1.00000000, 1.00000000], + ] + ), + decimal=7, + ) def test_n_dimensional_XYZ_to_xyY(self): """ - Tests :func:`colour.models.cie_xyy.XYZ_to_xyY` definition n-dimensional + Test :func:`colour.models.cie_xyy.XYZ_to_xyY` definition n-dimensional support. """ @@ -84,44 +97,46 @@ def test_n_dimensional_XYZ_to_xyY(self): XYZ = np.tile(XYZ, (6, 1)) xyY = np.tile(xyY, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_xyY(XYZ, illuminant), xyY, decimal=7) + XYZ_to_xyY(XYZ, illuminant), xyY, decimal=7 + ) illuminant = np.tile(illuminant, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_xyY(XYZ, illuminant), xyY, decimal=7) + XYZ_to_xyY(XYZ, illuminant), xyY, decimal=7 + ) XYZ = np.reshape(XYZ, (2, 3, 3)) illuminant = np.reshape(illuminant, (2, 3, 2)) xyY = np.reshape(xyY, (2, 3, 3)) np.testing.assert_almost_equal( - XYZ_to_xyY(XYZ, illuminant), xyY, decimal=7) + XYZ_to_xyY(XYZ, illuminant), xyY, decimal=7 + ) def test_domain_range_scale_XYZ_to_xyY(self): """ - Tests :func:`colour.models.cie_xyy.XYZ_to_xyY` definition domain and + Test :func:`colour.models.cie_xyy.XYZ_to_xyY` definition domain and range scale support. """ XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) xyY = XYZ_to_xyY(XYZ) - XYZ = np.tile(XYZ, (6, 1)).reshape(2, 3, 3) - xyY = np.tile(xyY, (6, 1)).reshape(2, 3, 3) - - d_r = (('reference', 1, 1), (1, 1, 1), ( - 100, - 100, - np.array([1, 1, 100]), - )) + XYZ = np.reshape(np.tile(XYZ, (6, 1)), (2, 3, 3)) + xyY = np.reshape(np.tile(xyY, (6, 1)), (2, 3, 3)) + + d_r = ( + ("reference", 1, 1), + ("1", 1, 1), + ("100", 100, np.array([1, 1, 100])), + ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - XYZ_to_xyY(XYZ * factor_a), xyY * factor_b, decimal=7) + XYZ_to_xyY(XYZ * factor_a), xyY * factor_b, decimal=7 + ) @ignore_numpy_errors def test_nan_XYZ_to_xyY(self): - """ - Tests :func:`colour.models.cie_xyy.XYZ_to_xyY` definition nan support. - """ + """Test :func:`colour.models.cie_xyy.XYZ_to_xyY` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -133,52 +148,60 @@ def test_nan_XYZ_to_xyY(self): class TestxyY_to_XYZ(unittest.TestCase): """ - Defines :func:`colour.models.cie_xyy.xyY_to_XYZ` definition unit tests + Define :func:`colour.models.cie_xyy.xyY_to_XYZ` definition unit tests methods. """ def test_xyY_to_XYZ(self): - """ - Tests :func:`colour.models.cie_xyy.xyY_to_XYZ` definition. - """ + """Test :func:`colour.models.cie_xyy.xyY_to_XYZ` definition.""" np.testing.assert_almost_equal( xyY_to_XYZ(np.array([0.54369557, 0.32107944, 0.12197225])), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( xyY_to_XYZ(np.array([0.29777735, 0.48246446, 0.23042768])), np.array([0.14222010, 0.23042768, 0.10495772]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( xyY_to_XYZ(np.array([0.18582823, 0.14633764, 0.06157201])), np.array([0.07818780, 0.06157201, 0.28099326]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( xyY_to_XYZ(np.array([0.34567, 0.3585, 0.00000000])), np.array([0.00000000, 0.00000000, 0.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( xyY_to_XYZ( - np.array([ - [0.54369557, 0.32107944, 0.12197225], - [0.31270000, 0.32900000, 0.00000000], - [0.00000000, 1.00000000, 1.00000000], - ])), - np.array([ - [0.20654008, 0.12197225, 0.05136952], - [0.00000000, 0.00000000, 0.00000000], - [0.00000000, 1.00000000, 0.00000000], - ]), - decimal=7) + np.array( + [ + [0.54369557, 0.32107944, 0.12197225], + [0.31270000, 0.32900000, 0.00000000], + [0.00000000, 1.00000000, 1.00000000], + ] + ) + ), + np.array( + [ + [0.20654008, 0.12197225, 0.05136952], + [0.00000000, 0.00000000, 0.00000000], + [0.00000000, 1.00000000, 0.00000000], + ] + ), + decimal=7, + ) def test_n_dimensional_xyY_to_XYZ(self): """ - Tests :func:`colour.models.cie_xyy.xyY_to_XYZ` definition n-dimensional + Test :func:`colour.models.cie_xyy.xyY_to_XYZ` definition n-dimensional support. """ @@ -195,30 +218,29 @@ def test_n_dimensional_xyY_to_XYZ(self): def test_domain_range_scale_xyY_to_XYZ(self): """ - Tests :func:`colour.models.cie_xyy.xyY_to_XYZ` definition domain and + Test :func:`colour.models.cie_xyy.xyY_to_XYZ` definition domain and range scale support. """ xyY = np.array([0.54369557, 0.32107944, 0.12197225]) XYZ = xyY_to_XYZ(xyY) - xyY = np.tile(xyY, (6, 1)).reshape(2, 3, 3) - XYZ = np.tile(XYZ, (6, 1)).reshape(2, 3, 3) - - d_r = (('reference', 1, 1), (1, 1, 1), ( - 100, - np.array([1, 1, 100]), - 100, - )) + xyY = np.reshape(np.tile(xyY, (6, 1)), (2, 3, 3)) + XYZ = np.reshape(np.tile(XYZ, (6, 1)), (2, 3, 3)) + + d_r = ( + ("reference", 1, 1), + ("1", 1, 1), + ("100", np.array([1, 1, 100]), 100), + ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - xyY_to_XYZ(xyY * factor_a), XYZ * factor_b, decimal=7) + xyY_to_XYZ(xyY * factor_a), XYZ * factor_b, decimal=7 + ) @ignore_numpy_errors def test_nan_xyY_to_XYZ(self): - """ - Tests :func:`colour.models.cie_xyy.xyY_to_XYZ` definition nan support. - """ + """Test :func:`colour.models.cie_xyy.xyY_to_XYZ` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -229,38 +251,40 @@ def test_nan_xyY_to_XYZ(self): class TestxyY_to_xy(unittest.TestCase): """ - Defines :func:`colour.models.cie_xyy.xyY_to_xy` definition unit tests + Define :func:`colour.models.cie_xyy.xyY_to_xy` definition unit tests methods. """ def test_xyY_to_xy(self): - """ - Tests :func:`colour.models.cie_xyy.xyY_to_xy` definition. - """ + """Test :func:`colour.models.cie_xyy.xyY_to_xy` definition.""" np.testing.assert_almost_equal( xyY_to_xy(np.array([0.54369557, 0.32107944, 0.12197225])), np.array([0.54369557, 0.32107944]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( xyY_to_xy(np.array([0.29777735, 0.48246446, 0.23042768])), np.array([0.29777735, 0.48246446]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( xyY_to_xy(np.array([0.18582823, 0.14633764, 0.06157201])), np.array([0.18582823, 0.14633764]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( xyY_to_xy(np.array([0.31270, 0.32900])), np.array([0.31270000, 0.32900000]), - decimal=7) + decimal=7, + ) def test_n_dimensional_xyY_to_xy(self): """ - Tests :func:`colour.models.cie_xyy.xyY_to_xy` definition n-dimensional + Test :func:`colour.models.cie_xyy.xyY_to_xy` definition n-dimensional support. """ @@ -277,26 +301,29 @@ def test_n_dimensional_xyY_to_xy(self): def test_domain_range_scale_xyY_to_xy(self): """ - Tests :func:`colour.models.cie_xyy.xyY_to_xy` definition domain and + Test :func:`colour.models.cie_xyy.xyY_to_xy` definition domain and range scale support. """ xyY = np.array([0.54369557, 0.32107944, 0.12197225]) xy = xyY_to_xy(xyY) - xyY = np.tile(xyY, (6, 1)).reshape(2, 3, 3) - xy = np.tile(xy, (6, 1)).reshape(2, 3, 2) - - d_r = (('reference', 1, 1), (1, 1, 1), (100, np.array([1, 1, 100]), 1)) + xyY = np.reshape(np.tile(xyY, (6, 1)), (2, 3, 3)) + xy = np.reshape(np.tile(xy, (6, 1)), (2, 3, 2)) + + d_r = ( + ("reference", 1, 1), + ("1", 1, 1), + ("100", np.array([1, 1, 100]), 1), + ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - xyY_to_xy(xyY * factor_a), xy * factor_b, decimal=7) + xyY_to_xy(xyY * factor_a), xy * factor_b, decimal=7 + ) @ignore_numpy_errors def test_nan_xyY_to_xy(self): - """ - Tests :func:`colour.models.cie_xyy.xyY_to_xy` definition nan support. - """ + """Test :func:`colour.models.cie_xyy.xyY_to_xy` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=2)) @@ -307,43 +334,46 @@ def test_nan_xyY_to_xy(self): class Testxy_to_xyY(unittest.TestCase): """ - Defines :func:`colour.models.cie_xyy.xy_to_xyY` definition unit tests + Define :func:`colour.models.cie_xyy.xy_to_xyY` definition unit tests methods. """ def test_xy_to_xyY(self): - """ - Tests :func:`colour.models.cie_xyy.xy_to_xyY` definition. - """ + """Test :func:`colour.models.cie_xyy.xy_to_xyY` definition.""" np.testing.assert_almost_equal( xy_to_xyY(np.array([0.54369557, 0.32107944])), np.array([0.54369557, 0.32107944, 1.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( xy_to_xyY(np.array([0.29777735, 0.48246446])), np.array([0.29777735, 0.48246446, 1.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( xy_to_xyY(np.array([0.18582823, 0.14633764])), np.array([0.18582823, 0.14633764, 1.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( xy_to_xyY(np.array([0.31270000, 0.32900000, 1.00000000])), np.array([0.31270000, 0.32900000, 1.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( xy_to_xyY(np.array([0.31270000, 0.32900000]), 100), np.array([0.31270000, 0.32900000, 100.00000000]), - decimal=7) + decimal=7, + ) def test_n_dimensional_xy_to_xyY(self): """ - Tests :func:`colour.models.cie_xyy.xy_to_xyY` definition n-dimensional + Test :func:`colour.models.cie_xyy.xy_to_xyY` definition n-dimensional support. """ @@ -360,30 +390,33 @@ def test_n_dimensional_xy_to_xyY(self): def test_domain_range_scale_xy_to_xyY(self): """ - Tests :func:`colour.models.cie_xyy.xy_to_xyY` definition domain and + Test :func:`colour.models.cie_xyy.xy_to_xyY` definition domain and range scale support. """ xy = np.array([0.54369557, 0.32107944, 0.12197225]) xyY = xy_to_xyY(xy) - xy = np.tile(xy, (6, 1)).reshape(2, 3, 3) - xyY = np.tile(xyY, (6, 1)).reshape(2, 3, 3) - - d_r = (('reference', 1, 1), (1, 1, 1), ( - 100, - np.array([1, 1, 100]), - np.array([1, 1, 100]), - )) + xy = np.reshape(np.tile(xy, (6, 1)), (2, 3, 3)) + xyY = np.reshape(np.tile(xyY, (6, 1)), (2, 3, 3)) + + d_r = ( + ("reference", 1, 1), + (1, 1, 1), + ( + 100, + np.array([1, 1, 100]), + np.array([1, 1, 100]), + ), + ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - xy_to_xyY(xy * factor_a), xyY * factor_b, decimal=7) + xy_to_xyY(xy * factor_a), xyY * factor_b, decimal=7 + ) @ignore_numpy_errors def test_nan_xy_to_xyY(self): - """ - Tests :func:`colour.models.cie_xyy.xy_to_xyY` definition nan support. - """ + """Test :func:`colour.models.cie_xyy.xy_to_xyY` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=2)) @@ -394,38 +427,40 @@ def test_nan_xy_to_xyY(self): class TestXYZ_to_xy(unittest.TestCase): """ - Defines :func:`colour.models.cie_xyy.XYZ_to_xy` definition unit tests + Define :func:`colour.models.cie_xyy.XYZ_to_xy` definition unit tests methods. """ def test_XYZ_to_xy(self): - """ - Tests :func:`colour.models.cie_xyy.XYZ_to_xy` definition. - """ + """Test :func:`colour.models.cie_xyy.XYZ_to_xy` definition.""" np.testing.assert_almost_equal( XYZ_to_xy(np.array([0.20654008, 0.12197225, 0.05136952])), np.array([0.54369557, 0.32107944]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_xy(np.array([0.14222010, 0.23042768, 0.10495772])), np.array([0.29777735, 0.48246446]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_xy(np.array([0.07818780, 0.06157201, 0.28099326])), np.array([0.18582823, 0.14633764]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_xy(np.array([0.00000000, 0.00000000, 0.00000000])), np.array([0.31270000, 0.32900000]), - decimal=7) + decimal=7, + ) def test_n_dimensional_XYZ_to_xy(self): """ - Tests :func:`colour.models.cie_xyy.XYZ_to_xy` definition n-dimensional + Test :func:`colour.models.cie_xyy.XYZ_to_xy` definition n-dimensional support. """ @@ -436,40 +471,42 @@ def test_n_dimensional_XYZ_to_xy(self): XYZ = np.tile(XYZ, (6, 1)) xy = np.tile(xy, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_xy(XYZ, illuminant), xy, decimal=7) + XYZ_to_xy(XYZ, illuminant), xy, decimal=7 + ) illuminant = np.tile(illuminant, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_xy(XYZ, illuminant), xy, decimal=7) + XYZ_to_xy(XYZ, illuminant), xy, decimal=7 + ) XYZ = np.reshape(XYZ, (2, 3, 3)) illuminant = np.reshape(xy, (2, 3, 2)) xy = np.reshape(xy, (2, 3, 2)) np.testing.assert_almost_equal( - XYZ_to_xy(XYZ, illuminant), xy, decimal=7) + XYZ_to_xy(XYZ, illuminant), xy, decimal=7 + ) def test_domain_range_scale_XYZ_to_xy(self): """ - Tests :func:`colour.models.cie_xyy.XYZ_to_xy` definition domain and + Test :func:`colour.models.cie_xyy.XYZ_to_xy` definition domain and range scale support. """ XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) xy = XYZ_to_xy(XYZ) - XYZ = np.tile(XYZ, (6, 1)).reshape(2, 3, 3) - xy = np.tile(xy, (6, 1)).reshape(2, 3, 2) + XYZ = np.reshape(np.tile(XYZ, (6, 1)), (2, 3, 3)) + xy = np.reshape(np.tile(xy, (6, 1)), (2, 3, 2)) - d_r = (('reference', 1), (1, 1), (100, 1)) + d_r = (("reference", 1), ("1", 1), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - XYZ_to_xy(XYZ * factor), xy, decimal=7) + XYZ_to_xy(XYZ * factor), xy, decimal=7 + ) @ignore_numpy_errors def test_nan_XYZ_to_xy(self): - """ - Tests :func:`colour.models.cie_xyy.XYZ_to_xy` definition nan support. - """ + """Test :func:`colour.models.cie_xyy.XYZ_to_xy` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -481,38 +518,40 @@ def test_nan_XYZ_to_xy(self): class Testxy_to_XYZ(unittest.TestCase): """ - Defines :func:`colour.models.cie_xyy.xy_to_XYZ` definition unit tests + Define :func:`colour.models.cie_xyy.xy_to_XYZ` definition unit tests methods. """ def test_xy_to_XYZ(self): - """ - Tests :func:`colour.models.cie_xyy.xy_to_XYZ` definition. - """ + """Test :func:`colour.models.cie_xyy.xy_to_XYZ` definition.""" np.testing.assert_almost_equal( xy_to_XYZ(np.array([0.54369557, 0.32107944])), np.array([1.69333661, 1.00000000, 0.42115742]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( xy_to_XYZ(np.array([0.29777735, 0.48246446])), np.array([0.61720059, 1.00000000, 0.45549094]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( xy_to_XYZ(np.array([0.18582823, 0.14633764])), np.array([1.26985942, 1.00000000, 4.56365245]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( xy_to_XYZ(np.array([0.31270000, 0.32900000])), np.array([0.95045593, 1.00000000, 1.08905775]), - decimal=7) + decimal=7, + ) def test_n_dimensional_xy_to_XYZ(self): """ - Tests :func:`colour.models.cie_xyy.xy_to_XYZ` definition n-dimensional + Test :func:`colour.models.cie_xyy.xy_to_XYZ` definition n-dimensional support. """ @@ -529,30 +568,29 @@ def test_n_dimensional_xy_to_XYZ(self): def test_domain_range_scale_xy_to_XYZ(self): """ - Tests :func:`colour.models.cie_xyy.xy_to_XYZ` definition domain and + Test :func:`colour.models.cie_xyy.xy_to_XYZ` definition domain and range scale support. """ xy = np.array([0.54369557, 0.32107944, 0.12197225]) XYZ = xy_to_XYZ(xy) - xy = np.tile(xy, (6, 1)).reshape(2, 3, 3) - XYZ = np.tile(XYZ, (6, 1)).reshape(2, 3, 3) - - d_r = (('reference', 1, 1), (1, 1, 1), ( - 100, - np.array([1, 1, 100]), - 100, - )) + xy = np.reshape(np.tile(xy, (6, 1)), (2, 3, 3)) + XYZ = np.reshape(np.tile(XYZ, (6, 1)), (2, 3, 3)) + + d_r = ( + ("reference", 1, 1), + ("1", 1, 1), + ("100", np.array([1, 1, 100]), 100), + ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - xy_to_XYZ(xy * factor_a), XYZ * factor_b, decimal=7) + xy_to_XYZ(xy * factor_a), XYZ * factor_b, decimal=7 + ) @ignore_numpy_errors def test_nan_xy_to_XYZ(self): - """ - Tests :func:`colour.models.cie_xyy.xy_to_XYZ` definition nan support. - """ + """Test :func:`colour.models.cie_xyy.xy_to_XYZ` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=2)) @@ -561,5 +599,5 @@ def test_nan_xy_to_XYZ(self): xy_to_XYZ(xy) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/tests/test_common.py b/colour/models/tests/test_common.py index d6db751d1b..ab0e498b5b 100644 --- a/colour/models/tests/test_common.py +++ b/colour/models/tests/test_common.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.common` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.common` module.""" import numpy as np import unittest @@ -12,45 +7,49 @@ from colour.models import Jab_to_JCh, JCh_to_Jab from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestJab_to_JCh', 'TestJCh_to_Jab'] +__all__ = [ + "TestJab_to_JCh", + "TestJCh_to_Jab", +] class TestJab_to_JCh(unittest.TestCase): """ - Defines :func:`colour.models.common.Jab_to_JCh` definition unit tests + Define :func:`colour.models.common.Jab_to_JCh` definition unit tests methods. """ def test_Jab_to_JCh(self): - """ - Tests :func:`colour.models.common.Jab_to_JCh` definition. - """ + """Test :func:`colour.models.common.Jab_to_JCh` definition.""" np.testing.assert_almost_equal( Jab_to_JCh(np.array([41.52787529, 52.63858304, 26.92317922])), np.array([41.52787529, 59.12425901, 27.08848784]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Jab_to_JCh(np.array([55.11636304, -41.08791787, 30.91825778])), np.array([55.11636304, 51.42135412, 143.03889556]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Jab_to_JCh(np.array([29.80565520, 20.01830466, -48.34913874])), np.array([29.80565520, 52.32945383, 292.49133666]), - decimal=7) + decimal=7, + ) def test_n_dimensional_Jab_to_JCh(self): """ - Tests :func:`colour.models.common.Jab_to_JCh` definition n-dimensional + Test :func:`colour.models.common.Jab_to_JCh` definition n-dimensional arrays support. """ @@ -67,25 +66,27 @@ def test_n_dimensional_Jab_to_JCh(self): def test_domain_range_scale_Jab_to_JCh(self): """ - Tests :func:`colour.models.common.Jab_to_JCh` definition domain and + Test :func:`colour.models.common.Jab_to_JCh` definition domain and range scale support. """ Lab = np.array([41.52787529, 52.63858304, 26.92317922]) LCHab = Jab_to_JCh(Lab) - d_r = (('reference', 1, 1), (1, 0.01, np.array([0.01, 0.01, 1 / 360])), - (100, 1, np.array([1, 1, 1 / 3.6]))) + d_r = ( + ("reference", 1, 1), + ("1", 0.01, np.array([0.01, 0.01, 1 / 360])), + ("100", 1, np.array([1, 1, 1 / 3.6])), + ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - Jab_to_JCh(Lab * factor_a), LCHab * factor_b, decimal=7) + Jab_to_JCh(Lab * factor_a), LCHab * factor_b, decimal=7 + ) @ignore_numpy_errors def test_nan_Jab_to_JCh(self): - """ - Tests :func:`colour.models.common.Jab_to_JCh` definition nan support. - """ + """Test :func:`colour.models.common.Jab_to_JCh` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -96,33 +97,34 @@ def test_nan_Jab_to_JCh(self): class TestJCh_to_Jab(unittest.TestCase): """ - Defines :func:`colour.models.common.JCh_to_Jab` definition unit tests + Define :func:`colour.models.common.JCh_to_Jab` definition unit tests methods. """ def test_JCh_to_Jab(self): - """ - Tests :func:`colour.models.common.JCh_to_Jab` definition. - """ + """Test :func:`colour.models.common.JCh_to_Jab` definition.""" np.testing.assert_almost_equal( JCh_to_Jab(np.array([41.52787529, 59.12425901, 27.08848784])), np.array([41.52787529, 52.63858304, 26.92317922]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( JCh_to_Jab(np.array([55.11636304, 51.42135412, 143.03889556])), np.array([55.11636304, -41.08791787, 30.91825778]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( JCh_to_Jab(np.array([29.80565520, 52.32945383, 292.49133666])), np.array([29.80565520, 20.01830466, -48.34913874]), - decimal=7) + decimal=7, + ) def test_n_dimensional_JCh_to_Jab(self): """ - Tests :func:`colour.models.common.JCh_to_Jab` definition n-dimensional + Test :func:`colour.models.common.JCh_to_Jab` definition n-dimensional arrays support. """ @@ -139,25 +141,27 @@ def test_n_dimensional_JCh_to_Jab(self): def test_domain_range_scale_JCh_to_Jab(self): """ - Tests :func:`colour.models.common.JCh_to_Jab` definition domain and + Test :func:`colour.models.common.JCh_to_Jab` definition domain and range scale support. """ LCHab = np.array([41.52787529, 59.12425901, 27.08848784]) Lab = JCh_to_Jab(LCHab) - d_r = (('reference', 1, 1), (1, np.array([0.01, 0.01, 1 / 360]), 0.01), - (100, np.array([1, 1, 1 / 3.6]), 1)) + d_r = ( + ("reference", 1, 1), + ("1", np.array([0.01, 0.01, 1 / 360]), 0.01), + ("100", np.array([1, 1, 1 / 3.6]), 1), + ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - JCh_to_Jab(LCHab * factor_a), Lab * factor_b, decimal=7) + JCh_to_Jab(LCHab * factor_a), Lab * factor_b, decimal=7 + ) @ignore_numpy_errors def test_nan_JCh_to_Jab(self): - """ - Tests :func:`colour.models.common.JCh_to_Jab` definition nan support. - """ + """Test :func:`colour.models.common.JCh_to_Jab` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -166,5 +170,5 @@ def test_nan_JCh_to_Jab(self): JCh_to_Jab(LCHab) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/tests/test_din99.py b/colour/models/tests/test_din99.py index 08d9916277..6035830d29 100644 --- a/colour/models/tests/test_din99.py +++ b/colour/models/tests/test_din99.py @@ -1,56 +1,89 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.din99` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.din99` module.""" import numpy as np import unittest from itertools import permutations -from colour.models import Lab_to_DIN99, DIN99_to_Lab +from colour.models import ( + Lab_to_DIN99, + DIN99_to_Lab, + XYZ_to_DIN99, + DIN99_to_XYZ, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestLab_to_DIN99', 'TestDIN99_to_Lab'] +__all__ = [ + "TestLab_to_DIN99", + "TestDIN99_to_Lab", + "TestXYZ_to_DIN99", + "TestDIN99_to_XYZ", +] class TestLab_to_DIN99(unittest.TestCase): """ - Defines :func:`colour.models.din99.Lab_to_DIN99` definition unit tests + Define :func:`colour.models.din99.Lab_to_DIN99` definition unit tests methods. """ def test_Lab_to_DIN99(self): - """ - Tests :func:`colour.models.din99.Lab_to_DIN99` definition. - """ + """Test :func:`colour.models.din99.Lab_to_DIN99` definition.""" np.testing.assert_almost_equal( Lab_to_DIN99(np.array([41.52787529, 52.63858304, 26.92317922])), np.array([53.22821988, 28.41634656, 3.89839552]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Lab_to_DIN99(np.array([55.11636304, -41.08791787, 30.91825778])), np.array([66.08943912, -17.35290106, 16.09690691]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Lab_to_DIN99(np.array([29.80565520, 20.01830466, -48.34913874])), np.array([40.71533366, 3.48714163, -21.45321411]), - decimal=7) + decimal=7, + ) + + np.testing.assert_almost_equal( + Lab_to_DIN99( + np.array([41.52787529, 52.63858304, 26.92317922]), + method="DIN99b", + ), + np.array([45.58303137, 34.71824493, 17.61622149]), + decimal=7, + ) + + np.testing.assert_almost_equal( + Lab_to_DIN99( + np.array([41.52787529, 52.63858304, 26.92317922]), + method="DIN99c", + ), + np.array([45.40284208, 32.75074741, 15.74603532]), + decimal=7, + ) + + np.testing.assert_almost_equal( + Lab_to_DIN99( + np.array([41.52787529, 52.63858304, 26.92317922]), + method="DIN99d", + ), + np.array([45.31204747, 31.42106716, 14.17004652]), + decimal=7, + ) def test_n_dimensional_Lab_to_DIN99(self): """ - Tests :func:`colour.models.din99.Lab_to_DIN99` definition n-dimensional + Test :func:`colour.models.din99.Lab_to_DIN99` definition n-dimensional support. """ @@ -67,60 +100,108 @@ def test_n_dimensional_Lab_to_DIN99(self): def test_domain_range_scale_Lab_to_DIN99(self): """ - Tests :func:`colour.models.din99.Lab_to_DIN99` definition domain and + Test :func:`colour.models.din99.Lab_to_DIN99` definition domain and range scale support. """ Lab = np.array([41.52787529, 52.63858304, 26.92317922]) Lab_99 = Lab_to_DIN99(Lab) + Lab_99_b = Lab_to_DIN99(Lab, method="DIN99b") + Lab_99_c = Lab_to_DIN99(Lab, method="DIN99c") + Lab_99_d = Lab_to_DIN99(Lab, method="DIN99d") - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - Lab_to_DIN99(Lab * factor), Lab_99 * factor, decimal=7) + Lab_to_DIN99(Lab * factor), Lab_99 * factor, decimal=7 + ) + np.testing.assert_almost_equal( + Lab_to_DIN99((Lab * factor), method="DIN99b"), + Lab_99_b * factor, + decimal=7, + ) + np.testing.assert_almost_equal( + Lab_to_DIN99((Lab * factor), method="DIN99c"), + Lab_99_c * factor, + decimal=7, + ) + np.testing.assert_almost_equal( + Lab_to_DIN99((Lab * factor), method="DIN99d"), + Lab_99_d * factor, + decimal=7, + ) @ignore_numpy_errors def test_nan_Lab_to_DIN99(self): - """ - Tests :func:`colour.models.din99.Lab_to_DIN99` definition nan support. - """ + """Test :func:`colour.models.din99.Lab_to_DIN99` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) for case in cases: Lab_to_DIN99(np.array(case)) + Lab_to_DIN99(np.array(case), method="DIN99b") + Lab_to_DIN99(np.array(case), method="DIN99c") + Lab_to_DIN99(np.array(case), method="DIN99d") class TestDIN99_to_Lab(unittest.TestCase): """ - Defines :func:`colour.models.din99.DIN99_to_Lab` definition unit tests + Define :func:`colour.models.din99.DIN99_to_Lab` definition unit tests methods. """ def test_DIN99_to_Lab(self): - """ - Tests :func:`colour.models.din99.DIN99_to_Lab` definition. - """ + """Test :func:`colour.models.din99.DIN99_to_Lab` definition.""" np.testing.assert_almost_equal( DIN99_to_Lab(np.array([53.22821988, 28.41634656, 3.89839552])), np.array([41.52787529, 52.63858304, 26.92317922]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( DIN99_to_Lab(np.array([66.08943912, -17.35290106, 16.09690691])), np.array([55.11636304, -41.08791787, 30.91825778]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( DIN99_to_Lab(np.array([40.71533366, 3.48714163, -21.45321411])), np.array([29.80565520, 20.01830466, -48.34913874]), - decimal=7) + decimal=7, + ) + + np.testing.assert_almost_equal( + DIN99_to_Lab( + np.array([45.58303137, 34.71824493, 17.61622149]), + method="DIN99b", + ), + np.array([41.52787529, 52.63858304, 26.92317922]), + decimal=7, + ) + + np.testing.assert_almost_equal( + DIN99_to_Lab( + np.array([45.40284208, 32.75074741, 15.74603532]), + method="DIN99c", + ), + np.array([41.52787529, 52.63858304, 26.92317922]), + decimal=7, + ) + + np.testing.assert_almost_equal( + DIN99_to_Lab( + np.array([45.31204747, 31.42106716, 14.17004652]), + method="DIN99d", + ), + np.array([41.52787529, 52.63858304, 26.92317922]), + decimal=7, + ) def test_n_dimensional_DIN99_to_Lab(self): """ - Tests :func:`colour.models.din99.DIN99_to_Lab` definition n-dimensional + Test :func:`colour.models.din99.DIN99_to_Lab` definition n-dimensional support. """ @@ -137,30 +218,207 @@ def test_n_dimensional_DIN99_to_Lab(self): def test_domain_range_scale_DIN99_to_Lab(self): """ - Tests :func:`colour.models.din99.DIN99_to_Lab` definition domain and + Test :func:`colour.models.din99.DIN99_to_Lab` definition domain and range scale support. """ Lab_99 = np.array([53.22821988, 28.41634656, 3.89839552]) Lab = DIN99_to_Lab(Lab_99) + Lab_b = DIN99_to_Lab(Lab_99, method="DIN99b") + Lab_c = DIN99_to_Lab(Lab_99, method="DIN99c") + Lab_d = DIN99_to_Lab(Lab_99, method="DIN99d") - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - DIN99_to_Lab(Lab_99 * factor), Lab * factor, decimal=7) + DIN99_to_Lab(Lab_99 * factor), Lab * factor, decimal=7 + ) + np.testing.assert_almost_equal( + DIN99_to_Lab((Lab_99 * factor), method="DIN99b"), + Lab_b * factor, + decimal=7, + ) + np.testing.assert_almost_equal( + DIN99_to_Lab((Lab_99 * factor), method="DIN99c"), + Lab_c * factor, + decimal=7, + ) + np.testing.assert_almost_equal( + DIN99_to_Lab((Lab_99 * factor), method="DIN99d"), + Lab_d * factor, + decimal=7, + ) @ignore_numpy_errors def test_nan_DIN99_to_Lab(self): + """Test :func:`colour.models.din99.DIN99_to_Lab` definition nan support.""" + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + DIN99_to_Lab(np.array(case)) + DIN99_to_Lab(np.array(case), method="DIN99b") + DIN99_to_Lab(np.array(case), method="DIN99c") + DIN99_to_Lab(np.array(case), method="DIN99d") + + +class TestXYZ_to_DIN99(unittest.TestCase): + """ + Define :func:`colour.models.din99.XYZ_to_DIN99` definition unit tests + methods. + """ + + def test_XYZ_to_DIN99(self): + """Test :func:`colour.models.din99.XYZ_to_DIN99` definition.""" + + np.testing.assert_almost_equal( + XYZ_to_DIN99(np.array([0.20654008, 0.12197225, 0.05136952])), + np.array([53.22821988, 28.41634656, 3.89839552]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_DIN99(np.array([0.14222010, 0.23042768, 0.10495772])), + np.array([66.08943912, -17.35290106, 16.09690691]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_DIN99(np.array([0.07818780, 0.06157201, 0.28099326])), + np.array([40.71533366, 3.48714163, -21.45321411]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_DIN99( + np.array([0.20654008, 0.12197225, 0.05136952]), method="DIN99b" + ), + np.array([45.58303137, 34.71824493, 17.61622149]), + decimal=7, + ) + + def test_n_dimensional_XYZ_to_DIN99(self): """ - Tests :func:`colour.models.din99.DIN99_to_Lab` definition nan support. + Test :func:`colour.models.din99.XYZ_to_DIN99` definition n-dimensional + support. """ + XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + Lab_99 = XYZ_to_DIN99(XYZ) + + XYZ = np.tile(XYZ, (6, 1)) + Lab_99 = np.tile(Lab_99, (6, 1)) + np.testing.assert_almost_equal(XYZ_to_DIN99(XYZ), Lab_99, decimal=7) + + XYZ = np.reshape(XYZ, (2, 3, 3)) + Lab_99 = np.reshape(Lab_99, (2, 3, 3)) + np.testing.assert_almost_equal(XYZ_to_DIN99(XYZ), Lab_99, decimal=7) + + def test_domain_range_scale_XYZ_to_DIN99(self): + """ + Test :func:`colour.models.din99.XYZ_to_DIN99` definition domain and + range scale support. + """ + + XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + Lab_99 = XYZ_to_DIN99(XYZ) + + d_r = (("reference", 1, 1), ("1", 1, 0.01), ("100", 100, 1)) + for scale, factor_a, factor_b in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + XYZ_to_DIN99(XYZ * factor_a), Lab_99 * factor_b, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_XYZ_to_DIN99(self): + """Test :func:`colour.models.din99.XYZ_to_DIN99` definition nan support.""" + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) for case in cases: - DIN99_to_Lab(np.array(case)) + XYZ_to_DIN99(np.array(case)) + + +class TestDIN99_to_XYZ(unittest.TestCase): + """ + Define :func:`colour.models.din99.DIN99_to_XYZ` definition unit tests + methods. + """ + + def test_DIN99_to_XYZ(self): + """Test :func:`colour.models.din99.DIN99_to_XYZ` definition.""" + + np.testing.assert_almost_equal( + DIN99_to_XYZ(np.array([53.22821988, 28.41634656, 3.89839552])), + np.array([0.20654008, 0.12197225, 0.05136952]), + decimal=7, + ) + + np.testing.assert_almost_equal( + DIN99_to_XYZ(np.array([66.08943912, -17.35290106, 16.09690691])), + np.array([0.14222010, 0.23042768, 0.10495772]), + decimal=7, + ) + + np.testing.assert_almost_equal( + DIN99_to_XYZ(np.array([40.71533366, 3.48714163, -21.45321411])), + np.array([0.07818780, 0.06157201, 0.28099326]), + decimal=7, + ) + + np.testing.assert_almost_equal( + DIN99_to_XYZ( + np.array([45.58303137, 34.71824493, 17.61622149]), + method="DIN99b", + ), + np.array([0.20654008, 0.12197225, 0.05136952]), + decimal=7, + ) + + def test_n_dimensional_DIN99_to_XYZ(self): + """ + Test :func:`colour.models.din99.DIN99_to_XYZ` definition n-dimensional + support. + """ + + Lab_99 = np.array([53.22821988, 28.41634656, 3.89839552]) + XYZ = DIN99_to_XYZ(Lab_99) + + Lab_99 = np.tile(Lab_99, (6, 1)) + XYZ = np.tile(XYZ, (6, 1)) + np.testing.assert_almost_equal(DIN99_to_XYZ(Lab_99), XYZ, decimal=7) + + Lab_99 = np.reshape(Lab_99, (2, 3, 3)) + XYZ = np.reshape(XYZ, (2, 3, 3)) + np.testing.assert_almost_equal(DIN99_to_XYZ(Lab_99), XYZ, decimal=7) + + def test_domain_range_scale_DIN99_to_XYZ(self): + """ + Test :func:`colour.models.din99.DIN99_to_XYZ` definition domain and + range scale support. + """ + + Lab_99 = np.array([53.22821988, 28.41634656, 3.89839552]) + XYZ = DIN99_to_XYZ(Lab_99) + + d_r = (("reference", 1, 1), ("1", 0.01, 1), ("100", 1, 100)) + for scale, factor_a, factor_b in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + DIN99_to_XYZ(Lab_99 * factor_a), XYZ * factor_b, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_DIN99_to_XYZ(self): + """Test :func:`colour.models.din99.DIN99_to_XYZ` definition nan support.""" + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + DIN99_to_XYZ(np.array(case)) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/tests/test_hdr_cie_lab.py b/colour/models/tests/test_hdr_cie_lab.py index 9579ff12fb..582b43e540 100644 --- a/colour/models/tests/test_hdr_cie_lab.py +++ b/colour/models/tests/test_hdr_cie_lab.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.hdr_cie_lab` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.hdr_cie_lab` module.""" import numpy as np import unittest @@ -13,47 +8,53 @@ from colour.models.hdr_cie_lab import exponent_hdr_CIELab from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestExponent_hdr_CIELab', 'TestXYZ_to_hdr_CIELab', 'TestHdr_CIELab_to_XYZ' + "TestExponent_hdr_CIELab", + "TestXYZ_to_hdr_CIELab", + "TestHdr_CIELab_to_XYZ", ] class TestExponent_hdr_CIELab(unittest.TestCase): """ - Defines :func:`colour.models.hdr_cie_lab.exponent_hdr_CIELab` + Define :func:`colour.models.hdr_cie_lab.exponent_hdr_CIELab` definition unit tests methods. """ def test_exponent_hdr_CIELab(self): """ - Tests :func:`colour.models.hdr_cie_lab.exponent_hdr_CIELab` + Test :func:`colour.models.hdr_cie_lab.exponent_hdr_CIELab` definition. """ self.assertAlmostEqual( - exponent_hdr_CIELab(0.2, 100), 0.473851073746817, places=7) + exponent_hdr_CIELab(0.2, 100), 0.473851073746817, places=7 + ) self.assertAlmostEqual( - exponent_hdr_CIELab(0.4, 100), 0.656101486726362, places=7) + exponent_hdr_CIELab(0.4, 100), 0.656101486726362, places=7 + ) self.assertAlmostEqual( - exponent_hdr_CIELab(0.4, 100, method='Fairchild 2010'), + exponent_hdr_CIELab(0.4, 100, method="Fairchild 2010"), 1.326014370643925, - places=7) + places=7, + ) self.assertAlmostEqual( - exponent_hdr_CIELab(0.2, 1000), 0.710776610620225, places=7) + exponent_hdr_CIELab(0.2, 1000), 0.710776610620225, places=7 + ) def test_n_dimensional_exponent_hdr_CIELab(self): """ - Tests :func:`colour.models.hdr_cie_lab.exponent_hdr_CIELab` + Test :func:`colour.models.hdr_cie_lab.exponent_hdr_CIELab` definition n-dimensional arrays support. """ @@ -65,23 +66,26 @@ def test_n_dimensional_exponent_hdr_CIELab(self): Y_abs = np.tile(Y_abs, 6) epsilon = np.tile(epsilon, 6) np.testing.assert_almost_equal( - exponent_hdr_CIELab(Y_s, Y_abs), epsilon, decimal=7) + exponent_hdr_CIELab(Y_s, Y_abs), epsilon, decimal=7 + ) Y_s = np.reshape(Y_s, (2, 3)) Y_abs = np.reshape(Y_abs, (2, 3)) epsilon = np.reshape(epsilon, (2, 3)) np.testing.assert_almost_equal( - exponent_hdr_CIELab(Y_s, Y_abs), epsilon, decimal=7) + exponent_hdr_CIELab(Y_s, Y_abs), epsilon, decimal=7 + ) Y_s = np.reshape(Y_s, (2, 3, 1)) Y_abs = np.reshape(Y_abs, (2, 3, 1)) epsilon = np.reshape(epsilon, (2, 3, 1)) np.testing.assert_almost_equal( - exponent_hdr_CIELab(Y_s, Y_abs), epsilon, decimal=7) + exponent_hdr_CIELab(Y_s, Y_abs), epsilon, decimal=7 + ) def test_domain_range_scale_exponent_hdr_CIELab(self): """ - Tests :func:`colour.models.hdr_cie_lab.exponent_hdr_CIELab` definition + Test :func:`colour.models.hdr_cie_lab.exponent_hdr_CIELab` definition domain and range scale support. """ @@ -89,18 +93,19 @@ def test_domain_range_scale_exponent_hdr_CIELab(self): Y_abs = 100 epsilon = exponent_hdr_CIELab(Y_s, Y_abs) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( exponent_hdr_CIELab(Y_s * factor, Y_abs), epsilon, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_exponent_hdr_CIELab(self): """ - Tests :func:`colour.models.hdr_cie_lab.exponent_hdr_CIELab` + Test :func:`colour.models.hdr_cie_lab.exponent_hdr_CIELab` definition nan support. """ @@ -110,50 +115,57 @@ def test_nan_exponent_hdr_CIELab(self): class TestXYZ_to_hdr_CIELab(unittest.TestCase): """ - Defines :func:`colour.models.hdr_cie_lab.XYZ_to_hdr_CIELab` definition unit + Define :func:`colour.models.hdr_cie_lab.XYZ_to_hdr_CIELab` definition unit tests methods. """ def test_XYZ_to_hdr_CIELab(self): - """ - Tests :func:`colour.models.hdr_cie_lab.XYZ_to_hdr_CIELab` definition. - """ + """Test :func:`colour.models.hdr_cie_lab.XYZ_to_hdr_CIELab` definition.""" np.testing.assert_almost_equal( XYZ_to_hdr_CIELab(np.array([0.20654008, 0.12197225, 0.05136952])), np.array([51.87002062, 60.47633850, 32.14551912]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_hdr_CIELab( np.array([0.20654008, 0.12197225, 0.05136952]), - np.array([0.44757, 0.40745])), + np.array([0.44757, 0.40745]), + ), np.array([51.87002062, 44.49667330, -6.69619196]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_hdr_CIELab( np.array([0.20654008, 0.12197225, 0.05136952]), np.array([0.44757, 0.40745]), - method='Fairchild 2010'), + method="Fairchild 2010", + ), np.array([31.99621114, 95.08564341, -14.14047055]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_hdr_CIELab( - np.array([0.20654008, 0.12197225, 0.05136952]), Y_s=0.5), + np.array([0.20654008, 0.12197225, 0.05136952]), Y_s=0.5 + ), np.array([23.10388654, 59.31425004, 23.69960142]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_hdr_CIELab( - np.array([0.20654008, 0.12197225, 0.05136952]), Y_abs=1000), + np.array([0.20654008, 0.12197225, 0.05136952]), Y_abs=1000 + ), np.array([29.77261805, 62.58315675, 27.31232673]), - decimal=7) + decimal=7, + ) def test_n_dimensional_XYZ_to_hdr_CIELab(self): """ - Tests :func:`colour.models.hdr_cie_lab.XYZ_to_hdr_CIELab` definition + Test :func:`colour.models.hdr_cie_lab.XYZ_to_hdr_CIELab` definition n-dimensional support. """ @@ -166,13 +178,15 @@ def test_n_dimensional_XYZ_to_hdr_CIELab(self): XYZ = np.tile(XYZ, (6, 1)) Lab_hdr = np.tile(Lab_hdr, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_hdr_CIELab(XYZ, illuminant, Y_s, Y_abs), Lab_hdr, decimal=7) + XYZ_to_hdr_CIELab(XYZ, illuminant, Y_s, Y_abs), Lab_hdr, decimal=7 + ) illuminant = np.tile(illuminant, (6, 1)) Y_s = np.tile(Y_s, 6) Y_abs = np.tile(Y_abs, 6) np.testing.assert_almost_equal( - XYZ_to_hdr_CIELab(XYZ, illuminant, Y_s, Y_abs), Lab_hdr, decimal=7) + XYZ_to_hdr_CIELab(XYZ, illuminant, Y_s, Y_abs), Lab_hdr, decimal=7 + ) XYZ = np.reshape(XYZ, (2, 3, 3)) illuminant = np.reshape(illuminant, (2, 3, 2)) @@ -180,11 +194,12 @@ def test_n_dimensional_XYZ_to_hdr_CIELab(self): Y_abs = np.reshape(Y_abs, (2, 3)) Lab_hdr = np.reshape(Lab_hdr, (2, 3, 3)) np.testing.assert_almost_equal( - XYZ_to_hdr_CIELab(XYZ, illuminant, Y_s, Y_abs), Lab_hdr, decimal=7) + XYZ_to_hdr_CIELab(XYZ, illuminant, Y_s, Y_abs), Lab_hdr, decimal=7 + ) def test_domain_range_scale_XYZ_to_hdr_CIELab(self): """ - Tests :func:`colour.models.hdr_cie_lab.XYZ_to_hdr_CIELab` definition + Test :func:`colour.models.hdr_cie_lab.XYZ_to_hdr_CIELab` definition domain and range scale support. """ @@ -194,19 +209,21 @@ def test_domain_range_scale_XYZ_to_hdr_CIELab(self): Y_abs = 100 Lab_hdr = XYZ_to_hdr_CIELab(XYZ, illuminant, Y_s, Y_abs) - d_r = (('reference', 1, 1), (1, 1, 0.01), (100, 100, 1)) + d_r = (("reference", 1, 1), ("1", 1, 0.01), ("100", 100, 1)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - XYZ_to_hdr_CIELab(XYZ * factor_a, illuminant, - Y_s * factor_a, Y_abs), + XYZ_to_hdr_CIELab( + XYZ * factor_a, illuminant, Y_s * factor_a, Y_abs + ), Lab_hdr * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_XYZ_to_hdr_CIELab(self): """ - Tests :func:`colour.models.hdr_cie_lab.XYZ_to_hdr_CIELab` definition + Test :func:`colour.models.hdr_cie_lab.XYZ_to_hdr_CIELab` definition nan support. """ @@ -222,51 +239,59 @@ def test_nan_XYZ_to_hdr_CIELab(self): class TestHdr_CIELab_to_XYZ(unittest.TestCase): """ - Defines :func:`colour.models.hdr_cie_lab.hdr_CIELab_to_XYZ` definition unit + Define :func:`colour.models.hdr_cie_lab.hdr_CIELab_to_XYZ` definition unit tests methods. """ def test_hdr_CIELab_to_XYZ(self): - """ - Tests :func:`colour.models.hdr_cie_lab.hdr_CIELab_to_XYZ` definition. - """ + """Test :func:`colour.models.hdr_cie_lab.hdr_CIELab_to_XYZ` definition.""" np.testing.assert_almost_equal( hdr_CIELab_to_XYZ( - np.array([51.87002062, 60.47633850, 32.14551912])), + np.array([51.87002062, 60.47633850, 32.14551912]) + ), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( hdr_CIELab_to_XYZ( np.array([51.87002062, 44.49667330, -6.69619196]), - np.array([0.44757, 0.40745])), + np.array([0.44757, 0.40745]), + ), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( hdr_CIELab_to_XYZ( np.array([31.99621114, 95.08564341, -14.14047055]), np.array([0.44757, 0.40745]), - method='Fairchild 2010'), + method="Fairchild 2010", + ), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( hdr_CIELab_to_XYZ( - np.array([23.10388654, 59.31425004, 23.69960142]), Y_s=0.5), + np.array([23.10388654, 59.31425004, 23.69960142]), Y_s=0.5 + ), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( hdr_CIELab_to_XYZ( - np.array([29.77261805, 62.58315675, 27.31232673]), Y_abs=1000), + np.array([29.77261805, 62.58315675, 27.31232673]), Y_abs=1000 + ), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) def test_n_dimensional_hdr_CIELab_to_XYZ(self): """ - Tests :func:`colour.models.hdr_cie_lab.hdr_CIELab_to_XYZ` definition + Test :func:`colour.models.hdr_cie_lab.hdr_CIELab_to_XYZ` definition n-dimensional support. """ @@ -279,13 +304,15 @@ def test_n_dimensional_hdr_CIELab_to_XYZ(self): Lab_hdr = np.tile(Lab_hdr, (6, 1)) XYZ = np.tile(XYZ, (6, 1)) np.testing.assert_almost_equal( - hdr_CIELab_to_XYZ(Lab_hdr, illuminant, Y_s, Y_abs), XYZ, decimal=7) + hdr_CIELab_to_XYZ(Lab_hdr, illuminant, Y_s, Y_abs), XYZ, decimal=7 + ) illuminant = np.tile(illuminant, (6, 1)) Y_s = np.tile(Y_s, 6) Y_abs = np.tile(Y_abs, 6) np.testing.assert_almost_equal( - hdr_CIELab_to_XYZ(Lab_hdr, illuminant, Y_s, Y_abs), XYZ, decimal=7) + hdr_CIELab_to_XYZ(Lab_hdr, illuminant, Y_s, Y_abs), XYZ, decimal=7 + ) Lab_hdr = np.reshape(Lab_hdr, (2, 3, 3)) illuminant = np.reshape(illuminant, (2, 3, 2)) @@ -293,11 +320,12 @@ def test_n_dimensional_hdr_CIELab_to_XYZ(self): Y_abs = np.reshape(Y_abs, (2, 3)) XYZ = np.reshape(XYZ, (2, 3, 3)) np.testing.assert_almost_equal( - hdr_CIELab_to_XYZ(Lab_hdr, illuminant, Y_s, Y_abs), XYZ, decimal=7) + hdr_CIELab_to_XYZ(Lab_hdr, illuminant, Y_s, Y_abs), XYZ, decimal=7 + ) def test_domain_range_scale_hdr_CIELab_to_XYZ(self): """ - Tests :func:`colour.models.hdr_cie_lab.hdr_CIELab_to_XYZ` definition + Test :func:`colour.models.hdr_cie_lab.hdr_CIELab_to_XYZ` definition domain and range scale support. """ @@ -307,19 +335,21 @@ def test_domain_range_scale_hdr_CIELab_to_XYZ(self): Y_abs = 100 XYZ = hdr_CIELab_to_XYZ(Lab_hdr, illuminant, Y_s, Y_abs) - d_r = (('reference', 1, 1, 1), (1, 0.01, 1, 1), (100, 1, 100, 100)) + d_r = (("reference", 1, 1, 1), ("1", 0.01, 1, 1), ("100", 1, 100, 100)) for scale, factor_a, factor_b, factor_c in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - hdr_CIELab_to_XYZ(Lab_hdr * factor_a, illuminant, - Y_s * factor_b, Y_abs), + hdr_CIELab_to_XYZ( + Lab_hdr * factor_a, illuminant, Y_s * factor_b, Y_abs + ), XYZ * factor_c, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_hdr_CIELab_to_XYZ(self): """ - Tests :func:`colour.models.hdr_cie_lab.hdr_CIELab_to_XYZ` definition + Test :func:`colour.models.hdr_cie_lab.hdr_CIELab_to_XYZ` definition nan support. """ @@ -333,5 +363,5 @@ def test_nan_hdr_CIELab_to_XYZ(self): hdr_CIELab_to_XYZ(Lab_hdr, illuminant, Y_s, Y_abs) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/tests/test_hdr_ipt.py b/colour/models/tests/test_hdr_ipt.py index a4203b6565..e637eae11a 100644 --- a/colour/models/tests/test_hdr_ipt.py +++ b/colour/models/tests/test_hdr_ipt.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.hdr_ipt` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.hdr_ipt` module.""" import numpy as np import unittest @@ -13,44 +8,50 @@ from colour.models.hdr_ipt import exponent_hdr_IPT from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestExponent_hdr_IPT', 'TestXYZ_to_hdr_IPT', 'TestHdr_IPT_to_XYZ'] +__all__ = [ + "TestExponent_hdr_IPT", + "TestXYZ_to_hdr_IPT", + "TestHdr_IPT_to_XYZ", +] class TestExponent_hdr_IPT(unittest.TestCase): """ - Defines :func:`colour.models.hdr_ipt.exponent_hdr_IPT` + Define :func:`colour.models.hdr_ipt.exponent_hdr_IPT` definition unit tests methods. """ def test_exponent_hdr_IPT(self): - """ - Tests :func:`colour.models.hdr_ipt.exponent_hdr_IPT` definition. - """ + """Test :func:`colour.models.hdr_ipt.exponent_hdr_IPT` definition.""" self.assertAlmostEqual( - exponent_hdr_IPT(0.2, 100), 0.482020919845900, places=7) + exponent_hdr_IPT(0.2, 100), 0.482020919845900, places=7 + ) self.assertAlmostEqual( - exponent_hdr_IPT(0.4, 100), 0.667413581325092, places=7) + exponent_hdr_IPT(0.4, 100), 0.667413581325092, places=7 + ) self.assertAlmostEqual( - exponent_hdr_IPT(0.4, 100, method='Fairchild 2010'), + exponent_hdr_IPT(0.4, 100, method="Fairchild 2010"), 1.219933220992410, - places=7) + places=7, + ) self.assertAlmostEqual( - exponent_hdr_IPT(0.2, 1000), 0.723031379768850, places=7) + exponent_hdr_IPT(0.2, 1000), 0.723031379768850, places=7 + ) def test_n_dimensional_exponent_hdr_IPT(self): """ - Tests :func:`colour.models.hdr_ipt.exponent_hdr_IPT` definition + Test :func:`colour.models.hdr_ipt.exponent_hdr_IPT` definition n-dimensional arrays support. """ @@ -62,23 +63,26 @@ def test_n_dimensional_exponent_hdr_IPT(self): Y_abs = np.tile(Y_abs, 6) epsilon = np.tile(epsilon, 6) np.testing.assert_almost_equal( - exponent_hdr_IPT(Y_s, Y_abs), epsilon, decimal=7) + exponent_hdr_IPT(Y_s, Y_abs), epsilon, decimal=7 + ) Y_s = np.reshape(Y_s, (2, 3)) Y_abs = np.reshape(Y_abs, (2, 3)) epsilon = np.reshape(epsilon, (2, 3)) np.testing.assert_almost_equal( - exponent_hdr_IPT(Y_s, Y_abs), epsilon, decimal=7) + exponent_hdr_IPT(Y_s, Y_abs), epsilon, decimal=7 + ) Y_s = np.reshape(Y_s, (2, 3, 1)) Y_abs = np.reshape(Y_abs, (2, 3, 1)) epsilon = np.reshape(epsilon, (2, 3, 1)) np.testing.assert_almost_equal( - exponent_hdr_IPT(Y_s, Y_abs), epsilon, decimal=7) + exponent_hdr_IPT(Y_s, Y_abs), epsilon, decimal=7 + ) def test_domain_range_scale_exponent_hdr_IPT(self): """ - Tests :func:`colour.models.hdr_ipt.exponent_hdr_IPT` definition domain + Test :func:`colour.models.hdr_ipt.exponent_hdr_IPT` definition domain and range scale support. """ @@ -86,16 +90,17 @@ def test_domain_range_scale_exponent_hdr_IPT(self): Y_abs = 100 epsilon = exponent_hdr_IPT(Y_s, Y_abs) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - exponent_hdr_IPT(Y_s * factor, Y_abs), epsilon, decimal=7) + exponent_hdr_IPT(Y_s * factor, Y_abs), epsilon, decimal=7 + ) @ignore_numpy_errors def test_nan_exponent_hdr_IPT(self): """ - Tests :func:`colour.models.hdr_ipt.exponent_hdr_IPT` definition nan + Test :func:`colour.models.hdr_ipt.exponent_hdr_IPT` definition nan support. """ @@ -105,42 +110,47 @@ def test_nan_exponent_hdr_IPT(self): class TestXYZ_to_hdr_IPT(unittest.TestCase): """ - Defines :func:`colour.models.hdr_ipt.XYZ_to_hdr_IPT` definition unit tests + Define :func:`colour.models.hdr_ipt.XYZ_to_hdr_IPT` definition unit tests methods. """ def test_XYZ_to_hdr_IPT(self): - """ - Tests :func:`colour.models.hdr_ipt.XYZ_to_hdr_IPT` definition. - """ + """Test :func:`colour.models.hdr_ipt.XYZ_to_hdr_IPT` definition.""" np.testing.assert_almost_equal( XYZ_to_hdr_IPT(np.array([0.20654008, 0.12197225, 0.05136952])), np.array([48.39376346, 42.44990202, 22.01954033]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_hdr_IPT( np.array([0.20654008, 0.12197225, 0.05136952]), - method='Fairchild 2010'), + method="Fairchild 2010", + ), np.array([30.02873147, 83.93845061, 34.90287382]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_hdr_IPT( - np.array([0.20654008, 0.12197225, 0.05136952]), Y_s=0.5), + np.array([0.20654008, 0.12197225, 0.05136952]), Y_s=0.5 + ), np.array([20.75088680, 37.98300971, 16.66974299]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_hdr_IPT( - np.array([0.07818780, 0.06157201, 0.28099326]), Y_abs=1000), + np.array([0.07818780, 0.06157201, 0.28099326]), Y_abs=1000 + ), np.array([23.83205010, -5.98739209, -32.74311745]), - decimal=7) + decimal=7, + ) def test_n_dimensional_XYZ_to_hdr_IPT(self): """ - Tests :func:`colour.models.hdr_ipt.XYZ_to_hdr_IPT` definition + Test :func:`colour.models.hdr_ipt.XYZ_to_hdr_IPT` definition n-dimensional support. """ @@ -152,23 +162,26 @@ def test_n_dimensional_XYZ_to_hdr_IPT(self): XYZ = np.tile(XYZ, (6, 1)) IPT_hdr = np.tile(IPT_hdr, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_hdr_IPT(XYZ, Y_s, Y_abs), IPT_hdr, decimal=7) + XYZ_to_hdr_IPT(XYZ, Y_s, Y_abs), IPT_hdr, decimal=7 + ) Y_s = np.tile(Y_s, 6) Y_abs = np.tile(Y_abs, 6) np.testing.assert_almost_equal( - XYZ_to_hdr_IPT(XYZ, Y_s, Y_abs), IPT_hdr, decimal=7) + XYZ_to_hdr_IPT(XYZ, Y_s, Y_abs), IPT_hdr, decimal=7 + ) XYZ = np.reshape(XYZ, (2, 3, 3)) Y_s = np.reshape(Y_s, (2, 3)) Y_abs = np.reshape(Y_abs, (2, 3)) IPT_hdr = np.reshape(IPT_hdr, (2, 3, 3)) np.testing.assert_almost_equal( - XYZ_to_hdr_IPT(XYZ, Y_s, Y_abs), IPT_hdr, decimal=7) + XYZ_to_hdr_IPT(XYZ, Y_s, Y_abs), IPT_hdr, decimal=7 + ) def test_domain_range_scale_XYZ_to_hdr_IPT(self): """ - Tests :func:`colour.models.hdr_ipt.XYZ_to_hdr_IPT` definition domain + Test :func:`colour.models.hdr_ipt.XYZ_to_hdr_IPT` definition domain and range scale support. """ @@ -177,18 +190,19 @@ def test_domain_range_scale_XYZ_to_hdr_IPT(self): Y_abs = 100 IPT_hdr = XYZ_to_hdr_IPT(XYZ, Y_s, Y_abs) - d_r = (('reference', 1, 1), (1, 1, 0.01), (100, 100, 1)) + d_r = (("reference", 1, 1), ("1", 1, 0.01), ("100", 100, 1)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( XYZ_to_hdr_IPT(XYZ * factor_a, Y_s * factor_a, Y_abs), IPT_hdr * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_XYZ_to_hdr_IPT(self): """ - Tests :func:`colour.models.hdr_ipt.XYZ_to_hdr_IPT` definition nan + Test :func:`colour.models.hdr_ipt.XYZ_to_hdr_IPT` definition nan support. """ @@ -203,43 +217,47 @@ def test_nan_XYZ_to_hdr_IPT(self): class TestHdr_IPT_to_XYZ(unittest.TestCase): """ - Defines :func:`colour.models.hdr_ipt.hdr_IPT_to_XYZ` definition unit tests + Define :func:`colour.models.hdr_ipt.hdr_IPT_to_XYZ` definition unit tests methods. """ def test_hdr_IPT_to_XYZ(self): - """ - Tests :func:`colour.models.hdr_ipt.hdr_IPT_to_XYZ` definition. - """ + """Test :func:`colour.models.hdr_ipt.hdr_IPT_to_XYZ` definition.""" np.testing.assert_almost_equal( hdr_IPT_to_XYZ(np.array([48.39376346, 42.44990202, 22.01954033])), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( hdr_IPT_to_XYZ( np.array([30.02873147, 83.93845061, 34.90287382]), - method='Fairchild 2010'), + method="Fairchild 2010", + ), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( hdr_IPT_to_XYZ( - np.array([20.75088680, 37.98300971, 16.66974299]), Y_s=0.5), + np.array([20.75088680, 37.98300971, 16.66974299]), Y_s=0.5 + ), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( hdr_IPT_to_XYZ( - np.array([23.83205010, -5.98739209, -32.74311745]), - Y_abs=1000), + np.array([23.83205010, -5.98739209, -32.74311745]), Y_abs=1000 + ), np.array([0.07818780, 0.06157201, 0.28099326]), - decimal=7) + decimal=7, + ) def test_n_dimensional_hdr_IPT_to_XYZ(self): """ - Tests :func:`colour.models.hdr_ipt.hdr_IPT_to_XYZ` definition + Test :func:`colour.models.hdr_ipt.hdr_IPT_to_XYZ` definition n-dimensional support. """ @@ -251,23 +269,26 @@ def test_n_dimensional_hdr_IPT_to_XYZ(self): IPT_hdr = np.tile(IPT_hdr, (6, 1)) XYZ = np.tile(XYZ, (6, 1)) np.testing.assert_almost_equal( - hdr_IPT_to_XYZ(IPT_hdr, Y_s, Y_abs), XYZ, decimal=7) + hdr_IPT_to_XYZ(IPT_hdr, Y_s, Y_abs), XYZ, decimal=7 + ) Y_s = np.tile(Y_s, 6) Y_abs = np.tile(Y_abs, 6) np.testing.assert_almost_equal( - hdr_IPT_to_XYZ(IPT_hdr, Y_s, Y_abs), XYZ, decimal=7) + hdr_IPT_to_XYZ(IPT_hdr, Y_s, Y_abs), XYZ, decimal=7 + ) IPT_hdr = np.reshape(IPT_hdr, (2, 3, 3)) Y_s = np.reshape(Y_s, (2, 3)) Y_abs = np.reshape(Y_abs, (2, 3)) XYZ = np.reshape(XYZ, (2, 3, 3)) np.testing.assert_almost_equal( - hdr_IPT_to_XYZ(IPT_hdr, Y_s, Y_abs), XYZ, decimal=7) + hdr_IPT_to_XYZ(IPT_hdr, Y_s, Y_abs), XYZ, decimal=7 + ) def test_domain_range_scale_hdr_IPT_to_XYZ(self): """ - Tests :func:`colour.models.hdr_ipt.hdr_IPT_to_XYZ` definition domain + Test :func:`colour.models.hdr_ipt.hdr_IPT_to_XYZ` definition domain and range scale support. """ @@ -276,18 +297,19 @@ def test_domain_range_scale_hdr_IPT_to_XYZ(self): Y_abs = 100 XYZ = hdr_IPT_to_XYZ(IPT_hdr, Y_s, Y_abs) - d_r = (('reference', 1, 1, 1), (1, 0.01, 1, 1), (100, 1, 100, 100)) + d_r = (("reference", 1, 1, 1), ("1", 0.01, 1, 1), ("100", 1, 100, 100)) for scale, factor_a, factor_b, factor_c in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( hdr_IPT_to_XYZ(IPT_hdr * factor_a, Y_s * factor_b, Y_abs), XYZ * factor_c, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_hdr_IPT_to_XYZ(self): """ - Tests :func:`colour.models.hdr_ipt.hdr_IPT_to_XYZ` definition nan + Test :func:`colour.models.hdr_ipt.hdr_IPT_to_XYZ` definition nan support. """ @@ -300,5 +322,5 @@ def test_nan_hdr_IPT_to_XYZ(self): hdr_IPT_to_XYZ(IPT_hdr, Y_s, Y_abs) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/tests/test_hunter_lab.py b/colour/models/tests/test_hunter_lab.py index 7a78f859e8..13a843d65c 100644 --- a/colour/models/tests/test_hunter_lab.py +++ b/colour/models/tests/test_hunter_lab.py @@ -1,66 +1,71 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.hunter_lab` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.hunter_lab` module.""" import numpy as np import unittest from itertools import permutations from colour.colorimetry import TVS_ILLUMINANTS_HUNTERLAB -from colour.models import (XYZ_to_K_ab_HunterLab1966, XYZ_to_Hunter_Lab, - Hunter_Lab_to_XYZ) +from colour.models import ( + XYZ_to_K_ab_HunterLab1966, + XYZ_to_Hunter_Lab, + Hunter_Lab_to_XYZ, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestXYZ_to_K_ab_HunterLab1966', 'TestXYZ_to_Hunter_Lab', - 'TestHunter_Lab_to_XYZ' + "TestXYZ_to_K_ab_HunterLab1966", + "TestXYZ_to_Hunter_Lab", + "TestHunter_Lab_to_XYZ", ] class TestXYZ_to_K_ab_HunterLab1966(unittest.TestCase): """ - Defines :func:`colour.models.hunter_lab.XYZ_to_K_ab_HunterLab1966` + Define :func:`colour.models.hunter_lab.XYZ_to_K_ab_HunterLab1966` definition unit tests methods. """ def test_XYZ_to_K_ab_HunterLab1966(self): """ - Tests :func:`colour.models.hunter_lab.XYZ_to_K_ab_HunterLab1966` + Test :func:`colour.models.hunter_lab.XYZ_to_K_ab_HunterLab1966` definition. """ np.testing.assert_almost_equal( XYZ_to_K_ab_HunterLab1966( - np.array([0.20654008, 0.12197225, 0.05136952]) * 100), + np.array([0.20654008, 0.12197225, 0.05136952]) * 100 + ), np.array([80.32152090, 14.59816495]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_K_ab_HunterLab1966( - np.array([0.14222010, 0.23042768, 0.10495772]) * 100), + np.array([0.14222010, 0.23042768, 0.10495772]) * 100 + ), np.array([66.65154834, 20.86664881]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_K_ab_HunterLab1966( - np.array([0.07818780, 0.06157201, 0.28099326]) * 100), + np.array([0.07818780, 0.06157201, 0.28099326]) * 100 + ), np.array([49.41960269, 34.14235426]), - decimal=7) + decimal=7, + ) def test_n_dimensional_XYZ_to_K_ab_HunterLab1966(self): """ - Tests :func:`colour.models.hunter_lab.XYZ_to_K_ab_HunterLab1966` + Test :func:`colour.models.hunter_lab.XYZ_to_K_ab_HunterLab1966` definition n-dimensional support. """ @@ -70,17 +75,19 @@ def test_n_dimensional_XYZ_to_K_ab_HunterLab1966(self): XYZ = np.tile(XYZ, (6, 1)) K_ab = np.tile(K_ab, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_K_ab_HunterLab1966(XYZ), K_ab, decimal=7) + XYZ_to_K_ab_HunterLab1966(XYZ), K_ab, decimal=7 + ) XYZ = np.reshape(XYZ, (2, 3, 3)) K_ab = np.reshape(K_ab, (2, 3, 2)) np.testing.assert_almost_equal( - XYZ_to_K_ab_HunterLab1966(XYZ), K_ab, decimal=7) + XYZ_to_K_ab_HunterLab1966(XYZ), K_ab, decimal=7 + ) @ignore_numpy_errors def test_nan_XYZ_to_K_ab_HunterLab1966(self): """ - Tests :func:`colour.models.hunter_lab.XYZ_to_K_ab_HunterLab1966` + Test :func:`colour.models.hunter_lab.XYZ_to_K_ab_HunterLab1966` definition nan support. """ @@ -92,66 +99,78 @@ def test_nan_XYZ_to_K_ab_HunterLab1966(self): class TestXYZ_to_Hunter_Lab(unittest.TestCase): """ - Defines :func:`colour.models.hunter_lab.XYZ_to_Hunter_Lab` definition unit + Define :func:`colour.models.hunter_lab.XYZ_to_Hunter_Lab` definition unit tests methods. """ def test_XYZ_to_Hunter_Lab(self): - """ - Tests :func:`colour.models.hunter_lab.XYZ_to_Hunter_Lab` definition. - """ + """Test :func:`colour.models.hunter_lab.XYZ_to_Hunter_Lab` definition.""" np.testing.assert_almost_equal( XYZ_to_Hunter_Lab( - np.array([0.20654008, 0.12197225, 0.05136952]) * 100), + np.array([0.20654008, 0.12197225, 0.05136952]) * 100 + ), np.array([34.92452577, 47.06189858, 14.38615107]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_Hunter_Lab( - np.array([0.14222010, 0.23042768, 0.10495772]) * 100), + np.array([0.14222010, 0.23042768, 0.10495772]) * 100 + ), np.array([48.00288325, -28.98551622, 18.75564181]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_Hunter_Lab( - np.array([0.07818780, 0.06157201, 0.28099326]) * 100), + np.array([0.07818780, 0.06157201, 0.28099326]) * 100 + ), np.array([24.81370791, 14.38300039, -53.25539126]), - decimal=7) + decimal=7, + ) - h_i = TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] - A = h_i['A'] + h_i = TVS_ILLUMINANTS_HUNTERLAB["CIE 1931 2 Degree Standard Observer"] + A = h_i["A"] np.testing.assert_almost_equal( XYZ_to_Hunter_Lab( - np.array([0.20654008, 0.12197225, 0.05136952]) * 100, A.XYZ_n, - A.K_ab), + np.array([0.20654008, 0.12197225, 0.05136952]) * 100, + A.XYZ_n, + A.K_ab, + ), np.array([34.92452577, 35.04243086, -2.47688619]), - decimal=7) + decimal=7, + ) - D65 = h_i['D65'] + D65 = h_i["D65"] np.testing.assert_almost_equal( XYZ_to_Hunter_Lab( np.array([0.20654008, 0.12197225, 0.05136952]) * 100, - D65.XYZ_n, D65.K_ab), + D65.XYZ_n, + D65.K_ab, + ), np.array([34.92452577, 47.06189858, 14.38615107]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_Hunter_Lab( np.array([0.20654008, 0.12197225, 0.05136952]) * 100, D65.XYZ_n, - K_ab=None), + K_ab=None, + ), np.array([34.92452577, 47.05669614, 14.38385238]), - decimal=7) + decimal=7, + ) def test_n_dimensional_XYZ_to_Hunter_Lab(self): """ - Tests :func:`colour.models.hunter_lab.XYZ_to_Hunter_Lab` definition + Test :func:`colour.models.hunter_lab.XYZ_to_Hunter_Lab` definition n-dimensional support. """ - h_i = TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] - D65 = h_i['D65'] + h_i = TVS_ILLUMINANTS_HUNTERLAB["CIE 1931 2 Degree Standard Observer"] + D65 = h_i["D65"] XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) * 100 XYZ_n = D65.XYZ_n @@ -161,46 +180,50 @@ def test_n_dimensional_XYZ_to_Hunter_Lab(self): XYZ = np.tile(XYZ, (6, 1)) Lab = np.tile(Lab, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_Hunter_Lab(XYZ, XYZ_n, K_ab), Lab, decimal=7) + XYZ_to_Hunter_Lab(XYZ, XYZ_n, K_ab), Lab, decimal=7 + ) XYZ_n = np.tile(XYZ_n, (6, 1)) K_ab = np.tile(K_ab, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_Hunter_Lab(XYZ, XYZ_n, K_ab), Lab, decimal=7) + XYZ_to_Hunter_Lab(XYZ, XYZ_n, K_ab), Lab, decimal=7 + ) XYZ = np.reshape(XYZ, (2, 3, 3)) XYZ_n = np.reshape(XYZ_n, (2, 3, 3)) K_ab = np.reshape(K_ab, (2, 3, 2)) Lab = np.reshape(Lab, (2, 3, 3)) np.testing.assert_almost_equal( - XYZ_to_Hunter_Lab(XYZ, XYZ_n, K_ab), Lab, decimal=7) + XYZ_to_Hunter_Lab(XYZ, XYZ_n, K_ab), Lab, decimal=7 + ) def test_domain_range_scale_XYZ_to_Hunter_Lab(self): """ - Tests :func:`colour.models.hunter_lab.XYZ_to_Hunter_Lab` definition + Test :func:`colour.models.hunter_lab.XYZ_to_Hunter_Lab` definition domain and range scale support. """ - h_i = TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] - D65 = h_i['D65'] + h_i = TVS_ILLUMINANTS_HUNTERLAB["CIE 1931 2 Degree Standard Observer"] + D65 = h_i["D65"] XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) * 100 XYZ_n = D65.XYZ_n K_ab = D65.K_ab Lab = XYZ_to_Hunter_Lab(XYZ, XYZ_n, K_ab) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( XYZ_to_Hunter_Lab(XYZ * factor, XYZ_n * factor, K_ab), Lab * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_XYZ_to_Hunter_Lab(self): """ - Tests :func:`colour.models.hunter_lab.XYZ_to_Hunter_Lab` definition nan + Test :func:`colour.models.hunter_lab.XYZ_to_Hunter_Lab` definition nan support. """ @@ -215,66 +238,78 @@ def test_nan_XYZ_to_Hunter_Lab(self): class TestHunter_Lab_to_XYZ(unittest.TestCase): """ - Defines :func:`colour.models.hunter_lab.Hunter_Lab_to_XYZ` definition unit + Define :func:`colour.models.hunter_lab.Hunter_Lab_to_XYZ` definition unit tests methods. """ def test_Hunter_Lab_to_XYZ(self): - """ - Tests :func:`colour.models.hunter_lab.Hunter_Lab_to_XYZ` definition. - """ + """Test :func:`colour.models.hunter_lab.Hunter_Lab_to_XYZ` definition.""" np.testing.assert_almost_equal( Hunter_Lab_to_XYZ( - np.array([34.92452577, 47.06189858, 14.38615107])), + np.array([34.92452577, 47.06189858, 14.38615107]) + ), np.array([20.65400800, 12.19722500, 5.13695200]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Hunter_Lab_to_XYZ( - np.array([48.00288325, -28.98551622, 18.75564181])), + np.array([48.00288325, -28.98551622, 18.75564181]) + ), np.array([14.22201000, 23.04276800, 10.49577200]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Hunter_Lab_to_XYZ( - np.array([24.81370791, 14.38300039, -53.25539126])), + np.array([24.81370791, 14.38300039, -53.25539126]) + ), np.array([7.81878000, 6.15720100, 28.09932601]), - decimal=7) + decimal=7, + ) - h_i = TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] - A = h_i['A'] + h_i = TVS_ILLUMINANTS_HUNTERLAB["CIE 1931 2 Degree Standard Observer"] + A = h_i["A"] np.testing.assert_almost_equal( Hunter_Lab_to_XYZ( - np.array([34.92452577, 35.04243086, -2.47688619]), A.XYZ_n, - A.K_ab), + np.array([34.92452577, 35.04243086, -2.47688619]), + A.XYZ_n, + A.K_ab, + ), np.array([20.65400800, 12.19722500, 5.13695200]), - decimal=7) + decimal=7, + ) - D65 = h_i['D65'] + D65 = h_i["D65"] np.testing.assert_almost_equal( Hunter_Lab_to_XYZ( - np.array([34.92452577, 47.06189858, 14.38615107]), D65.XYZ_n, - D65.K_ab), + np.array([34.92452577, 47.06189858, 14.38615107]), + D65.XYZ_n, + D65.K_ab, + ), np.array([20.65400800, 12.19722500, 5.13695200]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Hunter_Lab_to_XYZ( np.array([34.92452577, 47.05669614, 14.38385238]), D65.XYZ_n, - K_ab=None), + K_ab=None, + ), np.array([20.65400800, 12.19722500, 5.13695200]), - decimal=7) + decimal=7, + ) def test_n_dimensional_Hunter_Lab_to_XYZ(self): """ - Tests :func:`colour.models.hunter_lab.Hunter_Lab_to_XYZ` definition + Test :func:`colour.models.hunter_lab.Hunter_Lab_to_XYZ` definition n-dimensional support. """ - h_i = TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] - D65 = h_i['D65'] + h_i = TVS_ILLUMINANTS_HUNTERLAB["CIE 1931 2 Degree Standard Observer"] + D65 = h_i["D65"] Lab = np.array([34.92452577, 47.06189858, 14.38615107]) XYZ_n = D65.XYZ_n @@ -284,46 +319,50 @@ def test_n_dimensional_Hunter_Lab_to_XYZ(self): Lab = np.tile(Lab, (6, 1)) XYZ = np.tile(XYZ, (6, 1)) np.testing.assert_almost_equal( - Hunter_Lab_to_XYZ(Lab, XYZ_n, K_ab), XYZ, decimal=7) + Hunter_Lab_to_XYZ(Lab, XYZ_n, K_ab), XYZ, decimal=7 + ) K_ab = np.tile(K_ab, (6, 1)) XYZ_n = np.tile(XYZ_n, (6, 1)) np.testing.assert_almost_equal( - Hunter_Lab_to_XYZ(Lab, XYZ_n, K_ab), XYZ, decimal=7) + Hunter_Lab_to_XYZ(Lab, XYZ_n, K_ab), XYZ, decimal=7 + ) Lab = np.reshape(Lab, (2, 3, 3)) XYZ_n = np.reshape(XYZ_n, (2, 3, 3)) K_ab = np.reshape(K_ab, (2, 3, 2)) XYZ = np.reshape(XYZ, (2, 3, 3)) np.testing.assert_almost_equal( - Hunter_Lab_to_XYZ(Lab, XYZ_n, K_ab), XYZ, decimal=7) + Hunter_Lab_to_XYZ(Lab, XYZ_n, K_ab), XYZ, decimal=7 + ) def test_domain_range_scale_Hunter_Lab_to_XYZ(self): """ - Tests :func:`colour.models.hunter_lab.Hunter_Lab_to_XYZ` definition + Test :func:`colour.models.hunter_lab.Hunter_Lab_to_XYZ` definition domain and range scale support. """ - h_i = TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] - D65 = h_i['D65'] + h_i = TVS_ILLUMINANTS_HUNTERLAB["CIE 1931 2 Degree Standard Observer"] + D65 = h_i["D65"] Lab = np.array([34.92452577, 47.06189858, 14.38615107]) XYZ_n = D65.XYZ_n K_ab = D65.K_ab XYZ = Hunter_Lab_to_XYZ(Lab, XYZ_n, K_ab) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( Hunter_Lab_to_XYZ(Lab * factor, XYZ_n * factor, K_ab), XYZ * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_Hunter_Lab_to_XYZ(self): """ - Tests :func:`colour.models.hunter_lab.Hunter_Lab_to_XYZ` definition + Test :func:`colour.models.hunter_lab.Hunter_Lab_to_XYZ` definition nan support. """ @@ -336,5 +375,5 @@ def test_nan_Hunter_Lab_to_XYZ(self): Hunter_Lab_to_XYZ(Lab, XYZ_n, K_ab) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/tests/test_hunter_rdab.py b/colour/models/tests/test_hunter_rdab.py index 3394ccda04..4d595ca576 100644 --- a/colour/models/tests/test_hunter_rdab.py +++ b/colour/models/tests/test_hunter_rdab.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.hunter_rdab` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.hunter_rdab` module.""" import numpy as np import unittest @@ -14,78 +9,93 @@ from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestXYZ_to_Hunter_Rdab', 'TestHunter_Rdab_to_XYZ'] +__all__ = [ + "TestXYZ_to_Hunter_Rdab", + "TestHunter_Rdab_to_XYZ", +] class TestXYZ_to_Hunter_Rdab(unittest.TestCase): """ - Defines :func:`colour.models.hunter_rdab.XYZ_to_Hunter_Rdab` definition + Define :func:`colour.models.hunter_rdab.XYZ_to_Hunter_Rdab` definition unit tests methods. """ def test_XYZ_to_Hunter_Rdab(self): - """ - Tests :func:`colour.models.hunter_rdab.XYZ_to_Hunter_Rdab` definition. - """ + """Test :func:`colour.models.hunter_rdab.XYZ_to_Hunter_Rdab` definition.""" np.testing.assert_almost_equal( XYZ_to_Hunter_Rdab( - np.array([0.20654008, 0.12197225, 0.05136952]) * 100), + np.array([0.20654008, 0.12197225, 0.05136952]) * 100 + ), np.array([12.19722500, 57.12537874, 17.46241341]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_Hunter_Rdab( - np.array([0.14222010, 0.23042768, 0.10495772]) * 100), + np.array([0.14222010, 0.23042768, 0.10495772]) * 100 + ), np.array([23.04276800, -32.40057474, 20.96542183]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_Hunter_Rdab( - np.array([0.07818780, 0.06157201, 0.28099326]) * 100), + np.array([0.07818780, 0.06157201, 0.28099326]) * 100 + ), np.array([6.15720100, 18.13400284, -67.14408607]), - decimal=7) + decimal=7, + ) - h_i = TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] - A = h_i['A'] + h_i = TVS_ILLUMINANTS_HUNTERLAB["CIE 1931 2 Degree Standard Observer"] + A = h_i["A"] np.testing.assert_almost_equal( XYZ_to_Hunter_Rdab( - np.array([0.20654008, 0.12197225, 0.05136952]) * 100, A.XYZ_n, - A.K_ab), + np.array([0.20654008, 0.12197225, 0.05136952]) * 100, + A.XYZ_n, + A.K_ab, + ), np.array([12.19722500, 42.53572838, -3.00653110]), - decimal=7) + decimal=7, + ) - D65 = h_i['D65'] + D65 = h_i["D65"] np.testing.assert_almost_equal( XYZ_to_Hunter_Rdab( np.array([0.20654008, 0.12197225, 0.05136952]) * 100, - D65.XYZ_n, D65.K_ab), + D65.XYZ_n, + D65.K_ab, + ), np.array([12.19722500, 57.12537874, 17.46241341]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_Hunter_Rdab( np.array([0.20654008, 0.12197225, 0.05136952]) * 100, D65.XYZ_n, - K_ab=None), + K_ab=None, + ), np.array([12.19722500, 57.11906384, 17.45962317]), - decimal=7) + decimal=7, + ) def test_n_dimensional_XYZ_to_Hunter_Rdab(self): """ - Tests :func:`colour.models.hunter_rdab.XYZ_to_Hunter_Rdab` definition + Test :func:`colour.models.hunter_rdab.XYZ_to_Hunter_Rdab` definition n-dimensional support. """ - h_i = TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] - D65 = h_i['D65'] + h_i = TVS_ILLUMINANTS_HUNTERLAB["CIE 1931 2 Degree Standard Observer"] + D65 = h_i["D65"] XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) * 100 XYZ_n = D65.XYZ_n @@ -95,46 +105,50 @@ def test_n_dimensional_XYZ_to_Hunter_Rdab(self): XYZ = np.tile(XYZ, (6, 1)) R_d_ab = np.tile(R_d_ab, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_Hunter_Rdab(XYZ, XYZ_n, K_ab), R_d_ab, decimal=7) + XYZ_to_Hunter_Rdab(XYZ, XYZ_n, K_ab), R_d_ab, decimal=7 + ) XYZ_n = np.tile(XYZ_n, (6, 1)) K_ab = np.tile(K_ab, (6, 1)) np.testing.assert_almost_equal( - XYZ_to_Hunter_Rdab(XYZ, XYZ_n, K_ab), R_d_ab, decimal=7) + XYZ_to_Hunter_Rdab(XYZ, XYZ_n, K_ab), R_d_ab, decimal=7 + ) XYZ = np.reshape(XYZ, (2, 3, 3)) XYZ_n = np.reshape(XYZ_n, (2, 3, 3)) K_ab = np.reshape(K_ab, (2, 3, 2)) R_d_ab = np.reshape(R_d_ab, (2, 3, 3)) np.testing.assert_almost_equal( - XYZ_to_Hunter_Rdab(XYZ, XYZ_n, K_ab), R_d_ab, decimal=7) + XYZ_to_Hunter_Rdab(XYZ, XYZ_n, K_ab), R_d_ab, decimal=7 + ) def test_domain_range_scale_XYZ_to_Hunter_Rdab(self): """ - Tests :func:`colour.models.hunter_lab.XYZ_to_Hunter_Rdab` definition + Test :func:`colour.models.hunter_lab.XYZ_to_Hunter_Rdab` definition domain and range scale support. """ - h_i = TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] - D65 = h_i['D65'] + h_i = TVS_ILLUMINANTS_HUNTERLAB["CIE 1931 2 Degree Standard Observer"] + D65 = h_i["D65"] XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) * 100 XYZ_n = D65.XYZ_n K_ab = D65.K_ab R_d_ab = XYZ_to_Hunter_Rdab(XYZ, XYZ_n, K_ab) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( XYZ_to_Hunter_Rdab(XYZ * factor, XYZ_n * factor, K_ab), R_d_ab * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_XYZ_to_Hunter_Rdab(self): """ - Tests :func:`colour.models.hunter_rdab.XYZ_to_Hunter_Rdab` definition + Test :func:`colour.models.hunter_rdab.XYZ_to_Hunter_Rdab` definition nan support. """ @@ -149,66 +163,78 @@ def test_nan_XYZ_to_Hunter_Rdab(self): class TestHunter_Rdab_to_XYZ(unittest.TestCase): """ - Defines :func:`colour.models.hunter_rdab.Hunter_Rdab_to_XYZ` definition + Define :func:`colour.models.hunter_rdab.Hunter_Rdab_to_XYZ` definition unit tests methods. """ def test_Hunter_Rdab_to_XYZ(self): - """ - Tests :func:`colour.models.hunter_rdab.Hunter_Rdab_to_XYZ` definition. - """ + """Test :func:`colour.models.hunter_rdab.Hunter_Rdab_to_XYZ` definition.""" np.testing.assert_almost_equal( Hunter_Rdab_to_XYZ( - np.array([12.19722500, 57.12537874, 17.46241341])), + np.array([12.19722500, 57.12537874, 17.46241341]) + ), np.array([0.20654008, 0.12197225, 0.05136952]) * 100, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Hunter_Rdab_to_XYZ( - np.array([23.04276800, -32.40057474, 20.96542183])), + np.array([23.04276800, -32.40057474, 20.96542183]) + ), np.array([0.14222010, 0.23042768, 0.10495772]) * 100, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Hunter_Rdab_to_XYZ( - np.array([6.15720100, 18.13400284, -67.14408607])), + np.array([6.15720100, 18.13400284, -67.14408607]) + ), np.array([0.07818780, 0.06157201, 0.28099326]) * 100, - decimal=7) + decimal=7, + ) - h_i = TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] - A = h_i['A'] + h_i = TVS_ILLUMINANTS_HUNTERLAB["CIE 1931 2 Degree Standard Observer"] + A = h_i["A"] np.testing.assert_almost_equal( Hunter_Rdab_to_XYZ( - np.array([12.19722500, 42.53572838, -3.00653110]), A.XYZ_n, - A.K_ab), + np.array([12.19722500, 42.53572838, -3.00653110]), + A.XYZ_n, + A.K_ab, + ), np.array([0.20654008, 0.12197225, 0.05136952]) * 100, - decimal=7) + decimal=7, + ) - D65 = h_i['D65'] + D65 = h_i["D65"] np.testing.assert_almost_equal( Hunter_Rdab_to_XYZ( - np.array([12.19722500, 57.12537874, 17.46241341]), D65.XYZ_n, - D65.K_ab), + np.array([12.19722500, 57.12537874, 17.46241341]), + D65.XYZ_n, + D65.K_ab, + ), np.array([0.20654008, 0.12197225, 0.05136952]) * 100, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( Hunter_Rdab_to_XYZ( np.array([12.19722500, 57.11906384, 17.45962317]), D65.XYZ_n, - K_ab=None), + K_ab=None, + ), np.array([0.20654008, 0.12197225, 0.05136952]) * 100, - decimal=7) + decimal=7, + ) def test_n_dimensional_Hunter_Rdab_to_XYZ(self): """ - Tests :func:`colour.models.hunter_rdab.Hunter_Rdab_to_XYZ` definition + Test :func:`colour.models.hunter_rdab.Hunter_Rdab_to_XYZ` definition n-dimensional support. """ - h_i = TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] - D65 = h_i['D65'] + h_i = TVS_ILLUMINANTS_HUNTERLAB["CIE 1931 2 Degree Standard Observer"] + D65 = h_i["D65"] R_d_ab = np.array([12.19722500, 57.12537874, 17.46241341]) XYZ_n = D65.XYZ_n @@ -218,46 +244,50 @@ def test_n_dimensional_Hunter_Rdab_to_XYZ(self): R_d_ab = np.tile(R_d_ab, (6, 1)) XYZ = np.tile(XYZ, (6, 1)) np.testing.assert_almost_equal( - Hunter_Rdab_to_XYZ(R_d_ab, XYZ_n, K_ab), XYZ, decimal=7) + Hunter_Rdab_to_XYZ(R_d_ab, XYZ_n, K_ab), XYZ, decimal=7 + ) K_ab = np.tile(K_ab, (6, 1)) XYZ_n = np.tile(XYZ_n, (6, 1)) np.testing.assert_almost_equal( - Hunter_Rdab_to_XYZ(R_d_ab, XYZ_n, K_ab), XYZ, decimal=7) + Hunter_Rdab_to_XYZ(R_d_ab, XYZ_n, K_ab), XYZ, decimal=7 + ) R_d_ab = np.reshape(R_d_ab, (2, 3, 3)) XYZ_n = np.reshape(XYZ_n, (2, 3, 3)) K_ab = np.reshape(K_ab, (2, 3, 2)) XYZ = np.reshape(XYZ, (2, 3, 3)) np.testing.assert_almost_equal( - Hunter_Rdab_to_XYZ(R_d_ab, XYZ_n, K_ab), XYZ, decimal=7) + Hunter_Rdab_to_XYZ(R_d_ab, XYZ_n, K_ab), XYZ, decimal=7 + ) def test_domain_range_scale_Hunter_Rdab_to_XYZ(self): """ - Tests :func:`colour.models.hunter_lab.Hunter_Rdab_to_XYZ` definition + Test :func:`colour.models.hunter_lab.Hunter_Rdab_to_XYZ` definition domain and range scale support. """ - h_i = TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] - D65 = h_i['D65'] + h_i = TVS_ILLUMINANTS_HUNTERLAB["CIE 1931 2 Degree Standard Observer"] + D65 = h_i["D65"] R_d_ab = np.array([12.19722500, 57.12537874, 17.46241341]) XYZ_n = D65.XYZ_n K_ab = D65.K_ab XYZ = Hunter_Rdab_to_XYZ(R_d_ab, XYZ_n, K_ab) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( Hunter_Rdab_to_XYZ(R_d_ab * factor, XYZ_n * factor, K_ab), XYZ * factor, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_Hunter_Rdab_to_XYZ(self): """ - Tests :func:`colour.models.hunter_rdab.Hunter_Rdab_to_XYZ` definition + Test :func:`colour.models.hunter_rdab.Hunter_Rdab_to_XYZ` definition nan support. """ @@ -270,5 +300,5 @@ def test_nan_Hunter_Rdab_to_XYZ(self): Hunter_Rdab_to_XYZ(R_d_ab, XYZ_n, K_ab) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/tests/test_icacb.py b/colour/models/tests/test_icacb.py new file mode 100644 index 0000000000..88e18b2c06 --- /dev/null +++ b/colour/models/tests/test_icacb.py @@ -0,0 +1,177 @@ +"""Defines the unit tests for the :mod:`colour.models.hunter_rdab` module.""" + +from itertools import permutations +import numpy as np +import unittest + +from colour.models import XYZ_to_ICaCb, ICaCb_to_XYZ +from colour.utilities import ignore_numpy_errors, domain_range_scale + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestXYZ_to_ICaCb", + "TestICaCb_to_XYZ", +] + + +class TestXYZ_to_ICaCb(unittest.TestCase): + """ + Define :func:`colour.models.icacb.XYZ_to_ICaCb` definition unit tests + methods. + """ + + def test_XYZ_to_ICaCb(self): + """Test :func:`colour.models.icacb.XYZ_to_ICaCb` definition.""" + + np.testing.assert_almost_equal( + XYZ_to_ICaCb(np.array([0.20654008, 0.12197225, 0.05136952])), + np.array([0.06875297, 0.05753352, 0.02081548]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_ICaCb(np.array([0.14222010, 0.23042768, 0.10495772])), + np.array([0.08666353, -0.02479011, 0.03099396]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_ICaCb(np.array([0.07818780, 0.06157201, 0.28099326])), + np.array([0.05102472, -0.00965461, -0.05150706]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_ICaCb(np.array([0.00000000, 0.00000000, 1.00000000])), + np.array([1702.0656419, 14738.00583456, 1239.66837927]), + decimal=7, + ) + + def test_n_dimensional_XYZ_to_ICaCb(self): + """ + Test :func:`colour.models.icacb.XYZ_to_ICaCb` definition + n-dimensional support. + """ + + XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + ICaCb = XYZ_to_ICaCb(XYZ) + + XYZ = np.tile(XYZ, (6, 1)) + ICaCb = np.tile(ICaCb, (6, 1)) + np.testing.assert_almost_equal(XYZ_to_ICaCb(XYZ), ICaCb, decimal=7) + + XYZ = np.reshape(XYZ, (2, 3, 3)) + ICaCb = np.reshape(ICaCb, (2, 3, 3)) + np.testing.assert_almost_equal(XYZ_to_ICaCb(XYZ), ICaCb, decimal=7) + + def test_domain_range_scale_XYZ_to_ICaCb(self): + """ + Test :func:`colour.models.icacb.XYZ_to_ICaCb` definition domain and + range scale support. + """ + + XYZ = np.array([0.07818780, 0.06157201, 0.28099326]) + ICaCb = XYZ_to_ICaCb(XYZ) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + XYZ_to_ICaCb(XYZ * factor), ICaCb * factor, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_XYZ_to_ICaCb(self): + """Test :func:`colour.models.cie_lab.XYZ_to_Lab` definition nan support.""" + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + XYZ = np.array(case) + XYZ_to_ICaCb(XYZ) + + +class TestICaCb_to_XYZ(unittest.TestCase): + """Test :func:`colour.models.icacb.ICaCb_to_XYZ` definition.""" + + def test_XYZ_to_ICaCb(self): + """Test :func:`colour.models.icacb.ICaCb_to_XYZ` definition.""" + + np.testing.assert_almost_equal( + ICaCb_to_XYZ(np.array([0.06875297, 0.05753352, 0.02081548])), + np.array([0.20654008, 0.12197225, 0.05136952]), + decimal=7, + ) + + np.testing.assert_almost_equal( + ICaCb_to_XYZ(np.array([0.08666353, -0.02479011, 0.03099396])), + np.array([0.14222010, 0.23042768, 0.10495772]), + decimal=7, + ) + + np.testing.assert_almost_equal( + ICaCb_to_XYZ(np.array([0.05102472, -0.00965461, -0.05150706])), + np.array([0.07818780, 0.06157201, 0.28099326]), + decimal=7, + ) + + np.testing.assert_almost_equal( + ICaCb_to_XYZ( + np.array([1702.0656419, 14738.00583456, 1239.66837927]) + ), + np.array([0.00000000, 0.00000000, 1.00000000]), + decimal=7, + ) + + def test_n_dimensional_ICaCb_to_XYZ(self): + """ + Test :func:`colour.models.icacb.ICaCb_to_XYZ` definition + n-dimensional support. + """ + + ICaCb = np.array([0.06875297, 0.05753352, 0.02081548]) + XYZ = ICaCb_to_XYZ(ICaCb) + + ICaCb = np.tile(ICaCb, (6, 1)) + XYZ = np.tile(XYZ, (6, 1)) + np.testing.assert_almost_equal(ICaCb_to_XYZ(ICaCb), XYZ, decimal=7) + + ICaCb = np.reshape(ICaCb, (2, 3, 3)) + XYZ = np.reshape(XYZ, (2, 3, 3)) + np.testing.assert_almost_equal(ICaCb_to_XYZ(ICaCb), XYZ, decimal=7) + + def test_domain_range_scale_ICaCb_to_XYZ(self): + """ + Test :func:`colour.models.icacb.ICaCb_to_XYZ` definition domain and + range scale support. + """ + + ICaCb = np.array([0.06875297, 0.05753352, 0.02081548]) + XYZ = ICaCb_to_XYZ(ICaCb) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + ICaCb_to_XYZ(ICaCb * factor), XYZ * factor, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_ICaCb_to_XYZ(self): + """Test :func:`colour.models.cie_lab.XYZ_to_Lab` definition nan support.""" + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + ICaCb = np.array(case) + ICaCb_to_XYZ(ICaCb) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/models/tests/test_igpgtg.py b/colour/models/tests/test_igpgtg.py index 0f62b08a34..2d8b78ed43 100644 --- a/colour/models/tests/test_igpgtg.py +++ b/colour/models/tests/test_igpgtg.py @@ -1,89 +1,89 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.igpgtg` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.igpgtg` module.""" import numpy as np import unittest from itertools import permutations -from colour.models import XYZ_to_IGPGTG, IGPGTG_to_XYZ +from colour.models import XYZ_to_IgPgTg, IgPgTg_to_XYZ from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestXYZ_to_IGPGTG', 'TestIGPGTG_to_XYZ'] +__all__ = [ + "TestXYZ_to_IgPgTg", + "TestIgPgTg_to_XYZ", +] -class TestXYZ_to_IGPGTG(unittest.TestCase): +class TestXYZ_to_IgPgTg(unittest.TestCase): """ - Defines :func:`colour.models.igpgtg.XYZ_to_IGPGTG` definition unit tests + Define :func:`colour.models.igpgtg.XYZ_to_IgPgTg` definition unit tests methods. """ - def test_XYZ_to_IGPGTG(self): - """ - Tests :func:`colour.models.igpgtg.XYZ_to_IGPGTG` definition. - """ + def test_XYZ_to_IgPgTg(self): + """Test :func:`colour.models.igpgtg.XYZ_to_IgPgTg` definition.""" np.testing.assert_almost_equal( - XYZ_to_IGPGTG(np.array([0.20654008, 0.12197225, 0.05136952])), + XYZ_to_IgPgTg(np.array([0.20654008, 0.12197225, 0.05136952])), np.array([0.42421258, 0.18632491, 0.10689223]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - XYZ_to_IGPGTG(np.array([0.14222010, 0.23042768, 0.10495772])), + XYZ_to_IgPgTg(np.array([0.14222010, 0.23042768, 0.10495772])), np.array([0.50912820, -0.14804331, 0.11921472]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - XYZ_to_IGPGTG(np.array([0.07818780, 0.06157201, 0.28099326])), + XYZ_to_IgPgTg(np.array([0.07818780, 0.06157201, 0.28099326])), np.array([0.29095152, -0.04057508, -0.18220795]), - decimal=7) + decimal=7, + ) - def test_n_dimensional_XYZ_to_IGPGTG(self): + def test_n_dimensional_XYZ_to_IgPgTg(self): """ - Tests :func:`colour.models.igpgtg.XYZ_to_IGPGTG` definition + Test :func:`colour.models.igpgtg.XYZ_to_IgPgTg` definition n-dimensional support. """ XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) - IGPGTG = XYZ_to_IGPGTG(XYZ) + IgPgTg = XYZ_to_IgPgTg(XYZ) XYZ = np.tile(XYZ, (6, 1)) - IGPGTG = np.tile(IGPGTG, (6, 1)) - np.testing.assert_almost_equal(XYZ_to_IGPGTG(XYZ), IGPGTG, decimal=7) + IgPgTg = np.tile(IgPgTg, (6, 1)) + np.testing.assert_almost_equal(XYZ_to_IgPgTg(XYZ), IgPgTg, decimal=7) XYZ = np.reshape(XYZ, (2, 3, 3)) - IGPGTG = np.reshape(IGPGTG, (2, 3, 3)) - np.testing.assert_almost_equal(XYZ_to_IGPGTG(XYZ), IGPGTG, decimal=7) + IgPgTg = np.reshape(IgPgTg, (2, 3, 3)) + np.testing.assert_almost_equal(XYZ_to_IgPgTg(XYZ), IgPgTg, decimal=7) - def test_domain_range_scale_XYZ_to_IGPGTG(self): + def test_domain_range_scale_XYZ_to_IgPgTg(self): """ - Tests :func:`colour.models.igpgtg.XYZ_to_IGPGTG` definition domain and + Test :func:`colour.models.igpgtg.XYZ_to_IgPgTg` definition domain and range scale support. """ XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) - IGPGTG = XYZ_to_IGPGTG(XYZ) + IgPgTg = XYZ_to_IgPgTg(XYZ) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - XYZ_to_IGPGTG(XYZ * factor), IGPGTG * factor, decimal=7) + XYZ_to_IgPgTg(XYZ * factor), IgPgTg * factor, decimal=7 + ) @ignore_numpy_errors - def test_nan_XYZ_to_IGPGTG(self): + def test_nan_XYZ_to_IgPgTg(self): """ - Tests :func:`colour.models.igpgtg.XYZ_to_IGPGTG` definition nan + Test :func:`colour.models.igpgtg.XYZ_to_IgPgTg` definition nan support. """ @@ -91,80 +91,82 @@ def test_nan_XYZ_to_IGPGTG(self): cases = set(permutations(cases * 3, r=3)) for case in cases: XYZ = np.array(case) - XYZ_to_IGPGTG(XYZ) + XYZ_to_IgPgTg(XYZ) -class TestIGPGTG_to_XYZ(unittest.TestCase): +class TestIgPgTg_to_XYZ(unittest.TestCase): """ - Defines :func:`colour.models.igpgtg.IGPGTG_to_XYZ` definition unit tests + Define :func:`colour.models.igpgtg.IgPgTg_to_XYZ` definition unit tests methods. """ - def test_IGPGTG_to_XYZ(self): - """ - Tests :func:`colour.models.igpgtg.IGPGTG_to_XYZ` definition. - """ + def test_IgPgTg_to_XYZ(self): + """Test :func:`colour.models.igpgtg.IgPgTg_to_XYZ` definition.""" np.testing.assert_almost_equal( - IGPGTG_to_XYZ(np.array([0.42421258, 0.18632491, 0.10689223])), + IgPgTg_to_XYZ(np.array([0.42421258, 0.18632491, 0.10689223])), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - IGPGTG_to_XYZ(np.array([0.50912820, -0.14804331, 0.11921472])), + IgPgTg_to_XYZ(np.array([0.50912820, -0.14804331, 0.11921472])), np.array([0.14222010, 0.23042768, 0.10495772]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - IGPGTG_to_XYZ(np.array([0.29095152, -0.04057508, -0.18220795])), + IgPgTg_to_XYZ(np.array([0.29095152, -0.04057508, -0.18220795])), np.array([0.07818780, 0.06157201, 0.28099326]), - decimal=7) + decimal=7, + ) - def test_n_dimensional_IGPGTG_to_XYZ(self): + def test_n_dimensional_IgPgTg_to_XYZ(self): """ - Tests :func:`colour.models.igpgtg.IGPGTG_to_XYZ` definition + Test :func:`colour.models.igpgtg.IgPgTg_to_XYZ` definition n-dimensional support. """ - IGPGTG = np.array([0.42421258, 0.18632491, 0.10689223]) - XYZ = IGPGTG_to_XYZ(IGPGTG) + IgPgTg = np.array([0.42421258, 0.18632491, 0.10689223]) + XYZ = IgPgTg_to_XYZ(IgPgTg) - IGPGTG = np.tile(IGPGTG, (6, 1)) + IgPgTg = np.tile(IgPgTg, (6, 1)) XYZ = np.tile(XYZ, (6, 1)) - np.testing.assert_almost_equal(IGPGTG_to_XYZ(IGPGTG), XYZ, decimal=7) + np.testing.assert_almost_equal(IgPgTg_to_XYZ(IgPgTg), XYZ, decimal=7) - IGPGTG = np.reshape(IGPGTG, (2, 3, 3)) + IgPgTg = np.reshape(IgPgTg, (2, 3, 3)) XYZ = np.reshape(XYZ, (2, 3, 3)) - np.testing.assert_almost_equal(IGPGTG_to_XYZ(IGPGTG), XYZ, decimal=7) + np.testing.assert_almost_equal(IgPgTg_to_XYZ(IgPgTg), XYZ, decimal=7) - def test_domain_range_scale_IGPGTG_to_XYZ(self): + def test_domain_range_scale_IgPgTg_to_XYZ(self): """ - Tests :func:`colour.models.igpgtg.IGPGTG_to_XYZ` definition domain and + Test :func:`colour.models.igpgtg.IgPgTg_to_XYZ` definition domain and range scale support. """ - IGPGTG = np.array([0.42421258, 0.18632491, 0.10689223]) - XYZ = IGPGTG_to_XYZ(IGPGTG) + IgPgTg = np.array([0.42421258, 0.18632491, 0.10689223]) + XYZ = IgPgTg_to_XYZ(IgPgTg) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - IGPGTG_to_XYZ(IGPGTG * factor), XYZ * factor, decimal=7) + IgPgTg_to_XYZ(IgPgTg * factor), XYZ * factor, decimal=7 + ) @ignore_numpy_errors - def test_nan_IGPGTG_to_XYZ(self): + def test_nan_IgPgTg_to_XYZ(self): """ - Tests :func:`colour.models.igpgtg.IGPGTG_to_XYZ` definition nan + Test :func:`colour.models.igpgtg.IgPgTg_to_XYZ` definition nan support. """ cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) for case in cases: - IGPGTG = np.array(case) - IGPGTG_to_XYZ(IGPGTG) + IgPgTg = np.array(case) + IgPgTg_to_XYZ(IgPgTg) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/tests/test_ipt.py b/colour/models/tests/test_ipt.py index 0eb9685b25..f8c7959620 100644 --- a/colour/models/tests/test_ipt.py +++ b/colour/models/tests/test_ipt.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.ipt` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.ipt` module.""" import numpy as np import unittest @@ -12,44 +7,47 @@ from colour.models import XYZ_to_IPT, IPT_to_XYZ, IPT_hue_angle from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestXYZ_to_IPT', 'TestIPT_to_XYZ', 'TestIPTHueAngle'] +__all__ = [ + "TestXYZ_to_IPT", + "TestIPT_to_XYZ", + "TestIPTHueAngle", +] class TestXYZ_to_IPT(unittest.TestCase): - """ - Defines :func:`colour.models.ipt.XYZ_to_IPT` definition unit tests methods. - """ + """Define :func:`colour.models.ipt.XYZ_to_IPT` definition unit tests methods.""" def test_XYZ_to_IPT(self): - """ - Tests :func:`colour.models.ipt.XYZ_to_IPT` definition. - """ + """Test :func:`colour.models.ipt.XYZ_to_IPT` definition.""" np.testing.assert_almost_equal( XYZ_to_IPT(np.array([0.20654008, 0.12197225, 0.05136952])), np.array([0.38426191, 0.38487306, 0.18886838]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_IPT(np.array([0.14222010, 0.23042768, 0.10495772])), np.array([0.49437481, -0.19251742, 0.18080304]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_IPT(np.array([0.07818780, 0.06157201, 0.28099326])), np.array([0.35167774, -0.07525627, -0.30921279]), - decimal=7) + decimal=7, + ) def test_n_dimensional_XYZ_to_IPT(self): """ - Tests :func:`colour.models.ipt.XYZ_to_IPT` definition n-dimensional + Test :func:`colour.models.ipt.XYZ_to_IPT` definition n-dimensional support. """ @@ -66,24 +64,23 @@ def test_n_dimensional_XYZ_to_IPT(self): def test_domain_range_scale_XYZ_to_IPT(self): """ - Tests :func:`colour.models.ipt.XYZ_to_IPT` definition domain and + Test :func:`colour.models.ipt.XYZ_to_IPT` definition domain and range scale support. """ XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) IPT = XYZ_to_IPT(XYZ) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - XYZ_to_IPT(XYZ * factor), IPT * factor, decimal=7) + XYZ_to_IPT(XYZ * factor), IPT * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_XYZ_to_IPT(self): - """ - Tests :func:`colour.models.ipt.XYZ_to_IPT` definition nan support. - """ + """Test :func:`colour.models.ipt.XYZ_to_IPT` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -94,33 +91,34 @@ def test_nan_XYZ_to_IPT(self): class TestIPT_to_XYZ(unittest.TestCase): """ - Defines :func:`colour.models.ipt.IPT_to_XYZ` definition unit tests + Define :func:`colour.models.ipt.IPT_to_XYZ` definition unit tests methods. """ def test_IPT_to_XYZ(self): - """ - Tests :func:`colour.models.ipt.IPT_to_XYZ` definition. - """ + """Test :func:`colour.models.ipt.IPT_to_XYZ` definition.""" np.testing.assert_almost_equal( IPT_to_XYZ(np.array([0.38426191, 0.38487306, 0.18886838])), np.array([0.20654008, 0.12197225, 0.05136952]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( IPT_to_XYZ(np.array([0.49437481, -0.19251742, 0.18080304])), np.array([0.14222010, 0.23042768, 0.10495772]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( IPT_to_XYZ(np.array([0.35167774, -0.07525627, -0.30921279])), np.array([0.07818780, 0.06157201, 0.28099326]), - decimal=7) + decimal=7, + ) def test_n_dimensional_IPT_to_XYZ(self): """ - Tests :func:`colour.models.ipt.IPT_to_XYZ` definition n-dimensional + Test :func:`colour.models.ipt.IPT_to_XYZ` definition n-dimensional support. """ @@ -137,24 +135,23 @@ def test_n_dimensional_IPT_to_XYZ(self): def test_domain_range_scale_IPT_to_XYZ(self): """ - Tests :func:`colour.models.ipt.IPT_to_XYZ` definition domain and + Test :func:`colour.models.ipt.IPT_to_XYZ` definition domain and range scale support. """ IPT = np.array([0.38426191, 0.38487306, 0.18886838]) XYZ = IPT_to_XYZ(IPT) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - IPT_to_XYZ(IPT * factor), XYZ * factor, decimal=7) + IPT_to_XYZ(IPT * factor), XYZ * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_IPT_to_XYZ(self): - """ - Tests :func:`colour.models.ipt.IPT_to_XYZ` definition nan support. - """ + """Test :func:`colour.models.ipt.IPT_to_XYZ` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -165,33 +162,34 @@ def test_nan_IPT_to_XYZ(self): class TestIPTHueAngle(unittest.TestCase): """ - Defines :func:`colour.models.ipt.IPT_hue_angle` definition unit tests + Define :func:`colour.models.ipt.IPT_hue_angle` definition unit tests methods. """ def test_IPT_hue_angle(self): - """ - Tests :func:`colour.models.ipt.IPT_hue_angle` definition. - """ + """Test :func:`colour.models.ipt.IPT_hue_angle` definition.""" np.testing.assert_almost_equal( IPT_hue_angle(np.array([0.20654008, 0.12197225, 0.05136952])), 22.838754548625527, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( IPT_hue_angle(np.array([0.14222010, 0.23042768, 0.10495772])), 24.488834912466245, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( IPT_hue_angle(np.array([0.07818780, 0.06157201, 0.28099326])), 77.640533743711813, - decimal=7) + decimal=7, + ) def test_n_dimensional_IPT_hue_angle(self): """ - Tests :func:`colour.models.ipt.IPT_hue_angle` definition n-dimensional + Test :func:`colour.models.ipt.IPT_hue_angle` definition n-dimensional support. """ @@ -208,24 +206,23 @@ def test_n_dimensional_IPT_hue_angle(self): def test_domain_range_scale_IPT_hue_angle(self): """ - Tests :func:`colour.models.ipt.IPT_hue_angle` definition domain and + Test :func:`colour.models.ipt.IPT_hue_angle` definition domain and range scale support. """ IPT = np.array([0.20654008, 0.12197225, 0.05136952]) hue = IPT_hue_angle(IPT) - d_r = (('reference', 1, 1), (1, 1, 1 / 360), (100, 100, 1 / 3.6)) + d_r = (("reference", 1, 1), ("1", 1, 1 / 360), ("100", 100, 1 / 3.6)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - IPT_hue_angle(IPT * factor_a), hue * factor_b, decimal=7) + IPT_hue_angle(IPT * factor_a), hue * factor_b, decimal=7 + ) @ignore_numpy_errors def test_nan_IPT_hue_angle(self): - """ - Tests :func:`colour.models.ipt.IPT_hue_angle` definition nan support. - """ + """Test :func:`colour.models.ipt.IPT_hue_angle` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) @@ -234,5 +231,5 @@ def test_nan_IPT_hue_angle(self): IPT_hue_angle(IPT) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/tests/test_jzazbz.py b/colour/models/tests/test_jzazbz.py index 89064b931e..cb561a7710 100644 --- a/colour/models/tests/test_jzazbz.py +++ b/colour/models/tests/test_jzazbz.py @@ -1,89 +1,299 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.jzazbz` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.jzazbz` module.""" import numpy as np import unittest from itertools import permutations -from colour.models import XYZ_to_JzAzBz, JzAzBz_to_XYZ +from colour.models import ( + XYZ_to_Izazbz, + Izazbz_to_XYZ, + XYZ_to_Jzazbz, + Jzazbz_to_XYZ, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestXYZ_to_JzAzBz', 'TestJzAzBz_to_XYZ'] +__all__ = [ + "TestXYZ_to_Izazbz", + "TestIzazbz_to_XYZ", + "TestXYZ_to_Jzazbz", + "TestJzazbz_to_XYZ", +] -class TestXYZ_to_JzAzBz(unittest.TestCase): +class TestXYZ_to_Izazbz(unittest.TestCase): """ - Defines :func:`colour.models.jzazbz.TestXYZ_to_JzAzBz` definition unit + Define :func:`colour.models.jzazbz.TestXYZ_to_Izazbz` definition unit tests methods. """ - def test_XYZ_to_JzAzBz(self): + def test_XYZ_to_Izazbz(self): + """Test :func:`colour.models.jzazbz.XYZ_to_Izazbz` definition.""" + + np.testing.assert_almost_equal( + XYZ_to_Izazbz(np.array([0.20654008, 0.12197225, 0.05136952])), + np.array([0.01207793, 0.00924302, 0.00526007]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_Izazbz(np.array([0.14222010, 0.23042768, 0.10495772])), + np.array([0.01397346, -0.00608426, 0.00534077]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_Izazbz(np.array([0.96907232, 1.00000000, 1.12179215])), + np.array([0.03927203, 0.00064174, -0.00052906]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_Izazbz( + np.array([0.20654008, 0.12197225, 0.05136952]), + method="Safdar 2021", + ), + np.array([0.01049146, 0.00924302, 0.00526007]), + decimal=7, + ) + + np.testing.assert_array_equal( + XYZ_to_Izazbz( + np.array([0.20654008, 0.12197225, 0.05136952]), + method="Safdar 2021", + ), + XYZ_to_Izazbz( + np.array([0.20654008, 0.12197225, 0.05136952]), method="ZCAM" + ), + ) + + def test_n_dimensional_XYZ_to_Izazbz(self): + """ + Test :func:`colour.models.jzazbz.XYZ_to_Izazbz` definition + n-dimensional support. + """ + + XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + Izazbz = XYZ_to_Izazbz(XYZ) + + XYZ = np.tile(XYZ, (6, 1)) + Izazbz = np.tile(Izazbz, (6, 1)) + np.testing.assert_almost_equal(XYZ_to_Izazbz(XYZ), Izazbz, decimal=7) + + XYZ = np.reshape(XYZ, (2, 3, 3)) + Izazbz = np.reshape(Izazbz, (2, 3, 3)) + np.testing.assert_almost_equal(XYZ_to_Izazbz(XYZ), Izazbz, decimal=7) + + def test_domain_range_scale_XYZ_to_Izazbz(self): + """ + Test :func:`colour.models.jzazbz.XYZ_to_Izazbz` definition domain and + range scale support. + """ + + XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + Izazbz = XYZ_to_Izazbz(XYZ) + + d_r = (("reference", 1), ("1", 1), ("100", 1)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + XYZ_to_Izazbz(XYZ * factor), Izazbz * factor, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_XYZ_to_Izazbz(self): """ - Tests :func:`colour.models.jzazbz.XYZ_to_JzAzBz` definition. + Test :func:`colour.models.jzazbz.XYZ_to_Izazbz` definition nan + support. """ + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + XYZ = np.array(case) + XYZ_to_Izazbz(XYZ) + + +class TestIzazbz_to_XYZ(unittest.TestCase): + """ + Define :func:`colour.models.jzazbz.Izazbz_to_XYZ` definition unit tests + methods. + """ + + def test_Izazbz_to_XYZ(self): + """Test :func:`colour.models.jzazbz.Izazbz_to_XYZ` definition.""" + + np.testing.assert_allclose( + Izazbz_to_XYZ(np.array([0.01207793, 0.00924302, 0.00526007])), + np.array([0.20654008, 0.12197225, 0.05136952]), + rtol=0.000001, + atol=0.000001, + ) + + np.testing.assert_allclose( + Izazbz_to_XYZ(np.array([0.01397346, -0.00608426, 0.00534077])), + np.array([0.14222010, 0.23042768, 0.10495772]), + rtol=0.000001, + atol=0.000001, + ) + + np.testing.assert_allclose( + Izazbz_to_XYZ(np.array([0.03927203, 0.00064174, -0.00052906])), + np.array([0.96907232, 1.00000000, 1.12179215]), + rtol=0.000001, + atol=0.000001, + ) + + np.testing.assert_allclose( + Izazbz_to_XYZ(np.array([0.03927203, 0.00064174, -0.00052906])), + np.array([0.96907232, 1.00000000, 1.12179215]), + rtol=0.000001, + atol=0.000001, + ) + np.testing.assert_almost_equal( - XYZ_to_JzAzBz(np.array([0.20654008, 0.12197225, 0.05136952])), + Izazbz_to_XYZ( + np.array([0.01049146, 0.00924302, 0.00526007]), + method="Safdar 2021", + ), + np.array([0.20654008, 0.12197225, 0.05136952]), + decimal=7, + ) + + np.testing.assert_array_equal( + Izazbz_to_XYZ( + np.array([0.01049146, 0.00924302, 0.00526007]), + method="Safdar 2021", + ), + Izazbz_to_XYZ( + np.array([0.01049146, 0.00924302, 0.00526007]), method="ZCAM" + ), + ) + + def test_n_dimensional_Izazbz_to_XYZ(self): + """ + Test :func:`colour.models.jzazbz.Izazbz_to_XYZ` definition + n-dimensional support. + """ + + Izazbz = np.array([0.01207793, 0.00924302, 0.00526007]) + XYZ = Izazbz_to_XYZ(Izazbz) + + Izazbz = np.tile(Izazbz, (6, 1)) + XYZ = np.tile(XYZ, (6, 1)) + np.testing.assert_allclose( + Izazbz_to_XYZ(Izazbz), XYZ, rtol=0.000001, atol=0.000001 + ) + + Izazbz = np.reshape(Izazbz, (2, 3, 3)) + XYZ = np.reshape(XYZ, (2, 3, 3)) + np.testing.assert_allclose( + Izazbz_to_XYZ(Izazbz), XYZ, rtol=0.000001, atol=0.000001 + ) + + def test_domain_range_scale_Izazbz_to_XYZ(self): + """ + Test :func:`colour.models.jzazbz.Izazbz_to_XYZ` definition domain and + range scale support. + """ + + Izazbz = np.array([0.01207793, 0.00924302, 0.00526007]) + XYZ = Izazbz_to_XYZ(Izazbz) + + d_r = (("reference", 1), ("1", 1), ("100", 1)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_allclose( + Izazbz_to_XYZ(Izazbz * factor), + XYZ * factor, + rtol=0.000001, + atol=0.000001, + ) + + @ignore_numpy_errors + def test_nan_Izazbz_to_XYZ(self): + """ + Test :func:`colour.models.jzazbz.Izazbz_to_XYZ` definition nan + support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + Izazbz = np.array(case) + Izazbz_to_XYZ(Izazbz) + + +class TestXYZ_to_Jzazbz(unittest.TestCase): + """ + Define :func:`colour.models.jzazbz.TestXYZ_to_Jzazbz` definition unit + tests methods. + """ + + def test_XYZ_to_Jzazbz(self): + """Test :func:`colour.models.jzazbz.XYZ_to_Jzazbz` definition.""" + + np.testing.assert_almost_equal( + XYZ_to_Jzazbz(np.array([0.20654008, 0.12197225, 0.05136952])), np.array([0.00535048, 0.00924302, 0.00526007]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - XYZ_to_JzAzBz(np.array([0.14222010, 0.23042768, 0.10495772])), + XYZ_to_Jzazbz(np.array([0.14222010, 0.23042768, 0.10495772])), np.array([0.00619681, -0.00608426, 0.00534077]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - XYZ_to_JzAzBz(np.array([0.96907232, 1.00000000, 1.12179215])), + XYZ_to_Jzazbz(np.array([0.96907232, 1.00000000, 1.12179215])), np.array([0.01766826, 0.00064174, -0.00052906]), - decimal=7) + decimal=7, + ) - def test_n_dimensional_XYZ_to_JzAzBz(self): + def test_n_dimensional_XYZ_to_Jzazbz(self): """ - Tests :func:`colour.models.jzazbz.XYZ_to_JzAzBz` definition + Test :func:`colour.models.jzazbz.XYZ_to_Jzazbz` definition n-dimensional support. """ XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) - JzAzBz = XYZ_to_JzAzBz(XYZ) + Jzazbz = XYZ_to_Jzazbz(XYZ) XYZ = np.tile(XYZ, (6, 1)) - JzAzBz = np.tile(JzAzBz, (6, 1)) - np.testing.assert_almost_equal(XYZ_to_JzAzBz(XYZ), JzAzBz, decimal=7) + Jzazbz = np.tile(Jzazbz, (6, 1)) + np.testing.assert_almost_equal(XYZ_to_Jzazbz(XYZ), Jzazbz, decimal=7) XYZ = np.reshape(XYZ, (2, 3, 3)) - JzAzBz = np.reshape(JzAzBz, (2, 3, 3)) - np.testing.assert_almost_equal(XYZ_to_JzAzBz(XYZ), JzAzBz, decimal=7) + Jzazbz = np.reshape(Jzazbz, (2, 3, 3)) + np.testing.assert_almost_equal(XYZ_to_Jzazbz(XYZ), Jzazbz, decimal=7) - def test_domain_range_scale_XYZ_to_JzAzBz(self): + def test_domain_range_scale_XYZ_to_Jzazbz(self): """ - Tests :func:`colour.models.jzazbz.XYZ_to_JzAzBz` definition domain and + Test :func:`colour.models.jzazbz.XYZ_to_Jzazbz` definition domain and range scale support. """ XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) - JzAzBz = XYZ_to_JzAzBz(XYZ) + Jzazbz = XYZ_to_Jzazbz(XYZ) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - XYZ_to_JzAzBz(XYZ * factor), JzAzBz * factor, decimal=7) + XYZ_to_Jzazbz(XYZ * factor), Jzazbz * factor, decimal=7 + ) @ignore_numpy_errors - def test_nan_XYZ_to_JzAzBz(self): + def test_nan_XYZ_to_Jzazbz(self): """ - Tests :func:`colour.models.jzazbz.XYZ_to_JzAzBz` definition nan + Test :func:`colour.models.jzazbz.XYZ_to_Jzazbz` definition nan support. """ @@ -91,88 +301,92 @@ def test_nan_XYZ_to_JzAzBz(self): cases = set(permutations(cases * 3, r=3)) for case in cases: XYZ = np.array(case) - XYZ_to_JzAzBz(XYZ) + XYZ_to_Jzazbz(XYZ) -class TestJzAzBz_to_XYZ(unittest.TestCase): +class TestJzazbz_to_XYZ(unittest.TestCase): """ - Defines :func:`colour.models.jzazbz.JzAzBz_to_XYZ` definition unit tests + Define :func:`colour.models.jzazbz.Jzazbz_to_XYZ` definition unit tests methods. """ - def test_JzAzBz_to_XYZ(self): - """ - Tests :func:`colour.models.jzazbz.JzAzBz_to_XYZ` definition. - """ + def test_Jzazbz_to_XYZ(self): + """Test :func:`colour.models.jzazbz.Jzazbz_to_XYZ` definition.""" np.testing.assert_allclose( - JzAzBz_to_XYZ(np.array([0.00535048, 0.00924302, 0.00526007])), + Jzazbz_to_XYZ(np.array([0.00535048, 0.00924302, 0.00526007])), np.array([0.20654008, 0.12197225, 0.05136952]), rtol=0.000001, - atol=0.000001) + atol=0.000001, + ) np.testing.assert_allclose( - JzAzBz_to_XYZ(np.array([0.00619681, -0.00608426, 0.00534077])), + Jzazbz_to_XYZ(np.array([0.00619681, -0.00608426, 0.00534077])), np.array([0.14222010, 0.23042768, 0.10495772]), rtol=0.000001, - atol=0.000001) + atol=0.000001, + ) np.testing.assert_allclose( - JzAzBz_to_XYZ(np.array([0.01766826, 0.00064174, -0.00052906])), + Jzazbz_to_XYZ(np.array([0.01766826, 0.00064174, -0.00052906])), np.array([0.96907232, 1.00000000, 1.12179215]), rtol=0.000001, - atol=0.000001) + atol=0.000001, + ) - def test_n_dimensional_JzAzBz_to_XYZ(self): + def test_n_dimensional_Jzazbz_to_XYZ(self): """ - Tests :func:`colour.models.jzazbz.JzAzBz_to_XYZ` definition + Test :func:`colour.models.jzazbz.Jzazbz_to_XYZ` definition n-dimensional support. """ - JzAzBz = np.array([0.00535048, 0.00924302, 0.00526007]) - XYZ = JzAzBz_to_XYZ(JzAzBz) + Jzazbz = np.array([0.00535048, 0.00924302, 0.00526007]) + XYZ = Jzazbz_to_XYZ(Jzazbz) - JzAzBz = np.tile(JzAzBz, (6, 1)) + Jzazbz = np.tile(Jzazbz, (6, 1)) XYZ = np.tile(XYZ, (6, 1)) np.testing.assert_allclose( - JzAzBz_to_XYZ(JzAzBz), XYZ, rtol=0.000001, atol=0.000001) + Jzazbz_to_XYZ(Jzazbz), XYZ, rtol=0.000001, atol=0.000001 + ) - JzAzBz = np.reshape(JzAzBz, (2, 3, 3)) + Jzazbz = np.reshape(Jzazbz, (2, 3, 3)) XYZ = np.reshape(XYZ, (2, 3, 3)) np.testing.assert_allclose( - JzAzBz_to_XYZ(JzAzBz), XYZ, rtol=0.000001, atol=0.000001) + Jzazbz_to_XYZ(Jzazbz), XYZ, rtol=0.000001, atol=0.000001 + ) - def test_domain_range_scale_JzAzBz_to_XYZ(self): + def test_domain_range_scale_Jzazbz_to_XYZ(self): """ - Tests :func:`colour.models.jzazbz.JzAzBz_to_XYZ` definition domain and + Test :func:`colour.models.jzazbz.Jzazbz_to_XYZ` definition domain and range scale support. """ - JzAzBz = np.array([0.00535048, 0.00924302, 0.00526007]) - XYZ = JzAzBz_to_XYZ(JzAzBz) + Jzazbz = np.array([0.00535048, 0.00924302, 0.00526007]) + XYZ = Jzazbz_to_XYZ(Jzazbz) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_allclose( - JzAzBz_to_XYZ(JzAzBz * factor), + Jzazbz_to_XYZ(Jzazbz * factor), XYZ * factor, rtol=0.000001, - atol=0.000001) + atol=0.000001, + ) @ignore_numpy_errors - def test_nan_JzAzBz_to_XYZ(self): + def test_nan_Jzazbz_to_XYZ(self): """ - Tests :func:`colour.models.jzazbz.JzAzBz_to_XYZ` definition nan + Test :func:`colour.models.jzazbz.Jzazbz_to_XYZ` definition nan support. """ cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) for case in cases: - JzAzBz = np.array(case) - JzAzBz_to_XYZ(JzAzBz) + Jzazbz = np.array(case) + Jzazbz_to_XYZ(Jzazbz) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/tests/test_oklab.py b/colour/models/tests/test_oklab.py new file mode 100644 index 0000000000..66b2c42061 --- /dev/null +++ b/colour/models/tests/test_oklab.py @@ -0,0 +1,182 @@ +"""Defines the unit tests for the :mod:`colour.models.oklab` module.""" + +import numpy as np +import unittest +from itertools import permutations + +from colour.models import XYZ_to_Oklab, Oklab_to_XYZ +from colour.utilities import domain_range_scale, ignore_numpy_errors + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestXYZ_to_Oklab", + "TestOklab_to_XYZ", +] + + +class TestXYZ_to_Oklab(unittest.TestCase): + """ + Define :func:`colour.models.oklab.TestXYZ_to_Oklab` definition unit + tests methods. + """ + + def test_XYZ_to_Oklab(self): + """Test :func:`colour.models.oklab.XYZ_to_Oklab` definition.""" + + np.testing.assert_almost_equal( + XYZ_to_Oklab(np.array([0.20654008, 0.12197225, 0.05136952])), + np.array([0.51634019, 0.15469500, 0.06289579]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_Oklab(np.array([0.14222010, 0.23042768, 0.10495772])), + np.array([0.59910746, -0.11139207, 0.07508465]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_Oklab(np.array([0.96907232, 1.00000000, 1.12179215])), + np.array([1.00121561, 0.00899591, -0.00535107]), + decimal=7, + ) + + def test_n_dimensional_XYZ_to_Oklab(self): + """ + Test :func:`colour.models.oklab.XYZ_to_Oklab` definition + n-dimensional support. + """ + + XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + Oklab = XYZ_to_Oklab(XYZ) + + XYZ = np.tile(XYZ, (6, 1)) + Oklab = np.tile(Oklab, (6, 1)) + np.testing.assert_almost_equal(XYZ_to_Oklab(XYZ), Oklab, decimal=7) + + XYZ = np.reshape(XYZ, (2, 3, 3)) + Oklab = np.reshape(Oklab, (2, 3, 3)) + np.testing.assert_almost_equal(XYZ_to_Oklab(XYZ), Oklab, decimal=7) + + def test_domain_range_scale_XYZ_to_Oklab(self): + """ + Test :func:`colour.models.oklab.XYZ_to_Oklab` definition domain and + range scale support. + """ + + XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + Oklab = XYZ_to_Oklab(XYZ) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + XYZ_to_Oklab(XYZ * factor), Oklab * factor, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_XYZ_to_Oklab(self): + """ + Test :func:`colour.models.oklab.XYZ_to_Oklab` definition nan + support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + XYZ = np.array(case) + XYZ_to_Oklab(XYZ) + + +class TestOklab_to_XYZ(unittest.TestCase): + """ + Define :func:`colour.models.oklab.Oklab_to_XYZ` definition unit tests + methods. + """ + + def test_Oklab_to_XYZ(self): + """Test :func:`colour.models.oklab.Oklab_to_XYZ` definition.""" + + np.testing.assert_allclose( + Oklab_to_XYZ(np.array([0.51634019, 0.15469500, 0.06289579])), + np.array([0.20654008, 0.12197225, 0.05136952]), + rtol=0.000001, + atol=0.000001, + ) + + np.testing.assert_allclose( + Oklab_to_XYZ(np.array([0.59910746, -0.11139207, 0.07508465])), + np.array([0.14222010, 0.23042768, 0.10495772]), + rtol=0.000001, + atol=0.000001, + ) + + np.testing.assert_allclose( + Oklab_to_XYZ(np.array([1.00121561, 0.00899591, -0.00535107])), + np.array([0.96907232, 1.00000000, 1.12179215]), + rtol=0.000001, + atol=0.000001, + ) + + def test_n_dimensional_Oklab_to_XYZ(self): + """ + Test :func:`colour.models.oklab.Oklab_to_XYZ` definition + n-dimensional support. + """ + + Oklab = np.array([0.51634019, 0.15469500, 0.06289579]) + XYZ = Oklab_to_XYZ(Oklab) + + Oklab = np.tile(Oklab, (6, 1)) + XYZ = np.tile(XYZ, (6, 1)) + np.testing.assert_allclose( + Oklab_to_XYZ(Oklab), XYZ, rtol=0.000001, atol=0.000001 + ) + + Oklab = np.reshape(Oklab, (2, 3, 3)) + XYZ = np.reshape(XYZ, (2, 3, 3)) + np.testing.assert_allclose( + Oklab_to_XYZ(Oklab), XYZ, rtol=0.000001, atol=0.000001 + ) + + def test_domain_range_scale_Oklab_to_XYZ(self): + """ + Test :func:`colour.models.oklab.Oklab_to_XYZ` definition domain and + range scale support. + """ + + Oklab = np.array([0.51634019, 0.15469500, 0.06289579]) + XYZ = Oklab_to_XYZ(Oklab) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_allclose( + Oklab_to_XYZ(Oklab * factor), + XYZ * factor, + rtol=0.000001, + atol=0.000001, + ) + + @ignore_numpy_errors + def test_nan_Oklab_to_XYZ(self): + """ + Test :func:`colour.models.oklab.Oklab_to_XYZ` definition nan + support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + Oklab = np.array(case) + Oklab_to_XYZ(Oklab) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/models/tests/test_osa_ucs.py b/colour/models/tests/test_osa_ucs.py index 2bffe17eb1..7bc34248ab 100644 --- a/colour/models/tests/test_osa_ucs.py +++ b/colour/models/tests/test_osa_ucs.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.models.osa_ucs` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.models.osa_ucs` module.""" import numpy as np import unittest @@ -12,48 +7,55 @@ from colour.models import XYZ_to_OSA_UCS, OSA_UCS_to_XYZ from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestXYZ_to_OSA_UCS', 'TestOSA_UCS_to_XYZ'] +__all__ = [ + "TestXYZ_to_OSA_UCS", + "TestOSA_UCS_to_XYZ", +] class TestXYZ_to_OSA_UCS(unittest.TestCase): """ - Defines :func:`colour.models.osa_ucs.XYZ_to_OSA_UCS` definition unit tests + Define :func:`colour.models.osa_ucs.XYZ_to_OSA_UCS` definition unit tests methods. """ def test_XYZ_to_OSA_UCS(self): - """ - Tests :func:`colour.models.osa_ucs.XYZ_to_OSA_UCS` definition. - """ + """Test :func:`colour.models.osa_ucs.XYZ_to_OSA_UCS` definition.""" np.testing.assert_almost_equal( XYZ_to_OSA_UCS( - np.array([0.20654008, 0.12197225, 0.05136952]) * 100), + np.array([0.20654008, 0.12197225, 0.05136952]) * 100 + ), np.array([-3.00499790, 2.99713697, -9.66784231]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_OSA_UCS( - np.array([0.14222010, 0.23042768, 0.10495772]) * 100), + np.array([0.14222010, 0.23042768, 0.10495772]) * 100 + ), np.array([-1.64657491, 4.59201565, 5.31738757]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( XYZ_to_OSA_UCS( - np.array([0.07818780, 0.06157201, 0.28099326]) * 100), + np.array([0.07818780, 0.06157201, 0.28099326]) * 100 + ), np.array([-5.08589672, -7.91062749, 0.98107575]), - decimal=7) + decimal=7, + ) def test_n_dimensional_XYZ_to_OSA_UCS(self): """ - Tests :func:`colour.models.osa_ucs.XYZ_to_OSA_UCS` definition + Test :func:`colour.models.osa_ucs.XYZ_to_OSA_UCS` definition n-dimensional support. """ @@ -70,23 +72,24 @@ def test_n_dimensional_XYZ_to_OSA_UCS(self): def test_domain_range_scale_XYZ_to_OSA_UCS(self): """ - Tests :func:`colour.models.osa_ucs.XYZ_to_OSA_UCS` definition domain + Test :func:`colour.models.osa_ucs.XYZ_to_OSA_UCS` definition domain and range scale support. """ XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) * 100 Ljg = XYZ_to_OSA_UCS(XYZ) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - XYZ_to_OSA_UCS(XYZ * factor), Ljg * factor, decimal=7) + XYZ_to_OSA_UCS(XYZ * factor), Ljg * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_XYZ_to_OSA_UCS(self): """ - Tests :func:`colour.models.osa_ucs.XYZ_to_OSA_UCS` definition nan + Test :func:`colour.models.osa_ucs.XYZ_to_OSA_UCS` definition nan support. """ @@ -98,42 +101,46 @@ def test_nan_XYZ_to_OSA_UCS(self): class TestOSA_UCS_to_XYZ(unittest.TestCase): """ - Defines :func:`colour.models.osa_ucs.OSA_UCS_to_XYZ` definition unit tests + Define :func:`colour.models.osa_ucs.OSA_UCS_to_XYZ` definition unit tests methods. """ def test_OSA_UCS_to_XYZ(self): - """ - Tests :func:`colour.models.osa_ucs.OSA_UCS_to_XYZ` definition. - """ + """Test :func:`colour.models.osa_ucs.OSA_UCS_to_XYZ` definition.""" np.testing.assert_allclose( OSA_UCS_to_XYZ( np.array([-3.00499790, 2.99713697, -9.66784231]), - {'disp': False}), + {"disp": False}, + ), np.array([0.20654008, 0.12197225, 0.05136952]) * 100, rtol=0.00001, - atol=0.00001) + atol=0.00001, + ) np.testing.assert_allclose( OSA_UCS_to_XYZ( np.array([-1.64657491, 4.59201565, 5.31738757]), - {'disp': False}), + {"disp": False}, + ), np.array([0.14222010, 0.23042768, 0.10495772]) * 100, rtol=0.00001, - atol=0.00001) + atol=0.00001, + ) np.testing.assert_allclose( OSA_UCS_to_XYZ( np.array([-5.08589672, -7.91062749, 0.98107575]), - {'disp': False}), + {"disp": False}, + ), np.array([0.07818780, 0.06157201, 0.28099326]) * 100, rtol=0.00001, - atol=0.00001) + atol=0.00001, + ) def test_n_dimensional_OSA_UCS_to_XYZ(self): """ - Tests :func:`colour.models.osa_ucs.OSA_UCS_to_XYZ` definition + Test :func:`colour.models.osa_ucs.OSA_UCS_to_XYZ` definition n-dimensional support. """ @@ -143,32 +150,35 @@ def test_n_dimensional_OSA_UCS_to_XYZ(self): Ljg = np.tile(Ljg, (6, 1)) XYZ = np.tile(XYZ, (6, 1)) np.testing.assert_allclose( - OSA_UCS_to_XYZ(Ljg), XYZ, rtol=0.00001, atol=0.00001) + OSA_UCS_to_XYZ(Ljg), XYZ, rtol=0.00001, atol=0.00001 + ) Ljg = np.reshape(Ljg, (2, 3, 3)) XYZ = np.reshape(XYZ, (2, 3, 3)) np.testing.assert_allclose( - OSA_UCS_to_XYZ(Ljg), XYZ, rtol=0.00001, atol=0.00001) + OSA_UCS_to_XYZ(Ljg), XYZ, rtol=0.00001, atol=0.00001 + ) def test_domain_range_scale_OSA_UCS_to_XYZ(self): """ - Tests :func:`colour.models.osa_ucs.OSA_UCS_to_XYZ` definition domain + Test :func:`colour.models.osa_ucs.OSA_UCS_to_XYZ` definition domain and range scale support. """ Ljg = np.array([-3.00499790, 2.99713697, -9.66784231]) XYZ = OSA_UCS_to_XYZ(Ljg) - d_r = (('reference', 1), (1, 0.01), (100, 1)) + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - OSA_UCS_to_XYZ(Ljg * factor), XYZ * factor, decimal=7) + OSA_UCS_to_XYZ(Ljg * factor), XYZ * factor, decimal=7 + ) @ignore_numpy_errors def test_nan_OSA_UCS_to_XYZ(self): """ - Tests :func:`colour.models.osa_ucs.OSA_UCS_to_XYZ` definition nan + Test :func:`colour.models.osa_ucs.OSA_UCS_to_XYZ` definition nan support. """ @@ -178,5 +188,5 @@ def test_nan_OSA_UCS_to_XYZ(self): OSA_UCS_to_XYZ(np.array(case)) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/models/tests/test_prolab.py b/colour/models/tests/test_prolab.py new file mode 100644 index 0000000000..997f8db054 --- /dev/null +++ b/colour/models/tests/test_prolab.py @@ -0,0 +1,169 @@ +"""Defines the unit tests for the :mod:`colour.models.prolab` module.""" + +import numpy as np +import unittest +from itertools import permutations + +from colour.models import XYZ_to_ProLab, ProLab_to_XYZ +from colour.utilities import ignore_numpy_errors, domain_range_scale + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestXYZ_to_ProLab", + "TestProLab_to_XYZ", +] + + +class TestXYZ_to_ProLab(unittest.TestCase): + """ + Define :func:`colour.models.ProLab.TestXYZ_to_ProLab` definition unit + tests methods. + """ + + def test_XYZ_to_ProLab(self): + """Test :func:`colour.models.ProLab.XYZ_to_ProLab` definition.""" + + np.testing.assert_almost_equal( + XYZ_to_ProLab(np.array([0.20654008, 0.12197225, 0.05136952])), + np.array([48.7948929, 35.31503175, 13.30044932]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_ProLab(np.array([0.14222010, 0.23042768, 0.10495772])), + np.array([64.45929636, -21.67007419, 13.25749056]), + decimal=7, + ) + + np.testing.assert_almost_equal( + XYZ_to_ProLab(np.array([0.96907232, 1.00000000, 0.12179215])), + np.array([100.0, 5.47367608, 37.26313098]), + decimal=7, + ) + + def test_n_dimensional_XYZ_to_ProLab(self): + """ + Test :func:`colour.models.prolab.XYZ_to_ProLab` definition + n-dimensional support. + """ + + XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + ProLab = XYZ_to_ProLab(XYZ) + + XYZ = np.tile(XYZ, (6, 1)) + ProLab = np.tile(ProLab, (6, 1)) + np.testing.assert_almost_equal(XYZ_to_ProLab(XYZ), ProLab, decimal=7) + + XYZ = np.reshape(XYZ, (2, 3, 3)) + ProLab = np.reshape(ProLab, (2, 3, 3)) + np.testing.assert_almost_equal(XYZ_to_ProLab(XYZ), ProLab, decimal=7) + + def test_domain_range_scale_XYZ_to_ProLab(self): + """ + Test :func:`colour.models.prolab.XYZ_to_ProLab` definition domain and + range scale support. + """ + + XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + ProLab = XYZ_to_ProLab(XYZ) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + XYZ_to_ProLab(XYZ * factor), ProLab * factor, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_XYZ_to_ProLab(self): + """ + Test :func:`colour.models.ProLab.XYZ_to_ProLab` definition + nan support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + XYZ = np.array(case) + XYZ_to_ProLab(XYZ) + + +class TestProLab_to_XYZ(unittest.TestCase): + """ + Define :func:`colour.models.ProLab.ProLab_to_XYZ` definition unit tests + methods. + """ + + def test_ProLab_to_XYZ(self): + """Test :func:`colour.models.ProLab.ProLab_to_XYZ` definition.""" + + np.testing.assert_almost_equal( + ProLab_to_XYZ(np.array([48.7948929, 35.31503175, 13.30044932])), + np.array([0.20654008, 0.12197225, 0.05136952]), + ) + + np.testing.assert_almost_equal( + ProLab_to_XYZ(np.array([64.45929636, -21.67007419, 13.25749056])), + np.array([0.14222010, 0.23042768, 0.10495772]), + ) + + np.testing.assert_almost_equal( + ProLab_to_XYZ(np.array([100.0, 5.47367608, 37.26313098])), + np.array([0.96907232, 1.00000000, 0.12179215]), + ) + + def test_n_dimensional_XYZ_to_ProLab(self): + """ + Test :func:`colour.models.prolab.XYZ_to_ProLab` definition + n-dimensional support. + """ + + ProLab = np.array([48.7948929, 35.31503175, 13.30044932]) + XYZ = ProLab_to_XYZ(ProLab) + + ProLab = np.tile(ProLab, (6, 1)) + XYZ = np.tile(XYZ, (6, 1)) + np.testing.assert_almost_equal(ProLab_to_XYZ(ProLab), XYZ, decimal=7) + + ProLab = np.reshape(ProLab, (2, 3, 3)) + XYZ = np.reshape(XYZ, (2, 3, 3)) + np.testing.assert_almost_equal(ProLab_to_XYZ(ProLab), XYZ, decimal=7) + + def test_domain_range_scale_XYZ_to_ProLab(self): + """ + Test :func:`colour.models.prolab.XYZ_to_ProLab` definition domain and + range scale support. + """ + + ProLab = np.array([48.7948929, 35.31503175, 13.30044932]) + XYZ = XYZ_to_ProLab(ProLab) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_almost_equal( + XYZ_to_ProLab(ProLab * factor), XYZ * factor, decimal=7 + ) + + @ignore_numpy_errors + def test_nan_ProLab_to_XYZ(self): + """ + Test :func:`colour.models.ProLab.ProLab_to_XYZ` definition nan + support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = set(permutations(cases * 3, r=3)) + for case in cases: + ProLab = np.array(case) + ProLab_to_XYZ(ProLab) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/notation/__init__.py b/colour/notation/__init__.py index f8aeae2d75..4d62ea2222 100644 --- a/colour/notation/__init__.py +++ b/colour/notation/__init__.py @@ -1,60 +1,49 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - -import sys - -from colour.utilities.deprecation import ModuleAPI, build_API_changes -from colour.utilities.documentation import is_documentation_building - -from .datasets import * # noqa -from . import datasets +from .datasets import ( + MUNSELL_COLOURS_ALL, + MUNSELL_COLOURS_1929, + MUNSELL_COLOURS_REAL, + MUNSELL_COLOURS, +) from .munsell import MUNSELL_VALUE_METHODS from .munsell import munsell_value -from .munsell import (munsell_value_Priest1920, munsell_value_Munsell1933, - munsell_value_Moon1943, munsell_value_Saunderson1944, - munsell_value_Ladd1955, munsell_value_McCamy1987, - munsell_value_ASTMD1535) +from .munsell import ( + munsell_value_Priest1920, + munsell_value_Munsell1933, + munsell_value_Moon1943, + munsell_value_Saunderson1944, + munsell_value_Ladd1955, + munsell_value_McCamy1987, + munsell_value_ASTMD1535, +) from .munsell import munsell_colour_to_xyY, xyY_to_munsell_colour from .hexadecimal import RGB_to_HEX, HEX_to_RGB -__all__ = [] -__all__ += datasets.__all__ -__all__ += ['munsell_value'] -__all__ += ['MUNSELL_VALUE_METHODS'] +__all__ = [ + "MUNSELL_COLOURS_ALL", + "MUNSELL_COLOURS_1929", + "MUNSELL_COLOURS_REAL", + "MUNSELL_COLOURS", +] __all__ += [ - 'munsell_value_Priest1920', 'munsell_value_Munsell1933', - 'munsell_value_Moon1943', 'munsell_value_Saunderson1944', - 'munsell_value_Ladd1955', 'munsell_value_McCamy1987', - 'munsell_value_ASTMD1535' + "munsell_value", +] +__all__ += [ + "MUNSELL_VALUE_METHODS", +] +__all__ += [ + "munsell_value_Priest1920", + "munsell_value_Munsell1933", + "munsell_value_Moon1943", + "munsell_value_Saunderson1944", + "munsell_value_Ladd1955", + "munsell_value_McCamy1987", + "munsell_value_ASTMD1535", +] +__all__ += [ + "munsell_colour_to_xyY", + "xyY_to_munsell_colour", +] +__all__ += [ + "RGB_to_HEX", + "HEX_to_RGB", ] -__all__ += ['munsell_colour_to_xyY', 'xyY_to_munsell_colour'] -__all__ += ['RGB_to_HEX', 'HEX_to_RGB'] - - -# ----------------------------------------------------------------------------# -# --- API Changes and Deprecation Management ---# -# ----------------------------------------------------------------------------# -class notation(ModuleAPI): - def __getattr__(self, attribute): - return super(notation, self).__getattr__(attribute) - - -# v0.3.14 -API_CHANGES = { - 'ObjectRenamed': [[ - 'colour.notation.munsell_value_ASTMD153508', - 'colour.notation.munsell_value_ASTMD1535', - ], ] -} -""" -Defines *colour.notation* sub-package API changes. - -API_CHANGES : dict -""" - -if not is_documentation_building(): - sys.modules['colour.notation'] = notation(sys.modules['colour.notation'], - build_API_changes(API_CHANGES)) - - del ModuleAPI, is_documentation_building, build_API_changes, sys diff --git a/colour/notation/datasets/__init__.py b/colour/notation/datasets/__init__.py index 5fe43d3dae..0f5a496c88 100644 --- a/colour/notation/datasets/__init__.py +++ b/colour/notation/datasets/__init__.py @@ -1,9 +1,13 @@ -# -*- coding: utf-8 -*- +from .munsell import ( + MUNSELL_COLOURS_ALL, + MUNSELL_COLOURS_1929, + MUNSELL_COLOURS_REAL, + MUNSELL_COLOURS, +) -from __future__ import absolute_import - -from .munsell import * # noqa -from . import munsell - -__all__ = [] -__all__ += munsell.__all__ +__all__ = [ + "MUNSELL_COLOURS_ALL", + "MUNSELL_COLOURS_1929", + "MUNSELL_COLOURS_REAL", + "MUNSELL_COLOURS", +] diff --git a/colour/notation/datasets/munsell/__init__.py b/colour/notation/datasets/munsell/__init__.py index 02c8770183..2195aafc36 100644 --- a/colour/notation/datasets/munsell/__init__.py +++ b/colour/notation/datasets/munsell/__init__.py @@ -1,25 +1,64 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .all import MUNSELL_COLOURS_ALL from .experimental import MUNSELL_COLOURS_1929 from .real import MUNSELL_COLOURS_REAL from colour.utilities import CaseInsensitiveMapping -__all__ = ['MUNSELL_COLOURS_ALL'] -__all__ += ['MUNSELL_COLOURS_1929'] -__all__ += ['MUNSELL_COLOURS_REAL'] +__all__ = [ + "MUNSELL_COLOURS_ALL", +] +__all__ += [ + "MUNSELL_COLOURS_1929", +] +__all__ += [ + "MUNSELL_COLOURS_REAL", +] -MUNSELL_COLOURS = CaseInsensitiveMapping({ - 'Munsell Colours All': MUNSELL_COLOURS_ALL, - 'Munsell Colours 1929': MUNSELL_COLOURS_1929, - 'Munsell Colours Real': MUNSELL_COLOURS_REAL -}) +MUNSELL_COLOURS = CaseInsensitiveMapping( + { + "Munsell Colours All": MUNSELL_COLOURS_ALL, + "Munsell Colours 1929": MUNSELL_COLOURS_1929, + "Munsell Colours Real": MUNSELL_COLOURS_REAL, + } +) MUNSELL_COLOURS.__doc__ = """ -Aggregated *Munsell* colours. +Defines the *Munsell Renotation System* datasets. + +- ``Munsell Colours All``: *all* published *Munsell* colours, including the + extrapolated colors. +- ``Munsell Colours 1929``: the colours appearing in the 1929 + *Munsell Book of Color*. These data has been used in the scaling + experiments leading to the 1943 renotation. +- ``Munsell Colours Real``: *real*, within MacAdam limits *Munsell* colours + only. They are the colours listed in the original 1943 renotation article + *(Newhall, Nickerson, & Judd, 1943)*. + +Notes +----- +- The Munsell Renotation data commonly available within the *all.dat*, + *experimental.dat* and *real.dat* files features *CIE xyY* colourspace + values that are scaled by a :math:`1 / 0.975 \\simeq 1.02568` factor. If + you are performing conversions using *Munsell* *Colorlab* specification, + e.g. *2.5R 9/2*, according to *ASTM D1535-08e1* method, you should not + scale the output :math:`Y` Luminance. However, if you use directly the + *CIE xyY* colourspace values from the Munsell Renotation data data, you + should scale the :math:`Y` Luminance before conversions by a :math:`0.975` + factor. + + *ASTM D1535-08e1* states that:: + + The coefficients of this equation are obtained from the 1943 equation + by multiplying each coefficient by 0.975, the reflectance factor of + magnesium oxide with respect to the perfect reflecting diffuser, and + rounding to ve digits of precision. + +- Chromaticities assume *CIE Illuminant C*, approximately 6700K, as neutral + origin for both the hue and chroma loci. -MUNSELL_COLOURS : CaseInsensitiveMapping +References +---------- +- :cite:`MunsellColorSciencec` : Munsell Color Science. (n.d.). Munsell + Colours Data. Retrieved August 20, 2014, from + http://www.cis.rit.edu/research/mcsl2/online/munsell.php Aliases: @@ -27,8 +66,10 @@ - '1929': 'Munsell Colours 1929' - 'real': 'Munsell Colours Real' """ -MUNSELL_COLOURS['all'] = MUNSELL_COLOURS['Munsell Colours All'] -MUNSELL_COLOURS['1929'] = MUNSELL_COLOURS['Munsell Colours 1929'] -MUNSELL_COLOURS['real'] = MUNSELL_COLOURS['Munsell Colours Real'] +MUNSELL_COLOURS["all"] = MUNSELL_COLOURS["Munsell Colours All"] +MUNSELL_COLOURS["1929"] = MUNSELL_COLOURS["Munsell Colours 1929"] +MUNSELL_COLOURS["real"] = MUNSELL_COLOURS["Munsell Colours Real"] -__all__ += ['MUNSELL_COLOURS'] +__all__ += [ + "MUNSELL_COLOURS", +] diff --git a/colour/notation/datasets/munsell/all.py b/colour/notation/datasets/munsell/all.py index 4656e9508e..81a2a071a9 100644 --- a/colour/notation/datasets/munsell/all.py +++ b/colour/notation/datasets/munsell/all.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Munsell Renotation System Dataset - All Munsell Colours ======================================================= @@ -8,10 +7,10 @@ Notes ----- -- The Munsell Renotation data commonly available within the all.dat, - experimental.dat and real.dat files features *CIE xyY* colourspace values - that are scaled by a :math:`1 / 0.975 \\simeq 1.02568` factor. If you are - performing conversions using *Munsell* *Colorlab* specification, +- The Munsell Renotation data commonly available within the *all.dat*, + *experimental.dat* and *real.dat* files features *CIE xyY* colourspace + values that are scaled by a :math:`1 / 0.975 \\simeq 1.02568` factor. If + you are performing conversions using *Munsell* *Colorlab* specification, e.g. *2.5R 9/2*, according to *ASTM D1535-08e1* method, you should not scale the output :math:`Y` Luminance. However, if you use directly the *CIE xyY* colourspace values from the Munsell Renotation data data, you @@ -25,6 +24,9 @@ magnesium oxide with respect to the perfect reflecting diffuser, and rounding to ve digits of precision. +- Chromaticities assume *CIE Illuminant C*, approximately 6700K, as neutral + origin for both the hue and chroma loci. + References ---------- - :cite:`MunsellColorSciencec` : Munsell Color Science. (n.d.). Munsell @@ -32,5024 +34,5033 @@ http://www.cis.rit.edu/research/mcsl2/online/munsell.php """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import Tuple + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "MUNSELL_COLOURS_ALL", +] -__all__ = ['MUNSELL_COLOURS_ALL'] -# yapf: disable -MUNSELL_COLOURS_ALL = ( - (('2.5GY', 0.2, 2.0), np.array([0.7130, 1.4140, 0.2370])), - (('5GY', 0.2, 2.0), np.array([0.4490, 1.1450, 0.2370])), - (('7.5GY', 0.2, 2.0), np.array([0.2620, 0.8370, 0.2370])), - (('7.5GY', 0.2, 4.0), np.array([-0.0780, 2.1600, 0.2370])), - (('10GY', 0.2, 2.0), np.array([0.1850, 0.6760, 0.2370])), - (('10GY', 0.2, 4.0), np.array([-0.2570, 1.2330, 0.2370])), - (('2.5G', 0.2, 2.0), np.array([0.1440, 0.5840, 0.2370])), - (('2.5G', 0.2, 4.0), np.array([-0.2350, 0.8910, 0.2370])), - (('5G', 0.2, 2.0), np.array([0.1170, 0.5160, 0.2370])), - (('5G', 0.2, 4.0), np.array([-0.2090, 0.7190, 0.2370])), - (('7.5G', 0.2, 2.0), np.array([0.0970, 0.4580, 0.2370])), - (('7.5G', 0.2, 4.0), np.array([-0.1810, 0.5750, 0.2370])), - (('10G', 0.2, 2.0), np.array([0.0800, 0.3970, 0.2370])), - (('10G', 0.2, 4.0), np.array([-0.1370, 0.4250, 0.2370])), - (('2.5BG', 0.2, 2.0), np.array([0.0680, 0.3320, 0.2370])), - (('2.5BG', 0.2, 4.0), np.array([-0.1000, 0.3250, 0.2370])), - (('5BG', 0.2, 2.0), np.array([0.0660, 0.2610, 0.2370])), - (('5BG', 0.2, 4.0), np.array([-0.0480, 0.2230, 0.2370])), - (('7.5BG', 0.2, 2.0), np.array([0.0720, 0.2260, 0.2370])), - (('7.5BG', 0.2, 4.0), np.array([-0.0160, 0.1810, 0.2370])), - (('10BG', 0.2, 2.0), np.array([0.0850, 0.1950, 0.2370])), - (('10BG', 0.2, 4.0), np.array([0.0150, 0.1480, 0.2370])), - (('2.5B', 0.2, 2.0), np.array([0.0970, 0.1770, 0.2370])), - (('2.5B', 0.2, 4.0), np.array([0.0390, 0.1280, 0.2370])), - (('5B', 0.2, 2.0), np.array([0.1110, 0.1640, 0.2370])), - (('5B', 0.2, 4.0), np.array([0.0630, 0.1140, 0.2370])), - (('7.5B', 0.2, 2.0), np.array([0.1210, 0.1570, 0.2370])), - (('7.5B', 0.2, 4.0), np.array([0.0740, 0.1090, 0.2370])), - (('10B', 0.2, 2.0), np.array([0.1330, 0.1490, 0.2370])), - (('10B', 0.2, 4.0), np.array([0.0880, 0.1020, 0.2370])), - (('10B', 0.2, 6.0), np.array([0.0630, 0.0740, 0.2370])), - (('2.5PB', 0.2, 2.0), np.array([0.1470, 0.1430, 0.2370])), - (('2.5PB', 0.2, 4.0), np.array([0.1090, 0.0940, 0.2370])), - (('2.5PB', 0.2, 6.0), np.array([0.0870, 0.0670, 0.2370])), - (('5PB', 0.2, 2.0), np.array([0.1650, 0.1360, 0.2370])), - (('5PB', 0.2, 4.0), np.array([0.1330, 0.0900, 0.2370])), - (('5PB', 0.2, 6.0), np.array([0.1150, 0.0620, 0.2370])), - (('5PB', 0.2, 8.0), np.array([0.1040, 0.0450, 0.2370])), - (('5PB', 0.2, 10.0), np.array([0.0960, 0.0320, 0.2370])), - (('5PB', 0.2, 12.0), np.array([0.0860, 0.0180, 0.2370])), - (('7.5PB', 0.2, 2.0), np.array([0.1920, 0.1300, 0.2370])), - (('7.5PB', 0.2, 4.0), np.array([0.1710, 0.0870, 0.2370])), - (('7.5PB', 0.2, 6.0), np.array([0.1590, 0.0610, 0.2370])), - (('7.5PB', 0.2, 8.0), np.array([0.1520, 0.0450, 0.2370])), - (('7.5PB', 0.2, 10.0), np.array([0.1480, 0.0340, 0.2370])), - (('7.5PB', 0.2, 12.0), np.array([0.1450, 0.0250, 0.2370])), - (('7.5PB', 0.2, 14.0), np.array([0.1420, 0.0180, 0.2370])), - (('7.5PB', 0.2, 16.0), np.array([0.1390, 0.0120, 0.2370])), - (('7.5PB', 0.2, 18.0), np.array([0.1370, 0.0060, 0.2370])), - (('7.5PB', 0.2, 20.0), np.array([0.1360, 0.0000, 0.2370])), - (('10PB', 0.2, 2.0), np.array([0.2270, 0.1260, 0.2370])), - (('10PB', 0.2, 4.0), np.array([0.2130, 0.0880, 0.2370])), - (('10PB', 0.2, 6.0), np.array([0.2060, 0.0640, 0.2370])), - (('10PB', 0.2, 8.0), np.array([0.2020, 0.0490, 0.2370])), - (('10PB', 0.2, 10.0), np.array([0.2000, 0.0400, 0.2370])), - (('10PB', 0.2, 12.0), np.array([0.1980, 0.0310, 0.2370])), - (('10PB', 0.2, 14.0), np.array([0.1960, 0.0240, 0.2370])), - (('10PB', 0.2, 16.0), np.array([0.1950, 0.0180, 0.2370])), - (('10PB', 0.2, 18.0), np.array([0.1930, 0.0120, 0.2370])), - (('10PB', 0.2, 20.0), np.array([0.1920, 0.0060, 0.2370])), - (('2.5P', 0.2, 2.0), np.array([0.2500, 0.1270, 0.2370])), - (('2.5P', 0.2, 4.0), np.array([0.2410, 0.0900, 0.2370])), - (('2.5P', 0.2, 6.0), np.array([0.2360, 0.0670, 0.2370])), - (('2.5P', 0.2, 8.0), np.array([0.2320, 0.0520, 0.2370])), - (('2.5P', 0.2, 10.0), np.array([0.2310, 0.0430, 0.2370])), - (('2.5P', 0.2, 12.0), np.array([0.2290, 0.0350, 0.2370])), - (('2.5P', 0.2, 14.0), np.array([0.2280, 0.0280, 0.2370])), - (('2.5P', 0.2, 16.0), np.array([0.2270, 0.0220, 0.2370])), - (('2.5P', 0.2, 18.0), np.array([0.2260, 0.0160, 0.2370])), - (('5P', 0.2, 2.0), np.array([0.2750, 0.1290, 0.2370])), - (('5P', 0.2, 4.0), np.array([0.2690, 0.0930, 0.2370])), - (('5P', 0.2, 6.0), np.array([0.2660, 0.0720, 0.2370])), - (('5P', 0.2, 8.0), np.array([0.2640, 0.0560, 0.2370])), - (('5P', 0.2, 10.0), np.array([0.2630, 0.0470, 0.2370])), - (('5P', 0.2, 12.0), np.array([0.2630, 0.0390, 0.2370])), - (('5P', 0.2, 14.0), np.array([0.2620, 0.0320, 0.2370])), - (('7.5P', 0.2, 2.0), np.array([0.2880, 0.1310, 0.2370])), - (('7.5P', 0.2, 4.0), np.array([0.2830, 0.0940, 0.2370])), - (('7.5P', 0.2, 6.0), np.array([0.2800, 0.0740, 0.2370])), - (('7.5P', 0.2, 8.0), np.array([0.2770, 0.0580, 0.2370])), - (('7.5P', 0.2, 10.0), np.array([0.2760, 0.0490, 0.2370])), - (('7.5P', 0.2, 12.0), np.array([0.2750, 0.0400, 0.2370])), - (('10P', 0.2, 2.0), np.array([0.3000, 0.1340, 0.2370])), - (('10P', 0.2, 4.0), np.array([0.2960, 0.0970, 0.2370])), - (('10P', 0.2, 6.0), np.array([0.2930, 0.0750, 0.2370])), - (('10P', 0.2, 8.0), np.array([0.2910, 0.0600, 0.2370])), - (('10P', 0.2, 10.0), np.array([0.2900, 0.0510, 0.2370])), - (('2.5RP', 0.2, 2.0), np.array([0.3150, 0.1370, 0.2370])), - (('2.5RP', 0.2, 4.0), np.array([0.3130, 0.1000, 0.2370])), - (('2.5RP', 0.2, 6.0), np.array([0.3120, 0.0780, 0.2370])), - (('2.5RP', 0.2, 8.0), np.array([0.3100, 0.0640, 0.2370])), - (('2.5RP', 0.2, 10.0), np.array([0.3100, 0.0530, 0.2370])), - (('5RP', 0.2, 2.0), np.array([0.3370, 0.1430, 0.2370])), - (('5RP', 0.2, 4.0), np.array([0.3410, 0.1060, 0.2370])), - (('5RP', 0.2, 6.0), np.array([0.3420, 0.0840, 0.2370])), - (('5RP', 0.2, 8.0), np.array([0.3420, 0.0690, 0.2370])), - (('7.5RP', 0.2, 2.0), np.array([0.3700, 0.1520, 0.2370])), - (('7.5RP', 0.2, 4.0), np.array([0.3810, 0.1150, 0.2370])), - (('7.5RP', 0.2, 6.0), np.array([0.3880, 0.0930, 0.2370])), - (('7.5RP', 0.2, 8.0), np.array([0.3930, 0.0770, 0.2370])), - (('10RP', 0.2, 2.0), np.array([0.4040, 0.1640, 0.2370])), - (('10RP', 0.2, 4.0), np.array([0.4240, 0.1250, 0.2370])), - (('10RP', 0.2, 6.0), np.array([0.4350, 0.1030, 0.2370])), - (('2.5R', 0.2, 2.0), np.array([0.4510, 0.1830, 0.2370])), - (('2.5R', 0.2, 4.0), np.array([0.4880, 0.1420, 0.2370])), - (('2.5R', 0.2, 6.0), np.array([0.5100, 0.1190, 0.2370])), - (('5R', 0.2, 2.0), np.array([0.5010, 0.2040, 0.2370])), - (('5R', 0.2, 4.0), np.array([0.5560, 0.1590, 0.2370])), - (('7.5R', 0.2, 2.0), np.array([0.5430, 0.2240, 0.2370])), - (('7.5R', 0.2, 4.0), np.array([0.6260, 0.1780, 0.2370])), - (('10R', 0.2, 2.0), np.array([0.5920, 0.2460, 0.2370])), - (('10R', 0.2, 4.0), np.array([0.6960, 0.1980, 0.2370])), - (('2.5YR', 0.2, 2.0), np.array([0.6790, 0.2900, 0.2370])), - (('2.5YR', 0.2, 4.0), np.array([0.8430, 0.2450, 0.2370])), - (('5YR', 0.2, 2.0), np.array([0.8370, 0.3750, 0.2370])), - (('5YR', 0.2, 4.0), np.array([1.1740, 0.3550, 0.2370])), - (('7.5YR', 0.2, 2.0), np.array([1.0000, 0.4800, 0.2370])), - (('10YR', 0.2, 2.0), np.array([1.2900, 0.7400, 0.2370])), - (('2.5Y', 0.2, 2.0), np.array([1.4300, 0.9700, 0.2370])), - (('5Y', 0.2, 2.0), np.array([1.4950, 1.2840, 0.2370])), - (('7.5Y', 0.2, 2.0), np.array([1.4340, 1.4590, 0.2370])), - (('10Y', 0.4, 2.0), np.array([0.5420, 0.6700, 0.4670])), - (('2.5GY', 0.4, 2.0), np.array([0.4230, 0.5900, 0.4670])), - (('2.5GY', 0.4, 4.0), np.array([0.7350, 1.8200, 0.4670])), - (('5GY', 0.4, 2.0), np.array([0.3580, 0.5280, 0.4670])), - (('5GY', 0.4, 4.0), np.array([0.3230, 1.4670, 0.4670])), - (('7.5GY', 0.4, 2.0), np.array([0.3120, 0.4820, 0.4670])), - (('7.5GY', 0.4, 4.0), np.array([0.1280, 1.1410, 0.4670])), - (('10GY', 0.4, 2.0), np.array([0.2770, 0.4450, 0.4670])), - (('10GY', 0.4, 4.0), np.array([0.0410, 0.9060, 0.4670])), - (('2.5G', 0.4, 2.0), np.array([0.2580, 0.4230, 0.4670])), - (('2.5G', 0.4, 4.0), np.array([0.0050, 0.7430, 0.4670])), - (('5G', 0.4, 2.0), np.array([0.2390, 0.3990, 0.4670])), - (('5G', 0.4, 4.0), np.array([-0.0110, 0.6040, 0.4670])), - (('7.5G', 0.4, 2.0), np.array([0.2260, 0.3800, 0.4670])), - (('7.5G', 0.4, 4.0), np.array([-0.0110, 0.5010, 0.4670])), - (('10G', 0.4, 2.0), np.array([0.2130, 0.3610, 0.4670])), - (('10G', 0.4, 4.0), np.array([-0.0040, 0.4050, 0.4670])), - (('2.5BG', 0.4, 2.0), np.array([0.1960, 0.3320, 0.4670])), - (('2.5BG', 0.4, 4.0), np.array([0.0080, 0.3340, 0.4670])), - (('5BG', 0.4, 2.0), np.array([0.1800, 0.2980, 0.4670])), - (('5BG', 0.4, 4.0), np.array([0.0310, 0.2590, 0.4670])), - (('7.5BG', 0.4, 2.0), np.array([0.1730, 0.2750, 0.4670])), - (('7.5BG', 0.4, 4.0), np.array([0.0510, 0.2200, 0.4670])), - (('7.5BG', 0.4, 6.0), np.array([-0.0050, 0.1930, 0.4670])), - (('10BG', 0.4, 2.0), np.array([0.1690, 0.2490, 0.4670])), - (('10BG', 0.4, 4.0), np.array([0.0740, 0.1870, 0.4670])), - (('10BG', 0.4, 6.0), np.array([0.0320, 0.1490, 0.4670])), - (('2.5B', 0.4, 2.0), np.array([0.1690, 0.2360, 0.4670])), - (('2.5B', 0.4, 4.0), np.array([0.0870, 0.1720, 0.4670])), - (('2.5B', 0.4, 6.0), np.array([0.0480, 0.1340, 0.4670])), - (('5B', 0.4, 2.0), np.array([0.1720, 0.2230, 0.4670])), - (('5B', 0.4, 4.0), np.array([0.1020, 0.1590, 0.4670])), - (('5B', 0.4, 6.0), np.array([0.0670, 0.1190, 0.4670])), - (('7.5B', 0.4, 2.0), np.array([0.1760, 0.2130, 0.4670])), - (('7.5B', 0.4, 4.0), np.array([0.1130, 0.1510, 0.4670])), - (('7.5B', 0.4, 6.0), np.array([0.0810, 0.1110, 0.4670])), - (('10B', 0.4, 2.0), np.array([0.1830, 0.2030, 0.4670])), - (('10B', 0.4, 4.0), np.array([0.1260, 0.1450, 0.4670])), - (('10B', 0.4, 6.0), np.array([0.0930, 0.1050, 0.4670])), - (('10B', 0.4, 8.0), np.array([0.0670, 0.0670, 0.4670])), - (('2.5PB', 0.4, 2.0), np.array([0.1900, 0.1960, 0.4670])), - (('2.5PB', 0.4, 4.0), np.array([0.1410, 0.1390, 0.4670])), - (('2.5PB', 0.4, 6.0), np.array([0.1130, 0.0980, 0.4670])), - (('2.5PB', 0.4, 8.0), np.array([0.0930, 0.0650, 0.4670])), - (('5PB', 0.4, 2.0), np.array([0.2020, 0.1880, 0.4670])), - (('5PB', 0.4, 4.0), np.array([0.1610, 0.1340, 0.4670])), - (('5PB', 0.4, 6.0), np.array([0.1350, 0.0950, 0.4670])), - (('5PB', 0.4, 8.0), np.array([0.1160, 0.0660, 0.4670])), - (('5PB', 0.4, 10.0), np.array([0.1070, 0.0530, 0.4670])), - (('5PB', 0.4, 12.0), np.array([0.0990, 0.0420, 0.4670])), - (('5PB', 0.4, 14.0), np.array([0.0960, 0.0360, 0.4670])), - (('5PB', 0.4, 16.0), np.array([0.0940, 0.0300, 0.4670])), - (('5PB', 0.4, 18.0), np.array([0.0910, 0.0250, 0.4670])), - (('5PB', 0.4, 20.0), np.array([0.0860, 0.0180, 0.4670])), - (('7.5PB', 0.4, 2.0), np.array([0.2200, 0.1800, 0.4670])), - (('7.5PB', 0.4, 4.0), np.array([0.1920, 0.1300, 0.4670])), - (('7.5PB', 0.4, 6.0), np.array([0.1750, 0.0950, 0.4670])), - (('7.5PB', 0.4, 8.0), np.array([0.1650, 0.0720, 0.4670])), - (('7.5PB', 0.4, 10.0), np.array([0.1600, 0.0580, 0.4670])), - (('7.5PB', 0.4, 12.0), np.array([0.1550, 0.0460, 0.4670])), - (('7.5PB', 0.4, 14.0), np.array([0.1520, 0.0400, 0.4670])), - (('7.5PB', 0.4, 16.0), np.array([0.1500, 0.0320, 0.4670])), - (('7.5PB', 0.4, 18.0), np.array([0.1470, 0.0260, 0.4670])), - (('7.5PB', 0.4, 20.0), np.array([0.1450, 0.0200, 0.4670])), - (('7.5PB', 0.4, 22.0), np.array([0.1430, 0.0160, 0.4670])), - (('7.5PB', 0.4, 24.0), np.array([0.1410, 0.0100, 0.4670])), - (('7.5PB', 0.4, 26.0), np.array([0.1390, 0.0050, 0.4670])), - (('7.5PB', 0.4, 28.0), np.array([0.1370, 0.0010, 0.4670])), - (('10PB', 0.4, 2.0), np.array([0.2410, 0.1760, 0.4670])), - (('10PB', 0.4, 4.0), np.array([0.2230, 0.1310, 0.4670])), - (('10PB', 0.4, 6.0), np.array([0.2120, 0.1000, 0.4670])), - (('10PB', 0.4, 8.0), np.array([0.2060, 0.0780, 0.4670])), - (('10PB', 0.4, 10.0), np.array([0.2020, 0.0620, 0.4670])), - (('10PB', 0.4, 12.0), np.array([0.1990, 0.0500, 0.4670])), - (('10PB', 0.4, 14.0), np.array([0.1970, 0.0430, 0.4670])), - (('10PB', 0.4, 16.0), np.array([0.1950, 0.0360, 0.4670])), - (('10PB', 0.4, 18.0), np.array([0.1930, 0.0290, 0.4670])), - (('10PB', 0.4, 20.0), np.array([0.1920, 0.0240, 0.4670])), - (('10PB', 0.4, 22.0), np.array([0.1910, 0.0180, 0.4670])), - (('10PB', 0.4, 24.0), np.array([0.1900, 0.0130, 0.4670])), - (('10PB', 0.4, 26.0), np.array([0.1880, 0.0080, 0.4670])), - (('10PB', 0.4, 28.0), np.array([0.1870, 0.0040, 0.4670])), - (('2.5P', 0.4, 2.0), np.array([0.2590, 0.1770, 0.4670])), - (('2.5P', 0.4, 4.0), np.array([0.2460, 0.1340, 0.4670])), - (('2.5P', 0.4, 6.0), np.array([0.2380, 0.1040, 0.4670])), - (('2.5P', 0.4, 8.0), np.array([0.2330, 0.0820, 0.4670])), - (('2.5P', 0.4, 10.0), np.array([0.2300, 0.0640, 0.4670])), - (('2.5P', 0.4, 12.0), np.array([0.2270, 0.0540, 0.4670])), - (('2.5P', 0.4, 14.0), np.array([0.2260, 0.0450, 0.4670])), - (('2.5P', 0.4, 16.0), np.array([0.2240, 0.0380, 0.4670])), - (('2.5P', 0.4, 18.0), np.array([0.2230, 0.0310, 0.4670])), - (('2.5P', 0.4, 20.0), np.array([0.2220, 0.0260, 0.4670])), - (('2.5P', 0.4, 22.0), np.array([0.2200, 0.0200, 0.4670])), - (('2.5P', 0.4, 24.0), np.array([0.2190, 0.0160, 0.4670])), - (('2.5P', 0.4, 26.0), np.array([0.2180, 0.0100, 0.4670])), - (('5P', 0.4, 2.0), np.array([0.2810, 0.1820, 0.4670])), - (('5P', 0.4, 4.0), np.array([0.2720, 0.1380, 0.4670])), - (('5P', 0.4, 6.0), np.array([0.2650, 0.1100, 0.4670])), - (('5P', 0.4, 8.0), np.array([0.2600, 0.0870, 0.4670])), - (('5P', 0.4, 10.0), np.array([0.2570, 0.0690, 0.4670])), - (('5P', 0.4, 12.0), np.array([0.2530, 0.0570, 0.4670])), - (('5P', 0.4, 14.0), np.array([0.2520, 0.0480, 0.4670])), - (('5P', 0.4, 16.0), np.array([0.2500, 0.0400, 0.4670])), - (('5P', 0.4, 18.0), np.array([0.2480, 0.0340, 0.4670])), - (('5P', 0.4, 20.0), np.array([0.2470, 0.0280, 0.4670])), - (('7.5P', 0.4, 2.0), np.array([0.2960, 0.1850, 0.4670])), - (('7.5P', 0.4, 4.0), np.array([0.2890, 0.1420, 0.4670])), - (('7.5P', 0.4, 6.0), np.array([0.2840, 0.1140, 0.4670])), - (('7.5P', 0.4, 8.0), np.array([0.2800, 0.0910, 0.4670])), - (('7.5P', 0.4, 10.0), np.array([0.2760, 0.0720, 0.4670])), - (('7.5P', 0.4, 12.0), np.array([0.2730, 0.0590, 0.4670])), - (('7.5P', 0.4, 14.0), np.array([0.2720, 0.0490, 0.4670])), - (('7.5P', 0.4, 16.0), np.array([0.2700, 0.0420, 0.4670])), - (('7.5P', 0.4, 18.0), np.array([0.2690, 0.0340, 0.4670])), - (('10P', 0.4, 2.0), np.array([0.3090, 0.1890, 0.4670])), - (('10P', 0.4, 4.0), np.array([0.3040, 0.1460, 0.4670])), - (('10P', 0.4, 6.0), np.array([0.3020, 0.1190, 0.4670])), - (('10P', 0.4, 8.0), np.array([0.2980, 0.0950, 0.4670])), - (('10P', 0.4, 10.0), np.array([0.2960, 0.0730, 0.4670])), - (('10P', 0.4, 12.0), np.array([0.2940, 0.0610, 0.4670])), - (('10P', 0.4, 14.0), np.array([0.2930, 0.0520, 0.4670])), - (('10P', 0.4, 16.0), np.array([0.2910, 0.0430, 0.4670])), - (('2.5RP', 0.4, 2.0), np.array([0.3200, 0.1930, 0.4670])), - (('2.5RP', 0.4, 4.0), np.array([0.3200, 0.1510, 0.4670])), - (('2.5RP', 0.4, 6.0), np.array([0.3200, 0.1230, 0.4670])), - (('2.5RP', 0.4, 8.0), np.array([0.3200, 0.1000, 0.4670])), - (('2.5RP', 0.4, 10.0), np.array([0.3200, 0.0780, 0.4670])), - (('2.5RP', 0.4, 12.0), np.array([0.3200, 0.0650, 0.4670])), - (('2.5RP', 0.4, 14.0), np.array([0.3200, 0.0540, 0.4670])), - (('5RP', 0.4, 2.0), np.array([0.3370, 0.1990, 0.4670])), - (('5RP', 0.4, 4.0), np.array([0.3440, 0.1580, 0.4670])), - (('5RP', 0.4, 6.0), np.array([0.3480, 0.1310, 0.4670])), - (('5RP', 0.4, 8.0), np.array([0.3500, 0.1060, 0.4670])), - (('5RP', 0.4, 10.0), np.array([0.3530, 0.0820, 0.4670])), - (('5RP', 0.4, 12.0), np.array([0.3530, 0.0700, 0.4670])), - (('7.5RP', 0.4, 2.0), np.array([0.3600, 0.2090, 0.4670])), - (('7.5RP', 0.4, 4.0), np.array([0.3740, 0.1690, 0.4670])), - (('7.5RP', 0.4, 6.0), np.array([0.3840, 0.1410, 0.4670])), - (('7.5RP', 0.4, 8.0), np.array([0.3910, 0.1170, 0.4670])), - (('7.5RP', 0.4, 10.0), np.array([0.3980, 0.0900, 0.4670])), - (('10RP', 0.4, 2.0), np.array([0.3810, 0.2200, 0.4670])), - (('10RP', 0.4, 4.0), np.array([0.4060, 0.1810, 0.4670])), - (('10RP', 0.4, 6.0), np.array([0.4230, 0.1530, 0.4670])), - (('10RP', 0.4, 8.0), np.array([0.4370, 0.1280, 0.4670])), - (('10RP', 0.4, 10.0), np.array([0.4540, 0.0980, 0.4670])), - (('2.5R', 0.4, 2.0), np.array([0.4110, 0.2360, 0.4670])), - (('2.5R', 0.4, 4.0), np.array([0.4500, 0.1980, 0.4670])), - (('2.5R', 0.4, 6.0), np.array([0.4770, 0.1700, 0.4670])), - (('2.5R', 0.4, 8.0), np.array([0.5010, 0.1430, 0.4670])), - (('2.5R', 0.4, 10.0), np.array([0.5290, 0.1130, 0.4670])), - (('5R', 0.4, 2.0), np.array([0.4410, 0.2550, 0.4670])), - (('5R', 0.4, 4.0), np.array([0.4980, 0.2190, 0.4670])), - (('5R', 0.4, 6.0), np.array([0.5370, 0.1900, 0.4670])), - (('5R', 0.4, 8.0), np.array([0.5750, 0.1610, 0.4670])), - (('7.5R', 0.4, 2.0), np.array([0.4660, 0.2720, 0.4670])), - (('7.5R', 0.4, 4.0), np.array([0.5390, 0.2380, 0.4670])), - (('7.5R', 0.4, 6.0), np.array([0.5880, 0.2080, 0.4670])), - (('7.5R', 0.4, 8.0), np.array([0.6350, 0.1760, 0.4670])), - (('10R', 0.4, 2.0), np.array([0.4900, 0.2890, 0.4670])), - (('10R', 0.4, 4.0), np.array([0.5820, 0.2580, 0.4670])), - (('10R', 0.4, 6.0), np.array([0.6490, 0.2290, 0.4670])), - (('10R', 0.4, 8.0), np.array([0.7060, 0.1960, 0.4670])), - (('2.5YR', 0.4, 2.0), np.array([0.5340, 0.3240, 0.4670])), - (('2.5YR', 0.4, 4.0), np.array([0.6650, 0.2980, 0.4670])), - (('2.5YR', 0.4, 6.0), np.array([0.7550, 0.2700, 0.4670])), - (('2.5YR', 0.4, 8.0), np.array([0.8440, 0.2410, 0.4670])), - (('5YR', 0.4, 2.0), np.array([0.5850, 0.3670, 0.4670])), - (('5YR', 0.4, 4.0), np.array([0.7750, 0.3620, 0.4670])), - (('5YR', 0.4, 6.0), np.array([0.8890, 0.3440, 0.4670])), - (('7.5YR', 0.4, 2.0), np.array([0.6380, 0.4200, 0.4670])), - (('7.5YR', 0.4, 4.0), np.array([0.9080, 0.4520, 0.4670])), - (('10YR', 0.4, 2.0), np.array([0.6980, 0.4990, 0.4670])), - (('2.5Y', 0.4, 2.0), np.array([0.7290, 0.5880, 0.4670])), - (('5Y', 0.4, 2.0), np.array([0.7210, 0.6560, 0.4670])), - (('7.5Y', 0.4, 2.0), np.array([0.6590, 0.7000, 0.4670])), - (('10Y', 0.6, 2.0), np.array([0.4320, 0.5010, 0.6990])), - (('10Y', 0.6, 4.0), np.array([0.9020, 1.2230, 0.6990])), - (('2.5GY', 0.6, 2.0), np.array([0.3770, 0.4680, 0.6990])), - (('2.5GY', 0.6, 4.0), np.array([0.6030, 1.1690, 0.6990])), - (('5GY', 0.6, 2.0), np.array([0.3420, 0.4420, 0.6990])), - (('5GY', 0.6, 4.0), np.array([0.4100, 0.9950, 0.6990])), - (('7.5GY', 0.6, 2.0), np.array([0.3150, 0.4200, 0.6990])), - (('7.5GY', 0.6, 4.0), np.array([0.2740, 0.7920, 0.6990])), - (('7.5GY', 0.6, 6.0), np.array([-0.0450, 1.4590, 0.6990])), - (('10GY', 0.6, 2.0), np.array([0.2920, 0.3990, 0.6990])), - (('10GY', 0.6, 4.0), np.array([0.2080, 0.6520, 0.6990])), - (('10GY', 0.6, 6.0), np.array([-0.1280, 1.1250, 0.6990])), - (('2.5G', 0.6, 2.0), np.array([0.2810, 0.3880, 0.6990])), - (('2.5G', 0.6, 4.0), np.array([0.1750, 0.5610, 0.6990])), - (('2.5G', 0.6, 6.0), np.array([-0.1430, 0.8890, 0.6990])), - (('5G', 0.6, 2.0), np.array([0.2700, 0.3760, 0.6990])), - (('5G', 0.6, 4.0), np.array([0.1520, 0.4930, 0.6990])), - (('5G', 0.6, 6.0), np.array([-0.1280, 0.6820, 0.6990])), - (('7.5G', 0.6, 2.0), np.array([0.2590, 0.3630, 0.6990])), - (('7.5G', 0.6, 4.0), np.array([0.1370, 0.4400, 0.6990])), - (('7.5G', 0.6, 6.0), np.array([-0.1020, 0.5410, 0.6990])), - (('10G', 0.6, 2.0), np.array([0.2470, 0.3490, 0.6990])), - (('10G', 0.6, 4.0), np.array([0.1240, 0.3890, 0.6990])), - (('10G', 0.6, 6.0), np.array([-0.0680, 0.4250, 0.6990])), - (('2.5BG', 0.6, 2.0), np.array([0.2360, 0.3340, 0.6990])), - (('2.5BG', 0.6, 4.0), np.array([0.1170, 0.3410, 0.6990])), - (('2.5BG', 0.6, 6.0), np.array([-0.0360, 0.3340, 0.6990])), - (('5BG', 0.6, 2.0), np.array([0.2210, 0.3110, 0.6990])), - (('5BG', 0.6, 4.0), np.array([0.1120, 0.2840, 0.6990])), - (('5BG', 0.6, 6.0), np.array([0.0090, 0.2430, 0.6990])), - (('7.5BG', 0.6, 2.0), np.array([0.2130, 0.2950, 0.6990])), - (('7.5BG', 0.6, 4.0), np.array([0.1130, 0.2540, 0.6990])), - (('7.5BG', 0.6, 6.0), np.array([0.0300, 0.2110, 0.6990])), - (('10BG', 0.6, 2.0), np.array([0.2060, 0.2760, 0.6990])), - (('10BG', 0.6, 4.0), np.array([0.1160, 0.2210, 0.6990])), - (('10BG', 0.6, 6.0), np.array([0.0520, 0.1760, 0.6990])), - (('2.5B', 0.6, 2.0), np.array([0.2020, 0.2600, 0.6990])), - (('2.5B', 0.6, 4.0), np.array([0.1230, 0.2020, 0.6990])), - (('2.5B', 0.6, 6.0), np.array([0.0710, 0.1570, 0.6990])), - (('2.5B', 0.6, 8.0), np.array([0.0170, 0.1110, 0.6990])), - (('5B', 0.6, 2.0), np.array([0.2020, 0.2450, 0.6990])), - (('5B', 0.6, 4.0), np.array([0.1340, 0.1870, 0.6990])), - (('5B', 0.6, 6.0), np.array([0.0880, 0.1450, 0.6990])), - (('5B', 0.6, 8.0), np.array([0.0440, 0.1000, 0.6990])), - (('7.5B', 0.6, 2.0), np.array([0.2040, 0.2350, 0.6990])), - (('7.5B', 0.6, 4.0), np.array([0.1430, 0.1780, 0.6990])), - (('7.5B', 0.6, 6.0), np.array([0.0990, 0.1360, 0.6990])), - (('7.5B', 0.6, 8.0), np.array([0.0600, 0.0960, 0.6990])), - (('10B', 0.6, 2.0), np.array([0.2090, 0.2270, 0.6990])), - (('10B', 0.6, 4.0), np.array([0.1530, 0.1720, 0.6990])), - (('10B', 0.6, 6.0), np.array([0.1150, 0.1280, 0.6990])), - (('10B', 0.6, 8.0), np.array([0.0840, 0.0910, 0.6990])), - (('2.5PB', 0.6, 2.0), np.array([0.2150, 0.2210, 0.6990])), - (('2.5PB', 0.6, 4.0), np.array([0.1660, 0.1650, 0.6990])), - (('2.5PB', 0.6, 6.0), np.array([0.1310, 0.1220, 0.6990])), - (('2.5PB', 0.6, 8.0), np.array([0.1060, 0.0900, 0.6990])), - (('2.5PB', 0.6, 10.0), np.array([0.0890, 0.0680, 0.6990])), - (('2.5PB', 0.6, 12.0), np.array([0.0790, 0.0550, 0.6990])), - (('5PB', 0.6, 2.0), np.array([0.2230, 0.2150, 0.6990])), - (('5PB', 0.6, 4.0), np.array([0.1820, 0.1600, 0.6990])), - (('5PB', 0.6, 6.0), np.array([0.1520, 0.1180, 0.6990])), - (('5PB', 0.6, 8.0), np.array([0.1310, 0.0880, 0.6990])), - (('5PB', 0.6, 10.0), np.array([0.1180, 0.0690, 0.6990])), - (('5PB', 0.6, 12.0), np.array([0.1100, 0.0570, 0.6990])), - (('5PB', 0.6, 14.0), np.array([0.1040, 0.0480, 0.6990])), - (('5PB', 0.6, 16.0), np.array([0.0990, 0.0400, 0.6990])), - (('5PB', 0.6, 18.0), np.array([0.0930, 0.0320, 0.6990])), - (('5PB', 0.6, 20.0), np.array([0.0880, 0.0240, 0.6990])), - (('5PB', 0.6, 22.0), np.array([0.0840, 0.0180, 0.6990])), - (('7.5PB', 0.6, 2.0), np.array([0.2390, 0.2080, 0.6990])), - (('7.5PB', 0.6, 4.0), np.array([0.2080, 0.1550, 0.6990])), - (('7.5PB', 0.6, 6.0), np.array([0.1880, 0.1170, 0.6990])), - (('7.5PB', 0.6, 8.0), np.array([0.1760, 0.0920, 0.6990])), - (('7.5PB', 0.6, 10.0), np.array([0.1680, 0.0740, 0.6990])), - (('7.5PB', 0.6, 12.0), np.array([0.1630, 0.0620, 0.6990])), - (('7.5PB', 0.6, 14.0), np.array([0.1590, 0.0530, 0.6990])), - (('7.5PB', 0.6, 16.0), np.array([0.1560, 0.0450, 0.6990])), - (('7.5PB', 0.6, 18.0), np.array([0.1530, 0.0370, 0.6990])), - (('7.5PB', 0.6, 20.0), np.array([0.1500, 0.0300, 0.6990])), - (('7.5PB', 0.6, 22.0), np.array([0.1480, 0.0250, 0.6990])), - (('7.5PB', 0.6, 24.0), np.array([0.1460, 0.0200, 0.6990])), - (('7.5PB', 0.6, 26.0), np.array([0.1440, 0.0140, 0.6990])), - (('7.5PB', 0.6, 28.0), np.array([0.1420, 0.0080, 0.6990])), - (('7.5PB', 0.6, 30.0), np.array([0.1400, 0.0040, 0.6990])), - (('10PB', 0.6, 2.0), np.array([0.2570, 0.2040, 0.6990])), - (('10PB', 0.6, 4.0), np.array([0.2370, 0.1570, 0.6990])), - (('10PB', 0.6, 6.0), np.array([0.2250, 0.1240, 0.6990])), - (('10PB', 0.6, 8.0), np.array([0.2160, 0.0980, 0.6990])), - (('10PB', 0.6, 10.0), np.array([0.2110, 0.0820, 0.6990])), - (('10PB', 0.6, 12.0), np.array([0.2080, 0.0700, 0.6990])), - (('10PB', 0.6, 14.0), np.array([0.2040, 0.0580, 0.6990])), - (('10PB', 0.6, 16.0), np.array([0.2020, 0.0500, 0.6990])), - (('10PB', 0.6, 18.0), np.array([0.2000, 0.0420, 0.6990])), - (('10PB', 0.6, 20.0), np.array([0.1980, 0.0350, 0.6990])), - (('10PB', 0.6, 22.0), np.array([0.1960, 0.0290, 0.6990])), - (('10PB', 0.6, 24.0), np.array([0.1950, 0.0240, 0.6990])), - (('10PB', 0.6, 26.0), np.array([0.1940, 0.0180, 0.6990])), - (('10PB', 0.6, 28.0), np.array([0.1920, 0.0120, 0.6990])), - (('10PB', 0.6, 30.0), np.array([0.1900, 0.0060, 0.6990])), - (('2.5P', 0.6, 2.0), np.array([0.2720, 0.2050, 0.6990])), - (('2.5P', 0.6, 4.0), np.array([0.2580, 0.1600, 0.6990])), - (('2.5P', 0.6, 6.0), np.array([0.2500, 0.1290, 0.6990])), - (('2.5P', 0.6, 8.0), np.array([0.2440, 0.1040, 0.6990])), - (('2.5P', 0.6, 10.0), np.array([0.2400, 0.0880, 0.6990])), - (('2.5P', 0.6, 12.0), np.array([0.2380, 0.0750, 0.6990])), - (('2.5P', 0.6, 14.0), np.array([0.2350, 0.0600, 0.6990])), - (('2.5P', 0.6, 16.0), np.array([0.2330, 0.0530, 0.6990])), - (('2.5P', 0.6, 18.0), np.array([0.2320, 0.0440, 0.6990])), - (('2.5P', 0.6, 20.0), np.array([0.2300, 0.0380, 0.6990])), - (('2.5P', 0.6, 22.0), np.array([0.2290, 0.0320, 0.6990])), - (('2.5P', 0.6, 24.0), np.array([0.2280, 0.0270, 0.6990])), - (('2.5P', 0.6, 26.0), np.array([0.2270, 0.0210, 0.6990])), - (('2.5P', 0.6, 28.0), np.array([0.2260, 0.0140, 0.6990])), - (('5P', 0.6, 2.0), np.array([0.2870, 0.2070, 0.6990])), - (('5P', 0.6, 4.0), np.array([0.2800, 0.1660, 0.6990])), - (('5P', 0.6, 6.0), np.array([0.2740, 0.1360, 0.6990])), - (('5P', 0.6, 8.0), np.array([0.2700, 0.1100, 0.6990])), - (('5P', 0.6, 10.0), np.array([0.2680, 0.0940, 0.6990])), - (('5P', 0.6, 12.0), np.array([0.2660, 0.0800, 0.6990])), - (('5P', 0.6, 14.0), np.array([0.2640, 0.0650, 0.6990])), - (('5P', 0.6, 16.0), np.array([0.2630, 0.0560, 0.6990])), - (('5P', 0.6, 18.0), np.array([0.2620, 0.0460, 0.6990])), - (('5P', 0.6, 20.0), np.array([0.2610, 0.0410, 0.6990])), - (('5P', 0.6, 22.0), np.array([0.2600, 0.0340, 0.6990])), - (('5P', 0.6, 24.0), np.array([0.2600, 0.0300, 0.6990])), - (('7.5P', 0.6, 2.0), np.array([0.3010, 0.2110, 0.6990])), - (('7.5P', 0.6, 4.0), np.array([0.2960, 0.1700, 0.6990])), - (('7.5P', 0.6, 6.0), np.array([0.2920, 0.1410, 0.6990])), - (('7.5P', 0.6, 8.0), np.array([0.2880, 0.1150, 0.6990])), - (('7.5P', 0.6, 10.0), np.array([0.2840, 0.0980, 0.6990])), - (('7.5P', 0.6, 12.0), np.array([0.2810, 0.0830, 0.6990])), - (('7.5P', 0.6, 14.0), np.array([0.2790, 0.0670, 0.6990])), - (('7.5P', 0.6, 16.0), np.array([0.2770, 0.0560, 0.6990])), - (('7.5P', 0.6, 18.0), np.array([0.2760, 0.0490, 0.6990])), - (('7.5P', 0.6, 20.0), np.array([0.2750, 0.0420, 0.6990])), - (('10P', 0.6, 2.0), np.array([0.3110, 0.2140, 0.6990])), - (('10P', 0.6, 4.0), np.array([0.3080, 0.1740, 0.6990])), - (('10P', 0.6, 6.0), np.array([0.3060, 0.1450, 0.6990])), - (('10P', 0.6, 8.0), np.array([0.3040, 0.1190, 0.6990])), - (('10P', 0.6, 10.0), np.array([0.3020, 0.1010, 0.6990])), - (('10P', 0.6, 12.0), np.array([0.3010, 0.0870, 0.6990])), - (('10P', 0.6, 14.0), np.array([0.2990, 0.0690, 0.6990])), - (('10P', 0.6, 16.0), np.array([0.2980, 0.0600, 0.6990])), - (('10P', 0.6, 18.0), np.array([0.2970, 0.0510, 0.6990])), - (('2.5RP', 0.6, 2.0), np.array([0.3220, 0.2180, 0.6990])), - (('2.5RP', 0.6, 4.0), np.array([0.3240, 0.1790, 0.6990])), - (('2.5RP', 0.6, 6.0), np.array([0.3250, 0.1510, 0.6990])), - (('2.5RP', 0.6, 8.0), np.array([0.3260, 0.1250, 0.6990])), - (('2.5RP', 0.6, 10.0), np.array([0.3260, 0.1060, 0.6990])), - (('2.5RP', 0.6, 12.0), np.array([0.3260, 0.0920, 0.6990])), - (('2.5RP', 0.6, 14.0), np.array([0.3260, 0.0730, 0.6990])), - (('2.5RP', 0.6, 16.0), np.array([0.3250, 0.0620, 0.6990])), - (('5RP', 0.6, 2.0), np.array([0.3370, 0.2260, 0.6990])), - (('5RP', 0.6, 4.0), np.array([0.3470, 0.1890, 0.6990])), - (('5RP', 0.6, 6.0), np.array([0.3540, 0.1590, 0.6990])), - (('5RP', 0.6, 8.0), np.array([0.3590, 0.1350, 0.6990])), - (('5RP', 0.6, 10.0), np.array([0.3630, 0.1140, 0.6990])), - (('5RP', 0.6, 12.0), np.array([0.3660, 0.0990, 0.6990])), - (('5RP', 0.6, 14.0), np.array([0.3700, 0.0800, 0.6990])), - (('5RP', 0.6, 16.0), np.array([0.3730, 0.0680, 0.6990])), - (('7.5RP', 0.6, 2.0), np.array([0.3550, 0.2360, 0.6990])), - (('7.5RP', 0.6, 4.0), np.array([0.3730, 0.2000, 0.6990])), - (('7.5RP', 0.6, 6.0), np.array([0.3870, 0.1700, 0.6990])), - (('7.5RP', 0.6, 8.0), np.array([0.3970, 0.1460, 0.6990])), - (('7.5RP', 0.6, 10.0), np.array([0.4060, 0.1240, 0.6990])), - (('7.5RP', 0.6, 12.0), np.array([0.4130, 0.1060, 0.6990])), - (('7.5RP', 0.6, 14.0), np.array([0.4210, 0.0870, 0.6990])), - (('10RP', 0.6, 2.0), np.array([0.3720, 0.2470, 0.6990])), - (('10RP', 0.6, 4.0), np.array([0.3990, 0.2110, 0.6990])), - (('10RP', 0.6, 6.0), np.array([0.4190, 0.1820, 0.6990])), - (('10RP', 0.6, 8.0), np.array([0.4340, 0.1580, 0.6990])), - (('10RP', 0.6, 10.0), np.array([0.4470, 0.1350, 0.6990])), - (('10RP', 0.6, 12.0), np.array([0.4590, 0.1140, 0.6990])), - (('2.5R', 0.6, 2.0), np.array([0.3910, 0.2600, 0.6990])), - (('2.5R', 0.6, 4.0), np.array([0.4320, 0.2270, 0.6990])), - (('2.5R', 0.6, 6.0), np.array([0.4640, 0.2000, 0.6990])), - (('2.5R', 0.6, 8.0), np.array([0.4890, 0.1760, 0.6990])), - (('2.5R', 0.6, 10.0), np.array([0.5110, 0.1540, 0.6990])), - (('2.5R', 0.6, 12.0), np.array([0.5370, 0.1260, 0.6990])), - (('5R', 0.6, 2.0), np.array([0.4110, 0.2740, 0.6990])), - (('5R', 0.6, 4.0), np.array([0.4690, 0.2460, 0.6990])), - (('5R', 0.6, 6.0), np.array([0.5140, 0.2210, 0.6990])), - (('5R', 0.6, 8.0), np.array([0.5510, 0.1970, 0.6990])), - (('5R', 0.6, 10.0), np.array([0.5860, 0.1760, 0.6990])), - (('7.5R', 0.6, 2.0), np.array([0.4310, 0.2900, 0.6990])), - (('7.5R', 0.6, 4.0), np.array([0.5020, 0.2640, 0.6990])), - (('7.5R', 0.6, 6.0), np.array([0.5580, 0.2400, 0.6990])), - (('7.5R', 0.6, 8.0), np.array([0.6040, 0.2140, 0.6990])), - (('7.5R', 0.6, 10.0), np.array([0.6400, 0.1920, 0.6990])), - (('10R', 0.6, 2.0), np.array([0.4470, 0.3050, 0.6990])), - (('10R', 0.6, 4.0), np.array([0.5370, 0.2840, 0.6990])), - (('10R', 0.6, 6.0), np.array([0.6050, 0.2610, 0.6990])), - (('10R', 0.6, 8.0), np.array([0.6600, 0.2350, 0.6990])), - (('10R', 0.6, 10.0), np.array([0.7040, 0.2140, 0.6990])), - (('2.5YR', 0.6, 2.0), np.array([0.4740, 0.3320, 0.6990])), - (('2.5YR', 0.6, 4.0), np.array([0.6030, 0.3220, 0.6990])), - (('2.5YR', 0.6, 6.0), np.array([0.6930, 0.3030, 0.6990])), - (('2.5YR', 0.6, 8.0), np.array([0.7870, 0.2820, 0.6990])), - (('2.5YR', 0.6, 10.0), np.array([0.8460, 0.2660, 0.6990])), - (('5YR', 0.6, 2.0), np.array([0.5050, 0.3670, 0.6990])), - (('5YR', 0.6, 4.0), np.array([0.6730, 0.3710, 0.6990])), - (('5YR', 0.6, 6.0), np.array([0.8050, 0.3620, 0.6990])), - (('5YR', 0.6, 8.0), np.array([0.9300, 0.3470, 0.6990])), - (('7.5YR', 0.6, 2.0), np.array([0.5260, 0.3970, 0.6990])), - (('7.5YR', 0.6, 4.0), np.array([0.7640, 0.4410, 0.6990])), - (('10YR', 0.6, 2.0), np.array([0.5510, 0.4440, 0.6990])), - (('10YR', 0.6, 4.0), np.array([0.8990, 0.5870, 0.6990])), - (('2.5Y', 0.6, 2.0), np.array([0.5630, 0.4910, 0.6990])), - (('2.5Y', 0.6, 4.0), np.array([0.9950, 0.7340, 0.6990])), - (('5Y', 0.6, 2.0), np.array([0.5480, 0.5260, 0.6990])), - (('7.5Y', 0.6, 2.0), np.array([0.5020, 0.5310, 0.6990])), - (('7.5Y', 0.6, 4.0), np.array([1.0440, 1.0740, 0.6990])), - (('10Y', 0.8, 2.0), np.array([0.3970, 0.4480, 0.9430])), - (('10Y', 0.8, 4.0), np.array([0.7610, 0.9980, 0.9430])), - (('2.5GY', 0.8, 2.0), np.array([0.3630, 0.4250, 0.9430])), - (('2.5GY', 0.8, 4.0), np.array([0.5560, 0.9070, 0.9430])), - (('5GY', 0.8, 2.0), np.array([0.3360, 0.4100, 0.9430])), - (('5GY', 0.8, 4.0), np.array([0.4000, 0.7380, 0.9430])), - (('5GY', 0.8, 6.0), np.array([0.4250, 1.2660, 0.9430])), - (('7.5GY', 0.8, 2.0), np.array([0.3140, 0.3940, 0.9430])), - (('7.5GY', 0.8, 4.0), np.array([0.3050, 0.6130, 0.9430])), - (('7.5GY', 0.8, 6.0), np.array([0.2370, 0.9690, 0.9430])), - (('7.5GY', 0.8, 8.0), np.array([-0.1190, 1.6320, 0.9430])), - (('10GY', 0.8, 2.0), np.array([0.2980, 0.3810, 0.9430])), - (('10GY', 0.8, 4.0), np.array([0.2540, 0.5370, 0.9430])), - (('10GY', 0.8, 6.0), np.array([0.1500, 0.7910, 0.9430])), - (('10GY', 0.8, 8.0), np.array([-0.1560, 1.2200, 0.9430])), - (('2.5G', 0.8, 2.0), np.array([0.2870, 0.3710, 0.9430])), - (('2.5G', 0.8, 4.0), np.array([0.2250, 0.4880, 0.9430])), - (('2.5G', 0.8, 6.0), np.array([0.1020, 0.6600, 0.9430])), - (('2.5G', 0.8, 8.0), np.array([-0.2130, 0.9670, 0.9430])), - (('5G', 0.8, 2.0), np.array([0.2800, 0.3630, 0.9430])), - (('5G', 0.8, 4.0), np.array([0.2050, 0.4470, 0.9430])), - (('5G', 0.8, 6.0), np.array([0.0820, 0.5530, 0.9430])), - (('5G', 0.8, 8.0), np.array([-0.1850, 0.7370, 0.9430])), - (('7.5G', 0.8, 2.0), np.array([0.2720, 0.3550, 0.9430])), - (('7.5G', 0.8, 4.0), np.array([0.1910, 0.4140, 0.9430])), - (('7.5G', 0.8, 6.0), np.array([0.0730, 0.4760, 0.9430])), - (('7.5G', 0.8, 8.0), np.array([-0.1430, 0.5610, 0.9430])), - (('10G', 0.8, 2.0), np.array([0.2650, 0.3460, 0.9430])), - (('10G', 0.8, 4.0), np.array([0.1780, 0.3820, 0.9430])), - (('10G', 0.8, 6.0), np.array([0.0700, 0.4080, 0.9430])), - (('10G', 0.8, 8.0), np.array([-0.1000, 0.4280, 0.9430])), - (('2.5BG', 0.8, 2.0), np.array([0.2530, 0.3320, 0.9430])), - (('2.5BG', 0.8, 4.0), np.array([0.1630, 0.3420, 0.9430])), - (('2.5BG', 0.8, 6.0), np.array([0.0700, 0.3410, 0.9430])), - (('2.5BG', 0.8, 8.0), np.array([-0.0620, 0.3300, 0.9430])), - (('5BG', 0.8, 2.0), np.array([0.2410, 0.3150, 0.9430])), - (('5BG', 0.8, 4.0), np.array([0.1500, 0.2990, 0.9430])), - (('5BG', 0.8, 6.0), np.array([0.0720, 0.2750, 0.9430])), - (('5BG', 0.8, 8.0), np.array([-0.0180, 0.2390, 0.9430])), - (('7.5BG', 0.8, 2.0), np.array([0.2300, 0.2960, 0.9430])), - (('7.5BG', 0.8, 4.0), np.array([0.1450, 0.2640, 0.9430])), - (('7.5BG', 0.8, 6.0), np.array([0.0770, 0.2330, 0.9430])), - (('7.5BG', 0.8, 8.0), np.array([0.0080, 0.1980, 0.9430])), - (('10BG', 0.8, 2.0), np.array([0.2230, 0.2800, 0.9430])), - (('10BG', 0.8, 4.0), np.array([0.1460, 0.2370, 0.9430])), - (('10BG', 0.8, 6.0), np.array([0.0860, 0.1990, 0.9430])), - (('10BG', 0.8, 8.0), np.array([0.0330, 0.1640, 0.9430])), - (('2.5B', 0.8, 2.0), np.array([0.2200, 0.2710, 0.9430])), - (('2.5B', 0.8, 4.0), np.array([0.1490, 0.2220, 0.9430])), - (('2.5B', 0.8, 6.0), np.array([0.0940, 0.1810, 0.9430])), - (('2.5B', 0.8, 8.0), np.array([0.0480, 0.1470, 0.9430])), - (('5B', 0.8, 2.0), np.array([0.2180, 0.2580, 0.9430])), - (('5B', 0.8, 4.0), np.array([0.1540, 0.2070, 0.9430])), - (('5B', 0.8, 6.0), np.array([0.1060, 0.1630, 0.9430])), - (('5B', 0.8, 8.0), np.array([0.0690, 0.1270, 0.9430])), - (('7.5B', 0.8, 2.0), np.array([0.2200, 0.2490, 0.9430])), - (('7.5B', 0.8, 4.0), np.array([0.1600, 0.1960, 0.9430])), - (('7.5B', 0.8, 6.0), np.array([0.1150, 0.1530, 0.9430])), - (('7.5B', 0.8, 8.0), np.array([0.0820, 0.1200, 0.9430])), - (('7.5B', 0.8, 10.0), np.array([0.0490, 0.0890, 0.9430])), - (('10B', 0.8, 2.0), np.array([0.2220, 0.2410, 0.9430])), - (('10B', 0.8, 4.0), np.array([0.1680, 0.1870, 0.9430])), - (('10B', 0.8, 6.0), np.array([0.1280, 0.1450, 0.9430])), - (('10B', 0.8, 8.0), np.array([0.0970, 0.1120, 0.9430])), - (('10B', 0.8, 10.0), np.array([0.0710, 0.0830, 0.9430])), - (('2.5PB', 0.8, 2.0), np.array([0.2250, 0.2340, 0.9430])), - (('2.5PB', 0.8, 4.0), np.array([0.1780, 0.1810, 0.9430])), - (('2.5PB', 0.8, 6.0), np.array([0.1420, 0.1380, 0.9430])), - (('2.5PB', 0.8, 8.0), np.array([0.1170, 0.1050, 0.9430])), - (('2.5PB', 0.8, 10.0), np.array([0.0980, 0.0810, 0.9430])), - (('2.5PB', 0.8, 12.0), np.array([0.0820, 0.0580, 0.9430])), - (('5PB', 0.8, 2.0), np.array([0.2340, 0.2260, 0.9430])), - (('5PB', 0.8, 4.0), np.array([0.1920, 0.1740, 0.9430])), - (('5PB', 0.8, 6.0), np.array([0.1600, 0.1320, 0.9430])), - (('5PB', 0.8, 8.0), np.array([0.1390, 0.1020, 0.9430])), - (('5PB', 0.8, 10.0), np.array([0.1230, 0.0830, 0.9430])), - (('5PB', 0.8, 12.0), np.array([0.1090, 0.0600, 0.9430])), - (('5PB', 0.8, 14.0), np.array([0.0990, 0.0450, 0.9430])), - (('5PB', 0.8, 16.0), np.array([0.0920, 0.0340, 0.9430])), - (('5PB', 0.8, 18.0), np.array([0.0870, 0.0260, 0.9430])), - (('5PB', 0.8, 20.0), np.array([0.0810, 0.0180, 0.9430])), - (('7.5PB', 0.8, 2.0), np.array([0.2470, 0.2210, 0.9430])), - (('7.5PB', 0.8, 4.0), np.array([0.2160, 0.1700, 0.9430])), - (('7.5PB', 0.8, 6.0), np.array([0.1940, 0.1310, 0.9430])), - (('7.5PB', 0.8, 8.0), np.array([0.1790, 0.1040, 0.9430])), - (('7.5PB', 0.8, 10.0), np.array([0.1700, 0.0890, 0.9430])), - (('7.5PB', 0.8, 12.0), np.array([0.1620, 0.0720, 0.9430])), - (('7.5PB', 0.8, 14.0), np.array([0.1550, 0.0560, 0.9430])), - (('7.5PB', 0.8, 16.0), np.array([0.1500, 0.0460, 0.9430])), - (('7.5PB', 0.8, 18.0), np.array([0.1470, 0.0390, 0.9430])), - (('7.5PB', 0.8, 20.0), np.array([0.1440, 0.0320, 0.9430])), - (('7.5PB', 0.8, 22.0), np.array([0.1400, 0.0250, 0.9430])), - (('7.5PB', 0.8, 24.0), np.array([0.1370, 0.0180, 0.9430])), - (('7.5PB', 0.8, 26.0), np.array([0.1350, 0.0130, 0.9430])), - (('7.5PB', 0.8, 28.0), np.array([0.1330, 0.0090, 0.9430])), - (('7.5PB', 0.8, 30.0), np.array([0.1310, 0.0050, 0.9430])), - (('7.5PB', 0.8, 32.0), np.array([0.1300, 0.0020, 0.9430])), - (('7.5PB', 0.8, 34.0), np.array([0.1290, -0.0010, 0.9430])), - (('7.5PB', 0.8, 36.0), np.array([0.1280, -0.0040, 0.9430])), - (('10PB', 0.8, 2.0), np.array([0.2630, 0.2190, 0.9430])), - (('10PB', 0.8, 4.0), np.array([0.2420, 0.1700, 0.9430])), - (('10PB', 0.8, 6.0), np.array([0.2290, 0.1370, 0.9430])), - (('10PB', 0.8, 8.0), np.array([0.2200, 0.1120, 0.9430])), - (('10PB', 0.8, 10.0), np.array([0.2150, 0.0970, 0.9430])), - (('10PB', 0.8, 12.0), np.array([0.2100, 0.0810, 0.9430])), - (('10PB', 0.8, 14.0), np.array([0.2060, 0.0680, 0.9430])), - (('10PB', 0.8, 16.0), np.array([0.2030, 0.0570, 0.9430])), - (('10PB', 0.8, 18.0), np.array([0.2000, 0.0500, 0.9430])), - (('10PB', 0.8, 20.0), np.array([0.1990, 0.0430, 0.9430])), - (('10PB', 0.8, 22.0), np.array([0.1970, 0.0360, 0.9430])), - (('10PB', 0.8, 24.0), np.array([0.1960, 0.0300, 0.9430])), - (('10PB', 0.8, 26.0), np.array([0.1940, 0.0260, 0.9430])), - (('10PB', 0.8, 28.0), np.array([0.1930, 0.0210, 0.9430])), - (('10PB', 0.8, 30.0), np.array([0.1920, 0.0180, 0.9430])), - (('10PB', 0.8, 32.0), np.array([0.1910, 0.0140, 0.9430])), - (('10PB', 0.8, 34.0), np.array([0.1900, 0.0100, 0.9430])), - (('10PB', 0.8, 36.0), np.array([0.1890, 0.0080, 0.9430])), - (('2.5P', 0.8, 2.0), np.array([0.2770, 0.2200, 0.9430])), - (('2.5P', 0.8, 4.0), np.array([0.2640, 0.1740, 0.9430])), - (('2.5P', 0.8, 6.0), np.array([0.2550, 0.1440, 0.9430])), - (('2.5P', 0.8, 8.0), np.array([0.2480, 0.1200, 0.9430])), - (('2.5P', 0.8, 10.0), np.array([0.2450, 0.1050, 0.9430])), - (('2.5P', 0.8, 12.0), np.array([0.2420, 0.0900, 0.9430])), - (('2.5P', 0.8, 14.0), np.array([0.2380, 0.0760, 0.9430])), - (('2.5P', 0.8, 16.0), np.array([0.2360, 0.0640, 0.9430])), - (('2.5P', 0.8, 18.0), np.array([0.2350, 0.0570, 0.9430])), - (('2.5P', 0.8, 20.0), np.array([0.2330, 0.0500, 0.9430])), - (('2.5P', 0.8, 22.0), np.array([0.2320, 0.0440, 0.9430])), - (('2.5P', 0.8, 24.0), np.array([0.2310, 0.0380, 0.9430])), - (('2.5P', 0.8, 26.0), np.array([0.2300, 0.0340, 0.9430])), - (('2.5P', 0.8, 28.0), np.array([0.2280, 0.0280, 0.9430])), - (('2.5P', 0.8, 30.0), np.array([0.2280, 0.0260, 0.9430])), - (('2.5P', 0.8, 32.0), np.array([0.2270, 0.0220, 0.9430])), - (('2.5P', 0.8, 34.0), np.array([0.2270, 0.0190, 0.9430])), - (('2.5P', 0.8, 36.0), np.array([0.2270, 0.0150, 0.9430])), - (('5P', 0.8, 2.0), np.array([0.2920, 0.2240, 0.9430])), - (('5P', 0.8, 4.0), np.array([0.2830, 0.1790, 0.9430])), - (('5P', 0.8, 6.0), np.array([0.2790, 0.1510, 0.9430])), - (('5P', 0.8, 8.0), np.array([0.2750, 0.1270, 0.9430])), - (('5P', 0.8, 10.0), np.array([0.2690, 0.1120, 0.9430])), - (('5P', 0.8, 12.0), np.array([0.2660, 0.0950, 0.9430])), - (('5P', 0.8, 14.0), np.array([0.2630, 0.0800, 0.9430])), - (('5P', 0.8, 16.0), np.array([0.2620, 0.0690, 0.9430])), - (('5P', 0.8, 18.0), np.array([0.2600, 0.0620, 0.9430])), - (('5P', 0.8, 20.0), np.array([0.2580, 0.0560, 0.9430])), - (('5P', 0.8, 22.0), np.array([0.2570, 0.0490, 0.9430])), - (('5P', 0.8, 24.0), np.array([0.2560, 0.0440, 0.9430])), - (('5P', 0.8, 26.0), np.array([0.2560, 0.0390, 0.9430])), - (('5P', 0.8, 28.0), np.array([0.2550, 0.0350, 0.9430])), - (('5P', 0.8, 30.0), np.array([0.2540, 0.0300, 0.9430])), - (('7.5P', 0.8, 2.0), np.array([0.3040, 0.2280, 0.9430])), - (('7.5P', 0.8, 4.0), np.array([0.2980, 0.1840, 0.9430])), - (('7.5P', 0.8, 6.0), np.array([0.2940, 0.1560, 0.9430])), - (('7.5P', 0.8, 8.0), np.array([0.2910, 0.1320, 0.9430])), - (('7.5P', 0.8, 10.0), np.array([0.2900, 0.1180, 0.9430])), - (('7.5P', 0.8, 12.0), np.array([0.2870, 0.1010, 0.9430])), - (('7.5P', 0.8, 14.0), np.array([0.2840, 0.0840, 0.9430])), - (('7.5P', 0.8, 16.0), np.array([0.2830, 0.0720, 0.9430])), - (('7.5P', 0.8, 18.0), np.array([0.2810, 0.0660, 0.9430])), - (('7.5P', 0.8, 20.0), np.array([0.2800, 0.0600, 0.9430])), - (('7.5P', 0.8, 22.0), np.array([0.2790, 0.0540, 0.9430])), - (('7.5P', 0.8, 24.0), np.array([0.2780, 0.0490, 0.9430])), - (('7.5P', 0.8, 26.0), np.array([0.2780, 0.0440, 0.9430])), - (('10P', 0.8, 2.0), np.array([0.3120, 0.2320, 0.9430])), - (('10P', 0.8, 4.0), np.array([0.3100, 0.1890, 0.9430])), - (('10P', 0.8, 6.0), np.array([0.3090, 0.1620, 0.9430])), - (('10P', 0.8, 8.0), np.array([0.3080, 0.1370, 0.9430])), - (('10P', 0.8, 10.0), np.array([0.3070, 0.1230, 0.9430])), - (('10P', 0.8, 12.0), np.array([0.3060, 0.1060, 0.9430])), - (('10P', 0.8, 14.0), np.array([0.3040, 0.0890, 0.9430])), - (('10P', 0.8, 16.0), np.array([0.3030, 0.0780, 0.9430])), - (('10P', 0.8, 18.0), np.array([0.3020, 0.0710, 0.9430])), - (('10P', 0.8, 20.0), np.array([0.3020, 0.0650, 0.9430])), - (('10P', 0.8, 22.0), np.array([0.3020, 0.0590, 0.9430])), - (('10P', 0.8, 24.0), np.array([0.3020, 0.0530, 0.9430])), - (('2.5RP', 0.8, 2.0), np.array([0.3220, 0.2360, 0.9430])), - (('2.5RP', 0.8, 4.0), np.array([0.3260, 0.1950, 0.9430])), - (('2.5RP', 0.8, 6.0), np.array([0.3280, 0.1680, 0.9430])), - (('2.5RP', 0.8, 8.0), np.array([0.3290, 0.1440, 0.9430])), - (('2.5RP', 0.8, 10.0), np.array([0.3290, 0.1290, 0.9430])), - (('2.5RP', 0.8, 12.0), np.array([0.3300, 0.1110, 0.9430])), - (('2.5RP', 0.8, 14.0), np.array([0.3300, 0.0930, 0.9430])), - (('2.5RP', 0.8, 16.0), np.array([0.3310, 0.0840, 0.9430])), - (('2.5RP', 0.8, 18.0), np.array([0.3310, 0.0770, 0.9430])), - (('2.5RP', 0.8, 20.0), np.array([0.3310, 0.0700, 0.9430])), - (('2.5RP', 0.8, 22.0), np.array([0.3310, 0.0650, 0.9430])), - (('5RP', 0.8, 2.0), np.array([0.3360, 0.2430, 0.9430])), - (('5RP', 0.8, 4.0), np.array([0.3470, 0.2060, 0.9430])), - (('5RP', 0.8, 6.0), np.array([0.3550, 0.1790, 0.9430])), - (('5RP', 0.8, 8.0), np.array([0.3620, 0.1540, 0.9430])), - (('5RP', 0.8, 10.0), np.array([0.3660, 0.1390, 0.9430])), - (('5RP', 0.8, 12.0), np.array([0.3700, 0.1200, 0.9430])), - (('5RP', 0.8, 14.0), np.array([0.3730, 0.1050, 0.9430])), - (('5RP', 0.8, 16.0), np.array([0.3760, 0.0940, 0.9430])), - (('5RP', 0.8, 18.0), np.array([0.3780, 0.0860, 0.9430])), - (('5RP', 0.8, 20.0), np.array([0.3790, 0.0810, 0.9430])), - (('7.5RP', 0.8, 2.0), np.array([0.3500, 0.2510, 0.9430])), - (('7.5RP', 0.8, 4.0), np.array([0.3690, 0.2170, 0.9430])), - (('7.5RP', 0.8, 6.0), np.array([0.3840, 0.1900, 0.9430])), - (('7.5RP', 0.8, 8.0), np.array([0.3970, 0.1650, 0.9430])), - (('7.5RP', 0.8, 10.0), np.array([0.4050, 0.1490, 0.9430])), - (('7.5RP', 0.8, 12.0), np.array([0.4130, 0.1320, 0.9430])), - (('7.5RP', 0.8, 14.0), np.array([0.4210, 0.1150, 0.9430])), - (('7.5RP', 0.8, 16.0), np.array([0.4270, 0.1040, 0.9430])), - (('10RP', 0.8, 2.0), np.array([0.3650, 0.2610, 0.9430])), - (('10RP', 0.8, 4.0), np.array([0.3930, 0.2300, 0.9430])), - (('10RP', 0.8, 6.0), np.array([0.4150, 0.2030, 0.9430])), - (('10RP', 0.8, 8.0), np.array([0.4350, 0.1770, 0.9430])), - (('10RP', 0.8, 10.0), np.array([0.4470, 0.1600, 0.9430])), - (('10RP', 0.8, 12.0), np.array([0.4590, 0.1430, 0.9430])), - (('10RP', 0.8, 14.0), np.array([0.4700, 0.1270, 0.9430])), - (('2.5R', 0.8, 2.0), np.array([0.3810, 0.2720, 0.9430])), - (('2.5R', 0.8, 4.0), np.array([0.4210, 0.2450, 0.9430])), - (('2.5R', 0.8, 6.0), np.array([0.4550, 0.2190, 0.9430])), - (('2.5R', 0.8, 8.0), np.array([0.4830, 0.1950, 0.9430])), - (('2.5R', 0.8, 10.0), np.array([0.5020, 0.1770, 0.9430])), - (('2.5R', 0.8, 12.0), np.array([0.5210, 0.1590, 0.9430])), - (('2.5R', 0.8, 14.0), np.array([0.5360, 0.1430, 0.9430])), - (('5R', 0.8, 2.0), np.array([0.3990, 0.2860, 0.9430])), - (('5R', 0.8, 4.0), np.array([0.4500, 0.2610, 0.9430])), - (('5R', 0.8, 6.0), np.array([0.4960, 0.2370, 0.9430])), - (('5R', 0.8, 8.0), np.array([0.5360, 0.2140, 0.9430])), - (('5R', 0.8, 10.0), np.array([0.5650, 0.1970, 0.9430])), - (('5R', 0.8, 12.0), np.array([0.5910, 0.1800, 0.9430])), - (('7.5R', 0.8, 2.0), np.array([0.4110, 0.2970, 0.9430])), - (('7.5R', 0.8, 4.0), np.array([0.4770, 0.2760, 0.9430])), - (('7.5R', 0.8, 6.0), np.array([0.5340, 0.2550, 0.9430])), - (('7.5R', 0.8, 8.0), np.array([0.5840, 0.2340, 0.9430])), - (('7.5R', 0.8, 10.0), np.array([0.6240, 0.2160, 0.9430])), - (('7.5R', 0.8, 12.0), np.array([0.6600, 0.1990, 0.9430])), - (('10R', 0.8, 2.0), np.array([0.4230, 0.3090, 0.9430])), - (('10R', 0.8, 4.0), np.array([0.5080, 0.2960, 0.9430])), - (('10R', 0.8, 6.0), np.array([0.5780, 0.2800, 0.9430])), - (('10R', 0.8, 8.0), np.array([0.6350, 0.2590, 0.9430])), - (('10R', 0.8, 10.0), np.array([0.6850, 0.2400, 0.9430])), - (('10R', 0.8, 12.0), np.array([0.7250, 0.2180, 0.9430])), - (('2.5YR', 0.8, 2.0), np.array([0.4450, 0.3330, 0.9430])), - (('2.5YR', 0.8, 4.0), np.array([0.5580, 0.3300, 0.9430])), - (('2.5YR', 0.8, 6.0), np.array([0.6370, 0.3200, 0.9430])), - (('2.5YR', 0.8, 8.0), np.array([0.7150, 0.3060, 0.9430])), - (('2.5YR', 0.8, 10.0), np.array([0.8020, 0.2880, 0.9430])), - (('2.5YR', 0.8, 12.0), np.array([0.8750, 0.2730, 0.9430])), - (('5YR', 0.8, 2.0), np.array([0.4630, 0.3610, 0.9430])), - (('5YR', 0.8, 4.0), np.array([0.6120, 0.3760, 0.9430])), - (('5YR', 0.8, 6.0), np.array([0.7210, 0.3760, 0.9430])), - (('5YR', 0.8, 8.0), np.array([0.8260, 0.3720, 0.9430])), - (('7.5YR', 0.8, 2.0), np.array([0.4750, 0.3860, 0.9430])), - (('7.5YR', 0.8, 4.0), np.array([0.6630, 0.4300, 0.9430])), - (('7.5YR', 0.8, 6.0), np.array([0.8170, 0.4500, 0.9430])), - (('10YR', 0.8, 2.0), np.array([0.4810, 0.4110, 0.9430])), - (('10YR', 0.8, 4.0), np.array([0.7490, 0.5340, 0.9430])), - (('2.5Y', 0.8, 2.0), np.array([0.4790, 0.4390, 0.9430])), - (('2.5Y', 0.8, 4.0), np.array([0.8200, 0.6400, 0.9430])), - (('5Y', 0.8, 2.0), np.array([0.4650, 0.4570, 0.9430])), - (('5Y', 0.8, 4.0), np.array([0.8910, 0.7900, 0.9430])), - (('7.5Y', 0.8, 2.0), np.array([0.4340, 0.4600, 0.9430])), - (('7.5Y', 0.8, 4.0), np.array([0.8800, 0.9360, 0.9430])), - (('10Y', 1.0, 2.0), np.array([0.3802, 0.4212, 1.2100])), - (('10Y', 1.0, 4.0), np.array([0.5010, 0.6000, 1.2100])), - (('2.5GY', 1.0, 2.0), np.array([0.3540, 0.4088, 1.2100])), - (('2.5GY', 1.0, 4.0), np.array([0.4390, 0.6150, 1.2100])), - (('2.5GY', 1.0, 6.0), np.array([0.4800, 0.7230, 1.2100])), - (('5GY', 1.0, 2.0), np.array([0.3359, 0.3982, 1.2100])), - (('5GY', 1.0, 4.0), np.array([0.3765, 0.5942, 1.2100])), - (('5GY', 1.0, 6.0), np.array([0.3980, 0.7400, 1.2100])), - (('7.5GY', 1.0, 2.0), np.array([0.3154, 0.3840, 1.2100])), - (('7.5GY', 1.0, 4.0), np.array([0.3133, 0.5380, 1.2100])), - (('7.5GY', 1.0, 6.0), np.array([0.2900, 0.7060, 1.2100])), - (('7.5GY', 1.0, 8.0), np.array([0.2350, 0.9440, 1.2100])), - (('10GY', 1.0, 2.0), np.array([0.3006, 0.3720, 1.2100])), - (('10GY', 1.0, 4.0), np.array([0.2722, 0.4903, 1.2100])), - (('10GY', 1.0, 6.0), np.array([0.2232, 0.6392, 1.2100])), - (('10GY', 1.0, 8.0), np.array([0.1100, 0.8830, 1.2100])), - (('10GY', 1.0, 10.0), np.array([-0.0130, 1.0650, 1.2100])), - (('2.5G', 1.0, 2.0), np.array([0.2910, 0.3634, 1.2100])), - (('2.5G', 1.0, 4.0), np.array([0.2454, 0.4489, 1.2100])), - (('2.5G', 1.0, 6.0), np.array([0.1711, 0.5619, 1.2100])), - (('2.5G', 1.0, 8.0), np.array([0.0620, 0.6896, 1.2100])), - (('2.5G', 1.0, 10.0), np.array([-0.0220, 0.7760, 1.2100])), - (('5G', 1.0, 2.0), np.array([0.2833, 0.3564, 1.2100])), - (('5G', 1.0, 4.0), np.array([0.2290, 0.4218, 1.2100])), - (('5G', 1.0, 6.0), np.array([0.1468, 0.4996, 1.2100])), - (('5G', 1.0, 8.0), np.array([0.0559, 0.5710, 1.2100])), - (('5G', 1.0, 10.0), np.array([-0.0200, 0.6200, 1.2100])), - (('7.5G', 1.0, 2.0), np.array([0.2758, 0.3484, 1.2100])), - (('7.5G', 1.0, 4.0), np.array([0.2159, 0.3967, 1.2100])), - (('7.5G', 1.0, 6.0), np.array([0.1344, 0.4505, 1.2100])), - (('7.5G', 1.0, 8.0), np.array([0.0530, 0.4943, 1.2100])), - (('7.5G', 1.0, 10.0), np.array([-0.0200, 0.5280, 1.2100])), - (('10G', 1.0, 2.0), np.array([0.2689, 0.3407, 1.2100])), - (('10G', 1.0, 4.0), np.array([0.2040, 0.3724, 1.2100])), - (('10G', 1.0, 6.0), np.array([0.1249, 0.4019, 1.2100])), - (('10G', 1.0, 8.0), np.array([0.0511, 0.4158, 1.2100])), - (('10G', 1.0, 10.0), np.array([-0.0180, 0.4240, 1.2100])), - (('2.5BG', 1.0, 2.0), np.array([0.2600, 0.3289, 1.2100])), - (('2.5BG', 1.0, 4.0), np.array([0.1883, 0.3406, 1.2100])), - (('2.5BG', 1.0, 6.0), np.array([0.1169, 0.3452, 1.2100])), - (('2.5BG', 1.0, 8.0), np.array([0.0476, 0.3458, 1.2100])), - (('2.5BG', 1.0, 10.0), np.array([-0.0170, 0.3440, 1.2100])), - (('5BG', 1.0, 2.0), np.array([0.2500, 0.3141, 1.2100])), - (('5BG', 1.0, 4.0), np.array([0.1753, 0.3021, 1.2100])), - (('5BG', 1.0, 6.0), np.array([0.1093, 0.2860, 1.2100])), - (('5BG', 1.0, 8.0), np.array([0.0460, 0.2640, 1.2100])), - (('5BG', 1.0, 10.0), np.array([-0.0140, 0.2370, 1.2100])), - (('7.5BG', 1.0, 2.0), np.array([0.2430, 0.3023, 1.2100])), - (('7.5BG', 1.0, 4.0), np.array([0.1702, 0.2768, 1.2100])), - (('7.5BG', 1.0, 6.0), np.array([0.1059, 0.2485, 1.2100])), - (('7.5BG', 1.0, 8.0), np.array([0.0500, 0.2180, 1.2100])), - (('10BG', 1.0, 2.0), np.array([0.2362, 0.2882, 1.2100])), - (('10BG', 1.0, 4.0), np.array([0.1658, 0.2496, 1.2100])), - (('10BG', 1.0, 6.0), np.array([0.1074, 0.2129, 1.2100])), - (('10BG', 1.0, 8.0), np.array([0.0600, 0.1800, 1.2100])), - (('2.5B', 1.0, 2.0), np.array([0.2322, 0.2781, 1.2100])), - (('2.5B', 1.0, 4.0), np.array([0.1649, 0.2324, 1.2100])), - (('2.5B', 1.0, 6.0), np.array([0.1118, 0.1908, 1.2100])), - (('2.5B', 1.0, 8.0), np.array([0.0710, 0.1550, 1.2100])), - (('5B', 1.0, 2.0), np.array([0.2291, 0.2677, 1.2100])), - (('5B', 1.0, 4.0), np.array([0.1667, 0.2168, 1.2100])), - (('5B', 1.0, 6.0), np.array([0.1212, 0.1745, 1.2100])), - (('5B', 1.0, 8.0), np.array([0.0840, 0.1390, 1.2100])), - (('5B', 1.0, 10.0), np.array([0.0550, 0.1030, 1.2100])), - (('7.5B', 1.0, 2.0), np.array([0.2291, 0.2579, 1.2100])), - (('7.5B', 1.0, 4.0), np.array([0.1716, 0.2048, 1.2100])), - (('7.5B', 1.0, 6.0), np.array([0.1303, 0.1639, 1.2100])), - (('7.5B', 1.0, 8.0), np.array([0.0968, 0.1280, 1.2100])), - (('7.5B', 1.0, 10.0), np.array([0.0710, 0.0970, 1.2100])), - (('10B', 1.0, 2.0), np.array([0.2309, 0.2491, 1.2100])), - (('10B', 1.0, 4.0), np.array([0.1783, 0.1974, 1.2100])), - (('10B', 1.0, 6.0), np.array([0.1392, 0.1563, 1.2100])), - (('10B', 1.0, 8.0), np.array([0.1077, 0.1218, 1.2100])), - (('10B', 1.0, 10.0), np.array([0.0840, 0.0940, 1.2100])), - (('2.5PB', 1.0, 2.0), np.array([0.2360, 0.2420, 1.2100])), - (('2.5PB', 1.0, 4.0), np.array([0.1895, 0.1911, 1.2100])), - (('2.5PB', 1.0, 6.0), np.array([0.1539, 0.1491, 1.2100])), - (('2.5PB', 1.0, 8.0), np.array([0.1273, 0.1157, 1.2100])), - (('2.5PB', 1.0, 10.0), np.array([0.1060, 0.0900, 1.2100])), - (('2.5PB', 1.0, 12.0), np.array([0.0910, 0.0680, 1.2100])), - (('2.5PB', 1.0, 14.0), np.array([0.0780, 0.0560, 1.2100])), - (('5PB', 1.0, 2.0), np.array([0.2427, 0.2368, 1.2100])), - (('5PB', 1.0, 4.0), np.array([0.2012, 0.1867, 1.2100])), - (('5PB', 1.0, 6.0), np.array([0.1678, 0.1447, 1.2100])), - (('5PB', 1.0, 8.0), np.array([0.1447, 0.1124, 1.2100])), - (('5PB', 1.0, 10.0), np.array([0.1285, 0.0870, 1.2100])), - (('5PB', 1.0, 12.0), np.array([0.1170, 0.0700, 1.2100])), - (('5PB', 1.0, 14.0), np.array([0.1120, 0.0600, 1.2100])), - (('5PB', 1.0, 16.0), np.array([0.1060, 0.0500, 1.2100])), - (('5PB', 1.0, 18.0), np.array([0.1040, 0.0440, 1.2100])), - (('5PB', 1.0, 20.0), np.array([0.1000, 0.0380, 1.2100])), - (('5PB', 1.0, 22.0), np.array([0.0980, 0.0340, 1.2100])), - (('5PB', 1.0, 24.0), np.array([0.0960, 0.0300, 1.2100])), - (('5PB', 1.0, 26.0), np.array([0.0950, 0.0270, 1.2100])), - (('5PB', 1.0, 28.0), np.array([0.0940, 0.0240, 1.2100])), - (('5PB', 1.0, 30.0), np.array([0.0930, 0.0220, 1.2100])), - (('5PB', 1.0, 32.0), np.array([0.0920, 0.0200, 1.2100])), - (('5PB', 1.0, 34.0), np.array([0.0910, 0.0180, 1.2100])), - (('5PB', 1.0, 36.0), np.array([0.0900, 0.0160, 1.2100])), - (('5PB', 1.0, 38.0), np.array([0.0890, 0.0140, 1.2100])), - (('5PB', 1.0, 40.0), np.array([0.0880, 0.0120, 1.2100])), - (('5PB', 1.0, 42.0), np.array([0.0880, 0.0100, 1.2100])), - (('5PB', 1.0, 44.0), np.array([0.0870, 0.0080, 1.2100])), - (('7.5PB', 1.0, 2.0), np.array([0.2547, 0.2310, 1.2100])), - (('7.5PB', 1.0, 4.0), np.array([0.2232, 0.1821, 1.2100])), - (('7.5PB', 1.0, 6.0), np.array([0.2000, 0.1422, 1.2100])), - (('7.5PB', 1.0, 8.0), np.array([0.1872, 0.1141, 1.2100])), - (('7.5PB', 1.0, 10.0), np.array([0.1804, 0.0950, 1.2100])), - (('7.5PB', 1.0, 12.0), np.array([0.1763, 0.0804, 1.2100])), - (('7.5PB', 1.0, 14.0), np.array([0.1738, 0.0688, 1.2100])), - (('7.5PB', 1.0, 16.0), np.array([0.1720, 0.0583, 1.2100])), - (('7.5PB', 1.0, 18.0), np.array([0.1709, 0.0518, 1.2100])), - (('7.5PB', 1.0, 20.0), np.array([0.1701, 0.0454, 1.2100])), - (('7.5PB', 1.0, 22.0), np.array([0.1696, 0.0402, 1.2100])), - (('7.5PB', 1.0, 24.0), np.array([0.1691, 0.0352, 1.2100])), - (('7.5PB', 1.0, 26.0), np.array([0.1689, 0.0309, 1.2100])), - (('7.5PB', 1.0, 28.0), np.array([0.1686, 0.0270, 1.2100])), - (('7.5PB', 1.0, 30.0), np.array([0.1684, 0.0234, 1.2100])), - (('7.5PB', 1.0, 32.0), np.array([0.1682, 0.0202, 1.2100])), - (('7.5PB', 1.0, 34.0), np.array([0.1682, 0.0180, 1.2100])), - (('7.5PB', 1.0, 36.0), np.array([0.1681, 0.0160, 1.2100])), - (('7.5PB', 1.0, 38.0), np.array([0.1680, 0.0140, 1.2100])), - (('7.5PB', 1.0, 40.0), np.array([0.1680, 0.0120, 1.2100])), - (('7.5PB', 1.0, 42.0), np.array([0.1680, 0.0100, 1.2100])), - (('7.5PB', 1.0, 44.0), np.array([0.1680, 0.0080, 1.2100])), - (('7.5PB', 1.0, 46.0), np.array([0.1680, 0.0060, 1.2100])), - (('7.5PB', 1.0, 48.0), np.array([0.1680, 0.0040, 1.2100])), - (('10PB', 1.0, 2.0), np.array([0.2677, 0.2280, 1.2100])), - (('10PB', 1.0, 4.0), np.array([0.2459, 0.1828, 1.2100])), - (('10PB', 1.0, 6.0), np.array([0.2290, 0.1470, 1.2100])), - (('10PB', 1.0, 8.0), np.array([0.2190, 0.1228, 1.2100])), - (('10PB', 1.0, 10.0), np.array([0.2120, 0.1029, 1.2100])), - (('10PB', 1.0, 12.0), np.array([0.2070, 0.0869, 1.2100])), - (('10PB', 1.0, 14.0), np.array([0.2038, 0.0745, 1.2100])), - (('10PB', 1.0, 16.0), np.array([0.2008, 0.0638, 1.2100])), - (('10PB', 1.0, 18.0), np.array([0.1991, 0.0564, 1.2100])), - (('10PB', 1.0, 20.0), np.array([0.1976, 0.0493, 1.2100])), - (('10PB', 1.0, 22.0), np.array([0.1965, 0.0436, 1.2100])), - (('10PB', 1.0, 24.0), np.array([0.1952, 0.0380, 1.2100])), - (('10PB', 1.0, 26.0), np.array([0.1942, 0.0326, 1.2100])), - (('10PB', 1.0, 28.0), np.array([0.1936, 0.0281, 1.2100])), - (('10PB', 1.0, 30.0), np.array([0.1928, 0.0240, 1.2100])), - (('10PB', 1.0, 32.0), np.array([0.1930, 0.0210, 1.2100])), - (('10PB', 1.0, 34.0), np.array([0.1930, 0.0190, 1.2100])), - (('10PB', 1.0, 36.0), np.array([0.1930, 0.0160, 1.2100])), - (('10PB', 1.0, 38.0), np.array([0.1930, 0.0140, 1.2100])), - (('10PB', 1.0, 40.0), np.array([0.1930, 0.0130, 1.2100])), - (('10PB', 1.0, 42.0), np.array([0.1920, 0.0110, 1.2100])), - (('10PB', 1.0, 44.0), np.array([0.1920, 0.0090, 1.2100])), - (('10PB', 1.0, 46.0), np.array([0.1920, 0.0070, 1.2100])), - (('10PB', 1.0, 48.0), np.array([0.1920, 0.0050, 1.2100])), - (('2.5P', 1.0, 2.0), np.array([0.2808, 0.2296, 1.2100])), - (('2.5P', 1.0, 4.0), np.array([0.2668, 0.1874, 1.2100])), - (('2.5P', 1.0, 6.0), np.array([0.2570, 0.1559, 1.2100])), - (('2.5P', 1.0, 8.0), np.array([0.2496, 0.1303, 1.2100])), - (('2.5P', 1.0, 10.0), np.array([0.2441, 0.1112, 1.2100])), - (('2.5P', 1.0, 12.0), np.array([0.2394, 0.0940, 1.2100])), - (('2.5P', 1.0, 14.0), np.array([0.2361, 0.0810, 1.2100])), - (('2.5P', 1.0, 16.0), np.array([0.2331, 0.0696, 1.2100])), - (('2.5P', 1.0, 18.0), np.array([0.2312, 0.0618, 1.2100])), - (('2.5P', 1.0, 20.0), np.array([0.2295, 0.0542, 1.2100])), - (('2.5P', 1.0, 22.0), np.array([0.2279, 0.0473, 1.2100])), - (('2.5P', 1.0, 24.0), np.array([0.2266, 0.0418, 1.2100])), - (('2.5P', 1.0, 26.0), np.array([0.2251, 0.0355, 1.2100])), - (('2.5P', 1.0, 28.0), np.array([0.2250, 0.0310, 1.2100])), - (('2.5P', 1.0, 30.0), np.array([0.2240, 0.0260, 1.2100])), - (('2.5P', 1.0, 32.0), np.array([0.2240, 0.0220, 1.2100])), - (('2.5P', 1.0, 34.0), np.array([0.2230, 0.0200, 1.2100])), - (('2.5P', 1.0, 36.0), np.array([0.2220, 0.0180, 1.2100])), - (('2.5P', 1.0, 38.0), np.array([0.2220, 0.0150, 1.2100])), - (('2.5P', 1.0, 40.0), np.array([0.2220, 0.0130, 1.2100])), - (('2.5P', 1.0, 42.0), np.array([0.2220, 0.0110, 1.2100])), - (('5P', 1.0, 2.0), np.array([0.2936, 0.2330, 1.2100])), - (('5P', 1.0, 4.0), np.array([0.2854, 0.1927, 1.2100])), - (('5P', 1.0, 6.0), np.array([0.2794, 0.1628, 1.2100])), - (('5P', 1.0, 8.0), np.array([0.2742, 0.1375, 1.2100])), - (('5P', 1.0, 10.0), np.array([0.2701, 0.1178, 1.2100])), - (('5P', 1.0, 12.0), np.array([0.2670, 0.1006, 1.2100])), - (('5P', 1.0, 14.0), np.array([0.2645, 0.0863, 1.2100])), - (('5P', 1.0, 16.0), np.array([0.2625, 0.0746, 1.2100])), - (('5P', 1.0, 18.0), np.array([0.2612, 0.0667, 1.2100])), - (('5P', 1.0, 20.0), np.array([0.2601, 0.0586, 1.2100])), - (('5P', 1.0, 22.0), np.array([0.2590, 0.0509, 1.2100])), - (('5P', 1.0, 24.0), np.array([0.2580, 0.0460, 1.2100])), - (('5P', 1.0, 26.0), np.array([0.2580, 0.0390, 1.2100])), - (('5P', 1.0, 28.0), np.array([0.2570, 0.0340, 1.2100])), - (('5P', 1.0, 30.0), np.array([0.2560, 0.0280, 1.2100])), - (('5P', 1.0, 32.0), np.array([0.2560, 0.0240, 1.2100])), - (('7.5P', 1.0, 2.0), np.array([0.3030, 0.2361, 1.2100])), - (('7.5P', 1.0, 4.0), np.array([0.2991, 0.1974, 1.2100])), - (('7.5P', 1.0, 6.0), np.array([0.2960, 0.1682, 1.2100])), - (('7.5P', 1.0, 8.0), np.array([0.2932, 0.1429, 1.2100])), - (('7.5P', 1.0, 10.0), np.array([0.2905, 0.1229, 1.2100])), - (('7.5P', 1.0, 12.0), np.array([0.2884, 0.1059, 1.2100])), - (('7.5P', 1.0, 14.0), np.array([0.2868, 0.0903, 1.2100])), - (('7.5P', 1.0, 16.0), np.array([0.2852, 0.0790, 1.2100])), - (('7.5P', 1.0, 18.0), np.array([0.2841, 0.0706, 1.2100])), - (('7.5P', 1.0, 20.0), np.array([0.2831, 0.0625, 1.2100])), - (('7.5P', 1.0, 22.0), np.array([0.2820, 0.0550, 1.2100])), - (('7.5P', 1.0, 24.0), np.array([0.2820, 0.0490, 1.2100])), - (('7.5P', 1.0, 26.0), np.array([0.2810, 0.0420, 1.2100])), - (('10P', 1.0, 2.0), np.array([0.3132, 0.2404, 1.2100])), - (('10P', 1.0, 4.0), np.array([0.3132, 0.2032, 1.2100])), - (('10P', 1.0, 6.0), np.array([0.3126, 0.1737, 1.2100])), - (('10P', 1.0, 8.0), np.array([0.3114, 0.1481, 1.2100])), - (('10P', 1.0, 10.0), np.array([0.3102, 0.1282, 1.2100])), - (('10P', 1.0, 12.0), np.array([0.3094, 0.1110, 1.2100])), - (('10P', 1.0, 14.0), np.array([0.3084, 0.0952, 1.2100])), - (('10P', 1.0, 16.0), np.array([0.3078, 0.0839, 1.2100])), - (('10P', 1.0, 18.0), np.array([0.3069, 0.0748, 1.2100])), - (('10P', 1.0, 20.0), np.array([0.3070, 0.0660, 1.2100])), - (('10P', 1.0, 22.0), np.array([0.3060, 0.0580, 1.2100])), - (('10P', 1.0, 24.0), np.array([0.3050, 0.0510, 1.2100])), - (('2.5RP', 1.0, 2.0), np.array([0.3240, 0.2459, 1.2100])), - (('2.5RP', 1.0, 4.0), np.array([0.3290, 0.2095, 1.2100])), - (('2.5RP', 1.0, 6.0), np.array([0.3321, 0.1811, 1.2100])), - (('2.5RP', 1.0, 8.0), np.array([0.3342, 0.1551, 1.2100])), - (('2.5RP', 1.0, 10.0), np.array([0.3354, 0.1351, 1.2100])), - (('2.5RP', 1.0, 12.0), np.array([0.3361, 0.1181, 1.2100])), - (('2.5RP', 1.0, 14.0), np.array([0.3368, 0.1020, 1.2100])), - (('2.5RP', 1.0, 16.0), np.array([0.3368, 0.0902, 1.2100])), - (('2.5RP', 1.0, 18.0), np.array([0.3370, 0.0800, 1.2100])), - (('2.5RP', 1.0, 20.0), np.array([0.3360, 0.0700, 1.2100])), - (('2.5RP', 1.0, 22.0), np.array([0.3360, 0.0610, 1.2100])), - (('5RP', 1.0, 2.0), np.array([0.3378, 0.2542, 1.2100])), - (('5RP', 1.0, 4.0), np.array([0.3503, 0.2196, 1.2100])), - (('5RP', 1.0, 6.0), np.array([0.3588, 0.1920, 1.2100])), - (('5RP', 1.0, 8.0), np.array([0.3660, 0.1662, 1.2100])), - (('5RP', 1.0, 10.0), np.array([0.3727, 0.1458, 1.2100])), - (('5RP', 1.0, 12.0), np.array([0.3772, 0.1283, 1.2100])), - (('5RP', 1.0, 14.0), np.array([0.3811, 0.1138, 1.2100])), - (('5RP', 1.0, 16.0), np.array([0.3840, 0.1020, 1.2100])), - (('5RP', 1.0, 18.0), np.array([0.3860, 0.0920, 1.2100])), - (('5RP', 1.0, 20.0), np.array([0.3890, 0.0790, 1.2100])), - (('7.5RP', 1.0, 2.0), np.array([0.3498, 0.2617, 1.2100])), - (('7.5RP', 1.0, 4.0), np.array([0.3705, 0.2300, 1.2100])), - (('7.5RP', 1.0, 6.0), np.array([0.3865, 0.2036, 1.2100])), - (('7.5RP', 1.0, 8.0), np.array([0.4005, 0.1793, 1.2100])), - (('7.5RP', 1.0, 10.0), np.array([0.4132, 0.1580, 1.2100])), - (('7.5RP', 1.0, 12.0), np.array([0.4240, 0.1400, 1.2100])), - (('7.5RP', 1.0, 14.0), np.array([0.4330, 0.1250, 1.2100])), - (('7.5RP', 1.0, 16.0), np.array([0.4400, 0.1130, 1.2100])), - (('10RP', 1.0, 2.0), np.array([0.3629, 0.2710, 1.2100])), - (('10RP', 1.0, 4.0), np.array([0.3920, 0.2423, 1.2100])), - (('10RP', 1.0, 6.0), np.array([0.4151, 0.2169, 1.2100])), - (('10RP', 1.0, 8.0), np.array([0.4357, 0.1921, 1.2100])), - (('10RP', 1.0, 10.0), np.array([0.4521, 0.1710, 1.2100])), - (('10RP', 1.0, 12.0), np.array([0.4668, 0.1514, 1.2100])), - (('10RP', 1.0, 14.0), np.array([0.4790, 0.1360, 1.2100])), - (('10RP', 1.0, 16.0), np.array([0.4880, 0.1240, 1.2100])), - (('2.5R', 1.0, 2.0), np.array([0.3768, 0.2816, 1.2100])), - (('2.5R', 1.0, 4.0), np.array([0.4166, 0.2569, 1.2100])), - (('2.5R', 1.0, 6.0), np.array([0.4515, 0.2329, 1.2100])), - (('2.5R', 1.0, 8.0), np.array([0.4812, 0.2103, 1.2100])), - (('2.5R', 1.0, 10.0), np.array([0.5058, 0.1900, 1.2100])), - (('2.5R', 1.0, 12.0), np.array([0.5330, 0.1690, 1.2100])), - (('2.5R', 1.0, 14.0), np.array([0.5480, 0.1560, 1.2100])), - (('5R', 1.0, 2.0), np.array([0.3908, 0.2929, 1.2100])), - (('5R', 1.0, 4.0), np.array([0.4420, 0.2728, 1.2100])), - (('5R', 1.0, 6.0), np.array([0.4885, 0.2515, 1.2100])), - (('5R', 1.0, 8.0), np.array([0.5282, 0.2297, 1.2100])), - (('5R', 1.0, 10.0), np.array([0.5604, 0.2100, 1.2100])), - (('5R', 1.0, 12.0), np.array([0.5950, 0.1870, 1.2100])), - (('7.5R', 1.0, 2.0), np.array([0.4020, 0.3034, 1.2100])), - (('7.5R', 1.0, 4.0), np.array([0.4660, 0.2888, 1.2100])), - (('7.5R', 1.0, 6.0), np.array([0.5235, 0.2698, 1.2100])), - (('7.5R', 1.0, 8.0), np.array([0.5722, 0.2487, 1.2100])), - (('7.5R', 1.0, 10.0), np.array([0.6111, 0.2290, 1.2100])), - (('7.5R', 1.0, 12.0), np.array([0.6540, 0.2040, 1.2100])), - (('10R', 1.0, 2.0), np.array([0.4128, 0.3154, 1.2100])), - (('10R', 1.0, 4.0), np.array([0.4933, 0.3068, 1.2100])), - (('10R', 1.0, 6.0), np.array([0.5584, 0.2921, 1.2100])), - (('10R', 1.0, 8.0), np.array([0.6178, 0.2713, 1.2100])), - (('10R', 1.0, 10.0), np.array([0.6661, 0.2499, 1.2100])), - (('10R', 1.0, 12.0), np.array([0.7110, 0.2270, 1.2100])), - (('2.5YR', 1.0, 2.0), np.array([0.4258, 0.3344, 1.2100])), - (('2.5YR', 1.0, 4.0), np.array([0.5311, 0.3371, 1.2100])), - (('2.5YR', 1.0, 6.0), np.array([0.6048, 0.3270, 1.2100])), - (('2.5YR', 1.0, 8.0), np.array([0.6721, 0.3058, 1.2100])), - (('2.5YR', 1.0, 10.0), np.array([0.7270, 0.2790, 1.2100])), - (('2.5YR', 1.0, 12.0), np.array([0.7780, 0.2480, 1.2100])), - (('5YR', 1.0, 2.0), np.array([0.4377, 0.3580, 1.2100])), - (('5YR', 1.0, 4.0), np.array([0.5660, 0.3795, 1.2100])), - (('5YR', 1.0, 6.0), np.array([0.6560, 0.3840, 1.2100])), - (('5YR', 1.0, 8.0), np.array([0.7360, 0.3850, 1.2100])), - (('5YR', 1.0, 10.0), np.array([0.8290, 0.3850, 1.2100])), - (('7.5YR', 1.0, 2.0), np.array([0.4430, 0.3775, 1.2100])), - (('7.5YR', 1.0, 4.0), np.array([0.5850, 0.4220, 1.2100])), - (('7.5YR', 1.0, 6.0), np.array([0.6850, 0.4420, 1.2100])), - (('10YR', 1.0, 2.0), np.array([0.4446, 0.3982, 1.2100])), - (('10YR', 1.0, 4.0), np.array([0.5910, 0.4640, 1.2100])), - (('2.5Y', 1.0, 2.0), np.array([0.4362, 0.4177, 1.2100])), - (('2.5Y', 1.0, 4.0), np.array([0.5810, 0.5050, 1.2100])), - (('5Y', 1.0, 2.0), np.array([0.4230, 0.4265, 1.2100])), - (('5Y', 1.0, 4.0), np.array([0.5650, 0.5430, 1.2100])), - (('7.5Y', 1.0, 2.0), np.array([0.4042, 0.4287, 1.2100])), - (('7.5Y', 1.0, 4.0), np.array([0.5430, 0.5700, 1.2100])), - (('10Y', 2.0, 2.0), np.array([0.3556, 0.3848, 3.1260])), - (('10Y', 2.0, 4.0), np.array([0.4188, 0.4789, 3.1260])), - (('10Y', 2.0, 6.0), np.array([0.4820, 0.5760, 3.1260])), - (('2.5GY', 2.0, 2.0), np.array([0.3421, 0.3803, 3.1260])), - (('2.5GY', 2.0, 4.0), np.array([0.3881, 0.4752, 3.1260])), - (('2.5GY', 2.0, 6.0), np.array([0.4340, 0.5900, 3.1260])), - (('2.5GY', 2.0, 8.0), np.array([0.4760, 0.7160, 3.1260])), - (('5GY', 2.0, 2.0), np.array([0.3309, 0.3743, 3.1260])), - (('5GY', 2.0, 4.0), np.array([0.3582, 0.4650, 3.1260])), - (('5GY', 2.0, 6.0), np.array([0.3839, 0.5748, 3.1260])), - (('5GY', 2.0, 8.0), np.array([0.4090, 0.7100, 3.1260])), - (('5GY', 2.0, 10.0), np.array([0.4180, 0.8250, 3.1260])), - (('7.5GY', 2.0, 2.0), np.array([0.3165, 0.3650, 3.1260])), - (('7.5GY', 2.0, 4.0), np.array([0.3248, 0.4457, 3.1260])), - (('7.5GY', 2.0, 6.0), np.array([0.3260, 0.5379, 3.1260])), - (('7.5GY', 2.0, 8.0), np.array([0.3160, 0.6509, 3.1260])), - (('7.5GY', 2.0, 10.0), np.array([0.2970, 0.7680, 3.1260])), - (('7.5GY', 2.0, 12.0), np.array([0.2740, 0.8790, 3.1260])), - (('7.5GY', 2.0, 14.0), np.array([0.2430, 1.0100, 3.1260])), - (('10GY', 2.0, 2.0), np.array([0.3069, 0.3580, 3.1260])), - (('10GY', 2.0, 4.0), np.array([0.2986, 0.4240, 3.1260])), - (('10GY', 2.0, 6.0), np.array([0.2852, 0.4972, 3.1260])), - (('10GY', 2.0, 8.0), np.array([0.2628, 0.5837, 3.1260])), - (('10GY', 2.0, 10.0), np.array([0.2307, 0.6814, 3.1260])), - (('10GY', 2.0, 12.0), np.array([0.1907, 0.7798, 3.1260])), - (('10GY', 2.0, 14.0), np.array([0.1410, 0.8760, 3.1260])), - (('10GY', 2.0, 16.0), np.array([0.0610, 0.9980, 3.1260])), - (('10GY', 2.0, 18.0), np.array([-0.0300, 1.1100, 3.1260])), - (('2.5G', 2.0, 2.0), np.array([0.2978, 0.3507, 3.1260])), - (('2.5G', 2.0, 4.0), np.array([0.2763, 0.3998, 3.1260])), - (('2.5G', 2.0, 6.0), np.array([0.2493, 0.4522, 3.1260])), - (('2.5G', 2.0, 8.0), np.array([0.2192, 0.5042, 3.1260])), - (('2.5G', 2.0, 10.0), np.array([0.1773, 0.5698, 3.1260])), - (('2.5G', 2.0, 12.0), np.array([0.1307, 0.6308, 3.1260])), - (('2.5G', 2.0, 14.0), np.array([0.0820, 0.6860, 3.1260])), - (('2.5G', 2.0, 16.0), np.array([0.0329, 0.7358, 3.1260])), - (('2.5G', 2.0, 18.0), np.array([-0.0200, 0.7880, 3.1260])), - (('5G', 2.0, 2.0), np.array([0.2918, 0.3450, 3.1260])), - (('5G', 2.0, 4.0), np.array([0.2640, 0.3845, 3.1260])), - (('5G', 2.0, 6.0), np.array([0.2318, 0.4231, 3.1260])), - (('5G', 2.0, 8.0), np.array([0.1979, 0.4583, 3.1260])), - (('5G', 2.0, 10.0), np.array([0.1560, 0.4981, 3.1260])), - (('5G', 2.0, 12.0), np.array([0.1120, 0.5358, 3.1260])), - (('5G', 2.0, 14.0), np.array([0.0688, 0.5691, 3.1260])), - (('5G', 2.0, 16.0), np.array([0.0277, 0.5986, 3.1260])), - (('5G', 2.0, 18.0), np.array([-0.0150, 0.6250, 3.1260])), - (('7.5G', 2.0, 2.0), np.array([0.2869, 0.3400, 3.1260])), - (('7.5G', 2.0, 4.0), np.array([0.2540, 0.3705, 3.1260])), - (('7.5G', 2.0, 6.0), np.array([0.2200, 0.3983, 3.1260])), - (('7.5G', 2.0, 8.0), np.array([0.1842, 0.4244, 3.1260])), - (('7.5G', 2.0, 10.0), np.array([0.1442, 0.4505, 3.1260])), - (('7.5G', 2.0, 12.0), np.array([0.1022, 0.4759, 3.1260])), - (('7.5G', 2.0, 14.0), np.array([0.0629, 0.4973, 3.1260])), - (('7.5G', 2.0, 16.0), np.array([0.0276, 0.5153, 3.1260])), - (('7.5G', 2.0, 18.0), np.array([-0.0100, 0.5320, 3.1260])), - (('10G', 2.0, 2.0), np.array([0.2820, 0.3341, 3.1260])), - (('10G', 2.0, 4.0), np.array([0.2442, 0.3559, 3.1260])), - (('10G', 2.0, 6.0), np.array([0.2092, 0.3739, 3.1260])), - (('10G', 2.0, 8.0), np.array([0.1705, 0.3911, 3.1260])), - (('10G', 2.0, 10.0), np.array([0.1321, 0.4059, 3.1260])), - (('10G', 2.0, 12.0), np.array([0.0934, 0.4183, 3.1260])), - (('10G', 2.0, 14.0), np.array([0.0599, 0.4270, 3.1260])), - (('10G', 2.0, 16.0), np.array([0.0285, 0.4327, 3.1260])), - (('10G', 2.0, 18.0), np.array([-0.0070, 0.4410, 3.1260])), - (('2.5BG', 2.0, 2.0), np.array([0.2765, 0.3271, 3.1260])), - (('2.5BG', 2.0, 4.0), np.array([0.2343, 0.3378, 3.1260])), - (('2.5BG', 2.0, 6.0), np.array([0.1971, 0.3452, 3.1260])), - (('2.5BG', 2.0, 8.0), np.array([0.1557, 0.3517, 3.1260])), - (('2.5BG', 2.0, 10.0), np.array([0.1190, 0.3551, 3.1260])), - (('2.5BG', 2.0, 12.0), np.array([0.0851, 0.3576, 3.1260])), - (('2.5BG', 2.0, 14.0), np.array([0.0555, 0.3588, 3.1260])), - (('2.5BG', 2.0, 16.0), np.array([0.0290, 0.3590, 3.1260])), - (('2.5BG', 2.0, 18.0), np.array([-0.0020, 0.3600, 3.1260])), - (('5BG', 2.0, 2.0), np.array([0.2697, 0.3175, 3.1260])), - (('5BG', 2.0, 4.0), np.array([0.2234, 0.3150, 3.1260])), - (('5BG', 2.0, 6.0), np.array([0.1843, 0.3110, 3.1260])), - (('5BG', 2.0, 8.0), np.array([0.1405, 0.3037, 3.1260])), - (('5BG', 2.0, 10.0), np.array([0.1050, 0.2956, 3.1260])), - (('5BG', 2.0, 12.0), np.array([0.0769, 0.2880, 3.1260])), - (('5BG', 2.0, 14.0), np.array([0.0510, 0.2800, 3.1260])), - (('5BG', 2.0, 16.0), np.array([0.0280, 0.2740, 3.1260])), - (('7.5BG', 2.0, 2.0), np.array([0.2651, 0.3098, 3.1260])), - (('7.5BG', 2.0, 4.0), np.array([0.2162, 0.2981, 3.1260])), - (('7.5BG', 2.0, 6.0), np.array([0.1747, 0.2853, 3.1260])), - (('7.5BG', 2.0, 8.0), np.array([0.1325, 0.2710, 3.1260])), - (('7.5BG', 2.0, 10.0), np.array([0.0991, 0.2582, 3.1260])), - (('7.5BG', 2.0, 12.0), np.array([0.0724, 0.2478, 3.1260])), - (('7.5BG', 2.0, 14.0), np.array([0.0500, 0.2370, 3.1260])), - (('7.5BG', 2.0, 16.0), np.array([0.0280, 0.2280, 3.1260])), - (('10BG', 2.0, 2.0), np.array([0.2606, 0.3010, 3.1260])), - (('10BG', 2.0, 4.0), np.array([0.2096, 0.2790, 3.1260])), - (('10BG', 2.0, 6.0), np.array([0.1669, 0.2570, 3.1260])), - (('10BG', 2.0, 8.0), np.array([0.1258, 0.2331, 3.1260])), - (('10BG', 2.0, 10.0), np.array([0.0929, 0.2133, 3.1260])), - (('10BG', 2.0, 12.0), np.array([0.0680, 0.1940, 3.1260])), - (('10BG', 2.0, 14.0), np.array([0.0500, 0.1810, 3.1260])), - (('2.5B', 2.0, 2.0), np.array([0.2578, 0.2940, 3.1260])), - (('2.5B', 2.0, 4.0), np.array([0.2060, 0.2649, 3.1260])), - (('2.5B', 2.0, 6.0), np.array([0.1621, 0.2358, 3.1260])), - (('2.5B', 2.0, 8.0), np.array([0.1230, 0.2076, 3.1260])), - (('2.5B', 2.0, 10.0), np.array([0.0911, 0.1828, 3.1260])), - (('2.5B', 2.0, 12.0), np.array([0.0670, 0.1650, 3.1260])), - (('5B', 2.0, 2.0), np.array([0.2559, 0.2874, 3.1260])), - (('5B', 2.0, 4.0), np.array([0.2048, 0.2518, 3.1260])), - (('5B', 2.0, 6.0), np.array([0.1617, 0.2162, 3.1260])), - (('5B', 2.0, 8.0), np.array([0.1245, 0.1827, 3.1260])), - (('5B', 2.0, 10.0), np.array([0.0965, 0.1558, 3.1260])), - (('5B', 2.0, 12.0), np.array([0.0770, 0.1360, 3.1260])), - (('7.5B', 2.0, 2.0), np.array([0.2545, 0.2799, 3.1260])), - (('7.5B', 2.0, 4.0), np.array([0.2063, 0.2400, 3.1260])), - (('7.5B', 2.0, 6.0), np.array([0.1658, 0.2026, 3.1260])), - (('7.5B', 2.0, 8.0), np.array([0.1313, 0.1692, 3.1260])), - (('7.5B', 2.0, 10.0), np.array([0.1051, 0.1422, 3.1260])), - (('7.5B', 2.0, 12.0), np.array([0.0880, 0.1240, 3.1260])), - (('7.5B', 2.0, 14.0), np.array([0.0700, 0.1060, 3.1260])), - (('10B', 2.0, 2.0), np.array([0.2558, 0.2725, 3.1260])), - (('10B', 2.0, 4.0), np.array([0.2102, 0.2313, 3.1260])), - (('10B', 2.0, 6.0), np.array([0.1716, 0.1937, 3.1260])), - (('10B', 2.0, 8.0), np.array([0.1396, 0.1603, 3.1260])), - (('10B', 2.0, 10.0), np.array([0.1157, 0.1346, 3.1260])), - (('10B', 2.0, 12.0), np.array([0.0990, 0.1150, 3.1260])), - (('10B', 2.0, 14.0), np.array([0.0830, 0.0970, 3.1260])), - (('2.5PB', 2.0, 2.0), np.array([0.2592, 0.2675, 3.1260])), - (('2.5PB', 2.0, 4.0), np.array([0.2175, 0.2245, 3.1260])), - (('2.5PB', 2.0, 6.0), np.array([0.1825, 0.1857, 3.1260])), - (('2.5PB', 2.0, 8.0), np.array([0.1540, 0.1530, 3.1260])), - (('2.5PB', 2.0, 10.0), np.array([0.1332, 0.1278, 3.1260])), - (('2.5PB', 2.0, 12.0), np.array([0.1166, 0.1076, 3.1260])), - (('2.5PB', 2.0, 14.0), np.array([0.1020, 0.0890, 3.1260])), - (('2.5PB', 2.0, 16.0), np.array([0.0920, 0.0760, 3.1260])), - (('2.5PB', 2.0, 18.0), np.array([0.0840, 0.0640, 3.1260])), - (('5PB', 2.0, 2.0), np.array([0.2638, 0.2624, 3.1260])), - (('5PB', 2.0, 4.0), np.array([0.2263, 0.2192, 3.1260])), - (('5PB', 2.0, 6.0), np.array([0.1942, 0.1811, 3.1260])), - (('5PB', 2.0, 8.0), np.array([0.1685, 0.1491, 3.1260])), - (('5PB', 2.0, 10.0), np.array([0.1500, 0.1240, 3.1260])), - (('5PB', 2.0, 12.0), np.array([0.1363, 0.1048, 3.1260])), - (('5PB', 2.0, 14.0), np.array([0.1253, 0.0873, 3.1260])), - (('5PB', 2.0, 16.0), np.array([0.1170, 0.0740, 3.1260])), - (('5PB', 2.0, 18.0), np.array([0.1110, 0.0640, 3.1260])), - (('5PB', 2.0, 20.0), np.array([0.1080, 0.0580, 3.1260])), - (('5PB', 2.0, 22.0), np.array([0.1040, 0.0520, 3.1260])), - (('5PB', 2.0, 24.0), np.array([0.1000, 0.0460, 3.1260])), - (('5PB', 2.0, 26.0), np.array([0.0980, 0.0420, 3.1260])), - (('5PB', 2.0, 28.0), np.array([0.0970, 0.0400, 3.1260])), - (('5PB', 2.0, 30.0), np.array([0.0960, 0.0380, 3.1260])), - (('5PB', 2.0, 32.0), np.array([0.0950, 0.0360, 3.1260])), - (('5PB', 2.0, 34.0), np.array([0.0930, 0.0330, 3.1260])), - (('5PB', 2.0, 36.0), np.array([0.0920, 0.0310, 3.1260])), - (('5PB', 2.0, 38.0), np.array([0.0900, 0.0280, 3.1260])), - (('5PB', 2.0, 40.0), np.array([0.0890, 0.0250, 3.1260])), - (('5PB', 2.0, 42.0), np.array([0.0870, 0.0220, 3.1260])), - (('5PB', 2.0, 44.0), np.array([0.0860, 0.0190, 3.1260])), - (('5PB', 2.0, 46.0), np.array([0.0840, 0.0160, 3.1260])), - (('7.5PB', 2.0, 2.0), np.array([0.2712, 0.2582, 3.1260])), - (('7.5PB', 2.0, 4.0), np.array([0.2420, 0.2148, 3.1260])), - (('7.5PB', 2.0, 6.0), np.array([0.2189, 0.1790, 3.1260])), - (('7.5PB', 2.0, 8.0), np.array([0.2005, 0.1495, 3.1260])), - (('7.5PB', 2.0, 10.0), np.array([0.1882, 0.1258, 3.1260])), - (('7.5PB', 2.0, 12.0), np.array([0.1813, 0.1094, 3.1260])), - (('7.5PB', 2.0, 14.0), np.array([0.1762, 0.0955, 3.1260])), - (('7.5PB', 2.0, 16.0), np.array([0.1728, 0.0839, 3.1260])), - (('7.5PB', 2.0, 18.0), np.array([0.1701, 0.0742, 3.1260])), - (('7.5PB', 2.0, 20.0), np.array([0.1685, 0.0666, 3.1260])), - (('7.5PB', 2.0, 22.0), np.array([0.1670, 0.0594, 3.1260])), - (('7.5PB', 2.0, 24.0), np.array([0.1660, 0.0538, 3.1260])), - (('7.5PB', 2.0, 26.0), np.array([0.1653, 0.0492, 3.1260])), - (('7.5PB', 2.0, 28.0), np.array([0.1647, 0.0451, 3.1260])), - (('7.5PB', 2.0, 30.0), np.array([0.1640, 0.0409, 3.1260])), - (('7.5PB', 2.0, 32.0), np.array([0.1635, 0.0373, 3.1260])), - (('7.5PB', 2.0, 34.0), np.array([0.1630, 0.0340, 3.1260])), - (('7.5PB', 2.0, 36.0), np.array([0.1628, 0.0310, 3.1260])), - (('7.5PB', 2.0, 38.0), np.array([0.1623, 0.0280, 3.1260])), - (('7.5PB', 2.0, 40.0), np.array([0.1620, 0.0250, 3.1260])), - (('7.5PB', 2.0, 42.0), np.array([0.1620, 0.0220, 3.1260])), - (('7.5PB', 2.0, 44.0), np.array([0.1620, 0.0190, 3.1260])), - (('7.5PB', 2.0, 46.0), np.array([0.1610, 0.0160, 3.1260])), - (('7.5PB', 2.0, 48.0), np.array([0.1610, 0.0130, 3.1260])), - (('7.5PB', 2.0, 50.0), np.array([0.1600, 0.0100, 3.1260])), - (('10PB', 2.0, 2.0), np.array([0.2803, 0.2567, 3.1260])), - (('10PB', 2.0, 4.0), np.array([0.2600, 0.2162, 3.1260])), - (('10PB', 2.0, 6.0), np.array([0.2440, 0.1840, 3.1260])), - (('10PB', 2.0, 8.0), np.array([0.2294, 0.1551, 3.1260])), - (('10PB', 2.0, 10.0), np.array([0.2200, 0.1330, 3.1260])), - (('10PB', 2.0, 12.0), np.array([0.2139, 0.1170, 3.1260])), - (('10PB', 2.0, 14.0), np.array([0.2087, 0.1026, 3.1260])), - (('10PB', 2.0, 16.0), np.array([0.2052, 0.0910, 3.1260])), - (('10PB', 2.0, 18.0), np.array([0.2021, 0.0808, 3.1260])), - (('10PB', 2.0, 20.0), np.array([0.1998, 0.0718, 3.1260])), - (('10PB', 2.0, 22.0), np.array([0.1978, 0.0643, 3.1260])), - (('10PB', 2.0, 24.0), np.array([0.1962, 0.0578, 3.1260])), - (('10PB', 2.0, 26.0), np.array([0.1949, 0.0520, 3.1260])), - (('10PB', 2.0, 28.0), np.array([0.1937, 0.0471, 3.1260])), - (('10PB', 2.0, 30.0), np.array([0.1925, 0.0420, 3.1260])), - (('10PB', 2.0, 32.0), np.array([0.1918, 0.0379, 3.1260])), - (('10PB', 2.0, 34.0), np.array([0.1911, 0.0344, 3.1260])), - (('10PB', 2.0, 36.0), np.array([0.1900, 0.0310, 3.1260])), - (('10PB', 2.0, 38.0), np.array([0.1900, 0.0280, 3.1260])), - (('10PB', 2.0, 40.0), np.array([0.1900, 0.0250, 3.1260])), - (('10PB', 2.0, 42.0), np.array([0.1890, 0.0220, 3.1260])), - (('10PB', 2.0, 44.0), np.array([0.1890, 0.0190, 3.1260])), - (('10PB', 2.0, 46.0), np.array([0.1890, 0.0160, 3.1260])), - (('10PB', 2.0, 48.0), np.array([0.1880, 0.0130, 3.1260])), - (('10PB', 2.0, 50.0), np.array([0.1880, 0.0100, 3.1260])), - (('2.5P', 2.0, 2.0), np.array([0.2892, 0.2583, 3.1260])), - (('2.5P', 2.0, 4.0), np.array([0.2758, 0.2208, 3.1260])), - (('2.5P', 2.0, 6.0), np.array([0.2661, 0.1921, 3.1260])), - (('2.5P', 2.0, 8.0), np.array([0.2570, 0.1635, 3.1260])), - (('2.5P', 2.0, 10.0), np.array([0.2501, 0.1422, 3.1260])), - (('2.5P', 2.0, 12.0), np.array([0.2449, 0.1245, 3.1260])), - (('2.5P', 2.0, 14.0), np.array([0.2406, 0.1100, 3.1260])), - (('2.5P', 2.0, 16.0), np.array([0.2372, 0.0980, 3.1260])), - (('2.5P', 2.0, 18.0), np.array([0.2345, 0.0873, 3.1260])), - (('2.5P', 2.0, 20.0), np.array([0.2320, 0.0779, 3.1260])), - (('2.5P', 2.0, 22.0), np.array([0.2298, 0.0696, 3.1260])), - (('2.5P', 2.0, 24.0), np.array([0.2277, 0.0621, 3.1260])), - (('2.5P', 2.0, 26.0), np.array([0.2260, 0.0555, 3.1260])), - (('2.5P', 2.0, 28.0), np.array([0.2245, 0.0491, 3.1260])), - (('2.5P', 2.0, 30.0), np.array([0.2231, 0.0432, 3.1260])), - (('2.5P', 2.0, 32.0), np.array([0.2220, 0.0380, 3.1260])), - (('2.5P', 2.0, 34.0), np.array([0.2210, 0.0350, 3.1260])), - (('2.5P', 2.0, 36.0), np.array([0.2200, 0.0310, 3.1260])), - (('2.5P', 2.0, 38.0), np.array([0.2190, 0.0280, 3.1260])), - (('2.5P', 2.0, 40.0), np.array([0.2190, 0.0250, 3.1260])), - (('2.5P', 2.0, 42.0), np.array([0.2180, 0.0220, 3.1260])), - (('2.5P', 2.0, 44.0), np.array([0.2170, 0.0190, 3.1260])), - (('5P', 2.0, 2.0), np.array([0.2984, 0.2612, 3.1260])), - (('5P', 2.0, 4.0), np.array([0.2908, 0.2261, 3.1260])), - (('5P', 2.0, 6.0), np.array([0.2850, 0.1992, 3.1260])), - (('5P', 2.0, 8.0), np.array([0.2791, 0.1707, 3.1260])), - (('5P', 2.0, 10.0), np.array([0.2748, 0.1500, 3.1260])), - (('5P', 2.0, 12.0), np.array([0.2709, 0.1320, 3.1260])), - (('5P', 2.0, 14.0), np.array([0.2676, 0.1163, 3.1260])), - (('5P', 2.0, 16.0), np.array([0.2652, 0.1045, 3.1260])), - (('5P', 2.0, 18.0), np.array([0.2632, 0.0935, 3.1260])), - (('5P', 2.0, 20.0), np.array([0.2612, 0.0838, 3.1260])), - (('5P', 2.0, 22.0), np.array([0.2597, 0.0750, 3.1260])), - (('5P', 2.0, 24.0), np.array([0.2582, 0.0669, 3.1260])), - (('5P', 2.0, 26.0), np.array([0.2569, 0.0594, 3.1260])), - (('5P', 2.0, 28.0), np.array([0.2559, 0.0525, 3.1260])), - (('5P', 2.0, 30.0), np.array([0.2550, 0.0450, 3.1260])), - (('5P', 2.0, 32.0), np.array([0.2540, 0.0390, 3.1260])), - (('5P', 2.0, 34.0), np.array([0.2530, 0.0350, 3.1260])), - (('5P', 2.0, 36.0), np.array([0.2520, 0.0310, 3.1260])), - (('7.5P', 2.0, 2.0), np.array([0.3071, 0.2647, 3.1260])), - (('7.5P', 2.0, 4.0), np.array([0.3048, 0.2321, 3.1260])), - (('7.5P', 2.0, 6.0), np.array([0.3025, 0.2058, 3.1260])), - (('7.5P', 2.0, 8.0), np.array([0.3000, 0.1781, 3.1260])), - (('7.5P', 2.0, 10.0), np.array([0.2979, 0.1569, 3.1260])), - (('7.5P', 2.0, 12.0), np.array([0.2956, 0.1392, 3.1260])), - (('7.5P', 2.0, 14.0), np.array([0.2938, 0.1235, 3.1260])), - (('7.5P', 2.0, 16.0), np.array([0.2922, 0.1106, 3.1260])), - (('7.5P', 2.0, 18.0), np.array([0.2912, 0.0995, 3.1260])), - (('7.5P', 2.0, 20.0), np.array([0.2902, 0.0901, 3.1260])), - (('7.5P', 2.0, 22.0), np.array([0.2890, 0.0799, 3.1260])), - (('7.5P', 2.0, 24.0), np.array([0.2882, 0.0719, 3.1260])), - (('7.5P', 2.0, 26.0), np.array([0.2870, 0.0640, 3.1260])), - (('7.5P', 2.0, 28.0), np.array([0.2860, 0.0550, 3.1260])), - (('7.5P', 2.0, 30.0), np.array([0.2840, 0.0460, 3.1260])), - (('10P', 2.0, 2.0), np.array([0.3161, 0.2691, 3.1260])), - (('10P', 2.0, 4.0), np.array([0.3189, 0.2390, 3.1260])), - (('10P', 2.0, 6.0), np.array([0.3207, 0.2132, 3.1260])), - (('10P', 2.0, 8.0), np.array([0.3219, 0.1862, 3.1260])), - (('10P', 2.0, 10.0), np.array([0.3230, 0.1659, 3.1260])), - (('10P', 2.0, 12.0), np.array([0.3233, 0.1477, 3.1260])), - (('10P', 2.0, 14.0), np.array([0.3235, 0.1317, 3.1260])), - (('10P', 2.0, 16.0), np.array([0.3235, 0.1181, 3.1260])), - (('10P', 2.0, 18.0), np.array([0.3233, 0.1063, 3.1260])), - (('10P', 2.0, 20.0), np.array([0.3231, 0.0962, 3.1260])), - (('10P', 2.0, 22.0), np.array([0.3230, 0.0861, 3.1260])), - (('10P', 2.0, 24.0), np.array([0.3220, 0.0760, 3.1260])), - (('10P', 2.0, 26.0), np.array([0.3220, 0.0680, 3.1260])), - (('10P', 2.0, 28.0), np.array([0.3220, 0.0580, 3.1260])), - (('2.5RP', 2.0, 2.0), np.array([0.3279, 0.2754, 3.1260])), - (('2.5RP', 2.0, 4.0), np.array([0.3382, 0.2496, 3.1260])), - (('2.5RP', 2.0, 6.0), np.array([0.3470, 0.2259, 3.1260])), - (('2.5RP', 2.0, 8.0), np.array([0.3555, 0.2003, 3.1260])), - (('2.5RP', 2.0, 10.0), np.array([0.3617, 0.1800, 3.1260])), - (('2.5RP', 2.0, 12.0), np.array([0.3668, 0.1618, 3.1260])), - (('2.5RP', 2.0, 14.0), np.array([0.3711, 0.1449, 3.1260])), - (('2.5RP', 2.0, 16.0), np.array([0.3748, 0.1310, 3.1260])), - (('2.5RP', 2.0, 18.0), np.array([0.3778, 0.1188, 3.1260])), - (('2.5RP', 2.0, 20.0), np.array([0.3802, 0.1080, 3.1260])), - (('2.5RP', 2.0, 22.0), np.array([0.3830, 0.0960, 3.1260])), - (('2.5RP', 2.0, 24.0), np.array([0.3860, 0.0860, 3.1260])), - (('2.5RP', 2.0, 26.0), np.array([0.3880, 0.0760, 3.1260])), - (('5RP', 2.0, 2.0), np.array([0.3383, 0.2829, 3.1260])), - (('5RP', 2.0, 4.0), np.array([0.3558, 0.2597, 3.1260])), - (('5RP', 2.0, 6.0), np.array([0.3708, 0.2380, 3.1260])), - (('5RP', 2.0, 8.0), np.array([0.3858, 0.2140, 3.1260])), - (('5RP', 2.0, 10.0), np.array([0.3971, 0.1939, 3.1260])), - (('5RP', 2.0, 12.0), np.array([0.4080, 0.1764, 3.1260])), - (('5RP', 2.0, 14.0), np.array([0.4180, 0.1598, 3.1260])), - (('5RP', 2.0, 16.0), np.array([0.4269, 0.1454, 3.1260])), - (('5RP', 2.0, 18.0), np.array([0.4338, 0.1340, 3.1260])), - (('5RP', 2.0, 20.0), np.array([0.4420, 0.1210, 3.1260])), - (('5RP', 2.0, 22.0), np.array([0.4480, 0.1100, 3.1260])), - (('7.5RP', 2.0, 2.0), np.array([0.3459, 0.2892, 3.1260])), - (('7.5RP', 2.0, 4.0), np.array([0.3702, 0.2683, 3.1260])), - (('7.5RP', 2.0, 6.0), np.array([0.3918, 0.2490, 3.1260])), - (('7.5RP', 2.0, 8.0), np.array([0.4137, 0.2276, 3.1260])), - (('7.5RP', 2.0, 10.0), np.array([0.4321, 0.2082, 3.1260])), - (('7.5RP', 2.0, 12.0), np.array([0.4481, 0.1903, 3.1260])), - (('7.5RP', 2.0, 14.0), np.array([0.4624, 0.1737, 3.1260])), - (('7.5RP', 2.0, 16.0), np.array([0.4744, 0.1595, 3.1260])), - (('7.5RP', 2.0, 18.0), np.array([0.4850, 0.1460, 3.1260])), - (('7.5RP', 2.0, 20.0), np.array([0.4970, 0.1320, 3.1260])), - (('10RP', 2.0, 2.0), np.array([0.3532, 0.2957, 3.1260])), - (('10RP', 2.0, 4.0), np.array([0.3850, 0.2778, 3.1260])), - (('10RP', 2.0, 6.0), np.array([0.4139, 0.2608, 3.1260])), - (('10RP', 2.0, 8.0), np.array([0.4428, 0.2419, 3.1260])), - (('10RP', 2.0, 10.0), np.array([0.4678, 0.2237, 3.1260])), - (('10RP', 2.0, 12.0), np.array([0.4911, 0.2060, 3.1260])), - (('10RP', 2.0, 14.0), np.array([0.5129, 0.1888, 3.1260])), - (('10RP', 2.0, 16.0), np.array([0.5310, 0.1740, 3.1260])), - (('10RP', 2.0, 18.0), np.array([0.5460, 0.1610, 3.1260])), - (('2.5R', 2.0, 2.0), np.array([0.3614, 0.3033, 3.1260])), - (('2.5R', 2.0, 4.0), np.array([0.4021, 0.2900, 3.1260])), - (('2.5R', 2.0, 6.0), np.array([0.4390, 0.2760, 3.1260])), - (('2.5R', 2.0, 8.0), np.array([0.4776, 0.2593, 3.1260])), - (('2.5R', 2.0, 10.0), np.array([0.5122, 0.2428, 3.1260])), - (('2.5R', 2.0, 12.0), np.array([0.5438, 0.2254, 3.1260])), - (('2.5R', 2.0, 14.0), np.array([0.5734, 0.2083, 3.1260])), - (('2.5R', 2.0, 16.0), np.array([0.6010, 0.1920, 3.1260])), - (('5R', 2.0, 2.0), np.array([0.3692, 0.3111, 3.1260])), - (('5R', 2.0, 4.0), np.array([0.4184, 0.3032, 3.1260])), - (('5R', 2.0, 6.0), np.array([0.4642, 0.2934, 3.1260])), - (('5R', 2.0, 8.0), np.array([0.5143, 0.2800, 3.1260])), - (('5R', 2.0, 10.0), np.array([0.5557, 0.2633, 3.1260])), - (('5R', 2.0, 12.0), np.array([0.5930, 0.2465, 3.1260])), - (('5R', 2.0, 14.0), np.array([0.6302, 0.2287, 3.1260])), - (('5R', 2.0, 16.0), np.array([0.6590, 0.2120, 3.1260])), - (('7.5R', 2.0, 2.0), np.array([0.3751, 0.3181, 3.1260])), - (('7.5R', 2.0, 4.0), np.array([0.4335, 0.3169, 3.1260])), - (('7.5R', 2.0, 6.0), np.array([0.4875, 0.3123, 3.1260])), - (('7.5R', 2.0, 8.0), np.array([0.5433, 0.3027, 3.1260])), - (('7.5R', 2.0, 10.0), np.array([0.5952, 0.2874, 3.1260])), - (('7.5R', 2.0, 12.0), np.array([0.6392, 0.2704, 3.1260])), - (('7.5R', 2.0, 14.0), np.array([0.6791, 0.2520, 3.1260])), - (('7.5R', 2.0, 16.0), np.array([0.7140, 0.2340, 3.1260])), - (('10R', 2.0, 2.0), np.array([0.3811, 0.3274, 3.1260])), - (('10R', 2.0, 4.0), np.array([0.4481, 0.3330, 3.1260])), - (('10R', 2.0, 6.0), np.array([0.5095, 0.3331, 3.1260])), - (('10R', 2.0, 8.0), np.array([0.5713, 0.3259, 3.1260])), - (('10R', 2.0, 10.0), np.array([0.6247, 0.3120, 3.1260])), - (('10R', 2.0, 12.0), np.array([0.6732, 0.2937, 3.1260])), - (('10R', 2.0, 14.0), np.array([0.7165, 0.2734, 3.1260])), - (('10R', 2.0, 16.0), np.array([0.7520, 0.2540, 3.1260])), - (('2.5YR', 2.0, 2.0), np.array([0.3852, 0.3365, 3.1260])), - (('2.5YR', 2.0, 4.0), np.array([0.4598, 0.3508, 3.1260])), - (('2.5YR', 2.0, 6.0), np.array([0.5280, 0.3581, 3.1260])), - (('2.5YR', 2.0, 8.0), np.array([0.5995, 0.3590, 3.1260])), - (('2.5YR', 2.0, 10.0), np.array([0.6590, 0.3500, 3.1260])), - (('2.5YR', 2.0, 12.0), np.array([0.7180, 0.3400, 3.1260])), - (('2.5YR', 2.0, 14.0), np.array([0.7790, 0.3230, 3.1260])), - (('2.5YR', 2.0, 16.0), np.array([0.8240, 0.3090, 3.1260])), - (('5YR', 2.0, 2.0), np.array([0.3880, 0.3476, 3.1260])), - (('5YR', 2.0, 4.0), np.array([0.4674, 0.3738, 3.1260])), - (('5YR', 2.0, 6.0), np.array([0.5426, 0.3925, 3.1260])), - (('5YR', 2.0, 8.0), np.array([0.6200, 0.4060, 3.1260])), - (('5YR', 2.0, 10.0), np.array([0.6840, 0.4150, 3.1260])), - (('7.5YR', 2.0, 2.0), np.array([0.3889, 0.3590, 3.1260])), - (('7.5YR', 2.0, 4.0), np.array([0.4690, 0.3964, 3.1260])), - (('7.5YR', 2.0, 6.0), np.array([0.5475, 0.4271, 3.1260])), - (('7.5YR', 2.0, 8.0), np.array([0.6200, 0.4560, 3.1260])), - (('10YR', 2.0, 2.0), np.array([0.3872, 0.3688, 3.1260])), - (('10YR', 2.0, 4.0), np.array([0.4676, 0.4168, 3.1260])), - (('10YR', 2.0, 6.0), np.array([0.5450, 0.4580, 3.1260])), - (('10YR', 2.0, 8.0), np.array([0.6120, 0.4930, 3.1260])), - (('2.5Y', 2.0, 2.0), np.array([0.3825, 0.3785, 3.1260])), - (('2.5Y', 2.0, 4.0), np.array([0.4627, 0.4392, 3.1260])), - (('2.5Y', 2.0, 6.0), np.array([0.5380, 0.4860, 3.1260])), - (('2.5Y', 2.0, 8.0), np.array([0.6040, 0.5260, 3.1260])), - (('5Y', 2.0, 2.0), np.array([0.3757, 0.3839, 3.1260])), - (('5Y', 2.0, 4.0), np.array([0.4543, 0.4573, 3.1260])), - (('5Y', 2.0, 6.0), np.array([0.5260, 0.5190, 3.1260])), - (('7.5Y', 2.0, 2.0), np.array([0.3660, 0.3858, 3.1260])), - (('7.5Y', 2.0, 4.0), np.array([0.4401, 0.4723, 3.1260])), - (('7.5Y', 2.0, 6.0), np.array([0.5100, 0.5470, 3.1260])), - (('10Y', 3.0, 2.0), np.array([0.3513, 0.3789, 6.5500])), - (('10Y', 3.0, 4.0), np.array([0.3961, 0.4452, 6.5500])), - (('10Y', 3.0, 6.0), np.array([0.4345, 0.5026, 6.5500])), - (('10Y', 3.0, 8.0), np.array([0.4700, 0.5550, 6.5500])), - (('2.5GY', 3.0, 2.0), np.array([0.3412, 0.3768, 6.5500])), - (('2.5GY', 3.0, 4.0), np.array([0.3772, 0.4484, 6.5500])), - (('2.5GY', 3.0, 6.0), np.array([0.4069, 0.5110, 6.5500])), - (('2.5GY', 3.0, 8.0), np.array([0.4320, 0.5760, 6.5500])), - (('2.5GY', 3.0, 10.0), np.array([0.4550, 0.6380, 6.5500])), - (('5GY', 3.0, 2.0), np.array([0.3319, 0.3729, 6.5500])), - (('5GY', 3.0, 4.0), np.array([0.3554, 0.4429, 6.5500])), - (('5GY', 3.0, 6.0), np.array([0.3750, 0.5109, 6.5500])), - (('5GY', 3.0, 8.0), np.array([0.3924, 0.5832, 6.5500])), - (('5GY', 3.0, 10.0), np.array([0.4040, 0.6500, 6.5500])), - (('5GY', 3.0, 12.0), np.array([0.4130, 0.7180, 6.5500])), - (('7.5GY', 3.0, 2.0), np.array([0.3180, 0.3644, 6.5500])), - (('7.5GY', 3.0, 4.0), np.array([0.3270, 0.4288, 6.5500])), - (('7.5GY', 3.0, 6.0), np.array([0.3333, 0.4967, 6.5500])), - (('7.5GY', 3.0, 8.0), np.array([0.3341, 0.5700, 6.5500])), - (('7.5GY', 3.0, 10.0), np.array([0.3266, 0.6448, 6.5500])), - (('7.5GY', 3.0, 12.0), np.array([0.3150, 0.7200, 6.5500])), - (('7.5GY', 3.0, 14.0), np.array([0.3000, 0.7930, 6.5500])), - (('10GY', 3.0, 2.0), np.array([0.3088, 0.3578, 6.5500])), - (('10GY', 3.0, 4.0), np.array([0.3053, 0.4123, 6.5500])), - (('10GY', 3.0, 6.0), np.array([0.2992, 0.4717, 6.5500])), - (('10GY', 3.0, 8.0), np.array([0.2887, 0.5361, 6.5500])), - (('10GY', 3.0, 10.0), np.array([0.2724, 0.6026, 6.5500])), - (('10GY', 3.0, 12.0), np.array([0.2531, 0.6700, 6.5500])), - (('10GY', 3.0, 14.0), np.array([0.2283, 0.7423, 6.5500])), - (('10GY', 3.0, 16.0), np.array([0.2020, 0.8070, 6.5500])), - (('10GY', 3.0, 18.0), np.array([0.1680, 0.8800, 6.5500])), - (('10GY', 3.0, 20.0), np.array([0.1300, 0.9480, 6.5500])), - (('10GY', 3.0, 22.0), np.array([0.0890, 1.0140, 6.5500])), - (('10GY', 3.0, 24.0), np.array([0.0460, 1.0780, 6.5500])), - (('2.5G', 3.0, 2.0), np.array([0.2999, 0.3500, 6.5500])), - (('2.5G', 3.0, 4.0), np.array([0.2836, 0.3915, 6.5500])), - (('2.5G', 3.0, 6.0), np.array([0.2642, 0.4342, 6.5500])), - (('2.5G', 3.0, 8.0), np.array([0.2435, 0.4752, 6.5500])), - (('2.5G', 3.0, 10.0), np.array([0.2170, 0.5211, 6.5500])), - (('2.5G', 3.0, 12.0), np.array([0.1902, 0.5642, 6.5500])), - (('2.5G', 3.0, 14.0), np.array([0.1626, 0.6052, 6.5500])), - (('2.5G', 3.0, 16.0), np.array([0.1341, 0.6420, 6.5500])), - (('2.5G', 3.0, 18.0), np.array([0.1049, 0.6766, 6.5500])), - (('2.5G', 3.0, 20.0), np.array([0.0720, 0.7127, 6.5500])), - (('2.5G', 3.0, 22.0), np.array([0.0390, 0.7468, 6.5500])), - (('2.5G', 3.0, 24.0), np.array([0.0090, 0.7760, 6.5500])), - (('5G', 3.0, 2.0), np.array([0.2935, 0.3439, 6.5500])), - (('5G', 3.0, 4.0), np.array([0.2711, 0.3780, 6.5500])), - (('5G', 3.0, 6.0), np.array([0.2471, 0.4100, 6.5500])), - (('5G', 3.0, 8.0), np.array([0.2228, 0.4380, 6.5500])), - (('5G', 3.0, 10.0), np.array([0.1935, 0.4682, 6.5500])), - (('5G', 3.0, 12.0), np.array([0.1660, 0.4948, 6.5500])), - (('5G', 3.0, 14.0), np.array([0.1382, 0.5197, 6.5500])), - (('5G', 3.0, 16.0), np.array([0.1120, 0.5414, 6.5500])), - (('5G', 3.0, 18.0), np.array([0.0882, 0.5605, 6.5500])), - (('5G', 3.0, 20.0), np.array([0.0620, 0.5802, 6.5500])), - (('5G', 3.0, 22.0), np.array([0.0340, 0.6011, 6.5500])), - (('5G', 3.0, 24.0), np.array([0.0040, 0.6220, 6.5500])), - (('7.5G', 3.0, 2.0), np.array([0.2890, 0.3391, 6.5500])), - (('7.5G', 3.0, 4.0), np.array([0.2618, 0.3667, 6.5500])), - (('7.5G', 3.0, 6.0), np.array([0.2346, 0.3901, 6.5500])), - (('7.5G', 3.0, 8.0), np.array([0.2088, 0.4101, 6.5500])), - (('7.5G', 3.0, 10.0), np.array([0.1800, 0.4310, 6.5500])), - (('7.5G', 3.0, 12.0), np.array([0.1516, 0.4505, 6.5500])), - (('7.5G', 3.0, 14.0), np.array([0.1262, 0.4667, 6.5500])), - (('7.5G', 3.0, 16.0), np.array([0.1023, 0.4818, 6.5500])), - (('7.5G', 3.0, 18.0), np.array([0.0798, 0.4954, 6.5500])), - (('7.5G', 3.0, 20.0), np.array([0.0568, 0.5082, 6.5500])), - (('7.5G', 3.0, 22.0), np.array([0.0332, 0.5206, 6.5500])), - (('7.5G', 3.0, 24.0), np.array([0.0060, 0.5340, 6.5500])), - (('10G', 3.0, 2.0), np.array([0.2844, 0.3337, 6.5500])), - (('10G', 3.0, 4.0), np.array([0.2525, 0.3537, 6.5500])), - (('10G', 3.0, 6.0), np.array([0.2240, 0.3699, 6.5500])), - (('10G', 3.0, 8.0), np.array([0.1970, 0.3841, 6.5500])), - (('10G', 3.0, 10.0), np.array([0.1688, 0.3974, 6.5500])), - (('10G', 3.0, 12.0), np.array([0.1411, 0.4095, 6.5500])), - (('10G', 3.0, 14.0), np.array([0.1161, 0.4192, 6.5500])), - (('10G', 3.0, 16.0), np.array([0.0925, 0.4275, 6.5500])), - (('10G', 3.0, 18.0), np.array([0.0718, 0.4340, 6.5500])), - (('10G', 3.0, 20.0), np.array([0.0528, 0.4393, 6.5500])), - (('10G', 3.0, 22.0), np.array([0.0333, 0.4444, 6.5500])), - (('10G', 3.0, 24.0), np.array([0.0090, 0.4500, 6.5500])), - (('2.5BG', 3.0, 2.0), np.array([0.2799, 0.3271, 6.5500])), - (('2.5BG', 3.0, 4.0), np.array([0.2437, 0.3386, 6.5500])), - (('2.5BG', 3.0, 6.0), np.array([0.2132, 0.3468, 6.5500])), - (('2.5BG', 3.0, 8.0), np.array([0.1845, 0.3531, 6.5500])), - (('2.5BG', 3.0, 10.0), np.array([0.1552, 0.3580, 6.5500])), - (('2.5BG', 3.0, 12.0), np.array([0.1288, 0.3620, 6.5500])), - (('2.5BG', 3.0, 14.0), np.array([0.1051, 0.3648, 6.5500])), - (('2.5BG', 3.0, 16.0), np.array([0.0843, 0.3667, 6.5500])), - (('2.5BG', 3.0, 18.0), np.array([0.0648, 0.3682, 6.5500])), - (('2.5BG', 3.0, 20.0), np.array([0.0482, 0.3695, 6.5500])), - (('2.5BG', 3.0, 22.0), np.array([0.0320, 0.3700, 6.5500])), - (('2.5BG', 3.0, 24.0), np.array([0.0120, 0.3710, 6.5500])), - (('5BG', 3.0, 2.0), np.array([0.2742, 0.3192, 6.5500])), - (('5BG', 3.0, 4.0), np.array([0.2343, 0.3200, 6.5500])), - (('5BG', 3.0, 6.0), np.array([0.2020, 0.3188, 6.5500])), - (('5BG', 3.0, 8.0), np.array([0.1703, 0.3159, 6.5500])), - (('5BG', 3.0, 10.0), np.array([0.1410, 0.3118, 6.5500])), - (('5BG', 3.0, 12.0), np.array([0.1158, 0.3071, 6.5500])), - (('5BG', 3.0, 14.0), np.array([0.0940, 0.3027, 6.5500])), - (('5BG', 3.0, 16.0), np.array([0.0735, 0.2979, 6.5500])), - (('5BG', 3.0, 18.0), np.array([0.0580, 0.2940, 6.5500])), - (('5BG', 3.0, 20.0), np.array([0.0430, 0.2910, 6.5500])), - (('5BG', 3.0, 22.0), np.array([0.0280, 0.2860, 6.5500])), - (('7.5BG', 3.0, 2.0), np.array([0.2699, 0.3120, 6.5500])), - (('7.5BG', 3.0, 4.0), np.array([0.2272, 0.3041, 6.5500])), - (('7.5BG', 3.0, 6.0), np.array([0.1928, 0.2958, 6.5500])), - (('7.5BG', 3.0, 8.0), np.array([0.1620, 0.2872, 6.5500])), - (('7.5BG', 3.0, 10.0), np.array([0.1326, 0.2784, 6.5500])), - (('7.5BG', 3.0, 12.0), np.array([0.1086, 0.2706, 6.5500])), - (('7.5BG', 3.0, 14.0), np.array([0.0874, 0.2627, 6.5500])), - (('7.5BG', 3.0, 16.0), np.array([0.0691, 0.2559, 6.5500])), - (('7.5BG', 3.0, 18.0), np.array([0.0530, 0.2490, 6.5500])), - (('7.5BG', 3.0, 20.0), np.array([0.0390, 0.2420, 6.5500])), - (('10BG', 3.0, 2.0), np.array([0.2660, 0.3050, 6.5500])), - (('10BG', 3.0, 4.0), np.array([0.2221, 0.2886, 6.5500])), - (('10BG', 3.0, 6.0), np.array([0.1861, 0.2722, 6.5500])), - (('10BG', 3.0, 8.0), np.array([0.1551, 0.2571, 6.5500])), - (('10BG', 3.0, 10.0), np.array([0.1250, 0.2411, 6.5500])), - (('10BG', 3.0, 12.0), np.array([0.1018, 0.2281, 6.5500])), - (('10BG', 3.0, 14.0), np.array([0.0798, 0.2151, 6.5500])), - (('10BG', 3.0, 16.0), np.array([0.0650, 0.2060, 6.5500])), - (('10BG', 3.0, 18.0), np.array([0.0530, 0.1990, 6.5500])), - (('2.5B', 3.0, 2.0), np.array([0.2636, 0.2983, 6.5500])), - (('2.5B', 3.0, 4.0), np.array([0.2183, 0.2748, 6.5500])), - (('2.5B', 3.0, 6.0), np.array([0.1826, 0.2536, 6.5500])), - (('2.5B', 3.0, 8.0), np.array([0.1511, 0.2331, 6.5500])), - (('2.5B', 3.0, 10.0), np.array([0.1220, 0.2132, 6.5500])), - (('2.5B', 3.0, 12.0), np.array([0.0989, 0.1963, 6.5500])), - (('2.5B', 3.0, 14.0), np.array([0.0800, 0.1800, 6.5500])), - (('2.5B', 3.0, 16.0), np.array([0.0650, 0.1700, 6.5500])), - (('5B', 3.0, 2.0), np.array([0.2617, 0.2921, 6.5500])), - (('5B', 3.0, 4.0), np.array([0.2176, 0.2632, 6.5500])), - (('5B', 3.0, 6.0), np.array([0.1835, 0.2375, 6.5500])), - (('5B', 3.0, 8.0), np.array([0.1527, 0.2119, 6.5500])), - (('5B', 3.0, 10.0), np.array([0.1259, 0.1879, 6.5500])), - (('5B', 3.0, 12.0), np.array([0.1042, 0.1681, 6.5500])), - (('5B', 3.0, 14.0), np.array([0.0860, 0.1500, 6.5500])), - (('5B', 3.0, 16.0), np.array([0.0710, 0.1370, 6.5500])), - (('7.5B', 3.0, 2.0), np.array([0.2616, 0.2857, 6.5500])), - (('7.5B', 3.0, 4.0), np.array([0.2200, 0.2536, 6.5500])), - (('7.5B', 3.0, 6.0), np.array([0.1875, 0.2258, 6.5500])), - (('7.5B', 3.0, 8.0), np.array([0.1583, 0.1987, 6.5500])), - (('7.5B', 3.0, 10.0), np.array([0.1343, 0.1756, 6.5500])), - (('7.5B', 3.0, 12.0), np.array([0.1131, 0.1542, 6.5500])), - (('7.5B', 3.0, 14.0), np.array([0.0950, 0.1360, 6.5500])), - (('7.5B', 3.0, 16.0), np.array([0.0830, 0.1230, 6.5500])), - (('10B', 3.0, 2.0), np.array([0.2631, 0.2801, 6.5500])), - (('10B', 3.0, 4.0), np.array([0.2246, 0.2467, 6.5500])), - (('10B', 3.0, 6.0), np.array([0.1933, 0.2173, 6.5500])), - (('10B', 3.0, 8.0), np.array([0.1658, 0.1905, 6.5500])), - (('10B', 3.0, 10.0), np.array([0.1432, 0.1675, 6.5500])), - (('10B', 3.0, 12.0), np.array([0.1228, 0.1460, 6.5500])), - (('10B', 3.0, 14.0), np.array([0.1065, 0.1285, 6.5500])), - (('10B', 3.0, 16.0), np.array([0.0950, 0.1150, 6.5500])), - (('10B', 3.0, 18.0), np.array([0.0840, 0.1000, 6.5500])), - (('2.5PB', 3.0, 2.0), np.array([0.2663, 0.2756, 6.5500])), - (('2.5PB', 3.0, 4.0), np.array([0.2312, 0.2405, 6.5500])), - (('2.5PB', 3.0, 6.0), np.array([0.2022, 0.2101, 6.5500])), - (('2.5PB', 3.0, 8.0), np.array([0.1780, 0.1833, 6.5500])), - (('2.5PB', 3.0, 10.0), np.array([0.1576, 0.1600, 6.5500])), - (('2.5PB', 3.0, 12.0), np.array([0.1398, 0.1395, 6.5500])), - (('2.5PB', 3.0, 14.0), np.array([0.1251, 0.1218, 6.5500])), - (('2.5PB', 3.0, 16.0), np.array([0.1130, 0.1070, 6.5500])), - (('2.5PB', 3.0, 18.0), np.array([0.1020, 0.0930, 6.5500])), - (('2.5PB', 3.0, 20.0), np.array([0.0950, 0.0830, 6.5500])), - (('2.5PB', 3.0, 22.0), np.array([0.0880, 0.0730, 6.5500])), - (('5PB', 3.0, 2.0), np.array([0.2708, 0.2719, 6.5500])), - (('5PB', 3.0, 4.0), np.array([0.2393, 0.2361, 6.5500])), - (('5PB', 3.0, 6.0), np.array([0.2122, 0.2052, 6.5500])), - (('5PB', 3.0, 8.0), np.array([0.1908, 0.1799, 6.5500])), - (('5PB', 3.0, 10.0), np.array([0.1718, 0.1562, 6.5500])), - (('5PB', 3.0, 12.0), np.array([0.1557, 0.1356, 6.5500])), - (('5PB', 3.0, 14.0), np.array([0.1431, 0.1184, 6.5500])), - (('5PB', 3.0, 16.0), np.array([0.1318, 0.1024, 6.5500])), - (('5PB', 3.0, 18.0), np.array([0.1228, 0.0895, 6.5500])), - (('5PB', 3.0, 20.0), np.array([0.1170, 0.0800, 6.5500])), - (('5PB', 3.0, 22.0), np.array([0.1120, 0.0720, 6.5500])), - (('5PB', 3.0, 24.0), np.array([0.1070, 0.0640, 6.5500])), - (('5PB', 3.0, 26.0), np.array([0.1040, 0.0590, 6.5500])), - (('5PB', 3.0, 28.0), np.array([0.1020, 0.0540, 6.5500])), - (('5PB', 3.0, 30.0), np.array([0.0990, 0.0500, 6.5500])), - (('5PB', 3.0, 32.0), np.array([0.0970, 0.0450, 6.5500])), - (('5PB', 3.0, 34.0), np.array([0.0950, 0.0420, 6.5500])), - (('5PB', 3.0, 36.0), np.array([0.0940, 0.0400, 6.5500])), - (('5PB', 3.0, 38.0), np.array([0.0920, 0.0370, 6.5500])), - (('5PB', 3.0, 40.0), np.array([0.0910, 0.0340, 6.5500])), - (('5PB', 3.0, 42.0), np.array([0.0900, 0.0320, 6.5500])), - (('5PB', 3.0, 44.0), np.array([0.0890, 0.0300, 6.5500])), - (('5PB', 3.0, 46.0), np.array([0.0880, 0.0280, 6.5500])), - (('5PB', 3.0, 48.0), np.array([0.0870, 0.0250, 6.5500])), - (('5PB', 3.0, 50.0), np.array([0.0860, 0.0220, 6.5500])), - (('7.5PB', 3.0, 2.0), np.array([0.2777, 0.2687, 6.5500])), - (('7.5PB', 3.0, 4.0), np.array([0.2520, 0.2319, 6.5500])), - (('7.5PB', 3.0, 6.0), np.array([0.2311, 0.2010, 6.5500])), - (('7.5PB', 3.0, 8.0), np.array([0.2149, 0.1761, 6.5500])), - (('7.5PB', 3.0, 10.0), np.array([0.2005, 0.1536, 6.5500])), - (('7.5PB', 3.0, 12.0), np.array([0.1903, 0.1353, 6.5500])), - (('7.5PB', 3.0, 14.0), np.array([0.1824, 0.1188, 6.5500])), - (('7.5PB', 3.0, 16.0), np.array([0.1765, 0.1048, 6.5500])), - (('7.5PB', 3.0, 18.0), np.array([0.1730, 0.0948, 6.5500])), - (('7.5PB', 3.0, 20.0), np.array([0.1702, 0.0867, 6.5500])), - (('7.5PB', 3.0, 22.0), np.array([0.1677, 0.0782, 6.5500])), - (('7.5PB', 3.0, 24.0), np.array([0.1658, 0.0711, 6.5500])), - (('7.5PB', 3.0, 26.0), np.array([0.1642, 0.0655, 6.5500])), - (('7.5PB', 3.0, 28.0), np.array([0.1632, 0.0609, 6.5500])), - (('7.5PB', 3.0, 30.0), np.array([0.1621, 0.0556, 6.5500])), - (('7.5PB', 3.0, 32.0), np.array([0.1612, 0.0511, 6.5500])), - (('7.5PB', 3.0, 34.0), np.array([0.1608, 0.0480, 6.5500])), - (('7.5PB', 3.0, 36.0), np.array([0.1590, 0.0440, 6.5500])), - (('7.5PB', 3.0, 38.0), np.array([0.1580, 0.0400, 6.5500])), - (('7.5PB', 3.0, 40.0), np.array([0.1580, 0.0370, 6.5500])), - (('7.5PB', 3.0, 42.0), np.array([0.1570, 0.0340, 6.5500])), - (('7.5PB', 3.0, 44.0), np.array([0.1570, 0.0310, 6.5500])), - (('7.5PB', 3.0, 46.0), np.array([0.1570, 0.0280, 6.5500])), - (('7.5PB', 3.0, 48.0), np.array([0.1560, 0.0250, 6.5500])), - (('7.5PB', 3.0, 50.0), np.array([0.1560, 0.0220, 6.5500])), - (('10PB', 3.0, 2.0), np.array([0.2847, 0.2670, 6.5500])), - (('10PB', 3.0, 4.0), np.array([0.2660, 0.2319, 6.5500])), - (('10PB', 3.0, 6.0), np.array([0.2511, 0.2031, 6.5500])), - (('10PB', 3.0, 8.0), np.array([0.2387, 0.1786, 6.5500])), - (('10PB', 3.0, 10.0), np.array([0.2278, 0.1565, 6.5500])), - (('10PB', 3.0, 12.0), np.array([0.2206, 0.1407, 6.5500])), - (('10PB', 3.0, 14.0), np.array([0.2142, 0.1250, 6.5500])), - (('10PB', 3.0, 16.0), np.array([0.2092, 0.1118, 6.5500])), - (('10PB', 3.0, 18.0), np.array([0.2060, 0.1020, 6.5500])), - (('10PB', 3.0, 20.0), np.array([0.2030, 0.0930, 6.5500])), - (('10PB', 3.0, 22.0), np.array([0.2004, 0.0847, 6.5500])), - (('10PB', 3.0, 24.0), np.array([0.1982, 0.0772, 6.5500])), - (('10PB', 3.0, 26.0), np.array([0.1963, 0.0708, 6.5500])), - (('10PB', 3.0, 28.0), np.array([0.1950, 0.0650, 6.5500])), - (('10PB', 3.0, 30.0), np.array([0.1938, 0.0599, 6.5500])), - (('10PB', 3.0, 32.0), np.array([0.1926, 0.0542, 6.5500])), - (('10PB', 3.0, 34.0), np.array([0.1918, 0.0503, 6.5500])), - (('10PB', 3.0, 36.0), np.array([0.1900, 0.0460, 6.5500])), - (('10PB', 3.0, 38.0), np.array([0.1900, 0.0420, 6.5500])), - (('10PB', 3.0, 40.0), np.array([0.1890, 0.0380, 6.5500])), - (('10PB', 3.0, 42.0), np.array([0.1890, 0.0340, 6.5500])), - (('10PB', 3.0, 44.0), np.array([0.1880, 0.0310, 6.5500])), - (('10PB', 3.0, 46.0), np.array([0.1880, 0.0280, 6.5500])), - (('10PB', 3.0, 48.0), np.array([0.1880, 0.0250, 6.5500])), - (('10PB', 3.0, 50.0), np.array([0.1880, 0.0220, 6.5500])), - (('2.5P', 3.0, 2.0), np.array([0.2922, 0.2680, 6.5500])), - (('2.5P', 3.0, 4.0), np.array([0.2792, 0.2342, 6.5500])), - (('2.5P', 3.0, 6.0), np.array([0.2691, 0.2072, 6.5500])), - (('2.5P', 3.0, 8.0), np.array([0.2615, 0.1845, 6.5500])), - (('2.5P', 3.0, 10.0), np.array([0.2548, 0.1638, 6.5500])), - (('2.5P', 3.0, 12.0), np.array([0.2498, 0.1480, 6.5500])), - (('2.5P', 3.0, 14.0), np.array([0.2449, 0.1325, 6.5500])), - (('2.5P', 3.0, 16.0), np.array([0.2410, 0.1198, 6.5500])), - (('2.5P', 3.0, 18.0), np.array([0.2380, 0.1094, 6.5500])), - (('2.5P', 3.0, 20.0), np.array([0.2354, 0.1003, 6.5500])), - (('2.5P', 3.0, 22.0), np.array([0.2329, 0.0911, 6.5500])), - (('2.5P', 3.0, 24.0), np.array([0.2305, 0.0832, 6.5500])), - (('2.5P', 3.0, 26.0), np.array([0.2286, 0.0765, 6.5500])), - (('2.5P', 3.0, 28.0), np.array([0.2268, 0.0698, 6.5500])), - (('2.5P', 3.0, 30.0), np.array([0.2252, 0.0638, 6.5500])), - (('2.5P', 3.0, 32.0), np.array([0.2242, 0.0587, 6.5500])), - (('2.5P', 3.0, 34.0), np.array([0.2230, 0.0543, 6.5500])), - (('2.5P', 3.0, 36.0), np.array([0.2220, 0.0480, 6.5500])), - (('2.5P', 3.0, 38.0), np.array([0.2210, 0.0440, 6.5500])), - (('2.5P', 3.0, 40.0), np.array([0.2200, 0.0400, 6.5500])), - (('2.5P', 3.0, 42.0), np.array([0.2200, 0.0350, 6.5500])), - (('2.5P', 3.0, 44.0), np.array([0.2190, 0.0320, 6.5500])), - (('2.5P', 3.0, 46.0), np.array([0.2180, 0.0280, 6.5500])), - (('5P', 3.0, 2.0), np.array([0.2997, 0.2700, 6.5500])), - (('5P', 3.0, 4.0), np.array([0.2928, 0.2386, 6.5500])), - (('5P', 3.0, 6.0), np.array([0.2870, 0.2135, 6.5500])), - (('5P', 3.0, 8.0), np.array([0.2819, 0.1910, 6.5500])), - (('5P', 3.0, 10.0), np.array([0.2772, 0.1707, 6.5500])), - (('5P', 3.0, 12.0), np.array([0.2739, 0.1539, 6.5500])), - (('5P', 3.0, 14.0), np.array([0.2707, 0.1397, 6.5500])), - (('5P', 3.0, 16.0), np.array([0.2680, 0.1272, 6.5500])), - (('5P', 3.0, 18.0), np.array([0.2657, 0.1163, 6.5500])), - (('5P', 3.0, 20.0), np.array([0.2639, 0.1074, 6.5500])), - (('5P', 3.0, 22.0), np.array([0.2620, 0.0978, 6.5500])), - (('5P', 3.0, 24.0), np.array([0.2602, 0.0891, 6.5500])), - (('5P', 3.0, 26.0), np.array([0.2590, 0.0822, 6.5500])), - (('5P', 3.0, 28.0), np.array([0.2579, 0.0750, 6.5500])), - (('5P', 3.0, 30.0), np.array([0.2568, 0.0690, 6.5500])), - (('5P', 3.0, 32.0), np.array([0.2557, 0.0630, 6.5500])), - (('5P', 3.0, 34.0), np.array([0.2550, 0.0560, 6.5500])), - (('5P', 3.0, 36.0), np.array([0.2540, 0.0510, 6.5500])), - (('5P', 3.0, 38.0), np.array([0.2530, 0.0460, 6.5500])), - (('5P', 3.0, 40.0), np.array([0.2520, 0.0410, 6.5500])), - (('5P', 3.0, 42.0), np.array([0.2510, 0.0360, 6.5500])), - (('7.5P', 3.0, 2.0), np.array([0.3088, 0.2740, 6.5500])), - (('7.5P', 3.0, 4.0), np.array([0.3072, 0.2448, 6.5500])), - (('7.5P', 3.0, 6.0), np.array([0.3057, 0.2208, 6.5500])), - (('7.5P', 3.0, 8.0), np.array([0.3037, 0.1981, 6.5500])), - (('7.5P', 3.0, 10.0), np.array([0.3020, 0.1794, 6.5500])), - (('7.5P', 3.0, 12.0), np.array([0.3003, 0.1618, 6.5500])), - (('7.5P', 3.0, 14.0), np.array([0.2992, 0.1475, 6.5500])), - (('7.5P', 3.0, 16.0), np.array([0.2981, 0.1356, 6.5500])), - (('7.5P', 3.0, 18.0), np.array([0.2969, 0.1239, 6.5500])), - (('7.5P', 3.0, 20.0), np.array([0.2961, 0.1151, 6.5500])), - (('7.5P', 3.0, 22.0), np.array([0.2953, 0.1057, 6.5500])), - (('7.5P', 3.0, 24.0), np.array([0.2944, 0.0967, 6.5500])), - (('7.5P', 3.0, 26.0), np.array([0.2938, 0.0892, 6.5500])), - (('7.5P', 3.0, 28.0), np.array([0.2930, 0.0812, 6.5500])), - (('7.5P', 3.0, 30.0), np.array([0.2922, 0.0750, 6.5500])), - (('7.5P', 3.0, 32.0), np.array([0.2920, 0.0670, 6.5500])), - (('7.5P', 3.0, 34.0), np.array([0.2910, 0.0600, 6.5500])), - (('7.5P', 3.0, 36.0), np.array([0.2900, 0.0540, 6.5500])), - (('7.5P', 3.0, 38.0), np.array([0.2890, 0.0480, 6.5500])), - (('10P', 3.0, 2.0), np.array([0.3170, 0.2790, 6.5500])), - (('10P', 3.0, 4.0), np.array([0.3214, 0.2517, 6.5500])), - (('10P', 3.0, 6.0), np.array([0.3243, 0.2293, 6.5500])), - (('10P', 3.0, 8.0), np.array([0.3269, 0.2075, 6.5500])), - (('10P', 3.0, 10.0), np.array([0.3286, 0.1889, 6.5500])), - (('10P', 3.0, 12.0), np.array([0.3301, 0.1715, 6.5500])), - (('10P', 3.0, 14.0), np.array([0.3309, 0.1572, 6.5500])), - (('10P', 3.0, 16.0), np.array([0.3320, 0.1456, 6.5500])), - (('10P', 3.0, 18.0), np.array([0.3329, 0.1332, 6.5500])), - (('10P', 3.0, 20.0), np.array([0.3332, 0.1240, 6.5500])), - (('10P', 3.0, 22.0), np.array([0.3340, 0.1146, 6.5500])), - (('10P', 3.0, 24.0), np.array([0.3341, 0.1055, 6.5500])), - (('10P', 3.0, 26.0), np.array([0.3343, 0.0978, 6.5500])), - (('10P', 3.0, 28.0), np.array([0.3350, 0.0880, 6.5500])), - (('10P', 3.0, 30.0), np.array([0.3350, 0.0810, 6.5500])), - (('10P', 3.0, 32.0), np.array([0.3350, 0.0720, 6.5500])), - (('10P', 3.0, 34.0), np.array([0.3350, 0.0630, 6.5500])), - (('2.5RP', 3.0, 2.0), np.array([0.3272, 0.2861, 6.5500])), - (('2.5RP', 3.0, 4.0), np.array([0.3400, 0.2624, 6.5500])), - (('2.5RP', 3.0, 6.0), np.array([0.3501, 0.2425, 6.5500])), - (('2.5RP', 3.0, 8.0), np.array([0.3598, 0.2233, 6.5500])), - (('2.5RP', 3.0, 10.0), np.array([0.3681, 0.2054, 6.5500])), - (('2.5RP', 3.0, 12.0), np.array([0.3754, 0.1898, 6.5500])), - (('2.5RP', 3.0, 14.0), np.array([0.3818, 0.1758, 6.5500])), - (('2.5RP', 3.0, 16.0), np.array([0.3876, 0.1629, 6.5500])), - (('2.5RP', 3.0, 18.0), np.array([0.3929, 0.1506, 6.5500])), - (('2.5RP', 3.0, 20.0), np.array([0.3969, 0.1413, 6.5500])), - (('2.5RP', 3.0, 22.0), np.array([0.4018, 0.1304, 6.5500])), - (('2.5RP', 3.0, 24.0), np.array([0.4050, 0.1220, 6.5500])), - (('2.5RP', 3.0, 26.0), np.array([0.4080, 0.1140, 6.5500])), - (('2.5RP', 3.0, 28.0), np.array([0.4140, 0.1020, 6.5500])), - (('2.5RP', 3.0, 30.0), np.array([0.4170, 0.0940, 6.5500])), - (('5RP', 3.0, 2.0), np.array([0.3370, 0.2940, 6.5500])), - (('5RP', 3.0, 4.0), np.array([0.3586, 0.2742, 6.5500])), - (('5RP', 3.0, 6.0), np.array([0.3765, 0.2569, 6.5500])), - (('5RP', 3.0, 8.0), np.array([0.3930, 0.2395, 6.5500])), - (('5RP', 3.0, 10.0), np.array([0.4073, 0.2235, 6.5500])), - (('5RP', 3.0, 12.0), np.array([0.4199, 0.2089, 6.5500])), - (('5RP', 3.0, 14.0), np.array([0.4313, 0.1944, 6.5500])), - (('5RP', 3.0, 16.0), np.array([0.4418, 0.1809, 6.5500])), - (('5RP', 3.0, 18.0), np.array([0.4503, 0.1695, 6.5500])), - (('5RP', 3.0, 20.0), np.array([0.4577, 0.1593, 6.5500])), - (('5RP', 3.0, 22.0), np.array([0.4670, 0.1450, 6.5500])), - (('5RP', 3.0, 24.0), np.array([0.4720, 0.1360, 6.5500])), - (('5RP', 3.0, 26.0), np.array([0.4790, 0.1270, 6.5500])), - (('7.5RP', 3.0, 2.0), np.array([0.3450, 0.3001, 6.5500])), - (('7.5RP', 3.0, 4.0), np.array([0.3739, 0.2851, 6.5500])), - (('7.5RP', 3.0, 6.0), np.array([0.3990, 0.2708, 6.5500])), - (('7.5RP', 3.0, 8.0), np.array([0.4234, 0.2556, 6.5500])), - (('7.5RP', 3.0, 10.0), np.array([0.4445, 0.2419, 6.5500])), - (('7.5RP', 3.0, 12.0), np.array([0.4654, 0.2273, 6.5500])), - (('7.5RP', 3.0, 14.0), np.array([0.4831, 0.2140, 6.5500])), - (('7.5RP', 3.0, 16.0), np.array([0.4991, 0.2011, 6.5500])), - (('7.5RP', 3.0, 18.0), np.array([0.5130, 0.1893, 6.5500])), - (('7.5RP', 3.0, 20.0), np.array([0.5280, 0.1780, 6.5500])), - (('7.5RP', 3.0, 22.0), np.array([0.5420, 0.1650, 6.5500])), - (('10RP', 3.0, 2.0), np.array([0.3526, 0.3068, 6.5500])), - (('10RP', 3.0, 4.0), np.array([0.3889, 0.2969, 6.5500])), - (('10RP', 3.0, 6.0), np.array([0.4218, 0.2864, 6.5500])), - (('10RP', 3.0, 8.0), np.array([0.4552, 0.2741, 6.5500])), - (('10RP', 3.0, 10.0), np.array([0.4851, 0.2618, 6.5500])), - (('10RP', 3.0, 12.0), np.array([0.5139, 0.2489, 6.5500])), - (('10RP', 3.0, 14.0), np.array([0.5380, 0.2369, 6.5500])), - (('10RP', 3.0, 16.0), np.array([0.5628, 0.2241, 6.5500])), - (('10RP', 3.0, 18.0), np.array([0.5840, 0.2120, 6.5500])), - (('10RP', 3.0, 20.0), np.array([0.6020, 0.2000, 6.5500])), - (('2.5R', 3.0, 2.0), np.array([0.3591, 0.3130, 6.5500])), - (('2.5R', 3.0, 4.0), np.array([0.4021, 0.3076, 6.5500])), - (('2.5R', 3.0, 6.0), np.array([0.4409, 0.3009, 6.5500])), - (('2.5R', 3.0, 8.0), np.array([0.4821, 0.2918, 6.5500])), - (('2.5R', 3.0, 10.0), np.array([0.5191, 0.2811, 6.5500])), - (('2.5R', 3.0, 12.0), np.array([0.5536, 0.2691, 6.5500])), - (('2.5R', 3.0, 14.0), np.array([0.5828, 0.2579, 6.5500])), - (('2.5R', 3.0, 16.0), np.array([0.6116, 0.2456, 6.5500])), - (('2.5R', 3.0, 18.0), np.array([0.6400, 0.2320, 6.5500])), - (('2.5R', 3.0, 20.0), np.array([0.6670, 0.2170, 6.5500])), - (('5R', 3.0, 2.0), np.array([0.3645, 0.3190, 6.5500])), - (('5R', 3.0, 4.0), np.array([0.4148, 0.3190, 6.5500])), - (('5R', 3.0, 6.0), np.array([0.4592, 0.3168, 6.5500])), - (('5R', 3.0, 8.0), np.array([0.5064, 0.3114, 6.5500])), - (('5R', 3.0, 10.0), np.array([0.5500, 0.3024, 6.5500])), - (('5R', 3.0, 12.0), np.array([0.5884, 0.2904, 6.5500])), - (('5R', 3.0, 14.0), np.array([0.6204, 0.2789, 6.5500])), - (('5R', 3.0, 16.0), np.array([0.6520, 0.2660, 6.5500])), - (('5R', 3.0, 18.0), np.array([0.6820, 0.2510, 6.5500])), - (('5R', 3.0, 20.0), np.array([0.7100, 0.2340, 6.5500])), - (('7.5R', 3.0, 2.0), np.array([0.3690, 0.3248, 6.5500])), - (('7.5R', 3.0, 4.0), np.array([0.4240, 0.3302, 6.5500])), - (('7.5R', 3.0, 6.0), np.array([0.4738, 0.3316, 6.5500])), - (('7.5R', 3.0, 8.0), np.array([0.5251, 0.3297, 6.5500])), - (('7.5R', 3.0, 10.0), np.array([0.5730, 0.3240, 6.5500])), - (('7.5R', 3.0, 12.0), np.array([0.6158, 0.3129, 6.5500])), - (('7.5R', 3.0, 14.0), np.array([0.6492, 0.3012, 6.5500])), - (('7.5R', 3.0, 16.0), np.array([0.6817, 0.2872, 6.5500])), - (('7.5R', 3.0, 18.0), np.array([0.7140, 0.2710, 6.5500])), - (('7.5R', 3.0, 20.0), np.array([0.7470, 0.2510, 6.5500])), - (('10R', 3.0, 2.0), np.array([0.3728, 0.3314, 6.5500])), - (('10R', 3.0, 4.0), np.array([0.4308, 0.3412, 6.5500])), - (('10R', 3.0, 6.0), np.array([0.4854, 0.3467, 6.5500])), - (('10R', 3.0, 8.0), np.array([0.5393, 0.3477, 6.5500])), - (('10R', 3.0, 10.0), np.array([0.5871, 0.3440, 6.5500])), - (('10R', 3.0, 12.0), np.array([0.6322, 0.3361, 6.5500])), - (('10R', 3.0, 14.0), np.array([0.6703, 0.3249, 6.5500])), - (('10R', 3.0, 16.0), np.array([0.7030, 0.3140, 6.5500])), - (('10R', 3.0, 18.0), np.array([0.7390, 0.3020, 6.5500])), - (('10R', 3.0, 20.0), np.array([0.7780, 0.2860, 6.5500])), - (('2.5YR', 3.0, 2.0), np.array([0.3757, 0.3391, 6.5500])), - (('2.5YR', 3.0, 4.0), np.array([0.4360, 0.3563, 6.5500])), - (('2.5YR', 3.0, 6.0), np.array([0.4954, 0.3692, 6.5500])), - (('2.5YR', 3.0, 8.0), np.array([0.5475, 0.3771, 6.5500])), - (('2.5YR', 3.0, 10.0), np.array([0.5941, 0.3818, 6.5500])), - (('2.5YR', 3.0, 12.0), np.array([0.6370, 0.3810, 6.5500])), - (('2.5YR', 3.0, 14.0), np.array([0.6740, 0.3790, 6.5500])), - (('2.5YR', 3.0, 16.0), np.array([0.7080, 0.3740, 6.5500])), - (('5YR', 3.0, 2.0), np.array([0.3771, 0.3476, 6.5500])), - (('5YR', 3.0, 4.0), np.array([0.4376, 0.3715, 6.5500])), - (('5YR', 3.0, 6.0), np.array([0.4966, 0.3908, 6.5500])), - (('5YR', 3.0, 8.0), np.array([0.5456, 0.4040, 6.5500])), - (('5YR', 3.0, 10.0), np.array([0.5900, 0.4140, 6.5500])), - (('5YR', 3.0, 12.0), np.array([0.6290, 0.4230, 6.5500])), - (('7.5YR', 3.0, 2.0), np.array([0.3771, 0.3549, 6.5500])), - (('7.5YR', 3.0, 4.0), np.array([0.4378, 0.3865, 6.5500])), - (('7.5YR', 3.0, 6.0), np.array([0.4930, 0.4116, 6.5500])), - (('7.5YR', 3.0, 8.0), np.array([0.5390, 0.4306, 6.5500])), - (('7.5YR', 3.0, 10.0), np.array([0.5810, 0.4480, 6.5500])), - (('10YR', 3.0, 2.0), np.array([0.3747, 0.3630, 6.5500])), - (('10YR', 3.0, 4.0), np.array([0.4341, 0.4018, 6.5500])), - (('10YR', 3.0, 6.0), np.array([0.4872, 0.4326, 6.5500])), - (('10YR', 3.0, 8.0), np.array([0.5305, 0.4559, 6.5500])), - (('10YR', 3.0, 10.0), np.array([0.5720, 0.4750, 6.5500])), - (('2.5Y', 3.0, 2.0), np.array([0.3703, 0.3700, 6.5500])), - (('2.5Y', 3.0, 4.0), np.array([0.4277, 0.4166, 6.5500])), - (('2.5Y', 3.0, 6.0), np.array([0.4784, 0.4531, 6.5500])), - (('2.5Y', 3.0, 8.0), np.array([0.5210, 0.4820, 6.5500])), - (('2.5Y', 3.0, 10.0), np.array([0.5600, 0.5050, 6.5500])), - (('5Y', 3.0, 2.0), np.array([0.3646, 0.3748, 6.5500])), - (('5Y', 3.0, 4.0), np.array([0.4191, 0.4283, 6.5500])), - (('5Y', 3.0, 6.0), np.array([0.4670, 0.4711, 6.5500])), - (('5Y', 3.0, 8.0), np.array([0.5090, 0.5090, 6.5500])), - (('7.5Y', 3.0, 2.0), np.array([0.3589, 0.3778, 6.5500])), - (('7.5Y', 3.0, 4.0), np.array([0.4086, 0.4379, 6.5500])), - (('7.5Y', 3.0, 6.0), np.array([0.4526, 0.4889, 6.5500])), - (('7.5Y', 3.0, 8.0), np.array([0.4920, 0.5350, 6.5500])), - (('10Y', 4.0, 2.0), np.array([0.3476, 0.3732, 12.0000])), - (('10Y', 4.0, 4.0), np.array([0.3871, 0.4321, 12.0000])), - (('10Y', 4.0, 6.0), np.array([0.4190, 0.4795, 12.0000])), - (('10Y', 4.0, 8.0), np.array([0.4430, 0.5153, 12.0000])), - (('10Y', 4.0, 10.0), np.array([0.4620, 0.5430, 12.0000])), - (('10Y', 4.0, 12.0), np.array([0.4730, 0.5620, 12.0000])), - (('2.5GY', 4.0, 2.0), np.array([0.3382, 0.3706, 12.0000])), - (('2.5GY', 4.0, 4.0), np.array([0.3708, 0.4329, 12.0000])), - (('2.5GY', 4.0, 6.0), np.array([0.3968, 0.4857, 12.0000])), - (('2.5GY', 4.0, 8.0), np.array([0.4174, 0.5300, 12.0000])), - (('2.5GY', 4.0, 10.0), np.array([0.4330, 0.5680, 12.0000])), - (('2.5GY', 4.0, 12.0), np.array([0.4430, 0.5940, 12.0000])), - (('5GY', 4.0, 2.0), np.array([0.3312, 0.3678, 12.0000])), - (('5GY', 4.0, 4.0), np.array([0.3538, 0.4284, 12.0000])), - (('5GY', 4.0, 6.0), np.array([0.3718, 0.4852, 12.0000])), - (('5GY', 4.0, 8.0), np.array([0.3868, 0.5384, 12.0000])), - (('5GY', 4.0, 10.0), np.array([0.3983, 0.5850, 12.0000])), - (('5GY', 4.0, 12.0), np.array([0.4070, 0.6190, 12.0000])), - (('5GY', 4.0, 14.0), np.array([0.4150, 0.6590, 12.0000])), - (('7.5GY', 4.0, 2.0), np.array([0.3185, 0.3604, 12.0000])), - (('7.5GY', 4.0, 4.0), np.array([0.3281, 0.4157, 12.0000])), - (('7.5GY', 4.0, 6.0), np.array([0.3355, 0.4739, 12.0000])), - (('7.5GY', 4.0, 8.0), np.array([0.3400, 0.5348, 12.0000])), - (('7.5GY', 4.0, 10.0), np.array([0.3395, 0.5913, 12.0000])), - (('7.5GY', 4.0, 12.0), np.array([0.3348, 0.6468, 12.0000])), - (('7.5GY', 4.0, 14.0), np.array([0.3270, 0.6980, 12.0000])), - (('7.5GY', 4.0, 16.0), np.array([0.3150, 0.7570, 12.0000])), - (('7.5GY', 4.0, 18.0), np.array([0.3030, 0.8090, 12.0000])), - (('10GY', 4.0, 2.0), np.array([0.3109, 0.3550, 12.0000])), - (('10GY', 4.0, 4.0), np.array([0.3100, 0.4018, 12.0000])), - (('10GY', 4.0, 6.0), np.array([0.3069, 0.4550, 12.0000])), - (('10GY', 4.0, 8.0), np.array([0.3008, 0.5095, 12.0000])), - (('10GY', 4.0, 10.0), np.array([0.2908, 0.5672, 12.0000])), - (('10GY', 4.0, 12.0), np.array([0.2758, 0.6282, 12.0000])), - (('10GY', 4.0, 14.0), np.array([0.2590, 0.6858, 12.0000])), - (('10GY', 4.0, 16.0), np.array([0.2422, 0.7360, 12.0000])), - (('10GY', 4.0, 18.0), np.array([0.2210, 0.7930, 12.0000])), - (('10GY', 4.0, 20.0), np.array([0.1920, 0.8600, 12.0000])), - (('10GY', 4.0, 22.0), np.array([0.1650, 0.9170, 12.0000])), - (('10GY', 4.0, 24.0), np.array([0.1420, 0.9640, 12.0000])), - (('10GY', 4.0, 26.0), np.array([0.1060, 1.0280, 12.0000])), - (('10GY', 4.0, 28.0), np.array([0.0580, 1.1000, 12.0000])), - (('2.5G', 4.0, 2.0), np.array([0.3012, 0.3470, 12.0000])), - (('2.5G', 4.0, 4.0), np.array([0.2891, 0.3821, 12.0000])), - (('2.5G', 4.0, 6.0), np.array([0.2735, 0.4215, 12.0000])), - (('2.5G', 4.0, 8.0), np.array([0.2561, 0.4597, 12.0000])), - (('2.5G', 4.0, 10.0), np.array([0.2355, 0.5006, 12.0000])), - (('2.5G', 4.0, 12.0), np.array([0.2128, 0.5425, 12.0000])), - (('2.5G', 4.0, 14.0), np.array([0.1909, 0.5779, 12.0000])), - (('2.5G', 4.0, 16.0), np.array([0.1682, 0.6111, 12.0000])), - (('2.5G', 4.0, 18.0), np.array([0.1446, 0.6431, 12.0000])), - (('2.5G', 4.0, 20.0), np.array([0.1230, 0.6706, 12.0000])), - (('2.5G', 4.0, 22.0), np.array([0.1009, 0.6975, 12.0000])), - (('2.5G', 4.0, 24.0), np.array([0.0760, 0.7250, 12.0000])), - (('2.5G', 4.0, 26.0), np.array([0.0528, 0.7502, 12.0000])), - (('2.5G', 4.0, 28.0), np.array([0.0280, 0.7800, 12.0000])), - (('2.5G', 4.0, 30.0), np.array([-0.0050, 0.8160, 12.0000])), - (('5G', 4.0, 2.0), np.array([0.2959, 0.3417, 12.0000])), - (('5G', 4.0, 4.0), np.array([0.2781, 0.3704, 12.0000])), - (('5G', 4.0, 6.0), np.array([0.2581, 0.3992, 12.0000])), - (('5G', 4.0, 8.0), np.array([0.2359, 0.4266, 12.0000])), - (('5G', 4.0, 10.0), np.array([0.2115, 0.4532, 12.0000])), - (('5G', 4.0, 12.0), np.array([0.1843, 0.4807, 12.0000])), - (('5G', 4.0, 14.0), np.array([0.1627, 0.5015, 12.0000])), - (('5G', 4.0, 16.0), np.array([0.1402, 0.5214, 12.0000])), - (('5G', 4.0, 18.0), np.array([0.1188, 0.5400, 12.0000])), - (('5G', 4.0, 20.0), np.array([0.1018, 0.5543, 12.0000])), - (('5G', 4.0, 22.0), np.array([0.0841, 0.5684, 12.0000])), - (('5G', 4.0, 24.0), np.array([0.0614, 0.5857, 12.0000])), - (('5G', 4.0, 26.0), np.array([0.0407, 0.6010, 12.0000])), - (('5G', 4.0, 28.0), np.array([0.0200, 0.6180, 12.0000])), - (('5G', 4.0, 30.0), np.array([-0.0030, 0.6320, 12.0000])), - (('7.5G', 4.0, 2.0), np.array([0.2919, 0.3371, 12.0000])), - (('7.5G', 4.0, 4.0), np.array([0.2702, 0.3602, 12.0000])), - (('7.5G', 4.0, 6.0), np.array([0.2467, 0.3822, 12.0000])), - (('7.5G', 4.0, 8.0), np.array([0.2232, 0.4022, 12.0000])), - (('7.5G', 4.0, 10.0), np.array([0.1989, 0.4219, 12.0000])), - (('7.5G', 4.0, 12.0), np.array([0.1706, 0.4419, 12.0000])), - (('7.5G', 4.0, 14.0), np.array([0.1500, 0.4562, 12.0000])), - (('7.5G', 4.0, 16.0), np.array([0.1293, 0.4703, 12.0000])), - (('7.5G', 4.0, 18.0), np.array([0.1086, 0.4842, 12.0000])), - (('7.5G', 4.0, 20.0), np.array([0.0928, 0.4942, 12.0000])), - (('7.5G', 4.0, 22.0), np.array([0.0770, 0.5040, 12.0000])), - (('7.5G', 4.0, 24.0), np.array([0.0581, 0.5151, 12.0000])), - (('7.5G', 4.0, 26.0), np.array([0.0392, 0.5258, 12.0000])), - (('7.5G', 4.0, 28.0), np.array([0.0200, 0.5360, 12.0000])), - (('7.5G', 4.0, 30.0), np.array([0.0020, 0.5460, 12.0000])), - (('10G', 4.0, 2.0), np.array([0.2880, 0.3327, 12.0000])), - (('10G', 4.0, 4.0), np.array([0.2628, 0.3498, 12.0000])), - (('10G', 4.0, 6.0), np.array([0.2374, 0.3655, 12.0000])), - (('10G', 4.0, 8.0), np.array([0.2124, 0.3799, 12.0000])), - (('10G', 4.0, 10.0), np.array([0.1876, 0.3933, 12.0000])), - (('10G', 4.0, 12.0), np.array([0.1602, 0.4070, 12.0000])), - (('10G', 4.0, 14.0), np.array([0.1398, 0.4168, 12.0000])), - (('10G', 4.0, 16.0), np.array([0.1212, 0.4245, 12.0000])), - (('10G', 4.0, 18.0), np.array([0.1006, 0.4330, 12.0000])), - (('10G', 4.0, 20.0), np.array([0.0850, 0.4388, 12.0000])), - (('10G', 4.0, 22.0), np.array([0.0702, 0.4440, 12.0000])), - (('10G', 4.0, 24.0), np.array([0.0553, 0.4492, 12.0000])), - (('10G', 4.0, 26.0), np.array([0.0400, 0.4545, 12.0000])), - (('10G', 4.0, 28.0), np.array([0.0230, 0.4600, 12.0000])), - (('10G', 4.0, 30.0), np.array([0.0080, 0.4650, 12.0000])), - (('2.5BG', 4.0, 2.0), np.array([0.2840, 0.3270, 12.0000])), - (('2.5BG', 4.0, 4.0), np.array([0.2552, 0.3375, 12.0000])), - (('2.5BG', 4.0, 6.0), np.array([0.2278, 0.3463, 12.0000])), - (('2.5BG', 4.0, 8.0), np.array([0.2006, 0.3540, 12.0000])), - (('2.5BG', 4.0, 10.0), np.array([0.1738, 0.3600, 12.0000])), - (('2.5BG', 4.0, 12.0), np.array([0.1492, 0.3649, 12.0000])), - (('2.5BG', 4.0, 14.0), np.array([0.1283, 0.3688, 12.0000])), - (('2.5BG', 4.0, 16.0), np.array([0.1102, 0.3720, 12.0000])), - (('2.5BG', 4.0, 18.0), np.array([0.0915, 0.3754, 12.0000])), - (('2.5BG', 4.0, 20.0), np.array([0.0768, 0.3773, 12.0000])), - (('2.5BG', 4.0, 22.0), np.array([0.0636, 0.3788, 12.0000])), - (('2.5BG', 4.0, 24.0), np.array([0.0510, 0.3800, 12.0000])), - (('2.5BG', 4.0, 26.0), np.array([0.0380, 0.3820, 12.0000])), - (('2.5BG', 4.0, 28.0), np.array([0.0250, 0.3830, 12.0000])), - (('5BG', 4.0, 2.0), np.array([0.2799, 0.3208, 12.0000])), - (('5BG', 4.0, 4.0), np.array([0.2480, 0.3232, 12.0000])), - (('5BG', 4.0, 6.0), np.array([0.2182, 0.3240, 12.0000])), - (('5BG', 4.0, 8.0), np.array([0.1890, 0.3234, 12.0000])), - (('5BG', 4.0, 10.0), np.array([0.1618, 0.3219, 12.0000])), - (('5BG', 4.0, 12.0), np.array([0.1379, 0.3198, 12.0000])), - (('5BG', 4.0, 14.0), np.array([0.1170, 0.3170, 12.0000])), - (('5BG', 4.0, 16.0), np.array([0.0992, 0.3141, 12.0000])), - (('5BG', 4.0, 18.0), np.array([0.0828, 0.3108, 12.0000])), - (('5BG', 4.0, 20.0), np.array([0.0675, 0.3075, 12.0000])), - (('5BG', 4.0, 22.0), np.array([0.0560, 0.3050, 12.0000])), - (('5BG', 4.0, 24.0), np.array([0.0470, 0.3040, 12.0000])), - (('5BG', 4.0, 26.0), np.array([0.0360, 0.3030, 12.0000])), - (('7.5BG', 4.0, 2.0), np.array([0.2764, 0.3148, 12.0000])), - (('7.5BG', 4.0, 4.0), np.array([0.2429, 0.3108, 12.0000])), - (('7.5BG', 4.0, 6.0), np.array([0.2113, 0.3052, 12.0000])), - (('7.5BG', 4.0, 8.0), np.array([0.1815, 0.2985, 12.0000])), - (('7.5BG', 4.0, 10.0), np.array([0.1540, 0.2910, 12.0000])), - (('7.5BG', 4.0, 12.0), np.array([0.1298, 0.2840, 12.0000])), - (('7.5BG', 4.0, 14.0), np.array([0.1092, 0.2774, 12.0000])), - (('7.5BG', 4.0, 16.0), np.array([0.0922, 0.2718, 12.0000])), - (('7.5BG', 4.0, 18.0), np.array([0.0768, 0.2667, 12.0000])), - (('7.5BG', 4.0, 20.0), np.array([0.0650, 0.2620, 12.0000])), - (('7.5BG', 4.0, 22.0), np.array([0.0540, 0.2580, 12.0000])), - (('7.5BG', 4.0, 24.0), np.array([0.0450, 0.2550, 12.0000])), - (('10BG', 4.0, 2.0), np.array([0.2740, 0.3091, 12.0000])), - (('10BG', 4.0, 4.0), np.array([0.2384, 0.2984, 12.0000])), - (('10BG', 4.0, 6.0), np.array([0.2065, 0.2863, 12.0000])), - (('10BG', 4.0, 8.0), np.array([0.1760, 0.2730, 12.0000])), - (('10BG', 4.0, 10.0), np.array([0.1480, 0.2600, 12.0000])), - (('10BG', 4.0, 12.0), np.array([0.1248, 0.2484, 12.0000])), - (('10BG', 4.0, 14.0), np.array([0.1033, 0.2376, 12.0000])), - (('10BG', 4.0, 16.0), np.array([0.0888, 0.2298, 12.0000])), - (('10BG', 4.0, 18.0), np.array([0.0730, 0.2210, 12.0000])), - (('10BG', 4.0, 20.0), np.array([0.0620, 0.2140, 12.0000])), - (('10BG', 4.0, 22.0), np.array([0.0510, 0.2070, 12.0000])), - (('2.5B', 4.0, 2.0), np.array([0.2727, 0.3038, 12.0000])), - (('2.5B', 4.0, 4.0), np.array([0.2360, 0.2872, 12.0000])), - (('2.5B', 4.0, 6.0), np.array([0.2048, 0.2708, 12.0000])), - (('2.5B', 4.0, 8.0), np.array([0.1737, 0.2524, 12.0000])), - (('2.5B', 4.0, 10.0), np.array([0.1463, 0.2354, 12.0000])), - (('2.5B', 4.0, 12.0), np.array([0.1247, 0.2209, 12.0000])), - (('2.5B', 4.0, 14.0), np.array([0.1027, 0.2057, 12.0000])), - (('2.5B', 4.0, 16.0), np.array([0.0900, 0.1973, 12.0000])), - (('2.5B', 4.0, 18.0), np.array([0.0730, 0.1840, 12.0000])), - (('2.5B', 4.0, 20.0), np.array([0.0620, 0.1770, 12.0000])), - (('5B', 4.0, 2.0), np.array([0.2723, 0.2992, 12.0000])), - (('5B', 4.0, 4.0), np.array([0.2363, 0.2782, 12.0000])), - (('5B', 4.0, 6.0), np.array([0.2060, 0.2572, 12.0000])), - (('5B', 4.0, 8.0), np.array([0.1759, 0.2345, 12.0000])), - (('5B', 4.0, 10.0), np.array([0.1512, 0.2148, 12.0000])), - (('5B', 4.0, 12.0), np.array([0.1299, 0.1963, 12.0000])), - (('5B', 4.0, 14.0), np.array([0.1098, 0.1785, 12.0000])), - (('5B', 4.0, 16.0), np.array([0.0940, 0.1630, 12.0000])), - (('5B', 4.0, 18.0), np.array([0.0790, 0.1500, 12.0000])), - (('7.5B', 4.0, 2.0), np.array([0.2733, 0.2947, 12.0000])), - (('7.5B', 4.0, 4.0), np.array([0.2388, 0.2704, 12.0000])), - (('7.5B', 4.0, 6.0), np.array([0.2102, 0.2470, 12.0000])), - (('7.5B', 4.0, 8.0), np.array([0.1821, 0.2232, 12.0000])), - (('7.5B', 4.0, 10.0), np.array([0.1601, 0.2028, 12.0000])), - (('7.5B', 4.0, 12.0), np.array([0.1393, 0.1837, 12.0000])), - (('7.5B', 4.0, 14.0), np.array([0.1204, 0.1655, 12.0000])), - (('7.5B', 4.0, 16.0), np.array([0.1020, 0.1490, 12.0000])), - (('7.5B', 4.0, 18.0), np.array([0.0910, 0.1380, 12.0000])), - (('10B', 4.0, 2.0), np.array([0.2753, 0.2910, 12.0000])), - (('10B', 4.0, 4.0), np.array([0.2429, 0.2648, 12.0000])), - (('10B', 4.0, 6.0), np.array([0.2157, 0.2407, 12.0000])), - (('10B', 4.0, 8.0), np.array([0.1893, 0.2160, 12.0000])), - (('10B', 4.0, 10.0), np.array([0.1681, 0.1954, 12.0000])), - (('10B', 4.0, 12.0), np.array([0.1487, 0.1760, 12.0000])), - (('10B', 4.0, 14.0), np.array([0.1310, 0.1580, 12.0000])), - (('10B', 4.0, 16.0), np.array([0.1155, 0.1416, 12.0000])), - (('10B', 4.0, 18.0), np.array([0.1030, 0.1280, 12.0000])), - (('10B', 4.0, 20.0), np.array([0.0920, 0.1170, 12.0000])), - (('2.5PB', 4.0, 2.0), np.array([0.2782, 0.2876, 12.0000])), - (('2.5PB', 4.0, 4.0), np.array([0.2487, 0.2597, 12.0000])), - (('2.5PB', 4.0, 6.0), np.array([0.2235, 0.2343, 12.0000])), - (('2.5PB', 4.0, 8.0), np.array([0.1995, 0.2094, 12.0000])), - (('2.5PB', 4.0, 10.0), np.array([0.1805, 0.1888, 12.0000])), - (('2.5PB', 4.0, 12.0), np.array([0.1634, 0.1698, 12.0000])), - (('2.5PB', 4.0, 14.0), np.array([0.1473, 0.1513, 12.0000])), - (('2.5PB', 4.0, 16.0), np.array([0.1336, 0.1349, 12.0000])), - (('2.5PB', 4.0, 18.0), np.array([0.1218, 0.1208, 12.0000])), - (('2.5PB', 4.0, 20.0), np.array([0.1120, 0.1080, 12.0000])), - (('2.5PB', 4.0, 22.0), np.array([0.1040, 0.0960, 12.0000])), - (('2.5PB', 4.0, 24.0), np.array([0.0980, 0.0890, 12.0000])), - (('5PB', 4.0, 2.0), np.array([0.2816, 0.2842, 12.0000])), - (('5PB', 4.0, 4.0), np.array([0.2562, 0.2560, 12.0000])), - (('5PB', 4.0, 6.0), np.array([0.2325, 0.2300, 12.0000])), - (('5PB', 4.0, 8.0), np.array([0.2103, 0.2050, 12.0000])), - (('5PB', 4.0, 10.0), np.array([0.1925, 0.1843, 12.0000])), - (('5PB', 4.0, 12.0), np.array([0.1773, 0.1659, 12.0000])), - (('5PB', 4.0, 14.0), np.array([0.1627, 0.1479, 12.0000])), - (('5PB', 4.0, 16.0), np.array([0.1504, 0.1317, 12.0000])), - (('5PB', 4.0, 18.0), np.array([0.1392, 0.1167, 12.0000])), - (('5PB', 4.0, 20.0), np.array([0.1288, 0.1027, 12.0000])), - (('5PB', 4.0, 22.0), np.array([0.1220, 0.0920, 12.0000])), - (('5PB', 4.0, 24.0), np.array([0.1180, 0.0840, 12.0000])), - (('5PB', 4.0, 26.0), np.array([0.1150, 0.0780, 12.0000])), - (('5PB', 4.0, 28.0), np.array([0.1110, 0.0730, 12.0000])), - (('5PB', 4.0, 30.0), np.array([0.1080, 0.0680, 12.0000])), - (('5PB', 4.0, 32.0), np.array([0.1060, 0.0640, 12.0000])), - (('5PB', 4.0, 34.0), np.array([0.1040, 0.0600, 12.0000])), - (('5PB', 4.0, 36.0), np.array([0.1020, 0.0550, 12.0000])), - (('5PB', 4.0, 38.0), np.array([0.0980, 0.0480, 12.0000])), - (('5PB', 4.0, 40.0), np.array([0.0960, 0.0440, 12.0000])), - (('7.5PB', 4.0, 2.0), np.array([0.2861, 0.2819, 12.0000])), - (('7.5PB', 4.0, 4.0), np.array([0.2657, 0.2528, 12.0000])), - (('7.5PB', 4.0, 6.0), np.array([0.2471, 0.2266, 12.0000])), - (('7.5PB', 4.0, 8.0), np.array([0.2304, 0.2023, 12.0000])), - (('7.5PB', 4.0, 10.0), np.array([0.2158, 0.1811, 12.0000])), - (('7.5PB', 4.0, 12.0), np.array([0.2037, 0.1629, 12.0000])), - (('7.5PB', 4.0, 14.0), np.array([0.1941, 0.1468, 12.0000])), - (('7.5PB', 4.0, 16.0), np.array([0.1861, 0.1316, 12.0000])), - (('7.5PB', 4.0, 18.0), np.array([0.1798, 0.1185, 12.0000])), - (('7.5PB', 4.0, 20.0), np.array([0.1742, 0.1058, 12.0000])), - (('7.5PB', 4.0, 22.0), np.array([0.1713, 0.0980, 12.0000])), - (('7.5PB', 4.0, 24.0), np.array([0.1684, 0.0899, 12.0000])), - (('7.5PB', 4.0, 26.0), np.array([0.1659, 0.0825, 12.0000])), - (('7.5PB', 4.0, 28.0), np.array([0.1640, 0.0770, 12.0000])), - (('7.5PB', 4.0, 30.0), np.array([0.1620, 0.0720, 12.0000])), - (('7.5PB', 4.0, 32.0), np.array([0.1600, 0.0660, 12.0000])), - (('7.5PB', 4.0, 34.0), np.array([0.1580, 0.0600, 12.0000])), - (('7.5PB', 4.0, 36.0), np.array([0.1570, 0.0540, 12.0000])), - (('7.5PB', 4.0, 38.0), np.array([0.1550, 0.0490, 12.0000])), - (('7.5PB', 4.0, 40.0), np.array([0.1530, 0.0440, 12.0000])), - (('10PB', 4.0, 2.0), np.array([0.2911, 0.2804, 12.0000])), - (('10PB', 4.0, 4.0), np.array([0.2759, 0.2522, 12.0000])), - (('10PB', 4.0, 6.0), np.array([0.2618, 0.2263, 12.0000])), - (('10PB', 4.0, 8.0), np.array([0.2497, 0.2038, 12.0000])), - (('10PB', 4.0, 10.0), np.array([0.2388, 0.1837, 12.0000])), - (('10PB', 4.0, 12.0), np.array([0.2298, 0.1659, 12.0000])), - (('10PB', 4.0, 14.0), np.array([0.2220, 0.1503, 12.0000])), - (('10PB', 4.0, 16.0), np.array([0.2170, 0.1373, 12.0000])), - (('10PB', 4.0, 18.0), np.array([0.2120, 0.1256, 12.0000])), - (('10PB', 4.0, 20.0), np.array([0.2075, 0.1140, 12.0000])), - (('10PB', 4.0, 22.0), np.array([0.2048, 0.1064, 12.0000])), - (('10PB', 4.0, 24.0), np.array([0.2020, 0.0985, 12.0000])), - (('10PB', 4.0, 26.0), np.array([0.1994, 0.0904, 12.0000])), - (('10PB', 4.0, 28.0), np.array([0.1971, 0.0840, 12.0000])), - (('10PB', 4.0, 30.0), np.array([0.1952, 0.0778, 12.0000])), - (('10PB', 4.0, 32.0), np.array([0.1920, 0.0710, 12.0000])), - (('10PB', 4.0, 34.0), np.array([0.1910, 0.0640, 12.0000])), - (('10PB', 4.0, 36.0), np.array([0.1890, 0.0580, 12.0000])), - (('10PB', 4.0, 38.0), np.array([0.1870, 0.0520, 12.0000])), - (('10PB', 4.0, 40.0), np.array([0.1840, 0.0460, 12.0000])), - (('2.5P', 4.0, 2.0), np.array([0.2962, 0.2807, 12.0000])), - (('2.5P', 4.0, 4.0), np.array([0.2855, 0.2531, 12.0000])), - (('2.5P', 4.0, 6.0), np.array([0.2763, 0.2300, 12.0000])), - (('2.5P', 4.0, 8.0), np.array([0.2685, 0.2089, 12.0000])), - (('2.5P', 4.0, 10.0), np.array([0.2619, 0.1903, 12.0000])), - (('2.5P', 4.0, 12.0), np.array([0.2559, 0.1730, 12.0000])), - (('2.5P', 4.0, 14.0), np.array([0.2509, 0.1585, 12.0000])), - (('2.5P', 4.0, 16.0), np.array([0.2467, 0.1452, 12.0000])), - (('2.5P', 4.0, 18.0), np.array([0.2430, 0.1332, 12.0000])), - (('2.5P', 4.0, 20.0), np.array([0.2394, 0.1221, 12.0000])), - (('2.5P', 4.0, 22.0), np.array([0.2371, 0.1143, 12.0000])), - (('2.5P', 4.0, 24.0), np.array([0.2348, 0.1062, 12.0000])), - (('2.5P', 4.0, 26.0), np.array([0.2322, 0.0978, 12.0000])), - (('2.5P', 4.0, 28.0), np.array([0.2302, 0.0909, 12.0000])), - (('2.5P', 4.0, 30.0), np.array([0.2285, 0.0847, 12.0000])), - (('2.5P', 4.0, 32.0), np.array([0.2265, 0.0774, 12.0000])), - (('2.5P', 4.0, 34.0), np.array([0.2240, 0.0700, 12.0000])), - (('2.5P', 4.0, 36.0), np.array([0.2220, 0.0640, 12.0000])), - (('2.5P', 4.0, 38.0), np.array([0.2200, 0.0570, 12.0000])), - (('2.5P', 4.0, 40.0), np.array([0.2180, 0.0510, 12.0000])), - (('5P', 4.0, 2.0), np.array([0.3022, 0.2825, 12.0000])), - (('5P', 4.0, 4.0), np.array([0.2958, 0.2565, 12.0000])), - (('5P', 4.0, 6.0), np.array([0.2903, 0.2347, 12.0000])), - (('5P', 4.0, 8.0), np.array([0.2855, 0.2150, 12.0000])), - (('5P', 4.0, 10.0), np.array([0.2814, 0.1967, 12.0000])), - (('5P', 4.0, 12.0), np.array([0.2778, 0.1808, 12.0000])), - (('5P', 4.0, 14.0), np.array([0.2747, 0.1660, 12.0000])), - (('5P', 4.0, 16.0), np.array([0.2718, 0.1520, 12.0000])), - (('5P', 4.0, 18.0), np.array([0.2693, 0.1408, 12.0000])), - (('5P', 4.0, 20.0), np.array([0.2670, 0.1300, 12.0000])), - (('5P', 4.0, 22.0), np.array([0.2652, 0.1218, 12.0000])), - (('5P', 4.0, 24.0), np.array([0.2635, 0.1132, 12.0000])), - (('5P', 4.0, 26.0), np.array([0.2618, 0.1052, 12.0000])), - (('5P', 4.0, 28.0), np.array([0.2600, 0.0971, 12.0000])), - (('5P', 4.0, 30.0), np.array([0.2588, 0.0907, 12.0000])), - (('5P', 4.0, 32.0), np.array([0.2574, 0.0833, 12.0000])), - (('5P', 4.0, 34.0), np.array([0.2560, 0.0740, 12.0000])), - (('5P', 4.0, 36.0), np.array([0.2550, 0.0690, 12.0000])), - (('5P', 4.0, 38.0), np.array([0.2540, 0.0630, 12.0000])), - (('5P', 4.0, 40.0), np.array([0.2540, 0.0560, 12.0000])), - (('7.5P', 4.0, 2.0), np.array([0.3093, 0.2859, 12.0000])), - (('7.5P', 4.0, 4.0), np.array([0.3084, 0.2622, 12.0000])), - (('7.5P', 4.0, 6.0), np.array([0.3076, 0.2416, 12.0000])), - (('7.5P', 4.0, 8.0), np.array([0.3066, 0.2228, 12.0000])), - (('7.5P', 4.0, 10.0), np.array([0.3056, 0.2060, 12.0000])), - (('7.5P', 4.0, 12.0), np.array([0.3045, 0.1905, 12.0000])), - (('7.5P', 4.0, 14.0), np.array([0.3035, 0.1755, 12.0000])), - (('7.5P', 4.0, 16.0), np.array([0.3028, 0.1621, 12.0000])), - (('7.5P', 4.0, 18.0), np.array([0.3016, 0.1500, 12.0000])), - (('7.5P', 4.0, 20.0), np.array([0.3010, 0.1396, 12.0000])), - (('7.5P', 4.0, 22.0), np.array([0.3001, 0.1306, 12.0000])), - (('7.5P', 4.0, 24.0), np.array([0.2993, 0.1225, 12.0000])), - (('7.5P', 4.0, 26.0), np.array([0.2986, 0.1135, 12.0000])), - (('7.5P', 4.0, 28.0), np.array([0.2979, 0.1062, 12.0000])), - (('7.5P', 4.0, 30.0), np.array([0.2969, 0.0979, 12.0000])), - (('7.5P', 4.0, 32.0), np.array([0.2962, 0.0906, 12.0000])), - (('7.5P', 4.0, 34.0), np.array([0.2950, 0.0820, 12.0000])), - (('7.5P', 4.0, 36.0), np.array([0.2950, 0.0750, 12.0000])), - (('7.5P', 4.0, 38.0), np.array([0.2940, 0.0690, 12.0000])), - (('7.5P', 4.0, 40.0), np.array([0.2930, 0.0620, 12.0000])), - (('10P', 4.0, 2.0), np.array([0.3162, 0.2902, 12.0000])), - (('10P', 4.0, 4.0), np.array([0.3210, 0.2686, 12.0000])), - (('10P', 4.0, 6.0), np.array([0.3248, 0.2493, 12.0000])), - (('10P', 4.0, 8.0), np.array([0.3280, 0.2318, 12.0000])), - (('10P', 4.0, 10.0), np.array([0.3306, 0.2162, 12.0000])), - (('10P', 4.0, 12.0), np.array([0.3331, 0.2014, 12.0000])), - (('10P', 4.0, 14.0), np.array([0.3351, 0.1875, 12.0000])), - (('10P', 4.0, 16.0), np.array([0.3370, 0.1756, 12.0000])), - (('10P', 4.0, 18.0), np.array([0.3386, 0.1626, 12.0000])), - (('10P', 4.0, 20.0), np.array([0.3400, 0.1500, 12.0000])), - (('10P', 4.0, 22.0), np.array([0.3411, 0.1424, 12.0000])), - (('10P', 4.0, 24.0), np.array([0.3421, 0.1337, 12.0000])), - (('10P', 4.0, 26.0), np.array([0.3428, 0.1248, 12.0000])), - (('10P', 4.0, 28.0), np.array([0.3432, 0.1172, 12.0000])), - (('10P', 4.0, 30.0), np.array([0.3440, 0.1080, 12.0000])), - (('10P', 4.0, 32.0), np.array([0.3450, 0.1000, 12.0000])), - (('10P', 4.0, 34.0), np.array([0.3460, 0.0930, 12.0000])), - (('10P', 4.0, 36.0), np.array([0.3460, 0.0850, 12.0000])), - (('10P', 4.0, 38.0), np.array([0.3470, 0.0780, 12.0000])), - (('2.5RP', 4.0, 2.0), np.array([0.3231, 0.2951, 12.0000])), - (('2.5RP', 4.0, 4.0), np.array([0.3340, 0.2770, 12.0000])), - (('2.5RP', 4.0, 6.0), np.array([0.3442, 0.2595, 12.0000])), - (('2.5RP', 4.0, 8.0), np.array([0.3533, 0.2438, 12.0000])), - (('2.5RP', 4.0, 10.0), np.array([0.3608, 0.2301, 12.0000])), - (('2.5RP', 4.0, 12.0), np.array([0.3683, 0.2162, 12.0000])), - (('2.5RP', 4.0, 14.0), np.array([0.3748, 0.2039, 12.0000])), - (('2.5RP', 4.0, 16.0), np.array([0.3807, 0.1923, 12.0000])), - (('2.5RP', 4.0, 18.0), np.array([0.3865, 0.1802, 12.0000])), - (('2.5RP', 4.0, 20.0), np.array([0.3926, 0.1679, 12.0000])), - (('2.5RP', 4.0, 22.0), np.array([0.3967, 0.1593, 12.0000])), - (('2.5RP', 4.0, 24.0), np.array([0.4011, 0.1504, 12.0000])), - (('2.5RP', 4.0, 26.0), np.array([0.4048, 0.1428, 12.0000])), - (('2.5RP', 4.0, 28.0), np.array([0.4090, 0.1340, 12.0000])), - (('2.5RP', 4.0, 30.0), np.array([0.4140, 0.1220, 12.0000])), - (('2.5RP', 4.0, 32.0), np.array([0.4170, 0.1150, 12.0000])), - (('2.5RP', 4.0, 34.0), np.array([0.4190, 0.1080, 12.0000])), - (('5RP', 4.0, 2.0), np.array([0.3310, 0.3010, 12.0000])), - (('5RP', 4.0, 4.0), np.array([0.3491, 0.2872, 12.0000])), - (('5RP', 4.0, 6.0), np.array([0.3671, 0.2733, 12.0000])), - (('5RP', 4.0, 8.0), np.array([0.3833, 0.2600, 12.0000])), - (('5RP', 4.0, 10.0), np.array([0.3960, 0.2489, 12.0000])), - (('5RP', 4.0, 12.0), np.array([0.4104, 0.2361, 12.0000])), - (('5RP', 4.0, 14.0), np.array([0.4225, 0.2249, 12.0000])), - (('5RP', 4.0, 16.0), np.array([0.4339, 0.2139, 12.0000])), - (('5RP', 4.0, 18.0), np.array([0.4455, 0.2023, 12.0000])), - (('5RP', 4.0, 20.0), np.array([0.4571, 0.1906, 12.0000])), - (('5RP', 4.0, 22.0), np.array([0.4656, 0.1821, 12.0000])), - (('5RP', 4.0, 24.0), np.array([0.4730, 0.1720, 12.0000])), - (('5RP', 4.0, 26.0), np.array([0.4820, 0.1620, 12.0000])), - (('5RP', 4.0, 28.0), np.array([0.4890, 0.1540, 12.0000])), - (('5RP', 4.0, 30.0), np.array([0.4990, 0.1430, 12.0000])), - (('7.5RP', 4.0, 2.0), np.array([0.3371, 0.3061, 12.0000])), - (('7.5RP', 4.0, 4.0), np.array([0.3612, 0.2963, 12.0000])), - (('7.5RP', 4.0, 6.0), np.array([0.3850, 0.2859, 12.0000])), - (('7.5RP', 4.0, 8.0), np.array([0.4072, 0.2750, 12.0000])), - (('7.5RP', 4.0, 10.0), np.array([0.4259, 0.2651, 12.0000])), - (('7.5RP', 4.0, 12.0), np.array([0.4450, 0.2541, 12.0000])), - (('7.5RP', 4.0, 14.0), np.array([0.4629, 0.2437, 12.0000])), - (('7.5RP', 4.0, 16.0), np.array([0.4799, 0.2329, 12.0000])), - (('7.5RP', 4.0, 18.0), np.array([0.4965, 0.2217, 12.0000])), - (('7.5RP', 4.0, 20.0), np.array([0.5130, 0.2101, 12.0000])), - (('7.5RP', 4.0, 22.0), np.array([0.5230, 0.2020, 12.0000])), - (('7.5RP', 4.0, 24.0), np.array([0.5360, 0.1920, 12.0000])), - (('7.5RP', 4.0, 26.0), np.array([0.5490, 0.1810, 12.0000])), - (('10RP', 4.0, 2.0), np.array([0.3417, 0.3106, 12.0000])), - (('10RP', 4.0, 4.0), np.array([0.3715, 0.3042, 12.0000])), - (('10RP', 4.0, 6.0), np.array([0.3999, 0.2972, 12.0000])), - (('10RP', 4.0, 8.0), np.array([0.4282, 0.2890, 12.0000])), - (('10RP', 4.0, 10.0), np.array([0.4528, 0.2811, 12.0000])), - (('10RP', 4.0, 12.0), np.array([0.4789, 0.2717, 12.0000])), - (('10RP', 4.0, 14.0), np.array([0.5020, 0.2623, 12.0000])), - (('10RP', 4.0, 16.0), np.array([0.5234, 0.2530, 12.0000])), - (('10RP', 4.0, 18.0), np.array([0.5466, 0.2424, 12.0000])), - (('10RP', 4.0, 20.0), np.array([0.5674, 0.2319, 12.0000])), - (('10RP', 4.0, 22.0), np.array([0.5820, 0.2240, 12.0000])), - (('10RP', 4.0, 24.0), np.array([0.5980, 0.2140, 12.0000])), - (('2.5R', 4.0, 2.0), np.array([0.3461, 0.3150, 12.0000])), - (('2.5R', 4.0, 4.0), np.array([0.3806, 0.3125, 12.0000])), - (('2.5R', 4.0, 6.0), np.array([0.4141, 0.3085, 12.0000])), - (('2.5R', 4.0, 8.0), np.array([0.4472, 0.3031, 12.0000])), - (('2.5R', 4.0, 10.0), np.array([0.4774, 0.2969, 12.0000])), - (('2.5R', 4.0, 12.0), np.array([0.5072, 0.2897, 12.0000])), - (('2.5R', 4.0, 14.0), np.array([0.5369, 0.2810, 12.0000])), - (('2.5R', 4.0, 16.0), np.array([0.5620, 0.2724, 12.0000])), - (('2.5R', 4.0, 18.0), np.array([0.5898, 0.2622, 12.0000])), - (('2.5R', 4.0, 20.0), np.array([0.6150, 0.2530, 12.0000])), - (('2.5R', 4.0, 22.0), np.array([0.6330, 0.2440, 12.0000])), - (('2.5R', 4.0, 24.0), np.array([0.6540, 0.2360, 12.0000])), - (('5R', 4.0, 2.0), np.array([0.3508, 0.3200, 12.0000])), - (('5R', 4.0, 4.0), np.array([0.3916, 0.3223, 12.0000])), - (('5R', 4.0, 6.0), np.array([0.4299, 0.3226, 12.0000])), - (('5R', 4.0, 8.0), np.array([0.4690, 0.3209, 12.0000])), - (('5R', 4.0, 10.0), np.array([0.5043, 0.3176, 12.0000])), - (('5R', 4.0, 12.0), np.array([0.5385, 0.3129, 12.0000])), - (('5R', 4.0, 14.0), np.array([0.5734, 0.3057, 12.0000])), - (('5R', 4.0, 16.0), np.array([0.6039, 0.2978, 12.0000])), - (('5R', 4.0, 18.0), np.array([0.6329, 0.2881, 12.0000])), - (('5R', 4.0, 20.0), np.array([0.6580, 0.2780, 12.0000])), - (('5R', 4.0, 22.0), np.array([0.6780, 0.2700, 12.0000])), - (('5R', 4.0, 24.0), np.array([0.6990, 0.2600, 12.0000])), - (('7.5R', 4.0, 2.0), np.array([0.3538, 0.3236, 12.0000])), - (('7.5R', 4.0, 4.0), np.array([0.3990, 0.3300, 12.0000])), - (('7.5R', 4.0, 6.0), np.array([0.4415, 0.3340, 12.0000])), - (('7.5R', 4.0, 8.0), np.array([0.4850, 0.3359, 12.0000])), - (('7.5R', 4.0, 10.0), np.array([0.5235, 0.3351, 12.0000])), - (('7.5R', 4.0, 12.0), np.array([0.5603, 0.3321, 12.0000])), - (('7.5R', 4.0, 14.0), np.array([0.5959, 0.3269, 12.0000])), - (('7.5R', 4.0, 16.0), np.array([0.6260, 0.3192, 12.0000])), - (('7.5R', 4.0, 18.0), np.array([0.6538, 0.3100, 12.0000])), - (('7.5R', 4.0, 20.0), np.array([0.6806, 0.2988, 12.0000])), - (('7.5R', 4.0, 22.0), np.array([0.7030, 0.2910, 12.0000])), - (('7.5R', 4.0, 24.0), np.array([0.7260, 0.2800, 12.0000])), - (('10R', 4.0, 2.0), np.array([0.3582, 0.3294, 12.0000])), - (('10R', 4.0, 4.0), np.array([0.4078, 0.3412, 12.0000])), - (('10R', 4.0, 6.0), np.array([0.4535, 0.3500, 12.0000])), - (('10R', 4.0, 8.0), np.array([0.4995, 0.3557, 12.0000])), - (('10R', 4.0, 10.0), np.array([0.5418, 0.3580, 12.0000])), - (('10R', 4.0, 12.0), np.array([0.5801, 0.3588, 12.0000])), - (('10R', 4.0, 14.0), np.array([0.6154, 0.3568, 12.0000])), - (('10R', 4.0, 16.0), np.array([0.6409, 0.3533, 12.0000])), - (('10R', 4.0, 18.0), np.array([0.6710, 0.3480, 12.0000])), - (('10R', 4.0, 20.0), np.array([0.6980, 0.3440, 12.0000])), - (('10R', 4.0, 22.0), np.array([0.7260, 0.3380, 12.0000])), - (('10R', 4.0, 24.0), np.array([0.7560, 0.3320, 12.0000])), - (('2.5YR', 4.0, 2.0), np.array([0.3624, 0.3367, 12.0000])), - (('2.5YR', 4.0, 4.0), np.array([0.4141, 0.3539, 12.0000])), - (('2.5YR', 4.0, 6.0), np.array([0.4612, 0.3674, 12.0000])), - (('2.5YR', 4.0, 8.0), np.array([0.5071, 0.3777, 12.0000])), - (('2.5YR', 4.0, 10.0), np.array([0.5475, 0.3856, 12.0000])), - (('2.5YR', 4.0, 12.0), np.array([0.5809, 0.3910, 12.0000])), - (('2.5YR', 4.0, 14.0), np.array([0.6140, 0.3960, 12.0000])), - (('2.5YR', 4.0, 16.0), np.array([0.6390, 0.4000, 12.0000])), - (('2.5YR', 4.0, 18.0), np.array([0.6690, 0.4040, 12.0000])), - (('5YR', 4.0, 2.0), np.array([0.3651, 0.3442, 12.0000])), - (('5YR', 4.0, 4.0), np.array([0.4187, 0.3679, 12.0000])), - (('5YR', 4.0, 6.0), np.array([0.4651, 0.3859, 12.0000])), - (('5YR', 4.0, 8.0), np.array([0.5070, 0.3994, 12.0000])), - (('5YR', 4.0, 10.0), np.array([0.5432, 0.4097, 12.0000])), - (('5YR', 4.0, 12.0), np.array([0.5729, 0.4169, 12.0000])), - (('5YR', 4.0, 14.0), np.array([0.6030, 0.4230, 12.0000])), - (('7.5YR', 4.0, 2.0), np.array([0.3662, 0.3504, 12.0000])), - (('7.5YR', 4.0, 4.0), np.array([0.4208, 0.3809, 12.0000])), - (('7.5YR', 4.0, 6.0), np.array([0.4655, 0.4029, 12.0000])), - (('7.5YR', 4.0, 8.0), np.array([0.5038, 0.4204, 12.0000])), - (('7.5YR', 4.0, 10.0), np.array([0.5356, 0.4342, 12.0000])), - (('7.5YR', 4.0, 12.0), np.array([0.5590, 0.4450, 12.0000])), - (('7.5YR', 4.0, 14.0), np.array([0.5870, 0.4560, 12.0000])), - (('10YR', 4.0, 2.0), np.array([0.3660, 0.3590, 12.0000])), - (('10YR', 4.0, 4.0), np.array([0.4189, 0.3948, 12.0000])), - (('10YR', 4.0, 6.0), np.array([0.4618, 0.4213, 12.0000])), - (('10YR', 4.0, 8.0), np.array([0.4965, 0.4414, 12.0000])), - (('10YR', 4.0, 10.0), np.array([0.5250, 0.4573, 12.0000])), - (('10YR', 4.0, 12.0), np.array([0.5450, 0.4680, 12.0000])), - (('2.5Y', 4.0, 2.0), np.array([0.3633, 0.3654, 12.0000])), - (('2.5Y', 4.0, 4.0), np.array([0.4138, 0.4076, 12.0000])), - (('2.5Y', 4.0, 6.0), np.array([0.4542, 0.4391, 12.0000])), - (('2.5Y', 4.0, 8.0), np.array([0.4865, 0.4625, 12.0000])), - (('2.5Y', 4.0, 10.0), np.array([0.5120, 0.4800, 12.0000])), - (('2.5Y', 4.0, 12.0), np.array([0.5290, 0.4920, 12.0000])), - (('5Y', 4.0, 2.0), np.array([0.3590, 0.3701, 12.0000])), - (('5Y', 4.0, 4.0), np.array([0.4069, 0.4188, 12.0000])), - (('5Y', 4.0, 6.0), np.array([0.4451, 0.4550, 12.0000])), - (('5Y', 4.0, 8.0), np.array([0.4745, 0.4810, 12.0000])), - (('5Y', 4.0, 10.0), np.array([0.4960, 0.5030, 12.0000])), - (('5Y', 4.0, 12.0), np.array([0.5120, 0.5160, 12.0000])), - (('7.5Y', 4.0, 2.0), np.array([0.3542, 0.3727, 12.0000])), - (('7.5Y', 4.0, 4.0), np.array([0.3982, 0.4272, 12.0000])), - (('7.5Y', 4.0, 6.0), np.array([0.4331, 0.4688, 12.0000])), - (('7.5Y', 4.0, 8.0), np.array([0.4595, 0.4990, 12.0000])), - (('7.5Y', 4.0, 10.0), np.array([0.4810, 0.5230, 12.0000])), - (('7.5Y', 4.0, 12.0), np.array([0.4940, 0.5380, 12.0000])), - (('10Y', 5.0, 2.0), np.array([0.3422, 0.3648, 19.7700])), - (('10Y', 5.0, 4.0), np.array([0.3762, 0.4158, 19.7700])), - (('10Y', 5.0, 6.0), np.array([0.4072, 0.4621, 19.7700])), - (('10Y', 5.0, 8.0), np.array([0.4307, 0.4967, 19.7700])), - (('10Y', 5.0, 10.0), np.array([0.4468, 0.5209, 19.7700])), - (('10Y', 5.0, 12.0), np.array([0.4590, 0.5390, 19.7700])), - (('10Y', 5.0, 14.0), np.array([0.4690, 0.5540, 19.7700])), - (('2.5GY', 5.0, 2.0), np.array([0.3352, 0.3636, 19.7700])), - (('2.5GY', 5.0, 4.0), np.array([0.3621, 0.4143, 19.7700])), - (('2.5GY', 5.0, 6.0), np.array([0.3879, 0.4646, 19.7700])), - (('2.5GY', 5.0, 8.0), np.array([0.4088, 0.5068, 19.7700])), - (('2.5GY', 5.0, 10.0), np.array([0.4224, 0.5369, 19.7700])), - (('2.5GY', 5.0, 12.0), np.array([0.4333, 0.5602, 19.7700])), - (('2.5GY', 5.0, 14.0), np.array([0.4400, 0.5800, 19.7700])), - (('5GY', 5.0, 2.0), np.array([0.3289, 0.3612, 19.7700])), - (('5GY', 5.0, 4.0), np.array([0.3482, 0.4097, 19.7700])), - (('5GY', 5.0, 6.0), np.array([0.3663, 0.4614, 19.7700])), - (('5GY', 5.0, 8.0), np.array([0.3815, 0.5093, 19.7700])), - (('5GY', 5.0, 10.0), np.array([0.3928, 0.5485, 19.7700])), - (('5GY', 5.0, 12.0), np.array([0.4011, 0.5802, 19.7700])), - (('5GY', 5.0, 14.0), np.array([0.4070, 0.6040, 19.7700])), - (('5GY', 5.0, 16.0), np.array([0.4100, 0.6260, 19.7700])), - (('7.5GY', 5.0, 2.0), np.array([0.3188, 0.3560, 19.7700])), - (('7.5GY', 5.0, 4.0), np.array([0.3274, 0.3994, 19.7700])), - (('7.5GY', 5.0, 6.0), np.array([0.3354, 0.4483, 19.7700])), - (('7.5GY', 5.0, 8.0), np.array([0.3412, 0.4976, 19.7700])), - (('7.5GY', 5.0, 10.0), np.array([0.3451, 0.5490, 19.7700])), - (('7.5GY', 5.0, 12.0), np.array([0.3450, 0.5949, 19.7700])), - (('7.5GY', 5.0, 14.0), np.array([0.3429, 0.6335, 19.7700])), - (('7.5GY', 5.0, 16.0), np.array([0.3410, 0.6660, 19.7700])), - (('7.5GY', 5.0, 18.0), np.array([0.3390, 0.6970, 19.7700])), - (('7.5GY', 5.0, 20.0), np.array([0.3330, 0.7330, 19.7700])), - (('10GY', 5.0, 2.0), np.array([0.3110, 0.3508, 19.7700])), - (('10GY', 5.0, 4.0), np.array([0.3111, 0.3881, 19.7700])), - (('10GY', 5.0, 6.0), np.array([0.3108, 0.4301, 19.7700])), - (('10GY', 5.0, 8.0), np.array([0.3080, 0.4759, 19.7700])), - (('10GY', 5.0, 10.0), np.array([0.3028, 0.5237, 19.7700])), - (('10GY', 5.0, 12.0), np.array([0.2940, 0.5751, 19.7700])), - (('10GY', 5.0, 14.0), np.array([0.2838, 0.6208, 19.7700])), - (('10GY', 5.0, 16.0), np.array([0.2702, 0.6700, 19.7700])), - (('10GY', 5.0, 18.0), np.array([0.2549, 0.7179, 19.7700])), - (('10GY', 5.0, 20.0), np.array([0.2370, 0.7670, 19.7700])), - (('10GY', 5.0, 22.0), np.array([0.2210, 0.8080, 19.7700])), - (('10GY', 5.0, 24.0), np.array([0.2020, 0.8520, 19.7700])), - (('10GY', 5.0, 26.0), np.array([0.1790, 0.8980, 19.7700])), - (('10GY', 5.0, 28.0), np.array([0.1540, 0.9490, 19.7700])), - (('10GY', 5.0, 30.0), np.array([0.1250, 1.0000, 19.7700])), - (('10GY', 5.0, 32.0), np.array([0.0970, 1.0480, 19.7700])), - (('2.5G', 5.0, 2.0), np.array([0.3030, 0.3445, 19.7700])), - (('2.5G', 5.0, 4.0), np.array([0.2943, 0.3735, 19.7700])), - (('2.5G', 5.0, 6.0), np.array([0.2841, 0.4045, 19.7700])), - (('2.5G', 5.0, 8.0), np.array([0.2710, 0.4380, 19.7700])), - (('2.5G', 5.0, 10.0), np.array([0.2565, 0.4705, 19.7700])), - (('2.5G', 5.0, 12.0), np.array([0.2385, 0.5071, 19.7700])), - (('2.5G', 5.0, 14.0), np.array([0.2211, 0.5411, 19.7700])), - (('2.5G', 5.0, 16.0), np.array([0.2005, 0.5759, 19.7700])), - (('2.5G', 5.0, 18.0), np.array([0.1782, 0.6095, 19.7700])), - (('2.5G', 5.0, 20.0), np.array([0.1579, 0.6392, 19.7700])), - (('2.5G', 5.0, 22.0), np.array([0.1377, 0.6674, 19.7700])), - (('2.5G', 5.0, 24.0), np.array([0.1188, 0.6918, 19.7700])), - (('2.5G', 5.0, 26.0), np.array([0.0992, 0.7155, 19.7700])), - (('2.5G', 5.0, 28.0), np.array([0.0794, 0.7385, 19.7700])), - (('2.5G', 5.0, 30.0), np.array([0.0550, 0.7660, 19.7700])), - (('2.5G', 5.0, 32.0), np.array([0.0340, 0.7890, 19.7700])), - (('5G', 5.0, 2.0), np.array([0.2978, 0.3392, 19.7700])), - (('5G', 5.0, 4.0), np.array([0.2841, 0.3628, 19.7700])), - (('5G', 5.0, 6.0), np.array([0.2690, 0.3860, 19.7700])), - (('5G', 5.0, 8.0), np.array([0.2511, 0.4107, 19.7700])), - (('5G', 5.0, 10.0), np.array([0.2329, 0.4331, 19.7700])), - (('5G', 5.0, 12.0), np.array([0.2104, 0.4578, 19.7700])), - (('5G', 5.0, 14.0), np.array([0.1912, 0.4773, 19.7700])), - (('5G', 5.0, 16.0), np.array([0.1695, 0.4981, 19.7700])), - (('5G', 5.0, 18.0), np.array([0.1489, 0.5171, 19.7700])), - (('5G', 5.0, 20.0), np.array([0.1318, 0.5321, 19.7700])), - (('5G', 5.0, 22.0), np.array([0.1144, 0.5463, 19.7700])), - (('5G', 5.0, 24.0), np.array([0.0953, 0.5628, 19.7700])), - (('5G', 5.0, 26.0), np.array([0.0784, 0.5761, 19.7700])), - (('5G', 5.0, 28.0), np.array([0.0609, 0.5898, 19.7700])), - (('5G', 5.0, 30.0), np.array([0.0400, 0.6050, 19.7700])), - (('5G', 5.0, 32.0), np.array([0.0200, 0.6220, 19.7700])), - (('7.5G', 5.0, 2.0), np.array([0.2945, 0.3355, 19.7700])), - (('7.5G', 5.0, 4.0), np.array([0.2775, 0.3545, 19.7700])), - (('7.5G', 5.0, 6.0), np.array([0.2598, 0.3724, 19.7700])), - (('7.5G', 5.0, 8.0), np.array([0.2395, 0.3915, 19.7700])), - (('7.5G', 5.0, 10.0), np.array([0.2200, 0.4082, 19.7700])), - (('7.5G', 5.0, 12.0), np.array([0.1964, 0.4271, 19.7700])), - (('7.5G', 5.0, 14.0), np.array([0.1776, 0.4415, 19.7700])), - (('7.5G', 5.0, 16.0), np.array([0.1571, 0.4561, 19.7700])), - (('7.5G', 5.0, 18.0), np.array([0.1372, 0.4705, 19.7700])), - (('7.5G', 5.0, 20.0), np.array([0.1212, 0.4817, 19.7700])), - (('7.5G', 5.0, 22.0), np.array([0.1050, 0.4927, 19.7700])), - (('7.5G', 5.0, 24.0), np.array([0.0878, 0.5039, 19.7700])), - (('7.5G', 5.0, 26.0), np.array([0.0730, 0.5131, 19.7700])), - (('7.5G', 5.0, 28.0), np.array([0.0585, 0.5224, 19.7700])), - (('7.5G', 5.0, 30.0), np.array([0.0410, 0.5320, 19.7700])), - (('7.5G', 5.0, 32.0), np.array([0.0210, 0.5420, 19.7700])), - (('10G', 5.0, 2.0), np.array([0.2910, 0.3310, 19.7700])), - (('10G', 5.0, 4.0), np.array([0.2711, 0.3455, 19.7700])), - (('10G', 5.0, 6.0), np.array([0.2519, 0.3587, 19.7700])), - (('10G', 5.0, 8.0), np.array([0.2297, 0.3730, 19.7700])), - (('10G', 5.0, 10.0), np.array([0.2095, 0.3853, 19.7700])), - (('10G', 5.0, 12.0), np.array([0.1852, 0.3992, 19.7700])), - (('10G', 5.0, 14.0), np.array([0.1671, 0.4089, 19.7700])), - (('10G', 5.0, 16.0), np.array([0.1469, 0.4192, 19.7700])), - (('10G', 5.0, 18.0), np.array([0.1275, 0.4288, 19.7700])), - (('10G', 5.0, 20.0), np.array([0.1120, 0.4360, 19.7700])), - (('10G', 5.0, 22.0), np.array([0.0958, 0.4428, 19.7700])), - (('10G', 5.0, 24.0), np.array([0.0811, 0.4491, 19.7700])), - (('10G', 5.0, 26.0), np.array([0.0690, 0.4542, 19.7700])), - (('10G', 5.0, 28.0), np.array([0.0572, 0.4590, 19.7700])), - (('10G', 5.0, 30.0), np.array([0.0410, 0.4660, 19.7700])), - (('10G', 5.0, 32.0), np.array([0.0230, 0.4740, 19.7700])), - (('2.5BG', 5.0, 2.0), np.array([0.2880, 0.3270, 19.7700])), - (('2.5BG', 5.0, 4.0), np.array([0.2659, 0.3369, 19.7700])), - (('2.5BG', 5.0, 6.0), np.array([0.2448, 0.3452, 19.7700])), - (('2.5BG', 5.0, 8.0), np.array([0.2205, 0.3537, 19.7700])), - (('2.5BG', 5.0, 10.0), np.array([0.1980, 0.3606, 19.7700])), - (('2.5BG', 5.0, 12.0), np.array([0.1735, 0.3668, 19.7700])), - (('2.5BG', 5.0, 14.0), np.array([0.1559, 0.3708, 19.7700])), - (('2.5BG', 5.0, 16.0), np.array([0.1348, 0.3750, 19.7700])), - (('2.5BG', 5.0, 18.0), np.array([0.1165, 0.3785, 19.7700])), - (('2.5BG', 5.0, 20.0), np.array([0.1005, 0.3814, 19.7700])), - (('2.5BG', 5.0, 22.0), np.array([0.0861, 0.3832, 19.7700])), - (('2.5BG', 5.0, 24.0), np.array([0.0738, 0.3851, 19.7700])), - (('2.5BG', 5.0, 26.0), np.array([0.0640, 0.3860, 19.7700])), - (('2.5BG', 5.0, 28.0), np.array([0.0530, 0.3880, 19.7700])), - (('2.5BG', 5.0, 30.0), np.array([0.0420, 0.3890, 19.7700])), - (('2.5BG', 5.0, 32.0), np.array([0.0270, 0.3910, 19.7700])), - (('5BG', 5.0, 2.0), np.array([0.2841, 0.3210, 19.7700])), - (('5BG', 5.0, 4.0), np.array([0.2591, 0.3246, 19.7700])), - (('5BG', 5.0, 6.0), np.array([0.2360, 0.3270, 19.7700])), - (('5BG', 5.0, 8.0), np.array([0.2100, 0.3280, 19.7700])), - (('5BG', 5.0, 10.0), np.array([0.1850, 0.3280, 19.7700])), - (('5BG', 5.0, 12.0), np.array([0.1614, 0.3280, 19.7700])), - (('5BG', 5.0, 14.0), np.array([0.1448, 0.3275, 19.7700])), - (('5BG', 5.0, 16.0), np.array([0.1243, 0.3261, 19.7700])), - (('5BG', 5.0, 18.0), np.array([0.1046, 0.3244, 19.7700])), - (('5BG', 5.0, 20.0), np.array([0.0904, 0.3231, 19.7700])), - (('5BG', 5.0, 22.0), np.array([0.0781, 0.3211, 19.7700])), - (('5BG', 5.0, 24.0), np.array([0.0670, 0.3200, 19.7700])), - (('5BG', 5.0, 26.0), np.array([0.0580, 0.3180, 19.7700])), - (('5BG', 5.0, 28.0), np.array([0.0500, 0.3170, 19.7700])), - (('5BG', 5.0, 30.0), np.array([0.0420, 0.3160, 19.7700])), - (('7.5BG', 5.0, 2.0), np.array([0.2812, 0.3161, 19.7700])), - (('7.5BG', 5.0, 4.0), np.array([0.2550, 0.3150, 19.7700])), - (('7.5BG', 5.0, 6.0), np.array([0.2292, 0.3125, 19.7700])), - (('7.5BG', 5.0, 8.0), np.array([0.2030, 0.3082, 19.7700])), - (('7.5BG', 5.0, 10.0), np.array([0.1776, 0.3032, 19.7700])), - (('7.5BG', 5.0, 12.0), np.array([0.1537, 0.2976, 19.7700])), - (('7.5BG', 5.0, 14.0), np.array([0.1364, 0.2932, 19.7700])), - (('7.5BG', 5.0, 16.0), np.array([0.1167, 0.2880, 19.7700])), - (('7.5BG', 5.0, 18.0), np.array([0.0982, 0.2828, 19.7700])), - (('7.5BG', 5.0, 20.0), np.array([0.0830, 0.2780, 19.7700])), - (('7.5BG', 5.0, 22.0), np.array([0.0710, 0.2740, 19.7700])), - (('7.5BG', 5.0, 24.0), np.array([0.0620, 0.2700, 19.7700])), - (('7.5BG', 5.0, 26.0), np.array([0.0550, 0.2680, 19.7700])), - (('10BG', 5.0, 2.0), np.array([0.2796, 0.3111, 19.7700])), - (('10BG', 5.0, 4.0), np.array([0.2512, 0.3040, 19.7700])), - (('10BG', 5.0, 6.0), np.array([0.2234, 0.2952, 19.7700])), - (('10BG', 5.0, 8.0), np.array([0.1970, 0.2860, 19.7700])), - (('10BG', 5.0, 10.0), np.array([0.1716, 0.2760, 19.7700])), - (('10BG', 5.0, 12.0), np.array([0.1485, 0.2662, 19.7700])), - (('10BG', 5.0, 14.0), np.array([0.1308, 0.2582, 19.7700])), - (('10BG', 5.0, 16.0), np.array([0.1108, 0.2489, 19.7700])), - (('10BG', 5.0, 18.0), np.array([0.0930, 0.2400, 19.7700])), - (('10BG', 5.0, 20.0), np.array([0.0790, 0.2320, 19.7700])), - (('10BG', 5.0, 22.0), np.array([0.0660, 0.2260, 19.7700])), - (('10BG', 5.0, 24.0), np.array([0.0580, 0.2210, 19.7700])), - (('2.5B', 5.0, 2.0), np.array([0.2791, 0.3071, 19.7700])), - (('2.5B', 5.0, 4.0), np.array([0.2492, 0.2954, 19.7700])), - (('2.5B', 5.0, 6.0), np.array([0.2210, 0.2823, 19.7700])), - (('2.5B', 5.0, 8.0), np.array([0.1947, 0.2687, 19.7700])), - (('2.5B', 5.0, 10.0), np.array([0.1697, 0.2549, 19.7700])), - (('2.5B', 5.0, 12.0), np.array([0.1461, 0.2406, 19.7700])), - (('2.5B', 5.0, 14.0), np.array([0.1283, 0.2292, 19.7700])), - (('2.5B', 5.0, 16.0), np.array([0.1090, 0.2166, 19.7700])), - (('2.5B', 5.0, 18.0), np.array([0.0940, 0.2060, 19.7700])), - (('2.5B', 5.0, 20.0), np.array([0.0770, 0.1940, 19.7700])), - (('5B', 5.0, 2.0), np.array([0.2794, 0.3032, 19.7700])), - (('5B', 5.0, 4.0), np.array([0.2493, 0.2879, 19.7700])), - (('5B', 5.0, 6.0), np.array([0.2215, 0.2701, 19.7700])), - (('5B', 5.0, 8.0), np.array([0.1958, 0.2519, 19.7700])), - (('5B', 5.0, 10.0), np.array([0.1729, 0.2347, 19.7700])), - (('5B', 5.0, 12.0), np.array([0.1505, 0.2172, 19.7700])), - (('5B', 5.0, 14.0), np.array([0.1320, 0.2021, 19.7700])), - (('5B', 5.0, 16.0), np.array([0.1132, 0.1863, 19.7700])), - (('5B', 5.0, 18.0), np.array([0.0980, 0.1720, 19.7700])), - (('5B', 5.0, 20.0), np.array([0.0860, 0.1600, 19.7700])), - (('7.5B', 5.0, 2.0), np.array([0.2803, 0.3000, 19.7700])), - (('7.5B', 5.0, 4.0), np.array([0.2511, 0.2808, 19.7700])), - (('7.5B', 5.0, 6.0), np.array([0.2248, 0.2612, 19.7700])), - (('7.5B', 5.0, 8.0), np.array([0.2007, 0.2417, 19.7700])), - (('7.5B', 5.0, 10.0), np.array([0.1792, 0.2230, 19.7700])), - (('7.5B', 5.0, 12.0), np.array([0.1584, 0.2042, 19.7700])), - (('7.5B', 5.0, 14.0), np.array([0.1404, 0.1878, 19.7700])), - (('7.5B', 5.0, 16.0), np.array([0.1230, 0.1711, 19.7700])), - (('7.5B', 5.0, 18.0), np.array([0.1090, 0.1570, 19.7700])), - (('7.5B', 5.0, 20.0), np.array([0.0980, 0.1460, 19.7700])), - (('10B', 5.0, 2.0), np.array([0.2821, 0.2966, 19.7700])), - (('10B', 5.0, 4.0), np.array([0.2547, 0.2757, 19.7700])), - (('10B', 5.0, 6.0), np.array([0.2299, 0.2548, 19.7700])), - (('10B', 5.0, 8.0), np.array([0.2067, 0.2344, 19.7700])), - (('10B', 5.0, 10.0), np.array([0.1860, 0.2149, 19.7700])), - (('10B', 5.0, 12.0), np.array([0.1666, 0.1964, 19.7700])), - (('10B', 5.0, 14.0), np.array([0.1492, 0.1797, 19.7700])), - (('10B', 5.0, 16.0), np.array([0.1326, 0.1632, 19.7700])), - (('10B', 5.0, 18.0), np.array([0.1203, 0.1505, 19.7700])), - (('10B', 5.0, 20.0), np.array([0.1080, 0.1370, 19.7700])), - (('10B', 5.0, 22.0), np.array([0.0960, 0.1260, 19.7700])), - (('2.5PB', 5.0, 2.0), np.array([0.2847, 0.2942, 19.7700])), - (('2.5PB', 5.0, 4.0), np.array([0.2600, 0.2720, 19.7700])), - (('2.5PB', 5.0, 6.0), np.array([0.2365, 0.2488, 19.7700])), - (('2.5PB', 5.0, 8.0), np.array([0.2157, 0.2278, 19.7700])), - (('2.5PB', 5.0, 10.0), np.array([0.1968, 0.2078, 19.7700])), - (('2.5PB', 5.0, 12.0), np.array([0.1793, 0.1894, 19.7700])), - (('2.5PB', 5.0, 14.0), np.array([0.1642, 0.1728, 19.7700])), - (('2.5PB', 5.0, 16.0), np.array([0.1495, 0.1559, 19.7700])), - (('2.5PB', 5.0, 18.0), np.array([0.1363, 0.1410, 19.7700])), - (('2.5PB', 5.0, 20.0), np.array([0.1240, 0.1280, 19.7700])), - (('2.5PB', 5.0, 22.0), np.array([0.1140, 0.1160, 19.7700])), - (('2.5PB', 5.0, 24.0), np.array([0.1030, 0.1040, 19.7700])), - (('5PB', 5.0, 2.0), np.array([0.2882, 0.2923, 19.7700])), - (('5PB', 5.0, 4.0), np.array([0.2662, 0.2687, 19.7700])), - (('5PB', 5.0, 6.0), np.array([0.2447, 0.2449, 19.7700])), - (('5PB', 5.0, 8.0), np.array([0.2255, 0.2239, 19.7700])), - (('5PB', 5.0, 10.0), np.array([0.2080, 0.2041, 19.7700])), - (('5PB', 5.0, 12.0), np.array([0.1918, 0.1858, 19.7700])), - (('5PB', 5.0, 14.0), np.array([0.1773, 0.1689, 19.7700])), - (('5PB', 5.0, 16.0), np.array([0.1638, 0.1521, 19.7700])), - (('5PB', 5.0, 18.0), np.array([0.1518, 0.1365, 19.7700])), - (('5PB', 5.0, 20.0), np.array([0.1420, 0.1230, 19.7700])), - (('5PB', 5.0, 22.0), np.array([0.1340, 0.1130, 19.7700])), - (('5PB', 5.0, 24.0), np.array([0.1240, 0.1000, 19.7700])), - (('5PB', 5.0, 26.0), np.array([0.1160, 0.0890, 19.7700])), - (('5PB', 5.0, 28.0), np.array([0.1090, 0.0790, 19.7700])), - (('5PB', 5.0, 30.0), np.array([0.1040, 0.0700, 19.7700])), - (('5PB', 5.0, 32.0), np.array([0.0970, 0.0620, 19.7700])), - (('7.5PB', 5.0, 2.0), np.array([0.2918, 0.2908, 19.7700])), - (('7.5PB', 5.0, 4.0), np.array([0.2739, 0.2666, 19.7700])), - (('7.5PB', 5.0, 6.0), np.array([0.2563, 0.2417, 19.7700])), - (('7.5PB', 5.0, 8.0), np.array([0.2417, 0.2204, 19.7700])), - (('7.5PB', 5.0, 10.0), np.array([0.2285, 0.2020, 19.7700])), - (('7.5PB', 5.0, 12.0), np.array([0.2157, 0.1830, 19.7700])), - (('7.5PB', 5.0, 14.0), np.array([0.2042, 0.1661, 19.7700])), - (('7.5PB', 5.0, 16.0), np.array([0.1945, 0.1511, 19.7700])), - (('7.5PB', 5.0, 18.0), np.array([0.1862, 0.1365, 19.7700])), - (('7.5PB', 5.0, 20.0), np.array([0.1794, 0.1239, 19.7700])), - (('7.5PB', 5.0, 22.0), np.array([0.1750, 0.1140, 19.7700])), - (('7.5PB', 5.0, 24.0), np.array([0.1700, 0.1030, 19.7700])), - (('7.5PB', 5.0, 26.0), np.array([0.1650, 0.0940, 19.7700])), - (('7.5PB', 5.0, 28.0), np.array([0.1620, 0.0860, 19.7700])), - (('7.5PB', 5.0, 30.0), np.array([0.1580, 0.0780, 19.7700])), - (('7.5PB', 5.0, 32.0), np.array([0.1540, 0.0700, 19.7700])), - (('7.5PB', 5.0, 34.0), np.array([0.1500, 0.0620, 19.7700])), - (('10PB', 5.0, 2.0), np.array([0.2959, 0.2905, 19.7700])), - (('10PB', 5.0, 4.0), np.array([0.2821, 0.2659, 19.7700])), - (('10PB', 5.0, 6.0), np.array([0.2686, 0.2412, 19.7700])), - (('10PB', 5.0, 8.0), np.array([0.2572, 0.2211, 19.7700])), - (('10PB', 5.0, 10.0), np.array([0.2478, 0.2030, 19.7700])), - (('10PB', 5.0, 12.0), np.array([0.2384, 0.1857, 19.7700])), - (('10PB', 5.0, 14.0), np.array([0.2299, 0.1698, 19.7700])), - (('10PB', 5.0, 16.0), np.array([0.2224, 0.1555, 19.7700])), - (('10PB', 5.0, 18.0), np.array([0.2174, 0.1444, 19.7700])), - (('10PB', 5.0, 20.0), np.array([0.2121, 0.1329, 19.7700])), - (('10PB', 5.0, 22.0), np.array([0.2082, 0.1225, 19.7700])), - (('10PB', 5.0, 24.0), np.array([0.2040, 0.1130, 19.7700])), - (('10PB', 5.0, 26.0), np.array([0.2000, 0.1030, 19.7700])), - (('10PB', 5.0, 28.0), np.array([0.1970, 0.0950, 19.7700])), - (('10PB', 5.0, 30.0), np.array([0.1930, 0.0860, 19.7700])), - (('10PB', 5.0, 32.0), np.array([0.1890, 0.0780, 19.7700])), - (('10PB', 5.0, 34.0), np.array([0.1860, 0.0710, 19.7700])), - (('10PB', 5.0, 36.0), np.array([0.1830, 0.0630, 19.7700])), - (('2.5P', 5.0, 2.0), np.array([0.3000, 0.2912, 19.7700])), - (('2.5P', 5.0, 4.0), np.array([0.2898, 0.2667, 19.7700])), - (('2.5P', 5.0, 6.0), np.array([0.2806, 0.2444, 19.7700])), - (('2.5P', 5.0, 8.0), np.array([0.2728, 0.2240, 19.7700])), - (('2.5P', 5.0, 10.0), np.array([0.2665, 0.2075, 19.7700])), - (('2.5P', 5.0, 12.0), np.array([0.2608, 0.1913, 19.7700])), - (('2.5P', 5.0, 14.0), np.array([0.2560, 0.1774, 19.7700])), - (('2.5P', 5.0, 16.0), np.array([0.2515, 0.1644, 19.7700])), - (('2.5P', 5.0, 18.0), np.array([0.2476, 0.1532, 19.7700])), - (('2.5P', 5.0, 20.0), np.array([0.2438, 0.1419, 19.7700])), - (('2.5P', 5.0, 22.0), np.array([0.2402, 0.1315, 19.7700])), - (('2.5P', 5.0, 24.0), np.array([0.2372, 0.1223, 19.7700])), - (('2.5P', 5.0, 26.0), np.array([0.2348, 0.1140, 19.7700])), - (('2.5P', 5.0, 28.0), np.array([0.2310, 0.1040, 19.7700])), - (('2.5P', 5.0, 30.0), np.array([0.2290, 0.0970, 19.7700])), - (('2.5P', 5.0, 32.0), np.array([0.2260, 0.0880, 19.7700])), - (('2.5P', 5.0, 34.0), np.array([0.2230, 0.0800, 19.7700])), - (('2.5P', 5.0, 36.0), np.array([0.2210, 0.0720, 19.7700])), - (('2.5P', 5.0, 38.0), np.array([0.2190, 0.0660, 19.7700])), - (('5P', 5.0, 2.0), np.array([0.3045, 0.2928, 19.7700])), - (('5P', 5.0, 4.0), np.array([0.2986, 0.2699, 19.7700])), - (('5P', 5.0, 6.0), np.array([0.2932, 0.2487, 19.7700])), - (('5P', 5.0, 8.0), np.array([0.2885, 0.2296, 19.7700])), - (('5P', 5.0, 10.0), np.array([0.2845, 0.2137, 19.7700])), - (('5P', 5.0, 12.0), np.array([0.2806, 0.1977, 19.7700])), - (('5P', 5.0, 14.0), np.array([0.2775, 0.1847, 19.7700])), - (('5P', 5.0, 16.0), np.array([0.2744, 0.1718, 19.7700])), - (('5P', 5.0, 18.0), np.array([0.2718, 0.1604, 19.7700])), - (('5P', 5.0, 20.0), np.array([0.2694, 0.1499, 19.7700])), - (('5P', 5.0, 22.0), np.array([0.2673, 0.1398, 19.7700])), - (('5P', 5.0, 24.0), np.array([0.2652, 0.1304, 19.7700])), - (('5P', 5.0, 26.0), np.array([0.2635, 0.1224, 19.7700])), - (('5P', 5.0, 28.0), np.array([0.2618, 0.1135, 19.7700])), - (('5P', 5.0, 30.0), np.array([0.2590, 0.1050, 19.7700])), - (('5P', 5.0, 32.0), np.array([0.2560, 0.0960, 19.7700])), - (('5P', 5.0, 34.0), np.array([0.2550, 0.0880, 19.7700])), - (('5P', 5.0, 36.0), np.array([0.2530, 0.0810, 19.7700])), - (('5P', 5.0, 38.0), np.array([0.2520, 0.0750, 19.7700])), - (('5P', 5.0, 40.0), np.array([0.2510, 0.0690, 19.7700])), - (('7.5P', 5.0, 2.0), np.array([0.3103, 0.2959, 19.7700])), - (('7.5P', 5.0, 4.0), np.array([0.3100, 0.2750, 19.7700])), - (('7.5P', 5.0, 6.0), np.array([0.3093, 0.2555, 19.7700])), - (('7.5P', 5.0, 8.0), np.array([0.3087, 0.2375, 19.7700])), - (('7.5P', 5.0, 10.0), np.array([0.3080, 0.2230, 19.7700])), - (('7.5P', 5.0, 12.0), np.array([0.3071, 0.2080, 19.7700])), - (('7.5P', 5.0, 14.0), np.array([0.3068, 0.1951, 19.7700])), - (('7.5P', 5.0, 16.0), np.array([0.3060, 0.1830, 19.7700])), - (('7.5P', 5.0, 18.0), np.array([0.3052, 0.1711, 19.7700])), - (('7.5P', 5.0, 20.0), np.array([0.3042, 0.1606, 19.7700])), - (('7.5P', 5.0, 22.0), np.array([0.3038, 0.1500, 19.7700])), - (('7.5P', 5.0, 24.0), np.array([0.3030, 0.1423, 19.7700])), - (('7.5P', 5.0, 26.0), np.array([0.3022, 0.1331, 19.7700])), - (('7.5P', 5.0, 28.0), np.array([0.3018, 0.1253, 19.7700])), - (('7.5P', 5.0, 30.0), np.array([0.3010, 0.1170, 19.7700])), - (('7.5P', 5.0, 32.0), np.array([0.3000, 0.1080, 19.7700])), - (('7.5P', 5.0, 34.0), np.array([0.3000, 0.1000, 19.7700])), - (('7.5P', 5.0, 36.0), np.array([0.2990, 0.0930, 19.7700])), - (('7.5P', 5.0, 38.0), np.array([0.2980, 0.0880, 19.7700])), - (('7.5P', 5.0, 40.0), np.array([0.2980, 0.0810, 19.7700])), - (('10P', 5.0, 2.0), np.array([0.3148, 0.2986, 19.7700])), - (('10P', 5.0, 4.0), np.array([0.3198, 0.2807, 19.7700])), - (('10P', 5.0, 6.0), np.array([0.3243, 0.2630, 19.7700])), - (('10P', 5.0, 8.0), np.array([0.3280, 0.2464, 19.7700])), - (('10P', 5.0, 10.0), np.array([0.3308, 0.2328, 19.7700])), - (('10P', 5.0, 12.0), np.array([0.3335, 0.2187, 19.7700])), - (('10P', 5.0, 14.0), np.array([0.3360, 0.2066, 19.7700])), - (('10P', 5.0, 16.0), np.array([0.3382, 0.1951, 19.7700])), - (('10P', 5.0, 18.0), np.array([0.3401, 0.1840, 19.7700])), - (('10P', 5.0, 20.0), np.array([0.3422, 0.1735, 19.7700])), - (('10P', 5.0, 22.0), np.array([0.3437, 0.1644, 19.7700])), - (('10P', 5.0, 24.0), np.array([0.3450, 0.1555, 19.7700])), - (('10P', 5.0, 26.0), np.array([0.3468, 0.1460, 19.7700])), - (('10P', 5.0, 28.0), np.array([0.3478, 0.1388, 19.7700])), - (('10P', 5.0, 30.0), np.array([0.3490, 0.1308, 19.7700])), - (('10P', 5.0, 32.0), np.array([0.3500, 0.1240, 19.7700])), - (('10P', 5.0, 34.0), np.array([0.3510, 0.1150, 19.7700])), - (('10P', 5.0, 36.0), np.array([0.3520, 0.1070, 19.7700])), - (('10P', 5.0, 38.0), np.array([0.3530, 0.1020, 19.7700])), - (('10P', 5.0, 40.0), np.array([0.3540, 0.0960, 19.7700])), - (('2.5RP', 5.0, 2.0), np.array([0.3199, 0.3019, 19.7700])), - (('2.5RP', 5.0, 4.0), np.array([0.3298, 0.2869, 19.7700])), - (('2.5RP', 5.0, 6.0), np.array([0.3396, 0.2718, 19.7700])), - (('2.5RP', 5.0, 8.0), np.array([0.3490, 0.2570, 19.7700])), - (('2.5RP', 5.0, 10.0), np.array([0.3560, 0.2452, 19.7700])), - (('2.5RP', 5.0, 12.0), np.array([0.3635, 0.2325, 19.7700])), - (('2.5RP', 5.0, 14.0), np.array([0.3703, 0.2211, 19.7700])), - (('2.5RP', 5.0, 16.0), np.array([0.3763, 0.2108, 19.7700])), - (('2.5RP', 5.0, 18.0), np.array([0.3821, 0.2007, 19.7700])), - (('2.5RP', 5.0, 20.0), np.array([0.3873, 0.1909, 19.7700])), - (('2.5RP', 5.0, 22.0), np.array([0.3924, 0.1814, 19.7700])), - (('2.5RP', 5.0, 24.0), np.array([0.3965, 0.1738, 19.7700])), - (('2.5RP', 5.0, 26.0), np.array([0.4011, 0.1652, 19.7700])), - (('2.5RP', 5.0, 28.0), np.array([0.4050, 0.1570, 19.7700])), - (('2.5RP', 5.0, 30.0), np.array([0.4090, 0.1490, 19.7700])), - (('2.5RP', 5.0, 32.0), np.array([0.4130, 0.1420, 19.7700])), - (('2.5RP', 5.0, 34.0), np.array([0.4160, 0.1350, 19.7700])), - (('2.5RP', 5.0, 36.0), np.array([0.4210, 0.1260, 19.7700])), - (('5RP', 5.0, 2.0), np.array([0.3256, 0.3065, 19.7700])), - (('5RP', 5.0, 4.0), np.array([0.3421, 0.2954, 19.7700])), - (('5RP', 5.0, 6.0), np.array([0.3585, 0.2842, 19.7700])), - (('5RP', 5.0, 8.0), np.array([0.3748, 0.2729, 19.7700])), - (('5RP', 5.0, 10.0), np.array([0.3880, 0.2630, 19.7700])), - (('5RP', 5.0, 12.0), np.array([0.4022, 0.2523, 19.7700])), - (('5RP', 5.0, 14.0), np.array([0.4142, 0.2428, 19.7700])), - (('5RP', 5.0, 16.0), np.array([0.4261, 0.2331, 19.7700])), - (('5RP', 5.0, 18.0), np.array([0.4372, 0.2242, 19.7700])), - (('5RP', 5.0, 20.0), np.array([0.4484, 0.2150, 19.7700])), - (('5RP', 5.0, 22.0), np.array([0.4581, 0.2068, 19.7700])), - (('5RP', 5.0, 24.0), np.array([0.4683, 0.1978, 19.7700])), - (('5RP', 5.0, 26.0), np.array([0.4760, 0.1900, 19.7700])), - (('5RP', 5.0, 28.0), np.array([0.4830, 0.1830, 19.7700])), - (('5RP', 5.0, 30.0), np.array([0.4910, 0.1760, 19.7700])), - (('5RP', 5.0, 32.0), np.array([0.4980, 0.1690, 19.7700])), - (('7.5RP', 5.0, 2.0), np.array([0.3296, 0.3098, 19.7700])), - (('7.5RP', 5.0, 4.0), np.array([0.3515, 0.3024, 19.7700])), - (('7.5RP', 5.0, 6.0), np.array([0.3726, 0.2941, 19.7700])), - (('7.5RP', 5.0, 8.0), np.array([0.3932, 0.2852, 19.7700])), - (('7.5RP', 5.0, 10.0), np.array([0.4108, 0.2773, 19.7700])), - (('7.5RP', 5.0, 12.0), np.array([0.4303, 0.2675, 19.7700])), - (('7.5RP', 5.0, 14.0), np.array([0.4454, 0.2596, 19.7700])), - (('7.5RP', 5.0, 16.0), np.array([0.4617, 0.2506, 19.7700])), - (('7.5RP', 5.0, 18.0), np.array([0.4761, 0.2421, 19.7700])), - (('7.5RP', 5.0, 20.0), np.array([0.4915, 0.2330, 19.7700])), - (('7.5RP', 5.0, 22.0), np.array([0.5045, 0.2248, 19.7700])), - (('7.5RP', 5.0, 24.0), np.array([0.5170, 0.2170, 19.7700])), - (('7.5RP', 5.0, 26.0), np.array([0.5260, 0.2100, 19.7700])), - (('7.5RP', 5.0, 28.0), np.array([0.5360, 0.2040, 19.7700])), - (('7.5RP', 5.0, 30.0), np.array([0.5460, 0.1970, 19.7700])), - (('10RP', 5.0, 2.0), np.array([0.3332, 0.3131, 19.7700])), - (('10RP', 5.0, 4.0), np.array([0.3594, 0.3090, 19.7700])), - (('10RP', 5.0, 6.0), np.array([0.3851, 0.3039, 19.7700])), - (('10RP', 5.0, 8.0), np.array([0.4105, 0.2980, 19.7700])), - (('10RP', 5.0, 10.0), np.array([0.4332, 0.2918, 19.7700])), - (('10RP', 5.0, 12.0), np.array([0.4579, 0.2841, 19.7700])), - (('10RP', 5.0, 14.0), np.array([0.4767, 0.2776, 19.7700])), - (('10RP', 5.0, 16.0), np.array([0.4986, 0.2695, 19.7700])), - (('10RP', 5.0, 18.0), np.array([0.5185, 0.2620, 19.7700])), - (('10RP', 5.0, 20.0), np.array([0.5396, 0.2535, 19.7700])), - (('10RP', 5.0, 22.0), np.array([0.5540, 0.2460, 19.7700])), - (('10RP', 5.0, 24.0), np.array([0.5700, 0.2400, 19.7700])), - (('10RP', 5.0, 26.0), np.array([0.5820, 0.2340, 19.7700])), - (('10RP', 5.0, 28.0), np.array([0.5930, 0.2290, 19.7700])), - (('2.5R', 5.0, 2.0), np.array([0.3360, 0.3158, 19.7700])), - (('2.5R', 5.0, 4.0), np.array([0.3660, 0.3148, 19.7700])), - (('2.5R', 5.0, 6.0), np.array([0.3960, 0.3130, 19.7700])), - (('2.5R', 5.0, 8.0), np.array([0.4252, 0.3101, 19.7700])), - (('2.5R', 5.0, 10.0), np.array([0.4533, 0.3058, 19.7700])), - (('2.5R', 5.0, 12.0), np.array([0.4820, 0.3002, 19.7700])), - (('2.5R', 5.0, 14.0), np.array([0.5047, 0.2950, 19.7700])), - (('2.5R', 5.0, 16.0), np.array([0.5300, 0.2880, 19.7700])), - (('2.5R', 5.0, 18.0), np.array([0.5540, 0.2804, 19.7700])), - (('2.5R', 5.0, 20.0), np.array([0.5784, 0.2719, 19.7700])), - (('2.5R', 5.0, 22.0), np.array([0.5930, 0.2650, 19.7700])), - (('2.5R', 5.0, 24.0), np.array([0.6070, 0.2590, 19.7700])), - (('2.5R', 5.0, 26.0), np.array([0.6200, 0.2530, 19.7700])), - (('2.5R', 5.0, 28.0), np.array([0.6310, 0.2480, 19.7700])), - (('5R', 5.0, 2.0), np.array([0.3392, 0.3192, 19.7700])), - (('5R', 5.0, 4.0), np.array([0.3740, 0.3220, 19.7700])), - (('5R', 5.0, 6.0), np.array([0.4078, 0.3238, 19.7700])), - (('5R', 5.0, 8.0), np.array([0.4413, 0.3240, 19.7700])), - (('5R', 5.0, 10.0), np.array([0.4747, 0.3227, 19.7700])), - (('5R', 5.0, 12.0), np.array([0.5071, 0.3194, 19.7700])), - (('5R', 5.0, 14.0), np.array([0.5341, 0.3158, 19.7700])), - (('5R', 5.0, 16.0), np.array([0.5637, 0.3102, 19.7700])), - (('5R', 5.0, 18.0), np.array([0.5918, 0.3038, 19.7700])), - (('5R', 5.0, 20.0), np.array([0.6142, 0.2970, 19.7700])), - (('5R', 5.0, 22.0), np.array([0.6340, 0.2900, 19.7700])), - (('5R', 5.0, 24.0), np.array([0.6480, 0.2840, 19.7700])), - (('5R', 5.0, 26.0), np.array([0.6620, 0.2790, 19.7700])), - (('5R', 5.0, 28.0), np.array([0.6740, 0.2740, 19.7700])), - (('7.5R', 5.0, 2.0), np.array([0.3425, 0.3229, 19.7700])), - (('7.5R', 5.0, 4.0), np.array([0.3806, 0.3294, 19.7700])), - (('7.5R', 5.0, 6.0), np.array([0.4180, 0.3348, 19.7700])), - (('7.5R', 5.0, 8.0), np.array([0.4563, 0.3387, 19.7700])), - (('7.5R', 5.0, 10.0), np.array([0.4927, 0.3399, 19.7700])), - (('7.5R', 5.0, 12.0), np.array([0.5280, 0.3389, 19.7700])), - (('7.5R', 5.0, 14.0), np.array([0.5590, 0.3370, 19.7700])), - (('7.5R', 5.0, 16.0), np.array([0.5901, 0.3331, 19.7700])), - (('7.5R', 5.0, 18.0), np.array([0.6161, 0.3277, 19.7700])), - (('7.5R', 5.0, 20.0), np.array([0.6388, 0.3216, 19.7700])), - (('7.5R', 5.0, 22.0), np.array([0.6610, 0.3170, 19.7700])), - (('7.5R', 5.0, 24.0), np.array([0.6800, 0.3120, 19.7700])), - (('7.5R', 5.0, 26.0), np.array([0.6920, 0.3070, 19.7700])), - (('7.5R', 5.0, 28.0), np.array([0.7080, 0.3020, 19.7700])), - (('10R', 5.0, 2.0), np.array([0.3465, 0.3278, 19.7700])), - (('10R', 5.0, 4.0), np.array([0.3879, 0.3398, 19.7700])), - (('10R', 5.0, 6.0), np.array([0.4299, 0.3499, 19.7700])), - (('10R', 5.0, 8.0), np.array([0.4713, 0.3575, 19.7700])), - (('10R', 5.0, 10.0), np.array([0.5113, 0.3630, 19.7700])), - (('10R', 5.0, 12.0), np.array([0.5481, 0.3660, 19.7700])), - (('10R', 5.0, 14.0), np.array([0.5771, 0.3664, 19.7700])), - (('10R', 5.0, 16.0), np.array([0.6037, 0.3657, 19.7700])), - (('10R', 5.0, 18.0), np.array([0.6297, 0.3642, 19.7700])), - (('10R', 5.0, 20.0), np.array([0.6550, 0.3610, 19.7700])), - (('10R', 5.0, 22.0), np.array([0.6800, 0.3570, 19.7700])), - (('10R', 5.0, 24.0), np.array([0.7020, 0.3540, 19.7700])), - (('10R', 5.0, 26.0), np.array([0.7210, 0.3500, 19.7700])), - (('2.5YR', 5.0, 2.0), np.array([0.3506, 0.3337, 19.7700])), - (('2.5YR', 5.0, 4.0), np.array([0.3925, 0.3494, 19.7700])), - (('2.5YR', 5.0, 6.0), np.array([0.4365, 0.3640, 19.7700])), - (('2.5YR', 5.0, 8.0), np.array([0.4795, 0.3758, 19.7700])), - (('2.5YR', 5.0, 10.0), np.array([0.5175, 0.3844, 19.7700])), - (('2.5YR', 5.0, 12.0), np.array([0.5482, 0.3909, 19.7700])), - (('2.5YR', 5.0, 14.0), np.array([0.5731, 0.3953, 19.7700])), - (('2.5YR', 5.0, 16.0), np.array([0.5933, 0.3989, 19.7700])), - (('2.5YR', 5.0, 18.0), np.array([0.6170, 0.4020, 19.7700])), - (('2.5YR', 5.0, 20.0), np.array([0.6360, 0.4060, 19.7700])), - (('5YR', 5.0, 2.0), np.array([0.3530, 0.3395, 19.7700])), - (('5YR', 5.0, 4.0), np.array([0.3968, 0.3614, 19.7700])), - (('5YR', 5.0, 6.0), np.array([0.4420, 0.3808, 19.7700])), - (('5YR', 5.0, 8.0), np.array([0.4830, 0.3960, 19.7700])), - (('5YR', 5.0, 10.0), np.array([0.5161, 0.4064, 19.7700])), - (('5YR', 5.0, 12.0), np.array([0.5422, 0.4141, 19.7700])), - (('5YR', 5.0, 14.0), np.array([0.5642, 0.4201, 19.7700])), - (('5YR', 5.0, 16.0), np.array([0.5840, 0.4250, 19.7700])), - (('5YR', 5.0, 18.0), np.array([0.6010, 0.4290, 19.7700])), - (('7.5YR', 5.0, 2.0), np.array([0.3540, 0.3445, 19.7700])), - (('7.5YR', 5.0, 4.0), np.array([0.3991, 0.3714, 19.7700])), - (('7.5YR', 5.0, 6.0), np.array([0.4440, 0.3954, 19.7700])), - (('7.5YR', 5.0, 8.0), np.array([0.4820, 0.4141, 19.7700])), - (('7.5YR', 5.0, 10.0), np.array([0.5108, 0.4276, 19.7700])), - (('7.5YR', 5.0, 12.0), np.array([0.5335, 0.4378, 19.7700])), - (('7.5YR', 5.0, 14.0), np.array([0.5506, 0.4450, 19.7700])), - (('7.5YR', 5.0, 16.0), np.array([0.5680, 0.4530, 19.7700])), - (('10YR', 5.0, 2.0), np.array([0.3546, 0.3514, 19.7700])), - (('10YR', 5.0, 4.0), np.array([0.3995, 0.3840, 19.7700])), - (('10YR', 5.0, 6.0), np.array([0.4428, 0.4128, 19.7700])), - (('10YR', 5.0, 8.0), np.array([0.4770, 0.4338, 19.7700])), - (('10YR', 5.0, 10.0), np.array([0.5025, 0.4489, 19.7700])), - (('10YR', 5.0, 12.0), np.array([0.5211, 0.4600, 19.7700])), - (('10YR', 5.0, 14.0), np.array([0.5390, 0.4680, 19.7700])), - (('10YR', 5.0, 16.0), np.array([0.5520, 0.4770, 19.7700])), - (('2.5Y', 5.0, 2.0), np.array([0.3534, 0.3570, 19.7700])), - (('2.5Y', 5.0, 4.0), np.array([0.3968, 0.3954, 19.7700])), - (('2.5Y', 5.0, 6.0), np.array([0.4380, 0.4292, 19.7700])), - (('2.5Y', 5.0, 8.0), np.array([0.4685, 0.4524, 19.7700])), - (('2.5Y', 5.0, 10.0), np.array([0.4905, 0.4683, 19.7700])), - (('2.5Y', 5.0, 12.0), np.array([0.5082, 0.4812, 19.7700])), - (('2.5Y', 5.0, 14.0), np.array([0.5230, 0.4910, 19.7700])), - (('5Y', 5.0, 2.0), np.array([0.3500, 0.3620, 19.7700])), - (('5Y', 5.0, 4.0), np.array([0.3915, 0.4057, 19.7700])), - (('5Y', 5.0, 6.0), np.array([0.4302, 0.4435, 19.7700])), - (('5Y', 5.0, 8.0), np.array([0.4579, 0.4692, 19.7700])), - (('5Y', 5.0, 10.0), np.array([0.4777, 0.4876, 19.7700])), - (('5Y', 5.0, 12.0), np.array([0.4932, 0.5019, 19.7700])), - (('5Y', 5.0, 14.0), np.array([0.5070, 0.5120, 19.7700])), - (('7.5Y', 5.0, 2.0), np.array([0.3470, 0.3640, 19.7700])), - (('7.5Y', 5.0, 4.0), np.array([0.3850, 0.4120, 19.7700])), - (('7.5Y', 5.0, 6.0), np.array([0.4199, 0.4551, 19.7700])), - (('7.5Y', 5.0, 8.0), np.array([0.4450, 0.4850, 19.7700])), - (('7.5Y', 5.0, 10.0), np.array([0.4632, 0.5057, 19.7700])), - (('7.5Y', 5.0, 12.0), np.array([0.4767, 0.5208, 19.7700])), - (('7.5Y', 5.0, 14.0), np.array([0.4890, 0.5350, 19.7700])), - (('10Y', 6.0, 2.0), np.array([0.3398, 0.3611, 30.0300])), - (('10Y', 6.0, 4.0), np.array([0.3679, 0.4033, 30.0300])), - (('10Y', 6.0, 6.0), np.array([0.3960, 0.4452, 30.0300])), - (('10Y', 6.0, 8.0), np.array([0.4201, 0.4812, 30.0300])), - (('10Y', 6.0, 10.0), np.array([0.4372, 0.5068, 30.0300])), - (('10Y', 6.0, 12.0), np.array([0.4488, 0.5237, 30.0300])), - (('10Y', 6.0, 14.0), np.array([0.4593, 0.5392, 30.0300])), - (('10Y', 6.0, 16.0), np.array([0.4650, 0.5460, 30.0300])), - (('2.5GY', 6.0, 2.0), np.array([0.3342, 0.3607, 30.0300])), - (('2.5GY', 6.0, 4.0), np.array([0.3572, 0.4038, 30.0300])), - (('2.5GY', 6.0, 6.0), np.array([0.3799, 0.4470, 30.0300])), - (('2.5GY', 6.0, 8.0), np.array([0.4006, 0.4885, 30.0300])), - (('2.5GY', 6.0, 10.0), np.array([0.4159, 0.5190, 30.0300])), - (('2.5GY', 6.0, 12.0), np.array([0.4269, 0.5414, 30.0300])), - (('2.5GY', 6.0, 14.0), np.array([0.4354, 0.5594, 30.0300])), - (('2.5GY', 6.0, 16.0), np.array([0.4390, 0.5680, 30.0300])), - (('5GY', 6.0, 2.0), np.array([0.3288, 0.3592, 30.0300])), - (('5GY', 6.0, 4.0), np.array([0.3461, 0.4008, 30.0300])), - (('5GY', 6.0, 6.0), np.array([0.3622, 0.4438, 30.0300])), - (('5GY', 6.0, 8.0), np.array([0.3772, 0.4880, 30.0300])), - (('5GY', 6.0, 10.0), np.array([0.3891, 0.5264, 30.0300])), - (('5GY', 6.0, 12.0), np.array([0.3980, 0.5564, 30.0300])), - (('5GY', 6.0, 14.0), np.array([0.4042, 0.5788, 30.0300])), - (('5GY', 6.0, 16.0), np.array([0.4090, 0.5940, 30.0300])), - (('5GY', 6.0, 18.0), np.array([0.4130, 0.6070, 30.0300])), - (('7.5GY', 6.0, 2.0), np.array([0.3193, 0.3550, 30.0300])), - (('7.5GY', 6.0, 4.0), np.array([0.3275, 0.3922, 30.0300])), - (('7.5GY', 6.0, 6.0), np.array([0.3351, 0.4321, 30.0300])), - (('7.5GY', 6.0, 8.0), np.array([0.3418, 0.4768, 30.0300])), - (('7.5GY', 6.0, 10.0), np.array([0.3463, 0.5196, 30.0300])), - (('7.5GY', 6.0, 12.0), np.array([0.3488, 0.5596, 30.0300])), - (('7.5GY', 6.0, 14.0), np.array([0.3498, 0.5985, 30.0300])), - (('7.5GY', 6.0, 16.0), np.array([0.3498, 0.6282, 30.0300])), - (('7.5GY', 6.0, 18.0), np.array([0.3490, 0.6500, 30.0300])), - (('7.5GY', 6.0, 20.0), np.array([0.3480, 0.6670, 30.0300])), - (('7.5GY', 6.0, 22.0), np.array([0.3470, 0.6880, 30.0300])), - (('10GY', 6.0, 2.0), np.array([0.3112, 0.3496, 30.0300])), - (('10GY', 6.0, 4.0), np.array([0.3124, 0.3822, 30.0300])), - (('10GY', 6.0, 6.0), np.array([0.3128, 0.4175, 30.0300])), - (('10GY', 6.0, 8.0), np.array([0.3116, 0.4563, 30.0300])), - (('10GY', 6.0, 10.0), np.array([0.3086, 0.4949, 30.0300])), - (('10GY', 6.0, 12.0), np.array([0.3037, 0.5358, 30.0300])), - (('10GY', 6.0, 14.0), np.array([0.2962, 0.5802, 30.0300])), - (('10GY', 6.0, 16.0), np.array([0.2872, 0.6199, 30.0300])), - (('10GY', 6.0, 18.0), np.array([0.2763, 0.6616, 30.0300])), - (('10GY', 6.0, 20.0), np.array([0.2648, 0.7004, 30.0300])), - (('10GY', 6.0, 22.0), np.array([0.2530, 0.7380, 30.0300])), - (('10GY', 6.0, 24.0), np.array([0.2380, 0.7820, 30.0300])), - (('10GY', 6.0, 26.0), np.array([0.2250, 0.8200, 30.0300])), - (('10GY', 6.0, 28.0), np.array([0.2080, 0.8630, 30.0300])), - (('10GY', 6.0, 30.0), np.array([0.1880, 0.9050, 30.0300])), - (('10GY', 6.0, 32.0), np.array([0.1710, 0.9390, 30.0300])), - (('2.5G', 6.0, 2.0), np.array([0.3039, 0.3437, 30.0300])), - (('2.5G', 6.0, 4.0), np.array([0.2967, 0.3695, 30.0300])), - (('2.5G', 6.0, 6.0), np.array([0.2892, 0.3963, 30.0300])), - (('2.5G', 6.0, 8.0), np.array([0.2799, 0.4239, 30.0300])), - (('2.5G', 6.0, 10.0), np.array([0.2690, 0.4530, 30.0300])), - (('2.5G', 6.0, 12.0), np.array([0.2574, 0.4814, 30.0300])), - (('2.5G', 6.0, 14.0), np.array([0.2426, 0.5133, 30.0300])), - (('2.5G', 6.0, 16.0), np.array([0.2278, 0.5430, 30.0300])), - (('2.5G', 6.0, 18.0), np.array([0.2102, 0.5737, 30.0300])), - (('2.5G', 6.0, 20.0), np.array([0.1922, 0.6035, 30.0300])), - (('2.5G', 6.0, 22.0), np.array([0.1739, 0.6318, 30.0300])), - (('2.5G', 6.0, 24.0), np.array([0.1536, 0.6605, 30.0300])), - (('2.5G', 6.0, 26.0), np.array([0.1340, 0.6871, 30.0300])), - (('2.5G', 6.0, 28.0), np.array([0.1145, 0.7122, 30.0300])), - (('2.5G', 6.0, 30.0), np.array([0.0920, 0.7380, 30.0300])), - (('2.5G', 6.0, 32.0), np.array([0.0690, 0.7640, 30.0300])), - (('2.5G', 6.0, 34.0), np.array([0.0470, 0.7890, 30.0300])), - (('5G', 6.0, 2.0), np.array([0.2988, 0.3382, 30.0300])), - (('5G', 6.0, 4.0), np.array([0.2868, 0.3595, 30.0300])), - (('5G', 6.0, 6.0), np.array([0.2748, 0.3795, 30.0300])), - (('5G', 6.0, 8.0), np.array([0.2612, 0.3990, 30.0300])), - (('5G', 6.0, 10.0), np.array([0.2466, 0.4181, 30.0300])), - (('5G', 6.0, 12.0), np.array([0.2293, 0.4390, 30.0300])), - (('5G', 6.0, 14.0), np.array([0.2130, 0.4571, 30.0300])), - (('5G', 6.0, 16.0), np.array([0.1960, 0.4751, 30.0300])), - (('5G', 6.0, 18.0), np.array([0.1785, 0.4924, 30.0300])), - (('5G', 6.0, 20.0), np.array([0.1609, 0.5091, 30.0300])), - (('5G', 6.0, 22.0), np.array([0.1432, 0.5252, 30.0300])), - (('5G', 6.0, 24.0), np.array([0.1252, 0.5408, 30.0300])), - (('5G', 6.0, 26.0), np.array([0.1079, 0.5560, 30.0300])), - (('5G', 6.0, 28.0), np.array([0.0908, 0.5695, 30.0300])), - (('5G', 6.0, 30.0), np.array([0.0710, 0.5860, 30.0300])), - (('5G', 6.0, 32.0), np.array([0.0530, 0.5990, 30.0300])), - (('5G', 6.0, 34.0), np.array([0.0310, 0.6140, 30.0300])), - (('7.5G', 6.0, 2.0), np.array([0.2958, 0.3344, 30.0300])), - (('7.5G', 6.0, 4.0), np.array([0.2807, 0.3522, 30.0300])), - (('7.5G', 6.0, 6.0), np.array([0.2662, 0.3672, 30.0300])), - (('7.5G', 6.0, 8.0), np.array([0.2510, 0.3829, 30.0300])), - (('7.5G', 6.0, 10.0), np.array([0.2350, 0.3979, 30.0300])), - (('7.5G', 6.0, 12.0), np.array([0.2171, 0.4138, 30.0300])), - (('7.5G', 6.0, 14.0), np.array([0.2001, 0.4278, 30.0300])), - (('7.5G', 6.0, 16.0), np.array([0.1832, 0.4414, 30.0300])), - (('7.5G', 6.0, 18.0), np.array([0.1654, 0.4551, 30.0300])), - (('7.5G', 6.0, 20.0), np.array([0.1485, 0.4677, 30.0300])), - (('7.5G', 6.0, 22.0), np.array([0.1325, 0.4795, 30.0300])), - (('7.5G', 6.0, 24.0), np.array([0.1159, 0.4910, 30.0300])), - (('7.5G', 6.0, 26.0), np.array([0.1010, 0.5018, 30.0300])), - (('7.5G', 6.0, 28.0), np.array([0.0858, 0.5127, 30.0300])), - (('7.5G', 6.0, 30.0), np.array([0.0680, 0.5240, 30.0300])), - (('7.5G', 6.0, 32.0), np.array([0.0520, 0.5350, 30.0300])), - (('7.5G', 6.0, 34.0), np.array([0.0340, 0.5460, 30.0300])), - (('10G', 6.0, 2.0), np.array([0.2929, 0.3303, 30.0300])), - (('10G', 6.0, 4.0), np.array([0.2749, 0.3443, 30.0300])), - (('10G', 6.0, 6.0), np.array([0.2591, 0.3558, 30.0300])), - (('10G', 6.0, 8.0), np.array([0.2420, 0.3679, 30.0300])), - (('10G', 6.0, 10.0), np.array([0.2247, 0.3796, 30.0300])), - (('10G', 6.0, 12.0), np.array([0.2060, 0.3914, 30.0300])), - (('10G', 6.0, 14.0), np.array([0.1895, 0.4015, 30.0300])), - (('10G', 6.0, 16.0), np.array([0.1722, 0.4113, 30.0300])), - (('10G', 6.0, 18.0), np.array([0.1551, 0.4208, 30.0300])), - (('10G', 6.0, 20.0), np.array([0.1382, 0.4299, 30.0300])), - (('10G', 6.0, 22.0), np.array([0.1230, 0.4378, 30.0300])), - (('10G', 6.0, 24.0), np.array([0.1070, 0.4458, 30.0300])), - (('10G', 6.0, 26.0), np.array([0.0941, 0.4520, 30.0300])), - (('10G', 6.0, 28.0), np.array([0.0810, 0.4580, 30.0300])), - (('10G', 6.0, 30.0), np.array([0.0660, 0.4650, 30.0300])), - (('10G', 6.0, 32.0), np.array([0.0520, 0.4710, 30.0300])), - (('10G', 6.0, 34.0), np.array([0.0350, 0.4800, 30.0300])), - (('2.5BG', 6.0, 2.0), np.array([0.2902, 0.3268, 30.0300])), - (('2.5BG', 6.0, 4.0), np.array([0.2702, 0.3369, 30.0300])), - (('2.5BG', 6.0, 6.0), np.array([0.2526, 0.3448, 30.0300])), - (('2.5BG', 6.0, 8.0), np.array([0.2332, 0.3522, 30.0300])), - (('2.5BG', 6.0, 10.0), np.array([0.2148, 0.3584, 30.0300])), - (('2.5BG', 6.0, 12.0), np.array([0.1954, 0.3645, 30.0300])), - (('2.5BG', 6.0, 14.0), np.array([0.1779, 0.3699, 30.0300])), - (('2.5BG', 6.0, 16.0), np.array([0.1600, 0.3748, 30.0300])), - (('2.5BG', 6.0, 18.0), np.array([0.1428, 0.3790, 30.0300])), - (('2.5BG', 6.0, 20.0), np.array([0.1269, 0.3829, 30.0300])), - (('2.5BG', 6.0, 22.0), np.array([0.1120, 0.3860, 30.0300])), - (('2.5BG', 6.0, 24.0), np.array([0.0960, 0.3890, 30.0300])), - (('2.5BG', 6.0, 26.0), np.array([0.0830, 0.3920, 30.0300])), - (('2.5BG', 6.0, 28.0), np.array([0.0740, 0.3940, 30.0300])), - (('2.5BG', 6.0, 30.0), np.array([0.0620, 0.3970, 30.0300])), - (('2.5BG', 6.0, 32.0), np.array([0.0500, 0.3980, 30.0300])), - (('5BG', 6.0, 2.0), np.array([0.2872, 0.3219, 30.0300])), - (('5BG', 6.0, 4.0), np.array([0.2648, 0.3262, 30.0300])), - (('5BG', 6.0, 6.0), np.array([0.2441, 0.3290, 30.0300])), - (('5BG', 6.0, 8.0), np.array([0.2236, 0.3311, 30.0300])), - (('5BG', 6.0, 10.0), np.array([0.2037, 0.3329, 30.0300])), - (('5BG', 6.0, 12.0), np.array([0.1844, 0.3337, 30.0300])), - (('5BG', 6.0, 14.0), np.array([0.1662, 0.3343, 30.0300])), - (('5BG', 6.0, 16.0), np.array([0.1491, 0.3345, 30.0300])), - (('5BG', 6.0, 18.0), np.array([0.1325, 0.3345, 30.0300])), - (('5BG', 6.0, 20.0), np.array([0.1168, 0.3344, 30.0300])), - (('5BG', 6.0, 22.0), np.array([0.1010, 0.3320, 30.0300])), - (('5BG', 6.0, 24.0), np.array([0.0860, 0.3320, 30.0300])), - (('5BG', 6.0, 26.0), np.array([0.0750, 0.3310, 30.0300])), - (('5BG', 6.0, 28.0), np.array([0.0680, 0.3300, 30.0300])), - (('5BG', 6.0, 30.0), np.array([0.0600, 0.3300, 30.0300])), - (('7.5BG', 6.0, 2.0), np.array([0.2849, 0.3172, 30.0300])), - (('7.5BG', 6.0, 4.0), np.array([0.2604, 0.3169, 30.0300])), - (('7.5BG', 6.0, 6.0), np.array([0.2384, 0.3155, 30.0300])), - (('7.5BG', 6.0, 8.0), np.array([0.2171, 0.3138, 30.0300])), - (('7.5BG', 6.0, 10.0), np.array([0.1961, 0.3110, 30.0300])), - (('7.5BG', 6.0, 12.0), np.array([0.1762, 0.3081, 30.0300])), - (('7.5BG', 6.0, 14.0), np.array([0.1585, 0.3052, 30.0300])), - (('7.5BG', 6.0, 16.0), np.array([0.1408, 0.3017, 30.0300])), - (('7.5BG', 6.0, 18.0), np.array([0.1248, 0.2981, 30.0300])), - (('7.5BG', 6.0, 20.0), np.array([0.1080, 0.2940, 30.0300])), - (('7.5BG', 6.0, 22.0), np.array([0.0920, 0.2900, 30.0300])), - (('7.5BG', 6.0, 24.0), np.array([0.0780, 0.2870, 30.0300])), - (('7.5BG', 6.0, 26.0), np.array([0.0680, 0.2830, 30.0300])), - (('10BG', 6.0, 2.0), np.array([0.2837, 0.3132, 30.0300])), - (('10BG', 6.0, 4.0), np.array([0.2578, 0.3078, 30.0300])), - (('10BG', 6.0, 6.0), np.array([0.2335, 0.3015, 30.0300])), - (('10BG', 6.0, 8.0), np.array([0.2116, 0.2950, 30.0300])), - (('10BG', 6.0, 10.0), np.array([0.1909, 0.2881, 30.0300])), - (('10BG', 6.0, 12.0), np.array([0.1698, 0.2802, 30.0300])), - (('10BG', 6.0, 14.0), np.array([0.1518, 0.2729, 30.0300])), - (('10BG', 6.0, 16.0), np.array([0.1337, 0.2651, 30.0300])), - (('10BG', 6.0, 18.0), np.array([0.1181, 0.2581, 30.0300])), - (('10BG', 6.0, 20.0), np.array([0.1010, 0.2510, 30.0300])), - (('10BG', 6.0, 22.0), np.array([0.0860, 0.2440, 30.0300])), - (('10BG', 6.0, 24.0), np.array([0.0720, 0.2370, 30.0300])), - (('2.5B', 6.0, 2.0), np.array([0.2835, 0.3097, 30.0300])), - (('2.5B', 6.0, 4.0), np.array([0.2571, 0.3008, 30.0300])), - (('2.5B', 6.0, 6.0), np.array([0.2312, 0.2899, 30.0300])), - (('2.5B', 6.0, 8.0), np.array([0.2080, 0.2789, 30.0300])), - (('2.5B', 6.0, 10.0), np.array([0.1879, 0.2682, 30.0300])), - (('2.5B', 6.0, 12.0), np.array([0.1660, 0.2561, 30.0300])), - (('2.5B', 6.0, 14.0), np.array([0.1480, 0.2459, 30.0300])), - (('2.5B', 6.0, 16.0), np.array([0.1294, 0.2348, 30.0300])), - (('2.5B', 6.0, 18.0), np.array([0.1140, 0.2260, 30.0300])), - (('2.5B', 6.0, 20.0), np.array([0.0990, 0.2160, 30.0300])), - (('2.5B', 6.0, 22.0), np.array([0.0850, 0.2050, 30.0300])), - (('5B', 6.0, 2.0), np.array([0.2842, 0.3063, 30.0300])), - (('5B', 6.0, 4.0), np.array([0.2579, 0.2938, 30.0300])), - (('5B', 6.0, 6.0), np.array([0.2320, 0.2789, 30.0300])), - (('5B', 6.0, 8.0), np.array([0.2088, 0.2635, 30.0300])), - (('5B', 6.0, 10.0), np.array([0.1883, 0.2487, 30.0300])), - (('5B', 6.0, 12.0), np.array([0.1685, 0.2339, 30.0300])), - (('5B', 6.0, 14.0), np.array([0.1496, 0.2193, 30.0300])), - (('5B', 6.0, 16.0), np.array([0.1310, 0.2048, 30.0300])), - (('5B', 6.0, 18.0), np.array([0.1170, 0.1920, 30.0300])), - (('5B', 6.0, 20.0), np.array([0.1020, 0.1810, 30.0300])), - (('7.5B', 6.0, 2.0), np.array([0.2854, 0.3037, 30.0300])), - (('7.5B', 6.0, 4.0), np.array([0.2602, 0.2881, 30.0300])), - (('7.5B', 6.0, 6.0), np.array([0.2352, 0.2708, 30.0300])), - (('7.5B', 6.0, 8.0), np.array([0.2132, 0.2537, 30.0300])), - (('7.5B', 6.0, 10.0), np.array([0.1934, 0.2374, 30.0300])), - (('7.5B', 6.0, 12.0), np.array([0.1734, 0.2203, 30.0300])), - (('7.5B', 6.0, 14.0), np.array([0.1556, 0.2043, 30.0300])), - (('7.5B', 6.0, 16.0), np.array([0.1376, 0.1879, 30.0300])), - (('7.5B', 6.0, 18.0), np.array([0.1230, 0.1740, 30.0300])), - (('7.5B', 6.0, 20.0), np.array([0.1110, 0.1620, 30.0300])), - (('7.5B', 6.0, 22.0), np.array([0.1000, 0.1510, 30.0300])), - (('10B', 6.0, 2.0), np.array([0.2871, 0.3012, 30.0300])), - (('10B', 6.0, 4.0), np.array([0.2637, 0.2840, 30.0300])), - (('10B', 6.0, 6.0), np.array([0.2399, 0.2650, 30.0300])), - (('10B', 6.0, 8.0), np.array([0.2189, 0.2468, 30.0300])), - (('10B', 6.0, 10.0), np.array([0.2000, 0.2298, 30.0300])), - (('10B', 6.0, 12.0), np.array([0.1803, 0.2114, 30.0300])), - (('10B', 6.0, 14.0), np.array([0.1629, 0.1947, 30.0300])), - (('10B', 6.0, 16.0), np.array([0.1454, 0.1778, 30.0300])), - (('10B', 6.0, 18.0), np.array([0.1310, 0.1640, 30.0300])), - (('10B', 6.0, 20.0), np.array([0.1200, 0.1530, 30.0300])), - (('10B', 6.0, 22.0), np.array([0.1110, 0.1440, 30.0300])), - (('10B', 6.0, 24.0), np.array([0.0990, 0.1330, 30.0300])), - (('2.5PB', 6.0, 2.0), np.array([0.2897, 0.2991, 30.0300])), - (('2.5PB', 6.0, 4.0), np.array([0.2684, 0.2804, 30.0300])), - (('2.5PB', 6.0, 6.0), np.array([0.2465, 0.2599, 30.0300])), - (('2.5PB', 6.0, 8.0), np.array([0.2274, 0.2406, 30.0300])), - (('2.5PB', 6.0, 10.0), np.array([0.2095, 0.2225, 30.0300])), - (('2.5PB', 6.0, 12.0), np.array([0.1913, 0.2038, 30.0300])), - (('2.5PB', 6.0, 14.0), np.array([0.1754, 0.1868, 30.0300])), - (('2.5PB', 6.0, 16.0), np.array([0.1590, 0.1690, 30.0300])), - (('2.5PB', 6.0, 18.0), np.array([0.1480, 0.1560, 30.0300])), - (('2.5PB', 6.0, 20.0), np.array([0.1370, 0.1440, 30.0300])), - (('2.5PB', 6.0, 22.0), np.array([0.1270, 0.1340, 30.0300])), - (('2.5PB', 6.0, 24.0), np.array([0.1170, 0.1230, 30.0300])), - (('5PB', 6.0, 2.0), np.array([0.2923, 0.2978, 30.0300])), - (('5PB', 6.0, 4.0), np.array([0.2734, 0.2778, 30.0300])), - (('5PB', 6.0, 6.0), np.array([0.2533, 0.2558, 30.0300])), - (('5PB', 6.0, 8.0), np.array([0.2360, 0.2365, 30.0300])), - (('5PB', 6.0, 10.0), np.array([0.2197, 0.2188, 30.0300])), - (('5PB', 6.0, 12.0), np.array([0.2026, 0.1999, 30.0300])), - (('5PB', 6.0, 14.0), np.array([0.1873, 0.1822, 30.0300])), - (('5PB', 6.0, 16.0), np.array([0.1710, 0.1640, 30.0300])), - (('5PB', 6.0, 18.0), np.array([0.1620, 0.1530, 30.0300])), - (('5PB', 6.0, 20.0), np.array([0.1510, 0.1400, 30.0300])), - (('5PB', 6.0, 22.0), np.array([0.1430, 0.1290, 30.0300])), - (('5PB', 6.0, 24.0), np.array([0.1350, 0.1180, 30.0300])), - (('5PB', 6.0, 26.0), np.array([0.1260, 0.1070, 30.0300])), - (('7.5PB', 6.0, 2.0), np.array([0.2955, 0.2963, 30.0300])), - (('7.5PB', 6.0, 4.0), np.array([0.2798, 0.2752, 30.0300])), - (('7.5PB', 6.0, 6.0), np.array([0.2638, 0.2531, 30.0300])), - (('7.5PB', 6.0, 8.0), np.array([0.2505, 0.2347, 30.0300])), - (('7.5PB', 6.0, 10.0), np.array([0.2378, 0.2168, 30.0300])), - (('7.5PB', 6.0, 12.0), np.array([0.2241, 0.1975, 30.0300])), - (('7.5PB', 6.0, 14.0), np.array([0.2119, 0.1799, 30.0300])), - (('7.5PB', 6.0, 16.0), np.array([0.1990, 0.1630, 30.0300])), - (('7.5PB', 6.0, 18.0), np.array([0.1920, 0.1510, 30.0300])), - (('7.5PB', 6.0, 20.0), np.array([0.1850, 0.1390, 30.0300])), - (('7.5PB', 6.0, 22.0), np.array([0.1790, 0.1280, 30.0300])), - (('7.5PB', 6.0, 24.0), np.array([0.1730, 0.1180, 30.0300])), - (('7.5PB', 6.0, 26.0), np.array([0.1660, 0.1050, 30.0300])), - (('10PB', 6.0, 2.0), np.array([0.2988, 0.2961, 30.0300])), - (('10PB', 6.0, 4.0), np.array([0.2863, 0.2747, 30.0300])), - (('10PB', 6.0, 6.0), np.array([0.2740, 0.2533, 30.0300])), - (('10PB', 6.0, 8.0), np.array([0.2637, 0.2352, 30.0300])), - (('10PB', 6.0, 10.0), np.array([0.2540, 0.2176, 30.0300])), - (('10PB', 6.0, 12.0), np.array([0.2440, 0.1998, 30.0300])), - (('10PB', 6.0, 14.0), np.array([0.2352, 0.1839, 30.0300])), - (('10PB', 6.0, 16.0), np.array([0.2265, 0.1671, 30.0300])), - (('10PB', 6.0, 18.0), np.array([0.2210, 0.1560, 30.0300])), - (('10PB', 6.0, 20.0), np.array([0.2140, 0.1430, 30.0300])), - (('10PB', 6.0, 22.0), np.array([0.2080, 0.1320, 30.0300])), - (('10PB', 6.0, 24.0), np.array([0.2040, 0.1230, 30.0300])), - (('10PB', 6.0, 26.0), np.array([0.1990, 0.1110, 30.0300])), - (('10PB', 6.0, 28.0), np.array([0.1940, 0.1010, 30.0300])), - (('2.5P', 6.0, 2.0), np.array([0.3016, 0.2960, 30.0300])), - (('2.5P', 6.0, 4.0), np.array([0.2932, 0.2759, 30.0300])), - (('2.5P', 6.0, 6.0), np.array([0.2842, 0.2550, 30.0300])), - (('2.5P', 6.0, 8.0), np.array([0.2770, 0.2372, 30.0300])), - (('2.5P', 6.0, 10.0), np.array([0.2703, 0.2204, 30.0300])), - (('2.5P', 6.0, 12.0), np.array([0.2647, 0.2052, 30.0300])), - (('2.5P', 6.0, 14.0), np.array([0.2593, 0.1909, 30.0300])), - (('2.5P', 6.0, 16.0), np.array([0.2548, 0.1768, 30.0300])), - (('2.5P', 6.0, 18.0), np.array([0.2504, 0.1658, 30.0300])), - (('2.5P', 6.0, 20.0), np.array([0.2460, 0.1530, 30.0300])), - (('2.5P', 6.0, 22.0), np.array([0.2420, 0.1420, 30.0300])), - (('2.5P', 6.0, 24.0), np.array([0.2390, 0.1320, 30.0300])), - (('2.5P', 6.0, 26.0), np.array([0.2350, 0.1210, 30.0300])), - (('2.5P', 6.0, 28.0), np.array([0.2310, 0.1120, 30.0300])), - (('2.5P', 6.0, 30.0), np.array([0.2280, 0.1020, 30.0300])), - (('2.5P', 6.0, 32.0), np.array([0.2250, 0.0930, 30.0300])), - (('5P', 6.0, 2.0), np.array([0.3050, 0.2967, 30.0300])), - (('5P', 6.0, 4.0), np.array([0.3001, 0.2778, 30.0300])), - (('5P', 6.0, 6.0), np.array([0.2950, 0.2585, 30.0300])), - (('5P', 6.0, 8.0), np.array([0.2905, 0.2421, 30.0300])), - (('5P', 6.0, 10.0), np.array([0.2862, 0.2260, 30.0300])), - (('5P', 6.0, 12.0), np.array([0.2829, 0.2121, 30.0300])), - (('5P', 6.0, 14.0), np.array([0.2794, 0.1979, 30.0300])), - (('5P', 6.0, 16.0), np.array([0.2761, 0.1852, 30.0300])), - (('5P', 6.0, 18.0), np.array([0.2731, 0.1738, 30.0300])), - (('5P', 6.0, 20.0), np.array([0.2702, 0.1621, 30.0300])), - (('5P', 6.0, 22.0), np.array([0.2670, 0.1490, 30.0300])), - (('5P', 6.0, 24.0), np.array([0.2650, 0.1400, 30.0300])), - (('5P', 6.0, 26.0), np.array([0.2630, 0.1300, 30.0300])), - (('5P', 6.0, 28.0), np.array([0.2600, 0.1200, 30.0300])), - (('5P', 6.0, 30.0), np.array([0.2580, 0.1110, 30.0300])), - (('5P', 6.0, 32.0), np.array([0.2560, 0.1030, 30.0300])), - (('5P', 6.0, 34.0), np.array([0.2540, 0.0950, 30.0300])), - (('5P', 6.0, 36.0), np.array([0.2520, 0.0890, 30.0300])), - (('7.5P', 6.0, 2.0), np.array([0.3107, 0.2993, 30.0300])), - (('7.5P', 6.0, 4.0), np.array([0.3107, 0.2831, 30.0300])), - (('7.5P', 6.0, 6.0), np.array([0.3101, 0.2650, 30.0300])), - (('7.5P', 6.0, 8.0), np.array([0.3099, 0.2502, 30.0300])), - (('7.5P', 6.0, 10.0), np.array([0.3092, 0.2350, 30.0300])), - (('7.5P', 6.0, 12.0), np.array([0.3090, 0.2222, 30.0300])), - (('7.5P', 6.0, 14.0), np.array([0.3084, 0.2095, 30.0300])), - (('7.5P', 6.0, 16.0), np.array([0.3080, 0.1976, 30.0300])), - (('7.5P', 6.0, 18.0), np.array([0.3075, 0.1870, 30.0300])), - (('7.5P', 6.0, 20.0), np.array([0.3069, 0.1745, 30.0300])), - (('7.5P', 6.0, 22.0), np.array([0.3062, 0.1638, 30.0300])), - (('7.5P', 6.0, 24.0), np.array([0.3058, 0.1547, 30.0300])), - (('7.5P', 6.0, 26.0), np.array([0.3050, 0.1440, 30.0300])), - (('7.5P', 6.0, 28.0), np.array([0.3040, 0.1360, 30.0300])), - (('7.5P', 6.0, 30.0), np.array([0.3030, 0.1260, 30.0300])), - (('7.5P', 6.0, 32.0), np.array([0.3020, 0.1180, 30.0300])), - (('7.5P', 6.0, 34.0), np.array([0.3010, 0.1110, 30.0300])), - (('7.5P', 6.0, 36.0), np.array([0.3010, 0.1050, 30.0300])), - (('10P', 6.0, 2.0), np.array([0.3146, 0.3018, 30.0300])), - (('10P', 6.0, 4.0), np.array([0.3181, 0.2871, 30.0300])), - (('10P', 6.0, 6.0), np.array([0.3226, 0.2716, 30.0300])), - (('10P', 6.0, 8.0), np.array([0.3259, 0.2584, 30.0300])), - (('10P', 6.0, 10.0), np.array([0.3293, 0.2450, 30.0300])), - (('10P', 6.0, 12.0), np.array([0.3321, 0.2329, 30.0300])), - (('10P', 6.0, 14.0), np.array([0.3349, 0.2203, 30.0300])), - (('10P', 6.0, 16.0), np.array([0.3370, 0.2095, 30.0300])), - (('10P', 6.0, 18.0), np.array([0.3388, 0.1995, 30.0300])), - (('10P', 6.0, 20.0), np.array([0.3409, 0.1882, 30.0300])), - (('10P', 6.0, 22.0), np.array([0.3426, 0.1785, 30.0300])), - (('10P', 6.0, 24.0), np.array([0.3441, 0.1698, 30.0300])), - (('10P', 6.0, 26.0), np.array([0.3457, 0.1604, 30.0300])), - (('10P', 6.0, 28.0), np.array([0.3470, 0.1510, 30.0300])), - (('10P', 6.0, 30.0), np.array([0.3490, 0.1440, 30.0300])), - (('10P', 6.0, 32.0), np.array([0.3500, 0.1360, 30.0300])), - (('10P', 6.0, 34.0), np.array([0.3510, 0.1290, 30.0300])), - (('10P', 6.0, 36.0), np.array([0.3520, 0.1230, 30.0300])), - (('2.5RP', 6.0, 2.0), np.array([0.3188, 0.3048, 30.0300])), - (('2.5RP', 6.0, 4.0), np.array([0.3272, 0.2929, 30.0300])), - (('2.5RP', 6.0, 6.0), np.array([0.3362, 0.2799, 30.0300])), - (('2.5RP', 6.0, 8.0), np.array([0.3437, 0.2688, 30.0300])), - (('2.5RP', 6.0, 10.0), np.array([0.3509, 0.2578, 30.0300])), - (('2.5RP', 6.0, 12.0), np.array([0.3582, 0.2462, 30.0300])), - (('2.5RP', 6.0, 14.0), np.array([0.3652, 0.2355, 30.0300])), - (('2.5RP', 6.0, 16.0), np.array([0.3718, 0.2251, 30.0300])), - (('2.5RP', 6.0, 18.0), np.array([0.3773, 0.2158, 30.0300])), - (('2.5RP', 6.0, 20.0), np.array([0.3833, 0.2056, 30.0300])), - (('2.5RP', 6.0, 22.0), np.array([0.3877, 0.1978, 30.0300])), - (('2.5RP', 6.0, 24.0), np.array([0.3927, 0.1892, 30.0300])), - (('2.5RP', 6.0, 26.0), np.array([0.3970, 0.1810, 30.0300])), - (('2.5RP', 6.0, 28.0), np.array([0.4010, 0.1740, 30.0300])), - (('2.5RP', 6.0, 30.0), np.array([0.4060, 0.1660, 30.0300])), - (('2.5RP', 6.0, 32.0), np.array([0.4100, 0.1590, 30.0300])), - (('2.5RP', 6.0, 34.0), np.array([0.4130, 0.1530, 30.0300])), - (('2.5RP', 6.0, 36.0), np.array([0.4160, 0.1480, 30.0300])), - (('5RP', 6.0, 2.0), np.array([0.3232, 0.3085, 30.0300])), - (('5RP', 6.0, 4.0), np.array([0.3371, 0.3001, 30.0300])), - (('5RP', 6.0, 6.0), np.array([0.3520, 0.2904, 30.0300])), - (('5RP', 6.0, 8.0), np.array([0.3648, 0.2820, 30.0300])), - (('5RP', 6.0, 10.0), np.array([0.3769, 0.2738, 30.0300])), - (('5RP', 6.0, 12.0), np.array([0.3900, 0.2646, 30.0300])), - (('5RP', 6.0, 14.0), np.array([0.4023, 0.2552, 30.0300])), - (('5RP', 6.0, 16.0), np.array([0.4136, 0.2467, 30.0300])), - (('5RP', 6.0, 18.0), np.array([0.4245, 0.2382, 30.0300])), - (('5RP', 6.0, 20.0), np.array([0.4368, 0.2283, 30.0300])), - (('5RP', 6.0, 22.0), np.array([0.4449, 0.2219, 30.0300])), - (('5RP', 6.0, 24.0), np.array([0.4520, 0.2150, 30.0300])), - (('5RP', 6.0, 26.0), np.array([0.4620, 0.2070, 30.0300])), - (('5RP', 6.0, 28.0), np.array([0.4680, 0.2010, 30.0300])), - (('5RP', 6.0, 30.0), np.array([0.4740, 0.1960, 30.0300])), - (('5RP', 6.0, 32.0), np.array([0.4810, 0.1890, 30.0300])), - (('7.5RP', 6.0, 2.0), np.array([0.3261, 0.3113, 30.0300])), - (('7.5RP', 6.0, 4.0), np.array([0.3439, 0.3056, 30.0300])), - (('7.5RP', 6.0, 6.0), np.array([0.3635, 0.2987, 30.0300])), - (('7.5RP', 6.0, 8.0), np.array([0.3791, 0.2929, 30.0300])), - (('7.5RP', 6.0, 10.0), np.array([0.3960, 0.2860, 30.0300])), - (('7.5RP', 6.0, 12.0), np.array([0.4125, 0.2784, 30.0300])), - (('7.5RP', 6.0, 14.0), np.array([0.4285, 0.2705, 30.0300])), - (('7.5RP', 6.0, 16.0), np.array([0.4448, 0.2622, 30.0300])), - (('7.5RP', 6.0, 18.0), np.array([0.4581, 0.2549, 30.0300])), - (('7.5RP', 6.0, 20.0), np.array([0.4735, 0.2464, 30.0300])), - (('7.5RP', 6.0, 22.0), np.array([0.4860, 0.2400, 30.0300])), - (('7.5RP', 6.0, 24.0), np.array([0.4960, 0.2330, 30.0300])), - (('7.5RP', 6.0, 26.0), np.array([0.5050, 0.2270, 30.0300])), - (('7.5RP', 6.0, 28.0), np.array([0.5130, 0.2210, 30.0300])), - (('7.5RP', 6.0, 30.0), np.array([0.5190, 0.2160, 30.0300])), - (('10RP', 6.0, 2.0), np.array([0.3292, 0.3141, 30.0300])), - (('10RP', 6.0, 4.0), np.array([0.3508, 0.3112, 30.0300])), - (('10RP', 6.0, 6.0), np.array([0.3740, 0.3074, 30.0300])), - (('10RP', 6.0, 8.0), np.array([0.3930, 0.3038, 30.0300])), - (('10RP', 6.0, 10.0), np.array([0.4150, 0.2989, 30.0300])), - (('10RP', 6.0, 12.0), np.array([0.4360, 0.2936, 30.0300])), - (('10RP', 6.0, 14.0), np.array([0.4552, 0.2881, 30.0300])), - (('10RP', 6.0, 16.0), np.array([0.4781, 0.2812, 30.0300])), - (('10RP', 6.0, 18.0), np.array([0.4961, 0.2751, 30.0300])), - (('10RP', 6.0, 20.0), np.array([0.5130, 0.2680, 30.0300])), - (('10RP', 6.0, 22.0), np.array([0.5270, 0.2620, 30.0300])), - (('10RP', 6.0, 24.0), np.array([0.5410, 0.2560, 30.0300])), - (('10RP', 6.0, 26.0), np.array([0.5520, 0.2500, 30.0300])), - (('10RP', 6.0, 28.0), np.array([0.5630, 0.2460, 30.0300])), - (('2.5R', 6.0, 2.0), np.array([0.3318, 0.3166, 30.0300])), - (('2.5R', 6.0, 4.0), np.array([0.3566, 0.3163, 30.0300])), - (('2.5R', 6.0, 6.0), np.array([0.3832, 0.3158, 30.0300])), - (('2.5R', 6.0, 8.0), np.array([0.4065, 0.3144, 30.0300])), - (('2.5R', 6.0, 10.0), np.array([0.4320, 0.3118, 30.0300])), - (('2.5R', 6.0, 12.0), np.array([0.4568, 0.3082, 30.0300])), - (('2.5R', 6.0, 14.0), np.array([0.4790, 0.3041, 30.0300])), - (('2.5R', 6.0, 16.0), np.array([0.5041, 0.2983, 30.0300])), - (('2.5R', 6.0, 18.0), np.array([0.5262, 0.2928, 30.0300])), - (('2.5R', 6.0, 20.0), np.array([0.5450, 0.2880, 30.0300])), - (('2.5R', 6.0, 22.0), np.array([0.5610, 0.2820, 30.0300])), - (('2.5R', 6.0, 24.0), np.array([0.5740, 0.2760, 30.0300])), - (('2.5R', 6.0, 26.0), np.array([0.5880, 0.2710, 30.0300])), - (('2.5R', 6.0, 28.0), np.array([0.5970, 0.2650, 30.0300])), - (('5R', 6.0, 2.0), np.array([0.3343, 0.3190, 30.0300])), - (('5R', 6.0, 4.0), np.array([0.3628, 0.3221, 30.0300])), - (('5R', 6.0, 6.0), np.array([0.3921, 0.3244, 30.0300])), - (('5R', 6.0, 8.0), np.array([0.4187, 0.3251, 30.0300])), - (('5R', 6.0, 10.0), np.array([0.4480, 0.3250, 30.0300])), - (('5R', 6.0, 12.0), np.array([0.4760, 0.3234, 30.0300])), - (('5R', 6.0, 14.0), np.array([0.5020, 0.3212, 30.0300])), - (('5R', 6.0, 16.0), np.array([0.5297, 0.3179, 30.0300])), - (('5R', 6.0, 18.0), np.array([0.5552, 0.3138, 30.0300])), - (('5R', 6.0, 20.0), np.array([0.5750, 0.3090, 30.0300])), - (('5R', 6.0, 22.0), np.array([0.5930, 0.3040, 30.0300])), - (('5R', 6.0, 24.0), np.array([0.6070, 0.2990, 30.0300])), - (('5R', 6.0, 26.0), np.array([0.6180, 0.2950, 30.0300])), - (('5R', 6.0, 28.0), np.array([0.6320, 0.2890, 30.0300])), - (('5R', 6.0, 30.0), np.array([0.6430, 0.2830, 30.0300])), - (('7.5R', 6.0, 2.0), np.array([0.3381, 0.3228, 30.0300])), - (('7.5R', 6.0, 4.0), np.array([0.3692, 0.3291, 30.0300])), - (('7.5R', 6.0, 6.0), np.array([0.4000, 0.3340, 30.0300])), - (('7.5R', 6.0, 8.0), np.array([0.4318, 0.3383, 30.0300])), - (('7.5R', 6.0, 10.0), np.array([0.4655, 0.3412, 30.0300])), - (('7.5R', 6.0, 12.0), np.array([0.4961, 0.3428, 30.0300])), - (('7.5R', 6.0, 14.0), np.array([0.5265, 0.3431, 30.0300])), - (('7.5R', 6.0, 16.0), np.array([0.5560, 0.3420, 30.0300])), - (('7.5R', 6.0, 18.0), np.array([0.5829, 0.3396, 30.0300])), - (('7.5R', 6.0, 20.0), np.array([0.6030, 0.3360, 30.0300])), - (('7.5R', 6.0, 22.0), np.array([0.6250, 0.3330, 30.0300])), - (('7.5R', 6.0, 24.0), np.array([0.6390, 0.3300, 30.0300])), - (('7.5R', 6.0, 26.0), np.array([0.6520, 0.3260, 30.0300])), - (('7.5R', 6.0, 28.0), np.array([0.6670, 0.3210, 30.0300])), - (('7.5R', 6.0, 30.0), np.array([0.6790, 0.3160, 30.0300])), - (('10R', 6.0, 2.0), np.array([0.3417, 0.3268, 30.0300])), - (('10R', 6.0, 4.0), np.array([0.3768, 0.3381, 30.0300])), - (('10R', 6.0, 6.0), np.array([0.4103, 0.3473, 30.0300])), - (('10R', 6.0, 8.0), np.array([0.4449, 0.3550, 30.0300])), - (('10R', 6.0, 10.0), np.array([0.4812, 0.3619, 30.0300])), - (('10R', 6.0, 12.0), np.array([0.5150, 0.3667, 30.0300])), - (('10R', 6.0, 14.0), np.array([0.5468, 0.3697, 30.0300])), - (('10R', 6.0, 16.0), np.array([0.5741, 0.3713, 30.0300])), - (('10R', 6.0, 18.0), np.array([0.6009, 0.3720, 30.0300])), - (('10R', 6.0, 20.0), np.array([0.6240, 0.3710, 30.0300])), - (('10R', 6.0, 22.0), np.array([0.6460, 0.3700, 30.0300])), - (('10R', 6.0, 24.0), np.array([0.6640, 0.3700, 30.0300])), - (('10R', 6.0, 26.0), np.array([0.6790, 0.3690, 30.0300])), - (('10R', 6.0, 28.0), np.array([0.6960, 0.3680, 30.0300])), - (('10R', 6.0, 30.0), np.array([0.7140, 0.3650, 30.0300])), - (('2.5YR', 6.0, 2.0), np.array([0.3453, 0.3321, 30.0300])), - (('2.5YR', 6.0, 4.0), np.array([0.3806, 0.3467, 30.0300])), - (('2.5YR', 6.0, 6.0), np.array([0.4180, 0.3600, 30.0300])), - (('2.5YR', 6.0, 8.0), np.array([0.4533, 0.3708, 30.0300])), - (('2.5YR', 6.0, 10.0), np.array([0.4891, 0.3806, 30.0300])), - (('2.5YR', 6.0, 12.0), np.array([0.5215, 0.3887, 30.0300])), - (('2.5YR', 6.0, 14.0), np.array([0.5488, 0.3947, 30.0300])), - (('2.5YR', 6.0, 16.0), np.array([0.5698, 0.3990, 30.0300])), - (('2.5YR', 6.0, 18.0), np.array([0.5879, 0.4021, 30.0300])), - (('2.5YR', 6.0, 20.0), np.array([0.6040, 0.4030, 30.0300])), - (('2.5YR', 6.0, 22.0), np.array([0.6140, 0.4060, 30.0300])), - (('5YR', 6.0, 2.0), np.array([0.3474, 0.3373, 30.0300])), - (('5YR', 6.0, 4.0), np.array([0.3840, 0.3564, 30.0300])), - (('5YR', 6.0, 6.0), np.array([0.4229, 0.3750, 30.0300])), - (('5YR', 6.0, 8.0), np.array([0.4592, 0.3900, 30.0300])), - (('5YR', 6.0, 10.0), np.array([0.4921, 0.4022, 30.0300])), - (('5YR', 6.0, 12.0), np.array([0.5199, 0.4119, 30.0300])), - (('5YR', 6.0, 14.0), np.array([0.5423, 0.4188, 30.0300])), - (('5YR', 6.0, 16.0), np.array([0.5597, 0.4239, 30.0300])), - (('5YR', 6.0, 18.0), np.array([0.5715, 0.4270, 30.0300])), - (('5YR', 6.0, 20.0), np.array([0.5850, 0.4310, 30.0300])), - (('7.5YR', 6.0, 2.0), np.array([0.3487, 0.3421, 30.0300])), - (('7.5YR', 6.0, 4.0), np.array([0.3860, 0.3652, 30.0300])), - (('7.5YR', 6.0, 6.0), np.array([0.4242, 0.3876, 30.0300])), - (('7.5YR', 6.0, 8.0), np.array([0.4596, 0.4064, 30.0300])), - (('7.5YR', 6.0, 10.0), np.array([0.4904, 0.4220, 30.0300])), - (('7.5YR', 6.0, 12.0), np.array([0.5145, 0.4331, 30.0300])), - (('7.5YR', 6.0, 14.0), np.array([0.5320, 0.4412, 30.0300])), - (('7.5YR', 6.0, 16.0), np.array([0.5468, 0.4478, 30.0300])), - (('7.5YR', 6.0, 18.0), np.array([0.5550, 0.4520, 30.0300])), - (('7.5YR', 6.0, 20.0), np.array([0.5640, 0.4550, 30.0300])), - (('10YR', 6.0, 2.0), np.array([0.3491, 0.3483, 30.0300])), - (('10YR', 6.0, 4.0), np.array([0.3861, 0.3767, 30.0300])), - (('10YR', 6.0, 6.0), np.array([0.4240, 0.4030, 30.0300])), - (('10YR', 6.0, 8.0), np.array([0.4570, 0.4249, 30.0300])), - (('10YR', 6.0, 10.0), np.array([0.4843, 0.4416, 30.0300])), - (('10YR', 6.0, 12.0), np.array([0.5050, 0.4536, 30.0300])), - (('10YR', 6.0, 14.0), np.array([0.5200, 0.4623, 30.0300])), - (('10YR', 6.0, 16.0), np.array([0.5310, 0.4690, 30.0300])), - (('10YR', 6.0, 18.0), np.array([0.5390, 0.4720, 30.0300])), - (('2.5Y', 6.0, 2.0), np.array([0.3480, 0.3540, 30.0300])), - (('2.5Y', 6.0, 4.0), np.array([0.3840, 0.3867, 30.0300])), - (('2.5Y', 6.0, 6.0), np.array([0.4203, 0.4176, 30.0300])), - (('2.5Y', 6.0, 8.0), np.array([0.4517, 0.4421, 30.0300])), - (('2.5Y', 6.0, 10.0), np.array([0.4760, 0.4607, 30.0300])), - (('2.5Y', 6.0, 12.0), np.array([0.4928, 0.4730, 30.0300])), - (('2.5Y', 6.0, 14.0), np.array([0.5061, 0.4829, 30.0300])), - (('2.5Y', 6.0, 16.0), np.array([0.5160, 0.4890, 30.0300])), - (('2.5Y', 6.0, 18.0), np.array([0.5230, 0.4940, 30.0300])), - (('5Y', 6.0, 2.0), np.array([0.3457, 0.3580, 30.0300])), - (('5Y', 6.0, 4.0), np.array([0.3794, 0.3955, 30.0300])), - (('5Y', 6.0, 6.0), np.array([0.4140, 0.4305, 30.0300])), - (('5Y', 6.0, 8.0), np.array([0.4426, 0.4588, 30.0300])), - (('5Y', 6.0, 10.0), np.array([0.4639, 0.4790, 30.0300])), - (('5Y', 6.0, 12.0), np.array([0.4780, 0.4920, 30.0300])), - (('5Y', 6.0, 14.0), np.array([0.4905, 0.5038, 30.0300])), - (('5Y', 6.0, 16.0), np.array([0.4990, 0.5100, 30.0300])), - (('7.5Y', 6.0, 2.0), np.array([0.3431, 0.3601, 30.0300])), - (('7.5Y', 6.0, 4.0), np.array([0.3745, 0.4004, 30.0300])), - (('7.5Y', 6.0, 6.0), np.array([0.4060, 0.4400, 30.0300])), - (('7.5Y', 6.0, 8.0), np.array([0.4321, 0.4719, 30.0300])), - (('7.5Y', 6.0, 10.0), np.array([0.4512, 0.4943, 30.0300])), - (('7.5Y', 6.0, 12.0), np.array([0.4638, 0.5087, 30.0300])), - (('7.5Y', 6.0, 14.0), np.array([0.4754, 0.5220, 30.0300])), - (('7.5Y', 6.0, 16.0), np.array([0.4820, 0.5280, 30.0300])), - (('10Y', 7.0, 2.0), np.array([0.3369, 0.3569, 43.0600])), - (('10Y', 7.0, 4.0), np.array([0.3624, 0.3951, 43.0600])), - (('10Y', 7.0, 6.0), np.array([0.3864, 0.4305, 43.0600])), - (('10Y', 7.0, 8.0), np.array([0.4090, 0.4641, 43.0600])), - (('10Y', 7.0, 10.0), np.array([0.4289, 0.4937, 43.0600])), - (('10Y', 7.0, 12.0), np.array([0.4420, 0.5131, 43.0600])), - (('10Y', 7.0, 14.0), np.array([0.4516, 0.5277, 43.0600])), - (('10Y', 7.0, 16.0), np.array([0.4582, 0.5375, 43.0600])), - (('10Y', 7.0, 18.0), np.array([0.4620, 0.5430, 43.0600])), - (('2.5GY', 7.0, 2.0), np.array([0.3328, 0.3569, 43.0600])), - (('2.5GY', 7.0, 4.0), np.array([0.3534, 0.3953, 43.0600])), - (('2.5GY', 7.0, 6.0), np.array([0.3728, 0.4316, 43.0600])), - (('2.5GY', 7.0, 8.0), np.array([0.3919, 0.4684, 43.0600])), - (('2.5GY', 7.0, 10.0), np.array([0.4091, 0.5030, 43.0600])), - (('2.5GY', 7.0, 12.0), np.array([0.4213, 0.5270, 43.0600])), - (('2.5GY', 7.0, 14.0), np.array([0.4309, 0.5459, 43.0600])), - (('2.5GY', 7.0, 16.0), np.array([0.4366, 0.5578, 43.0600])), - (('2.5GY', 7.0, 18.0), np.array([0.4390, 0.5640, 43.0600])), - (('5GY', 7.0, 2.0), np.array([0.3284, 0.3559, 43.0600])), - (('5GY', 7.0, 4.0), np.array([0.3437, 0.3929, 43.0600])), - (('5GY', 7.0, 6.0), np.array([0.3581, 0.4291, 43.0600])), - (('5GY', 7.0, 8.0), np.array([0.3722, 0.4669, 43.0600])), - (('5GY', 7.0, 10.0), np.array([0.3852, 0.5051, 43.0600])), - (('5GY', 7.0, 12.0), np.array([0.3949, 0.5367, 43.0600])), - (('5GY', 7.0, 14.0), np.array([0.4027, 0.5615, 43.0600])), - (('5GY', 7.0, 16.0), np.array([0.4076, 0.5783, 43.0600])), - (('5GY', 7.0, 18.0), np.array([0.4100, 0.5890, 43.0600])), - (('5GY', 7.0, 20.0), np.array([0.4130, 0.6010, 43.0600])), - (('7.5GY', 7.0, 2.0), np.array([0.3190, 0.3516, 43.0600])), - (('7.5GY', 7.0, 4.0), np.array([0.3267, 0.3848, 43.0600])), - (('7.5GY', 7.0, 6.0), np.array([0.3341, 0.4191, 43.0600])), - (('7.5GY', 7.0, 8.0), np.array([0.3406, 0.4558, 43.0600])), - (('7.5GY', 7.0, 10.0), np.array([0.3461, 0.4950, 43.0600])), - (('7.5GY', 7.0, 12.0), np.array([0.3502, 0.5328, 43.0600])), - (('7.5GY', 7.0, 14.0), np.array([0.3532, 0.5700, 43.0600])), - (('7.5GY', 7.0, 16.0), np.array([0.3549, 0.6000, 43.0600])), - (('7.5GY', 7.0, 18.0), np.array([0.3555, 0.6242, 43.0600])), - (('7.5GY', 7.0, 20.0), np.array([0.3560, 0.6420, 43.0600])), - (('7.5GY', 7.0, 22.0), np.array([0.3560, 0.6610, 43.0600])), - (('7.5GY', 7.0, 24.0), np.array([0.3560, 0.6760, 43.0600])), - (('10GY', 7.0, 2.0), np.array([0.3117, 0.3469, 43.0600])), - (('10GY', 7.0, 4.0), np.array([0.3133, 0.3764, 43.0600])), - (('10GY', 7.0, 6.0), np.array([0.3142, 0.4058, 43.0600])), - (('10GY', 7.0, 8.0), np.array([0.3140, 0.4387, 43.0600])), - (('10GY', 7.0, 10.0), np.array([0.3123, 0.4732, 43.0600])), - (('10GY', 7.0, 12.0), np.array([0.3092, 0.5095, 43.0600])), - (('10GY', 7.0, 14.0), np.array([0.3047, 0.5458, 43.0600])), - (('10GY', 7.0, 16.0), np.array([0.2981, 0.5835, 43.0600])), - (('10GY', 7.0, 18.0), np.array([0.2905, 0.6186, 43.0600])), - (('10GY', 7.0, 20.0), np.array([0.2816, 0.6563, 43.0600])), - (('10GY', 7.0, 22.0), np.array([0.2728, 0.6893, 43.0600])), - (('10GY', 7.0, 24.0), np.array([0.2620, 0.7270, 43.0600])), - (('10GY', 7.0, 26.0), np.array([0.2520, 0.7580, 43.0600])), - (('10GY', 7.0, 28.0), np.array([0.2420, 0.7900, 43.0600])), - (('10GY', 7.0, 30.0), np.array([0.2330, 0.8190, 43.0600])), - (('10GY', 7.0, 32.0), np.array([0.2240, 0.8510, 43.0600])), - (('2.5G', 7.0, 2.0), np.array([0.3047, 0.3413, 43.0600])), - (('2.5G', 7.0, 4.0), np.array([0.2992, 0.3644, 43.0600])), - (('2.5G', 7.0, 6.0), np.array([0.2933, 0.3873, 43.0600])), - (('2.5G', 7.0, 8.0), np.array([0.2861, 0.4129, 43.0600])), - (('2.5G', 7.0, 10.0), np.array([0.2775, 0.4395, 43.0600])), - (('2.5G', 7.0, 12.0), np.array([0.2672, 0.4667, 43.0600])), - (('2.5G', 7.0, 14.0), np.array([0.2568, 0.4931, 43.0600])), - (('2.5G', 7.0, 16.0), np.array([0.2448, 0.5203, 43.0600])), - (('2.5G', 7.0, 18.0), np.array([0.2328, 0.5467, 43.0600])), - (('2.5G', 7.0, 20.0), np.array([0.2181, 0.5744, 43.0600])), - (('2.5G', 7.0, 22.0), np.array([0.2029, 0.6017, 43.0600])), - (('2.5G', 7.0, 24.0), np.array([0.1875, 0.6265, 43.0600])), - (('2.5G', 7.0, 26.0), np.array([0.1689, 0.6549, 43.0600])), - (('2.5G', 7.0, 28.0), np.array([0.1490, 0.6810, 43.0600])), - (('2.5G', 7.0, 30.0), np.array([0.1260, 0.7090, 43.0600])), - (('2.5G', 7.0, 32.0), np.array([0.1060, 0.7330, 43.0600])), - (('2.5G', 7.0, 34.0), np.array([0.0810, 0.7590, 43.0600])), - (('5G', 7.0, 2.0), np.array([0.3001, 0.3366, 43.0600])), - (('5G', 7.0, 4.0), np.array([0.2902, 0.3548, 43.0600])), - (('5G', 7.0, 6.0), np.array([0.2801, 0.3721, 43.0600])), - (('5G', 7.0, 8.0), np.array([0.2687, 0.3901, 43.0600])), - (('5G', 7.0, 10.0), np.array([0.2554, 0.4087, 43.0600])), - (('5G', 7.0, 12.0), np.array([0.2416, 0.4267, 43.0600])), - (('5G', 7.0, 14.0), np.array([0.2262, 0.4450, 43.0600])), - (('5G', 7.0, 16.0), np.array([0.2111, 0.4616, 43.0600])), - (('5G', 7.0, 18.0), np.array([0.1967, 0.4771, 43.0600])), - (('5G', 7.0, 20.0), np.array([0.1805, 0.4933, 43.0600])), - (('5G', 7.0, 22.0), np.array([0.1659, 0.5074, 43.0600])), - (('5G', 7.0, 24.0), np.array([0.1521, 0.5200, 43.0600])), - (('5G', 7.0, 26.0), np.array([0.1397, 0.5312, 43.0600])), - (('5G', 7.0, 28.0), np.array([0.1230, 0.5460, 43.0600])), - (('5G', 7.0, 30.0), np.array([0.1050, 0.5600, 43.0600])), - (('5G', 7.0, 32.0), np.array([0.0880, 0.5730, 43.0600])), - (('5G', 7.0, 34.0), np.array([0.0690, 0.5870, 43.0600])), - (('7.5G', 7.0, 2.0), np.array([0.2972, 0.3333, 43.0600])), - (('7.5G', 7.0, 4.0), np.array([0.2850, 0.3482, 43.0600])), - (('7.5G', 7.0, 6.0), np.array([0.2728, 0.3622, 43.0600])), - (('7.5G', 7.0, 8.0), np.array([0.2595, 0.3764, 43.0600])), - (('7.5G', 7.0, 10.0), np.array([0.2445, 0.3914, 43.0600])), - (('7.5G', 7.0, 12.0), np.array([0.2295, 0.4058, 43.0600])), - (('7.5G', 7.0, 14.0), np.array([0.2139, 0.4199, 43.0600])), - (('7.5G', 7.0, 16.0), np.array([0.1982, 0.4330, 43.0600])), - (('7.5G', 7.0, 18.0), np.array([0.1841, 0.4448, 43.0600])), - (('7.5G', 7.0, 20.0), np.array([0.1688, 0.4570, 43.0600])), - (('7.5G', 7.0, 22.0), np.array([0.1539, 0.4683, 43.0600])), - (('7.5G', 7.0, 24.0), np.array([0.1415, 0.4778, 43.0600])), - (('7.5G', 7.0, 26.0), np.array([0.1303, 0.4858, 43.0600])), - (('7.5G', 7.0, 28.0), np.array([0.1150, 0.4970, 43.0600])), - (('7.5G', 7.0, 30.0), np.array([0.0990, 0.5070, 43.0600])), - (('7.5G', 7.0, 32.0), np.array([0.0840, 0.5170, 43.0600])), - (('7.5G', 7.0, 34.0), np.array([0.0690, 0.5270, 43.0600])), - (('10G', 7.0, 2.0), np.array([0.2945, 0.3297, 43.0600])), - (('10G', 7.0, 4.0), np.array([0.2803, 0.3415, 43.0600])), - (('10G', 7.0, 6.0), np.array([0.2662, 0.3526, 43.0600])), - (('10G', 7.0, 8.0), np.array([0.2513, 0.3635, 43.0600])), - (('10G', 7.0, 10.0), np.array([0.2352, 0.3748, 43.0600])), - (('10G', 7.0, 12.0), np.array([0.2195, 0.3854, 43.0600])), - (('10G', 7.0, 14.0), np.array([0.2033, 0.3956, 43.0600])), - (('10G', 7.0, 16.0), np.array([0.1881, 0.4049, 43.0600])), - (('10G', 7.0, 18.0), np.array([0.1734, 0.4135, 43.0600])), - (('10G', 7.0, 20.0), np.array([0.1589, 0.4220, 43.0600])), - (('10G', 7.0, 22.0), np.array([0.1434, 0.4306, 43.0600])), - (('10G', 7.0, 24.0), np.array([0.1310, 0.4377, 43.0600])), - (('10G', 7.0, 26.0), np.array([0.1210, 0.4430, 43.0600])), - (('10G', 7.0, 28.0), np.array([0.1080, 0.4490, 43.0600])), - (('10G', 7.0, 30.0), np.array([0.0940, 0.4570, 43.0600])), - (('10G', 7.0, 32.0), np.array([0.0830, 0.4630, 43.0600])), - (('2.5BG', 7.0, 2.0), np.array([0.2927, 0.3269, 43.0600])), - (('2.5BG', 7.0, 4.0), np.array([0.2764, 0.3354, 43.0600])), - (('2.5BG', 7.0, 6.0), np.array([0.2608, 0.3430, 43.0600])), - (('2.5BG', 7.0, 8.0), np.array([0.2439, 0.3508, 43.0600])), - (('2.5BG', 7.0, 10.0), np.array([0.2264, 0.3576, 43.0600])), - (('2.5BG', 7.0, 12.0), np.array([0.2102, 0.3636, 43.0600])), - (('2.5BG', 7.0, 14.0), np.array([0.1932, 0.3694, 43.0600])), - (('2.5BG', 7.0, 16.0), np.array([0.1788, 0.3739, 43.0600])), - (('2.5BG', 7.0, 18.0), np.array([0.1626, 0.3788, 43.0600])), - (('2.5BG', 7.0, 20.0), np.array([0.1490, 0.3827, 43.0600])), - (('2.5BG', 7.0, 22.0), np.array([0.1334, 0.3870, 43.0600])), - (('2.5BG', 7.0, 24.0), np.array([0.1220, 0.3900, 43.0600])), - (('2.5BG', 7.0, 26.0), np.array([0.1110, 0.3920, 43.0600])), - (('2.5BG', 7.0, 28.0), np.array([0.0990, 0.3940, 43.0600])), - (('2.5BG', 7.0, 30.0), np.array([0.0880, 0.3970, 43.0600])), - (('2.5BG', 7.0, 32.0), np.array([0.0790, 0.3980, 43.0600])), - (('5BG', 7.0, 2.0), np.array([0.2898, 0.3225, 43.0600])), - (('5BG', 7.0, 4.0), np.array([0.2712, 0.3269, 43.0600])), - (('5BG', 7.0, 6.0), np.array([0.2543, 0.3302, 43.0600])), - (('5BG', 7.0, 8.0), np.array([0.2354, 0.3335, 43.0600])), - (('5BG', 7.0, 10.0), np.array([0.2163, 0.3361, 43.0600])), - (('5BG', 7.0, 12.0), np.array([0.1997, 0.3379, 43.0600])), - (('5BG', 7.0, 14.0), np.array([0.1838, 0.3390, 43.0600])), - (('5BG', 7.0, 16.0), np.array([0.1675, 0.3401, 43.0600])), - (('5BG', 7.0, 18.0), np.array([0.1515, 0.3410, 43.0600])), - (('5BG', 7.0, 20.0), np.array([0.1380, 0.3412, 43.0600])), - (('5BG', 7.0, 22.0), np.array([0.1220, 0.3400, 43.0600])), - (('5BG', 7.0, 24.0), np.array([0.1100, 0.3400, 43.0600])), - (('5BG', 7.0, 26.0), np.array([0.1010, 0.3400, 43.0600])), - (('5BG', 7.0, 28.0), np.array([0.0920, 0.3400, 43.0600])), - (('5BG', 7.0, 30.0), np.array([0.0830, 0.3390, 43.0600])), - (('7.5BG', 7.0, 2.0), np.array([0.2878, 0.3182, 43.0600])), - (('7.5BG', 7.0, 4.0), np.array([0.2671, 0.3189, 43.0600])), - (('7.5BG', 7.0, 6.0), np.array([0.2490, 0.3186, 43.0600])), - (('7.5BG', 7.0, 8.0), np.array([0.2292, 0.3178, 43.0600])), - (('7.5BG', 7.0, 10.0), np.array([0.2094, 0.3165, 43.0600])), - (('7.5BG', 7.0, 12.0), np.array([0.1914, 0.3148, 43.0600])), - (('7.5BG', 7.0, 14.0), np.array([0.1751, 0.3129, 43.0600])), - (('7.5BG', 7.0, 16.0), np.array([0.1584, 0.3101, 43.0600])), - (('7.5BG', 7.0, 18.0), np.array([0.1427, 0.3076, 43.0600])), - (('7.5BG', 7.0, 20.0), np.array([0.1300, 0.3050, 43.0600])), - (('7.5BG', 7.0, 22.0), np.array([0.1140, 0.3020, 43.0600])), - (('7.5BG', 7.0, 24.0), np.array([0.1020, 0.3000, 43.0600])), - (('7.5BG', 7.0, 26.0), np.array([0.0940, 0.2980, 43.0600])), - (('10BG', 7.0, 2.0), np.array([0.2869, 0.3143, 43.0600])), - (('10BG', 7.0, 4.0), np.array([0.2642, 0.3109, 43.0600])), - (('10BG', 7.0, 6.0), np.array([0.2448, 0.3069, 43.0600])), - (('10BG', 7.0, 8.0), np.array([0.2235, 0.3014, 43.0600])), - (('10BG', 7.0, 10.0), np.array([0.2035, 0.2956, 43.0600])), - (('10BG', 7.0, 12.0), np.array([0.1841, 0.2892, 43.0600])), - (('10BG', 7.0, 14.0), np.array([0.1671, 0.2832, 43.0600])), - (('10BG', 7.0, 16.0), np.array([0.1489, 0.2768, 43.0600])), - (('10BG', 7.0, 18.0), np.array([0.1340, 0.2710, 43.0600])), - (('10BG', 7.0, 20.0), np.array([0.1220, 0.2660, 43.0600])), - (('10BG', 7.0, 22.0), np.array([0.1080, 0.2600, 43.0600])), - (('10BG', 7.0, 24.0), np.array([0.0940, 0.2530, 43.0600])), - (('2.5B', 7.0, 2.0), np.array([0.2867, 0.3110, 43.0600])), - (('2.5B', 7.0, 4.0), np.array([0.2629, 0.3038, 43.0600])), - (('2.5B', 7.0, 6.0), np.array([0.2418, 0.2960, 43.0600])), - (('2.5B', 7.0, 8.0), np.array([0.2208, 0.2871, 43.0600])), - (('2.5B', 7.0, 10.0), np.array([0.1994, 0.2775, 43.0600])), - (('2.5B', 7.0, 12.0), np.array([0.1797, 0.2672, 43.0600])), - (('2.5B', 7.0, 14.0), np.array([0.1624, 0.2581, 43.0600])), - (('2.5B', 7.0, 16.0), np.array([0.1435, 0.2472, 43.0600])), - (('2.5B', 7.0, 18.0), np.array([0.1300, 0.2390, 43.0600])), - (('2.5B', 7.0, 20.0), np.array([0.1170, 0.2310, 43.0600])), - (('2.5B', 7.0, 22.0), np.array([0.1040, 0.2220, 43.0600])), - (('5B', 7.0, 2.0), np.array([0.2875, 0.3078, 43.0600])), - (('5B', 7.0, 4.0), np.array([0.2633, 0.2972, 43.0600])), - (('5B', 7.0, 6.0), np.array([0.2410, 0.2854, 43.0600])), - (('5B', 7.0, 8.0), np.array([0.2204, 0.2729, 43.0600])), - (('5B', 7.0, 10.0), np.array([0.1986, 0.2579, 43.0600])), - (('5B', 7.0, 12.0), np.array([0.1778, 0.2430, 43.0600])), - (('5B', 7.0, 14.0), np.array([0.1615, 0.2307, 43.0600])), - (('5B', 7.0, 16.0), np.array([0.1450, 0.2190, 43.0600])), - (('5B', 7.0, 18.0), np.array([0.1280, 0.2060, 43.0600])), - (('5B', 7.0, 20.0), np.array([0.1140, 0.1950, 43.0600])), - (('7.5B', 7.0, 2.0), np.array([0.2888, 0.3058, 43.0600])), - (('7.5B', 7.0, 4.0), np.array([0.2651, 0.2927, 43.0600])), - (('7.5B', 7.0, 6.0), np.array([0.2436, 0.2787, 43.0600])), - (('7.5B', 7.0, 8.0), np.array([0.2225, 0.2631, 43.0600])), - (('7.5B', 7.0, 10.0), np.array([0.2016, 0.2466, 43.0600])), - (('7.5B', 7.0, 12.0), np.array([0.1818, 0.2303, 43.0600])), - (('7.5B', 7.0, 14.0), np.array([0.1650, 0.2160, 43.0600])), - (('7.5B', 7.0, 16.0), np.array([0.1490, 0.2020, 43.0600])), - (('7.5B', 7.0, 18.0), np.array([0.1320, 0.1870, 43.0600])), - (('7.5B', 7.0, 20.0), np.array([0.1210, 0.1750, 43.0600])), - (('10B', 7.0, 2.0), np.array([0.2908, 0.3039, 43.0600])), - (('10B', 7.0, 4.0), np.array([0.2685, 0.2886, 43.0600])), - (('10B', 7.0, 6.0), np.array([0.2478, 0.2728, 43.0600])), - (('10B', 7.0, 8.0), np.array([0.2277, 0.2559, 43.0600])), - (('10B', 7.0, 10.0), np.array([0.2078, 0.2382, 43.0600])), - (('10B', 7.0, 12.0), np.array([0.1883, 0.2203, 43.0600])), - (('10B', 7.0, 14.0), np.array([0.1720, 0.2060, 43.0600])), - (('10B', 7.0, 16.0), np.array([0.1560, 0.1900, 43.0600])), - (('10B', 7.0, 18.0), np.array([0.1410, 0.1760, 43.0600])), - (('10B', 7.0, 20.0), np.array([0.1280, 0.1620, 43.0600])), - (('2.5PB', 7.0, 2.0), np.array([0.2932, 0.3025, 43.0600])), - (('2.5PB', 7.0, 4.0), np.array([0.2729, 0.2848, 43.0600])), - (('2.5PB', 7.0, 6.0), np.array([0.2538, 0.2677, 43.0600])), - (('2.5PB', 7.0, 8.0), np.array([0.2352, 0.2498, 43.0600])), - (('2.5PB', 7.0, 10.0), np.array([0.2162, 0.2309, 43.0600])), - (('2.5PB', 7.0, 12.0), np.array([0.1990, 0.2130, 43.0600])), - (('2.5PB', 7.0, 14.0), np.array([0.1830, 0.1950, 43.0600])), - (('2.5PB', 7.0, 16.0), np.array([0.1680, 0.1790, 43.0600])), - (('2.5PB', 7.0, 18.0), np.array([0.1560, 0.1650, 43.0600])), - (('5PB', 7.0, 2.0), np.array([0.2952, 0.3011, 43.0600])), - (('5PB', 7.0, 4.0), np.array([0.2773, 0.2828, 43.0600])), - (('5PB', 7.0, 6.0), np.array([0.2596, 0.2643, 43.0600])), - (('5PB', 7.0, 8.0), np.array([0.2427, 0.2458, 43.0600])), - (('5PB', 7.0, 10.0), np.array([0.2254, 0.2267, 43.0600])), - (('5PB', 7.0, 12.0), np.array([0.2080, 0.2080, 43.0600])), - (('5PB', 7.0, 14.0), np.array([0.1940, 0.1910, 43.0600])), - (('5PB', 7.0, 16.0), np.array([0.1810, 0.1750, 43.0600])), - (('5PB', 7.0, 18.0), np.array([0.1700, 0.1610, 43.0600])), - (('7.5PB', 7.0, 2.0), np.array([0.2982, 0.3003, 43.0600])), - (('7.5PB', 7.0, 4.0), np.array([0.2833, 0.2809, 43.0600])), - (('7.5PB', 7.0, 6.0), np.array([0.2687, 0.2612, 43.0600])), - (('7.5PB', 7.0, 8.0), np.array([0.2546, 0.2418, 43.0600])), - (('7.5PB', 7.0, 10.0), np.array([0.2410, 0.2224, 43.0600])), - (('7.5PB', 7.0, 12.0), np.array([0.2280, 0.2040, 43.0600])), - (('7.5PB', 7.0, 14.0), np.array([0.2170, 0.1890, 43.0600])), - (('7.5PB', 7.0, 16.0), np.array([0.2070, 0.1730, 43.0600])), - (('7.5PB', 7.0, 18.0), np.array([0.1980, 0.1600, 43.0600])), - (('7.5PB', 7.0, 20.0), np.array([0.1880, 0.1460, 43.0600])), - (('10PB', 7.0, 2.0), np.array([0.3005, 0.3000, 43.0600])), - (('10PB', 7.0, 4.0), np.array([0.2886, 0.2801, 43.0600])), - (('10PB', 7.0, 6.0), np.array([0.2776, 0.2612, 43.0600])), - (('10PB', 7.0, 8.0), np.array([0.2670, 0.2425, 43.0600])), - (('10PB', 7.0, 10.0), np.array([0.2563, 0.2240, 43.0600])), - (('10PB', 7.0, 12.0), np.array([0.2465, 0.2058, 43.0600])), - (('10PB', 7.0, 14.0), np.array([0.2390, 0.1910, 43.0600])), - (('10PB', 7.0, 16.0), np.array([0.2320, 0.1780, 43.0600])), - (('10PB', 7.0, 18.0), np.array([0.2250, 0.1660, 43.0600])), - (('10PB', 7.0, 20.0), np.array([0.2190, 0.1520, 43.0600])), - (('10PB', 7.0, 22.0), np.array([0.2120, 0.1400, 43.0600])), - (('2.5P', 7.0, 2.0), np.array([0.3031, 0.3000, 43.0600])), - (('2.5P', 7.0, 4.0), np.array([0.2950, 0.2810, 43.0600])), - (('2.5P', 7.0, 6.0), np.array([0.2873, 0.2633, 43.0600])), - (('2.5P', 7.0, 8.0), np.array([0.2799, 0.2459, 43.0600])), - (('2.5P', 7.0, 10.0), np.array([0.2729, 0.2289, 43.0600])), - (('2.5P', 7.0, 12.0), np.array([0.2664, 0.2127, 43.0600])), - (('2.5P', 7.0, 14.0), np.array([0.2610, 0.1980, 43.0600])), - (('2.5P', 7.0, 16.0), np.array([0.2560, 0.1850, 43.0600])), - (('2.5P', 7.0, 18.0), np.array([0.2520, 0.1760, 43.0600])), - (('2.5P', 7.0, 20.0), np.array([0.2480, 0.1620, 43.0600])), - (('2.5P', 7.0, 22.0), np.array([0.2440, 0.1500, 43.0600])), - (('2.5P', 7.0, 24.0), np.array([0.2400, 0.1400, 43.0600])), - (('5P', 7.0, 2.0), np.array([0.3059, 0.3010, 43.0600])), - (('5P', 7.0, 4.0), np.array([0.3009, 0.2831, 43.0600])), - (('5P', 7.0, 6.0), np.array([0.2961, 0.2663, 43.0600])), - (('5P', 7.0, 8.0), np.array([0.2918, 0.2504, 43.0600])), - (('5P', 7.0, 10.0), np.array([0.2872, 0.2343, 43.0600])), - (('5P', 7.0, 12.0), np.array([0.2833, 0.2197, 43.0600])), - (('5P', 7.0, 14.0), np.array([0.2801, 0.2068, 43.0600])), - (('5P', 7.0, 16.0), np.array([0.2770, 0.1940, 43.0600])), - (('5P', 7.0, 18.0), np.array([0.2740, 0.1820, 43.0600])), - (('5P', 7.0, 20.0), np.array([0.2710, 0.1700, 43.0600])), - (('5P', 7.0, 22.0), np.array([0.2680, 0.1580, 43.0600])), - (('5P', 7.0, 24.0), np.array([0.2650, 0.1480, 43.0600])), - (('5P', 7.0, 26.0), np.array([0.2620, 0.1370, 43.0600])), - (('5P', 7.0, 28.0), np.array([0.2600, 0.1280, 43.0600])), - (('7.5P', 7.0, 2.0), np.array([0.3109, 0.3037, 43.0600])), - (('7.5P', 7.0, 4.0), np.array([0.3111, 0.2880, 43.0600])), - (('7.5P', 7.0, 6.0), np.array([0.3111, 0.2730, 43.0600])), - (('7.5P', 7.0, 8.0), np.array([0.3109, 0.2584, 43.0600])), - (('7.5P', 7.0, 10.0), np.array([0.3108, 0.2442, 43.0600])), - (('7.5P', 7.0, 12.0), np.array([0.3104, 0.2320, 43.0600])), - (('7.5P', 7.0, 14.0), np.array([0.3101, 0.2192, 43.0600])), - (('7.5P', 7.0, 16.0), np.array([0.3099, 0.2074, 43.0600])), - (('7.5P', 7.0, 18.0), np.array([0.3093, 0.1962, 43.0600])), - (('7.5P', 7.0, 20.0), np.array([0.3080, 0.1850, 43.0600])), - (('7.5P', 7.0, 22.0), np.array([0.3080, 0.1740, 43.0600])), - (('7.5P', 7.0, 24.0), np.array([0.3070, 0.1640, 43.0600])), - (('7.5P', 7.0, 26.0), np.array([0.3060, 0.1540, 43.0600])), - (('7.5P', 7.0, 28.0), np.array([0.3050, 0.1450, 43.0600])), - (('7.5P', 7.0, 30.0), np.array([0.3040, 0.1360, 43.0600])), - (('10P', 7.0, 2.0), np.array([0.3138, 0.3054, 43.0600])), - (('10P', 7.0, 4.0), np.array([0.3181, 0.2920, 43.0600])), - (('10P', 7.0, 6.0), np.array([0.3221, 0.2786, 43.0600])), - (('10P', 7.0, 8.0), np.array([0.3256, 0.2654, 43.0600])), - (('10P', 7.0, 10.0), np.array([0.3288, 0.2531, 43.0600])), - (('10P', 7.0, 12.0), np.array([0.3314, 0.2423, 43.0600])), - (('10P', 7.0, 14.0), np.array([0.3341, 0.2308, 43.0600])), - (('10P', 7.0, 16.0), np.array([0.3368, 0.2192, 43.0600])), - (('10P', 7.0, 18.0), np.array([0.3391, 0.2088, 43.0600])), - (('10P', 7.0, 20.0), np.array([0.3410, 0.1988, 43.0600])), - (('10P', 7.0, 22.0), np.array([0.3430, 0.1883, 43.0600])), - (('10P', 7.0, 24.0), np.array([0.3440, 0.1790, 43.0600])), - (('10P', 7.0, 26.0), np.array([0.3440, 0.1700, 43.0600])), - (('10P', 7.0, 28.0), np.array([0.3450, 0.1620, 43.0600])), - (('10P', 7.0, 30.0), np.array([0.3460, 0.1540, 43.0600])), - (('2.5RP', 7.0, 2.0), np.array([0.3170, 0.3076, 43.0600])), - (('2.5RP', 7.0, 4.0), np.array([0.3254, 0.2971, 43.0600])), - (('2.5RP', 7.0, 6.0), np.array([0.3338, 0.2854, 43.0600])), - (('2.5RP', 7.0, 8.0), np.array([0.3417, 0.2745, 43.0600])), - (('2.5RP', 7.0, 10.0), np.array([0.3487, 0.2648, 43.0600])), - (('2.5RP', 7.0, 12.0), np.array([0.3555, 0.2545, 43.0600])), - (('2.5RP', 7.0, 14.0), np.array([0.3620, 0.2448, 43.0600])), - (('2.5RP', 7.0, 16.0), np.array([0.3688, 0.2342, 43.0600])), - (('2.5RP', 7.0, 18.0), np.array([0.3751, 0.2241, 43.0600])), - (('2.5RP', 7.0, 20.0), np.array([0.3811, 0.2143, 43.0600])), - (('2.5RP', 7.0, 22.0), np.array([0.3850, 0.2060, 43.0600])), - (('2.5RP', 7.0, 24.0), np.array([0.3890, 0.2000, 43.0600])), - (('2.5RP', 7.0, 26.0), np.array([0.3940, 0.1920, 43.0600])), - (('2.5RP', 7.0, 28.0), np.array([0.3980, 0.1840, 43.0600])), - (('2.5RP', 7.0, 30.0), np.array([0.4020, 0.1780, 43.0600])), - (('5RP', 7.0, 2.0), np.array([0.3206, 0.3104, 43.0600])), - (('5RP', 7.0, 4.0), np.array([0.3332, 0.3032, 43.0600])), - (('5RP', 7.0, 6.0), np.array([0.3470, 0.2949, 43.0600])), - (('5RP', 7.0, 8.0), np.array([0.3603, 0.2869, 43.0600])), - (('5RP', 7.0, 10.0), np.array([0.3713, 0.2798, 43.0600])), - (('5RP', 7.0, 12.0), np.array([0.3841, 0.2710, 43.0600])), - (('5RP', 7.0, 14.0), np.array([0.3958, 0.2628, 43.0600])), - (('5RP', 7.0, 16.0), np.array([0.4076, 0.2540, 43.0600])), - (('5RP', 7.0, 18.0), np.array([0.4186, 0.2459, 43.0600])), - (('5RP', 7.0, 20.0), np.array([0.4260, 0.2390, 43.0600])), - (('5RP', 7.0, 22.0), np.array([0.4360, 0.2320, 43.0600])), - (('5RP', 7.0, 24.0), np.array([0.4430, 0.2260, 43.0600])), - (('5RP', 7.0, 26.0), np.array([0.4520, 0.2170, 43.0600])), - (('5RP', 7.0, 28.0), np.array([0.4600, 0.2110, 43.0600])), - (('7.5RP', 7.0, 2.0), np.array([0.3232, 0.3125, 43.0600])), - (('7.5RP', 7.0, 4.0), np.array([0.3389, 0.3079, 43.0600])), - (('7.5RP', 7.0, 6.0), np.array([0.3562, 0.3022, 43.0600])), - (('7.5RP', 7.0, 8.0), np.array([0.3722, 0.2963, 43.0600])), - (('7.5RP', 7.0, 10.0), np.array([0.3871, 0.2906, 43.0600])), - (('7.5RP', 7.0, 12.0), np.array([0.4040, 0.2834, 43.0600])), - (('7.5RP', 7.0, 14.0), np.array([0.4195, 0.2762, 43.0600])), - (('7.5RP', 7.0, 16.0), np.array([0.4346, 0.2689, 43.0600])), - (('7.5RP', 7.0, 18.0), np.array([0.4480, 0.2610, 43.0600])), - (('7.5RP', 7.0, 20.0), np.array([0.4600, 0.2550, 43.0600])), - (('7.5RP', 7.0, 22.0), np.array([0.4710, 0.2490, 43.0600])), - (('7.5RP', 7.0, 24.0), np.array([0.4810, 0.2430, 43.0600])), - (('7.5RP', 7.0, 26.0), np.array([0.4910, 0.2360, 43.0600])), - (('10RP', 7.0, 2.0), np.array([0.3258, 0.3148, 43.0600])), - (('10RP', 7.0, 4.0), np.array([0.3446, 0.3125, 43.0600])), - (('10RP', 7.0, 6.0), np.array([0.3648, 0.3098, 43.0600])), - (('10RP', 7.0, 8.0), np.array([0.3851, 0.3067, 43.0600])), - (('10RP', 7.0, 10.0), np.array([0.4040, 0.3030, 43.0600])), - (('10RP', 7.0, 12.0), np.array([0.4260, 0.2980, 43.0600])), - (('10RP', 7.0, 14.0), np.array([0.4456, 0.2931, 43.0600])), - (('10RP', 7.0, 16.0), np.array([0.4648, 0.2878, 43.0600])), - (('10RP', 7.0, 18.0), np.array([0.4830, 0.2830, 43.0600])), - (('10RP', 7.0, 20.0), np.array([0.4980, 0.2790, 43.0600])), - (('10RP', 7.0, 22.0), np.array([0.5110, 0.2740, 43.0600])), - (('10RP', 7.0, 24.0), np.array([0.5260, 0.2670, 43.0600])), - (('2.5R', 7.0, 2.0), np.array([0.3284, 0.3170, 43.0600])), - (('2.5R', 7.0, 4.0), np.array([0.3499, 0.3171, 43.0600])), - (('2.5R', 7.0, 6.0), np.array([0.3728, 0.3170, 43.0600])), - (('2.5R', 7.0, 8.0), np.array([0.3961, 0.3160, 43.0600])), - (('2.5R', 7.0, 10.0), np.array([0.4183, 0.3144, 43.0600])), - (('2.5R', 7.0, 12.0), np.array([0.4435, 0.3119, 43.0600])), - (('2.5R', 7.0, 14.0), np.array([0.4660, 0.3082, 43.0600])), - (('2.5R', 7.0, 16.0), np.array([0.4885, 0.3039, 43.0600])), - (('2.5R', 7.0, 18.0), np.array([0.5070, 0.3000, 43.0600])), - (('2.5R', 7.0, 20.0), np.array([0.5230, 0.2960, 43.0600])), - (('2.5R', 7.0, 22.0), np.array([0.5360, 0.2910, 43.0600])), - (('2.5R', 7.0, 24.0), np.array([0.5530, 0.2870, 43.0600])), - (('5R', 7.0, 2.0), np.array([0.3306, 0.3190, 43.0600])), - (('5R', 7.0, 4.0), np.array([0.3552, 0.3222, 43.0600])), - (('5R', 7.0, 6.0), np.array([0.3805, 0.3244, 43.0600])), - (('5R', 7.0, 8.0), np.array([0.4067, 0.3256, 43.0600])), - (('5R', 7.0, 10.0), np.array([0.4320, 0.3260, 43.0600])), - (('5R', 7.0, 12.0), np.array([0.4595, 0.3252, 43.0600])), - (('5R', 7.0, 14.0), np.array([0.4848, 0.3238, 43.0600])), - (('5R', 7.0, 16.0), np.array([0.5100, 0.3210, 43.0600])), - (('5R', 7.0, 18.0), np.array([0.5300, 0.3180, 43.0600])), - (('5R', 7.0, 20.0), np.array([0.5470, 0.3150, 43.0600])), - (('5R', 7.0, 22.0), np.array([0.5630, 0.3110, 43.0600])), - (('5R', 7.0, 24.0), np.array([0.5800, 0.3060, 43.0600])), - (('7.5R', 7.0, 2.0), np.array([0.3335, 0.3220, 43.0600])), - (('7.5R', 7.0, 4.0), np.array([0.3611, 0.3282, 43.0600])), - (('7.5R', 7.0, 6.0), np.array([0.3888, 0.3336, 43.0600])), - (('7.5R', 7.0, 8.0), np.array([0.4196, 0.3382, 43.0600])), - (('7.5R', 7.0, 10.0), np.array([0.4470, 0.3413, 43.0600])), - (('7.5R', 7.0, 12.0), np.array([0.4777, 0.3435, 43.0600])), - (('7.5R', 7.0, 14.0), np.array([0.5059, 0.3450, 43.0600])), - (('7.5R', 7.0, 16.0), np.array([0.5341, 0.3452, 43.0600])), - (('7.5R', 7.0, 18.0), np.array([0.5540, 0.3430, 43.0600])), - (('7.5R', 7.0, 20.0), np.array([0.5730, 0.3410, 43.0600])), - (('7.5R', 7.0, 22.0), np.array([0.5920, 0.3380, 43.0600])), - (('7.5R', 7.0, 24.0), np.array([0.6070, 0.3350, 43.0600])), - (('7.5R', 7.0, 26.0), np.array([0.6220, 0.3320, 43.0600])), - (('10R', 7.0, 2.0), np.array([0.3360, 0.3253, 43.0600])), - (('10R', 7.0, 4.0), np.array([0.3671, 0.3360, 43.0600])), - (('10R', 7.0, 6.0), np.array([0.3984, 0.3452, 43.0600])), - (('10R', 7.0, 8.0), np.array([0.4308, 0.3533, 43.0600])), - (('10R', 7.0, 10.0), np.array([0.4600, 0.3596, 43.0600])), - (('10R', 7.0, 12.0), np.array([0.4930, 0.3659, 43.0600])), - (('10R', 7.0, 14.0), np.array([0.5234, 0.3700, 43.0600])), - (('10R', 7.0, 16.0), np.array([0.5519, 0.3729, 43.0600])), - (('10R', 7.0, 18.0), np.array([0.5710, 0.3730, 43.0600])), - (('10R', 7.0, 20.0), np.array([0.5920, 0.3740, 43.0600])), - (('10R', 7.0, 22.0), np.array([0.6140, 0.3740, 43.0600])), - (('10R', 7.0, 24.0), np.array([0.6300, 0.3720, 43.0600])), - (('10R', 7.0, 26.0), np.array([0.6470, 0.3690, 43.0600])), - (('2.5YR', 7.0, 2.0), np.array([0.3392, 0.3298, 43.0600])), - (('2.5YR', 7.0, 4.0), np.array([0.3715, 0.3439, 43.0600])), - (('2.5YR', 7.0, 6.0), np.array([0.4053, 0.3570, 43.0600])), - (('2.5YR', 7.0, 8.0), np.array([0.4371, 0.3679, 43.0600])), - (('2.5YR', 7.0, 10.0), np.array([0.4671, 0.3768, 43.0600])), - (('2.5YR', 7.0, 12.0), np.array([0.5001, 0.3861, 43.0600])), - (('2.5YR', 7.0, 14.0), np.array([0.5297, 0.3938, 43.0600])), - (('2.5YR', 7.0, 16.0), np.array([0.5522, 0.3989, 43.0600])), - (('2.5YR', 7.0, 18.0), np.array([0.5695, 0.4024, 43.0600])), - (('2.5YR', 7.0, 20.0), np.array([0.5824, 0.4046, 43.0600])), - (('2.5YR', 7.0, 22.0), np.array([0.5940, 0.4070, 43.0600])), - (('2.5YR', 7.0, 24.0), np.array([0.6020, 0.4090, 43.0600])), - (('5YR', 7.0, 2.0), np.array([0.3421, 0.3349, 43.0600])), - (('5YR', 7.0, 4.0), np.array([0.3750, 0.3530, 43.0600])), - (('5YR', 7.0, 6.0), np.array([0.4091, 0.3701, 43.0600])), - (('5YR', 7.0, 8.0), np.array([0.4402, 0.3842, 43.0600])), - (('5YR', 7.0, 10.0), np.array([0.4711, 0.3972, 43.0600])), - (('5YR', 7.0, 12.0), np.array([0.5007, 0.4081, 43.0600])), - (('5YR', 7.0, 14.0), np.array([0.5252, 0.4168, 43.0600])), - (('5YR', 7.0, 16.0), np.array([0.5437, 0.4228, 43.0600])), - (('5YR', 7.0, 18.0), np.array([0.5564, 0.4267, 43.0600])), - (('5YR', 7.0, 20.0), np.array([0.5657, 0.4298, 43.0600])), - (('5YR', 7.0, 22.0), np.array([0.5750, 0.4340, 43.0600])), - (('7.5YR', 7.0, 2.0), np.array([0.3437, 0.3397, 43.0600])), - (('7.5YR', 7.0, 4.0), np.array([0.3772, 0.3613, 43.0600])), - (('7.5YR', 7.0, 6.0), np.array([0.4107, 0.3820, 43.0600])), - (('7.5YR', 7.0, 8.0), np.array([0.4415, 0.3996, 43.0600])), - (('7.5YR', 7.0, 10.0), np.array([0.4704, 0.4151, 43.0600])), - (('7.5YR', 7.0, 12.0), np.array([0.4970, 0.4282, 43.0600])), - (('7.5YR', 7.0, 14.0), np.array([0.5174, 0.4381, 43.0600])), - (('7.5YR', 7.0, 16.0), np.array([0.5319, 0.4449, 43.0600])), - (('7.5YR', 7.0, 18.0), np.array([0.5417, 0.4492, 43.0600])), - (('7.5YR', 7.0, 20.0), np.array([0.5480, 0.4530, 43.0600])), - (('7.5YR', 7.0, 22.0), np.array([0.5560, 0.4560, 43.0600])), - (('10YR', 7.0, 2.0), np.array([0.3443, 0.3454, 43.0600])), - (('10YR', 7.0, 4.0), np.array([0.3778, 0.3719, 43.0600])), - (('10YR', 7.0, 6.0), np.array([0.4102, 0.3960, 43.0600])), - (('10YR', 7.0, 8.0), np.array([0.4399, 0.4164, 43.0600])), - (('10YR', 7.0, 10.0), np.array([0.4667, 0.4335, 43.0600])), - (('10YR', 7.0, 12.0), np.array([0.4900, 0.4480, 43.0600])), - (('10YR', 7.0, 14.0), np.array([0.5074, 0.4581, 43.0600])), - (('10YR', 7.0, 16.0), np.array([0.5188, 0.4650, 43.0600])), - (('10YR', 7.0, 18.0), np.array([0.5276, 0.4700, 43.0600])), - (('10YR', 7.0, 20.0), np.array([0.5320, 0.4740, 43.0600])), - (('2.5Y', 7.0, 2.0), np.array([0.3436, 0.3507, 43.0600])), - (('2.5Y', 7.0, 4.0), np.array([0.3761, 0.3800, 43.0600])), - (('2.5Y', 7.0, 6.0), np.array([0.4073, 0.4073, 43.0600])), - (('2.5Y', 7.0, 8.0), np.array([0.4353, 0.4312, 43.0600])), - (('2.5Y', 7.0, 10.0), np.array([0.4606, 0.4516, 43.0600])), - (('2.5Y', 7.0, 12.0), np.array([0.4806, 0.4666, 43.0600])), - (('2.5Y', 7.0, 14.0), np.array([0.4950, 0.4773, 43.0600])), - (('2.5Y', 7.0, 16.0), np.array([0.5049, 0.4843, 43.0600])), - (('2.5Y', 7.0, 18.0), np.array([0.5110, 0.4880, 43.0600])), - (('2.5Y', 7.0, 20.0), np.array([0.5160, 0.4920, 43.0600])), - (('5Y', 7.0, 2.0), np.array([0.3419, 0.3540, 43.0600])), - (('5Y', 7.0, 4.0), np.array([0.3718, 0.3885, 43.0600])), - (('5Y', 7.0, 6.0), np.array([0.4009, 0.4198, 43.0600])), - (('5Y', 7.0, 8.0), np.array([0.4271, 0.4462, 43.0600])), - (('5Y', 7.0, 10.0), np.array([0.4509, 0.4696, 43.0600])), - (('5Y', 7.0, 12.0), np.array([0.4677, 0.4857, 43.0600])), - (('5Y', 7.0, 14.0), np.array([0.4791, 0.4965, 43.0600])), - (('5Y', 7.0, 16.0), np.array([0.4875, 0.5047, 43.0600])), - (('5Y', 7.0, 18.0), np.array([0.4930, 0.5090, 43.0600])), - (('5Y', 7.0, 20.0), np.array([0.4980, 0.5130, 43.0600])), - (('7.5Y', 7.0, 2.0), np.array([0.3396, 0.3558, 43.0600])), - (('7.5Y', 7.0, 4.0), np.array([0.3677, 0.3925, 43.0600])), - (('7.5Y', 7.0, 6.0), np.array([0.3943, 0.4264, 43.0600])), - (('7.5Y', 7.0, 8.0), np.array([0.4184, 0.4568, 43.0600])), - (('7.5Y', 7.0, 10.0), np.array([0.4400, 0.4830, 43.0600])), - (('7.5Y', 7.0, 12.0), np.array([0.4547, 0.5005, 43.0600])), - (('7.5Y', 7.0, 14.0), np.array([0.4652, 0.5128, 43.0600])), - (('7.5Y', 7.0, 16.0), np.array([0.4728, 0.5215, 43.0600])), - (('7.5Y', 7.0, 18.0), np.array([0.4770, 0.5270, 43.0600])), - (('10Y', 8.0, 2.0), np.array([0.3359, 0.3552, 59.1000])), - (('10Y', 8.0, 4.0), np.array([0.3581, 0.3883, 59.1000])), - (('10Y', 8.0, 6.0), np.array([0.3803, 0.4216, 59.1000])), - (('10Y', 8.0, 8.0), np.array([0.4008, 0.4520, 59.1000])), - (('10Y', 8.0, 10.0), np.array([0.4190, 0.4791, 59.1000])), - (('10Y', 8.0, 12.0), np.array([0.4341, 0.5020, 59.1000])), - (('10Y', 8.0, 14.0), np.array([0.4450, 0.5181, 59.1000])), - (('10Y', 8.0, 16.0), np.array([0.4525, 0.5295, 59.1000])), - (('10Y', 8.0, 18.0), np.array([0.4570, 0.5366, 59.1000])), - (('10Y', 8.0, 20.0), np.array([0.4610, 0.5420, 59.1000])), - (('2.5GY', 8.0, 2.0), np.array([0.3327, 0.3555, 59.1000])), - (('2.5GY', 8.0, 4.0), np.array([0.3504, 0.3887, 59.1000])), - (('2.5GY', 8.0, 6.0), np.array([0.3690, 0.4230, 59.1000])), - (('2.5GY', 8.0, 8.0), np.array([0.3858, 0.4550, 59.1000])), - (('2.5GY', 8.0, 10.0), np.array([0.4021, 0.4869, 59.1000])), - (('2.5GY', 8.0, 12.0), np.array([0.4154, 0.5133, 59.1000])), - (('2.5GY', 8.0, 14.0), np.array([0.4261, 0.5344, 59.1000])), - (('2.5GY', 8.0, 16.0), np.array([0.4327, 0.5475, 59.1000])), - (('2.5GY', 8.0, 18.0), np.array([0.4371, 0.5557, 59.1000])), - (('2.5GY', 8.0, 20.0), np.array([0.4400, 0.5620, 59.1000])), - (('2.5GY', 8.0, 22.0), np.array([0.4430, 0.5680, 59.1000])), - (('5GY', 8.0, 2.0), np.array([0.3284, 0.3542, 59.1000])), - (('5GY', 8.0, 4.0), np.array([0.3433, 0.3872, 59.1000])), - (('5GY', 8.0, 6.0), np.array([0.3573, 0.4214, 59.1000])), - (('5GY', 8.0, 8.0), np.array([0.3696, 0.4542, 59.1000])), - (('5GY', 8.0, 10.0), np.array([0.3816, 0.4879, 59.1000])), - (('5GY', 8.0, 12.0), np.array([0.3924, 0.5199, 59.1000])), - (('5GY', 8.0, 14.0), np.array([0.4011, 0.5468, 59.1000])), - (('5GY', 8.0, 16.0), np.array([0.4061, 0.5641, 59.1000])), - (('5GY', 8.0, 18.0), np.array([0.4104, 0.5785, 59.1000])), - (('5GY', 8.0, 20.0), np.array([0.4127, 0.5855, 59.1000])), - (('5GY', 8.0, 22.0), np.array([0.4150, 0.5950, 59.1000])), - (('7.5GY', 8.0, 2.0), np.array([0.3194, 0.3502, 59.1000])), - (('7.5GY', 8.0, 4.0), np.array([0.3266, 0.3809, 59.1000])), - (('7.5GY', 8.0, 6.0), np.array([0.3339, 0.4129, 59.1000])), - (('7.5GY', 8.0, 8.0), np.array([0.3408, 0.4452, 59.1000])), - (('7.5GY', 8.0, 10.0), np.array([0.3463, 0.4791, 59.1000])), - (('7.5GY', 8.0, 12.0), np.array([0.3511, 0.5144, 59.1000])), - (('7.5GY', 8.0, 14.0), np.array([0.3546, 0.5490, 59.1000])), - (('7.5GY', 8.0, 16.0), np.array([0.3569, 0.5798, 59.1000])), - (('7.5GY', 8.0, 18.0), np.array([0.3585, 0.6063, 59.1000])), - (('7.5GY', 8.0, 20.0), np.array([0.3592, 0.6235, 59.1000])), - (('7.5GY', 8.0, 22.0), np.array([0.3600, 0.6450, 59.1000])), - (('7.5GY', 8.0, 24.0), np.array([0.3600, 0.6600, 59.1000])), - (('7.5GY', 8.0, 26.0), np.array([0.3610, 0.6790, 59.1000])), - (('7.5GY', 8.0, 28.0), np.array([0.3610, 0.6960, 59.1000])), - (('10GY', 8.0, 2.0), np.array([0.3121, 0.3459, 59.1000])), - (('10GY', 8.0, 4.0), np.array([0.3140, 0.3727, 59.1000])), - (('10GY', 8.0, 6.0), np.array([0.3150, 0.4014, 59.1000])), - (('10GY', 8.0, 8.0), np.array([0.3149, 0.4284, 59.1000])), - (('10GY', 8.0, 10.0), np.array([0.3140, 0.4601, 59.1000])), - (('10GY', 8.0, 12.0), np.array([0.3124, 0.4926, 59.1000])), - (('10GY', 8.0, 14.0), np.array([0.3091, 0.5247, 59.1000])), - (('10GY', 8.0, 16.0), np.array([0.3043, 0.5578, 59.1000])), - (('10GY', 8.0, 18.0), np.array([0.2987, 0.5919, 59.1000])), - (('10GY', 8.0, 20.0), np.array([0.2918, 0.6255, 59.1000])), - (('10GY', 8.0, 22.0), np.array([0.2846, 0.6564, 59.1000])), - (('10GY', 8.0, 24.0), np.array([0.2781, 0.6840, 59.1000])), - (('10GY', 8.0, 26.0), np.array([0.2710, 0.7020, 59.1000])), - (('10GY', 8.0, 28.0), np.array([0.2620, 0.7260, 59.1000])), - (('10GY', 8.0, 30.0), np.array([0.2520, 0.7520, 59.1000])), - (('10GY', 8.0, 32.0), np.array([0.2400, 0.7840, 59.1000])), - (('2.5G', 8.0, 2.0), np.array([0.3053, 0.3404, 59.1000])), - (('2.5G', 8.0, 4.0), np.array([0.3009, 0.3614, 59.1000])), - (('2.5G', 8.0, 6.0), np.array([0.2952, 0.3851, 59.1000])), - (('2.5G', 8.0, 8.0), np.array([0.2896, 0.4065, 59.1000])), - (('2.5G', 8.0, 10.0), np.array([0.2829, 0.4301, 59.1000])), - (('2.5G', 8.0, 12.0), np.array([0.2743, 0.4554, 59.1000])), - (('2.5G', 8.0, 14.0), np.array([0.2661, 0.4780, 59.1000])), - (('2.5G', 8.0, 16.0), np.array([0.2563, 0.5045, 59.1000])), - (('2.5G', 8.0, 18.0), np.array([0.2451, 0.5309, 59.1000])), - (('2.5G', 8.0, 20.0), np.array([0.2339, 0.5561, 59.1000])), - (('2.5G', 8.0, 22.0), np.array([0.2221, 0.5799, 59.1000])), - (('2.5G', 8.0, 24.0), np.array([0.2091, 0.6033, 59.1000])), - (('2.5G', 8.0, 26.0), np.array([0.1960, 0.6220, 59.1000])), - (('2.5G', 8.0, 28.0), np.array([0.1800, 0.6470, 59.1000])), - (('2.5G', 8.0, 30.0), np.array([0.1630, 0.6700, 59.1000])), - (('2.5G', 8.0, 32.0), np.array([0.1440, 0.6920, 59.1000])), - (('5G', 8.0, 2.0), np.array([0.3009, 0.3359, 59.1000])), - (('5G', 8.0, 4.0), np.array([0.2924, 0.3523, 59.1000])), - (('5G', 8.0, 6.0), np.array([0.2822, 0.3702, 59.1000])), - (('5G', 8.0, 8.0), np.array([0.2723, 0.3865, 59.1000])), - (('5G', 8.0, 10.0), np.array([0.2613, 0.4026, 59.1000])), - (('5G', 8.0, 12.0), np.array([0.2489, 0.4191, 59.1000])), - (('5G', 8.0, 14.0), np.array([0.2368, 0.4348, 59.1000])), - (('5G', 8.0, 16.0), np.array([0.2240, 0.4500, 59.1000])), - (('5G', 8.0, 18.0), np.array([0.2103, 0.4652, 59.1000])), - (('5G', 8.0, 20.0), np.array([0.1956, 0.4806, 59.1000])), - (('5G', 8.0, 22.0), np.array([0.1821, 0.4940, 59.1000])), - (('5G', 8.0, 24.0), np.array([0.1680, 0.5070, 59.1000])), - (('5G', 8.0, 26.0), np.array([0.1510, 0.5210, 59.1000])), - (('5G', 8.0, 28.0), np.array([0.1340, 0.5370, 59.1000])), - (('5G', 8.0, 30.0), np.array([0.1150, 0.5530, 59.1000])), - (('5G', 8.0, 32.0), np.array([0.0960, 0.5640, 59.1000])), - (('7.5G', 8.0, 2.0), np.array([0.2981, 0.3326, 59.1000])), - (('7.5G', 8.0, 4.0), np.array([0.2874, 0.3464, 59.1000])), - (('7.5G', 8.0, 6.0), np.array([0.2754, 0.3608, 59.1000])), - (('7.5G', 8.0, 8.0), np.array([0.2639, 0.3733, 59.1000])), - (('7.5G', 8.0, 10.0), np.array([0.2515, 0.3867, 59.1000])), - (('7.5G', 8.0, 12.0), np.array([0.2380, 0.4002, 59.1000])), - (('7.5G', 8.0, 14.0), np.array([0.2254, 0.4125, 59.1000])), - (('7.5G', 8.0, 16.0), np.array([0.2120, 0.4252, 59.1000])), - (('7.5G', 8.0, 18.0), np.array([0.1980, 0.4372, 59.1000])), - (('7.5G', 8.0, 20.0), np.array([0.1845, 0.4492, 59.1000])), - (('7.5G', 8.0, 22.0), np.array([0.1700, 0.4590, 59.1000])), - (('7.5G', 8.0, 24.0), np.array([0.1550, 0.4710, 59.1000])), - (('7.5G', 8.0, 26.0), np.array([0.1390, 0.4830, 59.1000])), - (('7.5G', 8.0, 28.0), np.array([0.1230, 0.4940, 59.1000])), - (('7.5G', 8.0, 30.0), np.array([0.1050, 0.5070, 59.1000])), - (('10G', 8.0, 2.0), np.array([0.2957, 0.3293, 59.1000])), - (('10G', 8.0, 4.0), np.array([0.2828, 0.3403, 59.1000])), - (('10G', 8.0, 6.0), np.array([0.2693, 0.3512, 59.1000])), - (('10G', 8.0, 8.0), np.array([0.2564, 0.3611, 59.1000])), - (('10G', 8.0, 10.0), np.array([0.2430, 0.3710, 59.1000])), - (('10G', 8.0, 12.0), np.array([0.2282, 0.3811, 59.1000])), - (('10G', 8.0, 14.0), np.array([0.2148, 0.3903, 59.1000])), - (('10G', 8.0, 16.0), np.array([0.2012, 0.3992, 59.1000])), - (('10G', 8.0, 18.0), np.array([0.1866, 0.4086, 59.1000])), - (('10G', 8.0, 20.0), np.array([0.1734, 0.4164, 59.1000])), - (('10G', 8.0, 22.0), np.array([0.1590, 0.4240, 59.1000])), - (('10G', 8.0, 24.0), np.array([0.1420, 0.4330, 59.1000])), - (('10G', 8.0, 26.0), np.array([0.1270, 0.4410, 59.1000])), - (('10G', 8.0, 28.0), np.array([0.1120, 0.4480, 59.1000])), - (('2.5BG', 8.0, 2.0), np.array([0.2940, 0.3268, 59.1000])), - (('2.5BG', 8.0, 4.0), np.array([0.2791, 0.3351, 59.1000])), - (('2.5BG', 8.0, 6.0), np.array([0.2647, 0.3429, 59.1000])), - (('2.5BG', 8.0, 8.0), np.array([0.2500, 0.3500, 59.1000])), - (('2.5BG', 8.0, 10.0), np.array([0.2352, 0.3566, 59.1000])), - (('2.5BG', 8.0, 12.0), np.array([0.2196, 0.3630, 59.1000])), - (('2.5BG', 8.0, 14.0), np.array([0.2057, 0.3681, 59.1000])), - (('2.5BG', 8.0, 16.0), np.array([0.1915, 0.3732, 59.1000])), - (('2.5BG', 8.0, 18.0), np.array([0.1759, 0.3782, 59.1000])), - (('2.5BG', 8.0, 20.0), np.array([0.1620, 0.3830, 59.1000])), - (('2.5BG', 8.0, 22.0), np.array([0.1480, 0.3870, 59.1000])), - (('2.5BG', 8.0, 24.0), np.array([0.1320, 0.3920, 59.1000])), - (('2.5BG', 8.0, 26.0), np.array([0.1160, 0.3960, 59.1000])), - (('5BG', 8.0, 2.0), np.array([0.2919, 0.3228, 59.1000])), - (('5BG', 8.0, 4.0), np.array([0.2752, 0.3278, 59.1000])), - (('5BG', 8.0, 6.0), np.array([0.2588, 0.3318, 59.1000])), - (('5BG', 8.0, 8.0), np.array([0.2419, 0.3352, 59.1000])), - (('5BG', 8.0, 10.0), np.array([0.2264, 0.3383, 59.1000])), - (('5BG', 8.0, 12.0), np.array([0.2101, 0.3412, 59.1000])), - (('5BG', 8.0, 14.0), np.array([0.1958, 0.3432, 59.1000])), - (('5BG', 8.0, 16.0), np.array([0.1814, 0.3450, 59.1000])), - (('5BG', 8.0, 18.0), np.array([0.1650, 0.3460, 59.1000])), - (('5BG', 8.0, 20.0), np.array([0.1510, 0.3480, 59.1000])), - (('5BG', 8.0, 22.0), np.array([0.1380, 0.3490, 59.1000])), - (('5BG', 8.0, 24.0), np.array([0.1220, 0.3510, 59.1000])), - (('5BG', 8.0, 26.0), np.array([0.1050, 0.3520, 59.1000])), - (('7.5BG', 8.0, 2.0), np.array([0.2900, 0.3183, 59.1000])), - (('7.5BG', 8.0, 4.0), np.array([0.2718, 0.3200, 59.1000])), - (('7.5BG', 8.0, 6.0), np.array([0.2525, 0.3198, 59.1000])), - (('7.5BG', 8.0, 8.0), np.array([0.2352, 0.3198, 59.1000])), - (('7.5BG', 8.0, 10.0), np.array([0.2184, 0.3196, 59.1000])), - (('7.5BG', 8.0, 12.0), np.array([0.2010, 0.3188, 59.1000])), - (('7.5BG', 8.0, 14.0), np.array([0.1868, 0.3179, 59.1000])), - (('7.5BG', 8.0, 16.0), np.array([0.1721, 0.3168, 59.1000])), - (('7.5BG', 8.0, 18.0), np.array([0.1550, 0.3140, 59.1000])), - (('7.5BG', 8.0, 20.0), np.array([0.1420, 0.3110, 59.1000])), - (('7.5BG', 8.0, 22.0), np.array([0.1270, 0.3090, 59.1000])), - (('7.5BG', 8.0, 24.0), np.array([0.1110, 0.3070, 59.1000])), - (('10BG', 8.0, 2.0), np.array([0.2894, 0.3152, 59.1000])), - (('10BG', 8.0, 4.0), np.array([0.2686, 0.3130, 59.1000])), - (('10BG', 8.0, 6.0), np.array([0.2489, 0.3099, 59.1000])), - (('10BG', 8.0, 8.0), np.array([0.2302, 0.3063, 59.1000])), - (('10BG', 8.0, 10.0), np.array([0.2120, 0.3025, 59.1000])), - (('10BG', 8.0, 12.0), np.array([0.1937, 0.2978, 59.1000])), - (('10BG', 8.0, 14.0), np.array([0.1788, 0.2936, 59.1000])), - (('10BG', 8.0, 16.0), np.array([0.1610, 0.2880, 59.1000])), - (('10BG', 8.0, 18.0), np.array([0.1450, 0.2820, 59.1000])), - (('10BG', 8.0, 20.0), np.array([0.1330, 0.2780, 59.1000])), - (('10BG', 8.0, 22.0), np.array([0.1180, 0.2730, 59.1000])), - (('2.5B', 8.0, 2.0), np.array([0.2897, 0.3124, 59.1000])), - (('2.5B', 8.0, 4.0), np.array([0.2668, 0.3067, 59.1000])), - (('2.5B', 8.0, 6.0), np.array([0.2462, 0.3000, 59.1000])), - (('2.5B', 8.0, 8.0), np.array([0.2264, 0.2923, 59.1000])), - (('2.5B', 8.0, 10.0), np.array([0.2066, 0.2839, 59.1000])), - (('2.5B', 8.0, 12.0), np.array([0.1877, 0.2752, 59.1000])), - (('2.5B', 8.0, 14.0), np.array([0.1720, 0.2660, 59.1000])), - (('2.5B', 8.0, 16.0), np.array([0.1520, 0.2560, 59.1000])), - (('2.5B', 8.0, 18.0), np.array([0.1370, 0.2450, 59.1000])), - (('2.5B', 8.0, 20.0), np.array([0.1230, 0.2350, 59.1000])), - (('5B', 8.0, 2.0), np.array([0.2908, 0.3096, 59.1000])), - (('5B', 8.0, 4.0), np.array([0.2671, 0.2998, 59.1000])), - (('5B', 8.0, 6.0), np.array([0.2457, 0.2888, 59.1000])), - (('5B', 8.0, 8.0), np.array([0.2237, 0.2761, 59.1000])), - (('5B', 8.0, 10.0), np.array([0.2040, 0.2630, 59.1000])), - (('5B', 8.0, 12.0), np.array([0.1820, 0.2470, 59.1000])), - (('5B', 8.0, 14.0), np.array([0.1660, 0.2360, 59.1000])), - (('5B', 8.0, 16.0), np.array([0.1480, 0.2210, 59.1000])), - (('5B', 8.0, 18.0), np.array([0.1320, 0.2070, 59.1000])), - (('7.5B', 8.0, 2.0), np.array([0.2922, 0.3077, 59.1000])), - (('7.5B', 8.0, 4.0), np.array([0.2688, 0.2956, 59.1000])), - (('7.5B', 8.0, 6.0), np.array([0.2472, 0.2821, 59.1000])), - (('7.5B', 8.0, 8.0), np.array([0.2252, 0.2668, 59.1000])), - (('7.5B', 8.0, 10.0), np.array([0.2050, 0.2500, 59.1000])), - (('7.5B', 8.0, 12.0), np.array([0.1840, 0.2330, 59.1000])), - (('7.5B', 8.0, 14.0), np.array([0.1680, 0.2190, 59.1000])), - (('7.5B', 8.0, 16.0), np.array([0.1510, 0.2040, 59.1000])), - (('10B', 8.0, 2.0), np.array([0.2935, 0.3062, 59.1000])), - (('10B', 8.0, 4.0), np.array([0.2718, 0.2911, 59.1000])), - (('10B', 8.0, 6.0), np.array([0.2512, 0.2760, 59.1000])), - (('10B', 8.0, 8.0), np.array([0.2294, 0.2587, 59.1000])), - (('10B', 8.0, 10.0), np.array([0.2100, 0.2420, 59.1000])), - (('10B', 8.0, 12.0), np.array([0.1900, 0.2240, 59.1000])), - (('10B', 8.0, 14.0), np.array([0.1740, 0.2060, 59.1000])), - (('2.5PB', 8.0, 2.0), np.array([0.2957, 0.3047, 59.1000])), - (('2.5PB', 8.0, 4.0), np.array([0.2758, 0.2879, 59.1000])), - (('2.5PB', 8.0, 6.0), np.array([0.2562, 0.2709, 59.1000])), - (('2.5PB', 8.0, 8.0), np.array([0.2370, 0.2530, 59.1000])), - (('2.5PB', 8.0, 10.0), np.array([0.2180, 0.2350, 59.1000])), - (('2.5PB', 8.0, 12.0), np.array([0.2020, 0.2170, 59.1000])), - (('2.5PB', 8.0, 14.0), np.array([0.1850, 0.1990, 59.1000])), - (('5PB', 8.0, 2.0), np.array([0.2974, 0.3039, 59.1000])), - (('5PB', 8.0, 4.0), np.array([0.2798, 0.2861, 59.1000])), - (('5PB', 8.0, 6.0), np.array([0.2614, 0.2670, 59.1000])), - (('5PB', 8.0, 8.0), np.array([0.2440, 0.2490, 59.1000])), - (('5PB', 8.0, 10.0), np.array([0.2270, 0.2290, 59.1000])), - (('5PB', 8.0, 12.0), np.array([0.2140, 0.2140, 59.1000])), - (('5PB', 8.0, 14.0), np.array([0.2000, 0.1940, 59.1000])), - (('7.5PB', 8.0, 2.0), np.array([0.3003, 0.3034, 59.1000])), - (('7.5PB', 8.0, 4.0), np.array([0.2856, 0.2846, 59.1000])), - (('7.5PB', 8.0, 6.0), np.array([0.2702, 0.2648, 59.1000])), - (('7.5PB', 8.0, 8.0), np.array([0.2550, 0.2440, 59.1000])), - (('7.5PB', 8.0, 10.0), np.array([0.2410, 0.2240, 59.1000])), - (('7.5PB', 8.0, 12.0), np.array([0.2320, 0.2100, 59.1000])), - (('7.5PB', 8.0, 14.0), np.array([0.2210, 0.1930, 59.1000])), - (('10PB', 8.0, 2.0), np.array([0.3027, 0.3035, 59.1000])), - (('10PB', 8.0, 4.0), np.array([0.2911, 0.2848, 59.1000])), - (('10PB', 8.0, 6.0), np.array([0.2792, 0.2649, 59.1000])), - (('10PB', 8.0, 8.0), np.array([0.2677, 0.2443, 59.1000])), - (('10PB', 8.0, 10.0), np.array([0.2570, 0.2250, 59.1000])), - (('10PB', 8.0, 12.0), np.array([0.2480, 0.2110, 59.1000])), - (('10PB', 8.0, 14.0), np.array([0.2400, 0.1960, 59.1000])), - (('10PB', 8.0, 16.0), np.array([0.2330, 0.1850, 59.1000])), - (('2.5P', 8.0, 2.0), np.array([0.3048, 0.3040, 59.1000])), - (('2.5P', 8.0, 4.0), np.array([0.2962, 0.2850, 59.1000])), - (('2.5P', 8.0, 6.0), np.array([0.2881, 0.2671, 59.1000])), - (('2.5P', 8.0, 8.0), np.array([0.2800, 0.2488, 59.1000])), - (('2.5P', 8.0, 10.0), np.array([0.2740, 0.2310, 59.1000])), - (('2.5P', 8.0, 12.0), np.array([0.2680, 0.2180, 59.1000])), - (('2.5P', 8.0, 14.0), np.array([0.2630, 0.2040, 59.1000])), - (('2.5P', 8.0, 16.0), np.array([0.2590, 0.1940, 59.1000])), - (('2.5P', 8.0, 18.0), np.array([0.2550, 0.1830, 59.1000])), - (('5P', 8.0, 2.0), np.array([0.3065, 0.3047, 59.1000])), - (('5P', 8.0, 4.0), np.array([0.3012, 0.2868, 59.1000])), - (('5P', 8.0, 6.0), np.array([0.2963, 0.2704, 59.1000])), - (('5P', 8.0, 8.0), np.array([0.2914, 0.2534, 59.1000])), - (('5P', 8.0, 10.0), np.array([0.2870, 0.2380, 59.1000])), - (('5P', 8.0, 12.0), np.array([0.2830, 0.2240, 59.1000])), - (('5P', 8.0, 14.0), np.array([0.2800, 0.2110, 59.1000])), - (('5P', 8.0, 16.0), np.array([0.2780, 0.2010, 59.1000])), - (('5P', 8.0, 18.0), np.array([0.2760, 0.1910, 59.1000])), - (('5P', 8.0, 20.0), np.array([0.2730, 0.1790, 59.1000])), - (('5P', 8.0, 22.0), np.array([0.2700, 0.1680, 59.1000])), - (('7.5P', 8.0, 2.0), np.array([0.3107, 0.3070, 59.1000])), - (('7.5P', 8.0, 4.0), np.array([0.3114, 0.2915, 59.1000])), - (('7.5P', 8.0, 6.0), np.array([0.3114, 0.2785, 59.1000])), - (('7.5P', 8.0, 8.0), np.array([0.3116, 0.2626, 59.1000])), - (('7.5P', 8.0, 10.0), np.array([0.3116, 0.2497, 59.1000])), - (('7.5P', 8.0, 12.0), np.array([0.3117, 0.2370, 59.1000])), - (('7.5P', 8.0, 14.0), np.array([0.3110, 0.2240, 59.1000])), - (('7.5P', 8.0, 16.0), np.array([0.3110, 0.2140, 59.1000])), - (('7.5P', 8.0, 18.0), np.array([0.3110, 0.2040, 59.1000])), - (('7.5P', 8.0, 20.0), np.array([0.3110, 0.1940, 59.1000])), - (('7.5P', 8.0, 22.0), np.array([0.3110, 0.1840, 59.1000])), - (('7.5P', 8.0, 24.0), np.array([0.3110, 0.1730, 59.1000])), - (('7.5P', 8.0, 26.0), np.array([0.3110, 0.1620, 59.1000])), - (('10P', 8.0, 2.0), np.array([0.3131, 0.3084, 59.1000])), - (('10P', 8.0, 4.0), np.array([0.3175, 0.2955, 59.1000])), - (('10P', 8.0, 6.0), np.array([0.3213, 0.2829, 59.1000])), - (('10P', 8.0, 8.0), np.array([0.3250, 0.2700, 59.1000])), - (('10P', 8.0, 10.0), np.array([0.3282, 0.2582, 59.1000])), - (('10P', 8.0, 12.0), np.array([0.3312, 0.2470, 59.1000])), - (('10P', 8.0, 14.0), np.array([0.3342, 0.2349, 59.1000])), - (('10P', 8.0, 16.0), np.array([0.3370, 0.2250, 59.1000])), - (('10P', 8.0, 18.0), np.array([0.3390, 0.2170, 59.1000])), - (('10P', 8.0, 20.0), np.array([0.3410, 0.2070, 59.1000])), - (('10P', 8.0, 22.0), np.array([0.3440, 0.1960, 59.1000])), - (('10P', 8.0, 24.0), np.array([0.3460, 0.1860, 59.1000])), - (('10P', 8.0, 26.0), np.array([0.3480, 0.1760, 59.1000])), - (('2.5RP', 8.0, 2.0), np.array([0.3154, 0.3100, 59.1000])), - (('2.5RP', 8.0, 4.0), np.array([0.3239, 0.3000, 59.1000])), - (('2.5RP', 8.0, 6.0), np.array([0.3327, 0.2898, 59.1000])), - (('2.5RP', 8.0, 8.0), np.array([0.3406, 0.2793, 59.1000])), - (('2.5RP', 8.0, 10.0), np.array([0.3479, 0.2699, 59.1000])), - (('2.5RP', 8.0, 12.0), np.array([0.3552, 0.2594, 59.1000])), - (('2.5RP', 8.0, 14.0), np.array([0.3621, 0.2496, 59.1000])), - (('2.5RP', 8.0, 16.0), np.array([0.3690, 0.2400, 59.1000])), - (('2.5RP', 8.0, 18.0), np.array([0.3730, 0.2330, 59.1000])), - (('2.5RP', 8.0, 20.0), np.array([0.3790, 0.2240, 59.1000])), - (('2.5RP', 8.0, 22.0), np.array([0.3850, 0.2150, 59.1000])), - (('2.5RP', 8.0, 24.0), np.array([0.3890, 0.2060, 59.1000])), - (('2.5RP', 8.0, 26.0), np.array([0.3940, 0.1970, 59.1000])), - (('5RP', 8.0, 2.0), np.array([0.3180, 0.3120, 59.1000])), - (('5RP', 8.0, 4.0), np.array([0.3308, 0.3052, 59.1000])), - (('5RP', 8.0, 6.0), np.array([0.3440, 0.2978, 59.1000])), - (('5RP', 8.0, 8.0), np.array([0.3570, 0.2900, 59.1000])), - (('5RP', 8.0, 10.0), np.array([0.3685, 0.2828, 59.1000])), - (('5RP', 8.0, 12.0), np.array([0.3818, 0.2742, 59.1000])), - (('5RP', 8.0, 14.0), np.array([0.3930, 0.2670, 59.1000])), - (('5RP', 8.0, 16.0), np.array([0.4020, 0.2600, 59.1000])), - (('5RP', 8.0, 18.0), np.array([0.4110, 0.2530, 59.1000])), - (('5RP', 8.0, 20.0), np.array([0.4200, 0.2450, 59.1000])), - (('5RP', 8.0, 22.0), np.array([0.4290, 0.2370, 59.1000])), - (('5RP', 8.0, 24.0), np.array([0.4370, 0.2300, 59.1000])), - (('7.5RP', 8.0, 2.0), np.array([0.3200, 0.3136, 59.1000])), - (('7.5RP', 8.0, 4.0), np.array([0.3360, 0.3092, 59.1000])), - (('7.5RP', 8.0, 6.0), np.array([0.3521, 0.3042, 59.1000])), - (('7.5RP', 8.0, 8.0), np.array([0.3682, 0.2983, 59.1000])), - (('7.5RP', 8.0, 10.0), np.array([0.3830, 0.2930, 59.1000])), - (('7.5RP', 8.0, 12.0), np.array([0.4002, 0.2859, 59.1000])), - (('7.5RP', 8.0, 14.0), np.array([0.4140, 0.2800, 59.1000])), - (('7.5RP', 8.0, 16.0), np.array([0.4260, 0.2740, 59.1000])), - (('7.5RP', 8.0, 18.0), np.array([0.4380, 0.2670, 59.1000])), - (('7.5RP', 8.0, 20.0), np.array([0.4490, 0.2610, 59.1000])), - (('7.5RP', 8.0, 22.0), np.array([0.4600, 0.2550, 59.1000])), - (('10RP', 8.0, 2.0), np.array([0.3218, 0.3152, 59.1000])), - (('10RP', 8.0, 4.0), np.array([0.3412, 0.3135, 59.1000])), - (('10RP', 8.0, 6.0), np.array([0.3600, 0.3112, 59.1000])), - (('10RP', 8.0, 8.0), np.array([0.3800, 0.3082, 59.1000])), - (('10RP', 8.0, 10.0), np.array([0.3983, 0.3049, 59.1000])), - (('10RP', 8.0, 12.0), np.array([0.4220, 0.3000, 59.1000])), - (('10RP', 8.0, 14.0), np.array([0.4390, 0.2970, 59.1000])), - (('10RP', 8.0, 16.0), np.array([0.4530, 0.2930, 59.1000])), - (('10RP', 8.0, 18.0), np.array([0.4690, 0.2890, 59.1000])), - (('10RP', 8.0, 20.0), np.array([0.4840, 0.2840, 59.1000])), - (('2.5R', 8.0, 2.0), np.array([0.3236, 0.3169, 59.1000])), - (('2.5R', 8.0, 4.0), np.array([0.3460, 0.3177, 59.1000])), - (('2.5R', 8.0, 6.0), np.array([0.3671, 0.3175, 59.1000])), - (('2.5R', 8.0, 8.0), np.array([0.3900, 0.3171, 59.1000])), - (('2.5R', 8.0, 10.0), np.array([0.4125, 0.3160, 59.1000])), - (('2.5R', 8.0, 12.0), np.array([0.4390, 0.3140, 59.1000])), - (('2.5R', 8.0, 14.0), np.array([0.4600, 0.3110, 59.1000])), - (('2.5R', 8.0, 16.0), np.array([0.4760, 0.3080, 59.1000])), - (('2.5R', 8.0, 18.0), np.array([0.4930, 0.3060, 59.1000])), - (('2.5R', 8.0, 20.0), np.array([0.5100, 0.3030, 59.1000])), - (('5R', 8.0, 2.0), np.array([0.3254, 0.3186, 59.1000])), - (('5R', 8.0, 4.0), np.array([0.3510, 0.3224, 59.1000])), - (('5R', 8.0, 6.0), np.array([0.3743, 0.3248, 59.1000])), - (('5R', 8.0, 8.0), np.array([0.4001, 0.3263, 59.1000])), - (('5R', 8.0, 10.0), np.array([0.4249, 0.3270, 59.1000])), - (('5R', 8.0, 12.0), np.array([0.4540, 0.3260, 59.1000])), - (('5R', 8.0, 14.0), np.array([0.4760, 0.3250, 59.1000])), - (('5R', 8.0, 16.0), np.array([0.4940, 0.3240, 59.1000])), - (('5R', 8.0, 18.0), np.array([0.5130, 0.3220, 59.1000])), - (('7.5R', 8.0, 2.0), np.array([0.3277, 0.3211, 59.1000])), - (('7.5R', 8.0, 4.0), np.array([0.3564, 0.3279, 59.1000])), - (('7.5R', 8.0, 6.0), np.array([0.3830, 0.3335, 59.1000])), - (('7.5R', 8.0, 8.0), np.array([0.4118, 0.3385, 59.1000])), - (('7.5R', 8.0, 10.0), np.array([0.4388, 0.3419, 59.1000])), - (('7.5R', 8.0, 12.0), np.array([0.4700, 0.3450, 59.1000])), - (('7.5R', 8.0, 14.0), np.array([0.4920, 0.3440, 59.1000])), - (('7.5R', 8.0, 16.0), np.array([0.5140, 0.3440, 59.1000])), - (('7.5R', 8.0, 18.0), np.array([0.5360, 0.3430, 59.1000])), - (('7.5R', 8.0, 20.0), np.array([0.5530, 0.3430, 59.1000])), - (('10R', 8.0, 2.0), np.array([0.3301, 0.3237, 59.1000])), - (('10R', 8.0, 4.0), np.array([0.3621, 0.3349, 59.1000])), - (('10R', 8.0, 6.0), np.array([0.3910, 0.3442, 59.1000])), - (('10R', 8.0, 8.0), np.array([0.4212, 0.3526, 59.1000])), - (('10R', 8.0, 10.0), np.array([0.4490, 0.3589, 59.1000])), - (('10R', 8.0, 12.0), np.array([0.4810, 0.3650, 59.1000])), - (('10R', 8.0, 14.0), np.array([0.5070, 0.3690, 59.1000])), - (('10R', 8.0, 16.0), np.array([0.5290, 0.3710, 59.1000])), - (('10R', 8.0, 18.0), np.array([0.5510, 0.3740, 59.1000])), - (('10R', 8.0, 20.0), np.array([0.5690, 0.3740, 59.1000])), - (('10R', 8.0, 22.0), np.array([0.5880, 0.3750, 59.1000])), - (('10R', 8.0, 24.0), np.array([0.6070, 0.3750, 59.1000])), - (('10R', 8.0, 26.0), np.array([0.6250, 0.3750, 59.1000])), - (('2.5YR', 8.0, 2.0), np.array([0.3334, 0.3276, 59.1000])), - (('2.5YR', 8.0, 4.0), np.array([0.3667, 0.3429, 59.1000])), - (('2.5YR', 8.0, 6.0), np.array([0.3960, 0.3547, 59.1000])), - (('2.5YR', 8.0, 8.0), np.array([0.4275, 0.3662, 59.1000])), - (('2.5YR', 8.0, 10.0), np.array([0.4552, 0.3761, 59.1000])), - (('2.5YR', 8.0, 12.0), np.array([0.4852, 0.3847, 59.1000])), - (('2.5YR', 8.0, 14.0), np.array([0.5130, 0.3910, 59.1000])), - (('2.5YR', 8.0, 16.0), np.array([0.5330, 0.3960, 59.1000])), - (('2.5YR', 8.0, 18.0), np.array([0.5510, 0.3990, 59.1000])), - (('2.5YR', 8.0, 20.0), np.array([0.5660, 0.4020, 59.1000])), - (('2.5YR', 8.0, 22.0), np.array([0.5800, 0.4030, 59.1000])), - (('2.5YR', 8.0, 24.0), np.array([0.5920, 0.4040, 59.1000])), - (('2.5YR', 8.0, 26.0), np.array([0.6020, 0.4050, 59.1000])), - (('5YR', 8.0, 2.0), np.array([0.3373, 0.3330, 59.1000])), - (('5YR', 8.0, 4.0), np.array([0.3690, 0.3510, 59.1000])), - (('5YR', 8.0, 6.0), np.array([0.3988, 0.3663, 59.1000])), - (('5YR', 8.0, 8.0), np.array([0.4310, 0.3820, 59.1000])), - (('5YR', 8.0, 10.0), np.array([0.4576, 0.3938, 59.1000])), - (('5YR', 8.0, 12.0), np.array([0.4849, 0.4050, 59.1000])), - (('5YR', 8.0, 14.0), np.array([0.5088, 0.4145, 59.1000])), - (('5YR', 8.0, 16.0), np.array([0.5300, 0.4210, 59.1000])), - (('5YR', 8.0, 18.0), np.array([0.5420, 0.4250, 59.1000])), - (('5YR', 8.0, 20.0), np.array([0.5530, 0.4270, 59.1000])), - (('5YR', 8.0, 22.0), np.array([0.5610, 0.4310, 59.1000])), - (('5YR', 8.0, 24.0), np.array([0.5710, 0.4330, 59.1000])), - (('5YR', 8.0, 26.0), np.array([0.5780, 0.4350, 59.1000])), - (('7.5YR', 8.0, 2.0), np.array([0.3395, 0.3379, 59.1000])), - (('7.5YR', 8.0, 4.0), np.array([0.3699, 0.3586, 59.1000])), - (('7.5YR', 8.0, 6.0), np.array([0.4000, 0.3770, 59.1000])), - (('7.5YR', 8.0, 8.0), np.array([0.4306, 0.3952, 59.1000])), - (('7.5YR', 8.0, 10.0), np.array([0.4568, 0.4100, 59.1000])), - (('7.5YR', 8.0, 12.0), np.array([0.4816, 0.4232, 59.1000])), - (('7.5YR', 8.0, 14.0), np.array([0.5025, 0.4338, 59.1000])), - (('7.5YR', 8.0, 16.0), np.array([0.5195, 0.4424, 59.1000])), - (('7.5YR', 8.0, 18.0), np.array([0.5316, 0.4480, 59.1000])), - (('7.5YR', 8.0, 20.0), np.array([0.5391, 0.4518, 59.1000])), - (('7.5YR', 8.0, 22.0), np.array([0.5460, 0.4540, 59.1000])), - (('7.5YR', 8.0, 24.0), np.array([0.5530, 0.4580, 59.1000])), - (('10YR', 8.0, 2.0), np.array([0.3407, 0.3434, 59.1000])), - (('10YR', 8.0, 4.0), np.array([0.3701, 0.3674, 59.1000])), - (('10YR', 8.0, 6.0), np.array([0.3994, 0.3896, 59.1000])), - (('10YR', 8.0, 8.0), np.array([0.4280, 0.4102, 59.1000])), - (('10YR', 8.0, 10.0), np.array([0.4527, 0.4268, 59.1000])), - (('10YR', 8.0, 12.0), np.array([0.4753, 0.4414, 59.1000])), - (('10YR', 8.0, 14.0), np.array([0.4940, 0.4530, 59.1000])), - (('10YR', 8.0, 16.0), np.array([0.5079, 0.4613, 59.1000])), - (('10YR', 8.0, 18.0), np.array([0.5179, 0.4670, 59.1000])), - (('10YR', 8.0, 20.0), np.array([0.5245, 0.4709, 59.1000])), - (('10YR', 8.0, 22.0), np.array([0.5300, 0.4740, 59.1000])), - (('2.5Y', 8.0, 2.0), np.array([0.3406, 0.3484, 59.1000])), - (('2.5Y', 8.0, 4.0), np.array([0.3684, 0.3751, 59.1000])), - (('2.5Y', 8.0, 6.0), np.array([0.3969, 0.4009, 59.1000])), - (('2.5Y', 8.0, 8.0), np.array([0.4231, 0.4231, 59.1000])), - (('2.5Y', 8.0, 10.0), np.array([0.4469, 0.4423, 59.1000])), - (('2.5Y', 8.0, 12.0), np.array([0.4678, 0.4589, 59.1000])), - (('2.5Y', 8.0, 14.0), np.array([0.4842, 0.4712, 59.1000])), - (('2.5Y', 8.0, 16.0), np.array([0.4957, 0.4800, 59.1000])), - (('2.5Y', 8.0, 18.0), np.array([0.5033, 0.4855, 59.1000])), - (('2.5Y', 8.0, 20.0), np.array([0.5091, 0.4900, 59.1000])), - (('2.5Y', 8.0, 22.0), np.array([0.5140, 0.4940, 59.1000])), - (('5Y', 8.0, 2.0), np.array([0.3394, 0.3518, 59.1000])), - (('5Y', 8.0, 4.0), np.array([0.3650, 0.3826, 59.1000])), - (('5Y', 8.0, 6.0), np.array([0.3913, 0.4117, 59.1000])), - (('5Y', 8.0, 8.0), np.array([0.4158, 0.4378, 59.1000])), - (('5Y', 8.0, 10.0), np.array([0.4376, 0.4601, 59.1000])), - (('5Y', 8.0, 12.0), np.array([0.4562, 0.4788, 59.1000])), - (('5Y', 8.0, 14.0), np.array([0.4699, 0.4920, 59.1000])), - (('5Y', 8.0, 16.0), np.array([0.4791, 0.5012, 59.1000])), - (('5Y', 8.0, 18.0), np.array([0.4847, 0.5069, 59.1000])), - (('5Y', 8.0, 20.0), np.array([0.4900, 0.5110, 59.1000])), - (('5Y', 8.0, 22.0), np.array([0.4930, 0.5150, 59.1000])), - (('7.5Y', 8.0, 2.0), np.array([0.3379, 0.3540, 59.1000])), - (('7.5Y', 8.0, 4.0), np.array([0.3622, 0.3861, 59.1000])), - (('7.5Y', 8.0, 6.0), np.array([0.3862, 0.4175, 59.1000])), - (('7.5Y', 8.0, 8.0), np.array([0.4088, 0.4466, 59.1000])), - (('7.5Y', 8.0, 10.0), np.array([0.4283, 0.4712, 59.1000])), - (('7.5Y', 8.0, 12.0), np.array([0.4455, 0.4917, 59.1000])), - (('7.5Y', 8.0, 14.0), np.array([0.4574, 0.5062, 59.1000])), - (('7.5Y', 8.0, 16.0), np.array([0.4658, 0.5158, 59.1000])), - (('7.5Y', 8.0, 18.0), np.array([0.4709, 0.5220, 59.1000])), - (('7.5Y', 8.0, 20.0), np.array([0.4750, 0.5260, 59.1000])), - (('10Y', 9.0, 2.0), np.array([0.3349, 0.3537, 78.6600])), - (('10Y', 9.0, 4.0), np.array([0.3558, 0.3852, 78.6600])), - (('10Y', 9.0, 6.0), np.array([0.3761, 0.4155, 78.6600])), - (('10Y', 9.0, 8.0), np.array([0.3957, 0.4450, 78.6600])), - (('10Y', 9.0, 10.0), np.array([0.4120, 0.4694, 78.6600])), - (('10Y', 9.0, 12.0), np.array([0.4271, 0.4920, 78.6600])), - (('10Y', 9.0, 14.0), np.array([0.4393, 0.5101, 78.6600])), - (('10Y', 9.0, 16.0), np.array([0.4477, 0.5225, 78.6600])), - (('10Y', 9.0, 18.0), np.array([0.4540, 0.5320, 78.6600])), - (('10Y', 9.0, 20.0), np.array([0.4570, 0.5370, 78.6600])), - (('10Y', 9.0, 22.0), np.array([0.4620, 0.5440, 78.6600])), - (('2.5GY', 9.0, 2.0), np.array([0.3321, 0.3539, 78.6600])), - (('2.5GY', 9.0, 4.0), np.array([0.3499, 0.3866, 78.6600])), - (('2.5GY', 9.0, 6.0), np.array([0.3670, 0.4178, 78.6600])), - (('2.5GY', 9.0, 8.0), np.array([0.3834, 0.4490, 78.6600])), - (('2.5GY', 9.0, 10.0), np.array([0.3973, 0.4761, 78.6600])), - (('2.5GY', 9.0, 12.0), np.array([0.4108, 0.5028, 78.6600])), - (('2.5GY', 9.0, 14.0), np.array([0.4212, 0.5237, 78.6600])), - (('2.5GY', 9.0, 16.0), np.array([0.4288, 0.5383, 78.6600])), - (('2.5GY', 9.0, 18.0), np.array([0.4354, 0.5508, 78.6600])), - (('2.5GY', 9.0, 20.0), np.array([0.4380, 0.5560, 78.6600])), - (('2.5GY', 9.0, 22.0), np.array([0.4420, 0.5640, 78.6600])), - (('5GY', 9.0, 2.0), np.array([0.3284, 0.3534, 78.6600])), - (('5GY', 9.0, 4.0), np.array([0.3437, 0.3861, 78.6600])), - (('5GY', 9.0, 6.0), np.array([0.3572, 0.4179, 78.6600])), - (('5GY', 9.0, 8.0), np.array([0.3698, 0.4497, 78.6600])), - (('5GY', 9.0, 10.0), np.array([0.3810, 0.4791, 78.6600])), - (('5GY', 9.0, 12.0), np.array([0.3911, 0.5082, 78.6600])), - (('5GY', 9.0, 14.0), np.array([0.3993, 0.5329, 78.6600])), - (('5GY', 9.0, 16.0), np.array([0.4058, 0.5541, 78.6600])), - (('5GY', 9.0, 18.0), np.array([0.4108, 0.5699, 78.6600])), - (('5GY', 9.0, 20.0), np.array([0.4140, 0.5800, 78.6600])), - (('5GY', 9.0, 22.0), np.array([0.4170, 0.5900, 78.6600])), - (('7.5GY', 9.0, 2.0), np.array([0.3198, 0.3500, 78.6600])), - (('7.5GY', 9.0, 4.0), np.array([0.3274, 0.3793, 78.6600])), - (('7.5GY', 9.0, 6.0), np.array([0.3351, 0.4111, 78.6600])), - (('7.5GY', 9.0, 8.0), np.array([0.3414, 0.4415, 78.6600])), - (('7.5GY', 9.0, 10.0), np.array([0.3471, 0.4735, 78.6600])), - (('7.5GY', 9.0, 12.0), np.array([0.3518, 0.5042, 78.6600])), - (('7.5GY', 9.0, 14.0), np.array([0.3551, 0.5339, 78.6600])), - (('7.5GY', 9.0, 16.0), np.array([0.3581, 0.5654, 78.6600])), - (('7.5GY', 9.0, 18.0), np.array([0.3602, 0.5920, 78.6600])), - (('7.5GY', 9.0, 20.0), np.array([0.3610, 0.6140, 78.6600])), - (('7.5GY', 9.0, 22.0), np.array([0.3620, 0.6380, 78.6600])), - (('7.5GY', 9.0, 24.0), np.array([0.3600, 0.6570, 78.6600])), - (('7.5GY', 9.0, 26.0), np.array([0.3600, 0.6820, 78.6600])), - (('7.5GY', 9.0, 28.0), np.array([0.3600, 0.7070, 78.6600])), - (('10GY', 9.0, 2.0), np.array([0.3124, 0.3454, 78.6600])), - (('10GY', 9.0, 4.0), np.array([0.3144, 0.3711, 78.6600])), - (('10GY', 9.0, 6.0), np.array([0.3153, 0.4008, 78.6600])), - (('10GY', 9.0, 8.0), np.array([0.3157, 0.4259, 78.6600])), - (('10GY', 9.0, 10.0), np.array([0.3155, 0.4558, 78.6600])), - (('10GY', 9.0, 12.0), np.array([0.3139, 0.4829, 78.6600])), - (('10GY', 9.0, 14.0), np.array([0.3115, 0.5129, 78.6600])), - (('10GY', 9.0, 16.0), np.array([0.3079, 0.5440, 78.6600])), - (('10GY', 9.0, 18.0), np.array([0.3032, 0.5748, 78.6600])), - (('10GY', 9.0, 20.0), np.array([0.2980, 0.6020, 78.6600])), - (('10GY', 9.0, 22.0), np.array([0.2920, 0.6270, 78.6600])), - (('10GY', 9.0, 24.0), np.array([0.2840, 0.6530, 78.6600])), - (('10GY', 9.0, 26.0), np.array([0.2770, 0.6810, 78.6600])), - (('10GY', 9.0, 28.0), np.array([0.2680, 0.7070, 78.6600])), - (('2.5G', 9.0, 2.0), np.array([0.3058, 0.3400, 78.6600])), - (('2.5G', 9.0, 4.0), np.array([0.3018, 0.3606, 78.6600])), - (('2.5G', 9.0, 6.0), np.array([0.2966, 0.3846, 78.6600])), - (('2.5G', 9.0, 8.0), np.array([0.2912, 0.4054, 78.6600])), - (('2.5G', 9.0, 10.0), np.array([0.2851, 0.4275, 78.6600])), - (('2.5G', 9.0, 12.0), np.array([0.2786, 0.4491, 78.6600])), - (('2.5G', 9.0, 14.0), np.array([0.2711, 0.4726, 78.6600])), - (('2.5G', 9.0, 16.0), np.array([0.2630, 0.4966, 78.6600])), - (('2.5G', 9.0, 18.0), np.array([0.2530, 0.5250, 78.6600])), - (('2.5G', 9.0, 20.0), np.array([0.2420, 0.5520, 78.6600])), - (('2.5G', 9.0, 22.0), np.array([0.2300, 0.5760, 78.6600])), - (('2.5G', 9.0, 24.0), np.array([0.2190, 0.5990, 78.6600])), - (('2.5G', 9.0, 26.0), np.array([0.2060, 0.6190, 78.6600])), - (('2.5G', 9.0, 28.0), np.array([0.1920, 0.6430, 78.6600])), - (('5G', 9.0, 2.0), np.array([0.3017, 0.3357, 78.6600])), - (('5G', 9.0, 4.0), np.array([0.2933, 0.3519, 78.6600])), - (('5G', 9.0, 6.0), np.array([0.2832, 0.3697, 78.6600])), - (('5G', 9.0, 8.0), np.array([0.2735, 0.3854, 78.6600])), - (('5G', 9.0, 10.0), np.array([0.2639, 0.4001, 78.6600])), - (('5G', 9.0, 12.0), np.array([0.2528, 0.4160, 78.6600])), - (('5G', 9.0, 14.0), np.array([0.2420, 0.4300, 78.6600])), - (('5G', 9.0, 16.0), np.array([0.2290, 0.4460, 78.6600])), - (('5G', 9.0, 18.0), np.array([0.2140, 0.4610, 78.6600])), - (('5G', 9.0, 20.0), np.array([0.2000, 0.4740, 78.6600])), - (('5G', 9.0, 22.0), np.array([0.1850, 0.4870, 78.6600])), - (('5G', 9.0, 24.0), np.array([0.1700, 0.4990, 78.6600])), - (('5G', 9.0, 26.0), np.array([0.1570, 0.5090, 78.6600])), - (('5G', 9.0, 28.0), np.array([0.1430, 0.5190, 78.6600])), - (('7.5G', 9.0, 2.0), np.array([0.2987, 0.3323, 78.6600])), - (('7.5G', 9.0, 4.0), np.array([0.2882, 0.3461, 78.6600])), - (('7.5G', 9.0, 6.0), np.array([0.2763, 0.3607, 78.6600])), - (('7.5G', 9.0, 8.0), np.array([0.2652, 0.3738, 78.6600])), - (('7.5G', 9.0, 10.0), np.array([0.2545, 0.3855, 78.6600])), - (('7.5G', 9.0, 12.0), np.array([0.2419, 0.3985, 78.6600])), - (('7.5G', 9.0, 14.0), np.array([0.2290, 0.4100, 78.6600])), - (('7.5G', 9.0, 16.0), np.array([0.2160, 0.4230, 78.6600])), - (('7.5G', 9.0, 18.0), np.array([0.2010, 0.4360, 78.6600])), - (('7.5G', 9.0, 20.0), np.array([0.1870, 0.4460, 78.6600])), - (('7.5G', 9.0, 22.0), np.array([0.1740, 0.4570, 78.6600])), - (('7.5G', 9.0, 24.0), np.array([0.1600, 0.4670, 78.6600])), - (('10G', 9.0, 2.0), np.array([0.2965, 0.3293, 78.6600])), - (('10G', 9.0, 4.0), np.array([0.2840, 0.3402, 78.6600])), - (('10G', 9.0, 6.0), np.array([0.2703, 0.3513, 78.6600])), - (('10G', 9.0, 8.0), np.array([0.2574, 0.3618, 78.6600])), - (('10G', 9.0, 10.0), np.array([0.2457, 0.3702, 78.6600])), - (('10G', 9.0, 12.0), np.array([0.2325, 0.3796, 78.6600])), - (('10G', 9.0, 14.0), np.array([0.2170, 0.3880, 78.6600])), - (('10G', 9.0, 16.0), np.array([0.2030, 0.3960, 78.6600])), - (('10G', 9.0, 18.0), np.array([0.1880, 0.4050, 78.6600])), - (('10G', 9.0, 20.0), np.array([0.1750, 0.4110, 78.6600])), - (('10G', 9.0, 22.0), np.array([0.1610, 0.4190, 78.6600])), - (('10G', 9.0, 24.0), np.array([0.1470, 0.4250, 78.6600])), - (('2.5BG', 9.0, 2.0), np.array([0.2947, 0.3267, 78.6600])), - (('2.5BG', 9.0, 4.0), np.array([0.2805, 0.3349, 78.6600])), - (('2.5BG', 9.0, 6.0), np.array([0.2652, 0.3433, 78.6600])), - (('2.5BG', 9.0, 8.0), np.array([0.2509, 0.3507, 78.6600])), - (('2.5BG', 9.0, 10.0), np.array([0.2382, 0.3568, 78.6600])), - (('2.5BG', 9.0, 12.0), np.array([0.2220, 0.3640, 78.6600])), - (('2.5BG', 9.0, 14.0), np.array([0.2080, 0.3700, 78.6600])), - (('2.5BG', 9.0, 16.0), np.array([0.1940, 0.3760, 78.6600])), - (('2.5BG', 9.0, 18.0), np.array([0.1780, 0.3820, 78.6600])), - (('2.5BG', 9.0, 20.0), np.array([0.1660, 0.3860, 78.6600])), - (('2.5BG', 9.0, 22.0), np.array([0.1520, 0.3910, 78.6600])), - (('5BG', 9.0, 2.0), np.array([0.2930, 0.3232, 78.6600])), - (('5BG', 9.0, 4.0), np.array([0.2768, 0.3287, 78.6600])), - (('5BG', 9.0, 6.0), np.array([0.2599, 0.3338, 78.6600])), - (('5BG', 9.0, 8.0), np.array([0.2437, 0.3378, 78.6600])), - (('5BG', 9.0, 10.0), np.array([0.2301, 0.3405, 78.6600])), - (('5BG', 9.0, 12.0), np.array([0.2120, 0.3440, 78.6600])), - (('5BG', 9.0, 14.0), np.array([0.1960, 0.3460, 78.6600])), - (('5BG', 9.0, 16.0), np.array([0.1800, 0.3480, 78.6600])), - (('5BG', 9.0, 18.0), np.array([0.1640, 0.3490, 78.6600])), - (('5BG', 9.0, 20.0), np.array([0.1500, 0.3500, 78.6600])), - (('7.5BG', 9.0, 2.0), np.array([0.2911, 0.3188, 78.6600])), - (('7.5BG', 9.0, 4.0), np.array([0.2728, 0.3208, 78.6600])), - (('7.5BG', 9.0, 6.0), np.array([0.2543, 0.3220, 78.6600])), - (('7.5BG', 9.0, 8.0), np.array([0.2361, 0.3225, 78.6600])), - (('7.5BG', 9.0, 10.0), np.array([0.2215, 0.3226, 78.6600])), - (('7.5BG', 9.0, 12.0), np.array([0.2030, 0.3230, 78.6600])), - (('7.5BG', 9.0, 14.0), np.array([0.1870, 0.3230, 78.6600])), - (('7.5BG', 9.0, 16.0), np.array([0.1700, 0.3230, 78.6600])), - (('7.5BG', 9.0, 18.0), np.array([0.1560, 0.3220, 78.6600])), - (('10BG', 9.0, 2.0), np.array([0.2907, 0.3159, 78.6600])), - (('10BG', 9.0, 4.0), np.array([0.2700, 0.3140, 78.6600])), - (('10BG', 9.0, 6.0), np.array([0.2501, 0.3118, 78.6600])), - (('10BG', 9.0, 8.0), np.array([0.2320, 0.3100, 78.6600])), - (('10BG', 9.0, 10.0), np.array([0.2140, 0.3060, 78.6600])), - (('10BG', 9.0, 12.0), np.array([0.1960, 0.3040, 78.6600])), - (('10BG', 9.0, 14.0), np.array([0.1800, 0.3010, 78.6600])), - (('10BG', 9.0, 16.0), np.array([0.1620, 0.2980, 78.6600])), - (('10BG', 9.0, 18.0), np.array([0.1470, 0.2940, 78.6600])), - (('2.5B', 9.0, 2.0), np.array([0.2909, 0.3125, 78.6600])), - (('2.5B', 9.0, 4.0), np.array([0.2680, 0.3073, 78.6600])), - (('2.5B', 9.0, 6.0), np.array([0.2470, 0.3020, 78.6600])), - (('2.5B', 9.0, 8.0), np.array([0.2280, 0.2960, 78.6600])), - (('2.5B', 9.0, 10.0), np.array([0.2110, 0.2900, 78.6600])), - (('2.5B', 9.0, 12.0), np.array([0.1930, 0.2840, 78.6600])), - (('2.5B', 9.0, 14.0), np.array([0.1760, 0.2780, 78.6600])), - (('2.5B', 9.0, 16.0), np.array([0.1580, 0.2710, 78.6600])), - (('5B', 9.0, 2.0), np.array([0.2919, 0.3102, 78.6600])), - (('5B', 9.0, 4.0), np.array([0.2675, 0.3005, 78.6600])), - (('5B', 9.0, 6.0), np.array([0.2460, 0.2900, 78.6600])), - (('5B', 9.0, 8.0), np.array([0.2280, 0.2810, 78.6600])), - (('5B', 9.0, 10.0), np.array([0.2100, 0.2720, 78.6600])), - (('5B', 9.0, 12.0), np.array([0.1910, 0.2600, 78.6600])), - (('5B', 9.0, 14.0), np.array([0.1750, 0.2510, 78.6600])), - (('5B', 9.0, 16.0), np.array([0.1570, 0.2410, 78.6600])), - (('7.5B', 9.0, 2.0), np.array([0.2937, 0.3087, 78.6600])), - (('7.5B', 9.0, 4.0), np.array([0.2688, 0.2961, 78.6600])), - (('7.5B', 9.0, 6.0), np.array([0.2470, 0.2850, 78.6600])), - (('7.5B', 9.0, 8.0), np.array([0.2280, 0.2730, 78.6600])), - (('7.5B', 9.0, 10.0), np.array([0.2110, 0.2620, 78.6600])), - (('7.5B', 9.0, 12.0), np.array([0.1940, 0.2520, 78.6600])), - (('10B', 9.0, 2.0), np.array([0.2949, 0.3076, 78.6600])), - (('10B', 9.0, 4.0), np.array([0.2712, 0.2924, 78.6600])), - (('10B', 9.0, 6.0), np.array([0.2500, 0.2780, 78.6600])), - (('10B', 9.0, 8.0), np.array([0.2330, 0.2650, 78.6600])), - (('10B', 9.0, 10.0), np.array([0.2160, 0.2500, 78.6600])), - (('2.5PB', 9.0, 2.0), np.array([0.2975, 0.3063, 78.6600])), - (('2.5PB', 9.0, 4.0), np.array([0.2760, 0.2890, 78.6600])), - (('2.5PB', 9.0, 6.0), np.array([0.2560, 0.2720, 78.6600])), - (('2.5PB', 9.0, 8.0), np.array([0.2390, 0.2570, 78.6600])), - (('2.5PB', 9.0, 10.0), np.array([0.2240, 0.2430, 78.6600])), - (('5PB', 9.0, 2.0), np.array([0.2991, 0.3057, 78.6600])), - (('5PB', 9.0, 4.0), np.array([0.2810, 0.2870, 78.6600])), - (('5PB', 9.0, 6.0), np.array([0.2620, 0.2680, 78.6600])), - (('5PB', 9.0, 8.0), np.array([0.2470, 0.2520, 78.6600])), - (('5PB', 9.0, 10.0), np.array([0.2320, 0.2350, 78.6600])), - (('7.5PB', 9.0, 2.0), np.array([0.3015, 0.3052, 78.6600])), - (('7.5PB', 9.0, 4.0), np.array([0.2860, 0.2850, 78.6600])), - (('7.5PB', 9.0, 6.0), np.array([0.2710, 0.2660, 78.6600])), - (('7.5PB', 9.0, 8.0), np.array([0.2570, 0.2450, 78.6600])), - (('7.5PB', 9.0, 10.0), np.array([0.2470, 0.2270, 78.6600])), - (('10PB', 9.0, 2.0), np.array([0.3038, 0.3054, 78.6600])), - (('10PB', 9.0, 4.0), np.array([0.2910, 0.2850, 78.6600])), - (('10PB', 9.0, 6.0), np.array([0.2780, 0.2660, 78.6600])), - (('10PB', 9.0, 8.0), np.array([0.2670, 0.2450, 78.6600])), - (('10PB', 9.0, 10.0), np.array([0.2560, 0.2260, 78.6600])), - (('2.5P', 9.0, 2.0), np.array([0.3050, 0.3051, 78.6600])), - (('2.5P', 9.0, 4.0), np.array([0.2963, 0.2865, 78.6600])), - (('2.5P', 9.0, 6.0), np.array([0.2880, 0.2680, 78.6600])), - (('2.5P', 9.0, 8.0), np.array([0.2800, 0.2490, 78.6600])), - (('2.5P', 9.0, 10.0), np.array([0.2730, 0.2300, 78.6600])), - (('5P', 9.0, 2.0), np.array([0.3067, 0.3060, 78.6600])), - (('5P', 9.0, 4.0), np.array([0.3003, 0.2870, 78.6600])), - (('5P', 9.0, 6.0), np.array([0.2960, 0.2710, 78.6600])), - (('5P', 9.0, 8.0), np.array([0.2920, 0.2530, 78.6600])), - (('5P', 9.0, 10.0), np.array([0.2860, 0.2350, 78.6600])), - (('5P', 9.0, 12.0), np.array([0.2840, 0.2210, 78.6600])), - (('5P', 9.0, 14.0), np.array([0.2810, 0.2080, 78.6600])), - (('7.5P', 9.0, 2.0), np.array([0.3107, 0.3081, 78.6600])), - (('7.5P', 9.0, 4.0), np.array([0.3117, 0.2928, 78.6600])), - (('7.5P', 9.0, 6.0), np.array([0.3120, 0.2788, 78.6600])), - (('7.5P', 9.0, 8.0), np.array([0.3110, 0.2620, 78.6600])), - (('7.5P', 9.0, 10.0), np.array([0.3100, 0.2470, 78.6600])), - (('7.5P', 9.0, 12.0), np.array([0.3080, 0.2330, 78.6600])), - (('7.5P', 9.0, 14.0), np.array([0.3060, 0.2200, 78.6600])), - (('7.5P', 9.0, 16.0), np.array([0.3050, 0.2080, 78.6600])), - (('10P', 9.0, 2.0), np.array([0.3128, 0.3094, 78.6600])), - (('10P', 9.0, 4.0), np.array([0.3176, 0.2966, 78.6600])), - (('10P', 9.0, 6.0), np.array([0.3218, 0.2845, 78.6600])), - (('10P', 9.0, 8.0), np.array([0.3260, 0.2700, 78.6600])), - (('10P', 9.0, 10.0), np.array([0.3300, 0.2570, 78.6600])), - (('10P', 9.0, 12.0), np.array([0.3340, 0.2460, 78.6600])), - (('10P', 9.0, 14.0), np.array([0.3360, 0.2350, 78.6600])), - (('10P', 9.0, 16.0), np.array([0.3390, 0.2240, 78.6600])), - (('2.5RP', 9.0, 2.0), np.array([0.3149, 0.3108, 78.6600])), - (('2.5RP', 9.0, 4.0), np.array([0.3234, 0.3010, 78.6600])), - (('2.5RP', 9.0, 6.0), np.array([0.3322, 0.2910, 78.6600])), - (('2.5RP', 9.0, 8.0), np.array([0.3400, 0.2800, 78.6600])), - (('2.5RP', 9.0, 10.0), np.array([0.3490, 0.2710, 78.6600])), - (('2.5RP', 9.0, 12.0), np.array([0.3560, 0.2610, 78.6600])), - (('2.5RP', 9.0, 14.0), np.array([0.3640, 0.2510, 78.6600])), - (('2.5RP', 9.0, 16.0), np.array([0.3710, 0.2430, 78.6600])), - (('5RP', 9.0, 2.0), np.array([0.3172, 0.3126, 78.6600])), - (('5RP', 9.0, 4.0), np.array([0.3301, 0.3060, 78.6600])), - (('5RP', 9.0, 6.0), np.array([0.3431, 0.2988, 78.6600])), - (('5RP', 9.0, 8.0), np.array([0.3550, 0.2920, 78.6600])), - (('5RP', 9.0, 10.0), np.array([0.3680, 0.2840, 78.6600])), - (('5RP', 9.0, 12.0), np.array([0.3800, 0.2760, 78.6600])), - (('5RP', 9.0, 14.0), np.array([0.3890, 0.2690, 78.6600])), - (('5RP', 9.0, 16.0), np.array([0.4000, 0.2630, 78.6600])), - (('7.5RP', 9.0, 2.0), np.array([0.3190, 0.3141, 78.6600])), - (('7.5RP', 9.0, 4.0), np.array([0.3350, 0.3099, 78.6600])), - (('7.5RP', 9.0, 6.0), np.array([0.3512, 0.3052, 78.6600])), - (('7.5RP', 9.0, 8.0), np.array([0.3640, 0.3000, 78.6600])), - (('7.5RP', 9.0, 10.0), np.array([0.3820, 0.2950, 78.6600])), - (('7.5RP', 9.0, 12.0), np.array([0.3970, 0.2890, 78.6600])), - (('7.5RP', 9.0, 14.0), np.array([0.4100, 0.2840, 78.6600])), - (('10RP', 9.0, 2.0), np.array([0.3205, 0.3155, 78.6600])), - (('10RP', 9.0, 4.0), np.array([0.3400, 0.3140, 78.6600])), - (('10RP', 9.0, 6.0), np.array([0.3590, 0.3118, 78.6600])), - (('10RP', 9.0, 8.0), np.array([0.3760, 0.3100, 78.6600])), - (('10RP', 9.0, 10.0), np.array([0.3940, 0.3060, 78.6600])), - (('10RP', 9.0, 12.0), np.array([0.4140, 0.3020, 78.6600])), - (('10RP', 9.0, 14.0), np.array([0.4310, 0.2980, 78.6600])), - (('2.5R', 9.0, 2.0), np.array([0.3220, 0.3168, 78.6600])), - (('2.5R', 9.0, 4.0), np.array([0.3445, 0.3179, 78.6600])), - (('2.5R', 9.0, 6.0), np.array([0.3665, 0.3183, 78.6600])), - (('2.5R', 9.0, 8.0), np.array([0.3840, 0.3180, 78.6600])), - (('2.5R', 9.0, 10.0), np.array([0.4060, 0.3160, 78.6600])), - (('2.5R', 9.0, 12.0), np.array([0.4290, 0.3140, 78.6600])), - (('2.5R', 9.0, 14.0), np.array([0.4480, 0.3110, 78.6600])), - (('5R', 9.0, 2.0), np.array([0.3240, 0.3188, 78.6600])), - (('5R', 9.0, 4.0), np.array([0.3495, 0.3226, 78.6600])), - (('5R', 9.0, 6.0), np.array([0.3734, 0.3256, 78.6600])), - (('5R', 9.0, 8.0), np.array([0.3950, 0.3260, 78.6600])), - (('5R', 9.0, 10.0), np.array([0.4180, 0.3260, 78.6600])), - (('5R', 9.0, 12.0), np.array([0.4440, 0.3250, 78.6600])), - (('7.5R', 9.0, 2.0), np.array([0.3263, 0.3210, 78.6600])), - (('7.5R', 9.0, 4.0), np.array([0.3551, 0.3283, 78.6600])), - (('7.5R', 9.0, 6.0), np.array([0.3812, 0.3348, 78.6600])), - (('7.5R', 9.0, 8.0), np.array([0.4070, 0.3380, 78.6600])), - (('7.5R', 9.0, 10.0), np.array([0.4310, 0.3400, 78.6600])), - (('7.5R', 9.0, 12.0), np.array([0.4570, 0.3400, 78.6600])), - (('7.5R', 9.0, 14.0), np.array([0.4800, 0.3400, 78.6600])), - (('10R', 9.0, 2.0), np.array([0.3284, 0.3233, 78.6600])), - (('10R', 9.0, 4.0), np.array([0.3600, 0.3348, 78.6600])), - (('10R', 9.0, 6.0), np.array([0.3880, 0.3439, 78.6600])), - (('10R', 9.0, 8.0), np.array([0.4160, 0.3500, 78.6600])), - (('10R', 9.0, 10.0), np.array([0.4410, 0.3560, 78.6600])), - (('10R', 9.0, 12.0), np.array([0.4650, 0.3610, 78.6600])), - (('10R', 9.0, 14.0), np.array([0.4900, 0.3640, 78.6600])), - (('10R', 9.0, 16.0), np.array([0.5100, 0.3680, 78.6600])), - (('2.5YR', 9.0, 2.0), np.array([0.3320, 0.3273, 78.6600])), - (('2.5YR', 9.0, 4.0), np.array([0.3641, 0.3422, 78.6600])), - (('2.5YR', 9.0, 6.0), np.array([0.3927, 0.3550, 78.6600])), - (('2.5YR', 9.0, 8.0), np.array([0.4220, 0.3650, 78.6600])), - (('2.5YR', 9.0, 10.0), np.array([0.4460, 0.3730, 78.6600])), - (('2.5YR', 9.0, 12.0), np.array([0.4690, 0.3800, 78.6600])), - (('2.5YR', 9.0, 14.0), np.array([0.4920, 0.3870, 78.6600])), - (('2.5YR', 9.0, 16.0), np.array([0.5130, 0.3920, 78.6600])), - (('2.5YR', 9.0, 18.0), np.array([0.5320, 0.3960, 78.6600])), - (('5YR', 9.0, 2.0), np.array([0.3353, 0.3325, 78.6600])), - (('5YR', 9.0, 4.0), np.array([0.3668, 0.3509, 78.6600])), - (('5YR', 9.0, 6.0), np.array([0.3948, 0.3659, 78.6600])), - (('5YR', 9.0, 8.0), np.array([0.4220, 0.3790, 78.6600])), - (('5YR', 9.0, 10.0), np.array([0.4480, 0.3900, 78.6600])), - (('5YR', 9.0, 12.0), np.array([0.4700, 0.4000, 78.6600])), - (('5YR', 9.0, 14.0), np.array([0.4910, 0.4080, 78.6600])), - (('5YR', 9.0, 16.0), np.array([0.5100, 0.4160, 78.6600])), - (('5YR', 9.0, 18.0), np.array([0.5250, 0.4210, 78.6600])), - (('5YR', 9.0, 20.0), np.array([0.5410, 0.4260, 78.6600])), - (('5YR', 9.0, 22.0), np.array([0.5500, 0.4300, 78.6600])), - (('5YR', 9.0, 24.0), np.array([0.5590, 0.4330, 78.6600])), - (('5YR', 9.0, 26.0), np.array([0.5670, 0.4350, 78.6600])), - (('7.5YR', 9.0, 2.0), np.array([0.3380, 0.3377, 78.6600])), - (('7.5YR', 9.0, 4.0), np.array([0.3679, 0.3585, 78.6600])), - (('7.5YR', 9.0, 6.0), np.array([0.3950, 0.3763, 78.6600])), - (('7.5YR', 9.0, 8.0), np.array([0.4220, 0.3930, 78.6600])), - (('7.5YR', 9.0, 10.0), np.array([0.4470, 0.4070, 78.6600])), - (('7.5YR', 9.0, 12.0), np.array([0.4680, 0.4180, 78.6600])), - (('7.5YR', 9.0, 14.0), np.array([0.4860, 0.4280, 78.6600])), - (('7.5YR', 9.0, 16.0), np.array([0.5030, 0.4360, 78.6600])), - (('7.5YR', 9.0, 18.0), np.array([0.5170, 0.4440, 78.6600])), - (('7.5YR', 9.0, 20.0), np.array([0.5290, 0.4500, 78.6600])), - (('7.5YR', 9.0, 22.0), np.array([0.5370, 0.4540, 78.6600])), - (('7.5YR', 9.0, 24.0), np.array([0.5450, 0.4590, 78.6600])), - (('7.5YR', 9.0, 26.0), np.array([0.5530, 0.4630, 78.6600])), - (('10YR', 9.0, 2.0), np.array([0.3392, 0.3430, 78.6600])), - (('10YR', 9.0, 4.0), np.array([0.3677, 0.3668, 78.6600])), - (('10YR', 9.0, 6.0), np.array([0.3941, 0.3877, 78.6600])), - (('10YR', 9.0, 8.0), np.array([0.4199, 0.4069, 78.6600])), - (('10YR', 9.0, 10.0), np.array([0.4440, 0.4240, 78.6600])), - (('10YR', 9.0, 12.0), np.array([0.4630, 0.4360, 78.6600])), - (('10YR', 9.0, 14.0), np.array([0.4800, 0.4470, 78.6600])), - (('10YR', 9.0, 16.0), np.array([0.4950, 0.4550, 78.6600])), - (('10YR', 9.0, 18.0), np.array([0.5060, 0.4620, 78.6600])), - (('10YR', 9.0, 20.0), np.array([0.5160, 0.4680, 78.6600])), - (('10YR', 9.0, 22.0), np.array([0.5240, 0.4730, 78.6600])), - (('10YR', 9.0, 24.0), np.array([0.5320, 0.4780, 78.6600])), - (('2.5Y', 9.0, 2.0), np.array([0.3390, 0.3472, 78.6600])), - (('2.5Y', 9.0, 4.0), np.array([0.3655, 0.3738, 78.6600])), - (('2.5Y', 9.0, 6.0), np.array([0.3910, 0.3972, 78.6600])), - (('2.5Y', 9.0, 8.0), np.array([0.4154, 0.4186, 78.6600])), - (('2.5Y', 9.0, 10.0), np.array([0.4370, 0.4369, 78.6600])), - (('2.5Y', 9.0, 12.0), np.array([0.4569, 0.4527, 78.6600])), - (('2.5Y', 9.0, 14.0), np.array([0.4730, 0.4650, 78.6600])), - (('2.5Y', 9.0, 16.0), np.array([0.4860, 0.4760, 78.6600])), - (('2.5Y', 9.0, 18.0), np.array([0.4940, 0.4830, 78.6600])), - (('2.5Y', 9.0, 20.0), np.array([0.5010, 0.4880, 78.6600])), - (('2.5Y', 9.0, 22.0), np.array([0.5090, 0.4940, 78.6600])), - (('2.5Y', 9.0, 24.0), np.array([0.5150, 0.4990, 78.6600])), - (('5Y', 9.0, 2.0), np.array([0.3378, 0.3504, 78.6600])), - (('5Y', 9.0, 4.0), np.array([0.3621, 0.3799, 78.6600])), - (('5Y', 9.0, 6.0), np.array([0.3858, 0.4071, 78.6600])), - (('5Y', 9.0, 8.0), np.array([0.4080, 0.4319, 78.6600])), - (('5Y', 9.0, 10.0), np.array([0.4275, 0.4529, 78.6600])), - (('5Y', 9.0, 12.0), np.array([0.4455, 0.4719, 78.6600])), - (('5Y', 9.0, 14.0), np.array([0.4602, 0.4869, 78.6600])), - (('5Y', 9.0, 16.0), np.array([0.4711, 0.4977, 78.6600])), - (('5Y', 9.0, 18.0), np.array([0.4782, 0.5049, 78.6600])), - (('5Y', 9.0, 20.0), np.array([0.4830, 0.5092, 78.6600])), - (('5Y', 9.0, 22.0), np.array([0.4890, 0.5150, 78.6600])), - (('5Y', 9.0, 24.0), np.array([0.4950, 0.5220, 78.6600])), - (('7.5Y', 9.0, 2.0), np.array([0.3365, 0.3527, 78.6600])), - (('7.5Y', 9.0, 4.0), np.array([0.3591, 0.3832, 78.6600])), - (('7.5Y', 9.0, 6.0), np.array([0.3811, 0.4123, 78.6600])), - (('7.5Y', 9.0, 8.0), np.array([0.4019, 0.4392, 78.6600])), - (('7.5Y', 9.0, 10.0), np.array([0.4201, 0.4622, 78.6600])), - (('7.5Y', 9.0, 12.0), np.array([0.4369, 0.4829, 78.6600])), - (('7.5Y', 9.0, 14.0), np.array([0.4503, 0.4993, 78.6600])), - (('7.5Y', 9.0, 16.0), np.array([0.4595, 0.5104, 78.6600])), - (('7.5Y', 9.0, 18.0), np.array([0.4663, 0.5188, 78.6600])), - (('7.5Y', 9.0, 20.0), np.array([0.4710, 0.5230, 78.6600])), - (('7.5Y', 9.0, 22.0), np.array([0.4760, 0.5280, 78.6600])), - (('10Y', 10.0, 2.0), np.array([0.3340, 0.3520, 102.5700])), - (('10Y', 10.0, 4.0), np.array([0.3550, 0.3820, 102.5700])), - (('10Y', 10.0, 6.0), np.array([0.3710, 0.4090, 102.5700])), - (('10Y', 10.0, 8.0), np.array([0.3900, 0.4370, 102.5700])), - (('10Y', 10.0, 10.0), np.array([0.4050, 0.4600, 102.5700])), - (('10Y', 10.0, 12.0), np.array([0.4200, 0.4810, 102.5700])), - (('10Y', 10.0, 14.0), np.array([0.4330, 0.5020, 102.5700])), - (('10Y', 10.0, 16.0), np.array([0.4420, 0.5150, 102.5700])), - (('10Y', 10.0, 18.0), np.array([0.4500, 0.5270, 102.5700])), - (('10Y', 10.0, 20.0), np.array([0.4550, 0.5340, 102.5700])), - (('10Y', 10.0, 22.0), np.array([0.4600, 0.5410, 102.5700])), - (('2.5GY', 10.0, 2.0), np.array([0.3320, 0.3540, 102.5700])), - (('2.5GY', 10.0, 4.0), np.array([0.3480, 0.3840, 102.5700])), - (('2.5GY', 10.0, 6.0), np.array([0.3650, 0.4130, 102.5700])), - (('2.5GY', 10.0, 8.0), np.array([0.3800, 0.4420, 102.5700])), - (('2.5GY', 10.0, 10.0), np.array([0.3930, 0.4660, 102.5700])), - (('2.5GY', 10.0, 12.0), np.array([0.4060, 0.4920, 102.5700])), - (('2.5GY', 10.0, 14.0), np.array([0.4160, 0.5140, 102.5700])), - (('2.5GY', 10.0, 16.0), np.array([0.4240, 0.5290, 102.5700])), - (('2.5GY', 10.0, 18.0), np.array([0.4320, 0.5460, 102.5700])), - (('2.5GY', 10.0, 20.0), np.array([0.4360, 0.5520, 102.5700])), - (('2.5GY', 10.0, 22.0), np.array([0.4400, 0.5600, 102.5700])), - (('5GY', 10.0, 2.0), np.array([0.3280, 0.3540, 102.5700])), - (('5GY', 10.0, 4.0), np.array([0.3430, 0.3840, 102.5700])), - (('5GY', 10.0, 6.0), np.array([0.3560, 0.4140, 102.5700])), - (('5GY', 10.0, 8.0), np.array([0.3700, 0.4460, 102.5700])), - (('5GY', 10.0, 10.0), np.array([0.3800, 0.4700, 102.5700])), - (('5GY', 10.0, 12.0), np.array([0.3910, 0.4970, 102.5700])), - (('5GY', 10.0, 14.0), np.array([0.3980, 0.5180, 102.5700])), - (('5GY', 10.0, 16.0), np.array([0.4050, 0.5380, 102.5700])), - (('5GY', 10.0, 18.0), np.array([0.4100, 0.5600, 102.5700])), - (('5GY', 10.0, 20.0), np.array([0.4130, 0.5720, 102.5700])), - (('5GY', 10.0, 22.0), np.array([0.4160, 0.5800, 102.5700])), - (('7.5GY', 10.0, 2.0), np.array([0.3200, 0.3510, 102.5700])), - (('7.5GY', 10.0, 4.0), np.array([0.3270, 0.3780, 102.5700])), - (('7.5GY', 10.0, 6.0), np.array([0.3340, 0.4100, 102.5700])), - (('7.5GY', 10.0, 8.0), np.array([0.3420, 0.4390, 102.5700])), - (('7.5GY', 10.0, 10.0), np.array([0.3470, 0.4700, 102.5700])), - (('7.5GY', 10.0, 12.0), np.array([0.3510, 0.4930, 102.5700])), - (('7.5GY', 10.0, 14.0), np.array([0.3540, 0.5180, 102.5700])), - (('7.5GY', 10.0, 16.0), np.array([0.3570, 0.5450, 102.5700])), - (('7.5GY', 10.0, 18.0), np.array([0.3600, 0.5710, 102.5700])), - (('7.5GY', 10.0, 20.0), np.array([0.3610, 0.5920, 102.5700])), - (('7.5GY', 10.0, 22.0), np.array([0.3600, 0.6080, 102.5700])), - (('10GY', 10.0, 2.0), np.array([0.3120, 0.3460, 102.5700])), - (('10GY', 10.0, 4.0), np.array([0.3140, 0.3700, 102.5700])), - (('10GY', 10.0, 6.0), np.array([0.3140, 0.4000, 102.5700])), - (('10GY', 10.0, 8.0), np.array([0.3140, 0.4230, 102.5700])), - (('10GY', 10.0, 10.0), np.array([0.3140, 0.4540, 102.5700])), - (('10GY', 10.0, 12.0), np.array([0.3140, 0.4740, 102.5700])), - (('10GY', 10.0, 14.0), np.array([0.3130, 0.5000, 102.5700])), - (('10GY', 10.0, 16.0), np.array([0.3100, 0.5270, 102.5700])), - (('10GY', 10.0, 18.0), np.array([0.3070, 0.5560, 102.5700])), - (('10GY', 10.0, 20.0), np.array([0.3030, 0.5800, 102.5700])), - (('10GY', 10.0, 22.0), np.array([0.2990, 0.6000, 102.5700])), - (('2.5G', 10.0, 2.0), np.array([0.3060, 0.3420, 102.5700])), - (('2.5G', 10.0, 4.0), np.array([0.3030, 0.3610, 102.5700])), - (('2.5G', 10.0, 6.0), np.array([0.2980, 0.3850, 102.5700])), - (('2.5G', 10.0, 8.0), np.array([0.2930, 0.4040, 102.5700])), - (('2.5G', 10.0, 10.0), np.array([0.2860, 0.4270, 102.5700])), - (('2.5G', 10.0, 12.0), np.array([0.2820, 0.4440, 102.5700])), - (('2.5G', 10.0, 14.0), np.array([0.2760, 0.4670, 102.5700])), - (('2.5G', 10.0, 16.0), np.array([0.2700, 0.4900, 102.5700])), - (('2.5G', 10.0, 18.0), np.array([0.2620, 0.5170, 102.5700])), - (('2.5G', 10.0, 20.0), np.array([0.2540, 0.5430, 102.5700])), - (('2.5G', 10.0, 22.0), np.array([0.2480, 0.5630, 102.5700])), - (('5G', 10.0, 2.0), np.array([0.3010, 0.3370, 102.5700])), - (('5G', 10.0, 4.0), np.array([0.2930, 0.3530, 102.5700])), - (('5G', 10.0, 6.0), np.array([0.2830, 0.3690, 102.5700])), - (('5G', 10.0, 8.0), np.array([0.2740, 0.3840, 102.5700])), - (('5G', 10.0, 10.0), np.array([0.2650, 0.3980, 102.5700])), - (('5G', 10.0, 12.0), np.array([0.2560, 0.4120, 102.5700])), - (('5G', 10.0, 14.0), np.array([0.2450, 0.4260, 102.5700])), - (('5G', 10.0, 16.0), np.array([0.2350, 0.4400, 102.5700])), - (('5G', 10.0, 18.0), np.array([0.2220, 0.4570, 102.5700])), - (('7.5G', 10.0, 2.0), np.array([0.2980, 0.3330, 102.5700])), - (('7.5G', 10.0, 4.0), np.array([0.2880, 0.3460, 102.5700])), - (('7.5G', 10.0, 6.0), np.array([0.2770, 0.3600, 102.5700])), - (('7.5G', 10.0, 8.0), np.array([0.2660, 0.3740, 102.5700])), - (('7.5G', 10.0, 10.0), np.array([0.2560, 0.3850, 102.5700])), - (('7.5G', 10.0, 12.0), np.array([0.2460, 0.3970, 102.5700])), - (('7.5G', 10.0, 14.0), np.array([0.2350, 0.4090, 102.5700])), - (('7.5G', 10.0, 16.0), np.array([0.2240, 0.4200, 102.5700])), - (('10G', 10.0, 2.0), np.array([0.2960, 0.3310, 102.5700])), - (('10G', 10.0, 4.0), np.array([0.2840, 0.3410, 102.5700])), - (('10G', 10.0, 6.0), np.array([0.2710, 0.3510, 102.5700])), - (('10G', 10.0, 8.0), np.array([0.2580, 0.3610, 102.5700])), - (('10G', 10.0, 10.0), np.array([0.2480, 0.3690, 102.5700])), - (('10G', 10.0, 12.0), np.array([0.2350, 0.3780, 102.5700])), - (('10G', 10.0, 14.0), np.array([0.2230, 0.3870, 102.5700])), - (('2.5BG', 10.0, 2.0), np.array([0.2940, 0.3270, 102.5700])), - (('2.5BG', 10.0, 4.0), np.array([0.2800, 0.3350, 102.5700])), - (('2.5BG', 10.0, 6.0), np.array([0.2650, 0.3430, 102.5700])), - (('2.5BG', 10.0, 8.0), np.array([0.2510, 0.3500, 102.5700])), - (('2.5BG', 10.0, 10.0), np.array([0.2410, 0.3570, 102.5700])), - (('2.5BG', 10.0, 12.0), np.array([0.2270, 0.3620, 102.5700])), - (('2.5BG', 10.0, 14.0), np.array([0.2130, 0.3680, 102.5700])), - (('5BG', 10.0, 2.0), np.array([0.2930, 0.3230, 102.5700])), - (('5BG', 10.0, 4.0), np.array([0.2770, 0.3300, 102.5700])), - (('5BG', 10.0, 6.0), np.array([0.2620, 0.3350, 102.5700])), - (('5BG', 10.0, 8.0), np.array([0.2460, 0.3400, 102.5700])), - (('5BG', 10.0, 10.0), np.array([0.2330, 0.3420, 102.5700])), - (('5BG', 10.0, 12.0), np.array([0.2200, 0.3450, 102.5700])), - (('5BG', 10.0, 14.0), np.array([0.2030, 0.3480, 102.5700])), - (('7.5BG', 10.0, 2.0), np.array([0.2930, 0.3180, 102.5700])), - (('7.5BG', 10.0, 4.0), np.array([0.2730, 0.3200, 102.5700])), - (('7.5BG', 10.0, 6.0), np.array([0.2560, 0.3210, 102.5700])), - (('7.5BG', 10.0, 8.0), np.array([0.2380, 0.3220, 102.5700])), - (('7.5BG', 10.0, 10.0), np.array([0.2240, 0.3220, 102.5700])), - (('7.5BG', 10.0, 12.0), np.array([0.2090, 0.3220, 102.5700])), - (('10BG', 10.0, 2.0), np.array([0.2930, 0.3150, 102.5700])), - (('10BG', 10.0, 4.0), np.array([0.2700, 0.3130, 102.5700])), - (('10BG', 10.0, 6.0), np.array([0.2510, 0.3110, 102.5700])), - (('10BG', 10.0, 8.0), np.array([0.2340, 0.3100, 102.5700])), - (('10BG', 10.0, 10.0), np.array([0.2190, 0.3080, 102.5700])), - (('10BG', 10.0, 12.0), np.array([0.2030, 0.3050, 102.5700])), - (('2.5B', 10.0, 2.0), np.array([0.2930, 0.3130, 102.5700])), - (('2.5B', 10.0, 4.0), np.array([0.2700, 0.3080, 102.5700])), - (('2.5B', 10.0, 6.0), np.array([0.2490, 0.3040, 102.5700])), - (('2.5B', 10.0, 8.0), np.array([0.2320, 0.2990, 102.5700])), - (('2.5B', 10.0, 10.0), np.array([0.2150, 0.2930, 102.5700])), - (('5B', 10.0, 2.0), np.array([0.2940, 0.3120, 102.5700])), - (('5B', 10.0, 4.0), np.array([0.2680, 0.3020, 102.5700])), - (('5B', 10.0, 6.0), np.array([0.2480, 0.2960, 102.5700])), - (('5B', 10.0, 8.0), np.array([0.2300, 0.2880, 102.5700])), - (('7.5B', 10.0, 2.0), np.array([0.2950, 0.3100, 102.5700])), - (('7.5B', 10.0, 4.0), np.array([0.2690, 0.2980, 102.5700])), - (('7.5B', 10.0, 6.0), np.array([0.2490, 0.2870, 102.5700])), - (('10B', 10.0, 2.0), np.array([0.2960, 0.3090, 102.5700])), - (('10B', 10.0, 4.0), np.array([0.2720, 0.2940, 102.5700])), - (('10B', 10.0, 6.0), np.array([0.2510, 0.2800, 102.5700])), - (('2.5PB', 10.0, 2.0), np.array([0.2980, 0.3070, 102.5700])), - (('2.5PB', 10.0, 4.0), np.array([0.2780, 0.2900, 102.5700])), - (('2.5PB', 10.0, 6.0), np.array([0.2600, 0.2740, 102.5700])), - (('5PB', 10.0, 2.0), np.array([0.2990, 0.3070, 102.5700])), - (('5PB', 10.0, 4.0), np.array([0.2820, 0.2880, 102.5700])), - (('5PB', 10.0, 6.0), np.array([0.2650, 0.2700, 102.5700])), - (('7.5PB', 10.0, 2.0), np.array([0.3020, 0.3070, 102.5700])), - (('7.5PB', 10.0, 4.0), np.array([0.2860, 0.2860, 102.5700])), - (('7.5PB', 10.0, 6.0), np.array([0.2730, 0.2680, 102.5700])), - (('10PB', 10.0, 2.0), np.array([0.3030, 0.3070, 102.5700])), - (('10PB', 10.0, 4.0), np.array([0.2910, 0.2860, 102.5700])), - (('10PB', 10.0, 6.0), np.array([0.2800, 0.2680, 102.5700])), - (('2.5P', 10.0, 2.0), np.array([0.3050, 0.3070, 102.5700])), - (('2.5P', 10.0, 4.0), np.array([0.2960, 0.2880, 102.5700])), - (('2.5P', 10.0, 6.0), np.array([0.2890, 0.2700, 102.5700])), - (('5P', 10.0, 2.0), np.array([0.3070, 0.3070, 102.5700])), - (('5P', 10.0, 4.0), np.array([0.3020, 0.2890, 102.5700])), - (('5P', 10.0, 6.0), np.array([0.2980, 0.2740, 102.5700])), - (('5P', 10.0, 8.0), np.array([0.2940, 0.2580, 102.5700])), - (('7.5P', 10.0, 2.0), np.array([0.3100, 0.3080, 102.5700])), - (('7.5P', 10.0, 4.0), np.array([0.3110, 0.2940, 102.5700])), - (('7.5P', 10.0, 6.0), np.array([0.3110, 0.2800, 102.5700])), - (('7.5P', 10.0, 8.0), np.array([0.3110, 0.2660, 102.5700])), - (('10P', 10.0, 2.0), np.array([0.3130, 0.3090, 102.5700])), - (('10P', 10.0, 4.0), np.array([0.3170, 0.2970, 102.5700])), - (('10P', 10.0, 6.0), np.array([0.3210, 0.2840, 102.5700])), - (('10P', 10.0, 8.0), np.array([0.3260, 0.2730, 102.5700])), - (('10P', 10.0, 10.0), np.array([0.3280, 0.2640, 102.5700])), - (('2.5RP', 10.0, 2.0), np.array([0.3150, 0.3110, 102.5700])), - (('2.5RP', 10.0, 4.0), np.array([0.3230, 0.3010, 102.5700])), - (('2.5RP', 10.0, 6.0), np.array([0.3320, 0.2910, 102.5700])), - (('2.5RP', 10.0, 8.0), np.array([0.3390, 0.2820, 102.5700])), - (('2.5RP', 10.0, 10.0), np.array([0.3440, 0.2750, 102.5700])), - (('5RP', 10.0, 2.0), np.array([0.3180, 0.3130, 102.5700])), - (('5RP', 10.0, 4.0), np.array([0.3320, 0.3070, 102.5700])), - (('5RP', 10.0, 6.0), np.array([0.3450, 0.3000, 102.5700])), - (('5RP', 10.0, 8.0), np.array([0.3550, 0.2930, 102.5700])), - (('5RP', 10.0, 10.0), np.array([0.3630, 0.2880, 102.5700])), - (('7.5RP', 10.0, 2.0), np.array([0.3200, 0.3140, 102.5700])), - (('7.5RP', 10.0, 4.0), np.array([0.3350, 0.3100, 102.5700])), - (('7.5RP', 10.0, 6.0), np.array([0.3510, 0.3050, 102.5700])), - (('7.5RP', 10.0, 8.0), np.array([0.3650, 0.3010, 102.5700])), - (('10RP', 10.0, 2.0), np.array([0.3220, 0.3160, 102.5700])), - (('10RP', 10.0, 4.0), np.array([0.3420, 0.3150, 102.5700])), - (('10RP', 10.0, 6.0), np.array([0.3610, 0.3140, 102.5700])), - (('10RP', 10.0, 8.0), np.array([0.3780, 0.3110, 102.5700])), - (('2.5R', 10.0, 2.0), np.array([0.3240, 0.3180, 102.5700])), - (('2.5R', 10.0, 4.0), np.array([0.3450, 0.3180, 102.5700])), - (('2.5R', 10.0, 6.0), np.array([0.3660, 0.3180, 102.5700])), - (('2.5R', 10.0, 8.0), np.array([0.3850, 0.3180, 102.5700])), - (('5R', 10.0, 2.0), np.array([0.3260, 0.3200, 102.5700])), - (('5R', 10.0, 4.0), np.array([0.3480, 0.3220, 102.5700])), - (('5R', 10.0, 6.0), np.array([0.3720, 0.3250, 102.5700])), - (('5R', 10.0, 8.0), np.array([0.3920, 0.3260, 102.5700])), - (('7.5R', 10.0, 2.0), np.array([0.3290, 0.3220, 102.5700])), - (('7.5R', 10.0, 4.0), np.array([0.3540, 0.3290, 102.5700])), - (('7.5R', 10.0, 6.0), np.array([0.3800, 0.3340, 102.5700])), - (('7.5R', 10.0, 8.0), np.array([0.4020, 0.3370, 102.5700])), - (('10R', 10.0, 2.0), np.array([0.3320, 0.3260, 102.5700])), - (('10R', 10.0, 4.0), np.array([0.3590, 0.3360, 102.5700])), - (('10R', 10.0, 6.0), np.array([0.3860, 0.3440, 102.5700])), - (('10R', 10.0, 8.0), np.array([0.4110, 0.3490, 102.5700])), - (('2.5YR', 10.0, 2.0), np.array([0.3340, 0.3290, 102.5700])), - (('2.5YR', 10.0, 4.0), np.array([0.3620, 0.3420, 102.5700])), - (('2.5YR', 10.0, 6.0), np.array([0.3890, 0.3540, 102.5700])), - (('2.5YR', 10.0, 8.0), np.array([0.4150, 0.3630, 102.5700])), - (('5YR', 10.0, 2.0), np.array([0.3360, 0.3330, 102.5700])), - (('5YR', 10.0, 4.0), np.array([0.3640, 0.3500, 102.5700])), - (('5YR', 10.0, 6.0), np.array([0.3910, 0.3650, 102.5700])), - (('5YR', 10.0, 8.0), np.array([0.4160, 0.3780, 102.5700])), - (('5YR', 10.0, 10.0), np.array([0.4400, 0.3880, 102.5700])), - (('7.5YR', 10.0, 2.0), np.array([0.3370, 0.3380, 102.5700])), - (('7.5YR', 10.0, 4.0), np.array([0.3660, 0.3570, 102.5700])), - (('7.5YR', 10.0, 6.0), np.array([0.3910, 0.3750, 102.5700])), - (('7.5YR', 10.0, 8.0), np.array([0.4150, 0.3910, 102.5700])), - (('7.5YR', 10.0, 10.0), np.array([0.4390, 0.4050, 102.5700])), - (('7.5YR', 10.0, 12.0), np.array([0.4550, 0.4160, 102.5700])), - (('10YR', 10.0, 2.0), np.array([0.3380, 0.3430, 102.5700])), - (('10YR', 10.0, 4.0), np.array([0.3660, 0.3670, 102.5700])), - (('10YR', 10.0, 6.0), np.array([0.3890, 0.3850, 102.5700])), - (('10YR', 10.0, 8.0), np.array([0.4120, 0.4020, 102.5700])), - (('10YR', 10.0, 10.0), np.array([0.4350, 0.4200, 102.5700])), - (('10YR', 10.0, 12.0), np.array([0.4520, 0.4310, 102.5700])), - (('10YR', 10.0, 14.0), np.array([0.4670, 0.4420, 102.5700])), - (('10YR', 10.0, 16.0), np.array([0.4830, 0.4530, 102.5700])), - (('2.5Y', 10.0, 2.0), np.array([0.3370, 0.3470, 102.5700])), - (('2.5Y', 10.0, 4.0), np.array([0.3630, 0.3730, 102.5700])), - (('2.5Y', 10.0, 6.0), np.array([0.3850, 0.3950, 102.5700])), - (('2.5Y', 10.0, 8.0), np.array([0.4070, 0.4130, 102.5700])), - (('2.5Y', 10.0, 10.0), np.array([0.4280, 0.4310, 102.5700])), - (('2.5Y', 10.0, 12.0), np.array([0.4460, 0.4460, 102.5700])), - (('2.5Y', 10.0, 14.0), np.array([0.4630, 0.4600, 102.5700])), - (('2.5Y', 10.0, 16.0), np.array([0.4760, 0.4700, 102.5700])), - (('2.5Y', 10.0, 18.0), np.array([0.4870, 0.4770, 102.5700])), - (('2.5Y', 10.0, 20.0), np.array([0.4960, 0.4860, 102.5700])), - (('2.5Y', 10.0, 22.0), np.array([0.5030, 0.4900, 102.5700])), - (('5Y', 10.0, 2.0), np.array([0.3360, 0.3480, 102.5700])), - (('5Y', 10.0, 4.0), np.array([0.3600, 0.3770, 102.5700])), - (('5Y', 10.0, 6.0), np.array([0.3810, 0.4020, 102.5700])), - (('5Y', 10.0, 8.0), np.array([0.4000, 0.4250, 102.5700])), - (('5Y', 10.0, 10.0), np.array([0.4180, 0.4460, 102.5700])), - (('5Y', 10.0, 12.0), np.array([0.4350, 0.4650, 102.5700])), - (('5Y', 10.0, 14.0), np.array([0.4500, 0.4810, 102.5700])), - (('5Y', 10.0, 16.0), np.array([0.4620, 0.4940, 102.5700])), - (('5Y', 10.0, 18.0), np.array([0.4720, 0.5020, 102.5700])), - (('5Y', 10.0, 20.0), np.array([0.4780, 0.5080, 102.5700])), - (('5Y', 10.0, 22.0), np.array([0.4840, 0.5140, 102.5700])), - (('7.5Y', 10.0, 2.0), np.array([0.3350, 0.3510, 102.5700])), - (('7.5Y', 10.0, 4.0), np.array([0.3570, 0.3800, 102.5700])), - (('7.5Y', 10.0, 6.0), np.array([0.3760, 0.4070, 102.5700])), - (('7.5Y', 10.0, 8.0), np.array([0.3950, 0.4320, 102.5700])), - (('7.5Y', 10.0, 10.0), np.array([0.4120, 0.4540, 102.5700])), - (('7.5Y', 10.0, 12.0), np.array([0.4270, 0.4730, 102.5700])), - (('7.5Y', 10.0, 14.0), np.array([0.4420, 0.4920, 102.5700])), - (('7.5Y', 10.0, 16.0), np.array([0.4530, 0.5050, 102.5700])), - (('7.5Y', 10.0, 18.0), np.array([0.4620, 0.5150, 102.5700])), - (('7.5Y', 10.0, 20.0), np.array([0.4670, 0.5210, 102.5700])), - (('7.5Y', 10.0, 22.0), np.array([0.4720, 0.5280, 102.5700]))) +MUNSELL_COLOURS_ALL: Tuple = ( + (("2.5GY", 0.2, 2.0), np.array([0.7130, 1.4140, 0.2370])), + (("5GY", 0.2, 2.0), np.array([0.4490, 1.1450, 0.2370])), + (("7.5GY", 0.2, 2.0), np.array([0.2620, 0.8370, 0.2370])), + (("7.5GY", 0.2, 4.0), np.array([-0.0780, 2.1600, 0.2370])), + (("10GY", 0.2, 2.0), np.array([0.1850, 0.6760, 0.2370])), + (("10GY", 0.2, 4.0), np.array([-0.2570, 1.2330, 0.2370])), + (("2.5G", 0.2, 2.0), np.array([0.1440, 0.5840, 0.2370])), + (("2.5G", 0.2, 4.0), np.array([-0.2350, 0.8910, 0.2370])), + (("5G", 0.2, 2.0), np.array([0.1170, 0.5160, 0.2370])), + (("5G", 0.2, 4.0), np.array([-0.2090, 0.7190, 0.2370])), + (("7.5G", 0.2, 2.0), np.array([0.0970, 0.4580, 0.2370])), + (("7.5G", 0.2, 4.0), np.array([-0.1810, 0.5750, 0.2370])), + (("10G", 0.2, 2.0), np.array([0.0800, 0.3970, 0.2370])), + (("10G", 0.2, 4.0), np.array([-0.1370, 0.4250, 0.2370])), + (("2.5BG", 0.2, 2.0), np.array([0.0680, 0.3320, 0.2370])), + (("2.5BG", 0.2, 4.0), np.array([-0.1000, 0.3250, 0.2370])), + (("5BG", 0.2, 2.0), np.array([0.0660, 0.2610, 0.2370])), + (("5BG", 0.2, 4.0), np.array([-0.0480, 0.2230, 0.2370])), + (("7.5BG", 0.2, 2.0), np.array([0.0720, 0.2260, 0.2370])), + (("7.5BG", 0.2, 4.0), np.array([-0.0160, 0.1810, 0.2370])), + (("10BG", 0.2, 2.0), np.array([0.0850, 0.1950, 0.2370])), + (("10BG", 0.2, 4.0), np.array([0.0150, 0.1480, 0.2370])), + (("2.5B", 0.2, 2.0), np.array([0.0970, 0.1770, 0.2370])), + (("2.5B", 0.2, 4.0), np.array([0.0390, 0.1280, 0.2370])), + (("5B", 0.2, 2.0), np.array([0.1110, 0.1640, 0.2370])), + (("5B", 0.2, 4.0), np.array([0.0630, 0.1140, 0.2370])), + (("7.5B", 0.2, 2.0), np.array([0.1210, 0.1570, 0.2370])), + (("7.5B", 0.2, 4.0), np.array([0.0740, 0.1090, 0.2370])), + (("10B", 0.2, 2.0), np.array([0.1330, 0.1490, 0.2370])), + (("10B", 0.2, 4.0), np.array([0.0880, 0.1020, 0.2370])), + (("10B", 0.2, 6.0), np.array([0.0630, 0.0740, 0.2370])), + (("2.5PB", 0.2, 2.0), np.array([0.1470, 0.1430, 0.2370])), + (("2.5PB", 0.2, 4.0), np.array([0.1090, 0.0940, 0.2370])), + (("2.5PB", 0.2, 6.0), np.array([0.0870, 0.0670, 0.2370])), + (("5PB", 0.2, 2.0), np.array([0.1650, 0.1360, 0.2370])), + (("5PB", 0.2, 4.0), np.array([0.1330, 0.0900, 0.2370])), + (("5PB", 0.2, 6.0), np.array([0.1150, 0.0620, 0.2370])), + (("5PB", 0.2, 8.0), np.array([0.1040, 0.0450, 0.2370])), + (("5PB", 0.2, 10.0), np.array([0.0960, 0.0320, 0.2370])), + (("5PB", 0.2, 12.0), np.array([0.0860, 0.0180, 0.2370])), + (("7.5PB", 0.2, 2.0), np.array([0.1920, 0.1300, 0.2370])), + (("7.5PB", 0.2, 4.0), np.array([0.1710, 0.0870, 0.2370])), + (("7.5PB", 0.2, 6.0), np.array([0.1590, 0.0610, 0.2370])), + (("7.5PB", 0.2, 8.0), np.array([0.1520, 0.0450, 0.2370])), + (("7.5PB", 0.2, 10.0), np.array([0.1480, 0.0340, 0.2370])), + (("7.5PB", 0.2, 12.0), np.array([0.1450, 0.0250, 0.2370])), + (("7.5PB", 0.2, 14.0), np.array([0.1420, 0.0180, 0.2370])), + (("7.5PB", 0.2, 16.0), np.array([0.1390, 0.0120, 0.2370])), + (("7.5PB", 0.2, 18.0), np.array([0.1370, 0.0060, 0.2370])), + (("7.5PB", 0.2, 20.0), np.array([0.1360, 0.0000, 0.2370])), + (("10PB", 0.2, 2.0), np.array([0.2270, 0.1260, 0.2370])), + (("10PB", 0.2, 4.0), np.array([0.2130, 0.0880, 0.2370])), + (("10PB", 0.2, 6.0), np.array([0.2060, 0.0640, 0.2370])), + (("10PB", 0.2, 8.0), np.array([0.2020, 0.0490, 0.2370])), + (("10PB", 0.2, 10.0), np.array([0.2000, 0.0400, 0.2370])), + (("10PB", 0.2, 12.0), np.array([0.1980, 0.0310, 0.2370])), + (("10PB", 0.2, 14.0), np.array([0.1960, 0.0240, 0.2370])), + (("10PB", 0.2, 16.0), np.array([0.1950, 0.0180, 0.2370])), + (("10PB", 0.2, 18.0), np.array([0.1930, 0.0120, 0.2370])), + (("10PB", 0.2, 20.0), np.array([0.1920, 0.0060, 0.2370])), + (("2.5P", 0.2, 2.0), np.array([0.2500, 0.1270, 0.2370])), + (("2.5P", 0.2, 4.0), np.array([0.2410, 0.0900, 0.2370])), + (("2.5P", 0.2, 6.0), np.array([0.2360, 0.0670, 0.2370])), + (("2.5P", 0.2, 8.0), np.array([0.2320, 0.0520, 0.2370])), + (("2.5P", 0.2, 10.0), np.array([0.2310, 0.0430, 0.2370])), + (("2.5P", 0.2, 12.0), np.array([0.2290, 0.0350, 0.2370])), + (("2.5P", 0.2, 14.0), np.array([0.2280, 0.0280, 0.2370])), + (("2.5P", 0.2, 16.0), np.array([0.2270, 0.0220, 0.2370])), + (("2.5P", 0.2, 18.0), np.array([0.2260, 0.0160, 0.2370])), + (("5P", 0.2, 2.0), np.array([0.2750, 0.1290, 0.2370])), + (("5P", 0.2, 4.0), np.array([0.2690, 0.0930, 0.2370])), + (("5P", 0.2, 6.0), np.array([0.2660, 0.0720, 0.2370])), + (("5P", 0.2, 8.0), np.array([0.2640, 0.0560, 0.2370])), + (("5P", 0.2, 10.0), np.array([0.2630, 0.0470, 0.2370])), + (("5P", 0.2, 12.0), np.array([0.2630, 0.0390, 0.2370])), + (("5P", 0.2, 14.0), np.array([0.2620, 0.0320, 0.2370])), + (("7.5P", 0.2, 2.0), np.array([0.2880, 0.1310, 0.2370])), + (("7.5P", 0.2, 4.0), np.array([0.2830, 0.0940, 0.2370])), + (("7.5P", 0.2, 6.0), np.array([0.2800, 0.0740, 0.2370])), + (("7.5P", 0.2, 8.0), np.array([0.2770, 0.0580, 0.2370])), + (("7.5P", 0.2, 10.0), np.array([0.2760, 0.0490, 0.2370])), + (("7.5P", 0.2, 12.0), np.array([0.2750, 0.0400, 0.2370])), + (("10P", 0.2, 2.0), np.array([0.3000, 0.1340, 0.2370])), + (("10P", 0.2, 4.0), np.array([0.2960, 0.0970, 0.2370])), + (("10P", 0.2, 6.0), np.array([0.2930, 0.0750, 0.2370])), + (("10P", 0.2, 8.0), np.array([0.2910, 0.0600, 0.2370])), + (("10P", 0.2, 10.0), np.array([0.2900, 0.0510, 0.2370])), + (("2.5RP", 0.2, 2.0), np.array([0.3150, 0.1370, 0.2370])), + (("2.5RP", 0.2, 4.0), np.array([0.3130, 0.1000, 0.2370])), + (("2.5RP", 0.2, 6.0), np.array([0.3120, 0.0780, 0.2370])), + (("2.5RP", 0.2, 8.0), np.array([0.3100, 0.0640, 0.2370])), + (("2.5RP", 0.2, 10.0), np.array([0.3100, 0.0530, 0.2370])), + (("5RP", 0.2, 2.0), np.array([0.3370, 0.1430, 0.2370])), + (("5RP", 0.2, 4.0), np.array([0.3410, 0.1060, 0.2370])), + (("5RP", 0.2, 6.0), np.array([0.3420, 0.0840, 0.2370])), + (("5RP", 0.2, 8.0), np.array([0.3420, 0.0690, 0.2370])), + (("7.5RP", 0.2, 2.0), np.array([0.3700, 0.1520, 0.2370])), + (("7.5RP", 0.2, 4.0), np.array([0.3810, 0.1150, 0.2370])), + (("7.5RP", 0.2, 6.0), np.array([0.3880, 0.0930, 0.2370])), + (("7.5RP", 0.2, 8.0), np.array([0.3930, 0.0770, 0.2370])), + (("10RP", 0.2, 2.0), np.array([0.4040, 0.1640, 0.2370])), + (("10RP", 0.2, 4.0), np.array([0.4240, 0.1250, 0.2370])), + (("10RP", 0.2, 6.0), np.array([0.4350, 0.1030, 0.2370])), + (("2.5R", 0.2, 2.0), np.array([0.4510, 0.1830, 0.2370])), + (("2.5R", 0.2, 4.0), np.array([0.4880, 0.1420, 0.2370])), + (("2.5R", 0.2, 6.0), np.array([0.5100, 0.1190, 0.2370])), + (("5R", 0.2, 2.0), np.array([0.5010, 0.2040, 0.2370])), + (("5R", 0.2, 4.0), np.array([0.5560, 0.1590, 0.2370])), + (("7.5R", 0.2, 2.0), np.array([0.5430, 0.2240, 0.2370])), + (("7.5R", 0.2, 4.0), np.array([0.6260, 0.1780, 0.2370])), + (("10R", 0.2, 2.0), np.array([0.5920, 0.2460, 0.2370])), + (("10R", 0.2, 4.0), np.array([0.6960, 0.1980, 0.2370])), + (("2.5YR", 0.2, 2.0), np.array([0.6790, 0.2900, 0.2370])), + (("2.5YR", 0.2, 4.0), np.array([0.8430, 0.2450, 0.2370])), + (("5YR", 0.2, 2.0), np.array([0.8370, 0.3750, 0.2370])), + (("5YR", 0.2, 4.0), np.array([1.1740, 0.3550, 0.2370])), + (("7.5YR", 0.2, 2.0), np.array([1.0000, 0.4800, 0.2370])), + (("10YR", 0.2, 2.0), np.array([1.2900, 0.7400, 0.2370])), + (("2.5Y", 0.2, 2.0), np.array([1.4300, 0.9700, 0.2370])), + (("5Y", 0.2, 2.0), np.array([1.4950, 1.2840, 0.2370])), + (("7.5Y", 0.2, 2.0), np.array([1.4340, 1.4590, 0.2370])), + (("10Y", 0.4, 2.0), np.array([0.5420, 0.6700, 0.4670])), + (("2.5GY", 0.4, 2.0), np.array([0.4230, 0.5900, 0.4670])), + (("2.5GY", 0.4, 4.0), np.array([0.7350, 1.8200, 0.4670])), + (("5GY", 0.4, 2.0), np.array([0.3580, 0.5280, 0.4670])), + (("5GY", 0.4, 4.0), np.array([0.3230, 1.4670, 0.4670])), + (("7.5GY", 0.4, 2.0), np.array([0.3120, 0.4820, 0.4670])), + (("7.5GY", 0.4, 4.0), np.array([0.1280, 1.1410, 0.4670])), + (("10GY", 0.4, 2.0), np.array([0.2770, 0.4450, 0.4670])), + (("10GY", 0.4, 4.0), np.array([0.0410, 0.9060, 0.4670])), + (("2.5G", 0.4, 2.0), np.array([0.2580, 0.4230, 0.4670])), + (("2.5G", 0.4, 4.0), np.array([0.0050, 0.7430, 0.4670])), + (("5G", 0.4, 2.0), np.array([0.2390, 0.3990, 0.4670])), + (("5G", 0.4, 4.0), np.array([-0.0110, 0.6040, 0.4670])), + (("7.5G", 0.4, 2.0), np.array([0.2260, 0.3800, 0.4670])), + (("7.5G", 0.4, 4.0), np.array([-0.0110, 0.5010, 0.4670])), + (("10G", 0.4, 2.0), np.array([0.2130, 0.3610, 0.4670])), + (("10G", 0.4, 4.0), np.array([-0.0040, 0.4050, 0.4670])), + (("2.5BG", 0.4, 2.0), np.array([0.1960, 0.3320, 0.4670])), + (("2.5BG", 0.4, 4.0), np.array([0.0080, 0.3340, 0.4670])), + (("5BG", 0.4, 2.0), np.array([0.1800, 0.2980, 0.4670])), + (("5BG", 0.4, 4.0), np.array([0.0310, 0.2590, 0.4670])), + (("7.5BG", 0.4, 2.0), np.array([0.1730, 0.2750, 0.4670])), + (("7.5BG", 0.4, 4.0), np.array([0.0510, 0.2200, 0.4670])), + (("7.5BG", 0.4, 6.0), np.array([-0.0050, 0.1930, 0.4670])), + (("10BG", 0.4, 2.0), np.array([0.1690, 0.2490, 0.4670])), + (("10BG", 0.4, 4.0), np.array([0.0740, 0.1870, 0.4670])), + (("10BG", 0.4, 6.0), np.array([0.0320, 0.1490, 0.4670])), + (("2.5B", 0.4, 2.0), np.array([0.1690, 0.2360, 0.4670])), + (("2.5B", 0.4, 4.0), np.array([0.0870, 0.1720, 0.4670])), + (("2.5B", 0.4, 6.0), np.array([0.0480, 0.1340, 0.4670])), + (("5B", 0.4, 2.0), np.array([0.1720, 0.2230, 0.4670])), + (("5B", 0.4, 4.0), np.array([0.1020, 0.1590, 0.4670])), + (("5B", 0.4, 6.0), np.array([0.0670, 0.1190, 0.4670])), + (("7.5B", 0.4, 2.0), np.array([0.1760, 0.2130, 0.4670])), + (("7.5B", 0.4, 4.0), np.array([0.1130, 0.1510, 0.4670])), + (("7.5B", 0.4, 6.0), np.array([0.0810, 0.1110, 0.4670])), + (("10B", 0.4, 2.0), np.array([0.1830, 0.2030, 0.4670])), + (("10B", 0.4, 4.0), np.array([0.1260, 0.1450, 0.4670])), + (("10B", 0.4, 6.0), np.array([0.0930, 0.1050, 0.4670])), + (("10B", 0.4, 8.0), np.array([0.0670, 0.0670, 0.4670])), + (("2.5PB", 0.4, 2.0), np.array([0.1900, 0.1960, 0.4670])), + (("2.5PB", 0.4, 4.0), np.array([0.1410, 0.1390, 0.4670])), + (("2.5PB", 0.4, 6.0), np.array([0.1130, 0.0980, 0.4670])), + (("2.5PB", 0.4, 8.0), np.array([0.0930, 0.0650, 0.4670])), + (("5PB", 0.4, 2.0), np.array([0.2020, 0.1880, 0.4670])), + (("5PB", 0.4, 4.0), np.array([0.1610, 0.1340, 0.4670])), + (("5PB", 0.4, 6.0), np.array([0.1350, 0.0950, 0.4670])), + (("5PB", 0.4, 8.0), np.array([0.1160, 0.0660, 0.4670])), + (("5PB", 0.4, 10.0), np.array([0.1070, 0.0530, 0.4670])), + (("5PB", 0.4, 12.0), np.array([0.0990, 0.0420, 0.4670])), + (("5PB", 0.4, 14.0), np.array([0.0960, 0.0360, 0.4670])), + (("5PB", 0.4, 16.0), np.array([0.0940, 0.0300, 0.4670])), + (("5PB", 0.4, 18.0), np.array([0.0910, 0.0250, 0.4670])), + (("5PB", 0.4, 20.0), np.array([0.0860, 0.0180, 0.4670])), + (("7.5PB", 0.4, 2.0), np.array([0.2200, 0.1800, 0.4670])), + (("7.5PB", 0.4, 4.0), np.array([0.1920, 0.1300, 0.4670])), + (("7.5PB", 0.4, 6.0), np.array([0.1750, 0.0950, 0.4670])), + (("7.5PB", 0.4, 8.0), np.array([0.1650, 0.0720, 0.4670])), + (("7.5PB", 0.4, 10.0), np.array([0.1600, 0.0580, 0.4670])), + (("7.5PB", 0.4, 12.0), np.array([0.1550, 0.0460, 0.4670])), + (("7.5PB", 0.4, 14.0), np.array([0.1520, 0.0400, 0.4670])), + (("7.5PB", 0.4, 16.0), np.array([0.1500, 0.0320, 0.4670])), + (("7.5PB", 0.4, 18.0), np.array([0.1470, 0.0260, 0.4670])), + (("7.5PB", 0.4, 20.0), np.array([0.1450, 0.0200, 0.4670])), + (("7.5PB", 0.4, 22.0), np.array([0.1430, 0.0160, 0.4670])), + (("7.5PB", 0.4, 24.0), np.array([0.1410, 0.0100, 0.4670])), + (("7.5PB", 0.4, 26.0), np.array([0.1390, 0.0050, 0.4670])), + (("7.5PB", 0.4, 28.0), np.array([0.1370, 0.0010, 0.4670])), + (("10PB", 0.4, 2.0), np.array([0.2410, 0.1760, 0.4670])), + (("10PB", 0.4, 4.0), np.array([0.2230, 0.1310, 0.4670])), + (("10PB", 0.4, 6.0), np.array([0.2120, 0.1000, 0.4670])), + (("10PB", 0.4, 8.0), np.array([0.2060, 0.0780, 0.4670])), + (("10PB", 0.4, 10.0), np.array([0.2020, 0.0620, 0.4670])), + (("10PB", 0.4, 12.0), np.array([0.1990, 0.0500, 0.4670])), + (("10PB", 0.4, 14.0), np.array([0.1970, 0.0430, 0.4670])), + (("10PB", 0.4, 16.0), np.array([0.1950, 0.0360, 0.4670])), + (("10PB", 0.4, 18.0), np.array([0.1930, 0.0290, 0.4670])), + (("10PB", 0.4, 20.0), np.array([0.1920, 0.0240, 0.4670])), + (("10PB", 0.4, 22.0), np.array([0.1910, 0.0180, 0.4670])), + (("10PB", 0.4, 24.0), np.array([0.1900, 0.0130, 0.4670])), + (("10PB", 0.4, 26.0), np.array([0.1880, 0.0080, 0.4670])), + (("10PB", 0.4, 28.0), np.array([0.1870, 0.0040, 0.4670])), + (("2.5P", 0.4, 2.0), np.array([0.2590, 0.1770, 0.4670])), + (("2.5P", 0.4, 4.0), np.array([0.2460, 0.1340, 0.4670])), + (("2.5P", 0.4, 6.0), np.array([0.2380, 0.1040, 0.4670])), + (("2.5P", 0.4, 8.0), np.array([0.2330, 0.0820, 0.4670])), + (("2.5P", 0.4, 10.0), np.array([0.2300, 0.0640, 0.4670])), + (("2.5P", 0.4, 12.0), np.array([0.2270, 0.0540, 0.4670])), + (("2.5P", 0.4, 14.0), np.array([0.2260, 0.0450, 0.4670])), + (("2.5P", 0.4, 16.0), np.array([0.2240, 0.0380, 0.4670])), + (("2.5P", 0.4, 18.0), np.array([0.2230, 0.0310, 0.4670])), + (("2.5P", 0.4, 20.0), np.array([0.2220, 0.0260, 0.4670])), + (("2.5P", 0.4, 22.0), np.array([0.2200, 0.0200, 0.4670])), + (("2.5P", 0.4, 24.0), np.array([0.2190, 0.0160, 0.4670])), + (("2.5P", 0.4, 26.0), np.array([0.2180, 0.0100, 0.4670])), + (("5P", 0.4, 2.0), np.array([0.2810, 0.1820, 0.4670])), + (("5P", 0.4, 4.0), np.array([0.2720, 0.1380, 0.4670])), + (("5P", 0.4, 6.0), np.array([0.2650, 0.1100, 0.4670])), + (("5P", 0.4, 8.0), np.array([0.2600, 0.0870, 0.4670])), + (("5P", 0.4, 10.0), np.array([0.2570, 0.0690, 0.4670])), + (("5P", 0.4, 12.0), np.array([0.2530, 0.0570, 0.4670])), + (("5P", 0.4, 14.0), np.array([0.2520, 0.0480, 0.4670])), + (("5P", 0.4, 16.0), np.array([0.2500, 0.0400, 0.4670])), + (("5P", 0.4, 18.0), np.array([0.2480, 0.0340, 0.4670])), + (("5P", 0.4, 20.0), np.array([0.2470, 0.0280, 0.4670])), + (("7.5P", 0.4, 2.0), np.array([0.2960, 0.1850, 0.4670])), + (("7.5P", 0.4, 4.0), np.array([0.2890, 0.1420, 0.4670])), + (("7.5P", 0.4, 6.0), np.array([0.2840, 0.1140, 0.4670])), + (("7.5P", 0.4, 8.0), np.array([0.2800, 0.0910, 0.4670])), + (("7.5P", 0.4, 10.0), np.array([0.2760, 0.0720, 0.4670])), + (("7.5P", 0.4, 12.0), np.array([0.2730, 0.0590, 0.4670])), + (("7.5P", 0.4, 14.0), np.array([0.2720, 0.0490, 0.4670])), + (("7.5P", 0.4, 16.0), np.array([0.2700, 0.0420, 0.4670])), + (("7.5P", 0.4, 18.0), np.array([0.2690, 0.0340, 0.4670])), + (("10P", 0.4, 2.0), np.array([0.3090, 0.1890, 0.4670])), + (("10P", 0.4, 4.0), np.array([0.3040, 0.1460, 0.4670])), + (("10P", 0.4, 6.0), np.array([0.3020, 0.1190, 0.4670])), + (("10P", 0.4, 8.0), np.array([0.2980, 0.0950, 0.4670])), + (("10P", 0.4, 10.0), np.array([0.2960, 0.0730, 0.4670])), + (("10P", 0.4, 12.0), np.array([0.2940, 0.0610, 0.4670])), + (("10P", 0.4, 14.0), np.array([0.2930, 0.0520, 0.4670])), + (("10P", 0.4, 16.0), np.array([0.2910, 0.0430, 0.4670])), + (("2.5RP", 0.4, 2.0), np.array([0.3200, 0.1930, 0.4670])), + (("2.5RP", 0.4, 4.0), np.array([0.3200, 0.1510, 0.4670])), + (("2.5RP", 0.4, 6.0), np.array([0.3200, 0.1230, 0.4670])), + (("2.5RP", 0.4, 8.0), np.array([0.3200, 0.1000, 0.4670])), + (("2.5RP", 0.4, 10.0), np.array([0.3200, 0.0780, 0.4670])), + (("2.5RP", 0.4, 12.0), np.array([0.3200, 0.0650, 0.4670])), + (("2.5RP", 0.4, 14.0), np.array([0.3200, 0.0540, 0.4670])), + (("5RP", 0.4, 2.0), np.array([0.3370, 0.1990, 0.4670])), + (("5RP", 0.4, 4.0), np.array([0.3440, 0.1580, 0.4670])), + (("5RP", 0.4, 6.0), np.array([0.3480, 0.1310, 0.4670])), + (("5RP", 0.4, 8.0), np.array([0.3500, 0.1060, 0.4670])), + (("5RP", 0.4, 10.0), np.array([0.3530, 0.0820, 0.4670])), + (("5RP", 0.4, 12.0), np.array([0.3530, 0.0700, 0.4670])), + (("7.5RP", 0.4, 2.0), np.array([0.3600, 0.2090, 0.4670])), + (("7.5RP", 0.4, 4.0), np.array([0.3740, 0.1690, 0.4670])), + (("7.5RP", 0.4, 6.0), np.array([0.3840, 0.1410, 0.4670])), + (("7.5RP", 0.4, 8.0), np.array([0.3910, 0.1170, 0.4670])), + (("7.5RP", 0.4, 10.0), np.array([0.3980, 0.0900, 0.4670])), + (("10RP", 0.4, 2.0), np.array([0.3810, 0.2200, 0.4670])), + (("10RP", 0.4, 4.0), np.array([0.4060, 0.1810, 0.4670])), + (("10RP", 0.4, 6.0), np.array([0.4230, 0.1530, 0.4670])), + (("10RP", 0.4, 8.0), np.array([0.4370, 0.1280, 0.4670])), + (("10RP", 0.4, 10.0), np.array([0.4540, 0.0980, 0.4670])), + (("2.5R", 0.4, 2.0), np.array([0.4110, 0.2360, 0.4670])), + (("2.5R", 0.4, 4.0), np.array([0.4500, 0.1980, 0.4670])), + (("2.5R", 0.4, 6.0), np.array([0.4770, 0.1700, 0.4670])), + (("2.5R", 0.4, 8.0), np.array([0.5010, 0.1430, 0.4670])), + (("2.5R", 0.4, 10.0), np.array([0.5290, 0.1130, 0.4670])), + (("5R", 0.4, 2.0), np.array([0.4410, 0.2550, 0.4670])), + (("5R", 0.4, 4.0), np.array([0.4980, 0.2190, 0.4670])), + (("5R", 0.4, 6.0), np.array([0.5370, 0.1900, 0.4670])), + (("5R", 0.4, 8.0), np.array([0.5750, 0.1610, 0.4670])), + (("7.5R", 0.4, 2.0), np.array([0.4660, 0.2720, 0.4670])), + (("7.5R", 0.4, 4.0), np.array([0.5390, 0.2380, 0.4670])), + (("7.5R", 0.4, 6.0), np.array([0.5880, 0.2080, 0.4670])), + (("7.5R", 0.4, 8.0), np.array([0.6350, 0.1760, 0.4670])), + (("10R", 0.4, 2.0), np.array([0.4900, 0.2890, 0.4670])), + (("10R", 0.4, 4.0), np.array([0.5820, 0.2580, 0.4670])), + (("10R", 0.4, 6.0), np.array([0.6490, 0.2290, 0.4670])), + (("10R", 0.4, 8.0), np.array([0.7060, 0.1960, 0.4670])), + (("2.5YR", 0.4, 2.0), np.array([0.5340, 0.3240, 0.4670])), + (("2.5YR", 0.4, 4.0), np.array([0.6650, 0.2980, 0.4670])), + (("2.5YR", 0.4, 6.0), np.array([0.7550, 0.2700, 0.4670])), + (("2.5YR", 0.4, 8.0), np.array([0.8440, 0.2410, 0.4670])), + (("5YR", 0.4, 2.0), np.array([0.5850, 0.3670, 0.4670])), + (("5YR", 0.4, 4.0), np.array([0.7750, 0.3620, 0.4670])), + (("5YR", 0.4, 6.0), np.array([0.8890, 0.3440, 0.4670])), + (("7.5YR", 0.4, 2.0), np.array([0.6380, 0.4200, 0.4670])), + (("7.5YR", 0.4, 4.0), np.array([0.9080, 0.4520, 0.4670])), + (("10YR", 0.4, 2.0), np.array([0.6980, 0.4990, 0.4670])), + (("2.5Y", 0.4, 2.0), np.array([0.7290, 0.5880, 0.4670])), + (("5Y", 0.4, 2.0), np.array([0.7210, 0.6560, 0.4670])), + (("7.5Y", 0.4, 2.0), np.array([0.6590, 0.7000, 0.4670])), + (("10Y", 0.6, 2.0), np.array([0.4320, 0.5010, 0.6990])), + (("10Y", 0.6, 4.0), np.array([0.9020, 1.2230, 0.6990])), + (("2.5GY", 0.6, 2.0), np.array([0.3770, 0.4680, 0.6990])), + (("2.5GY", 0.6, 4.0), np.array([0.6030, 1.1690, 0.6990])), + (("5GY", 0.6, 2.0), np.array([0.3420, 0.4420, 0.6990])), + (("5GY", 0.6, 4.0), np.array([0.4100, 0.9950, 0.6990])), + (("7.5GY", 0.6, 2.0), np.array([0.3150, 0.4200, 0.6990])), + (("7.5GY", 0.6, 4.0), np.array([0.2740, 0.7920, 0.6990])), + (("7.5GY", 0.6, 6.0), np.array([-0.0450, 1.4590, 0.6990])), + (("10GY", 0.6, 2.0), np.array([0.2920, 0.3990, 0.6990])), + (("10GY", 0.6, 4.0), np.array([0.2080, 0.6520, 0.6990])), + (("10GY", 0.6, 6.0), np.array([-0.1280, 1.1250, 0.6990])), + (("2.5G", 0.6, 2.0), np.array([0.2810, 0.3880, 0.6990])), + (("2.5G", 0.6, 4.0), np.array([0.1750, 0.5610, 0.6990])), + (("2.5G", 0.6, 6.0), np.array([-0.1430, 0.8890, 0.6990])), + (("5G", 0.6, 2.0), np.array([0.2700, 0.3760, 0.6990])), + (("5G", 0.6, 4.0), np.array([0.1520, 0.4930, 0.6990])), + (("5G", 0.6, 6.0), np.array([-0.1280, 0.6820, 0.6990])), + (("7.5G", 0.6, 2.0), np.array([0.2590, 0.3630, 0.6990])), + (("7.5G", 0.6, 4.0), np.array([0.1370, 0.4400, 0.6990])), + (("7.5G", 0.6, 6.0), np.array([-0.1020, 0.5410, 0.6990])), + (("10G", 0.6, 2.0), np.array([0.2470, 0.3490, 0.6990])), + (("10G", 0.6, 4.0), np.array([0.1240, 0.3890, 0.6990])), + (("10G", 0.6, 6.0), np.array([-0.0680, 0.4250, 0.6990])), + (("2.5BG", 0.6, 2.0), np.array([0.2360, 0.3340, 0.6990])), + (("2.5BG", 0.6, 4.0), np.array([0.1170, 0.3410, 0.6990])), + (("2.5BG", 0.6, 6.0), np.array([-0.0360, 0.3340, 0.6990])), + (("5BG", 0.6, 2.0), np.array([0.2210, 0.3110, 0.6990])), + (("5BG", 0.6, 4.0), np.array([0.1120, 0.2840, 0.6990])), + (("5BG", 0.6, 6.0), np.array([0.0090, 0.2430, 0.6990])), + (("7.5BG", 0.6, 2.0), np.array([0.2130, 0.2950, 0.6990])), + (("7.5BG", 0.6, 4.0), np.array([0.1130, 0.2540, 0.6990])), + (("7.5BG", 0.6, 6.0), np.array([0.0300, 0.2110, 0.6990])), + (("10BG", 0.6, 2.0), np.array([0.2060, 0.2760, 0.6990])), + (("10BG", 0.6, 4.0), np.array([0.1160, 0.2210, 0.6990])), + (("10BG", 0.6, 6.0), np.array([0.0520, 0.1760, 0.6990])), + (("2.5B", 0.6, 2.0), np.array([0.2020, 0.2600, 0.6990])), + (("2.5B", 0.6, 4.0), np.array([0.1230, 0.2020, 0.6990])), + (("2.5B", 0.6, 6.0), np.array([0.0710, 0.1570, 0.6990])), + (("2.5B", 0.6, 8.0), np.array([0.0170, 0.1110, 0.6990])), + (("5B", 0.6, 2.0), np.array([0.2020, 0.2450, 0.6990])), + (("5B", 0.6, 4.0), np.array([0.1340, 0.1870, 0.6990])), + (("5B", 0.6, 6.0), np.array([0.0880, 0.1450, 0.6990])), + (("5B", 0.6, 8.0), np.array([0.0440, 0.1000, 0.6990])), + (("7.5B", 0.6, 2.0), np.array([0.2040, 0.2350, 0.6990])), + (("7.5B", 0.6, 4.0), np.array([0.1430, 0.1780, 0.6990])), + (("7.5B", 0.6, 6.0), np.array([0.0990, 0.1360, 0.6990])), + (("7.5B", 0.6, 8.0), np.array([0.0600, 0.0960, 0.6990])), + (("10B", 0.6, 2.0), np.array([0.2090, 0.2270, 0.6990])), + (("10B", 0.6, 4.0), np.array([0.1530, 0.1720, 0.6990])), + (("10B", 0.6, 6.0), np.array([0.1150, 0.1280, 0.6990])), + (("10B", 0.6, 8.0), np.array([0.0840, 0.0910, 0.6990])), + (("2.5PB", 0.6, 2.0), np.array([0.2150, 0.2210, 0.6990])), + (("2.5PB", 0.6, 4.0), np.array([0.1660, 0.1650, 0.6990])), + (("2.5PB", 0.6, 6.0), np.array([0.1310, 0.1220, 0.6990])), + (("2.5PB", 0.6, 8.0), np.array([0.1060, 0.0900, 0.6990])), + (("2.5PB", 0.6, 10.0), np.array([0.0890, 0.0680, 0.6990])), + (("2.5PB", 0.6, 12.0), np.array([0.0790, 0.0550, 0.6990])), + (("5PB", 0.6, 2.0), np.array([0.2230, 0.2150, 0.6990])), + (("5PB", 0.6, 4.0), np.array([0.1820, 0.1600, 0.6990])), + (("5PB", 0.6, 6.0), np.array([0.1520, 0.1180, 0.6990])), + (("5PB", 0.6, 8.0), np.array([0.1310, 0.0880, 0.6990])), + (("5PB", 0.6, 10.0), np.array([0.1180, 0.0690, 0.6990])), + (("5PB", 0.6, 12.0), np.array([0.1100, 0.0570, 0.6990])), + (("5PB", 0.6, 14.0), np.array([0.1040, 0.0480, 0.6990])), + (("5PB", 0.6, 16.0), np.array([0.0990, 0.0400, 0.6990])), + (("5PB", 0.6, 18.0), np.array([0.0930, 0.0320, 0.6990])), + (("5PB", 0.6, 20.0), np.array([0.0880, 0.0240, 0.6990])), + (("5PB", 0.6, 22.0), np.array([0.0840, 0.0180, 0.6990])), + (("7.5PB", 0.6, 2.0), np.array([0.2390, 0.2080, 0.6990])), + (("7.5PB", 0.6, 4.0), np.array([0.2080, 0.1550, 0.6990])), + (("7.5PB", 0.6, 6.0), np.array([0.1880, 0.1170, 0.6990])), + (("7.5PB", 0.6, 8.0), np.array([0.1760, 0.0920, 0.6990])), + (("7.5PB", 0.6, 10.0), np.array([0.1680, 0.0740, 0.6990])), + (("7.5PB", 0.6, 12.0), np.array([0.1630, 0.0620, 0.6990])), + (("7.5PB", 0.6, 14.0), np.array([0.1590, 0.0530, 0.6990])), + (("7.5PB", 0.6, 16.0), np.array([0.1560, 0.0450, 0.6990])), + (("7.5PB", 0.6, 18.0), np.array([0.1530, 0.0370, 0.6990])), + (("7.5PB", 0.6, 20.0), np.array([0.1500, 0.0300, 0.6990])), + (("7.5PB", 0.6, 22.0), np.array([0.1480, 0.0250, 0.6990])), + (("7.5PB", 0.6, 24.0), np.array([0.1460, 0.0200, 0.6990])), + (("7.5PB", 0.6, 26.0), np.array([0.1440, 0.0140, 0.6990])), + (("7.5PB", 0.6, 28.0), np.array([0.1420, 0.0080, 0.6990])), + (("7.5PB", 0.6, 30.0), np.array([0.1400, 0.0040, 0.6990])), + (("10PB", 0.6, 2.0), np.array([0.2570, 0.2040, 0.6990])), + (("10PB", 0.6, 4.0), np.array([0.2370, 0.1570, 0.6990])), + (("10PB", 0.6, 6.0), np.array([0.2250, 0.1240, 0.6990])), + (("10PB", 0.6, 8.0), np.array([0.2160, 0.0980, 0.6990])), + (("10PB", 0.6, 10.0), np.array([0.2110, 0.0820, 0.6990])), + (("10PB", 0.6, 12.0), np.array([0.2080, 0.0700, 0.6990])), + (("10PB", 0.6, 14.0), np.array([0.2040, 0.0580, 0.6990])), + (("10PB", 0.6, 16.0), np.array([0.2020, 0.0500, 0.6990])), + (("10PB", 0.6, 18.0), np.array([0.2000, 0.0420, 0.6990])), + (("10PB", 0.6, 20.0), np.array([0.1980, 0.0350, 0.6990])), + (("10PB", 0.6, 22.0), np.array([0.1960, 0.0290, 0.6990])), + (("10PB", 0.6, 24.0), np.array([0.1950, 0.0240, 0.6990])), + (("10PB", 0.6, 26.0), np.array([0.1940, 0.0180, 0.6990])), + (("10PB", 0.6, 28.0), np.array([0.1920, 0.0120, 0.6990])), + (("10PB", 0.6, 30.0), np.array([0.1900, 0.0060, 0.6990])), + (("2.5P", 0.6, 2.0), np.array([0.2720, 0.2050, 0.6990])), + (("2.5P", 0.6, 4.0), np.array([0.2580, 0.1600, 0.6990])), + (("2.5P", 0.6, 6.0), np.array([0.2500, 0.1290, 0.6990])), + (("2.5P", 0.6, 8.0), np.array([0.2440, 0.1040, 0.6990])), + (("2.5P", 0.6, 10.0), np.array([0.2400, 0.0880, 0.6990])), + (("2.5P", 0.6, 12.0), np.array([0.2380, 0.0750, 0.6990])), + (("2.5P", 0.6, 14.0), np.array([0.2350, 0.0600, 0.6990])), + (("2.5P", 0.6, 16.0), np.array([0.2330, 0.0530, 0.6990])), + (("2.5P", 0.6, 18.0), np.array([0.2320, 0.0440, 0.6990])), + (("2.5P", 0.6, 20.0), np.array([0.2300, 0.0380, 0.6990])), + (("2.5P", 0.6, 22.0), np.array([0.2290, 0.0320, 0.6990])), + (("2.5P", 0.6, 24.0), np.array([0.2280, 0.0270, 0.6990])), + (("2.5P", 0.6, 26.0), np.array([0.2270, 0.0210, 0.6990])), + (("2.5P", 0.6, 28.0), np.array([0.2260, 0.0140, 0.6990])), + (("5P", 0.6, 2.0), np.array([0.2870, 0.2070, 0.6990])), + (("5P", 0.6, 4.0), np.array([0.2800, 0.1660, 0.6990])), + (("5P", 0.6, 6.0), np.array([0.2740, 0.1360, 0.6990])), + (("5P", 0.6, 8.0), np.array([0.2700, 0.1100, 0.6990])), + (("5P", 0.6, 10.0), np.array([0.2680, 0.0940, 0.6990])), + (("5P", 0.6, 12.0), np.array([0.2660, 0.0800, 0.6990])), + (("5P", 0.6, 14.0), np.array([0.2640, 0.0650, 0.6990])), + (("5P", 0.6, 16.0), np.array([0.2630, 0.0560, 0.6990])), + (("5P", 0.6, 18.0), np.array([0.2620, 0.0460, 0.6990])), + (("5P", 0.6, 20.0), np.array([0.2610, 0.0410, 0.6990])), + (("5P", 0.6, 22.0), np.array([0.2600, 0.0340, 0.6990])), + (("5P", 0.6, 24.0), np.array([0.2600, 0.0300, 0.6990])), + (("7.5P", 0.6, 2.0), np.array([0.3010, 0.2110, 0.6990])), + (("7.5P", 0.6, 4.0), np.array([0.2960, 0.1700, 0.6990])), + (("7.5P", 0.6, 6.0), np.array([0.2920, 0.1410, 0.6990])), + (("7.5P", 0.6, 8.0), np.array([0.2880, 0.1150, 0.6990])), + (("7.5P", 0.6, 10.0), np.array([0.2840, 0.0980, 0.6990])), + (("7.5P", 0.6, 12.0), np.array([0.2810, 0.0830, 0.6990])), + (("7.5P", 0.6, 14.0), np.array([0.2790, 0.0670, 0.6990])), + (("7.5P", 0.6, 16.0), np.array([0.2770, 0.0560, 0.6990])), + (("7.5P", 0.6, 18.0), np.array([0.2760, 0.0490, 0.6990])), + (("7.5P", 0.6, 20.0), np.array([0.2750, 0.0420, 0.6990])), + (("10P", 0.6, 2.0), np.array([0.3110, 0.2140, 0.6990])), + (("10P", 0.6, 4.0), np.array([0.3080, 0.1740, 0.6990])), + (("10P", 0.6, 6.0), np.array([0.3060, 0.1450, 0.6990])), + (("10P", 0.6, 8.0), np.array([0.3040, 0.1190, 0.6990])), + (("10P", 0.6, 10.0), np.array([0.3020, 0.1010, 0.6990])), + (("10P", 0.6, 12.0), np.array([0.3010, 0.0870, 0.6990])), + (("10P", 0.6, 14.0), np.array([0.2990, 0.0690, 0.6990])), + (("10P", 0.6, 16.0), np.array([0.2980, 0.0600, 0.6990])), + (("10P", 0.6, 18.0), np.array([0.2970, 0.0510, 0.6990])), + (("2.5RP", 0.6, 2.0), np.array([0.3220, 0.2180, 0.6990])), + (("2.5RP", 0.6, 4.0), np.array([0.3240, 0.1790, 0.6990])), + (("2.5RP", 0.6, 6.0), np.array([0.3250, 0.1510, 0.6990])), + (("2.5RP", 0.6, 8.0), np.array([0.3260, 0.1250, 0.6990])), + (("2.5RP", 0.6, 10.0), np.array([0.3260, 0.1060, 0.6990])), + (("2.5RP", 0.6, 12.0), np.array([0.3260, 0.0920, 0.6990])), + (("2.5RP", 0.6, 14.0), np.array([0.3260, 0.0730, 0.6990])), + (("2.5RP", 0.6, 16.0), np.array([0.3250, 0.0620, 0.6990])), + (("5RP", 0.6, 2.0), np.array([0.3370, 0.2260, 0.6990])), + (("5RP", 0.6, 4.0), np.array([0.3470, 0.1890, 0.6990])), + (("5RP", 0.6, 6.0), np.array([0.3540, 0.1590, 0.6990])), + (("5RP", 0.6, 8.0), np.array([0.3590, 0.1350, 0.6990])), + (("5RP", 0.6, 10.0), np.array([0.3630, 0.1140, 0.6990])), + (("5RP", 0.6, 12.0), np.array([0.3660, 0.0990, 0.6990])), + (("5RP", 0.6, 14.0), np.array([0.3700, 0.0800, 0.6990])), + (("5RP", 0.6, 16.0), np.array([0.3730, 0.0680, 0.6990])), + (("7.5RP", 0.6, 2.0), np.array([0.3550, 0.2360, 0.6990])), + (("7.5RP", 0.6, 4.0), np.array([0.3730, 0.2000, 0.6990])), + (("7.5RP", 0.6, 6.0), np.array([0.3870, 0.1700, 0.6990])), + (("7.5RP", 0.6, 8.0), np.array([0.3970, 0.1460, 0.6990])), + (("7.5RP", 0.6, 10.0), np.array([0.4060, 0.1240, 0.6990])), + (("7.5RP", 0.6, 12.0), np.array([0.4130, 0.1060, 0.6990])), + (("7.5RP", 0.6, 14.0), np.array([0.4210, 0.0870, 0.6990])), + (("10RP", 0.6, 2.0), np.array([0.3720, 0.2470, 0.6990])), + (("10RP", 0.6, 4.0), np.array([0.3990, 0.2110, 0.6990])), + (("10RP", 0.6, 6.0), np.array([0.4190, 0.1820, 0.6990])), + (("10RP", 0.6, 8.0), np.array([0.4340, 0.1580, 0.6990])), + (("10RP", 0.6, 10.0), np.array([0.4470, 0.1350, 0.6990])), + (("10RP", 0.6, 12.0), np.array([0.4590, 0.1140, 0.6990])), + (("2.5R", 0.6, 2.0), np.array([0.3910, 0.2600, 0.6990])), + (("2.5R", 0.6, 4.0), np.array([0.4320, 0.2270, 0.6990])), + (("2.5R", 0.6, 6.0), np.array([0.4640, 0.2000, 0.6990])), + (("2.5R", 0.6, 8.0), np.array([0.4890, 0.1760, 0.6990])), + (("2.5R", 0.6, 10.0), np.array([0.5110, 0.1540, 0.6990])), + (("2.5R", 0.6, 12.0), np.array([0.5370, 0.1260, 0.6990])), + (("5R", 0.6, 2.0), np.array([0.4110, 0.2740, 0.6990])), + (("5R", 0.6, 4.0), np.array([0.4690, 0.2460, 0.6990])), + (("5R", 0.6, 6.0), np.array([0.5140, 0.2210, 0.6990])), + (("5R", 0.6, 8.0), np.array([0.5510, 0.1970, 0.6990])), + (("5R", 0.6, 10.0), np.array([0.5860, 0.1760, 0.6990])), + (("7.5R", 0.6, 2.0), np.array([0.4310, 0.2900, 0.6990])), + (("7.5R", 0.6, 4.0), np.array([0.5020, 0.2640, 0.6990])), + (("7.5R", 0.6, 6.0), np.array([0.5580, 0.2400, 0.6990])), + (("7.5R", 0.6, 8.0), np.array([0.6040, 0.2140, 0.6990])), + (("7.5R", 0.6, 10.0), np.array([0.6400, 0.1920, 0.6990])), + (("10R", 0.6, 2.0), np.array([0.4470, 0.3050, 0.6990])), + (("10R", 0.6, 4.0), np.array([0.5370, 0.2840, 0.6990])), + (("10R", 0.6, 6.0), np.array([0.6050, 0.2610, 0.6990])), + (("10R", 0.6, 8.0), np.array([0.6600, 0.2350, 0.6990])), + (("10R", 0.6, 10.0), np.array([0.7040, 0.2140, 0.6990])), + (("2.5YR", 0.6, 2.0), np.array([0.4740, 0.3320, 0.6990])), + (("2.5YR", 0.6, 4.0), np.array([0.6030, 0.3220, 0.6990])), + (("2.5YR", 0.6, 6.0), np.array([0.6930, 0.3030, 0.6990])), + (("2.5YR", 0.6, 8.0), np.array([0.7870, 0.2820, 0.6990])), + (("2.5YR", 0.6, 10.0), np.array([0.8460, 0.2660, 0.6990])), + (("5YR", 0.6, 2.0), np.array([0.5050, 0.3670, 0.6990])), + (("5YR", 0.6, 4.0), np.array([0.6730, 0.3710, 0.6990])), + (("5YR", 0.6, 6.0), np.array([0.8050, 0.3620, 0.6990])), + (("5YR", 0.6, 8.0), np.array([0.9300, 0.3470, 0.6990])), + (("7.5YR", 0.6, 2.0), np.array([0.5260, 0.3970, 0.6990])), + (("7.5YR", 0.6, 4.0), np.array([0.7640, 0.4410, 0.6990])), + (("10YR", 0.6, 2.0), np.array([0.5510, 0.4440, 0.6990])), + (("10YR", 0.6, 4.0), np.array([0.8990, 0.5870, 0.6990])), + (("2.5Y", 0.6, 2.0), np.array([0.5630, 0.4910, 0.6990])), + (("2.5Y", 0.6, 4.0), np.array([0.9950, 0.7340, 0.6990])), + (("5Y", 0.6, 2.0), np.array([0.5480, 0.5260, 0.6990])), + (("7.5Y", 0.6, 2.0), np.array([0.5020, 0.5310, 0.6990])), + (("7.5Y", 0.6, 4.0), np.array([1.0440, 1.0740, 0.6990])), + (("10Y", 0.8, 2.0), np.array([0.3970, 0.4480, 0.9430])), + (("10Y", 0.8, 4.0), np.array([0.7610, 0.9980, 0.9430])), + (("2.5GY", 0.8, 2.0), np.array([0.3630, 0.4250, 0.9430])), + (("2.5GY", 0.8, 4.0), np.array([0.5560, 0.9070, 0.9430])), + (("5GY", 0.8, 2.0), np.array([0.3360, 0.4100, 0.9430])), + (("5GY", 0.8, 4.0), np.array([0.4000, 0.7380, 0.9430])), + (("5GY", 0.8, 6.0), np.array([0.4250, 1.2660, 0.9430])), + (("7.5GY", 0.8, 2.0), np.array([0.3140, 0.3940, 0.9430])), + (("7.5GY", 0.8, 4.0), np.array([0.3050, 0.6130, 0.9430])), + (("7.5GY", 0.8, 6.0), np.array([0.2370, 0.9690, 0.9430])), + (("7.5GY", 0.8, 8.0), np.array([-0.1190, 1.6320, 0.9430])), + (("10GY", 0.8, 2.0), np.array([0.2980, 0.3810, 0.9430])), + (("10GY", 0.8, 4.0), np.array([0.2540, 0.5370, 0.9430])), + (("10GY", 0.8, 6.0), np.array([0.1500, 0.7910, 0.9430])), + (("10GY", 0.8, 8.0), np.array([-0.1560, 1.2200, 0.9430])), + (("2.5G", 0.8, 2.0), np.array([0.2870, 0.3710, 0.9430])), + (("2.5G", 0.8, 4.0), np.array([0.2250, 0.4880, 0.9430])), + (("2.5G", 0.8, 6.0), np.array([0.1020, 0.6600, 0.9430])), + (("2.5G", 0.8, 8.0), np.array([-0.2130, 0.9670, 0.9430])), + (("5G", 0.8, 2.0), np.array([0.2800, 0.3630, 0.9430])), + (("5G", 0.8, 4.0), np.array([0.2050, 0.4470, 0.9430])), + (("5G", 0.8, 6.0), np.array([0.0820, 0.5530, 0.9430])), + (("5G", 0.8, 8.0), np.array([-0.1850, 0.7370, 0.9430])), + (("7.5G", 0.8, 2.0), np.array([0.2720, 0.3550, 0.9430])), + (("7.5G", 0.8, 4.0), np.array([0.1910, 0.4140, 0.9430])), + (("7.5G", 0.8, 6.0), np.array([0.0730, 0.4760, 0.9430])), + (("7.5G", 0.8, 8.0), np.array([-0.1430, 0.5610, 0.9430])), + (("10G", 0.8, 2.0), np.array([0.2650, 0.3460, 0.9430])), + (("10G", 0.8, 4.0), np.array([0.1780, 0.3820, 0.9430])), + (("10G", 0.8, 6.0), np.array([0.0700, 0.4080, 0.9430])), + (("10G", 0.8, 8.0), np.array([-0.1000, 0.4280, 0.9430])), + (("2.5BG", 0.8, 2.0), np.array([0.2530, 0.3320, 0.9430])), + (("2.5BG", 0.8, 4.0), np.array([0.1630, 0.3420, 0.9430])), + (("2.5BG", 0.8, 6.0), np.array([0.0700, 0.3410, 0.9430])), + (("2.5BG", 0.8, 8.0), np.array([-0.0620, 0.3300, 0.9430])), + (("5BG", 0.8, 2.0), np.array([0.2410, 0.3150, 0.9430])), + (("5BG", 0.8, 4.0), np.array([0.1500, 0.2990, 0.9430])), + (("5BG", 0.8, 6.0), np.array([0.0720, 0.2750, 0.9430])), + (("5BG", 0.8, 8.0), np.array([-0.0180, 0.2390, 0.9430])), + (("7.5BG", 0.8, 2.0), np.array([0.2300, 0.2960, 0.9430])), + (("7.5BG", 0.8, 4.0), np.array([0.1450, 0.2640, 0.9430])), + (("7.5BG", 0.8, 6.0), np.array([0.0770, 0.2330, 0.9430])), + (("7.5BG", 0.8, 8.0), np.array([0.0080, 0.1980, 0.9430])), + (("10BG", 0.8, 2.0), np.array([0.2230, 0.2800, 0.9430])), + (("10BG", 0.8, 4.0), np.array([0.1460, 0.2370, 0.9430])), + (("10BG", 0.8, 6.0), np.array([0.0860, 0.1990, 0.9430])), + (("10BG", 0.8, 8.0), np.array([0.0330, 0.1640, 0.9430])), + (("2.5B", 0.8, 2.0), np.array([0.2200, 0.2710, 0.9430])), + (("2.5B", 0.8, 4.0), np.array([0.1490, 0.2220, 0.9430])), + (("2.5B", 0.8, 6.0), np.array([0.0940, 0.1810, 0.9430])), + (("2.5B", 0.8, 8.0), np.array([0.0480, 0.1470, 0.9430])), + (("5B", 0.8, 2.0), np.array([0.2180, 0.2580, 0.9430])), + (("5B", 0.8, 4.0), np.array([0.1540, 0.2070, 0.9430])), + (("5B", 0.8, 6.0), np.array([0.1060, 0.1630, 0.9430])), + (("5B", 0.8, 8.0), np.array([0.0690, 0.1270, 0.9430])), + (("7.5B", 0.8, 2.0), np.array([0.2200, 0.2490, 0.9430])), + (("7.5B", 0.8, 4.0), np.array([0.1600, 0.1960, 0.9430])), + (("7.5B", 0.8, 6.0), np.array([0.1150, 0.1530, 0.9430])), + (("7.5B", 0.8, 8.0), np.array([0.0820, 0.1200, 0.9430])), + (("7.5B", 0.8, 10.0), np.array([0.0490, 0.0890, 0.9430])), + (("10B", 0.8, 2.0), np.array([0.2220, 0.2410, 0.9430])), + (("10B", 0.8, 4.0), np.array([0.1680, 0.1870, 0.9430])), + (("10B", 0.8, 6.0), np.array([0.1280, 0.1450, 0.9430])), + (("10B", 0.8, 8.0), np.array([0.0970, 0.1120, 0.9430])), + (("10B", 0.8, 10.0), np.array([0.0710, 0.0830, 0.9430])), + (("2.5PB", 0.8, 2.0), np.array([0.2250, 0.2340, 0.9430])), + (("2.5PB", 0.8, 4.0), np.array([0.1780, 0.1810, 0.9430])), + (("2.5PB", 0.8, 6.0), np.array([0.1420, 0.1380, 0.9430])), + (("2.5PB", 0.8, 8.0), np.array([0.1170, 0.1050, 0.9430])), + (("2.5PB", 0.8, 10.0), np.array([0.0980, 0.0810, 0.9430])), + (("2.5PB", 0.8, 12.0), np.array([0.0820, 0.0580, 0.9430])), + (("5PB", 0.8, 2.0), np.array([0.2340, 0.2260, 0.9430])), + (("5PB", 0.8, 4.0), np.array([0.1920, 0.1740, 0.9430])), + (("5PB", 0.8, 6.0), np.array([0.1600, 0.1320, 0.9430])), + (("5PB", 0.8, 8.0), np.array([0.1390, 0.1020, 0.9430])), + (("5PB", 0.8, 10.0), np.array([0.1230, 0.0830, 0.9430])), + (("5PB", 0.8, 12.0), np.array([0.1090, 0.0600, 0.9430])), + (("5PB", 0.8, 14.0), np.array([0.0990, 0.0450, 0.9430])), + (("5PB", 0.8, 16.0), np.array([0.0920, 0.0340, 0.9430])), + (("5PB", 0.8, 18.0), np.array([0.0870, 0.0260, 0.9430])), + (("5PB", 0.8, 20.0), np.array([0.0810, 0.0180, 0.9430])), + (("7.5PB", 0.8, 2.0), np.array([0.2470, 0.2210, 0.9430])), + (("7.5PB", 0.8, 4.0), np.array([0.2160, 0.1700, 0.9430])), + (("7.5PB", 0.8, 6.0), np.array([0.1940, 0.1310, 0.9430])), + (("7.5PB", 0.8, 8.0), np.array([0.1790, 0.1040, 0.9430])), + (("7.5PB", 0.8, 10.0), np.array([0.1700, 0.0890, 0.9430])), + (("7.5PB", 0.8, 12.0), np.array([0.1620, 0.0720, 0.9430])), + (("7.5PB", 0.8, 14.0), np.array([0.1550, 0.0560, 0.9430])), + (("7.5PB", 0.8, 16.0), np.array([0.1500, 0.0460, 0.9430])), + (("7.5PB", 0.8, 18.0), np.array([0.1470, 0.0390, 0.9430])), + (("7.5PB", 0.8, 20.0), np.array([0.1440, 0.0320, 0.9430])), + (("7.5PB", 0.8, 22.0), np.array([0.1400, 0.0250, 0.9430])), + (("7.5PB", 0.8, 24.0), np.array([0.1370, 0.0180, 0.9430])), + (("7.5PB", 0.8, 26.0), np.array([0.1350, 0.0130, 0.9430])), + (("7.5PB", 0.8, 28.0), np.array([0.1330, 0.0090, 0.9430])), + (("7.5PB", 0.8, 30.0), np.array([0.1310, 0.0050, 0.9430])), + (("7.5PB", 0.8, 32.0), np.array([0.1300, 0.0020, 0.9430])), + (("7.5PB", 0.8, 34.0), np.array([0.1290, -0.0010, 0.9430])), + (("7.5PB", 0.8, 36.0), np.array([0.1280, -0.0040, 0.9430])), + (("10PB", 0.8, 2.0), np.array([0.2630, 0.2190, 0.9430])), + (("10PB", 0.8, 4.0), np.array([0.2420, 0.1700, 0.9430])), + (("10PB", 0.8, 6.0), np.array([0.2290, 0.1370, 0.9430])), + (("10PB", 0.8, 8.0), np.array([0.2200, 0.1120, 0.9430])), + (("10PB", 0.8, 10.0), np.array([0.2150, 0.0970, 0.9430])), + (("10PB", 0.8, 12.0), np.array([0.2100, 0.0810, 0.9430])), + (("10PB", 0.8, 14.0), np.array([0.2060, 0.0680, 0.9430])), + (("10PB", 0.8, 16.0), np.array([0.2030, 0.0570, 0.9430])), + (("10PB", 0.8, 18.0), np.array([0.2000, 0.0500, 0.9430])), + (("10PB", 0.8, 20.0), np.array([0.1990, 0.0430, 0.9430])), + (("10PB", 0.8, 22.0), np.array([0.1970, 0.0360, 0.9430])), + (("10PB", 0.8, 24.0), np.array([0.1960, 0.0300, 0.9430])), + (("10PB", 0.8, 26.0), np.array([0.1940, 0.0260, 0.9430])), + (("10PB", 0.8, 28.0), np.array([0.1930, 0.0210, 0.9430])), + (("10PB", 0.8, 30.0), np.array([0.1920, 0.0180, 0.9430])), + (("10PB", 0.8, 32.0), np.array([0.1910, 0.0140, 0.9430])), + (("10PB", 0.8, 34.0), np.array([0.1900, 0.0100, 0.9430])), + (("10PB", 0.8, 36.0), np.array([0.1890, 0.0080, 0.9430])), + (("2.5P", 0.8, 2.0), np.array([0.2770, 0.2200, 0.9430])), + (("2.5P", 0.8, 4.0), np.array([0.2640, 0.1740, 0.9430])), + (("2.5P", 0.8, 6.0), np.array([0.2550, 0.1440, 0.9430])), + (("2.5P", 0.8, 8.0), np.array([0.2480, 0.1200, 0.9430])), + (("2.5P", 0.8, 10.0), np.array([0.2450, 0.1050, 0.9430])), + (("2.5P", 0.8, 12.0), np.array([0.2420, 0.0900, 0.9430])), + (("2.5P", 0.8, 14.0), np.array([0.2380, 0.0760, 0.9430])), + (("2.5P", 0.8, 16.0), np.array([0.2360, 0.0640, 0.9430])), + (("2.5P", 0.8, 18.0), np.array([0.2350, 0.0570, 0.9430])), + (("2.5P", 0.8, 20.0), np.array([0.2330, 0.0500, 0.9430])), + (("2.5P", 0.8, 22.0), np.array([0.2320, 0.0440, 0.9430])), + (("2.5P", 0.8, 24.0), np.array([0.2310, 0.0380, 0.9430])), + (("2.5P", 0.8, 26.0), np.array([0.2300, 0.0340, 0.9430])), + (("2.5P", 0.8, 28.0), np.array([0.2280, 0.0280, 0.9430])), + (("2.5P", 0.8, 30.0), np.array([0.2280, 0.0260, 0.9430])), + (("2.5P", 0.8, 32.0), np.array([0.2270, 0.0220, 0.9430])), + (("2.5P", 0.8, 34.0), np.array([0.2270, 0.0190, 0.9430])), + (("2.5P", 0.8, 36.0), np.array([0.2270, 0.0150, 0.9430])), + (("5P", 0.8, 2.0), np.array([0.2920, 0.2240, 0.9430])), + (("5P", 0.8, 4.0), np.array([0.2830, 0.1790, 0.9430])), + (("5P", 0.8, 6.0), np.array([0.2790, 0.1510, 0.9430])), + (("5P", 0.8, 8.0), np.array([0.2750, 0.1270, 0.9430])), + (("5P", 0.8, 10.0), np.array([0.2690, 0.1120, 0.9430])), + (("5P", 0.8, 12.0), np.array([0.2660, 0.0950, 0.9430])), + (("5P", 0.8, 14.0), np.array([0.2630, 0.0800, 0.9430])), + (("5P", 0.8, 16.0), np.array([0.2620, 0.0690, 0.9430])), + (("5P", 0.8, 18.0), np.array([0.2600, 0.0620, 0.9430])), + (("5P", 0.8, 20.0), np.array([0.2580, 0.0560, 0.9430])), + (("5P", 0.8, 22.0), np.array([0.2570, 0.0490, 0.9430])), + (("5P", 0.8, 24.0), np.array([0.2560, 0.0440, 0.9430])), + (("5P", 0.8, 26.0), np.array([0.2560, 0.0390, 0.9430])), + (("5P", 0.8, 28.0), np.array([0.2550, 0.0350, 0.9430])), + (("5P", 0.8, 30.0), np.array([0.2540, 0.0300, 0.9430])), + (("7.5P", 0.8, 2.0), np.array([0.3040, 0.2280, 0.9430])), + (("7.5P", 0.8, 4.0), np.array([0.2980, 0.1840, 0.9430])), + (("7.5P", 0.8, 6.0), np.array([0.2940, 0.1560, 0.9430])), + (("7.5P", 0.8, 8.0), np.array([0.2910, 0.1320, 0.9430])), + (("7.5P", 0.8, 10.0), np.array([0.2900, 0.1180, 0.9430])), + (("7.5P", 0.8, 12.0), np.array([0.2870, 0.1010, 0.9430])), + (("7.5P", 0.8, 14.0), np.array([0.2840, 0.0840, 0.9430])), + (("7.5P", 0.8, 16.0), np.array([0.2830, 0.0720, 0.9430])), + (("7.5P", 0.8, 18.0), np.array([0.2810, 0.0660, 0.9430])), + (("7.5P", 0.8, 20.0), np.array([0.2800, 0.0600, 0.9430])), + (("7.5P", 0.8, 22.0), np.array([0.2790, 0.0540, 0.9430])), + (("7.5P", 0.8, 24.0), np.array([0.2780, 0.0490, 0.9430])), + (("7.5P", 0.8, 26.0), np.array([0.2780, 0.0440, 0.9430])), + (("10P", 0.8, 2.0), np.array([0.3120, 0.2320, 0.9430])), + (("10P", 0.8, 4.0), np.array([0.3100, 0.1890, 0.9430])), + (("10P", 0.8, 6.0), np.array([0.3090, 0.1620, 0.9430])), + (("10P", 0.8, 8.0), np.array([0.3080, 0.1370, 0.9430])), + (("10P", 0.8, 10.0), np.array([0.3070, 0.1230, 0.9430])), + (("10P", 0.8, 12.0), np.array([0.3060, 0.1060, 0.9430])), + (("10P", 0.8, 14.0), np.array([0.3040, 0.0890, 0.9430])), + (("10P", 0.8, 16.0), np.array([0.3030, 0.0780, 0.9430])), + (("10P", 0.8, 18.0), np.array([0.3020, 0.0710, 0.9430])), + (("10P", 0.8, 20.0), np.array([0.3020, 0.0650, 0.9430])), + (("10P", 0.8, 22.0), np.array([0.3020, 0.0590, 0.9430])), + (("10P", 0.8, 24.0), np.array([0.3020, 0.0530, 0.9430])), + (("2.5RP", 0.8, 2.0), np.array([0.3220, 0.2360, 0.9430])), + (("2.5RP", 0.8, 4.0), np.array([0.3260, 0.1950, 0.9430])), + (("2.5RP", 0.8, 6.0), np.array([0.3280, 0.1680, 0.9430])), + (("2.5RP", 0.8, 8.0), np.array([0.3290, 0.1440, 0.9430])), + (("2.5RP", 0.8, 10.0), np.array([0.3290, 0.1290, 0.9430])), + (("2.5RP", 0.8, 12.0), np.array([0.3300, 0.1110, 0.9430])), + (("2.5RP", 0.8, 14.0), np.array([0.3300, 0.0930, 0.9430])), + (("2.5RP", 0.8, 16.0), np.array([0.3310, 0.0840, 0.9430])), + (("2.5RP", 0.8, 18.0), np.array([0.3310, 0.0770, 0.9430])), + (("2.5RP", 0.8, 20.0), np.array([0.3310, 0.0700, 0.9430])), + (("2.5RP", 0.8, 22.0), np.array([0.3310, 0.0650, 0.9430])), + (("5RP", 0.8, 2.0), np.array([0.3360, 0.2430, 0.9430])), + (("5RP", 0.8, 4.0), np.array([0.3470, 0.2060, 0.9430])), + (("5RP", 0.8, 6.0), np.array([0.3550, 0.1790, 0.9430])), + (("5RP", 0.8, 8.0), np.array([0.3620, 0.1540, 0.9430])), + (("5RP", 0.8, 10.0), np.array([0.3660, 0.1390, 0.9430])), + (("5RP", 0.8, 12.0), np.array([0.3700, 0.1200, 0.9430])), + (("5RP", 0.8, 14.0), np.array([0.3730, 0.1050, 0.9430])), + (("5RP", 0.8, 16.0), np.array([0.3760, 0.0940, 0.9430])), + (("5RP", 0.8, 18.0), np.array([0.3780, 0.0860, 0.9430])), + (("5RP", 0.8, 20.0), np.array([0.3790, 0.0810, 0.9430])), + (("7.5RP", 0.8, 2.0), np.array([0.3500, 0.2510, 0.9430])), + (("7.5RP", 0.8, 4.0), np.array([0.3690, 0.2170, 0.9430])), + (("7.5RP", 0.8, 6.0), np.array([0.3840, 0.1900, 0.9430])), + (("7.5RP", 0.8, 8.0), np.array([0.3970, 0.1650, 0.9430])), + (("7.5RP", 0.8, 10.0), np.array([0.4050, 0.1490, 0.9430])), + (("7.5RP", 0.8, 12.0), np.array([0.4130, 0.1320, 0.9430])), + (("7.5RP", 0.8, 14.0), np.array([0.4210, 0.1150, 0.9430])), + (("7.5RP", 0.8, 16.0), np.array([0.4270, 0.1040, 0.9430])), + (("10RP", 0.8, 2.0), np.array([0.3650, 0.2610, 0.9430])), + (("10RP", 0.8, 4.0), np.array([0.3930, 0.2300, 0.9430])), + (("10RP", 0.8, 6.0), np.array([0.4150, 0.2030, 0.9430])), + (("10RP", 0.8, 8.0), np.array([0.4350, 0.1770, 0.9430])), + (("10RP", 0.8, 10.0), np.array([0.4470, 0.1600, 0.9430])), + (("10RP", 0.8, 12.0), np.array([0.4590, 0.1430, 0.9430])), + (("10RP", 0.8, 14.0), np.array([0.4700, 0.1270, 0.9430])), + (("2.5R", 0.8, 2.0), np.array([0.3810, 0.2720, 0.9430])), + (("2.5R", 0.8, 4.0), np.array([0.4210, 0.2450, 0.9430])), + (("2.5R", 0.8, 6.0), np.array([0.4550, 0.2190, 0.9430])), + (("2.5R", 0.8, 8.0), np.array([0.4830, 0.1950, 0.9430])), + (("2.5R", 0.8, 10.0), np.array([0.5020, 0.1770, 0.9430])), + (("2.5R", 0.8, 12.0), np.array([0.5210, 0.1590, 0.9430])), + (("2.5R", 0.8, 14.0), np.array([0.5360, 0.1430, 0.9430])), + (("5R", 0.8, 2.0), np.array([0.3990, 0.2860, 0.9430])), + (("5R", 0.8, 4.0), np.array([0.4500, 0.2610, 0.9430])), + (("5R", 0.8, 6.0), np.array([0.4960, 0.2370, 0.9430])), + (("5R", 0.8, 8.0), np.array([0.5360, 0.2140, 0.9430])), + (("5R", 0.8, 10.0), np.array([0.5650, 0.1970, 0.9430])), + (("5R", 0.8, 12.0), np.array([0.5910, 0.1800, 0.9430])), + (("7.5R", 0.8, 2.0), np.array([0.4110, 0.2970, 0.9430])), + (("7.5R", 0.8, 4.0), np.array([0.4770, 0.2760, 0.9430])), + (("7.5R", 0.8, 6.0), np.array([0.5340, 0.2550, 0.9430])), + (("7.5R", 0.8, 8.0), np.array([0.5840, 0.2340, 0.9430])), + (("7.5R", 0.8, 10.0), np.array([0.6240, 0.2160, 0.9430])), + (("7.5R", 0.8, 12.0), np.array([0.6600, 0.1990, 0.9430])), + (("10R", 0.8, 2.0), np.array([0.4230, 0.3090, 0.9430])), + (("10R", 0.8, 4.0), np.array([0.5080, 0.2960, 0.9430])), + (("10R", 0.8, 6.0), np.array([0.5780, 0.2800, 0.9430])), + (("10R", 0.8, 8.0), np.array([0.6350, 0.2590, 0.9430])), + (("10R", 0.8, 10.0), np.array([0.6850, 0.2400, 0.9430])), + (("10R", 0.8, 12.0), np.array([0.7250, 0.2180, 0.9430])), + (("2.5YR", 0.8, 2.0), np.array([0.4450, 0.3330, 0.9430])), + (("2.5YR", 0.8, 4.0), np.array([0.5580, 0.3300, 0.9430])), + (("2.5YR", 0.8, 6.0), np.array([0.6370, 0.3200, 0.9430])), + (("2.5YR", 0.8, 8.0), np.array([0.7150, 0.3060, 0.9430])), + (("2.5YR", 0.8, 10.0), np.array([0.8020, 0.2880, 0.9430])), + (("2.5YR", 0.8, 12.0), np.array([0.8750, 0.2730, 0.9430])), + (("5YR", 0.8, 2.0), np.array([0.4630, 0.3610, 0.9430])), + (("5YR", 0.8, 4.0), np.array([0.6120, 0.3760, 0.9430])), + (("5YR", 0.8, 6.0), np.array([0.7210, 0.3760, 0.9430])), + (("5YR", 0.8, 8.0), np.array([0.8260, 0.3720, 0.9430])), + (("7.5YR", 0.8, 2.0), np.array([0.4750, 0.3860, 0.9430])), + (("7.5YR", 0.8, 4.0), np.array([0.6630, 0.4300, 0.9430])), + (("7.5YR", 0.8, 6.0), np.array([0.8170, 0.4500, 0.9430])), + (("10YR", 0.8, 2.0), np.array([0.4810, 0.4110, 0.9430])), + (("10YR", 0.8, 4.0), np.array([0.7490, 0.5340, 0.9430])), + (("2.5Y", 0.8, 2.0), np.array([0.4790, 0.4390, 0.9430])), + (("2.5Y", 0.8, 4.0), np.array([0.8200, 0.6400, 0.9430])), + (("5Y", 0.8, 2.0), np.array([0.4650, 0.4570, 0.9430])), + (("5Y", 0.8, 4.0), np.array([0.8910, 0.7900, 0.9430])), + (("7.5Y", 0.8, 2.0), np.array([0.4340, 0.4600, 0.9430])), + (("7.5Y", 0.8, 4.0), np.array([0.8800, 0.9360, 0.9430])), + (("10Y", 1.0, 2.0), np.array([0.3802, 0.4212, 1.2100])), + (("10Y", 1.0, 4.0), np.array([0.5010, 0.6000, 1.2100])), + (("2.5GY", 1.0, 2.0), np.array([0.3540, 0.4088, 1.2100])), + (("2.5GY", 1.0, 4.0), np.array([0.4390, 0.6150, 1.2100])), + (("2.5GY", 1.0, 6.0), np.array([0.4800, 0.7230, 1.2100])), + (("5GY", 1.0, 2.0), np.array([0.3359, 0.3982, 1.2100])), + (("5GY", 1.0, 4.0), np.array([0.3765, 0.5942, 1.2100])), + (("5GY", 1.0, 6.0), np.array([0.3980, 0.7400, 1.2100])), + (("7.5GY", 1.0, 2.0), np.array([0.3154, 0.3840, 1.2100])), + (("7.5GY", 1.0, 4.0), np.array([0.3133, 0.5380, 1.2100])), + (("7.5GY", 1.0, 6.0), np.array([0.2900, 0.7060, 1.2100])), + (("7.5GY", 1.0, 8.0), np.array([0.2350, 0.9440, 1.2100])), + (("10GY", 1.0, 2.0), np.array([0.3006, 0.3720, 1.2100])), + (("10GY", 1.0, 4.0), np.array([0.2722, 0.4903, 1.2100])), + (("10GY", 1.0, 6.0), np.array([0.2232, 0.6392, 1.2100])), + (("10GY", 1.0, 8.0), np.array([0.1100, 0.8830, 1.2100])), + (("10GY", 1.0, 10.0), np.array([-0.0130, 1.0650, 1.2100])), + (("2.5G", 1.0, 2.0), np.array([0.2910, 0.3634, 1.2100])), + (("2.5G", 1.0, 4.0), np.array([0.2454, 0.4489, 1.2100])), + (("2.5G", 1.0, 6.0), np.array([0.1711, 0.5619, 1.2100])), + (("2.5G", 1.0, 8.0), np.array([0.0620, 0.6896, 1.2100])), + (("2.5G", 1.0, 10.0), np.array([-0.0220, 0.7760, 1.2100])), + (("5G", 1.0, 2.0), np.array([0.2833, 0.3564, 1.2100])), + (("5G", 1.0, 4.0), np.array([0.2290, 0.4218, 1.2100])), + (("5G", 1.0, 6.0), np.array([0.1468, 0.4996, 1.2100])), + (("5G", 1.0, 8.0), np.array([0.0559, 0.5710, 1.2100])), + (("5G", 1.0, 10.0), np.array([-0.0200, 0.6200, 1.2100])), + (("7.5G", 1.0, 2.0), np.array([0.2758, 0.3484, 1.2100])), + (("7.5G", 1.0, 4.0), np.array([0.2159, 0.3967, 1.2100])), + (("7.5G", 1.0, 6.0), np.array([0.1344, 0.4505, 1.2100])), + (("7.5G", 1.0, 8.0), np.array([0.0530, 0.4943, 1.2100])), + (("7.5G", 1.0, 10.0), np.array([-0.0200, 0.5280, 1.2100])), + (("10G", 1.0, 2.0), np.array([0.2689, 0.3407, 1.2100])), + (("10G", 1.0, 4.0), np.array([0.2040, 0.3724, 1.2100])), + (("10G", 1.0, 6.0), np.array([0.1249, 0.4019, 1.2100])), + (("10G", 1.0, 8.0), np.array([0.0511, 0.4158, 1.2100])), + (("10G", 1.0, 10.0), np.array([-0.0180, 0.4240, 1.2100])), + (("2.5BG", 1.0, 2.0), np.array([0.2600, 0.3289, 1.2100])), + (("2.5BG", 1.0, 4.0), np.array([0.1883, 0.3406, 1.2100])), + (("2.5BG", 1.0, 6.0), np.array([0.1169, 0.3452, 1.2100])), + (("2.5BG", 1.0, 8.0), np.array([0.0476, 0.3458, 1.2100])), + (("2.5BG", 1.0, 10.0), np.array([-0.0170, 0.3440, 1.2100])), + (("5BG", 1.0, 2.0), np.array([0.2500, 0.3141, 1.2100])), + (("5BG", 1.0, 4.0), np.array([0.1753, 0.3021, 1.2100])), + (("5BG", 1.0, 6.0), np.array([0.1093, 0.2860, 1.2100])), + (("5BG", 1.0, 8.0), np.array([0.0460, 0.2640, 1.2100])), + (("5BG", 1.0, 10.0), np.array([-0.0140, 0.2370, 1.2100])), + (("7.5BG", 1.0, 2.0), np.array([0.2430, 0.3023, 1.2100])), + (("7.5BG", 1.0, 4.0), np.array([0.1702, 0.2768, 1.2100])), + (("7.5BG", 1.0, 6.0), np.array([0.1059, 0.2485, 1.2100])), + (("7.5BG", 1.0, 8.0), np.array([0.0500, 0.2180, 1.2100])), + (("10BG", 1.0, 2.0), np.array([0.2362, 0.2882, 1.2100])), + (("10BG", 1.0, 4.0), np.array([0.1658, 0.2496, 1.2100])), + (("10BG", 1.0, 6.0), np.array([0.1074, 0.2129, 1.2100])), + (("10BG", 1.0, 8.0), np.array([0.0600, 0.1800, 1.2100])), + (("2.5B", 1.0, 2.0), np.array([0.2322, 0.2781, 1.2100])), + (("2.5B", 1.0, 4.0), np.array([0.1649, 0.2324, 1.2100])), + (("2.5B", 1.0, 6.0), np.array([0.1118, 0.1908, 1.2100])), + (("2.5B", 1.0, 8.0), np.array([0.0710, 0.1550, 1.2100])), + (("5B", 1.0, 2.0), np.array([0.2291, 0.2677, 1.2100])), + (("5B", 1.0, 4.0), np.array([0.1667, 0.2168, 1.2100])), + (("5B", 1.0, 6.0), np.array([0.1212, 0.1745, 1.2100])), + (("5B", 1.0, 8.0), np.array([0.0840, 0.1390, 1.2100])), + (("5B", 1.0, 10.0), np.array([0.0550, 0.1030, 1.2100])), + (("7.5B", 1.0, 2.0), np.array([0.2291, 0.2579, 1.2100])), + (("7.5B", 1.0, 4.0), np.array([0.1716, 0.2048, 1.2100])), + (("7.5B", 1.0, 6.0), np.array([0.1303, 0.1639, 1.2100])), + (("7.5B", 1.0, 8.0), np.array([0.0968, 0.1280, 1.2100])), + (("7.5B", 1.0, 10.0), np.array([0.0710, 0.0970, 1.2100])), + (("10B", 1.0, 2.0), np.array([0.2309, 0.2491, 1.2100])), + (("10B", 1.0, 4.0), np.array([0.1783, 0.1974, 1.2100])), + (("10B", 1.0, 6.0), np.array([0.1392, 0.1563, 1.2100])), + (("10B", 1.0, 8.0), np.array([0.1077, 0.1218, 1.2100])), + (("10B", 1.0, 10.0), np.array([0.0840, 0.0940, 1.2100])), + (("2.5PB", 1.0, 2.0), np.array([0.2360, 0.2420, 1.2100])), + (("2.5PB", 1.0, 4.0), np.array([0.1895, 0.1911, 1.2100])), + (("2.5PB", 1.0, 6.0), np.array([0.1539, 0.1491, 1.2100])), + (("2.5PB", 1.0, 8.0), np.array([0.1273, 0.1157, 1.2100])), + (("2.5PB", 1.0, 10.0), np.array([0.1060, 0.0900, 1.2100])), + (("2.5PB", 1.0, 12.0), np.array([0.0910, 0.0680, 1.2100])), + (("2.5PB", 1.0, 14.0), np.array([0.0780, 0.0560, 1.2100])), + (("5PB", 1.0, 2.0), np.array([0.2427, 0.2368, 1.2100])), + (("5PB", 1.0, 4.0), np.array([0.2012, 0.1867, 1.2100])), + (("5PB", 1.0, 6.0), np.array([0.1678, 0.1447, 1.2100])), + (("5PB", 1.0, 8.0), np.array([0.1447, 0.1124, 1.2100])), + (("5PB", 1.0, 10.0), np.array([0.1285, 0.0870, 1.2100])), + (("5PB", 1.0, 12.0), np.array([0.1170, 0.0700, 1.2100])), + (("5PB", 1.0, 14.0), np.array([0.1120, 0.0600, 1.2100])), + (("5PB", 1.0, 16.0), np.array([0.1060, 0.0500, 1.2100])), + (("5PB", 1.0, 18.0), np.array([0.1040, 0.0440, 1.2100])), + (("5PB", 1.0, 20.0), np.array([0.1000, 0.0380, 1.2100])), + (("5PB", 1.0, 22.0), np.array([0.0980, 0.0340, 1.2100])), + (("5PB", 1.0, 24.0), np.array([0.0960, 0.0300, 1.2100])), + (("5PB", 1.0, 26.0), np.array([0.0950, 0.0270, 1.2100])), + (("5PB", 1.0, 28.0), np.array([0.0940, 0.0240, 1.2100])), + (("5PB", 1.0, 30.0), np.array([0.0930, 0.0220, 1.2100])), + (("5PB", 1.0, 32.0), np.array([0.0920, 0.0200, 1.2100])), + (("5PB", 1.0, 34.0), np.array([0.0910, 0.0180, 1.2100])), + (("5PB", 1.0, 36.0), np.array([0.0900, 0.0160, 1.2100])), + (("5PB", 1.0, 38.0), np.array([0.0890, 0.0140, 1.2100])), + (("5PB", 1.0, 40.0), np.array([0.0880, 0.0120, 1.2100])), + (("5PB", 1.0, 42.0), np.array([0.0880, 0.0100, 1.2100])), + (("5PB", 1.0, 44.0), np.array([0.0870, 0.0080, 1.2100])), + (("7.5PB", 1.0, 2.0), np.array([0.2547, 0.2310, 1.2100])), + (("7.5PB", 1.0, 4.0), np.array([0.2232, 0.1821, 1.2100])), + (("7.5PB", 1.0, 6.0), np.array([0.2000, 0.1422, 1.2100])), + (("7.5PB", 1.0, 8.0), np.array([0.1872, 0.1141, 1.2100])), + (("7.5PB", 1.0, 10.0), np.array([0.1804, 0.0950, 1.2100])), + (("7.5PB", 1.0, 12.0), np.array([0.1763, 0.0804, 1.2100])), + (("7.5PB", 1.0, 14.0), np.array([0.1738, 0.0688, 1.2100])), + (("7.5PB", 1.0, 16.0), np.array([0.1720, 0.0583, 1.2100])), + (("7.5PB", 1.0, 18.0), np.array([0.1709, 0.0518, 1.2100])), + (("7.5PB", 1.0, 20.0), np.array([0.1701, 0.0454, 1.2100])), + (("7.5PB", 1.0, 22.0), np.array([0.1696, 0.0402, 1.2100])), + (("7.5PB", 1.0, 24.0), np.array([0.1691, 0.0352, 1.2100])), + (("7.5PB", 1.0, 26.0), np.array([0.1689, 0.0309, 1.2100])), + (("7.5PB", 1.0, 28.0), np.array([0.1686, 0.0270, 1.2100])), + (("7.5PB", 1.0, 30.0), np.array([0.1684, 0.0234, 1.2100])), + (("7.5PB", 1.0, 32.0), np.array([0.1682, 0.0202, 1.2100])), + (("7.5PB", 1.0, 34.0), np.array([0.1682, 0.0180, 1.2100])), + (("7.5PB", 1.0, 36.0), np.array([0.1681, 0.0160, 1.2100])), + (("7.5PB", 1.0, 38.0), np.array([0.1680, 0.0140, 1.2100])), + (("7.5PB", 1.0, 40.0), np.array([0.1680, 0.0120, 1.2100])), + (("7.5PB", 1.0, 42.0), np.array([0.1680, 0.0100, 1.2100])), + (("7.5PB", 1.0, 44.0), np.array([0.1680, 0.0080, 1.2100])), + (("7.5PB", 1.0, 46.0), np.array([0.1680, 0.0060, 1.2100])), + (("7.5PB", 1.0, 48.0), np.array([0.1680, 0.0040, 1.2100])), + (("10PB", 1.0, 2.0), np.array([0.2677, 0.2280, 1.2100])), + (("10PB", 1.0, 4.0), np.array([0.2459, 0.1828, 1.2100])), + (("10PB", 1.0, 6.0), np.array([0.2290, 0.1470, 1.2100])), + (("10PB", 1.0, 8.0), np.array([0.2190, 0.1228, 1.2100])), + (("10PB", 1.0, 10.0), np.array([0.2120, 0.1029, 1.2100])), + (("10PB", 1.0, 12.0), np.array([0.2070, 0.0869, 1.2100])), + (("10PB", 1.0, 14.0), np.array([0.2038, 0.0745, 1.2100])), + (("10PB", 1.0, 16.0), np.array([0.2008, 0.0638, 1.2100])), + (("10PB", 1.0, 18.0), np.array([0.1991, 0.0564, 1.2100])), + (("10PB", 1.0, 20.0), np.array([0.1976, 0.0493, 1.2100])), + (("10PB", 1.0, 22.0), np.array([0.1965, 0.0436, 1.2100])), + (("10PB", 1.0, 24.0), np.array([0.1952, 0.0380, 1.2100])), + (("10PB", 1.0, 26.0), np.array([0.1942, 0.0326, 1.2100])), + (("10PB", 1.0, 28.0), np.array([0.1936, 0.0281, 1.2100])), + (("10PB", 1.0, 30.0), np.array([0.1928, 0.0240, 1.2100])), + (("10PB", 1.0, 32.0), np.array([0.1930, 0.0210, 1.2100])), + (("10PB", 1.0, 34.0), np.array([0.1930, 0.0190, 1.2100])), + (("10PB", 1.0, 36.0), np.array([0.1930, 0.0160, 1.2100])), + (("10PB", 1.0, 38.0), np.array([0.1930, 0.0140, 1.2100])), + (("10PB", 1.0, 40.0), np.array([0.1930, 0.0130, 1.2100])), + (("10PB", 1.0, 42.0), np.array([0.1920, 0.0110, 1.2100])), + (("10PB", 1.0, 44.0), np.array([0.1920, 0.0090, 1.2100])), + (("10PB", 1.0, 46.0), np.array([0.1920, 0.0070, 1.2100])), + (("10PB", 1.0, 48.0), np.array([0.1920, 0.0050, 1.2100])), + (("2.5P", 1.0, 2.0), np.array([0.2808, 0.2296, 1.2100])), + (("2.5P", 1.0, 4.0), np.array([0.2668, 0.1874, 1.2100])), + (("2.5P", 1.0, 6.0), np.array([0.2570, 0.1559, 1.2100])), + (("2.5P", 1.0, 8.0), np.array([0.2496, 0.1303, 1.2100])), + (("2.5P", 1.0, 10.0), np.array([0.2441, 0.1112, 1.2100])), + (("2.5P", 1.0, 12.0), np.array([0.2394, 0.0940, 1.2100])), + (("2.5P", 1.0, 14.0), np.array([0.2361, 0.0810, 1.2100])), + (("2.5P", 1.0, 16.0), np.array([0.2331, 0.0696, 1.2100])), + (("2.5P", 1.0, 18.0), np.array([0.2312, 0.0618, 1.2100])), + (("2.5P", 1.0, 20.0), np.array([0.2295, 0.0542, 1.2100])), + (("2.5P", 1.0, 22.0), np.array([0.2279, 0.0473, 1.2100])), + (("2.5P", 1.0, 24.0), np.array([0.2266, 0.0418, 1.2100])), + (("2.5P", 1.0, 26.0), np.array([0.2251, 0.0355, 1.2100])), + (("2.5P", 1.0, 28.0), np.array([0.2250, 0.0310, 1.2100])), + (("2.5P", 1.0, 30.0), np.array([0.2240, 0.0260, 1.2100])), + (("2.5P", 1.0, 32.0), np.array([0.2240, 0.0220, 1.2100])), + (("2.5P", 1.0, 34.0), np.array([0.2230, 0.0200, 1.2100])), + (("2.5P", 1.0, 36.0), np.array([0.2220, 0.0180, 1.2100])), + (("2.5P", 1.0, 38.0), np.array([0.2220, 0.0150, 1.2100])), + (("2.5P", 1.0, 40.0), np.array([0.2220, 0.0130, 1.2100])), + (("2.5P", 1.0, 42.0), np.array([0.2220, 0.0110, 1.2100])), + (("5P", 1.0, 2.0), np.array([0.2936, 0.2330, 1.2100])), + (("5P", 1.0, 4.0), np.array([0.2854, 0.1927, 1.2100])), + (("5P", 1.0, 6.0), np.array([0.2794, 0.1628, 1.2100])), + (("5P", 1.0, 8.0), np.array([0.2742, 0.1375, 1.2100])), + (("5P", 1.0, 10.0), np.array([0.2701, 0.1178, 1.2100])), + (("5P", 1.0, 12.0), np.array([0.2670, 0.1006, 1.2100])), + (("5P", 1.0, 14.0), np.array([0.2645, 0.0863, 1.2100])), + (("5P", 1.0, 16.0), np.array([0.2625, 0.0746, 1.2100])), + (("5P", 1.0, 18.0), np.array([0.2612, 0.0667, 1.2100])), + (("5P", 1.0, 20.0), np.array([0.2601, 0.0586, 1.2100])), + (("5P", 1.0, 22.0), np.array([0.2590, 0.0509, 1.2100])), + (("5P", 1.0, 24.0), np.array([0.2580, 0.0460, 1.2100])), + (("5P", 1.0, 26.0), np.array([0.2580, 0.0390, 1.2100])), + (("5P", 1.0, 28.0), np.array([0.2570, 0.0340, 1.2100])), + (("5P", 1.0, 30.0), np.array([0.2560, 0.0280, 1.2100])), + (("5P", 1.0, 32.0), np.array([0.2560, 0.0240, 1.2100])), + (("7.5P", 1.0, 2.0), np.array([0.3030, 0.2361, 1.2100])), + (("7.5P", 1.0, 4.0), np.array([0.2991, 0.1974, 1.2100])), + (("7.5P", 1.0, 6.0), np.array([0.2960, 0.1682, 1.2100])), + (("7.5P", 1.0, 8.0), np.array([0.2932, 0.1429, 1.2100])), + (("7.5P", 1.0, 10.0), np.array([0.2905, 0.1229, 1.2100])), + (("7.5P", 1.0, 12.0), np.array([0.2884, 0.1059, 1.2100])), + (("7.5P", 1.0, 14.0), np.array([0.2868, 0.0903, 1.2100])), + (("7.5P", 1.0, 16.0), np.array([0.2852, 0.0790, 1.2100])), + (("7.5P", 1.0, 18.0), np.array([0.2841, 0.0706, 1.2100])), + (("7.5P", 1.0, 20.0), np.array([0.2831, 0.0625, 1.2100])), + (("7.5P", 1.0, 22.0), np.array([0.2820, 0.0550, 1.2100])), + (("7.5P", 1.0, 24.0), np.array([0.2820, 0.0490, 1.2100])), + (("7.5P", 1.0, 26.0), np.array([0.2810, 0.0420, 1.2100])), + (("10P", 1.0, 2.0), np.array([0.3132, 0.2404, 1.2100])), + (("10P", 1.0, 4.0), np.array([0.3132, 0.2032, 1.2100])), + (("10P", 1.0, 6.0), np.array([0.3126, 0.1737, 1.2100])), + (("10P", 1.0, 8.0), np.array([0.3114, 0.1481, 1.2100])), + (("10P", 1.0, 10.0), np.array([0.3102, 0.1282, 1.2100])), + (("10P", 1.0, 12.0), np.array([0.3094, 0.1110, 1.2100])), + (("10P", 1.0, 14.0), np.array([0.3084, 0.0952, 1.2100])), + (("10P", 1.0, 16.0), np.array([0.3078, 0.0839, 1.2100])), + (("10P", 1.0, 18.0), np.array([0.3069, 0.0748, 1.2100])), + (("10P", 1.0, 20.0), np.array([0.3070, 0.0660, 1.2100])), + (("10P", 1.0, 22.0), np.array([0.3060, 0.0580, 1.2100])), + (("10P", 1.0, 24.0), np.array([0.3050, 0.0510, 1.2100])), + (("2.5RP", 1.0, 2.0), np.array([0.3240, 0.2459, 1.2100])), + (("2.5RP", 1.0, 4.0), np.array([0.3290, 0.2095, 1.2100])), + (("2.5RP", 1.0, 6.0), np.array([0.3321, 0.1811, 1.2100])), + (("2.5RP", 1.0, 8.0), np.array([0.3342, 0.1551, 1.2100])), + (("2.5RP", 1.0, 10.0), np.array([0.3354, 0.1351, 1.2100])), + (("2.5RP", 1.0, 12.0), np.array([0.3361, 0.1181, 1.2100])), + (("2.5RP", 1.0, 14.0), np.array([0.3368, 0.1020, 1.2100])), + (("2.5RP", 1.0, 16.0), np.array([0.3368, 0.0902, 1.2100])), + (("2.5RP", 1.0, 18.0), np.array([0.3370, 0.0800, 1.2100])), + (("2.5RP", 1.0, 20.0), np.array([0.3360, 0.0700, 1.2100])), + (("2.5RP", 1.0, 22.0), np.array([0.3360, 0.0610, 1.2100])), + (("5RP", 1.0, 2.0), np.array([0.3378, 0.2542, 1.2100])), + (("5RP", 1.0, 4.0), np.array([0.3503, 0.2196, 1.2100])), + (("5RP", 1.0, 6.0), np.array([0.3588, 0.1920, 1.2100])), + (("5RP", 1.0, 8.0), np.array([0.3660, 0.1662, 1.2100])), + (("5RP", 1.0, 10.0), np.array([0.3727, 0.1458, 1.2100])), + (("5RP", 1.0, 12.0), np.array([0.3772, 0.1283, 1.2100])), + (("5RP", 1.0, 14.0), np.array([0.3811, 0.1138, 1.2100])), + (("5RP", 1.0, 16.0), np.array([0.3840, 0.1020, 1.2100])), + (("5RP", 1.0, 18.0), np.array([0.3860, 0.0920, 1.2100])), + (("5RP", 1.0, 20.0), np.array([0.3890, 0.0790, 1.2100])), + (("7.5RP", 1.0, 2.0), np.array([0.3498, 0.2617, 1.2100])), + (("7.5RP", 1.0, 4.0), np.array([0.3705, 0.2300, 1.2100])), + (("7.5RP", 1.0, 6.0), np.array([0.3865, 0.2036, 1.2100])), + (("7.5RP", 1.0, 8.0), np.array([0.4005, 0.1793, 1.2100])), + (("7.5RP", 1.0, 10.0), np.array([0.4132, 0.1580, 1.2100])), + (("7.5RP", 1.0, 12.0), np.array([0.4240, 0.1400, 1.2100])), + (("7.5RP", 1.0, 14.0), np.array([0.4330, 0.1250, 1.2100])), + (("7.5RP", 1.0, 16.0), np.array([0.4400, 0.1130, 1.2100])), + (("10RP", 1.0, 2.0), np.array([0.3629, 0.2710, 1.2100])), + (("10RP", 1.0, 4.0), np.array([0.3920, 0.2423, 1.2100])), + (("10RP", 1.0, 6.0), np.array([0.4151, 0.2169, 1.2100])), + (("10RP", 1.0, 8.0), np.array([0.4357, 0.1921, 1.2100])), + (("10RP", 1.0, 10.0), np.array([0.4521, 0.1710, 1.2100])), + (("10RP", 1.0, 12.0), np.array([0.4668, 0.1514, 1.2100])), + (("10RP", 1.0, 14.0), np.array([0.4790, 0.1360, 1.2100])), + (("10RP", 1.0, 16.0), np.array([0.4880, 0.1240, 1.2100])), + (("2.5R", 1.0, 2.0), np.array([0.3768, 0.2816, 1.2100])), + (("2.5R", 1.0, 4.0), np.array([0.4166, 0.2569, 1.2100])), + (("2.5R", 1.0, 6.0), np.array([0.4515, 0.2329, 1.2100])), + (("2.5R", 1.0, 8.0), np.array([0.4812, 0.2103, 1.2100])), + (("2.5R", 1.0, 10.0), np.array([0.5058, 0.1900, 1.2100])), + (("2.5R", 1.0, 12.0), np.array([0.5330, 0.1690, 1.2100])), + (("2.5R", 1.0, 14.0), np.array([0.5480, 0.1560, 1.2100])), + (("5R", 1.0, 2.0), np.array([0.3908, 0.2929, 1.2100])), + (("5R", 1.0, 4.0), np.array([0.4420, 0.2728, 1.2100])), + (("5R", 1.0, 6.0), np.array([0.4885, 0.2515, 1.2100])), + (("5R", 1.0, 8.0), np.array([0.5282, 0.2297, 1.2100])), + (("5R", 1.0, 10.0), np.array([0.5604, 0.2100, 1.2100])), + (("5R", 1.0, 12.0), np.array([0.5950, 0.1870, 1.2100])), + (("7.5R", 1.0, 2.0), np.array([0.4020, 0.3034, 1.2100])), + (("7.5R", 1.0, 4.0), np.array([0.4660, 0.2888, 1.2100])), + (("7.5R", 1.0, 6.0), np.array([0.5235, 0.2698, 1.2100])), + (("7.5R", 1.0, 8.0), np.array([0.5722, 0.2487, 1.2100])), + (("7.5R", 1.0, 10.0), np.array([0.6111, 0.2290, 1.2100])), + (("7.5R", 1.0, 12.0), np.array([0.6540, 0.2040, 1.2100])), + (("10R", 1.0, 2.0), np.array([0.4128, 0.3154, 1.2100])), + (("10R", 1.0, 4.0), np.array([0.4933, 0.3068, 1.2100])), + (("10R", 1.0, 6.0), np.array([0.5584, 0.2921, 1.2100])), + (("10R", 1.0, 8.0), np.array([0.6178, 0.2713, 1.2100])), + (("10R", 1.0, 10.0), np.array([0.6661, 0.2499, 1.2100])), + (("10R", 1.0, 12.0), np.array([0.7110, 0.2270, 1.2100])), + (("2.5YR", 1.0, 2.0), np.array([0.4258, 0.3344, 1.2100])), + (("2.5YR", 1.0, 4.0), np.array([0.5311, 0.3371, 1.2100])), + (("2.5YR", 1.0, 6.0), np.array([0.6048, 0.3270, 1.2100])), + (("2.5YR", 1.0, 8.0), np.array([0.6721, 0.3058, 1.2100])), + (("2.5YR", 1.0, 10.0), np.array([0.7270, 0.2790, 1.2100])), + (("2.5YR", 1.0, 12.0), np.array([0.7780, 0.2480, 1.2100])), + (("5YR", 1.0, 2.0), np.array([0.4377, 0.3580, 1.2100])), + (("5YR", 1.0, 4.0), np.array([0.5660, 0.3795, 1.2100])), + (("5YR", 1.0, 6.0), np.array([0.6560, 0.3840, 1.2100])), + (("5YR", 1.0, 8.0), np.array([0.7360, 0.3850, 1.2100])), + (("5YR", 1.0, 10.0), np.array([0.8290, 0.3850, 1.2100])), + (("7.5YR", 1.0, 2.0), np.array([0.4430, 0.3775, 1.2100])), + (("7.5YR", 1.0, 4.0), np.array([0.5850, 0.4220, 1.2100])), + (("7.5YR", 1.0, 6.0), np.array([0.6850, 0.4420, 1.2100])), + (("10YR", 1.0, 2.0), np.array([0.4446, 0.3982, 1.2100])), + (("10YR", 1.0, 4.0), np.array([0.5910, 0.4640, 1.2100])), + (("2.5Y", 1.0, 2.0), np.array([0.4362, 0.4177, 1.2100])), + (("2.5Y", 1.0, 4.0), np.array([0.5810, 0.5050, 1.2100])), + (("5Y", 1.0, 2.0), np.array([0.4230, 0.4265, 1.2100])), + (("5Y", 1.0, 4.0), np.array([0.5650, 0.5430, 1.2100])), + (("7.5Y", 1.0, 2.0), np.array([0.4042, 0.4287, 1.2100])), + (("7.5Y", 1.0, 4.0), np.array([0.5430, 0.5700, 1.2100])), + (("10Y", 2.0, 2.0), np.array([0.3556, 0.3848, 3.1260])), + (("10Y", 2.0, 4.0), np.array([0.4188, 0.4789, 3.1260])), + (("10Y", 2.0, 6.0), np.array([0.4820, 0.5760, 3.1260])), + (("2.5GY", 2.0, 2.0), np.array([0.3421, 0.3803, 3.1260])), + (("2.5GY", 2.0, 4.0), np.array([0.3881, 0.4752, 3.1260])), + (("2.5GY", 2.0, 6.0), np.array([0.4340, 0.5900, 3.1260])), + (("2.5GY", 2.0, 8.0), np.array([0.4760, 0.7160, 3.1260])), + (("5GY", 2.0, 2.0), np.array([0.3309, 0.3743, 3.1260])), + (("5GY", 2.0, 4.0), np.array([0.3582, 0.4650, 3.1260])), + (("5GY", 2.0, 6.0), np.array([0.3839, 0.5748, 3.1260])), + (("5GY", 2.0, 8.0), np.array([0.4090, 0.7100, 3.1260])), + (("5GY", 2.0, 10.0), np.array([0.4180, 0.8250, 3.1260])), + (("7.5GY", 2.0, 2.0), np.array([0.3165, 0.3650, 3.1260])), + (("7.5GY", 2.0, 4.0), np.array([0.3248, 0.4457, 3.1260])), + (("7.5GY", 2.0, 6.0), np.array([0.3260, 0.5379, 3.1260])), + (("7.5GY", 2.0, 8.0), np.array([0.3160, 0.6509, 3.1260])), + (("7.5GY", 2.0, 10.0), np.array([0.2970, 0.7680, 3.1260])), + (("7.5GY", 2.0, 12.0), np.array([0.2740, 0.8790, 3.1260])), + (("7.5GY", 2.0, 14.0), np.array([0.2430, 1.0100, 3.1260])), + (("10GY", 2.0, 2.0), np.array([0.3069, 0.3580, 3.1260])), + (("10GY", 2.0, 4.0), np.array([0.2986, 0.4240, 3.1260])), + (("10GY", 2.0, 6.0), np.array([0.2852, 0.4972, 3.1260])), + (("10GY", 2.0, 8.0), np.array([0.2628, 0.5837, 3.1260])), + (("10GY", 2.0, 10.0), np.array([0.2307, 0.6814, 3.1260])), + (("10GY", 2.0, 12.0), np.array([0.1907, 0.7798, 3.1260])), + (("10GY", 2.0, 14.0), np.array([0.1410, 0.8760, 3.1260])), + (("10GY", 2.0, 16.0), np.array([0.0610, 0.9980, 3.1260])), + (("10GY", 2.0, 18.0), np.array([-0.0300, 1.1100, 3.1260])), + (("2.5G", 2.0, 2.0), np.array([0.2978, 0.3507, 3.1260])), + (("2.5G", 2.0, 4.0), np.array([0.2763, 0.3998, 3.1260])), + (("2.5G", 2.0, 6.0), np.array([0.2493, 0.4522, 3.1260])), + (("2.5G", 2.0, 8.0), np.array([0.2192, 0.5042, 3.1260])), + (("2.5G", 2.0, 10.0), np.array([0.1773, 0.5698, 3.1260])), + (("2.5G", 2.0, 12.0), np.array([0.1307, 0.6308, 3.1260])), + (("2.5G", 2.0, 14.0), np.array([0.0820, 0.6860, 3.1260])), + (("2.5G", 2.0, 16.0), np.array([0.0329, 0.7358, 3.1260])), + (("2.5G", 2.0, 18.0), np.array([-0.0200, 0.7880, 3.1260])), + (("5G", 2.0, 2.0), np.array([0.2918, 0.3450, 3.1260])), + (("5G", 2.0, 4.0), np.array([0.2640, 0.3845, 3.1260])), + (("5G", 2.0, 6.0), np.array([0.2318, 0.4231, 3.1260])), + (("5G", 2.0, 8.0), np.array([0.1979, 0.4583, 3.1260])), + (("5G", 2.0, 10.0), np.array([0.1560, 0.4981, 3.1260])), + (("5G", 2.0, 12.0), np.array([0.1120, 0.5358, 3.1260])), + (("5G", 2.0, 14.0), np.array([0.0688, 0.5691, 3.1260])), + (("5G", 2.0, 16.0), np.array([0.0277, 0.5986, 3.1260])), + (("5G", 2.0, 18.0), np.array([-0.0150, 0.6250, 3.1260])), + (("7.5G", 2.0, 2.0), np.array([0.2869, 0.3400, 3.1260])), + (("7.5G", 2.0, 4.0), np.array([0.2540, 0.3705, 3.1260])), + (("7.5G", 2.0, 6.0), np.array([0.2200, 0.3983, 3.1260])), + (("7.5G", 2.0, 8.0), np.array([0.1842, 0.4244, 3.1260])), + (("7.5G", 2.0, 10.0), np.array([0.1442, 0.4505, 3.1260])), + (("7.5G", 2.0, 12.0), np.array([0.1022, 0.4759, 3.1260])), + (("7.5G", 2.0, 14.0), np.array([0.0629, 0.4973, 3.1260])), + (("7.5G", 2.0, 16.0), np.array([0.0276, 0.5153, 3.1260])), + (("7.5G", 2.0, 18.0), np.array([-0.0100, 0.5320, 3.1260])), + (("10G", 2.0, 2.0), np.array([0.2820, 0.3341, 3.1260])), + (("10G", 2.0, 4.0), np.array([0.2442, 0.3559, 3.1260])), + (("10G", 2.0, 6.0), np.array([0.2092, 0.3739, 3.1260])), + (("10G", 2.0, 8.0), np.array([0.1705, 0.3911, 3.1260])), + (("10G", 2.0, 10.0), np.array([0.1321, 0.4059, 3.1260])), + (("10G", 2.0, 12.0), np.array([0.0934, 0.4183, 3.1260])), + (("10G", 2.0, 14.0), np.array([0.0599, 0.4270, 3.1260])), + (("10G", 2.0, 16.0), np.array([0.0285, 0.4327, 3.1260])), + (("10G", 2.0, 18.0), np.array([-0.0070, 0.4410, 3.1260])), + (("2.5BG", 2.0, 2.0), np.array([0.2765, 0.3271, 3.1260])), + (("2.5BG", 2.0, 4.0), np.array([0.2343, 0.3378, 3.1260])), + (("2.5BG", 2.0, 6.0), np.array([0.1971, 0.3452, 3.1260])), + (("2.5BG", 2.0, 8.0), np.array([0.1557, 0.3517, 3.1260])), + (("2.5BG", 2.0, 10.0), np.array([0.1190, 0.3551, 3.1260])), + (("2.5BG", 2.0, 12.0), np.array([0.0851, 0.3576, 3.1260])), + (("2.5BG", 2.0, 14.0), np.array([0.0555, 0.3588, 3.1260])), + (("2.5BG", 2.0, 16.0), np.array([0.0290, 0.3590, 3.1260])), + (("2.5BG", 2.0, 18.0), np.array([-0.0020, 0.3600, 3.1260])), + (("5BG", 2.0, 2.0), np.array([0.2697, 0.3175, 3.1260])), + (("5BG", 2.0, 4.0), np.array([0.2234, 0.3150, 3.1260])), + (("5BG", 2.0, 6.0), np.array([0.1843, 0.3110, 3.1260])), + (("5BG", 2.0, 8.0), np.array([0.1405, 0.3037, 3.1260])), + (("5BG", 2.0, 10.0), np.array([0.1050, 0.2956, 3.1260])), + (("5BG", 2.0, 12.0), np.array([0.0769, 0.2880, 3.1260])), + (("5BG", 2.0, 14.0), np.array([0.0510, 0.2800, 3.1260])), + (("5BG", 2.0, 16.0), np.array([0.0280, 0.2740, 3.1260])), + (("7.5BG", 2.0, 2.0), np.array([0.2651, 0.3098, 3.1260])), + (("7.5BG", 2.0, 4.0), np.array([0.2162, 0.2981, 3.1260])), + (("7.5BG", 2.0, 6.0), np.array([0.1747, 0.2853, 3.1260])), + (("7.5BG", 2.0, 8.0), np.array([0.1325, 0.2710, 3.1260])), + (("7.5BG", 2.0, 10.0), np.array([0.0991, 0.2582, 3.1260])), + (("7.5BG", 2.0, 12.0), np.array([0.0724, 0.2478, 3.1260])), + (("7.5BG", 2.0, 14.0), np.array([0.0500, 0.2370, 3.1260])), + (("7.5BG", 2.0, 16.0), np.array([0.0280, 0.2280, 3.1260])), + (("10BG", 2.0, 2.0), np.array([0.2606, 0.3010, 3.1260])), + (("10BG", 2.0, 4.0), np.array([0.2096, 0.2790, 3.1260])), + (("10BG", 2.0, 6.0), np.array([0.1669, 0.2570, 3.1260])), + (("10BG", 2.0, 8.0), np.array([0.1258, 0.2331, 3.1260])), + (("10BG", 2.0, 10.0), np.array([0.0929, 0.2133, 3.1260])), + (("10BG", 2.0, 12.0), np.array([0.0680, 0.1940, 3.1260])), + (("10BG", 2.0, 14.0), np.array([0.0500, 0.1810, 3.1260])), + (("2.5B", 2.0, 2.0), np.array([0.2578, 0.2940, 3.1260])), + (("2.5B", 2.0, 4.0), np.array([0.2060, 0.2649, 3.1260])), + (("2.5B", 2.0, 6.0), np.array([0.1621, 0.2358, 3.1260])), + (("2.5B", 2.0, 8.0), np.array([0.1230, 0.2076, 3.1260])), + (("2.5B", 2.0, 10.0), np.array([0.0911, 0.1828, 3.1260])), + (("2.5B", 2.0, 12.0), np.array([0.0670, 0.1650, 3.1260])), + (("5B", 2.0, 2.0), np.array([0.2559, 0.2874, 3.1260])), + (("5B", 2.0, 4.0), np.array([0.2048, 0.2518, 3.1260])), + (("5B", 2.0, 6.0), np.array([0.1617, 0.2162, 3.1260])), + (("5B", 2.0, 8.0), np.array([0.1245, 0.1827, 3.1260])), + (("5B", 2.0, 10.0), np.array([0.0965, 0.1558, 3.1260])), + (("5B", 2.0, 12.0), np.array([0.0770, 0.1360, 3.1260])), + (("7.5B", 2.0, 2.0), np.array([0.2545, 0.2799, 3.1260])), + (("7.5B", 2.0, 4.0), np.array([0.2063, 0.2400, 3.1260])), + (("7.5B", 2.0, 6.0), np.array([0.1658, 0.2026, 3.1260])), + (("7.5B", 2.0, 8.0), np.array([0.1313, 0.1692, 3.1260])), + (("7.5B", 2.0, 10.0), np.array([0.1051, 0.1422, 3.1260])), + (("7.5B", 2.0, 12.0), np.array([0.0880, 0.1240, 3.1260])), + (("7.5B", 2.0, 14.0), np.array([0.0700, 0.1060, 3.1260])), + (("10B", 2.0, 2.0), np.array([0.2558, 0.2725, 3.1260])), + (("10B", 2.0, 4.0), np.array([0.2102, 0.2313, 3.1260])), + (("10B", 2.0, 6.0), np.array([0.1716, 0.1937, 3.1260])), + (("10B", 2.0, 8.0), np.array([0.1396, 0.1603, 3.1260])), + (("10B", 2.0, 10.0), np.array([0.1157, 0.1346, 3.1260])), + (("10B", 2.0, 12.0), np.array([0.0990, 0.1150, 3.1260])), + (("10B", 2.0, 14.0), np.array([0.0830, 0.0970, 3.1260])), + (("2.5PB", 2.0, 2.0), np.array([0.2592, 0.2675, 3.1260])), + (("2.5PB", 2.0, 4.0), np.array([0.2175, 0.2245, 3.1260])), + (("2.5PB", 2.0, 6.0), np.array([0.1825, 0.1857, 3.1260])), + (("2.5PB", 2.0, 8.0), np.array([0.1540, 0.1530, 3.1260])), + (("2.5PB", 2.0, 10.0), np.array([0.1332, 0.1278, 3.1260])), + (("2.5PB", 2.0, 12.0), np.array([0.1166, 0.1076, 3.1260])), + (("2.5PB", 2.0, 14.0), np.array([0.1020, 0.0890, 3.1260])), + (("2.5PB", 2.0, 16.0), np.array([0.0920, 0.0760, 3.1260])), + (("2.5PB", 2.0, 18.0), np.array([0.0840, 0.0640, 3.1260])), + (("5PB", 2.0, 2.0), np.array([0.2638, 0.2624, 3.1260])), + (("5PB", 2.0, 4.0), np.array([0.2263, 0.2192, 3.1260])), + (("5PB", 2.0, 6.0), np.array([0.1942, 0.1811, 3.1260])), + (("5PB", 2.0, 8.0), np.array([0.1685, 0.1491, 3.1260])), + (("5PB", 2.0, 10.0), np.array([0.1500, 0.1240, 3.1260])), + (("5PB", 2.0, 12.0), np.array([0.1363, 0.1048, 3.1260])), + (("5PB", 2.0, 14.0), np.array([0.1253, 0.0873, 3.1260])), + (("5PB", 2.0, 16.0), np.array([0.1170, 0.0740, 3.1260])), + (("5PB", 2.0, 18.0), np.array([0.1110, 0.0640, 3.1260])), + (("5PB", 2.0, 20.0), np.array([0.1080, 0.0580, 3.1260])), + (("5PB", 2.0, 22.0), np.array([0.1040, 0.0520, 3.1260])), + (("5PB", 2.0, 24.0), np.array([0.1000, 0.0460, 3.1260])), + (("5PB", 2.0, 26.0), np.array([0.0980, 0.0420, 3.1260])), + (("5PB", 2.0, 28.0), np.array([0.0970, 0.0400, 3.1260])), + (("5PB", 2.0, 30.0), np.array([0.0960, 0.0380, 3.1260])), + (("5PB", 2.0, 32.0), np.array([0.0950, 0.0360, 3.1260])), + (("5PB", 2.0, 34.0), np.array([0.0930, 0.0330, 3.1260])), + (("5PB", 2.0, 36.0), np.array([0.0920, 0.0310, 3.1260])), + (("5PB", 2.0, 38.0), np.array([0.0900, 0.0280, 3.1260])), + (("5PB", 2.0, 40.0), np.array([0.0890, 0.0250, 3.1260])), + (("5PB", 2.0, 42.0), np.array([0.0870, 0.0220, 3.1260])), + (("5PB", 2.0, 44.0), np.array([0.0860, 0.0190, 3.1260])), + (("5PB", 2.0, 46.0), np.array([0.0840, 0.0160, 3.1260])), + (("7.5PB", 2.0, 2.0), np.array([0.2712, 0.2582, 3.1260])), + (("7.5PB", 2.0, 4.0), np.array([0.2420, 0.2148, 3.1260])), + (("7.5PB", 2.0, 6.0), np.array([0.2189, 0.1790, 3.1260])), + (("7.5PB", 2.0, 8.0), np.array([0.2005, 0.1495, 3.1260])), + (("7.5PB", 2.0, 10.0), np.array([0.1882, 0.1258, 3.1260])), + (("7.5PB", 2.0, 12.0), np.array([0.1813, 0.1094, 3.1260])), + (("7.5PB", 2.0, 14.0), np.array([0.1762, 0.0955, 3.1260])), + (("7.5PB", 2.0, 16.0), np.array([0.1728, 0.0839, 3.1260])), + (("7.5PB", 2.0, 18.0), np.array([0.1701, 0.0742, 3.1260])), + (("7.5PB", 2.0, 20.0), np.array([0.1685, 0.0666, 3.1260])), + (("7.5PB", 2.0, 22.0), np.array([0.1670, 0.0594, 3.1260])), + (("7.5PB", 2.0, 24.0), np.array([0.1660, 0.0538, 3.1260])), + (("7.5PB", 2.0, 26.0), np.array([0.1653, 0.0492, 3.1260])), + (("7.5PB", 2.0, 28.0), np.array([0.1647, 0.0451, 3.1260])), + (("7.5PB", 2.0, 30.0), np.array([0.1640, 0.0409, 3.1260])), + (("7.5PB", 2.0, 32.0), np.array([0.1635, 0.0373, 3.1260])), + (("7.5PB", 2.0, 34.0), np.array([0.1630, 0.0340, 3.1260])), + (("7.5PB", 2.0, 36.0), np.array([0.1628, 0.0310, 3.1260])), + (("7.5PB", 2.0, 38.0), np.array([0.1623, 0.0280, 3.1260])), + (("7.5PB", 2.0, 40.0), np.array([0.1620, 0.0250, 3.1260])), + (("7.5PB", 2.0, 42.0), np.array([0.1620, 0.0220, 3.1260])), + (("7.5PB", 2.0, 44.0), np.array([0.1620, 0.0190, 3.1260])), + (("7.5PB", 2.0, 46.0), np.array([0.1610, 0.0160, 3.1260])), + (("7.5PB", 2.0, 48.0), np.array([0.1610, 0.0130, 3.1260])), + (("7.5PB", 2.0, 50.0), np.array([0.1600, 0.0100, 3.1260])), + (("10PB", 2.0, 2.0), np.array([0.2803, 0.2567, 3.1260])), + (("10PB", 2.0, 4.0), np.array([0.2600, 0.2162, 3.1260])), + (("10PB", 2.0, 6.0), np.array([0.2440, 0.1840, 3.1260])), + (("10PB", 2.0, 8.0), np.array([0.2294, 0.1551, 3.1260])), + (("10PB", 2.0, 10.0), np.array([0.2200, 0.1330, 3.1260])), + (("10PB", 2.0, 12.0), np.array([0.2139, 0.1170, 3.1260])), + (("10PB", 2.0, 14.0), np.array([0.2087, 0.1026, 3.1260])), + (("10PB", 2.0, 16.0), np.array([0.2052, 0.0910, 3.1260])), + (("10PB", 2.0, 18.0), np.array([0.2021, 0.0808, 3.1260])), + (("10PB", 2.0, 20.0), np.array([0.1998, 0.0718, 3.1260])), + (("10PB", 2.0, 22.0), np.array([0.1978, 0.0643, 3.1260])), + (("10PB", 2.0, 24.0), np.array([0.1962, 0.0578, 3.1260])), + (("10PB", 2.0, 26.0), np.array([0.1949, 0.0520, 3.1260])), + (("10PB", 2.0, 28.0), np.array([0.1937, 0.0471, 3.1260])), + (("10PB", 2.0, 30.0), np.array([0.1925, 0.0420, 3.1260])), + (("10PB", 2.0, 32.0), np.array([0.1918, 0.0379, 3.1260])), + (("10PB", 2.0, 34.0), np.array([0.1911, 0.0344, 3.1260])), + (("10PB", 2.0, 36.0), np.array([0.1900, 0.0310, 3.1260])), + (("10PB", 2.0, 38.0), np.array([0.1900, 0.0280, 3.1260])), + (("10PB", 2.0, 40.0), np.array([0.1900, 0.0250, 3.1260])), + (("10PB", 2.0, 42.0), np.array([0.1890, 0.0220, 3.1260])), + (("10PB", 2.0, 44.0), np.array([0.1890, 0.0190, 3.1260])), + (("10PB", 2.0, 46.0), np.array([0.1890, 0.0160, 3.1260])), + (("10PB", 2.0, 48.0), np.array([0.1880, 0.0130, 3.1260])), + (("10PB", 2.0, 50.0), np.array([0.1880, 0.0100, 3.1260])), + (("2.5P", 2.0, 2.0), np.array([0.2892, 0.2583, 3.1260])), + (("2.5P", 2.0, 4.0), np.array([0.2758, 0.2208, 3.1260])), + (("2.5P", 2.0, 6.0), np.array([0.2661, 0.1921, 3.1260])), + (("2.5P", 2.0, 8.0), np.array([0.2570, 0.1635, 3.1260])), + (("2.5P", 2.0, 10.0), np.array([0.2501, 0.1422, 3.1260])), + (("2.5P", 2.0, 12.0), np.array([0.2449, 0.1245, 3.1260])), + (("2.5P", 2.0, 14.0), np.array([0.2406, 0.1100, 3.1260])), + (("2.5P", 2.0, 16.0), np.array([0.2372, 0.0980, 3.1260])), + (("2.5P", 2.0, 18.0), np.array([0.2345, 0.0873, 3.1260])), + (("2.5P", 2.0, 20.0), np.array([0.2320, 0.0779, 3.1260])), + (("2.5P", 2.0, 22.0), np.array([0.2298, 0.0696, 3.1260])), + (("2.5P", 2.0, 24.0), np.array([0.2277, 0.0621, 3.1260])), + (("2.5P", 2.0, 26.0), np.array([0.2260, 0.0555, 3.1260])), + (("2.5P", 2.0, 28.0), np.array([0.2245, 0.0491, 3.1260])), + (("2.5P", 2.0, 30.0), np.array([0.2231, 0.0432, 3.1260])), + (("2.5P", 2.0, 32.0), np.array([0.2220, 0.0380, 3.1260])), + (("2.5P", 2.0, 34.0), np.array([0.2210, 0.0350, 3.1260])), + (("2.5P", 2.0, 36.0), np.array([0.2200, 0.0310, 3.1260])), + (("2.5P", 2.0, 38.0), np.array([0.2190, 0.0280, 3.1260])), + (("2.5P", 2.0, 40.0), np.array([0.2190, 0.0250, 3.1260])), + (("2.5P", 2.0, 42.0), np.array([0.2180, 0.0220, 3.1260])), + (("2.5P", 2.0, 44.0), np.array([0.2170, 0.0190, 3.1260])), + (("5P", 2.0, 2.0), np.array([0.2984, 0.2612, 3.1260])), + (("5P", 2.0, 4.0), np.array([0.2908, 0.2261, 3.1260])), + (("5P", 2.0, 6.0), np.array([0.2850, 0.1992, 3.1260])), + (("5P", 2.0, 8.0), np.array([0.2791, 0.1707, 3.1260])), + (("5P", 2.0, 10.0), np.array([0.2748, 0.1500, 3.1260])), + (("5P", 2.0, 12.0), np.array([0.2709, 0.1320, 3.1260])), + (("5P", 2.0, 14.0), np.array([0.2676, 0.1163, 3.1260])), + (("5P", 2.0, 16.0), np.array([0.2652, 0.1045, 3.1260])), + (("5P", 2.0, 18.0), np.array([0.2632, 0.0935, 3.1260])), + (("5P", 2.0, 20.0), np.array([0.2612, 0.0838, 3.1260])), + (("5P", 2.0, 22.0), np.array([0.2597, 0.0750, 3.1260])), + (("5P", 2.0, 24.0), np.array([0.2582, 0.0669, 3.1260])), + (("5P", 2.0, 26.0), np.array([0.2569, 0.0594, 3.1260])), + (("5P", 2.0, 28.0), np.array([0.2559, 0.0525, 3.1260])), + (("5P", 2.0, 30.0), np.array([0.2550, 0.0450, 3.1260])), + (("5P", 2.0, 32.0), np.array([0.2540, 0.0390, 3.1260])), + (("5P", 2.0, 34.0), np.array([0.2530, 0.0350, 3.1260])), + (("5P", 2.0, 36.0), np.array([0.2520, 0.0310, 3.1260])), + (("7.5P", 2.0, 2.0), np.array([0.3071, 0.2647, 3.1260])), + (("7.5P", 2.0, 4.0), np.array([0.3048, 0.2321, 3.1260])), + (("7.5P", 2.0, 6.0), np.array([0.3025, 0.2058, 3.1260])), + (("7.5P", 2.0, 8.0), np.array([0.3000, 0.1781, 3.1260])), + (("7.5P", 2.0, 10.0), np.array([0.2979, 0.1569, 3.1260])), + (("7.5P", 2.0, 12.0), np.array([0.2956, 0.1392, 3.1260])), + (("7.5P", 2.0, 14.0), np.array([0.2938, 0.1235, 3.1260])), + (("7.5P", 2.0, 16.0), np.array([0.2922, 0.1106, 3.1260])), + (("7.5P", 2.0, 18.0), np.array([0.2912, 0.0995, 3.1260])), + (("7.5P", 2.0, 20.0), np.array([0.2902, 0.0901, 3.1260])), + (("7.5P", 2.0, 22.0), np.array([0.2890, 0.0799, 3.1260])), + (("7.5P", 2.0, 24.0), np.array([0.2882, 0.0719, 3.1260])), + (("7.5P", 2.0, 26.0), np.array([0.2870, 0.0640, 3.1260])), + (("7.5P", 2.0, 28.0), np.array([0.2860, 0.0550, 3.1260])), + (("7.5P", 2.0, 30.0), np.array([0.2840, 0.0460, 3.1260])), + (("10P", 2.0, 2.0), np.array([0.3161, 0.2691, 3.1260])), + (("10P", 2.0, 4.0), np.array([0.3189, 0.2390, 3.1260])), + (("10P", 2.0, 6.0), np.array([0.3207, 0.2132, 3.1260])), + (("10P", 2.0, 8.0), np.array([0.3219, 0.1862, 3.1260])), + (("10P", 2.0, 10.0), np.array([0.3230, 0.1659, 3.1260])), + (("10P", 2.0, 12.0), np.array([0.3233, 0.1477, 3.1260])), + (("10P", 2.0, 14.0), np.array([0.3235, 0.1317, 3.1260])), + (("10P", 2.0, 16.0), np.array([0.3235, 0.1181, 3.1260])), + (("10P", 2.0, 18.0), np.array([0.3233, 0.1063, 3.1260])), + (("10P", 2.0, 20.0), np.array([0.3231, 0.0962, 3.1260])), + (("10P", 2.0, 22.0), np.array([0.3230, 0.0861, 3.1260])), + (("10P", 2.0, 24.0), np.array([0.3220, 0.0760, 3.1260])), + (("10P", 2.0, 26.0), np.array([0.3220, 0.0680, 3.1260])), + (("10P", 2.0, 28.0), np.array([0.3220, 0.0580, 3.1260])), + (("2.5RP", 2.0, 2.0), np.array([0.3279, 0.2754, 3.1260])), + (("2.5RP", 2.0, 4.0), np.array([0.3382, 0.2496, 3.1260])), + (("2.5RP", 2.0, 6.0), np.array([0.3470, 0.2259, 3.1260])), + (("2.5RP", 2.0, 8.0), np.array([0.3555, 0.2003, 3.1260])), + (("2.5RP", 2.0, 10.0), np.array([0.3617, 0.1800, 3.1260])), + (("2.5RP", 2.0, 12.0), np.array([0.3668, 0.1618, 3.1260])), + (("2.5RP", 2.0, 14.0), np.array([0.3711, 0.1449, 3.1260])), + (("2.5RP", 2.0, 16.0), np.array([0.3748, 0.1310, 3.1260])), + (("2.5RP", 2.0, 18.0), np.array([0.3778, 0.1188, 3.1260])), + (("2.5RP", 2.0, 20.0), np.array([0.3802, 0.1080, 3.1260])), + (("2.5RP", 2.0, 22.0), np.array([0.3830, 0.0960, 3.1260])), + (("2.5RP", 2.0, 24.0), np.array([0.3860, 0.0860, 3.1260])), + (("2.5RP", 2.0, 26.0), np.array([0.3880, 0.0760, 3.1260])), + (("5RP", 2.0, 2.0), np.array([0.3383, 0.2829, 3.1260])), + (("5RP", 2.0, 4.0), np.array([0.3558, 0.2597, 3.1260])), + (("5RP", 2.0, 6.0), np.array([0.3708, 0.2380, 3.1260])), + (("5RP", 2.0, 8.0), np.array([0.3858, 0.2140, 3.1260])), + (("5RP", 2.0, 10.0), np.array([0.3971, 0.1939, 3.1260])), + (("5RP", 2.0, 12.0), np.array([0.4080, 0.1764, 3.1260])), + (("5RP", 2.0, 14.0), np.array([0.4180, 0.1598, 3.1260])), + (("5RP", 2.0, 16.0), np.array([0.4269, 0.1454, 3.1260])), + (("5RP", 2.0, 18.0), np.array([0.4338, 0.1340, 3.1260])), + (("5RP", 2.0, 20.0), np.array([0.4420, 0.1210, 3.1260])), + (("5RP", 2.0, 22.0), np.array([0.4480, 0.1100, 3.1260])), + (("7.5RP", 2.0, 2.0), np.array([0.3459, 0.2892, 3.1260])), + (("7.5RP", 2.0, 4.0), np.array([0.3702, 0.2683, 3.1260])), + (("7.5RP", 2.0, 6.0), np.array([0.3918, 0.2490, 3.1260])), + (("7.5RP", 2.0, 8.0), np.array([0.4137, 0.2276, 3.1260])), + (("7.5RP", 2.0, 10.0), np.array([0.4321, 0.2082, 3.1260])), + (("7.5RP", 2.0, 12.0), np.array([0.4481, 0.1903, 3.1260])), + (("7.5RP", 2.0, 14.0), np.array([0.4624, 0.1737, 3.1260])), + (("7.5RP", 2.0, 16.0), np.array([0.4744, 0.1595, 3.1260])), + (("7.5RP", 2.0, 18.0), np.array([0.4850, 0.1460, 3.1260])), + (("7.5RP", 2.0, 20.0), np.array([0.4970, 0.1320, 3.1260])), + (("10RP", 2.0, 2.0), np.array([0.3532, 0.2957, 3.1260])), + (("10RP", 2.0, 4.0), np.array([0.3850, 0.2778, 3.1260])), + (("10RP", 2.0, 6.0), np.array([0.4139, 0.2608, 3.1260])), + (("10RP", 2.0, 8.0), np.array([0.4428, 0.2419, 3.1260])), + (("10RP", 2.0, 10.0), np.array([0.4678, 0.2237, 3.1260])), + (("10RP", 2.0, 12.0), np.array([0.4911, 0.2060, 3.1260])), + (("10RP", 2.0, 14.0), np.array([0.5129, 0.1888, 3.1260])), + (("10RP", 2.0, 16.0), np.array([0.5310, 0.1740, 3.1260])), + (("10RP", 2.0, 18.0), np.array([0.5460, 0.1610, 3.1260])), + (("2.5R", 2.0, 2.0), np.array([0.3614, 0.3033, 3.1260])), + (("2.5R", 2.0, 4.0), np.array([0.4021, 0.2900, 3.1260])), + (("2.5R", 2.0, 6.0), np.array([0.4390, 0.2760, 3.1260])), + (("2.5R", 2.0, 8.0), np.array([0.4776, 0.2593, 3.1260])), + (("2.5R", 2.0, 10.0), np.array([0.5122, 0.2428, 3.1260])), + (("2.5R", 2.0, 12.0), np.array([0.5438, 0.2254, 3.1260])), + (("2.5R", 2.0, 14.0), np.array([0.5734, 0.2083, 3.1260])), + (("2.5R", 2.0, 16.0), np.array([0.6010, 0.1920, 3.1260])), + (("5R", 2.0, 2.0), np.array([0.3692, 0.3111, 3.1260])), + (("5R", 2.0, 4.0), np.array([0.4184, 0.3032, 3.1260])), + (("5R", 2.0, 6.0), np.array([0.4642, 0.2934, 3.1260])), + (("5R", 2.0, 8.0), np.array([0.5143, 0.2800, 3.1260])), + (("5R", 2.0, 10.0), np.array([0.5557, 0.2633, 3.1260])), + (("5R", 2.0, 12.0), np.array([0.5930, 0.2465, 3.1260])), + (("5R", 2.0, 14.0), np.array([0.6302, 0.2287, 3.1260])), + (("5R", 2.0, 16.0), np.array([0.6590, 0.2120, 3.1260])), + (("7.5R", 2.0, 2.0), np.array([0.3751, 0.3181, 3.1260])), + (("7.5R", 2.0, 4.0), np.array([0.4335, 0.3169, 3.1260])), + (("7.5R", 2.0, 6.0), np.array([0.4875, 0.3123, 3.1260])), + (("7.5R", 2.0, 8.0), np.array([0.5433, 0.3027, 3.1260])), + (("7.5R", 2.0, 10.0), np.array([0.5952, 0.2874, 3.1260])), + (("7.5R", 2.0, 12.0), np.array([0.6392, 0.2704, 3.1260])), + (("7.5R", 2.0, 14.0), np.array([0.6791, 0.2520, 3.1260])), + (("7.5R", 2.0, 16.0), np.array([0.7140, 0.2340, 3.1260])), + (("10R", 2.0, 2.0), np.array([0.3811, 0.3274, 3.1260])), + (("10R", 2.0, 4.0), np.array([0.4481, 0.3330, 3.1260])), + (("10R", 2.0, 6.0), np.array([0.5095, 0.3331, 3.1260])), + (("10R", 2.0, 8.0), np.array([0.5713, 0.3259, 3.1260])), + (("10R", 2.0, 10.0), np.array([0.6247, 0.3120, 3.1260])), + (("10R", 2.0, 12.0), np.array([0.6732, 0.2937, 3.1260])), + (("10R", 2.0, 14.0), np.array([0.7165, 0.2734, 3.1260])), + (("10R", 2.0, 16.0), np.array([0.7520, 0.2540, 3.1260])), + (("2.5YR", 2.0, 2.0), np.array([0.3852, 0.3365, 3.1260])), + (("2.5YR", 2.0, 4.0), np.array([0.4598, 0.3508, 3.1260])), + (("2.5YR", 2.0, 6.0), np.array([0.5280, 0.3581, 3.1260])), + (("2.5YR", 2.0, 8.0), np.array([0.5995, 0.3590, 3.1260])), + (("2.5YR", 2.0, 10.0), np.array([0.6590, 0.3500, 3.1260])), + (("2.5YR", 2.0, 12.0), np.array([0.7180, 0.3400, 3.1260])), + (("2.5YR", 2.0, 14.0), np.array([0.7790, 0.3230, 3.1260])), + (("2.5YR", 2.0, 16.0), np.array([0.8240, 0.3090, 3.1260])), + (("5YR", 2.0, 2.0), np.array([0.3880, 0.3476, 3.1260])), + (("5YR", 2.0, 4.0), np.array([0.4674, 0.3738, 3.1260])), + (("5YR", 2.0, 6.0), np.array([0.5426, 0.3925, 3.1260])), + (("5YR", 2.0, 8.0), np.array([0.6200, 0.4060, 3.1260])), + (("5YR", 2.0, 10.0), np.array([0.6840, 0.4150, 3.1260])), + (("7.5YR", 2.0, 2.0), np.array([0.3889, 0.3590, 3.1260])), + (("7.5YR", 2.0, 4.0), np.array([0.4690, 0.3964, 3.1260])), + (("7.5YR", 2.0, 6.0), np.array([0.5475, 0.4271, 3.1260])), + (("7.5YR", 2.0, 8.0), np.array([0.6200, 0.4560, 3.1260])), + (("10YR", 2.0, 2.0), np.array([0.3872, 0.3688, 3.1260])), + (("10YR", 2.0, 4.0), np.array([0.4676, 0.4168, 3.1260])), + (("10YR", 2.0, 6.0), np.array([0.5450, 0.4580, 3.1260])), + (("10YR", 2.0, 8.0), np.array([0.6120, 0.4930, 3.1260])), + (("2.5Y", 2.0, 2.0), np.array([0.3825, 0.3785, 3.1260])), + (("2.5Y", 2.0, 4.0), np.array([0.4627, 0.4392, 3.1260])), + (("2.5Y", 2.0, 6.0), np.array([0.5380, 0.4860, 3.1260])), + (("2.5Y", 2.0, 8.0), np.array([0.6040, 0.5260, 3.1260])), + (("5Y", 2.0, 2.0), np.array([0.3757, 0.3839, 3.1260])), + (("5Y", 2.0, 4.0), np.array([0.4543, 0.4573, 3.1260])), + (("5Y", 2.0, 6.0), np.array([0.5260, 0.5190, 3.1260])), + (("7.5Y", 2.0, 2.0), np.array([0.3660, 0.3858, 3.1260])), + (("7.5Y", 2.0, 4.0), np.array([0.4401, 0.4723, 3.1260])), + (("7.5Y", 2.0, 6.0), np.array([0.5100, 0.5470, 3.1260])), + (("10Y", 3.0, 2.0), np.array([0.3513, 0.3789, 6.5500])), + (("10Y", 3.0, 4.0), np.array([0.3961, 0.4452, 6.5500])), + (("10Y", 3.0, 6.0), np.array([0.4345, 0.5026, 6.5500])), + (("10Y", 3.0, 8.0), np.array([0.4700, 0.5550, 6.5500])), + (("2.5GY", 3.0, 2.0), np.array([0.3412, 0.3768, 6.5500])), + (("2.5GY", 3.0, 4.0), np.array([0.3772, 0.4484, 6.5500])), + (("2.5GY", 3.0, 6.0), np.array([0.4069, 0.5110, 6.5500])), + (("2.5GY", 3.0, 8.0), np.array([0.4320, 0.5760, 6.5500])), + (("2.5GY", 3.0, 10.0), np.array([0.4550, 0.6380, 6.5500])), + (("5GY", 3.0, 2.0), np.array([0.3319, 0.3729, 6.5500])), + (("5GY", 3.0, 4.0), np.array([0.3554, 0.4429, 6.5500])), + (("5GY", 3.0, 6.0), np.array([0.3750, 0.5109, 6.5500])), + (("5GY", 3.0, 8.0), np.array([0.3924, 0.5832, 6.5500])), + (("5GY", 3.0, 10.0), np.array([0.4040, 0.6500, 6.5500])), + (("5GY", 3.0, 12.0), np.array([0.4130, 0.7180, 6.5500])), + (("7.5GY", 3.0, 2.0), np.array([0.3180, 0.3644, 6.5500])), + (("7.5GY", 3.0, 4.0), np.array([0.3270, 0.4288, 6.5500])), + (("7.5GY", 3.0, 6.0), np.array([0.3333, 0.4967, 6.5500])), + (("7.5GY", 3.0, 8.0), np.array([0.3341, 0.5700, 6.5500])), + (("7.5GY", 3.0, 10.0), np.array([0.3266, 0.6448, 6.5500])), + (("7.5GY", 3.0, 12.0), np.array([0.3150, 0.7200, 6.5500])), + (("7.5GY", 3.0, 14.0), np.array([0.3000, 0.7930, 6.5500])), + (("10GY", 3.0, 2.0), np.array([0.3088, 0.3578, 6.5500])), + (("10GY", 3.0, 4.0), np.array([0.3053, 0.4123, 6.5500])), + (("10GY", 3.0, 6.0), np.array([0.2992, 0.4717, 6.5500])), + (("10GY", 3.0, 8.0), np.array([0.2887, 0.5361, 6.5500])), + (("10GY", 3.0, 10.0), np.array([0.2724, 0.6026, 6.5500])), + (("10GY", 3.0, 12.0), np.array([0.2531, 0.6700, 6.5500])), + (("10GY", 3.0, 14.0), np.array([0.2283, 0.7423, 6.5500])), + (("10GY", 3.0, 16.0), np.array([0.2020, 0.8070, 6.5500])), + (("10GY", 3.0, 18.0), np.array([0.1680, 0.8800, 6.5500])), + (("10GY", 3.0, 20.0), np.array([0.1300, 0.9480, 6.5500])), + (("10GY", 3.0, 22.0), np.array([0.0890, 1.0140, 6.5500])), + (("10GY", 3.0, 24.0), np.array([0.0460, 1.0780, 6.5500])), + (("2.5G", 3.0, 2.0), np.array([0.2999, 0.3500, 6.5500])), + (("2.5G", 3.0, 4.0), np.array([0.2836, 0.3915, 6.5500])), + (("2.5G", 3.0, 6.0), np.array([0.2642, 0.4342, 6.5500])), + (("2.5G", 3.0, 8.0), np.array([0.2435, 0.4752, 6.5500])), + (("2.5G", 3.0, 10.0), np.array([0.2170, 0.5211, 6.5500])), + (("2.5G", 3.0, 12.0), np.array([0.1902, 0.5642, 6.5500])), + (("2.5G", 3.0, 14.0), np.array([0.1626, 0.6052, 6.5500])), + (("2.5G", 3.0, 16.0), np.array([0.1341, 0.6420, 6.5500])), + (("2.5G", 3.0, 18.0), np.array([0.1049, 0.6766, 6.5500])), + (("2.5G", 3.0, 20.0), np.array([0.0720, 0.7127, 6.5500])), + (("2.5G", 3.0, 22.0), np.array([0.0390, 0.7468, 6.5500])), + (("2.5G", 3.0, 24.0), np.array([0.0090, 0.7760, 6.5500])), + (("5G", 3.0, 2.0), np.array([0.2935, 0.3439, 6.5500])), + (("5G", 3.0, 4.0), np.array([0.2711, 0.3780, 6.5500])), + (("5G", 3.0, 6.0), np.array([0.2471, 0.4100, 6.5500])), + (("5G", 3.0, 8.0), np.array([0.2228, 0.4380, 6.5500])), + (("5G", 3.0, 10.0), np.array([0.1935, 0.4682, 6.5500])), + (("5G", 3.0, 12.0), np.array([0.1660, 0.4948, 6.5500])), + (("5G", 3.0, 14.0), np.array([0.1382, 0.5197, 6.5500])), + (("5G", 3.0, 16.0), np.array([0.1120, 0.5414, 6.5500])), + (("5G", 3.0, 18.0), np.array([0.0882, 0.5605, 6.5500])), + (("5G", 3.0, 20.0), np.array([0.0620, 0.5802, 6.5500])), + (("5G", 3.0, 22.0), np.array([0.0340, 0.6011, 6.5500])), + (("5G", 3.0, 24.0), np.array([0.0040, 0.6220, 6.5500])), + (("7.5G", 3.0, 2.0), np.array([0.2890, 0.3391, 6.5500])), + (("7.5G", 3.0, 4.0), np.array([0.2618, 0.3667, 6.5500])), + (("7.5G", 3.0, 6.0), np.array([0.2346, 0.3901, 6.5500])), + (("7.5G", 3.0, 8.0), np.array([0.2088, 0.4101, 6.5500])), + (("7.5G", 3.0, 10.0), np.array([0.1800, 0.4310, 6.5500])), + (("7.5G", 3.0, 12.0), np.array([0.1516, 0.4505, 6.5500])), + (("7.5G", 3.0, 14.0), np.array([0.1262, 0.4667, 6.5500])), + (("7.5G", 3.0, 16.0), np.array([0.1023, 0.4818, 6.5500])), + (("7.5G", 3.0, 18.0), np.array([0.0798, 0.4954, 6.5500])), + (("7.5G", 3.0, 20.0), np.array([0.0568, 0.5082, 6.5500])), + (("7.5G", 3.0, 22.0), np.array([0.0332, 0.5206, 6.5500])), + (("7.5G", 3.0, 24.0), np.array([0.0060, 0.5340, 6.5500])), + (("10G", 3.0, 2.0), np.array([0.2844, 0.3337, 6.5500])), + (("10G", 3.0, 4.0), np.array([0.2525, 0.3537, 6.5500])), + (("10G", 3.0, 6.0), np.array([0.2240, 0.3699, 6.5500])), + (("10G", 3.0, 8.0), np.array([0.1970, 0.3841, 6.5500])), + (("10G", 3.0, 10.0), np.array([0.1688, 0.3974, 6.5500])), + (("10G", 3.0, 12.0), np.array([0.1411, 0.4095, 6.5500])), + (("10G", 3.0, 14.0), np.array([0.1161, 0.4192, 6.5500])), + (("10G", 3.0, 16.0), np.array([0.0925, 0.4275, 6.5500])), + (("10G", 3.0, 18.0), np.array([0.0718, 0.4340, 6.5500])), + (("10G", 3.0, 20.0), np.array([0.0528, 0.4393, 6.5500])), + (("10G", 3.0, 22.0), np.array([0.0333, 0.4444, 6.5500])), + (("10G", 3.0, 24.0), np.array([0.0090, 0.4500, 6.5500])), + (("2.5BG", 3.0, 2.0), np.array([0.2799, 0.3271, 6.5500])), + (("2.5BG", 3.0, 4.0), np.array([0.2437, 0.3386, 6.5500])), + (("2.5BG", 3.0, 6.0), np.array([0.2132, 0.3468, 6.5500])), + (("2.5BG", 3.0, 8.0), np.array([0.1845, 0.3531, 6.5500])), + (("2.5BG", 3.0, 10.0), np.array([0.1552, 0.3580, 6.5500])), + (("2.5BG", 3.0, 12.0), np.array([0.1288, 0.3620, 6.5500])), + (("2.5BG", 3.0, 14.0), np.array([0.1051, 0.3648, 6.5500])), + (("2.5BG", 3.0, 16.0), np.array([0.0843, 0.3667, 6.5500])), + (("2.5BG", 3.0, 18.0), np.array([0.0648, 0.3682, 6.5500])), + (("2.5BG", 3.0, 20.0), np.array([0.0482, 0.3695, 6.5500])), + (("2.5BG", 3.0, 22.0), np.array([0.0320, 0.3700, 6.5500])), + (("2.5BG", 3.0, 24.0), np.array([0.0120, 0.3710, 6.5500])), + (("5BG", 3.0, 2.0), np.array([0.2742, 0.3192, 6.5500])), + (("5BG", 3.0, 4.0), np.array([0.2343, 0.3200, 6.5500])), + (("5BG", 3.0, 6.0), np.array([0.2020, 0.3188, 6.5500])), + (("5BG", 3.0, 8.0), np.array([0.1703, 0.3159, 6.5500])), + (("5BG", 3.0, 10.0), np.array([0.1410, 0.3118, 6.5500])), + (("5BG", 3.0, 12.0), np.array([0.1158, 0.3071, 6.5500])), + (("5BG", 3.0, 14.0), np.array([0.0940, 0.3027, 6.5500])), + (("5BG", 3.0, 16.0), np.array([0.0735, 0.2979, 6.5500])), + (("5BG", 3.0, 18.0), np.array([0.0580, 0.2940, 6.5500])), + (("5BG", 3.0, 20.0), np.array([0.0430, 0.2910, 6.5500])), + (("5BG", 3.0, 22.0), np.array([0.0280, 0.2860, 6.5500])), + (("7.5BG", 3.0, 2.0), np.array([0.2699, 0.3120, 6.5500])), + (("7.5BG", 3.0, 4.0), np.array([0.2272, 0.3041, 6.5500])), + (("7.5BG", 3.0, 6.0), np.array([0.1928, 0.2958, 6.5500])), + (("7.5BG", 3.0, 8.0), np.array([0.1620, 0.2872, 6.5500])), + (("7.5BG", 3.0, 10.0), np.array([0.1326, 0.2784, 6.5500])), + (("7.5BG", 3.0, 12.0), np.array([0.1086, 0.2706, 6.5500])), + (("7.5BG", 3.0, 14.0), np.array([0.0874, 0.2627, 6.5500])), + (("7.5BG", 3.0, 16.0), np.array([0.0691, 0.2559, 6.5500])), + (("7.5BG", 3.0, 18.0), np.array([0.0530, 0.2490, 6.5500])), + (("7.5BG", 3.0, 20.0), np.array([0.0390, 0.2420, 6.5500])), + (("10BG", 3.0, 2.0), np.array([0.2660, 0.3050, 6.5500])), + (("10BG", 3.0, 4.0), np.array([0.2221, 0.2886, 6.5500])), + (("10BG", 3.0, 6.0), np.array([0.1861, 0.2722, 6.5500])), + (("10BG", 3.0, 8.0), np.array([0.1551, 0.2571, 6.5500])), + (("10BG", 3.0, 10.0), np.array([0.1250, 0.2411, 6.5500])), + (("10BG", 3.0, 12.0), np.array([0.1018, 0.2281, 6.5500])), + (("10BG", 3.0, 14.0), np.array([0.0798, 0.2151, 6.5500])), + (("10BG", 3.0, 16.0), np.array([0.0650, 0.2060, 6.5500])), + (("10BG", 3.0, 18.0), np.array([0.0530, 0.1990, 6.5500])), + (("2.5B", 3.0, 2.0), np.array([0.2636, 0.2983, 6.5500])), + (("2.5B", 3.0, 4.0), np.array([0.2183, 0.2748, 6.5500])), + (("2.5B", 3.0, 6.0), np.array([0.1826, 0.2536, 6.5500])), + (("2.5B", 3.0, 8.0), np.array([0.1511, 0.2331, 6.5500])), + (("2.5B", 3.0, 10.0), np.array([0.1220, 0.2132, 6.5500])), + (("2.5B", 3.0, 12.0), np.array([0.0989, 0.1963, 6.5500])), + (("2.5B", 3.0, 14.0), np.array([0.0800, 0.1800, 6.5500])), + (("2.5B", 3.0, 16.0), np.array([0.0650, 0.1700, 6.5500])), + (("5B", 3.0, 2.0), np.array([0.2617, 0.2921, 6.5500])), + (("5B", 3.0, 4.0), np.array([0.2176, 0.2632, 6.5500])), + (("5B", 3.0, 6.0), np.array([0.1835, 0.2375, 6.5500])), + (("5B", 3.0, 8.0), np.array([0.1527, 0.2119, 6.5500])), + (("5B", 3.0, 10.0), np.array([0.1259, 0.1879, 6.5500])), + (("5B", 3.0, 12.0), np.array([0.1042, 0.1681, 6.5500])), + (("5B", 3.0, 14.0), np.array([0.0860, 0.1500, 6.5500])), + (("5B", 3.0, 16.0), np.array([0.0710, 0.1370, 6.5500])), + (("7.5B", 3.0, 2.0), np.array([0.2616, 0.2857, 6.5500])), + (("7.5B", 3.0, 4.0), np.array([0.2200, 0.2536, 6.5500])), + (("7.5B", 3.0, 6.0), np.array([0.1875, 0.2258, 6.5500])), + (("7.5B", 3.0, 8.0), np.array([0.1583, 0.1987, 6.5500])), + (("7.5B", 3.0, 10.0), np.array([0.1343, 0.1756, 6.5500])), + (("7.5B", 3.0, 12.0), np.array([0.1131, 0.1542, 6.5500])), + (("7.5B", 3.0, 14.0), np.array([0.0950, 0.1360, 6.5500])), + (("7.5B", 3.0, 16.0), np.array([0.0830, 0.1230, 6.5500])), + (("10B", 3.0, 2.0), np.array([0.2631, 0.2801, 6.5500])), + (("10B", 3.0, 4.0), np.array([0.2246, 0.2467, 6.5500])), + (("10B", 3.0, 6.0), np.array([0.1933, 0.2173, 6.5500])), + (("10B", 3.0, 8.0), np.array([0.1658, 0.1905, 6.5500])), + (("10B", 3.0, 10.0), np.array([0.1432, 0.1675, 6.5500])), + (("10B", 3.0, 12.0), np.array([0.1228, 0.1460, 6.5500])), + (("10B", 3.0, 14.0), np.array([0.1065, 0.1285, 6.5500])), + (("10B", 3.0, 16.0), np.array([0.0950, 0.1150, 6.5500])), + (("10B", 3.0, 18.0), np.array([0.0840, 0.1000, 6.5500])), + (("2.5PB", 3.0, 2.0), np.array([0.2663, 0.2756, 6.5500])), + (("2.5PB", 3.0, 4.0), np.array([0.2312, 0.2405, 6.5500])), + (("2.5PB", 3.0, 6.0), np.array([0.2022, 0.2101, 6.5500])), + (("2.5PB", 3.0, 8.0), np.array([0.1780, 0.1833, 6.5500])), + (("2.5PB", 3.0, 10.0), np.array([0.1576, 0.1600, 6.5500])), + (("2.5PB", 3.0, 12.0), np.array([0.1398, 0.1395, 6.5500])), + (("2.5PB", 3.0, 14.0), np.array([0.1251, 0.1218, 6.5500])), + (("2.5PB", 3.0, 16.0), np.array([0.1130, 0.1070, 6.5500])), + (("2.5PB", 3.0, 18.0), np.array([0.1020, 0.0930, 6.5500])), + (("2.5PB", 3.0, 20.0), np.array([0.0950, 0.0830, 6.5500])), + (("2.5PB", 3.0, 22.0), np.array([0.0880, 0.0730, 6.5500])), + (("5PB", 3.0, 2.0), np.array([0.2708, 0.2719, 6.5500])), + (("5PB", 3.0, 4.0), np.array([0.2393, 0.2361, 6.5500])), + (("5PB", 3.0, 6.0), np.array([0.2122, 0.2052, 6.5500])), + (("5PB", 3.0, 8.0), np.array([0.1908, 0.1799, 6.5500])), + (("5PB", 3.0, 10.0), np.array([0.1718, 0.1562, 6.5500])), + (("5PB", 3.0, 12.0), np.array([0.1557, 0.1356, 6.5500])), + (("5PB", 3.0, 14.0), np.array([0.1431, 0.1184, 6.5500])), + (("5PB", 3.0, 16.0), np.array([0.1318, 0.1024, 6.5500])), + (("5PB", 3.0, 18.0), np.array([0.1228, 0.0895, 6.5500])), + (("5PB", 3.0, 20.0), np.array([0.1170, 0.0800, 6.5500])), + (("5PB", 3.0, 22.0), np.array([0.1120, 0.0720, 6.5500])), + (("5PB", 3.0, 24.0), np.array([0.1070, 0.0640, 6.5500])), + (("5PB", 3.0, 26.0), np.array([0.1040, 0.0590, 6.5500])), + (("5PB", 3.0, 28.0), np.array([0.1020, 0.0540, 6.5500])), + (("5PB", 3.0, 30.0), np.array([0.0990, 0.0500, 6.5500])), + (("5PB", 3.0, 32.0), np.array([0.0970, 0.0450, 6.5500])), + (("5PB", 3.0, 34.0), np.array([0.0950, 0.0420, 6.5500])), + (("5PB", 3.0, 36.0), np.array([0.0940, 0.0400, 6.5500])), + (("5PB", 3.0, 38.0), np.array([0.0920, 0.0370, 6.5500])), + (("5PB", 3.0, 40.0), np.array([0.0910, 0.0340, 6.5500])), + (("5PB", 3.0, 42.0), np.array([0.0900, 0.0320, 6.5500])), + (("5PB", 3.0, 44.0), np.array([0.0890, 0.0300, 6.5500])), + (("5PB", 3.0, 46.0), np.array([0.0880, 0.0280, 6.5500])), + (("5PB", 3.0, 48.0), np.array([0.0870, 0.0250, 6.5500])), + (("5PB", 3.0, 50.0), np.array([0.0860, 0.0220, 6.5500])), + (("7.5PB", 3.0, 2.0), np.array([0.2777, 0.2687, 6.5500])), + (("7.5PB", 3.0, 4.0), np.array([0.2520, 0.2319, 6.5500])), + (("7.5PB", 3.0, 6.0), np.array([0.2311, 0.2010, 6.5500])), + (("7.5PB", 3.0, 8.0), np.array([0.2149, 0.1761, 6.5500])), + (("7.5PB", 3.0, 10.0), np.array([0.2005, 0.1536, 6.5500])), + (("7.5PB", 3.0, 12.0), np.array([0.1903, 0.1353, 6.5500])), + (("7.5PB", 3.0, 14.0), np.array([0.1824, 0.1188, 6.5500])), + (("7.5PB", 3.0, 16.0), np.array([0.1765, 0.1048, 6.5500])), + (("7.5PB", 3.0, 18.0), np.array([0.1730, 0.0948, 6.5500])), + (("7.5PB", 3.0, 20.0), np.array([0.1702, 0.0867, 6.5500])), + (("7.5PB", 3.0, 22.0), np.array([0.1677, 0.0782, 6.5500])), + (("7.5PB", 3.0, 24.0), np.array([0.1658, 0.0711, 6.5500])), + (("7.5PB", 3.0, 26.0), np.array([0.1642, 0.0655, 6.5500])), + (("7.5PB", 3.0, 28.0), np.array([0.1632, 0.0609, 6.5500])), + (("7.5PB", 3.0, 30.0), np.array([0.1621, 0.0556, 6.5500])), + (("7.5PB", 3.0, 32.0), np.array([0.1612, 0.0511, 6.5500])), + (("7.5PB", 3.0, 34.0), np.array([0.1608, 0.0480, 6.5500])), + (("7.5PB", 3.0, 36.0), np.array([0.1590, 0.0440, 6.5500])), + (("7.5PB", 3.0, 38.0), np.array([0.1580, 0.0400, 6.5500])), + (("7.5PB", 3.0, 40.0), np.array([0.1580, 0.0370, 6.5500])), + (("7.5PB", 3.0, 42.0), np.array([0.1570, 0.0340, 6.5500])), + (("7.5PB", 3.0, 44.0), np.array([0.1570, 0.0310, 6.5500])), + (("7.5PB", 3.0, 46.0), np.array([0.1570, 0.0280, 6.5500])), + (("7.5PB", 3.0, 48.0), np.array([0.1560, 0.0250, 6.5500])), + (("7.5PB", 3.0, 50.0), np.array([0.1560, 0.0220, 6.5500])), + (("10PB", 3.0, 2.0), np.array([0.2847, 0.2670, 6.5500])), + (("10PB", 3.0, 4.0), np.array([0.2660, 0.2319, 6.5500])), + (("10PB", 3.0, 6.0), np.array([0.2511, 0.2031, 6.5500])), + (("10PB", 3.0, 8.0), np.array([0.2387, 0.1786, 6.5500])), + (("10PB", 3.0, 10.0), np.array([0.2278, 0.1565, 6.5500])), + (("10PB", 3.0, 12.0), np.array([0.2206, 0.1407, 6.5500])), + (("10PB", 3.0, 14.0), np.array([0.2142, 0.1250, 6.5500])), + (("10PB", 3.0, 16.0), np.array([0.2092, 0.1118, 6.5500])), + (("10PB", 3.0, 18.0), np.array([0.2060, 0.1020, 6.5500])), + (("10PB", 3.0, 20.0), np.array([0.2030, 0.0930, 6.5500])), + (("10PB", 3.0, 22.0), np.array([0.2004, 0.0847, 6.5500])), + (("10PB", 3.0, 24.0), np.array([0.1982, 0.0772, 6.5500])), + (("10PB", 3.0, 26.0), np.array([0.1963, 0.0708, 6.5500])), + (("10PB", 3.0, 28.0), np.array([0.1950, 0.0650, 6.5500])), + (("10PB", 3.0, 30.0), np.array([0.1938, 0.0599, 6.5500])), + (("10PB", 3.0, 32.0), np.array([0.1926, 0.0542, 6.5500])), + (("10PB", 3.0, 34.0), np.array([0.1918, 0.0503, 6.5500])), + (("10PB", 3.0, 36.0), np.array([0.1900, 0.0460, 6.5500])), + (("10PB", 3.0, 38.0), np.array([0.1900, 0.0420, 6.5500])), + (("10PB", 3.0, 40.0), np.array([0.1890, 0.0380, 6.5500])), + (("10PB", 3.0, 42.0), np.array([0.1890, 0.0340, 6.5500])), + (("10PB", 3.0, 44.0), np.array([0.1880, 0.0310, 6.5500])), + (("10PB", 3.0, 46.0), np.array([0.1880, 0.0280, 6.5500])), + (("10PB", 3.0, 48.0), np.array([0.1880, 0.0250, 6.5500])), + (("10PB", 3.0, 50.0), np.array([0.1880, 0.0220, 6.5500])), + (("2.5P", 3.0, 2.0), np.array([0.2922, 0.2680, 6.5500])), + (("2.5P", 3.0, 4.0), np.array([0.2792, 0.2342, 6.5500])), + (("2.5P", 3.0, 6.0), np.array([0.2691, 0.2072, 6.5500])), + (("2.5P", 3.0, 8.0), np.array([0.2615, 0.1845, 6.5500])), + (("2.5P", 3.0, 10.0), np.array([0.2548, 0.1638, 6.5500])), + (("2.5P", 3.0, 12.0), np.array([0.2498, 0.1480, 6.5500])), + (("2.5P", 3.0, 14.0), np.array([0.2449, 0.1325, 6.5500])), + (("2.5P", 3.0, 16.0), np.array([0.2410, 0.1198, 6.5500])), + (("2.5P", 3.0, 18.0), np.array([0.2380, 0.1094, 6.5500])), + (("2.5P", 3.0, 20.0), np.array([0.2354, 0.1003, 6.5500])), + (("2.5P", 3.0, 22.0), np.array([0.2329, 0.0911, 6.5500])), + (("2.5P", 3.0, 24.0), np.array([0.2305, 0.0832, 6.5500])), + (("2.5P", 3.0, 26.0), np.array([0.2286, 0.0765, 6.5500])), + (("2.5P", 3.0, 28.0), np.array([0.2268, 0.0698, 6.5500])), + (("2.5P", 3.0, 30.0), np.array([0.2252, 0.0638, 6.5500])), + (("2.5P", 3.0, 32.0), np.array([0.2242, 0.0587, 6.5500])), + (("2.5P", 3.0, 34.0), np.array([0.2230, 0.0543, 6.5500])), + (("2.5P", 3.0, 36.0), np.array([0.2220, 0.0480, 6.5500])), + (("2.5P", 3.0, 38.0), np.array([0.2210, 0.0440, 6.5500])), + (("2.5P", 3.0, 40.0), np.array([0.2200, 0.0400, 6.5500])), + (("2.5P", 3.0, 42.0), np.array([0.2200, 0.0350, 6.5500])), + (("2.5P", 3.0, 44.0), np.array([0.2190, 0.0320, 6.5500])), + (("2.5P", 3.0, 46.0), np.array([0.2180, 0.0280, 6.5500])), + (("5P", 3.0, 2.0), np.array([0.2997, 0.2700, 6.5500])), + (("5P", 3.0, 4.0), np.array([0.2928, 0.2386, 6.5500])), + (("5P", 3.0, 6.0), np.array([0.2870, 0.2135, 6.5500])), + (("5P", 3.0, 8.0), np.array([0.2819, 0.1910, 6.5500])), + (("5P", 3.0, 10.0), np.array([0.2772, 0.1707, 6.5500])), + (("5P", 3.0, 12.0), np.array([0.2739, 0.1539, 6.5500])), + (("5P", 3.0, 14.0), np.array([0.2707, 0.1397, 6.5500])), + (("5P", 3.0, 16.0), np.array([0.2680, 0.1272, 6.5500])), + (("5P", 3.0, 18.0), np.array([0.2657, 0.1163, 6.5500])), + (("5P", 3.0, 20.0), np.array([0.2639, 0.1074, 6.5500])), + (("5P", 3.0, 22.0), np.array([0.2620, 0.0978, 6.5500])), + (("5P", 3.0, 24.0), np.array([0.2602, 0.0891, 6.5500])), + (("5P", 3.0, 26.0), np.array([0.2590, 0.0822, 6.5500])), + (("5P", 3.0, 28.0), np.array([0.2579, 0.0750, 6.5500])), + (("5P", 3.0, 30.0), np.array([0.2568, 0.0690, 6.5500])), + (("5P", 3.0, 32.0), np.array([0.2557, 0.0630, 6.5500])), + (("5P", 3.0, 34.0), np.array([0.2550, 0.0560, 6.5500])), + (("5P", 3.0, 36.0), np.array([0.2540, 0.0510, 6.5500])), + (("5P", 3.0, 38.0), np.array([0.2530, 0.0460, 6.5500])), + (("5P", 3.0, 40.0), np.array([0.2520, 0.0410, 6.5500])), + (("5P", 3.0, 42.0), np.array([0.2510, 0.0360, 6.5500])), + (("7.5P", 3.0, 2.0), np.array([0.3088, 0.2740, 6.5500])), + (("7.5P", 3.0, 4.0), np.array([0.3072, 0.2448, 6.5500])), + (("7.5P", 3.0, 6.0), np.array([0.3057, 0.2208, 6.5500])), + (("7.5P", 3.0, 8.0), np.array([0.3037, 0.1981, 6.5500])), + (("7.5P", 3.0, 10.0), np.array([0.3020, 0.1794, 6.5500])), + (("7.5P", 3.0, 12.0), np.array([0.3003, 0.1618, 6.5500])), + (("7.5P", 3.0, 14.0), np.array([0.2992, 0.1475, 6.5500])), + (("7.5P", 3.0, 16.0), np.array([0.2981, 0.1356, 6.5500])), + (("7.5P", 3.0, 18.0), np.array([0.2969, 0.1239, 6.5500])), + (("7.5P", 3.0, 20.0), np.array([0.2961, 0.1151, 6.5500])), + (("7.5P", 3.0, 22.0), np.array([0.2953, 0.1057, 6.5500])), + (("7.5P", 3.0, 24.0), np.array([0.2944, 0.0967, 6.5500])), + (("7.5P", 3.0, 26.0), np.array([0.2938, 0.0892, 6.5500])), + (("7.5P", 3.0, 28.0), np.array([0.2930, 0.0812, 6.5500])), + (("7.5P", 3.0, 30.0), np.array([0.2922, 0.0750, 6.5500])), + (("7.5P", 3.0, 32.0), np.array([0.2920, 0.0670, 6.5500])), + (("7.5P", 3.0, 34.0), np.array([0.2910, 0.0600, 6.5500])), + (("7.5P", 3.0, 36.0), np.array([0.2900, 0.0540, 6.5500])), + (("7.5P", 3.0, 38.0), np.array([0.2890, 0.0480, 6.5500])), + (("10P", 3.0, 2.0), np.array([0.3170, 0.2790, 6.5500])), + (("10P", 3.0, 4.0), np.array([0.3214, 0.2517, 6.5500])), + (("10P", 3.0, 6.0), np.array([0.3243, 0.2293, 6.5500])), + (("10P", 3.0, 8.0), np.array([0.3269, 0.2075, 6.5500])), + (("10P", 3.0, 10.0), np.array([0.3286, 0.1889, 6.5500])), + (("10P", 3.0, 12.0), np.array([0.3301, 0.1715, 6.5500])), + (("10P", 3.0, 14.0), np.array([0.3309, 0.1572, 6.5500])), + (("10P", 3.0, 16.0), np.array([0.3320, 0.1456, 6.5500])), + (("10P", 3.0, 18.0), np.array([0.3329, 0.1332, 6.5500])), + (("10P", 3.0, 20.0), np.array([0.3332, 0.1240, 6.5500])), + (("10P", 3.0, 22.0), np.array([0.3340, 0.1146, 6.5500])), + (("10P", 3.0, 24.0), np.array([0.3341, 0.1055, 6.5500])), + (("10P", 3.0, 26.0), np.array([0.3343, 0.0978, 6.5500])), + (("10P", 3.0, 28.0), np.array([0.3350, 0.0880, 6.5500])), + (("10P", 3.0, 30.0), np.array([0.3350, 0.0810, 6.5500])), + (("10P", 3.0, 32.0), np.array([0.3350, 0.0720, 6.5500])), + (("10P", 3.0, 34.0), np.array([0.3350, 0.0630, 6.5500])), + (("2.5RP", 3.0, 2.0), np.array([0.3272, 0.2861, 6.5500])), + (("2.5RP", 3.0, 4.0), np.array([0.3400, 0.2624, 6.5500])), + (("2.5RP", 3.0, 6.0), np.array([0.3501, 0.2425, 6.5500])), + (("2.5RP", 3.0, 8.0), np.array([0.3598, 0.2233, 6.5500])), + (("2.5RP", 3.0, 10.0), np.array([0.3681, 0.2054, 6.5500])), + (("2.5RP", 3.0, 12.0), np.array([0.3754, 0.1898, 6.5500])), + (("2.5RP", 3.0, 14.0), np.array([0.3818, 0.1758, 6.5500])), + (("2.5RP", 3.0, 16.0), np.array([0.3876, 0.1629, 6.5500])), + (("2.5RP", 3.0, 18.0), np.array([0.3929, 0.1506, 6.5500])), + (("2.5RP", 3.0, 20.0), np.array([0.3969, 0.1413, 6.5500])), + (("2.5RP", 3.0, 22.0), np.array([0.4018, 0.1304, 6.5500])), + (("2.5RP", 3.0, 24.0), np.array([0.4050, 0.1220, 6.5500])), + (("2.5RP", 3.0, 26.0), np.array([0.4080, 0.1140, 6.5500])), + (("2.5RP", 3.0, 28.0), np.array([0.4140, 0.1020, 6.5500])), + (("2.5RP", 3.0, 30.0), np.array([0.4170, 0.0940, 6.5500])), + (("5RP", 3.0, 2.0), np.array([0.3370, 0.2940, 6.5500])), + (("5RP", 3.0, 4.0), np.array([0.3586, 0.2742, 6.5500])), + (("5RP", 3.0, 6.0), np.array([0.3765, 0.2569, 6.5500])), + (("5RP", 3.0, 8.0), np.array([0.3930, 0.2395, 6.5500])), + (("5RP", 3.0, 10.0), np.array([0.4073, 0.2235, 6.5500])), + (("5RP", 3.0, 12.0), np.array([0.4199, 0.2089, 6.5500])), + (("5RP", 3.0, 14.0), np.array([0.4313, 0.1944, 6.5500])), + (("5RP", 3.0, 16.0), np.array([0.4418, 0.1809, 6.5500])), + (("5RP", 3.0, 18.0), np.array([0.4503, 0.1695, 6.5500])), + (("5RP", 3.0, 20.0), np.array([0.4577, 0.1593, 6.5500])), + (("5RP", 3.0, 22.0), np.array([0.4670, 0.1450, 6.5500])), + (("5RP", 3.0, 24.0), np.array([0.4720, 0.1360, 6.5500])), + (("5RP", 3.0, 26.0), np.array([0.4790, 0.1270, 6.5500])), + (("7.5RP", 3.0, 2.0), np.array([0.3450, 0.3001, 6.5500])), + (("7.5RP", 3.0, 4.0), np.array([0.3739, 0.2851, 6.5500])), + (("7.5RP", 3.0, 6.0), np.array([0.3990, 0.2708, 6.5500])), + (("7.5RP", 3.0, 8.0), np.array([0.4234, 0.2556, 6.5500])), + (("7.5RP", 3.0, 10.0), np.array([0.4445, 0.2419, 6.5500])), + (("7.5RP", 3.0, 12.0), np.array([0.4654, 0.2273, 6.5500])), + (("7.5RP", 3.0, 14.0), np.array([0.4831, 0.2140, 6.5500])), + (("7.5RP", 3.0, 16.0), np.array([0.4991, 0.2011, 6.5500])), + (("7.5RP", 3.0, 18.0), np.array([0.5130, 0.1893, 6.5500])), + (("7.5RP", 3.0, 20.0), np.array([0.5280, 0.1780, 6.5500])), + (("7.5RP", 3.0, 22.0), np.array([0.5420, 0.1650, 6.5500])), + (("10RP", 3.0, 2.0), np.array([0.3526, 0.3068, 6.5500])), + (("10RP", 3.0, 4.0), np.array([0.3889, 0.2969, 6.5500])), + (("10RP", 3.0, 6.0), np.array([0.4218, 0.2864, 6.5500])), + (("10RP", 3.0, 8.0), np.array([0.4552, 0.2741, 6.5500])), + (("10RP", 3.0, 10.0), np.array([0.4851, 0.2618, 6.5500])), + (("10RP", 3.0, 12.0), np.array([0.5139, 0.2489, 6.5500])), + (("10RP", 3.0, 14.0), np.array([0.5380, 0.2369, 6.5500])), + (("10RP", 3.0, 16.0), np.array([0.5628, 0.2241, 6.5500])), + (("10RP", 3.0, 18.0), np.array([0.5840, 0.2120, 6.5500])), + (("10RP", 3.0, 20.0), np.array([0.6020, 0.2000, 6.5500])), + (("2.5R", 3.0, 2.0), np.array([0.3591, 0.3130, 6.5500])), + (("2.5R", 3.0, 4.0), np.array([0.4021, 0.3076, 6.5500])), + (("2.5R", 3.0, 6.0), np.array([0.4409, 0.3009, 6.5500])), + (("2.5R", 3.0, 8.0), np.array([0.4821, 0.2918, 6.5500])), + (("2.5R", 3.0, 10.0), np.array([0.5191, 0.2811, 6.5500])), + (("2.5R", 3.0, 12.0), np.array([0.5536, 0.2691, 6.5500])), + (("2.5R", 3.0, 14.0), np.array([0.5828, 0.2579, 6.5500])), + (("2.5R", 3.0, 16.0), np.array([0.6116, 0.2456, 6.5500])), + (("2.5R", 3.0, 18.0), np.array([0.6400, 0.2320, 6.5500])), + (("2.5R", 3.0, 20.0), np.array([0.6670, 0.2170, 6.5500])), + (("5R", 3.0, 2.0), np.array([0.3645, 0.3190, 6.5500])), + (("5R", 3.0, 4.0), np.array([0.4148, 0.3190, 6.5500])), + (("5R", 3.0, 6.0), np.array([0.4592, 0.3168, 6.5500])), + (("5R", 3.0, 8.0), np.array([0.5064, 0.3114, 6.5500])), + (("5R", 3.0, 10.0), np.array([0.5500, 0.3024, 6.5500])), + (("5R", 3.0, 12.0), np.array([0.5884, 0.2904, 6.5500])), + (("5R", 3.0, 14.0), np.array([0.6204, 0.2789, 6.5500])), + (("5R", 3.0, 16.0), np.array([0.6520, 0.2660, 6.5500])), + (("5R", 3.0, 18.0), np.array([0.6820, 0.2510, 6.5500])), + (("5R", 3.0, 20.0), np.array([0.7100, 0.2340, 6.5500])), + (("7.5R", 3.0, 2.0), np.array([0.3690, 0.3248, 6.5500])), + (("7.5R", 3.0, 4.0), np.array([0.4240, 0.3302, 6.5500])), + (("7.5R", 3.0, 6.0), np.array([0.4738, 0.3316, 6.5500])), + (("7.5R", 3.0, 8.0), np.array([0.5251, 0.3297, 6.5500])), + (("7.5R", 3.0, 10.0), np.array([0.5730, 0.3240, 6.5500])), + (("7.5R", 3.0, 12.0), np.array([0.6158, 0.3129, 6.5500])), + (("7.5R", 3.0, 14.0), np.array([0.6492, 0.3012, 6.5500])), + (("7.5R", 3.0, 16.0), np.array([0.6817, 0.2872, 6.5500])), + (("7.5R", 3.0, 18.0), np.array([0.7140, 0.2710, 6.5500])), + (("7.5R", 3.0, 20.0), np.array([0.7470, 0.2510, 6.5500])), + (("10R", 3.0, 2.0), np.array([0.3728, 0.3314, 6.5500])), + (("10R", 3.0, 4.0), np.array([0.4308, 0.3412, 6.5500])), + (("10R", 3.0, 6.0), np.array([0.4854, 0.3467, 6.5500])), + (("10R", 3.0, 8.0), np.array([0.5393, 0.3477, 6.5500])), + (("10R", 3.0, 10.0), np.array([0.5871, 0.3440, 6.5500])), + (("10R", 3.0, 12.0), np.array([0.6322, 0.3361, 6.5500])), + (("10R", 3.0, 14.0), np.array([0.6703, 0.3249, 6.5500])), + (("10R", 3.0, 16.0), np.array([0.7030, 0.3140, 6.5500])), + (("10R", 3.0, 18.0), np.array([0.7390, 0.3020, 6.5500])), + (("10R", 3.0, 20.0), np.array([0.7780, 0.2860, 6.5500])), + (("2.5YR", 3.0, 2.0), np.array([0.3757, 0.3391, 6.5500])), + (("2.5YR", 3.0, 4.0), np.array([0.4360, 0.3563, 6.5500])), + (("2.5YR", 3.0, 6.0), np.array([0.4954, 0.3692, 6.5500])), + (("2.5YR", 3.0, 8.0), np.array([0.5475, 0.3771, 6.5500])), + (("2.5YR", 3.0, 10.0), np.array([0.5941, 0.3818, 6.5500])), + (("2.5YR", 3.0, 12.0), np.array([0.6370, 0.3810, 6.5500])), + (("2.5YR", 3.0, 14.0), np.array([0.6740, 0.3790, 6.5500])), + (("2.5YR", 3.0, 16.0), np.array([0.7080, 0.3740, 6.5500])), + (("5YR", 3.0, 2.0), np.array([0.3771, 0.3476, 6.5500])), + (("5YR", 3.0, 4.0), np.array([0.4376, 0.3715, 6.5500])), + (("5YR", 3.0, 6.0), np.array([0.4966, 0.3908, 6.5500])), + (("5YR", 3.0, 8.0), np.array([0.5456, 0.4040, 6.5500])), + (("5YR", 3.0, 10.0), np.array([0.5900, 0.4140, 6.5500])), + (("5YR", 3.0, 12.0), np.array([0.6290, 0.4230, 6.5500])), + (("7.5YR", 3.0, 2.0), np.array([0.3771, 0.3549, 6.5500])), + (("7.5YR", 3.0, 4.0), np.array([0.4378, 0.3865, 6.5500])), + (("7.5YR", 3.0, 6.0), np.array([0.4930, 0.4116, 6.5500])), + (("7.5YR", 3.0, 8.0), np.array([0.5390, 0.4306, 6.5500])), + (("7.5YR", 3.0, 10.0), np.array([0.5810, 0.4480, 6.5500])), + (("10YR", 3.0, 2.0), np.array([0.3747, 0.3630, 6.5500])), + (("10YR", 3.0, 4.0), np.array([0.4341, 0.4018, 6.5500])), + (("10YR", 3.0, 6.0), np.array([0.4872, 0.4326, 6.5500])), + (("10YR", 3.0, 8.0), np.array([0.5305, 0.4559, 6.5500])), + (("10YR", 3.0, 10.0), np.array([0.5720, 0.4750, 6.5500])), + (("2.5Y", 3.0, 2.0), np.array([0.3703, 0.3700, 6.5500])), + (("2.5Y", 3.0, 4.0), np.array([0.4277, 0.4166, 6.5500])), + (("2.5Y", 3.0, 6.0), np.array([0.4784, 0.4531, 6.5500])), + (("2.5Y", 3.0, 8.0), np.array([0.5210, 0.4820, 6.5500])), + (("2.5Y", 3.0, 10.0), np.array([0.5600, 0.5050, 6.5500])), + (("5Y", 3.0, 2.0), np.array([0.3646, 0.3748, 6.5500])), + (("5Y", 3.0, 4.0), np.array([0.4191, 0.4283, 6.5500])), + (("5Y", 3.0, 6.0), np.array([0.4670, 0.4711, 6.5500])), + (("5Y", 3.0, 8.0), np.array([0.5090, 0.5090, 6.5500])), + (("7.5Y", 3.0, 2.0), np.array([0.3589, 0.3778, 6.5500])), + (("7.5Y", 3.0, 4.0), np.array([0.4086, 0.4379, 6.5500])), + (("7.5Y", 3.0, 6.0), np.array([0.4526, 0.4889, 6.5500])), + (("7.5Y", 3.0, 8.0), np.array([0.4920, 0.5350, 6.5500])), + (("10Y", 4.0, 2.0), np.array([0.3476, 0.3732, 12.0000])), + (("10Y", 4.0, 4.0), np.array([0.3871, 0.4321, 12.0000])), + (("10Y", 4.0, 6.0), np.array([0.4190, 0.4795, 12.0000])), + (("10Y", 4.0, 8.0), np.array([0.4430, 0.5153, 12.0000])), + (("10Y", 4.0, 10.0), np.array([0.4620, 0.5430, 12.0000])), + (("10Y", 4.0, 12.0), np.array([0.4730, 0.5620, 12.0000])), + (("2.5GY", 4.0, 2.0), np.array([0.3382, 0.3706, 12.0000])), + (("2.5GY", 4.0, 4.0), np.array([0.3708, 0.4329, 12.0000])), + (("2.5GY", 4.0, 6.0), np.array([0.3968, 0.4857, 12.0000])), + (("2.5GY", 4.0, 8.0), np.array([0.4174, 0.5300, 12.0000])), + (("2.5GY", 4.0, 10.0), np.array([0.4330, 0.5680, 12.0000])), + (("2.5GY", 4.0, 12.0), np.array([0.4430, 0.5940, 12.0000])), + (("5GY", 4.0, 2.0), np.array([0.3312, 0.3678, 12.0000])), + (("5GY", 4.0, 4.0), np.array([0.3538, 0.4284, 12.0000])), + (("5GY", 4.0, 6.0), np.array([0.3718, 0.4852, 12.0000])), + (("5GY", 4.0, 8.0), np.array([0.3868, 0.5384, 12.0000])), + (("5GY", 4.0, 10.0), np.array([0.3983, 0.5850, 12.0000])), + (("5GY", 4.0, 12.0), np.array([0.4070, 0.6190, 12.0000])), + (("5GY", 4.0, 14.0), np.array([0.4150, 0.6590, 12.0000])), + (("7.5GY", 4.0, 2.0), np.array([0.3185, 0.3604, 12.0000])), + (("7.5GY", 4.0, 4.0), np.array([0.3281, 0.4157, 12.0000])), + (("7.5GY", 4.0, 6.0), np.array([0.3355, 0.4739, 12.0000])), + (("7.5GY", 4.0, 8.0), np.array([0.3400, 0.5348, 12.0000])), + (("7.5GY", 4.0, 10.0), np.array([0.3395, 0.5913, 12.0000])), + (("7.5GY", 4.0, 12.0), np.array([0.3348, 0.6468, 12.0000])), + (("7.5GY", 4.0, 14.0), np.array([0.3270, 0.6980, 12.0000])), + (("7.5GY", 4.0, 16.0), np.array([0.3150, 0.7570, 12.0000])), + (("7.5GY", 4.0, 18.0), np.array([0.3030, 0.8090, 12.0000])), + (("10GY", 4.0, 2.0), np.array([0.3109, 0.3550, 12.0000])), + (("10GY", 4.0, 4.0), np.array([0.3100, 0.4018, 12.0000])), + (("10GY", 4.0, 6.0), np.array([0.3069, 0.4550, 12.0000])), + (("10GY", 4.0, 8.0), np.array([0.3008, 0.5095, 12.0000])), + (("10GY", 4.0, 10.0), np.array([0.2908, 0.5672, 12.0000])), + (("10GY", 4.0, 12.0), np.array([0.2758, 0.6282, 12.0000])), + (("10GY", 4.0, 14.0), np.array([0.2590, 0.6858, 12.0000])), + (("10GY", 4.0, 16.0), np.array([0.2422, 0.7360, 12.0000])), + (("10GY", 4.0, 18.0), np.array([0.2210, 0.7930, 12.0000])), + (("10GY", 4.0, 20.0), np.array([0.1920, 0.8600, 12.0000])), + (("10GY", 4.0, 22.0), np.array([0.1650, 0.9170, 12.0000])), + (("10GY", 4.0, 24.0), np.array([0.1420, 0.9640, 12.0000])), + (("10GY", 4.0, 26.0), np.array([0.1060, 1.0280, 12.0000])), + (("10GY", 4.0, 28.0), np.array([0.0580, 1.1000, 12.0000])), + (("2.5G", 4.0, 2.0), np.array([0.3012, 0.3470, 12.0000])), + (("2.5G", 4.0, 4.0), np.array([0.2891, 0.3821, 12.0000])), + (("2.5G", 4.0, 6.0), np.array([0.2735, 0.4215, 12.0000])), + (("2.5G", 4.0, 8.0), np.array([0.2561, 0.4597, 12.0000])), + (("2.5G", 4.0, 10.0), np.array([0.2355, 0.5006, 12.0000])), + (("2.5G", 4.0, 12.0), np.array([0.2128, 0.5425, 12.0000])), + (("2.5G", 4.0, 14.0), np.array([0.1909, 0.5779, 12.0000])), + (("2.5G", 4.0, 16.0), np.array([0.1682, 0.6111, 12.0000])), + (("2.5G", 4.0, 18.0), np.array([0.1446, 0.6431, 12.0000])), + (("2.5G", 4.0, 20.0), np.array([0.1230, 0.6706, 12.0000])), + (("2.5G", 4.0, 22.0), np.array([0.1009, 0.6975, 12.0000])), + (("2.5G", 4.0, 24.0), np.array([0.0760, 0.7250, 12.0000])), + (("2.5G", 4.0, 26.0), np.array([0.0528, 0.7502, 12.0000])), + (("2.5G", 4.0, 28.0), np.array([0.0280, 0.7800, 12.0000])), + (("2.5G", 4.0, 30.0), np.array([-0.0050, 0.8160, 12.0000])), + (("5G", 4.0, 2.0), np.array([0.2959, 0.3417, 12.0000])), + (("5G", 4.0, 4.0), np.array([0.2781, 0.3704, 12.0000])), + (("5G", 4.0, 6.0), np.array([0.2581, 0.3992, 12.0000])), + (("5G", 4.0, 8.0), np.array([0.2359, 0.4266, 12.0000])), + (("5G", 4.0, 10.0), np.array([0.2115, 0.4532, 12.0000])), + (("5G", 4.0, 12.0), np.array([0.1843, 0.4807, 12.0000])), + (("5G", 4.0, 14.0), np.array([0.1627, 0.5015, 12.0000])), + (("5G", 4.0, 16.0), np.array([0.1402, 0.5214, 12.0000])), + (("5G", 4.0, 18.0), np.array([0.1188, 0.5400, 12.0000])), + (("5G", 4.0, 20.0), np.array([0.1018, 0.5543, 12.0000])), + (("5G", 4.0, 22.0), np.array([0.0841, 0.5684, 12.0000])), + (("5G", 4.0, 24.0), np.array([0.0614, 0.5857, 12.0000])), + (("5G", 4.0, 26.0), np.array([0.0407, 0.6010, 12.0000])), + (("5G", 4.0, 28.0), np.array([0.0200, 0.6180, 12.0000])), + (("5G", 4.0, 30.0), np.array([-0.0030, 0.6320, 12.0000])), + (("7.5G", 4.0, 2.0), np.array([0.2919, 0.3371, 12.0000])), + (("7.5G", 4.0, 4.0), np.array([0.2702, 0.3602, 12.0000])), + (("7.5G", 4.0, 6.0), np.array([0.2467, 0.3822, 12.0000])), + (("7.5G", 4.0, 8.0), np.array([0.2232, 0.4022, 12.0000])), + (("7.5G", 4.0, 10.0), np.array([0.1989, 0.4219, 12.0000])), + (("7.5G", 4.0, 12.0), np.array([0.1706, 0.4419, 12.0000])), + (("7.5G", 4.0, 14.0), np.array([0.1500, 0.4562, 12.0000])), + (("7.5G", 4.0, 16.0), np.array([0.1293, 0.4703, 12.0000])), + (("7.5G", 4.0, 18.0), np.array([0.1086, 0.4842, 12.0000])), + (("7.5G", 4.0, 20.0), np.array([0.0928, 0.4942, 12.0000])), + (("7.5G", 4.0, 22.0), np.array([0.0770, 0.5040, 12.0000])), + (("7.5G", 4.0, 24.0), np.array([0.0581, 0.5151, 12.0000])), + (("7.5G", 4.0, 26.0), np.array([0.0392, 0.5258, 12.0000])), + (("7.5G", 4.0, 28.0), np.array([0.0200, 0.5360, 12.0000])), + (("7.5G", 4.0, 30.0), np.array([0.0020, 0.5460, 12.0000])), + (("10G", 4.0, 2.0), np.array([0.2880, 0.3327, 12.0000])), + (("10G", 4.0, 4.0), np.array([0.2628, 0.3498, 12.0000])), + (("10G", 4.0, 6.0), np.array([0.2374, 0.3655, 12.0000])), + (("10G", 4.0, 8.0), np.array([0.2124, 0.3799, 12.0000])), + (("10G", 4.0, 10.0), np.array([0.1876, 0.3933, 12.0000])), + (("10G", 4.0, 12.0), np.array([0.1602, 0.4070, 12.0000])), + (("10G", 4.0, 14.0), np.array([0.1398, 0.4168, 12.0000])), + (("10G", 4.0, 16.0), np.array([0.1212, 0.4245, 12.0000])), + (("10G", 4.0, 18.0), np.array([0.1006, 0.4330, 12.0000])), + (("10G", 4.0, 20.0), np.array([0.0850, 0.4388, 12.0000])), + (("10G", 4.0, 22.0), np.array([0.0702, 0.4440, 12.0000])), + (("10G", 4.0, 24.0), np.array([0.0553, 0.4492, 12.0000])), + (("10G", 4.0, 26.0), np.array([0.0400, 0.4545, 12.0000])), + (("10G", 4.0, 28.0), np.array([0.0230, 0.4600, 12.0000])), + (("10G", 4.0, 30.0), np.array([0.0080, 0.4650, 12.0000])), + (("2.5BG", 4.0, 2.0), np.array([0.2840, 0.3270, 12.0000])), + (("2.5BG", 4.0, 4.0), np.array([0.2552, 0.3375, 12.0000])), + (("2.5BG", 4.0, 6.0), np.array([0.2278, 0.3463, 12.0000])), + (("2.5BG", 4.0, 8.0), np.array([0.2006, 0.3540, 12.0000])), + (("2.5BG", 4.0, 10.0), np.array([0.1738, 0.3600, 12.0000])), + (("2.5BG", 4.0, 12.0), np.array([0.1492, 0.3649, 12.0000])), + (("2.5BG", 4.0, 14.0), np.array([0.1283, 0.3688, 12.0000])), + (("2.5BG", 4.0, 16.0), np.array([0.1102, 0.3720, 12.0000])), + (("2.5BG", 4.0, 18.0), np.array([0.0915, 0.3754, 12.0000])), + (("2.5BG", 4.0, 20.0), np.array([0.0768, 0.3773, 12.0000])), + (("2.5BG", 4.0, 22.0), np.array([0.0636, 0.3788, 12.0000])), + (("2.5BG", 4.0, 24.0), np.array([0.0510, 0.3800, 12.0000])), + (("2.5BG", 4.0, 26.0), np.array([0.0380, 0.3820, 12.0000])), + (("2.5BG", 4.0, 28.0), np.array([0.0250, 0.3830, 12.0000])), + (("5BG", 4.0, 2.0), np.array([0.2799, 0.3208, 12.0000])), + (("5BG", 4.0, 4.0), np.array([0.2480, 0.3232, 12.0000])), + (("5BG", 4.0, 6.0), np.array([0.2182, 0.3240, 12.0000])), + (("5BG", 4.0, 8.0), np.array([0.1890, 0.3234, 12.0000])), + (("5BG", 4.0, 10.0), np.array([0.1618, 0.3219, 12.0000])), + (("5BG", 4.0, 12.0), np.array([0.1379, 0.3198, 12.0000])), + (("5BG", 4.0, 14.0), np.array([0.1170, 0.3170, 12.0000])), + (("5BG", 4.0, 16.0), np.array([0.0992, 0.3141, 12.0000])), + (("5BG", 4.0, 18.0), np.array([0.0828, 0.3108, 12.0000])), + (("5BG", 4.0, 20.0), np.array([0.0675, 0.3075, 12.0000])), + (("5BG", 4.0, 22.0), np.array([0.0560, 0.3050, 12.0000])), + (("5BG", 4.0, 24.0), np.array([0.0470, 0.3040, 12.0000])), + (("5BG", 4.0, 26.0), np.array([0.0360, 0.3030, 12.0000])), + (("7.5BG", 4.0, 2.0), np.array([0.2764, 0.3148, 12.0000])), + (("7.5BG", 4.0, 4.0), np.array([0.2429, 0.3108, 12.0000])), + (("7.5BG", 4.0, 6.0), np.array([0.2113, 0.3052, 12.0000])), + (("7.5BG", 4.0, 8.0), np.array([0.1815, 0.2985, 12.0000])), + (("7.5BG", 4.0, 10.0), np.array([0.1540, 0.2910, 12.0000])), + (("7.5BG", 4.0, 12.0), np.array([0.1298, 0.2840, 12.0000])), + (("7.5BG", 4.0, 14.0), np.array([0.1092, 0.2774, 12.0000])), + (("7.5BG", 4.0, 16.0), np.array([0.0922, 0.2718, 12.0000])), + (("7.5BG", 4.0, 18.0), np.array([0.0768, 0.2667, 12.0000])), + (("7.5BG", 4.0, 20.0), np.array([0.0650, 0.2620, 12.0000])), + (("7.5BG", 4.0, 22.0), np.array([0.0540, 0.2580, 12.0000])), + (("7.5BG", 4.0, 24.0), np.array([0.0450, 0.2550, 12.0000])), + (("10BG", 4.0, 2.0), np.array([0.2740, 0.3091, 12.0000])), + (("10BG", 4.0, 4.0), np.array([0.2384, 0.2984, 12.0000])), + (("10BG", 4.0, 6.0), np.array([0.2065, 0.2863, 12.0000])), + (("10BG", 4.0, 8.0), np.array([0.1760, 0.2730, 12.0000])), + (("10BG", 4.0, 10.0), np.array([0.1480, 0.2600, 12.0000])), + (("10BG", 4.0, 12.0), np.array([0.1248, 0.2484, 12.0000])), + (("10BG", 4.0, 14.0), np.array([0.1033, 0.2376, 12.0000])), + (("10BG", 4.0, 16.0), np.array([0.0888, 0.2298, 12.0000])), + (("10BG", 4.0, 18.0), np.array([0.0730, 0.2210, 12.0000])), + (("10BG", 4.0, 20.0), np.array([0.0620, 0.2140, 12.0000])), + (("10BG", 4.0, 22.0), np.array([0.0510, 0.2070, 12.0000])), + (("2.5B", 4.0, 2.0), np.array([0.2727, 0.3038, 12.0000])), + (("2.5B", 4.0, 4.0), np.array([0.2360, 0.2872, 12.0000])), + (("2.5B", 4.0, 6.0), np.array([0.2048, 0.2708, 12.0000])), + (("2.5B", 4.0, 8.0), np.array([0.1737, 0.2524, 12.0000])), + (("2.5B", 4.0, 10.0), np.array([0.1463, 0.2354, 12.0000])), + (("2.5B", 4.0, 12.0), np.array([0.1247, 0.2209, 12.0000])), + (("2.5B", 4.0, 14.0), np.array([0.1027, 0.2057, 12.0000])), + (("2.5B", 4.0, 16.0), np.array([0.0900, 0.1973, 12.0000])), + (("2.5B", 4.0, 18.0), np.array([0.0730, 0.1840, 12.0000])), + (("2.5B", 4.0, 20.0), np.array([0.0620, 0.1770, 12.0000])), + (("5B", 4.0, 2.0), np.array([0.2723, 0.2992, 12.0000])), + (("5B", 4.0, 4.0), np.array([0.2363, 0.2782, 12.0000])), + (("5B", 4.0, 6.0), np.array([0.2060, 0.2572, 12.0000])), + (("5B", 4.0, 8.0), np.array([0.1759, 0.2345, 12.0000])), + (("5B", 4.0, 10.0), np.array([0.1512, 0.2148, 12.0000])), + (("5B", 4.0, 12.0), np.array([0.1299, 0.1963, 12.0000])), + (("5B", 4.0, 14.0), np.array([0.1098, 0.1785, 12.0000])), + (("5B", 4.0, 16.0), np.array([0.0940, 0.1630, 12.0000])), + (("5B", 4.0, 18.0), np.array([0.0790, 0.1500, 12.0000])), + (("7.5B", 4.0, 2.0), np.array([0.2733, 0.2947, 12.0000])), + (("7.5B", 4.0, 4.0), np.array([0.2388, 0.2704, 12.0000])), + (("7.5B", 4.0, 6.0), np.array([0.2102, 0.2470, 12.0000])), + (("7.5B", 4.0, 8.0), np.array([0.1821, 0.2232, 12.0000])), + (("7.5B", 4.0, 10.0), np.array([0.1601, 0.2028, 12.0000])), + (("7.5B", 4.0, 12.0), np.array([0.1393, 0.1837, 12.0000])), + (("7.5B", 4.0, 14.0), np.array([0.1204, 0.1655, 12.0000])), + (("7.5B", 4.0, 16.0), np.array([0.1020, 0.1490, 12.0000])), + (("7.5B", 4.0, 18.0), np.array([0.0910, 0.1380, 12.0000])), + (("10B", 4.0, 2.0), np.array([0.2753, 0.2910, 12.0000])), + (("10B", 4.0, 4.0), np.array([0.2429, 0.2648, 12.0000])), + (("10B", 4.0, 6.0), np.array([0.2157, 0.2407, 12.0000])), + (("10B", 4.0, 8.0), np.array([0.1893, 0.2160, 12.0000])), + (("10B", 4.0, 10.0), np.array([0.1681, 0.1954, 12.0000])), + (("10B", 4.0, 12.0), np.array([0.1487, 0.1760, 12.0000])), + (("10B", 4.0, 14.0), np.array([0.1310, 0.1580, 12.0000])), + (("10B", 4.0, 16.0), np.array([0.1155, 0.1416, 12.0000])), + (("10B", 4.0, 18.0), np.array([0.1030, 0.1280, 12.0000])), + (("10B", 4.0, 20.0), np.array([0.0920, 0.1170, 12.0000])), + (("2.5PB", 4.0, 2.0), np.array([0.2782, 0.2876, 12.0000])), + (("2.5PB", 4.0, 4.0), np.array([0.2487, 0.2597, 12.0000])), + (("2.5PB", 4.0, 6.0), np.array([0.2235, 0.2343, 12.0000])), + (("2.5PB", 4.0, 8.0), np.array([0.1995, 0.2094, 12.0000])), + (("2.5PB", 4.0, 10.0), np.array([0.1805, 0.1888, 12.0000])), + (("2.5PB", 4.0, 12.0), np.array([0.1634, 0.1698, 12.0000])), + (("2.5PB", 4.0, 14.0), np.array([0.1473, 0.1513, 12.0000])), + (("2.5PB", 4.0, 16.0), np.array([0.1336, 0.1349, 12.0000])), + (("2.5PB", 4.0, 18.0), np.array([0.1218, 0.1208, 12.0000])), + (("2.5PB", 4.0, 20.0), np.array([0.1120, 0.1080, 12.0000])), + (("2.5PB", 4.0, 22.0), np.array([0.1040, 0.0960, 12.0000])), + (("2.5PB", 4.0, 24.0), np.array([0.0980, 0.0890, 12.0000])), + (("5PB", 4.0, 2.0), np.array([0.2816, 0.2842, 12.0000])), + (("5PB", 4.0, 4.0), np.array([0.2562, 0.2560, 12.0000])), + (("5PB", 4.0, 6.0), np.array([0.2325, 0.2300, 12.0000])), + (("5PB", 4.0, 8.0), np.array([0.2103, 0.2050, 12.0000])), + (("5PB", 4.0, 10.0), np.array([0.1925, 0.1843, 12.0000])), + (("5PB", 4.0, 12.0), np.array([0.1773, 0.1659, 12.0000])), + (("5PB", 4.0, 14.0), np.array([0.1627, 0.1479, 12.0000])), + (("5PB", 4.0, 16.0), np.array([0.1504, 0.1317, 12.0000])), + (("5PB", 4.0, 18.0), np.array([0.1392, 0.1167, 12.0000])), + (("5PB", 4.0, 20.0), np.array([0.1288, 0.1027, 12.0000])), + (("5PB", 4.0, 22.0), np.array([0.1220, 0.0920, 12.0000])), + (("5PB", 4.0, 24.0), np.array([0.1180, 0.0840, 12.0000])), + (("5PB", 4.0, 26.0), np.array([0.1150, 0.0780, 12.0000])), + (("5PB", 4.0, 28.0), np.array([0.1110, 0.0730, 12.0000])), + (("5PB", 4.0, 30.0), np.array([0.1080, 0.0680, 12.0000])), + (("5PB", 4.0, 32.0), np.array([0.1060, 0.0640, 12.0000])), + (("5PB", 4.0, 34.0), np.array([0.1040, 0.0600, 12.0000])), + (("5PB", 4.0, 36.0), np.array([0.1020, 0.0550, 12.0000])), + (("5PB", 4.0, 38.0), np.array([0.0980, 0.0480, 12.0000])), + (("5PB", 4.0, 40.0), np.array([0.0960, 0.0440, 12.0000])), + (("7.5PB", 4.0, 2.0), np.array([0.2861, 0.2819, 12.0000])), + (("7.5PB", 4.0, 4.0), np.array([0.2657, 0.2528, 12.0000])), + (("7.5PB", 4.0, 6.0), np.array([0.2471, 0.2266, 12.0000])), + (("7.5PB", 4.0, 8.0), np.array([0.2304, 0.2023, 12.0000])), + (("7.5PB", 4.0, 10.0), np.array([0.2158, 0.1811, 12.0000])), + (("7.5PB", 4.0, 12.0), np.array([0.2037, 0.1629, 12.0000])), + (("7.5PB", 4.0, 14.0), np.array([0.1941, 0.1468, 12.0000])), + (("7.5PB", 4.0, 16.0), np.array([0.1861, 0.1316, 12.0000])), + (("7.5PB", 4.0, 18.0), np.array([0.1798, 0.1185, 12.0000])), + (("7.5PB", 4.0, 20.0), np.array([0.1742, 0.1058, 12.0000])), + (("7.5PB", 4.0, 22.0), np.array([0.1713, 0.0980, 12.0000])), + (("7.5PB", 4.0, 24.0), np.array([0.1684, 0.0899, 12.0000])), + (("7.5PB", 4.0, 26.0), np.array([0.1659, 0.0825, 12.0000])), + (("7.5PB", 4.0, 28.0), np.array([0.1640, 0.0770, 12.0000])), + (("7.5PB", 4.0, 30.0), np.array([0.1620, 0.0720, 12.0000])), + (("7.5PB", 4.0, 32.0), np.array([0.1600, 0.0660, 12.0000])), + (("7.5PB", 4.0, 34.0), np.array([0.1580, 0.0600, 12.0000])), + (("7.5PB", 4.0, 36.0), np.array([0.1570, 0.0540, 12.0000])), + (("7.5PB", 4.0, 38.0), np.array([0.1550, 0.0490, 12.0000])), + (("7.5PB", 4.0, 40.0), np.array([0.1530, 0.0440, 12.0000])), + (("10PB", 4.0, 2.0), np.array([0.2911, 0.2804, 12.0000])), + (("10PB", 4.0, 4.0), np.array([0.2759, 0.2522, 12.0000])), + (("10PB", 4.0, 6.0), np.array([0.2618, 0.2263, 12.0000])), + (("10PB", 4.0, 8.0), np.array([0.2497, 0.2038, 12.0000])), + (("10PB", 4.0, 10.0), np.array([0.2388, 0.1837, 12.0000])), + (("10PB", 4.0, 12.0), np.array([0.2298, 0.1659, 12.0000])), + (("10PB", 4.0, 14.0), np.array([0.2220, 0.1503, 12.0000])), + (("10PB", 4.0, 16.0), np.array([0.2170, 0.1373, 12.0000])), + (("10PB", 4.0, 18.0), np.array([0.2120, 0.1256, 12.0000])), + (("10PB", 4.0, 20.0), np.array([0.2075, 0.1140, 12.0000])), + (("10PB", 4.0, 22.0), np.array([0.2048, 0.1064, 12.0000])), + (("10PB", 4.0, 24.0), np.array([0.2020, 0.0985, 12.0000])), + (("10PB", 4.0, 26.0), np.array([0.1994, 0.0904, 12.0000])), + (("10PB", 4.0, 28.0), np.array([0.1971, 0.0840, 12.0000])), + (("10PB", 4.0, 30.0), np.array([0.1952, 0.0778, 12.0000])), + (("10PB", 4.0, 32.0), np.array([0.1920, 0.0710, 12.0000])), + (("10PB", 4.0, 34.0), np.array([0.1910, 0.0640, 12.0000])), + (("10PB", 4.0, 36.0), np.array([0.1890, 0.0580, 12.0000])), + (("10PB", 4.0, 38.0), np.array([0.1870, 0.0520, 12.0000])), + (("10PB", 4.0, 40.0), np.array([0.1840, 0.0460, 12.0000])), + (("2.5P", 4.0, 2.0), np.array([0.2962, 0.2807, 12.0000])), + (("2.5P", 4.0, 4.0), np.array([0.2855, 0.2531, 12.0000])), + (("2.5P", 4.0, 6.0), np.array([0.2763, 0.2300, 12.0000])), + (("2.5P", 4.0, 8.0), np.array([0.2685, 0.2089, 12.0000])), + (("2.5P", 4.0, 10.0), np.array([0.2619, 0.1903, 12.0000])), + (("2.5P", 4.0, 12.0), np.array([0.2559, 0.1730, 12.0000])), + (("2.5P", 4.0, 14.0), np.array([0.2509, 0.1585, 12.0000])), + (("2.5P", 4.0, 16.0), np.array([0.2467, 0.1452, 12.0000])), + (("2.5P", 4.0, 18.0), np.array([0.2430, 0.1332, 12.0000])), + (("2.5P", 4.0, 20.0), np.array([0.2394, 0.1221, 12.0000])), + (("2.5P", 4.0, 22.0), np.array([0.2371, 0.1143, 12.0000])), + (("2.5P", 4.0, 24.0), np.array([0.2348, 0.1062, 12.0000])), + (("2.5P", 4.0, 26.0), np.array([0.2322, 0.0978, 12.0000])), + (("2.5P", 4.0, 28.0), np.array([0.2302, 0.0909, 12.0000])), + (("2.5P", 4.0, 30.0), np.array([0.2285, 0.0847, 12.0000])), + (("2.5P", 4.0, 32.0), np.array([0.2265, 0.0774, 12.0000])), + (("2.5P", 4.0, 34.0), np.array([0.2240, 0.0700, 12.0000])), + (("2.5P", 4.0, 36.0), np.array([0.2220, 0.0640, 12.0000])), + (("2.5P", 4.0, 38.0), np.array([0.2200, 0.0570, 12.0000])), + (("2.5P", 4.0, 40.0), np.array([0.2180, 0.0510, 12.0000])), + (("5P", 4.0, 2.0), np.array([0.3022, 0.2825, 12.0000])), + (("5P", 4.0, 4.0), np.array([0.2958, 0.2565, 12.0000])), + (("5P", 4.0, 6.0), np.array([0.2903, 0.2347, 12.0000])), + (("5P", 4.0, 8.0), np.array([0.2855, 0.2150, 12.0000])), + (("5P", 4.0, 10.0), np.array([0.2814, 0.1967, 12.0000])), + (("5P", 4.0, 12.0), np.array([0.2778, 0.1808, 12.0000])), + (("5P", 4.0, 14.0), np.array([0.2747, 0.1660, 12.0000])), + (("5P", 4.0, 16.0), np.array([0.2718, 0.1520, 12.0000])), + (("5P", 4.0, 18.0), np.array([0.2693, 0.1408, 12.0000])), + (("5P", 4.0, 20.0), np.array([0.2670, 0.1300, 12.0000])), + (("5P", 4.0, 22.0), np.array([0.2652, 0.1218, 12.0000])), + (("5P", 4.0, 24.0), np.array([0.2635, 0.1132, 12.0000])), + (("5P", 4.0, 26.0), np.array([0.2618, 0.1052, 12.0000])), + (("5P", 4.0, 28.0), np.array([0.2600, 0.0971, 12.0000])), + (("5P", 4.0, 30.0), np.array([0.2588, 0.0907, 12.0000])), + (("5P", 4.0, 32.0), np.array([0.2574, 0.0833, 12.0000])), + (("5P", 4.0, 34.0), np.array([0.2560, 0.0740, 12.0000])), + (("5P", 4.0, 36.0), np.array([0.2550, 0.0690, 12.0000])), + (("5P", 4.0, 38.0), np.array([0.2540, 0.0630, 12.0000])), + (("5P", 4.0, 40.0), np.array([0.2540, 0.0560, 12.0000])), + (("7.5P", 4.0, 2.0), np.array([0.3093, 0.2859, 12.0000])), + (("7.5P", 4.0, 4.0), np.array([0.3084, 0.2622, 12.0000])), + (("7.5P", 4.0, 6.0), np.array([0.3076, 0.2416, 12.0000])), + (("7.5P", 4.0, 8.0), np.array([0.3066, 0.2228, 12.0000])), + (("7.5P", 4.0, 10.0), np.array([0.3056, 0.2060, 12.0000])), + (("7.5P", 4.0, 12.0), np.array([0.3045, 0.1905, 12.0000])), + (("7.5P", 4.0, 14.0), np.array([0.3035, 0.1755, 12.0000])), + (("7.5P", 4.0, 16.0), np.array([0.3028, 0.1621, 12.0000])), + (("7.5P", 4.0, 18.0), np.array([0.3016, 0.1500, 12.0000])), + (("7.5P", 4.0, 20.0), np.array([0.3010, 0.1396, 12.0000])), + (("7.5P", 4.0, 22.0), np.array([0.3001, 0.1306, 12.0000])), + (("7.5P", 4.0, 24.0), np.array([0.2993, 0.1225, 12.0000])), + (("7.5P", 4.0, 26.0), np.array([0.2986, 0.1135, 12.0000])), + (("7.5P", 4.0, 28.0), np.array([0.2979, 0.1062, 12.0000])), + (("7.5P", 4.0, 30.0), np.array([0.2969, 0.0979, 12.0000])), + (("7.5P", 4.0, 32.0), np.array([0.2962, 0.0906, 12.0000])), + (("7.5P", 4.0, 34.0), np.array([0.2950, 0.0820, 12.0000])), + (("7.5P", 4.0, 36.0), np.array([0.2950, 0.0750, 12.0000])), + (("7.5P", 4.0, 38.0), np.array([0.2940, 0.0690, 12.0000])), + (("7.5P", 4.0, 40.0), np.array([0.2930, 0.0620, 12.0000])), + (("10P", 4.0, 2.0), np.array([0.3162, 0.2902, 12.0000])), + (("10P", 4.0, 4.0), np.array([0.3210, 0.2686, 12.0000])), + (("10P", 4.0, 6.0), np.array([0.3248, 0.2493, 12.0000])), + (("10P", 4.0, 8.0), np.array([0.3280, 0.2318, 12.0000])), + (("10P", 4.0, 10.0), np.array([0.3306, 0.2162, 12.0000])), + (("10P", 4.0, 12.0), np.array([0.3331, 0.2014, 12.0000])), + (("10P", 4.0, 14.0), np.array([0.3351, 0.1875, 12.0000])), + (("10P", 4.0, 16.0), np.array([0.3370, 0.1756, 12.0000])), + (("10P", 4.0, 18.0), np.array([0.3386, 0.1626, 12.0000])), + (("10P", 4.0, 20.0), np.array([0.3400, 0.1500, 12.0000])), + (("10P", 4.0, 22.0), np.array([0.3411, 0.1424, 12.0000])), + (("10P", 4.0, 24.0), np.array([0.3421, 0.1337, 12.0000])), + (("10P", 4.0, 26.0), np.array([0.3428, 0.1248, 12.0000])), + (("10P", 4.0, 28.0), np.array([0.3432, 0.1172, 12.0000])), + (("10P", 4.0, 30.0), np.array([0.3440, 0.1080, 12.0000])), + (("10P", 4.0, 32.0), np.array([0.3450, 0.1000, 12.0000])), + (("10P", 4.0, 34.0), np.array([0.3460, 0.0930, 12.0000])), + (("10P", 4.0, 36.0), np.array([0.3460, 0.0850, 12.0000])), + (("10P", 4.0, 38.0), np.array([0.3470, 0.0780, 12.0000])), + (("2.5RP", 4.0, 2.0), np.array([0.3231, 0.2951, 12.0000])), + (("2.5RP", 4.0, 4.0), np.array([0.3340, 0.2770, 12.0000])), + (("2.5RP", 4.0, 6.0), np.array([0.3442, 0.2595, 12.0000])), + (("2.5RP", 4.0, 8.0), np.array([0.3533, 0.2438, 12.0000])), + (("2.5RP", 4.0, 10.0), np.array([0.3608, 0.2301, 12.0000])), + (("2.5RP", 4.0, 12.0), np.array([0.3683, 0.2162, 12.0000])), + (("2.5RP", 4.0, 14.0), np.array([0.3748, 0.2039, 12.0000])), + (("2.5RP", 4.0, 16.0), np.array([0.3807, 0.1923, 12.0000])), + (("2.5RP", 4.0, 18.0), np.array([0.3865, 0.1802, 12.0000])), + (("2.5RP", 4.0, 20.0), np.array([0.3926, 0.1679, 12.0000])), + (("2.5RP", 4.0, 22.0), np.array([0.3967, 0.1593, 12.0000])), + (("2.5RP", 4.0, 24.0), np.array([0.4011, 0.1504, 12.0000])), + (("2.5RP", 4.0, 26.0), np.array([0.4048, 0.1428, 12.0000])), + (("2.5RP", 4.0, 28.0), np.array([0.4090, 0.1340, 12.0000])), + (("2.5RP", 4.0, 30.0), np.array([0.4140, 0.1220, 12.0000])), + (("2.5RP", 4.0, 32.0), np.array([0.4170, 0.1150, 12.0000])), + (("2.5RP", 4.0, 34.0), np.array([0.4190, 0.1080, 12.0000])), + (("5RP", 4.0, 2.0), np.array([0.3310, 0.3010, 12.0000])), + (("5RP", 4.0, 4.0), np.array([0.3491, 0.2872, 12.0000])), + (("5RP", 4.0, 6.0), np.array([0.3671, 0.2733, 12.0000])), + (("5RP", 4.0, 8.0), np.array([0.3833, 0.2600, 12.0000])), + (("5RP", 4.0, 10.0), np.array([0.3960, 0.2489, 12.0000])), + (("5RP", 4.0, 12.0), np.array([0.4104, 0.2361, 12.0000])), + (("5RP", 4.0, 14.0), np.array([0.4225, 0.2249, 12.0000])), + (("5RP", 4.0, 16.0), np.array([0.4339, 0.2139, 12.0000])), + (("5RP", 4.0, 18.0), np.array([0.4455, 0.2023, 12.0000])), + (("5RP", 4.0, 20.0), np.array([0.4571, 0.1906, 12.0000])), + (("5RP", 4.0, 22.0), np.array([0.4656, 0.1821, 12.0000])), + (("5RP", 4.0, 24.0), np.array([0.4730, 0.1720, 12.0000])), + (("5RP", 4.0, 26.0), np.array([0.4820, 0.1620, 12.0000])), + (("5RP", 4.0, 28.0), np.array([0.4890, 0.1540, 12.0000])), + (("5RP", 4.0, 30.0), np.array([0.4990, 0.1430, 12.0000])), + (("7.5RP", 4.0, 2.0), np.array([0.3371, 0.3061, 12.0000])), + (("7.5RP", 4.0, 4.0), np.array([0.3612, 0.2963, 12.0000])), + (("7.5RP", 4.0, 6.0), np.array([0.3850, 0.2859, 12.0000])), + (("7.5RP", 4.0, 8.0), np.array([0.4072, 0.2750, 12.0000])), + (("7.5RP", 4.0, 10.0), np.array([0.4259, 0.2651, 12.0000])), + (("7.5RP", 4.0, 12.0), np.array([0.4450, 0.2541, 12.0000])), + (("7.5RP", 4.0, 14.0), np.array([0.4629, 0.2437, 12.0000])), + (("7.5RP", 4.0, 16.0), np.array([0.4799, 0.2329, 12.0000])), + (("7.5RP", 4.0, 18.0), np.array([0.4965, 0.2217, 12.0000])), + (("7.5RP", 4.0, 20.0), np.array([0.5130, 0.2101, 12.0000])), + (("7.5RP", 4.0, 22.0), np.array([0.5230, 0.2020, 12.0000])), + (("7.5RP", 4.0, 24.0), np.array([0.5360, 0.1920, 12.0000])), + (("7.5RP", 4.0, 26.0), np.array([0.5490, 0.1810, 12.0000])), + (("10RP", 4.0, 2.0), np.array([0.3417, 0.3106, 12.0000])), + (("10RP", 4.0, 4.0), np.array([0.3715, 0.3042, 12.0000])), + (("10RP", 4.0, 6.0), np.array([0.3999, 0.2972, 12.0000])), + (("10RP", 4.0, 8.0), np.array([0.4282, 0.2890, 12.0000])), + (("10RP", 4.0, 10.0), np.array([0.4528, 0.2811, 12.0000])), + (("10RP", 4.0, 12.0), np.array([0.4789, 0.2717, 12.0000])), + (("10RP", 4.0, 14.0), np.array([0.5020, 0.2623, 12.0000])), + (("10RP", 4.0, 16.0), np.array([0.5234, 0.2530, 12.0000])), + (("10RP", 4.0, 18.0), np.array([0.5466, 0.2424, 12.0000])), + (("10RP", 4.0, 20.0), np.array([0.5674, 0.2319, 12.0000])), + (("10RP", 4.0, 22.0), np.array([0.5820, 0.2240, 12.0000])), + (("10RP", 4.0, 24.0), np.array([0.5980, 0.2140, 12.0000])), + (("2.5R", 4.0, 2.0), np.array([0.3461, 0.3150, 12.0000])), + (("2.5R", 4.0, 4.0), np.array([0.3806, 0.3125, 12.0000])), + (("2.5R", 4.0, 6.0), np.array([0.4141, 0.3085, 12.0000])), + (("2.5R", 4.0, 8.0), np.array([0.4472, 0.3031, 12.0000])), + (("2.5R", 4.0, 10.0), np.array([0.4774, 0.2969, 12.0000])), + (("2.5R", 4.0, 12.0), np.array([0.5072, 0.2897, 12.0000])), + (("2.5R", 4.0, 14.0), np.array([0.5369, 0.2810, 12.0000])), + (("2.5R", 4.0, 16.0), np.array([0.5620, 0.2724, 12.0000])), + (("2.5R", 4.0, 18.0), np.array([0.5898, 0.2622, 12.0000])), + (("2.5R", 4.0, 20.0), np.array([0.6150, 0.2530, 12.0000])), + (("2.5R", 4.0, 22.0), np.array([0.6330, 0.2440, 12.0000])), + (("2.5R", 4.0, 24.0), np.array([0.6540, 0.2360, 12.0000])), + (("5R", 4.0, 2.0), np.array([0.3508, 0.3200, 12.0000])), + (("5R", 4.0, 4.0), np.array([0.3916, 0.3223, 12.0000])), + (("5R", 4.0, 6.0), np.array([0.4299, 0.3226, 12.0000])), + (("5R", 4.0, 8.0), np.array([0.4690, 0.3209, 12.0000])), + (("5R", 4.0, 10.0), np.array([0.5043, 0.3176, 12.0000])), + (("5R", 4.0, 12.0), np.array([0.5385, 0.3129, 12.0000])), + (("5R", 4.0, 14.0), np.array([0.5734, 0.3057, 12.0000])), + (("5R", 4.0, 16.0), np.array([0.6039, 0.2978, 12.0000])), + (("5R", 4.0, 18.0), np.array([0.6329, 0.2881, 12.0000])), + (("5R", 4.0, 20.0), np.array([0.6580, 0.2780, 12.0000])), + (("5R", 4.0, 22.0), np.array([0.6780, 0.2700, 12.0000])), + (("5R", 4.0, 24.0), np.array([0.6990, 0.2600, 12.0000])), + (("7.5R", 4.0, 2.0), np.array([0.3538, 0.3236, 12.0000])), + (("7.5R", 4.0, 4.0), np.array([0.3990, 0.3300, 12.0000])), + (("7.5R", 4.0, 6.0), np.array([0.4415, 0.3340, 12.0000])), + (("7.5R", 4.0, 8.0), np.array([0.4850, 0.3359, 12.0000])), + (("7.5R", 4.0, 10.0), np.array([0.5235, 0.3351, 12.0000])), + (("7.5R", 4.0, 12.0), np.array([0.5603, 0.3321, 12.0000])), + (("7.5R", 4.0, 14.0), np.array([0.5959, 0.3269, 12.0000])), + (("7.5R", 4.0, 16.0), np.array([0.6260, 0.3192, 12.0000])), + (("7.5R", 4.0, 18.0), np.array([0.6538, 0.3100, 12.0000])), + (("7.5R", 4.0, 20.0), np.array([0.6806, 0.2988, 12.0000])), + (("7.5R", 4.0, 22.0), np.array([0.7030, 0.2910, 12.0000])), + (("7.5R", 4.0, 24.0), np.array([0.7260, 0.2800, 12.0000])), + (("10R", 4.0, 2.0), np.array([0.3582, 0.3294, 12.0000])), + (("10R", 4.0, 4.0), np.array([0.4078, 0.3412, 12.0000])), + (("10R", 4.0, 6.0), np.array([0.4535, 0.3500, 12.0000])), + (("10R", 4.0, 8.0), np.array([0.4995, 0.3557, 12.0000])), + (("10R", 4.0, 10.0), np.array([0.5418, 0.3580, 12.0000])), + (("10R", 4.0, 12.0), np.array([0.5801, 0.3588, 12.0000])), + (("10R", 4.0, 14.0), np.array([0.6154, 0.3568, 12.0000])), + (("10R", 4.0, 16.0), np.array([0.6409, 0.3533, 12.0000])), + (("10R", 4.0, 18.0), np.array([0.6710, 0.3480, 12.0000])), + (("10R", 4.0, 20.0), np.array([0.6980, 0.3440, 12.0000])), + (("10R", 4.0, 22.0), np.array([0.7260, 0.3380, 12.0000])), + (("10R", 4.0, 24.0), np.array([0.7560, 0.3320, 12.0000])), + (("2.5YR", 4.0, 2.0), np.array([0.3624, 0.3367, 12.0000])), + (("2.5YR", 4.0, 4.0), np.array([0.4141, 0.3539, 12.0000])), + (("2.5YR", 4.0, 6.0), np.array([0.4612, 0.3674, 12.0000])), + (("2.5YR", 4.0, 8.0), np.array([0.5071, 0.3777, 12.0000])), + (("2.5YR", 4.0, 10.0), np.array([0.5475, 0.3856, 12.0000])), + (("2.5YR", 4.0, 12.0), np.array([0.5809, 0.3910, 12.0000])), + (("2.5YR", 4.0, 14.0), np.array([0.6140, 0.3960, 12.0000])), + (("2.5YR", 4.0, 16.0), np.array([0.6390, 0.4000, 12.0000])), + (("2.5YR", 4.0, 18.0), np.array([0.6690, 0.4040, 12.0000])), + (("5YR", 4.0, 2.0), np.array([0.3651, 0.3442, 12.0000])), + (("5YR", 4.0, 4.0), np.array([0.4187, 0.3679, 12.0000])), + (("5YR", 4.0, 6.0), np.array([0.4651, 0.3859, 12.0000])), + (("5YR", 4.0, 8.0), np.array([0.5070, 0.3994, 12.0000])), + (("5YR", 4.0, 10.0), np.array([0.5432, 0.4097, 12.0000])), + (("5YR", 4.0, 12.0), np.array([0.5729, 0.4169, 12.0000])), + (("5YR", 4.0, 14.0), np.array([0.6030, 0.4230, 12.0000])), + (("7.5YR", 4.0, 2.0), np.array([0.3662, 0.3504, 12.0000])), + (("7.5YR", 4.0, 4.0), np.array([0.4208, 0.3809, 12.0000])), + (("7.5YR", 4.0, 6.0), np.array([0.4655, 0.4029, 12.0000])), + (("7.5YR", 4.0, 8.0), np.array([0.5038, 0.4204, 12.0000])), + (("7.5YR", 4.0, 10.0), np.array([0.5356, 0.4342, 12.0000])), + (("7.5YR", 4.0, 12.0), np.array([0.5590, 0.4450, 12.0000])), + (("7.5YR", 4.0, 14.0), np.array([0.5870, 0.4560, 12.0000])), + (("10YR", 4.0, 2.0), np.array([0.3660, 0.3590, 12.0000])), + (("10YR", 4.0, 4.0), np.array([0.4189, 0.3948, 12.0000])), + (("10YR", 4.0, 6.0), np.array([0.4618, 0.4213, 12.0000])), + (("10YR", 4.0, 8.0), np.array([0.4965, 0.4414, 12.0000])), + (("10YR", 4.0, 10.0), np.array([0.5250, 0.4573, 12.0000])), + (("10YR", 4.0, 12.0), np.array([0.5450, 0.4680, 12.0000])), + (("2.5Y", 4.0, 2.0), np.array([0.3633, 0.3654, 12.0000])), + (("2.5Y", 4.0, 4.0), np.array([0.4138, 0.4076, 12.0000])), + (("2.5Y", 4.0, 6.0), np.array([0.4542, 0.4391, 12.0000])), + (("2.5Y", 4.0, 8.0), np.array([0.4865, 0.4625, 12.0000])), + (("2.5Y", 4.0, 10.0), np.array([0.5120, 0.4800, 12.0000])), + (("2.5Y", 4.0, 12.0), np.array([0.5290, 0.4920, 12.0000])), + (("5Y", 4.0, 2.0), np.array([0.3590, 0.3701, 12.0000])), + (("5Y", 4.0, 4.0), np.array([0.4069, 0.4188, 12.0000])), + (("5Y", 4.0, 6.0), np.array([0.4451, 0.4550, 12.0000])), + (("5Y", 4.0, 8.0), np.array([0.4745, 0.4810, 12.0000])), + (("5Y", 4.0, 10.0), np.array([0.4960, 0.5030, 12.0000])), + (("5Y", 4.0, 12.0), np.array([0.5120, 0.5160, 12.0000])), + (("7.5Y", 4.0, 2.0), np.array([0.3542, 0.3727, 12.0000])), + (("7.5Y", 4.0, 4.0), np.array([0.3982, 0.4272, 12.0000])), + (("7.5Y", 4.0, 6.0), np.array([0.4331, 0.4688, 12.0000])), + (("7.5Y", 4.0, 8.0), np.array([0.4595, 0.4990, 12.0000])), + (("7.5Y", 4.0, 10.0), np.array([0.4810, 0.5230, 12.0000])), + (("7.5Y", 4.0, 12.0), np.array([0.4940, 0.5380, 12.0000])), + (("10Y", 5.0, 2.0), np.array([0.3422, 0.3648, 19.7700])), + (("10Y", 5.0, 4.0), np.array([0.3762, 0.4158, 19.7700])), + (("10Y", 5.0, 6.0), np.array([0.4072, 0.4621, 19.7700])), + (("10Y", 5.0, 8.0), np.array([0.4307, 0.4967, 19.7700])), + (("10Y", 5.0, 10.0), np.array([0.4468, 0.5209, 19.7700])), + (("10Y", 5.0, 12.0), np.array([0.4590, 0.5390, 19.7700])), + (("10Y", 5.0, 14.0), np.array([0.4690, 0.5540, 19.7700])), + (("2.5GY", 5.0, 2.0), np.array([0.3352, 0.3636, 19.7700])), + (("2.5GY", 5.0, 4.0), np.array([0.3621, 0.4143, 19.7700])), + (("2.5GY", 5.0, 6.0), np.array([0.3879, 0.4646, 19.7700])), + (("2.5GY", 5.0, 8.0), np.array([0.4088, 0.5068, 19.7700])), + (("2.5GY", 5.0, 10.0), np.array([0.4224, 0.5369, 19.7700])), + (("2.5GY", 5.0, 12.0), np.array([0.4333, 0.5602, 19.7700])), + (("2.5GY", 5.0, 14.0), np.array([0.4400, 0.5800, 19.7700])), + (("5GY", 5.0, 2.0), np.array([0.3289, 0.3612, 19.7700])), + (("5GY", 5.0, 4.0), np.array([0.3482, 0.4097, 19.7700])), + (("5GY", 5.0, 6.0), np.array([0.3663, 0.4614, 19.7700])), + (("5GY", 5.0, 8.0), np.array([0.3815, 0.5093, 19.7700])), + (("5GY", 5.0, 10.0), np.array([0.3928, 0.5485, 19.7700])), + (("5GY", 5.0, 12.0), np.array([0.4011, 0.5802, 19.7700])), + (("5GY", 5.0, 14.0), np.array([0.4070, 0.6040, 19.7700])), + (("5GY", 5.0, 16.0), np.array([0.4100, 0.6260, 19.7700])), + (("7.5GY", 5.0, 2.0), np.array([0.3188, 0.3560, 19.7700])), + (("7.5GY", 5.0, 4.0), np.array([0.3274, 0.3994, 19.7700])), + (("7.5GY", 5.0, 6.0), np.array([0.3354, 0.4483, 19.7700])), + (("7.5GY", 5.0, 8.0), np.array([0.3412, 0.4976, 19.7700])), + (("7.5GY", 5.0, 10.0), np.array([0.3451, 0.5490, 19.7700])), + (("7.5GY", 5.0, 12.0), np.array([0.3450, 0.5949, 19.7700])), + (("7.5GY", 5.0, 14.0), np.array([0.3429, 0.6335, 19.7700])), + (("7.5GY", 5.0, 16.0), np.array([0.3410, 0.6660, 19.7700])), + (("7.5GY", 5.0, 18.0), np.array([0.3390, 0.6970, 19.7700])), + (("7.5GY", 5.0, 20.0), np.array([0.3330, 0.7330, 19.7700])), + (("10GY", 5.0, 2.0), np.array([0.3110, 0.3508, 19.7700])), + (("10GY", 5.0, 4.0), np.array([0.3111, 0.3881, 19.7700])), + (("10GY", 5.0, 6.0), np.array([0.3108, 0.4301, 19.7700])), + (("10GY", 5.0, 8.0), np.array([0.3080, 0.4759, 19.7700])), + (("10GY", 5.0, 10.0), np.array([0.3028, 0.5237, 19.7700])), + (("10GY", 5.0, 12.0), np.array([0.2940, 0.5751, 19.7700])), + (("10GY", 5.0, 14.0), np.array([0.2838, 0.6208, 19.7700])), + (("10GY", 5.0, 16.0), np.array([0.2702, 0.6700, 19.7700])), + (("10GY", 5.0, 18.0), np.array([0.2549, 0.7179, 19.7700])), + (("10GY", 5.0, 20.0), np.array([0.2370, 0.7670, 19.7700])), + (("10GY", 5.0, 22.0), np.array([0.2210, 0.8080, 19.7700])), + (("10GY", 5.0, 24.0), np.array([0.2020, 0.8520, 19.7700])), + (("10GY", 5.0, 26.0), np.array([0.1790, 0.8980, 19.7700])), + (("10GY", 5.0, 28.0), np.array([0.1540, 0.9490, 19.7700])), + (("10GY", 5.0, 30.0), np.array([0.1250, 1.0000, 19.7700])), + (("10GY", 5.0, 32.0), np.array([0.0970, 1.0480, 19.7700])), + (("2.5G", 5.0, 2.0), np.array([0.3030, 0.3445, 19.7700])), + (("2.5G", 5.0, 4.0), np.array([0.2943, 0.3735, 19.7700])), + (("2.5G", 5.0, 6.0), np.array([0.2841, 0.4045, 19.7700])), + (("2.5G", 5.0, 8.0), np.array([0.2710, 0.4380, 19.7700])), + (("2.5G", 5.0, 10.0), np.array([0.2565, 0.4705, 19.7700])), + (("2.5G", 5.0, 12.0), np.array([0.2385, 0.5071, 19.7700])), + (("2.5G", 5.0, 14.0), np.array([0.2211, 0.5411, 19.7700])), + (("2.5G", 5.0, 16.0), np.array([0.2005, 0.5759, 19.7700])), + (("2.5G", 5.0, 18.0), np.array([0.1782, 0.6095, 19.7700])), + (("2.5G", 5.0, 20.0), np.array([0.1579, 0.6392, 19.7700])), + (("2.5G", 5.0, 22.0), np.array([0.1377, 0.6674, 19.7700])), + (("2.5G", 5.0, 24.0), np.array([0.1188, 0.6918, 19.7700])), + (("2.5G", 5.0, 26.0), np.array([0.0992, 0.7155, 19.7700])), + (("2.5G", 5.0, 28.0), np.array([0.0794, 0.7385, 19.7700])), + (("2.5G", 5.0, 30.0), np.array([0.0550, 0.7660, 19.7700])), + (("2.5G", 5.0, 32.0), np.array([0.0340, 0.7890, 19.7700])), + (("5G", 5.0, 2.0), np.array([0.2978, 0.3392, 19.7700])), + (("5G", 5.0, 4.0), np.array([0.2841, 0.3628, 19.7700])), + (("5G", 5.0, 6.0), np.array([0.2690, 0.3860, 19.7700])), + (("5G", 5.0, 8.0), np.array([0.2511, 0.4107, 19.7700])), + (("5G", 5.0, 10.0), np.array([0.2329, 0.4331, 19.7700])), + (("5G", 5.0, 12.0), np.array([0.2104, 0.4578, 19.7700])), + (("5G", 5.0, 14.0), np.array([0.1912, 0.4773, 19.7700])), + (("5G", 5.0, 16.0), np.array([0.1695, 0.4981, 19.7700])), + (("5G", 5.0, 18.0), np.array([0.1489, 0.5171, 19.7700])), + (("5G", 5.0, 20.0), np.array([0.1318, 0.5321, 19.7700])), + (("5G", 5.0, 22.0), np.array([0.1144, 0.5463, 19.7700])), + (("5G", 5.0, 24.0), np.array([0.0953, 0.5628, 19.7700])), + (("5G", 5.0, 26.0), np.array([0.0784, 0.5761, 19.7700])), + (("5G", 5.0, 28.0), np.array([0.0609, 0.5898, 19.7700])), + (("5G", 5.0, 30.0), np.array([0.0400, 0.6050, 19.7700])), + (("5G", 5.0, 32.0), np.array([0.0200, 0.6220, 19.7700])), + (("7.5G", 5.0, 2.0), np.array([0.2945, 0.3355, 19.7700])), + (("7.5G", 5.0, 4.0), np.array([0.2775, 0.3545, 19.7700])), + (("7.5G", 5.0, 6.0), np.array([0.2598, 0.3724, 19.7700])), + (("7.5G", 5.0, 8.0), np.array([0.2395, 0.3915, 19.7700])), + (("7.5G", 5.0, 10.0), np.array([0.2200, 0.4082, 19.7700])), + (("7.5G", 5.0, 12.0), np.array([0.1964, 0.4271, 19.7700])), + (("7.5G", 5.0, 14.0), np.array([0.1776, 0.4415, 19.7700])), + (("7.5G", 5.0, 16.0), np.array([0.1571, 0.4561, 19.7700])), + (("7.5G", 5.0, 18.0), np.array([0.1372, 0.4705, 19.7700])), + (("7.5G", 5.0, 20.0), np.array([0.1212, 0.4817, 19.7700])), + (("7.5G", 5.0, 22.0), np.array([0.1050, 0.4927, 19.7700])), + (("7.5G", 5.0, 24.0), np.array([0.0878, 0.5039, 19.7700])), + (("7.5G", 5.0, 26.0), np.array([0.0730, 0.5131, 19.7700])), + (("7.5G", 5.0, 28.0), np.array([0.0585, 0.5224, 19.7700])), + (("7.5G", 5.0, 30.0), np.array([0.0410, 0.5320, 19.7700])), + (("7.5G", 5.0, 32.0), np.array([0.0210, 0.5420, 19.7700])), + (("10G", 5.0, 2.0), np.array([0.2910, 0.3310, 19.7700])), + (("10G", 5.0, 4.0), np.array([0.2711, 0.3455, 19.7700])), + (("10G", 5.0, 6.0), np.array([0.2519, 0.3587, 19.7700])), + (("10G", 5.0, 8.0), np.array([0.2297, 0.3730, 19.7700])), + (("10G", 5.0, 10.0), np.array([0.2095, 0.3853, 19.7700])), + (("10G", 5.0, 12.0), np.array([0.1852, 0.3992, 19.7700])), + (("10G", 5.0, 14.0), np.array([0.1671, 0.4089, 19.7700])), + (("10G", 5.0, 16.0), np.array([0.1469, 0.4192, 19.7700])), + (("10G", 5.0, 18.0), np.array([0.1275, 0.4288, 19.7700])), + (("10G", 5.0, 20.0), np.array([0.1120, 0.4360, 19.7700])), + (("10G", 5.0, 22.0), np.array([0.0958, 0.4428, 19.7700])), + (("10G", 5.0, 24.0), np.array([0.0811, 0.4491, 19.7700])), + (("10G", 5.0, 26.0), np.array([0.0690, 0.4542, 19.7700])), + (("10G", 5.0, 28.0), np.array([0.0572, 0.4590, 19.7700])), + (("10G", 5.0, 30.0), np.array([0.0410, 0.4660, 19.7700])), + (("10G", 5.0, 32.0), np.array([0.0230, 0.4740, 19.7700])), + (("2.5BG", 5.0, 2.0), np.array([0.2880, 0.3270, 19.7700])), + (("2.5BG", 5.0, 4.0), np.array([0.2659, 0.3369, 19.7700])), + (("2.5BG", 5.0, 6.0), np.array([0.2448, 0.3452, 19.7700])), + (("2.5BG", 5.0, 8.0), np.array([0.2205, 0.3537, 19.7700])), + (("2.5BG", 5.0, 10.0), np.array([0.1980, 0.3606, 19.7700])), + (("2.5BG", 5.0, 12.0), np.array([0.1735, 0.3668, 19.7700])), + (("2.5BG", 5.0, 14.0), np.array([0.1559, 0.3708, 19.7700])), + (("2.5BG", 5.0, 16.0), np.array([0.1348, 0.3750, 19.7700])), + (("2.5BG", 5.0, 18.0), np.array([0.1165, 0.3785, 19.7700])), + (("2.5BG", 5.0, 20.0), np.array([0.1005, 0.3814, 19.7700])), + (("2.5BG", 5.0, 22.0), np.array([0.0861, 0.3832, 19.7700])), + (("2.5BG", 5.0, 24.0), np.array([0.0738, 0.3851, 19.7700])), + (("2.5BG", 5.0, 26.0), np.array([0.0640, 0.3860, 19.7700])), + (("2.5BG", 5.0, 28.0), np.array([0.0530, 0.3880, 19.7700])), + (("2.5BG", 5.0, 30.0), np.array([0.0420, 0.3890, 19.7700])), + (("2.5BG", 5.0, 32.0), np.array([0.0270, 0.3910, 19.7700])), + (("5BG", 5.0, 2.0), np.array([0.2841, 0.3210, 19.7700])), + (("5BG", 5.0, 4.0), np.array([0.2591, 0.3246, 19.7700])), + (("5BG", 5.0, 6.0), np.array([0.2360, 0.3270, 19.7700])), + (("5BG", 5.0, 8.0), np.array([0.2100, 0.3280, 19.7700])), + (("5BG", 5.0, 10.0), np.array([0.1850, 0.3280, 19.7700])), + (("5BG", 5.0, 12.0), np.array([0.1614, 0.3280, 19.7700])), + (("5BG", 5.0, 14.0), np.array([0.1448, 0.3275, 19.7700])), + (("5BG", 5.0, 16.0), np.array([0.1243, 0.3261, 19.7700])), + (("5BG", 5.0, 18.0), np.array([0.1046, 0.3244, 19.7700])), + (("5BG", 5.0, 20.0), np.array([0.0904, 0.3231, 19.7700])), + (("5BG", 5.0, 22.0), np.array([0.0781, 0.3211, 19.7700])), + (("5BG", 5.0, 24.0), np.array([0.0670, 0.3200, 19.7700])), + (("5BG", 5.0, 26.0), np.array([0.0580, 0.3180, 19.7700])), + (("5BG", 5.0, 28.0), np.array([0.0500, 0.3170, 19.7700])), + (("5BG", 5.0, 30.0), np.array([0.0420, 0.3160, 19.7700])), + (("7.5BG", 5.0, 2.0), np.array([0.2812, 0.3161, 19.7700])), + (("7.5BG", 5.0, 4.0), np.array([0.2550, 0.3150, 19.7700])), + (("7.5BG", 5.0, 6.0), np.array([0.2292, 0.3125, 19.7700])), + (("7.5BG", 5.0, 8.0), np.array([0.2030, 0.3082, 19.7700])), + (("7.5BG", 5.0, 10.0), np.array([0.1776, 0.3032, 19.7700])), + (("7.5BG", 5.0, 12.0), np.array([0.1537, 0.2976, 19.7700])), + (("7.5BG", 5.0, 14.0), np.array([0.1364, 0.2932, 19.7700])), + (("7.5BG", 5.0, 16.0), np.array([0.1167, 0.2880, 19.7700])), + (("7.5BG", 5.0, 18.0), np.array([0.0982, 0.2828, 19.7700])), + (("7.5BG", 5.0, 20.0), np.array([0.0830, 0.2780, 19.7700])), + (("7.5BG", 5.0, 22.0), np.array([0.0710, 0.2740, 19.7700])), + (("7.5BG", 5.0, 24.0), np.array([0.0620, 0.2700, 19.7700])), + (("7.5BG", 5.0, 26.0), np.array([0.0550, 0.2680, 19.7700])), + (("10BG", 5.0, 2.0), np.array([0.2796, 0.3111, 19.7700])), + (("10BG", 5.0, 4.0), np.array([0.2512, 0.3040, 19.7700])), + (("10BG", 5.0, 6.0), np.array([0.2234, 0.2952, 19.7700])), + (("10BG", 5.0, 8.0), np.array([0.1970, 0.2860, 19.7700])), + (("10BG", 5.0, 10.0), np.array([0.1716, 0.2760, 19.7700])), + (("10BG", 5.0, 12.0), np.array([0.1485, 0.2662, 19.7700])), + (("10BG", 5.0, 14.0), np.array([0.1308, 0.2582, 19.7700])), + (("10BG", 5.0, 16.0), np.array([0.1108, 0.2489, 19.7700])), + (("10BG", 5.0, 18.0), np.array([0.0930, 0.2400, 19.7700])), + (("10BG", 5.0, 20.0), np.array([0.0790, 0.2320, 19.7700])), + (("10BG", 5.0, 22.0), np.array([0.0660, 0.2260, 19.7700])), + (("10BG", 5.0, 24.0), np.array([0.0580, 0.2210, 19.7700])), + (("2.5B", 5.0, 2.0), np.array([0.2791, 0.3071, 19.7700])), + (("2.5B", 5.0, 4.0), np.array([0.2492, 0.2954, 19.7700])), + (("2.5B", 5.0, 6.0), np.array([0.2210, 0.2823, 19.7700])), + (("2.5B", 5.0, 8.0), np.array([0.1947, 0.2687, 19.7700])), + (("2.5B", 5.0, 10.0), np.array([0.1697, 0.2549, 19.7700])), + (("2.5B", 5.0, 12.0), np.array([0.1461, 0.2406, 19.7700])), + (("2.5B", 5.0, 14.0), np.array([0.1283, 0.2292, 19.7700])), + (("2.5B", 5.0, 16.0), np.array([0.1090, 0.2166, 19.7700])), + (("2.5B", 5.0, 18.0), np.array([0.0940, 0.2060, 19.7700])), + (("2.5B", 5.0, 20.0), np.array([0.0770, 0.1940, 19.7700])), + (("5B", 5.0, 2.0), np.array([0.2794, 0.3032, 19.7700])), + (("5B", 5.0, 4.0), np.array([0.2493, 0.2879, 19.7700])), + (("5B", 5.0, 6.0), np.array([0.2215, 0.2701, 19.7700])), + (("5B", 5.0, 8.0), np.array([0.1958, 0.2519, 19.7700])), + (("5B", 5.0, 10.0), np.array([0.1729, 0.2347, 19.7700])), + (("5B", 5.0, 12.0), np.array([0.1505, 0.2172, 19.7700])), + (("5B", 5.0, 14.0), np.array([0.1320, 0.2021, 19.7700])), + (("5B", 5.0, 16.0), np.array([0.1132, 0.1863, 19.7700])), + (("5B", 5.0, 18.0), np.array([0.0980, 0.1720, 19.7700])), + (("5B", 5.0, 20.0), np.array([0.0860, 0.1600, 19.7700])), + (("7.5B", 5.0, 2.0), np.array([0.2803, 0.3000, 19.7700])), + (("7.5B", 5.0, 4.0), np.array([0.2511, 0.2808, 19.7700])), + (("7.5B", 5.0, 6.0), np.array([0.2248, 0.2612, 19.7700])), + (("7.5B", 5.0, 8.0), np.array([0.2007, 0.2417, 19.7700])), + (("7.5B", 5.0, 10.0), np.array([0.1792, 0.2230, 19.7700])), + (("7.5B", 5.0, 12.0), np.array([0.1584, 0.2042, 19.7700])), + (("7.5B", 5.0, 14.0), np.array([0.1404, 0.1878, 19.7700])), + (("7.5B", 5.0, 16.0), np.array([0.1230, 0.1711, 19.7700])), + (("7.5B", 5.0, 18.0), np.array([0.1090, 0.1570, 19.7700])), + (("7.5B", 5.0, 20.0), np.array([0.0980, 0.1460, 19.7700])), + (("10B", 5.0, 2.0), np.array([0.2821, 0.2966, 19.7700])), + (("10B", 5.0, 4.0), np.array([0.2547, 0.2757, 19.7700])), + (("10B", 5.0, 6.0), np.array([0.2299, 0.2548, 19.7700])), + (("10B", 5.0, 8.0), np.array([0.2067, 0.2344, 19.7700])), + (("10B", 5.0, 10.0), np.array([0.1860, 0.2149, 19.7700])), + (("10B", 5.0, 12.0), np.array([0.1666, 0.1964, 19.7700])), + (("10B", 5.0, 14.0), np.array([0.1492, 0.1797, 19.7700])), + (("10B", 5.0, 16.0), np.array([0.1326, 0.1632, 19.7700])), + (("10B", 5.0, 18.0), np.array([0.1203, 0.1505, 19.7700])), + (("10B", 5.0, 20.0), np.array([0.1080, 0.1370, 19.7700])), + (("10B", 5.0, 22.0), np.array([0.0960, 0.1260, 19.7700])), + (("2.5PB", 5.0, 2.0), np.array([0.2847, 0.2942, 19.7700])), + (("2.5PB", 5.0, 4.0), np.array([0.2600, 0.2720, 19.7700])), + (("2.5PB", 5.0, 6.0), np.array([0.2365, 0.2488, 19.7700])), + (("2.5PB", 5.0, 8.0), np.array([0.2157, 0.2278, 19.7700])), + (("2.5PB", 5.0, 10.0), np.array([0.1968, 0.2078, 19.7700])), + (("2.5PB", 5.0, 12.0), np.array([0.1793, 0.1894, 19.7700])), + (("2.5PB", 5.0, 14.0), np.array([0.1642, 0.1728, 19.7700])), + (("2.5PB", 5.0, 16.0), np.array([0.1495, 0.1559, 19.7700])), + (("2.5PB", 5.0, 18.0), np.array([0.1363, 0.1410, 19.7700])), + (("2.5PB", 5.0, 20.0), np.array([0.1240, 0.1280, 19.7700])), + (("2.5PB", 5.0, 22.0), np.array([0.1140, 0.1160, 19.7700])), + (("2.5PB", 5.0, 24.0), np.array([0.1030, 0.1040, 19.7700])), + (("5PB", 5.0, 2.0), np.array([0.2882, 0.2923, 19.7700])), + (("5PB", 5.0, 4.0), np.array([0.2662, 0.2687, 19.7700])), + (("5PB", 5.0, 6.0), np.array([0.2447, 0.2449, 19.7700])), + (("5PB", 5.0, 8.0), np.array([0.2255, 0.2239, 19.7700])), + (("5PB", 5.0, 10.0), np.array([0.2080, 0.2041, 19.7700])), + (("5PB", 5.0, 12.0), np.array([0.1918, 0.1858, 19.7700])), + (("5PB", 5.0, 14.0), np.array([0.1773, 0.1689, 19.7700])), + (("5PB", 5.0, 16.0), np.array([0.1638, 0.1521, 19.7700])), + (("5PB", 5.0, 18.0), np.array([0.1518, 0.1365, 19.7700])), + (("5PB", 5.0, 20.0), np.array([0.1420, 0.1230, 19.7700])), + (("5PB", 5.0, 22.0), np.array([0.1340, 0.1130, 19.7700])), + (("5PB", 5.0, 24.0), np.array([0.1240, 0.1000, 19.7700])), + (("5PB", 5.0, 26.0), np.array([0.1160, 0.0890, 19.7700])), + (("5PB", 5.0, 28.0), np.array([0.1090, 0.0790, 19.7700])), + (("5PB", 5.0, 30.0), np.array([0.1040, 0.0700, 19.7700])), + (("5PB", 5.0, 32.0), np.array([0.0970, 0.0620, 19.7700])), + (("7.5PB", 5.0, 2.0), np.array([0.2918, 0.2908, 19.7700])), + (("7.5PB", 5.0, 4.0), np.array([0.2739, 0.2666, 19.7700])), + (("7.5PB", 5.0, 6.0), np.array([0.2563, 0.2417, 19.7700])), + (("7.5PB", 5.0, 8.0), np.array([0.2417, 0.2204, 19.7700])), + (("7.5PB", 5.0, 10.0), np.array([0.2285, 0.2020, 19.7700])), + (("7.5PB", 5.0, 12.0), np.array([0.2157, 0.1830, 19.7700])), + (("7.5PB", 5.0, 14.0), np.array([0.2042, 0.1661, 19.7700])), + (("7.5PB", 5.0, 16.0), np.array([0.1945, 0.1511, 19.7700])), + (("7.5PB", 5.0, 18.0), np.array([0.1862, 0.1365, 19.7700])), + (("7.5PB", 5.0, 20.0), np.array([0.1794, 0.1239, 19.7700])), + (("7.5PB", 5.0, 22.0), np.array([0.1750, 0.1140, 19.7700])), + (("7.5PB", 5.0, 24.0), np.array([0.1700, 0.1030, 19.7700])), + (("7.5PB", 5.0, 26.0), np.array([0.1650, 0.0940, 19.7700])), + (("7.5PB", 5.0, 28.0), np.array([0.1620, 0.0860, 19.7700])), + (("7.5PB", 5.0, 30.0), np.array([0.1580, 0.0780, 19.7700])), + (("7.5PB", 5.0, 32.0), np.array([0.1540, 0.0700, 19.7700])), + (("7.5PB", 5.0, 34.0), np.array([0.1500, 0.0620, 19.7700])), + (("10PB", 5.0, 2.0), np.array([0.2959, 0.2905, 19.7700])), + (("10PB", 5.0, 4.0), np.array([0.2821, 0.2659, 19.7700])), + (("10PB", 5.0, 6.0), np.array([0.2686, 0.2412, 19.7700])), + (("10PB", 5.0, 8.0), np.array([0.2572, 0.2211, 19.7700])), + (("10PB", 5.0, 10.0), np.array([0.2478, 0.2030, 19.7700])), + (("10PB", 5.0, 12.0), np.array([0.2384, 0.1857, 19.7700])), + (("10PB", 5.0, 14.0), np.array([0.2299, 0.1698, 19.7700])), + (("10PB", 5.0, 16.0), np.array([0.2224, 0.1555, 19.7700])), + (("10PB", 5.0, 18.0), np.array([0.2174, 0.1444, 19.7700])), + (("10PB", 5.0, 20.0), np.array([0.2121, 0.1329, 19.7700])), + (("10PB", 5.0, 22.0), np.array([0.2082, 0.1225, 19.7700])), + (("10PB", 5.0, 24.0), np.array([0.2040, 0.1130, 19.7700])), + (("10PB", 5.0, 26.0), np.array([0.2000, 0.1030, 19.7700])), + (("10PB", 5.0, 28.0), np.array([0.1970, 0.0950, 19.7700])), + (("10PB", 5.0, 30.0), np.array([0.1930, 0.0860, 19.7700])), + (("10PB", 5.0, 32.0), np.array([0.1890, 0.0780, 19.7700])), + (("10PB", 5.0, 34.0), np.array([0.1860, 0.0710, 19.7700])), + (("10PB", 5.0, 36.0), np.array([0.1830, 0.0630, 19.7700])), + (("2.5P", 5.0, 2.0), np.array([0.3000, 0.2912, 19.7700])), + (("2.5P", 5.0, 4.0), np.array([0.2898, 0.2667, 19.7700])), + (("2.5P", 5.0, 6.0), np.array([0.2806, 0.2444, 19.7700])), + (("2.5P", 5.0, 8.0), np.array([0.2728, 0.2240, 19.7700])), + (("2.5P", 5.0, 10.0), np.array([0.2665, 0.2075, 19.7700])), + (("2.5P", 5.0, 12.0), np.array([0.2608, 0.1913, 19.7700])), + (("2.5P", 5.0, 14.0), np.array([0.2560, 0.1774, 19.7700])), + (("2.5P", 5.0, 16.0), np.array([0.2515, 0.1644, 19.7700])), + (("2.5P", 5.0, 18.0), np.array([0.2476, 0.1532, 19.7700])), + (("2.5P", 5.0, 20.0), np.array([0.2438, 0.1419, 19.7700])), + (("2.5P", 5.0, 22.0), np.array([0.2402, 0.1315, 19.7700])), + (("2.5P", 5.0, 24.0), np.array([0.2372, 0.1223, 19.7700])), + (("2.5P", 5.0, 26.0), np.array([0.2348, 0.1140, 19.7700])), + (("2.5P", 5.0, 28.0), np.array([0.2310, 0.1040, 19.7700])), + (("2.5P", 5.0, 30.0), np.array([0.2290, 0.0970, 19.7700])), + (("2.5P", 5.0, 32.0), np.array([0.2260, 0.0880, 19.7700])), + (("2.5P", 5.0, 34.0), np.array([0.2230, 0.0800, 19.7700])), + (("2.5P", 5.0, 36.0), np.array([0.2210, 0.0720, 19.7700])), + (("2.5P", 5.0, 38.0), np.array([0.2190, 0.0660, 19.7700])), + (("5P", 5.0, 2.0), np.array([0.3045, 0.2928, 19.7700])), + (("5P", 5.0, 4.0), np.array([0.2986, 0.2699, 19.7700])), + (("5P", 5.0, 6.0), np.array([0.2932, 0.2487, 19.7700])), + (("5P", 5.0, 8.0), np.array([0.2885, 0.2296, 19.7700])), + (("5P", 5.0, 10.0), np.array([0.2845, 0.2137, 19.7700])), + (("5P", 5.0, 12.0), np.array([0.2806, 0.1977, 19.7700])), + (("5P", 5.0, 14.0), np.array([0.2775, 0.1847, 19.7700])), + (("5P", 5.0, 16.0), np.array([0.2744, 0.1718, 19.7700])), + (("5P", 5.0, 18.0), np.array([0.2718, 0.1604, 19.7700])), + (("5P", 5.0, 20.0), np.array([0.2694, 0.1499, 19.7700])), + (("5P", 5.0, 22.0), np.array([0.2673, 0.1398, 19.7700])), + (("5P", 5.0, 24.0), np.array([0.2652, 0.1304, 19.7700])), + (("5P", 5.0, 26.0), np.array([0.2635, 0.1224, 19.7700])), + (("5P", 5.0, 28.0), np.array([0.2618, 0.1135, 19.7700])), + (("5P", 5.0, 30.0), np.array([0.2590, 0.1050, 19.7700])), + (("5P", 5.0, 32.0), np.array([0.2560, 0.0960, 19.7700])), + (("5P", 5.0, 34.0), np.array([0.2550, 0.0880, 19.7700])), + (("5P", 5.0, 36.0), np.array([0.2530, 0.0810, 19.7700])), + (("5P", 5.0, 38.0), np.array([0.2520, 0.0750, 19.7700])), + (("5P", 5.0, 40.0), np.array([0.2510, 0.0690, 19.7700])), + (("7.5P", 5.0, 2.0), np.array([0.3103, 0.2959, 19.7700])), + (("7.5P", 5.0, 4.0), np.array([0.3100, 0.2750, 19.7700])), + (("7.5P", 5.0, 6.0), np.array([0.3093, 0.2555, 19.7700])), + (("7.5P", 5.0, 8.0), np.array([0.3087, 0.2375, 19.7700])), + (("7.5P", 5.0, 10.0), np.array([0.3080, 0.2230, 19.7700])), + (("7.5P", 5.0, 12.0), np.array([0.3071, 0.2080, 19.7700])), + (("7.5P", 5.0, 14.0), np.array([0.3068, 0.1951, 19.7700])), + (("7.5P", 5.0, 16.0), np.array([0.3060, 0.1830, 19.7700])), + (("7.5P", 5.0, 18.0), np.array([0.3052, 0.1711, 19.7700])), + (("7.5P", 5.0, 20.0), np.array([0.3042, 0.1606, 19.7700])), + (("7.5P", 5.0, 22.0), np.array([0.3038, 0.1500, 19.7700])), + (("7.5P", 5.0, 24.0), np.array([0.3030, 0.1423, 19.7700])), + (("7.5P", 5.0, 26.0), np.array([0.3022, 0.1331, 19.7700])), + (("7.5P", 5.0, 28.0), np.array([0.3018, 0.1253, 19.7700])), + (("7.5P", 5.0, 30.0), np.array([0.3010, 0.1170, 19.7700])), + (("7.5P", 5.0, 32.0), np.array([0.3000, 0.1080, 19.7700])), + (("7.5P", 5.0, 34.0), np.array([0.3000, 0.1000, 19.7700])), + (("7.5P", 5.0, 36.0), np.array([0.2990, 0.0930, 19.7700])), + (("7.5P", 5.0, 38.0), np.array([0.2980, 0.0880, 19.7700])), + (("7.5P", 5.0, 40.0), np.array([0.2980, 0.0810, 19.7700])), + (("10P", 5.0, 2.0), np.array([0.3148, 0.2986, 19.7700])), + (("10P", 5.0, 4.0), np.array([0.3198, 0.2807, 19.7700])), + (("10P", 5.0, 6.0), np.array([0.3243, 0.2630, 19.7700])), + (("10P", 5.0, 8.0), np.array([0.3280, 0.2464, 19.7700])), + (("10P", 5.0, 10.0), np.array([0.3308, 0.2328, 19.7700])), + (("10P", 5.0, 12.0), np.array([0.3335, 0.2187, 19.7700])), + (("10P", 5.0, 14.0), np.array([0.3360, 0.2066, 19.7700])), + (("10P", 5.0, 16.0), np.array([0.3382, 0.1951, 19.7700])), + (("10P", 5.0, 18.0), np.array([0.3401, 0.1840, 19.7700])), + (("10P", 5.0, 20.0), np.array([0.3422, 0.1735, 19.7700])), + (("10P", 5.0, 22.0), np.array([0.3437, 0.1644, 19.7700])), + (("10P", 5.0, 24.0), np.array([0.3450, 0.1555, 19.7700])), + (("10P", 5.0, 26.0), np.array([0.3468, 0.1460, 19.7700])), + (("10P", 5.0, 28.0), np.array([0.3478, 0.1388, 19.7700])), + (("10P", 5.0, 30.0), np.array([0.3490, 0.1308, 19.7700])), + (("10P", 5.0, 32.0), np.array([0.3500, 0.1240, 19.7700])), + (("10P", 5.0, 34.0), np.array([0.3510, 0.1150, 19.7700])), + (("10P", 5.0, 36.0), np.array([0.3520, 0.1070, 19.7700])), + (("10P", 5.0, 38.0), np.array([0.3530, 0.1020, 19.7700])), + (("10P", 5.0, 40.0), np.array([0.3540, 0.0960, 19.7700])), + (("2.5RP", 5.0, 2.0), np.array([0.3199, 0.3019, 19.7700])), + (("2.5RP", 5.0, 4.0), np.array([0.3298, 0.2869, 19.7700])), + (("2.5RP", 5.0, 6.0), np.array([0.3396, 0.2718, 19.7700])), + (("2.5RP", 5.0, 8.0), np.array([0.3490, 0.2570, 19.7700])), + (("2.5RP", 5.0, 10.0), np.array([0.3560, 0.2452, 19.7700])), + (("2.5RP", 5.0, 12.0), np.array([0.3635, 0.2325, 19.7700])), + (("2.5RP", 5.0, 14.0), np.array([0.3703, 0.2211, 19.7700])), + (("2.5RP", 5.0, 16.0), np.array([0.3763, 0.2108, 19.7700])), + (("2.5RP", 5.0, 18.0), np.array([0.3821, 0.2007, 19.7700])), + (("2.5RP", 5.0, 20.0), np.array([0.3873, 0.1909, 19.7700])), + (("2.5RP", 5.0, 22.0), np.array([0.3924, 0.1814, 19.7700])), + (("2.5RP", 5.0, 24.0), np.array([0.3965, 0.1738, 19.7700])), + (("2.5RP", 5.0, 26.0), np.array([0.4011, 0.1652, 19.7700])), + (("2.5RP", 5.0, 28.0), np.array([0.4050, 0.1570, 19.7700])), + (("2.5RP", 5.0, 30.0), np.array([0.4090, 0.1490, 19.7700])), + (("2.5RP", 5.0, 32.0), np.array([0.4130, 0.1420, 19.7700])), + (("2.5RP", 5.0, 34.0), np.array([0.4160, 0.1350, 19.7700])), + (("2.5RP", 5.0, 36.0), np.array([0.4210, 0.1260, 19.7700])), + (("5RP", 5.0, 2.0), np.array([0.3256, 0.3065, 19.7700])), + (("5RP", 5.0, 4.0), np.array([0.3421, 0.2954, 19.7700])), + (("5RP", 5.0, 6.0), np.array([0.3585, 0.2842, 19.7700])), + (("5RP", 5.0, 8.0), np.array([0.3748, 0.2729, 19.7700])), + (("5RP", 5.0, 10.0), np.array([0.3880, 0.2630, 19.7700])), + (("5RP", 5.0, 12.0), np.array([0.4022, 0.2523, 19.7700])), + (("5RP", 5.0, 14.0), np.array([0.4142, 0.2428, 19.7700])), + (("5RP", 5.0, 16.0), np.array([0.4261, 0.2331, 19.7700])), + (("5RP", 5.0, 18.0), np.array([0.4372, 0.2242, 19.7700])), + (("5RP", 5.0, 20.0), np.array([0.4484, 0.2150, 19.7700])), + (("5RP", 5.0, 22.0), np.array([0.4581, 0.2068, 19.7700])), + (("5RP", 5.0, 24.0), np.array([0.4683, 0.1978, 19.7700])), + (("5RP", 5.0, 26.0), np.array([0.4760, 0.1900, 19.7700])), + (("5RP", 5.0, 28.0), np.array([0.4830, 0.1830, 19.7700])), + (("5RP", 5.0, 30.0), np.array([0.4910, 0.1760, 19.7700])), + (("5RP", 5.0, 32.0), np.array([0.4980, 0.1690, 19.7700])), + (("7.5RP", 5.0, 2.0), np.array([0.3296, 0.3098, 19.7700])), + (("7.5RP", 5.0, 4.0), np.array([0.3515, 0.3024, 19.7700])), + (("7.5RP", 5.0, 6.0), np.array([0.3726, 0.2941, 19.7700])), + (("7.5RP", 5.0, 8.0), np.array([0.3932, 0.2852, 19.7700])), + (("7.5RP", 5.0, 10.0), np.array([0.4108, 0.2773, 19.7700])), + (("7.5RP", 5.0, 12.0), np.array([0.4303, 0.2675, 19.7700])), + (("7.5RP", 5.0, 14.0), np.array([0.4454, 0.2596, 19.7700])), + (("7.5RP", 5.0, 16.0), np.array([0.4617, 0.2506, 19.7700])), + (("7.5RP", 5.0, 18.0), np.array([0.4761, 0.2421, 19.7700])), + (("7.5RP", 5.0, 20.0), np.array([0.4915, 0.2330, 19.7700])), + (("7.5RP", 5.0, 22.0), np.array([0.5045, 0.2248, 19.7700])), + (("7.5RP", 5.0, 24.0), np.array([0.5170, 0.2170, 19.7700])), + (("7.5RP", 5.0, 26.0), np.array([0.5260, 0.2100, 19.7700])), + (("7.5RP", 5.0, 28.0), np.array([0.5360, 0.2040, 19.7700])), + (("7.5RP", 5.0, 30.0), np.array([0.5460, 0.1970, 19.7700])), + (("10RP", 5.0, 2.0), np.array([0.3332, 0.3131, 19.7700])), + (("10RP", 5.0, 4.0), np.array([0.3594, 0.3090, 19.7700])), + (("10RP", 5.0, 6.0), np.array([0.3851, 0.3039, 19.7700])), + (("10RP", 5.0, 8.0), np.array([0.4105, 0.2980, 19.7700])), + (("10RP", 5.0, 10.0), np.array([0.4332, 0.2918, 19.7700])), + (("10RP", 5.0, 12.0), np.array([0.4579, 0.2841, 19.7700])), + (("10RP", 5.0, 14.0), np.array([0.4767, 0.2776, 19.7700])), + (("10RP", 5.0, 16.0), np.array([0.4986, 0.2695, 19.7700])), + (("10RP", 5.0, 18.0), np.array([0.5185, 0.2620, 19.7700])), + (("10RP", 5.0, 20.0), np.array([0.5396, 0.2535, 19.7700])), + (("10RP", 5.0, 22.0), np.array([0.5540, 0.2460, 19.7700])), + (("10RP", 5.0, 24.0), np.array([0.5700, 0.2400, 19.7700])), + (("10RP", 5.0, 26.0), np.array([0.5820, 0.2340, 19.7700])), + (("10RP", 5.0, 28.0), np.array([0.5930, 0.2290, 19.7700])), + (("2.5R", 5.0, 2.0), np.array([0.3360, 0.3158, 19.7700])), + (("2.5R", 5.0, 4.0), np.array([0.3660, 0.3148, 19.7700])), + (("2.5R", 5.0, 6.0), np.array([0.3960, 0.3130, 19.7700])), + (("2.5R", 5.0, 8.0), np.array([0.4252, 0.3101, 19.7700])), + (("2.5R", 5.0, 10.0), np.array([0.4533, 0.3058, 19.7700])), + (("2.5R", 5.0, 12.0), np.array([0.4820, 0.3002, 19.7700])), + (("2.5R", 5.0, 14.0), np.array([0.5047, 0.2950, 19.7700])), + (("2.5R", 5.0, 16.0), np.array([0.5300, 0.2880, 19.7700])), + (("2.5R", 5.0, 18.0), np.array([0.5540, 0.2804, 19.7700])), + (("2.5R", 5.0, 20.0), np.array([0.5784, 0.2719, 19.7700])), + (("2.5R", 5.0, 22.0), np.array([0.5930, 0.2650, 19.7700])), + (("2.5R", 5.0, 24.0), np.array([0.6070, 0.2590, 19.7700])), + (("2.5R", 5.0, 26.0), np.array([0.6200, 0.2530, 19.7700])), + (("2.5R", 5.0, 28.0), np.array([0.6310, 0.2480, 19.7700])), + (("5R", 5.0, 2.0), np.array([0.3392, 0.3192, 19.7700])), + (("5R", 5.0, 4.0), np.array([0.3740, 0.3220, 19.7700])), + (("5R", 5.0, 6.0), np.array([0.4078, 0.3238, 19.7700])), + (("5R", 5.0, 8.0), np.array([0.4413, 0.3240, 19.7700])), + (("5R", 5.0, 10.0), np.array([0.4747, 0.3227, 19.7700])), + (("5R", 5.0, 12.0), np.array([0.5071, 0.3194, 19.7700])), + (("5R", 5.0, 14.0), np.array([0.5341, 0.3158, 19.7700])), + (("5R", 5.0, 16.0), np.array([0.5637, 0.3102, 19.7700])), + (("5R", 5.0, 18.0), np.array([0.5918, 0.3038, 19.7700])), + (("5R", 5.0, 20.0), np.array([0.6142, 0.2970, 19.7700])), + (("5R", 5.0, 22.0), np.array([0.6340, 0.2900, 19.7700])), + (("5R", 5.0, 24.0), np.array([0.6480, 0.2840, 19.7700])), + (("5R", 5.0, 26.0), np.array([0.6620, 0.2790, 19.7700])), + (("5R", 5.0, 28.0), np.array([0.6740, 0.2740, 19.7700])), + (("7.5R", 5.0, 2.0), np.array([0.3425, 0.3229, 19.7700])), + (("7.5R", 5.0, 4.0), np.array([0.3806, 0.3294, 19.7700])), + (("7.5R", 5.0, 6.0), np.array([0.4180, 0.3348, 19.7700])), + (("7.5R", 5.0, 8.0), np.array([0.4563, 0.3387, 19.7700])), + (("7.5R", 5.0, 10.0), np.array([0.4927, 0.3399, 19.7700])), + (("7.5R", 5.0, 12.0), np.array([0.5280, 0.3389, 19.7700])), + (("7.5R", 5.0, 14.0), np.array([0.5590, 0.3370, 19.7700])), + (("7.5R", 5.0, 16.0), np.array([0.5901, 0.3331, 19.7700])), + (("7.5R", 5.0, 18.0), np.array([0.6161, 0.3277, 19.7700])), + (("7.5R", 5.0, 20.0), np.array([0.6388, 0.3216, 19.7700])), + (("7.5R", 5.0, 22.0), np.array([0.6610, 0.3170, 19.7700])), + (("7.5R", 5.0, 24.0), np.array([0.6800, 0.3120, 19.7700])), + (("7.5R", 5.0, 26.0), np.array([0.6920, 0.3070, 19.7700])), + (("7.5R", 5.0, 28.0), np.array([0.7080, 0.3020, 19.7700])), + (("10R", 5.0, 2.0), np.array([0.3465, 0.3278, 19.7700])), + (("10R", 5.0, 4.0), np.array([0.3879, 0.3398, 19.7700])), + (("10R", 5.0, 6.0), np.array([0.4299, 0.3499, 19.7700])), + (("10R", 5.0, 8.0), np.array([0.4713, 0.3575, 19.7700])), + (("10R", 5.0, 10.0), np.array([0.5113, 0.3630, 19.7700])), + (("10R", 5.0, 12.0), np.array([0.5481, 0.3660, 19.7700])), + (("10R", 5.0, 14.0), np.array([0.5771, 0.3664, 19.7700])), + (("10R", 5.0, 16.0), np.array([0.6037, 0.3657, 19.7700])), + (("10R", 5.0, 18.0), np.array([0.6297, 0.3642, 19.7700])), + (("10R", 5.0, 20.0), np.array([0.6550, 0.3610, 19.7700])), + (("10R", 5.0, 22.0), np.array([0.6800, 0.3570, 19.7700])), + (("10R", 5.0, 24.0), np.array([0.7020, 0.3540, 19.7700])), + (("10R", 5.0, 26.0), np.array([0.7210, 0.3500, 19.7700])), + (("2.5YR", 5.0, 2.0), np.array([0.3506, 0.3337, 19.7700])), + (("2.5YR", 5.0, 4.0), np.array([0.3925, 0.3494, 19.7700])), + (("2.5YR", 5.0, 6.0), np.array([0.4365, 0.3640, 19.7700])), + (("2.5YR", 5.0, 8.0), np.array([0.4795, 0.3758, 19.7700])), + (("2.5YR", 5.0, 10.0), np.array([0.5175, 0.3844, 19.7700])), + (("2.5YR", 5.0, 12.0), np.array([0.5482, 0.3909, 19.7700])), + (("2.5YR", 5.0, 14.0), np.array([0.5731, 0.3953, 19.7700])), + (("2.5YR", 5.0, 16.0), np.array([0.5933, 0.3989, 19.7700])), + (("2.5YR", 5.0, 18.0), np.array([0.6170, 0.4020, 19.7700])), + (("2.5YR", 5.0, 20.0), np.array([0.6360, 0.4060, 19.7700])), + (("5YR", 5.0, 2.0), np.array([0.3530, 0.3395, 19.7700])), + (("5YR", 5.0, 4.0), np.array([0.3968, 0.3614, 19.7700])), + (("5YR", 5.0, 6.0), np.array([0.4420, 0.3808, 19.7700])), + (("5YR", 5.0, 8.0), np.array([0.4830, 0.3960, 19.7700])), + (("5YR", 5.0, 10.0), np.array([0.5161, 0.4064, 19.7700])), + (("5YR", 5.0, 12.0), np.array([0.5422, 0.4141, 19.7700])), + (("5YR", 5.0, 14.0), np.array([0.5642, 0.4201, 19.7700])), + (("5YR", 5.0, 16.0), np.array([0.5840, 0.4250, 19.7700])), + (("5YR", 5.0, 18.0), np.array([0.6010, 0.4290, 19.7700])), + (("7.5YR", 5.0, 2.0), np.array([0.3540, 0.3445, 19.7700])), + (("7.5YR", 5.0, 4.0), np.array([0.3991, 0.3714, 19.7700])), + (("7.5YR", 5.0, 6.0), np.array([0.4440, 0.3954, 19.7700])), + (("7.5YR", 5.0, 8.0), np.array([0.4820, 0.4141, 19.7700])), + (("7.5YR", 5.0, 10.0), np.array([0.5108, 0.4276, 19.7700])), + (("7.5YR", 5.0, 12.0), np.array([0.5335, 0.4378, 19.7700])), + (("7.5YR", 5.0, 14.0), np.array([0.5506, 0.4450, 19.7700])), + (("7.5YR", 5.0, 16.0), np.array([0.5680, 0.4530, 19.7700])), + (("10YR", 5.0, 2.0), np.array([0.3546, 0.3514, 19.7700])), + (("10YR", 5.0, 4.0), np.array([0.3995, 0.3840, 19.7700])), + (("10YR", 5.0, 6.0), np.array([0.4428, 0.4128, 19.7700])), + (("10YR", 5.0, 8.0), np.array([0.4770, 0.4338, 19.7700])), + (("10YR", 5.0, 10.0), np.array([0.5025, 0.4489, 19.7700])), + (("10YR", 5.0, 12.0), np.array([0.5211, 0.4600, 19.7700])), + (("10YR", 5.0, 14.0), np.array([0.5390, 0.4680, 19.7700])), + (("10YR", 5.0, 16.0), np.array([0.5520, 0.4770, 19.7700])), + (("2.5Y", 5.0, 2.0), np.array([0.3534, 0.3570, 19.7700])), + (("2.5Y", 5.0, 4.0), np.array([0.3968, 0.3954, 19.7700])), + (("2.5Y", 5.0, 6.0), np.array([0.4380, 0.4292, 19.7700])), + (("2.5Y", 5.0, 8.0), np.array([0.4685, 0.4524, 19.7700])), + (("2.5Y", 5.0, 10.0), np.array([0.4905, 0.4683, 19.7700])), + (("2.5Y", 5.0, 12.0), np.array([0.5082, 0.4812, 19.7700])), + (("2.5Y", 5.0, 14.0), np.array([0.5230, 0.4910, 19.7700])), + (("5Y", 5.0, 2.0), np.array([0.3500, 0.3620, 19.7700])), + (("5Y", 5.0, 4.0), np.array([0.3915, 0.4057, 19.7700])), + (("5Y", 5.0, 6.0), np.array([0.4302, 0.4435, 19.7700])), + (("5Y", 5.0, 8.0), np.array([0.4579, 0.4692, 19.7700])), + (("5Y", 5.0, 10.0), np.array([0.4777, 0.4876, 19.7700])), + (("5Y", 5.0, 12.0), np.array([0.4932, 0.5019, 19.7700])), + (("5Y", 5.0, 14.0), np.array([0.5070, 0.5120, 19.7700])), + (("7.5Y", 5.0, 2.0), np.array([0.3470, 0.3640, 19.7700])), + (("7.5Y", 5.0, 4.0), np.array([0.3850, 0.4120, 19.7700])), + (("7.5Y", 5.0, 6.0), np.array([0.4199, 0.4551, 19.7700])), + (("7.5Y", 5.0, 8.0), np.array([0.4450, 0.4850, 19.7700])), + (("7.5Y", 5.0, 10.0), np.array([0.4632, 0.5057, 19.7700])), + (("7.5Y", 5.0, 12.0), np.array([0.4767, 0.5208, 19.7700])), + (("7.5Y", 5.0, 14.0), np.array([0.4890, 0.5350, 19.7700])), + (("10Y", 6.0, 2.0), np.array([0.3398, 0.3611, 30.0300])), + (("10Y", 6.0, 4.0), np.array([0.3679, 0.4033, 30.0300])), + (("10Y", 6.0, 6.0), np.array([0.3960, 0.4452, 30.0300])), + (("10Y", 6.0, 8.0), np.array([0.4201, 0.4812, 30.0300])), + (("10Y", 6.0, 10.0), np.array([0.4372, 0.5068, 30.0300])), + (("10Y", 6.0, 12.0), np.array([0.4488, 0.5237, 30.0300])), + (("10Y", 6.0, 14.0), np.array([0.4593, 0.5392, 30.0300])), + (("10Y", 6.0, 16.0), np.array([0.4650, 0.5460, 30.0300])), + (("2.5GY", 6.0, 2.0), np.array([0.3342, 0.3607, 30.0300])), + (("2.5GY", 6.0, 4.0), np.array([0.3572, 0.4038, 30.0300])), + (("2.5GY", 6.0, 6.0), np.array([0.3799, 0.4470, 30.0300])), + (("2.5GY", 6.0, 8.0), np.array([0.4006, 0.4885, 30.0300])), + (("2.5GY", 6.0, 10.0), np.array([0.4159, 0.5190, 30.0300])), + (("2.5GY", 6.0, 12.0), np.array([0.4269, 0.5414, 30.0300])), + (("2.5GY", 6.0, 14.0), np.array([0.4354, 0.5594, 30.0300])), + (("2.5GY", 6.0, 16.0), np.array([0.4390, 0.5680, 30.0300])), + (("5GY", 6.0, 2.0), np.array([0.3288, 0.3592, 30.0300])), + (("5GY", 6.0, 4.0), np.array([0.3461, 0.4008, 30.0300])), + (("5GY", 6.0, 6.0), np.array([0.3622, 0.4438, 30.0300])), + (("5GY", 6.0, 8.0), np.array([0.3772, 0.4880, 30.0300])), + (("5GY", 6.0, 10.0), np.array([0.3891, 0.5264, 30.0300])), + (("5GY", 6.0, 12.0), np.array([0.3980, 0.5564, 30.0300])), + (("5GY", 6.0, 14.0), np.array([0.4042, 0.5788, 30.0300])), + (("5GY", 6.0, 16.0), np.array([0.4090, 0.5940, 30.0300])), + (("5GY", 6.0, 18.0), np.array([0.4130, 0.6070, 30.0300])), + (("7.5GY", 6.0, 2.0), np.array([0.3193, 0.3550, 30.0300])), + (("7.5GY", 6.0, 4.0), np.array([0.3275, 0.3922, 30.0300])), + (("7.5GY", 6.0, 6.0), np.array([0.3351, 0.4321, 30.0300])), + (("7.5GY", 6.0, 8.0), np.array([0.3418, 0.4768, 30.0300])), + (("7.5GY", 6.0, 10.0), np.array([0.3463, 0.5196, 30.0300])), + (("7.5GY", 6.0, 12.0), np.array([0.3488, 0.5596, 30.0300])), + (("7.5GY", 6.0, 14.0), np.array([0.3498, 0.5985, 30.0300])), + (("7.5GY", 6.0, 16.0), np.array([0.3498, 0.6282, 30.0300])), + (("7.5GY", 6.0, 18.0), np.array([0.3490, 0.6500, 30.0300])), + (("7.5GY", 6.0, 20.0), np.array([0.3480, 0.6670, 30.0300])), + (("7.5GY", 6.0, 22.0), np.array([0.3470, 0.6880, 30.0300])), + (("10GY", 6.0, 2.0), np.array([0.3112, 0.3496, 30.0300])), + (("10GY", 6.0, 4.0), np.array([0.3124, 0.3822, 30.0300])), + (("10GY", 6.0, 6.0), np.array([0.3128, 0.4175, 30.0300])), + (("10GY", 6.0, 8.0), np.array([0.3116, 0.4563, 30.0300])), + (("10GY", 6.0, 10.0), np.array([0.3086, 0.4949, 30.0300])), + (("10GY", 6.0, 12.0), np.array([0.3037, 0.5358, 30.0300])), + (("10GY", 6.0, 14.0), np.array([0.2962, 0.5802, 30.0300])), + (("10GY", 6.0, 16.0), np.array([0.2872, 0.6199, 30.0300])), + (("10GY", 6.0, 18.0), np.array([0.2763, 0.6616, 30.0300])), + (("10GY", 6.0, 20.0), np.array([0.2648, 0.7004, 30.0300])), + (("10GY", 6.0, 22.0), np.array([0.2530, 0.7380, 30.0300])), + (("10GY", 6.0, 24.0), np.array([0.2380, 0.7820, 30.0300])), + (("10GY", 6.0, 26.0), np.array([0.2250, 0.8200, 30.0300])), + (("10GY", 6.0, 28.0), np.array([0.2080, 0.8630, 30.0300])), + (("10GY", 6.0, 30.0), np.array([0.1880, 0.9050, 30.0300])), + (("10GY", 6.0, 32.0), np.array([0.1710, 0.9390, 30.0300])), + (("2.5G", 6.0, 2.0), np.array([0.3039, 0.3437, 30.0300])), + (("2.5G", 6.0, 4.0), np.array([0.2967, 0.3695, 30.0300])), + (("2.5G", 6.0, 6.0), np.array([0.2892, 0.3963, 30.0300])), + (("2.5G", 6.0, 8.0), np.array([0.2799, 0.4239, 30.0300])), + (("2.5G", 6.0, 10.0), np.array([0.2690, 0.4530, 30.0300])), + (("2.5G", 6.0, 12.0), np.array([0.2574, 0.4814, 30.0300])), + (("2.5G", 6.0, 14.0), np.array([0.2426, 0.5133, 30.0300])), + (("2.5G", 6.0, 16.0), np.array([0.2278, 0.5430, 30.0300])), + (("2.5G", 6.0, 18.0), np.array([0.2102, 0.5737, 30.0300])), + (("2.5G", 6.0, 20.0), np.array([0.1922, 0.6035, 30.0300])), + (("2.5G", 6.0, 22.0), np.array([0.1739, 0.6318, 30.0300])), + (("2.5G", 6.0, 24.0), np.array([0.1536, 0.6605, 30.0300])), + (("2.5G", 6.0, 26.0), np.array([0.1340, 0.6871, 30.0300])), + (("2.5G", 6.0, 28.0), np.array([0.1145, 0.7122, 30.0300])), + (("2.5G", 6.0, 30.0), np.array([0.0920, 0.7380, 30.0300])), + (("2.5G", 6.0, 32.0), np.array([0.0690, 0.7640, 30.0300])), + (("2.5G", 6.0, 34.0), np.array([0.0470, 0.7890, 30.0300])), + (("5G", 6.0, 2.0), np.array([0.2988, 0.3382, 30.0300])), + (("5G", 6.0, 4.0), np.array([0.2868, 0.3595, 30.0300])), + (("5G", 6.0, 6.0), np.array([0.2748, 0.3795, 30.0300])), + (("5G", 6.0, 8.0), np.array([0.2612, 0.3990, 30.0300])), + (("5G", 6.0, 10.0), np.array([0.2466, 0.4181, 30.0300])), + (("5G", 6.0, 12.0), np.array([0.2293, 0.4390, 30.0300])), + (("5G", 6.0, 14.0), np.array([0.2130, 0.4571, 30.0300])), + (("5G", 6.0, 16.0), np.array([0.1960, 0.4751, 30.0300])), + (("5G", 6.0, 18.0), np.array([0.1785, 0.4924, 30.0300])), + (("5G", 6.0, 20.0), np.array([0.1609, 0.5091, 30.0300])), + (("5G", 6.0, 22.0), np.array([0.1432, 0.5252, 30.0300])), + (("5G", 6.0, 24.0), np.array([0.1252, 0.5408, 30.0300])), + (("5G", 6.0, 26.0), np.array([0.1079, 0.5560, 30.0300])), + (("5G", 6.0, 28.0), np.array([0.0908, 0.5695, 30.0300])), + (("5G", 6.0, 30.0), np.array([0.0710, 0.5860, 30.0300])), + (("5G", 6.0, 32.0), np.array([0.0530, 0.5990, 30.0300])), + (("5G", 6.0, 34.0), np.array([0.0310, 0.6140, 30.0300])), + (("7.5G", 6.0, 2.0), np.array([0.2958, 0.3344, 30.0300])), + (("7.5G", 6.0, 4.0), np.array([0.2807, 0.3522, 30.0300])), + (("7.5G", 6.0, 6.0), np.array([0.2662, 0.3672, 30.0300])), + (("7.5G", 6.0, 8.0), np.array([0.2510, 0.3829, 30.0300])), + (("7.5G", 6.0, 10.0), np.array([0.2350, 0.3979, 30.0300])), + (("7.5G", 6.0, 12.0), np.array([0.2171, 0.4138, 30.0300])), + (("7.5G", 6.0, 14.0), np.array([0.2001, 0.4278, 30.0300])), + (("7.5G", 6.0, 16.0), np.array([0.1832, 0.4414, 30.0300])), + (("7.5G", 6.0, 18.0), np.array([0.1654, 0.4551, 30.0300])), + (("7.5G", 6.0, 20.0), np.array([0.1485, 0.4677, 30.0300])), + (("7.5G", 6.0, 22.0), np.array([0.1325, 0.4795, 30.0300])), + (("7.5G", 6.0, 24.0), np.array([0.1159, 0.4910, 30.0300])), + (("7.5G", 6.0, 26.0), np.array([0.1010, 0.5018, 30.0300])), + (("7.5G", 6.0, 28.0), np.array([0.0858, 0.5127, 30.0300])), + (("7.5G", 6.0, 30.0), np.array([0.0680, 0.5240, 30.0300])), + (("7.5G", 6.0, 32.0), np.array([0.0520, 0.5350, 30.0300])), + (("7.5G", 6.0, 34.0), np.array([0.0340, 0.5460, 30.0300])), + (("10G", 6.0, 2.0), np.array([0.2929, 0.3303, 30.0300])), + (("10G", 6.0, 4.0), np.array([0.2749, 0.3443, 30.0300])), + (("10G", 6.0, 6.0), np.array([0.2591, 0.3558, 30.0300])), + (("10G", 6.0, 8.0), np.array([0.2420, 0.3679, 30.0300])), + (("10G", 6.0, 10.0), np.array([0.2247, 0.3796, 30.0300])), + (("10G", 6.0, 12.0), np.array([0.2060, 0.3914, 30.0300])), + (("10G", 6.0, 14.0), np.array([0.1895, 0.4015, 30.0300])), + (("10G", 6.0, 16.0), np.array([0.1722, 0.4113, 30.0300])), + (("10G", 6.0, 18.0), np.array([0.1551, 0.4208, 30.0300])), + (("10G", 6.0, 20.0), np.array([0.1382, 0.4299, 30.0300])), + (("10G", 6.0, 22.0), np.array([0.1230, 0.4378, 30.0300])), + (("10G", 6.0, 24.0), np.array([0.1070, 0.4458, 30.0300])), + (("10G", 6.0, 26.0), np.array([0.0941, 0.4520, 30.0300])), + (("10G", 6.0, 28.0), np.array([0.0810, 0.4580, 30.0300])), + (("10G", 6.0, 30.0), np.array([0.0660, 0.4650, 30.0300])), + (("10G", 6.0, 32.0), np.array([0.0520, 0.4710, 30.0300])), + (("10G", 6.0, 34.0), np.array([0.0350, 0.4800, 30.0300])), + (("2.5BG", 6.0, 2.0), np.array([0.2902, 0.3268, 30.0300])), + (("2.5BG", 6.0, 4.0), np.array([0.2702, 0.3369, 30.0300])), + (("2.5BG", 6.0, 6.0), np.array([0.2526, 0.3448, 30.0300])), + (("2.5BG", 6.0, 8.0), np.array([0.2332, 0.3522, 30.0300])), + (("2.5BG", 6.0, 10.0), np.array([0.2148, 0.3584, 30.0300])), + (("2.5BG", 6.0, 12.0), np.array([0.1954, 0.3645, 30.0300])), + (("2.5BG", 6.0, 14.0), np.array([0.1779, 0.3699, 30.0300])), + (("2.5BG", 6.0, 16.0), np.array([0.1600, 0.3748, 30.0300])), + (("2.5BG", 6.0, 18.0), np.array([0.1428, 0.3790, 30.0300])), + (("2.5BG", 6.0, 20.0), np.array([0.1269, 0.3829, 30.0300])), + (("2.5BG", 6.0, 22.0), np.array([0.1120, 0.3860, 30.0300])), + (("2.5BG", 6.0, 24.0), np.array([0.0960, 0.3890, 30.0300])), + (("2.5BG", 6.0, 26.0), np.array([0.0830, 0.3920, 30.0300])), + (("2.5BG", 6.0, 28.0), np.array([0.0740, 0.3940, 30.0300])), + (("2.5BG", 6.0, 30.0), np.array([0.0620, 0.3970, 30.0300])), + (("2.5BG", 6.0, 32.0), np.array([0.0500, 0.3980, 30.0300])), + (("5BG", 6.0, 2.0), np.array([0.2872, 0.3219, 30.0300])), + (("5BG", 6.0, 4.0), np.array([0.2648, 0.3262, 30.0300])), + (("5BG", 6.0, 6.0), np.array([0.2441, 0.3290, 30.0300])), + (("5BG", 6.0, 8.0), np.array([0.2236, 0.3311, 30.0300])), + (("5BG", 6.0, 10.0), np.array([0.2037, 0.3329, 30.0300])), + (("5BG", 6.0, 12.0), np.array([0.1844, 0.3337, 30.0300])), + (("5BG", 6.0, 14.0), np.array([0.1662, 0.3343, 30.0300])), + (("5BG", 6.0, 16.0), np.array([0.1491, 0.3345, 30.0300])), + (("5BG", 6.0, 18.0), np.array([0.1325, 0.3345, 30.0300])), + (("5BG", 6.0, 20.0), np.array([0.1168, 0.3344, 30.0300])), + (("5BG", 6.0, 22.0), np.array([0.1010, 0.3320, 30.0300])), + (("5BG", 6.0, 24.0), np.array([0.0860, 0.3320, 30.0300])), + (("5BG", 6.0, 26.0), np.array([0.0750, 0.3310, 30.0300])), + (("5BG", 6.0, 28.0), np.array([0.0680, 0.3300, 30.0300])), + (("5BG", 6.0, 30.0), np.array([0.0600, 0.3300, 30.0300])), + (("7.5BG", 6.0, 2.0), np.array([0.2849, 0.3172, 30.0300])), + (("7.5BG", 6.0, 4.0), np.array([0.2604, 0.3169, 30.0300])), + (("7.5BG", 6.0, 6.0), np.array([0.2384, 0.3155, 30.0300])), + (("7.5BG", 6.0, 8.0), np.array([0.2171, 0.3138, 30.0300])), + (("7.5BG", 6.0, 10.0), np.array([0.1961, 0.3110, 30.0300])), + (("7.5BG", 6.0, 12.0), np.array([0.1762, 0.3081, 30.0300])), + (("7.5BG", 6.0, 14.0), np.array([0.1585, 0.3052, 30.0300])), + (("7.5BG", 6.0, 16.0), np.array([0.1408, 0.3017, 30.0300])), + (("7.5BG", 6.0, 18.0), np.array([0.1248, 0.2981, 30.0300])), + (("7.5BG", 6.0, 20.0), np.array([0.1080, 0.2940, 30.0300])), + (("7.5BG", 6.0, 22.0), np.array([0.0920, 0.2900, 30.0300])), + (("7.5BG", 6.0, 24.0), np.array([0.0780, 0.2870, 30.0300])), + (("7.5BG", 6.0, 26.0), np.array([0.0680, 0.2830, 30.0300])), + (("10BG", 6.0, 2.0), np.array([0.2837, 0.3132, 30.0300])), + (("10BG", 6.0, 4.0), np.array([0.2578, 0.3078, 30.0300])), + (("10BG", 6.0, 6.0), np.array([0.2335, 0.3015, 30.0300])), + (("10BG", 6.0, 8.0), np.array([0.2116, 0.2950, 30.0300])), + (("10BG", 6.0, 10.0), np.array([0.1909, 0.2881, 30.0300])), + (("10BG", 6.0, 12.0), np.array([0.1698, 0.2802, 30.0300])), + (("10BG", 6.0, 14.0), np.array([0.1518, 0.2729, 30.0300])), + (("10BG", 6.0, 16.0), np.array([0.1337, 0.2651, 30.0300])), + (("10BG", 6.0, 18.0), np.array([0.1181, 0.2581, 30.0300])), + (("10BG", 6.0, 20.0), np.array([0.1010, 0.2510, 30.0300])), + (("10BG", 6.0, 22.0), np.array([0.0860, 0.2440, 30.0300])), + (("10BG", 6.0, 24.0), np.array([0.0720, 0.2370, 30.0300])), + (("2.5B", 6.0, 2.0), np.array([0.2835, 0.3097, 30.0300])), + (("2.5B", 6.0, 4.0), np.array([0.2571, 0.3008, 30.0300])), + (("2.5B", 6.0, 6.0), np.array([0.2312, 0.2899, 30.0300])), + (("2.5B", 6.0, 8.0), np.array([0.2080, 0.2789, 30.0300])), + (("2.5B", 6.0, 10.0), np.array([0.1879, 0.2682, 30.0300])), + (("2.5B", 6.0, 12.0), np.array([0.1660, 0.2561, 30.0300])), + (("2.5B", 6.0, 14.0), np.array([0.1480, 0.2459, 30.0300])), + (("2.5B", 6.0, 16.0), np.array([0.1294, 0.2348, 30.0300])), + (("2.5B", 6.0, 18.0), np.array([0.1140, 0.2260, 30.0300])), + (("2.5B", 6.0, 20.0), np.array([0.0990, 0.2160, 30.0300])), + (("2.5B", 6.0, 22.0), np.array([0.0850, 0.2050, 30.0300])), + (("5B", 6.0, 2.0), np.array([0.2842, 0.3063, 30.0300])), + (("5B", 6.0, 4.0), np.array([0.2579, 0.2938, 30.0300])), + (("5B", 6.0, 6.0), np.array([0.2320, 0.2789, 30.0300])), + (("5B", 6.0, 8.0), np.array([0.2088, 0.2635, 30.0300])), + (("5B", 6.0, 10.0), np.array([0.1883, 0.2487, 30.0300])), + (("5B", 6.0, 12.0), np.array([0.1685, 0.2339, 30.0300])), + (("5B", 6.0, 14.0), np.array([0.1496, 0.2193, 30.0300])), + (("5B", 6.0, 16.0), np.array([0.1310, 0.2048, 30.0300])), + (("5B", 6.0, 18.0), np.array([0.1170, 0.1920, 30.0300])), + (("5B", 6.0, 20.0), np.array([0.1020, 0.1810, 30.0300])), + (("7.5B", 6.0, 2.0), np.array([0.2854, 0.3037, 30.0300])), + (("7.5B", 6.0, 4.0), np.array([0.2602, 0.2881, 30.0300])), + (("7.5B", 6.0, 6.0), np.array([0.2352, 0.2708, 30.0300])), + (("7.5B", 6.0, 8.0), np.array([0.2132, 0.2537, 30.0300])), + (("7.5B", 6.0, 10.0), np.array([0.1934, 0.2374, 30.0300])), + (("7.5B", 6.0, 12.0), np.array([0.1734, 0.2203, 30.0300])), + (("7.5B", 6.0, 14.0), np.array([0.1556, 0.2043, 30.0300])), + (("7.5B", 6.0, 16.0), np.array([0.1376, 0.1879, 30.0300])), + (("7.5B", 6.0, 18.0), np.array([0.1230, 0.1740, 30.0300])), + (("7.5B", 6.0, 20.0), np.array([0.1110, 0.1620, 30.0300])), + (("7.5B", 6.0, 22.0), np.array([0.1000, 0.1510, 30.0300])), + (("10B", 6.0, 2.0), np.array([0.2871, 0.3012, 30.0300])), + (("10B", 6.0, 4.0), np.array([0.2637, 0.2840, 30.0300])), + (("10B", 6.0, 6.0), np.array([0.2399, 0.2650, 30.0300])), + (("10B", 6.0, 8.0), np.array([0.2189, 0.2468, 30.0300])), + (("10B", 6.0, 10.0), np.array([0.2000, 0.2298, 30.0300])), + (("10B", 6.0, 12.0), np.array([0.1803, 0.2114, 30.0300])), + (("10B", 6.0, 14.0), np.array([0.1629, 0.1947, 30.0300])), + (("10B", 6.0, 16.0), np.array([0.1454, 0.1778, 30.0300])), + (("10B", 6.0, 18.0), np.array([0.1310, 0.1640, 30.0300])), + (("10B", 6.0, 20.0), np.array([0.1200, 0.1530, 30.0300])), + (("10B", 6.0, 22.0), np.array([0.1110, 0.1440, 30.0300])), + (("10B", 6.0, 24.0), np.array([0.0990, 0.1330, 30.0300])), + (("2.5PB", 6.0, 2.0), np.array([0.2897, 0.2991, 30.0300])), + (("2.5PB", 6.0, 4.0), np.array([0.2684, 0.2804, 30.0300])), + (("2.5PB", 6.0, 6.0), np.array([0.2465, 0.2599, 30.0300])), + (("2.5PB", 6.0, 8.0), np.array([0.2274, 0.2406, 30.0300])), + (("2.5PB", 6.0, 10.0), np.array([0.2095, 0.2225, 30.0300])), + (("2.5PB", 6.0, 12.0), np.array([0.1913, 0.2038, 30.0300])), + (("2.5PB", 6.0, 14.0), np.array([0.1754, 0.1868, 30.0300])), + (("2.5PB", 6.0, 16.0), np.array([0.1590, 0.1690, 30.0300])), + (("2.5PB", 6.0, 18.0), np.array([0.1480, 0.1560, 30.0300])), + (("2.5PB", 6.0, 20.0), np.array([0.1370, 0.1440, 30.0300])), + (("2.5PB", 6.0, 22.0), np.array([0.1270, 0.1340, 30.0300])), + (("2.5PB", 6.0, 24.0), np.array([0.1170, 0.1230, 30.0300])), + (("5PB", 6.0, 2.0), np.array([0.2923, 0.2978, 30.0300])), + (("5PB", 6.0, 4.0), np.array([0.2734, 0.2778, 30.0300])), + (("5PB", 6.0, 6.0), np.array([0.2533, 0.2558, 30.0300])), + (("5PB", 6.0, 8.0), np.array([0.2360, 0.2365, 30.0300])), + (("5PB", 6.0, 10.0), np.array([0.2197, 0.2188, 30.0300])), + (("5PB", 6.0, 12.0), np.array([0.2026, 0.1999, 30.0300])), + (("5PB", 6.0, 14.0), np.array([0.1873, 0.1822, 30.0300])), + (("5PB", 6.0, 16.0), np.array([0.1710, 0.1640, 30.0300])), + (("5PB", 6.0, 18.0), np.array([0.1620, 0.1530, 30.0300])), + (("5PB", 6.0, 20.0), np.array([0.1510, 0.1400, 30.0300])), + (("5PB", 6.0, 22.0), np.array([0.1430, 0.1290, 30.0300])), + (("5PB", 6.0, 24.0), np.array([0.1350, 0.1180, 30.0300])), + (("5PB", 6.0, 26.0), np.array([0.1260, 0.1070, 30.0300])), + (("7.5PB", 6.0, 2.0), np.array([0.2955, 0.2963, 30.0300])), + (("7.5PB", 6.0, 4.0), np.array([0.2798, 0.2752, 30.0300])), + (("7.5PB", 6.0, 6.0), np.array([0.2638, 0.2531, 30.0300])), + (("7.5PB", 6.0, 8.0), np.array([0.2505, 0.2347, 30.0300])), + (("7.5PB", 6.0, 10.0), np.array([0.2378, 0.2168, 30.0300])), + (("7.5PB", 6.0, 12.0), np.array([0.2241, 0.1975, 30.0300])), + (("7.5PB", 6.0, 14.0), np.array([0.2119, 0.1799, 30.0300])), + (("7.5PB", 6.0, 16.0), np.array([0.1990, 0.1630, 30.0300])), + (("7.5PB", 6.0, 18.0), np.array([0.1920, 0.1510, 30.0300])), + (("7.5PB", 6.0, 20.0), np.array([0.1850, 0.1390, 30.0300])), + (("7.5PB", 6.0, 22.0), np.array([0.1790, 0.1280, 30.0300])), + (("7.5PB", 6.0, 24.0), np.array([0.1730, 0.1180, 30.0300])), + (("7.5PB", 6.0, 26.0), np.array([0.1660, 0.1050, 30.0300])), + (("10PB", 6.0, 2.0), np.array([0.2988, 0.2961, 30.0300])), + (("10PB", 6.0, 4.0), np.array([0.2863, 0.2747, 30.0300])), + (("10PB", 6.0, 6.0), np.array([0.2740, 0.2533, 30.0300])), + (("10PB", 6.0, 8.0), np.array([0.2637, 0.2352, 30.0300])), + (("10PB", 6.0, 10.0), np.array([0.2540, 0.2176, 30.0300])), + (("10PB", 6.0, 12.0), np.array([0.2440, 0.1998, 30.0300])), + (("10PB", 6.0, 14.0), np.array([0.2352, 0.1839, 30.0300])), + (("10PB", 6.0, 16.0), np.array([0.2265, 0.1671, 30.0300])), + (("10PB", 6.0, 18.0), np.array([0.2210, 0.1560, 30.0300])), + (("10PB", 6.0, 20.0), np.array([0.2140, 0.1430, 30.0300])), + (("10PB", 6.0, 22.0), np.array([0.2080, 0.1320, 30.0300])), + (("10PB", 6.0, 24.0), np.array([0.2040, 0.1230, 30.0300])), + (("10PB", 6.0, 26.0), np.array([0.1990, 0.1110, 30.0300])), + (("10PB", 6.0, 28.0), np.array([0.1940, 0.1010, 30.0300])), + (("2.5P", 6.0, 2.0), np.array([0.3016, 0.2960, 30.0300])), + (("2.5P", 6.0, 4.0), np.array([0.2932, 0.2759, 30.0300])), + (("2.5P", 6.0, 6.0), np.array([0.2842, 0.2550, 30.0300])), + (("2.5P", 6.0, 8.0), np.array([0.2770, 0.2372, 30.0300])), + (("2.5P", 6.0, 10.0), np.array([0.2703, 0.2204, 30.0300])), + (("2.5P", 6.0, 12.0), np.array([0.2647, 0.2052, 30.0300])), + (("2.5P", 6.0, 14.0), np.array([0.2593, 0.1909, 30.0300])), + (("2.5P", 6.0, 16.0), np.array([0.2548, 0.1768, 30.0300])), + (("2.5P", 6.0, 18.0), np.array([0.2504, 0.1658, 30.0300])), + (("2.5P", 6.0, 20.0), np.array([0.2460, 0.1530, 30.0300])), + (("2.5P", 6.0, 22.0), np.array([0.2420, 0.1420, 30.0300])), + (("2.5P", 6.0, 24.0), np.array([0.2390, 0.1320, 30.0300])), + (("2.5P", 6.0, 26.0), np.array([0.2350, 0.1210, 30.0300])), + (("2.5P", 6.0, 28.0), np.array([0.2310, 0.1120, 30.0300])), + (("2.5P", 6.0, 30.0), np.array([0.2280, 0.1020, 30.0300])), + (("2.5P", 6.0, 32.0), np.array([0.2250, 0.0930, 30.0300])), + (("5P", 6.0, 2.0), np.array([0.3050, 0.2967, 30.0300])), + (("5P", 6.0, 4.0), np.array([0.3001, 0.2778, 30.0300])), + (("5P", 6.0, 6.0), np.array([0.2950, 0.2585, 30.0300])), + (("5P", 6.0, 8.0), np.array([0.2905, 0.2421, 30.0300])), + (("5P", 6.0, 10.0), np.array([0.2862, 0.2260, 30.0300])), + (("5P", 6.0, 12.0), np.array([0.2829, 0.2121, 30.0300])), + (("5P", 6.0, 14.0), np.array([0.2794, 0.1979, 30.0300])), + (("5P", 6.0, 16.0), np.array([0.2761, 0.1852, 30.0300])), + (("5P", 6.0, 18.0), np.array([0.2731, 0.1738, 30.0300])), + (("5P", 6.0, 20.0), np.array([0.2702, 0.1621, 30.0300])), + (("5P", 6.0, 22.0), np.array([0.2670, 0.1490, 30.0300])), + (("5P", 6.0, 24.0), np.array([0.2650, 0.1400, 30.0300])), + (("5P", 6.0, 26.0), np.array([0.2630, 0.1300, 30.0300])), + (("5P", 6.0, 28.0), np.array([0.2600, 0.1200, 30.0300])), + (("5P", 6.0, 30.0), np.array([0.2580, 0.1110, 30.0300])), + (("5P", 6.0, 32.0), np.array([0.2560, 0.1030, 30.0300])), + (("5P", 6.0, 34.0), np.array([0.2540, 0.0950, 30.0300])), + (("5P", 6.0, 36.0), np.array([0.2520, 0.0890, 30.0300])), + (("7.5P", 6.0, 2.0), np.array([0.3107, 0.2993, 30.0300])), + (("7.5P", 6.0, 4.0), np.array([0.3107, 0.2831, 30.0300])), + (("7.5P", 6.0, 6.0), np.array([0.3101, 0.2650, 30.0300])), + (("7.5P", 6.0, 8.0), np.array([0.3099, 0.2502, 30.0300])), + (("7.5P", 6.0, 10.0), np.array([0.3092, 0.2350, 30.0300])), + (("7.5P", 6.0, 12.0), np.array([0.3090, 0.2222, 30.0300])), + (("7.5P", 6.0, 14.0), np.array([0.3084, 0.2095, 30.0300])), + (("7.5P", 6.0, 16.0), np.array([0.3080, 0.1976, 30.0300])), + (("7.5P", 6.0, 18.0), np.array([0.3075, 0.1870, 30.0300])), + (("7.5P", 6.0, 20.0), np.array([0.3069, 0.1745, 30.0300])), + (("7.5P", 6.0, 22.0), np.array([0.3062, 0.1638, 30.0300])), + (("7.5P", 6.0, 24.0), np.array([0.3058, 0.1547, 30.0300])), + (("7.5P", 6.0, 26.0), np.array([0.3050, 0.1440, 30.0300])), + (("7.5P", 6.0, 28.0), np.array([0.3040, 0.1360, 30.0300])), + (("7.5P", 6.0, 30.0), np.array([0.3030, 0.1260, 30.0300])), + (("7.5P", 6.0, 32.0), np.array([0.3020, 0.1180, 30.0300])), + (("7.5P", 6.0, 34.0), np.array([0.3010, 0.1110, 30.0300])), + (("7.5P", 6.0, 36.0), np.array([0.3010, 0.1050, 30.0300])), + (("10P", 6.0, 2.0), np.array([0.3146, 0.3018, 30.0300])), + (("10P", 6.0, 4.0), np.array([0.3181, 0.2871, 30.0300])), + (("10P", 6.0, 6.0), np.array([0.3226, 0.2716, 30.0300])), + (("10P", 6.0, 8.0), np.array([0.3259, 0.2584, 30.0300])), + (("10P", 6.0, 10.0), np.array([0.3293, 0.2450, 30.0300])), + (("10P", 6.0, 12.0), np.array([0.3321, 0.2329, 30.0300])), + (("10P", 6.0, 14.0), np.array([0.3349, 0.2203, 30.0300])), + (("10P", 6.0, 16.0), np.array([0.3370, 0.2095, 30.0300])), + (("10P", 6.0, 18.0), np.array([0.3388, 0.1995, 30.0300])), + (("10P", 6.0, 20.0), np.array([0.3409, 0.1882, 30.0300])), + (("10P", 6.0, 22.0), np.array([0.3426, 0.1785, 30.0300])), + (("10P", 6.0, 24.0), np.array([0.3441, 0.1698, 30.0300])), + (("10P", 6.0, 26.0), np.array([0.3457, 0.1604, 30.0300])), + (("10P", 6.0, 28.0), np.array([0.3470, 0.1510, 30.0300])), + (("10P", 6.0, 30.0), np.array([0.3490, 0.1440, 30.0300])), + (("10P", 6.0, 32.0), np.array([0.3500, 0.1360, 30.0300])), + (("10P", 6.0, 34.0), np.array([0.3510, 0.1290, 30.0300])), + (("10P", 6.0, 36.0), np.array([0.3520, 0.1230, 30.0300])), + (("2.5RP", 6.0, 2.0), np.array([0.3188, 0.3048, 30.0300])), + (("2.5RP", 6.0, 4.0), np.array([0.3272, 0.2929, 30.0300])), + (("2.5RP", 6.0, 6.0), np.array([0.3362, 0.2799, 30.0300])), + (("2.5RP", 6.0, 8.0), np.array([0.3437, 0.2688, 30.0300])), + (("2.5RP", 6.0, 10.0), np.array([0.3509, 0.2578, 30.0300])), + (("2.5RP", 6.0, 12.0), np.array([0.3582, 0.2462, 30.0300])), + (("2.5RP", 6.0, 14.0), np.array([0.3652, 0.2355, 30.0300])), + (("2.5RP", 6.0, 16.0), np.array([0.3718, 0.2251, 30.0300])), + (("2.5RP", 6.0, 18.0), np.array([0.3773, 0.2158, 30.0300])), + (("2.5RP", 6.0, 20.0), np.array([0.3833, 0.2056, 30.0300])), + (("2.5RP", 6.0, 22.0), np.array([0.3877, 0.1978, 30.0300])), + (("2.5RP", 6.0, 24.0), np.array([0.3927, 0.1892, 30.0300])), + (("2.5RP", 6.0, 26.0), np.array([0.3970, 0.1810, 30.0300])), + (("2.5RP", 6.0, 28.0), np.array([0.4010, 0.1740, 30.0300])), + (("2.5RP", 6.0, 30.0), np.array([0.4060, 0.1660, 30.0300])), + (("2.5RP", 6.0, 32.0), np.array([0.4100, 0.1590, 30.0300])), + (("2.5RP", 6.0, 34.0), np.array([0.4130, 0.1530, 30.0300])), + (("2.5RP", 6.0, 36.0), np.array([0.4160, 0.1480, 30.0300])), + (("5RP", 6.0, 2.0), np.array([0.3232, 0.3085, 30.0300])), + (("5RP", 6.0, 4.0), np.array([0.3371, 0.3001, 30.0300])), + (("5RP", 6.0, 6.0), np.array([0.3520, 0.2904, 30.0300])), + (("5RP", 6.0, 8.0), np.array([0.3648, 0.2820, 30.0300])), + (("5RP", 6.0, 10.0), np.array([0.3769, 0.2738, 30.0300])), + (("5RP", 6.0, 12.0), np.array([0.3900, 0.2646, 30.0300])), + (("5RP", 6.0, 14.0), np.array([0.4023, 0.2552, 30.0300])), + (("5RP", 6.0, 16.0), np.array([0.4136, 0.2467, 30.0300])), + (("5RP", 6.0, 18.0), np.array([0.4245, 0.2382, 30.0300])), + (("5RP", 6.0, 20.0), np.array([0.4368, 0.2283, 30.0300])), + (("5RP", 6.0, 22.0), np.array([0.4449, 0.2219, 30.0300])), + (("5RP", 6.0, 24.0), np.array([0.4520, 0.2150, 30.0300])), + (("5RP", 6.0, 26.0), np.array([0.4620, 0.2070, 30.0300])), + (("5RP", 6.0, 28.0), np.array([0.4680, 0.2010, 30.0300])), + (("5RP", 6.0, 30.0), np.array([0.4740, 0.1960, 30.0300])), + (("5RP", 6.0, 32.0), np.array([0.4810, 0.1890, 30.0300])), + (("7.5RP", 6.0, 2.0), np.array([0.3261, 0.3113, 30.0300])), + (("7.5RP", 6.0, 4.0), np.array([0.3439, 0.3056, 30.0300])), + (("7.5RP", 6.0, 6.0), np.array([0.3635, 0.2987, 30.0300])), + (("7.5RP", 6.0, 8.0), np.array([0.3791, 0.2929, 30.0300])), + (("7.5RP", 6.0, 10.0), np.array([0.3960, 0.2860, 30.0300])), + (("7.5RP", 6.0, 12.0), np.array([0.4125, 0.2784, 30.0300])), + (("7.5RP", 6.0, 14.0), np.array([0.4285, 0.2705, 30.0300])), + (("7.5RP", 6.0, 16.0), np.array([0.4448, 0.2622, 30.0300])), + (("7.5RP", 6.0, 18.0), np.array([0.4581, 0.2549, 30.0300])), + (("7.5RP", 6.0, 20.0), np.array([0.4735, 0.2464, 30.0300])), + (("7.5RP", 6.0, 22.0), np.array([0.4860, 0.2400, 30.0300])), + (("7.5RP", 6.0, 24.0), np.array([0.4960, 0.2330, 30.0300])), + (("7.5RP", 6.0, 26.0), np.array([0.5050, 0.2270, 30.0300])), + (("7.5RP", 6.0, 28.0), np.array([0.5130, 0.2210, 30.0300])), + (("7.5RP", 6.0, 30.0), np.array([0.5190, 0.2160, 30.0300])), + (("10RP", 6.0, 2.0), np.array([0.3292, 0.3141, 30.0300])), + (("10RP", 6.0, 4.0), np.array([0.3508, 0.3112, 30.0300])), + (("10RP", 6.0, 6.0), np.array([0.3740, 0.3074, 30.0300])), + (("10RP", 6.0, 8.0), np.array([0.3930, 0.3038, 30.0300])), + (("10RP", 6.0, 10.0), np.array([0.4150, 0.2989, 30.0300])), + (("10RP", 6.0, 12.0), np.array([0.4360, 0.2936, 30.0300])), + (("10RP", 6.0, 14.0), np.array([0.4552, 0.2881, 30.0300])), + (("10RP", 6.0, 16.0), np.array([0.4781, 0.2812, 30.0300])), + (("10RP", 6.0, 18.0), np.array([0.4961, 0.2751, 30.0300])), + (("10RP", 6.0, 20.0), np.array([0.5130, 0.2680, 30.0300])), + (("10RP", 6.0, 22.0), np.array([0.5270, 0.2620, 30.0300])), + (("10RP", 6.0, 24.0), np.array([0.5410, 0.2560, 30.0300])), + (("10RP", 6.0, 26.0), np.array([0.5520, 0.2500, 30.0300])), + (("10RP", 6.0, 28.0), np.array([0.5630, 0.2460, 30.0300])), + (("2.5R", 6.0, 2.0), np.array([0.3318, 0.3166, 30.0300])), + (("2.5R", 6.0, 4.0), np.array([0.3566, 0.3163, 30.0300])), + (("2.5R", 6.0, 6.0), np.array([0.3832, 0.3158, 30.0300])), + (("2.5R", 6.0, 8.0), np.array([0.4065, 0.3144, 30.0300])), + (("2.5R", 6.0, 10.0), np.array([0.4320, 0.3118, 30.0300])), + (("2.5R", 6.0, 12.0), np.array([0.4568, 0.3082, 30.0300])), + (("2.5R", 6.0, 14.0), np.array([0.4790, 0.3041, 30.0300])), + (("2.5R", 6.0, 16.0), np.array([0.5041, 0.2983, 30.0300])), + (("2.5R", 6.0, 18.0), np.array([0.5262, 0.2928, 30.0300])), + (("2.5R", 6.0, 20.0), np.array([0.5450, 0.2880, 30.0300])), + (("2.5R", 6.0, 22.0), np.array([0.5610, 0.2820, 30.0300])), + (("2.5R", 6.0, 24.0), np.array([0.5740, 0.2760, 30.0300])), + (("2.5R", 6.0, 26.0), np.array([0.5880, 0.2710, 30.0300])), + (("2.5R", 6.0, 28.0), np.array([0.5970, 0.2650, 30.0300])), + (("5R", 6.0, 2.0), np.array([0.3343, 0.3190, 30.0300])), + (("5R", 6.0, 4.0), np.array([0.3628, 0.3221, 30.0300])), + (("5R", 6.0, 6.0), np.array([0.3921, 0.3244, 30.0300])), + (("5R", 6.0, 8.0), np.array([0.4187, 0.3251, 30.0300])), + (("5R", 6.0, 10.0), np.array([0.4480, 0.3250, 30.0300])), + (("5R", 6.0, 12.0), np.array([0.4760, 0.3234, 30.0300])), + (("5R", 6.0, 14.0), np.array([0.5020, 0.3212, 30.0300])), + (("5R", 6.0, 16.0), np.array([0.5297, 0.3179, 30.0300])), + (("5R", 6.0, 18.0), np.array([0.5552, 0.3138, 30.0300])), + (("5R", 6.0, 20.0), np.array([0.5750, 0.3090, 30.0300])), + (("5R", 6.0, 22.0), np.array([0.5930, 0.3040, 30.0300])), + (("5R", 6.0, 24.0), np.array([0.6070, 0.2990, 30.0300])), + (("5R", 6.0, 26.0), np.array([0.6180, 0.2950, 30.0300])), + (("5R", 6.0, 28.0), np.array([0.6320, 0.2890, 30.0300])), + (("5R", 6.0, 30.0), np.array([0.6430, 0.2830, 30.0300])), + (("7.5R", 6.0, 2.0), np.array([0.3381, 0.3228, 30.0300])), + (("7.5R", 6.0, 4.0), np.array([0.3692, 0.3291, 30.0300])), + (("7.5R", 6.0, 6.0), np.array([0.4000, 0.3340, 30.0300])), + (("7.5R", 6.0, 8.0), np.array([0.4318, 0.3383, 30.0300])), + (("7.5R", 6.0, 10.0), np.array([0.4655, 0.3412, 30.0300])), + (("7.5R", 6.0, 12.0), np.array([0.4961, 0.3428, 30.0300])), + (("7.5R", 6.0, 14.0), np.array([0.5265, 0.3431, 30.0300])), + (("7.5R", 6.0, 16.0), np.array([0.5560, 0.3420, 30.0300])), + (("7.5R", 6.0, 18.0), np.array([0.5829, 0.3396, 30.0300])), + (("7.5R", 6.0, 20.0), np.array([0.6030, 0.3360, 30.0300])), + (("7.5R", 6.0, 22.0), np.array([0.6250, 0.3330, 30.0300])), + (("7.5R", 6.0, 24.0), np.array([0.6390, 0.3300, 30.0300])), + (("7.5R", 6.0, 26.0), np.array([0.6520, 0.3260, 30.0300])), + (("7.5R", 6.0, 28.0), np.array([0.6670, 0.3210, 30.0300])), + (("7.5R", 6.0, 30.0), np.array([0.6790, 0.3160, 30.0300])), + (("10R", 6.0, 2.0), np.array([0.3417, 0.3268, 30.0300])), + (("10R", 6.0, 4.0), np.array([0.3768, 0.3381, 30.0300])), + (("10R", 6.0, 6.0), np.array([0.4103, 0.3473, 30.0300])), + (("10R", 6.0, 8.0), np.array([0.4449, 0.3550, 30.0300])), + (("10R", 6.0, 10.0), np.array([0.4812, 0.3619, 30.0300])), + (("10R", 6.0, 12.0), np.array([0.5150, 0.3667, 30.0300])), + (("10R", 6.0, 14.0), np.array([0.5468, 0.3697, 30.0300])), + (("10R", 6.0, 16.0), np.array([0.5741, 0.3713, 30.0300])), + (("10R", 6.0, 18.0), np.array([0.6009, 0.3720, 30.0300])), + (("10R", 6.0, 20.0), np.array([0.6240, 0.3710, 30.0300])), + (("10R", 6.0, 22.0), np.array([0.6460, 0.3700, 30.0300])), + (("10R", 6.0, 24.0), np.array([0.6640, 0.3700, 30.0300])), + (("10R", 6.0, 26.0), np.array([0.6790, 0.3690, 30.0300])), + (("10R", 6.0, 28.0), np.array([0.6960, 0.3680, 30.0300])), + (("10R", 6.0, 30.0), np.array([0.7140, 0.3650, 30.0300])), + (("2.5YR", 6.0, 2.0), np.array([0.3453, 0.3321, 30.0300])), + (("2.5YR", 6.0, 4.0), np.array([0.3806, 0.3467, 30.0300])), + (("2.5YR", 6.0, 6.0), np.array([0.4180, 0.3600, 30.0300])), + (("2.5YR", 6.0, 8.0), np.array([0.4533, 0.3708, 30.0300])), + (("2.5YR", 6.0, 10.0), np.array([0.4891, 0.3806, 30.0300])), + (("2.5YR", 6.0, 12.0), np.array([0.5215, 0.3887, 30.0300])), + (("2.5YR", 6.0, 14.0), np.array([0.5488, 0.3947, 30.0300])), + (("2.5YR", 6.0, 16.0), np.array([0.5698, 0.3990, 30.0300])), + (("2.5YR", 6.0, 18.0), np.array([0.5879, 0.4021, 30.0300])), + (("2.5YR", 6.0, 20.0), np.array([0.6040, 0.4030, 30.0300])), + (("2.5YR", 6.0, 22.0), np.array([0.6140, 0.4060, 30.0300])), + (("5YR", 6.0, 2.0), np.array([0.3474, 0.3373, 30.0300])), + (("5YR", 6.0, 4.0), np.array([0.3840, 0.3564, 30.0300])), + (("5YR", 6.0, 6.0), np.array([0.4229, 0.3750, 30.0300])), + (("5YR", 6.0, 8.0), np.array([0.4592, 0.3900, 30.0300])), + (("5YR", 6.0, 10.0), np.array([0.4921, 0.4022, 30.0300])), + (("5YR", 6.0, 12.0), np.array([0.5199, 0.4119, 30.0300])), + (("5YR", 6.0, 14.0), np.array([0.5423, 0.4188, 30.0300])), + (("5YR", 6.0, 16.0), np.array([0.5597, 0.4239, 30.0300])), + (("5YR", 6.0, 18.0), np.array([0.5715, 0.4270, 30.0300])), + (("5YR", 6.0, 20.0), np.array([0.5850, 0.4310, 30.0300])), + (("7.5YR", 6.0, 2.0), np.array([0.3487, 0.3421, 30.0300])), + (("7.5YR", 6.0, 4.0), np.array([0.3860, 0.3652, 30.0300])), + (("7.5YR", 6.0, 6.0), np.array([0.4242, 0.3876, 30.0300])), + (("7.5YR", 6.0, 8.0), np.array([0.4596, 0.4064, 30.0300])), + (("7.5YR", 6.0, 10.0), np.array([0.4904, 0.4220, 30.0300])), + (("7.5YR", 6.0, 12.0), np.array([0.5145, 0.4331, 30.0300])), + (("7.5YR", 6.0, 14.0), np.array([0.5320, 0.4412, 30.0300])), + (("7.5YR", 6.0, 16.0), np.array([0.5468, 0.4478, 30.0300])), + (("7.5YR", 6.0, 18.0), np.array([0.5550, 0.4520, 30.0300])), + (("7.5YR", 6.0, 20.0), np.array([0.5640, 0.4550, 30.0300])), + (("10YR", 6.0, 2.0), np.array([0.3491, 0.3483, 30.0300])), + (("10YR", 6.0, 4.0), np.array([0.3861, 0.3767, 30.0300])), + (("10YR", 6.0, 6.0), np.array([0.4240, 0.4030, 30.0300])), + (("10YR", 6.0, 8.0), np.array([0.4570, 0.4249, 30.0300])), + (("10YR", 6.0, 10.0), np.array([0.4843, 0.4416, 30.0300])), + (("10YR", 6.0, 12.0), np.array([0.5050, 0.4536, 30.0300])), + (("10YR", 6.0, 14.0), np.array([0.5200, 0.4623, 30.0300])), + (("10YR", 6.0, 16.0), np.array([0.5310, 0.4690, 30.0300])), + (("10YR", 6.0, 18.0), np.array([0.5390, 0.4720, 30.0300])), + (("2.5Y", 6.0, 2.0), np.array([0.3480, 0.3540, 30.0300])), + (("2.5Y", 6.0, 4.0), np.array([0.3840, 0.3867, 30.0300])), + (("2.5Y", 6.0, 6.0), np.array([0.4203, 0.4176, 30.0300])), + (("2.5Y", 6.0, 8.0), np.array([0.4517, 0.4421, 30.0300])), + (("2.5Y", 6.0, 10.0), np.array([0.4760, 0.4607, 30.0300])), + (("2.5Y", 6.0, 12.0), np.array([0.4928, 0.4730, 30.0300])), + (("2.5Y", 6.0, 14.0), np.array([0.5061, 0.4829, 30.0300])), + (("2.5Y", 6.0, 16.0), np.array([0.5160, 0.4890, 30.0300])), + (("2.5Y", 6.0, 18.0), np.array([0.5230, 0.4940, 30.0300])), + (("5Y", 6.0, 2.0), np.array([0.3457, 0.3580, 30.0300])), + (("5Y", 6.0, 4.0), np.array([0.3794, 0.3955, 30.0300])), + (("5Y", 6.0, 6.0), np.array([0.4140, 0.4305, 30.0300])), + (("5Y", 6.0, 8.0), np.array([0.4426, 0.4588, 30.0300])), + (("5Y", 6.0, 10.0), np.array([0.4639, 0.4790, 30.0300])), + (("5Y", 6.0, 12.0), np.array([0.4780, 0.4920, 30.0300])), + (("5Y", 6.0, 14.0), np.array([0.4905, 0.5038, 30.0300])), + (("5Y", 6.0, 16.0), np.array([0.4990, 0.5100, 30.0300])), + (("7.5Y", 6.0, 2.0), np.array([0.3431, 0.3601, 30.0300])), + (("7.5Y", 6.0, 4.0), np.array([0.3745, 0.4004, 30.0300])), + (("7.5Y", 6.0, 6.0), np.array([0.4060, 0.4400, 30.0300])), + (("7.5Y", 6.0, 8.0), np.array([0.4321, 0.4719, 30.0300])), + (("7.5Y", 6.0, 10.0), np.array([0.4512, 0.4943, 30.0300])), + (("7.5Y", 6.0, 12.0), np.array([0.4638, 0.5087, 30.0300])), + (("7.5Y", 6.0, 14.0), np.array([0.4754, 0.5220, 30.0300])), + (("7.5Y", 6.0, 16.0), np.array([0.4820, 0.5280, 30.0300])), + (("10Y", 7.0, 2.0), np.array([0.3369, 0.3569, 43.0600])), + (("10Y", 7.0, 4.0), np.array([0.3624, 0.3951, 43.0600])), + (("10Y", 7.0, 6.0), np.array([0.3864, 0.4305, 43.0600])), + (("10Y", 7.0, 8.0), np.array([0.4090, 0.4641, 43.0600])), + (("10Y", 7.0, 10.0), np.array([0.4289, 0.4937, 43.0600])), + (("10Y", 7.0, 12.0), np.array([0.4420, 0.5131, 43.0600])), + (("10Y", 7.0, 14.0), np.array([0.4516, 0.5277, 43.0600])), + (("10Y", 7.0, 16.0), np.array([0.4582, 0.5375, 43.0600])), + (("10Y", 7.0, 18.0), np.array([0.4620, 0.5430, 43.0600])), + (("2.5GY", 7.0, 2.0), np.array([0.3328, 0.3569, 43.0600])), + (("2.5GY", 7.0, 4.0), np.array([0.3534, 0.3953, 43.0600])), + (("2.5GY", 7.0, 6.0), np.array([0.3728, 0.4316, 43.0600])), + (("2.5GY", 7.0, 8.0), np.array([0.3919, 0.4684, 43.0600])), + (("2.5GY", 7.0, 10.0), np.array([0.4091, 0.5030, 43.0600])), + (("2.5GY", 7.0, 12.0), np.array([0.4213, 0.5270, 43.0600])), + (("2.5GY", 7.0, 14.0), np.array([0.4309, 0.5459, 43.0600])), + (("2.5GY", 7.0, 16.0), np.array([0.4366, 0.5578, 43.0600])), + (("2.5GY", 7.0, 18.0), np.array([0.4390, 0.5640, 43.0600])), + (("5GY", 7.0, 2.0), np.array([0.3284, 0.3559, 43.0600])), + (("5GY", 7.0, 4.0), np.array([0.3437, 0.3929, 43.0600])), + (("5GY", 7.0, 6.0), np.array([0.3581, 0.4291, 43.0600])), + (("5GY", 7.0, 8.0), np.array([0.3722, 0.4669, 43.0600])), + (("5GY", 7.0, 10.0), np.array([0.3852, 0.5051, 43.0600])), + (("5GY", 7.0, 12.0), np.array([0.3949, 0.5367, 43.0600])), + (("5GY", 7.0, 14.0), np.array([0.4027, 0.5615, 43.0600])), + (("5GY", 7.0, 16.0), np.array([0.4076, 0.5783, 43.0600])), + (("5GY", 7.0, 18.0), np.array([0.4100, 0.5890, 43.0600])), + (("5GY", 7.0, 20.0), np.array([0.4130, 0.6010, 43.0600])), + (("7.5GY", 7.0, 2.0), np.array([0.3190, 0.3516, 43.0600])), + (("7.5GY", 7.0, 4.0), np.array([0.3267, 0.3848, 43.0600])), + (("7.5GY", 7.0, 6.0), np.array([0.3341, 0.4191, 43.0600])), + (("7.5GY", 7.0, 8.0), np.array([0.3406, 0.4558, 43.0600])), + (("7.5GY", 7.0, 10.0), np.array([0.3461, 0.4950, 43.0600])), + (("7.5GY", 7.0, 12.0), np.array([0.3502, 0.5328, 43.0600])), + (("7.5GY", 7.0, 14.0), np.array([0.3532, 0.5700, 43.0600])), + (("7.5GY", 7.0, 16.0), np.array([0.3549, 0.6000, 43.0600])), + (("7.5GY", 7.0, 18.0), np.array([0.3555, 0.6242, 43.0600])), + (("7.5GY", 7.0, 20.0), np.array([0.3560, 0.6420, 43.0600])), + (("7.5GY", 7.0, 22.0), np.array([0.3560, 0.6610, 43.0600])), + (("7.5GY", 7.0, 24.0), np.array([0.3560, 0.6760, 43.0600])), + (("10GY", 7.0, 2.0), np.array([0.3117, 0.3469, 43.0600])), + (("10GY", 7.0, 4.0), np.array([0.3133, 0.3764, 43.0600])), + (("10GY", 7.0, 6.0), np.array([0.3142, 0.4058, 43.0600])), + (("10GY", 7.0, 8.0), np.array([0.3140, 0.4387, 43.0600])), + (("10GY", 7.0, 10.0), np.array([0.3123, 0.4732, 43.0600])), + (("10GY", 7.0, 12.0), np.array([0.3092, 0.5095, 43.0600])), + (("10GY", 7.0, 14.0), np.array([0.3047, 0.5458, 43.0600])), + (("10GY", 7.0, 16.0), np.array([0.2981, 0.5835, 43.0600])), + (("10GY", 7.0, 18.0), np.array([0.2905, 0.6186, 43.0600])), + (("10GY", 7.0, 20.0), np.array([0.2816, 0.6563, 43.0600])), + (("10GY", 7.0, 22.0), np.array([0.2728, 0.6893, 43.0600])), + (("10GY", 7.0, 24.0), np.array([0.2620, 0.7270, 43.0600])), + (("10GY", 7.0, 26.0), np.array([0.2520, 0.7580, 43.0600])), + (("10GY", 7.0, 28.0), np.array([0.2420, 0.7900, 43.0600])), + (("10GY", 7.0, 30.0), np.array([0.2330, 0.8190, 43.0600])), + (("10GY", 7.0, 32.0), np.array([0.2240, 0.8510, 43.0600])), + (("2.5G", 7.0, 2.0), np.array([0.3047, 0.3413, 43.0600])), + (("2.5G", 7.0, 4.0), np.array([0.2992, 0.3644, 43.0600])), + (("2.5G", 7.0, 6.0), np.array([0.2933, 0.3873, 43.0600])), + (("2.5G", 7.0, 8.0), np.array([0.2861, 0.4129, 43.0600])), + (("2.5G", 7.0, 10.0), np.array([0.2775, 0.4395, 43.0600])), + (("2.5G", 7.0, 12.0), np.array([0.2672, 0.4667, 43.0600])), + (("2.5G", 7.0, 14.0), np.array([0.2568, 0.4931, 43.0600])), + (("2.5G", 7.0, 16.0), np.array([0.2448, 0.5203, 43.0600])), + (("2.5G", 7.0, 18.0), np.array([0.2328, 0.5467, 43.0600])), + (("2.5G", 7.0, 20.0), np.array([0.2181, 0.5744, 43.0600])), + (("2.5G", 7.0, 22.0), np.array([0.2029, 0.6017, 43.0600])), + (("2.5G", 7.0, 24.0), np.array([0.1875, 0.6265, 43.0600])), + (("2.5G", 7.0, 26.0), np.array([0.1689, 0.6549, 43.0600])), + (("2.5G", 7.0, 28.0), np.array([0.1490, 0.6810, 43.0600])), + (("2.5G", 7.0, 30.0), np.array([0.1260, 0.7090, 43.0600])), + (("2.5G", 7.0, 32.0), np.array([0.1060, 0.7330, 43.0600])), + (("2.5G", 7.0, 34.0), np.array([0.0810, 0.7590, 43.0600])), + (("5G", 7.0, 2.0), np.array([0.3001, 0.3366, 43.0600])), + (("5G", 7.0, 4.0), np.array([0.2902, 0.3548, 43.0600])), + (("5G", 7.0, 6.0), np.array([0.2801, 0.3721, 43.0600])), + (("5G", 7.0, 8.0), np.array([0.2687, 0.3901, 43.0600])), + (("5G", 7.0, 10.0), np.array([0.2554, 0.4087, 43.0600])), + (("5G", 7.0, 12.0), np.array([0.2416, 0.4267, 43.0600])), + (("5G", 7.0, 14.0), np.array([0.2262, 0.4450, 43.0600])), + (("5G", 7.0, 16.0), np.array([0.2111, 0.4616, 43.0600])), + (("5G", 7.0, 18.0), np.array([0.1967, 0.4771, 43.0600])), + (("5G", 7.0, 20.0), np.array([0.1805, 0.4933, 43.0600])), + (("5G", 7.0, 22.0), np.array([0.1659, 0.5074, 43.0600])), + (("5G", 7.0, 24.0), np.array([0.1521, 0.5200, 43.0600])), + (("5G", 7.0, 26.0), np.array([0.1397, 0.5312, 43.0600])), + (("5G", 7.0, 28.0), np.array([0.1230, 0.5460, 43.0600])), + (("5G", 7.0, 30.0), np.array([0.1050, 0.5600, 43.0600])), + (("5G", 7.0, 32.0), np.array([0.0880, 0.5730, 43.0600])), + (("5G", 7.0, 34.0), np.array([0.0690, 0.5870, 43.0600])), + (("7.5G", 7.0, 2.0), np.array([0.2972, 0.3333, 43.0600])), + (("7.5G", 7.0, 4.0), np.array([0.2850, 0.3482, 43.0600])), + (("7.5G", 7.0, 6.0), np.array([0.2728, 0.3622, 43.0600])), + (("7.5G", 7.0, 8.0), np.array([0.2595, 0.3764, 43.0600])), + (("7.5G", 7.0, 10.0), np.array([0.2445, 0.3914, 43.0600])), + (("7.5G", 7.0, 12.0), np.array([0.2295, 0.4058, 43.0600])), + (("7.5G", 7.0, 14.0), np.array([0.2139, 0.4199, 43.0600])), + (("7.5G", 7.0, 16.0), np.array([0.1982, 0.4330, 43.0600])), + (("7.5G", 7.0, 18.0), np.array([0.1841, 0.4448, 43.0600])), + (("7.5G", 7.0, 20.0), np.array([0.1688, 0.4570, 43.0600])), + (("7.5G", 7.0, 22.0), np.array([0.1539, 0.4683, 43.0600])), + (("7.5G", 7.0, 24.0), np.array([0.1415, 0.4778, 43.0600])), + (("7.5G", 7.0, 26.0), np.array([0.1303, 0.4858, 43.0600])), + (("7.5G", 7.0, 28.0), np.array([0.1150, 0.4970, 43.0600])), + (("7.5G", 7.0, 30.0), np.array([0.0990, 0.5070, 43.0600])), + (("7.5G", 7.0, 32.0), np.array([0.0840, 0.5170, 43.0600])), + (("7.5G", 7.0, 34.0), np.array([0.0690, 0.5270, 43.0600])), + (("10G", 7.0, 2.0), np.array([0.2945, 0.3297, 43.0600])), + (("10G", 7.0, 4.0), np.array([0.2803, 0.3415, 43.0600])), + (("10G", 7.0, 6.0), np.array([0.2662, 0.3526, 43.0600])), + (("10G", 7.0, 8.0), np.array([0.2513, 0.3635, 43.0600])), + (("10G", 7.0, 10.0), np.array([0.2352, 0.3748, 43.0600])), + (("10G", 7.0, 12.0), np.array([0.2195, 0.3854, 43.0600])), + (("10G", 7.0, 14.0), np.array([0.2033, 0.3956, 43.0600])), + (("10G", 7.0, 16.0), np.array([0.1881, 0.4049, 43.0600])), + (("10G", 7.0, 18.0), np.array([0.1734, 0.4135, 43.0600])), + (("10G", 7.0, 20.0), np.array([0.1589, 0.4220, 43.0600])), + (("10G", 7.0, 22.0), np.array([0.1434, 0.4306, 43.0600])), + (("10G", 7.0, 24.0), np.array([0.1310, 0.4377, 43.0600])), + (("10G", 7.0, 26.0), np.array([0.1210, 0.4430, 43.0600])), + (("10G", 7.0, 28.0), np.array([0.1080, 0.4490, 43.0600])), + (("10G", 7.0, 30.0), np.array([0.0940, 0.4570, 43.0600])), + (("10G", 7.0, 32.0), np.array([0.0830, 0.4630, 43.0600])), + (("2.5BG", 7.0, 2.0), np.array([0.2927, 0.3269, 43.0600])), + (("2.5BG", 7.0, 4.0), np.array([0.2764, 0.3354, 43.0600])), + (("2.5BG", 7.0, 6.0), np.array([0.2608, 0.3430, 43.0600])), + (("2.5BG", 7.0, 8.0), np.array([0.2439, 0.3508, 43.0600])), + (("2.5BG", 7.0, 10.0), np.array([0.2264, 0.3576, 43.0600])), + (("2.5BG", 7.0, 12.0), np.array([0.2102, 0.3636, 43.0600])), + (("2.5BG", 7.0, 14.0), np.array([0.1932, 0.3694, 43.0600])), + (("2.5BG", 7.0, 16.0), np.array([0.1788, 0.3739, 43.0600])), + (("2.5BG", 7.0, 18.0), np.array([0.1626, 0.3788, 43.0600])), + (("2.5BG", 7.0, 20.0), np.array([0.1490, 0.3827, 43.0600])), + (("2.5BG", 7.0, 22.0), np.array([0.1334, 0.3870, 43.0600])), + (("2.5BG", 7.0, 24.0), np.array([0.1220, 0.3900, 43.0600])), + (("2.5BG", 7.0, 26.0), np.array([0.1110, 0.3920, 43.0600])), + (("2.5BG", 7.0, 28.0), np.array([0.0990, 0.3940, 43.0600])), + (("2.5BG", 7.0, 30.0), np.array([0.0880, 0.3970, 43.0600])), + (("2.5BG", 7.0, 32.0), np.array([0.0790, 0.3980, 43.0600])), + (("5BG", 7.0, 2.0), np.array([0.2898, 0.3225, 43.0600])), + (("5BG", 7.0, 4.0), np.array([0.2712, 0.3269, 43.0600])), + (("5BG", 7.0, 6.0), np.array([0.2543, 0.3302, 43.0600])), + (("5BG", 7.0, 8.0), np.array([0.2354, 0.3335, 43.0600])), + (("5BG", 7.0, 10.0), np.array([0.2163, 0.3361, 43.0600])), + (("5BG", 7.0, 12.0), np.array([0.1997, 0.3379, 43.0600])), + (("5BG", 7.0, 14.0), np.array([0.1838, 0.3390, 43.0600])), + (("5BG", 7.0, 16.0), np.array([0.1675, 0.3401, 43.0600])), + (("5BG", 7.0, 18.0), np.array([0.1515, 0.3410, 43.0600])), + (("5BG", 7.0, 20.0), np.array([0.1380, 0.3412, 43.0600])), + (("5BG", 7.0, 22.0), np.array([0.1220, 0.3400, 43.0600])), + (("5BG", 7.0, 24.0), np.array([0.1100, 0.3400, 43.0600])), + (("5BG", 7.0, 26.0), np.array([0.1010, 0.3400, 43.0600])), + (("5BG", 7.0, 28.0), np.array([0.0920, 0.3400, 43.0600])), + (("5BG", 7.0, 30.0), np.array([0.0830, 0.3390, 43.0600])), + (("7.5BG", 7.0, 2.0), np.array([0.2878, 0.3182, 43.0600])), + (("7.5BG", 7.0, 4.0), np.array([0.2671, 0.3189, 43.0600])), + (("7.5BG", 7.0, 6.0), np.array([0.2490, 0.3186, 43.0600])), + (("7.5BG", 7.0, 8.0), np.array([0.2292, 0.3178, 43.0600])), + (("7.5BG", 7.0, 10.0), np.array([0.2094, 0.3165, 43.0600])), + (("7.5BG", 7.0, 12.0), np.array([0.1914, 0.3148, 43.0600])), + (("7.5BG", 7.0, 14.0), np.array([0.1751, 0.3129, 43.0600])), + (("7.5BG", 7.0, 16.0), np.array([0.1584, 0.3101, 43.0600])), + (("7.5BG", 7.0, 18.0), np.array([0.1427, 0.3076, 43.0600])), + (("7.5BG", 7.0, 20.0), np.array([0.1300, 0.3050, 43.0600])), + (("7.5BG", 7.0, 22.0), np.array([0.1140, 0.3020, 43.0600])), + (("7.5BG", 7.0, 24.0), np.array([0.1020, 0.3000, 43.0600])), + (("7.5BG", 7.0, 26.0), np.array([0.0940, 0.2980, 43.0600])), + (("10BG", 7.0, 2.0), np.array([0.2869, 0.3143, 43.0600])), + (("10BG", 7.0, 4.0), np.array([0.2642, 0.3109, 43.0600])), + (("10BG", 7.0, 6.0), np.array([0.2448, 0.3069, 43.0600])), + (("10BG", 7.0, 8.0), np.array([0.2235, 0.3014, 43.0600])), + (("10BG", 7.0, 10.0), np.array([0.2035, 0.2956, 43.0600])), + (("10BG", 7.0, 12.0), np.array([0.1841, 0.2892, 43.0600])), + (("10BG", 7.0, 14.0), np.array([0.1671, 0.2832, 43.0600])), + (("10BG", 7.0, 16.0), np.array([0.1489, 0.2768, 43.0600])), + (("10BG", 7.0, 18.0), np.array([0.1340, 0.2710, 43.0600])), + (("10BG", 7.0, 20.0), np.array([0.1220, 0.2660, 43.0600])), + (("10BG", 7.0, 22.0), np.array([0.1080, 0.2600, 43.0600])), + (("10BG", 7.0, 24.0), np.array([0.0940, 0.2530, 43.0600])), + (("2.5B", 7.0, 2.0), np.array([0.2867, 0.3110, 43.0600])), + (("2.5B", 7.0, 4.0), np.array([0.2629, 0.3038, 43.0600])), + (("2.5B", 7.0, 6.0), np.array([0.2418, 0.2960, 43.0600])), + (("2.5B", 7.0, 8.0), np.array([0.2208, 0.2871, 43.0600])), + (("2.5B", 7.0, 10.0), np.array([0.1994, 0.2775, 43.0600])), + (("2.5B", 7.0, 12.0), np.array([0.1797, 0.2672, 43.0600])), + (("2.5B", 7.0, 14.0), np.array([0.1624, 0.2581, 43.0600])), + (("2.5B", 7.0, 16.0), np.array([0.1435, 0.2472, 43.0600])), + (("2.5B", 7.0, 18.0), np.array([0.1300, 0.2390, 43.0600])), + (("2.5B", 7.0, 20.0), np.array([0.1170, 0.2310, 43.0600])), + (("2.5B", 7.0, 22.0), np.array([0.1040, 0.2220, 43.0600])), + (("5B", 7.0, 2.0), np.array([0.2875, 0.3078, 43.0600])), + (("5B", 7.0, 4.0), np.array([0.2633, 0.2972, 43.0600])), + (("5B", 7.0, 6.0), np.array([0.2410, 0.2854, 43.0600])), + (("5B", 7.0, 8.0), np.array([0.2204, 0.2729, 43.0600])), + (("5B", 7.0, 10.0), np.array([0.1986, 0.2579, 43.0600])), + (("5B", 7.0, 12.0), np.array([0.1778, 0.2430, 43.0600])), + (("5B", 7.0, 14.0), np.array([0.1615, 0.2307, 43.0600])), + (("5B", 7.0, 16.0), np.array([0.1450, 0.2190, 43.0600])), + (("5B", 7.0, 18.0), np.array([0.1280, 0.2060, 43.0600])), + (("5B", 7.0, 20.0), np.array([0.1140, 0.1950, 43.0600])), + (("7.5B", 7.0, 2.0), np.array([0.2888, 0.3058, 43.0600])), + (("7.5B", 7.0, 4.0), np.array([0.2651, 0.2927, 43.0600])), + (("7.5B", 7.0, 6.0), np.array([0.2436, 0.2787, 43.0600])), + (("7.5B", 7.0, 8.0), np.array([0.2225, 0.2631, 43.0600])), + (("7.5B", 7.0, 10.0), np.array([0.2016, 0.2466, 43.0600])), + (("7.5B", 7.0, 12.0), np.array([0.1818, 0.2303, 43.0600])), + (("7.5B", 7.0, 14.0), np.array([0.1650, 0.2160, 43.0600])), + (("7.5B", 7.0, 16.0), np.array([0.1490, 0.2020, 43.0600])), + (("7.5B", 7.0, 18.0), np.array([0.1320, 0.1870, 43.0600])), + (("7.5B", 7.0, 20.0), np.array([0.1210, 0.1750, 43.0600])), + (("10B", 7.0, 2.0), np.array([0.2908, 0.3039, 43.0600])), + (("10B", 7.0, 4.0), np.array([0.2685, 0.2886, 43.0600])), + (("10B", 7.0, 6.0), np.array([0.2478, 0.2728, 43.0600])), + (("10B", 7.0, 8.0), np.array([0.2277, 0.2559, 43.0600])), + (("10B", 7.0, 10.0), np.array([0.2078, 0.2382, 43.0600])), + (("10B", 7.0, 12.0), np.array([0.1883, 0.2203, 43.0600])), + (("10B", 7.0, 14.0), np.array([0.1720, 0.2060, 43.0600])), + (("10B", 7.0, 16.0), np.array([0.1560, 0.1900, 43.0600])), + (("10B", 7.0, 18.0), np.array([0.1410, 0.1760, 43.0600])), + (("10B", 7.0, 20.0), np.array([0.1280, 0.1620, 43.0600])), + (("2.5PB", 7.0, 2.0), np.array([0.2932, 0.3025, 43.0600])), + (("2.5PB", 7.0, 4.0), np.array([0.2729, 0.2848, 43.0600])), + (("2.5PB", 7.0, 6.0), np.array([0.2538, 0.2677, 43.0600])), + (("2.5PB", 7.0, 8.0), np.array([0.2352, 0.2498, 43.0600])), + (("2.5PB", 7.0, 10.0), np.array([0.2162, 0.2309, 43.0600])), + (("2.5PB", 7.0, 12.0), np.array([0.1990, 0.2130, 43.0600])), + (("2.5PB", 7.0, 14.0), np.array([0.1830, 0.1950, 43.0600])), + (("2.5PB", 7.0, 16.0), np.array([0.1680, 0.1790, 43.0600])), + (("2.5PB", 7.0, 18.0), np.array([0.1560, 0.1650, 43.0600])), + (("5PB", 7.0, 2.0), np.array([0.2952, 0.3011, 43.0600])), + (("5PB", 7.0, 4.0), np.array([0.2773, 0.2828, 43.0600])), + (("5PB", 7.0, 6.0), np.array([0.2596, 0.2643, 43.0600])), + (("5PB", 7.0, 8.0), np.array([0.2427, 0.2458, 43.0600])), + (("5PB", 7.0, 10.0), np.array([0.2254, 0.2267, 43.0600])), + (("5PB", 7.0, 12.0), np.array([0.2080, 0.2080, 43.0600])), + (("5PB", 7.0, 14.0), np.array([0.1940, 0.1910, 43.0600])), + (("5PB", 7.0, 16.0), np.array([0.1810, 0.1750, 43.0600])), + (("5PB", 7.0, 18.0), np.array([0.1700, 0.1610, 43.0600])), + (("7.5PB", 7.0, 2.0), np.array([0.2982, 0.3003, 43.0600])), + (("7.5PB", 7.0, 4.0), np.array([0.2833, 0.2809, 43.0600])), + (("7.5PB", 7.0, 6.0), np.array([0.2687, 0.2612, 43.0600])), + (("7.5PB", 7.0, 8.0), np.array([0.2546, 0.2418, 43.0600])), + (("7.5PB", 7.0, 10.0), np.array([0.2410, 0.2224, 43.0600])), + (("7.5PB", 7.0, 12.0), np.array([0.2280, 0.2040, 43.0600])), + (("7.5PB", 7.0, 14.0), np.array([0.2170, 0.1890, 43.0600])), + (("7.5PB", 7.0, 16.0), np.array([0.2070, 0.1730, 43.0600])), + (("7.5PB", 7.0, 18.0), np.array([0.1980, 0.1600, 43.0600])), + (("7.5PB", 7.0, 20.0), np.array([0.1880, 0.1460, 43.0600])), + (("10PB", 7.0, 2.0), np.array([0.3005, 0.3000, 43.0600])), + (("10PB", 7.0, 4.0), np.array([0.2886, 0.2801, 43.0600])), + (("10PB", 7.0, 6.0), np.array([0.2776, 0.2612, 43.0600])), + (("10PB", 7.0, 8.0), np.array([0.2670, 0.2425, 43.0600])), + (("10PB", 7.0, 10.0), np.array([0.2563, 0.2240, 43.0600])), + (("10PB", 7.0, 12.0), np.array([0.2465, 0.2058, 43.0600])), + (("10PB", 7.0, 14.0), np.array([0.2390, 0.1910, 43.0600])), + (("10PB", 7.0, 16.0), np.array([0.2320, 0.1780, 43.0600])), + (("10PB", 7.0, 18.0), np.array([0.2250, 0.1660, 43.0600])), + (("10PB", 7.0, 20.0), np.array([0.2190, 0.1520, 43.0600])), + (("10PB", 7.0, 22.0), np.array([0.2120, 0.1400, 43.0600])), + (("2.5P", 7.0, 2.0), np.array([0.3031, 0.3000, 43.0600])), + (("2.5P", 7.0, 4.0), np.array([0.2950, 0.2810, 43.0600])), + (("2.5P", 7.0, 6.0), np.array([0.2873, 0.2633, 43.0600])), + (("2.5P", 7.0, 8.0), np.array([0.2799, 0.2459, 43.0600])), + (("2.5P", 7.0, 10.0), np.array([0.2729, 0.2289, 43.0600])), + (("2.5P", 7.0, 12.0), np.array([0.2664, 0.2127, 43.0600])), + (("2.5P", 7.0, 14.0), np.array([0.2610, 0.1980, 43.0600])), + (("2.5P", 7.0, 16.0), np.array([0.2560, 0.1850, 43.0600])), + (("2.5P", 7.0, 18.0), np.array([0.2520, 0.1760, 43.0600])), + (("2.5P", 7.0, 20.0), np.array([0.2480, 0.1620, 43.0600])), + (("2.5P", 7.0, 22.0), np.array([0.2440, 0.1500, 43.0600])), + (("2.5P", 7.0, 24.0), np.array([0.2400, 0.1400, 43.0600])), + (("5P", 7.0, 2.0), np.array([0.3059, 0.3010, 43.0600])), + (("5P", 7.0, 4.0), np.array([0.3009, 0.2831, 43.0600])), + (("5P", 7.0, 6.0), np.array([0.2961, 0.2663, 43.0600])), + (("5P", 7.0, 8.0), np.array([0.2918, 0.2504, 43.0600])), + (("5P", 7.0, 10.0), np.array([0.2872, 0.2343, 43.0600])), + (("5P", 7.0, 12.0), np.array([0.2833, 0.2197, 43.0600])), + (("5P", 7.0, 14.0), np.array([0.2801, 0.2068, 43.0600])), + (("5P", 7.0, 16.0), np.array([0.2770, 0.1940, 43.0600])), + (("5P", 7.0, 18.0), np.array([0.2740, 0.1820, 43.0600])), + (("5P", 7.0, 20.0), np.array([0.2710, 0.1700, 43.0600])), + (("5P", 7.0, 22.0), np.array([0.2680, 0.1580, 43.0600])), + (("5P", 7.0, 24.0), np.array([0.2650, 0.1480, 43.0600])), + (("5P", 7.0, 26.0), np.array([0.2620, 0.1370, 43.0600])), + (("5P", 7.0, 28.0), np.array([0.2600, 0.1280, 43.0600])), + (("7.5P", 7.0, 2.0), np.array([0.3109, 0.3037, 43.0600])), + (("7.5P", 7.0, 4.0), np.array([0.3111, 0.2880, 43.0600])), + (("7.5P", 7.0, 6.0), np.array([0.3111, 0.2730, 43.0600])), + (("7.5P", 7.0, 8.0), np.array([0.3109, 0.2584, 43.0600])), + (("7.5P", 7.0, 10.0), np.array([0.3108, 0.2442, 43.0600])), + (("7.5P", 7.0, 12.0), np.array([0.3104, 0.2320, 43.0600])), + (("7.5P", 7.0, 14.0), np.array([0.3101, 0.2192, 43.0600])), + (("7.5P", 7.0, 16.0), np.array([0.3099, 0.2074, 43.0600])), + (("7.5P", 7.0, 18.0), np.array([0.3093, 0.1962, 43.0600])), + (("7.5P", 7.0, 20.0), np.array([0.3080, 0.1850, 43.0600])), + (("7.5P", 7.0, 22.0), np.array([0.3080, 0.1740, 43.0600])), + (("7.5P", 7.0, 24.0), np.array([0.3070, 0.1640, 43.0600])), + (("7.5P", 7.0, 26.0), np.array([0.3060, 0.1540, 43.0600])), + (("7.5P", 7.0, 28.0), np.array([0.3050, 0.1450, 43.0600])), + (("7.5P", 7.0, 30.0), np.array([0.3040, 0.1360, 43.0600])), + (("10P", 7.0, 2.0), np.array([0.3138, 0.3054, 43.0600])), + (("10P", 7.0, 4.0), np.array([0.3181, 0.2920, 43.0600])), + (("10P", 7.0, 6.0), np.array([0.3221, 0.2786, 43.0600])), + (("10P", 7.0, 8.0), np.array([0.3256, 0.2654, 43.0600])), + (("10P", 7.0, 10.0), np.array([0.3288, 0.2531, 43.0600])), + (("10P", 7.0, 12.0), np.array([0.3314, 0.2423, 43.0600])), + (("10P", 7.0, 14.0), np.array([0.3341, 0.2308, 43.0600])), + (("10P", 7.0, 16.0), np.array([0.3368, 0.2192, 43.0600])), + (("10P", 7.0, 18.0), np.array([0.3391, 0.2088, 43.0600])), + (("10P", 7.0, 20.0), np.array([0.3410, 0.1988, 43.0600])), + (("10P", 7.0, 22.0), np.array([0.3430, 0.1883, 43.0600])), + (("10P", 7.0, 24.0), np.array([0.3440, 0.1790, 43.0600])), + (("10P", 7.0, 26.0), np.array([0.3440, 0.1700, 43.0600])), + (("10P", 7.0, 28.0), np.array([0.3450, 0.1620, 43.0600])), + (("10P", 7.0, 30.0), np.array([0.3460, 0.1540, 43.0600])), + (("2.5RP", 7.0, 2.0), np.array([0.3170, 0.3076, 43.0600])), + (("2.5RP", 7.0, 4.0), np.array([0.3254, 0.2971, 43.0600])), + (("2.5RP", 7.0, 6.0), np.array([0.3338, 0.2854, 43.0600])), + (("2.5RP", 7.0, 8.0), np.array([0.3417, 0.2745, 43.0600])), + (("2.5RP", 7.0, 10.0), np.array([0.3487, 0.2648, 43.0600])), + (("2.5RP", 7.0, 12.0), np.array([0.3555, 0.2545, 43.0600])), + (("2.5RP", 7.0, 14.0), np.array([0.3620, 0.2448, 43.0600])), + (("2.5RP", 7.0, 16.0), np.array([0.3688, 0.2342, 43.0600])), + (("2.5RP", 7.0, 18.0), np.array([0.3751, 0.2241, 43.0600])), + (("2.5RP", 7.0, 20.0), np.array([0.3811, 0.2143, 43.0600])), + (("2.5RP", 7.0, 22.0), np.array([0.3850, 0.2060, 43.0600])), + (("2.5RP", 7.0, 24.0), np.array([0.3890, 0.2000, 43.0600])), + (("2.5RP", 7.0, 26.0), np.array([0.3940, 0.1920, 43.0600])), + (("2.5RP", 7.0, 28.0), np.array([0.3980, 0.1840, 43.0600])), + (("2.5RP", 7.0, 30.0), np.array([0.4020, 0.1780, 43.0600])), + (("5RP", 7.0, 2.0), np.array([0.3206, 0.3104, 43.0600])), + (("5RP", 7.0, 4.0), np.array([0.3332, 0.3032, 43.0600])), + (("5RP", 7.0, 6.0), np.array([0.3470, 0.2949, 43.0600])), + (("5RP", 7.0, 8.0), np.array([0.3603, 0.2869, 43.0600])), + (("5RP", 7.0, 10.0), np.array([0.3713, 0.2798, 43.0600])), + (("5RP", 7.0, 12.0), np.array([0.3841, 0.2710, 43.0600])), + (("5RP", 7.0, 14.0), np.array([0.3958, 0.2628, 43.0600])), + (("5RP", 7.0, 16.0), np.array([0.4076, 0.2540, 43.0600])), + (("5RP", 7.0, 18.0), np.array([0.4186, 0.2459, 43.0600])), + (("5RP", 7.0, 20.0), np.array([0.4260, 0.2390, 43.0600])), + (("5RP", 7.0, 22.0), np.array([0.4360, 0.2320, 43.0600])), + (("5RP", 7.0, 24.0), np.array([0.4430, 0.2260, 43.0600])), + (("5RP", 7.0, 26.0), np.array([0.4520, 0.2170, 43.0600])), + (("5RP", 7.0, 28.0), np.array([0.4600, 0.2110, 43.0600])), + (("7.5RP", 7.0, 2.0), np.array([0.3232, 0.3125, 43.0600])), + (("7.5RP", 7.0, 4.0), np.array([0.3389, 0.3079, 43.0600])), + (("7.5RP", 7.0, 6.0), np.array([0.3562, 0.3022, 43.0600])), + (("7.5RP", 7.0, 8.0), np.array([0.3722, 0.2963, 43.0600])), + (("7.5RP", 7.0, 10.0), np.array([0.3871, 0.2906, 43.0600])), + (("7.5RP", 7.0, 12.0), np.array([0.4040, 0.2834, 43.0600])), + (("7.5RP", 7.0, 14.0), np.array([0.4195, 0.2762, 43.0600])), + (("7.5RP", 7.0, 16.0), np.array([0.4346, 0.2689, 43.0600])), + (("7.5RP", 7.0, 18.0), np.array([0.4480, 0.2610, 43.0600])), + (("7.5RP", 7.0, 20.0), np.array([0.4600, 0.2550, 43.0600])), + (("7.5RP", 7.0, 22.0), np.array([0.4710, 0.2490, 43.0600])), + (("7.5RP", 7.0, 24.0), np.array([0.4810, 0.2430, 43.0600])), + (("7.5RP", 7.0, 26.0), np.array([0.4910, 0.2360, 43.0600])), + (("10RP", 7.0, 2.0), np.array([0.3258, 0.3148, 43.0600])), + (("10RP", 7.0, 4.0), np.array([0.3446, 0.3125, 43.0600])), + (("10RP", 7.0, 6.0), np.array([0.3648, 0.3098, 43.0600])), + (("10RP", 7.0, 8.0), np.array([0.3851, 0.3067, 43.0600])), + (("10RP", 7.0, 10.0), np.array([0.4040, 0.3030, 43.0600])), + (("10RP", 7.0, 12.0), np.array([0.4260, 0.2980, 43.0600])), + (("10RP", 7.0, 14.0), np.array([0.4456, 0.2931, 43.0600])), + (("10RP", 7.0, 16.0), np.array([0.4648, 0.2878, 43.0600])), + (("10RP", 7.0, 18.0), np.array([0.4830, 0.2830, 43.0600])), + (("10RP", 7.0, 20.0), np.array([0.4980, 0.2790, 43.0600])), + (("10RP", 7.0, 22.0), np.array([0.5110, 0.2740, 43.0600])), + (("10RP", 7.0, 24.0), np.array([0.5260, 0.2670, 43.0600])), + (("2.5R", 7.0, 2.0), np.array([0.3284, 0.3170, 43.0600])), + (("2.5R", 7.0, 4.0), np.array([0.3499, 0.3171, 43.0600])), + (("2.5R", 7.0, 6.0), np.array([0.3728, 0.3170, 43.0600])), + (("2.5R", 7.0, 8.0), np.array([0.3961, 0.3160, 43.0600])), + (("2.5R", 7.0, 10.0), np.array([0.4183, 0.3144, 43.0600])), + (("2.5R", 7.0, 12.0), np.array([0.4435, 0.3119, 43.0600])), + (("2.5R", 7.0, 14.0), np.array([0.4660, 0.3082, 43.0600])), + (("2.5R", 7.0, 16.0), np.array([0.4885, 0.3039, 43.0600])), + (("2.5R", 7.0, 18.0), np.array([0.5070, 0.3000, 43.0600])), + (("2.5R", 7.0, 20.0), np.array([0.5230, 0.2960, 43.0600])), + (("2.5R", 7.0, 22.0), np.array([0.5360, 0.2910, 43.0600])), + (("2.5R", 7.0, 24.0), np.array([0.5530, 0.2870, 43.0600])), + (("5R", 7.0, 2.0), np.array([0.3306, 0.3190, 43.0600])), + (("5R", 7.0, 4.0), np.array([0.3552, 0.3222, 43.0600])), + (("5R", 7.0, 6.0), np.array([0.3805, 0.3244, 43.0600])), + (("5R", 7.0, 8.0), np.array([0.4067, 0.3256, 43.0600])), + (("5R", 7.0, 10.0), np.array([0.4320, 0.3260, 43.0600])), + (("5R", 7.0, 12.0), np.array([0.4595, 0.3252, 43.0600])), + (("5R", 7.0, 14.0), np.array([0.4848, 0.3238, 43.0600])), + (("5R", 7.0, 16.0), np.array([0.5100, 0.3210, 43.0600])), + (("5R", 7.0, 18.0), np.array([0.5300, 0.3180, 43.0600])), + (("5R", 7.0, 20.0), np.array([0.5470, 0.3150, 43.0600])), + (("5R", 7.0, 22.0), np.array([0.5630, 0.3110, 43.0600])), + (("5R", 7.0, 24.0), np.array([0.5800, 0.3060, 43.0600])), + (("7.5R", 7.0, 2.0), np.array([0.3335, 0.3220, 43.0600])), + (("7.5R", 7.0, 4.0), np.array([0.3611, 0.3282, 43.0600])), + (("7.5R", 7.0, 6.0), np.array([0.3888, 0.3336, 43.0600])), + (("7.5R", 7.0, 8.0), np.array([0.4196, 0.3382, 43.0600])), + (("7.5R", 7.0, 10.0), np.array([0.4470, 0.3413, 43.0600])), + (("7.5R", 7.0, 12.0), np.array([0.4777, 0.3435, 43.0600])), + (("7.5R", 7.0, 14.0), np.array([0.5059, 0.3450, 43.0600])), + (("7.5R", 7.0, 16.0), np.array([0.5341, 0.3452, 43.0600])), + (("7.5R", 7.0, 18.0), np.array([0.5540, 0.3430, 43.0600])), + (("7.5R", 7.0, 20.0), np.array([0.5730, 0.3410, 43.0600])), + (("7.5R", 7.0, 22.0), np.array([0.5920, 0.3380, 43.0600])), + (("7.5R", 7.0, 24.0), np.array([0.6070, 0.3350, 43.0600])), + (("7.5R", 7.0, 26.0), np.array([0.6220, 0.3320, 43.0600])), + (("10R", 7.0, 2.0), np.array([0.3360, 0.3253, 43.0600])), + (("10R", 7.0, 4.0), np.array([0.3671, 0.3360, 43.0600])), + (("10R", 7.0, 6.0), np.array([0.3984, 0.3452, 43.0600])), + (("10R", 7.0, 8.0), np.array([0.4308, 0.3533, 43.0600])), + (("10R", 7.0, 10.0), np.array([0.4600, 0.3596, 43.0600])), + (("10R", 7.0, 12.0), np.array([0.4930, 0.3659, 43.0600])), + (("10R", 7.0, 14.0), np.array([0.5234, 0.3700, 43.0600])), + (("10R", 7.0, 16.0), np.array([0.5519, 0.3729, 43.0600])), + (("10R", 7.0, 18.0), np.array([0.5710, 0.3730, 43.0600])), + (("10R", 7.0, 20.0), np.array([0.5920, 0.3740, 43.0600])), + (("10R", 7.0, 22.0), np.array([0.6140, 0.3740, 43.0600])), + (("10R", 7.0, 24.0), np.array([0.6300, 0.3720, 43.0600])), + (("10R", 7.0, 26.0), np.array([0.6470, 0.3690, 43.0600])), + (("2.5YR", 7.0, 2.0), np.array([0.3392, 0.3298, 43.0600])), + (("2.5YR", 7.0, 4.0), np.array([0.3715, 0.3439, 43.0600])), + (("2.5YR", 7.0, 6.0), np.array([0.4053, 0.3570, 43.0600])), + (("2.5YR", 7.0, 8.0), np.array([0.4371, 0.3679, 43.0600])), + (("2.5YR", 7.0, 10.0), np.array([0.4671, 0.3768, 43.0600])), + (("2.5YR", 7.0, 12.0), np.array([0.5001, 0.3861, 43.0600])), + (("2.5YR", 7.0, 14.0), np.array([0.5297, 0.3938, 43.0600])), + (("2.5YR", 7.0, 16.0), np.array([0.5522, 0.3989, 43.0600])), + (("2.5YR", 7.0, 18.0), np.array([0.5695, 0.4024, 43.0600])), + (("2.5YR", 7.0, 20.0), np.array([0.5824, 0.4046, 43.0600])), + (("2.5YR", 7.0, 22.0), np.array([0.5940, 0.4070, 43.0600])), + (("2.5YR", 7.0, 24.0), np.array([0.6020, 0.4090, 43.0600])), + (("5YR", 7.0, 2.0), np.array([0.3421, 0.3349, 43.0600])), + (("5YR", 7.0, 4.0), np.array([0.3750, 0.3530, 43.0600])), + (("5YR", 7.0, 6.0), np.array([0.4091, 0.3701, 43.0600])), + (("5YR", 7.0, 8.0), np.array([0.4402, 0.3842, 43.0600])), + (("5YR", 7.0, 10.0), np.array([0.4711, 0.3972, 43.0600])), + (("5YR", 7.0, 12.0), np.array([0.5007, 0.4081, 43.0600])), + (("5YR", 7.0, 14.0), np.array([0.5252, 0.4168, 43.0600])), + (("5YR", 7.0, 16.0), np.array([0.5437, 0.4228, 43.0600])), + (("5YR", 7.0, 18.0), np.array([0.5564, 0.4267, 43.0600])), + (("5YR", 7.0, 20.0), np.array([0.5657, 0.4298, 43.0600])), + (("5YR", 7.0, 22.0), np.array([0.5750, 0.4340, 43.0600])), + (("7.5YR", 7.0, 2.0), np.array([0.3437, 0.3397, 43.0600])), + (("7.5YR", 7.0, 4.0), np.array([0.3772, 0.3613, 43.0600])), + (("7.5YR", 7.0, 6.0), np.array([0.4107, 0.3820, 43.0600])), + (("7.5YR", 7.0, 8.0), np.array([0.4415, 0.3996, 43.0600])), + (("7.5YR", 7.0, 10.0), np.array([0.4704, 0.4151, 43.0600])), + (("7.5YR", 7.0, 12.0), np.array([0.4970, 0.4282, 43.0600])), + (("7.5YR", 7.0, 14.0), np.array([0.5174, 0.4381, 43.0600])), + (("7.5YR", 7.0, 16.0), np.array([0.5319, 0.4449, 43.0600])), + (("7.5YR", 7.0, 18.0), np.array([0.5417, 0.4492, 43.0600])), + (("7.5YR", 7.0, 20.0), np.array([0.5480, 0.4530, 43.0600])), + (("7.5YR", 7.0, 22.0), np.array([0.5560, 0.4560, 43.0600])), + (("10YR", 7.0, 2.0), np.array([0.3443, 0.3454, 43.0600])), + (("10YR", 7.0, 4.0), np.array([0.3778, 0.3719, 43.0600])), + (("10YR", 7.0, 6.0), np.array([0.4102, 0.3960, 43.0600])), + (("10YR", 7.0, 8.0), np.array([0.4399, 0.4164, 43.0600])), + (("10YR", 7.0, 10.0), np.array([0.4667, 0.4335, 43.0600])), + (("10YR", 7.0, 12.0), np.array([0.4900, 0.4480, 43.0600])), + (("10YR", 7.0, 14.0), np.array([0.5074, 0.4581, 43.0600])), + (("10YR", 7.0, 16.0), np.array([0.5188, 0.4650, 43.0600])), + (("10YR", 7.0, 18.0), np.array([0.5276, 0.4700, 43.0600])), + (("10YR", 7.0, 20.0), np.array([0.5320, 0.4740, 43.0600])), + (("2.5Y", 7.0, 2.0), np.array([0.3436, 0.3507, 43.0600])), + (("2.5Y", 7.0, 4.0), np.array([0.3761, 0.3800, 43.0600])), + (("2.5Y", 7.0, 6.0), np.array([0.4073, 0.4073, 43.0600])), + (("2.5Y", 7.0, 8.0), np.array([0.4353, 0.4312, 43.0600])), + (("2.5Y", 7.0, 10.0), np.array([0.4606, 0.4516, 43.0600])), + (("2.5Y", 7.0, 12.0), np.array([0.4806, 0.4666, 43.0600])), + (("2.5Y", 7.0, 14.0), np.array([0.4950, 0.4773, 43.0600])), + (("2.5Y", 7.0, 16.0), np.array([0.5049, 0.4843, 43.0600])), + (("2.5Y", 7.0, 18.0), np.array([0.5110, 0.4880, 43.0600])), + (("2.5Y", 7.0, 20.0), np.array([0.5160, 0.4920, 43.0600])), + (("5Y", 7.0, 2.0), np.array([0.3419, 0.3540, 43.0600])), + (("5Y", 7.0, 4.0), np.array([0.3718, 0.3885, 43.0600])), + (("5Y", 7.0, 6.0), np.array([0.4009, 0.4198, 43.0600])), + (("5Y", 7.0, 8.0), np.array([0.4271, 0.4462, 43.0600])), + (("5Y", 7.0, 10.0), np.array([0.4509, 0.4696, 43.0600])), + (("5Y", 7.0, 12.0), np.array([0.4677, 0.4857, 43.0600])), + (("5Y", 7.0, 14.0), np.array([0.4791, 0.4965, 43.0600])), + (("5Y", 7.0, 16.0), np.array([0.4875, 0.5047, 43.0600])), + (("5Y", 7.0, 18.0), np.array([0.4930, 0.5090, 43.0600])), + (("5Y", 7.0, 20.0), np.array([0.4980, 0.5130, 43.0600])), + (("7.5Y", 7.0, 2.0), np.array([0.3396, 0.3558, 43.0600])), + (("7.5Y", 7.0, 4.0), np.array([0.3677, 0.3925, 43.0600])), + (("7.5Y", 7.0, 6.0), np.array([0.3943, 0.4264, 43.0600])), + (("7.5Y", 7.0, 8.0), np.array([0.4184, 0.4568, 43.0600])), + (("7.5Y", 7.0, 10.0), np.array([0.4400, 0.4830, 43.0600])), + (("7.5Y", 7.0, 12.0), np.array([0.4547, 0.5005, 43.0600])), + (("7.5Y", 7.0, 14.0), np.array([0.4652, 0.5128, 43.0600])), + (("7.5Y", 7.0, 16.0), np.array([0.4728, 0.5215, 43.0600])), + (("7.5Y", 7.0, 18.0), np.array([0.4770, 0.5270, 43.0600])), + (("10Y", 8.0, 2.0), np.array([0.3359, 0.3552, 59.1000])), + (("10Y", 8.0, 4.0), np.array([0.3581, 0.3883, 59.1000])), + (("10Y", 8.0, 6.0), np.array([0.3803, 0.4216, 59.1000])), + (("10Y", 8.0, 8.0), np.array([0.4008, 0.4520, 59.1000])), + (("10Y", 8.0, 10.0), np.array([0.4190, 0.4791, 59.1000])), + (("10Y", 8.0, 12.0), np.array([0.4341, 0.5020, 59.1000])), + (("10Y", 8.0, 14.0), np.array([0.4450, 0.5181, 59.1000])), + (("10Y", 8.0, 16.0), np.array([0.4525, 0.5295, 59.1000])), + (("10Y", 8.0, 18.0), np.array([0.4570, 0.5366, 59.1000])), + (("10Y", 8.0, 20.0), np.array([0.4610, 0.5420, 59.1000])), + (("2.5GY", 8.0, 2.0), np.array([0.3327, 0.3555, 59.1000])), + (("2.5GY", 8.0, 4.0), np.array([0.3504, 0.3887, 59.1000])), + (("2.5GY", 8.0, 6.0), np.array([0.3690, 0.4230, 59.1000])), + (("2.5GY", 8.0, 8.0), np.array([0.3858, 0.4550, 59.1000])), + (("2.5GY", 8.0, 10.0), np.array([0.4021, 0.4869, 59.1000])), + (("2.5GY", 8.0, 12.0), np.array([0.4154, 0.5133, 59.1000])), + (("2.5GY", 8.0, 14.0), np.array([0.4261, 0.5344, 59.1000])), + (("2.5GY", 8.0, 16.0), np.array([0.4327, 0.5475, 59.1000])), + (("2.5GY", 8.0, 18.0), np.array([0.4371, 0.5557, 59.1000])), + (("2.5GY", 8.0, 20.0), np.array([0.4400, 0.5620, 59.1000])), + (("2.5GY", 8.0, 22.0), np.array([0.4430, 0.5680, 59.1000])), + (("5GY", 8.0, 2.0), np.array([0.3284, 0.3542, 59.1000])), + (("5GY", 8.0, 4.0), np.array([0.3433, 0.3872, 59.1000])), + (("5GY", 8.0, 6.0), np.array([0.3573, 0.4214, 59.1000])), + (("5GY", 8.0, 8.0), np.array([0.3696, 0.4542, 59.1000])), + (("5GY", 8.0, 10.0), np.array([0.3816, 0.4879, 59.1000])), + (("5GY", 8.0, 12.0), np.array([0.3924, 0.5199, 59.1000])), + (("5GY", 8.0, 14.0), np.array([0.4011, 0.5468, 59.1000])), + (("5GY", 8.0, 16.0), np.array([0.4061, 0.5641, 59.1000])), + (("5GY", 8.0, 18.0), np.array([0.4104, 0.5785, 59.1000])), + (("5GY", 8.0, 20.0), np.array([0.4127, 0.5855, 59.1000])), + (("5GY", 8.0, 22.0), np.array([0.4150, 0.5950, 59.1000])), + (("7.5GY", 8.0, 2.0), np.array([0.3194, 0.3502, 59.1000])), + (("7.5GY", 8.0, 4.0), np.array([0.3266, 0.3809, 59.1000])), + (("7.5GY", 8.0, 6.0), np.array([0.3339, 0.4129, 59.1000])), + (("7.5GY", 8.0, 8.0), np.array([0.3408, 0.4452, 59.1000])), + (("7.5GY", 8.0, 10.0), np.array([0.3463, 0.4791, 59.1000])), + (("7.5GY", 8.0, 12.0), np.array([0.3511, 0.5144, 59.1000])), + (("7.5GY", 8.0, 14.0), np.array([0.3546, 0.5490, 59.1000])), + (("7.5GY", 8.0, 16.0), np.array([0.3569, 0.5798, 59.1000])), + (("7.5GY", 8.0, 18.0), np.array([0.3585, 0.6063, 59.1000])), + (("7.5GY", 8.0, 20.0), np.array([0.3592, 0.6235, 59.1000])), + (("7.5GY", 8.0, 22.0), np.array([0.3600, 0.6450, 59.1000])), + (("7.5GY", 8.0, 24.0), np.array([0.3600, 0.6600, 59.1000])), + (("7.5GY", 8.0, 26.0), np.array([0.3610, 0.6790, 59.1000])), + (("7.5GY", 8.0, 28.0), np.array([0.3610, 0.6960, 59.1000])), + (("10GY", 8.0, 2.0), np.array([0.3121, 0.3459, 59.1000])), + (("10GY", 8.0, 4.0), np.array([0.3140, 0.3727, 59.1000])), + (("10GY", 8.0, 6.0), np.array([0.3150, 0.4014, 59.1000])), + (("10GY", 8.0, 8.0), np.array([0.3149, 0.4284, 59.1000])), + (("10GY", 8.0, 10.0), np.array([0.3140, 0.4601, 59.1000])), + (("10GY", 8.0, 12.0), np.array([0.3124, 0.4926, 59.1000])), + (("10GY", 8.0, 14.0), np.array([0.3091, 0.5247, 59.1000])), + (("10GY", 8.0, 16.0), np.array([0.3043, 0.5578, 59.1000])), + (("10GY", 8.0, 18.0), np.array([0.2987, 0.5919, 59.1000])), + (("10GY", 8.0, 20.0), np.array([0.2918, 0.6255, 59.1000])), + (("10GY", 8.0, 22.0), np.array([0.2846, 0.6564, 59.1000])), + (("10GY", 8.0, 24.0), np.array([0.2781, 0.6840, 59.1000])), + (("10GY", 8.0, 26.0), np.array([0.2710, 0.7020, 59.1000])), + (("10GY", 8.0, 28.0), np.array([0.2620, 0.7260, 59.1000])), + (("10GY", 8.0, 30.0), np.array([0.2520, 0.7520, 59.1000])), + (("10GY", 8.0, 32.0), np.array([0.2400, 0.7840, 59.1000])), + (("2.5G", 8.0, 2.0), np.array([0.3053, 0.3404, 59.1000])), + (("2.5G", 8.0, 4.0), np.array([0.3009, 0.3614, 59.1000])), + (("2.5G", 8.0, 6.0), np.array([0.2952, 0.3851, 59.1000])), + (("2.5G", 8.0, 8.0), np.array([0.2896, 0.4065, 59.1000])), + (("2.5G", 8.0, 10.0), np.array([0.2829, 0.4301, 59.1000])), + (("2.5G", 8.0, 12.0), np.array([0.2743, 0.4554, 59.1000])), + (("2.5G", 8.0, 14.0), np.array([0.2661, 0.4780, 59.1000])), + (("2.5G", 8.0, 16.0), np.array([0.2563, 0.5045, 59.1000])), + (("2.5G", 8.0, 18.0), np.array([0.2451, 0.5309, 59.1000])), + (("2.5G", 8.0, 20.0), np.array([0.2339, 0.5561, 59.1000])), + (("2.5G", 8.0, 22.0), np.array([0.2221, 0.5799, 59.1000])), + (("2.5G", 8.0, 24.0), np.array([0.2091, 0.6033, 59.1000])), + (("2.5G", 8.0, 26.0), np.array([0.1960, 0.6220, 59.1000])), + (("2.5G", 8.0, 28.0), np.array([0.1800, 0.6470, 59.1000])), + (("2.5G", 8.0, 30.0), np.array([0.1630, 0.6700, 59.1000])), + (("2.5G", 8.0, 32.0), np.array([0.1440, 0.6920, 59.1000])), + (("5G", 8.0, 2.0), np.array([0.3009, 0.3359, 59.1000])), + (("5G", 8.0, 4.0), np.array([0.2924, 0.3523, 59.1000])), + (("5G", 8.0, 6.0), np.array([0.2822, 0.3702, 59.1000])), + (("5G", 8.0, 8.0), np.array([0.2723, 0.3865, 59.1000])), + (("5G", 8.0, 10.0), np.array([0.2613, 0.4026, 59.1000])), + (("5G", 8.0, 12.0), np.array([0.2489, 0.4191, 59.1000])), + (("5G", 8.0, 14.0), np.array([0.2368, 0.4348, 59.1000])), + (("5G", 8.0, 16.0), np.array([0.2240, 0.4500, 59.1000])), + (("5G", 8.0, 18.0), np.array([0.2103, 0.4652, 59.1000])), + (("5G", 8.0, 20.0), np.array([0.1956, 0.4806, 59.1000])), + (("5G", 8.0, 22.0), np.array([0.1821, 0.4940, 59.1000])), + (("5G", 8.0, 24.0), np.array([0.1680, 0.5070, 59.1000])), + (("5G", 8.0, 26.0), np.array([0.1510, 0.5210, 59.1000])), + (("5G", 8.0, 28.0), np.array([0.1340, 0.5370, 59.1000])), + (("5G", 8.0, 30.0), np.array([0.1150, 0.5530, 59.1000])), + (("5G", 8.0, 32.0), np.array([0.0960, 0.5640, 59.1000])), + (("7.5G", 8.0, 2.0), np.array([0.2981, 0.3326, 59.1000])), + (("7.5G", 8.0, 4.0), np.array([0.2874, 0.3464, 59.1000])), + (("7.5G", 8.0, 6.0), np.array([0.2754, 0.3608, 59.1000])), + (("7.5G", 8.0, 8.0), np.array([0.2639, 0.3733, 59.1000])), + (("7.5G", 8.0, 10.0), np.array([0.2515, 0.3867, 59.1000])), + (("7.5G", 8.0, 12.0), np.array([0.2380, 0.4002, 59.1000])), + (("7.5G", 8.0, 14.0), np.array([0.2254, 0.4125, 59.1000])), + (("7.5G", 8.0, 16.0), np.array([0.2120, 0.4252, 59.1000])), + (("7.5G", 8.0, 18.0), np.array([0.1980, 0.4372, 59.1000])), + (("7.5G", 8.0, 20.0), np.array([0.1845, 0.4492, 59.1000])), + (("7.5G", 8.0, 22.0), np.array([0.1700, 0.4590, 59.1000])), + (("7.5G", 8.0, 24.0), np.array([0.1550, 0.4710, 59.1000])), + (("7.5G", 8.0, 26.0), np.array([0.1390, 0.4830, 59.1000])), + (("7.5G", 8.0, 28.0), np.array([0.1230, 0.4940, 59.1000])), + (("7.5G", 8.0, 30.0), np.array([0.1050, 0.5070, 59.1000])), + (("10G", 8.0, 2.0), np.array([0.2957, 0.3293, 59.1000])), + (("10G", 8.0, 4.0), np.array([0.2828, 0.3403, 59.1000])), + (("10G", 8.0, 6.0), np.array([0.2693, 0.3512, 59.1000])), + (("10G", 8.0, 8.0), np.array([0.2564, 0.3611, 59.1000])), + (("10G", 8.0, 10.0), np.array([0.2430, 0.3710, 59.1000])), + (("10G", 8.0, 12.0), np.array([0.2282, 0.3811, 59.1000])), + (("10G", 8.0, 14.0), np.array([0.2148, 0.3903, 59.1000])), + (("10G", 8.0, 16.0), np.array([0.2012, 0.3992, 59.1000])), + (("10G", 8.0, 18.0), np.array([0.1866, 0.4086, 59.1000])), + (("10G", 8.0, 20.0), np.array([0.1734, 0.4164, 59.1000])), + (("10G", 8.0, 22.0), np.array([0.1590, 0.4240, 59.1000])), + (("10G", 8.0, 24.0), np.array([0.1420, 0.4330, 59.1000])), + (("10G", 8.0, 26.0), np.array([0.1270, 0.4410, 59.1000])), + (("10G", 8.0, 28.0), np.array([0.1120, 0.4480, 59.1000])), + (("2.5BG", 8.0, 2.0), np.array([0.2940, 0.3268, 59.1000])), + (("2.5BG", 8.0, 4.0), np.array([0.2791, 0.3351, 59.1000])), + (("2.5BG", 8.0, 6.0), np.array([0.2647, 0.3429, 59.1000])), + (("2.5BG", 8.0, 8.0), np.array([0.2500, 0.3500, 59.1000])), + (("2.5BG", 8.0, 10.0), np.array([0.2352, 0.3566, 59.1000])), + (("2.5BG", 8.0, 12.0), np.array([0.2196, 0.3630, 59.1000])), + (("2.5BG", 8.0, 14.0), np.array([0.2057, 0.3681, 59.1000])), + (("2.5BG", 8.0, 16.0), np.array([0.1915, 0.3732, 59.1000])), + (("2.5BG", 8.0, 18.0), np.array([0.1759, 0.3782, 59.1000])), + (("2.5BG", 8.0, 20.0), np.array([0.1620, 0.3830, 59.1000])), + (("2.5BG", 8.0, 22.0), np.array([0.1480, 0.3870, 59.1000])), + (("2.5BG", 8.0, 24.0), np.array([0.1320, 0.3920, 59.1000])), + (("2.5BG", 8.0, 26.0), np.array([0.1160, 0.3960, 59.1000])), + (("5BG", 8.0, 2.0), np.array([0.2919, 0.3228, 59.1000])), + (("5BG", 8.0, 4.0), np.array([0.2752, 0.3278, 59.1000])), + (("5BG", 8.0, 6.0), np.array([0.2588, 0.3318, 59.1000])), + (("5BG", 8.0, 8.0), np.array([0.2419, 0.3352, 59.1000])), + (("5BG", 8.0, 10.0), np.array([0.2264, 0.3383, 59.1000])), + (("5BG", 8.0, 12.0), np.array([0.2101, 0.3412, 59.1000])), + (("5BG", 8.0, 14.0), np.array([0.1958, 0.3432, 59.1000])), + (("5BG", 8.0, 16.0), np.array([0.1814, 0.3450, 59.1000])), + (("5BG", 8.0, 18.0), np.array([0.1650, 0.3460, 59.1000])), + (("5BG", 8.0, 20.0), np.array([0.1510, 0.3480, 59.1000])), + (("5BG", 8.0, 22.0), np.array([0.1380, 0.3490, 59.1000])), + (("5BG", 8.0, 24.0), np.array([0.1220, 0.3510, 59.1000])), + (("5BG", 8.0, 26.0), np.array([0.1050, 0.3520, 59.1000])), + (("7.5BG", 8.0, 2.0), np.array([0.2900, 0.3183, 59.1000])), + (("7.5BG", 8.0, 4.0), np.array([0.2718, 0.3200, 59.1000])), + (("7.5BG", 8.0, 6.0), np.array([0.2525, 0.3198, 59.1000])), + (("7.5BG", 8.0, 8.0), np.array([0.2352, 0.3198, 59.1000])), + (("7.5BG", 8.0, 10.0), np.array([0.2184, 0.3196, 59.1000])), + (("7.5BG", 8.0, 12.0), np.array([0.2010, 0.3188, 59.1000])), + (("7.5BG", 8.0, 14.0), np.array([0.1868, 0.3179, 59.1000])), + (("7.5BG", 8.0, 16.0), np.array([0.1721, 0.3168, 59.1000])), + (("7.5BG", 8.0, 18.0), np.array([0.1550, 0.3140, 59.1000])), + (("7.5BG", 8.0, 20.0), np.array([0.1420, 0.3110, 59.1000])), + (("7.5BG", 8.0, 22.0), np.array([0.1270, 0.3090, 59.1000])), + (("7.5BG", 8.0, 24.0), np.array([0.1110, 0.3070, 59.1000])), + (("10BG", 8.0, 2.0), np.array([0.2894, 0.3152, 59.1000])), + (("10BG", 8.0, 4.0), np.array([0.2686, 0.3130, 59.1000])), + (("10BG", 8.0, 6.0), np.array([0.2489, 0.3099, 59.1000])), + (("10BG", 8.0, 8.0), np.array([0.2302, 0.3063, 59.1000])), + (("10BG", 8.0, 10.0), np.array([0.2120, 0.3025, 59.1000])), + (("10BG", 8.0, 12.0), np.array([0.1937, 0.2978, 59.1000])), + (("10BG", 8.0, 14.0), np.array([0.1788, 0.2936, 59.1000])), + (("10BG", 8.0, 16.0), np.array([0.1610, 0.2880, 59.1000])), + (("10BG", 8.0, 18.0), np.array([0.1450, 0.2820, 59.1000])), + (("10BG", 8.0, 20.0), np.array([0.1330, 0.2780, 59.1000])), + (("10BG", 8.0, 22.0), np.array([0.1180, 0.2730, 59.1000])), + (("2.5B", 8.0, 2.0), np.array([0.2897, 0.3124, 59.1000])), + (("2.5B", 8.0, 4.0), np.array([0.2668, 0.3067, 59.1000])), + (("2.5B", 8.0, 6.0), np.array([0.2462, 0.3000, 59.1000])), + (("2.5B", 8.0, 8.0), np.array([0.2264, 0.2923, 59.1000])), + (("2.5B", 8.0, 10.0), np.array([0.2066, 0.2839, 59.1000])), + (("2.5B", 8.0, 12.0), np.array([0.1877, 0.2752, 59.1000])), + (("2.5B", 8.0, 14.0), np.array([0.1720, 0.2660, 59.1000])), + (("2.5B", 8.0, 16.0), np.array([0.1520, 0.2560, 59.1000])), + (("2.5B", 8.0, 18.0), np.array([0.1370, 0.2450, 59.1000])), + (("2.5B", 8.0, 20.0), np.array([0.1230, 0.2350, 59.1000])), + (("5B", 8.0, 2.0), np.array([0.2908, 0.3096, 59.1000])), + (("5B", 8.0, 4.0), np.array([0.2671, 0.2998, 59.1000])), + (("5B", 8.0, 6.0), np.array([0.2457, 0.2888, 59.1000])), + (("5B", 8.0, 8.0), np.array([0.2237, 0.2761, 59.1000])), + (("5B", 8.0, 10.0), np.array([0.2040, 0.2630, 59.1000])), + (("5B", 8.0, 12.0), np.array([0.1820, 0.2470, 59.1000])), + (("5B", 8.0, 14.0), np.array([0.1660, 0.2360, 59.1000])), + (("5B", 8.0, 16.0), np.array([0.1480, 0.2210, 59.1000])), + (("5B", 8.0, 18.0), np.array([0.1320, 0.2070, 59.1000])), + (("7.5B", 8.0, 2.0), np.array([0.2922, 0.3077, 59.1000])), + (("7.5B", 8.0, 4.0), np.array([0.2688, 0.2956, 59.1000])), + (("7.5B", 8.0, 6.0), np.array([0.2472, 0.2821, 59.1000])), + (("7.5B", 8.0, 8.0), np.array([0.2252, 0.2668, 59.1000])), + (("7.5B", 8.0, 10.0), np.array([0.2050, 0.2500, 59.1000])), + (("7.5B", 8.0, 12.0), np.array([0.1840, 0.2330, 59.1000])), + (("7.5B", 8.0, 14.0), np.array([0.1680, 0.2190, 59.1000])), + (("7.5B", 8.0, 16.0), np.array([0.1510, 0.2040, 59.1000])), + (("10B", 8.0, 2.0), np.array([0.2935, 0.3062, 59.1000])), + (("10B", 8.0, 4.0), np.array([0.2718, 0.2911, 59.1000])), + (("10B", 8.0, 6.0), np.array([0.2512, 0.2760, 59.1000])), + (("10B", 8.0, 8.0), np.array([0.2294, 0.2587, 59.1000])), + (("10B", 8.0, 10.0), np.array([0.2100, 0.2420, 59.1000])), + (("10B", 8.0, 12.0), np.array([0.1900, 0.2240, 59.1000])), + (("10B", 8.0, 14.0), np.array([0.1740, 0.2060, 59.1000])), + (("2.5PB", 8.0, 2.0), np.array([0.2957, 0.3047, 59.1000])), + (("2.5PB", 8.0, 4.0), np.array([0.2758, 0.2879, 59.1000])), + (("2.5PB", 8.0, 6.0), np.array([0.2562, 0.2709, 59.1000])), + (("2.5PB", 8.0, 8.0), np.array([0.2370, 0.2530, 59.1000])), + (("2.5PB", 8.0, 10.0), np.array([0.2180, 0.2350, 59.1000])), + (("2.5PB", 8.0, 12.0), np.array([0.2020, 0.2170, 59.1000])), + (("2.5PB", 8.0, 14.0), np.array([0.1850, 0.1990, 59.1000])), + (("5PB", 8.0, 2.0), np.array([0.2974, 0.3039, 59.1000])), + (("5PB", 8.0, 4.0), np.array([0.2798, 0.2861, 59.1000])), + (("5PB", 8.0, 6.0), np.array([0.2614, 0.2670, 59.1000])), + (("5PB", 8.0, 8.0), np.array([0.2440, 0.2490, 59.1000])), + (("5PB", 8.0, 10.0), np.array([0.2270, 0.2290, 59.1000])), + (("5PB", 8.0, 12.0), np.array([0.2140, 0.2140, 59.1000])), + (("5PB", 8.0, 14.0), np.array([0.2000, 0.1940, 59.1000])), + (("7.5PB", 8.0, 2.0), np.array([0.3003, 0.3034, 59.1000])), + (("7.5PB", 8.0, 4.0), np.array([0.2856, 0.2846, 59.1000])), + (("7.5PB", 8.0, 6.0), np.array([0.2702, 0.2648, 59.1000])), + (("7.5PB", 8.0, 8.0), np.array([0.2550, 0.2440, 59.1000])), + (("7.5PB", 8.0, 10.0), np.array([0.2410, 0.2240, 59.1000])), + (("7.5PB", 8.0, 12.0), np.array([0.2320, 0.2100, 59.1000])), + (("7.5PB", 8.0, 14.0), np.array([0.2210, 0.1930, 59.1000])), + (("10PB", 8.0, 2.0), np.array([0.3027, 0.3035, 59.1000])), + (("10PB", 8.0, 4.0), np.array([0.2911, 0.2848, 59.1000])), + (("10PB", 8.0, 6.0), np.array([0.2792, 0.2649, 59.1000])), + (("10PB", 8.0, 8.0), np.array([0.2677, 0.2443, 59.1000])), + (("10PB", 8.0, 10.0), np.array([0.2570, 0.2250, 59.1000])), + (("10PB", 8.0, 12.0), np.array([0.2480, 0.2110, 59.1000])), + (("10PB", 8.0, 14.0), np.array([0.2400, 0.1960, 59.1000])), + (("10PB", 8.0, 16.0), np.array([0.2330, 0.1850, 59.1000])), + (("2.5P", 8.0, 2.0), np.array([0.3048, 0.3040, 59.1000])), + (("2.5P", 8.0, 4.0), np.array([0.2962, 0.2850, 59.1000])), + (("2.5P", 8.0, 6.0), np.array([0.2881, 0.2671, 59.1000])), + (("2.5P", 8.0, 8.0), np.array([0.2800, 0.2488, 59.1000])), + (("2.5P", 8.0, 10.0), np.array([0.2740, 0.2310, 59.1000])), + (("2.5P", 8.0, 12.0), np.array([0.2680, 0.2180, 59.1000])), + (("2.5P", 8.0, 14.0), np.array([0.2630, 0.2040, 59.1000])), + (("2.5P", 8.0, 16.0), np.array([0.2590, 0.1940, 59.1000])), + (("2.5P", 8.0, 18.0), np.array([0.2550, 0.1830, 59.1000])), + (("5P", 8.0, 2.0), np.array([0.3065, 0.3047, 59.1000])), + (("5P", 8.0, 4.0), np.array([0.3012, 0.2868, 59.1000])), + (("5P", 8.0, 6.0), np.array([0.2963, 0.2704, 59.1000])), + (("5P", 8.0, 8.0), np.array([0.2914, 0.2534, 59.1000])), + (("5P", 8.0, 10.0), np.array([0.2870, 0.2380, 59.1000])), + (("5P", 8.0, 12.0), np.array([0.2830, 0.2240, 59.1000])), + (("5P", 8.0, 14.0), np.array([0.2800, 0.2110, 59.1000])), + (("5P", 8.0, 16.0), np.array([0.2780, 0.2010, 59.1000])), + (("5P", 8.0, 18.0), np.array([0.2760, 0.1910, 59.1000])), + (("5P", 8.0, 20.0), np.array([0.2730, 0.1790, 59.1000])), + (("5P", 8.0, 22.0), np.array([0.2700, 0.1680, 59.1000])), + (("7.5P", 8.0, 2.0), np.array([0.3107, 0.3070, 59.1000])), + (("7.5P", 8.0, 4.0), np.array([0.3114, 0.2915, 59.1000])), + (("7.5P", 8.0, 6.0), np.array([0.3114, 0.2785, 59.1000])), + (("7.5P", 8.0, 8.0), np.array([0.3116, 0.2626, 59.1000])), + (("7.5P", 8.0, 10.0), np.array([0.3116, 0.2497, 59.1000])), + (("7.5P", 8.0, 12.0), np.array([0.3117, 0.2370, 59.1000])), + (("7.5P", 8.0, 14.0), np.array([0.3110, 0.2240, 59.1000])), + (("7.5P", 8.0, 16.0), np.array([0.3110, 0.2140, 59.1000])), + (("7.5P", 8.0, 18.0), np.array([0.3110, 0.2040, 59.1000])), + (("7.5P", 8.0, 20.0), np.array([0.3110, 0.1940, 59.1000])), + (("7.5P", 8.0, 22.0), np.array([0.3110, 0.1840, 59.1000])), + (("7.5P", 8.0, 24.0), np.array([0.3110, 0.1730, 59.1000])), + (("7.5P", 8.0, 26.0), np.array([0.3110, 0.1620, 59.1000])), + (("10P", 8.0, 2.0), np.array([0.3131, 0.3084, 59.1000])), + (("10P", 8.0, 4.0), np.array([0.3175, 0.2955, 59.1000])), + (("10P", 8.0, 6.0), np.array([0.3213, 0.2829, 59.1000])), + (("10P", 8.0, 8.0), np.array([0.3250, 0.2700, 59.1000])), + (("10P", 8.0, 10.0), np.array([0.3282, 0.2582, 59.1000])), + (("10P", 8.0, 12.0), np.array([0.3312, 0.2470, 59.1000])), + (("10P", 8.0, 14.0), np.array([0.3342, 0.2349, 59.1000])), + (("10P", 8.0, 16.0), np.array([0.3370, 0.2250, 59.1000])), + (("10P", 8.0, 18.0), np.array([0.3390, 0.2170, 59.1000])), + (("10P", 8.0, 20.0), np.array([0.3410, 0.2070, 59.1000])), + (("10P", 8.0, 22.0), np.array([0.3440, 0.1960, 59.1000])), + (("10P", 8.0, 24.0), np.array([0.3460, 0.1860, 59.1000])), + (("10P", 8.0, 26.0), np.array([0.3480, 0.1760, 59.1000])), + (("2.5RP", 8.0, 2.0), np.array([0.3154, 0.3100, 59.1000])), + (("2.5RP", 8.0, 4.0), np.array([0.3239, 0.3000, 59.1000])), + (("2.5RP", 8.0, 6.0), np.array([0.3327, 0.2898, 59.1000])), + (("2.5RP", 8.0, 8.0), np.array([0.3406, 0.2793, 59.1000])), + (("2.5RP", 8.0, 10.0), np.array([0.3479, 0.2699, 59.1000])), + (("2.5RP", 8.0, 12.0), np.array([0.3552, 0.2594, 59.1000])), + (("2.5RP", 8.0, 14.0), np.array([0.3621, 0.2496, 59.1000])), + (("2.5RP", 8.0, 16.0), np.array([0.3690, 0.2400, 59.1000])), + (("2.5RP", 8.0, 18.0), np.array([0.3730, 0.2330, 59.1000])), + (("2.5RP", 8.0, 20.0), np.array([0.3790, 0.2240, 59.1000])), + (("2.5RP", 8.0, 22.0), np.array([0.3850, 0.2150, 59.1000])), + (("2.5RP", 8.0, 24.0), np.array([0.3890, 0.2060, 59.1000])), + (("2.5RP", 8.0, 26.0), np.array([0.3940, 0.1970, 59.1000])), + (("5RP", 8.0, 2.0), np.array([0.3180, 0.3120, 59.1000])), + (("5RP", 8.0, 4.0), np.array([0.3308, 0.3052, 59.1000])), + (("5RP", 8.0, 6.0), np.array([0.3440, 0.2978, 59.1000])), + (("5RP", 8.0, 8.0), np.array([0.3570, 0.2900, 59.1000])), + (("5RP", 8.0, 10.0), np.array([0.3685, 0.2828, 59.1000])), + (("5RP", 8.0, 12.0), np.array([0.3818, 0.2742, 59.1000])), + (("5RP", 8.0, 14.0), np.array([0.3930, 0.2670, 59.1000])), + (("5RP", 8.0, 16.0), np.array([0.4020, 0.2600, 59.1000])), + (("5RP", 8.0, 18.0), np.array([0.4110, 0.2530, 59.1000])), + (("5RP", 8.0, 20.0), np.array([0.4200, 0.2450, 59.1000])), + (("5RP", 8.0, 22.0), np.array([0.4290, 0.2370, 59.1000])), + (("5RP", 8.0, 24.0), np.array([0.4370, 0.2300, 59.1000])), + (("7.5RP", 8.0, 2.0), np.array([0.3200, 0.3136, 59.1000])), + (("7.5RP", 8.0, 4.0), np.array([0.3360, 0.3092, 59.1000])), + (("7.5RP", 8.0, 6.0), np.array([0.3521, 0.3042, 59.1000])), + (("7.5RP", 8.0, 8.0), np.array([0.3682, 0.2983, 59.1000])), + (("7.5RP", 8.0, 10.0), np.array([0.3830, 0.2930, 59.1000])), + (("7.5RP", 8.0, 12.0), np.array([0.4002, 0.2859, 59.1000])), + (("7.5RP", 8.0, 14.0), np.array([0.4140, 0.2800, 59.1000])), + (("7.5RP", 8.0, 16.0), np.array([0.4260, 0.2740, 59.1000])), + (("7.5RP", 8.0, 18.0), np.array([0.4380, 0.2670, 59.1000])), + (("7.5RP", 8.0, 20.0), np.array([0.4490, 0.2610, 59.1000])), + (("7.5RP", 8.0, 22.0), np.array([0.4600, 0.2550, 59.1000])), + (("10RP", 8.0, 2.0), np.array([0.3218, 0.3152, 59.1000])), + (("10RP", 8.0, 4.0), np.array([0.3412, 0.3135, 59.1000])), + (("10RP", 8.0, 6.0), np.array([0.3600, 0.3112, 59.1000])), + (("10RP", 8.0, 8.0), np.array([0.3800, 0.3082, 59.1000])), + (("10RP", 8.0, 10.0), np.array([0.3983, 0.3049, 59.1000])), + (("10RP", 8.0, 12.0), np.array([0.4220, 0.3000, 59.1000])), + (("10RP", 8.0, 14.0), np.array([0.4390, 0.2970, 59.1000])), + (("10RP", 8.0, 16.0), np.array([0.4530, 0.2930, 59.1000])), + (("10RP", 8.0, 18.0), np.array([0.4690, 0.2890, 59.1000])), + (("10RP", 8.0, 20.0), np.array([0.4840, 0.2840, 59.1000])), + (("2.5R", 8.0, 2.0), np.array([0.3236, 0.3169, 59.1000])), + (("2.5R", 8.0, 4.0), np.array([0.3460, 0.3177, 59.1000])), + (("2.5R", 8.0, 6.0), np.array([0.3671, 0.3175, 59.1000])), + (("2.5R", 8.0, 8.0), np.array([0.3900, 0.3171, 59.1000])), + (("2.5R", 8.0, 10.0), np.array([0.4125, 0.3160, 59.1000])), + (("2.5R", 8.0, 12.0), np.array([0.4390, 0.3140, 59.1000])), + (("2.5R", 8.0, 14.0), np.array([0.4600, 0.3110, 59.1000])), + (("2.5R", 8.0, 16.0), np.array([0.4760, 0.3080, 59.1000])), + (("2.5R", 8.0, 18.0), np.array([0.4930, 0.3060, 59.1000])), + (("2.5R", 8.0, 20.0), np.array([0.5100, 0.3030, 59.1000])), + (("5R", 8.0, 2.0), np.array([0.3254, 0.3186, 59.1000])), + (("5R", 8.0, 4.0), np.array([0.3510, 0.3224, 59.1000])), + (("5R", 8.0, 6.0), np.array([0.3743, 0.3248, 59.1000])), + (("5R", 8.0, 8.0), np.array([0.4001, 0.3263, 59.1000])), + (("5R", 8.0, 10.0), np.array([0.4249, 0.3270, 59.1000])), + (("5R", 8.0, 12.0), np.array([0.4540, 0.3260, 59.1000])), + (("5R", 8.0, 14.0), np.array([0.4760, 0.3250, 59.1000])), + (("5R", 8.0, 16.0), np.array([0.4940, 0.3240, 59.1000])), + (("5R", 8.0, 18.0), np.array([0.5130, 0.3220, 59.1000])), + (("7.5R", 8.0, 2.0), np.array([0.3277, 0.3211, 59.1000])), + (("7.5R", 8.0, 4.0), np.array([0.3564, 0.3279, 59.1000])), + (("7.5R", 8.0, 6.0), np.array([0.3830, 0.3335, 59.1000])), + (("7.5R", 8.0, 8.0), np.array([0.4118, 0.3385, 59.1000])), + (("7.5R", 8.0, 10.0), np.array([0.4388, 0.3419, 59.1000])), + (("7.5R", 8.0, 12.0), np.array([0.4700, 0.3450, 59.1000])), + (("7.5R", 8.0, 14.0), np.array([0.4920, 0.3440, 59.1000])), + (("7.5R", 8.0, 16.0), np.array([0.5140, 0.3440, 59.1000])), + (("7.5R", 8.0, 18.0), np.array([0.5360, 0.3430, 59.1000])), + (("7.5R", 8.0, 20.0), np.array([0.5530, 0.3430, 59.1000])), + (("10R", 8.0, 2.0), np.array([0.3301, 0.3237, 59.1000])), + (("10R", 8.0, 4.0), np.array([0.3621, 0.3349, 59.1000])), + (("10R", 8.0, 6.0), np.array([0.3910, 0.3442, 59.1000])), + (("10R", 8.0, 8.0), np.array([0.4212, 0.3526, 59.1000])), + (("10R", 8.0, 10.0), np.array([0.4490, 0.3589, 59.1000])), + (("10R", 8.0, 12.0), np.array([0.4810, 0.3650, 59.1000])), + (("10R", 8.0, 14.0), np.array([0.5070, 0.3690, 59.1000])), + (("10R", 8.0, 16.0), np.array([0.5290, 0.3710, 59.1000])), + (("10R", 8.0, 18.0), np.array([0.5510, 0.3740, 59.1000])), + (("10R", 8.0, 20.0), np.array([0.5690, 0.3740, 59.1000])), + (("10R", 8.0, 22.0), np.array([0.5880, 0.3750, 59.1000])), + (("10R", 8.0, 24.0), np.array([0.6070, 0.3750, 59.1000])), + (("10R", 8.0, 26.0), np.array([0.6250, 0.3750, 59.1000])), + (("2.5YR", 8.0, 2.0), np.array([0.3334, 0.3276, 59.1000])), + (("2.5YR", 8.0, 4.0), np.array([0.3667, 0.3429, 59.1000])), + (("2.5YR", 8.0, 6.0), np.array([0.3960, 0.3547, 59.1000])), + (("2.5YR", 8.0, 8.0), np.array([0.4275, 0.3662, 59.1000])), + (("2.5YR", 8.0, 10.0), np.array([0.4552, 0.3761, 59.1000])), + (("2.5YR", 8.0, 12.0), np.array([0.4852, 0.3847, 59.1000])), + (("2.5YR", 8.0, 14.0), np.array([0.5130, 0.3910, 59.1000])), + (("2.5YR", 8.0, 16.0), np.array([0.5330, 0.3960, 59.1000])), + (("2.5YR", 8.0, 18.0), np.array([0.5510, 0.3990, 59.1000])), + (("2.5YR", 8.0, 20.0), np.array([0.5660, 0.4020, 59.1000])), + (("2.5YR", 8.0, 22.0), np.array([0.5800, 0.4030, 59.1000])), + (("2.5YR", 8.0, 24.0), np.array([0.5920, 0.4040, 59.1000])), + (("2.5YR", 8.0, 26.0), np.array([0.6020, 0.4050, 59.1000])), + (("5YR", 8.0, 2.0), np.array([0.3373, 0.3330, 59.1000])), + (("5YR", 8.0, 4.0), np.array([0.3690, 0.3510, 59.1000])), + (("5YR", 8.0, 6.0), np.array([0.3988, 0.3663, 59.1000])), + (("5YR", 8.0, 8.0), np.array([0.4310, 0.3820, 59.1000])), + (("5YR", 8.0, 10.0), np.array([0.4576, 0.3938, 59.1000])), + (("5YR", 8.0, 12.0), np.array([0.4849, 0.4050, 59.1000])), + (("5YR", 8.0, 14.0), np.array([0.5088, 0.4145, 59.1000])), + (("5YR", 8.0, 16.0), np.array([0.5300, 0.4210, 59.1000])), + (("5YR", 8.0, 18.0), np.array([0.5420, 0.4250, 59.1000])), + (("5YR", 8.0, 20.0), np.array([0.5530, 0.4270, 59.1000])), + (("5YR", 8.0, 22.0), np.array([0.5610, 0.4310, 59.1000])), + (("5YR", 8.0, 24.0), np.array([0.5710, 0.4330, 59.1000])), + (("5YR", 8.0, 26.0), np.array([0.5780, 0.4350, 59.1000])), + (("7.5YR", 8.0, 2.0), np.array([0.3395, 0.3379, 59.1000])), + (("7.5YR", 8.0, 4.0), np.array([0.3699, 0.3586, 59.1000])), + (("7.5YR", 8.0, 6.0), np.array([0.4000, 0.3770, 59.1000])), + (("7.5YR", 8.0, 8.0), np.array([0.4306, 0.3952, 59.1000])), + (("7.5YR", 8.0, 10.0), np.array([0.4568, 0.4100, 59.1000])), + (("7.5YR", 8.0, 12.0), np.array([0.4816, 0.4232, 59.1000])), + (("7.5YR", 8.0, 14.0), np.array([0.5025, 0.4338, 59.1000])), + (("7.5YR", 8.0, 16.0), np.array([0.5195, 0.4424, 59.1000])), + (("7.5YR", 8.0, 18.0), np.array([0.5316, 0.4480, 59.1000])), + (("7.5YR", 8.0, 20.0), np.array([0.5391, 0.4518, 59.1000])), + (("7.5YR", 8.0, 22.0), np.array([0.5460, 0.4540, 59.1000])), + (("7.5YR", 8.0, 24.0), np.array([0.5530, 0.4580, 59.1000])), + (("10YR", 8.0, 2.0), np.array([0.3407, 0.3434, 59.1000])), + (("10YR", 8.0, 4.0), np.array([0.3701, 0.3674, 59.1000])), + (("10YR", 8.0, 6.0), np.array([0.3994, 0.3896, 59.1000])), + (("10YR", 8.0, 8.0), np.array([0.4280, 0.4102, 59.1000])), + (("10YR", 8.0, 10.0), np.array([0.4527, 0.4268, 59.1000])), + (("10YR", 8.0, 12.0), np.array([0.4753, 0.4414, 59.1000])), + (("10YR", 8.0, 14.0), np.array([0.4940, 0.4530, 59.1000])), + (("10YR", 8.0, 16.0), np.array([0.5079, 0.4613, 59.1000])), + (("10YR", 8.0, 18.0), np.array([0.5179, 0.4670, 59.1000])), + (("10YR", 8.0, 20.0), np.array([0.5245, 0.4709, 59.1000])), + (("10YR", 8.0, 22.0), np.array([0.5300, 0.4740, 59.1000])), + (("2.5Y", 8.0, 2.0), np.array([0.3406, 0.3484, 59.1000])), + (("2.5Y", 8.0, 4.0), np.array([0.3684, 0.3751, 59.1000])), + (("2.5Y", 8.0, 6.0), np.array([0.3969, 0.4009, 59.1000])), + (("2.5Y", 8.0, 8.0), np.array([0.4231, 0.4231, 59.1000])), + (("2.5Y", 8.0, 10.0), np.array([0.4469, 0.4423, 59.1000])), + (("2.5Y", 8.0, 12.0), np.array([0.4678, 0.4589, 59.1000])), + (("2.5Y", 8.0, 14.0), np.array([0.4842, 0.4712, 59.1000])), + (("2.5Y", 8.0, 16.0), np.array([0.4957, 0.4800, 59.1000])), + (("2.5Y", 8.0, 18.0), np.array([0.5033, 0.4855, 59.1000])), + (("2.5Y", 8.0, 20.0), np.array([0.5091, 0.4900, 59.1000])), + (("2.5Y", 8.0, 22.0), np.array([0.5140, 0.4940, 59.1000])), + (("5Y", 8.0, 2.0), np.array([0.3394, 0.3518, 59.1000])), + (("5Y", 8.0, 4.0), np.array([0.3650, 0.3826, 59.1000])), + (("5Y", 8.0, 6.0), np.array([0.3913, 0.4117, 59.1000])), + (("5Y", 8.0, 8.0), np.array([0.4158, 0.4378, 59.1000])), + (("5Y", 8.0, 10.0), np.array([0.4376, 0.4601, 59.1000])), + (("5Y", 8.0, 12.0), np.array([0.4562, 0.4788, 59.1000])), + (("5Y", 8.0, 14.0), np.array([0.4699, 0.4920, 59.1000])), + (("5Y", 8.0, 16.0), np.array([0.4791, 0.5012, 59.1000])), + (("5Y", 8.0, 18.0), np.array([0.4847, 0.5069, 59.1000])), + (("5Y", 8.0, 20.0), np.array([0.4900, 0.5110, 59.1000])), + (("5Y", 8.0, 22.0), np.array([0.4930, 0.5150, 59.1000])), + (("7.5Y", 8.0, 2.0), np.array([0.3379, 0.3540, 59.1000])), + (("7.5Y", 8.0, 4.0), np.array([0.3622, 0.3861, 59.1000])), + (("7.5Y", 8.0, 6.0), np.array([0.3862, 0.4175, 59.1000])), + (("7.5Y", 8.0, 8.0), np.array([0.4088, 0.4466, 59.1000])), + (("7.5Y", 8.0, 10.0), np.array([0.4283, 0.4712, 59.1000])), + (("7.5Y", 8.0, 12.0), np.array([0.4455, 0.4917, 59.1000])), + (("7.5Y", 8.0, 14.0), np.array([0.4574, 0.5062, 59.1000])), + (("7.5Y", 8.0, 16.0), np.array([0.4658, 0.5158, 59.1000])), + (("7.5Y", 8.0, 18.0), np.array([0.4709, 0.5220, 59.1000])), + (("7.5Y", 8.0, 20.0), np.array([0.4750, 0.5260, 59.1000])), + (("10Y", 9.0, 2.0), np.array([0.3349, 0.3537, 78.6600])), + (("10Y", 9.0, 4.0), np.array([0.3558, 0.3852, 78.6600])), + (("10Y", 9.0, 6.0), np.array([0.3761, 0.4155, 78.6600])), + (("10Y", 9.0, 8.0), np.array([0.3957, 0.4450, 78.6600])), + (("10Y", 9.0, 10.0), np.array([0.4120, 0.4694, 78.6600])), + (("10Y", 9.0, 12.0), np.array([0.4271, 0.4920, 78.6600])), + (("10Y", 9.0, 14.0), np.array([0.4393, 0.5101, 78.6600])), + (("10Y", 9.0, 16.0), np.array([0.4477, 0.5225, 78.6600])), + (("10Y", 9.0, 18.0), np.array([0.4540, 0.5320, 78.6600])), + (("10Y", 9.0, 20.0), np.array([0.4570, 0.5370, 78.6600])), + (("10Y", 9.0, 22.0), np.array([0.4620, 0.5440, 78.6600])), + (("2.5GY", 9.0, 2.0), np.array([0.3321, 0.3539, 78.6600])), + (("2.5GY", 9.0, 4.0), np.array([0.3499, 0.3866, 78.6600])), + (("2.5GY", 9.0, 6.0), np.array([0.3670, 0.4178, 78.6600])), + (("2.5GY", 9.0, 8.0), np.array([0.3834, 0.4490, 78.6600])), + (("2.5GY", 9.0, 10.0), np.array([0.3973, 0.4761, 78.6600])), + (("2.5GY", 9.0, 12.0), np.array([0.4108, 0.5028, 78.6600])), + (("2.5GY", 9.0, 14.0), np.array([0.4212, 0.5237, 78.6600])), + (("2.5GY", 9.0, 16.0), np.array([0.4288, 0.5383, 78.6600])), + (("2.5GY", 9.0, 18.0), np.array([0.4354, 0.5508, 78.6600])), + (("2.5GY", 9.0, 20.0), np.array([0.4380, 0.5560, 78.6600])), + (("2.5GY", 9.0, 22.0), np.array([0.4420, 0.5640, 78.6600])), + (("5GY", 9.0, 2.0), np.array([0.3284, 0.3534, 78.6600])), + (("5GY", 9.0, 4.0), np.array([0.3437, 0.3861, 78.6600])), + (("5GY", 9.0, 6.0), np.array([0.3572, 0.4179, 78.6600])), + (("5GY", 9.0, 8.0), np.array([0.3698, 0.4497, 78.6600])), + (("5GY", 9.0, 10.0), np.array([0.3810, 0.4791, 78.6600])), + (("5GY", 9.0, 12.0), np.array([0.3911, 0.5082, 78.6600])), + (("5GY", 9.0, 14.0), np.array([0.3993, 0.5329, 78.6600])), + (("5GY", 9.0, 16.0), np.array([0.4058, 0.5541, 78.6600])), + (("5GY", 9.0, 18.0), np.array([0.4108, 0.5699, 78.6600])), + (("5GY", 9.0, 20.0), np.array([0.4140, 0.5800, 78.6600])), + (("5GY", 9.0, 22.0), np.array([0.4170, 0.5900, 78.6600])), + (("7.5GY", 9.0, 2.0), np.array([0.3198, 0.3500, 78.6600])), + (("7.5GY", 9.0, 4.0), np.array([0.3274, 0.3793, 78.6600])), + (("7.5GY", 9.0, 6.0), np.array([0.3351, 0.4111, 78.6600])), + (("7.5GY", 9.0, 8.0), np.array([0.3414, 0.4415, 78.6600])), + (("7.5GY", 9.0, 10.0), np.array([0.3471, 0.4735, 78.6600])), + (("7.5GY", 9.0, 12.0), np.array([0.3518, 0.5042, 78.6600])), + (("7.5GY", 9.0, 14.0), np.array([0.3551, 0.5339, 78.6600])), + (("7.5GY", 9.0, 16.0), np.array([0.3581, 0.5654, 78.6600])), + (("7.5GY", 9.0, 18.0), np.array([0.3602, 0.5920, 78.6600])), + (("7.5GY", 9.0, 20.0), np.array([0.3610, 0.6140, 78.6600])), + (("7.5GY", 9.0, 22.0), np.array([0.3620, 0.6380, 78.6600])), + (("7.5GY", 9.0, 24.0), np.array([0.3600, 0.6570, 78.6600])), + (("7.5GY", 9.0, 26.0), np.array([0.3600, 0.6820, 78.6600])), + (("7.5GY", 9.0, 28.0), np.array([0.3600, 0.7070, 78.6600])), + (("10GY", 9.0, 2.0), np.array([0.3124, 0.3454, 78.6600])), + (("10GY", 9.0, 4.0), np.array([0.3144, 0.3711, 78.6600])), + (("10GY", 9.0, 6.0), np.array([0.3153, 0.4008, 78.6600])), + (("10GY", 9.0, 8.0), np.array([0.3157, 0.4259, 78.6600])), + (("10GY", 9.0, 10.0), np.array([0.3155, 0.4558, 78.6600])), + (("10GY", 9.0, 12.0), np.array([0.3139, 0.4829, 78.6600])), + (("10GY", 9.0, 14.0), np.array([0.3115, 0.5129, 78.6600])), + (("10GY", 9.0, 16.0), np.array([0.3079, 0.5440, 78.6600])), + (("10GY", 9.0, 18.0), np.array([0.3032, 0.5748, 78.6600])), + (("10GY", 9.0, 20.0), np.array([0.2980, 0.6020, 78.6600])), + (("10GY", 9.0, 22.0), np.array([0.2920, 0.6270, 78.6600])), + (("10GY", 9.0, 24.0), np.array([0.2840, 0.6530, 78.6600])), + (("10GY", 9.0, 26.0), np.array([0.2770, 0.6810, 78.6600])), + (("10GY", 9.0, 28.0), np.array([0.2680, 0.7070, 78.6600])), + (("2.5G", 9.0, 2.0), np.array([0.3058, 0.3400, 78.6600])), + (("2.5G", 9.0, 4.0), np.array([0.3018, 0.3606, 78.6600])), + (("2.5G", 9.0, 6.0), np.array([0.2966, 0.3846, 78.6600])), + (("2.5G", 9.0, 8.0), np.array([0.2912, 0.4054, 78.6600])), + (("2.5G", 9.0, 10.0), np.array([0.2851, 0.4275, 78.6600])), + (("2.5G", 9.0, 12.0), np.array([0.2786, 0.4491, 78.6600])), + (("2.5G", 9.0, 14.0), np.array([0.2711, 0.4726, 78.6600])), + (("2.5G", 9.0, 16.0), np.array([0.2630, 0.4966, 78.6600])), + (("2.5G", 9.0, 18.0), np.array([0.2530, 0.5250, 78.6600])), + (("2.5G", 9.0, 20.0), np.array([0.2420, 0.5520, 78.6600])), + (("2.5G", 9.0, 22.0), np.array([0.2300, 0.5760, 78.6600])), + (("2.5G", 9.0, 24.0), np.array([0.2190, 0.5990, 78.6600])), + (("2.5G", 9.0, 26.0), np.array([0.2060, 0.6190, 78.6600])), + (("2.5G", 9.0, 28.0), np.array([0.1920, 0.6430, 78.6600])), + (("5G", 9.0, 2.0), np.array([0.3017, 0.3357, 78.6600])), + (("5G", 9.0, 4.0), np.array([0.2933, 0.3519, 78.6600])), + (("5G", 9.0, 6.0), np.array([0.2832, 0.3697, 78.6600])), + (("5G", 9.0, 8.0), np.array([0.2735, 0.3854, 78.6600])), + (("5G", 9.0, 10.0), np.array([0.2639, 0.4001, 78.6600])), + (("5G", 9.0, 12.0), np.array([0.2528, 0.4160, 78.6600])), + (("5G", 9.0, 14.0), np.array([0.2420, 0.4300, 78.6600])), + (("5G", 9.0, 16.0), np.array([0.2290, 0.4460, 78.6600])), + (("5G", 9.0, 18.0), np.array([0.2140, 0.4610, 78.6600])), + (("5G", 9.0, 20.0), np.array([0.2000, 0.4740, 78.6600])), + (("5G", 9.0, 22.0), np.array([0.1850, 0.4870, 78.6600])), + (("5G", 9.0, 24.0), np.array([0.1700, 0.4990, 78.6600])), + (("5G", 9.0, 26.0), np.array([0.1570, 0.5090, 78.6600])), + (("5G", 9.0, 28.0), np.array([0.1430, 0.5190, 78.6600])), + (("7.5G", 9.0, 2.0), np.array([0.2987, 0.3323, 78.6600])), + (("7.5G", 9.0, 4.0), np.array([0.2882, 0.3461, 78.6600])), + (("7.5G", 9.0, 6.0), np.array([0.2763, 0.3607, 78.6600])), + (("7.5G", 9.0, 8.0), np.array([0.2652, 0.3738, 78.6600])), + (("7.5G", 9.0, 10.0), np.array([0.2545, 0.3855, 78.6600])), + (("7.5G", 9.0, 12.0), np.array([0.2419, 0.3985, 78.6600])), + (("7.5G", 9.0, 14.0), np.array([0.2290, 0.4100, 78.6600])), + (("7.5G", 9.0, 16.0), np.array([0.2160, 0.4230, 78.6600])), + (("7.5G", 9.0, 18.0), np.array([0.2010, 0.4360, 78.6600])), + (("7.5G", 9.0, 20.0), np.array([0.1870, 0.4460, 78.6600])), + (("7.5G", 9.0, 22.0), np.array([0.1740, 0.4570, 78.6600])), + (("7.5G", 9.0, 24.0), np.array([0.1600, 0.4670, 78.6600])), + (("10G", 9.0, 2.0), np.array([0.2965, 0.3293, 78.6600])), + (("10G", 9.0, 4.0), np.array([0.2840, 0.3402, 78.6600])), + (("10G", 9.0, 6.0), np.array([0.2703, 0.3513, 78.6600])), + (("10G", 9.0, 8.0), np.array([0.2574, 0.3618, 78.6600])), + (("10G", 9.0, 10.0), np.array([0.2457, 0.3702, 78.6600])), + (("10G", 9.0, 12.0), np.array([0.2325, 0.3796, 78.6600])), + (("10G", 9.0, 14.0), np.array([0.2170, 0.3880, 78.6600])), + (("10G", 9.0, 16.0), np.array([0.2030, 0.3960, 78.6600])), + (("10G", 9.0, 18.0), np.array([0.1880, 0.4050, 78.6600])), + (("10G", 9.0, 20.0), np.array([0.1750, 0.4110, 78.6600])), + (("10G", 9.0, 22.0), np.array([0.1610, 0.4190, 78.6600])), + (("10G", 9.0, 24.0), np.array([0.1470, 0.4250, 78.6600])), + (("2.5BG", 9.0, 2.0), np.array([0.2947, 0.3267, 78.6600])), + (("2.5BG", 9.0, 4.0), np.array([0.2805, 0.3349, 78.6600])), + (("2.5BG", 9.0, 6.0), np.array([0.2652, 0.3433, 78.6600])), + (("2.5BG", 9.0, 8.0), np.array([0.2509, 0.3507, 78.6600])), + (("2.5BG", 9.0, 10.0), np.array([0.2382, 0.3568, 78.6600])), + (("2.5BG", 9.0, 12.0), np.array([0.2220, 0.3640, 78.6600])), + (("2.5BG", 9.0, 14.0), np.array([0.2080, 0.3700, 78.6600])), + (("2.5BG", 9.0, 16.0), np.array([0.1940, 0.3760, 78.6600])), + (("2.5BG", 9.0, 18.0), np.array([0.1780, 0.3820, 78.6600])), + (("2.5BG", 9.0, 20.0), np.array([0.1660, 0.3860, 78.6600])), + (("2.5BG", 9.0, 22.0), np.array([0.1520, 0.3910, 78.6600])), + (("5BG", 9.0, 2.0), np.array([0.2930, 0.3232, 78.6600])), + (("5BG", 9.0, 4.0), np.array([0.2768, 0.3287, 78.6600])), + (("5BG", 9.0, 6.0), np.array([0.2599, 0.3338, 78.6600])), + (("5BG", 9.0, 8.0), np.array([0.2437, 0.3378, 78.6600])), + (("5BG", 9.0, 10.0), np.array([0.2301, 0.3405, 78.6600])), + (("5BG", 9.0, 12.0), np.array([0.2120, 0.3440, 78.6600])), + (("5BG", 9.0, 14.0), np.array([0.1960, 0.3460, 78.6600])), + (("5BG", 9.0, 16.0), np.array([0.1800, 0.3480, 78.6600])), + (("5BG", 9.0, 18.0), np.array([0.1640, 0.3490, 78.6600])), + (("5BG", 9.0, 20.0), np.array([0.1500, 0.3500, 78.6600])), + (("7.5BG", 9.0, 2.0), np.array([0.2911, 0.3188, 78.6600])), + (("7.5BG", 9.0, 4.0), np.array([0.2728, 0.3208, 78.6600])), + (("7.5BG", 9.0, 6.0), np.array([0.2543, 0.3220, 78.6600])), + (("7.5BG", 9.0, 8.0), np.array([0.2361, 0.3225, 78.6600])), + (("7.5BG", 9.0, 10.0), np.array([0.2215, 0.3226, 78.6600])), + (("7.5BG", 9.0, 12.0), np.array([0.2030, 0.3230, 78.6600])), + (("7.5BG", 9.0, 14.0), np.array([0.1870, 0.3230, 78.6600])), + (("7.5BG", 9.0, 16.0), np.array([0.1700, 0.3230, 78.6600])), + (("7.5BG", 9.0, 18.0), np.array([0.1560, 0.3220, 78.6600])), + (("10BG", 9.0, 2.0), np.array([0.2907, 0.3159, 78.6600])), + (("10BG", 9.0, 4.0), np.array([0.2700, 0.3140, 78.6600])), + (("10BG", 9.0, 6.0), np.array([0.2501, 0.3118, 78.6600])), + (("10BG", 9.0, 8.0), np.array([0.2320, 0.3100, 78.6600])), + (("10BG", 9.0, 10.0), np.array([0.2140, 0.3060, 78.6600])), + (("10BG", 9.0, 12.0), np.array([0.1960, 0.3040, 78.6600])), + (("10BG", 9.0, 14.0), np.array([0.1800, 0.3010, 78.6600])), + (("10BG", 9.0, 16.0), np.array([0.1620, 0.2980, 78.6600])), + (("10BG", 9.0, 18.0), np.array([0.1470, 0.2940, 78.6600])), + (("2.5B", 9.0, 2.0), np.array([0.2909, 0.3125, 78.6600])), + (("2.5B", 9.0, 4.0), np.array([0.2680, 0.3073, 78.6600])), + (("2.5B", 9.0, 6.0), np.array([0.2470, 0.3020, 78.6600])), + (("2.5B", 9.0, 8.0), np.array([0.2280, 0.2960, 78.6600])), + (("2.5B", 9.0, 10.0), np.array([0.2110, 0.2900, 78.6600])), + (("2.5B", 9.0, 12.0), np.array([0.1930, 0.2840, 78.6600])), + (("2.5B", 9.0, 14.0), np.array([0.1760, 0.2780, 78.6600])), + (("2.5B", 9.0, 16.0), np.array([0.1580, 0.2710, 78.6600])), + (("5B", 9.0, 2.0), np.array([0.2919, 0.3102, 78.6600])), + (("5B", 9.0, 4.0), np.array([0.2675, 0.3005, 78.6600])), + (("5B", 9.0, 6.0), np.array([0.2460, 0.2900, 78.6600])), + (("5B", 9.0, 8.0), np.array([0.2280, 0.2810, 78.6600])), + (("5B", 9.0, 10.0), np.array([0.2100, 0.2720, 78.6600])), + (("5B", 9.0, 12.0), np.array([0.1910, 0.2600, 78.6600])), + (("5B", 9.0, 14.0), np.array([0.1750, 0.2510, 78.6600])), + (("5B", 9.0, 16.0), np.array([0.1570, 0.2410, 78.6600])), + (("7.5B", 9.0, 2.0), np.array([0.2937, 0.3087, 78.6600])), + (("7.5B", 9.0, 4.0), np.array([0.2688, 0.2961, 78.6600])), + (("7.5B", 9.0, 6.0), np.array([0.2470, 0.2850, 78.6600])), + (("7.5B", 9.0, 8.0), np.array([0.2280, 0.2730, 78.6600])), + (("7.5B", 9.0, 10.0), np.array([0.2110, 0.2620, 78.6600])), + (("7.5B", 9.0, 12.0), np.array([0.1940, 0.2520, 78.6600])), + (("10B", 9.0, 2.0), np.array([0.2949, 0.3076, 78.6600])), + (("10B", 9.0, 4.0), np.array([0.2712, 0.2924, 78.6600])), + (("10B", 9.0, 6.0), np.array([0.2500, 0.2780, 78.6600])), + (("10B", 9.0, 8.0), np.array([0.2330, 0.2650, 78.6600])), + (("10B", 9.0, 10.0), np.array([0.2160, 0.2500, 78.6600])), + (("2.5PB", 9.0, 2.0), np.array([0.2975, 0.3063, 78.6600])), + (("2.5PB", 9.0, 4.0), np.array([0.2760, 0.2890, 78.6600])), + (("2.5PB", 9.0, 6.0), np.array([0.2560, 0.2720, 78.6600])), + (("2.5PB", 9.0, 8.0), np.array([0.2390, 0.2570, 78.6600])), + (("2.5PB", 9.0, 10.0), np.array([0.2240, 0.2430, 78.6600])), + (("5PB", 9.0, 2.0), np.array([0.2991, 0.3057, 78.6600])), + (("5PB", 9.0, 4.0), np.array([0.2810, 0.2870, 78.6600])), + (("5PB", 9.0, 6.0), np.array([0.2620, 0.2680, 78.6600])), + (("5PB", 9.0, 8.0), np.array([0.2470, 0.2520, 78.6600])), + (("5PB", 9.0, 10.0), np.array([0.2320, 0.2350, 78.6600])), + (("7.5PB", 9.0, 2.0), np.array([0.3015, 0.3052, 78.6600])), + (("7.5PB", 9.0, 4.0), np.array([0.2860, 0.2850, 78.6600])), + (("7.5PB", 9.0, 6.0), np.array([0.2710, 0.2660, 78.6600])), + (("7.5PB", 9.0, 8.0), np.array([0.2570, 0.2450, 78.6600])), + (("7.5PB", 9.0, 10.0), np.array([0.2470, 0.2270, 78.6600])), + (("10PB", 9.0, 2.0), np.array([0.3038, 0.3054, 78.6600])), + (("10PB", 9.0, 4.0), np.array([0.2910, 0.2850, 78.6600])), + (("10PB", 9.0, 6.0), np.array([0.2780, 0.2660, 78.6600])), + (("10PB", 9.0, 8.0), np.array([0.2670, 0.2450, 78.6600])), + (("10PB", 9.0, 10.0), np.array([0.2560, 0.2260, 78.6600])), + (("2.5P", 9.0, 2.0), np.array([0.3050, 0.3051, 78.6600])), + (("2.5P", 9.0, 4.0), np.array([0.2963, 0.2865, 78.6600])), + (("2.5P", 9.0, 6.0), np.array([0.2880, 0.2680, 78.6600])), + (("2.5P", 9.0, 8.0), np.array([0.2800, 0.2490, 78.6600])), + (("2.5P", 9.0, 10.0), np.array([0.2730, 0.2300, 78.6600])), + (("5P", 9.0, 2.0), np.array([0.3067, 0.3060, 78.6600])), + (("5P", 9.0, 4.0), np.array([0.3003, 0.2870, 78.6600])), + (("5P", 9.0, 6.0), np.array([0.2960, 0.2710, 78.6600])), + (("5P", 9.0, 8.0), np.array([0.2920, 0.2530, 78.6600])), + (("5P", 9.0, 10.0), np.array([0.2860, 0.2350, 78.6600])), + (("5P", 9.0, 12.0), np.array([0.2840, 0.2210, 78.6600])), + (("5P", 9.0, 14.0), np.array([0.2810, 0.2080, 78.6600])), + (("7.5P", 9.0, 2.0), np.array([0.3107, 0.3081, 78.6600])), + (("7.5P", 9.0, 4.0), np.array([0.3117, 0.2928, 78.6600])), + (("7.5P", 9.0, 6.0), np.array([0.3120, 0.2788, 78.6600])), + (("7.5P", 9.0, 8.0), np.array([0.3110, 0.2620, 78.6600])), + (("7.5P", 9.0, 10.0), np.array([0.3100, 0.2470, 78.6600])), + (("7.5P", 9.0, 12.0), np.array([0.3080, 0.2330, 78.6600])), + (("7.5P", 9.0, 14.0), np.array([0.3060, 0.2200, 78.6600])), + (("7.5P", 9.0, 16.0), np.array([0.3050, 0.2080, 78.6600])), + (("10P", 9.0, 2.0), np.array([0.3128, 0.3094, 78.6600])), + (("10P", 9.0, 4.0), np.array([0.3176, 0.2966, 78.6600])), + (("10P", 9.0, 6.0), np.array([0.3218, 0.2845, 78.6600])), + (("10P", 9.0, 8.0), np.array([0.3260, 0.2700, 78.6600])), + (("10P", 9.0, 10.0), np.array([0.3300, 0.2570, 78.6600])), + (("10P", 9.0, 12.0), np.array([0.3340, 0.2460, 78.6600])), + (("10P", 9.0, 14.0), np.array([0.3360, 0.2350, 78.6600])), + (("10P", 9.0, 16.0), np.array([0.3390, 0.2240, 78.6600])), + (("2.5RP", 9.0, 2.0), np.array([0.3149, 0.3108, 78.6600])), + (("2.5RP", 9.0, 4.0), np.array([0.3234, 0.3010, 78.6600])), + (("2.5RP", 9.0, 6.0), np.array([0.3322, 0.2910, 78.6600])), + (("2.5RP", 9.0, 8.0), np.array([0.3400, 0.2800, 78.6600])), + (("2.5RP", 9.0, 10.0), np.array([0.3490, 0.2710, 78.6600])), + (("2.5RP", 9.0, 12.0), np.array([0.3560, 0.2610, 78.6600])), + (("2.5RP", 9.0, 14.0), np.array([0.3640, 0.2510, 78.6600])), + (("2.5RP", 9.0, 16.0), np.array([0.3710, 0.2430, 78.6600])), + (("5RP", 9.0, 2.0), np.array([0.3172, 0.3126, 78.6600])), + (("5RP", 9.0, 4.0), np.array([0.3301, 0.3060, 78.6600])), + (("5RP", 9.0, 6.0), np.array([0.3431, 0.2988, 78.6600])), + (("5RP", 9.0, 8.0), np.array([0.3550, 0.2920, 78.6600])), + (("5RP", 9.0, 10.0), np.array([0.3680, 0.2840, 78.6600])), + (("5RP", 9.0, 12.0), np.array([0.3800, 0.2760, 78.6600])), + (("5RP", 9.0, 14.0), np.array([0.3890, 0.2690, 78.6600])), + (("5RP", 9.0, 16.0), np.array([0.4000, 0.2630, 78.6600])), + (("7.5RP", 9.0, 2.0), np.array([0.3190, 0.3141, 78.6600])), + (("7.5RP", 9.0, 4.0), np.array([0.3350, 0.3099, 78.6600])), + (("7.5RP", 9.0, 6.0), np.array([0.3512, 0.3052, 78.6600])), + (("7.5RP", 9.0, 8.0), np.array([0.3640, 0.3000, 78.6600])), + (("7.5RP", 9.0, 10.0), np.array([0.3820, 0.2950, 78.6600])), + (("7.5RP", 9.0, 12.0), np.array([0.3970, 0.2890, 78.6600])), + (("7.5RP", 9.0, 14.0), np.array([0.4100, 0.2840, 78.6600])), + (("10RP", 9.0, 2.0), np.array([0.3205, 0.3155, 78.6600])), + (("10RP", 9.0, 4.0), np.array([0.3400, 0.3140, 78.6600])), + (("10RP", 9.0, 6.0), np.array([0.3590, 0.3118, 78.6600])), + (("10RP", 9.0, 8.0), np.array([0.3760, 0.3100, 78.6600])), + (("10RP", 9.0, 10.0), np.array([0.3940, 0.3060, 78.6600])), + (("10RP", 9.0, 12.0), np.array([0.4140, 0.3020, 78.6600])), + (("10RP", 9.0, 14.0), np.array([0.4310, 0.2980, 78.6600])), + (("2.5R", 9.0, 2.0), np.array([0.3220, 0.3168, 78.6600])), + (("2.5R", 9.0, 4.0), np.array([0.3445, 0.3179, 78.6600])), + (("2.5R", 9.0, 6.0), np.array([0.3665, 0.3183, 78.6600])), + (("2.5R", 9.0, 8.0), np.array([0.3840, 0.3180, 78.6600])), + (("2.5R", 9.0, 10.0), np.array([0.4060, 0.3160, 78.6600])), + (("2.5R", 9.0, 12.0), np.array([0.4290, 0.3140, 78.6600])), + (("2.5R", 9.0, 14.0), np.array([0.4480, 0.3110, 78.6600])), + (("5R", 9.0, 2.0), np.array([0.3240, 0.3188, 78.6600])), + (("5R", 9.0, 4.0), np.array([0.3495, 0.3226, 78.6600])), + (("5R", 9.0, 6.0), np.array([0.3734, 0.3256, 78.6600])), + (("5R", 9.0, 8.0), np.array([0.3950, 0.3260, 78.6600])), + (("5R", 9.0, 10.0), np.array([0.4180, 0.3260, 78.6600])), + (("5R", 9.0, 12.0), np.array([0.4440, 0.3250, 78.6600])), + (("7.5R", 9.0, 2.0), np.array([0.3263, 0.3210, 78.6600])), + (("7.5R", 9.0, 4.0), np.array([0.3551, 0.3283, 78.6600])), + (("7.5R", 9.0, 6.0), np.array([0.3812, 0.3348, 78.6600])), + (("7.5R", 9.0, 8.0), np.array([0.4070, 0.3380, 78.6600])), + (("7.5R", 9.0, 10.0), np.array([0.4310, 0.3400, 78.6600])), + (("7.5R", 9.0, 12.0), np.array([0.4570, 0.3400, 78.6600])), + (("7.5R", 9.0, 14.0), np.array([0.4800, 0.3400, 78.6600])), + (("10R", 9.0, 2.0), np.array([0.3284, 0.3233, 78.6600])), + (("10R", 9.0, 4.0), np.array([0.3600, 0.3348, 78.6600])), + (("10R", 9.0, 6.0), np.array([0.3880, 0.3439, 78.6600])), + (("10R", 9.0, 8.0), np.array([0.4160, 0.3500, 78.6600])), + (("10R", 9.0, 10.0), np.array([0.4410, 0.3560, 78.6600])), + (("10R", 9.0, 12.0), np.array([0.4650, 0.3610, 78.6600])), + (("10R", 9.0, 14.0), np.array([0.4900, 0.3640, 78.6600])), + (("10R", 9.0, 16.0), np.array([0.5100, 0.3680, 78.6600])), + (("2.5YR", 9.0, 2.0), np.array([0.3320, 0.3273, 78.6600])), + (("2.5YR", 9.0, 4.0), np.array([0.3641, 0.3422, 78.6600])), + (("2.5YR", 9.0, 6.0), np.array([0.3927, 0.3550, 78.6600])), + (("2.5YR", 9.0, 8.0), np.array([0.4220, 0.3650, 78.6600])), + (("2.5YR", 9.0, 10.0), np.array([0.4460, 0.3730, 78.6600])), + (("2.5YR", 9.0, 12.0), np.array([0.4690, 0.3800, 78.6600])), + (("2.5YR", 9.0, 14.0), np.array([0.4920, 0.3870, 78.6600])), + (("2.5YR", 9.0, 16.0), np.array([0.5130, 0.3920, 78.6600])), + (("2.5YR", 9.0, 18.0), np.array([0.5320, 0.3960, 78.6600])), + (("5YR", 9.0, 2.0), np.array([0.3353, 0.3325, 78.6600])), + (("5YR", 9.0, 4.0), np.array([0.3668, 0.3509, 78.6600])), + (("5YR", 9.0, 6.0), np.array([0.3948, 0.3659, 78.6600])), + (("5YR", 9.0, 8.0), np.array([0.4220, 0.3790, 78.6600])), + (("5YR", 9.0, 10.0), np.array([0.4480, 0.3900, 78.6600])), + (("5YR", 9.0, 12.0), np.array([0.4700, 0.4000, 78.6600])), + (("5YR", 9.0, 14.0), np.array([0.4910, 0.4080, 78.6600])), + (("5YR", 9.0, 16.0), np.array([0.5100, 0.4160, 78.6600])), + (("5YR", 9.0, 18.0), np.array([0.5250, 0.4210, 78.6600])), + (("5YR", 9.0, 20.0), np.array([0.5410, 0.4260, 78.6600])), + (("5YR", 9.0, 22.0), np.array([0.5500, 0.4300, 78.6600])), + (("5YR", 9.0, 24.0), np.array([0.5590, 0.4330, 78.6600])), + (("5YR", 9.0, 26.0), np.array([0.5670, 0.4350, 78.6600])), + (("7.5YR", 9.0, 2.0), np.array([0.3380, 0.3377, 78.6600])), + (("7.5YR", 9.0, 4.0), np.array([0.3679, 0.3585, 78.6600])), + (("7.5YR", 9.0, 6.0), np.array([0.3950, 0.3763, 78.6600])), + (("7.5YR", 9.0, 8.0), np.array([0.4220, 0.3930, 78.6600])), + (("7.5YR", 9.0, 10.0), np.array([0.4470, 0.4070, 78.6600])), + (("7.5YR", 9.0, 12.0), np.array([0.4680, 0.4180, 78.6600])), + (("7.5YR", 9.0, 14.0), np.array([0.4860, 0.4280, 78.6600])), + (("7.5YR", 9.0, 16.0), np.array([0.5030, 0.4360, 78.6600])), + (("7.5YR", 9.0, 18.0), np.array([0.5170, 0.4440, 78.6600])), + (("7.5YR", 9.0, 20.0), np.array([0.5290, 0.4500, 78.6600])), + (("7.5YR", 9.0, 22.0), np.array([0.5370, 0.4540, 78.6600])), + (("7.5YR", 9.0, 24.0), np.array([0.5450, 0.4590, 78.6600])), + (("7.5YR", 9.0, 26.0), np.array([0.5530, 0.4630, 78.6600])), + (("10YR", 9.0, 2.0), np.array([0.3392, 0.3430, 78.6600])), + (("10YR", 9.0, 4.0), np.array([0.3677, 0.3668, 78.6600])), + (("10YR", 9.0, 6.0), np.array([0.3941, 0.3877, 78.6600])), + (("10YR", 9.0, 8.0), np.array([0.4199, 0.4069, 78.6600])), + (("10YR", 9.0, 10.0), np.array([0.4440, 0.4240, 78.6600])), + (("10YR", 9.0, 12.0), np.array([0.4630, 0.4360, 78.6600])), + (("10YR", 9.0, 14.0), np.array([0.4800, 0.4470, 78.6600])), + (("10YR", 9.0, 16.0), np.array([0.4950, 0.4550, 78.6600])), + (("10YR", 9.0, 18.0), np.array([0.5060, 0.4620, 78.6600])), + (("10YR", 9.0, 20.0), np.array([0.5160, 0.4680, 78.6600])), + (("10YR", 9.0, 22.0), np.array([0.5240, 0.4730, 78.6600])), + (("10YR", 9.0, 24.0), np.array([0.5320, 0.4780, 78.6600])), + (("2.5Y", 9.0, 2.0), np.array([0.3390, 0.3472, 78.6600])), + (("2.5Y", 9.0, 4.0), np.array([0.3655, 0.3738, 78.6600])), + (("2.5Y", 9.0, 6.0), np.array([0.3910, 0.3972, 78.6600])), + (("2.5Y", 9.0, 8.0), np.array([0.4154, 0.4186, 78.6600])), + (("2.5Y", 9.0, 10.0), np.array([0.4370, 0.4369, 78.6600])), + (("2.5Y", 9.0, 12.0), np.array([0.4569, 0.4527, 78.6600])), + (("2.5Y", 9.0, 14.0), np.array([0.4730, 0.4650, 78.6600])), + (("2.5Y", 9.0, 16.0), np.array([0.4860, 0.4760, 78.6600])), + (("2.5Y", 9.0, 18.0), np.array([0.4940, 0.4830, 78.6600])), + (("2.5Y", 9.0, 20.0), np.array([0.5010, 0.4880, 78.6600])), + (("2.5Y", 9.0, 22.0), np.array([0.5090, 0.4940, 78.6600])), + (("2.5Y", 9.0, 24.0), np.array([0.5150, 0.4990, 78.6600])), + (("5Y", 9.0, 2.0), np.array([0.3378, 0.3504, 78.6600])), + (("5Y", 9.0, 4.0), np.array([0.3621, 0.3799, 78.6600])), + (("5Y", 9.0, 6.0), np.array([0.3858, 0.4071, 78.6600])), + (("5Y", 9.0, 8.0), np.array([0.4080, 0.4319, 78.6600])), + (("5Y", 9.0, 10.0), np.array([0.4275, 0.4529, 78.6600])), + (("5Y", 9.0, 12.0), np.array([0.4455, 0.4719, 78.6600])), + (("5Y", 9.0, 14.0), np.array([0.4602, 0.4869, 78.6600])), + (("5Y", 9.0, 16.0), np.array([0.4711, 0.4977, 78.6600])), + (("5Y", 9.0, 18.0), np.array([0.4782, 0.5049, 78.6600])), + (("5Y", 9.0, 20.0), np.array([0.4830, 0.5092, 78.6600])), + (("5Y", 9.0, 22.0), np.array([0.4890, 0.5150, 78.6600])), + (("5Y", 9.0, 24.0), np.array([0.4950, 0.5220, 78.6600])), + (("7.5Y", 9.0, 2.0), np.array([0.3365, 0.3527, 78.6600])), + (("7.5Y", 9.0, 4.0), np.array([0.3591, 0.3832, 78.6600])), + (("7.5Y", 9.0, 6.0), np.array([0.3811, 0.4123, 78.6600])), + (("7.5Y", 9.0, 8.0), np.array([0.4019, 0.4392, 78.6600])), + (("7.5Y", 9.0, 10.0), np.array([0.4201, 0.4622, 78.6600])), + (("7.5Y", 9.0, 12.0), np.array([0.4369, 0.4829, 78.6600])), + (("7.5Y", 9.0, 14.0), np.array([0.4503, 0.4993, 78.6600])), + (("7.5Y", 9.0, 16.0), np.array([0.4595, 0.5104, 78.6600])), + (("7.5Y", 9.0, 18.0), np.array([0.4663, 0.5188, 78.6600])), + (("7.5Y", 9.0, 20.0), np.array([0.4710, 0.5230, 78.6600])), + (("7.5Y", 9.0, 22.0), np.array([0.4760, 0.5280, 78.6600])), + (("10Y", 10.0, 2.0), np.array([0.3340, 0.3520, 102.5700])), + (("10Y", 10.0, 4.0), np.array([0.3550, 0.3820, 102.5700])), + (("10Y", 10.0, 6.0), np.array([0.3710, 0.4090, 102.5700])), + (("10Y", 10.0, 8.0), np.array([0.3900, 0.4370, 102.5700])), + (("10Y", 10.0, 10.0), np.array([0.4050, 0.4600, 102.5700])), + (("10Y", 10.0, 12.0), np.array([0.4200, 0.4810, 102.5700])), + (("10Y", 10.0, 14.0), np.array([0.4330, 0.5020, 102.5700])), + (("10Y", 10.0, 16.0), np.array([0.4420, 0.5150, 102.5700])), + (("10Y", 10.0, 18.0), np.array([0.4500, 0.5270, 102.5700])), + (("10Y", 10.0, 20.0), np.array([0.4550, 0.5340, 102.5700])), + (("10Y", 10.0, 22.0), np.array([0.4600, 0.5410, 102.5700])), + (("2.5GY", 10.0, 2.0), np.array([0.3320, 0.3540, 102.5700])), + (("2.5GY", 10.0, 4.0), np.array([0.3480, 0.3840, 102.5700])), + (("2.5GY", 10.0, 6.0), np.array([0.3650, 0.4130, 102.5700])), + (("2.5GY", 10.0, 8.0), np.array([0.3800, 0.4420, 102.5700])), + (("2.5GY", 10.0, 10.0), np.array([0.3930, 0.4660, 102.5700])), + (("2.5GY", 10.0, 12.0), np.array([0.4060, 0.4920, 102.5700])), + (("2.5GY", 10.0, 14.0), np.array([0.4160, 0.5140, 102.5700])), + (("2.5GY", 10.0, 16.0), np.array([0.4240, 0.5290, 102.5700])), + (("2.5GY", 10.0, 18.0), np.array([0.4320, 0.5460, 102.5700])), + (("2.5GY", 10.0, 20.0), np.array([0.4360, 0.5520, 102.5700])), + (("2.5GY", 10.0, 22.0), np.array([0.4400, 0.5600, 102.5700])), + (("5GY", 10.0, 2.0), np.array([0.3280, 0.3540, 102.5700])), + (("5GY", 10.0, 4.0), np.array([0.3430, 0.3840, 102.5700])), + (("5GY", 10.0, 6.0), np.array([0.3560, 0.4140, 102.5700])), + (("5GY", 10.0, 8.0), np.array([0.3700, 0.4460, 102.5700])), + (("5GY", 10.0, 10.0), np.array([0.3800, 0.4700, 102.5700])), + (("5GY", 10.0, 12.0), np.array([0.3910, 0.4970, 102.5700])), + (("5GY", 10.0, 14.0), np.array([0.3980, 0.5180, 102.5700])), + (("5GY", 10.0, 16.0), np.array([0.4050, 0.5380, 102.5700])), + (("5GY", 10.0, 18.0), np.array([0.4100, 0.5600, 102.5700])), + (("5GY", 10.0, 20.0), np.array([0.4130, 0.5720, 102.5700])), + (("5GY", 10.0, 22.0), np.array([0.4160, 0.5800, 102.5700])), + (("7.5GY", 10.0, 2.0), np.array([0.3200, 0.3510, 102.5700])), + (("7.5GY", 10.0, 4.0), np.array([0.3270, 0.3780, 102.5700])), + (("7.5GY", 10.0, 6.0), np.array([0.3340, 0.4100, 102.5700])), + (("7.5GY", 10.0, 8.0), np.array([0.3420, 0.4390, 102.5700])), + (("7.5GY", 10.0, 10.0), np.array([0.3470, 0.4700, 102.5700])), + (("7.5GY", 10.0, 12.0), np.array([0.3510, 0.4930, 102.5700])), + (("7.5GY", 10.0, 14.0), np.array([0.3540, 0.5180, 102.5700])), + (("7.5GY", 10.0, 16.0), np.array([0.3570, 0.5450, 102.5700])), + (("7.5GY", 10.0, 18.0), np.array([0.3600, 0.5710, 102.5700])), + (("7.5GY", 10.0, 20.0), np.array([0.3610, 0.5920, 102.5700])), + (("7.5GY", 10.0, 22.0), np.array([0.3600, 0.6080, 102.5700])), + (("10GY", 10.0, 2.0), np.array([0.3120, 0.3460, 102.5700])), + (("10GY", 10.0, 4.0), np.array([0.3140, 0.3700, 102.5700])), + (("10GY", 10.0, 6.0), np.array([0.3140, 0.4000, 102.5700])), + (("10GY", 10.0, 8.0), np.array([0.3140, 0.4230, 102.5700])), + (("10GY", 10.0, 10.0), np.array([0.3140, 0.4540, 102.5700])), + (("10GY", 10.0, 12.0), np.array([0.3140, 0.4740, 102.5700])), + (("10GY", 10.0, 14.0), np.array([0.3130, 0.5000, 102.5700])), + (("10GY", 10.0, 16.0), np.array([0.3100, 0.5270, 102.5700])), + (("10GY", 10.0, 18.0), np.array([0.3070, 0.5560, 102.5700])), + (("10GY", 10.0, 20.0), np.array([0.3030, 0.5800, 102.5700])), + (("10GY", 10.0, 22.0), np.array([0.2990, 0.6000, 102.5700])), + (("2.5G", 10.0, 2.0), np.array([0.3060, 0.3420, 102.5700])), + (("2.5G", 10.0, 4.0), np.array([0.3030, 0.3610, 102.5700])), + (("2.5G", 10.0, 6.0), np.array([0.2980, 0.3850, 102.5700])), + (("2.5G", 10.0, 8.0), np.array([0.2930, 0.4040, 102.5700])), + (("2.5G", 10.0, 10.0), np.array([0.2860, 0.4270, 102.5700])), + (("2.5G", 10.0, 12.0), np.array([0.2820, 0.4440, 102.5700])), + (("2.5G", 10.0, 14.0), np.array([0.2760, 0.4670, 102.5700])), + (("2.5G", 10.0, 16.0), np.array([0.2700, 0.4900, 102.5700])), + (("2.5G", 10.0, 18.0), np.array([0.2620, 0.5170, 102.5700])), + (("2.5G", 10.0, 20.0), np.array([0.2540, 0.5430, 102.5700])), + (("2.5G", 10.0, 22.0), np.array([0.2480, 0.5630, 102.5700])), + (("5G", 10.0, 2.0), np.array([0.3010, 0.3370, 102.5700])), + (("5G", 10.0, 4.0), np.array([0.2930, 0.3530, 102.5700])), + (("5G", 10.0, 6.0), np.array([0.2830, 0.3690, 102.5700])), + (("5G", 10.0, 8.0), np.array([0.2740, 0.3840, 102.5700])), + (("5G", 10.0, 10.0), np.array([0.2650, 0.3980, 102.5700])), + (("5G", 10.0, 12.0), np.array([0.2560, 0.4120, 102.5700])), + (("5G", 10.0, 14.0), np.array([0.2450, 0.4260, 102.5700])), + (("5G", 10.0, 16.0), np.array([0.2350, 0.4400, 102.5700])), + (("5G", 10.0, 18.0), np.array([0.2220, 0.4570, 102.5700])), + (("7.5G", 10.0, 2.0), np.array([0.2980, 0.3330, 102.5700])), + (("7.5G", 10.0, 4.0), np.array([0.2880, 0.3460, 102.5700])), + (("7.5G", 10.0, 6.0), np.array([0.2770, 0.3600, 102.5700])), + (("7.5G", 10.0, 8.0), np.array([0.2660, 0.3740, 102.5700])), + (("7.5G", 10.0, 10.0), np.array([0.2560, 0.3850, 102.5700])), + (("7.5G", 10.0, 12.0), np.array([0.2460, 0.3970, 102.5700])), + (("7.5G", 10.0, 14.0), np.array([0.2350, 0.4090, 102.5700])), + (("7.5G", 10.0, 16.0), np.array([0.2240, 0.4200, 102.5700])), + (("10G", 10.0, 2.0), np.array([0.2960, 0.3310, 102.5700])), + (("10G", 10.0, 4.0), np.array([0.2840, 0.3410, 102.5700])), + (("10G", 10.0, 6.0), np.array([0.2710, 0.3510, 102.5700])), + (("10G", 10.0, 8.0), np.array([0.2580, 0.3610, 102.5700])), + (("10G", 10.0, 10.0), np.array([0.2480, 0.3690, 102.5700])), + (("10G", 10.0, 12.0), np.array([0.2350, 0.3780, 102.5700])), + (("10G", 10.0, 14.0), np.array([0.2230, 0.3870, 102.5700])), + (("2.5BG", 10.0, 2.0), np.array([0.2940, 0.3270, 102.5700])), + (("2.5BG", 10.0, 4.0), np.array([0.2800, 0.3350, 102.5700])), + (("2.5BG", 10.0, 6.0), np.array([0.2650, 0.3430, 102.5700])), + (("2.5BG", 10.0, 8.0), np.array([0.2510, 0.3500, 102.5700])), + (("2.5BG", 10.0, 10.0), np.array([0.2410, 0.3570, 102.5700])), + (("2.5BG", 10.0, 12.0), np.array([0.2270, 0.3620, 102.5700])), + (("2.5BG", 10.0, 14.0), np.array([0.2130, 0.3680, 102.5700])), + (("5BG", 10.0, 2.0), np.array([0.2930, 0.3230, 102.5700])), + (("5BG", 10.0, 4.0), np.array([0.2770, 0.3300, 102.5700])), + (("5BG", 10.0, 6.0), np.array([0.2620, 0.3350, 102.5700])), + (("5BG", 10.0, 8.0), np.array([0.2460, 0.3400, 102.5700])), + (("5BG", 10.0, 10.0), np.array([0.2330, 0.3420, 102.5700])), + (("5BG", 10.0, 12.0), np.array([0.2200, 0.3450, 102.5700])), + (("5BG", 10.0, 14.0), np.array([0.2030, 0.3480, 102.5700])), + (("7.5BG", 10.0, 2.0), np.array([0.2930, 0.3180, 102.5700])), + (("7.5BG", 10.0, 4.0), np.array([0.2730, 0.3200, 102.5700])), + (("7.5BG", 10.0, 6.0), np.array([0.2560, 0.3210, 102.5700])), + (("7.5BG", 10.0, 8.0), np.array([0.2380, 0.3220, 102.5700])), + (("7.5BG", 10.0, 10.0), np.array([0.2240, 0.3220, 102.5700])), + (("7.5BG", 10.0, 12.0), np.array([0.2090, 0.3220, 102.5700])), + (("10BG", 10.0, 2.0), np.array([0.2930, 0.3150, 102.5700])), + (("10BG", 10.0, 4.0), np.array([0.2700, 0.3130, 102.5700])), + (("10BG", 10.0, 6.0), np.array([0.2510, 0.3110, 102.5700])), + (("10BG", 10.0, 8.0), np.array([0.2340, 0.3100, 102.5700])), + (("10BG", 10.0, 10.0), np.array([0.2190, 0.3080, 102.5700])), + (("10BG", 10.0, 12.0), np.array([0.2030, 0.3050, 102.5700])), + (("2.5B", 10.0, 2.0), np.array([0.2930, 0.3130, 102.5700])), + (("2.5B", 10.0, 4.0), np.array([0.2700, 0.3080, 102.5700])), + (("2.5B", 10.0, 6.0), np.array([0.2490, 0.3040, 102.5700])), + (("2.5B", 10.0, 8.0), np.array([0.2320, 0.2990, 102.5700])), + (("2.5B", 10.0, 10.0), np.array([0.2150, 0.2930, 102.5700])), + (("5B", 10.0, 2.0), np.array([0.2940, 0.3120, 102.5700])), + (("5B", 10.0, 4.0), np.array([0.2680, 0.3020, 102.5700])), + (("5B", 10.0, 6.0), np.array([0.2480, 0.2960, 102.5700])), + (("5B", 10.0, 8.0), np.array([0.2300, 0.2880, 102.5700])), + (("7.5B", 10.0, 2.0), np.array([0.2950, 0.3100, 102.5700])), + (("7.5B", 10.0, 4.0), np.array([0.2690, 0.2980, 102.5700])), + (("7.5B", 10.0, 6.0), np.array([0.2490, 0.2870, 102.5700])), + (("10B", 10.0, 2.0), np.array([0.2960, 0.3090, 102.5700])), + (("10B", 10.0, 4.0), np.array([0.2720, 0.2940, 102.5700])), + (("10B", 10.0, 6.0), np.array([0.2510, 0.2800, 102.5700])), + (("2.5PB", 10.0, 2.0), np.array([0.2980, 0.3070, 102.5700])), + (("2.5PB", 10.0, 4.0), np.array([0.2780, 0.2900, 102.5700])), + (("2.5PB", 10.0, 6.0), np.array([0.2600, 0.2740, 102.5700])), + (("5PB", 10.0, 2.0), np.array([0.2990, 0.3070, 102.5700])), + (("5PB", 10.0, 4.0), np.array([0.2820, 0.2880, 102.5700])), + (("5PB", 10.0, 6.0), np.array([0.2650, 0.2700, 102.5700])), + (("7.5PB", 10.0, 2.0), np.array([0.3020, 0.3070, 102.5700])), + (("7.5PB", 10.0, 4.0), np.array([0.2860, 0.2860, 102.5700])), + (("7.5PB", 10.0, 6.0), np.array([0.2730, 0.2680, 102.5700])), + (("10PB", 10.0, 2.0), np.array([0.3030, 0.3070, 102.5700])), + (("10PB", 10.0, 4.0), np.array([0.2910, 0.2860, 102.5700])), + (("10PB", 10.0, 6.0), np.array([0.2800, 0.2680, 102.5700])), + (("2.5P", 10.0, 2.0), np.array([0.3050, 0.3070, 102.5700])), + (("2.5P", 10.0, 4.0), np.array([0.2960, 0.2880, 102.5700])), + (("2.5P", 10.0, 6.0), np.array([0.2890, 0.2700, 102.5700])), + (("5P", 10.0, 2.0), np.array([0.3070, 0.3070, 102.5700])), + (("5P", 10.0, 4.0), np.array([0.3020, 0.2890, 102.5700])), + (("5P", 10.0, 6.0), np.array([0.2980, 0.2740, 102.5700])), + (("5P", 10.0, 8.0), np.array([0.2940, 0.2580, 102.5700])), + (("7.5P", 10.0, 2.0), np.array([0.3100, 0.3080, 102.5700])), + (("7.5P", 10.0, 4.0), np.array([0.3110, 0.2940, 102.5700])), + (("7.5P", 10.0, 6.0), np.array([0.3110, 0.2800, 102.5700])), + (("7.5P", 10.0, 8.0), np.array([0.3110, 0.2660, 102.5700])), + (("10P", 10.0, 2.0), np.array([0.3130, 0.3090, 102.5700])), + (("10P", 10.0, 4.0), np.array([0.3170, 0.2970, 102.5700])), + (("10P", 10.0, 6.0), np.array([0.3210, 0.2840, 102.5700])), + (("10P", 10.0, 8.0), np.array([0.3260, 0.2730, 102.5700])), + (("10P", 10.0, 10.0), np.array([0.3280, 0.2640, 102.5700])), + (("2.5RP", 10.0, 2.0), np.array([0.3150, 0.3110, 102.5700])), + (("2.5RP", 10.0, 4.0), np.array([0.3230, 0.3010, 102.5700])), + (("2.5RP", 10.0, 6.0), np.array([0.3320, 0.2910, 102.5700])), + (("2.5RP", 10.0, 8.0), np.array([0.3390, 0.2820, 102.5700])), + (("2.5RP", 10.0, 10.0), np.array([0.3440, 0.2750, 102.5700])), + (("5RP", 10.0, 2.0), np.array([0.3180, 0.3130, 102.5700])), + (("5RP", 10.0, 4.0), np.array([0.3320, 0.3070, 102.5700])), + (("5RP", 10.0, 6.0), np.array([0.3450, 0.3000, 102.5700])), + (("5RP", 10.0, 8.0), np.array([0.3550, 0.2930, 102.5700])), + (("5RP", 10.0, 10.0), np.array([0.3630, 0.2880, 102.5700])), + (("7.5RP", 10.0, 2.0), np.array([0.3200, 0.3140, 102.5700])), + (("7.5RP", 10.0, 4.0), np.array([0.3350, 0.3100, 102.5700])), + (("7.5RP", 10.0, 6.0), np.array([0.3510, 0.3050, 102.5700])), + (("7.5RP", 10.0, 8.0), np.array([0.3650, 0.3010, 102.5700])), + (("10RP", 10.0, 2.0), np.array([0.3220, 0.3160, 102.5700])), + (("10RP", 10.0, 4.0), np.array([0.3420, 0.3150, 102.5700])), + (("10RP", 10.0, 6.0), np.array([0.3610, 0.3140, 102.5700])), + (("10RP", 10.0, 8.0), np.array([0.3780, 0.3110, 102.5700])), + (("2.5R", 10.0, 2.0), np.array([0.3240, 0.3180, 102.5700])), + (("2.5R", 10.0, 4.0), np.array([0.3450, 0.3180, 102.5700])), + (("2.5R", 10.0, 6.0), np.array([0.3660, 0.3180, 102.5700])), + (("2.5R", 10.0, 8.0), np.array([0.3850, 0.3180, 102.5700])), + (("5R", 10.0, 2.0), np.array([0.3260, 0.3200, 102.5700])), + (("5R", 10.0, 4.0), np.array([0.3480, 0.3220, 102.5700])), + (("5R", 10.0, 6.0), np.array([0.3720, 0.3250, 102.5700])), + (("5R", 10.0, 8.0), np.array([0.3920, 0.3260, 102.5700])), + (("7.5R", 10.0, 2.0), np.array([0.3290, 0.3220, 102.5700])), + (("7.5R", 10.0, 4.0), np.array([0.3540, 0.3290, 102.5700])), + (("7.5R", 10.0, 6.0), np.array([0.3800, 0.3340, 102.5700])), + (("7.5R", 10.0, 8.0), np.array([0.4020, 0.3370, 102.5700])), + (("10R", 10.0, 2.0), np.array([0.3320, 0.3260, 102.5700])), + (("10R", 10.0, 4.0), np.array([0.3590, 0.3360, 102.5700])), + (("10R", 10.0, 6.0), np.array([0.3860, 0.3440, 102.5700])), + (("10R", 10.0, 8.0), np.array([0.4110, 0.3490, 102.5700])), + (("2.5YR", 10.0, 2.0), np.array([0.3340, 0.3290, 102.5700])), + (("2.5YR", 10.0, 4.0), np.array([0.3620, 0.3420, 102.5700])), + (("2.5YR", 10.0, 6.0), np.array([0.3890, 0.3540, 102.5700])), + (("2.5YR", 10.0, 8.0), np.array([0.4150, 0.3630, 102.5700])), + (("5YR", 10.0, 2.0), np.array([0.3360, 0.3330, 102.5700])), + (("5YR", 10.0, 4.0), np.array([0.3640, 0.3500, 102.5700])), + (("5YR", 10.0, 6.0), np.array([0.3910, 0.3650, 102.5700])), + (("5YR", 10.0, 8.0), np.array([0.4160, 0.3780, 102.5700])), + (("5YR", 10.0, 10.0), np.array([0.4400, 0.3880, 102.5700])), + (("7.5YR", 10.0, 2.0), np.array([0.3370, 0.3380, 102.5700])), + (("7.5YR", 10.0, 4.0), np.array([0.3660, 0.3570, 102.5700])), + (("7.5YR", 10.0, 6.0), np.array([0.3910, 0.3750, 102.5700])), + (("7.5YR", 10.0, 8.0), np.array([0.4150, 0.3910, 102.5700])), + (("7.5YR", 10.0, 10.0), np.array([0.4390, 0.4050, 102.5700])), + (("7.5YR", 10.0, 12.0), np.array([0.4550, 0.4160, 102.5700])), + (("10YR", 10.0, 2.0), np.array([0.3380, 0.3430, 102.5700])), + (("10YR", 10.0, 4.0), np.array([0.3660, 0.3670, 102.5700])), + (("10YR", 10.0, 6.0), np.array([0.3890, 0.3850, 102.5700])), + (("10YR", 10.0, 8.0), np.array([0.4120, 0.4020, 102.5700])), + (("10YR", 10.0, 10.0), np.array([0.4350, 0.4200, 102.5700])), + (("10YR", 10.0, 12.0), np.array([0.4520, 0.4310, 102.5700])), + (("10YR", 10.0, 14.0), np.array([0.4670, 0.4420, 102.5700])), + (("10YR", 10.0, 16.0), np.array([0.4830, 0.4530, 102.5700])), + (("2.5Y", 10.0, 2.0), np.array([0.3370, 0.3470, 102.5700])), + (("2.5Y", 10.0, 4.0), np.array([0.3630, 0.3730, 102.5700])), + (("2.5Y", 10.0, 6.0), np.array([0.3850, 0.3950, 102.5700])), + (("2.5Y", 10.0, 8.0), np.array([0.4070, 0.4130, 102.5700])), + (("2.5Y", 10.0, 10.0), np.array([0.4280, 0.4310, 102.5700])), + (("2.5Y", 10.0, 12.0), np.array([0.4460, 0.4460, 102.5700])), + (("2.5Y", 10.0, 14.0), np.array([0.4630, 0.4600, 102.5700])), + (("2.5Y", 10.0, 16.0), np.array([0.4760, 0.4700, 102.5700])), + (("2.5Y", 10.0, 18.0), np.array([0.4870, 0.4770, 102.5700])), + (("2.5Y", 10.0, 20.0), np.array([0.4960, 0.4860, 102.5700])), + (("2.5Y", 10.0, 22.0), np.array([0.5030, 0.4900, 102.5700])), + (("5Y", 10.0, 2.0), np.array([0.3360, 0.3480, 102.5700])), + (("5Y", 10.0, 4.0), np.array([0.3600, 0.3770, 102.5700])), + (("5Y", 10.0, 6.0), np.array([0.3810, 0.4020, 102.5700])), + (("5Y", 10.0, 8.0), np.array([0.4000, 0.4250, 102.5700])), + (("5Y", 10.0, 10.0), np.array([0.4180, 0.4460, 102.5700])), + (("5Y", 10.0, 12.0), np.array([0.4350, 0.4650, 102.5700])), + (("5Y", 10.0, 14.0), np.array([0.4500, 0.4810, 102.5700])), + (("5Y", 10.0, 16.0), np.array([0.4620, 0.4940, 102.5700])), + (("5Y", 10.0, 18.0), np.array([0.4720, 0.5020, 102.5700])), + (("5Y", 10.0, 20.0), np.array([0.4780, 0.5080, 102.5700])), + (("5Y", 10.0, 22.0), np.array([0.4840, 0.5140, 102.5700])), + (("7.5Y", 10.0, 2.0), np.array([0.3350, 0.3510, 102.5700])), + (("7.5Y", 10.0, 4.0), np.array([0.3570, 0.3800, 102.5700])), + (("7.5Y", 10.0, 6.0), np.array([0.3760, 0.4070, 102.5700])), + (("7.5Y", 10.0, 8.0), np.array([0.3950, 0.4320, 102.5700])), + (("7.5Y", 10.0, 10.0), np.array([0.4120, 0.4540, 102.5700])), + (("7.5Y", 10.0, 12.0), np.array([0.4270, 0.4730, 102.5700])), + (("7.5Y", 10.0, 14.0), np.array([0.4420, 0.4920, 102.5700])), + (("7.5Y", 10.0, 16.0), np.array([0.4530, 0.5050, 102.5700])), + (("7.5Y", 10.0, 18.0), np.array([0.4620, 0.5150, 102.5700])), + (("7.5Y", 10.0, 20.0), np.array([0.4670, 0.5210, 102.5700])), + (("7.5Y", 10.0, 22.0), np.array([0.4720, 0.5280, 102.5700])), +) # yapf: enable """ -*All* published *Munsell* colours, including the extrapolated colors. +*All* published *Munsell* colours, including the extrapolated colors as a +*tuple* as follows:: + + ( + (('hue', 'value', 'chroma'), np.array(['x', 'y', 'Y'])), + ..., + (('hue', 'value', 'chroma'), np.array(['x', 'y', 'Y'])), + ) References ---------- :cite:`MunsellColorSciencec` - -MUNSELL_COLOURS_ALL : tuple - (('hue', 'value', 'chroma'), np.array(['x', 'y', 'Y'])) """ diff --git a/colour/notation/datasets/munsell/experimental.py b/colour/notation/datasets/munsell/experimental.py index 1fcf136e54..40b5d1217e 100644 --- a/colour/notation/datasets/munsell/experimental.py +++ b/colour/notation/datasets/munsell/experimental.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Munsell Renotation System Dataset - 1929 Munsell Colours ======================================================== @@ -9,10 +8,10 @@ Notes ----- -- The Munsell Renotation data commonly available within the all.dat, - experimental.dat and real.dat files features *CIE xyY* colourspace values - that are scaled by a :math:`1 / 0.975 \\simeq 1.02568` factor. If you are - performing conversions using *Munsell* *Colorlab* specification, +- The Munsell Renotation data commonly available within the *all.dat*, + *experimental.dat* and *real.dat* files features *CIE xyY* colourspace + values that are scaled by a :math:`1 / 0.975 \\simeq 1.02568` factor. If + you are performing conversions using *Munsell* *Colorlab* specification, e.g. *2.5R 9/2*, according to *ASTM D1535-08e1* method, you should not scale the output :math:`Y` Luminance. However, if you use directly the *CIE xyY* colourspace values from the Munsell Renotation data data, you @@ -26,6 +25,9 @@ magnesium oxide with respect to the perfect reflecting diffuser, and rounding to ve digits of precision. +- Chromaticities assume *CIE Illuminant C*, approximately 6700K, as neutral + origin for both the hue and chroma loci. + References ---------- - :cite:`MunsellColorSciencec` : Munsell Color Science. (n.d.). Munsell @@ -33,985 +35,993 @@ http://www.cis.rit.edu/research/mcsl2/online/munsell.php """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import Tuple + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "MUNSELL_COLOURS_1929", +] -__all__ = ['MUNSELL_COLOURS_1929'] -# yapf: disable -MUNSELL_COLOURS_1929 = ( - (('10RP', 2.0, 2.0), np.array([0.3532, 0.2957, 3.1260])), - (('10RP', 2.0, 4.0), np.array([0.3850, 0.2778, 3.1260])), - (('10RP', 2.0, 6.0), np.array([0.4139, 0.2608, 3.1260])), - (('10RP', 2.0, 8.0), np.array([0.4428, 0.2419, 3.1260])), - (('2.5R', 2.0, 2.0), np.array([0.3614, 0.3033, 3.1260])), - (('2.5R', 2.0, 4.0), np.array([0.4021, 0.2900, 3.1260])), - (('2.5R', 2.0, 6.0), np.array([0.4390, 0.2760, 3.1260])), - (('5R', 2.0, 2.0), np.array([0.3692, 0.3111, 3.1260])), - (('5R', 2.0, 4.0), np.array([0.4184, 0.3032, 3.1260])), - (('5R', 2.0, 6.0), np.array([0.4642, 0.2934, 3.1260])), - (('7.5R', 2.0, 2.0), np.array([0.3751, 0.3181, 3.1260])), - (('7.5R', 2.0, 4.0), np.array([0.4335, 0.3169, 3.1260])), - (('10R', 2.0, 2.0), np.array([0.3811, 0.3274, 3.1260])), - (('2.5YR', 2.0, 2.0), np.array([0.3852, 0.3365, 3.1260])), - (('5YR', 2.0, 2.0), np.array([0.3880, 0.3476, 3.1260])), - (('7.5YR', 2.0, 2.0), np.array([0.3889, 0.3590, 3.1260])), - (('10YR', 2.0, 2.0), np.array([0.3872, 0.3688, 3.1260])), - (('2.5Y', 2.0, 2.0), np.array([0.3825, 0.3785, 3.1260])), - (('5Y', 2.0, 2.0), np.array([0.3757, 0.3839, 3.1260])), - (('7.5Y', 2.0, 2.0), np.array([0.3660, 0.3858, 3.1260])), - (('10Y', 2.0, 2.0), np.array([0.3556, 0.3848, 3.1260])), - (('2.5GY', 2.0, 2.0), np.array([0.3421, 0.3803, 3.1260])), - (('5GY', 2.0, 2.0), np.array([0.3309, 0.3743, 3.1260])), - (('7.5GY', 2.0, 2.0), np.array([0.3165, 0.3650, 3.1260])), - (('10GY', 2.0, 2.0), np.array([0.3069, 0.3580, 3.1260])), - (('2.5G', 2.0, 2.0), np.array([0.2978, 0.3507, 3.1260])), - (('5G', 2.0, 2.0), np.array([0.2918, 0.3450, 3.1260])), - (('7.5G', 2.0, 2.0), np.array([0.2869, 0.3400, 3.1260])), - (('10G', 2.0, 2.0), np.array([0.2820, 0.3341, 3.1260])), - (('2.5BG', 2.0, 2.0), np.array([0.2765, 0.3271, 3.1260])), - (('2.5BG', 2.0, 4.0), np.array([0.2343, 0.3378, 3.1260])), - (('5BG', 2.0, 2.0), np.array([0.2697, 0.3175, 3.1260])), - (('5BG', 2.0, 4.0), np.array([0.2234, 0.3150, 3.1260])), - (('7.5BG', 2.0, 2.0), np.array([0.2651, 0.3098, 3.1260])), - (('7.5BG', 2.0, 4.0), np.array([0.2162, 0.2981, 3.1260])), - (('10BG', 2.0, 2.0), np.array([0.2606, 0.3010, 3.1260])), - (('2.5B', 2.0, 2.0), np.array([0.2578, 0.2940, 3.1260])), - (('5B', 2.0, 2.0), np.array([0.2559, 0.2874, 3.1260])), - (('7.5B', 2.0, 2.0), np.array([0.2545, 0.2799, 3.1260])), - (('7.5B', 2.0, 4.0), np.array([0.2063, 0.2400, 3.1260])), - (('10B', 2.0, 2.0), np.array([0.2558, 0.2725, 3.1260])), - (('10B', 2.0, 4.0), np.array([0.2102, 0.2313, 3.1260])), - (('2.5PB', 2.0, 2.0), np.array([0.2592, 0.2675, 3.1260])), - (('2.5PB', 2.0, 4.0), np.array([0.2175, 0.2245, 3.1260])), - (('2.5PB', 2.0, 6.0), np.array([0.1825, 0.1857, 3.1260])), - (('5PB', 2.0, 2.0), np.array([0.2638, 0.2624, 3.1260])), - (('5PB', 2.0, 4.0), np.array([0.2263, 0.2192, 3.1260])), - (('5PB', 2.0, 6.0), np.array([0.1942, 0.1811, 3.1260])), - (('7.5PB', 2.0, 2.0), np.array([0.2712, 0.2582, 3.1260])), - (('7.5PB', 2.0, 4.0), np.array([0.2420, 0.2148, 3.1260])), - (('7.5PB', 2.0, 6.0), np.array([0.2189, 0.1790, 3.1260])), - (('10PB', 2.0, 2.0), np.array([0.2803, 0.2567, 3.1260])), - (('10PB', 2.0, 4.0), np.array([0.2600, 0.2162, 3.1260])), - (('10PB', 2.0, 6.0), np.array([0.2440, 0.1840, 3.1260])), - (('2.5P', 2.0, 2.0), np.array([0.2892, 0.2583, 3.1260])), - (('2.5P', 2.0, 4.0), np.array([0.2758, 0.2208, 3.1260])), - (('2.5P', 2.0, 6.0), np.array([0.2661, 0.1921, 3.1260])), - (('5P', 2.0, 2.0), np.array([0.2984, 0.2612, 3.1260])), - (('5P', 2.0, 4.0), np.array([0.2908, 0.2261, 3.1260])), - (('5P', 2.0, 6.0), np.array([0.2850, 0.1992, 3.1260])), - (('7.5P', 2.0, 2.0), np.array([0.3071, 0.2647, 3.1260])), - (('7.5P', 2.0, 4.0), np.array([0.3048, 0.2321, 3.1260])), - (('7.5P', 2.0, 6.0), np.array([0.3025, 0.2058, 3.1260])), - (('10P', 2.0, 2.0), np.array([0.3161, 0.2691, 3.1260])), - (('10P', 2.0, 4.0), np.array([0.3189, 0.2390, 3.1260])), - (('10P', 2.0, 6.0), np.array([0.3207, 0.2132, 3.1260])), - (('2.5RP', 2.0, 2.0), np.array([0.3279, 0.2754, 3.1260])), - (('2.5RP', 2.0, 4.0), np.array([0.3382, 0.2496, 3.1260])), - (('2.5RP', 2.0, 6.0), np.array([0.3470, 0.2259, 3.1260])), - (('5RP', 2.0, 2.0), np.array([0.3383, 0.2829, 3.1260])), - (('5RP', 2.0, 4.0), np.array([0.3558, 0.2597, 3.1260])), - (('5RP', 2.0, 6.0), np.array([0.3708, 0.2380, 3.1260])), - (('7.5RP', 2.0, 2.0), np.array([0.3459, 0.2892, 3.1260])), - (('7.5RP', 2.0, 4.0), np.array([0.3702, 0.2683, 3.1260])), - (('7.5RP', 2.0, 6.0), np.array([0.3918, 0.2490, 3.1260])), - (('10RP', 3.0, 2.0), np.array([0.3526, 0.3068, 6.5550])), - (('10RP', 3.0, 4.0), np.array([0.3889, 0.2969, 6.5550])), - (('10RP', 3.0, 6.0), np.array([0.4218, 0.2864, 6.5550])), - (('10RP', 3.0, 8.0), np.array([0.4552, 0.2741, 6.5550])), - (('10RP', 3.0, 10.0), np.array([0.4851, 0.2618, 6.5550])), - (('2.5R', 3.0, 2.0), np.array([0.3591, 0.3130, 6.5550])), - (('2.5R', 3.0, 4.0), np.array([0.4021, 0.3076, 6.5550])), - (('2.5R', 3.0, 6.0), np.array([0.4409, 0.3009, 6.5550])), - (('2.5R', 3.0, 8.0), np.array([0.4821, 0.2918, 6.5550])), - (('2.5R', 3.0, 10.0), np.array([0.5191, 0.2811, 6.5550])), - (('5R', 3.0, 2.0), np.array([0.3645, 0.3190, 6.5550])), - (('5R', 3.0, 4.0), np.array([0.4148, 0.3190, 6.5550])), - (('5R', 3.0, 6.0), np.array([0.4592, 0.3168, 6.5550])), - (('5R', 3.0, 8.0), np.array([0.5064, 0.3114, 6.5550])), - (('5R', 3.0, 10.0), np.array([0.5500, 0.3024, 6.5550])), - (('7.5R', 3.0, 2.0), np.array([0.3690, 0.3248, 6.5550])), - (('7.5R', 3.0, 4.0), np.array([0.4240, 0.3302, 6.5550])), - (('7.5R', 3.0, 6.0), np.array([0.4738, 0.3316, 6.5550])), - (('7.5R', 3.0, 8.0), np.array([0.5251, 0.3297, 6.5550])), - (('10R', 3.0, 2.0), np.array([0.3728, 0.3314, 6.5550])), - (('10R', 3.0, 4.0), np.array([0.4308, 0.3412, 6.5550])), - (('10R', 3.0, 6.0), np.array([0.4854, 0.3467, 6.5550])), - (('2.5YR', 3.0, 2.0), np.array([0.3757, 0.3391, 6.5550])), - (('2.5YR', 3.0, 4.0), np.array([0.4360, 0.3563, 6.5550])), - (('2.5YR', 3.0, 6.0), np.array([0.4954, 0.3692, 6.5550])), - (('5YR', 3.0, 2.0), np.array([0.3771, 0.3476, 6.5550])), - (('5YR', 3.0, 4.0), np.array([0.4376, 0.3715, 6.5550])), - (('7.5YR', 3.0, 2.0), np.array([0.3771, 0.3549, 6.5550])), - (('7.5YR', 3.0, 4.0), np.array([0.4378, 0.3865, 6.5550])), - (('10YR', 3.0, 2.0), np.array([0.3747, 0.3630, 6.5550])), - (('10YR', 3.0, 4.0), np.array([0.4341, 0.4018, 6.5550])), - (('2.5Y', 3.0, 2.0), np.array([0.3703, 0.3700, 6.5550])), - (('2.5Y', 3.0, 4.0), np.array([0.4277, 0.4166, 6.5550])), - (('5Y', 3.0, 2.0), np.array([0.3646, 0.3748, 6.5550])), - (('7.5Y', 3.0, 2.0), np.array([0.3589, 0.3778, 6.5550])), - (('10Y', 3.0, 2.0), np.array([0.3513, 0.3789, 6.5550])), - (('2.5GY', 3.0, 2.0), np.array([0.3412, 0.3768, 6.5550])), - (('2.5GY', 3.0, 4.0), np.array([0.3772, 0.4484, 6.5550])), - (('5GY', 3.0, 2.0), np.array([0.3319, 0.3729, 6.5550])), - (('5GY', 3.0, 4.0), np.array([0.3554, 0.4429, 6.5550])), - (('7.5GY', 3.0, 2.0), np.array([0.3180, 0.3644, 6.5550])), - (('7.5GY', 3.0, 4.0), np.array([0.3270, 0.4288, 6.5550])), - (('10GY', 3.0, 2.0), np.array([0.3088, 0.3578, 6.5550])), - (('10GY', 3.0, 4.0), np.array([0.3053, 0.4123, 6.5550])), - (('2.5G', 3.0, 2.0), np.array([0.2999, 0.3500, 6.5550])), - (('2.5G', 3.0, 4.0), np.array([0.2836, 0.3915, 6.5550])), - (('5G', 3.0, 2.0), np.array([0.2935, 0.3439, 6.5550])), - (('5G', 3.0, 4.0), np.array([0.2711, 0.3780, 6.5550])), - (('7.5G', 3.0, 2.0), np.array([0.2890, 0.3391, 6.5550])), - (('7.5G', 3.0, 4.0), np.array([0.2618, 0.3667, 6.5550])), - (('10G', 3.0, 2.0), np.array([0.2844, 0.3337, 6.5550])), - (('10G', 3.0, 4.0), np.array([0.2525, 0.3537, 6.5550])), - (('2.5BG', 3.0, 2.0), np.array([0.2799, 0.3271, 6.5550])), - (('2.5BG', 3.0, 4.0), np.array([0.2437, 0.3386, 6.5550])), - (('2.5BG', 3.0, 6.0), np.array([0.2132, 0.3468, 6.5550])), - (('5BG', 3.0, 2.0), np.array([0.2742, 0.3192, 6.5550])), - (('5BG', 3.0, 4.0), np.array([0.2343, 0.3200, 6.5550])), - (('5BG', 3.0, 6.0), np.array([0.2020, 0.3188, 6.5550])), - (('7.5BG', 3.0, 2.0), np.array([0.2699, 0.3120, 6.5550])), - (('7.5BG', 3.0, 4.0), np.array([0.2272, 0.3041, 6.5550])), - (('7.5BG', 3.0, 6.0), np.array([0.1928, 0.2958, 6.5550])), - (('10BG', 3.0, 2.0), np.array([0.2660, 0.3050, 6.5550])), - (('10BG', 3.0, 4.0), np.array([0.2221, 0.2886, 6.5550])), - (('10BG', 3.0, 6.0), np.array([0.1861, 0.2722, 6.5550])), - (('2.5B', 3.0, 2.0), np.array([0.2636, 0.2983, 6.5550])), - (('2.5B', 3.0, 4.0), np.array([0.2183, 0.2748, 6.5550])), - (('2.5B', 3.0, 6.0), np.array([0.1826, 0.2536, 6.5550])), - (('5B', 3.0, 2.0), np.array([0.2617, 0.2921, 6.5550])), - (('5B', 3.0, 4.0), np.array([0.2176, 0.2632, 6.5550])), - (('5B', 3.0, 6.0), np.array([0.1835, 0.2375, 6.5550])), - (('7.5B', 3.0, 2.0), np.array([0.2616, 0.2857, 6.5550])), - (('7.5B', 3.0, 4.0), np.array([0.2200, 0.2536, 6.5550])), - (('7.5B', 3.0, 6.0), np.array([0.1875, 0.2258, 6.5550])), - (('7.5B', 3.0, 8.0), np.array([0.1583, 0.1987, 6.5550])), - (('10B', 3.0, 2.0), np.array([0.2631, 0.2801, 6.5550])), - (('10B', 3.0, 4.0), np.array([0.2246, 0.2467, 6.5550])), - (('10B', 3.0, 6.0), np.array([0.1933, 0.2173, 6.5550])), - (('10B', 3.0, 8.0), np.array([0.1658, 0.1905, 6.5550])), - (('2.5PB', 3.0, 2.0), np.array([0.2663, 0.2756, 6.5550])), - (('2.5PB', 3.0, 4.0), np.array([0.2312, 0.2405, 6.5550])), - (('2.5PB', 3.0, 6.0), np.array([0.2022, 0.2101, 6.5550])), - (('2.5PB', 3.0, 8.0), np.array([0.1780, 0.1833, 6.5550])), - (('2.5PB', 3.0, 10.0), np.array([0.1576, 0.1600, 6.5550])), - (('5PB', 3.0, 2.0), np.array([0.2708, 0.2719, 6.5550])), - (('5PB', 3.0, 4.0), np.array([0.2393, 0.2361, 6.5550])), - (('5PB', 3.0, 6.0), np.array([0.2122, 0.2052, 6.5550])), - (('5PB', 3.0, 8.0), np.array([0.1908, 0.1799, 6.5550])), - (('5PB', 3.0, 10.0), np.array([0.1718, 0.1562, 6.5550])), - (('5PB', 3.0, 12.0), np.array([0.1557, 0.1356, 6.5550])), - (('7.5PB', 3.0, 2.0), np.array([0.2777, 0.2687, 6.5550])), - (('7.5PB', 3.0, 4.0), np.array([0.2520, 0.2319, 6.5550])), - (('7.5PB', 3.0, 6.0), np.array([0.2311, 0.2010, 6.5550])), - (('7.5PB', 3.0, 8.0), np.array([0.2149, 0.1761, 6.5550])), - (('7.5PB', 3.0, 10.0), np.array([0.2005, 0.1536, 6.5550])), - (('10PB', 3.0, 2.0), np.array([0.2847, 0.2670, 6.5550])), - (('10PB', 3.0, 4.0), np.array([0.2660, 0.2319, 6.5550])), - (('10PB', 3.0, 6.0), np.array([0.2511, 0.2031, 6.5550])), - (('10PB', 3.0, 8.0), np.array([0.2387, 0.1786, 6.5550])), - (('10PB', 3.0, 10.0), np.array([0.2278, 0.1565, 6.5550])), - (('2.5P', 3.0, 2.0), np.array([0.2922, 0.2680, 6.5550])), - (('2.5P', 3.0, 4.0), np.array([0.2792, 0.2342, 6.5550])), - (('2.5P', 3.0, 6.0), np.array([0.2691, 0.2072, 6.5550])), - (('2.5P', 3.0, 8.0), np.array([0.2615, 0.1845, 6.5550])), - (('2.5P', 3.0, 10.0), np.array([0.2548, 0.1638, 6.5550])), - (('5P', 3.0, 2.0), np.array([0.2997, 0.2700, 6.5550])), - (('5P', 3.0, 4.0), np.array([0.2928, 0.2386, 6.5550])), - (('5P', 3.0, 6.0), np.array([0.2870, 0.2135, 6.5550])), - (('5P', 3.0, 8.0), np.array([0.2819, 0.1910, 6.5550])), - (('5P', 3.0, 10.0), np.array([0.2772, 0.1707, 6.5550])), - (('7.5P', 3.0, 2.0), np.array([0.3088, 0.2740, 6.5550])), - (('7.5P', 3.0, 4.0), np.array([0.3072, 0.2448, 6.5550])), - (('7.5P', 3.0, 6.0), np.array([0.3057, 0.2208, 6.5550])), - (('7.5P', 3.0, 8.0), np.array([0.3037, 0.1981, 6.5550])), - (('7.5P', 3.0, 10.0), np.array([0.3020, 0.1794, 6.5550])), - (('10P', 3.0, 2.0), np.array([0.3170, 0.2790, 6.5550])), - (('10P', 3.0, 4.0), np.array([0.3214, 0.2517, 6.5550])), - (('10P', 3.0, 6.0), np.array([0.3243, 0.2293, 6.5550])), - (('10P', 3.0, 8.0), np.array([0.3269, 0.2075, 6.5550])), - (('10P', 3.0, 10.0), np.array([0.3286, 0.1889, 6.5550])), - (('2.5RP', 3.0, 2.0), np.array([0.3272, 0.2861, 6.5550])), - (('2.5RP', 3.0, 4.0), np.array([0.3400, 0.2624, 6.5550])), - (('2.5RP', 3.0, 6.0), np.array([0.3501, 0.2425, 6.5550])), - (('2.5RP', 3.0, 8.0), np.array([0.3598, 0.2233, 6.5550])), - (('2.5RP', 3.0, 10.0), np.array([0.3681, 0.2054, 6.5550])), - (('5RP', 3.0, 2.0), np.array([0.3370, 0.2940, 6.5550])), - (('5RP', 3.0, 4.0), np.array([0.3586, 0.2742, 6.5550])), - (('5RP', 3.0, 6.0), np.array([0.3765, 0.2569, 6.5550])), - (('5RP', 3.0, 8.0), np.array([0.3930, 0.2395, 6.5550])), - (('5RP', 3.0, 10.0), np.array([0.4073, 0.2235, 6.5550])), - (('7.5RP', 3.0, 2.0), np.array([0.3450, 0.3001, 6.5550])), - (('7.5RP', 3.0, 4.0), np.array([0.3739, 0.2851, 6.5550])), - (('7.5RP', 3.0, 6.0), np.array([0.3990, 0.2708, 6.5550])), - (('7.5RP', 3.0, 8.0), np.array([0.4234, 0.2556, 6.5550])), - (('7.5RP', 3.0, 10.0), np.array([0.4445, 0.2419, 6.5550])), - (('10RP', 4.0, 2.0), np.array([0.3417, 0.3106, 12.0000])), - (('10RP', 4.0, 4.0), np.array([0.3715, 0.3042, 12.0000])), - (('10RP', 4.0, 6.0), np.array([0.3999, 0.2972, 12.0000])), - (('10RP', 4.0, 8.0), np.array([0.4282, 0.2890, 12.0000])), - (('10RP', 4.0, 10.0), np.array([0.4528, 0.2811, 12.0000])), - (('2.5R', 4.0, 2.0), np.array([0.3461, 0.3150, 12.0000])), - (('2.5R', 4.0, 4.0), np.array([0.3806, 0.3125, 12.0000])), - (('2.5R', 4.0, 6.0), np.array([0.4141, 0.3085, 12.0000])), - (('2.5R', 4.0, 8.0), np.array([0.4472, 0.3031, 12.0000])), - (('2.5R', 4.0, 10.0), np.array([0.4774, 0.2969, 12.0000])), - (('2.5R', 4.0, 12.0), np.array([0.5072, 0.2897, 12.0000])), - (('5R', 4.0, 2.0), np.array([0.3508, 0.3200, 12.0000])), - (('5R', 4.0, 4.0), np.array([0.3916, 0.3223, 12.0000])), - (('5R', 4.0, 6.0), np.array([0.4299, 0.3226, 12.0000])), - (('5R', 4.0, 8.0), np.array([0.4690, 0.3209, 12.0000])), - (('5R', 4.0, 10.0), np.array([0.5043, 0.3176, 12.0000])), - (('5R', 4.0, 12.0), np.array([0.5385, 0.3129, 12.0000])), - (('5R', 4.0, 14.0), np.array([0.5734, 0.3057, 12.0000])), - (('7.5R', 4.0, 2.0), np.array([0.3538, 0.3236, 12.0000])), - (('7.5R', 4.0, 4.0), np.array([0.3990, 0.3300, 12.0000])), - (('7.5R', 4.0, 6.0), np.array([0.4415, 0.3340, 12.0000])), - (('7.5R', 4.0, 8.0), np.array([0.4850, 0.3359, 12.0000])), - (('7.5R', 4.0, 10.0), np.array([0.5235, 0.3351, 12.0000])), - (('7.5R', 4.0, 12.0), np.array([0.5603, 0.3321, 12.0000])), - (('10R', 4.0, 2.0), np.array([0.3582, 0.3294, 12.0000])), - (('10R', 4.0, 4.0), np.array([0.4078, 0.3412, 12.0000])), - (('10R', 4.0, 6.0), np.array([0.4535, 0.3500, 12.0000])), - (('10R', 4.0, 8.0), np.array([0.4995, 0.3557, 12.0000])), - (('10R', 4.0, 10.0), np.array([0.5418, 0.3580, 12.0000])), - (('2.5YR', 4.0, 2.0), np.array([0.3624, 0.3367, 12.0000])), - (('2.5YR', 4.0, 4.0), np.array([0.4141, 0.3539, 12.0000])), - (('2.5YR', 4.0, 6.0), np.array([0.4612, 0.3674, 12.0000])), - (('2.5YR', 4.0, 8.0), np.array([0.5071, 0.3777, 12.0000])), - (('2.5YR', 4.0, 10.0), np.array([0.5475, 0.3856, 12.0000])), - (('5YR', 4.0, 2.0), np.array([0.3651, 0.3442, 12.0000])), - (('5YR', 4.0, 4.0), np.array([0.4187, 0.3679, 12.0000])), - (('5YR', 4.0, 6.0), np.array([0.4651, 0.3859, 12.0000])), - (('5YR', 4.0, 8.0), np.array([0.5070, 0.3994, 12.0000])), - (('7.5YR', 4.0, 2.0), np.array([0.3662, 0.3504, 12.0000])), - (('7.5YR', 4.0, 4.0), np.array([0.4208, 0.3809, 12.0000])), - (('7.5YR', 4.0, 6.0), np.array([0.4655, 0.4029, 12.0000])), - (('7.5YR', 4.0, 8.0), np.array([0.5038, 0.4204, 12.0000])), - (('10YR', 4.0, 2.0), np.array([0.3660, 0.3590, 12.0000])), - (('10YR', 4.0, 4.0), np.array([0.4189, 0.3948, 12.0000])), - (('10YR', 4.0, 6.0), np.array([0.4618, 0.4213, 12.0000])), - (('2.5Y', 4.0, 2.0), np.array([0.3633, 0.3654, 12.0000])), - (('2.5Y', 4.0, 4.0), np.array([0.4138, 0.4076, 12.0000])), - (('2.5Y', 4.0, 6.0), np.array([0.4542, 0.4391, 12.0000])), - (('5Y', 4.0, 2.0), np.array([0.3590, 0.3701, 12.0000])), - (('5Y', 4.0, 4.0), np.array([0.4069, 0.4188, 12.0000])), - (('7.5Y', 4.0, 2.0), np.array([0.3542, 0.3727, 12.0000])), - (('7.5Y', 4.0, 4.0), np.array([0.3982, 0.4272, 12.0000])), - (('10Y', 4.0, 2.0), np.array([0.3436, 0.3732, 12.0000])), - (('10Y', 4.0, 4.0), np.array([0.3871, 0.4321, 12.0000])), - (('2.5GY', 4.0, 2.0), np.array([0.3382, 0.3706, 12.0000])), - (('2.5GY', 4.0, 4.0), np.array([0.3708, 0.4329, 12.0000])), - (('2.5GY', 4.0, 6.0), np.array([0.3968, 0.4857, 12.0000])), - (('5GY', 4.0, 2.0), np.array([0.3312, 0.3678, 12.0000])), - (('5GY', 4.0, 4.0), np.array([0.3538, 0.4284, 12.0000])), - (('5GY', 4.0, 6.0), np.array([0.3718, 0.4852, 12.0000])), - (('7.5GY', 4.0, 2.0), np.array([0.3185, 0.3604, 12.0000])), - (('7.5GY', 4.0, 4.0), np.array([0.3281, 0.4157, 12.0000])), - (('7.5GY', 4.0, 6.0), np.array([0.3355, 0.4739, 12.0000])), - (('10GY', 4.0, 2.0), np.array([0.3109, 0.3550, 12.0000])), - (('10GY', 4.0, 4.0), np.array([0.3100, 0.4018, 12.0000])), - (('10GY', 4.0, 6.0), np.array([0.3069, 0.4550, 12.0000])), - (('2.5G', 4.0, 2.0), np.array([0.3012, 0.3470, 12.0000])), - (('2.5G', 4.0, 4.0), np.array([0.2891, 0.3821, 12.0000])), - (('2.5G', 4.0, 6.0), np.array([0.2735, 0.4215, 12.0000])), - (('5G', 4.0, 2.0), np.array([0.2959, 0.3417, 12.0000])), - (('5G', 4.0, 4.0), np.array([0.2781, 0.3704, 12.0000])), - (('7.5G', 4.0, 2.0), np.array([0.2919, 0.3371, 12.0000])), - (('7.5G', 4.0, 4.0), np.array([0.2702, 0.3602, 12.0000])), - (('10G', 4.0, 2.0), np.array([0.2880, 0.3327, 12.0000])), - (('10G', 4.0, 4.0), np.array([0.2628, 0.3498, 12.0000])), - (('2.5BG', 4.0, 2.0), np.array([0.2840, 0.3270, 12.0000])), - (('2.5BG', 4.0, 4.0), np.array([0.2552, 0.3375, 12.0000])), - (('2.5BG', 4.0, 6.0), np.array([0.2278, 0.3463, 12.0000])), - (('5BG', 4.0, 2.0), np.array([0.2799, 0.3208, 12.0000])), - (('5BG', 4.0, 4.0), np.array([0.2480, 0.3232, 12.0000])), - (('5BG', 4.0, 6.0), np.array([0.2182, 0.3240, 12.0000])), - (('7.5BG', 4.0, 2.0), np.array([0.2764, 0.3148, 12.0000])), - (('7.5BG', 4.0, 4.0), np.array([0.2429, 0.3108, 12.0000])), - (('7.5BG', 4.0, 6.0), np.array([0.2113, 0.3052, 12.0000])), - (('10BG', 4.0, 2.0), np.array([0.2740, 0.3091, 12.0000])), - (('10BG', 4.0, 4.0), np.array([0.2384, 0.2984, 12.0000])), - (('10BG', 4.0, 6.0), np.array([0.2065, 0.2863, 12.0000])), - (('2.5B', 4.0, 2.0), np.array([0.2727, 0.3038, 12.0000])), - (('2.5B', 4.0, 4.0), np.array([0.2360, 0.2872, 12.0000])), - (('2.5B', 4.0, 6.0), np.array([0.2048, 0.2708, 12.0000])), - (('2.5B', 4.0, 8.0), np.array([0.1737, 0.2524, 12.0000])), - (('5B', 4.0, 2.0), np.array([0.2723, 0.2992, 12.0000])), - (('5B', 4.0, 4.0), np.array([0.2363, 0.2782, 12.0000])), - (('5B', 4.0, 6.0), np.array([0.2060, 0.2572, 12.0000])), - (('5B', 4.0, 8.0), np.array([0.1759, 0.2345, 12.0000])), - (('7.5B', 4.0, 2.0), np.array([0.2733, 0.2947, 12.0000])), - (('7.5B', 4.0, 4.0), np.array([0.2388, 0.2704, 12.0000])), - (('7.5B', 4.0, 6.0), np.array([0.2102, 0.2470, 12.0000])), - (('7.5B', 4.0, 8.0), np.array([0.1821, 0.2232, 12.0000])), - (('10B', 4.0, 2.0), np.array([0.2753, 0.2910, 12.0000])), - (('10B', 4.0, 4.0), np.array([0.2429, 0.2648, 12.0000])), - (('10B', 4.0, 6.0), np.array([0.2157, 0.2407, 12.0000])), - (('10B', 4.0, 8.0), np.array([0.1893, 0.2160, 12.0000])), - (('2.5PB', 4.0, 2.0), np.array([0.2782, 0.2876, 12.0000])), - (('2.5PB', 4.0, 4.0), np.array([0.2487, 0.2597, 12.0000])), - (('2.5PB', 4.0, 6.0), np.array([0.2235, 0.2343, 12.0000])), - (('2.5PB', 4.0, 8.0), np.array([0.1995, 0.2094, 12.0000])), - (('2.5PB', 4.0, 10.0), np.array([0.1805, 0.1888, 12.0000])), - (('5PB', 4.0, 2.0), np.array([0.2816, 0.2842, 12.0000])), - (('5PB', 4.0, 4.0), np.array([0.2562, 0.2560, 12.0000])), - (('5PB', 4.0, 6.0), np.array([0.2325, 0.2300, 12.0000])), - (('5PB', 4.0, 8.0), np.array([0.2103, 0.2050, 12.0000])), - (('5PB', 4.0, 10.0), np.array([0.1925, 0.1843, 12.0000])), - (('7.5PB', 4.0, 2.0), np.array([0.2861, 0.2819, 12.0000])), - (('7.5PB', 4.0, 4.0), np.array([0.2657, 0.2528, 12.0000])), - (('7.5PB', 4.0, 6.0), np.array([0.2471, 0.2266, 12.0000])), - (('7.5PB', 4.0, 8.0), np.array([0.2304, 0.2023, 12.0000])), - (('7.5PB', 4.0, 10.0), np.array([0.2158, 0.1811, 12.0000])), - (('10PB', 4.0, 2.0), np.array([0.2911, 0.2804, 12.0000])), - (('10PB', 4.0, 4.0), np.array([0.2759, 0.2522, 12.0000])), - (('10PB', 4.0, 6.0), np.array([0.2618, 0.2263, 12.0000])), - (('10PB', 4.0, 8.0), np.array([0.2497, 0.2038, 12.0000])), - (('10PB', 4.0, 10.0), np.array([0.2388, 0.1837, 12.0000])), - (('2.5P', 4.0, 2.0), np.array([0.2962, 0.2807, 12.0000])), - (('2.5P', 4.0, 4.0), np.array([0.2855, 0.2531, 12.0000])), - (('2.5P', 4.0, 6.0), np.array([0.2763, 0.2300, 12.0000])), - (('2.5P', 4.0, 8.0), np.array([0.2685, 0.2089, 12.0000])), - (('2.5P', 4.0, 10.0), np.array([0.2619, 0.1903, 12.0000])), - (('2.5P', 4.0, 12.0), np.array([0.2559, 0.1730, 12.0000])), - (('5P', 4.0, 2.0), np.array([0.3022, 0.2825, 12.0000])), - (('5P', 4.0, 4.0), np.array([0.2958, 0.2565, 12.0000])), - (('5P', 4.0, 6.0), np.array([0.2903, 0.2347, 12.0000])), - (('5P', 4.0, 8.0), np.array([0.2855, 0.2150, 12.0000])), - (('5P', 4.0, 10.0), np.array([0.2814, 0.1967, 12.0000])), - (('5P', 4.0, 12.0), np.array([0.2778, 0.1808, 12.0000])), - (('7.5P', 4.0, 2.0), np.array([0.3093, 0.2859, 12.0000])), - (('7.5P', 4.0, 4.0), np.array([0.3084, 0.2622, 12.0000])), - (('7.5P', 4.0, 6.0), np.array([0.3076, 0.2416, 12.0000])), - (('7.5P', 4.0, 8.0), np.array([0.3066, 0.2228, 12.0000])), - (('7.5P', 4.0, 10.0), np.array([0.3056, 0.2060, 12.0000])), - (('7.5P', 4.0, 12.0), np.array([0.3045, 0.1905, 12.0000])), - (('10P', 4.0, 2.0), np.array([0.3162, 0.2902, 12.0000])), - (('10P', 4.0, 4.0), np.array([0.3210, 0.2686, 12.0000])), - (('10P', 4.0, 6.0), np.array([0.3248, 0.2493, 12.0000])), - (('10P', 4.0, 8.0), np.array([0.3280, 0.2318, 12.0000])), - (('10P', 4.0, 10.0), np.array([0.3306, 0.2162, 12.0000])), - (('2.5RP', 4.0, 2.0), np.array([0.3231, 0.2951, 12.0000])), - (('2.5RP', 4.0, 4.0), np.array([0.3340, 0.2770, 12.0000])), - (('2.5RP', 4.0, 6.0), np.array([0.3442, 0.2595, 12.0000])), - (('2.5RP', 4.0, 8.0), np.array([0.3533, 0.2438, 12.0000])), - (('2.5RP', 4.0, 10.0), np.array([0.3608, 0.2301, 12.0000])), - (('2.5RP', 4.0, 12.0), np.array([0.3683, 0.2162, 12.0000])), - (('5RP', 4.0, 2.0), np.array([0.3310, 0.3010, 12.0000])), - (('5RP', 4.0, 4.0), np.array([0.3491, 0.2872, 12.0000])), - (('5RP', 4.0, 6.0), np.array([0.3671, 0.2733, 12.0000])), - (('5RP', 4.0, 8.0), np.array([0.3833, 0.2600, 12.0000])), - (('5RP', 4.0, 10.0), np.array([0.3960, 0.2489, 12.0000])), - (('5RP', 4.0, 12.0), np.array([0.4104, 0.2361, 12.0000])), - (('7.5RP', 4.0, 2.0), np.array([0.3371, 0.3061, 12.0000])), - (('7.5RP', 4.0, 4.0), np.array([0.3612, 0.2963, 12.0000])), - (('7.5RP', 4.0, 6.0), np.array([0.3850, 0.2859, 12.0000])), - (('7.5RP', 4.0, 8.0), np.array([0.4072, 0.2750, 12.0000])), - (('7.5RP', 4.0, 10.0), np.array([0.4259, 0.2651, 12.0000])), - (('7.5RP', 4.0, 12.0), np.array([0.4450, 0.2541, 12.0000])), - (('10RP', 5.0, 2.0), np.array([0.3332, 0.3131, 19.7700])), - (('10RP', 5.0, 4.0), np.array([0.3594, 0.3090, 19.7700])), - (('10RP', 5.0, 6.0), np.array([0.3851, 0.3039, 19.7700])), - (('10RP', 5.0, 8.0), np.array([0.4105, 0.2980, 19.7700])), - (('10RP', 5.0, 10.0), np.array([0.4332, 0.2918, 19.7700])), - (('2.5R', 5.0, 2.0), np.array([0.3360, 0.3158, 19.7700])), - (('2.5R', 5.0, 4.0), np.array([0.3660, 0.3148, 19.7700])), - (('2.5R', 5.0, 6.0), np.array([0.3960, 0.3130, 19.7700])), - (('2.5R', 5.0, 8.0), np.array([0.4252, 0.3101, 19.7700])), - (('2.5R', 5.0, 10.0), np.array([0.4533, 0.3058, 19.7700])), - (('2.5R', 5.0, 12.0), np.array([0.4820, 0.3002, 19.7700])), - (('5R', 5.0, 2.0), np.array([0.3392, 0.3192, 19.7700])), - (('5R', 5.0, 4.0), np.array([0.3740, 0.3220, 19.7700])), - (('5R', 5.0, 6.0), np.array([0.4078, 0.3238, 19.7700])), - (('5R', 5.0, 8.0), np.array([0.4413, 0.3240, 19.7700])), - (('5R', 5.0, 10.0), np.array([0.4747, 0.3227, 19.7700])), - (('5R', 5.0, 12.0), np.array([0.5071, 0.3194, 19.7700])), - (('7.5R', 5.0, 2.0), np.array([0.3425, 0.3229, 19.7700])), - (('7.5R', 5.0, 4.0), np.array([0.3806, 0.3294, 19.7700])), - (('7.5R', 5.0, 6.0), np.array([0.4180, 0.3348, 19.7700])), - (('7.5R', 5.0, 8.0), np.array([0.4563, 0.3387, 19.7700])), - (('7.5R', 5.0, 10.0), np.array([0.4927, 0.3399, 19.7700])), - (('7.5R', 5.0, 12.0), np.array([0.5280, 0.3389, 19.7700])), - (('10R', 5.0, 2.0), np.array([0.3465, 0.3278, 19.7700])), - (('10R', 5.0, 4.0), np.array([0.3879, 0.3398, 19.7700])), - (('10R', 5.0, 6.0), np.array([0.4299, 0.3499, 19.7700])), - (('10R', 5.0, 8.0), np.array([0.4713, 0.3575, 19.7700])), - (('10R', 5.0, 10.0), np.array([0.5113, 0.3630, 19.7700])), - (('2.5YR', 5.0, 2.0), np.array([0.3506, 0.3337, 19.7700])), - (('2.5YR', 5.0, 4.0), np.array([0.3925, 0.3494, 19.7700])), - (('2.5YR', 5.0, 6.0), np.array([0.4365, 0.3640, 19.7700])), - (('2.5YR', 5.0, 8.0), np.array([0.4795, 0.3758, 19.7700])), - (('2.5YR', 5.0, 10.0), np.array([0.5175, 0.3844, 19.7700])), - (('5YR', 5.0, 2.0), np.array([0.3530, 0.3395, 19.7700])), - (('5YR', 5.0, 4.0), np.array([0.3968, 0.3614, 19.7700])), - (('5YR', 5.0, 6.0), np.array([0.4420, 0.3808, 19.7700])), - (('5YR', 5.0, 8.0), np.array([0.4830, 0.3960, 19.7700])), - (('5YR', 5.0, 10.0), np.array([0.5161, 0.4064, 19.7700])), - (('7.5YR', 5.0, 2.0), np.array([0.3540, 0.3445, 19.7700])), - (('7.5YR', 5.0, 4.0), np.array([0.3991, 0.3714, 19.7700])), - (('7.5YR', 5.0, 6.0), np.array([0.4440, 0.3954, 19.7700])), - (('7.5YR', 5.0, 8.0), np.array([0.4820, 0.4141, 19.7700])), - (('7.5YR', 5.0, 10.0), np.array([0.5108, 0.4276, 19.7700])), - (('10YR', 5.0, 2.0), np.array([0.3546, 0.3514, 19.7700])), - (('10YR', 5.0, 4.0), np.array([0.3995, 0.3840, 19.7700])), - (('10YR', 5.0, 6.0), np.array([0.4428, 0.4128, 19.7700])), - (('10YR', 5.0, 8.0), np.array([0.4770, 0.4338, 19.7700])), - (('2.5Y', 5.0, 2.0), np.array([0.3534, 0.3570, 19.7700])), - (('2.5Y', 5.0, 4.0), np.array([0.3968, 0.3954, 19.7700])), - (('2.5Y', 5.0, 6.0), np.array([0.4380, 0.4292, 19.7700])), - (('2.5Y', 5.0, 8.0), np.array([0.4685, 0.4524, 19.7700])), - (('5Y', 5.0, 2.0), np.array([0.3500, 0.3620, 19.7700])), - (('5Y', 5.0, 4.0), np.array([0.3915, 0.4057, 19.7700])), - (('5Y', 5.0, 6.0), np.array([0.4302, 0.4435, 19.7700])), - (('7.5Y', 5.0, 2.0), np.array([0.3470, 0.3640, 19.7700])), - (('7.5Y', 5.0, 4.0), np.array([0.3850, 0.4120, 19.7700])), - (('7.5Y', 5.0, 6.0), np.array([0.4199, 0.4551, 19.7700])), - (('10Y', 5.0, 2.0), np.array([0.3422, 0.3648, 19.7700])), - (('10Y', 5.0, 4.0), np.array([0.3762, 0.4158, 19.7700])), - (('10Y', 5.0, 6.0), np.array([0.4072, 0.4621, 19.7700])), - (('2.5GY', 5.0, 2.0), np.array([0.3352, 0.3636, 19.7700])), - (('2.5GY', 5.0, 4.0), np.array([0.3621, 0.4143, 19.7700])), - (('2.5GY', 5.0, 6.0), np.array([0.3879, 0.4646, 19.7700])), - (('2.5GY', 5.0, 8.0), np.array([0.4088, 0.5068, 19.7700])), - (('5GY', 5.0, 2.0), np.array([0.3289, 0.3612, 19.7700])), - (('5GY', 5.0, 4.0), np.array([0.3482, 0.4097, 19.7700])), - (('5GY', 5.0, 6.0), np.array([0.3663, 0.4614, 19.7700])), - (('5GY', 5.0, 8.0), np.array([0.3815, 0.5093, 19.7700])), - (('7.5GY', 5.0, 2.0), np.array([0.3188, 0.3560, 19.7700])), - (('7.5GY', 5.0, 4.0), np.array([0.3274, 0.3994, 19.7700])), - (('7.5GY', 5.0, 6.0), np.array([0.3354, 0.4483, 19.7700])), - (('7.5GY', 5.0, 8.0), np.array([0.3412, 0.4976, 19.7700])), - (('10GY', 5.0, 2.0), np.array([0.3110, 0.3508, 19.7700])), - (('10GY', 5.0, 4.0), np.array([0.3111, 0.3881, 19.7700])), - (('10GY', 5.0, 6.0), np.array([0.3108, 0.4301, 19.7700])), - (('10GY', 5.0, 8.0), np.array([0.3080, 0.4759, 19.7700])), - (('2.5G', 5.0, 2.0), np.array([0.3030, 0.3445, 19.7700])), - (('2.5G', 5.0, 4.0), np.array([0.2943, 0.3735, 19.7700])), - (('2.5G', 5.0, 6.0), np.array([0.2841, 0.4045, 19.7700])), - (('2.5G', 5.0, 8.0), np.array([0.2710, 0.4380, 19.7700])), - (('5G', 5.0, 2.0), np.array([0.2978, 0.3392, 19.7700])), - (('5G', 5.0, 4.0), np.array([0.2841, 0.3628, 19.7700])), - (('5G', 5.0, 6.0), np.array([0.2690, 0.3860, 19.7700])), - (('5G', 5.0, 8.0), np.array([0.2511, 0.4107, 19.7700])), - (('7.5G', 5.0, 2.0), np.array([0.2945, 0.3355, 19.7700])), - (('7.5G', 5.0, 4.0), np.array([0.2775, 0.3545, 19.7700])), - (('7.5G', 5.0, 6.0), np.array([0.2598, 0.3724, 19.7700])), - (('7.5G', 5.0, 8.0), np.array([0.2395, 0.3915, 19.7700])), - (('10G', 5.0, 2.0), np.array([0.2910, 0.3310, 19.7700])), - (('10G', 5.0, 4.0), np.array([0.2711, 0.3455, 19.7700])), - (('10G', 5.0, 6.0), np.array([0.2519, 0.3587, 19.7700])), - (('2.5BG', 5.0, 2.0), np.array([0.2880, 0.3270, 19.7700])), - (('2.5BG', 5.0, 4.0), np.array([0.2659, 0.3369, 19.7700])), - (('2.5BG', 5.0, 6.0), np.array([0.2448, 0.3452, 19.7700])), - (('5BG', 5.0, 2.0), np.array([0.2841, 0.3210, 19.7700])), - (('5BG', 5.0, 4.0), np.array([0.2591, 0.3246, 19.7700])), - (('5BG', 5.0, 6.0), np.array([0.2360, 0.3270, 19.7700])), - (('7.5BG', 5.0, 2.0), np.array([0.2812, 0.3161, 19.7700])), - (('7.5BG', 5.0, 4.0), np.array([0.2550, 0.3150, 19.7700])), - (('7.5BG', 5.0, 6.0), np.array([0.2292, 0.3125, 19.7700])), - (('10BG', 5.0, 2.0), np.array([0.2796, 0.3111, 19.7700])), - (('10BG', 5.0, 4.0), np.array([0.2512, 0.3040, 19.7700])), - (('10BG', 5.0, 6.0), np.array([0.2234, 0.2952, 19.7700])), - (('2.5B', 5.0, 2.0), np.array([0.2791, 0.3071, 19.7700])), - (('2.5B', 5.0, 4.0), np.array([0.2492, 0.2954, 19.7700])), - (('2.5B', 5.0, 6.0), np.array([0.2210, 0.2823, 19.7700])), - (('5B', 5.0, 2.0), np.array([0.2794, 0.3032, 19.7700])), - (('5B', 5.0, 4.0), np.array([0.2493, 0.2879, 19.7700])), - (('5B', 5.0, 6.0), np.array([0.2215, 0.2701, 19.7700])), - (('7.5B', 5.0, 2.0), np.array([0.2803, 0.3000, 19.7700])), - (('7.5B', 5.0, 4.0), np.array([0.2511, 0.2808, 19.7700])), - (('7.5B', 5.0, 6.0), np.array([0.2248, 0.2612, 19.7700])), - (('10B', 5.0, 2.0), np.array([0.2821, 0.2966, 19.7700])), - (('10B', 5.0, 4.0), np.array([0.2547, 0.2757, 19.7700])), - (('10B', 5.0, 6.0), np.array([0.2299, 0.2548, 19.7700])), - (('2.5PB', 5.0, 2.0), np.array([0.2847, 0.2942, 19.7700])), - (('2.5PB', 5.0, 4.0), np.array([0.2600, 0.2720, 19.7700])), - (('2.5PB', 5.0, 6.0), np.array([0.2365, 0.2488, 19.7700])), - (('2.5PB', 5.0, 8.0), np.array([0.2157, 0.2278, 19.7700])), - (('5PB', 5.0, 2.0), np.array([0.2882, 0.2923, 19.7700])), - (('5PB', 5.0, 4.0), np.array([0.2662, 0.2687, 19.7700])), - (('5PB', 5.0, 6.0), np.array([0.2447, 0.2449, 19.7700])), - (('5PB', 5.0, 8.0), np.array([0.2255, 0.2239, 19.7700])), - (('5PB', 5.0, 10.0), np.array([0.2080, 0.2041, 19.7700])), - (('7.5PB', 5.0, 2.0), np.array([0.2918, 0.2908, 19.7700])), - (('7.5PB', 5.0, 4.0), np.array([0.2739, 0.2666, 19.7700])), - (('7.5PB', 5.0, 6.0), np.array([0.2563, 0.2417, 19.7700])), - (('7.5PB', 5.0, 8.0), np.array([0.2417, 0.2204, 19.7700])), - (('7.5PB', 5.0, 10.0), np.array([0.2285, 0.2020, 19.7700])), - (('10PB', 5.0, 2.0), np.array([0.2959, 0.2905, 19.7700])), - (('10PB', 5.0, 4.0), np.array([0.2821, 0.2659, 19.7700])), - (('10PB', 5.0, 6.0), np.array([0.2686, 0.2412, 19.7700])), - (('10PB', 5.0, 8.0), np.array([0.2572, 0.2211, 19.7700])), - (('10PB', 5.0, 10.0), np.array([0.2478, 0.2030, 19.7700])), - (('2.5P', 5.0, 2.0), np.array([0.3000, 0.2912, 19.7700])), - (('2.5P', 5.0, 4.0), np.array([0.2898, 0.2667, 19.7700])), - (('2.5P', 5.0, 6.0), np.array([0.2806, 0.2444, 19.7700])), - (('2.5P', 5.0, 8.0), np.array([0.2728, 0.2240, 19.7700])), - (('2.5P', 5.0, 10.0), np.array([0.2665, 0.2075, 19.7700])), - (('5P', 5.0, 2.0), np.array([0.3045, 0.2928, 19.7700])), - (('5P', 5.0, 4.0), np.array([0.2986, 0.2699, 19.7700])), - (('5P', 5.0, 6.0), np.array([0.2932, 0.2487, 19.7700])), - (('5P', 5.0, 8.0), np.array([0.2885, 0.2296, 19.7700])), - (('5P', 5.0, 10.0), np.array([0.2845, 0.2137, 19.7700])), - (('7.5P', 5.0, 2.0), np.array([0.3103, 0.2959, 19.7700])), - (('7.5P', 5.0, 4.0), np.array([0.3100, 0.2750, 19.7700])), - (('7.5P', 5.0, 6.0), np.array([0.3093, 0.2555, 19.7700])), - (('7.5P', 5.0, 8.0), np.array([0.3087, 0.2375, 19.7700])), - (('7.5P', 5.0, 10.0), np.array([0.3080, 0.2230, 19.7700])), - (('10P', 5.0, 2.0), np.array([0.3148, 0.2986, 19.7700])), - (('10P', 5.0, 4.0), np.array([0.3198, 0.2807, 19.7700])), - (('10P', 5.0, 6.0), np.array([0.3243, 0.2630, 19.7700])), - (('10P', 5.0, 8.0), np.array([0.3280, 0.2464, 19.7700])), - (('10P', 5.0, 10.0), np.array([0.3308, 0.2328, 19.7700])), - (('2.5RP', 5.0, 2.0), np.array([0.3199, 0.3019, 19.7700])), - (('2.5RP', 5.0, 4.0), np.array([0.3298, 0.2869, 19.7700])), - (('2.5RP', 5.0, 6.0), np.array([0.3396, 0.2718, 19.7700])), - (('2.5RP', 5.0, 8.0), np.array([0.3490, 0.2570, 19.7700])), - (('2.5RP', 5.0, 10.0), np.array([0.3560, 0.2452, 19.7700])), - (('5RP', 5.0, 2.0), np.array([0.3256, 0.3065, 19.7700])), - (('5RP', 5.0, 4.0), np.array([0.3421, 0.2954, 19.7700])), - (('5RP', 5.0, 6.0), np.array([0.3585, 0.2842, 19.7700])), - (('5RP', 5.0, 8.0), np.array([0.3748, 0.2729, 19.7700])), - (('5RP', 5.0, 10.0), np.array([0.3880, 0.2630, 19.7700])), - (('7.5RP', 5.0, 2.0), np.array([0.3296, 0.3098, 19.7700])), - (('7.5RP', 5.0, 4.0), np.array([0.3515, 0.3024, 19.7700])), - (('7.5RP', 5.0, 6.0), np.array([0.3726, 0.2941, 19.7700])), - (('7.5RP', 5.0, 8.0), np.array([0.3932, 0.2852, 19.7700])), - (('7.5RP', 5.0, 10.0), np.array([0.4108, 0.2773, 19.7700])), - (('10RP', 6.0, 2.0), np.array([0.3292, 0.3141, 30.0500])), - (('10RP', 6.0, 4.0), np.array([0.3508, 0.3112, 30.0500])), - (('10RP', 6.0, 6.0), np.array([0.3740, 0.3074, 30.0500])), - (('10RP', 6.0, 8.0), np.array([0.3930, 0.3038, 30.0500])), - (('10RP', 6.0, 10.0), np.array([0.4150, 0.2989, 30.0500])), - (('2.5R', 6.0, 2.0), np.array([0.3318, 0.3166, 30.0500])), - (('2.5R', 6.0, 4.0), np.array([0.3566, 0.3163, 30.0500])), - (('2.5R', 6.0, 6.0), np.array([0.3832, 0.3158, 30.0500])), - (('2.5R', 6.0, 8.0), np.array([0.4065, 0.3144, 30.0500])), - (('2.5R', 6.0, 10.0), np.array([0.4320, 0.3118, 30.0500])), - (('5R', 6.0, 2.0), np.array([0.3343, 0.3190, 30.0500])), - (('5R', 6.0, 4.0), np.array([0.3628, 0.3221, 30.0500])), - (('5R', 6.0, 6.0), np.array([0.3921, 0.3244, 30.0500])), - (('5R', 6.0, 8.0), np.array([0.4187, 0.3251, 30.0500])), - (('5R', 6.0, 10.0), np.array([0.4480, 0.3250, 30.0500])), - (('7.5R', 6.0, 2.0), np.array([0.3381, 0.3228, 30.0500])), - (('7.5R', 6.0, 4.0), np.array([0.3692, 0.3291, 30.0500])), - (('7.5R', 6.0, 6.0), np.array([0.4000, 0.3340, 30.0500])), - (('7.5R', 6.0, 8.0), np.array([0.4318, 0.3383, 30.0500])), - (('7.5R', 6.0, 10.0), np.array([0.4655, 0.3412, 30.0500])), - (('10R', 6.0, 2.0), np.array([0.3417, 0.3268, 30.0500])), - (('10R', 6.0, 4.0), np.array([0.3768, 0.3381, 30.0500])), - (('10R', 6.0, 6.0), np.array([0.4103, 0.3473, 30.0500])), - (('10R', 6.0, 8.0), np.array([0.4449, 0.3550, 30.0500])), - (('10R', 6.0, 10.0), np.array([0.4812, 0.3619, 30.0500])), - (('2.5YR', 6.0, 2.0), np.array([0.3453, 0.3321, 30.0500])), - (('2.5YR', 6.0, 4.0), np.array([0.3806, 0.3467, 30.0500])), - (('2.5YR', 6.0, 6.0), np.array([0.4180, 0.3600, 30.0500])), - (('2.5YR', 6.0, 8.0), np.array([0.4533, 0.3708, 30.0500])), - (('2.5YR', 6.0, 10.0), np.array([0.4891, 0.3806, 30.0500])), - (('2.5YR', 6.0, 12.0), np.array([0.5215, 0.3887, 30.0500])), - (('5YR', 6.0, 2.0), np.array([0.3474, 0.3373, 30.0500])), - (('5YR', 6.0, 4.0), np.array([0.3840, 0.3564, 30.0500])), - (('5YR', 6.0, 6.0), np.array([0.4229, 0.3750, 30.0500])), - (('5YR', 6.0, 8.0), np.array([0.4592, 0.3900, 30.0500])), - (('5YR', 6.0, 10.0), np.array([0.4921, 0.4022, 30.0500])), - (('5YR', 6.0, 12.0), np.array([0.5199, 0.4119, 30.0500])), - (('7.5YR', 6.0, 2.0), np.array([0.3487, 0.3421, 30.0500])), - (('7.5YR', 6.0, 4.0), np.array([0.3860, 0.3652, 30.0500])), - (('7.5YR', 6.0, 6.0), np.array([0.4242, 0.3876, 30.0500])), - (('7.5YR', 6.0, 8.0), np.array([0.4596, 0.4064, 30.0500])), - (('7.5YR', 6.0, 10.0), np.array([0.4904, 0.4220, 30.0500])), - (('7.5YR', 6.0, 12.0), np.array([0.5145, 0.4331, 30.0500])), - (('10YR', 6.0, 2.0), np.array([0.3491, 0.3483, 30.0500])), - (('10YR', 6.0, 4.0), np.array([0.3861, 0.3767, 30.0500])), - (('10YR', 6.0, 6.0), np.array([0.4240, 0.4030, 30.0500])), - (('10YR', 6.0, 8.0), np.array([0.4570, 0.4249, 30.0500])), - (('10YR', 6.0, 10.0), np.array([0.4843, 0.4416, 30.0500])), - (('2.5Y', 6.0, 2.0), np.array([0.3480, 0.3540, 30.0500])), - (('2.5Y', 6.0, 4.0), np.array([0.3840, 0.3867, 30.0500])), - (('2.5Y', 6.0, 6.0), np.array([0.4203, 0.4176, 30.0500])), - (('2.5Y', 6.0, 8.0), np.array([0.4517, 0.4421, 30.0500])), - (('2.5Y', 6.0, 10.0), np.array([0.4760, 0.4607, 30.0500])), - (('5Y', 6.0, 2.0), np.array([0.3457, 0.3580, 30.0500])), - (('5Y', 6.0, 4.0), np.array([0.3794, 0.3955, 30.0500])), - (('5Y', 6.0, 6.0), np.array([0.4140, 0.4305, 30.0500])), - (('5Y', 6.0, 8.0), np.array([0.4426, 0.4588, 30.0500])), - (('7.5Y', 6.0, 2.0), np.array([0.3431, 0.3601, 30.0500])), - (('7.5Y', 6.0, 4.0), np.array([0.3745, 0.4004, 30.0500])), - (('7.5Y', 6.0, 6.0), np.array([0.4060, 0.4400, 30.0500])), - (('7.5Y', 6.0, 8.0), np.array([0.4321, 0.4719, 30.0500])), - (('10Y', 6.0, 2.0), np.array([0.3398, 0.3611, 30.0500])), - (('10Y', 6.0, 4.0), np.array([0.3679, 0.4033, 30.0500])), - (('10Y', 6.0, 6.0), np.array([0.3960, 0.4452, 30.0500])), - (('2.5GY', 6.0, 2.0), np.array([0.3342, 0.3607, 30.0500])), - (('2.5GY', 6.0, 4.0), np.array([0.3572, 0.4038, 30.0500])), - (('2.5GY', 6.0, 6.0), np.array([0.3799, 0.4470, 30.0500])), - (('2.5GY', 6.0, 8.0), np.array([0.4006, 0.4885, 30.0500])), - (('5GY', 6.0, 2.0), np.array([0.3288, 0.3592, 30.0500])), - (('5GY', 6.0, 4.0), np.array([0.3461, 0.4008, 30.0500])), - (('5GY', 6.0, 6.0), np.array([0.3622, 0.4438, 30.0500])), - (('5GY', 6.0, 8.0), np.array([0.3772, 0.4880, 30.0500])), - (('7.5GY', 6.0, 2.0), np.array([0.3193, 0.3550, 30.0500])), - (('7.5GY', 6.0, 4.0), np.array([0.3275, 0.3922, 30.0500])), - (('7.5GY', 6.0, 6.0), np.array([0.3351, 0.4321, 30.0500])), - (('7.5GY', 6.0, 8.0), np.array([0.3418, 0.4768, 30.0500])), - (('7.5GY', 6.0, 10.0), np.array([0.3463, 0.5196, 30.0500])), - (('10GY', 6.0, 2.0), np.array([0.3112, 0.3496, 30.0500])), - (('10GY', 6.0, 4.0), np.array([0.3124, 0.3822, 30.0500])), - (('10GY', 6.0, 6.0), np.array([0.3128, 0.4175, 30.0500])), - (('10GY', 6.0, 8.0), np.array([0.3116, 0.4563, 30.0500])), - (('10GY', 6.0, 10.0), np.array([0.3086, 0.4949, 30.0500])), - (('2.5G', 6.0, 2.0), np.array([0.3039, 0.3437, 30.0500])), - (('2.5G', 6.0, 4.0), np.array([0.2967, 0.3695, 30.0500])), - (('2.5G', 6.0, 6.0), np.array([0.2892, 0.3963, 30.0500])), - (('2.5G', 6.0, 8.0), np.array([0.2799, 0.4239, 30.0500])), - (('5G', 6.0, 2.0), np.array([0.2988, 0.3382, 30.0500])), - (('5G', 6.0, 4.0), np.array([0.2868, 0.3595, 30.0500])), - (('5G', 6.0, 6.0), np.array([0.2748, 0.3795, 30.0500])), - (('7.5G', 6.0, 2.0), np.array([0.2958, 0.3344, 30.0500])), - (('7.5G', 6.0, 4.0), np.array([0.2807, 0.3522, 30.0500])), - (('7.5G', 6.0, 6.0), np.array([0.2662, 0.3672, 30.0500])), - (('10G', 6.0, 2.0), np.array([0.2929, 0.3303, 30.0500])), - (('10G', 6.0, 4.0), np.array([0.2749, 0.3443, 30.0500])), - (('10G', 6.0, 6.0), np.array([0.2591, 0.3558, 30.0500])), - (('2.5BG', 6.0, 2.0), np.array([0.2902, 0.3268, 30.0500])), - (('2.5BG', 6.0, 4.0), np.array([0.2702, 0.3369, 30.0500])), - (('2.5BG', 6.0, 6.0), np.array([0.2526, 0.3448, 30.0500])), - (('5BG', 6.0, 2.0), np.array([0.2872, 0.3219, 30.0500])), - (('5BG', 6.0, 4.0), np.array([0.2648, 0.3262, 30.0500])), - (('5BG', 6.0, 6.0), np.array([0.2441, 0.3290, 30.0500])), - (('7.5BG', 6.0, 2.0), np.array([0.2849, 0.3172, 30.0500])), - (('7.5BG', 6.0, 4.0), np.array([0.2604, 0.3169, 30.0500])), - (('7.5BG', 6.0, 6.0), np.array([0.2384, 0.3155, 30.0500])), - (('10BG', 6.0, 2.0), np.array([0.2837, 0.3132, 30.0500])), - (('10BG', 6.0, 4.0), np.array([0.2578, 0.3078, 30.0500])), - (('10BG', 6.0, 6.0), np.array([0.2335, 0.3015, 30.0500])), - (('2.5B', 6.0, 2.0), np.array([0.2835, 0.3097, 30.0500])), - (('2.5B', 6.0, 4.0), np.array([0.2571, 0.3008, 30.0500])), - (('2.5B', 6.0, 6.0), np.array([0.2312, 0.2899, 30.0500])), - (('5B', 6.0, 2.0), np.array([0.2842, 0.3063, 30.0500])), - (('5B', 6.0, 4.0), np.array([0.2579, 0.2938, 30.0500])), - (('5B', 6.0, 6.0), np.array([0.2320, 0.2789, 30.0500])), - (('7.5B', 6.0, 2.0), np.array([0.2854, 0.3037, 30.0500])), - (('7.5B', 6.0, 4.0), np.array([0.2602, 0.2881, 30.0500])), - (('7.5B', 6.0, 6.0), np.array([0.2352, 0.2708, 30.0500])), - (('10B', 6.0, 2.0), np.array([0.2871, 0.3012, 30.0500])), - (('10B', 6.0, 4.0), np.array([0.2637, 0.2840, 30.0500])), - (('10B', 6.0, 6.0), np.array([0.2399, 0.2650, 30.0500])), - (('2.5PB', 6.0, 2.0), np.array([0.2897, 0.2991, 30.0500])), - (('2.5PB', 6.0, 4.0), np.array([0.2684, 0.2804, 30.0500])), - (('2.5PB', 6.0, 6.0), np.array([0.2465, 0.2599, 30.0500])), - (('2.5PB', 6.0, 8.0), np.array([0.2274, 0.2406, 30.0500])), - (('5PB', 6.0, 2.0), np.array([0.2923, 0.2978, 30.0500])), - (('5PB', 6.0, 4.0), np.array([0.2734, 0.2778, 30.0500])), - (('5PB', 6.0, 6.0), np.array([0.2533, 0.2558, 30.0500])), - (('5PB', 6.0, 8.0), np.array([0.2360, 0.2365, 30.0500])), - (('7.5PB', 6.0, 2.0), np.array([0.2955, 0.2963, 30.0500])), - (('7.5PB', 6.0, 4.0), np.array([0.2798, 0.2752, 30.0500])), - (('7.5PB', 6.0, 6.0), np.array([0.2638, 0.2531, 30.0500])), - (('7.5PB', 6.0, 8.0), np.array([0.2505, 0.2347, 30.0500])), - (('10PB', 6.0, 2.0), np.array([0.2988, 0.2961, 30.0500])), - (('10PB', 6.0, 4.0), np.array([0.2863, 0.2747, 30.0500])), - (('10PB', 6.0, 6.0), np.array([0.2740, 0.2533, 30.0500])), - (('10PB', 6.0, 8.0), np.array([0.2637, 0.2352, 30.0500])), - (('2.5P', 6.0, 2.0), np.array([0.3016, 0.2960, 30.0500])), - (('2.5P', 6.0, 4.0), np.array([0.2932, 0.2759, 30.0500])), - (('2.5P', 6.0, 6.0), np.array([0.2842, 0.2550, 30.0500])), - (('2.5P', 6.0, 8.0), np.array([0.2770, 0.2372, 30.0500])), - (('5P', 6.0, 2.0), np.array([0.3050, 0.2967, 30.0500])), - (('5P', 6.0, 4.0), np.array([0.3001, 0.2778, 30.0500])), - (('5P', 6.0, 6.0), np.array([0.2950, 0.2585, 30.0500])), - (('5P', 6.0, 8.0), np.array([0.2905, 0.2421, 30.0500])), - (('7.5P', 6.0, 2.0), np.array([0.3107, 0.2993, 30.0500])), - (('7.5P', 6.0, 4.0), np.array([0.3107, 0.2831, 30.0500])), - (('7.5P', 6.0, 6.0), np.array([0.3101, 0.2650, 30.0500])), - (('7.5P', 6.0, 8.0), np.array([0.3099, 0.2502, 30.0500])), - (('10P', 6.0, 2.0), np.array([0.3146, 0.3018, 30.0500])), - (('10P', 6.0, 4.0), np.array([0.3181, 0.2871, 30.0500])), - (('10P', 6.0, 6.0), np.array([0.3226, 0.2716, 30.0500])), - (('10P', 6.0, 8.0), np.array([0.3259, 0.2584, 30.0500])), - (('2.5RP', 6.0, 2.0), np.array([0.3188, 0.3048, 30.0500])), - (('2.5RP', 6.0, 4.0), np.array([0.3272, 0.2929, 30.0500])), - (('2.5RP', 6.0, 6.0), np.array([0.3362, 0.2799, 30.0500])), - (('2.5RP', 6.0, 8.0), np.array([0.3437, 0.2688, 30.0500])), - (('2.5RP', 6.0, 10.0), np.array([0.3509, 0.2578, 30.0500])), - (('5RP', 6.0, 2.0), np.array([0.3232, 0.3085, 30.0500])), - (('5RP', 6.0, 4.0), np.array([0.3371, 0.3001, 30.0500])), - (('5RP', 6.0, 6.0), np.array([0.3520, 0.2904, 30.0500])), - (('5RP', 6.0, 8.0), np.array([0.3648, 0.2820, 30.0500])), - (('5RP', 6.0, 10.0), np.array([0.3769, 0.2738, 30.0500])), - (('7.5RP', 6.0, 2.0), np.array([0.3261, 0.3113, 30.0500])), - (('7.5RP', 6.0, 4.0), np.array([0.3439, 0.3056, 30.0500])), - (('7.5RP', 6.0, 6.0), np.array([0.3635, 0.2987, 30.0500])), - (('7.5RP', 6.0, 8.0), np.array([0.3791, 0.2929, 30.0500])), - (('7.5RP', 6.0, 10.0), np.array([0.3960, 0.2860, 30.0500])), - (('10RP', 7.0, 2.0), np.array([0.3258, 0.3148, 43.0600])), - (('10RP', 7.0, 4.0), np.array([0.3446, 0.3125, 43.0600])), - (('10RP', 7.0, 6.0), np.array([0.3648, 0.3098, 43.0600])), - (('10RP', 7.0, 8.0), np.array([0.3851, 0.3067, 43.0600])), - (('2.5R', 7.0, 2.0), np.array([0.3284, 0.3170, 43.0600])), - (('2.5R', 7.0, 4.0), np.array([0.3499, 0.3171, 43.0600])), - (('2.5R', 7.0, 6.0), np.array([0.3728, 0.3170, 43.0600])), - (('2.5R', 7.0, 8.0), np.array([0.3961, 0.3160, 43.0600])), - (('5R', 7.0, 2.0), np.array([0.3306, 0.3190, 43.0600])), - (('5R', 7.0, 4.0), np.array([0.3552, 0.3222, 43.0600])), - (('5R', 7.0, 6.0), np.array([0.3805, 0.3244, 43.0600])), - (('5R', 7.0, 8.0), np.array([0.4067, 0.3256, 43.0600])), - (('7.5R', 7.0, 2.0), np.array([0.3335, 0.3220, 43.0600])), - (('7.5R', 7.0, 4.0), np.array([0.3611, 0.3282, 43.0600])), - (('7.5R', 7.0, 6.0), np.array([0.3888, 0.3336, 43.0600])), - (('7.5R', 7.0, 8.0), np.array([0.4196, 0.3382, 43.0600])), - (('10R', 7.0, 2.0), np.array([0.3360, 0.3253, 43.0600])), - (('10R', 7.0, 4.0), np.array([0.3671, 0.3360, 43.0600])), - (('10R', 7.0, 6.0), np.array([0.3984, 0.3452, 43.0600])), - (('10R', 7.0, 8.0), np.array([0.4308, 0.3533, 43.0600])), - (('10R', 7.0, 10.0), np.array([0.4600, 0.3596, 43.0600])), - (('2.5YR', 7.0, 2.0), np.array([0.3392, 0.3298, 43.0600])), - (('2.5YR', 7.0, 4.0), np.array([0.3715, 0.3439, 43.0600])), - (('2.5YR', 7.0, 6.0), np.array([0.4053, 0.3570, 43.0600])), - (('2.5YR', 7.0, 8.0), np.array([0.4371, 0.3679, 43.0600])), - (('2.5YR', 7.0, 10.0), np.array([0.4671, 0.3768, 43.0600])), - (('5YR', 7.0, 2.0), np.array([0.3421, 0.3349, 43.0600])), - (('5YR', 7.0, 4.0), np.array([0.3750, 0.3530, 43.0600])), - (('5YR', 7.0, 6.0), np.array([0.4091, 0.3701, 43.0600])), - (('5YR', 7.0, 8.0), np.array([0.4402, 0.3842, 43.0600])), - (('5YR', 7.0, 10.0), np.array([0.4711, 0.3972, 43.0600])), - (('7.5YR', 7.0, 2.0), np.array([0.3437, 0.3397, 43.0600])), - (('7.5YR', 7.0, 4.0), np.array([0.3772, 0.3613, 43.0600])), - (('7.5YR', 7.0, 6.0), np.array([0.4107, 0.3820, 43.0600])), - (('7.5YR', 7.0, 8.0), np.array([0.4415, 0.3996, 43.0600])), - (('7.5YR', 7.0, 10.0), np.array([0.4704, 0.4151, 43.0600])), - (('10YR', 7.0, 2.0), np.array([0.3443, 0.3454, 43.0600])), - (('10YR', 7.0, 4.0), np.array([0.3778, 0.3719, 43.0600])), - (('10YR', 7.0, 6.0), np.array([0.4102, 0.3960, 43.0600])), - (('10YR', 7.0, 8.0), np.array([0.4399, 0.4164, 43.0600])), - (('10YR', 7.0, 10.0), np.array([0.4667, 0.4335, 43.0600])), - (('2.5Y', 7.0, 2.0), np.array([0.3436, 0.3507, 43.0600])), - (('2.5Y', 7.0, 4.0), np.array([0.3761, 0.3800, 43.0600])), - (('2.5Y', 7.0, 6.0), np.array([0.4073, 0.4073, 43.0600])), - (('2.5Y', 7.0, 8.0), np.array([0.4353, 0.4312, 43.0600])), - (('2.5Y', 7.0, 10.0), np.array([0.4606, 0.4516, 43.0600])), - (('5Y', 7.0, 2.0), np.array([0.3419, 0.3540, 43.0600])), - (('5Y', 7.0, 4.0), np.array([0.3718, 0.3885, 43.0600])), - (('5Y', 7.0, 6.0), np.array([0.4009, 0.4198, 43.0600])), - (('5Y', 7.0, 8.0), np.array([0.4271, 0.4462, 43.0600])), - (('5Y', 7.0, 10.0), np.array([0.4509, 0.4696, 43.0600])), - (('7.5Y', 7.0, 2.0), np.array([0.3396, 0.3558, 43.0600])), - (('7.5Y', 7.0, 4.0), np.array([0.3677, 0.3925, 43.0600])), - (('7.5Y', 7.0, 6.0), np.array([0.3943, 0.4264, 43.0600])), - (('7.5Y', 7.0, 8.0), np.array([0.4184, 0.4568, 43.0600])), - (('7.5Y', 7.0, 10.0), np.array([0.4400, 0.4830, 43.0600])), - (('10Y', 7.0, 2.0), np.array([0.3369, 0.3569, 43.0600])), - (('10Y', 7.0, 4.0), np.array([0.3624, 0.3951, 43.0600])), - (('10Y', 7.0, 6.0), np.array([0.3864, 0.4305, 43.0600])), - (('10Y', 7.0, 8.0), np.array([0.4090, 0.4641, 43.0600])), - (('2.5GY', 7.0, 2.0), np.array([0.3328, 0.3569, 43.0600])), - (('2.5GY', 7.0, 4.0), np.array([0.3534, 0.3953, 43.0600])), - (('2.5GY', 7.0, 6.0), np.array([0.3728, 0.4316, 43.0600])), - (('2.5GY', 7.0, 8.0), np.array([0.3919, 0.4684, 43.0600])), - (('2.5GY', 7.0, 10.0), np.array([0.4091, 0.5030, 43.0600])), - (('5GY', 7.0, 2.0), np.array([0.3284, 0.3559, 43.0600])), - (('5GY', 7.0, 4.0), np.array([0.3437, 0.3929, 43.0600])), - (('5GY', 7.0, 6.0), np.array([0.3581, 0.4291, 43.0600])), - (('5GY', 7.0, 8.0), np.array([0.3722, 0.4669, 43.0600])), - (('5GY', 7.0, 10.0), np.array([0.3852, 0.5051, 43.0600])), - (('7.5GY', 7.0, 2.0), np.array([0.3190, 0.3516, 43.0600])), - (('7.5GY', 7.0, 4.0), np.array([0.3267, 0.3848, 43.0600])), - (('7.5GY', 7.0, 6.0), np.array([0.3341, 0.4191, 43.0600])), - (('7.5GY', 7.0, 8.0), np.array([0.3406, 0.4558, 43.0600])), - (('7.5GY', 7.0, 10.0), np.array([0.3461, 0.4950, 43.0600])), - (('10GY', 7.0, 2.0), np.array([0.3117, 0.3469, 43.0600])), - (('10GY', 7.0, 4.0), np.array([0.3133, 0.3764, 43.0600])), - (('10GY', 7.0, 6.0), np.array([0.3142, 0.4058, 43.0600])), - (('10GY', 7.0, 8.0), np.array([0.3140, 0.4387, 43.0600])), - (('2.5G', 7.0, 2.0), np.array([0.3047, 0.3413, 43.0600])), - (('2.5G', 7.0, 4.0), np.array([0.2992, 0.3644, 43.0600])), - (('2.5G', 7.0, 6.0), np.array([0.2933, 0.3873, 43.0600])), - (('2.5G', 7.0, 8.0), np.array([0.2861, 0.4129, 43.0600])), - (('5G', 7.0, 2.0), np.array([0.3001, 0.3366, 43.0600])), - (('5G', 7.0, 4.0), np.array([0.2902, 0.3548, 43.0600])), - (('5G', 7.0, 6.0), np.array([0.2801, 0.3721, 43.0600])), - (('7.5G', 7.0, 2.0), np.array([0.2972, 0.3333, 43.0600])), - (('7.5G', 7.0, 4.0), np.array([0.2850, 0.3482, 43.0600])), - (('7.5G', 7.0, 6.0), np.array([0.2728, 0.3622, 43.0600])), - (('10G', 7.0, 2.0), np.array([0.2945, 0.3297, 43.0600])), - (('10G', 7.0, 4.0), np.array([0.2803, 0.3415, 43.0600])), - (('2.5BG', 7.0, 2.0), np.array([0.2927, 0.3269, 43.0600])), - (('2.5BG', 7.0, 4.0), np.array([0.2764, 0.3354, 43.0600])), - (('5BG', 7.0, 2.0), np.array([0.2898, 0.3225, 43.0600])), - (('5BG', 7.0, 4.0), np.array([0.2712, 0.3269, 43.0600])), - (('7.5BG', 7.0, 2.0), np.array([0.2878, 0.3182, 43.0600])), - (('7.5BG', 7.0, 4.0), np.array([0.2671, 0.3189, 43.0600])), - (('10BG', 7.0, 2.0), np.array([0.2869, 0.3143, 43.0600])), - (('10BG', 7.0, 4.0), np.array([0.2642, 0.3109, 43.0600])), - (('2.5B', 7.0, 2.0), np.array([0.2867, 0.3110, 43.0600])), - (('2.5B', 7.0, 4.0), np.array([0.2629, 0.3038, 43.0600])), - (('2.5B', 7.0, 6.0), np.array([0.2418, 0.2960, 43.0600])), - (('5B', 7.0, 2.0), np.array([0.2875, 0.3078, 43.0600])), - (('5B', 7.0, 4.0), np.array([0.2633, 0.2972, 43.0600])), - (('5B', 7.0, 6.0), np.array([0.2410, 0.2854, 43.0600])), - (('7.5B', 7.0, 2.0), np.array([0.2888, 0.3058, 43.0600])), - (('7.5B', 7.0, 4.0), np.array([0.2651, 0.2927, 43.0600])), - (('7.5B', 7.0, 6.0), np.array([0.2436, 0.2787, 43.0600])), - (('10B', 7.0, 2.0), np.array([0.2908, 0.3039, 43.0600])), - (('10B', 7.0, 4.0), np.array([0.2685, 0.2886, 43.0600])), - (('10B', 7.0, 6.0), np.array([0.2478, 0.2728, 43.0600])), - (('2.5PB', 7.0, 2.0), np.array([0.2932, 0.3025, 43.0600])), - (('2.5PB', 7.0, 4.0), np.array([0.2729, 0.2848, 43.0600])), - (('2.5PB', 7.0, 6.0), np.array([0.2538, 0.2677, 43.0600])), - (('5PB', 7.0, 2.0), np.array([0.2952, 0.3011, 43.0600])), - (('5PB', 7.0, 4.0), np.array([0.2773, 0.2828, 43.0600])), - (('5PB', 7.0, 6.0), np.array([0.2596, 0.2643, 43.0600])), - (('7.5PB', 7.0, 2.0), np.array([0.2982, 0.3003, 43.0600])), - (('7.5PB', 7.0, 4.0), np.array([0.2833, 0.2809, 43.0600])), - (('7.5PB', 7.0, 6.0), np.array([0.2687, 0.2612, 43.0600])), - (('10PB', 7.0, 2.0), np.array([0.3005, 0.3000, 43.0600])), - (('10PB', 7.0, 4.0), np.array([0.2886, 0.2801, 43.0600])), - (('10PB', 7.0, 6.0), np.array([0.2776, 0.2612, 43.0600])), - (('2.5P', 7.0, 2.0), np.array([0.3031, 0.3000, 43.0600])), - (('2.5P', 7.0, 4.0), np.array([0.2950, 0.2810, 43.0600])), - (('2.5P', 7.0, 6.0), np.array([0.2873, 0.2633, 43.0600])), - (('5P', 7.0, 2.0), np.array([0.3059, 0.3010, 43.0600])), - (('5P', 7.0, 4.0), np.array([0.3009, 0.2831, 43.0600])), - (('5P', 7.0, 6.0), np.array([0.2961, 0.2663, 43.0600])), - (('7.5P', 7.0, 2.0), np.array([0.3109, 0.3037, 43.0600])), - (('7.5P', 7.0, 4.0), np.array([0.3111, 0.2880, 43.0600])), - (('7.5P', 7.0, 6.0), np.array([0.3111, 0.2730, 43.0600])), - (('7.5P', 7.0, 8.0), np.array([0.3109, 0.2584, 43.0600])), - (('10P', 7.0, 2.0), np.array([0.3138, 0.3054, 43.0600])), - (('10P', 7.0, 4.0), np.array([0.3181, 0.2920, 43.0600])), - (('10P', 7.0, 6.0), np.array([0.3221, 0.2786, 43.0600])), - (('10P', 7.0, 8.0), np.array([0.3256, 0.2654, 43.0600])), - (('2.5RP', 7.0, 2.0), np.array([0.3170, 0.3076, 43.0600])), - (('2.5RP', 7.0, 4.0), np.array([0.3254, 0.2971, 43.0600])), - (('2.5RP', 7.0, 6.0), np.array([0.3338, 0.2854, 43.0600])), - (('2.5RP', 7.0, 8.0), np.array([0.3417, 0.2745, 43.0600])), - (('5RP', 7.0, 2.0), np.array([0.3206, 0.3104, 43.0600])), - (('5RP', 7.0, 4.0), np.array([0.3332, 0.3032, 43.0600])), - (('5RP', 7.0, 6.0), np.array([0.3470, 0.2949, 43.0600])), - (('5RP', 7.0, 8.0), np.array([0.3603, 0.2869, 43.0600])), - (('7.5RP', 7.0, 2.0), np.array([0.3232, 0.3125, 43.0600])), - (('7.5RP', 7.0, 4.0), np.array([0.3389, 0.3079, 43.0600])), - (('7.5RP', 7.0, 6.0), np.array([0.3562, 0.3022, 43.0600])), - (('7.5RP', 7.0, 8.0), np.array([0.3722, 0.2963, 43.0600])), - (('10RP', 8.0, 2.0), np.array([0.3218, 0.3152, 59.1000])), - (('10RP', 8.0, 4.0), np.array([0.3412, 0.3135, 59.1000])), - (('10RP', 8.0, 6.0), np.array([0.3600, 0.3112, 59.1000])), - (('2.5R', 8.0, 2.0), np.array([0.3236, 0.3169, 59.1000])), - (('2.5R', 8.0, 4.0), np.array([0.3460, 0.3177, 59.1000])), - (('2.5R', 8.0, 6.0), np.array([0.3671, 0.3175, 59.1000])), - (('5R', 8.0, 2.0), np.array([0.3254, 0.3186, 59.1000])), - (('5R', 8.0, 4.0), np.array([0.3510, 0.3224, 59.1000])), - (('7.5R', 8.0, 2.0), np.array([0.3277, 0.3211, 59.1000])), - (('7.5R', 8.0, 4.0), np.array([0.3564, 0.3279, 59.1000])), - (('10R', 8.0, 2.0), np.array([0.3301, 0.3237, 59.1000])), - (('10R', 8.0, 4.0), np.array([0.3621, 0.3349, 59.1000])), - (('2.5YR', 8.0, 2.0), np.array([0.3334, 0.3276, 59.1000])), - (('2.5YR', 8.0, 4.0), np.array([0.3667, 0.3429, 59.1000])), - (('5YR', 8.0, 2.0), np.array([0.3373, 0.3330, 59.1000])), - (('5YR', 8.0, 4.0), np.array([0.3690, 0.3510, 59.1000])), - (('7.5YR', 8.0, 2.0), np.array([0.3395, 0.3379, 59.1000])), - (('7.5YR', 8.0, 4.0), np.array([0.3699, 0.3586, 59.1000])), - (('7.5YR', 8.0, 6.0), np.array([0.4000, 0.3770, 59.1000])), - (('10YR', 8.0, 2.0), np.array([0.3407, 0.3434, 59.1000])), - (('10YR', 8.0, 4.0), np.array([0.3701, 0.3674, 59.1000])), - (('10YR', 8.0, 6.0), np.array([0.3994, 0.3896, 59.1000])), - (('2.5Y', 8.0, 2.0), np.array([0.3406, 0.3484, 59.1000])), - (('2.5Y', 8.0, 4.0), np.array([0.3684, 0.3751, 59.1000])), - (('2.5Y', 8.0, 6.0), np.array([0.3969, 0.4009, 59.1000])), - (('2.5Y', 8.0, 8.0), np.array([0.4231, 0.4231, 59.1000])), - (('5Y', 8.0, 2.0), np.array([0.3394, 0.3518, 59.1000])), - (('5Y', 8.0, 4.0), np.array([0.3650, 0.3826, 59.1000])), - (('5Y', 8.0, 6.0), np.array([0.3913, 0.4117, 59.1000])), - (('5Y', 8.0, 8.0), np.array([0.4158, 0.4378, 59.1000])), - (('5Y', 8.0, 10.0), np.array([0.4376, 0.4601, 59.1000])), - (('5Y', 8.0, 12.0), np.array([0.4562, 0.4788, 59.1000])), - (('7.5Y', 8.0, 2.0), np.array([0.3379, 0.3540, 59.1000])), - (('7.5Y', 8.0, 4.0), np.array([0.3622, 0.3861, 59.1000])), - (('7.5Y', 8.0, 6.0), np.array([0.3862, 0.4175, 59.1000])), - (('7.5Y', 8.0, 8.0), np.array([0.4088, 0.4466, 59.1000])), - (('7.5Y', 8.0, 10.0), np.array([0.4283, 0.4712, 59.1000])), - (('10Y', 8.0, 2.0), np.array([0.3359, 0.3552, 59.1000])), - (('10Y', 8.0, 4.0), np.array([0.3581, 0.3883, 59.1000])), - (('10Y', 8.0, 6.0), np.array([0.3803, 0.4216, 59.1000])), - (('10Y', 8.0, 8.0), np.array([0.4008, 0.4520, 59.1000])), - (('2.5GY', 8.0, 2.0), np.array([0.3327, 0.3555, 59.1000])), - (('2.5GY', 8.0, 4.0), np.array([0.3504, 0.3887, 59.1000])), - (('2.5GY', 8.0, 6.0), np.array([0.3690, 0.4230, 59.1000])), - (('2.5GY', 8.0, 8.0), np.array([0.3858, 0.4550, 59.1000])), - (('5GY', 8.0, 2.0), np.array([0.3284, 0.3542, 59.1000])), - (('5GY', 8.0, 4.0), np.array([0.3433, 0.3872, 59.1000])), - (('5GY', 8.0, 6.0), np.array([0.3573, 0.4214, 59.1000])), - (('5GY', 8.0, 8.0), np.array([0.3696, 0.4542, 59.1000])), - (('7.5GY', 8.0, 2.0), np.array([0.3194, 0.3502, 59.1000])), - (('7.5GY', 8.0, 4.0), np.array([0.3266, 0.3809, 59.1000])), - (('7.5GY', 8.0, 6.0), np.array([0.3339, 0.4129, 59.1000])), - (('7.5GY', 8.0, 8.0), np.array([0.3408, 0.4452, 59.1000])), - (('10GY', 8.0, 2.0), np.array([0.3121, 0.3459, 59.1000])), - (('10GY', 8.0, 4.0), np.array([0.3140, 0.3727, 59.1000])), - (('10GY', 8.0, 6.0), np.array([0.3150, 0.4014, 59.1000])), - (('2.5G', 8.0, 2.0), np.array([0.3053, 0.3404, 59.1000])), - (('2.5G', 8.0, 4.0), np.array([0.3009, 0.3614, 59.1000])), - (('2.5G', 8.0, 6.0), np.array([0.2952, 0.3851, 59.1000])), - (('5G', 8.0, 2.0), np.array([0.3009, 0.3359, 59.1000])), - (('5G', 8.0, 4.0), np.array([0.2924, 0.3523, 59.1000])), - (('5G', 8.0, 6.0), np.array([0.2822, 0.3702, 59.1000])), - (('7.5G', 8.0, 2.0), np.array([0.2981, 0.3326, 59.1000])), - (('7.5G', 8.0, 4.0), np.array([0.2874, 0.3464, 59.1000])), - (('10G', 8.0, 2.0), np.array([0.2957, 0.3293, 59.1000])), - (('2.5BG', 8.0, 2.0), np.array([0.2940, 0.3268, 59.1000])), - (('5BG', 8.0, 2.0), np.array([0.2919, 0.3228, 59.1000])), - (('7.5BG', 8.0, 2.0), np.array([0.2900, 0.3183, 59.1000])), - (('10BG', 8.0, 2.0), np.array([0.2894, 0.3152, 59.1000])), - (('2.5B', 8.0, 2.0), np.array([0.2897, 0.3124, 59.1000])), - (('2.5B', 8.0, 4.0), np.array([0.2668, 0.3067, 59.1000])), - (('5B', 8.0, 2.0), np.array([0.2908, 0.3096, 59.1000])), - (('5B', 8.0, 4.0), np.array([0.2671, 0.2998, 59.1000])), - (('7.5B', 8.0, 2.0), np.array([0.2922, 0.3077, 59.1000])), - (('7.5B', 8.0, 4.0), np.array([0.2688, 0.2956, 59.1000])), - (('10B', 8.0, 2.0), np.array([0.2935, 0.3062, 59.1000])), - (('10B', 8.0, 4.0), np.array([0.2718, 0.2911, 59.1000])), - (('2.5PB', 8.0, 2.0), np.array([0.2957, 0.3047, 59.1000])), - (('2.5PB', 8.0, 4.0), np.array([0.2758, 0.2879, 59.1000])), - (('5PB', 8.0, 2.0), np.array([0.2974, 0.3039, 59.1000])), - (('7.5PB', 8.0, 2.0), np.array([0.3003, 0.3034, 59.1000])), - (('10PB', 8.0, 2.0), np.array([0.3027, 0.3035, 59.1000])), - (('2.5P', 8.0, 2.0), np.array([0.3048, 0.3040, 59.1000])), - (('2.5P', 8.0, 4.0), np.array([0.2962, 0.2850, 59.1000])), - (('5P', 8.0, 2.0), np.array([0.3065, 0.3047, 59.1000])), - (('5P', 8.0, 4.0), np.array([0.3012, 0.2868, 59.1000])), - (('7.5P', 8.0, 2.0), np.array([0.3107, 0.3070, 59.1000])), - (('7.5P', 8.0, 4.0), np.array([0.3114, 0.2915, 59.1000])), - (('10P', 8.0, 2.0), np.array([0.3131, 0.3084, 59.1000])), - (('10P', 8.0, 4.0), np.array([0.3175, 0.2955, 59.1000])), - (('2.5RP', 8.0, 2.0), np.array([0.3154, 0.3100, 59.1000])), - (('2.5RP', 8.0, 4.0), np.array([0.3239, 0.3000, 59.1000])), - (('2.5RP', 8.0, 6.0), np.array([0.3327, 0.2898, 59.1000])), - (('5RP', 8.0, 2.0), np.array([0.3180, 0.3120, 59.1000])), - (('5RP', 8.0, 4.0), np.array([0.3308, 0.3052, 59.1000])), - (('5RP', 8.0, 6.0), np.array([0.3440, 0.2978, 59.1000])), - (('7.5RP', 8.0, 2.0), np.array([0.3200, 0.3136, 59.1000])), - (('7.5RP', 8.0, 4.0), np.array([0.3360, 0.3092, 59.1000])), - (('7.5RP', 8.0, 6.0), np.array([0.3521, 0.3042, 59.1000]))) +MUNSELL_COLOURS_1929: Tuple = ( + (("10RP", 2.0, 2.0), np.array([0.3532, 0.2957, 3.1260])), + (("10RP", 2.0, 4.0), np.array([0.3850, 0.2778, 3.1260])), + (("10RP", 2.0, 6.0), np.array([0.4139, 0.2608, 3.1260])), + (("10RP", 2.0, 8.0), np.array([0.4428, 0.2419, 3.1260])), + (("2.5R", 2.0, 2.0), np.array([0.3614, 0.3033, 3.1260])), + (("2.5R", 2.0, 4.0), np.array([0.4021, 0.2900, 3.1260])), + (("2.5R", 2.0, 6.0), np.array([0.4390, 0.2760, 3.1260])), + (("5R", 2.0, 2.0), np.array([0.3692, 0.3111, 3.1260])), + (("5R", 2.0, 4.0), np.array([0.4184, 0.3032, 3.1260])), + (("5R", 2.0, 6.0), np.array([0.4642, 0.2934, 3.1260])), + (("7.5R", 2.0, 2.0), np.array([0.3751, 0.3181, 3.1260])), + (("7.5R", 2.0, 4.0), np.array([0.4335, 0.3169, 3.1260])), + (("10R", 2.0, 2.0), np.array([0.3811, 0.3274, 3.1260])), + (("2.5YR", 2.0, 2.0), np.array([0.3852, 0.3365, 3.1260])), + (("5YR", 2.0, 2.0), np.array([0.3880, 0.3476, 3.1260])), + (("7.5YR", 2.0, 2.0), np.array([0.3889, 0.3590, 3.1260])), + (("10YR", 2.0, 2.0), np.array([0.3872, 0.3688, 3.1260])), + (("2.5Y", 2.0, 2.0), np.array([0.3825, 0.3785, 3.1260])), + (("5Y", 2.0, 2.0), np.array([0.3757, 0.3839, 3.1260])), + (("7.5Y", 2.0, 2.0), np.array([0.3660, 0.3858, 3.1260])), + (("10Y", 2.0, 2.0), np.array([0.3556, 0.3848, 3.1260])), + (("2.5GY", 2.0, 2.0), np.array([0.3421, 0.3803, 3.1260])), + (("5GY", 2.0, 2.0), np.array([0.3309, 0.3743, 3.1260])), + (("7.5GY", 2.0, 2.0), np.array([0.3165, 0.3650, 3.1260])), + (("10GY", 2.0, 2.0), np.array([0.3069, 0.3580, 3.1260])), + (("2.5G", 2.0, 2.0), np.array([0.2978, 0.3507, 3.1260])), + (("5G", 2.0, 2.0), np.array([0.2918, 0.3450, 3.1260])), + (("7.5G", 2.0, 2.0), np.array([0.2869, 0.3400, 3.1260])), + (("10G", 2.0, 2.0), np.array([0.2820, 0.3341, 3.1260])), + (("2.5BG", 2.0, 2.0), np.array([0.2765, 0.3271, 3.1260])), + (("2.5BG", 2.0, 4.0), np.array([0.2343, 0.3378, 3.1260])), + (("5BG", 2.0, 2.0), np.array([0.2697, 0.3175, 3.1260])), + (("5BG", 2.0, 4.0), np.array([0.2234, 0.3150, 3.1260])), + (("7.5BG", 2.0, 2.0), np.array([0.2651, 0.3098, 3.1260])), + (("7.5BG", 2.0, 4.0), np.array([0.2162, 0.2981, 3.1260])), + (("10BG", 2.0, 2.0), np.array([0.2606, 0.3010, 3.1260])), + (("2.5B", 2.0, 2.0), np.array([0.2578, 0.2940, 3.1260])), + (("5B", 2.0, 2.0), np.array([0.2559, 0.2874, 3.1260])), + (("7.5B", 2.0, 2.0), np.array([0.2545, 0.2799, 3.1260])), + (("7.5B", 2.0, 4.0), np.array([0.2063, 0.2400, 3.1260])), + (("10B", 2.0, 2.0), np.array([0.2558, 0.2725, 3.1260])), + (("10B", 2.0, 4.0), np.array([0.2102, 0.2313, 3.1260])), + (("2.5PB", 2.0, 2.0), np.array([0.2592, 0.2675, 3.1260])), + (("2.5PB", 2.0, 4.0), np.array([0.2175, 0.2245, 3.1260])), + (("2.5PB", 2.0, 6.0), np.array([0.1825, 0.1857, 3.1260])), + (("5PB", 2.0, 2.0), np.array([0.2638, 0.2624, 3.1260])), + (("5PB", 2.0, 4.0), np.array([0.2263, 0.2192, 3.1260])), + (("5PB", 2.0, 6.0), np.array([0.1942, 0.1811, 3.1260])), + (("7.5PB", 2.0, 2.0), np.array([0.2712, 0.2582, 3.1260])), + (("7.5PB", 2.0, 4.0), np.array([0.2420, 0.2148, 3.1260])), + (("7.5PB", 2.0, 6.0), np.array([0.2189, 0.1790, 3.1260])), + (("10PB", 2.0, 2.0), np.array([0.2803, 0.2567, 3.1260])), + (("10PB", 2.0, 4.0), np.array([0.2600, 0.2162, 3.1260])), + (("10PB", 2.0, 6.0), np.array([0.2440, 0.1840, 3.1260])), + (("2.5P", 2.0, 2.0), np.array([0.2892, 0.2583, 3.1260])), + (("2.5P", 2.0, 4.0), np.array([0.2758, 0.2208, 3.1260])), + (("2.5P", 2.0, 6.0), np.array([0.2661, 0.1921, 3.1260])), + (("5P", 2.0, 2.0), np.array([0.2984, 0.2612, 3.1260])), + (("5P", 2.0, 4.0), np.array([0.2908, 0.2261, 3.1260])), + (("5P", 2.0, 6.0), np.array([0.2850, 0.1992, 3.1260])), + (("7.5P", 2.0, 2.0), np.array([0.3071, 0.2647, 3.1260])), + (("7.5P", 2.0, 4.0), np.array([0.3048, 0.2321, 3.1260])), + (("7.5P", 2.0, 6.0), np.array([0.3025, 0.2058, 3.1260])), + (("10P", 2.0, 2.0), np.array([0.3161, 0.2691, 3.1260])), + (("10P", 2.0, 4.0), np.array([0.3189, 0.2390, 3.1260])), + (("10P", 2.0, 6.0), np.array([0.3207, 0.2132, 3.1260])), + (("2.5RP", 2.0, 2.0), np.array([0.3279, 0.2754, 3.1260])), + (("2.5RP", 2.0, 4.0), np.array([0.3382, 0.2496, 3.1260])), + (("2.5RP", 2.0, 6.0), np.array([0.3470, 0.2259, 3.1260])), + (("5RP", 2.0, 2.0), np.array([0.3383, 0.2829, 3.1260])), + (("5RP", 2.0, 4.0), np.array([0.3558, 0.2597, 3.1260])), + (("5RP", 2.0, 6.0), np.array([0.3708, 0.2380, 3.1260])), + (("7.5RP", 2.0, 2.0), np.array([0.3459, 0.2892, 3.1260])), + (("7.5RP", 2.0, 4.0), np.array([0.3702, 0.2683, 3.1260])), + (("7.5RP", 2.0, 6.0), np.array([0.3918, 0.2490, 3.1260])), + (("10RP", 3.0, 2.0), np.array([0.3526, 0.3068, 6.5550])), + (("10RP", 3.0, 4.0), np.array([0.3889, 0.2969, 6.5550])), + (("10RP", 3.0, 6.0), np.array([0.4218, 0.2864, 6.5550])), + (("10RP", 3.0, 8.0), np.array([0.4552, 0.2741, 6.5550])), + (("10RP", 3.0, 10.0), np.array([0.4851, 0.2618, 6.5550])), + (("2.5R", 3.0, 2.0), np.array([0.3591, 0.3130, 6.5550])), + (("2.5R", 3.0, 4.0), np.array([0.4021, 0.3076, 6.5550])), + (("2.5R", 3.0, 6.0), np.array([0.4409, 0.3009, 6.5550])), + (("2.5R", 3.0, 8.0), np.array([0.4821, 0.2918, 6.5550])), + (("2.5R", 3.0, 10.0), np.array([0.5191, 0.2811, 6.5550])), + (("5R", 3.0, 2.0), np.array([0.3645, 0.3190, 6.5550])), + (("5R", 3.0, 4.0), np.array([0.4148, 0.3190, 6.5550])), + (("5R", 3.0, 6.0), np.array([0.4592, 0.3168, 6.5550])), + (("5R", 3.0, 8.0), np.array([0.5064, 0.3114, 6.5550])), + (("5R", 3.0, 10.0), np.array([0.5500, 0.3024, 6.5550])), + (("7.5R", 3.0, 2.0), np.array([0.3690, 0.3248, 6.5550])), + (("7.5R", 3.0, 4.0), np.array([0.4240, 0.3302, 6.5550])), + (("7.5R", 3.0, 6.0), np.array([0.4738, 0.3316, 6.5550])), + (("7.5R", 3.0, 8.0), np.array([0.5251, 0.3297, 6.5550])), + (("10R", 3.0, 2.0), np.array([0.3728, 0.3314, 6.5550])), + (("10R", 3.0, 4.0), np.array([0.4308, 0.3412, 6.5550])), + (("10R", 3.0, 6.0), np.array([0.4854, 0.3467, 6.5550])), + (("2.5YR", 3.0, 2.0), np.array([0.3757, 0.3391, 6.5550])), + (("2.5YR", 3.0, 4.0), np.array([0.4360, 0.3563, 6.5550])), + (("2.5YR", 3.0, 6.0), np.array([0.4954, 0.3692, 6.5550])), + (("5YR", 3.0, 2.0), np.array([0.3771, 0.3476, 6.5550])), + (("5YR", 3.0, 4.0), np.array([0.4376, 0.3715, 6.5550])), + (("7.5YR", 3.0, 2.0), np.array([0.3771, 0.3549, 6.5550])), + (("7.5YR", 3.0, 4.0), np.array([0.4378, 0.3865, 6.5550])), + (("10YR", 3.0, 2.0), np.array([0.3747, 0.3630, 6.5550])), + (("10YR", 3.0, 4.0), np.array([0.4341, 0.4018, 6.5550])), + (("2.5Y", 3.0, 2.0), np.array([0.3703, 0.3700, 6.5550])), + (("2.5Y", 3.0, 4.0), np.array([0.4277, 0.4166, 6.5550])), + (("5Y", 3.0, 2.0), np.array([0.3646, 0.3748, 6.5550])), + (("7.5Y", 3.0, 2.0), np.array([0.3589, 0.3778, 6.5550])), + (("10Y", 3.0, 2.0), np.array([0.3513, 0.3789, 6.5550])), + (("2.5GY", 3.0, 2.0), np.array([0.3412, 0.3768, 6.5550])), + (("2.5GY", 3.0, 4.0), np.array([0.3772, 0.4484, 6.5550])), + (("5GY", 3.0, 2.0), np.array([0.3319, 0.3729, 6.5550])), + (("5GY", 3.0, 4.0), np.array([0.3554, 0.4429, 6.5550])), + (("7.5GY", 3.0, 2.0), np.array([0.3180, 0.3644, 6.5550])), + (("7.5GY", 3.0, 4.0), np.array([0.3270, 0.4288, 6.5550])), + (("10GY", 3.0, 2.0), np.array([0.3088, 0.3578, 6.5550])), + (("10GY", 3.0, 4.0), np.array([0.3053, 0.4123, 6.5550])), + (("2.5G", 3.0, 2.0), np.array([0.2999, 0.3500, 6.5550])), + (("2.5G", 3.0, 4.0), np.array([0.2836, 0.3915, 6.5550])), + (("5G", 3.0, 2.0), np.array([0.2935, 0.3439, 6.5550])), + (("5G", 3.0, 4.0), np.array([0.2711, 0.3780, 6.5550])), + (("7.5G", 3.0, 2.0), np.array([0.2890, 0.3391, 6.5550])), + (("7.5G", 3.0, 4.0), np.array([0.2618, 0.3667, 6.5550])), + (("10G", 3.0, 2.0), np.array([0.2844, 0.3337, 6.5550])), + (("10G", 3.0, 4.0), np.array([0.2525, 0.3537, 6.5550])), + (("2.5BG", 3.0, 2.0), np.array([0.2799, 0.3271, 6.5550])), + (("2.5BG", 3.0, 4.0), np.array([0.2437, 0.3386, 6.5550])), + (("2.5BG", 3.0, 6.0), np.array([0.2132, 0.3468, 6.5550])), + (("5BG", 3.0, 2.0), np.array([0.2742, 0.3192, 6.5550])), + (("5BG", 3.0, 4.0), np.array([0.2343, 0.3200, 6.5550])), + (("5BG", 3.0, 6.0), np.array([0.2020, 0.3188, 6.5550])), + (("7.5BG", 3.0, 2.0), np.array([0.2699, 0.3120, 6.5550])), + (("7.5BG", 3.0, 4.0), np.array([0.2272, 0.3041, 6.5550])), + (("7.5BG", 3.0, 6.0), np.array([0.1928, 0.2958, 6.5550])), + (("10BG", 3.0, 2.0), np.array([0.2660, 0.3050, 6.5550])), + (("10BG", 3.0, 4.0), np.array([0.2221, 0.2886, 6.5550])), + (("10BG", 3.0, 6.0), np.array([0.1861, 0.2722, 6.5550])), + (("2.5B", 3.0, 2.0), np.array([0.2636, 0.2983, 6.5550])), + (("2.5B", 3.0, 4.0), np.array([0.2183, 0.2748, 6.5550])), + (("2.5B", 3.0, 6.0), np.array([0.1826, 0.2536, 6.5550])), + (("5B", 3.0, 2.0), np.array([0.2617, 0.2921, 6.5550])), + (("5B", 3.0, 4.0), np.array([0.2176, 0.2632, 6.5550])), + (("5B", 3.0, 6.0), np.array([0.1835, 0.2375, 6.5550])), + (("7.5B", 3.0, 2.0), np.array([0.2616, 0.2857, 6.5550])), + (("7.5B", 3.0, 4.0), np.array([0.2200, 0.2536, 6.5550])), + (("7.5B", 3.0, 6.0), np.array([0.1875, 0.2258, 6.5550])), + (("7.5B", 3.0, 8.0), np.array([0.1583, 0.1987, 6.5550])), + (("10B", 3.0, 2.0), np.array([0.2631, 0.2801, 6.5550])), + (("10B", 3.0, 4.0), np.array([0.2246, 0.2467, 6.5550])), + (("10B", 3.0, 6.0), np.array([0.1933, 0.2173, 6.5550])), + (("10B", 3.0, 8.0), np.array([0.1658, 0.1905, 6.5550])), + (("2.5PB", 3.0, 2.0), np.array([0.2663, 0.2756, 6.5550])), + (("2.5PB", 3.0, 4.0), np.array([0.2312, 0.2405, 6.5550])), + (("2.5PB", 3.0, 6.0), np.array([0.2022, 0.2101, 6.5550])), + (("2.5PB", 3.0, 8.0), np.array([0.1780, 0.1833, 6.5550])), + (("2.5PB", 3.0, 10.0), np.array([0.1576, 0.1600, 6.5550])), + (("5PB", 3.0, 2.0), np.array([0.2708, 0.2719, 6.5550])), + (("5PB", 3.0, 4.0), np.array([0.2393, 0.2361, 6.5550])), + (("5PB", 3.0, 6.0), np.array([0.2122, 0.2052, 6.5550])), + (("5PB", 3.0, 8.0), np.array([0.1908, 0.1799, 6.5550])), + (("5PB", 3.0, 10.0), np.array([0.1718, 0.1562, 6.5550])), + (("5PB", 3.0, 12.0), np.array([0.1557, 0.1356, 6.5550])), + (("7.5PB", 3.0, 2.0), np.array([0.2777, 0.2687, 6.5550])), + (("7.5PB", 3.0, 4.0), np.array([0.2520, 0.2319, 6.5550])), + (("7.5PB", 3.0, 6.0), np.array([0.2311, 0.2010, 6.5550])), + (("7.5PB", 3.0, 8.0), np.array([0.2149, 0.1761, 6.5550])), + (("7.5PB", 3.0, 10.0), np.array([0.2005, 0.1536, 6.5550])), + (("10PB", 3.0, 2.0), np.array([0.2847, 0.2670, 6.5550])), + (("10PB", 3.0, 4.0), np.array([0.2660, 0.2319, 6.5550])), + (("10PB", 3.0, 6.0), np.array([0.2511, 0.2031, 6.5550])), + (("10PB", 3.0, 8.0), np.array([0.2387, 0.1786, 6.5550])), + (("10PB", 3.0, 10.0), np.array([0.2278, 0.1565, 6.5550])), + (("2.5P", 3.0, 2.0), np.array([0.2922, 0.2680, 6.5550])), + (("2.5P", 3.0, 4.0), np.array([0.2792, 0.2342, 6.5550])), + (("2.5P", 3.0, 6.0), np.array([0.2691, 0.2072, 6.5550])), + (("2.5P", 3.0, 8.0), np.array([0.2615, 0.1845, 6.5550])), + (("2.5P", 3.0, 10.0), np.array([0.2548, 0.1638, 6.5550])), + (("5P", 3.0, 2.0), np.array([0.2997, 0.2700, 6.5550])), + (("5P", 3.0, 4.0), np.array([0.2928, 0.2386, 6.5550])), + (("5P", 3.0, 6.0), np.array([0.2870, 0.2135, 6.5550])), + (("5P", 3.0, 8.0), np.array([0.2819, 0.1910, 6.5550])), + (("5P", 3.0, 10.0), np.array([0.2772, 0.1707, 6.5550])), + (("7.5P", 3.0, 2.0), np.array([0.3088, 0.2740, 6.5550])), + (("7.5P", 3.0, 4.0), np.array([0.3072, 0.2448, 6.5550])), + (("7.5P", 3.0, 6.0), np.array([0.3057, 0.2208, 6.5550])), + (("7.5P", 3.0, 8.0), np.array([0.3037, 0.1981, 6.5550])), + (("7.5P", 3.0, 10.0), np.array([0.3020, 0.1794, 6.5550])), + (("10P", 3.0, 2.0), np.array([0.3170, 0.2790, 6.5550])), + (("10P", 3.0, 4.0), np.array([0.3214, 0.2517, 6.5550])), + (("10P", 3.0, 6.0), np.array([0.3243, 0.2293, 6.5550])), + (("10P", 3.0, 8.0), np.array([0.3269, 0.2075, 6.5550])), + (("10P", 3.0, 10.0), np.array([0.3286, 0.1889, 6.5550])), + (("2.5RP", 3.0, 2.0), np.array([0.3272, 0.2861, 6.5550])), + (("2.5RP", 3.0, 4.0), np.array([0.3400, 0.2624, 6.5550])), + (("2.5RP", 3.0, 6.0), np.array([0.3501, 0.2425, 6.5550])), + (("2.5RP", 3.0, 8.0), np.array([0.3598, 0.2233, 6.5550])), + (("2.5RP", 3.0, 10.0), np.array([0.3681, 0.2054, 6.5550])), + (("5RP", 3.0, 2.0), np.array([0.3370, 0.2940, 6.5550])), + (("5RP", 3.0, 4.0), np.array([0.3586, 0.2742, 6.5550])), + (("5RP", 3.0, 6.0), np.array([0.3765, 0.2569, 6.5550])), + (("5RP", 3.0, 8.0), np.array([0.3930, 0.2395, 6.5550])), + (("5RP", 3.0, 10.0), np.array([0.4073, 0.2235, 6.5550])), + (("7.5RP", 3.0, 2.0), np.array([0.3450, 0.3001, 6.5550])), + (("7.5RP", 3.0, 4.0), np.array([0.3739, 0.2851, 6.5550])), + (("7.5RP", 3.0, 6.0), np.array([0.3990, 0.2708, 6.5550])), + (("7.5RP", 3.0, 8.0), np.array([0.4234, 0.2556, 6.5550])), + (("7.5RP", 3.0, 10.0), np.array([0.4445, 0.2419, 6.5550])), + (("10RP", 4.0, 2.0), np.array([0.3417, 0.3106, 12.0000])), + (("10RP", 4.0, 4.0), np.array([0.3715, 0.3042, 12.0000])), + (("10RP", 4.0, 6.0), np.array([0.3999, 0.2972, 12.0000])), + (("10RP", 4.0, 8.0), np.array([0.4282, 0.2890, 12.0000])), + (("10RP", 4.0, 10.0), np.array([0.4528, 0.2811, 12.0000])), + (("2.5R", 4.0, 2.0), np.array([0.3461, 0.3150, 12.0000])), + (("2.5R", 4.0, 4.0), np.array([0.3806, 0.3125, 12.0000])), + (("2.5R", 4.0, 6.0), np.array([0.4141, 0.3085, 12.0000])), + (("2.5R", 4.0, 8.0), np.array([0.4472, 0.3031, 12.0000])), + (("2.5R", 4.0, 10.0), np.array([0.4774, 0.2969, 12.0000])), + (("2.5R", 4.0, 12.0), np.array([0.5072, 0.2897, 12.0000])), + (("5R", 4.0, 2.0), np.array([0.3508, 0.3200, 12.0000])), + (("5R", 4.0, 4.0), np.array([0.3916, 0.3223, 12.0000])), + (("5R", 4.0, 6.0), np.array([0.4299, 0.3226, 12.0000])), + (("5R", 4.0, 8.0), np.array([0.4690, 0.3209, 12.0000])), + (("5R", 4.0, 10.0), np.array([0.5043, 0.3176, 12.0000])), + (("5R", 4.0, 12.0), np.array([0.5385, 0.3129, 12.0000])), + (("5R", 4.0, 14.0), np.array([0.5734, 0.3057, 12.0000])), + (("7.5R", 4.0, 2.0), np.array([0.3538, 0.3236, 12.0000])), + (("7.5R", 4.0, 4.0), np.array([0.3990, 0.3300, 12.0000])), + (("7.5R", 4.0, 6.0), np.array([0.4415, 0.3340, 12.0000])), + (("7.5R", 4.0, 8.0), np.array([0.4850, 0.3359, 12.0000])), + (("7.5R", 4.0, 10.0), np.array([0.5235, 0.3351, 12.0000])), + (("7.5R", 4.0, 12.0), np.array([0.5603, 0.3321, 12.0000])), + (("10R", 4.0, 2.0), np.array([0.3582, 0.3294, 12.0000])), + (("10R", 4.0, 4.0), np.array([0.4078, 0.3412, 12.0000])), + (("10R", 4.0, 6.0), np.array([0.4535, 0.3500, 12.0000])), + (("10R", 4.0, 8.0), np.array([0.4995, 0.3557, 12.0000])), + (("10R", 4.0, 10.0), np.array([0.5418, 0.3580, 12.0000])), + (("2.5YR", 4.0, 2.0), np.array([0.3624, 0.3367, 12.0000])), + (("2.5YR", 4.0, 4.0), np.array([0.4141, 0.3539, 12.0000])), + (("2.5YR", 4.0, 6.0), np.array([0.4612, 0.3674, 12.0000])), + (("2.5YR", 4.0, 8.0), np.array([0.5071, 0.3777, 12.0000])), + (("2.5YR", 4.0, 10.0), np.array([0.5475, 0.3856, 12.0000])), + (("5YR", 4.0, 2.0), np.array([0.3651, 0.3442, 12.0000])), + (("5YR", 4.0, 4.0), np.array([0.4187, 0.3679, 12.0000])), + (("5YR", 4.0, 6.0), np.array([0.4651, 0.3859, 12.0000])), + (("5YR", 4.0, 8.0), np.array([0.5070, 0.3994, 12.0000])), + (("7.5YR", 4.0, 2.0), np.array([0.3662, 0.3504, 12.0000])), + (("7.5YR", 4.0, 4.0), np.array([0.4208, 0.3809, 12.0000])), + (("7.5YR", 4.0, 6.0), np.array([0.4655, 0.4029, 12.0000])), + (("7.5YR", 4.0, 8.0), np.array([0.5038, 0.4204, 12.0000])), + (("10YR", 4.0, 2.0), np.array([0.3660, 0.3590, 12.0000])), + (("10YR", 4.0, 4.0), np.array([0.4189, 0.3948, 12.0000])), + (("10YR", 4.0, 6.0), np.array([0.4618, 0.4213, 12.0000])), + (("2.5Y", 4.0, 2.0), np.array([0.3633, 0.3654, 12.0000])), + (("2.5Y", 4.0, 4.0), np.array([0.4138, 0.4076, 12.0000])), + (("2.5Y", 4.0, 6.0), np.array([0.4542, 0.4391, 12.0000])), + (("5Y", 4.0, 2.0), np.array([0.3590, 0.3701, 12.0000])), + (("5Y", 4.0, 4.0), np.array([0.4069, 0.4188, 12.0000])), + (("7.5Y", 4.0, 2.0), np.array([0.3542, 0.3727, 12.0000])), + (("7.5Y", 4.0, 4.0), np.array([0.3982, 0.4272, 12.0000])), + (("10Y", 4.0, 2.0), np.array([0.3436, 0.3732, 12.0000])), + (("10Y", 4.0, 4.0), np.array([0.3871, 0.4321, 12.0000])), + (("2.5GY", 4.0, 2.0), np.array([0.3382, 0.3706, 12.0000])), + (("2.5GY", 4.0, 4.0), np.array([0.3708, 0.4329, 12.0000])), + (("2.5GY", 4.0, 6.0), np.array([0.3968, 0.4857, 12.0000])), + (("5GY", 4.0, 2.0), np.array([0.3312, 0.3678, 12.0000])), + (("5GY", 4.0, 4.0), np.array([0.3538, 0.4284, 12.0000])), + (("5GY", 4.0, 6.0), np.array([0.3718, 0.4852, 12.0000])), + (("7.5GY", 4.0, 2.0), np.array([0.3185, 0.3604, 12.0000])), + (("7.5GY", 4.0, 4.0), np.array([0.3281, 0.4157, 12.0000])), + (("7.5GY", 4.0, 6.0), np.array([0.3355, 0.4739, 12.0000])), + (("10GY", 4.0, 2.0), np.array([0.3109, 0.3550, 12.0000])), + (("10GY", 4.0, 4.0), np.array([0.3100, 0.4018, 12.0000])), + (("10GY", 4.0, 6.0), np.array([0.3069, 0.4550, 12.0000])), + (("2.5G", 4.0, 2.0), np.array([0.3012, 0.3470, 12.0000])), + (("2.5G", 4.0, 4.0), np.array([0.2891, 0.3821, 12.0000])), + (("2.5G", 4.0, 6.0), np.array([0.2735, 0.4215, 12.0000])), + (("5G", 4.0, 2.0), np.array([0.2959, 0.3417, 12.0000])), + (("5G", 4.0, 4.0), np.array([0.2781, 0.3704, 12.0000])), + (("7.5G", 4.0, 2.0), np.array([0.2919, 0.3371, 12.0000])), + (("7.5G", 4.0, 4.0), np.array([0.2702, 0.3602, 12.0000])), + (("10G", 4.0, 2.0), np.array([0.2880, 0.3327, 12.0000])), + (("10G", 4.0, 4.0), np.array([0.2628, 0.3498, 12.0000])), + (("2.5BG", 4.0, 2.0), np.array([0.2840, 0.3270, 12.0000])), + (("2.5BG", 4.0, 4.0), np.array([0.2552, 0.3375, 12.0000])), + (("2.5BG", 4.0, 6.0), np.array([0.2278, 0.3463, 12.0000])), + (("5BG", 4.0, 2.0), np.array([0.2799, 0.3208, 12.0000])), + (("5BG", 4.0, 4.0), np.array([0.2480, 0.3232, 12.0000])), + (("5BG", 4.0, 6.0), np.array([0.2182, 0.3240, 12.0000])), + (("7.5BG", 4.0, 2.0), np.array([0.2764, 0.3148, 12.0000])), + (("7.5BG", 4.0, 4.0), np.array([0.2429, 0.3108, 12.0000])), + (("7.5BG", 4.0, 6.0), np.array([0.2113, 0.3052, 12.0000])), + (("10BG", 4.0, 2.0), np.array([0.2740, 0.3091, 12.0000])), + (("10BG", 4.0, 4.0), np.array([0.2384, 0.2984, 12.0000])), + (("10BG", 4.0, 6.0), np.array([0.2065, 0.2863, 12.0000])), + (("2.5B", 4.0, 2.0), np.array([0.2727, 0.3038, 12.0000])), + (("2.5B", 4.0, 4.0), np.array([0.2360, 0.2872, 12.0000])), + (("2.5B", 4.0, 6.0), np.array([0.2048, 0.2708, 12.0000])), + (("2.5B", 4.0, 8.0), np.array([0.1737, 0.2524, 12.0000])), + (("5B", 4.0, 2.0), np.array([0.2723, 0.2992, 12.0000])), + (("5B", 4.0, 4.0), np.array([0.2363, 0.2782, 12.0000])), + (("5B", 4.0, 6.0), np.array([0.2060, 0.2572, 12.0000])), + (("5B", 4.0, 8.0), np.array([0.1759, 0.2345, 12.0000])), + (("7.5B", 4.0, 2.0), np.array([0.2733, 0.2947, 12.0000])), + (("7.5B", 4.0, 4.0), np.array([0.2388, 0.2704, 12.0000])), + (("7.5B", 4.0, 6.0), np.array([0.2102, 0.2470, 12.0000])), + (("7.5B", 4.0, 8.0), np.array([0.1821, 0.2232, 12.0000])), + (("10B", 4.0, 2.0), np.array([0.2753, 0.2910, 12.0000])), + (("10B", 4.0, 4.0), np.array([0.2429, 0.2648, 12.0000])), + (("10B", 4.0, 6.0), np.array([0.2157, 0.2407, 12.0000])), + (("10B", 4.0, 8.0), np.array([0.1893, 0.2160, 12.0000])), + (("2.5PB", 4.0, 2.0), np.array([0.2782, 0.2876, 12.0000])), + (("2.5PB", 4.0, 4.0), np.array([0.2487, 0.2597, 12.0000])), + (("2.5PB", 4.0, 6.0), np.array([0.2235, 0.2343, 12.0000])), + (("2.5PB", 4.0, 8.0), np.array([0.1995, 0.2094, 12.0000])), + (("2.5PB", 4.0, 10.0), np.array([0.1805, 0.1888, 12.0000])), + (("5PB", 4.0, 2.0), np.array([0.2816, 0.2842, 12.0000])), + (("5PB", 4.0, 4.0), np.array([0.2562, 0.2560, 12.0000])), + (("5PB", 4.0, 6.0), np.array([0.2325, 0.2300, 12.0000])), + (("5PB", 4.0, 8.0), np.array([0.2103, 0.2050, 12.0000])), + (("5PB", 4.0, 10.0), np.array([0.1925, 0.1843, 12.0000])), + (("7.5PB", 4.0, 2.0), np.array([0.2861, 0.2819, 12.0000])), + (("7.5PB", 4.0, 4.0), np.array([0.2657, 0.2528, 12.0000])), + (("7.5PB", 4.0, 6.0), np.array([0.2471, 0.2266, 12.0000])), + (("7.5PB", 4.0, 8.0), np.array([0.2304, 0.2023, 12.0000])), + (("7.5PB", 4.0, 10.0), np.array([0.2158, 0.1811, 12.0000])), + (("10PB", 4.0, 2.0), np.array([0.2911, 0.2804, 12.0000])), + (("10PB", 4.0, 4.0), np.array([0.2759, 0.2522, 12.0000])), + (("10PB", 4.0, 6.0), np.array([0.2618, 0.2263, 12.0000])), + (("10PB", 4.0, 8.0), np.array([0.2497, 0.2038, 12.0000])), + (("10PB", 4.0, 10.0), np.array([0.2388, 0.1837, 12.0000])), + (("2.5P", 4.0, 2.0), np.array([0.2962, 0.2807, 12.0000])), + (("2.5P", 4.0, 4.0), np.array([0.2855, 0.2531, 12.0000])), + (("2.5P", 4.0, 6.0), np.array([0.2763, 0.2300, 12.0000])), + (("2.5P", 4.0, 8.0), np.array([0.2685, 0.2089, 12.0000])), + (("2.5P", 4.0, 10.0), np.array([0.2619, 0.1903, 12.0000])), + (("2.5P", 4.0, 12.0), np.array([0.2559, 0.1730, 12.0000])), + (("5P", 4.0, 2.0), np.array([0.3022, 0.2825, 12.0000])), + (("5P", 4.0, 4.0), np.array([0.2958, 0.2565, 12.0000])), + (("5P", 4.0, 6.0), np.array([0.2903, 0.2347, 12.0000])), + (("5P", 4.0, 8.0), np.array([0.2855, 0.2150, 12.0000])), + (("5P", 4.0, 10.0), np.array([0.2814, 0.1967, 12.0000])), + (("5P", 4.0, 12.0), np.array([0.2778, 0.1808, 12.0000])), + (("7.5P", 4.0, 2.0), np.array([0.3093, 0.2859, 12.0000])), + (("7.5P", 4.0, 4.0), np.array([0.3084, 0.2622, 12.0000])), + (("7.5P", 4.0, 6.0), np.array([0.3076, 0.2416, 12.0000])), + (("7.5P", 4.0, 8.0), np.array([0.3066, 0.2228, 12.0000])), + (("7.5P", 4.0, 10.0), np.array([0.3056, 0.2060, 12.0000])), + (("7.5P", 4.0, 12.0), np.array([0.3045, 0.1905, 12.0000])), + (("10P", 4.0, 2.0), np.array([0.3162, 0.2902, 12.0000])), + (("10P", 4.0, 4.0), np.array([0.3210, 0.2686, 12.0000])), + (("10P", 4.0, 6.0), np.array([0.3248, 0.2493, 12.0000])), + (("10P", 4.0, 8.0), np.array([0.3280, 0.2318, 12.0000])), + (("10P", 4.0, 10.0), np.array([0.3306, 0.2162, 12.0000])), + (("2.5RP", 4.0, 2.0), np.array([0.3231, 0.2951, 12.0000])), + (("2.5RP", 4.0, 4.0), np.array([0.3340, 0.2770, 12.0000])), + (("2.5RP", 4.0, 6.0), np.array([0.3442, 0.2595, 12.0000])), + (("2.5RP", 4.0, 8.0), np.array([0.3533, 0.2438, 12.0000])), + (("2.5RP", 4.0, 10.0), np.array([0.3608, 0.2301, 12.0000])), + (("2.5RP", 4.0, 12.0), np.array([0.3683, 0.2162, 12.0000])), + (("5RP", 4.0, 2.0), np.array([0.3310, 0.3010, 12.0000])), + (("5RP", 4.0, 4.0), np.array([0.3491, 0.2872, 12.0000])), + (("5RP", 4.0, 6.0), np.array([0.3671, 0.2733, 12.0000])), + (("5RP", 4.0, 8.0), np.array([0.3833, 0.2600, 12.0000])), + (("5RP", 4.0, 10.0), np.array([0.3960, 0.2489, 12.0000])), + (("5RP", 4.0, 12.0), np.array([0.4104, 0.2361, 12.0000])), + (("7.5RP", 4.0, 2.0), np.array([0.3371, 0.3061, 12.0000])), + (("7.5RP", 4.0, 4.0), np.array([0.3612, 0.2963, 12.0000])), + (("7.5RP", 4.0, 6.0), np.array([0.3850, 0.2859, 12.0000])), + (("7.5RP", 4.0, 8.0), np.array([0.4072, 0.2750, 12.0000])), + (("7.5RP", 4.0, 10.0), np.array([0.4259, 0.2651, 12.0000])), + (("7.5RP", 4.0, 12.0), np.array([0.4450, 0.2541, 12.0000])), + (("10RP", 5.0, 2.0), np.array([0.3332, 0.3131, 19.7700])), + (("10RP", 5.0, 4.0), np.array([0.3594, 0.3090, 19.7700])), + (("10RP", 5.0, 6.0), np.array([0.3851, 0.3039, 19.7700])), + (("10RP", 5.0, 8.0), np.array([0.4105, 0.2980, 19.7700])), + (("10RP", 5.0, 10.0), np.array([0.4332, 0.2918, 19.7700])), + (("2.5R", 5.0, 2.0), np.array([0.3360, 0.3158, 19.7700])), + (("2.5R", 5.0, 4.0), np.array([0.3660, 0.3148, 19.7700])), + (("2.5R", 5.0, 6.0), np.array([0.3960, 0.3130, 19.7700])), + (("2.5R", 5.0, 8.0), np.array([0.4252, 0.3101, 19.7700])), + (("2.5R", 5.0, 10.0), np.array([0.4533, 0.3058, 19.7700])), + (("2.5R", 5.0, 12.0), np.array([0.4820, 0.3002, 19.7700])), + (("5R", 5.0, 2.0), np.array([0.3392, 0.3192, 19.7700])), + (("5R", 5.0, 4.0), np.array([0.3740, 0.3220, 19.7700])), + (("5R", 5.0, 6.0), np.array([0.4078, 0.3238, 19.7700])), + (("5R", 5.0, 8.0), np.array([0.4413, 0.3240, 19.7700])), + (("5R", 5.0, 10.0), np.array([0.4747, 0.3227, 19.7700])), + (("5R", 5.0, 12.0), np.array([0.5071, 0.3194, 19.7700])), + (("7.5R", 5.0, 2.0), np.array([0.3425, 0.3229, 19.7700])), + (("7.5R", 5.0, 4.0), np.array([0.3806, 0.3294, 19.7700])), + (("7.5R", 5.0, 6.0), np.array([0.4180, 0.3348, 19.7700])), + (("7.5R", 5.0, 8.0), np.array([0.4563, 0.3387, 19.7700])), + (("7.5R", 5.0, 10.0), np.array([0.4927, 0.3399, 19.7700])), + (("7.5R", 5.0, 12.0), np.array([0.5280, 0.3389, 19.7700])), + (("10R", 5.0, 2.0), np.array([0.3465, 0.3278, 19.7700])), + (("10R", 5.0, 4.0), np.array([0.3879, 0.3398, 19.7700])), + (("10R", 5.0, 6.0), np.array([0.4299, 0.3499, 19.7700])), + (("10R", 5.0, 8.0), np.array([0.4713, 0.3575, 19.7700])), + (("10R", 5.0, 10.0), np.array([0.5113, 0.3630, 19.7700])), + (("2.5YR", 5.0, 2.0), np.array([0.3506, 0.3337, 19.7700])), + (("2.5YR", 5.0, 4.0), np.array([0.3925, 0.3494, 19.7700])), + (("2.5YR", 5.0, 6.0), np.array([0.4365, 0.3640, 19.7700])), + (("2.5YR", 5.0, 8.0), np.array([0.4795, 0.3758, 19.7700])), + (("2.5YR", 5.0, 10.0), np.array([0.5175, 0.3844, 19.7700])), + (("5YR", 5.0, 2.0), np.array([0.3530, 0.3395, 19.7700])), + (("5YR", 5.0, 4.0), np.array([0.3968, 0.3614, 19.7700])), + (("5YR", 5.0, 6.0), np.array([0.4420, 0.3808, 19.7700])), + (("5YR", 5.0, 8.0), np.array([0.4830, 0.3960, 19.7700])), + (("5YR", 5.0, 10.0), np.array([0.5161, 0.4064, 19.7700])), + (("7.5YR", 5.0, 2.0), np.array([0.3540, 0.3445, 19.7700])), + (("7.5YR", 5.0, 4.0), np.array([0.3991, 0.3714, 19.7700])), + (("7.5YR", 5.0, 6.0), np.array([0.4440, 0.3954, 19.7700])), + (("7.5YR", 5.0, 8.0), np.array([0.4820, 0.4141, 19.7700])), + (("7.5YR", 5.0, 10.0), np.array([0.5108, 0.4276, 19.7700])), + (("10YR", 5.0, 2.0), np.array([0.3546, 0.3514, 19.7700])), + (("10YR", 5.0, 4.0), np.array([0.3995, 0.3840, 19.7700])), + (("10YR", 5.0, 6.0), np.array([0.4428, 0.4128, 19.7700])), + (("10YR", 5.0, 8.0), np.array([0.4770, 0.4338, 19.7700])), + (("2.5Y", 5.0, 2.0), np.array([0.3534, 0.3570, 19.7700])), + (("2.5Y", 5.0, 4.0), np.array([0.3968, 0.3954, 19.7700])), + (("2.5Y", 5.0, 6.0), np.array([0.4380, 0.4292, 19.7700])), + (("2.5Y", 5.0, 8.0), np.array([0.4685, 0.4524, 19.7700])), + (("5Y", 5.0, 2.0), np.array([0.3500, 0.3620, 19.7700])), + (("5Y", 5.0, 4.0), np.array([0.3915, 0.4057, 19.7700])), + (("5Y", 5.0, 6.0), np.array([0.4302, 0.4435, 19.7700])), + (("7.5Y", 5.0, 2.0), np.array([0.3470, 0.3640, 19.7700])), + (("7.5Y", 5.0, 4.0), np.array([0.3850, 0.4120, 19.7700])), + (("7.5Y", 5.0, 6.0), np.array([0.4199, 0.4551, 19.7700])), + (("10Y", 5.0, 2.0), np.array([0.3422, 0.3648, 19.7700])), + (("10Y", 5.0, 4.0), np.array([0.3762, 0.4158, 19.7700])), + (("10Y", 5.0, 6.0), np.array([0.4072, 0.4621, 19.7700])), + (("2.5GY", 5.0, 2.0), np.array([0.3352, 0.3636, 19.7700])), + (("2.5GY", 5.0, 4.0), np.array([0.3621, 0.4143, 19.7700])), + (("2.5GY", 5.0, 6.0), np.array([0.3879, 0.4646, 19.7700])), + (("2.5GY", 5.0, 8.0), np.array([0.4088, 0.5068, 19.7700])), + (("5GY", 5.0, 2.0), np.array([0.3289, 0.3612, 19.7700])), + (("5GY", 5.0, 4.0), np.array([0.3482, 0.4097, 19.7700])), + (("5GY", 5.0, 6.0), np.array([0.3663, 0.4614, 19.7700])), + (("5GY", 5.0, 8.0), np.array([0.3815, 0.5093, 19.7700])), + (("7.5GY", 5.0, 2.0), np.array([0.3188, 0.3560, 19.7700])), + (("7.5GY", 5.0, 4.0), np.array([0.3274, 0.3994, 19.7700])), + (("7.5GY", 5.0, 6.0), np.array([0.3354, 0.4483, 19.7700])), + (("7.5GY", 5.0, 8.0), np.array([0.3412, 0.4976, 19.7700])), + (("10GY", 5.0, 2.0), np.array([0.3110, 0.3508, 19.7700])), + (("10GY", 5.0, 4.0), np.array([0.3111, 0.3881, 19.7700])), + (("10GY", 5.0, 6.0), np.array([0.3108, 0.4301, 19.7700])), + (("10GY", 5.0, 8.0), np.array([0.3080, 0.4759, 19.7700])), + (("2.5G", 5.0, 2.0), np.array([0.3030, 0.3445, 19.7700])), + (("2.5G", 5.0, 4.0), np.array([0.2943, 0.3735, 19.7700])), + (("2.5G", 5.0, 6.0), np.array([0.2841, 0.4045, 19.7700])), + (("2.5G", 5.0, 8.0), np.array([0.2710, 0.4380, 19.7700])), + (("5G", 5.0, 2.0), np.array([0.2978, 0.3392, 19.7700])), + (("5G", 5.0, 4.0), np.array([0.2841, 0.3628, 19.7700])), + (("5G", 5.0, 6.0), np.array([0.2690, 0.3860, 19.7700])), + (("5G", 5.0, 8.0), np.array([0.2511, 0.4107, 19.7700])), + (("7.5G", 5.0, 2.0), np.array([0.2945, 0.3355, 19.7700])), + (("7.5G", 5.0, 4.0), np.array([0.2775, 0.3545, 19.7700])), + (("7.5G", 5.0, 6.0), np.array([0.2598, 0.3724, 19.7700])), + (("7.5G", 5.0, 8.0), np.array([0.2395, 0.3915, 19.7700])), + (("10G", 5.0, 2.0), np.array([0.2910, 0.3310, 19.7700])), + (("10G", 5.0, 4.0), np.array([0.2711, 0.3455, 19.7700])), + (("10G", 5.0, 6.0), np.array([0.2519, 0.3587, 19.7700])), + (("2.5BG", 5.0, 2.0), np.array([0.2880, 0.3270, 19.7700])), + (("2.5BG", 5.0, 4.0), np.array([0.2659, 0.3369, 19.7700])), + (("2.5BG", 5.0, 6.0), np.array([0.2448, 0.3452, 19.7700])), + (("5BG", 5.0, 2.0), np.array([0.2841, 0.3210, 19.7700])), + (("5BG", 5.0, 4.0), np.array([0.2591, 0.3246, 19.7700])), + (("5BG", 5.0, 6.0), np.array([0.2360, 0.3270, 19.7700])), + (("7.5BG", 5.0, 2.0), np.array([0.2812, 0.3161, 19.7700])), + (("7.5BG", 5.0, 4.0), np.array([0.2550, 0.3150, 19.7700])), + (("7.5BG", 5.0, 6.0), np.array([0.2292, 0.3125, 19.7700])), + (("10BG", 5.0, 2.0), np.array([0.2796, 0.3111, 19.7700])), + (("10BG", 5.0, 4.0), np.array([0.2512, 0.3040, 19.7700])), + (("10BG", 5.0, 6.0), np.array([0.2234, 0.2952, 19.7700])), + (("2.5B", 5.0, 2.0), np.array([0.2791, 0.3071, 19.7700])), + (("2.5B", 5.0, 4.0), np.array([0.2492, 0.2954, 19.7700])), + (("2.5B", 5.0, 6.0), np.array([0.2210, 0.2823, 19.7700])), + (("5B", 5.0, 2.0), np.array([0.2794, 0.3032, 19.7700])), + (("5B", 5.0, 4.0), np.array([0.2493, 0.2879, 19.7700])), + (("5B", 5.0, 6.0), np.array([0.2215, 0.2701, 19.7700])), + (("7.5B", 5.0, 2.0), np.array([0.2803, 0.3000, 19.7700])), + (("7.5B", 5.0, 4.0), np.array([0.2511, 0.2808, 19.7700])), + (("7.5B", 5.0, 6.0), np.array([0.2248, 0.2612, 19.7700])), + (("10B", 5.0, 2.0), np.array([0.2821, 0.2966, 19.7700])), + (("10B", 5.0, 4.0), np.array([0.2547, 0.2757, 19.7700])), + (("10B", 5.0, 6.0), np.array([0.2299, 0.2548, 19.7700])), + (("2.5PB", 5.0, 2.0), np.array([0.2847, 0.2942, 19.7700])), + (("2.5PB", 5.0, 4.0), np.array([0.2600, 0.2720, 19.7700])), + (("2.5PB", 5.0, 6.0), np.array([0.2365, 0.2488, 19.7700])), + (("2.5PB", 5.0, 8.0), np.array([0.2157, 0.2278, 19.7700])), + (("5PB", 5.0, 2.0), np.array([0.2882, 0.2923, 19.7700])), + (("5PB", 5.0, 4.0), np.array([0.2662, 0.2687, 19.7700])), + (("5PB", 5.0, 6.0), np.array([0.2447, 0.2449, 19.7700])), + (("5PB", 5.0, 8.0), np.array([0.2255, 0.2239, 19.7700])), + (("5PB", 5.0, 10.0), np.array([0.2080, 0.2041, 19.7700])), + (("7.5PB", 5.0, 2.0), np.array([0.2918, 0.2908, 19.7700])), + (("7.5PB", 5.0, 4.0), np.array([0.2739, 0.2666, 19.7700])), + (("7.5PB", 5.0, 6.0), np.array([0.2563, 0.2417, 19.7700])), + (("7.5PB", 5.0, 8.0), np.array([0.2417, 0.2204, 19.7700])), + (("7.5PB", 5.0, 10.0), np.array([0.2285, 0.2020, 19.7700])), + (("10PB", 5.0, 2.0), np.array([0.2959, 0.2905, 19.7700])), + (("10PB", 5.0, 4.0), np.array([0.2821, 0.2659, 19.7700])), + (("10PB", 5.0, 6.0), np.array([0.2686, 0.2412, 19.7700])), + (("10PB", 5.0, 8.0), np.array([0.2572, 0.2211, 19.7700])), + (("10PB", 5.0, 10.0), np.array([0.2478, 0.2030, 19.7700])), + (("2.5P", 5.0, 2.0), np.array([0.3000, 0.2912, 19.7700])), + (("2.5P", 5.0, 4.0), np.array([0.2898, 0.2667, 19.7700])), + (("2.5P", 5.0, 6.0), np.array([0.2806, 0.2444, 19.7700])), + (("2.5P", 5.0, 8.0), np.array([0.2728, 0.2240, 19.7700])), + (("2.5P", 5.0, 10.0), np.array([0.2665, 0.2075, 19.7700])), + (("5P", 5.0, 2.0), np.array([0.3045, 0.2928, 19.7700])), + (("5P", 5.0, 4.0), np.array([0.2986, 0.2699, 19.7700])), + (("5P", 5.0, 6.0), np.array([0.2932, 0.2487, 19.7700])), + (("5P", 5.0, 8.0), np.array([0.2885, 0.2296, 19.7700])), + (("5P", 5.0, 10.0), np.array([0.2845, 0.2137, 19.7700])), + (("7.5P", 5.0, 2.0), np.array([0.3103, 0.2959, 19.7700])), + (("7.5P", 5.0, 4.0), np.array([0.3100, 0.2750, 19.7700])), + (("7.5P", 5.0, 6.0), np.array([0.3093, 0.2555, 19.7700])), + (("7.5P", 5.0, 8.0), np.array([0.3087, 0.2375, 19.7700])), + (("7.5P", 5.0, 10.0), np.array([0.3080, 0.2230, 19.7700])), + (("10P", 5.0, 2.0), np.array([0.3148, 0.2986, 19.7700])), + (("10P", 5.0, 4.0), np.array([0.3198, 0.2807, 19.7700])), + (("10P", 5.0, 6.0), np.array([0.3243, 0.2630, 19.7700])), + (("10P", 5.0, 8.0), np.array([0.3280, 0.2464, 19.7700])), + (("10P", 5.0, 10.0), np.array([0.3308, 0.2328, 19.7700])), + (("2.5RP", 5.0, 2.0), np.array([0.3199, 0.3019, 19.7700])), + (("2.5RP", 5.0, 4.0), np.array([0.3298, 0.2869, 19.7700])), + (("2.5RP", 5.0, 6.0), np.array([0.3396, 0.2718, 19.7700])), + (("2.5RP", 5.0, 8.0), np.array([0.3490, 0.2570, 19.7700])), + (("2.5RP", 5.0, 10.0), np.array([0.3560, 0.2452, 19.7700])), + (("5RP", 5.0, 2.0), np.array([0.3256, 0.3065, 19.7700])), + (("5RP", 5.0, 4.0), np.array([0.3421, 0.2954, 19.7700])), + (("5RP", 5.0, 6.0), np.array([0.3585, 0.2842, 19.7700])), + (("5RP", 5.0, 8.0), np.array([0.3748, 0.2729, 19.7700])), + (("5RP", 5.0, 10.0), np.array([0.3880, 0.2630, 19.7700])), + (("7.5RP", 5.0, 2.0), np.array([0.3296, 0.3098, 19.7700])), + (("7.5RP", 5.0, 4.0), np.array([0.3515, 0.3024, 19.7700])), + (("7.5RP", 5.0, 6.0), np.array([0.3726, 0.2941, 19.7700])), + (("7.5RP", 5.0, 8.0), np.array([0.3932, 0.2852, 19.7700])), + (("7.5RP", 5.0, 10.0), np.array([0.4108, 0.2773, 19.7700])), + (("10RP", 6.0, 2.0), np.array([0.3292, 0.3141, 30.0500])), + (("10RP", 6.0, 4.0), np.array([0.3508, 0.3112, 30.0500])), + (("10RP", 6.0, 6.0), np.array([0.3740, 0.3074, 30.0500])), + (("10RP", 6.0, 8.0), np.array([0.3930, 0.3038, 30.0500])), + (("10RP", 6.0, 10.0), np.array([0.4150, 0.2989, 30.0500])), + (("2.5R", 6.0, 2.0), np.array([0.3318, 0.3166, 30.0500])), + (("2.5R", 6.0, 4.0), np.array([0.3566, 0.3163, 30.0500])), + (("2.5R", 6.0, 6.0), np.array([0.3832, 0.3158, 30.0500])), + (("2.5R", 6.0, 8.0), np.array([0.4065, 0.3144, 30.0500])), + (("2.5R", 6.0, 10.0), np.array([0.4320, 0.3118, 30.0500])), + (("5R", 6.0, 2.0), np.array([0.3343, 0.3190, 30.0500])), + (("5R", 6.0, 4.0), np.array([0.3628, 0.3221, 30.0500])), + (("5R", 6.0, 6.0), np.array([0.3921, 0.3244, 30.0500])), + (("5R", 6.0, 8.0), np.array([0.4187, 0.3251, 30.0500])), + (("5R", 6.0, 10.0), np.array([0.4480, 0.3250, 30.0500])), + (("7.5R", 6.0, 2.0), np.array([0.3381, 0.3228, 30.0500])), + (("7.5R", 6.0, 4.0), np.array([0.3692, 0.3291, 30.0500])), + (("7.5R", 6.0, 6.0), np.array([0.4000, 0.3340, 30.0500])), + (("7.5R", 6.0, 8.0), np.array([0.4318, 0.3383, 30.0500])), + (("7.5R", 6.0, 10.0), np.array([0.4655, 0.3412, 30.0500])), + (("10R", 6.0, 2.0), np.array([0.3417, 0.3268, 30.0500])), + (("10R", 6.0, 4.0), np.array([0.3768, 0.3381, 30.0500])), + (("10R", 6.0, 6.0), np.array([0.4103, 0.3473, 30.0500])), + (("10R", 6.0, 8.0), np.array([0.4449, 0.3550, 30.0500])), + (("10R", 6.0, 10.0), np.array([0.4812, 0.3619, 30.0500])), + (("2.5YR", 6.0, 2.0), np.array([0.3453, 0.3321, 30.0500])), + (("2.5YR", 6.0, 4.0), np.array([0.3806, 0.3467, 30.0500])), + (("2.5YR", 6.0, 6.0), np.array([0.4180, 0.3600, 30.0500])), + (("2.5YR", 6.0, 8.0), np.array([0.4533, 0.3708, 30.0500])), + (("2.5YR", 6.0, 10.0), np.array([0.4891, 0.3806, 30.0500])), + (("2.5YR", 6.0, 12.0), np.array([0.5215, 0.3887, 30.0500])), + (("5YR", 6.0, 2.0), np.array([0.3474, 0.3373, 30.0500])), + (("5YR", 6.0, 4.0), np.array([0.3840, 0.3564, 30.0500])), + (("5YR", 6.0, 6.0), np.array([0.4229, 0.3750, 30.0500])), + (("5YR", 6.0, 8.0), np.array([0.4592, 0.3900, 30.0500])), + (("5YR", 6.0, 10.0), np.array([0.4921, 0.4022, 30.0500])), + (("5YR", 6.0, 12.0), np.array([0.5199, 0.4119, 30.0500])), + (("7.5YR", 6.0, 2.0), np.array([0.3487, 0.3421, 30.0500])), + (("7.5YR", 6.0, 4.0), np.array([0.3860, 0.3652, 30.0500])), + (("7.5YR", 6.0, 6.0), np.array([0.4242, 0.3876, 30.0500])), + (("7.5YR", 6.0, 8.0), np.array([0.4596, 0.4064, 30.0500])), + (("7.5YR", 6.0, 10.0), np.array([0.4904, 0.4220, 30.0500])), + (("7.5YR", 6.0, 12.0), np.array([0.5145, 0.4331, 30.0500])), + (("10YR", 6.0, 2.0), np.array([0.3491, 0.3483, 30.0500])), + (("10YR", 6.0, 4.0), np.array([0.3861, 0.3767, 30.0500])), + (("10YR", 6.0, 6.0), np.array([0.4240, 0.4030, 30.0500])), + (("10YR", 6.0, 8.0), np.array([0.4570, 0.4249, 30.0500])), + (("10YR", 6.0, 10.0), np.array([0.4843, 0.4416, 30.0500])), + (("2.5Y", 6.0, 2.0), np.array([0.3480, 0.3540, 30.0500])), + (("2.5Y", 6.0, 4.0), np.array([0.3840, 0.3867, 30.0500])), + (("2.5Y", 6.0, 6.0), np.array([0.4203, 0.4176, 30.0500])), + (("2.5Y", 6.0, 8.0), np.array([0.4517, 0.4421, 30.0500])), + (("2.5Y", 6.0, 10.0), np.array([0.4760, 0.4607, 30.0500])), + (("5Y", 6.0, 2.0), np.array([0.3457, 0.3580, 30.0500])), + (("5Y", 6.0, 4.0), np.array([0.3794, 0.3955, 30.0500])), + (("5Y", 6.0, 6.0), np.array([0.4140, 0.4305, 30.0500])), + (("5Y", 6.0, 8.0), np.array([0.4426, 0.4588, 30.0500])), + (("7.5Y", 6.0, 2.0), np.array([0.3431, 0.3601, 30.0500])), + (("7.5Y", 6.0, 4.0), np.array([0.3745, 0.4004, 30.0500])), + (("7.5Y", 6.0, 6.0), np.array([0.4060, 0.4400, 30.0500])), + (("7.5Y", 6.0, 8.0), np.array([0.4321, 0.4719, 30.0500])), + (("10Y", 6.0, 2.0), np.array([0.3398, 0.3611, 30.0500])), + (("10Y", 6.0, 4.0), np.array([0.3679, 0.4033, 30.0500])), + (("10Y", 6.0, 6.0), np.array([0.3960, 0.4452, 30.0500])), + (("2.5GY", 6.0, 2.0), np.array([0.3342, 0.3607, 30.0500])), + (("2.5GY", 6.0, 4.0), np.array([0.3572, 0.4038, 30.0500])), + (("2.5GY", 6.0, 6.0), np.array([0.3799, 0.4470, 30.0500])), + (("2.5GY", 6.0, 8.0), np.array([0.4006, 0.4885, 30.0500])), + (("5GY", 6.0, 2.0), np.array([0.3288, 0.3592, 30.0500])), + (("5GY", 6.0, 4.0), np.array([0.3461, 0.4008, 30.0500])), + (("5GY", 6.0, 6.0), np.array([0.3622, 0.4438, 30.0500])), + (("5GY", 6.0, 8.0), np.array([0.3772, 0.4880, 30.0500])), + (("7.5GY", 6.0, 2.0), np.array([0.3193, 0.3550, 30.0500])), + (("7.5GY", 6.0, 4.0), np.array([0.3275, 0.3922, 30.0500])), + (("7.5GY", 6.0, 6.0), np.array([0.3351, 0.4321, 30.0500])), + (("7.5GY", 6.0, 8.0), np.array([0.3418, 0.4768, 30.0500])), + (("7.5GY", 6.0, 10.0), np.array([0.3463, 0.5196, 30.0500])), + (("10GY", 6.0, 2.0), np.array([0.3112, 0.3496, 30.0500])), + (("10GY", 6.0, 4.0), np.array([0.3124, 0.3822, 30.0500])), + (("10GY", 6.0, 6.0), np.array([0.3128, 0.4175, 30.0500])), + (("10GY", 6.0, 8.0), np.array([0.3116, 0.4563, 30.0500])), + (("10GY", 6.0, 10.0), np.array([0.3086, 0.4949, 30.0500])), + (("2.5G", 6.0, 2.0), np.array([0.3039, 0.3437, 30.0500])), + (("2.5G", 6.0, 4.0), np.array([0.2967, 0.3695, 30.0500])), + (("2.5G", 6.0, 6.0), np.array([0.2892, 0.3963, 30.0500])), + (("2.5G", 6.0, 8.0), np.array([0.2799, 0.4239, 30.0500])), + (("5G", 6.0, 2.0), np.array([0.2988, 0.3382, 30.0500])), + (("5G", 6.0, 4.0), np.array([0.2868, 0.3595, 30.0500])), + (("5G", 6.0, 6.0), np.array([0.2748, 0.3795, 30.0500])), + (("7.5G", 6.0, 2.0), np.array([0.2958, 0.3344, 30.0500])), + (("7.5G", 6.0, 4.0), np.array([0.2807, 0.3522, 30.0500])), + (("7.5G", 6.0, 6.0), np.array([0.2662, 0.3672, 30.0500])), + (("10G", 6.0, 2.0), np.array([0.2929, 0.3303, 30.0500])), + (("10G", 6.0, 4.0), np.array([0.2749, 0.3443, 30.0500])), + (("10G", 6.0, 6.0), np.array([0.2591, 0.3558, 30.0500])), + (("2.5BG", 6.0, 2.0), np.array([0.2902, 0.3268, 30.0500])), + (("2.5BG", 6.0, 4.0), np.array([0.2702, 0.3369, 30.0500])), + (("2.5BG", 6.0, 6.0), np.array([0.2526, 0.3448, 30.0500])), + (("5BG", 6.0, 2.0), np.array([0.2872, 0.3219, 30.0500])), + (("5BG", 6.0, 4.0), np.array([0.2648, 0.3262, 30.0500])), + (("5BG", 6.0, 6.0), np.array([0.2441, 0.3290, 30.0500])), + (("7.5BG", 6.0, 2.0), np.array([0.2849, 0.3172, 30.0500])), + (("7.5BG", 6.0, 4.0), np.array([0.2604, 0.3169, 30.0500])), + (("7.5BG", 6.0, 6.0), np.array([0.2384, 0.3155, 30.0500])), + (("10BG", 6.0, 2.0), np.array([0.2837, 0.3132, 30.0500])), + (("10BG", 6.0, 4.0), np.array([0.2578, 0.3078, 30.0500])), + (("10BG", 6.0, 6.0), np.array([0.2335, 0.3015, 30.0500])), + (("2.5B", 6.0, 2.0), np.array([0.2835, 0.3097, 30.0500])), + (("2.5B", 6.0, 4.0), np.array([0.2571, 0.3008, 30.0500])), + (("2.5B", 6.0, 6.0), np.array([0.2312, 0.2899, 30.0500])), + (("5B", 6.0, 2.0), np.array([0.2842, 0.3063, 30.0500])), + (("5B", 6.0, 4.0), np.array([0.2579, 0.2938, 30.0500])), + (("5B", 6.0, 6.0), np.array([0.2320, 0.2789, 30.0500])), + (("7.5B", 6.0, 2.0), np.array([0.2854, 0.3037, 30.0500])), + (("7.5B", 6.0, 4.0), np.array([0.2602, 0.2881, 30.0500])), + (("7.5B", 6.0, 6.0), np.array([0.2352, 0.2708, 30.0500])), + (("10B", 6.0, 2.0), np.array([0.2871, 0.3012, 30.0500])), + (("10B", 6.0, 4.0), np.array([0.2637, 0.2840, 30.0500])), + (("10B", 6.0, 6.0), np.array([0.2399, 0.2650, 30.0500])), + (("2.5PB", 6.0, 2.0), np.array([0.2897, 0.2991, 30.0500])), + (("2.5PB", 6.0, 4.0), np.array([0.2684, 0.2804, 30.0500])), + (("2.5PB", 6.0, 6.0), np.array([0.2465, 0.2599, 30.0500])), + (("2.5PB", 6.0, 8.0), np.array([0.2274, 0.2406, 30.0500])), + (("5PB", 6.0, 2.0), np.array([0.2923, 0.2978, 30.0500])), + (("5PB", 6.0, 4.0), np.array([0.2734, 0.2778, 30.0500])), + (("5PB", 6.0, 6.0), np.array([0.2533, 0.2558, 30.0500])), + (("5PB", 6.0, 8.0), np.array([0.2360, 0.2365, 30.0500])), + (("7.5PB", 6.0, 2.0), np.array([0.2955, 0.2963, 30.0500])), + (("7.5PB", 6.0, 4.0), np.array([0.2798, 0.2752, 30.0500])), + (("7.5PB", 6.0, 6.0), np.array([0.2638, 0.2531, 30.0500])), + (("7.5PB", 6.0, 8.0), np.array([0.2505, 0.2347, 30.0500])), + (("10PB", 6.0, 2.0), np.array([0.2988, 0.2961, 30.0500])), + (("10PB", 6.0, 4.0), np.array([0.2863, 0.2747, 30.0500])), + (("10PB", 6.0, 6.0), np.array([0.2740, 0.2533, 30.0500])), + (("10PB", 6.0, 8.0), np.array([0.2637, 0.2352, 30.0500])), + (("2.5P", 6.0, 2.0), np.array([0.3016, 0.2960, 30.0500])), + (("2.5P", 6.0, 4.0), np.array([0.2932, 0.2759, 30.0500])), + (("2.5P", 6.0, 6.0), np.array([0.2842, 0.2550, 30.0500])), + (("2.5P", 6.0, 8.0), np.array([0.2770, 0.2372, 30.0500])), + (("5P", 6.0, 2.0), np.array([0.3050, 0.2967, 30.0500])), + (("5P", 6.0, 4.0), np.array([0.3001, 0.2778, 30.0500])), + (("5P", 6.0, 6.0), np.array([0.2950, 0.2585, 30.0500])), + (("5P", 6.0, 8.0), np.array([0.2905, 0.2421, 30.0500])), + (("7.5P", 6.0, 2.0), np.array([0.3107, 0.2993, 30.0500])), + (("7.5P", 6.0, 4.0), np.array([0.3107, 0.2831, 30.0500])), + (("7.5P", 6.0, 6.0), np.array([0.3101, 0.2650, 30.0500])), + (("7.5P", 6.0, 8.0), np.array([0.3099, 0.2502, 30.0500])), + (("10P", 6.0, 2.0), np.array([0.3146, 0.3018, 30.0500])), + (("10P", 6.0, 4.0), np.array([0.3181, 0.2871, 30.0500])), + (("10P", 6.0, 6.0), np.array([0.3226, 0.2716, 30.0500])), + (("10P", 6.0, 8.0), np.array([0.3259, 0.2584, 30.0500])), + (("2.5RP", 6.0, 2.0), np.array([0.3188, 0.3048, 30.0500])), + (("2.5RP", 6.0, 4.0), np.array([0.3272, 0.2929, 30.0500])), + (("2.5RP", 6.0, 6.0), np.array([0.3362, 0.2799, 30.0500])), + (("2.5RP", 6.0, 8.0), np.array([0.3437, 0.2688, 30.0500])), + (("2.5RP", 6.0, 10.0), np.array([0.3509, 0.2578, 30.0500])), + (("5RP", 6.0, 2.0), np.array([0.3232, 0.3085, 30.0500])), + (("5RP", 6.0, 4.0), np.array([0.3371, 0.3001, 30.0500])), + (("5RP", 6.0, 6.0), np.array([0.3520, 0.2904, 30.0500])), + (("5RP", 6.0, 8.0), np.array([0.3648, 0.2820, 30.0500])), + (("5RP", 6.0, 10.0), np.array([0.3769, 0.2738, 30.0500])), + (("7.5RP", 6.0, 2.0), np.array([0.3261, 0.3113, 30.0500])), + (("7.5RP", 6.0, 4.0), np.array([0.3439, 0.3056, 30.0500])), + (("7.5RP", 6.0, 6.0), np.array([0.3635, 0.2987, 30.0500])), + (("7.5RP", 6.0, 8.0), np.array([0.3791, 0.2929, 30.0500])), + (("7.5RP", 6.0, 10.0), np.array([0.3960, 0.2860, 30.0500])), + (("10RP", 7.0, 2.0), np.array([0.3258, 0.3148, 43.0600])), + (("10RP", 7.0, 4.0), np.array([0.3446, 0.3125, 43.0600])), + (("10RP", 7.0, 6.0), np.array([0.3648, 0.3098, 43.0600])), + (("10RP", 7.0, 8.0), np.array([0.3851, 0.3067, 43.0600])), + (("2.5R", 7.0, 2.0), np.array([0.3284, 0.3170, 43.0600])), + (("2.5R", 7.0, 4.0), np.array([0.3499, 0.3171, 43.0600])), + (("2.5R", 7.0, 6.0), np.array([0.3728, 0.3170, 43.0600])), + (("2.5R", 7.0, 8.0), np.array([0.3961, 0.3160, 43.0600])), + (("5R", 7.0, 2.0), np.array([0.3306, 0.3190, 43.0600])), + (("5R", 7.0, 4.0), np.array([0.3552, 0.3222, 43.0600])), + (("5R", 7.0, 6.0), np.array([0.3805, 0.3244, 43.0600])), + (("5R", 7.0, 8.0), np.array([0.4067, 0.3256, 43.0600])), + (("7.5R", 7.0, 2.0), np.array([0.3335, 0.3220, 43.0600])), + (("7.5R", 7.0, 4.0), np.array([0.3611, 0.3282, 43.0600])), + (("7.5R", 7.0, 6.0), np.array([0.3888, 0.3336, 43.0600])), + (("7.5R", 7.0, 8.0), np.array([0.4196, 0.3382, 43.0600])), + (("10R", 7.0, 2.0), np.array([0.3360, 0.3253, 43.0600])), + (("10R", 7.0, 4.0), np.array([0.3671, 0.3360, 43.0600])), + (("10R", 7.0, 6.0), np.array([0.3984, 0.3452, 43.0600])), + (("10R", 7.0, 8.0), np.array([0.4308, 0.3533, 43.0600])), + (("10R", 7.0, 10.0), np.array([0.4600, 0.3596, 43.0600])), + (("2.5YR", 7.0, 2.0), np.array([0.3392, 0.3298, 43.0600])), + (("2.5YR", 7.0, 4.0), np.array([0.3715, 0.3439, 43.0600])), + (("2.5YR", 7.0, 6.0), np.array([0.4053, 0.3570, 43.0600])), + (("2.5YR", 7.0, 8.0), np.array([0.4371, 0.3679, 43.0600])), + (("2.5YR", 7.0, 10.0), np.array([0.4671, 0.3768, 43.0600])), + (("5YR", 7.0, 2.0), np.array([0.3421, 0.3349, 43.0600])), + (("5YR", 7.0, 4.0), np.array([0.3750, 0.3530, 43.0600])), + (("5YR", 7.0, 6.0), np.array([0.4091, 0.3701, 43.0600])), + (("5YR", 7.0, 8.0), np.array([0.4402, 0.3842, 43.0600])), + (("5YR", 7.0, 10.0), np.array([0.4711, 0.3972, 43.0600])), + (("7.5YR", 7.0, 2.0), np.array([0.3437, 0.3397, 43.0600])), + (("7.5YR", 7.0, 4.0), np.array([0.3772, 0.3613, 43.0600])), + (("7.5YR", 7.0, 6.0), np.array([0.4107, 0.3820, 43.0600])), + (("7.5YR", 7.0, 8.0), np.array([0.4415, 0.3996, 43.0600])), + (("7.5YR", 7.0, 10.0), np.array([0.4704, 0.4151, 43.0600])), + (("10YR", 7.0, 2.0), np.array([0.3443, 0.3454, 43.0600])), + (("10YR", 7.0, 4.0), np.array([0.3778, 0.3719, 43.0600])), + (("10YR", 7.0, 6.0), np.array([0.4102, 0.3960, 43.0600])), + (("10YR", 7.0, 8.0), np.array([0.4399, 0.4164, 43.0600])), + (("10YR", 7.0, 10.0), np.array([0.4667, 0.4335, 43.0600])), + (("2.5Y", 7.0, 2.0), np.array([0.3436, 0.3507, 43.0600])), + (("2.5Y", 7.0, 4.0), np.array([0.3761, 0.3800, 43.0600])), + (("2.5Y", 7.0, 6.0), np.array([0.4073, 0.4073, 43.0600])), + (("2.5Y", 7.0, 8.0), np.array([0.4353, 0.4312, 43.0600])), + (("2.5Y", 7.0, 10.0), np.array([0.4606, 0.4516, 43.0600])), + (("5Y", 7.0, 2.0), np.array([0.3419, 0.3540, 43.0600])), + (("5Y", 7.0, 4.0), np.array([0.3718, 0.3885, 43.0600])), + (("5Y", 7.0, 6.0), np.array([0.4009, 0.4198, 43.0600])), + (("5Y", 7.0, 8.0), np.array([0.4271, 0.4462, 43.0600])), + (("5Y", 7.0, 10.0), np.array([0.4509, 0.4696, 43.0600])), + (("7.5Y", 7.0, 2.0), np.array([0.3396, 0.3558, 43.0600])), + (("7.5Y", 7.0, 4.0), np.array([0.3677, 0.3925, 43.0600])), + (("7.5Y", 7.0, 6.0), np.array([0.3943, 0.4264, 43.0600])), + (("7.5Y", 7.0, 8.0), np.array([0.4184, 0.4568, 43.0600])), + (("7.5Y", 7.0, 10.0), np.array([0.4400, 0.4830, 43.0600])), + (("10Y", 7.0, 2.0), np.array([0.3369, 0.3569, 43.0600])), + (("10Y", 7.0, 4.0), np.array([0.3624, 0.3951, 43.0600])), + (("10Y", 7.0, 6.0), np.array([0.3864, 0.4305, 43.0600])), + (("10Y", 7.0, 8.0), np.array([0.4090, 0.4641, 43.0600])), + (("2.5GY", 7.0, 2.0), np.array([0.3328, 0.3569, 43.0600])), + (("2.5GY", 7.0, 4.0), np.array([0.3534, 0.3953, 43.0600])), + (("2.5GY", 7.0, 6.0), np.array([0.3728, 0.4316, 43.0600])), + (("2.5GY", 7.0, 8.0), np.array([0.3919, 0.4684, 43.0600])), + (("2.5GY", 7.0, 10.0), np.array([0.4091, 0.5030, 43.0600])), + (("5GY", 7.0, 2.0), np.array([0.3284, 0.3559, 43.0600])), + (("5GY", 7.0, 4.0), np.array([0.3437, 0.3929, 43.0600])), + (("5GY", 7.0, 6.0), np.array([0.3581, 0.4291, 43.0600])), + (("5GY", 7.0, 8.0), np.array([0.3722, 0.4669, 43.0600])), + (("5GY", 7.0, 10.0), np.array([0.3852, 0.5051, 43.0600])), + (("7.5GY", 7.0, 2.0), np.array([0.3190, 0.3516, 43.0600])), + (("7.5GY", 7.0, 4.0), np.array([0.3267, 0.3848, 43.0600])), + (("7.5GY", 7.0, 6.0), np.array([0.3341, 0.4191, 43.0600])), + (("7.5GY", 7.0, 8.0), np.array([0.3406, 0.4558, 43.0600])), + (("7.5GY", 7.0, 10.0), np.array([0.3461, 0.4950, 43.0600])), + (("10GY", 7.0, 2.0), np.array([0.3117, 0.3469, 43.0600])), + (("10GY", 7.0, 4.0), np.array([0.3133, 0.3764, 43.0600])), + (("10GY", 7.0, 6.0), np.array([0.3142, 0.4058, 43.0600])), + (("10GY", 7.0, 8.0), np.array([0.3140, 0.4387, 43.0600])), + (("2.5G", 7.0, 2.0), np.array([0.3047, 0.3413, 43.0600])), + (("2.5G", 7.0, 4.0), np.array([0.2992, 0.3644, 43.0600])), + (("2.5G", 7.0, 6.0), np.array([0.2933, 0.3873, 43.0600])), + (("2.5G", 7.0, 8.0), np.array([0.2861, 0.4129, 43.0600])), + (("5G", 7.0, 2.0), np.array([0.3001, 0.3366, 43.0600])), + (("5G", 7.0, 4.0), np.array([0.2902, 0.3548, 43.0600])), + (("5G", 7.0, 6.0), np.array([0.2801, 0.3721, 43.0600])), + (("7.5G", 7.0, 2.0), np.array([0.2972, 0.3333, 43.0600])), + (("7.5G", 7.0, 4.0), np.array([0.2850, 0.3482, 43.0600])), + (("7.5G", 7.0, 6.0), np.array([0.2728, 0.3622, 43.0600])), + (("10G", 7.0, 2.0), np.array([0.2945, 0.3297, 43.0600])), + (("10G", 7.0, 4.0), np.array([0.2803, 0.3415, 43.0600])), + (("2.5BG", 7.0, 2.0), np.array([0.2927, 0.3269, 43.0600])), + (("2.5BG", 7.0, 4.0), np.array([0.2764, 0.3354, 43.0600])), + (("5BG", 7.0, 2.0), np.array([0.2898, 0.3225, 43.0600])), + (("5BG", 7.0, 4.0), np.array([0.2712, 0.3269, 43.0600])), + (("7.5BG", 7.0, 2.0), np.array([0.2878, 0.3182, 43.0600])), + (("7.5BG", 7.0, 4.0), np.array([0.2671, 0.3189, 43.0600])), + (("10BG", 7.0, 2.0), np.array([0.2869, 0.3143, 43.0600])), + (("10BG", 7.0, 4.0), np.array([0.2642, 0.3109, 43.0600])), + (("2.5B", 7.0, 2.0), np.array([0.2867, 0.3110, 43.0600])), + (("2.5B", 7.0, 4.0), np.array([0.2629, 0.3038, 43.0600])), + (("2.5B", 7.0, 6.0), np.array([0.2418, 0.2960, 43.0600])), + (("5B", 7.0, 2.0), np.array([0.2875, 0.3078, 43.0600])), + (("5B", 7.0, 4.0), np.array([0.2633, 0.2972, 43.0600])), + (("5B", 7.0, 6.0), np.array([0.2410, 0.2854, 43.0600])), + (("7.5B", 7.0, 2.0), np.array([0.2888, 0.3058, 43.0600])), + (("7.5B", 7.0, 4.0), np.array([0.2651, 0.2927, 43.0600])), + (("7.5B", 7.0, 6.0), np.array([0.2436, 0.2787, 43.0600])), + (("10B", 7.0, 2.0), np.array([0.2908, 0.3039, 43.0600])), + (("10B", 7.0, 4.0), np.array([0.2685, 0.2886, 43.0600])), + (("10B", 7.0, 6.0), np.array([0.2478, 0.2728, 43.0600])), + (("2.5PB", 7.0, 2.0), np.array([0.2932, 0.3025, 43.0600])), + (("2.5PB", 7.0, 4.0), np.array([0.2729, 0.2848, 43.0600])), + (("2.5PB", 7.0, 6.0), np.array([0.2538, 0.2677, 43.0600])), + (("5PB", 7.0, 2.0), np.array([0.2952, 0.3011, 43.0600])), + (("5PB", 7.0, 4.0), np.array([0.2773, 0.2828, 43.0600])), + (("5PB", 7.0, 6.0), np.array([0.2596, 0.2643, 43.0600])), + (("7.5PB", 7.0, 2.0), np.array([0.2982, 0.3003, 43.0600])), + (("7.5PB", 7.0, 4.0), np.array([0.2833, 0.2809, 43.0600])), + (("7.5PB", 7.0, 6.0), np.array([0.2687, 0.2612, 43.0600])), + (("10PB", 7.0, 2.0), np.array([0.3005, 0.3000, 43.0600])), + (("10PB", 7.0, 4.0), np.array([0.2886, 0.2801, 43.0600])), + (("10PB", 7.0, 6.0), np.array([0.2776, 0.2612, 43.0600])), + (("2.5P", 7.0, 2.0), np.array([0.3031, 0.3000, 43.0600])), + (("2.5P", 7.0, 4.0), np.array([0.2950, 0.2810, 43.0600])), + (("2.5P", 7.0, 6.0), np.array([0.2873, 0.2633, 43.0600])), + (("5P", 7.0, 2.0), np.array([0.3059, 0.3010, 43.0600])), + (("5P", 7.0, 4.0), np.array([0.3009, 0.2831, 43.0600])), + (("5P", 7.0, 6.0), np.array([0.2961, 0.2663, 43.0600])), + (("7.5P", 7.0, 2.0), np.array([0.3109, 0.3037, 43.0600])), + (("7.5P", 7.0, 4.0), np.array([0.3111, 0.2880, 43.0600])), + (("7.5P", 7.0, 6.0), np.array([0.3111, 0.2730, 43.0600])), + (("7.5P", 7.0, 8.0), np.array([0.3109, 0.2584, 43.0600])), + (("10P", 7.0, 2.0), np.array([0.3138, 0.3054, 43.0600])), + (("10P", 7.0, 4.0), np.array([0.3181, 0.2920, 43.0600])), + (("10P", 7.0, 6.0), np.array([0.3221, 0.2786, 43.0600])), + (("10P", 7.0, 8.0), np.array([0.3256, 0.2654, 43.0600])), + (("2.5RP", 7.0, 2.0), np.array([0.3170, 0.3076, 43.0600])), + (("2.5RP", 7.0, 4.0), np.array([0.3254, 0.2971, 43.0600])), + (("2.5RP", 7.0, 6.0), np.array([0.3338, 0.2854, 43.0600])), + (("2.5RP", 7.0, 8.0), np.array([0.3417, 0.2745, 43.0600])), + (("5RP", 7.0, 2.0), np.array([0.3206, 0.3104, 43.0600])), + (("5RP", 7.0, 4.0), np.array([0.3332, 0.3032, 43.0600])), + (("5RP", 7.0, 6.0), np.array([0.3470, 0.2949, 43.0600])), + (("5RP", 7.0, 8.0), np.array([0.3603, 0.2869, 43.0600])), + (("7.5RP", 7.0, 2.0), np.array([0.3232, 0.3125, 43.0600])), + (("7.5RP", 7.0, 4.0), np.array([0.3389, 0.3079, 43.0600])), + (("7.5RP", 7.0, 6.0), np.array([0.3562, 0.3022, 43.0600])), + (("7.5RP", 7.0, 8.0), np.array([0.3722, 0.2963, 43.0600])), + (("10RP", 8.0, 2.0), np.array([0.3218, 0.3152, 59.1000])), + (("10RP", 8.0, 4.0), np.array([0.3412, 0.3135, 59.1000])), + (("10RP", 8.0, 6.0), np.array([0.3600, 0.3112, 59.1000])), + (("2.5R", 8.0, 2.0), np.array([0.3236, 0.3169, 59.1000])), + (("2.5R", 8.0, 4.0), np.array([0.3460, 0.3177, 59.1000])), + (("2.5R", 8.0, 6.0), np.array([0.3671, 0.3175, 59.1000])), + (("5R", 8.0, 2.0), np.array([0.3254, 0.3186, 59.1000])), + (("5R", 8.0, 4.0), np.array([0.3510, 0.3224, 59.1000])), + (("7.5R", 8.0, 2.0), np.array([0.3277, 0.3211, 59.1000])), + (("7.5R", 8.0, 4.0), np.array([0.3564, 0.3279, 59.1000])), + (("10R", 8.0, 2.0), np.array([0.3301, 0.3237, 59.1000])), + (("10R", 8.0, 4.0), np.array([0.3621, 0.3349, 59.1000])), + (("2.5YR", 8.0, 2.0), np.array([0.3334, 0.3276, 59.1000])), + (("2.5YR", 8.0, 4.0), np.array([0.3667, 0.3429, 59.1000])), + (("5YR", 8.0, 2.0), np.array([0.3373, 0.3330, 59.1000])), + (("5YR", 8.0, 4.0), np.array([0.3690, 0.3510, 59.1000])), + (("7.5YR", 8.0, 2.0), np.array([0.3395, 0.3379, 59.1000])), + (("7.5YR", 8.0, 4.0), np.array([0.3699, 0.3586, 59.1000])), + (("7.5YR", 8.0, 6.0), np.array([0.4000, 0.3770, 59.1000])), + (("10YR", 8.0, 2.0), np.array([0.3407, 0.3434, 59.1000])), + (("10YR", 8.0, 4.0), np.array([0.3701, 0.3674, 59.1000])), + (("10YR", 8.0, 6.0), np.array([0.3994, 0.3896, 59.1000])), + (("2.5Y", 8.0, 2.0), np.array([0.3406, 0.3484, 59.1000])), + (("2.5Y", 8.0, 4.0), np.array([0.3684, 0.3751, 59.1000])), + (("2.5Y", 8.0, 6.0), np.array([0.3969, 0.4009, 59.1000])), + (("2.5Y", 8.0, 8.0), np.array([0.4231, 0.4231, 59.1000])), + (("5Y", 8.0, 2.0), np.array([0.3394, 0.3518, 59.1000])), + (("5Y", 8.0, 4.0), np.array([0.3650, 0.3826, 59.1000])), + (("5Y", 8.0, 6.0), np.array([0.3913, 0.4117, 59.1000])), + (("5Y", 8.0, 8.0), np.array([0.4158, 0.4378, 59.1000])), + (("5Y", 8.0, 10.0), np.array([0.4376, 0.4601, 59.1000])), + (("5Y", 8.0, 12.0), np.array([0.4562, 0.4788, 59.1000])), + (("7.5Y", 8.0, 2.0), np.array([0.3379, 0.3540, 59.1000])), + (("7.5Y", 8.0, 4.0), np.array([0.3622, 0.3861, 59.1000])), + (("7.5Y", 8.0, 6.0), np.array([0.3862, 0.4175, 59.1000])), + (("7.5Y", 8.0, 8.0), np.array([0.4088, 0.4466, 59.1000])), + (("7.5Y", 8.0, 10.0), np.array([0.4283, 0.4712, 59.1000])), + (("10Y", 8.0, 2.0), np.array([0.3359, 0.3552, 59.1000])), + (("10Y", 8.0, 4.0), np.array([0.3581, 0.3883, 59.1000])), + (("10Y", 8.0, 6.0), np.array([0.3803, 0.4216, 59.1000])), + (("10Y", 8.0, 8.0), np.array([0.4008, 0.4520, 59.1000])), + (("2.5GY", 8.0, 2.0), np.array([0.3327, 0.3555, 59.1000])), + (("2.5GY", 8.0, 4.0), np.array([0.3504, 0.3887, 59.1000])), + (("2.5GY", 8.0, 6.0), np.array([0.3690, 0.4230, 59.1000])), + (("2.5GY", 8.0, 8.0), np.array([0.3858, 0.4550, 59.1000])), + (("5GY", 8.0, 2.0), np.array([0.3284, 0.3542, 59.1000])), + (("5GY", 8.0, 4.0), np.array([0.3433, 0.3872, 59.1000])), + (("5GY", 8.0, 6.0), np.array([0.3573, 0.4214, 59.1000])), + (("5GY", 8.0, 8.0), np.array([0.3696, 0.4542, 59.1000])), + (("7.5GY", 8.0, 2.0), np.array([0.3194, 0.3502, 59.1000])), + (("7.5GY", 8.0, 4.0), np.array([0.3266, 0.3809, 59.1000])), + (("7.5GY", 8.0, 6.0), np.array([0.3339, 0.4129, 59.1000])), + (("7.5GY", 8.0, 8.0), np.array([0.3408, 0.4452, 59.1000])), + (("10GY", 8.0, 2.0), np.array([0.3121, 0.3459, 59.1000])), + (("10GY", 8.0, 4.0), np.array([0.3140, 0.3727, 59.1000])), + (("10GY", 8.0, 6.0), np.array([0.3150, 0.4014, 59.1000])), + (("2.5G", 8.0, 2.0), np.array([0.3053, 0.3404, 59.1000])), + (("2.5G", 8.0, 4.0), np.array([0.3009, 0.3614, 59.1000])), + (("2.5G", 8.0, 6.0), np.array([0.2952, 0.3851, 59.1000])), + (("5G", 8.0, 2.0), np.array([0.3009, 0.3359, 59.1000])), + (("5G", 8.0, 4.0), np.array([0.2924, 0.3523, 59.1000])), + (("5G", 8.0, 6.0), np.array([0.2822, 0.3702, 59.1000])), + (("7.5G", 8.0, 2.0), np.array([0.2981, 0.3326, 59.1000])), + (("7.5G", 8.0, 4.0), np.array([0.2874, 0.3464, 59.1000])), + (("10G", 8.0, 2.0), np.array([0.2957, 0.3293, 59.1000])), + (("2.5BG", 8.0, 2.0), np.array([0.2940, 0.3268, 59.1000])), + (("5BG", 8.0, 2.0), np.array([0.2919, 0.3228, 59.1000])), + (("7.5BG", 8.0, 2.0), np.array([0.2900, 0.3183, 59.1000])), + (("10BG", 8.0, 2.0), np.array([0.2894, 0.3152, 59.1000])), + (("2.5B", 8.0, 2.0), np.array([0.2897, 0.3124, 59.1000])), + (("2.5B", 8.0, 4.0), np.array([0.2668, 0.3067, 59.1000])), + (("5B", 8.0, 2.0), np.array([0.2908, 0.3096, 59.1000])), + (("5B", 8.0, 4.0), np.array([0.2671, 0.2998, 59.1000])), + (("7.5B", 8.0, 2.0), np.array([0.2922, 0.3077, 59.1000])), + (("7.5B", 8.0, 4.0), np.array([0.2688, 0.2956, 59.1000])), + (("10B", 8.0, 2.0), np.array([0.2935, 0.3062, 59.1000])), + (("10B", 8.0, 4.0), np.array([0.2718, 0.2911, 59.1000])), + (("2.5PB", 8.0, 2.0), np.array([0.2957, 0.3047, 59.1000])), + (("2.5PB", 8.0, 4.0), np.array([0.2758, 0.2879, 59.1000])), + (("5PB", 8.0, 2.0), np.array([0.2974, 0.3039, 59.1000])), + (("7.5PB", 8.0, 2.0), np.array([0.3003, 0.3034, 59.1000])), + (("10PB", 8.0, 2.0), np.array([0.3027, 0.3035, 59.1000])), + (("2.5P", 8.0, 2.0), np.array([0.3048, 0.3040, 59.1000])), + (("2.5P", 8.0, 4.0), np.array([0.2962, 0.2850, 59.1000])), + (("5P", 8.0, 2.0), np.array([0.3065, 0.3047, 59.1000])), + (("5P", 8.0, 4.0), np.array([0.3012, 0.2868, 59.1000])), + (("7.5P", 8.0, 2.0), np.array([0.3107, 0.3070, 59.1000])), + (("7.5P", 8.0, 4.0), np.array([0.3114, 0.2915, 59.1000])), + (("10P", 8.0, 2.0), np.array([0.3131, 0.3084, 59.1000])), + (("10P", 8.0, 4.0), np.array([0.3175, 0.2955, 59.1000])), + (("2.5RP", 8.0, 2.0), np.array([0.3154, 0.3100, 59.1000])), + (("2.5RP", 8.0, 4.0), np.array([0.3239, 0.3000, 59.1000])), + (("2.5RP", 8.0, 6.0), np.array([0.3327, 0.2898, 59.1000])), + (("5RP", 8.0, 2.0), np.array([0.3180, 0.3120, 59.1000])), + (("5RP", 8.0, 4.0), np.array([0.3308, 0.3052, 59.1000])), + (("5RP", 8.0, 6.0), np.array([0.3440, 0.2978, 59.1000])), + (("7.5RP", 8.0, 2.0), np.array([0.3200, 0.3136, 59.1000])), + (("7.5RP", 8.0, 4.0), np.array([0.3360, 0.3092, 59.1000])), + (("7.5RP", 8.0, 6.0), np.array([0.3521, 0.3042, 59.1000])), +) # yapf: enable """ -*1929* published *Munsell* colours. +*1929* published *Munsell* colours as a *tuple* as follows:: + + ( + (('hue', 'value', 'chroma'), np.array(['x', 'y', 'Y'])), + ..., + (('hue', 'value', 'chroma'), np.array(['x', 'y', 'Y'])), + ) References ---------- :cite:`MunsellColorSciencec` - -MUNSELL_COLOURS_1929 : tuple -(('hue', 'value', 'chroma'), np.array(['x', 'y', 'Y']))) """ diff --git a/colour/notation/datasets/munsell/real.py b/colour/notation/datasets/munsell/real.py index d862586f54..9ef5884064 100644 --- a/colour/notation/datasets/munsell/real.py +++ b/colour/notation/datasets/munsell/real.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Munsell Renotation System Dataset - Real Munsell Colours ======================================================== @@ -9,10 +8,10 @@ Notes ----- -- The Munsell Renotation data commonly available within the all.dat, - experimental.dat and real.dat files features *CIE xyY* colourspace values - that are scaled by a :math:`1 / 0.975 \\simeq 1.02568` factor. If you are - performing conversions using *Munsell* *Colorlab* specification, +- The Munsell Renotation data commonly available within the *all.dat*, + *experimental.dat* and *real.dat* files features *CIE xyY* colourspace + values that are scaled by a :math:`1 / 0.975 \\simeq 1.02568` factor. If + you are performing conversions using *Munsell* *Colorlab* specification, e.g. *2.5R 9/2*, according to *ASTM D1535-08e1* method, you should not scale the output :math:`Y` Luminance. However, if you use directly the *CIE xyY* colourspace values from the Munsell Renotation data data, you @@ -26,6 +25,9 @@ magnesium oxide with respect to the perfect reflecting diffuser, and rounding to ve digits of precision. +- Chromaticities assume *CIE Illuminant C*, approximately 6700K, as neutral + origin for both the hue and chroma loci. + References ---------- - :cite:`MunsellColorSciencec` : Munsell Color Science. (n.d.). Munsell @@ -33,2763 +35,2772 @@ http://www.cis.rit.edu/research/mcsl2/online/munsell.php """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import Tuple + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "MUNSELL_COLOURS_REAL", +] -__all__ = ['MUNSELL_COLOURS_REAL'] -# yapf: disable -MUNSELL_COLOURS_REAL = ( - (('10RP', 1.0, 2.0), np.array([0.3629, 0.2710, 1.2100])), - (('10RP', 1.0, 4.0), np.array([0.3920, 0.2423, 1.2100])), - (('10RP', 1.0, 6.0), np.array([0.4151, 0.2169, 1.2100])), - (('10RP', 1.0, 8.0), np.array([0.4357, 0.1921, 1.2100])), - (('10RP', 1.0, 10.0), np.array([0.4521, 0.1710, 1.2100])), - (('10RP', 1.0, 12.0), np.array([0.4668, 0.1514, 1.2100])), - (('2.5R', 1.0, 2.0), np.array([0.3768, 0.2816, 1.2100])), - (('2.5R', 1.0, 4.0), np.array([0.4166, 0.2569, 1.2100])), - (('2.5R', 1.0, 6.0), np.array([0.4515, 0.2329, 1.2100])), - (('2.5R', 1.0, 8.0), np.array([0.4812, 0.2103, 1.2100])), - (('2.5R', 1.0, 10.0), np.array([0.5058, 0.1900, 1.2100])), - (('5R', 1.0, 2.0), np.array([0.3908, 0.2929, 1.2100])), - (('5R', 1.0, 4.0), np.array([0.4420, 0.2728, 1.2100])), - (('5R', 1.0, 6.0), np.array([0.4885, 0.2515, 1.2100])), - (('5R', 1.0, 8.0), np.array([0.5282, 0.2297, 1.2100])), - (('5R', 1.0, 10.0), np.array([0.5604, 0.2100, 1.2100])), - (('7.5R', 1.0, 2.0), np.array([0.4020, 0.3034, 1.2100])), - (('7.5R', 1.0, 4.0), np.array([0.4660, 0.2888, 1.2100])), - (('7.5R', 1.0, 6.0), np.array([0.5235, 0.2698, 1.2100])), - (('7.5R', 1.0, 8.0), np.array([0.5722, 0.2487, 1.2100])), - (('7.5R', 1.0, 10.0), np.array([0.6111, 0.2290, 1.2100])), - (('10R', 1.0, 2.0), np.array([0.4128, 0.3154, 1.2100])), - (('10R', 1.0, 4.0), np.array([0.4933, 0.3068, 1.2100])), - (('10R', 1.0, 6.0), np.array([0.5584, 0.2921, 1.2100])), - (('10R', 1.0, 8.0), np.array([0.6178, 0.2713, 1.2100])), - (('10R', 1.0, 10.0), np.array([0.6661, 0.2499, 1.2100])), - (('2.5YR', 1.0, 2.0), np.array([0.4258, 0.3344, 1.2100])), - (('2.5YR', 1.0, 4.0), np.array([0.5311, 0.3371, 1.2100])), - (('2.5YR', 1.0, 6.0), np.array([0.6048, 0.3270, 1.2100])), - (('2.5YR', 1.0, 8.0), np.array([0.6721, 0.3058, 1.2100])), - (('5YR', 1.0, 2.0), np.array([0.4377, 0.3580, 1.2100])), - (('5YR', 1.0, 4.0), np.array([0.5660, 0.3795, 1.2100])), - (('7.5YR', 1.0, 2.0), np.array([0.4430, 0.3775, 1.2100])), - (('10YR', 1.0, 2.0), np.array([0.4446, 0.3982, 1.2100])), - (('2.5Y', 1.0, 2.0), np.array([0.4362, 0.4177, 1.2100])), - (('5Y', 1.0, 2.0), np.array([0.4230, 0.4265, 1.2100])), - (('7.5Y', 1.0, 2.0), np.array([0.4042, 0.4287, 1.2100])), - (('10Y', 1.0, 2.0), np.array([0.3802, 0.4212, 1.2100])), - (('2.5GY', 1.0, 2.0), np.array([0.3540, 0.4088, 1.2100])), - (('5GY', 1.0, 2.0), np.array([0.3359, 0.3982, 1.2100])), - (('5GY', 1.0, 4.0), np.array([0.3765, 0.5942, 1.2100])), - (('7.5GY', 1.0, 2.0), np.array([0.3154, 0.3840, 1.2100])), - (('7.5GY', 1.0, 4.0), np.array([0.3133, 0.5380, 1.2100])), - (('10GY', 1.0, 2.0), np.array([0.3006, 0.3720, 1.2100])), - (('10GY', 1.0, 4.0), np.array([0.2722, 0.4903, 1.2100])), - (('10GY', 1.0, 6.0), np.array([0.2232, 0.6392, 1.2100])), - (('2.5G', 1.0, 2.0), np.array([0.2910, 0.3634, 1.2100])), - (('2.5G', 1.0, 4.0), np.array([0.2454, 0.4489, 1.2100])), - (('2.5G', 1.0, 6.0), np.array([0.1711, 0.5619, 1.2100])), - (('2.5G', 1.0, 8.0), np.array([0.0620, 0.6896, 1.2100])), - (('5G', 1.0, 2.0), np.array([0.2833, 0.3564, 1.2100])), - (('5G', 1.0, 4.0), np.array([0.2290, 0.4218, 1.2100])), - (('5G', 1.0, 6.0), np.array([0.1468, 0.4996, 1.2100])), - (('5G', 1.0, 8.0), np.array([0.0559, 0.5710, 1.2100])), - (('7.5G', 1.0, 2.0), np.array([0.2758, 0.3484, 1.2100])), - (('7.5G', 1.0, 4.0), np.array([0.2159, 0.3967, 1.2100])), - (('7.5G', 1.0, 6.0), np.array([0.1344, 0.4505, 1.2100])), - (('7.5G', 1.0, 8.0), np.array([0.0530, 0.4943, 1.2100])), - (('10G', 1.0, 2.0), np.array([0.2689, 0.3407, 1.2100])), - (('10G', 1.0, 4.0), np.array([0.2040, 0.3724, 1.2100])), - (('10G', 1.0, 6.0), np.array([0.1249, 0.4019, 1.2100])), - (('10G', 1.0, 8.0), np.array([0.0511, 0.4158, 1.2100])), - (('2.5BG', 1.0, 2.0), np.array([0.2600, 0.3289, 1.2100])), - (('2.5BG', 1.0, 4.0), np.array([0.1883, 0.3406, 1.2100])), - (('2.5BG', 1.0, 6.0), np.array([0.1169, 0.3452, 1.2100])), - (('2.5BG', 1.0, 8.0), np.array([0.0476, 0.3458, 1.2100])), - (('5BG', 1.0, 2.0), np.array([0.2500, 0.3141, 1.2100])), - (('5BG', 1.0, 4.0), np.array([0.1753, 0.3021, 1.2100])), - (('5BG', 1.0, 6.0), np.array([0.1093, 0.2860, 1.2100])), - (('7.5BG', 1.0, 2.0), np.array([0.2430, 0.3023, 1.2100])), - (('7.5BG', 1.0, 4.0), np.array([0.1702, 0.2768, 1.2100])), - (('7.5BG', 1.0, 6.0), np.array([0.1059, 0.2485, 1.2100])), - (('10BG', 1.0, 2.0), np.array([0.2362, 0.2882, 1.2100])), - (('10BG', 1.0, 4.0), np.array([0.1658, 0.2496, 1.2100])), - (('10BG', 1.0, 6.0), np.array([0.1074, 0.2129, 1.2100])), - (('2.5B', 1.0, 2.0), np.array([0.2322, 0.2781, 1.2100])), - (('2.5B', 1.0, 4.0), np.array([0.1649, 0.2324, 1.2100])), - (('2.5B', 1.0, 6.0), np.array([0.1118, 0.1908, 1.2100])), - (('5B', 1.0, 2.0), np.array([0.2291, 0.2677, 1.2100])), - (('5B', 1.0, 4.0), np.array([0.1667, 0.2168, 1.2100])), - (('5B', 1.0, 6.0), np.array([0.1212, 0.1745, 1.2100])), - (('7.5B', 1.0, 2.0), np.array([0.2291, 0.2579, 1.2100])), - (('7.5B', 1.0, 4.0), np.array([0.1716, 0.2048, 1.2100])), - (('7.5B', 1.0, 6.0), np.array([0.1303, 0.1639, 1.2100])), - (('7.5B', 1.0, 8.0), np.array([0.0968, 0.1280, 1.2100])), - (('10B', 1.0, 2.0), np.array([0.2309, 0.2491, 1.2100])), - (('10B', 1.0, 4.0), np.array([0.1783, 0.1974, 1.2100])), - (('10B', 1.0, 6.0), np.array([0.1392, 0.1563, 1.2100])), - (('10B', 1.0, 8.0), np.array([0.1077, 0.1218, 1.2100])), - (('2.5PB', 1.0, 2.0), np.array([0.2360, 0.2420, 1.2100])), - (('2.5PB', 1.0, 4.0), np.array([0.1895, 0.1911, 1.2100])), - (('2.5PB', 1.0, 6.0), np.array([0.1539, 0.1491, 1.2100])), - (('2.5PB', 1.0, 8.0), np.array([0.1273, 0.1157, 1.2100])), - (('5PB', 1.0, 2.0), np.array([0.2427, 0.2368, 1.2100])), - (('5PB', 1.0, 4.0), np.array([0.2012, 0.1867, 1.2100])), - (('5PB', 1.0, 6.0), np.array([0.1678, 0.1447, 1.2100])), - (('5PB', 1.0, 8.0), np.array([0.1447, 0.1124, 1.2100])), - (('5PB', 1.0, 10.0), np.array([0.1285, 0.0870, 1.2100])), - (('7.5PB', 1.0, 2.0), np.array([0.2547, 0.2310, 1.2100])), - (('7.5PB', 1.0, 4.0), np.array([0.2232, 0.1821, 1.2100])), - (('7.5PB', 1.0, 6.0), np.array([0.2000, 0.1422, 1.2100])), - (('7.5PB', 1.0, 8.0), np.array([0.1872, 0.1141, 1.2100])), - (('7.5PB', 1.0, 10.0), np.array([0.1804, 0.0950, 1.2100])), - (('7.5PB', 1.0, 12.0), np.array([0.1763, 0.0804, 1.2100])), - (('7.5PB', 1.0, 14.0), np.array([0.1738, 0.0688, 1.2100])), - (('7.5PB', 1.0, 16.0), np.array([0.1720, 0.0583, 1.2100])), - (('7.5PB', 1.0, 18.0), np.array([0.1709, 0.0518, 1.2100])), - (('7.5PB', 1.0, 20.0), np.array([0.1701, 0.0454, 1.2100])), - (('7.5PB', 1.0, 22.0), np.array([0.1696, 0.0402, 1.2100])), - (('7.5PB', 1.0, 24.0), np.array([0.1691, 0.0352, 1.2100])), - (('7.5PB', 1.0, 26.0), np.array([0.1689, 0.0309, 1.2100])), - (('7.5PB', 1.0, 28.0), np.array([0.1686, 0.0270, 1.2100])), - (('7.5PB', 1.0, 30.0), np.array([0.1684, 0.0234, 1.2100])), - (('7.5PB', 1.0, 32.0), np.array([0.1682, 0.0202, 1.2100])), - (('7.5PB', 1.0, 34.0), np.array([0.1682, 0.0180, 1.2100])), - (('7.5PB', 1.0, 36.0), np.array([0.1681, 0.0160, 1.2100])), - (('7.5PB', 1.0, 38.0), np.array([0.1680, 0.0140, 1.2100])), - (('10PB', 1.0, 2.0), np.array([0.2677, 0.2280, 1.2100])), - (('10PB', 1.0, 4.0), np.array([0.2459, 0.1828, 1.2100])), - (('10PB', 1.0, 6.0), np.array([0.2290, 0.1470, 1.2100])), - (('10PB', 1.0, 8.0), np.array([0.2190, 0.1228, 1.2100])), - (('10PB', 1.0, 10.0), np.array([0.2120, 0.1029, 1.2100])), - (('10PB', 1.0, 12.0), np.array([0.2070, 0.0869, 1.2100])), - (('10PB', 1.0, 14.0), np.array([0.2038, 0.0745, 1.2100])), - (('10PB', 1.0, 16.0), np.array([0.2008, 0.0638, 1.2100])), - (('10PB', 1.0, 18.0), np.array([0.1991, 0.0564, 1.2100])), - (('10PB', 1.0, 20.0), np.array([0.1976, 0.0493, 1.2100])), - (('10PB', 1.0, 22.0), np.array([0.1965, 0.0436, 1.2100])), - (('10PB', 1.0, 24.0), np.array([0.1952, 0.0380, 1.2100])), - (('10PB', 1.0, 26.0), np.array([0.1942, 0.0326, 1.2100])), - (('10PB', 1.0, 28.0), np.array([0.1936, 0.0281, 1.2100])), - (('10PB', 1.0, 30.0), np.array([0.1928, 0.0240, 1.2100])), - (('2.5P', 1.0, 2.0), np.array([0.2808, 0.2296, 1.2100])), - (('2.5P', 1.0, 4.0), np.array([0.2668, 0.1874, 1.2100])), - (('2.5P', 1.0, 6.0), np.array([0.2570, 0.1559, 1.2100])), - (('2.5P', 1.0, 8.0), np.array([0.2496, 0.1303, 1.2100])), - (('2.5P', 1.0, 10.0), np.array([0.2441, 0.1112, 1.2100])), - (('2.5P', 1.0, 12.0), np.array([0.2394, 0.0940, 1.2100])), - (('2.5P', 1.0, 14.0), np.array([0.2361, 0.0810, 1.2100])), - (('2.5P', 1.0, 16.0), np.array([0.2331, 0.0696, 1.2100])), - (('2.5P', 1.0, 18.0), np.array([0.2312, 0.0618, 1.2100])), - (('2.5P', 1.0, 20.0), np.array([0.2295, 0.0542, 1.2100])), - (('2.5P', 1.0, 22.0), np.array([0.2279, 0.0473, 1.2100])), - (('2.5P', 1.0, 24.0), np.array([0.2266, 0.0418, 1.2100])), - (('2.5P', 1.0, 26.0), np.array([0.2251, 0.0355, 1.2100])), - (('5P', 1.0, 2.0), np.array([0.2936, 0.2330, 1.2100])), - (('5P', 1.0, 4.0), np.array([0.2854, 0.1927, 1.2100])), - (('5P', 1.0, 6.0), np.array([0.2794, 0.1628, 1.2100])), - (('5P', 1.0, 8.0), np.array([0.2742, 0.1375, 1.2100])), - (('5P', 1.0, 10.0), np.array([0.2701, 0.1178, 1.2100])), - (('5P', 1.0, 12.0), np.array([0.2670, 0.1006, 1.2100])), - (('5P', 1.0, 14.0), np.array([0.2645, 0.0863, 1.2100])), - (('5P', 1.0, 16.0), np.array([0.2625, 0.0746, 1.2100])), - (('5P', 1.0, 18.0), np.array([0.2612, 0.0667, 1.2100])), - (('5P', 1.0, 20.0), np.array([0.2601, 0.0586, 1.2100])), - (('5P', 1.0, 22.0), np.array([0.2590, 0.0509, 1.2100])), - (('7.5P', 1.0, 2.0), np.array([0.3030, 0.2361, 1.2100])), - (('7.5P', 1.0, 4.0), np.array([0.2991, 0.1974, 1.2100])), - (('7.5P', 1.0, 6.0), np.array([0.2960, 0.1682, 1.2100])), - (('7.5P', 1.0, 8.0), np.array([0.2932, 0.1429, 1.2100])), - (('7.5P', 1.0, 10.0), np.array([0.2905, 0.1229, 1.2100])), - (('7.5P', 1.0, 12.0), np.array([0.2884, 0.1059, 1.2100])), - (('7.5P', 1.0, 14.0), np.array([0.2868, 0.0903, 1.2100])), - (('7.5P', 1.0, 16.0), np.array([0.2852, 0.0790, 1.2100])), - (('7.5P', 1.0, 18.0), np.array([0.2841, 0.0706, 1.2100])), - (('7.5P', 1.0, 20.0), np.array([0.2831, 0.0625, 1.2100])), - (('10P', 1.0, 2.0), np.array([0.3132, 0.2404, 1.2100])), - (('10P', 1.0, 4.0), np.array([0.3132, 0.2032, 1.2100])), - (('10P', 1.0, 6.0), np.array([0.3126, 0.1737, 1.2100])), - (('10P', 1.0, 8.0), np.array([0.3114, 0.1481, 1.2100])), - (('10P', 1.0, 10.0), np.array([0.3102, 0.1282, 1.2100])), - (('10P', 1.0, 12.0), np.array([0.3094, 0.1110, 1.2100])), - (('10P', 1.0, 14.0), np.array([0.3084, 0.0952, 1.2100])), - (('10P', 1.0, 16.0), np.array([0.3078, 0.0839, 1.2100])), - (('10P', 1.0, 18.0), np.array([0.3069, 0.0748, 1.2100])), - (('2.5RP', 1.0, 2.0), np.array([0.3240, 0.2459, 1.2100])), - (('2.5RP', 1.0, 4.0), np.array([0.3290, 0.2095, 1.2100])), - (('2.5RP', 1.0, 6.0), np.array([0.3321, 0.1811, 1.2100])), - (('2.5RP', 1.0, 8.0), np.array([0.3342, 0.1551, 1.2100])), - (('2.5RP', 1.0, 10.0), np.array([0.3354, 0.1351, 1.2100])), - (('2.5RP', 1.0, 12.0), np.array([0.3361, 0.1181, 1.2100])), - (('2.5RP', 1.0, 14.0), np.array([0.3368, 0.1020, 1.2100])), - (('2.5RP', 1.0, 16.0), np.array([0.3368, 0.0902, 1.2100])), - (('5RP', 1.0, 2.0), np.array([0.3378, 0.2542, 1.2100])), - (('5RP', 1.0, 4.0), np.array([0.3503, 0.2196, 1.2100])), - (('5RP', 1.0, 6.0), np.array([0.3588, 0.1920, 1.2100])), - (('5RP', 1.0, 8.0), np.array([0.3660, 0.1662, 1.2100])), - (('5RP', 1.0, 10.0), np.array([0.3727, 0.1458, 1.2100])), - (('5RP', 1.0, 12.0), np.array([0.3772, 0.1283, 1.2100])), - (('5RP', 1.0, 14.0), np.array([0.3811, 0.1138, 1.2100])), - (('7.5RP', 1.0, 2.0), np.array([0.3498, 0.2617, 1.2100])), - (('7.5RP', 1.0, 4.0), np.array([0.3705, 0.2300, 1.2100])), - (('7.5RP', 1.0, 6.0), np.array([0.3865, 0.2036, 1.2100])), - (('7.5RP', 1.0, 8.0), np.array([0.4005, 0.1793, 1.2100])), - (('7.5RP', 1.0, 10.0), np.array([0.4132, 0.1580, 1.2100])), - (('7.5RP', 1.0, 12.0), np.array([0.4240, 0.1400, 1.2100])), - (('10RP', 2.0, 2.0), np.array([0.3532, 0.2957, 3.1260])), - (('10RP', 2.0, 4.0), np.array([0.3850, 0.2778, 3.1260])), - (('10RP', 2.0, 6.0), np.array([0.4139, 0.2608, 3.1260])), - (('10RP', 2.0, 8.0), np.array([0.4428, 0.2419, 3.1260])), - (('10RP', 2.0, 10.0), np.array([0.4678, 0.2237, 3.1260])), - (('10RP', 2.0, 12.0), np.array([0.4911, 0.2060, 3.1260])), - (('10RP', 2.0, 14.0), np.array([0.5129, 0.1888, 3.1260])), - (('2.5R', 2.0, 2.0), np.array([0.3614, 0.3033, 3.1260])), - (('2.5R', 2.0, 4.0), np.array([0.4021, 0.2900, 3.1260])), - (('2.5R', 2.0, 6.0), np.array([0.4390, 0.2760, 3.1260])), - (('2.5R', 2.0, 8.0), np.array([0.4776, 0.2593, 3.1260])), - (('2.5R', 2.0, 10.0), np.array([0.5122, 0.2428, 3.1260])), - (('2.5R', 2.0, 12.0), np.array([0.5438, 0.2254, 3.1260])), - (('2.5R', 2.0, 14.0), np.array([0.5734, 0.2083, 3.1260])), - (('5R', 2.0, 2.0), np.array([0.3692, 0.3111, 3.1260])), - (('5R', 2.0, 4.0), np.array([0.4184, 0.3032, 3.1260])), - (('5R', 2.0, 6.0), np.array([0.4642, 0.2934, 3.1260])), - (('5R', 2.0, 8.0), np.array([0.5143, 0.2800, 3.1260])), - (('5R', 2.0, 10.0), np.array([0.5557, 0.2633, 3.1260])), - (('5R', 2.0, 12.0), np.array([0.5930, 0.2465, 3.1260])), - (('5R', 2.0, 14.0), np.array([0.6302, 0.2287, 3.1260])), - (('7.5R', 2.0, 2.0), np.array([0.3751, 0.3181, 3.1260])), - (('7.5R', 2.0, 4.0), np.array([0.4335, 0.3169, 3.1260])), - (('7.5R', 2.0, 6.0), np.array([0.4875, 0.3123, 3.1260])), - (('7.5R', 2.0, 8.0), np.array([0.5433, 0.3027, 3.1260])), - (('7.5R', 2.0, 10.0), np.array([0.5952, 0.2874, 3.1260])), - (('7.5R', 2.0, 12.0), np.array([0.6392, 0.2704, 3.1260])), - (('7.5R', 2.0, 14.0), np.array([0.6791, 0.2520, 3.1260])), - (('10R', 2.0, 2.0), np.array([0.3811, 0.3274, 3.1260])), - (('10R', 2.0, 4.0), np.array([0.4481, 0.3330, 3.1260])), - (('10R', 2.0, 6.0), np.array([0.5095, 0.3331, 3.1260])), - (('10R', 2.0, 8.0), np.array([0.5713, 0.3259, 3.1260])), - (('10R', 2.0, 10.0), np.array([0.6247, 0.3120, 3.1260])), - (('10R', 2.0, 12.0), np.array([0.6732, 0.2937, 3.1260])), - (('10R', 2.0, 14.0), np.array([0.7165, 0.2734, 3.1260])), - (('2.5YR', 2.0, 2.0), np.array([0.3852, 0.3365, 3.1260])), - (('2.5YR', 2.0, 4.0), np.array([0.4598, 0.3508, 3.1260])), - (('2.5YR', 2.0, 6.0), np.array([0.5280, 0.3581, 3.1260])), - (('2.5YR', 2.0, 8.0), np.array([0.5995, 0.3590, 3.1260])), - (('5YR', 2.0, 2.0), np.array([0.3880, 0.3476, 3.1260])), - (('5YR', 2.0, 4.0), np.array([0.4674, 0.3738, 3.1260])), - (('5YR', 2.0, 6.0), np.array([0.5426, 0.3925, 3.1260])), - (('7.5YR', 2.0, 2.0), np.array([0.3889, 0.3590, 3.1260])), - (('7.5YR', 2.0, 4.0), np.array([0.4690, 0.3964, 3.1260])), - (('7.5YR', 2.0, 6.0), np.array([0.5475, 0.4271, 3.1260])), - (('10YR', 2.0, 2.0), np.array([0.3872, 0.3688, 3.1260])), - (('10YR', 2.0, 4.0), np.array([0.4676, 0.4168, 3.1260])), - (('2.5Y', 2.0, 2.0), np.array([0.3825, 0.3785, 3.1260])), - (('2.5Y', 2.0, 4.0), np.array([0.4627, 0.4392, 3.1260])), - (('5Y', 2.0, 2.0), np.array([0.3757, 0.3839, 3.1260])), - (('5Y', 2.0, 4.0), np.array([0.4543, 0.4573, 3.1260])), - (('7.5Y', 2.0, 2.0), np.array([0.3660, 0.3858, 3.1260])), - (('7.5Y', 2.0, 4.0), np.array([0.4401, 0.4723, 3.1260])), - (('10Y', 2.0, 2.0), np.array([0.3556, 0.3848, 3.1260])), - (('10Y', 2.0, 4.0), np.array([0.4188, 0.4789, 3.1260])), - (('2.5GY', 2.0, 2.0), np.array([0.3421, 0.3803, 3.1260])), - (('2.5GY', 2.0, 4.0), np.array([0.3881, 0.4752, 3.1260])), - (('5GY', 2.0, 2.0), np.array([0.3309, 0.3743, 3.1260])), - (('5GY', 2.0, 4.0), np.array([0.3582, 0.4650, 3.1260])), - (('5GY', 2.0, 6.0), np.array([0.3839, 0.5748, 3.1260])), - (('7.5GY', 2.0, 2.0), np.array([0.3165, 0.3650, 3.1260])), - (('7.5GY', 2.0, 4.0), np.array([0.3248, 0.4457, 3.1260])), - (('7.5GY', 2.0, 6.0), np.array([0.3260, 0.5379, 3.1260])), - (('7.5GY', 2.0, 8.0), np.array([0.3160, 0.6509, 3.1260])), - (('10GY', 2.0, 2.0), np.array([0.3069, 0.3580, 3.1260])), - (('10GY', 2.0, 4.0), np.array([0.2986, 0.4240, 3.1260])), - (('10GY', 2.0, 6.0), np.array([0.2852, 0.4972, 3.1260])), - (('10GY', 2.0, 8.0), np.array([0.2628, 0.5837, 3.1260])), - (('10GY', 2.0, 10.0), np.array([0.2307, 0.6814, 3.1260])), - (('10GY', 2.0, 12.0), np.array([0.1907, 0.7798, 3.1260])), - (('2.5G', 2.0, 2.0), np.array([0.2978, 0.3507, 3.1260])), - (('2.5G', 2.0, 4.0), np.array([0.2763, 0.3998, 3.1260])), - (('2.5G', 2.0, 6.0), np.array([0.2493, 0.4522, 3.1260])), - (('2.5G', 2.0, 8.0), np.array([0.2192, 0.5042, 3.1260])), - (('2.5G', 2.0, 10.0), np.array([0.1773, 0.5698, 3.1260])), - (('2.5G', 2.0, 12.0), np.array([0.1307, 0.6308, 3.1260])), - (('2.5G', 2.0, 14.0), np.array([0.0820, 0.6860, 3.1260])), - (('2.5G', 2.0, 16.0), np.array([0.0329, 0.7358, 3.1260])), - (('5G', 2.0, 2.0), np.array([0.2918, 0.3450, 3.1260])), - (('5G', 2.0, 4.0), np.array([0.2640, 0.3845, 3.1260])), - (('5G', 2.0, 6.0), np.array([0.2318, 0.4231, 3.1260])), - (('5G', 2.0, 8.0), np.array([0.1979, 0.4583, 3.1260])), - (('5G', 2.0, 10.0), np.array([0.1560, 0.4981, 3.1260])), - (('5G', 2.0, 12.0), np.array([0.1120, 0.5358, 3.1260])), - (('5G', 2.0, 14.0), np.array([0.0688, 0.5691, 3.1260])), - (('5G', 2.0, 16.0), np.array([0.0277, 0.5986, 3.1260])), - (('7.5G', 2.0, 2.0), np.array([0.2869, 0.3400, 3.1260])), - (('7.5G', 2.0, 4.0), np.array([0.2540, 0.3705, 3.1260])), - (('7.5G', 2.0, 6.0), np.array([0.2200, 0.3983, 3.1260])), - (('7.5G', 2.0, 8.0), np.array([0.1842, 0.4244, 3.1260])), - (('7.5G', 2.0, 10.0), np.array([0.1442, 0.4505, 3.1260])), - (('7.5G', 2.0, 12.0), np.array([0.1022, 0.4759, 3.1260])), - (('7.5G', 2.0, 14.0), np.array([0.0629, 0.4973, 3.1260])), - (('7.5G', 2.0, 16.0), np.array([0.0276, 0.5153, 3.1260])), - (('10G', 2.0, 2.0), np.array([0.2820, 0.3341, 3.1260])), - (('10G', 2.0, 4.0), np.array([0.2442, 0.3559, 3.1260])), - (('10G', 2.0, 6.0), np.array([0.2092, 0.3739, 3.1260])), - (('10G', 2.0, 8.0), np.array([0.1705, 0.3911, 3.1260])), - (('10G', 2.0, 10.0), np.array([0.1321, 0.4059, 3.1260])), - (('10G', 2.0, 12.0), np.array([0.0934, 0.4183, 3.1260])), - (('10G', 2.0, 14.0), np.array([0.0599, 0.4270, 3.1260])), - (('10G', 2.0, 16.0), np.array([0.0285, 0.4327, 3.1260])), - (('2.5BG', 2.0, 2.0), np.array([0.2765, 0.3271, 3.1260])), - (('2.5BG', 2.0, 4.0), np.array([0.2343, 0.3378, 3.1260])), - (('2.5BG', 2.0, 6.0), np.array([0.1971, 0.3452, 3.1260])), - (('2.5BG', 2.0, 8.0), np.array([0.1557, 0.3517, 3.1260])), - (('2.5BG', 2.0, 10.0), np.array([0.1190, 0.3551, 3.1260])), - (('2.5BG', 2.0, 12.0), np.array([0.0851, 0.3576, 3.1260])), - (('2.5BG', 2.0, 14.0), np.array([0.0555, 0.3588, 3.1260])), - (('5BG', 2.0, 2.0), np.array([0.2697, 0.3175, 3.1260])), - (('5BG', 2.0, 4.0), np.array([0.2234, 0.3150, 3.1260])), - (('5BG', 2.0, 6.0), np.array([0.1843, 0.3110, 3.1260])), - (('5BG', 2.0, 8.0), np.array([0.1405, 0.3037, 3.1260])), - (('5BG', 2.0, 10.0), np.array([0.1050, 0.2956, 3.1260])), - (('5BG', 2.0, 12.0), np.array([0.0769, 0.2880, 3.1260])), - (('7.5BG', 2.0, 2.0), np.array([0.2651, 0.3098, 3.1260])), - (('7.5BG', 2.0, 4.0), np.array([0.2162, 0.2981, 3.1260])), - (('7.5BG', 2.0, 6.0), np.array([0.1747, 0.2853, 3.1260])), - (('7.5BG', 2.0, 8.0), np.array([0.1325, 0.2710, 3.1260])), - (('7.5BG', 2.0, 10.0), np.array([0.0991, 0.2582, 3.1260])), - (('7.5BG', 2.0, 12.0), np.array([0.0724, 0.2478, 3.1260])), - (('10BG', 2.0, 2.0), np.array([0.2606, 0.3010, 3.1260])), - (('10BG', 2.0, 4.0), np.array([0.2096, 0.2790, 3.1260])), - (('10BG', 2.0, 6.0), np.array([0.1669, 0.2570, 3.1260])), - (('10BG', 2.0, 8.0), np.array([0.1258, 0.2331, 3.1260])), - (('10BG', 2.0, 10.0), np.array([0.0929, 0.2133, 3.1260])), - (('2.5B', 2.0, 2.0), np.array([0.2578, 0.2940, 3.1260])), - (('2.5B', 2.0, 4.0), np.array([0.2060, 0.2649, 3.1260])), - (('2.5B', 2.0, 6.0), np.array([0.1621, 0.2358, 3.1260])), - (('2.5B', 2.0, 8.0), np.array([0.1230, 0.2076, 3.1260])), - (('2.5B', 2.0, 10.0), np.array([0.0911, 0.1828, 3.1260])), - (('5B', 2.0, 2.0), np.array([0.2559, 0.2874, 3.1260])), - (('5B', 2.0, 4.0), np.array([0.2048, 0.2518, 3.1260])), - (('5B', 2.0, 6.0), np.array([0.1617, 0.2162, 3.1260])), - (('5B', 2.0, 8.0), np.array([0.1245, 0.1827, 3.1260])), - (('5B', 2.0, 10.0), np.array([0.0965, 0.1558, 3.1260])), - (('7.5B', 2.0, 2.0), np.array([0.2545, 0.2799, 3.1260])), - (('7.5B', 2.0, 4.0), np.array([0.2063, 0.2400, 3.1260])), - (('7.5B', 2.0, 6.0), np.array([0.1658, 0.2026, 3.1260])), - (('7.5B', 2.0, 8.0), np.array([0.1313, 0.1692, 3.1260])), - (('7.5B', 2.0, 10.0), np.array([0.1051, 0.1422, 3.1260])), - (('10B', 2.0, 2.0), np.array([0.2558, 0.2725, 3.1260])), - (('10B', 2.0, 4.0), np.array([0.2102, 0.2313, 3.1260])), - (('10B', 2.0, 6.0), np.array([0.1716, 0.1937, 3.1260])), - (('10B', 2.0, 8.0), np.array([0.1396, 0.1603, 3.1260])), - (('10B', 2.0, 10.0), np.array([0.1157, 0.1346, 3.1260])), - (('2.5PB', 2.0, 2.0), np.array([0.2592, 0.2675, 3.1260])), - (('2.5PB', 2.0, 4.0), np.array([0.2175, 0.2245, 3.1260])), - (('2.5PB', 2.0, 6.0), np.array([0.1825, 0.1857, 3.1260])), - (('2.5PB', 2.0, 8.0), np.array([0.1540, 0.1530, 3.1260])), - (('2.5PB', 2.0, 10.0), np.array([0.1332, 0.1278, 3.1260])), - (('2.5PB', 2.0, 12.0), np.array([0.1166, 0.1076, 3.1260])), - (('5PB', 2.0, 2.0), np.array([0.2638, 0.2624, 3.1260])), - (('5PB', 2.0, 4.0), np.array([0.2263, 0.2192, 3.1260])), - (('5PB', 2.0, 6.0), np.array([0.1942, 0.1811, 3.1260])), - (('5PB', 2.0, 8.0), np.array([0.1685, 0.1491, 3.1260])), - (('5PB', 2.0, 10.0), np.array([0.1500, 0.1240, 3.1260])), - (('5PB', 2.0, 12.0), np.array([0.1363, 0.1048, 3.1260])), - (('5PB', 2.0, 14.0), np.array([0.1253, 0.0873, 3.1260])), - (('7.5PB', 2.0, 2.0), np.array([0.2712, 0.2582, 3.1260])), - (('7.5PB', 2.0, 4.0), np.array([0.2420, 0.2148, 3.1260])), - (('7.5PB', 2.0, 6.0), np.array([0.2189, 0.1790, 3.1260])), - (('7.5PB', 2.0, 8.0), np.array([0.2005, 0.1495, 3.1260])), - (('7.5PB', 2.0, 10.0), np.array([0.1882, 0.1258, 3.1260])), - (('7.5PB', 2.0, 12.0), np.array([0.1813, 0.1094, 3.1260])), - (('7.5PB', 2.0, 14.0), np.array([0.1762, 0.0955, 3.1260])), - (('7.5PB', 2.0, 16.0), np.array([0.1728, 0.0839, 3.1260])), - (('7.5PB', 2.0, 18.0), np.array([0.1701, 0.0742, 3.1260])), - (('7.5PB', 2.0, 20.0), np.array([0.1685, 0.0666, 3.1260])), - (('7.5PB', 2.0, 22.0), np.array([0.1670, 0.0594, 3.1260])), - (('7.5PB', 2.0, 24.0), np.array([0.1660, 0.0538, 3.1260])), - (('7.5PB', 2.0, 26.0), np.array([0.1653, 0.0492, 3.1260])), - (('7.5PB', 2.0, 28.0), np.array([0.1647, 0.0451, 3.1260])), - (('7.5PB', 2.0, 30.0), np.array([0.1640, 0.0409, 3.1260])), - (('7.5PB', 2.0, 32.0), np.array([0.1635, 0.0373, 3.1260])), - (('7.5PB', 2.0, 34.0), np.array([0.1630, 0.0340, 3.1260])), - (('7.5PB', 2.0, 36.0), np.array([0.1628, 0.0310, 3.1260])), - (('7.5PB', 2.0, 38.0), np.array([0.1623, 0.0280, 3.1260])), - (('10PB', 2.0, 2.0), np.array([0.2803, 0.2567, 3.1260])), - (('10PB', 2.0, 4.0), np.array([0.2600, 0.2162, 3.1260])), - (('10PB', 2.0, 6.0), np.array([0.2440, 0.1840, 3.1260])), - (('10PB', 2.0, 8.0), np.array([0.2294, 0.1551, 3.1260])), - (('10PB', 2.0, 10.0), np.array([0.2200, 0.1330, 3.1260])), - (('10PB', 2.0, 12.0), np.array([0.2139, 0.1170, 3.1260])), - (('10PB', 2.0, 14.0), np.array([0.2087, 0.1026, 3.1260])), - (('10PB', 2.0, 16.0), np.array([0.2052, 0.0910, 3.1260])), - (('10PB', 2.0, 18.0), np.array([0.2021, 0.0808, 3.1260])), - (('10PB', 2.0, 20.0), np.array([0.1998, 0.0718, 3.1260])), - (('10PB', 2.0, 22.0), np.array([0.1978, 0.0643, 3.1260])), - (('10PB', 2.0, 24.0), np.array([0.1962, 0.0578, 3.1260])), - (('10PB', 2.0, 26.0), np.array([0.1949, 0.0520, 3.1260])), - (('10PB', 2.0, 28.0), np.array([0.1937, 0.0471, 3.1260])), - (('10PB', 2.0, 30.0), np.array([0.1925, 0.0420, 3.1260])), - (('10PB', 2.0, 32.0), np.array([0.1918, 0.0379, 3.1260])), - (('10PB', 2.0, 34.0), np.array([0.1911, 0.0344, 3.1260])), - (('2.5P', 2.0, 2.0), np.array([0.2892, 0.2583, 3.1260])), - (('2.5P', 2.0, 4.0), np.array([0.2758, 0.2208, 3.1260])), - (('2.5P', 2.0, 6.0), np.array([0.2661, 0.1921, 3.1260])), - (('2.5P', 2.0, 8.0), np.array([0.2570, 0.1635, 3.1260])), - (('2.5P', 2.0, 10.0), np.array([0.2501, 0.1422, 3.1260])), - (('2.5P', 2.0, 12.0), np.array([0.2449, 0.1245, 3.1260])), - (('2.5P', 2.0, 14.0), np.array([0.2406, 0.1100, 3.1260])), - (('2.5P', 2.0, 16.0), np.array([0.2372, 0.0980, 3.1260])), - (('2.5P', 2.0, 18.0), np.array([0.2345, 0.0873, 3.1260])), - (('2.5P', 2.0, 20.0), np.array([0.2320, 0.0779, 3.1260])), - (('2.5P', 2.0, 22.0), np.array([0.2298, 0.0696, 3.1260])), - (('2.5P', 2.0, 24.0), np.array([0.2277, 0.0621, 3.1260])), - (('2.5P', 2.0, 26.0), np.array([0.2260, 0.0555, 3.1260])), - (('2.5P', 2.0, 28.0), np.array([0.2245, 0.0491, 3.1260])), - (('2.5P', 2.0, 30.0), np.array([0.2231, 0.0432, 3.1260])), - (('5P', 2.0, 2.0), np.array([0.2984, 0.2612, 3.1260])), - (('5P', 2.0, 4.0), np.array([0.2908, 0.2261, 3.1260])), - (('5P', 2.0, 6.0), np.array([0.2850, 0.1992, 3.1260])), - (('5P', 2.0, 8.0), np.array([0.2791, 0.1707, 3.1260])), - (('5P', 2.0, 10.0), np.array([0.2748, 0.1500, 3.1260])), - (('5P', 2.0, 12.0), np.array([0.2709, 0.1320, 3.1260])), - (('5P', 2.0, 14.0), np.array([0.2676, 0.1163, 3.1260])), - (('5P', 2.0, 16.0), np.array([0.2652, 0.1045, 3.1260])), - (('5P', 2.0, 18.0), np.array([0.2632, 0.0935, 3.1260])), - (('5P', 2.0, 20.0), np.array([0.2612, 0.0838, 3.1260])), - (('5P', 2.0, 22.0), np.array([0.2597, 0.0750, 3.1260])), - (('5P', 2.0, 24.0), np.array([0.2582, 0.0669, 3.1260])), - (('5P', 2.0, 26.0), np.array([0.2569, 0.0594, 3.1260])), - (('5P', 2.0, 28.0), np.array([0.2559, 0.0525, 3.1260])), - (('7.5P', 2.0, 2.0), np.array([0.3071, 0.2647, 3.1260])), - (('7.5P', 2.0, 4.0), np.array([0.3048, 0.2321, 3.1260])), - (('7.5P', 2.0, 6.0), np.array([0.3025, 0.2058, 3.1260])), - (('7.5P', 2.0, 8.0), np.array([0.3000, 0.1781, 3.1260])), - (('7.5P', 2.0, 10.0), np.array([0.2979, 0.1569, 3.1260])), - (('7.5P', 2.0, 12.0), np.array([0.2956, 0.1392, 3.1260])), - (('7.5P', 2.0, 14.0), np.array([0.2938, 0.1235, 3.1260])), - (('7.5P', 2.0, 16.0), np.array([0.2922, 0.1106, 3.1260])), - (('7.5P', 2.0, 18.0), np.array([0.2912, 0.0995, 3.1260])), - (('7.5P', 2.0, 20.0), np.array([0.2902, 0.0901, 3.1260])), - (('7.5P', 2.0, 22.0), np.array([0.2890, 0.0799, 3.1260])), - (('7.5P', 2.0, 24.0), np.array([0.2882, 0.0719, 3.1260])), - (('10P', 2.0, 2.0), np.array([0.3161, 0.2691, 3.1260])), - (('10P', 2.0, 4.0), np.array([0.3189, 0.2390, 3.1260])), - (('10P', 2.0, 6.0), np.array([0.3207, 0.2132, 3.1260])), - (('10P', 2.0, 8.0), np.array([0.3219, 0.1862, 3.1260])), - (('10P', 2.0, 10.0), np.array([0.3230, 0.1659, 3.1260])), - (('10P', 2.0, 12.0), np.array([0.3233, 0.1477, 3.1260])), - (('10P', 2.0, 14.0), np.array([0.3235, 0.1317, 3.1260])), - (('10P', 2.0, 16.0), np.array([0.3235, 0.1181, 3.1260])), - (('10P', 2.0, 18.0), np.array([0.3233, 0.1063, 3.1260])), - (('10P', 2.0, 20.0), np.array([0.3231, 0.0962, 3.1260])), - (('10P', 2.0, 22.0), np.array([0.3230, 0.0861, 3.1260])), - (('2.5RP', 2.0, 2.0), np.array([0.3279, 0.2754, 3.1260])), - (('2.5RP', 2.0, 4.0), np.array([0.3382, 0.2496, 3.1260])), - (('2.5RP', 2.0, 6.0), np.array([0.3470, 0.2259, 3.1260])), - (('2.5RP', 2.0, 8.0), np.array([0.3555, 0.2003, 3.1260])), - (('2.5RP', 2.0, 10.0), np.array([0.3617, 0.1800, 3.1260])), - (('2.5RP', 2.0, 12.0), np.array([0.3668, 0.1618, 3.1260])), - (('2.5RP', 2.0, 14.0), np.array([0.3711, 0.1449, 3.1260])), - (('2.5RP', 2.0, 16.0), np.array([0.3748, 0.1310, 3.1260])), - (('2.5RP', 2.0, 18.0), np.array([0.3778, 0.1188, 3.1260])), - (('2.5RP', 2.0, 20.0), np.array([0.3802, 0.1080, 3.1260])), - (('5RP', 2.0, 2.0), np.array([0.3383, 0.2829, 3.1260])), - (('5RP', 2.0, 4.0), np.array([0.3558, 0.2597, 3.1260])), - (('5RP', 2.0, 6.0), np.array([0.3708, 0.2380, 3.1260])), - (('5RP', 2.0, 8.0), np.array([0.3858, 0.2140, 3.1260])), - (('5RP', 2.0, 10.0), np.array([0.3971, 0.1939, 3.1260])), - (('5RP', 2.0, 12.0), np.array([0.4080, 0.1764, 3.1260])), - (('5RP', 2.0, 14.0), np.array([0.4180, 0.1598, 3.1260])), - (('5RP', 2.0, 16.0), np.array([0.4269, 0.1454, 3.1260])), - (('5RP', 2.0, 18.0), np.array([0.4338, 0.1340, 3.1260])), - (('7.5RP', 2.0, 2.0), np.array([0.3459, 0.2892, 3.1260])), - (('7.5RP', 2.0, 4.0), np.array([0.3702, 0.2683, 3.1260])), - (('7.5RP', 2.0, 6.0), np.array([0.3918, 0.2490, 3.1260])), - (('7.5RP', 2.0, 8.0), np.array([0.4137, 0.2276, 3.1260])), - (('7.5RP', 2.0, 10.0), np.array([0.4321, 0.2082, 3.1260])), - (('7.5RP', 2.0, 12.0), np.array([0.4481, 0.1903, 3.1260])), - (('7.5RP', 2.0, 14.0), np.array([0.4624, 0.1737, 3.1260])), - (('7.5RP', 2.0, 16.0), np.array([0.4744, 0.1595, 3.1260])), - (('10RP', 3.0, 2.0), np.array([0.3526, 0.3068, 6.5550])), - (('10RP', 3.0, 4.0), np.array([0.3889, 0.2969, 6.5550])), - (('10RP', 3.0, 6.0), np.array([0.4218, 0.2864, 6.5550])), - (('10RP', 3.0, 8.0), np.array([0.4552, 0.2741, 6.5550])), - (('10RP', 3.0, 10.0), np.array([0.4851, 0.2618, 6.5550])), - (('10RP', 3.0, 12.0), np.array([0.5139, 0.2489, 6.5550])), - (('10RP', 3.0, 14.0), np.array([0.5380, 0.2369, 6.5550])), - (('10RP', 3.0, 16.0), np.array([0.5628, 0.2241, 6.5550])), - (('2.5R', 3.0, 2.0), np.array([0.3591, 0.3130, 6.5550])), - (('2.5R', 3.0, 4.0), np.array([0.4021, 0.3076, 6.5550])), - (('2.5R', 3.0, 6.0), np.array([0.4409, 0.3009, 6.5550])), - (('2.5R', 3.0, 8.0), np.array([0.4821, 0.2918, 6.5550])), - (('2.5R', 3.0, 10.0), np.array([0.5191, 0.2811, 6.5550])), - (('2.5R', 3.0, 12.0), np.array([0.5536, 0.2691, 6.5550])), - (('2.5R', 3.0, 14.0), np.array([0.5828, 0.2579, 6.5550])), - (('2.5R', 3.0, 16.0), np.array([0.6116, 0.2456, 6.5550])), - (('5R', 3.0, 2.0), np.array([0.3645, 0.3190, 6.5550])), - (('5R', 3.0, 4.0), np.array([0.4148, 0.3190, 6.5550])), - (('5R', 3.0, 6.0), np.array([0.4592, 0.3168, 6.5550])), - (('5R', 3.0, 8.0), np.array([0.5064, 0.3114, 6.5550])), - (('5R', 3.0, 10.0), np.array([0.5500, 0.3024, 6.5550])), - (('5R', 3.0, 12.0), np.array([0.5884, 0.2904, 6.5550])), - (('5R', 3.0, 14.0), np.array([0.6204, 0.2789, 6.5550])), - (('5R', 3.0, 16.0), np.array([0.6520, 0.2660, 6.5550])), - (('7.5R', 3.0, 2.0), np.array([0.3690, 0.3248, 6.5550])), - (('7.5R', 3.0, 4.0), np.array([0.4240, 0.3302, 6.5550])), - (('7.5R', 3.0, 6.0), np.array([0.4738, 0.3316, 6.5550])), - (('7.5R', 3.0, 8.0), np.array([0.5251, 0.3297, 6.5550])), - (('7.5R', 3.0, 10.0), np.array([0.5730, 0.3240, 6.5550])), - (('7.5R', 3.0, 12.0), np.array([0.6158, 0.3129, 6.5550])), - (('7.5R', 3.0, 14.0), np.array([0.6492, 0.3012, 6.5550])), - (('7.5R', 3.0, 16.0), np.array([0.6817, 0.2872, 6.5550])), - (('10R', 3.0, 2.0), np.array([0.3728, 0.3314, 6.5550])), - (('10R', 3.0, 4.0), np.array([0.4308, 0.3412, 6.5550])), - (('10R', 3.0, 6.0), np.array([0.4854, 0.3467, 6.5550])), - (('10R', 3.0, 8.0), np.array([0.5393, 0.3477, 6.5550])), - (('10R', 3.0, 10.0), np.array([0.5871, 0.3440, 6.5550])), - (('10R', 3.0, 12.0), np.array([0.6322, 0.3361, 6.5550])), - (('10R', 3.0, 14.0), np.array([0.6703, 0.3249, 6.5550])), - (('2.5YR', 3.0, 2.0), np.array([0.3757, 0.3391, 6.5550])), - (('2.5YR', 3.0, 4.0), np.array([0.4360, 0.3563, 6.5550])), - (('2.5YR', 3.0, 6.0), np.array([0.4954, 0.3692, 6.5550])), - (('2.5YR', 3.0, 8.0), np.array([0.5475, 0.3771, 6.5550])), - (('2.5YR', 3.0, 10.0), np.array([0.5941, 0.3818, 6.5550])), - (('5YR', 3.0, 2.0), np.array([0.3771, 0.3476, 6.5550])), - (('5YR', 3.0, 4.0), np.array([0.4376, 0.3715, 6.5550])), - (('5YR', 3.0, 6.0), np.array([0.4966, 0.3908, 6.5550])), - (('5YR', 3.0, 8.0), np.array([0.5456, 0.4040, 6.5550])), - (('7.5YR', 3.0, 2.0), np.array([0.3771, 0.3549, 6.5550])), - (('7.5YR', 3.0, 4.0), np.array([0.4378, 0.3865, 6.5550])), - (('7.5YR', 3.0, 6.0), np.array([0.4930, 0.4116, 6.5550])), - (('7.5YR', 3.0, 8.0), np.array([0.5390, 0.4306, 6.5550])), - (('10YR', 3.0, 2.0), np.array([0.3747, 0.3630, 6.5550])), - (('10YR', 3.0, 4.0), np.array([0.4341, 0.4018, 6.5550])), - (('10YR', 3.0, 6.0), np.array([0.4872, 0.4326, 6.5550])), - (('10YR', 3.0, 8.0), np.array([0.5305, 0.4559, 6.5550])), - (('2.5Y', 3.0, 2.0), np.array([0.3703, 0.3700, 6.5550])), - (('2.5Y', 3.0, 4.0), np.array([0.4277, 0.4166, 6.5550])), - (('2.5Y', 3.0, 6.0), np.array([0.4784, 0.4531, 6.5550])), - (('5Y', 3.0, 2.0), np.array([0.3646, 0.3748, 6.5550])), - (('5Y', 3.0, 4.0), np.array([0.4191, 0.4283, 6.5550])), - (('5Y', 3.0, 6.0), np.array([0.4670, 0.4711, 6.5550])), - (('7.5Y', 3.0, 2.0), np.array([0.3589, 0.3778, 6.5550])), - (('7.5Y', 3.0, 4.0), np.array([0.4086, 0.4379, 6.5550])), - (('7.5Y', 3.0, 6.0), np.array([0.4526, 0.4889, 6.5550])), - (('10Y', 3.0, 2.0), np.array([0.3513, 0.3789, 6.5550])), - (('10Y', 3.0, 4.0), np.array([0.3961, 0.4452, 6.5550])), - (('10Y', 3.0, 6.0), np.array([0.4345, 0.5026, 6.5550])), - (('2.5GY', 3.0, 2.0), np.array([0.3412, 0.3768, 6.5550])), - (('2.5GY', 3.0, 4.0), np.array([0.3772, 0.4484, 6.5550])), - (('2.5GY', 3.0, 6.0), np.array([0.4069, 0.5110, 6.5550])), - (('5GY', 3.0, 2.0), np.array([0.3319, 0.3729, 6.5550])), - (('5GY', 3.0, 4.0), np.array([0.3554, 0.4429, 6.5550])), - (('5GY', 3.0, 6.0), np.array([0.3750, 0.5109, 6.5550])), - (('5GY', 3.0, 8.0), np.array([0.3924, 0.5832, 6.5550])), - (('7.5GY', 3.0, 2.0), np.array([0.3180, 0.3644, 6.5550])), - (('7.5GY', 3.0, 4.0), np.array([0.3270, 0.4288, 6.5550])), - (('7.5GY', 3.0, 6.0), np.array([0.3333, 0.4967, 6.5550])), - (('7.5GY', 3.0, 8.0), np.array([0.3341, 0.5700, 6.5550])), - (('7.5GY', 3.0, 10.0), np.array([0.3266, 0.6448, 6.5550])), - (('10GY', 3.0, 2.0), np.array([0.3088, 0.3578, 6.5550])), - (('10GY', 3.0, 4.0), np.array([0.3053, 0.4123, 6.5550])), - (('10GY', 3.0, 6.0), np.array([0.2992, 0.4717, 6.5550])), - (('10GY', 3.0, 8.0), np.array([0.2887, 0.5361, 6.5550])), - (('10GY', 3.0, 10.0), np.array([0.2724, 0.6026, 6.5550])), - (('10GY', 3.0, 12.0), np.array([0.2531, 0.6700, 6.5550])), - (('10GY', 3.0, 14.0), np.array([0.2283, 0.7423, 6.5550])), - (('2.5G', 3.0, 2.0), np.array([0.2999, 0.3500, 6.5550])), - (('2.5G', 3.0, 4.0), np.array([0.2836, 0.3915, 6.5550])), - (('2.5G', 3.0, 6.0), np.array([0.2642, 0.4342, 6.5550])), - (('2.5G', 3.0, 8.0), np.array([0.2435, 0.4752, 6.5550])), - (('2.5G', 3.0, 10.0), np.array([0.2170, 0.5211, 6.5550])), - (('2.5G', 3.0, 12.0), np.array([0.1902, 0.5642, 6.5550])), - (('2.5G', 3.0, 14.0), np.array([0.1626, 0.6052, 6.5550])), - (('2.5G', 3.0, 16.0), np.array([0.1341, 0.6420, 6.5550])), - (('2.5G', 3.0, 18.0), np.array([0.1049, 0.6766, 6.5550])), - (('2.5G', 3.0, 20.0), np.array([0.0720, 0.7127, 6.5550])), - (('2.5G', 3.0, 22.0), np.array([0.0390, 0.7468, 6.5550])), - (('5G', 3.0, 2.0), np.array([0.2935, 0.3439, 6.5550])), - (('5G', 3.0, 4.0), np.array([0.2711, 0.3780, 6.5550])), - (('5G', 3.0, 6.0), np.array([0.2471, 0.4100, 6.5550])), - (('5G', 3.0, 8.0), np.array([0.2228, 0.4380, 6.5550])), - (('5G', 3.0, 10.0), np.array([0.1935, 0.4682, 6.5550])), - (('5G', 3.0, 12.0), np.array([0.1660, 0.4948, 6.5550])), - (('5G', 3.0, 14.0), np.array([0.1382, 0.5197, 6.5550])), - (('5G', 3.0, 16.0), np.array([0.1120, 0.5414, 6.5550])), - (('5G', 3.0, 18.0), np.array([0.0882, 0.5605, 6.5550])), - (('5G', 3.0, 20.0), np.array([0.0620, 0.5802, 6.5550])), - (('5G', 3.0, 22.0), np.array([0.0340, 0.6011, 6.5550])), - (('7.5G', 3.0, 2.0), np.array([0.2890, 0.3391, 6.5550])), - (('7.5G', 3.0, 4.0), np.array([0.2618, 0.3667, 6.5550])), - (('7.5G', 3.0, 6.0), np.array([0.2346, 0.3901, 6.5550])), - (('7.5G', 3.0, 8.0), np.array([0.2088, 0.4101, 6.5550])), - (('7.5G', 3.0, 10.0), np.array([0.1800, 0.4310, 6.5550])), - (('7.5G', 3.0, 12.0), np.array([0.1516, 0.4505, 6.5550])), - (('7.5G', 3.0, 14.0), np.array([0.1262, 0.4667, 6.5550])), - (('7.5G', 3.0, 16.0), np.array([0.1023, 0.4818, 6.5550])), - (('7.5G', 3.0, 18.0), np.array([0.0798, 0.4954, 6.5550])), - (('7.5G', 3.0, 20.0), np.array([0.0568, 0.5082, 6.5550])), - (('7.5G', 3.0, 22.0), np.array([0.0332, 0.5206, 6.5550])), - (('10G', 3.0, 2.0), np.array([0.2844, 0.3337, 6.5550])), - (('10G', 3.0, 4.0), np.array([0.2525, 0.3537, 6.5550])), - (('10G', 3.0, 6.0), np.array([0.2240, 0.3699, 6.5550])), - (('10G', 3.0, 8.0), np.array([0.1970, 0.3841, 6.5550])), - (('10G', 3.0, 10.0), np.array([0.1688, 0.3974, 6.5550])), - (('10G', 3.0, 12.0), np.array([0.1411, 0.4095, 6.5550])), - (('10G', 3.0, 14.0), np.array([0.1161, 0.4192, 6.5550])), - (('10G', 3.0, 16.0), np.array([0.0925, 0.4275, 6.5550])), - (('10G', 3.0, 18.0), np.array([0.0718, 0.4340, 6.5550])), - (('10G', 3.0, 20.0), np.array([0.0528, 0.4393, 6.5550])), - (('10G', 3.0, 22.0), np.array([0.0333, 0.4444, 6.5550])), - (('2.5BG', 3.0, 2.0), np.array([0.2799, 0.3271, 6.5550])), - (('2.5BG', 3.0, 4.0), np.array([0.2437, 0.3386, 6.5550])), - (('2.5BG', 3.0, 6.0), np.array([0.2132, 0.3468, 6.5550])), - (('2.5BG', 3.0, 8.0), np.array([0.1845, 0.3531, 6.5550])), - (('2.5BG', 3.0, 10.0), np.array([0.1552, 0.3580, 6.5550])), - (('2.5BG', 3.0, 12.0), np.array([0.1288, 0.3620, 6.5550])), - (('2.5BG', 3.0, 14.0), np.array([0.1051, 0.3648, 6.5550])), - (('2.5BG', 3.0, 16.0), np.array([0.0843, 0.3667, 6.5550])), - (('2.5BG', 3.0, 18.0), np.array([0.0648, 0.3682, 6.5550])), - (('2.5BG', 3.0, 20.0), np.array([0.0482, 0.3695, 6.5550])), - (('5BG', 3.0, 2.0), np.array([0.2742, 0.3192, 6.5550])), - (('5BG', 3.0, 4.0), np.array([0.2343, 0.3200, 6.5550])), - (('5BG', 3.0, 6.0), np.array([0.2020, 0.3188, 6.5550])), - (('5BG', 3.0, 8.0), np.array([0.1703, 0.3159, 6.5550])), - (('5BG', 3.0, 10.0), np.array([0.1410, 0.3118, 6.5550])), - (('5BG', 3.0, 12.0), np.array([0.1158, 0.3071, 6.5550])), - (('5BG', 3.0, 14.0), np.array([0.0940, 0.3027, 6.5550])), - (('5BG', 3.0, 16.0), np.array([0.0735, 0.2979, 6.5550])), - (('5BG', 3.0, 18.0), np.array([0.0580, 0.2940, 6.5550])), - (('7.5BG', 3.0, 2.0), np.array([0.2699, 0.3120, 6.5550])), - (('7.5BG', 3.0, 4.0), np.array([0.2272, 0.3041, 6.5550])), - (('7.5BG', 3.0, 6.0), np.array([0.1928, 0.2958, 6.5550])), - (('7.5BG', 3.0, 8.0), np.array([0.1620, 0.2872, 6.5550])), - (('7.5BG', 3.0, 10.0), np.array([0.1326, 0.2784, 6.5550])), - (('7.5BG', 3.0, 12.0), np.array([0.1086, 0.2706, 6.5550])), - (('7.5BG', 3.0, 14.0), np.array([0.0874, 0.2627, 6.5550])), - (('7.5BG', 3.0, 16.0), np.array([0.0691, 0.2559, 6.5550])), - (('10BG', 3.0, 2.0), np.array([0.2660, 0.3050, 6.5550])), - (('10BG', 3.0, 4.0), np.array([0.2221, 0.2886, 6.5550])), - (('10BG', 3.0, 6.0), np.array([0.1861, 0.2722, 6.5550])), - (('10BG', 3.0, 8.0), np.array([0.1551, 0.2571, 6.5550])), - (('10BG', 3.0, 10.0), np.array([0.1250, 0.2411, 6.5550])), - (('10BG', 3.0, 12.0), np.array([0.1018, 0.2281, 6.5550])), - (('10BG', 3.0, 14.0), np.array([0.0798, 0.2151, 6.5550])), - (('2.5B', 3.0, 2.0), np.array([0.2636, 0.2983, 6.5550])), - (('2.5B', 3.0, 4.0), np.array([0.2183, 0.2748, 6.5550])), - (('2.5B', 3.0, 6.0), np.array([0.1826, 0.2536, 6.5550])), - (('2.5B', 3.0, 8.0), np.array([0.1511, 0.2331, 6.5550])), - (('2.5B', 3.0, 10.0), np.array([0.1220, 0.2132, 6.5550])), - (('2.5B', 3.0, 12.0), np.array([0.0989, 0.1963, 6.5550])), - (('5B', 3.0, 2.0), np.array([0.2617, 0.2921, 6.5550])), - (('5B', 3.0, 4.0), np.array([0.2176, 0.2632, 6.5550])), - (('5B', 3.0, 6.0), np.array([0.1835, 0.2375, 6.5550])), - (('5B', 3.0, 8.0), np.array([0.1527, 0.2119, 6.5550])), - (('5B', 3.0, 10.0), np.array([0.1259, 0.1879, 6.5550])), - (('5B', 3.0, 12.0), np.array([0.1042, 0.1681, 6.5550])), - (('7.5B', 3.0, 2.0), np.array([0.2616, 0.2857, 6.5550])), - (('7.5B', 3.0, 4.0), np.array([0.2200, 0.2536, 6.5550])), - (('7.5B', 3.0, 6.0), np.array([0.1875, 0.2258, 6.5550])), - (('7.5B', 3.0, 8.0), np.array([0.1583, 0.1987, 6.5550])), - (('7.5B', 3.0, 10.0), np.array([0.1343, 0.1756, 6.5550])), - (('7.5B', 3.0, 12.0), np.array([0.1131, 0.1542, 6.5550])), - (('10B', 3.0, 2.0), np.array([0.2631, 0.2801, 6.5550])), - (('10B', 3.0, 4.0), np.array([0.2246, 0.2467, 6.5550])), - (('10B', 3.0, 6.0), np.array([0.1933, 0.2173, 6.5550])), - (('10B', 3.0, 8.0), np.array([0.1658, 0.1905, 6.5550])), - (('10B', 3.0, 10.0), np.array([0.1432, 0.1675, 6.5550])), - (('10B', 3.0, 12.0), np.array([0.1228, 0.1460, 6.5550])), - (('10B', 3.0, 14.0), np.array([0.1065, 0.1285, 6.5550])), - (('2.5PB', 3.0, 2.0), np.array([0.2663, 0.2756, 6.5550])), - (('2.5PB', 3.0, 4.0), np.array([0.2312, 0.2405, 6.5550])), - (('2.5PB', 3.0, 6.0), np.array([0.2022, 0.2101, 6.5550])), - (('2.5PB', 3.0, 8.0), np.array([0.1780, 0.1833, 6.5550])), - (('2.5PB', 3.0, 10.0), np.array([0.1576, 0.1600, 6.5550])), - (('2.5PB', 3.0, 12.0), np.array([0.1398, 0.1395, 6.5550])), - (('2.5PB', 3.0, 14.0), np.array([0.1251, 0.1218, 6.5550])), - (('5PB', 3.0, 2.0), np.array([0.2708, 0.2719, 6.5550])), - (('5PB', 3.0, 4.0), np.array([0.2393, 0.2361, 6.5550])), - (('5PB', 3.0, 6.0), np.array([0.2122, 0.2052, 6.5550])), - (('5PB', 3.0, 8.0), np.array([0.1908, 0.1799, 6.5550])), - (('5PB', 3.0, 10.0), np.array([0.1718, 0.1562, 6.5550])), - (('5PB', 3.0, 12.0), np.array([0.1557, 0.1356, 6.5550])), - (('5PB', 3.0, 14.0), np.array([0.1431, 0.1184, 6.5550])), - (('5PB', 3.0, 16.0), np.array([0.1318, 0.1024, 6.5550])), - (('5PB', 3.0, 18.0), np.array([0.1228, 0.0895, 6.5550])), - (('7.5PB', 3.0, 2.0), np.array([0.2777, 0.2687, 6.5550])), - (('7.5PB', 3.0, 4.0), np.array([0.2520, 0.2319, 6.5550])), - (('7.5PB', 3.0, 6.0), np.array([0.2311, 0.2010, 6.5550])), - (('7.5PB', 3.0, 8.0), np.array([0.2149, 0.1761, 6.5550])), - (('7.5PB', 3.0, 10.0), np.array([0.2005, 0.1536, 6.5550])), - (('7.5PB', 3.0, 12.0), np.array([0.1903, 0.1353, 6.5550])), - (('7.5PB', 3.0, 14.0), np.array([0.1824, 0.1188, 6.5550])), - (('7.5PB', 3.0, 16.0), np.array([0.1765, 0.1048, 6.5550])), - (('7.5PB', 3.0, 18.0), np.array([0.1730, 0.0948, 6.5550])), - (('7.5PB', 3.0, 20.0), np.array([0.1702, 0.0867, 6.5550])), - (('7.5PB', 3.0, 22.0), np.array([0.1677, 0.0782, 6.5550])), - (('7.5PB', 3.0, 24.0), np.array([0.1658, 0.0711, 6.5550])), - (('7.5PB', 3.0, 26.0), np.array([0.1642, 0.0655, 6.5550])), - (('7.5PB', 3.0, 28.0), np.array([0.1632, 0.0609, 6.5550])), - (('7.5PB', 3.0, 30.0), np.array([0.1621, 0.0556, 6.5550])), - (('7.5PB', 3.0, 32.0), np.array([0.1612, 0.0511, 6.5550])), - (('7.5PB', 3.0, 34.0), np.array([0.1608, 0.0480, 6.5550])), - (('10PB', 3.0, 2.0), np.array([0.2847, 0.2670, 6.5550])), - (('10PB', 3.0, 4.0), np.array([0.2660, 0.2319, 6.5550])), - (('10PB', 3.0, 6.0), np.array([0.2511, 0.2031, 6.5550])), - (('10PB', 3.0, 8.0), np.array([0.2387, 0.1786, 6.5550])), - (('10PB', 3.0, 10.0), np.array([0.2278, 0.1565, 6.5550])), - (('10PB', 3.0, 12.0), np.array([0.2206, 0.1407, 6.5550])), - (('10PB', 3.0, 14.0), np.array([0.2142, 0.1250, 6.5550])), - (('10PB', 3.0, 16.0), np.array([0.2092, 0.1118, 6.5550])), - (('10PB', 3.0, 18.0), np.array([0.2060, 0.1020, 6.5550])), - (('10PB', 3.0, 20.0), np.array([0.2030, 0.0930, 6.5550])), - (('10PB', 3.0, 22.0), np.array([0.2004, 0.0847, 6.5550])), - (('10PB', 3.0, 24.0), np.array([0.1982, 0.0772, 6.5550])), - (('10PB', 3.0, 26.0), np.array([0.1963, 0.0708, 6.5550])), - (('10PB', 3.0, 28.0), np.array([0.1950, 0.0650, 6.5550])), - (('10PB', 3.0, 30.0), np.array([0.1938, 0.0599, 6.5550])), - (('10PB', 3.0, 32.0), np.array([0.1926, 0.0542, 6.5550])), - (('10PB', 3.0, 34.0), np.array([0.1918, 0.0503, 6.5550])), - (('2.5P', 3.0, 2.0), np.array([0.2922, 0.2680, 6.5550])), - (('2.5P', 3.0, 4.0), np.array([0.2792, 0.2342, 6.5550])), - (('2.5P', 3.0, 6.0), np.array([0.2691, 0.2072, 6.5550])), - (('2.5P', 3.0, 8.0), np.array([0.2615, 0.1845, 6.5550])), - (('2.5P', 3.0, 10.0), np.array([0.2548, 0.1638, 6.5550])), - (('2.5P', 3.0, 12.0), np.array([0.2498, 0.1480, 6.5550])), - (('2.5P', 3.0, 14.0), np.array([0.2449, 0.1325, 6.5550])), - (('2.5P', 3.0, 16.0), np.array([0.2410, 0.1198, 6.5550])), - (('2.5P', 3.0, 18.0), np.array([0.2380, 0.1094, 6.5550])), - (('2.5P', 3.0, 20.0), np.array([0.2354, 0.1003, 6.5550])), - (('2.5P', 3.0, 22.0), np.array([0.2329, 0.0911, 6.5550])), - (('2.5P', 3.0, 24.0), np.array([0.2305, 0.0832, 6.5550])), - (('2.5P', 3.0, 26.0), np.array([0.2286, 0.0765, 6.5550])), - (('2.5P', 3.0, 28.0), np.array([0.2268, 0.0698, 6.5550])), - (('2.5P', 3.0, 30.0), np.array([0.2252, 0.0638, 6.5550])), - (('2.5P', 3.0, 32.0), np.array([0.2242, 0.0587, 6.5550])), - (('2.5P', 3.0, 34.0), np.array([0.2230, 0.0543, 6.5550])), - (('5P', 3.0, 2.0), np.array([0.2997, 0.2700, 6.5550])), - (('5P', 3.0, 4.0), np.array([0.2928, 0.2386, 6.5550])), - (('5P', 3.0, 6.0), np.array([0.2870, 0.2135, 6.5550])), - (('5P', 3.0, 8.0), np.array([0.2819, 0.1910, 6.5550])), - (('5P', 3.0, 10.0), np.array([0.2772, 0.1707, 6.5550])), - (('5P', 3.0, 12.0), np.array([0.2739, 0.1539, 6.5550])), - (('5P', 3.0, 14.0), np.array([0.2707, 0.1397, 6.5550])), - (('5P', 3.0, 16.0), np.array([0.2680, 0.1272, 6.5550])), - (('5P', 3.0, 18.0), np.array([0.2657, 0.1163, 6.5550])), - (('5P', 3.0, 20.0), np.array([0.2639, 0.1074, 6.5550])), - (('5P', 3.0, 22.0), np.array([0.2620, 0.0978, 6.5550])), - (('5P', 3.0, 24.0), np.array([0.2602, 0.0891, 6.5550])), - (('5P', 3.0, 26.0), np.array([0.2590, 0.0822, 6.5550])), - (('5P', 3.0, 28.0), np.array([0.2579, 0.0750, 6.5550])), - (('5P', 3.0, 30.0), np.array([0.2568, 0.0690, 6.5550])), - (('5P', 3.0, 32.0), np.array([0.2557, 0.0630, 6.5550])), - (('7.5P', 3.0, 2.0), np.array([0.3088, 0.2740, 6.5550])), - (('7.5P', 3.0, 4.0), np.array([0.3072, 0.2448, 6.5550])), - (('7.5P', 3.0, 6.0), np.array([0.3057, 0.2208, 6.5550])), - (('7.5P', 3.0, 8.0), np.array([0.3037, 0.1981, 6.5550])), - (('7.5P', 3.0, 10.0), np.array([0.3020, 0.1794, 6.5550])), - (('7.5P', 3.0, 12.0), np.array([0.3003, 0.1618, 6.5550])), - (('7.5P', 3.0, 14.0), np.array([0.2992, 0.1475, 6.5550])), - (('7.5P', 3.0, 16.0), np.array([0.2981, 0.1356, 6.5550])), - (('7.5P', 3.0, 18.0), np.array([0.2969, 0.1239, 6.5550])), - (('7.5P', 3.0, 20.0), np.array([0.2961, 0.1151, 6.5550])), - (('7.5P', 3.0, 22.0), np.array([0.2953, 0.1057, 6.5550])), - (('7.5P', 3.0, 24.0), np.array([0.2944, 0.0967, 6.5550])), - (('7.5P', 3.0, 26.0), np.array([0.2938, 0.0892, 6.5550])), - (('7.5P', 3.0, 28.0), np.array([0.2930, 0.0812, 6.5550])), - (('7.5P', 3.0, 30.0), np.array([0.2922, 0.0750, 6.5550])), - (('10P', 3.0, 2.0), np.array([0.3170, 0.2790, 6.5550])), - (('10P', 3.0, 4.0), np.array([0.3214, 0.2517, 6.5550])), - (('10P', 3.0, 6.0), np.array([0.3243, 0.2293, 6.5550])), - (('10P', 3.0, 8.0), np.array([0.3269, 0.2075, 6.5550])), - (('10P', 3.0, 10.0), np.array([0.3286, 0.1889, 6.5550])), - (('10P', 3.0, 12.0), np.array([0.3301, 0.1715, 6.5550])), - (('10P', 3.0, 14.0), np.array([0.3309, 0.1572, 6.5550])), - (('10P', 3.0, 16.0), np.array([0.3320, 0.1456, 6.5550])), - (('10P', 3.0, 18.0), np.array([0.3329, 0.1332, 6.5550])), - (('10P', 3.0, 20.0), np.array([0.3332, 0.1240, 6.5550])), - (('10P', 3.0, 22.0), np.array([0.3340, 0.1146, 6.5550])), - (('10P', 3.0, 24.0), np.array([0.3341, 0.1055, 6.5550])), - (('10P', 3.0, 26.0), np.array([0.3343, 0.0978, 6.5550])), - (('2.5RP', 3.0, 2.0), np.array([0.3272, 0.2861, 6.5550])), - (('2.5RP', 3.0, 4.0), np.array([0.3400, 0.2624, 6.5550])), - (('2.5RP', 3.0, 6.0), np.array([0.3501, 0.2425, 6.5550])), - (('2.5RP', 3.0, 8.0), np.array([0.3598, 0.2233, 6.5550])), - (('2.5RP', 3.0, 10.0), np.array([0.3681, 0.2054, 6.5550])), - (('2.5RP', 3.0, 12.0), np.array([0.3754, 0.1898, 6.5550])), - (('2.5RP', 3.0, 14.0), np.array([0.3818, 0.1758, 6.5550])), - (('2.5RP', 3.0, 16.0), np.array([0.3876, 0.1629, 6.5550])), - (('2.5RP', 3.0, 18.0), np.array([0.3929, 0.1506, 6.5550])), - (('2.5RP', 3.0, 20.0), np.array([0.3969, 0.1413, 6.5550])), - (('2.5RP', 3.0, 22.0), np.array([0.4018, 0.1304, 6.5550])), - (('5RP', 3.0, 2.0), np.array([0.3370, 0.2940, 6.5550])), - (('5RP', 3.0, 4.0), np.array([0.3586, 0.2742, 6.5550])), - (('5RP', 3.0, 6.0), np.array([0.3765, 0.2569, 6.5550])), - (('5RP', 3.0, 8.0), np.array([0.3930, 0.2395, 6.5550])), - (('5RP', 3.0, 10.0), np.array([0.4073, 0.2235, 6.5550])), - (('5RP', 3.0, 12.0), np.array([0.4199, 0.2089, 6.5550])), - (('5RP', 3.0, 14.0), np.array([0.4313, 0.1944, 6.5550])), - (('5RP', 3.0, 16.0), np.array([0.4418, 0.1809, 6.5550])), - (('5RP', 3.0, 18.0), np.array([0.4503, 0.1695, 6.5550])), - (('5RP', 3.0, 20.0), np.array([0.4577, 0.1593, 6.5550])), - (('7.5RP', 3.0, 2.0), np.array([0.3450, 0.3001, 6.5550])), - (('7.5RP', 3.0, 4.0), np.array([0.3739, 0.2851, 6.5550])), - (('7.5RP', 3.0, 6.0), np.array([0.3990, 0.2708, 6.5550])), - (('7.5RP', 3.0, 8.0), np.array([0.4234, 0.2556, 6.5550])), - (('7.5RP', 3.0, 10.0), np.array([0.4445, 0.2419, 6.5550])), - (('7.5RP', 3.0, 12.0), np.array([0.4654, 0.2273, 6.5550])), - (('7.5RP', 3.0, 14.0), np.array([0.4831, 0.2140, 6.5550])), - (('7.5RP', 3.0, 16.0), np.array([0.4991, 0.2011, 6.5550])), - (('7.5RP', 3.0, 18.0), np.array([0.5130, 0.1893, 6.5550])), - (('10RP', 4.0, 2.0), np.array([0.3417, 0.3106, 12.0000])), - (('10RP', 4.0, 4.0), np.array([0.3715, 0.3042, 12.0000])), - (('10RP', 4.0, 6.0), np.array([0.3999, 0.2972, 12.0000])), - (('10RP', 4.0, 8.0), np.array([0.4282, 0.2890, 12.0000])), - (('10RP', 4.0, 10.0), np.array([0.4528, 0.2811, 12.0000])), - (('10RP', 4.0, 12.0), np.array([0.4789, 0.2717, 12.0000])), - (('10RP', 4.0, 14.0), np.array([0.5020, 0.2623, 12.0000])), - (('10RP', 4.0, 16.0), np.array([0.5234, 0.2530, 12.0000])), - (('10RP', 4.0, 18.0), np.array([0.5466, 0.2424, 12.0000])), - (('10RP', 4.0, 20.0), np.array([0.5674, 0.2319, 12.0000])), - (('2.5R', 4.0, 2.0), np.array([0.3461, 0.3150, 12.0000])), - (('2.5R', 4.0, 4.0), np.array([0.3806, 0.3125, 12.0000])), - (('2.5R', 4.0, 6.0), np.array([0.4141, 0.3085, 12.0000])), - (('2.5R', 4.0, 8.0), np.array([0.4472, 0.3031, 12.0000])), - (('2.5R', 4.0, 10.0), np.array([0.4774, 0.2969, 12.0000])), - (('2.5R', 4.0, 12.0), np.array([0.5072, 0.2897, 12.0000])), - (('2.5R', 4.0, 14.0), np.array([0.5369, 0.2810, 12.0000])), - (('2.5R', 4.0, 16.0), np.array([0.5620, 0.2724, 12.0000])), - (('2.5R', 4.0, 18.0), np.array([0.5898, 0.2622, 12.0000])), - (('5R', 4.0, 2.0), np.array([0.3508, 0.3200, 12.0000])), - (('5R', 4.0, 4.0), np.array([0.3916, 0.3223, 12.0000])), - (('5R', 4.0, 6.0), np.array([0.4299, 0.3226, 12.0000])), - (('5R', 4.0, 8.0), np.array([0.4690, 0.3209, 12.0000])), - (('5R', 4.0, 10.0), np.array([0.5043, 0.3176, 12.0000])), - (('5R', 4.0, 12.0), np.array([0.5385, 0.3129, 12.0000])), - (('5R', 4.0, 14.0), np.array([0.5734, 0.3057, 12.0000])), - (('5R', 4.0, 16.0), np.array([0.6039, 0.2978, 12.0000])), - (('5R', 4.0, 18.0), np.array([0.6329, 0.2881, 12.0000])), - (('7.5R', 4.0, 2.0), np.array([0.3538, 0.3236, 12.0000])), - (('7.5R', 4.0, 4.0), np.array([0.3990, 0.3300, 12.0000])), - (('7.5R', 4.0, 6.0), np.array([0.4415, 0.3340, 12.0000])), - (('7.5R', 4.0, 8.0), np.array([0.4850, 0.3359, 12.0000])), - (('7.5R', 4.0, 10.0), np.array([0.5235, 0.3351, 12.0000])), - (('7.5R', 4.0, 12.0), np.array([0.5603, 0.3321, 12.0000])), - (('7.5R', 4.0, 14.0), np.array([0.5959, 0.3269, 12.0000])), - (('7.5R', 4.0, 16.0), np.array([0.6260, 0.3192, 12.0000])), - (('7.5R', 4.0, 18.0), np.array([0.6538, 0.3100, 12.0000])), - (('7.5R', 4.0, 20.0), np.array([0.6806, 0.2988, 12.0000])), - (('10R', 4.0, 2.0), np.array([0.3582, 0.3294, 12.0000])), - (('10R', 4.0, 4.0), np.array([0.4078, 0.3412, 12.0000])), - (('10R', 4.0, 6.0), np.array([0.4535, 0.3500, 12.0000])), - (('10R', 4.0, 8.0), np.array([0.4995, 0.3557, 12.0000])), - (('10R', 4.0, 10.0), np.array([0.5418, 0.3580, 12.0000])), - (('10R', 4.0, 12.0), np.array([0.5801, 0.3588, 12.0000])), - (('10R', 4.0, 14.0), np.array([0.6154, 0.3568, 12.0000])), - (('10R', 4.0, 16.0), np.array([0.6409, 0.3533, 12.0000])), - (('2.5YR', 4.0, 2.0), np.array([0.3624, 0.3367, 12.0000])), - (('2.5YR', 4.0, 4.0), np.array([0.4141, 0.3539, 12.0000])), - (('2.5YR', 4.0, 6.0), np.array([0.4612, 0.3674, 12.0000])), - (('2.5YR', 4.0, 8.0), np.array([0.5071, 0.3777, 12.0000])), - (('2.5YR', 4.0, 10.0), np.array([0.5475, 0.3856, 12.0000])), - (('2.5YR', 4.0, 12.0), np.array([0.5809, 0.3910, 12.0000])), - (('5YR', 4.0, 2.0), np.array([0.3651, 0.3442, 12.0000])), - (('5YR', 4.0, 4.0), np.array([0.4187, 0.3679, 12.0000])), - (('5YR', 4.0, 6.0), np.array([0.4651, 0.3859, 12.0000])), - (('5YR', 4.0, 8.0), np.array([0.5070, 0.3994, 12.0000])), - (('5YR', 4.0, 10.0), np.array([0.5432, 0.4097, 12.0000])), - (('5YR', 4.0, 12.0), np.array([0.5729, 0.4169, 12.0000])), - (('7.5YR', 4.0, 2.0), np.array([0.3662, 0.3504, 12.0000])), - (('7.5YR', 4.0, 4.0), np.array([0.4208, 0.3809, 12.0000])), - (('7.5YR', 4.0, 6.0), np.array([0.4655, 0.4029, 12.0000])), - (('7.5YR', 4.0, 8.0), np.array([0.5038, 0.4204, 12.0000])), - (('7.5YR', 4.0, 10.0), np.array([0.5356, 0.4342, 12.0000])), - (('10YR', 4.0, 2.0), np.array([0.3660, 0.3590, 12.0000])), - (('10YR', 4.0, 4.0), np.array([0.4189, 0.3948, 12.0000])), - (('10YR', 4.0, 6.0), np.array([0.4618, 0.4213, 12.0000])), - (('10YR', 4.0, 8.0), np.array([0.4965, 0.4414, 12.0000])), - (('10YR', 4.0, 10.0), np.array([0.5250, 0.4573, 12.0000])), - (('2.5Y', 4.0, 2.0), np.array([0.3633, 0.3654, 12.0000])), - (('2.5Y', 4.0, 4.0), np.array([0.4138, 0.4076, 12.0000])), - (('2.5Y', 4.0, 6.0), np.array([0.4542, 0.4391, 12.0000])), - (('2.5Y', 4.0, 8.0), np.array([0.4865, 0.4625, 12.0000])), - (('2.5Y', 4.0, 10.0), np.array([0.5120, 0.4800, 12.0000])), - (('5Y', 4.0, 2.0), np.array([0.3590, 0.3701, 12.0000])), - (('5Y', 4.0, 4.0), np.array([0.4069, 0.4188, 12.0000])), - (('5Y', 4.0, 6.0), np.array([0.4451, 0.4550, 12.0000])), - (('5Y', 4.0, 8.0), np.array([0.4745, 0.4810, 12.0000])), - (('7.5Y', 4.0, 2.0), np.array([0.3542, 0.3727, 12.0000])), - (('7.5Y', 4.0, 4.0), np.array([0.3982, 0.4272, 12.0000])), - (('7.5Y', 4.0, 6.0), np.array([0.4331, 0.4688, 12.0000])), - (('7.5Y', 4.0, 8.0), np.array([0.4595, 0.4990, 12.0000])), - (('10Y', 4.0, 2.0), np.array([0.3436, 0.3732, 12.0000])), - (('10Y', 4.0, 4.0), np.array([0.3871, 0.4321, 12.0000])), - (('10Y', 4.0, 6.0), np.array([0.4190, 0.4795, 12.0000])), - (('10Y', 4.0, 8.0), np.array([0.4430, 0.5153, 12.0000])), - (('2.5GY', 4.0, 2.0), np.array([0.3382, 0.3706, 12.0000])), - (('2.5GY', 4.0, 4.0), np.array([0.3708, 0.4329, 12.0000])), - (('2.5GY', 4.0, 6.0), np.array([0.3968, 0.4857, 12.0000])), - (('2.5GY', 4.0, 8.0), np.array([0.4174, 0.5300, 12.0000])), - (('5GY', 4.0, 2.0), np.array([0.3312, 0.3678, 12.0000])), - (('5GY', 4.0, 4.0), np.array([0.3538, 0.4284, 12.0000])), - (('5GY', 4.0, 6.0), np.array([0.3718, 0.4852, 12.0000])), - (('5GY', 4.0, 8.0), np.array([0.3868, 0.5384, 12.0000])), - (('5GY', 4.0, 10.0), np.array([0.3983, 0.5850, 12.0000])), - (('7.5GY', 4.0, 2.0), np.array([0.3185, 0.3604, 12.0000])), - (('7.5GY', 4.0, 4.0), np.array([0.3281, 0.4157, 12.0000])), - (('7.5GY', 4.0, 6.0), np.array([0.3355, 0.4739, 12.0000])), - (('7.5GY', 4.0, 8.0), np.array([0.3400, 0.5348, 12.0000])), - (('7.5GY', 4.0, 10.0), np.array([0.3395, 0.5913, 12.0000])), - (('7.5GY', 4.0, 12.0), np.array([0.3348, 0.6468, 12.0000])), - (('10GY', 4.0, 2.0), np.array([0.3109, 0.3550, 12.0000])), - (('10GY', 4.0, 4.0), np.array([0.3100, 0.4018, 12.0000])), - (('10GY', 4.0, 6.0), np.array([0.3069, 0.4550, 12.0000])), - (('10GY', 4.0, 8.0), np.array([0.3008, 0.5095, 12.0000])), - (('10GY', 4.0, 10.0), np.array([0.2908, 0.5672, 12.0000])), - (('10GY', 4.0, 12.0), np.array([0.2758, 0.6282, 12.0000])), - (('10GY', 4.0, 14.0), np.array([0.2590, 0.6858, 12.0000])), - (('10GY', 4.0, 16.0), np.array([0.2422, 0.7360, 12.0000])), - (('2.5G', 4.0, 2.0), np.array([0.3012, 0.3470, 12.0000])), - (('2.5G', 4.0, 4.0), np.array([0.2891, 0.3821, 12.0000])), - (('2.5G', 4.0, 6.0), np.array([0.2735, 0.4215, 12.0000])), - (('2.5G', 4.0, 8.0), np.array([0.2561, 0.4597, 12.0000])), - (('2.5G', 4.0, 10.0), np.array([0.2355, 0.5006, 12.0000])), - (('2.5G', 4.0, 12.0), np.array([0.2128, 0.5425, 12.0000])), - (('2.5G', 4.0, 14.0), np.array([0.1909, 0.5779, 12.0000])), - (('2.5G', 4.0, 16.0), np.array([0.1682, 0.6111, 12.0000])), - (('2.5G', 4.0, 18.0), np.array([0.1446, 0.6431, 12.0000])), - (('2.5G', 4.0, 20.0), np.array([0.1230, 0.6706, 12.0000])), - (('2.5G', 4.0, 22.0), np.array([0.1009, 0.6975, 12.0000])), - (('2.5G', 4.0, 24.0), np.array([0.0760, 0.7250, 12.0000])), - (('2.5G', 4.0, 26.0), np.array([0.0528, 0.7502, 12.0000])), - (('5G', 4.0, 2.0), np.array([0.2959, 0.3417, 12.0000])), - (('5G', 4.0, 4.0), np.array([0.2781, 0.3704, 12.0000])), - (('5G', 4.0, 6.0), np.array([0.2581, 0.3992, 12.0000])), - (('5G', 4.0, 8.0), np.array([0.2359, 0.4266, 12.0000])), - (('5G', 4.0, 10.0), np.array([0.2115, 0.4532, 12.0000])), - (('5G', 4.0, 12.0), np.array([0.1843, 0.4807, 12.0000])), - (('5G', 4.0, 14.0), np.array([0.1627, 0.5015, 12.0000])), - (('5G', 4.0, 16.0), np.array([0.1402, 0.5214, 12.0000])), - (('5G', 4.0, 18.0), np.array([0.1188, 0.5400, 12.0000])), - (('5G', 4.0, 20.0), np.array([0.1018, 0.5543, 12.0000])), - (('5G', 4.0, 22.0), np.array([0.0841, 0.5684, 12.0000])), - (('5G', 4.0, 24.0), np.array([0.0614, 0.5857, 12.0000])), - (('5G', 4.0, 26.0), np.array([0.0407, 0.6010, 12.0000])), - (('7.5G', 4.0, 2.0), np.array([0.2919, 0.3371, 12.0000])), - (('7.5G', 4.0, 4.0), np.array([0.2702, 0.3602, 12.0000])), - (('7.5G', 4.0, 6.0), np.array([0.2467, 0.3822, 12.0000])), - (('7.5G', 4.0, 8.0), np.array([0.2232, 0.4022, 12.0000])), - (('7.5G', 4.0, 10.0), np.array([0.1989, 0.4219, 12.0000])), - (('7.5G', 4.0, 12.0), np.array([0.1706, 0.4419, 12.0000])), - (('7.5G', 4.0, 14.0), np.array([0.1500, 0.4562, 12.0000])), - (('7.5G', 4.0, 16.0), np.array([0.1293, 0.4703, 12.0000])), - (('7.5G', 4.0, 18.0), np.array([0.1086, 0.4842, 12.0000])), - (('7.5G', 4.0, 20.0), np.array([0.0928, 0.4942, 12.0000])), - (('7.5G', 4.0, 22.0), np.array([0.0770, 0.5040, 12.0000])), - (('7.5G', 4.0, 24.0), np.array([0.0581, 0.5151, 12.0000])), - (('7.5G', 4.0, 26.0), np.array([0.0392, 0.5258, 12.0000])), - (('10G', 4.0, 2.0), np.array([0.2880, 0.3327, 12.0000])), - (('10G', 4.0, 4.0), np.array([0.2628, 0.3498, 12.0000])), - (('10G', 4.0, 6.0), np.array([0.2374, 0.3655, 12.0000])), - (('10G', 4.0, 8.0), np.array([0.2124, 0.3799, 12.0000])), - (('10G', 4.0, 10.0), np.array([0.1876, 0.3933, 12.0000])), - (('10G', 4.0, 12.0), np.array([0.1602, 0.4070, 12.0000])), - (('10G', 4.0, 14.0), np.array([0.1398, 0.4168, 12.0000])), - (('10G', 4.0, 16.0), np.array([0.1212, 0.4245, 12.0000])), - (('10G', 4.0, 18.0), np.array([0.1006, 0.4330, 12.0000])), - (('10G', 4.0, 20.0), np.array([0.0850, 0.4388, 12.0000])), - (('10G', 4.0, 22.0), np.array([0.0702, 0.4440, 12.0000])), - (('10G', 4.0, 24.0), np.array([0.0553, 0.4492, 12.0000])), - (('10G', 4.0, 26.0), np.array([0.0400, 0.4545, 12.0000])), - (('2.5BG', 4.0, 2.0), np.array([0.2840, 0.3270, 12.0000])), - (('2.5BG', 4.0, 4.0), np.array([0.2552, 0.3375, 12.0000])), - (('2.5BG', 4.0, 6.0), np.array([0.2278, 0.3463, 12.0000])), - (('2.5BG', 4.0, 8.0), np.array([0.2006, 0.3540, 12.0000])), - (('2.5BG', 4.0, 10.0), np.array([0.1738, 0.3600, 12.0000])), - (('2.5BG', 4.0, 12.0), np.array([0.1492, 0.3649, 12.0000])), - (('2.5BG', 4.0, 14.0), np.array([0.1283, 0.3688, 12.0000])), - (('2.5BG', 4.0, 16.0), np.array([0.1102, 0.3720, 12.0000])), - (('2.5BG', 4.0, 18.0), np.array([0.0915, 0.3754, 12.0000])), - (('2.5BG', 4.0, 20.0), np.array([0.0768, 0.3773, 12.0000])), - (('2.5BG', 4.0, 22.0), np.array([0.0636, 0.3788, 12.0000])), - (('2.5BG', 4.0, 24.0), np.array([0.0510, 0.3800, 12.0000])), - (('5BG', 4.0, 2.0), np.array([0.2799, 0.3208, 12.0000])), - (('5BG', 4.0, 4.0), np.array([0.2480, 0.3232, 12.0000])), - (('5BG', 4.0, 6.0), np.array([0.2182, 0.3240, 12.0000])), - (('5BG', 4.0, 8.0), np.array([0.1890, 0.3234, 12.0000])), - (('5BG', 4.0, 10.0), np.array([0.1618, 0.3219, 12.0000])), - (('5BG', 4.0, 12.0), np.array([0.1379, 0.3198, 12.0000])), - (('5BG', 4.0, 14.0), np.array([0.1170, 0.3170, 12.0000])), - (('5BG', 4.0, 16.0), np.array([0.0992, 0.3141, 12.0000])), - (('5BG', 4.0, 18.0), np.array([0.0828, 0.3108, 12.0000])), - (('5BG', 4.0, 20.0), np.array([0.0675, 0.3075, 12.0000])), - (('7.5BG', 4.0, 2.0), np.array([0.2764, 0.3148, 12.0000])), - (('7.5BG', 4.0, 4.0), np.array([0.2429, 0.3108, 12.0000])), - (('7.5BG', 4.0, 6.0), np.array([0.2113, 0.3052, 12.0000])), - (('7.5BG', 4.0, 8.0), np.array([0.1815, 0.2985, 12.0000])), - (('7.5BG', 4.0, 10.0), np.array([0.1540, 0.2910, 12.0000])), - (('7.5BG', 4.0, 12.0), np.array([0.1298, 0.2840, 12.0000])), - (('7.5BG', 4.0, 14.0), np.array([0.1092, 0.2774, 12.0000])), - (('7.5BG', 4.0, 16.0), np.array([0.0922, 0.2718, 12.0000])), - (('7.5BG', 4.0, 18.0), np.array([0.0768, 0.2667, 12.0000])), - (('10BG', 4.0, 2.0), np.array([0.2740, 0.3091, 12.0000])), - (('10BG', 4.0, 4.0), np.array([0.2384, 0.2984, 12.0000])), - (('10BG', 4.0, 6.0), np.array([0.2065, 0.2863, 12.0000])), - (('10BG', 4.0, 8.0), np.array([0.1760, 0.2730, 12.0000])), - (('10BG', 4.0, 10.0), np.array([0.1480, 0.2600, 12.0000])), - (('10BG', 4.0, 12.0), np.array([0.1248, 0.2484, 12.0000])), - (('10BG', 4.0, 14.0), np.array([0.1033, 0.2376, 12.0000])), - (('10BG', 4.0, 16.0), np.array([0.0888, 0.2298, 12.0000])), - (('2.5B', 4.0, 2.0), np.array([0.2727, 0.3038, 12.0000])), - (('2.5B', 4.0, 4.0), np.array([0.2360, 0.2872, 12.0000])), - (('2.5B', 4.0, 6.0), np.array([0.2048, 0.2708, 12.0000])), - (('2.5B', 4.0, 8.0), np.array([0.1737, 0.2524, 12.0000])), - (('2.5B', 4.0, 10.0), np.array([0.1463, 0.2354, 12.0000])), - (('2.5B', 4.0, 12.0), np.array([0.1247, 0.2209, 12.0000])), - (('2.5B', 4.0, 14.0), np.array([0.1027, 0.2057, 12.0000])), - (('2.5B', 4.0, 16.0), np.array([0.0900, 0.1973, 12.0000])), - (('5B', 4.0, 2.0), np.array([0.2723, 0.2992, 12.0000])), - (('5B', 4.0, 4.0), np.array([0.2363, 0.2782, 12.0000])), - (('5B', 4.0, 6.0), np.array([0.2060, 0.2572, 12.0000])), - (('5B', 4.0, 8.0), np.array([0.1759, 0.2345, 12.0000])), - (('5B', 4.0, 10.0), np.array([0.1512, 0.2148, 12.0000])), - (('5B', 4.0, 12.0), np.array([0.1299, 0.1963, 12.0000])), - (('5B', 4.0, 14.0), np.array([0.1098, 0.1785, 12.0000])), - (('7.5B', 4.0, 2.0), np.array([0.2733, 0.2947, 12.0000])), - (('7.5B', 4.0, 4.0), np.array([0.2388, 0.2704, 12.0000])), - (('7.5B', 4.0, 6.0), np.array([0.2102, 0.2470, 12.0000])), - (('7.5B', 4.0, 8.0), np.array([0.1821, 0.2232, 12.0000])), - (('7.5B', 4.0, 10.0), np.array([0.1601, 0.2028, 12.0000])), - (('7.5B', 4.0, 12.0), np.array([0.1393, 0.1837, 12.0000])), - (('7.5B', 4.0, 14.0), np.array([0.1204, 0.1655, 12.0000])), - (('10B', 4.0, 2.0), np.array([0.2753, 0.2910, 12.0000])), - (('10B', 4.0, 4.0), np.array([0.2429, 0.2648, 12.0000])), - (('10B', 4.0, 6.0), np.array([0.2157, 0.2407, 12.0000])), - (('10B', 4.0, 8.0), np.array([0.1893, 0.2160, 12.0000])), - (('10B', 4.0, 10.0), np.array([0.1681, 0.1954, 12.0000])), - (('10B', 4.0, 12.0), np.array([0.1487, 0.1760, 12.0000])), - (('10B', 4.0, 14.0), np.array([0.1310, 0.1580, 12.0000])), - (('10B', 4.0, 16.0), np.array([0.1155, 0.1416, 12.0000])), - (('2.5PB', 4.0, 2.0), np.array([0.2782, 0.2876, 12.0000])), - (('2.5PB', 4.0, 4.0), np.array([0.2487, 0.2597, 12.0000])), - (('2.5PB', 4.0, 6.0), np.array([0.2235, 0.2343, 12.0000])), - (('2.5PB', 4.0, 8.0), np.array([0.1995, 0.2094, 12.0000])), - (('2.5PB', 4.0, 10.0), np.array([0.1805, 0.1888, 12.0000])), - (('2.5PB', 4.0, 12.0), np.array([0.1634, 0.1698, 12.0000])), - (('2.5PB', 4.0, 14.0), np.array([0.1473, 0.1513, 12.0000])), - (('2.5PB', 4.0, 16.0), np.array([0.1336, 0.1349, 12.0000])), - (('2.5PB', 4.0, 18.0), np.array([0.1218, 0.1208, 12.0000])), - (('5PB', 4.0, 2.0), np.array([0.2816, 0.2842, 12.0000])), - (('5PB', 4.0, 4.0), np.array([0.2562, 0.2560, 12.0000])), - (('5PB', 4.0, 6.0), np.array([0.2325, 0.2300, 12.0000])), - (('5PB', 4.0, 8.0), np.array([0.2103, 0.2050, 12.0000])), - (('5PB', 4.0, 10.0), np.array([0.1925, 0.1843, 12.0000])), - (('5PB', 4.0, 12.0), np.array([0.1773, 0.1659, 12.0000])), - (('5PB', 4.0, 14.0), np.array([0.1627, 0.1479, 12.0000])), - (('5PB', 4.0, 16.0), np.array([0.1504, 0.1317, 12.0000])), - (('5PB', 4.0, 18.0), np.array([0.1392, 0.1167, 12.0000])), - (('5PB', 4.0, 20.0), np.array([0.1288, 0.1027, 12.0000])), - (('7.5PB', 4.0, 2.0), np.array([0.2861, 0.2819, 12.0000])), - (('7.5PB', 4.0, 4.0), np.array([0.2657, 0.2528, 12.0000])), - (('7.5PB', 4.0, 6.0), np.array([0.2471, 0.2266, 12.0000])), - (('7.5PB', 4.0, 8.0), np.array([0.2304, 0.2023, 12.0000])), - (('7.5PB', 4.0, 10.0), np.array([0.2158, 0.1811, 12.0000])), - (('7.5PB', 4.0, 12.0), np.array([0.2037, 0.1629, 12.0000])), - (('7.5PB', 4.0, 14.0), np.array([0.1941, 0.1468, 12.0000])), - (('7.5PB', 4.0, 16.0), np.array([0.1861, 0.1316, 12.0000])), - (('7.5PB', 4.0, 18.0), np.array([0.1798, 0.1185, 12.0000])), - (('7.5PB', 4.0, 20.0), np.array([0.1742, 0.1058, 12.0000])), - (('7.5PB', 4.0, 22.0), np.array([0.1713, 0.0980, 12.0000])), - (('7.5PB', 4.0, 24.0), np.array([0.1684, 0.0899, 12.0000])), - (('7.5PB', 4.0, 26.0), np.array([0.1659, 0.0825, 12.0000])), - (('10PB', 4.0, 2.0), np.array([0.2911, 0.2804, 12.0000])), - (('10PB', 4.0, 4.0), np.array([0.2759, 0.2522, 12.0000])), - (('10PB', 4.0, 6.0), np.array([0.2618, 0.2263, 12.0000])), - (('10PB', 4.0, 8.0), np.array([0.2497, 0.2038, 12.0000])), - (('10PB', 4.0, 10.0), np.array([0.2388, 0.1837, 12.0000])), - (('10PB', 4.0, 12.0), np.array([0.2298, 0.1659, 12.0000])), - (('10PB', 4.0, 14.0), np.array([0.2220, 0.1503, 12.0000])), - (('10PB', 4.0, 16.0), np.array([0.2170, 0.1373, 12.0000])), - (('10PB', 4.0, 18.0), np.array([0.2120, 0.1256, 12.0000])), - (('10PB', 4.0, 20.0), np.array([0.2075, 0.1140, 12.0000])), - (('10PB', 4.0, 22.0), np.array([0.2048, 0.1064, 12.0000])), - (('10PB', 4.0, 24.0), np.array([0.2020, 0.0985, 12.0000])), - (('10PB', 4.0, 26.0), np.array([0.1994, 0.0904, 12.0000])), - (('10PB', 4.0, 28.0), np.array([0.1971, 0.0840, 12.0000])), - (('10PB', 4.0, 30.0), np.array([0.1952, 0.0778, 12.0000])), - (('2.5P', 4.0, 2.0), np.array([0.2962, 0.2807, 12.0000])), - (('2.5P', 4.0, 4.0), np.array([0.2855, 0.2531, 12.0000])), - (('2.5P', 4.0, 6.0), np.array([0.2763, 0.2300, 12.0000])), - (('2.5P', 4.0, 8.0), np.array([0.2685, 0.2089, 12.0000])), - (('2.5P', 4.0, 10.0), np.array([0.2619, 0.1903, 12.0000])), - (('2.5P', 4.0, 12.0), np.array([0.2559, 0.1730, 12.0000])), - (('2.5P', 4.0, 14.0), np.array([0.2509, 0.1585, 12.0000])), - (('2.5P', 4.0, 16.0), np.array([0.2467, 0.1452, 12.0000])), - (('2.5P', 4.0, 18.0), np.array([0.2430, 0.1332, 12.0000])), - (('2.5P', 4.0, 20.0), np.array([0.2394, 0.1221, 12.0000])), - (('2.5P', 4.0, 22.0), np.array([0.2371, 0.1143, 12.0000])), - (('2.5P', 4.0, 24.0), np.array([0.2348, 0.1062, 12.0000])), - (('2.5P', 4.0, 26.0), np.array([0.2322, 0.0978, 12.0000])), - (('2.5P', 4.0, 28.0), np.array([0.2302, 0.0909, 12.0000])), - (('2.5P', 4.0, 30.0), np.array([0.2285, 0.0847, 12.0000])), - (('2.5P', 4.0, 32.0), np.array([0.2265, 0.0774, 12.0000])), - (('5P', 4.0, 2.0), np.array([0.3022, 0.2825, 12.0000])), - (('5P', 4.0, 4.0), np.array([0.2958, 0.2565, 12.0000])), - (('5P', 4.0, 6.0), np.array([0.2903, 0.2347, 12.0000])), - (('5P', 4.0, 8.0), np.array([0.2855, 0.2150, 12.0000])), - (('5P', 4.0, 10.0), np.array([0.2814, 0.1967, 12.0000])), - (('5P', 4.0, 12.0), np.array([0.2778, 0.1808, 12.0000])), - (('5P', 4.0, 14.0), np.array([0.2747, 0.1660, 12.0000])), - (('5P', 4.0, 16.0), np.array([0.2718, 0.1520, 12.0000])), - (('5P', 4.0, 18.0), np.array([0.2693, 0.1408, 12.0000])), - (('5P', 4.0, 20.0), np.array([0.2670, 0.1300, 12.0000])), - (('5P', 4.0, 22.0), np.array([0.2652, 0.1218, 12.0000])), - (('5P', 4.0, 24.0), np.array([0.2635, 0.1132, 12.0000])), - (('5P', 4.0, 26.0), np.array([0.2618, 0.1052, 12.0000])), - (('5P', 4.0, 28.0), np.array([0.2600, 0.0971, 12.0000])), - (('5P', 4.0, 30.0), np.array([0.2588, 0.0907, 12.0000])), - (('5P', 4.0, 32.0), np.array([0.2574, 0.0833, 12.0000])), - (('7.5P', 4.0, 2.0), np.array([0.3093, 0.2859, 12.0000])), - (('7.5P', 4.0, 4.0), np.array([0.3084, 0.2622, 12.0000])), - (('7.5P', 4.0, 6.0), np.array([0.3076, 0.2416, 12.0000])), - (('7.5P', 4.0, 8.0), np.array([0.3066, 0.2228, 12.0000])), - (('7.5P', 4.0, 10.0), np.array([0.3056, 0.2060, 12.0000])), - (('7.5P', 4.0, 12.0), np.array([0.3045, 0.1905, 12.0000])), - (('7.5P', 4.0, 14.0), np.array([0.3035, 0.1755, 12.0000])), - (('7.5P', 4.0, 16.0), np.array([0.3028, 0.1621, 12.0000])), - (('7.5P', 4.0, 18.0), np.array([0.3016, 0.1500, 12.0000])), - (('7.5P', 4.0, 20.0), np.array([0.3010, 0.1396, 12.0000])), - (('7.5P', 4.0, 22.0), np.array([0.3001, 0.1306, 12.0000])), - (('7.5P', 4.0, 24.0), np.array([0.2993, 0.1225, 12.0000])), - (('7.5P', 4.0, 26.0), np.array([0.2986, 0.1135, 12.0000])), - (('7.5P', 4.0, 28.0), np.array([0.2979, 0.1062, 12.0000])), - (('7.5P', 4.0, 30.0), np.array([0.2969, 0.0979, 12.0000])), - (('7.5P', 4.0, 32.0), np.array([0.2962, 0.0906, 12.0000])), - (('10P', 4.0, 2.0), np.array([0.3162, 0.2902, 12.0000])), - (('10P', 4.0, 4.0), np.array([0.3210, 0.2686, 12.0000])), - (('10P', 4.0, 6.0), np.array([0.3248, 0.2493, 12.0000])), - (('10P', 4.0, 8.0), np.array([0.3280, 0.2318, 12.0000])), - (('10P', 4.0, 10.0), np.array([0.3306, 0.2162, 12.0000])), - (('10P', 4.0, 12.0), np.array([0.3331, 0.2014, 12.0000])), - (('10P', 4.0, 14.0), np.array([0.3351, 0.1875, 12.0000])), - (('10P', 4.0, 16.0), np.array([0.3370, 0.1756, 12.0000])), - (('10P', 4.0, 18.0), np.array([0.3386, 0.1626, 12.0000])), - (('10P', 4.0, 20.0), np.array([0.3400, 0.1500, 12.0000])), - (('10P', 4.0, 22.0), np.array([0.3411, 0.1424, 12.0000])), - (('10P', 4.0, 24.0), np.array([0.3421, 0.1337, 12.0000])), - (('10P', 4.0, 26.0), np.array([0.3428, 0.1248, 12.0000])), - (('10P', 4.0, 28.0), np.array([0.3432, 0.1172, 12.0000])), - (('10P', 4.0, 30.0), np.array([0.3440, 0.1080, 12.0000])), - (('2.5RP', 4.0, 2.0), np.array([0.3231, 0.2951, 12.0000])), - (('2.5RP', 4.0, 4.0), np.array([0.3340, 0.2770, 12.0000])), - (('2.5RP', 4.0, 6.0), np.array([0.3442, 0.2595, 12.0000])), - (('2.5RP', 4.0, 8.0), np.array([0.3533, 0.2438, 12.0000])), - (('2.5RP', 4.0, 10.0), np.array([0.3608, 0.2301, 12.0000])), - (('2.5RP', 4.0, 12.0), np.array([0.3683, 0.2162, 12.0000])), - (('2.5RP', 4.0, 14.0), np.array([0.3748, 0.2039, 12.0000])), - (('2.5RP', 4.0, 16.0), np.array([0.3807, 0.1923, 12.0000])), - (('2.5RP', 4.0, 18.0), np.array([0.3865, 0.1802, 12.0000])), - (('2.5RP', 4.0, 20.0), np.array([0.3926, 0.1679, 12.0000])), - (('2.5RP', 4.0, 22.0), np.array([0.3967, 0.1593, 12.0000])), - (('2.5RP', 4.0, 24.0), np.array([0.4011, 0.1504, 12.0000])), - (('2.5RP', 4.0, 26.0), np.array([0.4048, 0.1428, 12.0000])), - (('5RP', 4.0, 2.0), np.array([0.3310, 0.3010, 12.0000])), - (('5RP', 4.0, 4.0), np.array([0.3491, 0.2872, 12.0000])), - (('5RP', 4.0, 6.0), np.array([0.3671, 0.2733, 12.0000])), - (('5RP', 4.0, 8.0), np.array([0.3833, 0.2600, 12.0000])), - (('5RP', 4.0, 10.0), np.array([0.3960, 0.2489, 12.0000])), - (('5RP', 4.0, 12.0), np.array([0.4104, 0.2361, 12.0000])), - (('5RP', 4.0, 14.0), np.array([0.4225, 0.2249, 12.0000])), - (('5RP', 4.0, 16.0), np.array([0.4339, 0.2139, 12.0000])), - (('5RP', 4.0, 18.0), np.array([0.4455, 0.2023, 12.0000])), - (('5RP', 4.0, 20.0), np.array([0.4571, 0.1906, 12.0000])), - (('5RP', 4.0, 22.0), np.array([0.4656, 0.1821, 12.0000])), - (('7.5RP', 4.0, 2.0), np.array([0.3371, 0.3061, 12.0000])), - (('7.5RP', 4.0, 4.0), np.array([0.3612, 0.2963, 12.0000])), - (('7.5RP', 4.0, 6.0), np.array([0.3850, 0.2859, 12.0000])), - (('7.5RP', 4.0, 8.0), np.array([0.4072, 0.2750, 12.0000])), - (('7.5RP', 4.0, 10.0), np.array([0.4259, 0.2651, 12.0000])), - (('7.5RP', 4.0, 12.0), np.array([0.4450, 0.2541, 12.0000])), - (('7.5RP', 4.0, 14.0), np.array([0.4629, 0.2437, 12.0000])), - (('7.5RP', 4.0, 16.0), np.array([0.4799, 0.2329, 12.0000])), - (('7.5RP', 4.0, 18.0), np.array([0.4965, 0.2217, 12.0000])), - (('7.5RP', 4.0, 20.0), np.array([0.5130, 0.2101, 12.0000])), - (('10RP', 5.0, 2.0), np.array([0.3332, 0.3131, 19.7700])), - (('10RP', 5.0, 4.0), np.array([0.3594, 0.3090, 19.7700])), - (('10RP', 5.0, 6.0), np.array([0.3851, 0.3039, 19.7700])), - (('10RP', 5.0, 8.0), np.array([0.4105, 0.2980, 19.7700])), - (('10RP', 5.0, 10.0), np.array([0.4332, 0.2918, 19.7700])), - (('10RP', 5.0, 12.0), np.array([0.4579, 0.2841, 19.7700])), - (('10RP', 5.0, 14.0), np.array([0.4767, 0.2776, 19.7700])), - (('10RP', 5.0, 16.0), np.array([0.4986, 0.2695, 19.7700])), - (('10RP', 5.0, 18.0), np.array([0.5185, 0.2620, 19.7700])), - (('10RP', 5.0, 20.0), np.array([0.5396, 0.2535, 19.7700])), - (('2.5R', 5.0, 2.0), np.array([0.3360, 0.3158, 19.7700])), - (('2.5R', 5.0, 4.0), np.array([0.3660, 0.3148, 19.7700])), - (('2.5R', 5.0, 6.0), np.array([0.3960, 0.3130, 19.7700])), - (('2.5R', 5.0, 8.0), np.array([0.4252, 0.3101, 19.7700])), - (('2.5R', 5.0, 10.0), np.array([0.4533, 0.3058, 19.7700])), - (('2.5R', 5.0, 12.0), np.array([0.4820, 0.3002, 19.7700])), - (('2.5R', 5.0, 14.0), np.array([0.5047, 0.2950, 19.7700])), - (('2.5R', 5.0, 16.0), np.array([0.5300, 0.2880, 19.7700])), - (('2.5R', 5.0, 18.0), np.array([0.5540, 0.2804, 19.7700])), - (('2.5R', 5.0, 20.0), np.array([0.5784, 0.2719, 19.7700])), - (('5R', 5.0, 2.0), np.array([0.3392, 0.3192, 19.7700])), - (('5R', 5.0, 4.0), np.array([0.3740, 0.3220, 19.7700])), - (('5R', 5.0, 6.0), np.array([0.4078, 0.3238, 19.7700])), - (('5R', 5.0, 8.0), np.array([0.4413, 0.3240, 19.7700])), - (('5R', 5.0, 10.0), np.array([0.4747, 0.3227, 19.7700])), - (('5R', 5.0, 12.0), np.array([0.5071, 0.3194, 19.7700])), - (('5R', 5.0, 14.0), np.array([0.5341, 0.3158, 19.7700])), - (('5R', 5.0, 16.0), np.array([0.5637, 0.3102, 19.7700])), - (('5R', 5.0, 18.0), np.array([0.5918, 0.3038, 19.7700])), - (('5R', 5.0, 20.0), np.array([0.6142, 0.2970, 19.7700])), - (('7.5R', 5.0, 2.0), np.array([0.3425, 0.3229, 19.7700])), - (('7.5R', 5.0, 4.0), np.array([0.3806, 0.3294, 19.7700])), - (('7.5R', 5.0, 6.0), np.array([0.4180, 0.3348, 19.7700])), - (('7.5R', 5.0, 8.0), np.array([0.4563, 0.3387, 19.7700])), - (('7.5R', 5.0, 10.0), np.array([0.4927, 0.3399, 19.7700])), - (('7.5R', 5.0, 12.0), np.array([0.5280, 0.3389, 19.7700])), - (('7.5R', 5.0, 14.0), np.array([0.5590, 0.3370, 19.7700])), - (('7.5R', 5.0, 16.0), np.array([0.5901, 0.3331, 19.7700])), - (('7.5R', 5.0, 18.0), np.array([0.6161, 0.3277, 19.7700])), - (('7.5R', 5.0, 20.0), np.array([0.6388, 0.3216, 19.7700])), - (('10R', 5.0, 2.0), np.array([0.3465, 0.3278, 19.7700])), - (('10R', 5.0, 4.0), np.array([0.3879, 0.3398, 19.7700])), - (('10R', 5.0, 6.0), np.array([0.4299, 0.3499, 19.7700])), - (('10R', 5.0, 8.0), np.array([0.4713, 0.3575, 19.7700])), - (('10R', 5.0, 10.0), np.array([0.5113, 0.3630, 19.7700])), - (('10R', 5.0, 12.0), np.array([0.5481, 0.3660, 19.7700])), - (('10R', 5.0, 14.0), np.array([0.5771, 0.3664, 19.7700])), - (('10R', 5.0, 16.0), np.array([0.6037, 0.3657, 19.7700])), - (('10R', 5.0, 18.0), np.array([0.6297, 0.3642, 19.7700])), - (('2.5YR', 5.0, 2.0), np.array([0.3506, 0.3337, 19.7700])), - (('2.5YR', 5.0, 4.0), np.array([0.3925, 0.3494, 19.7700])), - (('2.5YR', 5.0, 6.0), np.array([0.4365, 0.3640, 19.7700])), - (('2.5YR', 5.0, 8.0), np.array([0.4795, 0.3758, 19.7700])), - (('2.5YR', 5.0, 10.0), np.array([0.5175, 0.3844, 19.7700])), - (('2.5YR', 5.0, 12.0), np.array([0.5482, 0.3909, 19.7700])), - (('2.5YR', 5.0, 14.0), np.array([0.5731, 0.3953, 19.7700])), - (('2.5YR', 5.0, 16.0), np.array([0.5933, 0.3989, 19.7700])), - (('5YR', 5.0, 2.0), np.array([0.3530, 0.3395, 19.7700])), - (('5YR', 5.0, 4.0), np.array([0.3968, 0.3614, 19.7700])), - (('5YR', 5.0, 6.0), np.array([0.4420, 0.3808, 19.7700])), - (('5YR', 5.0, 8.0), np.array([0.4830, 0.3960, 19.7700])), - (('5YR', 5.0, 10.0), np.array([0.5161, 0.4064, 19.7700])), - (('5YR', 5.0, 12.0), np.array([0.5422, 0.4141, 19.7700])), - (('5YR', 5.0, 14.0), np.array([0.5642, 0.4201, 19.7700])), - (('7.5YR', 5.0, 2.0), np.array([0.3540, 0.3445, 19.7700])), - (('7.5YR', 5.0, 4.0), np.array([0.3991, 0.3714, 19.7700])), - (('7.5YR', 5.0, 6.0), np.array([0.4440, 0.3954, 19.7700])), - (('7.5YR', 5.0, 8.0), np.array([0.4820, 0.4141, 19.7700])), - (('7.5YR', 5.0, 10.0), np.array([0.5108, 0.4276, 19.7700])), - (('7.5YR', 5.0, 12.0), np.array([0.5335, 0.4378, 19.7700])), - (('7.5YR', 5.0, 14.0), np.array([0.5506, 0.4450, 19.7700])), - (('10YR', 5.0, 2.0), np.array([0.3546, 0.3514, 19.7700])), - (('10YR', 5.0, 4.0), np.array([0.3995, 0.3840, 19.7700])), - (('10YR', 5.0, 6.0), np.array([0.4428, 0.4128, 19.7700])), - (('10YR', 5.0, 8.0), np.array([0.4770, 0.4338, 19.7700])), - (('10YR', 5.0, 10.0), np.array([0.5025, 0.4489, 19.7700])), - (('10YR', 5.0, 12.0), np.array([0.5211, 0.4600, 19.7700])), - (('2.5Y', 5.0, 2.0), np.array([0.3534, 0.3570, 19.7700])), - (('2.5Y', 5.0, 4.0), np.array([0.3968, 0.3954, 19.7700])), - (('2.5Y', 5.0, 6.0), np.array([0.4380, 0.4292, 19.7700])), - (('2.5Y', 5.0, 8.0), np.array([0.4685, 0.4524, 19.7700])), - (('2.5Y', 5.0, 10.0), np.array([0.4905, 0.4683, 19.7700])), - (('2.5Y', 5.0, 12.0), np.array([0.5082, 0.4812, 19.7700])), - (('5Y', 5.0, 2.0), np.array([0.3500, 0.3620, 19.7700])), - (('5Y', 5.0, 4.0), np.array([0.3915, 0.4057, 19.7700])), - (('5Y', 5.0, 6.0), np.array([0.4302, 0.4435, 19.7700])), - (('5Y', 5.0, 8.0), np.array([0.4579, 0.4692, 19.7700])), - (('5Y', 5.0, 10.0), np.array([0.4777, 0.4876, 19.7700])), - (('5Y', 5.0, 12.0), np.array([0.4932, 0.5019, 19.7700])), - (('7.5Y', 5.0, 2.0), np.array([0.3470, 0.3640, 19.7700])), - (('7.5Y', 5.0, 4.0), np.array([0.3850, 0.4120, 19.7700])), - (('7.5Y', 5.0, 6.0), np.array([0.4199, 0.4551, 19.7700])), - (('7.5Y', 5.0, 8.0), np.array([0.4450, 0.4850, 19.7700])), - (('7.5Y', 5.0, 10.0), np.array([0.4632, 0.5057, 19.7700])), - (('7.5Y', 5.0, 12.0), np.array([0.4767, 0.5208, 19.7700])), - (('10Y', 5.0, 2.0), np.array([0.3422, 0.3648, 19.7700])), - (('10Y', 5.0, 4.0), np.array([0.3762, 0.4158, 19.7700])), - (('10Y', 5.0, 6.0), np.array([0.4072, 0.4621, 19.7700])), - (('10Y', 5.0, 8.0), np.array([0.4307, 0.4967, 19.7700])), - (('10Y', 5.0, 10.0), np.array([0.4468, 0.5209, 19.7700])), - (('10Y', 5.0, 12.0), np.array([0.4590, 0.5390, 19.7700])), - (('2.5GY', 5.0, 2.0), np.array([0.3352, 0.3636, 19.7700])), - (('2.5GY', 5.0, 4.0), np.array([0.3621, 0.4143, 19.7700])), - (('2.5GY', 5.0, 6.0), np.array([0.3879, 0.4646, 19.7700])), - (('2.5GY', 5.0, 8.0), np.array([0.4088, 0.5068, 19.7700])), - (('2.5GY', 5.0, 10.0), np.array([0.4224, 0.5369, 19.7700])), - (('2.5GY', 5.0, 12.0), np.array([0.4333, 0.5602, 19.7700])), - (('5GY', 5.0, 2.0), np.array([0.3289, 0.3612, 19.7700])), - (('5GY', 5.0, 4.0), np.array([0.3482, 0.4097, 19.7700])), - (('5GY', 5.0, 6.0), np.array([0.3663, 0.4614, 19.7700])), - (('5GY', 5.0, 8.0), np.array([0.3815, 0.5093, 19.7700])), - (('5GY', 5.0, 10.0), np.array([0.3928, 0.5485, 19.7700])), - (('5GY', 5.0, 12.0), np.array([0.4011, 0.5802, 19.7700])), - (('7.5GY', 5.0, 2.0), np.array([0.3188, 0.3560, 19.7700])), - (('7.5GY', 5.0, 4.0), np.array([0.3274, 0.3994, 19.7700])), - (('7.5GY', 5.0, 6.0), np.array([0.3354, 0.4483, 19.7700])), - (('7.5GY', 5.0, 8.0), np.array([0.3412, 0.4976, 19.7700])), - (('7.5GY', 5.0, 10.0), np.array([0.3451, 0.5490, 19.7700])), - (('7.5GY', 5.0, 12.0), np.array([0.3450, 0.5949, 19.7700])), - (('7.5GY', 5.0, 14.0), np.array([0.3429, 0.6335, 19.7700])), - (('10GY', 5.0, 2.0), np.array([0.3110, 0.3508, 19.7700])), - (('10GY', 5.0, 4.0), np.array([0.3111, 0.3881, 19.7700])), - (('10GY', 5.0, 6.0), np.array([0.3108, 0.4301, 19.7700])), - (('10GY', 5.0, 8.0), np.array([0.3080, 0.4759, 19.7700])), - (('10GY', 5.0, 10.0), np.array([0.3028, 0.5237, 19.7700])), - (('10GY', 5.0, 12.0), np.array([0.2940, 0.5751, 19.7700])), - (('10GY', 5.0, 14.0), np.array([0.2838, 0.6208, 19.7700])), - (('10GY', 5.0, 16.0), np.array([0.2702, 0.6700, 19.7700])), - (('10GY', 5.0, 18.0), np.array([0.2549, 0.7179, 19.7700])), - (('2.5G', 5.0, 2.0), np.array([0.3030, 0.3445, 19.7700])), - (('2.5G', 5.0, 4.0), np.array([0.2943, 0.3735, 19.7700])), - (('2.5G', 5.0, 6.0), np.array([0.2841, 0.4045, 19.7700])), - (('2.5G', 5.0, 8.0), np.array([0.2710, 0.4380, 19.7700])), - (('2.5G', 5.0, 10.0), np.array([0.2565, 0.4705, 19.7700])), - (('2.5G', 5.0, 12.0), np.array([0.2385, 0.5071, 19.7700])), - (('2.5G', 5.0, 14.0), np.array([0.2211, 0.5411, 19.7700])), - (('2.5G', 5.0, 16.0), np.array([0.2005, 0.5759, 19.7700])), - (('2.5G', 5.0, 18.0), np.array([0.1782, 0.6095, 19.7700])), - (('2.5G', 5.0, 20.0), np.array([0.1579, 0.6392, 19.7700])), - (('2.5G', 5.0, 22.0), np.array([0.1377, 0.6674, 19.7700])), - (('2.5G', 5.0, 24.0), np.array([0.1188, 0.6918, 19.7700])), - (('2.5G', 5.0, 26.0), np.array([0.0992, 0.7155, 19.7700])), - (('2.5G', 5.0, 28.0), np.array([0.0794, 0.7385, 19.7700])), - (('5G', 5.0, 2.0), np.array([0.2978, 0.3392, 19.7700])), - (('5G', 5.0, 4.0), np.array([0.2841, 0.3628, 19.7700])), - (('5G', 5.0, 6.0), np.array([0.2690, 0.3860, 19.7700])), - (('5G', 5.0, 8.0), np.array([0.2511, 0.4107, 19.7700])), - (('5G', 5.0, 10.0), np.array([0.2329, 0.4331, 19.7700])), - (('5G', 5.0, 12.0), np.array([0.2104, 0.4578, 19.7700])), - (('5G', 5.0, 14.0), np.array([0.1912, 0.4773, 19.7700])), - (('5G', 5.0, 16.0), np.array([0.1695, 0.4981, 19.7700])), - (('5G', 5.0, 18.0), np.array([0.1489, 0.5171, 19.7700])), - (('5G', 5.0, 20.0), np.array([0.1318, 0.5321, 19.7700])), - (('5G', 5.0, 22.0), np.array([0.1144, 0.5463, 19.7700])), - (('5G', 5.0, 24.0), np.array([0.0953, 0.5628, 19.7700])), - (('5G', 5.0, 26.0), np.array([0.0784, 0.5761, 19.7700])), - (('5G', 5.0, 28.0), np.array([0.0609, 0.5898, 19.7700])), - (('7.5G', 5.0, 2.0), np.array([0.2945, 0.3355, 19.7700])), - (('7.5G', 5.0, 4.0), np.array([0.2775, 0.3545, 19.7700])), - (('7.5G', 5.0, 6.0), np.array([0.2598, 0.3724, 19.7700])), - (('7.5G', 5.0, 8.0), np.array([0.2395, 0.3915, 19.7700])), - (('7.5G', 5.0, 10.0), np.array([0.2200, 0.4082, 19.7700])), - (('7.5G', 5.0, 12.0), np.array([0.1964, 0.4271, 19.7700])), - (('7.5G', 5.0, 14.0), np.array([0.1776, 0.4415, 19.7700])), - (('7.5G', 5.0, 16.0), np.array([0.1571, 0.4561, 19.7700])), - (('7.5G', 5.0, 18.0), np.array([0.1372, 0.4705, 19.7700])), - (('7.5G', 5.0, 20.0), np.array([0.1212, 0.4817, 19.7700])), - (('7.5G', 5.0, 22.0), np.array([0.1050, 0.4927, 19.7700])), - (('7.5G', 5.0, 24.0), np.array([0.0878, 0.5039, 19.7700])), - (('7.5G', 5.0, 26.0), np.array([0.0730, 0.5131, 19.7700])), - (('7.5G', 5.0, 28.0), np.array([0.0585, 0.5224, 19.7700])), - (('10G', 5.0, 2.0), np.array([0.2910, 0.3310, 19.7700])), - (('10G', 5.0, 4.0), np.array([0.2711, 0.3455, 19.7700])), - (('10G', 5.0, 6.0), np.array([0.2519, 0.3587, 19.7700])), - (('10G', 5.0, 8.0), np.array([0.2297, 0.3730, 19.7700])), - (('10G', 5.0, 10.0), np.array([0.2095, 0.3853, 19.7700])), - (('10G', 5.0, 12.0), np.array([0.1852, 0.3992, 19.7700])), - (('10G', 5.0, 14.0), np.array([0.1671, 0.4089, 19.7700])), - (('10G', 5.0, 16.0), np.array([0.1469, 0.4192, 19.7700])), - (('10G', 5.0, 18.0), np.array([0.1275, 0.4288, 19.7700])), - (('10G', 5.0, 20.0), np.array([0.1120, 0.4360, 19.7700])), - (('10G', 5.0, 22.0), np.array([0.0958, 0.4428, 19.7700])), - (('10G', 5.0, 24.0), np.array([0.0811, 0.4491, 19.7700])), - (('10G', 5.0, 26.0), np.array([0.0690, 0.4542, 19.7700])), - (('10G', 5.0, 28.0), np.array([0.0572, 0.4590, 19.7700])), - (('2.5BG', 5.0, 2.0), np.array([0.2880, 0.3270, 19.7700])), - (('2.5BG', 5.0, 4.0), np.array([0.2659, 0.3369, 19.7700])), - (('2.5BG', 5.0, 6.0), np.array([0.2448, 0.3452, 19.7700])), - (('2.5BG', 5.0, 8.0), np.array([0.2205, 0.3537, 19.7700])), - (('2.5BG', 5.0, 10.0), np.array([0.1980, 0.3606, 19.7700])), - (('2.5BG', 5.0, 12.0), np.array([0.1735, 0.3668, 19.7700])), - (('2.5BG', 5.0, 14.0), np.array([0.1559, 0.3708, 19.7700])), - (('2.5BG', 5.0, 16.0), np.array([0.1348, 0.3750, 19.7700])), - (('2.5BG', 5.0, 18.0), np.array([0.1165, 0.3785, 19.7700])), - (('2.5BG', 5.0, 20.0), np.array([0.1005, 0.3814, 19.7700])), - (('2.5BG', 5.0, 22.0), np.array([0.0861, 0.3832, 19.7700])), - (('2.5BG', 5.0, 24.0), np.array([0.0738, 0.3851, 19.7700])), - (('5BG', 5.0, 2.0), np.array([0.2841, 0.3210, 19.7700])), - (('5BG', 5.0, 4.0), np.array([0.2591, 0.3246, 19.7700])), - (('5BG', 5.0, 6.0), np.array([0.2360, 0.3270, 19.7700])), - (('5BG', 5.0, 8.0), np.array([0.2100, 0.3280, 19.7700])), - (('5BG', 5.0, 10.0), np.array([0.1850, 0.3280, 19.7700])), - (('5BG', 5.0, 12.0), np.array([0.1614, 0.3280, 19.7700])), - (('5BG', 5.0, 14.0), np.array([0.1448, 0.3275, 19.7700])), - (('5BG', 5.0, 16.0), np.array([0.1243, 0.3261, 19.7700])), - (('5BG', 5.0, 18.0), np.array([0.1046, 0.3244, 19.7700])), - (('5BG', 5.0, 20.0), np.array([0.0904, 0.3231, 19.7700])), - (('5BG', 5.0, 22.0), np.array([0.0781, 0.3211, 19.7700])), - (('7.5BG', 5.0, 2.0), np.array([0.2812, 0.3161, 19.7700])), - (('7.5BG', 5.0, 4.0), np.array([0.2550, 0.3150, 19.7700])), - (('7.5BG', 5.0, 6.0), np.array([0.2292, 0.3125, 19.7700])), - (('7.5BG', 5.0, 8.0), np.array([0.2030, 0.3082, 19.7700])), - (('7.5BG', 5.0, 10.0), np.array([0.1776, 0.3032, 19.7700])), - (('7.5BG', 5.0, 12.0), np.array([0.1537, 0.2976, 19.7700])), - (('7.5BG', 5.0, 14.0), np.array([0.1364, 0.2932, 19.7700])), - (('7.5BG', 5.0, 16.0), np.array([0.1167, 0.2880, 19.7700])), - (('7.5BG', 5.0, 18.0), np.array([0.0982, 0.2828, 19.7700])), - (('10BG', 5.0, 2.0), np.array([0.2796, 0.3111, 19.7700])), - (('10BG', 5.0, 4.0), np.array([0.2512, 0.3040, 19.7700])), - (('10BG', 5.0, 6.0), np.array([0.2234, 0.2952, 19.7700])), - (('10BG', 5.0, 8.0), np.array([0.1970, 0.2860, 19.7700])), - (('10BG', 5.0, 10.0), np.array([0.1716, 0.2760, 19.7700])), - (('10BG', 5.0, 12.0), np.array([0.1485, 0.2662, 19.7700])), - (('10BG', 5.0, 14.0), np.array([0.1308, 0.2582, 19.7700])), - (('10BG', 5.0, 16.0), np.array([0.1108, 0.2489, 19.7700])), - (('2.5B', 5.0, 2.0), np.array([0.2791, 0.3071, 19.7700])), - (('2.5B', 5.0, 4.0), np.array([0.2492, 0.2954, 19.7700])), - (('2.5B', 5.0, 6.0), np.array([0.2210, 0.2823, 19.7700])), - (('2.5B', 5.0, 8.0), np.array([0.1947, 0.2687, 19.7700])), - (('2.5B', 5.0, 10.0), np.array([0.1697, 0.2549, 19.7700])), - (('2.5B', 5.0, 12.0), np.array([0.1461, 0.2406, 19.7700])), - (('2.5B', 5.0, 14.0), np.array([0.1283, 0.2292, 19.7700])), - (('2.5B', 5.0, 16.0), np.array([0.1090, 0.2166, 19.7700])), - (('5B', 5.0, 2.0), np.array([0.2794, 0.3032, 19.7700])), - (('5B', 5.0, 4.0), np.array([0.2493, 0.2879, 19.7700])), - (('5B', 5.0, 6.0), np.array([0.2215, 0.2701, 19.7700])), - (('5B', 5.0, 8.0), np.array([0.1958, 0.2519, 19.7700])), - (('5B', 5.0, 10.0), np.array([0.1729, 0.2347, 19.7700])), - (('5B', 5.0, 12.0), np.array([0.1505, 0.2172, 19.7700])), - (('5B', 5.0, 14.0), np.array([0.1320, 0.2021, 19.7700])), - (('5B', 5.0, 16.0), np.array([0.1132, 0.1863, 19.7700])), - (('7.5B', 5.0, 2.0), np.array([0.2803, 0.3000, 19.7700])), - (('7.5B', 5.0, 4.0), np.array([0.2511, 0.2808, 19.7700])), - (('7.5B', 5.0, 6.0), np.array([0.2248, 0.2612, 19.7700])), - (('7.5B', 5.0, 8.0), np.array([0.2007, 0.2417, 19.7700])), - (('7.5B', 5.0, 10.0), np.array([0.1792, 0.2230, 19.7700])), - (('7.5B', 5.0, 12.0), np.array([0.1584, 0.2042, 19.7700])), - (('7.5B', 5.0, 14.0), np.array([0.1404, 0.1878, 19.7700])), - (('7.5B', 5.0, 16.0), np.array([0.1230, 0.1711, 19.7700])), - (('10B', 5.0, 2.0), np.array([0.2821, 0.2966, 19.7700])), - (('10B', 5.0, 4.0), np.array([0.2547, 0.2757, 19.7700])), - (('10B', 5.0, 6.0), np.array([0.2299, 0.2548, 19.7700])), - (('10B', 5.0, 8.0), np.array([0.2067, 0.2344, 19.7700])), - (('10B', 5.0, 10.0), np.array([0.1860, 0.2149, 19.7700])), - (('10B', 5.0, 12.0), np.array([0.1666, 0.1964, 19.7700])), - (('10B', 5.0, 14.0), np.array([0.1492, 0.1797, 19.7700])), - (('10B', 5.0, 16.0), np.array([0.1326, 0.1632, 19.7700])), - (('10B', 5.0, 18.0), np.array([0.1203, 0.1505, 19.7700])), - (('2.5PB', 5.0, 2.0), np.array([0.2847, 0.2942, 19.7700])), - (('2.5PB', 5.0, 4.0), np.array([0.2600, 0.2720, 19.7700])), - (('2.5PB', 5.0, 6.0), np.array([0.2365, 0.2488, 19.7700])), - (('2.5PB', 5.0, 8.0), np.array([0.2157, 0.2278, 19.7700])), - (('2.5PB', 5.0, 10.0), np.array([0.1968, 0.2078, 19.7700])), - (('2.5PB', 5.0, 12.0), np.array([0.1793, 0.1894, 19.7700])), - (('2.5PB', 5.0, 14.0), np.array([0.1642, 0.1728, 19.7700])), - (('2.5PB', 5.0, 16.0), np.array([0.1495, 0.1559, 19.7700])), - (('2.5PB', 5.0, 18.0), np.array([0.1363, 0.1410, 19.7700])), - (('5PB', 5.0, 2.0), np.array([0.2882, 0.2923, 19.7700])), - (('5PB', 5.0, 4.0), np.array([0.2662, 0.2687, 19.7700])), - (('5PB', 5.0, 6.0), np.array([0.2447, 0.2449, 19.7700])), - (('5PB', 5.0, 8.0), np.array([0.2255, 0.2239, 19.7700])), - (('5PB', 5.0, 10.0), np.array([0.2080, 0.2041, 19.7700])), - (('5PB', 5.0, 12.0), np.array([0.1918, 0.1858, 19.7700])), - (('5PB', 5.0, 14.0), np.array([0.1773, 0.1689, 19.7700])), - (('5PB', 5.0, 16.0), np.array([0.1638, 0.1521, 19.7700])), - (('5PB', 5.0, 18.0), np.array([0.1518, 0.1365, 19.7700])), - (('7.5PB', 5.0, 2.0), np.array([0.2918, 0.2908, 19.7700])), - (('7.5PB', 5.0, 4.0), np.array([0.2739, 0.2666, 19.7700])), - (('7.5PB', 5.0, 6.0), np.array([0.2563, 0.2417, 19.7700])), - (('7.5PB', 5.0, 8.0), np.array([0.2417, 0.2204, 19.7700])), - (('7.5PB', 5.0, 10.0), np.array([0.2285, 0.2020, 19.7700])), - (('7.5PB', 5.0, 12.0), np.array([0.2157, 0.1830, 19.7700])), - (('7.5PB', 5.0, 14.0), np.array([0.2042, 0.1661, 19.7700])), - (('7.5PB', 5.0, 16.0), np.array([0.1945, 0.1511, 19.7700])), - (('7.5PB', 5.0, 18.0), np.array([0.1862, 0.1365, 19.7700])), - (('7.5PB', 5.0, 20.0), np.array([0.1794, 0.1239, 19.7700])), - (('10PB', 5.0, 2.0), np.array([0.2959, 0.2905, 19.7700])), - (('10PB', 5.0, 4.0), np.array([0.2821, 0.2659, 19.7700])), - (('10PB', 5.0, 6.0), np.array([0.2686, 0.2412, 19.7700])), - (('10PB', 5.0, 8.0), np.array([0.2572, 0.2211, 19.7700])), - (('10PB', 5.0, 10.0), np.array([0.2478, 0.2030, 19.7700])), - (('10PB', 5.0, 12.0), np.array([0.2384, 0.1857, 19.7700])), - (('10PB', 5.0, 14.0), np.array([0.2299, 0.1698, 19.7700])), - (('10PB', 5.0, 16.0), np.array([0.2224, 0.1555, 19.7700])), - (('10PB', 5.0, 18.0), np.array([0.2174, 0.1444, 19.7700])), - (('10PB', 5.0, 20.0), np.array([0.2121, 0.1329, 19.7700])), - (('10PB', 5.0, 22.0), np.array([0.2082, 0.1225, 19.7700])), - (('2.5P', 5.0, 2.0), np.array([0.3000, 0.2912, 19.7700])), - (('2.5P', 5.0, 4.0), np.array([0.2898, 0.2667, 19.7700])), - (('2.5P', 5.0, 6.0), np.array([0.2806, 0.2444, 19.7700])), - (('2.5P', 5.0, 8.0), np.array([0.2728, 0.2240, 19.7700])), - (('2.5P', 5.0, 10.0), np.array([0.2665, 0.2075, 19.7700])), - (('2.5P', 5.0, 12.0), np.array([0.2608, 0.1913, 19.7700])), - (('2.5P', 5.0, 14.0), np.array([0.2560, 0.1774, 19.7700])), - (('2.5P', 5.0, 16.0), np.array([0.2515, 0.1644, 19.7700])), - (('2.5P', 5.0, 18.0), np.array([0.2476, 0.1532, 19.7700])), - (('2.5P', 5.0, 20.0), np.array([0.2438, 0.1419, 19.7700])), - (('2.5P', 5.0, 22.0), np.array([0.2402, 0.1315, 19.7700])), - (('2.5P', 5.0, 24.0), np.array([0.2372, 0.1223, 19.7700])), - (('2.5P', 5.0, 26.0), np.array([0.2348, 0.1140, 19.7700])), - (('5P', 5.0, 2.0), np.array([0.3045, 0.2928, 19.7700])), - (('5P', 5.0, 4.0), np.array([0.2986, 0.2699, 19.7700])), - (('5P', 5.0, 6.0), np.array([0.2932, 0.2487, 19.7700])), - (('5P', 5.0, 8.0), np.array([0.2885, 0.2296, 19.7700])), - (('5P', 5.0, 10.0), np.array([0.2845, 0.2137, 19.7700])), - (('5P', 5.0, 12.0), np.array([0.2806, 0.1977, 19.7700])), - (('5P', 5.0, 14.0), np.array([0.2775, 0.1847, 19.7700])), - (('5P', 5.0, 16.0), np.array([0.2744, 0.1718, 19.7700])), - (('5P', 5.0, 18.0), np.array([0.2718, 0.1604, 19.7700])), - (('5P', 5.0, 20.0), np.array([0.2694, 0.1499, 19.7700])), - (('5P', 5.0, 22.0), np.array([0.2673, 0.1398, 19.7700])), - (('5P', 5.0, 24.0), np.array([0.2652, 0.1304, 19.7700])), - (('5P', 5.0, 26.0), np.array([0.2635, 0.1224, 19.7700])), - (('5P', 5.0, 28.0), np.array([0.2618, 0.1135, 19.7700])), - (('7.5P', 5.0, 2.0), np.array([0.3103, 0.2959, 19.7700])), - (('7.5P', 5.0, 4.0), np.array([0.3100, 0.2750, 19.7700])), - (('7.5P', 5.0, 6.0), np.array([0.3093, 0.2555, 19.7700])), - (('7.5P', 5.0, 8.0), np.array([0.3087, 0.2375, 19.7700])), - (('7.5P', 5.0, 10.0), np.array([0.3080, 0.2230, 19.7700])), - (('7.5P', 5.0, 12.0), np.array([0.3071, 0.2080, 19.7700])), - (('7.5P', 5.0, 14.0), np.array([0.3068, 0.1951, 19.7700])), - (('7.5P', 5.0, 16.0), np.array([0.3060, 0.1830, 19.7700])), - (('7.5P', 5.0, 18.0), np.array([0.3052, 0.1711, 19.7700])), - (('7.5P', 5.0, 20.0), np.array([0.3042, 0.1606, 19.7700])), - (('7.5P', 5.0, 22.0), np.array([0.3038, 0.1500, 19.7700])), - (('7.5P', 5.0, 24.0), np.array([0.3030, 0.1423, 19.7700])), - (('7.5P', 5.0, 26.0), np.array([0.3022, 0.1331, 19.7700])), - (('7.5P', 5.0, 28.0), np.array([0.3018, 0.1253, 19.7700])), - (('7.5P', 5.0, 30.0), np.array([0.3010, 0.1170, 19.7700])), - (('10P', 5.0, 2.0), np.array([0.3148, 0.2986, 19.7700])), - (('10P', 5.0, 4.0), np.array([0.3198, 0.2807, 19.7700])), - (('10P', 5.0, 6.0), np.array([0.3243, 0.2630, 19.7700])), - (('10P', 5.0, 8.0), np.array([0.3280, 0.2464, 19.7700])), - (('10P', 5.0, 10.0), np.array([0.3308, 0.2328, 19.7700])), - (('10P', 5.0, 12.0), np.array([0.3335, 0.2187, 19.7700])), - (('10P', 5.0, 14.0), np.array([0.3360, 0.2066, 19.7700])), - (('10P', 5.0, 16.0), np.array([0.3382, 0.1951, 19.7700])), - (('10P', 5.0, 18.0), np.array([0.3401, 0.1840, 19.7700])), - (('10P', 5.0, 20.0), np.array([0.3422, 0.1735, 19.7700])), - (('10P', 5.0, 22.0), np.array([0.3437, 0.1644, 19.7700])), - (('10P', 5.0, 24.0), np.array([0.3450, 0.1555, 19.7700])), - (('10P', 5.0, 26.0), np.array([0.3468, 0.1460, 19.7700])), - (('10P', 5.0, 28.0), np.array([0.3478, 0.1388, 19.7700])), - (('10P', 5.0, 30.0), np.array([0.3490, 0.1308, 19.7700])), - (('2.5RP', 5.0, 2.0), np.array([0.3199, 0.3019, 19.7700])), - (('2.5RP', 5.0, 4.0), np.array([0.3298, 0.2869, 19.7700])), - (('2.5RP', 5.0, 6.0), np.array([0.3396, 0.2718, 19.7700])), - (('2.5RP', 5.0, 8.0), np.array([0.3490, 0.2570, 19.7700])), - (('2.5RP', 5.0, 10.0), np.array([0.3560, 0.2452, 19.7700])), - (('2.5RP', 5.0, 12.0), np.array([0.3635, 0.2325, 19.7700])), - (('2.5RP', 5.0, 14.0), np.array([0.3703, 0.2211, 19.7700])), - (('2.5RP', 5.0, 16.0), np.array([0.3763, 0.2108, 19.7700])), - (('2.5RP', 5.0, 18.0), np.array([0.3821, 0.2007, 19.7700])), - (('2.5RP', 5.0, 20.0), np.array([0.3873, 0.1909, 19.7700])), - (('2.5RP', 5.0, 22.0), np.array([0.3924, 0.1814, 19.7700])), - (('2.5RP', 5.0, 24.0), np.array([0.3965, 0.1738, 19.7700])), - (('2.5RP', 5.0, 26.0), np.array([0.4011, 0.1652, 19.7700])), - (('5RP', 5.0, 2.0), np.array([0.3256, 0.3065, 19.7700])), - (('5RP', 5.0, 4.0), np.array([0.3421, 0.2954, 19.7700])), - (('5RP', 5.0, 6.0), np.array([0.3585, 0.2842, 19.7700])), - (('5RP', 5.0, 8.0), np.array([0.3748, 0.2729, 19.7700])), - (('5RP', 5.0, 10.0), np.array([0.3880, 0.2630, 19.7700])), - (('5RP', 5.0, 12.0), np.array([0.4022, 0.2523, 19.7700])), - (('5RP', 5.0, 14.0), np.array([0.4142, 0.2428, 19.7700])), - (('5RP', 5.0, 16.0), np.array([0.4261, 0.2331, 19.7700])), - (('5RP', 5.0, 18.0), np.array([0.4372, 0.2242, 19.7700])), - (('5RP', 5.0, 20.0), np.array([0.4484, 0.2150, 19.7700])), - (('5RP', 5.0, 22.0), np.array([0.4581, 0.2068, 19.7700])), - (('5RP', 5.0, 24.0), np.array([0.4683, 0.1978, 19.7700])), - (('7.5RP', 5.0, 2.0), np.array([0.3296, 0.3098, 19.7700])), - (('7.5RP', 5.0, 4.0), np.array([0.3515, 0.3024, 19.7700])), - (('7.5RP', 5.0, 6.0), np.array([0.3726, 0.2941, 19.7700])), - (('7.5RP', 5.0, 8.0), np.array([0.3932, 0.2852, 19.7700])), - (('7.5RP', 5.0, 10.0), np.array([0.4108, 0.2773, 19.7700])), - (('7.5RP', 5.0, 12.0), np.array([0.4303, 0.2675, 19.7700])), - (('7.5RP', 5.0, 14.0), np.array([0.4454, 0.2596, 19.7700])), - (('7.5RP', 5.0, 16.0), np.array([0.4617, 0.2506, 19.7700])), - (('7.5RP', 5.0, 18.0), np.array([0.4761, 0.2421, 19.7700])), - (('7.5RP', 5.0, 20.0), np.array([0.4915, 0.2330, 19.7700])), - (('7.5RP', 5.0, 22.0), np.array([0.5045, 0.2248, 19.7700])), - (('10RP', 6.0, 2.0), np.array([0.3292, 0.3141, 30.0500])), - (('10RP', 6.0, 4.0), np.array([0.3508, 0.3112, 30.0500])), - (('10RP', 6.0, 6.0), np.array([0.3740, 0.3074, 30.0500])), - (('10RP', 6.0, 8.0), np.array([0.3930, 0.3038, 30.0500])), - (('10RP', 6.0, 10.0), np.array([0.4150, 0.2989, 30.0500])), - (('10RP', 6.0, 12.0), np.array([0.4360, 0.2936, 30.0500])), - (('10RP', 6.0, 14.0), np.array([0.4552, 0.2881, 30.0500])), - (('10RP', 6.0, 16.0), np.array([0.4781, 0.2812, 30.0500])), - (('10RP', 6.0, 18.0), np.array([0.4961, 0.2751, 30.0500])), - (('2.5R', 6.0, 2.0), np.array([0.3318, 0.3166, 30.0500])), - (('2.5R', 6.0, 4.0), np.array([0.3566, 0.3163, 30.0500])), - (('2.5R', 6.0, 6.0), np.array([0.3832, 0.3158, 30.0500])), - (('2.5R', 6.0, 8.0), np.array([0.4065, 0.3144, 30.0500])), - (('2.5R', 6.0, 10.0), np.array([0.4320, 0.3118, 30.0500])), - (('2.5R', 6.0, 12.0), np.array([0.4568, 0.3082, 30.0500])), - (('2.5R', 6.0, 14.0), np.array([0.4790, 0.3041, 30.0500])), - (('2.5R', 6.0, 16.0), np.array([0.5041, 0.2983, 30.0500])), - (('2.5R', 6.0, 18.0), np.array([0.5262, 0.2928, 30.0500])), - (('5R', 6.0, 2.0), np.array([0.3343, 0.3190, 30.0500])), - (('5R', 6.0, 4.0), np.array([0.3628, 0.3221, 30.0500])), - (('5R', 6.0, 6.0), np.array([0.3921, 0.3244, 30.0500])), - (('5R', 6.0, 8.0), np.array([0.4187, 0.3251, 30.0500])), - (('5R', 6.0, 10.0), np.array([0.4480, 0.3250, 30.0500])), - (('5R', 6.0, 12.0), np.array([0.4760, 0.3234, 30.0500])), - (('5R', 6.0, 14.0), np.array([0.5020, 0.3212, 30.0500])), - (('5R', 6.0, 16.0), np.array([0.5297, 0.3179, 30.0500])), - (('5R', 6.0, 18.0), np.array([0.5552, 0.3138, 30.0500])), - (('7.5R', 6.0, 2.0), np.array([0.3381, 0.3228, 30.0500])), - (('7.5R', 6.0, 4.0), np.array([0.3692, 0.3291, 30.0500])), - (('7.5R', 6.0, 6.0), np.array([0.4000, 0.3340, 30.0500])), - (('7.5R', 6.0, 8.0), np.array([0.4318, 0.3383, 30.0500])), - (('7.5R', 6.0, 10.0), np.array([0.4655, 0.3412, 30.0500])), - (('7.5R', 6.0, 12.0), np.array([0.4961, 0.3428, 30.0500])), - (('7.5R', 6.0, 14.0), np.array([0.5265, 0.3431, 30.0500])), - (('7.5R', 6.0, 16.0), np.array([0.5560, 0.3420, 30.0500])), - (('7.5R', 6.0, 18.0), np.array([0.5829, 0.3396, 30.0500])), - (('10R', 6.0, 2.0), np.array([0.3417, 0.3268, 30.0500])), - (('10R', 6.0, 4.0), np.array([0.3768, 0.3381, 30.0500])), - (('10R', 6.0, 6.0), np.array([0.4103, 0.3473, 30.0500])), - (('10R', 6.0, 8.0), np.array([0.4449, 0.3550, 30.0500])), - (('10R', 6.0, 10.0), np.array([0.4812, 0.3619, 30.0500])), - (('10R', 6.0, 12.0), np.array([0.5150, 0.3667, 30.0500])), - (('10R', 6.0, 14.0), np.array([0.5468, 0.3697, 30.0500])), - (('10R', 6.0, 16.0), np.array([0.5741, 0.3713, 30.0500])), - (('10R', 6.0, 18.0), np.array([0.6009, 0.3720, 30.0500])), - (('2.5YR', 6.0, 2.0), np.array([0.3453, 0.3321, 30.0500])), - (('2.5YR', 6.0, 4.0), np.array([0.3806, 0.3467, 30.0500])), - (('2.5YR', 6.0, 6.0), np.array([0.4180, 0.3600, 30.0500])), - (('2.5YR', 6.0, 8.0), np.array([0.4533, 0.3708, 30.0500])), - (('2.5YR', 6.0, 10.0), np.array([0.4891, 0.3806, 30.0500])), - (('2.5YR', 6.0, 12.0), np.array([0.5215, 0.3887, 30.0500])), - (('2.5YR', 6.0, 14.0), np.array([0.5488, 0.3947, 30.0500])), - (('2.5YR', 6.0, 16.0), np.array([0.5698, 0.3990, 30.0500])), - (('2.5YR', 6.0, 18.0), np.array([0.5879, 0.4021, 30.0500])), - (('5YR', 6.0, 2.0), np.array([0.3474, 0.3373, 30.0500])), - (('5YR', 6.0, 4.0), np.array([0.3840, 0.3564, 30.0500])), - (('5YR', 6.0, 6.0), np.array([0.4229, 0.3750, 30.0500])), - (('5YR', 6.0, 8.0), np.array([0.4592, 0.3900, 30.0500])), - (('5YR', 6.0, 10.0), np.array([0.4921, 0.4022, 30.0500])), - (('5YR', 6.0, 12.0), np.array([0.5199, 0.4119, 30.0500])), - (('5YR', 6.0, 14.0), np.array([0.5423, 0.4188, 30.0500])), - (('5YR', 6.0, 16.0), np.array([0.5597, 0.4239, 30.0500])), - (('5YR', 6.0, 18.0), np.array([0.5715, 0.4270, 30.0500])), - (('7.5YR', 6.0, 2.0), np.array([0.3487, 0.3421, 30.0500])), - (('7.5YR', 6.0, 4.0), np.array([0.3860, 0.3652, 30.0500])), - (('7.5YR', 6.0, 6.0), np.array([0.4242, 0.3876, 30.0500])), - (('7.5YR', 6.0, 8.0), np.array([0.4596, 0.4064, 30.0500])), - (('7.5YR', 6.0, 10.0), np.array([0.4904, 0.4220, 30.0500])), - (('7.5YR', 6.0, 12.0), np.array([0.5145, 0.4331, 30.0500])), - (('7.5YR', 6.0, 14.0), np.array([0.5320, 0.4412, 30.0500])), - (('7.5YR', 6.0, 16.0), np.array([0.5468, 0.4478, 30.0500])), - (('10YR', 6.0, 2.0), np.array([0.3491, 0.3483, 30.0500])), - (('10YR', 6.0, 4.0), np.array([0.3861, 0.3767, 30.0500])), - (('10YR', 6.0, 6.0), np.array([0.4240, 0.4030, 30.0500])), - (('10YR', 6.0, 8.0), np.array([0.4570, 0.4249, 30.0500])), - (('10YR', 6.0, 10.0), np.array([0.4843, 0.4416, 30.0500])), - (('10YR', 6.0, 12.0), np.array([0.5050, 0.4536, 30.0500])), - (('10YR', 6.0, 14.0), np.array([0.5200, 0.4623, 30.0500])), - (('2.5Y', 6.0, 2.0), np.array([0.3480, 0.3540, 30.0500])), - (('2.5Y', 6.0, 4.0), np.array([0.3840, 0.3867, 30.0500])), - (('2.5Y', 6.0, 6.0), np.array([0.4203, 0.4176, 30.0500])), - (('2.5Y', 6.0, 8.0), np.array([0.4517, 0.4421, 30.0500])), - (('2.5Y', 6.0, 10.0), np.array([0.4760, 0.4607, 30.0500])), - (('2.5Y', 6.0, 12.0), np.array([0.4928, 0.4730, 30.0500])), - (('2.5Y', 6.0, 14.0), np.array([0.5061, 0.4829, 30.0500])), - (('5Y', 6.0, 2.0), np.array([0.3457, 0.3580, 30.0500])), - (('5Y', 6.0, 4.0), np.array([0.3794, 0.3955, 30.0500])), - (('5Y', 6.0, 6.0), np.array([0.4140, 0.4305, 30.0500])), - (('5Y', 6.0, 8.0), np.array([0.4426, 0.4588, 30.0500])), - (('5Y', 6.0, 10.0), np.array([0.4639, 0.4790, 30.0500])), - (('5Y', 6.0, 12.0), np.array([0.4780, 0.4920, 30.0500])), - (('5Y', 6.0, 14.0), np.array([0.4905, 0.5038, 30.0500])), - (('7.5Y', 6.0, 2.0), np.array([0.3431, 0.3601, 30.0500])), - (('7.5Y', 6.0, 4.0), np.array([0.3745, 0.4004, 30.0500])), - (('7.5Y', 6.0, 6.0), np.array([0.4060, 0.4400, 30.0500])), - (('7.5Y', 6.0, 8.0), np.array([0.4321, 0.4719, 30.0500])), - (('7.5Y', 6.0, 10.0), np.array([0.4512, 0.4943, 30.0500])), - (('7.5Y', 6.0, 12.0), np.array([0.4638, 0.5087, 30.0500])), - (('7.5Y', 6.0, 14.0), np.array([0.4754, 0.5220, 30.0500])), - (('10Y', 6.0, 2.0), np.array([0.3398, 0.3611, 30.0500])), - (('10Y', 6.0, 4.0), np.array([0.3679, 0.4033, 30.0500])), - (('10Y', 6.0, 6.0), np.array([0.3960, 0.4452, 30.0500])), - (('10Y', 6.0, 8.0), np.array([0.4201, 0.4812, 30.0500])), - (('10Y', 6.0, 10.0), np.array([0.4372, 0.5068, 30.0500])), - (('10Y', 6.0, 12.0), np.array([0.4488, 0.5237, 30.0500])), - (('10Y', 6.0, 14.0), np.array([0.4593, 0.5392, 30.0500])), - (('2.5GY', 6.0, 2.0), np.array([0.3342, 0.3607, 30.0500])), - (('2.5GY', 6.0, 4.0), np.array([0.3572, 0.4038, 30.0500])), - (('2.5GY', 6.0, 6.0), np.array([0.3799, 0.4470, 30.0500])), - (('2.5GY', 6.0, 8.0), np.array([0.4006, 0.4885, 30.0500])), - (('2.5GY', 6.0, 10.0), np.array([0.4159, 0.5190, 30.0500])), - (('2.5GY', 6.0, 12.0), np.array([0.4269, 0.5414, 30.0500])), - (('2.5GY', 6.0, 14.0), np.array([0.4354, 0.5594, 30.0500])), - (('5GY', 6.0, 2.0), np.array([0.3288, 0.3592, 30.0500])), - (('5GY', 6.0, 4.0), np.array([0.3461, 0.4008, 30.0500])), - (('5GY', 6.0, 6.0), np.array([0.3622, 0.4438, 30.0500])), - (('5GY', 6.0, 8.0), np.array([0.3772, 0.4880, 30.0500])), - (('5GY', 6.0, 10.0), np.array([0.3891, 0.5264, 30.0500])), - (('5GY', 6.0, 12.0), np.array([0.3980, 0.5564, 30.0500])), - (('5GY', 6.0, 14.0), np.array([0.4042, 0.5788, 30.0500])), - (('7.5GY', 6.0, 2.0), np.array([0.3193, 0.3550, 30.0500])), - (('7.5GY', 6.0, 4.0), np.array([0.3275, 0.3922, 30.0500])), - (('7.5GY', 6.0, 6.0), np.array([0.3351, 0.4321, 30.0500])), - (('7.5GY', 6.0, 8.0), np.array([0.3418, 0.4768, 30.0500])), - (('7.5GY', 6.0, 10.0), np.array([0.3463, 0.5196, 30.0500])), - (('7.5GY', 6.0, 12.0), np.array([0.3488, 0.5596, 30.0500])), - (('7.5GY', 6.0, 14.0), np.array([0.3498, 0.5985, 30.0500])), - (('7.5GY', 6.0, 16.0), np.array([0.3498, 0.6282, 30.0500])), - (('10GY', 6.0, 2.0), np.array([0.3112, 0.3496, 30.0500])), - (('10GY', 6.0, 4.0), np.array([0.3124, 0.3822, 30.0500])), - (('10GY', 6.0, 6.0), np.array([0.3128, 0.4175, 30.0500])), - (('10GY', 6.0, 8.0), np.array([0.3116, 0.4563, 30.0500])), - (('10GY', 6.0, 10.0), np.array([0.3086, 0.4949, 30.0500])), - (('10GY', 6.0, 12.0), np.array([0.3037, 0.5358, 30.0500])), - (('10GY', 6.0, 14.0), np.array([0.2962, 0.5802, 30.0500])), - (('10GY', 6.0, 16.0), np.array([0.2872, 0.6199, 30.0500])), - (('10GY', 6.0, 18.0), np.array([0.2763, 0.6616, 30.0500])), - (('10GY', 6.0, 20.0), np.array([0.2648, 0.7004, 30.0500])), - (('2.5G', 6.0, 2.0), np.array([0.3039, 0.3437, 30.0500])), - (('2.5G', 6.0, 4.0), np.array([0.2967, 0.3695, 30.0500])), - (('2.5G', 6.0, 6.0), np.array([0.2892, 0.3963, 30.0500])), - (('2.5G', 6.0, 8.0), np.array([0.2799, 0.4239, 30.0500])), - (('2.5G', 6.0, 10.0), np.array([0.2690, 0.4530, 30.0500])), - (('2.5G', 6.0, 12.0), np.array([0.2574, 0.4814, 30.0500])), - (('2.5G', 6.0, 14.0), np.array([0.2426, 0.5133, 30.0500])), - (('2.5G', 6.0, 16.0), np.array([0.2278, 0.5430, 30.0500])), - (('2.5G', 6.0, 18.0), np.array([0.2102, 0.5737, 30.0500])), - (('2.5G', 6.0, 20.0), np.array([0.1922, 0.6035, 30.0500])), - (('2.5G', 6.0, 22.0), np.array([0.1739, 0.6318, 30.0500])), - (('2.5G', 6.0, 24.0), np.array([0.1536, 0.6605, 30.0500])), - (('2.5G', 6.0, 26.0), np.array([0.1340, 0.6871, 30.0500])), - (('2.5G', 6.0, 28.0), np.array([0.1145, 0.7122, 30.0500])), - (('5G', 6.0, 2.0), np.array([0.2988, 0.3382, 30.0500])), - (('5G', 6.0, 4.0), np.array([0.2868, 0.3595, 30.0500])), - (('5G', 6.0, 6.0), np.array([0.2748, 0.3795, 30.0500])), - (('5G', 6.0, 8.0), np.array([0.2612, 0.3990, 30.0500])), - (('5G', 6.0, 10.0), np.array([0.2466, 0.4181, 30.0500])), - (('5G', 6.0, 12.0), np.array([0.2293, 0.4390, 30.0500])), - (('5G', 6.0, 14.0), np.array([0.2130, 0.4571, 30.0500])), - (('5G', 6.0, 16.0), np.array([0.1960, 0.4751, 30.0500])), - (('5G', 6.0, 18.0), np.array([0.1785, 0.4924, 30.0500])), - (('5G', 6.0, 20.0), np.array([0.1609, 0.5091, 30.0500])), - (('5G', 6.0, 22.0), np.array([0.1432, 0.5252, 30.0500])), - (('5G', 6.0, 24.0), np.array([0.1252, 0.5408, 30.0500])), - (('5G', 6.0, 26.0), np.array([0.1079, 0.5560, 30.0500])), - (('5G', 6.0, 28.0), np.array([0.0908, 0.5695, 30.0500])), - (('7.5G', 6.0, 2.0), np.array([0.2958, 0.3344, 30.0500])), - (('7.5G', 6.0, 4.0), np.array([0.2807, 0.3522, 30.0500])), - (('7.5G', 6.0, 6.0), np.array([0.2662, 0.3672, 30.0500])), - (('7.5G', 6.0, 8.0), np.array([0.2510, 0.3829, 30.0500])), - (('7.5G', 6.0, 10.0), np.array([0.2350, 0.3979, 30.0500])), - (('7.5G', 6.0, 12.0), np.array([0.2171, 0.4138, 30.0500])), - (('7.5G', 6.0, 14.0), np.array([0.2001, 0.4278, 30.0500])), - (('7.5G', 6.0, 16.0), np.array([0.1832, 0.4414, 30.0500])), - (('7.5G', 6.0, 18.0), np.array([0.1654, 0.4551, 30.0500])), - (('7.5G', 6.0, 20.0), np.array([0.1485, 0.4677, 30.0500])), - (('7.5G', 6.0, 22.0), np.array([0.1325, 0.4795, 30.0500])), - (('7.5G', 6.0, 24.0), np.array([0.1159, 0.4910, 30.0500])), - (('7.5G', 6.0, 26.0), np.array([0.1010, 0.5018, 30.0500])), - (('7.5G', 6.0, 28.0), np.array([0.0858, 0.5127, 30.0500])), - (('10G', 6.0, 2.0), np.array([0.2929, 0.3303, 30.0500])), - (('10G', 6.0, 4.0), np.array([0.2749, 0.3443, 30.0500])), - (('10G', 6.0, 6.0), np.array([0.2591, 0.3558, 30.0500])), - (('10G', 6.0, 8.0), np.array([0.2420, 0.3679, 30.0500])), - (('10G', 6.0, 10.0), np.array([0.2247, 0.3796, 30.0500])), - (('10G', 6.0, 12.0), np.array([0.2060, 0.3914, 30.0500])), - (('10G', 6.0, 14.0), np.array([0.1895, 0.4015, 30.0500])), - (('10G', 6.0, 16.0), np.array([0.1722, 0.4113, 30.0500])), - (('10G', 6.0, 18.0), np.array([0.1551, 0.4208, 30.0500])), - (('10G', 6.0, 20.0), np.array([0.1382, 0.4299, 30.0500])), - (('10G', 6.0, 22.0), np.array([0.1230, 0.4378, 30.0500])), - (('10G', 6.0, 24.0), np.array([0.1070, 0.4458, 30.0500])), - (('10G', 6.0, 26.0), np.array([0.0941, 0.4520, 30.0500])), - (('2.5BG', 6.0, 2.0), np.array([0.2902, 0.3268, 30.0500])), - (('2.5BG', 6.0, 4.0), np.array([0.2702, 0.3369, 30.0500])), - (('2.5BG', 6.0, 6.0), np.array([0.2526, 0.3448, 30.0500])), - (('2.5BG', 6.0, 8.0), np.array([0.2332, 0.3522, 30.0500])), - (('2.5BG', 6.0, 10.0), np.array([0.2148, 0.3584, 30.0500])), - (('2.5BG', 6.0, 12.0), np.array([0.1954, 0.3645, 30.0500])), - (('2.5BG', 6.0, 14.0), np.array([0.1779, 0.3699, 30.0500])), - (('2.5BG', 6.0, 16.0), np.array([0.1600, 0.3748, 30.0500])), - (('2.5BG', 6.0, 18.0), np.array([0.1428, 0.3790, 30.0500])), - (('2.5BG', 6.0, 20.0), np.array([0.1269, 0.3829, 30.0500])), - (('2.5BG', 6.0, 22.0), np.array([0.1120, 0.3860, 30.0500])), - (('5BG', 6.0, 2.0), np.array([0.2872, 0.3219, 30.0500])), - (('5BG', 6.0, 4.0), np.array([0.2648, 0.3262, 30.0500])), - (('5BG', 6.0, 6.0), np.array([0.2441, 0.3290, 30.0500])), - (('5BG', 6.0, 8.0), np.array([0.2236, 0.3311, 30.0500])), - (('5BG', 6.0, 10.0), np.array([0.2037, 0.3329, 30.0500])), - (('5BG', 6.0, 12.0), np.array([0.1844, 0.3337, 30.0500])), - (('5BG', 6.0, 14.0), np.array([0.1662, 0.3343, 30.0500])), - (('5BG', 6.0, 16.0), np.array([0.1491, 0.3345, 30.0500])), - (('5BG', 6.0, 18.0), np.array([0.1325, 0.3345, 30.0500])), - (('5BG', 6.0, 20.0), np.array([0.1168, 0.3344, 30.0500])), - (('7.5BG', 6.0, 2.0), np.array([0.2849, 0.3172, 30.0500])), - (('7.5BG', 6.0, 4.0), np.array([0.2604, 0.3169, 30.0500])), - (('7.5BG', 6.0, 6.0), np.array([0.2384, 0.3155, 30.0500])), - (('7.5BG', 6.0, 8.0), np.array([0.2171, 0.3138, 30.0500])), - (('7.5BG', 6.0, 10.0), np.array([0.1961, 0.3110, 30.0500])), - (('7.5BG', 6.0, 12.0), np.array([0.1762, 0.3081, 30.0500])), - (('7.5BG', 6.0, 14.0), np.array([0.1585, 0.3052, 30.0500])), - (('7.5BG', 6.0, 16.0), np.array([0.1408, 0.3017, 30.0500])), - (('7.5BG', 6.0, 18.0), np.array([0.1248, 0.2981, 30.0500])), - (('10BG', 6.0, 2.0), np.array([0.2837, 0.3132, 30.0500])), - (('10BG', 6.0, 4.0), np.array([0.2578, 0.3078, 30.0500])), - (('10BG', 6.0, 6.0), np.array([0.2335, 0.3015, 30.0500])), - (('10BG', 6.0, 8.0), np.array([0.2116, 0.2950, 30.0500])), - (('10BG', 6.0, 10.0), np.array([0.1909, 0.2881, 30.0500])), - (('10BG', 6.0, 12.0), np.array([0.1698, 0.2802, 30.0500])), - (('10BG', 6.0, 14.0), np.array([0.1518, 0.2729, 30.0500])), - (('10BG', 6.0, 16.0), np.array([0.1337, 0.2651, 30.0500])), - (('10BG', 6.0, 18.0), np.array([0.1181, 0.2581, 30.0500])), - (('2.5B', 6.0, 2.0), np.array([0.2835, 0.3097, 30.0500])), - (('2.5B', 6.0, 4.0), np.array([0.2571, 0.3008, 30.0500])), - (('2.5B', 6.0, 6.0), np.array([0.2312, 0.2899, 30.0500])), - (('2.5B', 6.0, 8.0), np.array([0.2080, 0.2789, 30.0500])), - (('2.5B', 6.0, 10.0), np.array([0.1879, 0.2682, 30.0500])), - (('2.5B', 6.0, 12.0), np.array([0.1660, 0.2561, 30.0500])), - (('2.5B', 6.0, 14.0), np.array([0.1480, 0.2459, 30.0500])), - (('2.5B', 6.0, 16.0), np.array([0.1294, 0.2348, 30.0500])), - (('5B', 6.0, 2.0), np.array([0.2842, 0.3063, 30.0500])), - (('5B', 6.0, 4.0), np.array([0.2579, 0.2938, 30.0500])), - (('5B', 6.0, 6.0), np.array([0.2320, 0.2789, 30.0500])), - (('5B', 6.0, 8.0), np.array([0.2088, 0.2635, 30.0500])), - (('5B', 6.0, 10.0), np.array([0.1883, 0.2487, 30.0500])), - (('5B', 6.0, 12.0), np.array([0.1685, 0.2339, 30.0500])), - (('5B', 6.0, 14.0), np.array([0.1496, 0.2193, 30.0500])), - (('5B', 6.0, 16.0), np.array([0.1310, 0.2048, 30.0500])), - (('7.5B', 6.0, 2.0), np.array([0.2854, 0.3037, 30.0500])), - (('7.5B', 6.0, 4.0), np.array([0.2602, 0.2881, 30.0500])), - (('7.5B', 6.0, 6.0), np.array([0.2352, 0.2708, 30.0500])), - (('7.5B', 6.0, 8.0), np.array([0.2132, 0.2537, 30.0500])), - (('7.5B', 6.0, 10.0), np.array([0.1934, 0.2374, 30.0500])), - (('7.5B', 6.0, 12.0), np.array([0.1734, 0.2203, 30.0500])), - (('7.5B', 6.0, 14.0), np.array([0.1556, 0.2043, 30.0500])), - (('7.5B', 6.0, 16.0), np.array([0.1376, 0.1879, 30.0500])), - (('10B', 6.0, 2.0), np.array([0.2871, 0.3012, 30.0500])), - (('10B', 6.0, 4.0), np.array([0.2637, 0.2840, 30.0500])), - (('10B', 6.0, 6.0), np.array([0.2399, 0.2650, 30.0500])), - (('10B', 6.0, 8.0), np.array([0.2189, 0.2468, 30.0500])), - (('10B', 6.0, 10.0), np.array([0.2000, 0.2298, 30.0500])), - (('10B', 6.0, 12.0), np.array([0.1803, 0.2114, 30.0500])), - (('10B', 6.0, 14.0), np.array([0.1629, 0.1947, 30.0500])), - (('10B', 6.0, 16.0), np.array([0.1454, 0.1778, 30.0500])), - (('2.5PB', 6.0, 2.0), np.array([0.2897, 0.2991, 30.0500])), - (('2.5PB', 6.0, 4.0), np.array([0.2684, 0.2804, 30.0500])), - (('2.5PB', 6.0, 6.0), np.array([0.2465, 0.2599, 30.0500])), - (('2.5PB', 6.0, 8.0), np.array([0.2274, 0.2406, 30.0500])), - (('2.5PB', 6.0, 10.0), np.array([0.2095, 0.2225, 30.0500])), - (('2.5PB', 6.0, 12.0), np.array([0.1913, 0.2038, 30.0500])), - (('2.5PB', 6.0, 14.0), np.array([0.1754, 0.1868, 30.0500])), - (('5PB', 6.0, 2.0), np.array([0.2923, 0.2978, 30.0500])), - (('5PB', 6.0, 4.0), np.array([0.2734, 0.2778, 30.0500])), - (('5PB', 6.0, 6.0), np.array([0.2533, 0.2558, 30.0500])), - (('5PB', 6.0, 8.0), np.array([0.2360, 0.2365, 30.0500])), - (('5PB', 6.0, 10.0), np.array([0.2197, 0.2188, 30.0500])), - (('5PB', 6.0, 12.0), np.array([0.2026, 0.1999, 30.0500])), - (('5PB', 6.0, 14.0), np.array([0.1873, 0.1822, 30.0500])), - (('7.5PB', 6.0, 2.0), np.array([0.2955, 0.2963, 30.0500])), - (('7.5PB', 6.0, 4.0), np.array([0.2798, 0.2752, 30.0500])), - (('7.5PB', 6.0, 6.0), np.array([0.2638, 0.2531, 30.0500])), - (('7.5PB', 6.0, 8.0), np.array([0.2505, 0.2347, 30.0500])), - (('7.5PB', 6.0, 10.0), np.array([0.2378, 0.2168, 30.0500])), - (('7.5PB', 6.0, 12.0), np.array([0.2241, 0.1975, 30.0500])), - (('7.5PB', 6.0, 14.0), np.array([0.2119, 0.1799, 30.0500])), - (('10PB', 6.0, 2.0), np.array([0.2988, 0.2961, 30.0500])), - (('10PB', 6.0, 4.0), np.array([0.2863, 0.2747, 30.0500])), - (('10PB', 6.0, 6.0), np.array([0.2740, 0.2533, 30.0500])), - (('10PB', 6.0, 8.0), np.array([0.2637, 0.2352, 30.0500])), - (('10PB', 6.0, 10.0), np.array([0.2540, 0.2176, 30.0500])), - (('10PB', 6.0, 12.0), np.array([0.2440, 0.1998, 30.0500])), - (('10PB', 6.0, 14.0), np.array([0.2352, 0.1839, 30.0500])), - (('10PB', 6.0, 16.0), np.array([0.2265, 0.1671, 30.0500])), - (('2.5P', 6.0, 2.0), np.array([0.3016, 0.2960, 30.0500])), - (('2.5P', 6.0, 4.0), np.array([0.2932, 0.2759, 30.0500])), - (('2.5P', 6.0, 6.0), np.array([0.2842, 0.2550, 30.0500])), - (('2.5P', 6.0, 8.0), np.array([0.2770, 0.2372, 30.0500])), - (('2.5P', 6.0, 10.0), np.array([0.2703, 0.2204, 30.0500])), - (('2.5P', 6.0, 12.0), np.array([0.2647, 0.2052, 30.0500])), - (('2.5P', 6.0, 14.0), np.array([0.2593, 0.1909, 30.0500])), - (('2.5P', 6.0, 16.0), np.array([0.2548, 0.1768, 30.0500])), - (('2.5P', 6.0, 18.0), np.array([0.2504, 0.1658, 30.0500])), - (('5P', 6.0, 2.0), np.array([0.3050, 0.2967, 30.0500])), - (('5P', 6.0, 4.0), np.array([0.3001, 0.2778, 30.0500])), - (('5P', 6.0, 6.0), np.array([0.2950, 0.2585, 30.0500])), - (('5P', 6.0, 8.0), np.array([0.2905, 0.2421, 30.0500])), - (('5P', 6.0, 10.0), np.array([0.2862, 0.2260, 30.0500])), - (('5P', 6.0, 12.0), np.array([0.2829, 0.2121, 30.0500])), - (('5P', 6.0, 14.0), np.array([0.2794, 0.1979, 30.0500])), - (('5P', 6.0, 16.0), np.array([0.2761, 0.1852, 30.0500])), - (('5P', 6.0, 18.0), np.array([0.2731, 0.1738, 30.0500])), - (('5P', 6.0, 20.0), np.array([0.2702, 0.1621, 30.0500])), - (('7.5P', 6.0, 2.0), np.array([0.3107, 0.2993, 30.0500])), - (('7.5P', 6.0, 4.0), np.array([0.3107, 0.2831, 30.0500])), - (('7.5P', 6.0, 6.0), np.array([0.3101, 0.2650, 30.0500])), - (('7.5P', 6.0, 8.0), np.array([0.3099, 0.2502, 30.0500])), - (('7.5P', 6.0, 10.0), np.array([0.3092, 0.2350, 30.0500])), - (('7.5P', 6.0, 12.0), np.array([0.3090, 0.2222, 30.0500])), - (('7.5P', 6.0, 14.0), np.array([0.3084, 0.2095, 30.0500])), - (('7.5P', 6.0, 16.0), np.array([0.3080, 0.1976, 30.0500])), - (('7.5P', 6.0, 18.0), np.array([0.3075, 0.1870, 30.0500])), - (('7.5P', 6.0, 20.0), np.array([0.3069, 0.1745, 30.0500])), - (('7.5P', 6.0, 22.0), np.array([0.3062, 0.1638, 30.0500])), - (('7.5P', 6.0, 24.0), np.array([0.3058, 0.1547, 30.0500])), - (('10P', 6.0, 2.0), np.array([0.3146, 0.3018, 30.0500])), - (('10P', 6.0, 4.0), np.array([0.3181, 0.2871, 30.0500])), - (('10P', 6.0, 6.0), np.array([0.3226, 0.2716, 30.0500])), - (('10P', 6.0, 8.0), np.array([0.3259, 0.2584, 30.0500])), - (('10P', 6.0, 10.0), np.array([0.3293, 0.2450, 30.0500])), - (('10P', 6.0, 12.0), np.array([0.3321, 0.2329, 30.0500])), - (('10P', 6.0, 14.0), np.array([0.3349, 0.2203, 30.0500])), - (('10P', 6.0, 16.0), np.array([0.3370, 0.2095, 30.0500])), - (('10P', 6.0, 18.0), np.array([0.3388, 0.1995, 30.0500])), - (('10P', 6.0, 20.0), np.array([0.3409, 0.1882, 30.0500])), - (('10P', 6.0, 22.0), np.array([0.3426, 0.1785, 30.0500])), - (('10P', 6.0, 24.0), np.array([0.3441, 0.1698, 30.0500])), - (('10P', 6.0, 26.0), np.array([0.3457, 0.1604, 30.0500])), - (('2.5RP', 6.0, 2.0), np.array([0.3188, 0.3048, 30.0500])), - (('2.5RP', 6.0, 4.0), np.array([0.3272, 0.2929, 30.0500])), - (('2.5RP', 6.0, 6.0), np.array([0.3362, 0.2799, 30.0500])), - (('2.5RP', 6.0, 8.0), np.array([0.3437, 0.2688, 30.0500])), - (('2.5RP', 6.0, 10.0), np.array([0.3509, 0.2578, 30.0500])), - (('2.5RP', 6.0, 12.0), np.array([0.3582, 0.2462, 30.0500])), - (('2.5RP', 6.0, 14.0), np.array([0.3652, 0.2355, 30.0500])), - (('2.5RP', 6.0, 16.0), np.array([0.3718, 0.2251, 30.0500])), - (('2.5RP', 6.0, 18.0), np.array([0.3773, 0.2158, 30.0500])), - (('2.5RP', 6.0, 20.0), np.array([0.3833, 0.2056, 30.0500])), - (('2.5RP', 6.0, 22.0), np.array([0.3877, 0.1978, 30.0500])), - (('2.5RP', 6.0, 24.0), np.array([0.3927, 0.1892, 30.0500])), - (('5RP', 6.0, 2.0), np.array([0.3232, 0.3085, 30.0500])), - (('5RP', 6.0, 4.0), np.array([0.3371, 0.3001, 30.0500])), - (('5RP', 6.0, 6.0), np.array([0.3520, 0.2904, 30.0500])), - (('5RP', 6.0, 8.0), np.array([0.3648, 0.2820, 30.0500])), - (('5RP', 6.0, 10.0), np.array([0.3769, 0.2738, 30.0500])), - (('5RP', 6.0, 12.0), np.array([0.3900, 0.2646, 30.0500])), - (('5RP', 6.0, 14.0), np.array([0.4023, 0.2552, 30.0500])), - (('5RP', 6.0, 16.0), np.array([0.4136, 0.2467, 30.0500])), - (('5RP', 6.0, 18.0), np.array([0.4245, 0.2382, 30.0500])), - (('5RP', 6.0, 20.0), np.array([0.4368, 0.2283, 30.0500])), - (('5RP', 6.0, 22.0), np.array([0.4449, 0.2219, 30.0500])), - (('7.5RP', 6.0, 2.0), np.array([0.3261, 0.3113, 30.0500])), - (('7.5RP', 6.0, 4.0), np.array([0.3439, 0.3056, 30.0500])), - (('7.5RP', 6.0, 6.0), np.array([0.3635, 0.2987, 30.0500])), - (('7.5RP', 6.0, 8.0), np.array([0.3791, 0.2929, 30.0500])), - (('7.5RP', 6.0, 10.0), np.array([0.3960, 0.2860, 30.0500])), - (('7.5RP', 6.0, 12.0), np.array([0.4125, 0.2784, 30.0500])), - (('7.5RP', 6.0, 14.0), np.array([0.4285, 0.2705, 30.0500])), - (('7.5RP', 6.0, 16.0), np.array([0.4448, 0.2622, 30.0500])), - (('7.5RP', 6.0, 18.0), np.array([0.4581, 0.2549, 30.0500])), - (('7.5RP', 6.0, 20.0), np.array([0.4735, 0.2464, 30.0500])), - (('10RP', 7.0, 2.0), np.array([0.3258, 0.3148, 43.0600])), - (('10RP', 7.0, 4.0), np.array([0.3446, 0.3125, 43.0600])), - (('10RP', 7.0, 6.0), np.array([0.3648, 0.3098, 43.0600])), - (('10RP', 7.0, 8.0), np.array([0.3851, 0.3067, 43.0600])), - (('10RP', 7.0, 10.0), np.array([0.4040, 0.3030, 43.0600])), - (('10RP', 7.0, 12.0), np.array([0.4260, 0.2980, 43.0600])), - (('10RP', 7.0, 14.0), np.array([0.4456, 0.2931, 43.0600])), - (('10RP', 7.0, 16.0), np.array([0.4648, 0.2878, 43.0600])), - (('2.5R', 7.0, 2.0), np.array([0.3284, 0.3170, 43.0600])), - (('2.5R', 7.0, 4.0), np.array([0.3499, 0.3171, 43.0600])), - (('2.5R', 7.0, 6.0), np.array([0.3728, 0.3170, 43.0600])), - (('2.5R', 7.0, 8.0), np.array([0.3961, 0.3160, 43.0600])), - (('2.5R', 7.0, 10.0), np.array([0.4183, 0.3144, 43.0600])), - (('2.5R', 7.0, 12.0), np.array([0.4435, 0.3119, 43.0600])), - (('2.5R', 7.0, 14.0), np.array([0.4660, 0.3082, 43.0600])), - (('2.5R', 7.0, 16.0), np.array([0.4885, 0.3039, 43.0600])), - (('5R', 7.0, 2.0), np.array([0.3306, 0.3190, 43.0600])), - (('5R', 7.0, 4.0), np.array([0.3552, 0.3222, 43.0600])), - (('5R', 7.0, 6.0), np.array([0.3805, 0.3244, 43.0600])), - (('5R', 7.0, 8.0), np.array([0.4067, 0.3256, 43.0600])), - (('5R', 7.0, 10.0), np.array([0.4320, 0.3260, 43.0600])), - (('5R', 7.0, 12.0), np.array([0.4595, 0.3252, 43.0600])), - (('5R', 7.0, 14.0), np.array([0.4848, 0.3238, 43.0600])), - (('7.5R', 7.0, 2.0), np.array([0.3335, 0.3220, 43.0600])), - (('7.5R', 7.0, 4.0), np.array([0.3611, 0.3282, 43.0600])), - (('7.5R', 7.0, 6.0), np.array([0.3888, 0.3336, 43.0600])), - (('7.5R', 7.0, 8.0), np.array([0.4196, 0.3382, 43.0600])), - (('7.5R', 7.0, 10.0), np.array([0.4470, 0.3413, 43.0600])), - (('7.5R', 7.0, 12.0), np.array([0.4777, 0.3435, 43.0600])), - (('7.5R', 7.0, 14.0), np.array([0.5059, 0.3450, 43.0600])), - (('7.5R', 7.0, 16.0), np.array([0.5341, 0.3452, 43.0600])), - (('10R', 7.0, 2.0), np.array([0.3360, 0.3253, 43.0600])), - (('10R', 7.0, 4.0), np.array([0.3671, 0.3360, 43.0600])), - (('10R', 7.0, 6.0), np.array([0.3984, 0.3452, 43.0600])), - (('10R', 7.0, 8.0), np.array([0.4308, 0.3533, 43.0600])), - (('10R', 7.0, 10.0), np.array([0.4600, 0.3596, 43.0600])), - (('10R', 7.0, 12.0), np.array([0.4930, 0.3659, 43.0600])), - (('10R', 7.0, 14.0), np.array([0.5234, 0.3700, 43.0600])), - (('10R', 7.0, 16.0), np.array([0.5519, 0.3729, 43.0600])), - (('2.5YR', 7.0, 2.0), np.array([0.3392, 0.3298, 43.0600])), - (('2.5YR', 7.0, 4.0), np.array([0.3715, 0.3439, 43.0600])), - (('2.5YR', 7.0, 6.0), np.array([0.4053, 0.3570, 43.0600])), - (('2.5YR', 7.0, 8.0), np.array([0.4371, 0.3679, 43.0600])), - (('2.5YR', 7.0, 10.0), np.array([0.4671, 0.3768, 43.0600])), - (('2.5YR', 7.0, 12.0), np.array([0.5001, 0.3861, 43.0600])), - (('2.5YR', 7.0, 14.0), np.array([0.5297, 0.3938, 43.0600])), - (('2.5YR', 7.0, 16.0), np.array([0.5522, 0.3989, 43.0600])), - (('2.5YR', 7.0, 18.0), np.array([0.5695, 0.4024, 43.0600])), - (('2.5YR', 7.0, 20.0), np.array([0.5824, 0.4046, 43.0600])), - (('5YR', 7.0, 2.0), np.array([0.3421, 0.3349, 43.0600])), - (('5YR', 7.0, 4.0), np.array([0.3750, 0.3530, 43.0600])), - (('5YR', 7.0, 6.0), np.array([0.4091, 0.3701, 43.0600])), - (('5YR', 7.0, 8.0), np.array([0.4402, 0.3842, 43.0600])), - (('5YR', 7.0, 10.0), np.array([0.4711, 0.3972, 43.0600])), - (('5YR', 7.0, 12.0), np.array([0.5007, 0.4081, 43.0600])), - (('5YR', 7.0, 14.0), np.array([0.5252, 0.4168, 43.0600])), - (('5YR', 7.0, 16.0), np.array([0.5437, 0.4228, 43.0600])), - (('5YR', 7.0, 18.0), np.array([0.5564, 0.4267, 43.0600])), - (('5YR', 7.0, 20.0), np.array([0.5657, 0.4298, 43.0600])), - (('7.5YR', 7.0, 2.0), np.array([0.3437, 0.3397, 43.0600])), - (('7.5YR', 7.0, 4.0), np.array([0.3772, 0.3613, 43.0600])), - (('7.5YR', 7.0, 6.0), np.array([0.4107, 0.3820, 43.0600])), - (('7.5YR', 7.0, 8.0), np.array([0.4415, 0.3996, 43.0600])), - (('7.5YR', 7.0, 10.0), np.array([0.4704, 0.4151, 43.0600])), - (('7.5YR', 7.0, 12.0), np.array([0.4970, 0.4282, 43.0600])), - (('7.5YR', 7.0, 14.0), np.array([0.5174, 0.4381, 43.0600])), - (('7.5YR', 7.0, 16.0), np.array([0.5319, 0.4449, 43.0600])), - (('7.5YR', 7.0, 18.0), np.array([0.5417, 0.4492, 43.0600])), - (('10YR', 7.0, 2.0), np.array([0.3443, 0.3454, 43.0600])), - (('10YR', 7.0, 4.0), np.array([0.3778, 0.3719, 43.0600])), - (('10YR', 7.0, 6.0), np.array([0.4102, 0.3960, 43.0600])), - (('10YR', 7.0, 8.0), np.array([0.4399, 0.4164, 43.0600])), - (('10YR', 7.0, 10.0), np.array([0.4667, 0.4335, 43.0600])), - (('10YR', 7.0, 12.0), np.array([0.4900, 0.4480, 43.0600])), - (('10YR', 7.0, 14.0), np.array([0.5074, 0.4581, 43.0600])), - (('10YR', 7.0, 16.0), np.array([0.5188, 0.4650, 43.0600])), - (('10YR', 7.0, 18.0), np.array([0.5276, 0.4700, 43.0600])), - (('2.5Y', 7.0, 2.0), np.array([0.3436, 0.3507, 43.0600])), - (('2.5Y', 7.0, 4.0), np.array([0.3761, 0.3800, 43.0600])), - (('2.5Y', 7.0, 6.0), np.array([0.4073, 0.4073, 43.0600])), - (('2.5Y', 7.0, 8.0), np.array([0.4353, 0.4312, 43.0600])), - (('2.5Y', 7.0, 10.0), np.array([0.4606, 0.4516, 43.0600])), - (('2.5Y', 7.0, 12.0), np.array([0.4806, 0.4666, 43.0600])), - (('2.5Y', 7.0, 14.0), np.array([0.4950, 0.4773, 43.0600])), - (('2.5Y', 7.0, 16.0), np.array([0.5049, 0.4843, 43.0600])), - (('5Y', 7.0, 2.0), np.array([0.3419, 0.3540, 43.0600])), - (('5Y', 7.0, 4.0), np.array([0.3718, 0.3885, 43.0600])), - (('5Y', 7.0, 6.0), np.array([0.4009, 0.4198, 43.0600])), - (('5Y', 7.0, 8.0), np.array([0.4271, 0.4462, 43.0600])), - (('5Y', 7.0, 10.0), np.array([0.4509, 0.4696, 43.0600])), - (('5Y', 7.0, 12.0), np.array([0.4677, 0.4857, 43.0600])), - (('5Y', 7.0, 14.0), np.array([0.4791, 0.4965, 43.0600])), - (('5Y', 7.0, 16.0), np.array([0.4875, 0.5047, 43.0600])), - (('7.5Y', 7.0, 2.0), np.array([0.3396, 0.3558, 43.0600])), - (('7.5Y', 7.0, 4.0), np.array([0.3677, 0.3925, 43.0600])), - (('7.5Y', 7.0, 6.0), np.array([0.3943, 0.4264, 43.0600])), - (('7.5Y', 7.0, 8.0), np.array([0.4184, 0.4568, 43.0600])), - (('7.5Y', 7.0, 10.0), np.array([0.4400, 0.4830, 43.0600])), - (('7.5Y', 7.0, 12.0), np.array([0.4547, 0.5005, 43.0600])), - (('7.5Y', 7.0, 14.0), np.array([0.4652, 0.5128, 43.0600])), - (('7.5Y', 7.0, 16.0), np.array([0.4728, 0.5215, 43.0600])), - (('10Y', 7.0, 2.0), np.array([0.3369, 0.3569, 43.0600])), - (('10Y', 7.0, 4.0), np.array([0.3624, 0.3951, 43.0600])), - (('10Y', 7.0, 6.0), np.array([0.3864, 0.4305, 43.0600])), - (('10Y', 7.0, 8.0), np.array([0.4090, 0.4641, 43.0600])), - (('10Y', 7.0, 10.0), np.array([0.4289, 0.4937, 43.0600])), - (('10Y', 7.0, 12.0), np.array([0.4420, 0.5131, 43.0600])), - (('10Y', 7.0, 14.0), np.array([0.4516, 0.5277, 43.0600])), - (('10Y', 7.0, 16.0), np.array([0.4582, 0.5375, 43.0600])), - (('2.5GY', 7.0, 2.0), np.array([0.3328, 0.3569, 43.0600])), - (('2.5GY', 7.0, 4.0), np.array([0.3534, 0.3953, 43.0600])), - (('2.5GY', 7.0, 6.0), np.array([0.3728, 0.4316, 43.0600])), - (('2.5GY', 7.0, 8.0), np.array([0.3919, 0.4684, 43.0600])), - (('2.5GY', 7.0, 10.0), np.array([0.4091, 0.5030, 43.0600])), - (('2.5GY', 7.0, 12.0), np.array([0.4213, 0.5270, 43.0600])), - (('2.5GY', 7.0, 14.0), np.array([0.4309, 0.5459, 43.0600])), - (('2.5GY', 7.0, 16.0), np.array([0.4366, 0.5578, 43.0600])), - (('5GY', 7.0, 2.0), np.array([0.3284, 0.3559, 43.0600])), - (('5GY', 7.0, 4.0), np.array([0.3437, 0.3929, 43.0600])), - (('5GY', 7.0, 6.0), np.array([0.3581, 0.4291, 43.0600])), - (('5GY', 7.0, 8.0), np.array([0.3722, 0.4669, 43.0600])), - (('5GY', 7.0, 10.0), np.array([0.3852, 0.5051, 43.0600])), - (('5GY', 7.0, 12.0), np.array([0.3949, 0.5367, 43.0600])), - (('5GY', 7.0, 14.0), np.array([0.4027, 0.5615, 43.0600])), - (('5GY', 7.0, 16.0), np.array([0.4076, 0.5783, 43.0600])), - (('7.5GY', 7.0, 2.0), np.array([0.3190, 0.3516, 43.0600])), - (('7.5GY', 7.0, 4.0), np.array([0.3267, 0.3848, 43.0600])), - (('7.5GY', 7.0, 6.0), np.array([0.3341, 0.4191, 43.0600])), - (('7.5GY', 7.0, 8.0), np.array([0.3406, 0.4558, 43.0600])), - (('7.5GY', 7.0, 10.0), np.array([0.3461, 0.4950, 43.0600])), - (('7.5GY', 7.0, 12.0), np.array([0.3502, 0.5328, 43.0600])), - (('7.5GY', 7.0, 14.0), np.array([0.3532, 0.5700, 43.0600])), - (('7.5GY', 7.0, 16.0), np.array([0.3549, 0.6000, 43.0600])), - (('7.5GY', 7.0, 18.0), np.array([0.3555, 0.6242, 43.0600])), - (('10GY', 7.0, 2.0), np.array([0.3117, 0.3469, 43.0600])), - (('10GY', 7.0, 4.0), np.array([0.3133, 0.3764, 43.0600])), - (('10GY', 7.0, 6.0), np.array([0.3142, 0.4058, 43.0600])), - (('10GY', 7.0, 8.0), np.array([0.3140, 0.4387, 43.0600])), - (('10GY', 7.0, 10.0), np.array([0.3123, 0.4732, 43.0600])), - (('10GY', 7.0, 12.0), np.array([0.3092, 0.5095, 43.0600])), - (('10GY', 7.0, 14.0), np.array([0.3047, 0.5458, 43.0600])), - (('10GY', 7.0, 16.0), np.array([0.2981, 0.5835, 43.0600])), - (('10GY', 7.0, 18.0), np.array([0.2905, 0.6186, 43.0600])), - (('10GY', 7.0, 20.0), np.array([0.2816, 0.6563, 43.0600])), - (('10GY', 7.0, 22.0), np.array([0.2728, 0.6893, 43.0600])), - (('2.5G', 7.0, 2.0), np.array([0.3047, 0.3413, 43.0600])), - (('2.5G', 7.0, 4.0), np.array([0.2992, 0.3644, 43.0600])), - (('2.5G', 7.0, 6.0), np.array([0.2933, 0.3873, 43.0600])), - (('2.5G', 7.0, 8.0), np.array([0.2861, 0.4129, 43.0600])), - (('2.5G', 7.0, 10.0), np.array([0.2775, 0.4395, 43.0600])), - (('2.5G', 7.0, 12.0), np.array([0.2672, 0.4667, 43.0600])), - (('2.5G', 7.0, 14.0), np.array([0.2568, 0.4931, 43.0600])), - (('2.5G', 7.0, 16.0), np.array([0.2448, 0.5203, 43.0600])), - (('2.5G', 7.0, 18.0), np.array([0.2328, 0.5467, 43.0600])), - (('2.5G', 7.0, 20.0), np.array([0.2181, 0.5744, 43.0600])), - (('2.5G', 7.0, 22.0), np.array([0.2029, 0.6017, 43.0600])), - (('2.5G', 7.0, 24.0), np.array([0.1875, 0.6265, 43.0600])), - (('2.5G', 7.0, 26.0), np.array([0.1689, 0.6549, 43.0600])), - (('5G', 7.0, 2.0), np.array([0.3001, 0.3366, 43.0600])), - (('5G', 7.0, 4.0), np.array([0.2902, 0.3548, 43.0600])), - (('5G', 7.0, 6.0), np.array([0.2801, 0.3721, 43.0600])), - (('5G', 7.0, 8.0), np.array([0.2687, 0.3901, 43.0600])), - (('5G', 7.0, 10.0), np.array([0.2554, 0.4087, 43.0600])), - (('5G', 7.0, 12.0), np.array([0.2416, 0.4267, 43.0600])), - (('5G', 7.0, 14.0), np.array([0.2262, 0.4450, 43.0600])), - (('5G', 7.0, 16.0), np.array([0.2111, 0.4616, 43.0600])), - (('5G', 7.0, 18.0), np.array([0.1967, 0.4771, 43.0600])), - (('5G', 7.0, 20.0), np.array([0.1805, 0.4933, 43.0600])), - (('5G', 7.0, 22.0), np.array([0.1659, 0.5074, 43.0600])), - (('5G', 7.0, 24.0), np.array([0.1521, 0.5200, 43.0600])), - (('5G', 7.0, 26.0), np.array([0.1397, 0.5312, 43.0600])), - (('7.5G', 7.0, 2.0), np.array([0.2972, 0.3333, 43.0600])), - (('7.5G', 7.0, 4.0), np.array([0.2850, 0.3482, 43.0600])), - (('7.5G', 7.0, 6.0), np.array([0.2728, 0.3622, 43.0600])), - (('7.5G', 7.0, 8.0), np.array([0.2595, 0.3764, 43.0600])), - (('7.5G', 7.0, 10.0), np.array([0.2445, 0.3914, 43.0600])), - (('7.5G', 7.0, 12.0), np.array([0.2295, 0.4058, 43.0600])), - (('7.5G', 7.0, 14.0), np.array([0.2139, 0.4199, 43.0600])), - (('7.5G', 7.0, 16.0), np.array([0.1982, 0.4330, 43.0600])), - (('7.5G', 7.0, 18.0), np.array([0.1841, 0.4448, 43.0600])), - (('7.5G', 7.0, 20.0), np.array([0.1688, 0.4570, 43.0600])), - (('7.5G', 7.0, 22.0), np.array([0.1539, 0.4683, 43.0600])), - (('7.5G', 7.0, 24.0), np.array([0.1415, 0.4778, 43.0600])), - (('7.5G', 7.0, 26.0), np.array([0.1303, 0.4858, 43.0600])), - (('10G', 7.0, 2.0), np.array([0.2945, 0.3297, 43.0600])), - (('10G', 7.0, 4.0), np.array([0.2803, 0.3415, 43.0600])), - (('10G', 7.0, 6.0), np.array([0.2662, 0.3526, 43.0600])), - (('10G', 7.0, 8.0), np.array([0.2513, 0.3635, 43.0600])), - (('10G', 7.0, 10.0), np.array([0.2352, 0.3748, 43.0600])), - (('10G', 7.0, 12.0), np.array([0.2195, 0.3854, 43.0600])), - (('10G', 7.0, 14.0), np.array([0.2033, 0.3956, 43.0600])), - (('10G', 7.0, 16.0), np.array([0.1881, 0.4049, 43.0600])), - (('10G', 7.0, 18.0), np.array([0.1734, 0.4135, 43.0600])), - (('10G', 7.0, 20.0), np.array([0.1589, 0.4220, 43.0600])), - (('10G', 7.0, 22.0), np.array([0.1434, 0.4306, 43.0600])), - (('10G', 7.0, 24.0), np.array([0.1310, 0.4377, 43.0600])), - (('2.5BG', 7.0, 2.0), np.array([0.2927, 0.3269, 43.0600])), - (('2.5BG', 7.0, 4.0), np.array([0.2764, 0.3354, 43.0600])), - (('2.5BG', 7.0, 6.0), np.array([0.2608, 0.3430, 43.0600])), - (('2.5BG', 7.0, 8.0), np.array([0.2439, 0.3508, 43.0600])), - (('2.5BG', 7.0, 10.0), np.array([0.2264, 0.3576, 43.0600])), - (('2.5BG', 7.0, 12.0), np.array([0.2102, 0.3636, 43.0600])), - (('2.5BG', 7.0, 14.0), np.array([0.1932, 0.3694, 43.0600])), - (('2.5BG', 7.0, 16.0), np.array([0.1788, 0.3739, 43.0600])), - (('2.5BG', 7.0, 18.0), np.array([0.1626, 0.3788, 43.0600])), - (('2.5BG', 7.0, 20.0), np.array([0.1490, 0.3827, 43.0600])), - (('2.5BG', 7.0, 22.0), np.array([0.1334, 0.3870, 43.0600])), - (('5BG', 7.0, 2.0), np.array([0.2898, 0.3225, 43.0600])), - (('5BG', 7.0, 4.0), np.array([0.2712, 0.3269, 43.0600])), - (('5BG', 7.0, 6.0), np.array([0.2543, 0.3302, 43.0600])), - (('5BG', 7.0, 8.0), np.array([0.2354, 0.3335, 43.0600])), - (('5BG', 7.0, 10.0), np.array([0.2163, 0.3361, 43.0600])), - (('5BG', 7.0, 12.0), np.array([0.1997, 0.3379, 43.0600])), - (('5BG', 7.0, 14.0), np.array([0.1838, 0.3390, 43.0600])), - (('5BG', 7.0, 16.0), np.array([0.1675, 0.3401, 43.0600])), - (('5BG', 7.0, 18.0), np.array([0.1515, 0.3410, 43.0600])), - (('5BG', 7.0, 20.0), np.array([0.1380, 0.3412, 43.0600])), - (('7.5BG', 7.0, 2.0), np.array([0.2878, 0.3182, 43.0600])), - (('7.5BG', 7.0, 4.0), np.array([0.2671, 0.3189, 43.0600])), - (('7.5BG', 7.0, 6.0), np.array([0.2490, 0.3186, 43.0600])), - (('7.5BG', 7.0, 8.0), np.array([0.2292, 0.3178, 43.0600])), - (('7.5BG', 7.0, 10.0), np.array([0.2094, 0.3165, 43.0600])), - (('7.5BG', 7.0, 12.0), np.array([0.1914, 0.3148, 43.0600])), - (('7.5BG', 7.0, 14.0), np.array([0.1751, 0.3129, 43.0600])), - (('7.5BG', 7.0, 16.0), np.array([0.1584, 0.3101, 43.0600])), - (('7.5BG', 7.0, 18.0), np.array([0.1427, 0.3076, 43.0600])), - (('10BG', 7.0, 2.0), np.array([0.2869, 0.3143, 43.0600])), - (('10BG', 7.0, 4.0), np.array([0.2642, 0.3109, 43.0600])), - (('10BG', 7.0, 6.0), np.array([0.2448, 0.3069, 43.0600])), - (('10BG', 7.0, 8.0), np.array([0.2235, 0.3014, 43.0600])), - (('10BG', 7.0, 10.0), np.array([0.2035, 0.2956, 43.0600])), - (('10BG', 7.0, 12.0), np.array([0.1841, 0.2892, 43.0600])), - (('10BG', 7.0, 14.0), np.array([0.1671, 0.2832, 43.0600])), - (('10BG', 7.0, 16.0), np.array([0.1489, 0.2768, 43.0600])), - (('2.5B', 7.0, 2.0), np.array([0.2867, 0.3110, 43.0600])), - (('2.5B', 7.0, 4.0), np.array([0.2629, 0.3038, 43.0600])), - (('2.5B', 7.0, 6.0), np.array([0.2418, 0.2960, 43.0600])), - (('2.5B', 7.0, 8.0), np.array([0.2208, 0.2871, 43.0600])), - (('2.5B', 7.0, 10.0), np.array([0.1994, 0.2775, 43.0600])), - (('2.5B', 7.0, 12.0), np.array([0.1797, 0.2672, 43.0600])), - (('2.5B', 7.0, 14.0), np.array([0.1624, 0.2581, 43.0600])), - (('2.5B', 7.0, 16.0), np.array([0.1435, 0.2472, 43.0600])), - (('5B', 7.0, 2.0), np.array([0.2875, 0.3078, 43.0600])), - (('5B', 7.0, 4.0), np.array([0.2633, 0.2972, 43.0600])), - (('5B', 7.0, 6.0), np.array([0.2410, 0.2854, 43.0600])), - (('5B', 7.0, 8.0), np.array([0.2204, 0.2729, 43.0600])), - (('5B', 7.0, 10.0), np.array([0.1986, 0.2579, 43.0600])), - (('5B', 7.0, 12.0), np.array([0.1778, 0.2430, 43.0600])), - (('5B', 7.0, 14.0), np.array([0.1615, 0.2307, 43.0600])), - (('7.5B', 7.0, 2.0), np.array([0.2888, 0.3058, 43.0600])), - (('7.5B', 7.0, 4.0), np.array([0.2651, 0.2927, 43.0600])), - (('7.5B', 7.0, 6.0), np.array([0.2436, 0.2787, 43.0600])), - (('7.5B', 7.0, 8.0), np.array([0.2225, 0.2631, 43.0600])), - (('7.5B', 7.0, 10.0), np.array([0.2016, 0.2466, 43.0600])), - (('7.5B', 7.0, 12.0), np.array([0.1818, 0.2303, 43.0600])), - (('10B', 7.0, 2.0), np.array([0.2908, 0.3039, 43.0600])), - (('10B', 7.0, 4.0), np.array([0.2685, 0.2886, 43.0600])), - (('10B', 7.0, 6.0), np.array([0.2478, 0.2728, 43.0600])), - (('10B', 7.0, 8.0), np.array([0.2277, 0.2559, 43.0600])), - (('10B', 7.0, 10.0), np.array([0.2078, 0.2382, 43.0600])), - (('10B', 7.0, 12.0), np.array([0.1883, 0.2203, 43.0600])), - (('2.5PB', 7.0, 2.0), np.array([0.2932, 0.3025, 43.0600])), - (('2.5PB', 7.0, 4.0), np.array([0.2729, 0.2848, 43.0600])), - (('2.5PB', 7.0, 6.0), np.array([0.2538, 0.2677, 43.0600])), - (('2.5PB', 7.0, 8.0), np.array([0.2352, 0.2498, 43.0600])), - (('2.5PB', 7.0, 10.0), np.array([0.2162, 0.2309, 43.0600])), - (('5PB', 7.0, 2.0), np.array([0.2952, 0.3011, 43.0600])), - (('5PB', 7.0, 4.0), np.array([0.2773, 0.2828, 43.0600])), - (('5PB', 7.0, 6.0), np.array([0.2596, 0.2643, 43.0600])), - (('5PB', 7.0, 8.0), np.array([0.2427, 0.2458, 43.0600])), - (('5PB', 7.0, 10.0), np.array([0.2254, 0.2267, 43.0600])), - (('7.5PB', 7.0, 2.0), np.array([0.2982, 0.3003, 43.0600])), - (('7.5PB', 7.0, 4.0), np.array([0.2833, 0.2809, 43.0600])), - (('7.5PB', 7.0, 6.0), np.array([0.2687, 0.2612, 43.0600])), - (('7.5PB', 7.0, 8.0), np.array([0.2546, 0.2418, 43.0600])), - (('7.5PB', 7.0, 10.0), np.array([0.2410, 0.2224, 43.0600])), - (('10PB', 7.0, 2.0), np.array([0.3005, 0.3000, 43.0600])), - (('10PB', 7.0, 4.0), np.array([0.2886, 0.2801, 43.0600])), - (('10PB', 7.0, 6.0), np.array([0.2776, 0.2612, 43.0600])), - (('10PB', 7.0, 8.0), np.array([0.2670, 0.2425, 43.0600])), - (('10PB', 7.0, 10.0), np.array([0.2563, 0.2240, 43.0600])), - (('10PB', 7.0, 12.0), np.array([0.2465, 0.2058, 43.0600])), - (('2.5P', 7.0, 2.0), np.array([0.3031, 0.3000, 43.0600])), - (('2.5P', 7.0, 4.0), np.array([0.2950, 0.2810, 43.0600])), - (('2.5P', 7.0, 6.0), np.array([0.2873, 0.2633, 43.0600])), - (('2.5P', 7.0, 8.0), np.array([0.2799, 0.2459, 43.0600])), - (('2.5P', 7.0, 10.0), np.array([0.2729, 0.2289, 43.0600])), - (('2.5P', 7.0, 12.0), np.array([0.2664, 0.2127, 43.0600])), - (('5P', 7.0, 2.0), np.array([0.3059, 0.3010, 43.0600])), - (('5P', 7.0, 4.0), np.array([0.3009, 0.2831, 43.0600])), - (('5P', 7.0, 6.0), np.array([0.2961, 0.2663, 43.0600])), - (('5P', 7.0, 8.0), np.array([0.2918, 0.2504, 43.0600])), - (('5P', 7.0, 10.0), np.array([0.2872, 0.2343, 43.0600])), - (('5P', 7.0, 12.0), np.array([0.2833, 0.2197, 43.0600])), - (('5P', 7.0, 14.0), np.array([0.2801, 0.2068, 43.0600])), - (('7.5P', 7.0, 2.0), np.array([0.3109, 0.3037, 43.0600])), - (('7.5P', 7.0, 4.0), np.array([0.3111, 0.2880, 43.0600])), - (('7.5P', 7.0, 6.0), np.array([0.3111, 0.2730, 43.0600])), - (('7.5P', 7.0, 8.0), np.array([0.3109, 0.2584, 43.0600])), - (('7.5P', 7.0, 10.0), np.array([0.3108, 0.2442, 43.0600])), - (('7.5P', 7.0, 12.0), np.array([0.3104, 0.2320, 43.0600])), - (('7.5P', 7.0, 14.0), np.array([0.3101, 0.2192, 43.0600])), - (('7.5P', 7.0, 16.0), np.array([0.3099, 0.2074, 43.0600])), - (('7.5P', 7.0, 18.0), np.array([0.3093, 0.1962, 43.0600])), - (('10P', 7.0, 2.0), np.array([0.3138, 0.3054, 43.0600])), - (('10P', 7.0, 4.0), np.array([0.3181, 0.2920, 43.0600])), - (('10P', 7.0, 6.0), np.array([0.3221, 0.2786, 43.0600])), - (('10P', 7.0, 8.0), np.array([0.3256, 0.2654, 43.0600])), - (('10P', 7.0, 10.0), np.array([0.3288, 0.2531, 43.0600])), - (('10P', 7.0, 12.0), np.array([0.3314, 0.2423, 43.0600])), - (('10P', 7.0, 14.0), np.array([0.3341, 0.2308, 43.0600])), - (('10P', 7.0, 16.0), np.array([0.3368, 0.2192, 43.0600])), - (('10P', 7.0, 18.0), np.array([0.3391, 0.2088, 43.0600])), - (('10P', 7.0, 20.0), np.array([0.3410, 0.1988, 43.0600])), - (('10P', 7.0, 22.0), np.array([0.3430, 0.1883, 43.0600])), - (('2.5RP', 7.0, 2.0), np.array([0.3170, 0.3076, 43.0600])), - (('2.5RP', 7.0, 4.0), np.array([0.3254, 0.2971, 43.0600])), - (('2.5RP', 7.0, 6.0), np.array([0.3338, 0.2854, 43.0600])), - (('2.5RP', 7.0, 8.0), np.array([0.3417, 0.2745, 43.0600])), - (('2.5RP', 7.0, 10.0), np.array([0.3487, 0.2648, 43.0600])), - (('2.5RP', 7.0, 12.0), np.array([0.3555, 0.2545, 43.0600])), - (('2.5RP', 7.0, 14.0), np.array([0.3620, 0.2448, 43.0600])), - (('2.5RP', 7.0, 16.0), np.array([0.3688, 0.2342, 43.0600])), - (('2.5RP', 7.0, 18.0), np.array([0.3751, 0.2241, 43.0600])), - (('2.5RP', 7.0, 20.0), np.array([0.3811, 0.2143, 43.0600])), - (('5RP', 7.0, 2.0), np.array([0.3206, 0.3104, 43.0600])), - (('5RP', 7.0, 4.0), np.array([0.3332, 0.3032, 43.0600])), - (('5RP', 7.0, 6.0), np.array([0.3470, 0.2949, 43.0600])), - (('5RP', 7.0, 8.0), np.array([0.3603, 0.2869, 43.0600])), - (('5RP', 7.0, 10.0), np.array([0.3713, 0.2798, 43.0600])), - (('5RP', 7.0, 12.0), np.array([0.3841, 0.2710, 43.0600])), - (('5RP', 7.0, 14.0), np.array([0.3958, 0.2628, 43.0600])), - (('5RP', 7.0, 16.0), np.array([0.4076, 0.2540, 43.0600])), - (('5RP', 7.0, 18.0), np.array([0.4186, 0.2459, 43.0600])), - (('7.5RP', 7.0, 2.0), np.array([0.3232, 0.3125, 43.0600])), - (('7.5RP', 7.0, 4.0), np.array([0.3389, 0.3079, 43.0600])), - (('7.5RP', 7.0, 6.0), np.array([0.3562, 0.3022, 43.0600])), - (('7.5RP', 7.0, 8.0), np.array([0.3722, 0.2963, 43.0600])), - (('7.5RP', 7.0, 10.0), np.array([0.3871, 0.2906, 43.0600])), - (('7.5RP', 7.0, 12.0), np.array([0.4040, 0.2834, 43.0600])), - (('7.5RP', 7.0, 14.0), np.array([0.4195, 0.2762, 43.0600])), - (('7.5RP', 7.0, 16.0), np.array([0.4346, 0.2689, 43.0600])), - (('10RP', 8.0, 2.0), np.array([0.3218, 0.3152, 59.1000])), - (('10RP', 8.0, 4.0), np.array([0.3412, 0.3135, 59.1000])), - (('10RP', 8.0, 6.0), np.array([0.3600, 0.3112, 59.1000])), - (('10RP', 8.0, 8.0), np.array([0.3800, 0.3082, 59.1000])), - (('10RP', 8.0, 10.0), np.array([0.3983, 0.3049, 59.1000])), - (('2.5R', 8.0, 2.0), np.array([0.3236, 0.3169, 59.1000])), - (('2.5R', 8.0, 4.0), np.array([0.3460, 0.3177, 59.1000])), - (('2.5R', 8.0, 6.0), np.array([0.3671, 0.3175, 59.1000])), - (('2.5R', 8.0, 8.0), np.array([0.3900, 0.3171, 59.1000])), - (('2.5R', 8.0, 10.0), np.array([0.4125, 0.3160, 59.1000])), - (('5R', 8.0, 2.0), np.array([0.3254, 0.3186, 59.1000])), - (('5R', 8.0, 4.0), np.array([0.3510, 0.3224, 59.1000])), - (('5R', 8.0, 6.0), np.array([0.3743, 0.3248, 59.1000])), - (('5R', 8.0, 8.0), np.array([0.4001, 0.3263, 59.1000])), - (('5R', 8.0, 10.0), np.array([0.4249, 0.3270, 59.1000])), - (('7.5R', 8.0, 2.0), np.array([0.3277, 0.3211, 59.1000])), - (('7.5R', 8.0, 4.0), np.array([0.3564, 0.3279, 59.1000])), - (('7.5R', 8.0, 6.0), np.array([0.3830, 0.3335, 59.1000])), - (('7.5R', 8.0, 8.0), np.array([0.4118, 0.3385, 59.1000])), - (('7.5R', 8.0, 10.0), np.array([0.4388, 0.3419, 59.1000])), - (('10R', 8.0, 2.0), np.array([0.3301, 0.3237, 59.1000])), - (('10R', 8.0, 4.0), np.array([0.3621, 0.3349, 59.1000])), - (('10R', 8.0, 6.0), np.array([0.3910, 0.3442, 59.1000])), - (('10R', 8.0, 8.0), np.array([0.4212, 0.3526, 59.1000])), - (('10R', 8.0, 10.0), np.array([0.4490, 0.3589, 59.1000])), - (('2.5YR', 8.0, 2.0), np.array([0.3334, 0.3276, 59.1000])), - (('2.5YR', 8.0, 4.0), np.array([0.3667, 0.3429, 59.1000])), - (('2.5YR', 8.0, 6.0), np.array([0.3960, 0.3547, 59.1000])), - (('2.5YR', 8.0, 8.0), np.array([0.4275, 0.3662, 59.1000])), - (('2.5YR', 8.0, 10.0), np.array([0.4552, 0.3761, 59.1000])), - (('2.5YR', 8.0, 12.0), np.array([0.4852, 0.3847, 59.1000])), - (('5YR', 8.0, 2.0), np.array([0.3373, 0.3330, 59.1000])), - (('5YR', 8.0, 4.0), np.array([0.3690, 0.3510, 59.1000])), - (('5YR', 8.0, 6.0), np.array([0.3988, 0.3663, 59.1000])), - (('5YR', 8.0, 8.0), np.array([0.4310, 0.3820, 59.1000])), - (('5YR', 8.0, 10.0), np.array([0.4576, 0.3938, 59.1000])), - (('5YR', 8.0, 12.0), np.array([0.4849, 0.4050, 59.1000])), - (('5YR', 8.0, 14.0), np.array([0.5088, 0.4145, 59.1000])), - (('7.5YR', 8.0, 2.0), np.array([0.3395, 0.3379, 59.1000])), - (('7.5YR', 8.0, 4.0), np.array([0.3699, 0.3586, 59.1000])), - (('7.5YR', 8.0, 6.0), np.array([0.4000, 0.3770, 59.1000])), - (('7.5YR', 8.0, 8.0), np.array([0.4306, 0.3952, 59.1000])), - (('7.5YR', 8.0, 10.0), np.array([0.4568, 0.4100, 59.1000])), - (('7.5YR', 8.0, 12.0), np.array([0.4816, 0.4232, 59.1000])), - (('7.5YR', 8.0, 14.0), np.array([0.5025, 0.4338, 59.1000])), - (('7.5YR', 8.0, 16.0), np.array([0.5195, 0.4424, 59.1000])), - (('7.5YR', 8.0, 18.0), np.array([0.5316, 0.4480, 59.1000])), - (('7.5YR', 8.0, 20.0), np.array([0.5391, 0.4518, 59.1000])), - (('10YR', 8.0, 2.0), np.array([0.3407, 0.3434, 59.1000])), - (('10YR', 8.0, 4.0), np.array([0.3701, 0.3674, 59.1000])), - (('10YR', 8.0, 6.0), np.array([0.3994, 0.3896, 59.1000])), - (('10YR', 8.0, 8.0), np.array([0.4280, 0.4102, 59.1000])), - (('10YR', 8.0, 10.0), np.array([0.4527, 0.4268, 59.1000])), - (('10YR', 8.0, 12.0), np.array([0.4753, 0.4414, 59.1000])), - (('10YR', 8.0, 14.0), np.array([0.4940, 0.4530, 59.1000])), - (('10YR', 8.0, 16.0), np.array([0.5079, 0.4613, 59.1000])), - (('10YR', 8.0, 18.0), np.array([0.5179, 0.4670, 59.1000])), - (('10YR', 8.0, 20.0), np.array([0.5245, 0.4709, 59.1000])), - (('2.5Y', 8.0, 2.0), np.array([0.3406, 0.3484, 59.1000])), - (('2.5Y', 8.0, 4.0), np.array([0.3684, 0.3751, 59.1000])), - (('2.5Y', 8.0, 6.0), np.array([0.3969, 0.4009, 59.1000])), - (('2.5Y', 8.0, 8.0), np.array([0.4231, 0.4231, 59.1000])), - (('2.5Y', 8.0, 10.0), np.array([0.4469, 0.4423, 59.1000])), - (('2.5Y', 8.0, 12.0), np.array([0.4678, 0.4589, 59.1000])), - (('2.5Y', 8.0, 14.0), np.array([0.4842, 0.4712, 59.1000])), - (('2.5Y', 8.0, 16.0), np.array([0.4957, 0.4800, 59.1000])), - (('2.5Y', 8.0, 18.0), np.array([0.5033, 0.4855, 59.1000])), - (('2.5Y', 8.0, 20.0), np.array([0.5091, 0.4900, 59.1000])), - (('5Y', 8.0, 2.0), np.array([0.3394, 0.3518, 59.1000])), - (('5Y', 8.0, 4.0), np.array([0.3650, 0.3826, 59.1000])), - (('5Y', 8.0, 6.0), np.array([0.3913, 0.4117, 59.1000])), - (('5Y', 8.0, 8.0), np.array([0.4158, 0.4378, 59.1000])), - (('5Y', 8.0, 10.0), np.array([0.4376, 0.4601, 59.1000])), - (('5Y', 8.0, 12.0), np.array([0.4562, 0.4788, 59.1000])), - (('5Y', 8.0, 14.0), np.array([0.4699, 0.4920, 59.1000])), - (('5Y', 8.0, 16.0), np.array([0.4791, 0.5012, 59.1000])), - (('5Y', 8.0, 18.0), np.array([0.4847, 0.5069, 59.1000])), - (('7.5Y', 8.0, 2.0), np.array([0.3379, 0.3540, 59.1000])), - (('7.5Y', 8.0, 4.0), np.array([0.3622, 0.3861, 59.1000])), - (('7.5Y', 8.0, 6.0), np.array([0.3862, 0.4175, 59.1000])), - (('7.5Y', 8.0, 8.0), np.array([0.4088, 0.4466, 59.1000])), - (('7.5Y', 8.0, 10.0), np.array([0.4283, 0.4712, 59.1000])), - (('7.5Y', 8.0, 12.0), np.array([0.4455, 0.4917, 59.1000])), - (('7.5Y', 8.0, 14.0), np.array([0.4574, 0.5062, 59.1000])), - (('7.5Y', 8.0, 16.0), np.array([0.4658, 0.5158, 59.1000])), - (('7.5Y', 8.0, 18.0), np.array([0.4709, 0.5220, 59.1000])), - (('10Y', 8.0, 2.0), np.array([0.3359, 0.3552, 59.1000])), - (('10Y', 8.0, 4.0), np.array([0.3581, 0.3883, 59.1000])), - (('10Y', 8.0, 6.0), np.array([0.3803, 0.4216, 59.1000])), - (('10Y', 8.0, 8.0), np.array([0.4008, 0.4520, 59.1000])), - (('10Y', 8.0, 10.0), np.array([0.4190, 0.4791, 59.1000])), - (('10Y', 8.0, 12.0), np.array([0.4341, 0.5020, 59.1000])), - (('10Y', 8.0, 14.0), np.array([0.4450, 0.5181, 59.1000])), - (('10Y', 8.0, 16.0), np.array([0.4525, 0.5295, 59.1000])), - (('10Y', 8.0, 18.0), np.array([0.4570, 0.5366, 59.1000])), - (('2.5GY', 8.0, 2.0), np.array([0.3327, 0.3555, 59.1000])), - (('2.5GY', 8.0, 4.0), np.array([0.3504, 0.3887, 59.1000])), - (('2.5GY', 8.0, 6.0), np.array([0.3690, 0.4230, 59.1000])), - (('2.5GY', 8.0, 8.0), np.array([0.3858, 0.4550, 59.1000])), - (('2.5GY', 8.0, 10.0), np.array([0.4021, 0.4869, 59.1000])), - (('2.5GY', 8.0, 12.0), np.array([0.4154, 0.5133, 59.1000])), - (('2.5GY', 8.0, 14.0), np.array([0.4261, 0.5344, 59.1000])), - (('2.5GY', 8.0, 16.0), np.array([0.4327, 0.5475, 59.1000])), - (('2.5GY', 8.0, 18.0), np.array([0.4371, 0.5557, 59.1000])), - (('5GY', 8.0, 2.0), np.array([0.3284, 0.3542, 59.1000])), - (('5GY', 8.0, 4.0), np.array([0.3433, 0.3872, 59.1000])), - (('5GY', 8.0, 6.0), np.array([0.3573, 0.4214, 59.1000])), - (('5GY', 8.0, 8.0), np.array([0.3696, 0.4542, 59.1000])), - (('5GY', 8.0, 10.0), np.array([0.3816, 0.4879, 59.1000])), - (('5GY', 8.0, 12.0), np.array([0.3924, 0.5199, 59.1000])), - (('5GY', 8.0, 14.0), np.array([0.4011, 0.5468, 59.1000])), - (('5GY', 8.0, 16.0), np.array([0.4061, 0.5641, 59.1000])), - (('5GY', 8.0, 18.0), np.array([0.4104, 0.5785, 59.1000])), - (('5GY', 8.0, 20.0), np.array([0.4127, 0.5855, 59.1000])), - (('7.5GY', 8.0, 2.0), np.array([0.3194, 0.3502, 59.1000])), - (('7.5GY', 8.0, 4.0), np.array([0.3266, 0.3809, 59.1000])), - (('7.5GY', 8.0, 6.0), np.array([0.3339, 0.4129, 59.1000])), - (('7.5GY', 8.0, 8.0), np.array([0.3408, 0.4452, 59.1000])), - (('7.5GY', 8.0, 10.0), np.array([0.3463, 0.4791, 59.1000])), - (('7.5GY', 8.0, 12.0), np.array([0.3511, 0.5144, 59.1000])), - (('7.5GY', 8.0, 14.0), np.array([0.3546, 0.5490, 59.1000])), - (('7.5GY', 8.0, 16.0), np.array([0.3569, 0.5798, 59.1000])), - (('7.5GY', 8.0, 18.0), np.array([0.3585, 0.6063, 59.1000])), - (('7.5GY', 8.0, 20.0), np.array([0.3592, 0.6235, 59.1000])), - (('10GY', 8.0, 2.0), np.array([0.3121, 0.3459, 59.1000])), - (('10GY', 8.0, 4.0), np.array([0.3140, 0.3727, 59.1000])), - (('10GY', 8.0, 6.0), np.array([0.3150, 0.4014, 59.1000])), - (('10GY', 8.0, 8.0), np.array([0.3149, 0.4284, 59.1000])), - (('10GY', 8.0, 10.0), np.array([0.3140, 0.4601, 59.1000])), - (('10GY', 8.0, 12.0), np.array([0.3124, 0.4926, 59.1000])), - (('10GY', 8.0, 14.0), np.array([0.3091, 0.5247, 59.1000])), - (('10GY', 8.0, 16.0), np.array([0.3043, 0.5578, 59.1000])), - (('10GY', 8.0, 18.0), np.array([0.2987, 0.5919, 59.1000])), - (('10GY', 8.0, 20.0), np.array([0.2918, 0.6255, 59.1000])), - (('10GY', 8.0, 22.0), np.array([0.2846, 0.6564, 59.1000])), - (('10GY', 8.0, 24.0), np.array([0.2781, 0.6840, 59.1000])), - (('2.5G', 8.0, 2.0), np.array([0.3053, 0.3404, 59.1000])), - (('2.5G', 8.0, 4.0), np.array([0.3009, 0.3614, 59.1000])), - (('2.5G', 8.0, 6.0), np.array([0.2952, 0.3851, 59.1000])), - (('2.5G', 8.0, 8.0), np.array([0.2896, 0.4065, 59.1000])), - (('2.5G', 8.0, 10.0), np.array([0.2829, 0.4301, 59.1000])), - (('2.5G', 8.0, 12.0), np.array([0.2743, 0.4554, 59.1000])), - (('2.5G', 8.0, 14.0), np.array([0.2661, 0.4780, 59.1000])), - (('2.5G', 8.0, 16.0), np.array([0.2563, 0.5045, 59.1000])), - (('2.5G', 8.0, 18.0), np.array([0.2451, 0.5309, 59.1000])), - (('2.5G', 8.0, 20.0), np.array([0.2339, 0.5561, 59.1000])), - (('2.5G', 8.0, 22.0), np.array([0.2221, 0.5799, 59.1000])), - (('2.5G', 8.0, 24.0), np.array([0.2091, 0.6033, 59.1000])), - (('5G', 8.0, 2.0), np.array([0.3009, 0.3359, 59.1000])), - (('5G', 8.0, 4.0), np.array([0.2924, 0.3523, 59.1000])), - (('5G', 8.0, 6.0), np.array([0.2822, 0.3702, 59.1000])), - (('5G', 8.0, 8.0), np.array([0.2723, 0.3865, 59.1000])), - (('5G', 8.0, 10.0), np.array([0.2613, 0.4026, 59.1000])), - (('5G', 8.0, 12.0), np.array([0.2489, 0.4191, 59.1000])), - (('5G', 8.0, 14.0), np.array([0.2368, 0.4348, 59.1000])), - (('5G', 8.0, 16.0), np.array([0.2240, 0.4500, 59.1000])), - (('5G', 8.0, 18.0), np.array([0.2103, 0.4652, 59.1000])), - (('5G', 8.0, 20.0), np.array([0.1956, 0.4806, 59.1000])), - (('5G', 8.0, 22.0), np.array([0.1821, 0.4940, 59.1000])), - (('7.5G', 8.0, 2.0), np.array([0.2981, 0.3326, 59.1000])), - (('7.5G', 8.0, 4.0), np.array([0.2874, 0.3464, 59.1000])), - (('7.5G', 8.0, 6.0), np.array([0.2754, 0.3608, 59.1000])), - (('7.5G', 8.0, 8.0), np.array([0.2639, 0.3733, 59.1000])), - (('7.5G', 8.0, 10.0), np.array([0.2515, 0.3867, 59.1000])), - (('7.5G', 8.0, 12.0), np.array([0.2380, 0.4002, 59.1000])), - (('7.5G', 8.0, 14.0), np.array([0.2254, 0.4125, 59.1000])), - (('7.5G', 8.0, 16.0), np.array([0.2120, 0.4252, 59.1000])), - (('7.5G', 8.0, 18.0), np.array([0.1980, 0.4372, 59.1000])), - (('7.5G', 8.0, 20.0), np.array([0.1845, 0.4492, 59.1000])), - (('10G', 8.0, 2.0), np.array([0.2957, 0.3293, 59.1000])), - (('10G', 8.0, 4.0), np.array([0.2828, 0.3403, 59.1000])), - (('10G', 8.0, 6.0), np.array([0.2693, 0.3512, 59.1000])), - (('10G', 8.0, 8.0), np.array([0.2564, 0.3611, 59.1000])), - (('10G', 8.0, 10.0), np.array([0.2430, 0.3710, 59.1000])), - (('10G', 8.0, 12.0), np.array([0.2282, 0.3811, 59.1000])), - (('10G', 8.0, 14.0), np.array([0.2148, 0.3903, 59.1000])), - (('10G', 8.0, 16.0), np.array([0.2012, 0.3992, 59.1000])), - (('10G', 8.0, 18.0), np.array([0.1866, 0.4086, 59.1000])), - (('10G', 8.0, 20.0), np.array([0.1734, 0.4164, 59.1000])), - (('2.5BG', 8.0, 2.0), np.array([0.2940, 0.3268, 59.1000])), - (('2.5BG', 8.0, 4.0), np.array([0.2791, 0.3351, 59.1000])), - (('2.5BG', 8.0, 6.0), np.array([0.2647, 0.3429, 59.1000])), - (('2.5BG', 8.0, 8.0), np.array([0.2500, 0.3500, 59.1000])), - (('2.5BG', 8.0, 10.0), np.array([0.2352, 0.3566, 59.1000])), - (('2.5BG', 8.0, 12.0), np.array([0.2196, 0.3630, 59.1000])), - (('2.5BG', 8.0, 14.0), np.array([0.2057, 0.3681, 59.1000])), - (('2.5BG', 8.0, 16.0), np.array([0.1915, 0.3732, 59.1000])), - (('2.5BG', 8.0, 18.0), np.array([0.1759, 0.3782, 59.1000])), - (('5BG', 8.0, 2.0), np.array([0.2919, 0.3228, 59.1000])), - (('5BG', 8.0, 4.0), np.array([0.2752, 0.3278, 59.1000])), - (('5BG', 8.0, 6.0), np.array([0.2588, 0.3318, 59.1000])), - (('5BG', 8.0, 8.0), np.array([0.2419, 0.3352, 59.1000])), - (('5BG', 8.0, 10.0), np.array([0.2264, 0.3383, 59.1000])), - (('5BG', 8.0, 12.0), np.array([0.2101, 0.3412, 59.1000])), - (('5BG', 8.0, 14.0), np.array([0.1958, 0.3432, 59.1000])), - (('5BG', 8.0, 16.0), np.array([0.1814, 0.3450, 59.1000])), - (('7.5BG', 8.0, 2.0), np.array([0.2900, 0.3183, 59.1000])), - (('7.5BG', 8.0, 4.0), np.array([0.2718, 0.3200, 59.1000])), - (('7.5BG', 8.0, 6.0), np.array([0.2525, 0.3198, 59.1000])), - (('7.5BG', 8.0, 8.0), np.array([0.2352, 0.3198, 59.1000])), - (('7.5BG', 8.0, 10.0), np.array([0.2184, 0.3196, 59.1000])), - (('7.5BG', 8.0, 12.0), np.array([0.2010, 0.3188, 59.1000])), - (('7.5BG', 8.0, 14.0), np.array([0.1868, 0.3179, 59.1000])), - (('7.5BG', 8.0, 16.0), np.array([0.1721, 0.3168, 59.1000])), - (('10BG', 8.0, 2.0), np.array([0.2894, 0.3152, 59.1000])), - (('10BG', 8.0, 4.0), np.array([0.2686, 0.3130, 59.1000])), - (('10BG', 8.0, 6.0), np.array([0.2489, 0.3099, 59.1000])), - (('10BG', 8.0, 8.0), np.array([0.2302, 0.3063, 59.1000])), - (('10BG', 8.0, 10.0), np.array([0.2120, 0.3025, 59.1000])), - (('10BG', 8.0, 12.0), np.array([0.1937, 0.2978, 59.1000])), - (('10BG', 8.0, 14.0), np.array([0.1788, 0.2936, 59.1000])), - (('2.5B', 8.0, 2.0), np.array([0.2897, 0.3124, 59.1000])), - (('2.5B', 8.0, 4.0), np.array([0.2668, 0.3067, 59.1000])), - (('2.5B', 8.0, 6.0), np.array([0.2462, 0.3000, 59.1000])), - (('2.5B', 8.0, 8.0), np.array([0.2264, 0.2923, 59.1000])), - (('2.5B', 8.0, 10.0), np.array([0.2066, 0.2839, 59.1000])), - (('2.5B', 8.0, 12.0), np.array([0.1877, 0.2752, 59.1000])), - (('5B', 8.0, 2.0), np.array([0.2908, 0.3096, 59.1000])), - (('5B', 8.0, 4.0), np.array([0.2671, 0.2998, 59.1000])), - (('5B', 8.0, 6.0), np.array([0.2457, 0.2888, 59.1000])), - (('5B', 8.0, 8.0), np.array([0.2237, 0.2761, 59.1000])), - (('7.5B', 8.0, 2.0), np.array([0.2922, 0.3077, 59.1000])), - (('7.5B', 8.0, 4.0), np.array([0.2688, 0.2956, 59.1000])), - (('7.5B', 8.0, 6.0), np.array([0.2472, 0.2821, 59.1000])), - (('7.5B', 8.0, 8.0), np.array([0.2252, 0.2668, 59.1000])), - (('10B', 8.0, 2.0), np.array([0.2935, 0.3062, 59.1000])), - (('10B', 8.0, 4.0), np.array([0.2718, 0.2911, 59.1000])), - (('10B', 8.0, 6.0), np.array([0.2512, 0.2760, 59.1000])), - (('10B', 8.0, 8.0), np.array([0.2294, 0.2587, 59.1000])), - (('2.5PB', 8.0, 2.0), np.array([0.2957, 0.3047, 59.1000])), - (('2.5PB', 8.0, 4.0), np.array([0.2758, 0.2879, 59.1000])), - (('2.5PB', 8.0, 6.0), np.array([0.2562, 0.2709, 59.1000])), - (('5PB', 8.0, 2.0), np.array([0.2974, 0.3039, 59.1000])), - (('5PB', 8.0, 4.0), np.array([0.2798, 0.2861, 59.1000])), - (('5PB', 8.0, 6.0), np.array([0.2614, 0.2670, 59.1000])), - (('7.5PB', 8.0, 2.0), np.array([0.3003, 0.3034, 59.1000])), - (('7.5PB', 8.0, 4.0), np.array([0.2856, 0.2846, 59.1000])), - (('7.5PB', 8.0, 6.0), np.array([0.2702, 0.2648, 59.1000])), - (('10PB', 8.0, 2.0), np.array([0.3027, 0.3035, 59.1000])), - (('10PB', 8.0, 4.0), np.array([0.2911, 0.2848, 59.1000])), - (('10PB', 8.0, 6.0), np.array([0.2792, 0.2649, 59.1000])), - (('10PB', 8.0, 8.0), np.array([0.2677, 0.2443, 59.1000])), - (('2.5P', 8.0, 2.0), np.array([0.3048, 0.3040, 59.1000])), - (('2.5P', 8.0, 4.0), np.array([0.2962, 0.2850, 59.1000])), - (('2.5P', 8.0, 6.0), np.array([0.2881, 0.2671, 59.1000])), - (('2.5P', 8.0, 8.0), np.array([0.2800, 0.2488, 59.1000])), - (('5P', 8.0, 2.0), np.array([0.3065, 0.3047, 59.1000])), - (('5P', 8.0, 4.0), np.array([0.3012, 0.2868, 59.1000])), - (('5P', 8.0, 6.0), np.array([0.2963, 0.2704, 59.1000])), - (('5P', 8.0, 8.0), np.array([0.2914, 0.2534, 59.1000])), - (('5P', 8.0, 10.0), np.array([0.2870, 0.2380, 59.1000])), - (('7.5P', 8.0, 2.0), np.array([0.3107, 0.3070, 59.1000])), - (('7.5P', 8.0, 4.0), np.array([0.3114, 0.2915, 59.1000])), - (('7.5P', 8.0, 6.0), np.array([0.3114, 0.2785, 59.1000])), - (('7.5P', 8.0, 8.0), np.array([0.3116, 0.2626, 59.1000])), - (('7.5P', 8.0, 10.0), np.array([0.3116, 0.2497, 59.1000])), - (('7.5P', 8.0, 12.0), np.array([0.3117, 0.2370, 59.1000])), - (('10P', 8.0, 2.0), np.array([0.3131, 0.3084, 59.1000])), - (('10P', 8.0, 4.0), np.array([0.3175, 0.2955, 59.1000])), - (('10P', 8.0, 6.0), np.array([0.3213, 0.2829, 59.1000])), - (('10P', 8.0, 8.0), np.array([0.3250, 0.2700, 59.1000])), - (('10P', 8.0, 10.0), np.array([0.3282, 0.2582, 59.1000])), - (('10P', 8.0, 12.0), np.array([0.3312, 0.2470, 59.1000])), - (('10P', 8.0, 14.0), np.array([0.3342, 0.2349, 59.1000])), - (('2.5RP', 8.0, 2.0), np.array([0.3154, 0.3100, 59.1000])), - (('2.5RP', 8.0, 4.0), np.array([0.3239, 0.3000, 59.1000])), - (('2.5RP', 8.0, 6.0), np.array([0.3327, 0.2898, 59.1000])), - (('2.5RP', 8.0, 8.0), np.array([0.3406, 0.2793, 59.1000])), - (('2.5RP', 8.0, 10.0), np.array([0.3479, 0.2699, 59.1000])), - (('2.5RP', 8.0, 12.0), np.array([0.3552, 0.2594, 59.1000])), - (('2.5RP', 8.0, 14.0), np.array([0.3621, 0.2496, 59.1000])), - (('5RP', 8.0, 2.0), np.array([0.3180, 0.3120, 59.1000])), - (('5RP', 8.0, 4.0), np.array([0.3308, 0.3052, 59.1000])), - (('5RP', 8.0, 6.0), np.array([0.3440, 0.2978, 59.1000])), - (('5RP', 8.0, 8.0), np.array([0.3570, 0.2900, 59.1000])), - (('5RP', 8.0, 10.0), np.array([0.3685, 0.2828, 59.1000])), - (('5RP', 8.0, 12.0), np.array([0.3818, 0.2742, 59.1000])), - (('7.5RP', 8.0, 2.0), np.array([0.3200, 0.3136, 59.1000])), - (('7.5RP', 8.0, 4.0), np.array([0.3360, 0.3092, 59.1000])), - (('7.5RP', 8.0, 6.0), np.array([0.3521, 0.3042, 59.1000])), - (('7.5RP', 8.0, 8.0), np.array([0.3682, 0.2983, 59.1000])), - (('7.5RP', 8.0, 10.0), np.array([0.3830, 0.2930, 59.1000])), - (('7.5RP', 8.0, 12.0), np.array([0.4002, 0.2859, 59.1000])), - (('10RP', 9.0, 2.0), np.array([0.3205, 0.3155, 78.6600])), - (('10RP', 9.0, 4.0), np.array([0.3400, 0.3140, 78.6600])), - (('10RP', 9.0, 6.0), np.array([0.3590, 0.3118, 78.6600])), - (('2.5R', 9.0, 2.0), np.array([0.3210, 0.3168, 78.6600])), - (('2.5R', 9.0, 4.0), np.array([0.3445, 0.3179, 78.6600])), - (('2.5R', 9.0, 6.0), np.array([0.3665, 0.3183, 78.6600])), - (('5R', 9.0, 2.0), np.array([0.3240, 0.3188, 78.6600])), - (('5R', 9.0, 4.0), np.array([0.3495, 0.3226, 78.6600])), - (('5R', 9.0, 6.0), np.array([0.3734, 0.3256, 78.6600])), - (('7.5R', 9.0, 2.0), np.array([0.3263, 0.3210, 78.6600])), - (('7.5R', 9.0, 4.0), np.array([0.3551, 0.3283, 78.6600])), - (('7.5R', 9.0, 6.0), np.array([0.3812, 0.3348, 78.6600])), - (('10R', 9.0, 2.0), np.array([0.3284, 0.3233, 78.6600])), - (('10R', 9.0, 4.0), np.array([0.3600, 0.3348, 78.6600])), - (('10R', 9.0, 6.0), np.array([0.3880, 0.3439, 78.6600])), - (('2.5YR', 9.0, 2.0), np.array([0.3320, 0.3273, 78.6600])), - (('2.5YR', 9.0, 4.0), np.array([0.3641, 0.3422, 78.6600])), - (('2.5YR', 9.0, 6.0), np.array([0.3927, 0.3550, 78.6600])), - (('5YR', 9.0, 2.0), np.array([0.3353, 0.3325, 78.6600])), - (('5YR', 9.0, 4.0), np.array([0.3668, 0.3509, 78.6600])), - (('5YR', 9.0, 6.0), np.array([0.3948, 0.3659, 78.6600])), - (('7.5YR', 9.0, 2.0), np.array([0.3380, 0.3377, 78.6600])), - (('7.5YR', 9.0, 4.0), np.array([0.3679, 0.3585, 78.6600])), - (('7.5YR', 9.0, 6.0), np.array([0.3950, 0.3763, 78.6600])), - (('7.5YR', 9.0, 8.0), np.array([0.4220, 0.3930, 78.6600])), - (('10YR', 9.0, 2.0), np.array([0.3392, 0.3430, 78.6600])), - (('10YR', 9.0, 4.0), np.array([0.3677, 0.3668, 78.6600])), - (('10YR', 9.0, 6.0), np.array([0.3941, 0.3877, 78.6600])), - (('10YR', 9.0, 8.0), np.array([0.4199, 0.4069, 78.6600])), - (('2.5Y', 9.0, 2.0), np.array([0.3390, 0.3472, 78.6600])), - (('2.5Y', 9.0, 4.0), np.array([0.3655, 0.3738, 78.6600])), - (('2.5Y', 9.0, 6.0), np.array([0.3910, 0.3972, 78.6600])), - (('2.5Y', 9.0, 8.0), np.array([0.4154, 0.4186, 78.6600])), - (('2.5Y', 9.0, 10.0), np.array([0.4370, 0.4369, 78.6600])), - (('2.5Y', 9.0, 12.0), np.array([0.4569, 0.4527, 78.6600])), - (('5Y', 9.0, 2.0), np.array([0.3378, 0.3504, 78.6600])), - (('5Y', 9.0, 4.0), np.array([0.3621, 0.3799, 78.6600])), - (('5Y', 9.0, 6.0), np.array([0.3858, 0.4071, 78.6600])), - (('5Y', 9.0, 8.0), np.array([0.4080, 0.4319, 78.6600])), - (('5Y', 9.0, 10.0), np.array([0.4275, 0.4529, 78.6600])), - (('5Y', 9.0, 12.0), np.array([0.4455, 0.4719, 78.6600])), - (('5Y', 9.0, 14.0), np.array([0.4602, 0.4869, 78.6600])), - (('5Y', 9.0, 16.0), np.array([0.4711, 0.4977, 78.6600])), - (('5Y', 9.0, 18.0), np.array([0.4782, 0.5049, 78.6600])), - (('5Y', 9.0, 20.0), np.array([0.4830, 0.5092, 78.6600])), - (('7.5Y', 9.0, 2.0), np.array([0.3365, 0.3527, 78.6600])), - (('7.5Y', 9.0, 4.0), np.array([0.3591, 0.3832, 78.6600])), - (('7.5Y', 9.0, 6.0), np.array([0.3811, 0.4123, 78.6600])), - (('7.5Y', 9.0, 8.0), np.array([0.4019, 0.4392, 78.6600])), - (('7.5Y', 9.0, 10.0), np.array([0.4201, 0.4622, 78.6600])), - (('7.5Y', 9.0, 12.0), np.array([0.4369, 0.4829, 78.6600])), - (('7.5Y', 9.0, 14.0), np.array([0.4503, 0.4993, 78.6600])), - (('7.5Y', 9.0, 16.0), np.array([0.4595, 0.5104, 78.6600])), - (('7.5Y', 9.0, 18.0), np.array([0.4663, 0.5188, 78.6600])), - (('10Y', 9.0, 2.0), np.array([0.3349, 0.3537, 78.6600])), - (('10Y', 9.0, 4.0), np.array([0.3558, 0.3852, 78.6600])), - (('10Y', 9.0, 6.0), np.array([0.3761, 0.4155, 78.6600])), - (('10Y', 9.0, 8.0), np.array([0.3957, 0.4450, 78.6600])), - (('10Y', 9.0, 10.0), np.array([0.4120, 0.4694, 78.6600])), - (('10Y', 9.0, 12.0), np.array([0.4271, 0.4920, 78.6600])), - (('10Y', 9.0, 14.0), np.array([0.4393, 0.5101, 78.6600])), - (('10Y', 9.0, 16.0), np.array([0.4477, 0.5225, 78.6600])), - (('10Y', 9.0, 18.0), np.array([0.4540, 0.5320, 78.6600])), - (('2.5GY', 9.0, 2.0), np.array([0.3321, 0.3539, 78.6600])), - (('2.5GY', 9.0, 4.0), np.array([0.3499, 0.3866, 78.6600])), - (('2.5GY', 9.0, 6.0), np.array([0.3670, 0.4178, 78.6600])), - (('2.5GY', 9.0, 8.0), np.array([0.3834, 0.4490, 78.6600])), - (('2.5GY', 9.0, 10.0), np.array([0.3973, 0.4761, 78.6600])), - (('2.5GY', 9.0, 12.0), np.array([0.4108, 0.5028, 78.6600])), - (('2.5GY', 9.0, 14.0), np.array([0.4212, 0.5237, 78.6600])), - (('2.5GY', 9.0, 16.0), np.array([0.4288, 0.5383, 78.6600])), - (('2.5GY', 9.0, 18.0), np.array([0.4354, 0.5508, 78.6600])), - (('5GY', 9.0, 2.0), np.array([0.3284, 0.3534, 78.6600])), - (('5GY', 9.0, 4.0), np.array([0.3437, 0.3861, 78.6600])), - (('5GY', 9.0, 6.0), np.array([0.3572, 0.4179, 78.6600])), - (('5GY', 9.0, 8.0), np.array([0.3698, 0.4497, 78.6600])), - (('5GY', 9.0, 10.0), np.array([0.3810, 0.4791, 78.6600])), - (('5GY', 9.0, 12.0), np.array([0.3911, 0.5082, 78.6600])), - (('5GY', 9.0, 14.0), np.array([0.3993, 0.5329, 78.6600])), - (('5GY', 9.0, 16.0), np.array([0.4058, 0.5541, 78.6600])), - (('5GY', 9.0, 18.0), np.array([0.4108, 0.5699, 78.6600])), - (('7.5GY', 9.0, 2.0), np.array([0.3198, 0.3500, 78.6600])), - (('7.5GY', 9.0, 4.0), np.array([0.3274, 0.3793, 78.6600])), - (('7.5GY', 9.0, 6.0), np.array([0.3351, 0.4111, 78.6600])), - (('7.5GY', 9.0, 8.0), np.array([0.3414, 0.4415, 78.6600])), - (('7.5GY', 9.0, 10.0), np.array([0.3471, 0.4735, 78.6600])), - (('7.5GY', 9.0, 12.0), np.array([0.3518, 0.5042, 78.6600])), - (('7.5GY', 9.0, 14.0), np.array([0.3551, 0.5339, 78.6600])), - (('7.5GY', 9.0, 16.0), np.array([0.3581, 0.5654, 78.6600])), - (('7.5GY', 9.0, 18.0), np.array([0.3602, 0.5920, 78.6600])), - (('10GY', 9.0, 2.0), np.array([0.3124, 0.3454, 78.6600])), - (('10GY', 9.0, 4.0), np.array([0.3144, 0.3711, 78.6600])), - (('10GY', 9.0, 6.0), np.array([0.3153, 0.4008, 78.6600])), - (('10GY', 9.0, 8.0), np.array([0.3157, 0.4259, 78.6600])), - (('10GY', 9.0, 10.0), np.array([0.3155, 0.4558, 78.6600])), - (('10GY', 9.0, 12.0), np.array([0.3139, 0.4829, 78.6600])), - (('10GY', 9.0, 14.0), np.array([0.3115, 0.5129, 78.6600])), - (('10GY', 9.0, 16.0), np.array([0.3079, 0.5440, 78.6600])), - (('10GY', 9.0, 18.0), np.array([0.3032, 0.5748, 78.6600])), - (('2.5G', 9.0, 2.0), np.array([0.3058, 0.3400, 78.6600])), - (('2.5G', 9.0, 4.0), np.array([0.3018, 0.3606, 78.6600])), - (('2.5G', 9.0, 6.0), np.array([0.2966, 0.3846, 78.6600])), - (('2.5G', 9.0, 8.0), np.array([0.2912, 0.4054, 78.6600])), - (('2.5G', 9.0, 10.0), np.array([0.2851, 0.4275, 78.6600])), - (('2.5G', 9.0, 12.0), np.array([0.2786, 0.4491, 78.6600])), - (('2.5G', 9.0, 14.0), np.array([0.2711, 0.4726, 78.6600])), - (('2.5G', 9.0, 16.0), np.array([0.2630, 0.4966, 78.6600])), - (('5G', 9.0, 2.0), np.array([0.3017, 0.3357, 78.6600])), - (('5G', 9.0, 4.0), np.array([0.2933, 0.3519, 78.6600])), - (('5G', 9.0, 6.0), np.array([0.2832, 0.3697, 78.6600])), - (('5G', 9.0, 8.0), np.array([0.2735, 0.3854, 78.6600])), - (('5G', 9.0, 10.0), np.array([0.2639, 0.4001, 78.6600])), - (('5G', 9.0, 12.0), np.array([0.2528, 0.4160, 78.6600])), - (('7.5G', 9.0, 2.0), np.array([0.2987, 0.3323, 78.6600])), - (('7.5G', 9.0, 4.0), np.array([0.2882, 0.3461, 78.6600])), - (('7.5G', 9.0, 6.0), np.array([0.2763, 0.3607, 78.6600])), - (('7.5G', 9.0, 8.0), np.array([0.2652, 0.3738, 78.6600])), - (('7.5G', 9.0, 10.0), np.array([0.2545, 0.3855, 78.6600])), - (('7.5G', 9.0, 12.0), np.array([0.2419, 0.3985, 78.6600])), - (('10G', 9.0, 2.0), np.array([0.2965, 0.3293, 78.6600])), - (('10G', 9.0, 4.0), np.array([0.2840, 0.3402, 78.6600])), - (('10G', 9.0, 6.0), np.array([0.2703, 0.3513, 78.6600])), - (('10G', 9.0, 8.0), np.array([0.2574, 0.3618, 78.6600])), - (('10G', 9.0, 10.0), np.array([0.2457, 0.3702, 78.6600])), - (('10G', 9.0, 12.0), np.array([0.2325, 0.3796, 78.6600])), - (('2.5BG', 9.0, 2.0), np.array([0.2947, 0.3267, 78.6600])), - (('2.5BG', 9.0, 4.0), np.array([0.2805, 0.3349, 78.6600])), - (('2.5BG', 9.0, 6.0), np.array([0.2652, 0.3433, 78.6600])), - (('2.5BG', 9.0, 8.0), np.array([0.2509, 0.3507, 78.6600])), - (('2.5BG', 9.0, 10.0), np.array([0.2382, 0.3568, 78.6600])), - (('5BG', 9.0, 2.0), np.array([0.2930, 0.3232, 78.6600])), - (('5BG', 9.0, 4.0), np.array([0.2768, 0.3287, 78.6600])), - (('5BG', 9.0, 6.0), np.array([0.2599, 0.3338, 78.6600])), - (('5BG', 9.0, 8.0), np.array([0.2437, 0.3378, 78.6600])), - (('5BG', 9.0, 10.0), np.array([0.2301, 0.3405, 78.6600])), - (('7.5BG', 9.0, 2.0), np.array([0.2911, 0.3188, 78.6600])), - (('7.5BG', 9.0, 4.0), np.array([0.2728, 0.3208, 78.6600])), - (('7.5BG', 9.0, 6.0), np.array([0.2543, 0.3220, 78.6600])), - (('7.5BG', 9.0, 8.0), np.array([0.2361, 0.3225, 78.6600])), - (('7.5BG', 9.0, 10.0), np.array([0.2215, 0.3226, 78.6600])), - (('10BG', 9.0, 2.0), np.array([0.2907, 0.3159, 78.6600])), - (('10BG', 9.0, 4.0), np.array([0.2700, 0.3140, 78.6600])), - (('10BG', 9.0, 6.0), np.array([0.2501, 0.3118, 78.6600])), - (('2.5B', 9.0, 2.0), np.array([0.2909, 0.3125, 78.6600])), - (('2.5B', 9.0, 4.0), np.array([0.2680, 0.3073, 78.6600])), - (('5B', 9.0, 2.0), np.array([0.2919, 0.3102, 78.6600])), - (('5B', 9.0, 4.0), np.array([0.2675, 0.3005, 78.6600])), - (('7.5B', 9.0, 2.0), np.array([0.2937, 0.3087, 78.6600])), - (('7.5B', 9.0, 4.0), np.array([0.2688, 0.2961, 78.6600])), - (('10B', 9.0, 2.0), np.array([0.2949, 0.3076, 78.6600])), - (('10B', 9.0, 4.0), np.array([0.2712, 0.2924, 78.6600])), - (('2.5PB', 9.0, 2.0), np.array([0.2975, 0.3063, 78.6600])), - (('5PB', 9.0, 2.0), np.array([0.2991, 0.3057, 78.6600])), - (('7.5PB', 9.0, 2.0), np.array([0.3015, 0.3052, 78.6600])), - (('10PB', 9.0, 2.0), np.array([0.3038, 0.3054, 78.6600])), - (('10PB', 9.0, 4.0), np.array([0.2910, 0.2850, 78.6600])), - (('2.5P', 9.0, 2.0), np.array([0.3050, 0.3051, 78.6600])), - (('2.5P', 9.0, 4.0), np.array([0.2963, 0.2865, 78.6600])), - (('5P', 9.0, 2.0), np.array([0.3067, 0.3060, 78.6600])), - (('5P', 9.0, 4.0), np.array([0.3003, 0.2870, 78.6600])), - (('7.5P', 9.0, 2.0), np.array([0.3107, 0.3081, 78.6600])), - (('7.5P', 9.0, 4.0), np.array([0.3117, 0.2928, 78.6600])), - (('7.5P', 9.0, 6.0), np.array([0.3120, 0.2788, 78.6600])), - (('10P', 9.0, 2.0), np.array([0.3128, 0.3094, 78.6600])), - (('10P', 9.0, 4.0), np.array([0.3176, 0.2966, 78.6600])), - (('10P', 9.0, 6.0), np.array([0.3218, 0.2845, 78.6600])), - (('2.5RP', 9.0, 2.0), np.array([0.3149, 0.3108, 78.6600])), - (('2.5RP', 9.0, 4.0), np.array([0.3234, 0.3010, 78.6600])), - (('2.5RP', 9.0, 6.0), np.array([0.3322, 0.2910, 78.6600])), - (('5RP', 9.0, 2.0), np.array([0.3172, 0.3126, 78.6600])), - (('5RP', 9.0, 4.0), np.array([0.3301, 0.3060, 78.6600])), - (('5RP', 9.0, 6.0), np.array([0.3431, 0.2988, 78.6600])), - (('7.5RP', 9.0, 2.0), np.array([0.3190, 0.3141, 78.6600])), - (('7.5RP', 9.0, 4.0), np.array([0.3350, 0.3099, 78.6600])), - (('7.5RP', 9.0, 6.0), np.array([0.3512, 0.3052, 78.6600]))) +MUNSELL_COLOURS_REAL: Tuple = ( + (("10RP", 1.0, 2.0), np.array([0.3629, 0.2710, 1.2100])), + (("10RP", 1.0, 4.0), np.array([0.3920, 0.2423, 1.2100])), + (("10RP", 1.0, 6.0), np.array([0.4151, 0.2169, 1.2100])), + (("10RP", 1.0, 8.0), np.array([0.4357, 0.1921, 1.2100])), + (("10RP", 1.0, 10.0), np.array([0.4521, 0.1710, 1.2100])), + (("10RP", 1.0, 12.0), np.array([0.4668, 0.1514, 1.2100])), + (("2.5R", 1.0, 2.0), np.array([0.3768, 0.2816, 1.2100])), + (("2.5R", 1.0, 4.0), np.array([0.4166, 0.2569, 1.2100])), + (("2.5R", 1.0, 6.0), np.array([0.4515, 0.2329, 1.2100])), + (("2.5R", 1.0, 8.0), np.array([0.4812, 0.2103, 1.2100])), + (("2.5R", 1.0, 10.0), np.array([0.5058, 0.1900, 1.2100])), + (("5R", 1.0, 2.0), np.array([0.3908, 0.2929, 1.2100])), + (("5R", 1.0, 4.0), np.array([0.4420, 0.2728, 1.2100])), + (("5R", 1.0, 6.0), np.array([0.4885, 0.2515, 1.2100])), + (("5R", 1.0, 8.0), np.array([0.5282, 0.2297, 1.2100])), + (("5R", 1.0, 10.0), np.array([0.5604, 0.2100, 1.2100])), + (("7.5R", 1.0, 2.0), np.array([0.4020, 0.3034, 1.2100])), + (("7.5R", 1.0, 4.0), np.array([0.4660, 0.2888, 1.2100])), + (("7.5R", 1.0, 6.0), np.array([0.5235, 0.2698, 1.2100])), + (("7.5R", 1.0, 8.0), np.array([0.5722, 0.2487, 1.2100])), + (("7.5R", 1.0, 10.0), np.array([0.6111, 0.2290, 1.2100])), + (("10R", 1.0, 2.0), np.array([0.4128, 0.3154, 1.2100])), + (("10R", 1.0, 4.0), np.array([0.4933, 0.3068, 1.2100])), + (("10R", 1.0, 6.0), np.array([0.5584, 0.2921, 1.2100])), + (("10R", 1.0, 8.0), np.array([0.6178, 0.2713, 1.2100])), + (("10R", 1.0, 10.0), np.array([0.6661, 0.2499, 1.2100])), + (("2.5YR", 1.0, 2.0), np.array([0.4258, 0.3344, 1.2100])), + (("2.5YR", 1.0, 4.0), np.array([0.5311, 0.3371, 1.2100])), + (("2.5YR", 1.0, 6.0), np.array([0.6048, 0.3270, 1.2100])), + (("2.5YR", 1.0, 8.0), np.array([0.6721, 0.3058, 1.2100])), + (("5YR", 1.0, 2.0), np.array([0.4377, 0.3580, 1.2100])), + (("5YR", 1.0, 4.0), np.array([0.5660, 0.3795, 1.2100])), + (("7.5YR", 1.0, 2.0), np.array([0.4430, 0.3775, 1.2100])), + (("10YR", 1.0, 2.0), np.array([0.4446, 0.3982, 1.2100])), + (("2.5Y", 1.0, 2.0), np.array([0.4362, 0.4177, 1.2100])), + (("5Y", 1.0, 2.0), np.array([0.4230, 0.4265, 1.2100])), + (("7.5Y", 1.0, 2.0), np.array([0.4042, 0.4287, 1.2100])), + (("10Y", 1.0, 2.0), np.array([0.3802, 0.4212, 1.2100])), + (("2.5GY", 1.0, 2.0), np.array([0.3540, 0.4088, 1.2100])), + (("5GY", 1.0, 2.0), np.array([0.3359, 0.3982, 1.2100])), + (("5GY", 1.0, 4.0), np.array([0.3765, 0.5942, 1.2100])), + (("7.5GY", 1.0, 2.0), np.array([0.3154, 0.3840, 1.2100])), + (("7.5GY", 1.0, 4.0), np.array([0.3133, 0.5380, 1.2100])), + (("10GY", 1.0, 2.0), np.array([0.3006, 0.3720, 1.2100])), + (("10GY", 1.0, 4.0), np.array([0.2722, 0.4903, 1.2100])), + (("10GY", 1.0, 6.0), np.array([0.2232, 0.6392, 1.2100])), + (("2.5G", 1.0, 2.0), np.array([0.2910, 0.3634, 1.2100])), + (("2.5G", 1.0, 4.0), np.array([0.2454, 0.4489, 1.2100])), + (("2.5G", 1.0, 6.0), np.array([0.1711, 0.5619, 1.2100])), + (("2.5G", 1.0, 8.0), np.array([0.0620, 0.6896, 1.2100])), + (("5G", 1.0, 2.0), np.array([0.2833, 0.3564, 1.2100])), + (("5G", 1.0, 4.0), np.array([0.2290, 0.4218, 1.2100])), + (("5G", 1.0, 6.0), np.array([0.1468, 0.4996, 1.2100])), + (("5G", 1.0, 8.0), np.array([0.0559, 0.5710, 1.2100])), + (("7.5G", 1.0, 2.0), np.array([0.2758, 0.3484, 1.2100])), + (("7.5G", 1.0, 4.0), np.array([0.2159, 0.3967, 1.2100])), + (("7.5G", 1.0, 6.0), np.array([0.1344, 0.4505, 1.2100])), + (("7.5G", 1.0, 8.0), np.array([0.0530, 0.4943, 1.2100])), + (("10G", 1.0, 2.0), np.array([0.2689, 0.3407, 1.2100])), + (("10G", 1.0, 4.0), np.array([0.2040, 0.3724, 1.2100])), + (("10G", 1.0, 6.0), np.array([0.1249, 0.4019, 1.2100])), + (("10G", 1.0, 8.0), np.array([0.0511, 0.4158, 1.2100])), + (("2.5BG", 1.0, 2.0), np.array([0.2600, 0.3289, 1.2100])), + (("2.5BG", 1.0, 4.0), np.array([0.1883, 0.3406, 1.2100])), + (("2.5BG", 1.0, 6.0), np.array([0.1169, 0.3452, 1.2100])), + (("2.5BG", 1.0, 8.0), np.array([0.0476, 0.3458, 1.2100])), + (("5BG", 1.0, 2.0), np.array([0.2500, 0.3141, 1.2100])), + (("5BG", 1.0, 4.0), np.array([0.1753, 0.3021, 1.2100])), + (("5BG", 1.0, 6.0), np.array([0.1093, 0.2860, 1.2100])), + (("7.5BG", 1.0, 2.0), np.array([0.2430, 0.3023, 1.2100])), + (("7.5BG", 1.0, 4.0), np.array([0.1702, 0.2768, 1.2100])), + (("7.5BG", 1.0, 6.0), np.array([0.1059, 0.2485, 1.2100])), + (("10BG", 1.0, 2.0), np.array([0.2362, 0.2882, 1.2100])), + (("10BG", 1.0, 4.0), np.array([0.1658, 0.2496, 1.2100])), + (("10BG", 1.0, 6.0), np.array([0.1074, 0.2129, 1.2100])), + (("2.5B", 1.0, 2.0), np.array([0.2322, 0.2781, 1.2100])), + (("2.5B", 1.0, 4.0), np.array([0.1649, 0.2324, 1.2100])), + (("2.5B", 1.0, 6.0), np.array([0.1118, 0.1908, 1.2100])), + (("5B", 1.0, 2.0), np.array([0.2291, 0.2677, 1.2100])), + (("5B", 1.0, 4.0), np.array([0.1667, 0.2168, 1.2100])), + (("5B", 1.0, 6.0), np.array([0.1212, 0.1745, 1.2100])), + (("7.5B", 1.0, 2.0), np.array([0.2291, 0.2579, 1.2100])), + (("7.5B", 1.0, 4.0), np.array([0.1716, 0.2048, 1.2100])), + (("7.5B", 1.0, 6.0), np.array([0.1303, 0.1639, 1.2100])), + (("7.5B", 1.0, 8.0), np.array([0.0968, 0.1280, 1.2100])), + (("10B", 1.0, 2.0), np.array([0.2309, 0.2491, 1.2100])), + (("10B", 1.0, 4.0), np.array([0.1783, 0.1974, 1.2100])), + (("10B", 1.0, 6.0), np.array([0.1392, 0.1563, 1.2100])), + (("10B", 1.0, 8.0), np.array([0.1077, 0.1218, 1.2100])), + (("2.5PB", 1.0, 2.0), np.array([0.2360, 0.2420, 1.2100])), + (("2.5PB", 1.0, 4.0), np.array([0.1895, 0.1911, 1.2100])), + (("2.5PB", 1.0, 6.0), np.array([0.1539, 0.1491, 1.2100])), + (("2.5PB", 1.0, 8.0), np.array([0.1273, 0.1157, 1.2100])), + (("5PB", 1.0, 2.0), np.array([0.2427, 0.2368, 1.2100])), + (("5PB", 1.0, 4.0), np.array([0.2012, 0.1867, 1.2100])), + (("5PB", 1.0, 6.0), np.array([0.1678, 0.1447, 1.2100])), + (("5PB", 1.0, 8.0), np.array([0.1447, 0.1124, 1.2100])), + (("5PB", 1.0, 10.0), np.array([0.1285, 0.0870, 1.2100])), + (("7.5PB", 1.0, 2.0), np.array([0.2547, 0.2310, 1.2100])), + (("7.5PB", 1.0, 4.0), np.array([0.2232, 0.1821, 1.2100])), + (("7.5PB", 1.0, 6.0), np.array([0.2000, 0.1422, 1.2100])), + (("7.5PB", 1.0, 8.0), np.array([0.1872, 0.1141, 1.2100])), + (("7.5PB", 1.0, 10.0), np.array([0.1804, 0.0950, 1.2100])), + (("7.5PB", 1.0, 12.0), np.array([0.1763, 0.0804, 1.2100])), + (("7.5PB", 1.0, 14.0), np.array([0.1738, 0.0688, 1.2100])), + (("7.5PB", 1.0, 16.0), np.array([0.1720, 0.0583, 1.2100])), + (("7.5PB", 1.0, 18.0), np.array([0.1709, 0.0518, 1.2100])), + (("7.5PB", 1.0, 20.0), np.array([0.1701, 0.0454, 1.2100])), + (("7.5PB", 1.0, 22.0), np.array([0.1696, 0.0402, 1.2100])), + (("7.5PB", 1.0, 24.0), np.array([0.1691, 0.0352, 1.2100])), + (("7.5PB", 1.0, 26.0), np.array([0.1689, 0.0309, 1.2100])), + (("7.5PB", 1.0, 28.0), np.array([0.1686, 0.0270, 1.2100])), + (("7.5PB", 1.0, 30.0), np.array([0.1684, 0.0234, 1.2100])), + (("7.5PB", 1.0, 32.0), np.array([0.1682, 0.0202, 1.2100])), + (("7.5PB", 1.0, 34.0), np.array([0.1682, 0.0180, 1.2100])), + (("7.5PB", 1.0, 36.0), np.array([0.1681, 0.0160, 1.2100])), + (("7.5PB", 1.0, 38.0), np.array([0.1680, 0.0140, 1.2100])), + (("10PB", 1.0, 2.0), np.array([0.2677, 0.2280, 1.2100])), + (("10PB", 1.0, 4.0), np.array([0.2459, 0.1828, 1.2100])), + (("10PB", 1.0, 6.0), np.array([0.2290, 0.1470, 1.2100])), + (("10PB", 1.0, 8.0), np.array([0.2190, 0.1228, 1.2100])), + (("10PB", 1.0, 10.0), np.array([0.2120, 0.1029, 1.2100])), + (("10PB", 1.0, 12.0), np.array([0.2070, 0.0869, 1.2100])), + (("10PB", 1.0, 14.0), np.array([0.2038, 0.0745, 1.2100])), + (("10PB", 1.0, 16.0), np.array([0.2008, 0.0638, 1.2100])), + (("10PB", 1.0, 18.0), np.array([0.1991, 0.0564, 1.2100])), + (("10PB", 1.0, 20.0), np.array([0.1976, 0.0493, 1.2100])), + (("10PB", 1.0, 22.0), np.array([0.1965, 0.0436, 1.2100])), + (("10PB", 1.0, 24.0), np.array([0.1952, 0.0380, 1.2100])), + (("10PB", 1.0, 26.0), np.array([0.1942, 0.0326, 1.2100])), + (("10PB", 1.0, 28.0), np.array([0.1936, 0.0281, 1.2100])), + (("10PB", 1.0, 30.0), np.array([0.1928, 0.0240, 1.2100])), + (("2.5P", 1.0, 2.0), np.array([0.2808, 0.2296, 1.2100])), + (("2.5P", 1.0, 4.0), np.array([0.2668, 0.1874, 1.2100])), + (("2.5P", 1.0, 6.0), np.array([0.2570, 0.1559, 1.2100])), + (("2.5P", 1.0, 8.0), np.array([0.2496, 0.1303, 1.2100])), + (("2.5P", 1.0, 10.0), np.array([0.2441, 0.1112, 1.2100])), + (("2.5P", 1.0, 12.0), np.array([0.2394, 0.0940, 1.2100])), + (("2.5P", 1.0, 14.0), np.array([0.2361, 0.0810, 1.2100])), + (("2.5P", 1.0, 16.0), np.array([0.2331, 0.0696, 1.2100])), + (("2.5P", 1.0, 18.0), np.array([0.2312, 0.0618, 1.2100])), + (("2.5P", 1.0, 20.0), np.array([0.2295, 0.0542, 1.2100])), + (("2.5P", 1.0, 22.0), np.array([0.2279, 0.0473, 1.2100])), + (("2.5P", 1.0, 24.0), np.array([0.2266, 0.0418, 1.2100])), + (("2.5P", 1.0, 26.0), np.array([0.2251, 0.0355, 1.2100])), + (("5P", 1.0, 2.0), np.array([0.2936, 0.2330, 1.2100])), + (("5P", 1.0, 4.0), np.array([0.2854, 0.1927, 1.2100])), + (("5P", 1.0, 6.0), np.array([0.2794, 0.1628, 1.2100])), + (("5P", 1.0, 8.0), np.array([0.2742, 0.1375, 1.2100])), + (("5P", 1.0, 10.0), np.array([0.2701, 0.1178, 1.2100])), + (("5P", 1.0, 12.0), np.array([0.2670, 0.1006, 1.2100])), + (("5P", 1.0, 14.0), np.array([0.2645, 0.0863, 1.2100])), + (("5P", 1.0, 16.0), np.array([0.2625, 0.0746, 1.2100])), + (("5P", 1.0, 18.0), np.array([0.2612, 0.0667, 1.2100])), + (("5P", 1.0, 20.0), np.array([0.2601, 0.0586, 1.2100])), + (("5P", 1.0, 22.0), np.array([0.2590, 0.0509, 1.2100])), + (("7.5P", 1.0, 2.0), np.array([0.3030, 0.2361, 1.2100])), + (("7.5P", 1.0, 4.0), np.array([0.2991, 0.1974, 1.2100])), + (("7.5P", 1.0, 6.0), np.array([0.2960, 0.1682, 1.2100])), + (("7.5P", 1.0, 8.0), np.array([0.2932, 0.1429, 1.2100])), + (("7.5P", 1.0, 10.0), np.array([0.2905, 0.1229, 1.2100])), + (("7.5P", 1.0, 12.0), np.array([0.2884, 0.1059, 1.2100])), + (("7.5P", 1.0, 14.0), np.array([0.2868, 0.0903, 1.2100])), + (("7.5P", 1.0, 16.0), np.array([0.2852, 0.0790, 1.2100])), + (("7.5P", 1.0, 18.0), np.array([0.2841, 0.0706, 1.2100])), + (("7.5P", 1.0, 20.0), np.array([0.2831, 0.0625, 1.2100])), + (("10P", 1.0, 2.0), np.array([0.3132, 0.2404, 1.2100])), + (("10P", 1.0, 4.0), np.array([0.3132, 0.2032, 1.2100])), + (("10P", 1.0, 6.0), np.array([0.3126, 0.1737, 1.2100])), + (("10P", 1.0, 8.0), np.array([0.3114, 0.1481, 1.2100])), + (("10P", 1.0, 10.0), np.array([0.3102, 0.1282, 1.2100])), + (("10P", 1.0, 12.0), np.array([0.3094, 0.1110, 1.2100])), + (("10P", 1.0, 14.0), np.array([0.3084, 0.0952, 1.2100])), + (("10P", 1.0, 16.0), np.array([0.3078, 0.0839, 1.2100])), + (("10P", 1.0, 18.0), np.array([0.3069, 0.0748, 1.2100])), + (("2.5RP", 1.0, 2.0), np.array([0.3240, 0.2459, 1.2100])), + (("2.5RP", 1.0, 4.0), np.array([0.3290, 0.2095, 1.2100])), + (("2.5RP", 1.0, 6.0), np.array([0.3321, 0.1811, 1.2100])), + (("2.5RP", 1.0, 8.0), np.array([0.3342, 0.1551, 1.2100])), + (("2.5RP", 1.0, 10.0), np.array([0.3354, 0.1351, 1.2100])), + (("2.5RP", 1.0, 12.0), np.array([0.3361, 0.1181, 1.2100])), + (("2.5RP", 1.0, 14.0), np.array([0.3368, 0.1020, 1.2100])), + (("2.5RP", 1.0, 16.0), np.array([0.3368, 0.0902, 1.2100])), + (("5RP", 1.0, 2.0), np.array([0.3378, 0.2542, 1.2100])), + (("5RP", 1.0, 4.0), np.array([0.3503, 0.2196, 1.2100])), + (("5RP", 1.0, 6.0), np.array([0.3588, 0.1920, 1.2100])), + (("5RP", 1.0, 8.0), np.array([0.3660, 0.1662, 1.2100])), + (("5RP", 1.0, 10.0), np.array([0.3727, 0.1458, 1.2100])), + (("5RP", 1.0, 12.0), np.array([0.3772, 0.1283, 1.2100])), + (("5RP", 1.0, 14.0), np.array([0.3811, 0.1138, 1.2100])), + (("7.5RP", 1.0, 2.0), np.array([0.3498, 0.2617, 1.2100])), + (("7.5RP", 1.0, 4.0), np.array([0.3705, 0.2300, 1.2100])), + (("7.5RP", 1.0, 6.0), np.array([0.3865, 0.2036, 1.2100])), + (("7.5RP", 1.0, 8.0), np.array([0.4005, 0.1793, 1.2100])), + (("7.5RP", 1.0, 10.0), np.array([0.4132, 0.1580, 1.2100])), + (("7.5RP", 1.0, 12.0), np.array([0.4240, 0.1400, 1.2100])), + (("10RP", 2.0, 2.0), np.array([0.3532, 0.2957, 3.1260])), + (("10RP", 2.0, 4.0), np.array([0.3850, 0.2778, 3.1260])), + (("10RP", 2.0, 6.0), np.array([0.4139, 0.2608, 3.1260])), + (("10RP", 2.0, 8.0), np.array([0.4428, 0.2419, 3.1260])), + (("10RP", 2.0, 10.0), np.array([0.4678, 0.2237, 3.1260])), + (("10RP", 2.0, 12.0), np.array([0.4911, 0.2060, 3.1260])), + (("10RP", 2.0, 14.0), np.array([0.5129, 0.1888, 3.1260])), + (("2.5R", 2.0, 2.0), np.array([0.3614, 0.3033, 3.1260])), + (("2.5R", 2.0, 4.0), np.array([0.4021, 0.2900, 3.1260])), + (("2.5R", 2.0, 6.0), np.array([0.4390, 0.2760, 3.1260])), + (("2.5R", 2.0, 8.0), np.array([0.4776, 0.2593, 3.1260])), + (("2.5R", 2.0, 10.0), np.array([0.5122, 0.2428, 3.1260])), + (("2.5R", 2.0, 12.0), np.array([0.5438, 0.2254, 3.1260])), + (("2.5R", 2.0, 14.0), np.array([0.5734, 0.2083, 3.1260])), + (("5R", 2.0, 2.0), np.array([0.3692, 0.3111, 3.1260])), + (("5R", 2.0, 4.0), np.array([0.4184, 0.3032, 3.1260])), + (("5R", 2.0, 6.0), np.array([0.4642, 0.2934, 3.1260])), + (("5R", 2.0, 8.0), np.array([0.5143, 0.2800, 3.1260])), + (("5R", 2.0, 10.0), np.array([0.5557, 0.2633, 3.1260])), + (("5R", 2.0, 12.0), np.array([0.5930, 0.2465, 3.1260])), + (("5R", 2.0, 14.0), np.array([0.6302, 0.2287, 3.1260])), + (("7.5R", 2.0, 2.0), np.array([0.3751, 0.3181, 3.1260])), + (("7.5R", 2.0, 4.0), np.array([0.4335, 0.3169, 3.1260])), + (("7.5R", 2.0, 6.0), np.array([0.4875, 0.3123, 3.1260])), + (("7.5R", 2.0, 8.0), np.array([0.5433, 0.3027, 3.1260])), + (("7.5R", 2.0, 10.0), np.array([0.5952, 0.2874, 3.1260])), + (("7.5R", 2.0, 12.0), np.array([0.6392, 0.2704, 3.1260])), + (("7.5R", 2.0, 14.0), np.array([0.6791, 0.2520, 3.1260])), + (("10R", 2.0, 2.0), np.array([0.3811, 0.3274, 3.1260])), + (("10R", 2.0, 4.0), np.array([0.4481, 0.3330, 3.1260])), + (("10R", 2.0, 6.0), np.array([0.5095, 0.3331, 3.1260])), + (("10R", 2.0, 8.0), np.array([0.5713, 0.3259, 3.1260])), + (("10R", 2.0, 10.0), np.array([0.6247, 0.3120, 3.1260])), + (("10R", 2.0, 12.0), np.array([0.6732, 0.2937, 3.1260])), + (("10R", 2.0, 14.0), np.array([0.7165, 0.2734, 3.1260])), + (("2.5YR", 2.0, 2.0), np.array([0.3852, 0.3365, 3.1260])), + (("2.5YR", 2.0, 4.0), np.array([0.4598, 0.3508, 3.1260])), + (("2.5YR", 2.0, 6.0), np.array([0.5280, 0.3581, 3.1260])), + (("2.5YR", 2.0, 8.0), np.array([0.5995, 0.3590, 3.1260])), + (("5YR", 2.0, 2.0), np.array([0.3880, 0.3476, 3.1260])), + (("5YR", 2.0, 4.0), np.array([0.4674, 0.3738, 3.1260])), + (("5YR", 2.0, 6.0), np.array([0.5426, 0.3925, 3.1260])), + (("7.5YR", 2.0, 2.0), np.array([0.3889, 0.3590, 3.1260])), + (("7.5YR", 2.0, 4.0), np.array([0.4690, 0.3964, 3.1260])), + (("7.5YR", 2.0, 6.0), np.array([0.5475, 0.4271, 3.1260])), + (("10YR", 2.0, 2.0), np.array([0.3872, 0.3688, 3.1260])), + (("10YR", 2.0, 4.0), np.array([0.4676, 0.4168, 3.1260])), + (("2.5Y", 2.0, 2.0), np.array([0.3825, 0.3785, 3.1260])), + (("2.5Y", 2.0, 4.0), np.array([0.4627, 0.4392, 3.1260])), + (("5Y", 2.0, 2.0), np.array([0.3757, 0.3839, 3.1260])), + (("5Y", 2.0, 4.0), np.array([0.4543, 0.4573, 3.1260])), + (("7.5Y", 2.0, 2.0), np.array([0.3660, 0.3858, 3.1260])), + (("7.5Y", 2.0, 4.0), np.array([0.4401, 0.4723, 3.1260])), + (("10Y", 2.0, 2.0), np.array([0.3556, 0.3848, 3.1260])), + (("10Y", 2.0, 4.0), np.array([0.4188, 0.4789, 3.1260])), + (("2.5GY", 2.0, 2.0), np.array([0.3421, 0.3803, 3.1260])), + (("2.5GY", 2.0, 4.0), np.array([0.3881, 0.4752, 3.1260])), + (("5GY", 2.0, 2.0), np.array([0.3309, 0.3743, 3.1260])), + (("5GY", 2.0, 4.0), np.array([0.3582, 0.4650, 3.1260])), + (("5GY", 2.0, 6.0), np.array([0.3839, 0.5748, 3.1260])), + (("7.5GY", 2.0, 2.0), np.array([0.3165, 0.3650, 3.1260])), + (("7.5GY", 2.0, 4.0), np.array([0.3248, 0.4457, 3.1260])), + (("7.5GY", 2.0, 6.0), np.array([0.3260, 0.5379, 3.1260])), + (("7.5GY", 2.0, 8.0), np.array([0.3160, 0.6509, 3.1260])), + (("10GY", 2.0, 2.0), np.array([0.3069, 0.3580, 3.1260])), + (("10GY", 2.0, 4.0), np.array([0.2986, 0.4240, 3.1260])), + (("10GY", 2.0, 6.0), np.array([0.2852, 0.4972, 3.1260])), + (("10GY", 2.0, 8.0), np.array([0.2628, 0.5837, 3.1260])), + (("10GY", 2.0, 10.0), np.array([0.2307, 0.6814, 3.1260])), + (("10GY", 2.0, 12.0), np.array([0.1907, 0.7798, 3.1260])), + (("2.5G", 2.0, 2.0), np.array([0.2978, 0.3507, 3.1260])), + (("2.5G", 2.0, 4.0), np.array([0.2763, 0.3998, 3.1260])), + (("2.5G", 2.0, 6.0), np.array([0.2493, 0.4522, 3.1260])), + (("2.5G", 2.0, 8.0), np.array([0.2192, 0.5042, 3.1260])), + (("2.5G", 2.0, 10.0), np.array([0.1773, 0.5698, 3.1260])), + (("2.5G", 2.0, 12.0), np.array([0.1307, 0.6308, 3.1260])), + (("2.5G", 2.0, 14.0), np.array([0.0820, 0.6860, 3.1260])), + (("2.5G", 2.0, 16.0), np.array([0.0329, 0.7358, 3.1260])), + (("5G", 2.0, 2.0), np.array([0.2918, 0.3450, 3.1260])), + (("5G", 2.0, 4.0), np.array([0.2640, 0.3845, 3.1260])), + (("5G", 2.0, 6.0), np.array([0.2318, 0.4231, 3.1260])), + (("5G", 2.0, 8.0), np.array([0.1979, 0.4583, 3.1260])), + (("5G", 2.0, 10.0), np.array([0.1560, 0.4981, 3.1260])), + (("5G", 2.0, 12.0), np.array([0.1120, 0.5358, 3.1260])), + (("5G", 2.0, 14.0), np.array([0.0688, 0.5691, 3.1260])), + (("5G", 2.0, 16.0), np.array([0.0277, 0.5986, 3.1260])), + (("7.5G", 2.0, 2.0), np.array([0.2869, 0.3400, 3.1260])), + (("7.5G", 2.0, 4.0), np.array([0.2540, 0.3705, 3.1260])), + (("7.5G", 2.0, 6.0), np.array([0.2200, 0.3983, 3.1260])), + (("7.5G", 2.0, 8.0), np.array([0.1842, 0.4244, 3.1260])), + (("7.5G", 2.0, 10.0), np.array([0.1442, 0.4505, 3.1260])), + (("7.5G", 2.0, 12.0), np.array([0.1022, 0.4759, 3.1260])), + (("7.5G", 2.0, 14.0), np.array([0.0629, 0.4973, 3.1260])), + (("7.5G", 2.0, 16.0), np.array([0.0276, 0.5153, 3.1260])), + (("10G", 2.0, 2.0), np.array([0.2820, 0.3341, 3.1260])), + (("10G", 2.0, 4.0), np.array([0.2442, 0.3559, 3.1260])), + (("10G", 2.0, 6.0), np.array([0.2092, 0.3739, 3.1260])), + (("10G", 2.0, 8.0), np.array([0.1705, 0.3911, 3.1260])), + (("10G", 2.0, 10.0), np.array([0.1321, 0.4059, 3.1260])), + (("10G", 2.0, 12.0), np.array([0.0934, 0.4183, 3.1260])), + (("10G", 2.0, 14.0), np.array([0.0599, 0.4270, 3.1260])), + (("10G", 2.0, 16.0), np.array([0.0285, 0.4327, 3.1260])), + (("2.5BG", 2.0, 2.0), np.array([0.2765, 0.3271, 3.1260])), + (("2.5BG", 2.0, 4.0), np.array([0.2343, 0.3378, 3.1260])), + (("2.5BG", 2.0, 6.0), np.array([0.1971, 0.3452, 3.1260])), + (("2.5BG", 2.0, 8.0), np.array([0.1557, 0.3517, 3.1260])), + (("2.5BG", 2.0, 10.0), np.array([0.1190, 0.3551, 3.1260])), + (("2.5BG", 2.0, 12.0), np.array([0.0851, 0.3576, 3.1260])), + (("2.5BG", 2.0, 14.0), np.array([0.0555, 0.3588, 3.1260])), + (("5BG", 2.0, 2.0), np.array([0.2697, 0.3175, 3.1260])), + (("5BG", 2.0, 4.0), np.array([0.2234, 0.3150, 3.1260])), + (("5BG", 2.0, 6.0), np.array([0.1843, 0.3110, 3.1260])), + (("5BG", 2.0, 8.0), np.array([0.1405, 0.3037, 3.1260])), + (("5BG", 2.0, 10.0), np.array([0.1050, 0.2956, 3.1260])), + (("5BG", 2.0, 12.0), np.array([0.0769, 0.2880, 3.1260])), + (("7.5BG", 2.0, 2.0), np.array([0.2651, 0.3098, 3.1260])), + (("7.5BG", 2.0, 4.0), np.array([0.2162, 0.2981, 3.1260])), + (("7.5BG", 2.0, 6.0), np.array([0.1747, 0.2853, 3.1260])), + (("7.5BG", 2.0, 8.0), np.array([0.1325, 0.2710, 3.1260])), + (("7.5BG", 2.0, 10.0), np.array([0.0991, 0.2582, 3.1260])), + (("7.5BG", 2.0, 12.0), np.array([0.0724, 0.2478, 3.1260])), + (("10BG", 2.0, 2.0), np.array([0.2606, 0.3010, 3.1260])), + (("10BG", 2.0, 4.0), np.array([0.2096, 0.2790, 3.1260])), + (("10BG", 2.0, 6.0), np.array([0.1669, 0.2570, 3.1260])), + (("10BG", 2.0, 8.0), np.array([0.1258, 0.2331, 3.1260])), + (("10BG", 2.0, 10.0), np.array([0.0929, 0.2133, 3.1260])), + (("2.5B", 2.0, 2.0), np.array([0.2578, 0.2940, 3.1260])), + (("2.5B", 2.0, 4.0), np.array([0.2060, 0.2649, 3.1260])), + (("2.5B", 2.0, 6.0), np.array([0.1621, 0.2358, 3.1260])), + (("2.5B", 2.0, 8.0), np.array([0.1230, 0.2076, 3.1260])), + (("2.5B", 2.0, 10.0), np.array([0.0911, 0.1828, 3.1260])), + (("5B", 2.0, 2.0), np.array([0.2559, 0.2874, 3.1260])), + (("5B", 2.0, 4.0), np.array([0.2048, 0.2518, 3.1260])), + (("5B", 2.0, 6.0), np.array([0.1617, 0.2162, 3.1260])), + (("5B", 2.0, 8.0), np.array([0.1245, 0.1827, 3.1260])), + (("5B", 2.0, 10.0), np.array([0.0965, 0.1558, 3.1260])), + (("7.5B", 2.0, 2.0), np.array([0.2545, 0.2799, 3.1260])), + (("7.5B", 2.0, 4.0), np.array([0.2063, 0.2400, 3.1260])), + (("7.5B", 2.0, 6.0), np.array([0.1658, 0.2026, 3.1260])), + (("7.5B", 2.0, 8.0), np.array([0.1313, 0.1692, 3.1260])), + (("7.5B", 2.0, 10.0), np.array([0.1051, 0.1422, 3.1260])), + (("10B", 2.0, 2.0), np.array([0.2558, 0.2725, 3.1260])), + (("10B", 2.0, 4.0), np.array([0.2102, 0.2313, 3.1260])), + (("10B", 2.0, 6.0), np.array([0.1716, 0.1937, 3.1260])), + (("10B", 2.0, 8.0), np.array([0.1396, 0.1603, 3.1260])), + (("10B", 2.0, 10.0), np.array([0.1157, 0.1346, 3.1260])), + (("2.5PB", 2.0, 2.0), np.array([0.2592, 0.2675, 3.1260])), + (("2.5PB", 2.0, 4.0), np.array([0.2175, 0.2245, 3.1260])), + (("2.5PB", 2.0, 6.0), np.array([0.1825, 0.1857, 3.1260])), + (("2.5PB", 2.0, 8.0), np.array([0.1540, 0.1530, 3.1260])), + (("2.5PB", 2.0, 10.0), np.array([0.1332, 0.1278, 3.1260])), + (("2.5PB", 2.0, 12.0), np.array([0.1166, 0.1076, 3.1260])), + (("5PB", 2.0, 2.0), np.array([0.2638, 0.2624, 3.1260])), + (("5PB", 2.0, 4.0), np.array([0.2263, 0.2192, 3.1260])), + (("5PB", 2.0, 6.0), np.array([0.1942, 0.1811, 3.1260])), + (("5PB", 2.0, 8.0), np.array([0.1685, 0.1491, 3.1260])), + (("5PB", 2.0, 10.0), np.array([0.1500, 0.1240, 3.1260])), + (("5PB", 2.0, 12.0), np.array([0.1363, 0.1048, 3.1260])), + (("5PB", 2.0, 14.0), np.array([0.1253, 0.0873, 3.1260])), + (("7.5PB", 2.0, 2.0), np.array([0.2712, 0.2582, 3.1260])), + (("7.5PB", 2.0, 4.0), np.array([0.2420, 0.2148, 3.1260])), + (("7.5PB", 2.0, 6.0), np.array([0.2189, 0.1790, 3.1260])), + (("7.5PB", 2.0, 8.0), np.array([0.2005, 0.1495, 3.1260])), + (("7.5PB", 2.0, 10.0), np.array([0.1882, 0.1258, 3.1260])), + (("7.5PB", 2.0, 12.0), np.array([0.1813, 0.1094, 3.1260])), + (("7.5PB", 2.0, 14.0), np.array([0.1762, 0.0955, 3.1260])), + (("7.5PB", 2.0, 16.0), np.array([0.1728, 0.0839, 3.1260])), + (("7.5PB", 2.0, 18.0), np.array([0.1701, 0.0742, 3.1260])), + (("7.5PB", 2.0, 20.0), np.array([0.1685, 0.0666, 3.1260])), + (("7.5PB", 2.0, 22.0), np.array([0.1670, 0.0594, 3.1260])), + (("7.5PB", 2.0, 24.0), np.array([0.1660, 0.0538, 3.1260])), + (("7.5PB", 2.0, 26.0), np.array([0.1653, 0.0492, 3.1260])), + (("7.5PB", 2.0, 28.0), np.array([0.1647, 0.0451, 3.1260])), + (("7.5PB", 2.0, 30.0), np.array([0.1640, 0.0409, 3.1260])), + (("7.5PB", 2.0, 32.0), np.array([0.1635, 0.0373, 3.1260])), + (("7.5PB", 2.0, 34.0), np.array([0.1630, 0.0340, 3.1260])), + (("7.5PB", 2.0, 36.0), np.array([0.1628, 0.0310, 3.1260])), + (("7.5PB", 2.0, 38.0), np.array([0.1623, 0.0280, 3.1260])), + (("10PB", 2.0, 2.0), np.array([0.2803, 0.2567, 3.1260])), + (("10PB", 2.0, 4.0), np.array([0.2600, 0.2162, 3.1260])), + (("10PB", 2.0, 6.0), np.array([0.2440, 0.1840, 3.1260])), + (("10PB", 2.0, 8.0), np.array([0.2294, 0.1551, 3.1260])), + (("10PB", 2.0, 10.0), np.array([0.2200, 0.1330, 3.1260])), + (("10PB", 2.0, 12.0), np.array([0.2139, 0.1170, 3.1260])), + (("10PB", 2.0, 14.0), np.array([0.2087, 0.1026, 3.1260])), + (("10PB", 2.0, 16.0), np.array([0.2052, 0.0910, 3.1260])), + (("10PB", 2.0, 18.0), np.array([0.2021, 0.0808, 3.1260])), + (("10PB", 2.0, 20.0), np.array([0.1998, 0.0718, 3.1260])), + (("10PB", 2.0, 22.0), np.array([0.1978, 0.0643, 3.1260])), + (("10PB", 2.0, 24.0), np.array([0.1962, 0.0578, 3.1260])), + (("10PB", 2.0, 26.0), np.array([0.1949, 0.0520, 3.1260])), + (("10PB", 2.0, 28.0), np.array([0.1937, 0.0471, 3.1260])), + (("10PB", 2.0, 30.0), np.array([0.1925, 0.0420, 3.1260])), + (("10PB", 2.0, 32.0), np.array([0.1918, 0.0379, 3.1260])), + (("10PB", 2.0, 34.0), np.array([0.1911, 0.0344, 3.1260])), + (("2.5P", 2.0, 2.0), np.array([0.2892, 0.2583, 3.1260])), + (("2.5P", 2.0, 4.0), np.array([0.2758, 0.2208, 3.1260])), + (("2.5P", 2.0, 6.0), np.array([0.2661, 0.1921, 3.1260])), + (("2.5P", 2.0, 8.0), np.array([0.2570, 0.1635, 3.1260])), + (("2.5P", 2.0, 10.0), np.array([0.2501, 0.1422, 3.1260])), + (("2.5P", 2.0, 12.0), np.array([0.2449, 0.1245, 3.1260])), + (("2.5P", 2.0, 14.0), np.array([0.2406, 0.1100, 3.1260])), + (("2.5P", 2.0, 16.0), np.array([0.2372, 0.0980, 3.1260])), + (("2.5P", 2.0, 18.0), np.array([0.2345, 0.0873, 3.1260])), + (("2.5P", 2.0, 20.0), np.array([0.2320, 0.0779, 3.1260])), + (("2.5P", 2.0, 22.0), np.array([0.2298, 0.0696, 3.1260])), + (("2.5P", 2.0, 24.0), np.array([0.2277, 0.0621, 3.1260])), + (("2.5P", 2.0, 26.0), np.array([0.2260, 0.0555, 3.1260])), + (("2.5P", 2.0, 28.0), np.array([0.2245, 0.0491, 3.1260])), + (("2.5P", 2.0, 30.0), np.array([0.2231, 0.0432, 3.1260])), + (("5P", 2.0, 2.0), np.array([0.2984, 0.2612, 3.1260])), + (("5P", 2.0, 4.0), np.array([0.2908, 0.2261, 3.1260])), + (("5P", 2.0, 6.0), np.array([0.2850, 0.1992, 3.1260])), + (("5P", 2.0, 8.0), np.array([0.2791, 0.1707, 3.1260])), + (("5P", 2.0, 10.0), np.array([0.2748, 0.1500, 3.1260])), + (("5P", 2.0, 12.0), np.array([0.2709, 0.1320, 3.1260])), + (("5P", 2.0, 14.0), np.array([0.2676, 0.1163, 3.1260])), + (("5P", 2.0, 16.0), np.array([0.2652, 0.1045, 3.1260])), + (("5P", 2.0, 18.0), np.array([0.2632, 0.0935, 3.1260])), + (("5P", 2.0, 20.0), np.array([0.2612, 0.0838, 3.1260])), + (("5P", 2.0, 22.0), np.array([0.2597, 0.0750, 3.1260])), + (("5P", 2.0, 24.0), np.array([0.2582, 0.0669, 3.1260])), + (("5P", 2.0, 26.0), np.array([0.2569, 0.0594, 3.1260])), + (("5P", 2.0, 28.0), np.array([0.2559, 0.0525, 3.1260])), + (("7.5P", 2.0, 2.0), np.array([0.3071, 0.2647, 3.1260])), + (("7.5P", 2.0, 4.0), np.array([0.3048, 0.2321, 3.1260])), + (("7.5P", 2.0, 6.0), np.array([0.3025, 0.2058, 3.1260])), + (("7.5P", 2.0, 8.0), np.array([0.3000, 0.1781, 3.1260])), + (("7.5P", 2.0, 10.0), np.array([0.2979, 0.1569, 3.1260])), + (("7.5P", 2.0, 12.0), np.array([0.2956, 0.1392, 3.1260])), + (("7.5P", 2.0, 14.0), np.array([0.2938, 0.1235, 3.1260])), + (("7.5P", 2.0, 16.0), np.array([0.2922, 0.1106, 3.1260])), + (("7.5P", 2.0, 18.0), np.array([0.2912, 0.0995, 3.1260])), + (("7.5P", 2.0, 20.0), np.array([0.2902, 0.0901, 3.1260])), + (("7.5P", 2.0, 22.0), np.array([0.2890, 0.0799, 3.1260])), + (("7.5P", 2.0, 24.0), np.array([0.2882, 0.0719, 3.1260])), + (("10P", 2.0, 2.0), np.array([0.3161, 0.2691, 3.1260])), + (("10P", 2.0, 4.0), np.array([0.3189, 0.2390, 3.1260])), + (("10P", 2.0, 6.0), np.array([0.3207, 0.2132, 3.1260])), + (("10P", 2.0, 8.0), np.array([0.3219, 0.1862, 3.1260])), + (("10P", 2.0, 10.0), np.array([0.3230, 0.1659, 3.1260])), + (("10P", 2.0, 12.0), np.array([0.3233, 0.1477, 3.1260])), + (("10P", 2.0, 14.0), np.array([0.3235, 0.1317, 3.1260])), + (("10P", 2.0, 16.0), np.array([0.3235, 0.1181, 3.1260])), + (("10P", 2.0, 18.0), np.array([0.3233, 0.1063, 3.1260])), + (("10P", 2.0, 20.0), np.array([0.3231, 0.0962, 3.1260])), + (("10P", 2.0, 22.0), np.array([0.3230, 0.0861, 3.1260])), + (("2.5RP", 2.0, 2.0), np.array([0.3279, 0.2754, 3.1260])), + (("2.5RP", 2.0, 4.0), np.array([0.3382, 0.2496, 3.1260])), + (("2.5RP", 2.0, 6.0), np.array([0.3470, 0.2259, 3.1260])), + (("2.5RP", 2.0, 8.0), np.array([0.3555, 0.2003, 3.1260])), + (("2.5RP", 2.0, 10.0), np.array([0.3617, 0.1800, 3.1260])), + (("2.5RP", 2.0, 12.0), np.array([0.3668, 0.1618, 3.1260])), + (("2.5RP", 2.0, 14.0), np.array([0.3711, 0.1449, 3.1260])), + (("2.5RP", 2.0, 16.0), np.array([0.3748, 0.1310, 3.1260])), + (("2.5RP", 2.0, 18.0), np.array([0.3778, 0.1188, 3.1260])), + (("2.5RP", 2.0, 20.0), np.array([0.3802, 0.1080, 3.1260])), + (("5RP", 2.0, 2.0), np.array([0.3383, 0.2829, 3.1260])), + (("5RP", 2.0, 4.0), np.array([0.3558, 0.2597, 3.1260])), + (("5RP", 2.0, 6.0), np.array([0.3708, 0.2380, 3.1260])), + (("5RP", 2.0, 8.0), np.array([0.3858, 0.2140, 3.1260])), + (("5RP", 2.0, 10.0), np.array([0.3971, 0.1939, 3.1260])), + (("5RP", 2.0, 12.0), np.array([0.4080, 0.1764, 3.1260])), + (("5RP", 2.0, 14.0), np.array([0.4180, 0.1598, 3.1260])), + (("5RP", 2.0, 16.0), np.array([0.4269, 0.1454, 3.1260])), + (("5RP", 2.0, 18.0), np.array([0.4338, 0.1340, 3.1260])), + (("7.5RP", 2.0, 2.0), np.array([0.3459, 0.2892, 3.1260])), + (("7.5RP", 2.0, 4.0), np.array([0.3702, 0.2683, 3.1260])), + (("7.5RP", 2.0, 6.0), np.array([0.3918, 0.2490, 3.1260])), + (("7.5RP", 2.0, 8.0), np.array([0.4137, 0.2276, 3.1260])), + (("7.5RP", 2.0, 10.0), np.array([0.4321, 0.2082, 3.1260])), + (("7.5RP", 2.0, 12.0), np.array([0.4481, 0.1903, 3.1260])), + (("7.5RP", 2.0, 14.0), np.array([0.4624, 0.1737, 3.1260])), + (("7.5RP", 2.0, 16.0), np.array([0.4744, 0.1595, 3.1260])), + (("10RP", 3.0, 2.0), np.array([0.3526, 0.3068, 6.5550])), + (("10RP", 3.0, 4.0), np.array([0.3889, 0.2969, 6.5550])), + (("10RP", 3.0, 6.0), np.array([0.4218, 0.2864, 6.5550])), + (("10RP", 3.0, 8.0), np.array([0.4552, 0.2741, 6.5550])), + (("10RP", 3.0, 10.0), np.array([0.4851, 0.2618, 6.5550])), + (("10RP", 3.0, 12.0), np.array([0.5139, 0.2489, 6.5550])), + (("10RP", 3.0, 14.0), np.array([0.5380, 0.2369, 6.5550])), + (("10RP", 3.0, 16.0), np.array([0.5628, 0.2241, 6.5550])), + (("2.5R", 3.0, 2.0), np.array([0.3591, 0.3130, 6.5550])), + (("2.5R", 3.0, 4.0), np.array([0.4021, 0.3076, 6.5550])), + (("2.5R", 3.0, 6.0), np.array([0.4409, 0.3009, 6.5550])), + (("2.5R", 3.0, 8.0), np.array([0.4821, 0.2918, 6.5550])), + (("2.5R", 3.0, 10.0), np.array([0.5191, 0.2811, 6.5550])), + (("2.5R", 3.0, 12.0), np.array([0.5536, 0.2691, 6.5550])), + (("2.5R", 3.0, 14.0), np.array([0.5828, 0.2579, 6.5550])), + (("2.5R", 3.0, 16.0), np.array([0.6116, 0.2456, 6.5550])), + (("5R", 3.0, 2.0), np.array([0.3645, 0.3190, 6.5550])), + (("5R", 3.0, 4.0), np.array([0.4148, 0.3190, 6.5550])), + (("5R", 3.0, 6.0), np.array([0.4592, 0.3168, 6.5550])), + (("5R", 3.0, 8.0), np.array([0.5064, 0.3114, 6.5550])), + (("5R", 3.0, 10.0), np.array([0.5500, 0.3024, 6.5550])), + (("5R", 3.0, 12.0), np.array([0.5884, 0.2904, 6.5550])), + (("5R", 3.0, 14.0), np.array([0.6204, 0.2789, 6.5550])), + (("5R", 3.0, 16.0), np.array([0.6520, 0.2660, 6.5550])), + (("7.5R", 3.0, 2.0), np.array([0.3690, 0.3248, 6.5550])), + (("7.5R", 3.0, 4.0), np.array([0.4240, 0.3302, 6.5550])), + (("7.5R", 3.0, 6.0), np.array([0.4738, 0.3316, 6.5550])), + (("7.5R", 3.0, 8.0), np.array([0.5251, 0.3297, 6.5550])), + (("7.5R", 3.0, 10.0), np.array([0.5730, 0.3240, 6.5550])), + (("7.5R", 3.0, 12.0), np.array([0.6158, 0.3129, 6.5550])), + (("7.5R", 3.0, 14.0), np.array([0.6492, 0.3012, 6.5550])), + (("7.5R", 3.0, 16.0), np.array([0.6817, 0.2872, 6.5550])), + (("10R", 3.0, 2.0), np.array([0.3728, 0.3314, 6.5550])), + (("10R", 3.0, 4.0), np.array([0.4308, 0.3412, 6.5550])), + (("10R", 3.0, 6.0), np.array([0.4854, 0.3467, 6.5550])), + (("10R", 3.0, 8.0), np.array([0.5393, 0.3477, 6.5550])), + (("10R", 3.0, 10.0), np.array([0.5871, 0.3440, 6.5550])), + (("10R", 3.0, 12.0), np.array([0.6322, 0.3361, 6.5550])), + (("10R", 3.0, 14.0), np.array([0.6703, 0.3249, 6.5550])), + (("2.5YR", 3.0, 2.0), np.array([0.3757, 0.3391, 6.5550])), + (("2.5YR", 3.0, 4.0), np.array([0.4360, 0.3563, 6.5550])), + (("2.5YR", 3.0, 6.0), np.array([0.4954, 0.3692, 6.5550])), + (("2.5YR", 3.0, 8.0), np.array([0.5475, 0.3771, 6.5550])), + (("2.5YR", 3.0, 10.0), np.array([0.5941, 0.3818, 6.5550])), + (("5YR", 3.0, 2.0), np.array([0.3771, 0.3476, 6.5550])), + (("5YR", 3.0, 4.0), np.array([0.4376, 0.3715, 6.5550])), + (("5YR", 3.0, 6.0), np.array([0.4966, 0.3908, 6.5550])), + (("5YR", 3.0, 8.0), np.array([0.5456, 0.4040, 6.5550])), + (("7.5YR", 3.0, 2.0), np.array([0.3771, 0.3549, 6.5550])), + (("7.5YR", 3.0, 4.0), np.array([0.4378, 0.3865, 6.5550])), + (("7.5YR", 3.0, 6.0), np.array([0.4930, 0.4116, 6.5550])), + (("7.5YR", 3.0, 8.0), np.array([0.5390, 0.4306, 6.5550])), + (("10YR", 3.0, 2.0), np.array([0.3747, 0.3630, 6.5550])), + (("10YR", 3.0, 4.0), np.array([0.4341, 0.4018, 6.5550])), + (("10YR", 3.0, 6.0), np.array([0.4872, 0.4326, 6.5550])), + (("10YR", 3.0, 8.0), np.array([0.5305, 0.4559, 6.5550])), + (("2.5Y", 3.0, 2.0), np.array([0.3703, 0.3700, 6.5550])), + (("2.5Y", 3.0, 4.0), np.array([0.4277, 0.4166, 6.5550])), + (("2.5Y", 3.0, 6.0), np.array([0.4784, 0.4531, 6.5550])), + (("5Y", 3.0, 2.0), np.array([0.3646, 0.3748, 6.5550])), + (("5Y", 3.0, 4.0), np.array([0.4191, 0.4283, 6.5550])), + (("5Y", 3.0, 6.0), np.array([0.4670, 0.4711, 6.5550])), + (("7.5Y", 3.0, 2.0), np.array([0.3589, 0.3778, 6.5550])), + (("7.5Y", 3.0, 4.0), np.array([0.4086, 0.4379, 6.5550])), + (("7.5Y", 3.0, 6.0), np.array([0.4526, 0.4889, 6.5550])), + (("10Y", 3.0, 2.0), np.array([0.3513, 0.3789, 6.5550])), + (("10Y", 3.0, 4.0), np.array([0.3961, 0.4452, 6.5550])), + (("10Y", 3.0, 6.0), np.array([0.4345, 0.5026, 6.5550])), + (("2.5GY", 3.0, 2.0), np.array([0.3412, 0.3768, 6.5550])), + (("2.5GY", 3.0, 4.0), np.array([0.3772, 0.4484, 6.5550])), + (("2.5GY", 3.0, 6.0), np.array([0.4069, 0.5110, 6.5550])), + (("5GY", 3.0, 2.0), np.array([0.3319, 0.3729, 6.5550])), + (("5GY", 3.0, 4.0), np.array([0.3554, 0.4429, 6.5550])), + (("5GY", 3.0, 6.0), np.array([0.3750, 0.5109, 6.5550])), + (("5GY", 3.0, 8.0), np.array([0.3924, 0.5832, 6.5550])), + (("7.5GY", 3.0, 2.0), np.array([0.3180, 0.3644, 6.5550])), + (("7.5GY", 3.0, 4.0), np.array([0.3270, 0.4288, 6.5550])), + (("7.5GY", 3.0, 6.0), np.array([0.3333, 0.4967, 6.5550])), + (("7.5GY", 3.0, 8.0), np.array([0.3341, 0.5700, 6.5550])), + (("7.5GY", 3.0, 10.0), np.array([0.3266, 0.6448, 6.5550])), + (("10GY", 3.0, 2.0), np.array([0.3088, 0.3578, 6.5550])), + (("10GY", 3.0, 4.0), np.array([0.3053, 0.4123, 6.5550])), + (("10GY", 3.0, 6.0), np.array([0.2992, 0.4717, 6.5550])), + (("10GY", 3.0, 8.0), np.array([0.2887, 0.5361, 6.5550])), + (("10GY", 3.0, 10.0), np.array([0.2724, 0.6026, 6.5550])), + (("10GY", 3.0, 12.0), np.array([0.2531, 0.6700, 6.5550])), + (("10GY", 3.0, 14.0), np.array([0.2283, 0.7423, 6.5550])), + (("2.5G", 3.0, 2.0), np.array([0.2999, 0.3500, 6.5550])), + (("2.5G", 3.0, 4.0), np.array([0.2836, 0.3915, 6.5550])), + (("2.5G", 3.0, 6.0), np.array([0.2642, 0.4342, 6.5550])), + (("2.5G", 3.0, 8.0), np.array([0.2435, 0.4752, 6.5550])), + (("2.5G", 3.0, 10.0), np.array([0.2170, 0.5211, 6.5550])), + (("2.5G", 3.0, 12.0), np.array([0.1902, 0.5642, 6.5550])), + (("2.5G", 3.0, 14.0), np.array([0.1626, 0.6052, 6.5550])), + (("2.5G", 3.0, 16.0), np.array([0.1341, 0.6420, 6.5550])), + (("2.5G", 3.0, 18.0), np.array([0.1049, 0.6766, 6.5550])), + (("2.5G", 3.0, 20.0), np.array([0.0720, 0.7127, 6.5550])), + (("2.5G", 3.0, 22.0), np.array([0.0390, 0.7468, 6.5550])), + (("5G", 3.0, 2.0), np.array([0.2935, 0.3439, 6.5550])), + (("5G", 3.0, 4.0), np.array([0.2711, 0.3780, 6.5550])), + (("5G", 3.0, 6.0), np.array([0.2471, 0.4100, 6.5550])), + (("5G", 3.0, 8.0), np.array([0.2228, 0.4380, 6.5550])), + (("5G", 3.0, 10.0), np.array([0.1935, 0.4682, 6.5550])), + (("5G", 3.0, 12.0), np.array([0.1660, 0.4948, 6.5550])), + (("5G", 3.0, 14.0), np.array([0.1382, 0.5197, 6.5550])), + (("5G", 3.0, 16.0), np.array([0.1120, 0.5414, 6.5550])), + (("5G", 3.0, 18.0), np.array([0.0882, 0.5605, 6.5550])), + (("5G", 3.0, 20.0), np.array([0.0620, 0.5802, 6.5550])), + (("5G", 3.0, 22.0), np.array([0.0340, 0.6011, 6.5550])), + (("7.5G", 3.0, 2.0), np.array([0.2890, 0.3391, 6.5550])), + (("7.5G", 3.0, 4.0), np.array([0.2618, 0.3667, 6.5550])), + (("7.5G", 3.0, 6.0), np.array([0.2346, 0.3901, 6.5550])), + (("7.5G", 3.0, 8.0), np.array([0.2088, 0.4101, 6.5550])), + (("7.5G", 3.0, 10.0), np.array([0.1800, 0.4310, 6.5550])), + (("7.5G", 3.0, 12.0), np.array([0.1516, 0.4505, 6.5550])), + (("7.5G", 3.0, 14.0), np.array([0.1262, 0.4667, 6.5550])), + (("7.5G", 3.0, 16.0), np.array([0.1023, 0.4818, 6.5550])), + (("7.5G", 3.0, 18.0), np.array([0.0798, 0.4954, 6.5550])), + (("7.5G", 3.0, 20.0), np.array([0.0568, 0.5082, 6.5550])), + (("7.5G", 3.0, 22.0), np.array([0.0332, 0.5206, 6.5550])), + (("10G", 3.0, 2.0), np.array([0.2844, 0.3337, 6.5550])), + (("10G", 3.0, 4.0), np.array([0.2525, 0.3537, 6.5550])), + (("10G", 3.0, 6.0), np.array([0.2240, 0.3699, 6.5550])), + (("10G", 3.0, 8.0), np.array([0.1970, 0.3841, 6.5550])), + (("10G", 3.0, 10.0), np.array([0.1688, 0.3974, 6.5550])), + (("10G", 3.0, 12.0), np.array([0.1411, 0.4095, 6.5550])), + (("10G", 3.0, 14.0), np.array([0.1161, 0.4192, 6.5550])), + (("10G", 3.0, 16.0), np.array([0.0925, 0.4275, 6.5550])), + (("10G", 3.0, 18.0), np.array([0.0718, 0.4340, 6.5550])), + (("10G", 3.0, 20.0), np.array([0.0528, 0.4393, 6.5550])), + (("10G", 3.0, 22.0), np.array([0.0333, 0.4444, 6.5550])), + (("2.5BG", 3.0, 2.0), np.array([0.2799, 0.3271, 6.5550])), + (("2.5BG", 3.0, 4.0), np.array([0.2437, 0.3386, 6.5550])), + (("2.5BG", 3.0, 6.0), np.array([0.2132, 0.3468, 6.5550])), + (("2.5BG", 3.0, 8.0), np.array([0.1845, 0.3531, 6.5550])), + (("2.5BG", 3.0, 10.0), np.array([0.1552, 0.3580, 6.5550])), + (("2.5BG", 3.0, 12.0), np.array([0.1288, 0.3620, 6.5550])), + (("2.5BG", 3.0, 14.0), np.array([0.1051, 0.3648, 6.5550])), + (("2.5BG", 3.0, 16.0), np.array([0.0843, 0.3667, 6.5550])), + (("2.5BG", 3.0, 18.0), np.array([0.0648, 0.3682, 6.5550])), + (("2.5BG", 3.0, 20.0), np.array([0.0482, 0.3695, 6.5550])), + (("5BG", 3.0, 2.0), np.array([0.2742, 0.3192, 6.5550])), + (("5BG", 3.0, 4.0), np.array([0.2343, 0.3200, 6.5550])), + (("5BG", 3.0, 6.0), np.array([0.2020, 0.3188, 6.5550])), + (("5BG", 3.0, 8.0), np.array([0.1703, 0.3159, 6.5550])), + (("5BG", 3.0, 10.0), np.array([0.1410, 0.3118, 6.5550])), + (("5BG", 3.0, 12.0), np.array([0.1158, 0.3071, 6.5550])), + (("5BG", 3.0, 14.0), np.array([0.0940, 0.3027, 6.5550])), + (("5BG", 3.0, 16.0), np.array([0.0735, 0.2979, 6.5550])), + (("5BG", 3.0, 18.0), np.array([0.0580, 0.2940, 6.5550])), + (("7.5BG", 3.0, 2.0), np.array([0.2699, 0.3120, 6.5550])), + (("7.5BG", 3.0, 4.0), np.array([0.2272, 0.3041, 6.5550])), + (("7.5BG", 3.0, 6.0), np.array([0.1928, 0.2958, 6.5550])), + (("7.5BG", 3.0, 8.0), np.array([0.1620, 0.2872, 6.5550])), + (("7.5BG", 3.0, 10.0), np.array([0.1326, 0.2784, 6.5550])), + (("7.5BG", 3.0, 12.0), np.array([0.1086, 0.2706, 6.5550])), + (("7.5BG", 3.0, 14.0), np.array([0.0874, 0.2627, 6.5550])), + (("7.5BG", 3.0, 16.0), np.array([0.0691, 0.2559, 6.5550])), + (("10BG", 3.0, 2.0), np.array([0.2660, 0.3050, 6.5550])), + (("10BG", 3.0, 4.0), np.array([0.2221, 0.2886, 6.5550])), + (("10BG", 3.0, 6.0), np.array([0.1861, 0.2722, 6.5550])), + (("10BG", 3.0, 8.0), np.array([0.1551, 0.2571, 6.5550])), + (("10BG", 3.0, 10.0), np.array([0.1250, 0.2411, 6.5550])), + (("10BG", 3.0, 12.0), np.array([0.1018, 0.2281, 6.5550])), + (("10BG", 3.0, 14.0), np.array([0.0798, 0.2151, 6.5550])), + (("2.5B", 3.0, 2.0), np.array([0.2636, 0.2983, 6.5550])), + (("2.5B", 3.0, 4.0), np.array([0.2183, 0.2748, 6.5550])), + (("2.5B", 3.0, 6.0), np.array([0.1826, 0.2536, 6.5550])), + (("2.5B", 3.0, 8.0), np.array([0.1511, 0.2331, 6.5550])), + (("2.5B", 3.0, 10.0), np.array([0.1220, 0.2132, 6.5550])), + (("2.5B", 3.0, 12.0), np.array([0.0989, 0.1963, 6.5550])), + (("5B", 3.0, 2.0), np.array([0.2617, 0.2921, 6.5550])), + (("5B", 3.0, 4.0), np.array([0.2176, 0.2632, 6.5550])), + (("5B", 3.0, 6.0), np.array([0.1835, 0.2375, 6.5550])), + (("5B", 3.0, 8.0), np.array([0.1527, 0.2119, 6.5550])), + (("5B", 3.0, 10.0), np.array([0.1259, 0.1879, 6.5550])), + (("5B", 3.0, 12.0), np.array([0.1042, 0.1681, 6.5550])), + (("7.5B", 3.0, 2.0), np.array([0.2616, 0.2857, 6.5550])), + (("7.5B", 3.0, 4.0), np.array([0.2200, 0.2536, 6.5550])), + (("7.5B", 3.0, 6.0), np.array([0.1875, 0.2258, 6.5550])), + (("7.5B", 3.0, 8.0), np.array([0.1583, 0.1987, 6.5550])), + (("7.5B", 3.0, 10.0), np.array([0.1343, 0.1756, 6.5550])), + (("7.5B", 3.0, 12.0), np.array([0.1131, 0.1542, 6.5550])), + (("10B", 3.0, 2.0), np.array([0.2631, 0.2801, 6.5550])), + (("10B", 3.0, 4.0), np.array([0.2246, 0.2467, 6.5550])), + (("10B", 3.0, 6.0), np.array([0.1933, 0.2173, 6.5550])), + (("10B", 3.0, 8.0), np.array([0.1658, 0.1905, 6.5550])), + (("10B", 3.0, 10.0), np.array([0.1432, 0.1675, 6.5550])), + (("10B", 3.0, 12.0), np.array([0.1228, 0.1460, 6.5550])), + (("10B", 3.0, 14.0), np.array([0.1065, 0.1285, 6.5550])), + (("2.5PB", 3.0, 2.0), np.array([0.2663, 0.2756, 6.5550])), + (("2.5PB", 3.0, 4.0), np.array([0.2312, 0.2405, 6.5550])), + (("2.5PB", 3.0, 6.0), np.array([0.2022, 0.2101, 6.5550])), + (("2.5PB", 3.0, 8.0), np.array([0.1780, 0.1833, 6.5550])), + (("2.5PB", 3.0, 10.0), np.array([0.1576, 0.1600, 6.5550])), + (("2.5PB", 3.0, 12.0), np.array([0.1398, 0.1395, 6.5550])), + (("2.5PB", 3.0, 14.0), np.array([0.1251, 0.1218, 6.5550])), + (("5PB", 3.0, 2.0), np.array([0.2708, 0.2719, 6.5550])), + (("5PB", 3.0, 4.0), np.array([0.2393, 0.2361, 6.5550])), + (("5PB", 3.0, 6.0), np.array([0.2122, 0.2052, 6.5550])), + (("5PB", 3.0, 8.0), np.array([0.1908, 0.1799, 6.5550])), + (("5PB", 3.0, 10.0), np.array([0.1718, 0.1562, 6.5550])), + (("5PB", 3.0, 12.0), np.array([0.1557, 0.1356, 6.5550])), + (("5PB", 3.0, 14.0), np.array([0.1431, 0.1184, 6.5550])), + (("5PB", 3.0, 16.0), np.array([0.1318, 0.1024, 6.5550])), + (("5PB", 3.0, 18.0), np.array([0.1228, 0.0895, 6.5550])), + (("7.5PB", 3.0, 2.0), np.array([0.2777, 0.2687, 6.5550])), + (("7.5PB", 3.0, 4.0), np.array([0.2520, 0.2319, 6.5550])), + (("7.5PB", 3.0, 6.0), np.array([0.2311, 0.2010, 6.5550])), + (("7.5PB", 3.0, 8.0), np.array([0.2149, 0.1761, 6.5550])), + (("7.5PB", 3.0, 10.0), np.array([0.2005, 0.1536, 6.5550])), + (("7.5PB", 3.0, 12.0), np.array([0.1903, 0.1353, 6.5550])), + (("7.5PB", 3.0, 14.0), np.array([0.1824, 0.1188, 6.5550])), + (("7.5PB", 3.0, 16.0), np.array([0.1765, 0.1048, 6.5550])), + (("7.5PB", 3.0, 18.0), np.array([0.1730, 0.0948, 6.5550])), + (("7.5PB", 3.0, 20.0), np.array([0.1702, 0.0867, 6.5550])), + (("7.5PB", 3.0, 22.0), np.array([0.1677, 0.0782, 6.5550])), + (("7.5PB", 3.0, 24.0), np.array([0.1658, 0.0711, 6.5550])), + (("7.5PB", 3.0, 26.0), np.array([0.1642, 0.0655, 6.5550])), + (("7.5PB", 3.0, 28.0), np.array([0.1632, 0.0609, 6.5550])), + (("7.5PB", 3.0, 30.0), np.array([0.1621, 0.0556, 6.5550])), + (("7.5PB", 3.0, 32.0), np.array([0.1612, 0.0511, 6.5550])), + (("7.5PB", 3.0, 34.0), np.array([0.1608, 0.0480, 6.5550])), + (("10PB", 3.0, 2.0), np.array([0.2847, 0.2670, 6.5550])), + (("10PB", 3.0, 4.0), np.array([0.2660, 0.2319, 6.5550])), + (("10PB", 3.0, 6.0), np.array([0.2511, 0.2031, 6.5550])), + (("10PB", 3.0, 8.0), np.array([0.2387, 0.1786, 6.5550])), + (("10PB", 3.0, 10.0), np.array([0.2278, 0.1565, 6.5550])), + (("10PB", 3.0, 12.0), np.array([0.2206, 0.1407, 6.5550])), + (("10PB", 3.0, 14.0), np.array([0.2142, 0.1250, 6.5550])), + (("10PB", 3.0, 16.0), np.array([0.2092, 0.1118, 6.5550])), + (("10PB", 3.0, 18.0), np.array([0.2060, 0.1020, 6.5550])), + (("10PB", 3.0, 20.0), np.array([0.2030, 0.0930, 6.5550])), + (("10PB", 3.0, 22.0), np.array([0.2004, 0.0847, 6.5550])), + (("10PB", 3.0, 24.0), np.array([0.1982, 0.0772, 6.5550])), + (("10PB", 3.0, 26.0), np.array([0.1963, 0.0708, 6.5550])), + (("10PB", 3.0, 28.0), np.array([0.1950, 0.0650, 6.5550])), + (("10PB", 3.0, 30.0), np.array([0.1938, 0.0599, 6.5550])), + (("10PB", 3.0, 32.0), np.array([0.1926, 0.0542, 6.5550])), + (("10PB", 3.0, 34.0), np.array([0.1918, 0.0503, 6.5550])), + (("2.5P", 3.0, 2.0), np.array([0.2922, 0.2680, 6.5550])), + (("2.5P", 3.0, 4.0), np.array([0.2792, 0.2342, 6.5550])), + (("2.5P", 3.0, 6.0), np.array([0.2691, 0.2072, 6.5550])), + (("2.5P", 3.0, 8.0), np.array([0.2615, 0.1845, 6.5550])), + (("2.5P", 3.0, 10.0), np.array([0.2548, 0.1638, 6.5550])), + (("2.5P", 3.0, 12.0), np.array([0.2498, 0.1480, 6.5550])), + (("2.5P", 3.0, 14.0), np.array([0.2449, 0.1325, 6.5550])), + (("2.5P", 3.0, 16.0), np.array([0.2410, 0.1198, 6.5550])), + (("2.5P", 3.0, 18.0), np.array([0.2380, 0.1094, 6.5550])), + (("2.5P", 3.0, 20.0), np.array([0.2354, 0.1003, 6.5550])), + (("2.5P", 3.0, 22.0), np.array([0.2329, 0.0911, 6.5550])), + (("2.5P", 3.0, 24.0), np.array([0.2305, 0.0832, 6.5550])), + (("2.5P", 3.0, 26.0), np.array([0.2286, 0.0765, 6.5550])), + (("2.5P", 3.0, 28.0), np.array([0.2268, 0.0698, 6.5550])), + (("2.5P", 3.0, 30.0), np.array([0.2252, 0.0638, 6.5550])), + (("2.5P", 3.0, 32.0), np.array([0.2242, 0.0587, 6.5550])), + (("2.5P", 3.0, 34.0), np.array([0.2230, 0.0543, 6.5550])), + (("5P", 3.0, 2.0), np.array([0.2997, 0.2700, 6.5550])), + (("5P", 3.0, 4.0), np.array([0.2928, 0.2386, 6.5550])), + (("5P", 3.0, 6.0), np.array([0.2870, 0.2135, 6.5550])), + (("5P", 3.0, 8.0), np.array([0.2819, 0.1910, 6.5550])), + (("5P", 3.0, 10.0), np.array([0.2772, 0.1707, 6.5550])), + (("5P", 3.0, 12.0), np.array([0.2739, 0.1539, 6.5550])), + (("5P", 3.0, 14.0), np.array([0.2707, 0.1397, 6.5550])), + (("5P", 3.0, 16.0), np.array([0.2680, 0.1272, 6.5550])), + (("5P", 3.0, 18.0), np.array([0.2657, 0.1163, 6.5550])), + (("5P", 3.0, 20.0), np.array([0.2639, 0.1074, 6.5550])), + (("5P", 3.0, 22.0), np.array([0.2620, 0.0978, 6.5550])), + (("5P", 3.0, 24.0), np.array([0.2602, 0.0891, 6.5550])), + (("5P", 3.0, 26.0), np.array([0.2590, 0.0822, 6.5550])), + (("5P", 3.0, 28.0), np.array([0.2579, 0.0750, 6.5550])), + (("5P", 3.0, 30.0), np.array([0.2568, 0.0690, 6.5550])), + (("5P", 3.0, 32.0), np.array([0.2557, 0.0630, 6.5550])), + (("7.5P", 3.0, 2.0), np.array([0.3088, 0.2740, 6.5550])), + (("7.5P", 3.0, 4.0), np.array([0.3072, 0.2448, 6.5550])), + (("7.5P", 3.0, 6.0), np.array([0.3057, 0.2208, 6.5550])), + (("7.5P", 3.0, 8.0), np.array([0.3037, 0.1981, 6.5550])), + (("7.5P", 3.0, 10.0), np.array([0.3020, 0.1794, 6.5550])), + (("7.5P", 3.0, 12.0), np.array([0.3003, 0.1618, 6.5550])), + (("7.5P", 3.0, 14.0), np.array([0.2992, 0.1475, 6.5550])), + (("7.5P", 3.0, 16.0), np.array([0.2981, 0.1356, 6.5550])), + (("7.5P", 3.0, 18.0), np.array([0.2969, 0.1239, 6.5550])), + (("7.5P", 3.0, 20.0), np.array([0.2961, 0.1151, 6.5550])), + (("7.5P", 3.0, 22.0), np.array([0.2953, 0.1057, 6.5550])), + (("7.5P", 3.0, 24.0), np.array([0.2944, 0.0967, 6.5550])), + (("7.5P", 3.0, 26.0), np.array([0.2938, 0.0892, 6.5550])), + (("7.5P", 3.0, 28.0), np.array([0.2930, 0.0812, 6.5550])), + (("7.5P", 3.0, 30.0), np.array([0.2922, 0.0750, 6.5550])), + (("10P", 3.0, 2.0), np.array([0.3170, 0.2790, 6.5550])), + (("10P", 3.0, 4.0), np.array([0.3214, 0.2517, 6.5550])), + (("10P", 3.0, 6.0), np.array([0.3243, 0.2293, 6.5550])), + (("10P", 3.0, 8.0), np.array([0.3269, 0.2075, 6.5550])), + (("10P", 3.0, 10.0), np.array([0.3286, 0.1889, 6.5550])), + (("10P", 3.0, 12.0), np.array([0.3301, 0.1715, 6.5550])), + (("10P", 3.0, 14.0), np.array([0.3309, 0.1572, 6.5550])), + (("10P", 3.0, 16.0), np.array([0.3320, 0.1456, 6.5550])), + (("10P", 3.0, 18.0), np.array([0.3329, 0.1332, 6.5550])), + (("10P", 3.0, 20.0), np.array([0.3332, 0.1240, 6.5550])), + (("10P", 3.0, 22.0), np.array([0.3340, 0.1146, 6.5550])), + (("10P", 3.0, 24.0), np.array([0.3341, 0.1055, 6.5550])), + (("10P", 3.0, 26.0), np.array([0.3343, 0.0978, 6.5550])), + (("2.5RP", 3.0, 2.0), np.array([0.3272, 0.2861, 6.5550])), + (("2.5RP", 3.0, 4.0), np.array([0.3400, 0.2624, 6.5550])), + (("2.5RP", 3.0, 6.0), np.array([0.3501, 0.2425, 6.5550])), + (("2.5RP", 3.0, 8.0), np.array([0.3598, 0.2233, 6.5550])), + (("2.5RP", 3.0, 10.0), np.array([0.3681, 0.2054, 6.5550])), + (("2.5RP", 3.0, 12.0), np.array([0.3754, 0.1898, 6.5550])), + (("2.5RP", 3.0, 14.0), np.array([0.3818, 0.1758, 6.5550])), + (("2.5RP", 3.0, 16.0), np.array([0.3876, 0.1629, 6.5550])), + (("2.5RP", 3.0, 18.0), np.array([0.3929, 0.1506, 6.5550])), + (("2.5RP", 3.0, 20.0), np.array([0.3969, 0.1413, 6.5550])), + (("2.5RP", 3.0, 22.0), np.array([0.4018, 0.1304, 6.5550])), + (("5RP", 3.0, 2.0), np.array([0.3370, 0.2940, 6.5550])), + (("5RP", 3.0, 4.0), np.array([0.3586, 0.2742, 6.5550])), + (("5RP", 3.0, 6.0), np.array([0.3765, 0.2569, 6.5550])), + (("5RP", 3.0, 8.0), np.array([0.3930, 0.2395, 6.5550])), + (("5RP", 3.0, 10.0), np.array([0.4073, 0.2235, 6.5550])), + (("5RP", 3.0, 12.0), np.array([0.4199, 0.2089, 6.5550])), + (("5RP", 3.0, 14.0), np.array([0.4313, 0.1944, 6.5550])), + (("5RP", 3.0, 16.0), np.array([0.4418, 0.1809, 6.5550])), + (("5RP", 3.0, 18.0), np.array([0.4503, 0.1695, 6.5550])), + (("5RP", 3.0, 20.0), np.array([0.4577, 0.1593, 6.5550])), + (("7.5RP", 3.0, 2.0), np.array([0.3450, 0.3001, 6.5550])), + (("7.5RP", 3.0, 4.0), np.array([0.3739, 0.2851, 6.5550])), + (("7.5RP", 3.0, 6.0), np.array([0.3990, 0.2708, 6.5550])), + (("7.5RP", 3.0, 8.0), np.array([0.4234, 0.2556, 6.5550])), + (("7.5RP", 3.0, 10.0), np.array([0.4445, 0.2419, 6.5550])), + (("7.5RP", 3.0, 12.0), np.array([0.4654, 0.2273, 6.5550])), + (("7.5RP", 3.0, 14.0), np.array([0.4831, 0.2140, 6.5550])), + (("7.5RP", 3.0, 16.0), np.array([0.4991, 0.2011, 6.5550])), + (("7.5RP", 3.0, 18.0), np.array([0.5130, 0.1893, 6.5550])), + (("10RP", 4.0, 2.0), np.array([0.3417, 0.3106, 12.0000])), + (("10RP", 4.0, 4.0), np.array([0.3715, 0.3042, 12.0000])), + (("10RP", 4.0, 6.0), np.array([0.3999, 0.2972, 12.0000])), + (("10RP", 4.0, 8.0), np.array([0.4282, 0.2890, 12.0000])), + (("10RP", 4.0, 10.0), np.array([0.4528, 0.2811, 12.0000])), + (("10RP", 4.0, 12.0), np.array([0.4789, 0.2717, 12.0000])), + (("10RP", 4.0, 14.0), np.array([0.5020, 0.2623, 12.0000])), + (("10RP", 4.0, 16.0), np.array([0.5234, 0.2530, 12.0000])), + (("10RP", 4.0, 18.0), np.array([0.5466, 0.2424, 12.0000])), + (("10RP", 4.0, 20.0), np.array([0.5674, 0.2319, 12.0000])), + (("2.5R", 4.0, 2.0), np.array([0.3461, 0.3150, 12.0000])), + (("2.5R", 4.0, 4.0), np.array([0.3806, 0.3125, 12.0000])), + (("2.5R", 4.0, 6.0), np.array([0.4141, 0.3085, 12.0000])), + (("2.5R", 4.0, 8.0), np.array([0.4472, 0.3031, 12.0000])), + (("2.5R", 4.0, 10.0), np.array([0.4774, 0.2969, 12.0000])), + (("2.5R", 4.0, 12.0), np.array([0.5072, 0.2897, 12.0000])), + (("2.5R", 4.0, 14.0), np.array([0.5369, 0.2810, 12.0000])), + (("2.5R", 4.0, 16.0), np.array([0.5620, 0.2724, 12.0000])), + (("2.5R", 4.0, 18.0), np.array([0.5898, 0.2622, 12.0000])), + (("5R", 4.0, 2.0), np.array([0.3508, 0.3200, 12.0000])), + (("5R", 4.0, 4.0), np.array([0.3916, 0.3223, 12.0000])), + (("5R", 4.0, 6.0), np.array([0.4299, 0.3226, 12.0000])), + (("5R", 4.0, 8.0), np.array([0.4690, 0.3209, 12.0000])), + (("5R", 4.0, 10.0), np.array([0.5043, 0.3176, 12.0000])), + (("5R", 4.0, 12.0), np.array([0.5385, 0.3129, 12.0000])), + (("5R", 4.0, 14.0), np.array([0.5734, 0.3057, 12.0000])), + (("5R", 4.0, 16.0), np.array([0.6039, 0.2978, 12.0000])), + (("5R", 4.0, 18.0), np.array([0.6329, 0.2881, 12.0000])), + (("7.5R", 4.0, 2.0), np.array([0.3538, 0.3236, 12.0000])), + (("7.5R", 4.0, 4.0), np.array([0.3990, 0.3300, 12.0000])), + (("7.5R", 4.0, 6.0), np.array([0.4415, 0.3340, 12.0000])), + (("7.5R", 4.0, 8.0), np.array([0.4850, 0.3359, 12.0000])), + (("7.5R", 4.0, 10.0), np.array([0.5235, 0.3351, 12.0000])), + (("7.5R", 4.0, 12.0), np.array([0.5603, 0.3321, 12.0000])), + (("7.5R", 4.0, 14.0), np.array([0.5959, 0.3269, 12.0000])), + (("7.5R", 4.0, 16.0), np.array([0.6260, 0.3192, 12.0000])), + (("7.5R", 4.0, 18.0), np.array([0.6538, 0.3100, 12.0000])), + (("7.5R", 4.0, 20.0), np.array([0.6806, 0.2988, 12.0000])), + (("10R", 4.0, 2.0), np.array([0.3582, 0.3294, 12.0000])), + (("10R", 4.0, 4.0), np.array([0.4078, 0.3412, 12.0000])), + (("10R", 4.0, 6.0), np.array([0.4535, 0.3500, 12.0000])), + (("10R", 4.0, 8.0), np.array([0.4995, 0.3557, 12.0000])), + (("10R", 4.0, 10.0), np.array([0.5418, 0.3580, 12.0000])), + (("10R", 4.0, 12.0), np.array([0.5801, 0.3588, 12.0000])), + (("10R", 4.0, 14.0), np.array([0.6154, 0.3568, 12.0000])), + (("10R", 4.0, 16.0), np.array([0.6409, 0.3533, 12.0000])), + (("2.5YR", 4.0, 2.0), np.array([0.3624, 0.3367, 12.0000])), + (("2.5YR", 4.0, 4.0), np.array([0.4141, 0.3539, 12.0000])), + (("2.5YR", 4.0, 6.0), np.array([0.4612, 0.3674, 12.0000])), + (("2.5YR", 4.0, 8.0), np.array([0.5071, 0.3777, 12.0000])), + (("2.5YR", 4.0, 10.0), np.array([0.5475, 0.3856, 12.0000])), + (("2.5YR", 4.0, 12.0), np.array([0.5809, 0.3910, 12.0000])), + (("5YR", 4.0, 2.0), np.array([0.3651, 0.3442, 12.0000])), + (("5YR", 4.0, 4.0), np.array([0.4187, 0.3679, 12.0000])), + (("5YR", 4.0, 6.0), np.array([0.4651, 0.3859, 12.0000])), + (("5YR", 4.0, 8.0), np.array([0.5070, 0.3994, 12.0000])), + (("5YR", 4.0, 10.0), np.array([0.5432, 0.4097, 12.0000])), + (("5YR", 4.0, 12.0), np.array([0.5729, 0.4169, 12.0000])), + (("7.5YR", 4.0, 2.0), np.array([0.3662, 0.3504, 12.0000])), + (("7.5YR", 4.0, 4.0), np.array([0.4208, 0.3809, 12.0000])), + (("7.5YR", 4.0, 6.0), np.array([0.4655, 0.4029, 12.0000])), + (("7.5YR", 4.0, 8.0), np.array([0.5038, 0.4204, 12.0000])), + (("7.5YR", 4.0, 10.0), np.array([0.5356, 0.4342, 12.0000])), + (("10YR", 4.0, 2.0), np.array([0.3660, 0.3590, 12.0000])), + (("10YR", 4.0, 4.0), np.array([0.4189, 0.3948, 12.0000])), + (("10YR", 4.0, 6.0), np.array([0.4618, 0.4213, 12.0000])), + (("10YR", 4.0, 8.0), np.array([0.4965, 0.4414, 12.0000])), + (("10YR", 4.0, 10.0), np.array([0.5250, 0.4573, 12.0000])), + (("2.5Y", 4.0, 2.0), np.array([0.3633, 0.3654, 12.0000])), + (("2.5Y", 4.0, 4.0), np.array([0.4138, 0.4076, 12.0000])), + (("2.5Y", 4.0, 6.0), np.array([0.4542, 0.4391, 12.0000])), + (("2.5Y", 4.0, 8.0), np.array([0.4865, 0.4625, 12.0000])), + (("2.5Y", 4.0, 10.0), np.array([0.5120, 0.4800, 12.0000])), + (("5Y", 4.0, 2.0), np.array([0.3590, 0.3701, 12.0000])), + (("5Y", 4.0, 4.0), np.array([0.4069, 0.4188, 12.0000])), + (("5Y", 4.0, 6.0), np.array([0.4451, 0.4550, 12.0000])), + (("5Y", 4.0, 8.0), np.array([0.4745, 0.4810, 12.0000])), + (("7.5Y", 4.0, 2.0), np.array([0.3542, 0.3727, 12.0000])), + (("7.5Y", 4.0, 4.0), np.array([0.3982, 0.4272, 12.0000])), + (("7.5Y", 4.0, 6.0), np.array([0.4331, 0.4688, 12.0000])), + (("7.5Y", 4.0, 8.0), np.array([0.4595, 0.4990, 12.0000])), + (("10Y", 4.0, 2.0), np.array([0.3436, 0.3732, 12.0000])), + (("10Y", 4.0, 4.0), np.array([0.3871, 0.4321, 12.0000])), + (("10Y", 4.0, 6.0), np.array([0.4190, 0.4795, 12.0000])), + (("10Y", 4.0, 8.0), np.array([0.4430, 0.5153, 12.0000])), + (("2.5GY", 4.0, 2.0), np.array([0.3382, 0.3706, 12.0000])), + (("2.5GY", 4.0, 4.0), np.array([0.3708, 0.4329, 12.0000])), + (("2.5GY", 4.0, 6.0), np.array([0.3968, 0.4857, 12.0000])), + (("2.5GY", 4.0, 8.0), np.array([0.4174, 0.5300, 12.0000])), + (("5GY", 4.0, 2.0), np.array([0.3312, 0.3678, 12.0000])), + (("5GY", 4.0, 4.0), np.array([0.3538, 0.4284, 12.0000])), + (("5GY", 4.0, 6.0), np.array([0.3718, 0.4852, 12.0000])), + (("5GY", 4.0, 8.0), np.array([0.3868, 0.5384, 12.0000])), + (("5GY", 4.0, 10.0), np.array([0.3983, 0.5850, 12.0000])), + (("7.5GY", 4.0, 2.0), np.array([0.3185, 0.3604, 12.0000])), + (("7.5GY", 4.0, 4.0), np.array([0.3281, 0.4157, 12.0000])), + (("7.5GY", 4.0, 6.0), np.array([0.3355, 0.4739, 12.0000])), + (("7.5GY", 4.0, 8.0), np.array([0.3400, 0.5348, 12.0000])), + (("7.5GY", 4.0, 10.0), np.array([0.3395, 0.5913, 12.0000])), + (("7.5GY", 4.0, 12.0), np.array([0.3348, 0.6468, 12.0000])), + (("10GY", 4.0, 2.0), np.array([0.3109, 0.3550, 12.0000])), + (("10GY", 4.0, 4.0), np.array([0.3100, 0.4018, 12.0000])), + (("10GY", 4.0, 6.0), np.array([0.3069, 0.4550, 12.0000])), + (("10GY", 4.0, 8.0), np.array([0.3008, 0.5095, 12.0000])), + (("10GY", 4.0, 10.0), np.array([0.2908, 0.5672, 12.0000])), + (("10GY", 4.0, 12.0), np.array([0.2758, 0.6282, 12.0000])), + (("10GY", 4.0, 14.0), np.array([0.2590, 0.6858, 12.0000])), + (("10GY", 4.0, 16.0), np.array([0.2422, 0.7360, 12.0000])), + (("2.5G", 4.0, 2.0), np.array([0.3012, 0.3470, 12.0000])), + (("2.5G", 4.0, 4.0), np.array([0.2891, 0.3821, 12.0000])), + (("2.5G", 4.0, 6.0), np.array([0.2735, 0.4215, 12.0000])), + (("2.5G", 4.0, 8.0), np.array([0.2561, 0.4597, 12.0000])), + (("2.5G", 4.0, 10.0), np.array([0.2355, 0.5006, 12.0000])), + (("2.5G", 4.0, 12.0), np.array([0.2128, 0.5425, 12.0000])), + (("2.5G", 4.0, 14.0), np.array([0.1909, 0.5779, 12.0000])), + (("2.5G", 4.0, 16.0), np.array([0.1682, 0.6111, 12.0000])), + (("2.5G", 4.0, 18.0), np.array([0.1446, 0.6431, 12.0000])), + (("2.5G", 4.0, 20.0), np.array([0.1230, 0.6706, 12.0000])), + (("2.5G", 4.0, 22.0), np.array([0.1009, 0.6975, 12.0000])), + (("2.5G", 4.0, 24.0), np.array([0.0760, 0.7250, 12.0000])), + (("2.5G", 4.0, 26.0), np.array([0.0528, 0.7502, 12.0000])), + (("5G", 4.0, 2.0), np.array([0.2959, 0.3417, 12.0000])), + (("5G", 4.0, 4.0), np.array([0.2781, 0.3704, 12.0000])), + (("5G", 4.0, 6.0), np.array([0.2581, 0.3992, 12.0000])), + (("5G", 4.0, 8.0), np.array([0.2359, 0.4266, 12.0000])), + (("5G", 4.0, 10.0), np.array([0.2115, 0.4532, 12.0000])), + (("5G", 4.0, 12.0), np.array([0.1843, 0.4807, 12.0000])), + (("5G", 4.0, 14.0), np.array([0.1627, 0.5015, 12.0000])), + (("5G", 4.0, 16.0), np.array([0.1402, 0.5214, 12.0000])), + (("5G", 4.0, 18.0), np.array([0.1188, 0.5400, 12.0000])), + (("5G", 4.0, 20.0), np.array([0.1018, 0.5543, 12.0000])), + (("5G", 4.0, 22.0), np.array([0.0841, 0.5684, 12.0000])), + (("5G", 4.0, 24.0), np.array([0.0614, 0.5857, 12.0000])), + (("5G", 4.0, 26.0), np.array([0.0407, 0.6010, 12.0000])), + (("7.5G", 4.0, 2.0), np.array([0.2919, 0.3371, 12.0000])), + (("7.5G", 4.0, 4.0), np.array([0.2702, 0.3602, 12.0000])), + (("7.5G", 4.0, 6.0), np.array([0.2467, 0.3822, 12.0000])), + (("7.5G", 4.0, 8.0), np.array([0.2232, 0.4022, 12.0000])), + (("7.5G", 4.0, 10.0), np.array([0.1989, 0.4219, 12.0000])), + (("7.5G", 4.0, 12.0), np.array([0.1706, 0.4419, 12.0000])), + (("7.5G", 4.0, 14.0), np.array([0.1500, 0.4562, 12.0000])), + (("7.5G", 4.0, 16.0), np.array([0.1293, 0.4703, 12.0000])), + (("7.5G", 4.0, 18.0), np.array([0.1086, 0.4842, 12.0000])), + (("7.5G", 4.0, 20.0), np.array([0.0928, 0.4942, 12.0000])), + (("7.5G", 4.0, 22.0), np.array([0.0770, 0.5040, 12.0000])), + (("7.5G", 4.0, 24.0), np.array([0.0581, 0.5151, 12.0000])), + (("7.5G", 4.0, 26.0), np.array([0.0392, 0.5258, 12.0000])), + (("10G", 4.0, 2.0), np.array([0.2880, 0.3327, 12.0000])), + (("10G", 4.0, 4.0), np.array([0.2628, 0.3498, 12.0000])), + (("10G", 4.0, 6.0), np.array([0.2374, 0.3655, 12.0000])), + (("10G", 4.0, 8.0), np.array([0.2124, 0.3799, 12.0000])), + (("10G", 4.0, 10.0), np.array([0.1876, 0.3933, 12.0000])), + (("10G", 4.0, 12.0), np.array([0.1602, 0.4070, 12.0000])), + (("10G", 4.0, 14.0), np.array([0.1398, 0.4168, 12.0000])), + (("10G", 4.0, 16.0), np.array([0.1212, 0.4245, 12.0000])), + (("10G", 4.0, 18.0), np.array([0.1006, 0.4330, 12.0000])), + (("10G", 4.0, 20.0), np.array([0.0850, 0.4388, 12.0000])), + (("10G", 4.0, 22.0), np.array([0.0702, 0.4440, 12.0000])), + (("10G", 4.0, 24.0), np.array([0.0553, 0.4492, 12.0000])), + (("10G", 4.0, 26.0), np.array([0.0400, 0.4545, 12.0000])), + (("2.5BG", 4.0, 2.0), np.array([0.2840, 0.3270, 12.0000])), + (("2.5BG", 4.0, 4.0), np.array([0.2552, 0.3375, 12.0000])), + (("2.5BG", 4.0, 6.0), np.array([0.2278, 0.3463, 12.0000])), + (("2.5BG", 4.0, 8.0), np.array([0.2006, 0.3540, 12.0000])), + (("2.5BG", 4.0, 10.0), np.array([0.1738, 0.3600, 12.0000])), + (("2.5BG", 4.0, 12.0), np.array([0.1492, 0.3649, 12.0000])), + (("2.5BG", 4.0, 14.0), np.array([0.1283, 0.3688, 12.0000])), + (("2.5BG", 4.0, 16.0), np.array([0.1102, 0.3720, 12.0000])), + (("2.5BG", 4.0, 18.0), np.array([0.0915, 0.3754, 12.0000])), + (("2.5BG", 4.0, 20.0), np.array([0.0768, 0.3773, 12.0000])), + (("2.5BG", 4.0, 22.0), np.array([0.0636, 0.3788, 12.0000])), + (("2.5BG", 4.0, 24.0), np.array([0.0510, 0.3800, 12.0000])), + (("5BG", 4.0, 2.0), np.array([0.2799, 0.3208, 12.0000])), + (("5BG", 4.0, 4.0), np.array([0.2480, 0.3232, 12.0000])), + (("5BG", 4.0, 6.0), np.array([0.2182, 0.3240, 12.0000])), + (("5BG", 4.0, 8.0), np.array([0.1890, 0.3234, 12.0000])), + (("5BG", 4.0, 10.0), np.array([0.1618, 0.3219, 12.0000])), + (("5BG", 4.0, 12.0), np.array([0.1379, 0.3198, 12.0000])), + (("5BG", 4.0, 14.0), np.array([0.1170, 0.3170, 12.0000])), + (("5BG", 4.0, 16.0), np.array([0.0992, 0.3141, 12.0000])), + (("5BG", 4.0, 18.0), np.array([0.0828, 0.3108, 12.0000])), + (("5BG", 4.0, 20.0), np.array([0.0675, 0.3075, 12.0000])), + (("7.5BG", 4.0, 2.0), np.array([0.2764, 0.3148, 12.0000])), + (("7.5BG", 4.0, 4.0), np.array([0.2429, 0.3108, 12.0000])), + (("7.5BG", 4.0, 6.0), np.array([0.2113, 0.3052, 12.0000])), + (("7.5BG", 4.0, 8.0), np.array([0.1815, 0.2985, 12.0000])), + (("7.5BG", 4.0, 10.0), np.array([0.1540, 0.2910, 12.0000])), + (("7.5BG", 4.0, 12.0), np.array([0.1298, 0.2840, 12.0000])), + (("7.5BG", 4.0, 14.0), np.array([0.1092, 0.2774, 12.0000])), + (("7.5BG", 4.0, 16.0), np.array([0.0922, 0.2718, 12.0000])), + (("7.5BG", 4.0, 18.0), np.array([0.0768, 0.2667, 12.0000])), + (("10BG", 4.0, 2.0), np.array([0.2740, 0.3091, 12.0000])), + (("10BG", 4.0, 4.0), np.array([0.2384, 0.2984, 12.0000])), + (("10BG", 4.0, 6.0), np.array([0.2065, 0.2863, 12.0000])), + (("10BG", 4.0, 8.0), np.array([0.1760, 0.2730, 12.0000])), + (("10BG", 4.0, 10.0), np.array([0.1480, 0.2600, 12.0000])), + (("10BG", 4.0, 12.0), np.array([0.1248, 0.2484, 12.0000])), + (("10BG", 4.0, 14.0), np.array([0.1033, 0.2376, 12.0000])), + (("10BG", 4.0, 16.0), np.array([0.0888, 0.2298, 12.0000])), + (("2.5B", 4.0, 2.0), np.array([0.2727, 0.3038, 12.0000])), + (("2.5B", 4.0, 4.0), np.array([0.2360, 0.2872, 12.0000])), + (("2.5B", 4.0, 6.0), np.array([0.2048, 0.2708, 12.0000])), + (("2.5B", 4.0, 8.0), np.array([0.1737, 0.2524, 12.0000])), + (("2.5B", 4.0, 10.0), np.array([0.1463, 0.2354, 12.0000])), + (("2.5B", 4.0, 12.0), np.array([0.1247, 0.2209, 12.0000])), + (("2.5B", 4.0, 14.0), np.array([0.1027, 0.2057, 12.0000])), + (("2.5B", 4.0, 16.0), np.array([0.0900, 0.1973, 12.0000])), + (("5B", 4.0, 2.0), np.array([0.2723, 0.2992, 12.0000])), + (("5B", 4.0, 4.0), np.array([0.2363, 0.2782, 12.0000])), + (("5B", 4.0, 6.0), np.array([0.2060, 0.2572, 12.0000])), + (("5B", 4.0, 8.0), np.array([0.1759, 0.2345, 12.0000])), + (("5B", 4.0, 10.0), np.array([0.1512, 0.2148, 12.0000])), + (("5B", 4.0, 12.0), np.array([0.1299, 0.1963, 12.0000])), + (("5B", 4.0, 14.0), np.array([0.1098, 0.1785, 12.0000])), + (("7.5B", 4.0, 2.0), np.array([0.2733, 0.2947, 12.0000])), + (("7.5B", 4.0, 4.0), np.array([0.2388, 0.2704, 12.0000])), + (("7.5B", 4.0, 6.0), np.array([0.2102, 0.2470, 12.0000])), + (("7.5B", 4.0, 8.0), np.array([0.1821, 0.2232, 12.0000])), + (("7.5B", 4.0, 10.0), np.array([0.1601, 0.2028, 12.0000])), + (("7.5B", 4.0, 12.0), np.array([0.1393, 0.1837, 12.0000])), + (("7.5B", 4.0, 14.0), np.array([0.1204, 0.1655, 12.0000])), + (("10B", 4.0, 2.0), np.array([0.2753, 0.2910, 12.0000])), + (("10B", 4.0, 4.0), np.array([0.2429, 0.2648, 12.0000])), + (("10B", 4.0, 6.0), np.array([0.2157, 0.2407, 12.0000])), + (("10B", 4.0, 8.0), np.array([0.1893, 0.2160, 12.0000])), + (("10B", 4.0, 10.0), np.array([0.1681, 0.1954, 12.0000])), + (("10B", 4.0, 12.0), np.array([0.1487, 0.1760, 12.0000])), + (("10B", 4.0, 14.0), np.array([0.1310, 0.1580, 12.0000])), + (("10B", 4.0, 16.0), np.array([0.1155, 0.1416, 12.0000])), + (("2.5PB", 4.0, 2.0), np.array([0.2782, 0.2876, 12.0000])), + (("2.5PB", 4.0, 4.0), np.array([0.2487, 0.2597, 12.0000])), + (("2.5PB", 4.0, 6.0), np.array([0.2235, 0.2343, 12.0000])), + (("2.5PB", 4.0, 8.0), np.array([0.1995, 0.2094, 12.0000])), + (("2.5PB", 4.0, 10.0), np.array([0.1805, 0.1888, 12.0000])), + (("2.5PB", 4.0, 12.0), np.array([0.1634, 0.1698, 12.0000])), + (("2.5PB", 4.0, 14.0), np.array([0.1473, 0.1513, 12.0000])), + (("2.5PB", 4.0, 16.0), np.array([0.1336, 0.1349, 12.0000])), + (("2.5PB", 4.0, 18.0), np.array([0.1218, 0.1208, 12.0000])), + (("5PB", 4.0, 2.0), np.array([0.2816, 0.2842, 12.0000])), + (("5PB", 4.0, 4.0), np.array([0.2562, 0.2560, 12.0000])), + (("5PB", 4.0, 6.0), np.array([0.2325, 0.2300, 12.0000])), + (("5PB", 4.0, 8.0), np.array([0.2103, 0.2050, 12.0000])), + (("5PB", 4.0, 10.0), np.array([0.1925, 0.1843, 12.0000])), + (("5PB", 4.0, 12.0), np.array([0.1773, 0.1659, 12.0000])), + (("5PB", 4.0, 14.0), np.array([0.1627, 0.1479, 12.0000])), + (("5PB", 4.0, 16.0), np.array([0.1504, 0.1317, 12.0000])), + (("5PB", 4.0, 18.0), np.array([0.1392, 0.1167, 12.0000])), + (("5PB", 4.0, 20.0), np.array([0.1288, 0.1027, 12.0000])), + (("7.5PB", 4.0, 2.0), np.array([0.2861, 0.2819, 12.0000])), + (("7.5PB", 4.0, 4.0), np.array([0.2657, 0.2528, 12.0000])), + (("7.5PB", 4.0, 6.0), np.array([0.2471, 0.2266, 12.0000])), + (("7.5PB", 4.0, 8.0), np.array([0.2304, 0.2023, 12.0000])), + (("7.5PB", 4.0, 10.0), np.array([0.2158, 0.1811, 12.0000])), + (("7.5PB", 4.0, 12.0), np.array([0.2037, 0.1629, 12.0000])), + (("7.5PB", 4.0, 14.0), np.array([0.1941, 0.1468, 12.0000])), + (("7.5PB", 4.0, 16.0), np.array([0.1861, 0.1316, 12.0000])), + (("7.5PB", 4.0, 18.0), np.array([0.1798, 0.1185, 12.0000])), + (("7.5PB", 4.0, 20.0), np.array([0.1742, 0.1058, 12.0000])), + (("7.5PB", 4.0, 22.0), np.array([0.1713, 0.0980, 12.0000])), + (("7.5PB", 4.0, 24.0), np.array([0.1684, 0.0899, 12.0000])), + (("7.5PB", 4.0, 26.0), np.array([0.1659, 0.0825, 12.0000])), + (("10PB", 4.0, 2.0), np.array([0.2911, 0.2804, 12.0000])), + (("10PB", 4.0, 4.0), np.array([0.2759, 0.2522, 12.0000])), + (("10PB", 4.0, 6.0), np.array([0.2618, 0.2263, 12.0000])), + (("10PB", 4.0, 8.0), np.array([0.2497, 0.2038, 12.0000])), + (("10PB", 4.0, 10.0), np.array([0.2388, 0.1837, 12.0000])), + (("10PB", 4.0, 12.0), np.array([0.2298, 0.1659, 12.0000])), + (("10PB", 4.0, 14.0), np.array([0.2220, 0.1503, 12.0000])), + (("10PB", 4.0, 16.0), np.array([0.2170, 0.1373, 12.0000])), + (("10PB", 4.0, 18.0), np.array([0.2120, 0.1256, 12.0000])), + (("10PB", 4.0, 20.0), np.array([0.2075, 0.1140, 12.0000])), + (("10PB", 4.0, 22.0), np.array([0.2048, 0.1064, 12.0000])), + (("10PB", 4.0, 24.0), np.array([0.2020, 0.0985, 12.0000])), + (("10PB", 4.0, 26.0), np.array([0.1994, 0.0904, 12.0000])), + (("10PB", 4.0, 28.0), np.array([0.1971, 0.0840, 12.0000])), + (("10PB", 4.0, 30.0), np.array([0.1952, 0.0778, 12.0000])), + (("2.5P", 4.0, 2.0), np.array([0.2962, 0.2807, 12.0000])), + (("2.5P", 4.0, 4.0), np.array([0.2855, 0.2531, 12.0000])), + (("2.5P", 4.0, 6.0), np.array([0.2763, 0.2300, 12.0000])), + (("2.5P", 4.0, 8.0), np.array([0.2685, 0.2089, 12.0000])), + (("2.5P", 4.0, 10.0), np.array([0.2619, 0.1903, 12.0000])), + (("2.5P", 4.0, 12.0), np.array([0.2559, 0.1730, 12.0000])), + (("2.5P", 4.0, 14.0), np.array([0.2509, 0.1585, 12.0000])), + (("2.5P", 4.0, 16.0), np.array([0.2467, 0.1452, 12.0000])), + (("2.5P", 4.0, 18.0), np.array([0.2430, 0.1332, 12.0000])), + (("2.5P", 4.0, 20.0), np.array([0.2394, 0.1221, 12.0000])), + (("2.5P", 4.0, 22.0), np.array([0.2371, 0.1143, 12.0000])), + (("2.5P", 4.0, 24.0), np.array([0.2348, 0.1062, 12.0000])), + (("2.5P", 4.0, 26.0), np.array([0.2322, 0.0978, 12.0000])), + (("2.5P", 4.0, 28.0), np.array([0.2302, 0.0909, 12.0000])), + (("2.5P", 4.0, 30.0), np.array([0.2285, 0.0847, 12.0000])), + (("2.5P", 4.0, 32.0), np.array([0.2265, 0.0774, 12.0000])), + (("5P", 4.0, 2.0), np.array([0.3022, 0.2825, 12.0000])), + (("5P", 4.0, 4.0), np.array([0.2958, 0.2565, 12.0000])), + (("5P", 4.0, 6.0), np.array([0.2903, 0.2347, 12.0000])), + (("5P", 4.0, 8.0), np.array([0.2855, 0.2150, 12.0000])), + (("5P", 4.0, 10.0), np.array([0.2814, 0.1967, 12.0000])), + (("5P", 4.0, 12.0), np.array([0.2778, 0.1808, 12.0000])), + (("5P", 4.0, 14.0), np.array([0.2747, 0.1660, 12.0000])), + (("5P", 4.0, 16.0), np.array([0.2718, 0.1520, 12.0000])), + (("5P", 4.0, 18.0), np.array([0.2693, 0.1408, 12.0000])), + (("5P", 4.0, 20.0), np.array([0.2670, 0.1300, 12.0000])), + (("5P", 4.0, 22.0), np.array([0.2652, 0.1218, 12.0000])), + (("5P", 4.0, 24.0), np.array([0.2635, 0.1132, 12.0000])), + (("5P", 4.0, 26.0), np.array([0.2618, 0.1052, 12.0000])), + (("5P", 4.0, 28.0), np.array([0.2600, 0.0971, 12.0000])), + (("5P", 4.0, 30.0), np.array([0.2588, 0.0907, 12.0000])), + (("5P", 4.0, 32.0), np.array([0.2574, 0.0833, 12.0000])), + (("7.5P", 4.0, 2.0), np.array([0.3093, 0.2859, 12.0000])), + (("7.5P", 4.0, 4.0), np.array([0.3084, 0.2622, 12.0000])), + (("7.5P", 4.0, 6.0), np.array([0.3076, 0.2416, 12.0000])), + (("7.5P", 4.0, 8.0), np.array([0.3066, 0.2228, 12.0000])), + (("7.5P", 4.0, 10.0), np.array([0.3056, 0.2060, 12.0000])), + (("7.5P", 4.0, 12.0), np.array([0.3045, 0.1905, 12.0000])), + (("7.5P", 4.0, 14.0), np.array([0.3035, 0.1755, 12.0000])), + (("7.5P", 4.0, 16.0), np.array([0.3028, 0.1621, 12.0000])), + (("7.5P", 4.0, 18.0), np.array([0.3016, 0.1500, 12.0000])), + (("7.5P", 4.0, 20.0), np.array([0.3010, 0.1396, 12.0000])), + (("7.5P", 4.0, 22.0), np.array([0.3001, 0.1306, 12.0000])), + (("7.5P", 4.0, 24.0), np.array([0.2993, 0.1225, 12.0000])), + (("7.5P", 4.0, 26.0), np.array([0.2986, 0.1135, 12.0000])), + (("7.5P", 4.0, 28.0), np.array([0.2979, 0.1062, 12.0000])), + (("7.5P", 4.0, 30.0), np.array([0.2969, 0.0979, 12.0000])), + (("7.5P", 4.0, 32.0), np.array([0.2962, 0.0906, 12.0000])), + (("10P", 4.0, 2.0), np.array([0.3162, 0.2902, 12.0000])), + (("10P", 4.0, 4.0), np.array([0.3210, 0.2686, 12.0000])), + (("10P", 4.0, 6.0), np.array([0.3248, 0.2493, 12.0000])), + (("10P", 4.0, 8.0), np.array([0.3280, 0.2318, 12.0000])), + (("10P", 4.0, 10.0), np.array([0.3306, 0.2162, 12.0000])), + (("10P", 4.0, 12.0), np.array([0.3331, 0.2014, 12.0000])), + (("10P", 4.0, 14.0), np.array([0.3351, 0.1875, 12.0000])), + (("10P", 4.0, 16.0), np.array([0.3370, 0.1756, 12.0000])), + (("10P", 4.0, 18.0), np.array([0.3386, 0.1626, 12.0000])), + (("10P", 4.0, 20.0), np.array([0.3400, 0.1500, 12.0000])), + (("10P", 4.0, 22.0), np.array([0.3411, 0.1424, 12.0000])), + (("10P", 4.0, 24.0), np.array([0.3421, 0.1337, 12.0000])), + (("10P", 4.0, 26.0), np.array([0.3428, 0.1248, 12.0000])), + (("10P", 4.0, 28.0), np.array([0.3432, 0.1172, 12.0000])), + (("10P", 4.0, 30.0), np.array([0.3440, 0.1080, 12.0000])), + (("2.5RP", 4.0, 2.0), np.array([0.3231, 0.2951, 12.0000])), + (("2.5RP", 4.0, 4.0), np.array([0.3340, 0.2770, 12.0000])), + (("2.5RP", 4.0, 6.0), np.array([0.3442, 0.2595, 12.0000])), + (("2.5RP", 4.0, 8.0), np.array([0.3533, 0.2438, 12.0000])), + (("2.5RP", 4.0, 10.0), np.array([0.3608, 0.2301, 12.0000])), + (("2.5RP", 4.0, 12.0), np.array([0.3683, 0.2162, 12.0000])), + (("2.5RP", 4.0, 14.0), np.array([0.3748, 0.2039, 12.0000])), + (("2.5RP", 4.0, 16.0), np.array([0.3807, 0.1923, 12.0000])), + (("2.5RP", 4.0, 18.0), np.array([0.3865, 0.1802, 12.0000])), + (("2.5RP", 4.0, 20.0), np.array([0.3926, 0.1679, 12.0000])), + (("2.5RP", 4.0, 22.0), np.array([0.3967, 0.1593, 12.0000])), + (("2.5RP", 4.0, 24.0), np.array([0.4011, 0.1504, 12.0000])), + (("2.5RP", 4.0, 26.0), np.array([0.4048, 0.1428, 12.0000])), + (("5RP", 4.0, 2.0), np.array([0.3310, 0.3010, 12.0000])), + (("5RP", 4.0, 4.0), np.array([0.3491, 0.2872, 12.0000])), + (("5RP", 4.0, 6.0), np.array([0.3671, 0.2733, 12.0000])), + (("5RP", 4.0, 8.0), np.array([0.3833, 0.2600, 12.0000])), + (("5RP", 4.0, 10.0), np.array([0.3960, 0.2489, 12.0000])), + (("5RP", 4.0, 12.0), np.array([0.4104, 0.2361, 12.0000])), + (("5RP", 4.0, 14.0), np.array([0.4225, 0.2249, 12.0000])), + (("5RP", 4.0, 16.0), np.array([0.4339, 0.2139, 12.0000])), + (("5RP", 4.0, 18.0), np.array([0.4455, 0.2023, 12.0000])), + (("5RP", 4.0, 20.0), np.array([0.4571, 0.1906, 12.0000])), + (("5RP", 4.0, 22.0), np.array([0.4656, 0.1821, 12.0000])), + (("7.5RP", 4.0, 2.0), np.array([0.3371, 0.3061, 12.0000])), + (("7.5RP", 4.0, 4.0), np.array([0.3612, 0.2963, 12.0000])), + (("7.5RP", 4.0, 6.0), np.array([0.3850, 0.2859, 12.0000])), + (("7.5RP", 4.0, 8.0), np.array([0.4072, 0.2750, 12.0000])), + (("7.5RP", 4.0, 10.0), np.array([0.4259, 0.2651, 12.0000])), + (("7.5RP", 4.0, 12.0), np.array([0.4450, 0.2541, 12.0000])), + (("7.5RP", 4.0, 14.0), np.array([0.4629, 0.2437, 12.0000])), + (("7.5RP", 4.0, 16.0), np.array([0.4799, 0.2329, 12.0000])), + (("7.5RP", 4.0, 18.0), np.array([0.4965, 0.2217, 12.0000])), + (("7.5RP", 4.0, 20.0), np.array([0.5130, 0.2101, 12.0000])), + (("10RP", 5.0, 2.0), np.array([0.3332, 0.3131, 19.7700])), + (("10RP", 5.0, 4.0), np.array([0.3594, 0.3090, 19.7700])), + (("10RP", 5.0, 6.0), np.array([0.3851, 0.3039, 19.7700])), + (("10RP", 5.0, 8.0), np.array([0.4105, 0.2980, 19.7700])), + (("10RP", 5.0, 10.0), np.array([0.4332, 0.2918, 19.7700])), + (("10RP", 5.0, 12.0), np.array([0.4579, 0.2841, 19.7700])), + (("10RP", 5.0, 14.0), np.array([0.4767, 0.2776, 19.7700])), + (("10RP", 5.0, 16.0), np.array([0.4986, 0.2695, 19.7700])), + (("10RP", 5.0, 18.0), np.array([0.5185, 0.2620, 19.7700])), + (("10RP", 5.0, 20.0), np.array([0.5396, 0.2535, 19.7700])), + (("2.5R", 5.0, 2.0), np.array([0.3360, 0.3158, 19.7700])), + (("2.5R", 5.0, 4.0), np.array([0.3660, 0.3148, 19.7700])), + (("2.5R", 5.0, 6.0), np.array([0.3960, 0.3130, 19.7700])), + (("2.5R", 5.0, 8.0), np.array([0.4252, 0.3101, 19.7700])), + (("2.5R", 5.0, 10.0), np.array([0.4533, 0.3058, 19.7700])), + (("2.5R", 5.0, 12.0), np.array([0.4820, 0.3002, 19.7700])), + (("2.5R", 5.0, 14.0), np.array([0.5047, 0.2950, 19.7700])), + (("2.5R", 5.0, 16.0), np.array([0.5300, 0.2880, 19.7700])), + (("2.5R", 5.0, 18.0), np.array([0.5540, 0.2804, 19.7700])), + (("2.5R", 5.0, 20.0), np.array([0.5784, 0.2719, 19.7700])), + (("5R", 5.0, 2.0), np.array([0.3392, 0.3192, 19.7700])), + (("5R", 5.0, 4.0), np.array([0.3740, 0.3220, 19.7700])), + (("5R", 5.0, 6.0), np.array([0.4078, 0.3238, 19.7700])), + (("5R", 5.0, 8.0), np.array([0.4413, 0.3240, 19.7700])), + (("5R", 5.0, 10.0), np.array([0.4747, 0.3227, 19.7700])), + (("5R", 5.0, 12.0), np.array([0.5071, 0.3194, 19.7700])), + (("5R", 5.0, 14.0), np.array([0.5341, 0.3158, 19.7700])), + (("5R", 5.0, 16.0), np.array([0.5637, 0.3102, 19.7700])), + (("5R", 5.0, 18.0), np.array([0.5918, 0.3038, 19.7700])), + (("5R", 5.0, 20.0), np.array([0.6142, 0.2970, 19.7700])), + (("7.5R", 5.0, 2.0), np.array([0.3425, 0.3229, 19.7700])), + (("7.5R", 5.0, 4.0), np.array([0.3806, 0.3294, 19.7700])), + (("7.5R", 5.0, 6.0), np.array([0.4180, 0.3348, 19.7700])), + (("7.5R", 5.0, 8.0), np.array([0.4563, 0.3387, 19.7700])), + (("7.5R", 5.0, 10.0), np.array([0.4927, 0.3399, 19.7700])), + (("7.5R", 5.0, 12.0), np.array([0.5280, 0.3389, 19.7700])), + (("7.5R", 5.0, 14.0), np.array([0.5590, 0.3370, 19.7700])), + (("7.5R", 5.0, 16.0), np.array([0.5901, 0.3331, 19.7700])), + (("7.5R", 5.0, 18.0), np.array([0.6161, 0.3277, 19.7700])), + (("7.5R", 5.0, 20.0), np.array([0.6388, 0.3216, 19.7700])), + (("10R", 5.0, 2.0), np.array([0.3465, 0.3278, 19.7700])), + (("10R", 5.0, 4.0), np.array([0.3879, 0.3398, 19.7700])), + (("10R", 5.0, 6.0), np.array([0.4299, 0.3499, 19.7700])), + (("10R", 5.0, 8.0), np.array([0.4713, 0.3575, 19.7700])), + (("10R", 5.0, 10.0), np.array([0.5113, 0.3630, 19.7700])), + (("10R", 5.0, 12.0), np.array([0.5481, 0.3660, 19.7700])), + (("10R", 5.0, 14.0), np.array([0.5771, 0.3664, 19.7700])), + (("10R", 5.0, 16.0), np.array([0.6037, 0.3657, 19.7700])), + (("10R", 5.0, 18.0), np.array([0.6297, 0.3642, 19.7700])), + (("2.5YR", 5.0, 2.0), np.array([0.3506, 0.3337, 19.7700])), + (("2.5YR", 5.0, 4.0), np.array([0.3925, 0.3494, 19.7700])), + (("2.5YR", 5.0, 6.0), np.array([0.4365, 0.3640, 19.7700])), + (("2.5YR", 5.0, 8.0), np.array([0.4795, 0.3758, 19.7700])), + (("2.5YR", 5.0, 10.0), np.array([0.5175, 0.3844, 19.7700])), + (("2.5YR", 5.0, 12.0), np.array([0.5482, 0.3909, 19.7700])), + (("2.5YR", 5.0, 14.0), np.array([0.5731, 0.3953, 19.7700])), + (("2.5YR", 5.0, 16.0), np.array([0.5933, 0.3989, 19.7700])), + (("5YR", 5.0, 2.0), np.array([0.3530, 0.3395, 19.7700])), + (("5YR", 5.0, 4.0), np.array([0.3968, 0.3614, 19.7700])), + (("5YR", 5.0, 6.0), np.array([0.4420, 0.3808, 19.7700])), + (("5YR", 5.0, 8.0), np.array([0.4830, 0.3960, 19.7700])), + (("5YR", 5.0, 10.0), np.array([0.5161, 0.4064, 19.7700])), + (("5YR", 5.0, 12.0), np.array([0.5422, 0.4141, 19.7700])), + (("5YR", 5.0, 14.0), np.array([0.5642, 0.4201, 19.7700])), + (("7.5YR", 5.0, 2.0), np.array([0.3540, 0.3445, 19.7700])), + (("7.5YR", 5.0, 4.0), np.array([0.3991, 0.3714, 19.7700])), + (("7.5YR", 5.0, 6.0), np.array([0.4440, 0.3954, 19.7700])), + (("7.5YR", 5.0, 8.0), np.array([0.4820, 0.4141, 19.7700])), + (("7.5YR", 5.0, 10.0), np.array([0.5108, 0.4276, 19.7700])), + (("7.5YR", 5.0, 12.0), np.array([0.5335, 0.4378, 19.7700])), + (("7.5YR", 5.0, 14.0), np.array([0.5506, 0.4450, 19.7700])), + (("10YR", 5.0, 2.0), np.array([0.3546, 0.3514, 19.7700])), + (("10YR", 5.0, 4.0), np.array([0.3995, 0.3840, 19.7700])), + (("10YR", 5.0, 6.0), np.array([0.4428, 0.4128, 19.7700])), + (("10YR", 5.0, 8.0), np.array([0.4770, 0.4338, 19.7700])), + (("10YR", 5.0, 10.0), np.array([0.5025, 0.4489, 19.7700])), + (("10YR", 5.0, 12.0), np.array([0.5211, 0.4600, 19.7700])), + (("2.5Y", 5.0, 2.0), np.array([0.3534, 0.3570, 19.7700])), + (("2.5Y", 5.0, 4.0), np.array([0.3968, 0.3954, 19.7700])), + (("2.5Y", 5.0, 6.0), np.array([0.4380, 0.4292, 19.7700])), + (("2.5Y", 5.0, 8.0), np.array([0.4685, 0.4524, 19.7700])), + (("2.5Y", 5.0, 10.0), np.array([0.4905, 0.4683, 19.7700])), + (("2.5Y", 5.0, 12.0), np.array([0.5082, 0.4812, 19.7700])), + (("5Y", 5.0, 2.0), np.array([0.3500, 0.3620, 19.7700])), + (("5Y", 5.0, 4.0), np.array([0.3915, 0.4057, 19.7700])), + (("5Y", 5.0, 6.0), np.array([0.4302, 0.4435, 19.7700])), + (("5Y", 5.0, 8.0), np.array([0.4579, 0.4692, 19.7700])), + (("5Y", 5.0, 10.0), np.array([0.4777, 0.4876, 19.7700])), + (("5Y", 5.0, 12.0), np.array([0.4932, 0.5019, 19.7700])), + (("7.5Y", 5.0, 2.0), np.array([0.3470, 0.3640, 19.7700])), + (("7.5Y", 5.0, 4.0), np.array([0.3850, 0.4120, 19.7700])), + (("7.5Y", 5.0, 6.0), np.array([0.4199, 0.4551, 19.7700])), + (("7.5Y", 5.0, 8.0), np.array([0.4450, 0.4850, 19.7700])), + (("7.5Y", 5.0, 10.0), np.array([0.4632, 0.5057, 19.7700])), + (("7.5Y", 5.0, 12.0), np.array([0.4767, 0.5208, 19.7700])), + (("10Y", 5.0, 2.0), np.array([0.3422, 0.3648, 19.7700])), + (("10Y", 5.0, 4.0), np.array([0.3762, 0.4158, 19.7700])), + (("10Y", 5.0, 6.0), np.array([0.4072, 0.4621, 19.7700])), + (("10Y", 5.0, 8.0), np.array([0.4307, 0.4967, 19.7700])), + (("10Y", 5.0, 10.0), np.array([0.4468, 0.5209, 19.7700])), + (("10Y", 5.0, 12.0), np.array([0.4590, 0.5390, 19.7700])), + (("2.5GY", 5.0, 2.0), np.array([0.3352, 0.3636, 19.7700])), + (("2.5GY", 5.0, 4.0), np.array([0.3621, 0.4143, 19.7700])), + (("2.5GY", 5.0, 6.0), np.array([0.3879, 0.4646, 19.7700])), + (("2.5GY", 5.0, 8.0), np.array([0.4088, 0.5068, 19.7700])), + (("2.5GY", 5.0, 10.0), np.array([0.4224, 0.5369, 19.7700])), + (("2.5GY", 5.0, 12.0), np.array([0.4333, 0.5602, 19.7700])), + (("5GY", 5.0, 2.0), np.array([0.3289, 0.3612, 19.7700])), + (("5GY", 5.0, 4.0), np.array([0.3482, 0.4097, 19.7700])), + (("5GY", 5.0, 6.0), np.array([0.3663, 0.4614, 19.7700])), + (("5GY", 5.0, 8.0), np.array([0.3815, 0.5093, 19.7700])), + (("5GY", 5.0, 10.0), np.array([0.3928, 0.5485, 19.7700])), + (("5GY", 5.0, 12.0), np.array([0.4011, 0.5802, 19.7700])), + (("7.5GY", 5.0, 2.0), np.array([0.3188, 0.3560, 19.7700])), + (("7.5GY", 5.0, 4.0), np.array([0.3274, 0.3994, 19.7700])), + (("7.5GY", 5.0, 6.0), np.array([0.3354, 0.4483, 19.7700])), + (("7.5GY", 5.0, 8.0), np.array([0.3412, 0.4976, 19.7700])), + (("7.5GY", 5.0, 10.0), np.array([0.3451, 0.5490, 19.7700])), + (("7.5GY", 5.0, 12.0), np.array([0.3450, 0.5949, 19.7700])), + (("7.5GY", 5.0, 14.0), np.array([0.3429, 0.6335, 19.7700])), + (("10GY", 5.0, 2.0), np.array([0.3110, 0.3508, 19.7700])), + (("10GY", 5.0, 4.0), np.array([0.3111, 0.3881, 19.7700])), + (("10GY", 5.0, 6.0), np.array([0.3108, 0.4301, 19.7700])), + (("10GY", 5.0, 8.0), np.array([0.3080, 0.4759, 19.7700])), + (("10GY", 5.0, 10.0), np.array([0.3028, 0.5237, 19.7700])), + (("10GY", 5.0, 12.0), np.array([0.2940, 0.5751, 19.7700])), + (("10GY", 5.0, 14.0), np.array([0.2838, 0.6208, 19.7700])), + (("10GY", 5.0, 16.0), np.array([0.2702, 0.6700, 19.7700])), + (("10GY", 5.0, 18.0), np.array([0.2549, 0.7179, 19.7700])), + (("2.5G", 5.0, 2.0), np.array([0.3030, 0.3445, 19.7700])), + (("2.5G", 5.0, 4.0), np.array([0.2943, 0.3735, 19.7700])), + (("2.5G", 5.0, 6.0), np.array([0.2841, 0.4045, 19.7700])), + (("2.5G", 5.0, 8.0), np.array([0.2710, 0.4380, 19.7700])), + (("2.5G", 5.0, 10.0), np.array([0.2565, 0.4705, 19.7700])), + (("2.5G", 5.0, 12.0), np.array([0.2385, 0.5071, 19.7700])), + (("2.5G", 5.0, 14.0), np.array([0.2211, 0.5411, 19.7700])), + (("2.5G", 5.0, 16.0), np.array([0.2005, 0.5759, 19.7700])), + (("2.5G", 5.0, 18.0), np.array([0.1782, 0.6095, 19.7700])), + (("2.5G", 5.0, 20.0), np.array([0.1579, 0.6392, 19.7700])), + (("2.5G", 5.0, 22.0), np.array([0.1377, 0.6674, 19.7700])), + (("2.5G", 5.0, 24.0), np.array([0.1188, 0.6918, 19.7700])), + (("2.5G", 5.0, 26.0), np.array([0.0992, 0.7155, 19.7700])), + (("2.5G", 5.0, 28.0), np.array([0.0794, 0.7385, 19.7700])), + (("5G", 5.0, 2.0), np.array([0.2978, 0.3392, 19.7700])), + (("5G", 5.0, 4.0), np.array([0.2841, 0.3628, 19.7700])), + (("5G", 5.0, 6.0), np.array([0.2690, 0.3860, 19.7700])), + (("5G", 5.0, 8.0), np.array([0.2511, 0.4107, 19.7700])), + (("5G", 5.0, 10.0), np.array([0.2329, 0.4331, 19.7700])), + (("5G", 5.0, 12.0), np.array([0.2104, 0.4578, 19.7700])), + (("5G", 5.0, 14.0), np.array([0.1912, 0.4773, 19.7700])), + (("5G", 5.0, 16.0), np.array([0.1695, 0.4981, 19.7700])), + (("5G", 5.0, 18.0), np.array([0.1489, 0.5171, 19.7700])), + (("5G", 5.0, 20.0), np.array([0.1318, 0.5321, 19.7700])), + (("5G", 5.0, 22.0), np.array([0.1144, 0.5463, 19.7700])), + (("5G", 5.0, 24.0), np.array([0.0953, 0.5628, 19.7700])), + (("5G", 5.0, 26.0), np.array([0.0784, 0.5761, 19.7700])), + (("5G", 5.0, 28.0), np.array([0.0609, 0.5898, 19.7700])), + (("7.5G", 5.0, 2.0), np.array([0.2945, 0.3355, 19.7700])), + (("7.5G", 5.0, 4.0), np.array([0.2775, 0.3545, 19.7700])), + (("7.5G", 5.0, 6.0), np.array([0.2598, 0.3724, 19.7700])), + (("7.5G", 5.0, 8.0), np.array([0.2395, 0.3915, 19.7700])), + (("7.5G", 5.0, 10.0), np.array([0.2200, 0.4082, 19.7700])), + (("7.5G", 5.0, 12.0), np.array([0.1964, 0.4271, 19.7700])), + (("7.5G", 5.0, 14.0), np.array([0.1776, 0.4415, 19.7700])), + (("7.5G", 5.0, 16.0), np.array([0.1571, 0.4561, 19.7700])), + (("7.5G", 5.0, 18.0), np.array([0.1372, 0.4705, 19.7700])), + (("7.5G", 5.0, 20.0), np.array([0.1212, 0.4817, 19.7700])), + (("7.5G", 5.0, 22.0), np.array([0.1050, 0.4927, 19.7700])), + (("7.5G", 5.0, 24.0), np.array([0.0878, 0.5039, 19.7700])), + (("7.5G", 5.0, 26.0), np.array([0.0730, 0.5131, 19.7700])), + (("7.5G", 5.0, 28.0), np.array([0.0585, 0.5224, 19.7700])), + (("10G", 5.0, 2.0), np.array([0.2910, 0.3310, 19.7700])), + (("10G", 5.0, 4.0), np.array([0.2711, 0.3455, 19.7700])), + (("10G", 5.0, 6.0), np.array([0.2519, 0.3587, 19.7700])), + (("10G", 5.0, 8.0), np.array([0.2297, 0.3730, 19.7700])), + (("10G", 5.0, 10.0), np.array([0.2095, 0.3853, 19.7700])), + (("10G", 5.0, 12.0), np.array([0.1852, 0.3992, 19.7700])), + (("10G", 5.0, 14.0), np.array([0.1671, 0.4089, 19.7700])), + (("10G", 5.0, 16.0), np.array([0.1469, 0.4192, 19.7700])), + (("10G", 5.0, 18.0), np.array([0.1275, 0.4288, 19.7700])), + (("10G", 5.0, 20.0), np.array([0.1120, 0.4360, 19.7700])), + (("10G", 5.0, 22.0), np.array([0.0958, 0.4428, 19.7700])), + (("10G", 5.0, 24.0), np.array([0.0811, 0.4491, 19.7700])), + (("10G", 5.0, 26.0), np.array([0.0690, 0.4542, 19.7700])), + (("10G", 5.0, 28.0), np.array([0.0572, 0.4590, 19.7700])), + (("2.5BG", 5.0, 2.0), np.array([0.2880, 0.3270, 19.7700])), + (("2.5BG", 5.0, 4.0), np.array([0.2659, 0.3369, 19.7700])), + (("2.5BG", 5.0, 6.0), np.array([0.2448, 0.3452, 19.7700])), + (("2.5BG", 5.0, 8.0), np.array([0.2205, 0.3537, 19.7700])), + (("2.5BG", 5.0, 10.0), np.array([0.1980, 0.3606, 19.7700])), + (("2.5BG", 5.0, 12.0), np.array([0.1735, 0.3668, 19.7700])), + (("2.5BG", 5.0, 14.0), np.array([0.1559, 0.3708, 19.7700])), + (("2.5BG", 5.0, 16.0), np.array([0.1348, 0.3750, 19.7700])), + (("2.5BG", 5.0, 18.0), np.array([0.1165, 0.3785, 19.7700])), + (("2.5BG", 5.0, 20.0), np.array([0.1005, 0.3814, 19.7700])), + (("2.5BG", 5.0, 22.0), np.array([0.0861, 0.3832, 19.7700])), + (("2.5BG", 5.0, 24.0), np.array([0.0738, 0.3851, 19.7700])), + (("5BG", 5.0, 2.0), np.array([0.2841, 0.3210, 19.7700])), + (("5BG", 5.0, 4.0), np.array([0.2591, 0.3246, 19.7700])), + (("5BG", 5.0, 6.0), np.array([0.2360, 0.3270, 19.7700])), + (("5BG", 5.0, 8.0), np.array([0.2100, 0.3280, 19.7700])), + (("5BG", 5.0, 10.0), np.array([0.1850, 0.3280, 19.7700])), + (("5BG", 5.0, 12.0), np.array([0.1614, 0.3280, 19.7700])), + (("5BG", 5.0, 14.0), np.array([0.1448, 0.3275, 19.7700])), + (("5BG", 5.0, 16.0), np.array([0.1243, 0.3261, 19.7700])), + (("5BG", 5.0, 18.0), np.array([0.1046, 0.3244, 19.7700])), + (("5BG", 5.0, 20.0), np.array([0.0904, 0.3231, 19.7700])), + (("5BG", 5.0, 22.0), np.array([0.0781, 0.3211, 19.7700])), + (("7.5BG", 5.0, 2.0), np.array([0.2812, 0.3161, 19.7700])), + (("7.5BG", 5.0, 4.0), np.array([0.2550, 0.3150, 19.7700])), + (("7.5BG", 5.0, 6.0), np.array([0.2292, 0.3125, 19.7700])), + (("7.5BG", 5.0, 8.0), np.array([0.2030, 0.3082, 19.7700])), + (("7.5BG", 5.0, 10.0), np.array([0.1776, 0.3032, 19.7700])), + (("7.5BG", 5.0, 12.0), np.array([0.1537, 0.2976, 19.7700])), + (("7.5BG", 5.0, 14.0), np.array([0.1364, 0.2932, 19.7700])), + (("7.5BG", 5.0, 16.0), np.array([0.1167, 0.2880, 19.7700])), + (("7.5BG", 5.0, 18.0), np.array([0.0982, 0.2828, 19.7700])), + (("10BG", 5.0, 2.0), np.array([0.2796, 0.3111, 19.7700])), + (("10BG", 5.0, 4.0), np.array([0.2512, 0.3040, 19.7700])), + (("10BG", 5.0, 6.0), np.array([0.2234, 0.2952, 19.7700])), + (("10BG", 5.0, 8.0), np.array([0.1970, 0.2860, 19.7700])), + (("10BG", 5.0, 10.0), np.array([0.1716, 0.2760, 19.7700])), + (("10BG", 5.0, 12.0), np.array([0.1485, 0.2662, 19.7700])), + (("10BG", 5.0, 14.0), np.array([0.1308, 0.2582, 19.7700])), + (("10BG", 5.0, 16.0), np.array([0.1108, 0.2489, 19.7700])), + (("2.5B", 5.0, 2.0), np.array([0.2791, 0.3071, 19.7700])), + (("2.5B", 5.0, 4.0), np.array([0.2492, 0.2954, 19.7700])), + (("2.5B", 5.0, 6.0), np.array([0.2210, 0.2823, 19.7700])), + (("2.5B", 5.0, 8.0), np.array([0.1947, 0.2687, 19.7700])), + (("2.5B", 5.0, 10.0), np.array([0.1697, 0.2549, 19.7700])), + (("2.5B", 5.0, 12.0), np.array([0.1461, 0.2406, 19.7700])), + (("2.5B", 5.0, 14.0), np.array([0.1283, 0.2292, 19.7700])), + (("2.5B", 5.0, 16.0), np.array([0.1090, 0.2166, 19.7700])), + (("5B", 5.0, 2.0), np.array([0.2794, 0.3032, 19.7700])), + (("5B", 5.0, 4.0), np.array([0.2493, 0.2879, 19.7700])), + (("5B", 5.0, 6.0), np.array([0.2215, 0.2701, 19.7700])), + (("5B", 5.0, 8.0), np.array([0.1958, 0.2519, 19.7700])), + (("5B", 5.0, 10.0), np.array([0.1729, 0.2347, 19.7700])), + (("5B", 5.0, 12.0), np.array([0.1505, 0.2172, 19.7700])), + (("5B", 5.0, 14.0), np.array([0.1320, 0.2021, 19.7700])), + (("5B", 5.0, 16.0), np.array([0.1132, 0.1863, 19.7700])), + (("7.5B", 5.0, 2.0), np.array([0.2803, 0.3000, 19.7700])), + (("7.5B", 5.0, 4.0), np.array([0.2511, 0.2808, 19.7700])), + (("7.5B", 5.0, 6.0), np.array([0.2248, 0.2612, 19.7700])), + (("7.5B", 5.0, 8.0), np.array([0.2007, 0.2417, 19.7700])), + (("7.5B", 5.0, 10.0), np.array([0.1792, 0.2230, 19.7700])), + (("7.5B", 5.0, 12.0), np.array([0.1584, 0.2042, 19.7700])), + (("7.5B", 5.0, 14.0), np.array([0.1404, 0.1878, 19.7700])), + (("7.5B", 5.0, 16.0), np.array([0.1230, 0.1711, 19.7700])), + (("10B", 5.0, 2.0), np.array([0.2821, 0.2966, 19.7700])), + (("10B", 5.0, 4.0), np.array([0.2547, 0.2757, 19.7700])), + (("10B", 5.0, 6.0), np.array([0.2299, 0.2548, 19.7700])), + (("10B", 5.0, 8.0), np.array([0.2067, 0.2344, 19.7700])), + (("10B", 5.0, 10.0), np.array([0.1860, 0.2149, 19.7700])), + (("10B", 5.0, 12.0), np.array([0.1666, 0.1964, 19.7700])), + (("10B", 5.0, 14.0), np.array([0.1492, 0.1797, 19.7700])), + (("10B", 5.0, 16.0), np.array([0.1326, 0.1632, 19.7700])), + (("10B", 5.0, 18.0), np.array([0.1203, 0.1505, 19.7700])), + (("2.5PB", 5.0, 2.0), np.array([0.2847, 0.2942, 19.7700])), + (("2.5PB", 5.0, 4.0), np.array([0.2600, 0.2720, 19.7700])), + (("2.5PB", 5.0, 6.0), np.array([0.2365, 0.2488, 19.7700])), + (("2.5PB", 5.0, 8.0), np.array([0.2157, 0.2278, 19.7700])), + (("2.5PB", 5.0, 10.0), np.array([0.1968, 0.2078, 19.7700])), + (("2.5PB", 5.0, 12.0), np.array([0.1793, 0.1894, 19.7700])), + (("2.5PB", 5.0, 14.0), np.array([0.1642, 0.1728, 19.7700])), + (("2.5PB", 5.0, 16.0), np.array([0.1495, 0.1559, 19.7700])), + (("2.5PB", 5.0, 18.0), np.array([0.1363, 0.1410, 19.7700])), + (("5PB", 5.0, 2.0), np.array([0.2882, 0.2923, 19.7700])), + (("5PB", 5.0, 4.0), np.array([0.2662, 0.2687, 19.7700])), + (("5PB", 5.0, 6.0), np.array([0.2447, 0.2449, 19.7700])), + (("5PB", 5.0, 8.0), np.array([0.2255, 0.2239, 19.7700])), + (("5PB", 5.0, 10.0), np.array([0.2080, 0.2041, 19.7700])), + (("5PB", 5.0, 12.0), np.array([0.1918, 0.1858, 19.7700])), + (("5PB", 5.0, 14.0), np.array([0.1773, 0.1689, 19.7700])), + (("5PB", 5.0, 16.0), np.array([0.1638, 0.1521, 19.7700])), + (("5PB", 5.0, 18.0), np.array([0.1518, 0.1365, 19.7700])), + (("7.5PB", 5.0, 2.0), np.array([0.2918, 0.2908, 19.7700])), + (("7.5PB", 5.0, 4.0), np.array([0.2739, 0.2666, 19.7700])), + (("7.5PB", 5.0, 6.0), np.array([0.2563, 0.2417, 19.7700])), + (("7.5PB", 5.0, 8.0), np.array([0.2417, 0.2204, 19.7700])), + (("7.5PB", 5.0, 10.0), np.array([0.2285, 0.2020, 19.7700])), + (("7.5PB", 5.0, 12.0), np.array([0.2157, 0.1830, 19.7700])), + (("7.5PB", 5.0, 14.0), np.array([0.2042, 0.1661, 19.7700])), + (("7.5PB", 5.0, 16.0), np.array([0.1945, 0.1511, 19.7700])), + (("7.5PB", 5.0, 18.0), np.array([0.1862, 0.1365, 19.7700])), + (("7.5PB", 5.0, 20.0), np.array([0.1794, 0.1239, 19.7700])), + (("10PB", 5.0, 2.0), np.array([0.2959, 0.2905, 19.7700])), + (("10PB", 5.0, 4.0), np.array([0.2821, 0.2659, 19.7700])), + (("10PB", 5.0, 6.0), np.array([0.2686, 0.2412, 19.7700])), + (("10PB", 5.0, 8.0), np.array([0.2572, 0.2211, 19.7700])), + (("10PB", 5.0, 10.0), np.array([0.2478, 0.2030, 19.7700])), + (("10PB", 5.0, 12.0), np.array([0.2384, 0.1857, 19.7700])), + (("10PB", 5.0, 14.0), np.array([0.2299, 0.1698, 19.7700])), + (("10PB", 5.0, 16.0), np.array([0.2224, 0.1555, 19.7700])), + (("10PB", 5.0, 18.0), np.array([0.2174, 0.1444, 19.7700])), + (("10PB", 5.0, 20.0), np.array([0.2121, 0.1329, 19.7700])), + (("10PB", 5.0, 22.0), np.array([0.2082, 0.1225, 19.7700])), + (("2.5P", 5.0, 2.0), np.array([0.3000, 0.2912, 19.7700])), + (("2.5P", 5.0, 4.0), np.array([0.2898, 0.2667, 19.7700])), + (("2.5P", 5.0, 6.0), np.array([0.2806, 0.2444, 19.7700])), + (("2.5P", 5.0, 8.0), np.array([0.2728, 0.2240, 19.7700])), + (("2.5P", 5.0, 10.0), np.array([0.2665, 0.2075, 19.7700])), + (("2.5P", 5.0, 12.0), np.array([0.2608, 0.1913, 19.7700])), + (("2.5P", 5.0, 14.0), np.array([0.2560, 0.1774, 19.7700])), + (("2.5P", 5.0, 16.0), np.array([0.2515, 0.1644, 19.7700])), + (("2.5P", 5.0, 18.0), np.array([0.2476, 0.1532, 19.7700])), + (("2.5P", 5.0, 20.0), np.array([0.2438, 0.1419, 19.7700])), + (("2.5P", 5.0, 22.0), np.array([0.2402, 0.1315, 19.7700])), + (("2.5P", 5.0, 24.0), np.array([0.2372, 0.1223, 19.7700])), + (("2.5P", 5.0, 26.0), np.array([0.2348, 0.1140, 19.7700])), + (("5P", 5.0, 2.0), np.array([0.3045, 0.2928, 19.7700])), + (("5P", 5.0, 4.0), np.array([0.2986, 0.2699, 19.7700])), + (("5P", 5.0, 6.0), np.array([0.2932, 0.2487, 19.7700])), + (("5P", 5.0, 8.0), np.array([0.2885, 0.2296, 19.7700])), + (("5P", 5.0, 10.0), np.array([0.2845, 0.2137, 19.7700])), + (("5P", 5.0, 12.0), np.array([0.2806, 0.1977, 19.7700])), + (("5P", 5.0, 14.0), np.array([0.2775, 0.1847, 19.7700])), + (("5P", 5.0, 16.0), np.array([0.2744, 0.1718, 19.7700])), + (("5P", 5.0, 18.0), np.array([0.2718, 0.1604, 19.7700])), + (("5P", 5.0, 20.0), np.array([0.2694, 0.1499, 19.7700])), + (("5P", 5.0, 22.0), np.array([0.2673, 0.1398, 19.7700])), + (("5P", 5.0, 24.0), np.array([0.2652, 0.1304, 19.7700])), + (("5P", 5.0, 26.0), np.array([0.2635, 0.1224, 19.7700])), + (("5P", 5.0, 28.0), np.array([0.2618, 0.1135, 19.7700])), + (("7.5P", 5.0, 2.0), np.array([0.3103, 0.2959, 19.7700])), + (("7.5P", 5.0, 4.0), np.array([0.3100, 0.2750, 19.7700])), + (("7.5P", 5.0, 6.0), np.array([0.3093, 0.2555, 19.7700])), + (("7.5P", 5.0, 8.0), np.array([0.3087, 0.2375, 19.7700])), + (("7.5P", 5.0, 10.0), np.array([0.3080, 0.2230, 19.7700])), + (("7.5P", 5.0, 12.0), np.array([0.3071, 0.2080, 19.7700])), + (("7.5P", 5.0, 14.0), np.array([0.3068, 0.1951, 19.7700])), + (("7.5P", 5.0, 16.0), np.array([0.3060, 0.1830, 19.7700])), + (("7.5P", 5.0, 18.0), np.array([0.3052, 0.1711, 19.7700])), + (("7.5P", 5.0, 20.0), np.array([0.3042, 0.1606, 19.7700])), + (("7.5P", 5.0, 22.0), np.array([0.3038, 0.1500, 19.7700])), + (("7.5P", 5.0, 24.0), np.array([0.3030, 0.1423, 19.7700])), + (("7.5P", 5.0, 26.0), np.array([0.3022, 0.1331, 19.7700])), + (("7.5P", 5.0, 28.0), np.array([0.3018, 0.1253, 19.7700])), + (("7.5P", 5.0, 30.0), np.array([0.3010, 0.1170, 19.7700])), + (("10P", 5.0, 2.0), np.array([0.3148, 0.2986, 19.7700])), + (("10P", 5.0, 4.0), np.array([0.3198, 0.2807, 19.7700])), + (("10P", 5.0, 6.0), np.array([0.3243, 0.2630, 19.7700])), + (("10P", 5.0, 8.0), np.array([0.3280, 0.2464, 19.7700])), + (("10P", 5.0, 10.0), np.array([0.3308, 0.2328, 19.7700])), + (("10P", 5.0, 12.0), np.array([0.3335, 0.2187, 19.7700])), + (("10P", 5.0, 14.0), np.array([0.3360, 0.2066, 19.7700])), + (("10P", 5.0, 16.0), np.array([0.3382, 0.1951, 19.7700])), + (("10P", 5.0, 18.0), np.array([0.3401, 0.1840, 19.7700])), + (("10P", 5.0, 20.0), np.array([0.3422, 0.1735, 19.7700])), + (("10P", 5.0, 22.0), np.array([0.3437, 0.1644, 19.7700])), + (("10P", 5.0, 24.0), np.array([0.3450, 0.1555, 19.7700])), + (("10P", 5.0, 26.0), np.array([0.3468, 0.1460, 19.7700])), + (("10P", 5.0, 28.0), np.array([0.3478, 0.1388, 19.7700])), + (("10P", 5.0, 30.0), np.array([0.3490, 0.1308, 19.7700])), + (("2.5RP", 5.0, 2.0), np.array([0.3199, 0.3019, 19.7700])), + (("2.5RP", 5.0, 4.0), np.array([0.3298, 0.2869, 19.7700])), + (("2.5RP", 5.0, 6.0), np.array([0.3396, 0.2718, 19.7700])), + (("2.5RP", 5.0, 8.0), np.array([0.3490, 0.2570, 19.7700])), + (("2.5RP", 5.0, 10.0), np.array([0.3560, 0.2452, 19.7700])), + (("2.5RP", 5.0, 12.0), np.array([0.3635, 0.2325, 19.7700])), + (("2.5RP", 5.0, 14.0), np.array([0.3703, 0.2211, 19.7700])), + (("2.5RP", 5.0, 16.0), np.array([0.3763, 0.2108, 19.7700])), + (("2.5RP", 5.0, 18.0), np.array([0.3821, 0.2007, 19.7700])), + (("2.5RP", 5.0, 20.0), np.array([0.3873, 0.1909, 19.7700])), + (("2.5RP", 5.0, 22.0), np.array([0.3924, 0.1814, 19.7700])), + (("2.5RP", 5.0, 24.0), np.array([0.3965, 0.1738, 19.7700])), + (("2.5RP", 5.0, 26.0), np.array([0.4011, 0.1652, 19.7700])), + (("5RP", 5.0, 2.0), np.array([0.3256, 0.3065, 19.7700])), + (("5RP", 5.0, 4.0), np.array([0.3421, 0.2954, 19.7700])), + (("5RP", 5.0, 6.0), np.array([0.3585, 0.2842, 19.7700])), + (("5RP", 5.0, 8.0), np.array([0.3748, 0.2729, 19.7700])), + (("5RP", 5.0, 10.0), np.array([0.3880, 0.2630, 19.7700])), + (("5RP", 5.0, 12.0), np.array([0.4022, 0.2523, 19.7700])), + (("5RP", 5.0, 14.0), np.array([0.4142, 0.2428, 19.7700])), + (("5RP", 5.0, 16.0), np.array([0.4261, 0.2331, 19.7700])), + (("5RP", 5.0, 18.0), np.array([0.4372, 0.2242, 19.7700])), + (("5RP", 5.0, 20.0), np.array([0.4484, 0.2150, 19.7700])), + (("5RP", 5.0, 22.0), np.array([0.4581, 0.2068, 19.7700])), + (("5RP", 5.0, 24.0), np.array([0.4683, 0.1978, 19.7700])), + (("7.5RP", 5.0, 2.0), np.array([0.3296, 0.3098, 19.7700])), + (("7.5RP", 5.0, 4.0), np.array([0.3515, 0.3024, 19.7700])), + (("7.5RP", 5.0, 6.0), np.array([0.3726, 0.2941, 19.7700])), + (("7.5RP", 5.0, 8.0), np.array([0.3932, 0.2852, 19.7700])), + (("7.5RP", 5.0, 10.0), np.array([0.4108, 0.2773, 19.7700])), + (("7.5RP", 5.0, 12.0), np.array([0.4303, 0.2675, 19.7700])), + (("7.5RP", 5.0, 14.0), np.array([0.4454, 0.2596, 19.7700])), + (("7.5RP", 5.0, 16.0), np.array([0.4617, 0.2506, 19.7700])), + (("7.5RP", 5.0, 18.0), np.array([0.4761, 0.2421, 19.7700])), + (("7.5RP", 5.0, 20.0), np.array([0.4915, 0.2330, 19.7700])), + (("7.5RP", 5.0, 22.0), np.array([0.5045, 0.2248, 19.7700])), + (("10RP", 6.0, 2.0), np.array([0.3292, 0.3141, 30.0500])), + (("10RP", 6.0, 4.0), np.array([0.3508, 0.3112, 30.0500])), + (("10RP", 6.0, 6.0), np.array([0.3740, 0.3074, 30.0500])), + (("10RP", 6.0, 8.0), np.array([0.3930, 0.3038, 30.0500])), + (("10RP", 6.0, 10.0), np.array([0.4150, 0.2989, 30.0500])), + (("10RP", 6.0, 12.0), np.array([0.4360, 0.2936, 30.0500])), + (("10RP", 6.0, 14.0), np.array([0.4552, 0.2881, 30.0500])), + (("10RP", 6.0, 16.0), np.array([0.4781, 0.2812, 30.0500])), + (("10RP", 6.0, 18.0), np.array([0.4961, 0.2751, 30.0500])), + (("2.5R", 6.0, 2.0), np.array([0.3318, 0.3166, 30.0500])), + (("2.5R", 6.0, 4.0), np.array([0.3566, 0.3163, 30.0500])), + (("2.5R", 6.0, 6.0), np.array([0.3832, 0.3158, 30.0500])), + (("2.5R", 6.0, 8.0), np.array([0.4065, 0.3144, 30.0500])), + (("2.5R", 6.0, 10.0), np.array([0.4320, 0.3118, 30.0500])), + (("2.5R", 6.0, 12.0), np.array([0.4568, 0.3082, 30.0500])), + (("2.5R", 6.0, 14.0), np.array([0.4790, 0.3041, 30.0500])), + (("2.5R", 6.0, 16.0), np.array([0.5041, 0.2983, 30.0500])), + (("2.5R", 6.0, 18.0), np.array([0.5262, 0.2928, 30.0500])), + (("5R", 6.0, 2.0), np.array([0.3343, 0.3190, 30.0500])), + (("5R", 6.0, 4.0), np.array([0.3628, 0.3221, 30.0500])), + (("5R", 6.0, 6.0), np.array([0.3921, 0.3244, 30.0500])), + (("5R", 6.0, 8.0), np.array([0.4187, 0.3251, 30.0500])), + (("5R", 6.0, 10.0), np.array([0.4480, 0.3250, 30.0500])), + (("5R", 6.0, 12.0), np.array([0.4760, 0.3234, 30.0500])), + (("5R", 6.0, 14.0), np.array([0.5020, 0.3212, 30.0500])), + (("5R", 6.0, 16.0), np.array([0.5297, 0.3179, 30.0500])), + (("5R", 6.0, 18.0), np.array([0.5552, 0.3138, 30.0500])), + (("7.5R", 6.0, 2.0), np.array([0.3381, 0.3228, 30.0500])), + (("7.5R", 6.0, 4.0), np.array([0.3692, 0.3291, 30.0500])), + (("7.5R", 6.0, 6.0), np.array([0.4000, 0.3340, 30.0500])), + (("7.5R", 6.0, 8.0), np.array([0.4318, 0.3383, 30.0500])), + (("7.5R", 6.0, 10.0), np.array([0.4655, 0.3412, 30.0500])), + (("7.5R", 6.0, 12.0), np.array([0.4961, 0.3428, 30.0500])), + (("7.5R", 6.0, 14.0), np.array([0.5265, 0.3431, 30.0500])), + (("7.5R", 6.0, 16.0), np.array([0.5560, 0.3420, 30.0500])), + (("7.5R", 6.0, 18.0), np.array([0.5829, 0.3396, 30.0500])), + (("10R", 6.0, 2.0), np.array([0.3417, 0.3268, 30.0500])), + (("10R", 6.0, 4.0), np.array([0.3768, 0.3381, 30.0500])), + (("10R", 6.0, 6.0), np.array([0.4103, 0.3473, 30.0500])), + (("10R", 6.0, 8.0), np.array([0.4449, 0.3550, 30.0500])), + (("10R", 6.0, 10.0), np.array([0.4812, 0.3619, 30.0500])), + (("10R", 6.0, 12.0), np.array([0.5150, 0.3667, 30.0500])), + (("10R", 6.0, 14.0), np.array([0.5468, 0.3697, 30.0500])), + (("10R", 6.0, 16.0), np.array([0.5741, 0.3713, 30.0500])), + (("10R", 6.0, 18.0), np.array([0.6009, 0.3720, 30.0500])), + (("2.5YR", 6.0, 2.0), np.array([0.3453, 0.3321, 30.0500])), + (("2.5YR", 6.0, 4.0), np.array([0.3806, 0.3467, 30.0500])), + (("2.5YR", 6.0, 6.0), np.array([0.4180, 0.3600, 30.0500])), + (("2.5YR", 6.0, 8.0), np.array([0.4533, 0.3708, 30.0500])), + (("2.5YR", 6.0, 10.0), np.array([0.4891, 0.3806, 30.0500])), + (("2.5YR", 6.0, 12.0), np.array([0.5215, 0.3887, 30.0500])), + (("2.5YR", 6.0, 14.0), np.array([0.5488, 0.3947, 30.0500])), + (("2.5YR", 6.0, 16.0), np.array([0.5698, 0.3990, 30.0500])), + (("2.5YR", 6.0, 18.0), np.array([0.5879, 0.4021, 30.0500])), + (("5YR", 6.0, 2.0), np.array([0.3474, 0.3373, 30.0500])), + (("5YR", 6.0, 4.0), np.array([0.3840, 0.3564, 30.0500])), + (("5YR", 6.0, 6.0), np.array([0.4229, 0.3750, 30.0500])), + (("5YR", 6.0, 8.0), np.array([0.4592, 0.3900, 30.0500])), + (("5YR", 6.0, 10.0), np.array([0.4921, 0.4022, 30.0500])), + (("5YR", 6.0, 12.0), np.array([0.5199, 0.4119, 30.0500])), + (("5YR", 6.0, 14.0), np.array([0.5423, 0.4188, 30.0500])), + (("5YR", 6.0, 16.0), np.array([0.5597, 0.4239, 30.0500])), + (("5YR", 6.0, 18.0), np.array([0.5715, 0.4270, 30.0500])), + (("7.5YR", 6.0, 2.0), np.array([0.3487, 0.3421, 30.0500])), + (("7.5YR", 6.0, 4.0), np.array([0.3860, 0.3652, 30.0500])), + (("7.5YR", 6.0, 6.0), np.array([0.4242, 0.3876, 30.0500])), + (("7.5YR", 6.0, 8.0), np.array([0.4596, 0.4064, 30.0500])), + (("7.5YR", 6.0, 10.0), np.array([0.4904, 0.4220, 30.0500])), + (("7.5YR", 6.0, 12.0), np.array([0.5145, 0.4331, 30.0500])), + (("7.5YR", 6.0, 14.0), np.array([0.5320, 0.4412, 30.0500])), + (("7.5YR", 6.0, 16.0), np.array([0.5468, 0.4478, 30.0500])), + (("10YR", 6.0, 2.0), np.array([0.3491, 0.3483, 30.0500])), + (("10YR", 6.0, 4.0), np.array([0.3861, 0.3767, 30.0500])), + (("10YR", 6.0, 6.0), np.array([0.4240, 0.4030, 30.0500])), + (("10YR", 6.0, 8.0), np.array([0.4570, 0.4249, 30.0500])), + (("10YR", 6.0, 10.0), np.array([0.4843, 0.4416, 30.0500])), + (("10YR", 6.0, 12.0), np.array([0.5050, 0.4536, 30.0500])), + (("10YR", 6.0, 14.0), np.array([0.5200, 0.4623, 30.0500])), + (("2.5Y", 6.0, 2.0), np.array([0.3480, 0.3540, 30.0500])), + (("2.5Y", 6.0, 4.0), np.array([0.3840, 0.3867, 30.0500])), + (("2.5Y", 6.0, 6.0), np.array([0.4203, 0.4176, 30.0500])), + (("2.5Y", 6.0, 8.0), np.array([0.4517, 0.4421, 30.0500])), + (("2.5Y", 6.0, 10.0), np.array([0.4760, 0.4607, 30.0500])), + (("2.5Y", 6.0, 12.0), np.array([0.4928, 0.4730, 30.0500])), + (("2.5Y", 6.0, 14.0), np.array([0.5061, 0.4829, 30.0500])), + (("5Y", 6.0, 2.0), np.array([0.3457, 0.3580, 30.0500])), + (("5Y", 6.0, 4.0), np.array([0.3794, 0.3955, 30.0500])), + (("5Y", 6.0, 6.0), np.array([0.4140, 0.4305, 30.0500])), + (("5Y", 6.0, 8.0), np.array([0.4426, 0.4588, 30.0500])), + (("5Y", 6.0, 10.0), np.array([0.4639, 0.4790, 30.0500])), + (("5Y", 6.0, 12.0), np.array([0.4780, 0.4920, 30.0500])), + (("5Y", 6.0, 14.0), np.array([0.4905, 0.5038, 30.0500])), + (("7.5Y", 6.0, 2.0), np.array([0.3431, 0.3601, 30.0500])), + (("7.5Y", 6.0, 4.0), np.array([0.3745, 0.4004, 30.0500])), + (("7.5Y", 6.0, 6.0), np.array([0.4060, 0.4400, 30.0500])), + (("7.5Y", 6.0, 8.0), np.array([0.4321, 0.4719, 30.0500])), + (("7.5Y", 6.0, 10.0), np.array([0.4512, 0.4943, 30.0500])), + (("7.5Y", 6.0, 12.0), np.array([0.4638, 0.5087, 30.0500])), + (("7.5Y", 6.0, 14.0), np.array([0.4754, 0.5220, 30.0500])), + (("10Y", 6.0, 2.0), np.array([0.3398, 0.3611, 30.0500])), + (("10Y", 6.0, 4.0), np.array([0.3679, 0.4033, 30.0500])), + (("10Y", 6.0, 6.0), np.array([0.3960, 0.4452, 30.0500])), + (("10Y", 6.0, 8.0), np.array([0.4201, 0.4812, 30.0500])), + (("10Y", 6.0, 10.0), np.array([0.4372, 0.5068, 30.0500])), + (("10Y", 6.0, 12.0), np.array([0.4488, 0.5237, 30.0500])), + (("10Y", 6.0, 14.0), np.array([0.4593, 0.5392, 30.0500])), + (("2.5GY", 6.0, 2.0), np.array([0.3342, 0.3607, 30.0500])), + (("2.5GY", 6.0, 4.0), np.array([0.3572, 0.4038, 30.0500])), + (("2.5GY", 6.0, 6.0), np.array([0.3799, 0.4470, 30.0500])), + (("2.5GY", 6.0, 8.0), np.array([0.4006, 0.4885, 30.0500])), + (("2.5GY", 6.0, 10.0), np.array([0.4159, 0.5190, 30.0500])), + (("2.5GY", 6.0, 12.0), np.array([0.4269, 0.5414, 30.0500])), + (("2.5GY", 6.0, 14.0), np.array([0.4354, 0.5594, 30.0500])), + (("5GY", 6.0, 2.0), np.array([0.3288, 0.3592, 30.0500])), + (("5GY", 6.0, 4.0), np.array([0.3461, 0.4008, 30.0500])), + (("5GY", 6.0, 6.0), np.array([0.3622, 0.4438, 30.0500])), + (("5GY", 6.0, 8.0), np.array([0.3772, 0.4880, 30.0500])), + (("5GY", 6.0, 10.0), np.array([0.3891, 0.5264, 30.0500])), + (("5GY", 6.0, 12.0), np.array([0.3980, 0.5564, 30.0500])), + (("5GY", 6.0, 14.0), np.array([0.4042, 0.5788, 30.0500])), + (("7.5GY", 6.0, 2.0), np.array([0.3193, 0.3550, 30.0500])), + (("7.5GY", 6.0, 4.0), np.array([0.3275, 0.3922, 30.0500])), + (("7.5GY", 6.0, 6.0), np.array([0.3351, 0.4321, 30.0500])), + (("7.5GY", 6.0, 8.0), np.array([0.3418, 0.4768, 30.0500])), + (("7.5GY", 6.0, 10.0), np.array([0.3463, 0.5196, 30.0500])), + (("7.5GY", 6.0, 12.0), np.array([0.3488, 0.5596, 30.0500])), + (("7.5GY", 6.0, 14.0), np.array([0.3498, 0.5985, 30.0500])), + (("7.5GY", 6.0, 16.0), np.array([0.3498, 0.6282, 30.0500])), + (("10GY", 6.0, 2.0), np.array([0.3112, 0.3496, 30.0500])), + (("10GY", 6.0, 4.0), np.array([0.3124, 0.3822, 30.0500])), + (("10GY", 6.0, 6.0), np.array([0.3128, 0.4175, 30.0500])), + (("10GY", 6.0, 8.0), np.array([0.3116, 0.4563, 30.0500])), + (("10GY", 6.0, 10.0), np.array([0.3086, 0.4949, 30.0500])), + (("10GY", 6.0, 12.0), np.array([0.3037, 0.5358, 30.0500])), + (("10GY", 6.0, 14.0), np.array([0.2962, 0.5802, 30.0500])), + (("10GY", 6.0, 16.0), np.array([0.2872, 0.6199, 30.0500])), + (("10GY", 6.0, 18.0), np.array([0.2763, 0.6616, 30.0500])), + (("10GY", 6.0, 20.0), np.array([0.2648, 0.7004, 30.0500])), + (("2.5G", 6.0, 2.0), np.array([0.3039, 0.3437, 30.0500])), + (("2.5G", 6.0, 4.0), np.array([0.2967, 0.3695, 30.0500])), + (("2.5G", 6.0, 6.0), np.array([0.2892, 0.3963, 30.0500])), + (("2.5G", 6.0, 8.0), np.array([0.2799, 0.4239, 30.0500])), + (("2.5G", 6.0, 10.0), np.array([0.2690, 0.4530, 30.0500])), + (("2.5G", 6.0, 12.0), np.array([0.2574, 0.4814, 30.0500])), + (("2.5G", 6.0, 14.0), np.array([0.2426, 0.5133, 30.0500])), + (("2.5G", 6.0, 16.0), np.array([0.2278, 0.5430, 30.0500])), + (("2.5G", 6.0, 18.0), np.array([0.2102, 0.5737, 30.0500])), + (("2.5G", 6.0, 20.0), np.array([0.1922, 0.6035, 30.0500])), + (("2.5G", 6.0, 22.0), np.array([0.1739, 0.6318, 30.0500])), + (("2.5G", 6.0, 24.0), np.array([0.1536, 0.6605, 30.0500])), + (("2.5G", 6.0, 26.0), np.array([0.1340, 0.6871, 30.0500])), + (("2.5G", 6.0, 28.0), np.array([0.1145, 0.7122, 30.0500])), + (("5G", 6.0, 2.0), np.array([0.2988, 0.3382, 30.0500])), + (("5G", 6.0, 4.0), np.array([0.2868, 0.3595, 30.0500])), + (("5G", 6.0, 6.0), np.array([0.2748, 0.3795, 30.0500])), + (("5G", 6.0, 8.0), np.array([0.2612, 0.3990, 30.0500])), + (("5G", 6.0, 10.0), np.array([0.2466, 0.4181, 30.0500])), + (("5G", 6.0, 12.0), np.array([0.2293, 0.4390, 30.0500])), + (("5G", 6.0, 14.0), np.array([0.2130, 0.4571, 30.0500])), + (("5G", 6.0, 16.0), np.array([0.1960, 0.4751, 30.0500])), + (("5G", 6.0, 18.0), np.array([0.1785, 0.4924, 30.0500])), + (("5G", 6.0, 20.0), np.array([0.1609, 0.5091, 30.0500])), + (("5G", 6.0, 22.0), np.array([0.1432, 0.5252, 30.0500])), + (("5G", 6.0, 24.0), np.array([0.1252, 0.5408, 30.0500])), + (("5G", 6.0, 26.0), np.array([0.1079, 0.5560, 30.0500])), + (("5G", 6.0, 28.0), np.array([0.0908, 0.5695, 30.0500])), + (("7.5G", 6.0, 2.0), np.array([0.2958, 0.3344, 30.0500])), + (("7.5G", 6.0, 4.0), np.array([0.2807, 0.3522, 30.0500])), + (("7.5G", 6.0, 6.0), np.array([0.2662, 0.3672, 30.0500])), + (("7.5G", 6.0, 8.0), np.array([0.2510, 0.3829, 30.0500])), + (("7.5G", 6.0, 10.0), np.array([0.2350, 0.3979, 30.0500])), + (("7.5G", 6.0, 12.0), np.array([0.2171, 0.4138, 30.0500])), + (("7.5G", 6.0, 14.0), np.array([0.2001, 0.4278, 30.0500])), + (("7.5G", 6.0, 16.0), np.array([0.1832, 0.4414, 30.0500])), + (("7.5G", 6.0, 18.0), np.array([0.1654, 0.4551, 30.0500])), + (("7.5G", 6.0, 20.0), np.array([0.1485, 0.4677, 30.0500])), + (("7.5G", 6.0, 22.0), np.array([0.1325, 0.4795, 30.0500])), + (("7.5G", 6.0, 24.0), np.array([0.1159, 0.4910, 30.0500])), + (("7.5G", 6.0, 26.0), np.array([0.1010, 0.5018, 30.0500])), + (("7.5G", 6.0, 28.0), np.array([0.0858, 0.5127, 30.0500])), + (("10G", 6.0, 2.0), np.array([0.2929, 0.3303, 30.0500])), + (("10G", 6.0, 4.0), np.array([0.2749, 0.3443, 30.0500])), + (("10G", 6.0, 6.0), np.array([0.2591, 0.3558, 30.0500])), + (("10G", 6.0, 8.0), np.array([0.2420, 0.3679, 30.0500])), + (("10G", 6.0, 10.0), np.array([0.2247, 0.3796, 30.0500])), + (("10G", 6.0, 12.0), np.array([0.2060, 0.3914, 30.0500])), + (("10G", 6.0, 14.0), np.array([0.1895, 0.4015, 30.0500])), + (("10G", 6.0, 16.0), np.array([0.1722, 0.4113, 30.0500])), + (("10G", 6.0, 18.0), np.array([0.1551, 0.4208, 30.0500])), + (("10G", 6.0, 20.0), np.array([0.1382, 0.4299, 30.0500])), + (("10G", 6.0, 22.0), np.array([0.1230, 0.4378, 30.0500])), + (("10G", 6.0, 24.0), np.array([0.1070, 0.4458, 30.0500])), + (("10G", 6.0, 26.0), np.array([0.0941, 0.4520, 30.0500])), + (("2.5BG", 6.0, 2.0), np.array([0.2902, 0.3268, 30.0500])), + (("2.5BG", 6.0, 4.0), np.array([0.2702, 0.3369, 30.0500])), + (("2.5BG", 6.0, 6.0), np.array([0.2526, 0.3448, 30.0500])), + (("2.5BG", 6.0, 8.0), np.array([0.2332, 0.3522, 30.0500])), + (("2.5BG", 6.0, 10.0), np.array([0.2148, 0.3584, 30.0500])), + (("2.5BG", 6.0, 12.0), np.array([0.1954, 0.3645, 30.0500])), + (("2.5BG", 6.0, 14.0), np.array([0.1779, 0.3699, 30.0500])), + (("2.5BG", 6.0, 16.0), np.array([0.1600, 0.3748, 30.0500])), + (("2.5BG", 6.0, 18.0), np.array([0.1428, 0.3790, 30.0500])), + (("2.5BG", 6.0, 20.0), np.array([0.1269, 0.3829, 30.0500])), + (("2.5BG", 6.0, 22.0), np.array([0.1120, 0.3860, 30.0500])), + (("5BG", 6.0, 2.0), np.array([0.2872, 0.3219, 30.0500])), + (("5BG", 6.0, 4.0), np.array([0.2648, 0.3262, 30.0500])), + (("5BG", 6.0, 6.0), np.array([0.2441, 0.3290, 30.0500])), + (("5BG", 6.0, 8.0), np.array([0.2236, 0.3311, 30.0500])), + (("5BG", 6.0, 10.0), np.array([0.2037, 0.3329, 30.0500])), + (("5BG", 6.0, 12.0), np.array([0.1844, 0.3337, 30.0500])), + (("5BG", 6.0, 14.0), np.array([0.1662, 0.3343, 30.0500])), + (("5BG", 6.0, 16.0), np.array([0.1491, 0.3345, 30.0500])), + (("5BG", 6.0, 18.0), np.array([0.1325, 0.3345, 30.0500])), + (("5BG", 6.0, 20.0), np.array([0.1168, 0.3344, 30.0500])), + (("7.5BG", 6.0, 2.0), np.array([0.2849, 0.3172, 30.0500])), + (("7.5BG", 6.0, 4.0), np.array([0.2604, 0.3169, 30.0500])), + (("7.5BG", 6.0, 6.0), np.array([0.2384, 0.3155, 30.0500])), + (("7.5BG", 6.0, 8.0), np.array([0.2171, 0.3138, 30.0500])), + (("7.5BG", 6.0, 10.0), np.array([0.1961, 0.3110, 30.0500])), + (("7.5BG", 6.0, 12.0), np.array([0.1762, 0.3081, 30.0500])), + (("7.5BG", 6.0, 14.0), np.array([0.1585, 0.3052, 30.0500])), + (("7.5BG", 6.0, 16.0), np.array([0.1408, 0.3017, 30.0500])), + (("7.5BG", 6.0, 18.0), np.array([0.1248, 0.2981, 30.0500])), + (("10BG", 6.0, 2.0), np.array([0.2837, 0.3132, 30.0500])), + (("10BG", 6.0, 4.0), np.array([0.2578, 0.3078, 30.0500])), + (("10BG", 6.0, 6.0), np.array([0.2335, 0.3015, 30.0500])), + (("10BG", 6.0, 8.0), np.array([0.2116, 0.2950, 30.0500])), + (("10BG", 6.0, 10.0), np.array([0.1909, 0.2881, 30.0500])), + (("10BG", 6.0, 12.0), np.array([0.1698, 0.2802, 30.0500])), + (("10BG", 6.0, 14.0), np.array([0.1518, 0.2729, 30.0500])), + (("10BG", 6.0, 16.0), np.array([0.1337, 0.2651, 30.0500])), + (("10BG", 6.0, 18.0), np.array([0.1181, 0.2581, 30.0500])), + (("2.5B", 6.0, 2.0), np.array([0.2835, 0.3097, 30.0500])), + (("2.5B", 6.0, 4.0), np.array([0.2571, 0.3008, 30.0500])), + (("2.5B", 6.0, 6.0), np.array([0.2312, 0.2899, 30.0500])), + (("2.5B", 6.0, 8.0), np.array([0.2080, 0.2789, 30.0500])), + (("2.5B", 6.0, 10.0), np.array([0.1879, 0.2682, 30.0500])), + (("2.5B", 6.0, 12.0), np.array([0.1660, 0.2561, 30.0500])), + (("2.5B", 6.0, 14.0), np.array([0.1480, 0.2459, 30.0500])), + (("2.5B", 6.0, 16.0), np.array([0.1294, 0.2348, 30.0500])), + (("5B", 6.0, 2.0), np.array([0.2842, 0.3063, 30.0500])), + (("5B", 6.0, 4.0), np.array([0.2579, 0.2938, 30.0500])), + (("5B", 6.0, 6.0), np.array([0.2320, 0.2789, 30.0500])), + (("5B", 6.0, 8.0), np.array([0.2088, 0.2635, 30.0500])), + (("5B", 6.0, 10.0), np.array([0.1883, 0.2487, 30.0500])), + (("5B", 6.0, 12.0), np.array([0.1685, 0.2339, 30.0500])), + (("5B", 6.0, 14.0), np.array([0.1496, 0.2193, 30.0500])), + (("5B", 6.0, 16.0), np.array([0.1310, 0.2048, 30.0500])), + (("7.5B", 6.0, 2.0), np.array([0.2854, 0.3037, 30.0500])), + (("7.5B", 6.0, 4.0), np.array([0.2602, 0.2881, 30.0500])), + (("7.5B", 6.0, 6.0), np.array([0.2352, 0.2708, 30.0500])), + (("7.5B", 6.0, 8.0), np.array([0.2132, 0.2537, 30.0500])), + (("7.5B", 6.0, 10.0), np.array([0.1934, 0.2374, 30.0500])), + (("7.5B", 6.0, 12.0), np.array([0.1734, 0.2203, 30.0500])), + (("7.5B", 6.0, 14.0), np.array([0.1556, 0.2043, 30.0500])), + (("7.5B", 6.0, 16.0), np.array([0.1376, 0.1879, 30.0500])), + (("10B", 6.0, 2.0), np.array([0.2871, 0.3012, 30.0500])), + (("10B", 6.0, 4.0), np.array([0.2637, 0.2840, 30.0500])), + (("10B", 6.0, 6.0), np.array([0.2399, 0.2650, 30.0500])), + (("10B", 6.0, 8.0), np.array([0.2189, 0.2468, 30.0500])), + (("10B", 6.0, 10.0), np.array([0.2000, 0.2298, 30.0500])), + (("10B", 6.0, 12.0), np.array([0.1803, 0.2114, 30.0500])), + (("10B", 6.0, 14.0), np.array([0.1629, 0.1947, 30.0500])), + (("10B", 6.0, 16.0), np.array([0.1454, 0.1778, 30.0500])), + (("2.5PB", 6.0, 2.0), np.array([0.2897, 0.2991, 30.0500])), + (("2.5PB", 6.0, 4.0), np.array([0.2684, 0.2804, 30.0500])), + (("2.5PB", 6.0, 6.0), np.array([0.2465, 0.2599, 30.0500])), + (("2.5PB", 6.0, 8.0), np.array([0.2274, 0.2406, 30.0500])), + (("2.5PB", 6.0, 10.0), np.array([0.2095, 0.2225, 30.0500])), + (("2.5PB", 6.0, 12.0), np.array([0.1913, 0.2038, 30.0500])), + (("2.5PB", 6.0, 14.0), np.array([0.1754, 0.1868, 30.0500])), + (("5PB", 6.0, 2.0), np.array([0.2923, 0.2978, 30.0500])), + (("5PB", 6.0, 4.0), np.array([0.2734, 0.2778, 30.0500])), + (("5PB", 6.0, 6.0), np.array([0.2533, 0.2558, 30.0500])), + (("5PB", 6.0, 8.0), np.array([0.2360, 0.2365, 30.0500])), + (("5PB", 6.0, 10.0), np.array([0.2197, 0.2188, 30.0500])), + (("5PB", 6.0, 12.0), np.array([0.2026, 0.1999, 30.0500])), + (("5PB", 6.0, 14.0), np.array([0.1873, 0.1822, 30.0500])), + (("7.5PB", 6.0, 2.0), np.array([0.2955, 0.2963, 30.0500])), + (("7.5PB", 6.0, 4.0), np.array([0.2798, 0.2752, 30.0500])), + (("7.5PB", 6.0, 6.0), np.array([0.2638, 0.2531, 30.0500])), + (("7.5PB", 6.0, 8.0), np.array([0.2505, 0.2347, 30.0500])), + (("7.5PB", 6.0, 10.0), np.array([0.2378, 0.2168, 30.0500])), + (("7.5PB", 6.0, 12.0), np.array([0.2241, 0.1975, 30.0500])), + (("7.5PB", 6.0, 14.0), np.array([0.2119, 0.1799, 30.0500])), + (("10PB", 6.0, 2.0), np.array([0.2988, 0.2961, 30.0500])), + (("10PB", 6.0, 4.0), np.array([0.2863, 0.2747, 30.0500])), + (("10PB", 6.0, 6.0), np.array([0.2740, 0.2533, 30.0500])), + (("10PB", 6.0, 8.0), np.array([0.2637, 0.2352, 30.0500])), + (("10PB", 6.0, 10.0), np.array([0.2540, 0.2176, 30.0500])), + (("10PB", 6.0, 12.0), np.array([0.2440, 0.1998, 30.0500])), + (("10PB", 6.0, 14.0), np.array([0.2352, 0.1839, 30.0500])), + (("10PB", 6.0, 16.0), np.array([0.2265, 0.1671, 30.0500])), + (("2.5P", 6.0, 2.0), np.array([0.3016, 0.2960, 30.0500])), + (("2.5P", 6.0, 4.0), np.array([0.2932, 0.2759, 30.0500])), + (("2.5P", 6.0, 6.0), np.array([0.2842, 0.2550, 30.0500])), + (("2.5P", 6.0, 8.0), np.array([0.2770, 0.2372, 30.0500])), + (("2.5P", 6.0, 10.0), np.array([0.2703, 0.2204, 30.0500])), + (("2.5P", 6.0, 12.0), np.array([0.2647, 0.2052, 30.0500])), + (("2.5P", 6.0, 14.0), np.array([0.2593, 0.1909, 30.0500])), + (("2.5P", 6.0, 16.0), np.array([0.2548, 0.1768, 30.0500])), + (("2.5P", 6.0, 18.0), np.array([0.2504, 0.1658, 30.0500])), + (("5P", 6.0, 2.0), np.array([0.3050, 0.2967, 30.0500])), + (("5P", 6.0, 4.0), np.array([0.3001, 0.2778, 30.0500])), + (("5P", 6.0, 6.0), np.array([0.2950, 0.2585, 30.0500])), + (("5P", 6.0, 8.0), np.array([0.2905, 0.2421, 30.0500])), + (("5P", 6.0, 10.0), np.array([0.2862, 0.2260, 30.0500])), + (("5P", 6.0, 12.0), np.array([0.2829, 0.2121, 30.0500])), + (("5P", 6.0, 14.0), np.array([0.2794, 0.1979, 30.0500])), + (("5P", 6.0, 16.0), np.array([0.2761, 0.1852, 30.0500])), + (("5P", 6.0, 18.0), np.array([0.2731, 0.1738, 30.0500])), + (("5P", 6.0, 20.0), np.array([0.2702, 0.1621, 30.0500])), + (("7.5P", 6.0, 2.0), np.array([0.3107, 0.2993, 30.0500])), + (("7.5P", 6.0, 4.0), np.array([0.3107, 0.2831, 30.0500])), + (("7.5P", 6.0, 6.0), np.array([0.3101, 0.2650, 30.0500])), + (("7.5P", 6.0, 8.0), np.array([0.3099, 0.2502, 30.0500])), + (("7.5P", 6.0, 10.0), np.array([0.3092, 0.2350, 30.0500])), + (("7.5P", 6.0, 12.0), np.array([0.3090, 0.2222, 30.0500])), + (("7.5P", 6.0, 14.0), np.array([0.3084, 0.2095, 30.0500])), + (("7.5P", 6.0, 16.0), np.array([0.3080, 0.1976, 30.0500])), + (("7.5P", 6.0, 18.0), np.array([0.3075, 0.1870, 30.0500])), + (("7.5P", 6.0, 20.0), np.array([0.3069, 0.1745, 30.0500])), + (("7.5P", 6.0, 22.0), np.array([0.3062, 0.1638, 30.0500])), + (("7.5P", 6.0, 24.0), np.array([0.3058, 0.1547, 30.0500])), + (("10P", 6.0, 2.0), np.array([0.3146, 0.3018, 30.0500])), + (("10P", 6.0, 4.0), np.array([0.3181, 0.2871, 30.0500])), + (("10P", 6.0, 6.0), np.array([0.3226, 0.2716, 30.0500])), + (("10P", 6.0, 8.0), np.array([0.3259, 0.2584, 30.0500])), + (("10P", 6.0, 10.0), np.array([0.3293, 0.2450, 30.0500])), + (("10P", 6.0, 12.0), np.array([0.3321, 0.2329, 30.0500])), + (("10P", 6.0, 14.0), np.array([0.3349, 0.2203, 30.0500])), + (("10P", 6.0, 16.0), np.array([0.3370, 0.2095, 30.0500])), + (("10P", 6.0, 18.0), np.array([0.3388, 0.1995, 30.0500])), + (("10P", 6.0, 20.0), np.array([0.3409, 0.1882, 30.0500])), + (("10P", 6.0, 22.0), np.array([0.3426, 0.1785, 30.0500])), + (("10P", 6.0, 24.0), np.array([0.3441, 0.1698, 30.0500])), + (("10P", 6.0, 26.0), np.array([0.3457, 0.1604, 30.0500])), + (("2.5RP", 6.0, 2.0), np.array([0.3188, 0.3048, 30.0500])), + (("2.5RP", 6.0, 4.0), np.array([0.3272, 0.2929, 30.0500])), + (("2.5RP", 6.0, 6.0), np.array([0.3362, 0.2799, 30.0500])), + (("2.5RP", 6.0, 8.0), np.array([0.3437, 0.2688, 30.0500])), + (("2.5RP", 6.0, 10.0), np.array([0.3509, 0.2578, 30.0500])), + (("2.5RP", 6.0, 12.0), np.array([0.3582, 0.2462, 30.0500])), + (("2.5RP", 6.0, 14.0), np.array([0.3652, 0.2355, 30.0500])), + (("2.5RP", 6.0, 16.0), np.array([0.3718, 0.2251, 30.0500])), + (("2.5RP", 6.0, 18.0), np.array([0.3773, 0.2158, 30.0500])), + (("2.5RP", 6.0, 20.0), np.array([0.3833, 0.2056, 30.0500])), + (("2.5RP", 6.0, 22.0), np.array([0.3877, 0.1978, 30.0500])), + (("2.5RP", 6.0, 24.0), np.array([0.3927, 0.1892, 30.0500])), + (("5RP", 6.0, 2.0), np.array([0.3232, 0.3085, 30.0500])), + (("5RP", 6.0, 4.0), np.array([0.3371, 0.3001, 30.0500])), + (("5RP", 6.0, 6.0), np.array([0.3520, 0.2904, 30.0500])), + (("5RP", 6.0, 8.0), np.array([0.3648, 0.2820, 30.0500])), + (("5RP", 6.0, 10.0), np.array([0.3769, 0.2738, 30.0500])), + (("5RP", 6.0, 12.0), np.array([0.3900, 0.2646, 30.0500])), + (("5RP", 6.0, 14.0), np.array([0.4023, 0.2552, 30.0500])), + (("5RP", 6.0, 16.0), np.array([0.4136, 0.2467, 30.0500])), + (("5RP", 6.0, 18.0), np.array([0.4245, 0.2382, 30.0500])), + (("5RP", 6.0, 20.0), np.array([0.4368, 0.2283, 30.0500])), + (("5RP", 6.0, 22.0), np.array([0.4449, 0.2219, 30.0500])), + (("7.5RP", 6.0, 2.0), np.array([0.3261, 0.3113, 30.0500])), + (("7.5RP", 6.0, 4.0), np.array([0.3439, 0.3056, 30.0500])), + (("7.5RP", 6.0, 6.0), np.array([0.3635, 0.2987, 30.0500])), + (("7.5RP", 6.0, 8.0), np.array([0.3791, 0.2929, 30.0500])), + (("7.5RP", 6.0, 10.0), np.array([0.3960, 0.2860, 30.0500])), + (("7.5RP", 6.0, 12.0), np.array([0.4125, 0.2784, 30.0500])), + (("7.5RP", 6.0, 14.0), np.array([0.4285, 0.2705, 30.0500])), + (("7.5RP", 6.0, 16.0), np.array([0.4448, 0.2622, 30.0500])), + (("7.5RP", 6.0, 18.0), np.array([0.4581, 0.2549, 30.0500])), + (("7.5RP", 6.0, 20.0), np.array([0.4735, 0.2464, 30.0500])), + (("10RP", 7.0, 2.0), np.array([0.3258, 0.3148, 43.0600])), + (("10RP", 7.0, 4.0), np.array([0.3446, 0.3125, 43.0600])), + (("10RP", 7.0, 6.0), np.array([0.3648, 0.3098, 43.0600])), + (("10RP", 7.0, 8.0), np.array([0.3851, 0.3067, 43.0600])), + (("10RP", 7.0, 10.0), np.array([0.4040, 0.3030, 43.0600])), + (("10RP", 7.0, 12.0), np.array([0.4260, 0.2980, 43.0600])), + (("10RP", 7.0, 14.0), np.array([0.4456, 0.2931, 43.0600])), + (("10RP", 7.0, 16.0), np.array([0.4648, 0.2878, 43.0600])), + (("2.5R", 7.0, 2.0), np.array([0.3284, 0.3170, 43.0600])), + (("2.5R", 7.0, 4.0), np.array([0.3499, 0.3171, 43.0600])), + (("2.5R", 7.0, 6.0), np.array([0.3728, 0.3170, 43.0600])), + (("2.5R", 7.0, 8.0), np.array([0.3961, 0.3160, 43.0600])), + (("2.5R", 7.0, 10.0), np.array([0.4183, 0.3144, 43.0600])), + (("2.5R", 7.0, 12.0), np.array([0.4435, 0.3119, 43.0600])), + (("2.5R", 7.0, 14.0), np.array([0.4660, 0.3082, 43.0600])), + (("2.5R", 7.0, 16.0), np.array([0.4885, 0.3039, 43.0600])), + (("5R", 7.0, 2.0), np.array([0.3306, 0.3190, 43.0600])), + (("5R", 7.0, 4.0), np.array([0.3552, 0.3222, 43.0600])), + (("5R", 7.0, 6.0), np.array([0.3805, 0.3244, 43.0600])), + (("5R", 7.0, 8.0), np.array([0.4067, 0.3256, 43.0600])), + (("5R", 7.0, 10.0), np.array([0.4320, 0.3260, 43.0600])), + (("5R", 7.0, 12.0), np.array([0.4595, 0.3252, 43.0600])), + (("5R", 7.0, 14.0), np.array([0.4848, 0.3238, 43.0600])), + (("7.5R", 7.0, 2.0), np.array([0.3335, 0.3220, 43.0600])), + (("7.5R", 7.0, 4.0), np.array([0.3611, 0.3282, 43.0600])), + (("7.5R", 7.0, 6.0), np.array([0.3888, 0.3336, 43.0600])), + (("7.5R", 7.0, 8.0), np.array([0.4196, 0.3382, 43.0600])), + (("7.5R", 7.0, 10.0), np.array([0.4470, 0.3413, 43.0600])), + (("7.5R", 7.0, 12.0), np.array([0.4777, 0.3435, 43.0600])), + (("7.5R", 7.0, 14.0), np.array([0.5059, 0.3450, 43.0600])), + (("7.5R", 7.0, 16.0), np.array([0.5341, 0.3452, 43.0600])), + (("10R", 7.0, 2.0), np.array([0.3360, 0.3253, 43.0600])), + (("10R", 7.0, 4.0), np.array([0.3671, 0.3360, 43.0600])), + (("10R", 7.0, 6.0), np.array([0.3984, 0.3452, 43.0600])), + (("10R", 7.0, 8.0), np.array([0.4308, 0.3533, 43.0600])), + (("10R", 7.0, 10.0), np.array([0.4600, 0.3596, 43.0600])), + (("10R", 7.0, 12.0), np.array([0.4930, 0.3659, 43.0600])), + (("10R", 7.0, 14.0), np.array([0.5234, 0.3700, 43.0600])), + (("10R", 7.0, 16.0), np.array([0.5519, 0.3729, 43.0600])), + (("2.5YR", 7.0, 2.0), np.array([0.3392, 0.3298, 43.0600])), + (("2.5YR", 7.0, 4.0), np.array([0.3715, 0.3439, 43.0600])), + (("2.5YR", 7.0, 6.0), np.array([0.4053, 0.3570, 43.0600])), + (("2.5YR", 7.0, 8.0), np.array([0.4371, 0.3679, 43.0600])), + (("2.5YR", 7.0, 10.0), np.array([0.4671, 0.3768, 43.0600])), + (("2.5YR", 7.0, 12.0), np.array([0.5001, 0.3861, 43.0600])), + (("2.5YR", 7.0, 14.0), np.array([0.5297, 0.3938, 43.0600])), + (("2.5YR", 7.0, 16.0), np.array([0.5522, 0.3989, 43.0600])), + (("2.5YR", 7.0, 18.0), np.array([0.5695, 0.4024, 43.0600])), + (("2.5YR", 7.0, 20.0), np.array([0.5824, 0.4046, 43.0600])), + (("5YR", 7.0, 2.0), np.array([0.3421, 0.3349, 43.0600])), + (("5YR", 7.0, 4.0), np.array([0.3750, 0.3530, 43.0600])), + (("5YR", 7.0, 6.0), np.array([0.4091, 0.3701, 43.0600])), + (("5YR", 7.0, 8.0), np.array([0.4402, 0.3842, 43.0600])), + (("5YR", 7.0, 10.0), np.array([0.4711, 0.3972, 43.0600])), + (("5YR", 7.0, 12.0), np.array([0.5007, 0.4081, 43.0600])), + (("5YR", 7.0, 14.0), np.array([0.5252, 0.4168, 43.0600])), + (("5YR", 7.0, 16.0), np.array([0.5437, 0.4228, 43.0600])), + (("5YR", 7.0, 18.0), np.array([0.5564, 0.4267, 43.0600])), + (("5YR", 7.0, 20.0), np.array([0.5657, 0.4298, 43.0600])), + (("7.5YR", 7.0, 2.0), np.array([0.3437, 0.3397, 43.0600])), + (("7.5YR", 7.0, 4.0), np.array([0.3772, 0.3613, 43.0600])), + (("7.5YR", 7.0, 6.0), np.array([0.4107, 0.3820, 43.0600])), + (("7.5YR", 7.0, 8.0), np.array([0.4415, 0.3996, 43.0600])), + (("7.5YR", 7.0, 10.0), np.array([0.4704, 0.4151, 43.0600])), + (("7.5YR", 7.0, 12.0), np.array([0.4970, 0.4282, 43.0600])), + (("7.5YR", 7.0, 14.0), np.array([0.5174, 0.4381, 43.0600])), + (("7.5YR", 7.0, 16.0), np.array([0.5319, 0.4449, 43.0600])), + (("7.5YR", 7.0, 18.0), np.array([0.5417, 0.4492, 43.0600])), + (("10YR", 7.0, 2.0), np.array([0.3443, 0.3454, 43.0600])), + (("10YR", 7.0, 4.0), np.array([0.3778, 0.3719, 43.0600])), + (("10YR", 7.0, 6.0), np.array([0.4102, 0.3960, 43.0600])), + (("10YR", 7.0, 8.0), np.array([0.4399, 0.4164, 43.0600])), + (("10YR", 7.0, 10.0), np.array([0.4667, 0.4335, 43.0600])), + (("10YR", 7.0, 12.0), np.array([0.4900, 0.4480, 43.0600])), + (("10YR", 7.0, 14.0), np.array([0.5074, 0.4581, 43.0600])), + (("10YR", 7.0, 16.0), np.array([0.5188, 0.4650, 43.0600])), + (("10YR", 7.0, 18.0), np.array([0.5276, 0.4700, 43.0600])), + (("2.5Y", 7.0, 2.0), np.array([0.3436, 0.3507, 43.0600])), + (("2.5Y", 7.0, 4.0), np.array([0.3761, 0.3800, 43.0600])), + (("2.5Y", 7.0, 6.0), np.array([0.4073, 0.4073, 43.0600])), + (("2.5Y", 7.0, 8.0), np.array([0.4353, 0.4312, 43.0600])), + (("2.5Y", 7.0, 10.0), np.array([0.4606, 0.4516, 43.0600])), + (("2.5Y", 7.0, 12.0), np.array([0.4806, 0.4666, 43.0600])), + (("2.5Y", 7.0, 14.0), np.array([0.4950, 0.4773, 43.0600])), + (("2.5Y", 7.0, 16.0), np.array([0.5049, 0.4843, 43.0600])), + (("5Y", 7.0, 2.0), np.array([0.3419, 0.3540, 43.0600])), + (("5Y", 7.0, 4.0), np.array([0.3718, 0.3885, 43.0600])), + (("5Y", 7.0, 6.0), np.array([0.4009, 0.4198, 43.0600])), + (("5Y", 7.0, 8.0), np.array([0.4271, 0.4462, 43.0600])), + (("5Y", 7.0, 10.0), np.array([0.4509, 0.4696, 43.0600])), + (("5Y", 7.0, 12.0), np.array([0.4677, 0.4857, 43.0600])), + (("5Y", 7.0, 14.0), np.array([0.4791, 0.4965, 43.0600])), + (("5Y", 7.0, 16.0), np.array([0.4875, 0.5047, 43.0600])), + (("7.5Y", 7.0, 2.0), np.array([0.3396, 0.3558, 43.0600])), + (("7.5Y", 7.0, 4.0), np.array([0.3677, 0.3925, 43.0600])), + (("7.5Y", 7.0, 6.0), np.array([0.3943, 0.4264, 43.0600])), + (("7.5Y", 7.0, 8.0), np.array([0.4184, 0.4568, 43.0600])), + (("7.5Y", 7.0, 10.0), np.array([0.4400, 0.4830, 43.0600])), + (("7.5Y", 7.0, 12.0), np.array([0.4547, 0.5005, 43.0600])), + (("7.5Y", 7.0, 14.0), np.array([0.4652, 0.5128, 43.0600])), + (("7.5Y", 7.0, 16.0), np.array([0.4728, 0.5215, 43.0600])), + (("10Y", 7.0, 2.0), np.array([0.3369, 0.3569, 43.0600])), + (("10Y", 7.0, 4.0), np.array([0.3624, 0.3951, 43.0600])), + (("10Y", 7.0, 6.0), np.array([0.3864, 0.4305, 43.0600])), + (("10Y", 7.0, 8.0), np.array([0.4090, 0.4641, 43.0600])), + (("10Y", 7.0, 10.0), np.array([0.4289, 0.4937, 43.0600])), + (("10Y", 7.0, 12.0), np.array([0.4420, 0.5131, 43.0600])), + (("10Y", 7.0, 14.0), np.array([0.4516, 0.5277, 43.0600])), + (("10Y", 7.0, 16.0), np.array([0.4582, 0.5375, 43.0600])), + (("2.5GY", 7.0, 2.0), np.array([0.3328, 0.3569, 43.0600])), + (("2.5GY", 7.0, 4.0), np.array([0.3534, 0.3953, 43.0600])), + (("2.5GY", 7.0, 6.0), np.array([0.3728, 0.4316, 43.0600])), + (("2.5GY", 7.0, 8.0), np.array([0.3919, 0.4684, 43.0600])), + (("2.5GY", 7.0, 10.0), np.array([0.4091, 0.5030, 43.0600])), + (("2.5GY", 7.0, 12.0), np.array([0.4213, 0.5270, 43.0600])), + (("2.5GY", 7.0, 14.0), np.array([0.4309, 0.5459, 43.0600])), + (("2.5GY", 7.0, 16.0), np.array([0.4366, 0.5578, 43.0600])), + (("5GY", 7.0, 2.0), np.array([0.3284, 0.3559, 43.0600])), + (("5GY", 7.0, 4.0), np.array([0.3437, 0.3929, 43.0600])), + (("5GY", 7.0, 6.0), np.array([0.3581, 0.4291, 43.0600])), + (("5GY", 7.0, 8.0), np.array([0.3722, 0.4669, 43.0600])), + (("5GY", 7.0, 10.0), np.array([0.3852, 0.5051, 43.0600])), + (("5GY", 7.0, 12.0), np.array([0.3949, 0.5367, 43.0600])), + (("5GY", 7.0, 14.0), np.array([0.4027, 0.5615, 43.0600])), + (("5GY", 7.0, 16.0), np.array([0.4076, 0.5783, 43.0600])), + (("7.5GY", 7.0, 2.0), np.array([0.3190, 0.3516, 43.0600])), + (("7.5GY", 7.0, 4.0), np.array([0.3267, 0.3848, 43.0600])), + (("7.5GY", 7.0, 6.0), np.array([0.3341, 0.4191, 43.0600])), + (("7.5GY", 7.0, 8.0), np.array([0.3406, 0.4558, 43.0600])), + (("7.5GY", 7.0, 10.0), np.array([0.3461, 0.4950, 43.0600])), + (("7.5GY", 7.0, 12.0), np.array([0.3502, 0.5328, 43.0600])), + (("7.5GY", 7.0, 14.0), np.array([0.3532, 0.5700, 43.0600])), + (("7.5GY", 7.0, 16.0), np.array([0.3549, 0.6000, 43.0600])), + (("7.5GY", 7.0, 18.0), np.array([0.3555, 0.6242, 43.0600])), + (("10GY", 7.0, 2.0), np.array([0.3117, 0.3469, 43.0600])), + (("10GY", 7.0, 4.0), np.array([0.3133, 0.3764, 43.0600])), + (("10GY", 7.0, 6.0), np.array([0.3142, 0.4058, 43.0600])), + (("10GY", 7.0, 8.0), np.array([0.3140, 0.4387, 43.0600])), + (("10GY", 7.0, 10.0), np.array([0.3123, 0.4732, 43.0600])), + (("10GY", 7.0, 12.0), np.array([0.3092, 0.5095, 43.0600])), + (("10GY", 7.0, 14.0), np.array([0.3047, 0.5458, 43.0600])), + (("10GY", 7.0, 16.0), np.array([0.2981, 0.5835, 43.0600])), + (("10GY", 7.0, 18.0), np.array([0.2905, 0.6186, 43.0600])), + (("10GY", 7.0, 20.0), np.array([0.2816, 0.6563, 43.0600])), + (("10GY", 7.0, 22.0), np.array([0.2728, 0.6893, 43.0600])), + (("2.5G", 7.0, 2.0), np.array([0.3047, 0.3413, 43.0600])), + (("2.5G", 7.0, 4.0), np.array([0.2992, 0.3644, 43.0600])), + (("2.5G", 7.0, 6.0), np.array([0.2933, 0.3873, 43.0600])), + (("2.5G", 7.0, 8.0), np.array([0.2861, 0.4129, 43.0600])), + (("2.5G", 7.0, 10.0), np.array([0.2775, 0.4395, 43.0600])), + (("2.5G", 7.0, 12.0), np.array([0.2672, 0.4667, 43.0600])), + (("2.5G", 7.0, 14.0), np.array([0.2568, 0.4931, 43.0600])), + (("2.5G", 7.0, 16.0), np.array([0.2448, 0.5203, 43.0600])), + (("2.5G", 7.0, 18.0), np.array([0.2328, 0.5467, 43.0600])), + (("2.5G", 7.0, 20.0), np.array([0.2181, 0.5744, 43.0600])), + (("2.5G", 7.0, 22.0), np.array([0.2029, 0.6017, 43.0600])), + (("2.5G", 7.0, 24.0), np.array([0.1875, 0.6265, 43.0600])), + (("2.5G", 7.0, 26.0), np.array([0.1689, 0.6549, 43.0600])), + (("5G", 7.0, 2.0), np.array([0.3001, 0.3366, 43.0600])), + (("5G", 7.0, 4.0), np.array([0.2902, 0.3548, 43.0600])), + (("5G", 7.0, 6.0), np.array([0.2801, 0.3721, 43.0600])), + (("5G", 7.0, 8.0), np.array([0.2687, 0.3901, 43.0600])), + (("5G", 7.0, 10.0), np.array([0.2554, 0.4087, 43.0600])), + (("5G", 7.0, 12.0), np.array([0.2416, 0.4267, 43.0600])), + (("5G", 7.0, 14.0), np.array([0.2262, 0.4450, 43.0600])), + (("5G", 7.0, 16.0), np.array([0.2111, 0.4616, 43.0600])), + (("5G", 7.0, 18.0), np.array([0.1967, 0.4771, 43.0600])), + (("5G", 7.0, 20.0), np.array([0.1805, 0.4933, 43.0600])), + (("5G", 7.0, 22.0), np.array([0.1659, 0.5074, 43.0600])), + (("5G", 7.0, 24.0), np.array([0.1521, 0.5200, 43.0600])), + (("5G", 7.0, 26.0), np.array([0.1397, 0.5312, 43.0600])), + (("7.5G", 7.0, 2.0), np.array([0.2972, 0.3333, 43.0600])), + (("7.5G", 7.0, 4.0), np.array([0.2850, 0.3482, 43.0600])), + (("7.5G", 7.0, 6.0), np.array([0.2728, 0.3622, 43.0600])), + (("7.5G", 7.0, 8.0), np.array([0.2595, 0.3764, 43.0600])), + (("7.5G", 7.0, 10.0), np.array([0.2445, 0.3914, 43.0600])), + (("7.5G", 7.0, 12.0), np.array([0.2295, 0.4058, 43.0600])), + (("7.5G", 7.0, 14.0), np.array([0.2139, 0.4199, 43.0600])), + (("7.5G", 7.0, 16.0), np.array([0.1982, 0.4330, 43.0600])), + (("7.5G", 7.0, 18.0), np.array([0.1841, 0.4448, 43.0600])), + (("7.5G", 7.0, 20.0), np.array([0.1688, 0.4570, 43.0600])), + (("7.5G", 7.0, 22.0), np.array([0.1539, 0.4683, 43.0600])), + (("7.5G", 7.0, 24.0), np.array([0.1415, 0.4778, 43.0600])), + (("7.5G", 7.0, 26.0), np.array([0.1303, 0.4858, 43.0600])), + (("10G", 7.0, 2.0), np.array([0.2945, 0.3297, 43.0600])), + (("10G", 7.0, 4.0), np.array([0.2803, 0.3415, 43.0600])), + (("10G", 7.0, 6.0), np.array([0.2662, 0.3526, 43.0600])), + (("10G", 7.0, 8.0), np.array([0.2513, 0.3635, 43.0600])), + (("10G", 7.0, 10.0), np.array([0.2352, 0.3748, 43.0600])), + (("10G", 7.0, 12.0), np.array([0.2195, 0.3854, 43.0600])), + (("10G", 7.0, 14.0), np.array([0.2033, 0.3956, 43.0600])), + (("10G", 7.0, 16.0), np.array([0.1881, 0.4049, 43.0600])), + (("10G", 7.0, 18.0), np.array([0.1734, 0.4135, 43.0600])), + (("10G", 7.0, 20.0), np.array([0.1589, 0.4220, 43.0600])), + (("10G", 7.0, 22.0), np.array([0.1434, 0.4306, 43.0600])), + (("10G", 7.0, 24.0), np.array([0.1310, 0.4377, 43.0600])), + (("2.5BG", 7.0, 2.0), np.array([0.2927, 0.3269, 43.0600])), + (("2.5BG", 7.0, 4.0), np.array([0.2764, 0.3354, 43.0600])), + (("2.5BG", 7.0, 6.0), np.array([0.2608, 0.3430, 43.0600])), + (("2.5BG", 7.0, 8.0), np.array([0.2439, 0.3508, 43.0600])), + (("2.5BG", 7.0, 10.0), np.array([0.2264, 0.3576, 43.0600])), + (("2.5BG", 7.0, 12.0), np.array([0.2102, 0.3636, 43.0600])), + (("2.5BG", 7.0, 14.0), np.array([0.1932, 0.3694, 43.0600])), + (("2.5BG", 7.0, 16.0), np.array([0.1788, 0.3739, 43.0600])), + (("2.5BG", 7.0, 18.0), np.array([0.1626, 0.3788, 43.0600])), + (("2.5BG", 7.0, 20.0), np.array([0.1490, 0.3827, 43.0600])), + (("2.5BG", 7.0, 22.0), np.array([0.1334, 0.3870, 43.0600])), + (("5BG", 7.0, 2.0), np.array([0.2898, 0.3225, 43.0600])), + (("5BG", 7.0, 4.0), np.array([0.2712, 0.3269, 43.0600])), + (("5BG", 7.0, 6.0), np.array([0.2543, 0.3302, 43.0600])), + (("5BG", 7.0, 8.0), np.array([0.2354, 0.3335, 43.0600])), + (("5BG", 7.0, 10.0), np.array([0.2163, 0.3361, 43.0600])), + (("5BG", 7.0, 12.0), np.array([0.1997, 0.3379, 43.0600])), + (("5BG", 7.0, 14.0), np.array([0.1838, 0.3390, 43.0600])), + (("5BG", 7.0, 16.0), np.array([0.1675, 0.3401, 43.0600])), + (("5BG", 7.0, 18.0), np.array([0.1515, 0.3410, 43.0600])), + (("5BG", 7.0, 20.0), np.array([0.1380, 0.3412, 43.0600])), + (("7.5BG", 7.0, 2.0), np.array([0.2878, 0.3182, 43.0600])), + (("7.5BG", 7.0, 4.0), np.array([0.2671, 0.3189, 43.0600])), + (("7.5BG", 7.0, 6.0), np.array([0.2490, 0.3186, 43.0600])), + (("7.5BG", 7.0, 8.0), np.array([0.2292, 0.3178, 43.0600])), + (("7.5BG", 7.0, 10.0), np.array([0.2094, 0.3165, 43.0600])), + (("7.5BG", 7.0, 12.0), np.array([0.1914, 0.3148, 43.0600])), + (("7.5BG", 7.0, 14.0), np.array([0.1751, 0.3129, 43.0600])), + (("7.5BG", 7.0, 16.0), np.array([0.1584, 0.3101, 43.0600])), + (("7.5BG", 7.0, 18.0), np.array([0.1427, 0.3076, 43.0600])), + (("10BG", 7.0, 2.0), np.array([0.2869, 0.3143, 43.0600])), + (("10BG", 7.0, 4.0), np.array([0.2642, 0.3109, 43.0600])), + (("10BG", 7.0, 6.0), np.array([0.2448, 0.3069, 43.0600])), + (("10BG", 7.0, 8.0), np.array([0.2235, 0.3014, 43.0600])), + (("10BG", 7.0, 10.0), np.array([0.2035, 0.2956, 43.0600])), + (("10BG", 7.0, 12.0), np.array([0.1841, 0.2892, 43.0600])), + (("10BG", 7.0, 14.0), np.array([0.1671, 0.2832, 43.0600])), + (("10BG", 7.0, 16.0), np.array([0.1489, 0.2768, 43.0600])), + (("2.5B", 7.0, 2.0), np.array([0.2867, 0.3110, 43.0600])), + (("2.5B", 7.0, 4.0), np.array([0.2629, 0.3038, 43.0600])), + (("2.5B", 7.0, 6.0), np.array([0.2418, 0.2960, 43.0600])), + (("2.5B", 7.0, 8.0), np.array([0.2208, 0.2871, 43.0600])), + (("2.5B", 7.0, 10.0), np.array([0.1994, 0.2775, 43.0600])), + (("2.5B", 7.0, 12.0), np.array([0.1797, 0.2672, 43.0600])), + (("2.5B", 7.0, 14.0), np.array([0.1624, 0.2581, 43.0600])), + (("2.5B", 7.0, 16.0), np.array([0.1435, 0.2472, 43.0600])), + (("5B", 7.0, 2.0), np.array([0.2875, 0.3078, 43.0600])), + (("5B", 7.0, 4.0), np.array([0.2633, 0.2972, 43.0600])), + (("5B", 7.0, 6.0), np.array([0.2410, 0.2854, 43.0600])), + (("5B", 7.0, 8.0), np.array([0.2204, 0.2729, 43.0600])), + (("5B", 7.0, 10.0), np.array([0.1986, 0.2579, 43.0600])), + (("5B", 7.0, 12.0), np.array([0.1778, 0.2430, 43.0600])), + (("5B", 7.0, 14.0), np.array([0.1615, 0.2307, 43.0600])), + (("7.5B", 7.0, 2.0), np.array([0.2888, 0.3058, 43.0600])), + (("7.5B", 7.0, 4.0), np.array([0.2651, 0.2927, 43.0600])), + (("7.5B", 7.0, 6.0), np.array([0.2436, 0.2787, 43.0600])), + (("7.5B", 7.0, 8.0), np.array([0.2225, 0.2631, 43.0600])), + (("7.5B", 7.0, 10.0), np.array([0.2016, 0.2466, 43.0600])), + (("7.5B", 7.0, 12.0), np.array([0.1818, 0.2303, 43.0600])), + (("10B", 7.0, 2.0), np.array([0.2908, 0.3039, 43.0600])), + (("10B", 7.0, 4.0), np.array([0.2685, 0.2886, 43.0600])), + (("10B", 7.0, 6.0), np.array([0.2478, 0.2728, 43.0600])), + (("10B", 7.0, 8.0), np.array([0.2277, 0.2559, 43.0600])), + (("10B", 7.0, 10.0), np.array([0.2078, 0.2382, 43.0600])), + (("10B", 7.0, 12.0), np.array([0.1883, 0.2203, 43.0600])), + (("2.5PB", 7.0, 2.0), np.array([0.2932, 0.3025, 43.0600])), + (("2.5PB", 7.0, 4.0), np.array([0.2729, 0.2848, 43.0600])), + (("2.5PB", 7.0, 6.0), np.array([0.2538, 0.2677, 43.0600])), + (("2.5PB", 7.0, 8.0), np.array([0.2352, 0.2498, 43.0600])), + (("2.5PB", 7.0, 10.0), np.array([0.2162, 0.2309, 43.0600])), + (("5PB", 7.0, 2.0), np.array([0.2952, 0.3011, 43.0600])), + (("5PB", 7.0, 4.0), np.array([0.2773, 0.2828, 43.0600])), + (("5PB", 7.0, 6.0), np.array([0.2596, 0.2643, 43.0600])), + (("5PB", 7.0, 8.0), np.array([0.2427, 0.2458, 43.0600])), + (("5PB", 7.0, 10.0), np.array([0.2254, 0.2267, 43.0600])), + (("7.5PB", 7.0, 2.0), np.array([0.2982, 0.3003, 43.0600])), + (("7.5PB", 7.0, 4.0), np.array([0.2833, 0.2809, 43.0600])), + (("7.5PB", 7.0, 6.0), np.array([0.2687, 0.2612, 43.0600])), + (("7.5PB", 7.0, 8.0), np.array([0.2546, 0.2418, 43.0600])), + (("7.5PB", 7.0, 10.0), np.array([0.2410, 0.2224, 43.0600])), + (("10PB", 7.0, 2.0), np.array([0.3005, 0.3000, 43.0600])), + (("10PB", 7.0, 4.0), np.array([0.2886, 0.2801, 43.0600])), + (("10PB", 7.0, 6.0), np.array([0.2776, 0.2612, 43.0600])), + (("10PB", 7.0, 8.0), np.array([0.2670, 0.2425, 43.0600])), + (("10PB", 7.0, 10.0), np.array([0.2563, 0.2240, 43.0600])), + (("10PB", 7.0, 12.0), np.array([0.2465, 0.2058, 43.0600])), + (("2.5P", 7.0, 2.0), np.array([0.3031, 0.3000, 43.0600])), + (("2.5P", 7.0, 4.0), np.array([0.2950, 0.2810, 43.0600])), + (("2.5P", 7.0, 6.0), np.array([0.2873, 0.2633, 43.0600])), + (("2.5P", 7.0, 8.0), np.array([0.2799, 0.2459, 43.0600])), + (("2.5P", 7.0, 10.0), np.array([0.2729, 0.2289, 43.0600])), + (("2.5P", 7.0, 12.0), np.array([0.2664, 0.2127, 43.0600])), + (("5P", 7.0, 2.0), np.array([0.3059, 0.3010, 43.0600])), + (("5P", 7.0, 4.0), np.array([0.3009, 0.2831, 43.0600])), + (("5P", 7.0, 6.0), np.array([0.2961, 0.2663, 43.0600])), + (("5P", 7.0, 8.0), np.array([0.2918, 0.2504, 43.0600])), + (("5P", 7.0, 10.0), np.array([0.2872, 0.2343, 43.0600])), + (("5P", 7.0, 12.0), np.array([0.2833, 0.2197, 43.0600])), + (("5P", 7.0, 14.0), np.array([0.2801, 0.2068, 43.0600])), + (("7.5P", 7.0, 2.0), np.array([0.3109, 0.3037, 43.0600])), + (("7.5P", 7.0, 4.0), np.array([0.3111, 0.2880, 43.0600])), + (("7.5P", 7.0, 6.0), np.array([0.3111, 0.2730, 43.0600])), + (("7.5P", 7.0, 8.0), np.array([0.3109, 0.2584, 43.0600])), + (("7.5P", 7.0, 10.0), np.array([0.3108, 0.2442, 43.0600])), + (("7.5P", 7.0, 12.0), np.array([0.3104, 0.2320, 43.0600])), + (("7.5P", 7.0, 14.0), np.array([0.3101, 0.2192, 43.0600])), + (("7.5P", 7.0, 16.0), np.array([0.3099, 0.2074, 43.0600])), + (("7.5P", 7.0, 18.0), np.array([0.3093, 0.1962, 43.0600])), + (("10P", 7.0, 2.0), np.array([0.3138, 0.3054, 43.0600])), + (("10P", 7.0, 4.0), np.array([0.3181, 0.2920, 43.0600])), + (("10P", 7.0, 6.0), np.array([0.3221, 0.2786, 43.0600])), + (("10P", 7.0, 8.0), np.array([0.3256, 0.2654, 43.0600])), + (("10P", 7.0, 10.0), np.array([0.3288, 0.2531, 43.0600])), + (("10P", 7.0, 12.0), np.array([0.3314, 0.2423, 43.0600])), + (("10P", 7.0, 14.0), np.array([0.3341, 0.2308, 43.0600])), + (("10P", 7.0, 16.0), np.array([0.3368, 0.2192, 43.0600])), + (("10P", 7.0, 18.0), np.array([0.3391, 0.2088, 43.0600])), + (("10P", 7.0, 20.0), np.array([0.3410, 0.1988, 43.0600])), + (("10P", 7.0, 22.0), np.array([0.3430, 0.1883, 43.0600])), + (("2.5RP", 7.0, 2.0), np.array([0.3170, 0.3076, 43.0600])), + (("2.5RP", 7.0, 4.0), np.array([0.3254, 0.2971, 43.0600])), + (("2.5RP", 7.0, 6.0), np.array([0.3338, 0.2854, 43.0600])), + (("2.5RP", 7.0, 8.0), np.array([0.3417, 0.2745, 43.0600])), + (("2.5RP", 7.0, 10.0), np.array([0.3487, 0.2648, 43.0600])), + (("2.5RP", 7.0, 12.0), np.array([0.3555, 0.2545, 43.0600])), + (("2.5RP", 7.0, 14.0), np.array([0.3620, 0.2448, 43.0600])), + (("2.5RP", 7.0, 16.0), np.array([0.3688, 0.2342, 43.0600])), + (("2.5RP", 7.0, 18.0), np.array([0.3751, 0.2241, 43.0600])), + (("2.5RP", 7.0, 20.0), np.array([0.3811, 0.2143, 43.0600])), + (("5RP", 7.0, 2.0), np.array([0.3206, 0.3104, 43.0600])), + (("5RP", 7.0, 4.0), np.array([0.3332, 0.3032, 43.0600])), + (("5RP", 7.0, 6.0), np.array([0.3470, 0.2949, 43.0600])), + (("5RP", 7.0, 8.0), np.array([0.3603, 0.2869, 43.0600])), + (("5RP", 7.0, 10.0), np.array([0.3713, 0.2798, 43.0600])), + (("5RP", 7.0, 12.0), np.array([0.3841, 0.2710, 43.0600])), + (("5RP", 7.0, 14.0), np.array([0.3958, 0.2628, 43.0600])), + (("5RP", 7.0, 16.0), np.array([0.4076, 0.2540, 43.0600])), + (("5RP", 7.0, 18.0), np.array([0.4186, 0.2459, 43.0600])), + (("7.5RP", 7.0, 2.0), np.array([0.3232, 0.3125, 43.0600])), + (("7.5RP", 7.0, 4.0), np.array([0.3389, 0.3079, 43.0600])), + (("7.5RP", 7.0, 6.0), np.array([0.3562, 0.3022, 43.0600])), + (("7.5RP", 7.0, 8.0), np.array([0.3722, 0.2963, 43.0600])), + (("7.5RP", 7.0, 10.0), np.array([0.3871, 0.2906, 43.0600])), + (("7.5RP", 7.0, 12.0), np.array([0.4040, 0.2834, 43.0600])), + (("7.5RP", 7.0, 14.0), np.array([0.4195, 0.2762, 43.0600])), + (("7.5RP", 7.0, 16.0), np.array([0.4346, 0.2689, 43.0600])), + (("10RP", 8.0, 2.0), np.array([0.3218, 0.3152, 59.1000])), + (("10RP", 8.0, 4.0), np.array([0.3412, 0.3135, 59.1000])), + (("10RP", 8.0, 6.0), np.array([0.3600, 0.3112, 59.1000])), + (("10RP", 8.0, 8.0), np.array([0.3800, 0.3082, 59.1000])), + (("10RP", 8.0, 10.0), np.array([0.3983, 0.3049, 59.1000])), + (("2.5R", 8.0, 2.0), np.array([0.3236, 0.3169, 59.1000])), + (("2.5R", 8.0, 4.0), np.array([0.3460, 0.3177, 59.1000])), + (("2.5R", 8.0, 6.0), np.array([0.3671, 0.3175, 59.1000])), + (("2.5R", 8.0, 8.0), np.array([0.3900, 0.3171, 59.1000])), + (("2.5R", 8.0, 10.0), np.array([0.4125, 0.3160, 59.1000])), + (("5R", 8.0, 2.0), np.array([0.3254, 0.3186, 59.1000])), + (("5R", 8.0, 4.0), np.array([0.3510, 0.3224, 59.1000])), + (("5R", 8.0, 6.0), np.array([0.3743, 0.3248, 59.1000])), + (("5R", 8.0, 8.0), np.array([0.4001, 0.3263, 59.1000])), + (("5R", 8.0, 10.0), np.array([0.4249, 0.3270, 59.1000])), + (("7.5R", 8.0, 2.0), np.array([0.3277, 0.3211, 59.1000])), + (("7.5R", 8.0, 4.0), np.array([0.3564, 0.3279, 59.1000])), + (("7.5R", 8.0, 6.0), np.array([0.3830, 0.3335, 59.1000])), + (("7.5R", 8.0, 8.0), np.array([0.4118, 0.3385, 59.1000])), + (("7.5R", 8.0, 10.0), np.array([0.4388, 0.3419, 59.1000])), + (("10R", 8.0, 2.0), np.array([0.3301, 0.3237, 59.1000])), + (("10R", 8.0, 4.0), np.array([0.3621, 0.3349, 59.1000])), + (("10R", 8.0, 6.0), np.array([0.3910, 0.3442, 59.1000])), + (("10R", 8.0, 8.0), np.array([0.4212, 0.3526, 59.1000])), + (("10R", 8.0, 10.0), np.array([0.4490, 0.3589, 59.1000])), + (("2.5YR", 8.0, 2.0), np.array([0.3334, 0.3276, 59.1000])), + (("2.5YR", 8.0, 4.0), np.array([0.3667, 0.3429, 59.1000])), + (("2.5YR", 8.0, 6.0), np.array([0.3960, 0.3547, 59.1000])), + (("2.5YR", 8.0, 8.0), np.array([0.4275, 0.3662, 59.1000])), + (("2.5YR", 8.0, 10.0), np.array([0.4552, 0.3761, 59.1000])), + (("2.5YR", 8.0, 12.0), np.array([0.4852, 0.3847, 59.1000])), + (("5YR", 8.0, 2.0), np.array([0.3373, 0.3330, 59.1000])), + (("5YR", 8.0, 4.0), np.array([0.3690, 0.3510, 59.1000])), + (("5YR", 8.0, 6.0), np.array([0.3988, 0.3663, 59.1000])), + (("5YR", 8.0, 8.0), np.array([0.4310, 0.3820, 59.1000])), + (("5YR", 8.0, 10.0), np.array([0.4576, 0.3938, 59.1000])), + (("5YR", 8.0, 12.0), np.array([0.4849, 0.4050, 59.1000])), + (("5YR", 8.0, 14.0), np.array([0.5088, 0.4145, 59.1000])), + (("7.5YR", 8.0, 2.0), np.array([0.3395, 0.3379, 59.1000])), + (("7.5YR", 8.0, 4.0), np.array([0.3699, 0.3586, 59.1000])), + (("7.5YR", 8.0, 6.0), np.array([0.4000, 0.3770, 59.1000])), + (("7.5YR", 8.0, 8.0), np.array([0.4306, 0.3952, 59.1000])), + (("7.5YR", 8.0, 10.0), np.array([0.4568, 0.4100, 59.1000])), + (("7.5YR", 8.0, 12.0), np.array([0.4816, 0.4232, 59.1000])), + (("7.5YR", 8.0, 14.0), np.array([0.5025, 0.4338, 59.1000])), + (("7.5YR", 8.0, 16.0), np.array([0.5195, 0.4424, 59.1000])), + (("7.5YR", 8.0, 18.0), np.array([0.5316, 0.4480, 59.1000])), + (("7.5YR", 8.0, 20.0), np.array([0.5391, 0.4518, 59.1000])), + (("10YR", 8.0, 2.0), np.array([0.3407, 0.3434, 59.1000])), + (("10YR", 8.0, 4.0), np.array([0.3701, 0.3674, 59.1000])), + (("10YR", 8.0, 6.0), np.array([0.3994, 0.3896, 59.1000])), + (("10YR", 8.0, 8.0), np.array([0.4280, 0.4102, 59.1000])), + (("10YR", 8.0, 10.0), np.array([0.4527, 0.4268, 59.1000])), + (("10YR", 8.0, 12.0), np.array([0.4753, 0.4414, 59.1000])), + (("10YR", 8.0, 14.0), np.array([0.4940, 0.4530, 59.1000])), + (("10YR", 8.0, 16.0), np.array([0.5079, 0.4613, 59.1000])), + (("10YR", 8.0, 18.0), np.array([0.5179, 0.4670, 59.1000])), + (("10YR", 8.0, 20.0), np.array([0.5245, 0.4709, 59.1000])), + (("2.5Y", 8.0, 2.0), np.array([0.3406, 0.3484, 59.1000])), + (("2.5Y", 8.0, 4.0), np.array([0.3684, 0.3751, 59.1000])), + (("2.5Y", 8.0, 6.0), np.array([0.3969, 0.4009, 59.1000])), + (("2.5Y", 8.0, 8.0), np.array([0.4231, 0.4231, 59.1000])), + (("2.5Y", 8.0, 10.0), np.array([0.4469, 0.4423, 59.1000])), + (("2.5Y", 8.0, 12.0), np.array([0.4678, 0.4589, 59.1000])), + (("2.5Y", 8.0, 14.0), np.array([0.4842, 0.4712, 59.1000])), + (("2.5Y", 8.0, 16.0), np.array([0.4957, 0.4800, 59.1000])), + (("2.5Y", 8.0, 18.0), np.array([0.5033, 0.4855, 59.1000])), + (("2.5Y", 8.0, 20.0), np.array([0.5091, 0.4900, 59.1000])), + (("5Y", 8.0, 2.0), np.array([0.3394, 0.3518, 59.1000])), + (("5Y", 8.0, 4.0), np.array([0.3650, 0.3826, 59.1000])), + (("5Y", 8.0, 6.0), np.array([0.3913, 0.4117, 59.1000])), + (("5Y", 8.0, 8.0), np.array([0.4158, 0.4378, 59.1000])), + (("5Y", 8.0, 10.0), np.array([0.4376, 0.4601, 59.1000])), + (("5Y", 8.0, 12.0), np.array([0.4562, 0.4788, 59.1000])), + (("5Y", 8.0, 14.0), np.array([0.4699, 0.4920, 59.1000])), + (("5Y", 8.0, 16.0), np.array([0.4791, 0.5012, 59.1000])), + (("5Y", 8.0, 18.0), np.array([0.4847, 0.5069, 59.1000])), + (("7.5Y", 8.0, 2.0), np.array([0.3379, 0.3540, 59.1000])), + (("7.5Y", 8.0, 4.0), np.array([0.3622, 0.3861, 59.1000])), + (("7.5Y", 8.0, 6.0), np.array([0.3862, 0.4175, 59.1000])), + (("7.5Y", 8.0, 8.0), np.array([0.4088, 0.4466, 59.1000])), + (("7.5Y", 8.0, 10.0), np.array([0.4283, 0.4712, 59.1000])), + (("7.5Y", 8.0, 12.0), np.array([0.4455, 0.4917, 59.1000])), + (("7.5Y", 8.0, 14.0), np.array([0.4574, 0.5062, 59.1000])), + (("7.5Y", 8.0, 16.0), np.array([0.4658, 0.5158, 59.1000])), + (("7.5Y", 8.0, 18.0), np.array([0.4709, 0.5220, 59.1000])), + (("10Y", 8.0, 2.0), np.array([0.3359, 0.3552, 59.1000])), + (("10Y", 8.0, 4.0), np.array([0.3581, 0.3883, 59.1000])), + (("10Y", 8.0, 6.0), np.array([0.3803, 0.4216, 59.1000])), + (("10Y", 8.0, 8.0), np.array([0.4008, 0.4520, 59.1000])), + (("10Y", 8.0, 10.0), np.array([0.4190, 0.4791, 59.1000])), + (("10Y", 8.0, 12.0), np.array([0.4341, 0.5020, 59.1000])), + (("10Y", 8.0, 14.0), np.array([0.4450, 0.5181, 59.1000])), + (("10Y", 8.0, 16.0), np.array([0.4525, 0.5295, 59.1000])), + (("10Y", 8.0, 18.0), np.array([0.4570, 0.5366, 59.1000])), + (("2.5GY", 8.0, 2.0), np.array([0.3327, 0.3555, 59.1000])), + (("2.5GY", 8.0, 4.0), np.array([0.3504, 0.3887, 59.1000])), + (("2.5GY", 8.0, 6.0), np.array([0.3690, 0.4230, 59.1000])), + (("2.5GY", 8.0, 8.0), np.array([0.3858, 0.4550, 59.1000])), + (("2.5GY", 8.0, 10.0), np.array([0.4021, 0.4869, 59.1000])), + (("2.5GY", 8.0, 12.0), np.array([0.4154, 0.5133, 59.1000])), + (("2.5GY", 8.0, 14.0), np.array([0.4261, 0.5344, 59.1000])), + (("2.5GY", 8.0, 16.0), np.array([0.4327, 0.5475, 59.1000])), + (("2.5GY", 8.0, 18.0), np.array([0.4371, 0.5557, 59.1000])), + (("5GY", 8.0, 2.0), np.array([0.3284, 0.3542, 59.1000])), + (("5GY", 8.0, 4.0), np.array([0.3433, 0.3872, 59.1000])), + (("5GY", 8.0, 6.0), np.array([0.3573, 0.4214, 59.1000])), + (("5GY", 8.0, 8.0), np.array([0.3696, 0.4542, 59.1000])), + (("5GY", 8.0, 10.0), np.array([0.3816, 0.4879, 59.1000])), + (("5GY", 8.0, 12.0), np.array([0.3924, 0.5199, 59.1000])), + (("5GY", 8.0, 14.0), np.array([0.4011, 0.5468, 59.1000])), + (("5GY", 8.0, 16.0), np.array([0.4061, 0.5641, 59.1000])), + (("5GY", 8.0, 18.0), np.array([0.4104, 0.5785, 59.1000])), + (("5GY", 8.0, 20.0), np.array([0.4127, 0.5855, 59.1000])), + (("7.5GY", 8.0, 2.0), np.array([0.3194, 0.3502, 59.1000])), + (("7.5GY", 8.0, 4.0), np.array([0.3266, 0.3809, 59.1000])), + (("7.5GY", 8.0, 6.0), np.array([0.3339, 0.4129, 59.1000])), + (("7.5GY", 8.0, 8.0), np.array([0.3408, 0.4452, 59.1000])), + (("7.5GY", 8.0, 10.0), np.array([0.3463, 0.4791, 59.1000])), + (("7.5GY", 8.0, 12.0), np.array([0.3511, 0.5144, 59.1000])), + (("7.5GY", 8.0, 14.0), np.array([0.3546, 0.5490, 59.1000])), + (("7.5GY", 8.0, 16.0), np.array([0.3569, 0.5798, 59.1000])), + (("7.5GY", 8.0, 18.0), np.array([0.3585, 0.6063, 59.1000])), + (("7.5GY", 8.0, 20.0), np.array([0.3592, 0.6235, 59.1000])), + (("10GY", 8.0, 2.0), np.array([0.3121, 0.3459, 59.1000])), + (("10GY", 8.0, 4.0), np.array([0.3140, 0.3727, 59.1000])), + (("10GY", 8.0, 6.0), np.array([0.3150, 0.4014, 59.1000])), + (("10GY", 8.0, 8.0), np.array([0.3149, 0.4284, 59.1000])), + (("10GY", 8.0, 10.0), np.array([0.3140, 0.4601, 59.1000])), + (("10GY", 8.0, 12.0), np.array([0.3124, 0.4926, 59.1000])), + (("10GY", 8.0, 14.0), np.array([0.3091, 0.5247, 59.1000])), + (("10GY", 8.0, 16.0), np.array([0.3043, 0.5578, 59.1000])), + (("10GY", 8.0, 18.0), np.array([0.2987, 0.5919, 59.1000])), + (("10GY", 8.0, 20.0), np.array([0.2918, 0.6255, 59.1000])), + (("10GY", 8.0, 22.0), np.array([0.2846, 0.6564, 59.1000])), + (("10GY", 8.0, 24.0), np.array([0.2781, 0.6840, 59.1000])), + (("2.5G", 8.0, 2.0), np.array([0.3053, 0.3404, 59.1000])), + (("2.5G", 8.0, 4.0), np.array([0.3009, 0.3614, 59.1000])), + (("2.5G", 8.0, 6.0), np.array([0.2952, 0.3851, 59.1000])), + (("2.5G", 8.0, 8.0), np.array([0.2896, 0.4065, 59.1000])), + (("2.5G", 8.0, 10.0), np.array([0.2829, 0.4301, 59.1000])), + (("2.5G", 8.0, 12.0), np.array([0.2743, 0.4554, 59.1000])), + (("2.5G", 8.0, 14.0), np.array([0.2661, 0.4780, 59.1000])), + (("2.5G", 8.0, 16.0), np.array([0.2563, 0.5045, 59.1000])), + (("2.5G", 8.0, 18.0), np.array([0.2451, 0.5309, 59.1000])), + (("2.5G", 8.0, 20.0), np.array([0.2339, 0.5561, 59.1000])), + (("2.5G", 8.0, 22.0), np.array([0.2221, 0.5799, 59.1000])), + (("2.5G", 8.0, 24.0), np.array([0.2091, 0.6033, 59.1000])), + (("5G", 8.0, 2.0), np.array([0.3009, 0.3359, 59.1000])), + (("5G", 8.0, 4.0), np.array([0.2924, 0.3523, 59.1000])), + (("5G", 8.0, 6.0), np.array([0.2822, 0.3702, 59.1000])), + (("5G", 8.0, 8.0), np.array([0.2723, 0.3865, 59.1000])), + (("5G", 8.0, 10.0), np.array([0.2613, 0.4026, 59.1000])), + (("5G", 8.0, 12.0), np.array([0.2489, 0.4191, 59.1000])), + (("5G", 8.0, 14.0), np.array([0.2368, 0.4348, 59.1000])), + (("5G", 8.0, 16.0), np.array([0.2240, 0.4500, 59.1000])), + (("5G", 8.0, 18.0), np.array([0.2103, 0.4652, 59.1000])), + (("5G", 8.0, 20.0), np.array([0.1956, 0.4806, 59.1000])), + (("5G", 8.0, 22.0), np.array([0.1821, 0.4940, 59.1000])), + (("7.5G", 8.0, 2.0), np.array([0.2981, 0.3326, 59.1000])), + (("7.5G", 8.0, 4.0), np.array([0.2874, 0.3464, 59.1000])), + (("7.5G", 8.0, 6.0), np.array([0.2754, 0.3608, 59.1000])), + (("7.5G", 8.0, 8.0), np.array([0.2639, 0.3733, 59.1000])), + (("7.5G", 8.0, 10.0), np.array([0.2515, 0.3867, 59.1000])), + (("7.5G", 8.0, 12.0), np.array([0.2380, 0.4002, 59.1000])), + (("7.5G", 8.0, 14.0), np.array([0.2254, 0.4125, 59.1000])), + (("7.5G", 8.0, 16.0), np.array([0.2120, 0.4252, 59.1000])), + (("7.5G", 8.0, 18.0), np.array([0.1980, 0.4372, 59.1000])), + (("7.5G", 8.0, 20.0), np.array([0.1845, 0.4492, 59.1000])), + (("10G", 8.0, 2.0), np.array([0.2957, 0.3293, 59.1000])), + (("10G", 8.0, 4.0), np.array([0.2828, 0.3403, 59.1000])), + (("10G", 8.0, 6.0), np.array([0.2693, 0.3512, 59.1000])), + (("10G", 8.0, 8.0), np.array([0.2564, 0.3611, 59.1000])), + (("10G", 8.0, 10.0), np.array([0.2430, 0.3710, 59.1000])), + (("10G", 8.0, 12.0), np.array([0.2282, 0.3811, 59.1000])), + (("10G", 8.0, 14.0), np.array([0.2148, 0.3903, 59.1000])), + (("10G", 8.0, 16.0), np.array([0.2012, 0.3992, 59.1000])), + (("10G", 8.0, 18.0), np.array([0.1866, 0.4086, 59.1000])), + (("10G", 8.0, 20.0), np.array([0.1734, 0.4164, 59.1000])), + (("2.5BG", 8.0, 2.0), np.array([0.2940, 0.3268, 59.1000])), + (("2.5BG", 8.0, 4.0), np.array([0.2791, 0.3351, 59.1000])), + (("2.5BG", 8.0, 6.0), np.array([0.2647, 0.3429, 59.1000])), + (("2.5BG", 8.0, 8.0), np.array([0.2500, 0.3500, 59.1000])), + (("2.5BG", 8.0, 10.0), np.array([0.2352, 0.3566, 59.1000])), + (("2.5BG", 8.0, 12.0), np.array([0.2196, 0.3630, 59.1000])), + (("2.5BG", 8.0, 14.0), np.array([0.2057, 0.3681, 59.1000])), + (("2.5BG", 8.0, 16.0), np.array([0.1915, 0.3732, 59.1000])), + (("2.5BG", 8.0, 18.0), np.array([0.1759, 0.3782, 59.1000])), + (("5BG", 8.0, 2.0), np.array([0.2919, 0.3228, 59.1000])), + (("5BG", 8.0, 4.0), np.array([0.2752, 0.3278, 59.1000])), + (("5BG", 8.0, 6.0), np.array([0.2588, 0.3318, 59.1000])), + (("5BG", 8.0, 8.0), np.array([0.2419, 0.3352, 59.1000])), + (("5BG", 8.0, 10.0), np.array([0.2264, 0.3383, 59.1000])), + (("5BG", 8.0, 12.0), np.array([0.2101, 0.3412, 59.1000])), + (("5BG", 8.0, 14.0), np.array([0.1958, 0.3432, 59.1000])), + (("5BG", 8.0, 16.0), np.array([0.1814, 0.3450, 59.1000])), + (("7.5BG", 8.0, 2.0), np.array([0.2900, 0.3183, 59.1000])), + (("7.5BG", 8.0, 4.0), np.array([0.2718, 0.3200, 59.1000])), + (("7.5BG", 8.0, 6.0), np.array([0.2525, 0.3198, 59.1000])), + (("7.5BG", 8.0, 8.0), np.array([0.2352, 0.3198, 59.1000])), + (("7.5BG", 8.0, 10.0), np.array([0.2184, 0.3196, 59.1000])), + (("7.5BG", 8.0, 12.0), np.array([0.2010, 0.3188, 59.1000])), + (("7.5BG", 8.0, 14.0), np.array([0.1868, 0.3179, 59.1000])), + (("7.5BG", 8.0, 16.0), np.array([0.1721, 0.3168, 59.1000])), + (("10BG", 8.0, 2.0), np.array([0.2894, 0.3152, 59.1000])), + (("10BG", 8.0, 4.0), np.array([0.2686, 0.3130, 59.1000])), + (("10BG", 8.0, 6.0), np.array([0.2489, 0.3099, 59.1000])), + (("10BG", 8.0, 8.0), np.array([0.2302, 0.3063, 59.1000])), + (("10BG", 8.0, 10.0), np.array([0.2120, 0.3025, 59.1000])), + (("10BG", 8.0, 12.0), np.array([0.1937, 0.2978, 59.1000])), + (("10BG", 8.0, 14.0), np.array([0.1788, 0.2936, 59.1000])), + (("2.5B", 8.0, 2.0), np.array([0.2897, 0.3124, 59.1000])), + (("2.5B", 8.0, 4.0), np.array([0.2668, 0.3067, 59.1000])), + (("2.5B", 8.0, 6.0), np.array([0.2462, 0.3000, 59.1000])), + (("2.5B", 8.0, 8.0), np.array([0.2264, 0.2923, 59.1000])), + (("2.5B", 8.0, 10.0), np.array([0.2066, 0.2839, 59.1000])), + (("2.5B", 8.0, 12.0), np.array([0.1877, 0.2752, 59.1000])), + (("5B", 8.0, 2.0), np.array([0.2908, 0.3096, 59.1000])), + (("5B", 8.0, 4.0), np.array([0.2671, 0.2998, 59.1000])), + (("5B", 8.0, 6.0), np.array([0.2457, 0.2888, 59.1000])), + (("5B", 8.0, 8.0), np.array([0.2237, 0.2761, 59.1000])), + (("7.5B", 8.0, 2.0), np.array([0.2922, 0.3077, 59.1000])), + (("7.5B", 8.0, 4.0), np.array([0.2688, 0.2956, 59.1000])), + (("7.5B", 8.0, 6.0), np.array([0.2472, 0.2821, 59.1000])), + (("7.5B", 8.0, 8.0), np.array([0.2252, 0.2668, 59.1000])), + (("10B", 8.0, 2.0), np.array([0.2935, 0.3062, 59.1000])), + (("10B", 8.0, 4.0), np.array([0.2718, 0.2911, 59.1000])), + (("10B", 8.0, 6.0), np.array([0.2512, 0.2760, 59.1000])), + (("10B", 8.0, 8.0), np.array([0.2294, 0.2587, 59.1000])), + (("2.5PB", 8.0, 2.0), np.array([0.2957, 0.3047, 59.1000])), + (("2.5PB", 8.0, 4.0), np.array([0.2758, 0.2879, 59.1000])), + (("2.5PB", 8.0, 6.0), np.array([0.2562, 0.2709, 59.1000])), + (("5PB", 8.0, 2.0), np.array([0.2974, 0.3039, 59.1000])), + (("5PB", 8.0, 4.0), np.array([0.2798, 0.2861, 59.1000])), + (("5PB", 8.0, 6.0), np.array([0.2614, 0.2670, 59.1000])), + (("7.5PB", 8.0, 2.0), np.array([0.3003, 0.3034, 59.1000])), + (("7.5PB", 8.0, 4.0), np.array([0.2856, 0.2846, 59.1000])), + (("7.5PB", 8.0, 6.0), np.array([0.2702, 0.2648, 59.1000])), + (("10PB", 8.0, 2.0), np.array([0.3027, 0.3035, 59.1000])), + (("10PB", 8.0, 4.0), np.array([0.2911, 0.2848, 59.1000])), + (("10PB", 8.0, 6.0), np.array([0.2792, 0.2649, 59.1000])), + (("10PB", 8.0, 8.0), np.array([0.2677, 0.2443, 59.1000])), + (("2.5P", 8.0, 2.0), np.array([0.3048, 0.3040, 59.1000])), + (("2.5P", 8.0, 4.0), np.array([0.2962, 0.2850, 59.1000])), + (("2.5P", 8.0, 6.0), np.array([0.2881, 0.2671, 59.1000])), + (("2.5P", 8.0, 8.0), np.array([0.2800, 0.2488, 59.1000])), + (("5P", 8.0, 2.0), np.array([0.3065, 0.3047, 59.1000])), + (("5P", 8.0, 4.0), np.array([0.3012, 0.2868, 59.1000])), + (("5P", 8.0, 6.0), np.array([0.2963, 0.2704, 59.1000])), + (("5P", 8.0, 8.0), np.array([0.2914, 0.2534, 59.1000])), + (("5P", 8.0, 10.0), np.array([0.2870, 0.2380, 59.1000])), + (("7.5P", 8.0, 2.0), np.array([0.3107, 0.3070, 59.1000])), + (("7.5P", 8.0, 4.0), np.array([0.3114, 0.2915, 59.1000])), + (("7.5P", 8.0, 6.0), np.array([0.3114, 0.2785, 59.1000])), + (("7.5P", 8.0, 8.0), np.array([0.3116, 0.2626, 59.1000])), + (("7.5P", 8.0, 10.0), np.array([0.3116, 0.2497, 59.1000])), + (("7.5P", 8.0, 12.0), np.array([0.3117, 0.2370, 59.1000])), + (("10P", 8.0, 2.0), np.array([0.3131, 0.3084, 59.1000])), + (("10P", 8.0, 4.0), np.array([0.3175, 0.2955, 59.1000])), + (("10P", 8.0, 6.0), np.array([0.3213, 0.2829, 59.1000])), + (("10P", 8.0, 8.0), np.array([0.3250, 0.2700, 59.1000])), + (("10P", 8.0, 10.0), np.array([0.3282, 0.2582, 59.1000])), + (("10P", 8.0, 12.0), np.array([0.3312, 0.2470, 59.1000])), + (("10P", 8.0, 14.0), np.array([0.3342, 0.2349, 59.1000])), + (("2.5RP", 8.0, 2.0), np.array([0.3154, 0.3100, 59.1000])), + (("2.5RP", 8.0, 4.0), np.array([0.3239, 0.3000, 59.1000])), + (("2.5RP", 8.0, 6.0), np.array([0.3327, 0.2898, 59.1000])), + (("2.5RP", 8.0, 8.0), np.array([0.3406, 0.2793, 59.1000])), + (("2.5RP", 8.0, 10.0), np.array([0.3479, 0.2699, 59.1000])), + (("2.5RP", 8.0, 12.0), np.array([0.3552, 0.2594, 59.1000])), + (("2.5RP", 8.0, 14.0), np.array([0.3621, 0.2496, 59.1000])), + (("5RP", 8.0, 2.0), np.array([0.3180, 0.3120, 59.1000])), + (("5RP", 8.0, 4.0), np.array([0.3308, 0.3052, 59.1000])), + (("5RP", 8.0, 6.0), np.array([0.3440, 0.2978, 59.1000])), + (("5RP", 8.0, 8.0), np.array([0.3570, 0.2900, 59.1000])), + (("5RP", 8.0, 10.0), np.array([0.3685, 0.2828, 59.1000])), + (("5RP", 8.0, 12.0), np.array([0.3818, 0.2742, 59.1000])), + (("7.5RP", 8.0, 2.0), np.array([0.3200, 0.3136, 59.1000])), + (("7.5RP", 8.0, 4.0), np.array([0.3360, 0.3092, 59.1000])), + (("7.5RP", 8.0, 6.0), np.array([0.3521, 0.3042, 59.1000])), + (("7.5RP", 8.0, 8.0), np.array([0.3682, 0.2983, 59.1000])), + (("7.5RP", 8.0, 10.0), np.array([0.3830, 0.2930, 59.1000])), + (("7.5RP", 8.0, 12.0), np.array([0.4002, 0.2859, 59.1000])), + (("10RP", 9.0, 2.0), np.array([0.3205, 0.3155, 78.6600])), + (("10RP", 9.0, 4.0), np.array([0.3400, 0.3140, 78.6600])), + (("10RP", 9.0, 6.0), np.array([0.3590, 0.3118, 78.6600])), + (("2.5R", 9.0, 2.0), np.array([0.3210, 0.3168, 78.6600])), + (("2.5R", 9.0, 4.0), np.array([0.3445, 0.3179, 78.6600])), + (("2.5R", 9.0, 6.0), np.array([0.3665, 0.3183, 78.6600])), + (("5R", 9.0, 2.0), np.array([0.3240, 0.3188, 78.6600])), + (("5R", 9.0, 4.0), np.array([0.3495, 0.3226, 78.6600])), + (("5R", 9.0, 6.0), np.array([0.3734, 0.3256, 78.6600])), + (("7.5R", 9.0, 2.0), np.array([0.3263, 0.3210, 78.6600])), + (("7.5R", 9.0, 4.0), np.array([0.3551, 0.3283, 78.6600])), + (("7.5R", 9.0, 6.0), np.array([0.3812, 0.3348, 78.6600])), + (("10R", 9.0, 2.0), np.array([0.3284, 0.3233, 78.6600])), + (("10R", 9.0, 4.0), np.array([0.3600, 0.3348, 78.6600])), + (("10R", 9.0, 6.0), np.array([0.3880, 0.3439, 78.6600])), + (("2.5YR", 9.0, 2.0), np.array([0.3320, 0.3273, 78.6600])), + (("2.5YR", 9.0, 4.0), np.array([0.3641, 0.3422, 78.6600])), + (("2.5YR", 9.0, 6.0), np.array([0.3927, 0.3550, 78.6600])), + (("5YR", 9.0, 2.0), np.array([0.3353, 0.3325, 78.6600])), + (("5YR", 9.0, 4.0), np.array([0.3668, 0.3509, 78.6600])), + (("5YR", 9.0, 6.0), np.array([0.3948, 0.3659, 78.6600])), + (("7.5YR", 9.0, 2.0), np.array([0.3380, 0.3377, 78.6600])), + (("7.5YR", 9.0, 4.0), np.array([0.3679, 0.3585, 78.6600])), + (("7.5YR", 9.0, 6.0), np.array([0.3950, 0.3763, 78.6600])), + (("7.5YR", 9.0, 8.0), np.array([0.4220, 0.3930, 78.6600])), + (("10YR", 9.0, 2.0), np.array([0.3392, 0.3430, 78.6600])), + (("10YR", 9.0, 4.0), np.array([0.3677, 0.3668, 78.6600])), + (("10YR", 9.0, 6.0), np.array([0.3941, 0.3877, 78.6600])), + (("10YR", 9.0, 8.0), np.array([0.4199, 0.4069, 78.6600])), + (("2.5Y", 9.0, 2.0), np.array([0.3390, 0.3472, 78.6600])), + (("2.5Y", 9.0, 4.0), np.array([0.3655, 0.3738, 78.6600])), + (("2.5Y", 9.0, 6.0), np.array([0.3910, 0.3972, 78.6600])), + (("2.5Y", 9.0, 8.0), np.array([0.4154, 0.4186, 78.6600])), + (("2.5Y", 9.0, 10.0), np.array([0.4370, 0.4369, 78.6600])), + (("2.5Y", 9.0, 12.0), np.array([0.4569, 0.4527, 78.6600])), + (("5Y", 9.0, 2.0), np.array([0.3378, 0.3504, 78.6600])), + (("5Y", 9.0, 4.0), np.array([0.3621, 0.3799, 78.6600])), + (("5Y", 9.0, 6.0), np.array([0.3858, 0.4071, 78.6600])), + (("5Y", 9.0, 8.0), np.array([0.4080, 0.4319, 78.6600])), + (("5Y", 9.0, 10.0), np.array([0.4275, 0.4529, 78.6600])), + (("5Y", 9.0, 12.0), np.array([0.4455, 0.4719, 78.6600])), + (("5Y", 9.0, 14.0), np.array([0.4602, 0.4869, 78.6600])), + (("5Y", 9.0, 16.0), np.array([0.4711, 0.4977, 78.6600])), + (("5Y", 9.0, 18.0), np.array([0.4782, 0.5049, 78.6600])), + (("5Y", 9.0, 20.0), np.array([0.4830, 0.5092, 78.6600])), + (("7.5Y", 9.0, 2.0), np.array([0.3365, 0.3527, 78.6600])), + (("7.5Y", 9.0, 4.0), np.array([0.3591, 0.3832, 78.6600])), + (("7.5Y", 9.0, 6.0), np.array([0.3811, 0.4123, 78.6600])), + (("7.5Y", 9.0, 8.0), np.array([0.4019, 0.4392, 78.6600])), + (("7.5Y", 9.0, 10.0), np.array([0.4201, 0.4622, 78.6600])), + (("7.5Y", 9.0, 12.0), np.array([0.4369, 0.4829, 78.6600])), + (("7.5Y", 9.0, 14.0), np.array([0.4503, 0.4993, 78.6600])), + (("7.5Y", 9.0, 16.0), np.array([0.4595, 0.5104, 78.6600])), + (("7.5Y", 9.0, 18.0), np.array([0.4663, 0.5188, 78.6600])), + (("10Y", 9.0, 2.0), np.array([0.3349, 0.3537, 78.6600])), + (("10Y", 9.0, 4.0), np.array([0.3558, 0.3852, 78.6600])), + (("10Y", 9.0, 6.0), np.array([0.3761, 0.4155, 78.6600])), + (("10Y", 9.0, 8.0), np.array([0.3957, 0.4450, 78.6600])), + (("10Y", 9.0, 10.0), np.array([0.4120, 0.4694, 78.6600])), + (("10Y", 9.0, 12.0), np.array([0.4271, 0.4920, 78.6600])), + (("10Y", 9.0, 14.0), np.array([0.4393, 0.5101, 78.6600])), + (("10Y", 9.0, 16.0), np.array([0.4477, 0.5225, 78.6600])), + (("10Y", 9.0, 18.0), np.array([0.4540, 0.5320, 78.6600])), + (("2.5GY", 9.0, 2.0), np.array([0.3321, 0.3539, 78.6600])), + (("2.5GY", 9.0, 4.0), np.array([0.3499, 0.3866, 78.6600])), + (("2.5GY", 9.0, 6.0), np.array([0.3670, 0.4178, 78.6600])), + (("2.5GY", 9.0, 8.0), np.array([0.3834, 0.4490, 78.6600])), + (("2.5GY", 9.0, 10.0), np.array([0.3973, 0.4761, 78.6600])), + (("2.5GY", 9.0, 12.0), np.array([0.4108, 0.5028, 78.6600])), + (("2.5GY", 9.0, 14.0), np.array([0.4212, 0.5237, 78.6600])), + (("2.5GY", 9.0, 16.0), np.array([0.4288, 0.5383, 78.6600])), + (("2.5GY", 9.0, 18.0), np.array([0.4354, 0.5508, 78.6600])), + (("5GY", 9.0, 2.0), np.array([0.3284, 0.3534, 78.6600])), + (("5GY", 9.0, 4.0), np.array([0.3437, 0.3861, 78.6600])), + (("5GY", 9.0, 6.0), np.array([0.3572, 0.4179, 78.6600])), + (("5GY", 9.0, 8.0), np.array([0.3698, 0.4497, 78.6600])), + (("5GY", 9.0, 10.0), np.array([0.3810, 0.4791, 78.6600])), + (("5GY", 9.0, 12.0), np.array([0.3911, 0.5082, 78.6600])), + (("5GY", 9.0, 14.0), np.array([0.3993, 0.5329, 78.6600])), + (("5GY", 9.0, 16.0), np.array([0.4058, 0.5541, 78.6600])), + (("5GY", 9.0, 18.0), np.array([0.4108, 0.5699, 78.6600])), + (("7.5GY", 9.0, 2.0), np.array([0.3198, 0.3500, 78.6600])), + (("7.5GY", 9.0, 4.0), np.array([0.3274, 0.3793, 78.6600])), + (("7.5GY", 9.0, 6.0), np.array([0.3351, 0.4111, 78.6600])), + (("7.5GY", 9.0, 8.0), np.array([0.3414, 0.4415, 78.6600])), + (("7.5GY", 9.0, 10.0), np.array([0.3471, 0.4735, 78.6600])), + (("7.5GY", 9.0, 12.0), np.array([0.3518, 0.5042, 78.6600])), + (("7.5GY", 9.0, 14.0), np.array([0.3551, 0.5339, 78.6600])), + (("7.5GY", 9.0, 16.0), np.array([0.3581, 0.5654, 78.6600])), + (("7.5GY", 9.0, 18.0), np.array([0.3602, 0.5920, 78.6600])), + (("10GY", 9.0, 2.0), np.array([0.3124, 0.3454, 78.6600])), + (("10GY", 9.0, 4.0), np.array([0.3144, 0.3711, 78.6600])), + (("10GY", 9.0, 6.0), np.array([0.3153, 0.4008, 78.6600])), + (("10GY", 9.0, 8.0), np.array([0.3157, 0.4259, 78.6600])), + (("10GY", 9.0, 10.0), np.array([0.3155, 0.4558, 78.6600])), + (("10GY", 9.0, 12.0), np.array([0.3139, 0.4829, 78.6600])), + (("10GY", 9.0, 14.0), np.array([0.3115, 0.5129, 78.6600])), + (("10GY", 9.0, 16.0), np.array([0.3079, 0.5440, 78.6600])), + (("10GY", 9.0, 18.0), np.array([0.3032, 0.5748, 78.6600])), + (("2.5G", 9.0, 2.0), np.array([0.3058, 0.3400, 78.6600])), + (("2.5G", 9.0, 4.0), np.array([0.3018, 0.3606, 78.6600])), + (("2.5G", 9.0, 6.0), np.array([0.2966, 0.3846, 78.6600])), + (("2.5G", 9.0, 8.0), np.array([0.2912, 0.4054, 78.6600])), + (("2.5G", 9.0, 10.0), np.array([0.2851, 0.4275, 78.6600])), + (("2.5G", 9.0, 12.0), np.array([0.2786, 0.4491, 78.6600])), + (("2.5G", 9.0, 14.0), np.array([0.2711, 0.4726, 78.6600])), + (("2.5G", 9.0, 16.0), np.array([0.2630, 0.4966, 78.6600])), + (("5G", 9.0, 2.0), np.array([0.3017, 0.3357, 78.6600])), + (("5G", 9.0, 4.0), np.array([0.2933, 0.3519, 78.6600])), + (("5G", 9.0, 6.0), np.array([0.2832, 0.3697, 78.6600])), + (("5G", 9.0, 8.0), np.array([0.2735, 0.3854, 78.6600])), + (("5G", 9.0, 10.0), np.array([0.2639, 0.4001, 78.6600])), + (("5G", 9.0, 12.0), np.array([0.2528, 0.4160, 78.6600])), + (("7.5G", 9.0, 2.0), np.array([0.2987, 0.3323, 78.6600])), + (("7.5G", 9.0, 4.0), np.array([0.2882, 0.3461, 78.6600])), + (("7.5G", 9.0, 6.0), np.array([0.2763, 0.3607, 78.6600])), + (("7.5G", 9.0, 8.0), np.array([0.2652, 0.3738, 78.6600])), + (("7.5G", 9.0, 10.0), np.array([0.2545, 0.3855, 78.6600])), + (("7.5G", 9.0, 12.0), np.array([0.2419, 0.3985, 78.6600])), + (("10G", 9.0, 2.0), np.array([0.2965, 0.3293, 78.6600])), + (("10G", 9.0, 4.0), np.array([0.2840, 0.3402, 78.6600])), + (("10G", 9.0, 6.0), np.array([0.2703, 0.3513, 78.6600])), + (("10G", 9.0, 8.0), np.array([0.2574, 0.3618, 78.6600])), + (("10G", 9.0, 10.0), np.array([0.2457, 0.3702, 78.6600])), + (("10G", 9.0, 12.0), np.array([0.2325, 0.3796, 78.6600])), + (("2.5BG", 9.0, 2.0), np.array([0.2947, 0.3267, 78.6600])), + (("2.5BG", 9.0, 4.0), np.array([0.2805, 0.3349, 78.6600])), + (("2.5BG", 9.0, 6.0), np.array([0.2652, 0.3433, 78.6600])), + (("2.5BG", 9.0, 8.0), np.array([0.2509, 0.3507, 78.6600])), + (("2.5BG", 9.0, 10.0), np.array([0.2382, 0.3568, 78.6600])), + (("5BG", 9.0, 2.0), np.array([0.2930, 0.3232, 78.6600])), + (("5BG", 9.0, 4.0), np.array([0.2768, 0.3287, 78.6600])), + (("5BG", 9.0, 6.0), np.array([0.2599, 0.3338, 78.6600])), + (("5BG", 9.0, 8.0), np.array([0.2437, 0.3378, 78.6600])), + (("5BG", 9.0, 10.0), np.array([0.2301, 0.3405, 78.6600])), + (("7.5BG", 9.0, 2.0), np.array([0.2911, 0.3188, 78.6600])), + (("7.5BG", 9.0, 4.0), np.array([0.2728, 0.3208, 78.6600])), + (("7.5BG", 9.0, 6.0), np.array([0.2543, 0.3220, 78.6600])), + (("7.5BG", 9.0, 8.0), np.array([0.2361, 0.3225, 78.6600])), + (("7.5BG", 9.0, 10.0), np.array([0.2215, 0.3226, 78.6600])), + (("10BG", 9.0, 2.0), np.array([0.2907, 0.3159, 78.6600])), + (("10BG", 9.0, 4.0), np.array([0.2700, 0.3140, 78.6600])), + (("10BG", 9.0, 6.0), np.array([0.2501, 0.3118, 78.6600])), + (("2.5B", 9.0, 2.0), np.array([0.2909, 0.3125, 78.6600])), + (("2.5B", 9.0, 4.0), np.array([0.2680, 0.3073, 78.6600])), + (("5B", 9.0, 2.0), np.array([0.2919, 0.3102, 78.6600])), + (("5B", 9.0, 4.0), np.array([0.2675, 0.3005, 78.6600])), + (("7.5B", 9.0, 2.0), np.array([0.2937, 0.3087, 78.6600])), + (("7.5B", 9.0, 4.0), np.array([0.2688, 0.2961, 78.6600])), + (("10B", 9.0, 2.0), np.array([0.2949, 0.3076, 78.6600])), + (("10B", 9.0, 4.0), np.array([0.2712, 0.2924, 78.6600])), + (("2.5PB", 9.0, 2.0), np.array([0.2975, 0.3063, 78.6600])), + (("5PB", 9.0, 2.0), np.array([0.2991, 0.3057, 78.6600])), + (("7.5PB", 9.0, 2.0), np.array([0.3015, 0.3052, 78.6600])), + (("10PB", 9.0, 2.0), np.array([0.3038, 0.3054, 78.6600])), + (("10PB", 9.0, 4.0), np.array([0.2910, 0.2850, 78.6600])), + (("2.5P", 9.0, 2.0), np.array([0.3050, 0.3051, 78.6600])), + (("2.5P", 9.0, 4.0), np.array([0.2963, 0.2865, 78.6600])), + (("5P", 9.0, 2.0), np.array([0.3067, 0.3060, 78.6600])), + (("5P", 9.0, 4.0), np.array([0.3003, 0.2870, 78.6600])), + (("7.5P", 9.0, 2.0), np.array([0.3107, 0.3081, 78.6600])), + (("7.5P", 9.0, 4.0), np.array([0.3117, 0.2928, 78.6600])), + (("7.5P", 9.0, 6.0), np.array([0.3120, 0.2788, 78.6600])), + (("10P", 9.0, 2.0), np.array([0.3128, 0.3094, 78.6600])), + (("10P", 9.0, 4.0), np.array([0.3176, 0.2966, 78.6600])), + (("10P", 9.0, 6.0), np.array([0.3218, 0.2845, 78.6600])), + (("2.5RP", 9.0, 2.0), np.array([0.3149, 0.3108, 78.6600])), + (("2.5RP", 9.0, 4.0), np.array([0.3234, 0.3010, 78.6600])), + (("2.5RP", 9.0, 6.0), np.array([0.3322, 0.2910, 78.6600])), + (("5RP", 9.0, 2.0), np.array([0.3172, 0.3126, 78.6600])), + (("5RP", 9.0, 4.0), np.array([0.3301, 0.3060, 78.6600])), + (("5RP", 9.0, 6.0), np.array([0.3431, 0.2988, 78.6600])), + (("7.5RP", 9.0, 2.0), np.array([0.3190, 0.3141, 78.6600])), + (("7.5RP", 9.0, 4.0), np.array([0.3350, 0.3099, 78.6600])), + (("7.5RP", 9.0, 6.0), np.array([0.3512, 0.3052, 78.6600])), +) # yapf: enable """ -*Real*, within MacAdam limits, published *Munsell* colours. +*Real*, within MacAdam limits, published *Munsell* colours as a *tuple* as +follows:: + + ( + (('hue', 'value', 'chroma'), np.array(['x', 'y', 'Y'])), + ..., + (('hue', 'value', 'chroma'), np.array(['x', 'y', 'Y'])), + ) References ---------- :cite:`MunsellColorSciencec` - -MUNSELL_COLOURS_REAL : tuple -(('hue', 'value', 'chroma'), np.array(['x', 'y', 'Y']))) """ diff --git a/colour/notation/hexadecimal.py b/colour/notation/hexadecimal.py index 0faf8949c2..1a796897db 100644 --- a/colour/notation/hexadecimal.py +++ b/colour/notation/hexadecimal.py @@ -1,49 +1,57 @@ -# -*- coding: utf-8 -*- """ Hexadecimal Notation ==================== -Defines objects for hexadecimal notation: +Defines the objects for hexadecimal notation: - :func:`colour.notation.RGB_to_HEX` - :func:`colour.notation.HEX_to_RGB` """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +from colour.algebra import normalise_maximum +from colour.hints import ArrayLike, List, NDArray, StrOrArrayLike, StrOrNDArray from colour.models import eotf_inverse_sRGB, eotf_sRGB -from colour.utilities import (as_float_array, from_range_1, normalise_maximum, - to_domain_1, usage_warning) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['RGB_to_HEX', 'HEX_to_RGB'] - - -def RGB_to_HEX(RGB): +from colour.utilities import ( + as_float_array, + as_int_array, + from_range_1, + to_domain_1, + usage_warning, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "RGB_to_HEX", + "HEX_to_RGB", +] + + +def RGB_to_HEX(RGB: ArrayLike) -> StrOrNDArray: """ - Converts from *RGB* colourspace to hexadecimal representation. + Convert from *RGB* colourspace to hexadecimal representation. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array. Returns ------- - unicode + :class:`str` or :class:`numpy.array` Hexadecimal representation. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -53,8 +61,7 @@ def RGB_to_HEX(RGB): Examples -------- >>> RGB = np.array([0.66666667, 0.86666667, 1.00000000]) - >>> # Doctests skip for Python 2.x compatibility. - >>> RGB_to_HEX(RGB) # doctest: +SKIP + >>> RGB_to_HEX(RGB) '#aaddff' """ @@ -63,42 +70,43 @@ def RGB_to_HEX(RGB): if np.any(RGB < 0): usage_warning( '"RGB" array contains negative values, those will be clipped, ' - 'unpredictable results may occur!') + "unpredictable results may occur!" + ) - RGB = np.clip(RGB, 0, np.inf) + RGB = as_float_array(np.clip(RGB, 0, np.inf)) if np.any(RGB > 1): usage_warning( '"RGB" array contains values over 1 and will be normalised, ' - 'unpredictable results may occur!') + "unpredictable results may occur!" + ) RGB = eotf_inverse_sRGB(normalise_maximum(eotf_sRGB(RGB))) - to_HEX = np.vectorize('{0:02x}'.format) + to_HEX = np.vectorize("{:02x}".format) - HEX = to_HEX((RGB * 255).astype(np.uint8)).astype(object) - HEX = np.asarray('#') + HEX[..., 0] + HEX[..., 1] + HEX[..., 2] + HEX = to_HEX(as_int_array(RGB * 255, dtype=np.uint8)).astype(object) + HEX = np.asarray("#") + HEX[..., 0] + HEX[..., 1] + HEX[..., 2] return HEX -def HEX_to_RGB(HEX): +def HEX_to_RGB(HEX: StrOrArrayLike) -> NDArray: """ - Converts from hexadecimal representation to *RGB* colourspace. + Convert from hexadecimal representation to *RGB* colourspace. Parameters ---------- - HEX : unicode or array_like + HEX Hexadecimal representation. Returns ------- - ndarray + :class:`numpy.array` *RGB* colourspace array. Notes ----- - +-----------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +===========+=======================+===============+ @@ -112,15 +120,17 @@ def HEX_to_RGB(HEX): array([ 0.6666666..., 0.8666666..., 1. ]) """ - HEX = np.core.defchararray.lstrip(HEX, '#') + HEX = np.core.defchararray.lstrip(HEX, "#") # type: ignore[arg-type] - def to_RGB(x): - """ - Converts given hexadecimal representation to *RGB*. - """ + def to_RGB(x: List) -> List: + """Convert given hexadecimal representation to *RGB*.""" l_x = len(x) - return [int(x[i:i + l_x // 3], 16) for i in range(0, l_x, l_x // 3)] + + return [ + int(x[i : i + l_x // 3], 16) # type: ignore[call-overload] + for i in range(0, l_x, l_x // 3) + ] to_RGB_v = np.vectorize(to_RGB, otypes=[np.ndarray]) diff --git a/colour/notation/munsell.py b/colour/notation/munsell.py index f9a771c8b3..03b1317265 100644 --- a/colour/notation/munsell.py +++ b/colour/notation/munsell.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Munsell Renotation System ========================= @@ -34,8 +33,8 @@ Notes ----- -- The Munsell Renotation data commonly available within the all.dat, - experimental.dat and real.dat files features *CIE xyY* colourspace values +- The Munsell Renotation data commonly available within the *all.dat*, + *experimental.dat* and *real.dat* files features *CIE xyY* colourspace values that are scaled by a :math:`1 / 0.975 \\simeq 1.02568` factor. If you are performing conversions using *Munsell* *Colorlab* specification, e.g. *2.5R 9/2*, according to *ASTM D1535-08e1* method, you should not @@ -114,198 +113,310 @@ Application, 20(3), 156-167. doi:10.1002/col.5080200305 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import re -from collections import OrderedDict -from colour.algebra import (Extrapolator, LinearInterpolator, - cartesian_to_cylindrical, euclidean_distance, - polar_to_cartesian, spow) +from colour.algebra import ( + Extrapolator, + LinearInterpolator, + cartesian_to_cylindrical, + euclidean_distance, + polar_to_cartesian, + spow, +) from colour.colorimetry import CCS_ILLUMINANTS, luminance_ASTMD1535 -from colour.constants import (DEFAULT_FLOAT_DTYPE, DEFAULT_INT_DTYPE, - INTEGER_THRESHOLD, FLOATING_POINT_NUMBER_PATTERN) +from colour.constants import ( + INTEGER_THRESHOLD, + FLOATING_POINT_NUMBER_PATTERN, +) +from colour.hints import ( + ArrayLike, + Boolean, + Dict, + Floating, + FloatingOrArrayLike, + FloatingOrNDArray, + Integer, + Literal, + NDArray, + Optional, + StrOrArrayLike, + StrOrNDArray, + Tuple, + Union, +) from colour.models import Lab_to_LCHab, XYZ_to_Lab, XYZ_to_xy, xyY_to_XYZ from colour.volume import is_within_macadam_limits from colour.notation import MUNSELL_COLOURS_ALL from colour.utilities import ( - CaseInsensitiveMapping, Lookup, as_float_array, as_float, as_int, - as_numeric, domain_range_scale, from_range_1, from_range_10, - get_domain_range_scale, to_domain_1, to_domain_10, to_domain_100, - is_integer, is_numeric, tsplit, usage_warning) - -__author__ = 'Colour Developers, Paul Centore' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__copyright__ += ', ' + CACHE_REGISTRY, + CaseInsensitiveMapping, + Lookup, + as_float, + as_float_array, + as_float_scalar, + as_int_scalar, + attest, + domain_range_scale, + from_range_1, + from_range_10, + get_domain_range_scale, + to_domain_1, + to_domain_10, + to_domain_100, + is_integer, + is_numeric, + tsplit, + tstack, + usage_warning, + validate_method, +) + +__author__ = "Colour Developers, Paul Centore" +__copyright__ = "Copyright 2013 Colour Developers" +__copyright__ += ", " __copyright__ += ( - 'The Munsell and Kubelka-Munk Toolbox: Copyright 2010-2018 Paul Centore ' - '(Gales Ferry, CT 06335, USA); used by permission.') -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + "The Munsell and Kubelka-Munk Toolbox: Copyright 2010-2018 Paul Centore " + "(Gales Ferry, CT 06335, USA); used by permission." +) +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" __all__ = [ - 'MUNSELL_GRAY_PATTERN', 'MUNSELL_COLOUR_PATTERN', 'MUNSELL_GRAY_FORMAT', - 'MUNSELL_COLOUR_FORMAT', 'MUNSELL_GRAY_EXTENDED_FORMAT', - 'MUNSELL_COLOUR_EXTENDED_FORMAT', 'MUNSELL_HUE_LETTER_CODES', - 'ILLUMINANT_NAME_MUNSELL', 'CCS_ILLUMINANT_MUNSELL', - 'munsell_value_Priest1920', 'munsell_value_Munsell1933', - 'munsell_value_Moon1943', 'munsell_value_Saunderson1944', - 'munsell_value_Ladd1955', 'munsell_value_McCamy1987', - 'munsell_value_ASTMD1535', 'MUNSELL_VALUE_METHODS', 'munsell_value', - 'munsell_specification_to_xyY', 'munsell_colour_to_xyY', - 'xyY_to_munsell_specification', 'xyY_to_munsell_colour', - 'parse_munsell_colour', 'is_grey_munsell_colour', - 'normalize_munsell_specification', - 'munsell_colour_to_munsell_specification', - 'munsell_specification_to_munsell_colour', 'xyY_from_renotation', - 'is_specification_in_renotation', 'bounding_hues_from_renotation', - 'hue_to_hue_angle', 'hue_angle_to_hue', 'hue_to_ASTM_hue', - 'interpolation_method_from_renotation_ovoid', 'xy_from_renotation_ovoid', - 'LCHab_to_munsell_specification', 'maximum_chroma_from_renotation', - 'munsell_specification_to_xy' + "MUNSELL_GRAY_PATTERN", + "MUNSELL_COLOUR_PATTERN", + "MUNSELL_GRAY_FORMAT", + "MUNSELL_COLOUR_FORMAT", + "MUNSELL_GRAY_EXTENDED_FORMAT", + "MUNSELL_COLOUR_EXTENDED_FORMAT", + "MUNSELL_HUE_LETTER_CODES", + "ILLUMINANT_NAME_MUNSELL", + "CCS_ILLUMINANT_MUNSELL", + "munsell_value_Priest1920", + "munsell_value_Munsell1933", + "munsell_value_Moon1943", + "munsell_value_Saunderson1944", + "munsell_value_Ladd1955", + "munsell_value_McCamy1987", + "munsell_value_ASTMD1535", + "MUNSELL_VALUE_METHODS", + "munsell_value", + "munsell_specification_to_xyY", + "munsell_colour_to_xyY", + "xyY_to_munsell_specification", + "xyY_to_munsell_colour", + "parse_munsell_colour", + "is_grey_munsell_colour", + "normalise_munsell_specification", + "munsell_colour_to_munsell_specification", + "munsell_specification_to_munsell_colour", + "xyY_from_renotation", + "is_specification_in_renotation", + "bounding_hues_from_renotation", + "hue_to_hue_angle", + "hue_angle_to_hue", + "hue_to_ASTM_hue", + "interpolation_method_from_renotation_ovoid", + "xy_from_renotation_ovoid", + "LCHab_to_munsell_specification", + "maximum_chroma_from_renotation", + "munsell_specification_to_xy", ] -MUNSELL_GRAY_PATTERN = 'N(?P{0})'.format(FLOATING_POINT_NUMBER_PATTERN) -MUNSELL_COLOUR_PATTERN = ('(?P{0})\\s*' - '(?PBG|GY|YR|RP|PB|B|G|Y|R|P)\\s*' - '(?P{0})\\s*\\/\\s*(?P[-+]?{0})'. - format(FLOATING_POINT_NUMBER_PATTERN)) - -MUNSELL_GRAY_FORMAT = 'N{0}' -MUNSELL_COLOUR_FORMAT = '{0} {1}/{2}' -MUNSELL_GRAY_EXTENDED_FORMAT = 'N{0:.{1}f}' -MUNSELL_COLOUR_EXTENDED_FORMAT = '{0:.{1}f}{2} {3:.{4}f}/{5:.{6}f}' - -MUNSELL_HUE_LETTER_CODES = Lookup({ - 'BG': 2, - 'GY': 4, - 'YR': 6, - 'RP': 8, - 'PB': 10, - 'B': 1, - 'G': 3, - 'Y': 5, - 'R': 7, - 'P': 9 -}) - -ILLUMINANT_NAME_MUNSELL = 'C' -CCS_ILLUMINANT_MUNSELL = (CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer'][ILLUMINANT_NAME_MUNSELL]) - -_MUNSELL_SPECIFICATIONS_CACHE = None -_MUNSELL_VALUE_ASTM_D1535_08_INTERPOLATOR_CACHE = None -_MUNSELL_MAXIMUM_CHROMAS_FROM_RENOTATION_CACHE = None - - -def _munsell_specifications(): - """ - Returns the *Munsell Renotation System* specifications and caches them if +MUNSELL_GRAY_PATTERN: str = f"N(?P{FLOATING_POINT_NUMBER_PATTERN})" +MUNSELL_COLOUR_PATTERN: str = ( + f"(?P{FLOATING_POINT_NUMBER_PATTERN})\\s*" + f"(?PBG|GY|YR|RP|PB|B|G|Y|R|P)\\s*" + f"(?P{FLOATING_POINT_NUMBER_PATTERN})\\s*\\/\\s*" + f"(?P[-+]?{FLOATING_POINT_NUMBER_PATTERN})" +) + +MUNSELL_GRAY_FORMAT: str = "N{0}" +MUNSELL_COLOUR_FORMAT: str = "{0} {1}/{2}" +MUNSELL_GRAY_EXTENDED_FORMAT: str = "N{0:.{1}f}" +MUNSELL_COLOUR_EXTENDED_FORMAT: str = "{0:.{1}f}{2} {3:.{4}f}/{5:.{6}f}" + +MUNSELL_HUE_LETTER_CODES: Lookup = Lookup( + { + "BG": 2, + "GY": 4, + "YR": 6, + "RP": 8, + "PB": 10, + "B": 1, + "G": 3, + "Y": 5, + "R": 7, + "P": 9, + } +) + +ILLUMINANT_NAME_MUNSELL: str = "C" +CCS_ILLUMINANT_MUNSELL: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +][ILLUMINANT_NAME_MUNSELL] + +_MUNSELL_SPECIFICATIONS_CACHE: Dict = CACHE_REGISTRY.register_cache( + f"{__name__}._MUNSELL_SPECIFICATIONS_CACHE" +) +_MUNSELL_VALUE_ASTM_D1535_08_INTERPOLATOR_CACHE: Dict = ( + CACHE_REGISTRY.register_cache( + f"{__name__}._MUNSELL_VALUE_ASTM_D1535_08_INTERPOLATOR_CACHE" + ) +) +_MUNSELL_MAXIMUM_CHROMAS_FROM_RENOTATION_CACHE: Dict = ( + CACHE_REGISTRY.register_cache( + f"{__name__}._MUNSELL_MAXIMUM_CHROMAS_FROM_RENOTATION_CACHE" + ) +) + + +def _munsell_specifications() -> NDArray: + """ + Return the *Munsell Renotation System* specifications and caches them if not existing. The *Munsell Renotation System* data is stored in - :attr:`colour.notation.MUNSELL_COLOURS` attribute in a 2 columns form: + :attr:`colour.notation.MUNSELL_COLOURS` attribute in a 2 columns form:: - (('2.5GY', 0.2, 2.0), (0.713, 1.414, 0.237)), - (('5GY', 0.2, 2.0), (0.449, 1.145, 0.237)), - (('7.5GY', 0.2, 2.0), (0.262, 0.837, 0.237)), - ...,) + ( + (('2.5GY', 0.2, 2.0), (0.713, 1.414, 0.237)), + (('5GY', 0.2, 2.0), (0.449, 1.145, 0.237)), + ..., + (('7.5GY', 0.2, 2.0), (0.262, 0.837, 0.237)), + ) The first column is converted from *Munsell* colour to specification using :func:`colour.notation.munsell.munsell_colour_to_munsell_specification` definition: - ('2.5GY', 0.2, 2.0) ---> (2.5, 0.2, 2.0, 4) + ('2.5GY', 0.2, 2.0) --> (2.5, 0.2, 2.0, 4) Returns ------- - ndarray + :class:`numpy.ndarray` *Munsell Renotation System* specifications. """ global _MUNSELL_SPECIFICATIONS_CACHE - if _MUNSELL_SPECIFICATIONS_CACHE is None: - _MUNSELL_SPECIFICATIONS_CACHE = np.array([ + if "All" in _MUNSELL_SPECIFICATIONS_CACHE: + return _MUNSELL_SPECIFICATIONS_CACHE["All"] + + munsell_specifications = np.array( + [ munsell_colour_to_munsell_specification( - MUNSELL_COLOUR_FORMAT.format(*colour[0])) + MUNSELL_COLOUR_FORMAT.format(*colour[0]) + ) for colour in MUNSELL_COLOURS_ALL - ]) - return _MUNSELL_SPECIFICATIONS_CACHE + ] + ) + + _MUNSELL_SPECIFICATIONS_CACHE["All"] = munsell_specifications + + return munsell_specifications -def _munsell_value_ASTMD1535_interpolator(): +def _munsell_value_ASTMD1535_interpolator() -> Extrapolator: """ - Returns the *Munsell* value interpolator for *ASTM D1535-08e1* method and + Return the *Munsell* value interpolator for *ASTM D1535-08e1* method and caches it if not existing. Returns ------- - Extrapolator + :class:`colour.Extrapolator` *Munsell* value interpolator for *ASTM D1535-08e1* method. """ global _MUNSELL_VALUE_ASTM_D1535_08_INTERPOLATOR_CACHE + if "ASTM D1535-08 Interpolator" in ( + _MUNSELL_VALUE_ASTM_D1535_08_INTERPOLATOR_CACHE + ): + return _MUNSELL_VALUE_ASTM_D1535_08_INTERPOLATOR_CACHE[ + "ASTM D1535-08 Interpolator" + ] + munsell_values = np.arange(0, 10, 0.001) - if _MUNSELL_VALUE_ASTM_D1535_08_INTERPOLATOR_CACHE is None: - _MUNSELL_VALUE_ASTM_D1535_08_INTERPOLATOR_CACHE = Extrapolator( - LinearInterpolator( - luminance_ASTMD1535(munsell_values), munsell_values)) + interpolator = LinearInterpolator( + luminance_ASTMD1535(munsell_values), munsell_values + ) + extrapolator = Extrapolator(interpolator) - return _MUNSELL_VALUE_ASTM_D1535_08_INTERPOLATOR_CACHE + _MUNSELL_VALUE_ASTM_D1535_08_INTERPOLATOR_CACHE[ + "ASTM D1535-08 Interpolator" + ] = extrapolator + return extrapolator -def _munsell_maximum_chromas_from_renotation(): + +def _munsell_maximum_chromas_from_renotation() -> Tuple[ + Tuple[Tuple[Floating, Floating, Floating], Floating], ... +]: """ - Returns the maximum *Munsell* chromas from *Munsell Renotation System* data + Return the maximum *Munsell* chromas from *Munsell Renotation System* data and caches them if not existing. Returns ------- - tuple + :class:`tuple` Maximum *Munsell* chromas. """ global _MUNSELL_MAXIMUM_CHROMAS_FROM_RENOTATION_CACHE - if _MUNSELL_MAXIMUM_CHROMAS_FROM_RENOTATION_CACHE is None: - chromas = OrderedDict() - for munsell_colour in MUNSELL_COLOURS_ALL: - hue, value, chroma, code = munsell_colour_to_munsell_specification( - MUNSELL_COLOUR_FORMAT.format(*munsell_colour[0])) - index = (hue, value, code) - if index in chromas: - chroma = max(chromas[index], chroma) + if "Maximum Chromas From Renotation" in ( + _MUNSELL_MAXIMUM_CHROMAS_FROM_RENOTATION_CACHE + ): + return _MUNSELL_MAXIMUM_CHROMAS_FROM_RENOTATION_CACHE[ + "Maximum Chromas From Renotation" + ] + + chromas: Dict[Tuple[Floating, Floating, Floating], Floating] = {} + for munsell_colour in MUNSELL_COLOURS_ALL: + hue, value, chroma, code = tsplit( + munsell_colour_to_munsell_specification( + MUNSELL_COLOUR_FORMAT.format(*munsell_colour[0]) + ) + ) + index = (hue, value, code) + if index in chromas: + chroma = max([chromas[index], chroma]) + + chromas[index] = chroma + + maximum_chromas_from_renotation = tuple( + zip(chromas.keys(), chromas.values()) + ) - chromas[index] = chroma + _MUNSELL_MAXIMUM_CHROMAS_FROM_RENOTATION_CACHE[ + "Maximum Chromas From Renotation" + ] = maximum_chromas_from_renotation - _MUNSELL_MAXIMUM_CHROMAS_FROM_RENOTATION_CACHE = tuple( - zip(chromas.keys(), chromas.values())) - return _MUNSELL_MAXIMUM_CHROMAS_FROM_RENOTATION_CACHE + return maximum_chromas_from_renotation -def munsell_value_Priest1920(Y): +def munsell_value_Priest1920(Y: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the *Munsell* value :math:`V` of given *luminance* :math:`Y` using + Return the *Munsell* value :math:`V` of given *luminance* :math:`Y` using *Priest et al. (1920)* method. Parameters ---------- - Y : numeric or array_like + Y *luminance* :math:`Y`. Returns ------- - numeric or ndarray + :class:`np.floating` or :class:`numpy.ndarray` *Munsell* value :math:`V`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -332,27 +443,26 @@ def munsell_value_Priest1920(Y): V = 10 * np.sqrt(Y / 100) - return from_range_10(V) + return as_float(from_range_10(V)) -def munsell_value_Munsell1933(Y): +def munsell_value_Munsell1933(Y: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the *Munsell* value :math:`V` of given *luminance* :math:`Y` using + Return the *Munsell* value :math:`V` of given *luminance* :math:`Y` using *Munsell et al. (1933)* method. Parameters ---------- - Y : numeric or array_like + Y *luminance* :math:`Y`. Returns ------- - numeric or ndarray + :class:`np.floating` or :class:`numpy.ndarray` *Munsell* value :math:`V`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -379,28 +489,27 @@ def munsell_value_Munsell1933(Y): V = np.sqrt(1.4742 * Y - 0.004743 * (Y * Y)) - return from_range_10(V) + return as_float(from_range_10(V)) -def munsell_value_Moon1943(Y): +def munsell_value_Moon1943(Y: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the *Munsell* value :math:`V` of given *luminance* :math:`Y` using + Return the *Munsell* value :math:`V` of given *luminance* :math:`Y` using *Moon and Spencer (1943)* method. Parameters ---------- - Y : numeric or array_like + Y *luminance* :math:`Y`. Returns ------- - numeric or ndarray + :class:`np.floating` or :class:`numpy.ndarray` *Munsell* value :math:`V`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -427,27 +536,26 @@ def munsell_value_Moon1943(Y): V = 1.4 * spow(Y, 0.426) - return from_range_10(V) + return as_float(from_range_10(V)) -def munsell_value_Saunderson1944(Y): +def munsell_value_Saunderson1944(Y: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the *Munsell* value :math:`V` of given *luminance* :math:`Y` using + Return the *Munsell* value :math:`V` of given *luminance* :math:`Y` using *Saunderson and Milner (1944)* method. Parameters ---------- - Y : numeric + Y *luminance* :math:`Y`. Returns ------- - numeric + :class:`np.floating` or :class:`numpy.ndarray` *Munsell* value :math:`V`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -474,27 +582,26 @@ def munsell_value_Saunderson1944(Y): V = 2.357 * spow(Y, 0.343) - 1.52 - return from_range_10(V) + return as_float(from_range_10(V)) -def munsell_value_Ladd1955(Y): +def munsell_value_Ladd1955(Y: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the *Munsell* value :math:`V` of given *luminance* :math:`Y` using + Return the *Munsell* value :math:`V` of given *luminance* :math:`Y` using *Ladd and Pinney (1955)* method. Parameters ---------- - Y : numeric or array_like + Y *luminance* :math:`Y`. Returns ------- - numeric or ndarray + :class:`np.floating` or :class:`numpy.ndarray` *Munsell* value :math:`V`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -521,27 +628,26 @@ def munsell_value_Ladd1955(Y): V = 2.468 * spow(Y, 1 / 3) - 1.636 - return from_range_10(V) + return as_float(from_range_10(V)) -def munsell_value_McCamy1987(Y): +def munsell_value_McCamy1987(Y: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the *Munsell* value :math:`V` of given *luminance* :math:`Y` using + Return the *Munsell* value :math:`V` of given *luminance* :math:`Y` using *McCamy (1987)* method. Parameters ---------- - Y : numeric or array_like + Y *luminance* :math:`Y`. Returns ------- - numeric or ndarray + :class:`np.floating` or :class:`numpy.ndarray` *Munsell* value :math:`V`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -561,7 +667,7 @@ def munsell_value_McCamy1987(Y): Examples -------- >>> munsell_value_McCamy1987(12.23634268) # doctest: +ELLIPSIS - array(4.0814348...) + 4.0814348... """ Y = to_domain_100(Y) @@ -569,34 +675,35 @@ def munsell_value_McCamy1987(Y): V = np.where( Y <= 0.9, 0.87445 * spow(Y, 0.9967), - 2.49268 * spow(Y, 1 / 3) - 1.5614 - (0.985 / (( - (0.1073 * Y - 3.084) ** 2) + 7.54)) + (0.0133 / spow(Y, 2.3)) + - 0.0084 * np.sin(4.1 * spow(Y, 1 / 3) + 1) + - (0.0221 / Y) * np.sin(0.39 * (Y - 2)) - - (0.0037 / (0.44 * Y)) * np.sin(1.28 * (Y - 0.53)), + 2.49268 * spow(Y, 1 / 3) + - 1.5614 + - (0.985 / (((0.1073 * Y - 3.084) ** 2) + 7.54)) + + (0.0133 / spow(Y, 2.3)) + + 0.0084 * np.sin(4.1 * spow(Y, 1 / 3) + 1) + + (0.0221 / Y) * np.sin(0.39 * (Y - 2)) + - (0.0037 / (0.44 * Y)) * np.sin(1.28 * (Y - 0.53)), ) - return from_range_10(V) + return as_float(from_range_10(V)) -def munsell_value_ASTMD1535(Y): +def munsell_value_ASTMD1535(Y: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the *Munsell* value :math:`V` of given *luminance* :math:`Y` using + Return the *Munsell* value :math:`V` of given *luminance* :math:`Y` using an inverse lookup table from *ASTM D1535-08e1* method. Parameters ---------- - Y : numeric or array_like + Y *luminance* :math:`Y` Returns ------- - numeric or ndarray + :class:`np.floating` or :class:`numpy.ndarray` *Munsell* value :math:`V`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -626,18 +733,20 @@ def munsell_value_ASTMD1535(Y): V = _munsell_value_ASTMD1535_interpolator()(Y) - return from_range_10(V) + return as_float(from_range_10(V)) -MUNSELL_VALUE_METHODS = CaseInsensitiveMapping({ - 'Priest 1920': munsell_value_Priest1920, - 'Munsell 1933': munsell_value_Munsell1933, - 'Moon 1943': munsell_value_Moon1943, - 'Saunderson 1944': munsell_value_Saunderson1944, - 'Ladd 1955': munsell_value_Ladd1955, - 'McCamy 1987': munsell_value_McCamy1987, - 'ASTM D1535': munsell_value_ASTMD1535 -}) +MUNSELL_VALUE_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Priest 1920": munsell_value_Priest1920, + "Munsell 1933": munsell_value_Munsell1933, + "Moon 1943": munsell_value_Moon1943, + "Saunderson 1944": munsell_value_Saunderson1944, + "Ladd 1955": munsell_value_Ladd1955, + "McCamy 1987": munsell_value_McCamy1987, + "ASTM D1535": munsell_value_ASTMD1535, + } +) MUNSELL_VALUE_METHODS.__doc__ = """ Supported *Munsell* value computation methods. @@ -645,39 +754,46 @@ def munsell_value_ASTMD1535(Y): ---------- :cite:`ASTMInternational1989a`, :cite:`Wikipedia2007c` -MUNSELL_VALUE_METHODS : CaseInsensitiveMapping - **{'Priest 1920', 'Munsell 1933', 'Moon 1943', 'Saunderson 1944', - 'Ladd 1955', 'McCamy 1987', 'ASTM D1535'}** - Aliases: - 'astm2008': 'ASTM D1535' """ -MUNSELL_VALUE_METHODS['astm2008'] = (MUNSELL_VALUE_METHODS['ASTM D1535']) - - -def munsell_value(Y, method='ASTM D1535'): - """ - Returns the *Munsell* value :math:`V` of given *luminance* :math:`Y` using +MUNSELL_VALUE_METHODS["astm2008"] = MUNSELL_VALUE_METHODS["ASTM D1535"] + + +def munsell_value( + Y: FloatingOrArrayLike, + method: Union[ + Literal[ + "ASTM D1535", + "Ladd 1955", + "McCamy 1987", + "Moon 1943", + "Munsell 1933", + "Priest 1920", + "Saunderson 1944", + ], + str, + ] = "ASTM D1535", +) -> FloatingOrNDArray: + """ + Return the *Munsell* value :math:`V` of given *luminance* :math:`Y` using given method. Parameters ---------- - Y : numeric or array_like + Y *luminance* :math:`Y`. - method : unicode, optional - **{'ASTM D1535', 'Priest 1920', 'Munsell 1933', 'Moon 1943', - 'Saunderson 1944', 'Ladd 1955', 'McCamy 1987'}**, + method Computation method. Returns ------- - numeric or ndarray + :class:`np.floating` or :class:`numpy.ndarray` *Munsell* value :math:`V`. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -710,60 +826,65 @@ def munsell_value(Y, method='ASTM D1535'): >>> munsell_value(12.23634268, method='Ladd 1955') # doctest: +ELLIPSIS 4.0511633... >>> munsell_value(12.23634268, method='McCamy 1987') # doctest: +ELLIPSIS - array(4.0814348...) + 4.0814348... """ - return MUNSELL_VALUE_METHODS.get(method)(Y) + method = validate_method(method, MUNSELL_VALUE_METHODS) + return MUNSELL_VALUE_METHODS[method](Y) -def _domain_range_scale_factor(): + +def _munsell_scale_factor() -> NDArray: """ - Returns the domain-range scale factor for *Munsell Renotation System*. + Return the domain-range scale factor for *Munsell Renotation System*. Returns ------- - ndarray + :class:`numpy.ndarray` Domain-range scale factor for *Munsell Renotation System*. """ - return np.array([10, 10, 50 if get_domain_range_scale() == '1' else 2, 10]) + return np.array([10, 10, 50 if get_domain_range_scale() == "1" else 2, 10]) -def _munsell_specification_to_xyY(specification): +def _munsell_specification_to_xyY(specification: ArrayLike) -> NDArray: """ - Converts given *Munsell* *Colorlab* specification to *CIE xyY* colourspace. - + Convert given *Munsell* *Colorlab* specification to *CIE xyY* colourspace. Parameters ---------- - specification : array_like + specification *Munsell* *Colorlab* specification. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE xyY* colourspace array. """ - specification = normalize_munsell_specification(specification) + specification = normalise_munsell_specification(specification) if is_grey_munsell_colour(specification): specification = as_float_array(to_domain_10(specification)) hue, value, chroma, code = specification else: - specification = to_domain_10(specification, - _domain_range_scale_factor()) - hue, value, chroma, code = ([as_float(i) for i in specification[0:3]] + - [DEFAULT_INT_DTYPE(specification[-1])]) - - assert 0 <= hue <= 10, ( - '"{0}" specification hue must be normalised to domain ' - '[0, 10]!'.format(specification)) - assert 0 <= value <= 10, ( - '"{0}" specification value must be normalised to domain ' - '[0, 10]!'.format(specification)) - - with domain_range_scale('ignore'): + specification = to_domain_10(specification, _munsell_scale_factor()) + hue, value, chroma, code = specification + code = as_int_scalar(code) + + attest( + 0 <= hue <= 10, + f'"{specification}" specification hue must be normalised to ' + f"domain [0, 10]!", + ) + + attest( + 0 <= value <= 10, + f'"{specification}" specification value must be normalised to ' + f"domain [0, 10]!", + ) + + with domain_range_scale("ignore"): Y = luminance_ASTMD1535(value) if is_integer(value): @@ -772,49 +893,54 @@ def _munsell_specification_to_xyY(specification): value_minus = np.floor(value) value_plus = value_minus + 1 - specification_minus = (value_minus if is_grey_munsell_colour(specification) - else (hue, value_minus, chroma, code)) - x_minus, y_minus = munsell_specification_to_xy(specification_minus) + specification_minus = as_float_array( + value_minus + if is_grey_munsell_colour(specification) + else [hue, value_minus, chroma, code] + ) + x_minus, y_minus = tsplit(munsell_specification_to_xy(specification_minus)) - plus_specification = (value_plus - if (is_grey_munsell_colour(specification) or - value_plus == 10) else (hue, value_plus, chroma, - code)) - x_plus, y_plus = munsell_specification_to_xy(plus_specification) + specification_plus = as_float_array( + value_plus + if (is_grey_munsell_colour(specification) or value_plus == 10) + else [hue, value_plus, chroma, code] + ) + x_plus, y_plus = tsplit(munsell_specification_to_xy(specification_plus)) if value_minus == value_plus: x = x_minus y = y_minus else: - with domain_range_scale('ignore'): - Y_minus = luminance_ASTMD1535(value_minus) - Y_plus = luminance_ASTMD1535(value_plus) + with domain_range_scale("ignore"): + Y_minus = as_float_array(luminance_ASTMD1535(value_minus)) + Y_plus = as_float_array(luminance_ASTMD1535(value_plus)) + + Y_minus_plus = np.squeeze([Y_minus, Y_plus]) + x_minus_plus = np.squeeze([x_minus, x_plus]) + y_minus_plus = np.squeeze([y_minus, y_plus]) - x = LinearInterpolator( - np.ravel([Y_minus, Y_plus]), np.ravel([x_minus, x_plus]))(Y) - y = LinearInterpolator( - np.ravel([Y_minus, Y_plus]), np.ravel([y_minus, y_plus]))(Y) + x = LinearInterpolator(Y_minus_plus, x_minus_plus)(Y) + y = LinearInterpolator(Y_minus_plus, y_minus_plus)(Y) - return np.array([x, y, from_range_1(Y / 100)]) + return tstack([x, y, from_range_1(Y / 100)]) -def munsell_specification_to_xyY(specification): +def munsell_specification_to_xyY(specification: ArrayLike) -> NDArray: """ - Converts given *Munsell* *Colorlab* specification to *CIE xyY* colourspace. + Convert given *Munsell* *Colorlab* specification to *CIE xyY* colourspace. Parameters ---------- - specification : array_like + specification *Munsell* *Colorlab* specification. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE xyY* colourspace array. Notes ----- - +-------------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +===================+=======================+===============+ @@ -857,26 +983,25 @@ def munsell_specification_to_xyY(specification): shape[-1] = 3 - return as_float_array(xyY).reshape(shape) + return np.reshape(as_float_array(xyY), shape) -def munsell_colour_to_xyY(munsell_colour): +def munsell_colour_to_xyY(munsell_colour: StrOrArrayLike) -> NDArray: """ - Converts given *Munsell* colour to *CIE xyY* colourspace. + Convert given *Munsell* colour to *CIE xyY* colourspace. Parameters ---------- - munsell_colour : unicode or array_like + munsell_colour *Munsell* colour. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE xyY* colourspace array. Notes ----- - +-----------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +===========+=======================+===============+ @@ -898,28 +1023,32 @@ def munsell_colour_to_xyY(munsell_colour): munsell_colour = np.array(munsell_colour) shape = list(munsell_colour.shape) - specification = np.array([ - munsell_colour_to_munsell_specification(a) - for a in np.ravel(munsell_colour) - ]) + specification = np.array( + [ + munsell_colour_to_munsell_specification(a) + for a in np.ravel(munsell_colour) + ] + ) return munsell_specification_to_xyY( from_range_10( - specification.reshape(shape + [4]), _domain_range_scale_factor())) + specification.reshape(shape + [4]), _munsell_scale_factor() + ) + ) -def _xyY_to_munsell_specification(xyY): +def _xyY_to_munsell_specification(xyY: ArrayLike) -> NDArray: """ - Converts from *CIE xyY* colourspace to *Munsell* *Colorlab* specification. + Convert from *CIE xyY* colourspace to *Munsell* *Colorlab* specification. Parameters ---------- - xyY : array_like + xyY *CIE xyY* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *Munsell* *Colorlab* specification. Raises @@ -936,25 +1065,30 @@ def _xyY_to_munsell_specification(xyY): Y = to_domain_1(Y) if not is_within_macadam_limits(xyY, ILLUMINANT_NAME_MUNSELL): - usage_warning('"{0}" is not within "MacAdam" limits for illuminant ' - '"{1}"!'.format(xyY, ILLUMINANT_NAME_MUNSELL)) + usage_warning( + f'"{xyY!r}" is not within "MacAdam" limits for illuminant ' + f'"{ILLUMINANT_NAME_MUNSELL}"!' + ) - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): value = munsell_value_ASTMD1535(Y * 100) if is_integer(value): - value = round(value) + value = np.around(value) - with domain_range_scale('ignore'): - x_center, y_center, Y_center = _munsell_specification_to_xyY(value) + with domain_range_scale("ignore"): + x_center, y_center, Y_center = tsplit( + _munsell_specification_to_xyY(value) + ) - rho_input, phi_input, _z_input = cartesian_to_cylindrical( - (x - x_center, y - y_center, Y_center)) + rho_input, phi_input, _z_input = tsplit( + cartesian_to_cylindrical([x - x_center, y - y_center, Y_center]) + ) phi_input = np.degrees(phi_input) grey_threshold = 1e-7 if rho_input < grey_threshold: - return from_range_10(normalize_munsell_specification(value)) + return from_range_10(normalise_munsell_specification(value)) X, Y, Z = xyY_to_XYZ([x, y, Y]) xi, yi = CCS_ILLUMINANT_MUNSELL @@ -965,10 +1099,14 @@ def _xyY_to_munsell_specification(xyY): Lab = XYZ_to_Lab(XYZ, XYZ_to_xy(XYZr)) LCHab = Lab_to_LCHab(Lab) - hue_initial, _value_initial, chroma_initial, code_initial = ( - LCHab_to_munsell_specification(LCHab)) + hue_initial, _value_initial, chroma_initial, code_initial = tsplit( + LCHab_to_munsell_specification(LCHab) + ) specification_current = [ - hue_initial, value, (5 / 5.5) * chroma_initial, code_initial + hue_initial, + value, + (5 / 5.5) * chroma_initial, + code_initial, ] convergence_threshold = 1e-7 @@ -978,36 +1116,48 @@ def _xyY_to_munsell_specification(xyY): while iterations <= iterations_maximum: iterations += 1 - hue_current, _value_current, chroma_current, code_current = ( - specification_current) - hue_angle_current = hue_to_hue_angle(hue_current, code_current) + ( + hue_current, + _value_current, + chroma_current, + code_current, + ) = specification_current + hue_angle_current = hue_to_hue_angle([hue_current, code_current]) chroma_maximum = maximum_chroma_from_renotation( - hue_current, value, code_current) + [hue_current, value, code_current] + ) if chroma_current > chroma_maximum: chroma_current = specification_current[2] = chroma_maximum - with domain_range_scale('ignore'): - x_current, y_current, _Y_current = _munsell_specification_to_xyY( - specification_current) + with domain_range_scale("ignore"): + x_current, y_current, _Y_current = tsplit( + _munsell_specification_to_xyY(specification_current) + ) - rho_current, phi_current, _z_current = cartesian_to_cylindrical( - (x_current - x_center, y_current - y_center, Y_center)) + rho_current, phi_current, _z_current = tsplit( + cartesian_to_cylindrical( + [x_current - x_center, y_current - y_center, Y_center] + ) + ) phi_current = np.degrees(phi_current) phi_current_difference = (360 - phi_input + phi_current) % 360 if phi_current_difference > 180: phi_current_difference -= 360 - phi_differences = [phi_current_difference] + phi_differences_data = [phi_current_difference] + hue_angles_differences_data = [0] hue_angles = [hue_angle_current] - hue_angles_differences = [0] iterations_maximum_inner = 16 iterations_inner = 0 extrapolate = False - while np.sign(min(phi_differences)) == np.sign( - max(phi_differences)) and extrapolate is False: + while ( + np.sign(np.min(phi_differences_data)) + == np.sign(np.max(phi_differences_data)) + and extrapolate is False + ): iterations_inner += 1 if iterations_inner > iterations_maximum_inner: @@ -1016,71 +1166,88 @@ def _xyY_to_munsell_specification(xyY): # path, it is kept for consistency with the reference # implementation. raise RuntimeError( # pragma: no cover - ('Maximum inner iterations count reached without ' - 'convergence!')) - - hue_angle_inner = ((hue_angle_current + iterations_inner * - (phi_input - phi_current)) % 360) + "Maximum inner iterations count reached without " + "convergence!" + ) + + hue_angle_inner = ( + hue_angle_current + + iterations_inner * (phi_input - phi_current) + ) % 360 hue_angle_difference_inner = ( - iterations_inner * (phi_input - phi_current) % 360) + iterations_inner * (phi_input - phi_current) % 360 + ) if hue_angle_difference_inner > 180: hue_angle_difference_inner -= 360 hue_inner, code_inner = hue_angle_to_hue(hue_angle_inner) - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): x_inner, y_inner, _Y_inner = _munsell_specification_to_xyY( - (hue_inner, value, chroma_current, code_inner)) + [hue_inner, value, chroma_current, code_inner] + ) - if len(phi_differences) >= 2: + if len(phi_differences_data) >= 2: extrapolate = True if extrapolate is False: rho_inner, phi_inner, _z_inner = cartesian_to_cylindrical( - (x_inner - x_center, y_inner - y_center, Y_center)) + [x_inner - x_center, y_inner - y_center, Y_center] + ) phi_inner = np.degrees(phi_inner) - phi_inner_difference = ((360 - phi_input + phi_inner) % 360) + phi_inner_difference = (360 - phi_input + phi_inner) % 360 if phi_inner_difference > 180: phi_inner_difference -= 360 - phi_differences.append(phi_inner_difference) + phi_differences_data.append(phi_inner_difference) hue_angles.append(hue_angle_inner) - hue_angles_differences.append(hue_angle_difference_inner) + hue_angles_differences_data.append(hue_angle_difference_inner) - phi_differences = np.array(phi_differences) - hue_angles_differences = np.array(hue_angles_differences) + phi_differences = np.array(phi_differences_data) + hue_angles_differences = np.array(hue_angles_differences_data) phi_differences_indexes = phi_differences.argsort() phi_differences = phi_differences[phi_differences_indexes] - hue_angles_differences = ( - hue_angles_differences[phi_differences_indexes]) + hue_angles_differences = hue_angles_differences[ + phi_differences_indexes + ] - hue_angle_difference_new = Extrapolator( - LinearInterpolator(phi_differences, - hue_angles_differences))(0) % 360 + hue_angle_difference_new = ( + Extrapolator( + LinearInterpolator(phi_differences, hue_angles_differences) + )(0) + % 360 + ) hue_angle_new = (hue_angle_current + hue_angle_difference_new) % 360 - hue_new, code_new = hue_angle_to_hue(hue_angle_new) + hue_new, code_new = hue_angle_to_hue(as_float_scalar(hue_angle_new)) specification_current = [hue_new, value, chroma_current, code_new] - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): x_current, y_current, _Y_current = _munsell_specification_to_xyY( - specification_current) + specification_current + ) - chroma_scale = 50 if get_domain_range_scale() == '1' else 2 + chroma_scale = 50 if get_domain_range_scale() == "1" else 2 - difference = euclidean_distance((x, y), (x_current, y_current)) + difference = euclidean_distance([x, y], [x_current, y_current]) if difference < convergence_threshold: return from_range_10( np.array(specification_current), - np.array([10, 10, chroma_scale, 10])) + np.array([10, 10, chroma_scale, 10]), + ) # TODO: Consider refactoring implementation. - hue_current, _value_current, chroma_current, code_current = ( - specification_current) + ( + hue_current, + _value_current, + chroma_current, + code_current, + ) = specification_current chroma_maximum = maximum_chroma_from_renotation( - hue_current, value, code_current) + [hue_current, value, code_current] + ) # NOTE: This condition is likely never "True" while producing a valid # "Munsell Specification" in practice: 100K iterations with random @@ -1089,45 +1256,58 @@ def _xyY_to_munsell_specification(xyY): if chroma_current > chroma_maximum: chroma_current = specification_current[2] = chroma_maximum - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): x_current, y_current, _Y_current = _munsell_specification_to_xyY( - specification_current) + specification_current + ) rho_current, phi_current, _z_current = cartesian_to_cylindrical( - (x_current - x_center, y_current - y_center, Y_center)) + [x_current - x_center, y_current - y_center, Y_center] + ) - rho_bounds = [rho_current] - chroma_bounds = [chroma_current] + rho_bounds_data = [rho_current] + chroma_bounds_data = [chroma_current] iterations_maximum_inner = 16 iterations_inner = 0 - while not (min(rho_bounds) < rho_input < max(rho_bounds)): + while not ( + np.min(rho_bounds_data) < rho_input < np.max(rho_bounds_data) + ): iterations_inner += 1 if iterations_inner > iterations_maximum_inner: - raise RuntimeError(('Maximum inner iterations count reached ' - 'without convergence!')) - - chroma_inner = (((rho_input / rho_current) ** iterations_inner) * - chroma_current) + raise RuntimeError( + "Maximum inner iterations count reached " + "without convergence!" + ) + + chroma_inner = ( + (rho_input / rho_current) ** iterations_inner + ) * chroma_current if chroma_inner > chroma_maximum: chroma_inner = specification_current[2] = chroma_maximum - specification_inner = (hue_current, value, chroma_inner, - code_current) + specification_inner = ( + hue_current, + value, + chroma_inner, + code_current, + ) - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): x_inner, y_inner, _Y_inner = _munsell_specification_to_xyY( - specification_inner) + specification_inner + ) rho_inner, phi_inner, _z_inner = cartesian_to_cylindrical( - (x_inner - x_center, y_inner - y_center, Y_center)) + [x_inner - x_center, y_inner - y_center, Y_center] + ) - rho_bounds.append(rho_inner) - chroma_bounds.append(chroma_inner) + rho_bounds_data.append(rho_inner) + chroma_bounds_data.append(chroma_inner) - rho_bounds = np.array(rho_bounds) - chroma_bounds = np.array(chroma_bounds) + rho_bounds = np.array(rho_bounds_data) + chroma_bounds = np.array(chroma_bounds_data) rhos_bounds_indexes = rho_bounds.argsort() @@ -1137,35 +1317,38 @@ def _xyY_to_munsell_specification(xyY): specification_current = [hue_current, value, chroma_new, code_current] - with domain_range_scale('ignore'): + with domain_range_scale("ignore"): x_current, y_current, _Y_current = _munsell_specification_to_xyY( - specification_current) + specification_current + ) - difference = euclidean_distance((x, y), (x_current, y_current)) + difference = euclidean_distance([x, y], [x_current, y_current]) if difference < convergence_threshold: return from_range_10( np.array(specification_current), - np.array([10, 10, chroma_scale, 10])) + np.array([10, 10, chroma_scale, 10]), + ) # NOTE: This exception is likely never raised in practice: 300K iterations # with random numbers never reached this code path, it is kept for # consistency with the reference # implementation raise RuntimeError( # pragma: no cover - 'Maximum outside iterations count reached without convergence!') + "Maximum outside iterations count reached without convergence!" + ) -def xyY_to_munsell_specification(xyY): +def xyY_to_munsell_specification(xyY: ArrayLike) -> NDArray: """ - Converts from *CIE xyY* colourspace to *Munsell* *Colorlab* specification. + Convert from *CIE xyY* colourspace to *Munsell* *Colorlab* specification. Parameters ---------- - xyY : array_lik + xyY *CIE xyY* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *Munsell* *Colorlab* specification. Raises @@ -1179,7 +1362,6 @@ def xyY_to_munsell_specification(xyY): Notes ----- - +-------------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +===================+=======================+===============+ @@ -1218,35 +1400,36 @@ def xyY_to_munsell_specification(xyY): shape[-1] = 4 - return as_float_array(specification).reshape(shape) + return np.reshape(as_float_array(specification), shape) -def xyY_to_munsell_colour(xyY, - hue_decimals=1, - value_decimals=1, - chroma_decimals=1): +def xyY_to_munsell_colour( + xyY: ArrayLike, + hue_decimals: Integer = 1, + value_decimals: Integer = 1, + chroma_decimals: Integer = 1, +) -> StrOrNDArray: """ - Converts from *CIE xyY* colourspace to *Munsell* colour. + Convert from *CIE xyY* colourspace to *Munsell* colour. Parameters ---------- - xyY : array_like, (3,) + xyY *CIE xyY* colourspace array. - hue_decimals : int + hue_decimals Hue formatting decimals. - value_decimals : int + value_decimals Value formatting decimals. - chroma_decimals : int + chroma_decimals Chroma formatting decimals. Returns ------- - unicode + :class:`str` or :class:`numpy.ndarray` *Munsell* colour. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -1260,39 +1443,42 @@ def xyY_to_munsell_colour(xyY, Examples -------- >>> xyY = np.array([0.38736945, 0.35751656, 0.59362000]) - >>> # Doctests skip for Python 2.x compatibility. - >>> xyY_to_munsell_colour(xyY) # doctest: +SKIP + >>> xyY_to_munsell_colour(xyY) '4.2YR 8.1/5.3' """ specification = to_domain_10( - xyY_to_munsell_specification(xyY), _domain_range_scale_factor()) + xyY_to_munsell_specification(xyY), _munsell_scale_factor() + ) shape = list(specification.shape) + decimals = (hue_decimals, value_decimals, chroma_decimals) + + munsell_colour = np.reshape( + np.array( + [ + munsell_specification_to_munsell_colour(a, *decimals) + for a in specification.reshape([-1, 4]) + ] + ), + shape[:-1], + ) - munsell_colour = [ - munsell_specification_to_munsell_colour( - a, hue_decimals, value_decimals, chroma_decimals) - for a in specification.reshape([-1, 4]) - ] - munsell_colour = np.array(munsell_colour).reshape(shape[:-1]) - munsell_colour = str(munsell_colour) if shape == [4] else munsell_colour - - return munsell_colour + return str(munsell_colour) if shape == [4] else munsell_colour -def parse_munsell_colour(munsell_colour): +def parse_munsell_colour(munsell_colour: str) -> NDArray: """ - Parses given *Munsell* colour and returns an intermediate *Munsell* + Parse given *Munsell* colour and returns an intermediate *Munsell* *Colorlab* specification. Parameters ---------- - munsell_colour : unicode + munsell_colour *Munsell* colour. Returns ------- - ndarray + :class:`numpy.ndarray` Intermediate *Munsell* *Colorlab* specification. Raises @@ -1311,40 +1497,46 @@ def parse_munsell_colour(munsell_colour): match = re.match(MUNSELL_GRAY_PATTERN, munsell_colour, flags=re.IGNORECASE) if match: - return as_float_array([ - np.nan, - DEFAULT_FLOAT_DTYPE(match.group('value')), - np.nan, - np.nan, - ]) + return tstack( + [ + np.nan, + match.group("value"), + np.nan, + np.nan, + ] + ) match = re.match( - MUNSELL_COLOUR_PATTERN, munsell_colour, flags=re.IGNORECASE) + MUNSELL_COLOUR_PATTERN, munsell_colour, flags=re.IGNORECASE + ) if match: - return as_float_array([ - DEFAULT_FLOAT_DTYPE(match.group('hue')), - DEFAULT_FLOAT_DTYPE(match.group('value')), - DEFAULT_FLOAT_DTYPE(match.group('chroma')), - MUNSELL_HUE_LETTER_CODES.get(match.group('letter').upper()), - ]) + return tstack( + [ + match.group("hue"), + match.group("value"), + match.group("chroma"), + MUNSELL_HUE_LETTER_CODES[match.group("letter").upper()], + ] + ) raise ValueError( - ('"{0}" is not a valid "Munsell Renotation System" colour ' - 'specification!').format(munsell_colour)) + f'"{munsell_colour}" is not a valid "Munsell Renotation System" ' + f"colour specification!" + ) -def is_grey_munsell_colour(specification): +def is_grey_munsell_colour(specification: ArrayLike) -> Boolean: """ - Returns if given *Munsell* *Colorlab* specification is a grey colour. + Return if given *Munsell* *Colorlab* specification is a grey colour. Parameters ---------- - specification : array_like + specification *Munsell* *Colorlab* specification. Returns ------- - bool + :class:`bool` Is specification a grey colour. Examples @@ -1359,39 +1551,37 @@ def is_grey_munsell_colour(specification): specification = specification[~np.isnan(specification)] - return is_numeric(as_numeric(specification)) + return is_numeric(as_float(specification)) -def normalize_munsell_specification(specification): +def normalise_munsell_specification(specification: ArrayLike) -> NDArray: """ - Normalises given *Munsell* *Colorlab* specification. + Normalise given *Munsell* *Colorlab* specification. Parameters ---------- - specification : array_like + specification *Munsell* *Colorlab* specification. Returns ------- - ndarray + :class:`numpy.ndarray` Normalised *Munsell* *Colorlab* specification. Examples -------- - >>> normalize_munsell_specification( + >>> normalise_munsell_specification( ... np.array([0.0, 2.0, 4.0, 6])) array([ 10., 2., 4., 7.]) - >>> normalize_munsell_specification( + >>> normalise_munsell_specification( ... np.array([np.nan, 0.5, np.nan, np.nan])) array([ nan, 0.5, nan, nan]) """ - if is_grey_munsell_colour(specification): - specification = as_float_array(specification) - - value = as_numeric(specification[~np.isnan(specification)]) + specification = as_float_array(specification) - return as_float_array([np.nan, value, np.nan, np.nan]) + if is_grey_munsell_colour(specification): + return specification * np.array([np.nan, 1, np.nan, np.nan]) else: hue, value, chroma, code = specification @@ -1400,24 +1590,24 @@ def normalize_munsell_specification(specification): hue, code = 10, (code + 1) % 10 if chroma == 0: - return as_float_array([np.nan, value, np.nan, np.nan]) + return tstack([np.nan, value, np.nan, np.nan]) else: - return as_float_array([hue, value, chroma, code]) + return tstack([hue, value, chroma, code]) -def munsell_colour_to_munsell_specification(munsell_colour): +def munsell_colour_to_munsell_specification(munsell_colour: str) -> NDArray: """ - Convenient definition to retrieve a normalised *Munsell* *Colorlab* - specification from given *Munsell* colour. + Retrieve a normalised *Munsell* *Colorlab* specification from given + *Munsell* colour. Parameters ---------- - munsell_colour : unicode + munsell_colour *Munsell* colour. Returns ------- - array_like + :class:`numpy.ndarray` Normalised *Munsell* *Colorlab* specification. Examples @@ -1428,73 +1618,81 @@ def munsell_colour_to_munsell_specification(munsell_colour): array([ 10., 2., 4., 7.]) """ - return normalize_munsell_specification( - parse_munsell_colour(munsell_colour)) + return normalise_munsell_specification( + parse_munsell_colour(munsell_colour) + ) -def munsell_specification_to_munsell_colour(specification, - hue_decimals=1, - value_decimals=1, - chroma_decimals=1): +def munsell_specification_to_munsell_colour( + specification: ArrayLike, + hue_decimals: Integer = 1, + value_decimals: Integer = 1, + chroma_decimals: Integer = 1, +) -> str: """ - Converts from *Munsell* *Colorlab* specification to given *Munsell* colour. + Convert from *Munsell* *Colorlab* specification to given *Munsell* colour. Parameters ---------- - specification : array_like + specification *Munsell* *Colorlab* specification. - hue_decimals : int, optional + hue_decimals Hue formatting decimals. - value_decimals : int, optional + value_decimals Value formatting decimals. - chroma_decimals : int, optional + chroma_decimals Chroma formatting decimals. Returns ------- - unicode + :class:`str` *Munsell* colour. Examples -------- - >>> # Doctests skip for Python 2.x compatibility. >>> munsell_specification_to_munsell_colour( - ... np.array([np.nan, 5.2, np.nan, np.nan])) # doctest: +SKIP + ... np.array([np.nan, 5.2, np.nan, np.nan])) 'N5.2' - >>> # Doctests skip for Python 2.x compatibility. >>> munsell_specification_to_munsell_colour( - ... np.array([10, 2.0, 4.0, 7])) # doctest: +SKIP + ... np.array([10, 2.0, 4.0, 7])) '10.0R 2.0/4.0' """ - if is_grey_munsell_colour(specification): - hue, value, chroma, code = normalize_munsell_specification( - specification) + hue, value, chroma, code = tsplit( + normalise_munsell_specification(specification) + ) + if is_grey_munsell_colour(specification): return MUNSELL_GRAY_EXTENDED_FORMAT.format(value, value_decimals) else: - rounding_decimals = (hue_decimals, value_decimals, chroma_decimals, 1) - hue, value, chroma, code = [ - round(component, rounding_decimals[i]) - for i, component in enumerate(specification) - ] - code_values = MUNSELL_HUE_LETTER_CODES.values() - - assert 0 <= hue <= 10, ( - '"{0}" specification hue must be normalised to domain ' - '[0, 10]!'.format(specification)) - assert 0 <= value <= 10, ( - '"{0}" specification value must be normalised to domain ' - '[0, 10]!'.format(specification)) - assert 2 <= chroma <= 50, ( - '"{0}" specification chroma must be normalised to domain ' - '[2, 50]!'.format(specification)) - assert code in code_values, ( - '"{0}" specification code must one of "{1}"!'.format( - specification, code_values)) + hue = round(hue, hue_decimals) + attest( + 0 <= hue <= 10, + f'"{specification!r}" specification hue must be normalised to ' + f"domain [0, 10]!", + ) + + value = round(value, value_decimals) + attest( + 0 <= value <= 10, + f'"{specification!r}" specification value must be normalised to ' + f"domain [0, 10]!", + ) + + chroma = round(chroma, chroma_decimals) + attest( + 2 <= chroma <= 50, + f'"{specification!r}" specification chroma must be normalised to ' + f"domain [2, 50]!", + ) - if hue == 0: - hue, code = 10, (code + 1) % 10 + code_values = MUNSELL_HUE_LETTER_CODES.values() + code = round(code, 1) + attest( + code in code_values, + f'"{specification!r}" specification code must one of ' + f'"{code_values}"!', + ) if value == 0: return MUNSELL_GRAY_EXTENDED_FORMAT.format(value, value_decimals) @@ -1502,23 +1700,29 @@ def munsell_specification_to_munsell_colour(specification, hue_letter = MUNSELL_HUE_LETTER_CODES.first_key_from_value(code) return MUNSELL_COLOUR_EXTENDED_FORMAT.format( - hue, hue_decimals, hue_letter, value, value_decimals, chroma, - chroma_decimals) + hue, + hue_decimals, + hue_letter, + value, + value_decimals, + chroma, + chroma_decimals, + ) -def xyY_from_renotation(specification): +def xyY_from_renotation(specification: ArrayLike) -> NDArray: """ - Returns given existing *Munsell* *Colorlab* specification *CIE xyY* + Return given existing *Munsell* *Colorlab* specification *CIE xyY* colourspace vector from *Munsell Renotation System* data. Parameters ---------- - specification : array_like + specification *Munsell* *Colorlab* specification. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE xyY* colourspace vector. Raises @@ -1533,33 +1737,35 @@ def xyY_from_renotation(specification): array([ 0.71..., 1.41..., 0.23...]) """ - specification = normalize_munsell_specification(specification) + specification = normalise_munsell_specification(specification) try: index = np.where( - (_munsell_specifications() == specification).all(axis=-1)) + (_munsell_specifications() == specification).all(axis=-1) + ) - return MUNSELL_COLOURS_ALL[as_int(index[0])][1] + return MUNSELL_COLOURS_ALL[int(index[0])][1] except Exception: raise ValueError( - ('"{0}" specification does not exists in ' - '"Munsell Renotation System" data!').format(specification)) + f'"{specification}" specification does not exists in ' + '"Munsell Renotation System" data!' + ) -def is_specification_in_renotation(specification): +def is_specification_in_renotation(specification: ArrayLike) -> Boolean: """ - Returns if given *Munsell* *Colorlab* specification is in + Return whether given *Munsell* *Colorlab* specification is in *Munsell Renotation System* data. Parameters ---------- - specification : array_like + specification *Munsell* *Colorlab* specification. Returns ------- - bool - Is specification in *Munsell Renotation System* data. + :class:`bool` + Whether specification is in *Munsell Renotation System* data. Examples -------- @@ -1577,21 +1783,21 @@ def is_specification_in_renotation(specification): return False -def bounding_hues_from_renotation(hue, code): +def bounding_hues_from_renotation(hue_and_code: ArrayLike) -> NDArray: """ - Returns for a given hue the two bounding hues from + Return for a given *Munsell* *Colorlab* specification hue and *Munsell* + *Colorlab* specification code the two bounding hues from *Munsell Renotation System* data. Parameters ---------- - hue : numeric - *Munsell* *Colorlab* specification hue. - code : numeric - *Munsell* *Colorlab* specification code. + hue_and_code + *Munsell* *Colorlab* specification hue and *Munsell* *Colorlab* + specification code. Returns ------- - ndarray + :class:`numpy.ndarray` Bounding hues. References @@ -1600,17 +1806,24 @@ def bounding_hues_from_renotation(hue, code): Examples -------- - >>> bounding_hues_from_renotation(3.2, 4) + >>> bounding_hues_from_renotation([3.2, 4]) array([[ 2.5, 4. ], [ 5. , 4. ]]) # Coverage Doctests - >>> bounding_hues_from_renotation(0.0, 1) + >>> bounding_hues_from_renotation([0.0, 1]) array([[ 10., 2.], [ 10., 2.]]) """ + hue, code = as_float_array(hue_and_code) + + hue_cw: Floating + code_cw: Floating + hue_ccw: Floating + code_ccw: Floating + if hue % 2.5 == 0: if hue == 0: hue_cw = 10 @@ -1638,21 +1851,20 @@ def bounding_hues_from_renotation(hue, code): return as_float_array([(hue_cw, code_cw), (hue_ccw, code_ccw)]) -def hue_to_hue_angle(hue, code): +def hue_to_hue_angle(hue_and_code: ArrayLike) -> Floating: """ - Converts from the *Munsell* *Colorlab* specification hue to hue angle in - degrees. + Convert from the *Munsell* *Colorlab* specification hue and *Munsell* + *Colorlab* specification code to hue angle in degrees. Parameters ---------- - hue : numeric - *Munsell* *Colorlab* specification hue. - code : numeric - *Munsell* *Colorlab* specification code. + hue_and_code + *Munsell* *Colorlab* specification hue and *Munsell* *Colorlab* + specification code. Returns ------- - numeric + :class:`numpy.floating` Hue angle in degrees. References @@ -1661,30 +1873,34 @@ def hue_to_hue_angle(hue, code): Examples -------- - >>> hue_to_hue_angle(3.2, 4) + >>> hue_to_hue_angle([3.2, 4]) 65.5 """ + hue, code = as_float_array(hue_and_code) + single_hue = ((17 - code) % 10 + (hue / 10) - 0.5) % 10 - return LinearInterpolator( - (0, 2, 3, 4, 5, 6, 8, 9, 10), - (0, 45, 70, 135, 160, 225, 255, 315, 360))(single_hue) + hue_angle = LinearInterpolator( + [0, 2, 3, 4, 5, 6, 8, 9, 10], [0, 45, 70, 135, 160, 225, 255, 315, 360] + )(single_hue) + return as_float_scalar(hue_angle) -def hue_angle_to_hue(hue_angle): + +def hue_angle_to_hue(hue_angle: Floating) -> NDArray: """ - Converts from hue angle in degrees to the *Munsell* *Colorlab* - specification hue. + Convert from hue angle in degrees to the *Munsell* *Colorlab* + specification hue and code. Parameters ---------- - hue_angle : numeric + hue_angle Hue angle in degrees. Returns ------- - ndarray + :class:`numpy.ndarray` (*Munsell* *Colorlab* specification hue, *Munsell* *Colorlab* specification code). @@ -1698,8 +1914,9 @@ def hue_angle_to_hue(hue_angle): array([ 3.216, 4. ]) """ - single_hue = LinearInterpolator((0, 45, 70, 135, 160, 225, 255, 315, 360), - (0, 2, 3, 4, 5, 6, 8, 9, 10))(hue_angle) + single_hue = LinearInterpolator( + [0, 45, 70, 135, 160, 225, 255, 315, 360], [0, 2, 3, 4, 5, 6, 8, 9, 10] + )(hue_angle) if single_hue <= 0.5: code = 7 @@ -1728,24 +1945,24 @@ def hue_angle_to_hue(hue_angle): if hue == 0: hue = 10 - return as_float_array([hue, code]) + return tstack([hue, code]) -def hue_to_ASTM_hue(hue, code): +def hue_to_ASTM_hue(hue_and_code) -> Floating: """ - Converts from the *Munsell* *Colorlab* specification hue to *ASTM* hue - number. + Convert from the *Munsell* *Colorlab* specification hue and *Munsell* + *Colorlab* specification codeto *ASTM* hue number. Parameters ---------- - hue : numeric - *Munsell* *Colorlab* specification hue. - code : numeric - *Munsell* *Colorlab* specification code. + hue_and_code + *Munsell* *Colorlab* specification hue and *Munsell* *Colorlab* + specification code. + Returns ------- - numeric + :class:`numpy.floating` *ASTM* hue number. References @@ -1754,29 +1971,33 @@ def hue_to_ASTM_hue(hue, code): Examples -------- - >>> hue_to_ASTM_hue(3.2, 4) # doctest: +ELLIPSIS + >>> hue_to_ASTM_hue([3.2, 4]) # doctest: +ELLIPSIS 33.2... """ + hue, code = as_float_array(hue_and_code) + ASTM_hue = 10 * ((7 - code) % 10) + hue return 100 if ASTM_hue == 0 else ASTM_hue -def interpolation_method_from_renotation_ovoid(specification): +def interpolation_method_from_renotation_ovoid( + specification: ArrayLike, +) -> Optional[Literal["Linear", "Radial"]]: """ - Returns whether to use linear or radial interpolation when drawing ovoids + Return whether to use linear or radial interpolation when drawing ovoids through data points in the *Munsell Renotation System* data from given specification. Parameters ---------- - specification : array_like + specification *Munsell* *Colorlab* specification. Returns ------- - unicode or None ('Linear', 'Radial', None) + :py:data:`None` or :class:`str` Interpolation method. References @@ -1785,54 +2006,61 @@ def interpolation_method_from_renotation_ovoid(specification): Examples -------- - >>> # Doctests skip for Python 2.x compatibility. - >>> interpolation_method_from_renotation_ovoid((2.5, 5.0, 12.0, 4)) - ... # doctest: +SKIP + >>> interpolation_method_from_renotation_ovoid([2.5, 5.0, 12.0, 4]) 'Radial' """ - specification = normalize_munsell_specification(specification) + specification = normalise_munsell_specification(specification) + + interpolation_methods: Dict[ + Integer, Optional[Literal["Linear", "Radial"]] + ] = { + 0: None, + 1: "Linear", + 2: "Radial", + } - interpolation_methods = {0: None, 1: 'Linear', 2: 'Radial'} - interpolation_method = 0 if is_grey_munsell_colour(specification): # No interpolation needed for grey colours. interpolation_method = 0 else: hue, value, chroma, code = specification - assert 0 <= value <= 10, ( - '"{0}" specification value must be normalised to domain ' - '[0, 10]!'.format(specification)) - assert is_integer(value), ( - '"{0}" specification value must be an integer!'.format( - specification)) + attest( + 0 <= value <= 10, + f'"{specification}" specification value must be normalised to ' + f"domain [0, 10]!", + ) + + attest( + is_integer(value), + f'"{specification}" specification value must be an integer!', + ) value = round(value) - # Ideal white, no interpolation needed. - if value == 10: - interpolation_method = 0 + attest( + 2 <= chroma <= 50, + f'"{specification}" specification chroma must be normalised to ' + f"domain [2, 50]!", + ) - assert 2 <= chroma <= 50, ( - '"{0}" specification chroma must be normalised to domain ' - '[2, 50]!'.format(specification)) - assert abs( - 2 * (chroma / 2 - round(chroma / 2))) <= INTEGER_THRESHOLD, (( - '"{0}" specification chroma must be an integer and ' - 'multiple of 2!').format(specification)) + attest( + abs(2 * (chroma / 2 - round(chroma / 2))) <= INTEGER_THRESHOLD, + f'"{specification}" specification chroma must be an integer and ' + f"multiple of 2!", + ) chroma = 2 * round(chroma / 2) + interpolation_method = 0 + # Standard Munsell Renotation System hue, no interpolation needed. if hue % 2.5 == 0: interpolation_method = 0 - ASTM_hue = hue_to_ASTM_hue(hue, code) + ASTM_hue = hue_to_ASTM_hue([hue, code]) - # NOTE: The first level "else" clauses are likely never reached in - # practice, they are kept for consistency with the reference - # implementation. if value == 1: if chroma == 2: if 15 < ASTM_hue < 30 or 60 < ASTM_hue < 85: @@ -1992,20 +2220,29 @@ def interpolation_method_from_renotation_ovoid(specification): else: interpolation_method = 1 elif chroma == 10: - if (30 < ASTM_hue < 42.5 or 5 < ASTM_hue < 25 or - 60 < ASTM_hue < 82.5): + if ( + 30 < ASTM_hue < 42.5 + or 5 < ASTM_hue < 25 + or 60 < ASTM_hue < 82.5 + ): interpolation_method = 2 else: interpolation_method = 1 elif chroma == 12: - if (30 < ASTM_hue < 42.5 or 7.5 < ASTM_hue < 27.5 or - 80 < ASTM_hue < 82.5): + if ( + 30 < ASTM_hue < 42.5 + or 7.5 < ASTM_hue < 27.5 + or 80 < ASTM_hue < 82.5 + ): interpolation_method = 2 else: interpolation_method = 1 elif chroma >= 14: - if (32.5 < ASTM_hue < 40 or 7.5 < ASTM_hue < 15 or - 80 < ASTM_hue < 82.5): + if ( + 32.5 < ASTM_hue < 40 + or 7.5 < ASTM_hue < 15 + or 80 < ASTM_hue < 82.5 + ): interpolation_method = 2 else: interpolation_method = 1 @@ -2018,8 +2255,11 @@ def interpolation_method_from_renotation_ovoid(specification): else: interpolation_method = 1 elif chroma >= 14: - if (32.5 < ASTM_hue < 40 or 5 < ASTM_hue < 15 or - 60 < ASTM_hue < 85): + if ( + 32.5 < ASTM_hue < 40 + or 5 < ASTM_hue < 15 + or 60 < ASTM_hue < 85 + ): interpolation_method = 2 else: interpolation_method = 1 @@ -2043,13 +2283,16 @@ def interpolation_method_from_renotation_ovoid(specification): interpolation_method = 1 else: # pragma: no cover interpolation_method = 1 + elif value == 10: + # Ideal white, no interpolation needed. + interpolation_method = 0 - return interpolation_methods.get(interpolation_method) + return interpolation_methods[interpolation_method] -def xy_from_renotation_ovoid(specification): +def xy_from_renotation_ovoid(specification: ArrayLike) -> NDArray: """ - Converts given *Munsell* *Colorlab* specification to *CIE xy* chromaticity + Convert given *Munsell* *Colorlab* specification to *CIE xy* chromaticity coordinates on *Munsell Renotation System* ovoid. The *CIE xy* point will be on the ovoid about the achromatic point, corresponding to the *Munsell* *Colorlab* specification @@ -2057,12 +2300,12 @@ def xy_from_renotation_ovoid(specification): Parameters ---------- - specification : array_like + specification *Munsell* *Colorlab* specification. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE xy* chromaticity coordinates. Raises @@ -2077,53 +2320,65 @@ def xy_from_renotation_ovoid(specification): Examples -------- - >>> xy_from_renotation_ovoid((2.5, 5.0, 12.0, 4)) + >>> xy_from_renotation_ovoid([2.5, 5.0, 12.0, 4]) ... # doctest: +ELLIPSIS array([ 0.4333..., 0.5602...]) - >>> xy_from_renotation_ovoid((np.nan, 8, np.nan, np.nan)) + >>> xy_from_renotation_ovoid([np.nan, 8, np.nan, np.nan]) ... # doctest: +ELLIPSIS array([ 0.31006..., 0.31616...]) """ - specification = normalize_munsell_specification(specification) + specification = normalise_munsell_specification(specification) if is_grey_munsell_colour(specification): return CCS_ILLUMINANT_MUNSELL else: hue, value, chroma, code = specification - assert 1 <= value <= 9, ( - '"{0}" specification value must be normalised to domain ' - '[1, 9]!'.format(specification)) - assert is_integer(value), ( - '"{0}" specification value must be an integer!'.format( - specification)) + attest( + 1 <= value <= 9, + f'"{specification}" specification value must be normalised to ' + f"domain [1, 9]!", + ) + + attest( + is_integer(value), + f'"{specification}" specification value must be an integer!', + ) value = round(value) - assert 2 <= chroma <= 50, ( - '"{0}" specification chroma must be normalised to domain ' - '[2, 50]!'.format(specification)) - assert abs( - 2 * (chroma / 2 - round(chroma / 2))) <= INTEGER_THRESHOLD, (( - '"{0}" specification chroma must be an integer and ' - 'multiple of 2!').format(specification)) + attest( + 2 <= chroma <= 50, + f'"{specification}" specification chroma must be normalised to ' + f"domain [2, 50]!", + ) + + attest( + abs(2 * (chroma / 2 - round(chroma / 2))) <= INTEGER_THRESHOLD, + f'"{specification}" specification chroma must be an integer and ' + f"multiple of 2!", + ) chroma = 2 * round(chroma / 2) # Checking if renotation data is available without interpolation using # given threshold. threshold = 1e-7 - if (abs(hue) < threshold or abs(hue - 2.5) < threshold or - abs(hue - 5) < threshold or abs(hue - 7.5) < threshold or - abs(hue - 10) < threshold): + if ( + abs(hue) < threshold + or abs(hue - 2.5) < threshold + or abs(hue - 5) < threshold + or abs(hue - 7.5) < threshold + or abs(hue - 10) < threshold + ): hue = 2.5 * round(hue / 2.5) - x, y, _Y = xyY_from_renotation((hue, value, chroma, code)) + x, y, _Y = xyY_from_renotation([hue, value, chroma, code]) - return as_float_array([x, y]) + return tstack([x, y]) - hue_cw, hue_ccw = bounding_hues_from_renotation(hue, code) + hue_cw, hue_ccw = bounding_hues_from_renotation([hue, code]) hue_minus, code_minus = hue_cw hue_plus, code_plus = hue_ccw @@ -2132,74 +2387,94 @@ def xy_from_renotation_ovoid(specification): specification_minus = (hue_minus, value, chroma, code_minus) x_minus, y_minus, Y_minus = xyY_from_renotation(specification_minus) rho_minus, phi_minus, _z_minus = cartesian_to_cylindrical( - (x_minus - x_grey, y_minus - y_grey, Y_minus)) + [x_minus - x_grey, y_minus - y_grey, Y_minus] + ) phi_minus = np.degrees(phi_minus) specification_plus = (hue_plus, value, chroma, code_plus) x_plus, y_plus, Y_plus = xyY_from_renotation(specification_plus) rho_plus, phi_plus, _z_plus = cartesian_to_cylindrical( - (x_plus - x_grey, y_plus - y_grey, Y_plus)) + [x_plus - x_grey, y_plus - y_grey, Y_plus] + ) phi_plus = np.degrees(phi_plus) - lower_hue_angle = hue_to_hue_angle(hue_minus, code_minus) - hue_angle = hue_to_hue_angle(hue, code) - upper_hue_angle = hue_to_hue_angle(hue_plus, code_plus) + hue_angle_lower = hue_to_hue_angle([hue_minus, code_minus]) + hue_angle = hue_to_hue_angle([hue, code]) + hue_angle_upper = hue_to_hue_angle([hue_plus, code_plus]) if phi_minus - phi_plus > 180: phi_plus += 360 - if lower_hue_angle == 0: - lower_hue_angle = 360 + if hue_angle_lower == 0: + hue_angle_lower = 360 - if lower_hue_angle > upper_hue_angle: - if lower_hue_angle > hue_angle: - lower_hue_angle -= 360 + if hue_angle_lower > hue_angle_upper: + if hue_angle_lower > hue_angle: + hue_angle_lower -= 360 else: - lower_hue_angle -= 360 + hue_angle_lower -= 360 hue_angle -= 360 interpolation_method = interpolation_method_from_renotation_ovoid( - specification).lower() - - assert interpolation_method is not None, ( - 'Interpolation method must be one of : "{0}"'.format(', '.join( - ['Linear', 'radial']))) - - if interpolation_method == 'linear': - x = LinearInterpolator( - np.ravel([lower_hue_angle, upper_hue_angle]), - np.ravel([x_minus, x_plus]))(hue_angle) - y = LinearInterpolator( - np.ravel([lower_hue_angle, upper_hue_angle]), - np.ravel([y_minus, y_plus]))(hue_angle) - elif interpolation_method == 'radial': - theta = LinearInterpolator( - np.ravel([lower_hue_angle, upper_hue_angle]), - np.ravel([phi_minus, phi_plus]))(hue_angle) - rho = LinearInterpolator( - np.ravel([lower_hue_angle, upper_hue_angle]), - np.ravel([rho_minus, rho_plus]))(hue_angle) - + specification + ) + + attest( + interpolation_method is not None, + f"Interpolation method must be one of: " + f"\"{', '.join(['Linear', 'Radial'])}\"", + ) + + hue_angle_lower_upper = np.squeeze( + as_float_array([hue_angle_lower, hue_angle_upper]) + ) + + if interpolation_method == "Linear": + x_minus_plus = np.squeeze([x_minus, x_plus]) + y_minus_plus = np.squeeze([y_minus, y_plus]) + + x = LinearInterpolator(hue_angle_lower_upper, x_minus_plus)( + hue_angle + ) + y = LinearInterpolator(hue_angle_lower_upper, y_minus_plus)( + hue_angle + ) + elif interpolation_method == "Radial": + rho_minus_plus = np.squeeze([rho_minus, rho_plus]) + phi_minus_plus = np.squeeze([phi_minus, phi_plus]) + + rho = as_float_array( + LinearInterpolator(hue_angle_lower_upper, rho_minus_plus)( + hue_angle + ) + ) + phi = as_float_array( + LinearInterpolator(hue_angle_lower_upper, phi_minus_plus)( + hue_angle + ) + ) + + rho_phi = np.squeeze([rho, np.radians(phi)]) x, y = tsplit( - polar_to_cartesian(np.ravel([rho, np.radians(theta)])) + - as_float_array([x_grey, y_grey])) + polar_to_cartesian(rho_phi) + tstack([x_grey, y_grey]) + ) - return as_float_array([x, y]) + return tstack([x, y]) -def LCHab_to_munsell_specification(LCHab): +def LCHab_to_munsell_specification(LCHab: ArrayLike) -> NDArray: """ - Converts from *CIE L\\*C\\*Hab* colourspace to approximate *Munsell* + Convert from *CIE L\\*C\\*Hab* colourspace to approximate *Munsell* *Colorlab* specification. Parameters ---------- - LCHab : array_like, (3,) + LCHab *CIE L\\*C\\*Hab* colourspace array. Returns ------- - ndarray + :class:`numpy.ndarray` *Munsell* *Colorlab* specification. References @@ -2238,34 +2513,33 @@ def LCHab_to_munsell_specification(LCHab): else: code = 8 - hue = LinearInterpolator((0, 36), (0, 10))(Hab % 36) + hue = LinearInterpolator([0, 36], [0, 10])(Hab % 36) if hue == 0: hue = 10 value = L / 10 chroma = C / 5 - return as_float_array([hue, value, chroma, code]) + return tstack([hue, value, chroma, code]) -def maximum_chroma_from_renotation(hue, value, code): +def maximum_chroma_from_renotation( + hue_and_value_and_code: ArrayLike, +) -> Floating: """ - Returns the maximum *Munsell* chroma from *Munsell Renotation System* data + Return the maximum *Munsell* chroma from *Munsell Renotation System* data using given *Munsell* *Colorlab* specification hue, *Munsell* *Colorlab* specification value and *Munsell* *Colorlab* specification code. Parameters ---------- - hue : numeric - *Munsell* *Colorlab* specification hue. - value : numeric - *Munsell* value code. - code : numeric - *Munsell* *Colorlab* specification code. + hue_and_value_and_code + *Munsell* *Colorlab* specification hue, *Munsell* *Colorlab* + specification value and *Munsell* *Colorlab* specification code. Returns ------- - numeric + :class:`numpy.floating` Maximum chroma. References @@ -2274,16 +2548,20 @@ def maximum_chroma_from_renotation(hue, value, code): Examples -------- - >>> maximum_chroma_from_renotation(2.5, 5, 5) + >>> maximum_chroma_from_renotation([2.5, 5, 5]) 14.0 """ + hue, value, code = as_float_array(hue_and_value_and_code) + # Ideal white, no chroma. if value >= 9.99: return 0 - assert 1 <= value <= 10, ( - '"{0}" value must be normalised to domain [1, 10]!'.format(value)) + attest( + 1 <= value <= 10, + f'"{value}" value must be normalised to domain [1, 10]!', + ) if value % 1 == 0: value_minus = value @@ -2292,49 +2570,62 @@ def maximum_chroma_from_renotation(hue, value, code): value_minus = np.floor(value) value_plus = value_minus + 1 - hue_cw, hue_ccw = bounding_hues_from_renotation(hue, code) + hue_cw, hue_ccw = bounding_hues_from_renotation([hue, code]) hue_cw, code_cw = hue_cw hue_ccw, code_ccw = hue_ccw maximum_chromas = _munsell_maximum_chromas_from_renotation() specification_for_indexes = [chroma[0] for chroma in maximum_chromas] - ma_limit_mcw = maximum_chromas[specification_for_indexes.index( - (hue_cw, value_minus, code_cw))][1] - ma_limit_mccw = maximum_chromas[specification_for_indexes.index( - (hue_ccw, value_minus, code_ccw))][1] + ma_limit_mcw = maximum_chromas[ + specification_for_indexes.index((hue_cw, value_minus, code_cw)) + ][1] + ma_limit_mccw = maximum_chromas[ + specification_for_indexes.index((hue_ccw, value_minus, code_ccw)) + ][1] if value_plus <= 9: - ma_limit_pcw = maximum_chromas[specification_for_indexes.index( - (hue_cw, value_plus, code_cw))][1] - ma_limit_pccw = maximum_chromas[specification_for_indexes.index( - (hue_ccw, value_plus, code_ccw))][1] - max_chroma = min(ma_limit_mcw, ma_limit_mccw, ma_limit_pcw, - ma_limit_pccw) + ma_limit_pcw = maximum_chromas[ + specification_for_indexes.index((hue_cw, value_plus, code_cw)) + ][1] + ma_limit_pccw = maximum_chromas[ + specification_for_indexes.index((hue_ccw, value_plus, code_ccw)) + ][1] + max_chroma = min( + [ma_limit_mcw, ma_limit_mccw, ma_limit_pcw, ma_limit_pccw] + ) else: - L = luminance_ASTMD1535(value) - L9 = luminance_ASTMD1535(9) - L10 = luminance_ASTMD1535(10) + L = as_float_scalar(luminance_ASTMD1535(value)) + L9 = as_float_scalar(luminance_ASTMD1535(9)) + L10 = as_float_scalar(luminance_ASTMD1535(10)) max_chroma = min( - LinearInterpolator((L9, L10), (ma_limit_mcw, 0))(L), - LinearInterpolator((L9, L10), (ma_limit_mccw, 0))(L)) + [ + as_float_scalar( + LinearInterpolator([L9, L10], [ma_limit_mcw, 0])(L) + ), + as_float_scalar( + LinearInterpolator([L9, L10], [ma_limit_mccw, 0])(L) + ), + ] + ) + return max_chroma -def munsell_specification_to_xy(specification): +def munsell_specification_to_xy(specification: ArrayLike) -> NDArray: """ - Converts given *Munsell* *Colorlab* specification to *CIE xy* chromaticity + Convert given *Munsell* *Colorlab* specification to *CIE xy* chromaticity coordinates by interpolating over *Munsell Renotation System* data. Parameters ---------- - specification : array_like + specification *Munsell* *Colorlab* specification. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE xy* chromaticity coordinates. References @@ -2343,28 +2634,31 @@ def munsell_specification_to_xy(specification): Examples -------- - >>> # Doctests ellipsis for Python 2.x compatibility. - >>> munsell_specification_to_xy((2.1, 8.0, 17.9, 4)) + >>> munsell_specification_to_xy([2.1, 8.0, 17.9, 4]) ... # doctest: +ELLIPSIS array([ 0.4400632..., 0.5522428...]) - >>> munsell_specification_to_xy((np.nan, 8, np.nan, np.nan)) + >>> munsell_specification_to_xy([np.nan, 8, np.nan, np.nan]) ... # doctest: +ELLIPSIS array([ 0.31006..., 0.31616...]) """ - specification = normalize_munsell_specification(specification) + specification = normalise_munsell_specification(specification) if is_grey_munsell_colour(specification): return CCS_ILLUMINANT_MUNSELL else: hue, value, chroma, code = specification - assert 0 <= value <= 10, ( - '"{0}" specification value must be normalised to domain ' - '[0, 10]!'.format(specification)) - assert is_integer(value), ( - '"{0}" specification value must be an integer!'.format( - specification)) + attest( + 0 <= value <= 10, + f'"{specification}" specification value must be normalised to ' + f"domain [0, 10]!", + ) + + attest( + is_integer(value), + f'"{specification}" specification value must be an integer!', + ) value = round(value) @@ -2377,21 +2671,25 @@ def munsell_specification_to_xy(specification): if chroma_minus == 0: # Smallest chroma ovoid collapses to illuminant chromaticity # coordinates. - x_minus, y_minus = (CCS_ILLUMINANT_MUNSELL) + x_minus, y_minus = CCS_ILLUMINANT_MUNSELL else: - x_minus, y_minus = xy_from_renotation_ovoid((hue, value, - chroma_minus, code)) + x_minus, y_minus = xy_from_renotation_ovoid( + [hue, value, chroma_minus, code] + ) - x_plus, y_plus = xy_from_renotation_ovoid((hue, value, chroma_plus, - code)) + x_plus, y_plus = xy_from_renotation_ovoid( + [hue, value, chroma_plus, code] + ) if chroma_minus == chroma_plus: x = x_minus y = y_minus else: - x = LinearInterpolator((chroma_minus, chroma_plus), - (x_minus, x_plus))(chroma) - y = LinearInterpolator((chroma_minus, chroma_plus), - (y_minus, y_plus))(chroma) + chroma_minus_plus = np.squeeze([chroma_minus, chroma_plus]) + x_minus_plus = np.squeeze([x_minus, x_plus]) + y_minus_plus = np.squeeze([y_minus, y_plus]) + + x = LinearInterpolator(chroma_minus_plus, x_minus_plus)(chroma) + y = LinearInterpolator(chroma_minus_plus, y_minus_plus)(chroma) - return as_float_array([x, y]) + return tstack([x, y]) diff --git a/colour/notation/tests/__init__.py b/colour/notation/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/notation/tests/__init__.py +++ b/colour/notation/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/notation/tests/test_hexadecimal.py b/colour/notation/tests/test_hexadecimal.py index 9fb5b42334..1f9f07f144 100644 --- a/colour/notation/tests/test_hexadecimal.py +++ b/colour/notation/tests/test_hexadecimal.py @@ -1,61 +1,68 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.notation.hexadecimal` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.notation.hexadecimal` module.""" import numpy as np import unittest from itertools import permutations -from colour.notation.hexadecimal import (RGB_to_HEX, HEX_to_RGB) +from colour.notation.hexadecimal import ( + RGB_to_HEX, + HEX_to_RGB, +) from colour.utilities import domain_range_scale, ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestRGB_to_HEX', 'TestHEX_to_RGB'] +__all__ = [ + "TestRGB_to_HEX", + "TestHEX_to_RGB", +] class TestRGB_to_HEX(unittest.TestCase): """ - Defines :func:`colour.notation.hexadecimal.RGB_to_HEX` definition unit + Define :func:`colour.notation.hexadecimal.RGB_to_HEX` definition unit tests methods. """ def test_RGB_to_HEX(self): - """ - Tests :func:`colour.notation.hexadecimal.RGB_to_HEX` definition. - """ + """Test :func:`colour.notation.hexadecimal.RGB_to_HEX` definition.""" self.assertEqual( RGB_to_HEX(np.array([0.45620519, 0.03081071, 0.04091952])), - '#74070a') + "#74070a", + ) self.assertEqual( RGB_to_HEX(np.array([0.00000000, 0.00000000, 0.00000000])), - '#000000') + "#000000", + ) self.assertEqual( RGB_to_HEX(np.array([1.00000000, 1.00000000, 1.00000000])), - '#ffffff') + "#ffffff", + ) np.testing.assert_equal( RGB_to_HEX( - np.array([ - [10.00000000, 1.00000000, 1.00000000], - [1.00000000, 1.00000000, 1.00000000], - [0.00000000, 1.00000000, 0.00000000], - ])), ['#fe0e0e', '#0e0e0e', '#000e00']) + np.array( + [ + [10.00000000, 1.00000000, 1.00000000], + [1.00000000, 1.00000000, 1.00000000], + [0.00000000, 1.00000000, 0.00000000], + ] + ) + ), + ["#fe0e0e", "#0e0e0e", "#000e00"], + ) def test_n_dimensional_RGB_to_HEX(self): """ - Tests :func:`colour.notation.hexadecimal.RGB_to_HEX` definition + Test :func:`colour.notation.hexadecimal.RGB_to_HEX` definition n-dimensional arrays support. """ @@ -72,14 +79,14 @@ def test_n_dimensional_RGB_to_HEX(self): def test_domain_range_scale_RGB_to_HEX(self): """ - Tests :func:`colour.notation.hexadecimal.RGB_to_HEX` definition domain + Test :func:`colour.notation.hexadecimal.RGB_to_HEX` definition domain and range scale support. """ RGB = np.array([0.45620519, 0.03081071, 0.04091952]) HEX = RGB_to_HEX(RGB) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): self.assertEqual(RGB_to_HEX(RGB * factor), HEX) @@ -87,7 +94,7 @@ def test_domain_range_scale_RGB_to_HEX(self): @ignore_numpy_errors def test_nan_RGB_to_HEX(self): """ - Tests :func:`colour.notation.hexadecimal.RGB_to_HEX` definition + Test :func:`colour.notation.hexadecimal.RGB_to_HEX` definition nan support. """ @@ -100,37 +107,38 @@ def test_nan_RGB_to_HEX(self): class TestHEX_to_RGB(unittest.TestCase): """ - Defines :func:`colour.notation.hexadecimal.HEX_to_RGB` definition unit + Define :func:`colour.notation.hexadecimal.HEX_to_RGB` definition unit tests methods. """ def test_HEX_to_RGB(self): - """ - Tests :func:`colour.notation.hexadecimal.HEX_to_RGB` definition. - """ + """Test :func:`colour.notation.hexadecimal.HEX_to_RGB` definition.""" np.testing.assert_almost_equal( - HEX_to_RGB('#74070a'), + HEX_to_RGB("#74070a"), np.array([0.45620519, 0.03081071, 0.04091952]), - decimal=2) + decimal=2, + ) np.testing.assert_almost_equal( - HEX_to_RGB('#000000'), + HEX_to_RGB("#000000"), np.array([0.00000000, 0.00000000, 0.00000000]), - decimal=2) + decimal=2, + ) np.testing.assert_almost_equal( - HEX_to_RGB('#ffffff'), + HEX_to_RGB("#ffffff"), np.array([1.00000000, 1.00000000, 1.00000000]), - decimal=2) + decimal=2, + ) def test_n_dimensional_HEX_to_RGB(self): """ - Tests :func:`colour.notation.hexadecimal.HEX_to_RGB` definition + Test :func:`colour.notation.hexadecimal.HEX_to_RGB` definition n-dimensional arrays support. """ - HEX = '#74070a' + HEX = "#74070a" RGB = HEX_to_RGB(HEX) HEX = np.tile(HEX, 6) @@ -143,19 +151,20 @@ def test_n_dimensional_HEX_to_RGB(self): def test_domain_range_scale_HEX_to_RGB(self): """ - Tests :func:`colour.notation.hexadecimal.HEX_to_RGB` definition domain + Test :func:`colour.notation.hexadecimal.HEX_to_RGB` definition domain and range scale support. """ - HEX = '#74070a' + HEX = "#74070a" RGB = HEX_to_RGB(HEX) - d_r = (('reference', 1), (1, 1), (100, 100)) + d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( - HEX_to_RGB(HEX), RGB * factor, decimal=2) + HEX_to_RGB(HEX), RGB * factor, decimal=2 + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/notation/tests/test_munsell.py b/colour/notation/tests/test_munsell.py index 040d6beaaf..bbe75cf6cd 100644 --- a/colour/notation/tests/test_munsell.py +++ b/colour/notation/tests/test_munsell.py @@ -1,77 +1,115 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.notation.munsell` module. -""" +"""Defines the unit tests for the :mod:`colour.notation.munsell` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import unittest from itertools import permutations -from colour.notation.munsell import (CCS_ILLUMINANT_MUNSELL) -from colour.notation.munsell import (parse_munsell_colour, - is_grey_munsell_colour, - normalize_munsell_specification) -from colour.notation.munsell import (munsell_colour_to_munsell_specification, - munsell_specification_to_munsell_colour) -from colour.notation.munsell import (xyY_from_renotation, - is_specification_in_renotation) +from colour.hints import List, NDArray, Tuple +from colour.notation.munsell import ( + CCS_ILLUMINANT_MUNSELL, +) +from colour.notation.munsell import ( + parse_munsell_colour, + is_grey_munsell_colour, + normalise_munsell_specification, +) +from colour.notation.munsell import ( + munsell_colour_to_munsell_specification, + munsell_specification_to_munsell_colour, +) +from colour.notation.munsell import ( + xyY_from_renotation, + is_specification_in_renotation, +) from colour.notation.munsell import bounding_hues_from_renotation from colour.notation.munsell import hue_to_hue_angle, hue_angle_to_hue from colour.notation.munsell import hue_to_ASTM_hue from colour.notation.munsell import ( - interpolation_method_from_renotation_ovoid, xy_from_renotation_ovoid) + interpolation_method_from_renotation_ovoid, + xy_from_renotation_ovoid, +) from colour.notation.munsell import LCHab_to_munsell_specification from colour.notation.munsell import maximum_chroma_from_renotation from colour.notation.munsell import munsell_specification_to_xy -from colour.notation.munsell import (munsell_colour_to_xyY, - xyY_to_munsell_colour) -from colour.notation.munsell import (munsell_specification_to_xyY, - xyY_to_munsell_specification) +from colour.notation.munsell import ( + munsell_colour_to_xyY, + xyY_to_munsell_colour, +) +from colour.notation.munsell import ( + munsell_specification_to_xyY, + xyY_to_munsell_specification, +) from colour.notation import ( - munsell_value_Priest1920, munsell_value_Munsell1933, - munsell_value_Moon1943, munsell_value_Saunderson1944, - munsell_value_Ladd1955, munsell_value_McCamy1987, munsell_value_ASTMD1535) -from colour.utilities import (as_float_array, domain_range_scale, - ignore_numpy_errors, tstack) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + munsell_value_Priest1920, + munsell_value_Munsell1933, + munsell_value_Moon1943, + munsell_value_Saunderson1944, + munsell_value_Ladd1955, + munsell_value_McCamy1987, + munsell_value_ASTMD1535, +) +from colour.utilities import ( + as_float_array, + domain_range_scale, + ignore_numpy_errors, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'MUNSELL_SPECIFICATIONS', 'MUNSELL_GREYS_SPECIFICATIONS', - 'MUNSELL_EVEN_SPECIFICATIONS', 'MUNSELL_BOUNDING_HUES', - 'MUNSELL_HUE_TO_ANGLE', 'MUNSELL_HUE_TO_ASTM_HUE', - 'MUNSELL_INTERPOLATION_METHODS', 'MUNSELL_XY_FROM_RENOTATION_OVOID', - 'TestMunsellValuePriest1920', 'TestMunsellValueMunsell1933', - 'TestMunsellValueMoon1943', 'TestMunsellValueSaunderson1944', - 'TestMunsellValueLadd1955', 'TestMunsellValueMcCamy1992', - 'TestMunsellValueASTMD1535', 'TestMunsellSpecification_to_xyY', - 'TestMunsellColour_to_xyY', 'TestxyY_to_munsell_specification', - 'TestxyY_to_munsell_colour', 'TestParseMunsellColour', - 'TestIsGreyMunsellColour', 'TestNormalizeMunsellSpecification', - 'TestMunsellColourToMunsellSpecification', - 'TestMunsellSpecificationToMunsellColour', 'Test_xyY_fromRenotation', - 'TestIsSpecificationInRenotation', 'TestBoundingHuesFromRenotation', - 'TestHueToHueAngle', 'TestHueAngleToHue', 'TestHueTo_ASTM_hue', - 'TestInterpolationMethodFromRenotationOvoid', - 'Test_xy_fromRenotationOvoid', 'TestLCHabToMunsellSpecification', - 'TestMaximumChromaFromRenotation', 'TestMunsellSpecification_to_xy' + "MUNSELL_SPECIFICATIONS", + "MUNSELL_GREYS_SPECIFICATIONS", + "MUNSELL_EVEN_SPECIFICATIONS", + "MUNSELL_BOUNDING_HUES", + "MUNSELL_HUE_TO_ANGLE", + "MUNSELL_HUE_TO_ASTM_HUE", + "MUNSELL_INTERPOLATION_METHODS", + "MUNSELL_XY_FROM_RENOTATION_OVOID", + "TestMunsellValuePriest1920", + "TestMunsellValueMunsell1933", + "TestMunsellValueMoon1943", + "TestMunsellValueSaunderson1944", + "TestMunsellValueLadd1955", + "TestMunsellValueMcCamy1992", + "TestMunsellValueASTMD1535", + "TestMunsellSpecification_to_xyY", + "TestMunsellColour_to_xyY", + "TestxyY_to_munsell_specification", + "TestxyY_to_munsell_colour", + "TestParseMunsellColour", + "TestIsGreyMunsellColour", + "TestNormaliseMunsellSpecification", + "TestMunsellColourToMunsellSpecification", + "TestMunsellSpecificationToMunsellColour", + "Test_xyY_fromRenotation", + "TestIsSpecificationInRenotation", + "TestBoundingHuesFromRenotation", + "TestHueToHueAngle", + "TestHueAngleToHue", + "TestHueTo_ASTM_hue", + "TestInterpolationMethodFromRenotationOvoid", + "Test_xy_fromRenotationOvoid", + "TestLCHabToMunsellSpecification", + "TestMaximumChromaFromRenotation", + "TestMunsellSpecification_to_xy", ] -def _generate_unit_tests_specifications(): # pragma: no cover +def _generate_unit_tests_specifications() -> Tuple: # pragma: no cover """ - Generates the unit tests specifications. + Generate the unit tests specifications. Returns ------- - tuple + :class:`tuple` Tuples of unit tests specifications. The first tuple represents even specifications and their corresponding *CIE xyY* colourspace values. The tuple represents random specifications and their corresponding @@ -82,18 +120,20 @@ def _generate_unit_tests_specifications(): # pragma: no cover np.random.seed(16) - indexes = np.arange(len(MUNSELL_COLOURS['real'])) + indexes = np.arange(len(MUNSELL_COLOURS["real"])) np.random.shuffle(indexes) specifications, specifications_r = [], [] for i in indexes: - munsell_colour = '{0} {1}/{2}'.format(*MUNSELL_COLOURS['real'][i][0]) + munsell_colour = "{} {}/{}".format(*MUNSELL_COLOURS["real"][i][0]) try: specification = munsell_colour_to_munsell_specification( - munsell_colour) + munsell_colour + ) specification_r = specification + np.hstack( - [np.random.uniform(-1, 1, 3), [0]]) + [np.random.uniform(-1, 1, 3), [0]] + ) xyY = munsell_specification_to_xyY(specification) xyY_r = munsell_specification_to_xyY(specification_r) @@ -113,213 +153,416 @@ def _generate_unit_tests_specifications(): # pragma: no cover return specifications, specifications_r -MUNSELL_SPECIFICATIONS = np.array([ - [[7.18927191, 5.34025196, 16.05861170, 3.00000000], - [0.16623068, 0.45684550, 0.22399519]], - [[6.75749691, 9.44255422, 11.79641069, 3.00000000], - [0.27297006, 0.36631948, 0.86427673]], - [[8.44964118, 7.42750072, 3.22576700, 7.00000000], - [0.35016714, 0.32764928, 0.48285006]], - [[2.27432787, 1.59581362, 8.78434161, 2.00000000], - [0.08494989, 0.35448667, 0.02152067]], - [[9.82364034, 5.10755230, 3.23527358, 6.00000000], - [0.38135277, 0.37024591, 0.20229405]], - [[9.43158071, 6.93268985, 23.81322134, 9.00000000], - [0.33551832, 0.17578313, 0.41043153]], - [[7.48132776, 2.89467904, 8.47029950, 7.00000000], - [0.53871366, 0.32437859, 0.05953010]], - [[7.19717582, 1.79579794, 3.60295782, 2.00000000], - [0.21606607, 0.29770875, 0.02567493]], - [[7.77779715, 1.83619425, 6.42105231, 1.00000000], - [0.15191376, 0.18640506, 0.02657869]], - [[7.05646374, 8.15113870, 2.63767438, 10.00000000], - [0.29508705, 0.29769267, 0.60266436]], - [[3.03969874, 8.41779328, 5.82455544, 10.00000000], - [0.25900977, 0.27198272, 0.65133417]], - [[7.12616461, 4.61877951, 3.25823453, 9.00000000], - [0.30806367, 0.27715003, 0.16105305]], - [[9.75994064, 5.00164995, 13.80804118, 3.00000000], - [0.16988405, 0.41104446, 0.19286318]], - [[6.97677165, 8.25846010, 6.75337147, 2.00000000], - [0.24767890, 0.32314418, 0.62194498]], - [[3.30599914, 6.13663591, 6.17907561, 1.00000000], - [0.23048205, 0.28607980, 0.30872546]], - [[9.18340797, 7.81066836, 4.15020349, 7.00000000], - [0.36349464, 0.33324537, 0.54413876]], - [[8.10631072, 3.26448077, 9.90156782, 1.00000000], - [0.14334034, 0.18086971, 0.07589385]], - [[5.69603430, 5.94554581, 26.45021730, 3.00000000], - [0.10047143, 0.54485967, 0.28688700]], - [[7.52941160, 3.10193862, 2.03837398, 2.00000000], - [0.26959492, 0.31200708, 0.06836215]], - [[9.01080996, 8.59514291, 5.06552338, 2.00000000], - [0.26018550, 0.31561977, 0.68515300]], - [[9.93350418, 6.92619502, 2.76022400, 3.00000000], - [0.28894781, 0.33443098, 0.40952934]], - [[9.96785141, 1.11835600, 3.99853550, 9.00000000], - [0.31352711, 0.20634513, 0.01346611]], - [[9.99840409, 3.19309711, 5.82065904, 10.00000000], - [0.25414765, 0.20939962, 0.07251951]], - [[9.27080064, 2.36096768, 2.35873176, 9.00000000], - [0.31412335, 0.26537265, 0.04053697]], - [[9.85478063, 4.04717472, 2.09829596, 5.00000000], - [0.34970053, 0.37571825, 0.12005323]], - [[8.02558980, 6.36819633, 4.60141462, 5.00000000], - [0.37965729, 0.40977154, 0.33650122]], - [[7.19056185, 7.66646120, 10.23200084, 3.00000000], - [0.24865695, 0.39205285, 0.52051894]], - [[9.27459121, 8.93329764, 2.82639874, 3.00000000], - [0.29214864, 0.33503056, 0.75302151]], - [[6.56514645, 4.20156404, 5.64991156, 9.00000000], - [0.30195484, 0.24513718, 0.13037695]], - [[9.90928732, 9.28999046, 17.40567009, 4.00000000], - [0.30741363, 0.49906135, 0.82974391]], - [[6.66033139, 7.82125559, 2.83656023, 10.00000000], - [0.29234640, 0.29513484, 0.54589974]], - [[7.09563810, 9.27179818, 3.41469395, 6.00000000], - [0.34662788, 0.34241664, 0.82569659]], - [[8.06308980, 7.35943482, 4.42642439, 8.00000000], - [0.34290892, 0.30832480, 0.47244825]], - [[6.73937163, 4.45035611, 17.29157077, 2.00000000], - [0.09315578, 0.28811552, 0.14817605]], - [[4.46091279, 4.97623184, 8.27347386, 3.00000000], - [0.25257308, 0.42043736, 0.19064074]], - [[9.59823267, 4.43380834, 4.49782459, 3.00000000], - [0.26156547, 0.35357051, 0.14694714]], - [[5.68236594, 5.20647917, 7.98231240, 4.00000000], - [0.36926047, 0.50258483, 0.21135463]], - [[6.75749120, 9.13825880, 5.79055270, 3.00000000], - [0.28340044, 0.35597801, 0.79643638]], - [[3.45818284, 2.10857125, 4.85791418, 8.00000000], - [0.34999194, 0.24490799, 0.03328770]], - [[2.88591190, 3.36970167, 10.21423066, 7.00000000], - [0.51380217, 0.28835519, 0.08106070]], - [[1.71270774, 1.53669828, 3.39403462, 9.00000000], - [0.26997438, 0.21369276, 0.02038916]], - [[4.90655199, 3.23724997, 18.85327042, 2.00000000], - [0.05681430, 0.29884718, 0.07459423]], - [[2.60942297, 4.07393682, 4.85686834, 8.00000000], - [0.33889111, 0.27070427, 0.12180360]], - [[7.80566706, 4.07902945, 30.88921341, 9.00000000], - [0.30271734, 0.09713576, 0.12213854]], - [[9.83519352, 8.12251250, 10.73221172, 5.00000000], - [0.42441612, 0.48582983, 0.59759029]], - [[7.35317886, 7.08150026, 4.62737592, 5.00000000], - [0.37586606, 0.40232896, 0.43144075]], - [[7.77389803, 6.90019200, 8.10157277, 9.00000000], - [0.31242681, 0.25756495, 0.40592957]], - [[4.87250279, 4.19552668, 18.70870067, 9.00000000], - [0.26755998, 0.13995403, 0.12996294]], - [[7.38366474, 3.35764668, 12.11239914, 9.00000000], - [0.30031035, 0.16953394, 0.08045698]], - [[5.11434827, 6.25840324, 18.46717586, 9.00000000], - [0.27423716, 0.17360892, 0.32315057]], - [[6.50234711, 7.39248581, 8.42947132, 8.00000000], - [0.36895615, 0.29211972, 0.47748118]], - [[5.12057785, 6.74131875, 9.90906687, 9.00000000], - [0.28824634, 0.23317117, 0.38435902]], - [[7.97767039, 3.80398969, 4.31555212, 7.00000000], - [0.41347359, 0.33271080, 0.10489086]], - [[8.28257058, 4.99288145, 4.13426077, 10.00000000], - [0.27534267, 0.26445867, 0.19209471]], - [[2.27833490, 3.13652250, 12.74644014, 9.00000000], - [0.24604475, 0.14443079, 0.06991948]], - [[4.17788614, 4.56875266, 4.69980113, 8.00000000], - [0.34608761, 0.28369563, 0.15715775]], - [[5.52166738, 2.72767471, 10.73842907, 8.00000000], - [0.41659713, 0.21166956, 0.05302211]], - [[1.96964226, 5.83362103, 28.30224843, 3.00000000], - [0.11730240, 0.75674218, 0.27454364]], - [[4.22034979, 7.29875716, 8.00238058, 7.00000000], - [0.40163259, 0.32283516, 0.46329585]], - [[8.10122939, 7.84099254, 13.76355873, 4.00000000], - [0.34218201, 0.54357248, 0.54919251]], - [[6.93175327, 3.88633728, 4.19698393, 4.00000000], - [0.33428973, 0.42675447, 0.10987617]], - [[7.17972997, 7.76394588, 5.52955464, 5.00000000], - [0.38311308, 0.41154624, 0.53641155]], - [[7.44539380, 2.45826571, 18.17553552, 8.00000000], - [0.49593109, 0.16155048, 0.04361652]], - [[6.57942671, 5.70954742, 4.21109407, 7.00000000], - [0.37391946, 0.32682537, 0.26124266]], - [[7.07918723, 3.60945090, 5.18867792, 10.00000000], - [0.24565114, 0.22732867, 0.09371006]], - [[9.19704045, 7.64270856, 20.12511878, 4.00000000], - [0.31036980, 0.63853638, 0.51669329]], - [[7.38744649, 2.69345468, 6.96087944, 8.00000000], - [0.40651600, 0.25385677, 0.05175378]], - [[5.71814887, 1.35726940, 4.25987210, 6.00000000], - [0.55344553, 0.38963438, 0.01720090]], - [[7.65193815, 5.15801515, 17.63637606, 8.00000000], - [0.47344711, 0.24662331, 0.20688549]], - [[5.80035357, 8.46843486, 3.91395266, 7.00000000], - [0.35101966, 0.32394467, 0.66087042]], - [[2.64017362, 6.58022725, 25.27474395, 3.00000000], - [0.15857410, 0.65244571, 0.36322040]], - [[6.81904597, 7.52735613, 14.14454379, 5.00000000], - [0.46547138, 0.50598542, 0.49837113]], - [[1.72002655, 2.00166767, 15.17756223, 3.00000000], - [0.06593801, 0.78772140, 0.03052292]], - [[7.17387426, 7.40686142, 4.67300544, 6.00000000], - [0.38508722, 0.36570863, 0.47968081]], - [[5.66354474, 8.29189799, 7.88289254, 2.00000000], - [0.24153734, 0.33167097, 0.62803620]], - [[4.96369167, 6.11709577, 2.83010340, 9.00000000], - [0.30299188, 0.28933160, 0.30644773]], - [[9.86666880, 3.53860824, 3.76283439, 4.00000000], - [0.30878925, 0.40194660, 0.08984394]], - [[2.54469570, 1.94205063, 6.51472847, 8.00000000], - [0.34838418, 0.21610764, 0.02905603]], - [[7.41841874, 7.28061720, 7.52688380, 5.00000000], - [0.41055673, 0.44673738, 0.46058155]], - [[1.75980807, 3.96245086, 15.45788733, 3.00000000], - [0.19389711, 0.63957903, 0.11461936]], - [[4.71729705, 3.34696841, 7.66988506, 6.00000000], - [0.52645405, 0.39761412, 0.07992475]], - [[2.08610570, 6.73042407, 7.05471426, 1.00000000], - [0.22763195, 0.29145327, 0.38290628]], - [[2.27268990, 3.26447649, 7.69468499, 6.00000000], - [0.53002779, 0.37341819, 0.07589364]], - [[7.23049017, 4.12309620, 15.10943786, 8.00000000], - [0.46585026, 0.23748776, 0.12506155]], - [[6.64140481, 2.17555713, 5.41523047, 7.00000000], - [0.46318547, 0.31027036, 0.03511077]], - [[7.37490620, 1.75480377, 16.97212717, 9.00000000], - [0.28825940, 0.09568892, 0.02478061]], - [[4.33290089, 2.10328054, 2.19891461, 10.00000000], - [0.25933572, 0.26024656, 0.03314673]], - [[9.12491352, 3.86980063, 15.33312818, 3.00000000], - [0.12639463, 0.43809589, 0.10886288]], - [[3.39633194, 4.88689265, 23.54167471, 3.00000000], - [0.10976610, 0.64434470, 0.18295497]], - [[2.61214646, 1.14977763, 7.81503339, 2.00000000], - [0.06586815, 0.34294740, 0.01392758]], - [[3.29714039, 2.54520254, 13.61325521, 8.00000000], - [0.39022459, 0.16803475, 0.04650827]], - [[2.78196233, 4.33925419, 16.17790238, 3.00000000], - [0.17272546, 0.59343082, 0.14004876]], - [[7.51260170, 3.39633095, 9.42584908, 2.00000000], - [0.14825319, 0.28502763, 0.08240516]], - [[4.46597913, 8.02986601, 3.85801792, 1.00000000], - [0.26863443, 0.30193859, 0.58136398]], - [[2.29263540, 4.86650662, 7.08151101, 8.00000000], - [0.34377887, 0.26102802, 0.18122849]], - [[6.82015249, 5.14066765, 21.13348572, 9.00000000], - [0.29448492, 0.15338239, 0.20529997]], - [[4.31245640, 4.85614027, 11.50803662, 6.00000000], - [0.54231929, 0.40629376, 0.18035446]], - [[8.22108412, 5.80818951, 3.94579416, 8.00000000], - [0.34702328, 0.30674052, 0.27178470]], - [[8.31388115, 4.66785495, 3.69434623, 5.00000000], - [0.38078540, 0.41116296, 0.16493242]], - [[3.40490668, 6.59689139, 9.20874115, 7.00000000], - [0.41967010, 0.31821655, 0.36537324]], -]) - -MUNSELL_GREYS_SPECIFICATIONS = np.array( +MUNSELL_SPECIFICATIONS: NDArray = np.array( + [ + [ + [7.18927191, 5.34025196, 16.05861170, 3.00000000], + [0.16623068, 0.45684550, 0.22399519], + ], + [ + [6.75749691, 9.44255422, 11.79641069, 3.00000000], + [0.27297006, 0.36631948, 0.86427673], + ], + [ + [8.44964118, 7.42750072, 3.22576700, 7.00000000], + [0.35016714, 0.32764928, 0.48285006], + ], + [ + [2.27432787, 1.59581362, 8.78434161, 2.00000000], + [0.08494989, 0.35448667, 0.02152067], + ], + [ + [9.82364034, 5.10755230, 3.23527358, 6.00000000], + [0.38135277, 0.37024591, 0.20229405], + ], + [ + [9.43158071, 6.93268985, 23.81322134, 9.00000000], + [0.33551832, 0.17578313, 0.41043153], + ], + [ + [7.48132776, 2.89467904, 8.47029950, 7.00000000], + [0.53871366, 0.32437859, 0.05953010], + ], + [ + [7.19717582, 1.79579794, 3.60295782, 2.00000000], + [0.21606607, 0.29770875, 0.02567493], + ], + [ + [7.77779715, 1.83619425, 6.42105231, 1.00000000], + [0.15191376, 0.18640506, 0.02657869], + ], + [ + [7.05646374, 8.15113870, 2.63767438, 10.00000000], + [0.29508705, 0.29769267, 0.60266436], + ], + [ + [3.03969874, 8.41779328, 5.82455544, 10.00000000], + [0.25900977, 0.27198272, 0.65133417], + ], + [ + [7.12616461, 4.61877951, 3.25823453, 9.00000000], + [0.30806367, 0.27715003, 0.16105305], + ], + [ + [9.75994064, 5.00164995, 13.80804118, 3.00000000], + [0.16988405, 0.41104446, 0.19286318], + ], + [ + [6.97677165, 8.25846010, 6.75337147, 2.00000000], + [0.24767890, 0.32314418, 0.62194498], + ], + [ + [3.30599914, 6.13663591, 6.17907561, 1.00000000], + [0.23048205, 0.28607980, 0.30872546], + ], + [ + [9.18340797, 7.81066836, 4.15020349, 7.00000000], + [0.36349464, 0.33324537, 0.54413876], + ], + [ + [8.10631072, 3.26448077, 9.90156782, 1.00000000], + [0.14334034, 0.18086971, 0.07589385], + ], + [ + [5.69603430, 5.94554581, 26.45021730, 3.00000000], + [0.10047143, 0.54485967, 0.28688700], + ], + [ + [7.52941160, 3.10193862, 2.03837398, 2.00000000], + [0.26959492, 0.31200708, 0.06836215], + ], + [ + [9.01080996, 8.59514291, 5.06552338, 2.00000000], + [0.26018550, 0.31561977, 0.68515300], + ], + [ + [9.93350418, 6.92619502, 2.76022400, 3.00000000], + [0.28894781, 0.33443098, 0.40952934], + ], + [ + [9.96785141, 1.11835600, 3.99853550, 9.00000000], + [0.31352711, 0.20634513, 0.01346611], + ], + [ + [9.99840409, 3.19309711, 5.82065904, 10.00000000], + [0.25414765, 0.20939962, 0.07251951], + ], + [ + [9.27080064, 2.36096768, 2.35873176, 9.00000000], + [0.31412335, 0.26537265, 0.04053697], + ], + [ + [9.85478063, 4.04717472, 2.09829596, 5.00000000], + [0.34970053, 0.37571825, 0.12005323], + ], + [ + [8.02558980, 6.36819633, 4.60141462, 5.00000000], + [0.37965729, 0.40977154, 0.33650122], + ], + [ + [7.19056185, 7.66646120, 10.23200084, 3.00000000], + [0.24865695, 0.39205285, 0.52051894], + ], + [ + [9.27459121, 8.93329764, 2.82639874, 3.00000000], + [0.29214864, 0.33503056, 0.75302151], + ], + [ + [6.56514645, 4.20156404, 5.64991156, 9.00000000], + [0.30195484, 0.24513718, 0.13037695], + ], + [ + [9.90928732, 9.28999046, 17.40567009, 4.00000000], + [0.30741363, 0.49906135, 0.82974391], + ], + [ + [6.66033139, 7.82125559, 2.83656023, 10.00000000], + [0.29234640, 0.29513484, 0.54589974], + ], + [ + [7.09563810, 9.27179818, 3.41469395, 6.00000000], + [0.34662788, 0.34241664, 0.82569659], + ], + [ + [8.06308980, 7.35943482, 4.42642439, 8.00000000], + [0.34290892, 0.30832480, 0.47244825], + ], + [ + [6.73937163, 4.45035611, 17.29157077, 2.00000000], + [0.09315578, 0.28811552, 0.14817605], + ], + [ + [4.46091279, 4.97623184, 8.27347386, 3.00000000], + [0.25257308, 0.42043736, 0.19064074], + ], + [ + [9.59823267, 4.43380834, 4.49782459, 3.00000000], + [0.26156547, 0.35357051, 0.14694714], + ], + [ + [5.68236594, 5.20647917, 7.98231240, 4.00000000], + [0.36926047, 0.50258483, 0.21135463], + ], + [ + [6.75749120, 9.13825880, 5.79055270, 3.00000000], + [0.28340044, 0.35597801, 0.79643638], + ], + [ + [3.45818284, 2.10857125, 4.85791418, 8.00000000], + [0.34999194, 0.24490799, 0.03328770], + ], + [ + [2.88591190, 3.36970167, 10.21423066, 7.00000000], + [0.51380217, 0.28835519, 0.08106070], + ], + [ + [1.71270774, 1.53669828, 3.39403462, 9.00000000], + [0.26997438, 0.21369276, 0.02038916], + ], + [ + [4.90655199, 3.23724997, 18.85327042, 2.00000000], + [0.05681430, 0.29884718, 0.07459423], + ], + [ + [2.60942297, 4.07393682, 4.85686834, 8.00000000], + [0.33889111, 0.27070427, 0.12180360], + ], + [ + [7.80566706, 4.07902945, 30.88921341, 9.00000000], + [0.30271734, 0.09713576, 0.12213854], + ], + [ + [9.83519352, 8.12251250, 10.73221172, 5.00000000], + [0.42441612, 0.48582983, 0.59759029], + ], + [ + [7.35317886, 7.08150026, 4.62737592, 5.00000000], + [0.37586606, 0.40232896, 0.43144075], + ], + [ + [7.77389803, 6.90019200, 8.10157277, 9.00000000], + [0.31242681, 0.25756495, 0.40592957], + ], + [ + [4.87250279, 4.19552668, 18.70870067, 9.00000000], + [0.26755998, 0.13995403, 0.12996294], + ], + [ + [7.38366474, 3.35764668, 12.11239914, 9.00000000], + [0.30031035, 0.16953394, 0.08045698], + ], + [ + [5.11434827, 6.25840324, 18.46717586, 9.00000000], + [0.27423716, 0.17360892, 0.32315057], + ], + [ + [6.50234711, 7.39248581, 8.42947132, 8.00000000], + [0.36895615, 0.29211972, 0.47748118], + ], + [ + [5.12057785, 6.74131875, 9.90906687, 9.00000000], + [0.28824634, 0.23317117, 0.38435902], + ], + [ + [7.97767039, 3.80398969, 4.31555212, 7.00000000], + [0.41347359, 0.33271080, 0.10489086], + ], + [ + [8.28257058, 4.99288145, 4.13426077, 10.00000000], + [0.27534267, 0.26445867, 0.19209471], + ], + [ + [2.27833490, 3.13652250, 12.74644014, 9.00000000], + [0.24604475, 0.14443079, 0.06991948], + ], + [ + [4.17788614, 4.56875266, 4.69980113, 8.00000000], + [0.34608761, 0.28369563, 0.15715775], + ], + [ + [5.52166738, 2.72767471, 10.73842907, 8.00000000], + [0.41659713, 0.21166956, 0.05302211], + ], + [ + [1.96964226, 5.83362103, 28.30224843, 3.00000000], + [0.11730240, 0.75674218, 0.27454364], + ], + [ + [4.22034979, 7.29875716, 8.00238058, 7.00000000], + [0.40163259, 0.32283516, 0.46329585], + ], + [ + [8.10122939, 7.84099254, 13.76355873, 4.00000000], + [0.34218201, 0.54357248, 0.54919251], + ], + [ + [6.93175327, 3.88633728, 4.19698393, 4.00000000], + [0.33428973, 0.42675447, 0.10987617], + ], + [ + [7.17972997, 7.76394588, 5.52955464, 5.00000000], + [0.38311308, 0.41154624, 0.53641155], + ], + [ + [7.44539380, 2.45826571, 18.17553552, 8.00000000], + [0.49593109, 0.16155048, 0.04361652], + ], + [ + [6.57942671, 5.70954742, 4.21109407, 7.00000000], + [0.37391946, 0.32682537, 0.26124266], + ], + [ + [7.07918723, 3.60945090, 5.18867792, 10.00000000], + [0.24565114, 0.22732867, 0.09371006], + ], + [ + [9.19704045, 7.64270856, 20.12511878, 4.00000000], + [0.31036980, 0.63853638, 0.51669329], + ], + [ + [7.38744649, 2.69345468, 6.96087944, 8.00000000], + [0.40651600, 0.25385677, 0.05175378], + ], + [ + [5.71814887, 1.35726940, 4.25987210, 6.00000000], + [0.55344553, 0.38963438, 0.01720090], + ], + [ + [7.65193815, 5.15801515, 17.63637606, 8.00000000], + [0.47344711, 0.24662331, 0.20688549], + ], + [ + [5.80035357, 8.46843486, 3.91395266, 7.00000000], + [0.35101966, 0.32394467, 0.66087042], + ], + [ + [2.64017362, 6.58022725, 25.27474395, 3.00000000], + [0.15857410, 0.65244571, 0.36322040], + ], + [ + [6.81904597, 7.52735613, 14.14454379, 5.00000000], + [0.46547138, 0.50598542, 0.49837113], + ], + [ + [1.72002655, 2.00166767, 15.17756223, 3.00000000], + [0.06593801, 0.78772140, 0.03052292], + ], + [ + [7.17387426, 7.40686142, 4.67300544, 6.00000000], + [0.38508722, 0.36570863, 0.47968081], + ], + [ + [5.66354474, 8.29189799, 7.88289254, 2.00000000], + [0.24153734, 0.33167097, 0.62803620], + ], + [ + [4.96369167, 6.11709577, 2.83010340, 9.00000000], + [0.30299188, 0.28933160, 0.30644773], + ], + [ + [9.86666880, 3.53860824, 3.76283439, 4.00000000], + [0.30878925, 0.40194660, 0.08984394], + ], + [ + [2.54469570, 1.94205063, 6.51472847, 8.00000000], + [0.34838418, 0.21610764, 0.02905603], + ], + [ + [7.41841874, 7.28061720, 7.52688380, 5.00000000], + [0.41055673, 0.44673738, 0.46058155], + ], + [ + [1.75980807, 3.96245086, 15.45788733, 3.00000000], + [0.19389711, 0.63957903, 0.11461936], + ], + [ + [4.71729705, 3.34696841, 7.66988506, 6.00000000], + [0.52645405, 0.39761412, 0.07992475], + ], + [ + [2.08610570, 6.73042407, 7.05471426, 1.00000000], + [0.22763195, 0.29145327, 0.38290628], + ], + [ + [2.27268990, 3.26447649, 7.69468499, 6.00000000], + [0.53002779, 0.37341819, 0.07589364], + ], + [ + [7.23049017, 4.12309620, 15.10943786, 8.00000000], + [0.46585026, 0.23748776, 0.12506155], + ], + [ + [6.64140481, 2.17555713, 5.41523047, 7.00000000], + [0.46318547, 0.31027036, 0.03511077], + ], + [ + [7.37490620, 1.75480377, 16.97212717, 9.00000000], + [0.28825940, 0.09568892, 0.02478061], + ], + [ + [4.33290089, 2.10328054, 2.19891461, 10.00000000], + [0.25933572, 0.26024656, 0.03314673], + ], + [ + [9.12491352, 3.86980063, 15.33312818, 3.00000000], + [0.12639463, 0.43809589, 0.10886288], + ], + [ + [3.39633194, 4.88689265, 23.54167471, 3.00000000], + [0.10976610, 0.64434470, 0.18295497], + ], + [ + [2.61214646, 1.14977763, 7.81503339, 2.00000000], + [0.06586815, 0.34294740, 0.01392758], + ], + [ + [3.29714039, 2.54520254, 13.61325521, 8.00000000], + [0.39022459, 0.16803475, 0.04650827], + ], + [ + [2.78196233, 4.33925419, 16.17790238, 3.00000000], + [0.17272546, 0.59343082, 0.14004876], + ], + [ + [7.51260170, 3.39633095, 9.42584908, 2.00000000], + [0.14825319, 0.28502763, 0.08240516], + ], + [ + [4.46597913, 8.02986601, 3.85801792, 1.00000000], + [0.26863443, 0.30193859, 0.58136398], + ], + [ + [2.29263540, 4.86650662, 7.08151101, 8.00000000], + [0.34377887, 0.26102802, 0.18122849], + ], + [ + [6.82015249, 5.14066765, 21.13348572, 9.00000000], + [0.29448492, 0.15338239, 0.20529997], + ], + [ + [4.31245640, 4.85614027, 11.50803662, 6.00000000], + [0.54231929, 0.40629376, 0.18035446], + ], + [ + [8.22108412, 5.80818951, 3.94579416, 8.00000000], + [0.34702328, 0.30674052, 0.27178470], + ], + [ + [8.31388115, 4.66785495, 3.69434623, 5.00000000], + [0.38078540, 0.41116296, 0.16493242], + ], + [ + [3.40490668, 6.59689139, 9.20874115, 7.00000000], + [0.41967010, 0.31821655, 0.36537324], + ], + ] +) + +MUNSELL_GREYS_SPECIFICATIONS: NDArray = np.array( list( zip( - np.linspace(0, 10, 25)[:, np.newaxis], ( + np.linspace(0, 10, 25)[:, np.newaxis], + ( [0.31006, 0.31616, 0.00000000], [0.31006, 0.31616, 0.00473582], [0.31006, 0.31616, 0.00961944], @@ -345,324 +588,421 @@ def _generate_unit_tests_specifications(): # pragma: no cover [0.31006, 0.31616, 0.80259539], [0.31006, 0.31616, 0.89710353], [0.31006, 0.31616, 1.00000000], - )))) - -MUNSELL_EVEN_SPECIFICATIONS = np.array([ - [(7.5, 6.0, 16.0, 3), [0.18320000, 0.44140000, 0.29301153]], - [(7.5, 9.0, 12.0, 3), [0.24190000, 0.39850000, 0.76695586]], - [(7.5, 8.0, 4.0, 7), [0.35640000, 0.32790000, 0.57619628]], - [(2.5, 2.0, 8.0, 2), [0.15570000, 0.35170000, 0.03048116]], - [(10.0, 6.0, 4.0, 6), [0.38610000, 0.37670000, 0.29301153]], - [(10.0, 6.0, 24.0, 9), [0.34410000, 0.16980000, 0.29301153]], - [(7.5, 2.0, 8.0, 7), [0.54330000, 0.30270000, 0.03048116]], - [(7.5, 1.0, 4.0, 2), [0.17020000, 0.27680000, 0.01179925]], - [(7.5, 1.0, 6.0, 1), [0.13030000, 0.16390000, 0.01179925]], - [(7.5, 9.0, 2.0, 10), [0.30150000, 0.30520000, 0.76695586]], - [(2.5, 8.0, 6.0, 10), [0.25620000, 0.27090000, 0.57619628]], - [(7.5, 5.0, 4.0, 9), [0.31000000, 0.27500000, 0.19271844]], - [(10.0, 5.0, 14.0, 3), [0.16710000, 0.40890000, 0.19271844]], - [(7.5, 9.0, 6.0, 2), [0.25430000, 0.32200000, 0.76695586]], - [(2.5, 6.0, 6.0, 1), [0.23120000, 0.28990000, 0.29301153]], - [(10.0, 8.0, 4.0, 7), [0.36210000, 0.33490000, 0.57619628]], - [(7.5, 4.0, 10.0, 1), [0.16010000, 0.20280000, 0.11700751]], - [(5.0, 5.0, 26.0, 3), [0.07840000, 0.57610000, 0.19271844]], - [(7.5, 3.0, 2.0, 2), [0.26990000, 0.31200000, 0.06391178]], - [(10.0, 9.0, 6.0, 2), [0.25010000, 0.31180000, 0.76695586]], - [(10.0, 6.0, 2.0, 3), [0.29290000, 0.33030000, 0.29301153]], - [(10.0, 1.0, 4.0, 9), [0.31320000, 0.20320000, 0.01179925]], - [(10.0, 3.0, 6.0, 10), [0.25110000, 0.20310000, 0.06391178]], - [(10.0, 2.0, 2.0, 9), [0.31610000, 0.26910000, 0.03048116]], - [(10.0, 5.0, 2.0, 5), [0.34220000, 0.36480000, 0.19271844]], - [(7.5, 6.0, 4.0, 5), [0.37450000, 0.40040000, 0.29301153]], - [(7.5, 7.0, 10.0, 3), [0.24450000, 0.39140000, 0.41985394]], - [(10.0, 9.0, 2.0, 3), [0.29650000, 0.32930000, 0.76695586]], - [(7.5, 4.0, 6.0, 9), [0.30760000, 0.24160000, 0.11700751]], - [(10.0, 9.0, 18.0, 4), [0.30320000, 0.57480000, 0.76695586]], - [(7.5, 7.0, 2.0, 10), [0.29820000, 0.30030000, 0.41985394]], - [(7.5, 9.0, 4.0, 6), [0.36790000, 0.35850000, 0.76695586]], - [(7.5, 7.0, 4.0, 8), [0.33890000, 0.30790000, 0.41985394]], - [(7.5, 5.0, 18.0, 2), [0.09820000, 0.28280000, 0.19271844]], - [(5.0, 5.0, 8.0, 3), [0.25110000, 0.41070000, 0.19271844]], - [(10.0, 5.0, 4.0, 3), [0.27110000, 0.34550000, 0.19271844]], - [(5.0, 5.0, 8.0, 4), [0.38150000, 0.50930000, 0.19271844]], - [(7.5, 9.0, 6.0, 3), [0.27630000, 0.36070000, 0.76695586]], - [(2.5, 2.0, 4.0, 8), [0.33820000, 0.24960000, 0.03048116]], - [(2.5, 4.0, 10.0, 7), [0.47740000, 0.29690000, 0.11700751]], - [(2.5, 2.0, 4.0, 9), [0.27580000, 0.22080000, 0.03048116]], - [(5.0, 4.0, 18.0, 2), [0.08280000, 0.31080000, 0.11700751]], - [(2.5, 5.0, 4.0, 8), [0.32980000, 0.28690000, 0.19271844]], - [(7.5, 4.0, 30.0, 9), [0.29690000, 0.09790000, 0.11700751]], - [(10.0, 8.0, 10.0, 5), [0.41900000, 0.47910000, 0.57619628]], - [(7.5, 8.0, 4.0, 5), [0.36220000, 0.38610000, 0.57619628]], - [(7.5, 6.0, 8.0, 9), [0.30990000, 0.25020000, 0.29301153]], - [(5.0, 5.0, 18.0, 9), [0.27180000, 0.16040000, 0.19271844]], - [(7.5, 4.0, 12.0, 9), [0.30450000, 0.19050000, 0.11700751]], - [(5.0, 6.0, 18.0, 9), [0.27310000, 0.17380000, 0.29301153]], - [(7.5, 8.0, 8.0, 8), [0.36820000, 0.29830000, 0.57619628]], - [(5.0, 6.0, 10.0, 9), [0.28620000, 0.22600000, 0.29301153]], - [(7.5, 3.0, 4.0, 7), [0.42400000, 0.33020000, 0.06391178]], - [(7.5, 4.0, 4.0, 10), [0.26570000, 0.25280000, 0.11700751]], - [(2.5, 4.0, 12.0, 9), [0.25590000, 0.17300000, 0.11700751]], - [(5.0, 4.0, 4.0, 8), [0.34910000, 0.28720000, 0.11700751]], - [(5.0, 3.0, 10.0, 8), [0.40730000, 0.22350000, 0.06391178]], - [(2.5, 5.0, 28.0, 3), [0.07940000, 0.73850000, 0.19271844]], - [(5.0, 8.0, 8.0, 7), [0.40010000, 0.32630000, 0.57619628]], - [(7.5, 8.0, 14.0, 4), [0.35460000, 0.54900000, 0.57619628]], - [(7.5, 3.0, 4.0, 4), [0.32700000, 0.42880000, 0.06391178]], - [(7.5, 7.0, 6.0, 5), [0.39430000, 0.42640000, 0.41985394]], - [(7.5, 3.0, 18.0, 8), [0.51300000, 0.18930000, 0.06391178]], - [(7.5, 5.0, 4.0, 7), [0.38060000, 0.32940000, 0.19271844]], - [(7.5, 3.0, 6.0, 10), [0.23110000, 0.20100000, 0.06391178]], - [(10.0, 7.0, 20.0, 4), [0.28160000, 0.65630000, 0.41985394]], - [(7.5, 3.0, 6.0, 8), [0.39900000, 0.27080000, 0.06391178]], - [(5.0, 1.0, 4.0, 6), [0.56600000, 0.37950000, 0.01179925]], - [(7.5, 6.0, 18.0, 8), [0.45810000, 0.25490000, 0.29301153]], - [(5.0, 9.0, 4.0, 7), [0.34950000, 0.32260000, 0.76695586]], - [(2.5, 6.0, 26.0, 3), [0.13400000, 0.68710000, 0.29301153]], - [(7.5, 8.0, 14.0, 5), [0.45740000, 0.50620000, 0.57619628]], - [(2.5, 2.0, 16.0, 3), [0.03290000, 0.73580000, 0.03048116]], - [(7.5, 8.0, 4.0, 6), [0.36990000, 0.35860000, 0.57619628]], - [(5.0, 8.0, 8.0, 2), [0.24190000, 0.33520000, 0.57619628]], - [(5.0, 6.0, 2.0, 9), [0.30500000, 0.29670000, 0.29301153]], - [(10.0, 4.0, 4.0, 4), [0.31000000, 0.40180000, 0.11700751]], - [(2.5, 2.0, 6.0, 8), [0.34700000, 0.22590000, 0.03048116]], - [(7.5, 7.0, 8.0, 5), [0.41840000, 0.45680000, 0.41985394]], - [(2.5, 3.0, 16.0, 3), [0.13410000, 0.64200000, 0.06391178]], - [(5.0, 3.0, 8.0, 6), [0.54560000, 0.40400000, 0.06391178]], - [(2.5, 6.0, 8.0, 1), [0.20800000, 0.27890000, 0.29301153]], - [(2.5, 4.0, 8.0, 6), [0.50710000, 0.37770000, 0.11700751]], - [(7.5, 5.0, 16.0, 8), [0.46170000, 0.25060000, 0.19271844]], - [(7.5, 2.0, 6.0, 7), [0.48750000, 0.31230000, 0.03048116]], - [(7.5, 2.0, 16.0, 9), [0.29220000, 0.11060000, 0.03048116]], - [(5.0, 2.0, 2.0, 10), [0.26380000, 0.26240000, 0.03048116]], - [(10.0, 3.0, 16.0, 3), [0.09250000, 0.42750000, 0.06391178]], - [(2.5, 5.0, 24.0, 3), [0.11880000, 0.69180000, 0.19271844]], - [(2.5, 1.0, 8.0, 2), [0.04760000, 0.34580000, 0.01179925]], - [(2.5, 2.0, 14.0, 8), [0.37110000, 0.14490000, 0.03048116]], - [(2.5, 5.0, 16.0, 3), [0.20050000, 0.57590000, 0.19271844]], - [(7.5, 3.0, 10.0, 2), [0.13260000, 0.27840000, 0.06391178]], - [(5.0, 8.0, 4.0, 1), [0.26710000, 0.29980000, 0.57619628]], - [(2.5, 5.0, 8.0, 8), [0.34900000, 0.25700000, 0.19271844]], - [(7.5, 5.0, 22.0, 9), [0.30380000, 0.15000000, 0.19271844]], - [(5.0, 5.0, 12.0, 6), [0.54220000, 0.41410000, 0.19271844]], - [(7.5, 5.0, 4.0, 8), [0.35150000, 0.30240000, 0.19271844]], - [(7.5, 5.0, 4.0, 5), [0.38500000, 0.41200000, 0.19271844]], - [(2.5, 6.0, 10.0, 7), [0.43200000, 0.31180000, 0.29301153]], - [(8.0, 2, 14.0, 1), [0.07257382, 0.10413956, 0.03048116]], -]) - -MUNSELL_BOUNDING_HUES = np.array([ - ((5.0, 3.0), (7.5, 3.0)), - ((5.0, 3.0), (7.5, 3.0)), - ((7.5, 7.0), (10, 7.0)), - ((10, 3.0), (2.5, 2.0)), - ((7.5, 6.0), (10, 6.0)), - ((7.5, 9.0), (10, 9.0)), - ((5.0, 7.0), (7.5, 7.0)), - ((5.0, 2.0), (7.5, 2.0)), - ((7.5, 1.0), (10, 1.0)), - ((5.0, 10.0), (7.5, 10.0)), - ((2.5, 10.0), (5.0, 10.0)), - ((5.0, 9.0), (7.5, 9.0)), - ((7.5, 3.0), (10, 3.0)), - ((5.0, 2.0), (7.5, 2.0)), - ((2.5, 1.0), (5.0, 1.0)), - ((7.5, 7.0), (10, 7.0)), - ((7.5, 1.0), (10, 1.0)), - ((5.0, 3.0), (7.5, 3.0)), - ((7.5, 2.0), (10, 2.0)), - ((7.5, 2.0), (10, 2.0)), - ((7.5, 3.0), (10, 3.0)), - ((7.5, 9.0), (10, 9.0)), - ((7.5, 10.0), (10, 10.0)), - ((7.5, 9.0), (10, 9.0)), - ((7.5, 5.0), (10, 5.0)), - ((7.5, 5.0), (10, 5.0)), - ((5.0, 3.0), (7.5, 3.0)), - ((7.5, 3.0), (10, 3.0)), - ((5.0, 9.0), (7.5, 9.0)), - ((7.5, 4.0), (10, 4.0)), - ((5.0, 10.0), (7.5, 10.0)), - ((5.0, 6.0), (7.5, 6.0)), - ((7.5, 8.0), (10, 8.0)), - ((5.0, 2.0), (7.5, 2.0)), - ((2.5, 3.0), (5.0, 3.0)), - ((7.5, 3.0), (10, 3.0)), - ((5.0, 4.0), (7.5, 4.0)), - ((5.0, 3.0), (7.5, 3.0)), - ((2.5, 8.0), (5.0, 8.0)), - ((2.5, 7.0), (5.0, 7.0)), - ((10, 10), (2.5, 9.0)), - ((2.5, 2.0), (5.0, 2.0)), - ((2.5, 8.0), (5.0, 8.0)), - ((7.5, 9.0), (10, 9.0)), - ((7.5, 5.0), (10, 5.0)), - ((5.0, 5.0), (7.5, 5.0)), - ((7.5, 9.0), (10, 9.0)), - ((2.5, 9.0), (5.0, 9.0)), - ((5.0, 9.0), (7.5, 9.0)), - ((5.0, 9.0), (7.5, 9.0)), - ((5.0, 8.0), (7.5, 8.0)), - ((5.0, 9.0), (7.5, 9.0)), - ((7.5, 7.0), (10, 7.0)), - ((7.5, 10.0), (10, 10.0)), - ((10, 10), (2.5, 9.0)), - ((2.5, 8.0), (5.0, 8.0)), - ((5.0, 8.0), (7.5, 8.0)), - ((10, 4.0), (2.5, 3.0)), - ((2.5, 7.0), (5.0, 7.0)), - ((7.5, 4.0), (10, 4.0)), - ((5.0, 4.0), (7.5, 4.0)), - ((5.0, 5.0), (7.5, 5.0)), - ((5.0, 8.0), (7.5, 8.0)), - ((5.0, 7.0), (7.5, 7.0)), - ((5.0, 10.0), (7.5, 10.0)), - ((7.5, 4.0), (10, 4.0)), - ((5.0, 8.0), (7.5, 8.0)), - ((5.0, 6.0), (7.5, 6.0)), - ((7.5, 8.0), (10, 8.0)), - ((5.0, 7.0), (7.5, 7.0)), - ((2.5, 3.0), (5.0, 3.0)), - ((5.0, 5.0), (7.5, 5.0)), - ((10, 4.0), (2.5, 3.0)), - ((5.0, 6.0), (7.5, 6.0)), - ((5.0, 2.0), (7.5, 2.0)), - ((2.5, 9.0), (5.0, 9.0)), - ((7.5, 4.0), (10, 4.0)), - ((2.5, 8.0), (5.0, 8.0)), - ((5.0, 5.0), (7.5, 5.0)), - ((10, 4.0), (2.5, 3.0)), - ((2.5, 6.0), (5.0, 6.0)), - ((10, 2.0), (2.5, 1.0)), - ((10, 7.0), (2.5, 6.0)), - ((5.0, 8.0), (7.5, 8.0)), - ((5.0, 7.0), (7.5, 7.0)), - ((5.0, 9.0), (7.5, 9.0)), - ((2.5, 10.0), (5.0, 10.0)), - ((7.5, 3.0), (10, 3.0)), - ((2.5, 3.0), (5.0, 3.0)), - ((2.5, 2.0), (5.0, 2.0)), - ((2.5, 8.0), (5.0, 8.0)), - ((2.5, 3.0), (5.0, 3.0)), - ((7.5, 2.0), (10, 2.0)), - ((2.5, 1.0), (5.0, 1.0)), - ((10, 9.0), (2.5, 8.0)), - ((5.0, 9.0), (7.5, 9.0)), - ((2.5, 6.0), (5.0, 6.0)), - ((7.5, 8.0), (10, 8.0)), - ((7.5, 5.0), (10, 5.0)), - ((2.5, 7.0), (5.0, 7.0)), -]) - -MUNSELL_HUE_TO_ANGLE = np.array([ - [2.5, 1, 208.750], - [2.5, 2, 153.750], - [2.5, 3, 118.750], - [2.5, 4, 63.750], - [2.5, 5, 39.375], - [2.5, 6, 16.875], - [2.5, 7, 348.750], - [2.5, 8, 300.000], - [2.5, 9, 251.250], - [2.5, 10, 236.250], - [5.0, 1, 225.000], - [5.0, 2, 160.000], - [5.0, 3, 135.000], - [5.0, 4, 70.000], - [5.0, 5, 45.000], - [5.0, 6, 22.500], - [5.0, 7, 0.000], - [5.0, 8, 315.000], - [5.0, 9, 255.000], - [5.0, 10, 240.000], - [7.5, 1, 228.750], - [7.5, 2, 176.250], - [7.5, 3, 141.250], - [7.5, 4, 86.250], - [7.5, 5, 51.250], - [7.5, 6, 28.125], - [7.5, 7, 5.625], - [7.5, 8, 326.250], - [7.5, 9, 270.000], - [7.5, 10, 243.750], - [10.0, 1, 232.500], - [10.0, 2, 192.500], - [10.0, 3, 147.500], - [10.0, 4, 102.500], - [10.0, 5, 57.500], - [10.0, 6, 33.750], - [10.0, 7, 11.250], - [10.0, 8, 337.500], - [10.0, 9, 285.000], - [10.0, 10, 247.500], -]) - -MUNSELL_HUE_TO_ASTM_HUE = np.array([ - [2.5, 0, 72.5], - [2.5, 1, 62.5], - [2.5, 2, 52.5], - [2.5, 3, 42.5], - [2.5, 4, 32.5], - [2.5, 5, 22.5], - [2.5, 6, 12.5], - [2.5, 7, 2.5], - [2.5, 8, 92.5], - [2.5, 9, 82.5], - [2.5, 10, 72.5], - [5.0, 0, 75.0], - [5.0, 1, 65.0], - [5.0, 2, 55.0], - [5.0, 3, 45.0], - [5.0, 4, 35.0], - [5.0, 5, 25.0], - [5.0, 6, 15.0], - [5.0, 7, 5.0], - [5.0, 8, 95.0], - [5.0, 9, 85.0], - [5.0, 10, 75.0], - [7.5, 0, 77.5], - [7.5, 1, 67.5], - [7.5, 2, 57.5], - [7.5, 3, 47.5], - [7.5, 4, 37.5], - [7.5, 5, 27.5], - [7.5, 6, 17.5], - [7.5, 7, 7.5], - [7.5, 8, 97.5], - [7.5, 9, 87.5], - [7.5, 10, 77.5], - [10.0, 0, 80.0], - [10.0, 1, 70.0], - [10.0, 2, 60.0], - [10.0, 3, 50.0], - [10.0, 4, 40.0], - [10.0, 5, 30.0], - [10.0, 6, 20.0], - [10.0, 7, 10.0], - [10.0, 8, 100.0], - [10.0, 9, 90.0], - [10.0, 10, 80.0], -]) - -MUNSELL_INTERPOLATION_METHODS = [ - 'Linear', 'Linear', 'Radial', 'Linear', 'Radial', 'Linear', 'Linear', - 'Linear', 'Radial', 'Radial', 'Radial', 'Linear', 'Linear', 'Linear', - 'Radial', 'Radial', 'Radial', 'Linear', 'Linear', 'Linear', 'Linear', - 'Linear', 'Radial', 'Linear', 'Radial', 'Radial', 'Linear', 'Linear', - 'Linear', 'Radial', 'Radial', 'Radial', 'Linear', 'Radial', 'Linear', - 'Linear', 'Radial', 'Linear', 'Linear', 'Linear', 'Linear', 'Linear', - 'Linear', 'Linear', 'Radial', 'Radial', 'Linear', 'Linear', 'Linear', - 'Linear', 'Linear', 'Linear', 'Radial', 'Radial', 'Linear', 'Linear', - 'Linear', 'Linear', 'Linear', 'Radial', 'Linear', 'Radial', 'Linear', - 'Radial', 'Radial', 'Linear', 'Linear', 'Radial', 'Linear', 'Linear', - 'Linear', 'Linear', 'Linear', 'Radial', 'Linear', 'Radial', 'Radial', - 'Linear', 'Radial', 'Linear', 'Radial', 'Radial', 'Radial', 'Linear', - 'Linear', 'Linear', 'Linear', 'Linear', 'Linear', 'Linear', 'Linear', - 'Linear', 'Linear', 'Radial', 'Linear', 'Linear', 'Radial', 'Linear', - 'Radial', 'Linear', 'Radial' + ), + ) + ) +) + +MUNSELL_EVEN_SPECIFICATIONS: NDArray = np.array( + [ + [(7.5, 6.0, 16.0, 3), [0.18320000, 0.44140000, 0.29301153]], + [(7.5, 9.0, 12.0, 3), [0.24190000, 0.39850000, 0.76695586]], + [(7.5, 8.0, 4.0, 7), [0.35640000, 0.32790000, 0.57619628]], + [(2.5, 2.0, 8.0, 2), [0.15570000, 0.35170000, 0.03048116]], + [(10.0, 6.0, 4.0, 6), [0.38610000, 0.37670000, 0.29301153]], + [(10.0, 6.0, 24.0, 9), [0.34410000, 0.16980000, 0.29301153]], + [(7.5, 2.0, 8.0, 7), [0.54330000, 0.30270000, 0.03048116]], + [(7.5, 1.0, 4.0, 2), [0.17020000, 0.27680000, 0.01179925]], + [(7.5, 1.0, 6.0, 1), [0.13030000, 0.16390000, 0.01179925]], + [(7.5, 9.0, 2.0, 10), [0.30150000, 0.30520000, 0.76695586]], + [(2.5, 8.0, 6.0, 10), [0.25620000, 0.27090000, 0.57619628]], + [(7.5, 5.0, 4.0, 9), [0.31000000, 0.27500000, 0.19271844]], + [(10.0, 5.0, 14.0, 3), [0.16710000, 0.40890000, 0.19271844]], + [(7.5, 9.0, 6.0, 2), [0.25430000, 0.32200000, 0.76695586]], + [(2.5, 6.0, 6.0, 1), [0.23120000, 0.28990000, 0.29301153]], + [(10.0, 8.0, 4.0, 7), [0.36210000, 0.33490000, 0.57619628]], + [(7.5, 4.0, 10.0, 1), [0.16010000, 0.20280000, 0.11700751]], + [(5.0, 5.0, 26.0, 3), [0.07840000, 0.57610000, 0.19271844]], + [(7.5, 3.0, 2.0, 2), [0.26990000, 0.31200000, 0.06391178]], + [(10.0, 9.0, 6.0, 2), [0.25010000, 0.31180000, 0.76695586]], + [(10.0, 6.0, 2.0, 3), [0.29290000, 0.33030000, 0.29301153]], + [(10.0, 1.0, 4.0, 9), [0.31320000, 0.20320000, 0.01179925]], + [(10.0, 3.0, 6.0, 10), [0.25110000, 0.20310000, 0.06391178]], + [(10.0, 2.0, 2.0, 9), [0.31610000, 0.26910000, 0.03048116]], + [(10.0, 5.0, 2.0, 5), [0.34220000, 0.36480000, 0.19271844]], + [(7.5, 6.0, 4.0, 5), [0.37450000, 0.40040000, 0.29301153]], + [(7.5, 7.0, 10.0, 3), [0.24450000, 0.39140000, 0.41985394]], + [(10.0, 9.0, 2.0, 3), [0.29650000, 0.32930000, 0.76695586]], + [(7.5, 4.0, 6.0, 9), [0.30760000, 0.24160000, 0.11700751]], + [(10.0, 9.0, 18.0, 4), [0.30320000, 0.57480000, 0.76695586]], + [(7.5, 7.0, 2.0, 10), [0.29820000, 0.30030000, 0.41985394]], + [(7.5, 9.0, 4.0, 6), [0.36790000, 0.35850000, 0.76695586]], + [(7.5, 7.0, 4.0, 8), [0.33890000, 0.30790000, 0.41985394]], + [(7.5, 5.0, 18.0, 2), [0.09820000, 0.28280000, 0.19271844]], + [(5.0, 5.0, 8.0, 3), [0.25110000, 0.41070000, 0.19271844]], + [(10.0, 5.0, 4.0, 3), [0.27110000, 0.34550000, 0.19271844]], + [(5.0, 5.0, 8.0, 4), [0.38150000, 0.50930000, 0.19271844]], + [(7.5, 9.0, 6.0, 3), [0.27630000, 0.36070000, 0.76695586]], + [(2.5, 2.0, 4.0, 8), [0.33820000, 0.24960000, 0.03048116]], + [(2.5, 4.0, 10.0, 7), [0.47740000, 0.29690000, 0.11700751]], + [(2.5, 2.0, 4.0, 9), [0.27580000, 0.22080000, 0.03048116]], + [(5.0, 4.0, 18.0, 2), [0.08280000, 0.31080000, 0.11700751]], + [(2.5, 5.0, 4.0, 8), [0.32980000, 0.28690000, 0.19271844]], + [(7.5, 4.0, 30.0, 9), [0.29690000, 0.09790000, 0.11700751]], + [(10.0, 8.0, 10.0, 5), [0.41900000, 0.47910000, 0.57619628]], + [(7.5, 8.0, 4.0, 5), [0.36220000, 0.38610000, 0.57619628]], + [(7.5, 6.0, 8.0, 9), [0.30990000, 0.25020000, 0.29301153]], + [(5.0, 5.0, 18.0, 9), [0.27180000, 0.16040000, 0.19271844]], + [(7.5, 4.0, 12.0, 9), [0.30450000, 0.19050000, 0.11700751]], + [(5.0, 6.0, 18.0, 9), [0.27310000, 0.17380000, 0.29301153]], + [(7.5, 8.0, 8.0, 8), [0.36820000, 0.29830000, 0.57619628]], + [(5.0, 6.0, 10.0, 9), [0.28620000, 0.22600000, 0.29301153]], + [(7.5, 3.0, 4.0, 7), [0.42400000, 0.33020000, 0.06391178]], + [(7.5, 4.0, 4.0, 10), [0.26570000, 0.25280000, 0.11700751]], + [(2.5, 4.0, 12.0, 9), [0.25590000, 0.17300000, 0.11700751]], + [(5.0, 4.0, 4.0, 8), [0.34910000, 0.28720000, 0.11700751]], + [(5.0, 3.0, 10.0, 8), [0.40730000, 0.22350000, 0.06391178]], + [(2.5, 5.0, 28.0, 3), [0.07940000, 0.73850000, 0.19271844]], + [(5.0, 8.0, 8.0, 7), [0.40010000, 0.32630000, 0.57619628]], + [(7.5, 8.0, 14.0, 4), [0.35460000, 0.54900000, 0.57619628]], + [(7.5, 3.0, 4.0, 4), [0.32700000, 0.42880000, 0.06391178]], + [(7.5, 7.0, 6.0, 5), [0.39430000, 0.42640000, 0.41985394]], + [(7.5, 3.0, 18.0, 8), [0.51300000, 0.18930000, 0.06391178]], + [(7.5, 5.0, 4.0, 7), [0.38060000, 0.32940000, 0.19271844]], + [(7.5, 3.0, 6.0, 10), [0.23110000, 0.20100000, 0.06391178]], + [(10.0, 7.0, 20.0, 4), [0.28160000, 0.65630000, 0.41985394]], + [(7.5, 3.0, 6.0, 8), [0.39900000, 0.27080000, 0.06391178]], + [(5.0, 1.0, 4.0, 6), [0.56600000, 0.37950000, 0.01179925]], + [(7.5, 6.0, 18.0, 8), [0.45810000, 0.25490000, 0.29301153]], + [(5.0, 9.0, 4.0, 7), [0.34950000, 0.32260000, 0.76695586]], + [(2.5, 6.0, 26.0, 3), [0.13400000, 0.68710000, 0.29301153]], + [(7.5, 8.0, 14.0, 5), [0.45740000, 0.50620000, 0.57619628]], + [(2.5, 2.0, 16.0, 3), [0.03290000, 0.73580000, 0.03048116]], + [(7.5, 8.0, 4.0, 6), [0.36990000, 0.35860000, 0.57619628]], + [(5.0, 8.0, 8.0, 2), [0.24190000, 0.33520000, 0.57619628]], + [(5.0, 6.0, 2.0, 9), [0.30500000, 0.29670000, 0.29301153]], + [(10.0, 4.0, 4.0, 4), [0.31000000, 0.40180000, 0.11700751]], + [(2.5, 2.0, 6.0, 8), [0.34700000, 0.22590000, 0.03048116]], + [(7.5, 7.0, 8.0, 5), [0.41840000, 0.45680000, 0.41985394]], + [(2.5, 3.0, 16.0, 3), [0.13410000, 0.64200000, 0.06391178]], + [(5.0, 3.0, 8.0, 6), [0.54560000, 0.40400000, 0.06391178]], + [(2.5, 6.0, 8.0, 1), [0.20800000, 0.27890000, 0.29301153]], + [(2.5, 4.0, 8.0, 6), [0.50710000, 0.37770000, 0.11700751]], + [(7.5, 5.0, 16.0, 8), [0.46170000, 0.25060000, 0.19271844]], + [(7.5, 2.0, 6.0, 7), [0.48750000, 0.31230000, 0.03048116]], + [(7.5, 2.0, 16.0, 9), [0.29220000, 0.11060000, 0.03048116]], + [(5.0, 2.0, 2.0, 10), [0.26380000, 0.26240000, 0.03048116]], + [(10.0, 3.0, 16.0, 3), [0.09250000, 0.42750000, 0.06391178]], + [(2.5, 5.0, 24.0, 3), [0.11880000, 0.69180000, 0.19271844]], + [(2.5, 1.0, 8.0, 2), [0.04760000, 0.34580000, 0.01179925]], + [(2.5, 2.0, 14.0, 8), [0.37110000, 0.14490000, 0.03048116]], + [(2.5, 5.0, 16.0, 3), [0.20050000, 0.57590000, 0.19271844]], + [(7.5, 3.0, 10.0, 2), [0.13260000, 0.27840000, 0.06391178]], + [(5.0, 8.0, 4.0, 1), [0.26710000, 0.29980000, 0.57619628]], + [(2.5, 5.0, 8.0, 8), [0.34900000, 0.25700000, 0.19271844]], + [(7.5, 5.0, 22.0, 9), [0.30380000, 0.15000000, 0.19271844]], + [(5.0, 5.0, 12.0, 6), [0.54220000, 0.41410000, 0.19271844]], + [(7.5, 5.0, 4.0, 8), [0.35150000, 0.30240000, 0.19271844]], + [(7.5, 5.0, 4.0, 5), [0.38500000, 0.41200000, 0.19271844]], + [(2.5, 6.0, 10.0, 7), [0.43200000, 0.31180000, 0.29301153]], + [(8.0, 2, 14.0, 1), [0.07257382, 0.10413956, 0.03048116]], + ] +) + +MUNSELL_BOUNDING_HUES: NDArray = np.array( + [ + ((5.0, 3.0), (7.5, 3.0)), + ((5.0, 3.0), (7.5, 3.0)), + ((7.5, 7.0), (10, 7.0)), + ((10, 3.0), (2.5, 2.0)), + ((7.5, 6.0), (10, 6.0)), + ((7.5, 9.0), (10, 9.0)), + ((5.0, 7.0), (7.5, 7.0)), + ((5.0, 2.0), (7.5, 2.0)), + ((7.5, 1.0), (10, 1.0)), + ((5.0, 10.0), (7.5, 10.0)), + ((2.5, 10.0), (5.0, 10.0)), + ((5.0, 9.0), (7.5, 9.0)), + ((7.5, 3.0), (10, 3.0)), + ((5.0, 2.0), (7.5, 2.0)), + ((2.5, 1.0), (5.0, 1.0)), + ((7.5, 7.0), (10, 7.0)), + ((7.5, 1.0), (10, 1.0)), + ((5.0, 3.0), (7.5, 3.0)), + ((7.5, 2.0), (10, 2.0)), + ((7.5, 2.0), (10, 2.0)), + ((7.5, 3.0), (10, 3.0)), + ((7.5, 9.0), (10, 9.0)), + ((7.5, 10.0), (10, 10.0)), + ((7.5, 9.0), (10, 9.0)), + ((7.5, 5.0), (10, 5.0)), + ((7.5, 5.0), (10, 5.0)), + ((5.0, 3.0), (7.5, 3.0)), + ((7.5, 3.0), (10, 3.0)), + ((5.0, 9.0), (7.5, 9.0)), + ((7.5, 4.0), (10, 4.0)), + ((5.0, 10.0), (7.5, 10.0)), + ((5.0, 6.0), (7.5, 6.0)), + ((7.5, 8.0), (10, 8.0)), + ((5.0, 2.0), (7.5, 2.0)), + ((2.5, 3.0), (5.0, 3.0)), + ((7.5, 3.0), (10, 3.0)), + ((5.0, 4.0), (7.5, 4.0)), + ((5.0, 3.0), (7.5, 3.0)), + ((2.5, 8.0), (5.0, 8.0)), + ((2.5, 7.0), (5.0, 7.0)), + ((10, 10), (2.5, 9.0)), + ((2.5, 2.0), (5.0, 2.0)), + ((2.5, 8.0), (5.0, 8.0)), + ((7.5, 9.0), (10, 9.0)), + ((7.5, 5.0), (10, 5.0)), + ((5.0, 5.0), (7.5, 5.0)), + ((7.5, 9.0), (10, 9.0)), + ((2.5, 9.0), (5.0, 9.0)), + ((5.0, 9.0), (7.5, 9.0)), + ((5.0, 9.0), (7.5, 9.0)), + ((5.0, 8.0), (7.5, 8.0)), + ((5.0, 9.0), (7.5, 9.0)), + ((7.5, 7.0), (10, 7.0)), + ((7.5, 10.0), (10, 10.0)), + ((10, 10), (2.5, 9.0)), + ((2.5, 8.0), (5.0, 8.0)), + ((5.0, 8.0), (7.5, 8.0)), + ((10, 4.0), (2.5, 3.0)), + ((2.5, 7.0), (5.0, 7.0)), + ((7.5, 4.0), (10, 4.0)), + ((5.0, 4.0), (7.5, 4.0)), + ((5.0, 5.0), (7.5, 5.0)), + ((5.0, 8.0), (7.5, 8.0)), + ((5.0, 7.0), (7.5, 7.0)), + ((5.0, 10.0), (7.5, 10.0)), + ((7.5, 4.0), (10, 4.0)), + ((5.0, 8.0), (7.5, 8.0)), + ((5.0, 6.0), (7.5, 6.0)), + ((7.5, 8.0), (10, 8.0)), + ((5.0, 7.0), (7.5, 7.0)), + ((2.5, 3.0), (5.0, 3.0)), + ((5.0, 5.0), (7.5, 5.0)), + ((10, 4.0), (2.5, 3.0)), + ((5.0, 6.0), (7.5, 6.0)), + ((5.0, 2.0), (7.5, 2.0)), + ((2.5, 9.0), (5.0, 9.0)), + ((7.5, 4.0), (10, 4.0)), + ((2.5, 8.0), (5.0, 8.0)), + ((5.0, 5.0), (7.5, 5.0)), + ((10, 4.0), (2.5, 3.0)), + ((2.5, 6.0), (5.0, 6.0)), + ((10, 2.0), (2.5, 1.0)), + ((10, 7.0), (2.5, 6.0)), + ((5.0, 8.0), (7.5, 8.0)), + ((5.0, 7.0), (7.5, 7.0)), + ((5.0, 9.0), (7.5, 9.0)), + ((2.5, 10.0), (5.0, 10.0)), + ((7.5, 3.0), (10, 3.0)), + ((2.5, 3.0), (5.0, 3.0)), + ((2.5, 2.0), (5.0, 2.0)), + ((2.5, 8.0), (5.0, 8.0)), + ((2.5, 3.0), (5.0, 3.0)), + ((7.5, 2.0), (10, 2.0)), + ((2.5, 1.0), (5.0, 1.0)), + ((10, 9.0), (2.5, 8.0)), + ((5.0, 9.0), (7.5, 9.0)), + ((2.5, 6.0), (5.0, 6.0)), + ((7.5, 8.0), (10, 8.0)), + ((7.5, 5.0), (10, 5.0)), + ((2.5, 7.0), (5.0, 7.0)), + ] +) + +MUNSELL_HUE_TO_ANGLE: NDArray = np.array( + [ + [2.5, 1, 208.750], + [2.5, 2, 153.750], + [2.5, 3, 118.750], + [2.5, 4, 63.750], + [2.5, 5, 39.375], + [2.5, 6, 16.875], + [2.5, 7, 348.750], + [2.5, 8, 300.000], + [2.5, 9, 251.250], + [2.5, 10, 236.250], + [5.0, 1, 225.000], + [5.0, 2, 160.000], + [5.0, 3, 135.000], + [5.0, 4, 70.000], + [5.0, 5, 45.000], + [5.0, 6, 22.500], + [5.0, 7, 0.000], + [5.0, 8, 315.000], + [5.0, 9, 255.000], + [5.0, 10, 240.000], + [7.5, 1, 228.750], + [7.5, 2, 176.250], + [7.5, 3, 141.250], + [7.5, 4, 86.250], + [7.5, 5, 51.250], + [7.5, 6, 28.125], + [7.5, 7, 5.625], + [7.5, 8, 326.250], + [7.5, 9, 270.000], + [7.5, 10, 243.750], + [10.0, 1, 232.500], + [10.0, 2, 192.500], + [10.0, 3, 147.500], + [10.0, 4, 102.500], + [10.0, 5, 57.500], + [10.0, 6, 33.750], + [10.0, 7, 11.250], + [10.0, 8, 337.500], + [10.0, 9, 285.000], + [10.0, 10, 247.500], + ] +) + +MUNSELL_HUE_TO_ASTM_HUE: NDArray = np.array( + [ + [2.5, 0, 72.5], + [2.5, 1, 62.5], + [2.5, 2, 52.5], + [2.5, 3, 42.5], + [2.5, 4, 32.5], + [2.5, 5, 22.5], + [2.5, 6, 12.5], + [2.5, 7, 2.5], + [2.5, 8, 92.5], + [2.5, 9, 82.5], + [2.5, 10, 72.5], + [5.0, 0, 75.0], + [5.0, 1, 65.0], + [5.0, 2, 55.0], + [5.0, 3, 45.0], + [5.0, 4, 35.0], + [5.0, 5, 25.0], + [5.0, 6, 15.0], + [5.0, 7, 5.0], + [5.0, 8, 95.0], + [5.0, 9, 85.0], + [5.0, 10, 75.0], + [7.5, 0, 77.5], + [7.5, 1, 67.5], + [7.5, 2, 57.5], + [7.5, 3, 47.5], + [7.5, 4, 37.5], + [7.5, 5, 27.5], + [7.5, 6, 17.5], + [7.5, 7, 7.5], + [7.5, 8, 97.5], + [7.5, 9, 87.5], + [7.5, 10, 77.5], + [10.0, 0, 80.0], + [10.0, 1, 70.0], + [10.0, 2, 60.0], + [10.0, 3, 50.0], + [10.0, 4, 40.0], + [10.0, 5, 30.0], + [10.0, 6, 20.0], + [10.0, 7, 10.0], + [10.0, 8, 100.0], + [10.0, 9, 90.0], + [10.0, 10, 80.0], + ] +) + +MUNSELL_INTERPOLATION_METHODS: List = [ + "Linear", + "Linear", + "Radial", + "Linear", + "Radial", + "Linear", + "Linear", + "Linear", + "Radial", + "Radial", + "Radial", + "Linear", + "Linear", + "Linear", + "Radial", + "Radial", + "Radial", + "Linear", + "Linear", + "Linear", + "Linear", + "Linear", + "Radial", + "Linear", + "Radial", + "Radial", + "Linear", + "Linear", + "Linear", + "Radial", + "Radial", + "Radial", + "Linear", + "Radial", + "Linear", + "Linear", + "Radial", + "Linear", + "Linear", + "Linear", + "Linear", + "Linear", + "Linear", + "Linear", + "Radial", + "Radial", + "Linear", + "Linear", + "Linear", + "Linear", + "Linear", + "Linear", + "Radial", + "Radial", + "Linear", + "Linear", + "Linear", + "Linear", + "Linear", + "Radial", + "Linear", + "Radial", + "Linear", + "Radial", + "Radial", + "Linear", + "Linear", + "Radial", + "Linear", + "Linear", + "Linear", + "Linear", + "Linear", + "Radial", + "Linear", + "Radial", + "Radial", + "Linear", + "Radial", + "Linear", + "Radial", + "Radial", + "Radial", + "Linear", + "Linear", + "Linear", + "Linear", + "Linear", + "Linear", + "Linear", + "Linear", + "Linear", + "Linear", + "Radial", + "Linear", + "Linear", + "Radial", + "Linear", + "Radial", + "Linear", + "Radial", ] -MUNSELL_XY_FROM_RENOTATION_OVOID = [ +MUNSELL_XY_FROM_RENOTATION_OVOID: List = [ [0.1832, 0.4414], [0.2419, 0.3985], [0.3564, 0.3279], @@ -768,30 +1108,31 @@ def _generate_unit_tests_specifications(): # pragma: no cover class TestMunsellValuePriest1920(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.munsell_value_Priest1920` definition + Define :func:`colour.notation.munsell.munsell_value_Priest1920` definition unit tests methods. """ def test_munsell_value_Priest1920(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Priest1920` + Test :func:`colour.notation.munsell.munsell_value_Priest1920` definition. """ self.assertAlmostEqual( - munsell_value_Priest1920(12.23634268), 3.498048410185314, places=7) + munsell_value_Priest1920(12.23634268), 3.498048410185314, places=7 + ) self.assertAlmostEqual( - munsell_value_Priest1920(22.89399987), - 4.7847674833788947, - places=7) + munsell_value_Priest1920(22.89399987), 4.7847674833788947, places=7 + ) self.assertAlmostEqual( - munsell_value_Priest1920(6.29022535), 2.5080321668591092, places=7) + munsell_value_Priest1920(6.29022535), 2.5080321668591092, places=7 + ) def test_n_dimensional_munsell_value_Priest1920(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Priest1920` + Test :func:`colour.notation.munsell.munsell_value_Priest1920` definition n-dimensional arrays support. """ @@ -801,76 +1142,82 @@ def test_n_dimensional_munsell_value_Priest1920(self): V = np.tile(V, 6) Y = np.tile(Y, 6) np.testing.assert_almost_equal( - munsell_value_Priest1920(Y), V, decimal=7) + munsell_value_Priest1920(Y), V, decimal=7 + ) V = np.reshape(V, (2, 3)) Y = np.reshape(Y, (2, 3)) np.testing.assert_almost_equal( - munsell_value_Priest1920(Y), V, decimal=7) + munsell_value_Priest1920(Y), V, decimal=7 + ) V = np.reshape(V, (2, 3, 1)) Y = np.reshape(Y, (2, 3, 1)) np.testing.assert_almost_equal( - munsell_value_Priest1920(Y), V, decimal=7) + munsell_value_Priest1920(Y), V, decimal=7 + ) def test_domain_range_scale_munsell_value_Priest1920(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Priest1920` + Test :func:`colour.notation.munsell.munsell_value_Priest1920` definition domain and range scale support. """ Y = 12.23634268 V = munsell_value_Priest1920(Y) - d_r = (('reference', 1, 1), (1, 0.01, 0.1), (100, 1, 10)) + d_r = (("reference", 1, 1), ("1", 0.01, 0.1), ("100", 1, 10)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( munsell_value_Priest1920(Y * factor_a), V * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_munsell_value_Priest1920(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Priest1920` + Test :func:`colour.notation.munsell.munsell_value_Priest1920` definition nan support. """ munsell_value_Priest1920( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestMunsellValueMunsell1933(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.munsell_value_Munsell1933` + Define :func:`colour.notation.munsell.munsell_value_Munsell1933` definition unit tests methods. """ def test_munsell_value_Munsell1933(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Munsell1933` + Test :func:`colour.notation.munsell.munsell_value_Munsell1933` definition. """ self.assertAlmostEqual( munsell_value_Munsell1933(12.23634268), 4.1627702416858083, - places=7) + places=7, + ) self.assertAlmostEqual( munsell_value_Munsell1933(22.89399987), 5.5914543020790592, - places=7) + places=7, + ) self.assertAlmostEqual( - munsell_value_Munsell1933(6.29022535), - 3.0141971134091761, - places=7) + munsell_value_Munsell1933(6.29022535), 3.0141971134091761, places=7 + ) def test_n_dimensional_munsell_value_Munsell1933(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Munsell1933` + Test :func:`colour.notation.munsell.munsell_value_Munsell1933` definition n-dimensional arrays support. """ @@ -880,70 +1227,78 @@ def test_n_dimensional_munsell_value_Munsell1933(self): V = np.tile(V, 6) Y = np.tile(Y, 6) np.testing.assert_almost_equal( - munsell_value_Munsell1933(Y), V, decimal=7) + munsell_value_Munsell1933(Y), V, decimal=7 + ) V = np.reshape(V, (2, 3)) Y = np.reshape(Y, (2, 3)) np.testing.assert_almost_equal( - munsell_value_Munsell1933(Y), V, decimal=7) + munsell_value_Munsell1933(Y), V, decimal=7 + ) V = np.reshape(V, (2, 3, 1)) Y = np.reshape(Y, (2, 3, 1)) np.testing.assert_almost_equal( - munsell_value_Munsell1933(Y), V, decimal=7) + munsell_value_Munsell1933(Y), V, decimal=7 + ) def test_domain_range_scale_munsell_value_Munsell1933(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Munsell1933` + Test :func:`colour.notation.munsell.munsell_value_Munsell1933` definition domain and range scale support. """ Y = 12.23634268 V = munsell_value_Munsell1933(Y) - d_r = (('reference', 1, 1), (1, 0.01, 0.1), (100, 1, 10)) + d_r = (("reference", 1, 1), ("1", 0.01, 0.1), ("100", 1, 10)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( munsell_value_Munsell1933(Y * factor_a), V * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_munsell_value_Munsell1933(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Munsell1933` + Test :func:`colour.notation.munsell.munsell_value_Munsell1933` definition nan support. """ munsell_value_Munsell1933( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestMunsellValueMoon1943(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.munsell_value_Moon1943` definition + Define :func:`colour.notation.munsell.munsell_value_Moon1943` definition unit tests methods. """ def test_munsell_value_Moon1943(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Moon1943` + Test :func:`colour.notation.munsell.munsell_value_Moon1943` definition. """ self.assertAlmostEqual( - munsell_value_Moon1943(12.23634268), 4.0688120634976421, places=7) + munsell_value_Moon1943(12.23634268), 4.0688120634976421, places=7 + ) self.assertAlmostEqual( - munsell_value_Moon1943(22.89399987), 5.3133627855494412, places=7) + munsell_value_Moon1943(22.89399987), 5.3133627855494412, places=7 + ) self.assertAlmostEqual( - munsell_value_Moon1943(6.29022535), 3.0645015037679695, places=7) + munsell_value_Moon1943(6.29022535), 3.0645015037679695, places=7 + ) def test_n_dimensional_munsell_value_Moon1943(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Moon1943` + Test :func:`colour.notation.munsell.munsell_value_Moon1943` definition n-dimensional arrays support. """ @@ -964,62 +1319,67 @@ def test_n_dimensional_munsell_value_Moon1943(self): def test_domain_range_scale_munsell_value_Moon1943(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Moon1943` + Test :func:`colour.notation.munsell.munsell_value_Moon1943` definition domain and range scale support. """ Y = 12.23634268 V = munsell_value_Moon1943(Y) - d_r = (('reference', 1, 1), (1, 0.01, 0.1), (100, 1, 10)) + d_r = (("reference", 1, 1), ("1", 0.01, 0.1), ("100", 1, 10)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( munsell_value_Moon1943(Y * factor_a), V * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_munsell_value_Moon1943(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Moon1943` + Test :func:`colour.notation.munsell.munsell_value_Moon1943` definition nan support. """ munsell_value_Moon1943( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestMunsellValueSaunderson1944(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.munsell_value_Saunderson1944` + Define :func:`colour.notation.munsell.munsell_value_Saunderson1944` definition unit tests methods. """ def test_munsell_value_Saunderson1944(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Saunderson1944` + Test :func:`colour.notation.munsell.munsell_value_Saunderson1944` definition. """ self.assertAlmostEqual( munsell_value_Saunderson1944(12.23634268), 4.0444736723175119, - places=7) + places=7, + ) self.assertAlmostEqual( munsell_value_Saunderson1944(22.89399987), 5.3783324022305923, - places=7) + places=7, + ) self.assertAlmostEqual( munsell_value_Saunderson1944(6.29022535), 2.9089633927316823, - places=7) + places=7, + ) def test_n_dimensional_munsell_value_Saunderson1944(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Saunderson1944` + Test :func:`colour.notation.munsell.munsell_value_Saunderson1944` definition n-dimensional arrays support. """ @@ -1029,70 +1389,78 @@ def test_n_dimensional_munsell_value_Saunderson1944(self): V = np.tile(V, 6) Y = np.tile(Y, 6) np.testing.assert_almost_equal( - munsell_value_Saunderson1944(Y), V, decimal=7) + munsell_value_Saunderson1944(Y), V, decimal=7 + ) V = np.reshape(V, (2, 3)) Y = np.reshape(Y, (2, 3)) np.testing.assert_almost_equal( - munsell_value_Saunderson1944(Y), V, decimal=7) + munsell_value_Saunderson1944(Y), V, decimal=7 + ) V = np.reshape(V, (2, 3, 1)) Y = np.reshape(Y, (2, 3, 1)) np.testing.assert_almost_equal( - munsell_value_Saunderson1944(Y), V, decimal=7) + munsell_value_Saunderson1944(Y), V, decimal=7 + ) def test_domain_range_scale_munsell_value_Saunderson1944(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Saunderson1944` + Test :func:`colour.notation.munsell.munsell_value_Saunderson1944` definition domain and range scale support. """ Y = 12.23634268 V = munsell_value_Saunderson1944(Y) - d_r = (('reference', 1, 1), (1, 0.01, 0.1), (100, 1, 10)) + d_r = (("reference", 1, 1), ("1", 0.01, 0.1), ("100", 1, 10)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( munsell_value_Saunderson1944(Y * factor_a), V * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_munsell_value_Saunderson1944(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Saunderson1944` + Test :func:`colour.notation.munsell.munsell_value_Saunderson1944` definition nan support. """ munsell_value_Saunderson1944( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestMunsellValueLadd1955(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.munsell_value_Ladd1955` definition + Define :func:`colour.notation.munsell.munsell_value_Ladd1955` definition unit tests methods. """ def test_munsell_value_Ladd1955(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Ladd1955` + Test :func:`colour.notation.munsell.munsell_value_Ladd1955` definition. """ self.assertAlmostEqual( - munsell_value_Ladd1955(12.23634268), 4.0511633044287088, places=7) + munsell_value_Ladd1955(12.23634268), 4.0511633044287088, places=7 + ) self.assertAlmostEqual( - munsell_value_Ladd1955(22.89399987), 5.3718647913936772, places=7) + munsell_value_Ladd1955(22.89399987), 5.3718647913936772, places=7 + ) self.assertAlmostEqual( - munsell_value_Ladd1955(6.29022535), 2.9198269939751613, places=7) + munsell_value_Ladd1955(6.29022535), 2.9198269939751613, places=7 + ) def test_n_dimensional_munsell_value_Ladd1955(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Ladd1955` + Test :func:`colour.notation.munsell.munsell_value_Ladd1955` definition n-dimensional arrays support. """ @@ -1113,56 +1481,61 @@ def test_n_dimensional_munsell_value_Ladd1955(self): def test_domain_range_scale_munsell_value_Ladd1955(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Ladd1955` + Test :func:`colour.notation.munsell.munsell_value_Ladd1955` definition domain and range scale support. """ Y = 12.23634268 V = munsell_value_Ladd1955(Y) - d_r = (('reference', 1, 1), (1, 0.01, 0.1), (100, 1, 10)) + d_r = (("reference", 1, 1), ("1", 0.01, 0.1), ("100", 1, 10)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( munsell_value_Ladd1955(Y * factor_a), V * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_munsell_value_Ladd1955(self): """ - Tests :func:`colour.notation.munsell.munsell_value_Ladd1955` + Test :func:`colour.notation.munsell.munsell_value_Ladd1955` definition nan support. """ munsell_value_Ladd1955( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestMunsellValueMcCamy1992(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.munsell_value_McCamy1987` definition + Define :func:`colour.notation.munsell.munsell_value_McCamy1987` definition unit tests methods. """ def test_munsell_value_McCamy1987(self): """ - Tests :func:`colour.notation.munsell.munsell_value_McCamy1987` + Test :func:`colour.notation.munsell.munsell_value_McCamy1987` definition. """ self.assertAlmostEqual( - munsell_value_McCamy1987(12.23634268), 4.081434853194113, places=7) + munsell_value_McCamy1987(12.23634268), 4.081434853194113, places=7 + ) self.assertAlmostEqual( - munsell_value_McCamy1987(22.89399987), 5.394083970919982, places=7) + munsell_value_McCamy1987(22.89399987), 5.394083970919982, places=7 + ) self.assertAlmostEqual( - munsell_value_McCamy1987(6.29022535), 2.9750160800320096, places=7) + munsell_value_McCamy1987(6.29022535), 2.9750160800320096, places=7 + ) def test_n_dimensional_munsell_value_McCamy1987(self): """ - Tests :func:`colour.notation.munsell.munsell_value_McCamy1987` + Test :func:`colour.notation.munsell.munsell_value_McCamy1987` definition n-dimensional arrays support. """ @@ -1172,70 +1545,78 @@ def test_n_dimensional_munsell_value_McCamy1987(self): V = np.tile(V, 6) Y = np.tile(Y, 6) np.testing.assert_almost_equal( - munsell_value_McCamy1987(Y), V, decimal=7) + munsell_value_McCamy1987(Y), V, decimal=7 + ) V = np.reshape(V, (2, 3)) Y = np.reshape(Y, (2, 3)) np.testing.assert_almost_equal( - munsell_value_McCamy1987(Y), V, decimal=7) + munsell_value_McCamy1987(Y), V, decimal=7 + ) V = np.reshape(V, (2, 3, 1)) Y = np.reshape(Y, (2, 3, 1)) np.testing.assert_almost_equal( - munsell_value_McCamy1987(Y), V, decimal=7) + munsell_value_McCamy1987(Y), V, decimal=7 + ) def test_domain_range_scale_munsell_value_McCamy1987(self): """ - Tests :func:`colour.notation.munsell.munsell_value_McCamy1987` + Test :func:`colour.notation.munsell.munsell_value_McCamy1987` definition domain and range scale support. """ Y = 12.23634268 V = munsell_value_McCamy1987(Y) - d_r = (('reference', 1, 1), (1, 0.01, 0.1), (100, 1, 10)) + d_r = (("reference", 1, 1), ("1", 0.01, 0.1), ("100", 1, 10)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( munsell_value_McCamy1987(Y * factor_a), V * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_munsell_value_McCamy1987(self): """ - Tests :func:`colour.notation.munsell.munsell_value_McCamy1987` + Test :func:`colour.notation.munsell.munsell_value_McCamy1987` definition nan support. """ munsell_value_McCamy1987( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestMunsellValueASTMD1535(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.munsell_value_ASTMD1535` + Define :func:`colour.notation.munsell.munsell_value_ASTMD1535` definition unit tests methods. """ def test_munsell_value_ASTMD1535(self): """ - Tests :func:`colour.notation.munsell.munsell_value_ASTMD1535` + Test :func:`colour.notation.munsell.munsell_value_ASTMD1535` definition. """ self.assertAlmostEqual( - munsell_value_ASTMD1535(12.23634268), 4.0824437076525664, places=7) + munsell_value_ASTMD1535(12.23634268), 4.0824437076525664, places=7 + ) self.assertAlmostEqual( - munsell_value_ASTMD1535(22.89399987), 5.3913268228155395, places=7) + munsell_value_ASTMD1535(22.89399987), 5.3913268228155395, places=7 + ) self.assertAlmostEqual( - munsell_value_ASTMD1535(6.29022535), 2.9761930839606454, places=7) + munsell_value_ASTMD1535(6.29022535), 2.9761930839606454, places=7 + ) def test_n_dimensional_munsell_value_ASTMD1535(self): """ - Tests :func:`colour.notation.munsell.munsell_value_ASTMD1535` + Test :func:`colour.notation.munsell.munsell_value_ASTMD1535` definition n-dimensional arrays support. """ @@ -1245,55 +1626,60 @@ def test_n_dimensional_munsell_value_ASTMD1535(self): V = np.tile(V, 6) Y = np.tile(Y, 6) np.testing.assert_almost_equal( - munsell_value_ASTMD1535(Y), V, decimal=7) + munsell_value_ASTMD1535(Y), V, decimal=7 + ) V = np.reshape(V, (2, 3)) Y = np.reshape(Y, (2, 3)) np.testing.assert_almost_equal( - munsell_value_ASTMD1535(Y), V, decimal=7) + munsell_value_ASTMD1535(Y), V, decimal=7 + ) V = np.reshape(V, (2, 3, 1)) Y = np.reshape(Y, (2, 3, 1)) np.testing.assert_almost_equal( - munsell_value_ASTMD1535(Y), V, decimal=7) + munsell_value_ASTMD1535(Y), V, decimal=7 + ) def test_domain_range_scale_munsell_value_ASTMD1535(self): """ - Tests :func:`colour.notation.munsell.munsell_value_ASTMD1535` + Test :func:`colour.notation.munsell.munsell_value_ASTMD1535` definition domain and range scale support. """ Y = 12.23634268 V = munsell_value_ASTMD1535(Y) - d_r = (('reference', 1, 1), (1, 0.01, 0.1), (100, 1, 10)) + d_r = (("reference", 1, 1), ("1", 0.01, 0.1), ("100", 1, 10)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( munsell_value_ASTMD1535(Y * factor_a), V * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_munsell_value_ASTMD1535(self): """ - Tests :func:`colour.notation.munsell.munsell_value_ASTMD1535` + Test :func:`colour.notation.munsell.munsell_value_ASTMD1535` definition nan support. """ munsell_value_ASTMD1535( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestMunsellSpecification_to_xyY(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.munsell_specification_to_xyY` + Define :func:`colour.notation.munsell.munsell_specification_to_xyY` definition unit tests methods. """ def test_munsell_specification_to_xyY(self): """ - Tests :func:`colour.notation.munsell.munsell_specification_to_xyY` + Test :func:`colour.notation.munsell.munsell_specification_to_xyY` definition. """ @@ -1302,7 +1688,8 @@ def test_munsell_specification_to_xyY(self): as_float_array(list(MUNSELL_SPECIFICATIONS[..., 1])), ) np.testing.assert_almost_equal( - munsell_specification_to_xyY(specification), xyY, decimal=7) + munsell_specification_to_xyY(specification), xyY, decimal=7 + ) specification, xyY = ( as_float_array(list(MUNSELL_GREYS_SPECIFICATIONS[..., 0])), @@ -1311,30 +1698,35 @@ def test_munsell_specification_to_xyY(self): specification = np.squeeze(specification) nan_array = np.full(specification.shape, np.nan) specification = tstack( - [nan_array, specification, nan_array, nan_array]) + [nan_array, specification, nan_array, nan_array] + ) np.testing.assert_almost_equal( - munsell_specification_to_xyY(specification), xyY, decimal=7) + munsell_specification_to_xyY(specification), xyY, decimal=7 + ) def test_n_dimensional_munsell_specification_to_xyY(self): """ - Tests :func:`colour.notation.munsell.munsell_specification_to_xyY` + Test :func:`colour.notation.munsell.munsell_specification_to_xyY` definition n-dimensional arrays support. """ specification = np.array( - [7.18927191, 5.34025196, 16.05861170, 3.00000000]) + [7.18927191, 5.34025196, 16.05861170, 3.00000000] + ) xyY = munsell_specification_to_xyY(specification) specification = np.tile(specification, (6, 1)) xyY = np.tile(xyY, (6, 1)) np.testing.assert_almost_equal( - munsell_specification_to_xyY(specification), xyY, decimal=7) + munsell_specification_to_xyY(specification), xyY, decimal=7 + ) specification = np.reshape(specification, (2, 3, 4)) xyY = np.reshape(xyY, (2, 3, 3)) np.testing.assert_almost_equal( - munsell_specification_to_xyY(specification), xyY, decimal=7) + munsell_specification_to_xyY(specification), xyY, decimal=7 + ) specification = np.array([np.nan, 8.9, np.nan, np.nan]) xyY = munsell_specification_to_xyY(specification) @@ -1342,39 +1734,43 @@ def test_n_dimensional_munsell_specification_to_xyY(self): specification = np.tile(specification, (6, 1)) xyY = np.tile(xyY, (6, 1)) np.testing.assert_almost_equal( - munsell_specification_to_xyY(specification), xyY, decimal=7) + munsell_specification_to_xyY(specification), xyY, decimal=7 + ) specification = np.reshape(specification, (2, 3, 4)) xyY = np.reshape(xyY, (2, 3, 3)) np.testing.assert_almost_equal( - munsell_specification_to_xyY(specification), xyY, decimal=7) + munsell_specification_to_xyY(specification), xyY, decimal=7 + ) def test_domain_range_scale_munsell_specification_to_xyY(self): """ - Tests :func:`colour.notation.munsell.munsell_specification_to_xyY` + Test :func:`colour.notation.munsell.munsell_specification_to_xyY` definition domain and range scale support. """ specification = np.array( - [7.18927191, 5.34025196, 16.05861170, 3.00000000]) + [7.18927191, 5.34025196, 16.05861170, 3.00000000] + ) xyY = munsell_specification_to_xyY(specification) d_r = ( - ('reference', 1, 1), - (1, np.array([0.1, 0.1, 1 / 50, 0.1]), 1), - (100, np.array([10, 10, 2, 10]), np.array([1, 1, 100])), + ("reference", 1, 1), + ("1", np.array([0.1, 0.1, 1 / 50, 0.1]), 1), + ("100", np.array([10, 10, 2, 10]), np.array([1, 1, 100])), ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( munsell_specification_to_xyY(specification * factor_a), xyY * factor_b, - decimal=7) + decimal=7, + ) @ignore_numpy_errors def test_nan_munsell_specification_to_xyY(self): """ - Tests :func:`colour.notation.munsell.munsell_specification_to_xyY` + Test :func:`colour.notation.munsell.munsell_specification_to_xyY` definition nan support. """ @@ -1390,73 +1786,78 @@ def test_nan_munsell_specification_to_xyY(self): class TestMunsellColour_to_xyY(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.munsell_colour_to_xyY` definition + Define :func:`colour.notation.munsell.munsell_colour_to_xyY` definition unit tests methods. """ def test_domain_range_scale_munsell_colour_to_xyY(self): """ - Tests :func:`colour.notation.munsell.munsell_colour_to_xyY` definition + Test :func:`colour.notation.munsell.munsell_colour_to_xyY` definition domain and range scale support. """ - munsell_colour = '4.2YR 8.1/5.3' + munsell_colour = "4.2YR 8.1/5.3" xyY = munsell_colour_to_xyY(munsell_colour) d_r = ( - ('reference', 1), - (1, 1), - (100, np.array([1, 1, 100])), + ("reference", 1), + ("1", 1), + ("100", np.array([1, 1, 100])), ) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( munsell_colour_to_xyY(munsell_colour), xyY * factor, - decimal=7) + decimal=7, + ) def test_n_dimensional_munsell_colour_to_xyY(self): """ - Tests :func:`colour.notation.munsell.munsell_colour_to_xyY` definition + Test :func:`colour.notation.munsell.munsell_colour_to_xyY` definition n-dimensional arrays support. """ - munsell_colour = '4.2YR 8.1/5.3' + munsell_colour = "4.2YR 8.1/5.3" xyY = munsell_colour_to_xyY(munsell_colour) munsell_colour = np.tile(munsell_colour, 6) xyY = np.tile(xyY, (6, 1)) np.testing.assert_almost_equal( - munsell_colour_to_xyY(munsell_colour), xyY, decimal=7) + munsell_colour_to_xyY(munsell_colour), xyY, decimal=7 + ) munsell_colour = np.reshape(munsell_colour, (2, 3)) xyY = np.reshape(xyY, (2, 3, 3)) np.testing.assert_almost_equal( - munsell_colour_to_xyY(munsell_colour), xyY, decimal=7) + munsell_colour_to_xyY(munsell_colour), xyY, decimal=7 + ) - munsell_colour = 'N8.9' + munsell_colour = "N8.9" xyY = munsell_colour_to_xyY(munsell_colour) munsell_colour = np.tile(munsell_colour, 6) xyY = np.tile(xyY, (6, 1)) np.testing.assert_almost_equal( - munsell_colour_to_xyY(munsell_colour), xyY, decimal=7) + munsell_colour_to_xyY(munsell_colour), xyY, decimal=7 + ) munsell_colour = np.reshape(munsell_colour, (2, 3)) xyY = np.reshape(xyY, (2, 3, 3)) np.testing.assert_almost_equal( - munsell_colour_to_xyY(munsell_colour), xyY, decimal=7) + munsell_colour_to_xyY(munsell_colour), xyY, decimal=7 + ) class TestxyY_to_munsell_specification(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.xyY_to_munsell_specification` + Define :func:`colour.notation.munsell.xyY_to_munsell_specification` definition unit tests methods. """ def test_xyY_to_munsell_specification(self): """ - Tests :func:`colour.notation.munsell.xyY_to_munsell_specification` + Test :func:`colour.notation.munsell.xyY_to_munsell_specification` definition. """ @@ -1469,7 +1870,8 @@ def test_xyY_to_munsell_specification(self): xyY_to_munsell_specification(xyY), specification, rtol=0.00001, - atol=0.00001) + atol=0.00001, + ) specification, xyY = ( as_float_array(list(MUNSELL_GREYS_SPECIFICATIONS[..., 0])), @@ -1478,17 +1880,19 @@ def test_xyY_to_munsell_specification(self): specification = np.squeeze(specification) nan_array = np.full(specification.shape, np.nan) specification = tstack( - [nan_array, specification, nan_array, nan_array]) + [nan_array, specification, nan_array, nan_array] + ) np.testing.assert_allclose( xyY_to_munsell_specification(xyY), specification, rtol=0.00001, - atol=0.00001) + atol=0.00001, + ) def test_n_dimensional_xyY_to_munsell_specification(self): """ - Tests :func:`colour.notation.munsell.xyY_to_munsell_specification` + Test :func:`colour.notation.munsell.xyY_to_munsell_specification` definition n-dimensional arrays support. """ @@ -1498,25 +1902,30 @@ def test_n_dimensional_xyY_to_munsell_specification(self): xyY = np.tile(xyY, (6, 1)) specification = np.tile(specification, (6, 1)) np.testing.assert_almost_equal( - xyY_to_munsell_specification(xyY), specification, decimal=7) + xyY_to_munsell_specification(xyY), specification, decimal=7 + ) xyY = np.reshape(xyY, (2, 3, 3)) specification = np.reshape(specification, (2, 3, 4)) np.testing.assert_almost_equal( - xyY_to_munsell_specification(xyY), specification, decimal=7) + xyY_to_munsell_specification(xyY), specification, decimal=7 + ) def test_raise_exception_xyY_to_munsell_specification(self): """ - Tests :func:`colour.notation.munsell.xyY_to_munsell_specification` + Test :func:`colour.notation.munsell.xyY_to_munsell_specification` definition raised exception. """ - self.assertRaises(RuntimeError, xyY_to_munsell_specification, - np.array([0.90615118, 0.57945103, 0.91984064])) + self.assertRaises( + RuntimeError, + xyY_to_munsell_specification, + np.array([0.90615118, 0.57945103, 0.91984064]), + ) def test_domain_range_scale_xyY_to_munsell_specification(self): """ - Tests :func:`colour.notation.munsell.xyY_to_munsell_specification` + Test :func:`colour.notation.munsell.xyY_to_munsell_specification` definition domain and range scale support. """ @@ -1524,9 +1933,9 @@ def test_domain_range_scale_xyY_to_munsell_specification(self): specification = xyY_to_munsell_specification(xyY) d_r = ( - ('reference', 1, 1), - (1, 1, np.array([0.1, 0.1, 1 / 50, 0.1])), - (100, np.array([1, 1, 100]), np.array([10, 10, 2, 10])), + ("reference", 1, 1), + ("1", 1, np.array([0.1, 0.1, 1 / 50, 0.1])), + ("100", np.array([1, 1, 100]), np.array([10, 10, 2, 10])), ) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): @@ -1534,12 +1943,13 @@ def test_domain_range_scale_xyY_to_munsell_specification(self): xyY_to_munsell_specification(xyY * factor_a), specification * factor_b, rtol=0.00001, - atol=0.00001) + atol=0.00001, + ) @ignore_numpy_errors def test_nan_xyY_to_munsell_specification(self): """ - Tests :func:`colour.notation.munsell.xyY_to_munsell_specification` + Test :func:`colour.notation.munsell.xyY_to_munsell_specification` definition nan support. """ @@ -1555,13 +1965,13 @@ def test_nan_xyY_to_munsell_specification(self): class TestxyY_to_munsell_colour(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.xyY_to_munsell_colour` definition + Define :func:`colour.notation.munsell.xyY_to_munsell_colour` definition unit tests methods. """ def test_domain_range_scale_xyY_to_munsell_colour(self): """ - Tests :func:`colour.notation.munsell.xyY_to_munsell_colour` definition + Test :func:`colour.notation.munsell.xyY_to_munsell_colour` definition domain and range scale support. """ @@ -1569,19 +1979,19 @@ def test_domain_range_scale_xyY_to_munsell_colour(self): munsell_colour = xyY_to_munsell_colour(xyY) d_r = ( - ('reference', 1), - (1, 1), - (100, np.array([1, 1, 100])), + ("reference", 1), + ("1", 1), + ("100", np.array([1, 1, 100])), ) for scale, factor in d_r: with domain_range_scale(scale): - print(scale, factor) self.assertEqual( - xyY_to_munsell_colour(xyY * factor), munsell_colour) + xyY_to_munsell_colour(xyY * factor), munsell_colour + ) def test_n_dimensional_xyY_to_munsell_colour(self): """ - Tests :func:`colour.notation.munsell.xyY_to_munsell_colour` definition + Test :func:`colour.notation.munsell.xyY_to_munsell_colour` definition n-dimensional arrays support. """ @@ -1610,49 +2020,52 @@ def test_n_dimensional_xyY_to_munsell_colour(self): class TestParseMunsellColour(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.parse_munsell_colour` definition + Define :func:`colour.notation.munsell.parse_munsell_colour` definition unit tests methods. """ def test_parse_munsell_colour(self): """ - Tests :func:`colour.notation.munsell.is_grey_munsell_colour` + Test :func:`colour.notation.munsell.is_grey_munsell_colour` definition. """ np.testing.assert_almost_equal( - parse_munsell_colour('N5.2'), + parse_munsell_colour("N5.2"), np.array([np.nan, 5.2, np.nan, np.nan]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - parse_munsell_colour('0YR 2.0/4.0'), + parse_munsell_colour("0YR 2.0/4.0"), np.array([0.0, 2.0, 4.0, 6]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - parse_munsell_colour('4.2YR 8.1/5.3'), + parse_munsell_colour("4.2YR 8.1/5.3"), np.array([4.2, 8.1, 5.3, 6]), - decimal=7) + decimal=7, + ) def test_raise_exception_parse_munsell_colour(self): """ - Tests :func:`colour.notation.munsell.is_grey_munsell_colour` + Test :func:`colour.notation.munsell.is_grey_munsell_colour` definition raised exception. """ - self.assertRaises(ValueError, parse_munsell_colour, '4.2YQ 8.1/5.3') + self.assertRaises(ValueError, parse_munsell_colour, "4.2YQ 8.1/5.3") class TestIsGreyMunsellColour(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.is_grey_munsell_colour` definition + Define :func:`colour.notation.munsell.is_grey_munsell_colour` definition unit tests methods. """ def test_is_grey_munsell_colour(self): """ - Tests :func:`colour.notation.munsell.is_grey_munsell_colour` + Test :func:`colour.notation.munsell.is_grey_munsell_colour` definition. """ @@ -1663,208 +2076,239 @@ def test_is_grey_munsell_colour(self): self.assertFalse(is_grey_munsell_colour(np.array([4.2, 8.1, 5.3, 6]))) self.assertTrue( - is_grey_munsell_colour(np.array([np.nan, 0.5, np.nan, np.nan]))) + is_grey_munsell_colour(np.array([np.nan, 0.5, np.nan, np.nan])) + ) -class TestNormalizeMunsellSpecification(unittest.TestCase): +class TestNormaliseMunsellSpecification(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.normalize_munsell_specification` + Define :func:`colour.notation.munsell.normalise_munsell_specification` definition unit tests methods. """ - def test_normalize_munsell_specification(self): + def test_normalise_munsell_specification(self): """ - Tests :func:`colour.notation.munsell.normalize_munsell_specification` + Test :func:`colour.notation.munsell.normalise_munsell_specification` definition. """ np.testing.assert_almost_equal( - normalize_munsell_specification((0.0, 2.0, 4.0, 6)), + normalise_munsell_specification((0.0, 2.0, 4.0, 6)), np.array([10.0, 2.0, 4.0, 7]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - normalize_munsell_specification((0.0, 2.0, 4.0, 8)), + normalise_munsell_specification((0.0, 2.0, 4.0, 8)), np.array([10.0, 2.0, 4.0, 9]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - normalize_munsell_specification((0, 2.0, 4.0, 10)), + normalise_munsell_specification((0, 2.0, 4.0, 10)), np.array([10.0, 2.0, 4.0, 1]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - normalize_munsell_specification(0.5), + normalise_munsell_specification(0.5), np.array([np.nan, 0.5, np.nan, np.nan]), - decimal=7) + decimal=7, + ) class TestMunsellColourToMunsellSpecification(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.\ + Define :func:`colour.notation.munsell.\ munsell_colour_to_munsell_specification` definition unit tests methods. """ def test_munsell_colour_to_munsell_specification(self): """ - Tests :func:`colour.notation.munsell.\ + Test :func:`colour.notation.munsell.\ munsell_colour_to_munsell_specification` definition. """ np.testing.assert_almost_equal( - munsell_colour_to_munsell_specification('0.0YR 2.0/4.0'), + munsell_colour_to_munsell_specification("0.0YR 2.0/4.0"), np.array([10.0, 2.0, 4.0, 7]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - munsell_colour_to_munsell_specification('0.0RP 2.0/4.0'), + munsell_colour_to_munsell_specification("0.0RP 2.0/4.0"), np.array([10.0, 2.0, 4.0, 9]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - munsell_colour_to_munsell_specification('10.0B 2.0/4.0'), + munsell_colour_to_munsell_specification("10.0B 2.0/4.0"), np.array([10.0, 2.0, 4.0, 1]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - munsell_colour_to_munsell_specification('N5.2'), + munsell_colour_to_munsell_specification("N5.2"), np.array([np.nan, 5.2, np.nan, np.nan]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - munsell_colour_to_munsell_specification('0.0YR 2.0/0.0'), + munsell_colour_to_munsell_specification("0.0YR 2.0/0.0"), np.array([np.nan, 2.0, np.nan, np.nan]), - decimal=7) + decimal=7, + ) class TestMunsellSpecificationToMunsellColour(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.\ + Define :func:`colour.notation.munsell.\ munsell_specification_to_munsell_colour` definition unit tests methods. """ def test_munsell_specification_to_munsell_colour(self): """ - Tests :func:`colour.notation.munsell.\ + Test :func:`colour.notation.munsell.\ munsell_specification_to_munsell_colour` definition. """ self.assertEqual( munsell_specification_to_munsell_colour( - np.array([10.0, 2.0, 4.0, 7])), '10.0R 2.0/4.0') + np.array([10.0, 2.0, 4.0, 7]) + ), + "10.0R 2.0/4.0", + ) self.assertEqual( munsell_specification_to_munsell_colour( - np.array([10.0, 2.0, 4.0, 9])), '10.0P 2.0/4.0') + np.array([10.0, 2.0, 4.0, 9]) + ), + "10.0P 2.0/4.0", + ) self.assertEqual( munsell_specification_to_munsell_colour( - np.array([10.0, 2.0, 4.0, 1])), '10.0B 2.0/4.0') + np.array([10.0, 2.0, 4.0, 1]) + ), + "10.0B 2.0/4.0", + ) self.assertEqual( munsell_specification_to_munsell_colour( - np.array([np.nan, 5.2, np.nan, np.nan])), 'N5.2') + np.array([np.nan, 5.2, np.nan, np.nan]) + ), + "N5.2", + ) self.assertEqual( munsell_specification_to_munsell_colour( - np.array([0.0, 2.0, 4.0, 7])), '10.0RP 2.0/4.0') + np.array([0.0, 2.0, 4.0, 7]) + ), + "10.0RP 2.0/4.0", + ) self.assertEqual( munsell_specification_to_munsell_colour( - np.array([10.0, 0.0, 4.0, 7])), 'N0.0') + np.array([10.0, 0.0, 4.0, 7]) + ), + "N0.0", + ) class Test_xyY_fromRenotation(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.xyY_from_renotation` definition + Define :func:`colour.notation.munsell.xyY_from_renotation` definition unit tests methods. """ def test_xyY_from_renotation(self): """ - Tests :func:`colour.notation.munsell.xyY_from_renotation` + Test :func:`colour.notation.munsell.xyY_from_renotation` definition. """ np.testing.assert_array_equal( xyY_from_renotation((2.5, 0.2, 2.0, 4)), - np.array([0.713, 1.414, 0.237])) + np.array([0.713, 1.414, 0.237]), + ) np.testing.assert_array_equal( xyY_from_renotation((5.0, 0.2, 2.0, 4)), - np.array([0.449, 1.145, 0.237])) + np.array([0.449, 1.145, 0.237]), + ) np.testing.assert_array_equal( xyY_from_renotation((7.5, 0.2, 2.0, 4)), - np.array([0.262, 0.837, 0.237])) + np.array([0.262, 0.837, 0.237]), + ) class TestIsSpecificationInRenotation(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.is_specification_in_renotation` + Define :func:`colour.notation.munsell.is_specification_in_renotation` definition unit tests methods. """ def test_is_specification_in_renotation(self): """ - Tests :func:`colour.notation.munsell.is_specification_in_renotation` + Test :func:`colour.notation.munsell.is_specification_in_renotation` definition. """ self.assertTrue( - is_specification_in_renotation(np.array([2.5, 0.2, 2.0, 4]))) + is_specification_in_renotation(np.array([2.5, 0.2, 2.0, 4])) + ) self.assertTrue( - is_specification_in_renotation(np.array([5.0, 0.2, 2.0, 4]))) + is_specification_in_renotation(np.array([5.0, 0.2, 2.0, 4])) + ) self.assertFalse( - is_specification_in_renotation(np.array([25.0, 0.2, 2.0, 4]))) + is_specification_in_renotation(np.array([25.0, 0.2, 2.0, 4])) + ) class TestBoundingHuesFromRenotation(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.bounding_hues_from_renotation` + Define :func:`colour.notation.munsell.bounding_hues_from_renotation` definition unit tests methods. """ def test_bounding_hues_from_renotation(self): """ - Tests :func:`colour.notation.munsell.bounding_hues_from_renotation` + Test :func:`colour.notation.munsell.bounding_hues_from_renotation` definition. """ for i, (specification, _xyY) in enumerate(MUNSELL_SPECIFICATIONS): hue, _value, _chroma, code = specification np.testing.assert_array_equal( - bounding_hues_from_renotation(hue, code), - MUNSELL_BOUNDING_HUES[i]) + bounding_hues_from_renotation([hue, code]), + MUNSELL_BOUNDING_HUES[i], + ) class TestHueToHueAngle(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.hue_to_hue_angle` definition units + Define :func:`colour.notation.munsell.hue_to_hue_angle` definition unit tests methods. """ def test_hue_to_hue_angle(self): - """ - Tests :func:`colour.notation.munsell.hue_to_hue_angle` definition. - """ + """Test :func:`colour.notation.munsell.hue_to_hue_angle` definition.""" for hue, code, angle in MUNSELL_HUE_TO_ANGLE: - self.assertEqual(hue_to_hue_angle(hue, code), angle) + self.assertEqual(hue_to_hue_angle([hue, code]), angle) class TestHueAngleToHue(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.hue_angle_to_hue` definition units + Define :func:`colour.notation.munsell.hue_angle_to_hue` definition unit tests methods. """ def test_hue_angle_to_hue(self): - """ - Tests :func:`colour.notation.munsell.hue_angle_to_hue` definition. - """ + """Test :func:`colour.notation.munsell.hue_angle_to_hue` definition.""" for hue, code, angle in MUNSELL_HUE_TO_ANGLE: np.testing.assert_array_equal(hue_angle_to_hue(angle), (hue, code)) @@ -1872,54 +2316,57 @@ def test_hue_angle_to_hue(self): class TestHueTo_ASTM_hue(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.hue_to_ASTM_hue` definition units + Define :func:`colour.notation.munsell.hue_to_ASTM_hue` definition unit tests methods. """ def test_hue_to_ASTM_hue(self): - """ - Tests :func:`colour.notation.munsell.hue_to_ASTM_hue` definition. - """ + """Test :func:`colour.notation.munsell.hue_to_ASTM_hue` definition.""" for hue, code, angle in MUNSELL_HUE_TO_ASTM_HUE: - self.assertEqual(hue_to_ASTM_hue(hue, code), angle) + self.assertEqual(hue_to_ASTM_hue([hue, code]), angle) class TestInterpolationMethodFromRenotationOvoid(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.\ + Define :func:`colour.notation.munsell.\ interpolation_method_from_renotation_ovoid` definition unit tests methods. """ def test_interpolation_method_from_renotation_ovoid(self): """ - Tests :func:`colour.notation.munsell.\ + Test :func:`colour.notation.munsell.\ interpolation_method_from_renotation_ovoid` definition. """ for i, (specification, _xyY) in enumerate(MUNSELL_EVEN_SPECIFICATIONS): self.assertEqual( interpolation_method_from_renotation_ovoid(specification), - MUNSELL_INTERPOLATION_METHODS[i]) + MUNSELL_INTERPOLATION_METHODS[i], + ) self.assertIsNone( interpolation_method_from_renotation_ovoid( - np.array([np.nan, 5.2, np.nan, np.nan]))) + np.array([np.nan, 5.2, np.nan, np.nan]) + ) + ) self.assertIsNone( interpolation_method_from_renotation_ovoid( - np.array([2.5, 10.0, 2.0, 4]))) + np.array([2.5, 10.0, 2.0, 4]) + ) + ) class Test_xy_fromRenotationOvoid(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.xy_from_renotation_ovoid` definition + Define :func:`colour.notation.munsell.xy_from_renotation_ovoid` definition unit tests methods. """ def test_xy_from_renotation_ovoid(self): """ - Tests :func:`colour.notation.munsell.xy_from_renotation_ovoid` + Test :func:`colour.notation.munsell.xy_from_renotation_ovoid` definition. """ @@ -1928,96 +2375,110 @@ def test_xy_from_renotation_ovoid(self): np.testing.assert_almost_equal( xy_from_renotation_ovoid(specification), MUNSELL_XY_FROM_RENOTATION_OVOID[i], - decimal=7) + decimal=7, + ) class TestLCHabToMunsellSpecification(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.LCHab_to_munsell_specification` + Define :func:`colour.notation.munsell.LCHab_to_munsell_specification` definition unit tests methods. """ def test_LCHab_to_munsell_specification(self): """ - Tests :func:`colour.notation.munsell.LCHab_to_munsell_specification` + Test :func:`colour.notation.munsell.LCHab_to_munsell_specification` definition. """ np.testing.assert_almost_equal( LCHab_to_munsell_specification( - np.array([100.00000000, 21.57210357, 272.22819350])), + np.array([100.00000000, 21.57210357, 272.22819350]) + ), np.array([5.618942638888882, 10.0, 4.314420714000000, 10]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( LCHab_to_munsell_specification( - np.array([100.00000000, 426.67945353, 72.39590835])), + np.array([100.00000000, 426.67945353, 72.39590835]) + ), np.array([0.109974541666666, 10.0, 85.335890706000001, 5]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( LCHab_to_munsell_specification( - np.array([100.00000000, 74.05216981, 276.45318193])), + np.array([100.00000000, 74.05216981, 276.45318193]) + ), np.array([6.792550536111119, 10.0, 14.810433961999999, 10]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( LCHab_to_munsell_specification( - np.array([100.00000000, 21.57210357, 0.00000000])), + np.array([100.00000000, 21.57210357, 0.00000000]) + ), np.array([10.000000000000000, 10.0, 4.314420714000000, 8]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( LCHab_to_munsell_specification( - np.array([100.00000000, 21.57210357, 36.00000000])), + np.array([100.00000000, 21.57210357, 36.00000000]) + ), np.array([10.000000000000000, 10.0, 4.314420714000000, 7]), - decimal=7) + decimal=7, + ) class TestMaximumChromaFromRenotation(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.maximum_chroma_from_renotation` + Define :func:`colour.notation.munsell.maximum_chroma_from_renotation` definition unit tests methods. """ def test_maximum_chroma_from_renotation(self): """ - Tests :func:`colour.notation.munsell.maximum_chroma_from_renotation` + Test :func:`colour.notation.munsell.maximum_chroma_from_renotation` definition. """ - self.assertEqual(maximum_chroma_from_renotation(2.5, 5, 5), 14.0) + self.assertEqual(maximum_chroma_from_renotation([2.5, 5, 5]), 14.0) self.assertEqual( - maximum_chroma_from_renotation(8.675, 1.225, 10), 48.0) + maximum_chroma_from_renotation([8.675, 1.225, 10]), 48.0 + ) - self.assertEqual(maximum_chroma_from_renotation(6.875, 3.425, 1), 16.0) + self.assertEqual( + maximum_chroma_from_renotation([6.875, 3.425, 1]), 16.0 + ) class TestMunsellSpecification_to_xy(unittest.TestCase): """ - Defines :func:`colour.notation.munsell.munsell_specification_to_xy` + Define :func:`colour.notation.munsell.munsell_specification_to_xy` definition unit tests methods. """ def test_munsell_specification_to_xy(self): """ - Tests :func:`colour.notation.munsell.munsell_specification_to_xy` + Test :func:`colour.notation.munsell.munsell_specification_to_xy` definition. """ for specification, xyY in MUNSELL_EVEN_SPECIFICATIONS: np.testing.assert_almost_equal( - munsell_specification_to_xy(specification), - xyY[0:2], - decimal=7) + munsell_specification_to_xy(specification), xyY[0:2], decimal=7 + ) for specification, xyY in MUNSELL_GREYS_SPECIFICATIONS: np.testing.assert_almost_equal( munsell_specification_to_xy(specification[0]), xyY[0:2], - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/phenomena/__init__.py b/colour/phenomena/__init__.py index a7d1140249..f080f2fffc 100644 --- a/colour/phenomena/__init__.py +++ b/colour/phenomena/__init__.py @@ -1,11 +1,13 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - -from .rayleigh import (scattering_cross_section, rayleigh_optical_depth, - rayleigh_scattering, sd_rayleigh_scattering) +from .rayleigh import ( + scattering_cross_section, + rayleigh_optical_depth, + rayleigh_scattering, + sd_rayleigh_scattering, +) __all__ = [ - 'scattering_cross_section', 'rayleigh_optical_depth', - 'rayleigh_scattering', 'sd_rayleigh_scattering' + "scattering_cross_section", + "rayleigh_optical_depth", + "rayleigh_scattering", + "sd_rayleigh_scattering", ] diff --git a/colour/phenomena/rayleigh.py b/colour/phenomena/rayleigh.py index aae5488270..ada4fe1c9d 100644 --- a/colour/phenomena/rayleigh.py +++ b/colour/phenomena/rayleigh.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Rayleigh Optical Depth - Scattering in the Atmosphere ===================================================== @@ -19,83 +18,82 @@ September 23, 2014, from http://en.wikipedia.org/wiki/Rayleigh_scattering """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.colorimetry import (SPECTRAL_SHAPE_DEFAULT, SpectralDistribution) +from colour.colorimetry import ( + SPECTRAL_SHAPE_DEFAULT, + SpectralDistribution, + SpectralShape, +) from colour.constants import CONSTANT_AVOGADRO -from colour.utilities import as_float_array, filter_kwargs +from colour.hints import Callable, FloatingOrArrayLike, FloatingOrNDArray +from colour.utilities import as_float, as_float_array, filter_kwargs -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'CONSTANT_STANDARD_AIR_TEMPERATURE', 'CONSTANT_STANDARD_CO2_CONCENTRATION', - 'CONSTANT_AVERAGE_PRESSURE_MEAN_SEA_LEVEL', 'CONSTANT_DEFAULT_LATITUDE', - 'CONSTANT_DEFAULT_ALTITUDE', 'air_refraction_index_Penndorf1957', - 'air_refraction_index_Edlen1966', 'air_refraction_index_Peck1972', - 'air_refraction_index_Bodhaine1999', 'N2_depolarisation', - 'O2_depolarisation', 'F_air_Penndorf1957', 'F_air_Young1981', - 'F_air_Bates1984', 'F_air_Bodhaine1999', 'molecular_density', - 'mean_molecular_weights', 'gravity_List1968', 'scattering_cross_section', - 'rayleigh_optical_depth', 'rayleigh_scattering' + "CONSTANT_STANDARD_AIR_TEMPERATURE", + "CONSTANT_STANDARD_CO2_CONCENTRATION", + "CONSTANT_AVERAGE_PRESSURE_MEAN_SEA_LEVEL", + "CONSTANT_DEFAULT_LATITUDE", + "CONSTANT_DEFAULT_ALTITUDE", + "air_refraction_index_Penndorf1957", + "air_refraction_index_Edlen1966", + "air_refraction_index_Peck1972", + "air_refraction_index_Bodhaine1999", + "N2_depolarisation", + "O2_depolarisation", + "F_air_Penndorf1957", + "F_air_Young1981", + "F_air_Bates1984", + "F_air_Bodhaine1999", + "molecular_density", + "mean_molecular_weights", + "gravity_List1968", + "scattering_cross_section", + "rayleigh_optical_depth", + "rayleigh_scattering", ] -CONSTANT_STANDARD_AIR_TEMPERATURE = 288.15 -""" -*Standard air* temperature :math:`T[K]` in kelvin degrees (:math:`15\\circ C`). +CONSTANT_STANDARD_AIR_TEMPERATURE: float = 288.15 +"""*Standard air* temperature :math:`T[K]` in kelvin degrees (:math:`15\\circ C`).""" -CONSTANT_STANDARD_AIR_TEMPERATURE : numeric -""" +CONSTANT_STANDARD_CO2_CONCENTRATION: float = 300 +"""*Standard air* :math:`CO_2` concentration in parts per million (ppm).""" -CONSTANT_STANDARD_CO2_CONCENTRATION = 300 -""" -*Standard air* :math:`CO_2` concentration in parts per million (ppm). +CONSTANT_AVERAGE_PRESSURE_MEAN_SEA_LEVEL: float = 101325 +"""*Standard air* average pressure :math:`Hg` at mean sea-level in pascal (Pa).""" -CONSTANT_STANDARD_CO2_CONCENTRATION : numeric -""" +CONSTANT_DEFAULT_LATITUDE: float = 0 +"""Default latitude in degrees (equator).""" -CONSTANT_AVERAGE_PRESSURE_MEAN_SEA_LEVEL = 101325 -""" -*Standard air* average pressure :math:`Hg` at mean sea-level in pascal (Pa). +CONSTANT_DEFAULT_ALTITUDE: float = 0 +"""Default altitude in meters (sea level).""" -CONSTANT_AVERAGE_PRESSURE_MEAN_SEA_LEVEL : numeric -""" -CONSTANT_DEFAULT_LATITUDE = 0 -""" -Default latitude in degrees (equator). - -CONSTANT_DEFAULT_LATITUDE : numeric -""" - -CONSTANT_DEFAULT_ALTITUDE = 0 -""" -Default altitude in meters (sea level). - -CONSTANT_DEFAULT_ALTITUDE : numeric -""" - - -def air_refraction_index_Penndorf1957(wavelength): +def air_refraction_index_Penndorf1957( + wavelength: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Returns the air refraction index :math:`n_s` from given wavelength + Return the air refraction index :math:`n_s` from given wavelength :math:`\\lambda` in micrometers (:math:`\\mu m`) using *Penndorf (1957)* method. Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` in micrometers (:math:`\\mu m`). Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Air refraction index :math:`n_s`. Examples @@ -113,20 +111,22 @@ def air_refraction_index_Penndorf1957(wavelength): return n -def air_refraction_index_Edlen1966(wavelength): +def air_refraction_index_Edlen1966( + wavelength: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Returns the air refraction index :math:`n_s` from given wavelength + Return the air refraction index :math:`n_s` from given wavelength :math:`\\lambda` in micrometers (:math:`\\mu m`) using *Edlen (1966)* method. Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` in micrometers (:math:`\\mu m`). Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Air refraction index :math:`n_s`. Examples @@ -144,20 +144,22 @@ def air_refraction_index_Edlen1966(wavelength): return n -def air_refraction_index_Peck1972(wavelength): +def air_refraction_index_Peck1972( + wavelength: FloatingOrArrayLike, +) -> FloatingOrNDArray: """ - Returns the air refraction index :math:`n_s` from given wavelength + Return the air refraction index :math:`n_s` from given wavelength :math:`\\lambda` in micrometers (:math:`\\mu m`) using *Peck and Reeder (1972)* method. Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` in micrometers (:math:`\\mu m`). Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Air refraction index :math:`n_s`. Examples @@ -168,8 +170,11 @@ def air_refraction_index_Peck1972(wavelength): wl = as_float_array(wavelength) - n = (8060.51 + 2480990 / (132.274 - wl ** - (-2)) + 17455.7 / (39.32957 - wl ** (-2))) + n = ( + 8060.51 + + 2480990 / (132.274 - wl ** (-2)) + + 17455.7 / (39.32957 - wl ** (-2)) + ) n /= 1.0e8 n += +1 @@ -177,22 +182,24 @@ def air_refraction_index_Peck1972(wavelength): def air_refraction_index_Bodhaine1999( - wavelength, CO2_concentration=CONSTANT_STANDARD_CO2_CONCENTRATION): + wavelength: FloatingOrArrayLike, + CO2_concentration: FloatingOrArrayLike = CONSTANT_STANDARD_CO2_CONCENTRATION, +) -> FloatingOrNDArray: """ - Returns the air refraction index :math:`n_s` from given wavelength + Return the air refraction index :math:`n_s` from given wavelength :math:`\\lambda` in micrometers (:math:`\\mu m`) using *Bodhaine, Wood, Dutton and Slusser (1999)* method. Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` in micrometers (:math:`\\mu m`). - CO2_concentration : numeric or array_like + CO2_concentration :math:`CO_2` concentration in parts per million (ppm). Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Air refraction index :math:`n_s`. Examples @@ -204,25 +211,26 @@ def air_refraction_index_Bodhaine1999( wl = as_float_array(wavelength) CO2_c = as_float_array(CO2_concentration) - n = ((1 + 0.54 * ((CO2_c * 1e-6) - 300e-6)) * - (air_refraction_index_Peck1972(wl) - 1) + 1) + n = (1 + 0.54 * ((CO2_c * 1e-6) - 300e-6)) * ( + air_refraction_index_Peck1972(wl) - 1 + ) + 1 - return n + return as_float(n) -def N2_depolarisation(wavelength): +def N2_depolarisation(wavelength: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the depolarisation of nitrogen :math:`N_2` as function of + Return the depolarisation of nitrogen :math:`N_2` as function of wavelength :math:`\\lambda` in micrometers (:math:`\\mu m`). Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` in micrometers (:math:`\\mu m`). Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Nitrogen :math:`N_2` depolarisation. Examples @@ -233,24 +241,24 @@ def N2_depolarisation(wavelength): wl = as_float_array(wavelength) - N2 = 1.034 + 3.17 * 1.0e-4 * (1 / wl ** 2) + N2 = 1.034 + 3.17 * 1.0e-4 * (1 / wl**2) return N2 -def O2_depolarisation(wavelength): +def O2_depolarisation(wavelength: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns the depolarisation of oxygen :math:`O_2` as function of + Return the depolarisation of oxygen :math:`O_2` as function of wavelength :math:`\\lambda` in micrometers (:math:`\\mu m`). Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` in micrometers (:math:`\\mu m`). Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Oxygen :math:`O_2` depolarisation. Examples @@ -261,25 +269,26 @@ def O2_depolarisation(wavelength): wl = as_float_array(wavelength) - O2 = (1.096 + 1.385 * 1.0e-3 * (1 / wl ** 2) + - 1.448 * 1.0e-4 * (1 / wl ** 4)) + O2 = ( + 1.096 + 1.385 * 1.0e-3 * (1 / wl**2) + 1.448 * 1.0e-4 * (1 / wl**4) + ) return O2 -def F_air_Penndorf1957(wavelength): +def F_air_Penndorf1957(wavelength: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns :math:`(6+3_p)/(6-7_p)`, the depolarisation term :math:`F(air)` or + Return :math:`(6+3_p)/(6-7_p)`, the depolarisation term :math:`F(air)` or *King Factor* using *Penndorf (1957)* method. Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` in micrometers (:math:`\\mu m`). Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Air depolarisation. Notes @@ -291,27 +300,27 @@ def F_air_Penndorf1957(wavelength): Examples -------- >>> F_air_Penndorf1957(0.555) - array(1.0608) + 1.0608 """ wl = as_float_array(wavelength) - return np.resize(np.array([1.0608]), wl.shape) + return as_float(np.resize(np.array([1.0608]), wl.shape)) -def F_air_Young1981(wavelength): +def F_air_Young1981(wavelength: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns :math:`(6+3_p)/(6-7_p)`, the depolarisation term :math:`F(air)` or + Return :math:`(6+3_p)/(6-7_p)`, the depolarisation term :math:`F(air)` or *King Factor* using *Young (1981)* method. Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` in micrometers (:math:`\\mu m`). Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Air depolarisation. Notes @@ -323,28 +332,28 @@ def F_air_Young1981(wavelength): Examples -------- >>> F_air_Young1981(0.555) - array(1.048) + 1.048 """ wl = as_float_array(wavelength) - return np.resize(np.array([1.0480]), wl.shape) + return as_float(np.resize(np.array([1.0480]), wl.shape)) -def F_air_Bates1984(wavelength): +def F_air_Bates1984(wavelength: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Returns :math:`(6+3_p)/(6-7_p)`, the depolarisation term :math:`F(air)` or + Return :math:`(6+3_p)/(6-7_p)`, the depolarisation term :math:`F(air)` or *King Factor* as function of wavelength :math:`\\lambda` in micrometers (:math:`\\mu m`) using *Bates (1984)* method. Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` in micrometers (:math:`\\mu m`). Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Air depolarisation. Examples @@ -358,30 +367,33 @@ def F_air_Bates1984(wavelength): Ar = 1.00 CO2 = 1.15 - F_air = ( - (78.084 * N2 + 20.946 * O2 + CO2 + Ar) / (78.084 + 20.946 + Ar + CO2)) + F_air = (78.084 * N2 + 20.946 * O2 + CO2 + Ar) / ( + 78.084 + 20.946 + Ar + CO2 + ) return F_air -def F_air_Bodhaine1999(wavelength, - CO2_concentration=CONSTANT_STANDARD_CO2_CONCENTRATION): +def F_air_Bodhaine1999( + wavelength: FloatingOrArrayLike, + CO2_concentration: FloatingOrArrayLike = CONSTANT_STANDARD_CO2_CONCENTRATION, +) -> FloatingOrNDArray: """ - Returns :math:`(6+3_p)/(6-7_p)`, the depolarisation term :math:`F(air)` or + Return :math:`(6+3_p)/(6-7_p)`, the depolarisation term :math:`F(air)` or *King Factor* as function of wavelength :math:`\\lambda` in micrometers (:math:`\\mu m`) and :math:`CO_2` concentration in parts per million (ppm) using *Bodhaine, Wood, Dutton and Slusser (1999)* method. Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` in micrometers (:math:`\\mu m`). - CO2_concentration : numeric or array_like, optional + CO2_concentration :math:`CO_2` concentration in parts per million (ppm). Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Air depolarisation. Examples @@ -394,28 +406,31 @@ def F_air_Bodhaine1999(wavelength, N2 = N2_depolarisation(wavelength) CO2_c = as_float_array(CO2_concentration) - F_air = ((78.084 * N2 + 20.946 * O2 + 0.934 * 1 + CO2_c * 1.15) / - (78.084 + 20.946 + 0.934 + CO2_c)) + F_air = (78.084 * N2 + 20.946 * O2 + 0.934 * 1 + CO2_c * 1.15) / ( + 78.084 + 20.946 + 0.934 + CO2_c + ) return F_air -def molecular_density(temperature=CONSTANT_STANDARD_AIR_TEMPERATURE, - avogadro_constant=CONSTANT_AVOGADRO): +def molecular_density( + temperature: FloatingOrArrayLike = CONSTANT_STANDARD_AIR_TEMPERATURE, + avogadro_constant: FloatingOrArrayLike = CONSTANT_AVOGADRO, +) -> FloatingOrNDArray: """ - Returns the molecular density :math:`N_s` (molecules :math:`cm^{-3}`) + Return the molecular density :math:`N_s` (molecules :math:`cm^{-3}`) as function of air temperature :math:`T[K]` in kelvin degrees. Parameters ---------- - temperature : numeric or array_like, optional + temperature Air temperature :math:`T[K]` in kelvin degrees. - avogadro_constant : numeric or array_like, optional + avogadro_constant *Avogadro*'s number (molecules :math:`mol^{-1}`). Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Molecular density :math:`N_s` (molecules :math:`cm^{-3}`). Notes @@ -434,6 +449,7 @@ def molecular_density(temperature=CONSTANT_STANDARD_AIR_TEMPERATURE, """ T = as_float_array(temperature) + avogadro_constant = as_float_array(avogadro_constant) N_s = (avogadro_constant / 22.4141) * (273.15 / T) * (1 / 1000) @@ -441,19 +457,20 @@ def molecular_density(temperature=CONSTANT_STANDARD_AIR_TEMPERATURE, def mean_molecular_weights( - CO2_concentration=CONSTANT_STANDARD_CO2_CONCENTRATION): + CO2_concentration: FloatingOrArrayLike = CONSTANT_STANDARD_CO2_CONCENTRATION, +) -> FloatingOrNDArray: """ - Returns the mean molecular weights :math:`m_a` for dry air as function of + Return the mean molecular weights :math:`m_a` for dry air as function of :math:`CO_2` concentration in parts per million (ppm). Parameters ---------- - CO2_concentration : numeric or array_like, optional + CO2_concentration :math:`CO_2` concentration in parts per million (ppm). Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Mean molecular weights :math:`m_a` for dry air. Examples @@ -462,29 +479,33 @@ def mean_molecular_weights( 28.9640166... """ + CO2_concentration = as_float_array(CO2_concentration) + CO2_c = CO2_concentration * 1.0e-6 m_a = 15.0556 * CO2_c + 28.9595 return m_a -def gravity_List1968(latitude=CONSTANT_DEFAULT_LATITUDE, - altitude=CONSTANT_DEFAULT_ALTITUDE): +def gravity_List1968( + latitude: FloatingOrArrayLike = CONSTANT_DEFAULT_LATITUDE, + altitude: FloatingOrArrayLike = CONSTANT_DEFAULT_ALTITUDE, +) -> FloatingOrNDArray: """ - Returns the gravity :math:`g` in :math:`cm/s_2` (gal) representative of the + Return the gravity :math:`g` in :math:`cm/s_2` (gal) representative of the mass-weighted column of air molecules above the site of given latitude and altitude using *List (1968)* method. Parameters ---------- - latitude : numeric or array_like, optional + latitude Latitude of the site in degrees. - altitude : numeric or array_like, optional + altitude Altitude of the site in meters. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Gravity :math:`g` in :math:`cm/s_2` (gal). Examples @@ -506,47 +527,51 @@ def gravity_List1968(latitude=CONSTANT_DEFAULT_LATITUDE, cos2phi = np.cos(2 * np.radians(latitude)) # Sea level acceleration of gravity. - g0 = 980.6160 * (1 - 0.0026373 * cos2phi + 0.0000059 * cos2phi ** 2) + g0 = 980.6160 * (1 - 0.0026373 * cos2phi + 0.0000059 * cos2phi**2) - g = (g0 - (3.085462e-4 + 2.27e-7 * cos2phi) * altitude + - (7.254e-11 + 1.0e-13 * cos2phi) * altitude ** 2 - - (1.517e-17 + 6e-20 * cos2phi) * altitude ** 3) + g = ( + g0 + - (3.085462e-4 + 2.27e-7 * cos2phi) * altitude + + (7.254e-11 + 1.0e-13 * cos2phi) * altitude**2 + - (1.517e-17 + 6e-20 * cos2phi) * altitude**3 + ) return g def scattering_cross_section( - wavelength, - CO2_concentration=CONSTANT_STANDARD_CO2_CONCENTRATION, - temperature=CONSTANT_STANDARD_AIR_TEMPERATURE, - avogadro_constant=CONSTANT_AVOGADRO, - n_s=air_refraction_index_Bodhaine1999, - F_air=F_air_Bodhaine1999): + wavelength: FloatingOrArrayLike, + CO2_concentration: FloatingOrArrayLike = CONSTANT_STANDARD_CO2_CONCENTRATION, + temperature: FloatingOrArrayLike = CONSTANT_STANDARD_AIR_TEMPERATURE, + avogadro_constant: FloatingOrArrayLike = CONSTANT_AVOGADRO, + n_s_function: Callable = air_refraction_index_Bodhaine1999, + F_air_function: Callable = F_air_Bodhaine1999, +) -> FloatingOrNDArray: """ - Returns the scattering cross section per molecule :math:`\\sigma` of dry + Return the scattering cross section per molecule :math:`\\sigma` of dry air as function of wavelength :math:`\\lambda` in centimeters (cm) using given :math:`CO_2` concentration in parts per million (ppm) and temperature :math:`T[K]` in kelvin degrees following *Van de Hulst (1957)* method. Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` in centimeters (cm). - CO2_concentration : numeric or array_like, optional + CO2_concentration :math:`CO_2` concentration in parts per million (ppm). - temperature : numeric or array_like, optional + temperature Air temperature :math:`T[K]` in kelvin degrees. - avogadro_constant : numeric or array_like, optional + avogadro_constant *Avogadro*'s number (molecules :math:`mol^{-1}`). - n_s : object + n_s_function Air refraction index :math:`n_s` computation method. - F_air : object + F_air_function :math:`(6+3_p)/(6-7_p)`, the depolarisation term :math:`F(air)` or *King Factor* computation method. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Scattering cross section per molecule :math:`\\sigma` of dry air. Warnings @@ -571,59 +596,67 @@ def scattering_cross_section( wl_micrometers = wl * 10e3 - n_s = n_s(wl_micrometers) - # n_s = n_s(**filter_kwargs( - # n_s, wavelength=wl_micrometers, CO2_concentration=CO2_c)) N_s = molecular_density(temperature, avogadro_constant) - F_air = F_air(**filter_kwargs( - F_air, wavelength=wl_micrometers, CO2_concentration=CO2_c)) - - sigma = (24 * np.pi ** 3 * (n_s ** 2 - 1) ** 2 / (wl ** 4 * N_s ** 2 * - (n_s ** 2 + 2) ** 2)) + n_s = n_s_function(wl_micrometers) + # n_s = n_s_function(**filter_kwargs( + # n_s_function, wavelength=wl_micrometers, CO2_concentration=CO2_c)) + F_air = F_air_function( + **filter_kwargs( + F_air_function, wavelength=wl_micrometers, CO2_concentration=CO2_c + ) + ) + + sigma = ( + 24 + * np.pi**3 + * (n_s**2 - 1) ** 2 + / (wl**4 * N_s**2 * (n_s**2 + 2) ** 2) + ) sigma *= F_air return sigma def rayleigh_optical_depth( - wavelength, - CO2_concentration=CONSTANT_STANDARD_CO2_CONCENTRATION, - temperature=CONSTANT_STANDARD_AIR_TEMPERATURE, - pressure=CONSTANT_AVERAGE_PRESSURE_MEAN_SEA_LEVEL, - latitude=CONSTANT_DEFAULT_LATITUDE, - altitude=CONSTANT_DEFAULT_ALTITUDE, - avogadro_constant=CONSTANT_AVOGADRO, - n_s=air_refraction_index_Bodhaine1999, - F_air=F_air_Bodhaine1999): + wavelength: FloatingOrArrayLike, + CO2_concentration: FloatingOrArrayLike = CONSTANT_STANDARD_CO2_CONCENTRATION, + temperature: FloatingOrArrayLike = CONSTANT_STANDARD_AIR_TEMPERATURE, + pressure: FloatingOrArrayLike = CONSTANT_AVERAGE_PRESSURE_MEAN_SEA_LEVEL, + latitude: FloatingOrArrayLike = CONSTANT_DEFAULT_LATITUDE, + altitude: FloatingOrArrayLike = CONSTANT_DEFAULT_ALTITUDE, + avogadro_constant: FloatingOrArrayLike = CONSTANT_AVOGADRO, + n_s_function: Callable = air_refraction_index_Bodhaine1999, + F_air_function: Callable = F_air_Bodhaine1999, +) -> FloatingOrNDArray: """ - Returns the *Rayleigh* optical depth :math:`T_r(\\lambda)` as function of + Return the *Rayleigh* optical depth :math:`T_r(\\lambda)` as function of wavelength :math:`\\lambda` in centimeters (cm). Parameters ---------- - wavelength : numeric or array_like + wavelength Wavelength :math:`\\lambda` in centimeters (cm). - CO2_concentration : numeric or array_like, optional + CO2_concentration :math:`CO_2` concentration in parts per million (ppm). - temperature : numeric or array_like, optional + temperature Air temperature :math:`T[K]` in kelvin degrees. - pressure : numeric or array_like + pressure Surface pressure :math:`P` of the measurement site. - latitude : numeric or array_like, optional + latitude Latitude of the site in degrees. - altitude : numeric or array_like, optional + altitude Altitude of the site in meters. - avogadro_constant : numeric or array_like, optional + avogadro_constant *Avogadro*'s number (molecules :math:`mol^{-1}`). - n_s : object + n_s_function Air refraction index :math:`n_s` computation method. - F_air : object + F_air_function :math:`(6+3_p)/(6-7_p)`, the depolarisation term :math:`F(air)` or *King Factor* computation method. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` *Rayleigh* optical depth :math:`T_r(\\lambda)`. Warnings @@ -644,64 +677,72 @@ def rayleigh_optical_depth( wavelength = as_float_array(wavelength) CO2_c = as_float_array(CO2_concentration) + pressure = as_float_array(pressure) latitude = as_float_array(latitude) altitude = as_float_array(altitude) # Conversion from pascal to dyne/cm2. P = as_float_array(pressure * 10) - sigma = scattering_cross_section(wavelength, CO2_c, temperature, - avogadro_constant, n_s, F_air) + sigma = scattering_cross_section( + wavelength, + CO2_c, + temperature, + avogadro_constant, + n_s_function, + F_air_function, + ) m_a = mean_molecular_weights(CO2_c) g = gravity_List1968(latitude, altitude) T_R = sigma * (P * avogadro_constant) / (m_a * g) - return T_R + return as_float(T_R) rayleigh_scattering = rayleigh_optical_depth def sd_rayleigh_scattering( - shape=SPECTRAL_SHAPE_DEFAULT, - CO2_concentration=CONSTANT_STANDARD_CO2_CONCENTRATION, - temperature=CONSTANT_STANDARD_AIR_TEMPERATURE, - pressure=CONSTANT_AVERAGE_PRESSURE_MEAN_SEA_LEVEL, - latitude=CONSTANT_DEFAULT_LATITUDE, - altitude=CONSTANT_DEFAULT_ALTITUDE, - avogadro_constant=CONSTANT_AVOGADRO, - n_s=air_refraction_index_Bodhaine1999, - F_air=F_air_Bodhaine1999): + shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, + CO2_concentration: FloatingOrArrayLike = CONSTANT_STANDARD_CO2_CONCENTRATION, + temperature: FloatingOrArrayLike = CONSTANT_STANDARD_AIR_TEMPERATURE, + pressure: FloatingOrArrayLike = CONSTANT_AVERAGE_PRESSURE_MEAN_SEA_LEVEL, + latitude: FloatingOrArrayLike = CONSTANT_DEFAULT_LATITUDE, + altitude: FloatingOrArrayLike = CONSTANT_DEFAULT_ALTITUDE, + avogadro_constant: FloatingOrArrayLike = CONSTANT_AVOGADRO, + n_s_function: Callable = air_refraction_index_Bodhaine1999, + F_air_function: Callable = F_air_Bodhaine1999, +) -> SpectralDistribution: """ - Returns the *Rayleigh* spectral distribution for given spectral shape. + Return the *Rayleigh* spectral distribution for given spectral shape. Parameters ---------- - shape : SpectralShape, optional + shape Spectral shape used to create the *Rayleigh* scattering spectral distribution. - CO2_concentration : numeric or array_like, optional + CO2_concentration :math:`CO_2` concentration in parts per million (ppm). - temperature : numeric or array_like, optional + temperature Air temperature :math:`T[K]` in kelvin degrees. - pressure : numeric or array_like + pressure Surface pressure :math:`P` of the measurement site. - latitude : numeric or array_like, optional + latitude Latitude of the site in degrees. - altitude : numeric or array_like, optional + altitude Altitude of the site in meters. - avogadro_constant : numeric or array_like, optional + avogadro_constant *Avogadro*'s number (molecules :math:`mol^{-1}`). - n_s : object + n_s_function Air refraction index :math:`n_s` computation method. - F_air : object + F_air_function :math:`(6+3_p)/(6-7_p)`, the depolarisation term :math:`F(air)` or *King Factor* computation method. Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` *Rayleigh* optical depth spectral distribution. References @@ -1142,13 +1183,24 @@ def sd_rayleigh_scattering( wavelengths = shape.range() return SpectralDistribution( - data=dict( - zip( - wavelengths, - rayleigh_optical_depth(wavelengths * 10e-8, CO2_concentration, - temperature, pressure, latitude, - altitude, avogadro_constant, n_s, - F_air))), - name=('Rayleigh Scattering - {0} ppm, {1} K, {2} Pa, {3} Degrees, ' - '{4} m').format(CO2_concentration, temperature, pressure, - latitude, altitude)) + rayleigh_optical_depth( + wavelengths * 10e-8, + CO2_concentration, + temperature, + pressure, + latitude, + altitude, + avogadro_constant, + n_s_function, + F_air_function, + ), + wavelengths, + name=( + "Rayleigh Scattering - " + f"{CO2_concentration!r} ppm, " + f"{temperature!r} K, " + f"{pressure!r} Pa, " + f"{latitude!r} Degrees, " + f"{altitude!r} m" + ), + ) diff --git a/colour/phenomena/tests/__init__.py b/colour/phenomena/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/phenomena/tests/__init__.py +++ b/colour/phenomena/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/phenomena/tests/test_rayleigh.py b/colour/phenomena/tests/test_rayleigh.py index 283c2af5a3..22c12193c5 100644 --- a/colour/phenomena/tests/test_rayleigh.py +++ b/colour/phenomena/tests/test_rayleigh.py @@ -1,43 +1,62 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.phenomena.rayleigh` module. -""" +"""Defines the unit tests for the :mod:`colour.phenomena.rayleigh` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import unittest from itertools import permutations +from colour.hints import Tuple from colour.phenomena.rayleigh import ( - air_refraction_index_Penndorf1957, air_refraction_index_Edlen1966, - air_refraction_index_Peck1972, air_refraction_index_Bodhaine1999, - N2_depolarisation, O2_depolarisation, F_air_Penndorf1957, F_air_Young1981, - F_air_Bates1984, F_air_Bodhaine1999, molecular_density, - mean_molecular_weights, gravity_List1968) -from colour.phenomena import (scattering_cross_section, rayleigh_optical_depth, - sd_rayleigh_scattering) + air_refraction_index_Penndorf1957, + air_refraction_index_Edlen1966, + air_refraction_index_Peck1972, + air_refraction_index_Bodhaine1999, + N2_depolarisation, + O2_depolarisation, + F_air_Penndorf1957, + F_air_Young1981, + F_air_Bates1984, + F_air_Bodhaine1999, + molecular_density, + mean_molecular_weights, + gravity_List1968, +) +from colour.phenomena import ( + scattering_cross_section, + rayleigh_optical_depth, + sd_rayleigh_scattering, +) from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'DATA_SD_RAYLEIGH_SCATTERING', 'TestAirRefractionIndexPenndorf1957', - 'TestAirRefractionIndexEdlen1966', 'TestAirRefractionIndexPeck1972', - 'TestAirRefractionIndexBodhaine1999', 'TestN2Depolarisation', - 'TestO2Depolarisation', 'TestF_airPenndorf1957', 'TestF_airYoung1981', - 'TestF_airBates1984', 'TestF_airBodhaine1999', 'TestMolecularDensity', - 'TestMeanMolecularWeights', 'TestGravityList1968', - 'TestScatteringCrossSection', 'TestRayleighOpticalDepth', - 'TestSdRayleighScattering' + "DATA_SD_RAYLEIGH_SCATTERING", + "TestAirRefractionIndexPenndorf1957", + "TestAirRefractionIndexEdlen1966", + "TestAirRefractionIndexPeck1972", + "TestAirRefractionIndexBodhaine1999", + "TestN2Depolarisation", + "TestO2Depolarisation", + "TestF_airPenndorf1957", + "TestF_airYoung1981", + "TestF_airBates1984", + "TestF_airBodhaine1999", + "TestMolecularDensity", + "TestMeanMolecularWeights", + "TestGravityList1968", + "TestScatteringCrossSection", + "TestRayleighOpticalDepth", + "TestSdRayleighScattering", ] -DATA_SD_RAYLEIGH_SCATTERING = ( +DATA_SD_RAYLEIGH_SCATTERING: Tuple = ( 0.59910134, 0.59217069, 0.58534101, @@ -458,39 +477,43 @@ 0.02565374, 0.02552084, 0.02538880, - 0.02525761) # yapf: disable + 0.02525761, +) class TestAirRefractionIndexPenndorf1957(unittest.TestCase): """ - Defines :func:`colour.phenomena.rayleigh.\ + Define :func:`colour.phenomena.rayleigh.\ air_refraction_index_Penndorf1957` definition unit tests methods. """ def test_air_refraction_index_Penndorf1957(self): """ - Tests :func:`colour.phenomena.rayleigh.\ + Test :func:`colour.phenomena.rayleigh.\ air_refraction_index_Penndorf1957` definition. """ self.assertAlmostEqual( air_refraction_index_Penndorf1957(0.360), 1.000285316795146, - places=10) + places=10, + ) self.assertAlmostEqual( air_refraction_index_Penndorf1957(0.555), 1.000277729533864, - places=10) + places=10, + ) self.assertAlmostEqual( air_refraction_index_Penndorf1957(0.830), 1.000274856640486, - places=10) + places=10, + ) def test_n_dimensional_air_refraction_index_Penndorf1957(self): """ - Tests :func:`colour.phenomena.rayleigh.\ + Test :func:`colour.phenomena.rayleigh.\ air_refraction_index_Penndorf1957` definition n-dimensional arrays support. """ @@ -500,59 +523,60 @@ def test_n_dimensional_air_refraction_index_Penndorf1957(self): wl = np.tile(wl, 6) n = np.tile(n, 6) np.testing.assert_almost_equal( - air_refraction_index_Penndorf1957(wl), n, decimal=7) + air_refraction_index_Penndorf1957(wl), n, decimal=7 + ) wl = np.reshape(wl, (2, 3)) n = np.reshape(n, (2, 3)) np.testing.assert_almost_equal( - air_refraction_index_Penndorf1957(wl), n, decimal=7) + air_refraction_index_Penndorf1957(wl), n, decimal=7 + ) wl = np.reshape(wl, (2, 3, 1)) n = np.reshape(n, (2, 3, 1)) np.testing.assert_almost_equal( - air_refraction_index_Penndorf1957(wl), n, decimal=7) + air_refraction_index_Penndorf1957(wl), n, decimal=7 + ) @ignore_numpy_errors def test_nan_air_refraction_index_Penndorf1957(self): """ - Tests :func:`colour.phenomena.rayleigh.\ + Test :func:`colour.phenomena.rayleigh.\ air_refraction_index_Penndorf1957` definition nan support. """ air_refraction_index_Penndorf1957( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestAirRefractionIndexEdlen1966(unittest.TestCase): """ - Defines :func:`colour.phenomena.rayleigh.air_refraction_index_Edlen1966` + Define :func:`colour.phenomena.rayleigh.air_refraction_index_Edlen1966` definition unit tests methods. """ def test_air_refraction_index_Edlen1966(self): """ - Tests :func:`colour.phenomena.\ + Test :func:`colour.phenomena.\ rayleigh.air_refraction_index_Edlen1966` definition. """ self.assertAlmostEqual( - air_refraction_index_Edlen1966(0.360), - 1.000285308809879, - places=10) + air_refraction_index_Edlen1966(0.360), 1.000285308809879, places=10 + ) self.assertAlmostEqual( - air_refraction_index_Edlen1966(0.555), - 1.000277727690364, - places=10) + air_refraction_index_Edlen1966(0.555), 1.000277727690364, places=10 + ) self.assertAlmostEqual( - air_refraction_index_Edlen1966(0.830), - 1.000274862218835, - places=10) + air_refraction_index_Edlen1966(0.830), 1.000274862218835, places=10 + ) def test_n_dimensional_air_refraction_index_Edlen1966(self): """ - Tests :func:`colour.phenomena.rayleigh.\ + Test :func:`colour.phenomena.rayleigh.\ air_refraction_index_Edlen1966` definition n-dimensional arrays support. """ @@ -562,53 +586,60 @@ def test_n_dimensional_air_refraction_index_Edlen1966(self): wl = np.tile(wl, 6) n = np.tile(n, 6) np.testing.assert_almost_equal( - air_refraction_index_Edlen1966(wl), n, decimal=7) + air_refraction_index_Edlen1966(wl), n, decimal=7 + ) wl = np.reshape(wl, (2, 3)) n = np.reshape(n, (2, 3)) np.testing.assert_almost_equal( - air_refraction_index_Edlen1966(wl), n, decimal=7) + air_refraction_index_Edlen1966(wl), n, decimal=7 + ) wl = np.reshape(wl, (2, 3, 1)) n = np.reshape(n, (2, 3, 1)) np.testing.assert_almost_equal( - air_refraction_index_Edlen1966(wl), n, decimal=7) + air_refraction_index_Edlen1966(wl), n, decimal=7 + ) @ignore_numpy_errors def test_nan_air_refraction_index_Edlen1966(self): """ - Tests :func:`colour.phenomena.rayleigh.\ + Test :func:`colour.phenomena.rayleigh.\ air_refraction_index_Edlen1966` definition nan support. """ air_refraction_index_Edlen1966( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestAirRefractionIndexPeck1972(unittest.TestCase): """ - Defines :func:`colour.phenomena.rayleigh.air_refraction_index_Peck1972` + Define :func:`colour.phenomena.rayleigh.air_refraction_index_Peck1972` definition unit tests methods. """ def test_air_refraction_index_Peck1972(self): """ - Tests :func:`colour.phenomena.rayleigh.air_refraction_index_Peck1972` + Test :func:`colour.phenomena.rayleigh.air_refraction_index_Peck1972` definition. """ self.assertAlmostEqual( - air_refraction_index_Peck1972(0.360), 1.000285310285056, places=10) + air_refraction_index_Peck1972(0.360), 1.000285310285056, places=10 + ) self.assertAlmostEqual( - air_refraction_index_Peck1972(0.555), 1.000277726541484, places=10) + air_refraction_index_Peck1972(0.555), 1.000277726541484, places=10 + ) self.assertAlmostEqual( - air_refraction_index_Peck1972(0.830), 1.000274859144804, places=10) + air_refraction_index_Peck1972(0.830), 1.000274859144804, places=10 + ) def test_n_dimensional_air_refraction_index_Peck1972(self): """ - Tests :func:`colour.phenomena.rayleigh.air_refraction_index_Peck1972` + Test :func:`colour.phenomena.rayleigh.air_refraction_index_Peck1972` definition n-dimensional arrays support. """ @@ -618,74 +649,84 @@ def test_n_dimensional_air_refraction_index_Peck1972(self): wl = np.tile(wl, 6) n = np.tile(n, 6) np.testing.assert_almost_equal( - air_refraction_index_Peck1972(wl), n, decimal=7) + air_refraction_index_Peck1972(wl), n, decimal=7 + ) wl = np.reshape(wl, (2, 3)) n = np.reshape(n, (2, 3)) np.testing.assert_almost_equal( - air_refraction_index_Peck1972(wl), n, decimal=7) + air_refraction_index_Peck1972(wl), n, decimal=7 + ) wl = np.reshape(wl, (2, 3, 1)) n = np.reshape(n, (2, 3, 1)) np.testing.assert_almost_equal( - air_refraction_index_Peck1972(wl), n, decimal=7) + air_refraction_index_Peck1972(wl), n, decimal=7 + ) @ignore_numpy_errors def test_nan_air_refraction_index_Peck1972(self): """ - Tests :func:`colour.phenomena.rayleigh.air_refraction_index_Peck1972` + Test :func:`colour.phenomena.rayleigh.air_refraction_index_Peck1972` definition nan support. """ air_refraction_index_Peck1972( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestAirRefractionIndexBodhaine1999(unittest.TestCase): """ - Defines :func:`colour.phenomena.rayleigh.\ + Define :func:`colour.phenomena.rayleigh.\ air_refraction_index_Bodhaine1999` definition unit tests methods. """ def test_air_refraction_index_Bodhaine1999(self): """ - Tests :func:`colour.phenomena.rayleigh.\ + Test :func:`colour.phenomena.rayleigh.\ air_refraction_index_Bodhaine1999` definition. """ self.assertAlmostEqual( air_refraction_index_Bodhaine1999(0.360), 1.000285310285056, - places=10) + places=10, + ) self.assertAlmostEqual( air_refraction_index_Bodhaine1999(0.555), 1.000277726541484, - places=10) + places=10, + ) self.assertAlmostEqual( air_refraction_index_Bodhaine1999(0.830), 1.000274859144804, - places=10) + places=10, + ) self.assertAlmostEqual( air_refraction_index_Bodhaine1999(0.360, 0), 1.000285264064789, - places=10) + places=10, + ) self.assertAlmostEqual( air_refraction_index_Bodhaine1999(0.555, 360), 1.000277735539824, - places=10) + places=10, + ) self.assertAlmostEqual( air_refraction_index_Bodhaine1999(0.830, 620), 1.000274906640464, - places=10) + places=10, + ) def test_n_dimensional_air_refraction_index_Bodhaine1999(self): """ - Tests :func:`colour.phenomena.rayleigh.\ + Test :func:`colour.phenomena.rayleigh.\ air_refraction_index_Bodhaine1999` definition n-dimensional arrays support. """ @@ -695,22 +736,25 @@ def test_n_dimensional_air_refraction_index_Bodhaine1999(self): wl = np.tile(wl, 6) n = np.tile(n, 6) np.testing.assert_almost_equal( - air_refraction_index_Bodhaine1999(wl), n, decimal=7) + air_refraction_index_Bodhaine1999(wl), n, decimal=7 + ) wl = np.reshape(wl, (2, 3)) n = np.reshape(n, (2, 3)) np.testing.assert_almost_equal( - air_refraction_index_Bodhaine1999(wl), n, decimal=7) + air_refraction_index_Bodhaine1999(wl), n, decimal=7 + ) wl = np.reshape(wl, (2, 3, 1)) n = np.reshape(n, (2, 3, 1)) np.testing.assert_almost_equal( - air_refraction_index_Bodhaine1999(wl), n, decimal=7) + air_refraction_index_Bodhaine1999(wl), n, decimal=7 + ) @ignore_numpy_errors def test_nan_air_refraction_index_Bodhaine1999(self): """ - Tests :func:`colour.phenomena.rayleigh.\ + Test :func:`colour.phenomena.rayleigh.\ air_refraction_index_Bodhaine1999` definition nan support. """ @@ -724,27 +768,28 @@ def test_nan_air_refraction_index_Bodhaine1999(self): class TestN2Depolarisation(unittest.TestCase): """ - Defines :func:`colour.phenomena.rayleigh.N2_depolarisation` definition + Define :func:`colour.phenomena.rayleigh.N2_depolarisation` definition unit tests methods. """ def test_N2_depolarisation(self): - """ - Tests :func:`colour.phenomena.rayleigh.N2_depolarisation` definition. - """ + """Test :func:`colour.phenomena.rayleigh.N2_depolarisation` definition.""" self.assertAlmostEqual( - N2_depolarisation(0.360), 1.036445987654321, places=7) + N2_depolarisation(0.360), 1.036445987654321, places=7 + ) self.assertAlmostEqual( - N2_depolarisation(0.555), 1.035029137245354, places=7) + N2_depolarisation(0.555), 1.035029137245354, places=7 + ) self.assertAlmostEqual( - N2_depolarisation(0.830), 1.034460153868486, places=7) + N2_depolarisation(0.830), 1.034460153868486, places=7 + ) def test_n_dimensional_N2_depolarisation(self): """ - Tests :func:`colour.phenomena.rayleigh.N2_depolarisation` + Test :func:`colour.phenomena.rayleigh.N2_depolarisation` definition n-dimensional arrays support. """ @@ -766,7 +811,7 @@ def test_n_dimensional_N2_depolarisation(self): @ignore_numpy_errors def test_nan_N2_depolarisation(self): """ - Tests :func:`colour.phenomena.rayleigh.N2_depolarisation` definition + Test :func:`colour.phenomena.rayleigh.N2_depolarisation` definition nan support. """ @@ -775,27 +820,28 @@ def test_nan_N2_depolarisation(self): class TestO2Depolarisation(unittest.TestCase): """ - Defines :func:`colour.phenomena.rayleigh.O2_depolarisation` definition + Define :func:`colour.phenomena.rayleigh.O2_depolarisation` definition unit tests methods. """ def test_O2_depolarisation(self): - """ - Tests :func:`colour.phenomena.rayleigh.O2_depolarisation` definition. - """ + """Test :func:`colour.phenomena.rayleigh.O2_depolarisation` definition.""" self.assertAlmostEqual( - O2_depolarisation(0.360), 1.115307746532541, places=7) + O2_depolarisation(0.360), 1.115307746532541, places=7 + ) self.assertAlmostEqual( - O2_depolarisation(0.555), 1.102022536201071, places=7) + O2_depolarisation(0.555), 1.102022536201071, places=7 + ) self.assertAlmostEqual( - O2_depolarisation(0.830), 1.098315561269013, places=7) + O2_depolarisation(0.830), 1.098315561269013, places=7 + ) def test_n_dimensional_O2_depolarisation(self): """ - Tests :func:`colour.phenomena.rayleigh.O2_depolarisation` definition + Test :func:`colour.phenomena.rayleigh.O2_depolarisation` definition n-dimensional arrays support. """ @@ -817,7 +863,7 @@ def test_n_dimensional_O2_depolarisation(self): @ignore_numpy_errors def test_nan_O2_depolarisation(self): """ - Tests :func:`colour.phenomena.rayleigh.O2_depolarisation` definition + Test :func:`colour.phenomena.rayleigh.O2_depolarisation` definition nan support. """ @@ -826,13 +872,13 @@ def test_nan_O2_depolarisation(self): class TestF_airPenndorf1957(unittest.TestCase): """ - Defines :func:`colour.phenomena.rayleigh.F_air_Penndorf1957` definition + Define :func:`colour.phenomena.rayleigh.F_air_Penndorf1957` definition unit tests methods. """ def test_F_air_Penndorf1957(self): """ - Tests :func:`colour.phenomena.rayleigh.F_air_Penndorf1957` + Test :func:`colour.phenomena.rayleigh.F_air_Penndorf1957` definition. """ @@ -840,7 +886,7 @@ def test_F_air_Penndorf1957(self): def test_n_dimensional_F_air_Penndorf1957(self): """ - Tests :func:`colour.phenomena.rayleigh.F_air_Penndorf1957` definition + Test :func:`colour.phenomena.rayleigh.F_air_Penndorf1957` definition n-dimensional arrays support. """ @@ -862,7 +908,7 @@ def test_n_dimensional_F_air_Penndorf1957(self): @ignore_numpy_errors def test_nan_F_air_Penndorf1957(self): """ - Tests :func:`colour.phenomena.rayleigh.F_air_Penndorf1957` definition + Test :func:`colour.phenomena.rayleigh.F_air_Penndorf1957` definition nan support. """ @@ -871,20 +917,18 @@ def test_nan_F_air_Penndorf1957(self): class TestF_airYoung1981(unittest.TestCase): """ - Defines :func:`colour.phenomena.rayleigh.F_air_Young1981` definition + Define :func:`colour.phenomena.rayleigh.F_air_Young1981` definition unit tests methods. """ def test_F_air_Young1981(self): - """ - Tests :func:`colour.phenomena.rayleigh.F_air_Young1981` definition. - """ + """Test :func:`colour.phenomena.rayleigh.F_air_Young1981` definition.""" self.assertEqual(F_air_Young1981(0.360), 1.0480) def test_n_dimensional_F_air_Young1981(self): """ - Tests :func:`colour.phenomena.rayleigh.F_air_Young1981` definition + Test :func:`colour.phenomena.rayleigh.F_air_Young1981` definition n-dimensional arrays support. """ @@ -906,7 +950,7 @@ def test_n_dimensional_F_air_Young1981(self): @ignore_numpy_errors def test_nan_F_air_Young1981(self): """ - Tests :func:`colour.phenomena.rayleigh.F_air_Young1981` definition + Test :func:`colour.phenomena.rayleigh.F_air_Young1981` definition nan support. """ @@ -915,27 +959,28 @@ def test_nan_F_air_Young1981(self): class TestF_airBates1984(unittest.TestCase): """ - Defines :func:`colour.phenomena.rayleigh.F_air_Bates1984` definition unit + Define :func:`colour.phenomena.rayleigh.F_air_Bates1984` definition unit tests methods. """ def test_F_air_Bates1984(self): - """ - Tests :func:`colour.phenomena.rayleigh.F_air_Bates1984` definition. - """ + """Test :func:`colour.phenomena.rayleigh.F_air_Bates1984` definition.""" self.assertAlmostEqual( - F_air_Bates1984(0.360), 1.051997277711708, places=7) + F_air_Bates1984(0.360), 1.051997277711708, places=7 + ) self.assertAlmostEqual( - F_air_Bates1984(0.555), 1.048153579718658, places=7) + F_air_Bates1984(0.555), 1.048153579718658, places=7 + ) self.assertAlmostEqual( - F_air_Bates1984(0.830), 1.046947068600589, places=7) + F_air_Bates1984(0.830), 1.046947068600589, places=7 + ) def test_n_dimensional_F_air_Bates1984(self): """ - Tests :func:`colour.phenomena.rayleigh.F_air_Bates1984` definition + Test :func:`colour.phenomena.rayleigh.F_air_Bates1984` definition n-dimensional arrays support. """ @@ -957,7 +1002,7 @@ def test_n_dimensional_F_air_Bates1984(self): @ignore_numpy_errors def test_nan_F_air_Bates1984(self): """ - Tests :func:`colour.phenomena.rayleigh.F_air_Bates1984` definition + Test :func:`colour.phenomena.rayleigh.F_air_Bates1984` definition nan support. """ @@ -966,37 +1011,43 @@ def test_nan_F_air_Bates1984(self): class TestF_airBodhaine1999(unittest.TestCase): """ - Defines :func:`colour.phenomena.rayleigh.F_air_Bodhaine1999` definition + Define :func:`colour.phenomena.rayleigh.F_air_Bodhaine1999` definition unit tests methods. """ def test_F_air_Bodhaine1999(self): """ - Tests :func:`colour.phenomena.rayleigh.F_air_Bodhaine1999` + Test :func:`colour.phenomena.rayleigh.F_air_Bodhaine1999` definition. """ self.assertAlmostEqual( - F_air_Bodhaine1999(0.360), 1.125664021159081, places=7) + F_air_Bodhaine1999(0.360), 1.125664021159081, places=7 + ) self.assertAlmostEqual( - F_air_Bodhaine1999(0.555), 1.124691670240156, places=7) + F_air_Bodhaine1999(0.555), 1.124691670240156, places=7 + ) self.assertAlmostEqual( - F_air_Bodhaine1999(0.830), 1.124386455783539, places=7) + F_air_Bodhaine1999(0.830), 1.124386455783539, places=7 + ) self.assertAlmostEqual( - F_air_Bodhaine1999(0.360, 0), 1.052629792313939, places=7) + F_air_Bodhaine1999(0.360, 0), 1.052629792313939, places=7 + ) self.assertAlmostEqual( - F_air_Bodhaine1999(0.555, 360), 1.127993015096689, places=7) + F_air_Bodhaine1999(0.555, 360), 1.127993015096689, places=7 + ) self.assertAlmostEqual( - F_air_Bodhaine1999(0.830, 620), 1.13577082, places=7) + F_air_Bodhaine1999(0.830, 620), 1.13577082, places=7 + ) def test_n_dimensional_F_air_Bodhaine1999(self): """ - Tests :func:`colour.phenomena.rayleigh.F_air_Bodhaine1999` definition + Test :func:`colour.phenomena.rayleigh.F_air_Bodhaine1999` definition n-dimensional arrays support. """ @@ -1018,7 +1069,7 @@ def test_n_dimensional_F_air_Bodhaine1999(self): @ignore_numpy_errors def test_nan_F_air_Bodhaine1999(self): """ - Tests :func:`colour.phenomena.rayleigh.F_air_Bodhaine1999` definition + Test :func:`colour.phenomena.rayleigh.F_air_Bodhaine1999` definition nan support. """ @@ -1032,27 +1083,28 @@ def test_nan_F_air_Bodhaine1999(self): class TestMolecularDensity(unittest.TestCase): """ - Defines :func:`colour.phenomena.rayleigh.molecular_density` definition + Define :func:`colour.phenomena.rayleigh.molecular_density` definition unit tests methods. """ def test_molecular_density(self): - """ - Tests :func:`colour.phenomena.rayleigh.molecular_density` definition. - """ + """Test :func:`colour.phenomena.rayleigh.molecular_density` definition.""" self.assertAlmostEqual( - molecular_density(200), 3.669449208173649e+19, delta=10000) + molecular_density(200), 3.669449208173649e19, delta=10000 + ) self.assertAlmostEqual( - molecular_density(300), 2.4462994721157665e+19, delta=10000) + molecular_density(300), 2.4462994721157665e19, delta=10000 + ) self.assertAlmostEqual( - molecular_density(400), 1.834724604086825e+19, delta=10000) + molecular_density(400), 1.834724604086825e19, delta=10000 + ) def test_n_dimensional_molecular_density(self): """ - Tests :func:`colour.phenomena.rayleigh.molecular_density` definition + Test :func:`colour.phenomena.rayleigh.molecular_density` definition n-dimensional arrays support. """ @@ -1062,22 +1114,25 @@ def test_n_dimensional_molecular_density(self): temperature = np.tile(temperature, 6) N_s = np.tile(N_s, 6) np.testing.assert_almost_equal( - molecular_density(temperature), N_s, decimal=7) + molecular_density(temperature), N_s, decimal=7 + ) temperature = np.reshape(temperature, (2, 3)) N_s = np.reshape(N_s, (2, 3)) np.testing.assert_almost_equal( - molecular_density(temperature), N_s, decimal=7) + molecular_density(temperature), N_s, decimal=7 + ) temperature = np.reshape(temperature, (2, 3, 1)) N_s = np.reshape(N_s, (2, 3, 1)) np.testing.assert_almost_equal( - molecular_density(temperature), N_s, decimal=7) + molecular_density(temperature), N_s, decimal=7 + ) @ignore_numpy_errors def test_nan_molecular_density(self): """ - Tests :func:`colour.phenomena.rayleigh.molecular_density` definition + Test :func:`colour.phenomena.rayleigh.molecular_density` definition nan support. """ @@ -1086,27 +1141,29 @@ def test_nan_molecular_density(self): class TestMeanMolecularWeights(unittest.TestCase): """ - Defines :func:`colour.phenomena.rayleigh.mean_molecular_weights` + Define :func:`colour.phenomena.rayleigh.mean_molecular_weights` definition unit tests methods. """ def test_mean_molecular_weights(self): """ - Tests :func:`colour.phenomena.rayleigh.mean_molecular_weights` + Test :func:`colour.phenomena.rayleigh.mean_molecular_weights` definition. """ self.assertAlmostEqual(mean_molecular_weights(0), 28.9595, places=7) self.assertAlmostEqual( - mean_molecular_weights(360), 28.964920015999997, places=7) + mean_molecular_weights(360), 28.964920015999997, places=7 + ) self.assertAlmostEqual( - mean_molecular_weights(620), 28.968834471999998, places=7) + mean_molecular_weights(620), 28.968834471999998, places=7 + ) def test_n_dimensional_mean_molecular_weights(self): """ - Tests :func:`colour.phenomena.rayleigh.mean_molecular_weights` + Test :func:`colour.phenomena.rayleigh.mean_molecular_weights` definition n-dimensional arrays support. """ @@ -1116,52 +1173,57 @@ def test_n_dimensional_mean_molecular_weights(self): CO2_c = np.tile(CO2_c, 6) m_a = np.tile(m_a, 6) np.testing.assert_almost_equal( - mean_molecular_weights(CO2_c), m_a, decimal=7) + mean_molecular_weights(CO2_c), m_a, decimal=7 + ) CO2_c = np.reshape(CO2_c, (2, 3)) m_a = np.reshape(m_a, (2, 3)) np.testing.assert_almost_equal( - mean_molecular_weights(CO2_c), m_a, decimal=7) + mean_molecular_weights(CO2_c), m_a, decimal=7 + ) CO2_c = np.reshape(CO2_c, (2, 3, 1)) m_a = np.reshape(m_a, (2, 3, 1)) np.testing.assert_almost_equal( - mean_molecular_weights(CO2_c), m_a, decimal=7) + mean_molecular_weights(CO2_c), m_a, decimal=7 + ) @ignore_numpy_errors def test_nan_mean_molecular_weights(self): """ - Tests :func:`colour.phenomena.rayleigh.mean_molecular_weights` + Test :func:`colour.phenomena.rayleigh.mean_molecular_weights` definition nan support. """ mean_molecular_weights( - np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan])) + np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]) + ) class TestGravityList1968(unittest.TestCase): """ - Defines :func:`colour.phenomena.rayleigh.gravity_List1968` definition + Define :func:`colour.phenomena.rayleigh.gravity_List1968` definition unit tests methods. """ def test_gravity_List1968(self): - """ - Tests :func:`colour.phenomena.rayleigh.gravity_List1968` definition. - """ + """Test :func:`colour.phenomena.rayleigh.gravity_List1968` definition.""" self.assertAlmostEqual( - gravity_List1968(0.0, 0.0), 978.03560706, places=7) + gravity_List1968(0.0, 0.0), 978.03560706, places=7 + ) self.assertAlmostEqual( - gravity_List1968(45.0, 1500.0), 980.15334386, places=7) + gravity_List1968(45.0, 1500.0), 980.15334386, places=7 + ) self.assertAlmostEqual( - gravity_List1968(48.8567, 35.0), 980.95241784, places=7) + gravity_List1968(48.8567, 35.0), 980.95241784, places=7 + ) def test_n_dimensional_gravity_List1968(self): """ - Tests :func:`colour.phenomena.rayleigh.gravity_List1968` + Test :func:`colour.phenomena.rayleigh.gravity_List1968` definition n-dimensional arrays support. """ @@ -1180,7 +1242,7 @@ def test_n_dimensional_gravity_List1968(self): @ignore_numpy_errors def test_nan_gravity_List1968(self): """ - Tests :func:`colour.phenomena.rayleigh.gravity_List1968` definition + Test :func:`colour.phenomena.rayleigh.gravity_List1968` definition nan support. """ @@ -1194,64 +1256,73 @@ def test_nan_gravity_List1968(self): class TestScatteringCrossSection(unittest.TestCase): """ - Defines :func:`colour.phenomena.rayleigh.scattering_cross_section` + Define :func:`colour.phenomena.rayleigh.scattering_cross_section` definition unit tests methods. """ def test_scattering_cross_section(self): """ - Tests :func:`colour.phenomena.rayleigh.scattering_cross_section` + Test :func:`colour.phenomena.rayleigh.scattering_cross_section` definition. """ self.assertAlmostEqual( scattering_cross_section(360 * 10e-8), 2.781289234802031e-26, - places=32) + places=32, + ) self.assertAlmostEqual( scattering_cross_section(555 * 10e-8), 4.661330902337604e-27, - places=32) + places=32, + ) self.assertAlmostEqual( scattering_cross_section(830 * 10e-8), 9.125100352218880e-28, - places=32) + places=32, + ) self.assertAlmostEqual( scattering_cross_section(555 * 10e-8, 0), 4.346543336839102e-27, - places=32) + places=32, + ) self.assertAlmostEqual( scattering_cross_section(555 * 10e-8, 360), 4.675013461928133e-27, - places=32) + places=32, + ) self.assertAlmostEqual( scattering_cross_section(555 * 10e-8, 620), 4.707951639592975e-27, - places=32) + places=32, + ) self.assertAlmostEqual( scattering_cross_section(555 * 10e-8, temperature=200), 2.245601437154005e-27, - places=32) + places=32, + ) self.assertAlmostEqual( scattering_cross_section(555 * 10e-8, temperature=300), 5.052603233596510e-27, - places=32) + places=32, + ) self.assertAlmostEqual( scattering_cross_section(555 * 10e-8, temperature=400), 8.982405748616020e-27, - places=32) + places=32, + ) def test_n_dimensional_scattering_cross_section(self): """ - Tests :func:`colour.phenomena.rayleigh.scattering_cross_section` + Test :func:`colour.phenomena.rayleigh.scattering_cross_section` definition n-dimensional arrays support. """ @@ -1260,20 +1331,23 @@ def test_n_dimensional_scattering_cross_section(self): sigma = np.tile(sigma, 6) np.testing.assert_almost_equal( - scattering_cross_section(wl), sigma, decimal=32) + scattering_cross_section(wl), sigma, decimal=32 + ) sigma = np.reshape(sigma, (2, 3)) np.testing.assert_almost_equal( - scattering_cross_section(wl), sigma, decimal=32) + scattering_cross_section(wl), sigma, decimal=32 + ) sigma = np.reshape(sigma, (2, 3, 1)) np.testing.assert_almost_equal( - scattering_cross_section(wl), sigma, decimal=32) + scattering_cross_section(wl), sigma, decimal=32 + ) @ignore_numpy_errors def test_nan_scattering_cross_section(self): """ - Tests :func:`colour.phenomena.rayleigh.scattering_cross_section` + Test :func:`colour.phenomena.rayleigh.scattering_cross_section` definition nan support. """ @@ -1283,94 +1357,108 @@ def test_nan_scattering_cross_section(self): wavelength = case CO2_concentration = case temperature = case - scattering_cross_section(wavelength, CO2_concentration, - temperature) + scattering_cross_section( + wavelength, CO2_concentration, temperature + ) class TestRayleighOpticalDepth(unittest.TestCase): """ - Defines :func:`colour.phenomena.rayleigh.rayleigh_optical_depth` + Define :func:`colour.phenomena.rayleigh.rayleigh_optical_depth` definition unit tests methods. """ def test_rayleigh_optical_depth(self): """ - Tests :func:`colour.phenomena.rayleigh.rayleigh_optical_depth` + Test :func:`colour.phenomena.rayleigh.rayleigh_optical_depth` definition. """ self.assertAlmostEqual( - rayleigh_optical_depth(360 * 10e-8), 0.599101336848028, places=7) + rayleigh_optical_depth(360 * 10e-8), 0.599101336848028, places=7 + ) self.assertAlmostEqual( - rayleigh_optical_depth(555 * 10e-8), 0.100407017728965, places=7) + rayleigh_optical_depth(555 * 10e-8), 0.100407017728965, places=7 + ) self.assertAlmostEqual( - rayleigh_optical_depth(830 * 10e-8), 0.019655847912114, places=7) + rayleigh_optical_depth(830 * 10e-8), 0.019655847912114, places=7 + ) self.assertAlmostEqual( - rayleigh_optical_depth(555 * 10e-8, 0), - 0.093640964348049, - places=7) + rayleigh_optical_depth(555 * 10e-8, 0), 0.093640964348049, places=7 + ) self.assertAlmostEqual( rayleigh_optical_depth(555 * 10e-8, 360), 0.100698605176897, - places=7) + places=7, + ) self.assertAlmostEqual( rayleigh_optical_depth(555 * 10e-8, 620), 0.101394382260863, - places=7) + places=7, + ) self.assertAlmostEqual( rayleigh_optical_depth(555 * 10e-8, temperature=200), 0.048371194415621, - places=7) + places=7, + ) self.assertAlmostEqual( rayleigh_optical_depth(555 * 10e-8, temperature=300), 0.108835187435146, - places=7) + places=7, + ) self.assertAlmostEqual( rayleigh_optical_depth(555 * 10e-8, temperature=400), 0.193484777662482, - places=7) + places=7, + ) self.assertAlmostEqual( rayleigh_optical_depth(555 * 10e-8, pressure=101325), 0.100407017728965, - places=7) + places=7, + ) self.assertAlmostEqual( rayleigh_optical_depth(555 * 10e-8, pressure=100325), 0.099416077509583, - places=7) + places=7, + ) self.assertAlmostEqual( rayleigh_optical_depth(555 * 10e-8, pressure=99325), 0.098425137290200, - places=7) + places=7, + ) self.assertAlmostEqual( rayleigh_optical_depth(555 * 10e-8, latitude=0, altitude=0), 0.100407017728965, - places=10) + places=10, + ) self.assertAlmostEqual( rayleigh_optical_depth(555 * 10e-8, latitude=45, altitude=1500), 0.100190076534634, - places=10) + places=10, + ) self.assertAlmostEqual( rayleigh_optical_depth(555 * 10e-8, latitude=48.8567, altitude=35), 0.100108462705423, - places=10) + places=10, + ) def test_n_dimensional_rayleigh_optical_depth(self): """ - Tests :func:`colour.phenomena.rayleigh.rayleigh_optical_depth` + Test :func:`colour.phenomena.rayleigh.rayleigh_optical_depth` definition n-dimensional arrays support. """ @@ -1379,20 +1467,23 @@ def test_n_dimensional_rayleigh_optical_depth(self): T_R = np.tile(T_R, 6) np.testing.assert_almost_equal( - rayleigh_optical_depth(wl), T_R, decimal=7) + rayleigh_optical_depth(wl), T_R, decimal=7 + ) T_R = np.reshape(T_R, (2, 3)) np.testing.assert_almost_equal( - rayleigh_optical_depth(wl), T_R, decimal=7) + rayleigh_optical_depth(wl), T_R, decimal=7 + ) T_R = np.reshape(T_R, (2, 3, 1)) np.testing.assert_almost_equal( - rayleigh_optical_depth(wl), T_R, decimal=7) + rayleigh_optical_depth(wl), T_R, decimal=7 + ) @ignore_numpy_errors def test_nan_rayleigh_optical_depth(self): """ - Tests :func:`colour.phenomena.rayleigh.rayleigh_optical_depth` + Test :func:`colour.phenomena.rayleigh.rayleigh_optical_depth` definition nan support. """ @@ -1404,27 +1495,29 @@ def test_nan_rayleigh_optical_depth(self): temperature = case latitude = case altitude = case - rayleigh_optical_depth(wavelength, CO2_concentration, temperature, - latitude, altitude) + rayleigh_optical_depth( + wavelength, CO2_concentration, temperature, latitude, altitude + ) class TestSdRayleighScattering(unittest.TestCase): """ - Defines :func:`colour.phenomena.rayleigh.sd_rayleigh_scattering` + Define :func:`colour.phenomena.rayleigh.sd_rayleigh_scattering` definition unit tests methods. """ def test_sd_rayleigh_scattering(self): """ - Tests :func:`colour.phenomena.rayleigh.sd_rayleigh_scattering` + Test :func:`colour.phenomena.rayleigh.sd_rayleigh_scattering` definition. """ np.testing.assert_almost_equal( sd_rayleigh_scattering().values, DATA_SD_RAYLEIGH_SCATTERING, - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/plotting/__init__.py b/colour/plotting/__init__.py index c1960c0314..4509c1ca51 100644 --- a/colour/plotting/__init__.py +++ b/colour/plotting/__init__.py @@ -1,46 +1,67 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from colour.utilities import is_matplotlib_installed is_matplotlib_installed(raise_exception=True) -import sys # noqa - -from colour.utilities.deprecation import ModuleAPI, build_API_changes # noqa -from colour.utilities.documentation import is_documentation_building # noqa - from .datasets import * # noqa from . import datasets # noqa from .common import ( # noqa - CONSTANTS_COLOUR_STYLE, CONSTANTS_ARROW_STYLE, colour_style, - override_style, XYZ_to_plotting_colourspace, ColourSwatch, colour_cycle, - artist, camera, render, label_rectangles, uniform_axes3d, - filter_passthrough, filter_RGB_colourspaces, filter_cmfs, - filter_illuminants, filter_colour_checkers, update_settings_collection, - plot_single_colour_swatch, plot_multi_colour_swatches, - plot_single_function, plot_multi_functions, plot_image) + CONSTANTS_COLOUR_STYLE, + CONSTANTS_ARROW_STYLE, + colour_style, + override_style, + XYZ_to_plotting_colourspace, + ColourSwatch, + colour_cycle, + artist, + camera, + render, + label_rectangles, + uniform_axes3d, + filter_passthrough, + filter_RGB_colourspaces, + filter_cmfs, + filter_illuminants, + filter_colour_checkers, + update_settings_collection, + plot_single_colour_swatch, + plot_multi_colour_swatches, + plot_single_function, + plot_multi_functions, + plot_image, +) from .blindness import plot_cvd_simulation_Machado2009 # noqa from .colorimetry import ( # noqa - plot_single_sd, plot_multi_sds, plot_single_cmfs, plot_multi_cmfs, - plot_single_illuminant_sd, plot_multi_illuminant_sds, - plot_visible_spectrum, plot_single_lightness_function, - plot_multi_lightness_functions, plot_single_luminance_function, - plot_multi_luminance_functions, plot_blackbody_spectral_radiance, - plot_blackbody_colours) + plot_single_sd, + plot_multi_sds, + plot_single_cmfs, + plot_multi_cmfs, + plot_single_illuminant_sd, + plot_multi_illuminant_sds, + plot_visible_spectrum, + plot_single_lightness_function, + plot_multi_lightness_functions, + plot_single_luminance_function, + plot_multi_luminance_functions, + plot_blackbody_spectral_radiance, + plot_blackbody_colours, +) from .characterisation import ( # noqa - plot_single_colour_checker, plot_multi_colour_checkers) + plot_single_colour_checker, + plot_multi_colour_checkers, +) from .diagrams import ( # noqa - plot_chromaticity_diagram_CIE1931, plot_chromaticity_diagram_CIE1960UCS, + plot_chromaticity_diagram_CIE1931, + plot_chromaticity_diagram_CIE1960UCS, plot_chromaticity_diagram_CIE1976UCS, plot_sds_in_chromaticity_diagram_CIE1931, plot_sds_in_chromaticity_diagram_CIE1960UCS, - plot_sds_in_chromaticity_diagram_CIE1976UCS) + plot_sds_in_chromaticity_diagram_CIE1976UCS, +) from .corresponding import plot_corresponding_chromaticities_prediction # noqa from .graph import plot_automatic_colour_conversion_graph # noqa from .models import ( # noqa - common_colourspace_model_axis_reorder, plot_pointer_gamut, + colourspace_model_axis_reorder, + plot_pointer_gamut, plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931, plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS, plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS, @@ -50,438 +71,140 @@ plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931, plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS, plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS, - plot_single_cctf, plot_multi_cctfs, plot_constant_hue_loci) + plot_single_cctf, + plot_multi_cctfs, + plot_constant_hue_loci, +) from .notation import ( # noqa - plot_single_munsell_value_function, plot_multi_munsell_value_functions) + plot_single_munsell_value_function, + plot_multi_munsell_value_functions, +) from .phenomena import ( # noqa - plot_single_sd_rayleigh_scattering, plot_the_blue_sky) + plot_single_sd_rayleigh_scattering, + plot_the_blue_sky, +) from .quality import ( # noqa plot_single_sd_colour_rendering_index_bars, plot_multi_sds_colour_rendering_indexes_bars, plot_single_sd_colour_quality_scale_bars, - plot_multi_sds_colour_quality_scales_bars) + plot_multi_sds_colour_quality_scales_bars, +) +from .section import ( # noqa + plot_visible_spectrum_section, + plot_RGB_colourspace_section, +) from .temperature import ( # noqa plot_planckian_locus_in_chromaticity_diagram_CIE1931, - plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS) + plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS, +) from .tm3018 import plot_single_sd_colour_rendition_report # noqa from .volume import plot_RGB_colourspaces_gamuts, plot_RGB_scatter # noqa __all__ = [] __all__ += datasets.__all__ __all__ += [ - 'CONSTANTS_COLOUR_STYLE', 'CONSTANTS_ARROW_STYLE', 'colour_style', - 'override_style', 'XYZ_to_plotting_colourspace', 'ColourSwatch', - 'colour_cycle', 'artist', 'camera', 'render', 'label_rectangles', - 'uniform_axes3d', 'filter_passthrough', 'filter_RGB_colourspaces', - 'filter_cmfs', 'filter_illuminants', 'filter_colour_checkers', - 'update_settings_collection', 'plot_single_colour_swatch', - 'plot_multi_colour_swatches', 'plot_single_function', - 'plot_multi_functions', 'plot_image' + "CONSTANTS_COLOUR_STYLE", + "CONSTANTS_ARROW_STYLE", + "colour_style", + "override_style", + "XYZ_to_plotting_colourspace", + "ColourSwatch", + "colour_cycle", + "artist", + "camera", + "render", + "label_rectangles", + "uniform_axes3d", + "filter_passthrough", + "filter_RGB_colourspaces", + "filter_cmfs", + "filter_illuminants", + "filter_colour_checkers", + "update_settings_collection", + "plot_single_colour_swatch", + "plot_multi_colour_swatches", + "plot_single_function", + "plot_multi_functions", + "plot_image", ] -__all__ += ['plot_cvd_simulation_Machado2009'] __all__ += [ - 'plot_single_sd', 'plot_multi_sds', 'plot_single_cmfs', 'plot_multi_cmfs', - 'plot_single_illuminant_sd', 'plot_multi_illuminant_sds', - 'plot_visible_spectrum', 'plot_single_lightness_function', - 'plot_multi_lightness_functions', 'plot_single_luminance_function', - 'plot_multi_luminance_functions', 'plot_blackbody_spectral_radiance', - 'plot_blackbody_colours' + "plot_cvd_simulation_Machado2009", ] -__all__ += ['plot_single_colour_checker', 'plot_multi_colour_checkers'] __all__ += [ - 'plot_chromaticity_diagram_CIE1931', - 'plot_chromaticity_diagram_CIE1960UCS', - 'plot_chromaticity_diagram_CIE1976UCS', - 'plot_sds_in_chromaticity_diagram_CIE1931', - 'plot_sds_in_chromaticity_diagram_CIE1960UCS', - 'plot_sds_in_chromaticity_diagram_CIE1976UCS' + "plot_single_sd", + "plot_multi_sds", + "plot_single_cmfs", + "plot_multi_cmfs", + "plot_single_illuminant_sd", + "plot_multi_illuminant_sds", + "plot_visible_spectrum", + "plot_single_lightness_function", + "plot_multi_lightness_functions", + "plot_single_luminance_function", + "plot_multi_luminance_functions", + "plot_blackbody_spectral_radiance", + "plot_blackbody_colours", ] -__all__ += ['plot_corresponding_chromaticities_prediction'] -__all__ += ['plot_automatic_colour_conversion_graph'] __all__ += [ - 'common_colourspace_model_axis_reorder', 'plot_pointer_gamut', - 'plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931', - 'plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS', - 'plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS', - 'plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931', - 'plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS', - 'plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS', - 'plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931', - 'plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS', - 'plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS', - 'plot_single_cctf', 'plot_multi_cctfs', 'plot_constant_hue_loci' + "plot_single_colour_checker", + "plot_multi_colour_checkers", ] __all__ += [ - 'plot_single_munsell_value_function', 'plot_multi_munsell_value_functions' + "plot_chromaticity_diagram_CIE1931", + "plot_chromaticity_diagram_CIE1960UCS", + "plot_chromaticity_diagram_CIE1976UCS", + "plot_sds_in_chromaticity_diagram_CIE1931", + "plot_sds_in_chromaticity_diagram_CIE1960UCS", + "plot_sds_in_chromaticity_diagram_CIE1976UCS", ] -__all__ += ['plot_single_sd_rayleigh_scattering', 'plot_the_blue_sky'] __all__ += [ - 'plot_single_sd_colour_rendering_index_bars', - 'plot_multi_sds_colour_rendering_indexes_bars', - 'plot_single_sd_colour_quality_scale_bars', - 'plot_multi_sds_colour_quality_scales_bars' + "plot_corresponding_chromaticities_prediction", ] __all__ += [ - 'plot_planckian_locus_in_chromaticity_diagram_CIE1931', - 'plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS' + "plot_automatic_colour_conversion_graph", ] -__all__ += ['plot_single_sd_colour_rendition_report'] -__all__ += ['plot_RGB_colourspaces_gamuts', 'plot_RGB_scatter'] - - -# ----------------------------------------------------------------------------# -# --- API Changes and Deprecation Management ---# -# ----------------------------------------------------------------------------# -class plotting(ModuleAPI): - def __getattr__(self, attribute): - return super(plotting, self).__getattr__(attribute) - - -# v0.3.11 -API_CHANGES = { - 'ObjectRenamed': [ - [ - 'colour.plotting.CIE_1931_chromaticity_diagram_plot', - 'colour.plotting.plot_chromaticity_diagram_CIE1931', - ], - [ - 'colour.plotting.CIE_1960_UCS_chromaticity_diagram_plot', - 'colour.plotting.plot_chromaticity_diagram_CIE1960UCS', - ], - [ - 'colour.plotting.CIE_1976_UCS_chromaticity_diagram_plot', - 'colour.plotting.plot_chromaticity_diagram_CIE1976UCS', - ], - [ - 'colour.plotting.RGB_chromaticity_coordinates_CIE_1931_chromaticity_diagram_plot', # noqa - 'colour.plotting.plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931', # noqa - ], - [ - 'colour.plotting.RGB_chromaticity_coordinates_CIE_1960_UCS_chromaticity_diagram_plot', # noqa - 'colour.plotting.plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS', # noqa - ], - [ - 'colour.plotting.RGB_chromaticity_coordinates_CIE_1976_UCS_chromaticity_diagram_plot', # noqa - 'colour.plotting.plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS', # noqa - ], - [ - 'colour.plotting.RGB_colourspaces_CIE_1931_chromaticity_diagram_plot', # noqa - 'colour.plotting.plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931', # noqa - ], - [ - 'colour.plotting.RGB_colourspaces_CIE_1960_UCS_chromaticity_diagram_plot', # noqa - 'colour.plotting.plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS', # noqa - ], - [ - 'colour.plotting.RGB_colourspaces_CIE_1976_UCS_chromaticity_diagram_plot', # noqa - 'colour.plotting.plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS', # noqa - ], - [ - 'colour.plotting.planckian_locus_CIE_1931_chromaticity_diagram_plot', # noqa - 'colour.plotting.plot_planckian_locus_in_chromaticity_diagram_CIE1931', # noqa - ], - [ - 'colour.plotting.planckian_locus_CIE_1960_UCS_chromaticity_diagram_plot', # noqa - 'colour.plotting.plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS', # noqa - ], - [ - 'colour.plotting.spds_CIE_1931_chromaticity_diagram_plot', - 'colour.plotting.plot_sds_in_chromaticity_diagram_CIE1931', - ], - [ - 'colour.plotting.spds_CIE_1960_UCS_chromaticity_diagram_plot', - 'colour.plotting.plot_sds_in_chromaticity_diagram_CIE1960UCS', - ], - [ - 'colour.plotting.spds_CIE_1976_UCS_chromaticity_diagram_plot', - 'colour.plotting.plot_sds_in_chromaticity_diagram_CIE1976UCS' - ], - ] -} -""" -Defines *colour.plotting* sub-package API changes. - -API_CHANGES : dict -""" - -# v0.3.12 -API_CHANGES['ObjectRenamed'] = API_CHANGES['ObjectRenamed'] + [ - [ - 'colour.plotting.RGB_chromaticity_coordinates_chromaticity_diagram_plot', # noqa - 'colour.plotting.plot_RGB_chromaticities_in_chromaticity_diagram', - ], - [ - 'colour.plotting.RGB_chromaticity_coordinates_chromaticity_diagram_plot_CIE1931', # noqa - 'colour.plotting.plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931', # noqa - ], - [ - 'colour.plotting.RGB_chromaticity_coordinates_chromaticity_diagram_plot_CIE1960UCS', # noqa - 'colour.plotting.plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS', # noqa - ], - [ - 'colour.plotting.RGB_chromaticity_coordinates_chromaticity_diagram_plot_CIE1976UCS', # noqa - 'colour.plotting.plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS', # noqa - ], - [ - 'colour.plotting.RGB_colourspaces_chromaticity_diagram_plot', - 'colour.plotting.plot_RGB_colourspaces_in_chromaticity_diagram', - ], - [ - 'colour.plotting.RGB_colourspaces_chromaticity_diagram_plot_CIE1931', - 'colour.plotting.plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931', # noqa - ], - [ - 'colour.plotting.RGB_colourspaces_chromaticity_diagram_plot_CIE1960UCS', # noqa - 'colour.plotting.plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS', # noqa - ], - [ - 'colour.plotting.RGB_colourspaces_chromaticity_diagram_plot_CIE1976UCS', # noqa - 'colour.plotting.plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS', # noqa - ], - [ - 'colour.plotting.RGB_colourspaces_gamuts_plot', - 'colour.plotting.plot_RGB_colourspaces_gamuts', - ], - [ - 'colour.plotting.RGB_scatter_plot', - 'colour.plotting.plot_RGB_scatter', - ], - [ - 'colour.plotting.blackbody_colours_plot', - 'colour.plotting.plot_blackbody_colours', - ], - [ - 'colour.plotting.blackbody_spectral_radiance_plot', - 'colour.plotting.plot_blackbody_spectral_radiance', - ], - [ - 'colour.plotting.chromaticity_diagram_colours_plot', - 'colour.plotting.plot_chromaticity_diagram_colours', - ], - [ - 'colour.plotting.chromaticity_diagram_plot', - 'colour.plotting.plot_chromaticity_diagram', - ], - [ - 'colour.plotting.chromaticity_diagram_plot_CIE1931', - 'colour.plotting.plot_chromaticity_diagram_CIE1931', - ], - [ - 'colour.plotting.chromaticity_diagram_plot_CIE1960UCS', - 'colour.plotting.plot_chromaticity_diagram_CIE1960UCS', - ], - [ - 'colour.plotting.chromaticity_diagram_plot_CIE1976UCS', - 'colour.plotting.plot_chromaticity_diagram_CIE1976UCS', - ], - [ - 'colour.plotting.colour_plotting_defaults', - 'colour.plotting.colour_style', - ], - [ - 'colour.plotting.colour_quality_bars_plot', - 'colour.plotting.plot_colour_quality_bars', - ], - [ - 'colour.plotting.corresponding_chromaticities_prediction_plot', - 'colour.plotting.plot_corresponding_chromaticities_prediction', - ], - [ - 'colour.plotting.cvd_simulation_Machado2009_plot', - 'colour.plotting.plot_cvd_simulation_Machado2009', - ], - [ - 'colour.plotting.equal_axes3d', - 'colour.plotting.uniform_axes3d', - ], - [ - 'colour.plotting.get_RGB_colourspace', - 'colour.plotting.filter_RGB_colourspaces', - ], - [ - 'colour.plotting.get_cmfs', - 'colour.plotting.filter_cmfs', - ], - [ - 'colour.plotting.get_illuminant', - 'colour.plotting.filter_illuminants', - ], - [ - 'colour.plotting.image_plot', - 'colour.plotting.plot_image', - ], - [ - 'colour.plotting.multi_cctf_plot', - 'colour.plotting.plot_multi_cctfs', - ], - [ - 'colour.plotting.multi_cmfs_plot', - 'colour.plotting.plot_multi_cmfs', - ], - [ - 'colour.plotting.multi_colour_checker_plot', - 'colour.plotting.plot_multi_colour_checkers', - ], - [ - 'colour.plotting.multi_colour_swatch_plot', - 'colour.plotting.plot_multi_colour_swatches', - ], - [ - 'colour.plotting.multi_illuminant_spd_plot', - 'colour.plotting.plot_multi_illuminant_sds', - ], - [ - 'colour.plotting.multi_lightness_function_plot', - 'colour.plotting.plot_multi_lightness_functions', - ], - [ - 'colour.plotting.multi_munsell_value_function_plot', - 'colour.plotting.plot_multi_munsell_value_functions', - ], - [ - 'colour.plotting.multi_spd_colour_quality_scale_bars_plot', - 'colour.plotting.plot_multi_sds_colour_quality_scales_bars', - ], - [ - 'colour.plotting.multi_spd_colour_rendering_index_bars_plot', - 'colour.plotting.plot_multi_sds_colour_rendering_indexes_bars', - ], - [ - 'colour.plotting.multi_spd_plot', - 'colour.plotting.plot_multi_sds', - ], - [ - 'colour.plotting.planckian_locus_chromaticity_diagram_plot', - 'colour.plotting.plot_planckian_locus_in_chromaticity_diagram', - ], - [ - 'colour.plotting.planckian_locus_chromaticity_diagram_plot_CIE1931', - 'colour.plotting.plot_planckian_locus_in_chromaticity_diagram_CIE1931', - ], - [ - 'colour.plotting.planckian_locus_chromaticity_diagram_plot_CIE1960UCS', - 'colour.plotting.plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS', # noqa - ], - [ - 'colour.plotting.planckian_locus_plot', - 'colour.plotting.plot_planckian_locus', - ], - [ - 'colour.plotting.single_cctf_plot', - 'colour.plotting.plot_single_cctf', - ], - [ - 'colour.plotting.single_cmfs_plot', - 'colour.plotting.plot_single_cmfs', - ], - [ - 'colour.plotting.single_colour_checker_plot', - 'colour.plotting.plot_single_colour_checker', - ], - [ - 'colour.plotting.single_colour_swatch_plot', - 'colour.plotting.plot_single_colour_swatch', - ], - [ - 'colour.plotting.single_illuminant_spd_plot', - 'colour.plotting.plot_single_illuminant_sd', - ], - [ - 'colour.plotting.single_lightness_function_plot', - 'colour.plotting.plot_single_lightness_function', - ], - [ - 'colour.plotting.single_munsell_value_function_plot', - 'colour.plotting.plot_single_munsell_value_function', - ], - [ - 'colour.plotting.single_spd_colour_quality_scale_bars_plot', - 'colour.plotting.plot_single_sd_colour_quality_scale_bars', - ], - [ - 'colour.plotting.single_spd_colour_rendering_index_bars_plot', - 'colour.plotting.plot_single_sd_colour_rendering_index_bars', - ], - [ - 'colour.plotting.single_spd_plot', - 'colour.plotting.plot_single_sd', - ], - [ - 'colour.plotting.single_spd_rayleigh_scattering_plot', - 'colour.plotting.plot_single_sd_rayleigh_scattering', - ], - [ - 'colour.plotting.spds_chromaticity_diagram_plot', - 'colour.plotting.plot_sds_in_chromaticity_diagram', - ], - [ - 'colour.plotting.spds_chromaticity_diagram_plot_CIE1931', - 'colour.plotting.plot_sds_in_chromaticity_diagram_CIE1931', - ], - [ - 'colour.plotting.spds_chromaticity_diagram_plot_CIE1960UCS', - 'colour.plotting.plot_sds_in_chromaticity_diagram_CIE1960UCS', - ], - [ - 'colour.plotting.spds_chromaticity_diagram_plot_CIE1976UCS', - 'colour.plotting.plot_sds_in_chromaticity_diagram_CIE1976UCS', - ], - [ - 'colour.plotting.spectral_locus_plot', - 'colour.plotting.plot_spectral_locus', - ], - [ - 'colour.plotting.the_blue_sky_plot', - 'colour.plotting.plot_the_blue_sky', - ], - [ - 'colour.plotting.visible_spectrum_plot', - 'colour.plotting.plot_visible_spectrum' - ], +__all__ += [ + "colourspace_model_axis_reorder", + "plot_pointer_gamut", + "plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931", + "plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS", + "plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS", + "plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931", + "plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS", + "plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS", + "plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931", + "plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS", + "plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS", + "plot_single_cctf", + "plot_multi_cctfs", + "plot_constant_hue_loci", ] - -# v0.3.14 -API_CHANGES['ObjectRenamed'] = API_CHANGES['ObjectRenamed'] + [ - [ - 'colour.plotting.ASTM_G_173_DIRECT_CIRCUMSOLAR', - 'colour.plotting.SD_ASTMG173_DIRECT_CIRCUMSOLAR', - ], - [ - 'colour.plotting.ASTM_G_173_ETR', - 'colour.plotting.SD_ASTMG173_ETR', - ], - [ - 'colour.plotting.ASTM_G_173_GLOBAL_TILT', - 'colour.plotting.SD_ASTMG173_GLOBAL_TILT', - ], +__all__ += [ + "plot_single_munsell_value_function", + "plot_multi_munsell_value_functions", ] - -# v0.3.16 -API_CHANGES['ObjectRenamed'] = API_CHANGES['ObjectRenamed'] + [ - [ - 'colour.plotting.COLOUR_STYLE_CONSTANTS', - 'colour.plotting.CONSTANTS_COLOUR_STYLE', - ], - [ - 'colour.plotting.COLOUR_ARROW_STYLE', - 'colour.plotting.CONSTANTS_ARROW_STYLE', - ], - [ - 'colour.plotting.quad', - 'colour.geometry.primitive_vertices_quad_mpl', - ], - [ - 'colour.plotting.grid', - 'colour.geometry.primitive_vertices_grid_mpl', - ], - [ - 'colour.plotting.cube', - 'colour.geometry.primitive_vertices_cube_mpl', - ], +__all__ += [ + "plot_single_sd_rayleigh_scattering", + "plot_the_blue_sky", +] +__all__ += [ + "plot_single_sd_colour_rendering_index_bars", + "plot_multi_sds_colour_rendering_indexes_bars", + "plot_single_sd_colour_quality_scale_bars", + "plot_multi_sds_colour_quality_scales_bars", +] +__all__ += [ + "plot_visible_spectrum_section", + "plot_RGB_colourspace_section", +] +__all__ += [ + "plot_planckian_locus_in_chromaticity_diagram_CIE1931", + "plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS", +] +__all__ += [ + "plot_single_sd_colour_rendition_report", +] +__all__ += [ + "plot_RGB_colourspaces_gamuts", + "plot_RGB_scatter", ] - -if not is_documentation_building(): - sys.modules['colour.plotting'] = plotting(sys.modules['colour.plotting'], - build_API_changes(API_CHANGES)) - - del ModuleAPI, is_documentation_building, build_API_changes, sys diff --git a/colour/plotting/blindness.py b/colour/plotting/blindness.py index d2847d970f..61b8a5bfcd 100644 --- a/colour/plotting/blindness.py +++ b/colour/plotting/blindness.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Colour Blindness Plotting ========================= @@ -8,51 +7,70 @@ - :func:`colour.plotting.plot_cvd_simulation_Machado2009` """ -from __future__ import division +from __future__ import annotations +import matplotlib.pyplot as plt + +from colour.algebra import vector_dot from colour.blindness import matrix_cvd_Machado2009 +from colour.hints import ( + Any, + ArrayLike, + Dict, + Floating, + Literal, + Optional, + Tuple, + Union, + cast, +) from colour.plotting import CONSTANTS_COLOUR_STYLE, plot_image, override_style -from colour.utilities import vector_dot +from colour.utilities import optional -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['plot_cvd_simulation_Machado2009'] +__all__ = [ + "plot_cvd_simulation_Machado2009", +] @override_style() -def plot_cvd_simulation_Machado2009(RGB, - deficiency='Protanomaly', - severity=0.5, - M_a=None, - **kwargs): +def plot_cvd_simulation_Machado2009( + RGB: ArrayLike, + deficiency: Union[ + Literal["Deuteranomaly", "Protanomaly", "Tritanomaly"], str + ] = "Protanomaly", + severity: Floating = 0.5, + M_a: Optional[ArrayLike] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Performs colour vision deficiency simulation on given *RGB* colourspace + Perform colour vision deficiency simulation on given *RGB* colourspace array using *Machado et al. (2009)* model. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array. - deficiency : unicode, optional - {'Protanomaly', 'Deuteranomaly', 'Tritanomaly'} + deficiency Colour blindness / vision deficiency type. - severity : numeric, optional + severity Severity of the colour vision deficiency in domain [0, 1]. - M_a : array_like, optional + M_a Anomalous trichromacy matrix to use instead of Machado (2010) pre-computed matrix. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_image`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Notes ----- @@ -60,7 +78,7 @@ def plot_cvd_simulation_Machado2009(RGB, Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -75,14 +93,20 @@ def plot_cvd_simulation_Machado2009(RGB, :alt: plot_cvd_simulation_Machado2009 """ - if M_a is None: - M_a = matrix_cvd_Machado2009(deficiency, severity) - - text = 'Deficiency: {0} - Severity: {1}'.format(deficiency, severity) + M_a = cast( + ArrayLike, optional(M_a, matrix_cvd_Machado2009(deficiency, severity)) + ) - settings = {'text_kwargs': {'text': None if M_a is None else text}} + settings: Dict[str, Any] = { + "text_kwargs": { + "text": f"Deficiency: {deficiency} - Severity: {severity}" + } + } settings.update(kwargs) return plot_image( CONSTANTS_COLOUR_STYLE.colour.colourspace.cctf_encoding( - vector_dot(M_a, RGB)), **settings) + vector_dot(M_a, RGB) + ), + **settings, + ) diff --git a/colour/plotting/characterisation.py b/colour/plotting/characterisation.py index f74ef7a910..4c7af1e217 100644 --- a/colour/plotting/characterisation.py +++ b/colour/plotting/characterisation.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Characterisation Plotting ========================= @@ -9,56 +8,75 @@ - :func:`colour.plotting.plot_multi_colour_checkers` """ -from __future__ import division +from __future__ import annotations import numpy as np +import matplotlib.pyplot as plt +from colour.hints import Any, Dict, Sequence, Tuple, Union +from colour.characterisation import ColourChecker from colour.models import xyY_to_XYZ from colour.plotting import ( - CONSTANTS_COLOUR_STYLE, ColourSwatch, XYZ_to_plotting_colourspace, artist, - filter_colour_checkers, plot_multi_colour_swatches, override_style, render) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['plot_single_colour_checker', 'plot_multi_colour_checkers'] + CONSTANTS_COLOUR_STYLE, + ColourSwatch, + XYZ_to_plotting_colourspace, + artist, + filter_colour_checkers, + plot_multi_colour_swatches, + override_style, + render, +) +from colour.utilities import attest + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "plot_single_colour_checker", + "plot_multi_colour_checkers", +] @override_style( **{ - 'axes.grid': False, - 'xtick.bottom': False, - 'ytick.left': False, - 'xtick.labelbottom': False, - 'ytick.labelleft': False, - }) + "axes.grid": False, + "xtick.bottom": False, + "ytick.left": False, + "xtick.labelbottom": False, + "ytick.labelleft": False, + } +) def plot_single_colour_checker( - colour_checker='ColorChecker24 - After November 2014', **kwargs): + colour_checker: Union[ + ColourChecker, str + ] = "ColorChecker24 - After November 2014", + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given colour checker. + Plot given colour checker. Parameters ---------- - colour_checker : unicode or ColourChecker, optional + colour_checker Color checker to plot. ``colour_checker`` can be of any type or form supported by the :func:`colour.plotting.filter_colour_checkers` definition. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_multi_colour_swatches`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -76,34 +94,40 @@ def plot_single_colour_checker( @override_style( **{ - 'axes.grid': False, - 'xtick.bottom': False, - 'ytick.left': False, - 'xtick.labelbottom': False, - 'ytick.labelleft': False, - }) -def plot_multi_colour_checkers(colour_checkers, **kwargs): + "axes.grid": False, + "xtick.bottom": False, + "ytick.left": False, + "xtick.labelbottom": False, + "ytick.labelleft": False, + } +) +def plot_multi_colour_checkers( + colour_checkers: Union[ + ColourChecker, str, Sequence[Union[ColourChecker, str]] + ], + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots and compares given colour checkers. + Plot and compares given colour checkers. Parameters ---------- - colour_checkers : unicode or ColourChecker or array_like + colour_checkers Color checker to plot, count must be less than or equal to 2. ``colour_checkers`` elements can be of any type or form supported by the :func:`colour.plotting.filter_colour_checkers` definition. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_multi_colour_swatches`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -117,71 +141,82 @@ def plot_multi_colour_checkers(colour_checkers, **kwargs): :alt: plot_multi_colour_checkers """ - assert len(colour_checkers) <= 2, ( - 'Only two colour checkers can be compared at a time!') + filtered_colour_checkers = list( + filter_colour_checkers(colour_checkers).values() + ) - colour_checkers = filter_colour_checkers(colour_checkers).values() + attest( + len(filtered_colour_checkers) <= 2, + "Only two colour checkers can be compared at a time!", + ) _figure, axes = artist(**kwargs) - compare_swatches = len(colour_checkers) == 2 + compare_swatches = len(filtered_colour_checkers) == 2 colour_swatches = [] colour_checker_names = [] - for colour_checker in colour_checkers: + for colour_checker in filtered_colour_checkers: colour_checker_names.append(colour_checker.name) for label, xyY in colour_checker.data.items(): XYZ = xyY_to_XYZ(xyY) RGB = XYZ_to_plotting_colourspace(XYZ, colour_checker.illuminant) colour_swatches.append( - ColourSwatch(label.title(), np.clip(np.ravel(RGB), 0, 1))) + ColourSwatch(np.clip(np.ravel(RGB), 0, 1), label.title()) + ) if compare_swatches: colour_swatches = [ swatch - for pairs in zip(colour_swatches[0:len(colour_swatches) // 2], - colour_swatches[len(colour_swatches) // 2:]) + for pairs in zip( + colour_swatches[0 : len(colour_swatches) // 2], + colour_swatches[len(colour_swatches) // 2 :], + ) for swatch in pairs ] - background_colour = '0.1' + background_colour = "0.1" width = height = 1.0 spacing = 0.25 columns = 6 - settings = { - 'axes': axes, - 'width': width, - 'height': height, - 'spacing': spacing, - 'columns': columns, - 'direction': '-y', - 'text_kwargs': { - 'size': 8 - }, - 'background_colour': background_colour, - 'compare_swatches': 'Stacked' if compare_swatches else None, + settings: Dict[str, Any] = { + "axes": axes, + "width": width, + "height": height, + "spacing": spacing, + "columns": columns, + "direction": "-y", + "text_kwargs": {"size": 8}, + "background_colour": background_colour, + "compare_swatches": "Stacked" if compare_swatches else None, } settings.update(kwargs) - settings['standalone'] = False + settings["standalone"] = False plot_multi_colour_swatches(colour_swatches, **settings) axes.text( 0.5, 0.005, - '{0} - {1} - Colour Rendition Chart'.format( - ', '.join(colour_checker_names), - CONSTANTS_COLOUR_STYLE.colour.colourspace.name), + ( + f"{', '.join(colour_checker_names)} - " + f"{CONSTANTS_COLOUR_STYLE.colour.colourspace.name} - " + f"Colour Rendition Chart" + ), transform=axes.transAxes, color=CONSTANTS_COLOUR_STYLE.colour.bright, - ha='center', - va='bottom') - - settings.update({ - 'axes': axes, - 'standalone': True, - 'title': ', '.join(colour_checker_names), - }) + ha="center", + va="bottom", + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_label, + ) + + settings.update( + { + "axes": axes, + "standalone": True, + "title": ", ".join(colour_checker_names), + } + ) return render(**settings) diff --git a/colour/plotting/colorimetry.py b/colour/plotting/colorimetry.py index eb17e32422..a186737582 100644 --- a/colour/plotting/colorimetry.py +++ b/colour/plotting/colorimetry.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Colorimetry Plotting ==================== @@ -25,71 +24,118 @@ T. and Shaw, N. """ -from __future__ import division +from __future__ import annotations import matplotlib.pyplot as plt import numpy as np +from functools import reduce from matplotlib.patches import Polygon -from six.moves import reduce -from colour.algebra import LinearInterpolator +from colour.algebra import LinearInterpolator, normalise_maximum from colour.colorimetry import ( - CCS_ILLUMINANTS, SDS_ILLUMINANTS, LIGHTNESS_METHODS, LUMINANCE_METHODS, - SpectralShape, sd_blackbody, sd_ones, sd_to_XYZ, sds_and_msds_to_sds, - wavelength_to_XYZ) + CCS_ILLUMINANTS, + SDS_ILLUMINANTS, + LIGHTNESS_METHODS, + LUMINANCE_METHODS, + MultiSpectralDistributions, + SpectralDistribution, + SpectralShape, + sd_blackbody, + sd_ones, + sd_to_XYZ, + sds_and_msds_to_sds, + wavelength_to_XYZ, +) +from colour.hints import ( + Any, + Boolean, + Callable, + Dict, + Floating, + List, + Optional, + Sequence, + Tuple, + Union, + cast, +) from colour.plotting import ( - ColourSwatch, CONSTANTS_COLOUR_STYLE, XYZ_to_plotting_colourspace, artist, - filter_passthrough, filter_cmfs, filter_illuminants, override_style, - render, plot_single_colour_swatch, plot_multi_functions, - update_settings_collection) -from colour.utilities import (domain_range_scale, first_item, - normalise_maximum, ones, tstack) -from colour.utilities.deprecation import handle_arguments_deprecation - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + CONSTANTS_COLOUR_STYLE, + XYZ_to_plotting_colourspace, + artist, + filter_passthrough, + filter_cmfs, + filter_illuminants, + override_style, + render, + plot_single_colour_swatch, + plot_multi_functions, + update_settings_collection, +) +from colour.utilities import ( + as_float_array, + domain_range_scale, + first_item, + ones, + tstack, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'plot_single_sd', 'plot_multi_sds', 'plot_single_cmfs', 'plot_multi_cmfs', - 'plot_single_illuminant_sd', 'plot_multi_illuminant_sds', - 'plot_visible_spectrum', 'plot_single_lightness_function', - 'plot_multi_lightness_functions', 'plot_single_luminance_function', - 'plot_multi_luminance_functions', 'plot_blackbody_spectral_radiance', - 'plot_blackbody_colours' + "plot_single_sd", + "plot_multi_sds", + "plot_single_cmfs", + "plot_multi_cmfs", + "plot_single_illuminant_sd", + "plot_multi_illuminant_sds", + "plot_visible_spectrum", + "plot_single_lightness_function", + "plot_multi_lightness_functions", + "plot_single_luminance_function", + "plot_multi_luminance_functions", + "plot_blackbody_spectral_radiance", + "plot_blackbody_colours", ] @override_style() -def plot_single_sd(sd, - cmfs='CIE 1931 2 Degree Standard Observer', - out_of_gamut_clipping=True, - modulate_colours_with_sd_amplitude=False, - equalize_sd_amplitude=False, - **kwargs): +def plot_single_sd( + sd: SpectralDistribution, + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + out_of_gamut_clipping: Boolean = True, + modulate_colours_with_sd_amplitude: Boolean = False, + equalize_sd_amplitude: Boolean = False, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given spectral distribution. + Plot given spectral distribution. Parameters ---------- - sd : SpectralDistribution + sd Spectral distribution to plot. - cmfs : unicode or LMS_ConeFundamentals or \ -RGB_ColourMatchingFunctions or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectrum domain and colours. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - out_of_gamut_clipping : bool, optional + out_of_gamut_clipping Whether to clip out of gamut colours otherwise, the colours will be offset by the absolute minimal colour leading to a rendering on gray background, less saturated and smoother. - modulate_colours_with_sd_amplitude : bool, optional + modulate_colours_with_sd_amplitude Whether to modulate the colours with the spectral distribution amplitude. - equalize_sd_amplitude : bool, optional + equalize_sd_amplitude Whether to equalize the spectral distribution amplitude. Equalization occurs after the colours modulation thus setting both arguments to *True* will generate a spectrum strip where each @@ -98,13 +144,13 @@ def plot_single_sd(sd, Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. References @@ -133,20 +179,27 @@ def plot_single_sd(sd, _figure, axes = artist(**kwargs) - cmfs = first_item(filter_cmfs(cmfs).values()) + cmfs = cast( + MultiSpectralDistributions, first_item(filter_cmfs(cmfs).values()) + ) - sd = sd.copy() + sd = cast(SpectralDistribution, sd.copy()) sd.interpolator = LinearInterpolator - wavelengths = cmfs.wavelengths[np.logical_and( - cmfs.wavelengths >= max(min(cmfs.wavelengths), min(sd.wavelengths)), - cmfs.wavelengths <= min(max(cmfs.wavelengths), max(sd.wavelengths)), - )] - values = sd[wavelengths] + wavelengths = cmfs.wavelengths[ + np.logical_and( + cmfs.wavelengths + >= max(min(cmfs.wavelengths), min(sd.wavelengths)), + cmfs.wavelengths + <= min(max(cmfs.wavelengths), max(sd.wavelengths)), + ) + ] + values = as_float_array(sd[wavelengths]) RGB = XYZ_to_plotting_colourspace( wavelength_to_XYZ(wavelengths, cmfs), - CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['E'], - apply_cctf_encoding=False) + CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["E"], + apply_cctf_encoding=False, + ) if not out_of_gamut_clipping: RGB += np.abs(np.min(RGB)) @@ -167,13 +220,17 @@ def plot_single_sd(sd, y_min, y_max = 0, max(values) + max(values) * margin polygon = Polygon( - np.vstack([ - (x_min, 0), - tstack([wavelengths, values]), - (x_max, 0), - ]), - facecolor='none', - edgecolor='none') + np.vstack( + [ + (x_min, 0), + tstack([wavelengths, values]), + (x_max, 0), + ] + ), + facecolor="none", + edgecolor="none", + zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, + ) axes.add_patch(polygon) padding = 0.1 @@ -182,17 +239,24 @@ def plot_single_sd(sd, height=max(values), width=1 + padding, color=RGB, - align='edge', - clip_path=polygon) - - axes.plot(wavelengths, values, color=CONSTANTS_COLOUR_STYLE.colour.dark) - - settings = { - 'axes': axes, - 'bounding_box': (x_min, x_max, y_min, y_max), - 'title': '{0} - {1}'.format(sd.strict_name, cmfs.strict_name), - 'x_label': 'Wavelength $\\lambda$ (nm)', - 'y_label': 'Spectral Distribution', + align="edge", + clip_path=polygon, + zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, + ) + + axes.plot( + wavelengths, + values, + color=CONSTANTS_COLOUR_STYLE.colour.dark, + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line, + ) + + settings: Dict[str, Any] = { + "axes": axes, + "bounding_box": (x_min, x_max, y_min, y_max), + "title": f"{sd.strict_name} - {cmfs.strict_name}", + "x_label": "Wavelength $\\lambda$ (nm)", + "y_label": "Spectral Distribution", } settings.update(kwargs) @@ -200,53 +264,60 @@ def plot_single_sd(sd, @override_style() -def plot_multi_sds(sds, plot_kwargs=None, **kwargs): +def plot_multi_sds( + sds: Union[ + Sequence[Union[SpectralDistribution, MultiSpectralDistributions]], + MultiSpectralDistributions, + ], + plot_kwargs: Optional[Union[Dict, List[Dict]]] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given spectral distributions. + Plot given spectral distributions. Parameters ---------- - sds : array_like or MultiSpectralDistributions + sds Spectral distributions or multi-spectral distributions to plot. `sds` can be a single :class:`colour.MultiSpectralDistributions` class instance, a list of :class:`colour.MultiSpectralDistributions` class instances or a list of :class:`colour.SpectralDistribution` class instances. - plot_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.plot` definition, used to control - the style of the plotted spectral distributions. ``plot_kwargs`` can be - either a single dictionary applied to all the plotted spectral - distributions with same settings or a sequence of dictionaries with - different settings for each plotted spectral distributions. - The following special keyword arguments can also be used: - - - *illuminant* : unicode or :class:`colour.SpectralDistribution`, the - illuminant used to compute the spectral distributions colours. The - default is the illuminant associated with the whitepoint of the - default plotting colourspace. ``illuminant`` can be of any type or - form supported by the :func:`colour.plotting.filter_cmfs` - definition. - - *cmfs* : unicode, the standard observer colour matching functions - used for computing the spectral distributions colours. ``cmfs`` can - be of any type or form supported by the + plot_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.plot` definition, + used to control the style of the plotted spectral distributions. + `plot_kwargs`` can be either a single dictionary applied to all the + plotted spectral distributions with the same settings or a sequence of + dictionaries with different settings for each plotted spectral + distributions. The following special keyword arguments can also be + used: + + - ``illuminant`` : The illuminant used to compute the spectral + distributions colours. The default is the illuminant associated + with the whitepoint of the default plotting colourspace. + ``illuminant`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - - *normalise_sd_colours* : bool, whether to normalise the computed + - ``cmfs`` : The standard observer colour matching functions used for + computing the spectral distributions colours. ``cmfs`` can be of + any type or form supported by the + :func:`colour.plotting.filter_cmfs` definition. + - ``normalise_sd_colours`` : Whether to normalise the computed spectral distributions colours. The default is *True*. - - *use_sd_colours* : bool, whether to use the computed spectral + - ``use_sd_colours`` : Whether to use the computed spectral distributions colours under the plotting colourspace illuminant. - Alternatively, it is possible to use the :func:`plt.plot` - definition ``color`` argument with pre-computed values. The default - is *True*. + Alternatively, it is possible to use the + :func:`matplotlib.pyplot.plot` definition ``color`` argument with + pre-computed values. The default is *True*. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -285,42 +356,45 @@ def plot_multi_sds(sds, plot_kwargs=None, **kwargs): :alt: plot_multi_sds """ - handle_arguments_deprecation({ - 'ArgumentRemoved': ['normalise_sd_colours', 'use_sds_colours'] - }, **kwargs) - _figure, axes = artist(**kwargs) - sds = sds_and_msds_to_sds(sds) + sds_converted = sds_and_msds_to_sds(sds) - plot_settings_collection = [{ - 'label': - '{0}'.format(sd.strict_name), - 'cmfs': - 'CIE 1931 2 Degree Standard Observer', - 'illuminant': - SDS_ILLUMINANTS[ + plot_settings_collection = [ + { + "label": f"{sd.strict_name}", + "zorder": CONSTANTS_COLOUR_STYLE.zorder.midground_line, + "cmfs": "CIE 1931 2 Degree Standard Observer", + "illuminant": SDS_ILLUMINANTS[ CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint_name ], - 'use_sd_colours': - False, - 'normalise_sd_colours': - False, - } for sd in sds] + "use_sd_colours": False, + "normalise_sd_colours": False, + } + for sd in sds_converted + ] if plot_kwargs is not None: - update_settings_collection(plot_settings_collection, plot_kwargs, - len(sds)) + update_settings_collection( + plot_settings_collection, plot_kwargs, len(sds_converted) + ) x_limit_min, x_limit_max, y_limit_min, y_limit_max = [], [], [], [] - for i, sd in enumerate(sds): + for i, sd in enumerate(sds_converted): plot_settings = plot_settings_collection[i] - cmfs = first_item(filter_cmfs(plot_settings.pop('cmfs')).values()) - illuminant = first_item( - filter_illuminants(plot_settings.pop('illuminant')).values()) - normalise_sd_colours = plot_settings.pop('normalise_sd_colours') - use_sd_colours = plot_settings.pop('use_sd_colours') + cmfs = cast( + MultiSpectralDistributions, + first_item(filter_cmfs(plot_settings.pop("cmfs")).values()), + ) + illuminant = cast( + SpectralDistribution, + first_item( + filter_illuminants(plot_settings.pop("illuminant")).values() + ), + ) + normalise_sd_colours = plot_settings.pop("normalise_sd_colours") + use_sd_colours = plot_settings.pop("use_sd_colours") wavelengths, values = sd.wavelengths, sd.values @@ -331,25 +405,30 @@ def plot_multi_sds(sds, plot_kwargs=None, **kwargs): y_limit_max.append(max(values)) if use_sd_colours: - with domain_range_scale('1'): + with domain_range_scale("1"): XYZ = sd_to_XYZ(sd, cmfs, illuminant) if normalise_sd_colours: XYZ /= XYZ[..., 1] - plot_settings['color'] = np.clip( - XYZ_to_plotting_colourspace(XYZ), 0, 1) + plot_settings["color"] = np.clip( + XYZ_to_plotting_colourspace(XYZ), 0, 1 + ) axes.plot(wavelengths, values, **plot_settings) - bounding_box = (min(x_limit_min), max(x_limit_max), min(y_limit_min), - max(y_limit_max) + max(y_limit_max) * 0.05) - settings = { - 'axes': axes, - 'bounding_box': bounding_box, - 'legend': True, - 'x_label': 'Wavelength $\\lambda$ (nm)', - 'y_label': 'Spectral Distribution', + bounding_box = ( + min(x_limit_min), + max(x_limit_max), + min(y_limit_min), + max(y_limit_max) + np.max(y_limit_max) * 0.05, + ) + settings: Dict[str, Any] = { + "axes": axes, + "bounding_box": bounding_box, + "legend": True, + "x_label": "Wavelength $\\lambda$ (nm)", + "y_label": "Spectral Distribution", } settings.update(kwargs) @@ -357,28 +436,34 @@ def plot_multi_sds(sds, plot_kwargs=None, **kwargs): @override_style() -def plot_single_cmfs(cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): +def plot_single_cmfs( + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given colour matching functions. + Plot given colour matching functions. Parameters ---------- - cmfs : unicode or LMS_ConeFundamentals or \ -RGB_ColourMatchingFunctions or XYZ_ColourMatchingFunctions, optional + cmfs Colour matching functions to plot. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_multi_cmfs`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -392,37 +477,46 @@ def plot_single_cmfs(cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): :alt: plot_single_cmfs """ - cmfs = first_item(filter_cmfs(cmfs).values()) - settings = { - 'title': '{0} - Colour Matching Functions'.format(cmfs.strict_name) + cmfs = cast( + MultiSpectralDistributions, first_item(filter_cmfs(cmfs).values()) + ) + + settings: Dict[str, Any] = { + "title": f"{cmfs.strict_name} - Colour Matching Functions" } settings.update(kwargs) - return plot_multi_cmfs((cmfs, ), **settings) + return plot_multi_cmfs((cmfs,), **settings) @override_style() -def plot_multi_cmfs(cmfs, **kwargs): +def plot_multi_cmfs( + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ], + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given colour matching functions. + Plot given colour matching functions. Parameters ---------- - cmfs : unicode or LMS_ConeFundamentals or \ -RGB_ColourMatchingFunctions or XYZ_ColourMatchingFunctions or array_like + cmfs Colour matching functions to plot. ``cmfs`` elements can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -439,44 +533,56 @@ def plot_multi_cmfs(cmfs, **kwargs): :alt: plot_multi_cmfs """ - cmfs = filter_cmfs(cmfs).values() + cmfs = cast( + List[MultiSpectralDistributions], list(filter_cmfs(cmfs).values()) + ) _figure, axes = artist(**kwargs) - axes.axhline(color=CONSTANTS_COLOUR_STYLE.colour.dark, linestyle='--') + axes.axhline( + color=CONSTANTS_COLOUR_STYLE.colour.dark, + linestyle="--", + zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_line, + ) x_limit_min, x_limit_max, y_limit_min, y_limit_max = [], [], [], [] for i, cmfs_i in enumerate(cmfs): - for j, RGB in enumerate([(1, 0, 0), (0, 1, 0), (0, 0, 1)]): + for j, RGB in enumerate( + as_float_array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) + ): RGB = [reduce(lambda y, _: y * 0.5, range(i), x) for x in RGB] values = cmfs_i.values[:, j] shape = cmfs_i.shape x_limit_min.append(shape.start) x_limit_max.append(shape.end) - y_limit_min.append(min(values)) - y_limit_max.append(max(values)) + y_limit_min.append(np.min(values)) + y_limit_max.append(np.max(values)) axes.plot( cmfs_i.wavelengths, values, color=RGB, - label='{0} - {1}'.format(cmfs_i.strict_labels[j], - cmfs_i.strict_name)) - - bounding_box = (min(x_limit_min), max(x_limit_max), - min(y_limit_min) - abs(min(y_limit_min)) * 0.05, - max(y_limit_max) + abs(max(y_limit_max)) * 0.05) - title = '{0} - Colour Matching Functions'.format(', '.join( - [cmfs_i.strict_name for cmfs_i in cmfs])) - - settings = { - 'axes': axes, - 'bounding_box': bounding_box, - 'legend': True, - 'title': title, - 'x_label': 'Wavelength $\\lambda$ (nm)', - 'y_label': 'Tristimulus Values', + label=f"{cmfs_i.strict_labels[j]} - {cmfs_i.strict_name}", + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line, + ) + + bounding_box = ( + min(x_limit_min), + max(x_limit_max), + min(y_limit_min) - np.abs(np.min(y_limit_min)) * 0.05, + max(y_limit_max) + np.abs(np.max(y_limit_max)) * 0.05, + ) + cmfs_strict_names = ", ".join([cmfs_i.strict_name for cmfs_i in cmfs]) + title = f"{cmfs_strict_names} - Colour Matching Functions" + + settings: Dict[str, Any] = { + "axes": axes, + "bounding_box": bounding_box, + "legend": True, + "title": title, + "x_label": "Wavelength $\\lambda$ (nm)", + "y_label": "Tristimulus Values", } settings.update(kwargs) @@ -484,39 +590,39 @@ def plot_multi_cmfs(cmfs, **kwargs): @override_style() -def plot_single_illuminant_sd(illuminant, - cmfs='CIE 1931 2 Degree Standard Observer', - **kwargs): +def plot_single_illuminant_sd( + illuminant: Union[SpectralDistribution, str], + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given single illuminant spectral distribution. + Plot given single illuminant spectral distribution. Parameters ---------- - illuminant : unicode or LMS_ConeFundamentals or \ -RGB_ColourMatchingFunctions or XYZ_ColourMatchingFunctions, optional + illuminant Illuminant to plot. ``illuminant`` can be of any type or form supported by the :func:`colour.plotting.filter_illuminants` definition. - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectrum domain and colours. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_single_sd`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - out_of_gamut_clipping : bool, optional - {:func:`colour.plotting.plot_single_sd`}, - Whether to clip out of gamut colours otherwise, the colours will be - offset by the absolute minimal colour leading to a rendering on - gray background, less saturated and smoother. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. References @@ -533,40 +639,48 @@ def plot_single_illuminant_sd(illuminant, :alt: plot_single_illuminant_sd """ - cmfs = first_item(filter_cmfs(cmfs).values()) - title = 'Illuminant {0} - {1}'.format(illuminant, cmfs.strict_name) + cmfs = cast( + MultiSpectralDistributions, first_item(filter_cmfs(cmfs).values()) + ) + + title = f"Illuminant {illuminant} - {cmfs.strict_name}" illuminant = first_item(filter_illuminants(illuminant).values()) - settings = {'title': title, 'y_label': 'Relative Power'} + settings: Dict[str, Any] = {"title": title, "y_label": "Relative Power"} settings.update(kwargs) return plot_single_sd(illuminant, **settings) @override_style() -def plot_multi_illuminant_sds(illuminants, **kwargs): +def plot_multi_illuminant_sds( + illuminants: Union[ + SpectralDistribution, str, Sequence[Union[SpectralDistribution, str]] + ], + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given illuminants spectral distributions. + Plot given illuminants spectral distributions. Parameters ---------- - illuminants : unicode or SpectralDistribution or array_like + illuminants Illuminants to plot. ``illuminants`` elements can be of any type or form supported by the :func:`colour.plotting.filter_illuminants` definition. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_multi_sds`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -579,61 +693,73 @@ def plot_multi_illuminant_sds(illuminants, **kwargs): :alt: plot_multi_illuminant_sds """ - if 'plot_kwargs' not in kwargs: - kwargs['plot_kwargs'] = {} + if "plot_kwargs" not in kwargs: + kwargs["plot_kwargs"] = {} - SD_E = SDS_ILLUMINANTS['E'] - if isinstance(kwargs['plot_kwargs'], dict): - kwargs['plot_kwargs']['illuminant'] = SD_E + SD_E = SDS_ILLUMINANTS["E"] + if isinstance(kwargs["plot_kwargs"], dict): + kwargs["plot_kwargs"]["illuminant"] = SD_E else: - for i in range(len(kwargs['plot_kwargs'])): - kwargs['plot_kwargs'][i]['illuminant'] = SD_E + for i in range(len(kwargs["plot_kwargs"])): + kwargs["plot_kwargs"][i]["illuminant"] = SD_E - illuminants = filter_illuminants(illuminants).values() + illuminants = cast( + List[SpectralDistribution], + list(filter_illuminants(illuminants).values()), + ) - title = '{0} - Illuminants Spectral Distributions'.format(', '.join( - [illuminant.strict_name for illuminant in illuminants])) + illuminant_strict_names = ", ".join( + [illuminant.strict_name for illuminant in illuminants] + ) + title = f"{illuminant_strict_names} - Illuminants Spectral Distributions" - settings = {'title': title, 'y_label': 'Relative Power'} + settings: Dict[str, Any] = {"title": title, "y_label": "Relative Power"} settings.update(kwargs) return plot_multi_sds(illuminants, **settings) -@override_style(**{ - 'ytick.left': False, - 'ytick.labelleft': False, -}) -def plot_visible_spectrum(cmfs='CIE 1931 2 Degree Standard Observer', - out_of_gamut_clipping=True, - **kwargs): +@override_style( + **{ + "ytick.left": False, + "ytick.labelleft": False, + } +) +def plot_visible_spectrum( + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + out_of_gamut_clipping: Boolean = True, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the visible colours spectrum using given standard observer *CIE XYZ* + Plot the visible colours spectrum using given standard observer *CIE XYZ* colour matching functions. Parameters ---------- - cmfs : unicode or LMS_ConeFundamentals or \ -RGB_ColourMatchingFunctions or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectrum domain and colours. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - out_of_gamut_clipping : bool, optional + out_of_gamut_clipping Whether to clip out of gamut colours otherwise, the colours will be offset by the absolute minimal colour leading to a rendering on gray background, less saturated and smoother. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_single_sd`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. References @@ -650,28 +776,31 @@ def plot_visible_spectrum(cmfs='CIE 1931 2 Degree Standard Observer', :alt: plot_visible_spectrum """ - cmfs = first_item(filter_cmfs(cmfs).values()) + cmfs = cast( + MultiSpectralDistributions, first_item(filter_cmfs(cmfs).values()) + ) bounding_box = (min(cmfs.wavelengths), max(cmfs.wavelengths), 0, 1) - settings = {'bounding_box': bounding_box, 'y_label': None} + settings: Dict[str, Any] = {"bounding_box": bounding_box, "y_label": None} settings.update(kwargs) - settings['standalone'] = False + settings["standalone"] = False _figure, axes = plot_single_sd( sd_ones(cmfs.shape), cmfs=cmfs, out_of_gamut_clipping=out_of_gamut_clipping, - **settings) + **settings, + ) # Removing wavelength line as it doubles with the axes spine. axes.lines.pop(0) settings = { - 'axes': axes, - 'standalone': True, - 'title': 'The Visible Spectrum - {0}'.format(cmfs.strict_name), - 'x_label': 'Wavelength $\\lambda$ (nm)', + "axes": axes, + "standalone": True, + "title": f"The Visible Spectrum - {cmfs.strict_name}", + "x_label": "Wavelength $\\lambda$ (nm)", } settings.update(kwargs) @@ -679,27 +808,29 @@ def plot_visible_spectrum(cmfs='CIE 1931 2 Degree Standard Observer', @override_style() -def plot_single_lightness_function(function, **kwargs): +def plot_single_lightness_function( + function: Union[Callable, str], **kwargs: Any +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given *Lightness* function. + Plot given *Lightness* function. Parameters ---------- - function : unicode or object + function *Lightness* function to plot. ``function`` can be of any type or form supported by the :func:`colour.plotting.filter_passthrough` definition. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_multi_functions`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -712,35 +843,38 @@ def plot_single_lightness_function(function, **kwargs): :alt: plot_single_lightness_function """ - settings = {'title': '{0} - Lightness Function'.format(function)} + settings: Dict[str, Any] = {"title": f"{function} - Lightness Function"} settings.update(kwargs) - return plot_multi_lightness_functions((function, ), **settings) + return plot_multi_lightness_functions((function,), **settings) @override_style() -def plot_multi_lightness_functions(functions, **kwargs): +def plot_multi_lightness_functions( + functions: Union[Callable, str, Sequence[Union[Callable, str]]], + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given *Lightness* functions. + Plot given *Lightness* functions. Parameters ---------- - functions : unicode or object or array_like + functions *Lightness* functions to plot. ``functions`` elements can be of any type or form supported by the :func:`colour.plotting.filter_passthrough` definition. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_multi_functions`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -754,42 +888,44 @@ def plot_multi_lightness_functions(functions, **kwargs): :alt: plot_multi_lightness_functions """ - functions = filter_passthrough(LIGHTNESS_METHODS, functions) + functions_filtered = filter_passthrough(LIGHTNESS_METHODS, functions) - settings = { - 'bounding_box': (0, 1, 0, 1), - 'legend': True, - 'title': '{0} - Lightness Functions'.format(', '.join(functions)), - 'x_label': 'Normalised Relative Luminance Y', - 'y_label': 'Normalised Lightness', + settings: Dict[str, Any] = { + "bounding_box": (0, 1, 0, 1), + "legend": True, + "title": f"{', '.join(functions_filtered)} - Lightness Functions", + "x_label": "Normalised Relative Luminance Y", + "y_label": "Normalised Lightness", } settings.update(kwargs) - with domain_range_scale(1): - return plot_multi_functions(functions, **settings) + with domain_range_scale("1"): + return plot_multi_functions(functions_filtered, **settings) @override_style() -def plot_single_luminance_function(function, **kwargs): +def plot_single_luminance_function( + function: Union[Callable, str], **kwargs: Any +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given *Luminance* function. + Plot given *Luminance* function. Parameters ---------- - function : unicode or object, optional + function *Luminance* function to plot. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_multi_functions`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -802,35 +938,38 @@ def plot_single_luminance_function(function, **kwargs): :alt: plot_single_luminance_function """ - settings = {'title': '{0} - Luminance Function'.format(function)} + settings: Dict[str, Any] = {"title": f"{function} - Luminance Function"} settings.update(kwargs) - return plot_multi_luminance_functions((function, ), **settings) + return plot_multi_luminance_functions((function,), **settings) @override_style() -def plot_multi_luminance_functions(functions, **kwargs): +def plot_multi_luminance_functions( + functions: Union[Callable, str, Sequence[Union[Callable, str]]], + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given *Luminance* functions. + Plot given *Luminance* functions. Parameters ---------- - functions : unicode or object or array_like + functions *Luminance* functions to plot. ``functions`` elements can be of any type or form supported by the :func:`colour.plotting.filter_passthrough` definition. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_multi_functions`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -844,52 +983,57 @@ def plot_multi_luminance_functions(functions, **kwargs): :alt: plot_multi_luminance_functions """ - functions = filter_passthrough(LUMINANCE_METHODS, functions) + functions_filtered = filter_passthrough(LUMINANCE_METHODS, functions) - settings = { - 'bounding_box': (0, 1, 0, 1), - 'legend': True, - 'title': '{0} - Luminance Functions'.format(', '.join(functions)), - 'x_label': 'Normalised Munsell Value / Lightness', - 'y_label': 'Normalised Relative Luminance Y', + settings: Dict[str, Any] = { + "bounding_box": (0, 1, 0, 1), + "legend": True, + "title": f"{', '.join(functions_filtered)} - Luminance Functions", + "x_label": "Normalised Munsell Value / Lightness", + "y_label": "Normalised Relative Luminance Y", } settings.update(kwargs) - with domain_range_scale(1): - return plot_multi_functions(functions, **settings) + with domain_range_scale("1"): + return plot_multi_functions(functions_filtered, **settings) @override_style() def plot_blackbody_spectral_radiance( - temperature=3500, - cmfs='CIE 1931 2 Degree Standard Observer', - blackbody='VY Canis Major', - **kwargs): + temperature: Floating = 3500, + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + blackbody: str = "VY Canis Major", + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given blackbody spectral radiance. + Plot given blackbody spectral radiance. Parameters ---------- - temperature : numeric, optional + temperature Blackbody temperature. - cmfs : unicode, optional + cmfs Standard observer colour matching functions used for computing the spectrum domain and colours. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - blackbody : unicode, optional + blackbody Blackbody name. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_single_sd`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -907,78 +1051,86 @@ def plot_blackbody_spectral_radiance( figure.subplots_adjust(hspace=CONSTANTS_COLOUR_STYLE.geometry.short / 2) - cmfs = first_item(filter_cmfs(cmfs).values()) + cmfs = cast( + MultiSpectralDistributions, first_item(filter_cmfs(cmfs).values()) + ) sd = sd_blackbody(temperature, cmfs.shape) axes = figure.add_subplot(211) - settings = { - 'axes': axes, - 'title': '{0} - Spectral Radiance'.format(blackbody), - 'y_label': 'W / (sr m$^2$) / m', + settings: Dict[str, Any] = { + "axes": axes, + "title": f"{blackbody} - Spectral Radiance", + "y_label": "W / (sr m$^2$) / m", } settings.update(kwargs) - settings['standalone'] = False + settings["standalone"] = False plot_single_sd(sd, cmfs.name, **settings) axes = figure.add_subplot(212) - with domain_range_scale('1'): + with domain_range_scale("1"): XYZ = sd_to_XYZ(sd, cmfs) RGB = normalise_maximum(XYZ_to_plotting_colourspace(XYZ)) settings = { - 'axes': axes, - 'aspect': None, - 'title': '{0} - Colour'.format(blackbody), - 'x_label': '{0}K'.format(temperature), - 'y_label': '', - 'x_ticker': False, - 'y_ticker': False, + "axes": axes, + "aspect": None, + "title": f"{blackbody} - Colour", + "x_label": f"{temperature}K", + "y_label": "", + "x_ticker": False, + "y_ticker": False, } settings.update(kwargs) - settings['standalone'] = False + settings["standalone"] = False - figure, axes = plot_single_colour_swatch( - ColourSwatch(name='', RGB=RGB), **settings) + figure, axes = plot_single_colour_swatch(RGB, **settings) - settings = {'axes': axes, 'standalone': True} + settings = {"axes": axes, "standalone": True} settings.update(kwargs) return render(**settings) -@override_style(**{ - 'ytick.left': False, - 'ytick.labelleft': False, -}) +@override_style( + **{ + "ytick.left": False, + "ytick.labelleft": False, + } +) def plot_blackbody_colours( - shape=SpectralShape(150, 12500, 50), - cmfs='CIE 1931 2 Degree Standard Observer', - **kwargs): + shape: SpectralShape = SpectralShape(150, 12500, 50), + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots blackbody colours. + Plot blackbody colours. Parameters ---------- - shape : SpectralShape, optional + shape Spectral shape to use as plot boundaries. - cmfs : unicode, optional + cmfs Standard observer colour matching functions used for computing the blackbody colours. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -994,7 +1146,9 @@ def plot_blackbody_colours( _figure, axes = artist(**kwargs) - cmfs = first_item(filter_cmfs(cmfs).values()) + cmfs = cast( + MultiSpectralDistributions, first_item(filter_cmfs(cmfs).values()) + ) RGB = [] temperatures = [] @@ -1002,7 +1156,7 @@ def plot_blackbody_colours( for temperature in shape: sd = sd_blackbody(temperature, cmfs.shape) - with domain_range_scale('1'): + with domain_range_scale("1"): XYZ = sd_to_XYZ(sd, cmfs) RGB.append(normalise_maximum(XYZ_to_plotting_colourspace(XYZ))) @@ -1017,14 +1171,16 @@ def plot_blackbody_colours( height=1, width=shape.interval + (padding * shape.interval), color=RGB, - align='edge') - - settings = { - 'axes': axes, - 'bounding_box': (x_min, x_max, y_min, y_max), - 'title': 'Blackbody Colours', - 'x_label': 'Temperature K', - 'y_label': None, + align="edge", + zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, + ) + + settings: Dict[str, Any] = { + "axes": axes, + "bounding_box": (x_min, x_max, y_min, y_max), + "title": "Blackbody Colours", + "x_label": "Temperature K", + "y_label": None, } settings.update(kwargs) diff --git a/colour/plotting/common.py b/colour/plotting/common.py index fc42ffa9dc..c9e20c0475 100644 --- a/colour/plotting/common.py +++ b/colour/plotting/common.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Common Plotting =============== @@ -23,226 +22,268 @@ - :func:`colour.plotting.plot_image` """ -from __future__ import division +from __future__ import annotations import functools import itertools import matplotlib import matplotlib.cm +import matplotlib.patches as Patch import matplotlib.pyplot as plt import matplotlib.ticker import numpy as np import re -import six -from collections import OrderedDict, namedtuple +from dataclasses import dataclass, field from functools import partial from matplotlib.colors import LinearSegmentedColormap -from colour.characterisation import CCS_COLOURCHECKERS -from colour.colorimetry import MSDS_CMFS, SDS_ILLUMINANTS, SDS_LIGHT_SOURCES -from colour.models import RGB_COLOURSPACES, XYZ_to_RGB -from colour.utilities import (CaseInsensitiveMapping, Structure, - as_float_array, is_sibling, is_string, - filter_mapping, runtime_warning) -from colour.utilities.deprecation import handle_arguments_deprecation - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.characterisation import CCS_COLOURCHECKERS, ColourChecker +from colour.colorimetry import ( + MultiSpectralDistributions, + MSDS_CMFS, + SDS_ILLUMINANTS, + SDS_LIGHT_SOURCES, + SpectralDistribution, +) +from colour.hints import ( + Any, + ArrayLike, + Boolean, + Callable, + Dict, + Floating, + Integer, + List, + Literal, + Mapping, + NDArray, + Optional, + RegexFlag, + Sequence, + Tuple, + TypedDict, + Union, + cast, +) +from colour.models import RGB_COLOURSPACES, RGB_Colourspace, XYZ_to_RGB +from colour.utilities import ( + CaseInsensitiveMapping, + Structure, + as_float_array, + attest, + first_item, + is_sibling, + is_string, + filter_mapping, + optional, + runtime_warning, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'CONSTANTS_COLOUR_STYLE', 'CONSTANTS_ARROW_STYLE', 'colour_style', - 'override_style', 'XYZ_to_plotting_colourspace', 'ColourSwatch', - 'colour_cycle', 'artist', 'camera', 'render', 'label_rectangles', - 'uniform_axes3d', 'filter_passthrough', 'filter_RGB_colourspaces', - 'filter_cmfs', 'filter_illuminants', 'filter_colour_checkers', - 'update_settings_collection', 'plot_single_colour_swatch', - 'plot_multi_colour_swatches', 'plot_single_function', - 'plot_multi_functions', 'plot_image' + "CONSTANTS_COLOUR_STYLE", + "CONSTANTS_ARROW_STYLE", + "colour_style", + "override_style", + "XYZ_to_plotting_colourspace", + "ColourSwatch", + "colour_cycle", + "KwargsArtist", + "artist", + "KwargsCamera", + "camera", + "KwargsRender", + "render", + "label_rectangles", + "uniform_axes3d", + "filter_passthrough", + "filter_RGB_colourspaces", + "filter_cmfs", + "filter_illuminants", + "filter_colour_checkers", + "update_settings_collection", + "plot_single_colour_swatch", + "plot_multi_colour_swatches", + "plot_single_function", + "plot_multi_functions", + "plot_image", ] -CONSTANTS_COLOUR_STYLE = Structure( +CONSTANTS_COLOUR_STYLE: Structure = Structure( **{ - 'colour': - Structure( - **{ - 'darkest': - '#111111', - 'darker': - '#222222', - 'dark': - '#333333', - 'dim': - '#505050', - 'average': - '#808080', - 'light': - '#D5D5D5', - 'bright': - '#EEEEEE', - 'brighter': - '#F0F0F0', - 'brightest': - '#F5F5F5', - 'cycle': ( - '#F44336', - '#9C27B0', - '#3F51B5', - '#03A9F4', - '#009688', - '#8BC34A', - '#FFEB3B', - '#FF9800', - '#795548', - '#607D8B', + "colour": Structure( + **{ + "darkest": "#111111", + "darker": "#222222", + "dark": "#333333", + "dim": "#505050", + "average": "#808080", + "light": "#D5D5D5", + "bright": "#EEEEEE", + "brighter": "#F0F0F0", + "brightest": "#F5F5F5", + "cycle": ( + "#F44336", + "#9C27B0", + "#3F51B5", + "#03A9F4", + "#009688", + "#8BC34A", + "#FFEB3B", + "#FF9800", + "#795548", + "#607D8B", + ), + "map": LinearSegmentedColormap.from_list( + "colour", + ( + "#F44336", + "#9C27B0", + "#3F51B5", + "#03A9F4", + "#009688", + "#8BC34A", + "#FFEB3B", + "#FF9800", + "#795548", + "#607D8B", ), - 'map': - LinearSegmentedColormap.from_list( - 'colour', ( - '#F44336', - '#9C27B0', - '#3F51B5', - '#03A9F4', - '#009688', - '#8BC34A', - '#FFEB3B', - '#FF9800', - '#795548', - '#607D8B', - )), - 'colourspace': - RGB_COLOURSPACES['sRGB'] - }), - 'opacity': - Structure(**{ - 'high': 0.75, - 'low': 0.25 - }), - 'hatch': - Structure(**{'patterns': ( - '\\\\', - 'o', - 'x', - '.', - '*', - '//', - )}), - 'geometry': - Structure(**{ - 'long': 5, - 'short': 1 - }) - }) -""" -Various defaults settings used across the plotting sub-package. - -CONSTANTS_COLOUR_STYLE : Structure -""" + ), + "colourspace": RGB_COLOURSPACES["sRGB"], + } + ), + "opacity": Structure(**{"high": 0.75, "medium": 0.5, "low": 0.25}), + "geometry": Structure(**{"long": 5, "medium": 2.5, "short": 1}), + "hatch": Structure( + **{ + "patterns": ( + "\\\\", + "o", + "x", + ".", + "*", + "//", + ) + } + ), + "zorder": Structure( + { + "background_polygon": -140, + "background_scatter": -130, + "background_line": -120, + "background_annotation": -110, + "background_label": -100, + "midground_polygon": -90, + "midground_scatter": -80, + "midground_line": -70, + "midground_annotation": -60, + "midground_label": -50, + "foreground_polygon": -40, + "foreground_scatter": -30, + "foreground_line": -20, + "foreground_annotation": -10, + "foreground_label": 0, + } + ), + } +) +"""Various defaults settings used across the plotting sub-package.""" -CONSTANTS_ARROW_STYLE = Structure( +CONSTANTS_ARROW_STYLE: Structure = Structure( **{ - 'color': CONSTANTS_COLOUR_STYLE.colour.dark, - 'headwidth': CONSTANTS_COLOUR_STYLE.geometry.short * 4, - 'headlength': CONSTANTS_COLOUR_STYLE.geometry.long, - 'width': CONSTANTS_COLOUR_STYLE.geometry.short * 0.5, - 'shrink': CONSTANTS_COLOUR_STYLE.geometry.short * 0.1, - 'connectionstyle': 'arc3,rad=-0.2', - }) -""" -Annotation arrow settings used across the plotting sub-package. - -CONSTANTS_ARROW_STYLE : Structure -""" + "color": CONSTANTS_COLOUR_STYLE.colour.dark, + "headwidth": CONSTANTS_COLOUR_STYLE.geometry.short * 4, + "headlength": CONSTANTS_COLOUR_STYLE.geometry.long, + "width": CONSTANTS_COLOUR_STYLE.geometry.short * 0.5, + "shrink": CONSTANTS_COLOUR_STYLE.geometry.short * 0.1, + "connectionstyle": "arc3,rad=-0.2", + } +) +"""Annotation arrow settings used across the plotting sub-package.""" -def colour_style(use_style=True): +def colour_style(use_style: Boolean = True) -> Dict: """ - Returns *Colour* plotting style. + Return *Colour* plotting style. Parameters ---------- - use_style : bool, optional + use_style Whether to use the style and load it into *Matplotlib*. Returns ------- - dict + :class:`dict` *Colour* style. """ constants = CONSTANTS_COLOUR_STYLE style = { # Figure Size Settings - 'figure.figsize': (12.80, 7.20), - 'figure.dpi': 100, - 'savefig.dpi': 100, - 'savefig.bbox': 'standard', - + "figure.figsize": (12.80, 7.20), + "figure.dpi": 100, + "savefig.dpi": 100, + "savefig.bbox": "standard", # Font Settings # 'font.size': 12, - 'axes.titlesize': 'x-large', - 'axes.labelsize': 'larger', - 'legend.fontsize': 'small', - 'xtick.labelsize': 'medium', - 'ytick.labelsize': 'medium', - + "axes.titlesize": "x-large", + "axes.labelsize": "larger", + "legend.fontsize": "small", + "xtick.labelsize": "medium", + "ytick.labelsize": "medium", # Text Settings - 'text.color': constants.colour.darkest, - + "text.color": constants.colour.darkest, # Tick Settings - 'xtick.top': False, - 'xtick.bottom': True, - 'ytick.right': False, - 'ytick.left': True, - 'xtick.minor.visible': True, - 'ytick.minor.visible': True, - 'xtick.direction': 'out', - 'ytick.direction': 'out', - 'xtick.major.size': constants.geometry.long * 1.25, - 'xtick.minor.size': constants.geometry.long * 0.75, - 'ytick.major.size': constants.geometry.long * 1.25, - 'ytick.minor.size': constants.geometry.long * 0.75, - 'xtick.major.width': constants.geometry.short, - 'xtick.minor.width': constants.geometry.short, - 'ytick.major.width': constants.geometry.short, - 'ytick.minor.width': constants.geometry.short, - + "xtick.top": False, + "xtick.bottom": True, + "ytick.right": False, + "ytick.left": True, + "xtick.minor.visible": True, + "ytick.minor.visible": True, + "xtick.direction": "out", + "ytick.direction": "out", + "xtick.major.size": constants.geometry.long * 1.25, + "xtick.minor.size": constants.geometry.long * 0.75, + "ytick.major.size": constants.geometry.long * 1.25, + "ytick.minor.size": constants.geometry.long * 0.75, + "xtick.major.width": constants.geometry.short, + "xtick.minor.width": constants.geometry.short, + "ytick.major.width": constants.geometry.short, + "ytick.minor.width": constants.geometry.short, # Spine Settings - 'axes.linewidth': constants.geometry.short, - 'axes.edgecolor': constants.colour.dark, - + "axes.linewidth": constants.geometry.short, + "axes.edgecolor": constants.colour.dark, # Title Settings - 'axes.titlepad': plt.rcParams['font.size'] * 0.75, - + "axes.titlepad": plt.rcParams["font.size"] * 0.75, # Axes Settings - 'axes.facecolor': constants.colour.brightest, - 'axes.grid': True, - 'axes.grid.which': 'major', - 'axes.grid.axis': 'both', - + "axes.facecolor": constants.colour.brightest, + "axes.grid": True, + "axes.grid.which": "major", + "axes.grid.axis": "both", # Grid Settings - 'axes.axisbelow': True, - 'grid.linewidth': constants.geometry.short * 0.5, - 'grid.linestyle': '--', - 'grid.color': constants.colour.light, - + "axes.axisbelow": True, + "grid.linewidth": constants.geometry.short * 0.5, + "grid.linestyle": "--", + "grid.color": constants.colour.light, # Legend - 'legend.frameon': True, - 'legend.framealpha': constants.opacity.high, - 'legend.fancybox': False, - 'legend.facecolor': constants.colour.brighter, - 'legend.borderpad': constants.geometry.short * 0.5, - + "legend.frameon": True, + "legend.framealpha": constants.opacity.high, + "legend.fancybox": False, + "legend.facecolor": constants.colour.brighter, + "legend.borderpad": constants.geometry.short * 0.5, # Lines - 'lines.linewidth': constants.geometry.short, - 'lines.markersize': constants.geometry.short * 3, - 'lines.markeredgewidth': constants.geometry.short * 0.75, - + "lines.linewidth": constants.geometry.short, + "lines.markersize": constants.geometry.short * 3, + "lines.markeredgewidth": constants.geometry.short * 0.75, # Cycle - 'axes.prop_cycle': matplotlib.cycler(color=constants.colour.cycle), + "axes.prop_cycle": matplotlib.cycler(color=constants.colour.cycle), } if use_style: @@ -251,18 +292,18 @@ def colour_style(use_style=True): return style -def override_style(**kwargs): +def override_style(**kwargs: Any) -> Callable: """ - Decorator for overriding *Matplotlib* style. + Decorate a function to override *Matplotlib* style. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs Keywords arguments. Returns ------- - object + Callable Examples -------- @@ -273,18 +314,14 @@ def override_style(**kwargs): >>> f() # doctest: +SKIP """ - keywords = kwargs.copy() + keywords = dict(kwargs) - def wrapper(function): - """ - Wrapper for given function. - """ + def wrapper(function: Callable) -> Callable: + """Wrap given function wrapper.""" @functools.wraps(function) - def wrapped(*args, **kwargs): - """ - Wrapped function. - """ + def wrapped(*args: Any, **kwargs: Any) -> Any: + """Wrap given function.""" keywords.update(kwargs) @@ -302,32 +339,47 @@ def wrapped(*args, **kwargs): return wrapper -def XYZ_to_plotting_colourspace(XYZ, - illuminant=RGB_COLOURSPACES['sRGB'].whitepoint, - chromatic_adaptation_transform='CAT02', - apply_cctf_encoding=True): +def XYZ_to_plotting_colourspace( + XYZ: ArrayLike, + illuminant: ArrayLike = RGB_COLOURSPACES["sRGB"].whitepoint, + chromatic_adaptation_transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", + apply_cctf_encoding: Boolean = True, +) -> NDArray: """ - Converts from *CIE XYZ* tristimulus values to the default plotting + Convert from *CIE XYZ* tristimulus values to the default plotting colourspace. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. - illuminant : array_like, optional + illuminant Source illuminant chromaticity coordinates. - chromatic_adaptation_transform : unicode, optional - **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', - 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02 Brill 2008', - 'Bianco 2010', 'Bianco PC 2010'}**, + chromatic_adaptation_transform *Chromatic adaptation* transform. - apply_cctf_encoding : bool, optional - Apply the default ploting colourspace encoding colour component + apply_cctf_encoding + Apply the default plotting colourspace encoding colour component transfer function / opto-electronic transfer function. Returns ------- - ndarray + :class:`numpy.ndarray` Default plotting colourspace colour array. Examples @@ -339,56 +391,57 @@ def XYZ_to_plotting_colourspace(XYZ, """ return XYZ_to_RGB( - XYZ, illuminant, CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint, + XYZ, + illuminant, + CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint, CONSTANTS_COLOUR_STYLE.colour.colourspace.matrix_XYZ_to_RGB, chromatic_adaptation_transform, CONSTANTS_COLOUR_STYLE.colour.colourspace.cctf_encoding - if apply_cctf_encoding else None) + if apply_cctf_encoding + else None, + ) -class ColourSwatch(namedtuple('ColourSwatch', ('name', 'RGB'))): +@dataclass +class ColourSwatch: """ - Defines a data structure for a colour swatch. + Define a data structure for a colour swatch. Parameters ---------- - name : unicode, optional - Colour name. - RGB : array_like, optional + RGB RGB Colour. + name + Colour name. """ - def __new__(cls, name=None, RGB=None): - """ - Returns a new instance of the :class:`colour.plotting.ColourSwatch` - class. - """ - - return super(ColourSwatch, cls).__new__(cls, name, RGB) + RGB: ArrayLike + name: Optional[str] = field(default_factory=lambda: None) -def colour_cycle(**kwargs): +def colour_cycle(**kwargs: Any) -> itertools.cycle: """ - Returns a colour cycle iterator using given colour map. + Return a colour cycle iterator using given colour map. Other Parameters ---------------- - colour_cycle_map : unicode or LinearSegmentedColormap, optional + colour_cycle_map Matplotlib colourmap name. - colour_cycle_count : int, optional + colour_cycle_count Colours count to pick in the colourmap. Returns ------- - cycle + :class:`itertools.cycle` Colour cycle iterator. """ settings = Structure( **{ - 'colour_cycle_map': CONSTANTS_COLOUR_STYLE.colour.map, - 'colour_cycle_count': len(CONSTANTS_COLOUR_STYLE.colour.cycle) - }) + "colour_cycle_map": CONSTANTS_COLOUR_STYLE.colour.map, + "colour_cycle_count": len(CONSTANTS_COLOUR_STYLE.colour.cycle), + } + ) settings.update(kwargs) samples = np.linspace(0, 1, settings.colour_cycle_count) @@ -400,28 +453,44 @@ def colour_cycle(**kwargs): return itertools.cycle(cycle) -def artist(**kwargs): +class KwargsArtist(TypedDict): """ - Returns the current figure and its axes or creates a new one. + Define the keyword argument types for the :func:`colour.plotting.artist` + definition. - Other Parameters - ---------------- - axes : Axes, optional + Parameters + ---------- + axes Axes that will be passed through without creating a new figure. - uniform : unicode, optional + uniform Whether to create the figure with an equal aspect ratio. + """ + + axes: plt.Axes + uniform: Boolean + + +def artist(**kwargs: Union[KwargsArtist, Any]) -> Tuple[plt.Figure, plt.Axes]: + """ + Return the current figure and its axes or creates a new one. + + Other Parameters + ---------------- + kwargs + {:func:`colour.plotting.common.KwargsArtist`}, + See the documentation of the previously listed class. Returns ------- - tuple + :class:`tuple` Current figure and axes. """ - width, height = plt.rcParams['figure.figsize'] + width, height = plt.rcParams["figure.figsize"] - figure_size = (width, width) if kwargs.get('uniform') else (width, height) + figure_size = (width, width) if kwargs.get("uniform") else (width, height) - axes = kwargs.get('axes') + axes = kwargs.get("axes") if axes is None: figure = plt.figure(figsize=figure_size) @@ -430,40 +499,57 @@ def artist(**kwargs): return plt.gcf(), axes -def camera(**kwargs): +class KwargsCamera(TypedDict): """ - Sets the camera settings. + Define the keyword argument types for the :func:`colour.plotting.camera` + definition. - Other Parameters - ---------------- - figure : Figure, optional + Parameters + ---------- + figure Figure to apply the render elements onto. - axes : Axes, optional + axes Axes to apply the render elements onto. - azimuth : numeric, optional + azimuth Camera azimuth. - camera_aspect : unicode, optional - Matplotlib axes aspect. Default is *equal*. - elevation : numeric, optional + elevation Camera elevation. + camera_aspect + Matplotlib axes aspect. Default is *equal*. + """ + + figure: plt.Figure + axes: plt.Axes + azimuth: Optional[Floating] + elevation: Optional[Floating] + camera_aspect: Union[Literal["equal"], str] + + +def camera(**kwargs: Union[KwargsCamera, Any]) -> Tuple[plt.Figure, plt.Axes]: + """ + Set the camera settings. + + Other Parameters + ---------------- + kwargs + {:func:`colour.plotting.common.KwargsCamera`}, + See the documentation of the previously listed class. Returns ------- - tuple + :class:`tuple` Current figure and axes. """ - figure = kwargs.get('figure', plt.gcf()) - axes = kwargs.get('axes', plt.gca()) + figure = cast(plt.Figure, kwargs.get("figure", plt.gcf())) + axes = cast(plt.Axes, kwargs.get("axes", plt.gca())) - settings = Structure(**{ - 'camera_aspect': 'equal', - 'elevation': None, - 'azimuth': None - }) + settings = Structure( + **{"camera_aspect": "equal", "elevation": None, "azimuth": None} + ) settings.update(kwargs) - if settings.camera_aspect == 'equal': + if settings.camera_aspect == "equal": uniform_axes3d(axes=axes) axes.view_init(elev=settings.elevation, azim=settings.azimuth) @@ -471,76 +557,110 @@ def camera(**kwargs): return figure, axes -def render(**kwargs): +class KwargsRender(TypedDict): """ - Renders the current figure while adjusting various settings such as the - bounding box, the title or background transparency. + Define the keyword argument types for the :func:`colour.plotting.render` + definition. - Other Parameters - ---------------- - figure : Figure, optional + Parameters + ---------- + figure Figure to apply the render elements onto. - axes : Axes, optional + axes Axes to apply the render elements onto. - filename : unicode, optional + filename Figure will be saved using given ``filename`` argument. - standalone : bool, optional - Whether to show the figure and call :func:`plt.show` definition. - aspect : unicode, optional + standalone + Whether to show the figure and call :func:`matplotlib.pyplot.show` + definition. + aspect Matplotlib axes aspect. - axes_visible : bool, optional + axes_visible Whether the axes are visible. Default is *True*. - bounding_box : array_like, optional + bounding_box Array defining current axes limits such `bounding_box = (x min, x max, y min, y max)`. - tight_layout : bool, optional - Whether to invoke the :func:`plt.tight_layout` definition. - legend : bool, optional + tight_layout + Whether to invoke the :func:`matplotlib.pyplot.tight_layout` + definition. + legend Whether to display the legend. Default is *False*. - legend_columns : int, optional + legend_columns Number of columns in the legend. Default is *1*. - transparent_background : bool, optional + transparent_background Whether to turn off the background patch. Default is *True*. - title : unicode, optional + title Figure title. - wrap_title : unicode, optional + wrap_title Whether to wrap the figure title. Default is *True*. - x_label : unicode, optional + x_label *X* axis label. - y_label : unicode, optional + y_label *Y* axis label. - x_ticker : bool, optional + x_ticker Whether to display the *X* axis ticker. Default is *True*. - y_ticker : bool, optional + y_ticker Whether to display the *Y* axis ticker. Default is *True*. + """ + + figure: plt.Figure + axes: plt.Axes + filename: str + standalone: Boolean + aspect: Union[Literal["auto", "equal"], Floating] + axes_visible: Boolean + bounding_box: ArrayLike + tight_layout: Boolean + legend: Boolean + legend_columns: Integer + transparent_background: Boolean + title: str + wrap_title: Boolean + x_label: str + y_label: str + x_ticker: Boolean + y_ticker: Boolean + + +def render(**kwargs: Union[KwargsRender, Any]) -> Tuple[plt.Figure, plt.Axes]: + """ + Render the current figure while adjusting various settings such as the + bounding box, the title or background transparency. + + Other Parameters + ---------------- + kwargs + {:func:`colour.plotting.common.KwargsRender`}, + See the documentation of the previously listed class. Returns ------- - tuple + :class:`tuple` Current figure and axes. """ - figure = kwargs.get('figure', plt.gcf()) - axes = kwargs.get('axes', plt.gca()) + figure = cast(plt.Figure, kwargs.get("figure", plt.gcf())) + axes = cast(plt.Axes, kwargs.get("axes", plt.gca())) settings = Structure( **{ - 'filename': None, - 'standalone': True, - 'aspect': None, - 'axes_visible': True, - 'bounding_box': None, - 'tight_layout': True, - 'legend': False, - 'legend_columns': 1, - 'transparent_background': True, - 'title': None, - 'wrap_title': True, - 'x_label': None, - 'y_label': None, - 'x_ticker': True, - 'y_ticker': True, - }) + "filename": None, + "standalone": True, + "aspect": None, + "axes_visible": True, + "bounding_box": None, + "tight_layout": True, + "legend": False, + "legend_columns": 1, + "transparent_background": True, + "title": None, + "wrap_title": True, + "x_label": None, + "y_label": None, + "x_ticker": True, + "y_ticker": True, + } + ) settings.update(kwargs) if settings.aspect: @@ -578,47 +698,53 @@ def render(**kwargs): return figure, axes -def label_rectangles(labels, - rectangles, - rotation='vertical', - text_size=10, - offset=None, - **kwargs): +def label_rectangles( + labels: Sequence[str], + rectangles: Sequence[Patch], + rotation: Union[Literal["horizontal", "vertical"], str] = "vertical", + text_size: Floating = 10, + offset: Optional[ArrayLike] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ Add labels above given rectangles. Parameters ---------- - labels : array_like + labels Labels to display. - rectangles : object + rectangles Rectangles to used to set the labels value and position. - rotation : unicode, optional - **{'horizontal', 'vertical'}**, + rotation Labels orientation. - text_size : numeric, optional + text_size Labels text size. - offset : array_like, optional + offset Labels offset as percentages of the largest rectangle dimensions. Other Parameters ---------------- - figure : Figure, optional + figure Figure to apply the render elements onto. - axes : Axes, optional + axes Axes to apply the render elements onto. Returns ------- - tuple + :class:`tuple` Current figure and axes. """ - figure = kwargs.get('figure', plt.gcf()) - axes = kwargs.get('axes', plt.gca()) + rotation = validate_method( + rotation, + ["horizontal", "vertical"], + '"{0}" rotation is invalid, it must be one of {1}!', + ) + + figure = kwargs.get("figure", plt.gcf()) + axes = kwargs.get("axes", plt.gca()) - if offset is None: - offset = (0.0, 0.025) + offset = as_float_array(cast(ArrayLike, optional(offset, (0.0, 0.025)))) x_m, y_m = 0, 0 for rectangle in rectangles: @@ -629,8 +755,8 @@ def label_rectangles(labels, x = rectangle.get_x() height = rectangle.get_height() width = rectangle.get_width() - ha = 'center' - va = 'bottom' + ha = "center" + va = "bottom" axes.text( x + width / 2 + offset[0] * width, height + offset[1] * y_m, @@ -639,58 +765,62 @@ def label_rectangles(labels, va=va, rotation=rotation, fontsize=text_size, - clip_on=True) + clip_on=True, + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_label, + ) return figure, axes -def uniform_axes3d(**kwargs): +def uniform_axes3d(**kwargs: Any) -> Tuple[plt.Figure, plt.Axes]: """ - Sets equal aspect ratio to given 3d axes. + Set equal aspect ratio to given 3d axes. Other Parameters ---------------- - figure : Figure, optional + figure Figure to apply the render elements onto. - axes : Axes, optional + axes Axes to apply the render elements onto. Returns ------- - tuple + :class:`tuple` Current figure and axes. """ - figure = kwargs.get('figure', plt.gcf()) - axes = kwargs.get('axes', plt.gca()) + figure = kwargs.get("figure", plt.gcf()) + axes = kwargs.get("axes", plt.gca()) try: # pragma: no cover # TODO: Reassess according to # https://github.com/matplotlib/matplotlib/issues/1077 - axes.set_aspect('equal') + axes.set_aspect("equal") except NotImplementedError: # pragma: no cover pass - extents = np.array( - [getattr(axes, 'get_{}lim'.format(axis))() for axis in 'xyz']) + extents = np.array([getattr(axes, f"get_{axis}lim")() for axis in "xyz"]) centers = np.mean(extents, axis=1) extent = np.max(np.abs(extents[..., 1] - extents[..., 0])) - for center, axis in zip(centers, 'xyz'): - getattr(axes, 'set_{}lim'.format(axis))(center - extent / 2, - center + extent / 2) + for center, axis in zip(centers, "xyz"): + getattr(axes, f"set_{axis}lim")( + center - extent / 2, center + extent / 2 + ) return figure, axes -def filter_passthrough(mapping, - filterers, - anchors=True, - allow_non_siblings=True, - flags=re.IGNORECASE): +def filter_passthrough( + mapping: Mapping, + filterers: Union[Any, str, Sequence[Union[Any, str]]], + anchors: Boolean = True, + allow_non_siblings: Boolean = True, + flags: Union[Integer, RegexFlag] = re.IGNORECASE, +) -> Dict: """ - Returns mapping objects matching given filterers while passing through + Return mapping objects matching given filterers while passing through class instances whose type is one of the mapping element types. This definition allows passing custom but compatible objects to the various @@ -739,23 +869,23 @@ class instances whose type is one of the mapping element types. Parameters ---------- - mapping : dict_like + mapping Mapping to filter. - filterers : unicode or object or array_like + filterers Filterer or object class instance (which is passed through directly if its type is one of the mapping element types) or list of filterers. - anchors : bool, optional + anchors Whether to use Regex line anchors, i.e. *^* and *$* are added, surrounding the filterers patterns. - allow_non_siblings : bool, optional + allow_non_siblings Whether to allow non-siblings to be also passed through. - flags : int, optional + flags Regex flags. Returns ------- - dict_like + :class:`dict` Filtered mapping. """ @@ -764,34 +894,36 @@ class instances whose type is one of the mapping element types. elif not isinstance(filterers, (list, tuple)): filterers = [filterers] - string_filterers = [ - filterer for filterer in filterers if is_string(filterer) + string_filterers: List[str] = [ + cast(str, filterer) for filterer in filterers if is_string(filterer) ] - object_filterers = [ + object_filterers: List[Any] = [ filterer for filterer in filterers if is_sibling(filterer, mapping) ] if allow_non_siblings: non_siblings = [ - filterer for filterer in filterers - if filterer not in string_filterers and - filterer not in object_filterers + filterer + for filterer in filterers + if filterer not in string_filterers + and filterer not in object_filterers ] if non_siblings: runtime_warning( - 'Non-sibling elements are passed-through: "{0}"'.format( - non_siblings)) + f'Non-sibling elements are passed-through: "{non_siblings}"' + ) object_filterers.extend(non_siblings) - filtered_mapping = filter_mapping(mapping, string_filterers, anchors, - flags) + filtered_mapping = filter_mapping( + mapping, string_filterers, anchors, flags + ) for filterer in object_filterers: # TODO: Consider using "MutableMapping" here. - if isinstance(filterer, (dict, OrderedDict, CaseInsensitiveMapping)): + if isinstance(filterer, (dict, CaseInsensitiveMapping)): for key, value in filterer.items(): filtered_mapping[key] = value else: @@ -808,50 +940,60 @@ class instances whose type is one of the mapping element types. return filtered_mapping -def filter_RGB_colourspaces(filterers, - anchors=True, - allow_non_siblings=True, - flags=re.IGNORECASE): +def filter_RGB_colourspaces( + filterers: Union[ + RGB_Colourspace, str, Sequence[Union[RGB_Colourspace, str]] + ], + anchors: Boolean = True, + allow_non_siblings: Boolean = True, + flags: Union[Integer, RegexFlag] = re.IGNORECASE, +) -> Dict[str, RGB_Colourspace]: """ - Returns the *RGB* colourspaces matching given filterers. + Return the *RGB* colourspaces matching given filterers. Parameters ---------- - filterers : unicode or RGB_Colourspace or array_like + filterers Filterer or :class:`colour.RGB_Colourspace` class instance (which is passed through directly if its type is one of the mapping element types) or list of filterers. ``filterers`` elements can also be of any form supported by the :func:`colour.plotting.filter_passthrough` definition. - anchors : bool, optional + anchors Whether to use Regex line anchors, i.e. *^* and *$* are added, surrounding the filterers patterns. - allow_non_siblings : bool, optional + allow_non_siblings Whether to allow non-siblings to be also passed through. - flags : int, optional + flags Regex flags. Returns ------- - dict_like + :class:`dict` Filtered *RGB* colourspaces. """ - return filter_passthrough(RGB_COLOURSPACES, filterers, anchors, - allow_non_siblings, flags) - - -def filter_cmfs(filterers, - anchors=True, - allow_non_siblings=True, - flags=re.IGNORECASE): + return filter_passthrough( + RGB_COLOURSPACES, filterers, anchors, allow_non_siblings, flags + ) + + +def filter_cmfs( + filterers: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ], + anchors: Boolean = True, + allow_non_siblings: Boolean = True, + flags: Union[Integer, RegexFlag] = re.IGNORECASE, +) -> Dict[str, MultiSpectralDistributions]: """ - Returns the colour matching functions matching given filterers. + Return the colour matching functions matching given filterers. Parameters ---------- - filterers : unicode or LMS_ConeFundamentals or \ -RGB_ColourMatchingFunctions or XYZ_ColourMatchingFunctions or array_like + filterers Filterer or :class:`colour.LMS_ConeFundamentals`, :class:`colour.RGB_ColourMatchingFunctions` or :class:`colour.XYZ_ColourMatchingFunctions` class instance (which is @@ -859,112 +1001,127 @@ def filter_cmfs(filterers, types) or list of filterers. ``filterers`` elements can also be of any form supported by the :func:`colour.plotting.filter_passthrough` definition. - anchors : bool, optional + anchors Whether to use Regex line anchors, i.e. *^* and *$* are added, surrounding the filterers patterns. - allow_non_siblings : bool, optional + allow_non_siblings Whether to allow non-siblings to be also passed through. - flags : int, optional + flags Regex flags. Returns ------- - dict_like + :class:`dict` Filtered colour matching functions. """ - return filter_passthrough(MSDS_CMFS, filterers, anchors, - allow_non_siblings, flags) + return filter_passthrough( + MSDS_CMFS, filterers, anchors, allow_non_siblings, flags + ) -def filter_illuminants(filterers, - anchors=True, - allow_non_siblings=True, - flags=re.IGNORECASE): +def filter_illuminants( + filterers: Union[ + SpectralDistribution, str, Sequence[Union[SpectralDistribution, str]] + ], + anchors: Boolean = True, + allow_non_siblings: Boolean = True, + flags: Union[Integer, RegexFlag] = re.IGNORECASE, +) -> Dict[str, SpectralDistribution]: """ - Returns the illuminants matching given filterers. + Return the illuminants matching given filterers. Parameters ---------- - filterers : unicode or SpectralDistribution or array_like + filterers Filterer or :class:`colour.SpectralDistribution` class instance (which is passed through directly if its type is one of the mapping element types) or list of filterers. ``filterers`` elements can also be of any form supported by the :func:`colour.plotting.filter_passthrough` definition. - anchors : bool, optional + anchors Whether to use Regex line anchors, i.e. *^* and *$* are added, surrounding the filterers patterns. - allow_non_siblings : bool, optional + allow_non_siblings Whether to allow non-siblings to be also passed through. - flags : int, optional + flags Regex flags. Returns ------- - dict_like + :class:`dict` Filtered illuminants. """ - illuminants = OrderedDict() + illuminants = {} illuminants.update( - filter_passthrough(SDS_ILLUMINANTS, filterers, anchors, - allow_non_siblings, flags)) + filter_passthrough( + SDS_ILLUMINANTS, filterers, anchors, allow_non_siblings, flags + ) + ) illuminants.update( - filter_passthrough(SDS_LIGHT_SOURCES, filterers, anchors, - allow_non_siblings, flags)) + filter_passthrough( + SDS_LIGHT_SOURCES, filterers, anchors, allow_non_siblings, flags + ) + ) return illuminants -def filter_colour_checkers(filterers, - anchors=True, - allow_non_siblings=True, - flags=re.IGNORECASE): +def filter_colour_checkers( + filterers: Union[ColourChecker, str, Sequence[Union[ColourChecker, str]]], + anchors: Boolean = True, + allow_non_siblings: Boolean = True, + flags: Union[Integer, RegexFlag] = re.IGNORECASE, +) -> Dict[str, ColourChecker]: """ - Returns the colour checkers matching given filterers. + Return the colour checkers matching given filterers. Parameters ---------- - filterers : unicode or ColourChecker or array_like + filterers Filterer or :class:`colour.characterisation.ColourChecker` class instance (which is passed through directly if its type is one of the mapping element types) or list of filterers. ``filterers`` elements can also be of any form supported by the :func:`colour.plotting.filter_passthrough` definition. - anchors : bool, optional + anchors Whether to use Regex line anchors, i.e. *^* and *$* are added, surrounding the filterers patterns. - allow_non_siblings : bool, optional + allow_non_siblings Whether to allow non-siblings to be also passed through. - flags : int, optional + flags Regex flags. Returns ------- - dict_like + :class:`dict` Filtered colour checkers. """ - return filter_passthrough(CCS_COLOURCHECKERS, filterers, anchors, - allow_non_siblings, flags) + return filter_passthrough( + CCS_COLOURCHECKERS, filterers, anchors, allow_non_siblings, flags + ) -def update_settings_collection(settings_collection, keyword_arguments, - expected_count): +def update_settings_collection( + settings_collection: Union[Dict, List[Dict]], + keyword_arguments: Union[Dict, List[Dict]], + expected_count: Integer, +): """ - Updates given settings collection, in-place, with given keyword arguments + Update given settings collection, *in-place*, with given keyword arguments and expected count of settings collection elements. Parameters ---------- - settings_collection : dict or list + settings_collection Settings collection to update. - keyword_arguments : dict + keyword_arguments Keyword arguments to update the settings collection. - expected_count : int + expected_count Expected count of settings collection elements. Examples @@ -982,9 +1139,11 @@ def update_settings_collection(settings_collection, keyword_arguments, """ if not isinstance(keyword_arguments, dict): - assert len(keyword_arguments) == expected_count, ( - 'Multiple keyword arguments defined, but they do not ' - 'match the expected count!') + attest( + len(keyword_arguments) == expected_count, + "Multiple keyword arguments defined, but they do not " + "match the expected count!", + ) for i, settings in enumerate(settings_collection): if isinstance(keyword_arguments, dict): @@ -995,56 +1154,41 @@ def update_settings_collection(settings_collection, keyword_arguments, @override_style( **{ - 'axes.grid': False, - 'xtick.bottom': False, - 'ytick.left': False, - 'xtick.labelbottom': False, - 'ytick.labelleft': False, - }) -def plot_single_colour_swatch(colour_swatch, **kwargs): + "axes.grid": False, + "xtick.bottom": False, + "ytick.left": False, + "xtick.labelbottom": False, + "ytick.labelleft": False, + } +) +def plot_single_colour_swatch( + colour_swatch: Union[ArrayLike, ColourSwatch], **kwargs: Any +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given colour swatch. + Plot given colour swatch. Parameters ---------- - colour_swatch : array_like or ColourSwatch - Colour swatch, either a regular *array_like* or a + colour_swatch + Colour swatch, either a regular `ArrayLike` or a :class:`colour.plotting.ColourSwatch` class instance. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_multi_colour_swatches`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - width : numeric, optional - {:func:`colour.plotting.plot_multi_colour_swatches`}, - Colour swatch width. - height : numeric, optional - {:func:`colour.plotting.plot_multi_colour_swatches`}, - Colour swatch height. - spacing : numeric, optional - {:func:`colour.plotting.plot_multi_colour_swatches`}, - Colour swatches spacing. - columns : int, optional - {:func:`colour.plotting.plot_multi_colour_swatches`}, - Colour swatches columns count. - text_kwargs : dict, optional - {:func:`colour.plotting.plot_multi_colour_swatches`}, - Keyword arguments for the :func:`plt.text` definition. The following - special keywords can also be used: - - - *offset*: Sets the text offset. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples -------- - >>> RGB = ColourSwatch(RGB=(0.45620519, 0.03081071, 0.04091952)) + >>> RGB = ColourSwatch((0.45620519, 0.03081071, 0.04091952)) >>> plot_single_colour_swatch(RGB) # doctest: +ELLIPSIS (
, <...AxesSubplot...>) @@ -1053,57 +1197,60 @@ def plot_single_colour_swatch(colour_swatch, **kwargs): :alt: plot_single_colour_swatch """ - return plot_multi_colour_swatches((colour_swatch, ), **kwargs) + return plot_multi_colour_swatches((colour_swatch,), **kwargs) @override_style( **{ - 'axes.grid': False, - 'xtick.bottom': False, - 'ytick.left': False, - 'xtick.labelbottom': False, - 'ytick.labelleft': False, - }) -def plot_multi_colour_swatches(colour_swatches, - width=1, - height=1, - spacing=0, - columns=None, - direction='+y', - text_kwargs=None, - background_colour=(1.0, 1.0, 1.0), - compare_swatches=None, - **kwargs): + "axes.grid": False, + "xtick.bottom": False, + "ytick.left": False, + "xtick.labelbottom": False, + "ytick.labelleft": False, + } +) +def plot_multi_colour_swatches( + colour_swatches: Sequence[Union[ArrayLike, ColourSwatch]], + width: Floating = 1, + height: Floating = 1, + spacing: Floating = 0, + columns: Optional[Integer] = None, + direction: Union[Literal["+y", "-y"], str] = "+y", + text_kwargs: Optional[Dict] = None, + background_colour: ArrayLike = (1.0, 1.0, 1.0), + compare_swatches: Optional[ + Union[Literal["Diagonal", "Stacked"], str] + ] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given colours swatches. + Plot given colours swatches. Parameters ---------- - colour_swatches : array_like - Colour swatch sequence, either a regular *array_like* or a sequence of + colour_swatches + Colour swatch sequence, either a regular `ArrayLike` or a sequence of :class:`colour.plotting.ColourSwatch` class instances. - width : numeric, optional + width Colour swatch width. - height : numeric, optional + height Colour swatch height. - spacing : numeric, optional + spacing Colour swatches spacing. - columns : int, optional + columns Colour swatches columns count, defaults to the colour swatch count or half of it if comparing. - direction : unicode, optional - {'+y', '-y'} + direction Row stacking direction. - text_kwargs : dict, optional - Keyword arguments for the :func:`plt.text` definition. The following - special keywords can also be used: + text_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.text` definition. + The following special keywords can also be used: - - *offset*: Sets the text offset. - - *visible*: Makes the text visible. - background_colour : array_like or unicode, optional + - ``offset``: Sets the text offset. + - ``visible``: Sets the text visibility. + background_colour Background colour. - compare_swatches : unicode, optional - **{None, 'Diagonal', 'Stacked'}**, + compare_swatches Whether to compare the swatches, in which case the colour swatch count must be an even number with alternating reference colour swatches and test colour swatches. *Stacked* will draw the test colour swatch in @@ -1113,20 +1260,19 @@ def plot_multi_colour_swatches(colour_swatches, Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - Also handles keywords arguments for deprecation management. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples -------- - >>> RGB_1 = ColourSwatch(RGB=(0.45293517, 0.31732158, 0.26414773)) - >>> RGB_2 = ColourSwatch(RGB=(0.77875824, 0.57726450, 0.50453169)) + >>> RGB_1 = ColourSwatch((0.45293517, 0.31732158, 0.26414773)) + >>> RGB_2 = ColourSwatch((0.77875824, 0.57726450, 0.50453169)) >>> plot_multi_colour_swatches([RGB_1, RGB_2]) # doctest: +ELLIPSIS (
, <...AxesSubplot...>) @@ -1135,66 +1281,76 @@ def plot_multi_colour_swatches(colour_swatches, :alt: plot_multi_colour_swatches """ - direction = direction.lower() - assert direction in ('+y', '-y'), ( - '"direction" must be one of *[\'+y\', \'-y\']*!') + direction = validate_method( + direction, + ["+y", "-y"], + '"{0}" direction is invalid, it must be one of {1}!', + ) if compare_swatches is not None: - compare_swatches = compare_swatches.lower() - - assert compare_swatches in ('diagonal', 'stacked'), ( - '"compare_swatches" must be one of *[\'diagonal\', \'stacked\']*!') - - text_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['text_args', 'text_kwargs']], - }, **kwargs).get('text_kwargs', text_kwargs) + compare_swatches = validate_method( + compare_swatches, + ["Diagonal", "Stacked"], + '"{0}" compare swatches method is invalid, it must be one of {1}!', + ) _figure, axes = artist(**kwargs) - # Handling case where `colour_swatches` is a regular array. - if len(colour_swatches) != 0: - if not isinstance(colour_swatches[0], ColourSwatch): - colour_swatches = as_float_array(colour_swatches).reshape( - [-1, 3]).tolist() - for i, colour_swatch in enumerate(colour_swatches): - colour_swatches[i] = ColourSwatch(RGB=colour_swatch) + # Handling case where `colour_swatches` is a regular *ArrayLike*. + colour_swatches = list(colour_swatches) + colour_swatches_converted = [] + if not isinstance(first_item(colour_swatches), ColourSwatch): + for i, colour_swatch in enumerate( + as_float_array(cast(ArrayLike, colour_swatches)).reshape([-1, 3]) + ): + colour_swatches_converted.append(ColourSwatch(colour_swatch)) + else: + colour_swatches_converted = cast(List[ColourSwatch], colour_swatches) + + colour_swatches = colour_swatches_converted if compare_swatches is not None: - assert len(colour_swatches) % 2 == 0, ( - 'Cannot compare an odd number of colour swatches!') + attest( + len(colour_swatches) % 2 == 0, + "Cannot compare an odd number of colour swatches!", + ) - reference_colour_swatches = colour_swatches[0::2] - test_colour_swatches = colour_swatches[1::2] + colour_swatches_reference = colour_swatches[0::2] + colour_swatches_test = colour_swatches[1::2] else: - reference_colour_swatches = test_colour_swatches = colour_swatches + colour_swatches_reference = colour_swatches_test = colour_swatches - if columns is None: - columns = len(reference_colour_swatches) + columns = optional(columns, len(colour_swatches_reference)) text_settings = { - 'offset': 0.05, - 'visible': True, + "offset": 0.05, + "visible": True, + "zorder": CONSTANTS_COLOUR_STYLE.zorder.midground_label, } if text_kwargs is not None: text_settings.update(text_kwargs) - text_offset = text_settings.pop('offset') + text_offset = text_settings.pop("offset") - offset_X = offset_Y = 0 + offset_X: Floating = 0 + offset_Y: Floating = 0 x_min, x_max, y_min, y_max = 0, width, 0, height - direction = 1 if direction == '+y' else -1 - for i, colour_swatch in enumerate(reference_colour_swatches): + y = 1 if direction == "+y" else -1 + for i, colour_swatch in enumerate(colour_swatches_reference): if i % columns == 0 and i != 0: offset_X = 0 - offset_Y += (height + spacing) * direction + offset_Y += (height + spacing) * y x_0, x_1 = offset_X, offset_X + width - y_0, y_1 = offset_Y, offset_Y + height * direction + y_0, y_1 = offset_Y, offset_Y + height * y axes.fill( - (x_0, x_1, x_1, x_0), (y_0, y_0, y_1, y_1), - color=np.clip(reference_colour_swatches[i].RGB, 0, 1)) + (x_0, x_1, x_1, x_0), + (y_0, y_0, y_1, y_1), + color=np.clip(colour_swatches_reference[i].RGB, 0, 1), + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_polygon, + ) - if compare_swatches == 'stacked': + if compare_swatches == "stacked": margin_X = width * 0.25 margin_Y = height * 0.25 axes.fill( @@ -1203,36 +1359,43 @@ def plot_multi_colour_swatches(colour_swatches, x_1 - margin_X, x_1 - margin_X, x_0 + margin_X, - ), ( - y_0 + margin_Y * direction, - y_0 + margin_Y * direction, - y_1 - margin_Y * direction, - y_1 - margin_Y * direction, ), - color=np.clip(test_colour_swatches[i].RGB, 0, 1)) + ( + y_0 + margin_Y * y, + y_0 + margin_Y * y, + y_1 - margin_Y * y, + y_1 - margin_Y * y, + ), + color=np.clip(colour_swatches_test[i].RGB, 0, 1), + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_polygon, + ) else: axes.fill( - (x_0, x_1, x_1), (y_0, y_0, y_1), - color=np.clip(test_colour_swatches[i].RGB, 0, 1)) + (x_0, x_1, x_1), + (y_0, y_0, y_1), + color=np.clip(colour_swatches_test[i].RGB, 0, 1), + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_polygon, + ) - if colour_swatch.name is not None and text_settings['visible']: + if colour_swatch.name is not None and text_settings["visible"]: axes.text( x_0 + text_offset, - y_0 + text_offset * direction, + y_0 + text_offset * y, colour_swatch.name, - verticalalignment='bottom' if direction == 1 else 'top', + verticalalignment="bottom" if y == 1 else "top", clip_on=True, - **text_settings) + **text_settings, + ) offset_X += width + spacing - x_max = min(len(colour_swatches), columns) + x_max = min(len(colour_swatches), int(columns)) x_max = x_max * width + x_max * spacing - spacing y_max = offset_Y axes.patch.set_facecolor(background_colour) - if direction == 1: + if y == 1: bounding_box = [ x_min - spacing, x_max + spacing, @@ -1247,10 +1410,10 @@ def plot_multi_colour_swatches(colour_swatches, y_min + spacing, ] - settings = { - 'axes': axes, - 'bounding_box': bounding_box, - 'aspect': 'equal', + settings: Dict[str, Any] = { + "axes": axes, + "bounding_box": bounding_box, + "aspect": "equal", } settings.update(kwargs) @@ -1258,42 +1421,44 @@ def plot_multi_colour_swatches(colour_swatches, @override_style() -def plot_single_function(function, - samples=None, - log_x=None, - log_y=None, - plot_kwargs=None, - **kwargs): +def plot_single_function( + function: Callable, + samples: Optional[ArrayLike] = None, + log_x: Optional[Integer] = None, + log_y: Optional[Integer] = None, + plot_kwargs: Optional[Union[Dict, List[Dict]]] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given function. + Plot given function. Parameters ---------- - function : callable + function Function to plot. - samples : array_like, optional, + samples Samples to evaluate the functions with. - log_x : int, optional + log_x Log base to use for the *x* axis scale, if *None*, the *x* axis scale will be linear. - log_y : int, optional + log_y Log base to use for the *y* axis scale, if *None*, the *y* axis scale will be linear. - plot_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.plot` definition, used to control - the style of the plotted function. + plot_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.plot` definition, + used to control the style of the plotted function. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_multi_functions`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -1311,57 +1476,59 @@ def plot_single_function(function, try: name = function.__name__ except AttributeError: - name = 'Unnamed' + name = "Unnamed" - settings = { - 'title': '{0} - Function'.format(name), - 'legend': False, + settings: Dict[str, Any] = { + "title": f"{name} - Function", + "legend": False, } settings.update(kwargs) - return plot_multi_functions({ - name: function - }, samples, log_x, log_y, plot_kwargs, **settings) + return plot_multi_functions( + {name: function}, samples, log_x, log_y, plot_kwargs, **settings + ) @override_style() -def plot_multi_functions(functions, - samples=None, - log_x=None, - log_y=None, - plot_kwargs=None, - **kwargs): +def plot_multi_functions( + functions: Dict[str, Callable], + samples: Optional[ArrayLike] = None, + log_x: Optional[Integer] = None, + log_y: Optional[Integer] = None, + plot_kwargs: Optional[Union[Dict, List[Dict]]] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given functions. + Plot given functions. Parameters ---------- - functions : dict + functions Functions to plot. - samples : array_like, optional, + samples Samples to evaluate the functions with. - log_x : int, optional + log_x Log base to use for the *x* axis scale, if *None*, the *x* axis scale will be linear. - log_y : int, optional + log_y Log base to use for the *y* axis scale, if *None*, the *y* axis scale will be linear. - plot_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.plot` definition, used to control - the style of the plotted functions. ``plot_kwargs`` can be either a - single dictionary applied to all the plotted functions with same - settings or a sequence of dictionaries with different settings for each - plotted function. + plot_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.plot` definition, + used to control the style of the plotted functions. ``plot_kwargs`` + can be either a single dictionary applied to all the plotted functions + with the same settings or a sequence of dictionaries with different + settings for each plotted function. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -1380,58 +1547,78 @@ def plot_multi_functions(functions, :alt: plot_multi_functions """ - settings = kwargs.copy() + settings: Dict[str, Any] = dict(kwargs) _figure, axes = artist(**settings) - plot_settings_collection = [{ - 'label': '{0}'.format(name) - } for name in functions.keys()] + plot_settings_collection = [ + { + "label": f"{name}", + "zorder": CONSTANTS_COLOUR_STYLE.zorder.midground_label, + } + for name in functions.keys() + ] if plot_kwargs is not None: - update_settings_collection(plot_settings_collection, plot_kwargs, - len(functions)) + update_settings_collection( + plot_settings_collection, plot_kwargs, len(functions) + ) + + # TODO: Remove when "Matplotlib" minimum version can be set to 3.5.0. + matplotlib_3_5 = tuple( + int(token) for token in matplotlib.__version__.split(".")[:2] + ) >= (3, 5) if log_x is not None and log_y is not None: - assert log_x >= 2 and log_y >= 2, ( - 'Log base must be equal or greater than 2.') + attest( + log_x >= 2 and log_y >= 2, + "Log base must be equal or greater than 2.", + ) plotting_function = axes.loglog - if six.PY3: # pragma: no cover - axes.set_xscale('log', base=log_x) - axes.set_yscale('log', base=log_y) - else: # pragma: no cover - axes.set_xscale('log', basex=log_x) - axes.set_yscale('log', basey=log_y) + axes.set_xscale("log", base=log_x) + axes.set_yscale("log", base=log_y) elif log_x is not None: - assert log_x >= 2, 'Log base must be equal or greater than 2.' + attest(log_x >= 2, "Log base must be equal or greater than 2.") - plotting_function = partial(axes.semilogx, basex=log_x) + if matplotlib_3_5: # pragma: no cover + plotting_function = partial(axes.semilogx, base=log_x) + else: # pragma: no cover + plotting_function = partial(axes.semilogx, basex=log_x) elif log_y is not None: - assert log_y >= 2, 'Log base must be equal or greater than 2.' + attest(log_y >= 2, "Log base must be equal or greater than 2.") - plotting_function = partial(axes.semilogy, basey=log_y) + if matplotlib_3_5: # pragma: no cover + plotting_function = partial(axes.semilogy, base=log_y) + else: # pragma: no cover + plotting_function = partial(axes.semilogy, basey=log_y) else: plotting_function = axes.plot - if samples is None: - samples = np.linspace(0, 1, 1000) + samples = cast(ArrayLike, optional(samples, np.linspace(0, 1, 1000))) for i, (_name, function) in enumerate(functions.items()): - plotting_function(samples, function(samples), - **plot_settings_collection[i]) - - x_label = ('x - Log Base {0} Scale'.format(log_x) - if log_x is not None else 'x - Linear Scale') - y_label = ('y - Log Base {0} Scale'.format(log_y) - if log_y is not None else 'y - Linear Scale') + plotting_function( + samples, function(samples), **plot_settings_collection[i] + ) + + x_label = ( + f"x - Log Base {log_x} Scale" + if log_x is not None + else "x - Linear Scale" + ) + y_label = ( + f"y - Log Base {log_y} Scale" + if log_y is not None + else "y - Linear Scale" + ) settings = { - 'axes': axes, - 'legend': True, - 'title': '{0} - Functions'.format(', '.join(functions)), - 'x_label': x_label, - 'y_label': y_label + "axes": axes, + "legend": True, + "title": f"{', '.join(functions)} - Functions", + "x_label": x_label, + "y_label": y_label, } settings.update(kwargs) @@ -1439,31 +1626,36 @@ def plot_multi_functions(functions, @override_style() -def plot_image(image, imshow_kwargs=None, text_kwargs=None, **kwargs): +def plot_image( + image: ArrayLike, + imshow_kwargs: Optional[Dict] = None, + text_kwargs: Optional[Dict] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given image. + Plot given image. Parameters ---------- - image : array_like + image Image to plot. - imshow_kwargs : dict, optional - Keyword arguments for the :func:`plt.imshow` definition. - text_kwargs : dict, optional - Keyword arguments for the :func:`plt.text` definition. The following - special keyword arguments can also be used: + imshow_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.imshow` definition. + text_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.text` definition. + The following special keyword arguments can also be used: - - *offset* : array_like, sets the text offset. + - ``offset`` : Sets the text offset. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -1472,7 +1664,8 @@ def plot_image(image, imshow_kwargs=None, text_kwargs=None, **kwargs): >>> import colour >>> from colour import read_image >>> path = os.path.join( - ... colour.__path__[0], '..', 'docs', '_static', 'Logo_Medium_001.png') + ... colour.__path__[0], 'examples', 'plotting', 'resources', + ... 'Ishihara_Colour_Blindness_Test_Plate_3.png') >>> plot_image(read_image(path)) # doctest: +ELLIPSIS (
, <...AxesSubplot...>) @@ -1481,48 +1674,47 @@ def plot_image(image, imshow_kwargs=None, text_kwargs=None, **kwargs): :alt: plot_image """ - text_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['text_parameters', 'text_kwargs']], - }, **kwargs).get('text_kwargs', text_kwargs) - _figure, axes = artist(**kwargs) imshow_settings = { - 'interpolation': 'nearest', - 'cmap': matplotlib.cm.Greys_r + "interpolation": "nearest", + "cmap": matplotlib.cm.Greys_r, + "zorder": CONSTANTS_COLOUR_STYLE.zorder.background_polygon, } if imshow_kwargs is not None: imshow_settings.update(imshow_kwargs) text_settings = { - 'text': None, - 'offset': 0.005, - 'color': CONSTANTS_COLOUR_STYLE.colour.brightest, - 'alpha': CONSTANTS_COLOUR_STYLE.opacity.high, + "text": None, + "offset": 0.005, + "color": CONSTANTS_COLOUR_STYLE.colour.brightest, + "alpha": CONSTANTS_COLOUR_STYLE.opacity.high, + "zorder": CONSTANTS_COLOUR_STYLE.zorder.midground_label, } if text_kwargs is not None: text_settings.update(text_kwargs) - text_offset = text_settings.pop('offset') + text_offset = text_settings.pop("offset") image = as_float_array(image) axes.imshow(np.clip(image, 0, 1), **imshow_settings) - if text_settings['text'] is not None: - text = text_settings.pop('text') + if text_settings["text"] is not None: + text = text_settings.pop("text") axes.text( text_offset, text_offset, text, transform=axes.transAxes, - ha='left', - va='bottom', - **text_settings) - - settings = { - 'axes': axes, - 'axes_visible': False, + ha="left", + va="bottom", + **text_settings, + ) + + settings: Dict[str, Any] = { + "axes": axes, + "axes_visible": False, } settings.update(kwargs) diff --git a/colour/plotting/corresponding.py b/colour/plotting/corresponding.py index 43ee3fa137..50b7c6c78e 100644 --- a/colour/plotting/corresponding.py +++ b/colour/plotting/corresponding.py @@ -1,67 +1,91 @@ -# -*- coding: utf-8 -*- """ Corresponding Chromaticities Prediction Plotting ================================================ -Defines corresponding chromaticities prediction plotting objects: +Defines the corresponding chromaticities prediction plotting objects: - :func:`colour.plotting.plot_corresponding_chromaticities_prediction` """ -from __future__ import division - -from colour.corresponding import corresponding_chromaticities_prediction -from colour.plotting import (CONSTANTS_COLOUR_STYLE, artist, - plot_chromaticity_diagram_CIE1976UCS, - override_style, render) +from __future__ import annotations + +import matplotlib.pyplot as plt + +from colour.corresponding import ( + CorrespondingColourDataset, + corresponding_chromaticities_prediction, +) +from colour.hints import Any, Dict, Literal, Optional, Tuple, Union +from colour.plotting import ( + CONSTANTS_COLOUR_STYLE, + artist, + plot_chromaticity_diagram_CIE1976UCS, + override_style, + render, +) from colour.utilities import is_numeric -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['plot_corresponding_chromaticities_prediction'] +__all__ = [ + "plot_corresponding_chromaticities_prediction", +] @override_style() -def plot_corresponding_chromaticities_prediction(experiment=1, - model='Von Kries', - transform='CAT02', - **kwargs): +def plot_corresponding_chromaticities_prediction( + experiment: Union[ + Literal[1, 2, 3, 4, 6, 8, 9, 11, 12], CorrespondingColourDataset + ] = 1, + model: Union[ + Literal[ + "CIE 1994", + "CMCCAT2000", + "Fairchild 1990", + "Von Kries", + "Zhai 2018", + ], + str, + ] = "Von Kries", + corresponding_chromaticities_prediction_kwargs: Optional[Dict] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given chromatic adaptation model corresponding chromaticities + Plot given chromatic adaptation model corresponding chromaticities prediction. Parameters ---------- - experiment : integer or CorrespondingColourDataset, optional - {1, 2, 3, 4, 6, 8, 9, 11, 12} + experiment *Breneman (1987)* experiment number or :class:`colour.CorrespondingColourDataset` class instance. - model : unicode, optional + model Corresponding chromaticities prediction model name. - transform : unicode, optional - Transformation to use with *Von Kries* chromatic adaptation model. + corresponding_chromaticities_prediction_kwargs + Keyword arguments for the :func:`colour.\ +corresponding_chromaticities_prediction` definition. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples -------- - >>> plot_corresponding_chromaticities_prediction(1, 'Von Kries', 'CAT02') + >>> plot_corresponding_chromaticities_prediction(1, 'Von Kries') ... # doctest: +ELLIPSIS (
, <...AxesSubplot...>) @@ -71,28 +95,33 @@ def plot_corresponding_chromaticities_prediction(experiment=1, :alt: plot_corresponding_chromaticities_prediction """ - settings = {'uniform': True} + if corresponding_chromaticities_prediction_kwargs is None: + corresponding_chromaticities_prediction_kwargs = {} + + settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) - name = ('Experiment {0}'.format(experiment) - if is_numeric(experiment) else experiment.name) - title = (('Corresponding Chromaticities Prediction - {0} ({1}) - {2} - ' - 'CIE 1976 UCS Chromaticity Diagram').format( - model, transform, name) - if model.lower() in ('von kries', 'vonkries') else - ('Corresponding Chromaticities Prediction - {0} - {1} - ' - 'CIE 1976 UCS Chromaticity Diagram').format(model, name)) - - settings = {'axes': axes, 'title': title} + name = ( + f"Experiment {experiment}" + if is_numeric(experiment) + else experiment.name # type: ignore[union-attr] + ) + title = ( + f"Corresponding Chromaticities Prediction - {model} - {name} - " + "CIE 1976 UCS Chromaticity Diagram" + ) + + settings = {"axes": axes, "title": title} settings.update(kwargs) - settings['standalone'] = False + settings["standalone"] = False plot_chromaticity_diagram_CIE1976UCS(**settings) results = corresponding_chromaticities_prediction( - experiment, transform=transform) + experiment, model, **corresponding_chromaticities_prediction_kwargs + ) for result in results: _name, uv_t, uv_m, uv_p = result @@ -103,32 +132,49 @@ def plot_corresponding_chromaticities_prediction(experiment=1, uv_p[1] - uv_t[1] - 0.1 * (uv_p[1] - uv_t[1]), color=CONSTANTS_COLOUR_STYLE.colour.dark, head_width=0.005, - head_length=0.005) + head_length=0.005, + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_annotation, + ) axes.plot( uv_t[0], uv_t[1], - 'o', + "o", color=CONSTANTS_COLOUR_STYLE.colour.brightest, markeredgecolor=CONSTANTS_COLOUR_STYLE.colour.dark, - markersize=(CONSTANTS_COLOUR_STYLE.geometry.short * 6 + - CONSTANTS_COLOUR_STYLE.geometry.short * 0.75), - markeredgewidth=CONSTANTS_COLOUR_STYLE.geometry.short * 0.75) + markersize=( + CONSTANTS_COLOUR_STYLE.geometry.short * 6 + + CONSTANTS_COLOUR_STYLE.geometry.short * 0.75 + ), + markeredgewidth=CONSTANTS_COLOUR_STYLE.geometry.short * 0.75, + zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_line, + ) axes.plot( uv_m[0], uv_m[1], - '^', + "^", color=CONSTANTS_COLOUR_STYLE.colour.brightest, markeredgecolor=CONSTANTS_COLOUR_STYLE.colour.dark, - markersize=(CONSTANTS_COLOUR_STYLE.geometry.short * 6 + - CONSTANTS_COLOUR_STYLE.geometry.short * 0.75), - markeredgewidth=CONSTANTS_COLOUR_STYLE.geometry.short * 0.75) + markersize=( + CONSTANTS_COLOUR_STYLE.geometry.short * 6 + + CONSTANTS_COLOUR_STYLE.geometry.short * 0.75 + ), + markeredgewidth=CONSTANTS_COLOUR_STYLE.geometry.short * 0.75, + zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_line, + ) axes.plot( - uv_p[0], uv_p[1], '^', color=CONSTANTS_COLOUR_STYLE.colour.dark) - - settings.update({ - 'standalone': True, - 'bounding_box': (-0.1, 0.7, -0.1, 0.7), - }) + uv_p[0], + uv_p[1], + "^", + color=CONSTANTS_COLOUR_STYLE.colour.dark, + zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_line, + ) + + settings.update( + { + "standalone": True, + "bounding_box": (-0.1, 0.7, -0.1, 0.7), + } + ) settings.update(kwargs) return render(**settings) diff --git a/colour/plotting/datasets/__init__.py b/colour/plotting/datasets/__init__.py index e285612f15..2528d404f7 100644 --- a/colour/plotting/datasets/__init__.py +++ b/colour/plotting/datasets/__init__.py @@ -1,11 +1,11 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - -from .astm_g_173 import (SD_ASTMG173_ETR, SD_ASTMG173_GLOBAL_TILT, - SD_ASTMG173_DIRECT_CIRCUMSOLAR) +from .astm_g_173 import ( + SD_ASTMG173_ETR, + SD_ASTMG173_GLOBAL_TILT, + SD_ASTMG173_DIRECT_CIRCUMSOLAR, +) __all__ = [ - 'SD_ASTMG173_ETR', 'SD_ASTMG173_GLOBAL_TILT', - 'SD_ASTMG173_DIRECT_CIRCUMSOLAR' + "SD_ASTMG173_ETR", + "SD_ASTMG173_GLOBAL_TILT", + "SD_ASTMG173_DIRECT_CIRCUMSOLAR", ] diff --git a/colour/plotting/datasets/astm_g_173.py b/colour/plotting/datasets/astm_g_173.py index 5c9e8683de..49192e1891 100644 --- a/colour/plotting/datasets/astm_g_173.py +++ b/colour/plotting/datasets/astm_g_173.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ ASTM G-173 Solar Spectral Irradiance ==================================== @@ -14,26 +13,30 @@ http://rredc.nrel.gov/solar/spectra/am1.5/ASTMG173/ASTMG173.html """ -from __future__ import division, unicode_literals +from __future__ import annotations from colour.algebra import LinearInterpolator from colour.colorimetry import SpectralDistribution +from colour.hints import Dict -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'DATA_ASTMG173_ETR', 'DATA_ASTMG173_GLOBAL_TILT', - 'DATA_ASTMG173_DIRECT_CIRCUMSOLAR', 'SD_ASTMG173_ETR', - 'SD_ASTMG173_GLOBAL_TILT', 'SD_ASTMG173_DIRECT_CIRCUMSOLAR' + "DATA_ASTMG173_ETR", + "DATA_ASTMG173_GLOBAL_TILT", + "DATA_ASTMG173_DIRECT_CIRCUMSOLAR", + "SD_ASTMG173_ETR", + "SD_ASTMG173_GLOBAL_TILT", + "SD_ASTMG173_DIRECT_CIRCUMSOLAR", ] -# yapf: disable -DATA_ASTMG173_ETR = { + +DATA_ASTMG173_ETR: Dict = { 280.0: 8.2000e-02, 280.5: 9.9000e-02, 281.0: 1.5000e-01, @@ -127,15 +130,15 @@ 325.0: 8.2918e-01, 325.5: 9.1377e-01, 326.0: 9.9835e-01, - 326.5: 1.0166e+00, - 327.0: 1.0047e+00, + 326.5: 1.0166e00, + 327.0: 1.0047e00, 327.5: 9.8679e-01, 328.0: 9.5681e-01, 328.5: 9.3400e-01, - 329.0: 1.0046e+00, - 329.5: 1.0958e+00, - 330.0: 1.1098e+00, - 330.5: 1.0429e+00, + 329.0: 1.0046e00, + 329.5: 1.0958e00, + 330.0: 1.1098e00, + 330.5: 1.0429e00, 331.0: 9.9100e-01, 331.5: 9.9396e-01, 332.0: 9.9277e-01, @@ -144,7 +147,7 @@ 333.5: 9.3700e-01, 334.0: 9.5557e-01, 334.5: 9.8862e-01, - 335.0: 1.0097e+00, + 335.0: 1.0097e00, 335.5: 9.7453e-01, 336.0: 8.8979e-01, 336.5: 8.2900e-01, @@ -154,13 +157,13 @@ 338.5: 9.5783e-01, 339.0: 9.6863e-01, 339.5: 9.8900e-01, - 340.0: 1.0544e+00, - 340.5: 1.0463e+00, + 340.0: 1.0544e00, + 340.5: 1.0463e00, 341.0: 9.7100e-01, 341.5: 9.5900e-01, 342.0: 9.9570e-01, - 342.5: 1.0277e+00, - 343.0: 1.0417e+00, + 342.5: 1.0277e00, + 343.0: 1.0417e00, 343.5: 9.8301e-01, 344.0: 8.5416e-01, 344.5: 8.1300e-01, @@ -174,556 +177,556 @@ 348.5: 9.3721e-01, 349.0: 8.9900e-01, 349.5: 9.1969e-01, - 350.0: 1.0122e+00, - 350.5: 1.0849e+00, - 351.0: 1.0534e+00, - 351.5: 1.0129e+00, + 350.0: 1.0122e00, + 350.5: 1.0849e00, + 351.0: 1.0534e00, + 351.5: 1.0129e00, 352.0: 9.8383e-01, 352.5: 9.2600e-01, 353.0: 9.8012e-01, - 353.5: 1.0752e+00, - 354.0: 1.1346e+00, - 354.5: 1.1440e+00, - 355.0: 1.1406e+00, - 355.5: 1.0964e+00, - 356.0: 1.0250e+00, + 353.5: 1.0752e00, + 354.0: 1.1346e00, + 354.5: 1.1440e00, + 355.0: 1.1406e00, + 355.5: 1.0964e00, + 356.0: 1.0250e00, 356.5: 9.5914e-01, 357.0: 8.4200e-01, 357.5: 8.5015e-01, 358.0: 7.8916e-01, 358.5: 7.3100e-01, 359.0: 8.5805e-01, - 359.5: 1.0321e+00, - 360.0: 1.0890e+00, - 360.5: 1.0265e+00, + 359.5: 1.0321e00, + 360.0: 1.0890e00, + 360.5: 1.0265e00, 361.0: 9.4150e-01, 361.5: 9.1800e-01, 362.0: 9.5800e-01, - 362.5: 1.0450e+00, - 363.0: 1.0710e+00, - 363.5: 1.0380e+00, - 364.0: 1.0716e+00, - 364.5: 1.0590e+00, - 365.0: 1.0970e+00, - 365.5: 1.2041e+00, - 366.0: 1.2860e+00, - 366.5: 1.2843e+00, - 367.0: 1.2577e+00, - 367.5: 1.2317e+00, - 368.0: 1.1570e+00, - 368.5: 1.1459e+00, - 369.0: 1.1944e+00, - 369.5: 1.2795e+00, - 370.0: 1.2934e+00, - 370.5: 1.1660e+00, - 371.0: 1.1811e+00, - 371.5: 1.2249e+00, - 372.0: 1.1444e+00, - 372.5: 1.0876e+00, - 373.0: 1.0445e+00, + 362.5: 1.0450e00, + 363.0: 1.0710e00, + 363.5: 1.0380e00, + 364.0: 1.0716e00, + 364.5: 1.0590e00, + 365.0: 1.0970e00, + 365.5: 1.2041e00, + 366.0: 1.2860e00, + 366.5: 1.2843e00, + 367.0: 1.2577e00, + 367.5: 1.2317e00, + 368.0: 1.1570e00, + 368.5: 1.1459e00, + 369.0: 1.1944e00, + 369.5: 1.2795e00, + 370.0: 1.2934e00, + 370.5: 1.1660e00, + 371.0: 1.1811e00, + 371.5: 1.2249e00, + 372.0: 1.1444e00, + 372.5: 1.0876e00, + 373.0: 1.0445e00, 373.5: 9.3900e-01, 374.0: 9.3442e-01, 374.5: 9.2500e-01, 375.0: 9.8500e-01, - 375.5: 1.0874e+00, - 376.0: 1.1240e+00, - 376.5: 1.1040e+00, - 377.0: 1.1813e+00, - 377.5: 1.3149e+00, - 378.0: 1.4144e+00, - 378.5: 1.3765e+00, - 379.0: 1.2260e+00, - 379.5: 1.0980e+00, - 380.0: 1.1520e+00, - 380.5: 1.2310e+00, - 381.0: 1.2490e+00, - 381.5: 1.1224e+00, + 375.5: 1.0874e00, + 376.0: 1.1240e00, + 376.5: 1.1040e00, + 377.0: 1.1813e00, + 377.5: 1.3149e00, + 378.0: 1.4144e00, + 378.5: 1.3765e00, + 379.0: 1.2260e00, + 379.5: 1.0980e00, + 380.0: 1.1520e00, + 380.5: 1.2310e00, + 381.0: 1.2490e00, + 381.5: 1.1224e00, 382.0: 9.5426e-01, 382.5: 8.2313e-01, 383.0: 7.3603e-01, 383.5: 7.1095e-01, 384.0: 8.2100e-01, 384.5: 9.8620e-01, - 385.0: 1.0802e+00, - 385.5: 1.0296e+00, + 385.0: 1.0802e00, + 385.5: 1.0296e00, 386.0: 9.9113e-01, - 386.5: 1.0279e+00, - 387.0: 1.0354e+00, - 387.5: 1.0186e+00, - 388.0: 1.0067e+00, + 386.5: 1.0279e00, + 387.0: 1.0354e00, + 387.5: 1.0186e00, + 388.0: 1.0067e00, 388.5: 9.9743e-01, - 389.0: 1.0810e+00, - 389.5: 1.1958e+00, - 390.0: 1.2519e+00, - 390.5: 1.2601e+00, - 391.0: 1.3322e+00, - 391.5: 1.3490e+00, - 392.0: 1.2400e+00, - 392.5: 1.0312e+00, + 389.0: 1.0810e00, + 389.5: 1.1958e00, + 390.0: 1.2519e00, + 390.5: 1.2601e00, + 391.0: 1.3322e00, + 391.5: 1.3490e00, + 392.0: 1.2400e00, + 392.5: 1.0312e00, 393.0: 7.4500e-01, 393.5: 5.9120e-01, 394.0: 7.6675e-01, - 394.5: 1.0557e+00, - 395.0: 1.2450e+00, - 395.5: 1.3245e+00, - 396.0: 1.1626e+00, + 394.5: 1.0557e00, + 395.0: 1.2450e00, + 395.5: 1.3245e00, + 396.0: 1.1626e00, 396.5: 8.4330e-01, 397.0: 6.5200e-01, 397.5: 9.6142e-01, - 398.0: 1.3010e+00, - 398.5: 1.5342e+00, - 399.0: 1.6245e+00, - 399.5: 1.6717e+00, - 400.0: 1.6885e+00, - 401.0: 1.7520e+00, - 402.0: 1.8140e+00, - 403.0: 1.7400e+00, - 404.0: 1.7630e+00, - 405.0: 1.7150e+00, - 406.0: 1.6660e+00, - 407.0: 1.6300e+00, - 408.0: 1.6990e+00, - 409.0: 1.8090e+00, - 410.0: 1.5370e+00, - 411.0: 1.7150e+00, - 412.0: 1.8160e+00, - 413.0: 1.7392e+00, - 414.0: 1.7144e+00, - 415.0: 1.7688e+00, - 416.0: 1.8150e+00, - 417.0: 1.7660e+00, - 418.0: 1.6850e+00, - 419.0: 1.7490e+00, - 420.0: 1.5990e+00, - 421.0: 1.8110e+00, - 422.0: 1.7820e+00, - 423.0: 1.7210e+00, - 424.0: 1.7080e+00, - 425.0: 1.7550e+00, - 426.0: 1.6990e+00, - 427.0: 1.6380e+00, - 428.0: 1.6510e+00, - 429.0: 1.5230e+00, - 430.0: 1.2120e+00, - 431.0: 1.0990e+00, - 432.0: 1.8220e+00, - 433.0: 1.6913e+00, - 434.0: 1.5600e+00, - 435.0: 1.7090e+00, - 436.0: 1.8680e+00, - 437.0: 1.9000e+00, - 438.0: 1.6630e+00, - 439.0: 1.6010e+00, - 440.0: 1.8300e+00, - 441.0: 1.7990e+00, - 442.0: 1.9220e+00, - 443.0: 1.9490e+00, - 444.0: 1.8941e+00, - 445.0: 1.9650e+00, - 446.0: 1.7557e+00, - 447.0: 1.9900e+00, - 448.0: 2.0140e+00, - 449.0: 2.0010e+00, - 450.0: 2.0690e+00, - 451.0: 2.1420e+00, - 452.0: 2.0470e+00, - 453.0: 1.8864e+00, - 454.0: 2.0180e+00, - 455.0: 2.0010e+00, - 456.0: 2.0630e+00, - 457.0: 2.0770e+00, - 458.0: 2.0320e+00, - 459.0: 2.0120e+00, - 460.0: 1.9973e+00, - 461.0: 2.0639e+00, - 462.0: 2.0780e+00, - 463.0: 2.0840e+00, - 464.0: 2.0150e+00, - 465.0: 1.9840e+00, - 466.0: 2.0210e+00, - 467.0: 1.9310e+00, - 468.0: 2.0120e+00, - 469.0: 2.0180e+00, - 470.0: 1.9390e+00, - 471.0: 1.9690e+00, - 472.0: 2.0700e+00, - 473.0: 1.9882e+00, - 474.0: 2.0120e+00, - 475.0: 2.0800e+00, - 476.0: 2.0120e+00, - 477.0: 2.0250e+00, - 478.0: 2.0860e+00, - 479.0: 2.0400e+00, - 480.0: 2.0680e+00, - 481.0: 2.0610e+00, - 482.0: 2.0623e+00, - 483.0: 2.0310e+00, - 484.0: 1.9890e+00, - 485.0: 1.9790e+00, - 486.0: 1.6010e+00, - 487.0: 1.7890e+00, - 488.0: 1.9350e+00, - 489.0: 1.8224e+00, - 490.0: 2.0320e+00, - 491.0: 1.9490e+00, - 492.0: 1.8560e+00, - 493.0: 1.9830e+00, - 494.0: 1.9339e+00, - 495.0: 2.0510e+00, - 496.0: 1.9490e+00, - 497.0: 1.9800e+00, - 498.0: 1.9240e+00, - 499.0: 1.9230e+00, - 500.0: 1.9160e+00, - 501.0: 1.8580e+00, - 502.0: 1.8600e+00, - 503.0: 1.9490e+00, - 504.0: 1.8330e+00, - 505.0: 1.9472e+00, - 506.0: 2.0250e+00, - 507.0: 1.9354e+00, - 508.0: 1.8800e+00, - 509.0: 1.9650e+00, - 510.0: 1.9100e+00, - 511.0: 1.9410e+00, - 512.0: 1.9890e+00, - 513.0: 1.8660e+00, - 514.0: 1.8240e+00, - 515.0: 1.8750e+00, - 516.0: 1.8910e+00, - 517.0: 1.5390e+00, - 518.0: 1.7590e+00, - 519.0: 1.7040e+00, - 520.0: 1.8600e+00, - 521.0: 1.8730e+00, - 522.0: 1.9150e+00, - 523.0: 1.8040e+00, - 524.0: 1.9410e+00, - 525.0: 1.9280e+00, - 526.0: 1.8740e+00, - 527.0: 1.6410e+00, - 528.0: 1.8800e+00, - 529.0: 1.9690e+00, - 530.0: 1.8920e+00, - 531.0: 1.9950e+00, - 532.0: 1.9580e+00, - 533.0: 1.7470e+00, - 534.0: 1.8690e+00, - 535.0: 1.8950e+00, - 536.0: 1.9740e+00, - 537.0: 1.8240e+00, - 538.0: 1.9130e+00, - 539.0: 1.8640e+00, - 540.0: 1.8000e+00, - 541.0: 1.7340e+00, - 542.0: 1.8880e+00, - 543.0: 1.8510e+00, - 544.0: 1.9190e+00, - 545.0: 1.8740e+00, - 546.0: 1.8609e+00, - 547.0: 1.8820e+00, - 548.0: 1.8260e+00, - 549.0: 1.8800e+00, - 550.0: 1.8630e+00, - 551.0: 1.8590e+00, - 552.0: 1.8960e+00, - 553.0: 1.8420e+00, - 554.0: 1.8780e+00, - 555.0: 1.8890e+00, - 556.0: 1.8570e+00, - 557.0: 1.8120e+00, - 558.0: 1.8530e+00, - 559.0: 1.7550e+00, - 560.0: 1.7860e+00, - 561.0: 1.8900e+00, - 562.0: 1.8010e+00, - 563.0: 1.8710e+00, - 564.0: 1.8360e+00, - 565.0: 1.8490e+00, - 566.0: 1.7500e+00, - 567.0: 1.8680e+00, - 568.0: 1.8590e+00, - 569.0: 1.8310e+00, - 570.0: 1.8280e+00, - 571.0: 1.7620e+00, - 572.0: 1.8720e+00, - 573.0: 1.8810e+00, - 574.0: 1.8730e+00, - 575.0: 1.8340e+00, - 576.0: 1.8180e+00, - 577.0: 1.8620e+00, - 578.0: 1.7990e+00, - 579.0: 1.8160e+00, - 580.0: 1.8340e+00, - 581.0: 1.8330e+00, - 582.0: 1.8520e+00, - 583.0: 1.8630e+00, - 584.0: 1.8540e+00, - 585.0: 1.8360e+00, - 586.0: 1.7920e+00, - 587.0: 1.8380e+00, - 588.0: 1.8210e+00, - 589.0: 1.6240e+00, - 590.0: 1.7218e+00, - 591.0: 1.8090e+00, - 592.0: 1.7880e+00, - 593.0: 1.7920e+00, - 594.0: 1.7890e+00, - 595.0: 1.7780e+00, - 596.0: 1.7960e+00, - 597.0: 1.8060e+00, - 598.0: 1.7720e+00, - 599.0: 1.7640e+00, - 600.0: 1.7700e+00, - 601.0: 1.7420e+00, - 602.0: 1.7150e+00, - 603.0: 1.7490e+00, - 604.0: 1.7790e+00, - 605.0: 1.7730e+00, - 606.0: 1.7580e+00, - 607.0: 1.7620e+00, - 608.0: 1.7510e+00, - 609.0: 1.7340e+00, - 610.0: 1.7240e+00, - 611.0: 1.7120e+00, - 612.0: 1.7360e+00, - 613.0: 1.7100e+00, - 614.0: 1.6550e+00, - 615.0: 1.7120e+00, - 616.0: 1.6640e+00, - 617.0: 1.6410e+00, - 618.0: 1.7020e+00, - 619.0: 1.7090e+00, - 620.0: 1.7110e+00, - 621.0: 1.7240e+00, - 622.0: 1.6784e+00, - 623.0: 1.6820e+00, - 624.0: 1.6670e+00, - 625.0: 1.6440e+00, - 626.0: 1.6400e+00, - 627.0: 1.6930e+00, - 628.0: 1.6930e+00, - 629.0: 1.6870e+00, - 630.0: 1.6650e+00, - 631.0: 1.6590e+00, - 632.0: 1.5901e+00, - 633.0: 1.6740e+00, - 634.0: 1.6370e+00, - 635.0: 1.6520e+00, - 636.0: 1.6093e+00, - 637.0: 1.6610e+00, - 638.0: 1.6650e+00, - 639.0: 1.6530e+00, - 640.0: 1.6130e+00, - 641.0: 1.6100e+00, - 642.0: 1.6090e+00, - 643.0: 1.6250e+00, - 644.0: 1.6140e+00, - 645.0: 1.6270e+00, - 646.0: 1.5910e+00, - 647.0: 1.6060e+00, - 648.0: 1.6020e+00, - 649.0: 1.5510e+00, - 650.0: 1.5260e+00, - 651.0: 1.6130e+00, - 652.0: 1.5910e+00, - 653.0: 1.5980e+00, - 654.0: 1.5750e+00, - 655.0: 1.5230e+00, - 656.0: 1.3233e+00, - 657.0: 1.3840e+00, - 658.0: 1.5390e+00, - 659.0: 1.5420e+00, - 660.0: 1.5580e+00, - 661.0: 1.5660e+00, - 662.0: 1.5710e+00, - 663.0: 1.5630e+00, - 664.0: 1.5540e+00, - 665.0: 1.5670e+00, - 666.0: 1.5550e+00, - 667.0: 1.5354e+00, - 668.0: 1.5348e+00, - 669.0: 1.5580e+00, - 670.0: 1.5340e+00, - 671.0: 1.5290e+00, - 672.0: 1.5060e+00, - 673.0: 1.5170e+00, - 674.0: 1.5130e+00, - 675.0: 1.4990e+00, - 676.0: 1.5150e+00, - 677.0: 1.5000e+00, - 678.0: 1.5070e+00, - 679.0: 1.4930e+00, - 680.0: 1.4940e+00, - 681.0: 1.4870e+00, - 682.0: 1.4930e+00, - 683.0: 1.4760e+00, - 684.0: 1.4660e+00, - 685.0: 1.4650e+00, - 686.0: 1.4330e+00, - 687.0: 1.4720e+00, - 688.0: 1.4760e+00, - 689.0: 1.4780e+00, - 690.0: 1.4790e+00, - 691.0: 1.4680e+00, - 692.0: 1.4540e+00, - 693.0: 1.4580e+00, - 694.0: 1.4570e+00, - 695.0: 1.4350e+00, - 696.0: 1.4420e+00, - 697.0: 1.4380e+00, - 698.0: 1.4170e+00, - 699.0: 1.4340e+00, - 700.0: 1.4220e+00, - 701.0: 1.4131e+00, - 702.0: 1.3987e+00, - 703.0: 1.4095e+00, - 704.0: 1.4187e+00, - 705.0: 1.4330e+00, - 706.0: 1.4138e+00, - 707.0: 1.4040e+00, - 708.0: 1.3990e+00, - 709.0: 1.3900e+00, - 710.0: 1.4040e+00, - 711.0: 1.3970e+00, - 712.0: 1.3818e+00, - 713.0: 1.3702e+00, - 714.0: 1.3819e+00, - 715.0: 1.3502e+00, - 716.0: 1.3694e+00, - 717.0: 1.3650e+00, - 718.0: 1.3570e+00, - 719.0: 1.3010e+00, - 720.0: 1.3487e+00, - 721.0: 1.3480e+00, - 722.0: 1.3600e+00, - 723.0: 1.3510e+00, - 724.0: 1.3607e+00, - 725.0: 1.3465e+00, - 726.0: 1.3429e+00, - 727.0: 1.3444e+00, - 728.0: 1.3370e+00, - 729.0: 1.2796e+00, - 730.0: 1.3357e+00, - 731.0: 1.3104e+00, - 732.0: 1.3333e+00, - 733.0: 1.3108e+00, - 734.0: 1.3390e+00, - 735.0: 1.3271e+00, - 736.0: 1.3100e+00, - 737.0: 1.3120e+00, - 738.0: 1.3000e+00, - 739.0: 1.2646e+00, - 740.0: 1.2830e+00, - 741.0: 1.2707e+00, - 742.0: 1.2649e+00, - 743.0: 1.2892e+00, - 744.0: 1.2955e+00, - 745.0: 1.2920e+00, - 746.0: 1.2892e+00, - 747.0: 1.2890e+00, - 748.0: 1.2808e+00, - 749.0: 1.2760e+00, - 750.0: 1.2740e+00, - 751.0: 1.2680e+00, - 752.0: 1.2720e+00, - 753.0: 1.2650e+00, - 754.0: 1.2809e+00, - 755.0: 1.2771e+00, - 756.0: 1.2610e+00, - 757.0: 1.2598e+00, - 758.0: 1.2680e+00, - 759.0: 1.2500e+00, - 760.0: 1.2590e+00, - 761.0: 1.2487e+00, - 762.0: 1.2460e+00, - 763.0: 1.2543e+00, - 764.0: 1.2566e+00, - 765.0: 1.2452e+00, - 766.0: 1.2000e+00, - 767.0: 1.2120e+00, - 768.0: 1.2310e+00, - 769.0: 1.2142e+00, - 770.0: 1.2146e+00, - 771.0: 1.2073e+00, - 772.0: 1.2120e+00, - 773.0: 1.2100e+00, - 774.0: 1.2090e+00, - 775.0: 1.2080e+00, - 776.0: 1.2102e+00, - 777.0: 1.2028e+00, - 778.0: 1.2014e+00, - 779.0: 1.2067e+00, - 780.0: 1.1930e+00, - 781.0: 1.1898e+00, - 782.0: 1.1950e+00, - 783.0: 1.1896e+00, - 784.0: 1.1810e+00, - 785.0: 1.1874e+00, - 786.0: 1.1890e+00, - 787.0: 1.1877e+00, - 788.0: 1.1800e+00, - 789.0: 1.1812e+00, - 790.0: 1.1704e+00, - 791.0: 1.1632e+00, - 792.0: 1.1500e+00, - 793.0: 1.1449e+00, - 794.0: 1.1260e+00, - 795.0: 1.1280e+00, - 796.0: 1.1427e+00, - 797.0: 1.1580e+00, - 798.0: 1.1501e+00, - 799.0: 1.1350e+00, - 800.0: 1.1248e+00, - 801.0: 1.1427e+00, - 802.0: 1.1422e+00, - 803.0: 1.1241e+00, - 804.0: 1.1342e+00, - 805.0: 1.1001e+00, - 806.0: 1.1309e+00, - 807.0: 1.1230e+00, - 808.0: 1.1162e+00, - 809.0: 1.0980e+00, - 810.0: 1.1100e+00, - 811.0: 1.1143e+00, - 812.0: 1.1160e+00, - 813.0: 1.1180e+00, - 814.0: 1.1174e+00, - 815.0: 1.1091e+00, - 816.0: 1.1050e+00, - 817.0: 1.1083e+00, - 818.0: 1.0861e+00, - 819.0: 1.0738e+00, - 820.0: 1.0740e+00, - 821.0: 1.0760e+00, - 822.0: 1.0560e+00, - 823.0: 1.0760e+00, - 824.0: 1.0715e+00, - 825.0: 1.0699e+00, - 826.0: 1.0759e+00, - 827.0: 1.0762e+00, - 828.0: 1.0749e+00, - 829.0: 1.0620e+00, - 830.0: 1.0563e+00, - 831.0: 1.0560e+00, - 832.0: 1.0550e+00, - 833.0: 1.0327e+00, - 834.0: 1.0459e+00, - 835.0: 1.0476e+00, - 836.0: 1.0597e+00, - 837.0: 1.0511e+00, - 838.0: 1.0549e+00, - 839.0: 1.0400e+00, - 840.0: 1.0500e+00, - 841.0: 1.0520e+00, - 842.0: 1.0287e+00, - 843.0: 1.0271e+00, - 844.0: 1.0140e+00, - 845.0: 1.0360e+00, - 846.0: 1.0370e+00, - 847.0: 1.0060e+00, - 848.0: 1.0110e+00, - 849.0: 1.0050e+00, + 398.0: 1.3010e00, + 398.5: 1.5342e00, + 399.0: 1.6245e00, + 399.5: 1.6717e00, + 400.0: 1.6885e00, + 401.0: 1.7520e00, + 402.0: 1.8140e00, + 403.0: 1.7400e00, + 404.0: 1.7630e00, + 405.0: 1.7150e00, + 406.0: 1.6660e00, + 407.0: 1.6300e00, + 408.0: 1.6990e00, + 409.0: 1.8090e00, + 410.0: 1.5370e00, + 411.0: 1.7150e00, + 412.0: 1.8160e00, + 413.0: 1.7392e00, + 414.0: 1.7144e00, + 415.0: 1.7688e00, + 416.0: 1.8150e00, + 417.0: 1.7660e00, + 418.0: 1.6850e00, + 419.0: 1.7490e00, + 420.0: 1.5990e00, + 421.0: 1.8110e00, + 422.0: 1.7820e00, + 423.0: 1.7210e00, + 424.0: 1.7080e00, + 425.0: 1.7550e00, + 426.0: 1.6990e00, + 427.0: 1.6380e00, + 428.0: 1.6510e00, + 429.0: 1.5230e00, + 430.0: 1.2120e00, + 431.0: 1.0990e00, + 432.0: 1.8220e00, + 433.0: 1.6913e00, + 434.0: 1.5600e00, + 435.0: 1.7090e00, + 436.0: 1.8680e00, + 437.0: 1.9000e00, + 438.0: 1.6630e00, + 439.0: 1.6010e00, + 440.0: 1.8300e00, + 441.0: 1.7990e00, + 442.0: 1.9220e00, + 443.0: 1.9490e00, + 444.0: 1.8941e00, + 445.0: 1.9650e00, + 446.0: 1.7557e00, + 447.0: 1.9900e00, + 448.0: 2.0140e00, + 449.0: 2.0010e00, + 450.0: 2.0690e00, + 451.0: 2.1420e00, + 452.0: 2.0470e00, + 453.0: 1.8864e00, + 454.0: 2.0180e00, + 455.0: 2.0010e00, + 456.0: 2.0630e00, + 457.0: 2.0770e00, + 458.0: 2.0320e00, + 459.0: 2.0120e00, + 460.0: 1.9973e00, + 461.0: 2.0639e00, + 462.0: 2.0780e00, + 463.0: 2.0840e00, + 464.0: 2.0150e00, + 465.0: 1.9840e00, + 466.0: 2.0210e00, + 467.0: 1.9310e00, + 468.0: 2.0120e00, + 469.0: 2.0180e00, + 470.0: 1.9390e00, + 471.0: 1.9690e00, + 472.0: 2.0700e00, + 473.0: 1.9882e00, + 474.0: 2.0120e00, + 475.0: 2.0800e00, + 476.0: 2.0120e00, + 477.0: 2.0250e00, + 478.0: 2.0860e00, + 479.0: 2.0400e00, + 480.0: 2.0680e00, + 481.0: 2.0610e00, + 482.0: 2.0623e00, + 483.0: 2.0310e00, + 484.0: 1.9890e00, + 485.0: 1.9790e00, + 486.0: 1.6010e00, + 487.0: 1.7890e00, + 488.0: 1.9350e00, + 489.0: 1.8224e00, + 490.0: 2.0320e00, + 491.0: 1.9490e00, + 492.0: 1.8560e00, + 493.0: 1.9830e00, + 494.0: 1.9339e00, + 495.0: 2.0510e00, + 496.0: 1.9490e00, + 497.0: 1.9800e00, + 498.0: 1.9240e00, + 499.0: 1.9230e00, + 500.0: 1.9160e00, + 501.0: 1.8580e00, + 502.0: 1.8600e00, + 503.0: 1.9490e00, + 504.0: 1.8330e00, + 505.0: 1.9472e00, + 506.0: 2.0250e00, + 507.0: 1.9354e00, + 508.0: 1.8800e00, + 509.0: 1.9650e00, + 510.0: 1.9100e00, + 511.0: 1.9410e00, + 512.0: 1.9890e00, + 513.0: 1.8660e00, + 514.0: 1.8240e00, + 515.0: 1.8750e00, + 516.0: 1.8910e00, + 517.0: 1.5390e00, + 518.0: 1.7590e00, + 519.0: 1.7040e00, + 520.0: 1.8600e00, + 521.0: 1.8730e00, + 522.0: 1.9150e00, + 523.0: 1.8040e00, + 524.0: 1.9410e00, + 525.0: 1.9280e00, + 526.0: 1.8740e00, + 527.0: 1.6410e00, + 528.0: 1.8800e00, + 529.0: 1.9690e00, + 530.0: 1.8920e00, + 531.0: 1.9950e00, + 532.0: 1.9580e00, + 533.0: 1.7470e00, + 534.0: 1.8690e00, + 535.0: 1.8950e00, + 536.0: 1.9740e00, + 537.0: 1.8240e00, + 538.0: 1.9130e00, + 539.0: 1.8640e00, + 540.0: 1.8000e00, + 541.0: 1.7340e00, + 542.0: 1.8880e00, + 543.0: 1.8510e00, + 544.0: 1.9190e00, + 545.0: 1.8740e00, + 546.0: 1.8609e00, + 547.0: 1.8820e00, + 548.0: 1.8260e00, + 549.0: 1.8800e00, + 550.0: 1.8630e00, + 551.0: 1.8590e00, + 552.0: 1.8960e00, + 553.0: 1.8420e00, + 554.0: 1.8780e00, + 555.0: 1.8890e00, + 556.0: 1.8570e00, + 557.0: 1.8120e00, + 558.0: 1.8530e00, + 559.0: 1.7550e00, + 560.0: 1.7860e00, + 561.0: 1.8900e00, + 562.0: 1.8010e00, + 563.0: 1.8710e00, + 564.0: 1.8360e00, + 565.0: 1.8490e00, + 566.0: 1.7500e00, + 567.0: 1.8680e00, + 568.0: 1.8590e00, + 569.0: 1.8310e00, + 570.0: 1.8280e00, + 571.0: 1.7620e00, + 572.0: 1.8720e00, + 573.0: 1.8810e00, + 574.0: 1.8730e00, + 575.0: 1.8340e00, + 576.0: 1.8180e00, + 577.0: 1.8620e00, + 578.0: 1.7990e00, + 579.0: 1.8160e00, + 580.0: 1.8340e00, + 581.0: 1.8330e00, + 582.0: 1.8520e00, + 583.0: 1.8630e00, + 584.0: 1.8540e00, + 585.0: 1.8360e00, + 586.0: 1.7920e00, + 587.0: 1.8380e00, + 588.0: 1.8210e00, + 589.0: 1.6240e00, + 590.0: 1.7218e00, + 591.0: 1.8090e00, + 592.0: 1.7880e00, + 593.0: 1.7920e00, + 594.0: 1.7890e00, + 595.0: 1.7780e00, + 596.0: 1.7960e00, + 597.0: 1.8060e00, + 598.0: 1.7720e00, + 599.0: 1.7640e00, + 600.0: 1.7700e00, + 601.0: 1.7420e00, + 602.0: 1.7150e00, + 603.0: 1.7490e00, + 604.0: 1.7790e00, + 605.0: 1.7730e00, + 606.0: 1.7580e00, + 607.0: 1.7620e00, + 608.0: 1.7510e00, + 609.0: 1.7340e00, + 610.0: 1.7240e00, + 611.0: 1.7120e00, + 612.0: 1.7360e00, + 613.0: 1.7100e00, + 614.0: 1.6550e00, + 615.0: 1.7120e00, + 616.0: 1.6640e00, + 617.0: 1.6410e00, + 618.0: 1.7020e00, + 619.0: 1.7090e00, + 620.0: 1.7110e00, + 621.0: 1.7240e00, + 622.0: 1.6784e00, + 623.0: 1.6820e00, + 624.0: 1.6670e00, + 625.0: 1.6440e00, + 626.0: 1.6400e00, + 627.0: 1.6930e00, + 628.0: 1.6930e00, + 629.0: 1.6870e00, + 630.0: 1.6650e00, + 631.0: 1.6590e00, + 632.0: 1.5901e00, + 633.0: 1.6740e00, + 634.0: 1.6370e00, + 635.0: 1.6520e00, + 636.0: 1.6093e00, + 637.0: 1.6610e00, + 638.0: 1.6650e00, + 639.0: 1.6530e00, + 640.0: 1.6130e00, + 641.0: 1.6100e00, + 642.0: 1.6090e00, + 643.0: 1.6250e00, + 644.0: 1.6140e00, + 645.0: 1.6270e00, + 646.0: 1.5910e00, + 647.0: 1.6060e00, + 648.0: 1.6020e00, + 649.0: 1.5510e00, + 650.0: 1.5260e00, + 651.0: 1.6130e00, + 652.0: 1.5910e00, + 653.0: 1.5980e00, + 654.0: 1.5750e00, + 655.0: 1.5230e00, + 656.0: 1.3233e00, + 657.0: 1.3840e00, + 658.0: 1.5390e00, + 659.0: 1.5420e00, + 660.0: 1.5580e00, + 661.0: 1.5660e00, + 662.0: 1.5710e00, + 663.0: 1.5630e00, + 664.0: 1.5540e00, + 665.0: 1.5670e00, + 666.0: 1.5550e00, + 667.0: 1.5354e00, + 668.0: 1.5348e00, + 669.0: 1.5580e00, + 670.0: 1.5340e00, + 671.0: 1.5290e00, + 672.0: 1.5060e00, + 673.0: 1.5170e00, + 674.0: 1.5130e00, + 675.0: 1.4990e00, + 676.0: 1.5150e00, + 677.0: 1.5000e00, + 678.0: 1.5070e00, + 679.0: 1.4930e00, + 680.0: 1.4940e00, + 681.0: 1.4870e00, + 682.0: 1.4930e00, + 683.0: 1.4760e00, + 684.0: 1.4660e00, + 685.0: 1.4650e00, + 686.0: 1.4330e00, + 687.0: 1.4720e00, + 688.0: 1.4760e00, + 689.0: 1.4780e00, + 690.0: 1.4790e00, + 691.0: 1.4680e00, + 692.0: 1.4540e00, + 693.0: 1.4580e00, + 694.0: 1.4570e00, + 695.0: 1.4350e00, + 696.0: 1.4420e00, + 697.0: 1.4380e00, + 698.0: 1.4170e00, + 699.0: 1.4340e00, + 700.0: 1.4220e00, + 701.0: 1.4131e00, + 702.0: 1.3987e00, + 703.0: 1.4095e00, + 704.0: 1.4187e00, + 705.0: 1.4330e00, + 706.0: 1.4138e00, + 707.0: 1.4040e00, + 708.0: 1.3990e00, + 709.0: 1.3900e00, + 710.0: 1.4040e00, + 711.0: 1.3970e00, + 712.0: 1.3818e00, + 713.0: 1.3702e00, + 714.0: 1.3819e00, + 715.0: 1.3502e00, + 716.0: 1.3694e00, + 717.0: 1.3650e00, + 718.0: 1.3570e00, + 719.0: 1.3010e00, + 720.0: 1.3487e00, + 721.0: 1.3480e00, + 722.0: 1.3600e00, + 723.0: 1.3510e00, + 724.0: 1.3607e00, + 725.0: 1.3465e00, + 726.0: 1.3429e00, + 727.0: 1.3444e00, + 728.0: 1.3370e00, + 729.0: 1.2796e00, + 730.0: 1.3357e00, + 731.0: 1.3104e00, + 732.0: 1.3333e00, + 733.0: 1.3108e00, + 734.0: 1.3390e00, + 735.0: 1.3271e00, + 736.0: 1.3100e00, + 737.0: 1.3120e00, + 738.0: 1.3000e00, + 739.0: 1.2646e00, + 740.0: 1.2830e00, + 741.0: 1.2707e00, + 742.0: 1.2649e00, + 743.0: 1.2892e00, + 744.0: 1.2955e00, + 745.0: 1.2920e00, + 746.0: 1.2892e00, + 747.0: 1.2890e00, + 748.0: 1.2808e00, + 749.0: 1.2760e00, + 750.0: 1.2740e00, + 751.0: 1.2680e00, + 752.0: 1.2720e00, + 753.0: 1.2650e00, + 754.0: 1.2809e00, + 755.0: 1.2771e00, + 756.0: 1.2610e00, + 757.0: 1.2598e00, + 758.0: 1.2680e00, + 759.0: 1.2500e00, + 760.0: 1.2590e00, + 761.0: 1.2487e00, + 762.0: 1.2460e00, + 763.0: 1.2543e00, + 764.0: 1.2566e00, + 765.0: 1.2452e00, + 766.0: 1.2000e00, + 767.0: 1.2120e00, + 768.0: 1.2310e00, + 769.0: 1.2142e00, + 770.0: 1.2146e00, + 771.0: 1.2073e00, + 772.0: 1.2120e00, + 773.0: 1.2100e00, + 774.0: 1.2090e00, + 775.0: 1.2080e00, + 776.0: 1.2102e00, + 777.0: 1.2028e00, + 778.0: 1.2014e00, + 779.0: 1.2067e00, + 780.0: 1.1930e00, + 781.0: 1.1898e00, + 782.0: 1.1950e00, + 783.0: 1.1896e00, + 784.0: 1.1810e00, + 785.0: 1.1874e00, + 786.0: 1.1890e00, + 787.0: 1.1877e00, + 788.0: 1.1800e00, + 789.0: 1.1812e00, + 790.0: 1.1704e00, + 791.0: 1.1632e00, + 792.0: 1.1500e00, + 793.0: 1.1449e00, + 794.0: 1.1260e00, + 795.0: 1.1280e00, + 796.0: 1.1427e00, + 797.0: 1.1580e00, + 798.0: 1.1501e00, + 799.0: 1.1350e00, + 800.0: 1.1248e00, + 801.0: 1.1427e00, + 802.0: 1.1422e00, + 803.0: 1.1241e00, + 804.0: 1.1342e00, + 805.0: 1.1001e00, + 806.0: 1.1309e00, + 807.0: 1.1230e00, + 808.0: 1.1162e00, + 809.0: 1.0980e00, + 810.0: 1.1100e00, + 811.0: 1.1143e00, + 812.0: 1.1160e00, + 813.0: 1.1180e00, + 814.0: 1.1174e00, + 815.0: 1.1091e00, + 816.0: 1.1050e00, + 817.0: 1.1083e00, + 818.0: 1.0861e00, + 819.0: 1.0738e00, + 820.0: 1.0740e00, + 821.0: 1.0760e00, + 822.0: 1.0560e00, + 823.0: 1.0760e00, + 824.0: 1.0715e00, + 825.0: 1.0699e00, + 826.0: 1.0759e00, + 827.0: 1.0762e00, + 828.0: 1.0749e00, + 829.0: 1.0620e00, + 830.0: 1.0563e00, + 831.0: 1.0560e00, + 832.0: 1.0550e00, + 833.0: 1.0327e00, + 834.0: 1.0459e00, + 835.0: 1.0476e00, + 836.0: 1.0597e00, + 837.0: 1.0511e00, + 838.0: 1.0549e00, + 839.0: 1.0400e00, + 840.0: 1.0500e00, + 841.0: 1.0520e00, + 842.0: 1.0287e00, + 843.0: 1.0271e00, + 844.0: 1.0140e00, + 845.0: 1.0360e00, + 846.0: 1.0370e00, + 847.0: 1.0060e00, + 848.0: 1.0110e00, + 849.0: 1.0050e00, 850.0: 9.1000e-01, 851.0: 9.9800e-01, 852.0: 9.9000e-01, @@ -731,13 +734,13 @@ 854.0: 8.6900e-01, 855.0: 9.2700e-01, 856.0: 9.9017e-01, - 857.0: 1.0056e+00, - 858.0: 1.0070e+00, - 859.0: 1.0050e+00, - 860.0: 1.0000e+00, + 857.0: 1.0056e00, + 858.0: 1.0070e00, + 859.0: 1.0050e00, + 860.0: 1.0000e00, 861.0: 9.9900e-01, - 862.0: 1.0060e+00, - 863.0: 1.0120e+00, + 862.0: 1.0060e00, + 863.0: 1.0120e00, 864.0: 9.9006e-01, 865.0: 9.7354e-01, 866.0: 8.5800e-01, @@ -2038,7 +2041,7 @@ 4000.0: 8.6800e-03, } -DATA_ASTMG173_GLOBAL_TILT = { +DATA_ASTMG173_GLOBAL_TILT: Dict = { 280.0: 4.7309e-23, 280.5: 1.2307e-21, 281.0: 5.6895e-21, @@ -2276,369 +2279,369 @@ 397.0: 4.2619e-01, 397.5: 6.2945e-01, 398.0: 8.5249e-01, - 398.5: 1.0069e+00, - 399.0: 1.0693e+00, - 399.5: 1.1021e+00, - 400.0: 1.1141e+00, - 401.0: 1.1603e+00, - 402.0: 1.2061e+00, - 403.0: 1.1613e+00, - 404.0: 1.1801e+00, - 405.0: 1.1511e+00, - 406.0: 1.1227e+00, - 407.0: 1.1026e+00, - 408.0: 1.1514e+00, - 409.0: 1.2299e+00, - 410.0: 1.0485e+00, - 411.0: 1.1738e+00, - 412.0: 1.2478e+00, - 413.0: 1.1971e+00, - 414.0: 1.1842e+00, - 415.0: 1.2258e+00, - 416.0: 1.2624e+00, - 417.0: 1.2312e+00, - 418.0: 1.1777e+00, - 419.0: 1.2258e+00, - 420.0: 1.1232e+00, - 421.0: 1.2757e+00, - 422.0: 1.2583e+00, - 423.0: 1.2184e+00, - 424.0: 1.2117e+00, - 425.0: 1.2488e+00, - 426.0: 1.2135e+00, - 427.0: 1.1724e+00, - 428.0: 1.1839e+00, - 429.0: 1.0963e+00, + 398.5: 1.0069e00, + 399.0: 1.0693e00, + 399.5: 1.1021e00, + 400.0: 1.1141e00, + 401.0: 1.1603e00, + 402.0: 1.2061e00, + 403.0: 1.1613e00, + 404.0: 1.1801e00, + 405.0: 1.1511e00, + 406.0: 1.1227e00, + 407.0: 1.1026e00, + 408.0: 1.1514e00, + 409.0: 1.2299e00, + 410.0: 1.0485e00, + 411.0: 1.1738e00, + 412.0: 1.2478e00, + 413.0: 1.1971e00, + 414.0: 1.1842e00, + 415.0: 1.2258e00, + 416.0: 1.2624e00, + 417.0: 1.2312e00, + 418.0: 1.1777e00, + 419.0: 1.2258e00, + 420.0: 1.1232e00, + 421.0: 1.2757e00, + 422.0: 1.2583e00, + 423.0: 1.2184e00, + 424.0: 1.2117e00, + 425.0: 1.2488e00, + 426.0: 1.2135e00, + 427.0: 1.1724e00, + 428.0: 1.1839e00, + 429.0: 1.0963e00, 430.0: 8.7462e-01, 431.0: 7.9394e-01, - 432.0: 1.3207e+00, - 433.0: 1.2288e+00, - 434.0: 1.1352e+00, - 435.0: 1.2452e+00, - 436.0: 1.3659e+00, - 437.0: 1.3943e+00, - 438.0: 1.2238e+00, - 439.0: 1.1775e+00, - 440.0: 1.3499e+00, - 441.0: 1.3313e+00, - 442.0: 1.4250e+00, - 443.0: 1.4453e+00, - 444.0: 1.4084e+00, - 445.0: 1.4619e+00, - 446.0: 1.3108e+00, - 447.0: 1.4903e+00, - 448.0: 1.5081e+00, - 449.0: 1.5045e+00, - 450.0: 1.5595e+00, - 451.0: 1.6173e+00, - 452.0: 1.5482e+00, - 453.0: 1.4297e+00, - 454.0: 1.5335e+00, - 455.0: 1.5224e+00, - 456.0: 1.5724e+00, - 457.0: 1.5854e+00, - 458.0: 1.5514e+00, - 459.0: 1.5391e+00, - 460.0: 1.5291e+00, - 461.0: 1.5827e+00, - 462.0: 1.5975e+00, - 463.0: 1.6031e+00, - 464.0: 1.5544e+00, - 465.0: 1.5350e+00, - 466.0: 1.5673e+00, - 467.0: 1.4973e+00, - 468.0: 1.5619e+00, - 469.0: 1.5682e+00, - 470.0: 1.5077e+00, - 471.0: 1.5331e+00, - 472.0: 1.6126e+00, - 473.0: 1.5499e+00, - 474.0: 1.5671e+00, - 475.0: 1.6185e+00, - 476.0: 1.5631e+00, - 477.0: 1.5724e+00, - 478.0: 1.6230e+00, - 479.0: 1.5916e+00, - 480.0: 1.6181e+00, - 481.0: 1.6177e+00, - 482.0: 1.6236e+00, - 483.0: 1.6038e+00, - 484.0: 1.5734e+00, - 485.0: 1.5683e+00, - 486.0: 1.2716e+00, - 487.0: 1.4241e+00, - 488.0: 1.5413e+00, - 489.0: 1.4519e+00, - 490.0: 1.6224e+00, - 491.0: 1.5595e+00, - 492.0: 1.4869e+00, - 493.0: 1.5903e+00, - 494.0: 1.5525e+00, - 495.0: 1.6485e+00, - 496.0: 1.5676e+00, - 497.0: 1.5944e+00, - 498.0: 1.5509e+00, - 499.0: 1.5507e+00, - 500.0: 1.5451e+00, - 501.0: 1.4978e+00, - 502.0: 1.4966e+00, - 503.0: 1.5653e+00, - 504.0: 1.4587e+00, - 505.0: 1.5635e+00, - 506.0: 1.6264e+00, - 507.0: 1.5560e+00, - 508.0: 1.5165e+00, - 509.0: 1.5893e+00, - 510.0: 1.5481e+00, - 511.0: 1.5769e+00, - 512.0: 1.6186e+00, - 513.0: 1.5206e+00, - 514.0: 1.4885e+00, - 515.0: 1.5314e+00, - 516.0: 1.5455e+00, - 517.0: 1.2594e+00, - 518.0: 1.4403e+00, - 519.0: 1.3957e+00, - 520.0: 1.5236e+00, - 521.0: 1.5346e+00, - 522.0: 1.5690e+00, - 523.0: 1.4789e+00, - 524.0: 1.5905e+00, - 525.0: 1.5781e+00, - 526.0: 1.5341e+00, - 527.0: 1.3417e+00, - 528.0: 1.5357e+00, - 529.0: 1.6071e+00, - 530.0: 1.5446e+00, - 531.0: 1.6292e+00, - 532.0: 1.5998e+00, - 533.0: 1.4286e+00, - 534.0: 1.5302e+00, - 535.0: 1.5535e+00, - 536.0: 1.6199e+00, - 537.0: 1.4989e+00, - 538.0: 1.5738e+00, - 539.0: 1.5352e+00, - 540.0: 1.4825e+00, - 541.0: 1.4251e+00, - 542.0: 1.5511e+00, - 543.0: 1.5256e+00, - 544.0: 1.5792e+00, - 545.0: 1.5435e+00, - 546.0: 1.5291e+00, - 547.0: 1.5490e+00, - 548.0: 1.5049e+00, - 549.0: 1.5520e+00, - 550.0: 1.5399e+00, - 551.0: 1.5382e+00, - 552.0: 1.5697e+00, - 553.0: 1.5250e+00, - 554.0: 1.5549e+00, - 555.0: 1.5634e+00, - 556.0: 1.5366e+00, - 557.0: 1.4988e+00, - 558.0: 1.5310e+00, - 559.0: 1.4483e+00, - 560.0: 1.4740e+00, - 561.0: 1.5595e+00, - 562.0: 1.4847e+00, - 563.0: 1.5408e+00, - 564.0: 1.5106e+00, - 565.0: 1.5201e+00, - 566.0: 1.4374e+00, - 567.0: 1.5320e+00, - 568.0: 1.5180e+00, - 569.0: 1.4807e+00, - 570.0: 1.4816e+00, - 571.0: 1.4331e+00, - 572.0: 1.5134e+00, - 573.0: 1.5198e+00, - 574.0: 1.5119e+00, - 575.0: 1.4777e+00, - 576.0: 1.4654e+00, - 577.0: 1.5023e+00, - 578.0: 1.4560e+00, - 579.0: 1.4770e+00, - 580.0: 1.5020e+00, - 581.0: 1.5089e+00, - 582.0: 1.5320e+00, - 583.0: 1.5479e+00, - 584.0: 1.5448e+00, - 585.0: 1.5324e+00, - 586.0: 1.4953e+00, - 587.0: 1.5281e+00, - 588.0: 1.4934e+00, - 589.0: 1.2894e+00, - 590.0: 1.3709e+00, - 591.0: 1.4662e+00, - 592.0: 1.4354e+00, - 593.0: 1.4561e+00, - 594.0: 1.4491e+00, - 595.0: 1.4308e+00, - 596.0: 1.4745e+00, - 597.0: 1.4788e+00, - 598.0: 1.4607e+00, - 599.0: 1.4606e+00, - 600.0: 1.4753e+00, - 601.0: 1.4579e+00, - 602.0: 1.4360e+00, - 603.0: 1.4664e+00, - 604.0: 1.4921e+00, - 605.0: 1.4895e+00, - 606.0: 1.4822e+00, - 607.0: 1.4911e+00, - 608.0: 1.4862e+00, - 609.0: 1.4749e+00, - 610.0: 1.4686e+00, - 611.0: 1.4611e+00, - 612.0: 1.4831e+00, - 613.0: 1.4621e+00, - 614.0: 1.4176e+00, - 615.0: 1.4697e+00, - 616.0: 1.4310e+00, - 617.0: 1.4128e+00, - 618.0: 1.4664e+00, - 619.0: 1.4733e+00, - 620.0: 1.4739e+00, - 621.0: 1.4802e+00, - 622.0: 1.4269e+00, - 623.0: 1.4165e+00, - 624.0: 1.4118e+00, - 625.0: 1.4026e+00, - 626.0: 1.4012e+00, - 627.0: 1.4417e+00, - 628.0: 1.3631e+00, - 629.0: 1.4114e+00, - 630.0: 1.3924e+00, - 631.0: 1.4161e+00, - 632.0: 1.3638e+00, - 633.0: 1.4508e+00, - 634.0: 1.4284e+00, - 635.0: 1.4458e+00, - 636.0: 1.4128e+00, - 637.0: 1.4610e+00, - 638.0: 1.4707e+00, - 639.0: 1.4646e+00, - 640.0: 1.4340e+00, - 641.0: 1.4348e+00, - 642.0: 1.4376e+00, - 643.0: 1.4525e+00, - 644.0: 1.4462e+00, - 645.0: 1.4567e+00, - 646.0: 1.4150e+00, - 647.0: 1.4086e+00, - 648.0: 1.3952e+00, - 649.0: 1.3519e+00, - 650.0: 1.3594e+00, - 651.0: 1.4447e+00, - 652.0: 1.3871e+00, - 653.0: 1.4311e+00, - 654.0: 1.4153e+00, - 655.0: 1.3499e+00, - 656.0: 1.1851e+00, - 657.0: 1.2393e+00, - 658.0: 1.3855e+00, - 659.0: 1.3905e+00, - 660.0: 1.3992e+00, - 661.0: 1.3933e+00, - 662.0: 1.3819e+00, - 663.0: 1.3844e+00, - 664.0: 1.3967e+00, - 665.0: 1.4214e+00, - 666.0: 1.4203e+00, - 667.0: 1.4102e+00, - 668.0: 1.4150e+00, - 669.0: 1.4394e+00, - 670.0: 1.4196e+00, - 671.0: 1.4169e+00, - 672.0: 1.3972e+00, - 673.0: 1.4094e+00, - 674.0: 1.4074e+00, - 675.0: 1.3958e+00, - 676.0: 1.4120e+00, - 677.0: 1.3991e+00, - 678.0: 1.4066e+00, - 679.0: 1.3947e+00, - 680.0: 1.3969e+00, - 681.0: 1.3915e+00, - 682.0: 1.3981e+00, - 683.0: 1.3830e+00, - 684.0: 1.3739e+00, - 685.0: 1.3748e+00, - 686.0: 1.3438e+00, + 432.0: 1.3207e00, + 433.0: 1.2288e00, + 434.0: 1.1352e00, + 435.0: 1.2452e00, + 436.0: 1.3659e00, + 437.0: 1.3943e00, + 438.0: 1.2238e00, + 439.0: 1.1775e00, + 440.0: 1.3499e00, + 441.0: 1.3313e00, + 442.0: 1.4250e00, + 443.0: 1.4453e00, + 444.0: 1.4084e00, + 445.0: 1.4619e00, + 446.0: 1.3108e00, + 447.0: 1.4903e00, + 448.0: 1.5081e00, + 449.0: 1.5045e00, + 450.0: 1.5595e00, + 451.0: 1.6173e00, + 452.0: 1.5482e00, + 453.0: 1.4297e00, + 454.0: 1.5335e00, + 455.0: 1.5224e00, + 456.0: 1.5724e00, + 457.0: 1.5854e00, + 458.0: 1.5514e00, + 459.0: 1.5391e00, + 460.0: 1.5291e00, + 461.0: 1.5827e00, + 462.0: 1.5975e00, + 463.0: 1.6031e00, + 464.0: 1.5544e00, + 465.0: 1.5350e00, + 466.0: 1.5673e00, + 467.0: 1.4973e00, + 468.0: 1.5619e00, + 469.0: 1.5682e00, + 470.0: 1.5077e00, + 471.0: 1.5331e00, + 472.0: 1.6126e00, + 473.0: 1.5499e00, + 474.0: 1.5671e00, + 475.0: 1.6185e00, + 476.0: 1.5631e00, + 477.0: 1.5724e00, + 478.0: 1.6230e00, + 479.0: 1.5916e00, + 480.0: 1.6181e00, + 481.0: 1.6177e00, + 482.0: 1.6236e00, + 483.0: 1.6038e00, + 484.0: 1.5734e00, + 485.0: 1.5683e00, + 486.0: 1.2716e00, + 487.0: 1.4241e00, + 488.0: 1.5413e00, + 489.0: 1.4519e00, + 490.0: 1.6224e00, + 491.0: 1.5595e00, + 492.0: 1.4869e00, + 493.0: 1.5903e00, + 494.0: 1.5525e00, + 495.0: 1.6485e00, + 496.0: 1.5676e00, + 497.0: 1.5944e00, + 498.0: 1.5509e00, + 499.0: 1.5507e00, + 500.0: 1.5451e00, + 501.0: 1.4978e00, + 502.0: 1.4966e00, + 503.0: 1.5653e00, + 504.0: 1.4587e00, + 505.0: 1.5635e00, + 506.0: 1.6264e00, + 507.0: 1.5560e00, + 508.0: 1.5165e00, + 509.0: 1.5893e00, + 510.0: 1.5481e00, + 511.0: 1.5769e00, + 512.0: 1.6186e00, + 513.0: 1.5206e00, + 514.0: 1.4885e00, + 515.0: 1.5314e00, + 516.0: 1.5455e00, + 517.0: 1.2594e00, + 518.0: 1.4403e00, + 519.0: 1.3957e00, + 520.0: 1.5236e00, + 521.0: 1.5346e00, + 522.0: 1.5690e00, + 523.0: 1.4789e00, + 524.0: 1.5905e00, + 525.0: 1.5781e00, + 526.0: 1.5341e00, + 527.0: 1.3417e00, + 528.0: 1.5357e00, + 529.0: 1.6071e00, + 530.0: 1.5446e00, + 531.0: 1.6292e00, + 532.0: 1.5998e00, + 533.0: 1.4286e00, + 534.0: 1.5302e00, + 535.0: 1.5535e00, + 536.0: 1.6199e00, + 537.0: 1.4989e00, + 538.0: 1.5738e00, + 539.0: 1.5352e00, + 540.0: 1.4825e00, + 541.0: 1.4251e00, + 542.0: 1.5511e00, + 543.0: 1.5256e00, + 544.0: 1.5792e00, + 545.0: 1.5435e00, + 546.0: 1.5291e00, + 547.0: 1.5490e00, + 548.0: 1.5049e00, + 549.0: 1.5520e00, + 550.0: 1.5399e00, + 551.0: 1.5382e00, + 552.0: 1.5697e00, + 553.0: 1.5250e00, + 554.0: 1.5549e00, + 555.0: 1.5634e00, + 556.0: 1.5366e00, + 557.0: 1.4988e00, + 558.0: 1.5310e00, + 559.0: 1.4483e00, + 560.0: 1.4740e00, + 561.0: 1.5595e00, + 562.0: 1.4847e00, + 563.0: 1.5408e00, + 564.0: 1.5106e00, + 565.0: 1.5201e00, + 566.0: 1.4374e00, + 567.0: 1.5320e00, + 568.0: 1.5180e00, + 569.0: 1.4807e00, + 570.0: 1.4816e00, + 571.0: 1.4331e00, + 572.0: 1.5134e00, + 573.0: 1.5198e00, + 574.0: 1.5119e00, + 575.0: 1.4777e00, + 576.0: 1.4654e00, + 577.0: 1.5023e00, + 578.0: 1.4560e00, + 579.0: 1.4770e00, + 580.0: 1.5020e00, + 581.0: 1.5089e00, + 582.0: 1.5320e00, + 583.0: 1.5479e00, + 584.0: 1.5448e00, + 585.0: 1.5324e00, + 586.0: 1.4953e00, + 587.0: 1.5281e00, + 588.0: 1.4934e00, + 589.0: 1.2894e00, + 590.0: 1.3709e00, + 591.0: 1.4662e00, + 592.0: 1.4354e00, + 593.0: 1.4561e00, + 594.0: 1.4491e00, + 595.0: 1.4308e00, + 596.0: 1.4745e00, + 597.0: 1.4788e00, + 598.0: 1.4607e00, + 599.0: 1.4606e00, + 600.0: 1.4753e00, + 601.0: 1.4579e00, + 602.0: 1.4360e00, + 603.0: 1.4664e00, + 604.0: 1.4921e00, + 605.0: 1.4895e00, + 606.0: 1.4822e00, + 607.0: 1.4911e00, + 608.0: 1.4862e00, + 609.0: 1.4749e00, + 610.0: 1.4686e00, + 611.0: 1.4611e00, + 612.0: 1.4831e00, + 613.0: 1.4621e00, + 614.0: 1.4176e00, + 615.0: 1.4697e00, + 616.0: 1.4310e00, + 617.0: 1.4128e00, + 618.0: 1.4664e00, + 619.0: 1.4733e00, + 620.0: 1.4739e00, + 621.0: 1.4802e00, + 622.0: 1.4269e00, + 623.0: 1.4165e00, + 624.0: 1.4118e00, + 625.0: 1.4026e00, + 626.0: 1.4012e00, + 627.0: 1.4417e00, + 628.0: 1.3631e00, + 629.0: 1.4114e00, + 630.0: 1.3924e00, + 631.0: 1.4161e00, + 632.0: 1.3638e00, + 633.0: 1.4508e00, + 634.0: 1.4284e00, + 635.0: 1.4458e00, + 636.0: 1.4128e00, + 637.0: 1.4610e00, + 638.0: 1.4707e00, + 639.0: 1.4646e00, + 640.0: 1.4340e00, + 641.0: 1.4348e00, + 642.0: 1.4376e00, + 643.0: 1.4525e00, + 644.0: 1.4462e00, + 645.0: 1.4567e00, + 646.0: 1.4150e00, + 647.0: 1.4086e00, + 648.0: 1.3952e00, + 649.0: 1.3519e00, + 650.0: 1.3594e00, + 651.0: 1.4447e00, + 652.0: 1.3871e00, + 653.0: 1.4311e00, + 654.0: 1.4153e00, + 655.0: 1.3499e00, + 656.0: 1.1851e00, + 657.0: 1.2393e00, + 658.0: 1.3855e00, + 659.0: 1.3905e00, + 660.0: 1.3992e00, + 661.0: 1.3933e00, + 662.0: 1.3819e00, + 663.0: 1.3844e00, + 664.0: 1.3967e00, + 665.0: 1.4214e00, + 666.0: 1.4203e00, + 667.0: 1.4102e00, + 668.0: 1.4150e00, + 669.0: 1.4394e00, + 670.0: 1.4196e00, + 671.0: 1.4169e00, + 672.0: 1.3972e00, + 673.0: 1.4094e00, + 674.0: 1.4074e00, + 675.0: 1.3958e00, + 676.0: 1.4120e00, + 677.0: 1.3991e00, + 678.0: 1.4066e00, + 679.0: 1.3947e00, + 680.0: 1.3969e00, + 681.0: 1.3915e00, + 682.0: 1.3981e00, + 683.0: 1.3830e00, + 684.0: 1.3739e00, + 685.0: 1.3748e00, + 686.0: 1.3438e00, 687.0: 9.6824e-01, - 688.0: 1.1206e+00, - 689.0: 1.1278e+00, - 690.0: 1.1821e+00, - 691.0: 1.2333e+00, - 692.0: 1.2689e+00, - 693.0: 1.2609e+00, - 694.0: 1.2464e+00, - 695.0: 1.2714e+00, - 696.0: 1.2684e+00, - 697.0: 1.3403e+00, - 698.0: 1.3192e+00, - 699.0: 1.2918e+00, - 700.0: 1.2823e+00, - 701.0: 1.2659e+00, - 702.0: 1.2674e+00, - 703.0: 1.2747e+00, - 704.0: 1.3078e+00, - 705.0: 1.3214e+00, - 706.0: 1.3144e+00, - 707.0: 1.3090e+00, - 708.0: 1.3048e+00, - 709.0: 1.3095e+00, - 710.0: 1.3175e+00, - 711.0: 1.3155e+00, - 712.0: 1.3071e+00, - 713.0: 1.2918e+00, - 714.0: 1.3029e+00, - 715.0: 1.2587e+00, - 716.0: 1.2716e+00, - 717.0: 1.1071e+00, - 718.0: 1.0296e+00, + 688.0: 1.1206e00, + 689.0: 1.1278e00, + 690.0: 1.1821e00, + 691.0: 1.2333e00, + 692.0: 1.2689e00, + 693.0: 1.2609e00, + 694.0: 1.2464e00, + 695.0: 1.2714e00, + 696.0: 1.2684e00, + 697.0: 1.3403e00, + 698.0: 1.3192e00, + 699.0: 1.2918e00, + 700.0: 1.2823e00, + 701.0: 1.2659e00, + 702.0: 1.2674e00, + 703.0: 1.2747e00, + 704.0: 1.3078e00, + 705.0: 1.3214e00, + 706.0: 1.3144e00, + 707.0: 1.3090e00, + 708.0: 1.3048e00, + 709.0: 1.3095e00, + 710.0: 1.3175e00, + 711.0: 1.3155e00, + 712.0: 1.3071e00, + 713.0: 1.2918e00, + 714.0: 1.3029e00, + 715.0: 1.2587e00, + 716.0: 1.2716e00, + 717.0: 1.1071e00, + 718.0: 1.0296e00, 719.0: 9.2318e-01, 720.0: 9.8550e-01, - 721.0: 1.0861e+00, - 722.0: 1.2407e+00, - 723.0: 1.1444e+00, - 724.0: 1.0555e+00, - 725.0: 1.0380e+00, - 726.0: 1.0813e+00, - 727.0: 1.0850e+00, - 728.0: 1.0400e+00, - 729.0: 1.0466e+00, - 730.0: 1.1285e+00, - 731.0: 1.0703e+00, - 732.0: 1.1534e+00, - 733.0: 1.1962e+00, - 734.0: 1.2357e+00, - 735.0: 1.2178e+00, - 736.0: 1.2059e+00, - 737.0: 1.2039e+00, - 738.0: 1.2269e+00, - 739.0: 1.1905e+00, - 740.0: 1.2195e+00, - 741.0: 1.2148e+00, - 742.0: 1.2153e+00, - 743.0: 1.2405e+00, - 744.0: 1.2503e+00, - 745.0: 1.2497e+00, - 746.0: 1.2470e+00, - 747.0: 1.2477e+00, - 748.0: 1.2401e+00, - 749.0: 1.2357e+00, - 750.0: 1.2341e+00, - 751.0: 1.2286e+00, - 752.0: 1.2330e+00, - 753.0: 1.2266e+00, - 754.0: 1.2420e+00, - 755.0: 1.2383e+00, - 756.0: 1.2232e+00, - 757.0: 1.2221e+00, - 758.0: 1.2295e+00, - 759.0: 1.1945e+00, + 721.0: 1.0861e00, + 722.0: 1.2407e00, + 723.0: 1.1444e00, + 724.0: 1.0555e00, + 725.0: 1.0380e00, + 726.0: 1.0813e00, + 727.0: 1.0850e00, + 728.0: 1.0400e00, + 729.0: 1.0466e00, + 730.0: 1.1285e00, + 731.0: 1.0703e00, + 732.0: 1.1534e00, + 733.0: 1.1962e00, + 734.0: 1.2357e00, + 735.0: 1.2178e00, + 736.0: 1.2059e00, + 737.0: 1.2039e00, + 738.0: 1.2269e00, + 739.0: 1.1905e00, + 740.0: 1.2195e00, + 741.0: 1.2148e00, + 742.0: 1.2153e00, + 743.0: 1.2405e00, + 744.0: 1.2503e00, + 745.0: 1.2497e00, + 746.0: 1.2470e00, + 747.0: 1.2477e00, + 748.0: 1.2401e00, + 749.0: 1.2357e00, + 750.0: 1.2341e00, + 751.0: 1.2286e00, + 752.0: 1.2330e00, + 753.0: 1.2266e00, + 754.0: 1.2420e00, + 755.0: 1.2383e00, + 756.0: 1.2232e00, + 757.0: 1.2221e00, + 758.0: 1.2295e00, + 759.0: 1.1945e00, 760.0: 2.6604e-01, 761.0: 1.5396e-01, 762.0: 6.8766e-01, @@ -2647,52 +2650,52 @@ 765.0: 6.8601e-01, 766.0: 8.1461e-01, 767.0: 9.7417e-01, - 768.0: 1.1138e+00, - 769.0: 1.1278e+00, - 770.0: 1.1608e+00, - 771.0: 1.1686e+00, - 772.0: 1.1778e+00, - 773.0: 1.1771e+00, - 774.0: 1.1771e+00, - 775.0: 1.1771e+00, - 776.0: 1.1798e+00, - 777.0: 1.1727e+00, - 778.0: 1.1713e+00, - 779.0: 1.1765e+00, - 780.0: 1.1636e+00, - 781.0: 1.1607e+00, - 782.0: 1.1662e+00, - 783.0: 1.1614e+00, - 784.0: 1.1536e+00, - 785.0: 1.1586e+00, - 786.0: 1.1592e+00, - 787.0: 1.1450e+00, - 788.0: 1.1305e+00, - 789.0: 1.1257e+00, - 790.0: 1.0910e+00, - 791.0: 1.1058e+00, - 792.0: 1.0953e+00, - 793.0: 1.0875e+00, - 794.0: 1.0972e+00, - 795.0: 1.0932e+00, - 796.0: 1.0742e+00, - 797.0: 1.0913e+00, - 798.0: 1.1121e+00, - 799.0: 1.0905e+00, - 800.0: 1.0725e+00, - 801.0: 1.0843e+00, - 802.0: 1.0856e+00, - 803.0: 1.0657e+00, - 804.0: 1.0782e+00, - 805.0: 1.0545e+00, - 806.0: 1.0974e+00, - 807.0: 1.0859e+00, - 808.0: 1.0821e+00, - 809.0: 1.0548e+00, - 810.0: 1.0559e+00, - 811.0: 1.0533e+00, - 812.0: 1.0268e+00, - 813.0: 1.0086e+00, + 768.0: 1.1138e00, + 769.0: 1.1278e00, + 770.0: 1.1608e00, + 771.0: 1.1686e00, + 772.0: 1.1778e00, + 773.0: 1.1771e00, + 774.0: 1.1771e00, + 775.0: 1.1771e00, + 776.0: 1.1798e00, + 777.0: 1.1727e00, + 778.0: 1.1713e00, + 779.0: 1.1765e00, + 780.0: 1.1636e00, + 781.0: 1.1607e00, + 782.0: 1.1662e00, + 783.0: 1.1614e00, + 784.0: 1.1536e00, + 785.0: 1.1586e00, + 786.0: 1.1592e00, + 787.0: 1.1450e00, + 788.0: 1.1305e00, + 789.0: 1.1257e00, + 790.0: 1.0910e00, + 791.0: 1.1058e00, + 792.0: 1.0953e00, + 793.0: 1.0875e00, + 794.0: 1.0972e00, + 795.0: 1.0932e00, + 796.0: 1.0742e00, + 797.0: 1.0913e00, + 798.0: 1.1121e00, + 799.0: 1.0905e00, + 800.0: 1.0725e00, + 801.0: 1.0843e00, + 802.0: 1.0856e00, + 803.0: 1.0657e00, + 804.0: 1.0782e00, + 805.0: 1.0545e00, + 806.0: 1.0974e00, + 807.0: 1.0859e00, + 808.0: 1.0821e00, + 809.0: 1.0548e00, + 810.0: 1.0559e00, + 811.0: 1.0533e00, + 812.0: 1.0268e00, + 813.0: 1.0086e00, 814.0: 9.0356e-01, 815.0: 8.9523e-01, 816.0: 8.3216e-01, @@ -2714,18 +2717,18 @@ 832.0: 8.9426e-01, 833.0: 9.5650e-01, 834.0: 9.3412e-01, - 835.0: 1.0032e+00, + 835.0: 1.0032e00, 836.0: 9.7234e-01, - 837.0: 1.0092e+00, + 837.0: 1.0092e00, 838.0: 9.9901e-01, - 839.0: 1.0013e+00, - 840.0: 1.0157e+00, - 841.0: 1.0101e+00, + 839.0: 1.0013e00, + 840.0: 1.0157e00, + 841.0: 1.0101e00, 842.0: 9.9703e-01, - 843.0: 1.0053e+00, + 843.0: 1.0053e00, 844.0: 9.8631e-01, - 845.0: 1.0165e+00, - 846.0: 1.0187e+00, + 845.0: 1.0165e00, + 846.0: 1.0187e00, 847.0: 9.9170e-01, 848.0: 9.9217e-01, 849.0: 9.8596e-01, @@ -2742,7 +2745,7 @@ 860.0: 9.8816e-01, 861.0: 9.8679e-01, 862.0: 9.9449e-01, - 863.0: 1.0005e+00, + 863.0: 1.0005e00, 864.0: 9.7916e-01, 865.0: 9.6324e-01, 866.0: 8.4900e-01, @@ -3774,13 +3777,13 @@ 2655.0: 1.3133e-27, 2660.0: 2.6068e-25, 2665.0: 1.1123e-37, - 2670.0: 0.0000e+00, - 2675.0: 0.0000e+00, - 2680.0: 0.0000e+00, - 2685.0: 0.0000e+00, + 2670.0: 0.0000e00, + 2675.0: 0.0000e00, + 2680.0: 0.0000e00, + 2685.0: 0.0000e00, 2690.0: 1.0226e-29, 2695.0: 7.1284e-33, - 2700.0: 0.0000e+00, + 2700.0: 0.0000e00, 2705.0: 2.9315e-42, 2710.0: 1.1250e-35, 2715.0: 3.8557e-26, @@ -3792,7 +3795,7 @@ 2745.0: 1.3146e-23, 2750.0: 1.6648e-28, 2755.0: 6.7262e-44, - 2760.0: 0.0000e+00, + 2760.0: 0.0000e00, 2765.0: 2.6777e-27, 2770.0: 8.3791e-24, 2775.0: 3.9990e-38, @@ -4043,7 +4046,7 @@ 4000.0: 7.1043e-03, } -DATA_ASTMG173_DIRECT_CIRCUMSOLAR = { +DATA_ASTMG173_DIRECT_CIRCUMSOLAR: Dict = { 280.0: 2.5361e-26, 280.5: 1.0917e-24, 281.0: 6.1253e-24, @@ -4305,7 +4308,7 @@ 418.0: 9.2392e-01, 419.0: 9.6354e-01, 420.0: 8.8467e-01, - 421.0: 1.0067e+00, + 421.0: 1.0067e00, 422.0: 9.9499e-01, 423.0: 9.6531e-01, 424.0: 9.6182e-01, @@ -4316,334 +4319,334 @@ 429.0: 8.7766e-01, 430.0: 7.0134e-01, 431.0: 6.3779e-01, - 432.0: 1.0628e+00, + 432.0: 1.0628e00, 433.0: 9.9050e-01, 434.0: 9.1653e-01, - 435.0: 1.0070e+00, - 436.0: 1.1061e+00, - 437.0: 1.1306e+00, + 435.0: 1.0070e00, + 436.0: 1.1061e00, + 437.0: 1.1306e00, 438.0: 9.9368e-01, 439.0: 9.5753e-01, - 440.0: 1.0993e+00, - 441.0: 1.0859e+00, - 442.0: 1.1640e+00, - 443.0: 1.1823e+00, - 444.0: 1.1537e+00, - 445.0: 1.1992e+00, - 446.0: 1.0766e+00, - 447.0: 1.2257e+00, - 448.0: 1.2422e+00, - 449.0: 1.2409e+00, - 450.0: 1.2881e+00, - 451.0: 1.3376e+00, - 452.0: 1.2822e+00, - 453.0: 1.1854e+00, - 454.0: 1.2730e+00, - 455.0: 1.2655e+00, - 456.0: 1.3088e+00, - 457.0: 1.3213e+00, - 458.0: 1.2946e+00, - 459.0: 1.2859e+00, - 460.0: 1.2791e+00, - 461.0: 1.3255e+00, - 462.0: 1.3392e+00, - 463.0: 1.3452e+00, - 464.0: 1.3055e+00, - 465.0: 1.2905e+00, - 466.0: 1.3190e+00, - 467.0: 1.2616e+00, - 468.0: 1.3178e+00, - 469.0: 1.3247e+00, - 470.0: 1.2749e+00, - 471.0: 1.2975e+00, - 472.0: 1.3661e+00, - 473.0: 1.3144e+00, - 474.0: 1.3304e+00, - 475.0: 1.3755e+00, - 476.0: 1.3299e+00, - 477.0: 1.3392e+00, - 478.0: 1.3839e+00, - 479.0: 1.3586e+00, - 480.0: 1.3825e+00, - 481.0: 1.3836e+00, - 482.0: 1.3899e+00, - 483.0: 1.3742e+00, - 484.0: 1.3492e+00, - 485.0: 1.3457e+00, - 486.0: 1.0918e+00, - 487.0: 1.2235e+00, - 488.0: 1.3252e+00, - 489.0: 1.2492e+00, - 490.0: 1.3968e+00, - 491.0: 1.3435e+00, - 492.0: 1.2818e+00, - 493.0: 1.3719e+00, - 494.0: 1.3402e+00, - 495.0: 1.4238e+00, - 496.0: 1.3548e+00, - 497.0: 1.3788e+00, - 498.0: 1.3421e+00, - 499.0: 1.3429e+00, - 500.0: 1.3391e+00, - 501.0: 1.2990e+00, - 502.0: 1.2991e+00, - 503.0: 1.3597e+00, - 504.0: 1.2682e+00, - 505.0: 1.3598e+00, - 506.0: 1.4153e+00, - 507.0: 1.3548e+00, - 508.0: 1.3210e+00, - 509.0: 1.3850e+00, - 510.0: 1.3497e+00, - 511.0: 1.3753e+00, - 512.0: 1.4125e+00, - 513.0: 1.3277e+00, - 514.0: 1.3003e+00, - 515.0: 1.3385e+00, - 516.0: 1.3514e+00, - 517.0: 1.1017e+00, - 518.0: 1.2605e+00, - 519.0: 1.2222e+00, - 520.0: 1.3349e+00, - 521.0: 1.3452e+00, - 522.0: 1.3760e+00, - 523.0: 1.2976e+00, - 524.0: 1.3962e+00, - 525.0: 1.3859e+00, - 526.0: 1.3479e+00, - 527.0: 1.1795e+00, - 528.0: 1.3508e+00, - 529.0: 1.4142e+00, - 530.0: 1.3598e+00, - 531.0: 1.4348e+00, - 532.0: 1.4094e+00, - 533.0: 1.2590e+00, - 534.0: 1.3491e+00, - 535.0: 1.3701e+00, - 536.0: 1.4292e+00, - 537.0: 1.3229e+00, - 538.0: 1.3896e+00, - 539.0: 1.3558e+00, - 540.0: 1.3096e+00, - 541.0: 1.2595e+00, - 542.0: 1.3714e+00, - 543.0: 1.3493e+00, - 544.0: 1.3971e+00, - 545.0: 1.3657e+00, - 546.0: 1.3536e+00, - 547.0: 1.3717e+00, - 548.0: 1.3331e+00, - 549.0: 1.3752e+00, - 550.0: 1.3648e+00, - 551.0: 1.3639e+00, - 552.0: 1.3923e+00, - 553.0: 1.3533e+00, - 554.0: 1.3802e+00, - 555.0: 1.3883e+00, - 556.0: 1.3651e+00, - 557.0: 1.3321e+00, - 558.0: 1.3613e+00, - 559.0: 1.2885e+00, - 560.0: 1.3118e+00, - 561.0: 1.3885e+00, - 562.0: 1.3225e+00, - 563.0: 1.3731e+00, - 564.0: 1.3466e+00, - 565.0: 1.3555e+00, - 566.0: 1.2823e+00, - 567.0: 1.3673e+00, - 568.0: 1.3554e+00, - 569.0: 1.3228e+00, - 570.0: 1.3240e+00, - 571.0: 1.2810e+00, - 572.0: 1.3534e+00, - 573.0: 1.3595e+00, - 574.0: 1.3527e+00, - 575.0: 1.3225e+00, - 576.0: 1.3118e+00, - 577.0: 1.3452e+00, - 578.0: 1.3040e+00, - 579.0: 1.3230e+00, - 580.0: 1.3455e+00, - 581.0: 1.3518e+00, - 582.0: 1.3729e+00, - 583.0: 1.3872e+00, - 584.0: 1.3845e+00, - 585.0: 1.3737e+00, - 586.0: 1.3409e+00, - 587.0: 1.3708e+00, - 588.0: 1.3403e+00, - 589.0: 1.1582e+00, - 590.0: 1.2316e+00, - 591.0: 1.3171e+00, - 592.0: 1.2900e+00, - 593.0: 1.3086e+00, - 594.0: 1.3029e+00, - 595.0: 1.2870e+00, - 596.0: 1.3260e+00, - 597.0: 1.3303e+00, - 598.0: 1.3142e+00, - 599.0: 1.3145e+00, - 600.0: 1.3278e+00, - 601.0: 1.3123e+00, - 602.0: 1.2928e+00, - 603.0: 1.3205e+00, - 604.0: 1.3439e+00, - 605.0: 1.3418e+00, - 606.0: 1.3353e+00, - 607.0: 1.3434e+00, - 608.0: 1.3392e+00, - 609.0: 1.3292e+00, - 610.0: 1.3237e+00, - 611.0: 1.3170e+00, - 612.0: 1.3370e+00, - 613.0: 1.3182e+00, - 614.0: 1.2783e+00, - 615.0: 1.3254e+00, - 616.0: 1.2906e+00, - 617.0: 1.2744e+00, - 618.0: 1.3228e+00, - 619.0: 1.3292e+00, - 620.0: 1.3299e+00, - 621.0: 1.3359e+00, - 622.0: 1.2882e+00, - 623.0: 1.2793e+00, - 624.0: 1.2751e+00, - 625.0: 1.2667e+00, - 626.0: 1.2655e+00, - 627.0: 1.3022e+00, - 628.0: 1.2328e+00, - 629.0: 1.2758e+00, - 630.0: 1.2589e+00, - 631.0: 1.2799e+00, - 632.0: 1.2327e+00, - 633.0: 1.3110e+00, - 634.0: 1.2907e+00, - 635.0: 1.3065e+00, - 636.0: 1.2768e+00, - 637.0: 1.3204e+00, - 638.0: 1.3292e+00, - 639.0: 1.3238e+00, - 640.0: 1.2962e+00, - 641.0: 1.2970e+00, - 642.0: 1.2995e+00, - 643.0: 1.3130e+00, - 644.0: 1.3074e+00, - 645.0: 1.3170e+00, - 646.0: 1.2797e+00, - 647.0: 1.2744e+00, - 648.0: 1.2625e+00, - 649.0: 1.2234e+00, - 650.0: 1.2299e+00, - 651.0: 1.3071e+00, - 652.0: 1.2558e+00, - 653.0: 1.2950e+00, - 654.0: 1.2807e+00, - 655.0: 1.2220e+00, - 656.0: 1.0727e+00, - 657.0: 1.1218e+00, - 658.0: 1.2540e+00, - 659.0: 1.2586e+00, - 660.0: 1.2668e+00, - 661.0: 1.2618e+00, - 662.0: 1.2518e+00, - 663.0: 1.2539e+00, - 664.0: 1.2647e+00, - 665.0: 1.2871e+00, - 666.0: 1.2860e+00, - 667.0: 1.2767e+00, - 668.0: 1.2810e+00, - 669.0: 1.3032e+00, - 670.0: 1.2853e+00, - 671.0: 1.2829e+00, - 672.0: 1.2651e+00, - 673.0: 1.2760e+00, - 674.0: 1.2742e+00, - 675.0: 1.2639e+00, - 676.0: 1.2786e+00, - 677.0: 1.2669e+00, - 678.0: 1.2737e+00, - 679.0: 1.2629e+00, - 680.0: 1.2650e+00, - 681.0: 1.2601e+00, - 682.0: 1.2662e+00, - 683.0: 1.2526e+00, - 684.0: 1.2445e+00, - 685.0: 1.2454e+00, - 686.0: 1.2174e+00, + 440.0: 1.0993e00, + 441.0: 1.0859e00, + 442.0: 1.1640e00, + 443.0: 1.1823e00, + 444.0: 1.1537e00, + 445.0: 1.1992e00, + 446.0: 1.0766e00, + 447.0: 1.2257e00, + 448.0: 1.2422e00, + 449.0: 1.2409e00, + 450.0: 1.2881e00, + 451.0: 1.3376e00, + 452.0: 1.2822e00, + 453.0: 1.1854e00, + 454.0: 1.2730e00, + 455.0: 1.2655e00, + 456.0: 1.3088e00, + 457.0: 1.3213e00, + 458.0: 1.2946e00, + 459.0: 1.2859e00, + 460.0: 1.2791e00, + 461.0: 1.3255e00, + 462.0: 1.3392e00, + 463.0: 1.3452e00, + 464.0: 1.3055e00, + 465.0: 1.2905e00, + 466.0: 1.3190e00, + 467.0: 1.2616e00, + 468.0: 1.3178e00, + 469.0: 1.3247e00, + 470.0: 1.2749e00, + 471.0: 1.2975e00, + 472.0: 1.3661e00, + 473.0: 1.3144e00, + 474.0: 1.3304e00, + 475.0: 1.3755e00, + 476.0: 1.3299e00, + 477.0: 1.3392e00, + 478.0: 1.3839e00, + 479.0: 1.3586e00, + 480.0: 1.3825e00, + 481.0: 1.3836e00, + 482.0: 1.3899e00, + 483.0: 1.3742e00, + 484.0: 1.3492e00, + 485.0: 1.3457e00, + 486.0: 1.0918e00, + 487.0: 1.2235e00, + 488.0: 1.3252e00, + 489.0: 1.2492e00, + 490.0: 1.3968e00, + 491.0: 1.3435e00, + 492.0: 1.2818e00, + 493.0: 1.3719e00, + 494.0: 1.3402e00, + 495.0: 1.4238e00, + 496.0: 1.3548e00, + 497.0: 1.3788e00, + 498.0: 1.3421e00, + 499.0: 1.3429e00, + 500.0: 1.3391e00, + 501.0: 1.2990e00, + 502.0: 1.2991e00, + 503.0: 1.3597e00, + 504.0: 1.2682e00, + 505.0: 1.3598e00, + 506.0: 1.4153e00, + 507.0: 1.3548e00, + 508.0: 1.3210e00, + 509.0: 1.3850e00, + 510.0: 1.3497e00, + 511.0: 1.3753e00, + 512.0: 1.4125e00, + 513.0: 1.3277e00, + 514.0: 1.3003e00, + 515.0: 1.3385e00, + 516.0: 1.3514e00, + 517.0: 1.1017e00, + 518.0: 1.2605e00, + 519.0: 1.2222e00, + 520.0: 1.3349e00, + 521.0: 1.3452e00, + 522.0: 1.3760e00, + 523.0: 1.2976e00, + 524.0: 1.3962e00, + 525.0: 1.3859e00, + 526.0: 1.3479e00, + 527.0: 1.1795e00, + 528.0: 1.3508e00, + 529.0: 1.4142e00, + 530.0: 1.3598e00, + 531.0: 1.4348e00, + 532.0: 1.4094e00, + 533.0: 1.2590e00, + 534.0: 1.3491e00, + 535.0: 1.3701e00, + 536.0: 1.4292e00, + 537.0: 1.3229e00, + 538.0: 1.3896e00, + 539.0: 1.3558e00, + 540.0: 1.3096e00, + 541.0: 1.2595e00, + 542.0: 1.3714e00, + 543.0: 1.3493e00, + 544.0: 1.3971e00, + 545.0: 1.3657e00, + 546.0: 1.3536e00, + 547.0: 1.3717e00, + 548.0: 1.3331e00, + 549.0: 1.3752e00, + 550.0: 1.3648e00, + 551.0: 1.3639e00, + 552.0: 1.3923e00, + 553.0: 1.3533e00, + 554.0: 1.3802e00, + 555.0: 1.3883e00, + 556.0: 1.3651e00, + 557.0: 1.3321e00, + 558.0: 1.3613e00, + 559.0: 1.2885e00, + 560.0: 1.3118e00, + 561.0: 1.3885e00, + 562.0: 1.3225e00, + 563.0: 1.3731e00, + 564.0: 1.3466e00, + 565.0: 1.3555e00, + 566.0: 1.2823e00, + 567.0: 1.3673e00, + 568.0: 1.3554e00, + 569.0: 1.3228e00, + 570.0: 1.3240e00, + 571.0: 1.2810e00, + 572.0: 1.3534e00, + 573.0: 1.3595e00, + 574.0: 1.3527e00, + 575.0: 1.3225e00, + 576.0: 1.3118e00, + 577.0: 1.3452e00, + 578.0: 1.3040e00, + 579.0: 1.3230e00, + 580.0: 1.3455e00, + 581.0: 1.3518e00, + 582.0: 1.3729e00, + 583.0: 1.3872e00, + 584.0: 1.3845e00, + 585.0: 1.3737e00, + 586.0: 1.3409e00, + 587.0: 1.3708e00, + 588.0: 1.3403e00, + 589.0: 1.1582e00, + 590.0: 1.2316e00, + 591.0: 1.3171e00, + 592.0: 1.2900e00, + 593.0: 1.3086e00, + 594.0: 1.3029e00, + 595.0: 1.2870e00, + 596.0: 1.3260e00, + 597.0: 1.3303e00, + 598.0: 1.3142e00, + 599.0: 1.3145e00, + 600.0: 1.3278e00, + 601.0: 1.3123e00, + 602.0: 1.2928e00, + 603.0: 1.3205e00, + 604.0: 1.3439e00, + 605.0: 1.3418e00, + 606.0: 1.3353e00, + 607.0: 1.3434e00, + 608.0: 1.3392e00, + 609.0: 1.3292e00, + 610.0: 1.3237e00, + 611.0: 1.3170e00, + 612.0: 1.3370e00, + 613.0: 1.3182e00, + 614.0: 1.2783e00, + 615.0: 1.3254e00, + 616.0: 1.2906e00, + 617.0: 1.2744e00, + 618.0: 1.3228e00, + 619.0: 1.3292e00, + 620.0: 1.3299e00, + 621.0: 1.3359e00, + 622.0: 1.2882e00, + 623.0: 1.2793e00, + 624.0: 1.2751e00, + 625.0: 1.2667e00, + 626.0: 1.2655e00, + 627.0: 1.3022e00, + 628.0: 1.2328e00, + 629.0: 1.2758e00, + 630.0: 1.2589e00, + 631.0: 1.2799e00, + 632.0: 1.2327e00, + 633.0: 1.3110e00, + 634.0: 1.2907e00, + 635.0: 1.3065e00, + 636.0: 1.2768e00, + 637.0: 1.3204e00, + 638.0: 1.3292e00, + 639.0: 1.3238e00, + 640.0: 1.2962e00, + 641.0: 1.2970e00, + 642.0: 1.2995e00, + 643.0: 1.3130e00, + 644.0: 1.3074e00, + 645.0: 1.3170e00, + 646.0: 1.2797e00, + 647.0: 1.2744e00, + 648.0: 1.2625e00, + 649.0: 1.2234e00, + 650.0: 1.2299e00, + 651.0: 1.3071e00, + 652.0: 1.2558e00, + 653.0: 1.2950e00, + 654.0: 1.2807e00, + 655.0: 1.2220e00, + 656.0: 1.0727e00, + 657.0: 1.1218e00, + 658.0: 1.2540e00, + 659.0: 1.2586e00, + 660.0: 1.2668e00, + 661.0: 1.2618e00, + 662.0: 1.2518e00, + 663.0: 1.2539e00, + 664.0: 1.2647e00, + 665.0: 1.2871e00, + 666.0: 1.2860e00, + 667.0: 1.2767e00, + 668.0: 1.2810e00, + 669.0: 1.3032e00, + 670.0: 1.2853e00, + 671.0: 1.2829e00, + 672.0: 1.2651e00, + 673.0: 1.2760e00, + 674.0: 1.2742e00, + 675.0: 1.2639e00, + 676.0: 1.2786e00, + 677.0: 1.2669e00, + 678.0: 1.2737e00, + 679.0: 1.2629e00, + 680.0: 1.2650e00, + 681.0: 1.2601e00, + 682.0: 1.2662e00, + 683.0: 1.2526e00, + 684.0: 1.2445e00, + 685.0: 1.2454e00, + 686.0: 1.2174e00, 687.0: 8.8285e-01, - 688.0: 1.0195e+00, - 689.0: 1.0260e+00, - 690.0: 1.0746e+00, - 691.0: 1.1201e+00, - 692.0: 1.1516e+00, - 693.0: 1.1446e+00, - 694.0: 1.1318e+00, - 695.0: 1.1538e+00, - 696.0: 1.1513e+00, - 697.0: 1.2151e+00, - 698.0: 1.1961e+00, - 699.0: 1.1721e+00, - 700.0: 1.1636e+00, - 701.0: 1.1489e+00, - 702.0: 1.1500e+00, - 703.0: 1.1567e+00, - 704.0: 1.1864e+00, - 705.0: 1.1989e+00, - 706.0: 1.1925e+00, - 707.0: 1.1875e+00, - 708.0: 1.1839e+00, - 709.0: 1.1880e+00, - 710.0: 1.1954e+00, - 711.0: 1.1934e+00, - 712.0: 1.1856e+00, - 713.0: 1.1719e+00, - 714.0: 1.1823e+00, - 715.0: 1.1428e+00, - 716.0: 1.1548e+00, - 717.0: 1.0081e+00, + 688.0: 1.0195e00, + 689.0: 1.0260e00, + 690.0: 1.0746e00, + 691.0: 1.1201e00, + 692.0: 1.1516e00, + 693.0: 1.1446e00, + 694.0: 1.1318e00, + 695.0: 1.1538e00, + 696.0: 1.1513e00, + 697.0: 1.2151e00, + 698.0: 1.1961e00, + 699.0: 1.1721e00, + 700.0: 1.1636e00, + 701.0: 1.1489e00, + 702.0: 1.1500e00, + 703.0: 1.1567e00, + 704.0: 1.1864e00, + 705.0: 1.1989e00, + 706.0: 1.1925e00, + 707.0: 1.1875e00, + 708.0: 1.1839e00, + 709.0: 1.1880e00, + 710.0: 1.1954e00, + 711.0: 1.1934e00, + 712.0: 1.1856e00, + 713.0: 1.1719e00, + 714.0: 1.1823e00, + 715.0: 1.1428e00, + 716.0: 1.1548e00, + 717.0: 1.0081e00, 718.0: 9.3873e-01, 719.0: 8.4274e-01, 720.0: 8.9940e-01, 721.0: 9.8967e-01, - 722.0: 1.1281e+00, - 723.0: 1.0423e+00, + 722.0: 1.1281e00, + 723.0: 1.0423e00, 724.0: 9.6305e-01, 725.0: 9.4741e-01, 726.0: 9.8638e-01, 727.0: 9.8988e-01, 728.0: 9.4968e-01, 729.0: 9.5503e-01, - 730.0: 1.0294e+00, + 730.0: 1.0294e00, 731.0: 9.7702e-01, - 732.0: 1.0520e+00, - 733.0: 1.0901e+00, - 734.0: 1.1261e+00, - 735.0: 1.1101e+00, - 736.0: 1.0994e+00, - 737.0: 1.0978e+00, - 738.0: 1.1184e+00, - 739.0: 1.0855e+00, - 740.0: 1.1119e+00, - 741.0: 1.1078e+00, - 742.0: 1.1084e+00, - 743.0: 1.1316e+00, - 744.0: 1.1408e+00, - 745.0: 1.1404e+00, - 746.0: 1.1381e+00, - 747.0: 1.1389e+00, - 748.0: 1.1323e+00, - 749.0: 1.1286e+00, - 750.0: 1.1273e+00, - 751.0: 1.1224e+00, - 752.0: 1.1265e+00, - 753.0: 1.1210e+00, - 754.0: 1.1353e+00, - 755.0: 1.1321e+00, - 756.0: 1.1185e+00, - 757.0: 1.1176e+00, - 758.0: 1.1246e+00, - 759.0: 1.0932e+00, + 732.0: 1.0520e00, + 733.0: 1.0901e00, + 734.0: 1.1261e00, + 735.0: 1.1101e00, + 736.0: 1.0994e00, + 737.0: 1.0978e00, + 738.0: 1.1184e00, + 739.0: 1.0855e00, + 740.0: 1.1119e00, + 741.0: 1.1078e00, + 742.0: 1.1084e00, + 743.0: 1.1316e00, + 744.0: 1.1408e00, + 745.0: 1.1404e00, + 746.0: 1.1381e00, + 747.0: 1.1389e00, + 748.0: 1.1323e00, + 749.0: 1.1286e00, + 750.0: 1.1273e00, + 751.0: 1.1224e00, + 752.0: 1.1265e00, + 753.0: 1.1210e00, + 754.0: 1.1353e00, + 755.0: 1.1321e00, + 756.0: 1.1185e00, + 757.0: 1.1176e00, + 758.0: 1.1246e00, + 759.0: 1.0932e00, 760.0: 2.4716e-01, 761.0: 1.4328e-01, 762.0: 6.3491e-01, @@ -4652,46 +4655,46 @@ 765.0: 6.3377e-01, 766.0: 7.5080e-01, 767.0: 8.9574e-01, - 768.0: 1.0222e+00, - 769.0: 1.0347e+00, - 770.0: 1.0646e+00, - 771.0: 1.0716e+00, - 772.0: 1.0802e+00, - 773.0: 1.0797e+00, - 774.0: 1.0800e+00, - 775.0: 1.0801e+00, - 776.0: 1.0827e+00, - 777.0: 1.0764e+00, - 778.0: 1.0754e+00, - 779.0: 1.0803e+00, - 780.0: 1.0687e+00, - 781.0: 1.0662e+00, - 782.0: 1.0714e+00, - 783.0: 1.0672e+00, - 784.0: 1.0602e+00, - 785.0: 1.0649e+00, - 786.0: 1.0656e+00, - 787.0: 1.0530e+00, - 788.0: 1.0399e+00, - 789.0: 1.0359e+00, - 790.0: 1.0045e+00, - 791.0: 1.0179e+00, - 792.0: 1.0084e+00, - 793.0: 1.0015e+00, - 794.0: 1.0101e+00, - 795.0: 1.0066e+00, + 768.0: 1.0222e00, + 769.0: 1.0347e00, + 770.0: 1.0646e00, + 771.0: 1.0716e00, + 772.0: 1.0802e00, + 773.0: 1.0797e00, + 774.0: 1.0800e00, + 775.0: 1.0801e00, + 776.0: 1.0827e00, + 777.0: 1.0764e00, + 778.0: 1.0754e00, + 779.0: 1.0803e00, + 780.0: 1.0687e00, + 781.0: 1.0662e00, + 782.0: 1.0714e00, + 783.0: 1.0672e00, + 784.0: 1.0602e00, + 785.0: 1.0649e00, + 786.0: 1.0656e00, + 787.0: 1.0530e00, + 788.0: 1.0399e00, + 789.0: 1.0359e00, + 790.0: 1.0045e00, + 791.0: 1.0179e00, + 792.0: 1.0084e00, + 793.0: 1.0015e00, + 794.0: 1.0101e00, + 795.0: 1.0066e00, 796.0: 9.8985e-01, - 797.0: 1.0057e+00, - 798.0: 1.0245e+00, - 799.0: 1.0048e+00, + 797.0: 1.0057e00, + 798.0: 1.0245e00, + 799.0: 1.0048e00, 800.0: 9.8859e-01, 801.0: 9.9978e-01, - 802.0: 1.0011e+00, + 802.0: 1.0011e00, 803.0: 9.8288e-01, 804.0: 9.9452e-01, 805.0: 9.7270e-01, - 806.0: 1.0122e+00, - 807.0: 1.0018e+00, + 806.0: 1.0122e00, + 807.0: 1.0018e00, 808.0: 9.9844e-01, 809.0: 9.7353e-01, 810.0: 9.7488e-01, @@ -5779,13 +5782,13 @@ 2655.0: 1.3036e-27, 2660.0: 2.5880e-25, 2665.0: 1.1045e-37, - 2670.0: 0.0000e+00, - 2675.0: 0.0000e+00, - 2680.0: 0.0000e+00, - 2685.0: 0.0000e+00, + 2670.0: 0.0000e00, + 2675.0: 0.0000e00, + 2680.0: 0.0000e00, + 2685.0: 0.0000e00, 2690.0: 1.0178e-29, 2695.0: 7.1014e-33, - 2700.0: 0.0000e+00, + 2700.0: 0.0000e00, 2705.0: 2.9273e-42, 2710.0: 1.1239e-35, 2715.0: 3.8549e-26, @@ -5797,7 +5800,7 @@ 2745.0: 1.3224e-23, 2750.0: 1.6758e-28, 2755.0: 6.7262e-44, - 2760.0: 0.0000e+00, + 2760.0: 0.0000e00, 2765.0: 2.7001e-27, 2770.0: 8.4528e-24, 2775.0: 4.0360e-38, @@ -6049,8 +6052,9 @@ } # yapf: enable -SD_ASTMG173_ETR = SpectralDistribution( - DATA_ASTMG173_ETR, name='ASTM G-173 ETR', interpolator=LinearInterpolator) +SD_ASTMG173_ETR: SpectralDistribution = SpectralDistribution( + DATA_ASTMG173_ETR, name="ASTM G-173 ETR", interpolator=LinearInterpolator +) """ Extraterrestrial Radiation (solar spectrum at top of atmosphere) at mean Earth-Sun distance. @@ -6058,14 +6062,13 @@ References ---------- :cite:`RenewableResourceDataCenter2003a` - -SD_ASTMG173_ETR : SpectralDistribution """ -SD_ASTMG173_GLOBAL_TILT = SpectralDistribution( +SD_ASTMG173_GLOBAL_TILT: SpectralDistribution = SpectralDistribution( DATA_ASTMG173_GLOBAL_TILT, - name='ASTM G-173 Global Tilt', - interpolator=LinearInterpolator) + name="ASTM G-173 Global Tilt", + interpolator=LinearInterpolator, +) """ Spectral radiation from solar disk plus sky diffuse and diffuse reflected from ground on south facing surface tilted 37 degrees from horizontal. @@ -6073,14 +6076,13 @@ References ---------- :cite:`RenewableResourceDataCenter2003a` - -SD_ASTMG173_GLOBAL_TILT : SpectralDistribution """ -SD_ASTMG173_DIRECT_CIRCUMSOLAR = SpectralDistribution( +SD_ASTMG173_DIRECT_CIRCUMSOLAR: SpectralDistribution = SpectralDistribution( DATA_ASTMG173_DIRECT_CIRCUMSOLAR, - name='ASTM G-173 Direct + Circumsolar', - interpolator=LinearInterpolator) + name="ASTM G-173 Direct + Circumsolar", + interpolator=LinearInterpolator, +) """ Direct Normal Irradiance Nearly parallel (0.5 degrees divergent cone) radiation on surface with surface normal tracking (pointing to) the sun, @@ -6092,6 +6094,4 @@ References ---------- :cite:`RenewableResourceDataCenter2003a` - -SD_ASTMG173_DIRECT_CIRCUMSOLAR : SpectralDistribution """ diff --git a/colour/plotting/diagrams.py b/colour/plotting/diagrams.py index 31aa5324b3..54090136b1 100644 --- a/colour/plotting/diagrams.py +++ b/colour/plotting/diagrams.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ CIE Chromaticity Diagrams Plotting ================================== @@ -13,79 +12,140 @@ - :func:`colour.plotting.plot_sds_in_chromaticity_diagram_CIE1976UCS` """ -from __future__ import division +from __future__ import annotations import bisect +import matplotlib.pyplot as plt import numpy as np from matplotlib.collections import LineCollection from matplotlib.patches import Polygon -from colour.algebra import normalise_vector -from colour.colorimetry import SDS_ILLUMINANTS, sd_to_XYZ, sds_and_msds_to_sds -from colour.models import (Luv_to_uv, Luv_uv_to_xy, UCS_to_uv, UCS_uv_to_xy, - XYZ_to_Luv, XYZ_to_UCS, XYZ_to_xy, xy_to_XYZ) -from colour.plotting import (CONSTANTS_COLOUR_STYLE, CONSTANTS_ARROW_STYLE, - XYZ_to_plotting_colourspace, artist, filter_cmfs, - filter_illuminants, override_style, render, - update_settings_collection) -from colour.utilities import (domain_range_scale, first_item, is_string, - normalise_maximum, tstack, suppress_warnings) -from colour.utilities.deprecation import handle_arguments_deprecation - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.algebra import normalise_maximum, normalise_vector +from colour.colorimetry import ( + MultiSpectralDistributions, + SDS_ILLUMINANTS, + SpectralDistribution, + sd_to_XYZ, + sds_and_msds_to_sds, +) +from colour.hints import ( + Any, + ArrayLike, + Boolean, + Callable, + Dict, + Floating, + Integer, + List, + Literal, + NDArray, + Optional, + Sequence, + Tuple, + Union, + cast, +) +from colour.models import ( + Luv_to_uv, + Luv_uv_to_xy, + UCS_to_uv, + UCS_uv_to_xy, + XYZ_to_Luv, + XYZ_to_UCS, + XYZ_to_xy, + xy_to_XYZ, +) +from colour.notation import HEX_to_RGB +from colour.plotting import ( + CONSTANTS_COLOUR_STYLE, + CONSTANTS_ARROW_STYLE, + XYZ_to_plotting_colourspace, + artist, + filter_cmfs, + filter_illuminants, + override_style, + render, + update_settings_collection, +) +from colour.utilities import ( + as_float_array, + domain_range_scale, + first_item, + is_string, + optional, + tsplit, + tstack, + suppress_warnings, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'plot_spectral_locus', 'plot_chromaticity_diagram_colours', - 'plot_chromaticity_diagram', 'plot_chromaticity_diagram_CIE1931', - 'plot_chromaticity_diagram_CIE1960UCS', - 'plot_chromaticity_diagram_CIE1976UCS', 'plot_sds_in_chromaticity_diagram', - 'plot_sds_in_chromaticity_diagram_CIE1931', - 'plot_sds_in_chromaticity_diagram_CIE1960UCS', - 'plot_sds_in_chromaticity_diagram_CIE1976UCS' + "plot_spectral_locus", + "plot_chromaticity_diagram_colours", + "plot_chromaticity_diagram", + "plot_chromaticity_diagram_CIE1931", + "plot_chromaticity_diagram_CIE1960UCS", + "plot_chromaticity_diagram_CIE1976UCS", + "plot_sds_in_chromaticity_diagram", + "plot_sds_in_chromaticity_diagram_CIE1931", + "plot_sds_in_chromaticity_diagram_CIE1960UCS", + "plot_sds_in_chromaticity_diagram_CIE1976UCS", ] @override_style() -def plot_spectral_locus(cmfs='CIE 1931 2 Degree Standard Observer', - spectral_locus_colours=None, - spectral_locus_labels=None, - method='CIE 1931', - **kwargs): +def plot_spectral_locus( + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + spectral_locus_colours: Optional[Union[ArrayLike, str]] = None, + spectral_locus_opacity: Floating = 1, + spectral_locus_labels: Optional[Sequence] = None, + method: Union[ + Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"], str + ] = "CIE 1931", + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the *Spectral Locus* according to given method. + Plot the *Spectral Locus* according to given method. Parameters ---------- - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - spectral_locus_colours : array_like or unicode, optional - *Spectral Locus* colours, if ``spectral_locus_colours`` is set to - *RGB*, the colours will be computed according to the corresponding + spectral_locus_colours + Colours of the *Spectral Locus*, if ``spectral_locus_colours`` is set + to *RGB*, the colours will be computed according to the corresponding chromaticity coordinates. - spectral_locus_labels : array_like, optional + spectral_locus_opacity + Opacity of the *Spectral Locus*. + spectral_locus_labels Array of wavelength labels used to customise which labels will be drawn around the spectral locus. Passing an empty array will result in no wavelength labels being drawn. - method : unicode, optional - **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**, + method *Chromaticity Diagram* method. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -98,116 +158,227 @@ def plot_spectral_locus(cmfs='CIE 1931 2 Degree Standard Observer', :alt: plot_spectral_locus """ - if spectral_locus_colours is None: - spectral_locus_colours = CONSTANTS_COLOUR_STYLE.colour.dark + method = validate_method( + method, ["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] + ) - settings = {'uniform': True} + spectral_locus_colours = optional( + spectral_locus_colours, CONSTANTS_COLOUR_STYLE.colour.dark + ) + + settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) - method = method.upper() - - cmfs = first_item(filter_cmfs(cmfs).values()) + cmfs = cast( + MultiSpectralDistributions, first_item(filter_cmfs(cmfs).values()) + ) illuminant = CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint - wavelengths = cmfs.wavelengths + wavelengths = list(cmfs.wavelengths) equal_energy = np.array([1 / 3] * 2) - if method == 'CIE 1931': + if method == "cie 1931": ij = XYZ_to_xy(cmfs.values, illuminant) - labels = ((390, 460, 470, 480, 490, 500, 510, 520, 540, 560, 580, 600, - 620, 700) - if spectral_locus_labels is None else spectral_locus_labels) - elif method == 'CIE 1960 UCS': + labels = cast( + Tuple, + optional( + spectral_locus_labels, + ( + 390, + 460, + 470, + 480, + 490, + 500, + 510, + 520, + 540, + 560, + 580, + 600, + 620, + 700, + ), + ), + ) + elif method == "cie 1960 ucs": ij = UCS_to_uv(XYZ_to_UCS(cmfs.values)) - labels = ((420, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540, - 550, 560, 570, 580, 590, 600, 610, 620, 630, 645, 680) - if spectral_locus_labels is None else spectral_locus_labels) - elif method == 'CIE 1976 UCS': + labels = cast( + Tuple, + optional( + spectral_locus_labels, + ( + 420, + 440, + 450, + 460, + 470, + 480, + 490, + 500, + 510, + 520, + 530, + 540, + 550, + 560, + 570, + 580, + 590, + 600, + 610, + 620, + 630, + 645, + 680, + ), + ), + ) + elif method == "cie 1976 ucs": ij = Luv_to_uv(XYZ_to_Luv(cmfs.values, illuminant), illuminant) - labels = ((420, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540, - 550, 560, 570, 580, 590, 600, 610, 620, 630, 645, 680) - if spectral_locus_labels is None else spectral_locus_labels) - else: - raise ValueError( - 'Invalid method: "{0}", must be one of ' - '[\'CIE 1931\', \'CIE 1960 UCS\', \'CIE 1976 UCS\']'.format( - method)) - - pl_ij = tstack([ - np.linspace(ij[0][0], ij[-1][0], 20), - np.linspace(ij[0][1], ij[-1][1], 20) - ]).reshape(-1, 1, 2) + labels = cast( + Tuple, + optional( + spectral_locus_labels, + ( + 420, + 440, + 450, + 460, + 470, + 480, + 490, + 500, + 510, + 520, + 530, + 540, + 550, + 560, + 570, + 580, + 590, + 600, + 610, + 620, + 630, + 645, + 680, + ), + ), + ) + + pl_ij = np.reshape( + tstack( + [ + np.linspace(ij[0][0], ij[-1][0], 20), + np.linspace(ij[0][1], ij[-1][1], 20), + ] + ), + (-1, 1, 2), + ) sl_ij = np.copy(ij).reshape(-1, 1, 2) - if spectral_locus_colours.upper() == 'RGB': + purple_line_colours: Optional[Union[ArrayLike, str]] + if str(spectral_locus_colours).upper() == "RGB": spectral_locus_colours = normalise_maximum( - XYZ_to_plotting_colourspace(cmfs.values), axis=-1) + XYZ_to_plotting_colourspace(cmfs.values), axis=-1 + ) - if method == 'CIE 1931': + if method == "cie 1931": XYZ = xy_to_XYZ(pl_ij) - elif method == 'CIE 1960 UCS': + elif method == "cie 1960 ucs": XYZ = xy_to_XYZ(UCS_uv_to_xy(pl_ij)) - elif method == 'CIE 1976 UCS': + elif method == "cie 1976 ucs": XYZ = xy_to_XYZ(Luv_uv_to_xy(pl_ij)) + purple_line_colours = normalise_maximum( - XYZ_to_plotting_colourspace(XYZ.reshape(-1, 3)), axis=-1) + XYZ_to_plotting_colourspace(np.reshape(XYZ, (-1, 3))), axis=-1 + ) else: purple_line_colours = spectral_locus_colours - for slp_ij, slp_colours in ((pl_ij, purple_line_colours), - (sl_ij, spectral_locus_colours)): + for slp_ij, slp_colours in ( + (pl_ij, purple_line_colours), + (sl_ij, spectral_locus_colours), + ): line_collection = LineCollection( np.concatenate([slp_ij[:-1], slp_ij[1:]], axis=1), - colors=slp_colours) + colors=slp_colours, + alpha=spectral_locus_opacity, + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_scatter, + ) axes.add_collection(line_collection) - wl_ij = dict(tuple(zip(wavelengths, ij))) + wl_ij = dict(zip(wavelengths, ij)) for label in labels: - ij = wl_ij.get(label) + ij_l = wl_ij.get(label) - if ij is None: + if ij_l is None: continue - i, j = ij - ij = np.array([ij]) + ij_l = as_float_array([ij_l]) + i, j = tsplit(ij_l) index = bisect.bisect(wavelengths, label) left = wavelengths[index - 1] if index >= 0 else wavelengths[index] - right = (wavelengths[index] - if index < len(wavelengths) else wavelengths[-1]) + right = ( + wavelengths[index] if index < len(wavelengths) else wavelengths[-1] + ) dx = wl_ij[right][0] - wl_ij[left][0] dy = wl_ij[right][1] - wl_ij[left][1] direction = np.array([-dy, dx]) - normal = (np.array([-dy, dx]) if np.dot( - normalise_vector(ij - equal_energy), normalise_vector(direction)) > - 0 else np.array([dy, -dx])) + normal = ( + np.array([-dy, dx]) + if np.dot( + normalise_vector(ij_l - equal_energy), + normalise_vector(direction), + ) + > 0 + else np.array([dy, -dx]) + ) normal = normalise_vector(normal) / 30 - label_colour = (spectral_locus_colours - if is_string(spectral_locus_colours) else - spectral_locus_colours[index]) + label_colour = ( + spectral_locus_colours + if is_string(spectral_locus_colours) + else spectral_locus_colours[index] # type: ignore[index] + ) axes.plot( - (i, i + normal[0] * 0.75), (j, j + normal[1] * 0.75), - color=label_colour) + (i, i + normal[0] * 0.75), + (j, j + normal[1] * 0.75), + color=label_colour, + alpha=spectral_locus_opacity, + zorder=CONSTANTS_COLOUR_STYLE.zorder.background_line, + ) - axes.plot(i, j, 'o', color=label_colour) + axes.plot( + i, + j, + "o", + color=label_colour, + alpha=spectral_locus_opacity, + zorder=CONSTANTS_COLOUR_STYLE.zorder.background_line, + ) axes.text( i + normal[0], j + normal[1], label, clip_on=True, - ha='left' if normal[0] >= 0 else 'right', - va='center', - fontdict={'size': 'small'}) + ha="left" if normal[0] >= 0 else "right", + va="center", + fontdict={"size": "small"}, + zorder=CONSTANTS_COLOUR_STYLE.zorder.background_label, + ) - settings = {'axes': axes} + settings = {"axes": axes} settings.update(kwargs) return render(**kwargs) @@ -215,45 +386,58 @@ def plot_spectral_locus(cmfs='CIE 1931 2 Degree Standard Observer', @override_style() def plot_chromaticity_diagram_colours( - samples=256, - diagram_opacity=1.0, - diagram_clipping_path=None, - cmfs='CIE 1931 2 Degree Standard Observer', - method='CIE 1931', - **kwargs): + samples: Integer = 256, + diagram_colours: Optional[Union[ArrayLike, str]] = None, + diagram_opacity: Floating = 1, + diagram_clipping_path: Optional[ArrayLike] = None, + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + method: Union[ + Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"], str + ] = "CIE 1931", + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the *Chromaticity Diagram* colours according to given method. + Plot the *Chromaticity Diagram* colours according to given method. Parameters ---------- - samples : numeric, optional - Samples count on one axis. - diagram_opacity : numeric, optional - Opacity of the *Chromaticity Diagram* colours. - diagram_clipping_path : array_like, optional + samples + Samples count on one axis when computing the *Chromaticity Diagram* + colours. + diagram_colours + Colours of the *Chromaticity Diagram*, if ``diagram_colours`` is set + to *RGB*, the colours will be computed according to the corresponding + coordinates. + diagram_opacity + Opacity of the *Chromaticity Diagram*. + diagram_clipping_path Path of points used to clip the *Chromaticity Diagram* colours. - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - method : unicode, optional - **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**, + method *Chromaticity Diagram* method. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples -------- - >>> plot_chromaticity_diagram_colours() # doctest: +ELLIPSIS + >>> plot_chromaticity_diagram_colours(diagram_colours='RGB') + ... # doctest: +ELLIPSIS (
, <...AxesSubplot...>) .. image:: ../_static/Plotting_Plot_Chromaticity_Diagram_Colours.png @@ -261,101 +445,134 @@ def plot_chromaticity_diagram_colours( :alt: plot_chromaticity_diagram_colours """ - settings = {'uniform': True} + method = validate_method( + method, ["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] + ) + + settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) - method = method.upper() + diagram_colours = cast( + ArrayLike, + optional( + diagram_colours, HEX_to_RGB(CONSTANTS_COLOUR_STYLE.colour.average) + ), + ) - cmfs = first_item(filter_cmfs(cmfs).values()) + cmfs = cast( + MultiSpectralDistributions, first_item(filter_cmfs(cmfs).values()) + ) illuminant = CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint - ii, jj = np.meshgrid( - np.linspace(0, 1, samples), np.linspace(1, 0, samples)) - ij = tstack([ii, jj]) - - # NOTE: Various values in the grid have potential to generate - # zero-divisions, they could be avoided by perturbing the grid, e.g. adding - # a small epsilon. It was decided instead to disable warnings. - with suppress_warnings(python_warnings=True): - if method == 'CIE 1931': - XYZ = xy_to_XYZ(ij) - spectral_locus = XYZ_to_xy(cmfs.values, illuminant) - elif method == 'CIE 1960 UCS': - XYZ = xy_to_XYZ(UCS_uv_to_xy(ij)) - spectral_locus = UCS_to_uv(XYZ_to_UCS(cmfs.values)) - elif method == 'CIE 1976 UCS': - XYZ = xy_to_XYZ(Luv_uv_to_xy(ij)) - spectral_locus = Luv_to_uv( - XYZ_to_Luv(cmfs.values, illuminant), illuminant) - else: - raise ValueError( - 'Invalid method: "{0}", must be one of ' - '[\'CIE 1931\', \'CIE 1960 UCS\', \'CIE 1976 UCS\']'.format( - method)) - - RGB = normalise_maximum( - XYZ_to_plotting_colourspace(XYZ, illuminant), axis=-1) + if method == "cie 1931": + spectral_locus = XYZ_to_xy(cmfs.values, illuminant) + elif method == "cie 1960 ucs": + spectral_locus = UCS_to_uv(XYZ_to_UCS(cmfs.values)) + elif method == "cie 1976 ucs": + spectral_locus = Luv_to_uv( + XYZ_to_Luv(cmfs.values, illuminant), illuminant + ) + + use_RGB_diagram_colours = str(diagram_colours).upper() == "RGB" + if use_RGB_diagram_colours: + ii, jj = np.meshgrid( + np.linspace(0, 1, samples), np.linspace(1, 0, samples) + ) + ij = tstack([ii, jj]) + + # NOTE: Various values in the grid have potential to generate + # zero-divisions, they could be avoided by perturbing the grid, e.g. + # adding a small epsilon. It was decided instead to disable warnings. + with suppress_warnings(python_warnings=True): + if method == "cie 1931": + XYZ = xy_to_XYZ(ij) + elif method == "cie 1960 ucs": + XYZ = xy_to_XYZ(UCS_uv_to_xy(ij)) + elif method == "cie 1976 ucs": + XYZ = xy_to_XYZ(Luv_uv_to_xy(ij)) + + diagram_colours = normalise_maximum( + XYZ_to_plotting_colourspace(XYZ, illuminant), axis=-1 + ) polygon = Polygon( spectral_locus - if diagram_clipping_path is None else diagram_clipping_path, - facecolor='none', - edgecolor='none') + if diagram_clipping_path is None + else diagram_clipping_path, + facecolor="none" + if use_RGB_diagram_colours + else np.hstack([diagram_colours, diagram_opacity]), + edgecolor="none" + if use_RGB_diagram_colours + else np.hstack([diagram_colours, diagram_opacity]), + zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, + ) axes.add_patch(polygon) - # Preventing bounding box related issues as per - # https://github.com/matplotlib/matplotlib/issues/10529 - image = axes.imshow( - RGB, - interpolation='bilinear', - extent=(0, 1, 0, 1), - clip_path=None, - alpha=diagram_opacity) - image.set_clip_path(polygon) - - settings = {'axes': axes} + + if use_RGB_diagram_colours: + # Preventing bounding box related issues as per + # https://github.com/matplotlib/matplotlib/issues/10529 + image = axes.imshow( + diagram_colours, + interpolation="bilinear", + extent=(0, 1, 0, 1), + clip_path=None, + alpha=diagram_opacity, + zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, + ) + image.set_clip_path(polygon) + + settings = {"axes": axes} settings.update(kwargs) return render(**kwargs) @override_style() -def plot_chromaticity_diagram(cmfs='CIE 1931 2 Degree Standard Observer', - show_diagram_colours=True, - show_spectral_locus=True, - method='CIE 1931', - **kwargs): +def plot_chromaticity_diagram( + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + show_diagram_colours: Boolean = True, + show_spectral_locus: Boolean = True, + method: Union[ + Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"], str + ] = "CIE 1931", + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the *Chromaticity Diagram* according to given method. + Plot the *Chromaticity Diagram* according to given method. Parameters ---------- - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - show_diagram_colours : bool, optional + show_diagram_colours Whether to display the *Chromaticity Diagram* background colours. - show_spectral_locus : bool, optional + show_spectral_locus Whether to display the *Spectral Locus*. - method : unicode, optional - **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**, + method *Chromaticity Diagram* method. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_spectral_locus`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram_colours`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -368,53 +585,57 @@ def plot_chromaticity_diagram(cmfs='CIE 1931 2 Degree Standard Observer', :alt: plot_chromaticity_diagram """ - settings = {'uniform': True} + method = validate_method( + method, ["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] + ) + + settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) - method = method.upper() - - cmfs = first_item(filter_cmfs(cmfs).values()) + cmfs = cast( + MultiSpectralDistributions, first_item(filter_cmfs(cmfs).values()) + ) if show_diagram_colours: - settings = {'axes': axes, 'method': method} + settings = {"axes": axes, "method": method, "diagram_colours": "RGB"} settings.update(kwargs) - settings['standalone'] = False - settings['cmfs'] = cmfs + settings["standalone"] = False + settings["cmfs"] = cmfs plot_chromaticity_diagram_colours(**settings) if show_spectral_locus: - settings = {'axes': axes, 'method': method} + settings = {"axes": axes, "method": method} settings.update(kwargs) - settings['standalone'] = False - settings['cmfs'] = cmfs + settings["standalone"] = False + settings["cmfs"] = cmfs plot_spectral_locus(**settings) - if method == 'CIE 1931': - x_label, y_label = 'CIE x', 'CIE y' - elif method == 'CIE 1960 UCS': - x_label, y_label = 'CIE u', 'CIE v' - elif method == 'CIE 1976 UCS': - x_label, y_label = 'CIE u\'', 'CIE v\'', - else: - raise ValueError( - 'Invalid method: "{0}", must be one of ' - '[\'CIE 1931\', \'CIE 1960 UCS\', \'CIE 1976 UCS\']'.format( - method)) - - title = '{0} Chromaticity Diagram - {1}'.format(method, cmfs.strict_name) - - settings.update({ - 'axes': axes, - 'standalone': True, - 'bounding_box': (0, 1, 0, 1), - 'title': title, - 'x_label': x_label, - 'y_label': y_label, - }) + if method == "cie 1931": + x_label, y_label = "CIE x", "CIE y" + elif method == "cie 1960 ucs": + x_label, y_label = "CIE u", "CIE v" + elif method == "cie 1976 ucs": + x_label, y_label = ( + "CIE u'", + "CIE v'", + ) + + title = f"{method.upper()} Chromaticity Diagram - {cmfs.strict_name}" + + settings.update( + { + "axes": axes, + "standalone": True, + "bounding_box": (0, 1, 0, 1), + "title": title, + "x_label": x_label, + "y_label": y_label, + } + ) settings.update(kwargs) return render(**settings) @@ -422,35 +643,40 @@ def plot_chromaticity_diagram(cmfs='CIE 1931 2 Degree Standard Observer', @override_style() def plot_chromaticity_diagram_CIE1931( - cmfs='CIE 1931 2 Degree Standard Observer', - show_diagram_colours=True, - show_spectral_locus=True, - **kwargs): + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + show_diagram_colours: Boolean = True, + show_spectral_locus: Boolean = True, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the *CIE 1931 Chromaticity Diagram*. + Plot the *CIE 1931 Chromaticity Diagram*. Parameters ---------- - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - show_diagram_colours : bool, optional + show_diagram_colours Whether to display the *Chromaticity Diagram* background colours. - show_spectral_locus : bool, optional + show_spectral_locus Whether to display the *Spectral Locus*. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -464,43 +690,49 @@ def plot_chromaticity_diagram_CIE1931( """ settings = dict(kwargs) - settings.update({'method': 'CIE 1931'}) + settings.update({"method": "CIE 1931"}) - return plot_chromaticity_diagram(cmfs, show_diagram_colours, - show_spectral_locus, **settings) + return plot_chromaticity_diagram( + cmfs, show_diagram_colours, show_spectral_locus, **settings + ) @override_style() def plot_chromaticity_diagram_CIE1960UCS( - cmfs='CIE 1931 2 Degree Standard Observer', - show_diagram_colours=True, - show_spectral_locus=True, - **kwargs): + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + show_diagram_colours: Boolean = True, + show_spectral_locus: Boolean = True, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the *CIE 1960 UCS Chromaticity Diagram*. + Plot the *CIE 1960 UCS Chromaticity Diagram*. Parameters ---------- - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - show_diagram_colours : bool, optional + show_diagram_colours Whether to display the *Chromaticity Diagram* background colours. - show_spectral_locus : bool, optional + show_spectral_locus Whether to display the *Spectral Locus*. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -514,43 +746,49 @@ def plot_chromaticity_diagram_CIE1960UCS( """ settings = dict(kwargs) - settings.update({'method': 'CIE 1960 UCS'}) + settings.update({"method": "CIE 1960 UCS"}) - return plot_chromaticity_diagram(cmfs, show_diagram_colours, - show_spectral_locus, **settings) + return plot_chromaticity_diagram( + cmfs, show_diagram_colours, show_spectral_locus, **settings + ) @override_style() def plot_chromaticity_diagram_CIE1976UCS( - cmfs='CIE 1931 2 Degree Standard Observer', - show_diagram_colours=True, - show_spectral_locus=True, - **kwargs): + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + show_diagram_colours: Boolean = True, + show_spectral_locus: Boolean = True, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the *CIE 1976 UCS Chromaticity Diagram*. + Plot the *CIE 1976 UCS Chromaticity Diagram*. Parameters ---------- - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - show_diagram_colours : bool, optional + show_diagram_colours Whether to display the *Chromaticity Diagram* background colours. - show_spectral_locus : bool, optional + show_spectral_locus Whether to display the *Spectral Locus*. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -564,89 +802,99 @@ def plot_chromaticity_diagram_CIE1976UCS( """ settings = dict(kwargs) - settings.update({'method': 'CIE 1976 UCS'}) + settings.update({"method": "CIE 1976 UCS"}) - return plot_chromaticity_diagram(cmfs, show_diagram_colours, - show_spectral_locus, **settings) + return plot_chromaticity_diagram( + cmfs, show_diagram_colours, show_spectral_locus, **settings + ) @override_style() def plot_sds_in_chromaticity_diagram( - sds, - cmfs='CIE 1931 2 Degree Standard Observer', - chromaticity_diagram_callable=plot_chromaticity_diagram, - method='CIE 1931', - annotate_kwargs=None, - plot_kwargs=None, - **kwargs): + sds: Union[ + Sequence[Union[SpectralDistribution, MultiSpectralDistributions]], + MultiSpectralDistributions, + ], + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + chromaticity_diagram_callable: Callable = plot_chromaticity_diagram, + method: Union[ + Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"], str + ] = "CIE 1931", + annotate_kwargs: Optional[Union[Dict, List[Dict]]] = None, + plot_kwargs: Optional[Union[Dict, List[Dict]]] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given spectral distribution chromaticity coordinates into the + Plot given spectral distribution chromaticity coordinates into the *Chromaticity Diagram* using given method. Parameters ---------- - sds : array_like or MultiSpectralDistributions + sds Spectral distributions or multi-spectral distributions to plot. `sds` can be a single :class:`colour.MultiSpectralDistributions` class instance, a list of :class:`colour.MultiSpectralDistributions` class instances or a list of :class:`colour.SpectralDistribution` class instances. - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - chromaticity_diagram_callable : callable, optional + chromaticity_diagram_callable Callable responsible for drawing the *Chromaticity Diagram*. - method : unicode, optional - **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**, + method *Chromaticity Diagram* method. - annotate_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.annotate` definition, used to - annotate the resulting chromaticity coordinates with their respective - spectral distribution names. ``annotate_kwargs`` can be either a single - dictionary applied to all the arrows with same settings or a sequence - of dictionaries with different settings for each spectral distribution. - The following special keyword arguments can also be used: - - - *annotate* : bool, whether to annotate the spectral distributions. - plot_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.plot` definition, used to control - the style of the plotted spectral distributions. ``plot_kwargs`` can be - either a single dictionary applied to all the plotted spectral - distributions with same settings or a sequence of dictionaries with - different settings for each plotted spectral distributions. - The following special keyword arguments can also be used: - - - *illuminant* : unicode or :class:`colour.SpectralDistribution`, the - illuminant used to compute the spectral distributions colours. The - default is the illuminant associated with the whitepoint of the - default plotting colourspace. ``illuminant`` can be of any type or - form supported by the :func:`colour.plotting.filter_cmfs` - definition. - - *cmfs* : unicode, the standard observer colour matching functions - used for computing the spectral distributions colours. ``cmfs`` can - be of any type or form supported by the + annotate_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.annotate` + definition, used to annotate the resulting chromaticity coordinates + with their respective spectral distribution names. ``annotate_kwargs`` + can be either a single dictionary applied to all the arrows with same + settings or a sequence of dictionaries with different settings for each + spectral distribution. The following special keyword arguments can also + be used: + + - ``annotate`` : Whether to annotate the spectral distributions. + plot_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.plot` definition, + used to control the style of the plotted spectral distributions. + `plot_kwargs`` can be either a single dictionary applied to all the + plotted spectral distributions with the same settings or a sequence of + dictionaries with different settings for each plotted spectral + distributions. The following special keyword arguments can also be + used: + + - ``illuminant`` : The illuminant used to compute the spectral + distributions colours. The default is the illuminant associated + with the whitepoint of the default plotting colourspace. + ``illuminant`` can be of any type or form supported by the + :func:`colour.plotting.filter_cmfs` definition. + - ``cmfs`` : The standard observer colour matching functions used for + computing the spectral distributions colours. ``cmfs`` can be of + any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - - *normalise_sd_colours* : bool, whether to normalise the computed + - ``normalise_sd_colours`` : Whether to normalise the computed spectral distributions colours. The default is *True*. - - *use_sd_colours* : bool, whether to use the computed spectral + - ``use_sd_colours`` : Whether to use the computed spectral distributions colours under the plotting colourspace illuminant. - Alternatively, it is possible to use the :func:`plt.plot` - definition ``color`` argument with pre-computed values. The default - is *True*. + Alternatively, it is possible to use the + :func:`matplotlib.pyplot.plot` definition ``color`` argument with + pre-computed values. The default is *True*. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - Also handles keywords arguments for deprecation management. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -676,44 +924,44 @@ def plot_sds_in_chromaticity_diagram( :alt: plot_sds_in_chromaticity_diagram """ - annotate_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['annotate_parameters', 'annotate_kwargs']], - }, **kwargs).get('annotate_kwargs', annotate_kwargs) + method = validate_method( + method, ["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] + ) - sds = sds_and_msds_to_sds(sds) + sds_converted = sds_and_msds_to_sds(sds) - settings = {'uniform': True} + settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) - method = method.upper() - - settings.update({ - 'axes': axes, - 'standalone': False, - 'method': method, - 'cmfs': cmfs, - }) + settings.update( + { + "axes": axes, + "standalone": False, + "method": method, + "cmfs": cmfs, + } + ) chromaticity_diagram_callable(**settings) - if method == 'CIE 1931': + if method == "cie 1931": - def XYZ_to_ij(XYZ): + def XYZ_to_ij(XYZ: NDArray) -> NDArray: """ - Converts given *CIE XYZ* tristimulus values to *ij* chromaticity + Convert given *CIE XYZ* tristimulus values to *ij* chromaticity coordinates. """ return XYZ_to_xy(XYZ) bounding_box = (-0.1, 0.9, -0.1, 0.9) - elif method == 'CIE 1960 UCS': + elif method == "cie 1960 ucs": - def XYZ_to_ij(XYZ): + def XYZ_to_ij(XYZ: NDArray) -> NDArray: """ - Converts given *CIE XYZ* tristimulus values to *ij* chromaticity + Convert given *CIE XYZ* tristimulus values to *ij* chromaticity coordinates. """ @@ -721,94 +969,99 @@ def XYZ_to_ij(XYZ): bounding_box = (-0.1, 0.7, -0.2, 0.6) - elif method == 'CIE 1976 UCS': + elif method == "cie 1976 ucs": - def XYZ_to_ij(XYZ): + def XYZ_to_ij(XYZ: NDArray) -> NDArray: """ - Converts given *CIE XYZ* tristimulus values to *ij* chromaticity + Convert given *CIE XYZ* tristimulus values to *ij* chromaticity coordinates. """ return Luv_to_uv(XYZ_to_Luv(XYZ)) bounding_box = (-0.1, 0.7, -0.1, 0.7) - else: - raise ValueError( - 'Invalid method: "{0}", must be one of ' - '[\'CIE 1931\', \'CIE 1960 UCS\', \'CIE 1976 UCS\']'.format( - method)) - - annotate_settings_collection = [{ - 'annotate': True, - 'xytext': (-50, 30), - 'textcoords': 'offset points', - 'arrowprops': CONSTANTS_ARROW_STYLE, - } for _ in range(len(sds))] + + annotate_settings_collection = [ + { + "annotate": True, + "xytext": (-50, 30), + "textcoords": "offset points", + "arrowprops": CONSTANTS_ARROW_STYLE, + "zorder": CONSTANTS_COLOUR_STYLE.zorder.midground_annotation, + } + for _ in range(len(sds_converted)) + ] if annotate_kwargs is not None: - update_settings_collection(annotate_settings_collection, - annotate_kwargs, len(sds)) - - plot_settings_collection = [{ - 'color': - CONSTANTS_COLOUR_STYLE.colour.brightest, - 'label': - '{0}'.format(sd.strict_name), - 'marker': - 'o', - 'markeredgecolor': - CONSTANTS_COLOUR_STYLE.colour.dark, - 'markeredgewidth': - CONSTANTS_COLOUR_STYLE.geometry.short * 0.75, - 'markersize': (CONSTANTS_COLOUR_STYLE.geometry.short * 6 + - CONSTANTS_COLOUR_STYLE.geometry.short * 0.75), - 'cmfs': - cmfs, - 'illuminant': - SDS_ILLUMINANTS[ + update_settings_collection( + annotate_settings_collection, annotate_kwargs, len(sds_converted) + ) + + plot_settings_collection = [ + { + "color": CONSTANTS_COLOUR_STYLE.colour.brightest, + "label": f"{sd.strict_name}", + "marker": "o", + "markeredgecolor": CONSTANTS_COLOUR_STYLE.colour.dark, + "markeredgewidth": CONSTANTS_COLOUR_STYLE.geometry.short * 0.75, + "markersize": ( + CONSTANTS_COLOUR_STYLE.geometry.short * 6 + + CONSTANTS_COLOUR_STYLE.geometry.short * 0.75 + ), + "zorder": CONSTANTS_COLOUR_STYLE.zorder.midground_line, + "cmfs": cmfs, + "illuminant": SDS_ILLUMINANTS[ CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint_name ], - 'use_sd_colours': - False, - 'normalise_sd_colours': - False, - } for sd in sds] + "use_sd_colours": False, + "normalise_sd_colours": False, + } + for sd in sds_converted + ] if plot_kwargs is not None: - update_settings_collection(plot_settings_collection, plot_kwargs, - len(sds)) + update_settings_collection( + plot_settings_collection, plot_kwargs, len(sds_converted) + ) - for i, sd in enumerate(sds): + for i, sd in enumerate(sds_converted): plot_settings = plot_settings_collection[i] - cmfs = first_item(filter_cmfs(plot_settings.pop('cmfs')).values()) - illuminant = first_item( - filter_illuminants(plot_settings.pop('illuminant')).values()) - normalise_sd_colours = plot_settings.pop('normalise_sd_colours') - use_sd_colours = plot_settings.pop('use_sd_colours') - - with domain_range_scale('1'): + cmfs = cast( + MultiSpectralDistributions, + first_item(filter_cmfs(plot_settings.pop("cmfs")).values()), + ) + illuminant = cast( + SpectralDistribution, + first_item( + filter_illuminants(plot_settings.pop("illuminant")).values() + ), + ) + normalise_sd_colours = plot_settings.pop("normalise_sd_colours") + use_sd_colours = plot_settings.pop("use_sd_colours") + + with domain_range_scale("1"): XYZ = sd_to_XYZ(sd, cmfs, illuminant) if use_sd_colours: if normalise_sd_colours: XYZ /= XYZ[..., 1] - plot_settings['color'] = np.clip( - XYZ_to_plotting_colourspace(XYZ), 0, 1) + plot_settings["color"] = np.clip( + XYZ_to_plotting_colourspace(XYZ), 0, 1 + ) ij = XYZ_to_ij(XYZ) axes.plot(ij[0], ij[1], **plot_settings) - if (sd.name is not None and - annotate_settings_collection[i]['annotate']): + if sd.name is not None and annotate_settings_collection[i]["annotate"]: annotate_settings = annotate_settings_collection[i] - annotate_settings.pop('annotate') + annotate_settings.pop("annotate") axes.annotate(sd.name, xy=ij, **annotate_settings) - settings.update({'standalone': True, 'bounding_box': bounding_box}) + settings.update({"standalone": True, "bounding_box": bounding_box}) settings.update(kwargs) return render(**settings) @@ -816,78 +1069,87 @@ def XYZ_to_ij(XYZ): @override_style() def plot_sds_in_chromaticity_diagram_CIE1931( - sds, - cmfs='CIE 1931 2 Degree Standard Observer', - chromaticity_diagram_callable_CIE1931=( - plot_chromaticity_diagram_CIE1931), - annotate_kwargs=None, - plot_kwargs=None, - **kwargs): + sds: Union[ + Sequence[Union[SpectralDistribution, MultiSpectralDistributions]], + MultiSpectralDistributions, + ], + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + chromaticity_diagram_callable_CIE1931: Callable = ( + plot_chromaticity_diagram_CIE1931 + ), + annotate_kwargs: Optional[Union[Dict, List[Dict]]] = None, + plot_kwargs: Optional[Union[Dict, List[Dict]]] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given spectral distribution chromaticity coordinates into the + Plot given spectral distribution chromaticity coordinates into the *CIE 1931 Chromaticity Diagram*. Parameters ---------- - sds : array_like or MultiSpectralDistributions + sds Spectral distributions or multi-spectral distributions to plot. `sds` can be a single :class:`colour.MultiSpectralDistributions` class instance, a list of :class:`colour.MultiSpectralDistributions` class instances or a list of :class:`colour.SpectralDistribution` class instances. - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - chromaticity_diagram_callable_CIE1931 : callable, optional + chromaticity_diagram_callable_CIE1931 Callable responsible for drawing the *CIE 1931 Chromaticity Diagram*. - annotate_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.annotate` definition, used to - annotate the resulting chromaticity coordinates with their respective - spectral distribution names. ``annotate_kwargs`` can be either a single - dictionary applied to all the arrows with same settings or a sequence - of dictionaries with different settings for each spectral distribution. - The following special keyword arguments can also be used: - - - *annotate* : bool, whether to annotate the spectral distributions. - plot_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.plot` definition, used to control - the style of the plotted spectral distributions. ``plot_kwargs`` can be - either a single dictionary applied to all the plotted spectral - distributions with same settings or a sequence of dictionaries with - different settings for each plotted spectral distributions. - The following special keyword arguments can also be used: - - - *illuminant* : unicode or :class:`colour.SpectralDistribution`, the - illuminant used to compute the spectral distributions colours. The - default is the illuminant associated with the whitepoint of the - default plotting colourspace. ``illuminant`` can be of any type or - form supported by the :func:`colour.plotting.filter_cmfs` - definition. - - *cmfs* : unicode, the standard observer colour matching functions - used for computing the spectral distributions colours. ``cmfs`` can - be of any type or form supported by the + annotate_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.annotate` + definition, used to annotate the resulting chromaticity coordinates + with their respective spectral distribution names. ``annotate_kwargs`` + can be either a single dictionary applied to all the arrows with same + settings or a sequence of dictionaries with different settings for each + spectral distribution. The following special keyword arguments can also + be used: + + - ``annotate`` : Whether to annotate the spectral distributions. + plot_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.plot` definition, + used to control the style of the plotted spectral distributions. + `plot_kwargs`` can be either a single dictionary applied to all the + plotted spectral distributions with the same settings or a sequence of + dictionaries with different settings for each plotted spectral + distributions. The following special keyword arguments can also be + used: + + - ``illuminant`` : The illuminant used to compute the spectral + distributions colours. The default is the illuminant associated + with the whitepoint of the default plotting colourspace. + ``illuminant`` can be of any type or form supported by the + :func:`colour.plotting.filter_cmfs` definition. + - ``cmfs`` : The standard observer colour matching functions used for + computing the spectral distributions colours. ``cmfs`` can be of + any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - - *normalise_sd_colours* : bool, whether to normalise the computed + - ``normalise_sd_colours`` : Whether to normalise the computed spectral distributions colours. The default is *True*. - - *use_sd_colours* : bool, whether to use the computed spectral + - ``use_sd_colours`` : Whether to use the computed spectral distributions colours under the plotting colourspace illuminant. - Alternatively, it is possible to use the :func:`plt.plot` - definition ``color`` argument with pre-computed values. The default - is *True*. + Alternatively, it is possible to use the + :func:`matplotlib.pyplot.plot` definition ``color`` argument with + pre-computed values. The default is *True*. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - Also handles keywords arguments for deprecation management. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -904,12 +1166,8 @@ class instances or a list of :class:`colour.SpectralDistribution` class :alt: plot_sds_in_chromaticity_diagram_CIE1931 """ - annotate_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['annotate_parameters', 'annotate_kwargs']], - }, **kwargs).get('annotate_kwargs', annotate_kwargs) - settings = dict(kwargs) - settings.update({'method': 'CIE 1931'}) + settings.update({"method": "CIE 1931"}) return plot_sds_in_chromaticity_diagram( sds, @@ -917,84 +1175,94 @@ class instances or a list of :class:`colour.SpectralDistribution` class chromaticity_diagram_callable_CIE1931, annotate_kwargs=annotate_kwargs, plot_kwargs=plot_kwargs, - **settings) + **settings, + ) @override_style() def plot_sds_in_chromaticity_diagram_CIE1960UCS( - sds, - cmfs='CIE 1931 2 Degree Standard Observer', - chromaticity_diagram_callable_CIE1960UCS=( - plot_chromaticity_diagram_CIE1960UCS), - annotate_kwargs=None, - plot_kwargs=None, - **kwargs): + sds: Union[ + Sequence[Union[SpectralDistribution, MultiSpectralDistributions]], + MultiSpectralDistributions, + ], + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + chromaticity_diagram_callable_CIE1960UCS: Callable = ( + plot_chromaticity_diagram_CIE1960UCS + ), + annotate_kwargs: Optional[Union[Dict, List[Dict]]] = None, + plot_kwargs: Optional[Union[Dict, List[Dict]]] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given spectral distribution chromaticity coordinates into the + Plot given spectral distribution chromaticity coordinates into the *CIE 1960 UCS Chromaticity Diagram*. Parameters ---------- - sds : array_like or MultiSpectralDistributions + sds Spectral distributions or multi-spectral distributions to plot. `sds` can be a single :class:`colour.MultiSpectralDistributions` class instance, a list of :class:`colour.MultiSpectralDistributions` class instances or a list of :class:`colour.SpectralDistribution` class instances. - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - chromaticity_diagram_callable_CIE1960UCS : callable, optional + chromaticity_diagram_callable_CIE1960UCS Callable responsible for drawing the *CIE 1960 UCS Chromaticity Diagram*. - annotate_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.annotate` definition, used to - annotate the resulting chromaticity coordinates with their respective - spectral distribution names. ``annotate_kwargs`` can be either a single - dictionary applied to all the arrows with same settings or a sequence - of dictionaries with different settings for each spectral distribution. - The following special keyword arguments can also be used: - - - *annotate* : bool, whether to annotate the spectral distributions. - plot_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.plot` definition, used to control - the style of the plotted spectral distributions. ``plot_kwargs`` can be - either a single dictionary applied to all the plotted spectral - distributions with same settings or a sequence of dictionaries with - different settings for each plotted spectral distributions. - The following special keyword arguments can also be used: - - - *illuminant* : unicode or :class:`colour.SpectralDistribution`, the - illuminant used to compute the spectral distributions colours. The - default is the illuminant associated with the whitepoint of the - default plotting colourspace. ``illuminant`` can be of any type or - form supported by the :func:`colour.plotting.filter_cmfs` - definition. - - *cmfs* : unicode, the standard observer colour matching functions - used for computing the spectral distributions colours. ``cmfs`` can - be of any type or form supported by the + annotate_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.annotate` + definition, used to annotate the resulting chromaticity coordinates + with their respective spectral distribution names. ``annotate_kwargs`` + can be either a single dictionary applied to all the arrows with same + settings or a sequence of dictionaries with different settings for each + spectral distribution. The following special keyword arguments can also + be used: + + - ``annotate`` : Whether to annotate the spectral distributions. + plot_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.plot` definition, + used to control the style of the plotted spectral distributions. + `plot_kwargs`` can be either a single dictionary applied to all the + plotted spectral distributions with the same settings or a sequence of + dictionaries with different settings for each plotted spectral + distributions. The following special keyword arguments can also be + used: + + - ``illuminant`` : The illuminant used to compute the spectral + distributions colours. The default is the illuminant associated + with the whitepoint of the default plotting colourspace. + ``illuminant`` can be of any type or form supported by the + :func:`colour.plotting.filter_cmfs` definition. + - ``cmfs`` : The standard observer colour matching functions used for + computing the spectral distributions colours. ``cmfs`` can be of + any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - - *normalise_sd_colours* : bool, whether to normalise the computed + - ``normalise_sd_colours`` : Whether to normalise the computed spectral distributions colours. The default is *True*. - - *use_sd_colours* : bool, whether to use the computed spectral + - ``use_sd_colours`` : Whether to use the computed spectral distributions colours under the plotting colourspace illuminant. - Alternatively, it is possible to use the :func:`plt.plot` - definition ``color`` argument with pre-computed values. The default - is *True*. + Alternatively, it is possible to use the + :func:`matplotlib.pyplot.plot` definition ``color`` argument with + pre-computed values. The default is *True*. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - Also handles keywords arguments for deprecation management. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -1011,12 +1279,8 @@ class instances or a list of :class:`colour.SpectralDistribution` class :alt: plot_sds_in_chromaticity_diagram_CIE1960UCS """ - annotate_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['annotate_parameters', 'annotate_kwargs']], - }, **kwargs).get('annotate_kwargs', annotate_kwargs) - settings = dict(kwargs) - settings.update({'method': 'CIE 1960 UCS'}) + settings.update({"method": "CIE 1960 UCS"}) return plot_sds_in_chromaticity_diagram( sds, @@ -1024,84 +1288,94 @@ class instances or a list of :class:`colour.SpectralDistribution` class chromaticity_diagram_callable_CIE1960UCS, annotate_kwargs=annotate_kwargs, plot_kwargs=plot_kwargs, - **settings) + **settings, + ) @override_style() def plot_sds_in_chromaticity_diagram_CIE1976UCS( - sds, - cmfs='CIE 1931 2 Degree Standard Observer', - chromaticity_diagram_callable_CIE1976UCS=( - plot_chromaticity_diagram_CIE1976UCS), - annotate_kwargs=None, - plot_kwargs=None, - **kwargs): + sds: Union[ + Sequence[Union[SpectralDistribution, MultiSpectralDistributions]], + MultiSpectralDistributions, + ], + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + chromaticity_diagram_callable_CIE1976UCS: Callable = ( + plot_chromaticity_diagram_CIE1976UCS + ), + annotate_kwargs: Optional[Union[Dict, List[Dict]]] = None, + plot_kwargs: Optional[Union[Dict, List[Dict]]] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given spectral distribution chromaticity coordinates into the + Plot given spectral distribution chromaticity coordinates into the *CIE 1976 UCS Chromaticity Diagram*. Parameters ---------- - sds : array_like or MultiSpectralDistributions + sds Spectral distributions or multi-spectral distributions to plot. `sds` can be a single :class:`colour.MultiSpectralDistributions` class instance, a list of :class:`colour.MultiSpectralDistributions` class instances or a list of :class:`colour.SpectralDistribution` class instances. - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - chromaticity_diagram_callable_CIE1976UCS : callable, optional + chromaticity_diagram_callable_CIE1976UCS Callable responsible for drawing the *CIE 1976 UCS Chromaticity Diagram*. - annotate_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.annotate` definition, used to - annotate the resulting chromaticity coordinates with their respective - spectral distribution names. ``annotate_kwargs`` can be either a single - dictionary applied to all the arrows with same settings or a sequence - of dictionaries with different settings for each spectral distribution. - The following special keyword arguments can also be used: - - - *annotate* : bool, whether to annotate the spectral distributions. - plot_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.plot` definition, used to control - the style of the plotted spectral distributions. ``plot_kwargs`` can be - either a single dictionary applied to all the plotted spectral - distributions with same settings or a sequence of dictionaries with - different settings for each plotted spectral distributions. - The following special keyword arguments can also be used: - - - *illuminant* : unicode or :class:`colour.SpectralDistribution`, the - illuminant used to compute the spectral distributions colours. The - default is the illuminant associated with the whitepoint of the - default plotting colourspace. ``illuminant`` can be of any type or - form supported by the :func:`colour.plotting.filter_cmfs` - definition. - - *cmfs* : unicode, the standard observer colour matching functions - used for computing the spectral distributions colours. ``cmfs`` can - be of any type or form supported by the + annotate_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.annotate` + definition, used to annotate the resulting chromaticity coordinates + with their respective spectral distribution names. ``annotate_kwargs`` + can be either a single dictionary applied to all the arrows with same + settings or a sequence of dictionaries with different settings for each + spectral distribution. The following special keyword arguments can also + be used: + + - ``annotate`` : Whether to annotate the spectral distributions. + plot_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.plot` definition, + used to control the style of the plotted spectral distributions. + `plot_kwargs`` can be either a single dictionary applied to all the + plotted spectral distributions with the same settings or a sequence of + dictionaries with different settings for each plotted spectral + distributions. The following special keyword arguments can also be + used: + + - ``illuminant`` : The illuminant used to compute the spectral + distributions colours. The default is the illuminant associated + with the whitepoint of the default plotting colourspace. + ``illuminant`` can be of any type or form supported by the + :func:`colour.plotting.filter_cmfs` definition. + - ``cmfs`` : The standard observer colour matching functions used for + computing the spectral distributions colours. ``cmfs`` can be of + any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - - *normalise_sd_colours* : bool, whether to normalise the computed + - ``normalise_sd_colours`` : Whether to normalise the computed spectral distributions colours. The default is *True*. - - *use_sd_colours* : bool, whether to use the computed spectral + - ``use_sd_colours`` : Whether to use the computed spectral distributions colours under the plotting colourspace illuminant. - Alternatively, it is possible to use the :func:`plt.plot` - definition ``color`` argument with pre-computed values. The default - is *True*. + Alternatively, it is possible to use the + :func:`matplotlib.pyplot.plot` definition ``color`` argument with + pre-computed values. The default is *True*. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - Also handles keywords arguments for deprecation management. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -1118,12 +1392,8 @@ class instances or a list of :class:`colour.SpectralDistribution` class :alt: plot_sds_in_chromaticity_diagram_CIE1976UCS """ - annotate_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['annotate_parameters', 'annotate_kwargs']], - }, **kwargs).get('annotate_kwargs', annotate_kwargs) - settings = dict(kwargs) - settings.update({'method': 'CIE 1976 UCS'}) + settings.update({"method": "CIE 1976 UCS"}) return plot_sds_in_chromaticity_diagram( sds, @@ -1131,4 +1401,5 @@ class instances or a list of :class:`colour.SpectralDistribution` class chromaticity_diagram_callable_CIE1976UCS, annotate_kwargs=annotate_kwargs, plot_kwargs=plot_kwargs, - **settings) + **settings, + ) diff --git a/colour/plotting/graph.py b/colour/plotting/graph.py index 2c122f1055..9f28499cdb 100644 --- a/colour/plotting/graph.py +++ b/colour/plotting/graph.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Automatic Colour Conversion Graph Plotting ========================================== @@ -8,43 +7,53 @@ - :func:`colour.plotting.plot_automatic_colour_conversion_graph` """ -from __future__ import division +from __future__ import annotations import colour -from colour.graph import (CONVERSION_GRAPH_NODE_LABELS, - describe_conversion_path) -from colour.utilities import required - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['plot_automatic_colour_conversion_graph'] - - -@required('NetworkX') -def plot_automatic_colour_conversion_graph(filename, prog='fdp', args=''): +from colour.graph import ( + CONVERSION_GRAPH_NODE_LABELS, + describe_conversion_path, +) +from colour.hints import Literal, Union +from colour.utilities import required, validate_method + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "plot_automatic_colour_conversion_graph", +] + + +@required("NetworkX") +def plot_automatic_colour_conversion_graph( + filename: str, + prog: Union[ + Literal["circo", "dot", "fdp", "neato", "nop", "twopi"], str + ] = "fdp", + args: str = "", +) -> AGraph: # type: ignore[name-defined] # noqa """ - Plots *Colour* automatic colour conversion graph using + Plot *Colour* automatic colour conversion graph using `Graphviz `__ and `pyraphviz `__. Parameters ---------- - filename : unicode + filename Filename to use to save the image. - prog : unicode, optional - {'neato', 'dot', 'twopi', 'circo', 'fdp', 'nop'}, + prog *Graphviz* layout method. - args : unicode, optional + args Additional arguments for *Graphviz*. Returns ------- - AGraph + :class:`AGraph` *Pyraphviz* graph. Notes @@ -70,8 +79,14 @@ def plot_automatic_colour_conversion_graph(filename, prog='fdp', args=''): import networkx as nx + prog = validate_method( + prog, + ["circo", "dot", "fdp", "neato", "nop", "twopi"], + '"{0}" program is invalid, it must be one of {1}!', + ) + # TODO: Investigate API to trigger the conversion graph build. - describe_conversion_path('RGB', 'RGB', print_callable=lambda x: x) + describe_conversion_path("RGB", "RGB", print_callable=lambda x: x) agraph = nx.nx_agraph.to_agraph(colour.graph.CONVERSION_GRAPH) @@ -79,23 +94,35 @@ def plot_automatic_colour_conversion_graph(filename, prog='fdp', args=''): node.attr.update(label=CONVERSION_GRAPH_NODE_LABELS[node.name]) agraph.node_attr.update( - style='filled', - shape='circle', - color='#2196F3FF', - fillcolor='#2196F370', - fontname='Helvetica', - fontcolor='#263238') - agraph.edge_attr.update(color='#26323870') - for node in ('CIE XYZ', 'RGB', 'Spectral Distribution'): + style="filled", + shape="circle", + color="#2196F3FF", + fillcolor="#2196F370", + fontname="Helvetica", + fontcolor="#263238", + ) + agraph.edge_attr.update(color="#26323870") + for node in ("CIE XYZ", "RGB", "Spectral Distribution"): agraph.get_node(node.lower()).attr.update( - shape='doublecircle', - color='#673AB7FF', - fillcolor='#673AB770', - fontsize=30) - for node in ('ATD95', 'CAM16', 'CIECAM02', 'Hunt', 'LLAB', 'Nayatani95', - 'RLAB'): + shape="doublecircle", + color="#673AB7FF", + fillcolor="#673AB770", + fontsize=30, + ) + for node in ( + "ATD95", + "CAM16", + "CIECAM02", + "Hunt", + "Kim 2009", + "LLAB", + "Nayatani95", + "RLAB", + "ZCAM", + ): agraph.get_node(node.lower()).attr.update( - color='#00BCD4FF', fillcolor='#00BCD470') + color="#00BCD4FF", fillcolor="#00BCD470" + ) agraph.draw(filename, prog=prog, args=args) diff --git a/colour/plotting/models.py b/colour/plotting/models.py index ceef0d70fa..40f5c0c723 100644 --- a/colour/plotting/models.py +++ b/colour/plotting/models.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Colour Models Plotting ====================== @@ -41,147 +40,274 @@ doi:10.5281/zenodo.3362520 """ -from __future__ import division +from __future__ import annotations +import matplotlib.pyplot as plt import numpy as np import scipy.optimize -try: # pragma: no cover - from collections import Mapping -except ImportError: # pragma: no cover - from collections.abc import Mapping - from matplotlib.patches import Ellipse from matplotlib.path import Path +from colour.colorimetry import MultiSpectralDistributions from colour.constants import EPSILON -from colour.algebra import (point_at_angle_on_ellipse, - ellipse_coefficients_canonical_form, - ellipse_fitting) +from colour.algebra import ( + point_at_angle_on_ellipse, + ellipse_coefficients_canonical_form, + ellipse_fitting, +) from colour.graph import convert +from colour.hints import ( + Any, + ArrayLike, + Boolean, + Callable, + Dict, + Floating, + List, + Literal, + NDArray, + Optional, + Sequence, + Tuple, + Union, + cast, +) from colour.models import ( - COLOURSPACE_MODELS_AXIS_LABELS, CCTF_ENCODINGS, CCTF_DECODINGS, - LCHab_to_Lab, Lab_to_XYZ, Luv_to_uv, DATA_MACADAM_1942_ELLIPSES, - CCS_POINTER_GAMUT_BOUNDARY, DATA_POINTER_GAMUT_VOLUME, - CCS_ILLUMINANT_POINTER_GAMUT, RGB_to_RGB, RGB_to_XYZ, UCS_to_uv, - XYZ_to_Luv, XYZ_to_RGB, XYZ_to_UCS, XYZ_to_xy, xy_to_Luv_uv, xy_to_UCS_uv) + COLOURSPACE_MODELS_AXIS_LABELS, + COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE, + CCTF_ENCODINGS, + CCTF_DECODINGS, + LCHab_to_Lab, + Lab_to_XYZ, + Luv_to_uv, + DATA_MACADAM_1942_ELLIPSES, + CCS_POINTER_GAMUT_BOUNDARY, + DATA_POINTER_GAMUT_VOLUME, + CCS_ILLUMINANT_POINTER_GAMUT, + RGB_Colourspace, + RGB_to_RGB, + RGB_to_XYZ, + UCS_to_uv, + XYZ_to_Luv, + XYZ_to_RGB, + XYZ_to_UCS, + XYZ_to_xy, + xy_to_Luv_uv, + xy_to_UCS_uv, +) from colour.plotting import ( - CONSTANTS_COLOUR_STYLE, plot_chromaticity_diagram_CIE1931, artist, - plot_chromaticity_diagram_CIE1960UCS, plot_chromaticity_diagram_CIE1976UCS, - colour_cycle, colour_style, filter_passthrough, filter_RGB_colourspaces, - filter_cmfs, plot_multi_functions, override_style, render, - update_settings_collection) + CONSTANTS_COLOUR_STYLE, + plot_chromaticity_diagram_CIE1931, + artist, + plot_chromaticity_diagram_CIE1960UCS, + plot_chromaticity_diagram_CIE1976UCS, + colour_cycle, + colour_style, + filter_passthrough, + filter_RGB_colourspaces, + filter_cmfs, + plot_multi_functions, + override_style, + render, + update_settings_collection, +) from colour.plotting.diagrams import plot_chromaticity_diagram -from colour.utilities import (as_float_array, as_int_array, domain_range_scale, - first_item, tsplit, tstack) -from colour.utilities.deprecation import handle_arguments_deprecation - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.utilities import ( + CaseInsensitiveMapping, + as_array, + as_float_array, + as_int_array, + domain_range_scale, + first_item, + optional, + tsplit, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'common_colourspace_model_axis_reorder', 'plot_pointer_gamut', - 'plot_RGB_colourspaces_in_chromaticity_diagram', - 'plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931', - 'plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS', - 'plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS', - 'plot_RGB_chromaticities_in_chromaticity_diagram', - 'plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931', - 'plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS', - 'plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS', - 'ellipses_MacAdam1942', - 'plot_ellipses_MacAdam1942_in_chromaticity_diagram', - 'plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931', - 'plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS', - 'plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS', - 'plot_single_cctf', 'plot_multi_cctfs', 'plot_constant_hue_loci' + "COLOURSPACE_MODELS_AXIS_ORDER", + "colourspace_model_axis_reorder", + "plot_pointer_gamut", + "plot_RGB_colourspaces_in_chromaticity_diagram", + "plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931", + "plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS", + "plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS", + "plot_RGB_chromaticities_in_chromaticity_diagram", + "plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931", + "plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS", + "plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS", + "ellipses_MacAdam1942", + "plot_ellipses_MacAdam1942_in_chromaticity_diagram", + "plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931", + "plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS", + "plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS", + "plot_single_cctf", + "plot_multi_cctfs", + "plot_constant_hue_loci", ] - -def common_colourspace_model_axis_reorder(a, model=None): +COLOURSPACE_MODELS_AXIS_ORDER: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "CAM02LCD": (1, 2, 0), + "CAM02SCD": (1, 2, 0), + "CAM02UCS": (1, 2, 0), + "CAM16LCD": (1, 2, 0), + "CAM16SCD": (1, 2, 0), + "CAM16UCS": (1, 2, 0), + "CIE XYZ": (0, 1, 2), + "CIE xyY": (0, 1, 2), + "CIE Lab": (1, 2, 0), + "CIE LCHab": (1, 2, 0), + "CIE Luv": (1, 2, 0), + "CIE LCHuv": (1, 2, 0), + "CIE UCS": (0, 1, 2), + "CIE UVW": (1, 2, 0), + "DIN99": (1, 2, 0), + "Hunter Lab": (1, 2, 0), + "Hunter Rdab": (1, 2, 0), + "ICaCb": (1, 2, 0), + "ICtCp": (1, 2, 0), + "IPT": (1, 2, 0), + "IgPgTg": (1, 2, 0), + "Jzazbz": (1, 2, 0), + "OSA UCS": (1, 2, 0), + "Oklab": (1, 2, 0), + "hdr-CIELAB": (1, 2, 0), + "hdr-IPT": (1, 2, 0), + } +) +"""Colourspace models axis order.""" + + +def colourspace_model_axis_reorder( + a: ArrayLike, + model: Union[ + Literal[ + "CAM02LCD", + "CAM02SCD", + "CAM02UCS", + "CAM16LCD", + "CAM16SCD", + "CAM16UCS", + "CIE XYZ", + "CIE xyY", + "CIE Lab", + "CIE LCHab", + "CIE Luv", + "CIE LCHuv", + "CIE UCS", + "CIE UVW", + "DIN99", + "Hunter Lab", + "Hunter Rdab", + "ICaCb", + "ICtCp", + "IPT", + "IgPgTg", + "Jzazbz", + "OSA UCS", + "Oklab", + "hdr-CIELAB", + "hdr-IPT", + ], + str, + ], + direction: Union[Literal["Forward", "Inverse"], str] = "Forward", +) -> NDArray: """ Reorder the axes of given colourspace model :math:`a` array according to the most common volume plotting axes order. Parameters ---------- - a : array_like + a Colourspace model :math:`a` array. - model : unicode, optional - **{'CIE XYZ', 'CIE xyY', 'CIE xy', 'CIE Lab', 'CIE LCHab', 'CIE Luv', - 'CIE Luv uv', 'CIE LCHuv', 'CIE UCS', 'CIE UCS uv', 'CIE UVW', - 'DIN 99', 'Hunter Lab', 'Hunter Rdab', 'IPT', 'JzAzBz', 'OSA UCS', - 'hdr-CIELAB', 'hdr-IPT'}**, - Colourspace model. + model + Colourspace model, see :attr:`colour.COLOURSPACE_MODELS` attribute for + the list of supported colourspace models. + direction + Reordering direction. Returns ------- - ndarray + :class:`numpy.ndarray` Reordered colourspace model :math:`a` array. Examples -------- >>> a = np.array([0, 1, 2]) - >>> common_colourspace_model_axis_reorder(a) - array([0, 1, 2]) - >>> common_colourspace_model_axis_reorder(a, 'CIE Lab') - array([ 1., 2., 0.]) - >>> common_colourspace_model_axis_reorder(a, 'CIE LCHab') - array([ 1., 2., 0.]) - >>> common_colourspace_model_axis_reorder(a, 'CIE Luv') - array([ 1., 2., 0.]) - >>> common_colourspace_model_axis_reorder(a, 'CIE LCHab') - array([ 1., 2., 0.]) - >>> common_colourspace_model_axis_reorder(a, 'DIN 99') + >>> colourspace_model_axis_reorder(a, 'CIE Lab') array([ 1., 2., 0.]) - >>> common_colourspace_model_axis_reorder(a, 'Hunter Lab') + >>> colourspace_model_axis_reorder(a, 'IPT') array([ 1., 2., 0.]) - >>> common_colourspace_model_axis_reorder(a, 'Hunter Rdab') - array([ 1., 2., 0.]) - >>> common_colourspace_model_axis_reorder(a, 'IPT') - array([ 1., 2., 0.]) - >>> common_colourspace_model_axis_reorder(a, 'JzAzBz') - array([ 1., 2., 0.]) - >>> common_colourspace_model_axis_reorder(a, 'OSA UCS') - array([ 1., 2., 0.]) - >>> common_colourspace_model_axis_reorder(a, 'hdr-CIELAB') - array([ 1., 2., 0.]) - >>> common_colourspace_model_axis_reorder(a, 'hdr-IPT') + >>> colourspace_model_axis_reorder(a, 'OSA UCS') array([ 1., 2., 0.]) + >>> b = np.array([1, 2, 0]) + >>> colourspace_model_axis_reorder(b, 'OSA UCS', 'Inverse') + array([ 0., 1., 2.]) """ - if model in ('CIE Lab', 'CIE LCHab', 'CIE Luv', 'CIE LCHuv', 'DIN 99', - 'Hunter Lab', 'Hunter Rdab', 'IPT', 'JzAzBz', 'OSA UCS', - 'hdr-CIELAB', 'hdr-IPT'): - i, j, k = tsplit(a) - a = tstack([j, k, i]) + a = as_float_array(a) - return a + model = validate_method( + model, + list(COLOURSPACE_MODELS_AXIS_ORDER.keys()), + '"{0}" model is invalid, it must be one of {1}!', + ) + + direction = validate_method( + direction, + ["Forward", "Inverse"], + '"{0}" direction is invalid, it must be one of {1}!', + ) + + order = COLOURSPACE_MODELS_AXIS_ORDER.get(model, (0, 1, 2)) + + if direction == "forward": + indexes = (order[0], order[1], order[2]) + else: + indexes = (order.index(0), order.index(1), order.index(2)) + + return a[..., indexes] @override_style() -def plot_pointer_gamut(method='CIE 1931', **kwargs): +def plot_pointer_gamut( + pointer_gamut_colours: Optional[Union[ArrayLike, str]] = None, + pointer_gamut_opacity: Floating = 1, + method: Union[ + Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"], str + ] = "CIE 1931", + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots *Pointer's Gamut* according to given method. + Plot *Pointer's Gamut* according to given method. Parameters ---------- - method : unicode, optional - **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**, + pointer_gamut_colours + Colours of the *Pointer's Gamut*. + pointer_gamut_opacity + Opacity of the *Pointer's Gamut*. + method Plotting method. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -194,94 +320,107 @@ def plot_pointer_gamut(method='CIE 1931', **kwargs): :alt: plot_pointer_gamut """ - settings = {'uniform': True} + method = validate_method( + method, ["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] + ) + + pointer_gamut_colours = optional( + pointer_gamut_colours, CONSTANTS_COLOUR_STYLE.colour.dark + ) + pointer_gamut_opacity = optional( + pointer_gamut_opacity, CONSTANTS_COLOUR_STYLE.opacity.high + ) + + settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) - method = method.upper() - - if method == 'CIE 1931': + if method == "cie 1931": - def XYZ_to_ij(XYZ, *args): + def XYZ_to_ij(XYZ: NDArray, *args: Any) -> NDArray: """ - Converts given *CIE XYZ* tristimulus values to *ij* chromaticity + Convert given *CIE XYZ* tristimulus values to *ij* chromaticity coordinates. """ return XYZ_to_xy(XYZ, *args) - def xy_to_ij(xy): + def xy_to_ij(xy: NDArray) -> NDArray: """ - Converts given *CIE xy* chromaticity coordinates to *ij* + Convert given *CIE xy* chromaticity coordinates to *ij* chromaticity coordinates. """ return xy - elif method == 'CIE 1960 UCS': + elif method == "cie 1960 ucs": - def XYZ_to_ij(XYZ, *args): + def XYZ_to_ij(XYZ: NDArray, *args: Any) -> NDArray: """ - Converts given *CIE XYZ* tristimulus values to *ij* chromaticity + Convert given *CIE XYZ* tristimulus values to *ij* chromaticity coordinates. """ return UCS_to_uv(XYZ_to_UCS(XYZ)) - def xy_to_ij(xy): + def xy_to_ij(xy: NDArray) -> NDArray: """ - Converts given *CIE xy* chromaticity coordinates to *ij* + Convert given *CIE xy* chromaticity coordinates to *ij* chromaticity coordinates. """ return xy_to_UCS_uv(xy) - elif method == 'CIE 1976 UCS': + elif method == "cie 1976 ucs": - def XYZ_to_ij(XYZ, *args): + def XYZ_to_ij(XYZ: NDArray, *args: Any) -> NDArray: """ - Converts given *CIE XYZ* tristimulus values to *ij* chromaticity + Convert given *CIE XYZ* tristimulus values to *ij* chromaticity coordinates. """ return Luv_to_uv(XYZ_to_Luv(XYZ, *args), *args) - def xy_to_ij(xy): + def xy_to_ij(xy: NDArray) -> NDArray: """ - Converts given *CIE xy* chromaticity coordinates to *ij* + Convert given *CIE xy* chromaticity coordinates to *ij* chromaticity coordinates. """ return xy_to_Luv_uv(xy) - else: - raise ValueError( - 'Invalid method: "{0}", must be one of ' - '[\'CIE 1931\', \'CIE 1960 UCS\', \'CIE 1976 UCS\']'.format( - method)) - ij = xy_to_ij(as_float_array(CCS_POINTER_GAMUT_BOUNDARY)) - alpha_p = CONSTANTS_COLOUR_STYLE.opacity.high - colour_p = CONSTANTS_COLOUR_STYLE.colour.darkest axes.plot( ij[..., 0], ij[..., 1], - label='Pointer\'s Gamut', - color=colour_p, - alpha=alpha_p) + label="Pointer's Gamut", + color=pointer_gamut_colours, + alpha=pointer_gamut_opacity, + zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_line, + ) axes.plot( - (ij[-1][0], ij[0][0]), (ij[-1][1], ij[0][1]), - color=colour_p, - alpha=alpha_p) + (ij[-1][0], ij[0][0]), + (ij[-1][1], ij[0][1]), + color=pointer_gamut_colours, + alpha=pointer_gamut_opacity, + zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_line, + ) XYZ = Lab_to_XYZ( - LCHab_to_Lab(DATA_POINTER_GAMUT_VOLUME), CCS_ILLUMINANT_POINTER_GAMUT) + LCHab_to_Lab(DATA_POINTER_GAMUT_VOLUME), CCS_ILLUMINANT_POINTER_GAMUT + ) ij = XYZ_to_ij(XYZ, CCS_ILLUMINANT_POINTER_GAMUT) - axes.scatter( - ij[..., 0], ij[..., 1], alpha=alpha_p / 2, color=colour_p, marker='+') - settings.update({'axes': axes}) + scatter_settings = { + "alpha": pointer_gamut_opacity / 2, + "color": pointer_gamut_colours, + "marker": "+", + "zorder": CONSTANTS_COLOUR_STYLE.zorder.foreground_scatter, + } + axes.scatter(ij[..., 0], ij[..., 1], **scatter_settings) + + settings.update({"axes": axes}) settings.update(kwargs) return render(**settings) @@ -289,60 +428,71 @@ def xy_to_ij(xy): @override_style() def plot_RGB_colourspaces_in_chromaticity_diagram( - colourspaces, - cmfs='CIE 1931 2 Degree Standard Observer', - chromaticity_diagram_callable=plot_chromaticity_diagram, - method='CIE 1931', - show_whitepoints=True, - show_pointer_gamut=False, - chromatically_adapt=False, - plot_kwargs=None, - **kwargs): + colourspaces: Union[ + RGB_Colourspace, str, Sequence[Union[RGB_Colourspace, str]] + ], + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + chromaticity_diagram_callable: Callable = ( + plot_chromaticity_diagram # type: ignore[has-type] + ), + method: Union[ + Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"], str + ] = "CIE 1931", + show_whitepoints: Boolean = True, + show_pointer_gamut: Boolean = False, + chromatically_adapt: Boolean = False, + plot_kwargs: Optional[Union[Dict, List[Dict]]] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given *RGB* colourspaces in the *Chromaticity Diagram* according + Plot given *RGB* colourspaces in the *Chromaticity Diagram* according to given method. Parameters ---------- - colourspaces : unicode or RGB_Colourspace or array_like + colourspaces *RGB* colourspaces to plot. ``colourspaces`` elements can be of any type or form supported by the :func:`colour.plotting.filter_RGB_colourspaces` definition. - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - chromaticity_diagram_callable : callable, optional + chromaticity_diagram_callable Callable responsible for drawing the *Chromaticity Diagram*. - method : unicode, optional - **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**, + method *Chromaticity Diagram* method. - show_whitepoints : bool, optional + show_whitepoints Whether to display the *RGB* colourspaces whitepoints. - show_pointer_gamut : bool, optional + show_pointer_gamut Whether to display the *Pointer's Gamut*. - chromatically_adapt : bool, optional + chromatically_adapt Whether to chromatically adapt the *RGB* colourspaces given in ``colourspaces`` to the whitepoint of the default plotting colourspace. - plot_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.plot` definition, used to control - the style of the plotted *RGB* colourspaces. ``plot_kwargs`` can be - either a single dictionary applied to all the plotted *RGB* - colourspaces with same settings or a sequence of dictionaries with - different settings for each plotted *RGB* colourspace. + plot_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.plot` definition, + used to control the style of the plotted *RGB* colourspaces. + ``plot_kwargs`` can be either a single dictionary applied to all the + plotted *RGB* colourspaces with the same settings or a sequence of + dictionaries with different settings for each plotted *RGB* + colourspace. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, - :func:`colour.plotting.plot_pointer_gamut`, + :func:`colour.plotting.models.plot_pointer_gamut`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -363,39 +513,47 @@ def plot_RGB_colourspaces_in_chromaticity_diagram( :alt: plot_RGB_colourspaces_in_chromaticity_diagram """ - colourspaces = filter_RGB_colourspaces(colourspaces).values() + method = validate_method( + method, ["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] + ) - settings = {'uniform': True} + colourspaces = cast( + List[RGB_Colourspace], + list(filter_RGB_colourspaces(colourspaces).values()), + ) + + settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) - method = method.upper() - - cmfs = first_item(filter_cmfs(cmfs).values()) + cmfs = cast( + MultiSpectralDistributions, first_item(filter_cmfs(cmfs).values()) + ) - title = '{0}\n{1} - {2} Chromaticity Diagram'.format( - ', '.join([colourspace.name for colourspace in colourspaces]), - cmfs.name, method) + title = ( + f"{', '.join([colourspace.name for colourspace in colourspaces])}\n" + f"{cmfs.name} - {method.upper()} Chromaticity Diagram" + ) - settings = {'axes': axes, 'title': title, 'method': method} + settings = {"axes": axes, "title": title, "method": method} settings.update(kwargs) - settings['standalone'] = False + settings["standalone"] = False chromaticity_diagram_callable(**settings) if show_pointer_gamut: - settings = {'axes': axes, 'method': method} + settings = {"axes": axes, "method": method} settings.update(kwargs) - settings['standalone'] = False + settings["standalone"] = False plot_pointer_gamut(**settings) - if method == 'CIE 1931': + if method == "cie 1931": - def xy_to_ij(xy): + def xy_to_ij(xy: NDArray) -> NDArray: """ - Converts given *CIE xy* chromaticity coordinates to *ij* + Convert given *CIE xy* chromaticity coordinates to *ij* chromaticity coordinates. """ @@ -403,11 +561,12 @@ def xy_to_ij(xy): x_limit_min, x_limit_max = [-0.1], [0.9] y_limit_min, y_limit_max = [-0.1], [0.9] - elif method == 'CIE 1960 UCS': - def xy_to_ij(xy): + elif method == "cie 1960 ucs": + + def xy_to_ij(xy: NDArray) -> NDArray: """ - Converts given *CIE xy* chromaticity coordinates to *ij* + Convert given *CIE xy* chromaticity coordinates to *ij* chromaticity coordinates. """ @@ -416,11 +575,11 @@ def xy_to_ij(xy): x_limit_min, x_limit_max = [-0.1], [0.7] y_limit_min, y_limit_max = [-0.2], [0.6] - elif method == 'CIE 1976 UCS': + elif method == "cie 1976 ucs": - def xy_to_ij(xy): + def xy_to_ij(xy: NDArray) -> NDArray: """ - Converts given *CIE xy* chromaticity coordinates to *ij* + Convert given *CIE xy* chromaticity coordinates to *ij* chromaticity coordinates. """ @@ -428,37 +587,39 @@ def xy_to_ij(xy): x_limit_min, x_limit_max = [-0.1], [0.7] y_limit_min, y_limit_max = [-0.1], [0.7] - else: - raise ValueError( - 'Invalid method: "{0}", must be one of ' - '[\'CIE 1931\', \'CIE 1960 UCS\', \'CIE 1976 UCS\']'.format( - method)) - settings = {'colour_cycle_count': len(colourspaces)} + settings = {"colour_cycle_count": len(colourspaces)} settings.update(kwargs) cycle = colour_cycle(**settings) plotting_colourspace = CONSTANTS_COLOUR_STYLE.colour.colourspace - plot_settings_collection = [{ - 'label': '{0}'.format(colourspace.name), - 'marker': 'o', - 'color': next(cycle)[:3] - } for colourspace in colourspaces] + plot_settings_collection = [ + { + "label": f"{colourspace.name}", + "marker": "o", + "color": next(cycle)[:3], + "zorder": CONSTANTS_COLOUR_STYLE.zorder.foreground_line, + } + for colourspace in colourspaces + ] if plot_kwargs is not None: - update_settings_collection(plot_settings_collection, plot_kwargs, - len(colourspaces)) + update_settings_collection( + plot_settings_collection, plot_kwargs, len(colourspaces) + ) for i, colourspace in enumerate(colourspaces): plot_settings = plot_settings_collection[i] if chromatically_adapt and not np.array_equal( - colourspace.whitepoint, plotting_colourspace.whitepoint): + colourspace.whitepoint, plotting_colourspace.whitepoint + ): colourspace = colourspace.chromatically_adapt( plotting_colourspace.whitepoint, - plotting_colourspace.whitepoint_name) + plotting_colourspace.whitepoint_name, + ) # RGB colourspaces such as *ACES2065-1* have primaries with # chromaticity coordinates set to 0 thus we prevent nan from being @@ -475,8 +636,8 @@ def xy_to_ij(xy): axes.plot(P_p[..., 0], P_p[..., 1], **plot_settings) if show_whitepoints: - plot_settings['marker'] = 'o' - plot_settings.pop('label') + plot_settings["marker"] = "o" + plot_settings.pop("label") W_p = np.vstack([W, W]) axes.plot(W_p[..., 0], W_p[..., 1], **plot_settings) @@ -493,11 +654,13 @@ def xy_to_ij(xy): max(y_limit_max), ) - settings.update({ - 'standalone': True, - 'legend': True, - 'bounding_box': bounding_box, - }) + settings.update( + { + "standalone": True, + "legend": True, + "bounding_box": bounding_box, + } + ) settings.update(kwargs) return render(**settings) @@ -505,55 +668,67 @@ def xy_to_ij(xy): @override_style() def plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931( - colourspaces, - cmfs='CIE 1931 2 Degree Standard Observer', - chromaticity_diagram_callable_CIE1931=( - plot_chromaticity_diagram_CIE1931), - show_whitepoints=True, - show_pointer_gamut=False, - chromatically_adapt=False, - plot_kwargs=None, - **kwargs): + colourspaces: Union[ + RGB_Colourspace, str, Sequence[Union[RGB_Colourspace, str]] + ], + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + chromaticity_diagram_callable_CIE1931: Callable = ( + plot_chromaticity_diagram_CIE1931 # type: ignore[has-type] + ), + show_whitepoints: Boolean = True, + show_pointer_gamut: Boolean = False, + chromatically_adapt: Boolean = False, + plot_kwargs: Optional[Union[Dict, List[Dict]]] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given *RGB* colourspaces in the *CIE 1931 Chromaticity Diagram*. + Plot given *RGB* colourspaces in the *CIE 1931 Chromaticity Diagram*. Parameters ---------- - colourspaces : unicode or RGB_Colourspace or array_like + colourspaces *RGB* colourspaces to plot. ``colourspaces`` elements can be of any type or form supported by the :func:`colour.plotting.filter_RGB_colourspaces` definition. - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - chromaticity_diagram_callable_CIE1931 : callable, optional + chromaticity_diagram_callable_CIE1931 Callable responsible for drawing the *CIE 1931 Chromaticity Diagram*. - show_whitepoints : bool, optional + show_whitepoints Whether to display the *RGB* colourspaces whitepoints. - show_pointer_gamut : bool, optional + show_pointer_gamut Whether to display the *Pointer's Gamut*. - chromatically_adapt : bool, optional + chromatically_adapt Whether to chromatically adapt the *RGB* colourspaces given in ``colourspaces`` to the whitepoint of the default plotting colourspace. - plot_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.plot` definition, used to control - the style of the plotted *RGB* colourspaces. ``plot_kwargs`` can be - either a single dictionary applied to all the plotted *RGB* - colourspaces with same settings or a sequence of dictionaries with - different settings for each plotted *RGB* colourspace. + plot_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.plot` definition, + used to control the style of the plotted *RGB* colourspaces. + ``plot_kwargs`` can be either a single dictionary applied to all the + plotted *RGB* colourspaces with the same settings or a sequence of + dictionaries with different settings for each plotted *RGB* + colourspace. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, + :func:`colour.plotting.models.plot_pointer_gamut`, + :func:`colour.plotting.models.\ +plot_RGB_colourspaces_in_chromaticity_diagram`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -570,7 +745,7 @@ def plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931( """ settings = dict(kwargs) - settings.update({'method': 'CIE 1931'}) + settings.update({"method": "CIE 1931"}) return plot_RGB_colourspaces_in_chromaticity_diagram( colourspaces, @@ -580,61 +755,74 @@ def plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931( show_pointer_gamut=show_pointer_gamut, chromatically_adapt=chromatically_adapt, plot_kwargs=plot_kwargs, - **settings) + **settings, + ) @override_style() def plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS( - colourspaces, - cmfs='CIE 1931 2 Degree Standard Observer', - chromaticity_diagram_callable_CIE1960UCS=( - plot_chromaticity_diagram_CIE1960UCS), - show_whitepoints=True, - show_pointer_gamut=False, - chromatically_adapt=False, - plot_kwargs=None, - **kwargs): + colourspaces: Union[ + RGB_Colourspace, str, Sequence[Union[RGB_Colourspace, str]] + ], + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + chromaticity_diagram_callable_CIE1960UCS: Callable = ( + plot_chromaticity_diagram_CIE1960UCS # type: ignore[has-type] + ), + show_whitepoints: Boolean = True, + show_pointer_gamut: Boolean = False, + chromatically_adapt: Boolean = False, + plot_kwargs: Optional[Union[Dict, List[Dict]]] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given *RGB* colourspaces in the *CIE 1960 UCS Chromaticity Diagram*. + Plot given *RGB* colourspaces in the *CIE 1960 UCS Chromaticity Diagram*. Parameters ---------- - colourspaces : unicode or RGB_Colourspace or array_like + colourspaces *RGB* colourspaces to plot. ``colourspaces`` elements can be of any type or form supported by the :func:`colour.plotting.filter_RGB_colourspaces` definition. - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - chromaticity_diagram_callable_CIE1960UCS : callable, optional + chromaticity_diagram_callable_CIE1960UCS Callable responsible for drawing the *CIE 1960 UCS Chromaticity Diagram*. - show_whitepoints : bool, optional + show_whitepoints Whether to display the *RGB* colourspaces whitepoints. - show_pointer_gamut : bool, optional + show_pointer_gamut Whether to display the *Pointer's Gamut*. - chromatically_adapt : bool, optional + chromatically_adapt Whether to chromatically adapt the *RGB* colourspaces given in ``colourspaces`` to the whitepoint of the default plotting colourspace. - plot_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.plot` definition, used to control - the style of the plotted *RGB* colourspaces. ``plot_kwargs`` can be - either a single dictionary applied to all the plotted *RGB* - colourspaces with same settings or a sequence of dictionaries with - different settings for each plotted *RGB* colourspace. + plot_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.plot` definition, + used to control the style of the plotted *RGB* colourspaces. + ``plot_kwargs`` can be either a single dictionary applied to all the + plotted *RGB* colourspaces with the same settings or a sequence of + dictionaries with different settings for each plotted *RGB* + colourspace. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, + :func:`colour.plotting.models.plot_pointer_gamut`, + :func:`colour.plotting.models.\ +plot_RGB_colourspaces_in_chromaticity_diagram`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -651,7 +839,7 @@ def plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS( """ settings = dict(kwargs) - settings.update({'method': 'CIE 1960 UCS'}) + settings.update({"method": "CIE 1960 UCS"}) return plot_RGB_colourspaces_in_chromaticity_diagram( colourspaces, @@ -661,61 +849,74 @@ def plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS( show_pointer_gamut=show_pointer_gamut, chromatically_adapt=chromatically_adapt, plot_kwargs=plot_kwargs, - **settings) + **settings, + ) @override_style() def plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS( - colourspaces, - cmfs='CIE 1931 2 Degree Standard Observer', - chromaticity_diagram_callable_CIE1976UCS=( - plot_chromaticity_diagram_CIE1976UCS), - show_whitepoints=True, - show_pointer_gamut=False, - chromatically_adapt=False, - plot_kwargs=None, - **kwargs): + colourspaces: Union[ + RGB_Colourspace, str, Sequence[Union[RGB_Colourspace, str]] + ], + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + chromaticity_diagram_callable_CIE1976UCS: Callable = ( + plot_chromaticity_diagram_CIE1976UCS # type: ignore[has-type] + ), + show_whitepoints: Boolean = True, + show_pointer_gamut: Boolean = False, + chromatically_adapt: Boolean = False, + plot_kwargs: Optional[Union[Dict, List[Dict]]] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given *RGB* colourspaces in the *CIE 1976 UCS Chromaticity Diagram*. + Plot given *RGB* colourspaces in the *CIE 1976 UCS Chromaticity Diagram*. Parameters ---------- - colourspaces : unicode or RGB_Colourspace or array_like + colourspaces *RGB* colourspaces to plot. ``colourspaces`` elements can be of any type or form supported by the :func:`colour.plotting.filter_RGB_colourspaces` definition. - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - chromaticity_diagram_callable_CIE1976UCS : callable, optional + chromaticity_diagram_callable_CIE1976UCS Callable responsible for drawing the *CIE 1976 UCS Chromaticity Diagram*. - show_whitepoints : bool, optional + show_whitepoints Whether to display the *RGB* colourspaces whitepoints. - show_pointer_gamut : bool, optional + show_pointer_gamut Whether to display the *Pointer's Gamut*. - chromatically_adapt : bool, optional + chromatically_adapt Whether to chromatically adapt the *RGB* colourspaces given in ``colourspaces`` to the whitepoint of the default plotting colourspace. - plot_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.plot` definition, used to control - the style of the plotted *RGB* colourspaces. ``plot_kwargs`` can be - either a single dictionary applied to all the plotted *RGB* - colourspaces with same settings or a sequence of dictionaries with - different settings for each plotted *RGB* colourspace. + plot_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.plot` definition, + used to control the style of the plotted *RGB* colourspaces. + ``plot_kwargs`` can be either a single dictionary applied to all the + plotted *RGB* colourspaces with the same settings or a sequence of + dictionaries with different settings for each plotted *RGB* + colourspace. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, + :func:`colour.plotting.models.plot_pointer_gamut`, + :func:`colour.plotting.models.\ +plot_RGB_colourspaces_in_chromaticity_diagram`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -732,7 +933,7 @@ def plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS( """ settings = dict(kwargs) - settings.update({'method': 'CIE 1976 UCS'}) + settings.update({"method": "CIE 1976 UCS"}) return plot_RGB_colourspaces_in_chromaticity_diagram( colourspaces, @@ -742,57 +943,61 @@ def plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS( show_pointer_gamut=show_pointer_gamut, chromatically_adapt=chromatically_adapt, plot_kwargs=plot_kwargs, - **settings) + **settings, + ) @override_style() def plot_RGB_chromaticities_in_chromaticity_diagram( - RGB, - colourspace='sRGB', - chromaticity_diagram_callable=( - plot_RGB_colourspaces_in_chromaticity_diagram), - method='CIE 1931', - scatter_kwargs=None, - **kwargs): + RGB: ArrayLike, + colourspace: Union[ + RGB_Colourspace, str, Sequence[Union[RGB_Colourspace, str]] + ] = "sRGB", + chromaticity_diagram_callable: Callable = ( + plot_RGB_colourspaces_in_chromaticity_diagram + ), + method: Union[ + Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"], str + ] = "CIE 1931", + scatter_kwargs: Optional[Dict] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given *RGB* colourspace array in the *Chromaticity Diagram* according + Plot given *RGB* colourspace array in the *Chromaticity Diagram* according to given method. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array. - colourspace : unicode or RGB_Colourspace, optional + colourspace *RGB* colourspace of the *RGB* array. ``colourspace`` can be of any type or form supported by the :func:`colour.plotting.filter_RGB_colourspaces` definition. - chromaticity_diagram_callable : callable, optional + chromaticity_diagram_callable Callable responsible for drawing the *Chromaticity Diagram*. - method : unicode, optional - **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**, + method *Chromaticity Diagram* method. - scatter_kwargs : dict, optional - Keyword arguments for the :func:`plt.scatter` definition. The following - special keyword arguments can also be used: - - - *c* : unicode or array_like, if ``c`` is set to *RGB*, the scatter - will use the colours as given by the ``RGB`` argument. + scatter_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.scatter` definition. + The following special keyword arguments can also be used: + - ``c`` : If ``c`` is set to *RGB*, the scatter will use the colours + as given by the ``RGB`` argument. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, - :func:`colour.plotting.diagrams.\ + :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - Also handles keywords arguments for deprecation management. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -809,62 +1014,78 @@ def plot_RGB_chromaticities_in_chromaticity_diagram( :alt: plot_RGB_chromaticities_in_chromaticity_diagram """ - scatter_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['scatter_parameters', 'scatter_kwargs']], - }, **kwargs).get('scatter_kwargs', scatter_kwargs) - - RGB = as_float_array(RGB).reshape(-1, 3) + RGB = np.reshape(as_float_array(RGB), (-1, 3)) + method = validate_method( + method, ["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] + ) - settings = {'uniform': True} + settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) - method = method.upper() - scatter_settings = { - 's': 40, - 'c': 'RGB', - 'marker': 'o', - 'alpha': 0.85, + "s": 40, + "c": "RGB", + "marker": "o", + "alpha": 0.85, + "zorder": CONSTANTS_COLOUR_STYLE.zorder.midground_scatter, } if scatter_kwargs is not None: scatter_settings.update(scatter_kwargs) settings = dict(kwargs) - settings.update({'axes': axes, 'standalone': False}) + settings.update({"axes": axes, "standalone": False}) - colourspace = first_item(filter_RGB_colourspaces(colourspace).values()) - settings['colourspaces'] = ( - ['^{0}$'.format(colourspace.name)] + settings.get('colourspaces', [])) + colourspace = cast( + RGB_Colourspace, + first_item(filter_RGB_colourspaces(colourspace).values()), + ) + + settings["colourspaces"] = [f"^{colourspace.name}$"] + settings.get( + "colourspaces", [] + ) chromaticity_diagram_callable(**settings) - use_RGB_colours = scatter_settings['c'].upper() == 'RGB' + use_RGB_colours = str(scatter_settings["c"]).upper() == "RGB" if use_RGB_colours: RGB = RGB[RGB[:, 1].argsort()] - scatter_settings['c'] = np.clip( - RGB_to_RGB( - RGB, - colourspace, - CONSTANTS_COLOUR_STYLE.colour.colourspace, - apply_cctf_encoding=True).reshape(-1, 3), 0, 1) + scatter_settings["c"] = np.clip( + np.reshape( + RGB_to_RGB( + RGB, + colourspace, + CONSTANTS_COLOUR_STYLE.colour.colourspace, + apply_cctf_encoding=True, + ), + (-1, 3), + ), + 0, + 1, + ) - XYZ = RGB_to_XYZ(RGB, colourspace.whitepoint, colourspace.whitepoint, - colourspace.matrix_RGB_to_XYZ) + XYZ = RGB_to_XYZ( + RGB, + colourspace.whitepoint, + colourspace.whitepoint, + colourspace.matrix_RGB_to_XYZ, + ) - if method == 'CIE 1931': + if method == "cie 1931": ij = XYZ_to_xy(XYZ, colourspace.whitepoint) - elif method == 'CIE 1960 UCS': + + elif method == "cie 1960 ucs": ij = UCS_to_uv(XYZ_to_UCS(XYZ)) - elif method == 'CIE 1976 UCS': + elif method == "cie 1976 ucs": ij = Luv_to_uv( - XYZ_to_Luv(XYZ, colourspace.whitepoint), colourspace.whitepoint) + XYZ_to_Luv(XYZ, colourspace.whitepoint), colourspace.whitepoint + ) axes.scatter(ij[..., 0], ij[..., 1], **scatter_settings) - settings.update({'standalone': True}) + settings.update({"standalone": True}) settings.update(kwargs) return render(**settings) @@ -872,46 +1093,49 @@ def plot_RGB_chromaticities_in_chromaticity_diagram( @override_style() def plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931( - RGB, - colourspace='sRGB', - chromaticity_diagram_callable_CIE1931=( - plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931), - scatter_kwargs=None, - **kwargs): + RGB: ArrayLike, + colourspace: Union[ + RGB_Colourspace, str, Sequence[Union[RGB_Colourspace, str]] + ] = "sRGB", + chromaticity_diagram_callable_CIE1931: Callable = ( + plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931 + ), + scatter_kwargs: Optional[Dict] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given *RGB* colourspace array in the *CIE 1931 Chromaticity Diagram*. + Plot given *RGB* colourspace array in the *CIE 1931 Chromaticity Diagram*. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array. - colourspace : unicode or RGB_Colourspace, optional + colourspace *RGB* colourspace of the *RGB* array. ``colourspace`` can be of any type or form supported by the :func:`colour.plotting.filter_RGB_colourspaces` definition. - chromaticity_diagram_callable_CIE1931 : callable, optional + chromaticity_diagram_callable_CIE1931 Callable responsible for drawing the *CIE 1931 Chromaticity Diagram*. - scatter_kwargs : dict, optional - Keyword arguments for the :func:`plt.scatter` definition. The following - special keyword arguments can also be used: + scatter_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.scatter` definition. + The following special keyword arguments can also be used: - - *c* : unicode or array_like, if ``c`` is set to *RGB*, the scatter - will use the colours as given by the ``RGB`` argument. + - ``c`` : If ``c`` is set to *RGB*, the scatter will use the colours + as given by the ``RGB`` argument. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, - :func:`colour.plotting.diagrams.\ + :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - Also handles keywords arguments for deprecation management. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -928,65 +1152,65 @@ def plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931( :alt: plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931 """ - scatter_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['scatter_parameters', 'scatter_kwargs']], - }, **kwargs).get('scatter_kwargs', scatter_kwargs) - settings = dict(kwargs) - settings.update({'method': 'CIE 1931'}) + settings.update({"method": "CIE 1931"}) return plot_RGB_chromaticities_in_chromaticity_diagram( RGB, colourspace, chromaticity_diagram_callable_CIE1931, scatter_kwargs=scatter_kwargs, - **settings) + **settings, + ) @override_style() def plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS( - RGB, - colourspace='sRGB', - chromaticity_diagram_callable_CIE1960UCS=( - plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS), - scatter_kwargs=None, - **kwargs): + RGB: ArrayLike, + colourspace: Union[ + RGB_Colourspace, str, Sequence[Union[RGB_Colourspace, str]] + ] = "sRGB", + chromaticity_diagram_callable_CIE1960UCS: Callable = ( + plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS + ), + scatter_kwargs: Optional[Dict] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given *RGB* colourspace array in the + Plot given *RGB* colourspace array in the *CIE 1960 UCS Chromaticity Diagram*. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array. - colourspace : unicode or RGB_Colourspace, optional + colourspace *RGB* colourspace of the *RGB* array. ``colourspace`` can be of any type or form supported by the :func:`colour.plotting.filter_RGB_colourspaces` definition. - chromaticity_diagram_callable_CIE1960UCS : callable, optional + chromaticity_diagram_callable_CIE1960UCS Callable responsible for drawing the *CIE 1960 UCS Chromaticity Diagram*. - scatter_kwargs : dict, optional - Keyword arguments for the :func:`plt.scatter` definition. The following - special keyword arguments can also be used: + scatter_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.scatter` definition. + The following special keyword arguments can also be used: - - *c* : unicode or array_like, if ``c`` is set to *RGB*, the scatter - will use the colours as given by the ``RGB`` argument. + - ``c`` : If ``c`` is set to *RGB*, the scatter will use the colours + as given by the ``RGB`` argument. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, - :func:`colour.plotting.diagrams.\ + :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - Also handles keywords arguments for deprecation management. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -1003,65 +1227,65 @@ def plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS( :alt: plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS """ - scatter_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['scatter_parameters', 'scatter_kwargs']], - }, **kwargs).get('scatter_kwargs', scatter_kwargs) - settings = dict(kwargs) - settings.update({'method': 'CIE 1960 UCS'}) + settings.update({"method": "CIE 1960 UCS"}) return plot_RGB_chromaticities_in_chromaticity_diagram( RGB, colourspace, chromaticity_diagram_callable_CIE1960UCS, scatter_kwargs=scatter_kwargs, - **settings) + **settings, + ) @override_style() def plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS( - RGB, - colourspace='sRGB', - chromaticity_diagram_callable_CIE1976UCS=( - plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS), - scatter_kwargs=None, - **kwargs): + RGB: ArrayLike, + colourspace: Union[ + RGB_Colourspace, str, Sequence[Union[RGB_Colourspace, str]] + ] = "sRGB", + chromaticity_diagram_callable_CIE1976UCS: Callable = ( + plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS + ), + scatter_kwargs: Optional[Dict] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given *RGB* colourspace array in the + Plot given *RGB* colourspace array in the *CIE 1976 UCS Chromaticity Diagram*. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array. - colourspace : unicode or RGB_Colourspace, optional + colourspace *RGB* colourspace of the *RGB* array. ``colourspace`` can be of any type or form supported by the :func:`colour.plotting.filter_RGB_colourspaces` definition. - chromaticity_diagram_callable_CIE1976UCS : callable, optional + chromaticity_diagram_callable_CIE1976UCS Callable responsible for drawing the *CIE 1976 UCS Chromaticity Diagram*. - scatter_kwargs : dict, optional - Keyword arguments for the :func:`plt.scatter` definition. The following - special keyword arguments can also be used: + scatter_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.scatter` definition. + The following special keyword arguments can also be used: - - *c* : unicode or array_like, if ``c`` is set to *RGB*, the scatter - will use the colours as given by the ``RGB`` argument. + - ``c`` : If ``c`` is set to *RGB*, the scatter will use the colours + as given by the ``RGB`` argument. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, - :func:`colour.plotting.diagrams.\ + :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - Also handles keywords arguments for deprecation management. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -1078,36 +1302,36 @@ def plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS( :alt: plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS """ - scatter_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['scatter_parameters', 'scatter_kwargs']], - }, **kwargs).get('scatter_kwargs', scatter_kwargs) - settings = dict(kwargs) - settings.update({'method': 'CIE 1976 UCS'}) + settings.update({"method": "CIE 1976 UCS"}) return plot_RGB_chromaticities_in_chromaticity_diagram( RGB, colourspace, chromaticity_diagram_callable_CIE1976UCS, scatter_kwargs=scatter_kwargs, - **settings) + **settings, + ) -def ellipses_MacAdam1942(method='CIE 1931'): +def ellipses_MacAdam1942( + method: Union[ + Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"], str + ] = "CIE 1931" +) -> List[NDArray]: """ - Returns *MacAdam (1942) Ellipses (Observer PGN)* coefficients according to + Return *MacAdam (1942) Ellipses (Observer PGN)* coefficients according to given method. Parameters ---------- - method : unicode, optional - **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**, + method Computation method. Returns ------- - tuple - Current figure and axes. + :class:`list` + *MacAdam (1942) Ellipses (Observer PGN)* coefficients. Examples -------- @@ -1116,44 +1340,40 @@ def ellipses_MacAdam1942(method='CIE 1931'): 1.56666660e-02, -2.77000015e+01]) """ - method = method.upper() + method = validate_method( + method, ["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] + ) - if method == 'CIE 1931': + if method == "cie 1931": - def xy_to_ij(xy): + def xy_to_ij(xy: NDArray) -> NDArray: """ - Converts given *CIE xy* chromaticity coordinates to *ij* + Convert given *CIE xy* chromaticity coordinates to *ij* chromaticity coordinates. """ return xy - elif method == 'CIE 1960 UCS': + elif method == "cie 1960 ucs": - def xy_to_ij(xy): + def xy_to_ij(xy: NDArray) -> NDArray: """ - Converts given *CIE xy* chromaticity coordinates to *ij* + Convert given *CIE xy* chromaticity coordinates to *ij* chromaticity coordinates. """ return xy_to_UCS_uv(xy) - elif method == 'CIE 1976 UCS': + elif method == "cie 1976 ucs": - def xy_to_ij(xy): + def xy_to_ij(xy: NDArray) -> NDArray: """ - Converts given *CIE xy* chromaticity coordinates to *ij* + Convert given *CIE xy* chromaticity coordinates to *ij* chromaticity coordinates. """ return xy_to_Luv_uv(xy) - else: - raise ValueError( - 'Invalid method: "{0}", must be one of ' - '[\'CIE 1931\', \'CIE 1960 UCS\', \'CIE 1976 UCS\']'.format( - method)) - x, y, _a, _b, _theta, a, b, theta = tsplit(DATA_MACADAM_1942_ELLIPSES) ellipses_coefficients = [] @@ -1165,32 +1385,37 @@ def xy_to_ij(xy): ) ij = xy_to_ij(xy) ellipses_coefficients.append( - ellipse_coefficients_canonical_form(ellipse_fitting(ij))) + ellipse_coefficients_canonical_form(ellipse_fitting(ij)) + ) return ellipses_coefficients @override_style() def plot_ellipses_MacAdam1942_in_chromaticity_diagram( - chromaticity_diagram_callable=plot_chromaticity_diagram, - method='CIE 1931', - chromaticity_diagram_clipping=False, - ellipse_kwargs=None, - **kwargs): + chromaticity_diagram_callable: Callable = ( + plot_chromaticity_diagram # type: ignore[has-type] + ), + method: Union[ + Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"], str + ] = "CIE 1931", + chromaticity_diagram_clipping: Boolean = False, + ellipse_kwargs: Optional[Union[Dict, List[Dict]]] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots *MacAdam (1942) Ellipses (Observer PGN)* in the + Plot *MacAdam (1942) Ellipses (Observer PGN)* in the *Chromaticity Diagram* according to given method. Parameters ---------- - chromaticity_diagram_callable : callable, optional + chromaticity_diagram_callable Callable responsible for drawing the *Chromaticity Diagram*. - method : unicode, optional - **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**, + method *Chromaticity Diagram* method. - chromaticity_diagram_clipping : bool, optional, + chromaticity_diagram_clipping Whether to clip the *Chromaticity Diagram* colours with the ellipses. - ellipse_kwargs : dict or array_like, optional + ellipse_kwargs Parameters for the :class:`Ellipse` class, ``ellipse_kwargs`` can be either a single dictionary applied to all the ellipses with same settings or a sequence of dictionaries with different settings for each @@ -1198,16 +1423,15 @@ def plot_ellipses_MacAdam1942_in_chromaticity_diagram( Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - Also handles keywords arguments for deprecation management. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -1222,17 +1446,13 @@ def plot_ellipses_MacAdam1942_in_chromaticity_diagram( :alt: plot_ellipses_MacAdam1942_in_chromaticity_diagram """ - ellipse_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['ellipse_parameters', 'ellipse_kwargs']], - }, **kwargs).get('ellipse_kwargs', ellipse_kwargs) - - settings = {'uniform': True} + settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) settings = dict(kwargs) - settings.update({'axes': axes, 'standalone': False}) + settings.update({"axes": axes, "standalone": False}) ellipses_coefficients = ellipses_MacAdam1942(method=method) @@ -1248,36 +1468,47 @@ def plot_ellipses_MacAdam1942_in_chromaticity_diagram( point_at_angle_on_ellipse( np.linspace(0, 360, 36), coefficients, - )) + ) + ) diagram_clipping_path_x.append(x) diagram_clipping_path_y.append(y) diagram_clipping_path = np.rollaxis( - np.array([diagram_clipping_path_x, diagram_clipping_path_y]), 0, 3) + np.array([diagram_clipping_path_x, diagram_clipping_path_y]), 0, 3 + ) diagram_clipping_path = Path.make_compound_path_from_polys( - diagram_clipping_path).vertices - settings.update({'diagram_clipping_path': diagram_clipping_path}) + diagram_clipping_path + ).vertices + settings.update({"diagram_clipping_path": diagram_clipping_path}) chromaticity_diagram_callable(**settings) - ellipse_settings_collection = [{ - 'color': CONSTANTS_COLOUR_STYLE.colour.cycle[4], - 'alpha': 0.4, - 'edgecolor': CONSTANTS_COLOUR_STYLE.colour.cycle[1], - 'linewidth': colour_style()['lines.linewidth'] - } for _ellipses_coefficient in ellipses_coefficients] + ellipse_settings_collection = [ + { + "color": CONSTANTS_COLOUR_STYLE.colour.cycle[4], + "alpha": 0.4, + "edgecolor": CONSTANTS_COLOUR_STYLE.colour.cycle[1], + "linewidth": colour_style()["lines.linewidth"], + "zorder": CONSTANTS_COLOUR_STYLE.zorder.midground_polygon, + } + for _ellipses_coefficient in ellipses_coefficients + ] if ellipse_kwargs is not None: - update_settings_collection(ellipse_settings_collection, ellipse_kwargs, - len(ellipses_coefficients)) + update_settings_collection( + ellipse_settings_collection, + ellipse_kwargs, + len(ellipses_coefficients), + ) for i, coefficients in enumerate(ellipses_coefficients): x_c, y_c, a_a, a_b, theta_e = coefficients - ellipse = Ellipse((x_c, y_c), a_a, a_b, theta_e, - **ellipse_settings_collection[i]) + ellipse = Ellipse( + (x_c, y_c), a_a, a_b, theta_e, **ellipse_settings_collection[i] + ) axes.add_artist(ellipse) - settings.update({'standalone': True}) + settings.update({"standalone": True}) settings.update(kwargs) return render(**settings) @@ -1285,23 +1516,25 @@ def plot_ellipses_MacAdam1942_in_chromaticity_diagram( @override_style() def plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931( - chromaticity_diagram_callable_CIE1931=( - plot_chromaticity_diagram_CIE1931), - chromaticity_diagram_clipping=False, - ellipse_kwargs=None, - **kwargs): + chromaticity_diagram_callable_CIE1931: Callable = ( + plot_chromaticity_diagram_CIE1931 # type: ignore[has-type] + ), + chromaticity_diagram_clipping: Boolean = False, + ellipse_kwargs: Optional[Union[Dict, List[Dict]]] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots *MacAdam (1942) Ellipses (Observer PGN)* in the + Plot *MacAdam (1942) Ellipses (Observer PGN)* in the *CIE 1931 Chromaticity Diagram*. Parameters ---------- - chromaticity_diagram_callable_CIE1931 : callable, optional + chromaticity_diagram_callable_CIE1931 Callable responsible for drawing the *CIE 1931 Chromaticity Diagram*. - chromaticity_diagram_clipping : bool, optional, + chromaticity_diagram_clipping Whether to clip the *CIE 1931 Chromaticity Diagram* colours with the ellipses. - ellipse_kwargs : dict or array_like, optional + ellipse_kwargs Parameters for the :class:`Ellipse` class, ``ellipse_kwargs`` can be either a single dictionary applied to all the ellipses with same settings or a sequence of dictionaries with different settings for each @@ -1309,18 +1542,17 @@ def plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931( Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.models.\ plot_ellipses_MacAdam1942_in_chromaticity_diagram`}, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - Also handles keywords arguments for deprecation management. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -1335,40 +1567,39 @@ def plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931( :alt: plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931 """ - ellipse_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['ellipse_parameters', 'ellipse_kwargs']], - }, **kwargs).get('ellipse_kwargs', ellipse_kwargs) - settings = dict(kwargs) - settings.update({'method': 'CIE 1931'}) + settings.update({"method": "CIE 1931"}) return plot_ellipses_MacAdam1942_in_chromaticity_diagram( chromaticity_diagram_callable_CIE1931, chromaticity_diagram_clipping=chromaticity_diagram_clipping, ellipse_kwargs=ellipse_kwargs, - **settings) + **settings, + ) @override_style() def plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS( - chromaticity_diagram_callable_CIE1960UCS=( - plot_chromaticity_diagram_CIE1960UCS), - chromaticity_diagram_clipping=False, - ellipse_kwargs=None, - **kwargs): + chromaticity_diagram_callable_CIE1960UCS: Callable = ( + plot_chromaticity_diagram_CIE1960UCS # type: ignore[has-type] + ), + chromaticity_diagram_clipping: Boolean = False, + ellipse_kwargs: Optional[Union[Dict, List[Dict]]] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots *MacAdam (1942) Ellipses (Observer PGN)* in the + Plot *MacAdam (1942) Ellipses (Observer PGN)* in the *CIE 1960 UCS Chromaticity Diagram*. Parameters ---------- - chromaticity_diagram_callable_CIE1960UCS : callable, optional + chromaticity_diagram_callable_CIE1960UCS Callable responsible for drawing the *CIE 1960 UCS Chromaticity Diagram*. - chromaticity_diagram_clipping : bool, optional, + chromaticity_diagram_clipping Whether to clip the *CIE 1960 UCS Chromaticity Diagram* colours with the ellipses. - ellipse_kwargs : dict or array_like, optional + ellipse_kwargs Parameters for the :class:`Ellipse` class, ``ellipse_kwargs`` can be either a single dictionary applied to all the ellipses with same settings or a sequence of dictionaries with different settings for each @@ -1376,18 +1607,17 @@ def plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS( Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.models.\ plot_ellipses_MacAdam1942_in_chromaticity_diagram`}, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - Also handles keywords arguments for deprecation management. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -1402,40 +1632,39 @@ def plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS( :alt: plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS """ - ellipse_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['ellipse_parameters', 'ellipse_kwargs']], - }, **kwargs).get('ellipse_kwargs', ellipse_kwargs) - settings = dict(kwargs) - settings.update({'method': 'CIE 1960 UCS'}) + settings.update({"method": "CIE 1960 UCS"}) return plot_ellipses_MacAdam1942_in_chromaticity_diagram( chromaticity_diagram_callable_CIE1960UCS, chromaticity_diagram_clipping=chromaticity_diagram_clipping, ellipse_kwargs=ellipse_kwargs, - **settings) + **settings, + ) @override_style() def plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS( - chromaticity_diagram_callable_CIE1976UCS=( - plot_chromaticity_diagram_CIE1976UCS), - chromaticity_diagram_clipping=False, - ellipse_kwargs=None, - **kwargs): + chromaticity_diagram_callable_CIE1976UCS: Callable = ( + plot_chromaticity_diagram_CIE1976UCS # type: ignore[has-type] + ), + chromaticity_diagram_clipping: Boolean = False, + ellipse_kwargs: Optional[Union[Dict, List[Dict]]] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots *MacAdam (1942) Ellipses (Observer PGN)* in the + Plot *MacAdam (1942) Ellipses (Observer PGN)* in the *CIE 1976 UCS Chromaticity Diagram*. Parameters ---------- - chromaticity_diagram_callable_CIE1976UCS : callable, optional + chromaticity_diagram_callable_CIE1976UCS Callable responsible for drawing the *CIE 1976 UCS Chromaticity Diagram*. - chromaticity_diagram_clipping : bool, optional, + chromaticity_diagram_clipping Whether to clip the *CIE 1976 UCS Chromaticity Diagram* colours with the ellipses. - ellipse_kwargs : dict or array_like, optional + ellipse_kwargs Parameters for the :class:`Ellipse` class, ``ellipse_kwargs`` can be either a single dictionary applied to all the ellipses with same settings or a sequence of dictionaries with different settings for each @@ -1443,18 +1672,17 @@ def plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS( Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.models.\ plot_ellipses_MacAdam1942_in_chromaticity_diagram`}, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - Also handles keywords arguments for deprecation management. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -1469,45 +1697,44 @@ def plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS( :alt: plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS """ - ellipse_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['ellipse_parameters', 'ellipse_kwargs']], - }, **kwargs).get('ellipse_kwargs', ellipse_kwargs) - settings = dict(kwargs) - settings.update({'method': 'CIE 1976 UCS'}) + settings.update({"method": "CIE 1976 UCS"}) return plot_ellipses_MacAdam1942_in_chromaticity_diagram( chromaticity_diagram_callable_CIE1976UCS, chromaticity_diagram_clipping=chromaticity_diagram_clipping, ellipse_kwargs=ellipse_kwargs, - **settings) + **settings, + ) @override_style() -def plot_single_cctf(cctf, cctf_decoding=False, **kwargs): +def plot_single_cctf( + cctf: Union[Callable, str], cctf_decoding: Boolean = False, **kwargs: Any +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given colourspace colour component transfer function. + Plot given colourspace colour component transfer function. Parameters ---------- - cctf : unicode or object + cctf Colour component transfer function to plot. ``function`` can be of any type or form supported by the :func:`colour.plotting.filter_passthrough` definition. - cctf_decoding : bool + cctf_decoding Plot the decoding colour component transfer function instead. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_multi_functions`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -1520,10 +1747,8 @@ def plot_single_cctf(cctf, cctf_decoding=False, **kwargs): :alt: plot_single_cctf """ - settings = { - 'title': - '{0} - {1} CCTF'.format( - cctf, 'Decoding' if cctf_decoding else 'Encoding') + settings: Dict[str, Any] = { + "title": f"{cctf} - {'Decoding' if cctf_decoding else 'Encoding'} CCTF" } settings.update(kwargs) @@ -1531,30 +1756,34 @@ def plot_single_cctf(cctf, cctf_decoding=False, **kwargs): @override_style() -def plot_multi_cctfs(cctfs, cctf_decoding=False, **kwargs): +def plot_multi_cctfs( + cctfs: Union[Callable, str, Sequence[Union[Callable, str]]], + cctf_decoding: Boolean = False, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given colour component transfer functions. + Plot given colour component transfer functions. Parameters ---------- - cctfs : unicode or object or array_like, optional + cctfs Colour component transfer function to plot. ``cctfs`` elements can be of any type or form supported by the :func:`colour.plotting.filter_passthrough` definition. - cctf_decoding : bool + cctf_decoding Plot the decoding colour component transfer function instead. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_multi_functions`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -1567,45 +1796,80 @@ def plot_multi_cctfs(cctfs, cctf_decoding=False, **kwargs): :alt: plot_multi_cctfs """ - cctfs = filter_passthrough( - CCTF_DECODINGS if cctf_decoding else CCTF_ENCODINGS, cctfs) + cctfs_filtered = filter_passthrough( + CCTF_DECODINGS if cctf_decoding else CCTF_ENCODINGS, cctfs + ) - mode = 'Decoding' if cctf_decoding else 'Encoding' - title = '{0} - {1} CCTFs'.format(', '.join([cctf for cctf in cctfs]), mode) + mode = "Decoding" if cctf_decoding else "Encoding" + title = f"{', '.join([cctf for cctf in cctfs_filtered])} - {mode} CCTFs" - settings = { - 'bounding_box': (0, 1, 0, 1), - 'legend': True, - 'title': title, - 'x_label': 'Signal Value' if cctf_decoding else 'Tristimulus Value', - 'y_label': 'Tristimulus Value' if cctf_decoding else 'Signal Value', + settings: Dict[str, Any] = { + "bounding_box": (0, 1, 0, 1), + "legend": True, + "title": title, + "x_label": "Signal Value" if cctf_decoding else "Tristimulus Value", + "y_label": "Tristimulus Value" if cctf_decoding else "Signal Value", } settings.update(kwargs) - with domain_range_scale(1): - return plot_multi_functions(cctfs, **settings) + with domain_range_scale("1"): + return plot_multi_functions(cctfs_filtered, **settings) @override_style() -def plot_constant_hue_loci(data, model, scatter_kwargs=None, **kwargs): +def plot_constant_hue_loci( + data: ArrayLike, + model: Union[ + Literal[ + "CAM02LCD", + "CAM02SCD", + "CAM02UCS", + "CAM16LCD", + "CAM16SCD", + "CAM16UCS", + "CIE XYZ", + "CIE xyY", + "CIE Lab", + "CIE Luv", + "CIE UCS", + "CIE UVW", + "DIN99", + "Hunter Lab", + "Hunter Rdab", + "ICaCb", + "ICtCp", + "IPT", + "IgPgTg", + "Jzazbz", + "OSA UCS", + "Oklab", + "hdr-CIELAB", + "hdr-IPT", + ], + str, + ] = "CIE Lab", + scatter_kwargs: Optional[Dict] = None, + convert_kwargs: Optional[Dict] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given constant hue loci colour matches data such as that from + Plot given constant hue loci colour matches data such as that from :cite:`Hung1995` or :cite:`Ebner1998` that are easily loaded with `Colour - Datasets `__. Parameters ---------- - data : array_like - Constant hue loci colour matches data expected to be an *array_like* as + data + Constant hue loci colour matches data expected to be an `ArrayLike` as follows:: [ ('name', XYZ_r, XYZ_cr, (XYZ_ct, XYZ_ct, XYZ_ct, ...), \ -{metadata}), + {metadata}), ('name', XYZ_r, XYZ_cr, (XYZ_ct, XYZ_ct, XYZ_ct, ...), \ -{metadata}), + {metadata}), ('name', XYZ_r, XYZ_cr, (XYZ_ct, XYZ_ct, XYZ_ct, ...), \ -{metadata}), + {metadata}), ... ] @@ -1615,31 +1879,29 @@ def plot_constant_hue_loci(data, model, scatter_kwargs=None, **kwargs): reference illuminant, ``XYZ_ct`` the *CIE XYZ* tristimulus values of the colour matches under the reference illuminant and ``metadata`` the dataset metadata. - model : unicode, optional - **{'CIE XYZ', 'CIE xyY', 'CIE xy', 'CIE Lab', 'CIE LCHab', 'CIE Luv', - 'CIE Luv uv', 'CIE LCHuv', 'CIE UCS', 'CIE UCS uv', 'CIE UVW', - 'DIN 99', 'Hunter Lab', 'Hunter Rdab', 'IPT', 'JzAzBz', 'OSA UCS', - 'hdr-CIELAB', 'hdr-IPT'}**, - Colourspace model. - scatter_kwargs : dict, optional - Keyword arguments for the :func:`plt.scatter` definition. The following - special keyword arguments can also be used: - - - *c* : unicode or array_like, if ``c`` is set to *RGB*, the scatter - will use the colours as given by the ``RGB`` argument. + model + Colourspace model, see :attr:`colour.COLOURSPACE_MODELS` attribute for + the list of supported colourspace models. + scatter_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.scatter` definition. + The following special keyword arguments can also be used: + + - ``c`` : If ``c`` is set to *RGB*, the scatter will use the colours + as given by the ``RGB`` argument. + convert_kwargs + Keyword arguments for the :func:`colour.convert` definition. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_multi_functions`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - Also handles keywords arguments for deprecation management. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. References @@ -1711,7 +1973,7 @@ def plot_constant_hue_loci(data, model, scatter_kwargs=None, **kwargs): ... None, ... ], ... ]) - >>> plot_constant_hue_loci(data, 'IPT') # doctest: +ELLIPSIS + >>> plot_constant_hue_loci(data, 'CIE Lab') # doctest: +ELLIPSIS (
, <...AxesSubplot...>) .. image:: ../_static/Plotting_Plot_Constant_Hue_Loci.png @@ -1719,60 +1981,68 @@ def plot_constant_hue_loci(data, model, scatter_kwargs=None, **kwargs): :alt: plot_constant_hue_loci """ - scatter_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['scatter_parameters', 'scatter_kwargs']], - }, **kwargs).get('scatter_kwargs', scatter_kwargs) - # TODO: Filter appropriate colour models. + data = as_array(data) - data = data.values() if isinstance(data, Mapping) else data - - settings = {'uniform': True} + settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) scatter_settings = { - 's': 40, - 'c': 'RGB', - 'marker': 'o', - 'alpha': 0.85, + "s": 40, + "c": "RGB", + "marker": "o", + "alpha": 0.85, + "zorder": CONSTANTS_COLOUR_STYLE.zorder.foreground_scatter, } if scatter_kwargs is not None: scatter_settings.update(scatter_kwargs) - use_RGB_colours = scatter_settings['c'].upper() == 'RGB' + convert_kwargs = optional(convert_kwargs, {}) + + use_RGB_colours = str(scatter_settings["c"]).upper() == "RGB" colourspace = CONSTANTS_COLOUR_STYLE.colour.colourspace for hue_data in data: _name, XYZ_r, XYZ_cr, XYZ_ct, _metadata = hue_data xy_r = XYZ_to_xy(XYZ_r) - ijk_ct = common_colourspace_model_axis_reorder( - convert(XYZ_ct, 'CIE XYZ', model, illuminant=xy_r), model) - ijk_cr = common_colourspace_model_axis_reorder( - convert(XYZ_cr, 'CIE XYZ', model, illuminant=xy_r), model) - def _linear_equation(x, a, b): - """ - Defines the canonical linear equation for a line. - """ + convert_settings = {"illuminant": xy_r} + convert_settings.update(convert_kwargs) + + ijk_ct = colourspace_model_axis_reorder( + convert(XYZ_ct, "CIE XYZ", model, **convert_settings), model + ) + ijk_cr = colourspace_model_axis_reorder( + convert(XYZ_cr, "CIE XYZ", model, **convert_settings), model + ) + + ijk_ct *= COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[model] + ijk_cr *= COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[model] + + def _linear_equation(x: NDArray, a: NDArray, b: NDArray) -> NDArray: + """Define the canonical linear equation for a line.""" return a * x + b - popt, _pcov = scipy.optimize.curve_fit(_linear_equation, - ijk_ct[..., 0], ijk_ct[..., 1]) + popt, _pcov = scipy.optimize.curve_fit( + _linear_equation, ijk_ct[..., 0], ijk_ct[..., 1] + ) axes.plot( ijk_ct[..., 0], _linear_equation(ijk_ct[..., 0], *popt), - c=CONSTANTS_COLOUR_STYLE.colour.average) + c=CONSTANTS_COLOUR_STYLE.colour.average, + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line, + ) if use_RGB_colours: - def _XYZ_to_RGB(XYZ): + def _XYZ_to_RGB(XYZ: NDArray) -> NDArray: """ - Converts given *CIE XYZ* tristimulus values to + Convert given *CIE XYZ* tristimulus values to ``colour.plotting`` *RGB* colourspace. """ @@ -1781,33 +2051,34 @@ def _XYZ_to_RGB(XYZ): xy_r, colourspace.whitepoint, colourspace.matrix_XYZ_to_RGB, - cctf_encoding=colourspace.cctf_encoding) + cctf_encoding=colourspace.cctf_encoding, + ) RGB_ct = _XYZ_to_RGB(XYZ_ct) RGB_cr = _XYZ_to_RGB(XYZ_cr) - scatter_settings['c'] = np.clip(RGB_ct, 0, 1) + scatter_settings["c"] = np.clip(RGB_ct, 0, 1) - axes.scatter( - ijk_ct[..., 0], ijk_ct[..., 1], zorder=10, **scatter_settings) + axes.scatter(ijk_ct[..., 0], ijk_ct[..., 1], **scatter_settings) axes.plot( ijk_cr[..., 0], ijk_cr[..., 1], - 's', - zorder=10, + "s", c=np.clip(np.ravel(RGB_cr), 0, 1), - markersize=CONSTANTS_COLOUR_STYLE.geometry.short * 8) + markersize=CONSTANTS_COLOUR_STYLE.geometry.short * 8, + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line, + ) - labels = np.array(COLOURSPACE_MODELS_AXIS_LABELS[model])[as_int_array( - common_colourspace_model_axis_reorder([0, 1, 2], model))] + labels = np.array(COLOURSPACE_MODELS_AXIS_LABELS[model])[ + as_int_array(colourspace_model_axis_reorder([0, 1, 2], model)) + ] settings = { - 'axes': axes, - 'title': 'Constant Hue Loci - ' - '{0} (Domain-Range Scale: 1)'.format(model), - 'x_label': labels[0], - 'y_label': labels[1], + "axes": axes, + "title": f"Constant Hue Loci - {model}", + "x_label": labels[0], + "y_label": labels[1], } settings.update(kwargs) diff --git a/colour/plotting/notation.py b/colour/plotting/notation.py index 61f51dca1a..da1aab665c 100644 --- a/colour/plotting/notation.py +++ b/colour/plotting/notation.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Colour Notation Systems Plotting ================================ @@ -9,49 +8,57 @@ - :func:`colour.plotting.plot_multi_munsell_value_functions` """ -from __future__ import division +from __future__ import annotations +import matplotlib.pyplot as plt import numpy as np +from colour.hints import Any, Callable, Dict, Sequence, Tuple, Union from colour.notation import MUNSELL_VALUE_METHODS -from colour.plotting import (filter_passthrough, plot_multi_functions, - override_style) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.plotting import ( + filter_passthrough, + plot_multi_functions, + override_style, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'plot_single_munsell_value_function', 'plot_multi_munsell_value_functions' + "plot_single_munsell_value_function", + "plot_multi_munsell_value_functions", ] @override_style() -def plot_single_munsell_value_function(function, **kwargs): +def plot_single_munsell_value_function( + function: Union[Callable, str], **kwargs: Any +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given *Lightness* function. + Plot given *Lightness* function. Parameters ---------- - function : unicode or object + function *Munsell* value function to plot. ``function`` can be of any type or form supported by the :func:`colour.plotting.filter_passthrough` definition. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_multi_functions`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -64,35 +71,40 @@ def plot_single_munsell_value_function(function, **kwargs): :alt: plot_single_munsell_value_function """ - settings = {'title': '{0} - Munsell Value Function'.format(function)} + settings: Dict[str, Any] = { + "title": f"{function} - Munsell Value Function" + } settings.update(kwargs) - return plot_multi_munsell_value_functions((function, ), **settings) + return plot_multi_munsell_value_functions((function,), **settings) @override_style() -def plot_multi_munsell_value_functions(functions, **kwargs): +def plot_multi_munsell_value_functions( + functions: Union[Callable, str, Sequence[Union[Callable, str]]], + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given *Munsell* value functions. + Plot given *Munsell* value functions. Parameters ---------- - functions : unicode or object or array_like + functions *Munsell* value functions to plot. ``functions`` elements can be of any type or form supported by the :func:`colour.plotting.filter_passthrough` definition. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_multi_functions`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -106,16 +118,17 @@ def plot_multi_munsell_value_functions(functions, **kwargs): :alt: plot_multi_munsell_value_functions """ - functions = filter_passthrough(MUNSELL_VALUE_METHODS, functions) + functions_filtered = filter_passthrough(MUNSELL_VALUE_METHODS, functions) - settings = { - 'bounding_box': (0, 100, 0, 10), - 'legend': True, - 'title': '{0} - Munsell Functions'.format(', '.join(functions)), - 'x_label': 'Luminance Y', - 'y_label': 'Munsell Value V', + settings: Dict[str, Any] = { + "bounding_box": (0, 100, 0, 10), + "legend": True, + "title": f"{', '.join(functions_filtered)} - Munsell Functions", + "x_label": "Luminance Y", + "y_label": "Munsell Value V", } settings.update(kwargs) return plot_multi_functions( - functions, samples=np.linspace(0, 100, 1000), **settings) + functions_filtered, samples=np.linspace(0, 100, 1000), **settings + ) diff --git a/colour/plotting/phenomena.py b/colour/plotting/phenomena.py index 5df2bf38a0..47a378ed33 100644 --- a/colour/plotting/phenomena.py +++ b/colour/plotting/phenomena.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Optical Phenomenon Plotting =========================== @@ -9,77 +8,104 @@ - :func:`colour.plotting.plot_the_blue_sky` """ -from __future__ import division +from __future__ import annotations import matplotlib.pyplot as plt -from colour.colorimetry import sd_to_XYZ +from colour.algebra import normalise_maximum +from colour.colorimetry import ( + MultiSpectralDistributions, + SpectralDistribution, + sd_to_XYZ, +) +from colour.hints import ( + Any, + Dict, + FloatingOrArrayLike, + Sequence, + Tuple, + Union, + cast, +) from colour.phenomena import sd_rayleigh_scattering from colour.phenomena.rayleigh import ( - CONSTANT_AVERAGE_PRESSURE_MEAN_SEA_LEVEL, CONSTANT_DEFAULT_ALTITUDE, - CONSTANT_DEFAULT_LATITUDE, CONSTANT_STANDARD_AIR_TEMPERATURE, - CONSTANT_STANDARD_CO2_CONCENTRATION) -from colour.plotting import (SD_ASTMG173_ETR, CONSTANTS_COLOUR_STYLE, - ColourSwatch, XYZ_to_plotting_colourspace, - filter_cmfs, override_style, render, - plot_single_colour_swatch, plot_single_sd) -from colour.utilities import first_item, normalise_maximum - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['plot_single_sd_rayleigh_scattering', 'plot_the_blue_sky'] + CONSTANT_AVERAGE_PRESSURE_MEAN_SEA_LEVEL, + CONSTANT_DEFAULT_ALTITUDE, + CONSTANT_DEFAULT_LATITUDE, + CONSTANT_STANDARD_AIR_TEMPERATURE, + CONSTANT_STANDARD_CO2_CONCENTRATION, +) +from colour.plotting import ( + SD_ASTMG173_ETR, + CONSTANTS_COLOUR_STYLE, + ColourSwatch, + XYZ_to_plotting_colourspace, + filter_cmfs, + override_style, + render, + plot_single_colour_swatch, + plot_single_sd, +) +from colour.utilities import first_item + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "plot_single_sd_rayleigh_scattering", + "plot_the_blue_sky", +] @override_style() def plot_single_sd_rayleigh_scattering( - CO2_concentration=CONSTANT_STANDARD_CO2_CONCENTRATION, - temperature=CONSTANT_STANDARD_AIR_TEMPERATURE, - pressure=CONSTANT_AVERAGE_PRESSURE_MEAN_SEA_LEVEL, - latitude=CONSTANT_DEFAULT_LATITUDE, - altitude=CONSTANT_DEFAULT_ALTITUDE, - cmfs='CIE 1931 2 Degree Standard Observer', - **kwargs): + CO2_concentration: FloatingOrArrayLike = CONSTANT_STANDARD_CO2_CONCENTRATION, + temperature: FloatingOrArrayLike = CONSTANT_STANDARD_AIR_TEMPERATURE, + pressure: FloatingOrArrayLike = CONSTANT_AVERAGE_PRESSURE_MEAN_SEA_LEVEL, + latitude: FloatingOrArrayLike = CONSTANT_DEFAULT_LATITUDE, + altitude: FloatingOrArrayLike = CONSTANT_DEFAULT_ALTITUDE, + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots a single *Rayleigh* scattering spectral distribution. + Plot a single *Rayleigh* scattering spectral distribution. Parameters ---------- - CO2_concentration : numeric, optional + CO2_concentration :math:`CO_2` concentration in parts per million (ppm). - temperature : numeric, optional + temperature Air temperature :math:`T[K]` in kelvin degrees. - pressure : numeric + pressure Surface pressure :math:`P` of the measurement site. - latitude : numeric, optional + latitude Latitude of the site in degrees. - altitude : numeric, optional + altitude Altitude of the site in meters. - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectrum domain and colours. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_single_sd`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - out_of_gamut_clipping : bool, optional - {:func:`colour.plotting.plot_single_sd`}, - Whether to clip out of gamut colours otherwise, the colours will be - offset by the absolute minimal colour leading to a rendering on - gray background, less saturated and smoother. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -92,43 +118,58 @@ def plot_single_sd_rayleigh_scattering( :alt: plot_single_sd_rayleigh_scattering """ - title = 'Rayleigh Scattering' + title = "Rayleigh Scattering" - cmfs = first_item(filter_cmfs(cmfs).values()) + cmfs = cast( + MultiSpectralDistributions, first_item(filter_cmfs(cmfs).values()) + ) - settings = {'title': title, 'y_label': 'Optical Depth'} + settings: Dict[str, Any] = {"title": title, "y_label": "Optical Depth"} settings.update(kwargs) - sd = sd_rayleigh_scattering(cmfs.shape, CO2_concentration, temperature, - pressure, latitude, altitude) + sd = sd_rayleigh_scattering( + cmfs.shape, + CO2_concentration, + temperature, + pressure, + latitude, + altitude, + ) return plot_single_sd(sd, **settings) @override_style() -def plot_the_blue_sky(cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): +def plot_the_blue_sky( + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the blue sky. + Plot the blue sky. Parameters ---------- - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectrum domain and colours. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_single_sd`, :func:`colour.plotting.plot_multi_colour_swatches`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -145,9 +186,11 @@ def plot_the_blue_sky(cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): figure.subplots_adjust(hspace=CONSTANTS_COLOUR_STYLE.geometry.short / 2) - cmfs = first_item(filter_cmfs(cmfs).values()) + cmfs = cast( + MultiSpectralDistributions, first_item(filter_cmfs(cmfs).values()) + ) - ASTMG173_sd = SD_ASTMG173_ETR.copy() + ASTMG173_sd = cast(SpectralDistribution, SD_ASTMG173_ETR.copy()) rayleigh_sd = sd_rayleigh_scattering() ASTMG173_sd.align(rayleigh_sd.shape) @@ -155,42 +198,47 @@ def plot_the_blue_sky(cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): axes = figure.add_subplot(211) - settings = { - 'axes': axes, - 'title': 'The Blue Sky - Synthetic Spectral Distribution', - 'y_label': u'W / m-2 / nm-1', + settings: Dict[str, Any] = { + "axes": axes, + "title": "The Blue Sky - Synthetic Spectral Distribution", + "y_label": "W / m-2 / nm-1", } settings.update(kwargs) - settings['standalone'] = False + settings["standalone"] = False plot_single_sd(sd, cmfs, **settings) axes = figure.add_subplot(212) - x_label = ('The sky is blue because molecules in the atmosphere ' - 'scatter shorter wavelengths more than longer ones.\n' - 'The synthetic spectral distribution is computed as ' - 'follows: ' - '(ASTM G-173 ETR * Standard Air Rayleigh Scattering).') + x_label = ( + "The sky is blue because molecules in the atmosphere " + "scatter shorter wavelengths more than longer ones.\n" + "The synthetic spectral distribution is computed as " + "follows: " + "(ASTM G-173 ETR * Standard Air Rayleigh Scattering)." + ) settings = { - 'axes': axes, - 'aspect': None, - 'title': 'The Blue Sky - Colour', - 'x_label': x_label, - 'y_label': '', - 'x_ticker': False, - 'y_ticker': False, + "axes": axes, + "aspect": None, + "title": "The Blue Sky - Colour", + "x_label": x_label, + "y_label": "", + "x_ticker": False, + "y_ticker": False, } settings.update(kwargs) - settings['standalone'] = False + settings["standalone"] = False - blue_sky_color = XYZ_to_plotting_colourspace(sd_to_XYZ(sd)) + blue_sky_color = XYZ_to_plotting_colourspace( + sd_to_XYZ(cast(SpectralDistribution, sd)) + ) figure, axes = plot_single_colour_swatch( - ColourSwatch('', normalise_maximum(blue_sky_color)), **settings) + ColourSwatch(normalise_maximum(blue_sky_color)), **settings + ) - settings = {'axes': axes, 'standalone': True} + settings = {"axes": axes, "standalone": True} settings.update(kwargs) return render(**settings) diff --git a/colour/plotting/quality.py b/colour/plotting/quality.py index 649a127105..b4ef290ba8 100644 --- a/colour/plotting/quality.py +++ b/colour/plotting/quality.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Colour Quality Plotting ======================= @@ -11,66 +10,103 @@ - :func:`colour.plotting.plot_multi_sds_colour_quality_scales_bars` """ -from __future__ import division +from __future__ import annotations +import matplotlib.pyplot as plt import numpy as np from itertools import cycle from colour.constants import DEFAULT_FLOAT_DTYPE -from colour.colorimetry import sds_and_msds_to_sds -from colour.plotting import (CONSTANTS_COLOUR_STYLE, - XYZ_to_plotting_colourspace, artist, - label_rectangles, override_style, render) -from colour.quality import (colour_quality_scale, colour_rendering_index) -from colour.quality.cri import TCS_ColorimetryData - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.colorimetry import ( + MultiSpectralDistributions, + SpectralDistribution, + sds_and_msds_to_sds, +) +from colour.hints import ( + Any, + Boolean, + Dict, + Integer, + List, + Literal, + Optional, + Sequence, + Tuple, + Union, + cast, +) +from colour.plotting import ( + CONSTANTS_COLOUR_STYLE, + XYZ_to_plotting_colourspace, + artist, + label_rectangles, + override_style, + render, +) +from colour.quality import ( + COLOUR_QUALITY_SCALE_METHODS, + ColourRendering_Specification_CQS, + ColourRendering_Specification_CRI, + colour_quality_scale, + colour_rendering_index, +) +from colour.utilities import as_float_array, validate_method + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'plot_colour_quality_bars', 'plot_single_sd_colour_rendering_index_bars', - 'plot_multi_sds_colour_rendering_indexes_bars', - 'plot_single_sd_colour_quality_scale_bars', - 'plot_multi_sds_colour_quality_scales_bars' + "plot_colour_quality_bars", + "plot_single_sd_colour_rendering_index_bars", + "plot_multi_sds_colour_rendering_indexes_bars", + "plot_single_sd_colour_quality_scale_bars", + "plot_multi_sds_colour_quality_scales_bars", ] @override_style() -def plot_colour_quality_bars(specifications, - labels=True, - hatching=None, - hatching_repeat=2, - **kwargs): +def plot_colour_quality_bars( + specifications: Sequence[ + Union[ + ColourRendering_Specification_CQS, + ColourRendering_Specification_CRI, + ] + ], + labels: Boolean = True, + hatching: Optional[Boolean] = None, + hatching_repeat: Integer = 2, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the colour quality data of given illuminants or light sources colour + Plot the colour quality data of given illuminants or light sources colour quality specifications. Parameters ---------- - specifications : array_like + specifications Array of illuminants or light sources colour quality specifications. - labels : bool, optional + labels Add labels above bars. - hatching : bool or None, optional + hatching Use hatching for the bars. - hatching_repeat : int, optional + hatching_repeat Hatching pattern repeat. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.quality.plot_colour_quality_bars`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -90,7 +126,7 @@ def plot_colour_quality_bars(specifications, :alt: plot_colour_quality_bars """ - settings = {'uniform': True} + settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) @@ -101,21 +137,39 @@ def plot_colour_quality_bars(specifications, patterns = cycle(CONSTANTS_COLOUR_STYLE.hatch.patterns) if hatching is None: hatching = False if count_s == 1 else True + for i, specification in enumerate(specifications): - Q_a, Q_as, colorimetry_data = (specification.Q_a, specification.Q_as, - specification.colorimetry_data) + Q_a, Q_as, colorimetry_data = ( + specification.Q_a, + specification.Q_as, + specification.colorimetry_data, + ) count_Q_as = len(Q_as) - RGB = ([[1] * 3] + [ + RGB = [[1] * 3] + [ np.clip(XYZ_to_plotting_colourspace(x.XYZ), 0, 1) for x in colorimetry_data[0] - ]) - - x = (i + np.arange( - 0, (count_Q_as + 1) * (count_s + 1), (count_s + 1), - dtype=DEFAULT_FLOAT_DTYPE)) * bar_width - y = [s[1].Q_a for s in sorted(Q_as.items(), key=lambda s: s[0])] - y = np.array([Q_a] + list(y)) + ] + + x = ( + as_float_array( + i + + np.arange( + 0, + (count_Q_as + 1) * (count_s + 1), + (count_s + 1), + dtype=DEFAULT_FLOAT_DTYPE, + ) + ) + * bar_width + ) + y = as_float_array( + [Q_a] + + [ + s[1].Q_a # type: ignore[attr-defined] + for s in sorted(Q_as.items(), key=lambda s: s[0]) + ] + ) bars = axes.bar( x, @@ -123,47 +177,73 @@ def plot_colour_quality_bars(specifications, color=RGB, width=bar_width, edgecolor=CONSTANTS_COLOUR_STYLE.colour.dark, - label=specification.name) - - hatches = ([next(patterns) * hatching_repeat] * (count_Q_as + 1) - if hatching else np.where(y < 0, next(patterns), - None).tolist()) + label=specification.name, + zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, + ) + + hatches = ( + [next(patterns) * hatching_repeat] * (count_Q_as + 1) + if hatching + else list( + np.where(y < 0, next(patterns), None) # type: ignore[call-overload] + ) + ) for j, bar in enumerate(bars.patches): bar.set_hatch(hatches[j]) if labels: label_rectangles( - ['{0:.1f}'.format(y_v) for y_v in y], + [f"{y_v:.1f}" for y_v in y], bars, - rotation='horizontal' if count_s == 1 else 'vertical', - offset=(0 if count_s == 1 else 3 / 100 * count_s + 65 / 1000, - 0.025), + rotation="horizontal" if count_s == 1 else "vertical", + offset=( + 0 if count_s == 1 else 3 / 100 * count_s + 65 / 1000, + 0.025, + ), text_size=-5 / 7 * count_s + 12.5, - axes=axes) + axes=axes, + ) axes.axhline( - y=100, color=CONSTANTS_COLOUR_STYLE.colour.dark, linestyle='--') - - axes.set_xticks((np.arange( - 0, (count_Q_as + 1) * (count_s + 1), (count_s + 1), - dtype=DEFAULT_FLOAT_DTYPE) - bar_width) * bar_width + - (count_s * bar_width / 2)) + y=100, + color=CONSTANTS_COLOUR_STYLE.colour.dark, + linestyle="--", + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line, + ) + + axes.set_xticks( + ( + np.arange( + 0, + (count_Q_as + 1) * (count_s + 1), + (count_s + 1), + dtype=DEFAULT_FLOAT_DTYPE, + ) + - bar_width + ) + * bar_width + + (count_s * bar_width / 2) + ) axes.set_xticklabels( - ['Qa'] + - ['Q{0}'.format(index + 1) for index in range(0, count_Q_as, 1)]) + ["Qa"] + [f"Q{index + 1}" for index in range(0, count_Q_as, 1)] + ) axes.set_yticks(range(0, 100 + y_ticks_interval, y_ticks_interval)) aspect = 1 / (120 / (bar_width + len(Q_as) + bar_width * 2)) - bounding_box = (-bar_width, - ((count_Q_as + 1) * (count_s + 1)) / 2 - bar_width, 0, 120) + bounding_box = ( + -bar_width, + ((count_Q_as + 1) * (count_s + 1)) / 2 - bar_width, + 0, + 120, + ) settings = { - 'axes': axes, - 'aspect': aspect, - 'bounding_box': bounding_box, - 'legend': hatching, - 'title': 'Colour Quality', + "axes": axes, + "aspect": aspect, + "bounding_box": bounding_box, + "legend": hatching, + "title": "Colour Quality", } settings.update(kwargs) @@ -171,37 +251,30 @@ def plot_colour_quality_bars(specifications, @override_style() -def plot_single_sd_colour_rendering_index_bars(sd, **kwargs): +def plot_single_sd_colour_rendering_index_bars( + sd: SpectralDistribution, **kwargs: Any +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the *Colour Rendering Index* (CRI) of given illuminant or light + Plot the *Colour Rendering Index* (CRI) of given illuminant or light source spectral distribution. Parameters ---------- - sd : SpectralDistribution + sd Illuminant or light source spectral distribution to plot the *Colour Rendering Index* (CRI). Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.quality.plot_colour_quality_bars`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - labels : bool, optional - {:func:`colour.plotting.quality.plot_colour_quality_bars`}, - Add labels above bars. - hatching : bool or None, optional - {:func:`colour.plotting.quality.plot_colour_quality_bars`}, - Use hatching for the bars. - hatching_repeat : int, optional - {:func:`colour.plotting.quality.plot_colour_quality_bars`}, - Hatching pattern repeat. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -222,14 +295,20 @@ def plot_single_sd_colour_rendering_index_bars(sd, **kwargs): @override_style() -def plot_multi_sds_colour_rendering_indexes_bars(sds, **kwargs): +def plot_multi_sds_colour_rendering_indexes_bars( + sds: Union[ + Sequence[Union[SpectralDistribution, MultiSpectralDistributions]], + MultiSpectralDistributions, + ], + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the *Colour Rendering Index* (CRI) of given illuminants or light + Plot the *Colour Rendering Index* (CRI) of given illuminants or light sources spectral distributions. Parameters ---------- - sds : array_like or MultiSpectralDistributions + sds Spectral distributions or multi-spectral distributions to plot. `sds` can be a single :class:`colour.MultiSpectralDistributions` class instance, a list @@ -238,24 +317,15 @@ def plot_multi_sds_colour_rendering_indexes_bars(sds, **kwargs): Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.quality.plot_colour_quality_bars`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - labels : bool, optional - {:func:`colour.plotting.quality.plot_colour_quality_bars`}, - Add labels above bars. - hatching : bool or None, optional - {:func:`colour.plotting.quality.plot_colour_quality_bars`}, - Use hatching for the bars. - hatching_repeat : int, optional - {:func:`colour.plotting.quality.plot_colour_quality_bars`}, - Hatching pattern repeat. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -274,14 +344,18 @@ def plot_multi_sds_colour_rendering_indexes_bars(sds, **kwargs): :alt: plot_multi_sds_colour_rendering_indexes_bars """ - sds = sds_and_msds_to_sds(sds) + sds_converted = sds_and_msds_to_sds(sds) - settings = dict(kwargs) - settings.update({'standalone': False}) + settings: Dict[str, Any] = dict(kwargs) + settings.update({"standalone": False}) - specifications = [ - colour_rendering_index(sd, additional_data=True) for sd in sds - ] + specifications = cast( + List[ColourRendering_Specification_CRI], + [ + colour_rendering_index(sd, additional_data=True) + for sd in sds_converted + ], + ) # *colour rendering index* colorimetry data tristimulus values are # computed in [0, 100] domain however `plot_colour_quality_bars` expects @@ -290,58 +364,53 @@ def plot_multi_sds_colour_rendering_indexes_bars(sds, **kwargs): # colorimetry data tristimulus values domain. for specification in specifications: colorimetry_data = specification.colorimetry_data - for i, c_d in enumerate(colorimetry_data[0]): - colorimetry_data[0][i] = TCS_ColorimetryData( - c_d.name, c_d.XYZ / 100, c_d.uv, c_d.UVW) + for i in range(len(colorimetry_data[0])): + colorimetry_data[0][i].XYZ /= 100 _figure, axes = plot_colour_quality_bars(specifications, **settings) - title = 'Colour Rendering Index - {0}'.format(', '.join( - [sd.strict_name for sd in sds])) + title = ( + f"Colour Rendering Index - " + f"{', '.join([sd.strict_name for sd in sds_converted])}" + ) - settings = {'axes': axes, 'title': title} + settings = {"axes": axes, "title": title} settings.update(kwargs) return render(**settings) @override_style() -def plot_single_sd_colour_quality_scale_bars(sd, - method='NIST CQS 9.0', - **kwargs): +def plot_single_sd_colour_quality_scale_bars( + sd: SpectralDistribution, + method: Union[ + Literal["NIST CQS 7.4", "NIST CQS 9.0"], str + ] = "NIST CQS 9.0", + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the *Colour Quality Scale* (CQS) of given illuminant or light source + Plot the *Colour Quality Scale* (CQS) of given illuminant or light source spectral distribution. Parameters ---------- - sd : SpectralDistribution + sd Illuminant or light source spectral distribution to plot the *Colour Quality Scale* (CQS). - method : unicode, optional - **{NIST CQS 7.4'}**, + method *Colour Quality Scale* (CQS) computation method. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.quality.plot_colour_quality_bars`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - labels : bool, optional - {:func:`colour.plotting.quality.plot_colour_quality_bars`}, - Add labels above bars. - hatching : bool or None, optional - {:func:`colour.plotting.quality.plot_colour_quality_bars`}, - Use hatching for the bars. - hatching_repeat : int, optional - {:func:`colour.plotting.quality.plot_colour_quality_bars`}, - Hatching pattern repeat. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -358,49 +427,48 @@ def plot_single_sd_colour_quality_scale_bars(sd, :alt: plot_single_sd_colour_quality_scale_bars """ + method = validate_method(method, COLOUR_QUALITY_SCALE_METHODS) + return plot_multi_sds_colour_quality_scales_bars([sd], method, **kwargs) @override_style() -def plot_multi_sds_colour_quality_scales_bars(sds, - method='NIST CQS 9.0', - **kwargs): +def plot_multi_sds_colour_quality_scales_bars( + sds: Union[ + Sequence[Union[SpectralDistribution, MultiSpectralDistributions]], + MultiSpectralDistributions, + ], + method: Union[ + Literal["NIST CQS 7.4", "NIST CQS 9.0"], str + ] = "NIST CQS 9.0", + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the *Colour Quality Scale* (CQS) of given illuminants or light + Plot the *Colour Quality Scale* (CQS) of given illuminants or light sources spectral distributions. Parameters ---------- - sds : array_like or MultiSpectralDistributions + sds Spectral distributions or multi-spectral distributions to plot. `sds` can be a single :class:`colour.MultiSpectralDistributions` class instance, a list of :class:`colour.MultiSpectralDistributions` class instances or a list of :class:`colour.SpectralDistribution` class instances. - method : unicode, optional - **{NIST CQS 7.4'}**, + method *Colour Quality Scale* (CQS) computation method. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.quality.plot_colour_quality_bars`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - labels : bool, optional - {:func:`colour.plotting.quality.plot_colour_quality_bars`}, - Add labels above bars. - hatching : bool or None, optional - {:func:`colour.plotting.quality.plot_colour_quality_bars`}, - Use hatching for the bars. - hatching_repeat : int, optional - {:func:`colour.plotting.quality.plot_colour_quality_bars`}, - Hatching pattern repeat. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -419,19 +487,26 @@ def plot_multi_sds_colour_quality_scales_bars(sds, :alt: plot_multi_sds_colour_quality_scales_bars """ - sds = sds_and_msds_to_sds(sds) + method = validate_method(method, COLOUR_QUALITY_SCALE_METHODS) + + sds_converted = sds_and_msds_to_sds(sds) - settings = dict(kwargs) - settings.update({'standalone': False}) + settings: Dict[str, Any] = dict(kwargs) + settings.update({"standalone": False}) - specifications = [colour_quality_scale(sd, True, method) for sd in sds] + specifications = cast( + List[ColourRendering_Specification_CQS], + [colour_quality_scale(sd, True, method) for sd in sds_converted], + ) _figure, axes = plot_colour_quality_bars(specifications, **settings) - title = 'Colour Quality Scale - {0}'.format(', '.join( - [sd.strict_name for sd in sds])) + title = ( + f"Colour Quality Scale - " + f"{', '.join([sd.strict_name for sd in sds_converted])}" + ) - settings = {'axes': axes, 'title': title} + settings = {"axes": axes, "title": title} settings.update(kwargs) return render(**settings) diff --git a/colour/plotting/section.py b/colour/plotting/section.py new file mode 100644 index 0000000000..6e3a620e79 --- /dev/null +++ b/colour/plotting/section.py @@ -0,0 +1,805 @@ +""" +Gamut Section Plotting +====================== + +Defines the gamut section plotting objects: + +- :func:`colour.plotting.section.plot_hull_section_colours` +- :func:`colour.plotting.section.plot_hull_section_contour` +- :func:`colour.plotting.plot_visible_spectrum_section` +- :func:`colour.plotting.plot_RGB_colourspace_section` +""" + +from __future__ import annotations + +import matplotlib.pyplot as plt +import numpy as np +from matplotlib.collections import LineCollection +from matplotlib.patches import Polygon + +from colour.colorimetry import ( + MultiSpectralDistributions, + SpectralDistribution, + SpectralShape, + reshape_msds, +) +from colour.geometry import hull_section, primitive_cube +from colour.graph import convert +from colour.hints import ( + Any, + ArrayLike, + Boolean, + Dict, + Floating, + Integer, + Literal, + Optional, + Sequence, + Tuple, + Union, + cast, +) +from colour.models import ( + COLOURSPACE_MODELS_AXIS_LABELS, + COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE, + RGB_Colourspace, + RGB_to_XYZ, +) +from colour.notation import HEX_to_RGB +from colour.plotting import ( + CONSTANTS_COLOUR_STYLE, + XYZ_to_plotting_colourspace, + artist, + colourspace_model_axis_reorder, + filter_cmfs, + filter_RGB_colourspaces, + filter_illuminants, + override_style, + render, +) +from colour.volume import solid_RoschMacAdam +from colour.utilities import ( + CaseInsensitiveMapping, + as_int_array, + first_item, + full, + optional, + required, + suppress_warnings, + tstack, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "MAPPING_AXIS_TO_PLANE", + "plot_hull_section_colours", + "plot_hull_section_contour", + "plot_visible_spectrum_section", + "plot_RGB_colourspace_section", +] + +MAPPING_AXIS_TO_PLANE: CaseInsensitiveMapping = CaseInsensitiveMapping( + {"+x": (1, 2), "+y": (0, 2), "+z": (0, 1)} +) +MAPPING_AXIS_TO_PLANE.__doc__ = """Axis to plane mapping.""" + + +@required("trimesh") +@override_style() +def plot_hull_section_colours( + hull: trimesh.Trimesh, # type: ignore[name-defined] # noqa + model: Union[ + Literal[ + "CAM02LCD", + "CAM02SCD", + "CAM02UCS", + "CAM16LCD", + "CAM16SCD", + "CAM16UCS", + "CIE XYZ", + "CIE xyY", + "CIE Lab", + "CIE Luv", + "CIE UCS", + "CIE UVW", + "DIN99", + "Hunter Lab", + "Hunter Rdab", + "ICaCb", + "ICtCp", + "IPT", + "IgPgTg", + "Jzazbz", + "OSA UCS", + "Oklab", + "hdr-CIELAB", + "hdr-IPT", + ], + str, + ] = "CIE xyY", + axis: Union[Literal["+z", "+x", "+y"], str] = "+z", + origin: Floating = 0.5, + normalise: Boolean = True, + section_colours: Optional[Union[ArrayLike, str]] = None, + section_opacity: Floating = 1, + convert_kwargs: Optional[Dict] = None, + samples: Integer = 256, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: + """ + Plot the section colours of given *trimesh* hull along given axis and + origin. + + Parameters + ---------- + hull + *Trimesh* hull. + model + Colourspace model, see :attr:`colour.COLOURSPACE_MODELS` attribute for + the list of supported colourspace models. + axis + Axis the hull section will be normal to. + origin + Coordinate along ``axis`` at which to plot the hull section. + normalise + Whether to normalise ``axis`` to the extent of the hull along it. + section_colours + Colours of the hull section, if ``section_colours`` is set to *RGB*, + the colours will be computed according to the corresponding + coordinates. + section_opacity + Opacity of the hull section colours. + convert_kwargs + Keyword arguments for the :func:`colour.convert` definition. + samples + Samples count on one axis when computing the hull section colours. + + Other Parameters + ---------------- + kwargs + {:func:`colour.plotting.artist`, + :func:`colour.plotting.render`}, + See the documentation of the previously listed definitions. + + Returns + ------- + :class:`tuple` + Current figure and axes. + + Examples + -------- + >>> from colour.models import RGB_COLOURSPACE_sRGB + >>> from colour.utilities import is_trimesh_installed + >>> vertices, faces, _outline = primitive_cube(1, 1, 1, 64, 64, 64) + >>> XYZ_vertices = RGB_to_XYZ( + ... vertices['position'] + 0.5, + ... RGB_COLOURSPACE_sRGB.whitepoint, + ... RGB_COLOURSPACE_sRGB.whitepoint, + ... RGB_COLOURSPACE_sRGB.matrix_RGB_to_XYZ, + ... ) + >>> if is_trimesh_installed: + ... import trimesh + ... hull = trimesh.Trimesh(XYZ_vertices, faces, process=False) + ... plot_hull_section_colours(hull, section_colours='RGB') + ... # doctest: +ELLIPSIS + (
, <...AxesSubplot...>) + + .. image:: ../_static/Plotting_Plot_Hull_Section_Colours.png + :align: center + :alt: plot_hull_section_colours + """ + + axis = validate_method( + axis, + ["+z", "+x", "+y"], + '"{0}" axis is invalid, it must be one of {1}!', + ) + + hull = hull.copy() + + settings: Dict[str, Any] = {"uniform": True} + settings.update(kwargs) + + _figure, axes = artist(**settings) + + section_colours = cast( + ArrayLike, + optional( + section_colours, HEX_to_RGB(CONSTANTS_COLOUR_STYLE.colour.average) + ), + ) + + convert_kwargs = optional(convert_kwargs, {}) + + # Luminance / Lightness reordered along "z" axis. + with suppress_warnings(python_warnings=True): + ijk_vertices = colourspace_model_axis_reorder( + convert(hull.vertices, "CIE XYZ", model, **convert_kwargs), model + ) + ijk_vertices = np.nan_to_num(ijk_vertices) + ijk_vertices *= COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[ + model + ] + + hull.vertices = ijk_vertices + + if axis == "+x": + index_origin = 0 + elif axis == "+y": + index_origin = 1 + elif axis == "+z": + index_origin = 2 + plane = MAPPING_AXIS_TO_PLANE[axis] + + section = hull_section(hull, axis, origin, normalise) + + padding = 0.1 * np.mean( + COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[model] + ) + min_x = np.min(ijk_vertices[..., plane[0]]) - padding + max_x = np.max(ijk_vertices[..., plane[0]]) + padding + min_y = np.min(ijk_vertices[..., plane[1]]) - padding + max_y = np.max(ijk_vertices[..., plane[1]]) + padding + extent = (min_x, max_x, min_y, max_y) + + use_RGB_section_colours = str(section_colours).upper() == "RGB" + if use_RGB_section_colours: + ii, jj = np.meshgrid( + np.linspace(min_x, max_x, samples), + np.linspace(max_y, min_y, samples), + ) + ij = tstack([ii, jj]) + ijk_section = full( + (samples, samples, 3), np.median(section[..., index_origin]) + ) + ijk_section[..., plane] = ij + ijk_section /= COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[ + model + ] + XYZ_section = convert( + colourspace_model_axis_reorder(ijk_section, model, "Inverse"), + model, + "CIE XYZ", + **convert_kwargs, + ) + RGB_section = XYZ_to_plotting_colourspace(XYZ_section) + else: + section_colours = np.hstack([section_colours, section_opacity]) + + facecolor = "none" if use_RGB_section_colours else section_colours + polygon = Polygon( + section[..., plane], + facecolor=facecolor, + edgecolor="none", + zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, + ) + axes.add_patch(polygon) + if use_RGB_section_colours: + image = axes.imshow( + np.clip(RGB_section, 0, 1), + interpolation="bilinear", + extent=extent, + clip_path=None, + alpha=section_opacity, + zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, + ) + image.set_clip_path(polygon) + + settings = { + "axes": axes, + "bounding_box": extent, + } + settings.update(kwargs) + + return render(**settings) + + +@required("trimesh") +@override_style() +def plot_hull_section_contour( + hull: trimesh.Trimesh, # type: ignore[name-defined] # noqa + model: Union[ + Literal[ + "CAM02LCD", + "CAM02SCD", + "CAM02UCS", + "CAM16LCD", + "CAM16SCD", + "CAM16UCS", + "CIE XYZ", + "CIE xyY", + "CIE Lab", + "CIE Luv", + "CIE UCS", + "CIE UVW", + "DIN99", + "Hunter Lab", + "Hunter Rdab", + "ICaCb", + "ICtCp", + "IPT", + "IgPgTg", + "Jzazbz", + "OSA UCS", + "Oklab", + "hdr-CIELAB", + "hdr-IPT", + ], + str, + ] = "CIE xyY", + axis: Union[Literal["+z", "+x", "+y"], str] = "+z", + origin: Floating = 0.5, + normalise: Boolean = True, + contour_colours: Optional[Union[ArrayLike, str]] = None, + contour_opacity: Floating = 1, + convert_kwargs: Optional[Dict] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: + """ + Plot the section contour of given *trimesh* hull along given axis and + origin. + + Parameters + ---------- + hull + *Trimesh* hull. + model + Colourspace model, see :attr:`colour.COLOURSPACE_MODELS` attribute for + the list of supported colourspace models. + axis + Axis the hull section will be normal to. + origin + Coordinate along ``axis`` at which to plot the hull section. + normalise + Whether to normalise ``axis`` to the extent of the hull along it. + contour_colours + Colours of the hull section contour, if ``contour_colours`` is set to + *RGB*, the colours will be computed according to the corresponding + coordinates. + contour_opacity + Opacity of the hull section contour. + convert_kwargs + Keyword arguments for the :func:`colour.convert` definition. + + Other Parameters + ---------------- + kwargs + {:func:`colour.plotting.artist`, + :func:`colour.plotting.render`}, + See the documentation of the previously listed definitions. + + Returns + ------- + :class:`tuple` + Current figure and axes. + + Examples + -------- + >>> from colour.models import RGB_COLOURSPACE_sRGB + >>> from colour.utilities import is_trimesh_installed + >>> vertices, faces, _outline = primitive_cube(1, 1, 1, 64, 64, 64) + >>> XYZ_vertices = RGB_to_XYZ( + ... vertices['position'] + 0.5, + ... RGB_COLOURSPACE_sRGB.whitepoint, + ... RGB_COLOURSPACE_sRGB.whitepoint, + ... RGB_COLOURSPACE_sRGB.matrix_RGB_to_XYZ, + ... ) + >>> if is_trimesh_installed: + ... import trimesh + ... hull = trimesh.Trimesh(XYZ_vertices, faces, process=False) + ... plot_hull_section_contour(hull, contour_colours='RGB') + ... # doctest: +ELLIPSIS + (
, <...AxesSubplot...>) + + .. image:: ../_static/Plotting_Plot_Hull_Section_Contour.png + :align: center + :alt: plot_hull_section_contour + """ + + hull = hull.copy() + + contour_colours = cast( + Union[ArrayLike, str], + optional(contour_colours, CONSTANTS_COLOUR_STYLE.colour.dark), + ) + + settings: Dict[str, Any] = {"uniform": True} + settings.update(kwargs) + + _figure, axes = artist(**settings) + + convert_kwargs = optional(convert_kwargs, {}) + + # Luminance / Lightness is re-ordered along "z-up" axis. + with suppress_warnings(python_warnings=True): + ijk_vertices = colourspace_model_axis_reorder( + convert(hull.vertices, "CIE XYZ", model, **convert_kwargs), model + ) + ijk_vertices = np.nan_to_num(ijk_vertices) + ijk_vertices *= COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[ + model + ] + + hull.vertices = ijk_vertices + + plane = MAPPING_AXIS_TO_PLANE[axis] + + padding = 0.1 * np.mean( + COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[model] + ) + min_x = np.min(ijk_vertices[..., plane[0]]) - padding + max_x = np.max(ijk_vertices[..., plane[0]]) + padding + min_y = np.min(ijk_vertices[..., plane[1]]) - padding + max_y = np.max(ijk_vertices[..., plane[1]]) + padding + extent = (min_x, max_x, min_y, max_y) + + use_RGB_contour_colours = str(contour_colours).upper() == "RGB" + section = hull_section(hull, axis, origin, normalise) + if use_RGB_contour_colours: + ijk_section = section / ( + COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[model] + ) + XYZ_section = convert( + colourspace_model_axis_reorder(ijk_section, model, "Inverse"), + model, + "CIE XYZ", + **convert_kwargs, + ) + contour_colours = np.clip( + XYZ_to_plotting_colourspace(XYZ_section), 0, 1 + ) + + section = np.reshape(section[..., plane], (-1, 1, 2)) + line_collection = LineCollection( + np.concatenate([section[:-1], section[1:]], axis=1), + colors=contour_colours, + alpha=contour_opacity, + zorder=CONSTANTS_COLOUR_STYLE.zorder.background_line, + ) + axes.add_collection(line_collection) + + settings = { + "axes": axes, + "bounding_box": extent, + } + settings.update(kwargs) + + return render(**settings) + + +@required("trimesh") +@override_style() +def plot_visible_spectrum_section( + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + illuminant: Union[SpectralDistribution, str] = "D65", + model: Union[ + Literal[ + "CAM02LCD", + "CAM02SCD", + "CAM02UCS", + "CAM16LCD", + "CAM16SCD", + "CAM16UCS", + "CIE XYZ", + "CIE xyY", + "CIE Lab", + "CIE Luv", + "CIE UCS", + "CIE UVW", + "DIN99", + "Hunter Lab", + "Hunter Rdab", + "ICaCb", + "ICtCp", + "IPT", + "IgPgTg", + "Jzazbz", + "OSA UCS", + "Oklab", + "hdr-CIELAB", + "hdr-IPT", + ], + str, + ] = "CIE xyY", + axis: Union[Literal["+z", "+x", "+y"], str] = "+z", + origin: Floating = 0.5, + normalise: Boolean = True, + show_section_colours: Boolean = True, + show_section_contour: Boolean = True, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: + """ + Plot the visible spectrum volume, i.e. *Rösch-MacAdam* colour solid, + section colours along given axis and origin. + + Parameters + ---------- + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. ``cmfs`` can be of any type or + form supported by the :func:`colour.plotting.filter_cmfs` definition. + illuminant + Illuminant spectral distribution, default to *CIE Illuminant D65*. + ``illuminant`` can be of any type or form supported by the + :func:`colour.plotting.filter_illuminants` definition. + model + Colourspace model, see :attr:`colour.COLOURSPACE_MODELS` attribute for + the list of supported colourspace models. + axis + Axis the hull section will be normal to. + origin + Coordinate along ``axis`` at which to plot the hull section. + normalise + Whether to normalise ``axis`` to the extent of the hull along it. + show_section_colours + Whether to show the hull section colours. + show_section_contour + Whether to show the hull section contour. + + Other Parameters + ---------------- + kwargs + {:func:`colour.plotting.artist`, + :func:`colour.plotting.render`, + :func:`colour.plotting.section.plot_hull_section_colours` + :func:`colour.plotting.section.plot_hull_section_contour`}, + See the documentation of the previously listed definitions. + + Returns + ------- + :class:`tuple` + Current figure and axes. + + Examples + -------- + >>> from colour.utilities import is_trimesh_installed + >>> if is_trimesh_installed: + ... plot_visible_spectrum_section( + ... section_colours='RGB', section_opacity=0.15) + ... # doctest: +ELLIPSIS + (
, <...AxesSubplot...>) + + .. image:: ../_static/Plotting_Plot_Visible_Spectrum_Section.png + :align: center + :alt: plot_visible_spectrum_section + """ + + import trimesh + + settings: Dict[str, Any] = {"uniform": True} + settings.update(kwargs) + + _figure, axes = artist(**settings) + + # pylint: disable=E1102 + cmfs = reshape_msds( + first_item(filter_cmfs(cmfs).values()), SpectralShape(360, 780, 1) + ) + illuminant = cast( + SpectralDistribution, + first_item(filter_illuminants(illuminant).values()), + ) + + vertices = solid_RoschMacAdam( + cmfs, + illuminant, + point_order="Pulse Wave Width", + filter_jagged_points=True, + ) + mesh = trimesh.Trimesh(vertices) + hull = trimesh.convex.convex_hull(mesh) + + if show_section_colours: + settings = {"axes": axes} + settings.update(kwargs) + settings["standalone"] = False + + plot_hull_section_colours( + hull, model, axis, origin, normalise, **settings + ) + + if show_section_contour: + settings = {"axes": axes} + settings.update(kwargs) + settings["standalone"] = False + + plot_hull_section_contour( + hull, model, axis, origin, normalise, **settings + ) + + title = ( + f"Visible Spectrum Section - " + f"{f'{origin * 100}%' if normalise else origin} - " + f"{model} - " + f"{cmfs.strict_name}" + ) + + plane = MAPPING_AXIS_TO_PLANE[axis] + + labels = np.array(COLOURSPACE_MODELS_AXIS_LABELS[model])[ + as_int_array(colourspace_model_axis_reorder([0, 1, 2], model)) + ] + x_label, y_label = labels[plane[0]], labels[plane[1]] + + settings.update( + { + "axes": axes, + "standalone": True, + "title": title, + "x_label": x_label, + "y_label": y_label, + } + ) + settings.update(kwargs) + + return render(**settings) + + +@required("trimesh") +@override_style() +def plot_RGB_colourspace_section( + colourspace: Union[ + RGB_Colourspace, str, Sequence[Union[RGB_Colourspace, str]] + ], + model: Union[ + Literal[ + "CAM02LCD", + "CAM02SCD", + "CAM02UCS", + "CAM16LCD", + "CAM16SCD", + "CAM16UCS", + "CIE XYZ", + "CIE xyY", + "CIE Lab", + "CIE Luv", + "CIE UCS", + "CIE UVW", + "DIN99", + "Hunter Lab", + "Hunter Rdab", + "ICaCb", + "ICtCp", + "IPT", + "IgPgTg", + "Jzazbz", + "OSA UCS", + "Oklab", + "hdr-CIELAB", + "hdr-IPT", + ], + str, + ] = "CIE xyY", + axis: Union[Literal["+z", "+x", "+y"], str] = "+z", + origin: Floating = 0.5, + normalise: Boolean = True, + show_section_colours: Boolean = True, + show_section_contour: Boolean = True, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: + """ + Plot given *RGB* colourspace section colours along given axis and origin. + + Parameters + ---------- + colourspace + *RGB* colourspace of the *RGB* array. ``colourspace`` can be of any + type or form supported by the + :func:`colour.plotting.filter_RGB_colourspaces` definition. + model + Colourspace model, see :attr:`colour.COLOURSPACE_MODELS` attribute for + the list of supported colourspace models. + axis + Axis the hull section will be normal to. + origin + Coordinate along ``axis`` at which to plot the hull section. + normalise + Whether to normalise ``axis`` to the extent of the hull along it. + show_section_colours + Whether to show the hull section colours. + show_section_contour + Whether to show the hull section contour. + + Other Parameters + ---------------- + kwargs + {:func:`colour.plotting.artist`, + :func:`colour.plotting.render`, + :func:`colour.plotting.section.plot_hull_section_colours` + :func:`colour.plotting.section.plot_hull_section_contour`}, + See the documentation of the previously listed definitions. + + Returns + ------- + :class:`tuple` + Current figure and axes. + + Examples + -------- + >>> from colour.utilities import is_trimesh_installed + >>> if is_trimesh_installed: + ... plot_RGB_colourspace_section( + ... 'sRGB', section_colours='RGB', section_opacity=0.15) + ... # doctest: +ELLIPSIS + (
, <...AxesSubplot...>) + + .. image:: ../_static/Plotting_Plot_RGB_Colourspace_Section.png + :align: center + :alt: plot_RGB_colourspace_section + """ + + import trimesh + + settings: Dict[str, Any] = {"uniform": True} + settings.update(kwargs) + + _figure, axes = artist(**settings) + + colourspace = cast( + RGB_Colourspace, + first_item(filter_RGB_colourspaces(colourspace).values()), + ) + + vertices, faces, _outline = primitive_cube(1, 1, 1, 64, 64, 64) + XYZ_vertices = RGB_to_XYZ( + vertices["position"] + 0.5, + colourspace.whitepoint, + colourspace.whitepoint, + colourspace.matrix_RGB_to_XYZ, + ) + hull = trimesh.Trimesh(XYZ_vertices, faces, process=False) + + if show_section_colours: + settings = {"axes": axes} + settings.update(kwargs) + settings["standalone"] = False + + plot_hull_section_colours( + hull, model, axis, origin, normalise, **settings + ) + + if show_section_contour: + settings = {"axes": axes} + settings.update(kwargs) + settings["standalone"] = False + + plot_hull_section_contour( + hull, model, axis, origin, normalise, **settings + ) + + title = ( + f"{colourspace.name} Section - " + f"{f'{origin * 100}%' if normalise else origin} - " + f"{model}" + ) + + plane = MAPPING_AXIS_TO_PLANE[axis] + + labels = np.array(COLOURSPACE_MODELS_AXIS_LABELS[model])[ + as_int_array(colourspace_model_axis_reorder([0, 1, 2], model)) + ] + x_label, y_label = labels[plane[0]], labels[plane[1]] + + settings.update( + { + "axes": axes, + "standalone": True, + "title": title, + "x_label": x_label, + "y_label": y_label, + } + ) + settings.update(kwargs) + + return render(**settings) diff --git a/colour/plotting/temperature.py b/colour/plotting/temperature.py index 3f5570b2f9..fc0ca9a880 100644 --- a/colour/plotting/temperature.py +++ b/colour/plotting/temperature.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Colour Temperature & Correlated Colour Temperature Plotting =========================================================== @@ -12,66 +11,117 @@ plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS` """ -from __future__ import division +from __future__ import annotations +import matplotlib.pyplot as plt import numpy as np +from matplotlib.collections import LineCollection +from colour.algebra import normalise_maximum from colour.colorimetry import MSDS_CMFS, CCS_ILLUMINANTS -from colour.models import (UCS_uv_to_xy, XYZ_to_UCS, UCS_to_uv, xy_to_XYZ) +from colour.hints import ( + Any, + ArrayLike, + Callable, + Dict, + Floating, + List, + Literal, + NDArray, + Optional, + Sequence, + Tuple, + Union, + cast, +) +from colour.models import ( + UCS_to_uv, + UCS_uv_to_xy, + XYZ_to_UCS, + xy_to_Luv_uv, + xy_to_XYZ, +) from colour.temperature import CCT_to_uv from colour.plotting import ( - CONSTANTS_COLOUR_STYLE, CONSTANTS_ARROW_STYLE, artist, - plot_chromaticity_diagram_CIE1931, plot_chromaticity_diagram_CIE1960UCS, - filter_passthrough, override_style, render, update_settings_collection) + CONSTANTS_COLOUR_STYLE, + CONSTANTS_ARROW_STYLE, + XYZ_to_plotting_colourspace, + artist, + plot_chromaticity_diagram_CIE1931, + plot_chromaticity_diagram_CIE1960UCS, + filter_passthrough, + override_style, + render, + update_settings_collection, +) from colour.plotting.diagrams import plot_chromaticity_diagram -from colour.utilities import tstack, zeros -from colour.utilities.deprecation import handle_arguments_deprecation - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.utilities import ( + as_int_scalar, + full, + optional, + tstack, + validate_method, + zeros, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'plot_planckian_locus', 'plot_planckian_locus_CIE1931', - 'plot_planckian_locus_CIE1960UCS', - 'plot_planckian_locus_in_chromaticity_diagram', - 'plot_planckian_locus_in_chromaticity_diagram_CIE1931', - 'plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS' + "plot_planckian_locus", + "plot_planckian_locus_in_chromaticity_diagram", + "plot_planckian_locus_in_chromaticity_diagram_CIE1931", + "plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS", ] @override_style() -def plot_planckian_locus(planckian_locus_colours=None, - method='CIE 1931', - **kwargs): +def plot_planckian_locus( + planckian_locus_colours: Optional[Union[ArrayLike, str]] = None, + planckian_locus_opacity: Floating = 1, + planckian_locus_labels: Optional[Sequence] = None, + method: Union[ + Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"], str + ] = "CIE 1931", + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the *Planckian Locus* according to given method. + Plot the *Planckian Locus* according to given method. Parameters ---------- - planckian_locus_colours : array_like or unicode, optional - *Planckian Locus* colours. - method : unicode, optional - **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**, + planckian_locus_colours + Colours of the *Planckian Locus*, if ``planckian_locus_colours`` is set + to *RGB*, the colours will be computed according to the corresponding + chromaticity coordinates. + planckian_locus_opacity + Opacity of the *Planckian Locus*. + planckian_locus_labels + Array of labels used to customise which iso-temperature lines will be + drawn along the *Planckian Locus*. Passing an empty array will result + in no iso-temperature lines being drawn. + method *Chromaticity Diagram* method. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples -------- - >>> plot_planckian_locus() # doctest: +ELLIPSIS + >>> plot_planckian_locus(planckian_locus_colours='RGB') + ... # doctest: +ELLIPSIS (
, <...AxesSubplot...>) .. image:: ../_static/Plotting_Plot_Planckian_Locus.png @@ -79,193 +129,180 @@ def plot_planckian_locus(planckian_locus_colours=None, :alt: plot_planckian_locus """ - if planckian_locus_colours is None: - planckian_locus_colours = CONSTANTS_COLOUR_STYLE.colour.dark + method = validate_method( + method, ["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] + ) + + planckian_locus_colours = optional( + planckian_locus_colours, CONSTANTS_COLOUR_STYLE.colour.dark + ) - settings = {'uniform': True} + labels = cast( + Tuple, + optional( + planckian_locus_labels, + (10**6 / 600, 2000, 2500, 3000, 4000, 6000, 10**6 / 100), + ), + ) + D_uv = 0.05 + + settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) - if method == 'CIE 1931': + if method == "cie 1931": - def uv_to_ij(uv): + def uv_to_ij(uv: NDArray) -> NDArray: """ - Converts given *uv* chromaticity coordinates to *ij* chromaticity + Convert given *uv* chromaticity coordinates to *ij* chromaticity coordinates. """ return UCS_uv_to_xy(uv) - D_uv = 0.025 - elif method == 'CIE 1960 UCS': + elif method == "cie 1960 ucs": - def uv_to_ij(uv): + def uv_to_ij(uv: NDArray) -> NDArray: """ - Converts given *uv* chromaticity coordinates to *ij* chromaticity + Convert given *uv* chromaticity coordinates to *ij* chromaticity coordinates. """ return uv - D_uv = 0.025 - else: - raise ValueError('Invalid method: "{0}", must be one of ' - '[\'CIE 1931\', \'CIE 1960 UCS\']'.format(method)) + elif method == "cie 1976 ucs": - start, end = 1667, 100000 - CCT = np.arange(start, end + 250, 250) - CCT_D_uv = tstack([CCT, zeros(CCT.shape)]) - ij = uv_to_ij(CCT_to_uv(CCT_D_uv, 'Robertson 1968')) - - axes.plot(ij[..., 0], ij[..., 1], color=planckian_locus_colours) + def uv_to_ij(uv: NDArray) -> NDArray: + """ + Convert given *uv* chromaticity coordinates to *ij* chromaticity + coordinates. + """ - for i in (1667, 2000, 2500, 3000, 4000, 6000, 10000): - i0, j0 = uv_to_ij(CCT_to_uv(np.array([i, -D_uv]), 'Robertson 1968')) - i1, j1 = uv_to_ij(CCT_to_uv(np.array([i, D_uv]), 'Robertson 1968')) - axes.plot((i0, i1), (j0, j1), color=planckian_locus_colours) + return xy_to_Luv_uv(UCS_uv_to_xy(uv)) + + def CCT_D_uv_to_plotting_colourspace(CCT_D_uv): + """ + Convert given *uv* chromaticity coordinates to the default plotting + colourspace. + """ + + return normalise_maximum( + XYZ_to_plotting_colourspace( + xy_to_XYZ(UCS_uv_to_xy(CCT_to_uv(CCT_D_uv, "Robertson 1968"))) + ), + axis=-1, + ) + + start, end = 10**6 / 600, 10**6 / 10 + CCT = np.arange(start, end + 100, 100) + CCT_D_uv = np.reshape(tstack([CCT, zeros(CCT.shape)]), (-1, 1, 2)) + ij = uv_to_ij(CCT_to_uv(CCT_D_uv, "Robertson 1968")) + + use_RGB_planckian_locus_colours = ( + str(planckian_locus_colours).upper() == "RGB" + ) + if use_RGB_planckian_locus_colours: + pl_colours = CCT_D_uv_to_plotting_colourspace(CCT_D_uv) + else: + pl_colours = planckian_locus_colours + + line_collection = LineCollection( + np.concatenate([ij[:-1], ij[1:]], axis=1), + colors=pl_colours, + alpha=planckian_locus_opacity, + zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_line, + ) + axes.add_collection(line_collection) + + for label in labels: + CCT_D_uv = np.reshape( + tstack([full(10, label), np.linspace(-D_uv, D_uv, 10)]), (-1, 1, 2) + ) + + if use_RGB_planckian_locus_colours: + itl_colours = CCT_D_uv_to_plotting_colourspace(CCT_D_uv) + else: + itl_colours = planckian_locus_colours + + ij = uv_to_ij(CCT_to_uv(CCT_D_uv, "Robertson 1968")) + + line_collection = LineCollection( + np.concatenate([ij[:-1], ij[1:]], axis=1), + colors=itl_colours, + alpha=planckian_locus_opacity, + zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_line, + ) + axes.add_collection(line_collection) axes.annotate( - '{0}K'.format(i), - xy=(i0, j0), - xytext=(0, -10), - textcoords='offset points', - size='x-small') - - settings = {'axes': axes} + f"{as_int_scalar(label)}K", + xy=(ij[-1, :, 0], ij[-1, :, 1]), + xytext=(0, CONSTANTS_COLOUR_STYLE.geometry.long / 2), + textcoords="offset points", + size="x-small", + zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_label, + ) + + settings = {"axes": axes} settings.update(kwargs) return render(**settings) -@override_style() -def plot_planckian_locus_CIE1931(planckian_locus_colours=None, **kwargs): - """ - Plots the *Planckian Locus* according to *CIE 1931* method. - - Parameters - ---------- - planckian_locus_colours : array_like or unicode, optional - *Planckian Locus* colours. - - Other Parameters - ---------------- - \\**kwargs : dict, optional - {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - - Returns - ------- - tuple - Current figure and axes. - - Examples - -------- - >>> plot_planckian_locus_CIE1931() # doctest: +ELLIPSIS - (
, <...AxesSubplot...>) - - .. image:: ../_static/Plotting_Plot_Planckian_Locus_CIE1931.png - :align: center - :alt: plot_planckian_locus_CIE1931 - """ - - settings = dict(kwargs) - settings.update({'method': 'CIE 1931'}) - - return plot_planckian_locus(planckian_locus_colours, **settings) - - -@override_style() -def plot_planckian_locus_CIE1960UCS(planckian_locus_colours=None, **kwargs): - """ - Plots the *Planckian Locus* according to *CIE 1960 UCS* method. - - Parameters - ---------- - planckian_locus_colours : array_like or unicode, optional - *Planckian Locus* colours. - - Other Parameters - ---------------- - \\**kwargs : dict, optional - {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - - Returns - ------- - tuple - Current figure and axes. - - Examples - -------- - >>> plot_planckian_locus_CIE1960UCS() # doctest: +ELLIPSIS - (
, <...AxesSubplot...>) - - .. image:: ../_static/Plotting_Plot_Planckian_Locus_CIE1960UCS.png - :align: center - :alt: plot_planckian_locus_CIE1960UCS - """ - - settings = dict(kwargs) - settings.update({'method': 'CIE 1960 UCS'}) - - return plot_planckian_locus(planckian_locus_colours, **settings) - - @override_style() def plot_planckian_locus_in_chromaticity_diagram( - illuminants, - chromaticity_diagram_callable=plot_chromaticity_diagram, - planckian_locus_callable=plot_planckian_locus, - method='CIE 1931', - annotate_kwargs=None, - plot_kwargs=None, - **kwargs): + illuminants: Union[str, Sequence[str]], + chromaticity_diagram_callable: Callable = ( + plot_chromaticity_diagram # type: ignore[has-type] + ), + method: Union[Literal["CIE 1931", "CIE 1960 UCS"], str] = "CIE 1931", + annotate_kwargs: Optional[Union[Dict, List[Dict]]] = None, + plot_kwargs: Optional[Union[Dict, List[Dict]]] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the *Planckian Locus* and given illuminants in the + Plot the *Planckian Locus* and given illuminants in the *Chromaticity Diagram* according to given method. Parameters ---------- - illuminants : unicode or object or array_like + illuminants Illuminants to plot. ``illuminants`` elements can be of any type or form supported by the :func:`colour.plotting.filter_passthrough` definition. - chromaticity_diagram_callable : callable, optional + chromaticity_diagram_callable Callable responsible for drawing the *Chromaticity Diagram*. - planckian_locus_callable : callable, optional - Callable responsible for drawing the *Planckian Locus*. - method : unicode, optional - **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**, + method *Chromaticity Diagram* method. - annotate_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.annotate` definition, used to - annotate the resulting chromaticity coordinates with their respective - illuminant names. ``annotate_kwargs`` can be either a single dictionary - applied to all the arrows with same settings or a sequence of - dictionaries with different settings for each illuminant. - The following special keyword arguments can also be used: - - - *annotate* : bool, whether to annotate the illuminants. - plot_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.plot` definition, used to control - the style of the plotted illuminants. ``plot_kwargs`` can be either a - single dictionary applied to all the plotted illuminants with same + annotate_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.annotate` + definition, used to annotate the resulting chromaticity coordinates + with their respective spectral distribution names. ``annotate_kwargs`` + can be either a single dictionary applied to all the arrows with same settings or a sequence of dictionaries with different settings for each - plotted illuminant. + spectral distribution. The following special keyword arguments can also + be used: + + - ``annotate`` : Whether to annotate the spectral distributions. + plot_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.plot` definition, + used to control the style of the plotted illuminants. ``plot_kwargs`` + can be either a single dictionary applied to all the plotted + illuminants with the same settings or a sequence of dictionaries with + different settings for eachplotted illuminant. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.temperature.plot_planckian_locus`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - Also handles keywords arguments for deprecation management. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -295,46 +332,43 @@ def plot_planckian_locus_in_chromaticity_diagram( :alt: plot_planckian_locus_in_chromaticity_diagram """ - annotate_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['annotate_parameters', 'annotate_kwargs']], - }, **kwargs).get('annotate_kwargs', annotate_kwargs) + cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] - cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] + illuminants_filtered = filter_passthrough( + CCS_ILLUMINANTS.get(cmfs.name), illuminants # type: ignore[arg-type] + ) - illuminants = filter_passthrough( - CCS_ILLUMINANTS.get(cmfs.name), illuminants) - - settings = {'uniform': True} + settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) method = method.upper() - settings = {'axes': axes, 'method': method} + settings = {"axes": axes, "method": method} settings.update(kwargs) - settings['standalone'] = False + settings["standalone"] = False chromaticity_diagram_callable(**settings) - planckian_locus_callable(**settings) + plot_planckian_locus(**settings) - if method == 'CIE 1931': + if method == "CIE 1931": - def xy_to_ij(xy): + def xy_to_ij(xy: NDArray) -> NDArray: """ - Converts given *CIE xy* chromaticity coordinates to *ij* + Convert given *CIE xy* chromaticity coordinates to *ij* chromaticity coordinates. """ return xy bounding_box = (-0.1, 0.9, -0.1, 0.9) - elif method == 'CIE 1960 UCS': + elif method == "CIE 1960 UCS": - def xy_to_ij(xy): + def xy_to_ij(xy: NDArray) -> NDArray: """ - Converts given *CIE xy* chromaticity coordinates to *ij* + Convert given *CIE xy* chromaticity coordinates to *ij* chromaticity coordinates. """ @@ -342,65 +376,84 @@ def xy_to_ij(xy): bounding_box = (-0.1, 0.7, -0.2, 0.6) else: - raise ValueError('Invalid method: "{0}", must be one of ' - '[\'CIE 1931\', \'CIE 1960 UCS\']'.format(method)) - - annotate_settings_collection = [{ - 'annotate': True, - 'xytext': (-50, 30), - 'textcoords': 'offset points', - 'arrowprops': CONSTANTS_ARROW_STYLE, - } for _ in range(len(illuminants))] + raise ValueError( + f'Invalid method: "{method}", must be one of ' + f'["CIE 1931", "CIE 1960 UCS"]' + ) + + annotate_settings_collection = [ + { + "annotate": True, + "xytext": (-50, 30), + "textcoords": "offset points", + "arrowprops": CONSTANTS_ARROW_STYLE, + "zorder": CONSTANTS_COLOUR_STYLE.zorder.foreground_annotation, + } + for _ in range(len(illuminants_filtered)) + ] if annotate_kwargs is not None: - update_settings_collection(annotate_settings_collection, - annotate_kwargs, len(illuminants)) - - plot_settings_collection = [{ - 'color': - CONSTANTS_COLOUR_STYLE.colour.brightest, - 'label': - '{0}'.format(illuminant), - 'marker': - 'o', - 'markeredgecolor': - CONSTANTS_COLOUR_STYLE.colour.dark, - 'markeredgewidth': - CONSTANTS_COLOUR_STYLE.geometry.short * 0.75, - 'markersize': (CONSTANTS_COLOUR_STYLE.geometry.short * 6 + - CONSTANTS_COLOUR_STYLE.geometry.short * 0.75), - } for illuminant in illuminants] + update_settings_collection( + annotate_settings_collection, + annotate_kwargs, + len(illuminants_filtered), + ) + + plot_settings_collection = [ + { + "color": CONSTANTS_COLOUR_STYLE.colour.brightest, + "label": f"{illuminant}", + "marker": "o", + "markeredgecolor": CONSTANTS_COLOUR_STYLE.colour.dark, + "markeredgewidth": CONSTANTS_COLOUR_STYLE.geometry.short * 0.75, + "markersize": ( + CONSTANTS_COLOUR_STYLE.geometry.short * 6 + + CONSTANTS_COLOUR_STYLE.geometry.short * 0.75 + ), + "zorder": CONSTANTS_COLOUR_STYLE.zorder.foreground_line, + } + for illuminant in illuminants_filtered + ] if plot_kwargs is not None: - update_settings_collection(plot_settings_collection, plot_kwargs, - len(illuminants)) + update_settings_collection( + plot_settings_collection, plot_kwargs, len(illuminants_filtered) + ) - for i, (illuminant, xy) in enumerate(illuminants.items()): + for i, (illuminant, xy) in enumerate(illuminants_filtered.items()): plot_settings = plot_settings_collection[i] ij = xy_to_ij(xy) axes.plot(ij[0], ij[1], **plot_settings) - if annotate_settings_collection[i]['annotate']: + if annotate_settings_collection[i]["annotate"]: annotate_settings = annotate_settings_collection[i] - annotate_settings.pop('annotate') + annotate_settings.pop("annotate") axes.annotate(illuminant, xy=ij, **annotate_settings) - title = (('{0} Illuminants - Planckian Locus\n' - '{1} Chromaticity Diagram - ' - 'CIE 1931 2 Degree Standard Observer').format( - ', '.join(illuminants), method) if illuminants else - ('Planckian Locus\n{0} Chromaticity Diagram - ' - 'CIE 1931 2 Degree Standard Observer'.format(method))) - - settings.update({ - 'axes': axes, - 'standalone': True, - 'bounding_box': bounding_box, - 'title': title, - }) + title = ( + ( + f"{', '.join(illuminants_filtered)} Illuminants - Planckian Locus\n" + f"{method.upper()} Chromaticity Diagram - " + "CIE 1931 2 Degree Standard Observer" + ) + if illuminants_filtered + else ( + f"Planckian Locus\n{method.upper()} Chromaticity Diagram - " + f"CIE 1931 2 Degree Standard Observer" + ) + ) + + settings.update( + { + "axes": axes, + "standalone": True, + "bounding_box": bounding_box, + "title": title, + } + ) settings.update(kwargs) return render(**settings) @@ -408,59 +461,57 @@ def xy_to_ij(xy): @override_style() def plot_planckian_locus_in_chromaticity_diagram_CIE1931( - illuminants, - chromaticity_diagram_callable_CIE1931=( - plot_chromaticity_diagram_CIE1931), - planckian_locus_callable_CIE1931=plot_planckian_locus_CIE1931, - annotate_kwargs=None, - plot_kwargs=None, - **kwargs): + illuminants: Union[str, Sequence[str]], + chromaticity_diagram_callable_CIE1931: Callable = ( + plot_chromaticity_diagram_CIE1931 # type: ignore[has-type] + ), + annotate_kwargs: Optional[Union[Dict, List[Dict]]] = None, + plot_kwargs: Optional[Union[Dict, List[Dict]]] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the *Planckian Locus* and given illuminants in + Plot the *Planckian Locus* and given illuminants in *CIE 1931 Chromaticity Diagram*. Parameters ---------- - illuminants : unicode or object or array_like + illuminants Illuminants to plot. ``illuminants`` elements can be of any type or form supported by the :func:`colour.plotting.filter_passthrough` definition. - chromaticity_diagram_callable_CIE1931 : callable, optional + chromaticity_diagram_callable_CIE1931 Callable responsible for drawing the *CIE 1931 Chromaticity Diagram*. - planckian_locus_callable_CIE1931 : callable, optional - Callable responsible for drawing the *Planckian Locus* according to - *CIE 1931* method. - annotate_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.annotate` definition, used to - annotate the resulting chromaticity coordinates with their respective - illuminant names. ``annotate_kwargs`` can be either a single dictionary - applied to all the arrows with same settings or a sequence of - dictionaries with different settings for each illuminant. - The following special keyword arguments can also be used: - - - *annotate* : bool, whether to annotate the illuminants. - plot_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.plot` definition, used to control - the style of the plotted illuminants. ``plot_kwargs`` can be either a - single dictionary applied to all the plotted illuminants with same + annotate_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.annotate` + definition, used to annotate the resulting chromaticity coordinates + with their respective spectral distribution names. ``annotate_kwargs`` + can be either a single dictionary applied to all the arrows with same settings or a sequence of dictionaries with different settings for each - plotted illuminant. + spectral distribution. The following special keyword arguments can also + be used: + + - ``annotate`` : Whether to annotate the spectral distributions. + plot_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.plot` definition, + used to control the style of the plotted illuminants. ``plot_kwargs`` + can be either a single dictionary applied to all the plotted + illuminants with the same settings or a sequence of dictionaries with + different settings for eachplotted illuminant. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.temperature.plot_planckian_locus`, :func:`colour.plotting.temperature.\ plot_planckian_locus_in_chromaticity_diagram`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - Also handles keywords arguments for deprecation management. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -475,78 +526,72 @@ def plot_planckian_locus_in_chromaticity_diagram_CIE1931( :alt: plot_planckian_locus_in_chromaticity_diagram_CIE1931 """ - annotate_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['annotate_parameters', 'annotate_kwargs']], - }, **kwargs).get('annotate_kwargs', annotate_kwargs) - settings = dict(kwargs) - settings.update({'method': 'CIE 1931'}) + settings.update({"method": "CIE 1931"}) return plot_planckian_locus_in_chromaticity_diagram( illuminants, chromaticity_diagram_callable_CIE1931, - planckian_locus_callable_CIE1931, annotate_kwargs=annotate_kwargs, plot_kwargs=plot_kwargs, - **settings) + **settings, + ) @override_style() def plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS( - illuminants, - chromaticity_diagram_callable_CIE1960UCS=( - plot_chromaticity_diagram_CIE1960UCS), - planckian_locus_callable_CIE1960UCS=plot_planckian_locus_CIE1960UCS, - annotate_kwargs=None, - plot_kwargs=None, - **kwargs): + illuminants: Union[str, Sequence[str]], + chromaticity_diagram_callable_CIE1960UCS: Callable = ( + plot_chromaticity_diagram_CIE1960UCS # type: ignore[has-type] + ), + annotate_kwargs: Optional[Union[Dict, List[Dict]]] = None, + plot_kwargs: Optional[Union[Dict, List[Dict]]] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the *Planckian Locus* and given illuminants in + Plot the *Planckian Locus* and given illuminants in *CIE 1960 UCS Chromaticity Diagram*. Parameters ---------- - illuminants : unicode or object or array_like + illuminants Illuminants to plot. ``illuminants`` elements can be of any type or form supported by the :func:`colour.plotting.filter_passthrough` definition. - chromaticity_diagram_callable_CIE1960UCS : callable, optional + chromaticity_diagram_callable_CIE1960UCS Callable responsible for drawing the *CIE 1960 UCS Chromaticity Diagram*. - planckian_locus_callable_CIE1960UCS : callable, optional - Callable responsible for drawing the *Planckian Locus* according to - *CIE 1960 UCS* method. - annotate_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.annotate` definition, used to - annotate the resulting chromaticity coordinates with their respective - illuminant names. ``annotate_kwargs`` can be either a single dictionary - applied to all the arrows with same settings or a sequence of - dictionaries with different settings for each illuminant. - The following special keyword arguments can also be used: - - - *annotate* : bool, whether to annotate the illuminants. - plot_kwargs : dict or array_like, optional - Keyword arguments for the :func:`plt.plot` definition, used to control - the style of the plotted illuminants. ``plot_kwargs`` can be either a - single dictionary applied to all the plotted illuminants with same + annotate_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.annotate` + definition, used to annotate the resulting chromaticity coordinates + with their respective spectral distribution names. ``annotate_kwargs`` + can be either a single dictionary applied to all the arrows with same settings or a sequence of dictionaries with different settings for each - plotted illuminant. + spectral distribution. The following special keyword arguments can also + be used: + + - ``annotate`` : Whether to annotate the spectral distributions. + plot_kwargs + Keyword arguments for the :func:`matplotlib.pyplot.plot` definition, + used to control the style of the plotted illuminants. ``plot_kwargs`` + can be either a single dictionary applied to all the plotted + illuminants with the same settings or a sequence of dictionaries with + different settings for eachplotted illuminant. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.temperature.plot_planckian_locus`, :func:`colour.plotting.temperature.\ plot_planckian_locus_in_chromaticity_diagram`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - Also handles keywords arguments for deprecation management. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -561,17 +606,13 @@ def plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS( :alt: plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS """ - annotate_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['annotate_parameters', 'annotate_kwargs']], - }, **kwargs).get('annotate_kwargs', annotate_kwargs) - settings = dict(kwargs) - settings.update({'method': 'CIE 1960 UCS'}) + settings.update({"method": "CIE 1960 UCS"}) return plot_planckian_locus_in_chromaticity_diagram( illuminants, chromaticity_diagram_callable_CIE1960UCS, - planckian_locus_callable_CIE1960UCS, annotate_kwargs=annotate_kwargs, plot_kwargs=plot_kwargs, - **settings) + **settings, + ) diff --git a/colour/plotting/tests/__init__.py b/colour/plotting/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/plotting/tests/__init__.py +++ b/colour/plotting/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/plotting/tests/test_blindness.py b/colour/plotting/tests/test_blindness.py index 4c883d3244..bd586962f2 100644 --- a/colour/plotting/tests/test_blindness.py +++ b/colour/plotting/tests/test_blindness.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.plotting.blindness` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.plotting.blindness` module.""" import numpy as np import unittest @@ -11,34 +6,37 @@ from colour.plotting import plot_cvd_simulation_Machado2009 -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestPlotCvdSimulationMachado2009'] +__all__ = [ + "TestPlotCvdSimulationMachado2009", +] class TestPlotCvdSimulationMachado2009(unittest.TestCase): """ - Defines :func:`colour.plotting.blindness.plot_cvd_simulation_Machado2009` + Define :func:`colour.plotting.blindness.plot_cvd_simulation_Machado2009` definition unit tests methods. """ def test_plot_cvd_simulation_Machado2009(self): """ - Tests :func:`colour.plotting.blindness.plot_cvd_simulation_Machado2009` + Test :func:`colour.plotting.blindness.plot_cvd_simulation_Machado2009` definition. """ figure, axes = plot_cvd_simulation_Machado2009( - np.random.rand(32, 32, 3)) + np.random.rand(32, 32, 3) + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/plotting/tests/test_characterisation.py b/colour/plotting/tests/test_characterisation.py index 1ce3fab34a..ed022e06bc 100644 --- a/colour/plotting/tests/test_characterisation.py +++ b/colour/plotting/tests/test_characterisation.py @@ -1,35 +1,35 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.plotting.characterisation` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.plotting.characterisation` module.""" import unittest from matplotlib.pyplot import Axes, Figure -from colour.plotting import (plot_single_colour_checker, - plot_multi_colour_checkers) +from colour.plotting import ( + plot_single_colour_checker, + plot_multi_colour_checkers, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestPlotSingleColourChecker', 'TestPlotMultiColourCheckers'] +__all__ = [ + "TestPlotSingleColourChecker", + "TestPlotMultiColourCheckers", +] class TestPlotSingleColourChecker(unittest.TestCase): """ - Defines :func:`colour.plotting.characterisation.plot_single_colour_checker` + Define :func:`colour.plotting.characterisation.plot_single_colour_checker` definition unit tests methods. """ def test_plot_single_colour_checker(self): """ - Tests :func:`colour.plotting.characterisation.\ + Test :func:`colour.plotting.characterisation.\ plot_single_colour_checker` definition. """ @@ -41,22 +41,23 @@ def test_plot_single_colour_checker(self): class TestPlotMultiColourCheckers(unittest.TestCase): """ - Defines :func:`colour.plotting.characterisation.plot_multi_colour_checkers` + Define :func:`colour.plotting.characterisation.plot_multi_colour_checkers` definition unit tests methods. """ def test_plot_multi_colour_checkers(self): """ - Tests :func:`colour.plotting.characterisation.\ + Test :func:`colour.plotting.characterisation.\ plot_multi_colour_checkers` definition. """ figure, axes = plot_multi_colour_checkers( - ['ColorChecker 1976', 'ColorChecker 2005']) + ["ColorChecker 1976", "ColorChecker 2005"] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/plotting/tests/test_colorimetry.py b/colour/plotting/tests/test_colorimetry.py index f2a2504c33..970113b802 100644 --- a/colour/plotting/tests/test_colorimetry.py +++ b/colour/plotting/tests/test_colorimetry.py @@ -1,49 +1,57 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.plotting.colorimetry` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.plotting.colorimetry` module.""" import unittest from matplotlib.pyplot import Axes, Figure from colour.colorimetry import SpectralDistribution from colour.plotting import ( - plot_single_sd, plot_multi_sds, plot_single_cmfs, plot_multi_cmfs, - plot_single_illuminant_sd, plot_multi_illuminant_sds, - plot_visible_spectrum, plot_single_lightness_function, - plot_multi_lightness_functions, plot_single_luminance_function, - plot_multi_luminance_functions, plot_blackbody_spectral_radiance, - plot_blackbody_colours) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + plot_single_sd, + plot_multi_sds, + plot_single_cmfs, + plot_multi_cmfs, + plot_single_illuminant_sd, + plot_multi_illuminant_sds, + plot_visible_spectrum, + plot_single_lightness_function, + plot_multi_lightness_functions, + plot_single_luminance_function, + plot_multi_luminance_functions, + plot_blackbody_spectral_radiance, + plot_blackbody_colours, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'TestPlotSingleSd', 'TestPlotMultiSds', 'TestPlotSingleCmfs', - 'TestPlotMultiCmfs', 'TestPlotSingleIlluminantSd', - 'TestPlotMultiIlluminantSds', 'TestPlotVisibleSpectrum', - 'TestPlotSingleLightnessFunction', 'TestPlotMultiLightnessFunctions', - 'TestPlotSingleLuminanceFunction', 'TestPlotMultiLuminanceFunctions', - 'TestPlotBlackbodySpectralRadiance', 'TestPlotBlackbodyColours' + "TestPlotSingleSd", + "TestPlotMultiSds", + "TestPlotSingleCmfs", + "TestPlotMultiCmfs", + "TestPlotSingleIlluminantSd", + "TestPlotMultiIlluminantSds", + "TestPlotVisibleSpectrum", + "TestPlotSingleLightnessFunction", + "TestPlotMultiLightnessFunctions", + "TestPlotSingleLuminanceFunction", + "TestPlotMultiLuminanceFunctions", + "TestPlotBlackbodySpectralRadiance", + "TestPlotBlackbodyColours", ] class TestPlotSingleSd(unittest.TestCase): """ - Defines :func:`colour.plotting.colorimetry.plot_single_sd` definition unit + Define :func:`colour.plotting.colorimetry.plot_single_sd` definition unit tests methods. """ def test_plot_single_sd(self): - """ - Tests :func:`colour.plotting.colorimetry.plot_single_sd` definition. - """ + """Test :func:`colour.plotting.colorimetry.plot_single_sd` definition.""" sd = SpectralDistribution( { @@ -53,15 +61,17 @@ def test_plot_single_sd(self): 530: 0.165500, 540: 0.290400, 550: 0.433450, - 560: 0.594500 + 560: 0.594500, }, - name='Custom 1') + name="Custom 1", + ) figure, axes = plot_single_sd( sd, out_of_gamut_clipping=False, modulate_colours_with_sd_amplitude=True, - equalize_sd_amplitude=True) + equalize_sd_amplitude=True, + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -69,14 +79,12 @@ def test_plot_single_sd(self): class TestPlotMultiSds(unittest.TestCase): """ - Defines :func:`colour.plotting.colorimetry.plot_multi_sds` definition unit + Define :func:`colour.plotting.colorimetry.plot_multi_sds` definition unit tests methods. """ def test_plot_multi_sds(self): - """ - Tests :func:`colour.plotting.colorimetry.plot_multi_sds` definition. - """ + """Test :func:`colour.plotting.colorimetry.plot_multi_sds` definition.""" sd_1 = SpectralDistribution( { @@ -86,9 +94,10 @@ def test_plot_multi_sds(self): 530: 0.165500, 540: 0.290400, 550: 0.433450, - 560: 0.594500 + 560: 0.594500, }, - name='Custom 1') + name="Custom 1", + ) sd_2 = SpectralDistribution( { 500: 0.323000, @@ -97,26 +106,26 @@ def test_plot_multi_sds(self): 530: 0.862000, 540: 0.954000, 550: 0.994950, - 560: 0.995000 + 560: 0.995000, }, - name='Custom 2') + name="Custom 2", + ) figure, axes = plot_multi_sds( [sd_1, sd_2], - plot_kwargs={ - 'use_sd_colours': True, - 'normalise_sd_colours': True - }) + plot_kwargs={"use_sd_colours": True, "normalise_sd_colours": True}, + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) figure, axes = plot_multi_sds( [sd_1, sd_2], - plot_kwargs=[{ - 'use_sd_colours': True, - 'normalise_sd_colours': True - }] * 2) + plot_kwargs=[ + {"use_sd_colours": True, "normalise_sd_colours": True} + ] + * 2, + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -124,14 +133,12 @@ def test_plot_multi_sds(self): class TestPlotSingleCmfs(unittest.TestCase): """ - Defines :func:`colour.plotting.colorimetry.plot_single_cmfs` definition + Define :func:`colour.plotting.colorimetry.plot_single_cmfs` definition unit tests methods. """ def test_plot_single_cmfs(self): - """ - Tests :func:`colour.plotting.colorimetry.plot_single_cmfs` definition. - """ + """Test :func:`colour.plotting.colorimetry.plot_single_cmfs` definition.""" figure, axes = plot_single_cmfs() @@ -141,19 +148,19 @@ def test_plot_single_cmfs(self): class TestPlotMultiCmfs(unittest.TestCase): """ - Defines :func:`colour.plotting.colorimetry.plot_multi_cmfs` definition unit + Define :func:`colour.plotting.colorimetry.plot_multi_cmfs` definition unit tests methods. """ def test_plot_multi_cmfs(self): - """ - Tests :func:`colour.plotting.colorimetry.plot_multi_cmfs` definition. - """ + """Test :func:`colour.plotting.colorimetry.plot_multi_cmfs` definition.""" - figure, axes = plot_multi_cmfs([ - 'CIE 1931 2 Degree Standard Observer', - 'CIE 1964 10 Degree Standard Observer', - ]) + figure, axes = plot_multi_cmfs( + [ + "CIE 1931 2 Degree Standard Observer", + "CIE 1964 10 Degree Standard Observer", + ] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -161,17 +168,17 @@ def test_plot_multi_cmfs(self): class TestPlotSingleIlluminantSd(unittest.TestCase): """ - Defines :func:`colour.plotting.colorimetry.plot_single_illuminant_sd` + Define :func:`colour.plotting.colorimetry.plot_single_illuminant_sd` definition unit tests methods. """ def test_plot_single_illuminant_sd(self): """ - Tests :func:`colour.plotting.colorimetry.plot_single_illuminant_sd` + Test :func:`colour.plotting.colorimetry.plot_single_illuminant_sd` definition. """ - figure, axes = plot_single_illuminant_sd('A') + figure, axes = plot_single_illuminant_sd("A") self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -179,27 +186,28 @@ def test_plot_single_illuminant_sd(self): class TestPlotMultiIlluminantSds(unittest.TestCase): """ - Defines :func:`colour.plotting.colorimetry.plot_multi_illuminant_sds` + Define :func:`colour.plotting.colorimetry.plot_multi_illuminant_sds` definition unit tests methods. """ def test_plot_multi_illuminant_sds(self): """ - Tests :func:`colour.plotting.colorimetry.plot_multi_illuminant_sds` + Test :func:`colour.plotting.colorimetry.plot_multi_illuminant_sds` definition. """ - figure, axes = plot_multi_illuminant_sds(['A', 'B', 'C']) + figure, axes = plot_multi_illuminant_sds(["A", "B", "C"]) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) figure, axes = plot_multi_illuminant_sds( - ['A', 'B', 'C'], - plot_kwargs=[{ - 'use_sd_colours': True, - 'normalise_sd_colours': True - }] * 3) + ["A", "B", "C"], + plot_kwargs=[ + {"use_sd_colours": True, "normalise_sd_colours": True} + ] + * 3, + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -207,13 +215,13 @@ def test_plot_multi_illuminant_sds(self): class TestPlotVisibleSpectrum(unittest.TestCase): """ - Defines :func:`colour.plotting.colorimetry.plot_visible_spectrum` + Define :func:`colour.plotting.colorimetry.plot_visible_spectrum` definition unit tests methods. """ def test_plot_visible_spectrum(self): """ - Tests :func:`colour.plotting.colorimetry.plot_visible_spectrum` + Test :func:`colour.plotting.colorimetry.plot_visible_spectrum` definition. """ @@ -225,17 +233,17 @@ def test_plot_visible_spectrum(self): class TestPlotSingleLightnessFunction(unittest.TestCase): """ - Defines :func:`colour.plotting.colorimetry.plot_single_lightness_function` + Define :func:`colour.plotting.colorimetry.plot_single_lightness_function` definition unit tests methods. """ def test_plot_single_lightness_function(self): """ - Tests :func:`colour.plotting.colorimetry.\ + Test :func:`colour.plotting.colorimetry.\ plot_single_lightness_function` definition. """ - figure, axes = plot_single_lightness_function('CIE 1976') + figure, axes = plot_single_lightness_function("CIE 1976") self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -243,18 +251,19 @@ def test_plot_single_lightness_function(self): class TestPlotMultiLightnessFunctions(unittest.TestCase): """ - Defines :func:`colour.plotting.colorimetry.plot_multi_lightness_functions` + Define :func:`colour.plotting.colorimetry.plot_multi_lightness_functions` definition unit tests methods. """ def test_plot_multi_lightness_functions(self): """ - Tests :func:`colour.plotting.colorimetry.\ + Test :func:`colour.plotting.colorimetry.\ plot_multi_lightness_functions` definition. """ figure, axes = plot_multi_lightness_functions( - ['CIE 1976', 'Wyszecki 1963']) + ["CIE 1976", "Wyszecki 1963"] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -262,17 +271,17 @@ def test_plot_multi_lightness_functions(self): class TestPlotSingleLuminanceFunction(unittest.TestCase): """ - Defines :func:`colour.plotting.colorimetry.plot_single_luminance_function` + Define :func:`colour.plotting.colorimetry.plot_single_luminance_function` definition unit tests methods. """ def test_plot_single_luminance_function(self): """ - Tests :func:`colour.plotting.colorimetry.\ + Test :func:`colour.plotting.colorimetry.\ plot_single_luminance_function` definition. """ - figure, axes = plot_single_luminance_function('CIE 1976') + figure, axes = plot_single_luminance_function("CIE 1976") self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -280,18 +289,19 @@ def test_plot_single_luminance_function(self): class TestPlotMultiLuminanceFunctions(unittest.TestCase): """ - Defines :func:`colour.plotting.colorimetry.plot_multi_luminance_functions` + Define :func:`colour.plotting.colorimetry.plot_multi_luminance_functions` definition unit tests methods. """ def test_plot_multi_luminance_functions(self): """ - Tests :func:`colour.plotting.colorimetry.\ + Test :func:`colour.plotting.colorimetry.\ plot_multi_luminance_functions` definition. """ figure, axes = plot_multi_luminance_functions( - ['CIE 1976', 'Newhall 1943']) + ["CIE 1976", "Newhall 1943"] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -299,13 +309,13 @@ def test_plot_multi_luminance_functions(self): class TestPlotBlackbodySpectralRadiance(unittest.TestCase): """ - Defines :func:`colour.plotting.colorimetry.\ + Define :func:`colour.plotting.colorimetry.\ plot_blackbody_spectral_radiance` definition unit tests methods. """ def test_plot_blackbody_spectral_radiance(self): """ - Tests :func:`colour.plotting.colorimetry.\ + Test :func:`colour.plotting.colorimetry.\ plot_blackbody_spectral_radiance` definition. """ @@ -317,13 +327,13 @@ def test_plot_blackbody_spectral_radiance(self): class TestPlotBlackbodyColours(unittest.TestCase): """ - Defines :func:`colour.plotting.colorimetry.plot_blackbody_colours` + Define :func:`colour.plotting.colorimetry.plot_blackbody_colours` definition unit tests methods. """ def test_plot_blackbody_colours(self): """ - Tests :func:`colour.plotting.colorimetry.plot_blackbody_colours` + Test :func:`colour.plotting.colorimetry.plot_blackbody_colours` definition. """ @@ -333,5 +343,5 @@ def test_plot_blackbody_colours(self): self.assertIsInstance(axes, Axes) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/plotting/tests/test_common.py b/colour/plotting/tests/test_common.py index e799568967..a08c50cba6 100644 --- a/colour/plotting/tests/test_common.py +++ b/colour/plotting/tests/test_common.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.plotting.common` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.plotting.common` module.""" import matplotlib.pyplot as plt import numpy as np @@ -20,135 +15,159 @@ from colour.models import RGB_COLOURSPACES, XYZ_to_sRGB, gamma_function from colour.plotting import ColourSwatch from colour.plotting import ( - colour_style, override_style, XYZ_to_plotting_colourspace, colour_cycle, - artist, camera, render, label_rectangles, uniform_axes3d, - filter_passthrough, filter_RGB_colourspaces, filter_cmfs, - filter_illuminants, filter_colour_checkers, update_settings_collection, - plot_single_colour_swatch, plot_multi_colour_swatches, - plot_single_function, plot_multi_functions, plot_image) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + colour_style, + override_style, + XYZ_to_plotting_colourspace, + colour_cycle, + artist, + camera, + render, + label_rectangles, + uniform_axes3d, + filter_passthrough, + filter_RGB_colourspaces, + filter_cmfs, + filter_illuminants, + filter_colour_checkers, + update_settings_collection, + plot_single_colour_swatch, + plot_multi_colour_swatches, + plot_single_function, + plot_multi_functions, + plot_image, +) +from colour.utilities import attest + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'TestColourStyle', 'TestOverrideStyle', 'TestXyzToPlottingColourspace', - 'TestColourCycle', 'TestArtist', 'TestCamera', 'TestRender', - 'TestLabelRectangles', 'TestUniformAxes3d', 'TestFilterPassthrough', - 'TestFilterRgbColourspaces', 'TestFilterCmfs', 'TestFilterIlluminants', - 'TestFilterColourCheckers', 'TestUpdateSettingsCollection', - 'TestPlotSingleColourSwatch', 'TestPlotMultiColourSwatches', - 'TestPlotSingleFunction', 'TestPlotMultiFunctions', 'TestPlotImage' + "TestColourStyle", + "TestOverrideStyle", + "TestXyzToPlottingColourspace", + "TestColourCycle", + "TestArtist", + "TestCamera", + "TestRender", + "TestLabelRectangles", + "TestUniformAxes3d", + "TestFilterPassthrough", + "TestFilterRgbColourspaces", + "TestFilterCmfs", + "TestFilterIlluminants", + "TestFilterColourCheckers", + "TestUpdateSettingsCollection", + "TestPlotSingleColourSwatch", + "TestPlotMultiColourSwatches", + "TestPlotSingleFunction", + "TestPlotMultiFunctions", + "TestPlotImage", ] class TestColourStyle(unittest.TestCase): """ - Defines :func:`colour.plotting.common.colour_style` definition unit tests + Define :func:`colour.plotting.common.colour_style` definition unit tests methods. """ def test_colour_style(self): - """ - Tests :func:`colour.plotting.common.colour_style` definition. - """ + """Test :func:`colour.plotting.common.colour_style` definition.""" self.assertIsInstance(colour_style(use_style=False), dict) class TestOverrideStyle(unittest.TestCase): """ - Defines :func:`colour.plotting.common.override_style` definition unit tests + Define :func:`colour.plotting.common.override_style` definition unit tests methods. """ def test_override_style(self): - """ - Tests :func:`colour.plotting.common.override_style` definition. - """ + """Test :func:`colour.plotting.common.override_style` definition.""" - text_color = plt.rcParams['text.color'] + text_color = plt.rcParams["text.color"] try: - @override_style(**{'text.color': 'red'}) + @override_style(**{"text.color": "red"}) def test_text_color_override(): - """ - Tests :func:`colour.plotting.common.override_style` definition. - """ + """Test :func:`colour.plotting.common.override_style` definition.""" - assert plt.rcParams['text.color'] == 'red' + attest(plt.rcParams["text.color"] == "red") test_text_color_override() finally: - plt.rcParams['text.color'] = text_color + plt.rcParams["text.color"] = text_color class TestXyzToPlottingColourspace(unittest.TestCase): """ - Defines :func:`colour.plotting.common.XYZ_to_plotting_colourspace` + Define :func:`colour.plotting.common.XYZ_to_plotting_colourspace` definition unit tests methods. """ def test_XYZ_to_plotting_colourspace(self): """ - Tests :func:`colour.plotting.common.XYZ_to_plotting_colourspace` + Test :func:`colour.plotting.common.XYZ_to_plotting_colourspace` definition. """ XYZ = np.random.random(3) np.testing.assert_almost_equal( - XYZ_to_sRGB(XYZ), XYZ_to_plotting_colourspace(XYZ), decimal=7) + XYZ_to_sRGB(XYZ), XYZ_to_plotting_colourspace(XYZ), decimal=7 + ) class TestColourCycle(unittest.TestCase): """ - Defines :func:`colour.plotting.common.colour_cycle` definition unit tests + Define :func:`colour.plotting.common.colour_cycle` definition unit tests methods. """ def test_colour_cycle(self): - """ - Tests :func:`colour.plotting.common.colour_cycle` definition. - """ + """Test :func:`colour.plotting.common.colour_cycle` definition.""" cycler = colour_cycle() np.testing.assert_almost_equal( next(cycler), np.array([0.95686275, 0.26274510, 0.21176471, 1.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( next(cycler), np.array([0.61582468, 0.15423299, 0.68456747, 1.00000000]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( next(cycler), np.array([0.25564014, 0.31377163, 0.70934256, 1.00000000]), - decimal=7) + decimal=7, + ) - cycler = colour_cycle(colour_cycle_map='viridis') + cycler = colour_cycle(colour_cycle_map="viridis") np.testing.assert_almost_equal( next(cycler), np.array([0.26700400, 0.00487400, 0.32941500, 1.00000000]), - decimal=7) + decimal=7, + ) class TestArtist(unittest.TestCase): """ - Defines :func:`colour.plotting.common.artist` definition unit tests + Define :func:`colour.plotting.common.artist` definition unit tests methods. """ def test_artist(self): - """ - Tests :func:`colour.plotting.common.artist` definition. - """ + """Test :func:`colour.plotting.common.artist` definition.""" figure_1, axes_1 = artist() @@ -164,17 +183,15 @@ def test_artist(self): class TestCamera(unittest.TestCase): """ - Defines :func:`colour.plotting.common.camera` definition unit tests + Define :func:`colour.plotting.common.camera` definition unit tests methods. """ def test_camera(self): - """ - Tests :func:`colour.plotting.common.camera` definition. - """ + """Test :func:`colour.plotting.common.camera` definition.""" figure, _axes = artist() - axes = figure.add_subplot(111, projection='3d') + axes = figure.add_subplot(111, projection="3d") _figure, axes = camera(axes=axes, elevation=45, azimuth=90) @@ -184,28 +201,22 @@ def test_camera(self): class TestRender(unittest.TestCase): """ - Defines :func:`colour.plotting.common.render` definition unit tests + Define :func:`colour.plotting.common.render` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._temporary_directory = tempfile.mkdtemp() def tearDown(self): - """ - After tests actions. - """ + """After tests actions.""" shutil.rmtree(self._temporary_directory) def test_render(self): - """ - Tests :func:`colour.plotting.common.render` definition. - """ + """Test :func:`colour.plotting.common.render` definition.""" figure, axes = artist() @@ -213,17 +224,17 @@ def test_render(self): figure=figure, axes=axes, standalone=False, - aspect='equal', + aspect="equal", axes_visible=True, bounding_box=[0, 1, 0, 1], tight_layout=False, legend=True, legend_columns=2, transparent_background=False, - title='Render Unit Test', + title="Render Unit Test", wrap_title=True, - x_label='x Label', - y_label='y Label', + x_label="x Label", + y_label="y Label", x_ticker=False, y_ticker=False, ) @@ -231,44 +242,42 @@ def test_render(self): render(standalone=True) render( - filename=os.path.join(self._temporary_directory, 'render.png'), - axes_visible=False) + filename=os.path.join(self._temporary_directory, "render.png"), + axes_visible=False, + ) class TestLabelRectangles(unittest.TestCase): """ - Defines :func:`colour.plotting.common.label_rectangles` definition unit + Define :func:`colour.plotting.common.label_rectangles` definition unit tests methods. """ def test_label_rectangles(self): - """ - Tests :func:`colour.plotting.common.label_rectangles` definition. - """ + """Test :func:`colour.plotting.common.label_rectangles` definition.""" figure, axes = artist() samples = np.linspace(0, 1, 10) _figure, axes = label_rectangles( - samples, axes.bar(samples, 1), figure=figure, axes=axes) + samples, axes.bar(samples, 1), figure=figure, axes=axes + ) self.assertEqual(len(axes.texts), len(samples)) class TestUniformAxes3d(unittest.TestCase): """ - Defines :func:`colour.plotting.common.uniform_axes3d` definition unit tests + Define :func:`colour.plotting.common.uniform_axes3d` definition unit tests methods. """ def test_uniform_axes3d(self): - """ - Tests :func:`colour.plotting.common.uniform_axes3d` definition. - """ + """Test :func:`colour.plotting.common.uniform_axes3d` definition.""" figure, _axes = artist() - axes = figure.add_subplot(111, projection='3d') + axes = figure.add_subplot(111, projection="3d") uniform_axes3d(axes=axes) @@ -278,143 +287,155 @@ def test_uniform_axes3d(self): class TestFilterPassthrough(unittest.TestCase): """ - Defines :func:`colour.plotting.common.filter_passthrough` definition unit + Define :func:`colour.plotting.common.filter_passthrough` definition unit tests methods. """ def test_filter_passthrough(self): - """ - Tests :func:`colour.plotting.common.filter_passthrough` definition. - """ + """Test :func:`colour.plotting.common.filter_passthrough` definition.""" self.assertListEqual( - sorted([ - colourspace.name for colourspace in filter_passthrough( - RGB_COLOURSPACES, ['^ACES.*']).values() - ]), ['ACES2065-1', 'ACEScc', 'ACEScct', 'ACEScg', 'ACESproxy']) + sorted( + colourspace.name + for colourspace in filter_passthrough( + RGB_COLOURSPACES, ["^ACES.*"] + ).values() + ), + ["ACES2065-1", "ACEScc", "ACEScct", "ACEScg", "ACESproxy"], + ) self.assertListEqual( - sorted(filter_passthrough(RGB_COLOURSPACES, ['^ACEScc$']).keys()), - ['ACEScc']) + sorted(filter_passthrough(RGB_COLOURSPACES, ["^ACEScc$"]).keys()), + ["ACEScc"], + ) self.assertListEqual( - sorted(filter_passthrough(RGB_COLOURSPACES, ['^acescc$']).keys()), - ['ACEScc']) + sorted(filter_passthrough(RGB_COLOURSPACES, ["^acescc$"]).keys()), + ["ACEScc"], + ) self.assertDictEqual( filter_passthrough( - SDS_ILLUMINANTS, [SDS_ILLUMINANTS['D65'], { - 'Is': 'Excluded' - }], - allow_non_siblings=False), {'D65': SDS_ILLUMINANTS['D65']}) + SDS_ILLUMINANTS, + [SDS_ILLUMINANTS["D65"], {"Is": "Excluded"}], + allow_non_siblings=False, + ), + {"D65": SDS_ILLUMINANTS["D65"]}, + ) self.assertDictEqual( filter_passthrough( - SDS_ILLUMINANTS, [SDS_ILLUMINANTS['D65'], { - 'Is': 'Included' - }], - allow_non_siblings=True), { - 'D65': SDS_ILLUMINANTS['D65'], - 'Is': 'Included' - }) + SDS_ILLUMINANTS, + [SDS_ILLUMINANTS["D65"], {"Is": "Included"}], + allow_non_siblings=True, + ), + {"D65": SDS_ILLUMINANTS["D65"], "Is": "Included"}, + ) self.assertListEqual( - sorted([ - element for element in filter_passthrough({ - 'John': 'Doe', - 'Luke': 'Skywalker' - }, ['John']).values() - ]), ['Doe', 'John']) + sorted( + element + for element in filter_passthrough( + {"John": "Doe", "Luke": "Skywalker"}, ["John"] + ).values() + ), + ["Doe", "John"], + ) class TestFilterRgbColourspaces(unittest.TestCase): """ - Defines :func:`colour.plotting.common.filter_RGB_colourspaces` definition + Define :func:`colour.plotting.common.filter_RGB_colourspaces` definition unit tests methods. """ def test_filter_RGB_colourspaces(self): """ - Tests :func:`colour.plotting.common.filter_RGB_colourspaces` + Test :func:`colour.plotting.common.filter_RGB_colourspaces` definition. """ self.assertListEqual( - sorted([ - colourspace.name for colourspace in filter_RGB_colourspaces( - ['^ACES.*']).values() - ]), ['ACES2065-1', 'ACEScc', 'ACEScct', 'ACEScg', 'ACESproxy']) + sorted( + colourspace.name + for colourspace in filter_RGB_colourspaces( + ["^ACES.*"] + ).values() + ), + ["ACES2065-1", "ACEScc", "ACEScct", "ACEScg", "ACESproxy"], + ) class TestFilterCmfs(unittest.TestCase): """ - Defines :func:`colour.plotting.common.filter_cmfs` definition unit tests + Define :func:`colour.plotting.common.filter_cmfs` definition unit tests methods. """ def test_filter_cmfs(self): - """ - Tests :func:`colour.plotting.common.filter_cmfs` definition. - """ + """Test :func:`colour.plotting.common.filter_cmfs` definition.""" self.assertListEqual( - sorted([ - cmfs.name for cmfs in filter_cmfs(['.*2 Degree.*']).values() - ]), [ - 'CIE 1931 2 Degree Standard Observer', - 'CIE 2012 2 Degree Standard Observer', - 'Stiles & Burch 1955 2 Degree RGB CMFs', - 'Stockman & Sharpe 2 Degree Cone Fundamentals', - 'Wright & Guild 1931 2 Degree RGB CMFs' - ]) + sorted( + cmfs.name for cmfs in filter_cmfs([".*2 Degree.*"]).values() + ), + [ + "CIE 1931 2 Degree Standard Observer", + "CIE 2012 2 Degree Standard Observer", + "Stiles & Burch 1955 2 Degree RGB CMFs", + "Stockman & Sharpe 2 Degree Cone Fundamentals", + "Wright & Guild 1931 2 Degree RGB CMFs", + ], + ) class TestFilterIlluminants(unittest.TestCase): """ - Defines :func:`colour.plotting.common.filter_illuminants` definition unit + Define :func:`colour.plotting.common.filter_illuminants` definition unit tests methods. """ def test_filter_illuminants(self): - """ - Tests :func:`colour.plotting.common.filter_illuminants` definition. - """ + """Test :func:`colour.plotting.common.filter_illuminants` definition.""" self.assertListEqual( - sorted(filter_illuminants(['^D.*']).keys()), - ['D50', 'D55', 'D60', 'D65', 'D75', 'Daylight FL']) + sorted(filter_illuminants(["^D.*"]).keys()), + ["D50", "D55", "D60", "D65", "D75", "Daylight FL"], + ) class TestFilterColourCheckers(unittest.TestCase): """ - Defines :func:`colour.plotting.common.filter_colour_checkers` definition + Define :func:`colour.plotting.common.filter_colour_checkers` definition unit tests methods. """ def test_filter_colour_checkers(self): - """ - Tests :func:`colour.plotting.common.filter_colour_checkers` definition. - """ + """Test :func:`colour.plotting.common.filter_colour_checkers` definition.""" self.assertListEqual( - sorted([ - colour_checker.name for colour_checker in - filter_colour_checkers(['.*24.*']).values() - ]), [ - 'ColorChecker24 - After November 2014', - 'ColorChecker24 - Before November 2014' - ]) + sorted( + colour_checker.name + for colour_checker in filter_colour_checkers( + [".*24.*"] + ).values() + ), + [ + "ColorChecker24 - After November 2014", + "ColorChecker24 - Before November 2014", + ], + ) class TestUpdateSettingsCollection(unittest.TestCase): """ - Defines :func:`colour.plotting.common.update_settings_collection` + Define :func:`colour.plotting.common.update_settings_collection` definition unit tests methods. """ def test_update_settings_collection(self): """ - Tests :func:`colour.plotting.common.update_settings_collection` + Test :func:`colour.plotting.common.update_settings_collection` definition. """ @@ -431,24 +452,26 @@ def test_update_settings_collection(self): class TestPlotSingleColourSwatch(unittest.TestCase): """ - Defines :func:`colour.plotting.common.plot_single_colour_swatch` definition + Define :func:`colour.plotting.common.plot_single_colour_swatch` definition unit tests methods. """ def test_plot_single_colour_swatch(self): """ - Tests :func:`colour.plotting.common.plot_single_colour_swatch` + Test :func:`colour.plotting.common.plot_single_colour_swatch` definition. """ figure, axes = plot_single_colour_swatch( - ColourSwatch(RGB=(0.45620519, 0.03081071, 0.04091952))) + ColourSwatch((0.45620519, 0.03081071, 0.04091952)) + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) figure, axes = plot_single_colour_swatch( - np.array([0.45620519, 0.03081071, 0.04091952])) + np.array([0.45620519, 0.03081071, 0.04091952]) + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -456,28 +479,35 @@ def test_plot_single_colour_swatch(self): class TestPlotMultiColourSwatches(unittest.TestCase): """ - Defines :func:`colour.plotting.common.plot_multi_colour_swatches` + Define :func:`colour.plotting.common.plot_multi_colour_swatches` definition unit tests methods. """ def test_plot_multi_colour_swatches(self): """ - Tests :func:`colour.plotting.common.plot_multi_colour_swatches` + Test :func:`colour.plotting.common.plot_multi_colour_swatches` definition. """ - figure, axes = plot_multi_colour_swatches([ - ColourSwatch(RGB=(0.45293517, 0.31732158, 0.26414773)), - ColourSwatch(RGB=(0.77875824, 0.57726450, 0.50453169)) - ]) + figure, axes = plot_multi_colour_swatches( + [ + ColourSwatch((0.45293517, 0.31732158, 0.26414773)), + ColourSwatch((0.77875824, 0.57726450, 0.50453169)), + ] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) figure, axes = plot_multi_colour_swatches( - np.array([[0.45293517, 0.31732158, 0.26414773], - [0.77875824, 0.57726450, 0.50453169]]), - direction='-y') + np.array( + [ + [0.45293517, 0.31732158, 0.26414773], + [0.77875824, 0.57726450, 0.50453169], + ] + ), + direction="-y", + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -485,17 +515,16 @@ def test_plot_multi_colour_swatches(self): class TestPlotSingleFunction(unittest.TestCase): """ - Defines :func:`colour.plotting.common.plot_single_function` definition unit + Define :func:`colour.plotting.common.plot_single_function` definition unit tests methods. """ def test_plot_single_function(self): - """ - Tests :func:`colour.plotting.common.plot_single_function` definition. - """ + """Test :func:`colour.plotting.common.plot_single_function` definition.""" figure, axes = plot_single_function( - partial(gamma_function, exponent=1 / 2.2)) + partial(gamma_function, exponent=1 / 2.2) + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -503,29 +532,28 @@ def test_plot_single_function(self): class TestPlotMultiFunctions(unittest.TestCase): """ - Defines :func:`colour.plotting.common.plot_multi_functions` definition unit + Define :func:`colour.plotting.common.plot_multi_functions` definition unit tests methods. """ def test_plot_multi_functions(self): - """ - Tests :func:`colour.plotting.common.plot_multi_functions` definition. - """ + """Test :func:`colour.plotting.common.plot_multi_functions` definition.""" functions = { - 'Gamma 2.2': lambda x: x ** (1 / 2.2), - 'Gamma 2.4': lambda x: x ** (1 / 2.4), - 'Gamma 2.6': lambda x: x ** (1 / 2.6), + "Gamma 2.2": lambda x: x ** (1 / 2.2), + "Gamma 2.4": lambda x: x ** (1 / 2.4), + "Gamma 2.6": lambda x: x ** (1 / 2.6), } - plot_kwargs = {'c': 'r'} + plot_kwargs = {"c": "r"} figure, axes = plot_multi_functions(functions, plot_kwargs=plot_kwargs) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) - plot_kwargs = [{'c': 'r'}, {'c': 'g'}, {'c': 'b'}] + plot_kwargs = [{"c": "r"}, {"c": "g"}, {"c": "b"}] figure, axes = plot_multi_functions( - functions, log_x=10, log_y=10, plot_kwargs=plot_kwargs) + functions, log_x=10, log_y=10, plot_kwargs=plot_kwargs + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -543,21 +571,20 @@ def test_plot_multi_functions(self): class TestPlotImage(unittest.TestCase): """ - Defines :func:`colour.plotting.common.plot_image` definition unit tests + Define :func:`colour.plotting.common.plot_image` definition unit tests methods. """ def test_plot_image(self): - """ - Tests :func:`colour.plotting.common.plot_image` definition. - """ + """Test :func:`colour.plotting.common.plot_image` definition.""" - path = os.path.join(colour.__path__[0], '..', 'docs', '_static', - 'Logo_Medium_001.png') + path = os.path.join( + colour.__path__[0], "..", "docs", "_static", "Logo_Medium_001.png" + ) # Distribution does not ship the documentation thus we are skipping # this unit test if the image does not exist. - if not os.path.exists(path): # noqa + if not os.path.exists(path): # pragma: no cover return figure, axes = plot_image(read_image(path)) @@ -566,5 +593,5 @@ def test_plot_image(self): self.assertIsInstance(axes, Axes) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/plotting/tests/test_corresponding.py b/colour/plotting/tests/test_corresponding.py index bfd6f2b400..d471bb8480 100644 --- a/colour/plotting/tests/test_corresponding.py +++ b/colour/plotting/tests/test_corresponding.py @@ -1,34 +1,31 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.plotting.corresponding` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.plotting.corresponding` module.""" import unittest from matplotlib.pyplot import Axes, Figure from colour.plotting import plot_corresponding_chromaticities_prediction -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestPlotCorrespondingChromaticitiesPrediction'] +__all__ = [ + "TestPlotCorrespondingChromaticitiesPrediction", +] class TestPlotCorrespondingChromaticitiesPrediction(unittest.TestCase): """ - Defines :func:`colour.plotting.corresponding.\ + Define :func:`colour.plotting.corresponding.\ plot_corresponding_chromaticities_prediction` definition unit tests methods. """ def test_plot_corresponding_chromaticities_prediction(self): """ - Tests :func:`colour.plotting.corresponding.\ + Test :func:`colour.plotting.corresponding.\ plot_corresponding_chromaticities_prediction` definition. """ @@ -38,5 +35,5 @@ def test_plot_corresponding_chromaticities_prediction(self): self.assertIsInstance(axes, Axes) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/plotting/tests/test_diagrams.py b/colour/plotting/tests/test_diagrams.py index 3aba55a081..f8e10fe5bd 100644 --- a/colour/plotting/tests/test_diagrams.py +++ b/colour/plotting/tests/test_diagrams.py @@ -1,98 +1,109 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.plotting.diagrams` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.plotting.diagrams` module.""" import unittest from matplotlib.pyplot import Axes, Figure -from colour.colorimetry import (SDS_ILLUMINANTS, SpectralShape, - MSDS_CMFS_STANDARD_OBSERVER) -from colour.plotting import (plot_chromaticity_diagram_CIE1931, - plot_chromaticity_diagram_CIE1960UCS, - plot_chromaticity_diagram_CIE1976UCS, - plot_sds_in_chromaticity_diagram_CIE1931, - plot_sds_in_chromaticity_diagram_CIE1960UCS, - plot_sds_in_chromaticity_diagram_CIE1976UCS) +from colour.colorimetry import ( + MSDS_CMFS, + SDS_ILLUMINANTS, + SpectralShape, + reshape_msds, +) +from colour.plotting import ( + plot_chromaticity_diagram_CIE1931, + plot_chromaticity_diagram_CIE1960UCS, + plot_chromaticity_diagram_CIE1976UCS, + plot_sds_in_chromaticity_diagram_CIE1931, + plot_sds_in_chromaticity_diagram_CIE1960UCS, + plot_sds_in_chromaticity_diagram_CIE1976UCS, +) from colour.plotting.diagrams import ( - plot_spectral_locus, plot_chromaticity_diagram_colours, - plot_chromaticity_diagram, plot_sds_in_chromaticity_diagram) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + plot_spectral_locus, + plot_chromaticity_diagram_colours, + plot_chromaticity_diagram, + plot_sds_in_chromaticity_diagram, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'TestPlotSpectralLocus', 'TestPlotChromaticityDiagramColours', - 'TestPlotChromaticityDiagram', 'TestPlotChromaticityDiagramCIE1931', - 'TestPlotChromaticityDiagramCIE1960UCS', - 'TestPlotChromaticityDiagramCIE1976UCS', - 'TestPlotSdsInChromaticityDiagram', - 'TestPlotSdsInChromaticityDiagramCIE1931', - 'TestPlotSdsInChromaticityDiagramCIE1960UCS', - 'TestPlotSdsInChromaticityDiagramCIE1976UCS' + "TestPlotSpectralLocus", + "TestPlotChromaticityDiagramColours", + "TestPlotChromaticityDiagram", + "TestPlotChromaticityDiagramCIE1931", + "TestPlotChromaticityDiagramCIE1960UCS", + "TestPlotChromaticityDiagramCIE1976UCS", + "TestPlotSdsInChromaticityDiagram", + "TestPlotSdsInChromaticityDiagramCIE1931", + "TestPlotSdsInChromaticityDiagramCIE1960UCS", + "TestPlotSdsInChromaticityDiagramCIE1976UCS", ] class TestPlotSpectralLocus(unittest.TestCase): """ - Defines :func:`colour.plotting.diagrams.plot_spectral_locus` definition + Define :func:`colour.plotting.diagrams.plot_spectral_locus` definition unit tests methods. """ def test_plot_spectral_locus(self): - """ - Tests :func:`colour.plotting.diagrams.plot_spectral_locus` definition. - """ + """Test :func:`colour.plotting.diagrams.plot_spectral_locus` definition.""" figure, axes = plot_spectral_locus() self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) - figure, axes = plot_spectral_locus(spectral_locus_colours='RGB') + figure, axes = plot_spectral_locus(spectral_locus_colours="RGB") self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) figure, axes = plot_spectral_locus( - method='CIE 1960 UCS', spectral_locus_colours='RGB') + method="CIE 1960 UCS", spectral_locus_colours="RGB" + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) figure, axes = plot_spectral_locus( - method='CIE 1976 UCS', spectral_locus_colours='RGB') + method="CIE 1976 UCS", spectral_locus_colours="RGB" + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) - figure, axes = plot_spectral_locus(MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'].copy().align( - SpectralShape(400, 700, 10))) + # pylint: disable=E1102 + figure, axes = plot_spectral_locus( + reshape_msds( + MSDS_CMFS["CIE 1931 2 Degree Standard Observer"], + SpectralShape(400, 700, 10), + ) + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) self.assertRaises( - ValueError, lambda: plot_spectral_locus(method='Undefined')) + ValueError, lambda: plot_spectral_locus(method="Undefined") + ) class TestPlotChromaticityDiagramColours(unittest.TestCase): """ - Defines :func:`colour.plotting.diagrams.plot_chromaticity_diagram_colours` + Define :func:`colour.plotting.diagrams.plot_chromaticity_diagram_colours` definition unit tests methods. """ def test_plot_chromaticity_diagram_colours(self): """ - Tests :func:`colour.plotting.diagrams.\ -plot_chromaticity_diagram_colours` definition. + Test :func:`colour.plotting.diagrams.plot_chromaticity_diagram_colours` + definition. """ figure, axes = plot_chromaticity_diagram_colours() @@ -102,18 +113,24 @@ def test_plot_chromaticity_diagram_colours(self): self.assertRaises( ValueError, - lambda: plot_chromaticity_diagram_colours(method='Undefined')) + lambda: plot_chromaticity_diagram_colours(method="Undefined"), + ) + + figure, axes = plot_chromaticity_diagram_colours(diagram_colours="RGB") + + self.assertIsInstance(figure, Figure) + self.assertIsInstance(axes, Axes) class TestPlotChromaticityDiagram(unittest.TestCase): """ - Defines :func:`colour.plotting.diagrams.plot_chromaticity_diagram` + Define :func:`colour.plotting.diagrams.plot_chromaticity_diagram` definition unit tests methods. """ def test_plot_chromaticity_diagram(self): """ - Tests :func:`colour.plotting.diagrams.plot_chromaticity_diagram` + Test :func:`colour.plotting.diagrams.plot_chromaticity_diagram` definition. """ @@ -122,33 +139,35 @@ def test_plot_chromaticity_diagram(self): self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) - figure, axes = plot_chromaticity_diagram(method='CIE 1960 UCS') + figure, axes = plot_chromaticity_diagram(method="CIE 1960 UCS") self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) - figure, axes = plot_chromaticity_diagram(method='CIE 1976 UCS') + figure, axes = plot_chromaticity_diagram(method="CIE 1976 UCS") self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) self.assertRaises( - ValueError, lambda: plot_chromaticity_diagram( - method='Undefined', + ValueError, + lambda: plot_chromaticity_diagram( + method="Undefined", show_diagram_colours=False, - show_spectral_locus=False) + show_spectral_locus=False, + ), ) class TestPlotChromaticityDiagramCIE1931(unittest.TestCase): """ - Defines :func:`colour.plotting.diagrams.plot_chromaticity_diagram_CIE1931` + Define :func:`colour.plotting.diagrams.plot_chromaticity_diagram_CIE1931` definition unit tests methods. """ def test_plot_chromaticity_diagram_CIE1931(self): """ - Tests :func:`colour.plotting.diagrams.\ + Test :func:`colour.plotting.diagrams.\ plot_chromaticity_diagram_CIE1931` definition. """ @@ -160,13 +179,13 @@ def test_plot_chromaticity_diagram_CIE1931(self): class TestPlotChromaticityDiagramCIE1960UCS(unittest.TestCase): """ - Defines :func:`colour.plotting.diagrams.\ + Define :func:`colour.plotting.diagrams.\ plot_chromaticity_diagram_CIE1960UCS` definition unit tests methods. """ def test_plot_chromaticity_diagram_CIE1960UCS(self): """ - Tests :func:`colour.plotting.diagrams.\ + Test :func:`colour.plotting.diagrams.\ plot_chromaticity_diagram_CIE1960UCS` definition. """ @@ -178,13 +197,13 @@ def test_plot_chromaticity_diagram_CIE1960UCS(self): class TestPlotChromaticityDiagramCIE1976UCS(unittest.TestCase): """ - Defines :func:`colour.plotting.diagrams.\ + Define :func:`colour.plotting.diagrams.\ plot_chromaticity_diagram_CIE1976UCS` definition unit tests methods. """ def test_plot_chromaticity_diagram_CIE1976UCS(self): """ - Tests :func:`colour.plotting.diagrams.\ + Test :func:`colour.plotting.diagrams.\ plot_chromaticity_diagram_CIE1976UCS` definition. """ @@ -196,40 +215,33 @@ def test_plot_chromaticity_diagram_CIE1976UCS(self): class TestPlotSdsInChromaticityDiagram(unittest.TestCase): """ - Defines :func:`colour.plotting.diagrams.\ + Define :func:`colour.plotting.diagrams.\ plot_sds_in_chromaticity_diagram` definition unit tests methods. """ def test_plot_sds_in_chromaticity_diagram(self): """ - Tests :func:`colour.plotting.diagrams.plot_sds_in_chromaticity_diagram` + Test :func:`colour.plotting.diagrams.plot_sds_in_chromaticity_diagram` definition. """ figure, axes = plot_sds_in_chromaticity_diagram( - [SDS_ILLUMINANTS['A'], SDS_ILLUMINANTS['D65']], - annotate_kwargs={'arrowprops': { - 'width': 10 - }}, - plot_kwargs={ - 'normalise_sd_colours': True, - 'use_sd_colours': True - }) + [SDS_ILLUMINANTS["A"], SDS_ILLUMINANTS["D65"]], + annotate_kwargs={"arrowprops": {"width": 10}}, + plot_kwargs={"normalise_sd_colours": True, "use_sd_colours": True}, + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) figure, axes = plot_sds_in_chromaticity_diagram( - [SDS_ILLUMINANTS['A'], SDS_ILLUMINANTS['D65']], - annotate_kwargs=[{ - 'arrowprops': { - 'width': 10 - } - }] * 2, - plot_kwargs=[{ - 'normalise_sd_colours': True, - 'use_sd_colours': True - }] * 2) + [SDS_ILLUMINANTS["A"], SDS_ILLUMINANTS["D65"]], + annotate_kwargs=[{"arrowprops": {"width": 10}}] * 2, + plot_kwargs=[ + {"normalise_sd_colours": True, "use_sd_colours": True} + ] + * 2, + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -237,26 +249,28 @@ def test_plot_sds_in_chromaticity_diagram(self): self.assertRaises( ValueError, lambda: plot_sds_in_chromaticity_diagram( - [SDS_ILLUMINANTS['A'], SDS_ILLUMINANTS['D65']], + [SDS_ILLUMINANTS["A"], SDS_ILLUMINANTS["D65"]], chromaticity_diagram_callable=lambda **x: x, - method='Undefined') + method="Undefined", + ), ) class TestPlotSdsInChromaticityDiagramCIE1931(unittest.TestCase): """ - Defines :func:`colour.plotting.diagrams.\ + Define :func:`colour.plotting.diagrams.\ plot_sds_in_chromaticity_diagram_CIE1931` definition unit tests methods. """ def test_plot_sds_in_chromaticity_diagram_CIE1931(self): """ - Tests :func:`colour.plotting.diagrams.\ + Test :func:`colour.plotting.diagrams.\ plot_sds_in_chromaticity_diagram_CIE1931` definition. """ figure, axes = plot_sds_in_chromaticity_diagram_CIE1931( - [SDS_ILLUMINANTS['A'], SDS_ILLUMINANTS['D65']]) + [SDS_ILLUMINANTS["A"], SDS_ILLUMINANTS["D65"]] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -264,18 +278,19 @@ def test_plot_sds_in_chromaticity_diagram_CIE1931(self): class TestPlotSdsInChromaticityDiagramCIE1960UCS(unittest.TestCase): """ - Defines :func:`colour.plotting.diagrams.\ + Define :func:`colour.plotting.diagrams.\ plot_sds_in_chromaticity_diagram_CIE1960UCS` definition unit tests methods. """ def test_plot_sds_in_chromaticity_diagram_CIE1960UCS(self): """ - Tests :func:`colour.plotting.diagrams.\ + Test :func:`colour.plotting.diagrams.\ plot_sds_in_chromaticity_diagram_CIE1960UCS` definition. """ figure, axes = plot_sds_in_chromaticity_diagram_CIE1960UCS( - [SDS_ILLUMINANTS['A'], SDS_ILLUMINANTS['D65']]) + [SDS_ILLUMINANTS["A"], SDS_ILLUMINANTS["D65"]] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -283,22 +298,23 @@ def test_plot_sds_in_chromaticity_diagram_CIE1960UCS(self): class TestPlotSdsInChromaticityDiagramCIE1976UCS(unittest.TestCase): """ - Defines :func:`colour.plotting.diagrams.\ + Define :func:`colour.plotting.diagrams.\ plot_sds_in_chromaticity_diagram_CIE1976UCS` definition unit tests methods. """ def test_plot_sds_in_chromaticity_diagram_CIE1976UCS(self): """ - Tests :func:`colour.plotting.diagrams.\ + Test :func:`colour.plotting.diagrams.\ plot_sds_in_chromaticity_diagram_CIE1976UCS` definition. """ figure, axes = plot_sds_in_chromaticity_diagram_CIE1976UCS( - [SDS_ILLUMINANTS['A'], SDS_ILLUMINANTS['D65']]) + [SDS_ILLUMINANTS["A"], SDS_ILLUMINANTS["D65"]] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/plotting/tests/test_graph.py b/colour/plotting/tests/test_graph.py index 28c212041e..c4474a5dff 100644 --- a/colour/plotting/tests/test_graph.py +++ b/colour/plotting/tests/test_graph.py @@ -1,44 +1,45 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.plotting.graph` module. -""" - -from __future__ import division, unicode_literals - +"""Defines the unit tests for the :mod:`colour.plotting.graph` module.""" +import platform import tempfile import unittest from colour.plotting import plot_automatic_colour_conversion_graph from colour.utilities import is_networkx_installed -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestPlotAutomaticColourConversionGraph'] +__all__ = [ + "TestPlotAutomaticColourConversionGraph", +] class TestPlotAutomaticColourConversionGraph(unittest.TestCase): """ - Defines :func:`colour.plotting.graph.\ + Define :func:`colour.plotting.graph.\ plot_automatic_colour_conversion_graph` definition unit tests methods. """ def test_plot_automatic_colour_conversion_graph(self): """ - Tests :func:`colour.plotting.graph.\ + Test :func:`colour.plotting.graph.\ plot_automatic_colour_conversion_graph` definition. """ - if is_networkx_installed(raise_exception=True): # pragma: no cover + if not is_networkx_installed() or platform.system() in ( + "Windows", + "Microsoft", + ): # pragma: no cover return plot_automatic_colour_conversion_graph( # pragma: no cover - '{0}.png'.format(tempfile.mkstemp()[-1])) + f"{tempfile.mkstemp()[-1]}.png" + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/plotting/tests/test_models.py b/colour/plotting/tests/test_models.py index 76ffcd3241..b1702e4fc2 100644 --- a/colour/plotting/tests/test_models.py +++ b/colour/plotting/tests/test_models.py @@ -1,16 +1,12 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.plotting.models` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.plotting.models` module.""" import numpy as np import unittest from matplotlib.pyplot import Axes, Figure from colour.plotting import ( - common_colourspace_model_axis_reorder, plot_pointer_gamut, + colourspace_model_axis_reorder, + plot_pointer_gamut, plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931, plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS, plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS, @@ -20,173 +16,143 @@ plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931, plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS, plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS, - plot_single_cctf, plot_multi_cctfs, plot_constant_hue_loci) + plot_single_cctf, + plot_multi_cctfs, + plot_constant_hue_loci, +) from colour.plotting.models import ( plot_RGB_colourspaces_in_chromaticity_diagram, - plot_RGB_chromaticities_in_chromaticity_diagram, ellipses_MacAdam1942, - plot_ellipses_MacAdam1942_in_chromaticity_diagram) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + plot_RGB_chromaticities_in_chromaticity_diagram, + ellipses_MacAdam1942, + plot_ellipses_MacAdam1942_in_chromaticity_diagram, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'TestCommonColourspaceModelAxisReorder', 'TestPlotPointerGamut', - 'TestPlotRGBColourspacesInChromaticityDiagram', - 'TestPlotRGBColourspacesInChromaticityDiagramCIE1931', - 'TestPlotRGBColourspacesInChromaticityDiagramCIE1960UCS', - 'TestPlotRGBColourspacesInChromaticityDiagramCIE1976UCS', - 'TestPlotRGBChromaticitiesInChromaticityDiagram', - 'TestPlotRGBChromaticitiesInChromaticityDiagramCIE1931', - 'TestPlotRGBChromaticitiesInChromaticityDiagramCIE1960UCS', - 'TestPlotRGBChromaticitiesInChromaticityDiagramCIE1976UCS', - 'TestPlotEllipsesMacAdam1942InChromaticityDiagram', - 'TestPlotEllipsesMacAdam1942InChromaticityDiagramCIE1931', - 'TestPlotEllipsesMacAdam1942InChromaticityDiagramCIE1960UCS', - 'TestPlotEllipsesMacAdam1942InChromaticityDiagramCIE1976UCS', - 'TestPlotSingleCctf', 'TestPlotMultiCctfs', 'TestPlotConstantHueLoci' + "TestCommonColourspaceModelAxisReorder", + "TestPlotPointerGamut", + "TestPlotRGBColourspacesInChromaticityDiagram", + "TestPlotRGBColourspacesInChromaticityDiagramCIE1931", + "TestPlotRGBColourspacesInChromaticityDiagramCIE1960UCS", + "TestPlotRGBColourspacesInChromaticityDiagramCIE1976UCS", + "TestPlotRGBChromaticitiesInChromaticityDiagram", + "TestPlotRGBChromaticitiesInChromaticityDiagramCIE1931", + "TestPlotRGBChromaticitiesInChromaticityDiagramCIE1960UCS", + "TestPlotRGBChromaticitiesInChromaticityDiagramCIE1976UCS", + "TestPlotEllipsesMacAdam1942InChromaticityDiagram", + "TestPlotEllipsesMacAdam1942InChromaticityDiagramCIE1931", + "TestPlotEllipsesMacAdam1942InChromaticityDiagramCIE1960UCS", + "TestPlotEllipsesMacAdam1942InChromaticityDiagramCIE1976UCS", + "TestPlotSingleCctf", + "TestPlotMultiCctfs", + "TestPlotConstantHueLoci", ] class TestCommonColourspaceModelAxisReorder(unittest.TestCase): """ - Defines :func:`colour.plotting.models.\ -common_colourspace_model_axis_reorder` definition unit tests methods. + Define :func:`colour.plotting.models.colourspace_model_axis_reorder` + definition unit tests methods. """ - def test_common_colourspace_model_axis_reorder(self): + def test_colourspace_model_axis_reorder(self): """ - Tests :func:`colour.plotting.models.\ -common_colourspace_model_axis_reorder` definition. + Test :func:`colour.plotting.models.colourspace_model_axis_reorder` + definition. """ a = np.array([0, 1, 2]) np.testing.assert_almost_equal( - common_colourspace_model_axis_reorder(a), - np.array([0, 1, 2]), - decimal=7) - - np.testing.assert_almost_equal( - common_colourspace_model_axis_reorder(a, 'CIE Lab'), - np.array([1, 2, 0]), - decimal=7) - - np.testing.assert_almost_equal( - common_colourspace_model_axis_reorder(a, 'CIE LCHab'), - np.array([1, 2, 0]), - decimal=7) - - np.testing.assert_almost_equal( - common_colourspace_model_axis_reorder(a, 'CIE Luv'), - np.array([1, 2, 0]), - decimal=7) - - np.testing.assert_almost_equal( - common_colourspace_model_axis_reorder(a, 'CIE LCHab'), - np.array([1, 2, 0]), - decimal=7) - - np.testing.assert_almost_equal( - common_colourspace_model_axis_reorder(a, 'DIN 99'), - np.array([1, 2, 0]), - decimal=7) - - np.testing.assert_almost_equal( - common_colourspace_model_axis_reorder(a, 'Hunter Lab'), - np.array([1, 2, 0]), - decimal=7) - - np.testing.assert_almost_equal( - common_colourspace_model_axis_reorder(a, 'Hunter Rdab'), - np.array([1, 2, 0]), - decimal=7) - - np.testing.assert_almost_equal( - common_colourspace_model_axis_reorder(a, 'IPT'), - np.array([1, 2, 0]), - decimal=7) - - np.testing.assert_almost_equal( - common_colourspace_model_axis_reorder(a, 'JzAzBz'), + colourspace_model_axis_reorder(a, "CIE Lab"), np.array([1, 2, 0]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - common_colourspace_model_axis_reorder(a, 'OSA UCS'), + colourspace_model_axis_reorder(a, "IPT"), np.array([1, 2, 0]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - common_colourspace_model_axis_reorder(a, 'hdr-CIELAB'), + colourspace_model_axis_reorder(a, "OSA UCS"), np.array([1, 2, 0]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - common_colourspace_model_axis_reorder(a, 'hdr-IPT'), - np.array([1, 2, 0]), - decimal=7) + colourspace_model_axis_reorder( + colourspace_model_axis_reorder(a, "OSA UCS"), + "OSA UCS", + "Inverse", + ), + np.array([0, 1, 2]), + decimal=7, + ) class TestPlotPointerGamut(unittest.TestCase): """ - Defines :func:`colour.plotting.models.plot_pointer_gamut` definition unit + Define :func:`colour.plotting.models.plot_pointer_gamut` definition unit tests methods. """ def test_plot_pointer_gamut(self): - """ - Tests :func:`colour.plotting.models.plot_pointer_gamut` definition. - """ + """Test :func:`colour.plotting.models.plot_pointer_gamut` definition.""" figure, axes = plot_pointer_gamut() self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) - figure, axes = plot_pointer_gamut(method='CIE 1960 UCS') + figure, axes = plot_pointer_gamut(method="CIE 1960 UCS") self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) - figure, axes = plot_pointer_gamut(method='CIE 1976 UCS') + figure, axes = plot_pointer_gamut(method="CIE 1976 UCS") self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) self.assertRaises( - ValueError, lambda: plot_pointer_gamut(method='Undefined')) + ValueError, lambda: plot_pointer_gamut(method="Undefined") + ) class TestPlotRGBColourspacesInChromaticityDiagram(unittest.TestCase): """ - Defines :func:`colour.plotting.models.\ + Define :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram` definition unit tests methods. """ def test_plot_RGB_colourspaces_in_chromaticity_diagram(self): """ - Tests :func:`colour.plotting.models.\ + Test :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram` definition. """ figure, axes = plot_RGB_colourspaces_in_chromaticity_diagram( - ['ITU-R BT.709', 'ACEScg', 'S-Gamut'], + ["ITU-R BT.709", "ACEScg", "S-Gamut"], show_pointer_gamut=True, chromatically_adapt=True, - plot_kwargs={'linestyle': 'dashed'}) + plot_kwargs={"linestyle": "dashed"}, + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) figure, axes = plot_RGB_colourspaces_in_chromaticity_diagram( - ['ITU-R BT.709', 'ACEScg', 'S-Gamut'], - plot_kwargs=[{ - 'linestyle': 'dashed' - }] * 3) + ["ITU-R BT.709", "ACEScg", "S-Gamut"], + plot_kwargs=[{"linestyle": "dashed"}] * 3, + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -194,71 +160,81 @@ def test_plot_RGB_colourspaces_in_chromaticity_diagram(self): self.assertRaises( ValueError, lambda: plot_RGB_colourspaces_in_chromaticity_diagram( - ['ITU-R BT.709', 'ACEScg', 'S-Gamut'], + ["ITU-R BT.709", "ACEScg", "S-Gamut"], chromaticity_diagram_callable=lambda **x: x, - method='Undefined') + method="Undefined", + ), ) class TestPlotRGBColourspacesInChromaticityDiagramCIE1931(unittest.TestCase): """ - Defines :func:`colour.plotting.models.\ + Define :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931` definition unit tests methods. """ def test_plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931(self): """ - Tests :func:`colour.plotting.models.\ + Test :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931` definition. """ figure, axes = plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931( - ['ITU-R BT.709', 'ACEScg', 'S-Gamut']) + ["ITU-R BT.709", "ACEScg", "S-Gamut"] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) class TestPlotRGBColourspacesInChromaticityDiagramCIE1960UCS( - unittest.TestCase): + unittest.TestCase +): """ - Defines :func:`colour.plotting.models.\ + Define :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS` definition unit tests methods. """ def test_plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS(self): """ - Tests :func:`colour.plotting.models.\ + Test :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS` definition. """ - figure, axes = ( - plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS( - ['ITU-R BT.709', 'ACEScg', 'S-Gamut'])) + ( + figure, + axes, + ) = plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS( + ["ITU-R BT.709", "ACEScg", "S-Gamut"] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) class TestPlotRGBColourspacesInChromaticityDiagramCIE1976UCS( - unittest.TestCase): + unittest.TestCase +): """ - Defines :func:`colour.plotting.models.\ + Define :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS` definition unit tests methods. """ def test_plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS(self): """ - Tests :func:`colour.plotting.models.\ + Test :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS` definition. """ - figure, axes = ( - plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS( - ['ITU-R BT.709', 'ACEScg', 'S-Gamut'])) + ( + figure, + axes, + ) = plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS( + ["ITU-R BT.709", "ACEScg", "S-Gamut"] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -266,18 +242,19 @@ def test_plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS(self): class TestPlotRGBChromaticitiesInChromaticityDiagram(unittest.TestCase): """ - Defines :func:`colour.plotting.models.\ + Define :func:`colour.plotting.models.\ plot_RGB_chromaticities_in_chromaticity_diagram` definition unit tests methods. """ def test_plot_RGB_chromaticities_in_chromaticity_diagram(self): """ - Tests :func:`colour.plotting.models.\ + Test :func:`colour.plotting.models.\ plot_RGB_chromaticities_in_chromaticity_diagram` definition. """ figure, axes = plot_RGB_chromaticities_in_chromaticity_diagram( - np.random.random((128, 128, 3)), scatter_kwargs={'marker': 'v'}) + np.random.random((128, 128, 3)), scatter_kwargs={"marker": "v"} + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -285,63 +262,72 @@ def test_plot_RGB_chromaticities_in_chromaticity_diagram(self): class TestPlotRGBChromaticitiesInChromaticityDiagramCIE1931(unittest.TestCase): """ - Defines :func:`colour.plotting.models.\ + Define :func:`colour.plotting.models.\ plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931` definition unit tests methods. """ def test_plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931(self): """ - Tests :func:`colour.plotting.models.\ + Test :func:`colour.plotting.models.\ plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931` definition. """ figure, axes = plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931( - np.random.random((128, 128, 3))) + np.random.random((128, 128, 3)) + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) class TestPlotRGBChromaticitiesInChromaticityDiagramCIE1960UCS( - unittest.TestCase): + unittest.TestCase +): """ - Defines :func:`colour.plotting.models.\ + Define :func:`colour.plotting.models.\ plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS` definition unit tests methods. """ def test_plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS(self): """ - Tests :func:`colour.plotting.models.\ + Test :func:`colour.plotting.models.\ plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS` definition. """ - figure, axes = ( - plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS( - np.random.random((128, 128, 3)))) + ( + figure, + axes, + ) = plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS( + np.random.random((128, 128, 3)) + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) class TestPlotRGBChromaticitiesInChromaticityDiagramCIE1976UCS( - unittest.TestCase): + unittest.TestCase +): """ - Defines :func:`colour.plotting.models.\ + Define :func:`colour.plotting.models.\ plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS` definition unit tests methods. """ def test_plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS(self): """ - Tests :func:`colour.plotting.models.\ + Test :func:`colour.plotting.models.\ plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS` definition. """ - figure, axes = ( - plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS( - np.random.random((128, 128, 3)))) + ( + figure, + axes, + ) = plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS( + np.random.random((128, 128, 3)) + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -349,110 +335,120 @@ def test_plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS(self): class TestEllipsesMacAdam1942(unittest.TestCase): """ - Defines :func:`colour.plotting.models.ellipses_MacAdam1942` definition unit + Define :func:`colour.plotting.models.ellipses_MacAdam1942` definition unit tests methods. """ def test_ellipses_MacAdam1942(self): - """ - Tests :func:`colour.plotting.models.ellipses_MacAdam1942` definition. - """ + """Test :func:`colour.plotting.models.ellipses_MacAdam1942` definition.""" self.assertEqual(len(ellipses_MacAdam1942()), 25) self.assertRaises( - ValueError, lambda: ellipses_MacAdam1942(method='Undefined')) + ValueError, lambda: ellipses_MacAdam1942(method="Undefined") + ) class TestPlotEllipsesMacAdam1942InChromaticityDiagram(unittest.TestCase): """ - Defines :func:`colour.plotting.models.\ + Define :func:`colour.plotting.models.\ plot_ellipses_MacAdam1942_in_chromaticity_diagram` definition unit tests methods. """ def test_plot_ellipses_MacAdam1942_in_chromaticity_diagram(self): """ - Tests :func:`colour.plotting.models.\ + Test :func:`colour.plotting.models.\ plot_ellipses_MacAdam1942_in_chromaticity_diagram` definition. """ figure, axes = plot_ellipses_MacAdam1942_in_chromaticity_diagram( - chromaticity_diagram_clipping=True, ellipse_kwargs={'color': 'k'}) + chromaticity_diagram_clipping=True, ellipse_kwargs={"color": "k"} + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) figure, axes = plot_ellipses_MacAdam1942_in_chromaticity_diagram( chromaticity_diagram_clipping=True, - ellipse_kwargs=[{ - 'color': 'k' - }] * 25) + ellipse_kwargs=[{"color": "k"}] * 25, + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) class TestPlotEllipsesMacAdam1942InChromaticityDiagramCIE1931( - unittest.TestCase): + unittest.TestCase +): """ - Defines :func:`colour.plotting.models.\ + Define :func:`colour.plotting.models.\ plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931` definition unit tests methods. """ def test_plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931(self): """ - Tests :func:`colour.plotting.models.\ + Test :func:`colour.plotting.models.\ plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931` definition. """ - figure, axes = ( - plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931()) + ( + figure, + axes, + ) = plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931() self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) class TestPlotEllipsesMacAdam1942InChromaticityDiagramCIE1960UCS( - unittest.TestCase): + unittest.TestCase +): """ - Defines :func:`colour.plotting.models.\ + Define :func:`colour.plotting.models.\ plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS` definition unit tests methods. """ def test_plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS( - self): + self, + ): """ - Tests :func:`colour.plotting.models.\ + Test :func:`colour.plotting.models.\ plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS` definition. """ - figure, axes = ( - plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS()) + ( + figure, + axes, + ) = plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS() self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) class TestPlotEllipsesMacAdam1942InChromaticityDiagramCIE1976UCS( - unittest.TestCase): + unittest.TestCase +): """ - Defines :func:`colour.plotting.models.\ + Define :func:`colour.plotting.models.\ plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS` definition unit tests methods. """ def test_plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS( - self): + self, + ): """ - Tests :func:`colour.plotting.models.\ + Test :func:`colour.plotting.models.\ plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS` definition. """ - figure, axes = ( - plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS()) + ( + figure, + axes, + ) = plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS() self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -460,16 +456,14 @@ def test_plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS( class TestPlotSingleCctf(unittest.TestCase): """ - Defines :func:`colour.plotting.models.plot_single_cctf` definition unit + Define :func:`colour.plotting.models.plot_single_cctf` definition unit tests methods. """ def test_plot_single_cctf(self): - """ - Tests :func:`colour.plotting.models.plot_single_cctf` definition. - """ + """Test :func:`colour.plotting.models.plot_single_cctf` definition.""" - figure, axes = plot_single_cctf('ITU-R BT.709') + figure, axes = plot_single_cctf("ITU-R BT.709") self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -477,16 +471,14 @@ def test_plot_single_cctf(self): class TestPlotMultiCctfs(unittest.TestCase): """ - Defines :func:`colour.plotting.models.plot_multi_cctfs` definition unit + Define :func:`colour.plotting.models.plot_multi_cctfs` definition unit tests methods. """ def test_plot_multi_cctfs(self): - """ - Tests :func:`colour.plotting.models.plot_multi_cctfs` definition. - """ + """Test :func:`colour.plotting.models.plot_multi_cctfs` definition.""" - figure, axes = plot_multi_cctfs(['ITU-R BT.709', 'sRGB']) + figure, axes = plot_multi_cctfs(["ITU-R BT.709", "sRGB"]) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -494,85 +486,96 @@ def test_plot_multi_cctfs(self): class TestPlotConstantHueLoci(unittest.TestCase): """ - Defines :func:`colour.plotting.models.plot_constant_hue_loci` definition + Define :func:`colour.plotting.models.plot_constant_hue_loci` definition unit tests methods. """ def test_plot_constant_hue_loci(self): - """ - Tests :func:`colour.plotting.models.plot_constant_hue_loci` definition. - """ + """Test :func:`colour.plotting.models.plot_constant_hue_loci` definition.""" - data = np.array([ - [ - None, - np.array([0.95010000, 1.00000000, 1.08810000]), - np.array([0.40920000, 0.28120000, 0.30600000]), - np.array([ - [0.02495100, 0.01908600, 0.02032900], - [0.10944300, 0.06235900, 0.06788100], - [0.27186500, 0.18418700, 0.19565300], - [0.48898900, 0.40749400, 0.44854600], - ]), - None, - ], - [ - None, - np.array([0.95010000, 1.00000000, 1.08810000]), - np.array([0.30760000, 0.48280000, 0.42770000]), - np.array([ - [0.02108000, 0.02989100, 0.02790400], - [0.06194700, 0.11251000, 0.09334400], - [0.15255800, 0.28123300, 0.23234900], - [0.34157700, 0.56681300, 0.47035300], - ]), - None, - ], + data = np.array( [ - None, - np.array([0.95010000, 1.00000000, 1.08810000]), - np.array([0.39530000, 0.28120000, 0.18450000]), - np.array([ - [0.02436400, 0.01908600, 0.01468800], - [0.10331200, 0.06235900, 0.02854600], - [0.26311900, 0.18418700, 0.12109700], - [0.43158700, 0.40749400, 0.39008600], - ]), - None, - ], - [ - None, - np.array([0.95010000, 1.00000000, 1.08810000]), - np.array([0.20510000, 0.18420000, 0.57130000]), - np.array([ - [0.03039800, 0.02989100, 0.06123300], - [0.08870000, 0.08498400, 0.21843500], - [0.18405800, 0.18418700, 0.40111400], - [0.32550100, 0.34047200, 0.50296900], - [0.53826100, 0.56681300, 0.80010400], - ]), - None, - ], - [ - None, - np.array([0.95010000, 1.00000000, 1.08810000]), - np.array([0.35770000, 0.28120000, 0.11250000]), - np.array([ - [0.03678100, 0.02989100, 0.01481100], - [0.17127700, 0.11251000, 0.01229900], - [0.30080900, 0.28123300, 0.21229800], - [0.52976000, 0.40749400, 0.11720000], - ]), - None, - ], - ]) + [ + None, + np.array([0.95010000, 1.00000000, 1.08810000]), + np.array([0.40920000, 0.28120000, 0.30600000]), + np.array( + [ + [0.02495100, 0.01908600, 0.02032900], + [0.10944300, 0.06235900, 0.06788100], + [0.27186500, 0.18418700, 0.19565300], + [0.48898900, 0.40749400, 0.44854600], + ] + ), + None, + ], + [ + None, + np.array([0.95010000, 1.00000000, 1.08810000]), + np.array([0.30760000, 0.48280000, 0.42770000]), + np.array( + [ + [0.02108000, 0.02989100, 0.02790400], + [0.06194700, 0.11251000, 0.09334400], + [0.15255800, 0.28123300, 0.23234900], + [0.34157700, 0.56681300, 0.47035300], + ] + ), + None, + ], + [ + None, + np.array([0.95010000, 1.00000000, 1.08810000]), + np.array([0.39530000, 0.28120000, 0.18450000]), + np.array( + [ + [0.02436400, 0.01908600, 0.01468800], + [0.10331200, 0.06235900, 0.02854600], + [0.26311900, 0.18418700, 0.12109700], + [0.43158700, 0.40749400, 0.39008600], + ] + ), + None, + ], + [ + None, + np.array([0.95010000, 1.00000000, 1.08810000]), + np.array([0.20510000, 0.18420000, 0.57130000]), + np.array( + [ + [0.03039800, 0.02989100, 0.06123300], + [0.08870000, 0.08498400, 0.21843500], + [0.18405800, 0.18418700, 0.40111400], + [0.32550100, 0.34047200, 0.50296900], + [0.53826100, 0.56681300, 0.80010400], + ] + ), + None, + ], + [ + None, + np.array([0.95010000, 1.00000000, 1.08810000]), + np.array([0.35770000, 0.28120000, 0.11250000]), + np.array( + [ + [0.03678100, 0.02989100, 0.01481100], + [0.17127700, 0.11251000, 0.01229900], + [0.30080900, 0.28123300, 0.21229800], + [0.52976000, 0.40749400, 0.11720000], + ] + ), + None, + ], + ] + ) figure, axes = plot_constant_hue_loci( - data, 'IPT', scatter_kwargs={'marker': 'v'}) + data, "IPT", scatter_kwargs={"marker": "v"} + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/plotting/tests/test_notation.py b/colour/plotting/tests/test_notation.py index 8349eab023..94d1c9137f 100644 --- a/colour/plotting/tests/test_notation.py +++ b/colour/plotting/tests/test_notation.py @@ -1,41 +1,39 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.plotting.notation` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.plotting.notation` module.""" import unittest from matplotlib.pyplot import Axes, Figure -from colour.plotting import (plot_single_munsell_value_function, - plot_multi_munsell_value_functions) +from colour.plotting import ( + plot_single_munsell_value_function, + plot_multi_munsell_value_functions, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestPlotSingleMunsellValueFunction', 'TestPlotMultiMunsellValueFunctions' + "TestPlotSingleMunsellValueFunction", + "TestPlotMultiMunsellValueFunctions", ] class TestPlotSingleMunsellValueFunction(unittest.TestCase): """ - Defines :func:`colour.plotting.notation.plot_single_munsell_value_function` + Define :func:`colour.plotting.notation.plot_single_munsell_value_function` definition unit tests methods. """ def test_plot_single_munsell_value_function(self): """ - Tests :func:`colour.plotting.notation.\ + Test :func:`colour.plotting.notation.\ plot_single_munsell_value_function` definition. """ - figure, axes = plot_single_munsell_value_function('ASTM D1535') + figure, axes = plot_single_munsell_value_function("ASTM D1535") self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -43,22 +41,23 @@ def test_plot_single_munsell_value_function(self): class TestPlotMultiMunsellValueFunctions(unittest.TestCase): """ - Defines :func:`colour.plotting.notation.plot_multi_munsell_value_functions` + Define :func:`colour.plotting.notation.plot_multi_munsell_value_functions` definition unit tests methods. """ def test_plot_multi_munsell_value_functions(self): """ - Tests :func:`colour.plotting.notation.\ + Test :func:`colour.plotting.notation.\ plot_multi_munsell_value_functions` definition. """ figure, axes = plot_multi_munsell_value_functions( - ['ASTM D1535', 'McCamy 1987']) + ["ASTM D1535", "McCamy 1987"] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/plotting/tests/test_phenomena.py b/colour/plotting/tests/test_phenomena.py index 56a38c21aa..9e0f683038 100644 --- a/colour/plotting/tests/test_phenomena.py +++ b/colour/plotting/tests/test_phenomena.py @@ -1,35 +1,35 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.plotting.phenomena` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.plotting.phenomena` module.""" import unittest from matplotlib.pyplot import Axes, Figure -from colour.plotting import (plot_single_sd_rayleigh_scattering, - plot_the_blue_sky) +from colour.plotting import ( + plot_single_sd_rayleigh_scattering, + plot_the_blue_sky, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestPlotSingleSdRayleighScattering', 'TestPlotTheBlueSky'] +__all__ = [ + "TestPlotSingleSdRayleighScattering", + "TestPlotTheBlueSky", +] class TestPlotSingleSdRayleighScattering(unittest.TestCase): """ - Defines :func:`colour.plotting.phenomena.\ + Define :func:`colour.plotting.phenomena.\ plot_single_sd_rayleigh_scattering` definition unit tests methods. """ def test_plot_single_sd_rayleigh_scattering(self): """ - Tests :func:`colour.plotting.phenomena.\ + Test :func:`colour.plotting.phenomena.\ plot_single_sd_rayleigh_scattering` definition. """ @@ -41,14 +41,12 @@ def test_plot_single_sd_rayleigh_scattering(self): class TestPlotTheBlueSky(unittest.TestCase): """ - Defines :func:`colour.plotting.phenomena.plot_the_blue_sky` definition unit + Define :func:`colour.plotting.phenomena.plot_the_blue_sky` definition unit tests methods. """ def test_plot_the_blue_sky(self): - """ - Tests :func:`colour.plotting.phenomena.plot_the_blue_sky` definition. - """ + """Test :func:`colour.plotting.phenomena.plot_the_blue_sky` definition.""" figure, axes = plot_the_blue_sky() @@ -56,5 +54,5 @@ def test_plot_the_blue_sky(self): self.assertIsInstance(axes, Axes) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/plotting/tests/test_quality.py b/colour/plotting/tests/test_quality.py index 0866a99ce8..b87903684c 100644 --- a/colour/plotting/tests/test_quality.py +++ b/colour/plotting/tests/test_quality.py @@ -1,52 +1,54 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.plotting.quality` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.plotting.quality` module.""" import unittest from matplotlib.pyplot import Axes, Figure -from colour.colorimetry import (SDS_ILLUMINANTS, SDS_LIGHT_SOURCES, - SpectralShape) -from colour.plotting import (plot_single_sd_colour_rendering_index_bars, - plot_multi_sds_colour_rendering_indexes_bars, - plot_single_sd_colour_quality_scale_bars, - plot_multi_sds_colour_quality_scales_bars) +from colour.colorimetry import ( + SDS_ILLUMINANTS, + SDS_LIGHT_SOURCES, + SpectralShape, + reshape_sd, +) +from colour.plotting import ( + plot_single_sd_colour_rendering_index_bars, + plot_multi_sds_colour_rendering_indexes_bars, + plot_single_sd_colour_quality_scale_bars, + plot_multi_sds_colour_quality_scales_bars, +) from colour.plotting.quality import plot_colour_quality_bars from colour.quality import colour_quality_scale -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestPlotColourQualityBars', 'TestPlotSingleSdColourRenderingIndexBars', - 'TestPlotMultiSdsColourRenderingIndexesBars', - 'TestPlotSingleSdColourQualityScaleBars', - 'TestPlotMultiSdsColourQualityScalesBars' + "TestPlotColourQualityBars", + "TestPlotSingleSdColourRenderingIndexBars", + "TestPlotMultiSdsColourRenderingIndexesBars", + "TestPlotSingleSdColourQualityScaleBars", + "TestPlotMultiSdsColourQualityScalesBars", ] class TestPlotColourQualityBars(unittest.TestCase): """ - Defines :func:`colour.plotting.quality.plot_colour_quality_bars` definition + Define :func:`colour.plotting.quality.plot_colour_quality_bars` definition unit tests methods. """ def test_plot_colour_quality_bars(self): """ - Tests :func:`colour.plotting.quality.plot_colour_quality_bars` + Test :func:`colour.plotting.quality.plot_colour_quality_bars` definition. """ - illuminant = SDS_ILLUMINANTS['FL2'] - light_source = SDS_LIGHT_SOURCES['Kinoton 75P'] - light_source = light_source.copy().align(SpectralShape(360, 830, 1)) + illuminant = SDS_ILLUMINANTS["FL2"] + light_source = SDS_LIGHT_SOURCES["Kinoton 75P"] + light_source = reshape_sd(light_source, SpectralShape(360, 830, 1)) cqs_i = colour_quality_scale(illuminant, additional_data=True) cqs_l = colour_quality_scale(light_source, additional_data=True) @@ -58,18 +60,19 @@ def test_plot_colour_quality_bars(self): class TestPlotSingleSdColourRenderingIndexBars(unittest.TestCase): """ - Defines :func:`colour.plotting.quality.\ + Define :func:`colour.plotting.quality.\ plot_single_sd_colour_rendering_index_bars` definition unit tests methods. """ def test_plot_single_sd_colour_rendering_index_bars(self): """ - Tests :func:`colour.plotting.quality.\ + Test :func:`colour.plotting.quality.\ plot_single_sd_colour_rendering_index_bars` definition. """ figure, axes = plot_single_sd_colour_rendering_index_bars( - SDS_ILLUMINANTS['FL2']) + SDS_ILLUMINANTS["FL2"] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -77,18 +80,19 @@ def test_plot_single_sd_colour_rendering_index_bars(self): class TestPlotMultiSdsColourRenderingIndexesBars(unittest.TestCase): """ - Defines :func:`colour.plotting.quality.\ + Define :func:`colour.plotting.quality.\ plot_multi_sds_colour_rendering_indexes_bars` definition unit tests methods. """ def test_plot_multi_sds_colour_rendering_indexes_bars(self): """ - Tests :func:`colour.plotting.quality.\ + Test :func:`colour.plotting.quality.\ plot_multi_sds_colour_rendering_indexes_bars` definition. """ figure, axes = plot_multi_sds_colour_rendering_indexes_bars( - [SDS_ILLUMINANTS['FL2'], SDS_LIGHT_SOURCES['Kinoton 75P']]) + [SDS_ILLUMINANTS["FL2"], SDS_LIGHT_SOURCES["Kinoton 75P"]] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -96,18 +100,19 @@ def test_plot_multi_sds_colour_rendering_indexes_bars(self): class TestPlotSingleSdColourQualityScaleBars(unittest.TestCase): """ - Defines :func:`colour.plotting.quality.\ + Define :func:`colour.plotting.quality.\ plot_single_sd_colour_quality_scale_bars` definition unit tests methods. """ def test_plot_single_sd_colour_quality_scale_bars(self): """ - Tests :func:`colour.plotting.quality.\ + Test :func:`colour.plotting.quality.\ plot_single_sd_colour_quality_scale_bars` definition. """ figure, axes = plot_single_sd_colour_quality_scale_bars( - SDS_ILLUMINANTS['FL2']) + SDS_ILLUMINANTS["FL2"] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -115,22 +120,23 @@ def test_plot_single_sd_colour_quality_scale_bars(self): class TestPlotMultiSdsColourQualityScalesBars(unittest.TestCase): """ - Defines :func:`colour.plotting.quality.\ + Define :func:`colour.plotting.quality.\ plot_multi_sds_colour_quality_scales_bars` definition unit tests methods. """ def test_plot_multi_sds_colour_quality_scales_bars(self): """ - Tests :func:`colour.plotting.quality.\ + Test :func:`colour.plotting.quality.\ plot_multi_sds_colour_quality_scales_bars` definition. """ figure, axes = plot_multi_sds_colour_quality_scales_bars( - [SDS_ILLUMINANTS['FL2'], SDS_LIGHT_SOURCES['Kinoton 75P']]) + [SDS_ILLUMINANTS["FL2"], SDS_LIGHT_SOURCES["Kinoton 75P"]] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/plotting/tests/test_section.py b/colour/plotting/tests/test_section.py new file mode 100644 index 0000000000..324836c140 --- /dev/null +++ b/colour/plotting/tests/test_section.py @@ -0,0 +1,150 @@ +"""Defines the unit tests for the :mod:`colour.plotting.section` module.""" + +import unittest +from matplotlib.pyplot import Axes, Figure + +from colour.geometry import primitive_cube +from colour.models import RGB_COLOURSPACE_sRGB, RGB_to_XYZ +from colour.plotting import ( + plot_visible_spectrum_section, + plot_RGB_colourspace_section, +) +from colour.plotting.section import ( + plot_hull_section_colours, + plot_hull_section_contour, +) +from colour.utilities import is_trimesh_installed + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "TestPlotHullSectionColours", + "TestPlotHullSectionContour", + "TestPlotVisibleSpectrumSection", + "TestPlotRGBColourspaceSection", +] + + +class TestPlotHullSectionColours(unittest.TestCase): + """ + Define :func:`colour.plotting.section.plot_hull_section_colours` + definition unit tests methods. + """ + + def test_plot_hull_section_colours(self): + """ + Test :func:`colour.plotting.section.plot_hull_section_colours` + definition. + """ + + if not is_trimesh_installed: # pragma: no cover + return + + import trimesh + + vertices, faces, _outline = primitive_cube(1, 1, 1, 64, 64, 64) + XYZ_vertices = RGB_to_XYZ( + vertices["position"] + 0.5, + RGB_COLOURSPACE_sRGB.whitepoint, + RGB_COLOURSPACE_sRGB.whitepoint, + RGB_COLOURSPACE_sRGB.matrix_RGB_to_XYZ, + ) + hull = trimesh.Trimesh(XYZ_vertices, faces, process=False) + + figure, axes = plot_hull_section_colours(hull) + + self.assertIsInstance(figure, Figure) + self.assertIsInstance(axes, Axes) + + figure, axes = plot_hull_section_colours(hull, axis="+x") + + self.assertIsInstance(figure, Figure) + self.assertIsInstance(axes, Axes) + + figure, axes = plot_hull_section_colours(hull, axis="+y") + + self.assertIsInstance(figure, Figure) + self.assertIsInstance(axes, Axes) + + +class TestPlotHullSectionContour(unittest.TestCase): + """ + Define :func:`colour.plotting.section.plot_hull_section_contour` + definition unit tests methods. + """ + + def test_plot_hull_section_contour(self): + """ + Test :func:`colour.plotting.section.plot_hull_section_contour` + definition. + """ + + if not is_trimesh_installed: # pragma: no cover + return + + import trimesh + + vertices, faces, _outline = primitive_cube(1, 1, 1, 64, 64, 64) + XYZ_vertices = RGB_to_XYZ( + vertices["position"] + 0.5, + RGB_COLOURSPACE_sRGB.whitepoint, + RGB_COLOURSPACE_sRGB.whitepoint, + RGB_COLOURSPACE_sRGB.matrix_RGB_to_XYZ, + ) + hull = trimesh.Trimesh(XYZ_vertices, faces, process=False) + + figure, axes = plot_hull_section_contour(hull) + + self.assertIsInstance(figure, Figure) + self.assertIsInstance(axes, Axes) + + +class TestPlotVisibleSpectrumSection(unittest.TestCase): + """ + Define :func:`colour.plotting.section.plot_visible_spectrum_section` + definition unit tests methods. + """ + + def test_plot_visible_spectrum_section(self): + """ + Test :func:`colour.plotting.section.plot_visible_spectrum_section` + definition. + """ + + if not is_trimesh_installed: # pragma: no cover + return + + figure, axes = plot_visible_spectrum_section() + + self.assertIsInstance(figure, Figure) + self.assertIsInstance(axes, Axes) + + +class TestPlotRGBColourspaceSection(unittest.TestCase): + """ + Define :func:`colour.plotting.section.plot_RGB_colourspace_section` + definition unit tests methods. + """ + + def test_plot_RGB_colourspace_section(self): + """ + Test :func:`colour.plotting.section.plot_RGB_colourspace_section` + definition. + """ + + if not is_trimesh_installed: # pragma: no cover + return + + figure, axes = plot_RGB_colourspace_section("sRGB") + + self.assertIsInstance(figure, Figure) + self.assertIsInstance(axes, Axes) + + +if __name__ == "__main__": + unittest.main() diff --git a/colour/plotting/tests/test_temperature.py b/colour/plotting/tests/test_temperature.py index c9dde9f310..46f7e27d66 100644 --- a/colour/plotting/tests/test_temperature.py +++ b/colour/plotting/tests/test_temperature.py @@ -1,46 +1,41 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.plotting.temperature` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.plotting.temperature` module.""" import unittest from matplotlib.pyplot import Axes, Figure from colour.plotting import ( plot_planckian_locus_in_chromaticity_diagram_CIE1931, - plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS) + plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS, +) from colour.plotting.temperature import ( - plot_planckian_locus, plot_planckian_locus_CIE1931, - plot_planckian_locus_CIE1960UCS, - plot_planckian_locus_in_chromaticity_diagram) + plot_planckian_locus, + plot_planckian_locus_in_chromaticity_diagram, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestPlotPlanckianLocus', 'TestPlotPlanckianLocusCIE1931', - 'TestPlotPlanckianLocusCIE1960UCS', - 'TestPlotPlanckianLocusInChromaticityDiagram', - 'TestPlotPlanckianLocusInChromaticityDiagramCIE1931', - 'TestPlotPlanckianLocusInChromaticityDiagramCIE1960UCS' + "TestPlotPlanckianLocus", + "TestPlotPlanckianLocusInChromaticityDiagram", + "TestPlotPlanckianLocusInChromaticityDiagramCIE1931", + "TestPlotPlanckianLocusInChromaticityDiagramCIE1960UCS", ] class TestPlotPlanckianLocus(unittest.TestCase): """ - Defines :func:`colour.plotting.temperature.plot_planckian_locus` definition + Define :func:`colour.plotting.temperature.plot_planckian_locus` definition unit tests methods. """ def test_plot_planckian_locus(self): """ - Tests :func:`colour.plotting.temperature.plot_planckian_locus` + Test :func:`colour.plotting.temperature.plot_planckian_locus` definition. """ @@ -50,40 +45,22 @@ def test_plot_planckian_locus(self): self.assertIsInstance(axes, Axes) self.assertRaises( - ValueError, lambda: plot_planckian_locus(method='Undefined')) - - -class TestPlotPlanckianLocusCIE1931(unittest.TestCase): - """ - Defines :func:`colour.plotting.temperature.plot_planckian_locus_CIE1931` - definition unit tests methods. - """ - - def test_plot_planckian_locus(self): - """ - Tests :func:`colour.plotting.temperature.plot_planckian_locus_CIE1931` - definition. - """ + ValueError, lambda: plot_planckian_locus(method="Undefined") + ) - figure, axes = plot_planckian_locus_CIE1931() + figure, axes = plot_planckian_locus(method="CIE 1976 UCS") self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) + figure, axes = plot_planckian_locus(planckian_locus_colours="RGB") -class TestPlotPlanckianLocusCIE1960UCS(unittest.TestCase): - """ - Defines :func:`colour.plotting.temperature.plot_planckian_locus_CIE1960UCS` - definition unit tests methods. - """ - - def test_plot_planckian_locus(self): - """ - Tests :func:`colour.plotting.temperature.\ -plot_planckian_locus_CIE1960UCS` definition. - """ + self.assertIsInstance(figure, Figure) + self.assertIsInstance(axes, Axes) - figure, axes = plot_planckian_locus_CIE1960UCS() + figure, axes = plot_planckian_locus( + planckian_locus_labels=[5500, 6500] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -91,65 +68,68 @@ def test_plot_planckian_locus(self): class TestPlotPlanckianLocusInChromaticityDiagram(unittest.TestCase): """ - Defines :func:`colour.plotting.temperature.\ + Define :func:`colour.plotting.temperature.\ plot_planckian_locus_in_chromaticity_diagram` definition unit tests methods. """ def test_plot_planckian_locus_in_chromaticity_diagram(self): """ - Tests :func:`colour.plotting.temperature.\ + Test :func:`colour.plotting.temperature.\ plot_planckian_locus_in_chromaticity_diagram` definition. """ figure, axes = plot_planckian_locus_in_chromaticity_diagram( - ['A', 'B', 'C'], - annotate_kwargs={'arrowprops': { - 'width': 10 - }}, + ["A", "B", "C"], + annotate_kwargs={"arrowprops": {"width": 10}}, plot_kwargs={ - 'markersize': 15, - }) + "markersize": 15, + }, + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) figure, axes = plot_planckian_locus_in_chromaticity_diagram( - ['A', 'B', 'C'], - annotate_kwargs=[{ - 'arrowprops': { - 'width': 10 + ["A", "B", "C"], + annotate_kwargs=[{"arrowprops": {"width": 10}}] * 3, + plot_kwargs=[ + { + "markersize": 15, } - }] * 3, - plot_kwargs=[{ - 'markersize': 15, - }] * 3) + ] + * 3, + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) self.assertRaises( - ValueError, lambda: plot_planckian_locus_in_chromaticity_diagram( - ['A', 'B', 'C'], + ValueError, + lambda: plot_planckian_locus_in_chromaticity_diagram( + ["A", "B", "C"], chromaticity_diagram_callable=lambda **x: x, planckian_locus_callable=lambda **x: x, - method='Undefined')) + method="Undefined", + ), + ) class TestPlotPlanckianLocusInChromaticityDiagramCIE1931(unittest.TestCase): """ - Defines :func:`colour.plotting.temperature.\ + Define :func:`colour.plotting.temperature.\ plot_planckian_locus_in_chromaticity_diagram_CIE1931` definition unit tests methods. """ def test_plot_planckian_locus_in_chromaticity_diagram_CIE1931(self): """ - Tests :func:`colour.plotting.temperature.\ + Test :func:`colour.plotting.temperature.\ plot_planckian_locus_in_chromaticity_diagram_CIE1931` definition. """ figure, axes = plot_planckian_locus_in_chromaticity_diagram_CIE1931( - ['A', 'B', 'C']) + ["A", "B", "C"] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -157,23 +137,24 @@ def test_plot_planckian_locus_in_chromaticity_diagram_CIE1931(self): class TestPlotPlanckianLocusInChromaticityDiagramCIE1960UCS(unittest.TestCase): """ - Defines :func:`colour.plotting.temperature.\ + Define :func:`colour.plotting.temperature.\ plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS` definition unit tests methods. """ def test_plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS(self): """ - Tests :func:`colour.plotting.temperature.\ + Test :func:`colour.plotting.temperature.\ plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS` definition. """ figure, axes = plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS( - ['A', 'B', 'C']) + ["A", "B", "C"] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/plotting/tests/test_volume.py b/colour/plotting/tests/test_volume.py index adf56e7887..0b4d2838bd 100644 --- a/colour/plotting/tests/test_volume.py +++ b/colour/plotting/tests/test_volume.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.plotting.volume` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.plotting.volume` module.""" import numpy as np import unittest @@ -12,192 +7,206 @@ from colour.plotting import plot_RGB_colourspaces_gamuts, plot_RGB_scatter from colour.plotting.volume import nadir_grid, RGB_identity_cube -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestNadirGrid', 'TestRGBIdentityCube', 'TestPlotRGBColourspacesGamuts', - 'TestPlotRGBScatter' + "TestNadirGrid", + "TestRGBIdentityCube", + "TestPlotRGBColourspacesGamuts", + "TestPlotRGBScatter", ] class TestNadirGrid(unittest.TestCase): """ - Defines :func:`colour.plotting.volume.nadir_grid` definition unit tests + Define :func:`colour.plotting.volume.nadir_grid` definition unit tests methods. """ def test_nadir_grid(self): - """ - Tests :func:`colour.plotting.volume.nadir_grid` definition. - """ + """Test :func:`colour.plotting.volume.nadir_grid` definition.""" quads, faces_colours, edges_colours = nadir_grid(segments=1) np.testing.assert_almost_equal( quads, - np.array([ - [ - [-1.00000000, -1.00000000, 0.00000000], - [1.00000000, -1.00000000, 0.00000000], - [1.00000000, 1.00000000, 0.00000000], - [-1.00000000, 1.00000000, 0.00000000], - ], - [ - [-1.00000000, -1.00000000, 0.00000000], - [0.00000000, -1.00000000, 0.00000000], - [0.00000000, 0.00000000, 0.00000000], - [-1.00000000, 0.00000000, 0.00000000], - ], - [ - [-1.00000000, 0.00000000, 0.00000000], - [0.00000000, 0.00000000, 0.00000000], - [0.00000000, 1.00000000, 0.00000000], - [-1.00000000, 1.00000000, 0.00000000], - ], + np.array( [ - [0.00000000, -1.00000000, 0.00000000], - [1.00000000, -1.00000000, 0.00000000], - [1.00000000, 0.00000000, 0.00000000], - [0.00000000, 0.00000000, 0.00000000], - ], - [ - [0.00000000, 0.00000000, 0.00000000], - [1.00000000, 0.00000000, 0.00000000], - [1.00000000, 1.00000000, 0.00000000], - [0.00000000, 1.00000000, 0.00000000], - ], - [ - [-1.00000000, -0.00100000, 0.00000000], - [1.00000000, -0.00100000, 0.00000000], - [1.00000000, 0.00100000, 0.00000000], - [-1.00000000, 0.00100000, 0.00000000], - ], - [ - [-0.00100000, -1.00000000, 0.00000000], - [0.00100000, -1.00000000, 0.00000000], - [0.00100000, 1.00000000, 0.00000000], - [-0.00100000, 1.00000000, 0.00000000], - ], - ]), - decimal=7) + [ + [-1.00000000, -1.00000000, 0.00000000], + [1.00000000, -1.00000000, 0.00000000], + [1.00000000, 1.00000000, 0.00000000], + [-1.00000000, 1.00000000, 0.00000000], + ], + [ + [-1.00000000, -1.00000000, 0.00000000], + [0.00000000, -1.00000000, 0.00000000], + [0.00000000, 0.00000000, 0.00000000], + [-1.00000000, 0.00000000, 0.00000000], + ], + [ + [-1.00000000, 0.00000000, 0.00000000], + [0.00000000, 0.00000000, 0.00000000], + [0.00000000, 1.00000000, 0.00000000], + [-1.00000000, 1.00000000, 0.00000000], + ], + [ + [0.00000000, -1.00000000, 0.00000000], + [1.00000000, -1.00000000, 0.00000000], + [1.00000000, 0.00000000, 0.00000000], + [0.00000000, 0.00000000, 0.00000000], + ], + [ + [0.00000000, 0.00000000, 0.00000000], + [1.00000000, 0.00000000, 0.00000000], + [1.00000000, 1.00000000, 0.00000000], + [0.00000000, 1.00000000, 0.00000000], + ], + [ + [-1.00000000, -0.00100000, 0.00000000], + [1.00000000, -0.00100000, 0.00000000], + [1.00000000, 0.00100000, 0.00000000], + [-1.00000000, 0.00100000, 0.00000000], + ], + [ + [-0.00100000, -1.00000000, 0.00000000], + [0.00100000, -1.00000000, 0.00000000], + [0.00100000, 1.00000000, 0.00000000], + [-0.00100000, 1.00000000, 0.00000000], + ], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( faces_colours, - np.array([ - [0.25000000, 0.25000000, 0.25000000, 0.10000000], - [0.00000000, 0.00000000, 0.00000000, 0.00000000], - [0.00000000, 0.00000000, 0.00000000, 0.00000000], - [0.00000000, 0.00000000, 0.00000000, 0.00000000], - [0.00000000, 0.00000000, 0.00000000, 0.00000000], - [0.00000000, 0.00000000, 0.00000000, 1.00000000], - [0.00000000, 0.00000000, 0.00000000, 1.00000000], - ]), - decimal=7) + np.array( + [ + [0.25000000, 0.25000000, 0.25000000, 0.10000000], + [0.00000000, 0.00000000, 0.00000000, 0.00000000], + [0.00000000, 0.00000000, 0.00000000, 0.00000000], + [0.00000000, 0.00000000, 0.00000000, 0.00000000], + [0.00000000, 0.00000000, 0.00000000, 0.00000000], + [0.00000000, 0.00000000, 0.00000000, 1.00000000], + [0.00000000, 0.00000000, 0.00000000, 1.00000000], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( edges_colours, - np.array([ - [0.50000000, 0.50000000, 0.50000000, 0.50000000], - [0.75000000, 0.75000000, 0.75000000, 0.25000000], - [0.75000000, 0.75000000, 0.75000000, 0.25000000], - [0.75000000, 0.75000000, 0.75000000, 0.25000000], - [0.75000000, 0.75000000, 0.75000000, 0.25000000], - [0.00000000, 0.00000000, 0.00000000, 1.00000000], - [0.00000000, 0.00000000, 0.00000000, 1.00000000], - ]), - decimal=7) + np.array( + [ + [0.50000000, 0.50000000, 0.50000000, 0.50000000], + [0.75000000, 0.75000000, 0.75000000, 0.25000000], + [0.75000000, 0.75000000, 0.75000000, 0.25000000], + [0.75000000, 0.75000000, 0.75000000, 0.25000000], + [0.75000000, 0.75000000, 0.75000000, 0.25000000], + [0.00000000, 0.00000000, 0.00000000, 1.00000000], + [0.00000000, 0.00000000, 0.00000000, 1.00000000], + ] + ), + decimal=7, + ) class TestRGBIdentityCube(unittest.TestCase): """ - Defines :func:`colour.plotting.volume.RGB_identity_cube` definition unit + Define :func:`colour.plotting.volume.RGB_identity_cube` definition unit tests methods. """ def test_RGB_identity_cube(self): - """ - Tests :func:`colour.plotting.volume.RGB_identity_cube` definition. - """ + """Test :func:`colour.plotting.volume.RGB_identity_cube` definition.""" vertices, RGB = RGB_identity_cube(1, 1, 1) np.testing.assert_almost_equal( vertices, - np.array([ - [ - [0.00000000, 0.00000000, 0.00000000], - [1.00000000, 0.00000000, 0.00000000], - [1.00000000, 1.00000000, 0.00000000], - [0.00000000, 1.00000000, 0.00000000], - ], - [ - [0.00000000, 0.00000000, 1.00000000], - [1.00000000, 0.00000000, 1.00000000], - [1.00000000, 1.00000000, 1.00000000], - [0.00000000, 1.00000000, 1.00000000], - ], - [ - [0.00000000, 0.00000000, 0.00000000], - [1.00000000, 0.00000000, 0.00000000], - [1.00000000, 0.00000000, 1.00000000], - [0.00000000, 0.00000000, 1.00000000], - ], + np.array( [ - [0.00000000, 1.00000000, 0.00000000], - [1.00000000, 1.00000000, 0.00000000], - [1.00000000, 1.00000000, 1.00000000], - [0.00000000, 1.00000000, 1.00000000], - ], - [ - [0.00000000, 0.00000000, 0.00000000], - [0.00000000, 1.00000000, 0.00000000], - [0.00000000, 1.00000000, 1.00000000], - [0.00000000, 0.00000000, 1.00000000], - ], - [ - [1.00000000, 0.00000000, 0.00000000], - [1.00000000, 1.00000000, 0.00000000], - [1.00000000, 1.00000000, 1.00000000], - [1.00000000, 0.00000000, 1.00000000], - ], - ]), - decimal=7) + [ + [0.00000000, 0.00000000, 0.00000000], + [1.00000000, 0.00000000, 0.00000000], + [1.00000000, 1.00000000, 0.00000000], + [0.00000000, 1.00000000, 0.00000000], + ], + [ + [0.00000000, 0.00000000, 1.00000000], + [1.00000000, 0.00000000, 1.00000000], + [1.00000000, 1.00000000, 1.00000000], + [0.00000000, 1.00000000, 1.00000000], + ], + [ + [0.00000000, 0.00000000, 0.00000000], + [1.00000000, 0.00000000, 0.00000000], + [1.00000000, 0.00000000, 1.00000000], + [0.00000000, 0.00000000, 1.00000000], + ], + [ + [0.00000000, 1.00000000, 0.00000000], + [1.00000000, 1.00000000, 0.00000000], + [1.00000000, 1.00000000, 1.00000000], + [0.00000000, 1.00000000, 1.00000000], + ], + [ + [0.00000000, 0.00000000, 0.00000000], + [0.00000000, 1.00000000, 0.00000000], + [0.00000000, 1.00000000, 1.00000000], + [0.00000000, 0.00000000, 1.00000000], + ], + [ + [1.00000000, 0.00000000, 0.00000000], + [1.00000000, 1.00000000, 0.00000000], + [1.00000000, 1.00000000, 1.00000000], + [1.00000000, 0.00000000, 1.00000000], + ], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( RGB, - np.array([ - [0.50000000, 0.50000000, 0.00000000], - [0.50000000, 0.50000000, 1.00000000], - [0.50000000, 0.00000000, 0.50000000], - [0.50000000, 1.00000000, 0.50000000], - [0.00000000, 0.50000000, 0.50000000], - [1.00000000, 0.50000000, 0.50000000], - ]), - decimal=7) + np.array( + [ + [0.50000000, 0.50000000, 0.00000000], + [0.50000000, 0.50000000, 1.00000000], + [0.50000000, 0.00000000, 0.50000000], + [0.50000000, 1.00000000, 0.50000000], + [0.00000000, 0.50000000, 0.50000000], + [1.00000000, 0.50000000, 0.50000000], + ] + ), + decimal=7, + ) class TestPlotRGBColourspacesGamuts(unittest.TestCase): """ - Defines :func:`colour.plotting.volume.plot_RGB_colourspaces_gamuts` + Define :func:`colour.plotting.volume.plot_RGB_colourspaces_gamuts` definition unit tests methods. """ def test_plot_RGB_colourspaces_gamuts(self): """ - Tests :func:`colour.plotting.volume.plot_RGB_colourspaces_gamuts` + Test :func:`colour.plotting.volume.plot_RGB_colourspaces_gamuts` definition. """ figure, axes = plot_RGB_colourspaces_gamuts( - ['ITU-R BT.709', 'ACEScg', 'S-Gamut'], + ["ITU-R BT.709", "ACEScg", "S-Gamut"], show_spectral_locus=True, face_colours=[0.18, 0.18, 0.18], - chromatically_adapt=True) + chromatically_adapt=True, + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -205,21 +214,20 @@ def test_plot_RGB_colourspaces_gamuts(self): class TestPlotRGBScatter(unittest.TestCase): """ - Defines :func:`colour.plotting.volume.plot_RGB_scatter` definition unit + Define :func:`colour.plotting.volume.plot_RGB_scatter` definition unit tests methods. """ def test_plot_RGB_scatter(self): - """ - Tests :func:`colour.plotting.volume.plot_RGB_scatter` definition. - """ + """Test :func:`colour.plotting.volume.plot_RGB_scatter` definition.""" figure, axes = plot_RGB_scatter( - np.random.random((128, 128, 3)), 'ITU-R BT.709') + np.random.random((128, 128, 3)), "ITU-R BT.709" + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/plotting/tm3018/__init__.py b/colour/plotting/tm3018/__init__.py index f019624992..25062abcec 100644 --- a/colour/plotting/tm3018/__init__.py +++ b/colour/plotting/tm3018/__init__.py @@ -1,15 +1,15 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - -from .report import (plot_single_sd_colour_rendition_report_full, - plot_single_sd_colour_rendition_report_intermediate, - plot_single_sd_colour_rendition_report_simple) +from .report import ( + plot_single_sd_colour_rendition_report_full, + plot_single_sd_colour_rendition_report_intermediate, + plot_single_sd_colour_rendition_report_simple, +) from .report import plot_single_sd_colour_rendition_report __all__ = [ - 'plot_single_sd_colour_rendition_report_full', - 'plot_single_sd_colour_rendition_report_intermediate', - 'plot_single_sd_colour_rendition_report_simple' + "plot_single_sd_colour_rendition_report_full", + "plot_single_sd_colour_rendition_report_intermediate", + "plot_single_sd_colour_rendition_report_simple", +] +__all__ += [ + "plot_single_sd_colour_rendition_report", ] -__all__ += ['plot_single_sd_colour_rendition_report'] diff --git a/colour/plotting/tm3018/components.py b/colour/plotting/tm3018/components.py index c4b7301c89..5b22109f69 100644 --- a/colour/plotting/tm3018/components.py +++ b/colour/plotting/tm3018/components.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ ANSI/IES TM-30-18 Colour Rendition Report Components ==================================================== @@ -15,91 +14,222 @@ - :func:`colour.plotting.tm3018.components.plot_colour_fidelity_indexes` """ +from __future__ import annotations + import os import numpy as np import matplotlib.pyplot as plt from colour.colorimetry import sd_to_XYZ +from colour.hints import ( + Any, + ArrayLike, + Boolean, + Dict, + Floating, + List, + Literal, + Tuple, + Union, + cast, +) from colour.io import read_image -from colour.utilities import as_float_array -from colour.plotting import (CONSTANTS_COLOUR_STYLE, artist, override_style, - plot_image, render) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.plotting import ( + CONSTANTS_COLOUR_STYLE, + artist, + override_style, + plot_image, + render, +) +from colour.quality import ColourQuality_Specification_ANSIIESTM3018 +from colour.utilities import as_float_array, validate_method + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'RESOURCES_DIRECTORY_ANSIIESTM3018', 'plot_spectra_ANSIIESTM3018', - 'plot_colour_vector_graphic', 'plot_16_bin_bars', - 'plot_local_chroma_shifts', 'plot_local_hue_shifts', - 'plot_local_colour_fidelities', 'plot_colour_fidelity_indexes' + "RESOURCES_DIRECTORY_ANSIIESTM3018", + "plot_spectra_ANSIIESTM3018", + "plot_colour_vector_graphic", + "plot_16_bin_bars", + "plot_local_chroma_shifts", + "plot_local_hue_shifts", + "plot_local_colour_fidelities", + "plot_colour_fidelity_indexes", ] -RESOURCES_DIRECTORY_ANSIIESTM3018 = os.path.join( - os.path.dirname(__file__), 'resources') -""" -Resources directory. - -RESOURCES_DIRECTORY_ANSIIESTM3018 : unicode -""" - -_BIN_BAR_COLOURS = [ - '#A35C60', '#CC765E', '#CC8145', '#D8AC62', '#AC9959', '#919E5D', - '#668B5E', '#61B290', '#7BBAA6', '#297A7E', '#55788D', '#708AB2', - '#988CAA', '#735877', '#8F6682', '#BA7A8E' +RESOURCES_DIRECTORY_ANSIIESTM3018: str = os.path.join( + os.path.dirname(__file__), "resources" +) +"""Resources directory.""" + +_COLOURS_BIN_BAR: List = [ + "#A35C60", + "#CC765E", + "#CC8145", + "#D8AC62", + "#AC9959", + "#919E5D", + "#668B5E", + "#61B290", + "#7BBAA6", + "#297A7E", + "#55788D", + "#708AB2", + "#988CAA", + "#735877", + "#8F6682", + "#BA7A8E", ] -_BIN_ARROW_COLOURS = [ - '#E62828', '#E74B4B', '#FB812E', '#FFB529', '#CBCA46', '#7EB94C', - '#41C06D', '#009C7C', '#16BCB0', '#00A4BF', '#0085C3', '#3B62AA', - '#4568AE', '#6A4E85', '#9D69A1', '#A74F81' +_COLOURS_BIN_ARROW: List = [ + "#E62828", + "#E74B4B", + "#FB812E", + "#FFB529", + "#CBCA46", + "#7EB94C", + "#41C06D", + "#009C7C", + "#16BCB0", + "#00A4BF", + "#0085C3", + "#3B62AA", + "#4568AE", + "#6A4E85", + "#9D69A1", + "#A74F81", ] -_TCS_BAR_COLOURS = [ - '#F1BDCD', '#CA6183', '#573A40', '#CD8791', '#AD3F55', '#925F62', - '#933440', '#8C3942', '#413D3E', '#FA8070', '#C35644', '#DA604A', - '#824E39', '#BCA89F', '#C29A89', '#8D593C', '#915E3F', '#99745B', - '#D39257', '#D07F2C', '#FEB45F', '#EFA248', '#F0DFBD', '#FED586', - '#D0981E', '#FED06A', '#B5AC81', '#645D37', '#EAD163', '#9E9464', - '#EBD969', '#C4B135', '#E6DE9C', '#99912C', '#61603A', '#C2C2AF', - '#6D703B', '#D2D7A1', '#4B5040', '#6B7751', '#D3DCC3', '#88B33A', - '#8EBF3E', '#3E3F3D', '#65984A', '#83A96E', '#92AE86', '#91CD8E', - '#477746', '#568C6A', '#659477', '#276E49', '#008D62', '#B6E2D4', - '#A5D9CD', '#39C4AD', '#00A18A', '#009786', '#B4E1D9', '#CDDDDC', - '#99C1C0', '#909FA1', '#494D4E', '#009FA8', '#32636A', '#007788', - '#007F95', '#66A0B2', '#687D88', '#75B6DB', '#1E5574', '#AAB9C3', - '#3091C4', '#3B3E41', '#274D72', '#376FB8', '#496692', '#3B63AC', - '#A0AED5', '#9293C8', '#61589D', '#D4D3E5', '#ACA6CA', '#3E3B45', - '#5F5770', '#A08CC7', '#664782', '#A77AB5', '#6A4172', '#7D4983', - '#C4BFC4', '#937391', '#AE91AA', '#764068', '#BF93B1', '#D7A9C5', - '#9D587F', '#CE6997', '#AE4A79' +_COLOURS_TCS_BAR: List = [ + "#F1BDCD", + "#CA6183", + "#573A40", + "#CD8791", + "#AD3F55", + "#925F62", + "#933440", + "#8C3942", + "#413D3E", + "#FA8070", + "#C35644", + "#DA604A", + "#824E39", + "#BCA89F", + "#C29A89", + "#8D593C", + "#915E3F", + "#99745B", + "#D39257", + "#D07F2C", + "#FEB45F", + "#EFA248", + "#F0DFBD", + "#FED586", + "#D0981E", + "#FED06A", + "#B5AC81", + "#645D37", + "#EAD163", + "#9E9464", + "#EBD969", + "#C4B135", + "#E6DE9C", + "#99912C", + "#61603A", + "#C2C2AF", + "#6D703B", + "#D2D7A1", + "#4B5040", + "#6B7751", + "#D3DCC3", + "#88B33A", + "#8EBF3E", + "#3E3F3D", + "#65984A", + "#83A96E", + "#92AE86", + "#91CD8E", + "#477746", + "#568C6A", + "#659477", + "#276E49", + "#008D62", + "#B6E2D4", + "#A5D9CD", + "#39C4AD", + "#00A18A", + "#009786", + "#B4E1D9", + "#CDDDDC", + "#99C1C0", + "#909FA1", + "#494D4E", + "#009FA8", + "#32636A", + "#007788", + "#007F95", + "#66A0B2", + "#687D88", + "#75B6DB", + "#1E5574", + "#AAB9C3", + "#3091C4", + "#3B3E41", + "#274D72", + "#376FB8", + "#496692", + "#3B63AC", + "#A0AED5", + "#9293C8", + "#61589D", + "#D4D3E5", + "#ACA6CA", + "#3E3B45", + "#5F5770", + "#A08CC7", + "#664782", + "#A77AB5", + "#6A4172", + "#7D4983", + "#C4BFC4", + "#937391", + "#AE91AA", + "#764068", + "#BF93B1", + "#D7A9C5", + "#9D587F", + "#CE6997", + "#AE4A79", ] @override_style() -def plot_spectra_ANSIIESTM3018(specification, **kwargs): +def plot_spectra_ANSIIESTM3018( + specification: ColourQuality_Specification_ANSIIESTM3018, **kwargs: Any +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots a comparison of the spectral distributions of a test emission source + Plot a comparison of the spectral distributions of a test emission source and a reference illuminant for *ANSI/IES TM-30-18 Colour Rendition Report*. Parameters ---------- - specification : ColourQuality_Specification_ANSIIESTM3018 + specification *ANSI/IES TM-30-18 Colour Rendition Report* specification. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes Examples @@ -113,7 +243,7 @@ def plot_spectra_ANSIIESTM3018(specification, **kwargs): (
, <...AxesSubplot...>) """ - settings = kwargs.copy() + settings: Dict[str, Any] = dict(kwargs) _figure, axes = artist(**settings) @@ -123,47 +253,53 @@ def plot_spectra_ANSIIESTM3018(specification, **kwargs): axes.plot( specification.sd_reference.wavelengths, specification.sd_reference.values / Y_reference, - 'black', - label='Reference') + "black", + label="Reference", + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line, + ) axes.plot( specification.sd_test.wavelengths, specification.sd_test.values / Y_test, - '#F05046', - label='Test') - axes.tick_params(axis='y', which='both', length=0) + "#F05046", + label="Test", + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line, + ) + axes.tick_params(axis="y", which="both", length=0) axes.set_yticklabels([]) settings = { - 'axes': axes, - 'legend': True, - 'legend_columns': 2, - 'x_label': 'Wavelength (nm)', - 'y_label': 'Radiant Power\n(Equal Luminous Flux)', + "axes": axes, + "legend": True, + "legend_columns": 2, + "x_label": "Wavelength (nm)", + "y_label": "Radiant Power\n(Equal Luminous Flux)", } settings.update(kwargs) return render(**settings) -def plot_colour_vector_graphic(specification, **kwargs): +def plot_colour_vector_graphic( + specification: ColourQuality_Specification_ANSIIESTM3018, **kwargs: Any +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots *Color Vector Graphic* according to + Plot *Color Vector Graphic* according to *ANSI/IES TM-30-18 Colour Rendition Report*. Parameters ---------- - specification : ColourQuality_Specification_ANSIIESTM3018 + specification *ANSI/IES TM-30-18 Colour Rendition Report* specification. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes Examples @@ -177,65 +313,109 @@ def plot_colour_vector_graphic(specification, **kwargs): (
, <...AxesSubplot...>) """ - settings = kwargs.copy() - settings['standalone'] = False + settings: Dict[str, Any] = dict(kwargs) + settings["standalone"] = False # Background background_image = read_image( - os.path.join(RESOURCES_DIRECTORY_ANSIIESTM3018, 'CVG_Background.jpg')) + os.path.join(RESOURCES_DIRECTORY_ANSIIESTM3018, "CVG_Background.jpg") + ) _figure, axes = plot_image( background_image, - imshow_kwargs={'extent': [-1.5, 1.5, -1.5, 1.5]}, - **settings) + imshow_kwargs={"extent": [-1.5, 1.5, -1.5, 1.5]}, + **settings, + ) # Lines dividing the hues in 16 equal parts along with bin numbers. - axes.plot(0, 0, '+', color='#A6A6A6') + axes.plot(0, 0, "+", color="#A6A6A6") for i in range(16): angle = 2 * np.pi * i / 16 dx = np.cos(angle) dy = np.sin(angle) axes.plot( - (0.15 * dx, 1.5 * dx), (0.15 * dy, 1.5 * dy), - '--', - color='#A6A6A6', - lw=0.75) + (0.15 * dx, 1.5 * dx), + (0.15 * dy, 1.5 * dy), + "--", + color="#A6A6A6", + lw=0.75, + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line, + ) angle = 2 * np.pi * (i + 0.5) / 16 axes.annotate( str(i + 1), - color='#A6A6A6', - ha='center', - va='center', + color="#A6A6A6", + ha="center", + va="center", xy=(1.41 * np.cos(angle), 1.41 * np.sin(angle)), - weight='bold', - size=9) + weight="bold", + size=9, + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_annotation, + ) # Circles. - circle = plt.Circle((0, 0), 1, color='black', lw=1.25, fill=False) + circle = plt.Circle( + (0, 0), + 1, + color="black", + lw=1.25, + fill=False, + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_polygon, + ) axes.add_artist(circle) for radius in [0.8, 0.9, 1.1, 1.2]: - circle = plt.Circle((0, 0), radius, color='white', lw=0.75, fill=False) + circle = plt.Circle( + (0, 0), + radius, + color="white", + lw=0.75, + fill=False, + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_polygon, + ) axes.add_artist(circle) # -/+20% marks near the white circles. - props = dict(ha='right', color='white', size=7) - axes.annotate('-20%', xy=(0, -0.8), va='bottom', **props) - axes.annotate('+20%', xy=(0, -1.2), va='top', **props) + props = dict(ha="right", color="white", size=7) + axes.annotate( + "-20%", + xy=(0, -0.8), + va="bottom", + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_annotation, + **props, + ) + axes.annotate( + "+20%", + xy=(0, -1.2), + va="top", + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_annotation, + **props, + ) # Average "CAM02" h correlate for each bin, in radians. - average_hues = as_float_array([ - np.mean([ - specification.colorimetry_data[1][i].CAM.h - for i in specification.bins[j] - ]) for j in range(16) - ]) / 180 * np.pi + average_hues = np.radians( + [ + np.mean( + as_float_array( + [ + cast( + Floating, + specification.colorimetry_data[1][i].CAM.h, + ) + for i in specification.bins[j] + ] + ) + ) + for j in range(16) + ] + ) xy_reference = np.transpose( - np.vstack([np.cos(average_hues), - np.sin(average_hues)])) + np.vstack([np.cos(average_hues), np.sin(average_hues)]) + ) # Arrow offsets as defined by the standard. - offsets = ((specification.averages_test - specification.averages_reference) - / specification.average_norms[:, np.newaxis]) + offsets = ( + specification.averages_test - specification.averages_reference + ) / specification.average_norms[:, np.newaxis] xy_test = xy_reference + offsets # Arrows. @@ -249,84 +429,98 @@ def plot_colour_vector_graphic(specification, **kwargs): width=0.005, head_width=0.04, linewidth=None, - color=_BIN_ARROW_COLOURS[i]) + color=_COLOURS_BIN_ARROW[i], + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_annotation, + ) # Red (test) gamut shape. loop = np.append(xy_test, xy_test[0, np.newaxis], axis=0) - axes.plot(loop[:, 0], loop[:, 1], '-', color='#F05046', lw=2) + axes.plot( + loop[:, 0], + loop[:, 1], + "-", + color="#F05046", + lw=2, + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line, + ) - def corner_label_and_text(label, text, ha, va): - """ - Draws a label and text in given corner. - """ + def corner_label_and_text(label: str, text: str, ha: str, va: str): + """Draw a label and text in given corner.""" - x = -1.45 if ha == 'left' else 1.45 - y = 1.45 if va == 'top' else -1.45 - y_text = -15 if va == 'top' else 15 + x = -1.45 if ha == "left" else 1.45 + y = 1.45 if va == "top" else -1.45 + y_text = -15 if va == "top" else 15 axes.annotate( text, xy=(x, y), - color='black', + color="black", ha=ha, va=va, - weight='bold', - size='larger') + weight="bold", + size="larger", + zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_label, + ) axes.annotate( label, xy=(x, y), - color='black', + color="black", xytext=(0, y_text), - textcoords='offset points', + textcoords="offset points", ha=ha, va=va, - size='small') - - corner_label_and_text('$R_f$', '{0:.0f}'.format(specification.R_f), 'left', - 'top') - corner_label_and_text('$R_g$', '{0:.0f}'.format(specification.R_g), - 'right', 'top') - corner_label_and_text('CCT', '{0:.0f} K'.format(specification.CCT), 'left', - 'bottom') - corner_label_and_text('$D_{uv}$', '{0:.4f}'.format(specification.D_uv), - 'right', 'bottom') - - settings = {'standalone': True} + size="small", + zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_label, + ) + + corner_label_and_text("$R_f$", f"{specification.R_f:.0f}", "left", "top") + corner_label_and_text("$R_g$", f"{specification.R_g:.0f}", "right", "top") + corner_label_and_text( + "CCT", f"{specification.CCT:.0f} K", "left", "bottom" + ) + corner_label_and_text( + "$D_{uv}$", f"{specification.D_uv:.4f}", "right", "bottom" + ) + + settings = {"standalone": True} settings.update(kwargs) return render(**settings) -def plot_16_bin_bars(values, - label_template, - x_ticker=False, - label_orientation='Vertical', - **kwargs): +def plot_16_bin_bars( + values: ArrayLike, + label_template: str, + x_ticker: Boolean = False, + label_orientation: Union[ + Literal["Horizontal", "Vertical"], str + ] = "Vertical", + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the 16 bin bars for given values according to + Plot the 16 bin bars for given values according to *ANSI/IES TM-30-18 Colour Rendition Report*. Parameters ---------- - values : array_like + values Values to generate the bin bars for. - label_template : unicode + label_template Template to format the labels. - x_ticker : bool, optional + x_ticker Whether to show the *X* axis ticker and the associated label. - label_orientation : unicode, optional - **{'Vertical', 'Horizonal'}**, + label_orientation Orientation of the labels. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes Examples @@ -336,70 +530,92 @@ def plot_16_bin_bars(values, (
, <...AxesSubplot...>) """ + values = as_float_array(values) + + label_orientation = validate_method( + label_orientation, ["Horizontal", "Vertical"] + ) + _figure, axes = artist(**kwargs) - bar_count = len(_BIN_BAR_COLOURS) + bar_count = len(_COLOURS_BIN_BAR) axes.bar( np.arange(bar_count) + 1, values, - color=_BIN_BAR_COLOURS, + color=_COLOURS_BIN_BAR, width=1, - edgecolor='black', - linewidth=CONSTANTS_COLOUR_STYLE.geometry.short / 3) + edgecolor="black", + linewidth=CONSTANTS_COLOUR_STYLE.geometry.short / 3, + zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, + ) axes.set_xlim(0.5, bar_count + 0.5) if x_ticker: axes.set_xticks(np.arange(1, bar_count + 1)) - axes.set_xlabel('Hue-Angle Bin (j)') + axes.set_xlabel("Hue-Angle Bin (j)") else: axes.set_xticks([]) label_orientation = label_orientation.lower() - value_max = max(values) + value_max = np.max(values) for i, value in enumerate(values): - if label_orientation == 'vertical': - va, vo = (('bottom', value_max * 0.15) - if value > 0 else ('top', -value_max * 0.15)) + if label_orientation == "vertical": + va, vo = ( + ("bottom", value_max * 0.15) + if value > 0 + else ("top", -value_max * 0.15) + ) axes.annotate( label_template.format(value), xy=(i + 1, value + vo), rotation=90, - fontsize='xx-small', - ha='center', - va=va) - elif label_orientation == 'horizontal': - va, vo = (('bottom', value_max * 0.1) - if value < 90 else ('top', -value_max * 0.1)) + fontsize="xx-small", + ha="center", + va=va, + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_label, + ) + elif label_orientation == "horizontal": + va, vo = ( + ("bottom", value_max * 0.025) + if value < 90 + else ("top", -value_max * 0.025) + ) axes.annotate( - label_template.format(value + vo), - xy=(i + 1, value), - fontsize='xx-small', - ha='center', - va=va) + label_template.format(value), + xy=(i + 1, value + vo), + fontsize="xx-small", + ha="center", + va=va, + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_label, + ) return render(**kwargs) -def plot_local_chroma_shifts(specification, x_ticker=False, **kwargs): +def plot_local_chroma_shifts( + specification: ColourQuality_Specification_ANSIIESTM3018, + x_ticker: Boolean = False, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the local chroma shifts according to + Plot the local chroma shifts according to *ANSI/IES TM-30-18 Colour Rendition Report*. Parameters ---------- - specification : ColourQuality_Specification_ANSIIESTM3018 + specification *ANSI/IES TM-30-18 Colour Rendition Report* specification. - x_ticker : bool, optional + x_ticker Whether to show the *X* axis ticker and the associated label. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes Examples @@ -413,46 +629,51 @@ def plot_local_chroma_shifts(specification, x_ticker=False, **kwargs): (
, <...AxesSubplot...>) """ - settings = kwargs.copy() - settings['standalone'] = False + settings: Dict[str, Any] = dict(kwargs) + settings["standalone"] = False - _figure, axes = plot_16_bin_bars(specification.R_cs, '{0:.0f}%', x_ticker, - **settings) + _figure, axes = plot_16_bin_bars( + specification.R_cs, "{0:.0f}%", x_ticker, **settings + ) axes.set_ylim(-40, 40) - axes.set_ylabel('Local Chroma Shift ($R_{cs,hj}$)') + axes.set_ylabel("Local Chroma Shift ($R_{cs,hj}$)") ticks = np.arange(-40, 41, 10) axes.set_yticks(ticks) - axes.set_yticklabels(['{0}%'.format(value) for value in ticks]) + axes.set_yticklabels([f"{value}%" for value in ticks]) - settings = {'standalone': True} + settings = {"standalone": True} settings.update(kwargs) return render(**settings) -def plot_local_hue_shifts(specification, x_ticker=False, **kwargs): +def plot_local_hue_shifts( + specification: ColourQuality_Specification_ANSIIESTM3018, + x_ticker: Boolean = False, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the local hue shifts according to + Plot the local hue shifts according to *ANSI/IES TM-30-18 Colour Rendition Report*. Parameters ---------- - specification : ColourQuality_Specification_ANSIIESTM3018 + specification *ANSI/IES TM-30-18 Colour Rendition Report* specification. - x_ticker : bool, optional + x_ticker Whether to show the *X* axis ticker and the associated label. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes Examples @@ -466,42 +687,47 @@ def plot_local_hue_shifts(specification, x_ticker=False, **kwargs): (
, <...AxesSubplot...>) """ - settings = kwargs.copy() - settings['standalone'] = False + settings: Dict[str, Any] = dict(kwargs) + settings["standalone"] = False - _figure, axes = plot_16_bin_bars(specification.R_hs, '{0:.2f}', x_ticker, - **settings) + _figure, axes = plot_16_bin_bars( + specification.R_hs, "{0:.2f}", x_ticker, **settings + ) axes.set_ylim(-0.5, 0.5) axes.set_yticks(np.arange(-0.5, 0.51, 0.1)) - axes.set_ylabel('Local Hue Shift ($R_{hs,hj}$)') + axes.set_ylabel("Local Hue Shift ($R_{hs,hj}$)") - settings = {'standalone': True} + settings = {"standalone": True} settings.update(kwargs) return render(**settings) -def plot_local_colour_fidelities(specification, x_ticker=False, **kwargs): +def plot_local_colour_fidelities( + specification: ColourQuality_Specification_ANSIIESTM3018, + x_ticker: Boolean = False, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the local colour fidelities according to + Plot the local colour fidelities according to *ANSI/IES TM-30-18 Colour Rendition Report*. Parameters ---------- - specification : ColourQuality_Specification_ANSIIESTM3018 + specification *ANSI/IES TM-30-18 Colour Rendition Report* specification. - x_ticker : bool, optional + x_ticker Whether to show the *X* axis ticker and the associated label. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes Examples @@ -515,40 +741,43 @@ def plot_local_colour_fidelities(specification, x_ticker=False, **kwargs): (
, <...AxesSubplot...>) """ - settings = kwargs.copy() - settings['standalone'] = False + settings: Dict[str, Any] = dict(kwargs) + settings["standalone"] = False - _figure, axes = plot_16_bin_bars(specification.R_fs, '{0:.0f}', x_ticker, - 'Horizontal', **settings) + _figure, axes = plot_16_bin_bars( + specification.R_fs, "{0:.0f}", x_ticker, "Horizontal", **settings + ) axes.set_ylim(0, 100) axes.set_yticks(np.arange(0, 101, 10)) - axes.set_ylabel('Local Color Fidelity ($R_{f,hj}$)') + axes.set_ylabel("Local Color Fidelity ($R_{f,hj}$)") - settings = {'standalone': True} + settings = {"standalone": True} settings.update(kwargs) return render(**settings) -def plot_colour_fidelity_indexes(specification, **kwargs): +def plot_colour_fidelity_indexes( + specification: ColourQuality_Specification_ANSIIESTM3018, **kwargs: Any +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots the local chroma shifts according to + Plot the local chroma shifts according to *ANSI/IES TM-30-18 Colour Rendition Report*. Parameters ---------- - specification : ColourQuality_Specification_ANSIIESTM3018 + specification *ANSI/IES TM-30-18 Colour Rendition Report* specification. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes Examples @@ -564,25 +793,26 @@ def plot_colour_fidelity_indexes(specification, **kwargs): _figure, axes = artist(**kwargs) - bar_count = len(_TCS_BAR_COLOURS) + bar_count = len(_COLOURS_TCS_BAR) axes.bar( np.arange(bar_count) + 1, specification.R_s, - color=_TCS_BAR_COLOURS, + color=_COLOURS_TCS_BAR, width=1, - edgecolor='black', - linewidth=CONSTANTS_COLOUR_STYLE.geometry.short / 3) + edgecolor="black", + linewidth=CONSTANTS_COLOUR_STYLE.geometry.short / 3, + zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, + ) axes.set_xlim(0.5, bar_count + 0.5) axes.set_ylim(0, 100) axes.set_yticks(np.arange(0, 110, 10)) - axes.set_ylabel('Color Sample Fidelity ($R_{f,CESi}$)') + axes.set_ylabel("Color Sample Fidelity ($R_{f,CESi}$)") ticks = list(range(1, bar_count + 1, 1)) axes.set_xticks(ticks) labels = [ - 'CES{0:02d}'.format(i) if i % 3 == 1 else '' - for i in range(1, bar_count + 1) + f"CES{i:02d}" if i % 3 == 1 else "" for i in range(1, bar_count + 1) ] axes.set_xticklabels(labels, rotation=90) diff --git a/colour/plotting/tm3018/report.py b/colour/plotting/tm3018/report.py index d3a099d899..5a16ba60af 100644 --- a/colour/plotting/tm3018/report.py +++ b/colour/plotting/tm3018/report.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ ANSI/IES TM-30-18 Colour Rendition Report ========================================= @@ -13,188 +12,154 @@ - :func:`colour.plotting.plot_single_sd_colour_rendition_report` """ -from __future__ import division +from __future__ import annotations import matplotlib.pyplot as plt -import six -from colour.colorimetry import sd_to_XYZ +from colour.colorimetry import SpectralDistribution, sd_to_XYZ +from colour.hints import Any, Dict, Literal, Optional, Tuple, Union, cast from colour.io import SpectralDistribution_IESTM2714 from colour.models import XYZ_to_xy, XYZ_to_Luv, Luv_to_uv from colour.plotting.tm3018.components import ( - plot_spectra_ANSIIESTM3018, plot_colour_vector_graphic, - plot_local_chroma_shifts, plot_local_hue_shifts, - plot_local_colour_fidelities, plot_colour_fidelity_indexes) -from colour.quality import (colour_fidelity_index_ANSIIESTM3018, - colour_rendering_index) + plot_spectra_ANSIIESTM3018, + plot_colour_vector_graphic, + plot_local_chroma_shifts, + plot_local_hue_shifts, + plot_local_colour_fidelities, + plot_colour_fidelity_indexes, +) +from colour.quality import ( + ColourQuality_Specification_ANSIIESTM3018, + ColourRendering_Specification_CRI, + colour_fidelity_index_ANSIIESTM3018, + colour_rendering_index, +) from colour.plotting import CONSTANTS_COLOUR_STYLE, override_style, render -from colour.utilities import describe_environment, runtime_warning +from colour.utilities import describe_environment, optional, validate_method -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'CONSTANT_REPORT_SIZE_FULL', 'CONSTANT_REPORT_ROW_HEIGHT_RATIOS_FULL', - 'CONSTANT_REPORT_PADDING_FULL', 'CONSTANT_REPORT_SIZE_INTERMEDIATE', - 'CONSTANT_REPORT_ROW_HEIGHT_RATIOS_INTERMEDIATE', - 'CONSTANT_REPORT_PADDING_INTERMEDIATE', 'CONSTANT_REPORT_SIZE_SIMPLE', - 'CONSTANT_REPORT_ROW_HEIGHT_RATIOS_SIMPLE', - 'CONSTANT_REPORT_PADDING_SIMPLE', 'CONSTANTS_REPORT_STYLE', - 'REPORT_HEADER_CONTENT', 'REPORT_FOOTER_CONTENT', - 'plot_single_sd_colour_rendition_report_full', - 'plot_single_sd_colour_rendition_report_intermediate', - 'plot_single_sd_colour_rendition_report_simple', - 'plot_single_sd_colour_rendition_report' + "CONSTANT_REPORT_SIZE_FULL", + "CONSTANT_REPORT_ROW_HEIGHT_RATIOS_FULL", + "CONSTANT_REPORT_PADDING_FULL", + "CONSTANT_REPORT_SIZE_INTERMEDIATE", + "CONSTANT_REPORT_ROW_HEIGHT_RATIOS_INTERMEDIATE", + "CONSTANT_REPORT_PADDING_INTERMEDIATE", + "CONSTANT_REPORT_SIZE_SIMPLE", + "CONSTANT_REPORT_ROW_HEIGHT_RATIOS_SIMPLE", + "CONSTANT_REPORT_PADDING_SIMPLE", + "CONSTANTS_REPORT_STYLE", + "CONTENT_REPORT_HEADER", + "CONTENT_REPORT_FOOTER", + "plot_single_sd_colour_rendition_report_full", + "plot_single_sd_colour_rendition_report_intermediate", + "plot_single_sd_colour_rendition_report_simple", + "plot_single_sd_colour_rendition_report", ] # Full Report Size Constants -CONSTANT_REPORT_SIZE_FULL = (8.27, 11.69) -""" -Full report size, default to A4 paper size in inches. - -CONSTANT_REPORT_SIZE_FULL : tuple -""" - -CONSTANT_REPORT_ROW_HEIGHT_RATIOS_FULL = (1, 2, 24, 3, 1) -""" -Full report size row height ratios. +CONSTANT_REPORT_SIZE_FULL: Tuple = (8.27, 11.69) +"""Full report size, default to A4 paper size in inches.""" -CONSTANT_REPORT_ROW_HEIGHT_RATIOS_FULL : tuple -""" +CONSTANT_REPORT_ROW_HEIGHT_RATIOS_FULL: Tuple = (1, 2, 24, 3, 1) +"""Full report size row height ratios.""" -CONSTANT_REPORT_PADDING_FULL = { - 'w_pad': 20 / 100, - 'h_pad': 10 / 100, - 'hspace': 0, - 'wspace': 0, +CONSTANT_REPORT_PADDING_FULL: Dict = { + "w_pad": 20 / 100, + "h_pad": 10 / 100, + "hspace": 0, + "wspace": 0, } """ Full report box padding, tries to define the padding around the figure and in-between the axes. - -CONSTANT_REPORT_PADDING_FULL : dict """ # Intermediate Report Size Constants -CONSTANT_REPORT_SIZE_INTERMEDIATE = (8.27, 11.69 / 2.35) -""" -Intermediate report size, a window into A4 paper size in inches. +CONSTANT_REPORT_SIZE_INTERMEDIATE: Tuple = (8.27, 11.69 / 2.35) +"""Intermediate report size, a window into A4 paper size in inches.""" -CONSTANT_REPORT_SIZE_INTERMEDIATE : tuple -""" - -CONSTANT_REPORT_ROW_HEIGHT_RATIOS_INTERMEDIATE = (1, 8, 1) -""" -Intermediate report size row height ratios. - -CONSTANT_REPORT_ROW_HEIGHT_RATIOS_INTERMEDIATE : tuple -""" +CONSTANT_REPORT_ROW_HEIGHT_RATIOS_INTERMEDIATE: Tuple = (1, 8, 1) +"""Intermediate report size row height ratios.""" -CONSTANT_REPORT_PADDING_INTERMEDIATE = { - 'w_pad': 20 / 100, - 'h_pad': 10 / 100, - 'hspace': 0, - 'wspace': 0, +CONSTANT_REPORT_PADDING_INTERMEDIATE: Dict = { + "w_pad": 20 / 100, + "h_pad": 10 / 100, + "hspace": 0, + "wspace": 0, } """ Intermediate report box padding, tries to define the padding around the figure and in-between the axes. - -CONSTANT_REPORT_PADDING_INTERMEDIATE : dict """ # Simple Report Size Constants -CONSTANT_REPORT_SIZE_SIMPLE = (8.27, 8.27) -""" -Simple report size, a window into A4 paper size in inches. +CONSTANT_REPORT_SIZE_SIMPLE: Tuple = (8.27, 8.27) +"""Simple report size, a window into A4 paper size in inches.""" -CONSTANT_REPORT_SIZE_SIMPLE : tuple -""" +CONSTANT_REPORT_ROW_HEIGHT_RATIOS_SIMPLE: Tuple = (1, 8, 1) +"""Simple report size row height ratios.""" -CONSTANT_REPORT_ROW_HEIGHT_RATIOS_SIMPLE = (1, 8, 1) -""" -Simple report size row height ratios. - -CONSTANT_REPORT_ROW_HEIGHT_RATIOS_SIMPLE : tuple -""" - -CONSTANT_REPORT_PADDING_SIMPLE = { - 'w_pad': 20 / 100, - 'h_pad': 10 / 100, - 'hspace': 0, - 'wspace': 0, +CONSTANT_REPORT_PADDING_SIMPLE: Dict = { + "w_pad": 20 / 100, + "h_pad": 10 / 100, + "hspace": 0, + "wspace": 0, } """ Simple report box padding, tries to define the padding around the figure and in-between the axes. - -CONSTANT_REPORT_PADDING_SIMPLE : dict """ -CONSTANTS_REPORT_STYLE = { - 'axes.grid': False, - 'axes.labelpad': CONSTANTS_COLOUR_STYLE.geometry.short * 3, - 'axes.labelsize': 'x-small', - 'axes.labelweight': 'bold', - 'legend.frameon': False, - 'xtick.labelsize': 'x-small', - 'ytick.labelsize': 'x-small', - 'xtick.direction': 'out', - 'ytick.direction': 'out', - 'xtick.major.size': CONSTANTS_COLOUR_STYLE.geometry.long * 0.5, - 'ytick.major.size': CONSTANTS_COLOUR_STYLE.geometry.long * 0.5, - 'xtick.minor.size': CONSTANTS_COLOUR_STYLE.geometry.long * 0.25, - 'ytick.minor.size': CONSTANTS_COLOUR_STYLE.geometry.long * 0.25, - 'xtick.minor.visible': False, - 'ytick.minor.visible': False, +CONSTANTS_REPORT_STYLE: Dict = { + "axes.grid": False, + "axes.labelpad": CONSTANTS_COLOUR_STYLE.geometry.short * 3, + "axes.labelsize": "x-small", + "axes.labelweight": "bold", + "legend.frameon": False, + "xtick.labelsize": "x-small", + "ytick.labelsize": "x-small", + "xtick.direction": "out", + "ytick.direction": "out", + "xtick.major.size": CONSTANTS_COLOUR_STYLE.geometry.long * 0.5, + "ytick.major.size": CONSTANTS_COLOUR_STYLE.geometry.long * 0.5, + "xtick.minor.size": CONSTANTS_COLOUR_STYLE.geometry.long * 0.25, + "ytick.minor.size": CONSTANTS_COLOUR_STYLE.geometry.long * 0.25, + "xtick.minor.visible": False, + "ytick.minor.visible": False, } -""" -Report style overrides. +"""Report style overrides.""" -CONSTANTS_REPORT_STYLE : dict -""" +CONTENT_REPORT_HEADER: str = "IES TM-30-18 Colour Rendition Report" +"""Report header content, i.e. the report title.""" -REPORT_HEADER_CONTENT = 'IES TM-30-18 Colour Rendition Report' -""" -Report header content, i.e. the report title. - -REPORT_HEADER_CONTENT : unicode -""" - -try: - _COLOUR_VERSION = ' {0}.'.format( - describe_environment( - print_callable=lambda x: x)['colour-science.org']['colour']) -except Exception: # noqa - _COLOUR_VERSION = '.' - -REPORT_FOOTER_CONTENT = ('Colours are for visual orientation purposes only. ' - 'Created with Colour{0}').format(_COLOUR_VERSION) -""" -Report footer content. - -REPORT_FOOTER_CONTENT : unicode -""" +CONTENT_REPORT_FOOTER: str = ( + "Colours are for visual orientation purposes only. " + "Created with Colour{0}" +) +"""Report footer content.""" -_NOT_APPLICABLE_VALUE = 'N/A' +_VALUE_NOT_APPLICABLE: str = "N/A" -def _plot_report_header(axes): +def _plot_report_header(axes: plt.Axes) -> plt.Axes: """ - Plots the report header, i.e. the title, on given axes. + Plot the report header, i.e. the title, on given axes. Parameters ---------- - axes : Axes + axes Axes to add the report header to. Returns ------- - Axes + :class:`matplotlib.axes._axes.Axes` Axes the report header was added to. """ @@ -202,98 +167,111 @@ def _plot_report_header(axes): axes.text( 0.5, 0.5, - REPORT_HEADER_CONTENT, - ha='center', - va='center', - size='x-large', - weight='bold') + CONTENT_REPORT_HEADER, + ha="center", + va="center", + size="x-large", + weight="bold", + zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_label, + ) return axes -def _plot_report_footer(axes): +def _plot_report_footer(axes: plt.Axes) -> plt.Axes: """ - Plots the report footer on given axes. + Plot the report footer on given axes. Parameters ---------- - axes : Axes + axes Axes to add the report footer to. Returns ------- - Axes + :class:`matplotlib.axes._axes.Axes` Axes the report footer was added to. """ + try: + describe = describe_environment(print_callable=lambda x: x)[ + "colour-science.org" + ]["colour"] + version = f" {describe}." + except Exception: # pragma: no cover + version = "." + axes.set_axis_off() axes.text( 0.5, 0.5, - REPORT_FOOTER_CONTENT, - ha='center', - va='center', - size='small') + CONTENT_REPORT_FOOTER.format(version), + ha="center", + va="center", + size="small", + zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_label, + ) @override_style(**CONSTANTS_REPORT_STYLE) def plot_single_sd_colour_rendition_report_full( - sd, - source=None, - date=None, - manufacturer=None, - model=None, - notes=None, - report_size=CONSTANT_REPORT_SIZE_FULL, - report_row_height_ratios=CONSTANT_REPORT_ROW_HEIGHT_RATIOS_FULL, - report_box_padding=None, - **kwargs): + sd: SpectralDistribution, + source: Optional[str] = None, + date: Optional[str] = None, + manufacturer: Optional[str] = None, + model: Optional[str] = None, + notes: Optional[str] = None, + report_size: Tuple = CONSTANT_REPORT_SIZE_FULL, + report_row_height_ratios: Tuple = CONSTANT_REPORT_ROW_HEIGHT_RATIOS_FULL, + report_box_padding: Optional[Dict] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: # noqa: D405,D407,D410,D411 """ - Generates the full *ANSI/IES TM-30-18 Colour Rendition Report* for given + Generate the full *ANSI/IES TM-30-18 Colour Rendition Report* for given spectral distribution. Parameters ---------- - sd : SpectralDistribution or SpectralDistribution_IESTM2714 + sd Spectral distribution of the emission source to generate the report for. - source : unicode, optional + source Emission source name, defaults to `colour.SpectralDistribution_IESTM2714.header.description` or `colour.SpectralDistribution_IESTM2714.name` properties value. - date : unicode, optional + date Emission source measurement date, defaults to `colour.SpectralDistribution_IESTM2714.header.report_date` property value. - manufacturer : unicode, optional + manufacturer Emission source manufacturer, defaults to `colour.SpectralDistribution_IESTM2714.header.manufacturer` property value. - model : unicode, optional + model Emission source model, defaults to `colour.SpectralDistribution_IESTM2714.header.catalog_number` property value. - notes : unicode, optional + notes Notes pertaining to the emission source, defaults to `colour.SpectralDistribution_IESTM2714.header.comments` property value. - report_size : array_like, optional + report_size Report size, default to A4 paper size in inches. - report_row_height_ratios : array_like, optional + report_row_height_ratios Report size row height ratios. - report_box_padding : array_like, optional + report_box_padding Report box padding, tries to define the padding around the figure and in-between the axes. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -310,42 +288,38 @@ def plot_single_sd_colour_rendition_report_full( :alt: plot_single_sd_colour_rendition_report_full """ - if six.PY2: - runtime_warning( - 'The "ANSI/IES TM-30-18 Colour Rendition Report" uses advanced ' - '"Matplotlib" layout capabilities only available for Python 3!') - - return render() + report_box_padding = optional( + report_box_padding, CONSTANT_REPORT_PADDING_FULL + ) - if report_box_padding is None: - report_box_padding = CONSTANT_REPORT_PADDING_FULL + specification: ColourQuality_Specification_ANSIIESTM3018 = cast( + ColourQuality_Specification_ANSIIESTM3018, + colour_fidelity_index_ANSIIESTM3018(sd, True), + ) - specification = colour_fidelity_index_ANSIIESTM3018(sd, True) + sd = ( + SpectralDistribution_IESTM2714(data=sd, name=sd.name) + if not isinstance(sd, SpectralDistribution_IESTM2714) + else sd + ) - sd = (SpectralDistribution_IESTM2714(data=sd, name=sd.name) - if not isinstance(sd, SpectralDistribution_IESTM2714) else sd) + NA = _VALUE_NOT_APPLICABLE - source = sd.header.description if source is None else source - source = sd.name if source is None else source - date = sd.header.report_date if date is None else date - date = _NOT_APPLICABLE_VALUE if date is None else date - manufacturer = (sd.header.manufacturer - if manufacturer is None else manufacturer) - manufacturer = (_NOT_APPLICABLE_VALUE - if manufacturer is None else manufacturer) - model = sd.header.catalog_number if model is None else model - model = _NOT_APPLICABLE_VALUE if model is None else model - notes = sd.header.comments if notes is None else notes - notes = _NOT_APPLICABLE_VALUE if notes is None else notes + source = optional(optional(source, sd.header.description), sd.name) + date = optional(optional(date, sd.header.report_date), NA) + manufacturer = optional(optional(manufacturer, sd.header.manufacturer), NA) + model = optional(optional(model, sd.header.catalog_number), NA) + notes = optional(optional(notes, sd.header.comments), NA) figure = plt.figure(figsize=report_size, constrained_layout=True) - settings = kwargs.copy() - settings['standalone'] = False - settings['tight_layout'] = False + settings: Dict[str, Any] = dict(kwargs) + settings["standalone"] = False + settings["tight_layout"] = False gridspec_report = figure.add_gridspec( - 5, 1, height_ratios=report_row_height_ratios) + 5, 1, height_ratios=report_row_height_ratios + ) # Title Row gridspec_title = gridspec_report[0].subgridspec(1, 1) @@ -360,22 +334,24 @@ def plot_single_sd_colour_rendition_report_full( axes_source_date.text( 0.25, 2 / 3, - 'Source: ', - ha='right', - va='center', - size='medium', - weight='bold') - axes_source_date.text(0.25, 2 / 3, source, va='center', size='medium') + "Source: ", + ha="right", + va="center", + size="medium", + weight="bold", + ) + axes_source_date.text(0.25, 2 / 3, source, va="center", size="medium") axes_source_date.text( 0.25, 1 / 3, - 'Date: ', - ha='right', - va='center', - size='medium', - weight='bold') - axes_source_date.text(0.25, 1 / 3, date, va='center', size='medium') + "Date: ", + ha="right", + va="center", + size="medium", + weight="bold", + ) + axes_source_date.text(0.25, 1 / 3, date, va="center", size="medium") # Manufacturer & Model Column axes_manufacturer_model = figure.add_subplot(gridspec_description[1]) @@ -383,50 +359,59 @@ def plot_single_sd_colour_rendition_report_full( axes_manufacturer_model.text( 0.25, 2 / 3, - 'Manufacturer: ', - ha='right', - va='center', - size='medium', - weight='bold') + "Manufacturer: ", + ha="right", + va="center", + size="medium", + weight="bold", + ) axes_manufacturer_model.text( - 0.25, 2 / 3, manufacturer, va='center', size='medium') + 0.25, 2 / 3, manufacturer, va="center", size="medium" + ) axes_manufacturer_model.text( 0.25, 1 / 3, - 'Model: ', - ha='right', - va='center', - size='medium', - weight='bold') + "Model: ", + ha="right", + va="center", + size="medium", + weight="bold", + ) axes_manufacturer_model.text( - 0.25, 1 / 3, model, va='center', size='medium') + 0.25, 1 / 3, model, va="center", size="medium" + ) # Main Figures Rows & Columns gridspec_figures = gridspec_report[2].subgridspec( - 4, 2, height_ratios=[1, 1, 1, 1.5]) + 4, 2, height_ratios=[1, 1, 1, 1.5] + ) axes_spectra = figure.add_subplot(gridspec_figures[0, 0]) plot_spectra_ANSIIESTM3018(specification, axes=axes_spectra, **settings) axes_vector_graphics = figure.add_subplot(gridspec_figures[1:3, 0]) plot_colour_vector_graphic( - specification, axes=axes_vector_graphics, **settings) + specification, axes=axes_vector_graphics, **settings + ) axes_chroma_shifts = figure.add_subplot(gridspec_figures[0, 1]) plot_local_chroma_shifts( - specification, axes=axes_chroma_shifts, **settings) + specification, axes=axes_chroma_shifts, **settings + ) axes_hue_shifts = figure.add_subplot(gridspec_figures[1, 1]) plot_local_hue_shifts(specification, axes=axes_hue_shifts, **settings) axes_colour_fidelities = figure.add_subplot(gridspec_figures[2, 1]) plot_local_colour_fidelities( - specification, axes=axes_colour_fidelities, x_ticker=True, **settings) + specification, axes=axes_colour_fidelities, x_ticker=True, **settings + ) # Colour Fidelity Indexes Row axes_colour_fidelity_indexes = figure.add_subplot(gridspec_figures[3, :]) plot_colour_fidelity_indexes( - specification, axes=axes_colour_fidelity_indexes, **settings) + specification, axes=axes_colour_fidelity_indexes, **settings + ) # Notes & Chromaticities / CRI Row and Columns gridspec_notes_chromaticities_CRI = gridspec_report[3].subgridspec(1, 2) @@ -435,14 +420,16 @@ def plot_single_sd_colour_rendition_report_full( axes_notes.text( 0.25, 1, - 'Notes: ', - ha='right', - va='center', - size='medium', - weight='bold') - axes_notes.text(0.25, 1, notes, va='center', size='medium') + "Notes: ", + ha="right", + va="center", + size="medium", + weight="bold", + ) + axes_notes.text(0.25, 1, notes, va="center", size="medium") gridspec_chromaticities_CRI = gridspec_notes_chromaticities_CRI[ - 1].subgridspec(1, 2) + 1 + ].subgridspec(1, 2) XYZ = sd_to_XYZ(specification.sd_test) xy = XYZ_to_xy(XYZ) @@ -455,43 +442,49 @@ def plot_single_sd_colour_rendition_report_full( axes_chromaticities.text( 0.5, 4 / 5, - '$x$ {:.4f}'.format(xy[0]), - ha='center', - va='center', - size='medium', - weight='bold') + f"$x$ {xy[0]:.4f}", + ha="center", + va="center", + size="medium", + weight="bold", + ) axes_chromaticities.text( 0.5, 3 / 5, - '$y$ {:.4f}'.format(xy[1]), - ha='center', - va='center', - size='medium', - weight='bold') + f"$y$ {xy[1]:.4f}", + ha="center", + va="center", + size="medium", + weight="bold", + ) axes_chromaticities.text( 0.5, 2 / 5, - '$u\'$ {:.4f}'.format(uv_p[0]), - ha='center', - va='center', - size='medium', - weight='bold') + f"$u'$ {uv_p[0]:.4f}", + ha="center", + va="center", + size="medium", + weight="bold", + ) axes_chromaticities.text( 0.5, 1 / 5, - '$v\'$ {:.4f}'.format(uv_p[1]), - ha='center', - va='center', - size='medium', - weight='bold') + f"$v'$ {uv_p[1]:.4f}", + ha="center", + va="center", + size="medium", + weight="bold", + ) gridspec_CRI = gridspec_chromaticities_CRI[1].subgridspec(1, 1) - CRI_spec = colour_rendering_index( - specification.sd_test, additional_data=True) + CRI_spec: ColourRendering_Specification_CRI = cast( + ColourRendering_Specification_CRI, + colour_rendering_index(specification.sd_test, additional_data=True), + ) axes_CRI = figure.add_subplot(gridspec_CRI[0]) axes_CRI.set_xticks([]) @@ -499,38 +492,42 @@ def plot_single_sd_colour_rendition_report_full( axes_CRI.text( 0.5, 4 / 5, - 'CIE 13.31-1995', - ha='center', - va='center', - size='medium', - weight='bold') + "CIE 13.31-1995", + ha="center", + va="center", + size="medium", + weight="bold", + ) axes_CRI.text( 0.5, 3 / 5, - '(CRI)', - ha='center', - va='center', - size='medium', - weight='bold') + "(CRI)", + ha="center", + va="center", + size="medium", + weight="bold", + ) axes_CRI.text( 0.5, 2 / 5, - '$R_a$ {:.0f}'.format(CRI_spec.Q_a), - ha='center', - va='center', - size='medium', - weight='bold') + f"$R_a$ {float(CRI_spec.Q_a):.0f}", + ha="center", + va="center", + size="medium", + weight="bold", + ) axes_CRI.text( 0.5, 1 / 5, - '$R_9$ {:.0f}'.format(CRI_spec.Q_as[8].Q_a), - ha='center', - va='center', - size='medium', - weight='bold') + f"$R_9$ {float(CRI_spec.Q_as[8].Q_a):.0f}", + ha="center", + va="center", + size="medium", + weight="bold", + ) gridspec_footer = gridspec_report[4].subgridspec(1, 1) axes_footer = figure.add_subplot(gridspec_footer[0]) @@ -538,46 +535,48 @@ def plot_single_sd_colour_rendition_report_full( figure.set_constrained_layout_pads(**report_box_padding) - settings = kwargs.copy() - settings['tight_layout'] = False + settings = dict(kwargs) + settings["tight_layout"] = False return render(**settings) @override_style(**CONSTANTS_REPORT_STYLE) def plot_single_sd_colour_rendition_report_intermediate( - sd, - report_size=CONSTANT_REPORT_SIZE_INTERMEDIATE, - report_row_height_ratios=( - CONSTANT_REPORT_ROW_HEIGHT_RATIOS_INTERMEDIATE), - report_box_padding=None, - **kwargs): + sd: SpectralDistribution, + report_size: Tuple = CONSTANT_REPORT_SIZE_INTERMEDIATE, + report_row_height_ratios: Tuple = ( + CONSTANT_REPORT_ROW_HEIGHT_RATIOS_INTERMEDIATE + ), + report_box_padding: Optional[Dict] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Generates the intermediate *ANSI/IES TM-30-18 Colour Rendition Report* for + Generate the intermediate *ANSI/IES TM-30-18 Colour Rendition Report* for given spectral distribution. Parameters ---------- - sd : SpectralDistribution or SpectralDistribution_IESTM2714 + sd Spectral distribution of the emission source to generate the report for. - report_size : array_like, optional + report_size Report size, default to A4 paper size in inches. - report_row_height_ratios : array_like, optional + report_row_height_ratios Report size row height ratios. - report_box_padding : array_like, optional + report_box_padding Report box padding, tries to define the padding around the figure and in-between the axes. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -594,26 +593,24 @@ def plot_single_sd_colour_rendition_report_intermediate( :alt: plot_single_sd_colour_rendition_report_intermediate """ - if six.PY2: - runtime_warning( - 'The "ANSI/IES TM-30-18 Colour Rendition Report" uses advanced ' - '"Matplotlib" layout capabilities only available for Python 3!') - - return render() - - if report_box_padding is None: - report_box_padding = CONSTANT_REPORT_PADDING_INTERMEDIATE + report_box_padding = optional( + report_box_padding, CONSTANT_REPORT_PADDING_INTERMEDIATE + ) - specification = colour_fidelity_index_ANSIIESTM3018(sd, True) + specification: ColourQuality_Specification_ANSIIESTM3018 = cast( + ColourQuality_Specification_ANSIIESTM3018, + colour_fidelity_index_ANSIIESTM3018(sd, True), + ) figure = plt.figure(figsize=report_size, constrained_layout=True) - settings = kwargs.copy() - settings['standalone'] = False - settings['tight_layout'] = False + settings: Dict[str, Any] = dict(kwargs) + settings["standalone"] = False + settings["tight_layout"] = False gridspec_report = figure.add_gridspec( - 3, 1, height_ratios=report_row_height_ratios) + 3, 1, height_ratios=report_row_height_ratios + ) # Title Row gridspec_title = gridspec_report[0].subgridspec(1, 1) @@ -625,15 +622,18 @@ def plot_single_sd_colour_rendition_report_intermediate( axes_vector_graphics = figure.add_subplot(gridspec_figures[0:2, 0]) plot_colour_vector_graphic( - specification, axes=axes_vector_graphics, **settings) + specification, axes=axes_vector_graphics, **settings + ) axes_chroma_shifts = figure.add_subplot(gridspec_figures[0, 1]) plot_local_chroma_shifts( - specification, axes=axes_chroma_shifts, **settings) + specification, axes=axes_chroma_shifts, **settings + ) axes_hue_shifts = figure.add_subplot(gridspec_figures[1, 1]) plot_local_hue_shifts( - specification, axes=axes_hue_shifts, x_ticker=True, **settings) + specification, axes=axes_hue_shifts, x_ticker=True, **settings + ) gridspec_footer = gridspec_report[2].subgridspec(1, 1) axes_footer = figure.add_subplot(gridspec_footer[0]) @@ -641,44 +641,45 @@ def plot_single_sd_colour_rendition_report_intermediate( figure.set_constrained_layout_pads(**report_box_padding) - settings = kwargs.copy() - settings['tight_layout'] = False + settings = dict(kwargs) + settings["tight_layout"] = False return render(**settings) def plot_single_sd_colour_rendition_report_simple( - sd, - report_size=CONSTANT_REPORT_SIZE_SIMPLE, - report_row_height_ratios=CONSTANT_REPORT_ROW_HEIGHT_RATIOS_SIMPLE, - report_box_padding=None, - **kwargs): + sd: SpectralDistribution, + report_size: Tuple = CONSTANT_REPORT_SIZE_SIMPLE, + report_row_height_ratios: Tuple = CONSTANT_REPORT_ROW_HEIGHT_RATIOS_SIMPLE, + report_box_padding: Optional[Dict] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Generates the simple *ANSI/IES TM-30-18 Colour Rendition Report* for given + Generate the simple *ANSI/IES TM-30-18 Colour Rendition Report* for given spectral distribution. Parameters ---------- - sd : SpectralDistribution or SpectralDistribution_IESTM2714 + sd Spectral distribution of the emission source to generate the report for. - report_size : array_like, optional + report_size Report size, default to A4 paper size in inches. - report_row_height_ratios : array_like, optional + report_row_height_ratios Report size row height ratios. - report_box_padding : array_like, optional + report_box_padding Report box padding, tries to define the padding around the figure and in-between the axes. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -695,26 +696,24 @@ def plot_single_sd_colour_rendition_report_simple( :alt: plot_single_sd_colour_rendition_report_simple """ - if six.PY2: - runtime_warning( - 'The "ANSI/IES TM-30-18 Colour Rendition Report" uses advanced ' - '"Matplotlib" layout capabilities only available for Python 3!') + report_box_padding = optional( + report_box_padding, CONSTANT_REPORT_PADDING_SIMPLE + ) - return render() - - if report_box_padding is None: - report_box_padding = CONSTANT_REPORT_PADDING_SIMPLE - - specification = colour_fidelity_index_ANSIIESTM3018(sd, True) + specification: ColourQuality_Specification_ANSIIESTM3018 = cast( + ColourQuality_Specification_ANSIIESTM3018, + colour_fidelity_index_ANSIIESTM3018(sd, True), + ) figure = plt.figure(figsize=report_size, constrained_layout=True) - settings = kwargs.copy() - settings['standalone'] = False - settings['tight_layout'] = False + settings: Dict[str, Any] = dict(kwargs) + settings["standalone"] = False + settings["tight_layout"] = False gridspec_report = figure.add_gridspec( - 3, 1, height_ratios=report_row_height_ratios) + 3, 1, height_ratios=report_row_height_ratios + ) # Title Row gridspec_title = gridspec_report[0].subgridspec(1, 1) @@ -726,7 +725,8 @@ def plot_single_sd_colour_rendition_report_simple( axes_vector_graphics = figure.add_subplot(gridspec_figures[0, 0]) plot_colour_vector_graphic( - specification, axes=axes_vector_graphics, **settings) + specification, axes=axes_vector_graphics, **settings + ) gridspec_footer = gridspec_report[2].subgridspec(1, 1) axes_footer = figure.add_subplot(gridspec_footer[0]) @@ -734,84 +734,42 @@ def plot_single_sd_colour_rendition_report_simple( figure.set_constrained_layout_pads(**report_box_padding) - settings = kwargs.copy() - settings['tight_layout'] = False + settings = dict(kwargs) + settings["tight_layout"] = False return render(**settings) -def plot_single_sd_colour_rendition_report(sd, method='Full', **kwargs): +def plot_single_sd_colour_rendition_report( + sd: SpectralDistribution, + method: Union[Literal["Full", "Intermediate", "Simple"], str] = "Full", + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Generates the *ANSI/IES TM-30-18 Colour Rendition Report* for given + Generate the *ANSI/IES TM-30-18 Colour Rendition Report* for given spectral distribution according to given method. Parameters ---------- - sd : SpectralDistribution or SpectralDistribution_IESTM2714 + sd Spectral distribution of the emission source to generate the report for. - method : unicode, optional - **{'Full', 'Intermediate', 'Simple'}**, + method Report plotting method. Other Parameters ---------------- - \\**kwargs : dict, optional - {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, - Please refer to the documentation of the previously listed definitions. - source : unicode, optional - {:func:`colour.plotting.tm3018.\ -plot_single_sd_colour_rendition_report_full`}, - Emission source name, defaults to - `colour.SpectralDistribution_IESTM2714.header.description` or - `colour.SpectralDistribution_IESTM2714.name` properties value. - date : unicode, optional - {:func:`colour.plotting.tm3018.\ -plot_single_sd_colour_rendition_report_full`}, - Emission source measurement date, defaults to - `colour.SpectralDistribution_IESTM2714.header.report_date` property - value. - manufacturer : unicode, optional - {:func:`colour.plotting.tm3018.\ -plot_single_sd_colour_rendition_report_full`}, - Emission source manufacturer, defaults to - `colour.SpectralDistribution_IESTM2714.header.manufacturer` property - value. - model : unicode, optional - {:func:`colour.plotting.tm3018.\ -plot_single_sd_colour_rendition_report_full`}, - Emission source model, defaults to - `colour.SpectralDistribution_IESTM2714.header.catalog_number` property - value. - notes : unicode, optional - {:func:`colour.plotting.tm3018.\ -plot_single_sd_colour_rendition_report_full`}, - Notes pertaining to the emission source, defaults to - `colour.SpectralDistribution_IESTM2714.header.comments` property - value. - report_size : array_like, optional - {:func:`colour.plotting.tm3018.\ + kwargs + {:func:`colour.plotting.artist`, :func:`colour.plotting.render`, + :func:`colour.plotting.tm3018.\ plot_single_sd_colour_rendition_report_full`, :func:`colour.plotting.tm3018.\ plot_single_sd_colour_rendition_report_intermediate`, \ -:func:`colour.plotting.tm3018.plot_single_sd_colour_rendition_report_simple}, - Report size, default to A4 paper size in inches. - report_row_height_ratios : array_like, optional - {:func:`colour.plotting.tm3018.\ -plot_single_sd_colour_rendition_report_full`, :func:`colour.plotting.tm3018.\ -plot_single_sd_colour_rendition_report_intermediate`, \ -:func:`colour.plotting.tm3018.plot_single_sd_colour_rendition_report_simple}, - Report size row height ratios. - report_box_padding : array_like, optional - {:func:`colour.plotting.tm3018.\ -plot_single_sd_colour_rendition_report_full`, :func:`colour.plotting.tm3018.\ -plot_single_sd_colour_rendition_report_intermediate`, \ -:func:`colour.plotting.tm3018.plot_single_sd_colour_rendition_report_simple}, - Report box padding, tries to define the padding around the figure and - in-between the axes. +:func:`colour.plotting.tm3018.plot_single_sd_colour_rendition_report_simple`} + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -846,15 +804,13 @@ def plot_single_sd_colour_rendition_report(sd, method='Full', **kwargs): :alt: plot_single_sd_colour_rendition_report_simple """ - method = method.lower() - if method == 'full': + method = validate_method(method, ["Full", "Intermediate", "Simple"]) + + if method == "full": return plot_single_sd_colour_rendition_report_full(sd, **kwargs) - elif method == 'intermediate': + elif method == "intermediate": return plot_single_sd_colour_rendition_report_intermediate( - sd, **kwargs) - elif method == 'simple': + sd, **kwargs + ) + else: # method == 'simple' return plot_single_sd_colour_rendition_report_simple(sd, **kwargs) - else: - raise ValueError( - 'Invalid method: "{0}", must be one of ' - '[\'Full\', \'Intermediate\', \'simple\']'.format(method)) diff --git a/colour/plotting/tm3018/tests/__init__.py b/colour/plotting/tm3018/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/plotting/tm3018/tests/__init__.py +++ b/colour/plotting/tm3018/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/plotting/tm3018/tests/test_components.py b/colour/plotting/tm3018/tests/test_components.py index 899828a4e0..f11dce989d 100644 --- a/colour/plotting/tm3018/tests/test_components.py +++ b/colour/plotting/tm3018/tests/test_components.py @@ -1,46 +1,58 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.plotting.tm3018.components` module. -""" +"""Defines the unit tests for the :mod:`colour.plotting.tm3018.components` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import unittest from matplotlib.pyplot import Axes, Figure from colour.colorimetry import SDS_ILLUMINANTS -from colour.quality import colour_fidelity_index_ANSIIESTM3018 +from colour.hints import cast +from colour.quality import ( + ColourQuality_Specification_ANSIIESTM3018, + colour_fidelity_index_ANSIIESTM3018, +) from colour.plotting.tm3018.components import ( - plot_spectra_ANSIIESTM3018, plot_colour_vector_graphic, plot_16_bin_bars, - plot_local_chroma_shifts, plot_local_hue_shifts, - plot_local_colour_fidelities, plot_colour_fidelity_indexes) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + plot_spectra_ANSIIESTM3018, + plot_colour_vector_graphic, + plot_16_bin_bars, + plot_local_chroma_shifts, + plot_local_hue_shifts, + plot_local_colour_fidelities, + plot_colour_fidelity_indexes, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'TestPlotSpectraANSIIESTM3018', 'TestPlotColourVectorGraphic', - 'TestPlot16BinBars', 'TestPlotLocalChromaShifts', 'TestPlotLocalHueShifts', - 'TestPlotLocalColourFidelities', 'TestPlotColourFidelityIndexes' + "TestPlotSpectraANSIIESTM3018", + "TestPlotColourVectorGraphic", + "TestPlot16BinBars", + "TestPlotLocalChromaShifts", + "TestPlotLocalHueShifts", + "TestPlotLocalColourFidelities", + "TestPlotColourFidelityIndexes", ] -SPECIFICATION_ANSIIESTM3018 = colour_fidelity_index_ANSIIESTM3018( - SDS_ILLUMINANTS['FL2'], True) +SPECIFICATION_ANSIIESTM3018: ColourQuality_Specification_ANSIIESTM3018 = cast( + ColourQuality_Specification_ANSIIESTM3018, + colour_fidelity_index_ANSIIESTM3018(SDS_ILLUMINANTS["FL2"], True), +) class TestPlotSpectraANSIIESTM3018(unittest.TestCase): """ - Defines :func:`colour.plotting.tm3018.components. -plot_spectra_ANSIIESTM3018` definition unit tests methods. + Define :func:`colour.plotting.tm3018.components. + plot_spectra_ANSIIESTM3018` definition unit tests methods. """ def test_plot_spectra_ANSIIESTM3018(self): """ - Tests :func:`colour.plotting.tm3018.components.\ + Test :func:`colour.plotting.tm3018.components.\ plot_spectra_ANSIIESTM3018` definition. """ @@ -52,13 +64,13 @@ def test_plot_spectra_ANSIIESTM3018(self): class TestPlotColourVectorGraphic(unittest.TestCase): """ - Defines :func:`colour.plotting.tm3018.components.\ + Define :func:`colour.plotting.tm3018.components.\ plot_colour_vector_graphic` definition unit tests methods. """ def test_plot_colour_vector_graphic(self): """ - Tests :func:`colour.plotting.tm3018.components.\ + Test :func:`colour.plotting.tm3018.components.\ plot_colour_vector_graphic` definition. """ @@ -70,17 +82,17 @@ def test_plot_colour_vector_graphic(self): class TestPlot16BinBars(unittest.TestCase): """ - Defines :func:`colour.plotting.tm3018.components.plot_16_bin_bars` + Define :func:`colour.plotting.tm3018.components.plot_16_bin_bars` definition unit tests methods. """ def test_plot_16_bin_bars(self): """ - Tests :func:`colour.plotting.tm3018.components.plot_16_bin_bars` + Test :func:`colour.plotting.tm3018.components.plot_16_bin_bars` definition. """ - figure, axes = plot_16_bin_bars(range(16), '{0}') + figure, axes = plot_16_bin_bars(range(16), "{0}") self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -88,13 +100,13 @@ def test_plot_16_bin_bars(self): class TestPlotLocalChromaShifts(unittest.TestCase): """ - Defines :func:`colour.plotting.tm3018.components.plot_local_chroma_shifts` + Define :func:`colour.plotting.tm3018.components.plot_local_chroma_shifts` definition unit tests methods. """ def test_plot_local_chroma_shifts(self): """ - Tests :func:`colour.plotting.tm3018.components.\ + Test :func:`colour.plotting.tm3018.components.\ plot_local_chroma_shifts` definition. """ @@ -106,13 +118,13 @@ def test_plot_local_chroma_shifts(self): class TestPlotLocalHueShifts(unittest.TestCase): """ - Defines :func:`colour.plotting.tm3018.components.plot_local_hue_shifts` + Define :func:`colour.plotting.tm3018.components.plot_local_hue_shifts` definition unit tests methods. """ def test_plot_local_hue_shifts(self): """ - Tests :func:`colour.plotting.tm3018.components.\ + Test :func:`colour.plotting.tm3018.components.\ plot_local_hue_shifts` definition. """ @@ -124,18 +136,19 @@ def test_plot_local_hue_shifts(self): class TestPlotLocalColourFidelities(unittest.TestCase): """ - Defines :func:`colour.plotting.tm3018.components. -plot_local_colour_fidelities` definition unit tests methods. + Define :func:`colour.plotting.tm3018.components. + plot_local_colour_fidelities` definition unit tests methods. """ def test_plot_local_colour_fidelities(self): """ - Tests :func:`colour.plotting.tm3018.components.\ + Test :func:`colour.plotting.tm3018.components.\ plot_local_colour_fidelities` definition. """ figure, axes = plot_local_colour_fidelities( - SPECIFICATION_ANSIIESTM3018) + SPECIFICATION_ANSIIESTM3018 + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -143,22 +156,23 @@ def test_plot_local_colour_fidelities(self): class TestPlotColourFidelityIndexes(unittest.TestCase): """ - Defines :func:`colour.plotting.tm3018.components.\ + Define :func:`colour.plotting.tm3018.components.\ plot_colour_fidelity_indexes` definition unit tests methods. """ def test_plot_colour_fidelity_indexes(self): """ - Tests :func:`colour.plotting.tm3018.components.\ + Test :func:`colour.plotting.tm3018.components.\ plot_colour_fidelity_indexes` definition. """ figure, axes = plot_colour_fidelity_indexes( - SPECIFICATION_ANSIIESTM3018) + SPECIFICATION_ANSIIESTM3018 + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/plotting/tm3018/tests/test_report.py b/colour/plotting/tm3018/tests/test_report.py index b0c83f4ab8..3bbca5e6ab 100644 --- a/colour/plotting/tm3018/tests/test_report.py +++ b/colour/plotting/tm3018/tests/test_report.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.plotting.tm3018.report` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.plotting.tm3018.report` module.""" import unittest from matplotlib.pyplot import Axes, Figure @@ -13,37 +8,39 @@ plot_single_sd_colour_rendition_report_full, plot_single_sd_colour_rendition_report_intermediate, plot_single_sd_colour_rendition_report_simple, - plot_single_sd_colour_rendition_report) + plot_single_sd_colour_rendition_report, +) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestPlotSingleSdColourRenditionReportFull', - 'TestPlotSingleSdColourRenditionReportIntermediate', - 'TestPlotSingleSdColourRenditionReportSimple', - 'TestPlotSingleSdColourRenditionReport' + "TestPlotSingleSdColourRenditionReportFull", + "TestPlotSingleSdColourRenditionReportIntermediate", + "TestPlotSingleSdColourRenditionReportSimple", + "TestPlotSingleSdColourRenditionReport", ] class TestPlotSingleSdColourRenditionReportFull(unittest.TestCase): """ - Defines :func:`colour.plotting.tm3018.report.\ + Define :func:`colour.plotting.tm3018.report.\ plot_single_sd_colour_rendition_report_full` definition unit tests methods. """ def test_plot_single_sd_colour_rendition_report_full(self): """ - Tests :func:`colour.plotting.tm3018.report.\ + Test :func:`colour.plotting.tm3018.report.\ plot_single_sd_colour_rendition_report_full` definition. """ figure, axes = plot_single_sd_colour_rendition_report_full( - SDS_ILLUMINANTS['FL2']) + SDS_ILLUMINANTS["FL2"] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -51,19 +48,20 @@ def test_plot_single_sd_colour_rendition_report_full(self): class TestPlotSingleSdColourRenditionReportIntermediate(unittest.TestCase): """ - Defines :func:`colour.plotting.tm3018.report.\ + Define :func:`colour.plotting.tm3018.report.\ plot_single_sd_colour_rendition_report_intermediate` definition unit tests methods. """ def test_plot_single_sd_colour_rendition_report_intermediate(self): """ - Tests :func:`colour.plotting.tm3018.report.\ + Test :func:`colour.plotting.tm3018.report.\ plot_single_sd_colour_rendition_report_intermediate` definition. """ figure, axes = plot_single_sd_colour_rendition_report_intermediate( - SDS_ILLUMINANTS['FL2']) + SDS_ILLUMINANTS["FL2"] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -71,18 +69,19 @@ def test_plot_single_sd_colour_rendition_report_intermediate(self): class TestPlotSingleSdColourRenditionReportSimple(unittest.TestCase): """ - Defines :func:`colour.plotting.tm3018.report.\ + Define :func:`colour.plotting.tm3018.report.\ plot_single_sd_colour_rendition_report_simple` definition unit tests methods. """ def test_plot_color_vector_graphic(self): """ - Tests :func:`colour.plotting.tm3018.report.\ + Test :func:`colour.plotting.tm3018.report.\ plot_single_sd_colour_rendition_report_simple` definition. """ figure, axes = plot_single_sd_colour_rendition_report_simple( - SDS_ILLUMINANTS['FL2']) + SDS_ILLUMINANTS["FL2"] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) @@ -90,22 +89,23 @@ def test_plot_color_vector_graphic(self): class TestPlotSingleSdColourRenditionReport(unittest.TestCase): """ - Defines :func:`colour.plotting.tm3018.report.\ + Define :func:`colour.plotting.tm3018.report.\ plot_single_sd_colour_rendition_report` definition unit tests methods. """ def test_plot_single_sd_colour_rendition_report(self): """ - Tests :func:`colour.plotting.tm3018.report.\ + Test :func:`colour.plotting.tm3018.report.\ plot_single_sd_colour_rendition_report` definition. """ figure, axes = plot_single_sd_colour_rendition_report( - SDS_ILLUMINANTS['FL2']) + SDS_ILLUMINANTS["FL2"] + ) self.assertIsInstance(figure, Figure) self.assertIsInstance(axes, Axes) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/plotting/volume.py b/colour/plotting/volume.py index 471acc4928..a630302e72 100644 --- a/colour/plotting/volume.py +++ b/colour/plotting/volume.py @@ -1,97 +1,137 @@ -# -*- coding: utf-8 -*- """ Colour Models Volume Plotting ============================= -Defines colour models volume and gamut plotting objects: +Defines the colour models volume and gamut plotting objects: - :func:`colour.plotting.plot_RGB_colourspaces_gamuts` - :func:`colour.plotting.plot_RGB_scatter` """ -from __future__ import division +from __future__ import annotations import matplotlib.pyplot as plt import numpy as np from mpl_toolkits.mplot3d.art3d import Poly3DCollection -from colour.constants import DEFAULT_FLOAT_DTYPE, DEFAULT_INT_DTYPE -from colour.geometry import (primitive_vertices_cube_mpl, - primitive_vertices_grid_mpl) -from colour.models import RGB_to_XYZ -from colour.models.common import (COLOURSPACE_MODELS_AXIS_LABELS, - XYZ_to_colourspace_model) +from colour.colorimetry import MultiSpectralDistributions +from colour.geometry import ( + primitive_vertices_cube_mpl, + primitive_vertices_grid_mpl, +) +from colour.graph import convert +from colour.hints import ( + Any, + ArrayLike, + Boolean, + Dict, + Floating, + Integer, + List, + Literal, + NDArray, + Optional, + Sequence, + Tuple, + Union, + cast, +) +from colour.models import RGB_Colourspace, RGB_to_XYZ +from colour.models.common import COLOURSPACE_MODELS_AXIS_LABELS from colour.plotting import ( - CONSTANTS_COLOUR_STYLE, common_colourspace_model_axis_reorder, - filter_RGB_colourspaces, filter_cmfs, override_style, render) -from colour.utilities import (Structure, as_float_array, as_int_array, - first_item, full, ones, zeros) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + CONSTANTS_COLOUR_STYLE, + colourspace_model_axis_reorder, + filter_RGB_colourspaces, + filter_cmfs, + override_style, + render, +) +from colour.utilities import ( + Structure, + as_float_array, + as_int_scalar, + as_int_array, + first_item, + full, + is_integer, + ones, + optional, + zeros, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'nadir_grid', 'RGB_identity_cube', 'plot_RGB_colourspaces_gamuts', - 'plot_RGB_scatter' + "nadir_grid", + "RGB_identity_cube", + "plot_RGB_colourspaces_gamuts", + "plot_RGB_scatter", ] -def nadir_grid(limits=None, segments=10, labels=None, axes=None, **kwargs): +def nadir_grid( + limits: Optional[ArrayLike] = None, + segments: Integer = 10, + labels: Optional[Sequence[str]] = None, + axes: Optional[plt.Axes] = None, + **kwargs: Any, +) -> Tuple[NDArray, NDArray, NDArray]: """ - Returns a grid on *CIE xy* plane made of quad geometric elements and its + Return a grid on *CIE xy* plane made of quad geometric elements and its associated faces and edges colours. Ticks and labels are added to the given axes according to the extended grid settings. Parameters ---------- - limits : array_like, optional + limits Extended grid limits. - segments : int, optional + segments Edge segments count for the extended grid. - labels : array_like, optional + labels Axis labels. - axes : matplotlib.axes.Axes, optional + axes Axes to add the grid. Other Parameters ---------------- - grid_face_colours : array_like, optional - Grid face colours array such as - `grid_face_colours = (0.25, 0.25, 0.25)`. - grid_edge_colours : array_like, optional + grid_edge_alpha + Grid edge opacity value such as `grid_edge_alpha = 0.5`. + grid_edge_colours Grid edge colours array such as `grid_edge_colours = (0.25, 0.25, 0.25)`. - grid_face_alpha : numeric, optional + grid_face_alpha Grid face opacity value such as `grid_face_alpha = 0.1`. - grid_edge_alpha : numeric, optional - Grid edge opacity value such as `grid_edge_alpha = 0.5`. - x_axis_colour : array_like, optional + grid_face_colours + Grid face colours array such as + `grid_face_colours = (0.25, 0.25, 0.25)`. + ticks_and_label_location + Location of the *X* and *Y* axis ticks and labels such as + `ticks_and_label_location = ('-x', '-y')`. + x_axis_colour *X* axis colour array such as `x_axis_colour = (0.0, 0.0, 0.0, 1.0)`. - y_axis_colour : array_like, optional - *Y* axis colour array such as `y_axis_colour = (0.0, 0.0, 0.0, 1.0)`. - x_ticks_colour : array_like, optional - *X* axis ticks colour array such as - `x_ticks_colour = (0.0, 0.0, 0.0, 0.85)`. - y_ticks_colour : array_like, optional - *Y* axis ticks colour array such as - `y_ticks_colour = (0.0, 0.0, 0.0, 0.85)`. - x_label_colour : array_like, optional + x_label_colour *X* axis label colour array such as `x_label_colour = (0.0, 0.0, 0.0, 0.85)`. - y_label_colour : array_like, optional + x_ticks_colour + *X* axis ticks colour array such as + `x_ticks_colour = (0.0, 0.0, 0.0, 0.85)`. + y_axis_colour + *Y* axis colour array such as `y_axis_colour = (0.0, 0.0, 0.0, 1.0)`. + y_label_colour *Y* axis label colour array such as `y_label_colour = (0.0, 0.0, 0.0, 0.85)`. - ticks_and_label_location : array_like, optional - Location of the *X* and *Y* axis ticks and labels such as - `ticks_and_label_location = ('-x', '-y')`. + y_ticks_colour + *Y* axis ticks colour array such as + `y_ticks_colour = (0.0, 0.0, 0.0, 0.85)`. Returns ------- - tuple + :class:`tuple` Grid quads, faces colours, edges colours. Examples @@ -145,28 +185,28 @@ def nadir_grid(limits=None, segments=10, labels=None, axes=None, **kwargs): [ 0. , 0. , 0. , 1. ]])) """ - if limits is None: - limits = np.array([[-1, 1], [-1, 1]]) - - if labels is None: - labels = ('x', 'y') + limits = as_float_array( + cast(ArrayLike, optional(limits, np.array([[-1, 1], [-1, 1]]))) + ) + labels = cast(Sequence, optional(labels, ("x", "y"))) extent = np.max(np.abs(limits[..., 1] - limits[..., 0])) settings = Structure( **{ - 'grid_face_colours': (0.25, 0.25, 0.25), - 'grid_edge_colours': (0.50, 0.50, 0.50), - 'grid_face_alpha': 0.1, - 'grid_edge_alpha': 0.5, - 'x_axis_colour': (0.0, 0.0, 0.0, 1.0), - 'y_axis_colour': (0.0, 0.0, 0.0, 1.0), - 'x_ticks_colour': (0.0, 0.0, 0.0, 0.85), - 'y_ticks_colour': (0.0, 0.0, 0.0, 0.85), - 'x_label_colour': (0.0, 0.0, 0.0, 0.85), - 'y_label_colour': (0.0, 0.0, 0.0, 0.85), - 'ticks_and_label_location': ('-x', '-y') - }) + "grid_face_colours": (0.25, 0.25, 0.25), + "grid_edge_colours": (0.50, 0.50, 0.50), + "grid_face_alpha": 0.1, + "grid_edge_alpha": 0.5, + "x_axis_colour": (0.0, 0.0, 0.0, 1.0), + "y_axis_colour": (0.0, 0.0, 0.0, 1.0), + "x_ticks_colour": (0.0, 0.0, 0.0, 0.85), + "y_ticks_colour": (0.0, 0.0, 0.0, 0.85), + "x_label_colour": (0.0, 0.0, 0.0, 0.85), + "y_label_colour": (0.0, 0.0, 0.0, 0.85), + "ticks_and_label_location": ("-x", "-y"), + } + ) settings.update(**kwargs) # Outer grid. @@ -175,15 +215,18 @@ def nadir_grid(limits=None, segments=10, labels=None, axes=None, **kwargs): width=extent, height=extent, height_segments=segments, - width_segments=segments) + width_segments=segments, + ) - RGB_g = ones([quads_g.shape[0], quads_g.shape[-1]]) + RGB_g = ones((quads_g.shape[0], quads_g.shape[-1])) RGB_gf = RGB_g * settings.grid_face_colours RGB_gf = np.hstack( - [RGB_gf, full([RGB_gf.shape[0], 1], settings.grid_face_alpha)]) + [RGB_gf, full((RGB_gf.shape[0], 1), settings.grid_face_alpha)] + ) RGB_ge = RGB_g * settings.grid_edge_colours RGB_ge = np.hstack( - [RGB_ge, full([RGB_ge.shape[0], 1], settings.grid_edge_alpha)]) + [RGB_ge, full((RGB_ge.shape[0], 1), settings.grid_edge_alpha)] + ) # Inner grid. quads_gs = primitive_vertices_grid_mpl( @@ -191,82 +234,98 @@ def nadir_grid(limits=None, segments=10, labels=None, axes=None, **kwargs): width=extent, height=extent, height_segments=segments * 2, - width_segments=segments * 2) + width_segments=segments * 2, + ) - RGB_gs = ones([quads_gs.shape[0], quads_gs.shape[-1]]) + RGB_gs = ones((quads_gs.shape[0], quads_gs.shape[-1])) RGB_gsf = RGB_gs * 0 - RGB_gsf = np.hstack([RGB_gsf, full([RGB_gsf.shape[0], 1], 0)]) + RGB_gsf = np.hstack([RGB_gsf, full((RGB_gsf.shape[0], 1), 0)]) RGB_gse = np.clip(RGB_gs * settings.grid_edge_colours * 1.5, 0, 1) - RGB_gse = np.hstack((RGB_gse, - full([RGB_gse.shape[0], 1], - settings.grid_edge_alpha / 2))) + RGB_gse = np.hstack( + (RGB_gse, full((RGB_gse.shape[0], 1), settings.grid_edge_alpha / 2)) + ) # Axis. thickness = extent / 1000 quad_x = primitive_vertices_grid_mpl( - origin=(limits[0, 0], -thickness / 2), width=extent, height=thickness) - RGB_x = ones([quad_x.shape[0], quad_x.shape[-1] + 1]) + origin=(limits[0, 0], -thickness / 2), width=extent, height=thickness + ) + RGB_x = ones((quad_x.shape[0], quad_x.shape[-1] + 1)) RGB_x = RGB_x * settings.x_axis_colour quad_y = primitive_vertices_grid_mpl( - origin=(-thickness / 2, limits[1, 0]), width=thickness, height=extent) - RGB_y = ones([quad_y.shape[0], quad_y.shape[-1] + 1]) + origin=(-thickness / 2, limits[1, 0]), width=thickness, height=extent + ) + RGB_y = ones((quad_y.shape[0], quad_y.shape[-1] + 1)) RGB_y = RGB_y * settings.y_axis_colour if axes is not None: # Ticks. - x_s = 1 if '+x' in settings.ticks_and_label_location else -1 - y_s = 1 if '+y' in settings.ticks_and_label_location else -1 - for i, axis in enumerate('xy'): - h_a = 'center' if axis == 'x' else 'left' if x_s == 1 else 'right' - v_a = 'center' + x_s = 1 if "+x" in settings.ticks_and_label_location else -1 + y_s = 1 if "+y" in settings.ticks_and_label_location else -1 + for i, axis in enumerate("xy"): + h_a = "center" if axis == "x" else "left" if x_s == 1 else "right" + v_a = "center" ticks = list(sorted(set(quads_g[..., 0, i]))) ticks += [ticks[-1] + ticks[-1] - ticks[-2]] for tick in ticks: - x = (limits[1, 1 if x_s == 1 else 0] + (x_s * extent / 25) - if i else tick) - y = (tick if i else - limits[0, 1 if y_s == 1 else 0] + (y_s * extent / 25)) - - tick = DEFAULT_INT_DTYPE(tick) if DEFAULT_FLOAT_DTYPE( - tick).is_integer() else tick - c = settings['{0}_ticks_colour'.format(axis)] + x = ( + limits[1, 1 if x_s == 1 else 0] + (x_s * extent / 25) + if i + else tick + ) + y = ( + tick + if i + else limits[0, 1 if y_s == 1 else 0] + (y_s * extent / 25) + ) + + tick = as_int_scalar(tick) if is_integer(tick) else tick + c = settings[f"{axis}_ticks_colour"] axes.text( x, y, 0, tick, - 'x', + "x", horizontalalignment=h_a, verticalalignment=v_a, color=c, - clip_on=True) + clip_on=True, + ) # Labels. - for i, axis in enumerate('xy'): - h_a = 'center' if axis == 'x' else 'left' if x_s == 1 else 'right' - v_a = 'center' - - x = (limits[1, 1 if x_s == 1 else 0] + (x_s * extent / 10) - if i else 0) - y = (0 if i else - limits[0, 1 if y_s == 1 else 0] + (y_s * extent / 10)) - - c = settings['{0}_label_colour'.format(axis)] + for i, axis in enumerate("xy"): + h_a = "center" if axis == "x" else "left" if x_s == 1 else "right" + v_a = "center" + + x = ( + limits[1, 1 if x_s == 1 else 0] + (x_s * extent / 10) + if i + else 0 + ) + y = ( + 0 + if i + else limits[0, 1 if y_s == 1 else 0] + (y_s * extent / 10) + ) + + c = settings[f"{axis}_label_colour"] axes.text( x, y, 0, labels[i], - 'x', + "x", horizontalalignment=h_a, verticalalignment=v_a, color=c, size=20, - clip_on=True) + clip_on=True, + ) quads = np.vstack([quads_g, quads_gs, quad_x, quad_y]) RGB_f = np.vstack([RGB_gf, RGB_gsf, RGB_x, RGB_y]) @@ -275,30 +334,45 @@ def nadir_grid(limits=None, segments=10, labels=None, axes=None, **kwargs): return quads, RGB_f, RGB_e -def RGB_identity_cube(width_segments=16, - height_segments=16, - depth_segments=16, - planes=None): +def RGB_identity_cube( + width_segments: Integer = 16, + height_segments: Integer = 16, + depth_segments: Integer = 16, + planes: Optional[ + Literal[ + "-x", + "+x", + "-y", + "+y", + "-z", + "+z", + "xy", + "xz", + "yz", + "yx", + "zx", + "zy", + ] + ] = None, +) -> Tuple[NDArray, NDArray]: """ - Returns an *RGB* identity cube made of quad geometric elements and its + Return an *RGB* identity cube made of quad geometric elements and its associated *RGB* colours. Parameters ---------- - width_segments: int, optional + width_segments Cube segments, quad counts along the width. - height_segments: int, optional + height_segments Cube segments, quad counts along the height. - depth_segments: int, optional + depth_segments Cube segments, quad counts along the depth. - planes : array_like, optional - **{'-x', '+x', '-y', '+y', '-z', '+z', - 'xy', 'xz', 'yz', 'yx', 'zx', 'zy'}**, + planes Grid primitives to include in the cube construction. Returns ------- - tuple + :class:`tuple` Cube quads, *RGB* colours. Examples @@ -350,74 +424,112 @@ def RGB_identity_cube(width_segments=16, width_segments=width_segments, height_segments=height_segments, depth_segments=depth_segments, - planes=planes) + planes=planes, + ) RGB = np.average(quads, axis=-2) return quads, RGB @override_style() -def plot_RGB_colourspaces_gamuts(colourspaces, - reference_colourspace='CIE xyY', - segments=8, - show_grid=True, - grid_segments=10, - show_spectral_locus=False, - spectral_locus_colour=None, - cmfs='CIE 1931 2 Degree Standard Observer', - chromatically_adapt=False, - **kwargs): +def plot_RGB_colourspaces_gamuts( + colourspaces: Union[ + RGB_Colourspace, str, Sequence[Union[RGB_Colourspace, str]] + ], + reference_colourspace: Union[ + Literal[ + "CAM02LCD", + "CAM02SCD", + "CAM02UCS", + "CAM16LCD", + "CAM16SCD", + "CAM16UCS", + "CIE XYZ", + "CIE xyY", + "CIE Lab", + "CIE Luv", + "CIE UCS", + "CIE UVW", + "DIN99", + "Hunter Lab", + "Hunter Rdab", + "ICaCb", + "ICtCp", + "IPT", + "IgPgTg", + "Jzazbz", + "OSA UCS", + "Oklab", + "hdr-CIELAB", + "hdr-IPT", + ], + str, + ] = "CIE xyY", + segments: Integer = 8, + show_grid: Boolean = True, + grid_segments: Integer = 10, + show_spectral_locus: Boolean = False, + spectral_locus_colour: Optional[Union[ArrayLike, str]] = None, + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + chromatically_adapt: Boolean = False, + convert_kwargs: Optional[Dict] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given *RGB* colourspaces gamuts in given reference colourspace. + Plot given *RGB* colourspaces gamuts in given reference colourspace. Parameters ---------- - colourspaces : unicode or RGB_Colourspace or array_like + colourspaces *RGB* colourspaces to plot the gamuts. ``colourspaces`` elements can be of any type or form supported by the :func:`colour.plotting.filter_RGB_colourspaces` definition. - reference_colourspace : unicode, optional - **{'CIE XYZ', 'CIE xyY', 'CIE xy', 'CIE Lab', 'CIE LCHab', 'CIE Luv', - 'CIE Luv uv', 'CIE LCHuv', 'CIE UCS', 'CIE UCS uv', 'CIE UVW', - 'DIN 99', 'Hunter Lab', 'Hunter Rdab', 'IPT', 'JzAzBz', 'OSA UCS', - 'hdr-CIELAB', 'hdr-IPT'}**, - Reference colourspace to plot the gamuts into. - segments : int, optional + reference_colourspace + Reference colourspace model to plot the gamuts into, see + :attr:`colour.COLOURSPACE_MODELS` attribute for the list of supported + colourspace models. + segments Edge segments count for each *RGB* colourspace cubes. - show_grid : bool, optional + show_grid Whether to show a grid at the bottom of the *RGB* colourspace cubes. - grid_segments : bool, optional + grid_segments Edge segments count for the grid. - show_spectral_locus : bool, optional + show_spectral_locus Whether to show the spectral locus. - spectral_locus_colour : array_like, optional + spectral_locus_colour Spectral locus colour. - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - chromatically_adapt : bool, optional + chromatically_adapt Whether to chromatically adapt the *RGB* colourspaces given in ``colourspaces`` to the whitepoint of the default plotting colourspace. + convert_kwargs + Keyword arguments for the :func:`colour.convert` definition. Other Parameters ---------------- - \\**kwargs : dict, optional - {:func:`colour.plotting.artist`, - :func:`colour.plotting.volume.nadir_grid`}, - Please refer to the documentation of the previously listed definitions. - face_colours : array_like, optional - Face colours array such as `face_colours = (None, (0.5, 0.5, 1.0))`. - edge_colours : array_like, optional + edge_colours Edge colours array such as `edge_colours = (None, (0.5, 0.5, 1.0))`. - face_alpha : numeric, optional - Face opacity value such as `face_alpha = (0.5, 1.0)`. - edge_alpha : numeric, optional + edge_alpha Edge opacity value such as `edge_alpha = (0.0, 1.0)`. + face_alpha + Face opacity value such as `face_alpha = (0.5, 1.0)`. + face_colours + Face colours array such as `face_colours = (None, (0.5, 0.5, 1.0))`. + kwargs + {:func:`colour.plotting.artist`, + :func:`colour.plotting.volume.nadir_grid`}, + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -431,118 +543,154 @@ def plot_RGB_colourspaces_gamuts(colourspaces, :alt: plot_RGB_colourspaces_gamuts """ - colourspaces = filter_RGB_colourspaces(colourspaces).values() + colourspaces = cast( + List[RGB_Colourspace], + list(filter_RGB_colourspaces(colourspaces).values()), + ) + + convert_kwargs = optional(convert_kwargs, {}) count_c = len(colourspaces) - title = '{0} - {1} Reference Colourspace'.format( - ', '.join([colourspace.name for colourspace in colourspaces]), - reference_colourspace, + title = ( + f"{', '.join([colourspace.name for colourspace in colourspaces])} " + f"- {reference_colourspace} Reference Colourspace" ) + illuminant = CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint + + convert_settings = {"illuminant": illuminant} + convert_settings.update(convert_kwargs) + settings = Structure( **{ - 'face_colours': [None] * count_c, - 'edge_colours': [None] * count_c, - 'face_alpha': [1] * count_c, - 'edge_alpha': [1] * count_c, - 'title': title, - }) + "face_colours": [None] * count_c, + "edge_colours": [None] * count_c, + "face_alpha": [1] * count_c, + "edge_alpha": [1] * count_c, + "title": title, + } + ) settings.update(kwargs) figure = plt.figure() - axes = figure.add_subplot(111, projection='3d') + axes = figure.add_subplot(111, projection="3d") - illuminant = CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint - - points = zeros([4, 3]) + points = zeros((4, 3)) if show_spectral_locus: - cmfs = first_item(filter_cmfs(cmfs).values()) + cmfs = cast( + MultiSpectralDistributions, first_item(filter_cmfs(cmfs).values()) + ) XYZ = cmfs.values - points = common_colourspace_model_axis_reorder( - XYZ_to_colourspace_model(XYZ, illuminant, reference_colourspace), - reference_colourspace) + points = colourspace_model_axis_reorder( + convert(XYZ, "CIE XYZ", reference_colourspace, **convert_settings), + reference_colourspace, + ) points[np.isnan(points)] = 0 - c = ((0.0, 0.0, 0.0, 0.5) - if spectral_locus_colour is None else spectral_locus_colour) + c = ( + (0.0, 0.0, 0.0, 0.5) + if spectral_locus_colour is None + else spectral_locus_colour + ) axes.plot( - points[..., 0], points[..., 1], points[..., 2], color=c, zorder=10) + points[..., 0], + points[..., 1], + points[..., 2], + color=c, + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line, + ) axes.plot( - (points[-1][0], points[0][0]), (points[-1][1], points[0][1]), + (points[-1][0], points[0][0]), + (points[-1][1], points[0][1]), (points[-1][2], points[0][2]), color=c, - zorder=10) + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line, + ) plotting_colourspace = CONSTANTS_COLOUR_STYLE.colour.colourspace - quads, RGB_f, RGB_e = [], [], [] + quads_c: List = [] + RGB_cf: List = [] + RGB_ce: List = [] for i, colourspace in enumerate(colourspaces): if chromatically_adapt and not np.array_equal( - colourspace.whitepoint, plotting_colourspace.whitepoint): + colourspace.whitepoint, plotting_colourspace.whitepoint + ): colourspace = colourspace.chromatically_adapt( plotting_colourspace.whitepoint, - plotting_colourspace.whitepoint_name) + plotting_colourspace.whitepoint_name, + ) - quads_c, RGB = RGB_identity_cube( + quads_cb, RGB = RGB_identity_cube( width_segments=segments, height_segments=segments, - depth_segments=segments) + depth_segments=segments, + ) XYZ = RGB_to_XYZ( - quads_c, + quads_cb, colourspace.whitepoint, colourspace.whitepoint, colourspace.matrix_RGB_to_XYZ, ) - quads.extend( - common_colourspace_model_axis_reorder( - XYZ_to_colourspace_model( - XYZ, - colourspace.whitepoint, - reference_colourspace, - ), reference_colourspace)) + convert_settings = {"illuminant": colourspace.whitepoint} + convert_settings.update(convert_kwargs) + + quads_c.extend( + colourspace_model_axis_reorder( + convert( + XYZ, "CIE XYZ", reference_colourspace, **convert_settings + ), + reference_colourspace, + ) + ) if settings.face_colours[i] is not None: RGB = ones(RGB.shape) * settings.face_colours[i] - RGB_f.extend( - np.hstack([RGB, - full([RGB.shape[0], 1], settings.face_alpha[i])])) + RGB_cf.extend( + np.hstack([RGB, full((RGB.shape[0], 1), settings.face_alpha[i])]) + ) if settings.edge_colours[i] is not None: RGB = ones(RGB.shape) * settings.edge_colours[i] - RGB_e.extend( - np.hstack([RGB, - full([RGB.shape[0], 1], settings.edge_alpha[i])])) + RGB_ce.extend( + np.hstack([RGB, full((RGB.shape[0], 1), settings.edge_alpha[i])]) + ) + + quads = as_float_array(quads_c) + RGB_f = as_float_array(RGB_cf) + RGB_e = as_float_array(RGB_ce) - quads = as_float_array(quads) quads[np.isnan(quads)] = 0 if quads.size != 0: - for i, axis in enumerate('xyz'): - min_a = min(np.min(quads[..., i]), np.min(points[..., i])) - max_a = max(np.max(quads[..., i]), np.max(points[..., i])) - getattr(axes, 'set_{}lim'.format(axis))((min_a, max_a)) - - labels = np.array( - COLOURSPACE_MODELS_AXIS_LABELS[reference_colourspace])[as_int_array( - common_colourspace_model_axis_reorder([0, 1, 2], - reference_colourspace))] - for i, axis in enumerate('xyz'): - getattr(axes, 'set_{}label'.format(axis))(labels[i]) + for i, axis in enumerate("xyz"): + min_a = np.minimum(np.min(quads[..., i]), np.min(points[..., i])) + max_a = np.maximum(np.max(quads[..., i]), np.max(points[..., i])) + getattr(axes, f"set_{axis}lim")((min_a, max_a)) + + labels = np.array(COLOURSPACE_MODELS_AXIS_LABELS[reference_colourspace])[ + as_int_array( + colourspace_model_axis_reorder([0, 1, 2], reference_colourspace) + ) + ] + for i, axis in enumerate("xyz"): + getattr(axes, f"set_{axis}label")(labels[i]) if show_grid: limits = np.array([[-1.5, 1.5], [-1.5, 1.5]]) - quads_g, RGB_gf, RGB_ge = nadir_grid(limits, grid_segments, labels, - axes, **settings) + quads_g, RGB_gf, RGB_ge = nadir_grid( + limits, grid_segments, labels, axes, **settings + ) quads = np.vstack([quads_g, quads]) RGB_f = np.vstack([RGB_gf, RGB_f]) RGB_e = np.vstack([RGB_ge, RGB_e]) @@ -553,81 +701,118 @@ def plot_RGB_colourspaces_gamuts(colourspaces, axes.add_collection3d(collection) - settings.update({ - 'axes': axes, - 'axes_visible': False, - 'camera_aspect': 'equal' - }) + settings.update( + {"axes": axes, "axes_visible": False, "camera_aspect": "equal"} + ) settings.update(kwargs) return render(**settings) @override_style() -def plot_RGB_scatter(RGB, - colourspace, - reference_colourspace='CIE xyY', - colourspaces=None, - segments=8, - show_grid=True, - grid_segments=10, - show_spectral_locus=False, - spectral_locus_colour=None, - points_size=12, - cmfs='CIE 1931 2 Degree Standard Observer', - chromatically_adapt=False, - **kwargs): +def plot_RGB_scatter( + RGB: ArrayLike, + colourspace: Union[ + RGB_Colourspace, str, Sequence[Union[RGB_Colourspace, str]] + ], + reference_colourspace: Union[ + Literal[ + "CAM02LCD", + "CAM02SCD", + "CAM02UCS", + "CAM16LCD", + "CAM16SCD", + "CAM16UCS", + "CIE XYZ", + "CIE xyY", + "CIE Lab", + "CIE Luv", + "CIE UCS", + "CIE UVW", + "DIN99", + "Hunter Lab", + "Hunter Rdab", + "ICaCb", + "ICtCp", + "IPT", + "IgPgTg", + "Jzazbz", + "OSA UCS", + "Oklab", + "hdr-CIELAB", + "hdr-IPT", + ], + str, + ] = "CIE xyY", + colourspaces: Optional[ + Union[RGB_Colourspace, str, Sequence[Union[RGB_Colourspace, str]]] + ] = None, + segments: Integer = 8, + show_grid: Boolean = True, + grid_segments: Integer = 10, + show_spectral_locus: Boolean = False, + spectral_locus_colour: Optional[Union[ArrayLike, str]] = None, + points_size: Floating = 12, + cmfs: Union[ + MultiSpectralDistributions, + str, + Sequence[Union[MultiSpectralDistributions, str]], + ] = "CIE 1931 2 Degree Standard Observer", + chromatically_adapt: Boolean = False, + convert_kwargs: Optional[Dict] = None, + **kwargs: Any, +) -> Tuple[plt.Figure, plt.Axes]: """ - Plots given *RGB* colourspace array in a scatter plot. + Plot given *RGB* colourspace array in a scatter plot. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array. - colourspace : unicode or RGB_Colourspace + colourspace *RGB* colourspace of the *RGB* array. ``colourspace`` can be of any type or form supported by the :func:`colour.plotting.filter_RGB_colourspaces` definition. - reference_colourspace : unicode, optional - **{'CIE XYZ', 'CIE xyY', 'CIE xy', 'CIE Lab', 'CIE LCHab', 'CIE Luv', - 'CIE Luv uv', 'CIE LCHuv', 'CIE UCS', 'CIE UCS uv', 'CIE UVW', - 'DIN 99', 'Hunter Lab', 'Hunter Rdab', 'IPT', 'JzAzBz', 'OSA UCS', - 'hdr-CIELAB', 'hdr-IPT'}**, - Reference colourspace for colour conversion. - colourspaces : unicode or RGB_Colourspace or array_like + reference_colourspace + Reference colourspace model to plot the gamuts into, see + :attr:`colour.COLOURSPACE_MODELS` attribute for the list of supported + colourspace models. + colourspaces *RGB* colourspaces to plot the gamuts. ``colourspaces`` elements can be of any type or form supported by the :func:`colour.plotting.filter_RGB_colourspaces` definition. - segments : int, optional + segments Edge segments count for each *RGB* colourspace cubes. - show_grid : bool, optional + show_grid Whether to show a grid at the bottom of the *RGB* colourspace cubes. - grid_segments : bool, optional + grid_segments Edge segments count for the grid. - show_spectral_locus : bool, optional + show_spectral_locus Whether to show the spectral locus. - spectral_locus_colour : array_like, optional + spectral_locus_colour Spectral locus colour. - points_size : numeric, optional + points_size Scatter points size. - cmfs : unicode or XYZ_ColourMatchingFunctions, optional + cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. - chromatically_adapt : bool, optional + chromatically_adapt Whether to chromatically adapt the *RGB* colourspaces given in ``colourspaces`` to the whitepoint of the default plotting colourspace. + convert_kwargs + Keyword arguments for the :func:`colour.convert` definition. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_RGB_colourspaces_gamuts`}, - Please refer to the documentation of the previously listed definitions. + See the documentation of the previously listed definitions. Returns ------- - tuple + :class:`tuple` Current figure and axes. Examples @@ -641,21 +826,25 @@ def plot_RGB_scatter(RGB, :alt: plot_RGB_scatter """ - colourspace = first_item(filter_RGB_colourspaces(colourspace).values()) + colourspace = cast( + RGB_Colourspace, + first_item(filter_RGB_colourspaces(colourspace).values()), + ) + colourspaces = cast(List[str], optional(colourspaces, [colourspace.name])) - if colourspaces is None: - colourspaces = (colourspace.name, ) + convert_kwargs = optional(convert_kwargs, {}) count_c = len(colourspaces) settings = Structure( **{ - 'face_colours': [None] * count_c, - 'edge_colours': [(0.25, 0.25, 0.25)] * count_c, - 'face_alpha': [0.0] * count_c, - 'edge_alpha': [0.1] * count_c, - }) + "face_colours": [None] * count_c, + "edge_colours": [(0.25, 0.25, 0.25)] * count_c, + "face_alpha": [0.0] * count_c, + "edge_alpha": [0.1] * count_c, + } + ) settings.update(kwargs) - settings['standalone'] = False + settings["standalone"] = False plot_RGB_colourspaces_gamuts( colourspaces=colourspaces, @@ -667,14 +856,23 @@ def plot_RGB_scatter(RGB, spectral_locus_colour=spectral_locus_colour, cmfs=cmfs, chromatically_adapt=chromatically_adapt, - **settings) + **settings, + ) - XYZ = RGB_to_XYZ(RGB, colourspace.whitepoint, colourspace.whitepoint, - colourspace.matrix_RGB_to_XYZ) + XYZ = RGB_to_XYZ( + RGB, + colourspace.whitepoint, + colourspace.whitepoint, + colourspace.matrix_RGB_to_XYZ, + ) - points = common_colourspace_model_axis_reorder( - XYZ_to_colourspace_model(XYZ, colourspace.whitepoint, - reference_colourspace), reference_colourspace) + convert_settings = {"illuminant": colourspace.whitepoint} + convert_settings.update(convert_kwargs) + + points = colourspace_model_axis_reorder( + convert(XYZ, "CIE XYZ", reference_colourspace, **convert_settings), + reference_colourspace, + ) axes = plt.gca() axes.scatter( @@ -682,9 +880,11 @@ def plot_RGB_scatter(RGB, points[..., 1], points[..., 2], color=np.reshape(RGB, (-1, 3)), - s=points_size) + s=points_size, + zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_scatter, + ) - settings.update({'axes': axes, 'standalone': True}) + settings.update({"axes": axes, "standalone": True}) settings.update(kwargs) return render(**settings) diff --git a/colour/py.typed b/colour/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/colour/quality/__init__.py b/colour/quality/__init__.py index 85f0d31a1b..9df11aa224 100644 --- a/colour/quality/__init__.py +++ b/colour/quality/__init__.py @@ -1,75 +1,92 @@ -# -*- coding: utf-8 -*- +from __future__ import annotations -from __future__ import absolute_import - -import sys - -from colour.utilities.deprecation import ModuleAPI, build_API_changes -from colour.utilities.documentation import is_documentation_building +from colour.colorimetry import SpectralDistribution +from colour.hints import Floating, Literal, Union from .datasets import * # noqa from . import datasets -from .cfi2017 import (ColourRendering_Specification_CIE2017, - colour_fidelity_index_CIE2017) +from .cfi2017 import ( + ColourRendering_Specification_CIE2017, + colour_fidelity_index_CIE2017, +) from .cri import ColourRendering_Specification_CRI, colour_rendering_index -from .cqs import (COLOUR_QUALITY_SCALE_METHODS, - ColourRendering_Specification_CQS, colour_quality_scale) +from .cqs import ( + COLOUR_QUALITY_SCALE_METHODS, + ColourRendering_Specification_CQS, + colour_quality_scale, +) from .ssi import spectral_similarity_index -from .tm3018 import (ColourQuality_Specification_ANSIIESTM3018, - colour_fidelity_index_ANSIIESTM3018) -from colour.utilities import CaseInsensitiveMapping +from .tm3018 import ( + ColourQuality_Specification_ANSIIESTM3018, + colour_fidelity_index_ANSIIESTM3018, +) +from colour.utilities import CaseInsensitiveMapping, validate_method __all__ = [] __all__ += datasets.__all__ __all__ += [ - 'ColourRendering_Specification_CIE2017', 'colour_fidelity_index_CIE2017' + "ColourRendering_Specification_CIE2017", + "colour_fidelity_index_CIE2017", +] +__all__ += [ + "ColourQuality_Specification_ANSIIESTM3018", + "colour_fidelity_index_ANSIIESTM3018", ] __all__ += [ - 'ColourQuality_Specification_ANSIIESTM3018', - 'colour_fidelity_index_ANSIIESTM3018' + "ColourRendering_Specification_CRI", + "colour_rendering_index", ] -__all__ += ['ColourRendering_Specification_CRI', 'colour_rendering_index'] __all__ += [ - 'ColourRendering_Specification_CQS', 'COLOUR_QUALITY_SCALE_METHODS', - 'colour_quality_scale' + "ColourRendering_Specification_CQS", + "COLOUR_QUALITY_SCALE_METHODS", + "colour_quality_scale", +] +__all__ += [ + "spectral_similarity_index", ] -__all__ += ['spectral_similarity_index'] -COLOUR_FIDELITY_INDEX_METHODS = CaseInsensitiveMapping({ - 'CIE 2017': colour_fidelity_index_CIE2017, - 'ANSI/IES TM-30-18': colour_fidelity_index_ANSIIESTM3018, -}) +COLOUR_FIDELITY_INDEX_METHODS = CaseInsensitiveMapping( + { + "CIE 2017": colour_fidelity_index_CIE2017, + "ANSI/IES TM-30-18": colour_fidelity_index_ANSIIESTM3018, + } +) COLOUR_FIDELITY_INDEX_METHODS.__doc__ = """ -Supported *Colour Fidelity Index* (CFI) computation methods. +Supported *Colour Fidelity Index* (CFI) computation methods. References ---------- :cite:`CIETC1-902017`, :cite:`ANSI2018` - -COLOUR_FIDELITY_INDEX_METHODS : tuple - **{'CIE 2017', 'ANSI/IES TM-30-18'}** """ -def colour_fidelity_index(sd_test, additional_data=False, method='CIE 2017'): +def colour_fidelity_index( + sd_test: SpectralDistribution, + additional_data=False, + method: Union[Literal["CIE 2017", "ANSI/IES TM-30-18"], str] = "CIE 2017", +) -> Union[ + Floating, + ColourRendering_Specification_CIE2017, + ColourQuality_Specification_ANSIIESTM3018, +]: """ - Returns the *Colour Fidelity Index* (CFI) :math:`R_f` of given - spectral distribution using given method. + Return the *Colour Fidelity Index* (CFI) :math:`R_f` of given spectral + distribution using given method. Parameters ---------- - sd_test : SpectralDistribution + sd_test Test spectral distribution. - additional_data : bool, optional + additional_data Whether to output additional data. - method : unicode, optional - **{'CIE 2017', 'ANSI/IES TM-30-18'}**, + method Computation method. Returns ------- - numeric or ColourRendering_Specification_CIE2017 or \ -ColourQuality_Specification_ANSIIESTM3018 + :class:`numpy.floating` or \ +:class:`colour.quality.ColourRendering_Specification_CIE2017` or \ +:class:`colour.quality.ColourQuality_Specification_ANSIIESTM3018` *Colour Fidelity Index* (CFI) :math:`R_f`. References @@ -84,51 +101,14 @@ def colour_fidelity_index(sd_test, additional_data=False, method='CIE 2017'): 70.1208254... """ + method = validate_method(method, COLOUR_FIDELITY_INDEX_METHODS) + function = COLOUR_FIDELITY_INDEX_METHODS[method] return function(sd_test, additional_data) -__all__ += ['COLOUR_FIDELITY_INDEX_METHODS', 'colour_fidelity_index'] - - -# ----------------------------------------------------------------------------# -# --- API Changes and Deprecation Management ---# -# ----------------------------------------------------------------------------# -class quality(ModuleAPI): - def __getattr__(self, attribute): - return super(quality, self).__getattr__(attribute) - - -# v0.3.16 -API_CHANGES = { - 'ObjectRenamed': [ - [ - 'colour.quality.CQS_Specification', - 'colour.quality.ColourRendering_Specification_CQS', - ], - [ - 'colour.quality.CRI_Specification', - 'colour.quality.ColourRendering_Specification_CRI', - ], - [ - 'colour.quality.TCS_SDS', - 'colour.quality.SDS_TCS', - ], - [ - 'colour.quality.VS_SDS', - 'colour.quality.SDS_VS', - ], - ] -} -""" -Defines *colour.quality* sub-package API changes. - -API_CHANGES : dict -""" - -if not is_documentation_building(): - sys.modules['colour.quality'] = quality(sys.modules['colour.quality'], - build_API_changes(API_CHANGES)) - - del ModuleAPI, is_documentation_building, build_API_changes, sys +__all__ += [ + "COLOUR_FIDELITY_INDEX_METHODS", + "colour_fidelity_index", +] diff --git a/colour/quality/cfi2017.py b/colour/quality/cfi2017.py index dc41254227..3577ad3cb4 100644 --- a/colour/quality/cfi2017.py +++ b/colour/quality/cfi2017.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ CIE 2017 Colour Fidelity Index ============================== @@ -14,107 +13,158 @@ for accurate scientific use. CIE Central Bureau. ISBN:978-3-902842-61-9 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import os -from collections import namedtuple - -from colour.algebra import euclidean_distance, Extrapolator -from colour.appearance import XYZ_to_CIECAM02, VIEWING_CONDITIONS_CIECAM02 +from dataclasses import dataclass + +from colour.algebra import Extrapolator, euclidean_distance, linstep_function +from colour.appearance import ( + CAM_Specification_CIECAM02, + XYZ_to_CIECAM02, + VIEWING_CONDITIONS_CIECAM02, +) from colour.colorimetry import ( - SpectralShape, SpectralDistribution, MultiSpectralDistributions, sd_to_XYZ, - sd_blackbody, MSDS_CMFS, sd_ones, sd_CIE_illuminant_D_series) + MSDS_CMFS, + MultiSpectralDistributions, + SpectralShape, + SpectralDistribution, + sd_to_XYZ, + sd_blackbody, + reshape_msds, + sd_ones, + sd_CIE_illuminant_D_series, +) +from colour.hints import ( + Boolean, + Dict, + Floating, + FloatingOrArrayLike, + FloatingOrNDArray, + NDArray, + Tuple, + Union, + cast, +) from colour.models import XYZ_to_UCS, UCS_to_uv, JMh_CIECAM02_to_CAM02UCS from colour.temperature import uv_to_CCT_Ohno2013, CCT_to_xy_CIE_D -from colour.utilities import as_int, lerp, usage_warning - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.utilities import ( + CACHE_REGISTRY, + as_float, + as_float_array, + as_float_scalar, + as_int_scalar, + attest, + tsplit, + tstack, + usage_warning, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'SPECTRAL_SHAPE_CIE2017', 'RESOURCES_DIRECTORY_CIE2017', - 'TCS_ColorimetryData_CIE2017', 'ColourRendering_Specification_CIE2017', - 'colour_fidelity_index_CIE2017', 'load_TCS_CIE2017', - 'CCT_reference_illuminant', 'sd_reference_illuminant', - 'tcs_colorimetry_data', 'delta_E_to_R_f' + "SPECTRAL_SHAPE_CIE2017", + "RESOURCES_DIRECTORY_CIE2017", + "TCS_ColorimetryData_CIE2017", + "ColourRendering_Specification_CIE2017", + "colour_fidelity_index_CIE2017", + "load_TCS_CIE2017", + "CCT_reference_illuminant", + "sd_reference_illuminant", + "tcs_colorimetry_data", + "delta_E_to_R_f", ] -SPECTRAL_SHAPE_CIE2017 = SpectralShape(380, 780, 1) +SPECTRAL_SHAPE_CIE2017: SpectralShape = SpectralShape(380, 780, 1) """ Spectral shape for *CIE 2017 Colour Fidelity Index* (CFI) standard. - -SPECTRAL_SHAPE_CIE2017 : SpectralShape """ -RESOURCES_DIRECTORY_CIE2017 = os.path.join( - os.path.dirname(__file__), 'datasets') -""" -*CIE 2017 Colour Fidelity Index* resources directory. +RESOURCES_DIRECTORY_CIE2017: str = os.path.join( + os.path.dirname(__file__), "datasets" +) +"""*CIE 2017 Colour Fidelity Index* resources directory.""" -RESOURCES_DIRECTORY_CIE2017 : unicode -""" +_CACHE_TCS_CIE2017: Dict = CACHE_REGISTRY.register_cache( + f"{__name__}._CACHE_TCS_CIE2017" +) -_CACHE_TCS_CIE2017 = {} +@dataclass +class TCS_ColorimetryData_CIE2017: + """Define the class storing *test colour samples* colorimetry data.""" -class TCS_ColorimetryData_CIE2017( - namedtuple('TCS_ColorimetryData_CIE2017', - ('name', 'XYZ', 'CAM', 'JMh', 'Jpapbp'))): - """ - Defines the the class storing *test colour samples* colorimetry data. - """ + name: str + XYZ: NDArray + CAM: CAM_Specification_CIECAM02 + JMh: NDArray + Jpapbp: NDArray -class ColourRendering_Specification_CIE2017( - namedtuple('ColourRendering_Specification_CIE2017', - ('name', 'sd_reference', 'R_f', 'R_s', 'CCT', 'D_uv', - 'colorimetry_data', 'delta_E_s'))): +@dataclass +class ColourRendering_Specification_CIE2017: """ - Defines the *CIE 2017 Colour Fidelity Index* (CFI) colour quality + Define the *CIE 2017 Colour Fidelity Index* (CFI) colour quality specification. Parameters ---------- - name : unicode + name Name of the test spectral distribution. - sd_reference : SpectralDistribution + sd_reference Spectral distribution of the reference illuminant. - R_f : numeric + R_f *CIE 2017 Colour Fidelity Index* (CFI) :math:`R_f`. - R_s : array_like + R_s Individual *colour fidelity indexes* data for each sample. - CCT : numeric + CCT Correlated colour temperature :math:`T_{cp}`. - D_uv : numeric + D_uv Distance from the Planckian locus :math:`\\Delta_{uv}`. - colorimetry_data : tuple + colorimetry_data Colorimetry data for the test and reference computations. - delta_E_s : ndarray, (16,) + delta_E_s Colour shifts of samples. """ - -def colour_fidelity_index_CIE2017(sd_test, additional_data=False): + name: str + sd_reference: SpectralDistribution + R_f: Floating + R_s: NDArray + CCT: Floating + D_uv: Floating + colorimetry_data: Tuple[ + Tuple[TCS_ColorimetryData_CIE2017, ...], + Tuple[TCS_ColorimetryData_CIE2017, ...], + ] + delta_E_s: NDArray + + +def colour_fidelity_index_CIE2017( + sd_test: SpectralDistribution, additional_data: Boolean = False +) -> Union[Floating, ColourRendering_Specification_CIE2017]: """ - Returns the *CIE 2017 Colour Fidelity Index* (CFI) :math:`R_f` of given + Return the *CIE 2017 Colour Fidelity Index* (CFI) :math:`R_f` of given spectral distribution. Parameters ---------- - sd_test : SpectralDistribution + sd_test Test spectral distribution. - additional_data : bool, optional + additional_data Whether to output additional data. Returns ------- - numeric or ColourRendering_Specification_CIE2017 + :class:`numpy.floating` or \ +:class:`colour.quality.ColourRendering_Specification_CIE2017` *CIE 2017 Colour Fidelity Index* (CFI) :math:`R_f`. References @@ -130,63 +180,81 @@ def colour_fidelity_index_CIE2017(sd_test, additional_data=False): """ if sd_test.shape.start > 380 or sd_test.shape.end < 780: - usage_warning('Test spectral distribution shape does not span the' - 'recommended 380-780nm range, missing values will be' - 'filled with zeros!') + usage_warning( + "Test spectral distribution shape does not span the" + "recommended 380-780nm range, missing values will be" + "filled with zeros!" + ) # NOTE: "CIE 2017 Colour Fidelity Index" standard recommends filling # missing values with zeros. - sd_test = sd_test.copy() + sd_test = cast(SpectralDistribution, sd_test.copy()) sd_test.extrapolator = Extrapolator sd_test.extrapolator_kwargs = { - 'method': 'constant', - 'left': 0, - 'right': 0 + "method": "constant", + "left": 0, + "right": 0, } if sd_test.shape.interval > 5: - raise ValueError('Test spectral distribution interval is greater than' - '5nm which is the maximum recommended value ' - 'for computing the "CIE 2017 Colour Fidelity Index"!') - - shape = SpectralShape(SPECTRAL_SHAPE_CIE2017.start, - SPECTRAL_SHAPE_CIE2017.end, sd_test.shape.interval) - - CCT, D_uv = CCT_reference_illuminant(sd_test) + raise ValueError( + "Test spectral distribution interval is greater than" + "5nm which is the maximum recommended value " + 'for computing the "CIE 2017 Colour Fidelity Index"!' + ) + + shape = SpectralShape( + SPECTRAL_SHAPE_CIE2017.start, + SPECTRAL_SHAPE_CIE2017.end, + sd_test.shape.interval, + ) + + CCT, D_uv = tsplit(CCT_reference_illuminant(sd_test)) sd_reference = sd_reference_illuminant(CCT, shape) # NOTE: All computations except CCT calculation use the # "CIE 1964 10 Degree Standard Observer". - cmfs_10 = MSDS_CMFS['CIE 1964 10 Degree Standard Observer'].copy().align( - shape) + # pylint: disable=E1102 + cmfs_10 = reshape_msds( + MSDS_CMFS["CIE 1964 10 Degree Standard Observer"], shape + ) - sds_tcs = load_TCS_CIE2017(shape).align(shape) + # pylint: disable=E1102 + sds_tcs = reshape_msds(load_TCS_CIE2017(shape), shape) test_tcs_colorimetry_data = tcs_colorimetry_data(sd_test, sds_tcs, cmfs_10) reference_tcs_colorimetry_data = tcs_colorimetry_data( - sd_reference, sds_tcs, cmfs_10) + sd_reference, sds_tcs, cmfs_10 + ) delta_E_s = np.empty(len(sds_tcs.labels)) for i, _delta_E in enumerate(delta_E_s): delta_E_s[i] = euclidean_distance( test_tcs_colorimetry_data[i].Jpapbp, - reference_tcs_colorimetry_data[i].Jpapbp) + reference_tcs_colorimetry_data[i].Jpapbp, + ) - R_s = delta_E_to_R_f(delta_E_s) - R_f = delta_E_to_R_f(np.average(delta_E_s)) + R_s = as_float_array(delta_E_to_R_f(delta_E_s)) + R_f = as_float_scalar(delta_E_to_R_f(np.average(delta_E_s))) if additional_data: return ColourRendering_Specification_CIE2017( - sd_test.name, sd_reference, R_f, R_s, CCT, D_uv, + sd_test.name, + sd_reference, + R_f, + R_s, + CCT, + D_uv, (test_tcs_colorimetry_data, reference_tcs_colorimetry_data), - delta_E_s) + delta_E_s, + ) else: return R_f -def load_TCS_CIE2017(shape): +def load_TCS_CIE2017(shape: SpectralShape) -> MultiSpectralDistributions: """ - Loads the *CIE 2017 Test Colour Samples* dataset appropriate for the given + Load the *CIE 2017 Test Colour Samples* dataset appropriate for the given spectral shape. The datasets are cached and won't be loaded again on subsequent calls to @@ -194,17 +262,17 @@ def load_TCS_CIE2017(shape): Parameters ---------- - shape : SpectralShape + shape Spectral shape of the tested illuminant. Returns ------- - MultiSpectralDistributions + :class:`colour.MultiSpectralDistributions` *CIE 2017 Test Colour Samples* dataset. Examples -------- - >>> sds_tcs = load_TCS_CIE2017(SpectralShape(interval=5)) + >>> sds_tcs = load_TCS_CIE2017(SpectralShape(380, 780, 5)) >>> len(sds_tcs.labels) 99 """ @@ -213,36 +281,42 @@ def load_TCS_CIE2017(shape): interval = shape.interval - assert interval in (1, 5), ( - 'Spectral shape interval must be either 1nm or 5nm!') + attest( + interval in (1, 5), + "Spectral shape interval must be either 1nm or 5nm!", + ) - filename = 'tcs_cfi2017_{0}_nm.csv.gz'.format(as_int(interval)) + filename = f"tcs_cfi2017_{as_int_scalar(interval)}_nm.csv.gz" if filename in _CACHE_TCS_CIE2017: return _CACHE_TCS_CIE2017[filename] data = np.genfromtxt( - str(os.path.join(RESOURCES_DIRECTORY_CIE2017, filename)), - delimiter=',') - labels = ['TCS{0} (CIE 2017)'.format(i) for i in range(99)] + str(os.path.join(RESOURCES_DIRECTORY_CIE2017, filename)), delimiter="," + ) + labels = [f"TCS{i} (CIE 2017)" for i in range(99)] + + tcs = MultiSpectralDistributions(data[:, 1:], data[:, 0], labels) - return MultiSpectralDistributions(data[:, 1:], data[:, 0], labels) + _CACHE_TCS_CIE2017[filename] = tcs + return tcs -def CCT_reference_illuminant(sd): + +def CCT_reference_illuminant(sd: SpectralDistribution) -> NDArray: """ - Computes the reference illuminant correlated colour temperature + Compute the reference illuminant correlated colour temperature :math:`T_{cp}` and :math:`\\Delta_{uv}` for given test spectral distribution using *Ohno (2013)* method. Parameters ---------- - sd : SpectralDistribution + sd Test spectral distribution. Returns ------- - ndarray + :class:`numpy.ndarray` Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`. Examples @@ -250,32 +324,32 @@ def CCT_reference_illuminant(sd): >>> from colour import SDS_ILLUMINANTS >>> sd = SDS_ILLUMINANTS['FL2'] >>> CCT_reference_illuminant(sd) # doctest: +ELLIPSIS - (4224.4697052..., 0.0017871...) + array([ 4.2244697...e+03, 1.7871111...e-03]) """ XYZ = sd_to_XYZ(sd) - CCT, D_uv = uv_to_CCT_Ohno2013(UCS_to_uv(XYZ_to_UCS(XYZ))) - - return CCT, D_uv + return uv_to_CCT_Ohno2013(UCS_to_uv(XYZ_to_UCS(XYZ))) -def sd_reference_illuminant(CCT, shape): +def sd_reference_illuminant( + CCT: Floating, shape: SpectralShape +) -> SpectralDistribution: """ - Computes the reference illuminant for a given correlated colour temperature + Compute the reference illuminant for a given correlated colour temperature :math:`T_{cp}` for use in *CIE 2017 Colour Fidelity Index* (CFI) computation. Parameters ---------- - CCT : numeric + CCT Correlated colour temperature :math:`T_{cp}`. - shape : SpectralShape + shape Desired shape of the returned spectral distribution. Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Reference illuminant for *CIE 2017 Colour Fidelity Index* (CFI) computation. @@ -324,14 +398,17 @@ def sd_reference_illuminant(CCT, shape): elif 4000 <= CCT <= 5000: # Planckian and daylight illuminant must be normalised so that the # mixture isn't biased. - sd_planckian /= sd_to_XYZ(sd_planckian)[1] - sd_daylight /= sd_to_XYZ(sd_daylight)[1] + sd_planckian /= sd_to_XYZ(sd_planckian)[1] # type: ignore[misc] + sd_daylight /= sd_to_XYZ(sd_daylight)[1] # type: ignore[misc] # Mixture: 4200K should be 80% Planckian, 20% CIE Illuminant D Series. m = (CCT - 4000) / 1000 - values = lerp(sd_planckian.values, sd_daylight.values, m) - name = ('{0}K Blackbody & CIE Illuminant D Series Mixture - {1:.1f}%' - .format(as_int(CCT), 100 * m)) + values = linstep_function(m, sd_planckian.values, sd_daylight.values) + name = ( + f"{as_int_scalar(CCT)}K " + f"Blackbody & CIE Illuminant D Series Mixture - " + f"{as_float_scalar(100 * m):.1f}%" + ) sd_reference = SpectralDistribution(values, shape.range(), name=name) elif CCT > 5000: sd_reference = sd_daylight @@ -339,25 +416,29 @@ def sd_reference_illuminant(CCT, shape): return sd_reference -def tcs_colorimetry_data(sd_irradiance, sds_tcs, cmfs): +def tcs_colorimetry_data( + sd_irradiance: SpectralDistribution, + sds_tcs: MultiSpectralDistributions, + cmfs: MultiSpectralDistributions, +) -> Tuple[TCS_ColorimetryData_CIE2017, ...]: """ - Returns the *test colour samples* colorimetry data under given test light + Return the *test colour samples* colorimetry data under given test light source or reference illuminant spectral distribution for the *CIE 2017 Colour Fidelity Index* (CFI) computations. Parameters ---------- - sd_irradiance : SpectralDistribution + sd_irradiance Test light source or reference illuminant spectral distribution, i.e. the irradiance emitter. - sds_tcs : MultiSpectralDistributions + sds_tcs *Test colour samples* spectral distributions. - cmfs : XYZ_ColourMatchingFunctions + cmfs Standard observer colour matching functions. Returns ------- - list + :class:`tuple` *Test colour samples* colorimetry data under the given test light source or reference illuminant spectral distribution. @@ -370,37 +451,40 @@ def tcs_colorimetry_data(sd_irradiance, sds_tcs, cmfs): XYZ_w = sd_to_XYZ(sd_ones(), cmfs, sd_irradiance) Y_b = 20 L_A = 100 - surround = VIEWING_CONDITIONS_CIECAM02['Average'] + surround = VIEWING_CONDITIONS_CIECAM02["Average"] tcs_data = [] for sd_tcs in sds_tcs.to_sds(): XYZ = sd_to_XYZ(sd_tcs, cmfs, sd_irradiance) CAM = XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround, True) - JMh = CAM.J, CAM.M, CAM.h + JMh = tstack([CAM.J, CAM.M, CAM.h]) Jpapbp = JMh_CIECAM02_to_CAM02UCS(JMh) tcs_data.append( - TCS_ColorimetryData_CIE2017(sd_tcs.name, XYZ, CAM, JMh, Jpapbp)) + TCS_ColorimetryData_CIE2017(sd_tcs.name, XYZ, CAM, JMh, Jpapbp) + ) - return tcs_data + return tuple(tcs_data) -def delta_E_to_R_f(delta_E): +def delta_E_to_R_f(delta_E: FloatingOrArrayLike) -> FloatingOrNDArray: """ - Converts from colour-appearance difference to + Convert from colour-appearance difference to *CIE 2017 Colour Fidelity Index* (CFI) :math:`R_f` value. Parameters ---------- - delta_E : numeric + delta_E Euclidean distance between two colours in *CAM02-UCS* colourspace. Returns ------- - float + :class:`numpy.floating` or :class:`numpy.ndarray` Corresponding *CIE 2017 Colour Fidelity Index* (CFI) :math:`R_f` value. """ + delta_E = as_float_array(delta_E) + c_f = 6.73 - return 10 * np.log(np.exp((100 - c_f * delta_E) / 10) + 1) + return as_float(10 * np.log1p(np.exp((100 - c_f * delta_E) / 10))) diff --git a/colour/quality/cqs.py b/colour/quality/cqs.py index 6d855bbd93..cce6838f04 100644 --- a/colour/quality/cqs.py +++ b/colour/quality/cqs.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Colour Quality Scale ==================== -Defines *Colour Quality Scale* (CQS) computation objects: +Defines the *Colour Quality Scale* (CQS) computation objects: - :class:`colour.quality.ColourRendering_Specification_CQS` - :func:`colour.colour_quality_scale` @@ -22,97 +21,142 @@ usp=sharing """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from collections import namedtuple +from dataclasses import dataclass from colour.algebra import euclidean_distance from colour.colorimetry import ( - SPECTRAL_SHAPE_DEFAULT, sd_CIE_illuminant_D_series, CCS_ILLUMINANTS, - MSDS_CMFS_STANDARD_OBSERVER, sd_blackbody, sd_to_XYZ) + CCS_ILLUMINANTS, + MSDS_CMFS, + MultiSpectralDistributions, + SPECTRAL_SHAPE_DEFAULT, + SpectralDistribution, + reshape_msds, + reshape_sd, + sd_CIE_illuminant_D_series, + sd_blackbody, + sd_to_XYZ, +) +from colour.hints import ( + ArrayLike, + Boolean, + Dict, + Floating, + Integer, + Literal, + NDArray, + Optional, + Tuple, + Union, +) +from colour.models import ( + Lab_to_LCHab, + UCS_to_uv, + XYZ_to_Lab, + XYZ_to_UCS, + XYZ_to_xy, + xy_to_XYZ, +) from colour.quality.datasets.vs import INDEXES_TO_NAMES_VS, SDS_VS -from colour.models import (Lab_to_LCHab, UCS_to_uv, XYZ_to_Lab, XYZ_to_UCS, - XYZ_to_xy, xy_to_XYZ) from colour.temperature import CCT_to_xy_CIE_D, uv_to_CCT_Ohno2013 from colour.adaptation import chromatic_adaptation_VonKries -from colour.utilities import as_float_array, domain_range_scale, tsplit -from colour.utilities.documentation import (DocstringTuple, - is_documentation_building) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.utilities import ( + as_float_array, + as_float_scalar, + domain_range_scale, + tsplit, + validate_method, +) +from colour.utilities.documentation import ( + DocstringTuple, + is_documentation_building, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'GAMUT_AREA_D65', 'VS_ColorimetryData', 'VS_ColourQualityScaleData', - 'ColourRendering_Specification_CQS', 'COLOUR_QUALITY_SCALE_METHODS', - 'colour_quality_scale', 'gamut_area', 'vs_colorimetry_data', 'CCT_factor', - 'scale_conversion', 'delta_E_RMS', 'colour_quality_scales' + "GAMUT_AREA_D65", + "VS_ColorimetryData", + "VS_ColourQualityScaleData", + "ColourRendering_Specification_CQS", + "COLOUR_QUALITY_SCALE_METHODS", + "colour_quality_scale", + "gamut_area", + "vs_colorimetry_data", + "CCT_factor", + "scale_conversion", + "delta_E_RMS", + "colour_quality_scales", ] -GAMUT_AREA_D65 = 8210 -""" -Gamut area for *CIE Illuminant D Series D65*. +GAMUT_AREA_D65: Integer = 8210 +"""Gamut area for *CIE Illuminant D Series D65*.""" -GAMUT_AREA_D65 : int -""" +@dataclass +class VS_ColorimetryData: + """Define the class storing *VS test colour samples* colorimetry data.""" -class VS_ColorimetryData( - namedtuple('VS_ColorimetryData', ('name', 'XYZ', 'Lab', 'C'))): - """ - Defines the the class storing *VS test colour samples* colorimetry data. - """ + name: str + XYZ: NDArray + Lab: NDArray + C: NDArray -class VS_ColourQualityScaleData( - namedtuple('VS_ColourQualityScaleData', - ('name', 'Q_a', 'D_C_ab', 'D_E_ab', 'D_Ep_ab'))): +@dataclass +class VS_ColourQualityScaleData: """ - Defines the the class storing *VS test colour samples* colour quality - scale data. + Define the class storing *VS test colour samples* colour quality scale + data. """ + name: str + Q_a: Floating + D_C_ab: Floating + D_E_ab: Floating + D_Ep_ab: Floating + -class ColourRendering_Specification_CQS( - namedtuple('ColourRendering_Specification_CQS', - ('name', 'Q_a', 'Q_f', 'Q_p', 'Q_g', 'Q_d', 'Q_as', - 'colorimetry_data'))): +@dataclass +class ColourRendering_Specification_CQS: """ - Defines the *Colour Quality Scale* (CQS) colour rendering (quality) + Define the *Colour Quality Scale* (CQS) colour rendering (quality) specification. Parameters ---------- - name : unicode + name Name of the test spectral distribution. - Q_a : numeric + Q_a Colour quality scale :math:`Q_a`. - Q_f : numeric + Q_f Colour fidelity scale :math:`Q_f` intended to evaluate the fidelity of object colour appearances (compared to the reference illuminant of the same correlated colour temperature and illuminance). - Q_p : numeric + Q_p Colour preference scale :math:`Q_p` similar to colour quality scale :math:`Q_a` but placing additional weight on preference of object colour appearance, set to *None* in *NIST CQS 9.0* method. This metric is based on the notion that increases in chroma are generally preferred and should be rewarded. - Q_g : numeric + Q_g Gamut area scale :math:`Q_g` representing the relative gamut formed by the (:math:`a^*`, :math:`b^*`) coordinates of the 15 samples illuminated by the test light source in the *CIE L\\*a\\*b\\** object colourspace. - Q_d : numeric + Q_d Relative gamut area scale :math:`Q_d`, set to *None* in *NIST CQS 9.0* method. - Q_as : dict + Q_as Individual *Colour Quality Scale* (CQS) data for each sample. - colorimetry_data : tuple + colorimetry_data Colorimetry data for the test and reference computations. References @@ -120,42 +164,55 @@ class ColourRendering_Specification_CQS( :cite:`Davis2010a`, :cite:`Ohno2008a`, :cite:`Ohno2013` """ + name: str + Q_a: Floating + Q_f: Floating + Q_p: Optional[Floating] + Q_g: Floating + Q_d: Optional[Floating] + Q_as: Dict[Integer, VS_ColourQualityScaleData] + colorimetry_data: Tuple[ + Tuple[VS_ColorimetryData, ...], Tuple[VS_ColorimetryData, ...] + ] + -COLOUR_QUALITY_SCALE_METHODS = ('NIST CQS 7.4', 'NIST CQS 9.0') +COLOUR_QUALITY_SCALE_METHODS: Tuple = ("NIST CQS 7.4", "NIST CQS 9.0") if is_documentation_building(): # pragma: no cover COLOUR_QUALITY_SCALE_METHODS = DocstringTuple(COLOUR_QUALITY_SCALE_METHODS) COLOUR_QUALITY_SCALE_METHODS.__doc__ = """ -Supported *Colour Quality Scale* (CQS) computation methods. +Supported *Colour Quality Scale* (CQS) computation methods. References ---------- :cite:`Davis2010a`, :cite:`Ohno2008a`, :cite:`Ohno2013` - -COLOUR_QUALITY_SCALE_METHODS : tuple - **{ 'NIST CQS 9.0', 'NIST CQS 7.4'}** """ -def colour_quality_scale(sd_test, additional_data=False, - method='NIST CQS 9.0'): +def colour_quality_scale( + sd_test: SpectralDistribution, + additional_data: Boolean = False, + method: Union[ + Literal["NIST CQS 7.4", "NIST CQS 9.0"], str + ] = "NIST CQS 9.0", +) -> Union[Floating, ColourRendering_Specification_CQS]: """ - Returns the *Colour Quality Scale* (CQS) of given spectral distribution + Return the *Colour Quality Scale* (CQS) of given spectral distribution using given method. Parameters ---------- - sd_test : SpectralDistribution + sd_test Test spectral distribution. - additional_data : bool, optional + additional_data Whether to output additional data. - method : unicode, optional - **{'NIST CQS 9.0', 'NIST CQS 7.4'}**, + method Computation method. Returns ------- - numeric or ColourRendering_Specification_CQS - Color quality scale. + :class:`numpy.floating` or \ +:class:`colour.quality.ColourRendering_Specification_CQS` + *Colour Quality Scale* (CQS). References ---------- @@ -169,24 +226,19 @@ def colour_quality_scale(sd_test, additional_data=False, 64.1117031... """ - method = method.lower() - assert method.lower() in [ - m.lower() for m in COLOUR_QUALITY_SCALE_METHODS - ], ('"{0}" method is invalid, must be one of {1}!'.format( - method, COLOUR_QUALITY_SCALE_METHODS)) + method = validate_method(method, COLOUR_QUALITY_SCALE_METHODS) - cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'].copy().trim( - SPECTRAL_SHAPE_DEFAULT) + # pylint: disable=E1102 + cmfs = reshape_msds( + MSDS_CMFS["CIE 1931 2 Degree Standard Observer"], + SPECTRAL_SHAPE_DEFAULT, + ) shape = cmfs.shape - sd_test = sd_test.copy().align(shape) - vs_sds = { - sd.name: sd.copy().align(shape) - for sd in SDS_VS[method].values() - } + sd_test = reshape_sd(sd_test, shape) + vs_sds = {sd.name: reshape_sd(sd, shape) for sd in SDS_VS[method].values()} - with domain_range_scale('1'): + with domain_range_scale("1"): XYZ = sd_to_XYZ(sd_test, cmfs) uv = UCS_to_uv(XYZ_to_UCS(XYZ)) @@ -200,12 +252,15 @@ def colour_quality_scale(sd_test, additional_data=False, sd_reference.align(shape) test_vs_colorimetry_data = vs_colorimetry_data( - sd_test, sd_reference, vs_sds, cmfs, chromatic_adaptation=True) + sd_test, sd_reference, vs_sds, cmfs, chromatic_adaptation=True + ) reference_vs_colorimetry_data = vs_colorimetry_data( - sd_reference, sd_reference, vs_sds, cmfs) + sd_reference, sd_reference, vs_sds, cmfs + ) - if method == 'nist cqs 9.0': + CCT_f: Floating + if method == "nist cqs 9.0": CCT_f = 1 scaling_f = 3.2 else: @@ -214,16 +269,19 @@ def colour_quality_scale(sd_test, additional_data=False, CCT_f = CCT_factor(reference_vs_colorimetry_data, XYZ_r) scaling_f = 3.104 - Q_as = colour_quality_scales(test_vs_colorimetry_data, - reference_vs_colorimetry_data, scaling_f, - CCT_f) + Q_as = colour_quality_scales( + test_vs_colorimetry_data, + reference_vs_colorimetry_data, + scaling_f, + CCT_f, + ) - D_E_RMS = delta_E_RMS(Q_as, 'D_E_ab') - D_Ep_RMS = delta_E_RMS(Q_as, 'D_Ep_ab') + D_E_RMS = delta_E_RMS(Q_as, "D_E_ab") + D_Ep_RMS = delta_E_RMS(Q_as, "D_Ep_ab") Q_a = scale_conversion(D_Ep_RMS, CCT_f, scaling_f) - if method == 'nist cqs 9.0': + if method == "nist cqs 9.0": scaling_f = 2.93 * 1.0343 else: scaling_f = 2.928 @@ -231,43 +289,54 @@ def colour_quality_scale(sd_test, additional_data=False, Q_f = scale_conversion(D_E_RMS, CCT_f, scaling_f) G_t = gamut_area( - [vs_CQS_data.Lab for vs_CQS_data in test_vs_colorimetry_data]) + [vs_CQS_data.Lab for vs_CQS_data in test_vs_colorimetry_data] + ) G_r = gamut_area( - [vs_CQS_data.Lab for vs_CQS_data in reference_vs_colorimetry_data]) + [vs_CQS_data.Lab for vs_CQS_data in reference_vs_colorimetry_data] + ) Q_g = G_t / GAMUT_AREA_D65 * 100 - if method == 'nist cqs 9.0': - Q_d = Q_p = None + if method == "nist cqs 9.0": + Q_p = Q_d = None else: - p_delta_C = np.average([ - sample_data.D_C_ab if sample_data.D_C_ab > 0 else 0 - for sample_data in Q_as.values() - ]) - Q_p = 100 - 3.6 * (D_Ep_RMS - p_delta_C) - Q_d = G_t / G_r * CCT_f * 100 + p_delta_C = np.average( + [ + sample_data.D_C_ab if sample_data.D_C_ab > 0 else 0 + for sample_data in Q_as.values() + ] + ) + Q_p = as_float_scalar(100 - 3.6 * (D_Ep_RMS - p_delta_C)) + Q_d = as_float_scalar(G_t / G_r * CCT_f * 100) if additional_data: return ColourRendering_Specification_CQS( - sd_test.name, Q_a, Q_f, Q_p, Q_g, Q_d, Q_as, - (test_vs_colorimetry_data, reference_vs_colorimetry_data)) + sd_test.name, + Q_a, + Q_f, + Q_p, + Q_g, + Q_d, + Q_as, + (test_vs_colorimetry_data, reference_vs_colorimetry_data), + ) else: return Q_a -def gamut_area(Lab): +def gamut_area(Lab: ArrayLike) -> Floating: """ - Returns the gamut area :math:`G` covered by given *CIE L\\*a\\*b\\** + Return the gamut area :math:`G` covered by given *CIE L\\*a\\*b\\** matrices. Parameters ---------- - Lab : array_like + Lab *CIE L\\*a\\*b\\** colourspace matrices. Returns ------- - numeric + :class:`numpy.floating` Gamut area :math:`G`. Examples @@ -308,30 +377,32 @@ def gamut_area(Lab): return np.sum(S) -def vs_colorimetry_data(sd_test, - sd_reference, - sds_vs, - cmfs, - chromatic_adaptation=False): +def vs_colorimetry_data( + sd_test: SpectralDistribution, + sd_reference: SpectralDistribution, + sds_vs: Dict[str, SpectralDistribution], + cmfs: MultiSpectralDistributions, + chromatic_adaptation: Boolean = False, +) -> Tuple[VS_ColorimetryData, ...]: """ - Returns the *VS test colour samples* colorimetry data. + Return the *VS test colour samples* colorimetry data. Parameters ---------- - sd_test : SpectralDistribution + sd_test Test spectral distribution. - sd_reference : SpectralDistribution + sd_reference Reference spectral distribution. - sds_vs : dict + sds_vs *VS test colour samples* spectral distributions. - cmfs : XYZ_ColourMatchingFunctions + cmfs Standard observer colour matching functions. - chromatic_adaptation : bool, optional - Perform chromatic adaptation. + chromatic_adaptation + Whether to perform chromatic adaptation. Returns ------- - list + :class:`tuple` *VS test colour samples* colorimetry data. """ @@ -346,140 +417,163 @@ def vs_colorimetry_data(sd_test, for _key, value in sorted(INDEXES_TO_NAMES_VS.items()): sd_vs = sds_vs[value] - with domain_range_scale('1'): + with domain_range_scale("1"): XYZ_vs = sd_to_XYZ(sd_vs, cmfs, sd_test) if chromatic_adaptation: XYZ_vs = chromatic_adaptation_VonKries( - XYZ_vs, XYZ_t, XYZ_r, transform='CMCCAT2000') + XYZ_vs, XYZ_t, XYZ_r, transform="CMCCAT2000" + ) Lab_vs = XYZ_to_Lab(XYZ_vs, illuminant=xy_r) _L_vs, C_vs, _Hab = Lab_to_LCHab(Lab_vs) vs_data.append(VS_ColorimetryData(sd_vs.name, XYZ_vs, Lab_vs, C_vs)) - return vs_data + return tuple(vs_data) -def CCT_factor(reference_data, XYZ_r): + +def CCT_factor( + reference_data: Tuple[VS_ColorimetryData, ...], XYZ_r: ArrayLike +) -> Floating: """ - Returns the correlated colour temperature factor penalizing lamps with + Return the correlated colour temperature factor penalizing lamps with extremely low correlated colour temperatures. Parameters ---------- - reference_data : VS_ColorimetryData + reference_data Reference colorimetry data. - XYZ_r : array_like + XYZ_r *CIE XYZ* tristimulus values for reference. Returns ------- - numeric + :class:`numpy.floating` Correlated colour temperature factor. """ - xy_w = CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65'] + xy_w = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"] XYZ_w = xy_to_XYZ(xy_w) - Labs = [] - for vs_colorimetry_data_ in reference_data: - _name, XYZ, _Lab, _C = vs_colorimetry_data_ - XYZ_a = chromatic_adaptation_VonKries( - XYZ, XYZ_r, XYZ_w, transform='CMCCAT2000') - - Lab = XYZ_to_Lab(XYZ_a, illuminant=xy_w) - Labs.append(Lab) - - G_r = gamut_area(Labs) / GAMUT_AREA_D65 + Lab = XYZ_to_Lab( + chromatic_adaptation_VonKries( + [colorimetry_data.XYZ for colorimetry_data in reference_data], + XYZ_r, + XYZ_w, + transform="CMCCAT2000", + ), + illuminant=xy_w, + ) + + G_r = gamut_area(Lab) / GAMUT_AREA_D65 CCT_f = 1 if G_r > 1 else G_r return CCT_f -def scale_conversion(D_E_ab, CCT_f, scaling_f): +def scale_conversion( + D_E_ab: Floating, CCT_f: Floating, scaling_f: Floating +) -> Floating: """ - Returns the *Colour Quality Scale* (CQS) for given :math:`\\Delta E_{ab}` + Return the *Colour Quality Scale* (CQS) for given :math:`\\Delta E_{ab}` value and given correlated colour temperature penalizing factor. Parameters ---------- - D_E_ab : numeric + D_E_ab :math:`\\Delta E_{ab}` value. - CCT_f : numeric + CCT_f Correlated colour temperature penalizing factor. - scaling_f : numeric, optional + scaling_f Scaling factor constant. Returns ------- - numeric + :class:`numpy.floating` *Colour Quality Scale* (CQS). """ - Q_a = 10 * np.log(np.exp((100 - scaling_f * D_E_ab) / 10) + 1) * CCT_f + Q_a = 10 * np.log1p(np.exp((100 - scaling_f * D_E_ab) / 10)) * CCT_f return Q_a -def delta_E_RMS(cqs_data, attribute): +def delta_E_RMS( + CQS_data: Dict[Integer, VS_ColourQualityScaleData], attribute: str +) -> Floating: """ - Computes the root-mean-square average for given *Colour Quality Scale* + Compute the root-mean-square average for given *Colour Quality Scale* (CQS) data. Parameters ---------- - cqs_data : VS_ColourQualityScaleData + CQS_data *Colour Quality Scale* (CQS) data. - attribute : unicode + attribute Colorimetry data attribute to use to compute the root-mean-square average. Returns ------- - numeric + :class:`numpy.floating` Root-mean-square average. """ - return np.sqrt(1 / len(cqs_data) * np.sum([ - getattr(sample_data, attribute) ** 2 - for sample_data in cqs_data.values() - ])) - - -def colour_quality_scales(test_data, reference_data, scaling_f, CCT_f): + return np.sqrt( + 1 + / len(CQS_data) + * np.sum( + [ + getattr(sample_data, attribute) ** 2 + for sample_data in CQS_data.values() + ] + ) + ) + + +def colour_quality_scales( + test_data: Tuple[VS_ColorimetryData, ...], + reference_data: Tuple[VS_ColorimetryData, ...], + scaling_f: Floating, + CCT_f: Floating, +) -> Dict[Integer, VS_ColourQualityScaleData]: """ - Returns the *VS test colour samples* rendering scales. + Return the *VS test colour samples* rendering scales. Parameters ---------- - test_data : list + test_data Test data. - reference_data : list + reference_data Reference data. - scaling_f : numeric, optional + scaling_f Scaling factor constant. - CCT_f : numeric + CCT_f Factor penalizing lamps with extremely low correlated colour temperatures. Returns ------- - dict + :class:`dict` *VS Test colour samples* colour rendering scales. """ Q_as = {} - for i, _ in enumerate(test_data): + for i in range(len(test_data)): D_C_ab = test_data[i].C - reference_data[i].C - D_E_ab = euclidean_distance(test_data[i].Lab, reference_data[i].Lab) + D_E_ab = as_float_scalar( + euclidean_distance(test_data[i].Lab, reference_data[i].Lab) + ) if D_C_ab > 0: - D_Ep_ab = np.sqrt(D_E_ab ** 2 - D_C_ab ** 2) + D_Ep_ab = np.sqrt(D_E_ab**2 - D_C_ab**2) else: D_Ep_ab = D_E_ab Q_a = scale_conversion(D_Ep_ab, CCT_f, scaling_f) - Q_as[i + 1] = VS_ColourQualityScaleData(test_data[i].name, Q_a, D_C_ab, - D_E_ab, D_Ep_ab) + Q_as[i + 1] = VS_ColourQualityScaleData( + test_data[i].name, Q_a, D_C_ab, D_E_ab, D_Ep_ab + ) return Q_as diff --git a/colour/quality/cri.py b/colour/quality/cri.py index f694f0d603..ff3e5b91fe 100644 --- a/colour/quality/cri.py +++ b/colour/quality/cri.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Colour Rendering Index ====================== -Defines *Colour Rendering Index* (CRI) computation objects: +Defines the *Colour Rendering Index* (CRI) computation objects: - :class:`colour.quality.ColourRendering_Specification_CRI` - :func:`colour.colour_rendering_index` @@ -16,64 +15,90 @@ usp=sharing """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from collections import namedtuple +from dataclasses import dataclass from colour.algebra import euclidean_distance, spow from colour.colorimetry import ( - SPECTRAL_SHAPE_DEFAULT, sd_CIE_illuminant_D_series, - MSDS_CMFS_STANDARD_OBSERVER, sd_blackbody, sd_to_XYZ) -from colour.quality.datasets.tcs import INDEXES_TO_NAMES_TCS, SDS_TCS + MSDS_CMFS, + MultiSpectralDistributions, + SPECTRAL_SHAPE_DEFAULT, + SpectralDistribution, + reshape_msds, + reshape_sd, + sd_CIE_illuminant_D_series, + sd_blackbody, + sd_to_XYZ, +) +from colour.hints import ( + Boolean, + Dict, + Floating, + FloatingOrNDArray, + Integer, + NDArray, + Tuple, + Union, +) from colour.models import UCS_to_uv, XYZ_to_UCS, XYZ_to_xyY +from colour.quality.datasets.tcs import INDEXES_TO_NAMES_TCS, SDS_TCS from colour.temperature import CCT_to_xy_CIE_D, uv_to_CCT_Robertson1968 -from colour.utilities import domain_range_scale +from colour.utilities import domain_range_scale, as_float_scalar -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TCS_ColorimetryData', 'TCS_ColourQualityScaleData', - 'ColourRendering_Specification_CRI', 'colour_rendering_index', - 'tcs_colorimetry_data', 'colour_rendering_indexes' + "TCS_ColorimetryData", + "TCS_ColourQualityScaleData", + "ColourRendering_Specification_CRI", + "colour_rendering_index", + "tcs_colorimetry_data", + "colour_rendering_indexes", ] -class TCS_ColorimetryData( - namedtuple('TCS_ColorimetryData', ('name', 'XYZ', 'uv', 'UVW'))): - """ - Defines the the class storing *test colour samples* colorimetry data. - """ +@dataclass +class TCS_ColorimetryData: + """Define the class storing *test colour samples* colorimetry data.""" + name: str + XYZ: NDArray + uv: NDArray + UVW: NDArray -class TCS_ColourQualityScaleData( - namedtuple('TCS_ColourQualityScaleData', ('name', 'Q_a'))): + +@dataclass +class TCS_ColourQualityScaleData: """ - Defines the the class storing *test colour samples* colour rendering + Define the class storing *test colour samples* colour rendering index data. """ + name: str + Q_a: Floating + -class ColourRendering_Specification_CRI( - namedtuple('ColourRendering_Specification_CRI', - ('name', 'Q_a', 'Q_as', 'colorimetry_data'))): +@dataclass() +class ColourRendering_Specification_CRI: """ - Defines the *Colour Rendering Index* (CRI) colour quality specification. + Define the *Colour Rendering Index* (CRI) colour quality specification. Parameters ---------- - name : unicode + name Name of the test spectral distribution. - Q_a : numeric + Q_a *Colour Rendering Index* (CRI) :math:`Q_a`. - Q_as : dict + Q_as Individual *colour rendering indexes* data for each sample. - colorimetry_data : tuple + colorimetry_data Colorimetry data for the test and reference computations. References @@ -81,22 +106,32 @@ class ColourRendering_Specification_CRI( :cite:`Ohno2008a` """ + name: str + Q_a: Floating + Q_as: Dict[Integer, TCS_ColourQualityScaleData] + colorimetry_data: Tuple[ + Tuple[TCS_ColorimetryData, ...], Tuple[TCS_ColorimetryData, ...] + ] -def colour_rendering_index(sd_test, additional_data=False): + +def colour_rendering_index( + sd_test: SpectralDistribution, additional_data: Boolean = False +) -> Union[Floating, ColourRendering_Specification_CRI]: """ - Returns the *Colour Rendering Index* (CRI) :math:`Q_a` of given spectral + Return the *Colour Rendering Index* (CRI) :math:`Q_a` of given spectral distribution. Parameters ---------- - sd_test : SpectralDistribution + sd_test Test spectral distribution. - additional_data : bool, optional + additional_data Whether to output additional data. Returns ------- - numeric or ColourRendering_Specification_CRI + :class:`numpy.floating` or \ +:class:`colour.quality.ColourRendering_Specification_CRI` *Colour Rendering Index* (CRI). References @@ -111,15 +146,17 @@ def colour_rendering_index(sd_test, additional_data=False): 64.2337241... """ - cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'].copy().trim( - SPECTRAL_SHAPE_DEFAULT) + # pylint: disable=E1102 + cmfs = reshape_msds( + MSDS_CMFS["CIE 1931 2 Degree Standard Observer"], + SPECTRAL_SHAPE_DEFAULT, + ) shape = cmfs.shape - sd_test = sd_test.copy().align(shape) - tcs_sds = {sd.name: sd.copy().align(shape) for sd in SDS_TCS.values()} + sd_test = reshape_sd(sd_test, shape) + tcs_sds = {sd.name: reshape_sd(sd, shape) for sd in SDS_TCS.values()} - with domain_range_scale('1'): + with domain_range_scale("1"): XYZ = sd_to_XYZ(sd_test, cmfs) uv = UCS_to_uv(XYZ_to_UCS(XYZ)) @@ -133,46 +170,60 @@ def colour_rendering_index(sd_test, additional_data=False): sd_reference.align(shape) test_tcs_colorimetry_data = tcs_colorimetry_data( - sd_test, sd_reference, tcs_sds, cmfs, chromatic_adaptation=True) + sd_test, sd_reference, tcs_sds, cmfs, chromatic_adaptation=True + ) reference_tcs_colorimetry_data = tcs_colorimetry_data( - sd_reference, sd_reference, tcs_sds, cmfs) + sd_reference, sd_reference, tcs_sds, cmfs + ) - Q_as = colour_rendering_indexes(test_tcs_colorimetry_data, - reference_tcs_colorimetry_data) + Q_as = colour_rendering_indexes( + test_tcs_colorimetry_data, reference_tcs_colorimetry_data + ) - Q_a = np.average( - [v.Q_a for k, v in Q_as.items() if k in (1, 2, 3, 4, 5, 6, 7, 8)]) + Q_a = as_float_scalar( + np.average( + [v.Q_a for k, v in Q_as.items() if k in (1, 2, 3, 4, 5, 6, 7, 8)] + ) + ) if additional_data: return ColourRendering_Specification_CRI( - sd_test.name, Q_a, Q_as, - (test_tcs_colorimetry_data, reference_tcs_colorimetry_data)) + sd_test.name, + Q_a, + Q_as, + (test_tcs_colorimetry_data, reference_tcs_colorimetry_data), + ) else: return Q_a -def tcs_colorimetry_data(sd_t, sd_r, sds_tcs, cmfs, - chromatic_adaptation=False): +def tcs_colorimetry_data( + sd_t: SpectralDistribution, + sd_r: SpectralDistribution, + sds_tcs: Dict[str, SpectralDistribution], + cmfs: MultiSpectralDistributions, + chromatic_adaptation: Boolean = False, +) -> Tuple[TCS_ColorimetryData, ...]: """ - Returns the *test colour samples* colorimetry data. + Return the *test colour samples* colorimetry data. Parameters ---------- - sd_t : SpectralDistribution + sd_t Test spectral distribution. - sd_r : SpectralDistribution + sd_r Reference spectral distribution. - sds_tcs : dict + sds_tcs *Test colour samples* spectral distributions. - cmfs : XYZ_ColourMatchingFunctions + cmfs Standard observer colour matching functions. - chromatic_adaptation : bool, optional + chromatic_adaptation Perform chromatic adaptation. Returns ------- - list + :class:`tuple` *Test colour samples* colorimetry data. """ @@ -194,17 +245,17 @@ def tcs_colorimetry_data(sd_t, sd_r, sds_tcs, cmfs, if chromatic_adaptation: - def c(x, y): - """ - Computes the :math:`c` term. - """ + def c( + x: FloatingOrNDArray, y: FloatingOrNDArray + ) -> FloatingOrNDArray: + """Compute the :math:`c` term.""" return (4 - x - 10 * y) / y - def d(x, y): - """ - Computes the :math:`d` term. - """ + def d( + x: FloatingOrNDArray, y: FloatingOrNDArray + ) -> FloatingOrNDArray: + """Compute the :math:`d` term.""" return (1.708 * y + 0.404 - 1.481 * x) / y @@ -212,42 +263,54 @@ def d(x, y): c_r, d_r = c(u_r, v_r), d(u_r, v_r) tcs_c, tcs_d = c(u_tcs, v_tcs), d(u_tcs, v_tcs) u_tcs = ( - (10.872 + 0.404 * c_r / c_t * tcs_c - 4 * d_r / d_t * tcs_d) / - (16.518 + 1.481 * c_r / c_t * tcs_c - d_r / d_t * tcs_d)) - v_tcs = (5.52 / - (16.518 + 1.481 * c_r / c_t * tcs_c - d_r / d_t * tcs_d)) + 10.872 + 0.404 * c_r / c_t * tcs_c - 4 * d_r / d_t * tcs_d + ) / (16.518 + 1.481 * c_r / c_t * tcs_c - d_r / d_t * tcs_d) + v_tcs = 5.52 / ( + 16.518 + 1.481 * c_r / c_t * tcs_c - d_r / d_t * tcs_d + ) W_tcs = 25 * spow(xyY_tcs[-1], 1 / 3) - 17 U_tcs = 13 * W_tcs * (u_tcs - u_r) V_tcs = 13 * W_tcs * (v_tcs - v_r) tcs_data.append( - TCS_ColorimetryData(sd_tcs.name, XYZ_tcs, uv_tcs, - np.array([U_tcs, V_tcs, W_tcs]))) + TCS_ColorimetryData( + sd_tcs.name, XYZ_tcs, uv_tcs, np.array([U_tcs, V_tcs, W_tcs]) + ) + ) - return tcs_data + return tuple(tcs_data) -def colour_rendering_indexes(test_data, reference_data): +def colour_rendering_indexes( + test_data: Tuple[TCS_ColorimetryData, ...], + reference_data: Tuple[TCS_ColorimetryData, ...], +) -> Dict[Integer, TCS_ColourQualityScaleData]: """ - Returns the *test colour samples* rendering indexes :math:`Q_a`. + Return the *test colour samples* rendering indexes :math:`Q_a`. Parameters ---------- - test_data : list + test_data Test data. - reference_data : list + reference_data Reference data. Returns ------- - dict + :class:`dict` *Test colour samples* *Colour Rendering Index* (CRI). """ Q_as = {} - for i, _ in enumerate(test_data): + for i in range(len(test_data)): Q_as[i + 1] = TCS_ColourQualityScaleData( - test_data[i].name, 100 - - 4.6 * euclidean_distance(reference_data[i].UVW, test_data[i].UVW)) + test_data[i].name, + 100 + - 4.6 + * as_float_scalar( + euclidean_distance(reference_data[i].UVW, test_data[i].UVW) + ), + ) + return Q_as diff --git a/colour/quality/datasets/__init__.py b/colour/quality/datasets/__init__.py index 50eef546f0..51e13932e1 100644 --- a/colour/quality/datasets/__init__.py +++ b/colour/quality/datasets/__init__.py @@ -1,8 +1,7 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .tcs import SDS_TCS from .vs import SDS_VS -__all__ = ['SDS_TCS', 'SDS_VS'] +__all__ = [ + "SDS_TCS", + "SDS_VS", +] diff --git a/colour/quality/datasets/tcs.py b/colour/quality/datasets/tcs.py index 336cb3018d..074f68671f 100644 --- a/colour/quality/datasets/tcs.py +++ b/colour/quality/datasets/tcs.py @@ -1,7 +1,6 @@ -# -*- coding: utf-8 -*- """ Test Colour Samples Spectral Distributions -================================================ +========================================== Defines the *CIE 1995* test colour samples spectral distributions. @@ -18,69 +17,68 @@ usp=sharing """ -from __future__ import division, unicode_literals +from __future__ import annotations from colour.colorimetry import SpectralDistribution +from colour.hints import Dict from colour.utilities import CaseInsensitiveMapping -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'INDEXES_TO_NAMES_TCS', 'APPROXIMATE_MUNSELL_NOTATIONS_TCS', 'DATA_TCS', - 'SDS_TCS' + "INDEXES_TO_NAMES_TCS", + "APPROXIMATE_MUNSELL_NOTATIONS_TCS", + "DATA_TCS", + "SDS_TCS", ] -INDEXES_TO_NAMES_TCS = { - 1: 'TCS01', - 2: 'TCS02', - 3: 'TCS03', - 4: 'TCS04', - 5: 'TCS05', - 6: 'TCS06', - 7: 'TCS07', - 8: 'TCS08', - 9: 'TCS09', - 10: 'TCS10', - 11: 'TCS11', - 12: 'TCS12', - 13: 'TCS13', - 14: 'TCS14' +INDEXES_TO_NAMES_TCS: Dict = { + 1: "TCS01", + 2: "TCS02", + 3: "TCS03", + 4: "TCS04", + 5: "TCS05", + 6: "TCS06", + 7: "TCS07", + 8: "TCS08", + 9: "TCS09", + 10: "TCS10", + 11: "TCS11", + 12: "TCS12", + 13: "TCS13", + 14: "TCS14", } -""" -Test colour samples indexes to names mapping. - -INDEXES_TO_NAMES_TCS : dict -""" - -APPROXIMATE_MUNSELL_NOTATIONS_TCS = CaseInsensitiveMapping({ - 'TCS01': '7.5 R 6/4', - 'TCS02': '5 Y 6/4', - 'TCS03': '5 GY 6/8', - 'TCS04': '2.5 G 6/6', - 'TCS05': '10 BG 6/4', - 'TCS06': '5 PB 6/8', - 'TCS07': '2.5 P 6/8', - 'TCS08': '10 P 6/8', - 'TCS09': '4.5 R 4/13', - 'TCS10': '5 Y 8/10', - 'TCS11': '4.5 G 5/8', - 'TCS12': '3 PB 3/11', - 'TCS13': '5 YR 8/4', - 'TCS14': '5 GY 4/4' -}) -""" -Test colour samples *Munsell* colour approximations. +"""Test colour samples indexes to names mapping.""" -APPROXIMATE_MUNSELL_NOTATIONS_TCS : CaseInsensitiveMapping -""" +APPROXIMATE_MUNSELL_NOTATIONS_TCS: CaseInsensitiveMapping = ( + CaseInsensitiveMapping( + { + "TCS01": "7.5 R 6/4", + "TCS02": "5 Y 6/4", + "TCS03": "5 GY 6/8", + "TCS04": "2.5 G 6/6", + "TCS05": "10 BG 6/4", + "TCS06": "5 PB 6/8", + "TCS07": "2.5 P 6/8", + "TCS08": "10 P 6/8", + "TCS09": "4.5 R 4/13", + "TCS10": "5 Y 8/10", + "TCS11": "4.5 G 5/8", + "TCS12": "3 PB 3/11", + "TCS13": "5 YR 8/4", + "TCS14": "5 GY 4/4", + } + ) +) +"""Test colour samples *Munsell* colour approximations.""" -DATA_TCS = { - 'TCS01': { +DATA_TCS: Dict = { + "TCS01": { 360: 0.116, 365: 0.136, 370: 0.159, @@ -175,9 +173,9 @@ 815: 0.466, 820: 0.465, 825: 0.464, - 830: 0.464 + 830: 0.464, }, - 'TCS02': { + "TCS02": { 360: 0.053, 365: 0.055, 370: 0.059, @@ -272,9 +270,9 @@ 815: 0.311, 820: 0.311, 825: 0.311, - 830: 0.310 + 830: 0.310, }, - 'TCS03': { + "TCS03": { 360: 0.058, 365: 0.059, 370: 0.061, @@ -369,9 +367,9 @@ 815: 0.544, 820: 0.548, 825: 0.552, - 830: 0.555 + 830: 0.555, }, - 'TCS04': { + "TCS04": { 360: 0.057, 365: 0.059, 370: 0.062, @@ -466,9 +464,9 @@ 815: 0.243, 820: 0.254, 825: 0.264, - 830: 0.274 + 830: 0.274, }, - 'TCS05': { + "TCS05": { 360: 0.143, 365: 0.187, 370: 0.233, @@ -563,9 +561,9 @@ 815: 0.257, 820: 0.265, 825: 0.273, - 830: 0.280 + 830: 0.280, }, - 'TCS06': { + "TCS06": { 360: 0.079, 365: 0.081, 370: 0.089, @@ -660,9 +658,9 @@ 815: 0.525, 820: 0.529, 825: 0.532, - 830: 0.535 + 830: 0.535, }, - 'TCS07': { + "TCS07": { 360: 0.150, 365: 0.177, 370: 0.218, @@ -757,9 +755,9 @@ 815: 0.590, 820: 0.590, 825: 0.591, - 830: 0.592 + 830: 0.592, }, - 'TCS08': { + "TCS08": { 360: 0.075, 365: 0.078, 370: 0.084, @@ -854,9 +852,9 @@ 815: 0.731, 820: 0.731, 825: 0.731, - 830: 0.731 + 830: 0.731, }, - 'TCS09': { + "TCS09": { 360: 0.069, 365: 0.072, 370: 0.073, @@ -951,9 +949,9 @@ 815: 0.837, 820: 0.837, 825: 0.836, - 830: 0.836 + 830: 0.836, }, - 'TCS10': { + "TCS10": { 360: 0.042, 365: 0.043, 370: 0.045, @@ -1048,9 +1046,9 @@ 815: 0.757, 820: 0.757, 825: 0.756, - 830: 0.756 + 830: 0.756, }, - 'TCS11': { + "TCS11": { 360: 0.074, 365: 0.079, 370: 0.086, @@ -1145,9 +1143,9 @@ 815: 0.380, 820: 0.388, 825: 0.396, - 830: 0.403 + 830: 0.403, }, - 'TCS12': { + "TCS12": { 360: 0.189, 365: 0.175, 370: 0.158, @@ -1242,9 +1240,9 @@ 815: 0.694, 820: 0.697, 825: 0.700, - 830: 0.702 + 830: 0.702, }, - 'TCS13': { + "TCS13": { 360: 0.071, 365: 0.076, 370: 0.082, @@ -1339,9 +1337,9 @@ 815: 0.745, 820: 0.745, 825: 0.745, - 830: 0.745 + 830: 0.745, }, - 'TCS14': { + "TCS14": { 360: 0.036, 365: 0.036, 370: 0.036, @@ -1436,19 +1434,20 @@ 815: 0.444, 820: 0.448, 825: 0.451, - 830: 0.454 - } + 830: 0.454, + }, } -SDS_TCS = CaseInsensitiveMapping( - dict((key, SpectralDistribution(value, name=key)) - for key, value in DATA_TCS.items())) +SDS_TCS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + key: SpectralDistribution(value, name=key) + for key, value in DATA_TCS.items() + } +) """ Test colour samples spectral distributions. References ---------- :cite:`Ohno2008a` - -SDS_TCS : CaseInsensitiveMapping """ diff --git a/colour/quality/datasets/vs.py b/colour/quality/datasets/vs.py index 775778114c..c7201ab7aa 100644 --- a/colour/quality/datasets/vs.py +++ b/colour/quality/datasets/vs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ NIST CQS Test Colour Samples Spectral Distributions =================================================== @@ -9,10 +8,12 @@ The *NIST CQS* test colour samples data is in the form of a *dict* of :class:`colour.SpectralDistribution` classes as follows:: - {'method': { - 'name': SpectralDistribution, - ..., - 'name': SpectralDistribution} + { + 'method': { + 'name': SpectralDistribution, + ..., + 'name': SpectralDistribution + } } References @@ -27,95 +28,95 @@ usp=sharing """ -from __future__ import division, unicode_literals +from __future__ import annotations from colour.colorimetry import SpectralDistribution +from colour.hints import Dict from colour.utilities import CaseInsensitiveMapping -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'INDEXES_TO_NAMES_VS', 'APPROXIMATE_MUNSELL_NOTATIONS_VS_NISTCQS74', - 'APPROXIMATE_MUNSELL_NOTATIONS_VS_NISTCQS90', 'DATA_VS', 'SDS_VS' + "INDEXES_TO_NAMES_VS", + "APPROXIMATE_MUNSELL_NOTATIONS_VS_NISTCQS74", + "APPROXIMATE_MUNSELL_NOTATIONS_VS_NISTCQS90", + "DATA_VS", + "SDS_VS", ] -INDEXES_TO_NAMES_VS = { - 1: 'VS1', - 2: 'VS2', - 3: 'VS3', - 4: 'VS4', - 5: 'VS5', - 6: 'VS6', - 7: 'VS7', - 8: 'VS8', - 9: 'VS9', - 10: 'VS10', - 11: 'VS11', - 12: 'VS12', - 13: 'VS13', - 14: 'VS14', - 15: 'VS15' +INDEXES_TO_NAMES_VS: Dict = { + 1: "VS1", + 2: "VS2", + 3: "VS3", + 4: "VS4", + 5: "VS5", + 6: "VS6", + 7: "VS7", + 8: "VS8", + 9: "VS9", + 10: "VS10", + 11: "VS11", + 12: "VS12", + 13: "VS13", + 14: "VS14", + 15: "VS15", } -""" -*NIST CQS* test colour samples indexes to names mapping. - -INDEXES_TO_NAMES_VS : dict -""" - -APPROXIMATE_MUNSELL_NOTATIONS_VS_NISTCQS74 = CaseInsensitiveMapping({ - 'VS1': '7.5P 4/10', - 'VS2': '10PB 4/10', - 'VS3': '5PB 4/2', - 'VS4': '7.5B 5/10', - 'VS5': '10B G6/8', - 'VS6': '2.5BG 6/10', - 'VS7': '2.5G 6/12', - 'VS8': '7.5GY 7/10', - 'VS9': '2.5GY 8/10', - 'VS10': '5Y 8.5/12', - 'VS11': '10YR 7/12', - 'VS12': '5YR 7/12', - 'VS13': '10R 6/12', - 'VS14': '5R 4/14', - 'VS15': '7.5RP 4/12' -}) -""" -*NIST CQS 7.4* test colour samples *Munsell* colour approximations. - -APPROXIMATE_MUNSELL_NOTATIONS_VS_NISTCQS74 : CaseInsensitiveMapping -""" +"""*NIST CQS* test colour samples indexes to names mapping.""" -APPROXIMATE_MUNSELL_NOTATIONS_VS_NISTCQS90 = CaseInsensitiveMapping({ - 'VS1': '5R 4/14', - 'VS2': '10R 6/12', - 'VS3': '7.5YR 7/12', - 'VS4': '5Y 8-12', - 'VS5': '2.5GY 8/10', - 'VS6': '7.5GY 7/10', - 'VS7': '2.5G 6/12', - 'VS8': '2.5BG 6/10', - 'VS9': '10BG 6/8', - 'VS10': '7.5B 5/10', - 'VS11': '2.5PB 4/10', - 'VS12': '7.5PB 4/12', - 'VS13': '5P 5/10', - 'VS14': '2.5RP 6/12', - 'VS15': '7.5RP 5/12' -}) -""" -*NIST CQS 9.0* test colour samples *Munsell* colour approximations. +APPROXIMATE_MUNSELL_NOTATIONS_VS_NISTCQS74: CaseInsensitiveMapping = ( + CaseInsensitiveMapping( + { + "VS1": "7.5P 4/10", + "VS2": "10PB 4/10", + "VS3": "5PB 4/2", + "VS4": "7.5B 5/10", + "VS5": "10B G6/8", + "VS6": "2.5BG 6/10", + "VS7": "2.5G 6/12", + "VS8": "7.5GY 7/10", + "VS9": "2.5GY 8/10", + "VS10": "5Y 8.5/12", + "VS11": "10YR 7/12", + "VS12": "5YR 7/12", + "VS13": "10R 6/12", + "VS14": "5R 4/14", + "VS15": "7.5RP 4/12", + } + ) +) +"""*NIST CQS 7.4* test colour samples *Munsell* colour approximations.""" -VS_APPROXIMATE_MUNSELL_NOTATIONS_NISTCQ90 : CaseInsensitiveMapping -""" +APPROXIMATE_MUNSELL_NOTATIONS_VS_NISTCQS90: CaseInsensitiveMapping = ( + CaseInsensitiveMapping( + { + "VS1": "5R 4/14", + "VS2": "10R 6/12", + "VS3": "7.5YR 7/12", + "VS4": "5Y 8-12", + "VS5": "2.5GY 8/10", + "VS6": "7.5GY 7/10", + "VS7": "2.5G 6/12", + "VS8": "2.5BG 6/10", + "VS9": "10BG 6/8", + "VS10": "7.5B 5/10", + "VS11": "2.5PB 4/10", + "VS12": "7.5PB 4/12", + "VS13": "5P 5/10", + "VS14": "2.5RP 6/12", + "VS15": "7.5RP 5/12", + } + ) +) +"""*NIST CQS 9.0* test colour samples *Munsell* colour approximations.""" -DATA_VS = { - 'NIST CQS 7.4': { - 'VS1': { +DATA_VS: Dict = { + "NIST CQS 7.4": { + "VS1": { 380: 0.1086, 385: 0.1380, 390: 0.1729, @@ -206,9 +207,9 @@ 815: 0.5191, 820: 0.5191, 825: 0.5191, - 830: 0.5191 + 830: 0.5191, }, - 'VS2': { + "VS2": { 380: 0.1053, 385: 0.1323, 390: 0.1662, @@ -299,9 +300,9 @@ 815: 0.4537, 820: 0.4537, 825: 0.4537, - 830: 0.4537 + 830: 0.4537, }, - 'VS3': { + "VS3": { 380: 0.0858, 385: 0.0990, 390: 0.1204, @@ -392,9 +393,9 @@ 815: 0.2338, 820: 0.2338, 825: 0.2338, - 830: 0.2338 + 830: 0.2338, }, - 'VS4': { + "VS4": { 380: 0.0790, 385: 0.0984, 390: 0.1242, @@ -485,9 +486,9 @@ 815: 0.3512, 820: 0.3512, 825: 0.3512, - 830: 0.3512 + 830: 0.3512, }, - 'VS5': { + "VS5": { 380: 0.1167, 385: 0.1352, 390: 0.1674, @@ -578,9 +579,9 @@ 815: 0.2269, 820: 0.2269, 825: 0.2269, - 830: 0.2269 + 830: 0.2269, }, - 'VS6': { + "VS6": { 380: 0.0872, 385: 0.1001, 390: 0.1159, @@ -671,9 +672,9 @@ 815: 0.1646, 820: 0.1646, 825: 0.1646, - 830: 0.1646 + 830: 0.1646, }, - 'VS7': { + "VS7": { 380: 0.0726, 385: 0.0760, 390: 0.0789, @@ -764,9 +765,9 @@ 815: 0.1393, 820: 0.1393, 825: 0.1393, - 830: 0.1393 + 830: 0.1393, }, - 'VS8': { + "VS8": { 380: 0.0652, 385: 0.0657, 390: 0.0667, @@ -857,9 +858,9 @@ 815: 0.3239, 820: 0.3239, 825: 0.3239, - 830: 0.3239 + 830: 0.3239, }, - 'VS9': { + "VS9": { 380: 0.0643, 385: 0.0661, 390: 0.0702, @@ -950,9 +951,9 @@ 815: 0.5450, 820: 0.5450, 825: 0.5450, - 830: 0.5450 + 830: 0.5450, }, - 'VS10': { + "VS10": { 380: 0.0540, 385: 0.0489, 390: 0.0548, @@ -1043,9 +1044,9 @@ 815: 0.7789, 820: 0.7789, 825: 0.7789, - 830: 0.7789 + 830: 0.7789, }, - 'VS11': { + "VS11": { 380: 0.0482, 385: 0.0456, 390: 0.0478, @@ -1136,9 +1137,9 @@ 815: 0.5953, 820: 0.5953, 825: 0.5953, - 830: 0.5953 + 830: 0.5953, }, - 'VS12': { + "VS12": { 380: 0.0691, 385: 0.0692, 390: 0.0727, @@ -1229,9 +1230,9 @@ 815: 0.7158, 820: 0.7158, 825: 0.7158, - 830: 0.7158 + 830: 0.7158, }, - 'VS13': { + "VS13": { 380: 0.0829, 385: 0.0829, 390: 0.0866, @@ -1322,9 +1323,9 @@ 815: 0.6813, 820: 0.6813, 825: 0.6813, - 830: 0.6813 + 830: 0.6813, }, - 'VS14': { + "VS14": { 380: 0.0530, 385: 0.0507, 390: 0.0505, @@ -1415,9 +1416,9 @@ 815: 0.7683, 820: 0.7683, 825: 0.7683, - 830: 0.7683 + 830: 0.7683, }, - 'VS15': { + "VS15": { 380: 0.0908, 385: 0.1021, 390: 0.1130, @@ -1508,11 +1509,11 @@ 815: 0.7075, 820: 0.7075, 825: 0.7075, - 830: 0.7075 + 830: 0.7075, }, }, - 'NIST CQS 9.0': { - 'VS1': { + "NIST CQS 9.0": { + "VS1": { 360: 0.0530, 365: 0.0530, 370: 0.0530, @@ -1607,9 +1608,9 @@ 815: 0.7683, 820: 0.7683, 825: 0.7683, - 830: 0.7683 + 830: 0.7683, }, - 'VS2': { + "VS2": { 360: 0.0829, 365: 0.0829, 370: 0.0829, @@ -1704,9 +1705,9 @@ 815: 0.6813, 820: 0.6813, 825: 0.6813, - 830: 0.6813 + 830: 0.6813, }, - 'VS3': { + "VS3": { 360: 0.0691, 365: 0.0691, 370: 0.0691, @@ -1801,9 +1802,9 @@ 815: 0.6283, 820: 0.6283, 825: 0.6283, - 830: 0.6283 + 830: 0.6283, }, - 'VS4': { + "VS4": { 360: 0.0449, 365: 0.0449, 370: 0.0449, @@ -1898,9 +1899,9 @@ 815: 0.6784, 820: 0.6784, 825: 0.6784, - 830: 0.6784 + 830: 0.6784, }, - 'VS5': { + "VS5": { 360: 0.0643, 365: 0.0643, 370: 0.0643, @@ -1995,9 +1996,9 @@ 815: 0.5450, 820: 0.5450, 825: 0.5450, - 830: 0.5450 + 830: 0.5450, }, - 'VS6': { + "VS6": { 360: 0.0652, 365: 0.0652, 370: 0.0652, @@ -2092,9 +2093,9 @@ 815: 0.3239, 820: 0.3239, 825: 0.3239, - 830: 0.3239 + 830: 0.3239, }, - 'VS7': { + "VS7": { 360: 0.0726, 365: 0.0726, 370: 0.0726, @@ -2189,9 +2190,9 @@ 815: 0.1393, 820: 0.1393, 825: 0.1393, - 830: 0.1393 + 830: 0.1393, }, - 'VS8': { + "VS8": { 360: 0.0872, 365: 0.0872, 370: 0.0872, @@ -2286,9 +2287,9 @@ 815: 0.1646, 820: 0.1646, 825: 0.1646, - 830: 0.1646 + 830: 0.1646, }, - 'VS9': { + "VS9": { 360: 0.1167, 365: 0.1167, 370: 0.1167, @@ -2383,9 +2384,9 @@ 815: 0.2269, 820: 0.2269, 825: 0.2269, - 830: 0.2269 + 830: 0.2269, }, - 'VS10': { + "VS10": { 360: 0.0790, 365: 0.0790, 370: 0.0790, @@ -2480,9 +2481,9 @@ 815: 0.3512, 820: 0.3512, 825: 0.3512, - 830: 0.3512 + 830: 0.3512, }, - 'VS11': { + "VS11": { 360: 0.0692, 365: 0.0692, 370: 0.0692, @@ -2577,9 +2578,9 @@ 815: 0.3811, 820: 0.3811, 825: 0.3811, - 830: 0.3811 + 830: 0.3811, }, - 'VS12': { + "VS12": { 360: 0.0941, 365: 0.0941, 370: 0.0941, @@ -2674,9 +2675,9 @@ 815: 0.3897, 820: 0.3897, 825: 0.3897, - 830: 0.3897 + 830: 0.3897, }, - 'VS13': { + "VS13": { 360: 0.1272, 365: 0.1272, 370: 0.1272, @@ -2771,9 +2772,9 @@ 815: 0.7633, 820: 0.7633, 825: 0.7633, - 830: 0.7633 + 830: 0.7633, }, - 'VS14': { + "VS14": { 360: 0.1086, 365: 0.1086, 370: 0.1086, @@ -2868,9 +2869,9 @@ 815: 0.7507, 820: 0.7507, 825: 0.7507, - 830: 0.7507 + 830: 0.7507, }, - 'VS15': { + "VS15": { 360: 0.1069, 365: 0.1069, 370: 0.1069, @@ -2965,23 +2966,24 @@ 815: 0.7439, 820: 0.7439, 825: 0.7439, - 830: 0.7439 - } - } + 830: 0.7439, + }, + }, } -SDS_VS = CaseInsensitiveMapping({ - key: dict((name, SpectralDistribution(data, name=name)) - for name, data in value.items()) - for key, value in DATA_VS.items() -}) +SDS_VS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + key: { + name: SpectralDistribution(data, name=name) + for name, data in value.items() + } + for key, value in DATA_VS.items() + } +) """ *NIST CQS* test colour samples spectral distributions. References ---------- :cite:`Ohno2008a`, :cite:`Ohno2013` - -SDS_VS : CaseInsensitiveMapping - **{'NIST CQS 7.4', 'NIST CQS 9.0'}** """ diff --git a/colour/quality/ssi.py b/colour/quality/ssi.py index afc6367a85..accc456702 100644 --- a/colour/quality/ssi.py +++ b/colour/quality/ssi.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Academy Spectral Similarity Index (SSI) ======================================= @@ -14,51 +13,53 @@ (SSI): Overview (pp. 1-7). """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from scipy.ndimage.filters import convolve1d from colour.algebra import LinearInterpolator -from colour.colorimetry import SpectralShape +from colour.colorimetry import SpectralDistribution, SpectralShape, reshape_sd +from colour.hints import NDArray, Optional from colour.utilities import zeros -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['SPECTRAL_SHAPE_SSI', 'spectral_similarity_index'] +__all__ = [ + "SPECTRAL_SHAPE_SSI", + "spectral_similarity_index", +] -SPECTRAL_SHAPE_SSI = SpectralShape(375, 675, 1) -""" -*Academy Spectral Similarity Index* (SSI) spectral shape. - -SPECTRAL_SHAPE_SSI : SpectralShape -""" +SPECTRAL_SHAPE_SSI: SpectralShape = SpectralShape(375, 675, 1) +"""*Academy Spectral Similarity Index* (SSI) spectral shape.""" -_SPECTRAL_SHAPE_SSI_LARGE = SpectralShape(380, 670, 10) +_SPECTRAL_SHAPE_SSI_LARGE: SpectralShape = SpectralShape(380, 670, 10) -_MATRIX_INTEGRATION = None +_MATRIX_INTEGRATION: Optional[NDArray] = None -def spectral_similarity_index(sd_test, sd_reference): +def spectral_similarity_index( + sd_test: SpectralDistribution, sd_reference: SpectralDistribution +) -> NDArray: """ - Returns the *Academy Spectral Similarity Index* (SSI) of given test + Return the *Academy Spectral Similarity Index* (SSI) of given test spectral distribution with given reference spectral distribution. Parameters ---------- - sd_test : SpectralDistribution + sd_test Test spectral distribution. - sd_reference : SpectralDistribution + sd_reference Reference spectral distribution. Returns ------- - numeric + :class:`numpy.ndarray` *Academy Spectral Similarity Index* (SSI). References @@ -77,26 +78,27 @@ def spectral_similarity_index(sd_test, sd_reference): global _MATRIX_INTEGRATION if _MATRIX_INTEGRATION is None: - _MATRIX_INTEGRATION = zeros([ - len(_SPECTRAL_SHAPE_SSI_LARGE.range()), - len(SPECTRAL_SHAPE_SSI.range()) - ]) + _MATRIX_INTEGRATION = zeros( + ( + len(_SPECTRAL_SHAPE_SSI_LARGE.range()), + len(SPECTRAL_SHAPE_SSI.range()), + ) + ) weights = np.array([0.5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.5]) for i in range(_MATRIX_INTEGRATION.shape[0]): - _MATRIX_INTEGRATION[i, (10 * i):(10 * i + 11)] = weights + _MATRIX_INTEGRATION[i, (10 * i) : (10 * i + 11)] = weights settings = { - 'interpolator': LinearInterpolator, - 'extrapolator_kwargs': { - 'left': 0, - 'right': 0 - } + "interpolator": LinearInterpolator, + "extrapolator_kwargs": {"left": 0, "right": 0}, } - sd_test = sd_test.copy().align(SPECTRAL_SHAPE_SSI, **settings) - sd_reference = sd_reference.copy().align(SPECTRAL_SHAPE_SSI, **settings) + sd_test = reshape_sd(sd_test, SPECTRAL_SHAPE_SSI, "Align", **settings) + sd_reference = reshape_sd( + sd_reference, SPECTRAL_SHAPE_SSI, "Align", **settings + ) test_i = np.dot(_MATRIX_INTEGRATION, sd_test.values) reference_i = np.dot(_MATRIX_INTEGRATION, sd_reference.values) @@ -107,11 +109,39 @@ def spectral_similarity_index(sd_test, sd_reference): d_i = test_i - reference_i dr_i = d_i / (reference_i + np.mean(reference_i)) wdr_i = dr_i * [ - 12 / 45, 22 / 45, 32 / 45, 40 / 45, 44 / 45, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 11 / 15, 3 / 15 + 12 / 45, + 22 / 45, + 32 / 45, + 40 / 45, + 44 / 45, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 11 / 15, + 3 / 15, ] c_wdr_i = convolve1d(np.hstack([0, wdr_i, 0]), [0.22, 0.56, 0.22]) - m_v = np.sum(c_wdr_i ** 2) + m_v = np.sum(c_wdr_i**2) SSI = np.around(100 - 32 * np.sqrt(m_v)) diff --git a/colour/quality/tests/__init__.py b/colour/quality/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/quality/tests/__init__.py +++ b/colour/quality/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/quality/tests/test_cfi2017.py b/colour/quality/tests/test_cfi2017.py index ade4582ee7..5181c115f4 100644 --- a/colour/quality/tests/test_cfi2017.py +++ b/colour/quality/tests/test_cfi2017.py @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.quality.CIE2017` module. +Defines the unit tests for the :mod:`colour.quality.CIE2017` module. Notes ----- @@ -8,33 +7,44 @@ by the CIE at this URL: http://files.cie.co.at/933_TC1-90.zip. """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -import six import unittest -from colour.colorimetry import (SpectralShape, SpectralDistribution, - sd_blackbody, SDS_ILLUMINANTS) -from colour.quality.cfi2017 import (CCT_reference_illuminant, - sd_reference_illuminant, - colour_fidelity_index_CIE2017) +from colour.colorimetry import ( + SDS_ILLUMINANTS, + SpectralShape, + SpectralDistribution, + reshape_sd, + sd_blackbody, +) +from colour.hints import Dict +from colour.quality.cfi2017 import ( + CCT_reference_illuminant, + sd_reference_illuminant, + colour_fidelity_index_CIE2017, +) from colour.utilities import ColourUsageWarning -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'DATA_SD_SAMPLE_5NM', 'SD_SAMPLE_5NM', 'DATA_SD_SAMPLE_1NM', - 'SD_SAMPLE_1NM', 'TestColourFidelityIndexCIE2017', - 'TestCctReferenceIlluminant', 'TestSdReferenceIlluminant' + "DATA_SD_SAMPLE_5NM", + "SD_SAMPLE_5NM", + "DATA_SD_SAMPLE_1NM", + "SD_SAMPLE_1NM", + "TestColourFidelityIndexCIE2017", + "TestCctReferenceIlluminant", + "TestSdReferenceIlluminant", ] -DATA_SD_SAMPLE_5NM = { +DATA_SD_SAMPLE_5NM: Dict = { 380: 0.000, 385: 0.000, 390: 0.001, @@ -115,12 +125,12 @@ 765: 0.040, 770: 0.035, 775: 0.031, - 780: 0.027 + 780: 0.027, } -SD_SAMPLE_5NM = SpectralDistribution(DATA_SD_SAMPLE_5NM) +SD_SAMPLE_5NM: SpectralDistribution = SpectralDistribution(DATA_SD_SAMPLE_5NM) -DATA_SD_SAMPLE_1NM = { +DATA_SD_SAMPLE_1NM: Dict = { 380: 0.000, 381: 0.000, 382: 0.000, @@ -521,96 +531,377 @@ 777: 0.029, 778: 0.028, 779: 0.028, - 780: 0.027 + 780: 0.027, } -SD_SAMPLE_1NM = SpectralDistribution(DATA_SD_SAMPLE_1NM) +SD_SAMPLE_1NM: SpectralDistribution = SpectralDistribution(DATA_SD_SAMPLE_1NM) class TestColourFidelityIndexCIE2017(unittest.TestCase): """ - Defines :func:`colour.quality.CIE2017.colour_fidelity_index_CIE2017` + Define :func:`colour.quality.CIE2017.colour_fidelity_index_CIE2017` definition unit tests methods. """ def test_colour_fidelity_index_CIE2017(self): """ - Tests :func:`colour.quality.CIE2017.colour_fidelity_index_CIE2017` + Test :func:`colour.quality.CIE2017.colour_fidelity_index_CIE2017` definition. """ for sd in [SD_SAMPLE_5NM, SD_SAMPLE_1NM]: specification = colour_fidelity_index_CIE2017( - sd, additional_data=True) + sd, additional_data=True + ) np.testing.assert_almost_equal(specification.R_f, 81.6, 1) - np.testing.assert_almost_equal(specification.R_s, [ - 89.5, 80.5, 81.5, 79.4, 65.9, 79.4, 73.2, 68.5, 95.9, 76.3, - 71.9, 71.8, 83.3, 93.0, 89.2, 72.9, 75.1, 85.8, 75.1, 63.4, - 69.4, 71.4, 89.7, 76.8, 67.6, 75.5, 92.7, 87.7, 81.1, 95.0, - 83.3, 74.4, 90.4, 80.4, 89.0, 86.9, 85.0, 95.7, 98.5, 96.3, - 98.7, 88.4, 85.2, 99.6, 90.4, 88.6, 94.3, 85.3, 86.4, 90.0, - 89.3, 88.0, 83.6, 89.6, 86.7, 81.4, 80.2, 80.6, 88.5, 89.7, - 84.2, 84.2, 79.4, 71.3, 72.8, 65.8, 64.1, 71.7, 77.4, 68.0, - 63.2, 87.1, 62.4, 92.7, 67.3, 67.6, 80.0, 70.4, 89.0, 87.0, - 81.5, 94.2, 94.3, 89.4, 79.3, 76.6, 83.7, 87.7, 76.7, 88.6, - 76.2, 68.5, 80.1, 65.3, 74.9, 83.9, 88.6, 84.2, 77.4 - ], 1) + np.testing.assert_almost_equal( + specification.R_s, + [ + 89.5, + 80.5, + 81.5, + 79.4, + 65.9, + 79.4, + 73.2, + 68.5, + 95.9, + 76.3, + 71.9, + 71.8, + 83.3, + 93.0, + 89.2, + 72.9, + 75.1, + 85.8, + 75.1, + 63.4, + 69.4, + 71.4, + 89.7, + 76.8, + 67.6, + 75.5, + 92.7, + 87.7, + 81.1, + 95.0, + 83.3, + 74.4, + 90.4, + 80.4, + 89.0, + 86.9, + 85.0, + 95.7, + 98.5, + 96.3, + 98.7, + 88.4, + 85.2, + 99.6, + 90.4, + 88.6, + 94.3, + 85.3, + 86.4, + 90.0, + 89.3, + 88.0, + 83.6, + 89.6, + 86.7, + 81.4, + 80.2, + 80.6, + 88.5, + 89.7, + 84.2, + 84.2, + 79.4, + 71.3, + 72.8, + 65.8, + 64.1, + 71.7, + 77.4, + 68.0, + 63.2, + 87.1, + 62.4, + 92.7, + 67.3, + 67.6, + 80.0, + 70.4, + 89.0, + 87.0, + 81.5, + 94.2, + 94.3, + 89.4, + 79.3, + 76.6, + 83.7, + 87.7, + 76.7, + 88.6, + 76.2, + 68.5, + 80.1, + 65.3, + 74.9, + 83.9, + 88.6, + 84.2, + 77.4, + ], + 1, + ) specification = colour_fidelity_index_CIE2017( - SDS_ILLUMINANTS['FL1'], additional_data=True) + SDS_ILLUMINANTS["FL1"], additional_data=True + ) np.testing.assert_almost_equal(specification.R_f, 80.6, 1) - np.testing.assert_almost_equal(specification.R_s, [ - 85.1, 68.9, 73.9, 79.7, 51.6, 77.8, 52.1, 47.8, 95.3, 68.9, 67.3, - 63.6, 71.3, 91.1, 79.0, 63.2, 72.8, 78.4, 75.2, 60.4, 68.0, 67.3, - 88.6, 78.4, 68.7, 75.7, 91.0, 91.5, 78.3, 83.0, 82.3, 78.7, 85.8, - 85.6, 92.3, 94.6, 88.3, 87.8, 97.4, 94.4, 95.4, 93.3, 90.5, 99.5, - 88.9, 87.6, 94.3, 77.7, 88.0, 89.6, 91.0, 87.3, 81.3, 83.8, 85.2, - 78.1, 78.2, 79.5, 86.9, 94.4, 87.4, 93.7, 88.6, 77.9, 74.5, 78.2, - 77.2, 79.7, 86.3, 76.3, 81.6, 91.0, 73.3, 98.1, 81.9, 77.4, 86.9, - 79.4, 90.2, 91.1, 80.6, 96.6, 95.1, 89.3, 84.2, 72.5, 78.6, 75.5, - 74.4, 75.3, 91.4, 58.2, 74.6, 52.6, 67.0, 76.2, 88.9, 75.2, 55.5 - ], 1) + np.testing.assert_almost_equal( + specification.R_s, + [ + 85.1, + 68.9, + 73.9, + 79.7, + 51.6, + 77.8, + 52.1, + 47.8, + 95.3, + 68.9, + 67.3, + 63.6, + 71.3, + 91.1, + 79.0, + 63.2, + 72.8, + 78.4, + 75.2, + 60.4, + 68.0, + 67.3, + 88.6, + 78.4, + 68.7, + 75.7, + 91.0, + 91.5, + 78.3, + 83.0, + 82.3, + 78.7, + 85.8, + 85.6, + 92.3, + 94.6, + 88.3, + 87.8, + 97.4, + 94.4, + 95.4, + 93.3, + 90.5, + 99.5, + 88.9, + 87.6, + 94.3, + 77.7, + 88.0, + 89.6, + 91.0, + 87.3, + 81.3, + 83.8, + 85.2, + 78.1, + 78.2, + 79.5, + 86.9, + 94.4, + 87.4, + 93.7, + 88.6, + 77.9, + 74.5, + 78.2, + 77.2, + 79.7, + 86.3, + 76.3, + 81.6, + 91.0, + 73.3, + 98.1, + 81.9, + 77.4, + 86.9, + 79.4, + 90.2, + 91.1, + 80.6, + 96.6, + 95.1, + 89.3, + 84.2, + 72.5, + 78.6, + 75.5, + 74.4, + 75.3, + 91.4, + 58.2, + 74.6, + 52.6, + 67.0, + 76.2, + 88.9, + 75.2, + 55.5, + ], + 1, + ) specification = colour_fidelity_index_CIE2017( - SDS_ILLUMINANTS['FL2'], additional_data=True) + SDS_ILLUMINANTS["FL2"], additional_data=True + ) np.testing.assert_almost_equal(specification.R_f, 70.1, 1) - np.testing.assert_almost_equal(specification.R_s, [ - 78.9, 59.0, 66.9, 65.7, 35.8, 66.1, 40.4, 34.7, 95.1, 53.5, 47.4, - 44.6, 64.1, 86.6, 71.6, 48.8, 56.1, 68.9, 56.8, 43.9, 46.9, 46.5, - 80.0, 62.6, 48.1, 58.4, 82.0, 84.6, 61.5, 69.6, 67.5, 62.3, 73.9, - 73.6, 85.9, 87.5, 79.4, 76.0, 96.6, 92.8, 90.5, 89.1, 83.0, 99.4, - 83.1, 80.7, 86.8, 66.1, 79.6, 80.7, 81.3, 76.1, 68.7, 76.8, 77.0, - 66.1, 65.5, 67.4, 78.8, 90.1, 77.5, 86.9, 76.8, 59.7, 61.2, 57.9, - 56.2, 62.0, 72.9, 57.7, 63.7, 84.0, 52.7, 96.2, 66.6, 56.6, 76.2, - 63.3, 81.8, 84.5, 73.5, 93.9, 90.9, 85.7, 80.5, 63.5, 73.7, 69.0, - 66.1, 67.5, 92.6, 51.3, 69.5, 40.7, 61.5, 70.2, 80.0, 67.0, 45.0 - ], 1) + np.testing.assert_almost_equal( + specification.R_s, + [ + 78.9, + 59.0, + 66.9, + 65.7, + 35.8, + 66.1, + 40.4, + 34.7, + 95.1, + 53.5, + 47.4, + 44.6, + 64.1, + 86.6, + 71.6, + 48.8, + 56.1, + 68.9, + 56.8, + 43.9, + 46.9, + 46.5, + 80.0, + 62.6, + 48.1, + 58.4, + 82.0, + 84.6, + 61.5, + 69.6, + 67.5, + 62.3, + 73.9, + 73.6, + 85.9, + 87.5, + 79.4, + 76.0, + 96.6, + 92.8, + 90.5, + 89.1, + 83.0, + 99.4, + 83.1, + 80.7, + 86.8, + 66.1, + 79.6, + 80.7, + 81.3, + 76.1, + 68.7, + 76.8, + 77.0, + 66.1, + 65.5, + 67.4, + 78.8, + 90.1, + 77.5, + 86.9, + 76.8, + 59.7, + 61.2, + 57.9, + 56.2, + 62.0, + 72.9, + 57.7, + 63.7, + 84.0, + 52.7, + 96.2, + 66.6, + 56.6, + 76.2, + 63.3, + 81.8, + 84.5, + 73.5, + 93.9, + 90.9, + 85.7, + 80.5, + 63.5, + 73.7, + 69.0, + 66.1, + 67.5, + 92.6, + 51.3, + 69.5, + 40.7, + 61.5, + 70.2, + 80.0, + 67.0, + 45.0, + ], + 1, + ) def test_raise_exception_colour_fidelity_index_CFI2017(self): """ - Tests :func:`colour.quality.CIE2017.colour_fidelity_index_CFI2017` + Test :func:`colour.quality.CIE2017.colour_fidelity_index_CFI2017` definition raised exception. """ - if six.PY3: # pragma: no cover - sd = SDS_ILLUMINANTS['FL2'].copy().align( - SpectralShape(400, 700, 5)) - self.assertWarns(ColourUsageWarning, colour_fidelity_index_CIE2017, - sd) + sd = reshape_sd(SDS_ILLUMINANTS["FL2"], SpectralShape(400, 700, 5)) + self.assertWarns(ColourUsageWarning, colour_fidelity_index_CIE2017, sd) - sd = SDS_ILLUMINANTS['FL2'].copy().align(SpectralShape(380, 780, 10)) + sd = reshape_sd(SDS_ILLUMINANTS["FL2"], SpectralShape(380, 780, 10)) self.assertRaises(ValueError, colour_fidelity_index_CIE2017, sd) class TestCctReferenceIlluminant(unittest.TestCase): """ - Defines :func:`colour.quality.CIE2017.CCT_reference_illuminant` + Define :func:`colour.quality.CIE2017.CCT_reference_illuminant` definition unit tests methods. """ def test_CCT_reference_illuminant(self): """ - Tests :func:`colour.quality.CIE2017.CCT_reference_illuminant` + Test :func:`colour.quality.CIE2017.CCT_reference_illuminant` definition. """ @@ -622,13 +913,13 @@ def test_CCT_reference_illuminant(self): class TestSdReferenceIlluminant(unittest.TestCase): """ - Defines :func:`colour.quality.CIE2017.sd_reference_illuminant` + Define :func:`colour.quality.CIE2017.sd_reference_illuminant` definition unit tests methods. """ def test_sd_reference_illuminant(self): """ - Tests :func:`colour.quality.CIE2017.sd_reference_illuminant` + Test :func:`colour.quality.CIE2017.sd_reference_illuminant` definition. """ @@ -642,8 +933,9 @@ def test_sd_reference_illuminant(self): np.testing.assert_allclose( sd_reference.values, sd_blackbody(3288, shape).values, - rtol=0.005) + rtol=0.005, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/quality/tests/test_cqs.py b/colour/quality/tests/test_cqs.py index 322b30f914..23c046b8d1 100644 --- a/colour/quality/tests/test_cqs.py +++ b/colour/quality/tests/test_cqs.py @@ -1,381 +1,435 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.quality.cqs` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.quality.cqs` module.""" import numpy as np import unittest -from colour.quality import (ColourRendering_Specification_CQS, - colour_quality_scale) +from colour.quality import ( + ColourRendering_Specification_CQS, + colour_quality_scale, +) from colour.colorimetry import SDS_ILLUMINANTS, SDS_LIGHT_SOURCES from colour.quality.cqs import VS_ColorimetryData, VS_ColourQualityScaleData -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestColourQualityScale'] +__all__ = [ + "TestColourQualityScale", +] class TestColourQualityScale(unittest.TestCase): """ - Defines :func:`colour.quality.cqs.colour_quality_scale` definition unit + Define :func:`colour.quality.cqs.colour_quality_scale` definition unit tests methods. """ def test_colour_quality_scale(self): - """ - Tests :func:`colour.quality.cqs.colour_quality_scale` definition. - """ + """Test :func:`colour.quality.cqs.colour_quality_scale` definition.""" self.assertAlmostEqual( - colour_quality_scale(SDS_ILLUMINANTS['FL1']), + colour_quality_scale(SDS_ILLUMINANTS["FL1"]), 74.982585798279871, - places=7) + places=7, + ) self.assertAlmostEqual( colour_quality_scale( - SDS_ILLUMINANTS['FL1'], method='NIST CQS 7.4'), + SDS_ILLUMINANTS["FL1"], method="NIST CQS 7.4" + ), 75.377089740493290, - places=7) + places=7, + ) self.assertAlmostEqual( - colour_quality_scale(SDS_ILLUMINANTS['FL2']), + colour_quality_scale(SDS_ILLUMINANTS["FL2"]), 64.111703163816699, - places=7) + places=7, + ) self.assertAlmostEqual( colour_quality_scale( - SDS_ILLUMINANTS['FL2'], method='NIST CQS 7.4'), + SDS_ILLUMINANTS["FL2"], method="NIST CQS 7.4" + ), 64.774490832419872, - places=7) + places=7, + ) self.assertAlmostEqual( - colour_quality_scale(SDS_LIGHT_SOURCES['Neodimium Incandescent']), + colour_quality_scale(SDS_LIGHT_SOURCES["Neodimium Incandescent"]), 89.737441458687044, - places=7) + places=7, + ) self.assertAlmostEqual( colour_quality_scale( - SDS_LIGHT_SOURCES['Neodimium Incandescent'], - method='NIST CQS 7.4'), + SDS_LIGHT_SOURCES["Neodimium Incandescent"], + method="NIST CQS 7.4", + ), 87.700319996664561, - places=7) + places=7, + ) self.assertAlmostEqual( colour_quality_scale( - SDS_LIGHT_SOURCES['F32T8/TL841 (Triphosphor)']), + SDS_LIGHT_SOURCES["F32T8/TL841 (Triphosphor)"] + ), 84.934929181986888, - places=7) + places=7, + ) self.assertAlmostEqual( colour_quality_scale( - SDS_LIGHT_SOURCES['F32T8/TL841 (Triphosphor)'], - method='NIST CQS 7.4'), + SDS_LIGHT_SOURCES["F32T8/TL841 (Triphosphor)"], + method="NIST CQS 7.4", + ), 83.255458192000233, - places=7) + places=7, + ) self.assertAlmostEqual( - colour_quality_scale(SDS_LIGHT_SOURCES['H38HT-100 (Mercury)']), + colour_quality_scale(SDS_LIGHT_SOURCES["H38HT-100 (Mercury)"]), 20.019979778489535, - places=7) + places=7, + ) self.assertAlmostEqual( colour_quality_scale( - SDS_LIGHT_SOURCES['H38HT-100 (Mercury)'], - method='NIST CQS 7.4'), + SDS_LIGHT_SOURCES["H38HT-100 (Mercury)"], method="NIST CQS 7.4" + ), 23.011011107054145, - places=7) + places=7, + ) self.assertAlmostEqual( - colour_quality_scale(SDS_LIGHT_SOURCES['Luxeon WW 2880']), + colour_quality_scale(SDS_LIGHT_SOURCES["Luxeon WW 2880"]), 86.497986329513722, - places=7) + places=7, + ) self.assertAlmostEqual( colour_quality_scale( - SDS_LIGHT_SOURCES['Luxeon WW 2880'], method='NIST CQS 7.4'), + SDS_LIGHT_SOURCES["Luxeon WW 2880"], method="NIST CQS 7.4" + ), 84.887918431764191, - places=7) + places=7, + ) specification_r = ColourRendering_Specification_CQS( - name='FL1', + name="FL1", Q_a=75.37708974049329, Q_f=76.387614864939863, Q_p=74.266319651754912, Q_g=84.236404852169287, Q_d=84.137299859998564, Q_as={ - 1: - VS_ColourQualityScaleData( - name='VS1', - Q_a=77.696843895396597, - D_C_ab=-1.4488382159802313, - D_E_ab=7.1866560767284948, - D_Ep_ab=7.1866560767284948), - 2: - VS_ColourQualityScaleData( - name='VS2', - Q_a=98.458786425718415, - D_C_ab=2.3828597530721822, - D_E_ab=2.4340762421637514, - D_Ep_ab=0.49669563100030184), - 3: - VS_ColourQualityScaleData( - name='VS3', - Q_a=85.114145145039103, - D_C_ab=2.0800948785243278, - D_E_ab=5.2279782355370283, - D_Ep_ab=4.7963487912771443), - 4: - VS_ColourQualityScaleData( - name='VS4', - Q_a=75.996470841598779, - D_C_ab=-1.5405082308077453, - D_E_ab=7.7347089860761855, - D_Ep_ab=7.7347089860761855), - 5: - VS_ColourQualityScaleData( - name='VS5', - Q_a=76.592736502399703, - D_C_ab=-5.9501334881332397, - D_E_ab=7.5425196871488174, - D_Ep_ab=7.5425196871488174), - 6: - VS_ColourQualityScaleData( - name='VS6', - Q_a=75.700247357264502, - D_C_ab=-7.777714756522542, - D_E_ab=7.8301903253053968, - D_Ep_ab=7.8301903253053968), - 7: - VS_ColourQualityScaleData( - name='VS7', - Q_a=74.888666225318985, - D_C_ab=-6.7678158335932963, - D_E_ab=8.0917938479197353, - D_Ep_ab=8.0917938479197353), - 8: - VS_ColourQualityScaleData( - name='VS8', - Q_a=85.018563761368782, - D_C_ab=-0.69329167385927803, - D_E_ab=4.8271479880620198, - D_Ep_ab=4.8271479880620198), - 9: - VS_ColourQualityScaleData( - name='VS9', - Q_a=95.598698383415993, - D_C_ab=3.2171709711080183, - D_E_ab=3.5158784941231844, - D_Ep_ab=1.4181722490931066), - 10: - VS_ColourQualityScaleData( - name='VS10', - Q_a=84.964267430208068, - D_C_ab=3.2356461456271859, - D_E_ab=5.8258030130410683, - D_Ep_ab=4.8446439257231608), - 11: - VS_ColourQualityScaleData( - name='VS11', - Q_a=76.736800445076625, - D_C_ab=0.87588476209290889, - D_E_ab=7.5470837614115442, - D_Ep_ab=7.496085590846417), - 12: - VS_ColourQualityScaleData( - name='VS12', - Q_a=74.434858693757448, - D_C_ab=-1.9423783441654692, - D_E_ab=8.238078426246771, - D_Ep_ab=8.238078426246771), - 13: - VS_ColourQualityScaleData( - name='VS13', - Q_a=71.82010326219509, - D_C_ab=-5.7426330500006273, - D_E_ab=9.081024809959402, - D_Ep_ab=9.081024809959402), - 14: - VS_ColourQualityScaleData( - name='VS14', - Q_a=53.401625187360011, - D_C_ab=-14.617738406753311, - D_E_ab=15.027848279487852, - D_Ep_ab=15.027848279487852), - 15: - VS_ColourQualityScaleData( - name='VS15', - Q_a=62.672288506573636, - D_C_ab=-10.059255730250122, - D_E_ab=12.031799070041069, - D_Ep_ab=12.031799070041069) + 1: VS_ColourQualityScaleData( + name="VS1", + Q_a=77.696843895396597, + D_C_ab=-1.4488382159802313, + D_E_ab=7.1866560767284948, + D_Ep_ab=7.1866560767284948, + ), + 2: VS_ColourQualityScaleData( + name="VS2", + Q_a=98.458786425718415, + D_C_ab=2.3828597530721822, + D_E_ab=2.4340762421637514, + D_Ep_ab=0.49669563100030184, + ), + 3: VS_ColourQualityScaleData( + name="VS3", + Q_a=85.114145145039103, + D_C_ab=2.0800948785243278, + D_E_ab=5.2279782355370283, + D_Ep_ab=4.7963487912771443, + ), + 4: VS_ColourQualityScaleData( + name="VS4", + Q_a=75.996470841598779, + D_C_ab=-1.5405082308077453, + D_E_ab=7.7347089860761855, + D_Ep_ab=7.7347089860761855, + ), + 5: VS_ColourQualityScaleData( + name="VS5", + Q_a=76.592736502399703, + D_C_ab=-5.9501334881332397, + D_E_ab=7.5425196871488174, + D_Ep_ab=7.5425196871488174, + ), + 6: VS_ColourQualityScaleData( + name="VS6", + Q_a=75.700247357264502, + D_C_ab=-7.777714756522542, + D_E_ab=7.8301903253053968, + D_Ep_ab=7.8301903253053968, + ), + 7: VS_ColourQualityScaleData( + name="VS7", + Q_a=74.888666225318985, + D_C_ab=-6.7678158335932963, + D_E_ab=8.0917938479197353, + D_Ep_ab=8.0917938479197353, + ), + 8: VS_ColourQualityScaleData( + name="VS8", + Q_a=85.018563761368782, + D_C_ab=-0.69329167385927803, + D_E_ab=4.8271479880620198, + D_Ep_ab=4.8271479880620198, + ), + 9: VS_ColourQualityScaleData( + name="VS9", + Q_a=95.598698383415993, + D_C_ab=3.2171709711080183, + D_E_ab=3.5158784941231844, + D_Ep_ab=1.4181722490931066, + ), + 10: VS_ColourQualityScaleData( + name="VS10", + Q_a=84.964267430208068, + D_C_ab=3.2356461456271859, + D_E_ab=5.8258030130410683, + D_Ep_ab=4.8446439257231608, + ), + 11: VS_ColourQualityScaleData( + name="VS11", + Q_a=76.736800445076625, + D_C_ab=0.87588476209290889, + D_E_ab=7.5470837614115442, + D_Ep_ab=7.496085590846417, + ), + 12: VS_ColourQualityScaleData( + name="VS12", + Q_a=74.434858693757448, + D_C_ab=-1.9423783441654692, + D_E_ab=8.238078426246771, + D_Ep_ab=8.238078426246771, + ), + 13: VS_ColourQualityScaleData( + name="VS13", + Q_a=71.82010326219509, + D_C_ab=-5.7426330500006273, + D_E_ab=9.081024809959402, + D_Ep_ab=9.081024809959402, + ), + 14: VS_ColourQualityScaleData( + name="VS14", + Q_a=53.401625187360011, + D_C_ab=-14.617738406753311, + D_E_ab=15.027848279487852, + D_Ep_ab=15.027848279487852, + ), + 15: VS_ColourQualityScaleData( + name="VS15", + Q_a=62.672288506573636, + D_C_ab=-10.059255730250122, + D_E_ab=12.031799070041069, + D_Ep_ab=12.031799070041069, + ), }, - colorimetry_data=([ - VS_ColorimetryData( - name='VS1', - XYZ=np.array([0.13183826, 0.09887241, 0.22510560]), - Lab=np.array([37.63929023, 27.59987425, -26.20530751]), - C=38.058786112947161), - VS_ColorimetryData( - name='VS2', - XYZ=np.array([0.13061818, 0.10336027, 0.30741529]), - Lab=np.array([38.43888224, 23.35252921, -37.81858787]), - C=44.447566961689617), - VS_ColorimetryData( - name='VS3', - XYZ=np.array([0.10089287, 0.09200351, 0.32265558]), - Lab=np.array([36.36721207, 11.00392324, -43.53144829]), - C=44.900705083686788), - VS_ColorimetryData( - name='VS4', - XYZ=np.array([0.13339390, 0.15696027, 0.38421714]), - Lab=np.array([46.57313273, -9.89411460, -33.95550516]), - C=35.367638235231176), - VS_ColorimetryData( - name='VS5', - XYZ=np.array([0.18662999, 0.24708620, 0.40043676]), - Lab=np.array([56.79040832, -23.15964295, -18.30798276]), - C=29.522047597875542), - VS_ColorimetryData( - name='VS6', - XYZ=np.array([0.15843362, 0.24157338, 0.26933196]), - Lab=np.array([56.24498076, -36.24891195, -1.43946286]), - C=36.277481597875926), - VS_ColorimetryData( - name='VS7', - XYZ=np.array([0.14991085, 0.24929718, 0.13823961]), - Lab=np.array([57.00687838, -44.55799945, 24.99093151]), - C=51.087786926248803), - VS_ColorimetryData( - name='VS8', - XYZ=np.array([0.26141761, 0.36817692, 0.11429088]), - Lab=np.array([67.14003019, -33.22377274, 48.66064659]), - C=58.920943658800866), - VS_ColorimetryData( - name='VS9', - XYZ=np.array([0.42410903, 0.52851922, 0.11439812]), - Lab=np.array([77.78749106, -22.21024952, 66.98873308]), - C=70.5746806093467), - VS_ColorimetryData( - name='VS10', - XYZ=np.array([0.55367933, 0.62018757, 0.09672217]), - Lab=np.array([82.92339395, -8.84301088, 80.99721844]), - C=81.478513954529475), - VS_ColorimetryData( - name='VS11', - XYZ=np.array([0.39755898, 0.39521027, 0.05739407]), - Lab=np.array([69.12701310, 6.97471851, 71.51095397]), - C=71.850283480415115), - VS_ColorimetryData( - name='VS12', - XYZ=np.array([0.43757530, 0.38969458, 0.08630191]), - Lab=np.array([68.72913590, 20.83589132, 59.86354051]), - C=63.385943630694335), - VS_ColorimetryData( - name='VS13', - XYZ=np.array([0.34657727, 0.27547744, 0.08900676]), - Lab=np.array([59.47793328, 31.84647528, 43.02166812]), - C=53.526273138196274), - VS_ColorimetryData( - name='VS14', - XYZ=np.array([0.14271714, 0.09107438, 0.04949461]), - Lab=np.array([36.19033157, 40.77665898, 18.34813575]), - C=44.714539058373845), - VS_ColorimetryData( - name='VS15', - XYZ=np.array([0.13593948, 0.09214669, 0.11591665]), - Lab=np.array([36.39436371, 35.62220213, -4.79596673]), - C=35.94360278722214) - ], [ - VS_ColorimetryData( - name='VS1', - XYZ=np.array([0.15205130, 0.10842697, 0.21629425]), - Lab=np.array([39.31425803, 32.98285941, -21.74818073]), - C=39.507624328927392), - VS_ColorimetryData( - name='VS2', - XYZ=np.array([0.13187179, 0.10619377, 0.29945481]), - Lab=np.array([38.93186373, 22.05038057, -35.82206456]), - C=42.064707208617435), - VS_ColorimetryData( - name='VS3', - XYZ=np.array([0.10123263, 0.09853741, 0.32956604]), - Lab=np.array([37.57864252, 6.04766746, -42.39139508]), - C=42.82061020516246), - VS_ColorimetryData( - name='VS4', - XYZ=np.array([0.13144454, 0.16803553, 0.39315864]), - Lab=np.array([48.01155296, -17.36604069, -32.56734417]), - C=36.908146466038922), - VS_ColorimetryData( - name='VS5', - XYZ=np.array([0.18145723, 0.25845953, 0.41319313]), - Lab=np.array([57.89053983, -30.61152779, -17.92233237]), - C=35.472181086008781), - VS_ColorimetryData( - name='VS6', - XYZ=np.array([0.15184114, 0.25076481, 0.28160235]), - Lab=np.array([57.14986362, -44.01984887, -1.76443512]), - C=44.055196354398468), - VS_ColorimetryData( - name='VS7', - XYZ=np.array([0.13956282, 0.25328776, 0.14470413]), - Lab=np.array([57.39436644, -52.59240053, 24.11037488]), - C=57.8556027598421), - VS_ColorimetryData( - name='VS8', - XYZ=np.array([0.24672357, 0.36210726, 0.11976641]), - Lab=np.array([66.68062145, -37.45331629, 46.38001890]), - C=59.614235332660144), - VS_ColorimetryData( - name='VS9', - XYZ=np.array([0.40820163, 0.50861708, 0.11894288]), - Lab=np.array([76.59516365, -21.90847578, 63.69499819]), - C=67.357509638238682), - VS_ColorimetryData( - name='VS10', - XYZ=np.array([0.56036726, 0.60569219, 0.10169199]), - Lab=np.array([82.14661230, -3.82032735, 78.14954550]), - C=78.24286780890229), - VS_ColorimetryData( - name='VS11', - XYZ=np.array([0.40540651, 0.38003446, 0.05753983]), - Lab=np.array([68.02315451, 14.17690044, 69.54409225]), - C=70.974398718322206), - VS_ColorimetryData( - name='VS12', - XYZ=np.array([0.45407809, 0.37920609, 0.08621297]), - Lab=np.array([67.96206127, 28.93563884, 58.57062794]), - C=65.328321974859804), - VS_ColorimetryData( - name='VS13', - XYZ=np.array([0.37207030, 0.27413935, 0.08882217]), - Lab=np.array([59.35552778, 40.92542311, 42.87088737]), - C=59.268906188196901), - VS_ColorimetryData( - name='VS14', - XYZ=np.array([0.19307398, 0.11049957, 0.04883445]), - Lab=np.array([39.66448332, 53.96576475, 24.65796798]), - C=59.332277465127156), - VS_ColorimetryData( - name='VS15', - XYZ=np.array([0.17306027, 0.10700056, 0.11280793]), - Lab=np.array([39.07062485, 45.99788526, 0.67641984]), - C=46.002858517472262) - ])) + colorimetry_data=( + [ + VS_ColorimetryData( + name="VS1", + XYZ=np.array([0.13183826, 0.09887241, 0.22510560]), + Lab=np.array([37.63929023, 27.59987425, -26.20530751]), + C=38.058786112947161, + ), + VS_ColorimetryData( + name="VS2", + XYZ=np.array([0.13061818, 0.10336027, 0.30741529]), + Lab=np.array([38.43888224, 23.35252921, -37.81858787]), + C=44.447566961689617, + ), + VS_ColorimetryData( + name="VS3", + XYZ=np.array([0.10089287, 0.09200351, 0.32265558]), + Lab=np.array([36.36721207, 11.00392324, -43.53144829]), + C=44.900705083686788, + ), + VS_ColorimetryData( + name="VS4", + XYZ=np.array([0.13339390, 0.15696027, 0.38421714]), + Lab=np.array([46.57313273, -9.89411460, -33.95550516]), + C=35.367638235231176, + ), + VS_ColorimetryData( + name="VS5", + XYZ=np.array([0.18662999, 0.24708620, 0.40043676]), + Lab=np.array( + [56.79040832, -23.15964295, -18.30798276] + ), + C=29.522047597875542, + ), + VS_ColorimetryData( + name="VS6", + XYZ=np.array([0.15843362, 0.24157338, 0.26933196]), + Lab=np.array([56.24498076, -36.24891195, -1.43946286]), + C=36.277481597875926, + ), + VS_ColorimetryData( + name="VS7", + XYZ=np.array([0.14991085, 0.24929718, 0.13823961]), + Lab=np.array([57.00687838, -44.55799945, 24.99093151]), + C=51.087786926248803, + ), + VS_ColorimetryData( + name="VS8", + XYZ=np.array([0.26141761, 0.36817692, 0.11429088]), + Lab=np.array([67.14003019, -33.22377274, 48.66064659]), + C=58.920943658800866, + ), + VS_ColorimetryData( + name="VS9", + XYZ=np.array([0.42410903, 0.52851922, 0.11439812]), + Lab=np.array([77.78749106, -22.21024952, 66.98873308]), + C=70.5746806093467, + ), + VS_ColorimetryData( + name="VS10", + XYZ=np.array([0.55367933, 0.62018757, 0.09672217]), + Lab=np.array([82.92339395, -8.84301088, 80.99721844]), + C=81.478513954529475, + ), + VS_ColorimetryData( + name="VS11", + XYZ=np.array([0.39755898, 0.39521027, 0.05739407]), + Lab=np.array([69.12701310, 6.97471851, 71.51095397]), + C=71.850283480415115, + ), + VS_ColorimetryData( + name="VS12", + XYZ=np.array([0.43757530, 0.38969458, 0.08630191]), + Lab=np.array([68.72913590, 20.83589132, 59.86354051]), + C=63.385943630694335, + ), + VS_ColorimetryData( + name="VS13", + XYZ=np.array([0.34657727, 0.27547744, 0.08900676]), + Lab=np.array([59.47793328, 31.84647528, 43.02166812]), + C=53.526273138196274, + ), + VS_ColorimetryData( + name="VS14", + XYZ=np.array([0.14271714, 0.09107438, 0.04949461]), + Lab=np.array([36.19033157, 40.77665898, 18.34813575]), + C=44.714539058373845, + ), + VS_ColorimetryData( + name="VS15", + XYZ=np.array([0.13593948, 0.09214669, 0.11591665]), + Lab=np.array([36.39436371, 35.62220213, -4.79596673]), + C=35.94360278722214, + ), + ], + [ + VS_ColorimetryData( + name="VS1", + XYZ=np.array([0.15205130, 0.10842697, 0.21629425]), + Lab=np.array([39.31425803, 32.98285941, -21.74818073]), + C=39.507624328927392, + ), + VS_ColorimetryData( + name="VS2", + XYZ=np.array([0.13187179, 0.10619377, 0.29945481]), + Lab=np.array([38.93186373, 22.05038057, -35.82206456]), + C=42.064707208617435, + ), + VS_ColorimetryData( + name="VS3", + XYZ=np.array([0.10123263, 0.09853741, 0.32956604]), + Lab=np.array([37.57864252, 6.04766746, -42.39139508]), + C=42.82061020516246, + ), + VS_ColorimetryData( + name="VS4", + XYZ=np.array([0.13144454, 0.16803553, 0.39315864]), + Lab=np.array( + [48.01155296, -17.36604069, -32.56734417] + ), + C=36.908146466038922, + ), + VS_ColorimetryData( + name="VS5", + XYZ=np.array([0.18145723, 0.25845953, 0.41319313]), + Lab=np.array( + [57.89053983, -30.61152779, -17.92233237] + ), + C=35.472181086008781, + ), + VS_ColorimetryData( + name="VS6", + XYZ=np.array([0.15184114, 0.25076481, 0.28160235]), + Lab=np.array([57.14986362, -44.01984887, -1.76443512]), + C=44.055196354398468, + ), + VS_ColorimetryData( + name="VS7", + XYZ=np.array([0.13956282, 0.25328776, 0.14470413]), + Lab=np.array([57.39436644, -52.59240053, 24.11037488]), + C=57.8556027598421, + ), + VS_ColorimetryData( + name="VS8", + XYZ=np.array([0.24672357, 0.36210726, 0.11976641]), + Lab=np.array([66.68062145, -37.45331629, 46.38001890]), + C=59.614235332660144, + ), + VS_ColorimetryData( + name="VS9", + XYZ=np.array([0.40820163, 0.50861708, 0.11894288]), + Lab=np.array([76.59516365, -21.90847578, 63.69499819]), + C=67.357509638238682, + ), + VS_ColorimetryData( + name="VS10", + XYZ=np.array([0.56036726, 0.60569219, 0.10169199]), + Lab=np.array([82.14661230, -3.82032735, 78.14954550]), + C=78.24286780890229, + ), + VS_ColorimetryData( + name="VS11", + XYZ=np.array([0.40540651, 0.38003446, 0.05753983]), + Lab=np.array([68.02315451, 14.17690044, 69.54409225]), + C=70.974398718322206, + ), + VS_ColorimetryData( + name="VS12", + XYZ=np.array([0.45407809, 0.37920609, 0.08621297]), + Lab=np.array([67.96206127, 28.93563884, 58.57062794]), + C=65.328321974859804, + ), + VS_ColorimetryData( + name="VS13", + XYZ=np.array([0.37207030, 0.27413935, 0.08882217]), + Lab=np.array([59.35552778, 40.92542311, 42.87088737]), + C=59.268906188196901, + ), + VS_ColorimetryData( + name="VS14", + XYZ=np.array([0.19307398, 0.11049957, 0.04883445]), + Lab=np.array([39.66448332, 53.96576475, 24.65796798]), + C=59.332277465127156, + ), + VS_ColorimetryData( + name="VS15", + XYZ=np.array([0.17306027, 0.10700056, 0.11280793]), + Lab=np.array([39.07062485, 45.99788526, 0.67641984]), + C=46.002858517472262, + ), + ], + ), + ) specification_t = colour_quality_scale( - SDS_ILLUMINANTS['FL1'], - additional_data=True, - method='NIST CQS 7.4') + SDS_ILLUMINANTS["FL1"], additional_data=True, method="NIST CQS 7.4" + ) np.testing.assert_almost_equal( [ @@ -390,277 +444,316 @@ def test_colour_quality_scale(self): ) specification_r = ColourRendering_Specification_CQS( - name='FL1', + name="FL1", Q_a=74.982585798279871, Q_f=75.945236961962692, Q_p=None, Q_g=83.880315530074398, Q_d=None, Q_as={ - 1: - VS_ColourQualityScaleData( - name='VS1', - Q_a=51.966399639139652, - D_C_ab=-14.617735662894113, - D_E_ab=15.027845447317082, - D_Ep_ab=15.027845447317082), - 2: - VS_ColourQualityScaleData( - name='VS2', - Q_a=70.949015257799516, - D_C_ab=-5.742631494382664, - D_E_ab=9.0810254398122403, - D_Ep_ab=9.0810254398122403), - 3: - VS_ColourQualityScaleData( - name='VS3', - Q_a=74.662762947552991, - D_C_ab=-0.18830304572394141, - D_E_ab=7.9196747607137059, - D_Ep_ab=7.9196747607137059), - 4: - VS_ColourQualityScaleData( - name='VS4', - Q_a=85.532024779429392, - D_C_ab=2.6056579409845568, - D_E_ab=5.2188636120497893, - D_Ep_ab=4.5218452091774983), - 5: - VS_ColourQualityScaleData( - name='VS5', - Q_a=95.462563991193164, - D_C_ab=3.2171678090164875, - D_E_ab=3.5158755402235844, - D_Ep_ab=1.4181720992074747), - 6: - VS_ColourQualityScaleData( - name='VS6', - Q_a=84.55525422632698, - D_C_ab=-0.69329228720708613, - D_E_ab=4.8271478805630519, - D_Ep_ab=4.8271478805630519), - 7: - VS_ColourQualityScaleData( - name='VS7', - Q_a=74.112296060906786, - D_C_ab=-6.7678218514458237, - D_E_ab=8.0917968864976295, - D_Ep_ab=8.0917968864976295), - 8: - VS_ColourQualityScaleData( - name='VS8', - Q_a=74.948915115579581, - D_C_ab=-7.7777262876453435, - D_E_ab=7.8302017501424785, - D_Ep_ab=7.8302017501424785), - 9: - VS_ColourQualityScaleData( - name='VS9', - Q_a=75.868964954677168, - D_C_ab=-5.9501389861388283, - D_E_ab=7.5425333918134534, - D_Ep_ab=7.5425333918134534), - 10: - VS_ColourQualityScaleData( - name='VS10', - Q_a=75.254253773456256, - D_C_ab=-1.540504353515054, - D_E_ab=7.7347311479094465, - D_Ep_ab=7.7347311479094465), - 11: - VS_ColourQualityScaleData( - name='VS11', - Q_a=78.159233353416667, - D_C_ab=1.7422491866669034, - D_E_ab=7.0453200659888839, - D_Ep_ab=6.8265000259125559), - 12: - VS_ColourQualityScaleData( - name='VS12', - Q_a=92.007574903997778, - D_C_ab=2.8431539673587451, - D_E_ab=3.7846096930430031, - D_Ep_ab=2.4979483674742524), - 13: - VS_ColourQualityScaleData( - name='VS13', - Q_a=81.483789597909151, - D_C_ab=-0.0084301917293103656, - D_E_ab=5.7872196432106016, - D_Ep_ab=5.7872196432106016), - 14: - VS_ColourQualityScaleData( - name='VS14', - Q_a=76.536988871102693, - D_C_ab=-5.0697802563888956, - D_E_ab=7.3336734748351624, - D_Ep_ab=7.3336734748351624), - 15: - VS_ColourQualityScaleData( - name='VS15', - Q_a=65.429713405731931, - D_C_ab=-9.4962363392015661, - D_E_ab=10.807718438492838, - D_Ep_ab=10.807718438492838) + 1: VS_ColourQualityScaleData( + name="VS1", + Q_a=51.966399639139652, + D_C_ab=-14.617735662894113, + D_E_ab=15.027845447317082, + D_Ep_ab=15.027845447317082, + ), + 2: VS_ColourQualityScaleData( + name="VS2", + Q_a=70.949015257799516, + D_C_ab=-5.742631494382664, + D_E_ab=9.0810254398122403, + D_Ep_ab=9.0810254398122403, + ), + 3: VS_ColourQualityScaleData( + name="VS3", + Q_a=74.662762947552991, + D_C_ab=-0.18830304572394141, + D_E_ab=7.9196747607137059, + D_Ep_ab=7.9196747607137059, + ), + 4: VS_ColourQualityScaleData( + name="VS4", + Q_a=85.532024779429392, + D_C_ab=2.6056579409845568, + D_E_ab=5.2188636120497893, + D_Ep_ab=4.5218452091774983, + ), + 5: VS_ColourQualityScaleData( + name="VS5", + Q_a=95.462563991193164, + D_C_ab=3.2171678090164875, + D_E_ab=3.5158755402235844, + D_Ep_ab=1.4181720992074747, + ), + 6: VS_ColourQualityScaleData( + name="VS6", + Q_a=84.55525422632698, + D_C_ab=-0.69329228720708613, + D_E_ab=4.8271478805630519, + D_Ep_ab=4.8271478805630519, + ), + 7: VS_ColourQualityScaleData( + name="VS7", + Q_a=74.112296060906786, + D_C_ab=-6.7678218514458237, + D_E_ab=8.0917968864976295, + D_Ep_ab=8.0917968864976295, + ), + 8: VS_ColourQualityScaleData( + name="VS8", + Q_a=74.948915115579581, + D_C_ab=-7.7777262876453435, + D_E_ab=7.8302017501424785, + D_Ep_ab=7.8302017501424785, + ), + 9: VS_ColourQualityScaleData( + name="VS9", + Q_a=75.868964954677168, + D_C_ab=-5.9501389861388283, + D_E_ab=7.5425333918134534, + D_Ep_ab=7.5425333918134534, + ), + 10: VS_ColourQualityScaleData( + name="VS10", + Q_a=75.254253773456256, + D_C_ab=-1.540504353515054, + D_E_ab=7.7347311479094465, + D_Ep_ab=7.7347311479094465, + ), + 11: VS_ColourQualityScaleData( + name="VS11", + Q_a=78.159233353416667, + D_C_ab=1.7422491866669034, + D_E_ab=7.0453200659888839, + D_Ep_ab=6.8265000259125559, + ), + 12: VS_ColourQualityScaleData( + name="VS12", + Q_a=92.007574903997778, + D_C_ab=2.8431539673587451, + D_E_ab=3.7846096930430031, + D_Ep_ab=2.4979483674742524, + ), + 13: VS_ColourQualityScaleData( + name="VS13", + Q_a=81.483789597909151, + D_C_ab=-0.0084301917293103656, + D_E_ab=5.7872196432106016, + D_Ep_ab=5.7872196432106016, + ), + 14: VS_ColourQualityScaleData( + name="VS14", + Q_a=76.536988871102693, + D_C_ab=-5.0697802563888956, + D_E_ab=7.3336734748351624, + D_Ep_ab=7.3336734748351624, + ), + 15: VS_ColourQualityScaleData( + name="VS15", + Q_a=65.429713405731931, + D_C_ab=-9.4962363392015661, + D_E_ab=10.807718438492838, + D_Ep_ab=10.807718438492838, + ), }, - colorimetry_data=([ - VS_ColorimetryData( - name='VS1', - XYZ=np.array([0.14271715, 0.09107438, 0.04949462]), - Lab=np.array([36.19033159, 40.77666015, 18.34813122]), - C=44.714538268056295), - VS_ColorimetryData( - name='VS2', - XYZ=np.array([0.34657727, 0.27547744, 0.08900676]), - Lab=np.array([59.47793328, 31.84647553, 43.02166698]), - C=53.526272364542265), - VS_ColorimetryData( - name='VS3', - XYZ=np.array([0.41050103, 0.38688447, 0.06613576]), - Lab=np.array([68.52498199, 13.58373774, 66.83110597]), - C=68.197614738277153), - VS_ColorimetryData( - name='VS4', - XYZ=np.array([0.45427132, 0.50401485, 0.08193287]), - Lab=np.array([76.31503360, -7.01311937, 74.42292542]), - C=74.752629859451574), - VS_ColorimetryData( - name='VS5', - XYZ=np.array([0.42410903, 0.52851922, 0.11439811]), - Lab=np.array([77.78749106, -22.21024992, 66.98873490]), - C=70.574682470722237), - VS_ColorimetryData( - name='VS6', - XYZ=np.array([0.26141761, 0.36817692, 0.11429088]), - Lab=np.array([67.14003019, -33.22377278, 48.66064673]), - C=58.920943801328043), - VS_ColorimetryData( - name='VS7', - XYZ=np.array([0.14991085, 0.24929718, 0.13823960]), - Lab=np.array([57.00687837, -44.55800078, 24.99093412]), - C=51.087789364109447), - VS_ColorimetryData( - name='VS8', - XYZ=np.array([0.15843361, 0.24157338, 0.26933192]), - Lab=np.array([56.24498073, -36.24891645, -1.43945699]), - C=36.277485856650493), - VS_ColorimetryData( - name='VS9', - XYZ=np.array([0.18662998, 0.24708620, 0.40043672]), - Lab=np.array([56.79040828, -23.15964795, -18.30797717]), - C=29.522048055967336), - VS_ColorimetryData( - name='VS10', - XYZ=np.array([0.13339389, 0.15696027, 0.38421709]), - Lab=np.array([46.57313267, -9.89412218, -33.95549821]), - C=35.367633681665495), - VS_ColorimetryData( - name='VS11', - XYZ=np.array([0.09900743, 0.09954465, 0.32039098]), - Lab=np.array([37.76058147, 3.51413565, -40.81527590]), - C=40.966277550944625), - VS_ColorimetryData( - name='VS12', - XYZ=np.array([0.11576390, 0.09613722, 0.31928926]), - Lab=np.array([37.14003664, 18.77460935, -41.73197608]), - C=45.760723157938472), - VS_ColorimetryData( - name='VS13', - XYZ=np.array([0.20975356, 0.16847879, 0.37267453]), - Lab=np.array([48.06778877, 25.97523691, -29.94366223]), - C=39.640078711661452), - VS_ColorimetryData( - name='VS14', - XYZ=np.array([0.32298108, 0.24163045, 0.36212750]), - Lab=np.array([56.25066973, 37.45976513, -14.49801776]), - C=40.167480906459893), - VS_ColorimetryData( - name='VS15', - XYZ=np.array([0.22039693, 0.15371392, 0.17553541]), - Lab=np.array([46.13873255, 39.31630210, -2.10769974]), - C=39.372757193029329) - ], [ - VS_ColorimetryData( - name='VS1', - XYZ=np.array([0.19307399, 0.11049957, 0.04883449]), - Lab=np.array([39.66448335, 53.96576813, 24.65795205]), - C=59.332273930950407), - VS_ColorimetryData( - name='VS2', - XYZ=np.array([0.37207030, 0.27413935, 0.08882218]), - Lab=np.array([59.35552779, 40.92542394, 42.87088336]), - C=59.268903858924929), - VS_ColorimetryData( - name='VS3', - XYZ=np.array([0.42080177, 0.37272049, 0.06618662]), - Lab=np.array([67.48063529, 21.22017785, 65.01028998]), - C=68.385917784001094), - VS_ColorimetryData( - name='VS4', - XYZ=np.array([0.46201298, 0.49481812, 0.08588402]), - Lab=np.array([75.75009449, -2.36998858, 72.10803500]), - C=72.146971918467017), - VS_ColorimetryData( - name='VS5', - XYZ=np.array([0.40820163, 0.50861708, 0.11894286]), - Lab=np.array([76.59516364, -21.90847694, 63.69500310]), - C=67.35751466170575), - VS_ColorimetryData( - name='VS6', - XYZ=np.array([0.24672357, 0.36210726, 0.11976641]), - Lab=np.array([66.68062144, -37.45331655, 46.38001966]), - C=59.614236088535129), - VS_ColorimetryData( - name='VS7', - XYZ=np.array([0.13956281, 0.25328776, 0.14470409]), - Lab=np.array([57.39436642, -52.59240564, 24.11038403]), - C=57.855611215555271), - VS_ColorimetryData( - name='VS8', - XYZ=np.array([0.15184111, 0.25076481, 0.28160222]), - Lab=np.array([57.14986354, -44.01986548, -1.76441495]), - C=44.055212144295837), - VS_ColorimetryData( - name='VS9', - XYZ=np.array([0.18145720, 0.25845953, 0.41319296]), - Lab=np.array([57.89053974, -30.61154597, -17.92231311]), - C=35.472187042106164), - VS_ColorimetryData( - name='VS10', - XYZ=np.array([0.13144449, 0.16803553, 0.39315843]), - Lab=np.array([48.01155280, -17.36606803, -32.56732004]), - C=36.908138035180549), - VS_ColorimetryData( - name='VS11', - XYZ=np.array([0.09725029, 0.10655822, 0.32331756]), - Lab=np.array([38.99463289, -3.20501320, -39.09286753]), - C=39.224028364277721), - VS_ColorimetryData( - name='VS12', - XYZ=np.array([0.11497971, 0.09965866, 0.31509326]), - Lab=np.array([37.78109906, 15.45054732, -40.03995920]), - C=42.917569190579727), - VS_ColorimetryData( - name='VS13', - XYZ=np.array([0.23125767, 0.17972670, 0.36038776]), - Lab=np.array([49.46294201, 29.95248104, -25.97793559]), - C=39.648508903390763), - VS_ColorimetryData( - name='VS14', - XYZ=np.array([0.35887695, 0.25609884, 0.35518732]), - Lab=np.array([57.66488716, 43.83765559, -11.16556087]), - C=45.237261162848789), - VS_ColorimetryData( - name='VS15', - XYZ=np.array([0.26552457, 0.17192965, 0.17300682]), - Lab=np.array([48.50225789, 48.80528996, 2.49443403]), - C=48.868993532230895) - ])) + colorimetry_data=( + [ + VS_ColorimetryData( + name="VS1", + XYZ=np.array([0.14271715, 0.09107438, 0.04949462]), + Lab=np.array([36.19033159, 40.77666015, 18.34813122]), + C=44.714538268056295, + ), + VS_ColorimetryData( + name="VS2", + XYZ=np.array([0.34657727, 0.27547744, 0.08900676]), + Lab=np.array([59.47793328, 31.84647553, 43.02166698]), + C=53.526272364542265, + ), + VS_ColorimetryData( + name="VS3", + XYZ=np.array([0.41050103, 0.38688447, 0.06613576]), + Lab=np.array([68.52498199, 13.58373774, 66.83110597]), + C=68.197614738277153, + ), + VS_ColorimetryData( + name="VS4", + XYZ=np.array([0.45427132, 0.50401485, 0.08193287]), + Lab=np.array([76.31503360, -7.01311937, 74.42292542]), + C=74.752629859451574, + ), + VS_ColorimetryData( + name="VS5", + XYZ=np.array([0.42410903, 0.52851922, 0.11439811]), + Lab=np.array([77.78749106, -22.21024992, 66.98873490]), + C=70.574682470722237, + ), + VS_ColorimetryData( + name="VS6", + XYZ=np.array([0.26141761, 0.36817692, 0.11429088]), + Lab=np.array([67.14003019, -33.22377278, 48.66064673]), + C=58.920943801328043, + ), + VS_ColorimetryData( + name="VS7", + XYZ=np.array([0.14991085, 0.24929718, 0.13823960]), + Lab=np.array([57.00687837, -44.55800078, 24.99093412]), + C=51.087789364109447, + ), + VS_ColorimetryData( + name="VS8", + XYZ=np.array([0.15843361, 0.24157338, 0.26933192]), + Lab=np.array([56.24498073, -36.24891645, -1.43945699]), + C=36.277485856650493, + ), + VS_ColorimetryData( + name="VS9", + XYZ=np.array([0.18662998, 0.24708620, 0.40043672]), + Lab=np.array( + [56.79040828, -23.15964795, -18.30797717] + ), + C=29.522048055967336, + ), + VS_ColorimetryData( + name="VS10", + XYZ=np.array([0.13339389, 0.15696027, 0.38421709]), + Lab=np.array([46.57313267, -9.89412218, -33.95549821]), + C=35.367633681665495, + ), + VS_ColorimetryData( + name="VS11", + XYZ=np.array([0.09900743, 0.09954465, 0.32039098]), + Lab=np.array([37.76058147, 3.51413565, -40.81527590]), + C=40.966277550944625, + ), + VS_ColorimetryData( + name="VS12", + XYZ=np.array([0.11576390, 0.09613722, 0.31928926]), + Lab=np.array([37.14003664, 18.77460935, -41.73197608]), + C=45.760723157938472, + ), + VS_ColorimetryData( + name="VS13", + XYZ=np.array([0.20975356, 0.16847879, 0.37267453]), + Lab=np.array([48.06778877, 25.97523691, -29.94366223]), + C=39.640078711661452, + ), + VS_ColorimetryData( + name="VS14", + XYZ=np.array([0.32298108, 0.24163045, 0.36212750]), + Lab=np.array([56.25066973, 37.45976513, -14.49801776]), + C=40.167480906459893, + ), + VS_ColorimetryData( + name="VS15", + XYZ=np.array([0.22039693, 0.15371392, 0.17553541]), + Lab=np.array([46.13873255, 39.31630210, -2.10769974]), + C=39.372757193029329, + ), + ], + [ + VS_ColorimetryData( + name="VS1", + XYZ=np.array([0.19307399, 0.11049957, 0.04883449]), + Lab=np.array([39.66448335, 53.96576813, 24.65795205]), + C=59.332273930950407, + ), + VS_ColorimetryData( + name="VS2", + XYZ=np.array([0.37207030, 0.27413935, 0.08882218]), + Lab=np.array([59.35552779, 40.92542394, 42.87088336]), + C=59.268903858924929, + ), + VS_ColorimetryData( + name="VS3", + XYZ=np.array([0.42080177, 0.37272049, 0.06618662]), + Lab=np.array([67.48063529, 21.22017785, 65.01028998]), + C=68.385917784001094, + ), + VS_ColorimetryData( + name="VS4", + XYZ=np.array([0.46201298, 0.49481812, 0.08588402]), + Lab=np.array([75.75009449, -2.36998858, 72.10803500]), + C=72.146971918467017, + ), + VS_ColorimetryData( + name="VS5", + XYZ=np.array([0.40820163, 0.50861708, 0.11894286]), + Lab=np.array([76.59516364, -21.90847694, 63.69500310]), + C=67.35751466170575, + ), + VS_ColorimetryData( + name="VS6", + XYZ=np.array([0.24672357, 0.36210726, 0.11976641]), + Lab=np.array([66.68062144, -37.45331655, 46.38001966]), + C=59.614236088535129, + ), + VS_ColorimetryData( + name="VS7", + XYZ=np.array([0.13956281, 0.25328776, 0.14470409]), + Lab=np.array([57.39436642, -52.59240564, 24.11038403]), + C=57.855611215555271, + ), + VS_ColorimetryData( + name="VS8", + XYZ=np.array([0.15184111, 0.25076481, 0.28160222]), + Lab=np.array([57.14986354, -44.01986548, -1.76441495]), + C=44.055212144295837, + ), + VS_ColorimetryData( + name="VS9", + XYZ=np.array([0.18145720, 0.25845953, 0.41319296]), + Lab=np.array( + [57.89053974, -30.61154597, -17.92231311] + ), + C=35.472187042106164, + ), + VS_ColorimetryData( + name="VS10", + XYZ=np.array([0.13144449, 0.16803553, 0.39315843]), + Lab=np.array( + [48.01155280, -17.36606803, -32.56732004] + ), + C=36.908138035180549, + ), + VS_ColorimetryData( + name="VS11", + XYZ=np.array([0.09725029, 0.10655822, 0.32331756]), + Lab=np.array([38.99463289, -3.20501320, -39.09286753]), + C=39.224028364277721, + ), + VS_ColorimetryData( + name="VS12", + XYZ=np.array([0.11497971, 0.09965866, 0.31509326]), + Lab=np.array([37.78109906, 15.45054732, -40.03995920]), + C=42.917569190579727, + ), + VS_ColorimetryData( + name="VS13", + XYZ=np.array([0.23125767, 0.17972670, 0.36038776]), + Lab=np.array([49.46294201, 29.95248104, -25.97793559]), + C=39.648508903390763, + ), + VS_ColorimetryData( + name="VS14", + XYZ=np.array([0.35887695, 0.25609884, 0.35518732]), + Lab=np.array([57.66488716, 43.83765559, -11.16556087]), + C=45.237261162848789, + ), + VS_ColorimetryData( + name="VS15", + XYZ=np.array([0.26552457, 0.17192965, 0.17300682]), + Lab=np.array([48.50225789, 48.80528996, 2.49443403]), + C=48.868993532230895, + ), + ], + ), + ) specification_t = colour_quality_scale( - SDS_ILLUMINANTS['FL1'], - additional_data=True, - method='NIST CQS 9.0') + SDS_ILLUMINANTS["FL1"], additional_data=True, method="NIST CQS 9.0" + ) np.testing.assert_almost_equal( [ @@ -675,5 +768,5 @@ def test_colour_quality_scale(self): ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/quality/tests/test_cri.py b/colour/quality/tests/test_cri.py index 1855e4cdf2..53a360f332 100644 --- a/colour/quality/tests/test_cri.py +++ b/colour/quality/tests/test_cri.py @@ -1,28 +1,30 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.quality.cri` module. -""" +"""Defines the unit tests for the :mod:`colour.quality.cri` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import unittest -from colour.quality import (ColourRendering_Specification_CRI, - colour_rendering_index) from colour.colorimetry import SDS_ILLUMINANTS, SpectralDistribution +from colour.hints import Dict +from colour.quality import ( + ColourRendering_Specification_CRI, + colour_rendering_index, +) from colour.quality.cri import TCS_ColorimetryData, TCS_ColourQualityScaleData -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestColourRenderingIndex'] +__all__ = [ + "TestColourRenderingIndex", +] -DATA_SAMPLE = { +DATA_SAMPLE: Dict = { 380: 0.00588346, 385: 0.00315377, 390: 0.00242868, @@ -103,234 +105,277 @@ 765: 0.00340568, 770: 0.00261153, 775: 0.00258850, - 780: 0.00293663 + 780: 0.00293663, } class TestColourRenderingIndex(unittest.TestCase): """ - Defines :func:`colour.quality.cri.colour_rendering_index` + Define :func:`colour.quality.cri.colour_rendering_index` definition unit tests methods. """ def test_colour_rendering_index(self): - """ - Tests :func:`colour.quality.cri.colour_rendering_index` definition. - """ + """Test :func:`colour.quality.cri.colour_rendering_index` definition.""" self.assertAlmostEqual( - colour_rendering_index(SDS_ILLUMINANTS['FL1']), + colour_rendering_index(SDS_ILLUMINANTS["FL1"]), 75.852827992149358, - places=7) + places=7, + ) self.assertAlmostEqual( - colour_rendering_index(SDS_ILLUMINANTS['FL2']), + colour_rendering_index(SDS_ILLUMINANTS["FL2"]), 64.233724121664778, - places=7) + places=7, + ) self.assertAlmostEqual( - colour_rendering_index(SDS_ILLUMINANTS['A']), + colour_rendering_index(SDS_ILLUMINANTS["A"]), 99.996230290506887, - places=7) + places=7, + ) self.assertAlmostEqual( colour_rendering_index(SpectralDistribution(DATA_SAMPLE)), 70.815265381660197, - places=7) + places=7, + ) specification_r = ColourRendering_Specification_CRI( - name='FL1', + name="FL1", Q_a=75.852827992149358, Q_as={ - 1: - TCS_ColourQualityScaleData( - name='TCS01', Q_a=69.196992839933557), - 2: - TCS_ColourQualityScaleData( - name='TCS02', Q_a=83.650754065677816), - 3: - TCS_ColourQualityScaleData( - name='TCS03', Q_a=92.136090764490675), - 4: - TCS_ColourQualityScaleData( - name='TCS04', Q_a=72.665649420972784), - 5: - TCS_ColourQualityScaleData( - name='TCS05', Q_a=73.890734513517486), - 6: - TCS_ColourQualityScaleData( - name='TCS06', Q_a=79.619188860052745), - 7: - TCS_ColourQualityScaleData( - name='TCS07', Q_a=82.272569853644669), - 8: - TCS_ColourQualityScaleData( - name='TCS08', Q_a=53.390643618905109), - 9: - TCS_ColourQualityScaleData( - name='TCS09', Q_a=-47.284477750225903), - 10: - TCS_ColourQualityScaleData( - name='TCS10', Q_a=61.568336431199967), - 11: - TCS_ColourQualityScaleData( - name='TCS11', Q_a=67.522241168172485), - 12: - TCS_ColourQualityScaleData( - name='TCS12', Q_a=74.890093866757994), - 13: - TCS_ColourQualityScaleData( - name='TCS13', Q_a=72.771930354944615), - 14: - TCS_ColourQualityScaleData( - name='TCS14', Q_a=94.884867470552663) + 1: TCS_ColourQualityScaleData( + name="TCS01", Q_a=69.196992839933557 + ), + 2: TCS_ColourQualityScaleData( + name="TCS02", Q_a=83.650754065677816 + ), + 3: TCS_ColourQualityScaleData( + name="TCS03", Q_a=92.136090764490675 + ), + 4: TCS_ColourQualityScaleData( + name="TCS04", Q_a=72.665649420972784 + ), + 5: TCS_ColourQualityScaleData( + name="TCS05", Q_a=73.890734513517486 + ), + 6: TCS_ColourQualityScaleData( + name="TCS06", Q_a=79.619188860052745 + ), + 7: TCS_ColourQualityScaleData( + name="TCS07", Q_a=82.272569853644669 + ), + 8: TCS_ColourQualityScaleData( + name="TCS08", Q_a=53.390643618905109 + ), + 9: TCS_ColourQualityScaleData( + name="TCS09", Q_a=-47.284477750225903 + ), + 10: TCS_ColourQualityScaleData( + name="TCS10", Q_a=61.568336431199967 + ), + 11: TCS_ColourQualityScaleData( + name="TCS11", Q_a=67.522241168172485 + ), + 12: TCS_ColourQualityScaleData( + name="TCS12", Q_a=74.890093866757994 + ), + 13: TCS_ColourQualityScaleData( + name="TCS13", Q_a=72.771930354944615 + ), + 14: TCS_ColourQualityScaleData( + name="TCS14", Q_a=94.884867470552663 + ), }, - colorimetry_data=([ - TCS_ColorimetryData( - name='TCS01', - XYZ=np.array([31.19561134, 29.74560797, 23.44190201]), - uv=np.array([0.22782766, 0.32585700]), - UVW=np.array([25.43090825, 8.72673714, 60.46061819])), - TCS_ColorimetryData( - name='TCS02', - XYZ=np.array([26.70829694, 29.25797165, 13.98804447]), - uv=np.array([0.21049132, 0.34587843]), - UVW=np.array([11.90704877, 24.68727835, 60.03499882])), - TCS_ColorimetryData( - name='TCS03', - XYZ=np.array([24.20150315, 31.45341032, 9.19170689]), - uv=np.array([0.18489328, 0.36044399]), - UVW=np.array([-8.11343024, 37.47469142, 61.91555021])), - TCS_ColorimetryData( - name='TCS04', - XYZ=np.array([20.74577359, 29.44046997, 19.40749007]), - uv=np.array([0.15940652, 0.33932233]), - UVW=np.array([-27.55686536, 19.30727375, 60.19483706])), - TCS_ColorimetryData( - name='TCS05', - XYZ=np.array([25.09405566, 30.60912259, 37.93634878]), - uv=np.array([0.16784200, 0.30709443]), - UVW=np.array([-21.30541564, -6.63859760, 61.20303996])), - TCS_ColorimetryData( - name='TCS06', - XYZ=np.array([27.43598853, 28.84467787, 55.15209308]), - uv=np.array([0.17543246, 0.27665994]), - UVW=np.array([-14.91500194, -30.51166823, 59.67054901])), - TCS_ColorimetryData( - name='TCS07', - XYZ=np.array([31.30354023, 28.49931283, 51.55875721]), - uv=np.array([0.20410821, 0.27873574]), - UVW=np.array([6.88826195, -28.65430811, 59.36332055])), - TCS_ColorimetryData( - name='TCS08', - XYZ=np.array([33.81156122, 29.92921717, 44.07459562]), - uv=np.array([0.21992203, 0.29200489]), - UVW=np.array([19.29699368, -18.57115711, 60.61967045])), - TCS_ColorimetryData( - name='TCS09', - XYZ=np.array([14.75210654, 9.07663825, 4.24056478]), - uv=np.array([0.36063567, 0.33283649]), - UVW=np.array([74.85972274, 8.59043120, 35.14928413])), - TCS_ColorimetryData( - name='TCS10', - XYZ=np.array([53.37923227, 60.57123196, 10.90035400]), - uv=np.array([0.21466565, 0.36538264]), - UVW=np.array([20.48612095, 54.66177264, 81.18130740])), - TCS_ColorimetryData( - name='TCS11', - XYZ=np.array([12.25313424, 19.77564573, 14.04738059]), - uv=np.array([0.13962494, 0.33801637]), - UVW=np.array([-36.00690822, 15.29571595, 50.60573931])), - TCS_ColorimetryData( - name='TCS12', - XYZ=np.array([5.77964943, 5.69096075, 24.73530409]), - uv=np.array([0.13981616, 0.20650602]), - UVW=np.array([-19.30689974, -39.58762581, 27.63428055])), - TCS_ColorimetryData( - name='TCS13', - XYZ=np.array([56.62340157, 57.50304691, 39.13768236]), - uv=np.array([0.21850039, 0.33284220]), - UVW=np.array([23.93135946, 18.85365757, 79.49473722])), - TCS_ColorimetryData( - name='TCS14', - XYZ=np.array([9.42270977, 12.06929274, 5.06066928]), - uv=np.array([0.18328188, 0.35214117]), - UVW=np.array([-6.11563143, 19.91896684, 40.34566797])) - ], [ - TCS_ColorimetryData( - name='TCS01', - XYZ=np.array([33.04774537, 29.80902109, 24.23929188]), - uv=np.array([0.23908620, 0.32348313]), - UVW=np.array([32.11891091, 8.39794012, 60.51562388])), - TCS_ColorimetryData( - name='TCS02', - XYZ=np.array([27.53677515, 28.90913487, 14.75211494]), - uv=np.array([0.21792745, 0.34318256]), - UVW=np.array([15.27177183, 23.58438306, 59.72761646])), - TCS_ColorimetryData( - name='TCS03', - XYZ=np.array([23.95706051, 30.44569936, 9.79977770]), - uv=np.array([0.18788308, 0.35815528]), - UVW=np.array([-8.23665275, 35.99767796, 61.06361523])), - TCS_ColorimetryData( - name='TCS04', - XYZ=np.array([20.43647757, 29.46748627, 21.03631859]), - uv=np.array([0.15554126, 0.33641389]), - UVW=np.array([-33.44111710, 18.47940853, 60.21844268])), - TCS_ColorimetryData( - name='TCS05', - XYZ=np.array([24.95511680, 30.81953457, 39.91407614]), - uv=np.array([0.16445149, 0.30464603]), - UVW=np.array([-26.97713986, -6.51317420, 61.38182431])), - TCS_ColorimetryData( - name='TCS06', - XYZ=np.array([28.14601546, 29.75632189, 57.16886797]), - uv=np.array([0.17427942, 0.27637560]), - UVW=np.array([-18.85053083, -28.64005452, 60.46991712])), - TCS_ColorimetryData( - name='TCS07', - XYZ=np.array([33.29720901, 29.36938555, 52.53803430]), - uv=np.array([0.21092469, 0.27906521]), - UVW=np.array([9.90110830, -26.37778265, 60.13265766])), - TCS_ColorimetryData( - name='TCS08', - XYZ=np.array([37.64974363, 31.35401774, 44.84930911]), - uv=np.array([0.23439240, 0.29279655]), - UVW=np.array([29.04479043, -16.08583648, 61.83233828])), - TCS_ColorimetryData( - name='TCS09', - XYZ=np.array([20.69443384, 11.28822434, 4.28723010]), - uv=np.array([0.40801431, 0.33384028]), - UVW=np.array([106.56664825, 10.68535426, 39.08093160])), - TCS_ColorimetryData( - name='TCS10', - XYZ=np.array([55.04099876, 59.04719161, 11.86354410]), - uv=np.array([0.22549942, 0.36286881]), - UVW=np.array([28.45432459, 52.29127793, 80.35085218])), - TCS_ColorimetryData( - name='TCS11', - XYZ=np.array([12.13069359, 20.35272336, 15.17132466]), - uv=np.array([0.13369530, 0.33646842]), - UVW=np.array([-43.02145539, 15.76573781, 51.25705062])), - TCS_ColorimetryData( - name='TCS12', - XYZ=np.array([6.18799870, 6.41132549, 27.28158667]), - uv=np.array([0.13437372, 0.20883497]), - UVW=np.array([-24.45285903, -39.79705961, 29.44325151])), - TCS_ColorimetryData( - name='TCS13', - XYZ=np.array([58.97935503, 57.14777525, 40.83450223]), - uv=np.array([0.22712769, 0.33011150]), - UVW=np.array([29.75912462, 17.83690656, 79.29560174])), - TCS_ColorimetryData( - name='TCS14', - XYZ=np.array([9.34469915, 11.70922060, 5.33442353]), - uv=np.array([0.18597686, 0.34955284]), - UVW=np.array([-6.34991066, 18.99712303, 39.76962229])) - ])) + colorimetry_data=( + [ + TCS_ColorimetryData( + name="TCS01", + XYZ=np.array([31.19561134, 29.74560797, 23.44190201]), + uv=np.array([0.22782766, 0.32585700]), + UVW=np.array([25.43090825, 8.72673714, 60.46061819]), + ), + TCS_ColorimetryData( + name="TCS02", + XYZ=np.array([26.70829694, 29.25797165, 13.98804447]), + uv=np.array([0.21049132, 0.34587843]), + UVW=np.array([11.90704877, 24.68727835, 60.03499882]), + ), + TCS_ColorimetryData( + name="TCS03", + XYZ=np.array([24.20150315, 31.45341032, 9.19170689]), + uv=np.array([0.18489328, 0.36044399]), + UVW=np.array([-8.11343024, 37.47469142, 61.91555021]), + ), + TCS_ColorimetryData( + name="TCS04", + XYZ=np.array([20.74577359, 29.44046997, 19.40749007]), + uv=np.array([0.15940652, 0.33932233]), + UVW=np.array([-27.55686536, 19.30727375, 60.19483706]), + ), + TCS_ColorimetryData( + name="TCS05", + XYZ=np.array([25.09405566, 30.60912259, 37.93634878]), + uv=np.array([0.16784200, 0.30709443]), + UVW=np.array([-21.30541564, -6.63859760, 61.20303996]), + ), + TCS_ColorimetryData( + name="TCS06", + XYZ=np.array([27.43598853, 28.84467787, 55.15209308]), + uv=np.array([0.17543246, 0.27665994]), + UVW=np.array( + [-14.91500194, -30.51166823, 59.67054901] + ), + ), + TCS_ColorimetryData( + name="TCS07", + XYZ=np.array([31.30354023, 28.49931283, 51.55875721]), + uv=np.array([0.20410821, 0.27873574]), + UVW=np.array([6.88826195, -28.65430811, 59.36332055]), + ), + TCS_ColorimetryData( + name="TCS08", + XYZ=np.array([33.81156122, 29.92921717, 44.07459562]), + uv=np.array([0.21992203, 0.29200489]), + UVW=np.array([19.29699368, -18.57115711, 60.61967045]), + ), + TCS_ColorimetryData( + name="TCS09", + XYZ=np.array([14.75210654, 9.07663825, 4.24056478]), + uv=np.array([0.36063567, 0.33283649]), + UVW=np.array([74.85972274, 8.59043120, 35.14928413]), + ), + TCS_ColorimetryData( + name="TCS10", + XYZ=np.array([53.37923227, 60.57123196, 10.90035400]), + uv=np.array([0.21466565, 0.36538264]), + UVW=np.array([20.48612095, 54.66177264, 81.18130740]), + ), + TCS_ColorimetryData( + name="TCS11", + XYZ=np.array([12.25313424, 19.77564573, 14.04738059]), + uv=np.array([0.13962494, 0.33801637]), + UVW=np.array([-36.00690822, 15.29571595, 50.60573931]), + ), + TCS_ColorimetryData( + name="TCS12", + XYZ=np.array([5.77964943, 5.69096075, 24.73530409]), + uv=np.array([0.13981616, 0.20650602]), + UVW=np.array( + [-19.30689974, -39.58762581, 27.63428055] + ), + ), + TCS_ColorimetryData( + name="TCS13", + XYZ=np.array([56.62340157, 57.50304691, 39.13768236]), + uv=np.array([0.21850039, 0.33284220]), + UVW=np.array([23.93135946, 18.85365757, 79.49473722]), + ), + TCS_ColorimetryData( + name="TCS14", + XYZ=np.array([9.42270977, 12.06929274, 5.06066928]), + uv=np.array([0.18328188, 0.35214117]), + UVW=np.array([-6.11563143, 19.91896684, 40.34566797]), + ), + ], + [ + TCS_ColorimetryData( + name="TCS01", + XYZ=np.array([33.04774537, 29.80902109, 24.23929188]), + uv=np.array([0.23908620, 0.32348313]), + UVW=np.array([32.11891091, 8.39794012, 60.51562388]), + ), + TCS_ColorimetryData( + name="TCS02", + XYZ=np.array([27.53677515, 28.90913487, 14.75211494]), + uv=np.array([0.21792745, 0.34318256]), + UVW=np.array([15.27177183, 23.58438306, 59.72761646]), + ), + TCS_ColorimetryData( + name="TCS03", + XYZ=np.array([23.95706051, 30.44569936, 9.79977770]), + uv=np.array([0.18788308, 0.35815528]), + UVW=np.array([-8.23665275, 35.99767796, 61.06361523]), + ), + TCS_ColorimetryData( + name="TCS04", + XYZ=np.array([20.43647757, 29.46748627, 21.03631859]), + uv=np.array([0.15554126, 0.33641389]), + UVW=np.array([-33.44111710, 18.47940853, 60.21844268]), + ), + TCS_ColorimetryData( + name="TCS05", + XYZ=np.array([24.95511680, 30.81953457, 39.91407614]), + uv=np.array([0.16445149, 0.30464603]), + UVW=np.array([-26.97713986, -6.51317420, 61.38182431]), + ), + TCS_ColorimetryData( + name="TCS06", + XYZ=np.array([28.14601546, 29.75632189, 57.16886797]), + uv=np.array([0.17427942, 0.27637560]), + UVW=np.array( + [-18.85053083, -28.64005452, 60.46991712] + ), + ), + TCS_ColorimetryData( + name="TCS07", + XYZ=np.array([33.29720901, 29.36938555, 52.53803430]), + uv=np.array([0.21092469, 0.27906521]), + UVW=np.array([9.90110830, -26.37778265, 60.13265766]), + ), + TCS_ColorimetryData( + name="TCS08", + XYZ=np.array([37.64974363, 31.35401774, 44.84930911]), + uv=np.array([0.23439240, 0.29279655]), + UVW=np.array([29.04479043, -16.08583648, 61.83233828]), + ), + TCS_ColorimetryData( + name="TCS09", + XYZ=np.array([20.69443384, 11.28822434, 4.28723010]), + uv=np.array([0.40801431, 0.33384028]), + UVW=np.array([106.56664825, 10.68535426, 39.08093160]), + ), + TCS_ColorimetryData( + name="TCS10", + XYZ=np.array([55.04099876, 59.04719161, 11.86354410]), + uv=np.array([0.22549942, 0.36286881]), + UVW=np.array([28.45432459, 52.29127793, 80.35085218]), + ), + TCS_ColorimetryData( + name="TCS11", + XYZ=np.array([12.13069359, 20.35272336, 15.17132466]), + uv=np.array([0.13369530, 0.33646842]), + UVW=np.array([-43.02145539, 15.76573781, 51.25705062]), + ), + TCS_ColorimetryData( + name="TCS12", + XYZ=np.array([6.18799870, 6.41132549, 27.28158667]), + uv=np.array([0.13437372, 0.20883497]), + UVW=np.array( + [-24.45285903, -39.79705961, 29.44325151] + ), + ), + TCS_ColorimetryData( + name="TCS13", + XYZ=np.array([58.97935503, 57.14777525, 40.83450223]), + uv=np.array([0.22712769, 0.33011150]), + UVW=np.array([29.75912462, 17.83690656, 79.29560174]), + ), + TCS_ColorimetryData( + name="TCS14", + XYZ=np.array([9.34469915, 11.70922060, 5.33442353]), + uv=np.array([0.18597686, 0.34955284]), + UVW=np.array([-6.34991066, 18.99712303, 39.76962229]), + ), + ], + ), + ) specification_t = colour_rendering_index( - SDS_ILLUMINANTS['FL1'], additional_data=True) + SDS_ILLUMINANTS["FL1"], additional_data=True + ) np.testing.assert_almost_equal( [ @@ -345,5 +390,5 @@ def test_colour_rendering_index(self): ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/quality/tests/test_ssi.py b/colour/quality/tests/test_ssi.py index 0317edbdba..1161b7f7e1 100644 --- a/colour/quality/tests/test_ssi.py +++ b/colour/quality/tests/test_ssi.py @@ -1,25 +1,25 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.quality.ssi` module. -""" +"""Defines the unit tests for the :mod:`colour.quality.ssi` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import unittest -from colour.quality import spectral_similarity_index from colour.colorimetry import SDS_ILLUMINANTS, SpectralDistribution +from colour.hints import Dict +from colour.quality import spectral_similarity_index -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestSpectralSimilarityIndex'] +__all__ = [ + "TestSpectralSimilarityIndex", +] -DATA_HMI = { +DATA_HMI: Dict = { 300: 0.000000000000000, 301: 0.000000000000000, 302: 0.000000000000000, @@ -550,28 +550,32 @@ 827: 0.000000000000000, 828: 0.000000000000000, 829: 0.000000000000000, - 830: 0.000000000000000 + 830: 0.000000000000000, } class TestSpectralSimilarityIndex(unittest.TestCase): """ - Defines :func:`colour.quality.ssi.spectral_similarity_index` + Define :func:`colour.quality.ssi.spectral_similarity_index` definition unit tests methods. """ def test_spectral_similarity_index(self): - """ - Tests :func:`colour.quality.ssi.spectral_similarity_index` definition. - """ + """Test :func:`colour.quality.ssi.spectral_similarity_index` definition.""" self.assertEqual( - spectral_similarity_index(SDS_ILLUMINANTS['C'], - SDS_ILLUMINANTS['D65']), 94.0) + spectral_similarity_index( + SDS_ILLUMINANTS["C"], SDS_ILLUMINANTS["D65"] + ), + 94.0, + ) self.assertEqual( spectral_similarity_index( - SpectralDistribution(DATA_HMI), SDS_ILLUMINANTS['D50']), 72.0) + SpectralDistribution(DATA_HMI), SDS_ILLUMINANTS["D50"] + ), + 72.0, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/quality/tests/test_tm3018.py b/colour/quality/tests/test_tm3018.py index 1dcc595d83..b836019f11 100644 --- a/colour/quality/tests/test_tm3018.py +++ b/colour/quality/tests/test_tm3018.py @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.quality.tm3018` module. +Defines the unit tests for the :mod:`colour.quality.tm3018` module. Notes ----- @@ -9,88 +8,207 @@ http://media.ies.org/docs/errata/TM-30-18_tools_etc.zip. """ -from __future__ import division, unicode_literals - import numpy as np import unittest from colour.colorimetry import SDS_ILLUMINANTS -from colour.quality.tm3018 import (averages_area, - colour_fidelity_index_ANSIIESTM3018) +from colour.quality.tm3018 import ( + averages_area, + colour_fidelity_index_ANSIIESTM3018, +) from colour.utilities import as_float_array -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestColourFidelityIndexANSIIESTM3018', 'TestAveragesArea'] +__all__ = [ + "TestColourFidelityIndexANSIIESTM3018", + "TestAveragesArea", +] class TestColourFidelityIndexANSIIESTM3018(unittest.TestCase): """ - Defines :func:`colour.quality.tm3018.colour_fidelity_index_ANSIIESTM3018` + Define :func:`colour.quality.tm3018.colour_fidelity_index_ANSIIESTM3018` definition unit tests methods. """ def test_colour_fidelity_index_ANSIIESTM3018(self): """ - Tests :func:`colour.quality.tm3018.colour_fidelity_index_ANSIIESTM3018` + Test :func:`colour.quality.tm3018.colour_fidelity_index_ANSIIESTM3018` definition. """ specification = colour_fidelity_index_ANSIIESTM3018( - SDS_ILLUMINANTS['FL2'], additional_data=True) + SDS_ILLUMINANTS["FL2"], additional_data=True + ) np.testing.assert_almost_equal(specification.R_f, 70, 0) np.testing.assert_almost_equal(specification.R_g, 86, 0) np.testing.assert_almost_equal(specification.CCT, 4225, 0) np.testing.assert_almost_equal(specification.D_uv, 0.0019, 4) - np.testing.assert_almost_equal(specification.R_s, [ - 79, 59, 67, 66, 36, 66, 40, 35, 95, 54, 48, 45, 64, 87, 72, 49, 56, - 69, 57, 44, 47, 47, 80, 63, 48, 59, 82, 85, 62, 70, 68, 62, 74, 74, - 86, 88, 80, 76, 97, 93, 91, 89, 83, 99, 83, 81, 87, 66, 80, 81, 81, - 76, 69, 77, 77, 66, 66, 67, 79, 90, 78, 87, 77, 60, 61, 58, 56, 62, - 73, 58, 64, 84, 53, 96, 67, 57, 76, 63, 82, 85, 74, 94, 91, 86, 81, - 64, 74, 69, 66, 68, 93, 51, 70, 41, 62, 70, 80, 67, 45 - ], 0) + np.testing.assert_almost_equal( + specification.R_s, + [ + 79, + 59, + 67, + 66, + 36, + 66, + 40, + 35, + 95, + 54, + 48, + 45, + 64, + 87, + 72, + 49, + 56, + 69, + 57, + 44, + 47, + 47, + 80, + 63, + 48, + 59, + 82, + 85, + 62, + 70, + 68, + 62, + 74, + 74, + 86, + 88, + 80, + 76, + 97, + 93, + 91, + 89, + 83, + 99, + 83, + 81, + 87, + 66, + 80, + 81, + 81, + 76, + 69, + 77, + 77, + 66, + 66, + 67, + 79, + 90, + 78, + 87, + 77, + 60, + 61, + 58, + 56, + 62, + 73, + 58, + 64, + 84, + 53, + 96, + 67, + 57, + 76, + 63, + 82, + 85, + 74, + 94, + 91, + 86, + 81, + 64, + 74, + 69, + 66, + 68, + 93, + 51, + 70, + 41, + 62, + 70, + 80, + 67, + 45, + ], + 0, + ) np.testing.assert_almost_equal( specification.R_fs, [60, 61, 53, 68, 80, 88, 77, 73, 76, 62, 70, 77, 81, 71, 64, 65], - 0) + 0, + ) np.testing.assert_almost_equal( specification.R_cs, [-25, -18, -9, 5, 11, 4, -8, -15, -17, -15, -4, 5, 11, 7, -6, -16], - 0) - np.testing.assert_almost_equal(specification.R_hs, [ - -0.02, 0.14, 0.24, 0.20, 0.09, -0.07, -0.12, -0.08, 0.01, 0.17, - 0.19, 0.11, -0.08, -0.15, -0.26, -0.17 - ], 2) + 0, + ) + np.testing.assert_almost_equal( + specification.R_hs, + [ + -0.02, + 0.14, + 0.24, + 0.20, + 0.09, + -0.07, + -0.12, + -0.08, + 0.01, + 0.17, + 0.19, + 0.11, + -0.08, + -0.15, + -0.26, + -0.17, + ], + 2, + ) class TestAveragesArea(unittest.TestCase): """ - Defines :func:`colour.quality.tm3018.averages_area` definition unit tests + Define :func:`colour.quality.tm3018.averages_area` definition unit tests methods. """ def test_averages_area(self): - """ - Tests :func:`colour.quality.tm3018.averages_area` definition. - """ + """Test :func:`colour.quality.tm3018.averages_area` definition.""" # Simple 3 * sqrt(2) by sqrt(2) rectangle. rectangle = as_float_array([[2, 1], [1, 2], [-2, -1], [-1, -2]]) np.allclose(averages_area(rectangle), 6) # Concave polygon. - poly = np.array([[1., -1], [1, 1], [3, 1], [3, 3], [-1, 3], [-1, -1]]) + poly = np.array([[1.0, -1], [1, 1], [3, 1], [3, 3], [-1, 3], [-1, -1]]) np.allclose(averages_area(poly), 12) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/quality/tm3018.py b/colour/quality/tm3018.py index f79b50a609..c0e21641ee 100644 --- a/colour/quality/tm3018.py +++ b/colour/quality/tm3018.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ ANSI/IES TM-30-18 Colour Fidelity Index ======================================= @@ -16,76 +15,118 @@ ISBN:978-0-87995-379-9 """ -import numpy as np -from collections import namedtuple +from __future__ import annotations +import numpy as np +from dataclasses import dataclass + +from colour.colorimetry import SpectralDistribution +from colour.hints import ( + ArrayLike, + Boolean, + Floating, + List, + NDArray, + Tuple, + Union, + cast, +) from colour.quality import colour_fidelity_index_CIE2017 -from colour.quality.cfi2017 import delta_E_to_R_f -from colour.utilities import as_float_array, as_int +from colour.quality.cfi2017 import ( + ColourRendering_Specification_CIE2017, + TCS_ColorimetryData_CIE2017, + delta_E_to_R_f, +) +from colour.utilities import as_float_array, as_int_scalar -class ColourQuality_Specification_ANSIIESTM3018( - namedtuple( - 'ColourQuality_Specification_ANSIIESTM3018', - ('name', 'sd_test', 'sd_reference', 'R_f', 'R_s', 'CCT', 'D_uv', - 'colorimetry_data', 'R_g', 'bins', 'averages_test', - 'averages_reference', 'average_norms', 'R_fs', 'R_cs', 'R_hs'))): +@dataclass +class ColourQuality_Specification_ANSIIESTM3018: """ - Defines the *ANSI/IES TM-30-18 Colour Fidelity Index* (CFI) colour quality + Define the *ANSI/IES TM-30-18 Colour Fidelity Index* (CFI) colour quality specification. Parameters ---------- - name : unicode + name Name of the test spectral distribution. - sd_test : SpectralDistribution + sd_test Spectral distribution of the tested illuminant. - sd_reference : SpectralDistribution + sd_reference Spectral distribution of the reference illuminant. - R_f : numeric + R_f *Colour Fidelity Index* (CFI) :math:`R_f`. - R_s : list + R_s Individual *colour fidelity indexes* data for each sample. - CCT : numeric + CCT Correlated colour temperature :math:`T_{cp}`. - D_uv : numeric + D_uv Distance from the Planckian locus :math:`\\Delta_{uv}`. - colorimetry_data : tuple + colorimetry_data Colorimetry data for the test and reference computations. - bins : list of list of int + R_g + Gamut index :math:`R_g`. + bins List of 16 lists, each containing the indexes of colour samples that lie in the respective hue bin. - averages_test : ndarray, (16, 2) + averages_test Averages of *CAM02-UCS* a', b' coordinates for each hue bin for test samples. - averages_reference : ndarray, (16, 2) + averages_reference Averages for reference samples. - average_norms : ndarray, (16,) + average_norms Distance of averages for reference samples from the origin. - R_fs : ndarray, (16,) + R_fs Local colour fidelities for each hue bin. - R_cs : ndarray, (16,) + R_cs Local chromaticity shifts for each hue bin, in percents. - R_hs : ndarray, (16,) + R_hs Local hue shifts for each hue bin. """ - -def colour_fidelity_index_ANSIIESTM3018(sd_test, additional_data=False): + name: str + sd_test: SpectralDistribution + sd_reference: SpectralDistribution + R_f: Floating + R_s: NDArray + CCT: Floating + D_uv: Floating + colorimetry_data: Tuple[ + Tuple[TCS_ColorimetryData_CIE2017, ...], + Tuple[TCS_ColorimetryData_CIE2017, ...], + ] + R_g: Floating + bins: List[List[int]] + averages_test: NDArray + averages_reference: NDArray + average_norms: NDArray + R_fs: NDArray + R_cs: NDArray + R_hs: NDArray + + +def colour_fidelity_index_ANSIIESTM3018( + sd_test: SpectralDistribution, additional_data: Boolean = False +) -> Union[ + Floating, + ColourQuality_Specification_ANSIIESTM3018, + ColourRendering_Specification_CIE2017, +]: """ - Returns the *ANSI/IES TM-30-18 Colour Fidelity Index* (CFI) :math:`R_f` + Return the *ANSI/IES TM-30-18 Colour Fidelity Index* (CFI) :math:`R_f` of given spectral distribution. Parameters ---------- - sd_test : SpectralDistribution + sd_test Test spectral distribution. - additional_data : bool, optional + additional_data Whether to output additional data. Returns ------- - numeric or ColourQuality_Specification_ANSIIESTM3018 + :class:`numpy.floating` or \ +:class:`colour.quality.ColourQuality_Specification_ANSIIESTM3018` *ANSI/IES TM-30-18 Colour Fidelity Index* (CFI). References @@ -103,12 +144,18 @@ def colour_fidelity_index_ANSIIESTM3018(sd_test, additional_data=False): if not additional_data: return colour_fidelity_index_CIE2017(sd_test, False) - specification = colour_fidelity_index_CIE2017(sd_test, True) + specification: ( + ColourRendering_Specification_CIE2017 + ) = colour_fidelity_index_CIE2017( + sd_test, True + ) # type: ignore[assignment] # Setup bins based on where the reference a'b' points are located. - bins = [[] for _i in range(16)] + bins: List[List[int]] = [[] for _i in range(16)] for i, sample in enumerate(specification.colorimetry_data[1]): - bin_index = as_int(np.floor(sample.CAM.h / 22.5)) + bin_index = as_int_scalar( + np.floor(cast(Floating, sample.CAM.h) / 22.5) + ) bins[bin_index].append(i) # Per-bin a'b' averages. @@ -128,13 +175,14 @@ def colour_fidelity_index_ANSIIESTM3018(sd_test, additional_data=False): # Gamut Index. R_g = 100 * ( - averages_area(averages_test) / averages_area(averages_reference)) + averages_area(averages_test) / averages_area(averages_reference) + ) # Local colour fidelity indexes, i.e. 16 CFIs for each bin. bin_delta_E_s = [ np.mean([specification.delta_E_s[bins[i]]]) for i in range(16) ] - R_fs = delta_E_to_R_f(as_float_array(bin_delta_E_s)) + R_fs = as_float_array(delta_E_to_R_f(bin_delta_E_s)) # Angles bisecting the hue bins. angles = (22.5 * np.arange(16) + 11.25) / 180 * np.pi @@ -152,27 +200,42 @@ def colour_fidelity_index_ANSIIESTM3018(sd_test, additional_data=False): R_hs = (-a_deltas * sines + b_deltas * cosines) / average_norms return ColourQuality_Specification_ANSIIESTM3018( - specification.name, sd_test, specification.sd_reference, - specification.R_f, specification.R_s, specification.CCT, - specification.D_uv, specification.colorimetry_data, R_g, bins, - averages_test, averages_reference, average_norms, R_fs, R_cs, R_hs) - - -def averages_area(averages): + specification.name, + sd_test, + specification.sd_reference, + specification.R_f, + specification.R_s, + specification.CCT, + specification.D_uv, + specification.colorimetry_data, + R_g, + bins, + averages_test, + averages_reference, + average_norms, + R_fs, + R_cs, + R_hs, + ) + + +def averages_area(averages: ArrayLike) -> Floating: """ - Computes the area of the polygon formed by the hue bin averages. + Compute the area of the polygon formed by the hue bin averages. Parameters ---------- - averages : array_like, (n, 2) + averages Hue bin averages. Returns ------- - float + :class:`numpy.floating` Area of the polygon. """ + averages = as_float_array(averages) + N = averages.shape[0] triangle_areas = np.empty(N) diff --git a/colour/recovery/__init__.py b/colour/recovery/__init__.py index 789c016c4f..f30ac342ec 100644 --- a/colour/recovery/__init__.py +++ b/colour/recovery/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ References ---------- @@ -19,45 +18,70 @@ doi:10.1080/10867651.1999.10487511 """ -from __future__ import absolute_import +from __future__ import annotations import sys +from colour.colorimetry import SpectralDistribution +from colour.hints import Any, ArrayLike, Literal, Union from colour.utilities.deprecation import ModuleAPI, build_API_changes from colour.utilities.documentation import is_documentation_building - -from colour.utilities import (CaseInsensitiveMapping, as_float_array, - filter_kwargs) +from colour.utilities import ( + CaseInsensitiveMapping, + as_float_array, + filter_kwargs, + validate_method, +) from .datasets import * # noqa from . import datasets -from .jakob2019 import (sd_Jakob2019, find_coefficients_Jakob2019, - XYZ_to_sd_Jakob2019, LUT3D_Jakob2019) -from .mallett2019 import (spectral_primary_decomposition_Mallett2019, - RGB_to_sd_Mallett2019) +from .jakob2019 import ( + sd_Jakob2019, + find_coefficients_Jakob2019, + XYZ_to_sd_Jakob2019, + LUT3D_Jakob2019, +) +from .mallett2019 import ( + spectral_primary_decomposition_Mallett2019, + RGB_to_sd_Mallett2019, +) from .meng2015 import XYZ_to_sd_Meng2015 -from .otsu2018 import Dataset_Otsu2018, NodeTree_Otsu2018, XYZ_to_sd_Otsu2018 +from .otsu2018 import Dataset_Otsu2018, Tree_Otsu2018, XYZ_to_sd_Otsu2018 from .smits1999 import RGB_to_sd_Smits1999 + __all__ = [] __all__ += datasets.__all__ __all__ += [ - 'sd_Jakob2019', 'find_coefficients_Jakob2019', 'XYZ_to_sd_Jakob2019', - 'LUT3D_Jakob2019' + "sd_Jakob2019", + "find_coefficients_Jakob2019", + "XYZ_to_sd_Jakob2019", + "LUT3D_Jakob2019", +] +__all__ += [ + "spectral_primary_decomposition_Mallett2019", + "RGB_to_sd_Mallett2019", ] __all__ += [ - 'spectral_primary_decomposition_Mallett2019', 'RGB_to_sd_Mallett2019' + "XYZ_to_sd_Meng2015", ] -__all__ += ['XYZ_to_sd_Meng2015'] -__all__ += ['Dataset_Otsu2018', 'NodeTree_Otsu2018', 'XYZ_to_sd_Otsu2018'] -__all__ += ['RGB_to_sd_Smits1999'] - -XYZ_TO_SD_METHODS = CaseInsensitiveMapping({ - 'Jakob 2019': XYZ_to_sd_Jakob2019, - 'Mallett 2019': RGB_to_sd_Mallett2019, - 'Meng 2015': XYZ_to_sd_Meng2015, - 'Otsu 2018': XYZ_to_sd_Otsu2018, - 'Smits 1999': RGB_to_sd_Smits1999, -}) +__all__ += [ + "Dataset_Otsu2018", + "Tree_Otsu2018", + "XYZ_to_sd_Otsu2018", +] +__all__ += [ + "RGB_to_sd_Smits1999", +] + +XYZ_TO_SD_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Jakob 2019": XYZ_to_sd_Jakob2019, + "Mallett 2019": RGB_to_sd_Mallett2019, + "Meng 2015": XYZ_to_sd_Meng2015, + "Otsu 2018": XYZ_to_sd_Otsu2018, + "Smits 1999": RGB_to_sd_Smits1999, + } +) XYZ_TO_SD_METHODS.__doc__ = """ Supported spectral distribution recovery methods. @@ -65,64 +89,73 @@ ---------- :cite:`Jakob2019`, :cite:`Mallett2019`, :cite:`Meng2015c`, :cite:`Smits1999a` - -XYZ_TO_SD_METHODS : CaseInsensitiveMapping - **{'Jakob 2019', 'Mallett 2019', 'Meng 2015', 'Otsu 2018', 'Smits 1999'}** """ -def XYZ_to_sd(XYZ, method='Meng 2015', **kwargs): +def XYZ_to_sd( + XYZ: ArrayLike, + method: Union[ + Literal[ + "Jakob 2019", + "Mallett 2019", + "Meng 2015", + "Otsu 2018", + "Smits 1999", + ], + str, + ] = "Meng 2015", + **kwargs: Any, +) -> SpectralDistribution: """ - Recovers the spectral distribution of given *CIE XYZ* tristimulus + Recover the spectral distribution of given *CIE XYZ* tristimulus values using given method. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values to recover the spectral distribution from. - method : unicode, optional - **{'Meng 2015', 'Jakob 2019', 'Mallett 2019', 'Otsu 2018', - 'Smits 1999'}** + method Computation method. Other Parameters ---------------- - additional_data : bool, optional + additional_data {:func:`colour.recovery.XYZ_to_sd_Jakob2019`}, If *True*, ``error`` will be returned alongside ``sd``. - basis_functions : MultiSpectralDistributions + basis_functions {:func:`colour.recovery.RGB_to_sd_Mallett2019`}, Basis functions for the method. The default is to use the built-in *sRGB* basis functions, i.e. :attr:`colour.recovery.MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019`. - clip : bool, optional + clip {:func:`colour.recovery.XYZ_to_sd_Otsu2018`}, If *True*, the default, values below zero and above unity in the recovered spectral distributions will be clipped. This ensures that the returned reflectance is physical and conserves energy, but will cause noticeable colour differences in case of very saturated colours. - cmfs : XYZ_ColourMatchingFunctions, optional + cmfs {:func:`colour.recovery.XYZ_to_sd_Meng2015`}, Standard observer colour matching functions. - colourspace : RGB_Colourspace, optional + colourspace {:func:`colour.recovery.XYZ_to_sd_Jakob2019`}, *RGB* colourspace of the target colour. Note that no chromatic adaptation is performed between ``illuminant`` and the colourspace whitepoint. - dataset : Dataset_Otsu2018, optional + dataset {:func:`colour.recovery.XYZ_to_sd_Otsu2018`}, Dataset to use for reconstruction. The default is to use the published data. - illuminant : SpectralDistribution, optional + illuminant {:func:`colour.recovery.XYZ_to_sd_Jakob2019`, :func:`colour.recovery.XYZ_to_sd_Meng2015`}, - Illuminant spectral distribution. - interval : numeric, optional + Illuminant spectral distribution, default to + *CIE Standard Illuminant D65*. + interval {:func:`colour.recovery.XYZ_to_sd_Meng2015`}, Wavelength :math:`\\lambda_{i}` range interval in nm. The smaller ``interval`` is, the longer the computations will be. - optimisation_kwargs : dict_like, optional + optimisation_kwargs {:func:`colour.recovery.XYZ_to_sd_Jakob2019`, :func:`colour.recovery.XYZ_to_sd_Meng2015`}, Parameters for :func:`scipy.optimize.minimize` and @@ -130,12 +163,11 @@ def XYZ_to_sd(XYZ, method='Meng 2015', **kwargs): Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Recovered spectral distribution. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -156,83 +188,79 @@ def XYZ_to_sd(XYZ, method='Meng 2015', **kwargs): *Jakob and Hanika (2009)* reflectance recovery: >>> import numpy as np - >>> from colour.colorimetry import ( - ... MSDS_CMFS_STANDARD_OBSERVER, SDS_ILLUMINANTS, SpectralShape, - ... sd_to_XYZ_integration - ... ) + >>> from colour import MSDS_CMFS, SDS_ILLUMINANTS, SpectralShape + >>> from colour.colorimetry import sd_to_XYZ_integration >>> from colour.utilities import numpy_print_options >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> cmfs = ( - ... MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']. + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SpectralShape(360, 780, 10)) ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) >>> sd = XYZ_to_sd( ... XYZ, method='Jakob 2019', cmfs=cmfs, illuminant=illuminant) >>> with numpy_print_options(suppress=True): - ... # Doctests skip for Python 2.x compatibility. - ... sd # doctest: +SKIP - SpectralDistribution([[ 360. , 0.4853113...], - [ 370. , 0.3229161...], - [ 380. , 0.2131016...], - [ 390. , 0.1473214...], - [ 400. , 0.1081038...], - [ 410. , 0.0838303...], - [ 420. , 0.0681378...], - [ 430. , 0.0576120...], - [ 440. , 0.0503683...], - [ 450. , 0.0453253...], - [ 460. , 0.0418415...], - [ 470. , 0.0395292...], - [ 480. , 0.0381568...], - [ 490. , 0.0375964...], - [ 500. , 0.0377979...], - [ 510. , 0.0387793...], - [ 520. , 0.0406297...], - [ 530. , 0.0435277...], - [ 540. , 0.0477792...], - [ 550. , 0.0538903...], - [ 560. , 0.0627048...], - [ 570. , 0.0756689...], - [ 580. , 0.0953554...], - [ 590. , 0.1265094...], - [ 600. , 0.1779719...], - [ 610. , 0.2648611...], - [ 620. , 0.4036392...], - [ 630. , 0.5826025...], - [ 640. , 0.7439192...], - [ 650. , 0.8495306...], - [ 660. , 0.9091674...], - [ 670. , 0.9423310...], - [ 680. , 0.9614969...], - [ 690. , 0.9731491...], - [ 700. , 0.9805843...], - [ 710. , 0.9855336...], - [ 720. , 0.9889492...], - [ 730. , 0.9913796...], - [ 740. , 0.9931546...], - [ 750. , 0.9944803...], - [ 760. , 0.9954898...], - [ 770. , 0.9962715...], - [ 780. , 0.9968859...]], + ... sd # doctest: +ELLIPSIS + SpectralDistribution([[ 360. , 0.4893773...], + [ 370. , 0.3258214...], + [ 380. , 0.2147792...], + [ 390. , 0.1482413...], + [ 400. , 0.1086169...], + [ 410. , 0.0841255...], + [ 420. , 0.0683114...], + [ 430. , 0.0577144...], + [ 440. , 0.0504267...], + [ 450. , 0.0453552...], + [ 460. , 0.0418520...], + [ 470. , 0.0395259...], + [ 480. , 0.0381430...], + [ 490. , 0.0375741...], + [ 500. , 0.0377685...], + [ 510. , 0.0387432...], + [ 520. , 0.0405871...], + [ 530. , 0.0434783...], + [ 540. , 0.0477225...], + [ 550. , 0.0538256...], + [ 560. , 0.0626314...], + [ 570. , 0.0755869...], + [ 580. , 0.0952675...], + [ 590. , 0.1264265...], + [ 600. , 0.1779272...], + [ 610. , 0.2649393...], + [ 620. , 0.4039779...], + [ 630. , 0.5832105...], + [ 640. , 0.7445440...], + [ 650. , 0.8499970...], + [ 660. , 0.9094792...], + [ 670. , 0.9425378...], + [ 680. , 0.9616376...], + [ 690. , 0.9732481...], + [ 700. , 0.9806562...], + [ 710. , 0.9855873...], + [ 720. , 0.9889903...], + [ 730. , 0.9914117...], + [ 740. , 0.9931801...], + [ 750. , 0.9945009...], + [ 760. , 0.9955066...], + [ 770. , 0.9962855...], + [ 780. , 0.9968976...]], interpolator=SpragueInterpolator, interpolator_kwargs={}, extrapolator=Extrapolator, extrapolator_kwargs={...}) >>> sd_to_XYZ_integration(sd, cmfs, illuminant) / 100 # doctest: +ELLIPSIS - array([ 0.2065462..., 0.1220220..., 0.0513514...]) + array([ 0.2066217..., 0.1220128..., 0.0513958...]) *Mallett and Yuksel (2019)* reflectance recovery: >>> cmfs = ( - ... MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']. + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SPECTRAL_SHAPE_sRGB_MALLETT2019) ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) >>> sd = XYZ_to_sd(XYZ, method='Mallett 2019') >>> with numpy_print_options(suppress=True): - ... # Doctests skip for Python 2.x compatibility. - ... sd # doctest: +SKIP + ... sd # doctest: +ELLIPSIS SpectralDistribution([[ 380. , 0.1735531...], [ 385. , 0.1720357...], [ 390. , 0.1677721...], @@ -325,58 +353,57 @@ def XYZ_to_sd(XYZ, method='Meng 2015', **kwargs): *Meng (2015)* reflectance recovery: >>> cmfs = ( - ... MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']. + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SpectralShape(360, 780, 10)) ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) >>> sd = XYZ_to_sd( ... XYZ, method='Meng 2015', cmfs=cmfs, illuminant=illuminant) >>> with numpy_print_options(suppress=True): - ... # Doctests skip for Python 2.x compatibility. ... sd # doctest: +SKIP - SpectralDistribution([[ 360. , 0.0765153...], - [ 370. , 0.0764771...], - [ 380. , 0.0764286...], - [ 390. , 0.0764329...], - [ 400. , 0.0765863...], - [ 410. , 0.0764339...], - [ 420. , 0.0757213...], - [ 430. , 0.0733091...], - [ 440. , 0.0676493...], - [ 450. , 0.0577616...], - [ 460. , 0.0440805...], - [ 470. , 0.0284802...], - [ 480. , 0.0138019...], - [ 490. , 0.0033557...], + SpectralDistribution([[ 360. , 0.0762005...], + [ 370. , 0.0761792...], + [ 380. , 0.0761363...], + [ 390. , 0.0761194...], + [ 400. , 0.0762539...], + [ 410. , 0.0761671...], + [ 420. , 0.0754649...], + [ 430. , 0.0731519...], + [ 440. , 0.0676701...], + [ 450. , 0.0577800...], + [ 460. , 0.0441993...], + [ 470. , 0.0285064...], + [ 480. , 0.0138728...], + [ 490. , 0.0033585...], [ 500. , 0. ...], [ 510. , 0. ...], [ 520. , 0. ...], [ 530. , 0. ...], - [ 540. , 0.0055360...], - [ 550. , 0.0317335...], - [ 560. , 0.075457 ...], - [ 570. , 0.1314930...], - [ 580. , 0.1938219...], - [ 590. , 0.2559747...], - [ 600. , 0.3122869...], - [ 610. , 0.3584363...], - [ 620. , 0.3927112...], - [ 630. , 0.4158866...], - [ 640. , 0.4305832...], - [ 650. , 0.4391142...], - [ 660. , 0.4439484...], - [ 670. , 0.4464121...], - [ 680. , 0.4475718...], - [ 690. , 0.4481182...], - [ 700. , 0.4483734...], - [ 710. , 0.4484743...], - [ 720. , 0.4485753...], - [ 730. , 0.4486474...], - [ 740. , 0.4486629...], - [ 750. , 0.4486995...], - [ 760. , 0.4486925...], - [ 770. , 0.4486794...], - [ 780. , 0.4486982...]], + [ 540. , 0.0055767...], + [ 550. , 0.0317581...], + [ 560. , 0.0754491...], + [ 570. , 0.1314115...], + [ 580. , 0.1937649...], + [ 590. , 0.2559311...], + [ 600. , 0.3123173...], + [ 610. , 0.3584966...], + [ 620. , 0.3927335...], + [ 630. , 0.4159458...], + [ 640. , 0.4306660...], + [ 650. , 0.4391040...], + [ 660. , 0.4439497...], + [ 670. , 0.4463618...], + [ 680. , 0.4474625...], + [ 690. , 0.4479868...], + [ 700. , 0.4482116...], + [ 710. , 0.4482800...], + [ 720. , 0.4483472...], + [ 730. , 0.4484251...], + [ 740. , 0.4484633...], + [ 750. , 0.4485071...], + [ 760. , 0.4484969...], + [ 770. , 0.4484853...], + [ 780. , 0.4485134...]], interpolator=SpragueInterpolator, interpolator_kwargs={}, extrapolator=Extrapolator, @@ -387,15 +414,14 @@ def XYZ_to_sd(XYZ, method='Meng 2015', **kwargs): *Otsu, Yamamoto and Hachisuka (2018)* reflectance recovery: >>> cmfs = ( - ... MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']. + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SPECTRAL_SHAPE_OTSU2018) ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) >>> sd = XYZ_to_sd( ... XYZ, method='Otsu 2018', cmfs=cmfs, illuminant=illuminant) >>> with numpy_print_options(suppress=True): - ... # Doctests skip for Python 2.x compatibility. - ... sd # doctest: +SKIP + ... sd # doctest: +ELLIPSIS SpectralDistribution([[ 380. , 0.0601939...], [ 390. , 0.0568063...], [ 400. , 0.0517429...], @@ -442,23 +468,23 @@ def XYZ_to_sd(XYZ, method='Meng 2015', **kwargs): *Smits (1999)* reflectance recovery: >>> cmfs = ( - ... MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']. + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SpectralShape(360, 780, 10)) ... ) >>> illuminant = SDS_ILLUMINANTS['E'].copy().align(cmfs.shape) >>> sd = XYZ_to_sd(XYZ, method='Smits 1999') >>> with numpy_print_options(suppress=True): ... sd # doctest: +ELLIPSIS - SpectralDistribution([[ 380. , 0.07691923], - [ 417.7778 , 0.0587005 ], - [ 455.5556 , 0.03943195], - [ 493.3333 , 0.03024978], - [ 531.1111 , 0.02750692], - [ 568.8889 , 0.02808645], - [ 606.6667 , 0.34298985], - [ 644.4444 , 0.41185795], - [ 682.2222 , 0.41185795], - [ 720. , 0.41180754]], + SpectralDistribution([[ 380. , 0.0787830...], + [ 417.7778 , 0.0622018...], + [ 455.5556 , 0.0446206...], + [ 493.3333 , 0.0352220...], + [ 531.1111 , 0.0324149...], + [ 568.8889 , 0.0330105...], + [ 606.6667 , 0.3207115...], + [ 644.4444 , 0.3836164...], + [ 682.2222 , 0.3836164...], + [ 720. , 0.3835649...]], interpolator=LinearInterpolator, interpolator_kwargs={}, extrapolator=Extrapolator, @@ -468,6 +494,7 @@ def XYZ_to_sd(XYZ, method='Meng 2015', **kwargs): """ a = as_float_array(XYZ) + method = validate_method(method, XYZ_TO_SD_METHODS) function = XYZ_TO_SD_METHODS[method] @@ -483,32 +510,38 @@ def XYZ_to_sd(XYZ, method='Meng 2015', **kwargs): return function(a, **filter_kwargs(function, **kwargs)) -__all__ += ['XYZ_TO_SD_METHODS', 'XYZ_to_sd'] +__all__ += [ + "XYZ_TO_SD_METHODS", + "XYZ_to_sd", +] # ----------------------------------------------------------------------------# # --- API Changes and Deprecation Management ---# # ----------------------------------------------------------------------------# class recovery(ModuleAPI): - def __getattr__(self, attribute): - return super(recovery, self).__getattr__(attribute) + """Define a class acting like the *recovery* module.""" + + def __getattr__(self, attribute) -> Any: + """Return the value from the attribute with given name.""" + + return super().__getattr__(attribute) -# v0.3.16 +# v0.4.0 API_CHANGES = { - 'ObjectRenamed': [[ - 'colour.recovery.SMITS_1999_SDS', - 'colour.recovery.SDS_SMITS1999', - ], ] + "ObjectRenamed": [ + [ + "colour.recovery.NodeTree_Otsu2018", + "colour.recovery.Tree_Otsu2018", + ], + ] } -""" -Defines *colour.recovery* sub-package API changes. - -API_CHANGES : dict -""" +"""Defines the *colour.recovery* sub-package API changes.""" if not is_documentation_building(): - sys.modules['colour.recovery'] = recovery(sys.modules['colour.recovery'], - build_API_changes(API_CHANGES)) + sys.modules["colour.recovery"] = recovery( # type: ignore[assignment] + sys.modules["colour.recovery"], build_API_changes(API_CHANGES) + ) del ModuleAPI, is_documentation_building, build_API_changes, sys diff --git a/colour/recovery/datasets/__init__.py b/colour/recovery/datasets/__init__.py index 1bf585ffb8..6facaff07e 100644 --- a/colour/recovery/datasets/__init__.py +++ b/colour/recovery/datasets/__init__.py @@ -1,18 +1,25 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - -from .mallett2019 import (SPECTRAL_SHAPE_sRGB_MALLETT2019, - MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019) -from .otsu2018 import (SPECTRAL_SHAPE_OTSU2018, BASIS_FUNCTIONS_OTSU2018, - CLUSTER_MEANS_OTSU2018, SELECTOR_ARRAY_OTSU2018) +from .mallett2019 import ( + SPECTRAL_SHAPE_sRGB_MALLETT2019, + MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019, +) +from .otsu2018 import ( + SPECTRAL_SHAPE_OTSU2018, + BASIS_FUNCTIONS_OTSU2018, + CLUSTER_MEANS_OTSU2018, + SELECTOR_ARRAY_OTSU2018, +) from .smits1999 import SDS_SMITS1999 __all__ = [ - 'SPECTRAL_SHAPE_sRGB_MALLETT2019', 'MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019' + "SPECTRAL_SHAPE_sRGB_MALLETT2019", + "MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019", +] +__all__ += [ + "SPECTRAL_SHAPE_OTSU2018", + "BASIS_FUNCTIONS_OTSU2018", + "CLUSTER_MEANS_OTSU2018", + "SELECTOR_ARRAY_OTSU2018", ] __all__ += [ - 'SPECTRAL_SHAPE_OTSU2018', 'BASIS_FUNCTIONS_OTSU2018', - 'CLUSTER_MEANS_OTSU2018', 'SELECTOR_ARRAY_OTSU2018' + "SDS_SMITS1999", ] -__all__ += ['SDS_SMITS1999'] diff --git a/colour/recovery/datasets/mallett2019.py b/colour/recovery/datasets/mallett2019.py index dbe2bce917..6e2408840c 100644 --- a/colour/recovery/datasets/mallett2019.py +++ b/colour/recovery/datasets/mallett2019.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Mallett and Yuksel (2019) - Reflectance Recovery ================================================ @@ -13,25 +12,27 @@ on Rendering - DL-Only and Industry Track, 7 pages. doi:10.2312/SR.20191216 """ -from __future__ import unicode_literals +from __future__ import annotations import numpy as np from colour.colorimetry import MultiSpectralDistributions, SpectralShape +from colour.hints import NDArray -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'SPECTRAL_SHAPE_sRGB_MALLETT2019', 'DATA_BASIS_FUNCTIONS_sRGB_MALLETT2019', - 'MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019' + "SPECTRAL_SHAPE_sRGB_MALLETT2019", + "DATA_BASIS_FUNCTIONS_sRGB_MALLETT2019", + "MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019", ] -SPECTRAL_SHAPE_sRGB_MALLETT2019 = SpectralShape(380, 780, 5) +SPECTRAL_SHAPE_sRGB_MALLETT2019: SpectralShape = SpectralShape(380, 780, 5) SPECTRAL_SHAPE_sRGB_MALLETT2019.__doc__ = """ Shape for *Mallett and Yuksel (2019)* *sRGB* colourspace basis functions: (380, 780, 5). @@ -39,105 +40,106 @@ References ---------- :cite:`Mallett2019` - -SPECTRAL_SHAPE_sRGB_MALLETT2019 : SpectralShape """ -DATA_BASIS_FUNCTIONS_sRGB_MALLETT2019 = np.array([ - [0.32745741382705500, 0.33186171308587400, 0.34068079154805200], - [0.32375057827054100, 0.32968818775939900, 0.34656118662485200], - [0.31343946125157700, 0.32786002162469700, 0.35870049314035100], - [0.28887938275526500, 0.31917358023175600, 0.39194702658819500], - [0.23920568115888600, 0.29432258369484200, 0.46647173058733300], - [0.18970203689053500, 0.25869706476873600, 0.55160089559860200], - [0.12174606795921800, 0.18889431925476500, 0.68935961094892800], - [0.07457827066946600, 0.12538838199168900, 0.80003334687860700], - [0.04443315863403370, 0.07868706031062170, 0.87687978093531400], - [0.02892863212850290, 0.05314327086594530, 0.91792809744395500], - [0.02231665348475120, 0.04228814603134210, 0.93539520066963200], - [0.01691130729263180, 0.03331834550291710, 0.94977034711518300], - [0.01418110711796670, 0.02975594818597240, 0.95606294480524000], - [0.01305314267748730, 0.03033125053690470, 0.95661560689031600], - [0.01198616362784500, 0.03098857189730070, 0.95702526493132800], - [0.01128871471240480, 0.03168635518883810, 0.95702493053471300], - [0.01090606646565170, 0.03466996150299740, 0.95442397273706600], - [0.01040071348100420, 0.03455195744367500, 0.95504732902020400], - [0.01063736025414650, 0.04068480619482970, 0.94867783309333400], - [0.01090766253377410, 0.05446003736940560, 0.93463229984232800], - [0.01103271244809880, 0.08090528742047370, 0.90806199985226900], - [0.01131065659122680, 0.14634830285704400, 0.84234103946372700], - [0.01115464205694030, 0.37967964329661700, 0.60916571536564700], - [0.01014877040621220, 0.76674426865403300, 0.22310696095953300], - [0.00891858211883843, 0.87621474761337000, 0.11486667029133600], - [0.00768557633847106, 0.91849165561384300, 0.07382276789574370], - [0.00670570828469526, 0.94065556253443700, 0.05263872879105550], - [0.00599580598764424, 0.95373188453302000, 0.04027230901688870], - [0.00553725664234189, 0.96164327984023800, 0.03281946265095910], - [0.00519378424120663, 0.96720001968507800, 0.02760619592704560], - [0.00502536226522334, 0.97098974639004600, 0.02398489112703940], - [0.00513636276967508, 0.97285230356355400, 0.02201133335279220], - [0.00543320026053983, 0.97311659407644400, 0.02145020525599660], - [0.00581998590243535, 0.97335106915414300, 0.02082894450956850], - [0.00640057277462412, 0.97335111554436900, 0.02024831138880870], - [0.00744952868340878, 0.97226107973172500, 0.02028939145120660], - [0.00858363581937657, 0.97335102174691700, 0.01806534233591300], - [0.01039576246516740, 0.97314849518569300, 0.01645574223446850], - [0.01356543353864920, 0.97106130630091400, 0.01537326013409550], - [0.01938451583997420, 0.96637130595518300, 0.01424417848455170], - [0.03208407120200240, 0.95494196750254800, 0.01297396155433470], - [0.07435603784594110, 0.91357898955126100, 0.01206497413452180], - [0.62439372417807500, 0.36434880390768700, 0.01125747816039010], - [0.91831003276872000, 0.07150724254088510, 0.01018272467169420], - [0.94925303017505100, 0.04123043447137510, 0.00951653538723741], - [0.95818783332924600, 0.03242387418366850, 0.00938829272866817], - [0.95818775133269800, 0.03192462979820030, 0.00988761909067028], - [0.95818762508778200, 0.03127603317309690, 0.01053634200645890], - [0.95567906077174600, 0.03263037042905740, 0.01169056883744480], - [0.95800615489342900, 0.02953087214907390, 0.01246297288710370], - [0.95410157345656400, 0.03156176117024640, 0.01433666517742030], - [0.94760760623723700, 0.03567421827082040, 0.01671817532754430], - [0.93868132844754900, 0.04140300539556730, 0.01991566607500250], - [0.92446668275143400, 0.05060426044895610, 0.02492905616328100], - [0.90460602533305600, 0.06343430038170030, 0.03195967358604020], - [0.88041219892793300, 0.07891824529392290, 0.04066955409524840], - [0.84778787315170000, 0.09954274266537470, 0.05266938242193960], - [0.80577912662301900, 0.12559576009328700, 0.06862511051419470], - [0.75253185387142100, 0.15759091044168000, 0.08987723230001360], - [0.68643939684457800, 0.19539823904421000, 0.11816235892643400], - [0.61869457086061000, 0.23147447477217800, 0.14983094744213300], - [0.54026444395911100, 0.26885213609526200, 0.19088340934183400], - [0.47296441629383800, 0.29602916421792800, 0.23100640302521700], - [0.43270159670404900, 0.30975499444194500, 0.25754338542220200], - [0.40535804552839200, 0.31781588338382200, 0.27682603872153600], - [0.38549183497490200, 0.32299034738989800, 0.29151777281079500], - [0.37098358455106100, 0.32635384793800900, 0.30266250608323300], - [0.35760870152308100, 0.32914390227898000, 0.31324730130288600], - [0.34871280010839300, 0.33080872680368200, 0.32047832512463300], - [0.34488011934469100, 0.33148268992224300, 0.32363699470796100], - [0.34191787732329100, 0.33198455035238900, 0.32609730884690000], - [0.33953109298712900, 0.33234117252254500, 0.32812736934018400], - [0.33716950377436700, 0.33291200941553900, 0.32991797595888800], - [0.33617201852771700, 0.33291927969521400, 0.33090790121664900], - [0.33516744343336300, 0.33302767257885600, 0.33180363309599500], - [0.33442162530646300, 0.33317970467326000, 0.33239662725536100], - [0.33400876037640200, 0.33324703097454900, 0.33274078072682400], - [0.33391579279008200, 0.33325934921060100, 0.33282085708148900], - [0.33381845494636700, 0.33327505027938300, 0.33290173128344400], - [0.33367277492845600, 0.33329432844873200, 0.33302596748863200], - [0.33356951340559100, 0.33330942495777500, 0.33311108308149700], -]) +DATA_BASIS_FUNCTIONS_sRGB_MALLETT2019: NDArray = np.array( + [ + [0.32745741382705500, 0.33186171308587400, 0.34068079154805200], + [0.32375057827054100, 0.32968818775939900, 0.34656118662485200], + [0.31343946125157700, 0.32786002162469700, 0.35870049314035100], + [0.28887938275526500, 0.31917358023175600, 0.39194702658819500], + [0.23920568115888600, 0.29432258369484200, 0.46647173058733300], + [0.18970203689053500, 0.25869706476873600, 0.55160089559860200], + [0.12174606795921800, 0.18889431925476500, 0.68935961094892800], + [0.07457827066946600, 0.12538838199168900, 0.80003334687860700], + [0.04443315863403370, 0.07868706031062170, 0.87687978093531400], + [0.02892863212850290, 0.05314327086594530, 0.91792809744395500], + [0.02231665348475120, 0.04228814603134210, 0.93539520066963200], + [0.01691130729263180, 0.03331834550291710, 0.94977034711518300], + [0.01418110711796670, 0.02975594818597240, 0.95606294480524000], + [0.01305314267748730, 0.03033125053690470, 0.95661560689031600], + [0.01198616362784500, 0.03098857189730070, 0.95702526493132800], + [0.01128871471240480, 0.03168635518883810, 0.95702493053471300], + [0.01090606646565170, 0.03466996150299740, 0.95442397273706600], + [0.01040071348100420, 0.03455195744367500, 0.95504732902020400], + [0.01063736025414650, 0.04068480619482970, 0.94867783309333400], + [0.01090766253377410, 0.05446003736940560, 0.93463229984232800], + [0.01103271244809880, 0.08090528742047370, 0.90806199985226900], + [0.01131065659122680, 0.14634830285704400, 0.84234103946372700], + [0.01115464205694030, 0.37967964329661700, 0.60916571536564700], + [0.01014877040621220, 0.76674426865403300, 0.22310696095953300], + [0.00891858211883843, 0.87621474761337000, 0.11486667029133600], + [0.00768557633847106, 0.91849165561384300, 0.07382276789574370], + [0.00670570828469526, 0.94065556253443700, 0.05263872879105550], + [0.00599580598764424, 0.95373188453302000, 0.04027230901688870], + [0.00553725664234189, 0.96164327984023800, 0.03281946265095910], + [0.00519378424120663, 0.96720001968507800, 0.02760619592704560], + [0.00502536226522334, 0.97098974639004600, 0.02398489112703940], + [0.00513636276967508, 0.97285230356355400, 0.02201133335279220], + [0.00543320026053983, 0.97311659407644400, 0.02145020525599660], + [0.00581998590243535, 0.97335106915414300, 0.02082894450956850], + [0.00640057277462412, 0.97335111554436900, 0.02024831138880870], + [0.00744952868340878, 0.97226107973172500, 0.02028939145120660], + [0.00858363581937657, 0.97335102174691700, 0.01806534233591300], + [0.01039576246516740, 0.97314849518569300, 0.01645574223446850], + [0.01356543353864920, 0.97106130630091400, 0.01537326013409550], + [0.01938451583997420, 0.96637130595518300, 0.01424417848455170], + [0.03208407120200240, 0.95494196750254800, 0.01297396155433470], + [0.07435603784594110, 0.91357898955126100, 0.01206497413452180], + [0.62439372417807500, 0.36434880390768700, 0.01125747816039010], + [0.91831003276872000, 0.07150724254088510, 0.01018272467169420], + [0.94925303017505100, 0.04123043447137510, 0.00951653538723741], + [0.95818783332924600, 0.03242387418366850, 0.00938829272866817], + [0.95818775133269800, 0.03192462979820030, 0.00988761909067028], + [0.95818762508778200, 0.03127603317309690, 0.01053634200645890], + [0.95567906077174600, 0.03263037042905740, 0.01169056883744480], + [0.95800615489342900, 0.02953087214907390, 0.01246297288710370], + [0.95410157345656400, 0.03156176117024640, 0.01433666517742030], + [0.94760760623723700, 0.03567421827082040, 0.01671817532754430], + [0.93868132844754900, 0.04140300539556730, 0.01991566607500250], + [0.92446668275143400, 0.05060426044895610, 0.02492905616328100], + [0.90460602533305600, 0.06343430038170030, 0.03195967358604020], + [0.88041219892793300, 0.07891824529392290, 0.04066955409524840], + [0.84778787315170000, 0.09954274266537470, 0.05266938242193960], + [0.80577912662301900, 0.12559576009328700, 0.06862511051419470], + [0.75253185387142100, 0.15759091044168000, 0.08987723230001360], + [0.68643939684457800, 0.19539823904421000, 0.11816235892643400], + [0.61869457086061000, 0.23147447477217800, 0.14983094744213300], + [0.54026444395911100, 0.26885213609526200, 0.19088340934183400], + [0.47296441629383800, 0.29602916421792800, 0.23100640302521700], + [0.43270159670404900, 0.30975499444194500, 0.25754338542220200], + [0.40535804552839200, 0.31781588338382200, 0.27682603872153600], + [0.38549183497490200, 0.32299034738989800, 0.29151777281079500], + [0.37098358455106100, 0.32635384793800900, 0.30266250608323300], + [0.35760870152308100, 0.32914390227898000, 0.31324730130288600], + [0.34871280010839300, 0.33080872680368200, 0.32047832512463300], + [0.34488011934469100, 0.33148268992224300, 0.32363699470796100], + [0.34191787732329100, 0.33198455035238900, 0.32609730884690000], + [0.33953109298712900, 0.33234117252254500, 0.32812736934018400], + [0.33716950377436700, 0.33291200941553900, 0.32991797595888800], + [0.33617201852771700, 0.33291927969521400, 0.33090790121664900], + [0.33516744343336300, 0.33302767257885600, 0.33180363309599500], + [0.33442162530646300, 0.33317970467326000, 0.33239662725536100], + [0.33400876037640200, 0.33324703097454900, 0.33274078072682400], + [0.33391579279008200, 0.33325934921060100, 0.33282085708148900], + [0.33381845494636700, 0.33327505027938300, 0.33290173128344400], + [0.33367277492845600, 0.33329432844873200, 0.33302596748863200], + [0.33356951340559100, 0.33330942495777500, 0.33311108308149700], + ] +) -MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019 = MultiSpectralDistributions( - DATA_BASIS_FUNCTIONS_sRGB_MALLETT2019, - SPECTRAL_SHAPE_sRGB_MALLETT2019.range(), - name='Basis Functions - sRGB - Mallett 2019', - labels=('red', 'green', 'blue')) +MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019: MultiSpectralDistributions = ( + MultiSpectralDistributions( + DATA_BASIS_FUNCTIONS_sRGB_MALLETT2019, + SPECTRAL_SHAPE_sRGB_MALLETT2019.range(), + name="Basis Functions - sRGB - Mallett 2019", + labels=("red", "green", "blue"), + ) +) """ *Mallett and Yuksel (2019)* basis functions for the *sRGB* colourspace. References ---------- :cite:`Mallett2019` - -MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019 : MultiSpectralDistributions """ diff --git a/colour/recovery/datasets/otsu2018.py b/colour/recovery/datasets/otsu2018.py index 2ffa47f0ea..3eec080745 100644 --- a/colour/recovery/datasets/otsu2018.py +++ b/colour/recovery/datasets/otsu2018.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Otsu et al. (2018) - Reflectance Recovery ========================================= @@ -13,1283 +12,1287 @@ Graphics Forum, 37(6), 370-381. doi:10.1111/cgf.13332 """ -from __future__ import unicode_literals +from __future__ import annotations import numpy as np + from colour.colorimetry import SpectralShape +from colour.hints import NDArray -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'SPECTRAL_SHAPE_OTSU2018', 'BASIS_FUNCTIONS_OTSU2018', - 'CLUSTER_MEANS_OTSU2018', 'SELECTOR_ARRAY_OTSU2018' + "SPECTRAL_SHAPE_OTSU2018", + "BASIS_FUNCTIONS_OTSU2018", + "CLUSTER_MEANS_OTSU2018", + "SELECTOR_ARRAY_OTSU2018", ] -SPECTRAL_SHAPE_OTSU2018 = SpectralShape(380, 730, 10) +SPECTRAL_SHAPE_OTSU2018: SpectralShape = SpectralShape(380, 730, 10) SPECTRAL_SHAPE_OTSU2018.__doc__ = """ The spectral shape of *Otsu et al. (2018)* basis functions and means. References ---------- :cite:`Otsu2018` - -SPECTRAL_SHAPE_OTSU2018 : SpectralShape """ -BASIS_FUNCTIONS_OTSU2018 = np.array([ +BASIS_FUNCTIONS_OTSU2018: NDArray = np.array( [ [ - 0.033359794, - 0.069816766, - 0.145858662, - 0.208587748, - 0.225045781, - 0.230260467, - 0.234686490, - 0.237479132, - 0.237922746, - 0.235743337, - 0.230636966, - 0.224868019, - 0.220538851, - 0.214245998, - 0.205011936, - 0.194449589, - 0.182172137, - 0.166753810, - 0.149673107, - 0.135884660, - 0.127383558, - 0.121562634, - 0.115070543, - 0.110303774, - 0.108658862, - 0.110404095, - 0.114133490, - 0.118971322, - 0.123157804, - 0.123054245, - 0.119622289, - 0.116466163, - 0.114699356, - 0.115051812, - 0.118079600, - 0.125700322, - ], - [ - -0.015712659, - -0.036497435, - -0.083631104, - -0.123972215, - -0.129432141, - -0.113757935, - -0.074978412, - -0.023837819, - 0.051049315, - 0.138878712, - 0.218967535, - 0.278285048, - 0.297475087, - 0.286292061, - 0.257830950, - 0.206696101, - 0.144324772, - 0.085519078, - 0.038286146, - 0.001619797, - -0.035117557, - -0.072027247, - -0.096533704, - -0.103087741, - -0.102368364, - -0.114391855, - -0.139967122, - -0.171162687, - -0.196658516, - -0.207454048, - -0.204801733, - -0.203879004, - -0.213328289, - -0.228958920, - -0.241698598, - -0.251464790, + [ + 0.033359794, + 0.069816766, + 0.145858662, + 0.208587748, + 0.225045781, + 0.230260467, + 0.234686490, + 0.237479132, + 0.237922746, + 0.235743337, + 0.230636966, + 0.224868019, + 0.220538851, + 0.214245998, + 0.205011936, + 0.194449589, + 0.182172137, + 0.166753810, + 0.149673107, + 0.135884660, + 0.127383558, + 0.121562634, + 0.115070543, + 0.110303774, + 0.108658862, + 0.110404095, + 0.114133490, + 0.118971322, + 0.123157804, + 0.123054245, + 0.119622289, + 0.116466163, + 0.114699356, + 0.115051812, + 0.118079600, + 0.125700322, + ], + [ + -0.015712659, + -0.036497435, + -0.083631104, + -0.123972215, + -0.129432141, + -0.113757935, + -0.074978412, + -0.023837819, + 0.051049315, + 0.138878712, + 0.218967535, + 0.278285048, + 0.297475087, + 0.286292061, + 0.257830950, + 0.206696101, + 0.144324772, + 0.085519078, + 0.038286146, + 0.001619797, + -0.035117557, + -0.072027247, + -0.096533704, + -0.103087741, + -0.102368364, + -0.114391855, + -0.139967122, + -0.171162687, + -0.196658516, + -0.207454048, + -0.204801733, + -0.203879004, + -0.213328289, + -0.228958920, + -0.241698598, + -0.251464790, + ], + [ + -0.020337631, + -0.053622913, + -0.122405706, + -0.179462494, + -0.200564443, + -0.232332240, + -0.283247005, + -0.304278573, + -0.276059936, + -0.220589316, + -0.139053387, + -0.046042855, + 0.031991873, + 0.103372660, + 0.170370407, + 0.214905881, + 0.240971387, + 0.254238542, + 0.255631209, + 0.238008218, + 0.209534818, + 0.177808852, + 0.153095310, + 0.139956117, + 0.131477876, + 0.116280805, + 0.095933679, + 0.075698905, + 0.065977800, + 0.069857488, + 0.082988360, + 0.090329558, + 0.082929925, + 0.067734041, + 0.053242860, + 0.041115251, + ], ], [ - -0.020337631, - -0.053622913, - -0.122405706, - -0.179462494, - -0.200564443, - -0.232332240, - -0.283247005, - -0.304278573, - -0.276059936, - -0.220589316, - -0.139053387, - -0.046042855, - 0.031991873, - 0.103372660, - 0.170370407, - 0.214905881, - 0.240971387, - 0.254238542, - 0.255631209, - 0.238008218, - 0.209534818, - 0.177808852, - 0.153095310, - 0.139956117, - 0.131477876, - 0.116280805, - 0.095933679, - 0.075698905, - 0.065977800, - 0.069857488, - 0.082988360, - 0.090329558, - 0.082929925, - 0.067734041, - 0.053242860, - 0.041115251, + [ + -0.02257427717386271300, + -0.04580454714541371000, + -0.08954033658115419200, + -0.12005530771055017000, + -0.12563459701293636000, + -0.12595333439645837000, + -0.12670444234219508000, + -0.12715705435395483000, + -0.12913057707506276000, + -0.13354123002336557000, + -0.13855666514343304000, + -0.14308799991330715000, + -0.14951791383958515000, + -0.15845145652482151000, + -0.16495787527646999000, + -0.16781427384520564000, + -0.17170232277018521000, + -0.18042142403466258000, + -0.18962359564086589000, + -0.19407674590552579000, + -0.19587634795792669000, + -0.19670397522552724000, + -0.19619975275648865000, + -0.19518165653001343000, + -0.19403913994258670000, + -0.19368980706846228000, + -0.19277035941743720000, + -0.19234347800755142000, + -0.19207998789953343000, + -0.19183499892170250000, + -0.19191989052413799000, + -0.19216208684132857000, + -0.19273004434759020000, + -0.19273134081681903000, + -0.19197345910434815000, + -0.19195334021916627000, + ], + [ + -0.02190524719876366800, + -0.04789585619670318200, + -0.10618234949979000000, + -0.15900405113964777000, + -0.17702417653425023000, + -0.18536131560391853000, + -0.19315079255300505000, + -0.20224319049559464000, + -0.21765026428606477000, + -0.24359160942259997000, + -0.27299579285349473000, + -0.29049279880094941000, + -0.28346659709985672000, + -0.24283365807615256000, + -0.19188028232791318000, + -0.15475085829598653000, + -0.12084943547312378000, + -0.07129023972829869000, + -0.01545410159994778000, + 0.02855797827859832300, + 0.06332656026225293200, + 0.09556345974296330500, + 0.12304807393347031000, + 0.14150866887002808000, + 0.15188057104091590000, + 0.15797342142055862000, + 0.16213144452203984000, + 0.16563351215078684000, + 0.16485760626112306000, + 0.15991188547906210000, + 0.15381118424011236000, + 0.14657408643195061000, + 0.14252150862995686000, + 0.14149924680385922000, + 0.14426259164189553000, + 0.14568116947965443000, + ], + [ + 0.03862192593327843500, + 0.08607977892966384200, + 0.18758937472970783000, + 0.26380287344461750000, + 0.27264656532342524000, + 0.26415033785924025000, + 0.25431550822206783000, + 0.24061274238989599000, + 0.21646081169191303000, + 0.16375249929345426000, + 0.06540594390641826800, + -0.05748957147471461100, + -0.16090875920779929000, + -0.22967562960590965000, + -0.28665433153182635000, + -0.32394421018597008000, + -0.31885416680273615000, + -0.26443039994661971000, + -0.19096541067598802000, + -0.13794182744763242000, + -0.09283207180313692000, + -0.04863827809054062400, + -0.00705701194648979300, + 0.02590864491028844700, + 0.04516812589249209800, + 0.05543340685273387400, + 0.06264866705777927300, + 0.06999797364036092200, + 0.07285747008320048200, + 0.06852130459855278200, + 0.05932205006228336000, + 0.04734424846797920200, + 0.03456970594041587600, + 0.02854412320123144900, + 0.03157197229148767800, + 0.03359122371504807300, + ], ], - ], - [ [ - -0.02257427717386271300, - -0.04580454714541371000, - -0.08954033658115419200, - -0.12005530771055017000, - -0.12563459701293636000, - -0.12595333439645837000, - -0.12670444234219508000, - -0.12715705435395483000, - -0.12913057707506276000, - -0.13354123002336557000, - -0.13855666514343304000, - -0.14308799991330715000, - -0.14951791383958515000, - -0.15845145652482151000, - -0.16495787527646999000, - -0.16781427384520564000, - -0.17170232277018521000, - -0.18042142403466258000, - -0.18962359564086589000, - -0.19407674590552579000, - -0.19587634795792669000, - -0.19670397522552724000, - -0.19619975275648865000, - -0.19518165653001343000, - -0.19403913994258670000, - -0.19368980706846228000, - -0.19277035941743720000, - -0.19234347800755142000, - -0.19207998789953343000, - -0.19183499892170250000, - -0.19191989052413799000, - -0.19216208684132857000, - -0.19273004434759020000, - -0.19273134081681903000, - -0.19197345910434815000, - -0.19195334021916627000, + [ + -0.02709280096187565900, + -0.05686223110461408000, + -0.10035667073983964000, + -0.11655686561575981000, + -0.11422957067657943000, + -0.11025418465506862000, + -0.10767542796968975000, + -0.10388290237276118000, + -0.10158225848918870000, + -0.10042808265232403000, + -0.09444391157646114800, + -0.08539718671110264900, + -0.08140351716465142600, + -0.07899836366862676700, + -0.07092361786822773400, + -0.06431797115426903000, + -0.06551130033522271900, + -0.07449341517075493100, + -0.08108361889808800300, + -0.09089442129206014300, + -0.12404701968906916000, + -0.16786332728947920000, + -0.20250158743930527000, + -0.22377717318551535000, + -0.23406228761612988000, + -0.24023286395208010000, + -0.24230652318518972000, + -0.24409552662937148000, + -0.24451095295280248000, + -0.24396897026804176000, + -0.24334193238409188000, + -0.24303731113979424000, + -0.24384555693930871000, + -0.24439637959579152000, + -0.24397837395728775000, + -0.24435930927502705000, + ], + [ + 0.02001148601020115100, + 0.04899841658049408600, + 0.09110231699495907600, + 0.10753208990080826000, + 0.09847067341821445800, + 0.09702556509879775600, + 0.10316156120973860000, + 0.10877098463664207000, + 0.11985177404764036000, + 0.13576834462543336000, + 0.14149065311281014000, + 0.13685946675439886000, + 0.13973613295200196000, + 0.14909977184067982000, + 0.14654199509091873000, + 0.14130994015312165000, + 0.14757252813883043000, + 0.18077817300610374000, + 0.21009387986393199000, + 0.23369907594299305000, + 0.29227081282780004000, + 0.31933306627506691000, + 0.26565290061291041000, + 0.17228196686094716000, + 0.07639027259115091500, + -0.00292291840580726410, + -0.06142800318223973200, + -0.10401144539762601000, + -0.13589472821785523000, + -0.15816114518317095000, + -0.17335642699648807000, + -0.18902786670833094000, + -0.20610333417513085000, + -0.22242224746425526000, + -0.23664147979214531000, + -0.24998288325425144000, + ], + [ + -0.01860488852357716800, + -0.06851902564207997000, + -0.21326717698986006000, + -0.30102165576283019000, + -0.31649926306090304000, + -0.30060756567987956000, + -0.27084117252238304000, + -0.23425912778142560000, + -0.20338041193211276000, + -0.17461428657405265000, + -0.14854465986513404000, + -0.12781379545378718000, + -0.11230428006257434000, + -0.08797584482037290600, + -0.05537699619205544600, + -0.03089875066975936200, + -0.02743502714477403700, + -0.01578802100640397700, + -0.00205272748385193100, + 0.01009006831808588100, + 0.08098296928681815400, + 0.21731539216862897000, + 0.31846566331995801000, + 0.32348510302323596000, + 0.26108337233732459000, + 0.18705889148548510000, + 0.11973584972940020000, + 0.06706555745724066000, + 0.03124326526269944500, + 0.00792265230294038160, + -0.00379962533995196120, + -0.01980851148987728500, + -0.03821643563126132800, + -0.06166276636610668700, + -0.08003315946555962500, + -0.09697997451524445500, + ], ], [ - -0.02190524719876366800, - -0.04789585619670318200, - -0.10618234949979000000, - -0.15900405113964777000, - -0.17702417653425023000, - -0.18536131560391853000, - -0.19315079255300505000, - -0.20224319049559464000, - -0.21765026428606477000, - -0.24359160942259997000, - -0.27299579285349473000, - -0.29049279880094941000, - -0.28346659709985672000, - -0.24283365807615256000, - -0.19188028232791318000, - -0.15475085829598653000, - -0.12084943547312378000, - -0.07129023972829869000, - -0.01545410159994778000, - 0.02855797827859832300, - 0.06332656026225293200, - 0.09556345974296330500, - 0.12304807393347031000, - 0.14150866887002808000, - 0.15188057104091590000, - 0.15797342142055862000, - 0.16213144452203984000, - 0.16563351215078684000, - 0.16485760626112306000, - 0.15991188547906210000, - 0.15381118424011236000, - 0.14657408643195061000, - 0.14252150862995686000, - 0.14149924680385922000, - 0.14426259164189553000, - 0.14568116947965443000, + [ + 0.02676824304188611300, + 0.05946407475965782100, + 0.12900702308575138000, + 0.17092577830538522000, + 0.17433273953929146000, + 0.16835540326878368000, + 0.15826424805464265000, + 0.14647224313786575000, + 0.13460263040798950000, + 0.12159264960543423000, + 0.10837126623117754000, + 0.09545938036189148500, + 0.08454657372677823500, + 0.07503059715637522300, + 0.06496740345758979200, + 0.05638854309961657100, + 0.05408497845708886600, + 0.05543735257043472300, + 0.05555858715239536000, + 0.05759583879709017500, + 0.06345289056498749900, + 0.07321840953073149900, + 0.09394017460665737800, + 0.12959719899113231000, + 0.17348415950453580000, + 0.21285437318563405000, + 0.23979589047651886000, + 0.25571215090479932000, + 0.26147369410179094000, + 0.26060735062830592000, + 0.25695755826517525000, + 0.25495084519503508000, + 0.25508140335236734000, + 0.25382248374599431000, + 0.25315232973604851000, + 0.25321847259615987000, + ], + [ + -0.01873744847940216800, + -0.05990899326442069400, + -0.13587937041765039000, + -0.19250932779711649000, + -0.21685711379342484000, + -0.23833290753023634000, + -0.25940907727956292000, + -0.27129217353501439000, + -0.27152046701232635000, + -0.25903073272885363000, + -0.23448070616812938000, + -0.19749538078381751000, + -0.16551798851884006000, + -0.14072688005214101000, + -0.11914770422601269000, + -0.11063268885678886000, + -0.10948247432054088000, + -0.10943336389685750000, + -0.10553405777913306000, + -0.10897919714631589000, + -0.11556837510924602000, + -0.12678816534296078000, + -0.12909988927091548000, + -0.09285888955799707900, + -0.00949866580609674310, + 0.06301250034760524900, + 0.10646985745101370000, + 0.12256333095253447000, + 0.13523848767144181000, + 0.14386501113297953000, + 0.15441065150742014000, + 0.16682910934595863000, + 0.17982703518357265000, + 0.19629872698899911000, + 0.20993169814916909000, + 0.22283191255022117000, + ], + [ + 0.00687799096172833900, + 0.00632982236190463440, + -0.01630024725336848600, + -0.05415778649574832400, + -0.07289153491194883000, + -0.08545489000605219200, + -0.09046789436531833800, + -0.08072015476785890400, + -0.07025393562283961700, + -0.05667429771892082400, + -0.04043014967453874300, + -0.01876275509068558700, + -0.00248907267489111750, + 0.00499985179874099400, + 0.00719666613671487470, + 0.00141123759501328960, + 0.00138660492172908220, + 0.00412730855466753730, + 0.00492544274519941470, + 0.00863927652266950270, + 0.01768264714224524300, + 0.02700767827843724200, + 0.05358757306348460600, + 0.12515958723848053000, + 0.24428784772611917000, + 0.34242457928205289000, + 0.38094858322069408000, + 0.35070697286866737000, + 0.25959544573108218000, + 0.13920978087352909000, + 0.00655712404118758820, + -0.11671906463074669000, + -0.21483468555348154000, + -0.28912185847014771000, + -0.34279486721072366000, + -0.38307271805096821000, + ], ], [ - 0.03862192593327843500, - 0.08607977892966384200, - 0.18758937472970783000, - 0.26380287344461750000, - 0.27264656532342524000, - 0.26415033785924025000, - 0.25431550822206783000, - 0.24061274238989599000, - 0.21646081169191303000, - 0.16375249929345426000, - 0.06540594390641826800, - -0.05748957147471461100, - -0.16090875920779929000, - -0.22967562960590965000, - -0.28665433153182635000, - -0.32394421018597008000, - -0.31885416680273615000, - -0.26443039994661971000, - -0.19096541067598802000, - -0.13794182744763242000, - -0.09283207180313692000, - -0.04863827809054062400, - -0.00705701194648979300, - 0.02590864491028844700, - 0.04516812589249209800, - 0.05543340685273387400, - 0.06264866705777927300, - 0.06999797364036092200, - 0.07285747008320048200, - 0.06852130459855278200, - 0.05932205006228336000, - 0.04734424846797920200, - 0.03456970594041587600, - 0.02854412320123144900, - 0.03157197229148767800, - 0.03359122371504807300, + [ + -0.02119674836738755600, + -0.04591441602701418200, + -0.10481010401668686000, + -0.16258395638429893000, + -0.17889545337044299000, + -0.18072228226884079000, + -0.18112664009792892000, + -0.18032434720631510000, + -0.17971618615047016000, + -0.17871153535087211000, + -0.17563285652502023000, + -0.17119808391063290000, + -0.16853689988040146000, + -0.16561345617512713000, + -0.16019419889858222000, + -0.15573208563516067000, + -0.15491795255226662000, + -0.15656042280911464000, + -0.15661531826131711000, + -0.15764891623922786000, + -0.16300024791746612000, + -0.16902407524171240000, + -0.17285036097124651000, + -0.17473874057599853000, + -0.17535320823704051000, + -0.17679028377314515000, + -0.17819373966686389000, + -0.18018711283913955000, + -0.18137104389357972000, + -0.18095677225518114000, + -0.18017559421686788000, + -0.17972235196479699000, + -0.17972279615663084000, + -0.17977929580104804000, + -0.17996014494612850000, + -0.18116676892356900000, + ], + [ + 0.00361061838890439770, + 0.00403832244766750420, + 0.00890962969648889660, + 0.03524815707980788500, + 0.05694248722530626000, + 0.07284328219269969900, + 0.08929685703045094100, + 0.10884229731927056000, + 0.12746765305849969000, + 0.14422870726029338000, + 0.16634052273914524000, + 0.18968300551257519000, + 0.20235756903744331000, + 0.21187377992143250000, + 0.23160746600613533000, + 0.24467573264157011000, + 0.23387224613948968000, + 0.20772756148326568000, + 0.18558478756428221000, + 0.15537257935462540000, + 0.08870855418533021800, + 0.01177297349282160400, + -0.05488997670980050300, + -0.10700795151145540000, + -0.14115288511720003000, + -0.16117542399322698000, + -0.17218230632555048000, + -0.18073298721042264000, + -0.18906539182769619000, + -0.20004311114038048000, + -0.21240795927734304000, + -0.22370131442259125000, + -0.22996533792354246000, + -0.23257699977177257000, + -0.23162059375757346000, + -0.22709792076398444000, + ], + [ + 0.02648512192955599500, + 0.06258688757724231700, + 0.14234632723786383000, + 0.21547368046916726000, + 0.24347885216491971000, + 0.24958734564816068000, + 0.24081448101006372000, + 0.22418376109960433000, + 0.19465144188536176000, + 0.15445250087507756000, + 0.11514475901976122000, + 0.07351250327715834500, + 0.02535169523252229400, + -0.02399041639806054300, + -0.05483824103107217500, + -0.06922149547192454900, + -0.09331536945909434100, + -0.14508948941010041000, + -0.19321726430075248000, + -0.22742341157660259000, + -0.27490607958077012000, + -0.29854536089141326000, + -0.28507620653536159000, + -0.25817167239338712000, + -0.23466391476319501000, + -0.19576301983826125000, + -0.13733990379390060000, + -0.07219380245437045900, + -0.01490762225910524300, + 0.02545250361032095800, + 0.04811592885693794000, + 0.06542845020422376200, + 0.08455968994091871100, + 0.10445141018651627000, + 0.12195299963801620000, + 0.14191426767047791000, + ], ], - ], - [ [ - -0.02709280096187565900, - -0.05686223110461408000, - -0.10035667073983964000, - -0.11655686561575981000, - -0.11422957067657943000, - -0.11025418465506862000, - -0.10767542796968975000, - -0.10388290237276118000, - -0.10158225848918870000, - -0.10042808265232403000, - -0.09444391157646114800, - -0.08539718671110264900, - -0.08140351716465142600, - -0.07899836366862676700, - -0.07092361786822773400, - -0.06431797115426903000, - -0.06551130033522271900, - -0.07449341517075493100, - -0.08108361889808800300, - -0.09089442129206014300, - -0.12404701968906916000, - -0.16786332728947920000, - -0.20250158743930527000, - -0.22377717318551535000, - -0.23406228761612988000, - -0.24023286395208010000, - -0.24230652318518972000, - -0.24409552662937148000, - -0.24451095295280248000, - -0.24396897026804176000, - -0.24334193238409188000, - -0.24303731113979424000, - -0.24384555693930871000, - -0.24439637959579152000, - -0.24397837395728775000, - -0.24435930927502705000, + [ + 0.02949905076348713400, + 0.05380114492830617700, + 0.08079555140864556900, + 0.08673461177222968600, + 0.08228344477860428900, + 0.07805333953848162000, + 0.07658066145809679100, + 0.07426332465408649900, + 0.07368750818425572100, + 0.07497674474470043800, + 0.07186747732133795500, + 0.06522606040102924000, + 0.06403448204208103700, + 0.06747959315712472000, + 0.06420025096863182800, + 0.05857099606594507600, + 0.06154483368131926500, + 0.08432979913189758700, + 0.12590749478955982000, + 0.16082638200698035000, + 0.19171530712010598000, + 0.21543531303958838000, + 0.22759452497177649000, + 0.23276904041357468000, + 0.23427367585435840000, + 0.23511801139892841000, + 0.23630540140724776000, + 0.23765549341378889000, + 0.23815849278526871000, + 0.23735655259306632000, + 0.23776190401926256000, + 0.23901602361789720000, + 0.23966837305669569000, + 0.23992004299222611000, + 0.23963837239716360000, + 0.24055974593482360000, + ], + [ + -0.05749317690390509700, + -0.10889049665477113000, + -0.16808106208696935000, + -0.18188449289155134000, + -0.17213928808785378000, + -0.16248122054252026000, + -0.15940007865141995000, + -0.15446135954382603000, + -0.15394359073226441000, + -0.15955177134499235000, + -0.15476803378874412000, + -0.14270086430739456000, + -0.14150192246537266000, + -0.15299122361377698000, + -0.14659024215186020000, + -0.13280364521210400000, + -0.13654216732616947000, + -0.18711606479886939000, + -0.27646809095728458000, + -0.33376341079739913000, + -0.31149240794382088000, + -0.21605192415199054000, + -0.08872261441759031500, + 0.01501716445322282900, + 0.07459564202244076100, + 0.10463473412925806000, + 0.12001248564282355000, + 0.12979112919629543000, + 0.13792378105731262000, + 0.14268404174686664000, + 0.14677817922155648000, + 0.15510473763112062000, + 0.16050632288055705000, + 0.16731616487345491000, + 0.17324998571439973000, + 0.17991527864215207000, + ], + [ + -0.08244436089607173900, + -0.15336030301728437000, + -0.24426622711321333000, + -0.26384931585866828000, + -0.24892857366446980000, + -0.23077245427661355000, + -0.22241952671928703000, + -0.20354553247501470000, + -0.19616122335466987000, + -0.19079938068965510000, + -0.17229835840027194000, + -0.14208870523710324000, + -0.12630253767591793000, + -0.10363604991038586000, + -0.07201968219532281500, + -0.05312886843809411200, + -0.04004075771120371000, + -0.00053440568762490803, + 0.16020422722625777000, + 0.40718001627624578000, + 0.41639470243864790000, + 0.27850647135244561000, + 0.14470374762313470000, + 0.04281535650889119900, + -0.01040587995462491900, + -0.03113779622382366000, + -0.03588005442805115200, + -0.03833924733056591500, + -0.03518356199038780200, + -0.02313756381588575800, + -0.02203870658163908600, + -0.02967782008259734300, + -0.02175113614544999000, + -0.01613469018407163300, + -0.01367555588545224900, + -0.01158219640370229700, + ], ], [ - 0.02001148601020115100, - 0.04899841658049408600, - 0.09110231699495907600, - 0.10753208990080826000, - 0.09847067341821445800, - 0.09702556509879775600, - 0.10316156120973860000, - 0.10877098463664207000, - 0.11985177404764036000, - 0.13576834462543336000, - 0.14149065311281014000, - 0.13685946675439886000, - 0.13973613295200196000, - 0.14909977184067982000, - 0.14654199509091873000, - 0.14130994015312165000, - 0.14757252813883043000, - 0.18077817300610374000, - 0.21009387986393199000, - 0.23369907594299305000, - 0.29227081282780004000, - 0.31933306627506691000, - 0.26565290061291041000, - 0.17228196686094716000, - 0.07639027259115091500, - -0.00292291840580726410, - -0.06142800318223973200, - -0.10401144539762601000, - -0.13589472821785523000, - -0.15816114518317095000, - -0.17335642699648807000, - -0.18902786670833094000, - -0.20610333417513085000, - -0.22242224746425526000, - -0.23664147979214531000, - -0.24998288325425144000, + [ + 0.01870695442590027500, + 0.03569161579037078600, + 0.04809268054013512300, + 0.04888694654858050800, + 0.04578808196032359300, + 0.04344586049591366000, + 0.04325685570994483600, + 0.04222189771723057500, + 0.04055681087575285100, + 0.04028180207781234500, + 0.03760574100567435500, + 0.03341449865332147400, + 0.03052287976995381500, + 0.03007254225309254400, + 0.02810981770811881200, + 0.02523222177779721500, + 0.02538661338781571200, + 0.02866947214354276900, + 0.03807571631578088300, + 0.05772133246350924500, + 0.10170369006245189000, + 0.16294646278543309000, + 0.20902545675000525000, + 0.23926676595591634000, + 0.25599658226130739000, + 0.26216124571088945000, + 0.26294121758247357000, + 0.26471231240392479000, + 0.26430141917656441000, + 0.26240021240987121000, + 0.26487052223495711000, + 0.26522291338114623000, + 0.26660542213939931000, + 0.26541584484337871000, + 0.26390547737136760000, + 0.26275220730492782000, + ], + [ + -0.05925964858380286500, + -0.09295695535933538900, + -0.11130621819722603000, + -0.10992697128373242000, + -0.10115389795872447000, + -0.09595515343251508100, + -0.09677723424692862000, + -0.09606038254348969700, + -0.09736670241206574200, + -0.09992202944918748800, + -0.09704383093180786500, + -0.09002079222538757200, + -0.08673352046691186500, + -0.08557770147265368600, + -0.08064000246969554900, + -0.07913645121578150000, + -0.08271807685423399100, + -0.09807361937813430200, + -0.11457149326572891000, + -0.14765147379188692000, + -0.23874210320885786000, + -0.36798022743083686000, + -0.43048231090135342000, + -0.35215701510596309000, + -0.19641466614055952000, + -0.05913838831484832100, + 0.02635518902441718500, + 0.07834688271614538600, + 0.11384481744795055000, + 0.13409102253165275000, + 0.14769925378529281000, + 0.16322353175604573000, + 0.18252922264663107000, + 0.20305737442069113000, + 0.22128357114965899000, + 0.23621196298998062000, + ], + [ + 0.11512389978331515000, + 0.22413006962808238000, + 0.31954389282025103000, + 0.32586304644263525000, + 0.30397127558670756000, + 0.28352212433907487000, + 0.26492834051113151000, + 0.24455825069511528000, + 0.23050435252217924000, + 0.21847071243995733000, + 0.19137596848708158000, + 0.16019742328929820000, + 0.13917066569959388000, + 0.11837326624126067000, + 0.09003968113096123800, + 0.07053180362500022200, + 0.06633604481687069300, + 0.05751726174543667800, + 0.01088835472471969500, + -0.03853843360012083900, + -0.08254453556203576700, + -0.10202561217467844000, + -0.14265191289777529000, + -0.20811311672905380000, + -0.22597980923018762000, + -0.16968023714155689000, + -0.09188792560239997200, + -0.03476358458153469600, + 0.00581952483262489300, + 0.02773258068377005800, + 0.01150936667521866600, + 0.01175763239248749100, + 0.03672792702540145900, + 0.07368275531823710600, + 0.10450314791523628000, + 0.13170821119526419000, + ], ], [ - -0.01860488852357716800, - -0.06851902564207997000, - -0.21326717698986006000, - -0.30102165576283019000, - -0.31649926306090304000, - -0.30060756567987956000, - -0.27084117252238304000, - -0.23425912778142560000, - -0.20338041193211276000, - -0.17461428657405265000, - -0.14854465986513404000, - -0.12781379545378718000, - -0.11230428006257434000, - -0.08797584482037290600, - -0.05537699619205544600, - -0.03089875066975936200, - -0.02743502714477403700, - -0.01578802100640397700, - -0.00205272748385193100, - 0.01009006831808588100, - 0.08098296928681815400, - 0.21731539216862897000, - 0.31846566331995801000, - 0.32348510302323596000, - 0.26108337233732459000, - 0.18705889148548510000, - 0.11973584972940020000, - 0.06706555745724066000, - 0.03124326526269944500, - 0.00792265230294038160, - -0.00379962533995196120, - -0.01980851148987728500, - -0.03821643563126132800, - -0.06166276636610668700, - -0.08003315946555962500, - -0.09697997451524445500, + [ + -0.01391512397332992000, + -0.02355616580370156800, + -0.03217974689527575100, + -0.03389713623044773900, + -0.03301036281363833200, + -0.03285354371423714800, + -0.03377540548706994800, + -0.03489680631234950300, + -0.03685746938516745000, + -0.04079745564524299400, + -0.04592855526325046600, + -0.05252409573372079900, + -0.06485881641780162600, + -0.08872150998392475100, + -0.12062905249920253000, + -0.14663172901574814000, + -0.16374285973859237000, + -0.17854545670524613000, + -0.19285090669470276000, + -0.20310119935107607000, + -0.20944935602779791000, + -0.21419896526525989000, + -0.21710861272899887000, + -0.21862667803330973000, + -0.22006016766927428000, + -0.22215077344601830000, + -0.22323849303277751000, + -0.22463343282950998000, + -0.22589338966445646000, + -0.22594179947730525000, + -0.22663921592189393000, + -0.22783675444090351000, + -0.22865193500586228000, + -0.22945714446182908000, + -0.22963380305061912000, + -0.23154386034935404000, + ], + [ + -0.02372876400796839600, + -0.03805126826063243200, + -0.04915951914715022100, + -0.04774626206533361100, + -0.04297236442671773000, + -0.03848115601428303600, + -0.03397642565799539900, + -0.02760803110730725800, + -0.02042716476955505300, + -0.00852478908021793260, + 0.01408937245236512900, + 0.04675928332177815100, + 0.09005268401626519100, + 0.15996855175372468000, + 0.27983350884234798000, + 0.40454760673506523000, + 0.46557425740279146000, + 0.42988255821256582000, + 0.30040479908227047000, + 0.16016147242639570000, + 0.06193514273781868000, + -0.00634158525039273980, + -0.05591728808815835100, + -0.08801614369298063600, + -0.10645480712256676000, + -0.11439528123368227000, + -0.12148762118556097000, + -0.12853160184622442000, + -0.13211749271913961000, + -0.13124291058777687000, + -0.12758412629342486000, + -0.12246334197729329000, + -0.11696553437732297000, + -0.11538122270705850000, + -0.11848205894639263000, + -0.12091688597538751000, + ], + [ + 0.13614121087578832000, + 0.22163172747838700000, + 0.29458621263180562000, + 0.30561224752530813000, + 0.29501884928368216000, + 0.28598447595430682000, + 0.28333441324709346000, + 0.27912705630178736000, + 0.27528060133650206000, + 0.27037172224265682000, + 0.25595129646913195000, + 0.23112118692222730000, + 0.20491022536680245000, + 0.18294409783499188000, + 0.09167575728467461400, + -0.03963638360275884000, + -0.11054285410554264000, + -0.07373458712121995500, + 0.02683953896149155600, + 0.06870196457024961600, + 0.05013136970896505500, + 0.02356262876335300100, + 0.00258884870471166790, + -0.00534038674246432950, + -0.01181644034067877400, + -0.01890402997198788200, + -0.02401552609276516200, + -0.03039072884274394300, + -0.03697543824746714300, + -0.04372079068719348500, + -0.05526250446342715500, + -0.06518385184466982100, + -0.07978737224572156300, + -0.09035377751454824700, + -0.09485714223592185700, + -0.09799962802996180200, + ], ], - ], - [ - [ - 0.02676824304188611300, - 0.05946407475965782100, - 0.12900702308575138000, - 0.17092577830538522000, - 0.17433273953929146000, - 0.16835540326878368000, - 0.15826424805464265000, - 0.14647224313786575000, - 0.13460263040798950000, - 0.12159264960543423000, - 0.10837126623117754000, - 0.09545938036189148500, - 0.08454657372677823500, - 0.07503059715637522300, - 0.06496740345758979200, - 0.05638854309961657100, - 0.05408497845708886600, - 0.05543735257043472300, - 0.05555858715239536000, - 0.05759583879709017500, - 0.06345289056498749900, - 0.07321840953073149900, - 0.09394017460665737800, - 0.12959719899113231000, - 0.17348415950453580000, - 0.21285437318563405000, - 0.23979589047651886000, - 0.25571215090479932000, - 0.26147369410179094000, - 0.26060735062830592000, - 0.25695755826517525000, - 0.25495084519503508000, - 0.25508140335236734000, - 0.25382248374599431000, - 0.25315232973604851000, - 0.25321847259615987000, - ], - [ - -0.01873744847940216800, - -0.05990899326442069400, - -0.13587937041765039000, - -0.19250932779711649000, - -0.21685711379342484000, - -0.23833290753023634000, - -0.25940907727956292000, - -0.27129217353501439000, - -0.27152046701232635000, - -0.25903073272885363000, - -0.23448070616812938000, - -0.19749538078381751000, - -0.16551798851884006000, - -0.14072688005214101000, - -0.11914770422601269000, - -0.11063268885678886000, - -0.10948247432054088000, - -0.10943336389685750000, - -0.10553405777913306000, - -0.10897919714631589000, - -0.11556837510924602000, - -0.12678816534296078000, - -0.12909988927091548000, - -0.09285888955799707900, - -0.00949866580609674310, - 0.06301250034760524900, - 0.10646985745101370000, - 0.12256333095253447000, - 0.13523848767144181000, - 0.14386501113297953000, - 0.15441065150742014000, - 0.16682910934595863000, - 0.17982703518357265000, - 0.19629872698899911000, - 0.20993169814916909000, - 0.22283191255022117000, - ], - [ - 0.00687799096172833900, - 0.00632982236190463440, - -0.01630024725336848600, - -0.05415778649574832400, - -0.07289153491194883000, - -0.08545489000605219200, - -0.09046789436531833800, - -0.08072015476785890400, - -0.07025393562283961700, - -0.05667429771892082400, - -0.04043014967453874300, - -0.01876275509068558700, - -0.00248907267489111750, - 0.00499985179874099400, - 0.00719666613671487470, - 0.00141123759501328960, - 0.00138660492172908220, - 0.00412730855466753730, - 0.00492544274519941470, - 0.00863927652266950270, - 0.01768264714224524300, - 0.02700767827843724200, - 0.05358757306348460600, - 0.12515958723848053000, - 0.24428784772611917000, - 0.34242457928205289000, - 0.38094858322069408000, - 0.35070697286866737000, - 0.25959544573108218000, - 0.13920978087352909000, - 0.00655712404118758820, - -0.11671906463074669000, - -0.21483468555348154000, - -0.28912185847014771000, - -0.34279486721072366000, - -0.38307271805096821000, - ], - ], - [ - [ - -0.02119674836738755600, - -0.04591441602701418200, - -0.10481010401668686000, - -0.16258395638429893000, - -0.17889545337044299000, - -0.18072228226884079000, - -0.18112664009792892000, - -0.18032434720631510000, - -0.17971618615047016000, - -0.17871153535087211000, - -0.17563285652502023000, - -0.17119808391063290000, - -0.16853689988040146000, - -0.16561345617512713000, - -0.16019419889858222000, - -0.15573208563516067000, - -0.15491795255226662000, - -0.15656042280911464000, - -0.15661531826131711000, - -0.15764891623922786000, - -0.16300024791746612000, - -0.16902407524171240000, - -0.17285036097124651000, - -0.17473874057599853000, - -0.17535320823704051000, - -0.17679028377314515000, - -0.17819373966686389000, - -0.18018711283913955000, - -0.18137104389357972000, - -0.18095677225518114000, - -0.18017559421686788000, - -0.17972235196479699000, - -0.17972279615663084000, - -0.17977929580104804000, - -0.17996014494612850000, - -0.18116676892356900000, - ], - [ - 0.00361061838890439770, - 0.00403832244766750420, - 0.00890962969648889660, - 0.03524815707980788500, - 0.05694248722530626000, - 0.07284328219269969900, - 0.08929685703045094100, - 0.10884229731927056000, - 0.12746765305849969000, - 0.14422870726029338000, - 0.16634052273914524000, - 0.18968300551257519000, - 0.20235756903744331000, - 0.21187377992143250000, - 0.23160746600613533000, - 0.24467573264157011000, - 0.23387224613948968000, - 0.20772756148326568000, - 0.18558478756428221000, - 0.15537257935462540000, - 0.08870855418533021800, - 0.01177297349282160400, - -0.05488997670980050300, - -0.10700795151145540000, - -0.14115288511720003000, - -0.16117542399322698000, - -0.17218230632555048000, - -0.18073298721042264000, - -0.18906539182769619000, - -0.20004311114038048000, - -0.21240795927734304000, - -0.22370131442259125000, - -0.22996533792354246000, - -0.23257699977177257000, - -0.23162059375757346000, - -0.22709792076398444000, - ], - [ - 0.02648512192955599500, - 0.06258688757724231700, - 0.14234632723786383000, - 0.21547368046916726000, - 0.24347885216491971000, - 0.24958734564816068000, - 0.24081448101006372000, - 0.22418376109960433000, - 0.19465144188536176000, - 0.15445250087507756000, - 0.11514475901976122000, - 0.07351250327715834500, - 0.02535169523252229400, - -0.02399041639806054300, - -0.05483824103107217500, - -0.06922149547192454900, - -0.09331536945909434100, - -0.14508948941010041000, - -0.19321726430075248000, - -0.22742341157660259000, - -0.27490607958077012000, - -0.29854536089141326000, - -0.28507620653536159000, - -0.25817167239338712000, - -0.23466391476319501000, - -0.19576301983826125000, - -0.13733990379390060000, - -0.07219380245437045900, - -0.01490762225910524300, - 0.02545250361032095800, - 0.04811592885693794000, - 0.06542845020422376200, - 0.08455968994091871100, - 0.10445141018651627000, - 0.12195299963801620000, - 0.14191426767047791000, - ], - ], + ] +) +""" +Basis functions for *Otsu et al. (2018)*. This is a list of eight arrays, +with one for entry for each cluster. Each array contains three basis functions, +quantised in accordance with :attr:`colour.recovery.SPECTRAL_SHAPE_OTSU2018` +attribute. + +References +---------- +:cite:`Otsu2018` +""" + +CLUSTER_MEANS_OTSU2018: NDArray = np.array( [ [ - 0.02949905076348713400, - 0.05380114492830617700, - 0.08079555140864556900, - 0.08673461177222968600, - 0.08228344477860428900, - 0.07805333953848162000, - 0.07658066145809679100, - 0.07426332465408649900, - 0.07368750818425572100, - 0.07497674474470043800, - 0.07186747732133795500, - 0.06522606040102924000, - 0.06403448204208103700, - 0.06747959315712472000, - 0.06420025096863182800, - 0.05857099606594507600, - 0.06154483368131926500, - 0.08432979913189758700, - 0.12590749478955982000, - 0.16082638200698035000, - 0.19171530712010598000, - 0.21543531303958838000, - 0.22759452497177649000, - 0.23276904041357468000, - 0.23427367585435840000, - 0.23511801139892841000, - 0.23630540140724776000, - 0.23765549341378889000, - 0.23815849278526871000, - 0.23735655259306632000, - 0.23776190401926256000, - 0.23901602361789720000, - 0.23966837305669569000, - 0.23992004299222611000, - 0.23963837239716360000, - 0.24055974593482360000, - ], - [ - -0.05749317690390509700, - -0.10889049665477113000, - -0.16808106208696935000, - -0.18188449289155134000, - -0.17213928808785378000, - -0.16248122054252026000, - -0.15940007865141995000, - -0.15446135954382603000, - -0.15394359073226441000, - -0.15955177134499235000, - -0.15476803378874412000, - -0.14270086430739456000, - -0.14150192246537266000, - -0.15299122361377698000, - -0.14659024215186020000, - -0.13280364521210400000, - -0.13654216732616947000, - -0.18711606479886939000, - -0.27646809095728458000, - -0.33376341079739913000, - -0.31149240794382088000, - -0.21605192415199054000, - -0.08872261441759031500, - 0.01501716445322282900, - 0.07459564202244076100, - 0.10463473412925806000, - 0.12001248564282355000, - 0.12979112919629543000, - 0.13792378105731262000, - 0.14268404174686664000, - 0.14677817922155648000, - 0.15510473763112062000, - 0.16050632288055705000, - 0.16731616487345491000, - 0.17324998571439973000, - 0.17991527864215207000, + 0.10085069182389943000, + 0.14557836477987415000, + 0.21618955974842774000, + 0.26241761006289305000, + 0.27539660377358477000, + 0.28531383647798736000, + 0.29863773584905656000, + 0.30855169811320765000, + 0.31710716981132059000, + 0.32332276729559734000, + 0.32056880503144641000, + 0.30730465408805036000, + 0.29085635220125783000, + 0.26974641509433944000, + 0.24537761006289302000, + 0.22229106918238989000, + 0.20037320754716983000, + 0.17840641509433955000, + 0.15444679245283027000, + 0.14225157232704402000, + 0.13367911949685529000, + 0.12840981132075477000, + 0.12297345911949682000, + 0.11905270440251571000, + 0.11778465408805038000, + 0.11974377358490564000, + 0.12402981132075469000, + 0.12976943396226415000, + 0.13483974842767291000, + 0.13579132075471703000, + 0.13499685534591194000, + 0.13219408805031446000, + 0.13386704402515726000, + 0.13686591194968545000, + 0.14213257861635226000, + 0.15195597484276738000, ], [ - -0.08244436089607173900, - -0.15336030301728437000, - -0.24426622711321333000, - -0.26384931585866828000, - -0.24892857366446980000, - -0.23077245427661355000, - -0.22241952671928703000, - -0.20354553247501470000, - -0.19616122335466987000, - -0.19079938068965510000, - -0.17229835840027194000, - -0.14208870523710324000, - -0.12630253767591793000, - -0.10363604991038586000, - -0.07201968219532281500, - -0.05312886843809411200, - -0.04004075771120371000, - -0.00053440568762490803, - 0.16020422722625777000, - 0.40718001627624578000, - 0.41639470243864790000, - 0.27850647135244561000, - 0.14470374762313470000, - 0.04281535650889119900, - -0.01040587995462491900, - -0.03113779622382366000, - -0.03588005442805115200, - -0.03833924733056591500, - -0.03518356199038780200, - -0.02313756381588575800, - -0.02203870658163908600, - -0.02967782008259734300, - -0.02175113614544999000, - -0.01613469018407163300, - -0.01367555588545224900, - -0.01158219640370229700, + 0.09973996241240880300, + 0.13832746329562026000, + 0.19263781475547445000, + 0.22097216947810219000, + 0.22491500950364970000, + 0.22584267932481750000, + 0.22858438229562053000, + 0.23144331213138686000, + 0.23944196714598540000, + 0.25520659802554735000, + 0.27474755001824847000, + 0.29176247448175174000, + 0.30942757119707986000, + 0.32833185188321135000, + 0.34152471104014598000, + 0.34492205800000048000, + 0.34524719568978096000, + 0.35006971621532840000, + 0.35226373910948888000, + 0.35257366194525530000, + 0.34635072814963563000, + 0.33788450155109495000, + 0.32730242764963524000, + 0.31817969820072978000, + 0.31196273630291999000, + 0.30864278988686139000, + 0.30511056795255503000, + 0.30259412779197087000, + 0.30141088878467137000, + 0.30166522981386845000, + 0.30447054271897822000, + 0.30566773899999983000, + 0.30979884475912473000, + 0.31087716667883208000, + 0.30918824107664239000, + 0.30983656317883246000, ], - ], - [ [ - 0.01870695442590027500, - 0.03569161579037078600, - 0.04809268054013512300, - 0.04888694654858050800, - 0.04578808196032359300, - 0.04344586049591366000, - 0.04325685570994483600, - 0.04222189771723057500, - 0.04055681087575285100, - 0.04028180207781234500, - 0.03760574100567435500, - 0.03341449865332147400, - 0.03052287976995381500, - 0.03007254225309254400, - 0.02810981770811881200, - 0.02523222177779721500, - 0.02538661338781571200, - 0.02866947214354276900, - 0.03807571631578088300, - 0.05772133246350924500, - 0.10170369006245189000, - 0.16294646278543309000, - 0.20902545675000525000, - 0.23926676595591634000, - 0.25599658226130739000, - 0.26216124571088945000, - 0.26294121758247357000, - 0.26471231240392479000, - 0.26430141917656441000, - 0.26240021240987121000, - 0.26487052223495711000, - 0.26522291338114623000, - 0.26660542213939931000, - 0.26541584484337871000, - 0.26390547737136760000, - 0.26275220730492782000, + 0.10163148148148149000, + 0.13874148148148155000, + 0.18048629629629634000, + 0.19058296296296293000, + 0.18498296296296293000, + 0.17870703703703697000, + 0.17455444444444446000, + 0.16798407407407415000, + 0.16265481481481481000, + 0.15838185185185186000, + 0.14838851851851853000, + 0.13549851851851852000, + 0.12967518518518517000, + 0.12491777777777781000, + 0.11482962962962959000, + 0.10857518518518521000, + 0.11038555555555557000, + 0.11997444444444444000, + 0.12557666666666670000, + 0.13899777777777780000, + 0.18298407407407410000, + 0.24777518518518515000, + 0.30390333333333330000, + 0.34408296296296298000, + 0.37026074074074061000, + 0.38781962962962946000, + 0.39744703703703704000, + 0.40430592592592590000, + 0.40855074074074083000, + 0.41022444444444450000, + 0.41229259259259260000, + 0.41340555555555564000, + 0.41682222222222221000, + 0.41964370370370369000, + 0.42174666666666671000, + 0.42481148148148135000, ], [ - -0.05925964858380286500, - -0.09295695535933538900, - -0.11130621819722603000, - -0.10992697128373242000, - -0.10115389795872447000, - -0.09595515343251508100, - -0.09677723424692862000, - -0.09606038254348969700, - -0.09736670241206574200, - -0.09992202944918748800, - -0.09704383093180786500, - -0.09002079222538757200, - -0.08673352046691186500, - -0.08557770147265368600, - -0.08064000246969554900, - -0.07913645121578150000, - -0.08271807685423399100, - -0.09807361937813430200, - -0.11457149326572891000, - -0.14765147379188692000, - -0.23874210320885786000, - -0.36798022743083686000, - -0.43048231090135342000, - -0.35215701510596309000, - -0.19641466614055952000, - -0.05913838831484832100, - 0.02635518902441718500, - 0.07834688271614538600, - 0.11384481744795055000, - 0.13409102253165275000, - 0.14769925378529281000, - 0.16322353175604573000, - 0.18252922264663107000, - 0.20305737442069113000, - 0.22128357114965899000, - 0.23621196298998062000, + 0.10518588235294116000, + 0.15928000000000003000, + 0.23761058823529413000, + 0.27525764705882355000, + 0.27820000000000000000, + 0.26746470588235294000, + 0.24924588235294115000, + 0.22593176470588236000, + 0.20151176470588236000, + 0.17872352941176470000, + 0.15638352941176470000, + 0.13379058823529413000, + 0.11768588235294120000, + 0.10634235294117647000, + 0.09573411764705883100, + 0.08831411764705882100, + 0.08577882352941176800, + 0.08672941176470587400, + 0.08687764705882353200, + 0.08794588235294117900, + 0.09118941176470588000, + 0.09866235294117646500, + 0.11569764705882354000, + 0.14323176470588234000, + 0.17372588235294120000, + 0.20689176470588236000, + 0.24427647058823532000, + 0.28684470588235295000, + 0.33063882352941182000, + 0.37028352941176473000, + 0.40439058823529417000, + 0.43227882352941183000, + 0.45577764705882357000, + 0.47360352941176476000, + 0.48674117647058823000, + 0.49979882352941163000, ], [ - 0.11512389978331515000, - 0.22413006962808238000, - 0.31954389282025103000, - 0.32586304644263525000, - 0.30397127558670756000, - 0.28352212433907487000, - 0.26492834051113151000, - 0.24455825069511528000, - 0.23050435252217924000, - 0.21847071243995733000, - 0.19137596848708158000, - 0.16019742328929820000, - 0.13917066569959388000, - 0.11837326624126067000, - 0.09003968113096123800, - 0.07053180362500022200, - 0.06633604481687069300, - 0.05751726174543667800, - 0.01088835472471969500, - -0.03853843360012083900, - -0.08254453556203576700, - -0.10202561217467844000, - -0.14265191289777529000, - -0.20811311672905380000, - -0.22597980923018762000, - -0.16968023714155689000, - -0.09188792560239997200, - -0.03476358458153469600, - 0.00581952483262489300, - 0.02773258068377005800, - 0.01150936667521866600, - 0.01175763239248749100, - 0.03672792702540145900, - 0.07368275531823710600, - 0.10450314791523628000, - 0.13170821119526419000, + 0.10779898591549292000, + 0.15870338028169012000, + 0.24335419718309842000, + 0.30154478873239432000, + 0.31312512676056359000, + 0.31187740845070405000, + 0.30963605633802815000, + 0.30475785915492964000, + 0.30064743661971804000, + 0.29648360563380305000, + 0.28867476056338026000, + 0.27758146478873252000, + 0.27091515492957735000, + 0.26409166197183093000, + 0.25295690140845062000, + 0.24406754929577457000, + 0.24216214084507048000, + 0.24517898591549292000, + 0.24370647887323940000, + 0.24752726760563384000, + 0.25809814084507027000, + 0.27048856338028177000, + 0.27946467605633790000, + 0.28564805633802820000, + 0.28950569014084515000, + 0.29394118309859157000, + 0.29796377464788726000, + 0.30271183098591542000, + 0.30628552112676050000, + 0.30746732394366194000, + 0.30875188732394343000, + 0.30856918309859155000, + 0.31044997183098588000, + 0.31218912676056354000, + 0.31383464788732385000, + 0.31724743661971833000, ], - ], - [ [ - -0.01391512397332992000, - -0.02355616580370156800, - -0.03217974689527575100, - -0.03389713623044773900, - -0.03301036281363833200, - -0.03285354371423714800, - -0.03377540548706994800, - -0.03489680631234950300, - -0.03685746938516745000, - -0.04079745564524299400, - -0.04592855526325046600, - -0.05252409573372079900, - -0.06485881641780162600, - -0.08872150998392475100, - -0.12062905249920253000, - -0.14663172901574814000, - -0.16374285973859237000, - -0.17854545670524613000, - -0.19285090669470276000, - -0.20310119935107607000, - -0.20944935602779791000, - -0.21419896526525989000, - -0.21710861272899887000, - -0.21862667803330973000, - -0.22006016766927428000, - -0.22215077344601830000, - -0.22323849303277751000, - -0.22463343282950998000, - -0.22589338966445646000, - -0.22594179947730525000, - -0.22663921592189393000, - -0.22783675444090351000, - -0.22865193500586228000, - -0.22945714446182908000, - -0.22963380305061912000, - -0.23154386034935404000, + 0.08891588235294117800, + 0.11170882352941176000, + 0.13155529411764705000, + 0.13345352941176472000, + 0.12773352941176472000, + 0.12348058823529412000, + 0.12199294117647061000, + 0.11907411764705882000, + 0.11792117647058822000, + 0.11871352941176466000, + 0.11512823529411763000, + 0.10845000000000000000, + 0.10843000000000001000, + 0.11113294117647059000, + 0.10788647058823528000, + 0.10542588235294116000, + 0.11212294117647059000, + 0.13771529411764705000, + 0.18127705882352937000, + 0.22836529411764706000, + 0.28692647058823539000, + 0.35075411764705883000, + 0.39709058823529408000, + 0.42251705882352941000, + 0.43389176470588225000, + 0.43937470588235289000, + 0.44075882352941176000, + 0.44195411764705883000, + 0.44239529411764700000, + 0.44206588235294109000, + 0.44273058823529410000, + 0.44242411764705880000, + 0.44468470588235293000, + 0.44592764705882354000, + 0.44652235294117637000, + 0.44930823529411767000, ], [ - -0.02372876400796839600, - -0.03805126826063243200, - -0.04915951914715022100, - -0.04774626206533361100, - -0.04297236442671773000, - -0.03848115601428303600, - -0.03397642565799539900, - -0.02760803110730725800, - -0.02042716476955505300, - -0.00852478908021793260, - 0.01408937245236512900, - 0.04675928332177815100, - 0.09005268401626519100, - 0.15996855175372468000, - 0.27983350884234798000, - 0.40454760673506523000, - 0.46557425740279146000, - 0.42988255821256582000, - 0.30040479908227047000, - 0.16016147242639570000, - 0.06193514273781868000, - -0.00634158525039273980, - -0.05591728808815835100, - -0.08801614369298063600, - -0.10645480712256676000, - -0.11439528123368227000, - -0.12148762118556097000, - -0.12853160184622442000, - -0.13211749271913961000, - -0.13124291058777687000, - -0.12758412629342486000, - -0.12246334197729329000, - -0.11696553437732297000, - -0.11538122270705850000, - -0.11848205894639263000, - -0.12091688597538751000, + 0.08701444444444445000, + 0.10270333333333331000, + 0.11151777777777777000, + 0.10953777777777778000, + 0.10569999999999999000, + 0.10276222222222224000, + 0.10133111111111109000, + 0.09741555555555554700, + 0.09392333333333334500, + 0.09028111111111110400, + 0.08486222222222221400, + 0.07873666666666664900, + 0.07606111111111110700, + 0.07359000000000001700, + 0.07044000000000000300, + 0.06970444444444444400, + 0.07159777777777778800, + 0.07601444444444444000, + 0.07899666666666667300, + 0.09216222222222221500, + 0.12827333333333332000, + 0.20277777777777778000, + 0.30198333333333338000, + 0.40135111111111121000, + 0.48075777777777773000, + 0.53332888888888896000, + 0.56130111111111125000, + 0.57818000000000003000, + 0.58851111111111121000, + 0.59322111111111120000, + 0.59782555555555550000, + 0.60259555555555566000, + 0.61115000000000008000, + 0.61758111111111114000, + 0.62303444444444445000, + 0.62989222222222208000, ], [ - 0.13614121087578832000, - 0.22163172747838700000, - 0.29458621263180562000, - 0.30561224752530813000, - 0.29501884928368216000, - 0.28598447595430682000, - 0.28333441324709346000, - 0.27912705630178736000, - 0.27528060133650206000, - 0.27037172224265682000, - 0.25595129646913195000, - 0.23112118692222730000, - 0.20491022536680245000, - 0.18294409783499188000, - 0.09167575728467461400, - -0.03963638360275884000, - -0.11054285410554264000, - -0.07373458712121995500, - 0.02683953896149155600, - 0.06870196457024961600, - 0.05013136970896505500, - 0.02356262876335300100, - 0.00258884870471166790, - -0.00534038674246432950, - -0.01181644034067877400, - -0.01890402997198788200, - -0.02401552609276516200, - -0.03039072884274394300, - -0.03697543824746714300, - -0.04372079068719348500, - -0.05526250446342715500, - -0.06518385184466982100, - -0.07978737224572156300, - -0.09035377751454824700, - -0.09485714223592185700, - -0.09799962802996180200, + 0.07023857142857142800, + 0.08111642857142858900, + 0.08923309523809523600, + 0.09028619047619047800, + 0.08905285714285708600, + 0.08834642857142854800, + 0.08952357142857139700, + 0.09056880952380949600, + 0.09293809523809525000, + 0.09693595238095237300, + 0.10173238095238095000, + 0.10764285714285717000, + 0.12443142857142857000, + 0.16565285714285716000, + 0.22232071428571437000, + 0.26490809523809533000, + 0.29603785714285719000, + 0.33891190476190480000, + 0.39374523809523820000, + 0.44306047619047628000, + 0.46590380952380966000, + 0.47824261904761900000, + 0.48273738095238095000, + 0.48290047619047627000, + 0.48206547619047607000, + 0.48232547619047622000, + 0.48180452380952365000, + 0.48200666666666664000, + 0.48249571428571431000, + 0.48179785714285728000, + 0.48414190476190488000, + 0.48353404761904767000, + 0.48836785714285708000, + 0.49024928571428561000, + 0.49035238095238093000, + 0.49339000000000011000, ], - ], -]) -""" -Basis functions for *Otsu et al. (2018)*. This is a list of eight arrays, -with one for entry for each cluster. Each array contains three basis functions, -quantised in accordance with :attr:`colour.recovery.SPECTRAL_SHAPE_OTSU2018` -attribute. - -References ----------- -:cite:`Otsu2018` - -BASIS_FUNCTIONS_OTSU2018 : ndarray -""" - -CLUSTER_MEANS_OTSU2018 = np.array([ - [ - 0.10085069182389943000, - 0.14557836477987415000, - 0.21618955974842774000, - 0.26241761006289305000, - 0.27539660377358477000, - 0.28531383647798736000, - 0.29863773584905656000, - 0.30855169811320765000, - 0.31710716981132059000, - 0.32332276729559734000, - 0.32056880503144641000, - 0.30730465408805036000, - 0.29085635220125783000, - 0.26974641509433944000, - 0.24537761006289302000, - 0.22229106918238989000, - 0.20037320754716983000, - 0.17840641509433955000, - 0.15444679245283027000, - 0.14225157232704402000, - 0.13367911949685529000, - 0.12840981132075477000, - 0.12297345911949682000, - 0.11905270440251571000, - 0.11778465408805038000, - 0.11974377358490564000, - 0.12402981132075469000, - 0.12976943396226415000, - 0.13483974842767291000, - 0.13579132075471703000, - 0.13499685534591194000, - 0.13219408805031446000, - 0.13386704402515726000, - 0.13686591194968545000, - 0.14213257861635226000, - 0.15195597484276738000, - ], - [ - 0.09973996241240880300, - 0.13832746329562026000, - 0.19263781475547445000, - 0.22097216947810219000, - 0.22491500950364970000, - 0.22584267932481750000, - 0.22858438229562053000, - 0.23144331213138686000, - 0.23944196714598540000, - 0.25520659802554735000, - 0.27474755001824847000, - 0.29176247448175174000, - 0.30942757119707986000, - 0.32833185188321135000, - 0.34152471104014598000, - 0.34492205800000048000, - 0.34524719568978096000, - 0.35006971621532840000, - 0.35226373910948888000, - 0.35257366194525530000, - 0.34635072814963563000, - 0.33788450155109495000, - 0.32730242764963524000, - 0.31817969820072978000, - 0.31196273630291999000, - 0.30864278988686139000, - 0.30511056795255503000, - 0.30259412779197087000, - 0.30141088878467137000, - 0.30166522981386845000, - 0.30447054271897822000, - 0.30566773899999983000, - 0.30979884475912473000, - 0.31087716667883208000, - 0.30918824107664239000, - 0.30983656317883246000, - ], - [ - 0.10163148148148149000, - 0.13874148148148155000, - 0.18048629629629634000, - 0.19058296296296293000, - 0.18498296296296293000, - 0.17870703703703697000, - 0.17455444444444446000, - 0.16798407407407415000, - 0.16265481481481481000, - 0.15838185185185186000, - 0.14838851851851853000, - 0.13549851851851852000, - 0.12967518518518517000, - 0.12491777777777781000, - 0.11482962962962959000, - 0.10857518518518521000, - 0.11038555555555557000, - 0.11997444444444444000, - 0.12557666666666670000, - 0.13899777777777780000, - 0.18298407407407410000, - 0.24777518518518515000, - 0.30390333333333330000, - 0.34408296296296298000, - 0.37026074074074061000, - 0.38781962962962946000, - 0.39744703703703704000, - 0.40430592592592590000, - 0.40855074074074083000, - 0.41022444444444450000, - 0.41229259259259260000, - 0.41340555555555564000, - 0.41682222222222221000, - 0.41964370370370369000, - 0.42174666666666671000, - 0.42481148148148135000, - ], - [ - 0.10518588235294116000, - 0.15928000000000003000, - 0.23761058823529413000, - 0.27525764705882355000, - 0.27820000000000000000, - 0.26746470588235294000, - 0.24924588235294115000, - 0.22593176470588236000, - 0.20151176470588236000, - 0.17872352941176470000, - 0.15638352941176470000, - 0.13379058823529413000, - 0.11768588235294120000, - 0.10634235294117647000, - 0.09573411764705883100, - 0.08831411764705882100, - 0.08577882352941176800, - 0.08672941176470587400, - 0.08687764705882353200, - 0.08794588235294117900, - 0.09118941176470588000, - 0.09866235294117646500, - 0.11569764705882354000, - 0.14323176470588234000, - 0.17372588235294120000, - 0.20689176470588236000, - 0.24427647058823532000, - 0.28684470588235295000, - 0.33063882352941182000, - 0.37028352941176473000, - 0.40439058823529417000, - 0.43227882352941183000, - 0.45577764705882357000, - 0.47360352941176476000, - 0.48674117647058823000, - 0.49979882352941163000, - ], - [ - 0.10779898591549292000, - 0.15870338028169012000, - 0.24335419718309842000, - 0.30154478873239432000, - 0.31312512676056359000, - 0.31187740845070405000, - 0.30963605633802815000, - 0.30475785915492964000, - 0.30064743661971804000, - 0.29648360563380305000, - 0.28867476056338026000, - 0.27758146478873252000, - 0.27091515492957735000, - 0.26409166197183093000, - 0.25295690140845062000, - 0.24406754929577457000, - 0.24216214084507048000, - 0.24517898591549292000, - 0.24370647887323940000, - 0.24752726760563384000, - 0.25809814084507027000, - 0.27048856338028177000, - 0.27946467605633790000, - 0.28564805633802820000, - 0.28950569014084515000, - 0.29394118309859157000, - 0.29796377464788726000, - 0.30271183098591542000, - 0.30628552112676050000, - 0.30746732394366194000, - 0.30875188732394343000, - 0.30856918309859155000, - 0.31044997183098588000, - 0.31218912676056354000, - 0.31383464788732385000, - 0.31724743661971833000, - ], - [ - 0.08891588235294117800, - 0.11170882352941176000, - 0.13155529411764705000, - 0.13345352941176472000, - 0.12773352941176472000, - 0.12348058823529412000, - 0.12199294117647061000, - 0.11907411764705882000, - 0.11792117647058822000, - 0.11871352941176466000, - 0.11512823529411763000, - 0.10845000000000000000, - 0.10843000000000001000, - 0.11113294117647059000, - 0.10788647058823528000, - 0.10542588235294116000, - 0.11212294117647059000, - 0.13771529411764705000, - 0.18127705882352937000, - 0.22836529411764706000, - 0.28692647058823539000, - 0.35075411764705883000, - 0.39709058823529408000, - 0.42251705882352941000, - 0.43389176470588225000, - 0.43937470588235289000, - 0.44075882352941176000, - 0.44195411764705883000, - 0.44239529411764700000, - 0.44206588235294109000, - 0.44273058823529410000, - 0.44242411764705880000, - 0.44468470588235293000, - 0.44592764705882354000, - 0.44652235294117637000, - 0.44930823529411767000, - ], - [ - 0.08701444444444445000, - 0.10270333333333331000, - 0.11151777777777777000, - 0.10953777777777778000, - 0.10569999999999999000, - 0.10276222222222224000, - 0.10133111111111109000, - 0.09741555555555554700, - 0.09392333333333334500, - 0.09028111111111110400, - 0.08486222222222221400, - 0.07873666666666664900, - 0.07606111111111110700, - 0.07359000000000001700, - 0.07044000000000000300, - 0.06970444444444444400, - 0.07159777777777778800, - 0.07601444444444444000, - 0.07899666666666667300, - 0.09216222222222221500, - 0.12827333333333332000, - 0.20277777777777778000, - 0.30198333333333338000, - 0.40135111111111121000, - 0.48075777777777773000, - 0.53332888888888896000, - 0.56130111111111125000, - 0.57818000000000003000, - 0.58851111111111121000, - 0.59322111111111120000, - 0.59782555555555550000, - 0.60259555555555566000, - 0.61115000000000008000, - 0.61758111111111114000, - 0.62303444444444445000, - 0.62989222222222208000, - ], - [ - 0.07023857142857142800, - 0.08111642857142858900, - 0.08923309523809523600, - 0.09028619047619047800, - 0.08905285714285708600, - 0.08834642857142854800, - 0.08952357142857139700, - 0.09056880952380949600, - 0.09293809523809525000, - 0.09693595238095237300, - 0.10173238095238095000, - 0.10764285714285717000, - 0.12443142857142857000, - 0.16565285714285716000, - 0.22232071428571437000, - 0.26490809523809533000, - 0.29603785714285719000, - 0.33891190476190480000, - 0.39374523809523820000, - 0.44306047619047628000, - 0.46590380952380966000, - 0.47824261904761900000, - 0.48273738095238095000, - 0.48290047619047627000, - 0.48206547619047607000, - 0.48232547619047622000, - 0.48180452380952365000, - 0.48200666666666664000, - 0.48249571428571431000, - 0.48179785714285728000, - 0.48414190476190488000, - 0.48353404761904767000, - 0.48836785714285708000, - 0.49024928571428561000, - 0.49035238095238093000, - 0.49339000000000011000, - ], -]) + ] +) """ Cluster means for *Otsu et al. (2018)*. This is a list of eight arrays, with one for entry for each cluster. Each array is the mean of all the spectral @@ -1299,22 +1302,20 @@ References ---------- :cite:`Otsu2018` - -CLUSTER_MEANS_OTSU2018 : ndarray """ -SELECTOR_ARRAY_OTSU2018 = np.array([ - [1, 0.333444973048471, -3, -1], - [0, 0.428556829741043, 1, -2], - [1, 0.368343583792887, 5, 7], - [0, 0.389059234962091, -5, -4], - [0, 0.464102042665547, 2, 6], - [0, 0.288243127874986, 0, -6], - [1, 0.247072787814766, 3, 4], -]) +SELECTOR_ARRAY_OTSU2018: NDArray = np.array( + [ + [0.333444973048471, 1, -3, -1], + [0.428556829741043, 0, 1, -2], + [0.368343583792887, 1, 5, 7], + [0.389059234962091, 0, -5, -4], + [0.464102042665547, 0, 2, 6], + [0.288243127874986, 0, 0, -6], + [0.247072787814766, 1, 3, 4], + ] +) """ Array describing how to select the appropriate cluster for given *CIE xy* chromaticity coordinates. - -SELECTOR_ARRAY_OTSU2018 : ndarray """ diff --git a/colour/recovery/datasets/smits1999.py b/colour/recovery/datasets/smits1999.py index e597e01e7c..551f62da73 100644 --- a/colour/recovery/datasets/smits1999.py +++ b/colour/recovery/datasets/smits1999.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Smits (1999) - Reflectance Recovery Dataset =========================================== @@ -12,23 +11,27 @@ doi:10.1080/10867651.1999.10487511 """ -from __future__ import division, unicode_literals +from __future__ import annotations from colour.algebra import LinearInterpolator from colour.colorimetry.spectrum import SpectralDistribution +from colour.hints import Dict from colour.utilities import CaseInsensitiveMapping -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['DATA_SMITS1999', 'SDS_SMITS1999'] +__all__ = [ + "DATA_SMITS1999", + "SDS_SMITS1999", +] -DATA_SMITS1999 = { - 'white': { +DATA_SMITS1999: Dict = { + "white": { 380.0000: 1.0000, 417.7778: 1.0000, 455.5556: 0.9999, @@ -38,9 +41,9 @@ 606.6667: 1.0000, 644.4444: 1.0000, 682.2222: 1.0000, - 720.0000: 1.0000 + 720.0000: 1.0000, }, - 'cyan': { + "cyan": { 380.0000: 0.9710, 417.7778: 0.9426, 455.5556: 1.0007, @@ -50,9 +53,9 @@ 606.6667: 0.1564, 644.4444: 0.0000, 682.2222: 0.0000, - 720.0000: 0.0000 + 720.0000: 0.0000, }, - 'magenta': { + "magenta": { 380.0000: 1.0000, 417.7778: 1.0000, 455.5556: 0.9685, @@ -62,9 +65,9 @@ 606.6667: 0.8369, 644.4444: 1.0000, 682.2222: 1.0000, - 720.0000: 0.9959 + 720.0000: 0.9959, }, - 'yellow': { + "yellow": { 380.0000: 0.0001, 417.7778: 0.0000, 455.5556: 0.1088, @@ -74,9 +77,9 @@ 606.6667: 0.9996, 644.4444: 0.9586, 682.2222: 0.9685, - 720.0000: 0.9840 + 720.0000: 0.9840, }, - 'red': { + "red": { 380.0000: 0.1012, 417.7778: 0.0515, 455.5556: 0.0000, @@ -86,9 +89,9 @@ 606.6667: 0.8325, 644.4444: 1.0149, 682.2222: 1.0149, - 720.0000: 1.0149 + 720.0000: 1.0149, }, - 'green': { + "green": { 380.0000: 0.0000, 417.7778: 0.0000, 455.5556: 0.0273, @@ -98,9 +101,9 @@ 606.6667: 0.1719, 644.4444: 0.0000, 682.2222: 0.0000, - 720.0000: 0.0025 + 720.0000: 0.0025, }, - 'blue': { + "blue": { 380.0000: 1.0000, 417.7778: 1.0000, 455.5556: 0.8916, @@ -110,48 +113,31 @@ 606.6667: 0.0003, 644.4444: 0.0369, 682.2222: 0.0483, - 720.0000: 0.0496 - } + 720.0000: 0.0496, + }, } -SDS_SMITS1999 = CaseInsensitiveMapping({ - 'white': - SpectralDistribution( - DATA_SMITS1999['white'], - name='white'), - 'cyan': - SpectralDistribution( - DATA_SMITS1999['cyan'], - name='cyan'), - 'magenta': - SpectralDistribution( - DATA_SMITS1999['magenta'], - name='magenta'), - 'yellow': - SpectralDistribution( - DATA_SMITS1999['yellow'], - name='yellow'), - 'red': - SpectralDistribution( - DATA_SMITS1999['red'], - name='red'), - 'green': - SpectralDistribution( - DATA_SMITS1999['green'], - name='green'), - 'blue': - SpectralDistribution( - DATA_SMITS1999['blue'], - name='blue') -}) # yapf: disable +SDS_SMITS1999: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "white": SpectralDistribution(DATA_SMITS1999["white"], name="white"), + "cyan": SpectralDistribution(DATA_SMITS1999["cyan"], name="cyan"), + "magenta": SpectralDistribution( + DATA_SMITS1999["magenta"], name="magenta" + ), + "yellow": SpectralDistribution( + DATA_SMITS1999["yellow"], name="yellow" + ), + "red": SpectralDistribution(DATA_SMITS1999["red"], name="red"), + "green": SpectralDistribution(DATA_SMITS1999["green"], name="green"), + "blue": SpectralDistribution(DATA_SMITS1999["blue"], name="blue"), + } +) SDS_SMITS1999.__doc__ = """ *Smits (1999)* spectral distributions. References ---------- :cite:`Smits1999a` - -SDS_SMITS1999 : CaseInsensitiveMapping """ # Using linear interpolation to preserve the shape of the basis spectral diff --git a/colour/recovery/jakob2019.py b/colour/recovery/jakob2019.py index 934be2b261..e6fe585474 100644 --- a/colour/recovery/jakob2019.py +++ b/colour/recovery/jakob2019.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Jakob and Hanika (2019) - Reflectance Recovery ============================================== -Defines objects for reflectance recovery, i.e. spectral upsampling, using +Defines the objects for reflectance recovery, i.e. spectral upsampling, using *Jakob and Hanika (2019)* method: - :func:`colour.recovery.sd_Jakob2019` @@ -18,51 +17,79 @@ 38(2), 147-155. doi:10.1111/cgf.13626 """ -from __future__ import division, print_function, unicode_literals +from __future__ import annotations import numpy as np import struct from scipy.optimize import minimize from scipy.interpolate import RegularGridInterpolator -from colour import SDS_ILLUMINANTS -from colour.algebra import spow, smoothstep_function +from colour.algebra import smoothstep_function, spow +from colour.constants import DEFAULT_INT_DTYPE from colour.colorimetry import ( - MSDS_CMFS_STANDARD_OBSERVER, SpectralDistribution, SpectralShape, - intermediate_lightness_function_CIE1976, sd_to_XYZ) + MultiSpectralDistributions, + SpectralDistribution, + SpectralShape, + handle_spectral_arguments, + intermediate_lightness_function_CIE1976, + sd_to_XYZ_integration, +) from colour.difference import JND_CIE1976 -from colour.models import XYZ_to_xy, XYZ_to_Lab, RGB_to_XYZ -from colour.utilities import (as_float_array, domain_range_scale, full, - index_along_last_axis, is_tqdm_installed, - message_box, to_domain_1, runtime_warning, zeros) -try: - from unittest import mock -except ImportError: # pragma: no cover - import mock +from colour.hints import ( + ArrayLike, + Boolean, + Callable, + Dict, + Floating, + Integer, + NDArray, + Optional, + Tuple, + Union, +) +from colour.models import RGB_Colourspace, XYZ_to_xy, XYZ_to_Lab, RGB_to_XYZ +from colour.utilities import ( + as_float_array, + as_float_scalar, + domain_range_scale, + full, + index_along_last_axis, + is_tqdm_installed, + message_box, + optional, + to_domain_1, + tsplit, + zeros, +) + if is_tqdm_installed(): from tqdm import tqdm -else: +else: # pragma: no cover + from unittest import mock + tqdm = mock.MagicMock() -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'SPECTRAL_SHAPE_JAKOB2019', 'StopMinimizationEarly', 'sd_Jakob2019', - 'error_function', 'dimensionalise_coefficients', 'lightness_scale', - 'find_coefficients_Jakob2019', 'XYZ_to_sd_Jakob2019', 'LUT3D_Jakob2019' + "SPECTRAL_SHAPE_JAKOB2019", + "StopMinimizationEarly", + "sd_Jakob2019", + "error_function", + "dimensionalise_coefficients", + "lightness_scale", + "find_coefficients_Jakob2019", + "XYZ_to_sd_Jakob2019", + "LUT3D_Jakob2019", ] -SPECTRAL_SHAPE_JAKOB2019 = SpectralShape(360, 780, 5) -""" -Spectral shape for *Jakob and Hanika (2019)* method. - -SPECTRAL_SHAPE_JAKOB2019 : SpectralShape -""" +SPECTRAL_SHAPE_JAKOB2019: SpectralShape = SpectralShape(360, 780, 5) +"""Spectral shape for *Jakob and Hanika (2019)* method.""" class StopMinimizationEarly(Exception): @@ -77,55 +104,57 @@ class StopMinimizationEarly(Exception): - :attr:`~colour.recovery.jakob2019.StopMinimizationEarly.error` """ - def __init__(self, coefficients, error): - self._coefficients = coefficients - self._error = error + def __init__(self, coefficients: ArrayLike, error: Floating): + self._coefficients = as_float_array(coefficients) + self._error = as_float_scalar(error) @property - def coefficients(self): + def coefficients(self) -> NDArray: """ Getter property for the *Jakob and Hanika (2019)* exception coefficients. Returns ------- - ndarray + :class:`numpy.ndarray` *Jakob and Hanika (2019)* exception coefficients. """ return self._coefficients @property - def error(self): + def error(self) -> Floating: """ Getter property for the *Jakob and Hanika (2019)* exception error value. Returns ------- - ndarray + :class:`numpy.floating` *Jakob and Hanika (2019)* exception coefficients. """ return self._error -def sd_Jakob2019(coefficients, shape=SPECTRAL_SHAPE_JAKOB2019): +def sd_Jakob2019( + coefficients: ArrayLike, shape: SpectralShape = SPECTRAL_SHAPE_JAKOB2019 +) -> SpectralDistribution: """ - Returns a spectral distribution following the spectral model given by + Return a spectral distribution following the spectral model given by *Jakob and Hanika (2019)*. Parameters ---------- - coefficients : array_like + coefficients Dimensionless coefficients for *Jakob and Hanika (2019)* reflectance spectral model. - shape : SpectralShape, optional + shape Shape used by the spectral distribution. Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` *Jakob and Hanika (2019)* spectral distribution. References @@ -162,55 +191,56 @@ def sd_Jakob2019(coefficients, shape=SPECTRAL_SHAPE_JAKOB2019): c_0, c_1, c_2 = as_float_array(coefficients) wl = shape.range() - U = c_0 * wl ** 2 + c_1 * wl + c_2 - R = 1 / 2 + U / (2 * np.sqrt(1 + U ** 2)) + U = c_0 * wl**2 + c_1 * wl + c_2 + R = 1 / 2 + U / (2 * np.sqrt(1 + U**2)) - name = '{0} (COEFF) - Jakob (2019)'.format(coefficients) + name = f"{coefficients!r} (COEFF) - Jakob (2019)" return SpectralDistribution(R, wl, name=name) -def error_function(coefficients, - target, - cmfs, - illuminant, - max_error=None, - additional_data=False): +def error_function( + coefficients: ArrayLike, + target: ArrayLike, + cmfs: MultiSpectralDistributions, + illuminant: SpectralDistribution, + max_error: Optional[Floating] = None, + additional_data: Boolean = False, +) -> Union[ + Tuple[Floating, NDArray], + Tuple[Floating, NDArray, NDArray, NDArray, NDArray], +]: """ - Computes :math:`\\Delta E_{76}` between the target colour and the colour + Compute :math:`\\Delta E_{76}` between the target colour and the colour defined by given spectral model, along with its gradient. Parameters ---------- - coefficients : array_like + coefficients Dimensionless coefficients for *Jakob and Hanika (2019)* reflectance spectral model. - target : array_like, (3,) + target *CIE L\\*a\\*b\\** colourspace array of the target colour. - cmfs : XYZ_ColourMatchingFunctions + cmfs Standard observer colour matching functions. - illuminant : SpectralDistribution + illuminant Illuminant spectral distribution. - max_error : float, optional + max_error Raise ``StopMinimizationEarly`` if the error is smaller than this. The default is *None* and the function doesn't raise anything. - additional_data : bool, optional + additional_data If *True*, some intermediate calculations are returned, for use in correctness tests: R, XYZ and Lab. Returns ------- - error : float - The computed :math:`\\Delta E_{76}` error. - derror : ndarray, (3,) - The gradient of error, i.e. the first derivatives of error with respect - to the input coefficients. - R : ndarray - Computed spectral reflectance. - XYZ : ndarray, (3,) - *CIE XYZ* tristimulus values corresponding to ``R``. - Lab : ndarray, (3,) - *CIE L\\*a\\*b\\** colourspace array corresponding to ``XYZ``. + :class:`tuple` or :class:`tuple` + Tuple of computed :math:`\\Delta E_{76}` error and gradient of error, + i.e. the first derivatives of error with respect to the input + coefficients or tuple of computed :math:`\\Delta E_{76}` error, + gradient of error, computed spectral reflectance, *CIE XYZ* tristimulus + values corresponding to ``R`` and *CIE L\\*a\\*b\\** colourspace array + corresponding to ``XYZ``. Raises ------ @@ -218,58 +248,69 @@ def error_function(coefficients, Raised when the error is below ``max_error``. """ + target = as_float_array(target) + c_0, c_1, c_2 = as_float_array(coefficients) wv = np.linspace(0, 1, len(cmfs.shape)) - U = c_0 * wv ** 2 + c_1 * wv + c_2 - t1 = np.sqrt(1 + U ** 2) + U = c_0 * wv**2 + c_1 * wv + c_2 + t1 = np.sqrt(1 + U**2) R = 1 / 2 + U / (2 * t1) - t2 = 1 / (2 * t1) - U ** 2 / (2 * t1 ** 3) - dR = np.array([wv ** 2 * t2, wv * t2, t2]) - - E = illuminant.values * R - dE = illuminant.values * dR + t2 = 1 / (2 * t1) - U**2 / (2 * t1**3) + dR = np.array([wv**2 * t2, wv * t2, t2]) - dw = cmfs.wavelengths[1] - cmfs.wavelengths[0] - k = 1 / (np.sum(cmfs.values[:, 1] * illuminant.values) * dw) - - XYZ = k * np.dot(E, cmfs.values) * dw - dXYZ = np.transpose(k * np.dot(dE, cmfs.values) * dw) + XYZ = sd_to_XYZ_integration(R, cmfs, illuminant, shape=cmfs.shape) / 100 + dXYZ = np.transpose( + sd_to_XYZ_integration(dR, cmfs, illuminant, shape=cmfs.shape) / 100 + ) - XYZ_n = sd_to_XYZ(illuminant, cmfs) + XYZ_n = sd_to_XYZ_integration(illuminant, cmfs) XYZ_n /= XYZ_n[1] XYZ_XYZ_n = XYZ / XYZ_n XYZ_f = intermediate_lightness_function_CIE1976(XYZ, XYZ_n) dXYZ_f = np.where( XYZ_XYZ_n[..., np.newaxis] > (24 / 116) ** 3, - 1 / (3 * spow(XYZ_n[..., np.newaxis], 1 / 3) * spow( - XYZ[..., np.newaxis], 2 / 3)) * dXYZ, + 1 + / ( + 3 + * spow(XYZ_n[..., np.newaxis], 1 / 3) + * spow(XYZ[..., np.newaxis], 2 / 3) + ) + * dXYZ, (841 / 108) * dXYZ / XYZ_n[..., np.newaxis], ) - def intermediate_XYZ_to_Lab(XYZ_i, offset=16): + def intermediate_XYZ_to_Lab( + XYZ_i: NDArray, offset: Optional[Floating] = 16 + ) -> NDArray: """ - Returns the final intermediate value for the *CIE Lab* to *CIE XYZ* + Return the final intermediate value for the *CIE Lab* to *CIE XYZ* conversion. """ - return np.array([ - 116 * XYZ_i[1] - offset, 500 * (XYZ_i[0] - XYZ_i[1]), - 200 * (XYZ_i[1] - XYZ_i[2]) - ]) + return np.array( + [ + 116 * XYZ_i[1] - offset, + 500 * (XYZ_i[0] - XYZ_i[1]), + 200 * (XYZ_i[1] - XYZ_i[2]), + ] + ) - Lab_i = intermediate_XYZ_to_Lab(XYZ_f) - dLab_i = intermediate_XYZ_to_Lab(dXYZ_f, 0) + Lab_i = intermediate_XYZ_to_Lab(as_float_array(XYZ_f)) + dLab_i = intermediate_XYZ_to_Lab(as_float_array(dXYZ_f), 0) error = np.sqrt(np.sum((Lab_i - target) ** 2)) if max_error is not None and error <= max_error: raise StopMinimizationEarly(coefficients, error) - derror = np.sum( - dLab_i * (Lab_i[..., np.newaxis] - target[..., np.newaxis]), - axis=0) / error + derror = ( + np.sum( + dLab_i * (Lab_i[..., np.newaxis] - target[..., np.newaxis]), axis=0 + ) + / error + ) if additional_data: return error, derror, R, XYZ, Lab_i @@ -277,9 +318,11 @@ def intermediate_XYZ_to_Lab(XYZ_i, offset=16): return error, derror -def dimensionalise_coefficients(coefficients, shape): +def dimensionalise_coefficients( + coefficients: ArrayLike, shape: SpectralShape +) -> NDArray: """ - Rescales the dimensionless coefficients to given spectral shape. + Rescale the dimensionless coefficients to given spectral shape. A dimensionless form of the reflectance spectral model is used in the optimisation process. Instead of the usual spectral shape, specified in @@ -289,45 +332,46 @@ def dimensionalise_coefficients(coefficients, shape): Parameters ---------- - coefficients : array_like, (3,) + coefficients Dimensionless coefficients. - shape : SpectralShape + shape Spectral distribution shape used in calculations. Returns ------- - ndarray, (3,) + :class:`numpy.ndarray` Dimensionful coefficients, with units of :math:`\\frac{1}{\\mathrm{nm}^2}`, :math:`\\frac{1}{\\mathrm{nm}}` and 1, respectively. """ - cp_0, cp_1, cp_2 = coefficients + cp_0, cp_1, cp_2 = tsplit(coefficients) span = shape.end - shape.start - c_0 = cp_0 / span ** 2 - c_1 = cp_1 / span - 2 * cp_0 * shape.start / span ** 2 + c_0 = cp_0 / span**2 + c_1 = cp_1 / span - 2 * cp_0 * shape.start / span**2 c_2 = ( - cp_0 * shape.start ** 2 / span ** 2 - cp_1 * shape.start / span + cp_2) + cp_0 * shape.start**2 / span**2 - cp_1 * shape.start / span + cp_2 + ) return np.array([c_0, c_1, c_2]) -def lightness_scale(steps): +def lightness_scale(steps: Integer) -> NDArray: """ - Creates a non-linear lightness scale, as described in *Jakob and Hanika + Create a non-linear lightness scale, as described in *Jakob and Hanika (2019)*. The spacing between very dark and very bright (and saturated) colours is made smaller, because in those regions coefficients tend to change rapidly and a finer resolution is needed. Parameters ---------- - steps : int + steps Samples/steps count along the non-linear lightness scale. Returns ------- - ndarray + :class:`numpy.ndarray` Non-linear lightness scale. Examples @@ -343,41 +387,41 @@ def lightness_scale(steps): def find_coefficients_Jakob2019( - XYZ, - cmfs=MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer'] - .copy().align(SPECTRAL_SHAPE_JAKOB2019), - illuminant=SDS_ILLUMINANTS['D65'].copy().align( - SPECTRAL_SHAPE_JAKOB2019), - coefficients_0=zeros(3), - max_error=JND_CIE1976 / 100, - dimensionalise=True): + XYZ: ArrayLike, + cmfs: Optional[MultiSpectralDistributions] = None, + illuminant: Optional[SpectralDistribution] = None, + coefficients_0: ArrayLike = zeros(3), + max_error: Floating = JND_CIE1976 / 100, + dimensionalise: Boolean = True, +) -> Tuple[NDArray, Floating]: """ - Computes the coefficients for *Jakob and Hanika (2019)* reflectance + Compute the coefficients for *Jakob and Hanika (2019)* reflectance spectral model. Parameters ---------- - XYZ : array_like, (3,) + XYZ *CIE XYZ* tristimulus values to find the coefficients for. - cmfs : XYZ_ColourMatchingFunctions - Standard observer colour matching functions. - illuminant : SpectralDistribution - Illuminant spectral distribution. - coefficients_0 : array_like, (3,), optional + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + illuminant + Illuminant spectral distribution, default to + *CIE Standard Illuminant D65*. + coefficients_0 Starting coefficients for the solver. - max_error : float, optional + max_error Maximal acceptable error. Set higher to save computational time. If *None*, the solver will keep going until it is very close to the minimum. The default is ``ACCEPTABLE_DELTA_E``. - dimensionalise : bool, optional + dimensionalise If *True*, returned coefficients are dimensionful and will not work correctly if fed back as ``coefficients_0``. The default is *True*. Returns ------- - coefficients : ndarray, (3,) - Computed coefficients that best fit the given colour. - error : float + :class:`tuple` + Tuple of computed coefficients that best fit the given colour and :math:`\\Delta E_{76}` between the target colour and the colour corresponding to the computed coefficients. @@ -393,31 +437,31 @@ def find_coefficients_Jakob2019( 0.0141941...) """ - shape = cmfs.shape + coefficients_0 = as_float_array(coefficients_0) - if illuminant.shape != shape: - runtime_warning( - 'Aligning "{0}" illuminant shape to "{1}" colour matching ' - 'functions shape.'.format(illuminant.name, cmfs.name)) - illuminant = illuminant.copy().align(cmfs.shape) + cmfs, illuminant = handle_spectral_arguments( + cmfs, illuminant, shape_default=SPECTRAL_SHAPE_JAKOB2019 + ) - def optimize(target_o, coefficients_0_o): - """ - Minimises the error function using *L-BFGS-B* method. - """ + def optimize( + target_o: NDArray, coefficients_0_o: NDArray + ) -> Tuple[NDArray, Floating]: + """Minimise the error function using *L-BFGS-B* method.""" try: result = minimize( error_function, - coefficients_0_o, (target_o, cmfs, illuminant, max_error), - method='L-BFGS-B', - jac=True) + coefficients_0_o, + (target_o, cmfs, illuminant, max_error), + method="L-BFGS-B", + jac=True, + ) return result.x, result.fun except StopMinimizationEarly as error: return error.coefficients, error.error - xy_n = XYZ_to_xy(sd_to_XYZ(illuminant, cmfs)) + xy_n = XYZ_to_xy(sd_to_XYZ_integration(illuminant, cmfs)) XYZ_good = full(3, 0.5) coefficients_good = zeros(3) @@ -451,44 +495,45 @@ def optimize(target_o, coefficients_0_o): coefficients, error = optimize(target, coefficients_0) if dimensionalise: - coefficients = dimensionalise_coefficients(coefficients, shape) + coefficients = dimensionalise_coefficients(coefficients, cmfs.shape) return coefficients, error def XYZ_to_sd_Jakob2019( - XYZ, - cmfs=MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer'] - .copy().align(SPECTRAL_SHAPE_JAKOB2019), - illuminant=SDS_ILLUMINANTS['D65'].copy().align( - SPECTRAL_SHAPE_JAKOB2019), - optimisation_kwargs=None, - additional_data=False): + XYZ: ArrayLike, + cmfs: Optional[MultiSpectralDistributions] = None, + illuminant: Optional[SpectralDistribution] = None, + optimisation_kwargs: Optional[Dict] = None, + additional_data: Boolean = False, +) -> Union[Tuple[SpectralDistribution, Floating], SpectralDistribution]: """ - Recovers the spectral distribution of given RGB colourspace array + Recover the spectral distribution of given RGB colourspace array using *Jakob and Hanika (2019)* method. Parameters ---------- - XYZ : array_like, (3,) + XYZ *CIE XYZ* tristimulus values to recover the spectral distribution from. - cmfs : XYZ_ColourMatchingFunctions - Standard observer colour matching functions. - illuminant : SpectralDistribution - Illuminant spectral distribution. - optimisation_kwargs : dict_like, optional + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + illuminant + Illuminant spectral distribution, default to + *CIE Standard Illuminant D65*. + optimisation_kwargs Parameters for :func:`colour.recovery.find_coefficients_Jakob2019` definition. - additional_data : bool, optional - If *True*, ``error`` will be returned alongside ``sd``. + additional_data + If *True*, ``error`` will be returned alongside the recovered spectral + distribution. Returns ------- - sd : SpectralDistribution - Recovered spectral distribution. - error : float - :math:`\\Delta E_{76}` between the target colour and the colour - corresponding to the computed coefficients. + :class:`tuple` or :class:`colour.SpectralDistribution` + Tuple of recovered spectral distribution and :math:`\\Delta E_{76}` + between the target colour and the colour corresponding to the computed + coefficients or recovered spectral distribution. References ---------- @@ -496,81 +541,85 @@ def XYZ_to_sd_Jakob2019( Examples -------- - >>> from colour.colorimetry import CCS_ILLUMINANTS, sd_to_XYZ_integration - >>> from colour.models import XYZ_to_sRGB + >>> from colour import ( + ... CCS_ILLUMINANTS, MSDS_CMFS, SDS_ILLUMINANTS, XYZ_to_sRGB) + >>> from colour.colorimetry import sd_to_XYZ_integration >>> from colour.utilities import numpy_print_options >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> cmfs = ( - ... MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']. + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SpectralShape(360, 780, 10)) ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) >>> sd = XYZ_to_sd_Jakob2019(XYZ, cmfs, illuminant) >>> with numpy_print_options(suppress=True): - ... # Doctests skip for Python 2.x compatibility. - ... sd # doctest: +SKIP - SpectralDistribution([[ 360. , 0.4884502...], - [ 370. , 0.3251871...], - [ 380. , 0.2144337...], - [ 390. , 0.1480663...], - [ 400. , 0.1085298...], - [ 410. , 0.0840835...], - [ 420. , 0.0682934...], - [ 430. , 0.0577098...], - [ 440. , 0.0504300...], - [ 450. , 0.0453634...], - [ 460. , 0.0418635...], - [ 470. , 0.0395397...], - [ 480. , 0.0381585...], - [ 490. , 0.0375912...], - [ 500. , 0.0377870...], - [ 510. , 0.0387631...], - [ 520. , 0.0406086...], - [ 530. , 0.0435015...], - [ 540. , 0.0477476...], - [ 550. , 0.0538528...], - [ 560. , 0.0626607...], - [ 570. , 0.0756177...], - [ 580. , 0.0952978...], - [ 590. , 0.1264501...], - [ 600. , 0.1779277...], - [ 610. , 0.2648782...], - [ 620. , 0.4037993...], - [ 630. , 0.5829234...], - [ 640. , 0.7442651...], - [ 650. , 0.8497961...], - [ 660. , 0.9093483...], - [ 670. , 0.9424527...], - [ 680. , 0.9615805...], - [ 690. , 0.9732085...], - [ 700. , 0.9806277...], - [ 710. , 0.9855663...], - [ 720. , 0.9889743...], - [ 730. , 0.9913993...], - [ 740. , 0.9931703...], - [ 750. , 0.9944931...], - [ 760. , 0.9955002...], - [ 770. , 0.9962802...], - [ 780. , 0.9968932...]], + ... sd # doctest: +ELLIPSIS + SpectralDistribution([[ 360. , 0.4893773...], + [ 370. , 0.3258214...], + [ 380. , 0.2147792...], + [ 390. , 0.1482413...], + [ 400. , 0.1086169...], + [ 410. , 0.0841255...], + [ 420. , 0.0683114...], + [ 430. , 0.0577144...], + [ 440. , 0.0504267...], + [ 450. , 0.0453552...], + [ 460. , 0.0418520...], + [ 470. , 0.0395259...], + [ 480. , 0.0381430...], + [ 490. , 0.0375741...], + [ 500. , 0.0377685...], + [ 510. , 0.0387432...], + [ 520. , 0.0405871...], + [ 530. , 0.0434783...], + [ 540. , 0.0477225...], + [ 550. , 0.0538256...], + [ 560. , 0.0626314...], + [ 570. , 0.0755869...], + [ 580. , 0.0952675...], + [ 590. , 0.1264265...], + [ 600. , 0.1779272...], + [ 610. , 0.2649393...], + [ 620. , 0.4039779...], + [ 630. , 0.5832105...], + [ 640. , 0.7445440...], + [ 650. , 0.8499970...], + [ 660. , 0.9094792...], + [ 670. , 0.9425378...], + [ 680. , 0.9616376...], + [ 690. , 0.9732481...], + [ 700. , 0.9806562...], + [ 710. , 0.9855873...], + [ 720. , 0.9889903...], + [ 730. , 0.9914117...], + [ 740. , 0.9931801...], + [ 750. , 0.9945009...], + [ 760. , 0.9955066...], + [ 770. , 0.9962855...], + [ 780. , 0.9968976...]], interpolator=SpragueInterpolator, interpolator_kwargs={}, extrapolator=Extrapolator, extrapolator_kwargs={...}) >>> sd_to_XYZ_integration(sd, cmfs, illuminant) / 100 # doctest: +ELLIPSIS - array([ 0.2065841..., 0.1220125..., 0.0514023...]) + array([ 0.2066217..., 0.1220128..., 0.0513958...]) """ XYZ = to_domain_1(XYZ) - if optimisation_kwargs is None: - optimisation_kwargs = {} + cmfs, illuminant = handle_spectral_arguments( + cmfs, illuminant, shape_default=SPECTRAL_SHAPE_JAKOB2019 + ) - with domain_range_scale('ignore'): + optimisation_kwargs = optional(optimisation_kwargs, {}) + + with domain_range_scale("ignore"): coefficients, error = find_coefficients_Jakob2019( - XYZ, cmfs, illuminant, **optimisation_kwargs) + XYZ, cmfs, illuminant, **optimisation_kwargs + ) sd = sd_Jakob2019(coefficients, cmfs.shape) - sd.name = '{0} (XYZ) - Jakob (2019)'.format(XYZ) + sd.name = f"{XYZ} (XYZ) - Jakob (2019)" if additional_data: return sd, error @@ -578,9 +627,9 @@ def XYZ_to_sd_Jakob2019( return sd -class LUT3D_Jakob2019(object): +class LUT3D_Jakob2019: """ - Class for working with pre-computed lookup tables for the + Clas for working with pre-computed lookup tables for the *Jakob and Hanika (2019)* spectral upsampling method. It allows significant time savings by performing the expensive numerical optimization ahead of time and storing the results in a file. @@ -615,10 +664,12 @@ class LUT3D_Jakob2019(object): -------- >>> import os >>> import colour + >>> from colour import CCS_ILLUMINANTS, SDS_ILLUMINANTS, MSDS_CMFS + >>> from colour.colorimetry import sd_to_XYZ_integration >>> from colour.models import RGB_COLOURSPACE_sRGB >>> from colour.utilities import numpy_print_options >>> cmfs = ( - ... MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']. + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SpectralShape(360, 780, 10)) ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) @@ -630,51 +681,50 @@ class LUT3D_Jakob2019(object): >>> LUT.read(path) # doctest: +SKIP >>> RGB = np.array([0.70573936, 0.19248266, 0.22354169]) >>> with numpy_print_options(suppress=True): - ... # Doctests skip for Python 2.x compatibility. - ... LUT.RGB_to_sd(RGB, cmfs.shape) # doctest: +SKIP - SpectralDistribution([[ 360. , 0.7663248...], - [ 370. , 0.6248040...], - [ 380. , 0.4582328...], - [ 390. , 0.3161403...], - [ 400. , 0.2196885...], - [ 410. , 0.1597642...], - [ 420. , 0.1226653...], - [ 430. , 0.0990878...], - [ 440. , 0.0836822...], - [ 450. , 0.0734525...], - [ 460. , 0.0667002...], - [ 470. , 0.0624502...], - [ 480. , 0.0601529...], - [ 490. , 0.0595328...], - [ 500. , 0.0605182...], - [ 510. , 0.0632235...], - [ 520. , 0.0679778...], - [ 530. , 0.0754093...], - [ 540. , 0.0866232...], - [ 550. , 0.1035471...], - [ 560. , 0.1295933...], - [ 570. , 0.1708525...], - [ 580. , 0.2377171...], - [ 590. , 0.3442627...], - [ 600. , 0.4952907...], - [ 610. , 0.6605014...], - [ 620. , 0.7914286...], - [ 630. , 0.8738002...], - [ 640. , 0.9212534...], - [ 650. , 0.9486329...], - [ 660. , 0.9650124...], - [ 670. , 0.9752510...], - [ 680. , 0.9819246...], - [ 690. , 0.9864387...], - [ 700. , 0.9895916...], - [ 710. , 0.9918554...], - [ 720. , 0.9935199...], - [ 730. , 0.9947694...], - [ 740. , 0.9957242...], - [ 750. , 0.9964656...], - [ 760. , 0.9970494...], - [ 770. , 0.9975148...], - [ 780. , 0.9978900...]], + ... LUT.RGB_to_sd(RGB, cmfs.shape) # doctest: +ELLIPSIS + SpectralDistribution([[ 360. , 0.7666803...], + [ 370. , 0.6251547...], + [ 380. , 0.4584310...], + [ 390. , 0.3161633...], + [ 400. , 0.2196155...], + [ 410. , 0.1596575...], + [ 420. , 0.1225525...], + [ 430. , 0.0989784...], + [ 440. , 0.0835782...], + [ 450. , 0.0733535...], + [ 460. , 0.0666049...], + [ 470. , 0.0623569...], + [ 480. , 0.06006 ...], + [ 490. , 0.0594383...], + [ 500. , 0.0604201...], + [ 510. , 0.0631195...], + [ 520. , 0.0678648...], + [ 530. , 0.0752834...], + [ 540. , 0.0864790...], + [ 550. , 0.1033773...], + [ 560. , 0.1293883...], + [ 570. , 0.1706018...], + [ 580. , 0.2374178...], + [ 590. , 0.3439472...], + [ 600. , 0.4950548...], + [ 610. , 0.6604253...], + [ 620. , 0.7914669...], + [ 630. , 0.8738724...], + [ 640. , 0.9213216...], + [ 650. , 0.9486880...], + [ 660. , 0.9650550...], + [ 670. , 0.9752838...], + [ 680. , 0.9819499...], + [ 690. , 0.9864585...], + [ 700. , 0.9896073...], + [ 710. , 0.9918680...], + [ 720. , 0.9935302...], + [ 730. , 0.9947778...], + [ 740. , 0.9957312...], + [ 750. , 0.9964714...], + [ 760. , 0.9970543...], + [ 770. , 0.9975190...], + [ 780. , 0.9978936...]], interpolator=SpragueInterpolator, interpolator_kwargs={}, extrapolator=Extrapolator, @@ -682,61 +732,61 @@ class LUT3D_Jakob2019(object): """ def __init__(self): - self._interpolator = None - self._size = None - self._lightness_scale = None - self._coefficients = None + self._interpolator: Optional[RegularGridInterpolator] = None + self._size: Integer = 0 + self._lightness_scale: NDArray = np.array([]) + self._coefficients: NDArray = np.array([]) @property - def size(self): + def size(self) -> Integer: """ Getter property for the *Jakob and Hanika (2019)* interpolator size, i.e. the samples count on one side of the 3D table. Returns ------- - ndarray + :class:`numpy.integer` *Jakob and Hanika (2019)* interpolator size. """ return self._size @property - def lightness_scale(self): + def lightness_scale(self) -> NDArray: """ Getter property for the *Jakob and Hanika (2019)* interpolator lightness scale. Returns ------- - int + :class:`numpy.ndarray` *Jakob and Hanika (2019)* interpolator lightness scale. """ return self._lightness_scale @property - def coefficients(self): + def coefficients(self) -> NDArray: """ Getter property for the *Jakob and Hanika (2019)* interpolator coefficients. Returns ------- - ndarray + :class:`numpy.ndarray` *Jakob and Hanika (2019)* interpolator coefficients. """ return self._coefficients @property - def interpolator(self): + def interpolator(self) -> RegularGridInterpolator: """ Getter property for the *Jakob and Hanika (2019)* interpolator. Returns ------- - RegularGridInterpolator + :class:`scipy.interpolate.RegularGridInterpolator` *Jakob and Hanika (2019)* interpolator. """ @@ -744,7 +794,7 @@ def interpolator(self): def _create_interpolator(self): """ - Creates a :class:`scipy.interpolate.RegularGridInterpolator` class + Create a :class:`scipy.interpolate.RegularGridInterpolator` class instance for read or generated coefficients. """ @@ -752,43 +802,47 @@ def _create_interpolator(self): axes = ([0, 1, 2], self._lightness_scale, samples, samples) self._interpolator = RegularGridInterpolator( - axes, self._coefficients, bounds_error=False) - - def generate(self, - colourspace, - cmfs=MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'] - .copy().align(SPECTRAL_SHAPE_JAKOB2019), - illuminant=SDS_ILLUMINANTS['D65'].copy().align( - SPECTRAL_SHAPE_JAKOB2019), - size=64, - print_callable=print): + axes, self._coefficients, bounds_error=False + ) + + def generate( + self, + colourspace: RGB_Colourspace, + cmfs: Optional[MultiSpectralDistributions] = None, + illuminant: Optional[SpectralDistribution] = None, + size: Integer = 64, + print_callable: Callable = print, + ): """ - Generates the lookup table data for given *RGB* colourspace, colour + Generate the lookup table data for given *RGB* colourspace, colour matching functions, illuminant and given size. Parameters ---------- - colourspace: RGB_Colourspace + colourspace The *RGB* colourspace to create a lookup table for. - cmfs : XYZ_ColourMatchingFunctions, optional - Standard observer colour matching functions. - illuminant : SpectralDistribution, optional - Illuminant spectral distribution. - size : int, optional + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + illuminant + Illuminant spectral distribution, default to + *CIE Standard Illuminant D65*. + size The resolution of the lookup table. Higher values will decrease errors but at the cost of a much longer run time. The published *\\*.coeff* files have a resolution of 64. - print_callable : callable, optional + print_callable Callable used to print progress and diagnostic information. Examples -------- - >>> from colour.utilities import numpy_print_options + >>> from colour import MSDS_CMFS, SDS_ILLUMINANTS >>> from colour.models import RGB_COLOURSPACE_sRGB - >>> cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - ... 'CIE 1931 2 Degree Standard Observer'].copy().align( - ... SpectralShape(360, 780, 10)) + >>> from colour.utilities import numpy_print_options + >>> cmfs = ( + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. + ... copy().align(SpectralShape(360, 780, 10)) + ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) >>> LUT = LUT3D_Jakob2019() >>> print(LUT.interpolator) @@ -809,18 +863,15 @@ def generate(self, >>> print(LUT.interpolator) ... # doctest: +ELLIPSIS - + """ + cmfs, illuminant = handle_spectral_arguments( + cmfs, illuminant, shape_default=SPECTRAL_SHAPE_JAKOB2019 + ) shape = cmfs.shape - if illuminant.shape != shape: - runtime_warning( - 'Aligning "{0}" illuminant shape to "{1}" colour matching ' - 'functions shape.'.format(illuminant.name, cmfs.name)) - illuminant = illuminant.copy().align(cmfs.shape) - - xy_n = XYZ_to_xy(sd_to_XYZ(illuminant, cmfs)) + xy_n = XYZ_to_xy(sd_to_XYZ_integration(illuminant, cmfs)) # It could be interesting to have different resolutions for lightness # and chromaticity, but the current file format doesn't allow it. @@ -829,45 +880,58 @@ def generate(self, self._lightness_scale = lightness_scale(lightness_steps) self._coefficients = np.empty( - [3, chroma_steps, chroma_steps, lightness_steps, 3]) + [3, chroma_steps, chroma_steps, lightness_steps, 3] + ) cube_indexes = np.ndindex(3, chroma_steps, chroma_steps) - total_coefficients = chroma_steps ** 2 * 3 + total_coefficients = chroma_steps**2 * 3 # First, create a list of all the fully bright colours with the order # matching cube_indexes. samples = np.linspace(0, 1, chroma_steps) - ij = np.meshgrid(*[[1], samples, samples], indexing='ij') - ij = np.transpose(ij).reshape(-1, 3) + ij = np.reshape( + np.transpose(np.meshgrid([1], samples, samples, indexing="ij")), + (-1, 3), + ) chromas = np.concatenate( - [ij, np.roll(ij, 1, axis=1), - np.roll(ij, 2, axis=1)]) + [ + as_float_array(ij), + np.roll(ij, 1, axis=1), + np.roll(ij, 2, axis=1), + ] + ) message_box( '"Jakob et al. (2018)" LUT Optimisation', - print_callable=print_callable) + print_callable=print_callable, + ) - print_callable( - '\nOptimising {0} coefficients...\n'.format(total_coefficients)) + print_callable(f"\nOptimising {total_coefficients} coefficients...\n") - def optimize(ijkL, coefficients_0): + def optimize(ijkL: ArrayLike, coefficients_0: ArrayLike) -> NDArray: """ - Solves for a specific lightness and stores the result in the + Solve for a specific lightness and stores the result in the appropriate cell. """ - i, j, k, L = ijkL + i, j, k, L = tsplit(ijkL, dtype=DEFAULT_INT_DTYPE) RGB = self._lightness_scale[L] * chroma - XYZ = RGB_to_XYZ(RGB, colourspace.whitepoint, xy_n, - colourspace.matrix_RGB_to_XYZ) + XYZ = RGB_to_XYZ( + RGB, + colourspace.whitepoint, + xy_n, + colourspace.matrix_RGB_to_XYZ, + ) coefficients, _error = find_coefficients_Jakob2019( - XYZ, cmfs, illuminant, coefficients_0, dimensionalise=False) + XYZ, cmfs, illuminant, coefficients_0, dimensionalise=False + ) self._coefficients[i, L, j, k, :] = dimensionalise_coefficients( - coefficients, shape) + coefficients, shape + ) return coefficients @@ -880,145 +944,163 @@ def optimize(ijkL, coefficients_0): # find_coefficients_Jakob2019" definition. L_middle = lightness_steps // 3 coefficients_middle = optimize( - np.hstack([ijk, L_middle]), zeros(3)) + np.hstack([ijk, L_middle]), zeros(3) + ) - # Goes down the lightness scale. + # Down the lightness scale. coefficients_0 = coefficients_middle for L in reversed(range(0, L_middle)): coefficients_0 = optimize( - np.hstack([ijk, L]), coefficients_0) + np.hstack([ijk, L]), coefficients_0 + ) - # Goes up the lightness scale. + # Up the lightness scale. coefficients_0 = coefficients_middle for L in range(L_middle + 1, lightness_steps): coefficients_0 = optimize( - np.hstack([ijk, L]), coefficients_0) + np.hstack([ijk, L]), coefficients_0 + ) self._size = size self._create_interpolator() - def RGB_to_coefficients(self, RGB): + def RGB_to_coefficients(self, RGB: ArrayLike) -> NDArray: """ Look up a given *RGB* colourspace array and return corresponding coefficients. Interpolation is used for colours not on the table grid. Parameters ---------- - RGB : ndarray, (3,) + RGB *RGB* colourspace array. Returns ------- - coefficients : ndarray, (3,) + :class:`numpy.ndarray` Corresponding coefficients that can be passed to :func:`colour.recovery.jakob2019.sd_Jakob2019` to obtain a spectral distribution. + Raises + ------ + RuntimeError + If the pre-computed lookup table has not been generated or read. + Examples -------- + >>> from colour import MSDS_CMFS, SDS_ILLUMINANTS >>> from colour.models import RGB_COLOURSPACE_sRGB - >>> cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - ... 'CIE 1931 2 Degree Standard Observer'].copy().align( - ... SpectralShape(360, 780, 10)) + >>> cmfs = ( + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. + ... copy().align(SpectralShape(360, 780, 10)) + ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) >>> LUT = LUT3D_Jakob2019() >>> LUT.generate( ... RGB_COLOURSPACE_sRGB, cmfs, illuminant, 3, lambda x: x) >>> RGB = np.array([0.70573936, 0.19248266, 0.22354169]) >>> LUT.RGB_to_coefficients(RGB) # doctest: +ELLIPSIS - array([ 1.5012557...e-04, -1.4678661...e-01, 3.4017293...e+01]) + array([ 1.5013448...e-04, -1.4679754...e-01, 3.4020219...e+01]) """ - RGB = as_float_array(RGB) + if self._interpolator is not None: + RGB = as_float_array(RGB) - value_max = np.max(RGB, axis=-1) - chroma = RGB / (np.expand_dims(value_max, -1) + 1e-10) + value_max = np.max(RGB, axis=-1) + chroma = RGB / (value_max[..., np.newaxis] + 1e-10) - i_m = np.argmax(RGB, axis=-1) - i_1 = index_along_last_axis(RGB, i_m) - i_2 = index_along_last_axis(chroma, (i_m + 2) % 3) - i_3 = index_along_last_axis(chroma, (i_m + 1) % 3) + i_m = np.argmax(RGB, axis=-1) + i_1 = index_along_last_axis(RGB, i_m) + i_2 = index_along_last_axis(chroma, (i_m + 2) % 3) + i_3 = index_along_last_axis(chroma, (i_m + 1) % 3) - indexes = np.stack([i_m, i_1, i_2, i_3], axis=-1) + indexes = np.stack([i_m, i_1, i_2, i_3], axis=-1) - return self._interpolator(indexes).squeeze() + return self._interpolator(indexes).squeeze() + else: + raise RuntimeError( + "The pre-computed lookup table has not been read or generated!" + ) - def RGB_to_sd(self, RGB, shape=SPECTRAL_SHAPE_JAKOB2019): + def RGB_to_sd( + self, RGB: ArrayLike, shape: SpectralShape = SPECTRAL_SHAPE_JAKOB2019 + ) -> SpectralDistribution: """ - Looks up a given *RGB* colourspace array and return the corresponding + Look up a given *RGB* colourspace array and return the corresponding spectral distribution. Parameters ---------- - RGB : ndarray, (3,) + RGB *RGB* colourspace array. - shape : SpectralShape, optional + shape Shape used by the spectral distribution. Returns ------- - sd : SpectralDistribution + :class:`colour.SpectralDistribution` Spectral distribution corresponding with the RGB* colourspace array. Examples -------- - >>> from colour.utilities import numpy_print_options + >>> from colour import MSDS_CMFS, SDS_ILLUMINANTS >>> from colour.models import RGB_COLOURSPACE_sRGB - >>> cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - ... 'CIE 1931 2 Degree Standard Observer'].copy().align( - ... SpectralShape(360, 780, 10)) + >>> from colour.utilities import numpy_print_options + >>> cmfs = ( + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. + ... copy().align(SpectralShape(360, 780, 10)) + ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) >>> LUT = LUT3D_Jakob2019() >>> LUT.generate( ... RGB_COLOURSPACE_sRGB, cmfs, illuminant, 3, lambda x: x) >>> RGB = np.array([0.70573936, 0.19248266, 0.22354169]) >>> with numpy_print_options(suppress=True): - ... # Doctests skip for Python 2.x compatibility. - ... LUT.RGB_to_sd(RGB, cmfs.shape) # doctest: +SKIP - SpectralDistribution([[ 360. , 0.7663250...], - [ 370. , 0.6248043...], - [ 380. , 0.4582331...], - [ 390. , 0.3161405...], - [ 400. , 0.2196887...], - [ 410. , 0.1597643...], - [ 420. , 0.1226654...], - [ 430. , 0.0990879...], - [ 440. , 0.0836822...], - [ 450. , 0.0734526...], - [ 460. , 0.0667003...], - [ 470. , 0.0624502...], - [ 480. , 0.060153 ...], - [ 490. , 0.0595329...], - [ 500. , 0.0605182...], - [ 510. , 0.0632236...], - [ 520. , 0.0679778...], - [ 530. , 0.0754094...], - [ 540. , 0.0866233...], - [ 550. , 0.1035472...], - [ 560. , 0.1295934...], - [ 570. , 0.1708527...], - [ 580. , 0.2377174...], - [ 590. , 0.3442632...], - [ 600. , 0.4952913...], - [ 610. , 0.6605019...], - [ 620. , 0.7914290...], - [ 630. , 0.8738004...], - [ 640. , 0.9212535...], - [ 650. , 0.9486330...], - [ 660. , 0.9650125...], - [ 670. , 0.9752511...], - [ 680. , 0.9819246...], - [ 690. , 0.9864387...], - [ 700. , 0.9895916...], - [ 710. , 0.9918554...], - [ 720. , 0.9935199...], - [ 730. , 0.9947694...], - [ 740. , 0.9957242...], - [ 750. , 0.9964656...], - [ 760. , 0.9970494...], - [ 770. , 0.9975148...], - [ 780. , 0.9978900...]], + ... LUT.RGB_to_sd(RGB, cmfs.shape) # doctest: +ELLIPSIS + SpectralDistribution([[ 360. , 0.7666803...], + [ 370. , 0.6251547...], + [ 380. , 0.4584310...], + [ 390. , 0.3161633...], + [ 400. , 0.2196155...], + [ 410. , 0.1596575...], + [ 420. , 0.1225525...], + [ 430. , 0.0989784...], + [ 440. , 0.0835782...], + [ 450. , 0.0733535...], + [ 460. , 0.0666049...], + [ 470. , 0.0623569...], + [ 480. , 0.06006 ...], + [ 490. , 0.0594383...], + [ 500. , 0.0604201...], + [ 510. , 0.0631195...], + [ 520. , 0.0678648...], + [ 530. , 0.0752834...], + [ 540. , 0.0864790...], + [ 550. , 0.1033773...], + [ 560. , 0.1293883...], + [ 570. , 0.1706018...], + [ 580. , 0.2374178...], + [ 590. , 0.3439472...], + [ 600. , 0.4950548...], + [ 610. , 0.6604253...], + [ 620. , 0.7914669...], + [ 630. , 0.8738724...], + [ 640. , 0.9213216...], + [ 650. , 0.9486880...], + [ 660. , 0.9650550...], + [ 670. , 0.9752838...], + [ 680. , 0.9819499...], + [ 690. , 0.9864585...], + [ 700. , 0.9896073...], + [ 710. , 0.9918680...], + [ 720. , 0.9935302...], + [ 730. , 0.9947778...], + [ 740. , 0.9957312...], + [ 750. , 0.9964714...], + [ 760. , 0.9970543...], + [ 770. , 0.9975190...], + [ 780. , 0.9978936...]], interpolator=SpragueInterpolator, interpolator_kwargs={}, extrapolator=Extrapolator, @@ -1026,28 +1108,30 @@ def RGB_to_sd(self, RGB, shape=SPECTRAL_SHAPE_JAKOB2019): """ sd = sd_Jakob2019(self.RGB_to_coefficients(RGB), shape) - sd.name = '{0} (RGB) - Jakob (2019)'.format(RGB) + sd.name = f"{RGB!r} (RGB) - Jakob (2019)" return sd - def read(self, path): + def read(self, path: str): """ - Loads a lookup table from a *\\*.coeff* file. + Load a lookup table from a *\\*.coeff* file. Parameters ---------- - path : unicode + path Path to the file. Examples -------- >>> import os >>> import colour - >>> from colour.utilities import numpy_print_options + >>> from colour import MSDS_CMFS, SDS_ILLUMINANTS >>> from colour.models import RGB_COLOURSPACE_sRGB - >>> cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - ... 'CIE 1931 2 Degree Standard Observer'].copy().align( - ... SpectralShape(360, 780, 10)) + >>> from colour.utilities import numpy_print_options + >>> cmfs = ( + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. + ... copy().align(SpectralShape(360, 780, 10)) + ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) >>> LUT = LUT3D_Jakob2019() >>> LUT.generate( @@ -1058,40 +1142,45 @@ def read(self, path): >>> LUT.read(path) # doctest: +SKIP """ - with open(path, 'rb') as coeff_file: - if coeff_file.read(4).decode('ISO-8859-1') != 'SPEC': + with open(path, "rb") as coeff_file: + if coeff_file.read(4).decode("ISO-8859-1") != "SPEC": raise ValueError( - 'Bad magic number, this is likely not the right file type!' + "Bad magic number, this is likely not the right file type!" ) - self._size = struct.unpack('i', coeff_file.read(4))[0] + self._size = struct.unpack("i", coeff_file.read(4))[0] self._lightness_scale = np.fromfile( - coeff_file, count=self._size, dtype=np.float32) + coeff_file, count=self._size, dtype=np.float32 + ) self._coefficients = np.fromfile( - coeff_file, count=3 * (self._size ** 3) * 3, dtype=np.float32) + coeff_file, count=3 * (self._size**3) * 3, dtype=np.float32 + ) self._coefficients = self._coefficients.reshape( - 3, self._size, self._size, self._size, 3) + 3, self._size, self._size, self._size, 3 + ) self._create_interpolator() - def write(self, path): + def write(self, path: str): """ - Writes the lookup table to a *\\*.coeff* file. + Write the lookup table to a *\\*.coeff* file. Parameters ---------- - path : unicode + path Path to the file. Examples -------- >>> import os >>> import colour - >>> from colour.utilities import numpy_print_options + >>> from colour import MSDS_CMFS, SDS_ILLUMINANTS >>> from colour.models import RGB_COLOURSPACE_sRGB - >>> cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - ... 'CIE 1931 2 Degree Standard Observer'].copy().align( - ... SpectralShape(360, 780, 10)) + >>> from colour.utilities import numpy_print_options + >>> cmfs = ( + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. + ... copy().align(SpectralShape(360, 780, 10)) + ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) >>> LUT = LUT3D_Jakob2019() >>> LUT.generate( @@ -1102,8 +1191,8 @@ def write(self, path): >>> LUT.read(path) # doctest: +SKIP """ - with open(path, 'wb') as coeff_file: - coeff_file.write(b'SPEC') - coeff_file.write(struct.pack('i', self._coefficients.shape[1])) + with open(path, "wb") as coeff_file: + coeff_file.write(b"SPEC") + coeff_file.write(struct.pack("i", self._coefficients.shape[1])) np.float32(self._lightness_scale).tofile(coeff_file) np.float32(self._coefficients).tofile(coeff_file) diff --git a/colour/recovery/mallett2019.py b/colour/recovery/mallett2019.py index c161542518..19170e26df 100644 --- a/colour/recovery/mallett2019.py +++ b/colour/recovery/mallett2019.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Mallett and Yuksel (2019) - Reflectance Recovery ================================================ -Defines objects for reflectance recovery, i.e. spectral upsampling, using +Defines the objects for reflectance recovery, i.e. spectral upsampling, using *Mallett and Yuksel (2019)* method: - :func:`colour.recovery.spectral_primary_decomposition_Mallett2019` @@ -16,66 +15,72 @@ on Rendering - DL-Only and Industry Track, 7 pages. doi:10.2312/SR.20191216 """ -from __future__ import division, print_function, unicode_literals +from __future__ import annotations import numpy as np from scipy.linalg import block_diag from scipy.optimize import Bounds, LinearConstraint, minimize -from colour.colorimetry import (SpectralDistribution, - MultiSpectralDistributions, - MSDS_CMFS_STANDARD_OBSERVER, SDS_ILLUMINANTS) +from colour.colorimetry import ( + MultiSpectralDistributions, + SpectralDistribution, + handle_spectral_arguments, +) +from colour.models import RGB_Colourspace +from colour.hints import ArrayLike, Callable, Dict, Optional, Tuple from colour.recovery import MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019 -from colour.utilities import to_domain_1, runtime_warning +from colour.utilities import to_domain_1 -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'spectral_primary_decomposition_Mallett2019', - 'RGB_to_sd_Mallett2019', + "spectral_primary_decomposition_Mallett2019", + "RGB_to_sd_Mallett2019", ] def spectral_primary_decomposition_Mallett2019( - colourspace, - cmfs=MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'], - illuminant=SDS_ILLUMINANTS['D65'], - metric=np.linalg.norm, - metric_args=tuple(), - optimisation_kwargs=None): + colourspace: RGB_Colourspace, + cmfs: Optional[MultiSpectralDistributions] = None, + illuminant: Optional[SpectralDistribution] = None, + metric: Callable = np.linalg.norm, + metric_args: Tuple = tuple(), + optimisation_kwargs: Optional[Dict] = None, +) -> MultiSpectralDistributions: """ - Performs the spectral primary decomposition as described in *Mallett and + Perform the spectral primary decomposition as described in *Mallett and Yuksel (2019)* for given *RGB* colourspace. Parameters ---------- - colourspace: RGB_Colourspace + colourspace *RGB* colourspace. - cmfs : XYZ_ColourMatchingFunctions, optional - Standard observer colour matching functions. - illuminant : SpectralDistribution, optional - Illuminant spectral distribution. - metric : unicode, optional + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + illuminant + Illuminant spectral distribution, default to + *CIE Standard Illuminant D65*. + metric Function to be minimised, i.e. the objective function. ``metric(basis, *metric_args) -> float`` where ``basis`` is three reflectances concatenated together, each with a shape matching ``shape``. - metric_args : tuple, optional + metric_args Additional arguments passed to ``metric``. - optimisation_kwargs : dict_like, optional + optimisation_kwargs Parameters for :func:`scipy.optimize.minimize` definition. Returns ------- - MultiSpectralDistributions + :class:`colour.MultiSpectralDistributions` Basis functions for given *RGB* colourspace. References @@ -95,11 +100,11 @@ def spectral_primary_decomposition_Mallett2019( Examples -------- - >>> from colour.colorimetry import SpectralShape + >>> from colour import MSDS_CMFS, SDS_ILLUMINANTS, SpectralShape >>> from colour.models import RGB_COLOURSPACE_PAL_SECAM >>> from colour.utilities import numpy_print_options >>> cmfs = ( - ... MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']. + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SpectralShape(360, 780, 10)) ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) @@ -109,121 +114,122 @@ def spectral_primary_decomposition_Mallett2019( ... } ... ) >>> with numpy_print_options(suppress=True): - ... # Doctests skip for Python 2.x compatibility. ... print(msds) # doctest: +SKIP - [[ 360. 0.3324728... 0.3332663... 0.3342608...] - [ 370. 0.3323307... 0.3327746... 0.3348946...] - [ 380. 0.3341115... 0.3323995... 0.3334889...] - [ 390. 0.3337570... 0.3298092... 0.3364336...] - [ 400. 0.3209352... 0.3218213... 0.3572433...] - [ 410. 0.2881025... 0.2837628... 0.4281346...] - [ 420. 0.1836749... 0.1838893... 0.6324357...] - [ 430. 0.0187212... 0.0529655... 0.9283132...] + [[ 360. 0.3395134... 0.3400214... 0.3204650...] + [ 370. 0.3355246... 0.3338028... 0.3306724...] + [ 380. 0.3376707... 0.3185578... 0.3437715...] + [ 390. 0.3178866... 0.3351754... 0.3469378...] + [ 400. 0.3045154... 0.3248376... 0.3706469...] + [ 410. 0.2935652... 0.2919463... 0.4144884...] + [ 420. 0.1875740... 0.1853729... 0.6270530...] + [ 430. 0.0167983... 0.054483 ... 0.9287186...] [ 440. 0. ... 0. ... 1. ...] [ 450. 0. ... 0. ... 1. ...] [ 460. 0. ... 0. ... 1. ...] - [ 470. 0. ... 0.0509556... 0.9490443...] - [ 480. 0. ... 0.2933996... 0.7066003...] - [ 490. 0. ... 0.5001396... 0.4998603...] - [ 500. 0. ... 0.6734805... 0.3265195...] - [ 510. 0. ... 0.8555213... 0.1444786...] - [ 520. 0. ... 0.9999985... 0.0000014...] + [ 470. 0. ... 0.0458044... 0.9541955...] + [ 480. 0. ... 0.2960917... 0.7039082...] + [ 490. 0. ... 0.5042592... 0.4957407...] + [ 500. 0. ... 0.6655795... 0.3344204...] + [ 510. 0. ... 0.8607541... 0.1392458...] + [ 520. 0. ... 0.9999998... 0.0000001...] [ 530. 0. ... 1. ... 0. ...] [ 540. 0. ... 1. ... 0. ...] [ 550. 0. ... 1. ... 0. ...] [ 560. 0. ... 0.9924229... 0. ...] - [ 570. 0. ... 0.9913344... 0.0083032...] - [ 580. 0.0370289... 0.9145168... 0.0484542...] - [ 590. 0.7100075... 0.2898477... 0.0001446...] + [ 570. 0. ... 0.9970703... 0.0025673...] + [ 580. 0.0396002... 0.9028231... 0.0575766...] + [ 590. 0.7058973... 0.2941026... 0. ...] [ 600. 1. ... 0. ... 0. ...] [ 610. 1. ... 0. ... 0. ...] [ 620. 1. ... 0. ... 0. ...] [ 630. 1. ... 0. ... 0. ...] - [ 640. 0.9711347... 0.0137659... 0.0150993...] - [ 650. 0.7996619... 0.1119379... 0.0884001...] - [ 660. 0.6064640... 0.202815 ... 0.1907209...] - [ 670. 0.4662959... 0.2675037... 0.2662005...] - [ 680. 0.4010958... 0.2998989... 0.2990052...] - [ 690. 0.3617485... 0.3208921... 0.3173592...] - [ 700. 0.3496691... 0.3247855... 0.3255453...] - [ 710. 0.3433979... 0.3273540... 0.329248 ...] - [ 720. 0.3358860... 0.3345583... 0.3295556...] - [ 730. 0.3349498... 0.3314232... 0.3336269...] - [ 740. 0.3359954... 0.3340147... 0.3299897...] - [ 750. 0.3310392... 0.3327595... 0.3362012...] - [ 760. 0.3346883... 0.3314158... 0.3338957...] - [ 770. 0.3332167... 0.333371 ... 0.3334122...] - [ 780. 0.3319670... 0.3325476... 0.3354852...]] + [ 640. 0.9835925... 0.0100166... 0.0063908...] + [ 650. 0.7878949... 0.1265097... 0.0855953...] + [ 660. 0.5987994... 0.2051062... 0.1960942...] + [ 670. 0.4724493... 0.2649623... 0.2625883...] + [ 680. 0.3989806... 0.3007488... 0.3002704...] + [ 690. 0.3666586... 0.3164003... 0.3169410...] + [ 700. 0.3497806... 0.3242863... 0.3259329...] + [ 710. 0.3563736... 0.3232441... 0.3203822...] + [ 720. 0.3362624... 0.3326209... 0.3311165...] + [ 730. 0.3245015... 0.3365982... 0.3389002...] + [ 740. 0.3335520... 0.3320670... 0.3343808...] + [ 750. 0.3441287... 0.3291168... 0.3267544...] + [ 760. 0.3343705... 0.3330132... 0.3326162...] + [ 770. 0.3274633... 0.3305704... 0.3419662...] + [ 780. 0.3475263... 0.3262331... 0.3262404...]] """ - if illuminant.shape != cmfs.shape: - runtime_warning( - 'Aligning "{0}" illuminant shape to "{1}" colour matching ' - 'functions shape.'.format(illuminant.name, cmfs.name)) - illuminant = illuminant.copy().align(cmfs.shape) + cmfs, illuminant = handle_spectral_arguments(cmfs, illuminant) N = len(cmfs.shape) R_to_XYZ = np.transpose( - np.expand_dims(illuminant.values, axis=1) * cmfs.values / (np.sum( - cmfs.values[:, 1] * illuminant.values))) + illuminant.values[..., np.newaxis] + * cmfs.values + / (np.sum(cmfs.values[:, 1] * illuminant.values)) + ) R_to_RGB = np.dot(colourspace.matrix_XYZ_to_RGB, R_to_XYZ) basis_to_RGB = block_diag(R_to_RGB, R_to_RGB, R_to_RGB) - primaries = np.identity(3).reshape(9) + primaries = np.reshape(np.identity(3), 9) - # Ensure the reflectances correspond to the correct RGB colours. + # Ensure that the reflectances correspond to the correct RGB colours. colour_match = LinearConstraint(basis_to_RGB, primaries, primaries) - # Ensure the reflectances are bounded by [0, 1]. + # Ensure that the reflectances are bounded by [0, 1]. energy_conservation = Bounds(np.zeros(3 * N), np.ones(3 * N)) - # Ensure the sum of the three bases is bounded by [0, 1]. + # Ensure that the sum of the three bases is bounded by [0, 1]. sum_matrix = np.transpose(np.tile(np.identity(N), (3, 1))) sum_constraint = LinearConstraint(sum_matrix, np.zeros(N), np.ones(N)) optimisation_settings = { - 'method': 'SLSQP', - 'constraints': [colour_match, sum_constraint], - 'bounds': energy_conservation, - 'options': { - 'ftol': 1e-10, - } + "method": "SLSQP", + "constraints": [colour_match, sum_constraint], + "bounds": energy_conservation, + "options": { + "ftol": 1e-10, + }, } if optimisation_kwargs is not None: optimisation_settings.update(optimisation_kwargs) result = minimize( - metric, args=metric_args, x0=np.zeros(3 * N), **optimisation_settings) + metric, args=metric_args, x0=np.zeros(3 * N), **optimisation_settings + ) - basis_functions = np.transpose(result.x.reshape(3, N)) + basis_functions = np.transpose(np.reshape(result.x, (3, N))) return MultiSpectralDistributions( basis_functions, cmfs.shape.range(), - name='Basis Functions - {0} - Mallett (2019)'.format(colourspace.name), - labels=('red', 'green', 'blue')) + name=f"Basis Functions - {colourspace.name} - Mallett (2019)", + labels=("red", "green", "blue"), + ) def RGB_to_sd_Mallett2019( - RGB, basis_functions=MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019): + RGB: ArrayLike, + basis_functions: MultiSpectralDistributions = MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019, +) -> SpectralDistribution: """ - Recovers the spectral distribution of given *RGB* colourspace array using + Recover the spectral distribution of given *RGB* colourspace array using *Mallett and Yuksel (2019)* method. Parameters ---------- - RGB : array_like, (3,) + RGB *RGB* colourspace array. - basis_functions : MultiSpectralDistributions + basis_functions Basis functions for the method. The default is to use the built-in *sRGB* basis functions, i.e. :attr:`colour.recovery.MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019`. Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Recovered reflectance. References @@ -243,21 +249,20 @@ def RGB_to_sd_Mallett2019( Examples -------- - >>> from colour.colorimetry import SDS_ILLUMINANTS, sd_to_XYZ_integration - >>> from colour.models import XYZ_to_sRGB + >>> from colour import MSDS_CMFS, SDS_ILLUMINANTS, XYZ_to_sRGB + >>> from colour.colorimetry import sd_to_XYZ_integration >>> from colour.recovery import SPECTRAL_SHAPE_sRGB_MALLETT2019 >>> from colour.utilities import numpy_print_options >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> RGB = XYZ_to_sRGB(XYZ, apply_cctf_encoding=False) >>> cmfs = ( - ... MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']. + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SPECTRAL_SHAPE_sRGB_MALLETT2019) ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) >>> sd = RGB_to_sd_Mallett2019(RGB) >>> with numpy_print_options(suppress=True): - ... # Doctests skip for Python 2.x compatibility. - ... sd # doctest: +SKIP + ... sd # doctest: +ELLIPSIS SpectralDistribution([[ 380. , 0.1735531...], [ 385. , 0.1720357...], [ 390. , 0.1677721...], @@ -352,7 +357,8 @@ def RGB_to_sd_Mallett2019( sd = SpectralDistribution( np.dot(RGB, np.transpose(basis_functions.values)), - basis_functions.wavelengths) - sd.name = '{0} (RGB) - Mallett (2019)'.format(RGB) + basis_functions.wavelengths, + ) + sd.name = f"{RGB} (RGB) - Mallett (2019)" return sd diff --git a/colour/recovery/meng2015.py b/colour/recovery/meng2015.py index 2d75b6fc51..1a9a24d9a1 100644 --- a/colour/recovery/meng2015.py +++ b/colour/recovery/meng2015.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ Meng et al. (2015) - Reflectance Recovery ========================================= -Defines objects for reflectance recovery using *Meng, Simon and Hanika (2015)* -method: +Defines the objects for reflectance recovery using +*Meng, Simon and Hanika (2015)* method: - :func:`colour.recovery.XYZ_to_sd_Meng2015` @@ -15,74 +14,74 @@ Graphics Forum, 34(4), 31-40. doi:10.1111/cgf.12676 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from scipy.optimize import minimize -from colour.colorimetry import (MSDS_CMFS_STANDARD_OBSERVER, SDS_ILLUMINANTS, - SpectralDistribution, SpectralShape, sd_ones, - sd_to_XYZ_integration) -from colour.utilities import to_domain_1, from_range_100, runtime_warning -from colour.utilities.deprecation import handle_arguments_deprecation - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['SPECTRAL_SHAPE_MENG2015', 'XYZ_to_sd_Meng2015'] - -SPECTRAL_SHAPE_MENG2015 = SpectralShape(360, 780, 5) +from colour.colorimetry import ( + MultiSpectralDistributions, + SpectralDistribution, + SpectralShape, + handle_spectral_arguments, + sd_ones, + sd_to_XYZ_integration, +) +from colour.hints import ArrayLike, Dict, FloatingOrNDArray, NDArray, Optional +from colour.utilities import to_domain_1, from_range_100 + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "SPECTRAL_SHAPE_MENG2015", + "XYZ_to_sd_Meng2015", +] + +SPECTRAL_SHAPE_MENG2015: SpectralShape = SpectralShape(360, 780, 5) """ Spectral shape according to *ASTM E308-15* practise shape but using an interval of 5. - -SPECTRAL_SHAPE_MENG2015 : SpectralShape """ def XYZ_to_sd_Meng2015( - XYZ, - cmfs=MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer'] - .copy().align(SPECTRAL_SHAPE_MENG2015), - illuminant=SDS_ILLUMINANTS['D65'].copy().align( - SPECTRAL_SHAPE_MENG2015), - optimisation_kwargs=None, - **kwargs): + XYZ: ArrayLike, + cmfs: Optional[MultiSpectralDistributions] = None, + illuminant: Optional[SpectralDistribution] = None, + optimisation_kwargs: Optional[Dict] = None, +) -> SpectralDistribution: """ - Recovers the spectral distribution of given *CIE XYZ* tristimulus values + Recover the spectral distribution of given *CIE XYZ* tristimulus values using *Meng et al. (2015)* method. Parameters ---------- - XYZ : array_like, (3,) + XYZ *CIE XYZ* tristimulus values to recover the spectral distribution from. - cmfs : XYZ_ColourMatchingFunctions + cmfs Standard observer colour matching functions. The wavelength :math:`\\lambda_{i}` range interval of the colour matching functions affects directly the time the computations take. The current default - interval of 5 is a good compromise between precision and time spent. - illuminant : SpectralDistribution, optional - Illuminant spectral distribution. - optimisation_kwargs : dict_like, optional + interval of 5 is a good compromise between precision and time spent, + default to the *CIE 1931 2 Degree Standard Observer*. + illuminant + Illuminant spectral distribution, default to + *CIE Standard Illuminant D65*. + optimisation_kwargs Parameters for :func:`scipy.optimize.minimize` definition. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Recovered spectral distribution. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -101,60 +100,60 @@ def XYZ_to_sd_Meng2015( Examples -------- + >>> from colour import MSDS_CMFS, SDS_ILLUMINANTS >>> from colour.utilities import numpy_print_options >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> cmfs = ( - ... MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']. + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SpectralShape(360, 780, 10)) ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) >>> sd = XYZ_to_sd_Meng2015(XYZ, cmfs, illuminant) >>> with numpy_print_options(suppress=True): - ... # Doctests skip for Python 2.x compatibility. ... sd # doctest: +SKIP - SpectralDistribution([[ 360. , 0.0765153...], - [ 370. , 0.0764771...], - [ 380. , 0.0764286...], - [ 390. , 0.0764329...], - [ 400. , 0.0765863...], - [ 410. , 0.0764339...], - [ 420. , 0.0757213...], - [ 430. , 0.0733091...], - [ 440. , 0.0676493...], - [ 450. , 0.0577616...], - [ 460. , 0.0440805...], - [ 470. , 0.0284802...], - [ 480. , 0.0138019...], - [ 490. , 0.0033557...], + SpectralDistribution([[ 360. , 0.0762005...], + [ 370. , 0.0761792...], + [ 380. , 0.0761363...], + [ 390. , 0.0761194...], + [ 400. , 0.0762539...], + [ 410. , 0.0761671...], + [ 420. , 0.0754649...], + [ 430. , 0.0731519...], + [ 440. , 0.0676701...], + [ 450. , 0.0577800...], + [ 460. , 0.0441993...], + [ 470. , 0.0285064...], + [ 480. , 0.0138728...], + [ 490. , 0.0033585...], [ 500. , 0. ...], [ 510. , 0. ...], [ 520. , 0. ...], [ 530. , 0. ...], - [ 540. , 0.0055360...], - [ 550. , 0.0317335...], - [ 560. , 0.075457 ...], - [ 570. , 0.1314930...], - [ 580. , 0.1938219...], - [ 590. , 0.2559747...], - [ 600. , 0.3122869...], - [ 610. , 0.3584363...], - [ 620. , 0.3927112...], - [ 630. , 0.4158866...], - [ 640. , 0.4305832...], - [ 650. , 0.4391142...], - [ 660. , 0.4439484...], - [ 670. , 0.4464121...], - [ 680. , 0.4475718...], - [ 690. , 0.4481182...], - [ 700. , 0.4483734...], - [ 710. , 0.4484743...], - [ 720. , 0.4485753...], - [ 730. , 0.4486474...], - [ 740. , 0.4486629...], - [ 750. , 0.4486995...], - [ 760. , 0.4486925...], - [ 770. , 0.4486794...], - [ 780. , 0.4486982...]], + [ 540. , 0.0055767...], + [ 550. , 0.0317581...], + [ 560. , 0.0754491...], + [ 570. , 0.1314115...], + [ 580. , 0.1937649...], + [ 590. , 0.2559311...], + [ 600. , 0.3123173...], + [ 610. , 0.3584966...], + [ 620. , 0.3927335...], + [ 630. , 0.4159458...], + [ 640. , 0.4306660...], + [ 650. , 0.4391040...], + [ 660. , 0.4439497...], + [ 670. , 0.4463618...], + [ 680. , 0.4474625...], + [ 690. , 0.4479868...], + [ 700. , 0.4482116...], + [ 710. , 0.4482800...], + [ 720. , 0.4483472...], + [ 730. , 0.4484251...], + [ 740. , 0.4484633...], + [ 750. , 0.4485071...], + [ 760. , 0.4484969...], + [ 770. , 0.4484853...], + [ 780. , 0.4485134...]], interpolator=SpragueInterpolator, interpolator_kwargs={}, extrapolator=Extrapolator, @@ -163,49 +162,36 @@ def XYZ_to_sd_Meng2015( array([ 0.2065400..., 0.1219722..., 0.0513695...]) """ - optimisation_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['optimisation_parameters', 'optimisation_kwargs'] - ], - }, **kwargs).get('optimisation_kwargs', optimisation_kwargs) - XYZ = to_domain_1(XYZ) - if illuminant.shape != cmfs.shape: - runtime_warning( - 'Aligning "{0}" illuminant shape to "{1}" colour matching ' - 'functions shape.'.format(illuminant.name, cmfs.name)) - illuminant = illuminant.copy().align(cmfs.shape) + cmfs, illuminant = handle_spectral_arguments( + cmfs, illuminant, shape_default=SPECTRAL_SHAPE_MENG2015 + ) sd = sd_ones(cmfs.shape) - def objective_function(a): - """ - Objective function. - """ + def objective_function(a: ArrayLike) -> FloatingOrNDArray: + """Define the objective function.""" return np.sum(np.diff(a) ** 2) - def constraint_function(a): - """ - Function defining the constraint. - """ + def constraint_function(a: ArrayLike) -> NDArray: + """Define the constraint function.""" sd[:] = a - return sd_to_XYZ_integration( - sd, cmfs=cmfs, illuminant=illuminant) - XYZ + return ( + sd_to_XYZ_integration(sd, cmfs=cmfs, illuminant=illuminant) - XYZ + ) wavelengths = sd.wavelengths bins = wavelengths.size optimisation_settings = { - 'method': 'SLSQP', - 'constraints': { - 'type': 'eq', - 'fun': constraint_function - }, - 'bounds': np.tile(np.array([0, 1000]), (bins, 1)), - 'options': { - 'ftol': 1e-10, + "method": "SLSQP", + "constraints": {"type": "eq", "fun": constraint_function}, + "bounds": np.tile(np.array([0, 1000]), (bins, 1)), + "options": { + "ftol": 1e-10, }, } if optimisation_kwargs is not None: @@ -215,10 +201,12 @@ def constraint_function(a): if not result.success: raise RuntimeError( - 'Optimization failed for {0} after {1} iterations: "{2}".'.format( - XYZ, result.nit, result.message)) + f"Optimization failed for {XYZ} after {result.nit} iterations: " + f'"{result.message}".' + ) return SpectralDistribution( from_range_100(result.x * 100), wavelengths, - name='{0} (XYZ) - Meng (2015)'.format(XYZ)) + name=f"{XYZ} (XYZ) - Meng (2015)", + ) diff --git a/colour/recovery/otsu2018.py b/colour/recovery/otsu2018.py index 2380a38bc7..e1cd6cc09d 100644 --- a/colour/recovery/otsu2018.py +++ b/colour/recovery/otsu2018.py @@ -1,14 +1,13 @@ -# -*- coding: utf-8 -*- """ Otsu, Yamamoto and Hachisuka (2018) - Reflectance Recovery ========================================================== -Defines objects for reflectance recovery, i.e. spectral upsampling, using +Defines the objects for reflectance recovery, i.e. spectral upsampling, using *Otsu et al. (2018)* method: - :class:`colour.recovery.Dataset_Otsu2018` - :func:`colour.recovery.XYZ_to_sd_Otsu2018` -- :func:`colour.recovery.NodeTree_Otsu2018` +- :func:`colour.recovery.Tree_Otsu2018` References ---------- @@ -17,64 +16,96 @@ Graphics Forum, 37(6), 370-381. doi:10.1111/cgf.13332 """ -from __future__ import division, print_function, unicode_literals +from __future__ import annotations import numpy as np -import six -from collections import namedtuple - -from colour.colorimetry import (MSDS_CMFS_STANDARD_OBSERVER, SDS_ILLUMINANTS, - SpectralDistribution, SpectralShape, - msds_to_XYZ, sd_to_XYZ) +from dataclasses import dataclass + +from colour.colorimetry import ( + MultiSpectralDistributions, + SpectralDistribution, + SpectralShape, + handle_spectral_arguments, + msds_to_XYZ_integration, + reshape_msds, + sd_to_XYZ, +) +from colour.hints import ( + ArrayLike, + Boolean, + Callable, + Dict, + Floating, + Integer, + List, + NDArray, + Optional, + Tuple, + cast, +) from colour.models import XYZ_to_xy -from colour.recovery import (SPECTRAL_SHAPE_OTSU2018, BASIS_FUNCTIONS_OTSU2018, - CLUSTER_MEANS_OTSU2018, SELECTOR_ARRAY_OTSU2018) -from colour.utilities import (as_float_array, domain_range_scale, - is_tqdm_installed, message_box, runtime_warning, - to_domain_1, zeros) +from colour.recovery import ( + SPECTRAL_SHAPE_OTSU2018, + BASIS_FUNCTIONS_OTSU2018, + CLUSTER_MEANS_OTSU2018, + SELECTOR_ARRAY_OTSU2018, +) +from colour.utilities import ( + Node, + as_float_array, + domain_range_scale, + is_tqdm_installed, + message_box, + optional, + to_domain_1, + zeros, +) -if six.PY3: - from unittest import mock -else: - import mock if is_tqdm_installed(): from tqdm import tqdm -else: +else: # pragma: no cover + from unittest import mock + tqdm = mock.MagicMock() -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'Dataset_Otsu2018', 'DATASET_REFERENCE_OTSU2018', 'XYZ_to_sd_Otsu2018', - 'PartitionAxis', 'ColourData', 'Node', 'NodeTree_Otsu2018' + "Dataset_Otsu2018", + "DATASET_REFERENCE_OTSU2018", + "XYZ_to_sd_Otsu2018", + "PartitionAxis", + "Data_Otsu2018", + "Node_Otsu2018", + "Tree_Otsu2018", ] -class Dataset_Otsu2018(object): +class Dataset_Otsu2018: """ - Stores all the information needed for the *Otsu et al. (2018)* spectral + Store all the information needed for the *Otsu et al. (2018)* spectral upsampling method. Datasets can be either generated and converted as a :class:`colour.recovery.Dataset_Otsu2018` class instance using the - :meth:`colour.recovery.NodeTree_Otsu2018.to_dataset` method or + :meth:`colour.recovery.Tree_Otsu2018.to_dataset` method or alternatively, loaded from disk with the :meth:`colour.recovery.Dataset_Otsu2018.read` method. Parameters ---------- - shape: SpectralShape + shape Shape of the spectral data. - basis_functions : array_like, (n, 3, m) + basis_functions Three basis functions for every cluster. - means : array_like, (n, m) + means Mean for every cluster. - selector_array : array_like, (k, 4) + selector_array Array describing how to select the appropriate cluster. See :meth:`colour.recovery.Dataset_Otsu2018.select` method for details. @@ -102,11 +133,11 @@ class Dataset_Otsu2018(object): >>> import os >>> import colour >>> from colour.characterisation import SDS_COLOURCHECKERS - >>> reflectances = [ - ... sd.copy().align(SPECTRAL_SHAPE_OTSU2018).values - ... for sd in SDS_COLOURCHECKERS['ColorChecker N Ohta'].values() - ... ] - >>> node_tree = NodeTree_Otsu2018(reflectances) + >>> from colour.colorimetry import sds_and_msds_to_msds + >>> reflectances = sds_and_msds_to_msds( + ... SDS_COLOURCHECKERS['ColorChecker N Ohta'].values() + ... ) + >>> node_tree = Tree_Otsu2018(reflectances) >>> node_tree.optimise(iterations=2, print_callable=lambda x: x) >>> dataset = node_tree.to_dataset() >>> path = os.path.join(colour.__path__[0], 'recovery', 'tests', @@ -116,161 +147,188 @@ class Dataset_Otsu2018(object): >>> dataset.read(path) # doctest: +SKIP """ - def __init__(self, - shape=None, - basis_functions=None, - means=None, - selector_array=None): - self._shape = shape - self._basis_functions = as_float_array(basis_functions) - self._means = as_float_array(means) - self._selector_array = selector_array + def __init__( + self, + shape: Optional[SpectralShape] = None, + basis_functions: Optional[NDArray] = None, + means: Optional[NDArray] = None, + selector_array: Optional[NDArray] = None, + ): + self._shape: Optional[SpectralShape] = shape + self._basis_functions: Optional[NDArray] = ( + basis_functions + if basis_functions is None + else as_float_array(basis_functions) + ) + self._means: Optional[NDArray] = ( + means if means is None else as_float_array(means) + ) + self._selector_array: Optional[NDArray] = ( + selector_array + if selector_array is None + else as_float_array(selector_array) + ) @property - def shape(self): + def shape(self) -> Optional[SpectralShape]: """ Getter property for the shape used by the *Otsu et al. (2018)* dataset. Returns ------- - SpectralShape + :py:data:`None` or :class:`colour.SpectralShape` Shape used by the *Otsu et al. (2018)* dataset. """ return self._shape @property - def basis_functions(self): + def basis_functions(self) -> Optional[NDArray]: """ Getter property for the basis functions of the *Otsu et al. (2018)* dataset. Returns ------- - ndarray + :py:data:`None` or :class:`numpy.ndarray` Basis functions of the *Otsu et al. (2018)* dataset. """ return self._basis_functions @property - def means(self): + def means(self) -> Optional[NDArray]: """ Getter property for means of the *Otsu et al. (2018)* dataset. Returns ------- - int + :py:data:`None` or :class:`numpy.ndarray` Means of the *Otsu et al. (2018)* dataset. """ return self._means @property - def selector_array(self): + def selector_array(self) -> Optional[NDArray]: """ Getter property for the selector array of the *Otsu et al. (2018)* dataset. Returns ------- - ndarray + :py:data:`None` or :class:`numpy.ndarray` Selector array of the *Otsu et al. (2018)* dataset. """ return self._selector_array - def __str__(self): + def __str__(self) -> str: """ - Returns a formatted string representation of the dataset. + Return a formatted string representation of the dataset. Returns ------- - unicode + :class:`str` Formatted string representation. """ - return '{0}({1} basis functions)'.format( - self.__class__.__name__, self._basis_functions.shape[0]) + if self._basis_functions is not None: + return ( + f"{self.__class__.__name__}" + f"({self._basis_functions.shape[0]} basis functions)" + ) + else: + return f"{self.__class__.__name__}()" - def select(self, xy): + def select(self, xy: ArrayLike) -> Integer: """ - Returns the cluster index appropriate for the given *CIE xy* + Return the cluster index appropriate for the given *CIE xy* coordinates. Parameters ---------- - xy : array_like, (2,) + xy *CIE xy* chromaticity coordinates. Returns ------- - int + :class:`numpy.integer` Cluster index. + + Raises + ------ + ValueError + If the selector array is undefined. """ - i = 0 - while True: - row = self._selector_array[i, :] - direction, origin, lesser_index, greater_index = row + xy = as_float_array(xy) + + if self._selector_array is not None: + i = 0 + while True: + row = self._selector_array[i, :] + origin, direction, lesser_index, greater_index = row - if xy[int(direction)] <= origin: - index = int(lesser_index) - else: - index = int(greater_index) + if xy[int(direction)] <= origin: + index = int(lesser_index) + else: + index = int(greater_index) - if index < 0: - i = -index - else: - return index + if index < 0: + i = -index + else: + return index + else: + raise ValueError('The "selector array" is undefined!') - def cluster(self, xy): + def cluster(self, xy: ArrayLike) -> Tuple[NDArray, NDArray]: """ - Returns the basis functions and dataset mean for the given *CIE xy* + Return the basis functions and dataset mean for the given *CIE xy* coordinates. Parameters ---------- - xy : array_like, (2,) + xy *CIE xy* chromaticity coordinates. Returns ------- - basis_functions : ndarray, (3, n) - Three basis functions. - mean : ndarray, (n,) - Dataset mean. + :class:`tuple` + Tuple of three basis functions and dataset mean. + + Raises + ------ + ValueError + If the basis functions or means are undefined. """ - index = self.select(xy) + if self._basis_functions is not None and self._means is not None: + index = self.select(xy) - return self._basis_functions[index, :, :], self._means[index, :] + return self._basis_functions[index, :, :], self._means[index, :] + else: + raise ValueError('The "basis functions" or "means" are undefined!') - def read(self, path): + def read(self, path: str): """ - Reads and loads a dataset from an *.npz* file. + Read and loads a dataset from an *.npz* file. Parameters ---------- - path : unicode + path Path to the file. - Raises - ------ - ValueError, KeyError - Raised when loading the file succeeded but it did not contain the - expected data. - Examples -------- >>> import os >>> import colour >>> from colour.characterisation import SDS_COLOURCHECKERS - >>> reflectances = [ - ... sd.copy().align(SPECTRAL_SHAPE_OTSU2018).values - ... for sd in SDS_COLOURCHECKERS['ColorChecker N Ohta'].values() - ... ] - >>> node_tree = NodeTree_Otsu2018(reflectances) + >>> from colour.colorimetry import sds_and_msds_to_msds + >>> reflectances = sds_and_msds_to_msds( + ... SDS_COLOURCHECKERS['ColorChecker N Ohta'].values() + ... ) + >>> node_tree = Tree_Otsu2018(reflectances) >>> node_tree.optimise(iterations=2, print_callable=lambda x: x) >>> dataset = node_tree.to_dataset() >>> path = os.path.join(colour.__path__[0], 'recovery', 'tests', @@ -280,43 +338,38 @@ def read(self, path): >>> dataset.read(path) # doctest: +SKIP """ - npz = np.load(path) - - if not isinstance(npz, np.lib.npyio.NpzFile): - raise ValueError('The loaded file is not an ".npz" type file!') + data = np.load(path) - start, end, interval = npz['shape'] + start, end, interval = data["shape"] self._shape = SpectralShape(start, end, interval) - self._basis_functions = npz['basis_functions'] - self._means = npz['means'] - self._selector_array = npz['selector_array'] - - n, three, m = self._basis_functions.shape - if (three != 3 or self._means.shape != (n, m) or - self._selector_array.shape[1] != 4): - raise ValueError( - 'Unexpected array shapes encountered, the file could be ' - 'corrupted or in a wrong format!') + self._basis_functions = data["basis_functions"] + self._means = data["means"] + self._selector_array = data["selector_array"] - def write(self, path): + def write(self, path: str): """ - Writes the dataset to an *.npz* file at given path. + Write the dataset to an *.npz* file at given path. Parameters ---------- - path : unicode + path Path to the file. + Raises + ------ + ValueError + If the shape is undefined. + Examples -------- >>> import os >>> import colour >>> from colour.characterisation import SDS_COLOURCHECKERS - >>> reflectances = [ - ... sd.copy().align(SPECTRAL_SHAPE_OTSU2018).values - ... for sd in SDS_COLOURCHECKERS['ColorChecker N Ohta'].values() - ... ] - >>> node_tree = NodeTree_Otsu2018(reflectances) + >>> from colour.colorimetry import sds_and_msds_to_msds + >>> reflectances = sds_and_msds_to_msds( + ... SDS_COLOURCHECKERS['ColorChecker N Ohta'].values() + ... ) + >>> node_tree = Tree_Otsu2018(reflectances) >>> node_tree.optimise(iterations=2, print_callable=lambda x: x) >>> dataset = node_tree.to_dataset() >>> path = os.path.join(colour.__path__[0], 'recovery', 'tests', @@ -324,20 +377,30 @@ def write(self, path): >>> dataset.write(path) # doctest: +SKIP """ - shape_array = as_float_array( - [self._shape.start, self._shape.end, self._shape.interval]) - - np.savez( - path, - shape=shape_array, - basis_functions=self._basis_functions, - means=self._means, - selector_array=self._selector_array) + if self._shape is not None: + np.savez( + path, + shape=as_float_array( + [ + self._shape.start, + self._shape.end, + self._shape.interval, + ] + ), + basis_functions=cast(NDArray, self._basis_functions), + means=cast(NDArray, self._means), + selector_array=cast(NDArray, self._selector_array), + ) + else: + raise ValueError('The "shape" is undefined!') -DATASET_REFERENCE_OTSU2018 = Dataset_Otsu2018( - SPECTRAL_SHAPE_OTSU2018, BASIS_FUNCTIONS_OTSU2018, CLUSTER_MEANS_OTSU2018, - SELECTOR_ARRAY_OTSU2018) +DATASET_REFERENCE_OTSU2018: Dataset_Otsu2018 = Dataset_Otsu2018( + SPECTRAL_SHAPE_OTSU2018, + BASIS_FUNCTIONS_OTSU2018, + CLUSTER_MEANS_OTSU2018, + SELECTOR_ARRAY_OTSU2018, +) """ Builtin *Otsu et al. (2018)* dataset as a :class:`colour.recovery.Dataset_Otsu2018` class instance, usable by @@ -346,29 +409,30 @@ def write(self, path): def XYZ_to_sd_Otsu2018( - XYZ, - cmfs=MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer'] - .copy().align(SPECTRAL_SHAPE_OTSU2018), - illuminant=SDS_ILLUMINANTS['D65'].copy().align( - SPECTRAL_SHAPE_OTSU2018), - dataset=DATASET_REFERENCE_OTSU2018, - clip=True): + XYZ: ArrayLike, + cmfs: Optional[MultiSpectralDistributions] = None, + illuminant: Optional[SpectralDistribution] = None, + dataset: Dataset_Otsu2018 = DATASET_REFERENCE_OTSU2018, + clip: Boolean = True, +) -> SpectralDistribution: """ - Recovers the spectral distribution of given *CIE XYZ* tristimulus values + Recover the spectral distribution of given *CIE XYZ* tristimulus values using *Otsu et al. (2018)* method. Parameters ---------- - XYZ : array_like, (3,) + XYZ *CIE XYZ* tristimulus values to recover the spectral distribution from. - cmfs : XYZ_ColourMatchingFunctions, optional - Standard observer colour matching functions. - illuminant : SpectralDistribution, optional - Illuminant spectral distribution. - dataset : Dataset_Otsu2018, optional + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + illuminant + Illuminant spectral distribution, default to + *CIE Standard Illuminant D65*. + dataset Dataset to use for reconstruction. The default is to use the published data. - clip : bool, optional + clip If *True*, the default, values below zero and above unity in the recovered spectral distributions will be clipped. This ensures that the returned reflectance is physical and conserves energy, but will cause @@ -376,29 +440,34 @@ def XYZ_to_sd_Otsu2018( Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Recovered spectral distribution. Its shape is always that of the :class:`colour.recovery.SPECTRAL_SHAPE_OTSU2018` class instance. + Raises + ------ + ValueError + If the dataset shape is undefined. + References ---------- :cite:`Otsu2018` Examples -------- - >>> from colour.colorimetry import CCS_ILLUMINANTS, sd_to_XYZ_integration - >>> from colour.models import XYZ_to_sRGB + >>> from colour import ( + ... CCS_ILLUMINANTS, SDS_ILLUMINANTS, MSDS_CMFS, XYZ_to_sRGB) + >>> from colour.colorimetry import sd_to_XYZ_integration >>> from colour.utilities import numpy_print_options >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> cmfs = ( - ... MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']. + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SPECTRAL_SHAPE_OTSU2018) ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) >>> sd = XYZ_to_sd_Otsu2018(XYZ, cmfs, illuminant) >>> with numpy_print_options(suppress=True): - ... # Doctests skip for Python 2.x compatibility. - ... sd # doctest: +SKIP + ... sd # doctest: +ELLIPSIS SpectralDistribution([[ 380. , 0.0601939...], [ 390. , 0.0568063...], [ 400. , 0.0517429...], @@ -443,44 +512,54 @@ def XYZ_to_sd_Otsu2018( array([ 0.2065494..., 0.1219712..., 0.0514002...]) """ - XYZ = to_domain_1(XYZ) - xy = XYZ_to_xy(XYZ) + shape = dataset.shape + if shape is not None: + XYZ = to_domain_1(XYZ) + + cmfs, illuminant = handle_spectral_arguments( + cmfs, illuminant, shape_default=SPECTRAL_SHAPE_OTSU2018 + ) + + xy = XYZ_to_xy(XYZ) - basis_functions, mean = dataset.cluster(xy) + basis_functions, mean = dataset.cluster(xy) - M = np.empty((3, 3)) - for i in range(3): - sd = SpectralDistribution(basis_functions[i, :], dataset.shape.range()) + M = np.empty((3, 3)) + for i in range(3): + sd = SpectralDistribution(basis_functions[i, :], shape.range()) - with domain_range_scale('ignore'): - M[:, i] = sd_to_XYZ(sd, cmfs, illuminant) / 100 + with domain_range_scale("ignore"): + M[:, i] = sd_to_XYZ(sd, cmfs, illuminant) / 100 - M_inverse = np.linalg.inv(M) + M_inverse = np.linalg.inv(M) - sd = SpectralDistribution(mean, dataset.shape.range()) + sd = SpectralDistribution(mean, shape.range()) - with domain_range_scale('ignore'): - XYZ_mu = sd_to_XYZ(sd, cmfs, illuminant) / 100 + with domain_range_scale("ignore"): + XYZ_mu = sd_to_XYZ(sd, cmfs, illuminant) / 100 - weights = np.dot(M_inverse, XYZ - XYZ_mu) - recovered_sd = np.dot(weights, basis_functions) + mean + weights = np.dot(M_inverse, XYZ - XYZ_mu) + recovered_sd = np.dot(weights, basis_functions) + mean - recovered_sd = np.clip(recovered_sd, 0, 1) if clip else recovered_sd + recovered_sd = np.clip(recovered_sd, 0, 1) if clip else recovered_sd - return SpectralDistribution(recovered_sd, dataset.shape.range()) + return SpectralDistribution(recovered_sd, shape.range()) + else: + raise ValueError('The dataset "shape" is undefined!') -class PartitionAxis(namedtuple('PartitionAxis', ('origin', 'direction'))): +@dataclass +class PartitionAxis: """ - Represents a horizontal or vertical line, partitioning the 2D space in + Represent a horizontal or vertical line, partitioning the 2D space in two half-planes. Parameters ---------- - origin : numeric + origin The x coordinate of a vertical line or the y coordinate of a horizontal line. - direction : int + direction *0* if vertical, *1* if horizontal. Methods @@ -488,620 +567,505 @@ class PartitionAxis(namedtuple('PartitionAxis', ('origin', 'direction'))): - :meth:`~colour.recovery.otsu2018.PartitionAxis.__str__` """ + origin: Floating + direction: Integer + def __str__(self): """ - Returns a formatted string representation of the partition axis. + Return a formatted string representation of the partition axis. Returns ------- - unicode + :class:`str` Formatted string representation. """ - return '{0}({1} partition at {2} = {3})'.format( - self.__class__.__name__, 'horizontal' - if self.direction else 'vertical', 'y' - if self.direction else 'x', self.origin) + return ( + f"{self.__class__.__name__}" + f"({'horizontal' if self.direction else 'vertical'} partition " + f"at {'y' if self.direction else 'x'} = {self.origin})" + ) -class ColourData(object): +class Data_Otsu2018: """ - Represents the data for multiple colours: their spectral reflectance - distributions, *CIE XYZ* tristimulus values and *CIE xy* coordinates. The - standard observer colour matching functions and illuminant are accessed via - the parent tree. + Store the reference reflectances and derived information along with the + methods to process them for a leaf :class:`colour.recovery.otsu2018.Node` + class instance. This class also supports partitioning: Creating two smaller instances of - :class:`colour.recovery.otsu2018.ColourData` class by splitting along a + :class:`colour.recovery.otsu2018.Data` class by splitting along an horizontal or a vertical axis on the *CIE xy* plane. Parameters ---------- - tree : NodeTree_Otsu2018, optional - The parent tree which determines the standard observer colour matching - functions and illuminant used in colourimetric calculations. - reflectances : ndarray, (n, m), optional - Reflectances of the *n* colours to be stored in this class. The shape - must match ``tree.shape`` with *m* points for each colour. + reflectances + Reference reflectances of the *n* colours to be stored. + The shape must match ``tree.shape`` with *m* points for each colour. + cmfs + Standard observer colour matching functions. + illuminant + Illuminant spectral distribution. Attributes ---------- - - :attr:`~colour.recovery.otsu2018.ColourData.tree` - - :attr:`~colour.recovery.otsu2018.ColourData.reflectances` - - :attr:`~colour.recovery.otsu2018.ColourData.XYZ` - - :attr:`~colour.recovery.otsu2018.ColourData.xy` + - :attr:`~colour.recovery.otsu2018.Data.reflectances` + - :attr:`~colour.recovery.otsu2018.Data.cmfs` + - :attr:`~colour.recovery.otsu2018.Data.illuminant` + - :attr:`~colour.recovery.otsu2018.Data.basis_functions` + - :attr:`~colour.recovery.otsu2018.Data.mean` Methods ------- - - :meth:`~colour.recovery.otsu2018.ColourData.__init__` - - :meth:`~colour.recovery.otsu2018.ColourData.__str__` - - :meth:`~colour.recovery.otsu2018.ColourData.__len__` - - :meth:`~colour.recovery.otsu2018.ColourData.partition` + - :meth:`~colour.recovery.otsu2018.Data.__str__` + - :meth:`~colour.recovery.otsu2018.Data.__len__` + - :meth:`~colour.recovery.otsu2018.Data.origin` + - :meth:`~colour.recovery.otsu2018.Data.partition` + - :meth:`~colour.recovery.otsu2018.Data.PCA` + - :meth:`~colour.recovery.otsu2018.Data.reconstruct` + - :meth:`~colour.recovery.otsu2018.Data.reconstruction_error` """ - def __init__(self, tree, reflectances): - self._tree = tree - self._XYZ = None - self._xy = None - self._reflectances = None - self.reflectances = reflectances - - @property - def tree(self): - """ - Getter property for the colour data tree. - - Returns - ------- - NodeTree_Otsu2018 - Colour data tree. - """ - - return self._tree + def __init__( + self, + reflectances: Optional[ArrayLike], + cmfs: MultiSpectralDistributions, + illuminant: SpectralDistribution, + ): + self._cmfs: MultiSpectralDistributions = cmfs + self._illuminant: SpectralDistribution = illuminant + + self._XYZ: Optional[NDArray] = None + self._xy: Optional[NDArray] = None + # TODO: Remove pragma when https://github.com/python/mypy/issues/3004 + # is resolved. + self._reflectances: NDArray = np.array([]) + self.reflectances = reflectances # type: ignore[assignment] + + self._basis_functions: Optional[NDArray] = None + self._mean: Optional[Floating] = None + self._M: Optional[NDArray] = None + self._XYZ_mu: Optional[NDArray] = None + + self._reconstruction_error: Optional[Floating] = None @property - def reflectances(self): + def reflectances(self) -> NDArray: """ - Getter and setter property for the colour data reflectances. + Getter and setter property for the reference reflectances. Parameters ---------- - value : array_like - Value to set the colour data reflectances with. + value + Value to set the reference reflectances with. Returns ------- - ndarray - Colour data reflectances. + :class:`numpy.ndarray` + Reference reflectances. """ return self._reflectances @reflectances.setter - def reflectances(self, value): - """ - Setter for the **self.reflectances** property. - """ + def reflectances(self, value: ArrayLike): + """Setter for the **self.reflectances** property.""" if value is not None: self._reflectances = as_float_array(value) - - self._XYZ = msds_to_XYZ( - self._reflectances, - self.tree.cmfs, - self.tree.illuminant, - method='Integration', - shape=self.tree.cmfs.shape) / 100 + self._XYZ = ( + msds_to_XYZ_integration( + self._reflectances, + self._cmfs, + self._illuminant, + shape=self._cmfs.shape, + ) + / 100 + ) self._xy = XYZ_to_xy(self._XYZ) + else: + self._reflectances, self._XYZ, self._xy = None, None, None @property - def XYZ(self): + def cmfs(self) -> MultiSpectralDistributions: """ - Getter property for the colour data *CIE XYZ* tristimulus values. + Getter property for the standard observer colour matching functions. Returns ------- - ndarray - Colour data *CIE XYZ* tristimulus values. + :class:`colour.MultiSpectralDistributions` + Standard observer colour matching functions. """ - return self._XYZ + return self._cmfs @property - def xy(self): + def illuminant(self) -> SpectralDistribution: """ - Getter property for the colour data *CIE xy* tristimulus values. + Getter property for the illuminant. Returns ------- - ndarray - Colour data *CIE xy* tristimulus values. + :class:`colour.SpectralDistribution` + Illuminant. """ - return self._xy + return self._illuminant - def __str__(self): + @property + def basis_functions(self) -> Optional[NDArray]: """ - Returns a formatted string representation of the colour data. + Getter property for the basis functions. Returns ------- - unicode - Formatted string representation. + :class:`numpy.ndarray` + Basis functions. """ - return '{0}({1} Reflectances)'.format(self.__class__.__name__, - len(self)) + return self._basis_functions - def __len__(self): + @property + def mean(self) -> Optional[Floating]: """ - Returns the number of colours in the colour data. + Getter property for the mean distribution. Returns ------- - int - Number of colours in the colour data. + :class:`numpy.floating` + Mean distribution. """ - return self._reflectances.shape[0] + return self._mean - def partition(self, axis): + def __str__(self) -> str: """ - Parameters - ---------- - axis : PartitionAxis - Partition axis used to partition the colour data. + Return a formatted string representation of the data. Returns ------- - lesser : ColourData - The left or lower part. - greater : ColourData - The right or upper part. + :class:`str` + Formatted string representation. """ - lesser = ColourData(self.tree, None) - greater = ColourData(self.tree, None) - - mask = self.xy[:, axis.direction] <= axis.origin - - lesser._reflectances = self.reflectances[mask, :] - greater._reflectances = self.reflectances[~mask, :] - - lesser._XYZ = self.XYZ[mask, :] - greater._XYZ = self.XYZ[~mask, :] - - lesser._xy = self.xy[mask, :] - greater._xy = self.xy[~mask, :] - - return lesser, greater - - -class Node(object): - """ - Represents a node in a :meth:`colour.recovery.NodeTree_Otsu2018` class - instance node tree. - - Parameters - ---------- - tree : NodeTree_Otsu2018 - The parent tree which determines the standard observer colour matching - functions and illuminant used in colourimetric calculations. - colour_data : ColourData - The colour data belonging to this node. - - Attributes - ---------- - - :attr:`~colour.recovery.otsu2018.Node.id` - - :attr:`~colour.recovery.otsu2018.Node.tree` - - :attr:`~colour.recovery.otsu2018.Node.colour_data` - - :attr:`~colour.recovery.otsu2018.Node.children` - - :attr:`~colour.recovery.otsu2018.Node.partition_axis` - - :attr:`~colour.recovery.otsu2018.Node.basis_functions` - - :attr:`~colour.recovery.otsu2018.Node.mean` - - :attr:`~colour.recovery.otsu2018.Node.leaves` - - Methods - ------- - - :meth:`~colour.recovery.otsu2018.Node.__init__` - - :meth:`~colour.recovery.otsu2018.Node.__str__` - - :meth:`~colour.recovery.otsu2018.Node.__len__` - - :meth:`~colour.recovery.otsu2018.Node.is_leaf` - - :meth:`~colour.recovery.otsu2018.Node.split` - - :meth:`~colour.recovery.otsu2018.Node.PCA` - - :meth:`~colour.recovery.otsu2018.Node.reconstruct` - - :meth:`~colour.recovery.otsu2018.Node.leaf_reconstruction_error` - - :meth:`~colour.recovery.otsu2018.Node.branch_reconstruction_error` - - :meth:`~colour.recovery.otsu2018.Node.partition_reconstruction_error` - - :meth:`~colour.recovery.otsu2018.Node.find_best_partition` - """ - - _NODE_COUNT = 1 - """ - Total node count. - - _NODE_COUNT : int - """ - - def __init__(self, tree, colour_data): - self._id = Node._NODE_COUNT - Node._NODE_COUNT += 1 + return f"{self.__class__.__name__}({len(self)} Reflectances)" - self._tree = tree - self._colour_data = colour_data - self._children = [] - self._partition_axis = None - self._mean = None - self._basis_functions = None - - self._M = None - self._M_inverse = None - self._XYZ_mu = None - - self._best_partition = None - self._cached_leaf_reconstruction_error = None - - @property - def id(self): + def __len__(self) -> Integer: """ - Getter property for the node id. + Return the number of colours in the data. Returns ------- - int - Node id. + :class:`numpy.integer` + Number of colours in the data. """ - return self._id - - @property - def tree(self): - """ - Getter property for the node tree. + return self._reflectances.shape[0] - Returns - ------- - NodeTree_Otsu2018 - Node tree. + def origin(self, i: Integer, direction: Integer) -> Floating: """ + Return the origin *CIE x* or *CIE y* chromaticity coordinate for given + index and direction. - return self._tree - - @property - def colour_data(self): - """ - Getter property for the node colour data. + Parameters + ---------- + i + Origin index. + direction + Origin direction. Returns ------- - ColourData - Node colour data. - """ - - return self._colour_data - - @property - def children(self): - """ - Getter property for the node children. + :class:`numpy.floating` + Origin *CIE x* or *CIE y* chromaticity coordinate. - Returns - ------- - tuple - Node children. + Raises + ------ + ValueError + If the chromaticity coordinates are undefined. """ - return self._children - - @property - def partition_axis(self): - """ - Getter property for the node partition axis. + if self._xy is not None: + return self._xy[i, direction] + else: + raise ValueError('The "chromaticity coordinates" are undefined!') - Returns - ------- - PartitionAxis - Node partition axis. + def partition( + self, axis: PartitionAxis + ) -> Tuple[Data_Otsu2018, Data_Otsu2018]: """ + Partition the data using given partition axis. - return self._partition_axis - - @property - def basis_functions(self): - """ - Getter property for the node basis functions. + Parameters + ---------- + axis + Partition axis used to partition the data. Returns ------- - array_like - Node basis functions. - """ - - return self._basis_functions + :class:`tuple` + Tuple of left or lower part and right or upper part. - @property - def mean(self): + Raises + ------ + ValueError + If the tristimulus values or chromaticity coordinates are + undefined. """ - Getter property for the node mean distribution. - Returns - ------- - array_like - Node mean distribution. - """ + lesser = Data_Otsu2018(None, self._cmfs, self._illuminant) + greater = Data_Otsu2018(None, self._cmfs, self._illuminant) - return self._mean + if self._XYZ is not None and self._xy is not None: + mask = self._xy[:, axis.direction] <= axis.origin - @property - def leaves(self): - """ - Getter property for the node leaves. + lesser._reflectances = self._reflectances[mask, :] + greater._reflectances = self._reflectances[~mask, :] - Returns - ------- - generator - Generator of all the leaves connected to this node. - """ + lesser._XYZ = self._XYZ[mask, :] + greater._XYZ = self._XYZ[~mask, :] - if self.is_leaf(): - yield self + lesser._xy = self._xy[mask, :] + greater._xy = self._xy[~mask, :] + + return lesser, greater else: - for child in self._children: - # TODO: Python 3 "yield from child.leaves". - for leaf in child.leaves: - yield leaf + raise ValueError( + 'The "tristimulus values" or "chromaticity coordinates" are ' + "undefined!" + ) - def __str__(self): + def PCA(self): """ - Returns a formatted string representation of the node. - - Returns - ------- - unicode - Formatted string representation. + Perform the *Principal Component Analysis* (PCA) on the data and sets + the relevant attributes accordingly. """ - return '{0}#{1}({2})'.format(self.__class__.__name__, self._id, - self._colour_data) + if self._M is None: + settings = { + "cmfs": self._cmfs, + "illuminant": self._illuminant, + "shape": self._cmfs.shape, + } + + self._mean = np.mean(self.reflectances, axis=0) + self._XYZ_mu = ( + msds_to_XYZ_integration(self._mean, **settings) / 100 + ) + + matrix_data = self.reflectances - self._mean + matrix_covariance = np.dot(np.transpose(matrix_data), matrix_data) + _eigenvalues, eigenvectors = np.linalg.eigh(matrix_covariance) + self._basis_functions = np.transpose(eigenvectors[:, -3:]) + + self._M = np.transpose( + msds_to_XYZ_integration(self._basis_functions, **settings) + / 100 + ) + + def reconstruct(self, XYZ: ArrayLike) -> SpectralDistribution: + """ + Reconstruct the reflectance for the given *CIE XYZ* tristimulus + values. - def __len__(self): - """ - Returns the number of children of the node. + Parameters + ---------- + XYZ + *CIE XYZ* tristimulus values to recover the spectral distribution + from. Returns ------- - int - Number of children of the node. + :class:`colour.SpectralDistribution` + Recovered spectral distribution. + + Raises + ------ + ValueError + If the matrix :math:`M`, the mean tristimulus values or the basis + functions are undefined. """ - return len(list(self.leaves)) + if ( + self._M is not None + and self._XYZ_mu is not None + and self._basis_functions is not None + ): + XYZ = as_float_array(XYZ) + + weights = np.dot(np.linalg.inv(self._M), XYZ - self._XYZ_mu) + reflectance = np.dot(weights, self._basis_functions) + self._mean + reflectance = np.clip(reflectance, 0, 1) + + return SpectralDistribution(reflectance, self._cmfs.wavelengths) + else: + raise ValueError( + 'The matrix "M", the "mean tristimulus values" or the ' + '"basis functions" are undefined!' + ) - def is_leaf(self): + def reconstruction_error(self) -> Floating: """ - Returns whether the node is a leaf. - :class:`colour.recovery.NodeTree_Otsu2018` class instance tree leaves - do not have any children and store instances of - :class:`colour.recovery.otsu2018.ColourData` class. + Return the reconstruction error of the data. The error is computed by + reconstructing the reflectances for the reference *CIE XYZ* tristimulus + values using PCA and, comparing the reconstructed reflectances against + the reference reflectances. Returns ------- - bool - Whether the node is a leaf. - """ + :class:`numpy.floating` + The reconstruction error for the data. - return len(self._children) == 0 - - def split(self, children, partition_axis): - """ - Converts the leaf node into a non-leaf node using given children and - partition axis. + Raises + ------ + ValueError + If the tristimulus values are undefined. - Parameters - ---------- - children : tuple - Tuple of two :class:`colour.recovery.otsu2018.Node` classes - instances. - partition_axis : PartitionAxis - Partition axis. + Notes + ----- + - The reconstruction error is cached upon being computed and thus is + only computed once per node. """ - self._colour_data = None - self._children = children - self._partition_axis = partition_axis + if self._reconstruction_error is not None: + return self._reconstruction_error - self._mean = None - self._basis_functions = None + if self._XYZ is not None: + self.PCA() - self._M = None - self._M_inverse = None - self._XYZ_mu = None + error = 0 + for i in range(len(self)): + sd = self._reflectances[i, :] + XYZ = self._XYZ[i, :] + recovered_sd = self.reconstruct(XYZ) + error += np.sum((sd - recovered_sd.values) ** 2) - self._best_partition = None - self._cached_leaf_reconstruction_error = None + self._reconstruction_error = error - # - # PCA and Reconstruction - # + return error + else: + raise ValueError('The "tristimulus values" are undefined!') - def PCA(self): - """ - Performs the *Principal Component Analysis* (PCA) on the colours data - of the node and sets the relevant private attributes accordingly. - Raises - ------ - RuntimeError - If the node is not a leaf node. - """ +class Node_Otsu2018(Node): + """ + Represent a node in a :meth:`colour.recovery.Tree_Otsu2018` class instance + node tree. - if not self.is_leaf(): - raise RuntimeError('{0} is not a leaf node!'.format(self)) + Parameters + ---------- + parent + Parent of the node. + children + Children of the node. + data + The colour data belonging to this node. - if self._M is not None: - return + Attributes + ---------- + - :attr:`~colour.recovery.otsu2018.Node.partition_axis` + - :attr:`~colour.recovery.otsu2018.Node.row` - self._mean = np.mean(self._colour_data.reflectances, axis=0) - self._XYZ_mu = self._tree.msds_to_XYZ(self._mean) + Methods + ------- + - :meth:`~colour.recovery.otsu2018.Node.__init__` + - :meth:`~colour.recovery.otsu2018.Node.split` + - :meth:`~colour.recovery.otsu2018.Node.minimise` + - :meth:`~colour.recovery.otsu2018.Node.leaf_reconstruction_error` + - :meth:`~colour.recovery.otsu2018.Node.branch_reconstruction_error` + """ - matrix_data = self._colour_data.reflectances - self._mean - matrix_covariance = np.dot(np.transpose(matrix_data), matrix_data) - _eigenvalues, eigenvectors = np.linalg.eigh(matrix_covariance) - self._basis_functions = np.transpose(eigenvectors[:, -3:]) + def __init__( + self, + parent: Optional[Node_Otsu2018] = None, + children: Optional[List] = None, + data: Optional[Data_Otsu2018] = None, + ): + super().__init__(parent=parent, children=children, data=data) - self._M = np.transpose(self._tree.msds_to_XYZ(self._basis_functions)) - self._M_inverse = np.linalg.inv(self._M) + self._partition_axis: Optional[PartitionAxis] = None + self._best_partition: Optional[ + Tuple[List[Node_Otsu2018], PartitionAxis, Floating] + ] = None - def reconstruct(self, XYZ): + @property + def partition_axis(self) -> Optional[PartitionAxis]: """ - Reconstructs the reflectance for the given *CIE XYZ* tristimulus - values. - - If the node is a leaf, the colour data from the node is used, otherwise - the branch is traversed recursively to find the leaves. - - Parameters - ---------- - XYZ : ndarray, (3,) - *CIE XYZ* tristimulus values to recover the spectral distribution - from. + Getter property for the node partition axis. Returns ------- - SpectralDistribution - Recovered spectral distribution. + :class:`colour.recovery.otsu2018.PartitionAxis` + Node partition axis. """ - xy = XYZ_to_xy(XYZ) - - if not self.is_leaf(): - if (xy[self._partition_axis.direction] <= - self._partition_axis.origin): - return self._children[0].reconstruct(XYZ) - else: - return self._children[1].reconstruct(XYZ) - - weights = np.dot(self._M_inverse, XYZ - self._XYZ_mu) - reflectance = np.dot(weights, self._basis_functions) + self._mean - reflectance = np.clip(reflectance, 0, 1) - - return SpectralDistribution(reflectance, self._tree.cmfs.wavelengths) - - # - # Optimisation - # + return self._partition_axis - def leaf_reconstruction_error(self): + @property + def row(self) -> Tuple[Floating, Integer, Node_Otsu2018, Node_Otsu2018]: """ - Reconstructs the reflectance of the *CIE XYZ* tristimulus values in - the colour data of this node using PCA and compares the reconstructed - spectrum against the measured spectrum. The reconstruction errors are - then summed up and returned. + Getter property for the node row for the selector array. Returns ------- - error : float - The reconstruction errors summation for the node. - - Notes - ----- - The reconstruction error is cached upon being computed and thus is only - computed once per node. + :class:`tuple` + Node row for the selector array. Raises ------ - RuntimeError - If the node is not a leaf node. - """ - - if not self.is_leaf(): - raise RuntimeError('{0} is not a leaf node!'.format(self)) - - if self._cached_leaf_reconstruction_error: - return self._cached_leaf_reconstruction_error - - if self._M is None: - self.PCA() - - error = 0 - for i in range(len(self.colour_data)): - sd = self.colour_data.reflectances[i, :] - XYZ = self.colour_data.XYZ[i, :] - recovered_sd = self.reconstruct(XYZ) - error += np.sum((sd - recovered_sd.values) ** 2) - - self._cached_leaf_reconstruction_error = error - - return error - - def branch_reconstruction_error(self): + ValueError + If the partition axis is undefined. """ - Computes the reconstruction error for an entire branch of the tree, - starting from the node, i.e. the reconstruction errors summation for - all the leaves in the branch. - Returns - ------- - error : float - Reconstruction errors summation for all the leaves in the branch. - """ - - if self.is_leaf(): - return self.leaf_reconstruction_error() + if self._partition_axis is not None: + return ( + self._partition_axis.origin, + self._partition_axis.direction, + cast(Node_Otsu2018, self.children[0]), + cast(Node_Otsu2018, self.children[1]), + ) else: - return sum([ - child.branch_reconstruction_error() for child in self._children - ]) + raise ValueError('The "partition axis" is undefined!') - def partition_reconstruction_error(self, axis): + def split(self, children: List[Node_Otsu2018], axis: PartitionAxis): """ - Computes the reconstruction errors summation of the two nodes created - by splitting the node with a given partition. + Convert the leaf node into an inner node using given children and + partition axis. Parameters ---------- - axis : PartitionAxis - Partition axis used to compute the reconstruction error. - - Returns - ------- - error : float - Reconstruction errors summation of the two nodes created - by splitting the node with a given partition. - lesser, greater : tuple - Nodes created by splitting the node with the given partition. + children + Tuple of two :class:`colour.recovery.otsu2018.Node` class + instances. + axis + Partition axis. """ - partition = self.colour_data.partition(axis) - - if (len(partition[0]) < self._tree.minimum_cluster_size or - len(partition[1]) < self._tree.minimum_cluster_size): - raise RuntimeError('Partition generated parts smaller ' - 'than the minimum cluster size!') - - lesser = Node(self._tree, partition[0]) - lesser.PCA() + self.data = None + self.children = list(children) - greater = Node(self._tree, partition[1]) - greater.PCA() - - error = (lesser.leaf_reconstruction_error() + - greater.leaf_reconstruction_error()) - - return error, (lesser, greater) + self._best_partition = None + self._partition_axis = axis - def find_best_partition(self): + def minimise( + self, minimum_cluster_size: Integer + ) -> Tuple[List[Node_Otsu2018], PartitionAxis, Floating]: """ - Finds the best partition for the node. + Find the best partition for the node that minimises the leaf + reconstruction error. + + Parameters + ---------- + minimum_cluster_size + Smallest acceptable cluster size. It must be at least 3 or the + *Principal Component Analysis* (PCA) is not be possible. Returns ------- - partition_error : float - Partition error - axis : PartitionAxis - Horizontal or vertical line, partitioning the 2D space in - two half-planes. - partition : tuple - Nodes created by splitting a node with a given partition. + :class:`tuple` + Tuple of tuple of nodes created by splitting a node with a given + partition, partition axis, i.e. the horizontal or vertical line, + partitioning the 2D space in two half-planes and partition error. """ if self._best_partition is not None: @@ -1110,33 +1074,99 @@ def find_best_partition(self): leaf_error = self.leaf_reconstruction_error() best_error = None - with tqdm(total=2 * len(self.colour_data)) as progress: + with tqdm(total=2 * len(self.data)) as progress: for direction in [0, 1]: - for i in range(len(self.colour_data)): + for i in range(len(self.data)): progress.update() - origin = self.colour_data.xy[i, direction] - axis = PartitionAxis(origin, direction) - try: - partition_error, partition = ( - self.partition_reconstruction_error(axis)) - except RuntimeError: + axis = PartitionAxis( + self.data.origin(i, direction), direction + ) + data_lesser, data_greater = self.data.partition(axis) + + if np.any( + np.array( + [ + len(data_lesser), + len(data_greater), + ] + ) + < minimum_cluster_size + ): continue + lesser = Node_Otsu2018(data=data_lesser) + lesser.data.PCA() + + greater = Node_Otsu2018(data=data_greater) + greater.data.PCA() + + partition_error = ( + lesser.leaf_reconstruction_error() + + greater.leaf_reconstruction_error() + ) + + partition = [lesser, greater] + if partition_error >= leaf_error: continue if best_error is None or partition_error < best_error: - self._best_partition = (partition_error, axis, - partition) + self._best_partition = ( + partition, + axis, + partition_error, + ) if self._best_partition is None: - raise RuntimeError('Could not find a best partition!') + raise RuntimeError("Could not find the best partition!") return self._best_partition + def leaf_reconstruction_error(self) -> Floating: + """ + Return the reconstruction error of the node data. The error is + computed by reconstructing the reflectances for the data reference + *CIE XYZ* tristimulus values using PCA and, comparing the reconstructed + reflectances against the data reference reflectances. + + Returns + ------- + :class:`numpy.floating` + The reconstruction errors summation for the node data. + """ + + return self.data.reconstruction_error() + + def branch_reconstruction_error(self) -> Floating: + """ + Compute the reconstruction error for all the leaves data connected to + the node or its children, i.e. the reconstruction errors summation for + all the leaves in the branch. + + Returns + ------- + :class:`numpy.floating` + Reconstruction errors summation for all the leaves' data in the + branch. + """ -class NodeTree_Otsu2018(Node): + if self.is_leaf(): + return self.leaf_reconstruction_error() + else: + return np.sum( + as_float_array( + [ + cast( + Node_Otsu2018, child + ).branch_reconstruction_error() + for child in self.children + ] + ) + ) + + +class Tree_Otsu2018(Node_Otsu2018): """ A sub-class of :class:`colour.recovery.otsu2018.Node` class representing the root node of a tree containing information shared with all the nodes, @@ -1144,31 +1174,32 @@ class NodeTree_Otsu2018(Node): if any is used. Global operations involving the entire tree, such as optimisation and - reconstruction, are implemented in this sub-class. + conversion to dataset, are implemented in this sub-class. Parameters ---------- - reflectances : ndarray, (n, m) - Reflectances of the *n* reference colours to use for optimisation. - cmfs : XYZ_ColourMatchingFunctions, optional - Standard observer colour matching functions. - illuminant : SpectralDistribution, optional - Illuminant spectral distribution. + reflectances + Reference reflectances of the *n* reference colours to use for + optimisation. + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + illuminant + Illuminant spectral distribution, default to + *CIE Standard Illuminant D65*. Attributes ---------- - - :attr:`~colour.recovery.NodeTree_Otsu2018.reflectances` - - :attr:`~colour.recovery.NodeTree_Otsu2018.cmfs` - - :attr:`~colour.recovery.NodeTree_Otsu2018.illuminant` - - :attr:`~colour.recovery.NodeTree_Otsu2018.minimum_cluster_size` + - :attr:`~colour.recovery.Tree_Otsu2018.reflectances` + - :attr:`~colour.recovery.Tree_Otsu2018.cmfs` + - :attr:`~colour.recovery.Tree_Otsu2018.illuminant` Methods ------- - - :meth:`~colour.recovery.otsu2018.NodeTree_Otsu2018.__init__` - - :meth:`~colour.recovery.otsu2018.NodeTree_Otsu2018.__str__` - - :meth:`~colour.recovery.otsu2018.NodeTree_Otsu2018.msds_to_XYZ` - - :meth:`~colour.recovery.otsu2018.NodeTree_Otsu2018.optimise` - - :meth:`~colour.recovery.otsu2018.NodeTree_Otsu2018.to_dataset` + - :meth:`~colour.recovery.otsu2018.Tree_Otsu2018.__init__` + - :meth:`~colour.recovery.otsu2018.Tree_Otsu2018.__str__` + - :meth:`~colour.recovery.otsu2018.Tree_Otsu2018.optimise` + - :meth:`~colour.recovery.otsu2018.Tree_Otsu2018.to_dataset` References ---------- @@ -1178,19 +1209,19 @@ class NodeTree_Otsu2018(Node): -------- >>> import os >>> import colour - >>> from colour.characterisation import SDS_COLOURCHECKERS + >>> from colour import MSDS_CMFS, SDS_COLOURCHECKERS, SDS_ILLUMINANTS + >>> from colour.colorimetry import sds_and_msds_to_msds >>> from colour.utilities import numpy_print_options >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> cmfs = ( - ... MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']. + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SpectralShape(360, 780, 10)) ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) - >>> reflectances = [ - ... sd.copy().align(cmfs.shape).values - ... for sd in SDS_COLOURCHECKERS['ColorChecker N Ohta'].values() - ... ] - >>> node_tree = NodeTree_Otsu2018(reflectances, cmfs, illuminant) + >>> reflectances = sds_and_msds_to_msds( + ... SDS_COLOURCHECKERS['ColorChecker N Ohta'].values() + ... ) + >>> node_tree = Tree_Otsu2018(reflectances, cmfs, illuminant) >>> node_tree.optimise(iterations=2, print_callable=lambda x: x) >>> dataset = node_tree.to_dataset() >>> path = os.path.join(colour.__path__[0], 'recovery', 'tests', @@ -1199,10 +1230,8 @@ class NodeTree_Otsu2018(Node): >>> dataset = Dataset_Otsu2018() # doctest: +SKIP >>> dataset.read(path) # doctest: +SKIP >>> sd = XYZ_to_sd_Otsu2018(XYZ, cmfs, illuminant, dataset) - ... # doctest: +SKIP >>> with numpy_print_options(suppress=True): - ... # Doctests skip for Python 2.x compatibility. - ... sd # doctest: +SKIP + ... sd # doctest: +ELLIPSIS SpectralDistribution([[ 360. , 0.0651341...], [ 370. , 0.0651341...], [ 380. , 0.0651341...], @@ -1252,215 +1281,111 @@ class NodeTree_Otsu2018(Node): extrapolator_kwargs={...}) """ - def __init__(self, - reflectances, - cmfs=MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'].copy().align( - SPECTRAL_SHAPE_OTSU2018), - illuminant=SDS_ILLUMINANTS['D65'].copy().align( - SPECTRAL_SHAPE_OTSU2018)): - self._reflectances = as_float_array(reflectances) - - self._cmfs = cmfs - - shape = cmfs.shape + def __init__( + self, + reflectances: MultiSpectralDistributions, + cmfs: Optional[MultiSpectralDistributions] = None, + illuminant: Optional[SpectralDistribution] = None, + ): + super().__init__() - if illuminant.shape != shape: - runtime_warning( - 'Aligning "{0}" illuminant shape to "{1}" colour matching ' - 'functions shape.'.format(illuminant.name, cmfs.name)) - illuminant = illuminant.copy().align(cmfs.shape) + cmfs, illuminant = handle_spectral_arguments( + cmfs, illuminant, shape_default=SPECTRAL_SHAPE_OTSU2018 + ) - self._illuminant = illuminant + self._cmfs: MultiSpectralDistributions = cmfs + self._illuminant: SpectralDistribution = illuminant - self._dw = shape.interval + self._reflectances: NDArray = np.transpose( + reshape_msds(reflectances, self._cmfs.shape).values + ) - # Normalising constant :math:`k`, see :func:`colour.msds_to_XYZ` - # definition. - self._k = 1 / (np.sum( - self._cmfs.values[:, 1] * self._illuminant.values) * self._dw) - - self._minimum_cluster_size = None - - super(NodeTree_Otsu2018, self).__init__( - self, ColourData(self, self._reflectances)) + self.data: Data_Otsu2018 = Data_Otsu2018( + self._reflectances, self._cmfs, self._illuminant + ) @property - def reflectances(self): + def reflectances(self) -> NDArray: """ - Getter property for the reflectances. + Getter property for the reference reflectances. Returns ------- - ndarray - Reflectances. + :class:`numpy.ndarray` + Reference reflectances. """ return self._reflectances @property - def cmfs(self): + def cmfs(self) -> MultiSpectralDistributions: """ Getter property for the standard observer colour matching functions. Returns ------- - XYZ_ColourMatchingFunctions + :class:`colour.MultiSpectralDistributions` Standard observer colour matching functions. """ return self._cmfs @property - def illuminant(self): + def illuminant(self) -> SpectralDistribution: """ Getter property for the illuminant. Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Illuminant. """ return self._illuminant - @property - def minimum_cluster_size(self): - """ - Getter property for the minimum cluster size. - - Returns - ------- - int - Minimum cluster size. - """ - - return self._minimum_cluster_size - - def __str__(self): - """ - Returns a formatted string representation of the tree. - - Returns - ------- - unicode - Formatted string representation. - """ - - child_count = len(self) - - return '{0}({1} {2})'.format(self.__class__.__name__, child_count, - 'Node' if child_count == 1 else 'Nodes') - - def _create_selector_array(self): - """ - Creates an array that describes how to select the appropriate cluster - for given *CIE xy* coordinates. - - See :meth:`colour.recovery.Dataset_Otsu2018.select` method for - information about what the array structure and its usage. - """ - - rows = [] - leaf_number = [0] - symbol_table = {} - - def add_rows(node): - """ - Add rows for given node and its children. - """ - - if node.is_leaf(): - symbol_table[node] = leaf_number[0] - leaf_number[0] += 1 - return - - symbol_table[node] = -len(rows) - rows.append([ - node.partition_axis.direction, node.partition_axis.origin, - node.children[0], node.children[1] - ]) - - for child in node.children: - add_rows(child) - - add_rows(self) - - # Special case for tree with just a root node. - if len(rows) == 0: - return zeros(4) - - for i, (_direction, _origin, symbol_1, symbol_2) in enumerate(rows): - rows[i][2] = symbol_table[symbol_1] - rows[i][3] = symbol_table[symbol_2] - - return as_float_array(rows) - - def msds_to_XYZ(self, reflectances): + def optimise( + self, + iterations: Integer = 8, + minimum_cluster_size: Optional[Integer] = None, + print_callable: Callable = print, + ): """ - Computes the XYZ tristimulus values of a given reflectance. Faster for - humans, by using cmfs and the illuminant stored in the ''tree'', - thus avoiding unnecessary repetition. Faster for computers, by using - a very simple and direct method. + Optimise the tree by repeatedly performing optimal partitioning of the + nodes, creating a tree that minimises the total reconstruction error. Parameters ---------- - reflectances : ndarray - Reflectance with shape matching the one used to construct this - ``tree``. - - Returns - ------- - ndarray (3,) - XYZ tristimulus values, normalised to 1. - """ - - E = self._illuminant.values * reflectances - - return self._k * np.dot(E, self._cmfs.values) * self._dw - - def optimise(self, - iterations=8, - minimum_cluster_size=None, - print_callable=print): - """ - Optimises the tree by repeatedly performing optimal partitioning of the - nodes, creating a tree that minimizes the total reconstruction error. - - Parameters - ---------- - iterations : int, optional + iterations Maximum number of splits. If the dataset is too small, this number might not be reached. The default is to create 8 clusters, like in :cite:`Otsu2018`. - minimum_cluster_size : int, optional - Smallest acceptable cluster size. By default it is chosen + minimum_cluster_size + Smallest acceptable cluster size. By default, it is chosen automatically, based on the size of the dataset and desired number of clusters. It must be at least 3 or the - *Principal Component Analysis* (PCA) will not be possible. - print_callable : callable, optional + *Principal Component Analysis* (PCA) is not be possible. + print_callable Callable used to print progress and diagnostic information. Examples -------- - >>> import os - >>> import colour - >>> from colour.characterisation import SDS_COLOURCHECKERS - >>> cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - ... 'CIE 1931 2 Degree Standard Observer'].copy().align( - ... SpectralShape(360, 780, 10)) + >>> from colour.colorimetry import sds_and_msds_to_msds + >>> from colour import MSDS_CMFS, SDS_COLOURCHECKERS, SDS_ILLUMINANTS + >>> cmfs = ( + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] + ... .copy().align(SpectralShape(360, 780, 10)) + ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) - >>> reflectances = [ - ... sd.copy().align(cmfs.shape).values - ... for sd in SDS_COLOURCHECKERS['ColorChecker N Ohta'].values() - ... ] - >>> node_tree = NodeTree_Otsu2018(reflectances, cmfs, illuminant) + >>> reflectances = sds_and_msds_to_msds( + ... SDS_COLOURCHECKERS['ColorChecker N Ohta'].values() + ... ) + >>> node_tree = Tree_Otsu2018(reflectances, cmfs, illuminant) >>> node_tree.optimise(iterations=2) # doctest: +ELLIPSIS ======================================================================\ ========= * \ * - * "Otsu et al. (2018)" Node Tree Optimisation \ + * "Otsu et al. (2018)" Tree Optimisation \ * * \ * @@ -1470,100 +1395,116 @@ def optimise(self, Iteration 1 of 2: - Optimising "NodeTree_Otsu2018(1 Node)"... + Optimising "Tree_Otsu2018#...(Data_Otsu2018(24 Reflectances))"... - Split "NodeTree_Otsu2018(1 Node)" into \ -"Node#...(ColourData(10 Reflectances))" and \ -"Node#...(ColourData(14 Reflectances))" along "\ -PartitionAxis(horizontal partition at y = 0.3240945...)". - Error is reduced by 0.0054840... and is now 4.8650513..., \ -99.9% of the initial error. + Splitting "Tree_Otsu2018#...(Data_Otsu2018(24 Reflectances))" into \ +"Node_Otsu2018#...(Data_Otsu2018(10 Reflectances))" and \ +"Node_Otsu2018#...(Data_Otsu2018(14 Reflectances))" along \ +"PartitionAxis(horizontal partition at y = 0.3240945...)". + Error is reduced by 0.0054840... and is now 4.8650513..., 99.9% of \ +the initial error. Iteration 2 of 2: - Optimising "Node#...(ColourData(10 Reflectances))"... - Optimisation failed: Could not find a best partition! - Optimising "Node#...(ColourData(14 Reflectances))"... + Optimising "Node_Otsu2018#...(Data_Otsu2018(10 Reflectances))"... + Optimisation failed: Could not find the best partition! + Optimising "Node_Otsu2018#...(Data_Otsu2018(14 Reflectances))"... - Split "Node#...(ColourData(14 Reflectances))" into \ -"Node#...(ColourData(7 Reflectances))" and \ -"Node#...(ColourData(7 Reflectances))" along \ + Splitting "Node_Otsu2018#...(Data_Otsu2018(14 Reflectances))" into \ +"Node_Otsu2018#...(Data_Otsu2018(7 Reflectances))" and \ +"Node_Otsu2018#...(Data_Otsu2018(7 Reflectances))" along \ "PartitionAxis(horizontal partition at y = 0.3600663...)". - Error is reduced by 0.9681059... and is now 3.8969453..., \ -80.0% of the initial error. - Node tree optimisation is complete! + Error is reduced by 0.9681059... and is now 3.8969453..., 80.0% of \ +the initial error. + Tree optimisation is complete! + >>> print(node_tree.render()) # doctest: +ELLIPSIS + |----"Tree_Otsu2018#..." + |----"Node_Otsu2018#..." + |----"Node_Otsu2018#..." + |----"Node_Otsu2018#..." + |----"Node_Otsu2018#..." + >>> len(node_tree) - 3 + 4 """ - self._minimum_cluster_size = (minimum_cluster_size - if minimum_cluster_size is not None else - len(self.colour_data) / iterations // 2) - self._minimum_cluster_size = max(self._minimum_cluster_size, 3) + default_cluster_size = len(self.data) / iterations // 2 + minimum_cluster_size = max( + cast(int, optional(minimum_cluster_size, default_cluster_size)), 3 + ) initial_branch_error = self.branch_reconstruction_error() message_box( - '"Otsu et al. (2018)" Node Tree Optimisation', - print_callable=print_callable) + '"Otsu et al. (2018)" Tree Optimisation', + print_callable=print_callable, + ) - print_callable( - 'Initial branch error is: {0}'.format(initial_branch_error)) + print_callable(f"Initial branch error is: {initial_branch_error}") best_leaf, best_partition, best_axis, partition_error = [None] * 4 for i in range(iterations): - print_callable('\nIteration {0} of {1}:\n'.format( - i + 1, iterations)) + print_callable(f"\nIteration {i + 1} of {iterations}:\n") total_error = self.branch_reconstruction_error() optimised_total_error = None for leaf in self.leaves: - print_callable('Optimising "{0}"...'.format(leaf)) + print_callable(f'Optimising "{leaf}"...') try: - partition_error, axis, partition = ( - leaf.find_best_partition()) + partition, axis, partition_error = leaf.minimise( + minimum_cluster_size + ) except RuntimeError as error: - print_callable('Optimisation failed: {0}'.format(error)) + print_callable(f"Optimisation failed: {error}") continue new_total_error = ( - total_error - leaf.leaf_reconstruction_error() + - partition_error) - - if (optimised_total_error is None or - new_total_error < optimised_total_error): + total_error + - leaf.leaf_reconstruction_error() + + partition_error + ) + + if ( + optimised_total_error is None + or new_total_error < optimised_total_error + ): optimised_total_error = new_total_error best_axis = axis best_leaf = leaf best_partition = partition if optimised_total_error is None: - print_callable('\nNo further improvements are possible!\n' - 'Terminating at iteration {0}.\n'.format(i)) + print_callable( + f"\nNo further improvement is possible!" + f"\nTerminating at iteration {i}.\n" + ) break - print_callable( - '\nSplit "{0}" into "{1}" and "{2}" along "{3}".'.format( - best_leaf, best_partition[0], best_partition[1], - best_axis)) + if best_partition is not None: + print_callable( + f'\nSplitting "{best_leaf}" into "{best_partition[0]}" ' + f'and "{best_partition[1]}" along "{best_axis}".' + ) print_callable( - 'Error is reduced by {0} and is now {1}, ' - '{2:.1f}% of the initial error.'.format( - leaf.leaf_reconstruction_error() - partition_error, - optimised_total_error, - 100 * optimised_total_error / initial_branch_error)) + f"Error is reduced by " + f"{leaf.leaf_reconstruction_error() - partition_error} and " + f"is now {optimised_total_error}, " + f"{100 * optimised_total_error / initial_branch_error:.1f}% " + f"of the initial error." + ) - best_leaf.split(best_partition, best_axis) + if best_leaf is not None: + best_leaf.split(best_partition, best_axis) - print_callable('Node tree optimisation is complete!') + print_callable("Tree optimisation is complete!") - def to_dataset(self): + def to_dataset(self) -> Dataset_Otsu2018: """ - Creates a :class:`colour.recovery.Dataset_Otsu2018` class instance + Create a :class:`colour.recovery.Dataset_Otsu2018` class instance based on data stored in the tree. The dataset can then be saved to disk or used to recover reflectance @@ -1571,27 +1512,67 @@ def to_dataset(self): Returns ------- - Dataset_Otsu2018 + :class:`colour.recovery.Dataset_Otsu2018` The dataset object. Examples -------- - >>> import os - >>> import colour + >>> from colour.colorimetry import sds_and_msds_to_msds >>> from colour.characterisation import SDS_COLOURCHECKERS - >>> reflectances = [ - ... sd.copy().align(SPECTRAL_SHAPE_OTSU2018).values - ... for sd in SDS_COLOURCHECKERS['ColorChecker N Ohta'].values() - ... ] - >>> node_tree = NodeTree_Otsu2018(reflectances) + >>> reflectances = sds_and_msds_to_msds( + ... SDS_COLOURCHECKERS['ColorChecker N Ohta'].values() + ... ) + >>> node_tree = Tree_Otsu2018(reflectances) >>> node_tree.optimise(iterations=2, print_callable=lambda x: x) >>> node_tree.to_dataset() # doctest: +ELLIPSIS """ - basis_functions = [leaf.basis_functions for leaf in self.leaves] - means = [leaf.mean for leaf in self.leaves] - selector_array = self._create_selector_array() + basis_functions = as_float_array( + [leaf.data.basis_functions for leaf in self.leaves] + ) + + means = as_float_array([leaf.data.mean for leaf in self.leaves]) + + if len(self.children) == 0: + selector_array = zeros(4) + else: + + def add_rows(node, data=None): + """Add rows for given node and its children.""" + + data = cast( + Dict, + optional( + data, {"rows": [], "node_to_leaf_id": {}, "leaf_id": 0} + ), + ) + + if node.is_leaf(): + data["node_to_leaf_id"][node] = data["leaf_id"] + data["leaf_id"] += 1 + return + + data["node_to_leaf_id"][node] = -len(data["rows"]) + data["rows"].append(list(node.row)) + + for child in node.children: + add_rows(child, data) + + return data + + data = add_rows(self) + rows = data["rows"] + + for i, row in enumerate(rows): + for j in (2, 3): + rows[i][j] = data["node_to_leaf_id"][row[j]] + + selector_array = as_float_array(rows) - return Dataset_Otsu2018(self._cmfs.shape, basis_functions, means, - selector_array) + return Dataset_Otsu2018( + self._cmfs.shape, + basis_functions, + means, + selector_array, + ) diff --git a/colour/recovery/smits1999.py b/colour/recovery/smits1999.py index 177dab18d7..d447b740b4 100644 --- a/colour/recovery/smits1999.py +++ b/colour/recovery/smits1999.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Smits (1999) - Reflectance Recovery =================================== -Defines objects for reflectance recovery using *Smits (1999)* method. +Defines the objects for reflectance recovery using *Smits (1999)* method. References ---------- @@ -12,68 +11,65 @@ doi:10.1080/10867651.1999.10487511 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.colorimetry import CCS_ILLUMINANTS -from colour.models import (XYZ_to_RGB, normalised_primary_matrix, - RGB_COLOURSPACE_sRGB) +from colour.colorimetry import CCS_ILLUMINANTS, SpectralDistribution +from colour.hints import ArrayLike, NDArray +from colour.models import ( + XYZ_to_RGB, + normalised_primary_matrix, + RGB_COLOURSPACE_sRGB, +) from colour.recovery import SDS_SMITS1999 from colour.utilities import to_domain_1 -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'PRIMARIES_SMITS1999', 'CCS_WHITEPOINT_SMITS1999', - 'MATRIX_XYZ_TO_RGB_SMITS1999', 'XYZ_to_RGB_Smits1999', - 'RGB_to_sd_Smits1999' + "PRIMARIES_SMITS1999", + "CCS_WHITEPOINT_SMITS1999", + "MATRIX_XYZ_TO_RGB_SMITS1999", + "XYZ_to_RGB_Smits1999", + "RGB_to_sd_Smits1999", ] -PRIMARIES_SMITS1999 = RGB_COLOURSPACE_sRGB.primaries -""" -Current *Smits (1999)* method implementation colourspace primaries. +PRIMARIES_SMITS1999: NDArray = RGB_COLOURSPACE_sRGB.primaries +"""Current *Smits (1999)* method implementation colourspace primaries.""" -PRIMARIES_SMITS1999 : ndarray, (3, 2) -""" +CCS_WHITEPOINT_SMITS1999: NDArray = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" +]["E"] +"""Current *Smits (1999)* method implementation colourspace whitepoint.""" -CCS_WHITEPOINT_SMITS1999 = ( - CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['E']) -""" -Current *Smits (1999)* method implementation colourspace whitepoint. - -CCS_WHITEPOINT_SMITS1999 : ndarray -""" - -MATRIX_XYZ_TO_RGB_SMITS1999 = np.linalg.inv( - normalised_primary_matrix(PRIMARIES_SMITS1999, CCS_WHITEPOINT_SMITS1999)) +MATRIX_XYZ_TO_RGB_SMITS1999: NDArray = np.linalg.inv( + normalised_primary_matrix(PRIMARIES_SMITS1999, CCS_WHITEPOINT_SMITS1999) +) """ Current *Smits (1999)* method implementation *RGB* colourspace to *CIE XYZ* tristimulus values matrix. - -MATRIX_XYZ_TO_RGB_SMITS1999 : array_like, (3, 3) """ -def XYZ_to_RGB_Smits1999(XYZ): +def XYZ_to_RGB_Smits1999(XYZ: ArrayLike) -> NDArray: """ - Convenient object to convert from *CIE XYZ* tristimulus values to *RGB* - colourspace in conditions required by the current *Smits (1999)* method - implementation. + Convert from *CIE XYZ* tristimulus values to *RGB* colourspace with + conditions required by the current *Smits (1999)* method implementation. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. Returns ------- - ndarray + :class:`numpy.ndarray` *RGB* colour array. Examples @@ -91,24 +87,23 @@ def XYZ_to_RGB_Smits1999(XYZ): ) -def RGB_to_sd_Smits1999(RGB): +def RGB_to_sd_Smits1999(RGB: ArrayLike) -> SpectralDistribution: """ - Recovers the spectral distribution of given *RGB* colourspace array using + Recover the spectral distribution of given *RGB* colourspace array using *Smits (1999)* method. Parameters ---------- - RGB : array_like, (3,) + RGB *RGB* colourspace array to recover the spectral distribution from. Returns ------- - SpectralDistribution + :class:`colour.SpectralDistribution` Recovered spectral distribution. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -121,15 +116,13 @@ def RGB_to_sd_Smits1999(RGB): Examples -------- - >>> from colour.colorimetry import ( - ... MSDS_CMFS_STANDARD_OBSERVER, SDS_ILLUMINANTS, - ... SpectralShape, sd_to_XYZ_integration - ... ) + >>> from colour import MSDS_CMFS, SDS_ILLUMINANTS, SpectralShape + >>> from colour.colorimetry import sd_to_XYZ_integration >>> from colour.utilities import numpy_print_options >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> RGB = XYZ_to_RGB_Smits1999(XYZ) >>> cmfs = ( - ... MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']. + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SpectralShape(360, 780, 10)) ... ) >>> illuminant = SDS_ILLUMINANTS['E'].copy().align(cmfs.shape) @@ -154,17 +147,17 @@ def RGB_to_sd_Smits1999(RGB): array([ 0.1894770..., 0.1126470..., 0.0474420...]) """ - white_sd = SDS_SMITS1999['white'].copy() - cyan_sd = SDS_SMITS1999['cyan'].copy() - magenta_sd = SDS_SMITS1999['magenta'].copy() - yellow_sd = SDS_SMITS1999['yellow'].copy() - red_sd = SDS_SMITS1999['red'].copy() - green_sd = SDS_SMITS1999['green'].copy() - blue_sd = SDS_SMITS1999['blue'].copy() + white_sd = SDS_SMITS1999["white"].copy() + cyan_sd = SDS_SMITS1999["cyan"].copy() + magenta_sd = SDS_SMITS1999["magenta"].copy() + yellow_sd = SDS_SMITS1999["yellow"].copy() + red_sd = SDS_SMITS1999["red"].copy() + green_sd = SDS_SMITS1999["green"].copy() + blue_sd = SDS_SMITS1999["blue"].copy() R, G, B = to_domain_1(RGB) sd = white_sd.copy() * 0 - sd.name = 'Smits (1999) - {0}'.format(RGB) + sd.name = f"Smits (1999) - {RGB!r}" if R <= G and R <= B: sd += white_sd * R diff --git a/colour/recovery/tests/__init__.py b/colour/recovery/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/recovery/tests/__init__.py +++ b/colour/recovery/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/recovery/tests/test__init__.py b/colour/recovery/tests/test__init__.py index 236c6420aa..bcc847ef58 100644 --- a/colour/recovery/tests/test__init__.py +++ b/colour/recovery/tests/test__init__.py @@ -1,64 +1,75 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.recovery` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.recovery` module.""" import numpy as np import unittest -from six.moves import zip -from colour.colorimetry import (MSDS_CMFS_STANDARD_OBSERVER, SDS_ILLUMINANTS, - SpectralShape, sd_to_XYZ_integration) +from colour.colorimetry import ( + MSDS_CMFS, + SDS_ILLUMINANTS, + SpectralShape, + reshape_msds, + reshape_sd, + sd_to_XYZ_integration, +) from colour.recovery import XYZ_to_sd from colour.utilities import domain_range_scale -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestXYZ_to_sd'] +__all__ = [ + "TestXYZ_to_sd", +] class TestXYZ_to_sd(unittest.TestCase): """ - Defines :func:`colour.recovery.XYZ_to_sd` definition unit tests + Define :func:`colour.recovery.XYZ_to_sd` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" - self._cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'].copy().align( - SpectralShape(360, 780, 10)) + # pylint: disable=E1102 + self._cmfs = reshape_msds( + MSDS_CMFS["CIE 1931 2 Degree Standard Observer"], + SpectralShape(360, 780, 10), + ) - self._sd_D65 = SDS_ILLUMINANTS['D65'].copy().align(self._cmfs.shape) + self._sd_D65 = reshape_sd(SDS_ILLUMINANTS["D65"], self._cmfs.shape) def test_domain_range_scale_XYZ_to_sd(self): """ - Tests :func:`colour.recovery.XYZ_to_sd` definition domain + Test :func:`colour.recovery.XYZ_to_sd` definition domain and range scale support. """ XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) - m = ('Jakob 2019', 'Mallett 2019', 'Meng 2015', 'Otsu 2018', - 'Smits 1999') + m = ( + "Jakob 2019", + "Mallett 2019", + "Meng 2015", + "Otsu 2018", + "Smits 1999", + ) v = [ sd_to_XYZ_integration( XYZ_to_sd( - XYZ, method, cmfs=self._cmfs, illuminant=self._sd_D65), - self._cmfs, self._sd_D65) for method in m + XYZ, method, cmfs=self._cmfs, illuminant=self._sd_D65 + ), + self._cmfs, + self._sd_D65, + ) + for method in m ] - d_r = (('reference', 1, 1), (1, 1, 0.01), (100, 100, 1)) + d_r = (("reference", 1, 1), ("1", 1, 0.01), ("100", 100, 1)) for method, value in zip(m, v): for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): @@ -68,11 +79,15 @@ def test_domain_range_scale_XYZ_to_sd(self): XYZ * factor_a, method, cmfs=self._cmfs, - illuminant=self._sd_D65), self._cmfs, - self._sd_D65), + illuminant=self._sd_D65, + ), + self._cmfs, + self._sd_D65, + ), value * factor_b, - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/recovery/tests/test_jakob2019.py b/colour/recovery/tests/test_jakob2019.py index 4b373b8eac..d0316973c3 100644 --- a/colour/recovery/tests/test_jakob2019.py +++ b/colour/recovery/tests/test_jakob2019.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.recovery.jakob2019` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.recovery.jakob2019` module.""" import numpy as np import os @@ -12,53 +7,59 @@ import unittest from colour.characterisation import SDS_COLOURCHECKERS -from colour.colorimetry import (CCS_ILLUMINANTS, SDS_ILLUMINANTS, - MSDS_CMFS_STANDARD_OBSERVER, sd_to_XYZ) +from colour.colorimetry import handle_spectral_arguments, sd_to_XYZ from colour.difference import JND_CIE1976, delta_E_CIE1976 -from colour.models import RGB_COLOURSPACE_sRGB, RGB_to_XYZ, XYZ_to_Lab +from colour.models import ( + RGB_COLOURSPACE_sRGB, + RGB_to_XYZ, + XYZ_to_Lab, + XYZ_to_xy, +) from colour.recovery.jakob2019 import ( - XYZ_to_sd_Jakob2019, sd_Jakob2019, error_function, - dimensionalise_coefficients, SPECTRAL_SHAPE_JAKOB2019, LUT3D_Jakob2019) + XYZ_to_sd_Jakob2019, + sd_Jakob2019, + error_function, + dimensionalise_coefficients, + SPECTRAL_SHAPE_JAKOB2019, + LUT3D_Jakob2019, +) from colour.utilities import domain_range_scale, full, ones, zeros -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestErrorFunction', 'TestXYZ_to_sd_Jakob2019', 'TestLUT3D_Jakob2019' + "TestErrorFunction", + "TestXYZ_to_sd_Jakob2019", + "TestLUT3D_Jakob2019", ] class TestErrorFunction(unittest.TestCase): """ - Defines :func:`colour.recovery.jakob2019.error_function` definition unit + Define :func:`colour.recovery.jakob2019.error_function` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._shape = SPECTRAL_SHAPE_JAKOB2019 - self._cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'].copy().align(self._shape) - - self._sd_D65 = SDS_ILLUMINANTS['D65'].copy().align(self._shape) + self._cmfs, self._sd_D65 = handle_spectral_arguments( + shape_default=self._shape + ) self._XYZ_D65 = sd_to_XYZ(self._sd_D65) - self._XYZ_D65 /= self._XYZ_D65[1] - self._xy_D65 = CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'][ - 'D65'] + self._xy_D65 = XYZ_to_xy(self._XYZ_D65) self._Lab_e = np.array([72, -20, 61]) def test_intermediates(self): """ - Tests intermediate results of + Test intermediate results of :func:`colour.recovery.jakob2019.error_function` with :func:`colour.sd_to_XYZ`, :func:`colour.XYZ_to_Lab` and checks if the error is computed correctly by comparing it with @@ -68,9 +69,9 @@ def test_intermediates(self): # Quoted names refer to colours from ColorChecker N Ohta (using D65). coefficient_list = [ np.array([0, 0, 0]), # 50% gray - np.array([0, 0, -1e+9]), # Pure black - np.array([0, 0, +1e+9]), # Pure white - np.array([1e+9, -1e+9, 2.1e+8]), # A pathological example + np.array([0, 0, -1e9]), # Pure black + np.array([0, 0, +1e9]), # Pure white + np.array([1e9, -1e9, 2.1e8]), # A pathological example np.array([2.2667394, -7.6313081, 1.03185]), # 'blue' np.array([-31.377077, 26.810094, -6.1139927]), # 'green' np.array([25.064246, -16.072039, 0.10431365]), # 'red' @@ -85,11 +86,13 @@ def test_intermediates(self): self._Lab_e, self._cmfs, self._sd_D65, - additional_data=True) + additional_data=True, + ) sd = sd_Jakob2019( dimensionalise_coefficients(coefficients, self._shape), - self._shape) + self._shape, + ) sd_XYZ = sd_to_XYZ(sd, self._cmfs, self._sd_D65) / 100 sd_Lab = XYZ_to_Lab(XYZ, self._xy_D65) @@ -103,7 +106,7 @@ def test_intermediates(self): def test_derivatives(self): """ - Tests the gradients computed using closed-form expressions of the + Test the gradients computed using closed-form expressions of the derivatives with finite difference approximations. """ @@ -119,8 +122,9 @@ def test_derivatives(self): coefficients = ones(3) coefficients[coefficient_i] = sample - error, derror = error_function(coefficients, self._Lab_e, - self._cmfs, self._sd_D65) + error, derror = error_function( + coefficients, self._Lab_e, self._cmfs, self._sd_D65 + ) errors[i] = error derrors[i] = derror[coefficient_i] @@ -131,153 +135,212 @@ def test_derivatives(self): # The approximated derivatives aren't too accurate, so tolerances # have to be rather loose. np.testing.assert_allclose( - staggered_derrors, approximate_derrors, atol=1e-3, rtol=1e-2) + staggered_derrors, approximate_derrors, atol=1e-3, rtol=1e-2 + ) class TestXYZ_to_sd_Jakob2019(unittest.TestCase): """ - Defines :func:`colour.recovery.jakob2019.XYZ_to_sd_Jakob2019` definition + Define :func:`colour.recovery.jakob2019.XYZ_to_sd_Jakob2019` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._shape = SPECTRAL_SHAPE_JAKOB2019 - self._cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'].copy().align(self._shape) - self._sd_D65 = SDS_ILLUMINANTS['D65'].copy().align(self._shape) + self._cmfs, self._sd_D65 = handle_spectral_arguments( + shape_default=self._shape + ) def test_XYZ_to_sd_Jakob2019(self): - """ - Tests :func:`colour.recovery.jakob2019.XYZ_to_sd_Jakob2019` definition. - """ + """Test :func:`colour.recovery.jakob2019.XYZ_to_sd_Jakob2019` definition.""" # Tests the round-trip with values of a colour checker. - for name, sd in SDS_COLOURCHECKERS['ColorChecker N Ohta'].items(): + for name, sd in SDS_COLOURCHECKERS["ColorChecker N Ohta"].items(): XYZ = sd_to_XYZ(sd, self._cmfs, self._sd_D65) / 100 _recovered_sd, error = XYZ_to_sd_Jakob2019( - XYZ, self._cmfs, self._sd_D65, additional_data=True) + XYZ, self._cmfs, self._sd_D65, additional_data=True + ) - if error > JND_CIE1976 / 100: - self.fail('Delta E for \'{0}\' is {1}!'.format(name, error)) + if error > JND_CIE1976 / 100: # pragma: no cover + self.fail(f"Delta E for '{name}' is {error}!") def test_domain_range_scale_XYZ_to_sd_Jakob2019(self): """ - Tests :func:`colour.recovery.jakob2019.XYZ_to_sd_Jakob2019` definition + Test :func:`colour.recovery.jakob2019.XYZ_to_sd_Jakob2019` definition domain and range scale support. """ XYZ_i = np.array([0.20654008, 0.12197225, 0.05136952]) XYZ_o = sd_to_XYZ( - XYZ_to_sd_Jakob2019(XYZ_i, self._cmfs, self._sd_D65), self._cmfs, - self._sd_D65) + XYZ_to_sd_Jakob2019(XYZ_i, self._cmfs, self._sd_D65), + self._cmfs, + self._sd_D65, + ) - d_r = (('reference', 1, 1), (1, 1, 0.01), (100, 100, 1)) + d_r = (("reference", 1, 1), ("1", 1, 0.01), ("100", 100, 1)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( sd_to_XYZ( - XYZ_to_sd_Jakob2019(XYZ_i * factor_a, self._cmfs, - self._sd_D65), self._cmfs, - self._sd_D65), + XYZ_to_sd_Jakob2019( + XYZ_i * factor_a, self._cmfs, self._sd_D65 + ), + self._cmfs, + self._sd_D65, + ), XYZ_o * factor_b, - decimal=7) + decimal=7, + ) class TestLUT3D_Jakob2019(unittest.TestCase): """ - Defines :class:`colour.recovery.jakob2019.LUT3D_Jakob2019` + Define :class:`colour.recovery.jakob2019.LUT3D_Jakob2019` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ - - self._RGB_colourspace = RGB_COLOURSPACE_sRGB + """Initialise the common tests attributes.""" self._shape = SPECTRAL_SHAPE_JAKOB2019 - self._cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'].copy().align(self._shape) + self._cmfs, self._sd_D65 = handle_spectral_arguments( + shape_default=self._shape + ) + self._XYZ_D65 = sd_to_XYZ(self._sd_D65) + self._xy_D65 = XYZ_to_xy(self._XYZ_D65) - self._sd_D65 = SDS_ILLUMINANTS['D65'].copy().align(self._shape) - self._xy_D65 = CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'][ - 'D65'] + self._RGB_colourspace = RGB_COLOURSPACE_sRGB self._temporary_directory = tempfile.mkdtemp() + self._LUT = LUT3D_Jakob2019() + self._LUT.generate(self._RGB_colourspace, self._cmfs, self._sd_D65, 5) + + self._path = os.path.join( + self._temporary_directory, "Test_Jakob2019.coeff" + ) + self._LUT.write(self._path) + def tearDown(self): - """ - After tests actions. - """ + """After tests actions.""" shutil.rmtree(self._temporary_directory) def test_required_attributes(self): - """ - Tests presence of required attributes. - """ + """Test the presence of required attributes.""" - required_attributes = ('lightness_scale', 'coefficients', 'size', - 'interpolator') + required_attributes = ( + "size", + "lightness_scale", + "coefficients", + "interpolator", + ) for attribute in required_attributes: self.assertIn(attribute, dir(LUT3D_Jakob2019)) def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__init__', 'generate', 'RGB_to_coefficients', - 'RGB_to_sd', 'read', 'write') + required_methods = ( + "__init__", + "generate", + "RGB_to_coefficients", + "RGB_to_sd", + "read", + "write", + ) for method in required_methods: self.assertIn(method, dir(LUT3D_Jakob2019)) + def test_size(self): + """Test :attr:`colour.recovery.jakob2019.LUT3D_Jakob2019.size` property.""" + + self.assertEqual(self._LUT.size, 5) + + def test_lightness_scale(self): + """ + Test :attr:`colour.recovery.jakob2019.LUT3D_Jakob2019.lightness_scale` + property. + """ + + np.testing.assert_almost_equal( + self._LUT.lightness_scale, + np.array( + [0.00000000, 0.06561279, 0.50000000, 0.93438721, 1.00000000] + ), + decimal=7, + ) + + def test_coefficients(self): + """ + Test :attr:`colour.recovery.jakob2019.LUT3D_Jakob2019.coefficients` + property. + """ + + self.assertTupleEqual(self._LUT.coefficients.shape, (3, 5, 5, 5, 3)) + def test_LUT3D_Jakob2019(self): """ - Tests the entirety of the + Test the entirety of the :class:`colour.recovery.jakob2019.LUT3D_Jakob2019`class. """ LUT = LUT3D_Jakob2019() - LUT.generate(self._RGB_colourspace, self._cmfs, self._sd_D65, 5) - - path = os.path.join(self._temporary_directory, 'Test_Jakob2019.coeff') - - LUT.write(path) - LUT.read(path) + LUT.read(self._path) for RGB in [ - np.array([1, 0, 0]), - np.array([0, 1, 0]), - np.array([0, 0, 1]), - zeros(3), - full(3, 0.5), - ones(3), + np.array([1, 0, 0]), + np.array([0, 1, 0]), + np.array([0, 0, 1]), + zeros(3), + full(3, 0.5), + ones(3), ]: - XYZ = RGB_to_XYZ(RGB, self._RGB_colourspace.whitepoint, - self._xy_D65, - self._RGB_colourspace.matrix_RGB_to_XYZ) + XYZ = RGB_to_XYZ( + RGB, + self._RGB_colourspace.whitepoint, + self._xy_D65, + self._RGB_colourspace.matrix_RGB_to_XYZ, + ) Lab = XYZ_to_Lab(XYZ, self._xy_D65) recovered_sd = LUT.RGB_to_sd(RGB) - recovered_XYZ = sd_to_XYZ(recovered_sd, self._cmfs, - self._sd_D65) / 100 + recovered_XYZ = ( + sd_to_XYZ(recovered_sd, self._cmfs, self._sd_D65) / 100 + ) recovered_Lab = XYZ_to_Lab(recovered_XYZ, self._xy_D65) error = delta_E_CIE1976(Lab, recovered_Lab) - if error > 2 * JND_CIE1976 / 100: - self.fail('Delta E for RGB={0} in colourspace {1} is {2}!' - .format(RGB, self._RGB_colourspace.name, error)) + if error > 2 * JND_CIE1976 / 100: # pragma: no cover + self.fail( + f"Delta E for RGB={RGB} in colourspace " + f"{self._RGB_colourspace.name} is {error}!" + ) + + def test_raise_exception_RGB_to_coefficients(self): + """ + Test :func:`colour.recovery.jakob2019.LUT3D_Jakob2019.\ +RGB_to_coefficients` method raised exception. + """ + + LUT = LUT3D_Jakob2019() + + self.assertRaises(RuntimeError, LUT.RGB_to_coefficients, np.array([])) + + def test_raise_exception_read(self): + """ + Test :func:`colour.recovery.jakob2019.LUT3D_Jakob2019.read` method + raised exception. + """ + + self.assertRaises(ValueError, self._LUT.read, __file__) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/recovery/tests/test_mallett2019.py b/colour/recovery/tests/test_mallett2019.py index 9b830d1bfb..931bc42f31 100644 --- a/colour/recovery/tests/test_mallett2019.py +++ b/colour/recovery/tests/test_mallett2019.py @@ -1,56 +1,64 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.recovery.mallett2019` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.recovery.mallett2019` module.""" import unittest import numpy as np from colour.characterisation import SDS_COLOURCHECKERS -from colour.colorimetry import (SpectralShape, MSDS_CMFS_STANDARD_OBSERVER, - SDS_ILLUMINANTS, CCS_ILLUMINANTS, sd_to_XYZ) +from colour.colorimetry import ( + MSDS_CMFS, + CCS_ILLUMINANTS, + SDS_ILLUMINANTS, + SpectralShape, + reshape_msds, + reshape_sd, + sd_to_XYZ, +) from colour.difference import JND_CIE1976, delta_E_CIE1976 -from colour.models import (RGB_COLOURSPACE_PAL_SECAM, RGB_COLOURSPACE_sRGB, - XYZ_to_RGB, XYZ_to_Lab) -from colour.recovery import (MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019, - spectral_primary_decomposition_Mallett2019, - RGB_to_sd_Mallett2019) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.models import ( + RGB_COLOURSPACE_PAL_SECAM, + RGB_COLOURSPACE_sRGB, + XYZ_to_RGB, + XYZ_to_Lab, +) +from colour.recovery import ( + MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019, + spectral_primary_decomposition_Mallett2019, + RGB_to_sd_Mallett2019, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'TestMixinMallett2019', 'TestSpectralPrimaryDecompositionMallett2019', - 'TestRGB_to_sd_Mallett2019' + "TestMixinMallett2019", + "TestSpectralPrimaryDecompositionMallett2019", + "TestRGB_to_sd_Mallett2019", ] -class TestMixinMallett2019(object): - """ - A mixin for testing the :mod:`colour.recovery.mallett2019` module. - """ +class TestMixinMallett2019: + """A mixin for testing the :mod:`colour.recovery.mallett2019` module.""" def __init__(self): - """ - Initialises common tests attributes for the mixin. - """ - - self._cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'].copy().align( - SpectralShape(360, 780, 10)) - self._sd_D65 = SDS_ILLUMINANTS['D65'].copy().align(self._cmfs.shape) - self._xy_D65 = CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'][ - 'D65'] + """Initialise common tests attributes for the mixin.""" + + # pylint: disable=E1102 + self._cmfs = reshape_msds( + MSDS_CMFS["CIE 1931 2 Degree Standard Observer"], + SpectralShape(360, 780, 10), + ) + self._sd_D65 = reshape_sd(SDS_ILLUMINANTS["D65"], self._cmfs.shape) + self._xy_D65 = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][ + "D65" + ] def check_basis_functions(self): """ - Tests :func:`colour.recovery.RGB_to_sd_Mallett2019` definition or the + Test :func:`colour.recovery.RGB_to_sd_Mallett2019` definition or the more specialised :func:`colour.recovery.RGB_to_sd_Mallett2019` definition. """ @@ -63,42 +71,45 @@ def check_basis_functions(self): # Check if the primaries or their combination exceeds the [0, 1] range. lower = np.zeros_like(sd.values) - 1e-12 - upper = np.ones_like(sd.values) + 1e+12 + upper = np.ones_like(sd.values) + 1e12 for RGB in [[1, 1, 1], [1, 0, 0], [0, 1, 0], [0, 0, 1]]: sd = RGB_to_sd_Mallett2019(RGB, self._basis) np.testing.assert_array_less(sd.values, upper) np.testing.assert_array_less(lower, sd.values) # Check Delta E's using a colour checker. - for name, sd in SDS_COLOURCHECKERS['ColorChecker N Ohta'].items(): + for name, sd in SDS_COLOURCHECKERS["ColorChecker N Ohta"].items(): XYZ = sd_to_XYZ(sd, self._cmfs, self._sd_D65) / 100 Lab = XYZ_to_Lab(XYZ, self._xy_D65) - RGB = XYZ_to_RGB(XYZ, self._RGB_colourspace.whitepoint, - self._xy_D65, - self._RGB_colourspace.matrix_XYZ_to_RGB) + RGB = XYZ_to_RGB( + XYZ, + self._RGB_colourspace.whitepoint, + self._xy_D65, + self._RGB_colourspace.matrix_XYZ_to_RGB, + ) recovered_sd = RGB_to_sd_Mallett2019(RGB, self._basis) - recovered_XYZ = sd_to_XYZ(recovered_sd, self._cmfs, - self._sd_D65) / 100 + recovered_XYZ = ( + sd_to_XYZ(recovered_sd, self._cmfs, self._sd_D65) / 100 + ) recovered_Lab = XYZ_to_Lab(recovered_XYZ, self._xy_D65) error = delta_E_CIE1976(Lab, recovered_Lab) - if error > 4 * JND_CIE1976 / 100: - self.fail('Delta E for "{0}" is {1}!'.format(name, error)) + if error > 4 * JND_CIE1976 / 100: # pragma: no cover + self.fail(f'Delta E for "{name}" is {error}!') -class TestSpectralPrimaryDecompositionMallett2019(unittest.TestCase, - TestMixinMallett2019): +class TestSpectralPrimaryDecompositionMallett2019( + unittest.TestCase, TestMixinMallett2019 +): """ - Defines :func:`colour.recovery.spectral_primary_decomposition_Mallett2019` + Define :func:`colour.recovery.spectral_primary_decomposition_Mallett2019` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" TestMixinMallett2019.__init__(self) @@ -106,26 +117,25 @@ def setUp(self): def test_spectral_primary_decomposition_Mallett2019(self): """ - Tests :func:`colour.recovery.\ + Test :func:`colour.recovery.\ test_spectral_primary_decomposition_Mallett2019` definition. """ self._basis = spectral_primary_decomposition_Mallett2019( - self._RGB_colourspace, self._cmfs, self._sd_D65) + self._RGB_colourspace, self._cmfs, self._sd_D65 + ) self.check_basis_functions() class TestRGB_to_sd_Mallett2019(unittest.TestCase, TestMixinMallett2019): """ - Defines :func:`colour.recovery.RGB_to_sd_Mallett2019` definition unit + Define :func:`colour.recovery.RGB_to_sd_Mallett2019` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" TestMixinMallett2019.__init__(self) @@ -133,12 +143,10 @@ def setUp(self): self._basis = MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019 def test_RGB_to_sd_Mallett2019(self): - """ - Tests :func:`colour.recovery.RGB_to_sd_Mallett2019` definition. - """ + """Test :func:`colour.recovery.RGB_to_sd_Mallett2019` definition.""" self.check_basis_functions() -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/recovery/tests/test_meng2015.py b/colour/recovery/tests/test_meng2015.py index c67f574a70..7984ad2fbd 100644 --- a/colour/recovery/tests/test_meng2015.py +++ b/colour/recovery/tests/test_meng2015.py @@ -1,64 +1,73 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.recovery.meng2015` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.recovery.meng2015` module.""" import numpy as np import unittest -from colour.colorimetry import (MSDS_CMFS_STANDARD_OBSERVER, SpectralShape, - SDS_ILLUMINANTS, sd_to_XYZ_integration) +from colour.colorimetry import ( + MSDS_CMFS, + SDS_ILLUMINANTS, + SpectralShape, + reshape_msds, + reshape_sd, + sd_to_XYZ_integration, +) from colour.recovery import XYZ_to_sd_Meng2015 from colour.utilities import domain_range_scale -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestXYZ_to_sd_Meng2015'] +__all__ = [ + "TestXYZ_to_sd_Meng2015", +] class TestXYZ_to_sd_Meng2015(unittest.TestCase): """ - Defines :func:`colour.recovery.meng2015.XYZ_to_sd_Meng2015` definition unit + Define :func:`colour.recovery.meng2015.XYZ_to_sd_Meng2015` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" - self._cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'].copy().align( - SpectralShape(360, 780, 10)) - self._sd_D65 = SDS_ILLUMINANTS['D65'].copy().align(self._cmfs.shape) - self._sd_E = SDS_ILLUMINANTS['E'].copy().align(self._cmfs.shape) + # pylint: disable=E1102 + self._cmfs = reshape_msds( + MSDS_CMFS["CIE 1931 2 Degree Standard Observer"], + SpectralShape(360, 780, 10), + ) + self._sd_D65 = reshape_sd(SDS_ILLUMINANTS["D65"], self._cmfs.shape) + self._sd_E = reshape_sd(SDS_ILLUMINANTS["E"], self._cmfs.shape) def test_XYZ_to_sd_Meng2015(self): - """ - Tests :func:`colour.recovery.meng2015.XYZ_to_sd_Meng2015` definition. - """ + """Test :func:`colour.recovery.meng2015.XYZ_to_sd_Meng2015` definition.""" XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) np.testing.assert_almost_equal( sd_to_XYZ_integration( - XYZ_to_sd_Meng2015(XYZ, self._cmfs, self._sd_D65), self._cmfs, - self._sd_D65) / 100, + XYZ_to_sd_Meng2015(XYZ, self._cmfs, self._sd_D65), + self._cmfs, + self._sd_D65, + ) + / 100, XYZ, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( sd_to_XYZ_integration( - XYZ_to_sd_Meng2015(XYZ, self._cmfs, self._sd_E), self._cmfs, - self._sd_E) / 100, + XYZ_to_sd_Meng2015(XYZ, self._cmfs, self._sd_E), + self._cmfs, + self._sd_E, + ) + / 100, XYZ, - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( sd_to_XYZ_integration( @@ -66,24 +75,35 @@ def test_XYZ_to_sd_Meng2015(self): XYZ, self._cmfs, self._sd_D65, - optimisation_kwargs={'options': { - 'ftol': 1e-10, - }}), self._cmfs, self._sd_D65) / 100, + optimisation_kwargs={ + "options": { + "ftol": 1e-10, + } + }, + ), + self._cmfs, + self._sd_D65, + ) + / 100, XYZ, - decimal=7) + decimal=7, + ) shape = SpectralShape(400, 700, 5) - cmfs = self._cmfs.copy().align(shape) + # pylint: disable=E1102 + cmfs = reshape_msds(self._cmfs, shape) np.testing.assert_almost_equal( sd_to_XYZ_integration( - XYZ_to_sd_Meng2015(XYZ, cmfs, self._sd_D65), cmfs, - self._sd_D65) / 100, + XYZ_to_sd_Meng2015(XYZ, cmfs, self._sd_D65), cmfs, self._sd_D65 + ) + / 100, XYZ, - decimal=7) + decimal=7, + ) def test_raise_exception_XYZ_to_sd_Meng2015(self): """ - Tests :func:`colour.recovery.meng2015.XYZ_to_sd_Meng2015` + Test :func:`colour.recovery.meng2015.XYZ_to_sd_Meng2015` definition raised exception. """ @@ -92,33 +112,38 @@ def test_raise_exception_XYZ_to_sd_Meng2015(self): XYZ_to_sd_Meng2015, np.array([0.0, 0.0, 1.0]), optimisation_kwargs={ - 'options': { - 'maxiter': 10 - }, - }) + "options": {"maxiter": 10}, + }, + ) def test_domain_range_scale_XYZ_to_sd_Meng2015(self): """ - Tests :func:`colour.recovery.meng2015.XYZ_to_sd_Meng2015` definition + Test :func:`colour.recovery.meng2015.XYZ_to_sd_Meng2015` definition domain and range scale support. """ XYZ_i = np.array([0.20654008, 0.12197225, 0.05136952]) XYZ_o = sd_to_XYZ_integration( - XYZ_to_sd_Meng2015(XYZ_i, self._cmfs, self._sd_D65), self._cmfs, - self._sd_D65) + XYZ_to_sd_Meng2015(XYZ_i, self._cmfs, self._sd_D65), + self._cmfs, + self._sd_D65, + ) - d_r = (('reference', 1, 1), (1, 1, 0.01), (100, 100, 1)) + d_r = (("reference", 1, 1), ("1", 1, 0.01), ("100", 100, 1)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( sd_to_XYZ_integration( - XYZ_to_sd_Meng2015(XYZ_i * factor_a, self._cmfs, - self._sd_D65), self._cmfs, - self._sd_D65), + XYZ_to_sd_Meng2015( + XYZ_i * factor_a, self._cmfs, self._sd_D65 + ), + self._cmfs, + self._sd_D65, + ), XYZ_o * factor_b, - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/recovery/tests/test_otsu2018.py b/colour/recovery/tests/test_otsu2018.py index 093deca6d9..74a3e9bf39 100644 --- a/colour/recovery/tests/test_otsu2018.py +++ b/colour/recovery/tests/test_otsu2018.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.recovery.jakob2019` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.recovery.jakob2019` module.""" import numpy as np import os @@ -12,272 +7,970 @@ import unittest from colour.characterisation import SDS_COLOURCHECKERS -from colour.colorimetry import (CCS_ILLUMINANTS, SDS_ILLUMINANTS, - MSDS_CMFS_STANDARD_OBSERVER, sd_to_XYZ) +from colour.colorimetry import ( + handle_spectral_arguments, + reshape_msds, + reshape_sd, + sd_to_XYZ, + sds_and_msds_to_msds, +) from colour.difference import delta_E_CIE1976 -from colour.models import XYZ_to_Lab -from colour.recovery import (XYZ_to_sd_Otsu2018, SPECTRAL_SHAPE_OTSU2018, - Dataset_Otsu2018, NodeTree_Otsu2018) -from colour.recovery.otsu2018 import ColourData, Node +from colour.models import XYZ_to_Lab, XYZ_to_xy +from colour.recovery import ( + XYZ_to_sd_Otsu2018, + SPECTRAL_SHAPE_OTSU2018, + Dataset_Otsu2018, + Tree_Otsu2018, +) +from colour.recovery.otsu2018 import ( + DATASET_REFERENCE_OTSU2018, + Data_Otsu2018, + Node_Otsu2018, + PartitionAxis, +) from colour.utilities import domain_range_scale, metric_mse -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestDataset_Otsu2018', 'TestXYZ_to_sd_Otsu2018', 'TestColourData', - 'TestNode', 'TestNodeTree_Otsu2018' + "TestDataset_Otsu2018", + "TestXYZ_to_sd_Otsu2018", + "TestData_Otsu2018", + "TestNode_Otsu2018", + "TestTree_Otsu2018", ] class TestDataset_Otsu2018(unittest.TestCase): """ - Defines :class:`colour.recovery.otsu2018.Dataset_Otsu2018` definition unit + Define :class:`colour.recovery.otsu2018.Dataset_Otsu2018` definition unit tests methods. """ + def setUp(self): + """Initialise the common tests attributes.""" + + self._dataset = DATASET_REFERENCE_OTSU2018 + self._xy = np.array([0.54369557, 0.32107944]) + + self._temporary_directory = tempfile.mkdtemp() + + self._path = os.path.join( + self._temporary_directory, "Test_Otsu2018.npz" + ) + self._dataset.write(self._path) + + def tearDown(self): + """After tests actions.""" + + shutil.rmtree(self._temporary_directory) + def test_required_attributes(self): - """ - Tests presence of required attributes. - """ + """Test the presence of required attributes.""" - required_attributes = ('shape', 'basis_functions', 'means', - 'selector_array') + required_attributes = ( + "shape", + "basis_functions", + "means", + "selector_array", + ) for attribute in required_attributes: self.assertIn(attribute, dir(Dataset_Otsu2018)) def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__init__', 'select', 'cluster', 'read', 'write') + required_methods = ( + "__init__", + "__str__", + "select", + "cluster", + "read", + "write", + ) for method in required_methods: self.assertIn(method, dir(Dataset_Otsu2018)) + def test_shape(self): + """Test :attr:`colour.recovery.otsu2018.Dataset_Otsu2018.shape` property.""" + + self.assertEqual(self._dataset.shape, SPECTRAL_SHAPE_OTSU2018) + + def test_basis_functions(self): + """ + Test :attr:`colour.recovery.otsu2018.Dataset_Otsu2018.basis_functions` + property. + """ + + self.assertTupleEqual(self._dataset.basis_functions.shape, (8, 3, 36)) + + def test_means(self): + """ + Test :attr:`colour.recovery.otsu2018.Dataset_Otsu2018.means` + property. + """ + + self.assertTupleEqual(self._dataset.means.shape, (8, 36)) + + def test_selector_array(self): + """ + Test :attr:`colour.recovery.otsu2018.Dataset_Otsu2018.selector_array` + property. + """ + + self.assertTupleEqual(self._dataset.selector_array.shape, (7, 4)) + + def test__str__(self): + """Test :meth:`colour.recovery.otsu2018.Dataset_Otsu2018.__str__` method.""" + + self.assertEqual( + str(self._dataset), "Dataset_Otsu2018(8 basis functions)" + ) + + self.assertEqual(str(Dataset_Otsu2018()), "Dataset_Otsu2018()") + + def test_select(self): + """Test :meth:`colour.recovery.otsu2018.Dataset_Otsu2018.select` method.""" + + self.assertEqual(self._dataset.select(self._xy), 6) + + def test_raise_exception_select(self): + """ + Test :meth:`colour.recovery.otsu2018.Dataset_Otsu2018.select` method + raised exception. + """ + + self.assertRaises( + ValueError, Dataset_Otsu2018().select, np.array([0, 0]) + ) + + def test_cluster(self): + """Test :meth:`colour.recovery.otsu2018.Dataset_Otsu2018.cluster` method.""" + + basis_functions, means = self._dataset.cluster(self._xy) + self.assertTupleEqual(basis_functions.shape, (3, 36)) + self.assertTupleEqual(means.shape, (36,)) + + def test_raise_exception_cluster(self): + """ + Test :meth:`colour.recovery.otsu2018.Dataset_Otsu2018.cluster` method + raised exception. + """ + + self.assertRaises( + ValueError, Dataset_Otsu2018().cluster, np.array([0, 0]) + ) + + def test_read(self): + """Test :meth:`colour.recovery.otsu2018.Dataset_Otsu2018.read` method.""" + + dataset = Dataset_Otsu2018() + dataset.read(self._path) + + self.assertEqual(dataset.shape, SPECTRAL_SHAPE_OTSU2018) + self.assertTupleEqual(dataset.basis_functions.shape, (8, 3, 36)) + self.assertTupleEqual(dataset.means.shape, (8, 36)) + self.assertTupleEqual(dataset.selector_array.shape, (7, 4)) + + def test_write(self): + """Test :meth:`colour.recovery.otsu2018.Dataset_Otsu2018.write` method.""" + + self._dataset.write(self._path) + + dataset = Dataset_Otsu2018() + dataset.read(self._path) + + self.assertEqual(dataset.shape, SPECTRAL_SHAPE_OTSU2018) + self.assertTupleEqual(dataset.basis_functions.shape, (8, 3, 36)) + self.assertTupleEqual(dataset.means.shape, (8, 36)) + self.assertTupleEqual(dataset.selector_array.shape, (7, 4)) + + def test_raise_exception_write(self): + """ + Test :meth:`colour.recovery.otsu2018.Dataset_Otsu2018.write` method + raised exception. + """ + + self.assertRaises(ValueError, Dataset_Otsu2018().write, "") + class TestXYZ_to_sd_Otsu2018(unittest.TestCase): """ - Defines :func:`colour.recovery.otsu2018.XYZ_to_sd_Otsu2018` definition unit + Define :func:`colour.recovery.otsu2018.XYZ_to_sd_Otsu2018` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._shape = SPECTRAL_SHAPE_OTSU2018 - self._cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'].copy().align(self._shape) - - self._sd_D65 = SDS_ILLUMINANTS['D65'].copy().align(self._shape) - self._xy_D65 = CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'][ - 'D65'] + self._cmfs, self._sd_D65 = handle_spectral_arguments( + shape_default=self._shape + ) + self._XYZ_D65 = sd_to_XYZ(self._sd_D65) + self._xy_D65 = XYZ_to_xy(self._XYZ_D65) def test_XYZ_to_sd_Otsu2018(self): - """ - Tests :func:`colour.recovery.otsu2018.XYZ_to_sd_Otsu2018` definition. - """ + """Test :func:`colour.recovery.otsu2018.XYZ_to_sd_Otsu2018` definition.""" # Tests the round-trip with values of a colour checker. - for _name, sd in SDS_COLOURCHECKERS['ColorChecker N Ohta'].items(): + for _name, sd in SDS_COLOURCHECKERS["ColorChecker N Ohta"].items(): XYZ = sd_to_XYZ(sd, self._cmfs, self._sd_D65) / 100 Lab = XYZ_to_Lab(XYZ, self._xy_D65) recovered_sd = XYZ_to_sd_Otsu2018( - XYZ, self._cmfs, self._sd_D65, clip=False) - recovered_XYZ = sd_to_XYZ(recovered_sd, self._cmfs, - self._sd_D65) / 100 + XYZ, self._cmfs, self._sd_D65, clip=False + ) + recovered_XYZ = ( + sd_to_XYZ(recovered_sd, self._cmfs, self._sd_D65) / 100 + ) recovered_Lab = XYZ_to_Lab(recovered_XYZ, self._xy_D65) - error = metric_mse(sd.copy().align(SPECTRAL_SHAPE_OTSU2018).values, - recovered_sd.values) + error = metric_mse( + reshape_sd(sd, SPECTRAL_SHAPE_OTSU2018).values, + recovered_sd.values, + ) self.assertLess(error, 0.02) delta_E = delta_E_CIE1976(Lab, recovered_Lab) self.assertLess(delta_E, 1e-12) + def test_raise_exception_XYZ_to_sd_Otsu2018(self): + """ + Test :func:`colour.recovery.otsu2018.XYZ_to_sd_Otsu2018` definition + raised_exception. + """ + + self.assertRaises( + ValueError, + XYZ_to_sd_Otsu2018, + np.array([0, 0, 0]), + self._cmfs, + self._sd_D65, + Dataset_Otsu2018(), + ) + def test_domain_range_scale_XYZ_to_sd_Otsu2018(self): """ - Tests :func:`colour.recovery.otsu2018.XYZ_to_sd_Otsu2018` definition + Test :func:`colour.recovery.otsu2018.XYZ_to_sd_Otsu2018` definition domain and range scale support. """ XYZ_i = np.array([0.20654008, 0.12197225, 0.05136952]) XYZ_o = sd_to_XYZ( - XYZ_to_sd_Otsu2018(XYZ_i, self._cmfs, self._sd_D65), self._cmfs, - self._sd_D65) + XYZ_to_sd_Otsu2018(XYZ_i, self._cmfs, self._sd_D65), + self._cmfs, + self._sd_D65, + ) - d_r = (('reference', 1, 1), (1, 1, 0.01), (100, 100, 1)) + d_r = (("reference", 1, 1), ("1", 1, 0.01), ("100", 100, 1)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( sd_to_XYZ( - XYZ_to_sd_Otsu2018(XYZ_i * factor_a, self._cmfs, - self._sd_D65), self._cmfs, - self._sd_D65), + XYZ_to_sd_Otsu2018( + XYZ_i * factor_a, self._cmfs, self._sd_D65 + ), + self._cmfs, + self._sd_D65, + ), XYZ_o * factor_b, - decimal=7) + decimal=7, + ) -class TestColourData(unittest.TestCase): +class TestData_Otsu2018(unittest.TestCase): """ - Defines :class:`colour.recovery.otsu2018.ColourData` definition unit tests - methods. + Define :class:`colour.recovery.otsu2018.Data_Otsu2018` definition unit + tests methods. """ + def setUp(self): + """Initialise the common tests attributes.""" + + self._shape = SPECTRAL_SHAPE_OTSU2018 + self._cmfs, self._sd_D65 = handle_spectral_arguments( + shape_default=self._shape + ) + + self._reflectances = np.transpose( + reshape_msds( + sds_and_msds_to_msds( + SDS_COLOURCHECKERS["ColorChecker N Ohta"].values() + ), + self._shape, + ).values + ) + + self._data = Data_Otsu2018( + self._reflectances, self._cmfs, self._sd_D65 + ) + def test_required_attributes(self): - """ - Tests presence of required attributes. - """ + """Test the presence of required attributes.""" - required_attributes = ('tree', 'reflectances', 'XYZ', 'xy') + required_attributes = ( + "reflectances", + "cmfs", + "illuminant", + "basis_functions", + "mean", + ) for attribute in required_attributes: - self.assertIn(attribute, dir(ColourData)) + self.assertIn(attribute, dir(Data_Otsu2018)) def test_required_methods(self): + """Test the presence of required methods.""" + + required_methods = ( + "__init__", + "__str__", + "__len__", + "origin", + "partition", + "PCA", + "reconstruct", + "reconstruction_error", + ) + + for method in required_methods: + self.assertIn(method, dir(Data_Otsu2018)) + + def test_reflectances(self): """ - Tests presence of required methods. + Test :attr:`colour.recovery.otsu2018.Data_Otsu2018.reflectances` + property. """ - required_methods = ('__init__', '__str__', '__len__', 'partition') + self.assertIs(self._data.reflectances, self._reflectances) - for method in required_methods: - self.assertIn(method, dir(ColourData)) + def test_cmfs(self): + """Test :attr:`colour.recovery.otsu2018.Data_Otsu2018.cmfs` property.""" + + self.assertIs(self._data.cmfs, self._cmfs) + + def test_illuminant(self): + """ + Test :attr:`colour.recovery.otsu2018.Data_Otsu2018.illuminant` + property. + """ + + self.assertIs(self._data.illuminant, self._sd_D65) + + def test_basis_functions(self): + """ + Test :attr:`colour.recovery.otsu2018.Data_Otsu2018.basis_functions` + property. + """ + + data = Data_Otsu2018(self._reflectances, self._cmfs, self._sd_D65) + + self.assertIsNone(data.basis_functions) + + data.PCA() + + self.assertTupleEqual(data.basis_functions.shape, (3, 36)) + + def test_mean(self): + """Test :attr:`colour.recovery.otsu2018.Data_Otsu2018.mean` property.""" + data = Data_Otsu2018(self._reflectances, self._cmfs, self._sd_D65) -class TestNode(unittest.TestCase): + self.assertIsNone(data.mean) + + data.PCA() + + self.assertTupleEqual(data.mean.shape, (36,)) + + def test__str__(self): + """Test :meth:`colour.recovery.otsu2018.Data_Otsu2018.__str__` method.""" + + self.assertEqual(str(self._data), "Data_Otsu2018(24 Reflectances)") + + def test__len__(self): + """Test :meth:`colour.recovery.otsu2018.Data_Otsu2018.__len__` method.""" + + self.assertEqual(len(self._data), 24) + + def test_origin(self): + """Test :meth:`colour.recovery.otsu2018.Data_Otsu2018.origin` method.""" + + self.assertAlmostEqual( + self._data.origin(4, 1), 0.255284008578559, places=7 + ) + + def test_raise_exception_origin(self): + """ + Test :meth:`colour.recovery.otsu2018.Data_Otsu2018.origin` method + raised exception. + """ + + self.assertRaises( + ValueError, + Data_Otsu2018(None, self._cmfs, self._sd_D65).origin, + 4, + 1, + ) + + def test_partition(self): + """Test :meth:`colour.recovery.otsu2018.Data_Otsu2018.partition` method.""" + + partition = self._data.partition(PartitionAxis(4, 1)) + + self.assertEqual(len(partition), 2) + + def test_raise_exception_partition(self): + """ + Test :meth:`colour.recovery.otsu2018.Data_Otsu2018.partition` method + raised exception. + """ + + self.assertRaises( + ValueError, + Data_Otsu2018(None, self._cmfs, self._sd_D65).partition, + PartitionAxis(4, 1), + ) + + def test_PCA(self): + """Test :meth:`colour.recovery.otsu2018.Data_Otsu2018.PCA` method.""" + + data = Data_Otsu2018(self._reflectances, self._cmfs, self._sd_D65) + + data.PCA() + + np.testing.assert_almost_equal( + data.basis_functions, + np.array( + [ + [ + 0.04391241, + 0.08560996, + 0.15556120, + 0.20826672, + 0.22981218, + 0.23117641, + 0.22718022, + 0.21742869, + 0.19854261, + 0.16868383, + 0.12020268, + 0.05958463, + -0.01015508, + -0.08775193, + -0.16957532, + -0.23186776, + -0.26516404, + -0.27409402, + -0.27856619, + -0.27685075, + -0.25597708, + -0.21331000, + -0.15372029, + -0.08746878, + -0.02744494, + 0.01725581, + 0.04756055, + 0.07184639, + 0.09090063, + 0.10317253, + 0.10830387, + 0.10872694, + 0.10645999, + 0.10766424, + 0.11170078, + 0.11620896, + ], + [ + 0.03137588, + 0.06204234, + 0.11364884, + 0.17579436, + 0.20914074, + 0.22152351, + 0.23120105, + 0.24039823, + 0.24730359, + 0.25195045, + 0.25237533, + 0.24672212, + 0.23538236, + 0.22094141, + 0.20389065, + 0.18356599, + 0.15952882, + 0.13567812, + 0.11401807, + 0.09178015, + 0.06539517, + 0.03173809, + -0.00658524, + -0.04710763, + -0.08379987, + -0.11074555, + -0.12606191, + -0.13630094, + -0.13988107, + -0.14193361, + -0.14671866, + -0.15164795, + -0.15772737, + -0.16328073, + -0.16588768, + -0.16947164, + ], + [ + -0.01360289, + -0.02375832, + -0.04262545, + -0.07345243, + -0.09081235, + -0.09227928, + -0.08922710, + -0.08626299, + -0.08584571, + -0.08843734, + -0.09475094, + -0.10376740, + -0.11331399, + -0.12109706, + -0.12678070, + -0.13401030, + -0.14417036, + -0.15408359, + -0.16265529, + -0.17079814, + -0.17972656, + -0.19005983, + -0.20053986, + -0.21017531, + -0.21808806, + -0.22347400, + -0.22650876, + -0.22895376, + -0.22982598, + -0.23001787, + -0.23036398, + -0.22917409, + -0.22684271, + -0.22387883, + -0.22065773, + -0.21821049, + ], + ] + ), + decimal=7, + ) + + np.testing.assert_almost_equal( + data.mean, + np.array( + [ + 0.08795833, + 0.12050000, + 0.16787500, + 0.20675000, + 0.22329167, + 0.22837500, + 0.23229167, + 0.23579167, + 0.23658333, + 0.23779167, + 0.23866667, + 0.23975000, + 0.24345833, + 0.25054167, + 0.25791667, + 0.26150000, + 0.26437500, + 0.26566667, + 0.26475000, + 0.26554167, + 0.27137500, + 0.28279167, + 0.29529167, + 0.31070833, + 0.32575000, + 0.33829167, + 0.34675000, + 0.35554167, + 0.36295833, + 0.37004167, + 0.37854167, + 0.38675000, + 0.39587500, + 0.40266667, + 0.40683333, + 0.41287500, + ] + ), + decimal=7, + ) + + def test_reconstruct(self): + """ + Test :meth:`colour.recovery.otsu2018.Data_Otsu2018.reconstruct` + method. + """ + + data = Data_Otsu2018(self._reflectances, self._cmfs, self._sd_D65) + + data.PCA() + + np.testing.assert_almost_equal( + data.reconstruct( + np.array( + [ + 0.20654008, + 0.12197225, + 0.05136952, + ] + ) + ).values, + np.array( + [ + 0.06899964, + 0.08241919, + 0.09768650, + 0.08938555, + 0.07872582, + 0.07140930, + 0.06385099, + 0.05471747, + 0.04281364, + 0.03073280, + 0.01761134, + 0.00772535, + 0.00379120, + 0.00405617, + 0.00595014, + 0.01323536, + 0.03229711, + 0.05661531, + 0.07763041, + 0.10271461, + 0.14276781, + 0.20239859, + 0.27288559, + 0.35044541, + 0.42170481, + 0.47567859, + 0.50910276, + 0.53578140, + 0.55251101, + 0.56530032, + 0.58029915, + 0.59367723, + 0.60830542, + 0.62100871, + 0.62881635, + 0.63971254, + ] + ), + decimal=7, + ) + + def test_raise_exception_reconstruct(self): + """ + Test :meth:`colour.recovery.otsu2018.Data_Otsu2018.reconstruct` method + raised exception. + """ + + self.assertRaises( + ValueError, + Data_Otsu2018(None, self._cmfs, self._sd_D65).reconstruct, + np.array([0, 0, 0]), + ) + + def test_reconstruction_error(self): + """ + Test :meth:`colour.recovery.otsu2018.Data_Otsu2018.\ +reconstruction_error` method. + """ + + data = Data_Otsu2018(self._reflectances, self._cmfs, self._sd_D65) + + self.assertAlmostEqual( + data.reconstruction_error(), 2.753352549148681, places=7 + ) + + def test_raise_exception_reconstruction_error(self): + """ + Test :meth:`colour.recovery.otsu2018.Data_Otsu2018.\ +reconstruction_error` method raised exception. + """ + + self.assertRaises( + ValueError, + Data_Otsu2018(None, self._cmfs, self._sd_D65).reconstruction_error, + ) + + +class TestNode_Otsu2018(unittest.TestCase): """ - Defines :class:`colour.recovery.otsu2018.Node` definition unit tests - methods. + Define :class:`colour.recovery.otsu2018.Node_Otsu2018` definition unit + tests methods. """ + def setUp(self): + """Initialise the common tests attributes.""" + + self._shape = SPECTRAL_SHAPE_OTSU2018 + self._cmfs, self._sd_D65 = handle_spectral_arguments( + shape_default=self._shape + ) + + self._reflectances = sds_and_msds_to_msds( + SDS_COLOURCHECKERS["ColorChecker N Ohta"].values() + ) + + self._tree = Tree_Otsu2018(self._reflectances) + self._tree.optimise() + for leaf in self._tree.leaves: + if len(leaf.parent.children) == 2: + self._node_a = leaf.parent + self._node_b, self._node_c = self._node_a.children + break + + self._data_a = Data_Otsu2018( + np.transpose(reshape_msds(self._reflectances, self._shape).values), + self._cmfs, + self._sd_D65, + ) + self._data_b = self._node_b.data + + self._partition_axis = self._node_a.partition_axis + def test_required_attributes(self): - """ - Tests presence of required attributes. - """ + """Test the presence of required attributes.""" - required_attributes = ('id', 'tree', 'colour_data', 'children', - 'partition_axis', 'basis_functions', 'mean', - 'leaves') + required_attributes = ("partition_axis", "row") for attribute in required_attributes: - self.assertIn(attribute, dir(Node)) + self.assertIn(attribute, dir(Node_Otsu2018)) def test_required_methods(self): + """Test the presence of required methods.""" + + required_methods = ( + "__init__", + "split", + "minimise", + "leaf_reconstruction_error", + "branch_reconstruction_error", + ) + + for method in required_methods: + self.assertIn(method, dir(Node_Otsu2018)) + + def test_partition_axis(self): """ - Tests presence of required methods. + Test :attr:`colour.recovery.otsu2018.Node_Otsu2018.partition_axis` + property. """ - required_methods = ('__init__', '__str__', '__len__', 'is_leaf', - 'split', 'PCA', 'reconstruct', - 'leaf_reconstruction_error', - 'branch_reconstruction_error', - 'partition_reconstruction_error', - 'find_best_partition') + self.assertIs(self._node_a.partition_axis, self._partition_axis) - for method in required_methods: - self.assertIn(method, dir(Node)) + def test_row(self): + """Test :attr:`colour.recovery.otsu2018.Node_Otsu2018.row` property.""" + + self.assertTupleEqual( + self._node_a.row, + ( + self._partition_axis.origin, + self._partition_axis.direction, + self._node_b, + self._node_c, + ), + ) + + def test_raise_exception_row(self): + """ + Test :attr:`colour.recovery.otsu2018.Node_Otsu2018.row` property + raised exception. + """ + + self.assertRaises(ValueError, lambda: Node_Otsu2018().row) + + def test_split(self): + """Test :meth:`colour.recovery.otsu2018.Node_Otsu2018.split` method.""" + + node_a = Node_Otsu2018(self._tree, None) + node_b = Node_Otsu2018(self._tree, data=self._data_a) + node_c = Node_Otsu2018(self._tree, data=self._data_a) + node_a.split([node_b, node_c], PartitionAxis(12, 0)) + + self.assertEqual(len(node_a.children), 2) + + def test_minimise(self): + """Test :meth:`colour.recovery.otsu2018.Node_Otsu2018.minimise` method.""" + + node = Node_Otsu2018(data=self._data_a) + partition, axis, partition_error = node.minimise(3) + + self.assertTupleEqual( + (len(partition[0].data), len(partition[1].data)), (10, 14) + ) + self.assertAlmostEqual(axis.origin, 0.324111380117147, places=7) + self.assertAlmostEqual(partition_error, 2.0402980027, places=7) + + def test_leaf_reconstruction_error(self): + """ + Test :meth:`colour.recovery.otsu2018.Node_Otsu2018.\ +leaf_reconstruction_error` method. + """ + + self.assertAlmostEqual( + self._node_b.leaf_reconstruction_error(), + 1.145340908277367e-29, + places=7, + ) + + def test_branch_reconstruction_error(self): + """ + Test :meth:`colour.recovery.otsu2018.Node_Otsu2018.\ +branch_reconstruction_error` method. + """ + + self.assertAlmostEqual( + self._node_a.branch_reconstruction_error(), + 3.900015991807948e-25, + places=7, + ) -class TestNodeTree_Otsu2018(unittest.TestCase): +class TestTree_Otsu2018(unittest.TestCase): """ - Defines :class:`colour.recovery.otsu2018.NodeTree_Otsu2018` definition unit + Define :class:`colour.recovery.otsu2018.Tree_Otsu2018` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ + """Initialise the common tests attributes.""" self._shape = SPECTRAL_SHAPE_OTSU2018 - self._cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'].copy().align(self._shape) + self._cmfs, self._sd_D65 = handle_spectral_arguments( + shape_default=self._shape + ) + + self._reflectances = sds_and_msds_to_msds( + list(SDS_COLOURCHECKERS["ColorChecker N Ohta"].values()) + + list(SDS_COLOURCHECKERS["BabelColor Average"].values()) + ) - self._sd_D65 = SDS_ILLUMINANTS['D65'].copy().align(self._shape) - self._xy_D65 = CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'][ - 'D65'] + self._tree = Tree_Otsu2018( + self._reflectances, self._cmfs, self._sd_D65 + ) + + self._XYZ_D65 = sd_to_XYZ(self._sd_D65) + self._xy_D65 = XYZ_to_xy(self._XYZ_D65) self._temporary_directory = tempfile.mkdtemp() + self._path = os.path.join( + self._temporary_directory, "Test_Otsu2018.npz" + ) + def tearDown(self): - """ - After tests actions. - """ + """After tests actions.""" shutil.rmtree(self._temporary_directory) def test_required_attributes(self): - """ - Tests presence of required attributes. - """ + """Test the presence of required attributes.""" - required_attributes = ('reflectances', 'cmfs', 'illuminant', - 'minimum_cluster_size') + required_attributes = ("reflectances", "cmfs", "illuminant") for attribute in required_attributes: - self.assertIn(attribute, dir(NodeTree_Otsu2018)) + self.assertIn(attribute, dir(Tree_Otsu2018)) def test_required_methods(self): + """Test the presence of required methods.""" + + required_methods = ("__init__", "__str__", "optimise", "to_dataset") + + for method in required_methods: + self.assertIn(method, dir(Tree_Otsu2018)) + + def test_reflectances(self): """ - Tests presence of required methods. + Test :attr:`colour.recovery.otsu2018.Tree_Otsu2018.reflectances` + property. """ - required_methods = ('__init__', '__str__', 'msds_to_XYZ', 'optimise', - 'to_dataset') + np.testing.assert_almost_equal( + self._tree.reflectances, + np.transpose( + reshape_msds( + sds_and_msds_to_msds(self._reflectances), self._shape + ).values + ), + decimal=7, + ) - for method in required_methods: - self.assertIn(method, dir(NodeTree_Otsu2018)) + def test_cmfs(self): + """Test :attr:`colour.recovery.otsu2018.Tree_Otsu2018.cmfs` property.""" - def test_NodeTree_Otsu2018_and_Dataset_Otsu2018(self): + self.assertIs(self._tree.cmfs, self._cmfs) + + def test_illuminant(self): """ - Tests :class:`colour.recovery.otsu2018.NodeTree_Otsu2018` dataset - generation and :class:`colour.recovery.otsu2018.Dataset_Otsu2018` - input and output. The generated dataset is also tested for - reconstruction errors. + Test :attr:`colour.recovery.otsu2018.Tree_Otsu2018.illuminant` + property. """ - reflectances = [] - for colourchecker in ['ColorChecker N Ohta', 'BabelColor Average']: - for sd in SDS_COLOURCHECKERS[colourchecker].values(): - reflectances.append(sd.copy().align(self._shape).values) + self.assertIs(self._tree.illuminant, self._sd_D65) + + def test_optimise(self): + """Test :class:`colour.recovery.otsu2018.Tree_Otsu2018.optimise` method.""" - node_tree = NodeTree_Otsu2018(reflectances, self._cmfs, self._sd_D65) - node_tree.optimise(iterations=2) + node_tree = Tree_Otsu2018(self._reflectances, self._cmfs, self._sd_D65) + node_tree.optimise(iterations=5) - path = os.path.join(self._temporary_directory, 'Test_Otsu2018.npz') dataset = node_tree.to_dataset() - dataset.write(path) + dataset.write(self._path) dataset = Dataset_Otsu2018() - dataset.read(path) + dataset.read(self._path) - for sd in SDS_COLOURCHECKERS['ColorChecker N Ohta'].values(): + for sd in SDS_COLOURCHECKERS["ColorChecker N Ohta"].values(): XYZ = sd_to_XYZ(sd, self._cmfs, self._sd_D65) / 100 Lab = XYZ_to_Lab(XYZ, self._xy_D65) - recovered_sd = XYZ_to_sd_Otsu2018(XYZ, self._cmfs, self._sd_D65, - dataset, False) - recovered_XYZ = sd_to_XYZ(recovered_sd, self._cmfs, - self._sd_D65) / 100 + recovered_sd = XYZ_to_sd_Otsu2018( + XYZ, self._cmfs, self._sd_D65, dataset, False + ) + recovered_XYZ = ( + sd_to_XYZ(recovered_sd, self._cmfs, self._sd_D65) / 100 + ) recovered_Lab = XYZ_to_Lab(recovered_XYZ, self._xy_D65) - error = metric_mse(sd.copy().align(SPECTRAL_SHAPE_OTSU2018).values, - recovered_sd.values) + error = metric_mse( + reshape_sd(sd, SPECTRAL_SHAPE_OTSU2018).values, + recovered_sd.values, + ) self.assertLess(error, 0.075) delta_E = delta_E_CIE1976(Lab, recovered_Lab) self.assertLess(delta_E, 1e-12) + def test_to_dataset(self): + """ + Test :attr:`colour.recovery.otsu2018.Tree_Otsu2018.to_dataset` + method. + """ + + node_tree = Tree_Otsu2018(self._reflectances, self._cmfs, self._sd_D65) + dataset = node_tree.to_dataset() + dataset.write(self._path) + -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/recovery/tests/test_smits1999.py b/colour/recovery/tests/test_smits1999.py index a75a0469a5..c94f138d87 100644 --- a/colour/recovery/tests/test_smits1999.py +++ b/colour/recovery/tests/test_smits1999.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.recovery.smits1999` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.recovery.smits1999` module.""" import numpy as np import unittest @@ -13,88 +8,165 @@ from colour.recovery.smits1999 import XYZ_to_RGB_Smits1999 from colour.utilities import domain_range_scale -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestRGB_to_sd_Smits1999'] +__all__ = [ + "TestRGB_to_sd_Smits1999", +] class TestRGB_to_sd_Smits1999(unittest.TestCase): """ - Defines :func:`colour.recovery.smits1999.RGB_to_sd_Smits1999` + Define :func:`colour.recovery.smits1999.RGB_to_sd_Smits1999` definition unit tests methods. """ def test_RGB_to_sd_Smits1999(self): """ - Tests :func:`colour.recovery.smits1999.RGB_to_sd_Smits1999` + Test :func:`colour.recovery.smits1999.RGB_to_sd_Smits1999` definition. """ np.testing.assert_almost_equal( RGB_to_sd_Smits1999( XYZ_to_RGB_Smits1999( - np.array([0.21781186, 0.12541048, 0.04697113]))).values, - np.array([ - 0.07691923, 0.05870050, 0.03943195, 0.03024978, 0.02750692, - 0.02808645, 0.34298985, 0.41185795, 0.41185795, 0.41180754 - ]), - decimal=7) + np.array([0.21781186, 0.12541048, 0.04697113]) + ) + ).values, + np.array( + [ + 0.07691923, + 0.05870050, + 0.03943195, + 0.03024978, + 0.02750692, + 0.02808645, + 0.34298985, + 0.41185795, + 0.41185795, + 0.41180754, + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_sd_Smits1999( XYZ_to_RGB_Smits1999( - np.array([0.15434689, 0.22960951, 0.09620221]))).values, - np.array([ - 0.06981477, 0.06981351, 0.07713379, 0.25139495, 0.30063408, - 0.28797045, 0.11990414, 0.08186170, 0.08198613, 0.08272671 - ]), - decimal=7) + np.array([0.15434689, 0.22960951, 0.09620221]) + ) + ).values, + np.array( + [ + 0.06981477, + 0.06981351, + 0.07713379, + 0.25139495, + 0.30063408, + 0.28797045, + 0.11990414, + 0.08186170, + 0.08198613, + 0.08272671, + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_sd_Smits1999( XYZ_to_RGB_Smits1999( - np.array([0.07683480, 0.06006092, 0.25833845]))).values, - np.array([ - 0.29091152, 0.29010285, 0.26572455, 0.13140471, 0.05160646, - 0.05162034, 0.02765638, 0.03199188, 0.03472939, 0.03504156 - ]), - decimal=7) + np.array([0.07683480, 0.06006092, 0.25833845]) + ) + ).values, + np.array( + [ + 0.29091152, + 0.29010285, + 0.26572455, + 0.13140471, + 0.05160646, + 0.05162034, + 0.02765638, + 0.03199188, + 0.03472939, + 0.03504156, + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_sd_Smits1999( - XYZ_to_RGB_Smits1999(np.array([0.0, 1.0, 0.0]))).values, - np.array([ - -0.2549796, -0.2848386, -0.1634905, 1.5254829, 1.9800433, - 1.8510762, -0.7327702, -1.2758621, -1.2758621, -1.2703551 - ]), - decimal=7) + XYZ_to_RGB_Smits1999(np.array([0.0, 1.0, 0.0])) + ).values, + np.array( + [ + -0.2549796, + -0.2848386, + -0.1634905, + 1.5254829, + 1.9800433, + 1.8510762, + -0.7327702, + -1.2758621, + -1.2758621, + -1.2703551, + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_sd_Smits1999( - XYZ_to_RGB_Smits1999(np.array([1.0, 1.0, 0.0]))).values, - np.array([ - -0.1168428, -0.1396982, -0.0414535, 0.581391, 0.9563091, - 0.9562111, 1.3366949, 1.3742666, 1.3853491, 1.4027005 - ]), - decimal=7) + XYZ_to_RGB_Smits1999(np.array([1.0, 1.0, 0.0])) + ).values, + np.array( + [ + -0.1168428, + -0.1396982, + -0.0414535, + 0.581391, + 0.9563091, + 0.9562111, + 1.3366949, + 1.3742666, + 1.3853491, + 1.4027005, + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( RGB_to_sd_Smits1999( - XYZ_to_RGB_Smits1999(np.array([0.5, 0.0, 1.0]))).values, - np.array([ - 1.1938776, 1.1938776, 1.1213867, -0.067889, -0.4668587, - -0.4030985, 0.703056, 0.9407334, 0.9437298, 0.9383386 - ]), - decimal=7) + XYZ_to_RGB_Smits1999(np.array([0.5, 0.0, 1.0])) + ).values, + np.array( + [ + 1.1938776, + 1.1938776, + 1.1213867, + -0.067889, + -0.4668587, + -0.4030985, + 0.703056, + 0.9407334, + 0.9437298, + 0.9383386, + ] + ), + decimal=7, + ) def test_domain_range_scale_RGB_to_sd_Smits1999(self): """ - Tests :func:`colour.recovery.smits1999.RGB_to_sd_Smits1999` + Test :func:`colour.recovery.smits1999.RGB_to_sd_Smits1999` definition domain and range scale support. """ @@ -102,15 +174,17 @@ def test_domain_range_scale_RGB_to_sd_Smits1999(self): RGB_i = XYZ_to_RGB_Smits1999(XYZ_i) XYZ_o = sd_to_XYZ_integration(RGB_to_sd_Smits1999(RGB_i)) - d_r = (('reference', 1, 1), (1, 1, 0.01), (100, 100, 1)) + d_r = (("reference", 1, 1), ("1", 1, 0.01), ("100", 100, 1)) for scale, factor_a, factor_b in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( sd_to_XYZ_integration( - RGB_to_sd_Smits1999(RGB_i * factor_a)), + RGB_to_sd_Smits1999(RGB_i * factor_a) + ), XYZ_o * factor_b, - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/temperature/__init__.py b/colour/temperature/__init__.py index 7aa13ca3a0..47c1cb5840 100644 --- a/colour/temperature/__init__.py +++ b/colour/temperature/__init__.py @@ -1,8 +1,57 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import +""" +References +---------- +- :cite:`AdobeSystems2013` : Adobe Systems. (2013). Adobe DNG Software + Development Kit (SDK) - 1.3.0.0 - + dng_sdk_1_3/dng_sdk/source/dng_temperature.cpp::dng_temperature::\ +Set_xy_coord. https://www.adobe.com/support/downloads/dng/dng_sdk.html +- :cite:`AdobeSystems2013a` : Adobe Systems. (2013). Adobe DNG Software + Development Kit (SDK) - 1.3.0.0 - + dng_sdk_1_3/dng_sdk/source/dng_temperature.cpp::dng_temperature::xy_coord. + https://www.adobe.com/support/downloads/dng/dng_sdk.html +- :cite:`Hernandez-Andres1999a` : Hernández-Andrés, J., Lee, R. L., & + Romero, J. (1999). Calculating correlated color temperatures across the + entire gamut of daylight and skylight chromaticities. Applied Optics, + 38(27), + 5703. doi:10.1364/AO.38.005703 +- :cite:`Kang2002a` : Kang, B., Moon, O., Hong, C., Lee, H., Cho, B., & Kim, + Y. (2002). Design of advanced color: Temperature control system for HDTV + applications. Journal of the Korean Physical Society, 41(6), 865-871. +- :cite:`Krystek1985b` : Krystek, M. (1985). An algorithm to calculate + correlated colour temperature. Color Research & Application, 10(1), 38-40. + doi:10.1002/col.5080100109 +- :cite:`Ohno2014a` : Ohno, Yoshiro. (2014). Practical Use and Calculation of + CCT and Duv. LEUKOS, 10(1), 47-55. doi:10.1080/15502724.2014.839020 +- :cite:`Wikipedia2001` : Wikipedia. (2001). Approximation. Retrieved June + 28, 2014, from http://en.wikipedia.org/wiki/Color_temperature#Approximation +- :cite:`Wikipedia2001a` : Wikipedia. (2001). Color temperature. Retrieved + June 28, 2014, from http://en.wikipedia.org/wiki/Color_temperature +- :cite:`Wyszecki2000y` : Wyszecki, Günther, & Stiles, W. S. (2000). + DISTRIBUTION TEMPERATURE, COLOR TEMPERATURE, AND CORRELATED COLOR + TEMPERATURE. In Color Science: Concepts and Methods, Quantitative Data and + Formulae (pp. 224-229). Wiley. ISBN:978-0-471-39918-6 +- :cite:`Wyszecki2000z` : Wyszecki, Günther, & Stiles, W. S. (2000). CIE + Method of Calculating D-Illuminants. In Color Science: Concepts and + Methods, Quantitative Data and Formulae (pp. 145-146). Wiley. + ISBN:978-0-471-39918-6 +""" -from colour.utilities import CaseInsensitiveMapping, filter_kwargs +from __future__ import annotations + +from colour.hints import ( + Any, + ArrayLike, + FloatingOrArrayLike, + FloatingOrNDArray, + NDArray, + Literal, + Union, +) +from colour.utilities import ( + CaseInsensitiveMapping, + filter_kwargs, + validate_method, +) from .cie_d import xy_to_CCT_CIE_D, CCT_to_xy_CIE_D from .hernandez1999 import xy_to_CCT_Hernandez1999, CCT_to_xy_Hernandez1999 @@ -12,78 +61,103 @@ from .ohno2013 import uv_to_CCT_Ohno2013, CCT_to_uv_Ohno2013 from .robertson1968 import uv_to_CCT_Robertson1968, CCT_to_uv_Robertson1968 -__all__ = ['xy_to_CCT_CIE_D', 'CCT_to_xy_CIE_D'] -__all__ += ['xy_to_CCT_Hernandez1999', 'CCT_to_xy_Hernandez1999'] -__all__ += ['xy_to_CCT_Kang2002', 'CCT_to_xy_Kang2002'] -__all__ += ['uv_to_CCT_Krystek1985', 'CCT_to_uv_Krystek1985'] -__all__ += ['xy_to_CCT_McCamy1992', 'CCT_to_xy_McCamy1992'] -__all__ += ['uv_to_CCT_Ohno2013', 'CCT_to_uv_Ohno2013'] -__all__ += ['uv_to_CCT_Robertson1968', 'CCT_to_uv_Robertson1968'] - -UV_TO_CCT_METHODS = CaseInsensitiveMapping({ - 'Krystek 1985': uv_to_CCT_Krystek1985, - 'Ohno 2013': uv_to_CCT_Ohno2013, - 'Robertson 1968': uv_to_CCT_Robertson1968 -}) +__all__ = [ + "xy_to_CCT_CIE_D", + "CCT_to_xy_CIE_D", +] +__all__ += [ + "xy_to_CCT_Hernandez1999", + "CCT_to_xy_Hernandez1999", +] +__all__ += [ + "xy_to_CCT_Kang2002", + "CCT_to_xy_Kang2002", +] +__all__ += [ + "uv_to_CCT_Krystek1985", + "CCT_to_uv_Krystek1985", +] +__all__ += [ + "xy_to_CCT_McCamy1992", + "CCT_to_xy_McCamy1992", +] +__all__ += [ + "uv_to_CCT_Ohno2013", + "CCT_to_uv_Ohno2013", +] +__all__ += [ + "uv_to_CCT_Robertson1968", + "CCT_to_uv_Robertson1968", +] + +UV_TO_CCT_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Krystek 1985": uv_to_CCT_Krystek1985, + "Ohno 2013": uv_to_CCT_Ohno2013, + "Robertson 1968": uv_to_CCT_Robertson1968, + } +) UV_TO_CCT_METHODS.__doc__ = """ Supported *CIE UCS* colourspace *uv* chromaticity coordinates to correlated colour temperature :math:`T_{cp}` computation methods. References ---------- -:cite:`AdobeSystems2013`, :cite:`AdobeSystems2013a`, :cite:`Ohno2014a`, -:cite:`Wyszecki2000y` - -UV_TO_CCT_METHODS : CaseInsensitiveMapping - **{'Ohno 2013', 'Krystek 1985, 'Robertson 1968'}** +:cite:`AdobeSystems2013`, :cite:`AdobeSystems2013a`, :cite:`Krystek1985b`, +:cite:`Ohno2014a`, :cite:`Wyszecki2000y` Aliases: - 'ohno2013': 'Ohno 2013' - 'robertson1968': 'Robertson 1968' """ -UV_TO_CCT_METHODS['ohno2013'] = UV_TO_CCT_METHODS['Ohno 2013'] -UV_TO_CCT_METHODS['robertson1968'] = UV_TO_CCT_METHODS['Robertson 1968'] +UV_TO_CCT_METHODS["ohno2013"] = UV_TO_CCT_METHODS["Ohno 2013"] +UV_TO_CCT_METHODS["robertson1968"] = UV_TO_CCT_METHODS["Robertson 1968"] -def uv_to_CCT(uv, method='Ohno 2013', **kwargs): +def uv_to_CCT( + uv: ArrayLike, + method: Union[ + Literal["Krystek 1985", "Ohno 2013", "Robertson 1968"], str + ] = "Ohno 2013", + **kwargs: Any, +) -> NDArray: """ - Returns the correlated colour temperature :math:`T_{cp}` and + Return the correlated colour temperature :math:`T_{cp}` and :math:`\\Delta_{uv}` from given *CIE UCS* colourspace *uv* chromaticity coordinates using given method. Parameters ---------- - uv : array_like + uv *CIE UCS* colourspace *uv* chromaticity coordinates. - method : unicode, optional - **{'Ohno 2013', 'Krystek 1985, 'Robertson 1968'}**, + method Computation method. Other Parameters ---------------- - cmfs : XYZ_ColourMatchingFunctions, optional + cmfs {:func:`colour.temperature.uv_to_CCT_Ohno2013`}, Standard observer colour matching functions. - start : numeric, optional + count {:func:`colour.temperature.uv_to_CCT_Ohno2013`}, - Temperature range start in kelvins. - end : numeric, optional + Temperatures count in the planckian tables. + end {:func:`colour.temperature.uv_to_CCT_Ohno2013`}, Temperature range end in kelvins. - count : int, optional - {:func:`colour.temperature.uv_to_CCT_Ohno2013`}, - Temperatures count in the planckian tables. - iterations : int, optional + iterations {:func:`colour.temperature.uv_to_CCT_Ohno2013`}, Number of planckian tables to generate. - optimisation_kwargs : dict_like, optional + optimisation_kwargs {:func:`colour.temperature.uv_to_CCT_Krystek1985`}, Parameters for :func:`scipy.optimize.minimize` definition. + start + {:func:`colour.temperature.uv_to_CCT_Ohno2013`}, + Temperature range start in kelvins. Returns ------- - ndarray + :class:`numpy.ndarray` Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`. References @@ -95,87 +169,69 @@ def uv_to_CCT(uv, method='Ohno 2013', **kwargs): -------- >>> import numpy as np >>> uv = np.array([0.1978, 0.3122]) - >>> # Doctests skipping for Python 2.x compatibility. - >>> uv_to_CCT(uv) # doctest: +SKIP - array([ 6.5074738...e+03, 3.2233460...e-03]) + >>> uv_to_CCT(uv) # doctest: +ELLIPSIS + array([ 6.507473...e+03, 3.223346...e-03]) """ + method = validate_method(method, UV_TO_CCT_METHODS) + function = UV_TO_CCT_METHODS[method] return function(uv, **filter_kwargs(function, **kwargs)) -CCT_TO_UV_METHODS = CaseInsensitiveMapping({ - 'Krystek 1985': CCT_to_uv_Krystek1985, - 'Ohno 2013': CCT_to_uv_Ohno2013, - 'Robertson 1968': CCT_to_uv_Robertson1968 -}) +CCT_TO_UV_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Krystek 1985": CCT_to_uv_Krystek1985, + "Ohno 2013": CCT_to_uv_Ohno2013, + "Robertson 1968": CCT_to_uv_Robertson1968, + } +) CCT_TO_UV_METHODS.__doc__ = """ Supported correlated colour temperature :math:`T_{cp}` to *CIE UCS* colourspace *uv* chromaticity coordinates computation methods. References ---------- -- :cite:`AdobeSystems2013` : Adobe Systems. (2013). Adobe DNG Software - Development Kit (SDK) - 1.3.0.0 - - dng_sdk_1_3/dng_sdk/source/dng_temperature.cpp::dng_temperature::\ -Set_xy_coord. https://www.adobe.com/support/downloads/dng/dng_sdk.html -- :cite:`AdobeSystems2013a` : Adobe Systems. (2013). Adobe DNG Software - Development Kit (SDK) - 1.3.0.0 - - dng_sdk_1_3/dng_sdk/source/dng_temperature.cpp::dng_temperature::xy_coord. - https://www.adobe.com/support/downloads/dng/dng_sdk.html -- :cite:`Hernandez-Andres1999a` : Hernández-Andrés, J., Lee, R. L., & - Romero, J. (1999). Calculating correlated color temperatures across the - entire gamut of daylight and skylight chromaticities. Applied Optics, - 38(27), - 5703. doi:10.1364/AO.38.005703 -- :cite:`Kang2002a` : Kang, B., Moon, O., Hong, C., Lee, H., Cho, B., & Kim, - Y. (2002). Design of advanced color: Temperature control system for HDTV - applications. Journal of the Korean Physical Society, 41(6), 865-871. -- :cite:`Krystek1985b` : Krystek, M. (1985). An algorithm to calculate - correlated colour temperature. Color Research & Application, 10(1), 38-40. - doi:10.1002/col.5080100109 -- :cite:`Ohno2014a` : Ohno, Yoshiro. (2014). Practical Use and Calculation of - CCT and Duv. LEUKOS, 10(1), 47-55. doi:10.1080/15502724.2014.839020 -- :cite:`Wikipedia2001` : Wikipedia. (2001). Approximation. Retrieved June - 28, 2014, from http://en.wikipedia.org/wiki/Color_temperature#Approximation -- :cite:`Wikipedia2001a` : Wikipedia. (2001). Color temperature. Retrieved - June 28, 2014, from http://en.wikipedia.org/wiki/Color_temperature -- :cite:`Wyszecki2000y` : Wyszecki, Günther, & Stiles, W. S. (2000). - DISTRIBUTION TEMPERATURE, COLOR TEMPERATURE, AND CORRELATED COLOR - TEMPERATURE. In Color Science: Concepts and Methods, Quantitative Data and - Formulae (pp. 224-229). Wiley. ISBN:978-0-471-39918-6 -- :cite:`Wyszecki2000z` : Wyszecki, Günther, & Stiles, W. S. (2000). CIE - Method of Calculating D-Illuminants. In Color Science: Concepts and - Methods, Quantitative Data and Formulae (pp. 145-146). Wiley. - ISBN:978-0-471-39918-6 +:cite:`AdobeSystems2013`, :cite:`AdobeSystems2013a`, :cite:`Krystek1985b`, +:cite:`Ohno2014a`, :cite:`Wyszecki2000y` + +Aliases: + +- 'ohno2013': 'Ohno 2013' +- 'robertson1968': 'Robertson 1968' """ -CCT_TO_UV_METHODS['ohno2013'] = CCT_TO_UV_METHODS['Ohno 2013'] -CCT_TO_UV_METHODS['robertson1968'] = CCT_TO_UV_METHODS['Robertson 1968'] +CCT_TO_UV_METHODS["ohno2013"] = CCT_TO_UV_METHODS["Ohno 2013"] +CCT_TO_UV_METHODS["robertson1968"] = CCT_TO_UV_METHODS["Robertson 1968"] -def CCT_to_uv(CCT_D_uv, method='Ohno 2013', **kwargs): +def CCT_to_uv( + CCT_D_uv: ArrayLike, + method: Union[ + Literal["Krystek 1985", "Ohno 2013", "Robertson 1968"], str + ] = "Ohno 2013", + **kwargs: Any, +) -> NDArray: """ - Returns the *CIE UCS* colourspace *uv* chromaticity coordinates from given + Return the *CIE UCS* colourspace *uv* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}` using given method. Parameters ---------- - CCT_D_uv : ndarray + CCT_D_uv Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`. - method : unicode, optional - **{'Ohno 2013', 'Robertson 1968', 'Krystek 1985}**, + method Computation method. Other Parameters ---------------- - cmfs : XYZ_ColourMatchingFunctions, optional + cmfs {:func:`colour.temperature.CCT_to_uv_Ohno2013`}, Standard observer colour matching functions. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE UCS* colourspace *uv* chromaticity coordinates. References @@ -191,20 +247,30 @@ def CCT_to_uv(CCT_D_uv, method='Ohno 2013', **kwargs): array([ 0.1977999..., 0.3121999...]) """ + method = validate_method(method, CCT_TO_UV_METHODS) + function = CCT_TO_UV_METHODS[method] return function(CCT_D_uv, **filter_kwargs(function, **kwargs)) -__all__ += ['UV_TO_CCT_METHODS', 'uv_to_CCT'] -__all__ += ['CCT_TO_UV_METHODS', 'CCT_to_uv'] - -XY_TO_CCT_METHODS = CaseInsensitiveMapping({ - 'CIE Illuminant D Series': xy_to_CCT_CIE_D, - 'Hernandez 1999': xy_to_CCT_Hernandez1999, - 'Kang 2002': xy_to_CCT_Kang2002, - 'McCamy 1992': xy_to_CCT_McCamy1992, -}) +__all__ += [ + "UV_TO_CCT_METHODS", + "uv_to_CCT", +] +__all__ += [ + "CCT_TO_UV_METHODS", + "CCT_to_uv", +] + +XY_TO_CCT_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "CIE Illuminant D Series": xy_to_CCT_CIE_D, + "Hernandez 1999": xy_to_CCT_Hernandez1999, + "Kang 2002": xy_to_CCT_Kang2002, + "McCamy 1992": xy_to_CCT_McCamy1992, + } +) XY_TO_CCT_METHODS.__doc__ = """ Supported *CIE xy* chromaticity coordinates to correlated colour temperature :math:`T_{cp}` computation methods. @@ -212,12 +278,7 @@ def CCT_to_uv(CCT_D_uv, method='Ohno 2013', **kwargs): References ---------- :cite:`Hernandez-Andres1999a`, :cite:`Kang2002a`, :cite:`Wikipedia2001`, -:cite:`Wikipedia2001a`, -:cite:`Wyszecki2000z` - -XY_TO_CCT_METHODS : CaseInsensitiveMapping - **{'McCamy 1992', 'CIE Illuminant D Series, 'Kang 2002', - 'Hernandez 1999'}** +:cite:`Wikipedia2001a`, :cite:`Wyszecki2000z` Aliases: @@ -226,36 +287,45 @@ def CCT_to_uv(CCT_D_uv, method='Ohno 2013', **kwargs): - 'mccamy1992': 'McCamy 1992' - 'hernandez1999': 'Hernandez 1999' """ -XY_TO_CCT_METHODS['daylight'] = XY_TO_CCT_METHODS['CIE Illuminant D Series'] -XY_TO_CCT_METHODS['kang2002'] = XY_TO_CCT_METHODS['Kang 2002'] -XY_TO_CCT_METHODS['mccamy1992'] = XY_TO_CCT_METHODS['McCamy 1992'] -XY_TO_CCT_METHODS['hernandez1999'] = XY_TO_CCT_METHODS['Hernandez 1999'] - - -def xy_to_CCT(xy, method='CIE Illuminant D Series'): +XY_TO_CCT_METHODS["daylight"] = XY_TO_CCT_METHODS["CIE Illuminant D Series"] +XY_TO_CCT_METHODS["kang2002"] = XY_TO_CCT_METHODS["Kang 2002"] +XY_TO_CCT_METHODS["mccamy1992"] = XY_TO_CCT_METHODS["McCamy 1992"] +XY_TO_CCT_METHODS["hernandez1999"] = XY_TO_CCT_METHODS["Hernandez 1999"] + + +def xy_to_CCT( + xy: ArrayLike, + method: Union[ + Literal[ + "CIE Illuminant D Series", + "Kang 2002", + "Hernandez 1999", + "McCamy 1992", + ], + str, + ] = "CIE Illuminant D Series", +) -> FloatingOrNDArray: """ - Returns the correlated colour temperature :math:`T_{cp}` from given + Return the correlated colour temperature :math:`T_{cp}` from given *CIE xy* chromaticity coordinates using given method. Parameters ---------- - xy : array_like + xy *CIE xy* chromaticity coordinates. - method : unicode, optional - **{'CIE Illuminant D Series', 'Kang 2002', 'Hernandez 1999', - 'McCamy 1992'}**, + method Computation method. Other Parameters ---------------- - optimisation_kwargs : dict_like, optional + optimisation_kwargs {:func:`colour.temperature.xy_to_CCT_CIE_D`, :func:`colour.temperature.xy_to_CCT_Kang2002`}, Parameters for :func:`scipy.optimize.minimize` definition. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Correlated colour temperature :math:`T_{cp}`. References @@ -273,15 +343,19 @@ def xy_to_CCT(xy, method='CIE Illuminant D Series'): 6500.7420431... """ - return XY_TO_CCT_METHODS.get(method)(xy) + method = validate_method(method, XY_TO_CCT_METHODS) + return XY_TO_CCT_METHODS[method](xy) -CCT_TO_XY_METHODS = CaseInsensitiveMapping({ - 'CIE Illuminant D Series': CCT_to_xy_CIE_D, - 'Hernandez 1999': CCT_to_xy_Hernandez1999, - 'Kang 2002': CCT_to_xy_Kang2002, - 'McCamy 1992': CCT_to_xy_McCamy1992, -}) + +CCT_TO_XY_METHODS: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "CIE Illuminant D Series": CCT_to_xy_CIE_D, + "Hernandez 1999": CCT_to_xy_Hernandez1999, + "Kang 2002": CCT_to_xy_Kang2002, + "McCamy 1992": CCT_to_xy_McCamy1992, + } +) CCT_TO_XY_METHODS.__doc__ = """ Supported correlated colour temperature :math:`T_{cp}` to *CIE xy* chromaticity coordinates computation methods. @@ -291,10 +365,6 @@ def xy_to_CCT(xy, method='CIE Illuminant D Series'): :cite:`Hernandez-Andres1999a`, :cite:`Kang2002a`, :cite:`Wikipedia2001`, :cite:`Wikipedia2001a`, :cite:`Wyszecki2000z` -CCT_TO_XY_METHODS : CaseInsensitiveMapping - **{'Kang 2002', 'CIE Illuminant D Series', 'Hernandez 1999', - 'McCamy 1992'}** - Aliases: - 'daylight': 'CIE Illuminant D Series' @@ -302,36 +372,45 @@ def xy_to_CCT(xy, method='CIE Illuminant D Series'): - 'mccamy1992': 'McCamy 1992' - 'hernandez1999': 'Hernandez 1999' """ -CCT_TO_XY_METHODS['daylight'] = CCT_TO_XY_METHODS['CIE Illuminant D Series'] -CCT_TO_XY_METHODS['kang2002'] = CCT_TO_XY_METHODS['Kang 2002'] -CCT_TO_XY_METHODS['mccamy1992'] = CCT_TO_XY_METHODS['McCamy 1992'] -CCT_TO_XY_METHODS['hernandez1999'] = CCT_TO_XY_METHODS['Hernandez 1999'] - - -def CCT_to_xy(CCT, method='CIE Illuminant D Series'): +CCT_TO_XY_METHODS["daylight"] = CCT_TO_XY_METHODS["CIE Illuminant D Series"] +CCT_TO_XY_METHODS["kang2002"] = CCT_TO_XY_METHODS["Kang 2002"] +CCT_TO_XY_METHODS["mccamy1992"] = CCT_TO_XY_METHODS["McCamy 1992"] +CCT_TO_XY_METHODS["hernandez1999"] = CCT_TO_XY_METHODS["Hernandez 1999"] + + +def CCT_to_xy( + CCT: FloatingOrArrayLike, + method: Union[ + Literal[ + "CIE Illuminant D Series", + "Kang 2002", + "Hernandez 1999", + "McCamy 1992", + ], + str, + ] = "CIE Illuminant D Series", +) -> NDArray: """ - Returns the *CIE xy* chromaticity coordinates from given correlated colour + Return the *CIE xy* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}` using given method. Parameters ---------- - CCT : numeric or array_like + CCT Correlated colour temperature :math:`T_{cp}`. - method : unicode, optional - **{'CIE Illuminant D Series', 'Hernandez 1999', 'Kang 2002', - 'McCamy 1992'}**, + method Computation method. Other Parameters ---------------- - optimisation_kwargs : dict_like, optional + optimisation_kwargs {:func:`colour.temperature.CCT_to_xy_Hernandez1999`, :func:`colour.temperature.CCT_to_xy_McCamy1992`}, Parameters for :func:`scipy.optimize.minimize` definition. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE xy* chromaticity coordinates. References @@ -348,8 +427,16 @@ def CCT_to_xy(CCT, method='CIE Illuminant D Series'): array([ 0.313426 ..., 0.3235959...]) """ - return CCT_TO_XY_METHODS.get(method)(CCT) + method = validate_method(method, CCT_TO_XY_METHODS) + + return CCT_TO_XY_METHODS[method](CCT) -__all__ += ['XY_TO_CCT_METHODS', 'xy_to_CCT'] -__all__ += ['CCT_TO_XY_METHODS', 'CCT_to_xy'] +__all__ += [ + "XY_TO_CCT_METHODS", + "xy_to_CCT", +] +__all__ += [ + "CCT_TO_XY_METHODS", + "CCT_to_xy", +] diff --git a/colour/temperature/cie_d.py b/colour/temperature/cie_d.py index 7075664627..845a73a858 100644 --- a/colour/temperature/cie_d.py +++ b/colour/temperature/cie_d.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ CIE Illuminant D Series Correlated Colour Temperature ===================================================== -Defines *CIE Illuminant D Series* correlated colour temperature :math:`T_{cp} -computations objects: +Defines the *CIE Illuminant D Series* correlated colour temperature +:math:`T_{cp} computations objects: - :func:`colour.temperature.xy_to_CCT_CIE_D`: Correlated colour temperature :math:`T_{cp}` computation of a *CIE Illuminant D Series* from its *CIE xy* @@ -21,45 +20,52 @@ ISBN:978-0-471-39918-6 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from scipy.optimize import minimize from colour.colorimetry import daylight_locus_function -from colour.utilities import as_float_array, as_numeric, tstack, usage_warning -from colour.utilities.deprecation import handle_arguments_deprecation - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['xy_to_CCT_CIE_D', 'CCT_to_xy_CIE_D'] - - -def xy_to_CCT_CIE_D(xy, optimisation_kwargs=None, **kwargs): +from colour.hints import ( + ArrayLike, + Dict, + FloatingOrArrayLike, + FloatingOrNDArray, + NDArray, + Optional, +) +from colour.utilities import as_float_array, as_float, tstack, usage_warning + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "xy_to_CCT_CIE_D", + "CCT_to_xy_CIE_D", +] + + +def xy_to_CCT_CIE_D( + xy: ArrayLike, optimisation_kwargs: Optional[Dict] = None +) -> FloatingOrNDArray: """ - Returns the correlated colour temperature :math:`T_{cp}` of a + Return the correlated colour temperature :math:`T_{cp}` of a *CIE Illuminant D Series* from its *CIE xy* chromaticity coordinates. Parameters ---------- - xy : array_like + xy *CIE xy* chromaticity coordinates. - optimisation_kwargs : dict_like, optional + optimisation_kwargs Parameters for :func:`scipy.optimize.minimize` definition. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Correlated colour temperature :math:`T_{cp}`. Warnings @@ -81,58 +87,57 @@ def xy_to_CCT_CIE_D(xy, optimisation_kwargs=None, **kwargs): 6504.3895840... """ - optimisation_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['optimisation_parameters', 'optimisation_kwargs'] - ], - }, **kwargs).get('optimisation_kwargs', optimisation_kwargs) - xy = as_float_array(xy) shape = xy.shape xy = np.atleast_1d(xy.reshape([-1, 2])) - def objective_function(CCT, xy): - """ - Objective function. - """ + def objective_function( + CCT: FloatingOrArrayLike, xy: ArrayLike + ) -> FloatingOrNDArray: + """Objective function.""" objective = np.linalg.norm(CCT_to_xy_CIE_D(CCT) - xy) - return objective + return as_float(objective) optimisation_settings = { - 'method': 'Nelder-Mead', - 'options': { - 'fatol': 1e-10, + "method": "Nelder-Mead", + "options": { + "fatol": 1e-10, }, } if optimisation_kwargs is not None: optimisation_settings.update(optimisation_kwargs) - CCT = as_float_array([ - minimize( - objective_function, - x0=6500, - args=(xy_i, ), - **optimisation_settings).x for xy_i in xy - ]) + CCT = as_float_array( + [ + minimize( + objective_function, + x0=6500, + args=(xy_i,), + **optimisation_settings, + ).x + for xy_i in as_float_array(xy) + ] + ) - return as_numeric(CCT.reshape(shape[:-1])) + return as_float(np.reshape(CCT, shape[:-1])) -def CCT_to_xy_CIE_D(CCT): +def CCT_to_xy_CIE_D(CCT: FloatingOrArrayLike) -> NDArray: """ - Returns the *CIE xy* chromaticity coordinates of a + Return the *CIE xy* chromaticity coordinates of a *CIE Illuminant D Series* from its correlated colour temperature :math:`T_{cp}`. Parameters ---------- - CCT : numeric or array_like + CCT Correlated colour temperature :math:`T_{cp}`. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE xy* chromaticity coordinates. Raises @@ -153,22 +158,26 @@ def CCT_to_xy_CIE_D(CCT): CCT = as_float_array(CCT) if np.any(CCT[np.asarray(np.logical_or(CCT < 4000, CCT > 25000))]): - usage_warning(('Correlated colour temperature must be in domain ' - '[4000, 25000], unpredictable results may occur!')) + usage_warning( + "Correlated colour temperature must be in domain " + "[4000, 25000], unpredictable results may occur!" + ) - CCT_3 = CCT ** 3 - CCT_2 = CCT ** 2 + CCT_3 = CCT**3 + CCT_2 = CCT**2 x = np.where( CCT <= 7000, - -4.607 * 10 ** 9 / CCT_3 + 2.9678 * 10 ** 6 / CCT_2 + - 0.09911 * 10 ** 3 / CCT + 0.244063, - -2.0064 * 10 ** 9 / CCT_3 + 1.9018 * 10 ** 6 / CCT_2 + - 0.24748 * 10 ** 3 / CCT + 0.23704, + -4.607 * 10**9 / CCT_3 + + 2.9678 * 10**6 / CCT_2 + + 0.09911 * 10**3 / CCT + + 0.244063, + -2.0064 * 10**9 / CCT_3 + + 1.9018 * 10**6 / CCT_2 + + 0.24748 * 10**3 / CCT + + 0.23704, ) y = daylight_locus_function(x) - xy = tstack([x, y]) - - return xy + return tstack([x, y]) diff --git a/colour/temperature/hernandez1999.py b/colour/temperature/hernandez1999.py index 59c4691a3b..cef566d65e 100644 --- a/colour/temperature/hernandez1999.py +++ b/colour/temperature/hernandez1999.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Hernandez-Andres, Lee and Romero (1999) Correlated Colour Temperature ===================================================================== -Defines *Hernandez-Andres et al. (1999)* correlated colour temperature +Defines the *Hernandez-Andres et al. (1999)* correlated colour temperature :math:`T_{cp}` computations objects: - :func:`colour.temperature.xy_to_CCT_Hernandez1999`: Correlated colour @@ -21,39 +20,49 @@ 38(27), 5703. doi:10.1364/AO.38.005703 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from scipy.optimize import minimize from colour.colorimetry import CCS_ILLUMINANTS -from colour.utilities import as_float_array, as_numeric, tsplit, usage_warning -from colour.utilities.deprecation import handle_arguments_deprecation - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['xy_to_CCT_Hernandez1999', 'CCT_to_xy_Hernandez1999'] - - -def xy_to_CCT_Hernandez1999(xy): +from colour.hints import ( + ArrayLike, + Dict, + FloatingOrArrayLike, + FloatingOrNDArray, + NDArray, + Optional, +) +from colour.utilities import as_float_array, as_float, tsplit, usage_warning + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "xy_to_CCT_Hernandez1999", + "CCT_to_xy_Hernandez1999", +] + + +def xy_to_CCT_Hernandez1999(xy: ArrayLike) -> FloatingOrNDArray: """ - Returns the correlated colour temperature :math:`T_{cp}` from given + Return the correlated colour temperature :math:`T_{cp}` from given *CIE xy* chromaticity coordinates using *Hernandez-Andres et al. (1999)* method. Parameters ---------- - xy : array_like + xy *CIE xy* chromaticity coordinates. Returns ------- - numeric + :class:`numpy.floating` or :class:`numpy.ndarray` Correlated colour temperature :math:`T_{cp}`. References @@ -70,41 +79,43 @@ def xy_to_CCT_Hernandez1999(xy): x, y = tsplit(xy) n = (x - 0.3366) / (y - 0.1735) - CCT = (-949.86315 + 6253.80338 * np.exp(-n / 0.92159) + - 28.70599 * np.exp(-n / 0.20039) + 0.00004 * np.exp(-n / 0.07125)) + CCT = ( + -949.86315 + + 6253.80338 * np.exp(-n / 0.92159) + + 28.70599 * np.exp(-n / 0.20039) + + 0.00004 * np.exp(-n / 0.07125) + ) n = np.where(CCT > 50000, (x - 0.3356) / (y - 0.1691), n) CCT = np.where( CCT > 50000, - 36284.48953 + 0.00228 * np.exp(-n / 0.07861) + - 5.4535e-36 * np.exp(-n / 0.01543), + 36284.48953 + + 0.00228 * np.exp(-n / 0.07861) + + 5.4535e-36 * np.exp(-n / 0.01543), CCT, ) - return as_numeric(CCT) + return as_float(CCT) -def CCT_to_xy_Hernandez1999(CCT, optimisation_kwargs=None, **kwargs): +def CCT_to_xy_Hernandez1999( + CCT: FloatingOrArrayLike, optimisation_kwargs: Optional[Dict] = None +) -> NDArray: """ - Returns the *CIE xy* chromaticity coordinates from given correlated colour + Return the *CIE xy* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}` using *Hernandez-Andres et al. (1999)* method. Parameters ---------- - CCT : numeric or array_like + CCT Correlated colour temperature :math:`T_{cp}`. - optimisation_kwargs : dict_like, optional + optimisation_kwargs Parameters for :func:`scipy.optimize.minimize` definition. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - ndarray + :class:`numpy.ndarray` *CIE xy* chromaticity coordinates. Warnings @@ -127,47 +138,52 @@ def CCT_to_xy_Hernandez1999(CCT, optimisation_kwargs=None, **kwargs): array([ 0.3127..., 0.329...]) """ - optimisation_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['optimisation_parameters', 'optimisation_kwargs'] - ], - }, **kwargs).get('optimisation_kwargs', optimisation_kwargs) - - usage_warning('"Hernandez-Andres et al. (1999)" method for computing ' - '"CIE xy" chromaticity coordinates from given correlated ' - 'colour temperature is not a bijective function and and' - 'might produce unexpected results. It is given for ' - 'consistency with other correlated colour temperature ' - 'computation methods but should be avoided for practical ' - 'applications.') + usage_warning( + '"Hernandez-Andres et al. (1999)" method for computing ' + '"CIE xy" chromaticity coordinates from given correlated ' + "colour temperature is not a bijective function and and" + "might produce unexpected results. It is given for " + "consistency with other correlated colour temperature " + "computation methods but should be avoided for practical " + "applications." + ) CCT = as_float_array(CCT) shape = list(CCT.shape) CCT = np.atleast_1d(CCT.reshape([-1, 1])) - def objective_function(xy, CCT): - """ - Objective function. - """ + def objective_function( + xy: ArrayLike, CCT: FloatingOrArrayLike + ) -> FloatingOrNDArray: + """Objective function.""" - objective = np.linalg.norm(xy_to_CCT_Hernandez1999(xy) - CCT) + objective = np.linalg.norm( + xy_to_CCT_Hernandez1999(xy) - as_float_array(CCT) + ) - return objective + return as_float(objective) optimisation_settings = { - 'method': 'Nelder-Mead', - 'options': { - 'fatol': 1e-10, + "method": "Nelder-Mead", + "options": { + "fatol": 1e-10, }, } if optimisation_kwargs is not None: optimisation_settings.update(optimisation_kwargs) - CCT = as_float_array([ - minimize( - objective_function, - x0=CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65'], - args=(CCT_i, ), - **optimisation_settings).x for CCT_i in CCT - ]) + xy = as_float_array( + [ + minimize( + objective_function, + x0=CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][ + "D65" + ], + args=(CCT_i,), + **optimisation_settings, + ).x + for CCT_i in as_float_array(CCT) + ] + ) - return as_numeric(CCT.reshape(shape + [2])) + return np.reshape(xy, (shape + [2])) diff --git a/colour/temperature/kang2002.py b/colour/temperature/kang2002.py index 68030eed5f..4104c08dee 100644 --- a/colour/temperature/kang2002.py +++ b/colour/temperature/kang2002.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Kang, Moon, Hong, Lee, Cho and Kim (2002) Correlated Colour Temperature ======================================================================= -Defines *Kang et al. (2002)* correlated colour temperature :math:`T_{cp}` +Defines the *Kang et al. (2002)* correlated colour temperature :math:`T_{cp}` computations objects: - :func:`colour.temperature.xy_to_CCT_Kang2002`: Correlated colour @@ -20,44 +19,51 @@ applications. Journal of the Korean Physical Society, 41(6), 865-871. """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from scipy.optimize import minimize -from colour.utilities import as_float_array, as_numeric, tstack, usage_warning -from colour.utilities.deprecation import handle_arguments_deprecation - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['xy_to_CCT_Kang2002', 'CCT_to_xy_Kang2002'] - - -def xy_to_CCT_Kang2002(xy, optimisation_kwargs=None, **kwargs): +from colour.hints import ( + ArrayLike, + Dict, + FloatingOrArrayLike, + FloatingOrNDArray, + NDArray, + Optional, +) +from colour.utilities import as_float_array, as_float, tstack, usage_warning + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "xy_to_CCT_Kang2002", + "CCT_to_xy_Kang2002", +] + + +def xy_to_CCT_Kang2002( + xy: ArrayLike, optimisation_kwargs: Optional[Dict] = None +) -> FloatingOrNDArray: """ - Returns the correlated colour temperature :math:`T_{cp}` from given + Return the correlated colour temperature :math:`T_{cp}` from given *CIE xy* chromaticity coordinates using *Kang et al. (2002)* method. Parameters ---------- - xy : array_like + xy *CIE xy* chromaticity coordinates. - optimisation_kwargs : dict_like, optional + optimisation_kwargs Parameters for :func:`scipy.optimize.minimize` definition. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Correlated colour temperature :math:`T_{cp}`. Warnings @@ -79,57 +85,56 @@ def xy_to_CCT_Kang2002(xy, optimisation_kwargs=None, **kwargs): 6504.3893128... """ - optimisation_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['optimisation_parameters', 'optimisation_kwargs'] - ], - }, **kwargs).get('optimisation_kwargs', optimisation_kwargs) - xy = as_float_array(xy) shape = xy.shape xy = np.atleast_1d(xy.reshape([-1, 2])) - def objective_function(CCT, xy): - """ - Objective function. - """ + def objective_function( + CCT: FloatingOrArrayLike, xy: ArrayLike + ) -> FloatingOrNDArray: + """Objective function.""" objective = np.linalg.norm(CCT_to_xy_Kang2002(CCT) - xy) - return objective + return as_float(objective) optimisation_settings = { - 'method': 'Nelder-Mead', - 'options': { - 'fatol': 1e-10, + "method": "Nelder-Mead", + "options": { + "fatol": 1e-10, }, } if optimisation_kwargs is not None: optimisation_settings.update(optimisation_kwargs) - CCT = as_float_array([ - minimize( - objective_function, - x0=6500, - args=(xy_i, ), - **optimisation_settings).x for xy_i in xy - ]) + CCT = as_float_array( + [ + minimize( + objective_function, + x0=6500, + args=(xy_i,), + **optimisation_settings, + ).x + for xy_i in as_float_array(xy) + ] + ) - return as_numeric(CCT.reshape(shape[:-1])) + return as_float(np.reshape(CCT, shape[:-1])) -def CCT_to_xy_Kang2002(CCT): +def CCT_to_xy_Kang2002(CCT: FloatingOrArrayLike) -> NDArray: """ - Returns the *CIE xy* chromaticity coordinates from given correlated colour + Return the *CIE xy* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}` using *Kang et al. (2002)* method. Parameters ---------- - CCT : numeric or array_like + CCT Correlated colour temperature :math:`T_{cp}`. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE xy* chromaticity coordinates. Raises @@ -150,22 +155,28 @@ def CCT_to_xy_Kang2002(CCT): CCT = as_float_array(CCT) if np.any(CCT[np.asarray(np.logical_or(CCT < 1667, CCT > 25000))]): - usage_warning(('Correlated colour temperature must be in domain ' - '[1667, 25000], unpredictable results may occur!')) + usage_warning( + "Correlated colour temperature must be in domain " + "[1667, 25000], unpredictable results may occur!" + ) - CCT_3 = CCT ** 3 - CCT_2 = CCT ** 2 + CCT_3 = CCT**3 + CCT_2 = CCT**2 x = np.where( CCT <= 4000, - -0.2661239 * 10 ** 9 / CCT_3 - 0.2343589 * 10 ** 6 / CCT_2 + - 0.8776956 * 10 ** 3 / CCT + 0.179910, - -3.0258469 * 10 ** 9 / CCT_3 + 2.1070379 * 10 ** 6 / CCT_2 + - 0.2226347 * 10 ** 3 / CCT + 0.24039, + -0.2661239 * 10**9 / CCT_3 + - 0.2343589 * 10**6 / CCT_2 + + 0.8776956 * 10**3 / CCT + + 0.179910, + -3.0258469 * 10**9 / CCT_3 + + 2.1070379 * 10**6 / CCT_2 + + 0.2226347 * 10**3 / CCT + + 0.24039, ) - x_3 = x ** 3 - x_2 = x ** 2 + x_3 = x**3 + x_2 = x**2 cnd_l = [CCT <= 2222, np.logical_and(CCT > 2222, CCT <= 4000), CCT > 4000] i = -1.1063814 * x_3 - 1.34811020 * x_2 + 2.18555832 * x - 0.20219683 @@ -173,6 +184,4 @@ def CCT_to_xy_Kang2002(CCT): k = 3.0817580 * x_3 - 5.8733867 * x_2 + 3.75112997 * x - 0.37001483 y = np.select(cnd_l, [i, j, k]) - xy = tstack([x, y]) - - return xy + return tstack([x, y]) diff --git a/colour/temperature/krystek1985.py b/colour/temperature/krystek1985.py index ebf6244b1c..0782eb48ff 100644 --- a/colour/temperature/krystek1985.py +++ b/colour/temperature/krystek1985.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Krystek (1985) Correlated Colour Temperature ============================================ -Defines *Krystek (1985)* correlated colour temperature :math:`T_{cp}` +Defines the *Krystek (1985)* correlated colour temperature :math:`T_{cp}` computations objects: - :func:`colour.temperature.uv_to_CCT_Krystek1985`: Correlated colour @@ -20,45 +19,52 @@ doi:10.1002/col.5080100109 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from scipy.optimize import minimize -from colour.utilities import as_float_array, as_numeric, tstack -from colour.utilities.deprecation import handle_arguments_deprecation - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['uv_to_CCT_Krystek1985', 'CCT_to_uv_Krystek1985'] - - -def uv_to_CCT_Krystek1985(uv, optimisation_kwargs=None, **kwargs): +from colour.hints import ( + ArrayLike, + Dict, + FloatingOrArrayLike, + FloatingOrNDArray, + NDArray, + Optional, +) +from colour.utilities import as_float_array, as_float, tstack + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "uv_to_CCT_Krystek1985", + "CCT_to_uv_Krystek1985", +] + + +def uv_to_CCT_Krystek1985( + uv: ArrayLike, optimisation_kwargs: Optional[Dict] = None +) -> FloatingOrNDArray: """ - Returns the correlated colour temperature :math:`T_{cp}` from given + Return the correlated colour temperature :math:`T_{cp}` from given *CIE UCS* colourspace *uv* chromaticity coordinates using *Krystek (1985)* method. Parameters ---------- - uv : array_like + uv *CIE UCS* colourspace *uv* chromaticity coordinates. - optimisation_kwargs : dict_like, optional + optimisation_kwargs Parameters for :func:`scipy.optimize.minimize` definition. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Correlated colour temperature :math:`T_{cp}`. Warnings @@ -85,57 +91,56 @@ def uv_to_CCT_Krystek1985(uv, optimisation_kwargs=None, **kwargs): 6504.3894290... """ - optimisation_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['optimisation_parameters', 'optimisation_kwargs'] - ], - }, **kwargs).get('optimisation_kwargs', optimisation_kwargs) - uv = as_float_array(uv) shape = uv.shape uv = np.atleast_1d(uv.reshape([-1, 2])) - def objective_function(CCT, uv): - """ - Objective function. - """ + def objective_function( + CCT: FloatingOrArrayLike, uv: ArrayLike + ) -> FloatingOrNDArray: + """Objective function.""" objective = np.linalg.norm(CCT_to_uv_Krystek1985(CCT) - uv) - return objective + return as_float(objective) optimisation_settings = { - 'method': 'Nelder-Mead', - 'options': { - 'fatol': 1e-10, + "method": "Nelder-Mead", + "options": { + "fatol": 1e-10, }, } if optimisation_kwargs is not None: optimisation_settings.update(optimisation_kwargs) - CCT = as_float_array([ - minimize( - objective_function, - x0=6500, - args=(uv_i, ), - **optimisation_settings).x for uv_i in uv - ]) + CCT = as_float_array( + [ + minimize( + objective_function, + x0=6500, + args=(uv_i,), + **optimisation_settings, + ).x + for uv_i in as_float_array(uv) + ] + ) - return as_numeric(CCT.reshape(shape[:-1])) + return as_float(np.reshape(CCT, shape[:-1])) -def CCT_to_uv_Krystek1985(CCT): +def CCT_to_uv_Krystek1985(CCT: FloatingOrArrayLike) -> NDArray: """ - Returns the *CIE UCS* colourspace *uv* chromaticity coordinates from given + Return the *CIE UCS* colourspace *uv* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}` using *Krystek (1985)* method. Parameters ---------- - CCT : array_like + CCT Correlated colour temperature :math:`T_{cp}`. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE UCS* colourspace *uv* chromaticity coordinates. Notes @@ -155,13 +160,13 @@ def CCT_to_uv_Krystek1985(CCT): T = as_float_array(CCT) - T_2 = T ** 2 + T_2 = T**2 u = ( - (0.860117757 + 1.54118254 * 10 ** -4 * T + 1.28641212 * 10 ** -7 * T_2) - / (1 + 8.42420235 * 10 ** -4 * T + 7.08145163 * 10 ** -7 * T_2)) + 0.860117757 + 1.54118254 * 10**-4 * T + 1.28641212 * 10**-7 * T_2 + ) / (1 + 8.42420235 * 10**-4 * T + 7.08145163 * 10**-7 * T_2) v = ( - (0.317398726 + 4.22806245 * 10 ** -5 * T + 4.20481691 * 10 ** -8 * T_2) - / (1 - 2.89741816 * 10 ** -5 * T + 1.61456053 * 10 ** -7 * T_2)) + 0.317398726 + 4.22806245 * 10**-5 * T + 4.20481691 * 10**-8 * T_2 + ) / (1 - 2.89741816 * 10**-5 * T + 1.61456053 * 10**-7 * T_2) return tstack([u, v]) diff --git a/colour/temperature/mccamy1992.py b/colour/temperature/mccamy1992.py index ff08847a8f..927381eab5 100644 --- a/colour/temperature/mccamy1992.py +++ b/colour/temperature/mccamy1992.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ McCamy (1992) Correlated Colour Temperature =========================================== -Defines *McCamy (1992)* correlated colour temperature :math:`T_{cp}` +Defines the *McCamy (1992)* correlated colour temperature :math:`T_{cp}` computations objects: - :func:`colour.temperature.xy_to_CCT_McCamy1992`: Correlated colour @@ -19,38 +18,48 @@ 28, 2014, from http://en.wikipedia.org/wiki/Color_temperature#Approximation """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from scipy.optimize import minimize from colour.colorimetry import CCS_ILLUMINANTS -from colour.utilities import as_float_array, as_numeric, tsplit, usage_warning -from colour.utilities.deprecation import handle_arguments_deprecation - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['xy_to_CCT_McCamy1992', 'CCT_to_xy_McCamy1992'] - - -def xy_to_CCT_McCamy1992(xy): +from colour.hints import ( + ArrayLike, + Dict, + FloatingOrArrayLike, + FloatingOrNDArray, + NDArray, + Optional, +) +from colour.utilities import as_float_array, as_float, tsplit, usage_warning + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "xy_to_CCT_McCamy1992", + "CCT_to_xy_McCamy1992", +] + + +def xy_to_CCT_McCamy1992(xy: ArrayLike) -> FloatingOrNDArray: """ - Returns the correlated colour temperature :math:`T_{cp}` from given + Return the correlated colour temperature :math:`T_{cp}` from given *CIE xy* chromaticity coordinates using *McCamy (1992)* method. Parameters ---------- - xy : array_like + xy *CIE xy* chromaticity coordinates. Returns ------- - numeric or ndarray + :class:`numpy.floating` or :class:`numpy.ndarray` Correlated colour temperature :math:`T_{cp}`. References @@ -68,31 +77,28 @@ def xy_to_CCT_McCamy1992(xy): x, y = tsplit(xy) n = (x - 0.3320) / (y - 0.1858) - CCT = -449 * n ** 3 + 3525 * n ** 2 - 6823.3 * n + 5520.33 + CCT = -449 * n**3 + 3525 * n**2 - 6823.3 * n + 5520.33 - return CCT + return as_float(CCT) -def CCT_to_xy_McCamy1992(CCT, optimisation_kwargs=None, **kwargs): +def CCT_to_xy_McCamy1992( + CCT: FloatingOrArrayLike, optimisation_kwargs: Optional[Dict] = None +) -> NDArray: """ - Returns the *CIE xy* chromaticity coordinates from given correlated colour + Return the *CIE xy* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}` using *McCamy (1992)* method. Parameters ---------- - CCT : numeric or array_like + CCT Correlated colour temperature :math:`T_{cp}`. - optimisation_kwargs : dict_like, optional + optimisation_kwargs Parameters for :func:`scipy.optimize.minimize` definition. - Other Parameters - ---------------- - \\**kwargs : dict, optional - Keywords arguments for deprecation management. - Returns ------- - ndarray + :class:`numpy.ndarray` *CIE xy* chromaticity coordinates. Warnings @@ -115,46 +121,51 @@ def CCT_to_xy_McCamy1992(CCT, optimisation_kwargs=None, **kwargs): array([ 0.3127..., 0.329...]) """ - optimisation_kwargs = handle_arguments_deprecation({ - 'ArgumentRenamed': [['optimisation_parameters', 'optimisation_kwargs'] - ], - }, **kwargs).get('optimisation_kwargs', optimisation_kwargs) - - usage_warning('"*McCamy (1992)" method for computing "CIE xy" ' - 'chromaticity coordinates from given correlated colour ' - 'temperature is not a bijective function and might produce ' - 'unexpected results. It is given for consistency with other ' - 'correlated colour temperature computation methods but ' - 'should be avoided for practical applications.') + usage_warning( + '"McCamy (1992)" method for computing "CIE xy" ' + "chromaticity coordinates from given correlated colour " + "temperature is not a bijective function and might produce " + "unexpected results. It is given for consistency with other " + "correlated colour temperature computation methods but " + "should be avoided for practical applications." + ) CCT = as_float_array(CCT) shape = list(CCT.shape) CCT = np.atleast_1d(CCT.reshape([-1, 1])) - def objective_function(xy, CCT): - """ - Objective function. - """ + def objective_function( + xy: ArrayLike, CCT: FloatingOrArrayLike + ) -> FloatingOrNDArray: + """Objective function.""" - objective = np.linalg.norm(xy_to_CCT_McCamy1992(xy) - CCT) + objective = np.linalg.norm( + xy_to_CCT_McCamy1992(xy) - as_float_array(CCT) + ) - return objective + return as_float(objective) optimisation_settings = { - 'method': 'Nelder-Mead', - 'options': { - 'fatol': 1e-10, + "method": "Nelder-Mead", + "options": { + "fatol": 1e-10, }, } if optimisation_kwargs is not None: optimisation_settings.update(optimisation_kwargs) - CCT = as_float_array([ - minimize( - objective_function, - x0=CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65'], - args=(CCT_i, ), - **optimisation_settings).x for CCT_i in CCT - ]) - - return as_numeric(CCT.reshape(shape + [2])) + xy = as_float_array( + [ + minimize( + objective_function, + x0=CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][ + "D65" + ], + args=(CCT_i,), + **optimisation_settings, + ).x + for CCT_i in as_float_array(CCT) + ] + ) + + return np.reshape(xy, (shape + [2])) diff --git a/colour/temperature/ohno2013.py b/colour/temperature/ohno2013.py index fb6a7a950f..6f831dddeb 100644 --- a/colour/temperature/ohno2013.py +++ b/colour/temperature/ohno2013.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ Ohno (2013) Correlated Colour Temperature ========================================= -Defines *Ohno (2013)* correlated colour temperature :math:`T_{cp}` computations -objects: +Defines the *Ohno (2013)* correlated colour temperature :math:`T_{cp}` +computations objects: - :func:`colour.temperature.uv_to_CCT_Ohno2013`: Correlated colour temperature :math:`T_{cp}` and :math:`\\Delta_{uv}` computation of given @@ -20,138 +19,176 @@ CCT and Duv. LEUKOS, 10(1), 47-55. doi:10.1080/15502724.2014.839020 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from collections import namedtuple - -from colour.colorimetry import (SPECTRAL_SHAPE_DEFAULT, - MSDS_CMFS_STANDARD_OBSERVER, sd_blackbody, - sd_to_XYZ) +from dataclasses import dataclass + +from colour.colorimetry import ( + MultiSpectralDistributions, + handle_spectral_arguments, + sd_blackbody, + sd_to_XYZ, +) +from colour.hints import ArrayLike, Floating, Integer, List, NDArray, Optional from colour.models import UCS_to_uv, XYZ_to_UCS -from colour.utilities import as_float_array, runtime_warning, tsplit - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.utilities import ( + as_float_array, + as_int_scalar, + runtime_warning, + tsplit, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'PLANCKIAN_TABLE_TUVD', 'CCT_MINIMAL', 'CCT_MAXIMAL', 'CCT_SAMPLES', - 'CCT_CALCULATION_ITERATIONS', 'planckian_table', - 'planckian_table_minimal_distance_index', 'uv_to_CCT_Ohno2013', - 'CCT_to_uv_Ohno2013' + "PlanckianTableRow", + "CCT_MINIMAL", + "CCT_MAXIMAL", + "CCT_SAMPLES", + "CCT_CALCULATION_ITERATIONS", + "planckian_table", + "planckian_table_minimal_distance_index", + "uv_to_CCT_Ohno2013", + "CCT_to_uv_Ohno2013", ] -PLANCKIAN_TABLE_TUVD = namedtuple('PlanckianTable_Tuvdi', - ('Ti', 'ui', 'vi', 'di')) -CCT_MINIMAL = 1000 -CCT_MAXIMAL = 100000 -CCT_SAMPLES = 10 -CCT_CALCULATION_ITERATIONS = 6 +@dataclass +class PlanckianTableRow: + """ + Define the data for a planckian table row at temperature :math:`T_i`. + Parameters + ---------- + Ti + Temperature :math:`T_i` in kelvin degrees. + ui + *u* chromaticity coordinate of the temperature :math:`T_i`. + vi + *v* chromaticity coordinate of the temperature :math:`T_i`. + di + Distance between the *uv* chromaticity coordinates or interest and + the *uv_i* chromaticity coordinates. + """ -def planckian_table(uv, cmfs, start, end, count): + Ti: Floating + ui: Floating + vi: Floating + di: Floating + + +CCT_MINIMAL: Floating = 1000 +CCT_MAXIMAL: Floating = 100000 +CCT_SAMPLES: Integer = 10 +CCT_CALCULATION_ITERATIONS: Integer = 6 + + +def planckian_table( + uv: ArrayLike, + cmfs: MultiSpectralDistributions, + start: Floating, + end: Floating, + count: Integer, +) -> List[PlanckianTableRow]: """ - Returns a planckian table from given *CIE UCS* colourspace *uv* + Return a planckian table from given *CIE UCS* colourspace *uv* chromaticity coordinates, colour matching functions and temperature range using *Ohno (2013)* method. Parameters ---------- - uv : array_like + uv *uv* chromaticity coordinates. - cmfs : XYZ_ColourMatchingFunctions + cmfs Standard observer colour matching functions. - start : numeric - Temperature range start in kelvins. - end : numeric - Temperature range end in kelvins. - count : int + start + Temperature range start in kelvin degrees. + end + Temperature range end in kelvin degrees. + count Temperatures count in the planckian table. Returns ------- - list + :class:`list` Planckian table. Examples -------- - >>> from colour.colorimetry import ( - ... SPECTRAL_SHAPE_DEFAULT, MSDS_CMFS_STANDARD_OBSERVER) - >>> from pprint import pprint + >>> from colour import MSDS_CMFS, SPECTRAL_SHAPE_DEFAULT >>> cmfs = ( - ... MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']. + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SPECTRAL_SHAPE_DEFAULT) ... ) >>> uv = np.array([0.1978, 0.3122]) >>> pprint(planckian_table(uv, cmfs, 1000, 1010, 10)) - ... # doctest: +ELLIPSIS - [PlanckianTable_Tuvdi(Ti=1000.0, \ -ui=0.4479628..., vi=0.3546296..., di=0.2537355...), - PlanckianTable_Tuvdi(Ti=1001.1111111..., \ -ui=0.4477030..., vi=0.3546521..., di=0.2534831...), - PlanckianTable_Tuvdi(Ti=1002.2222222..., \ -ui=0.4474434..., vi=0.3546746..., di=0.2532310...), - PlanckianTable_Tuvdi(Ti=1003.3333333..., \ -ui=0.4471842..., vi=0.3546970..., di=0.2529792...), - PlanckianTable_Tuvdi(Ti=1004.4444444..., \ -ui=0.4469252..., vi=0.3547194..., di=0.2527277...), - PlanckianTable_Tuvdi(Ti=1005.5555555..., \ -ui=0.4466666..., vi=0.3547417..., di=0.2524765...), - PlanckianTable_Tuvdi(Ti=1006.6666666..., \ -ui=0.4464083..., vi=0.3547640..., di=0.2522256...), - PlanckianTable_Tuvdi(Ti=1007.7777777..., \ -ui=0.4461502..., vi=0.3547862..., di=0.2519751...), - PlanckianTable_Tuvdi(Ti=1008.8888888..., \ -ui=0.4458925..., vi=0.3548084..., di=0.2517248...), - PlanckianTable_Tuvdi(Ti=1010.0, \ -ui=0.4456351..., vi=0.3548306..., di=0.2514749...)] + ... # doctest: +SKIP + [PlanckianTableRow(Ti=1000.0, ui=0.4479628..., \ +vi=0.3546296..., di=0.2537355...), + PlanckianTableRow(Ti=1001.1111111..., ui=0.4477030..., \ +vi=0.3546521..., di=0.2534831...), + PlanckianTableRow(Ti=1002.2222222..., ui=0.4474434..., \ +vi=0.3546746..., di=0.2532310...), + PlanckianTableRow(Ti=1003.3333333..., ui=0.4471842..., \ +vi=0.3546970..., di=0.2529792...), + PlanckianTableRow(Ti=1004.4444444..., ui=0.4469252..., \ +vi=0.3547194..., di=0.2527277...), + PlanckianTableRow(Ti=1005.5555555..., ui=0.4466666..., \ +vi=0.3547417..., di=0.2524765...), + PlanckianTableRow(Ti=1006.6666666..., ui=0.4464083..., \ +vi=0.3547640..., di=0.2522256...), + PlanckianTableRow(Ti=1007.7777777..., ui=0.4461502..., \ +vi=0.3547862..., di=0.2519751...), + PlanckianTableRow(Ti=1008.8888888..., ui=0.4458925..., \ +vi=0.3548084..., di=0.2517248...), + PlanckianTableRow(Ti=1010.0, ui=0.4456351..., \ +vi=0.3548306..., di=0.2514749...)] """ - ux, vx = uv - - cmfs = cmfs.copy().trim(SPECTRAL_SHAPE_DEFAULT) - - shape = cmfs.shape + ux, vx = tsplit(uv) table = [] for Ti in np.linspace(start, end, count): - sd = sd_blackbody(Ti, shape) + sd = sd_blackbody(Ti, cmfs.shape) XYZ = sd_to_XYZ(sd, cmfs) XYZ /= np.max(XYZ) UVW = XYZ_to_UCS(XYZ) ui, vi = UCS_to_uv(UVW) di = np.hypot(ux - ui, vx - vi) - table.append(PLANCKIAN_TABLE_TUVD(Ti, ui, vi, di)) + table.append(PlanckianTableRow(Ti, ui, vi, di)) return table -def planckian_table_minimal_distance_index(planckian_table_): +def planckian_table_minimal_distance_index( + planckian_table_: List[PlanckianTableRow], +) -> Integer: """ - Returns the shortest distance index in given planckian table using + Return the shortest distance index in given planckian table using *Ohno (2013)* method. Parameters ---------- - planckian_table_ : list + planckian_table_ Planckian table. Returns ------- - int + :class:`numpy.integer` Shortest distance index. Examples -------- - >>> from colour.colorimetry import ( - ... SPECTRAL_SHAPE_DEFAULT, MSDS_CMFS_STANDARD_OBSERVER) + >>> from colour import MSDS_CMFS, SPECTRAL_SHAPE_DEFAULT + >>> from colour.colorimetry import sd_to_XYZ_integration >>> cmfs = ( - ... MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']. + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SPECTRAL_SHAPE_DEFAULT) ... ) >>> uv = np.array([0.1978, 0.3122]) @@ -160,51 +197,55 @@ def planckian_table_minimal_distance_index(planckian_table_): 9 """ - distances = [x.di for x in planckian_table_] - return distances.index(min(distances)) + return as_int_scalar( + np.argmin(as_float_array([x.di for x in planckian_table_])) + ) def _uv_to_CCT_Ohno2013( - uv, - cmfs=MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer'] - .copy().trim(SPECTRAL_SHAPE_DEFAULT), - start=CCT_MINIMAL, - end=CCT_MAXIMAL, - count=CCT_SAMPLES, - iterations=CCT_CALCULATION_ITERATIONS): + uv: ArrayLike, + cmfs: Optional[MultiSpectralDistributions] = None, + start: Floating = CCT_MINIMAL, + end: Floating = CCT_MAXIMAL, + count: Integer = CCT_SAMPLES, + iterations: Integer = CCT_CALCULATION_ITERATIONS, +) -> NDArray: """ - Returns the correlated colour temperature :math:`T_{cp}` and + Return the correlated colour temperature :math:`T_{cp}` and :math:`\\Delta_{uv}` from given *CIE UCS* colourspace *uv* chromaticity coordinates, colour matching functions and temperature range using *Ohno (2013)* method. - The iterations parameter defines the calculations precision: The higher its - value, the more planckian tables will be generated through cascade - expansion in order to converge to the exact solution. + The ``iterations`` parameter defines the calculations' precision: The + higher its value, the more planckian tables will be generated through + cascade expansion in order to converge to the exact solution. Parameters ---------- - uv : array_like + uv *CIE UCS* colourspace *uv* chromaticity coordinates. - cmfs : XYZ_ColourMatchingFunctions, optional - Standard observer colour matching functions. - start : numeric, optional - Temperature range start in kelvins. - end : numeric, optional - Temperature range end in kelvins. - count : int, optional + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + start + Temperature range start in kelvin degrees. + end + Temperature range end in kelvin degrees. + count Temperatures count in the planckian tables. - iterations : int, optional + iterations Number of planckian tables to generate. Returns ------- - ndarray + :class:`numpy.ndarray` Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`. """ - # Ensuring we do at least one iteration to initialise variables. - iterations = max(iterations, 1) + cmfs, _illuminant = handle_spectral_arguments(cmfs) + + # Ensuring that we do at least one iteration to initialise the variables. + iterations = max(int(iterations), 1) # Planckian table creation through cascade expansion. for _i in range(iterations): @@ -212,19 +253,21 @@ def _uv_to_CCT_Ohno2013( index = planckian_table_minimal_distance_index(table) if index == 0: runtime_warning( - ('Minimal distance index is on lowest planckian table bound, ' - 'unpredictable results may occur!')) + "Minimal distance index is on lowest planckian table bound, " + "unpredictable results may occur!" + ) index += 1 elif index == len(table) - 1: runtime_warning( - ('Minimal distance index is on highest planckian table bound, ' - 'unpredictable results may occur!')) + "Minimal distance index is on highest planckian table bound, " + "unpredictable results may occur!" + ) index -= 1 start = table[index - 1].Ti end = table[index + 1].Ti - _ux, vx = uv + _ux, vx = tsplit(uv) Tuvdip, Tuvdi, Tuvdin = (table[index - 1], table[index], table[index + 1]) Tip, uip, vip, dip = Tuvdip.Ti, Tuvdip.ui, Tuvdip.vi, Tuvdip.di @@ -233,65 +276,78 @@ def _uv_to_CCT_Ohno2013( # Triangular solution. l = np.hypot(uin - uip, vin - vip) # noqa - x = (dip ** 2 - din ** 2 + l ** 2) / (2 * l) + x = (dip**2 - din**2 + l**2) / (2 * l) T = Tip + (Tin - Tip) * (x / l) vtx = vip + (vin - vip) * (x / l) sign = 1 if vx - vtx >= 0 else -1 - D_uv = (dip ** 2 - x ** 2) ** (1 / 2) * sign + D_uv = (dip**2 - x**2) ** (1 / 2) * sign # Parabolic solution. if np.abs(D_uv) >= 0.002: X = (Tin - Ti) * (Tip - Tin) * (Ti - Tip) - a = (Tip * (din - di) + Ti * (dip - din) + Tin * (di - dip)) * X ** -1 - b = (-(Tip ** 2 * (din - di) + Ti ** 2 * (dip - din) + Tin ** 2 * - (di - dip)) * X ** -1) + a = (Tip * (din - di) + Ti * (dip - din) + Tin * (di - dip)) * X**-1 + b = ( + -( + Tip**2 * (din - di) + + Ti**2 * (dip - din) + + Tin**2 * (di - dip) + ) + * X**-1 + ) c = ( - -(dip * (Tin - Ti) * Ti * Tin + di * - (Tip - Tin) * Tip * Tin + din * (Ti - Tip) * Tip * Ti) * X ** -1) + -( + dip * (Tin - Ti) * Ti * Tin + + di * (Tip - Tin) * Tip * Tin + + din * (Ti - Tip) * Tip * Ti + ) + * X**-1 + ) T = -b / (2 * a) - D_uv = sign * (a * T ** 2 + b * T + c) + D_uv = sign * (a * T**2 + b * T + c) return np.array([T, D_uv]) -def uv_to_CCT_Ohno2013(uv, - cmfs=MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'], - start=CCT_MINIMAL, - end=CCT_MAXIMAL, - count=CCT_SAMPLES, - iterations=CCT_CALCULATION_ITERATIONS): +def uv_to_CCT_Ohno2013( + uv: ArrayLike, + cmfs: Optional[MultiSpectralDistributions] = None, + start: Floating = CCT_MINIMAL, + end: Floating = CCT_MAXIMAL, + count: Integer = CCT_SAMPLES, + iterations: Integer = CCT_CALCULATION_ITERATIONS, +) -> NDArray: """ - Returns the correlated colour temperature :math:`T_{cp}` and + Return the correlated colour temperature :math:`T_{cp}` and :math:`\\Delta_{uv}` from given *CIE UCS* colourspace *uv* chromaticity coordinates, colour matching functions and temperature range using *Ohno (2013)* method. - The iterations parameter defines the calculations precision: The higher its - value, the more planckian tables will be generated through cascade - expansion in order to converge to the exact solution. + The ``iterations`` parameter defines the calculations' precision: The + higher its value, the more planckian tables will be generated through + cascade expansion in order to converge to the exact solution. Parameters ---------- - uv : array_like + uv *CIE UCS* colourspace *uv* chromaticity coordinates. - cmfs : XYZ_ColourMatchingFunctions, optional - Standard observer colour matching functions. - start : numeric, optional - Temperature range start in kelvins. - end : numeric, optional - Temperature range end in kelvins. - count : int, optional + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + start + Temperature range start in kelvin degrees. + end + Temperature range end in kelvin degrees. + count Temperatures count in the planckian tables. - iterations : int, optional + iterations Number of planckian tables to generate. Returns ------- - ndarray + :class:`numpy.ndarray` Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`. References @@ -300,16 +356,15 @@ def uv_to_CCT_Ohno2013(uv, Examples -------- - >>> from colour.colorimetry import ( - ... SPECTRAL_SHAPE_DEFAULT, MSDS_CMFS_STANDARD_OBSERVER) + >>> from pprint import pprint + >>> from colour import MSDS_CMFS, SPECTRAL_SHAPE_DEFAULT >>> cmfs = ( - ... MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']. + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SPECTRAL_SHAPE_DEFAULT) ... ) >>> uv = np.array([0.1978, 0.3122]) - >>> # Doctests skipping for Python 2.x compatibility. - >>> uv_to_CCT_Ohno2013(uv, cmfs) # doctest: +SKIP - array([ 6.5074738...e+03, 3.2233460...e-03]) + >>> uv_to_CCT_Ohno2013(uv, cmfs) # doctest: +ELLIPSIS + array([ 6.50747...e+03, 3.22334...e-03]) """ uv = as_float_array(uv) @@ -319,39 +374,38 @@ def uv_to_CCT_Ohno2013(uv, for a in np.reshape(uv, (-1, 2)) ] - return as_float_array(CCT_D_uv).reshape(uv.shape) + return np.reshape(as_float_array(CCT_D_uv), uv.shape) -def _CCT_to_uv_Ohno2013(CCT_D_uv, - cmfs=MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer']): +def _CCT_to_uv_Ohno2013( + CCT_D_uv: ArrayLike, cmfs: Optional[MultiSpectralDistributions] = None +) -> NDArray: """ - Returns the *CIE UCS* colourspace *uv* chromaticity coordinates from given + Return the *CIE UCS* colourspace *uv* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}` and colour matching functions using *Ohno (2013)* method. Parameters ---------- - CCT_D_uv : ndarray + CCT_D_uv Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`. - cmfs : XYZ_ColourMatchingFunctions, optional - Standard observer colour matching functions. + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE UCS* colourspace *uv* chromaticity coordinates. """ CCT, D_uv = tsplit(CCT_D_uv) - cmfs = cmfs.copy().trim(SPECTRAL_SHAPE_DEFAULT) - - shape = cmfs.shape + cmfs, _illuminant = handle_spectral_arguments(cmfs) delta = 0.01 - sd = sd_blackbody(CCT, shape) + sd = sd_blackbody(CCT, cmfs.shape) XYZ = sd_to_XYZ(sd, cmfs) XYZ *= 1 / np.max(XYZ) UVW = XYZ_to_UCS(XYZ) @@ -360,7 +414,7 @@ def _CCT_to_uv_Ohno2013(CCT_D_uv, if D_uv == 0: return np.array([u0, v0]) else: - sd = sd_blackbody(CCT + delta, shape) + sd = sd_blackbody(CCT + delta, cmfs.shape) XYZ = sd_to_XYZ(sd, cmfs) XYZ *= 1 / np.max(XYZ) UVW = XYZ_to_UCS(XYZ) @@ -375,24 +429,25 @@ def _CCT_to_uv_Ohno2013(CCT_D_uv, return np.array([u, v]) -def CCT_to_uv_Ohno2013(CCT_D_uv, - cmfs=MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer']): +def CCT_to_uv_Ohno2013( + CCT_D_uv: ArrayLike, cmfs: Optional[MultiSpectralDistributions] = None +) -> NDArray: """ - Returns the *CIE UCS* colourspace *uv* chromaticity coordinates from given + Return the *CIE UCS* colourspace *uv* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}` and colour matching functions using *Ohno (2013)* method. Parameters ---------- - CCT_D_uv : ndarray + CCT_D_uv Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`. - cmfs : XYZ_ColourMatchingFunctions, optional - Standard observer colour matching functions. + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE UCS* colourspace *uv* chromaticity coordinates. References @@ -401,10 +456,10 @@ def CCT_to_uv_Ohno2013(CCT_D_uv, Examples -------- - >>> from colour.colorimetry import ( - ... SPECTRAL_SHAPE_DEFAULT, MSDS_CMFS_STANDARD_OBSERVER) + >>> from pprint import pprint + >>> from colour import MSDS_CMFS, SPECTRAL_SHAPE_DEFAULT >>> cmfs = ( - ... MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']. + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SPECTRAL_SHAPE_DEFAULT) ... ) >>> CCT_D_uv = np.array([6507.4342201047066, 0.003223690901513]) @@ -416,4 +471,4 @@ def CCT_to_uv_Ohno2013(CCT_D_uv, uv = [_CCT_to_uv_Ohno2013(a, cmfs) for a in np.reshape(CCT_D_uv, (-1, 2))] - return as_float_array(uv).reshape(CCT_D_uv.shape) + return np.reshape(as_float_array(uv), CCT_D_uv.shape) diff --git a/colour/temperature/robertson1968.py b/colour/temperature/robertson1968.py index 7db4fa9800..425c11c40a 100644 --- a/colour/temperature/robertson1968.py +++ b/colour/temperature/robertson1968.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Robertson (1968) Correlated Colour Temperature ============================================== -Defines *Robertson (1968)* correlated colour temperature :math:`T_{cp}` +Defines the *Robertson (1968)* correlated colour temperature :math:`T_{cp}` computations objects: - :func:`colour.temperature.uv_to_CCT_Robertson1968`: Correlated colour @@ -34,28 +33,30 @@ Formulae (pp. 224-229). Wiley. ISBN:978-0-471-39918-6 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from collections import namedtuple +from dataclasses import dataclass +from colour.hints import ArrayLike, Floating, List, NDArray, Tuple from colour.utilities import as_float_array, tsplit -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'DATA_ISOTEMPERATURE_LINES_ROBERTSON1968', - 'RUVT_ISOTEMPERATURE_LINES_ROBERTSON1968', - 'ISOTEMPERATURE_LINES_ROBERTSON1968', 'uv_to_CCT_Robertson1968', - 'CCT_to_uv_Robertson1968' + "DATA_ISOTEMPERATURE_LINES_ROBERTSON1968", + "ISOTemperatureLine_Specification_Robertson1968", + "ISOTEMPERATURE_LINES_ROBERTSON1968", + "uv_to_CCT_Robertson1968", + "CCT_to_uv_Robertson1968", ] -DATA_ISOTEMPERATURE_LINES_ROBERTSON1968 = ( +DATA_ISOTEMPERATURE_LINES_ROBERTSON1968: Tuple = ( (0, 0.18006, 0.26352, -0.24341), (10, 0.18066, 0.26589, -0.25479), (20, 0.18133, 0.26846, -0.26876), @@ -75,7 +76,7 @@ (250, 0.22511, 0.33439, -1.4512), (275, 0.23247, 0.33904, -1.7298), (300, 0.24010, 0.34308, -2.0637), - (325, 0.24792, 0.34655, -2.4681), # 0.24702 ---> 0.24792 Bruce Lindbloom + (325, 0.24792, 0.34655, -2.4681), # 0.24702 --> 0.24792 Bruce Lindbloom (350, 0.25591, 0.34951, -2.9641), (375, 0.26400, 0.35200, -3.5814), (400, 0.27218, 0.35407, -4.3633), @@ -86,53 +87,77 @@ (525, 0.31320, 0.35968, -15.628), (550, 0.32129, 0.36011, -23.325), (575, 0.32931, 0.36038, -40.770), - (600, 0.33724, 0.36051, -116.45)) + (600, 0.33724, 0.36051, -116.45), +) """ -*Robertson (1968)* iso-temperature lines. +*Robertson (1968)* iso-temperature lines as a *tuple* as follows:: -DATA_ISOTEMPERATURE_LINES_ROBERTSON1968 : tuple - (Reciprocal Megakelvin, - CIE 1960 Chromaticity Coordinate *u*, - CIE 1960 Chromaticity Coordinate *v*, - Slope) + ( + ('Reciprocal Megakelvin', 'CIE 1960 Chromaticity Coordinate *u*', + 'CIE 1960 Chromaticity Coordinate *v*', 'Slope'), + ..., + ('Reciprocal Megakelvin', 'CIE 1960 Chromaticity Coordinate *u*', + 'CIE 1960 Chromaticity Coordinate *v*', 'Slope'), + ) Notes ----- - A correction has been done by Lindbloom for *325* Megakelvin - temperature: 0.24702 ---> 0.24792 + temperature: 0.24702 --> 0.24792 References ---------- :cite:`Wyszecki2000x` """ -RUVT_ISOTEMPERATURE_LINES_ROBERTSON1968 = namedtuple('WyszeckiRobertson_ruvt', - ('r', 'u', 'v', 't')) -ISOTEMPERATURE_LINES_ROBERTSON1968 = [ - RUVT_ISOTEMPERATURE_LINES_ROBERTSON1968(*x) +@dataclass +class ISOTemperatureLine_Specification_Robertson1968: + """ + Define the data for a *Roberston (1968)* iso-temperature line. + + Parameters + ---------- + r + Temperature :math:`r` in reciprocal mega-kelvin degrees. + u + *u* chromaticity coordinate of the temperature :math:`r`. + v + *v* chromaticity coordinate of the temperature :math:`r`. + t + Slope of the *v* chromaticity coordinate. + """ + + r: Floating + u: Floating + v: Floating + t: Floating + + +ISOTEMPERATURE_LINES_ROBERTSON1968: List = [ + ISOTemperatureLine_Specification_Robertson1968(*x) for x in DATA_ISOTEMPERATURE_LINES_ROBERTSON1968 ] -def _uv_to_CCT_Robertson1968(uv): +def _uv_to_CCT_Robertson1968(uv: ArrayLike) -> NDArray: """ - Returns the correlated colour temperature :math:`T_{cp}` and + Return the correlated colour temperature :math:`T_{cp}` and :math:`\\Delta_{uv}` from given *CIE UCS* colourspace *uv* chromaticity coordinates using *Roberston (1968)* method. Parameters ---------- - uv : array_like + uv *CIE UCS* colourspace *uv* chromaticity coordinates. Returns ------- - ndarray + :class:`numpy.ndarray` Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`. """ - u, v = uv + u, v = tsplit(uv) last_dt = last_dv = last_du = 0 @@ -185,20 +210,20 @@ def _uv_to_CCT_Robertson1968(uv): return np.array([T, -D_uv]) -def uv_to_CCT_Robertson1968(uv): +def uv_to_CCT_Robertson1968(uv: ArrayLike) -> NDArray: """ - Returns the correlated colour temperature :math:`T_{cp}` and + Return the correlated colour temperature :math:`T_{cp}` and :math:`\\Delta_{uv}` from given *CIE UCS* colourspace *uv* chromaticity coordinates using *Roberston (1968)* method. Parameters ---------- - uv : array_like + uv *CIE UCS* colourspace *uv* chromaticity coordinates. Returns ------- - ndarray + :class:`numpy.ndarray` Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`. References @@ -216,23 +241,23 @@ def uv_to_CCT_Robertson1968(uv): CCT_D_uv = [_uv_to_CCT_Robertson1968(a) for a in np.reshape(uv, (-1, 2))] - return as_float_array(CCT_D_uv).reshape(uv.shape) + return np.reshape(as_float_array(CCT_D_uv), uv.shape) -def _CCT_to_uv_Robertson1968(CCT_D_uv): +def _CCT_to_uv_Robertson1968(CCT_D_uv: ArrayLike) -> NDArray: """ - Returns the *CIE UCS* colourspace *uv* chromaticity coordinates from given + Return the *CIE UCS* colourspace *uv* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}` and :math:`\\Delta_{uv}` using *Roberston (1968)* method. Parameters ---------- - CCT_D_uv : ndarray + CCT_D_uv Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE UCS* colourspace *uv* chromaticity coordinates. """ @@ -240,6 +265,7 @@ def _CCT_to_uv_Robertson1968(CCT_D_uv): r = 1.0e6 / CCT + u, v = np.nan, np.nan for i in range(30): wr_ruvt = ISOTEMPERATURE_LINES_ROBERTSON1968[i] wr_ruvt_next = ISOTEMPERATURE_LINES_ROBERTSON1968[i + 1] @@ -273,23 +299,25 @@ def _CCT_to_uv_Robertson1968(CCT_D_uv): u += uu3 * -D_uv v += vv3 * -D_uv - return np.array([u, v]) + break + + return np.array([u, v]) -def CCT_to_uv_Robertson1968(CCT_D_uv): +def CCT_to_uv_Robertson1968(CCT_D_uv: ArrayLike) -> NDArray: """ - Returns the *CIE UCS* colourspace *uv* chromaticity coordinates from given + Return the *CIE UCS* colourspace *uv* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}` and :math:`\\Delta_{uv}` using *Roberston (1968)* method. Parameters ---------- - CCT_D_uv : ndarray + CCT_D_uv Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`. Returns ------- - ndarray + :class:`numpy.ndarray` *CIE UCS* colourspace *uv* chromaticity coordinates. References @@ -307,4 +335,4 @@ def CCT_to_uv_Robertson1968(CCT_D_uv): uv = [_CCT_to_uv_Robertson1968(a) for a in np.reshape(CCT_D_uv, (-1, 2))] - return as_float_array(uv).reshape(CCT_D_uv.shape) + return np.reshape(as_float_array(uv), CCT_D_uv.shape) diff --git a/colour/temperature/tests/__init__.py b/colour/temperature/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/temperature/tests/__init__.py +++ b/colour/temperature/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/temperature/tests/test_cie_d.py b/colour/temperature/tests/test_cie_d.py index 9568a4e963..2148f30424 100644 --- a/colour/temperature/tests/test_cie_d.py +++ b/colour/temperature/tests/test_cie_d.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.temperature.cie_d` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.temperature.cie_d` module.""" import numpy as np import unittest @@ -12,54 +7,61 @@ from colour.temperature import xy_to_CCT_CIE_D, CCT_to_xy_CIE_D from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestXy_to_CCT_CIE_D', 'TestCCT_to_xy_CIE_D'] +__all__ = [ + "TestXy_to_CCT_CIE_D", + "TestCCT_to_xy_CIE_D", +] class TestXy_to_CCT_CIE_D(unittest.TestCase): """ - Defines :func:`colour.temperature.cie_d.xy_to_CCT_CIE_D` definition units + Define :func:`colour.temperature.cie_d.xy_to_CCT_CIE_D` definition unit tests methods. """ def test_xy_to_CCT_CIE_D(self): - """ - Tests :func:`colour.temperature.cie_d.xy_to_CCT_CIE_D` definition. - """ + """Test :func:`colour.temperature.cie_d.xy_to_CCT_CIE_D` definition.""" np.testing.assert_allclose( xy_to_CCT_CIE_D( np.array([0.382343625000000, 0.383766261015578]), - {'method': 'Nelder-Mead'}), + {"method": "Nelder-Mead"}, + ), 4000, rtol=0.0000001, - atol=0.0000001) + atol=0.0000001, + ) np.testing.assert_allclose( xy_to_CCT_CIE_D( np.array([0.305357431486880, 0.321646345474552]), - {'method': 'Nelder-Mead'}), + {"method": "Nelder-Mead"}, + ), 7000, rtol=0.0000001, - atol=0.0000001) + atol=0.0000001, + ) np.testing.assert_allclose( xy_to_CCT_CIE_D( np.array([0.24985367, 0.254799464210944]), - {'method': 'Nelder-Mead'}), + {"method": "Nelder-Mead"}, + ), 25000, rtol=0.0000001, - atol=0.0000001) + atol=0.0000001, + ) def test_n_dimensional_xy_to_CCT_CIE_D(self): """ - Tests :func:`colour.temperature.cie_d.xy_to_CCT_CIE_D` definition + Test :func:`colour.temperature.cie_d.xy_to_CCT_CIE_D` definition n-dimensional arrays support. """ @@ -77,7 +79,7 @@ def test_n_dimensional_xy_to_CCT_CIE_D(self): @ignore_numpy_errors def test_nan_xy_to_CCT_CIE_D(self): """ - Tests :func:`colour.temperature.cie_d.xy_to_CCT_CIE_D` definition nan + Test :func:`colour.temperature.cie_d.xy_to_CCT_CIE_D` definition nan support. """ @@ -89,33 +91,34 @@ def test_nan_xy_to_CCT_CIE_D(self): class TestCCT_to_xy_CIE_D(unittest.TestCase): """ - Defines :func:`colour.temperature.cie_d.CCT_to_xy_CIE_D` definition + Define :func:`colour.temperature.cie_d.CCT_to_xy_CIE_D` definition unit tests methods. """ def test_CCT_to_xy_CIE_D(self): - """ - Tests :func:`colour.temperature.cie_d.CCT_to_xy_CIE_D` definition. - """ + """Test :func:`colour.temperature.cie_d.CCT_to_xy_CIE_D` definition.""" np.testing.assert_almost_equal( CCT_to_xy_CIE_D(4000), np.array([0.382343625000000, 0.383766261015578]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( CCT_to_xy_CIE_D(7000), np.array([0.305357431486880, 0.321646345474552]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( CCT_to_xy_CIE_D(25000), np.array([0.24985367, 0.254799464210944]), - decimal=7) + decimal=7, + ) def test_n_dimensional_CCT_to_xy_CIE_D(self): """ - Tests :func:`colour.temperature.cie_d.CCT_to_xy_CIE_D` definition + Test :func:`colour.temperature.cie_d.CCT_to_xy_CIE_D` definition n-dimensional arrays support. """ @@ -133,7 +136,7 @@ def test_n_dimensional_CCT_to_xy_CIE_D(self): @ignore_numpy_errors def test_nan_CCT_to_xy_CIE_D(self): """ - Tests :func:`colour.temperature.cie_d.CCT_to_xy_CIE_D` definition + Test :func:`colour.temperature.cie_d.CCT_to_xy_CIE_D` definition nan support. """ @@ -143,5 +146,5 @@ def test_nan_CCT_to_xy_CIE_D(self): CCT_to_xy_CIE_D(case) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/temperature/tests/test_hernandez1999.py b/colour/temperature/tests/test_hernandez1999.py index 2972a57f9b..dcd7fa1788 100644 --- a/colour/temperature/tests/test_hernandez1999.py +++ b/colour/temperature/tests/test_hernandez1999.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.temperature.hernandez1999` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.temperature.hernandez1999` module.""" import numpy as np import unittest @@ -12,47 +7,54 @@ from colour.temperature import xy_to_CCT_Hernandez1999, CCT_to_xy_Hernandez1999 from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['Testxy_to_CCT_Hernandez1999', 'TestCCT_to_xy_Hernandez1999'] +__all__ = [ + "Testxy_to_CCT_Hernandez1999", + "TestCCT_to_xy_Hernandez1999", +] class Testxy_to_CCT_Hernandez1999(unittest.TestCase): """ - Defines :func:`colour.temperature.hernandez1999.xy_to_CCT_Hernandez1999` + Define :func:`colour.temperature.hernandez1999.xy_to_CCT_Hernandez1999` definition unit tests methods. """ def test_xy_to_CCT_Hernandez1999(self): """ - Tests :func:`colour.temperature.hernandez1999.xy_to_CCT_McCamy1992` + Test :func:`colour.temperature.hernandez1999.xy_to_CCT_McCamy1992` definition. """ self.assertAlmostEqual( xy_to_CCT_Hernandez1999(np.array([0.31270, 0.32900])), 6500.74204318, - places=7) + places=7, + ) self.assertAlmostEqual( xy_to_CCT_Hernandez1999(np.array([0.44757, 0.40745])), 2790.64222533, - places=7) + places=7, + ) self.assertAlmostEqual( xy_to_CCT_Hernandez1999( - np.array([0.244162248213914, 0.240333674758318])), + np.array([0.244162248213914, 0.240333674758318]) + ), 64448.11092565, - places=7) + places=7, + ) def test_n_dimensional_xy_to_CCT_Hernandez1999(self): """ - Tests :func:`colour.temperature.hernandez1999.xy_to_CCT_Hernandez1999` + Test :func:`colour.temperature.hernandez1999.xy_to_CCT_Hernandez1999` definition n-dimensional arrays support. """ @@ -62,17 +64,19 @@ def test_n_dimensional_xy_to_CCT_Hernandez1999(self): xy = np.tile(xy, (6, 1)) CCT = np.tile(CCT, 6) np.testing.assert_almost_equal( - xy_to_CCT_Hernandez1999(xy), CCT, decimal=7) + xy_to_CCT_Hernandez1999(xy), CCT, decimal=7 + ) xy = np.reshape(xy, (2, 3, 2)) CCT = np.reshape(CCT, (2, 3)) np.testing.assert_almost_equal( - xy_to_CCT_Hernandez1999(xy), CCT, decimal=7) + xy_to_CCT_Hernandez1999(xy), CCT, decimal=7 + ) @ignore_numpy_errors def test_nan_xy_to_CCT_Hernandez1999(self): """ - Tests :func:`colour.temperature.hernandez1999.xy_to_CCT_Hernandez1999` + Test :func:`colour.temperature.hernandez1999.xy_to_CCT_Hernandez1999` definition nan support. """ @@ -84,34 +88,37 @@ def test_nan_xy_to_CCT_Hernandez1999(self): class TestCCT_to_xy_Hernandez1999(unittest.TestCase): """ - Defines :func:`colour.temperature.hernandez1999.CCT_to_xy_Hernandez1999` - definition units tests methods. + Define :func:`colour.temperature.hernandez1999.CCT_to_xy_Hernandez1999` + definition unit tests methods. """ def test_CCT_to_xy_Hernandez1999(self): """ - Tests :func:`colour.temperature.hernandez1999.CCT_to_xy_Hernandez1999` + Test :func:`colour.temperature.hernandez1999.CCT_to_xy_Hernandez1999` definition. """ np.testing.assert_almost_equal( - CCT_to_xy_Hernandez1999(6500.74204318, {'method': 'Nelder-Mead'}), + CCT_to_xy_Hernandez1999(6500.74204318, {"method": "Nelder-Mead"}), np.array([0.31269943, 0.32900373]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - CCT_to_xy_Hernandez1999(2790.64222533, {'method': 'Nelder-Mead'}), + CCT_to_xy_Hernandez1999(2790.64222533, {"method": "Nelder-Mead"}), np.array([0.42864308, 0.36754776]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - CCT_to_xy_Hernandez1999(64448.11092565, {'method': 'Nelder-Mead'}), + CCT_to_xy_Hernandez1999(64448.11092565, {"method": "Nelder-Mead"}), np.array([0.08269106, 0.36612620]), - decimal=7) + decimal=7, + ) def test_n_dimensional_CCT_to_xy_Hernandez1999(self): """ - Tests :func:`colour.temperature.hernandez1999.CCT_to_xy_Hernandez1999` + Test :func:`colour.temperature.hernandez1999.CCT_to_xy_Hernandez1999` definition n-dimensional arrays support. """ @@ -121,17 +128,19 @@ def test_n_dimensional_CCT_to_xy_Hernandez1999(self): CCT = np.tile(CCT, 6) xy = np.tile(xy, (6, 1)) np.testing.assert_almost_equal( - CCT_to_xy_Hernandez1999(CCT), xy, decimal=7) + CCT_to_xy_Hernandez1999(CCT), xy, decimal=7 + ) CCT = np.reshape(CCT, (2, 3)) xy = np.reshape(xy, (2, 3, 2)) np.testing.assert_almost_equal( - CCT_to_xy_Hernandez1999(CCT), xy, decimal=7) + CCT_to_xy_Hernandez1999(CCT), xy, decimal=7 + ) @ignore_numpy_errors def test_nan_CCT_to_xy_Hernandez1999(self): """ - Tests :func:`colour.temperature.hernandez1999.CCT_to_xy_Hernandez1999` + Test :func:`colour.temperature.hernandez1999.CCT_to_xy_Hernandez1999` definition nan support. """ @@ -141,5 +150,5 @@ def test_nan_CCT_to_xy_Hernandez1999(self): CCT_to_xy_Hernandez1999(case) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/temperature/tests/test_kang2002.py b/colour/temperature/tests/test_kang2002.py index 5b30465d7e..1e2b428608 100644 --- a/colour/temperature/tests/test_kang2002.py +++ b/colour/temperature/tests/test_kang2002.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.temperature.kang2002` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.temperature.kang2002` module.""" import numpy as np import unittest @@ -12,55 +7,64 @@ from colour.temperature import xy_to_CCT_Kang2002, CCT_to_xy_Kang2002 from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestXy_to_CCT_Kang2002', 'TestCCT_to_xy_Kang2002'] +__all__ = [ + "TestXy_to_CCT_Kang2002", + "TestCCT_to_xy_Kang2002", +] class TestXy_to_CCT_Kang2002(unittest.TestCase): """ - Defines :func:`colour.temperature.kang2002.xy_to_CCT_Kang2002` - definition units tests methods. + Define :func:`colour.temperature.kang2002.xy_to_CCT_Kang2002` + definition unit tests methods. """ def test_xy_to_CCT_Kang2002(self): """ - Tests :func:`colour.temperature.kang2002.xy_to_CCT_Kang2002` + Test :func:`colour.temperature.kang2002.xy_to_CCT_Kang2002` definition. """ np.testing.assert_allclose( xy_to_CCT_Kang2002( np.array([0.380528282812500, 0.376733530961114]), - {'method': 'Nelder-Mead'}), + {"method": "Nelder-Mead"}, + ), 4000, rtol=0.0000001, - atol=0.0000001) + atol=0.0000001, + ) np.testing.assert_allclose( xy_to_CCT_Kang2002( np.array([0.306374019533528, 0.316552869726577]), - {'method': 'Nelder-Mead'}), + {"method": "Nelder-Mead"}, + ), 7000, rtol=0.0000001, - atol=0.0000001) + atol=0.0000001, + ) np.testing.assert_allclose( xy_to_CCT_Kang2002( np.array([0.252472994438400, 0.252254791243654]), - {'method': 'Nelder-Mead'}), + {"method": "Nelder-Mead"}, + ), 25000, rtol=0.0000001, - atol=0.0000001) + atol=0.0000001, + ) def test_n_dimensional_xy_to_CCT_Kang2002(self): """ - Tests :func:`colour.temperature.kang2002.xy_to_CCT_Kang2002` + Test :func:`colour.temperature.kang2002.xy_to_CCT_Kang2002` definition n-dimensional arrays support. """ @@ -78,7 +82,7 @@ def test_n_dimensional_xy_to_CCT_Kang2002(self): @ignore_numpy_errors def test_nan_xy_to_CCT_Kang2002(self): """ - Tests :func:`colour.temperature.kang2002.xy_to_CCT_Kang2002` + Test :func:`colour.temperature.kang2002.xy_to_CCT_Kang2002` definition nan support. """ @@ -90,34 +94,37 @@ def test_nan_xy_to_CCT_Kang2002(self): class TestCCT_to_xy_Kang2002(unittest.TestCase): """ - Defines :func:`colour.temperature.kang2002.CCT_to_xy_Kang2002` definition - units tests methods. + Define :func:`colour.temperature.kang2002.CCT_to_xy_Kang2002` definition + unit tests methods. """ def test_CCT_to_xy_Kang2002(self): """ - Tests :func:`colour.temperature.kang2002.CCT_to_xy_Kang2002` + Test :func:`colour.temperature.kang2002.CCT_to_xy_Kang2002` definition. """ np.testing.assert_almost_equal( CCT_to_xy_Kang2002(4000), np.array([0.380528282812500, 0.376733530961114]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( CCT_to_xy_Kang2002(7000), np.array([0.306374019533528, 0.316552869726577]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( CCT_to_xy_Kang2002(25000), np.array([0.252472994438400, 0.252254791243654]), - decimal=7) + decimal=7, + ) def test_n_dimensional_CCT_to_xy_Kang2002(self): """ - Tests :func:`colour.temperature.kang2002.CCT_to_xy_Kang2002` definition + Test :func:`colour.temperature.kang2002.CCT_to_xy_Kang2002` definition n-dimensional arrays support. """ @@ -135,7 +142,7 @@ def test_n_dimensional_CCT_to_xy_Kang2002(self): @ignore_numpy_errors def test_nan_CCT_to_xy_Kang2002(self): """ - Tests :func:`colour.temperature.kang2002.CCT_to_xy_Kang2002` definition + Test :func:`colour.temperature.kang2002.CCT_to_xy_Kang2002` definition nan support. """ @@ -145,5 +152,5 @@ def test_nan_CCT_to_xy_Kang2002(self): CCT_to_xy_Kang2002(case) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/temperature/tests/test_krystek1985.py b/colour/temperature/tests/test_krystek1985.py index ee6aa0c26e..8d4f37a427 100644 --- a/colour/temperature/tests/test_krystek1985.py +++ b/colour/temperature/tests/test_krystek1985.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.temperature.krystek1985` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.temperature.krystek1985` module.""" import numpy as np import unittest @@ -12,55 +7,63 @@ from colour.temperature import uv_to_CCT_Krystek1985, CCT_to_uv_Krystek1985 from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestUv_to_CCT_Krystek1985'] +__all__ = [ + "TestUv_to_CCT_Krystek1985", +] class TestUv_to_CCT_Krystek1985(unittest.TestCase): """ - Defines :func:`colour.temperature.krystek1985.uv_to_CCT_Krystek1985` - definition units tests methods. + Define :func:`colour.temperature.krystek1985.uv_to_CCT_Krystek1985` + definition unit tests methods. """ def test_uv_to_CCT_Krystek1985(self): """ - Tests :func:`colour.temperature.krystek1985.uv_to_CCT_Krystek1985` + Test :func:`colour.temperature.krystek1985.uv_to_CCT_Krystek1985` definition. """ np.testing.assert_allclose( uv_to_CCT_Krystek1985( np.array([0.448087794140145, 0.354731965027727]), - {'method': 'Nelder-Mead'}), + {"method": "Nelder-Mead"}, + ), 1000, rtol=0.0000001, - atol=0.0000001) + atol=0.0000001, + ) np.testing.assert_allclose( uv_to_CCT_Krystek1985( np.array([0.198152565091092, 0.307023596915037]), - {'method': 'Nelder-Mead'}), + {"method": "Nelder-Mead"}, + ), 7000, rtol=0.0000001, - atol=0.0000001) + atol=0.0000001, + ) np.testing.assert_allclose( uv_to_CCT_Krystek1985( np.array([0.185675876767054, 0.282233658593898]), - {'method': 'Nelder-Mead'}), + {"method": "Nelder-Mead"}, + ), 15000, rtol=0.0000001, - atol=0.0000001) + atol=0.0000001, + ) def test_n_dimensional_uv_to_CCT_Krystek1985(self): """ - Tests :func:`colour.temperature.krystek1985.uv_to_CCT_Krystek1985` + Test :func:`colour.temperature.krystek1985.uv_to_CCT_Krystek1985` definition n-dimensional arrays support. """ @@ -70,17 +73,19 @@ def test_n_dimensional_uv_to_CCT_Krystek1985(self): uv = np.tile(uv, (6, 1)) CCT = np.tile(CCT, 6) np.testing.assert_almost_equal( - uv_to_CCT_Krystek1985(uv), CCT, decimal=7) + uv_to_CCT_Krystek1985(uv), CCT, decimal=7 + ) uv = np.reshape(uv, (2, 3, 2)) CCT = np.reshape(CCT, (2, 3)) np.testing.assert_almost_equal( - uv_to_CCT_Krystek1985(uv), CCT, decimal=7) + uv_to_CCT_Krystek1985(uv), CCT, decimal=7 + ) @ignore_numpy_errors def test_nan_uv_to_CCT_Krystek1985(self): """ - Tests :func:`colour.temperature.krystek1985.uv_to_CCT_Krystek1985` + Test :func:`colour.temperature.krystek1985.uv_to_CCT_Krystek1985` definition nan support. """ @@ -92,34 +97,37 @@ def test_nan_uv_to_CCT_Krystek1985(self): class TestCCT_to_uv_Krystek1985(unittest.TestCase): """ - Defines :func:`colour.temperature.krystek1985.CCT_to_uv_Krystek1985` - definition units tests methods. + Define :func:`colour.temperature.krystek1985.CCT_to_uv_Krystek1985` + definition unit tests methods. """ def test_CCT_to_uv_Krystek1985(self): """ - Tests :func:`colour.temperature.krystek1985.CCT_to_uv_Krystek1985` + Test :func:`colour.temperature.krystek1985.CCT_to_uv_Krystek1985` definition. """ np.testing.assert_almost_equal( CCT_to_uv_Krystek1985(1000), np.array([0.448087794140145, 0.354731965027727]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( CCT_to_uv_Krystek1985(7000), np.array([0.198152565091092, 0.307023596915037]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( CCT_to_uv_Krystek1985(15000), np.array([0.185675876767054, 0.282233658593898]), - decimal=7) + decimal=7, + ) def test_n_dimensional_CCT_to_uv_Krystek1985(self): """ - Tests :func:`colour.temperature.krystek1985.CCT_to_uv_Krystek1985` + Test :func:`colour.temperature.krystek1985.CCT_to_uv_Krystek1985` definition n-dimensional arrays support. """ @@ -129,17 +137,19 @@ def test_n_dimensional_CCT_to_uv_Krystek1985(self): CCT = np.tile(CCT, 6) uv = np.tile(uv, (6, 1)) np.testing.assert_almost_equal( - CCT_to_uv_Krystek1985(CCT), uv, decimal=7) + CCT_to_uv_Krystek1985(CCT), uv, decimal=7 + ) CCT = np.reshape(CCT, (2, 3)) uv = np.reshape(uv, (2, 3, 2)) np.testing.assert_almost_equal( - CCT_to_uv_Krystek1985(CCT), uv, decimal=7) + CCT_to_uv_Krystek1985(CCT), uv, decimal=7 + ) @ignore_numpy_errors def test_nan_CCT_to_uv_Krystek1985(self): """ - Tests :func:`colour.temperature.krystek1985.CCT_to_uv_Krystek1985` + Test :func:`colour.temperature.krystek1985.CCT_to_uv_Krystek1985` definition nan support. """ @@ -149,5 +159,5 @@ def test_nan_CCT_to_uv_Krystek1985(self): CCT_to_uv_Krystek1985(case) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/temperature/tests/test_mccamy1992.py b/colour/temperature/tests/test_mccamy1992.py index d2f1529d22..29ad2fa0c7 100644 --- a/colour/temperature/tests/test_mccamy1992.py +++ b/colour/temperature/tests/test_mccamy1992.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.temperature.mccamy1992` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.temperature.mccamy1992` module.""" import numpy as np import unittest @@ -12,47 +7,54 @@ from colour.temperature import xy_to_CCT_McCamy1992, CCT_to_xy_McCamy1992 from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['Testxy_to_CCT_McCamy1992', 'TestCCT_to_xy_McCamy1992'] +__all__ = [ + "Testxy_to_CCT_McCamy1992", + "TestCCT_to_xy_McCamy1992", +] class Testxy_to_CCT_McCamy1992(unittest.TestCase): """ - Defines :func:`colour.temperature.mccamy1992.xy_to_CCT_McCamy1992` + Define :func:`colour.temperature.mccamy1992.xy_to_CCT_McCamy1992` definition unit tests methods. """ def test_xy_to_CCT_McCamy1992(self): """ - Tests :func:`colour.temperature.mccamy1992.xy_to_CCT_McCamy1992` + Test :func:`colour.temperature.mccamy1992.xy_to_CCT_McCamy1992` definition. """ self.assertAlmostEqual( xy_to_CCT_McCamy1992(np.array([0.31270, 0.32900])), 6505.08059131, - places=7) + places=7, + ) self.assertAlmostEqual( xy_to_CCT_McCamy1992(np.array([0.44757, 0.40745])), 2857.28961266, - places=7) + places=7, + ) self.assertAlmostEqual( xy_to_CCT_McCamy1992( - np.array([0.252520939374083, 0.252220883926284])), + np.array([0.252520939374083, 0.252220883926284]) + ), 19501.61953130, - places=7) + places=7, + ) def test_n_dimensional_xy_to_CCT_McCamy1992(self): """ - Tests :func:`colour.temperature.mccamy1992.xy_to_CCT_McCamy1992` + Test :func:`colour.temperature.mccamy1992.xy_to_CCT_McCamy1992` definition n-dimensional arrays support. """ @@ -62,17 +64,19 @@ def test_n_dimensional_xy_to_CCT_McCamy1992(self): xy = np.tile(xy, (6, 1)) CCT = np.tile(CCT, 6) np.testing.assert_almost_equal( - xy_to_CCT_McCamy1992(xy), CCT, decimal=7) + xy_to_CCT_McCamy1992(xy), CCT, decimal=7 + ) xy = np.reshape(xy, (2, 3, 2)) CCT = np.reshape(CCT, (2, 3)) np.testing.assert_almost_equal( - xy_to_CCT_McCamy1992(xy), CCT, decimal=7) + xy_to_CCT_McCamy1992(xy), CCT, decimal=7 + ) @ignore_numpy_errors def test_nan_xy_to_CCT_McCamy1992(self): """ - Tests :func:`colour.temperature.mccamy1992.xy_to_CCT_McCamy1992` + Test :func:`colour.temperature.mccamy1992.xy_to_CCT_McCamy1992` definition nan support. """ @@ -84,34 +88,37 @@ def test_nan_xy_to_CCT_McCamy1992(self): class TestCCT_to_xy_McCamy1992(unittest.TestCase): """ - Defines :func:`colour.temperature.mccamy1992.CCT_to_xy_McCamy1992` - definition units tests methods. + Define :func:`colour.temperature.mccamy1992.CCT_to_xy_McCamy1992` + definition unit tests methods. """ def test_CCT_to_xy_McCamy1992(self): """ - Tests :func:`colour.temperature.mccamy1992.CCT_to_xy_McCamy1992` + Test :func:`colour.temperature.mccamy1992.CCT_to_xy_McCamy1992` definition. """ np.testing.assert_almost_equal( - CCT_to_xy_McCamy1992(6505.08059131, {'method': 'Nelder-Mead'}), + CCT_to_xy_McCamy1992(6505.08059131, {"method": "Nelder-Mead"}), np.array([0.31269945, 0.32900411]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - CCT_to_xy_McCamy1992(2857.28961266, {'method': 'Nelder-Mead'}), + CCT_to_xy_McCamy1992(2857.28961266, {"method": "Nelder-Mead"}), np.array([0.42350314, 0.36129253]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - CCT_to_xy_McCamy1992(19501.61953130, {'method': 'Nelder-Mead'}), + CCT_to_xy_McCamy1992(19501.61953130, {"method": "Nelder-Mead"}), np.array([0.11173782, 0.36987375]), - decimal=7) + decimal=7, + ) def test_n_dimensional_CCT_to_xy_McCamy1992(self): """ - Tests :func:`colour.temperature.mccamy1992.CCT_to_xy_McCamy1992` + Test :func:`colour.temperature.mccamy1992.CCT_to_xy_McCamy1992` definition n-dimensional arrays support. """ @@ -121,17 +128,19 @@ def test_n_dimensional_CCT_to_xy_McCamy1992(self): CCT = np.tile(CCT, 6) xy = np.tile(xy, (6, 1)) np.testing.assert_almost_equal( - CCT_to_xy_McCamy1992(CCT), xy, decimal=7) + CCT_to_xy_McCamy1992(CCT), xy, decimal=7 + ) CCT = np.reshape(CCT, (2, 3)) xy = np.reshape(xy, (2, 3, 2)) np.testing.assert_almost_equal( - CCT_to_xy_McCamy1992(CCT), xy, decimal=7) + CCT_to_xy_McCamy1992(CCT), xy, decimal=7 + ) @ignore_numpy_errors def test_nan_CCT_to_xy_McCamy1992(self): """ - Tests :func:`colour.temperature.mccamy1992.CCT_to_xy_McCamy1992` + Test :func:`colour.temperature.mccamy1992.CCT_to_xy_McCamy1992` definition nan support. """ @@ -141,5 +150,5 @@ def test_nan_CCT_to_xy_McCamy1992(self): CCT_to_xy_McCamy1992(case) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/temperature/tests/test_ohno2013.py b/colour/temperature/tests/test_ohno2013.py index 57324c4803..1440972056 100644 --- a/colour/temperature/tests/test_ohno2013.py +++ b/colour/temperature/tests/test_ohno2013.py @@ -1,118 +1,130 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.temperature.ohno2013` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.temperature.ohno2013` module.""" import numpy as np import unittest from itertools import permutations -from colour.colorimetry import MSDS_CMFS_STANDARD_OBSERVER +from colour.colorimetry import MSDS_CMFS from colour.temperature import CCT_to_uv_Ohno2013, uv_to_CCT_Ohno2013 from colour.temperature.ohno2013 import ( - planckian_table, planckian_table_minimal_distance_index) + planckian_table, + planckian_table_minimal_distance_index, +) from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestPlanckianTable', 'TestPlanckianTableMinimalDistanceIndex', - 'Testuv_to_CCT_Ohno2013', 'TestCCT_to_uv_Ohno2013' + "TestPlanckianTable", + "TestPlanckianTableMinimalDistanceIndex", + "Testuv_to_CCT_Ohno2013", + "TestCCT_to_uv_Ohno2013", ] -PLANCKIAN_TABLE = np.array([ - [1000.00000000, 0.44796288, 0.35462962, 0.25373557], - [1001.11111111, 0.44770303, 0.35465214, 0.25348315], - [1002.22222222, 0.44744348, 0.35467461, 0.25323104], - [1003.33333333, 0.44718423, 0.35469704, 0.25297924], - [1004.44444444, 0.44692529, 0.35471942, 0.25272774], - [1005.55555556, 0.44666666, 0.35474175, 0.25247656], - [1006.66666667, 0.44640833, 0.35476404, 0.25222569], - [1007.77777778, 0.44615030, 0.35478628, 0.25197512], - [1008.88888889, 0.44589258, 0.35480848, 0.25172487], - [1010.00000000, 0.44563516, 0.35483063, 0.25147492], -]) +PLANCKIAN_TABLE = np.array( + [ + [1000.00000000, 0.44796288, 0.35462962, 0.25373557], + [1001.11111111, 0.44770303, 0.35465214, 0.25348315], + [1002.22222222, 0.44744348, 0.35467461, 0.25323104], + [1003.33333333, 0.44718423, 0.35469704, 0.25297924], + [1004.44444444, 0.44692529, 0.35471942, 0.25272774], + [1005.55555556, 0.44666666, 0.35474175, 0.25247656], + [1006.66666667, 0.44640833, 0.35476404, 0.25222569], + [1007.77777778, 0.44615030, 0.35478628, 0.25197512], + [1008.88888889, 0.44589258, 0.35480848, 0.25172487], + [1010.00000000, 0.44563516, 0.35483063, 0.25147492], + ] +) class TestPlanckianTable(unittest.TestCase): """ - Defines :func:`colour.temperature.ohno2013.planckian_table` definition - units tests methods. + Define :func:`colour.temperature.ohno2013.planckian_table` definition + unit tests methods. """ def test_planckian_table(self): - """ - Tests :func:`colour.temperature.ohno2013.planckian_table` definition. - """ - - cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'] + """Test :func:`colour.temperature.ohno2013.planckian_table` definition.""" np.testing.assert_almost_equal( - [(x.Ti, x.ui, x.vi, x.di) for x in planckian_table( - np.array([0.1978, 0.3122]), cmfs, 1000, 1010, 10)], - PLANCKIAN_TABLE) + [ + (x.Ti, x.ui, x.vi, x.di) + for x in planckian_table( + np.array([0.1978, 0.3122]), + MSDS_CMFS["CIE 1931 2 Degree Standard Observer"], + 1000, + 1010, + 10, + ) + ], + PLANCKIAN_TABLE, + ) class TestPlanckianTableMinimalDistanceIndex(unittest.TestCase): """ - Defines :func:`colour.temperature.ohno2013.\ + Define :func:`colour.temperature.ohno2013.\ planckian_table_minimal_distance_index` definition unit tests methods. """ def test_planckian_table_minimal_distance_index(self): """ - Tests :func:`colour.temperature.ohno2013.\ + Test :func:`colour.temperature.ohno2013.\ planckian_table_minimal_distance_index` definition. """ - cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'] self.assertEqual( planckian_table_minimal_distance_index( planckian_table( - np.array([0.1978, 0.3122]), cmfs, 1000, 1010, 10)), 9) + np.array([0.1978, 0.3122]), + MSDS_CMFS["CIE 1931 2 Degree Standard Observer"], + 1000, + 1010, + 10, + ) + ), + 9, + ) class Testuv_to_CCT_Ohno2013(unittest.TestCase): """ - Defines :func:`colour.temperature.ohno2013.uv_to_CCT_Ohno2013` definition - units tests methods. + Define :func:`colour.temperature.ohno2013.uv_to_CCT_Ohno2013` definition + unit tests methods. """ def test_uv_to_CCT_Ohno2013(self): """ - Tests :func:`colour.temperature.ohno2013.uv_to_CCT_Ohno2013` + Test :func:`colour.temperature.ohno2013.uv_to_CCT_Ohno2013` definition. """ - cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'] np.testing.assert_almost_equal( - uv_to_CCT_Ohno2013(np.array([0.1978, 0.3122]), cmfs), + uv_to_CCT_Ohno2013(np.array([0.1978, 0.3122])), np.array([6507.47380460, 0.00322335]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - uv_to_CCT_Ohno2013(np.array([0.4328, 0.2883]), cmfs), + uv_to_CCT_Ohno2013(np.array([0.4328, 0.2883])), np.array([1041.68315360, -0.06737802]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - uv_to_CCT_Ohno2013(np.array([0.2927, 0.2722]), cmfs, iterations=4), + uv_to_CCT_Ohno2013(np.array([0.2927, 0.2722]), iterations=4), np.array([2452.15316417, -0.08437064]), - decimal=7) + decimal=7, + ) def test_n_dimensional_uv_to_CCT_Ohno2013(self): """ - Tests :func:`colour.temperature.ohno2013.uv_to_CCT_Ohno2013` definition + Test :func:`colour.temperature.ohno2013.uv_to_CCT_Ohno2013` definition n-dimensional arrays support. """ @@ -122,17 +134,19 @@ def test_n_dimensional_uv_to_CCT_Ohno2013(self): uv = np.tile(uv, (6, 1)) CCT_D_uv = np.tile(CCT_D_uv, (6, 1)) np.testing.assert_almost_equal( - uv_to_CCT_Ohno2013(uv), CCT_D_uv, decimal=7) + uv_to_CCT_Ohno2013(uv), CCT_D_uv, decimal=7 + ) uv = np.reshape(uv, (2, 3, 2)) CCT_D_uv = np.reshape(CCT_D_uv, (2, 3, 2)) np.testing.assert_almost_equal( - uv_to_CCT_Ohno2013(uv), CCT_D_uv, decimal=7) + uv_to_CCT_Ohno2013(uv), CCT_D_uv, decimal=7 + ) @ignore_numpy_errors def test_nan_uv_to_CCT_Ohno2013(self): """ - Tests :func:`colour.temperature.ohno2013.uv_to_CCT_Ohno2013` definition + Test :func:`colour.temperature.ohno2013.uv_to_CCT_Ohno2013` definition nan support. """ @@ -145,58 +159,59 @@ def test_nan_uv_to_CCT_Ohno2013(self): class TestCCT_to_uv_Ohno2013(unittest.TestCase): """ - Defines :func:`colour.temperature.ohno2013.CCT_to_uv_Ohno2013` definition - units tests methods. + Define :func:`colour.temperature.ohno2013.CCT_to_uv_Ohno2013` definition + unit tests methods. """ def test_CCT_to_uv_Ohno2013(self): """ - Tests :func:`colour.temperature.ohno2013.CCT_to_uv_Ohno2013` + Test :func:`colour.temperature.ohno2013.CCT_to_uv_Ohno2013` definition. """ - cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'] np.testing.assert_almost_equal( - CCT_to_uv_Ohno2013(np.array([6507.47380460, 0.00322335]), cmfs), + CCT_to_uv_Ohno2013(np.array([6507.47380460, 0.00322335])), np.array([0.19779997, 0.31219997]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - CCT_to_uv_Ohno2013(np.array([1041.68315360, -0.06737802]), cmfs), + CCT_to_uv_Ohno2013(np.array([1041.68315360, -0.06737802])), np.array([0.43279885, 0.28830013]), - decimal=7) + decimal=7, + ) np.testing.assert_almost_equal( - CCT_to_uv_Ohno2013(np.array([2452.15316417, -0.08437064]), cmfs), + CCT_to_uv_Ohno2013(np.array([2452.15316417, -0.08437064])), np.array([0.29247364, 0.27215157]), - decimal=7) + decimal=7, + ) def test_n_dimensional_CCT_to_uv_Ohno2013(self): """ - Tests :func:`colour.temperature.ohno2013.CCT_to_uv_Ohno2013` definition + Test :func:`colour.temperature.ohno2013.CCT_to_uv_Ohno2013` definition n-dimensional arrays support. """ - cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'] CCT_D_uv = np.array([6507.47380460, 0.00322335]) - uv = CCT_to_uv_Ohno2013(CCT_D_uv, cmfs) + uv = CCT_to_uv_Ohno2013(CCT_D_uv) CCT_D_uv = np.tile(CCT_D_uv, (6, 1)) uv = np.tile(uv, (6, 1)) np.testing.assert_almost_equal( - CCT_to_uv_Ohno2013(CCT_D_uv, cmfs), uv, decimal=7) + CCT_to_uv_Ohno2013(CCT_D_uv), uv, decimal=7 + ) CCT_D_uv = np.reshape(CCT_D_uv, (2, 3, 2)) uv = np.reshape(uv, (2, 3, 2)) np.testing.assert_almost_equal( - CCT_to_uv_Ohno2013(CCT_D_uv, cmfs), uv, decimal=7) + CCT_to_uv_Ohno2013(CCT_D_uv), uv, decimal=7 + ) @ignore_numpy_errors def test_nan_CCT_to_uv_Ohno2013(self): """ - Tests :func:`colour.temperature.ohno2013.CCT_to_uv_Ohno2013` definition + Test :func:`colour.temperature.ohno2013.CCT_to_uv_Ohno2013` definition nan support. """ @@ -207,5 +222,5 @@ def test_nan_CCT_to_uv_Ohno2013(self): CCT_to_uv_Ohno2013(CCT_D_uv) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/temperature/tests/test_robertson1968.py b/colour/temperature/tests/test_robertson1968.py index 75b9c4a69a..4cd23a4ba1 100644 --- a/colour/temperature/tests/test_robertson1968.py +++ b/colour/temperature/tests/test_robertson1968.py @@ -1,27 +1,28 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.temperature.robertson1968` module. -""" +"""Defines the unit tests for the :mod:`colour.temperature.robertson1968` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import unittest from itertools import permutations +from colour.hints import Dict from colour.temperature import CCT_to_uv_Robertson1968, uv_to_CCT_Robertson1968 from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['Testuv_to_CCT_Robertson1968', 'TestCCT_to_uv_Robertson1968'] +__all__ = [ + "Testuv_to_CCT_Robertson1968", + "TestCCT_to_uv_Robertson1968", +] -TEMPERATURE_DUV_TO_UV = { +TEMPERATURE_DUV_TO_UV: Dict = { (2000, -0.0500): np.array([0.309448284638118, 0.309263824757947]), (2000, -0.0250): np.array([0.307249142319059, 0.334166912378974]), (2000, 0.0000): np.array([0.305050000000000, 0.359070000000000]), @@ -121,29 +122,30 @@ (49500, -0.0250): np.array([0.205486370785934, 0.262018880883472]), (49500, 0.0000): np.array([0.181345151515151, 0.268515151515151]), (49500, 0.0250): np.array([0.157203932244369, 0.275011422146831]), - (49500, 0.0500): np.array([0.133062712973587, 0.281507692778510]) + (49500, 0.0500): np.array([0.133062712973587, 0.281507692778510]), } class Testuv_to_CCT_Robertson1968(unittest.TestCase): """ - Defines :func:`colour.temperature.robertson1968.uv_to_CCT_Robertson1968` + Define :func:`colour.temperature.robertson1968.uv_to_CCT_Robertson1968` definition unit tests methods. """ def test_uv_to_CCT_Robertson1968(self): """ - Tests :func:`colour.temperature.robertson1968.uv_to_CCT_Robertson1968` + Test :func:`colour.temperature.robertson1968.uv_to_CCT_Robertson1968` definition. """ for key, value in TEMPERATURE_DUV_TO_UV.items(): np.testing.assert_allclose( - uv_to_CCT_Robertson1968(value), key, atol=0.25) + uv_to_CCT_Robertson1968(value), key, atol=0.25 + ) def test_n_dimensional_uv_to_CCT_Robertson1968(self): """ - Tests :func:`colour.temperature.robertson1968.uv_to_CCT_Robertson1968` + Test :func:`colour.temperature.robertson1968.uv_to_CCT_Robertson1968` definition n-dimensional arrays support. """ @@ -153,17 +155,19 @@ def test_n_dimensional_uv_to_CCT_Robertson1968(self): uv = np.tile(uv, (6, 1)) CCT_D_uv = np.tile(CCT_D_uv, (6, 1)) np.testing.assert_almost_equal( - uv_to_CCT_Robertson1968(uv), CCT_D_uv, decimal=7) + uv_to_CCT_Robertson1968(uv), CCT_D_uv, decimal=7 + ) uv = np.reshape(uv, (2, 3, 2)) CCT_D_uv = np.reshape(CCT_D_uv, (2, 3, 2)) np.testing.assert_almost_equal( - uv_to_CCT_Robertson1968(uv), CCT_D_uv, decimal=7) + uv_to_CCT_Robertson1968(uv), CCT_D_uv, decimal=7 + ) @ignore_numpy_errors def test_nan_uv_to_CCT_Robertson1968(self): """ - Tests :func:`colour.temperature.robertson1968.uv_to_CCT_Robertson1968` + Test :func:`colour.temperature.robertson1968.uv_to_CCT_Robertson1968` definition nan support. """ @@ -176,23 +180,24 @@ def test_nan_uv_to_CCT_Robertson1968(self): class TestCCT_to_uv_Robertson1968(unittest.TestCase): """ - Defines :func:`colour.temperature.robertson1968.CCT_to_uv_Robertson1968` + Define :func:`colour.temperature.robertson1968.CCT_to_uv_Robertson1968` definition unit tests methods. """ def test_CCT_to_uv_Robertson1968(self): """ - Tests :func:`colour.temperature.robertson1968.CCT_to_uv_Robertson1968` + Test :func:`colour.temperature.robertson1968.CCT_to_uv_Robertson1968` definition. """ for key, value in TEMPERATURE_DUV_TO_UV.items(): np.testing.assert_almost_equal( - CCT_to_uv_Robertson1968(key), value, decimal=7) + CCT_to_uv_Robertson1968(key), value, decimal=7 + ) def test_n_dimensional_CCT_to_uv_Robertson1968(self): """ - Tests :func:`colour.temperature.robertson1968.CCT_to_uv_Robertson1968` + Test :func:`colour.temperature.robertson1968.CCT_to_uv_Robertson1968` definition n-dimensional arrays support. """ @@ -202,17 +207,19 @@ def test_n_dimensional_CCT_to_uv_Robertson1968(self): CCT_D_uv = np.tile(CCT_D_uv, (6, 1)) uv = np.tile(uv, (6, 1)) np.testing.assert_almost_equal( - CCT_to_uv_Robertson1968(CCT_D_uv), uv, decimal=7) + CCT_to_uv_Robertson1968(CCT_D_uv), uv, decimal=7 + ) CCT_D_uv = np.reshape(CCT_D_uv, (2, 3, 2)) uv = np.reshape(uv, (2, 3, 2)) np.testing.assert_almost_equal( - CCT_to_uv_Robertson1968(CCT_D_uv), uv, decimal=7) + CCT_to_uv_Robertson1968(CCT_D_uv), uv, decimal=7 + ) @ignore_numpy_errors def test_nan_CCT_to_uv_Robertson1968(self): """ - Tests :func:`colour.temperature.robertson1968.CCT_to_uv_Robertson1968` + Test :func:`colour.temperature.robertson1968.CCT_to_uv_Robertson1968` definition nan support. """ @@ -223,5 +230,5 @@ def test_nan_CCT_to_uv_Robertson1968(self): CCT_to_uv_Robertson1968(CCT_D_uv) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/utilities/__init__.py b/colour/utilities/__init__.py index 992a38fb7a..667985b323 100644 --- a/colour/utilities/__init__.py +++ b/colour/utilities/__init__.py @@ -1,106 +1,296 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import +from __future__ import annotations import sys -from .data_structures import (Lookup, Structure, CaseInsensitiveMapping, - LazyCaseInsensitiveMapping) +from colour.hints import Any, Dict + +from .data_structures import ( + Lookup, + Structure, + CaseInsensitiveMapping, + LazyCaseInsensitiveMapping, + Node, +) from .common import ( - handle_numpy_errors, ignore_numpy_errors, raise_numpy_errors, - print_numpy_errors, warn_numpy_errors, ignore_python_warnings, batch, - disable_multiprocessing, multiprocessing_pool, is_matplotlib_installed, - is_networkx_installed, is_openimageio_installed, is_pandas_installed, - is_tqdm_installed, required, is_iterable, is_string, is_numeric, - is_integer, is_sibling, filter_kwargs, filter_mapping, first_item, - get_domain_range_scale, set_domain_range_scale, domain_range_scale, - to_domain_1, to_domain_10, to_domain_100, to_domain_degrees, to_domain_int, - from_range_1, from_range_10, from_range_100, from_range_degrees, - from_range_int, copy_definition) + CacheRegistry, + CACHE_REGISTRY, + handle_numpy_errors, + ignore_numpy_errors, + raise_numpy_errors, + print_numpy_errors, + warn_numpy_errors, + ignore_python_warnings, + attest, + batch, + disable_multiprocessing, + multiprocessing_pool, + is_matplotlib_installed, + is_networkx_installed, + is_opencolorio_installed, + is_openimageio_installed, + is_pandas_installed, + is_sklearn_installed, + is_tqdm_installed, + is_trimesh_installed, + required, + is_iterable, + is_string, + is_numeric, + is_integer, + is_sibling, + filter_kwargs, + filter_mapping, + first_item, + copy_definition, + validate_method, + optional, +) from .verbose import ( - ColourWarning, ColourUsageWarning, ColourRuntimeWarning, message_box, - show_warning, warning, runtime_warning, usage_warning, filter_warnings, - suppress_warnings, numpy_print_options, ANCILLARY_COLOUR_SCIENCE_PACKAGES, - ANCILLARY_RUNTIME_PACKAGES, ANCILLARY_DEVELOPMENT_PACKAGES, - ANCILLARY_EXTRAS_PACKAGES, describe_environment) -from .array import (as_array, as_int_array, as_float_array, as_numeric, as_int, - as_float, set_float_precision, set_int_precision, - as_namedtuple, closest_indexes, closest, normalise_maximum, - interval, is_uniform, in_array, tstack, tsplit, - row_as_diagonal, vector_dot, matrix_dot, orient, centroid, - linear_conversion, lerp, fill_nan, ndarray_write, zeros, - ones, full, index_along_last_axis) + ColourWarning, + ColourUsageWarning, + ColourRuntimeWarning, + message_box, + show_warning, + warning, + runtime_warning, + usage_warning, + filter_warnings, + suppress_warnings, + numpy_print_options, + ANCILLARY_COLOUR_SCIENCE_PACKAGES, + ANCILLARY_RUNTIME_PACKAGES, + ANCILLARY_DEVELOPMENT_PACKAGES, + ANCILLARY_EXTRAS_PACKAGES, + describe_environment, +) +from .array import ( + MixinDataclassFields, + MixinDataclassIterable, + MixinDataclassArray, + MixinDataclassArithmetic, + as_array, + as_int, + as_float, + as_int_array, + as_float_array, + as_int_scalar, + as_float_scalar, + set_default_int_dtype, + set_default_float_dtype, + get_domain_range_scale, + set_domain_range_scale, + domain_range_scale, + to_domain_1, + to_domain_10, + to_domain_100, + to_domain_degrees, + to_domain_int, + from_range_1, + from_range_10, + from_range_100, + from_range_degrees, + from_range_int, + closest_indexes, + closest, + interval, + is_uniform, + in_array, + tstack, + tsplit, + row_as_diagonal, + orient, + centroid, + fill_nan, + has_only_nan, + ndarray_write, + zeros, + ones, + full, + index_along_last_axis, +) +from ..algebra.common import ( + normalise_maximum, + vector_dot, + matrix_dot, + linear_conversion, + linstep_function, +) from .metrics import metric_mse, metric_psnr from colour.utilities.deprecation import ModuleAPI, build_API_changes from colour.utilities.documentation import is_documentation_building __all__ = [ - 'Lookup', 'Structure', 'CaseInsensitiveMapping', - 'LazyCaseInsensitiveMapping' + "Lookup", + "Structure", + "CaseInsensitiveMapping", + "LazyCaseInsensitiveMapping", + "Node", +] +__all__ += [ + "CacheRegistry", + "CACHE_REGISTRY", + "handle_numpy_errors", + "ignore_numpy_errors", + "raise_numpy_errors", + "print_numpy_errors", + "warn_numpy_errors", + "ignore_python_warnings", + "attest", + "batch", + "disable_multiprocessing", + "multiprocessing_pool", + "is_matplotlib_installed", + "is_networkx_installed", + "is_opencolorio_installed", + "is_openimageio_installed", + "is_pandas_installed", + "is_sklearn_installed", + "is_tqdm_installed", + "is_trimesh_installed", + "required", + "is_iterable", + "is_string", + "is_numeric", + "is_integer", + "is_sibling", + "filter_kwargs", + "filter_mapping", + "first_item", + "copy_definition", + "validate_method", + "optional", ] __all__ += [ - 'handle_numpy_errors', 'ignore_numpy_errors', 'raise_numpy_errors', - 'print_numpy_errors', 'warn_numpy_errors', 'ignore_python_warnings', - 'batch', 'disable_multiprocessing', 'multiprocessing_pool', - 'is_matplotlib_installed', 'is_networkx_installed', - 'is_openimageio_installed', 'is_pandas_installed', 'is_tqdm_installed', - 'required', 'is_iterable', 'is_string', 'is_numeric', 'is_integer', - 'is_sibling', 'filter_kwargs', 'filter_mapping', 'first_item', - 'get_domain_range_scale', 'set_domain_range_scale', 'domain_range_scale', - 'to_domain_1', 'to_domain_10', 'to_domain_100', 'to_domain_degrees', - 'to_domain_int', 'from_range_1', 'from_range_10', 'from_range_100', - 'from_range_degrees', 'from_range_int', 'copy_definition' + "ColourWarning", + "ColourUsageWarning", + "ColourRuntimeWarning", + "message_box", + "show_warning", + "warning", + "runtime_warning", + "usage_warning", + "filter_warnings", + "suppress_warnings", + "numpy_print_options", + "ANCILLARY_COLOUR_SCIENCE_PACKAGES", + "ANCILLARY_RUNTIME_PACKAGES", + "ANCILLARY_DEVELOPMENT_PACKAGES", + "ANCILLARY_EXTRAS_PACKAGES", + "describe_environment", ] __all__ += [ - 'ColourWarning', 'ColourUsageWarning', 'ColourRuntimeWarning', - 'message_box', 'show_warning', 'warning', 'runtime_warning', - 'usage_warning', 'filter_warnings', 'suppress_warnings', - 'numpy_print_options', 'ANCILLARY_COLOUR_SCIENCE_PACKAGES', - 'ANCILLARY_RUNTIME_PACKAGES', 'ANCILLARY_DEVELOPMENT_PACKAGES', - 'ANCILLARY_EXTRAS_PACKAGES', 'describe_environment' + "MixinDataclassFields", + "MixinDataclassIterable", + "MixinDataclassArray", + "MixinDataclassArithmetic", + "as_array", + "as_int", + "as_float", + "as_int_array", + "as_float_array", + "as_int_scalar", + "as_float_scalar", + "set_default_int_dtype", + "set_default_float_dtype", + "get_domain_range_scale", + "set_domain_range_scale", + "domain_range_scale", + "to_domain_1", + "to_domain_10", + "to_domain_100", + "to_domain_degrees", + "to_domain_int", + "from_range_1", + "from_range_10", + "from_range_100", + "from_range_degrees", + "from_range_int", + "closest_indexes", + "closest", + "normalise_maximum", + "interval", + "is_uniform", + "in_array", + "tstack", + "tsplit", + "row_as_diagonal", + "vector_dot", + "matrix_dot", + "orient", + "centroid", + "linear_conversion", + "fill_nan", + "has_only_nan", + "linstep_function", + "ndarray_write", + "zeros", + "ones", + "full", + "index_along_last_axis", ] __all__ += [ - 'as_array', 'as_int_array', 'as_float_array', 'as_numeric', 'as_int', - 'as_float', 'set_float_precision', 'set_int_precision', 'as_namedtuple', - 'closest_indexes', 'closest', 'normalise_maximum', 'interval', - 'is_uniform', 'in_array', 'tstack', 'tsplit', 'row_as_diagonal', - 'vector_dot', 'matrix_dot', 'orient', 'centroid', 'linear_conversion', - 'fill_nan', 'lerp', 'ndarray_write', 'zeros', 'ones', 'full', - 'index_along_last_axis' + "metric_mse", + "metric_psnr", ] -__all__ += ['metric_mse', 'metric_psnr'] # ----------------------------------------------------------------------------# # --- API Changes and Deprecation Management ---# # ----------------------------------------------------------------------------# class utilities(ModuleAPI): - def __getattr__(self, attribute): - return super(utilities, self).__getattr__(attribute) + """Define a class acting like the *utilities* module.""" + def __getattr__(self, attribute) -> Any: + """Return the value from the attribute with given name.""" -# v0.3.16 -API_CHANGES = { - 'ObjectRenamed': [ + return super().__getattr__(attribute) + + +# v0.4.0 +API_CHANGES: Dict = { + "ObjectRenamed": [ + [ + "colour.utilities.set_int_precision", + "colour.utilities.set_default_int_dtype", + ], + [ + "colour.utilities.set_float_precision", + "colour.utilities.set_default_float_dtype", + ], + ], + "ObjectFutureAccessChange": [ + [ + "colour.utilities.linstep_function", + "colour.algebra.linstep_function", + ], + [ + "colour.utilities.linear_conversion", + "colour.algebra.linear_conversion", + ], + [ + "colour.utilities.matrix_dot", + "colour.algebra.matrix_dot", + ], [ - 'colour.utilities.dot_vector', - 'colour.utilities.vector_dot', + "colour.utilities.normalise_maximum", + "colour.algebra.normalise_maximum", ], [ - 'colour.utilities.dot_matrix', - 'colour.utilities.matrix_dot', + "colour.utilities.vector_dot", + "colour.algebra.vector_dot", ], - ] + ], } """ -Defines *colour.utilities* sub-package API changes. +Defines the *colour.utilities* sub-package API changes. -API_CHANGES : dict +API_CHANGES """ if not is_documentation_building(): - sys.modules['colour.utilities'] = utilities( - sys.modules['colour.utilities'], build_API_changes(API_CHANGES)) + sys.modules["colour.utilities"] = utilities( # type: ignore[assignment] + sys.modules["colour.utilities"], build_API_changes(API_CHANGES) + ) del ModuleAPI, is_documentation_building, build_API_changes, sys diff --git a/colour/utilities/array.py b/colour/utilities/array.py index 12e9ae3788..ea482ca537 100644 --- a/colour/utilities/array.py +++ b/colour/utilities/array.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Array Utilities =============== @@ -17,432 +16,1737 @@ http://stackoverflow.com/a/23521245/931625 """ -from __future__ import division, unicode_literals +from __future__ import annotations +import functools import numpy as np -import six import sys -try: # pragma: no cover - from collections import Mapping -except ImportError: # pragma: no cover - from collections.abc import Mapping - +from collections.abc import KeysView, ValuesView from contextlib import contextmanager +from dataclasses import fields, is_dataclass, replace +from operator import add, mul, pow, sub, truediv from colour.constants import DEFAULT_FLOAT_DTYPE, DEFAULT_INT_DTYPE, EPSILON -from colour.utilities import suppress_warnings - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import ( + Any, + ArrayLike, + Boolean, + Callable, + Dataclass, + DType, + DTypeBoolean, + DTypeFloating, + DTypeInteger, + DTypeNumber, + Floating, + FloatingOrArrayLike, + FloatingOrNDArray, + Generator, + Integer, + IntegerOrArrayLike, + IntegerOrNDArray, + Literal, + NDArray, + NestedSequence, + Number, + NumberOrArrayLike, + Optional, + Tuple, + Type, + Union, + cast, +) +from colour.utilities import ( + attest, + optional, + suppress_warnings, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'as_array', 'as_int_array', 'as_float_array', 'as_numeric', 'as_int', - 'as_float', 'set_float_precision', 'set_int_precision', 'as_namedtuple', - 'closest_indexes', 'closest', 'normalise_maximum', 'interval', - 'is_uniform', 'in_array', 'tstack', 'tsplit', 'row_as_diagonal', - 'vector_dot', 'matrix_dot', 'orient', 'centroid', 'linear_conversion', - 'lerp', 'fill_nan', 'ndarray_write', 'zeros', 'ones', 'full', - 'index_along_last_axis' + "MixinDataclassFields", + "MixinDataclassIterable", + "MixinDataclassArray", + "MixinDataclassArithmetic", + "as_array", + "as_int", + "as_float", + "as_int_array", + "as_float_array", + "as_int_scalar", + "as_float_scalar", + "set_default_int_dtype", + "set_default_float_dtype", + "get_domain_range_scale", + "set_domain_range_scale", + "domain_range_scale", + "to_domain_1", + "to_domain_10", + "to_domain_100", + "to_domain_degrees", + "to_domain_int", + "from_range_1", + "from_range_10", + "from_range_100", + "from_range_degrees", + "from_range_int", + "closest_indexes", + "closest", + "interval", + "is_uniform", + "in_array", + "tstack", + "tsplit", + "row_as_diagonal", + "orient", + "centroid", + "fill_nan", + "has_only_nan", + "ndarray_write", + "zeros", + "ones", + "full", + "index_along_last_axis", ] -def as_array(a, dtype=None): +class MixinDataclassFields: + """ + A mixin providing fields introspection for the :class:`dataclass`-like + class fields. + + Attributes + ---------- + - :meth:`~colour.utilities.MixinDataclassFields.fields` + """ + + @property + def fields(self) -> Tuple: + """ + Getter property for the fields of the :class:`dataclass`-like class. + + Returns + ------- + :class:`tuple` + Tuple of :class:`dataclass`-like class fields. + """ + + return fields(self) + + +class MixinDataclassIterable(MixinDataclassFields): + """ + A mixin providing iteration capabilities over the :class:`dataclass`-like + class fields. + + Attributes + ---------- + - :meth:`~colour.utilities.MixinDataclassIterable.keys` + - :meth:`~colour.utilities.MixinDataclassIterable.values` + - :meth:`~colour.utilities.MixinDataclassIterable.items` + + Methods + ------- + - :meth:`~colour.utilities.MixinDataclassIterable.__iter__` + + Notes + ----- + - The :class:`colour.utilities.MixinDataclassIterable` class inherits the + methods from the following class: + + - :class:`colour.utilities.MixinDataclassFields` + """ + + @property + def keys(self) -> Tuple: + """ + Getter property for the :class:`dataclass`-like class keys, i.e. the + field names. + + Returns + ------- + :class:`tuple` + :class:`dataclass`-like class keys. + """ + + return tuple(field for field, _value in self) + + @property + def values(self) -> Tuple: + """ + Getter property for the :class:`dataclass`-like class values, i.e. the + field values. + + Returns + ------- + :class:`tuple` + :class:`dataclass`-like class values. + """ + + return tuple(value for _field, value in self) + + @property + def items(self) -> Tuple: + """ + Getter property for the :class:`dataclass`-like class items, i.e. the + field names and values. + + Returns + ------- + :class:`tuple` + :class:`dataclass`-like class items. + """ + + return tuple((field, value) for field, value in self) + + def __iter__(self) -> Generator: + """ + Return a generator for the :class:`dataclass`-like class fields. + + Yields + ------ + Generator + :class:`dataclass`-like class field generator. + """ + + yield from { + field.name: getattr(self, field.name) for field in self.fields + }.items() + + +class MixinDataclassArray(MixinDataclassIterable): + """ + A mixin providing conversion methods for :class:`dataclass`-like class + conversion to :class:`numpy.ndarray` class. + + Methods + ------- + - :meth:`~colour.utilities.MixinDataclassArray.__array__` + + Notes + ----- + - The :class:`colour.utilities.MixinDataclassArray` class inherits the + methods from the following classes: + + - :class:`colour.utilities.MixinDataclassIterable` + - :class:`colour.utilities.MixinDataclassFields` + """ + + def __array__(self, dtype: Optional[Type[DTypeNumber]] = None) -> NDArray: + """ + Implement support for :class:`dataclass`-like class conversion to + :class:`numpy.ndarray` class. + + A field set to *None* will be filled with `np.nan` according to the + shape of the first field not set with *None*. + + Parameters + ---------- + dtype + :class:`numpy.dtype` to use for conversion to `np.ndarray`, default + to the :class:`numpy.dtype` defined by + :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. + + Returns + ------- + :class:`numpy.ndarray` + :class:`dataclass`-like class converted to :class:`numpy.ndarray`. + """ + + dtype = cast(Type[DTypeNumber], optional(dtype, DEFAULT_FLOAT_DTYPE)) + + default = None + for _field, value in self: + if value is not None: + default = full(as_float_array(value).shape, np.nan) + break + + return tstack( + [value if value is not None else default for value in self.values], + dtype=dtype, + ) + + +class MixinDataclassArithmetic(MixinDataclassArray): + """ + A mixin providing mathematical operations for :class:`dataclass`-like + class. + + Methods + ------- + - :meth:`~colour.utilities.MixinDataclassArray.__iadd__` + - :meth:`~colour.utilities.MixinDataclassArray.__add__` + - :meth:`~colour.utilities.MixinDataclassArray.__isub__` + - :meth:`~colour.utilities.MixinDataclassArray.__sub__` + - :meth:`~colour.utilities.MixinDataclassArray.__imul__` + - :meth:`~colour.utilities.MixinDataclassArray.__mul__` + - :meth:`~colour.utilities.MixinDataclassArray.__idiv__` + - :meth:`~colour.utilities.MixinDataclassArray.__div__` + - :meth:`~colour.utilities.MixinDataclassArray.__ipow__` + - :meth:`~colour.utilities.MixinDataclassArray.__pow__` + - :meth:`~colour.utilities.MixinDataclassArray.arithmetical_operation` + + Notes + ----- + - The :class:`colour.utilities.MixinDataclassArithmetic` class inherits + the methods from the following classes: + + - :class:`colour.utilities.MixinDataclassArray` + - :class:`colour.utilities.MixinDataclassIterable` + - :class:`colour.utilities.MixinDataclassFields` + """ + + def __add__(self, a: Any) -> Dataclass: + """ + Implement support for addition. + + Parameters + ---------- + a + Variable :math:`a` to add. + + Returns + ------- + :class:`dataclass` + Variable added :class:`dataclass`-like class. + """ + + return self.arithmetical_operation(a, "+") + + def __iadd__(self, a: Any) -> Dataclass: + """ + Implement support for in-place addition. + + Parameters + ---------- + a + Variable :math:`a` to add in-place. + + Returns + ------- + :class:`dataclass` + In-place variable added :class:`dataclass`-like class. + """ + + return self.arithmetical_operation(a, "+", True) + + def __sub__(self, a: Any) -> Dataclass: + """ + Implement support for subtraction. + + Parameters + ---------- + a + Variable :math:`a` to subtract. + + Returns + ------- + :class:`dataclass` + Variable subtracted :class:`dataclass`-like class. + """ + + return self.arithmetical_operation(a, "-") + + def __isub__(self, a: Any) -> Dataclass: + """ + Implement support for in-place subtraction. + + Parameters + ---------- + a + Variable :math:`a` to subtract in-place. + + Returns + ------- + :class:`dataclass` + In-place variable subtracted :class:`dataclass`-like class. + """ + + return self.arithmetical_operation(a, "-", True) + + def __mul__(self, a: Any) -> Dataclass: + """ + Implement support for multiplication. + + Parameters + ---------- + a + Variable :math:`a` to multiply by. + + Returns + ------- + :class:`dataclass` + Variable multiplied :class:`dataclass`-like class. + """ + + return self.arithmetical_operation(a, "*") + + def __imul__(self, a: Any) -> Dataclass: + """ + Implement support for in-place multiplication. + + Parameters + ---------- + a + Variable :math:`a` to multiply by in-place. + + Returns + ------- + :class:`dataclass` + In-place variable multiplied :class:`dataclass`-like class. + """ + + return self.arithmetical_operation(a, "*", True) + + def __div__(self, a: Any) -> Dataclass: + """ + Implement support for division. + + Parameters + ---------- + a + Variable :math:`a` to divide by. + + Returns + ------- + :class:`dataclass` + Variable divided :class:`dataclass`-like class. + """ + + return self.arithmetical_operation(a, "/") + + def __idiv__(self, a: Any) -> Dataclass: + """ + Implement support for in-place division. + + Parameters + ---------- + a + Variable :math:`a` to divide by in-place. + + Returns + ------- + :class:`dataclass` + In-place variable divided :class:`dataclass`-like class. + """ + + return self.arithmetical_operation(a, "/", True) + + __itruediv__ = __idiv__ + __truediv__ = __div__ + + def __pow__(self, a: Any) -> Dataclass: + """ + Implement support for exponentiation. + + Parameters + ---------- + a + Variable :math:`a` to exponentiate by. + + Returns + ------- + :class:`dataclass` + Variable exponentiated :class:`dataclass`-like class. + """ + + return self.arithmetical_operation(a, "**") + + def __ipow__(self, a: Any) -> Dataclass: + """ + Implement support for in-place exponentiation. + + Parameters + ---------- + a + Variable :math:`a` to exponentiate by in-place. + + Returns + ------- + :class:`dataclass` + In-place variable exponentiated :class:`dataclass`-like class. + """ + + return self.arithmetical_operation(a, "**", True) + + def arithmetical_operation( + self, a: Any, operation: str, in_place: Boolean = False + ) -> Dataclass: + """ + Perform given arithmetical operation with :math:`a` operand on the + :class:`dataclass`-like class. + + Parameters + ---------- + a + Operand. + operation + Operation to perform. + in_place + Operation happens in place. + + Returns + ------- + :class:`dataclass` + :class:`dataclass`-like class with arithmetical operation + performed. + """ + + callable_operation = { + "+": add, + "-": sub, + "*": mul, + "/": truediv, + "**": pow, + }[operation] + + if is_dataclass(a): + a = as_float_array(a) + + values = tsplit(callable_operation(as_float_array(self), a)) + field_values = {field: values[i] for i, field in enumerate(self.keys)} + field_values.update( + {field: None for field, value in self if value is None} + ) + + dataclass = replace(self, **field_values) + + if in_place: + for field in self.keys: + setattr(self, field, getattr(dataclass, field)) + return self + else: + return dataclass + + +def as_array( + a: Union[NumberOrArrayLike, NestedSequence[NumberOrArrayLike]], + dtype: Optional[Type[DType]] = None, +) -> NDArray: + """ + Convert given variable :math:`a` to :class:`numpy.ndarray` using given + :class:`numpy.dtype`. + + Parameters + ---------- + a + Variable to convert. + dtype + :class:`numpy.dtype` to use for conversion, default to the + :class:`numpy.dtype` defined by the + :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. + + Returns + ------- + :class:`numpy.ndarray` + Variable :math:`a` converted to :class:`numpy.ndarray`. + + Examples + -------- + >>> as_array([1, 2, 3]) # doctest: +ELLIPSIS + array([1, 2, 3]...) + >>> as_array([1, 2, 3], dtype=DEFAULT_FLOAT_DTYPE) + array([ 1., 2., 3.]) + """ + + # TODO: Remove when https://github.com/numpy/numpy/issues/5718 is + # addressed. + if isinstance(a, (KeysView, ValuesView)): + a = list(a) + + return np.asarray(a, dtype) + + +def as_int( + a: NumberOrArrayLike, dtype: Optional[Type[DTypeInteger]] = None +) -> IntegerOrNDArray: + """ + Attempt to convert given variable :math:`a` to :class:`numpy.integer` + using given :class:`numpy.dtype`. If variable :math:`a` is not a scalar or + 0-dimensional, it is converted to :class:`numpy.ndarray`. + + Parameters + ---------- + a + Variable to convert. + dtype + :class:`numpy.dtype` to use for conversion, default to the + :class:`numpy.dtype` defined by the + :attr:`colour.constant.DEFAULT_INT_DTYPE` attribute. + + Returns + ------- + :class:`numpy.integer` or :class:`numpy.ndarray` + Variable :math:`a` converted to :class:`numpy.integer`. + + Examples + -------- + >>> as_int(np.array([1])) + 1 + >>> as_int(np.arange(10)) # doctest: +ELLIPSIS + array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]...) + """ + + dtype = cast(Type[DTypeInteger], optional(dtype, DEFAULT_INT_DTYPE)) + + args = getattr(DTypeInteger, "__args__") + attest( + dtype in args, + f'"dtype" must be one of the following types: {args}', + ) + + # TODO: Reassess implementation when and if + # https://github.com/numpy/numpy/issues/11956 is addressed. + return dtype(np.squeeze(a)) # type: ignore[return-value] + + +def as_float( + a: NumberOrArrayLike, dtype: Optional[Type[DTypeFloating]] = None +) -> FloatingOrNDArray: + """ + Attempt to convert given variable :math:`a` to :class:`numpy.floating` + using given :class:`numpy.dtype`. If variable :math:`a` is not a scalar or + 0-dimensional, it is converted to :class:`numpy.ndarray`. + + Parameters + ---------- + a + Variable to convert. + dtype + :class:`numpy.dtype` to use for conversion, default to the + :class:`numpy.dtype` defined by the + :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. + + Returns + ------- + :class:`numpy.floating` or :class:`numpy.ndarray` + Variable :math:`a` converted to :class:`numpy.floating`. + + Examples + -------- + >>> as_float(np.array([1])) + 1.0 + >>> as_float(np.arange(10)) + array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]) + """ + + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) + + args = getattr(DTypeFloating, "__args__") + attest( + dtype in args, + f'"dtype" must be one of the following types: {args}', + ) + + return dtype(a) # type: ignore[arg-type, return-value] + + +def as_int_array( + a: NumberOrArrayLike, dtype: Optional[Type[DTypeInteger]] = None +) -> NDArray: + """ + Convert given variable :math:`a` to :class:`numpy.ndarray` using given + :class:`numpy.dtype`. + + Parameters + ---------- + a + Variable to convert. + dtype + :class:`numpy.dtype` to use for conversion, default to the + :class:`numpy.dtype` defined by the + :attr:`colour.constant.DEFAULT_INT_DTYPE` attribute. + + Returns + ------- + :class:`numpy.ndarray` + Variable :math:`a` converted to :class:`numpy.ndarray`. + + Examples + -------- + >>> as_int_array([1.0, 2.0, 3.0]) # doctest: +ELLIPSIS + array([1, 2, 3]...) + """ + + dtype = cast(Type[DTypeInteger], optional(dtype, DEFAULT_INT_DTYPE)) + + args = getattr(DTypeInteger, "__args__") + attest( + dtype in args, + f'"dtype" must be one of the following types: {args}', + ) + + return as_array(a, dtype) + + +def as_float_array( + a: NumberOrArrayLike, dtype: Optional[Type[DTypeFloating]] = None +) -> NDArray: + """ + Convert given variable :math:`a` to :class:`numpy.ndarray` using given + :class:`numpy.dtype`. + + Parameters + ---------- + a + Variable to convert. + dtype + :class:`numpy.dtype` to use for conversion, default to the + :class:`numpy.dtype` defined by the + :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. + + Returns + ------- + :class:`numpy.ndarray` + Variable :math:`a` converted to :class:`numpy.ndarray`. + + Examples + -------- + >>> as_float_array([1, 2, 3]) + array([ 1., 2., 3.]) + """ + + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) + + attest( + dtype in np.sctypes["float"], + f"\"dtype\" must be one of the following types: {np.sctypes['float']}", + ) + + return as_array(a, dtype) + + +def as_int_scalar( + a: NumberOrArrayLike, dtype: Optional[Type[DTypeInteger]] = None +) -> Integer: + """ + Convert given :math:`a` variable to :class:`numpy.integer` using given + :class:`numpy.dtype`. + + Parameters + ---------- + a + Variable to convert. + dtype + :class:`numpy.dtype` to use for conversion, default to the + :class:`numpy.dtype` defined by the + :attr:`colour.constant.DEFAULT_INT_DTYPE` attribute. + + Returns + ------- + :class:`numpy.integer` + :math:`a` variable converted to :class:`numpy.integer`. + + Examples + -------- + >>> as_int_scalar(np.array(1)) + 1 + """ + + a = np.squeeze(as_int_array(a, dtype)) + + attest(a.ndim == 0, f'"{a}" cannot be converted to "int" scalar!') + + return cast(Integer, as_int(a, dtype)) + + +def as_float_scalar( + a: NumberOrArrayLike, dtype: Optional[Type[DTypeFloating]] = None +) -> Floating: + """ + Convert given :math:`a` variable to :class:`numpy.floating` using given + :class:`numpy.dtype`. + + Parameters + ---------- + a + Variable to convert. + dtype + :class:`numpy.dtype` to use for conversion, default to the + :class:`numpy.dtype` defined by the + :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. + + Returns + ------- + :class:`numpy.floating` + :math:`a` variable converted to :class:`numpy.floating`. + + Examples + -------- + >>> as_float_scalar(np.array(1)) + 1.0 + """ + + a = np.squeeze(as_float_array(a, dtype)) + + attest(a.ndim == 0, f'"{a}" cannot be converted to "float" scalar!') + + return cast(Floating, as_float(a, dtype)) + + +def set_default_int_dtype( + dtype: Type[DTypeInteger] = DEFAULT_INT_DTYPE, +) -> None: + """ + Set *Colour* default :class:`numpy.integer` precision by setting + :attr:`colour.constant.DEFAULT_INT_DTYPE` attribute with given + :class:`numpy.dtype` wherever the attribute is imported. + + Parameters + ---------- + dtype + :class:`numpy.dtype` to set :attr:`colour.constant.DEFAULT_INT_DTYPE` + with. + + Notes + ----- + - It is possible to define the int precision at import time by setting + the *COLOUR_SCIENCE__DEFAULT_INT_DTYPE* environment variable, for + example `set COLOUR_SCIENCE__DEFAULT_INT_DTYPE=int32`. + + Warnings + -------- + This definition is mostly given for consistency purposes with + :func:`colour.utilities.set_default_float_dtype` definition but contrary to the + latter, changing *integer* precision will almost certainly completely break + *Colour*. With great power comes great responsibility. + + Examples + -------- + >>> as_int_array(np.ones(3)).dtype # doctest: +SKIP + dtype('int64') + >>> set_default_int_dtype(np.int32) + >>> as_int_array(np.ones(3)).dtype # doctest: +SKIP + dtype('int32') + >>> set_default_int_dtype(np.int64) + >>> as_int_array(np.ones(3)).dtype # doctest: +SKIP + dtype('int64') + """ + + # TODO: Investigate behaviour on Windows. + with suppress_warnings(colour_usage_warnings=True): + for module in sys.modules.values(): + if not hasattr(module, "DEFAULT_INT_DTYPE"): + continue + + setattr(module, "DEFAULT_INT_DTYPE", dtype) + + +def set_default_float_dtype( + dtype: Type[DTypeFloating] = DEFAULT_FLOAT_DTYPE, +) -> None: + """ + Set *Colour* default :class:`numpy.floating` precision by setting + :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute with given + :class:`numpy.dtype` wherever the attribute is imported. + + Parameters + ---------- + dtype + :class:`numpy.dtype` to set :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` + with. + + Warnings + -------- + Changing *float* precision might result in various *Colour* functionality + breaking entirely: https://github.com/numpy/numpy/issues/6860. With great + power comes great responsibility. + + Notes + ----- + - It is possible to define the *float* precision at import time by + setting the *COLOUR_SCIENCE__DEFAULT_FLOAT_DTYPE* environment variable, + for example `set COLOUR_SCIENCE__DEFAULT_FLOAT_DTYPE=float32`. + - Some definition returning a single-scalar ndarray might not honour the + given *float* precision: https://github.com/numpy/numpy/issues/16353 + + Examples + -------- + >>> as_float_array(np.ones(3)).dtype + dtype('float64') + >>> set_default_float_dtype(np.float16) + >>> as_float_array(np.ones(3)).dtype + dtype('float16') + >>> set_default_float_dtype(np.float64) + >>> as_float_array(np.ones(3)).dtype + dtype('float64') + """ + + with suppress_warnings(colour_usage_warnings=True): + for module in sys.modules.values(): + if not hasattr(module, "DEFAULT_FLOAT_DTYPE"): + continue + + setattr(module, "DEFAULT_FLOAT_DTYPE", dtype) + + +# TODO: Annotate with "Union[Literal['ignore', 'reference', '1', '100'], str]" +# when Python 3.7 is dropped. +_DOMAIN_RANGE_SCALE = "reference" +""" +Global variable storing the current *Colour* domain-range scale. + +_DOMAIN_RANGE_SCALE +""" + + +def get_domain_range_scale() -> Union[ + Literal["ignore", "reference", "1", "100"], str +]: + """ + Return the current *Colour* domain-range scale. The following scales are + available: + + - **'Reference'**, the default *Colour* domain-range scale which varies + depending on the referenced algorithm, e.g. [0, 1], [0, 10], [0, 100], + [0, 255], etc... + - **'1'**, a domain-range scale normalised to [0, 1], it is important to + acknowledge that this is a soft normalisation and it is possible to + use negative out of gamut values or high dynamic range data exceeding + 1. + + Returns + ------- + :class:`str` + *Colour* domain-range scale. + + Warnings + -------- + - The **'Ignore'** and **'100'** domain-range scales are for internal + usage only! + """ + + return _DOMAIN_RANGE_SCALE + + +def set_domain_range_scale( + scale: Union[ + Literal["ignore", "reference" "Ignore", "Reference", "1", "100"], str + ] = "reference" +): + """ + Set the current *Colour* domain-range scale. The following scales are + available: + + - **'Reference'**, the default *Colour* domain-range scale which varies + depending on the referenced algorithm, e.g. [0, 1], [0, 10], [0, 100], + [0, 255], etc... + - **'1'**, a domain-range scale normalised to [0, 1], it is important to + acknowledge that this is a soft normalisation and it is possible to + use negative out of gamut values or high dynamic range data exceeding + 1. + + Parameters + ---------- + scale + *Colour* domain-range scale to set. + + Warnings + -------- + - The **'Ignore'** and **'100'** domain-range scales are for internal + usage only! + """ + + global _DOMAIN_RANGE_SCALE + + _DOMAIN_RANGE_SCALE = validate_method( + str(scale), + ("ignore", "reference", "1", "100"), + '"{0}" scale is invalid, it must be one of {1}!', + ) + + +class domain_range_scale: + """ + Define context manager and decorator temporarily setting *Colour* + domain-range scale. + + The following scales are available: + + - **'Reference'**, the default *Colour* domain-range scale which varies + depending on the referenced algorithm, e.g. [0, 1], [0, 10], [0, 100], + [0, 255], etc... + - **'1'**, a domain-range scale normalised to [0, 1], it is important to + acknowledge that this is a soft normalisation and it is possible to + use negative out of gamut values or high dynamic range data exceeding + 1. + + Parameters + ---------- + scale + *Colour* domain-range scale to set. + + Warnings + -------- + - The **'Ignore'** and **'100'** domain-range scales are for internal + usage only! + + Examples + -------- + With *Colour* domain-range scale set to **'Reference'**: + + >>> with domain_range_scale('1'): + ... to_domain_1(1) + array(1.0) + >>> with domain_range_scale('Reference'): + ... from_range_1(1) + array(1.0) + + With *Colour* domain-range scale set to **'1'**: + + >>> with domain_range_scale('1'): + ... to_domain_1(1) + array(1.0) + >>> with domain_range_scale('1'): + ... from_range_1(1) + array(1.0) + + With *Colour* domain-range scale set to **'100'** (unsupported): + + >>> with domain_range_scale('100'): + ... to_domain_1(1) + array(0.01) + >>> with domain_range_scale('100'): + ... from_range_1(1) + array(100.0) + """ + + def __init__( + self, + scale: Union[ + Literal["ignore", "reference" "Ignore", "Reference", "1", "100"], + str, + ], + ): + self._scale = scale + self._previous_scale = get_domain_range_scale() + + def __enter__(self) -> domain_range_scale: + """Set the new domain-range scale upon entering the context manager.""" + + set_domain_range_scale(self._scale) + + return self + + def __exit__(self, *args: Any): + """Set the previous domain-range scale upon exiting the context manager.""" + + set_domain_range_scale(self._previous_scale) + + def __call__(self, function: Callable) -> Any: + """Call the wrapped definition.""" + + @functools.wraps(function) + def wrapper(*args: Any, **kwargs: Any) -> Any: + with self: + return function(*args, **kwargs) + + return wrapper + + +def to_domain_1( + a: ArrayLike, + scale_factor: FloatingOrArrayLike = 100, + dtype: Optional[Type[DTypeFloating]] = None, +) -> NDArray: + """ + Scale given array :math:`a` to domain **'1'**. The behaviour is as + follows: + + - If *Colour* domain-range scale is **'Reference'** or **'1'**, the + definition is almost entirely by-passed and will conveniently convert + array :math:`a` to :class:`np.ndarray`. + - If *Colour* domain-range scale is **'100'** (currently unsupported + private value only used for unit tests), array :math:`a` is divided by + ``scale_factor``, typically 100. + + Parameters + ---------- + a + Array :math:`a` to scale to domain **'1'**. + scale_factor + Scale factor, usually *numeric* but can be a :class:`numpy.ndarray` if + some axis need different scaling to be brought to domain **'1'**. + dtype + Data type used for the conversion to :class:`np.ndarray`. + + Returns + ------- + :class:`numpy.ndarray` + Array :math:`a` scaled to domain **'1'**. + + Examples + -------- + With *Colour* domain-range scale set to **'Reference'**: + + >>> with domain_range_scale('Reference'): + ... to_domain_1(1) + array(1.0) + + With *Colour* domain-range scale set to **'1'**: + + >>> with domain_range_scale('1'): + ... to_domain_1(1) + array(1.0) + + With *Colour* domain-range scale set to **'100'** (unsupported): + + >>> with domain_range_scale('100'): + ... to_domain_1(1) + array(0.01) + """ + + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) + + a = as_float_array(a, dtype).copy() + + if _DOMAIN_RANGE_SCALE == "100": + a /= as_float_array(scale_factor) + + return a + + +def to_domain_10( + a: ArrayLike, + scale_factor: FloatingOrArrayLike = 10, + dtype: Optional[Type[DTypeFloating]] = None, +) -> NDArray: + """ + Scale given array :math:`a` to domain **'10'**, used by + *Munsell Renotation System*. The behaviour is as follows: + + - If *Colour* domain-range scale is **'Reference'**, the + definition is almost entirely by-passed and will conveniently convert + array :math:`a` to :class:`np.ndarray`. + - If *Colour* domain-range scale is **'1'**, array :math:`a` is + multiplied by ``scale_factor``, typically 10. + - If *Colour* domain-range scale is **'100'** (currently unsupported + private value only used for unit tests), array :math:`a` is + divided by ``scale_factor``, typically 10. + + Parameters + ---------- + a + Array :math:`a` to scale to domain **'10'**. + scale_factor + Scale factor, usually *numeric* but can be a :class:`numpy.ndarray` if + some axis need different scaling to be brought to domain **'10'**. + dtype + Data type used for the conversion to :class:`np.ndarray`. + + Returns + ------- + :class:`numpy.ndarray` + Array :math:`a` scaled to domain **'10'**. + + Examples + -------- + With *Colour* domain-range scale set to **'Reference'**: + + >>> with domain_range_scale('Reference'): + ... to_domain_10(1) + array(1.0) + + With *Colour* domain-range scale set to **'1'**: + + >>> with domain_range_scale('1'): + ... to_domain_10(1) + array(10.0) + + With *Colour* domain-range scale set to **'100'** (unsupported): + + >>> with domain_range_scale('100'): + ... to_domain_10(1) + array(0.1) + """ + + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) + + a = as_float_array(a, dtype).copy() + + if _DOMAIN_RANGE_SCALE == "1": + a *= as_float_array(scale_factor) + + if _DOMAIN_RANGE_SCALE == "100": + a /= as_float_array(scale_factor) + + return a + + +def to_domain_100( + a: ArrayLike, + scale_factor: FloatingOrArrayLike = 100, + dtype: Optional[Type[DTypeFloating]] = None, +) -> NDArray: + """ + Scale given array :math:`a` to domain **'100'**. The behaviour is as + follows: + + - If *Colour* domain-range scale is **'Reference'** or **'100'** + (currently unsupported private value only used for unit tests), the + definition is almost entirely by-passed and will conveniently convert + array :math:`a` to :class:`np.ndarray`. + - If *Colour* domain-range scale is **'1'**, array :math:`a` is + multiplied by ``scale_factor``, typically 100. + + Parameters + ---------- + a + Array :math:`a` to scale to domain **'100'**. + scale_factor + Scale factor, usually *numeric* but can be a :class:`numpy.ndarray` if + some axis need different scaling to be brought to domain **'100'**. + dtype + Data type used for the conversion to :class:`np.ndarray`. + + Returns + ------- + :class:`numpy.ndarray` + Array :math:`a` scaled to domain **'100'**. + + Examples + -------- + With *Colour* domain-range scale set to **'Reference'**: + + >>> with domain_range_scale('Reference'): + ... to_domain_100(1) + array(1.0) + + With *Colour* domain-range scale set to **'1'**: + + >>> with domain_range_scale('1'): + ... to_domain_100(1) + array(100.0) + + With *Colour* domain-range scale set to **'100'** (unsupported): + + >>> with domain_range_scale('100'): + ... to_domain_100(1) + array(1.0) + """ + + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) + + a = as_float_array(a, dtype).copy() + + if _DOMAIN_RANGE_SCALE == "1": + a *= as_float_array(scale_factor) + + return a + + +def to_domain_degrees( + a: ArrayLike, + scale_factor: FloatingOrArrayLike = 360, + dtype: Optional[Type[DTypeFloating]] = None, +) -> NDArray: """ - Converts given :math:`a` variable to *ndarray* using given type. + Scale given array :math:`a` to degrees domain. The behaviour is as + follows: + + - If *Colour* domain-range scale is **'Reference'**, the + definition is almost entirely by-passed and will conveniently convert + array :math:`a` to :class:`np.ndarray`. + - If *Colour* domain-range scale is **'1'**, array :math:`a` is + multiplied by ``scale_factor``, typically 360. + - If *Colour* domain-range scale is **'100'** (currently unsupported + private value only used for unit tests), array :math:`a` is + multiplied by ``scale_factor`` / 100, typically 360 / 100. Parameters ---------- - a : object - Variable to convert. - dtype : object - Type to use for conversion, default to the type defined by the - :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. + a + Array :math:`a` to scale to degrees domain. + scale_factor + Scale factor, usually *numeric* but can be a :class:`numpy.ndarray` if + some axis need different scaling to be brought to degrees domain. + dtype + Data type used for the conversion to :class:`np.ndarray`. Returns ------- - ndarray - :math:`a` variable converted to *ndarray*. + :class:`numpy.ndarray` + Array :math:`a` scaled to degrees domain. Examples -------- - >>> as_array([1, 2, 3]) - array([ 1., 2., 3.]) - >>> as_array([1, 2, 3], dtype=DEFAULT_INT_DTYPE) # doctest: +ELLIPSIS - array([1, 2, 3]...) + With *Colour* domain-range scale set to **'Reference'**: + + >>> with domain_range_scale('Reference'): + ... to_domain_degrees(1) + array(1.0) + + With *Colour* domain-range scale set to **'1'**: + + >>> with domain_range_scale('1'): + ... to_domain_degrees(1) + array(360.0) + + With *Colour* domain-range scale set to **'100'** (unsupported): + + >>> with domain_range_scale('100'): + ... to_domain_degrees(1) + array(3.6) """ - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) - if six.PY3: # pragma: no cover - # TODO: Remove when https://github.com/numpy/numpy/issues/5718 is - # addressed. - from collections.abc import ValuesView + a = as_float_array(a, dtype).copy() - if isinstance(a, ValuesView): - a = list(a) + if _DOMAIN_RANGE_SCALE == "1": + a *= as_float_array(scale_factor) - return np.asarray(a, dtype) + if _DOMAIN_RANGE_SCALE == "100": + a *= as_float_array(scale_factor) / 100 + + return a -def as_int_array(a, dtype=None): +def to_domain_int( + a: ArrayLike, + bit_depth: IntegerOrArrayLike = 8, + dtype: Optional[Type[DTypeFloating]] = None, +) -> NDArray: """ - Converts given :math:`a` variable to *ndarray* using given type. + Scale given array :math:`a` to int domain. The behaviour is as follows: + + - If *Colour* domain-range scale is **'Reference'**, the + definition is almost entirely by-passed and will conveniently convert + array :math:`a` to :class:`np.ndarray`. + - If *Colour* domain-range scale is **'1'**, array :math:`a` is + multiplied by :math:`2^{bit\\_depth} - 1`. + - If *Colour* domain-range scale is **'100'** (currently unsupported + private value only used for unit tests), array :math:`a` is + multiplied by :math:`2^{bit\\_depth} - 1`. Parameters ---------- - a : object - Variable to convert. - dtype : object - Type to use for conversion, default to the type defined by the - :attr:`colour.constant.DEFAULT_INT_DTYPE` attribute. + a + Array :math:`a` to scale to int domain. + bit_depth + Bit depth, usually *integer* but can be a :class:`numpy.ndarray` if + some axis need different scaling to be brought to int domain. + dtype + Data type used for the conversion to :class:`np.ndarray`. Returns ------- - ndarray - :math:`a` variable converted to *ndarray*. + :class:`numpy.ndarray` + Array :math:`a` scaled to int domain. + + Notes + ----- + - To avoid precision issues and rounding, the scaling is performed on + *float* numbers. Examples -------- - >>> as_int_array([1.0, 2.0, 3.0]) # doctest: +ELLIPSIS - array([1, 2, 3]...) - """ + With *Colour* domain-range scale set to **'Reference'**: - if dtype is None: - dtype = DEFAULT_INT_DTYPE + >>> with domain_range_scale('Reference'): + ... to_domain_int(1) + array(1.0) - assert dtype in np.sctypes['int'], ( - '"dtype" must be one of the following types: {0}'.format( - np.sctypes['int'])) + With *Colour* domain-range scale set to **'1'**: - return as_array(a, dtype) + >>> with domain_range_scale('1'): + ... to_domain_int(1) + array(255.0) + With *Colour* domain-range scale set to **'100'** (unsupported): -def as_float_array(a, dtype=None): + >>> with domain_range_scale('100'): + ... to_domain_int(1) + array(2.55) """ - Converts given :math:`a` variable to *ndarray* using given type. - - Parameters - ---------- - a : object - Variable to convert. - dtype : object - Type to use for conversion, default to the type defined by the - :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. - Returns - ------- - ndarray - :math:`a` variable converted to *ndarray*. + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) - Examples - -------- - >>> as_float_array([1, 2, 3]) - array([ 1., 2., 3.]) - """ + a = as_float_array(a, dtype).copy() - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE + maximum_code_value = np.power(2, bit_depth) - 1 + if _DOMAIN_RANGE_SCALE == "1": + a *= maximum_code_value - assert dtype in np.sctypes['float'], ( - '"dtype" must be one of the following types: {0}'.format( - np.sctypes['float'])) + if _DOMAIN_RANGE_SCALE == "100": + a *= maximum_code_value / 100 - return as_array(a, dtype) + return a -def as_numeric(a, dtype=None): +def from_range_1( + a: ArrayLike, + scale_factor: FloatingOrArrayLike = 100, + dtype: Optional[Type[DTypeFloating]] = None, +) -> NDArray: """ - Converts given :math:`a` variable to *numeric*. In the event where - :math:`a` cannot be converted, it is passed as is. + Scale given array :math:`a` from range **'1'**. The behaviour is as + follows: + + - If *Colour* domain-range scale is **'Reference'** or **'1'**, the + definition is entirely by-passed. + - If *Colour* domain-range scale is **'100'** (currently unsupported + private value only used for unit tests), array :math:`a` is multiplied + by ``scale_factor``, typically 100. Parameters ---------- - a : object - Variable to convert. - dtype : object - Type to use for conversion, default to the type defined by the - :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. + a + Array :math:`a` to scale from range **'1'**. + scale_factor + Scale factor, usually *numeric* but can be a :class:`numpy.ndarray` if + some axis need different scaling to be brought from range **'1'**. + dtype + Data type used for the conversion to :class:`np.ndarray`. Returns ------- - ndarray - :math:`a` variable converted to *numeric*. + :class:`numpy.ndarray` + Array :math:`a` scaled from range **'1'**. + + Warnings + -------- + The scale conversion of variable :math:`a` happens in-place, i.e. :math:`a` + will be mutated! Examples -------- - >>> as_numeric(np.array([1])) - 1.0 - >>> as_numeric(np.arange(10)) - array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]) + With *Colour* domain-range scale set to **'Reference'**: + + >>> with domain_range_scale('Reference'): + ... from_range_1(1) + array(1.0) + + With *Colour* domain-range scale set to **'1'**: + + >>> with domain_range_scale('1'): + ... from_range_1(1) + array(1.0) + + With *Colour* domain-range scale set to **'100'** (unsupported): + + >>> with domain_range_scale('100'): + ... from_range_1(1) + array(100.0) """ - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) - try: - return dtype(a) - except (TypeError, ValueError): - return a + a = as_float_array(a, dtype) + + if _DOMAIN_RANGE_SCALE == "100": + a *= as_float_array(scale_factor) + + return a -def as_int(a, dtype=None): +def from_range_10( + a: ArrayLike, + scale_factor: FloatingOrArrayLike = 10, + dtype: Optional[Type[DTypeFloating]] = None, +) -> NDArray: """ - Attempts to converts given :math:`a` variable to *int* using given type. + Scale given array :math:`a` from range **'10'**, used by + *Munsell Renotation System*. The behaviour is as follows: + + - If *Colour* domain-range scale is **'Reference'**, the + definition is entirely by-passed. + - If *Colour* domain-range scale is **'1'**, array :math:`a` is + divided by ``scale_factor``, typically 10. + - If *Colour* domain-range scale is **'100'** (currently unsupported + private value only used for unit tests), array :math:`a` is + multiplied by ``scale_factor``, typically 10. Parameters ---------- - a : object - Variable to convert. - dtype : object - Type to use for conversion, default to the type defined by the - :attr:`colour.constant.DEFAULT_INT_DTYPE` attribute. In the event where - :math:`a` cannot be converted, it is converted to *ndarray* using the - type defined by :attr:`colour.constant.DEFAULT_INT_DTYPE` attribute. + a + Array :math:`a` to scale from range **'10'**. + scale_factor + Scale factor, usually *numeric* but can be a :class:`numpy.ndarray` if + some axis need different scaling to be brought from range **'10'**. + dtype + Data type used for the conversion to :class:`np.ndarray`. Returns ------- - ndarray - :math:`a` variable converted to *numeric*. + :class:`numpy.ndarray` + Array :math:`a` scaled from range **'10'**. Warnings -------- - The behaviour of this definition is different than - :func:`colour.utilities.as_numeric` definition when it comes to conversion - failure: the former will forcibly convert :math:`a` variable to *ndarray* - using the type defined by :attr:`colour.constant.DEFAULT_INT_DTYPE` - attribute while the later will pass the :math:`a` variable as is. + The scale conversion of variable :math:`a` happens in-place, i.e. :math:`a` + will be mutated! Examples -------- - >>> as_int(np.array([1])) - 1 - >>> as_int(np.arange(10)) # doctest: +ELLIPSIS - array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]...) + With *Colour* domain-range scale set to **'Reference'**: + + >>> with domain_range_scale('Reference'): + ... from_range_10(1) + array(1.0) + + With *Colour* domain-range scale set to **'1'**: + + >>> with domain_range_scale('1'): + ... from_range_10(1) + array(0.1) + + With *Colour* domain-range scale set to **'100'** (unsupported): + + >>> with domain_range_scale('100'): + ... from_range_10(1) + array(10.0) """ - if dtype is None: - dtype = DEFAULT_INT_DTYPE + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) - assert dtype in np.sctypes['int'], ( - '"dtype" must be one of the following types: {0}'.format( - np.sctypes['int'])) - try: - # TODO: Change to "DEFAULT_INT_DTYPE" when and if - # https://github.com/numpy/numpy/issues/11956 is addressed. - return int(a) - except TypeError: - return as_int_array(a, dtype) + a = as_float_array(a, dtype) + + if _DOMAIN_RANGE_SCALE == "1": + a /= as_float_array(scale_factor) + + if _DOMAIN_RANGE_SCALE == "100": + a *= as_float_array(scale_factor) + + return a -def as_float(a, dtype=None): +def from_range_100( + a: ArrayLike, + scale_factor: FloatingOrArrayLike = 100, + dtype: Optional[Type[DTypeFloating]] = None, +) -> NDArray: """ - Converts given :math:`a` variable to *numeric* using given type. + Scale given array :math:`a` from range **'100'**. The behaviour is as + follows: + + - If *Colour* domain-range scale is **'Reference'** or **'100'** + (currently unsupported private value only used for unit tests), the + definition is entirely by-passed. + - If *Colour* domain-range scale is **'1'**, array :math:`a` is + divided by ``scale_factor``, typically 100. Parameters ---------- - a : object - Variable to convert. - dtype : object - Type to use for conversion, default to the type defined by the - :attr:`colour.constant.DEFAULT_INT_DTYPE` attribute. In the event where - :math:`a` cannot be converted, it is converted to *ndarray* using the - type defined by :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. + a + Array :math:`a` to scale from range **'100'**. + scale_factor + Scale factor, usually *numeric* but can be a :class:`numpy.ndarray` if + some axis need different scaling to be brought from range **'100'**. + dtype + Data type used for the conversion to :class:`np.ndarray`. Returns ------- - ndarray - :math:`a` variable converted to *numeric*. + :class:`numpy.ndarray` + Array :math:`a` scaled from range **'100'**. Warnings -------- - The behaviour of this definition is different than - :func:`colour.utilities.as_numeric` definition when it comes to conversion - failure: the former will forcibly convert :math:`a` variable to *ndarray* - using the type defined by :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` - attribute while the later will pass the :math:`a` variable as is. + The scale conversion of variable :math:`a` happens in-place, i.e. :math:`a` + will be mutated! Examples -------- - >>> as_float(np.array([1])) - 1.0 - >>> as_float(np.arange(10)) - array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]) + With *Colour* domain-range scale set to **'Reference'**: + + >>> with domain_range_scale('Reference'): + ... from_range_100(1) + array(1.0) + + With *Colour* domain-range scale set to **'1'**: + + >>> with domain_range_scale('1'): + ... from_range_100(1) + array(0.01) + + With *Colour* domain-range scale set to **'100'** (unsupported): + + >>> with domain_range_scale('100'): + ... from_range_100(1) + array(1.0) """ - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) - assert dtype in np.sctypes['float'], ( - '"dtype" must be one of the following types: {0}'.format( - np.sctypes['float'])) + a = as_float_array(a, dtype) - return dtype(a) + if _DOMAIN_RANGE_SCALE == "1": + a /= as_float_array(scale_factor) + + return a -def set_float_precision(dtype=DEFAULT_FLOAT_DTYPE): +def from_range_degrees( + a: ArrayLike, + scale_factor: FloatingOrArrayLike = 360, + dtype: Optional[Type[DTypeFloating]] = None, +) -> NDArray: """ - Sets *Colour* float precision by setting - :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute with given type - wherever the attribute is imported. + Scale given array :math:`a` from degrees range. The behaviour is as + follows: + + - If *Colour* domain-range scale is **'Reference'**, the + definition is entirely by-passed. + - If *Colour* domain-range scale is **'1'**, array :math:`a` is + divided by ``scale_factor``, typically 360. + - If *Colour* domain-range scale is **'100'** (currently unsupported + private value only used for unit tests), array :math:`a` is + divided by ``scale_factor`` / 100, typically 360 / 100. Parameters ---------- - dtype : object - Type to set :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` with. + a + Array :math:`a` to scale from degrees range. + scale_factor + Scale factor, usually *numeric* but can be a :class:`numpy.ndarray` if + some axis need different scaling to be brought from degrees range. + dtype + Data type used for the conversion to :class:`np.ndarray`. + + Returns + ------- + :class:`numpy.ndarray` + Array :math:`a` scaled from degrees range. Warnings -------- - Changing float precision might result in various *Colour* functionality - breaking entirely: https://github.com/numpy/numpy/issues/6860. With great - power comes great responsibility. - - Notes - ----- - - It is possible to define the float precision at import time by setting - the *COLOUR_SCIENCE__FLOAT_PRECISION* environment variable, for example - `set COLOUR_SCIENCE__FLOAT_PRECISION=float32`. - - Some definition returning a single-scalar ndarray might not honour the - given float precision: https://github.com/numpy/numpy/issues/16353 + The scale conversion of variable :math:`a` happens in-place, i.e. :math:`a` + will be mutated! Examples -------- - >>> as_float_array(np.ones(3)).dtype - dtype('float64') - >>> set_float_precision(np.float16) - >>> as_float_array(np.ones(3)).dtype - dtype('float16') - >>> set_float_precision(np.float64) - >>> as_float_array(np.ones(3)).dtype - dtype('float64') + With *Colour* domain-range scale set to **'Reference'**: + + >>> with domain_range_scale('Reference'): + ... from_range_degrees(1) + array(1.0) + + With *Colour* domain-range scale set to **'1'**: + + >>> with domain_range_scale('1'): + ... from_range_degrees(1) # doctest: +ELLIPSIS + array(0.0027777...) + + With *Colour* domain-range scale set to **'100'** (unsupported): + + >>> with domain_range_scale('100'): + ... from_range_degrees(1) # doctest: +ELLIPSIS + array(0.2777777...) """ - with suppress_warnings(colour_usage_warnings=True): - for name, module in sys.modules.items(): - if not name.startswith(name): - continue + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) - if not hasattr(module, 'DEFAULT_FLOAT_DTYPE'): - continue + a = as_float_array(a, dtype) + + if _DOMAIN_RANGE_SCALE == "1": + a /= as_float_array(scale_factor) + + if _DOMAIN_RANGE_SCALE == "100": + a /= as_float_array(scale_factor) / 100 - setattr(module, 'DEFAULT_FLOAT_DTYPE', dtype) + return a -def set_int_precision(dtype=DEFAULT_INT_DTYPE): +def from_range_int( + a: ArrayLike, + bit_depth: IntegerOrArrayLike = 8, + dtype: Optional[Type[DTypeFloating]] = None, +) -> NDArray: """ - Sets *Colour* integer precision by setting - :attr:`colour.constant.DEFAULT_INT_DTYPE` attribute with given type - wherever the attribute is imported. + Scale given array :math:`a` from int range. The behaviour is as follows: + + - If *Colour* domain-range scale is **'Reference'**, the + definition is entirely by-passed. + - If *Colour* domain-range scale is **'1'**, array :math:`a` is converted + to :class:`np.ndarray` and divided by :math:`2^{bit\\_depth} - 1`. + - If *Colour* domain-range scale is **'100'** (currently unsupported + private value only used for unit tests), array :math:`a` is converted + to :class:`np.ndarray` and divided by :math:`2^{bit\\_depth} - 1`. Parameters ---------- - dtype : object - Type to set :attr:`colour.constant.DEFAULT_INT_DTYPE` with. + a + Array :math:`a` to scale from int range. + bit_depth + Bit depth, usually *integer* but can be a :class:`numpy.ndarray` if + some axis need different scaling to be brought from int range. + dtype + Data type used for the conversion to :class:`np.ndarray`. - Notes - ----- - - It is possible to define the int precision at import time by setting - the *COLOUR_SCIENCE__INT_PRECISION* environment variable, for example - `set COLOUR_SCIENCE__INT_PRECISION=int32`. + Returns + ------- + :class:`numpy.ndarray` + Array :math:`a` scaled from int range. Warnings -------- - This definition is mostly given for consistency purposes with - :func:`colour.utilities.set_float_precision` definition but contrary to the - latter, changing integer precision will almost certainly completely break - *Colour*. With great power comes great responsibility. + The scale conversion of variable :math:`a` happens in-place, i.e. :math:`a` + will be mutated! + + Notes + ----- + - To avoid precision issues and rounding, the scaling is performed on + *float* numbers. Examples -------- - >>> as_int_array(np.ones(3)).dtype # doctest: +SKIP - dtype('int64') - >>> set_int_precision(np.int32) - >>> as_int_array(np.ones(3)).dtype # doctest: +SKIP - dtype('int32') - >>> set_int_precision(np.int64) - >>> as_int_array(np.ones(3)).dtype # doctest: +SKIP - dtype('int64') - """ + With *Colour* domain-range scale set to **'Reference'**: - # TODO: Investigate behaviour on Windows. - with suppress_warnings(colour_usage_warnings=True): - for name, module in sys.modules.items(): - if not name.startswith(name): - continue + >>> with domain_range_scale('Reference'): + ... from_range_int(1) + array(1.0) - if not hasattr(module, 'DEFAULT_INT_DTYPE'): - continue + With *Colour* domain-range scale set to **'1'**: - setattr(module, 'DEFAULT_INT_DTYPE', dtype) + >>> with domain_range_scale('1'): + ... from_range_int(1) # doctest: +ELLIPSIS + array(0.0039215...) + With *Colour* domain-range scale set to **'100'** (unsupported): -def as_namedtuple(a, named_tuple): + >>> with domain_range_scale('100'): + ... from_range_int(1) # doctest: +ELLIPSIS + array(0.3921568...) """ - Converts given :math:`a` variable to given *namedtuple* class instance. - :math:`a` can be either a *Numpy* structured array, a *namedtuple*, - a *mapping*, or an *array_like* object. The definition will attempt to - convert it to given *namedtuple*. + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) - Parameters - ---------- - a : object - Variable to convert. - named_tuple : namedtuple - *namedtuple* class. + a = as_float_array(a, dtype) - Returns - ------- - namedtuple - math:`a` variable converted to *namedtuple*. + maximum_code_value = np.power(2, bit_depth) - 1 + if _DOMAIN_RANGE_SCALE == "1": + a /= maximum_code_value - Examples - -------- - >>> from collections import namedtuple - >>> a_a = 1 - >>> a_b = 2 - >>> a_c = 3 - >>> NamedTuple = namedtuple('NamedTuple', 'a b c') - >>> as_namedtuple(NamedTuple(a=1, b=2, c=3), NamedTuple) - NamedTuple(a=1, b=2, c=3) - >>> as_namedtuple({'a': a_a, 'b': a_b, 'c': a_c}, NamedTuple) - NamedTuple(a=1, b=2, c=3) - >>> as_namedtuple([a_a, a_b, a_c], NamedTuple) - NamedTuple(a=1, b=2, c=3) - """ - - if isinstance(a, np.ndarray): - if a.dtype.fields is not None: - a = {field: a[field] for field in a.dtype.fields} - - if isinstance(a, named_tuple): - return a - elif isinstance(a, Mapping): - return named_tuple(**a) - else: - return named_tuple(*a) + if _DOMAIN_RANGE_SCALE == "100": + a /= maximum_code_value / 100 + + return a -def closest_indexes(a, b): +def closest_indexes(a: ArrayLike, b: ArrayLike) -> NDArray: """ - Returns the :math:`a` variable closest element indexes to reference - :math:`b` variable elements. + Return the array :math:`a` closest element indexes to the reference array + :math:`b` elements. Parameters ---------- - a : array_like - Variable to search for the closest element indexes. - b : numeric - Reference variable. + a + Array :math:`a` to search for the closest element indexes. + b + Reference array :math:`b`. Returns ------- - numeric - Closest :math:`a` variable element indexes. + :class:`numpy.ndarray` + Closest array :math:`a` element indexes. Examples -------- @@ -460,22 +1764,22 @@ def closest_indexes(a, b): return np.abs(a - b).argmin(axis=0) -def closest(a, b): +def closest(a: ArrayLike, b: ArrayLike) -> NDArray: """ - Returns the :math:`a` variable closest elements to reference :math:`b` - variable elements. + Return the array :math:`a` closest elements to the reference array + :math:`b` elements. Parameters ---------- - a : array_like - Variable to search for the closest elements. - b : numeric - Reference variable. + a + Array :math:`a` to search for the closest element. + b + Reference array :math:`b`. Returns ------- - numeric - Closest :math:`a` variable elements. + :class:`numpy.ndarray` + Closest array :math:`a` elements. Examples -------- @@ -492,57 +1796,21 @@ def closest(a, b): return a[closest_indexes(a, b)] -def normalise_maximum(a, axis=None, factor=1, clip=True): - """ - Normalises given *array_like* :math:`a` variable values by :math:`a` - variable maximum value and optionally clip them between. - - Parameters - ---------- - a : array_like - :math:`a` variable to normalise. - axis : numeric, optional - Normalization axis. - factor : numeric, optional - Normalization factor. - clip : bool, optional - Clip values to domain [0, 'factor']. - - Returns - ------- - ndarray - Maximum normalised :math:`a` variable. - - Examples - -------- - >>> a = np.array([0.48222001, 0.31654775, 0.22070353]) - >>> normalise_maximum(a) # doctest: +ELLIPSIS - array([ 1. , 0.6564384..., 0.4576822...]) - """ - - a = as_float_array(a) - - maximum = np.max(a, axis=axis) - a = a * (1 / maximum[..., np.newaxis]) * factor - - return np.clip(a, 0, factor) if clip else a - - -def interval(distribution, unique=True): +def interval(distribution: ArrayLike, unique: Boolean = True) -> NDArray: """ - Returns the interval size of given distribution. + Return the interval size of given distribution. Parameters ---------- - distribution : array_like + distribution Distribution to retrieve the interval. - unique : bool, optional + unique Whether to return unique intervals if the distribution is non-uniformly spaced or the complete intervals Returns ------- - ndarray + :class:`numpy.ndarray` Distribution interval. Examples @@ -574,19 +1842,19 @@ def interval(distribution, unique=True): return differences -def is_uniform(distribution): +def is_uniform(distribution: ArrayLike) -> Boolean: """ - Returns if given distribution is uniform. + Return whether given distribution is uniform. Parameters ---------- - distribution : array_like - Distribution to check for uniformity. + distribution + Distribution to check the uniformity of. Returns ------- - bool - Is distribution uniform. + :class:`bool` + Whether distribution uniform. Examples -------- @@ -606,25 +1874,29 @@ def is_uniform(distribution): return True if interval(distribution).size == 1 else False -def in_array(a, b, tolerance=EPSILON): +def in_array( + a: ArrayLike, b: ArrayLike, tolerance: Number = EPSILON +) -> NDArray: """ - Tests whether each element of an array is also present in a second array - within given tolerance. + Return whether each element of the array :math:`a` is also present in the + array :math:`b` within given tolerance. Parameters ---------- - a : array_like - Array to test the elements from. - b : array_like - The values against which to test each value of array *a*. - tolerance : numeric, optional + a + Array :math:`a` to test the elements from. + b + The array :math:`b` against which to test the elements of array + :math:`a`. + tolerance Tolerance value. Returns ------- - ndarray - A boolean array with *a* shape describing whether an element of *a* is - present in *b* within given tolerance. + :class:`numpy.ndarray` + A boolean array with array :math:`a` shape describing whether an + element of array :math:`a` is present in array :math:`b` within given + tolerance. References ---------- @@ -645,26 +1917,33 @@ def in_array(a, b, tolerance=EPSILON): d = np.abs(np.ravel(a) - b[..., np.newaxis]) - return np.any(d <= tolerance, axis=0).reshape(a.shape) + return np.reshape(np.any(d <= tolerance, axis=0), a.shape) -def tstack(a, dtype=None): +def tstack( + a: Union[ArrayLike, NestedSequence[NumberOrArrayLike]], + dtype: Optional[Union[Type[DTypeBoolean], Type[DTypeNumber]]] = None, +) -> NDArray: """ - Stacks arrays in sequence along the last axis (tail). + Stack given array of arrays :math:`a` along the last axis (tail) to + produce a stacked array. - Rebuilds arrays divided by :func:`colour.utilities.tsplit`. + It is used to stack an array of arrays produced by the + :func:`colour.utilities.tsplit` definition. Parameters ---------- - a : array_like - Array to perform the stacking. - dtype : object - Type to use for initial conversion to *ndarray*, default to the type - defined by :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. + a + Array of arrays :math:`a` to stack along the last axis. + dtype + :class:`numpy.dtype` to use for initial conversion to + :class:`numpy.ndarray`, default to the :class:`numpy.dtype` defined by + :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. Returns ------- - ndarray + :class:`numpy.ndarray` + Stacked array. Examples -------- @@ -697,29 +1976,37 @@ def tstack(a, dtype=None): [ 5., 5., 5.]]]]) """ - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) a = as_array(a, dtype) return np.concatenate([x[..., np.newaxis] for x in a], axis=-1) -def tsplit(a, dtype=None): +def tsplit( + a: Union[ArrayLike, NestedSequence[NumberOrArrayLike]], + dtype: Optional[Union[Type[DTypeBoolean], Type[DTypeNumber]]] = None, +) -> NDArray: """ - Splits arrays in sequence along the last axis (tail). + Split given stacked array :math:`a` along the last axis (tail) to produce + an array of arrays. + + It is used to split a stacked array produced by the + :func:`colour.utilities.tstack` definition. Parameters ---------- - a : array_like - Array to perform the splitting. - dtype : object - Type to use for initial conversion to *ndarray*, default to the type - defined by :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. + a + Stacked array :math:`a` to split. + dtype + :class:`numpy.dtype` to use for initial conversion to + :class:`numpy.ndarray`, default to the :class:`numpy.dtype` defined by + :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. Returns ------- - ndarray + :class:`numpy.ndarray` + Array of arrays. Examples -------- @@ -754,26 +2041,26 @@ def tsplit(a, dtype=None): [[ 0., 1., 2., 3., 4., 5.]]]) """ - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) a = as_array(a, dtype) return np.array([a[..., x] for x in range(a.shape[-1])]) -def row_as_diagonal(a): +def row_as_diagonal(a: ArrayLike) -> NDArray: """ - Returns the per row diagonal matrices of the given array. + Return the rows of given array :math:`a` as diagonal matrices. Parameters ---------- - a : array_like - Array to perform the diagonal matrices computation. + a + Array :math:`a` to returns the rows of as diagonal matrices. Returns ------- - ndarray + :class:`numpy.ndarray` + Array :math:`a` rows as diagonal matrices. References ---------- @@ -810,134 +2097,30 @@ def row_as_diagonal(a): [ 0. , 0. , 0.88027331]]]) """ - a = np.expand_dims(a, -2) - - return np.eye(a.shape[-1]) * a - - -def vector_dot(m, v): - """ - Convenient wrapper around :func:`np.einsum` with the following subscripts: - *'...ij,...j->...i'*. - - It performs the dot product of two arrays where *m* parameter is expected - to be an array of 3x3 matrices and parameter *v* an array of vectors. - - Parameters - ---------- - m : array_like - Array of 3x3 matrices. - v : array_like - Array of vectors. - - Returns - ------- - ndarray - - Examples - -------- - >>> m = np.array( - ... [[0.7328, 0.4296, -0.1624], - ... [-0.7036, 1.6975, 0.0061], - ... [0.0030, 0.0136, 0.9834]] - ... ) - >>> m = np.reshape(np.tile(m, (6, 1)), (6, 3, 3)) - >>> v = np.array([0.20654008, 0.12197225, 0.05136952]) - >>> v = np.tile(v, (6, 1)) - >>> vector_dot(m, v) # doctest: +ELLIPSIS - array([[ 0.1954094..., 0.0620396..., 0.0527952...], - [ 0.1954094..., 0.0620396..., 0.0527952...], - [ 0.1954094..., 0.0620396..., 0.0527952...], - [ 0.1954094..., 0.0620396..., 0.0527952...], - [ 0.1954094..., 0.0620396..., 0.0527952...], - [ 0.1954094..., 0.0620396..., 0.0527952...]]) - """ - - m = as_float_array(m) - v = as_float_array(v) - - return np.einsum('...ij,...j->...i', m, v) - - -def matrix_dot(a, b): - """ - Convenient wrapper around :func:`np.einsum` with the following subscripts: - *'...ij,...jk->...ik'*. - - It performs the dot product of two arrays where *a* parameter is expected - to be an array of 3x3 matrices and parameter *b* another array of of 3x3 - matrices. - - Parameters - ---------- - a : array_like - Array of 3x3 matrices. - b : array_like - Array of 3x3 matrices. - dtype : object - Type to use for conversion, default to the type defined by the - :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. - - Returns - ------- - ndarray - - Examples - -------- - >>> a = np.array( - ... [[0.7328, 0.4296, -0.1624], - ... [-0.7036, 1.6975, 0.0061], - ... [0.0030, 0.0136, 0.9834]] - ... ) - >>> a = np.reshape(np.tile(a, (6, 1)), (6, 3, 3)) - >>> b = a - >>> matrix_dot(a, b) # doctest: +ELLIPSIS - array([[[ 0.2342420..., 1.0418482..., -0.2760903...], - [-1.7099407..., 2.5793226..., 0.1306181...], - [-0.0044203..., 0.0377490..., 0.9666713...]], - - [[ 0.2342420..., 1.0418482..., -0.2760903...], - [-1.7099407..., 2.5793226..., 0.1306181...], - [-0.0044203..., 0.0377490..., 0.9666713...]], - - [[ 0.2342420..., 1.0418482..., -0.2760903...], - [-1.7099407..., 2.5793226..., 0.1306181...], - [-0.0044203..., 0.0377490..., 0.9666713...]], - - [[ 0.2342420..., 1.0418482..., -0.2760903...], - [-1.7099407..., 2.5793226..., 0.1306181...], - [-0.0044203..., 0.0377490..., 0.9666713...]], - - [[ 0.2342420..., 1.0418482..., -0.2760903...], - [-1.7099407..., 2.5793226..., 0.1306181...], - [-0.0044203..., 0.0377490..., 0.9666713...]], - - [[ 0.2342420..., 1.0418482..., -0.2760903...], - [-1.7099407..., 2.5793226..., 0.1306181...], - [-0.0044203..., 0.0377490..., 0.9666713...]]]) - """ + d = as_array(a) - a = as_float_array(a) - b = as_float_array(b) + d = np.expand_dims(d, -2) - return np.einsum('...ij,...jk->...ik', a, b) + return np.eye(d.shape[-1]) * d -def orient(a, orientation): +def orient( + a: ArrayLike, + orientation: Union[Literal["Flip", "Flop", "90 CW", "90 CCW", "180"], str], +) -> Union[NDArray, None]: """ - Orient given array according to given ``orientation`` value. + Orient given array :math:`a` according to given orientation. Parameters ---------- - a : array_like - Array to perform the orientation onto. - orientation : unicode, optional - **{'Flip', 'Flop', '90 CW', '90 CCW', '180'}** + a + Array :math:`a` to orient. + orientation Orientation to perform. Returns ------- - ndarray + :class:`numpy.ndarray` Oriented array. Examples @@ -963,33 +2146,37 @@ def orient(a, orientation): [4, 3, 2, 1, 0]]) """ - if orientation.lower() == 'flip': + orientation = validate_method( + orientation, ["Flip", "Flop", "90 CW", "90 CCW", "180"] + ) + + if orientation == "flip": return np.fliplr(a) - elif orientation.lower() == 'flop': + elif orientation == "flop": return np.flipud(a) - elif orientation.lower() == '90 cw': + elif orientation == "90 cw": return np.rot90(a, 3) - elif orientation.lower() == '90 ccw': + elif orientation == "90 ccw": return np.rot90(a) - elif orientation.lower() == '180': + elif orientation == "180": return np.rot90(a, 2) - else: - return a + else: # pragma: no cover + return None -def centroid(a): +def centroid(a: ArrayLike) -> NDArray: """ - Computes the centroid indexes of given :math:`a` array. + Return the centroid indexes of given array :math:`a`. Parameters ---------- - a : array_like - :math:`a` array to compute the centroid indexes. + a + Array :math:`a` to returns the centroid indexes of. Returns ------- - ndarray - :math:`a` array centroid indexes. + :class:`numpy.ndarray` + Array :math:`a` centroid indexes. Examples -------- @@ -1018,131 +2205,99 @@ def centroid(a): return np.array(a_ci).astype(DEFAULT_INT_DTYPE) -def linear_conversion(a, old_range, new_range): +def fill_nan( + a: ArrayLike, + method: Union[Literal["Interpolation", "Constant"], str] = "Interpolation", + default: Number = 0, +) -> NDArray: """ - Performs a simple linear conversion of given array between the old and new - ranges. + Fill given array :math:`a` NaN values according to given method. Parameters ---------- - a : array_like - Array to perform the linear conversion onto. - old_range : array_like - Old range. - new_range : array_like - New range. + a + Array :math:`a` to fill the NaNs of. + method + *Interpolation* method linearly interpolates through the NaN values, + *Constant* method replaces NaN values with ``default``. + default + Value to use with the *Constant* method. Returns ------- - ndarray - Linear conversion result. + :class:`numpy.ndarray` + NaNs filled array :math:`a`. Examples -------- - >>> a = np.linspace(0, 1, 10) - >>> linear_conversion(a, np.array([0, 1]), np.array([1, 10])) - array([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.]) - """ - - a = as_float_array(a) - - in_min, in_max = tsplit(old_range) - out_min, out_max = tsplit(new_range) - - return ((a - in_min) / (in_max - in_min)) * (out_max - out_min) + out_min - - -def lerp(a, b, c): + >>> a = np.array([0.1, 0.2, np.nan, 0.4, 0.5]) + >>> fill_nan(a) + array([ 0.1, 0.2, 0.3, 0.4, 0.5]) + >>> fill_nan(a, method='Constant') + array([ 0.1, 0.2, 0. , 0.4, 0.5]) """ - Performs a simple linear interpolation between given array :math:`a` and - array :math:`b` using :math:`c` value. - Parameters - ---------- - a : array_like - Array :math:`a`, the start of the range in which to interpolate. - b : array_like - Array :math:`b`, the end of the range in which to interpolate. - c : array_like - Array :math:`c` value to use to interpolate between array :math:`a` and - array :math:`b`. + a = np.array(a, copy=True) + method = validate_method(method, ["Interpolation", "Constant"]) - Returns - ------- - ndarray - Linear interpolation result. - Examples - -------- - >>> a = 0 - >>> b = 2 - >>> lerp(a, b, 0.5) - 1.0 - """ + mask = np.isnan(a) - a = as_float_array(a) - b = as_float_array(b) - c = as_float_array(c) + if method == "interpolation": + a[mask] = np.interp( + np.flatnonzero(mask), np.flatnonzero(~mask), a[~mask] + ) + elif method == "constant": + a[mask] = default - return (1 - c) * a + c * b + return a -def fill_nan(a, method='Interpolation', default=0): +def has_only_nan(a: ArrayLike) -> Boolean: """ - Fills given array NaNs according to given method. + Return whether given array :math:`a` contains only NaN values. Parameters ---------- - a : array_like - Array to fill the NaNs of. - method : unicode - **{'Interpolation', 'Constant'}**, - *Interpolation* method linearly interpolates through the NaNs, - *Constant* method replaces NaNs with ``default``. - default : numeric - Value to use with the *Constant* method. + a + Array :math:`a` to check whether it contains only NaN values. Returns ------- - ndarray - NaNs filled array. + :class:`bool` + Whether array :math:`a` contains only NaN values. Examples -------- - >>> a = np.array([0.1, 0.2, np.nan, 0.4, 0.5]) - >>> fill_nan(a) - array([ 0.1, 0.2, 0.3, 0.4, 0.5]) - >>> fill_nan(a, method='Constant') - array([ 0.1, 0.2, 0. , 0.4, 0.5]) + >>> has_only_nan(None) + True + >>> has_only_nan([None, None]) + True + >>> has_only_nan([True, None]) + False + >>> has_only_nan([0.1, np.nan, 0.3]) + False """ - a = np.copy(a) - - mask = np.isnan(a) - - if method.lower() == 'interpolation': - a[mask] = np.interp( - np.flatnonzero(mask), np.flatnonzero(~mask), a[~mask]) - elif method.lower() == 'constant': - a[mask] = default + a = as_float_array(a) - return a + return bool(np.all(np.isnan(a))) @contextmanager -def ndarray_write(a): +def ndarray_write(a: ArrayLike) -> Generator: """ - A context manager setting given array writeable to perform an operation - and then read-only. + Define a context manager setting given array :math:`a` writeable to + operate one and then read-only. Parameters ---------- - a : array_like - Array to perform an operation. + a + Array :math:`a` to operate on. - Returns - ------- - ndarray - Array. + Yields + ------ + Generator + Array :math:`a` operated. Examples -------- @@ -1166,29 +2321,32 @@ def ndarray_write(a): a.setflags(write=False) -def zeros(shape, dtype=None, order='C'): +def zeros( + shape: Union[Integer, Tuple[int, ...]], + dtype: Optional[Type[DTypeNumber]] = None, + order: Literal["C", "F"] = "C", +) -> NDArray: """ - Simple wrapper around :func:`np.zeros` definition to create arrays with - the active type defined by the:attr:`colour.constant.DEFAULT_FLOAT_DTYPE` - attribute. + Wrap :func:`np.zeros` definition to create an array with the active + :class:`numpy.dtype` defined by the + :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. Parameters ---------- - shape : int or array_like + shape Shape of the new array, e.g., ``(2, 3)`` or ``2``. - dtype : object - Type to use for conversion, default to the type defined by the + dtype + :class:`numpy.dtype` to use for conversion, default to the + :class:`numpy.dtype` defined by the :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. - order : unicode, optional - {'C', 'F'}, - Whether to store multi-dimensional data in row-major - (C-style) or column-major (Fortran-style) order in - memory. + order + Whether to store multi-dimensional data in row-major (C-style) or + column-major (Fortran-style) order in memory. Returns ------- - ndarray - Array of given shape and type, filled with zeros. + :class:`numpy.ndarray` + Array of given shape and :class:`numpy.dtype`, filled with zeros. Examples -------- @@ -1196,34 +2354,36 @@ def zeros(shape, dtype=None, order='C'): array([ 0., 0., 0.]) """ - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) return np.zeros(shape, dtype, order) -def ones(shape, dtype=None, order='C'): +def ones( + shape: Union[Integer, Tuple[int, ...]], + dtype: Optional[Type[DTypeNumber]] = None, + order: Literal["C", "F"] = "C", +) -> NDArray: """ - Simple wrapper around :func:`np.ones` definition to create arrays with - the active type defined by the:attr:`colour.constant.DEFAULT_FLOAT_DTYPE` - attribute. + Wrap :func:`np.ones` definition to create an array with the active + :class:`numpy.dtype` defined by the + :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. Parameters ---------- - shape : int or array_like + shape Shape of the new array, e.g., ``(2, 3)`` or ``2``. - dtype : object - Type to use for conversion, default to the type defined by the + dtype + :class:`numpy.dtype` to use for conversion, default to the + :class:`numpy.dtype` defined by the :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. - order : unicode, optional - {'C', 'F'}, - Whether to store multi-dimensional data in row-major - (C-style) or column-major (Fortran-style) order in - memory. + order + Whether to store multi-dimensional data in row-major (C-style) or + column-major (Fortran-style) order in memory. Returns ------- - ndarray + :class:`numpy.ndarray` Array of given shape and type, filled with ones. Examples @@ -1232,37 +2392,39 @@ def ones(shape, dtype=None, order='C'): array([ 1., 1., 1.]) """ - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) return np.ones(shape, dtype, order) -def full(shape, fill_value, dtype=None, order='C'): +def full( + shape: Union[Integer, Tuple[int, ...]], + fill_value: Number, + dtype: Optional[Type[DTypeNumber]] = None, + order: Literal["C", "F"] = "C", +) -> NDArray: """ - Simple wrapper around :func:`np.full` definition to create arrays with - the active type defined by the:attr:`colour.constant.DEFAULT_FLOAT_DTYPE` - attribute. + Wrap :func:`np.full` definition to create an array with the active type + defined by the:attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. Parameters ---------- - shape : int or array_like + shape Shape of the new array, e.g., ``(2, 3)`` or ``2``. - fill_value : numeric + fill_value Fill value. - dtype : object - Type to use for conversion, default to the type defined by the + dtype + :class:`numpy.dtype` to use for conversion, default to the + :class:`numpy.dtype` defined by the :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. - order : unicode, optional - {'C', 'F'}, - Whether to store multi-dimensional data in row-major - (C-style) or column-major (Fortran-style) order in - memory. + order + Whether to store multi-dimensional data in row-major (C-style) or + column-major (Fortran-style) order in memory. Returns ------- - ndarray - Array of given shape and type, filled with given value. + :class:`numpy.ndarray` + Array of given shape and :class:`numpy.dtype`, filled with given value. Examples -------- @@ -1270,38 +2432,37 @@ def full(shape, fill_value, dtype=None, order='C'): array([ 1., 1., 1.]) """ - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE + dtype = cast(Type[DTypeFloating], optional(dtype, DEFAULT_FLOAT_DTYPE)) return np.full(shape, fill_value, dtype, order) -def index_along_last_axis(a, indexes): +def index_along_last_axis(a: ArrayLike, indexes: ArrayLike) -> NDArray: """ - Reduces the dimension of an array by one, by using an array of indexes to - to pick elements off the last axis. + Reduce the dimension of array :math:`a` by one, by using an array of + indexes to pick elements off the last axis. Parameters ---------- - a : ndarray, (Ni..., m) - Array to be indexed. - indexes : ndarray, (Ni...) - Integer array with the same shape as `a` but with one dimension fewer, - containing indixes to the last dimension of `a`. All elements must be - numbers between `0` and `m` - 1. + a + Array :math:`a` to be indexed. + indexes + *Integer* array with the same shape as `a` but with one dimension + fewer, containing indices to the last dimension of `a`. All elements + must be numbers between `0` and `m` - 1. Returns ------- - ndarray, (Ni...) - Result of the operation. + :class:`numpy.ndarray` + Indexed array :math:`a`. Raises ------ - ValueError - If the arrays have incompatible shapes. - IndexError - If `indexes` has elements outside of the allowed range of 0 to `m` - 1 - or if it's not an integer array. + :class:`ValueError` + If the array :math:`a` and ``indexes`` have incompatible shapes. + :class:`IndexError` + If ``indexes`` has elements outside of the allowed range of 0 to + `m` - 1 or if it's not an *integer* array. Examples -------- @@ -1345,7 +2506,7 @@ def index_along_last_axis(a, indexes): ... ) True - In particular, this can be used to manipulate the indices given by + In particular, this can be used to manipulate the indexes given by functions like :func:`np.min` before indexing the array. For example, to get elements directly following the smallest elements: @@ -1356,9 +2517,14 @@ def index_along_last_axis(a, indexes): [ 4.8, 6.9, 7.1, 1.9]]) """ + a = np.array(a) + indexes = np.array(indexes) + if a.shape[:-1] != indexes.shape: - raise ValueError('Arrays have incompatible shapes: {0} and {1}'.format( - a.shape, indexes.shape)) + raise ValueError( + f"Array and indexes have incompatible shapes: {a.shape} and {indexes.shape}" + ) - return np.take_along_axis( - a, np.expand_dims(indexes, axis=-1), axis=-1).squeeze(axis=-1) + return np.take_along_axis(a, indexes[..., np.newaxis], axis=-1).squeeze( + axis=-1 + ) diff --git a/colour/utilities/common.py b/colour/utilities/common.py index b01bf40313..90b97c88c1 100644 --- a/colour/utilities/common.py +++ b/colour/utilities/common.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """ Common Utilities ================ -Defines common utilities objects that don't fall in any specific category. +Defines the common utilities objects that don't fall in any specific category. References ---------- @@ -14,7 +13,7 @@ numpyerrors.html """ -from __future__ import division, unicode_literals +from __future__ import annotations import inspect import multiprocessing @@ -22,51 +21,275 @@ import functools import numpy as np import re -import six import types import warnings from contextlib import contextmanager -from collections import OrderedDict from copy import copy -from six import integer_types, string_types - -from colour.constants import INTEGER_THRESHOLD, DEFAULT_FLOAT_DTYPE +from pprint import pformat + +from colour.constants import INTEGER_THRESHOLD +from colour.hints import ( + Any, + Boolean, + Callable, + Dict, + Generator, + Integer, + Iterable, + Literal, + Mapping, + Optional, + RegexFlag, + Sequence, + TypeVar, + Union, +) from colour.utilities import CaseInsensitiveMapping, Lookup -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'handle_numpy_errors', 'ignore_numpy_errors', 'raise_numpy_errors', - 'print_numpy_errors', 'warn_numpy_errors', 'ignore_python_warnings', - 'batch', 'disable_multiprocessing', 'multiprocessing_pool', - 'is_matplotlib_installed', 'is_networkx_installed', - 'is_openimageio_installed', 'is_pandas_installed', 'is_tqdm_installed', - 'required', 'is_iterable', 'is_string', 'is_numeric', 'is_integer', - 'is_sibling', 'filter_kwargs', 'filter_mapping', 'first_item', - 'get_domain_range_scale', 'set_domain_range_scale', 'domain_range_scale', - 'to_domain_1', 'to_domain_10', 'to_domain_100', 'to_domain_degrees', - 'to_domain_int', 'from_range_1', 'from_range_10', 'from_range_100', - 'from_range_degrees', 'from_range_int', 'copy_definition' + "CacheRegistry", + "CACHE_REGISTRY", + "handle_numpy_errors", + "ignore_numpy_errors", + "raise_numpy_errors", + "print_numpy_errors", + "warn_numpy_errors", + "ignore_python_warnings", + "attest", + "batch", + "disable_multiprocessing", + "multiprocessing_pool", + "is_matplotlib_installed", + "is_networkx_installed", + "is_opencolorio_installed", + "is_openimageio_installed", + "is_pandas_installed", + "is_sklearn_installed", + "is_tqdm_installed", + "is_trimesh_installed", + "required", + "is_iterable", + "is_string", + "is_numeric", + "is_integer", + "is_sibling", + "filter_kwargs", + "filter_mapping", + "first_item", + "copy_definition", + "validate_method", + "optional", ] -def handle_numpy_errors(**kwargs): +class CacheRegistry: + """ + A registry for mapping-based caches. + + Attributes + ---------- + - :attr:`~colour.utilities.CacheRegistry.registry` + + Methods + ------- + - :meth:`~colour.SpectralShape.__init__` + - :meth:`~colour.SpectralShape.__str__` + - :meth:`~colour.SpectralShape.register_cache` + - :meth:`~colour.SpectralShape.unregister_cache` + - :meth:`~colour.SpectralShape.clear_cache` + - :meth:`~colour.SpectralShape.clear_all_caches` + + Examples + -------- + >>> cache_registry = CacheRegistry() + >>> cache_a = cache_registry.register_cache('Cache A') + >>> cache_a['Foo'] = 'Bar' + >>> cache_b = cache_registry.register_cache('Cache B') + >>> cache_b['John'] = 'Doe' + >>> cache_b['Luke'] = 'Skywalker' + >>> print(cache_registry) + {'Cache A': '1 item(s)', 'Cache B': '2 item(s)'} + >>> cache_registry.clear_cache('Cache A') + >>> print(cache_registry) + {'Cache A': '0 item(s)', 'Cache B': '2 item(s)'} + >>> cache_registry.unregister_cache('Cache B') + >>> print(cache_registry) + {'Cache A': '0 item(s)'} + >>> print(cache_b) + {} + """ + + def __init__(self): + self._registry = {} + + @property + def registry(self) -> Dict: + """ + Getter property for the cache registry. + + Returns + ------- + :class:`dict` + Cache registry. + """ + + return self._registry + + def __str__(self) -> str: + """ + Return a formatted string representation of the cache registry. + + Returns + ------- + :class:`str` + Formatted string representation. + """ + + return pformat( + { + name: f"{len(self._registry[name])} item(s)" + for name in sorted(self._registry) + } + ) + + def register_cache(self, name: str) -> Dict: + """ + Register a new cache with given name in the registry. + + Parameters + ---------- + name + Cache name for the registry. + + Returns + ------- + :class:`dict` + Registered cache. + + Examples + -------- + >>> cache_registry = CacheRegistry() + >>> cache_a = cache_registry.register_cache('Cache A') + >>> cache_a['Foo'] = 'Bar' + >>> cache_b = cache_registry.register_cache('Cache B') + >>> cache_b['John'] = 'Doe' + >>> cache_b['Luke'] = 'Skywalker' + >>> print(cache_registry) + {'Cache A': '1 item(s)', 'Cache B': '2 item(s)'} + """ + + self._registry[name] = {} + + return self._registry[name] + + def unregister_cache(self, name: str): + """ + Unregister cache with given name in the registry. + + Parameters + ---------- + name + Cache name in the registry. + + Notes + ----- + - The cache is cleared before being unregistered. + + Examples + -------- + >>> cache_registry = CacheRegistry() + >>> cache_a = cache_registry.register_cache('Cache A') + >>> cache_a['Foo'] = 'Bar' + >>> cache_b = cache_registry.register_cache('Cache B') + >>> cache_b['John'] = 'Doe' + >>> cache_b['Luke'] = 'Skywalker' + >>> print(cache_registry) + {'Cache A': '1 item(s)', 'Cache B': '2 item(s)'} + >>> cache_registry.unregister_cache('Cache B') + >>> print(cache_registry) + {'Cache A': '1 item(s)'} + >>> print(cache_b) + {} + """ + + self.clear_cache(name) + + del self._registry[name] + + def clear_cache(self, name: str): + """ + Clear the cache with given name. + + Parameters + ---------- + name + Cache name in the registry. + + Examples + -------- + >>> cache_registry = CacheRegistry() + >>> cache_a = cache_registry.register_cache('Cache A') + >>> cache_a['Foo'] = 'Bar' + >>> print(cache_registry) + {'Cache A': '1 item(s)'} + >>> cache_registry.clear_cache('Cache A') + >>> print(cache_registry) + {'Cache A': '0 item(s)'} + """ + + self._registry[name].clear() + + def clear_all_caches(self): + """ + Clear all the caches in the registry. + + Examples + -------- + >>> cache_registry = CacheRegistry() + >>> cache_a = cache_registry.register_cache('Cache A') + >>> cache_a['Foo'] = 'Bar' + >>> cache_b = cache_registry.register_cache('Cache B') + >>> cache_b['John'] = 'Doe' + >>> cache_b['Luke'] = 'Skywalker' + >>> print(cache_registry) + {'Cache A': '1 item(s)', 'Cache B': '2 item(s)'} + >>> cache_registry.clear_all_caches() + >>> print(cache_registry) + {'Cache A': '0 item(s)', 'Cache B': '0 item(s)'} + """ + + for key in self._registry: + self.clear_cache(key) + + +CACHE_REGISTRY: CacheRegistry = CacheRegistry() +""" +*Colour* cache registry referencing all the caches used for repetitive or long +processes. + +CACHE_REGISTRY +""" + + +def handle_numpy_errors(**kwargs: Any) -> Callable: """ - Decorator for handling *Numpy* errors. + Decorate a function to handle *Numpy* errors. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs Keywords arguments. Returns ------- - object + Callable References ---------- @@ -83,16 +306,12 @@ def handle_numpy_errors(**kwargs): context = np.errstate(**kwargs) - def wrapper(function): - """ - Wrapper for given function. - """ + def wrapper(function: Callable) -> Callable: + """Wrap given function wrapper.""" @functools.wraps(function) - def wrapped(*args, **kwargs): - """ - Wrapped function. - """ + def wrapped(*args: Any, **kwargs: Any) -> Any: + """Wrap given function.""" with context: return function(*args, **kwargs) @@ -102,24 +321,24 @@ def wrapped(*args, **kwargs): return wrapper -ignore_numpy_errors = handle_numpy_errors(all='ignore') -raise_numpy_errors = handle_numpy_errors(all='raise') -print_numpy_errors = handle_numpy_errors(all='print') -warn_numpy_errors = handle_numpy_errors(all='warn') +ignore_numpy_errors = handle_numpy_errors(all="ignore") +raise_numpy_errors = handle_numpy_errors(all="raise") +print_numpy_errors = handle_numpy_errors(all="print") +warn_numpy_errors = handle_numpy_errors(all="warn") -def ignore_python_warnings(function): +def ignore_python_warnings(function: Callable) -> Callable: """ - Decorator for ignoring *Python* warnings. + Decorate a function to ignore *Python* warnings. Parameters ---------- - function : object + function Function to decorate. Returns ------- - object + Callable Examples -------- @@ -130,62 +349,74 @@ def ignore_python_warnings(function): """ @functools.wraps(function) - def wrapped(*args, **kwargs): - """ - Wrapped function. - """ + def wrapper(*args: Any, **kwargs: Any) -> Any: + """Wrap given function.""" with warnings.catch_warnings(): - warnings.simplefilter('ignore') + warnings.simplefilter("ignore") return function(*args, **kwargs) - return wrapped + return wrapper -def batch(iterable, k=3): +def attest(condition: Boolean, message: str = ""): """ - Returns a batch generator from given iterable. + Provide the `assert` statement functionality without being disabled by + optimised Python execution. Parameters ---------- - iterable : iterable - Iterable to create batches from. - k : integer - Batches size. + condition + Condition to attest/assert. + message + Message to display when the assertion fails. + """ - Returns - ------- - bool - Is *string_like* variable. + if not condition: + raise AssertionError(message) + + +def batch(sequence: Sequence, k: Union[Integer, Literal[3]] = 3) -> Generator: + """ + Return a batch generator from given sequence. + + Parameters + ---------- + sequence + Sequence to create batches from. + k + Batch size. + + Yields + ------ + Generator + Batch generator. Examples -------- - >>> batch(tuple(range(10))) # doctest: +ELLIPSIS + >>> batch(tuple(range(10)), 3) # doctest: +ELLIPSIS """ - for i in range(0, len(iterable), k): - yield iterable[i:i + k] + for i in range(0, len(sequence), k): + yield sequence[i : i + k] -_MULTIPROCESSING_ENABLED = True -""" -Whether *Colour* multiprocessing is enabled. +_MULTIPROCESSING_ENABLED: Boolean = True +"""*Colour* multiprocessing state.""" -_MULTIPROCESSING_ENABLED : bool -""" - -class disable_multiprocessing(object): +class disable_multiprocessing: """ - A context manager and decorator temporarily disabling *Colour* - multiprocessing. + Define a context manager and decorator to temporarily disabling *Colour* + multiprocessing state. """ - def __enter__(self): + def __enter__(self) -> disable_multiprocessing: """ - Called upon entering the context manager and decorator. + Disable *Colour* multiprocessing state upon entering the context + manager. """ global _MULTIPROCESSING_ENABLED @@ -194,59 +425,69 @@ def __enter__(self): return self - def __exit__(self, *args): + def __exit__(self, *args: Any): """ - Called upon exiting the context manager and decorator. + Enable *Colour* multiprocessing state upon exiting the context + manager. """ global _MULTIPROCESSING_ENABLED _MULTIPROCESSING_ENABLED = True - def __call__(self, function): - """ - Calls the wrapped definition. - """ + def __call__(self, function: Callable) -> Callable: + """Call the wrapped definition.""" @functools.wraps(function) - def wrapper(*args, **kwargs): + def wrapper(*args: Any, **kwargs: Any) -> Any: + """Wrap given function.""" + with self: return function(*args, **kwargs) return wrapper -def _initializer(kwargs): +def _initializer(kwargs: Any): """ - Initializer for the multiprocessing pool. It is mainly use to ensure that - processes on *Windows* correctly inherit from the current domain-range - scale. + Initialize a multiprocessing pool. + + It is used to ensure that processes on *Windows* inherit correctly from the + current domain-range scale. Parameters ---------- - kwargs : dict + kwargs Initialisation arguments. """ - global _DOMAIN_RANGE_SCALE - # NOTE: No coverage information is available as this code is executed in # sub-processes. - _DOMAIN_RANGE_SCALE = kwargs.get('scale', 'reference') # pragma: no cover + + import colour.utilities.array # pragma: no cover + + colour.utilities.array._DOMAIN_RANGE_SCALE = kwargs.get( + "scale", "reference" + ) # pragma: no cover @contextmanager -def multiprocessing_pool(*args, **kwargs): +def multiprocessing_pool(*args: Any, **kwargs: Any) -> Generator: """ - A context manager providing a multiprocessing pool. + Define a context manager providing a multiprocessing pool. Other Parameters ---------------- - \\*args : list, optional + args Arguments. - \\**kwargs : dict, optional + kwargs Keywords arguments. + Yields + ------ + Generator + Multiprocessing pool. + Examples -------- >>> from functools import partial @@ -258,38 +499,37 @@ def multiprocessing_pool(*args, **kwargs): [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] """ - class _DummyPool(object): + from colour.utilities import get_domain_range_scale + + class _DummyPool: """ A dummy multiprocessing pool that does not perform multiprocessing. Other Parameters ---------------- - \\*args : list, optional + args Arguments. - \\**kwargs : dict, optional + kwargs Keywords arguments. """ - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any): pass def map(self, func, iterable, chunksize=None): - """ - Applies given function to each element of given iterable. - """ + """Apply given function to each element of given iterable.""" return [func(a) for a in iterable] def terminate(self): - """ - Terminate the process. - """ + """Terminate the process.""" pass - kwargs['initializer'] = _initializer - kwargs['initargs'] = ({'scale': get_domain_range_scale()}, ) + kwargs["initializer"] = _initializer + kwargs["initargs"] = ({"scale": get_domain_range_scale()},) + pool_factory: Callable if _MULTIPROCESSING_ENABLED: pool_factory = multiprocessing.Pool else: @@ -303,23 +543,23 @@ def terminate(self): pool.terminate() -def is_matplotlib_installed(raise_exception=False): +def is_matplotlib_installed(raise_exception: Boolean = False) -> Boolean: """ - Returns if *Matplotlib* is installed and available. + Return whether *Matplotlib* is installed and available. Parameters ---------- - raise_exception : bool - Raise exception if *Matplotlib* is unavailable. + raise_exception + Whether to raise an exception if *Matplotlib* is unavailable. Returns ------- - bool - Is *Matplotlib* installed. + :class:`bool` + Whether *Matplotlib* is installed. Raises ------ - ImportError + :class:`ImportError` If *Matplotlib* is not installed. """ @@ -331,31 +571,31 @@ def is_matplotlib_installed(raise_exception=False): except ImportError as error: # pragma: no cover if raise_exception: raise ImportError( - ('"Matplotlib" related API features are not available: ' - '"{0}".\nPlease refer to the installation guide for more ' - 'information: ' - 'https://www.colour-science.org/installation-guide/' - ).format(error)) + '"Matplotlib" related API features are not available: ' + f'"{error}".\nSee the installation guide for more information: ' + "https://www.colour-science.org/installation-guide/" + ) + return False -def is_networkx_installed(raise_exception=False): +def is_networkx_installed(raise_exception: Boolean = False) -> Boolean: """ - Returns if *NetworkX* is installed and available. + Return whether *NetworkX* is installed and available. Parameters ---------- - raise_exception : bool - Raise exception if *NetworkX* is unavailable. + raise_exception + Whether to raise an exception if *NetworkX* is unavailable. Returns ------- - bool - Is *NetworkX* installed. + :class:`bool` + Whether *NetworkX* is installed. Raises ------ - ImportError + :class:`ImportError` If *NetworkX* is not installed. """ @@ -367,31 +607,68 @@ def is_networkx_installed(raise_exception=False): except ImportError as error: # pragma: no cover if raise_exception: raise ImportError( - ('"NetworkX" related API features, e.g. the automatic colour ' - 'conversion graph, are not available: "{0}".\nPlease refer ' - 'to the installation guide for more information: ' - 'https://www.colour-science.org/installation-guide/' - ).format(error)) + '"NetworkX" related API features, e.g. the automatic colour ' + f'conversion graph, are not available: "{error}".\nPlease refer ' + "to the installation guide for more information: " + "https://www.colour-science.org/installation-guide/" + ) + return False -def is_openimageio_installed(raise_exception=False): +def is_opencolorio_installed(raise_exception: Boolean = False) -> Boolean: """ - Returns if *OpenImageIO* is installed and available. + Return whether *OpenColorIO* is installed and available. Parameters ---------- - raise_exception : bool - Raise exception if *OpenImageIO* is unavailable. + raise_exception + Whether to raise an exception if *OpenColorIO* is unavailable. Returns ------- - bool - Is *OpenImageIO* installed. + :class:`bool` + Whether *OpenColorIO* is installed. Raises ------ - ImportError + :class:`ImportError` + If *OpenColorIO* is not installed. + """ + + try: # pragma: no cover + # pylint: disable=W0612 + import PyOpenColorIO # noqa + + return True + except ImportError as error: # pragma: no cover + if raise_exception: + raise ImportError( + '"OpenColorIO" related API features are not available: ' + f'"{error}".\nSee the installation guide for more information: ' + "https://www.colour-science.org/installation-guide/" + ) + + return False + + +def is_openimageio_installed(raise_exception: Boolean = False) -> Boolean: + """ + Return whether *OpenImageIO* is installed and available. + + Parameters + ---------- + raise_exception + Whether to raise an exception if *OpenImageIO* is unavailable. + + Returns + ------- + :class:`bool` + Whether *OpenImageIO* is installed. + + Raises + ------ + :class:`ImportError` If *OpenImageIO* is not installed. """ @@ -403,31 +680,31 @@ def is_openimageio_installed(raise_exception=False): except ImportError as error: # pragma: no cover if raise_exception: raise ImportError( - ('"OpenImageIO" related API features are not available: ' - '"{0}".\nPlease refer to the installation guide for more ' - 'information: ' - 'https://www.colour-science.org/installation-guide/' - ).format(error)) + '"OpenImageIO" related API features are not available: ' + f'"{error}".\nSee the installation guide for more information: ' + "https://www.colour-science.org/installation-guide/" + ) + return False -def is_pandas_installed(raise_exception=False): +def is_pandas_installed(raise_exception: Boolean = False) -> Boolean: """ - Returns if *Pandas* is installed and available. + Return whether *Pandas* is installed and available. Parameters ---------- - raise_exception : bool - Raise exception if *Pandas* is unavailable. + raise_exception + Whether to raise an exception if *Pandas* is unavailable. Returns ------- - bool - Is *Pandas* installed. + :class:`bool` + Whether *Pandas* is installed. Raises ------ - ImportError + :class:`ImportError` If *Pandas* is not installed. """ @@ -439,31 +716,68 @@ def is_pandas_installed(raise_exception=False): except ImportError as error: # pragma: no cover if raise_exception: raise ImportError( - ('"Pandas" related API features are not available: "{0}".\n' - 'Please refer to the installation guide for more ' - 'information: ' - 'https://www.colour-science.org/installation-guide/' - ).format(error)) + f'"Pandas" related API features are not available: "{error}".\n' + "See the installation guide for more information: " + "https://www.colour-science.org/installation-guide/" + ) + + return False + + +def is_sklearn_installed(raise_exception: Boolean = False) -> Boolean: + """ + Return whether *Scikit-Learn* (sklearn) is installed and available. + + Parameters + ---------- + raise_exception + Whether to raise an exception if *Scikit-Learn* (sklearn) is + unavailable. + + Returns + ------- + :class:`bool` + Whether *Scikit- isLearn* (sklearn) installed. + + Raises + ------ + :class:`ImportError` + If *Scikit-Learn* (sklearn) is not installed. + """ + + try: # pragma: no cover + # pylint: disable=W0612 + import sklearn # noqa + + return True + except ImportError as error: # pragma: no cover + if raise_exception: + raise ImportError( + '"Scikit-Learn" related API features are not available: ' + f'"{error}".\nSee the installation guide for more information: ' + "https://www.colour-science.org/installation-guide/" + ) + return False -def is_tqdm_installed(raise_exception=False): +def is_tqdm_installed(raise_exception: Boolean = False) -> Boolean: """ - Returns if *tqdm* is installed and available. + Return whether *tqdm* is installed and available. Parameters ---------- - raise_exception : bool - Raise exception if *tqdm* is unavailable. + raise_exception + Whether to raise an exception if *tqdm* is unavailable. Returns ------- - bool - Is *tqdm* installed. + :class:`bool` + Whether *tqdm* is installed. Raises ------ - ImportError + :class:`ImportError` If *tqdm* is not installed. """ @@ -475,54 +789,103 @@ def is_tqdm_installed(raise_exception=False): except ImportError as error: # pragma: no cover if raise_exception: raise ImportError( - ('"tqdm" related API features are not available: "{0}".\n' - 'Please refer to the installation guide for more ' - 'information: ' - 'https://www.colour-science.org/installation-guide/' - ).format(error)) + f'"tqdm" related API features are not available: "{error}".\n' + "See the installation guide for more information: " + "https://www.colour-science.org/installation-guide/" + ) + + return False + + +def is_trimesh_installed(raise_exception: Boolean = False) -> Boolean: + """ + Return whether *Trimesh* is installed and available. + + Parameters + ---------- + raise_exception + Whether to raise an exception if *Trimesh* is unavailable. + + Returns + ------- + :class:`bool` + Whether *Trimesh* is installed. + + Raises + ------ + :class:`ImportError` + If *Trimesh* is not installed. + """ + + try: # pragma: no cover + # pylint: disable=W0612 + import trimesh # noqa + + return True + except ImportError as error: # pragma: no cover + if raise_exception: + raise ImportError( + '"Trimesh" related API features are not available: ' + f'"{error}".\nSee the installation guide for more information: ' + "https://www.colour-science.org/installation-guide/" + ) + return False -_REQUIREMENTS_TO_CALLABLE = CaseInsensitiveMapping({ - 'Matplotlib': is_matplotlib_installed, - 'NetworkX': is_networkx_installed, - 'OpenImageIO': is_openimageio_installed, - 'Pandas': is_pandas_installed, - 'tqdm': is_tqdm_installed, -}) +_REQUIREMENTS_TO_CALLABLE: CaseInsensitiveMapping = CaseInsensitiveMapping( + { + "Matplotlib": is_matplotlib_installed, + "NetworkX": is_networkx_installed, + "OpenColorIO": is_opencolorio_installed, + "OpenImageIO": is_openimageio_installed, + "Pandas": is_pandas_installed, + "Scikit-Learn": is_sklearn_installed, + "tqdm": is_tqdm_installed, + "trimesh": is_trimesh_installed, + } +) """ Mapping of requirements to their respective callables. -_REQUIREMENTS_TO_CALLABLE : CaseInsensitiveMapping - **{'Matplotlib', 'NetworkX', 'OpenImageIO', 'Pandas', 'tqdm'}** +_REQUIREMENTS_TO_CALLABLE + **{'Matplotlib', 'NetworkX', 'OpenColorIO', 'OpenImageIO', 'Pandas', + 'Scikit-Learn', 'tqdm', 'trimesh'}** """ -def required(*requirements): +def required( + *requirements: Literal[ + "Matplotlib", + "NetworkX", + "OpenColorIO", + "OpenImageIO", + "Pandas", + "Scikit-Learn", + "tqdm", + "trimesh", + ] +) -> Callable: """ - A decorator checking if various requirements are satisfied. + Decorate a function to check whether various ancillary package requirements + are satisfied. Other Parameters ---------------- - \\*requirements : list, optional - **{'Matplotlib', 'NetworkX', 'OpenImageIO', 'Pandas', 'tqdm'}**, + requirements Requirements to check whether they are satisfied. Returns ------- - object + Callable """ - def wrapper(function): - """ - Wrapper for given function. - """ + def wrapper(function: Callable) -> Callable: + """Wrap given function wrapper.""" @functools.wraps(function) - def wrapped(*args, **kwargs): - """ - Wrapped function. - """ + def wrapped(*args: Any, **kwargs: Any) -> Any: + """Wrap given function.""" for requirement in requirements: _REQUIREMENTS_TO_CALLABLE[requirement](raise_exception=True) @@ -534,19 +897,19 @@ def wrapped(*args, **kwargs): return wrapper -def is_iterable(a): +def is_iterable(a: Any) -> Boolean: """ - Returns if given :math:`a` variable is iterable. + Return whether given variable :math:`a` is iterable. Parameters ---------- - a : object - Variable to check the iterability. + a + Variable :math:`a` to check the iterability. Returns ------- - bool - :math:`a` variable iterability. + :class:`bool` + Whether variable :math:`a` is iterable. Examples -------- @@ -556,22 +919,22 @@ def is_iterable(a): False """ - return is_string(a) or (True if getattr(a, '__iter__', False) else False) + return is_string(a) or (True if getattr(a, "__iter__", False) else False) -def is_string(a): +def is_string(a: Any) -> Boolean: """ - Returns if given :math:`a` variable is a *string* like variable. + Return whether given variable :math:`a` is a :class:`str`-like variable. Parameters ---------- - a : object - Data to test. + a + Variable :math:`a` to test. Returns ------- - bool - Is :math:`a` variable a *string* like variable. + :class:`bool` + Whether variable :math:`a` is a :class:`str`-like variable. Examples -------- @@ -581,22 +944,23 @@ def is_string(a): False """ - return True if isinstance(a, string_types) else False + return True if isinstance(a, str) else False -def is_numeric(a): +def is_numeric(a: Any) -> Boolean: """ - Returns if given :math:`a` variable is a number. + Return whether given variable :math:`a` is a :class:`Number`-like + variable. Parameters ---------- - a : object - Variable to check. + a + Variable :math:`a` to test. Returns ------- - bool - Is :math:`a` variable a number. + :class:`bool` + Whether variable :math:`a` is a :class:`Number`-like variable. Examples -------- @@ -608,24 +972,30 @@ def is_numeric(a): return isinstance( a, - tuple( - list(integer_types) + - [float, complex, np.integer, np.floating, np.complex])) + ( + int, + float, + complex, + np.integer, + np.floating, + ), + ) -def is_integer(a): +def is_integer(a: Any) -> Boolean: """ - Returns if given :math:`a` variable is an integer under given threshold. + Return whether given variable :math:`a` is an :class:`numpy.integer`-like + variable under given threshold. Parameters ---------- - a : object - Variable to check. + a + Variable :math:`a` to test. Returns ------- - bool - Is :math:`a` variable an integer. + :class:`bool` + Whether variable :math:`a` is an :class:`numpy.integer`-like variable. Notes ----- @@ -643,39 +1013,40 @@ def is_integer(a): return abs(a - np.around(a)) <= INTEGER_THRESHOLD -def is_sibling(element, mapping): +def is_sibling(element: Any, mapping: Mapping) -> Boolean: """ - Returns whether given element type is present in given mapping types. + Return whether given element type is present in given mapping types. Parameters ---------- - element : object - Element to check if its type is present in the mapping types. - mapping : dict - Mapping. + element + Element to check whether its type is present in the mapping types. + mapping + Mapping types. Returns ------- - bool + :class:`bool` Whether given element type is present in given mapping types. """ return isinstance( - element, tuple(set(type(element) for element in mapping.values()))) + element, tuple({type(element) for element in mapping.values()}) + ) -def filter_kwargs(function, **kwargs): +def filter_kwargs(function: Callable, **kwargs: Any) -> Dict: """ - Filters keyword arguments incompatible with the given function signature. + Filter keyword arguments incompatible with the given function signature. Parameters ---------- - function : callable + function Callable to filter the incompatible keyword arguments. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs Keywords arguments. Returns @@ -683,12 +1054,6 @@ def filter_kwargs(function, **kwargs): dict Filtered keyword arguments. - Warnings - -------- - Python 2.7 does not support inspecting the signature of *partial* - functions, this could cause unexpected behaviour when using this - definition. - Examples -------- >>> def fn_a(a): @@ -707,54 +1072,51 @@ def filter_kwargs(function, **kwargs): kwargs = copy(kwargs) - # TODO: Remove when dropping Python 2.7. - if six.PY2: # pragma: no cover - try: - args, _varargs, _keywords, _defaults = inspect.getargspec(function) - except (TypeError, ValueError): - return {} - else: # pragma: no cover - try: - args = list(inspect.signature(function).parameters.keys()) - except ValueError: - return {} - - args = set(kwargs.keys()) - set(args) - for key in args: + try: + args = list(inspect.signature(function).parameters.keys()) + except ValueError: # pragma: no cover + return {} + + for key in set(kwargs.keys()) - set(args): kwargs.pop(key) return kwargs -def filter_mapping(mapping, filterers, anchors=True, flags=re.IGNORECASE): +def filter_mapping( + mapping: Mapping, + filterers: Union[str, Sequence[str]], + anchors: Boolean = True, + flags: Union[Integer, RegexFlag] = re.IGNORECASE, +) -> Dict: """ - Filters given mapping with given filterers. + Filter given mapping with given filterers. Parameters ---------- - mapping : dict_like + mapping Mapping to filter. - filterers : unicode or object or array_like + filterers Filterer pattern for given mapping elements or a list of filterers. - anchors : bool, optional + anchors Whether to use Regex line anchors, i.e. *^* and *$* are added, surrounding the filterer pattern. - flags : int, optional + flags Regex flags. Returns ------- - OrderedDict + dict Filtered mapping elements. Notes ----- - To honour the filterers ordering, the return value is an - :class:`OrderedDict` class instance. + :class:`dict` class instance. Examples -------- - >>> class Element(object): + >>> class Element: ... pass >>> mapping = { ... 'Element A': Element(), @@ -762,78 +1124,84 @@ def filter_mapping(mapping, filterers, anchors=True, flags=re.IGNORECASE): ... 'Element C': Element(), ... 'Not Element C': Element(), ... } - >>> # Doctests skip for Python 2.x compatibility. - >>> filter_mapping(mapping, '\\w+\\s+A') # doctest: +SKIP - {u'Element A': } - >>> # Doctests skip for Python 2.x compatibility. - >>> sorted(filter_mapping(mapping, 'Element.*')) # doctest: +SKIP - [u'Element A', u'Element B', u'Element C'] - """ - - def filter_mapping_with_filter(mapping, filterer, anchors, flags): + >>> filter_mapping(mapping, '\\w+\\s+A') # doctest: +ELLIPSIS + {'Element A': } + >>> sorted(filter_mapping(mapping, 'Element.*')) + ['Element A', 'Element B', 'Element C'] + """ + + def filter_mapping_with_filter( + mapping: Mapping, + filterer: str, + anchors: Boolean = True, + flags: Union[Integer, RegexFlag] = re.IGNORECASE, + ) -> Dict: """ - Filters given mapping with given filterer. + Filter given mapping with given filterer. Parameters ---------- - mapping : dict_like + mapping Mapping to filter. - filterer : unicode or object + filterer Filterer pattern for given mapping elements. - anchors : bool, optional + anchors Whether to use Regex line anchors, i.e. *^* and *$* are added, surrounding the filterer pattern. - flags : int, optional + flags Regex flags. Returns ------- - OrderedDict + dict Filtered mapping elements. """ if anchors: - filterer = '^{0}$'.format(filterer) - filterer = filterer.replace('^^', '^').replace('$$', '$') + filterer = f"^{filterer}$" + filterer = filterer.replace("^^", "^").replace("$$", "$") elements = [ - mapping[element] for element in mapping + mapping[element] + for element in mapping if re.match(filterer, element, flags) ] lookup = Lookup(mapping) - return OrderedDict((lookup.first_key_from_value(element), element) - for element in elements) + return { + lookup.first_key_from_value(element): element + for element in elements + } - if is_string(filterers): - filterers = [filterers] + filterers = [str(filterers)] if is_string(filterers) else filterers - filtered_mapping = OrderedDict() + filtered_mapping = {} for filterer in filterers: filtered_mapping.update( - filter_mapping_with_filter(mapping, filterer, anchors, flags)) + filter_mapping_with_filter(mapping, filterer, anchors, flags) + ) return filtered_mapping -def first_item(a): +def first_item(a: Iterable) -> Any: """ - Return the first item of an iterable. + Return the first item of given iterable. Parameters ---------- - a : object + a Iterable to get the first item from. Returns ------- - object + :class:`object` Raises ------ - StopIteration + :class:`StopIteration` If the iterable is empty. Examples @@ -846,790 +1214,111 @@ def first_item(a): return next(iter(a)) -_DOMAIN_RANGE_SCALE = 'reference' -""" -Global variable storing the current *Colour* domain-range scale. - -_DOMAIN_RANGE_SCALE : unicode -""" - - -def get_domain_range_scale(): - """ - Returns the current *Colour* domain-range scale. The following scales are - available: - - - **'Reference'**, the default *Colour* domain-range scale which varies - depending on the referenced algorithm, e.g. [0, 1], [0, 10], [0, 100], - [0, 255], etc... - - **'1'**, a domain-range scale normalised to [0, 1], it is important to - acknowledge that this is a soft normalisation and it is possible to - use negative out of gamut values or high dynamic range data exceeding - 1. - - Returns - ------- - unicode - *Colour* domain-range scale. - """ - - return _DOMAIN_RANGE_SCALE - - -def set_domain_range_scale(scale='Reference'): - """ - Sets the current *Colour* domain-range scale. The following scales are - available: - - - **'Reference'**, the default *Colour* domain-range scale which varies - depending on the referenced algorithm, e.g. [0, 1], [0, 10], [0, 100], - [0, 255], etc... - - **'1'**, a domain-range scale normalised to [0, 1], it is important to - acknowledge that this is a soft normalisation and it is possible to - use negative out of gamut values or high dynamic range data exceeding - 1. - - Parameters - ---------- - scale : unicode or int - **{'Reference', '1'}**, - *Colour* domain-range scale to set. - """ - - global _DOMAIN_RANGE_SCALE - - scale = str(scale).lower() - valid = ('1', '100', 'reference', 'ignore') - assert scale in valid, 'Scale must be one of "{0}".'.format(valid) - - _DOMAIN_RANGE_SCALE = scale - - -class domain_range_scale(object): - """ - A context manager and decorator temporarily setting *Colour* domain-range - scale. The following scales are available: - - - **'Reference'**, the default *Colour* domain-range scale which varies - depending on the referenced algorithm, e.g. [0, 1], [0, 10], [0, 100], - [0, 255], etc... - - **'1'**, a domain-range scale normalised to [0, 1], it is important to - acknowledge that this is a soft normalisation and it is possible to - use negative out of gamut values or high dynamic range data exceeding - 1. - - Parameters - ---------- - scale : unicode - **{'Reference', '1'}**, - *Colour* domain-range scale to set. - - Examples - -------- - With *Colour* domain-range scale set to **'Reference'**: - - >>> with domain_range_scale('1'): - ... to_domain_1(1) - array(1.0) - >>> with domain_range_scale('Reference'): - ... from_range_1(1) - 1 - - With *Colour* domain-range scale set to **'1'**: - - >>> with domain_range_scale('1'): - ... to_domain_1(1) - array(1.0) - >>> with domain_range_scale('1'): - ... from_range_1(1) - 1 - - With *Colour* domain-range scale set to **'100'** (unsupported): - - >>> with domain_range_scale('100'): - ... to_domain_1(1) - array(0.01) - >>> with domain_range_scale('100'): - ... from_range_1(1) - 100 - """ - - def __init__(self, scale): - self._scale = scale - self._previous_scale = get_domain_range_scale() - - def __enter__(self): - """ - Called upon entering the context manager and decorator. - """ - - set_domain_range_scale(self._scale) - - return self - - def __exit__(self, *args): - """ - Called upon exiting the context manager and decorator. - """ - - set_domain_range_scale(self._previous_scale) - - def __call__(self, function): - """ - Calls the wrapped definition. - """ - - @functools.wraps(function) - def wrapper(*args, **kwargs): - with self: - return function(*args, **kwargs) - - return wrapper - - -def to_domain_1(a, scale_factor=100, dtype=None): - """ - Scales given array :math:`a` to domain **'1'**. The behaviour is as - follows: - - - If *Colour* domain-range scale is **'Reference'** or **'1'**, the - definition is almost entirely by-passed and will just conveniently - convert array :math:`a` to :class:`np.ndarray`. - - If *Colour* domain-range scale is **'100'** (currently unsupported - private value only used for unit tests), array :math:`a` is divided by - ``scale_factor``, typically 100. - - Parameters - ---------- - a : array_like - :math:`a` to scale to domain **'1'**. - scale_factor : numeric or array_like, optional - Scale factor, usually *numeric* but can be an *array_like* if some - axis need different scaling to be brought to domain **'1'**. - dtype : object, optional - Data type used for the conversion to :class:`np.ndarray`. - - Returns - ------- - ndarray - :math:`a` scaled to domain **'1'**. - - Examples - -------- - With *Colour* domain-range scale set to **'Reference'**: - - >>> with domain_range_scale('Reference'): - ... to_domain_1(1) - array(1.0) - - With *Colour* domain-range scale set to **'1'**: - - >>> with domain_range_scale('1'): - ... to_domain_1(1) - array(1.0) - - With *Colour* domain-range scale set to **'100'** (unsupported): - - >>> with domain_range_scale('100'): - ... to_domain_1(1) - array(0.01) - """ - - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE - - a = np.asarray(a, dtype).copy() - - if _DOMAIN_RANGE_SCALE == '100': - a /= scale_factor - - return a - - -def to_domain_10(a, scale_factor=10, dtype=None): - """ - Scales given array :math:`a` to domain **'10'**, used by - *Munsell Renotation System*. The behaviour is as follows: - - - If *Colour* domain-range scale is **'Reference'**, the - definition is almost entirely by-passed and will just conveniently - convert array :math:`a` to :class:`np.ndarray`. - - If *Colour* domain-range scale is **'1'**, array :math:`a` is - multiplied by ``scale_factor``, typically 10. - - If *Colour* domain-range scale is **'100'** (currently unsupported - private value only used for unit tests), array :math:`a` is - divided by ``scale_factor``, typically 10. - - Parameters - ---------- - a : array_like - :math:`a` to scale to domain **'10'**. - scale_factor : numeric or array_like, optional - Scale factor, usually *numeric* but can be an *array_like* if some - axis need different scaling to be brought to domain **'10'**. - dtype : object, optional - Data type used for the conversion to :class:`np.ndarray`. - - Returns - ------- - ndarray - :math:`a` scaled to domain **'10'**. - - Examples - -------- - With *Colour* domain-range scale set to **'Reference'**: - - >>> with domain_range_scale('Reference'): - ... to_domain_10(1) - array(1.0) - - With *Colour* domain-range scale set to **'1'**: - - >>> with domain_range_scale('1'): - ... to_domain_10(1) - array(10.0) - - With *Colour* domain-range scale set to **'100'** (unsupported): - - >>> with domain_range_scale('100'): - ... to_domain_10(1) - array(0.1) - """ - - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE - - a = np.asarray(a, dtype).copy() - - if _DOMAIN_RANGE_SCALE == '1': - a *= scale_factor - - if _DOMAIN_RANGE_SCALE == '100': - a /= scale_factor - - return a - - -def to_domain_100(a, scale_factor=100, dtype=None): - """ - Scales given array :math:`a` to domain **'100'**. The behaviour is as - follows: - - - If *Colour* domain-range scale is **'Reference'** or **'100'** - (currently unsupported private value only used for unit tests), the - definition is almost entirely by-passed and will just conveniently - convert array :math:`a` to :class:`np.ndarray`. - - If *Colour* domain-range scale is **'1'**, array :math:`a` is - multiplied by ``scale_factor``, typically 100. - - Parameters - ---------- - a : array_like - :math:`a` to scale to domain **'100'**. - scale_factor : numeric or array_like, optional - Scale factor, usually *numeric* but can be an *array_like* if some - axis need different scaling to be brought to domain **'100'**. - dtype : object, optional - Data type used for the conversion to :class:`np.ndarray`. - - Returns - ------- - ndarray - :math:`a` scaled to domain **'100'**. - - Examples - -------- - With *Colour* domain-range scale set to **'Reference'**: - - >>> with domain_range_scale('Reference'): - ... to_domain_100(1) - array(1.0) - - With *Colour* domain-range scale set to **'1'**: - - >>> with domain_range_scale('1'): - ... to_domain_100(1) - array(100.0) - - With *Colour* domain-range scale set to **'100'** (unsupported): - - >>> with domain_range_scale('100'): - ... to_domain_100(1) - array(1.0) - """ - - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE - - a = np.asarray(a, dtype).copy() - - if _DOMAIN_RANGE_SCALE == '1': - a *= scale_factor - - return a - - -def to_domain_degrees(a, scale_factor=360, dtype=None): - """ - Scales given array :math:`a` to degrees domain. The behaviour is as - follows: - - - If *Colour* domain-range scale is **'Reference'**, the - definition is almost entirely by-passed and will just conveniently - convert array :math:`a` to :class:`np.ndarray`. - - If *Colour* domain-range scale is **'1'**, array :math:`a` is - multiplied by ``scale_factor``, typically 360. - - If *Colour* domain-range scale is **'100'** (currently unsupported - private value only used for unit tests), array :math:`a` is - multiplied by ``scale_factor`` / 100, typically 360 / 100. - - Parameters - ---------- - a : array_like - :math:`a` to scale to degrees domain. - scale_factor : numeric or array_like, optional - Scale factor, usually *numeric* but can be an *array_like* if some - axis need different scaling to be brought to degrees domain. - dtype : object, optional - Data type used for the conversion to :class:`np.ndarray`. - - Returns - ------- - ndarray - :math:`a` scaled to degrees domain. - - Examples - -------- - With *Colour* domain-range scale set to **'Reference'**: - - >>> with domain_range_scale('Reference'): - ... to_domain_degrees(1) - array(1.0) - - With *Colour* domain-range scale set to **'1'**: - - >>> with domain_range_scale('1'): - ... to_domain_degrees(1) - array(360.0) - - With *Colour* domain-range scale set to **'100'** (unsupported): - - >>> with domain_range_scale('100'): - ... to_domain_degrees(1) - array(3.6) - """ - - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE - - a = np.asarray(a, dtype).copy() - - if _DOMAIN_RANGE_SCALE == '1': - a *= scale_factor - - if _DOMAIN_RANGE_SCALE == '100': - a *= scale_factor / 100 - - return a - - -def to_domain_int(a, bit_depth=8, dtype=None): - """ - Scales given array :math:`a` to int domain. The behaviour is as follows: - - - If *Colour* domain-range scale is **'Reference'**, the - definition is almost entirely by-passed and will just conveniently - convert array :math:`a` to :class:`np.ndarray`. - - If *Colour* domain-range scale is **'1'**, array :math:`a` is - multiplied by :math:`2^{bit\\_depth} - 1`. - - If *Colour* domain-range scale is **'100'** (currently unsupported - private value only used for unit tests), array :math:`a` is - multiplied by :math:`2^{bit\\_depth} - 1`. - - Parameters - ---------- - a : array_like - :math:`a` to scale to int domain. - bit_depth : numeric or array_like, optional - Bit depth, usually *int* but can be an *array_like* if some axis need - different scaling to be brought to int domain. - dtype : object, optional - Data type used for the conversion to :class:`np.ndarray`. - - Returns - ------- - ndarray - :math:`a` scaled to int domain. - - Notes - ----- - - To avoid precision issues and rounding, the scaling is performed on - floating-point numbers. - - Examples - -------- - With *Colour* domain-range scale set to **'Reference'**: - - >>> with domain_range_scale('Reference'): - ... to_domain_int(1) - array(1.0) - - With *Colour* domain-range scale set to **'1'**: - - >>> with domain_range_scale('1'): - ... to_domain_int(1) - array(255.0) - - With *Colour* domain-range scale set to **'100'** (unsupported): - - >>> with domain_range_scale('100'): - ... to_domain_int(1) - array(2.55) - """ - - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE - - a = np.asarray(a, dtype).copy() - - maximum_code_value = 2 ** bit_depth - 1 - if _DOMAIN_RANGE_SCALE == '1': - a *= maximum_code_value - - if _DOMAIN_RANGE_SCALE == '100': - a *= maximum_code_value / 100 - - return a - - -def from_range_1(a, scale_factor=100): - """ - Scales given array :math:`a` from range **'1'**. The behaviour is as - follows: - - - If *Colour* domain-range scale is **'Reference'** or **'1'**, the - definition is entirely by-passed. - - If *Colour* domain-range scale is **'100'** (currently unsupported - private value only used for unit tests), array :math:`a` is multiplied - by ``scale_factor``, typically 100. - - Parameters - ---------- - a : array_like - :math:`a` to scale from range **'1'**. - scale_factor : numeric or array_like, optional - Scale factor, usually *numeric* but can be an *array_like* if some - axis need different scaling to be brought from range **'1'**. - - Returns - ------- - ndarray - :math:`a` scaled from range **'1'**. - - Warnings - -------- - The scale conversion of :math:`a` variable happens in-place, i.e. :math:`a` - will be mutated! - - Examples - -------- - With *Colour* domain-range scale set to **'Reference'**: - - >>> with domain_range_scale('Reference'): - ... from_range_1(1) - 1 - - With *Colour* domain-range scale set to **'1'**: - - >>> with domain_range_scale('1'): - ... from_range_1(1) - 1 - - With *Colour* domain-range scale set to **'100'** (unsupported): - - >>> with domain_range_scale('100'): - ... from_range_1(1) - 100 - """ - - if _DOMAIN_RANGE_SCALE == '100': - a *= scale_factor - - return a - - -def from_range_10(a, scale_factor=10): +def copy_definition( + definition: Callable, name: Optional[str] = None +) -> Callable: """ - Scales given array :math:`a` from range **'10'**, used by - *Munsell Renotation System*. The behaviour is as follows: - - - If *Colour* domain-range scale is **'Reference'**, the - definition is entirely by-passed. - - If *Colour* domain-range scale is **'1'**, array :math:`a` is - divided by ``scale_factor``, typically 10. - - If *Colour* domain-range scale is **'100'** (currently unsupported - private value only used for unit tests), array :math:`a` is - multiplied by ``scale_factor``, typically 10. + Copy a definition using the same code, globals, defaults, closure, and + name. Parameters ---------- - a : array_like - :math:`a` to scale from range **'10'**. - scale_factor : numeric or array_like, optional - Scale factor, usually *numeric* but can be an *array_like* if some - axis need different scaling to be brought from range **'10'**. + definition + Definition to be copied. + name + Optional definition copy name. Returns ------- - ndarray - :math:`a` scaled from range **'10'**. - - Warnings - -------- - The scale conversion of :math:`a` variable happens in-place, i.e. :math:`a` - will be mutated! - - Examples - -------- - With *Colour* domain-range scale set to **'Reference'**: - - >>> with domain_range_scale('Reference'): - ... from_range_10(1) - 1 - - With *Colour* domain-range scale set to **'1'**: - - >>> with domain_range_scale('1'): - ... from_range_10(1) - 0.1 - - With *Colour* domain-range scale set to **'100'** (unsupported): - - >>> with domain_range_scale('100'): - ... from_range_10(1) - 10 + Callable + Definition copy. """ - if _DOMAIN_RANGE_SCALE == '1': - a /= scale_factor - - if _DOMAIN_RANGE_SCALE == '100': - a *= scale_factor + copy = types.FunctionType( + definition.__code__, + definition.__globals__, # type: ignore[attr-defined] + str(name or definition.__name__), + definition.__defaults__, # type: ignore[attr-defined] + definition.__closure__, # type: ignore[attr-defined] + ) + copy.__dict__.update(definition.__dict__) - return a + return copy -def from_range_100(a, scale_factor=100): +def validate_method( + method: str, + valid_methods: Union[Sequence, Mapping], + message: str = '"{0}" method is invalid, it must be one of {1}!', +) -> str: """ - Scales given array :math:`a` from range **'100'**. The behaviour is as - follows: - - - If *Colour* domain-range scale is **'Reference'** or **'100'** - (currently unsupported private value only used for unit tests), the - definition is entirely by-passed. - - If *Colour* domain-range scale is **'1'**, array :math:`a` is - divided by ``scale_factor``, typically 100. + Validate whether given method exists in the given valid methods and + returns the method lower cased. Parameters ---------- - a : array_like - :math:`a` to scale from range **'100'**. - scale_factor : numeric or array_like, optional - Scale factor, usually *numeric* but can be an *array_like* if some - axis need different scaling to be brought from range **'100'**. + method + Method to validate. + valid_methods + Valid methods. + message + Message for the exception. Returns ------- - ndarray - :math:`a` scaled from range **'100'**. + :class:`str` + Method lower cased. - Warnings - -------- - The scale conversion of :math:`a` variable happens in-place, i.e. :math:`a` - will be mutated! + Raises + ------ + :class:`ValueError` + If the method does not exist. Examples -------- - With *Colour* domain-range scale set to **'Reference'**: - - >>> with domain_range_scale('Reference'): - ... from_range_100(1) - 1 - - With *Colour* domain-range scale set to **'1'**: - - >>> with domain_range_scale('1'): - ... from_range_100(1) - 0.01 - - With *Colour* domain-range scale set to **'100'** (unsupported): - - >>> with domain_range_scale('100'): - ... from_range_100(1) - 1 + >>> validate_method('Valid', ['Valid', 'Yes', 'Ok']) + 'valid' """ - if _DOMAIN_RANGE_SCALE == '1': - a /= scale_factor - - return a - + valid_methods = [str(valid_method) for valid_method in valid_methods] -def from_range_degrees(a, scale_factor=360): - """ - Scales given array :math:`a` from degrees range. The behaviour is as - follows: + method_lower = method.lower() + if method_lower not in [ + valid_method.lower() for valid_method in valid_methods + ]: + raise ValueError(message.format(method, valid_methods)) - - If *Colour* domain-range scale is **'Reference'**, the - definition is entirely by-passed. - - If *Colour* domain-range scale is **'1'**, array :math:`a` is - divided by ``scale_factor``, typically 360. - - If *Colour* domain-range scale is **'100'** (currently unsupported - private value only used for unit tests), array :math:`a` is - divided by ``scale_factor`` / 100, typically 360 / 100. + return method_lower - Parameters - ---------- - a : array_like - :math:`a` to scale from degrees range. - scale_factor : numeric or array_like, optional - Scale factor, usually *numeric* but can be an *array_like* if some - axis need different scaling to be brought from degrees range. - Warnings - -------- - The scale conversion of :math:`a` variable happens in-place, i.e. :math:`a` - will be mutated! +T = TypeVar("T") - Returns - ------- - ndarray - :math:`a` scaled from degrees range. - - Examples - -------- - With *Colour* domain-range scale set to **'Reference'**: - - >>> with domain_range_scale('Reference'): - ... from_range_degrees(1) - 1 - With *Colour* domain-range scale set to **'1'**: - - >>> with domain_range_scale('1'): - ... from_range_degrees(1) # doctest: +ELLIPSIS - 0.0027777... - - With *Colour* domain-range scale set to **'100'** (unsupported): - - >>> with domain_range_scale('100'): - ... from_range_degrees(1) # doctest: +ELLIPSIS - 0.2777777... - """ - - if _DOMAIN_RANGE_SCALE == '1': - a /= scale_factor - - if _DOMAIN_RANGE_SCALE == '100': - a /= scale_factor / 100 - - return a - - -def from_range_int(a, bit_depth=8, dtype=None): +def optional(value: Optional[T], default: T) -> T: """ - Scales given array :math:`a` from int range. The behaviour is as follows: - - - If *Colour* domain-range scale is **'Reference'**, the - definition is entirely by-passed. - - If *Colour* domain-range scale is **'1'**, array :math:`a` is converted - to :class:`np.ndarray` and divided by :math:`2^{bit\\_depth} - 1`. - - If *Colour* domain-range scale is **'100'** (currently unsupported - private value only used for unit tests), array :math:`a` is converted - to :class:`np.ndarray` and divided by :math:`2^{bit\\_depth} - 1`. + Handle optional argument value by providing a default value. Parameters ---------- - a : array_like - :math:`a` to scale from int range. - bit_depth : numeric or array_like, optional - Bit depth, usually *int* but can be an *array_like* if some axis need - different scaling to be brought from int range. - dtype : object, optional - Data type used for the conversion to :class:`np.ndarray`. + value + Optional argument value. + default + Default argument value if ``value`` is *None*. Returns ------- - ndarray - :math:`a` scaled from int range. - - Warnings - -------- - The scale conversion of :math:`a` variable happens in-place, i.e. :math:`a` - will be mutated! - - Notes - ----- - - To avoid precision issues and rounding, the scaling is performed on - floating-point numbers. + T + Argument value. Examples -------- - With *Colour* domain-range scale set to **'Reference'**: - - >>> with domain_range_scale('Reference'): - ... from_range_int(1) - 1 - - With *Colour* domain-range scale set to **'1'**: - - >>> with domain_range_scale('1'): - ... from_range_int(1) # doctest: +ELLIPSIS - array(0.0039215...) - - With *Colour* domain-range scale set to **'100'** (unsupported): - - >>> with domain_range_scale('100'): - ... from_range_int(1) # doctest: +ELLIPSIS - array(0.3921568...) + >>> optional('Foo', 'Bar') + 'Foo' + >>> optional(None, 'Bar') + 'Bar' """ - if dtype is None: - dtype = DEFAULT_FLOAT_DTYPE - - maximum_code_value = 2 ** bit_depth - 1 - if _DOMAIN_RANGE_SCALE == '1': - a = np.asarray(a, dtype) - a /= maximum_code_value - - if _DOMAIN_RANGE_SCALE == '100': - a = np.asarray(a, dtype) - a /= maximum_code_value / 100 - - return a - - -def copy_definition(definition, name=None): - """ - Copies a definition with same code, globals, defaults, closure, and - name. - - Parameters - ---------- - definition : callable - Definition to be copied. - name : unicode, optional - Optional definition copy name. - - Returns - ------- - callable - Definition copy. - """ - - copy = types.FunctionType(definition.__code__, definition.__globals__, - str(name or definition.__name__), - definition.__defaults__, definition.__closure__) - copy.__dict__.update(definition.__dict__) - - return copy + if value is None: + return default + else: + return value diff --git a/colour/utilities/data_structures.py b/colour/utilities/data_structures.py index e37aaa209b..8363dd93f7 100644 --- a/colour/utilities/data_structures.py +++ b/colour/utilities/data_structures.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Data Structures =============== @@ -7,87 +6,195 @@ - :class:`colour.utilities.Structure`: An object similar to C/C++ structured type. -- :class:`colour.utilities.Lookup`: A *dict* sub-class acting as a lookup to - retrieve keys by values. -- :class:`colour.utilities.CaseInsensitiveMapping`: A case insensitive - mapping allowing values retrieving from keys while ignoring the key case. +- :class:`colour.utilities.Lookup`: A :class:`dict` sub-class acting as a + lookup to retrieve keys by values. +- :class:`CaseInsensitiveMapping`: A case insensitive + :class:`dict`-like object allowing values retrieving from keys while + ignoring the key case. - :class:`colour.utilities.LazyCaseInsensitiveMapping`: Another case insensitive mapping allowing lazy values retrieving from keys while ignoring the key case. +- :class:`colour.utilities.Node`: A basic node object supporting creation of + basic node trees. References ---------- - :cite:`Mansencalc` : Mansencal, T. (n.d.). Lookup. https://github.com/KelSolaar/Foundations/blob/develop/foundations/\ data_structures.py -- :cite:`Mansencald` : Mansencal, T. (n.d.). Structure. - https://github.com/KelSolaar/Foundations/blob/develop/foundations/\ -data_structures.py +- :cite:`Rakotoarison2017` : Rakotoarison, H. (2017). Bunch. Retrieved + December 4, 2021, from https://github.com/scikit-learn/scikit-learn/blob/\ +0d378913b/sklearn/utils/__init__.py#L83 - :cite:`Reitza` : Reitz, K. (n.d.). CaseInsensitiveDict. https://github.com/kennethreitz/requests/blob/v1.2.3/requests/\ structures.py#L37 """ -from __future__ import division, unicode_literals - -try: # pragma: no cover - from collections import Mapping, MutableMapping -except ImportError: # pragma: no cover - from collections.abc import Mapping, MutableMapping - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from __future__ import annotations + +from collections.abc import MutableMapping + +from colour.hints import ( + Any, + Boolean, + Dict, + Generator, + Integer, + Iterable, + List, + Mapping, + Optional, + Union, +) +from colour.utilities.documentation import is_documentation_building + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'Structure', 'Lookup', 'CaseInsensitiveMapping', - 'LazyCaseInsensitiveMapping' + "attest", + "Structure", + "Lookup", + "CaseInsensitiveMapping", + "LazyCaseInsensitiveMapping", + "Node", ] +def attest(condition: Boolean, message: str = ""): + """ + Provide the `assert` statement functionality without being disabled by + optimised Python execution. + + See :func:`colour.utilities.assert` for more information. + + Notes + ----- + - This definition is duplicated to avoid import circular dependency. + """ + + # Avoiding circular dependency. + import colour.utilities + + colour.utilities.attest(condition, message) + + class Structure(dict): """ - Defines a dict-like object allowing to access key values using dot syntax. + Define a :class:`dict`-like object allowing to access key values using dot + syntax. Other Parameters ---------------- - \\*args : list, optional + args Arguments. - \\**kwargs : dict, optional - Key / Value pairs. + kwargs + Key / value pairs. Methods ------- - :meth:`~colour.utilities.Structure.__init__` + - :meth:`~colour.utilities.Structure.__setattr__` + - :meth:`~colour.utilities.Structure.__delattr__` + - :meth:`~colour.utilities.Structure.__dir__` + - :meth:`~colour.utilities.Structure.__getattr__` + - :meth:`~colour.utilities.Structure.__setstate__` References ---------- - :cite:`Mansencald` + :cite:`Rakotoarison2017` Examples -------- >>> person = Structure(first_name='John', last_name='Doe', gender='male') - >>> # Doctests skip for Python 2.x compatibility. - >>> person.first_name # doctest: +SKIP + >>> person.first_name 'John' >>> sorted(person.keys()) ['first_name', 'gender', 'last_name'] - >>> # Doctests skip for Python 2.x compatibility. - >>> person['gender'] # doctest: +SKIP + >>> person['gender'] 'male' """ - def __init__(self, *args, **kwargs): - super(Structure, self).__init__(*args, **kwargs) - self.__dict__ = self + def __init__(self, *args: Any, **kwargs: Any): + super().__init__(*args, **kwargs) + + def __setattr__(self, name: str, value: Any): + """ + Assign given value to the attribute with given name. + + Parameters + ---------- + name + Name of the attribute to assign the ``value`` to. + value + Value to assign to the attribute. + """ + + self[name] = value + + def __delattr__(self, name: str): + """ + Delete the attribute with given name. + + Parameters + ---------- + name + Name of the attribute to delete. + """ + + del self[name] + + def __dir__(self) -> Iterable: + """ + Return a list of valid attributes for the :class:`dict`-like object. + + Returns + ------- + :class:`list` + List of valid attributes for the :class:`dict`-like object. + """ + + return self.keys() + + def __getattr__(self, name: str) -> Any: + """ + Return the value from the attribute with given name. + + Parameters + ---------- + name + Name of the attribute to get the value from. + + Returns + ------- + :class:`object` + + Raises + ------ + AttributeError + If the attribute is not defined. + """ + + try: + return self[name] + except KeyError: + raise AttributeError(name) + + def __setstate__(self, state): + """Set the object state when unpickling.""" + # See https://github.com/scikit-learn/scikit-learn/issues/6196 for more + # information. + + pass class Lookup(dict): """ - Extends *dict* type to provide a lookup by value(s). + Extend :class:`dict` type to provide a lookup by value(s). Methods ------- @@ -108,18 +215,19 @@ class Lookup(dict): ['Jane', 'John'] """ - def keys_from_value(self, value): + def keys_from_value(self, value: Any) -> List: """ - Gets the keys with given value. + Get the keys associated with given value. Parameters ---------- - value : object - Value. + value + Value to find the associated keys. + Returns ------- - object - Keys. + :class:`list` + Keys associated with given value. """ keys = [] @@ -129,25 +237,26 @@ def keys_from_value(self, value): matching = all(matching) except TypeError: - matching = all((matching, )) + matching = all((matching,)) if matching: keys.append(key) return keys - def first_key_from_value(self, value): + def first_key_from_value(self, value: Any) -> Any: """ - Gets the first key with given value. + Get the first key associated with given value. Parameters ---------- - value : object - Value. + value + Value to find the associated first key. + Returns ------- - object - Key. + :class:`object` + First key associated with given value. """ return self.keys_from_value(value)[0] @@ -155,21 +264,22 @@ def first_key_from_value(self, value): class CaseInsensitiveMapping(MutableMapping): """ - Implements a case-insensitive mutable mapping / *dict* object. + Implement a case-insensitive :class:`dict`-like object. Allows values retrieving from keys while ignoring the key case. - The keys are expected to be unicode or string-like objects supporting the + The keys are expected to be str or :class:`str`-like objects supporting the :meth:`str.lower` method. Parameters ---------- - data : dict - *dict* of data to store into the mapping at initialisation. + data + Data to store into the case-insensitive :class:`dict`-like object at + initialisation. Other Parameters ---------------- - \\**kwargs : dict, optional - Key / Value pairs to store into the mapping at initialisation. + kwargs + Key / value pairs to store into the mapping at initialisation. Attributes ---------- @@ -178,6 +288,7 @@ class CaseInsensitiveMapping(MutableMapping): Methods ------- - :meth:`~colour.utilities.CaseInsensitiveMapping.__init__` + - :meth:`~colour.utilities.CaseInsensitiveMapping.__repr__` - :meth:`~colour.utilities.CaseInsensitiveMapping.__setitem__` - :meth:`~colour.utilities.CaseInsensitiveMapping.__getitem__` - :meth:`~colour.utilities.CaseInsensitiveMapping.__delitem__` @@ -186,14 +297,9 @@ class CaseInsensitiveMapping(MutableMapping): - :meth:`~colour.utilities.CaseInsensitiveMapping.__len__` - :meth:`~colour.utilities.CaseInsensitiveMapping.__eq__` - :meth:`~colour.utilities.CaseInsensitiveMapping.__ne__` - - :meth:`~colour.utilities.CaseInsensitiveMapping.__repr__` - :meth:`~colour.utilities.CaseInsensitiveMapping.copy` - :meth:`~colour.utilities.CaseInsensitiveMapping.lower_items` - Warnings - -------- - The keys are expected to be unicode or string-like objects. - References ---------- :cite:`Reitza` @@ -205,197 +311,256 @@ class CaseInsensitiveMapping(MutableMapping): 1 """ - def __init__(self, data=None, **kwargs): - self._data = dict() + def __init__( + self, data: Optional[Union[Generator, Mapping]] = None, **kwargs: Any + ): + self._data: Dict = dict() self.update({} if data is None else data, **kwargs) @property - def data(self): + def data(self) -> Dict: """ - Getter property for the data. + Getter property for the case-insensitive :class:`dict`-like object + data. Returns ------- - dict + :class:`dict` Data. """ return self._data - def __setitem__(self, item, value): + def __repr__(self) -> str: """ - Sets given item with given value. + Return an evaluable string representation of the case-insensitive + :class:`dict`-like object. - The item is stored as lower in the mapping while the original name and - its value are stored together as the value in a *tuple*: + Returns + ------- + :class:`str` + Evaluable string representation. + """ - {"item.lower()": ("item", value)} + if is_documentation_building(): # pragma: no cover + representation = repr( + dict(zip(self.keys(), ["..."] * len(self))) + ).replace("'...'", "...") + return f"{self.__class__.__name__}({representation})" + else: + return f"{self.__class__.__name__}({dict(self.items())})" + + def __setitem__(self, item: Union[str, Any], value: Any): + """ + Set given item with given value in the case-insensitive + :class:`dict`-like object. Parameters ---------- - item : object - Attribute. - value : object - Value. - """ + item + Item to set in the case-insensitive :class:`dict`-like object. + value + Value to store in the case-insensitive :class:`dict`-like object. - self._data[item.lower()] = (item, value) + Notes + ----- + - The item is stored as lower-case while the original name and its + value are stored together as the value in a *tuple*:: - def __getitem__(self, item): + {"item.lower()": ("item", value)} """ - Returns the value of given item. - The item value is retrieved using its lower name in the mapping. + self._data[self._lower_key(item)] = (item, value) + + def __getitem__(self, item: Union[str, Any]) -> Any: + """ + Return the value of given item from the case-insensitive + :class:`dict`-like object. Parameters ---------- - item : unicode - Item name. + item + Item to retrieve the value of from the case-insensitive + :class:`dict`-like object. Returns ------- - object + :class:`object` Item value. + + Notes + ----- + - The item value is retrieved by using its lower-case variant. """ - return self._data[item.lower()][1] + return self._data[self._lower_key(item)][1] - def __delitem__(self, item): + def __delitem__(self, item: Union[str, Any]): """ - Deletes the item with given name. - - The item is deleted from the mapping using its lower name. + Delete given item from the case-insensitive :class:`dict`-like object. Parameters ---------- - item : unicode - Item name. + item + Item to delete from the case-insensitive :class:`dict`-like object. + + Notes + ----- + - The item is deleted by using its lower-case variant. """ - del self._data[item.lower()] + del self._data[self._lower_key(item)] - def __contains__(self, item): + def __contains__(self, item: Union[str, Any]) -> bool: """ - Returns if the mapping contains given item. + Return whether the case-insensitive :class:`dict`-like object contains + given item. Parameters ---------- - item : unicode - Item name. + item + Item to find whether it is in the case-insensitive + :class:`dict`-like object. Returns ------- - bool - Is item in mapping. + :class:`bool` + Whether given item is in the case-insensitive :class:`dict`-like + object. """ - return item.lower() in self._data + return self._lower_key(item) in self._data - def __iter__(self): + def __iter__(self) -> Generator: """ - Iterates over the items names in the mapping. + Iterate over the items of the case-insensitive :class:`dict`-like + object. - The item names returned are the original input ones. + Yields + ------ + Generator + Item generator. - Returns - ------- - generator - Item names. + Notes + ----- + - The iterated items are the original items. """ return (item for item, value in self._data.values()) - def __len__(self): + def __len__(self) -> Integer: """ - Returns the items count. + Return the items count. Returns ------- - int + :class:`numpy.integer` Items count. """ return len(self._data) - def __eq__(self, item): + def __eq__(self, other: Any) -> bool: """ - Returns the equality with given object. + Return whether the case-insensitive :class:`dict`-like object is equal + to given other object. Parameters ---------- - item - Object item. + other + Object to test whether it is equal to the case-insensitive + :class:`dict`-like object Returns ------- - bool - Equality. + :class:`bool` + Whether given object is equal to the case-insensitive + :class:`dict`-like object. """ - if isinstance(item, Mapping): - item_mapping = CaseInsensitiveMapping(item) + if isinstance(other, Mapping): + other_mapping = CaseInsensitiveMapping(other) else: raise ValueError( - 'Impossible to test equality with "{0}" class type!'.format( - item.__class__.__name__)) + f"Impossible to test equality with " + f'"{other.__class__.__name__}" class type!' + ) - return dict(self.lower_items()) == dict(item_mapping.lower_items()) + return dict(self.lower_items()) == dict(other_mapping.lower_items()) - def __ne__(self, item): + def __ne__(self, other: Any) -> bool: """ - Returns the inequality with given object. + Return whether the case-insensitive :class:`dict`-like object is not + equal to given other object. Parameters ---------- - item - Object item. + other + Object to test whether it is not equal to the case-insensitive + :class:`dict`-like object Returns ------- - bool - Inequality. + :class:`bool` + Whether given object is not equal to the case-insensitive + :class:`dict`-like object. """ - return not (self == item) + return not (self == other) - def __repr__(self): + @staticmethod + def _lower_key(key: Union[str, Any]) -> Union[str, Any]: """ - Returns the mapping representation with the original item names. + Return the lower-case variant of given key, if the key cannot be + lower-cased, it is passed unmodified. + + Parameters + ---------- + key + Key to return the lower-case variant. Returns ------- - unicode - Mapping representation. + :class:`str` or :class:`object` + Key lower-case variant. """ - return '{0}({1})'.format(self.__class__.__name__, dict(self.items())) + try: + return key.lower() + except AttributeError: + return key - def copy(self): + def copy(self) -> CaseInsensitiveMapping: """ - Returns a copy of the mapping. + Return a copy of the case-insensitive :class:`dict`-like object. Returns ------- - CaseInsensitiveMapping - Mapping copy. + :class:`CaseInsensitiveMapping` + Case-insensitive :class:`dict`-like object copy. - Notes - ----- - - The :class:`colour.utilities.CaseInsensitiveMapping` class copy - returned is a simple *copy* not a *deepcopy*. + Warnings + -------- + - The :class:`CaseInsensitiveMapping` class copy returned is a + *copy* of the object not a *deepcopy*! """ - return CaseInsensitiveMapping(self._data.values()) + return CaseInsensitiveMapping(dict(self._data.values())) - def lower_items(self): + def lower_items(self) -> Generator: """ - Iterates over the lower items names. + Iterate over the lower-case items of the case-insensitive + :class:`dict`-like object. - Returns - ------- - generator - Lower item names. + Yields + ------ + Generator + Item generator. + + Notes + ----- + - The iterated items are the lower-case items. """ return ((item, value[1]) for (item, value) in self._data.items()) @@ -403,33 +568,32 @@ def lower_items(self): class LazyCaseInsensitiveMapping(CaseInsensitiveMapping): """ - Implements a lazy case-insensitive mutable mapping / *dict* object by - inheriting from :class:`colour.utilities.CaseInsensitiveMapping` class. + Implement a lazy case-insensitive :class:`dict`-like object inheriting + from :class:`CaseInsensitiveMapping` class. + + Allows lay values retrieving from keys while ignoring the key case. + The keys are expected to be str or :class:`str`-like objects supporting the + :meth:`str.lower` method. - Allows lazy values retrieving from keys while ignoring the key case. - The keys are expected to be unicode or string-like objects supporting the - :meth:`str.lower` method. The lazy retrieval is performed as follows: - If the value is a callable, then it is evaluated and its return value is - stored in place of the current value. + The lazy retrieval is performed as follows: If the value is a callable, + then it is evaluated and its return value is stored in place of the current + value. Parameters ---------- - data : dict - *dict* of data to store into the mapping at initialisation. + data + Data to store into the lazy case-insensitive :class:`dict`-like object + at initialisation. Other Parameters ---------------- - \\**kwargs : dict, optional - Key / Value pairs to store into the mapping at initialisation. + kwargs + Key / value pairs to store into the mapping at initialisation. Methods ------- - :meth:`~colour.utilities.LazyCaseInsensitiveMapping.__getitem__` - Warnings - -------- - The keys are expected to be unicode or string-like objects. - Examples -------- >>> def callable_a(): @@ -444,29 +608,497 @@ class LazyCaseInsensitiveMapping(CaseInsensitiveMapping): 2 """ - def __getitem__(self, item): + def __getitem__(self, item: Union[str, Any]) -> Any: """ - Returns the value of given item. - - The item value is retrieved using its lower name in the mapping. If - the value is a callable, then it is evaluated and its return value is - stored in place of the current value. + Return the value of given item from the case-insensitive + :class:`dict`-like object. Parameters ---------- - item : unicode - Item name. + item + Item to retrieve the value of from the case-insensitive + :class:`dict`-like object. Returns ------- - object + :class:`object` Item value. + + Notes + ----- + - The item value is retrieved by using its lower-case variant. """ - value = super(LazyCaseInsensitiveMapping, self).__getitem__(item) + import colour + + value = super().__getitem__(item) - if callable(value): + if callable(value) and hasattr(colour, "__disable_lazy_load__"): value = value() - super(LazyCaseInsensitiveMapping, self).__setitem__(item, value) + super().__setitem__(item, value) return value + + +class Node: + """ + Represent a basic node supporting the creation of basic node trees. + + Parameters + ---------- + name + Node name. + parent + Parent of the node. + children + Children of the node. + data + The data belonging to this node. + + Attributes + ---------- + - :attr:`~colour.utilities.Node.name` + - :attr:`~colour.utilities.Node.parent` + - :attr:`~colour.utilities.Node.children` + - :attr:`~colour.utilities.Node.id` + - :attr:`~colour.utilities.Node.root` + - :attr:`~colour.utilities.Node.leaves` + - :attr:`~colour.utilities.Node.siblings` + - :attr:`~colour.utilities.Node.data` + + Methods + ------- + - :meth:`~colour.utilities.Node.__new__` + - :meth:`~colour.utilities.Node.__init__` + - :meth:`~colour.utilities.Node.__str__` + - :meth:`~colour.utilities.Node.__len__` + - :meth:`~colour.utilities.Node.is_root` + - :meth:`~colour.utilities.Node.is_inner` + - :meth:`~colour.utilities.Node.is_leaf` + - :meth:`~colour.utilities.Node.walk` + - :meth:`~colour.utilities.Node.render` + + Examples + -------- + >>> node_a = Node('Node A') + >>> node_b = Node('Node B', node_a) + >>> node_c = Node('Node C', node_a) + >>> node_d = Node('Node D', node_b) + >>> node_e = Node('Node E', node_b) + >>> node_f = Node('Node F', node_d) + >>> node_g = Node('Node G', node_f) + >>> node_h = Node('Node H', node_g) + >>> [node.name for node in node_a.leaves] + ['Node H', 'Node E', 'Node C'] + >>> print(node_h.root.name) + Node A + >>> len(node_a) + 7 + """ + + _INSTANCE_ID: Integer = 1 + """ + Node id counter. + + _INSTANCE_ID + """ + + def __new__(cls, *args: Any, **kwargs: Any) -> Node: + """ + Return a new instance of the :class:`colour.utilities.Node` class. + + Other Parameters + ---------------- + args + Arguments. + kwargs + Keywords arguments. + """ + + instance = super().__new__(cls) + + instance._id = Node._INSTANCE_ID # type: ignore[attr-defined] + Node._INSTANCE_ID += 1 + + return instance + + def __init__( + self, + name: Optional[str] = None, + parent: Optional[Node] = None, + children: Optional[List[Node]] = None, + data: Optional[Any] = None, + ): + self._name: str = f"{self.__class__.__name__}#{self.id}" + self.name = self._name if name is None else name + self._parent: Optional[Node] = None + self.parent = parent + self._children: List[Node] = [] + self.children = self._children if children is None else children + self._data: Optional[Any] = data + + @property + def name(self) -> str: + """ + Getter and setter property for the name. + + Parameters + ---------- + value + Value to set the name with. + + Returns + ------- + :class:`str` + Node name. + """ + + return self._name + + @name.setter + def name(self, value: str): + """Setter for the **self.name** property.""" + + attest( + isinstance(value, str), + f'"name" property: "{value}" type is not "str"!', + ) + + self._name = value + + @property + def parent(self) -> Optional[Node]: + """ + Getter and setter property for the node parent. + + Parameters + ---------- + value + Parent to set the node with. + + Returns + ------- + :class:`Node` or :py:data:`None` + Node parent. + """ + + return self._parent + + @parent.setter + def parent(self, value: Optional[Node]): + """Setter for the **self.parent** property.""" + + if value is not None: + attest( + issubclass(value.__class__, Node), + f'"parent" property: "{value}" is not a ' + f'"{Node.__class__.__name__}" subclass!', + ) + + value.children.append(self) + + self._parent = value + + @property + def children(self) -> List[Node]: + """ + Getter and setter property for the node children. + + Parameters + ---------- + value + Children to set the node with. + + Returns + ------- + :class:`list` + Node children. + """ + + return self._children + + @children.setter + def children(self, value: List[Node]): + """Setter for the **self.children** property.""" + + attest( + isinstance(value, list), + f'"children" property: "{value}" type is not a "list" instance!', + ) + + for element in value: + attest( + issubclass(element.__class__, Node), + f'"children" property: A "{element}" element is not a ' + f'"{Node.__class__.__name__}" subclass!', + ) + + for node in value: + node.parent = self + + self._children = value + + @property + def id(self) -> Integer: + """ + Getter property for the node id. + + Returns + ------- + :class:`numpy.integer` + Node id. + """ + + return self._id # type: ignore[attr-defined] + + @property + def root(self) -> Node: + """ + Getter property for the node tree. + + Returns + ------- + :class:`Node` + Node root. + """ + + if self.is_root(): + return self + else: + return list(self.walk(ascendants=True))[-1] + + @property + def leaves(self) -> Generator: + """ + Getter property for the node leaves. + + Yields + ------ + Generator + Node leaves. + """ + + if self.is_leaf(): + return (node for node in (self,)) + else: + return (node for node in self.walk() if node.is_leaf()) + + @property + def siblings(self) -> Generator: + """ + Getter property for the node siblings. + + Returns + ------- + Generator + Node siblings. + """ + + if self.parent is None: + return (sibling for sibling in ()) # type: ignore[var-annotated] + else: + return ( + sibling + for sibling in self.parent.children + if sibling is not self + ) + + @property + def data(self) -> Any: + """ + Getter property for the node data. + + Returns + ------- + :class:`object` + Node data. + """ + + return self._data + + @data.setter + def data(self, value: Any): + """Setter for the **self.data** property.""" + + self._data = value + + def __str__(self) -> str: + """ + Return a formatted string representation of the node. + + Returns + ------- + :class`str` + Formatted string representation. + """ + + return f"{self.__class__.__name__}#{self.id}({self._data})" + + def __len__(self) -> Integer: + """ + Return the number of children of the node. + + Returns + ------- + :class:`numpy.integer` + Number of children of the node. + """ + + return len(list(self.walk())) + + def is_root(self) -> Boolean: + """ + Return whether the node is a root node. + + Returns + ------- + :class:`bool` + Whether the node is a root node. + + Examples + -------- + >>> node_a = Node('Node A') + >>> node_b = Node('Node B', node_a) + >>> node_c = Node('Node C', node_b) + >>> node_a.is_root() + True + >>> node_b.is_root() + False + """ + + return self.parent is None + + def is_inner(self) -> Boolean: + """ + Return whether the node is an inner node. + + Returns + ------- + :class:`bool` + Whether the node is an inner node. + + Examples + -------- + >>> node_a = Node('Node A') + >>> node_b = Node('Node B', node_a) + >>> node_c = Node('Node C', node_b) + >>> node_a.is_inner() + False + >>> node_b.is_inner() + True + """ + + return all([not self.is_root(), not self.is_leaf()]) + + def is_leaf(self) -> Boolean: + """ + Return whether the node is a leaf node. + + Returns + ------- + :class:`bool` + Whether the node is a leaf node. + + Examples + -------- + >>> node_a = Node('Node A') + >>> node_b = Node('Node B', node_a) + >>> node_c = Node('Node C', node_b) + >>> node_a.is_leaf() + False + >>> node_c.is_leaf() + True + """ + + return len(self._children) == 0 + + def walk(self, ascendants: Boolean = False) -> Generator: + """ + Return a generator used to walk into :class:`colour.utilities.Node` + trees. + + Parameters + ---------- + ascendants + Whether to walk up the node tree. + + Yields + ------ + Generator + Node tree walker. + + Examples + -------- + >>> node_a = Node('Node A') + >>> node_b = Node('Node B', node_a) + >>> node_c = Node('Node C', node_a) + >>> node_d = Node('Node D', node_b) + >>> node_e = Node('Node E', node_b) + >>> node_f = Node('Node F', node_d) + >>> node_g = Node('Node G', node_f) + >>> node_h = Node('Node H', node_g) + >>> for node in node_a.walk(): + ... print(node.name) + Node B + Node D + Node F + Node G + Node H + Node E + Node C + """ + + attribute = "children" if not ascendants else "parent" + + nodes = getattr(self, attribute) + nodes = nodes if isinstance(nodes, list) else [nodes] + + for node in nodes: + yield node + + if not getattr(node, attribute): + continue + + yield from node.walk(ascendants=ascendants) + + def render(self, tab_level: Integer = 0): + """ + Render the current node and its children as a string. + + Parameters + ---------- + tab_level + Initial indentation level + + Returns + ------- + :class:`str` + Rendered node tree. + + Examples + -------- + >>> node_a = Node('Node A') + >>> node_b = Node('Node B', node_a) + >>> node_c = Node('Node C', node_a) + >>> print(node_a.render()) + |----"Node A" + |----"Node B" + |----"Node C" + + """ + + output = "" + + for _i in range(tab_level): + output += " " + + tab_level += 1 + + output += f'|----"{self.name}"\n' + + for child in self._children: + output += child.render(tab_level) + + tab_level -= 1 + + return output diff --git a/colour/utilities/deprecation.py b/colour/utilities/deprecation.py index aa433ef705..829fea8e4a 100644 --- a/colour/utilities/deprecation.py +++ b/colour/utilities/deprecation.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Deprecation Utilities ===================== @@ -6,298 +5,318 @@ Defines various deprecation management related objects. """ -from __future__ import division, unicode_literals +from __future__ import annotations import sys from importlib import import_module from collections import namedtuple from operator import attrgetter -from colour.utilities import usage_warning +from colour.utilities import attest, optional, usage_warning +from colour.hints import Any, Dict, List, ModuleType, Optional -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'ObjectRenamed', 'ObjectRemoved', 'ObjectFutureRename', - 'ObjectFutureRemove', 'ObjectFutureAccessChange', - 'ObjectFutureAccessRemove', 'ModuleAPI', 'ArgumentRenamed', - 'ArgumentRemoved', 'ArgumentFutureRename', 'ArgumentFutureRemove', - 'get_attribute', 'build_API_changes', 'handle_arguments_deprecation' + "ObjectRenamed", + "ObjectRemoved", + "ObjectFutureRename", + "ObjectFutureRemove", + "ObjectFutureAccessChange", + "ObjectFutureAccessRemove", + "ModuleAPI", + "ArgumentRenamed", + "ArgumentRemoved", + "ArgumentFutureRename", + "ArgumentFutureRemove", + "get_attribute", + "build_API_changes", + "handle_arguments_deprecation", ] -class ObjectRenamed(namedtuple('ObjectRenamed', ('name', 'new_name'))): +class ObjectRenamed(namedtuple("ObjectRenamed", ("name", "new_name"))): """ A class used for an object that has been renamed. Parameters ---------- - name : unicode + name Object name that changed. - new_name : unicode + new_name Object new name. """ - def __str__(self): + def __str__(self) -> str: """ - Returns a formatted string representation of the class. + Return a formatted string representation of the class. Returns ------- - unicode + :class:`str` Formatted string representation. """ - return ('"{0}" object has been renamed to "{1}".'.format( - self.name, self.new_name)) + return f'"{self.name}" object has been renamed to "{self.new_name}".' -class ObjectRemoved(namedtuple('ObjectRemoved', ('name', ))): +class ObjectRemoved(namedtuple("ObjectRemoved", ("name",))): """ A class used for an object that has been removed. Parameters ---------- - name : unicode + name Object name that has been removed. """ - def __str__(self): + def __str__(self) -> str: """ - Returns a formatted string representation of the class. + Return a formatted string representation of the class. Returns ------- - unicode + :class:`str` Formatted string representation. """ - return '"{0}" object has been removed from the API.'.format(self.name) + return f'"{self.name}" object has been removed from the API.' class ObjectFutureRename( - namedtuple('ObjectFutureRename', ('name', 'new_name'))): + namedtuple("ObjectFutureRename", ("name", "new_name")) +): """ A class used for future object name deprecation, i.e. object name will change in a future release. Parameters ---------- - name : unicode + name Object name that will change in a future release. - new_name : unicode + new_name Object future release name. """ - def __str__(self): + def __str__(self) -> str: """ - Returns a formatted string representation of the deprecation type. + Return a formatted string representation of the deprecation type. Returns ------- - unicode + :class:`str` Formatted string representation. """ - return ('"{0}" object is deprecated and will be renamed to "{1}" ' - 'in a future release.'.format(self.name, self.new_name)) + return ( + f'"{self.name}" object is deprecated and will be renamed to ' + f'"{self.new_name}" in a future release.' + ) -class ObjectFutureRemove(namedtuple('ObjectFutureRemove', ('name', ))): +class ObjectFutureRemove(namedtuple("ObjectFutureRemove", ("name",))): """ A class used for future object removal. Parameters ---------- - name : unicode + name Object name that will be removed in a future release. """ - def __str__(self): + def __str__(self) -> str: """ - Returns a formatted string representation of the deprecation type. + Return a formatted string representation of the deprecation type. Returns ------- - unicode + :class:`str` Formatted string representation. """ - return ('"{0}" object is deprecated and will be removed ' - 'in a future release.'.format(self.name)) + return ( + f'"{self.name}" object is deprecated and will be removed in ' + f"a future release." + ) class ObjectFutureAccessChange( - namedtuple('ObjectFutureAccessChange', ('access', 'new_access'))): + namedtuple("ObjectFutureAccessChange", ("access", "new_access")) +): """ A class used for future object access deprecation, i.e. object access will change in a future release. Parameters ---------- - access : unicode + access Object access that will change in a future release. - new_access : unicode + new_access Object future release access. """ - def __str__(self): + def __str__(self) -> str: """ - Returns a formatted string representation of the deprecation type. + Return a formatted string representation of the deprecation type. Returns ------- - unicode + :class:`str` Formatted string representation. """ - return ('"{0}" object access is deprecated and will change to ' - '"{1}" in a future release.'.format(self.access, - self.new_access)) + return ( + f'"{self.access}" object access is deprecated and will change ' + f'to "{self.new_access}" in a future release.' + ) class ObjectFutureAccessRemove( - namedtuple('ObjectFutureAccessRemove', ('name', ))): + namedtuple("ObjectFutureAccessRemove", ("name",)) +): """ A class used for future object access removal, i.e. object access will be removed in a future release. Parameters ---------- - name : unicode + name Object name whose access will removed in a future release. """ - def __str__(self): + def __str__(self) -> str: """ - Returns a formatted string representation of the deprecation type. + Return a formatted string representation of the deprecation type. Returns ------- - unicode + :class:`str` Formatted string representation. """ return ( - '"{0}" object access will be removed in a future release.'.format( - self.name)) + f'"{self.name}" object access will be removed in a future release.' + ) -class ArgumentRenamed(namedtuple('ArgumentRenamed', ('name', 'new_name'))): +class ArgumentRenamed(namedtuple("ArgumentRenamed", ("name", "new_name"))): """ A class used for an argument that has been renamed. Parameters ---------- - name : unicode + name Argument name that changed. - new_name : unicode + new_name Argument new name. """ - def __str__(self): + def __str__(self) -> str: """ - Returns a formatted string representation of the class. + Return a formatted string representation of the class. Returns ------- - unicode + :class:`str` Formatted string representation. """ - return ('"{0}" argument has been renamed to "{1}".'.format( - self.name, self.new_name)) + return f'"{self.name}" argument has been renamed to "{self.new_name}".' -class ArgumentRemoved(namedtuple('ArgumentRemoved', ('name', ))): +class ArgumentRemoved(namedtuple("ArgumentRemoved", ("name",))): """ A class used for an argument that has been removed. Parameters ---------- - name : unicode + name Argument name that has been removed. """ - def __str__(self): + def __str__(self) -> str: """ - Returns a formatted string representation of the class. + Return a formatted string representation of the class. Returns ------- - unicode + :class:`str` Formatted string representation. """ - return '"{0}" argument has been removed from the API.'.format( - self.name) + return f'"{self.name}" argument has been removed from the API.' class ArgumentFutureRename( - namedtuple('ArgumentFutureRename', ('name', 'new_name'))): + namedtuple("ArgumentFutureRename", ("name", "new_name")) +): """ A class used for future argument name deprecation, i.e. argument name will change in a future release. Parameters ---------- - name : unicode + name Argument name that will change in a future release. - new_name : unicode + new_name Argument future release name. """ - def __str__(self): + def __str__(self) -> str: """ - Returns a formatted string representation of the deprecation type. + Return a formatted string representation of the deprecation type. Returns ------- - unicode + :class:`str` Formatted string representation. """ - return ('"{0}" argument is deprecated and will be renamed to "{1}" ' - 'in a future release.'.format(self.name, self.new_name)) + return ( + f'"{self.name}" argument is deprecated and will be renamed to ' + f'"{self.new_name}" in a future release.' + ) -class ArgumentFutureRemove(namedtuple('ArgumentFutureRemove', ('name', ))): +class ArgumentFutureRemove(namedtuple("ArgumentFutureRemove", ("name",))): """ A class used for future argument removal. Parameters ---------- - name : unicode + name Argument name that will be removed in a future release. """ - def __str__(self): + def __str__(self) -> str: """ - Returns a formatted string representation of the deprecation type. + Return a formatted string representation of the deprecation type. Returns ------- - unicode + :class:`str` Formatted string representation. """ - return ('"{0}" argument is deprecated and will be removed ' - 'in a future release.'.format(self.name)) + return ( + f'"{self.name}" argument is deprecated and will be removed in ' + f"a future release." + ) -class ModuleAPI(object): +class ModuleAPI: """ Define a class that allows customisation of module attributes access with deprecation management. Parameters ---------- - module : module + module Module to customise attributes access. Methods @@ -313,22 +332,22 @@ class ModuleAPI(object): ... # doctest: +SKIP """ - def __init__(self, module, changes=None): + def __init__(self, module: ModuleType, changes: Optional[Dict] = None): self._module = module - self._changes = changes or {} + self._changes = optional(changes, {}) - def __getattr__(self, attribute): + def __getattr__(self, attribute: str) -> Any: """ - Returns given attribute value while handling deprecation. + Return given attribute value while handling deprecation. Parameters ---------- - attribute : unicode + attribute Attribute name. Returns ------- - object + :class:`object` Attribute value. Raises @@ -338,51 +357,56 @@ def __getattr__(self, attribute): """ change = self._changes.get(attribute) + if change is not None: if not isinstance(change, ObjectRemoved): usage_warning(str(change)) - return (getattr(self._module, attribute) if isinstance( - change, ObjectFutureRemove) else get_attribute(change[1])) + return ( + getattr(self._module, attribute) + if isinstance(change, ObjectFutureRemove) + else get_attribute(change[1]) + ) else: raise AttributeError(str(change)) return getattr(self._module, attribute) - def __dir__(self): + def __dir__(self) -> List: """ - Returns list of names in the module local scope filtered according to - the changes. + Return the list of names in the module local scope filtered according + to the changes. Returns ------- - list + :class:`list` Filtered list of names in the module local scope. """ attributes = [ - attribute for attribute in dir(self._module) + attribute + for attribute in dir(self._module) if attribute not in self._changes ] return attributes -def get_attribute(attribute): +def get_attribute(attribute: str) -> Any: """ - Returns given attribute. + Return given attribute value. Parameters ---------- - attribute : unicode + attribute Attribute to retrieve, ``attribute`` must have a namespace module, e.g. *colour.models.eotf_BT2020*. Returns ------- - object - Retrieved attribute. + :class:`object` + Retrieved attribute value. Examples -------- @@ -390,33 +414,32 @@ def get_attribute(attribute): """ - assert '.' in attribute, '"{0}" attribute has no namespace!' + attest("." in attribute, '"{0}" attribute has no namespace!') - module_name, attribute = attribute.rsplit('.', 1) + module_name, attribute = attribute.rsplit(".", 1) - module = sys.modules.get(module_name) - if module is None: - module = import_module(module_name) + module = optional(sys.modules.get(module_name), import_module(module_name)) - assert module is not None, ( - '"{0}" module does not exists or cannot be imported!'.format( - module_name)) + attest( + module is not None, + f'"{module_name}" module does not exists or cannot be imported!', + ) return attrgetter(attribute)(module) -def build_API_changes(changes): +def build_API_changes(changes: dict) -> Dict: """ - Builds the effective API changes for a desired API changes mapping. + Build the effective API changes for a desired API changes mapping. Parameters ---------- - changes : dict + changes Dictionary of desired API changes. Returns ------- - dict + :class:`dict` API changes Examples @@ -453,38 +476,46 @@ def build_API_changes(changes): name='module.object_6_access')} """ - for change_type in (ObjectRenamed, ObjectFutureRename, - ObjectFutureAccessChange, ArgumentRenamed, - ArgumentFutureRename): - for change in changes.pop(change_type.__name__, []): - changes[change[0].split('.')[-1]] = change_type(*change) # noqa - - for change_type in (ObjectRemoved, ObjectFutureRemove, - ObjectFutureAccessRemove, ArgumentRemoved, - ArgumentFutureRemove): - for change in changes.pop(change_type.__name__, []): - changes[change.split('.')[-1]] = change_type(change) # noqa + for rename_type in ( + ObjectRenamed, + ObjectFutureRename, + ObjectFutureAccessChange, + ArgumentRenamed, + ArgumentFutureRename, + ): + for change in changes.pop(rename_type.__name__, []): + changes[change[0].split(".")[-1]] = rename_type(*change) # noqa + + for remove_type in ( + ObjectRemoved, + ObjectFutureRemove, + ObjectFutureAccessRemove, + ArgumentRemoved, + ArgumentFutureRemove, + ): + for change in changes.pop(remove_type.__name__, []): + changes[change.split(".")[-1]] = remove_type(change) # noqa return changes -def handle_arguments_deprecation(changes, **kwargs): +def handle_arguments_deprecation(changes: dict, **kwargs: Any) -> Dict: """ - Handles arguments deprecation according to desired API changes mapping. + Handle arguments deprecation according to desired API changes mapping. Parameters ---------- - changes : dict + changes Dictionary of desired API changes. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs Keywords arguments to handle. Returns ------- - dict + :class:`dict` Handled keywords arguments. Examples @@ -525,6 +556,7 @@ def handle_arguments_deprecation(changes, **kwargs): else: kwargs[change[1]] = kwargs.pop(kwarg) else: + kwargs.pop(kwarg) usage_warning(str(change)) return kwargs diff --git a/colour/utilities/documentation.py b/colour/utilities/documentation.py index 41f0e78f31..3993d1cfaa 100644 --- a/colour/utilities/documentation.py +++ b/colour/utilities/documentation.py @@ -1,26 +1,30 @@ -# -*- coding: utf-8 -*- """ Documentation ============= -Defines documentation related objects. +Defines the documentation related objects. """ -from __future__ import division, unicode_literals +from __future__ import annotations + +from colour.hints import Boolean import os -from six import text_type -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'DocstringDict', 'DocstringFloat', 'DocstringInt', 'DocstringText', - 'DocstringTuple', 'is_documentation_building' + "DocstringDict", + "DocstringFloat", + "DocstringInt", + "DocstringText", + "DocstringTuple", + "is_documentation_building", ] @@ -44,17 +48,17 @@ class DocstringFloat(float): class DocstringInt(int): """ - A :class:`int` sub-class that allows settings a docstring to - :class:`int` instances. + A :class:`numpy.integer` sub-class that allows settings a docstring to + :class:`numpy.integer` instances. """ pass -class DocstringText(text_type): +class DocstringText(str): """ - A :class:`unicode` sub-class that allows settings a docstring to - :class:`unicode` instances. + A :class:`str` sub-class that allows settings a docstring to + :class:`str` instances. """ pass @@ -69,15 +73,15 @@ class DocstringTuple(tuple): pass -def is_documentation_building(): +def is_documentation_building() -> Boolean: """ - Returns whether the documentation is being built by checking whether the + Return whether the documentation is being built by checking whether the *READTHEDOCS* or *COLOUR_SCIENCE__DOCUMENTATION_BUILD* environment variables are defined, their value is not accounted for. Returns ------- - bool + :class:`bool` Whether the documentation is being built. Examples @@ -100,5 +104,6 @@ def is_documentation_building(): """ return bool( - os.environ.get('READTHEDOCS') or - os.environ.get('COLOUR_SCIENCE__DOCUMENTATION_BUILD')) + os.environ.get("READTHEDOCS") + or os.environ.get("COLOUR_SCIENCE__DOCUMENTATION_BUILD") + ) diff --git a/colour/utilities/metrics.py b/colour/utilities/metrics.py index bc63c1ad19..4ea9da8ab6 100644 --- a/colour/utilities/metrics.py +++ b/colour/utilities/metrics.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Metrics ======= @@ -17,34 +16,50 @@ https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -from colour.utilities import as_float_array - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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__ = ['metric_mse', 'metric_psnr'] - - -def metric_mse(a, b, axis=None): +from colour.utilities import as_float, as_float_array +from colour.hints import ( + ArrayLike, + FloatingOrNDArray, + Integer, + Number, + Optional, + Tuple, + Union, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "metric_mse", + "metric_psnr", +] + + +def metric_mse( + a: ArrayLike, + b: ArrayLike, + axis: Optional[Union[Integer, Tuple[Integer]]] = None, +) -> FloatingOrNDArray: """ - Computes the mean squared error (MSE) or mean squared deviation (MSD) - between given *array_like* :math:`a` and :math:`b` variables. + Compute the mean squared error (MSE) or mean squared deviation (MSD) + between given variables :math:`a` and :math:`b`. Parameters ---------- - a : array_like - :math:`a` variable. - b : array_like - :math:`b` variable. - axis : None or int or tuple of ints, optional + a + Variable :math:`a`. + b + Variable :math:`b`. + axis Axis or axes along which the means are computed. The default is to compute the mean of the flattened array. If this is a tuple of ints, a mean is performed over multiple axes, @@ -52,7 +67,7 @@ def metric_mse(a, b, axis=None): Returns ------- - float + :class:`numpy.floating` or :class:`numpy.ndarray` Mean squared error (MSE). References @@ -67,23 +82,30 @@ def metric_mse(a, b, axis=None): 0.0012714... """ - return np.mean((as_float_array(a) - as_float_array(b)) ** 2, axis=axis) + return as_float( + np.mean((as_float_array(a) - as_float_array(b)) ** 2, axis=axis) + ) -def metric_psnr(a, b, max_a=1, axis=None): +def metric_psnr( + a: ArrayLike, + b: ArrayLike, + max_a: Number = 1, + axis: Optional[Union[Integer, Tuple[Integer]]] = None, +) -> FloatingOrNDArray: """ - Computes the peak signal-to-noise ratio (PSNR) between given *array_like* - :math:`a` and :math:`b` variables. + Compute the peak signal-to-noise ratio (PSNR) between given variables + :math:`a` and :math:`b`. Parameters ---------- - a : array_like - :math:`a` variable. - b : array_like - :math:`b` variable. - max_a : numeric, optional + a + Variable :math:`a`. + b + Variable :math:`b`. + max_a Maximum possible pixel value of the :math:`a` variable. - axis : None or int or tuple of ints, optional + axis Axis or axes along which the means are computed. The default is to compute the mean of the flattened array. If this is a tuple of ints, a mean is performed over multiple axes, @@ -91,7 +113,7 @@ def metric_psnr(a, b, max_a=1, axis=None): Returns ------- - float + :class:`numpy.floating` or :class:`numpy.ndarray` Peak signal-to-noise ratio (PSNR). References @@ -106,4 +128,4 @@ def metric_psnr(a, b, max_a=1, axis=None): 28.9568515... """ - return 10 * np.log10(max_a ** 2 / metric_mse(a, b, axis)) + return as_float(10 * np.log10(max_a**2 / metric_mse(a, b, axis))) diff --git a/colour/utilities/tests/__init__.py b/colour/utilities/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/utilities/tests/__init__.py +++ b/colour/utilities/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/utilities/tests/test_array.py b/colour/utilities/tests/test_array.py index 3de578ed2b..6bf6089e52 100644 --- a/colour/utilities/tests/test_array.py +++ b/colour/utilities/tests/test_array.py @@ -1,361 +1,1123 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.utilities.array` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.utilities.array` module.""" import numpy as np -import six import unittest -from collections import namedtuple +from dataclasses import dataclass, field, fields +from copy import deepcopy from colour.constants import DEFAULT_FLOAT_DTYPE, DEFAULT_INT_DTYPE +from colour.hints import NDArray, Optional, Type, Union from colour.utilities import ( - as_array, as_int_array, as_float_array, as_numeric, as_int, as_float, - set_float_precision, set_int_precision, as_namedtuple, closest_indexes, - closest, normalise_maximum, interval, is_uniform, in_array, tstack, tsplit, - row_as_diagonal, vector_dot, matrix_dot, orient, centroid, - linear_conversion, lerp, fill_nan, ndarray_write, zeros, ones, full, - index_along_last_axis) + MixinDataclassFields, + MixinDataclassIterable, + MixinDataclassArray, + MixinDataclassArithmetic, + as_array, + as_int, + as_float, + as_int_array, + as_float_array, + as_int_scalar, + as_float_scalar, + set_default_int_dtype, + set_default_float_dtype, + get_domain_range_scale, + set_domain_range_scale, + domain_range_scale, + to_domain_1, + to_domain_10, + to_domain_100, + to_domain_int, + to_domain_degrees, + from_range_1, + from_range_10, + from_range_100, + from_range_int, + from_range_degrees, + closest_indexes, + closest, + interval, + is_uniform, + in_array, + tstack, + tsplit, + row_as_diagonal, + orient, + centroid, + fill_nan, + has_only_nan, + ndarray_write, + zeros, + ones, + full, + index_along_last_axis, +) from colour.utilities import is_networkx_installed -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestAsArray', 'TestAsIntArray', 'TestAsFloatArray', 'TestAsNumeric', - 'TestAsInt', 'TestAsFloat', 'TestSetFloatPrecision', 'TestSetIntPrecision', - 'TestAsNametuple', 'TestClosestIndexes', 'TestClosest', - 'TestNormaliseMaximum', 'TestInterval', 'TestIsUniform', 'TestInArray', - 'TestTstack', 'TestTsplit', 'TestRowAsDiagonal', 'TestDotVector', - 'TestDotMatrix', 'TestOrient', 'TestCentroid', 'TestLinearConversion', - 'TestLerp', 'TestFillNan', 'TestNdarrayWrite', 'TestZeros', 'TestOnes', - 'TestFull', 'TestIndexAlongLastAxis' + "TestMixinDataclassFields", + "TestMixinDataclassIterable", + "TestMixinDataclassArray", + "TestMixinDataclassArithmetic", + "TestAsArray", + "TestAsInt", + "TestAsFloat", + "TestAsIntArray", + "TestAsFloatArray", + "TestAsIntScalar", + "TestAsFloatScalar", + "TestSetDefaultIntegerDtype", + "TestSetDefaultFloatDtype", + "TestGetDomainRangeScale", + "TestSetDomainRangeScale", + "TestDomainRangeScale", + "TestToDomain1", + "TestToDomain10", + "TestToDomain100", + "TestToDomainDegrees", + "TestToDomainInt", + "TestFromRange1", + "TestFromRange10", + "TestFromRange100", + "TestFromRangeDegrees", + "TestFromRangeInt", + "TestClosestIndexes", + "TestClosest", + "TestInterval", + "TestIsUniform", + "TestInArray", + "TestTstack", + "TestTsplit", + "TestRowAsDiagonal", + "TestOrient", + "TestCentroid", + "TestFillNan", + "TestHasNanOnly", + "TestNdarrayWrite", + "TestZeros", + "TestOnes", + "TestFull", + "TestIndexAlongLastAxis", ] -class TestAsArray(unittest.TestCase): +class TestMixinDataclassFields(unittest.TestCase): """ - Defines :func:`colour.utilities.array.as_array` definition unit tests - methods. + Define :class:`colour.utilities.array.MixinDataclassFields` class unit + tests methods. """ - def test_as_array(self): - """ - Tests :func:`colour.utilities.array.as_array` definition. - """ + def setUp(self): + """Initialise the common tests attributes.""" - np.testing.assert_equal(as_array([1, 2, 3]), np.array([1, 2, 3])) + @dataclass + class Data(MixinDataclassFields): + a: str + b: str + c: str - self.assertEqual( - as_array([1, 2, 3], DEFAULT_FLOAT_DTYPE).dtype, - DEFAULT_FLOAT_DTYPE) + self._data: Data = Data(a="Foo", b="Bar", c="Baz") - self.assertEqual( - as_array([1, 2, 3], DEFAULT_INT_DTYPE).dtype, DEFAULT_INT_DTYPE) + def test_required_attributes(self): + """Test the presence of required attributes.""" - if six.PY3: # pragma: no cover - np.testing.assert_equal( - as_array(dict(zip('abc', [1, 2, 3])).values()), - np.array([1, 2, 3])) + required_attributes = ("fields",) + for method in required_attributes: + self.assertIn(method, dir(MixinDataclassFields)) -class TestAsIntArray(unittest.TestCase): + def test_fields(self): + """ + Test :meth:`colour.utilities.array.MixinDataclassIterable._fields` + method. + """ + + self.assertTupleEqual( + self._data.fields, + fields(self._data), + ) + + +class TestMixinDataclassIterable(unittest.TestCase): """ - Defines :func:`colour.utilities.array.as_int_array` definition unit tests - methods. + Define :class:`colour.utilities.array.MixinDataclassIterable` class unit + tests methods. """ - def test_as_int_array(self): + def setUp(self): + """Initialise the common tests attributes.""" + + @dataclass + class Data(MixinDataclassIterable): + a: str + b: str + c: str + + self._data: Data = Data(a="Foo", b="Bar", c="Baz") + + def test_required_attributes(self): + """Test the presence of required attributes.""" + + required_attributes = ( + "keys", + "values", + "items", + ) + + for method in required_attributes: + self.assertIn(method, dir(MixinDataclassIterable)) + + def test_required_methods(self): + """Test the presence of required methods.""" + + required_methods = ("__iter__",) + + for method in required_methods: + self.assertIn(method, dir(MixinDataclassIterable)) + + def test__iter__(self): """ - Tests :func:`colour.utilities.array.as_int_array` definition. + Test :meth:`colour.utilities.array.MixinDataclassIterable.__iter__` + method. """ - np.testing.assert_equal( - as_int_array([1.0, 2.0, 3.0]), np.array([1, 2, 3])) + self.assertDictEqual( + {key: value for key, value in self._data}, + {"a": "Foo", "b": "Bar", "c": "Baz"}, + ) - self.assertEqual(as_int_array([1, 2, 3]).dtype, DEFAULT_INT_DTYPE) + def test_keys(self): + """ + Test :meth:`colour.utilities.array.MixinDataclassIterable.keys` + method. + """ + self.assertTupleEqual( + tuple(self._data.keys), + ("a", "b", "c"), + ) -class TestAsFloatArray(unittest.TestCase): + def test_values(self): + """ + Test :meth:`colour.utilities.array.MixinDataclassIterable.values` + method. + """ + + self.assertTupleEqual( + tuple(self._data.values), + ("Foo", "Bar", "Baz"), + ) + + def test_items(self): + """ + Test :meth:`colour.utilities.array.MixinDataclassIterable.items` + method. + """ + + self.assertTupleEqual( + tuple(self._data.items), + (("a", "Foo"), ("b", "Bar"), ("c", "Baz")), + ) + + +class TestMixinDataclassArray(unittest.TestCase): """ - Defines :func:`colour.utilities.array.as_float_array` definition unit tests - methods. + Define :class:`colour.utilities.array.MixinDataclassArray` class unit + tests methods. """ - def test_as_float_array(self): + def setUp(self): + """Initialise the common tests attributes.""" + + @dataclass + class Data(MixinDataclassArray): + a: Optional[Union[float, list, tuple, np.ndarray]] = field( + default_factory=lambda: None + ) + + b: Optional[Union[float, list, tuple, np.ndarray]] = field( + default_factory=lambda: None + ) + + c: Optional[Union[float, list, tuple, np.ndarray]] = field( + default_factory=lambda: None + ) + + self._data: Data = Data( + b=np.array([0.1, 0.2, 0.3]), c=np.array([0.4, 0.5, 0.6]) + ) + self._array: NDArray = np.array( + [ + [np.nan, 0.1, 0.4], + [np.nan, 0.2, 0.5], + [np.nan, 0.3, 0.6], + ] + ) + + def test_required_methods(self): + """Test the presence of required methods.""" + + required_methods = ("__array__",) + + for method in required_methods: + self.assertIn(method, dir(MixinDataclassArray)) + + def test__array__(self): """ - Tests :func:`colour.utilities.array.as_float_array` definition. + Test :meth:`colour.utilities.array.MixinDataclassArray.__array__` + method. """ - np.testing.assert_equal(as_float_array([1, 2, 3]), np.array([1, 2, 3])) + np.testing.assert_array_equal(np.array(self._data), self._array) - self.assertEqual(as_float_array([1, 2, 3]).dtype, DEFAULT_FLOAT_DTYPE) + self.assertEqual( + np.array(self._data, dtype=DEFAULT_INT_DTYPE).dtype, + DEFAULT_INT_DTYPE, + ) -class TestAsNumeric(unittest.TestCase): +class TestMixinDataclassArithmetic(unittest.TestCase): """ - Defines :func:`colour.utilities.array.as_numeric` definition unit tests - methods. + Define :class:`colour.utilities.array.MixinDataclassArithmetic` class unit + tests methods. """ - def test_as_numeric(self): - """ - Tests :func:`colour.utilities.array.as_numeric` definition. + def setUp(self): + """Initialise the common tests attributes.""" + + @dataclass + class Data(MixinDataclassArithmetic): + a: Optional[Union[float, list, tuple, np.ndarray]] = field( + default_factory=lambda: None + ) + + b: Optional[Union[float, list, tuple, np.ndarray]] = field( + default_factory=lambda: None + ) + + c: Optional[Union[float, list, tuple, np.ndarray]] = field( + default_factory=lambda: None + ) + + self._factory: Type[Data] = Data + self._data: Data = Data( + b=np.array([0.1, 0.2, 0.3]), c=np.array([0.4, 0.5, 0.6]) + ) + self._array: NDArray = np.array( + [ + [np.nan, 0.1, 0.4], + [np.nan, 0.2, 0.5], + [np.nan, 0.3, 0.6], + ] + ) + + def test_required_methods(self): + """Test the presence of required methods.""" + + required_methods = ( + "__iadd__", + "__add__", + "__isub__", + "__sub__", + "__imul__", + "__mul__", + "__idiv__", + "__div__", + "__ipow__", + "__pow__", + "arithmetical_operation", + ) + + for method in required_methods: + self.assertIn(method, dir(MixinDataclassArithmetic)) + + def test_arithmetical_operation(self): + """ + Test :meth:`colour.utilities.array.MixinDataclassArithmetic.\ +arithmetical_operation` method. """ - self.assertEqual(as_numeric(1), 1.0) + np.testing.assert_almost_equal( + np.array(self._data.arithmetical_operation(10, "+", False)), + self._array + 10, + decimal=7, + ) + + np.testing.assert_almost_equal( + np.array(self._data.arithmetical_operation(10, "-", False)), + self._array - 10, + decimal=7, + ) + + np.testing.assert_almost_equal( + np.array(self._data.arithmetical_operation(10, "*", False)), + self._array * 10, + decimal=7, + ) + + np.testing.assert_almost_equal( + np.array(self._data.arithmetical_operation(10, "/", False)), + self._array / 10, + decimal=7, + ) + + np.testing.assert_almost_equal( + np.array(self._data.arithmetical_operation(10, "**", False)), + self._array**10, + decimal=7, + ) + + np.testing.assert_almost_equal( + np.array(self._data + 10), self._array + 10, decimal=7 + ) + + np.testing.assert_almost_equal( + np.array(self._data - 10), self._array - 10, decimal=7 + ) + + np.testing.assert_almost_equal( + np.array(self._data * 10), self._array * 10, decimal=7 + ) + + np.testing.assert_almost_equal( + np.array(self._data / 10), self._array / 10, decimal=7 + ) + + np.testing.assert_almost_equal( + np.array(self._data**10), self._array**10, decimal=7 + ) + + data = deepcopy(self._data) + + np.testing.assert_almost_equal( + np.array(data.arithmetical_operation(10, "+", True)), + self._array + 10, + decimal=7, + ) + + np.testing.assert_almost_equal( + np.array(data.arithmetical_operation(10, "-", True)), + self._array, + decimal=7, + ) + + np.testing.assert_almost_equal( + np.array(data.arithmetical_operation(10, "*", True)), + self._array * 10, + decimal=7, + ) + + np.testing.assert_almost_equal( + np.array(data.arithmetical_operation(10, "/", True)), + self._array, + decimal=7, + ) + + np.testing.assert_almost_equal( + np.array(data.arithmetical_operation(10, "**", True)), + self._array**10, + decimal=7, + ) - self.assertEqual(as_numeric(np.array([1])), 1.0) + data = deepcopy(self._data) np.testing.assert_almost_equal( - as_numeric(np.array([1, 2, 3])), np.array([1.0, 2.0, 3.0])) + np.array(data.arithmetical_operation(self._array, "+", False)), + data + self._array, + decimal=7, + ) + + np.testing.assert_almost_equal( + np.array(data.arithmetical_operation(data, "+", False)), + data + data, + decimal=7, + ) + + data = self._factory(1, 2, 3) + + data += 1 + self.assertEqual(data.a, 2) + + data -= 1 + self.assertEqual(data.a, 1) + + data *= 2 + self.assertEqual(data.a, 2) + + data /= 2 + self.assertEqual(data.a, 1) - self.assertIsInstance(as_numeric(1), DEFAULT_FLOAT_DTYPE) + data **= 0.5 + self.assertEqual(data.a, 1) - self.assertIsInstance(as_numeric(1, int), int) - self.assertListEqual(as_numeric(['John', 'Doe']), ['John', 'Doe']) +class TestAsArray(unittest.TestCase): + """ + Define :func:`colour.utilities.array.as_array` definition unit tests + methods. + """ - self.assertEqual(as_numeric('John Doe'), 'John Doe') + def test_as_array(self): + """Test :func:`colour.utilities.array.as_array` definition.""" + + np.testing.assert_equal(as_array([1, 2, 3]), np.array([1, 2, 3])) + + self.assertEqual( + as_array([1, 2, 3], DEFAULT_FLOAT_DTYPE).dtype, DEFAULT_FLOAT_DTYPE + ) + + self.assertEqual( + as_array([1, 2, 3], DEFAULT_INT_DTYPE).dtype, DEFAULT_INT_DTYPE + ) + + np.testing.assert_equal( + as_array(dict(zip("abc", [1, 2, 3])).values()), np.array([1, 2, 3]) + ) class TestAsInt(unittest.TestCase): """ - Defines :func:`colour.utilities.array.as_int` definition unit tests + Define :func:`colour.utilities.array.as_int` definition unit tests methods. """ def test_as_int(self): - """ - Tests :func:`colour.utilities.array.as_int` definition. - """ + """Test :func:`colour.utilities.array.as_int` definition.""" self.assertEqual(as_int(1), 1) self.assertEqual(as_int(np.array([1])), 1) np.testing.assert_almost_equal( - as_int(np.array([1.0, 2.0, 3.0])), np.array([1, 2, 3])) + as_int(np.array([1.0, 2.0, 3.0])), np.array([1, 2, 3]) + ) self.assertEqual( - as_int(np.array([1.0, 2.0, 3.0])).dtype, DEFAULT_INT_DTYPE) + as_int(np.array([1.0, 2.0, 3.0])).dtype, DEFAULT_INT_DTYPE + ) - self.assertIsInstance(as_int(1), int) + self.assertIsInstance(as_int(1), DEFAULT_INT_DTYPE) class TestAsFloat(unittest.TestCase): """ - Defines :func:`colour.utilities.array.as_float` definition unit tests + Define :func:`colour.utilities.array.as_float` definition unit tests methods. """ def test_as_float(self): - """ - Tests :func:`colour.utilities.array.as_float` definition. - """ + """Test :func:`colour.utilities.array.as_float` definition.""" self.assertEqual(as_float(1), 1.0) self.assertEqual(as_float(np.array([1])), 1.0) np.testing.assert_almost_equal( - as_float(np.array([1, 2, 3])), np.array([1.0, 2.0, 3.0])) + as_float(np.array([1, 2, 3])), np.array([1.0, 2.0, 3.0]) + ) self.assertEqual( - as_float(np.array([1, 2, 3])).dtype, DEFAULT_FLOAT_DTYPE) + as_float(np.array([1, 2, 3])).dtype, DEFAULT_FLOAT_DTYPE + ) self.assertIsInstance(as_float(1), DEFAULT_FLOAT_DTYPE) -class TestSetFloatPrecision(unittest.TestCase): +class TestAsIntArray(unittest.TestCase): + """ + Define :func:`colour.utilities.array.as_int_array` definition unit tests + methods. + """ + + def test_as_int_array(self): + """Test :func:`colour.utilities.array.as_int_array` definition.""" + + np.testing.assert_equal( + as_int_array([1.0, 2.0, 3.0]), np.array([1, 2, 3]) + ) + + self.assertEqual(as_int_array([1, 2, 3]).dtype, DEFAULT_INT_DTYPE) + + +class TestAsFloatArray(unittest.TestCase): + """ + Define :func:`colour.utilities.array.as_float_array` definition unit tests + methods. + """ + + def test_as_float_array(self): + """Test :func:`colour.utilities.array.as_float_array` definition.""" + + np.testing.assert_equal(as_float_array([1, 2, 3]), np.array([1, 2, 3])) + + self.assertEqual(as_float_array([1, 2, 3]).dtype, DEFAULT_FLOAT_DTYPE) + + +class TestAsIntScalar(unittest.TestCase): + """ + Define :func:`colour.utilities.array.as_int_scalar` definition unit tests + methods. + """ + + def test_as_int_scalar(self): + """Test :func:`colour.utilities.array.as_int_scalar` definition.""" + + self.assertEqual(as_int_scalar(1.0), 1) + + self.assertEqual(as_int_scalar(1.0).dtype, DEFAULT_INT_DTYPE) + + +class TestAsFloatScalar(unittest.TestCase): + """ + Define :func:`colour.utilities.array.as_float_scalar` definition unit + tests methods. + """ + + def test_as_float_scalar(self): + """Test :func:`colour.utilities.array.as_float_scalar` definition.""" + + self.assertEqual(as_float_scalar(1), 1.0) + + self.assertEqual(as_float_scalar(1).dtype, DEFAULT_FLOAT_DTYPE) + + +class TestSetDefaultIntegerDtype(unittest.TestCase): + """ + Define :func:`colour.utilities.array.set_default_int_dtype` definition unit + tests methods. + """ + + def test_set_default_int_dtype(self): + """ + Test :func:`colour.utilities.array.set_default_int_dtype` definition. + """ + + self.assertEqual(as_int_array(np.ones(3)).dtype, np.int64) + + set_default_int_dtype(np.int32) + + self.assertEqual(as_int_array(np.ones(3)).dtype, np.int32) + + set_default_int_dtype(np.int64) + + self.assertEqual(as_int_array(np.ones(3)).dtype, np.int64) + + def tearDown(self): + """After tests actions.""" + + set_default_int_dtype(np.int64) + + +class TestSetDefaultFloatDtype(unittest.TestCase): """ - Defines :func:`colour.utilities.array.set_float_precision` definition units + Define :func:`colour.utilities.array.set_default_float_dtype` definition unit tests methods. """ - def test_set_float_precision(self): + def test_set_default_float_dtype(self): """ - Tests :func:`colour.utilities.array.set_float_precision` definition. + Test :func:`colour.utilities.array.set_default_float_dtype` + definition. """ self.assertEqual(as_float_array(np.ones(3)).dtype, np.float64) - set_float_precision(np.float16) + set_default_float_dtype(np.float16) self.assertEqual(as_float_array(np.ones(3)).dtype, np.float16) - set_float_precision(np.float64) + set_default_float_dtype(np.float64) self.assertEqual(as_float_array(np.ones(3)).dtype, np.float64) - def test_set_float_precision_enforcement(self): + def test_set_default_float_dtype_enforcement(self): """ - Tests whether :func:`colour.utilities.array.set_float_precision` effect - is applied through most of *Colour* public API. + Test whether :func:`colour.utilities.array.set_default_float_dtype` + effect is applied through most of *Colour* public API. """ - if not is_networkx_installed(): + if not is_networkx_installed(): # pragma: no cover return - from colour.appearance import (CAM_Specification_CAM16, - CAM_Specification_CIECAM02) - from colour.graph.conversion import (CONVERSION_SPECIFICATIONS_DATA, - convert) + from colour.appearance import ( + CAM_Specification_CAM16, + CAM_Specification_CIECAM02, + CAM_Specification_Kim2009, + CAM_Specification_ZCAM, + ) + from colour.graph.conversion import ( + CONVERSION_SPECIFICATIONS_DATA, + convert, + ) dtype = np.float32 - set_float_precision(dtype) + set_default_float_dtype(dtype) for source, target, _callable in CONVERSION_SPECIFICATIONS_DATA: - if target in ('Hexadecimal', 'Munsell Colour'): + if target in ("Hexadecimal", "Munsell Colour"): continue # Spectral distributions are instantiated with float64 data and # spectral up-sampling optimization fails. - if ('Spectral Distribution' in (source, target) or - target == 'Complementary Wavelength' or - target == 'Dominant Wavelength'): + if ( + "Spectral Distribution" in (source, target) + or target == "Complementary Wavelength" + or target == "Dominant Wavelength" + ): continue a = np.array([(0.25, 0.5, 0.25), (0.25, 0.5, 0.25)]) - if source == 'CAM16': + if source == "CAM16": a = CAM_Specification_CAM16(J=0.25, M=0.5, h=0.25) - if source == 'CIECAM02': + if source == "CIECAM02": a = CAM_Specification_CIECAM02(J=0.25, M=0.5, h=0.25) - if source == 'CMYK': + if source == "Kim 2009": + a = CAM_Specification_Kim2009(J=0.25, M=0.5, h=0.25) + + if source == "ZCAM": + a = CAM_Specification_ZCAM(J=0.25, M=0.5, h=0.25) + + if source == "CMYK": a = np.array([(0.25, 0.5, 0.25, 0.5), (0.25, 0.5, 0.25, 0.5)]) - if source == 'Hexadecimal': - a = np.array(['#FFFFFF', '#FFFFFF']) + if source == "Hexadecimal": + a = np.array(["#FFFFFF", "#FFFFFF"]) - if source == 'Munsell Colour': - a = ['4.2YR 8.1/5.3', '4.2YR 8.1/5.3'] + if source == "Munsell Colour": + a = ["4.2YR 8.1/5.3", "4.2YR 8.1/5.3"] - if source == 'Wavelength': + if source == "Wavelength": a = 555 - if source.endswith(' xy') or source.endswith(' uv'): + if source.endswith(" xy") or source.endswith(" uv"): a = np.array([(0.25, 0.5), (0.25, 0.5)]) def dtype_getter(x): - """ - dtype getter callable. - """ - - for specification in ('ATD95', 'CIECAM02', 'CAM16', 'Hunt', - 'LLAB', 'Nayatani95', 'RLAB'): + """Dtype getter callable.""" + + for specification in ( + "ATD95", + "CIECAM02", + "CAM16", + "Hunt", + "Kim 2009", + "LLAB", + "Nayatani95", + "RLAB", + "ZCAM", + ): if target.endswith(specification): - return x[0].dtype + return getattr(x, fields(x)[0].name).dtype return x.dtype self.assertEqual(dtype_getter(convert(a, source, target)), dtype) def tearDown(self): - """ - After tests actions. - """ + """After tests actions.""" - set_float_precision(np.float64) + set_default_float_dtype(np.float64) -class TestSetIntPrecision(unittest.TestCase): +class TestGetDomainRangeScale(unittest.TestCase): """ - Defines :func:`colour.utilities.array.set_int_precision` definition units - tests methods. + Define :func:`colour.utilities.common.get_domain_range_scale` definition + unit tests methods. """ - def test_set_int_precision(self): + def test_get_domain_range_scale(self): """ - Tests :func:`colour.utilities.array.set_int_precision` definition. + Test :func:`colour.utilities.common.get_domain_range_scale` + definition. """ - self.assertEqual(as_int_array(np.ones(3)).dtype, np.int64) + with domain_range_scale("Reference"): + self.assertEqual(get_domain_range_scale(), "reference") - set_int_precision(np.int32) + with domain_range_scale("1"): + self.assertEqual(get_domain_range_scale(), "1") - self.assertEqual(as_int_array(np.ones(3)).dtype, np.int32) + with domain_range_scale("100"): + self.assertEqual(get_domain_range_scale(), "100") - set_int_precision(np.int64) - self.assertEqual(as_int_array(np.ones(3)).dtype, np.int64) +class TestSetDomainRangeScale(unittest.TestCase): + """ + Define :func:`colour.utilities.common.set_domain_range_scale` definition + unit tests methods. + """ - def tearDown(self): + def test_set_domain_range_scale(self): """ - After tests actions. + Test :func:`colour.utilities.common.set_domain_range_scale` + definition. """ - set_int_precision(np.int64) + with domain_range_scale("Reference"): + set_domain_range_scale("1") + self.assertEqual(get_domain_range_scale(), "1") + with domain_range_scale("Reference"): + set_domain_range_scale("100") + self.assertEqual(get_domain_range_scale(), "100") -class TestAsNametuple(unittest.TestCase): + with domain_range_scale("1"): + set_domain_range_scale("Reference") + self.assertEqual(get_domain_range_scale(), "reference") + + self.assertRaises( + ValueError, lambda: set_domain_range_scale("Invalid") + ) + + +class TestDomainRangeScale(unittest.TestCase): """ - Defines :func:`colour.utilities.array.as_namedtuple` definition unit tests - methods. + Define :func:`colour.utilities.common.domain_range_scale` definition + unit tests methods. """ - def test_as_namedtuple(self): + def test_domain_range_scale(self): """ - Tests :func:`colour.utilities.array.as_namedtuple` definition. + Test :func:`colour.utilities.common.domain_range_scale` + definition. """ - NamedTuple = namedtuple('NamedTuple', 'a b c') + self.assertEqual(get_domain_range_scale(), "reference") - a_a = np.ones(3) - a_b = np.ones(3) + 1 - a_c = np.ones(3) + 2 + with domain_range_scale("Reference"): + self.assertEqual(get_domain_range_scale(), "reference") - named_tuple = NamedTuple(a_a, a_b, a_c) + self.assertEqual(get_domain_range_scale(), "reference") - self.assertEqual(named_tuple, as_namedtuple(named_tuple, NamedTuple)) + with domain_range_scale("1"): + self.assertEqual(get_domain_range_scale(), "1") - self.assertEqual( - named_tuple, - as_namedtuple({ - 'a': a_a, - 'b': a_b, - 'c': a_c - }, NamedTuple)) - - self.assertEqual(named_tuple, as_namedtuple([a_a, a_b, a_c], - NamedTuple)) - - a_r = np.array( - [tuple(a) for a in np.transpose((a_a, a_b, a_c)).tolist()], - dtype=[(str('a'), str('f8')), - (str('b'), str('f8')), - (str('c'), str('f8'))]) # yapf: disable - np.testing.assert_array_equal( - np.array(named_tuple), np.array(as_namedtuple(a_r, NamedTuple))) + self.assertEqual(get_domain_range_scale(), "reference") + + with domain_range_scale("100"): + self.assertEqual(get_domain_range_scale(), "100") + + self.assertEqual(get_domain_range_scale(), "reference") + + def fn_a(a): + """Change the domain-range scale for unit testing.""" + + b = to_domain_10(a) + + b *= 2 + + return from_range_100(b) + + with domain_range_scale("Reference"): + with domain_range_scale("1"): + with domain_range_scale("100"): + with domain_range_scale("Ignore"): + self.assertEqual(get_domain_range_scale(), "ignore") + self.assertEqual(fn_a(4), 8) + + self.assertEqual(get_domain_range_scale(), "100") + self.assertEqual(fn_a(40), 8) + + self.assertEqual(get_domain_range_scale(), "1") + self.assertEqual(fn_a(0.4), 0.08) + + self.assertEqual(get_domain_range_scale(), "reference") + self.assertEqual(fn_a(4), 8) + + self.assertEqual(get_domain_range_scale(), "reference") + + @domain_range_scale("1") + def fn_b(a): + """Change the domain-range scale for unit testing.""" + + b = to_domain_10(a) + + b *= 2 + + return from_range_100(b) + + self.assertEqual(fn_b(10), 2.0) + + +class TestToDomain1(unittest.TestCase): + """ + Define :func:`colour.utilities.common.to_domain_1` definition unit + tests methods. + """ + + def test_to_domain_1(self): + """Test :func:`colour.utilities.common.to_domain_1` definition.""" + + with domain_range_scale("Reference"): + self.assertEqual(to_domain_1(1), 1) + + with domain_range_scale("1"): + self.assertEqual(to_domain_1(1), 1) + + with domain_range_scale("100"): + self.assertEqual(to_domain_1(1), 0.01) + + with domain_range_scale("100"): + self.assertEqual(to_domain_1(1, np.pi), 1 / np.pi) + + with domain_range_scale("100"): + self.assertEqual( + to_domain_1(1, dtype=np.float16).dtype, np.float16 + ) + + +class TestToDomain10(unittest.TestCase): + """ + Define :func:`colour.utilities.common.to_domain_10` definition unit + tests methods. + """ + + def test_to_domain_10(self): + """Test :func:`colour.utilities.common.to_domain_10` definition.""" + + with domain_range_scale("Reference"): + self.assertEqual(to_domain_10(1), 1) + + with domain_range_scale("1"): + self.assertEqual(to_domain_10(1), 10) + + with domain_range_scale("100"): + self.assertEqual(to_domain_10(1), 0.1) + + with domain_range_scale("100"): + self.assertEqual(to_domain_10(1, np.pi), 1 / np.pi) + + with domain_range_scale("100"): + self.assertEqual( + to_domain_10(1, dtype=np.float16).dtype, np.float16 + ) + + +class TestToDomain100(unittest.TestCase): + """ + Define :func:`colour.utilities.common.to_domain_100` definition unit + tests methods. + """ + + def test_to_domain_100(self): + """Test :func:`colour.utilities.common.to_domain_100` definition.""" + + with domain_range_scale("Reference"): + self.assertEqual(to_domain_100(1), 1) + + with domain_range_scale("1"): + self.assertEqual(to_domain_100(1), 100) + + with domain_range_scale("100"): + self.assertEqual(to_domain_100(1), 1) + + with domain_range_scale("1"): + self.assertEqual(to_domain_100(1, np.pi), np.pi) + + with domain_range_scale("100"): + self.assertEqual( + to_domain_100(1, dtype=np.float16).dtype, np.float16 + ) + + +class TestToDomainDegrees(unittest.TestCase): + """ + Define :func:`colour.utilities.common.to_domain_degrees` definition unit + tests methods. + """ + + def test_to_domain_degrees(self): + """Test :func:`colour.utilities.common.to_domain_degrees` definition.""" + + with domain_range_scale("Reference"): + self.assertEqual(to_domain_degrees(1), 1) + + with domain_range_scale("1"): + self.assertEqual(to_domain_degrees(1), 360) + + with domain_range_scale("100"): + self.assertEqual(to_domain_degrees(1), 3.6) + + with domain_range_scale("100"): + self.assertEqual(to_domain_degrees(1, np.pi), np.pi / 100) + + with domain_range_scale("100"): + self.assertEqual( + to_domain_degrees(1, dtype=np.float16).dtype, np.float16 + ) + + +class TestToDomainInt(unittest.TestCase): + """ + Define :func:`colour.utilities.common.to_domain_int` definition unit + tests methods. + """ + + def test_to_domain_int(self): + """Test :func:`colour.utilities.common.to_domain_int` definition.""" + + with domain_range_scale("Reference"): + self.assertEqual(to_domain_int(1), 1) + + with domain_range_scale("1"): + self.assertEqual(to_domain_int(1), 255) + + with domain_range_scale("100"): + self.assertEqual(to_domain_int(1), 2.55) + + with domain_range_scale("100"): + self.assertEqual(to_domain_int(1, 10), 10.23) + + with domain_range_scale("100"): + self.assertEqual( + to_domain_int(1, dtype=np.float16).dtype, np.float16 + ) + + +class TestFromRange1(unittest.TestCase): + """ + Define :func:`colour.utilities.common.from_range_1` definition unit + tests methods. + """ + + def test_from_range_1(self): + """Test :func:`colour.utilities.common.from_range_1` definition.""" + + with domain_range_scale("Reference"): + self.assertEqual(from_range_1(1), 1) + + with domain_range_scale("1"): + self.assertEqual(from_range_1(1), 1) + + with domain_range_scale("100"): + self.assertEqual(from_range_1(1), 100) + + with domain_range_scale("100"): + self.assertEqual(from_range_1(1, np.pi), 1 * np.pi) + + +class TestFromRange10(unittest.TestCase): + """ + Define :func:`colour.utilities.common.from_range_10` definition unit + tests methods. + """ + + def test_from_range_10(self): + """Test :func:`colour.utilities.common.from_range_10` definition.""" + + with domain_range_scale("Reference"): + self.assertEqual(from_range_10(1), 1) + + with domain_range_scale("1"): + self.assertEqual(from_range_10(1), 0.1) + + with domain_range_scale("100"): + self.assertEqual(from_range_10(1), 10) + + with domain_range_scale("100"): + self.assertEqual(from_range_10(1, np.pi), 1 * np.pi) + + +class TestFromRange100(unittest.TestCase): + """ + Define :func:`colour.utilities.common.from_range_100` definition unit + tests methods. + """ + + def test_from_range_100(self): + """Test :func:`colour.utilities.common.from_range_100` definition.""" + + with domain_range_scale("Reference"): + self.assertEqual(from_range_100(1), 1) + + with domain_range_scale("1"): + self.assertEqual(from_range_100(1), 0.01) + + with domain_range_scale("100"): + self.assertEqual(from_range_100(1), 1) + + with domain_range_scale("1"): + self.assertEqual(from_range_100(1, np.pi), 1 / np.pi) + + +class TestFromRangeDegrees(unittest.TestCase): + """ + Define :func:`colour.utilities.common.from_range_degrees` definition unit + tests methods. + """ + + def test_from_range_degrees(self): + """Test :func:`colour.utilities.common.from_range_degrees` definition.""" + + with domain_range_scale("Reference"): + self.assertEqual(from_range_degrees(1), 1) + + with domain_range_scale("1"): + self.assertEqual(from_range_degrees(1), 1 / 360) + + with domain_range_scale("100"): + self.assertEqual(from_range_degrees(1), 1 / 3.6) + + with domain_range_scale("100"): + self.assertEqual(from_range_degrees(1, np.pi), 1 / (np.pi / 100)) + + +class TestFromRangeInt(unittest.TestCase): + """ + Define :func:`colour.utilities.common.from_range_int` definition unit + tests methods. + """ + + def test_from_range_int(self): + """Test :func:`colour.utilities.common.from_range_int` definition.""" + + with domain_range_scale("Reference"): + self.assertEqual(from_range_int(1), 1) + + with domain_range_scale("1"): + self.assertEqual(from_range_int(1), 1 / 255) + + with domain_range_scale("100"): + self.assertEqual(from_range_int(1), 1 / 2.55) + + with domain_range_scale("100"): + self.assertEqual(from_range_int(1, 10), 1 / (1023 / 100)) + + with domain_range_scale("100"): + self.assertEqual( + from_range_int(1, dtype=np.float16).dtype, np.float16 + ) class TestClosestIndexes(unittest.TestCase): """ - Defines :func:`colour.utilities.array.closest_indexes` definition unit + Define :func:`colour.utilities.array.closest_indexes` definition unit tests methods. """ def test_closest_indexes(self): - """ - Tests :func:`colour.utilities.array.closest_indexes` definition. - """ - - a = np.array([ - 24.31357115, - 63.62396289, - 55.71528816, - 62.70988028, - 46.84480573, - 25.40026416, - ]) + """Test :func:`colour.utilities.array.closest_indexes` definition.""" + + a = np.array( + [ + 24.31357115, + 63.62396289, + 55.71528816, + 62.70988028, + 46.84480573, + 25.40026416, + ] + ) self.assertEqual(closest_indexes(a, 63.05), 3) @@ -365,28 +1127,29 @@ def test_closest_indexes(self): np.testing.assert_array_equal( closest_indexes(a, np.array([63.05, 51.15, 24.90])), - np.array([3, 4, 5])) + np.array([3, 4, 5]), + ) class TestClosest(unittest.TestCase): """ - Defines :func:`colour.utilities.array.closest` definition unit tests + Define :func:`colour.utilities.array.closest` definition unit tests methods. """ def test_closest(self): - """ - Tests :func:`colour.utilities.array.closest` definition. - """ - - a = np.array([ - 24.31357115, - 63.62396289, - 55.71528816, - 62.70988028, - 46.84480573, - 25.40026416, - ]) + """Test :func:`colour.utilities.array.closest` definition.""" + + a = np.array( + [ + 24.31357115, + 63.62396289, + 55.71528816, + 62.70988028, + 46.84480573, + 25.40026416, + ] + ) self.assertEqual(closest(a, 63.05), 62.70988028) @@ -397,108 +1160,45 @@ def test_closest(self): np.testing.assert_almost_equal( closest(a, np.array([63.05, 51.15, 24.90])), np.array([62.70988028, 46.84480573, 25.40026416]), - decimal=7) - - -class TestNormaliseMaximum(unittest.TestCase): - """ - Defines :func:`colour.utilities.array.normalise_maximum` definition units - tests methods. - """ - - def test_normalise_maximum(self): - """ - Tests :func:`colour.utilities.array.normalise_maximum` definition. - """ - - np.testing.assert_almost_equal( - normalise_maximum(np.array([0.20654008, 0.12197225, 0.05136952])), - np.array([1.00000000, 0.59055003, 0.24871454]), - decimal=7) - - np.testing.assert_almost_equal( - normalise_maximum( - np.array([ - [0.20654008, 0.12197225, 0.05136952], - [0.14222010, 0.23042768, 0.10495772], - [0.07818780, 0.06157201, 0.28099326], - ])), - np.array([ - [0.73503571, 0.43407536, 0.18281406], - [0.50613349, 0.82004700, 0.37352398], - [0.27825507, 0.21912273, 1.00000000], - ]), - decimal=7) - - np.testing.assert_almost_equal( - normalise_maximum( - np.array([ - [0.20654008, 0.12197225, 0.05136952], - [0.14222010, 0.23042768, 0.10495772], - [0.07818780, 0.06157201, 0.28099326], - ]), - axis=-1), - np.array([ - [1.00000000, 0.59055003, 0.24871454], - [0.61720059, 1.00000000, 0.45549094], - [0.27825507, 0.21912273, 1.00000000], - ]), - decimal=7) - - np.testing.assert_almost_equal( - normalise_maximum( - np.array([0.20654008, 0.12197225, 0.05136952]), factor=10), - np.array([10.00000000, 5.90550028, 2.48714535]), - decimal=7) - - np.testing.assert_almost_equal( - normalise_maximum( - np.array([-0.11518475, -0.10080000, 0.05089373])), - np.array([0.00000000, 0.00000000, 1.00000000]), - decimal=7) - - np.testing.assert_almost_equal( - normalise_maximum( - np.array([-0.20654008, -0.12197225, 0.05136952]), clip=False), - np.array([-4.02067374, -2.37440899, 1.00000000]), - decimal=7) + decimal=7, + ) class TestInterval(unittest.TestCase): """ - Defines :func:`colour.utilities.array.interval` definition unit tests + Define :func:`colour.utilities.array.interval` definition unit tests methods. """ def test_interval(self): - """ - Tests :func:`colour.utilities.array.interval` definition. - """ + """Test :func:`colour.utilities.array.interval` definition.""" np.testing.assert_almost_equal( - interval(range(0, 10, 2)), np.array([2])) + interval(range(0, 10, 2)), np.array([2]) + ) np.testing.assert_almost_equal( - interval(range(0, 10, 2), False), np.array([2, 2, 2, 2])) + interval(range(0, 10, 2), False), np.array([2, 2, 2, 2]) + ) np.testing.assert_almost_equal( - interval([1, 2, 3, 4, 6, 6.5]), np.array([0.5, 1.0, 2.0])) + interval([1, 2, 3, 4, 6, 6.5]), np.array([0.5, 1.0, 2.0]) + ) np.testing.assert_almost_equal( interval([1, 2, 3, 4, 6, 6.5], False), - np.array([1.0, 1.0, 1.0, 2.0, 0.5])) + np.array([1.0, 1.0, 1.0, 2.0, 0.5]), + ) class TestIsUniform(unittest.TestCase): """ - Defines :func:`colour.utilities.array.is_uniform` definition unit tests + Define :func:`colour.utilities.array.is_uniform` definition unit tests methods. """ def test_is_uniform(self): - """ - Tests :func:`colour.utilities.array.is_uniform` definition. - """ + """Test :func:`colour.utilities.array.is_uniform` definition.""" self.assertTrue(is_uniform(range(0, 10, 2))) @@ -507,60 +1207,66 @@ def test_is_uniform(self): class TestInArray(unittest.TestCase): """ - Defines :func:`colour.utilities.array.in_array` definition unit tests + Define :func:`colour.utilities.array.in_array` definition unit tests methods. """ def test_in_array(self): - """ - Tests :func:`colour.utilities.array.in_array` definition. - """ + """Test :func:`colour.utilities.array.in_array` definition.""" self.assertTrue( np.array_equal( in_array(np.array([0.50, 0.60]), np.linspace(0, 10, 101)), - np.array([True, True]))) + np.array([True, True]), + ) + ) self.assertFalse( np.array_equal( in_array(np.array([0.50, 0.61]), np.linspace(0, 10, 101)), - np.array([True, True]))) + np.array([True, True]), + ) + ) self.assertTrue( np.array_equal( in_array(np.array([[0.50], [0.60]]), np.linspace(0, 10, 101)), - np.array([[True], [True]]))) + np.array([[True], [True]]), + ) + ) def test_n_dimensional_in_array(self): """ - Tests :func:`colour.utilities.array.in_array` definition n-dimensional + Test :func:`colour.utilities.array.in_array` definition n-dimensional support. """ np.testing.assert_almost_equal( in_array(np.array([0.50, 0.60]), np.linspace(0, 10, 101)).shape, - np.array([2])) + np.array([2]), + ) np.testing.assert_almost_equal( in_array(np.array([[0.50, 0.60]]), np.linspace(0, 10, 101)).shape, - np.array([1, 2])) + np.array([1, 2]), + ) np.testing.assert_almost_equal( - in_array(np.array([[0.50], [0.60]]), np.linspace(0, 10, - 101)).shape, - np.array([2, 1])) + in_array( + np.array([[0.50], [0.60]]), np.linspace(0, 10, 101) + ).shape, + np.array([2, 1]), + ) class TestTstack(unittest.TestCase): """ - Defines :func:`colour.utilities.array.tstack` definition unit tests + Define :func:`colour.utilities.array.tstack` definition unit tests methods. """ def test_tstack(self): - """ - Tests :func:`colour.utilities.array.tstack` definition. - """ + """Test :func:`colour.utilities.array.tstack` definition.""" a = 0 np.testing.assert_almost_equal(tstack([a, a, a]), np.array([0, 0, 0])) @@ -568,288 +1274,267 @@ def test_tstack(self): a = np.arange(0, 6) np.testing.assert_almost_equal( tstack([a, a, a]), - np.array([ - [0, 0, 0], - [1, 1, 1], - [2, 2, 2], - [3, 3, 3], - [4, 4, 4], - [5, 5, 5], - ])) + np.array( + [ + [0, 0, 0], + [1, 1, 1], + [2, 2, 2], + [3, 3, 3], + [4, 4, 4], + [5, 5, 5], + ] + ), + ) a = np.reshape(a, (1, 6)) np.testing.assert_almost_equal( tstack([a, a, a]), - np.array([[ - [0, 0, 0], - [1, 1, 1], - [2, 2, 2], - [3, 3, 3], - [4, 4, 4], - [5, 5, 5], - ]])) + np.array( + [ + [ + [0, 0, 0], + [1, 1, 1], + [2, 2, 2], + [3, 3, 3], + [4, 4, 4], + [5, 5, 5], + ] + ] + ), + ) a = np.reshape(a, (1, 2, 3)) np.testing.assert_almost_equal( tstack([a, a, a]), - np.array([[ - [[0, 0, 0], [1, 1, 1], [2, 2, 2]], - [[3, 3, 3], [4, 4, 4], [5, 5, 5]], - ]])) + np.array( + [ + [ + [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + [[3, 3, 3], [4, 4, 4], [5, 5, 5]], + ] + ] + ), + ) class TestTsplit(unittest.TestCase): """ - Defines :func:`colour.utilities.array.tsplit` definition unit tests + Define :func:`colour.utilities.array.tsplit` definition unit tests methods. """ def test_tsplit(self): - """ - Tests :func:`colour.utilities.array.tsplit` definition. - """ + """Test :func:`colour.utilities.array.tsplit` definition.""" a = np.array([0, 0, 0]) np.testing.assert_almost_equal(tsplit(a), np.array([0, 0, 0])) - a = np.array([ - [0, 0, 0], - [1, 1, 1], - [2, 2, 2], - [3, 3, 3], - [4, 4, 4], - [5, 5, 5], - ]) + a = np.array( + [ + [0, 0, 0], + [1, 1, 1], + [2, 2, 2], + [3, 3, 3], + [4, 4, 4], + [5, 5, 5], + ] + ) np.testing.assert_almost_equal( tsplit(a), - np.array([ - [0, 1, 2, 3, 4, 5], - [0, 1, 2, 3, 4, 5], - [0, 1, 2, 3, 4, 5], - ])) - - a = np.array([ - [[0, 0, 0], [1, 1, 1], [2, 2, 2], [3, 3, 3], [4, 4, 4], [5, 5, 5]], - ]) + np.array( + [ + [0, 1, 2, 3, 4, 5], + [0, 1, 2, 3, 4, 5], + [0, 1, 2, 3, 4, 5], + ] + ), + ) + + a = np.array( + [ + [ + [0, 0, 0], + [1, 1, 1], + [2, 2, 2], + [3, 3, 3], + [4, 4, 4], + [5, 5, 5], + ], + ] + ) np.testing.assert_almost_equal( tsplit(a), - np.array([ - [[0, 1, 2, 3, 4, 5]], - [[0, 1, 2, 3, 4, 5]], - [[0, 1, 2, 3, 4, 5]], - ])) - - a = np.array([[ - [[0, 0, 0], [1, 1, 1], [2, 2, 2]], - [[3, 3, 3], [4, 4, 4], [5, 5, 5]], - ]]) + np.array( + [ + [[0, 1, 2, 3, 4, 5]], + [[0, 1, 2, 3, 4, 5]], + [[0, 1, 2, 3, 4, 5]], + ] + ), + ) + + a = np.array( + [ + [ + [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + [[3, 3, 3], [4, 4, 4], [5, 5, 5]], + ] + ] + ) np.testing.assert_almost_equal( tsplit(a), - np.array([ - [[[0, 1, 2], [3, 4, 5]]], - [[[0, 1, 2], [3, 4, 5]]], - [[[0, 1, 2], [3, 4, 5]]], - ])) + np.array( + [ + [[[0, 1, 2], [3, 4, 5]]], + [[[0, 1, 2], [3, 4, 5]]], + [[[0, 1, 2], [3, 4, 5]]], + ] + ), + ) class TestRowAsDiagonal(unittest.TestCase): """ - Defines :func:`colour.utilities.array.row_as_diagonal` definition unit + Define :func:`colour.utilities.array.row_as_diagonal` definition unit tests methods. """ def test_row_as_diagonal(self): - """ - Tests :func:`colour.utilities.array.row_as_diagonal` definition. - """ + """Test :func:`colour.utilities.array.row_as_diagonal` definition.""" np.testing.assert_almost_equal( - row_as_diagonal(np.array( - [[0.25891593, 0.07299478, 0.36586996], - [0.30851087, 0.37131459, 0.16274825], - [0.71061831, 0.67718718, 0.09562581], - [0.71588836, 0.76772047, 0.15476079], - [0.92985142, 0.22263399, 0.88027331]]) + row_as_diagonal( + np.array( + [ + [0.25891593, 0.07299478, 0.36586996], + [0.30851087, 0.37131459, 0.16274825], + [0.71061831, 0.67718718, 0.09562581], + [0.71588836, 0.76772047, 0.15476079], + [0.92985142, 0.22263399, 0.88027331], + ] + ) ), np.array( - [[[0.25891593, 0.00000000, 0.00000000], - [0.00000000, 0.07299478, 0.00000000], - [0.00000000, 0.00000000, 0.36586996]], - [[0.30851087, 0.00000000, 0.00000000], - [0.00000000, 0.37131459, 0.00000000], - [0.00000000, 0.00000000, 0.16274825]], - [[0.71061831, 0.00000000, 0.00000000], - [0.00000000, 0.67718718, 0.00000000], - [0.00000000, 0.00000000, 0.09562581]], - [[0.71588836, 0.00000000, 0.00000000], - [0.00000000, 0.76772047, 0.00000000], - [0.00000000, 0.00000000, 0.15476079]], - [[0.92985142, 0.00000000, 0.00000000], - [0.00000000, 0.22263399, 0.00000000], - [0.00000000, 0.00000000, 0.88027331]]] - ) - ) # yapf: disable - - -class TestDotVector(unittest.TestCase): - """ - Defines :func:`colour.utilities.array.vector_dot` definition unit tests - methods. - """ - - def test_vector_dot(self): - """ - Tests :func:`colour.utilities.array.vector_dot` definition. - """ - - m = np.array([ - [0.7328, 0.4296, -0.1624], - [-0.7036, 1.6975, 0.0061], - [0.0030, 0.0136, 0.9834], - ]) - m = np.reshape(np.tile(m, (6, 1)), (6, 3, 3)) - - v = np.array([0.20654008, 0.12197225, 0.05136952]) - v = np.tile(v, (6, 1)) - - np.testing.assert_almost_equal( - vector_dot(m, v), - np.array([ - [0.19540944, 0.06203965, 0.05279523], - [0.19540944, 0.06203965, 0.05279523], - [0.19540944, 0.06203965, 0.05279523], - [0.19540944, 0.06203965, 0.05279523], - [0.19540944, 0.06203965, 0.05279523], - [0.19540944, 0.06203965, 0.05279523], - ]), - decimal=7) - - -class TestDotMatrix(unittest.TestCase): - """ - Defines :func:`colour.utilities.array.matrix_dot` definition unit tests - methods. - """ - - def test_matrix_dot(self): - """ - Tests :func:`colour.utilities.array.matrix_dot` definition. - """ - - a = np.array([ - [0.7328, 0.4296, -0.1624], - [-0.7036, 1.6975, 0.0061], - [0.0030, 0.0136, 0.9834], - ]) - a = np.reshape(np.tile(a, (6, 1)), (6, 3, 3)) - - b = a - - np.testing.assert_almost_equal( - matrix_dot(a, b), - np.array( - [[[0.23424208, 1.04184824, -0.27609032], - [-1.70994078, 2.57932265, 0.13061813], - [-0.00442036, 0.03774904, 0.96667132]], - [[0.23424208, 1.04184824, -0.27609032], - [-1.70994078, 2.57932265, 0.13061813], - [-0.00442036, 0.03774904, 0.96667132]], - [[0.23424208, 1.04184824, -0.27609032], - [-1.70994078, 2.57932265, 0.13061813], - [-0.00442036, 0.03774904, 0.96667132]], - [[0.23424208, 1.04184824, -0.27609032], - [-1.70994078, 2.57932265, 0.13061813], - [-0.00442036, 0.03774904, 0.96667132]], - [[0.23424208, 1.04184824, -0.27609032], - [-1.70994078, 2.57932265, 0.13061813], - [-0.00442036, 0.03774904, 0.96667132]], - [[0.23424208, 1.04184824, -0.27609032], - [-1.70994078, 2.57932265, 0.13061813], - [-0.00442036, 0.03774904, 0.96667132]]] + [ + [ + [0.25891593, 0.00000000, 0.00000000], + [0.00000000, 0.07299478, 0.00000000], + [0.00000000, 0.00000000, 0.36586996], + ], + [ + [0.30851087, 0.00000000, 0.00000000], + [0.00000000, 0.37131459, 0.00000000], + [0.00000000, 0.00000000, 0.16274825], + ], + [ + [0.71061831, 0.00000000, 0.00000000], + [0.00000000, 0.67718718, 0.00000000], + [0.00000000, 0.00000000, 0.09562581], + ], + [ + [0.71588836, 0.00000000, 0.00000000], + [0.00000000, 0.76772047, 0.00000000], + [0.00000000, 0.00000000, 0.15476079], + ], + [ + [0.92985142, 0.00000000, 0.00000000], + [0.00000000, 0.22263399, 0.00000000], + [0.00000000, 0.00000000, 0.88027331], + ], + ] ), - decimal=7) # yapf: disable + ) class TestOrient(unittest.TestCase): """ - Defines :func:`colour.utilities.array.orient` definition unit tests + Define :func:`colour.utilities.array.orient` definition unit tests methods. """ def test_orient(self): - """ - Tests :func:`colour.utilities.array.orient` definition. - """ + """Test :func:`colour.utilities.array.orient` definition.""" a = np.tile(np.arange(5), (5, 1)) - np.testing.assert_almost_equal(orient(a, 'Null'), a, decimal=7) - np.testing.assert_almost_equal( - orient(a, 'Flip'), - np.array([ - [4, 3, 2, 1, 0], - [4, 3, 2, 1, 0], - [4, 3, 2, 1, 0], - [4, 3, 2, 1, 0], - [4, 3, 2, 1, 0], - ]), - decimal=7) + orient(a, "Flip"), + np.array( + [ + [4, 3, 2, 1, 0], + [4, 3, 2, 1, 0], + [4, 3, 2, 1, 0], + [4, 3, 2, 1, 0], + [4, 3, 2, 1, 0], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - orient(a, 'Flop'), - np.array([ - [0, 1, 2, 3, 4], - [0, 1, 2, 3, 4], - [0, 1, 2, 3, 4], - [0, 1, 2, 3, 4], - [0, 1, 2, 3, 4], - ]), - decimal=7) + orient(a, "Flop"), + np.array( + [ + [0, 1, 2, 3, 4], + [0, 1, 2, 3, 4], + [0, 1, 2, 3, 4], + [0, 1, 2, 3, 4], + [0, 1, 2, 3, 4], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - orient(a, '90 CW'), - np.array([ - [0, 0, 0, 0, 0], - [1, 1, 1, 1, 1], - [2, 2, 2, 2, 2], - [3, 3, 3, 3, 3], - [4, 4, 4, 4, 4], - ]), - decimal=7) + orient(a, "90 CW"), + np.array( + [ + [0, 0, 0, 0, 0], + [1, 1, 1, 1, 1], + [2, 2, 2, 2, 2], + [3, 3, 3, 3, 3], + [4, 4, 4, 4, 4], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - orient(a, '90 CCW'), - np.array([ - [4, 4, 4, 4, 4], - [3, 3, 3, 3, 3], - [2, 2, 2, 2, 2], - [1, 1, 1, 1, 1], - [0, 0, 0, 0, 0], - ]), - decimal=7) + orient(a, "90 CCW"), + np.array( + [ + [4, 4, 4, 4, 4], + [3, 3, 3, 3, 3], + [2, 2, 2, 2, 2], + [1, 1, 1, 1, 1], + [0, 0, 0, 0, 0], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( - orient(a, '180'), - np.array([ - [4, 3, 2, 1, 0], - [4, 3, 2, 1, 0], - [4, 3, 2, 1, 0], - [4, 3, 2, 1, 0], - [4, 3, 2, 1, 0], - ]), - decimal=7) + orient(a, "180"), + np.array( + [ + [4, 3, 2, 1, 0], + [4, 3, 2, 1, 0], + [4, 3, 2, 1, 0], + [4, 3, 2, 1, 0], + [4, 3, 2, 1, 0], + ] + ), + decimal=7, + ) class TestCentroid(unittest.TestCase): """ - Defines :func:`colour.utilities.array.centroid` definition unit tests + Define :func:`colour.utilities.array.centroid` definition unit tests methods. """ def test_centroid(self): - """ - Tests :func:`colour.utilities.array.centroid` definition. - """ + """Test :func:`colour.utilities.array.centroid` definition.""" a = np.arange(5) np.testing.assert_array_equal(centroid(a), np.array([3])) @@ -864,90 +1549,53 @@ def test_centroid(self): np.testing.assert_array_equal(centroid(a), np.array([4, 6, 1])) -class TestLinearConversion(unittest.TestCase): +class TestFillNan(unittest.TestCase): """ - Defines :func:`colour.utilities.array.linear_conversion` definition unit - tests methods. + Define :func:`colour.utilities.array.fill_nan` definition unit tests + methods. """ - def test_linear_conversion(self): - """ - Tests :func:`colour.utilities.array.linear_conversion` definition. - """ + def test_fill_nan(self): + """Test :func:`colour.utilities.array.fill_nan` definition.""" + a = np.array([0.1, 0.2, np.nan, 0.4, 0.5]) np.testing.assert_almost_equal( - linear_conversion( - np.linspace(0, 1, 10), np.array([0, 1]), np.array([1, np.pi])), - np.array([ - 1.00000000, 1.23795474, 1.47590948, 1.71386422, 1.95181896, - 2.18977370, 2.42772844, 2.66568318, 2.90363791, 3.14159265 - ]), - decimal=8) - - -class TestLerp(unittest.TestCase): - """ - Defines :func:`colour.utilities.array.lerp` definition unit - tests methods. - """ - - def test_lerp(self): - """ - Tests :func:`colour.utilities.array.lerp` definition. - """ + fill_nan(a), np.array([0.1, 0.2, 0.3, 0.4, 0.5]), decimal=7 + ) np.testing.assert_almost_equal( - lerp( - np.linspace(0, 1, 10), - np.linspace(0, 2, 10), - np.linspace(0, 1, 10), - ), - np.array([ - 0.00000000, - 0.12345679, - 0.27160494, - 0.44444444, - 0.64197531, - 0.86419753, - 1.11111111, - 1.38271605, - 1.67901235, - 2.00000000, - ]), - decimal=8) + fill_nan(a, method="Constant", default=8.0), + np.array([0.1, 0.2, 8.0, 0.4, 0.5]), + decimal=7, + ) -class TestFillNan(unittest.TestCase): +class TestHasNanOnly(unittest.TestCase): """ - Defines :func:`colour.utilities.array.fill_nan` definition unit tests + Define :func:`colour.utilities.array.has_only_nan` definition unit tests methods. """ - def test_fill_nan(self): - """ - Tests :func:`colour.utilities.array.fill_nan` definition. - """ + def test_has_only_nan(self): + """Test :func:`colour.utilities.array.has_only_nan` definition.""" - a = np.array([0.1, 0.2, np.nan, 0.4, 0.5]) - np.testing.assert_almost_equal( - fill_nan(a), np.array([0.1, 0.2, 0.3, 0.4, 0.5]), decimal=7) + self.assertTrue(has_only_nan(None)) - np.testing.assert_almost_equal( - fill_nan(a, method='Constant', default=8.0), - np.array([0.1, 0.2, 8.0, 0.4, 0.5]), - decimal=7) + self.assertTrue(has_only_nan([None, None])) + + self.assertFalse(has_only_nan([True, None])) + + self.assertFalse(has_only_nan([0.1, np.nan, 0.3])) class TestNdarrayWrite(unittest.TestCase): """ - Defines :func:`colour.utilities.array.ndarray_write` definition unit tests + Define :func:`colour.utilities.array.ndarray_write` definition unit tests methods. """ def test_ndarray_write(self): - """ - Tests :func:`colour.utilities.array.ndarray_write` definition. - """ + """Test :func:`colour.utilities.array.ndarray_write` definition.""" a = np.linspace(0, 1, 10) a.setflags(write=False) @@ -961,97 +1609,117 @@ def test_ndarray_write(self): class TestZeros(unittest.TestCase): """ - Defines :func:`colour.utilities.array.zeros` definition unit tests + Define :func:`colour.utilities.array.zeros` definition unit tests methods. """ def test_zeros(self): - """ - Tests :func:`colour.utilities.array.zeros` definition. - """ + """Test :func:`colour.utilities.array.zeros` definition.""" np.testing.assert_equal(zeros(3), np.zeros(3)) class TestOnes(unittest.TestCase): """ - Defines :func:`colour.utilities.array.ones` definition unit tests + Define :func:`colour.utilities.array.ones` definition unit tests methods. """ def test_ones(self): - """ - Tests :func:`colour.utilities.array.ones` definition. - """ + """Test :func:`colour.utilities.array.ones` definition.""" np.testing.assert_equal(ones(3), np.ones(3)) class TestFull(unittest.TestCase): """ - Defines :func:`colour.utilities.array.full` definition unit tests + Define :func:`colour.utilities.array.full` definition unit tests methods. """ def test_full(self): - """ - Tests :func:`colour.utilities.array.full` definition. - """ + """Test :func:`colour.utilities.array.full` definition.""" np.testing.assert_equal(full(3, 0.5), np.full(3, 0.5)) class TestIndexAlongLastAxis(unittest.TestCase): """ - Defines :func:`colour.utilities.array.index_along_last_axis` definition + Define :func:`colour.utilities.array.index_along_last_axis` definition unit tests methods. """ def test_index_along_last_axis(self): - """ - Tests :func:`colour.utilities.array.index_along_last_axis` definition. - """ - a = np.array([[[[0.51090627, 0.86191718, 0.8687926], - [0.82738158, 0.80587656, 0.28285687]], - [[0.84085977, 0.03851814, 0.06057988], - [0.94659267, 0.79308353, 0.30870888]]], - [[[0.50758436, 0.24066455, 0.20199051], - [0.4507304, 0.84189245, 0.81160878]], - [[0.75421871, 0.88187494, 0.01612045], - [0.38777511, 0.58905552, 0.32970469]]], - [[[0.99285824, 0.738076, 0.0716432], - [0.35847844, 0.0367514, 0.18586322]], - [[0.72674561, 0.0822759, 0.9771182], - [0.90644279, 0.09689787, 0.93483977]]]]) - - indexes = np.array([[[0, 1], [0, 1]], [[2, 1], [2, 1]], [[2, 1], - [2, 0]]]) + """Test :func:`colour.utilities.array.index_along_last_axis` definition.""" + a = np.array( + [ + [ + [ + [0.51090627, 0.86191718, 0.8687926], + [0.82738158, 0.80587656, 0.28285687], + ], + [ + [0.84085977, 0.03851814, 0.06057988], + [0.94659267, 0.79308353, 0.30870888], + ], + ], + [ + [ + [0.50758436, 0.24066455, 0.20199051], + [0.4507304, 0.84189245, 0.81160878], + ], + [ + [0.75421871, 0.88187494, 0.01612045], + [0.38777511, 0.58905552, 0.32970469], + ], + ], + [ + [ + [0.99285824, 0.738076, 0.0716432], + [0.35847844, 0.0367514, 0.18586322], + ], + [ + [0.72674561, 0.0822759, 0.9771182], + [0.90644279, 0.09689787, 0.93483977], + ], + ], + ] + ) + + indexes = np.array( + [[[0, 1], [0, 1]], [[2, 1], [2, 1]], [[2, 1], [2, 0]]] + ) np.testing.assert_equal( index_along_last_axis(a, indexes), - np.array([[[0.51090627, 0.80587656], [0.84085977, 0.79308353]], - [[0.20199051, 0.84189245], [0.01612045, 0.58905552]], - [[0.0716432, 0.0367514], [0.9771182, 0.90644279]]])) + np.array( + [ + [[0.51090627, 0.80587656], [0.84085977, 0.79308353]], + [[0.20199051, 0.84189245], [0.01612045, 0.58905552]], + [[0.0716432, 0.0367514], [0.9771182, 0.90644279]], + ] + ), + ) def test_compare_with_argmin_argmax(self): """ - Tests :func:`colour.utilities.array.index_along_last_axis` definition + Test :func:`colour.utilities.array.index_along_last_axis` definition by comparison with :func:`argmin` and :func:`argmax`. """ a = np.random.random((2, 3, 4, 5, 6, 7)) np.testing.assert_equal( - index_along_last_axis(a, np.argmin(a, axis=-1)), np.min( - a, axis=-1)) + index_along_last_axis(a, np.argmin(a, axis=-1)), np.min(a, axis=-1) + ) np.testing.assert_equal( - index_along_last_axis(a, np.argmax(a, axis=-1)), np.max( - a, axis=-1)) + index_along_last_axis(a, np.argmax(a, axis=-1)), np.max(a, axis=-1) + ) def test_exceptions(self): """ - Tests :func:`colour.utilities.array.index_along_last_axis` definition + Test :func:`colour.utilities.array.index_along_last_axis` definition handling of invalid inputs. """ @@ -1069,9 +1737,9 @@ def test_exceptions(self): # Non-integer indexes with self.assertRaises(IndexError): - indexes = np.array([0., 0.]) + indexes = np.array([0.0, 0.0]) index_along_last_axis(a, indexes) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/utilities/tests/test_common.py b/colour/utilities/tests/test_common.py index 928b373849..f5d449c737 100644 --- a/colour/utilities/tests/test_common.py +++ b/colour/utilities/tests/test_common.py @@ -1,76 +1,207 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.utilities.common` module. -""" +"""Defines the unit tests for the :mod:`colour.utilities.common` module.""" -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np import unittest -import six -from collections import OrderedDict from functools import partial +from colour.hints import Any, Floating, Number, Tuple from colour.utilities import ( - batch, multiprocessing_pool, is_iterable, is_string, is_numeric, - is_integer, is_sibling, filter_kwargs, filter_mapping, first_item, - get_domain_range_scale, set_domain_range_scale, domain_range_scale, - to_domain_1, to_domain_10, to_domain_100, to_domain_int, to_domain_degrees, - from_range_1, from_range_10, from_range_100, from_range_int, - from_range_degrees) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + CacheRegistry, + attest, + batch, + multiprocessing_pool, + is_iterable, + is_string, + is_numeric, + is_integer, + is_sibling, + filter_kwargs, + filter_mapping, + first_item, + validate_method, + optional, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'TestBatch', 'TestMultiprocessingPool', 'TestIsIterable', 'TestIsString', - 'TestIsNumeric', 'TestIsInteger', 'TestIsSibling', 'TestFilterKwargs', - 'TestFilterMapping', 'TestFirstItem', 'TestGetDomainRangeScale', - 'TestSetDomainRangeScale', 'TestDomainRangeScale', 'TestToDomain1', - 'TestToDomain10', 'TestToDomain100', 'TestToDomainDegrees', - 'TestToDomainInt', 'TestFromRange1', 'TestFromRange10', 'TestFromRange100', - 'TestFromRangeDegrees', 'TestFromRangeInt' + "TestCacheRegistry", + "TestAttest", + "TestBatch", + "TestMultiprocessingPool", + "TestIsIterable", + "TestIsString", + "TestIsNumeric", + "TestIsInteger", + "TestIsSibling", + "TestFilterKwargs", + "TestFilterMapping", + "TestFirstItem", + "TestValidateMethod", + "TestOptional", ] +class TestCacheRegistry(unittest.TestCase): + """ + Define :class:`colour.utilities.common.CacheRegistry` class unit + tests methods. + """ + + @staticmethod + def _default_test_cache_registry(): + """Create a default test cache registry.""" + + cache_registry = CacheRegistry() + cache_a = cache_registry.register_cache("Cache A") + cache_a["Foo"] = "Bar" + cache_b = cache_registry.register_cache("Cache B") + cache_b["John"] = "Doe" + cache_b["Luke"] = "Skywalker" + + return cache_registry + + def test_required_attributes(self): + """Test the presence of required attributes.""" + + required_attributes = ("registry",) + + for attribute in required_attributes: + self.assertIn(attribute, dir(CacheRegistry)) + + def test_required_methods(self): + """Test the presence of required methods.""" + + required_methods = ( + "__init__", + "__str__", + "register_cache", + "unregister_cache", + "clear_cache", + "clear_all_caches", + ) + + for method in required_methods: + self.assertIn(method, dir(CacheRegistry)) + + def test__str__(self): + """Test :class:`colour.utilities.common.CacheRegistry.__str__` method.""" + + cache_registry = self._default_test_cache_registry() + self.assertEqual( + str(cache_registry), + "{'Cache A': '1 item(s)', 'Cache B': '2 item(s)'}", + ) + + def test_register_cache(self): + """ + Test :class:`colour.utilities.common.CacheRegistry.register_cache` + method. + """ + + cache_registry = CacheRegistry() + cache_a = cache_registry.register_cache("Cache A") + self.assertDictEqual(cache_registry.registry, {"Cache A": cache_a}) + cache_b = cache_registry.register_cache("Cache B") + self.assertDictEqual( + cache_registry.registry, {"Cache A": cache_a, "Cache B": cache_b} + ) + + def test_unregister_cache(self): + """ + Test :class:`colour.utilities.common.CacheRegistry.unregister_cache` + method. + """ + + cache_registry = self._default_test_cache_registry() + cache_registry.unregister_cache("Cache A") + self.assertNotIn("Cache A", cache_registry.registry) + self.assertIn("Cache B", cache_registry.registry) + + def test_clear_cache(self): + """ + Test :class:`colour.utilities.common.CacheRegistry.clear_cache` + method. + """ + + cache_registry = self._default_test_cache_registry() + cache_registry.clear_cache("Cache A") + self.assertDictEqual( + cache_registry.registry, + {"Cache A": {}, "Cache B": {"John": "Doe", "Luke": "Skywalker"}}, + ) + + def test_clear_all_caches(self): + """ + Test :class:`colour.utilities.common.CacheRegistry.clear_all_caches` + method. + """ + + cache_registry = self._default_test_cache_registry() + cache_registry.clear_all_caches() + self.assertDictEqual( + cache_registry.registry, {"Cache A": {}, "Cache B": {}} + ) + + +class TestAttest(unittest.TestCase): + """ + Define :func:`colour.utilities.common.attest` definition unit + tests methods. + """ + + def test_attest(self): + """Test :func:`colour.utilities.common.attest` definition.""" + + self.assertIsNone(attest(True, "")) + + self.assertRaises(AssertionError, attest, False) + + class TestBatch(unittest.TestCase): """ - Defines :func:`colour.utilities.common.batch` definition unit tests + Define :func:`colour.utilities.common.batch` definition unit tests methods. """ def test_batch(self): - """ - Tests :func:`colour.utilities.common.batch` definition. - """ + """Test :func:`colour.utilities.common.batch` definition.""" self.assertListEqual( - list(batch(tuple(range(10)))), - [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9,)]) # yapf: disable + list(batch(tuple(range(10)), 3)), + [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9,)], + ) self.assertListEqual( list(batch(tuple(range(10)), 5)), - [(0, 1, 2, 3, 4), (5, 6, 7, 8, 9)]) # yapf: disable + [(0, 1, 2, 3, 4), (5, 6, 7, 8, 9)], + ) self.assertListEqual( list(batch(tuple(range(10)), 1)), - [(0,), (1,), (2,), (3,), (4,), - (5,), (6,), (7,), (8,), (9,)]) # yapf: disable + [(0,), (1,), (2,), (3,), (4,), (5,), (6,), (7,), (8,), (9,)], + ) -def _add(a, b): +def _add(a: Number, b: Number): """ - Function to map with a multiprocessing pool. + Add two numbers. + + This definition is intended to be used with a multiprocessing pool for unit + testing. Parameters ---------- - a : numeric + a Variable :math:`a`. - b : numeric + b Variable :math:`b`. Returns @@ -86,33 +217,30 @@ def _add(a, b): class TestMultiprocessingPool(unittest.TestCase): """ - Defines :func:`colour.utilities.common.multiprocessing_pool` definition - units tests methods. + Define :func:`colour.utilities.common.multiprocessing_pool` definition + unit tests methods. """ def test_multiprocessing_pool(self): - """ - Tests :func:`colour.utilities.common.multiprocessing_pool` definition. - """ + """Test :func:`colour.utilities.common.multiprocessing_pool` definition.""" with multiprocessing_pool() as pool: self.assertListEqual( pool.map(partial(_add, b=2), range(10)), - [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) + [2, 3, 4, 5, 6, 7, 8, 9, 10, 11], + ) class TestIsIterable(unittest.TestCase): """ - Defines :func:`colour.utilities.common.is_iterable` definition unit tests + Define :func:`colour.utilities.common.is_iterable` definition unit tests methods. """ def test_is_iterable(self): - """ - Tests :func:`colour.utilities.common.is_iterable` definition. - """ + """Test :func:`colour.utilities.common.is_iterable` definition.""" - self.assertTrue(is_iterable('')) + self.assertTrue(is_iterable("")) self.assertTrue(is_iterable(())) @@ -135,20 +263,18 @@ def test_is_iterable(self): class TestIsString(unittest.TestCase): """ - Defines :func:`colour.utilities.common.is_string` definition unit tests + Define :func:`colour.utilities.common.is_string` definition unit tests methods. """ def test_is_string(self): - """ - Tests :func:`colour.utilities.common.is_string` definition. - """ + """Test :func:`colour.utilities.common.is_string` definition.""" - self.assertTrue(is_string(str('Hello World!'))) + self.assertTrue(is_string("Hello World!")) - self.assertTrue(is_string('Hello World!')) + self.assertTrue(is_string("Hello World!")) - self.assertTrue(is_string(r'Hello World!')) + self.assertTrue(is_string(r"Hello World!")) self.assertFalse(is_string(1)) @@ -159,38 +285,32 @@ def test_is_string(self): class TestIsNumeric(unittest.TestCase): """ - Defines :func:`colour.utilities.common.is_numeric` definition unit tests + Define :func:`colour.utilities.common.is_numeric` definition unit tests methods. """ def test_is_numeric(self): - """ - Tests :func:`colour.utilities.common.is_numeric` definition. - """ + """Test :func:`colour.utilities.common.is_numeric` definition.""" self.assertTrue(is_numeric(1)) self.assertTrue(is_numeric(1)) - self.assertTrue(is_numeric(complex(1))) - - self.assertFalse(is_numeric((1, ))) + self.assertFalse(is_numeric((1,))) self.assertFalse(is_numeric([1])) - self.assertFalse(is_numeric('1')) + self.assertFalse(is_numeric("1")) class TestIsInteger(unittest.TestCase): """ - Defines :func:`colour.utilities.common.is_integer` definition units + Define :func:`colour.utilities.common.is_integer` definition unit tests methods. """ def test_is_integer(self): - """ - Tests :func:`colour.utilities.common.is_integer` definition. - """ + """Test :func:`colour.utilities.common.is_integer` definition.""" self.assertTrue(is_integer(1)) @@ -201,70 +321,59 @@ def test_is_integer(self): class TestIsSibling(unittest.TestCase): """ - Defines :func:`colour.utilities.common.is_sibling` definition units tests + Define :func:`colour.utilities.common.is_sibling` definition unit tests methods. """ def test_is_sibling(self): - """ - Tests :func:`colour.utilities.common.is_sibling` definition. - """ + """Test :func:`colour.utilities.common.is_sibling` definition.""" - class Element(object): - """ - :func:`is_sibling` unit tests :class:`Element` class. - """ + class Element: + """:func:`is_sibling` unit tests :class:`Element` class.""" def __init__(self, name): self.name = name - class NotElement(object): - """ - :func:`is_sibling` unit tests :class:`NotElement` class. - """ + class NotElement: + """:func:`is_sibling` unit tests :class:`NotElement` class.""" def __init__(self, name): self.name = name mapping = { - 'Element A': Element('A'), - 'Element B': Element('B'), - 'Element C': Element('C'), + "Element A": Element("A"), + "Element B": Element("B"), + "Element C": Element("C"), } - self.assertTrue(is_sibling(Element('D'), mapping)) + self.assertTrue(is_sibling(Element("D"), mapping)) - self.assertFalse(is_sibling(NotElement('Not D'), mapping)) + self.assertFalse(is_sibling(NotElement("Not D"), mapping)) class TestFilterKwargs(unittest.TestCase): """ - Defines :func:`colour.utilities.common.filter_kwargs` definition units + Define :func:`colour.utilities.common.filter_kwargs` definition unit tests methods. """ def test_filter_kwargs(self): - """ - Tests :func:`colour.utilities.common.filter_kwargs` definition. - """ + """Test :func:`colour.utilities.common.filter_kwargs` definition.""" + + def fn_a(a: Any) -> Any: + """:func:`filter_kwargs` unit tests :func:`fn_a` definition.""" - def fn_a(a): - """ - :func:`filter_kwargs` unit tests :func:`fn_a` definition. - """ return a - def fn_b(a, b=0): - """ - :func:`filter_kwargs` unit tests :func:`fn_b` definition. - """ + def fn_b(a: Any, b: Floating = 0) -> Tuple[Any, Floating]: + """:func:`filter_kwargs` unit tests :func:`fn_b` definition.""" return a, b - def fn_c(a, b=0, c=0): - """ - :func:`filter_kwargs` unit tests :func:`fn_c` definition. - """ + def fn_c( + a: Any, b: Floating = 0, c: Floating = 0 + ) -> Tuple[Any, Floating, Floating]: + """:func:`filter_kwargs` unit tests :func:`fn_c` definition.""" return a, b, c @@ -272,485 +381,133 @@ def fn_c(a, b=0, c=0): self.assertTupleEqual((1, 2), fn_b(1, **filter_kwargs(fn_b, b=2, c=3))) - self.assertTupleEqual((1, 2, 3), - fn_c(1, **filter_kwargs(fn_c, b=2, c=3))) + self.assertTupleEqual( + (1, 2, 3), fn_c(1, **filter_kwargs(fn_c, b=2, c=3)) + ) - if six.PY2: # pragma: no cover - self.assertDictEqual(filter_kwargs(partial(fn_c, b=1), b=1), {}) - else: # pragma: no cover - self.assertDictEqual( - filter_kwargs(partial(fn_c, b=1), b=1), {'b': 1}) + self.assertDictEqual(filter_kwargs(partial(fn_c, b=1), b=1), {"b": 1}) class TestFilterMapping(unittest.TestCase): """ - Defines :func:`colour.utilities.common.filter_mapping` definition units + Define :func:`colour.utilities.common.filter_mapping` definition unit tests methods. """ def test_filter_mapping(self): - """ - Tests :func:`colour.utilities.common.filter_mapping` definition. - """ + """Test :func:`colour.utilities.common.filter_mapping` definition.""" - class Element(object): - """ - :func:`filter_mapping` unit tests :class:`Element` class. - """ + class Element: + """:func:`filter_mapping` unit tests :class:`Element` class.""" def __init__(self, name): self.name = name mapping = { - 'Element A': Element('A'), - 'Element B': Element('B'), - 'Element C': Element('C'), - 'Not Element C': Element('Not C'), + "Element A": Element("A"), + "Element B": Element("B"), + "Element C": Element("C"), + "Not Element C": Element("Not C"), } self.assertListEqual( - sorted(filter_mapping(mapping, '\\w+\\s+A')), ['Element A']) + sorted(filter_mapping(mapping, "\\w+\\s+A")), ["Element A"] + ) self.assertListEqual( - sorted(filter_mapping(mapping, 'Element.*')), [ - 'Element A', - 'Element B', - 'Element C', - ]) + sorted(filter_mapping(mapping, "Element.*")), + [ + "Element A", + "Element B", + "Element C", + ], + ) self.assertListEqual( - sorted(filter_mapping(mapping, '^Element.*')), [ - 'Element A', - 'Element B', - 'Element C', - ]) + sorted(filter_mapping(mapping, "^Element.*")), + [ + "Element A", + "Element B", + "Element C", + ], + ) self.assertListEqual( - sorted(filter_mapping(mapping, '^Element.*', False)), [ - 'Element A', - 'Element B', - 'Element C', - ]) + sorted(filter_mapping(mapping, "^Element.*", False)), + [ + "Element A", + "Element B", + "Element C", + ], + ) self.assertListEqual( - sorted(filter_mapping(mapping, ['.*A', '.*B'])), [ - 'Element A', - 'Element B', - ]) - - self.assertIsInstance( - filter_mapping(mapping, '^Element.*', False), type(mapping)) + sorted(filter_mapping(mapping, [".*A", ".*B"])), + [ + "Element A", + "Element B", + ], + ) self.assertIsInstance( - filter_mapping(OrderedDict(mapping), '^Element.*', False), - OrderedDict) + filter_mapping(mapping, "^Element.*", False), type(mapping) + ) class TestFirstItem(unittest.TestCase): """ - Defines :func:`colour.utilities.common.first_item` definition units + Define :func:`colour.utilities.common.first_item` definition unit tests methods. """ def test_first_item(self): - """ - Tests :func:`colour.utilities.common.first_item` definition. - """ + """Test :func:`colour.utilities.common.first_item` definition.""" self.assertEqual(first_item(range(10)), 0) - dictionary = OrderedDict([(0, 'a'), (1, 'b'), (2, 'c')]) - self.assertEqual(first_item(dictionary.items()), (0, 'a')) - - self.assertEqual(first_item(dictionary.values()), 'a') - - -class TestGetDomainRangeScale(unittest.TestCase): - """ - Defines :func:`colour.utilities.common.get_domain_range_scale` definition - units tests methods. - """ - - def test_get_domain_range_scale(self): - """ - Tests :func:`colour.utilities.common.get_domain_range_scale` - definition. - """ - - with domain_range_scale('Reference'): - self.assertEqual(get_domain_range_scale(), 'reference') + dictionary = {0: "a", 1: "b", 2: "c"} + self.assertEqual(first_item(dictionary.items()), (0, "a")) - with domain_range_scale('1'): - self.assertEqual(get_domain_range_scale(), '1') + self.assertEqual(first_item(dictionary.values()), "a") - with domain_range_scale('100'): - self.assertEqual(get_domain_range_scale(), '100') - -class TestSetDomainRangeScale(unittest.TestCase): - """ - Defines :func:`colour.utilities.common.set_domain_range_scale` definition - units tests methods. +class TestValidateMethod(unittest.TestCase): """ - - def test_set_domain_range_scale(self): - """ - Tests :func:`colour.utilities.common.set_domain_range_scale` - definition. - """ - - with domain_range_scale('Reference'): - set_domain_range_scale('1') - self.assertEqual(get_domain_range_scale(), '1') - - with domain_range_scale('Reference'): - set_domain_range_scale('100') - self.assertEqual(get_domain_range_scale(), '100') - - with domain_range_scale('1'): - set_domain_range_scale('Reference') - self.assertEqual(get_domain_range_scale(), 'reference') - - self.assertRaises(AssertionError, - lambda: set_domain_range_scale('Invalid')) - - -class TestDomainRangeScale(unittest.TestCase): - """ - Defines :func:`colour.utilities.common.domain_range_scale` definition - units tests methods. - """ - - def test_domain_range_scale(self): - """ - Tests :func:`colour.utilities.common.domain_range_scale` - definition. - """ - - self.assertEqual(get_domain_range_scale(), 'reference') - - with domain_range_scale('Reference'): - self.assertEqual(get_domain_range_scale(), 'reference') - - self.assertEqual(get_domain_range_scale(), 'reference') - - with domain_range_scale('1'): - self.assertEqual(get_domain_range_scale(), '1') - - self.assertEqual(get_domain_range_scale(), 'reference') - - with domain_range_scale('100'): - self.assertEqual(get_domain_range_scale(), '100') - - self.assertEqual(get_domain_range_scale(), 'reference') - - def fn_a(a): - """ - Helper definition performing domain-range scale. - """ - - b = to_domain_10(a) - - b *= 2 - - return from_range_100(b) - - with domain_range_scale('Reference'): - with domain_range_scale('1'): - with domain_range_scale('100'): - with domain_range_scale('Ignore'): - self.assertEqual(get_domain_range_scale(), 'ignore') - self.assertEqual(fn_a(4), 8) - - self.assertEqual(get_domain_range_scale(), '100') - self.assertEqual(fn_a(40), 8) - - self.assertEqual(get_domain_range_scale(), '1') - self.assertEqual(fn_a(0.4), 0.08) - - self.assertEqual(get_domain_range_scale(), 'reference') - self.assertEqual(fn_a(4), 8) - - self.assertEqual(get_domain_range_scale(), 'reference') - - @domain_range_scale(1) - def fn_b(a): - """ - Helper definition performing domain-range scale. - """ - - b = to_domain_10(a) - - b *= 2 - - return from_range_100(b) - - self.assertEqual(fn_b(10), 2.0) - - -class TestToDomain1(unittest.TestCase): - """ - Defines :func:`colour.utilities.common.to_domain_1` definition units - tests methods. - """ - - def test_to_domain_1(self): - """ - Tests :func:`colour.utilities.common.to_domain_1` definition. - """ - - with domain_range_scale('Reference'): - self.assertEqual(to_domain_1(1), 1) - - with domain_range_scale('1'): - self.assertEqual(to_domain_1(1), 1) - - with domain_range_scale('100'): - self.assertEqual(to_domain_1(1), 0.01) - - with domain_range_scale('100'): - self.assertEqual(to_domain_1(1, np.pi), 1 / np.pi) - - with domain_range_scale('100'): - self.assertEqual( - to_domain_1(1, dtype=np.float16).dtype, np.float16) - - -class TestToDomain10(unittest.TestCase): - """ - Defines :func:`colour.utilities.common.to_domain_10` definition units - tests methods. - """ - - def test_to_domain_10(self): - """ - Tests :func:`colour.utilities.common.to_domain_10` definition. - """ - - with domain_range_scale('Reference'): - self.assertEqual(to_domain_10(1), 1) - - with domain_range_scale('1'): - self.assertEqual(to_domain_10(1), 10) - - with domain_range_scale('100'): - self.assertEqual(to_domain_10(1), 0.1) - - with domain_range_scale('100'): - self.assertEqual(to_domain_10(1, np.pi), 1 / np.pi) - - with domain_range_scale('100'): - self.assertEqual( - to_domain_10(1, dtype=np.float16).dtype, np.float16) - - -class TestToDomain100(unittest.TestCase): - """ - Defines :func:`colour.utilities.common.to_domain_100` definition units - tests methods. - """ - - def test_to_domain_100(self): - """ - Tests :func:`colour.utilities.common.to_domain_100` definition. - """ - - with domain_range_scale('Reference'): - self.assertEqual(to_domain_100(1), 1) - - with domain_range_scale('1'): - self.assertEqual(to_domain_100(1), 100) - - with domain_range_scale('100'): - self.assertEqual(to_domain_100(1), 1) - - with domain_range_scale('1'): - self.assertEqual(to_domain_100(1, np.pi), np.pi) - - with domain_range_scale('100'): - self.assertEqual( - to_domain_100(1, dtype=np.float16).dtype, np.float16) - - -class TestToDomainDegrees(unittest.TestCase): - """ - Defines :func:`colour.utilities.common.to_domain_degrees` definition units - tests methods. - """ - - def test_to_domain_degrees(self): - """ - Tests :func:`colour.utilities.common.to_domain_degrees` definition. - """ - - with domain_range_scale('Reference'): - self.assertEqual(to_domain_degrees(1), 1) - - with domain_range_scale('1'): - self.assertEqual(to_domain_degrees(1), 360) - - with domain_range_scale('100'): - self.assertEqual(to_domain_degrees(1), 3.6) - - with domain_range_scale('100'): - self.assertEqual(to_domain_degrees(1, np.pi), np.pi / 100) - - with domain_range_scale('100'): - self.assertEqual( - to_domain_degrees(1, dtype=np.float16).dtype, np.float16) - - -class TestToDomainInt(unittest.TestCase): - """ - Defines :func:`colour.utilities.common.to_domain_int` definition units + Define :func:`colour.utilities.common.validate_method` definition unit tests methods. """ - def test_to_domain_int(self): - """ - Tests :func:`colour.utilities.common.to_domain_int` definition. - """ - - with domain_range_scale('Reference'): - self.assertEqual(to_domain_int(1), 1) - - with domain_range_scale('1'): - self.assertEqual(to_domain_int(1), 255) - - with domain_range_scale('100'): - self.assertEqual(to_domain_int(1), 2.55) - - with domain_range_scale('100'): - self.assertEqual(to_domain_int(1, 10), 10.23) - - with domain_range_scale('100'): - self.assertEqual( - to_domain_int(1, dtype=np.float16).dtype, np.float16) - - -class TestFromRange1(unittest.TestCase): - """ - Defines :func:`colour.utilities.common.from_range_1` definition units - tests methods. - """ - - def test_from_range_1(self): - """ - Tests :func:`colour.utilities.common.from_range_1` definition. - """ - - with domain_range_scale('Reference'): - self.assertEqual(from_range_1(1), 1) - - with domain_range_scale('1'): - self.assertEqual(from_range_1(1), 1) - - with domain_range_scale('100'): - self.assertEqual(from_range_1(1), 100) + def test_validate_method(self): + """Test :func:`colour.utilities.common.validate_method` definition.""" - with domain_range_scale('100'): - self.assertEqual(from_range_1(1, np.pi), 1 * np.pi) - - -class TestFromRange10(unittest.TestCase): - """ - Defines :func:`colour.utilities.common.from_range_10` definition units - tests methods. - """ + self.assertEqual( + validate_method("Valid", ["Valid", "Yes", "Ok"]), "valid" + ) - def test_from_range_10(self): + def test_raise_exception_validate_method(self): """ - Tests :func:`colour.utilities.common.from_range_10` definition. + Test :func:`colour.utilities.common.validate_method` definition raised + exception. """ - with domain_range_scale('Reference'): - self.assertEqual(from_range_10(1), 1) - - with domain_range_scale('1'): - self.assertEqual(from_range_10(1), 0.1) - - with domain_range_scale('100'): - self.assertEqual(from_range_10(1), 10) + self.assertRaises( + ValueError, validate_method, "Invalid", ["Valid", "Yes", "Ok"] + ) - with domain_range_scale('100'): - self.assertEqual(from_range_10(1, np.pi), 1 * np.pi) - -class TestFromRange100(unittest.TestCase): +class TestOptional(unittest.TestCase): """ - Defines :func:`colour.utilities.common.from_range_100` definition units + Define :func:`colour.utilities.common.optional` definition unit tests methods. """ - def test_from_range_100(self): - """ - Tests :func:`colour.utilities.common.from_range_100` definition. - """ - - with domain_range_scale('Reference'): - self.assertEqual(from_range_100(1), 1) - - with domain_range_scale('1'): - self.assertEqual(from_range_100(1), 0.01) - - with domain_range_scale('100'): - self.assertEqual(from_range_100(1), 1) - - with domain_range_scale('1'): - self.assertEqual(from_range_100(1, np.pi), 1 / np.pi) - - -class TestFromRangeDegrees(unittest.TestCase): - """ - Defines :func:`colour.utilities.common.from_range_degrees` definition units - tests methods. - """ - - def test_from_range_degrees(self): - """ - Tests :func:`colour.utilities.common.from_range_degrees` definition. - """ - - with domain_range_scale('Reference'): - self.assertEqual(from_range_degrees(1), 1) - - with domain_range_scale('1'): - self.assertEqual(from_range_degrees(1), 1 / 360) - - with domain_range_scale('100'): - self.assertEqual(from_range_degrees(1), 1 / 3.6) - - with domain_range_scale('100'): - self.assertEqual(from_range_degrees(1, np.pi), 1 / (np.pi / 100)) - - -class TestFromRangeInt(unittest.TestCase): - """ - Defines :func:`colour.utilities.common.from_range_int` definition units - tests methods. - """ - - def test_from_range_int(self): - """ - Tests :func:`colour.utilities.common.from_range_int` definition. - """ - - with domain_range_scale('Reference'): - self.assertEqual(from_range_int(1), 1) - - with domain_range_scale('1'): - self.assertEqual(from_range_int(1), 1 / 255) - - with domain_range_scale('100'): - self.assertEqual(from_range_int(1), 1 / 2.55) + def test_optional(self): + """Test :func:`colour.utilities.common.optional` definition.""" - with domain_range_scale('100'): - self.assertEqual(from_range_int(1, 10), 1 / (1023 / 100)) + self.assertEqual(optional("Foo", "Bar"), "Foo") - with domain_range_scale('100'): - self.assertEqual( - from_range_int(1, dtype=np.float16).dtype, np.float16) + self.assertEqual(optional(None, "Bar"), "Bar") -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/utilities/tests/test_data_structures.py b/colour/utilities/tests/test_data_structures.py index 971d7eb9d9..5f30f81746 100644 --- a/colour/utilities/tests/test_data_structures.py +++ b/colour/utilities/tests/test_data_structures.py @@ -1,79 +1,80 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.utilities.data_structures` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.utilities.data_structures` module.""" import numpy as np import operator import pickle import unittest -from colour.utilities import (Structure, Lookup, CaseInsensitiveMapping, - LazyCaseInsensitiveMapping) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.utilities import ( + Structure, + Lookup, + CaseInsensitiveMapping, + LazyCaseInsensitiveMapping, + Node, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'TestStructure', 'TestLookup', 'TestCaseInsensitiveMapping', - 'TestLazyCaseInsensitiveMapping' + "TestStructure", + "TestLookup", + "TestCaseInsensitiveMapping", + "TestLazyCaseInsensitiveMapping", + "TestNode", ] class TestStructure(unittest.TestCase): """ - Defines :class:`colour.utilities.data_structures.Structure` class units + Define :class:`colour.utilities.data_structures.Structure` class unit tests methods. """ def test_Structure(self): - """ - Tests :class:`colour.utilities.data_structures.Structure` class. - """ + """Test :class:`colour.utilities.data_structures.Structure` class.""" - structure = Structure(John='Doe', Jane='Doe') - self.assertIn('John', structure) - self.assertTrue(hasattr(structure, 'John')) + structure = Structure(John="Doe", Jane="Doe") + self.assertIn("John", structure) + self.assertTrue(hasattr(structure, "John")) - setattr(structure, 'John', 'Nemo') - self.assertEqual(structure['John'], 'Nemo') + setattr(structure, "John", "Nemo") + self.assertEqual(structure["John"], "Nemo") - structure['John'] = 'Vador' - self.assertEqual(structure['John'], 'Vador') + structure["John"] = "Vador" + self.assertEqual(structure["John"], "Vador") - del structure['John'] - self.assertNotIn('John', structure) - self.assertFalse(hasattr(structure, 'John')) + del structure["John"] + self.assertNotIn("John", structure) + self.assertFalse(hasattr(structure, "John")) - structure.John = 'Doe' - self.assertIn('John', structure) - self.assertTrue(hasattr(structure, 'John')) + structure.John = "Doe" + self.assertIn("John", structure) + self.assertTrue(hasattr(structure, "John")) del structure.John - self.assertNotIn('John', structure) - self.assertFalse(hasattr(structure, 'John')) + self.assertNotIn("John", structure) + self.assertFalse(hasattr(structure, "John")) structure = Structure(John=None, Jane=None) self.assertIsNone(structure.John) - self.assertIsNone(structure['John']) + self.assertIsNone(structure["John"]) - structure.update(**{'John': 'Doe', 'Jane': 'Doe'}) - self.assertEqual(structure.John, 'Doe') - self.assertEqual(structure['John'], 'Doe') + structure.update(**{"John": "Doe", "Jane": "Doe"}) + self.assertEqual(structure.John, "Doe") + self.assertEqual(structure["John"], "Doe") def test_Structure_pickle(self): """ - Tests :class:`colour.utilities.data_structures.Structure` class + Test :class:`colour.utilities.data_structures.Structure` class pickling. """ - structure = Structure(John='Doe', Jane='Doe') + structure = Structure(John="Doe", Jane="Doe") data = pickle.dumps(structure) data = pickle.loads(data) @@ -83,198 +84,218 @@ def test_Structure_pickle(self): data = pickle.loads(data) self.assertEqual(structure, data) + self.assertEqual(sorted(dir(data)), ["Jane", "John"]) + class TestLookup(unittest.TestCase): """ - Defines :class:`colour.utilities.data_structures.Lookup` class unit tests + Define :class:`colour.utilities.data_structures.Lookup` class unit tests methods. """ def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('keys_from_value', 'first_key_from_value') + required_methods = ("keys_from_value", "first_key_from_value") for method in required_methods: self.assertIn(method, dir(Lookup)) def test_keys_from_value(self): """ - Tests :meth:`colour.utilities.data_structures.Lookup.keys_from_value` + Test :meth:`colour.utilities.data_structures.Lookup.keys_from_value` method. """ - lookup = Lookup(John='Doe', Jane='Doe', Luke='Skywalker') - self.assertListEqual(['Jane', 'John'], - sorted(lookup.keys_from_value('Doe'))) + lookup = Lookup(John="Doe", Jane="Doe", Luke="Skywalker") + self.assertListEqual( + ["Jane", "John"], sorted(lookup.keys_from_value("Doe")) + ) lookup = Lookup( - A=np.array([0, 1, 2]), - B=np.array([0, 1, 2]), - C=np.array([1, 2, 3])) - self.assertListEqual(['A', 'B'], - sorted( - lookup.keys_from_value(np.array([0, 1, 2])))) + A=np.array([0, 1, 2]), B=np.array([0, 1, 2]), C=np.array([1, 2, 3]) + ) + self.assertListEqual( + ["A", "B"], sorted(lookup.keys_from_value(np.array([0, 1, 2]))) + ) def test_first_key_from_value(self): """ - Tests :meth:`colour.utilities.data_structures.\ + Test :meth:`colour.utilities.data_structures.\ Lookup.first_key_from_value` method. """ - lookup = Lookup(first_name='John', last_name='Doe', gender='male') - self.assertEqual('first_name', lookup.first_key_from_value('John')) + lookup = Lookup(first_name="John", last_name="Doe", gender="male") + self.assertEqual("first_name", lookup.first_key_from_value("John")) lookup = Lookup( - A=np.array([0, 1, 2]), - B=np.array([1, 2, 3]), - C=np.array([2, 3, 4])) - self.assertEqual('A', lookup.first_key_from_value(np.array([0, 1, 2]))) + A=np.array([0, 1, 2]), B=np.array([1, 2, 3]), C=np.array([2, 3, 4]) + ) + self.assertEqual("A", lookup.first_key_from_value(np.array([0, 1, 2]))) def test_raise_exception_first_key_from_value(self): """ - Tests :meth:`colour.utilities.data_structures.\ + Test :meth:`colour.utilities.data_structures.\ Lookup.first_key_from_value` method raised exception. """ - self.assertRaises(IndexError, Lookup().first_key_from_value, 'John') + self.assertRaises(IndexError, Lookup().first_key_from_value, "John") class TestCaseInsensitiveMapping(unittest.TestCase): """ - Defines :class:`colour.utilities.data_structures.CaseInsensitiveMapping` + Define :class:`colour.utilities.data_structures.CaseInsensitiveMapping` class unit tests methods. """ def test_required_attributes(self): - """ - Tests presence of required attributes. - """ + """Test the presence of required attributes.""" - required_attributes = ('data', ) + required_attributes = ("data",) for attribute in required_attributes: self.assertIn(attribute, dir(CaseInsensitiveMapping)) def test_required_methods(self): - """ - Tests presence of required methods. - """ - - required_methods = ('__init__', '__setitem__', '__getitem__', - '__delitem__', '__contains__', '__iter__', - '__len__', '__eq__', '__ne__', '__repr__', 'copy', - 'lower_items') + """Test the presence of required methods.""" + + required_methods = ( + "__init__", + "__repr__", + "__setitem__", + "__getitem__", + "__delitem__", + "__contains__", + "__iter__", + "__len__", + "__eq__", + "__ne__", + "copy", + "lower_items", + ) for method in required_methods: self.assertIn(method, dir(CaseInsensitiveMapping)) def test_data(self): """ - Tests :meth:`colour.utilities.data_structures.\ + Test :meth:`colour.utilities.data_structures.\ CaseInsensitiveMapping.data` property. """ self.assertDictEqual( - CaseInsensitiveMapping({ - 'John': 'Doe', - 'Jane': 'Doe' - }).data, { - 'jane': ('Jane', 'Doe'), - 'john': ('John', 'Doe') - }) + CaseInsensitiveMapping({"John": "Doe", "Jane": "Doe"}).data, + {"jane": ("Jane", "Doe"), "john": ("John", "Doe")}, + ) + + def test__repr__(self): + """ + Test :meth:`colour.utilities.data_structures.\ +CaseInsensitiveMapping.__repr__` method. + """ + + mapping = CaseInsensitiveMapping() + + mapping["John"] = "Doe" + self.assertEqual( + repr(mapping), "CaseInsensitiveMapping({'John': 'Doe'})" + ) def test__setitem__(self): """ - Tests :meth:`colour.utilities.data_structures.\ + Test :meth:`colour.utilities.data_structures.\ CaseInsensitiveMapping.__setitem__` method. """ mapping = CaseInsensitiveMapping() - mapping['John'] = 'Doe' - self.assertEqual(mapping['John'], 'Doe') - self.assertEqual(mapping['john'], 'Doe') + mapping["John"] = "Doe" + self.assertEqual(mapping["John"], "Doe") + self.assertEqual(mapping["john"], "Doe") def test__getitem__(self): """ - Tests :meth:`colour.utilities.data_structures.\ + Test :meth:`colour.utilities.data_structures.\ CaseInsensitiveMapping.__getitem__` method. """ - mapping = CaseInsensitiveMapping(John='Doe', Jane='Doe') + mapping = CaseInsensitiveMapping(John="Doe", Jane="Doe") - self.assertEqual(mapping['John'], 'Doe') + self.assertEqual(mapping["John"], "Doe") - self.assertEqual(mapping['john'], 'Doe') + self.assertEqual(mapping["john"], "Doe") - self.assertEqual(mapping['Jane'], 'Doe') + self.assertEqual(mapping["Jane"], "Doe") - self.assertEqual(mapping['jane'], 'Doe') + self.assertEqual(mapping["jane"], "Doe") + + mapping = CaseInsensitiveMapping({1: "Foo", 2: "Bar"}) + + self.assertEqual(mapping[1], "Foo") def test__delitem__(self): """ - Tests :meth:`colour.utilities.data_structures.\ + Test :meth:`colour.utilities.data_structures.\ CaseInsensitiveMapping.__delitem__` method. """ - mapping = CaseInsensitiveMapping(John='Doe', Jane='Doe') + mapping = CaseInsensitiveMapping(John="Doe", Jane="Doe") - del mapping['john'] - self.assertNotIn('John', mapping) + del mapping["john"] + self.assertNotIn("John", mapping) - del mapping['Jane'] - self.assertNotIn('jane', mapping) + del mapping["Jane"] + self.assertNotIn("jane", mapping) self.assertEqual(len(mapping), 0) def test__contains__(self): """ - Tests :meth:`colour.utilities.data_structures.\ + Test :meth:`colour.utilities.data_structures.\ CaseInsensitiveMapping.__contains__` method. """ - mapping = CaseInsensitiveMapping(John='Doe', Jane='Doe') + mapping = CaseInsensitiveMapping(John="Doe", Jane="Doe") - self.assertIn('John', mapping) + self.assertIn("John", mapping) - self.assertIn('john', mapping) + self.assertIn("john", mapping) - self.assertIn('Jane', mapping) + self.assertIn("Jane", mapping) - self.assertIn('jane', mapping) + self.assertIn("jane", mapping) def test__iter__(self): """ - Tests :meth:`colour.utilities.data_structures.\ + Test :meth:`colour.utilities.data_structures.\ CaseInsensitiveMapping.__iter__` method. """ - mapping = CaseInsensitiveMapping(John='Doe', Jane='Doe') + mapping = CaseInsensitiveMapping(John="Doe", Jane="Doe") self.assertListEqual( - sorted([item for item in mapping]), ['Jane', 'John']) + sorted(item for item in mapping), ["Jane", "John"] + ) def test__len__(self): """ - Tests :meth:`colour.utilities.data_structures.\ + Test :meth:`colour.utilities.data_structures.\ CaseInsensitiveMapping.__len__` method. """ self.assertEqual(len(CaseInsensitiveMapping()), 0) self.assertEqual( - len(CaseInsensitiveMapping(John='Doe', Jane='Doe')), 2) + len(CaseInsensitiveMapping(John="Doe", Jane="Doe")), 2 + ) def test__eq__(self): """ - Tests :meth:`colour.utilities.data_structures.\ + Test :meth:`colour.utilities.data_structures.\ CaseInsensitiveMapping.__eq__` method. """ - mapping1 = CaseInsensitiveMapping(John='Doe', Jane='Doe') - mapping2 = CaseInsensitiveMapping(John='Doe', Jane='Doe') - mapping3 = CaseInsensitiveMapping(john='Doe', jane='Doe') + mapping1 = CaseInsensitiveMapping(John="Doe", Jane="Doe") + mapping2 = CaseInsensitiveMapping(John="Doe", Jane="Doe") + mapping3 = CaseInsensitiveMapping(john="Doe", jane="Doe") self.assertEqual(mapping1, mapping2) @@ -282,42 +303,48 @@ def test__eq__(self): def test_raise_exception__eq__(self): """ - Tests :meth:`colour.utilities.data_structures.\ + Test :meth:`colour.utilities.data_structures.\ CaseInsensitiveMapping.__eq__` method raised exception. """ - self.assertRaises(ValueError, operator.eq, - CaseInsensitiveMapping(John='Doe', Jane='Doe'), - ['John', 'Doe', 'Jane', 'Doe']) + self.assertRaises( + ValueError, + operator.eq, + CaseInsensitiveMapping(John="Doe", Jane="Doe"), + ["John", "Doe", "Jane", "Doe"], + ) def test__ne__(self): """ - Tests :meth:`colour.utilities.data_structures.\ + Test :meth:`colour.utilities.data_structures.\ CaseInsensitiveMapping.__ne__` method. """ - mapping1 = CaseInsensitiveMapping(John='Doe', Jane='Doe') - mapping2 = CaseInsensitiveMapping(Gi='Doe', Jane='Doe') + mapping1 = CaseInsensitiveMapping(John="Doe", Jane="Doe") + mapping2 = CaseInsensitiveMapping(Gi="Doe", Jane="Doe") self.assertNotEqual(mapping1, mapping2) def test_raise_exception__ne__(self): """ - Tests :meth:`colour.utilities.data_structures.\ + Test :meth:`colour.utilities.data_structures.\ CaseInsensitiveMapping.__ne__` method raised exception. """ - self.assertRaises(ValueError, operator.ne, - CaseInsensitiveMapping(John='Doe', Jane='Doe'), - ['John', 'Doe', 'Jane', 'Doe']) + self.assertRaises( + ValueError, + operator.ne, + CaseInsensitiveMapping(John="Doe", Jane="Doe"), + ["John", "Doe", "Jane", "Doe"], + ) def test_copy(self): """ - Tests :meth:`colour.utilities.data_structures.\ + Test :meth:`colour.utilities.data_structures.\ CaseInsensitiveMapping.copy` method. """ - mapping1 = CaseInsensitiveMapping(John='Doe', Jane='Doe') + mapping1 = CaseInsensitiveMapping(John="Doe", Jane="Doe") mapping2 = mapping1.copy() self.assertEqual(mapping1, mapping2) @@ -326,59 +353,235 @@ def test_copy(self): def test_lower_items(self): """ - Tests :meth:`colour.utilities.data_structures.\ + Test :meth:`colour.utilities.data_structures.\ CaseInsensitiveMapping.lower_items` method. """ - mapping = CaseInsensitiveMapping(John='Doe', Jane='Doe') + mapping = CaseInsensitiveMapping(John="Doe", Jane="Doe") self.assertListEqual( - sorted([item for item in mapping.lower_items()]), - [('jane', 'Doe'), ('john', 'Doe')]) + sorted(item for item in mapping.lower_items()), + [("jane", "Doe"), ("john", "Doe")], + ) class TestLazyCaseInsensitiveMapping(unittest.TestCase): """ - Defines :class:`colour.utilities.data_structures.\ + Define :class:`colour.utilities.data_structures.\ LazyCaseInsensitiveMapping` class unit tests methods. """ def test_required_attributes(self): - """ - Tests presence of required attributes. - """ + """Test the presence of required attributes.""" required_attributes = () - for attribute in required_attributes: + for attribute in required_attributes: # pragma: no cover self.assertIn(attribute, dir(LazyCaseInsensitiveMapping)) def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__getitem__', ) + required_methods = ("__getitem__",) for method in required_methods: self.assertIn(method, dir(LazyCaseInsensitiveMapping)) def test__getitem__(self): """ - Tests :meth:`colour.utilities.data_structures.\ + Test :meth:`colour.utilities.data_structures.\ LazyCaseInsensitiveMapping.__getitem__` method. """ - mapping = LazyCaseInsensitiveMapping(John='Doe', Jane=lambda: 'Doe') + mapping = LazyCaseInsensitiveMapping(John="Doe", Jane=lambda: "Doe") + + self.assertEqual(mapping["John"], "Doe") + + self.assertEqual(mapping["john"], "Doe") + + self.assertEqual(mapping["Jane"], "Doe") + + self.assertEqual(mapping["jane"], "Doe") + + +class TestNode(unittest.TestCase): + """ + Define :class:`colour.utilities.data_structures.Node` class unit tests + methods. + """ + + def setUp(self): + """Initialise the common tests attributes.""" + + self._data = {"John": "Doe"} + + self._node_a = Node("Node A", data=self._data) + self._node_b = Node("Node B", self._node_a) + self._node_c = Node("Node C", self._node_a) + self._node_d = Node("Node D", self._node_b) + self._node_e = Node("Node E", self._node_b) + self._node_f = Node("Node F", self._node_d) + self._node_g = Node("Node G", self._node_f) + self._node_h = Node("Node H", self._node_g) + + self._tree = self._node_a + + def test_required_attributes(self): + """Test the presence of required attributes.""" + + required_attributes = ( + "name", + "parent", + "children", + "id", + "root", + "leaves", + "siblings", + "data", + ) + + for attribute in required_attributes: + self.assertIn(attribute, dir(Node)) + + def test_required_methods(self): + """Test the presence of required methods.""" + + required_methods = ( + "__new__", + "__init__", + "__str__", + "__len__", + "is_root", + "is_inner", + "is_leaf", + "walk", + "render", + ) + + for method in required_methods: + self.assertIn(method, dir(Node)) + + def test_name(self): + """Test :attr:`colour.utilities.data_structures.Node.name` property.""" + + self.assertEqual(self._tree.name, "Node A") + self.assertIn("Node#", Node().name) + + def test_parent(self): + """Test :attr:`colour.utilities.data_structures.Node.parent` property.""" + + self.assertIs(self._node_b.parent, self._node_a) + self.assertIs(self._node_h.parent, self._node_g) + + def test_children(self): + """Test :attr:`colour.utilities.data_structures.Node.children` property.""" + + self.assertListEqual( + self._node_a.children, [self._node_b, self._node_c] + ) + + def test_id(self): + """Test :attr:`colour.utilities.data_structures.Node.id` property.""" + + self.assertIsInstance(self._node_a.id, int) + + def test_root(self): + """Test :attr:`colour.utilities.data_structures.Node.root` property.""" + + self.assertIs(self._node_a.root, self._node_a) + self.assertIs(self._node_f.root, self._node_a) + self.assertIs(self._node_g.root, self._node_a) + self.assertIs(self._node_h.root, self._node_a) + + def test_leaves(self): + """Test :attr:`colour.utilities.data_structures.Node.leaves` property.""" + + self.assertListEqual(list(self._node_h.leaves), [self._node_h]) + + self.assertListEqual( + list(self._node_a.leaves), + [self._node_h, self._node_e, self._node_c], + ) + + def test_siblings(self): + """Test :attr:`colour.utilities.data_structures.Node.siblings` property.""" + + self.assertListEqual(list(self._node_a.siblings), []) + + self.assertListEqual(list(self._node_b.siblings), [self._node_c]) + + def test_data(self): + """Test :attr:`colour.utilities.data_structures.Node.data` property.""" + + self.assertIs(self._node_a.data, self._data) + + def test__str__(self): + """Test :attr:`colour.utilities.data_structures.Node.__str__` method.""" + + self.assertIn("Node#", str(self._node_a)) + self.assertIn("{'John': 'Doe'})", str(self._node_a)) + + def test__len__(self): + """Test :attr:`colour.utilities.data_structures.Node.__len__` method.""" - self.assertEqual(mapping['John'], 'Doe') + self.assertEqual(len(self._node_a), 7) - self.assertEqual(mapping['john'], 'Doe') + def test_is_root(self): + """Test :attr:`colour.utilities.data_structures.Node.is_root` method.""" + + self.assertTrue(self._node_a.is_root()) + self.assertFalse(self._node_b.is_root()) + self.assertFalse(self._node_c.is_root()) + self.assertFalse(self._node_h.is_root()) + + def test_is_inner(self): + """Test :attr:`colour.utilities.data_structures.Node.is_inner` method.""" + + self.assertFalse(self._node_a.is_inner()) + self.assertTrue(self._node_b.is_inner()) + self.assertFalse(self._node_c.is_inner()) + self.assertFalse(self._node_h.is_inner()) + + def test_is_leaf(self): + """Test :attr:`colour.utilities.data_structures.Node.is_leaf` method.""" + + self.assertFalse(self._node_a.is_leaf()) + self.assertFalse(self._node_b.is_leaf()) + self.assertTrue(self._node_c.is_leaf()) + self.assertTrue(self._node_h.is_leaf()) + + def test_walk(self): + """Test :attr:`colour.utilities.data_structures.Node.walk` method.""" + + self.assertListEqual( + list(self._node_a.walk()), + [ + self._node_b, + self._node_d, + self._node_f, + self._node_g, + self._node_h, + self._node_e, + self._node_c, + ], + ) + + self.assertListEqual( + list(self._node_h.walk(ascendants=True)), + [ + self._node_g, + self._node_f, + self._node_d, + self._node_b, + self._node_a, + ], + ) - self.assertEqual(mapping['Jane'], 'Doe') + def test_render(self): + """Test :attr:`colour.utilities.data_structures.Node.render` method.""" - self.assertEqual(mapping['jane'], 'Doe') + self.assertIsInstance(self._node_a.render(), str) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/utilities/tests/test_deprecated.py b/colour/utilities/tests/test_deprecated.py index de9c1e9e28..dba77cf921 100644 --- a/colour/utilities/tests/test_deprecated.py +++ b/colour/utilities/tests/test_deprecated.py @@ -1,41 +1,46 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - +# noqa: D100 import sys -from colour.utilities.deprecation import (ModuleAPI, ObjectRenamed, - ObjectRemoved) + +from colour.hints import Any +from colour.utilities.deprecation import ( + ModuleAPI, + ObjectRenamed, + ObjectRemoved, +) class deprecated(ModuleAPI): - def __getattr__(self, attribute): - return super(deprecated, self).__getattr__(attribute) - - -NAME = None -""" -An non-deprecated module attribute. - -NAME : object -""" - -NEW_NAME = None -""" -A module attribute with a new name. - -NAME : object -""" - -sys.modules['colour.utilities.tests.test_deprecated'] = (deprecated( - sys.modules['colour.utilities.tests.test_deprecated'], { - 'OLD_NAME': - ObjectRenamed( - name='colour.utilities.tests.test_deprecated.OLD_NAME', - new_name='colour.utilities.tests.test_deprecated.NEW_NAME'), - 'REMOVED': - ObjectRemoved(name='colour.utilities.tests.test_deprecated.REMOVED' - ) - })) + """Define a class acting like the *deprecated* module.""" + + def __getattr__(self, attribute) -> Any: + """Return the value from the attribute with given name.""" + + return super().__getattr__(attribute) + + +NAME: Any = None +"""An non-deprecated module attribute.""" + +NEW_NAME: Any = None +"""A module attribute with a new name.""" + +try: + sys.modules[ + "colour.utilities.tests.test_deprecated" + ] = deprecated( # type: ignore[assignment] + sys.modules["colour.utilities.tests.test_deprecated"], + { + "OLD_NAME": ObjectRenamed( + name=("colour.utilities.tests.test_deprecated.OLD_NAME"), + new_name=("colour.utilities.tests.test_deprecated.NEW_NAME"), + ), + "REMOVED": ObjectRemoved( + name="colour.utilities.tests.test_deprecated.REMOVED" + ), + }, + ) +except KeyError: # pragma: no cover + pass del ModuleAPI del ObjectRenamed diff --git a/colour/utilities/tests/test_deprecation.py b/colour/utilities/tests/test_deprecation.py index 206c6da793..6a26fff125 100644 --- a/colour/utilities/tests/test_deprecation.py +++ b/colour/utilities/tests/test_deprecation.py @@ -1,313 +1,330 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.utilities.deprecation` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.utilities.deprecation` module.""" import sys import unittest +from colour.utilities import ColourUsageWarning from colour.utilities.deprecation import ( - ObjectRenamed, ObjectRemoved, ObjectFutureRename, ObjectFutureRemove, - ObjectFutureAccessChange, ObjectFutureAccessRemove, ArgumentRenamed, - ArgumentRemoved, ArgumentFutureRename, ArgumentFutureRemove, ModuleAPI, - get_attribute, build_API_changes, handle_arguments_deprecation) - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' + ObjectRenamed, + ObjectRemoved, + ObjectFutureRename, + ObjectFutureRemove, + ObjectFutureAccessChange, + ObjectFutureAccessRemove, + ArgumentRenamed, + ArgumentRemoved, + ArgumentFutureRename, + ArgumentFutureRemove, + ModuleAPI, + get_attribute, + build_API_changes, + handle_arguments_deprecation, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'TestObjectRenamed', 'TestObjectRemoved', 'TestObjectFutureRename', - 'TestObjectFutureRemove', 'TestObjectFutureAccessChange', - 'TestObjectFutureAccessRemove', 'TestArgumentRenamed', - 'TestArgumentRemoved', 'TestArgumentFutureRename', - 'TestArgumentFutureRemove', 'TestModuleAPI', 'TestGetAttribute', - 'TestBuildAPIChanges', 'TestHandleArgumentsDeprecation' + "TestObjectRenamed", + "TestObjectRemoved", + "TestObjectFutureRename", + "TestObjectFutureRemove", + "TestObjectFutureAccessChange", + "TestObjectFutureAccessRemove", + "TestArgumentRenamed", + "TestArgumentRemoved", + "TestArgumentFutureRename", + "TestArgumentFutureRemove", + "TestModuleAPI", + "TestGetAttribute", + "TestBuildAPIChanges", + "TestHandleArgumentsDeprecation", ] class TestObjectRenamed(unittest.TestCase): """ - Defines :class:`colour.utilities.deprecation.ObjectRenamed` class unit + Define :class:`colour.utilities.deprecation.ObjectRenamed` class unit tests methods. """ def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__str__', ) + required_methods = ("__str__",) for method in required_methods: self.assertIn(method, dir(ObjectRenamed)) def test__str__(self): """ - Tests :meth:`colour.utilities.deprecation.ObjectRenamed.__str__` + Test :meth:`colour.utilities.deprecation.ObjectRenamed.__str__` method. """ - self.assertIn('name', str(ObjectRenamed('name', 'new_name'))) - self.assertIn('new_name', str(ObjectRenamed('name', 'new_name'))) + self.assertIn("name", str(ObjectRenamed("name", "new_name"))) + self.assertIn("new_name", str(ObjectRenamed("name", "new_name"))) class TestObjectRemoved(unittest.TestCase): """ - Defines :class:`colour.utilities.deprecation.ObjectRemoved` class unit + Define :class:`colour.utilities.deprecation.ObjectRemoved` class unit tests methods. """ def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__str__', ) + required_methods = ("__str__",) for method in required_methods: self.assertIn(method, dir(ObjectRemoved)) def test__str__(self): """ - Tests :meth:`colour.utilities.deprecation.ObjectRemoved.__str__` + Test :meth:`colour.utilities.deprecation.ObjectRemoved.__str__` method. """ - self.assertIn('name', str(ObjectRemoved('name'))) + self.assertIn("name", str(ObjectRemoved("name"))) class TestObjectFutureRename(unittest.TestCase): """ - Defines :class:`colour.utilities.deprecation.ObjectFutureRename` class unit + Define :class:`colour.utilities.deprecation.ObjectFutureRename` class unit tests methods. """ def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__str__', ) + required_methods = ("__str__",) for method in required_methods: self.assertIn(method, dir(ObjectFutureRename)) def test__str__(self): """ - Tests :meth:`colour.utilities.deprecation.ObjectFutureRename.__str__` + Test :meth:`colour.utilities.deprecation.ObjectFutureRename.__str__` method. """ - self.assertIn('name', str(ObjectFutureRename('name', 'new_name'))) - self.assertIn('new_name', str(ObjectFutureRename('name', 'new_name'))) + self.assertIn("name", str(ObjectFutureRename("name", "new_name"))) + self.assertIn("new_name", str(ObjectFutureRename("name", "new_name"))) class TestObjectFutureRemove(unittest.TestCase): """ - Defines :class:`colour.utilities.deprecation.ObjectFutureRemove` class unit + Define :class:`colour.utilities.deprecation.ObjectFutureRemove` class unit tests methods. """ def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__str__', ) + required_methods = ("__str__",) for method in required_methods: self.assertIn(method, dir(ObjectFutureRemove)) def test__str__(self): """ - Tests :meth:`colour.utilities.deprecation.ObjectFutureRemove.__str__` + Test :meth:`colour.utilities.deprecation.ObjectFutureRemove.__str__` method. """ - self.assertIn('name', str(ObjectFutureRemove('name', ))) + self.assertIn( + "name", + str( + ObjectFutureRemove( + "name", + ) + ), + ) class TestObjectFutureAccessChange(unittest.TestCase): """ - Defines :class:`colour.utilities.deprecation.ObjectFutureAccessChange` + Define :class:`colour.utilities.deprecation.ObjectFutureAccessChange` class unit tests methods. """ def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__str__', ) + required_methods = ("__str__",) for method in required_methods: self.assertIn(method, dir(ObjectFutureAccessChange)) def test__str__(self): """ - Tests :meth:`colour.utilities.deprecation.\ + Test :meth:`colour.utilities.deprecation.\ ObjectFutureAccessChange.__str__` method. """ - self.assertIn('name', - str(ObjectFutureAccessChange('name', 'new_access'))) - self.assertIn('new_access', - str(ObjectFutureAccessChange('name', 'new_access'))) + self.assertIn( + "name", str(ObjectFutureAccessChange("name", "new_access")) + ) + self.assertIn( + "new_access", str(ObjectFutureAccessChange("name", "new_access")) + ) class TestObjectFutureAccessRemove(unittest.TestCase): """ - Defines :class:`colour.utilities.deprecation.ObjectFutureAccessRemove` + Define :class:`colour.utilities.deprecation.ObjectFutureAccessRemove` class unit tests methods. """ def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__str__', ) + required_methods = ("__str__",) for method in required_methods: self.assertIn(method, dir(ObjectFutureAccessRemove)) def test__str__(self): """ - Tests :meth:`colour.utilities.deprecation.\ + Test :meth:`colour.utilities.deprecation.\ ObjectFutureAccessRemove.__str__` method. """ - self.assertIn('name', str(ObjectFutureAccessRemove('name', ))) + self.assertIn( + "name", + str( + ObjectFutureAccessRemove( + "name", + ) + ), + ) class TestArgumentRenamed(unittest.TestCase): """ - Defines :class:`colour.utilities.deprecation.ArgumentRenamed` class unit + Define :class:`colour.utilities.deprecation.ArgumentRenamed` class unit tests methods. """ def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__str__', ) + required_methods = ("__str__",) for method in required_methods: self.assertIn(method, dir(ArgumentRenamed)) def test__str__(self): """ - Tests :meth:`colour.utilities.deprecation.ArgumentRenamed.__str__` + Test :meth:`colour.utilities.deprecation.ArgumentRenamed.__str__` method. """ - self.assertIn('name', str(ArgumentRenamed('name', 'new_name'))) - self.assertIn('new_name', str(ArgumentRenamed('name', 'new_name'))) + self.assertIn("name", str(ArgumentRenamed("name", "new_name"))) + self.assertIn("new_name", str(ArgumentRenamed("name", "new_name"))) class TestArgumentRemoved(unittest.TestCase): """ - Defines :class:`colour.utilities.deprecation.ArgumentRemoved` class unit + Define :class:`colour.utilities.deprecation.ArgumentRemoved` class unit tests methods. """ def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__str__', ) + required_methods = ("__str__",) for method in required_methods: self.assertIn(method, dir(ArgumentRemoved)) def test__str__(self): """ - Tests :meth:`colour.utilities.deprecation.ArgumentRemoved.__str__` + Test :meth:`colour.utilities.deprecation.ArgumentRemoved.__str__` method. """ - self.assertIn('name', str(ArgumentRemoved('name'))) + self.assertIn("name", str(ArgumentRemoved("name"))) class TestArgumentFutureRename(unittest.TestCase): """ - Defines :class:`colour.utilities.deprecation.ArgumentFutureRename` class + Define :class:`colour.utilities.deprecation.ArgumentFutureRename` class unit tests methods. """ def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__str__', ) + required_methods = ("__str__",) for method in required_methods: self.assertIn(method, dir(ArgumentFutureRename)) def test__str__(self): """ - Tests :meth:`colour.utilities.deprecation.\ + Test :meth:`colour.utilities.deprecation.\ ArgumentFutureRename.__str__` method. """ - self.assertIn('name', str(ArgumentFutureRename('name', 'new_name'))) - self.assertIn('new_name', str( - ArgumentFutureRename('name', 'new_name'))) + self.assertIn("name", str(ArgumentFutureRename("name", "new_name"))) + self.assertIn( + "new_name", str(ArgumentFutureRename("name", "new_name")) + ) class TestArgumentFutureRemove(unittest.TestCase): """ - Defines :class:`colour.utilities.deprecation.ArgumentFutureRemove` class + Define :class:`colour.utilities.deprecation.ArgumentFutureRemove` class unit tests methods. """ def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__str__', ) + required_methods = ("__str__",) for method in required_methods: self.assertIn(method, dir(ArgumentFutureRemove)) def test__str__(self): """ - Tests :meth:`colour.utilities.deprecation.\ + Test :meth:`colour.utilities.deprecation.\ ArgumentFutureRemove.__str__` method. """ - self.assertIn('name', str(ArgumentFutureRemove('name', ))) + self.assertIn( + "name", + str( + ArgumentFutureRemove( + "name", + ) + ), + ) class TestModuleAPI(unittest.TestCase): """ - Defines :class:`colour.utilities.deprecation.ModuleAPI` class unit tests + Define :class:`colour.utilities.deprecation.ModuleAPI` class unit tests methods. """ def test_required_methods(self): - """ - Tests presence of required methods. - """ + """Test the presence of required methods.""" - required_methods = ('__init__', '__getattr__', '__dir__') + required_methods = ("__init__", "__getattr__", "__dir__") for method in required_methods: self.assertIn(method, dir(ModuleAPI)) def test__getattr__(self): """ - Tests :func:`colour.utilities.deprecation.ModuleAPI.__getattr__` + Test :meth:`colour.utilities.deprecation.ModuleAPI.__getattr__` method. """ @@ -315,135 +332,164 @@ def test__getattr__(self): self.assertIsNone(colour.utilities.tests.test_deprecated.NAME) - # TODO: Use "assertWarns" when dropping Python 2.7. - getattr(colour.utilities.tests.test_deprecated, 'OLD_NAME') + def assert_warns(): + """Help to test the runtime warning.""" + + getattr(colour.utilities.tests.test_deprecated, "OLD_NAME") - del sys.modules['colour.utilities.tests.test_deprecated'] + self.assertWarns(ColourUsageWarning, assert_warns) + + del sys.modules["colour.utilities.tests.test_deprecated"] def test_raise_exception__getattr__(self): """ - Tests :func:`colour.utilities.deprecation.ModuleAPI.__getattr__` + Test :func:`colour.utilities.deprecation.ModuleAPI.__getattr__` method raised exception. """ import colour.utilities.tests.test_deprecated - self.assertRaises(AttributeError, getattr, - colour.utilities.tests.test_deprecated, 'REMOVED') + self.assertRaises( + AttributeError, + getattr, + colour.utilities.tests.test_deprecated, + "REMOVED", + ) - del sys.modules['colour.utilities.tests.test_deprecated'] + del sys.modules["colour.utilities.tests.test_deprecated"] class TestGetAttribute(unittest.TestCase): """ - Defines :func:`colour.utilities.deprecation.get_attribute` definition unit + Define :func:`colour.utilities.deprecation.get_attribute` definition unit tests methods. """ def test_get_attribute(self): - """ - Tests :func:`colour.utilities.deprecation.get_attribute` definition. - """ + """Test :func:`colour.utilities.deprecation.get_attribute` definition.""" from colour import adaptation - self.assertIs(get_attribute('colour.adaptation'), adaptation) + + self.assertIs(get_attribute("colour.adaptation"), adaptation) from colour.models import eotf_inverse_sRGB + self.assertIs( - get_attribute('colour.models.eotf_inverse_sRGB'), - eotf_inverse_sRGB) + get_attribute("colour.models.eotf_inverse_sRGB"), eotf_inverse_sRGB + ) + + from colour.utilities.array import as_float - from colour.utilities.array import as_numeric self.assertIs( - get_attribute('colour.utilities.array.as_numeric'), as_numeric) + get_attribute("colour.utilities.array.as_float"), as_float + ) - if 'colour.utilities.tests.test_deprecated' in sys.modules: - del sys.modules['colour.utilities.tests.test_deprecated'] + if "colour.utilities.tests.test_deprecated" in sys.modules: + del sys.modules["colour.utilities.tests.test_deprecated"] attribute = get_attribute( - 'colour.utilities.tests.test_deprecated.NEW_NAME') + "colour.utilities.tests.test_deprecated.NEW_NAME" + ) import colour.utilities.tests.test_deprecated - self.assertIs(attribute, - colour.utilities.tests.test_deprecated.NEW_NAME) - del sys.modules['colour.utilities.tests.test_deprecated'] + + self.assertIs( + attribute, colour.utilities.tests.test_deprecated.NEW_NAME + ) + del sys.modules["colour.utilities.tests.test_deprecated"] class TestBuildAPIChanges(unittest.TestCase): """ - Defines :func:`colour.utilities.deprecation.build_API_changes` definition + Define :func:`colour.utilities.deprecation.build_API_changes` definition unit tests methods. """ def test_build_API_changes(self): """ - Tests :func:`colour.utilities.deprecation.build_API_changes` + Test :func:`colour.utilities.deprecation.build_API_changes` definition. """ - changes = build_API_changes({ - 'ObjectRenamed': [[ - 'module.object_1_name', - 'module.object_1_new_name', - ]], - 'ObjectFutureRename': [[ - 'module.object_2_name', - 'module.object_2_new_name', - ]], - 'ObjectFutureAccessChange': [[ - 'module.object_3_access', - 'module.sub_module.object_3_new_access', - ]], - 'ObjectRemoved': ['module.object_4_name'], - 'ObjectFutureRemove': ['module.object_5_name'], - 'ObjectFutureAccessRemove': ['module.object_6_access'], - 'ArgumentRenamed': [[ - 'argument_1_name', - 'argument_1_new_name', - ]], - 'ArgumentFutureRename': [[ - 'argument_2_name', - 'argument_2_new_name', - ]], - 'ArgumentRemoved': ['argument_3_name'], - 'ArgumentFutureRemove': ['argument_4_name'], - }) + changes = build_API_changes( + { + "ObjectRenamed": [ + [ + "module.object_1_name", + "module.object_1_new_name", + ] + ], + "ObjectFutureRename": [ + [ + "module.object_2_name", + "module.object_2_new_name", + ] + ], + "ObjectFutureAccessChange": [ + [ + "module.object_3_access", + "module.sub_module.object_3_new_access", + ] + ], + "ObjectRemoved": ["module.object_4_name"], + "ObjectFutureRemove": ["module.object_5_name"], + "ObjectFutureAccessRemove": ["module.object_6_access"], + "ArgumentRenamed": [ + [ + "argument_1_name", + "argument_1_new_name", + ] + ], + "ArgumentFutureRename": [ + [ + "argument_2_name", + "argument_2_new_name", + ] + ], + "ArgumentRemoved": ["argument_3_name"], + "ArgumentFutureRemove": ["argument_4_name"], + } + ) for name, change_type in ( - ('object_1_name', ObjectRenamed), - ('object_2_name', ObjectFutureRename), - ('object_3_access', ObjectFutureAccessChange), - ('object_4_name', ObjectRemoved), - ('object_5_name', ObjectFutureRemove), - ('object_6_access', ObjectFutureAccessRemove), - ('argument_1_name', ArgumentRenamed), - ('argument_2_name', ArgumentFutureRename), - ('argument_3_name', ArgumentRemoved), - ('argument_4_name', ArgumentFutureRemove), + ("object_1_name", ObjectRenamed), + ("object_2_name", ObjectFutureRename), + ("object_3_access", ObjectFutureAccessChange), + ("object_4_name", ObjectRemoved), + ("object_5_name", ObjectFutureRemove), + ("object_6_access", ObjectFutureAccessRemove), + ("argument_1_name", ArgumentRenamed), + ("argument_2_name", ArgumentFutureRename), + ("argument_3_name", ArgumentRemoved), + ("argument_4_name", ArgumentFutureRemove), ): self.assertIsInstance(changes[name], change_type) class TestHandleArgumentsDeprecation(unittest.TestCase): """ - Defines :func:`colour.utilities.deprecation.handle_arguments_deprecation` + Define :func:`colour.utilities.deprecation.handle_arguments_deprecation` definition unit tests methods. """ def test_handle_arguments_deprecation(self): """ - Tests :func:`colour.utilities.deprecation.handle_arguments_deprecation` + Test :func:`colour.utilities.deprecation.handle_arguments_deprecation` definition. """ changes = { - 'ArgumentRenamed': [[ - 'argument_1_name', - 'argument_1_new_name', - ]], - 'ArgumentFutureRename': [[ - 'argument_2_name', - 'argument_2_new_name', - ]], - 'ArgumentRemoved': ['argument_3_name'], - 'ArgumentFutureRemove': ['argument_4_name'], + "ArgumentRenamed": [ + [ + "argument_1_name", + "argument_1_new_name", + ] + ], + "ArgumentFutureRename": [ + [ + "argument_2_name", + "argument_2_new_name", + ] + ], + "ArgumentRemoved": ["argument_3_name"], + "ArgumentFutureRemove": ["argument_4_name"], } self.assertDictEqual( @@ -451,12 +497,18 @@ def test_handle_arguments_deprecation(self): changes, argument_1_name=True, argument_2_name=True, - argument_4_name=True), { - 'argument_1_new_name': True, - 'argument_2_new_name': True, - 'argument_4_name': True - }) - - -if __name__ == '__main__': + argument_3_name=True, + argument_4_name=True, + argument_5_name=True, + ), + { + "argument_1_new_name": True, + "argument_2_new_name": True, + "argument_4_name": True, + "argument_5_name": True, + }, + ) + + +if __name__ == "__main__": unittest.main() diff --git a/colour/utilities/tests/test_documentation.py b/colour/utilities/tests/test_documentation.py index 323ef688f1..2ab0fcf990 100644 --- a/colour/utilities/tests/test_documentation.py +++ b/colour/utilities/tests/test_documentation.py @@ -1,65 +1,62 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.utilities.documentation` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.utilities.documentation` module.""" import os import unittest from colour.utilities.documentation import is_documentation_building -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestIsDocumentationBuilding'] +__all__ = [ + "TestIsDocumentationBuilding", +] class TestIsDocumentationBuilding(unittest.TestCase): """ - Defines :func:`colour.utilities.documentation.is_documentation_building` + Define :func:`colour.utilities.documentation.is_documentation_building` definition unit tests methods. """ def test_is_documentation_building(self): """ - Tests :func:`colour.utilities.documentation.is_documentation_building` + Test :func:`colour.utilities.documentation.is_documentation_building` definition. """ try: self.assertFalse(is_documentation_building()) - os.environ['READTHEDOCS'] = 'True' + os.environ["READTHEDOCS"] = "True" self.assertTrue(is_documentation_building()) - os.environ['READTHEDOCS'] = 'False' + os.environ["READTHEDOCS"] = "False" self.assertTrue(is_documentation_building()) - del os.environ['READTHEDOCS'] + del os.environ["READTHEDOCS"] self.assertFalse(is_documentation_building()) - os.environ['COLOUR_SCIENCE__DOCUMENTATION_BUILD'] = 'True' + os.environ["COLOUR_SCIENCE__DOCUMENTATION_BUILD"] = "True" self.assertTrue(is_documentation_building()) - os.environ['COLOUR_SCIENCE__DOCUMENTATION_BUILD'] = 'False' + os.environ["COLOUR_SCIENCE__DOCUMENTATION_BUILD"] = "False" self.assertTrue(is_documentation_building()) - del os.environ['COLOUR_SCIENCE__DOCUMENTATION_BUILD'] + del os.environ["COLOUR_SCIENCE__DOCUMENTATION_BUILD"] self.assertFalse(is_documentation_building()) finally: # pragma: no cover - if os.environ.get('READTHEDOCS'): - del os.environ['READTHEDOCS'] + if os.environ.get("READTHEDOCS"): + del os.environ["READTHEDOCS"] - if os.environ.get('COLOUR_SCIENCE__DOCUMENTATION_BUILD'): - del os.environ['COLOUR_SCIENCE__DOCUMENTATION_BUILD'] + if os.environ.get("COLOUR_SCIENCE__DOCUMENTATION_BUILD"): + del os.environ["COLOUR_SCIENCE__DOCUMENTATION_BUILD"] -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/utilities/tests/test_metrics.py b/colour/utilities/tests/test_metrics.py index bd742283fe..6986db2532 100644 --- a/colour/utilities/tests/test_metrics.py +++ b/colour/utilities/tests/test_metrics.py @@ -1,58 +1,54 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.utilities.metrics` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.utilities.metrics` module.""" import numpy as np import unittest from colour.utilities import metric_mse, metric_psnr -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestMetricMse', 'TestMetricPsnr'] +__all__ = [ + "TestMetricMse", + "TestMetricPsnr", +] class TestMetricMse(unittest.TestCase): """ - Defines :func:`colour.utilities.metrics.metric_mse` definition unit tests + Define :func:`colour.utilities.metrics.metric_mse` definition unit tests methods. """ def test_metric_mse(self): - """ - Tests :func:`colour.utilities.metrics.metric_mse` definition. - """ + """Test :func:`colour.utilities.metrics.metric_mse` definition.""" a = np.array([0.48222001, 0.31654775, 0.22070353]) self.assertEqual(metric_mse(a, a), 0) b = a * 0.9 self.assertAlmostEqual( - metric_mse(a, b), 0.0012714955474297446, places=7) + metric_mse(a, b), 0.0012714955474297446, places=7 + ) b = a * 1.1 self.assertAlmostEqual( - metric_mse(a, b), 0.0012714955474297446, places=7) + metric_mse(a, b), 0.0012714955474297446, places=7 + ) class TestMetricPsnr(unittest.TestCase): """ - Defines :func:`colour.utilities.metrics.metric_psnr` definition unit tests + Define :func:`colour.utilities.metrics.metric_psnr` definition unit tests methods. """ def test_metric_psnr(self): - """ - Tests :func:`colour.utilities.metrics.metric_psnr` definition. - """ + """Test :func:`colour.utilities.metrics.metric_psnr` definition.""" a = np.array([0.48222001, 0.31654775, 0.22070353]) self.assertEqual(metric_psnr(a, a), np.inf) @@ -64,5 +60,5 @@ def test_metric_psnr(self): self.assertAlmostEqual(metric_psnr(a, b), 28.956851563141296, places=7) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/utilities/tests/test_verbose.py b/colour/utilities/tests/test_verbose.py index 96854402a0..44e0fb31ec 100644 --- a/colour/utilities/tests/test_verbose.py +++ b/colour/utilities/tests/test_verbose.py @@ -1,100 +1,103 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.utilities.verbose` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.utilities.verbose` module.""" import os import sys import unittest -from colour.utilities import (show_warning, suppress_warnings, - describe_environment) +from colour.utilities import ( + show_warning, + suppress_warnings, + describe_environment, +) from colour.utilities import warning -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestShowWarning', 'TestSuppressWarnings', 'TestDescribeEnvironment' + "TestShowWarning", + "TestSuppressWarnings", + "TestDescribeEnvironment", ] class TestShowWarning(unittest.TestCase): """ - Defines :func:`colour.utilities.verbose.show_warning` definition unit tests + Define :func:`colour.utilities.verbose.show_warning` definition unit tests methods. """ def test_show_warning(self): - """ - Tests :func:`colour.utilities.verbose.show_warning` definition. - """ + """Test :func:`colour.utilities.verbose.show_warning` definition.""" - show_warning('This is a unit test warning!', Warning, None, None) + show_warning("This is a unit test warning!", Warning, None, None) with open(os.devnull) as dev_null: - show_warning('This is a unit test warning!', Warning, None, None, - dev_null) + show_warning( + "This is a unit test warning!", Warning, None, None, dev_null + ) stderr = sys.stderr try: sys.stderr = None - show_warning('This is a unit test warning!', Warning, None, None) + show_warning("This is a unit test warning!", Warning, None, None) finally: sys.stderr = stderr class TestSuppressWarnings(unittest.TestCase): """ - Defines :func:`colour.utilities.verbose.suppress_warnings` definition unit + Define :func:`colour.utilities.verbose.suppress_warnings` definition unit tests methods. """ def test_suppress_warnings(self): - """ - Tests :func:`colour.utilities.verbose.suppress_warnings` definition. - """ + """Test :func:`colour.utilities.verbose.suppress_warnings` definition.""" with suppress_warnings(): - warning('This is a suppressed unit test warning!') + warning("This is a suppressed unit test warning!") class TestDescribeEnvironment(unittest.TestCase): """ - Defines :func:`colour.utilities.verbose.describe_environment` definition + Define :func:`colour.utilities.verbose.describe_environment` definition unit tests methods. """ def test_describe_environment(self): - """ - Tests :func:`colour.utilities.verbose.describe_environment` definition. - """ + """Test :func:`colour.utilities.verbose.describe_environment` definition.""" environment = describe_environment() self.assertIsInstance(environment, dict) self.assertListEqual( sorted(environment.keys()), - ['Interpreter', 'Runtime', 'colour-science.org']) + ["Interpreter", "Runtime", "colour-science.org"], + ) environment = describe_environment(development_packages=True) self.assertListEqual( sorted(environment.keys()), - ['Development', 'Interpreter', 'Runtime', 'colour-science.org']) + ["Development", "Interpreter", "Runtime", "colour-science.org"], + ) environment = describe_environment( - development_packages=True, extras_packages=True) + development_packages=True, extras_packages=True + ) self.assertListEqual( - sorted(environment.keys()), [ - 'Development', 'Extras', 'Interpreter', 'Runtime', - 'colour-science.org' - ]) + sorted(environment.keys()), + [ + "Development", + "Extras", + "Interpreter", + "Runtime", + "colour-science.org", + ], + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/utilities/verbose.py b/colour/utilities/verbose.py index 265a553cdb..5e16010286 100644 --- a/colour/utilities/verbose.py +++ b/colour/utilities/verbose.py @@ -1,87 +1,115 @@ -# -*- coding: utf-8 -*- """ Verbose ======= -Defines verbose related objects. +Defines the verbose related objects. """ -from __future__ import division, print_function, unicode_literals +from __future__ import annotations import numpy as np import os import sys import traceback import warnings -from collections import OrderedDict, defaultdict +from collections import defaultdict from contextlib import contextmanager from itertools import chain from textwrap import TextWrapper from warnings import filterwarnings, formatwarning, warn -from colour.utilities import is_string - -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.utilities import is_string, optional +from colour.hints import ( + Any, + Boolean, + Callable, + Dict, + Integer, + LiteralWarning, + Mapping, + Generator, + Optional, + TextIO, + Type, + Union, + cast, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ - 'ColourWarning', 'ColourUsageWarning', 'ColourRuntimeWarning', - 'message_box', 'show_warning', 'warning', 'runtime_warning', - 'usage_warning', 'filter_warnings', 'suppress_warnings', - 'numpy_print_options', 'ANCILLARY_COLOUR_SCIENCE_PACKAGES', - 'ANCILLARY_RUNTIME_PACKAGES', 'ANCILLARY_DEVELOPMENT_PACKAGES', - 'ANCILLARY_EXTRAS_PACKAGES', 'describe_environment' + "ColourWarning", + "ColourUsageWarning", + "ColourRuntimeWarning", + "message_box", + "show_warning", + "warning", + "runtime_warning", + "usage_warning", + "filter_warnings", + "suppress_warnings", + "numpy_print_options", + "ANCILLARY_COLOUR_SCIENCE_PACKAGES", + "ANCILLARY_RUNTIME_PACKAGES", + "ANCILLARY_DEVELOPMENT_PACKAGES", + "ANCILLARY_EXTRAS_PACKAGES", + "describe_environment", ] class ColourWarning(Warning): """ - This is the base class of *Colour* warnings. It is a subclass of - :class:`Warning` class. + Define the base class of *Colour* warnings. + + It is a subclass of the :class:`Warning` class. """ class ColourUsageWarning(Warning): """ - This is the base class of *Colour* usage warnings. It is a subclass - of :class:`colour.utilities.ColourWarning` class. + Define the base class of *Colour* usage warnings. + + It is a subclass of the :class:`colour.utilities.ColourWarning` class. """ class ColourRuntimeWarning(Warning): """ - This is the base class of *Colour* runtime warnings. It is a subclass - of :class:`colour.utilities.ColourWarning` class. + Define the base class of *Colour* runtime warnings. + + It is a subclass of the :class:`colour.utilities.ColourWarning` class. """ -def message_box(message, width=79, padding=3, print_callable=print): +def message_box( + message: str, + width: Integer = 79, + padding: Integer = 3, + print_callable: Callable = print, +): """ - Prints a message inside a box. + Print a message inside a box. Parameters ---------- - message : unicode + message Message to print. - width : int, optional + width Message box width. - padding : unicode, optional - Padding on each sides of the message. - print_callable : callable, optional + padding + Padding on each side of the message. + print_callable Callable used to print the message box. - Returns - ------- - bool - Definition success. - Examples -------- - >>> message = ('Lorem ipsum dolor sit amet, consectetur adipiscing elit, ' + >>> message = ( + ... 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, ' ... 'sed do eiusmod tempor incididunt ut labore et dolore magna ' ... 'aliqua.') >>> message_box(message, width=75) @@ -91,7 +119,6 @@ def message_box(message, width=79, padding=3, print_callable=print): * eiusmod tempor incididunt ut labore et dolore magna aliqua. * * * =========================================================================== - True >>> message_box(message, width=60) ============================================================ * * @@ -100,7 +127,6 @@ def message_box(message, width=79, padding=3, print_callable=print): * dolore magna aliqua. * * * ============================================================ - True >>> message_box(message, width=75, padding=16) =========================================================================== * * @@ -110,43 +136,42 @@ def message_box(message, width=79, padding=3, print_callable=print): * aliqua. * * * =========================================================================== - True """ ideal_width = width - padding * 2 - 2 def inner(text): - """ - Formats and pads inner text for the message box. - """ + """Format and pads inner text for the message box.""" - return '*{0}{1}{2}{0}*'.format( - ' ' * padding, text, (' ' * (width - len(text) - padding * 2 - 2))) + return ( + f'*{" " * padding}' + f'{text}{" " * (width - len(text) - padding * 2 - 2)}' + f'{" " * padding}*' + ) - print_callable('=' * width) - print_callable(inner('')) + print_callable("=" * width) + print_callable(inner("")) wrapper = TextWrapper( - width=ideal_width, break_long_words=False, replace_whitespace=False) + width=ideal_width, break_long_words=False, replace_whitespace=False + ) lines = [wrapper.wrap(line) for line in message.split("\n")] - lines = [' ' if len(line) == 0 else line for line in lines] - for line in chain(*lines): + for line in chain(*[" " if len(line) == 0 else line for line in lines]): print_callable(inner(line.expandtabs())) - print_callable(inner('')) - print_callable('=' * width) - - return True + print_callable(inner("")) + print_callable("=" * width) -def show_warning(message, - category, - path, - line, - file_=None, - code=None, - frame_range=(1, None)): +def show_warning( + message: Union[Warning, str], + category: Type[Warning], + filename: str, + lineno: Integer, + file: Optional[TextIO] = None, + line: Optional[str] = None, +) -> None: """ Alternative :func:`warnings.showwarning` definition that allows traceback printing. @@ -157,21 +182,19 @@ def show_warning(message, Parameters ---------- - message : unicode + message Warning message. - category : Warning + category :class:`Warning` sub-class. - path : unicode + filename File path to read the line at ``lineno`` from if ``line`` is None. - line : int + lineno Line number to read the line at in ``filename`` if ``line`` is None. - file_ : file, optional + file :class:`file` object to write the warning to, defaults to :attr:`sys.stderr` attribute. - code : unicode, optional + line Source code to be included in the warning message. - frame_range : array_like, optional - Traceback frame range, i.e first frame and numbers of frame above it. Notes ----- @@ -182,10 +205,11 @@ def show_warning(message, complete traceback from the point where the warning occurred. """ - if file_ is None: - file_ = sys.stderr - if file_ is None: - return + frame_range = (1, None) + + file = optional(file, sys.stderr) + if file is None: + return try: # Generating a traceback to print useful warning origin. @@ -194,113 +218,100 @@ def show_warning(message, try: raise ZeroDivisionError except ZeroDivisionError: - frame = sys.exc_info()[2].tb_frame.f_back - while frame_in: + exception_traceback = sys.exc_info()[2] + frame = ( + exception_traceback.tb_frame.f_back + if exception_traceback is not None + else None + ) + while frame_in and frame is not None: frame = frame.f_back frame_in -= 1 - traceback.print_stack(frame, frame_out, file_) + traceback.print_stack(frame, frame_out, file) - file_.write(formatwarning(message, category, path, line, code)) - except (IOError, UnicodeError): + file.write(formatwarning(message, category, filename, lineno, line)) + except (OSError, UnicodeError): pass if os.environ.get( # pragma: no cover - 'COLOUR_SCIENCE__COLOUR__SHOW_WARNINGS_WITH_TRACEBACK'): - warnings.showwarning = show_warning + "COLOUR_SCIENCE__COLOUR__SHOW_WARNINGS_WITH_TRACEBACK" +): + warnings.showwarning = show_warning # pragma: no cover -def warning(*args, **kwargs): +def warning(*args: Any, **kwargs: Any): """ - Issues a warning. + Issue a warning. Other Parameters ---------------- - \\*args : list, optional + args Arguments. - \\**kwargs : dict, optional + kwargs Keywords arguments. - Returns - ------- - bool - Definition success. - Examples -------- >>> warning('This is a warning!') # doctest: +SKIP """ - kwargs['category'] = kwargs.get('category', ColourWarning) + kwargs["category"] = kwargs.get("category", ColourWarning) warn(*args, **kwargs) - return True - -def runtime_warning(*args, **kwargs): +def runtime_warning(*args: Any, **kwargs: Any): """ - Issues a runtime warning. + Issue a runtime warning. Other Parameters ---------------- - \\*args : list, optional + args Arguments. - \\**kwargs : dict, optional + kwargs Keywords arguments. - Returns - ------- - bool - Definition success. - Examples -------- >>> usage_warning('This is a runtime warning!') # doctest: +SKIP """ - kwargs['category'] = ColourRuntimeWarning + kwargs["category"] = ColourRuntimeWarning warning(*args, **kwargs) - return True - -def usage_warning(*args, **kwargs): +def usage_warning(*args: Any, **kwargs: Any): """ - Issues an usage warning. + Issue a usage warning. Other Parameters ---------------- - \\*args : list, optional + args Arguments. - \\**kwargs : dict, optional + kwargs Keywords arguments. - Returns - ------- - bool - Definition success. - Examples -------- >>> usage_warning('This is an usage warning!') # doctest: +SKIP """ - kwargs['category'] = ColourUsageWarning + kwargs["category"] = ColourUsageWarning warning(*args, **kwargs) - return True - -def filter_warnings(colour_runtime_warnings=None, - colour_usage_warnings=None, - colour_warnings=None, - python_warnings=None): +def filter_warnings( + colour_runtime_warnings: Optional[Union[bool, LiteralWarning]] = None, + colour_usage_warnings: Optional[Union[bool, LiteralWarning]] = None, + colour_warnings: Optional[Union[bool, LiteralWarning]] = None, + python_warnings: Optional[Union[bool, LiteralWarning]] = None, +): """ - Filters *Colour* and also optionally overall Python warnings. + Filter *Colour* and also optionally overall Python warnings. The possible values for all the actions, i.e. each argument, are as follows: @@ -317,16 +328,16 @@ def filter_warnings(colour_runtime_warnings=None, Parameters ---------- - colour_runtime_warnings : bool or unicode, optional + colour_runtime_warnings Whether to filter *Colour* runtime warnings according to the action value. - colour_usage_warnings : bool or unicode, optional + colour_usage_warnings Whether to filter *Colour* usage warnings according to the action value. - colour_warnings : bool or unicode, optional + colour_warnings Whether to filter *Colour* warnings, this also filters *Colour* usage and runtime warnings according to the action value. - python_warnings : bool or unicode, optional + python_warnings Whether to filter *Python* warnings according to the action value. Examples @@ -370,9 +381,9 @@ def filter_warnings(colour_runtime_warnings=None, continue if is_string(action): - action = action + action = cast(LiteralWarning, str(action)) else: - action = 'ignore' if action else 'default' + action = "ignore" if action else "default" filterwarnings(action, category=category) @@ -382,13 +393,15 @@ def filter_warnings(colour_runtime_warnings=None, @contextmanager -def suppress_warnings(colour_runtime_warnings=None, - colour_usage_warnings=None, - colour_warnings=None, - python_warnings=None): +def suppress_warnings( + colour_runtime_warnings: Optional[Union[bool, LiteralWarning]] = None, + colour_usage_warnings: Optional[Union[bool, LiteralWarning]] = None, + colour_warnings: Optional[Union[bool, LiteralWarning]] = None, + python_warnings: Optional[Union[bool, LiteralWarning]] = None, +) -> Generator: """ - A context manager filtering *Colour* and also optionally overall Python - warnings. + Define a context manager filtering *Colour* and also optionally overall + Python warnings. The possible values for all the actions, i.e. each argument, are as follows: @@ -405,16 +418,16 @@ def suppress_warnings(colour_runtime_warnings=None, Parameters ---------- - colour_runtime_warnings : bool or unicode, optional + colour_runtime_warnings Whether to filter *Colour* runtime warnings according to the action value. - colour_usage_warnings : bool or unicode, optional + colour_usage_warnings Whether to filter *Colour* usage warnings according to the action value. - colour_warnings : bool or unicode, optional + colour_warnings Whether to filter *Colour* warnings, this also filters *Colour* usage and runtime warnings according to the action value. - python_warnings : bool or unicode, optional + python_warnings Whether to filter *Python* warnings according to the action value. """ @@ -425,7 +438,8 @@ def suppress_warnings(colour_runtime_warnings=None, colour_warnings=colour_warnings, colour_runtime_warnings=colour_runtime_warnings, colour_usage_warnings=colour_usage_warnings, - python_warnings=python_warnings) + python_warnings=python_warnings, + ) try: yield @@ -435,19 +449,20 @@ def suppress_warnings(colour_runtime_warnings=None, @contextmanager -def numpy_print_options(*args, **kwargs): +def numpy_print_options(*args: Any, **kwargs: Any) -> Generator: """ - A context manager implementing context changes to *Numpy* print behaviour. + Define a context manager implementing context changes to *Numpy* print + behaviour. Other Parameters ---------------- - \\*args : list, optional + args Arguments. - \\**kwargs : dict, optional + kwargs Keywords arguments. Examples - ------- + -------- >>> np.array([np.pi]) # doctest: +ELLIPSIS array([ 3.1415926...]) >>> with numpy_print_options(formatter={'float': '{:0.1f}'.format}): @@ -463,70 +478,72 @@ def numpy_print_options(*args, **kwargs): np.set_printoptions(**options) -ANCILLARY_COLOUR_SCIENCE_PACKAGES = OrderedDict() +ANCILLARY_COLOUR_SCIENCE_PACKAGES: Dict[str, str] = {} """ Ancillary *colour-science.org* packages to describe. -ANCILLARY_COLOUR_SCIENCE_PACKAGES : OrderedDict +ANCILLARY_COLOUR_SCIENCE_PACKAGES """ -ANCILLARY_RUNTIME_PACKAGES = OrderedDict() +ANCILLARY_RUNTIME_PACKAGES: Dict[str, str] = {} """ Ancillary runtime packages to describe. -ANCILLARY_RUNTIME_PACKAGES : OrderedDict +ANCILLARY_RUNTIME_PACKAGES """ -ANCILLARY_DEVELOPMENT_PACKAGES = OrderedDict() +ANCILLARY_DEVELOPMENT_PACKAGES: Dict[str, str] = {} """ Ancillary development packages to describe. -ANCILLARY_DEVELOPMENT_PACKAGES : OrderedDict +ANCILLARY_DEVELOPMENT_PACKAGES """ -ANCILLARY_EXTRAS_PACKAGES = OrderedDict() +ANCILLARY_EXTRAS_PACKAGES: Dict[str, str] = {} """ Ancillary extras packages to describe. -ANCILLARY_EXTRAS_PACKAGES : OrderedDict +ANCILLARY_EXTRAS_PACKAGES """ -def describe_environment(runtime_packages=True, - development_packages=False, - extras_packages=False, - print_environment=True, - **kwargs): +def describe_environment( + runtime_packages: Boolean = True, + development_packages: Boolean = False, + extras_packages: Boolean = False, + print_environment: Boolean = True, + **kwargs: Any, +) -> defaultdict: """ - Describes *Colour* running environment, i.e. interpreter, runtime and + Describe *Colour* running environment, i.e. interpreter, runtime and development packages. Parameters ---------- - runtime_packages : bool, optional + runtime_packages Whether to return the runtime packages versions. - development_packages : bool, optional + development_packages Whether to return the development packages versions. - extras_packages : bool, optional + extras_packages Whether to return the extras packages versions. - print_environment : bool, optional + print_environment Whether to print the environment. Other Parameters ---------------- - padding : unicode, optional + padding {:func:`colour.utilities.message_box`}, - Padding on each sides of the message. - print_callable : callable, optional + Padding on each side of the message. + print_callable {:func:`colour.utilities.message_box`}, Callable used to print the message box. - width : int, optional + width {:func:`colour.utilities.message_box`}, Message box width. Returns ------- - defaultdict + :class:`collections.defaultdict` Environment. Examples @@ -535,21 +552,21 @@ def describe_environment(runtime_packages=True, =========================================================================== * * * Interpreter : * - * python : 3.7.4 (default, Sep 7 2019, 18:27:02) * - * [Clang 10.0.1 (clang-1001.0.46.4)] * + * python : 3.8.6 (default, Nov 20 2020, 18:29:40) * + * [Clang 12.0.0 (clang-1200.0.32.27)] * * * * colour-science.org : * - * colour : v0.3.13-293-gecf1dc8a * + * colour : v0.3.16-3-gd8bac475 * * * * Runtime : * - * imageio : 2.6.1 * - * numpy : 1.17.2 * - * scipy : 1.3.1 * - * six : 1.12.0 * - * pandas : 0.24.2 * - * matplotlib : 3.0.3 * - * networkx : 2.3 * - * pygraphviz : 1.5 * + * imageio : 2.9.0 * + * matplotlib : 3.3.3 * + * networkx : 2.5 * + * numpy : 1.19.4 * + * pandas : 0.25.3 * + * pygraphviz : 1.6 * + * scipy : 1.5.4 * + * tqdm : 4.54.0 * * * =========================================================================== >>> environment = describe_environment(True, True, True, width=75) @@ -557,51 +574,51 @@ def describe_environment(runtime_packages=True, =========================================================================== * * * Interpreter : * - * python : 3.7.4 (default, Sep 7 2019, 18:27:02) * - * [Clang 10.0.1 (clang-1001.0.46.4)] * + * python : 3.8.6 (default, Nov 20 2020, 18:29:40) * + * [Clang 12.0.0 (clang-1200.0.32.27)] * * * * colour-science.org : * - * colour : v0.3.13-293-gecf1dc8a * + * colour : v0.3.16-3-gd8bac475 * * * * Runtime : * - * imageio : 2.6.1 * - * numpy : 1.17.2 * - * scipy : 1.3.1 * - * six : 1.12.0 * - * pandas : 0.24.2 * - * matplotlib : 3.0.3 * - * networkx : 2.3 * - * pygraphviz : 1.5 * + * imageio : 2.9.0 * + * matplotlib : 3.3.3 * + * networkx : 2.5 * + * numpy : 1.19.4 * + * pandas : 0.25.3 * + * pygraphviz : 1.6 * + * scipy : 1.5.4 * + * tqdm : 4.54.0 * * * * Development : * * biblib-simple : 0.1.1 * - * coverage : 4.5.4 * - * coveralls : 1.8.2 * - * flake8 : 3.7.8 * - * invoke : 1.3.0 * + * coverage : 5.3 * + * coveralls : 2.2.0 * + * flake8 : 3.8.4 * + * invoke : 1.4.1 * * jupyter : 1.0.0 * - * mock : 3.0.5 * + * mock : 4.0.2 * * nose : 1.3.7 * - * pre-commit : 1.18.3 * - * pytest : 5.2.1 * - * restructuredtext-lint : 1.3.0 * - * sphinx : 2.2.0 * - * sphinx_rtd_theme : 0.4.3 * + * pre-commit : 2.1.1 * + * pytest : 6.1.2 * + * restructuredtext-lint : 1.3.2 * + * sphinx : 3.1.2 * + * sphinx_rtd_theme : 0.5.0 * * sphinxcontrib-bibtex : 1.0.0 * - * toml : 0.10.0 * - * twine : 1.15.0 * + * toml : 0.10.2 * + * twine : 3.2.0 * * yapf : 0.23.0 * * * * Extras : * * ipywidgets : 7.5.1 * - * notebook : 6.0.1 * + * notebook : 6.1.5 * * * =========================================================================== """ - environment = defaultdict(OrderedDict) + environment: defaultdict = defaultdict(dict) - environment['Interpreter']['python'] = sys.version + environment["Interpreter"]["python"] = sys.version import subprocess # nosec @@ -613,41 +630,49 @@ def describe_environment(runtime_packages=True, # NOTE: A few clauses are not reached and a few packages are not available # during continuous integration and are thus ignored for coverage. try: # pragma: no cover - version = subprocess.check_output( # nosec - ['git', 'describe'], + output = subprocess.check_output( # nosec + ["git", "describe"], cwd=colour.__path__[0], - stderr=subprocess.STDOUT).strip() - version = version.decode('utf-8') + stderr=subprocess.STDOUT, + ).strip() + version = output.decode("utf-8") except Exception: # pragma: no cover version = colour.__version__ - environment['colour-science.org']['colour'] = version - environment['colour-science.org'].update(ANCILLARY_COLOUR_SCIENCE_PACKAGES) + environment["colour-science.org"]["colour"] = version + environment["colour-science.org"].update(ANCILLARY_COLOUR_SCIENCE_PACKAGES) if runtime_packages: for package in [ - 'imageio', 'matplotlib', 'networkx', 'numpy', 'pandas', - 'pygraphviz', 'scipy', 'six' + "imageio", + "matplotlib", + "networkx", + "numpy", + "pandas", + "pygraphviz", + "PyOpenColorIO", + "scipy", + "sklearn", + "tqdm", + "trimesh", ]: try: namespace = __import__(package) - environment['Runtime'][package] = namespace.__version__ + environment["Runtime"][package] = namespace.__version__ except ImportError: continue # OpenImageIO try: # pragma: no cover - namespace = __import__('OpenImageIO') - environment['Runtime']['OpenImageIO'] = namespace.VERSION_STRING + namespace = __import__("OpenImageIO") + environment["Runtime"]["OpenImageIO"] = namespace.VERSION_STRING except ImportError: # pragma: no cover pass - environment['Runtime'].update(ANCILLARY_RUNTIME_PACKAGES) + environment["Runtime"].update(ANCILLARY_RUNTIME_PACKAGES) - def _get_package_version(package, mapping): - """ - Returns given package version. - """ + def _get_package_version(package: str, mapping: Mapping) -> str: + """Return given package version.""" namespace = __import__(package) @@ -666,59 +691,77 @@ def _get_package_version(package, mapping): if development_packages: mapping = { - 'biblib.bib': 'biblib-simple', - 'pre_commit': 'pre-commit', - 'restructuredtext_lint': 'restructuredtext-lint', - 'sphinxcontrib.bibtex': 'sphinxcontrib-bibtex' + "biblib.bib": "biblib-simple", + "pre_commit": "pre-commit", + "restructuredtext_lint": "restructuredtext-lint", + "sphinxcontrib.bibtex": "sphinxcontrib-bibtex", } for package in [ - 'biblib.bib', 'coverage', 'coveralls', 'flake8', 'invoke', - 'jupyter', 'mock', 'nose', 'pre_commit', 'pytest', - 'restructuredtext_lint', 'sphinx', 'sphinx_rtd_theme', - 'sphinxcontrib.bibtex', 'toml', 'twine', 'yapf' + "biblib.bib", + "coverage", + "coveralls", + "flake8", + "invoke", + "jupyter", + "mock", + "nose", + "pre_commit", + "pytest", + "restructuredtext_lint", + "sphinx", + "sphinx_rtd_theme", + "sphinxcontrib.bibtex", + "toml", + "twine", + "yapf", ]: try: version = _get_package_version(package, mapping) package = mapping.get(package, package) - environment['Development'][package] = version - except Exception: + environment["Development"][package] = version + except Exception: # pragma: no cover # pylint: disable=B112 continue - environment['Development'].update(ANCILLARY_DEVELOPMENT_PACKAGES) + environment["Development"].update(ANCILLARY_DEVELOPMENT_PACKAGES) if extras_packages: mapping = {} - for package in ['ipywidgets', 'notebook']: + for package in ["ipywidgets", "notebook"]: try: version = _get_package_version(package, mapping) package = mapping.get(package, package) - environment['Extras'][package] = version - except Exception: + environment["Extras"][package] = version + except Exception: # pragma: no cover # pylint: disable=B112 continue - environment['Extras'].update(ANCILLARY_EXTRAS_PACKAGES) + environment["Extras"].update(ANCILLARY_EXTRAS_PACKAGES) if print_environment: - message = str() - for category in ('Interpreter', 'colour-science.org', 'Runtime', - 'Development', 'Extras'): + message = "" + for category in ( + "Interpreter", + "colour-science.org", + "Runtime", + "Development", + "Extras", + ): elements = environment.get(category) if not elements: continue - message += '{0} :\n'.format(category) + message += f"{category} :\n" for key, value in elements.items(): - lines = value.split('\n') - message += ' {0} : {1}\n'.format(key, lines.pop(0)) - indentation = len(' {0} : '.format(key)) + lines = value.split("\n") + message += f" {key} : {lines.pop(0)}\n" + indentation = len(f" {key} : ") for line in lines: - message += '{0}{1}\n'.format(' ' * indentation, line) + message += f"{' ' * indentation}{line}\n" - message += '\n' + message += "\n" message_box(message.strip(), **kwargs) diff --git a/colour/volume/__init__.py b/colour/volume/__init__.py index d2c6ef9b37..9871de1bec 100644 --- a/colour/volume/__init__.py +++ b/colour/volume/__init__.py @@ -1,63 +1,43 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - -import sys - -from colour.utilities.deprecation import ModuleAPI, build_API_changes -from colour.utilities.documentation import is_documentation_building - from .datasets import * # noqa from . import datasets from .macadam_limits import is_within_macadam_limits from .mesh import is_within_mesh_volume from .pointer_gamut import is_within_pointer_gamut -from .spectrum import (generate_pulse_waves, XYZ_outer_surface, - is_within_visible_spectrum) -from .rgb import (RGB_colourspace_limits, RGB_colourspace_volume_MonteCarlo, - RGB_colourspace_volume_coverage_MonteCarlo, - RGB_colourspace_pointer_gamut_coverage_MonteCarlo, - RGB_colourspace_visible_spectrum_coverage_MonteCarlo) +from .spectrum import ( + generate_pulse_waves, + XYZ_outer_surface, + solid_RoschMacAdam, + is_within_visible_spectrum, +) +from .rgb import ( + RGB_colourspace_limits, + RGB_colourspace_volume_MonteCarlo, + RGB_colourspace_volume_coverage_MonteCarlo, + RGB_colourspace_pointer_gamut_coverage_MonteCarlo, + RGB_colourspace_visible_spectrum_coverage_MonteCarlo, +) __all__ = [] __all__ += datasets.__all__ -__all__ += ['is_within_macadam_limits'] -__all__ += ['is_within_mesh_volume'] -__all__ += ['is_within_pointer_gamut'] __all__ += [ - 'generate_pulse_waves', 'XYZ_outer_surface', 'is_within_visible_spectrum' + "is_within_macadam_limits", ] __all__ += [ - 'RGB_colourspace_limits', 'RGB_colourspace_volume_MonteCarlo', - 'RGB_colourspace_volume_coverage_MonteCarlo', - 'RGB_colourspace_pointer_gamut_coverage_MonteCarlo', - 'RGB_colourspace_visible_spectrum_coverage_MonteCarlo' + "is_within_mesh_volume", +] +__all__ += [ + "is_within_pointer_gamut", +] +__all__ += [ + "generate_pulse_waves", + "XYZ_outer_surface", + "solid_RoschMacAdam", + "is_within_visible_spectrum", +] +__all__ += [ + "RGB_colourspace_limits", + "RGB_colourspace_volume_MonteCarlo", + "RGB_colourspace_volume_coverage_MonteCarlo", + "RGB_colourspace_pointer_gamut_coverage_MonteCarlo", + "RGB_colourspace_visible_spectrum_coverage_MonteCarlo", ] - - -# ----------------------------------------------------------------------------# -# --- API Changes and Deprecation Management ---# -# ----------------------------------------------------------------------------# -class volume(ModuleAPI): - def __getattr__(self, attribute): - return super(volume, self).__getattr__(attribute) - - -# v0.3.16 -API_CHANGES = { - 'ObjectRenamed': [[ - 'colour.volume.ILLUMINANT_OPTIMAL_COLOUR_STIMULI', - 'colour.volume.OPTIMAL_COLOUR_STIMULI_ILLUMINANTS', - ], ] -} -""" -Defines *colour.volume* sub-package API changes. - -API_CHANGES : dict -""" - -if not is_documentation_building(): - sys.modules['colour.volume'] = volume(sys.modules['colour.volume'], - build_API_changes(API_CHANGES)) - - del ModuleAPI, is_documentation_building, build_API_changes, sys diff --git a/colour/volume/datasets/__init__.py b/colour/volume/datasets/__init__.py index 3b17fa3dda..4178b48790 100644 --- a/colour/volume/datasets/__init__.py +++ b/colour/volume/datasets/__init__.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from .optimal_colour_stimuli import OPTIMAL_COLOUR_STIMULI_ILLUMINANTS -__all__ = ['OPTIMAL_COLOUR_STIMULI_ILLUMINANTS'] +__all__ = [ + "OPTIMAL_COLOUR_STIMULI_ILLUMINANTS", +] diff --git a/colour/volume/datasets/optimal_colour_stimuli.py b/colour/volume/datasets/optimal_colour_stimuli.py index 2b5e910d87..57e1f947cf 100644 --- a/colour/volume/datasets/optimal_colour_stimuli.py +++ b/colour/volume/datasets/optimal_colour_stimuli.py @@ -1,21 +1,15 @@ -# -*- coding: utf-8 -*- """ Optimal Colour Stimuli ====================== -Defines *MacAdam Optimal Colour Stimuli* for various illuminants in *CIE xyY* -colourspace. +Defines the *MacAdam Optimal Colour Stimuli* for various illuminants in +*CIE xyY* colourspace. The *Optimal Colour Stimuli* data is in the form of a *dict* of *ndarray* as follows:: {'name': ndarray, ..., 'name': ndarray} -where each *ndarray* contains a collection of optimal colour stimulus *ndarray* -as follows:: - - [np.array(['x', 'y', 'Y']], ..., np.array(['x', 'y', 'Y'])) - References ---------- - :cite:`MacAdam1935a` : MacAdam, D. L. (1935). Maximum Visual Efficiency of @@ -31,729 +25,733 @@ Formulae (pp. 778-779). Wiley. ISBN:978-0-471-39918-6 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np +from colour.hints import NDArray from colour.utilities import CaseInsensitiveMapping -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'OPTIMAL_COLOUR_STIMULI_A', 'OPTIMAL_COLOUR_STIMULI_C', - 'OPTIMAL_COLOUR_STIMULI_D65', 'OPTIMAL_COLOUR_STIMULI_ILLUMINANTS' + "OPTIMAL_COLOUR_STIMULI_A", + "OPTIMAL_COLOUR_STIMULI_C", + "OPTIMAL_COLOUR_STIMULI_D65", + "OPTIMAL_COLOUR_STIMULI_ILLUMINANTS", ] -OPTIMAL_COLOUR_STIMULI_A = np.array([ - [0.1120, 0.1985, 10], - [0.0859, 0.2957, 10], - [0.0549, 0.4593, 10], - [0.0433, 0.5548, 10], - [0.0386, 0.6764, 10], - [0.0441, 0.7368, 10], - [0.0578, 0.7834, 10], - [0.0786, 0.8102, 10], - [0.1030, 0.8188, 10], - [0.1276, 0.8151, 10], - [0.1510, 0.8054, 10], - [0.7188, 0.2812, 10], - [0.7112, 0.2773, 10], - [0.6506, 0.2469, 10], - [0.6015, 0.2228, 10], - [0.5604, 0.2032, 10], - [0.5179, 0.1839, 10], - [0.4590, 0.1606, 10], - [0.4302, 0.1526, 10], - [0.3946, 0.1488, 10], - [0.3514, 0.1519, 10], - [0.2949, 0.1610, 10], - [0.2452, 0.1706, 10], - [0.2009, 0.1797, 10], - [0.1197, 0.3185, 20], - [0.0977, 0.4993, 20], - [0.0929, 0.6609, 20], - [0.1073, 0.7534, 20], - [0.1187, 0.7744, 20], - [0.1335, 0.7863, 20], - [0.1505, 0.7896, 20], - [0.1683, 0.7863, 20], - [0.2028, 0.7690, 20], - [0.3641, 0.6326, 20], - [0.4206, 0.5776, 20], - [0.7008, 0.2991, 20], - [0.6726, 0.2834, 20], - [0.6350, 0.2629, 20], - [0.6020, 0.2454, 20], - [0.5601, 0.2246, 20], - [0.5005, 0.2027, 20], - [0.4823, 0.2013, 20], - [0.4532, 0.2053, 20], - [0.4281, 0.2118, 20], - [0.3651, 0.2320, 20], - [0.3070, 0.2521, 20], - [0.2500, 0.2721, 20], - [0.1828, 0.2960, 20], - [0.1442, 0.3923, 30], - [0.1407, 0.4547, 30], - [0.1393, 0.4995, 30], - [0.1390, 0.5533, 30], - [0.1402, 0.6008, 30], - [0.1439, 0.6546, 30], - [0.1535, 0.7106, 30], - [0.1667, 0.7410, 30], - [0.1763, 0.7503, 30], - [0.2002, 0.7548, 30], - [0.2403, 0.7366, 30], - [0.6800, 0.3198, 30], - [0.6759, 0.3173, 30], - [0.6488, 0.3006, 30], - [0.6208, 0.2837, 30], - [0.5863, 0.2637, 30], - [0.5606, 0.2500, 30], - [0.5382, 0.2402, 30], - [0.5168, 0.2358, 30], - [0.4791, 0.2435, 30], - [0.4295, 0.2636, 30], - [0.3905, 0.2807, 30], - [0.3290, 0.3083, 30], - [0.2202, 0.3576, 30], - [0.1769, 0.4360, 40], - [0.1800, 0.5225, 40], - [0.1881, 0.6104, 40], - [0.1958, 0.6562, 40], - [0.2019, 0.6791, 40], - [0.2106, 0.6997, 40], - [0.2314, 0.7173, 40], - [0.2405, 0.7178, 40], - [0.2607, 0.7118, 40], - [0.3023, 0.6839, 40], - [0.5021, 0.4968, 40], - [0.6570, 0.3427, 40], - [0.2151, 0.4588, 50], - [0.2202, 0.5035, 50], - [0.2303, 0.5698, 50], - [0.2392, 0.6119, 50], - [0.2507, 0.6483, 50], - [0.2574, 0.6615, 50], - [0.2660, 0.6720, 50], - [0.2842, 0.6781, 50], - [0.2994, 0.6742, 50], - [0.3244, 0.6595, 50], - [0.5025, 0.4961, 50], - [0.6332, 0.3664, 50], - [0.6296, 0.3635, 50], - [0.6054, 0.3447, 50], - [0.5803, 0.3257, 50], - [0.5600, 0.3111, 50], - [0.5350, 0.2957, 50], - [0.5207, 0.2913, 50], - [0.4996, 0.2960, 50], - [0.4503, 0.3221, 50], - [0.4000, 0.3511, 50], - [0.3587, 0.3751, 50], - [0.3105, 0.4031, 50], - [0.2546, 0.4358, 50], - [0.2576, 0.4662, 60], - [0.2656, 0.5051, 60], - [0.2702, 0.5247, 60], - [0.2806, 0.5633, 60], - [0.2898, 0.5910, 60], - [0.3000, 0.6140, 60], - [0.3192, 0.6345, 60], - [0.3400, 0.6339, 60], - [0.3797, 0.6090, 60], - [0.4252, 0.5692, 60], - [0.4923, 0.5056, 60], - [0.5995, 0.3999, 60], - [0.6065, 0.3871, 60], - [0.5751, 0.3606, 60], - [0.5508, 0.3403, 60], - [0.5252, 0.3217, 60], - [0.5139, 0.3168, 60], - [0.5005, 0.3178, 60], - [0.4761, 0.3301, 60], - [0.4496, 0.3461, 60], - [0.4103, 0.3705, 60], - [0.3375, 0.4161, 60], - [0.3124, 0.4318, 60], - [0.2634, 0.4626, 60], - [0.3038, 0.4616, 70], - [0.3105, 0.4832, 70], - [0.3202, 0.5119, 70], - [0.3255, 0.5258, 70], - [0.3395, 0.5580, 70], - [0.3537, 0.5806, 70], - [0.3810, 0.5916, 70], - [0.3900, 0.5886, 70], - [0.3999, 0.5835, 70], - [0.5005, 0.4967, 70], - [0.5690, 0.4300, 70], - [0.5849, 0.4143, 70], - [0.5812, 0.4106, 70], - [0.5776, 0.4070, 70], - [0.5706, 0.4001, 70], - [0.5351, 0.3661, 70], - [0.5202, 0.3530, 70], - [0.5004, 0.3407, 70], - [0.4904, 0.3412, 70], - [0.4794, 0.3466, 70], - [0.4703, 0.3519, 70], - [0.3706, 0.4174, 70], - [0.3501, 0.4310, 70], - [0.3219, 0.4497, 70], - [0.3527, 0.4480, 80], - [0.3603, 0.4657, 80], - [0.3803, 0.5061, 80], - [0.4100, 0.5440, 80], - [0.4299, 0.5467, 80], - [0.4402, 0.5426, 80], - [0.4598, 0.5298, 80], - [0.4803, 0.5130, 80], - [0.5000, 0.4954, 80], - [0.5218, 0.4750, 80], - [0.5419, 0.4559, 80], - [0.5603, 0.4380, 80], - [0.5566, 0.4338, 80], - [0.5457, 0.4217, 80], - [0.5190, 0.3928, 80], - [0.5004, 0.3744, 80], - [0.4916, 0.3672, 80], - [0.4799, 0.3636, 80], - [0.4751, 0.3652, 80], - [0.4698, 0.3679, 80], - [0.4560, 0.3767, 80], - [0.4011, 0.4146, 80], - [0.3805, 0.4289, 80], - [0.3704, 0.4358, 80], - [0.4016, 0.4288, 90], - [0.4033, 0.4319, 90], - [0.4081, 0.4402, 90], - [0.4158, 0.4531, 90], - [0.4308, 0.4756, 90], - [0.4458, 0.4935, 90], - [0.4552, 0.5011, 90], - [0.4658, 0.5049, 90], - [0.4854, 0.4999, 90], - [0.5081, 0.4842, 90], - [0.5228, 0.4717, 90], - [0.5343, 0.4614, 90], - [0.5304, 0.4565, 90], - [0.5158, 0.4381, 90], - [0.4987, 0.4173, 90], - [0.4827, 0.3990, 90], - [0.4656, 0.3859, 90], - [0.4562, 0.3900, 90], - [0.4420, 0.3999, 90], - [0.4275, 0.4103, 90], - [0.4079, 0.4244, 90], - [0.4024, 0.4283, 90], - [0.4250, 0.4183, 95], - [0.4276, 0.4223, 95], - [0.4351, 0.4339, 95], - [0.4447, 0.4476, 95], - [0.4550, 0.4607, 95], - [0.4660, 0.4728, 95], - [0.4787, 0.4823, 95], - [0.4921, 0.4849, 95], - [0.5032, 0.4816, 95], - [0.5189, 0.4719, 95], - [0.5151, 0.4667, 95], - [0.4901, 0.4334, 95], - [0.4740, 0.4131, 95], - [0.4588, 0.3975, 95], - [0.4504, 0.3999, 95], - [0.4392, 0.4080, 95], - [0.4294, 0.4151, 95], - [0.4254, 0.4180, 95], -]) +OPTIMAL_COLOUR_STIMULI_A: NDArray = np.array( + [ + [0.1120, 0.1985, 10], + [0.0859, 0.2957, 10], + [0.0549, 0.4593, 10], + [0.0433, 0.5548, 10], + [0.0386, 0.6764, 10], + [0.0441, 0.7368, 10], + [0.0578, 0.7834, 10], + [0.0786, 0.8102, 10], + [0.1030, 0.8188, 10], + [0.1276, 0.8151, 10], + [0.1510, 0.8054, 10], + [0.7188, 0.2812, 10], + [0.7112, 0.2773, 10], + [0.6506, 0.2469, 10], + [0.6015, 0.2228, 10], + [0.5604, 0.2032, 10], + [0.5179, 0.1839, 10], + [0.4590, 0.1606, 10], + [0.4302, 0.1526, 10], + [0.3946, 0.1488, 10], + [0.3514, 0.1519, 10], + [0.2949, 0.1610, 10], + [0.2452, 0.1706, 10], + [0.2009, 0.1797, 10], + [0.1197, 0.3185, 20], + [0.0977, 0.4993, 20], + [0.0929, 0.6609, 20], + [0.1073, 0.7534, 20], + [0.1187, 0.7744, 20], + [0.1335, 0.7863, 20], + [0.1505, 0.7896, 20], + [0.1683, 0.7863, 20], + [0.2028, 0.7690, 20], + [0.3641, 0.6326, 20], + [0.4206, 0.5776, 20], + [0.7008, 0.2991, 20], + [0.6726, 0.2834, 20], + [0.6350, 0.2629, 20], + [0.6020, 0.2454, 20], + [0.5601, 0.2246, 20], + [0.5005, 0.2027, 20], + [0.4823, 0.2013, 20], + [0.4532, 0.2053, 20], + [0.4281, 0.2118, 20], + [0.3651, 0.2320, 20], + [0.3070, 0.2521, 20], + [0.2500, 0.2721, 20], + [0.1828, 0.2960, 20], + [0.1442, 0.3923, 30], + [0.1407, 0.4547, 30], + [0.1393, 0.4995, 30], + [0.1390, 0.5533, 30], + [0.1402, 0.6008, 30], + [0.1439, 0.6546, 30], + [0.1535, 0.7106, 30], + [0.1667, 0.7410, 30], + [0.1763, 0.7503, 30], + [0.2002, 0.7548, 30], + [0.2403, 0.7366, 30], + [0.6800, 0.3198, 30], + [0.6759, 0.3173, 30], + [0.6488, 0.3006, 30], + [0.6208, 0.2837, 30], + [0.5863, 0.2637, 30], + [0.5606, 0.2500, 30], + [0.5382, 0.2402, 30], + [0.5168, 0.2358, 30], + [0.4791, 0.2435, 30], + [0.4295, 0.2636, 30], + [0.3905, 0.2807, 30], + [0.3290, 0.3083, 30], + [0.2202, 0.3576, 30], + [0.1769, 0.4360, 40], + [0.1800, 0.5225, 40], + [0.1881, 0.6104, 40], + [0.1958, 0.6562, 40], + [0.2019, 0.6791, 40], + [0.2106, 0.6997, 40], + [0.2314, 0.7173, 40], + [0.2405, 0.7178, 40], + [0.2607, 0.7118, 40], + [0.3023, 0.6839, 40], + [0.5021, 0.4968, 40], + [0.6570, 0.3427, 40], + [0.2151, 0.4588, 50], + [0.2202, 0.5035, 50], + [0.2303, 0.5698, 50], + [0.2392, 0.6119, 50], + [0.2507, 0.6483, 50], + [0.2574, 0.6615, 50], + [0.2660, 0.6720, 50], + [0.2842, 0.6781, 50], + [0.2994, 0.6742, 50], + [0.3244, 0.6595, 50], + [0.5025, 0.4961, 50], + [0.6332, 0.3664, 50], + [0.6296, 0.3635, 50], + [0.6054, 0.3447, 50], + [0.5803, 0.3257, 50], + [0.5600, 0.3111, 50], + [0.5350, 0.2957, 50], + [0.5207, 0.2913, 50], + [0.4996, 0.2960, 50], + [0.4503, 0.3221, 50], + [0.4000, 0.3511, 50], + [0.3587, 0.3751, 50], + [0.3105, 0.4031, 50], + [0.2546, 0.4358, 50], + [0.2576, 0.4662, 60], + [0.2656, 0.5051, 60], + [0.2702, 0.5247, 60], + [0.2806, 0.5633, 60], + [0.2898, 0.5910, 60], + [0.3000, 0.6140, 60], + [0.3192, 0.6345, 60], + [0.3400, 0.6339, 60], + [0.3797, 0.6090, 60], + [0.4252, 0.5692, 60], + [0.4923, 0.5056, 60], + [0.5995, 0.3999, 60], + [0.6065, 0.3871, 60], + [0.5751, 0.3606, 60], + [0.5508, 0.3403, 60], + [0.5252, 0.3217, 60], + [0.5139, 0.3168, 60], + [0.5005, 0.3178, 60], + [0.4761, 0.3301, 60], + [0.4496, 0.3461, 60], + [0.4103, 0.3705, 60], + [0.3375, 0.4161, 60], + [0.3124, 0.4318, 60], + [0.2634, 0.4626, 60], + [0.3038, 0.4616, 70], + [0.3105, 0.4832, 70], + [0.3202, 0.5119, 70], + [0.3255, 0.5258, 70], + [0.3395, 0.5580, 70], + [0.3537, 0.5806, 70], + [0.3810, 0.5916, 70], + [0.3900, 0.5886, 70], + [0.3999, 0.5835, 70], + [0.5005, 0.4967, 70], + [0.5690, 0.4300, 70], + [0.5849, 0.4143, 70], + [0.5812, 0.4106, 70], + [0.5776, 0.4070, 70], + [0.5706, 0.4001, 70], + [0.5351, 0.3661, 70], + [0.5202, 0.3530, 70], + [0.5004, 0.3407, 70], + [0.4904, 0.3412, 70], + [0.4794, 0.3466, 70], + [0.4703, 0.3519, 70], + [0.3706, 0.4174, 70], + [0.3501, 0.4310, 70], + [0.3219, 0.4497, 70], + [0.3527, 0.4480, 80], + [0.3603, 0.4657, 80], + [0.3803, 0.5061, 80], + [0.4100, 0.5440, 80], + [0.4299, 0.5467, 80], + [0.4402, 0.5426, 80], + [0.4598, 0.5298, 80], + [0.4803, 0.5130, 80], + [0.5000, 0.4954, 80], + [0.5218, 0.4750, 80], + [0.5419, 0.4559, 80], + [0.5603, 0.4380, 80], + [0.5566, 0.4338, 80], + [0.5457, 0.4217, 80], + [0.5190, 0.3928, 80], + [0.5004, 0.3744, 80], + [0.4916, 0.3672, 80], + [0.4799, 0.3636, 80], + [0.4751, 0.3652, 80], + [0.4698, 0.3679, 80], + [0.4560, 0.3767, 80], + [0.4011, 0.4146, 80], + [0.3805, 0.4289, 80], + [0.3704, 0.4358, 80], + [0.4016, 0.4288, 90], + [0.4033, 0.4319, 90], + [0.4081, 0.4402, 90], + [0.4158, 0.4531, 90], + [0.4308, 0.4756, 90], + [0.4458, 0.4935, 90], + [0.4552, 0.5011, 90], + [0.4658, 0.5049, 90], + [0.4854, 0.4999, 90], + [0.5081, 0.4842, 90], + [0.5228, 0.4717, 90], + [0.5343, 0.4614, 90], + [0.5304, 0.4565, 90], + [0.5158, 0.4381, 90], + [0.4987, 0.4173, 90], + [0.4827, 0.3990, 90], + [0.4656, 0.3859, 90], + [0.4562, 0.3900, 90], + [0.4420, 0.3999, 90], + [0.4275, 0.4103, 90], + [0.4079, 0.4244, 90], + [0.4024, 0.4283, 90], + [0.4250, 0.4183, 95], + [0.4276, 0.4223, 95], + [0.4351, 0.4339, 95], + [0.4447, 0.4476, 95], + [0.4550, 0.4607, 95], + [0.4660, 0.4728, 95], + [0.4787, 0.4823, 95], + [0.4921, 0.4849, 95], + [0.5032, 0.4816, 95], + [0.5189, 0.4719, 95], + [0.5151, 0.4667, 95], + [0.4901, 0.4334, 95], + [0.4740, 0.4131, 95], + [0.4588, 0.3975, 95], + [0.4504, 0.3999, 95], + [0.4392, 0.4080, 95], + [0.4294, 0.4151, 95], + [0.4254, 0.4180, 95], + ] +) """ *CIE Standard Illuminant A* *Optimal Colour Stimuli*. -OPTIMAL_COLOUR_STIMULI_A : ndarray - References ---------- :cite:`Wyszecki2000bb` """ -OPTIMAL_COLOUR_STIMULI_C = np.array([ - [0.1363, 0.0692, 10], - [0.1308, 0.0792, 10], - [0.0808, 0.2132, 10], - [0.0371, 0.4135, 10], - [0.0251, 0.5007, 10], - [0.0181, 0.5893, 10], - [0.0181, 0.6718, 10], - [0.0276, 0.7416, 10], - [0.0434, 0.7890, 10], - [0.0687, 0.8178, 10], - [0.0996, 0.8252, 10], - [0.7040, 0.2946, 10], - [0.5126, 0.1913, 10], - [0.3424, 0.1028, 10], - [0.2813, 0.0771, 10], - [0.2518, 0.0693, 10], - [0.2378, 0.0674, 10], - [0.2230, 0.0663, 10], - [0.1868, 0.0664, 10], - [0.1628, 0.0676, 10], - [0.1289, 0.1268, 20], - [0.1230, 0.1438, 20], - [0.1027, 0.2152, 20], - [0.0762, 0.3420, 20], - [0.0572, 0.4775, 20], - [0.0500, 0.6250, 20], - [0.0637, 0.7410, 20], - [0.0787, 0.7747, 20], - [0.0992, 0.7975, 20], - [0.1239, 0.8055, 20], - [0.1518, 0.7983, 20], - [0.6717, 0.3273, 20], - [0.5542, 0.2513, 20], - [0.4077, 0.1603, 20], - [0.3463, 0.1263, 20], - [0.3195, 0.1150, 20], - [0.3075, 0.1122, 20], - [0.2968, 0.1104, 20], - [0.2586, 0.1104, 20], - [0.1918, 0.1182, 20], - [0.1302, 0.1764, 30], - [0.1255, 0.1980, 30], - [0.1092, 0.2845, 30], - [0.0909, 0.4178, 30], - [0.0855, 0.5500, 30], - [0.0836, 0.6110, 30], - [0.0911, 0.6700, 30], - [0.0975, 0.7140, 30], - [0.1100, 0.7487, 30], - [0.1294, 0.7700, 30], - [0.1462, 0.7806, 30], - [0.1698, 0.7793, 30], - [0.1957, 0.7696, 30], - [0.6390, 0.3613, 30], - [0.5530, 0.2950, 30], - [0.4300, 0.2040, 30], - [0.3733, 0.1658, 30], - [0.3485, 0.1528, 30], - [0.3300, 0.1462, 30], - [0.3140, 0.1443, 30], - [0.3045, 0.1447, 30], - [0.2643, 0.1503, 30], - [0.1383, 0.2180, 40], - [0.1350, 0.2425, 40], - [0.1246, 0.3363, 40], - [0.1179, 0.4720, 40], - [0.1343, 0.6800, 40], - [0.1596, 0.7377, 40], - [0.1766, 0.7470, 40], - [0.1952, 0.7500, 40], - [0.2437, 0.7305, 40], - [0.2964, 0.6903, 40], - [0.3200, 0.6357, 40], - [0.6065, 0.3925, 40], - [0.5395, 0.3320, 40], - [0.4347, 0.2410, 40], - [0.3833, 0.2000, 40], - [0.3607, 0.1851, 40], - [0.3527, 0.1807, 40], - [0.3453, 0.1777, 40], - [0.3325, 0.1752, 40], - [0.3260, 0.1750, 40], - [0.3003, 0.1783, 40], - [0.2727, 0.1844, 40], - [0.2276, 0.1955, 40], - [0.1510, 0.2520, 50], - [0.1497, 0.2785, 50], - [0.1462, 0.3736, 50], - [0.1490, 0.5017, 50], - [0.1589, 0.5990, 50], - [0.1677, 0.6411, 50], - [0.1782, 0.6750, 50], - [0.1913, 0.6980, 50], - [0.2222, 0.7185, 50], - [0.2867, 0.6936, 50], - [0.3412, 0.6493, 50], - [0.4066, 0.5890, 50], - [0.5759, 0.4231, 50], - [0.5207, 0.3655, 50], - [0.4304, 0.2737, 50], - [0.3844, 0.2309, 50], - [0.3489, 0.2071, 50], - [0.3347, 0.2026, 50], - [0.3175, 0.2046, 50], - [0.3000, 0.2092, 50], - [0.2746, 0.2162, 50], - [0.2024, 0.2373, 50], - [0.1694, 0.2797, 60], - [0.1698, 0.3065, 60], - [0.1732, 0.3995, 60], - [0.1847, 0.5156, 60], - [0.2011, 0.5982, 60], - [0.2117, 0.6316, 60], - [0.2238, 0.6567, 60], - [0.2525, 0.6823, 60], - [0.2694, 0.6840, 60], - [0.3344, 0.6502, 60], - [0.3908, 0.6016, 60], - [0.4605, 0.5364, 60], - [0.5470, 0.4514, 60], - [0.5004, 0.3963, 60], - [0.4217, 0.3042, 60], - [0.3803, 0.2593, 60], - [0.3500, 0.2330, 60], - [0.3376, 0.2284, 60], - [0.3238, 0.2294, 60], - [0.3132, 0.2322, 60], - [0.2593, 0.2497, 60], - [0.1932, 0.3005, 70], - [0.1953, 0.3263, 70], - [0.2064, 0.4136, 70], - [0.2261, 0.5163, 70], - [0.2495, 0.5835, 70], - [0.2733, 0.6282, 70], - [0.3063, 0.6432, 70], - [0.3213, 0.6415, 70], - [0.3408, 0.6316, 70], - [0.3876, 0.5999, 70], - [0.5187, 0.4780, 70], - [0.4795, 0.4243, 70], - [0.4107, 0.3319, 70], - [0.3566, 0.2675, 70], - [0.3460, 0.2578, 70], - [0.3356, 0.2525, 70], - [0.3185, 0.2544, 70], - [0.2875, 0.2651, 70], - [0.2290, 0.2868, 70], - [0.2236, 0.3120, 80], - [0.2282, 0.3382, 80], - [0.2465, 0.4183, 80], - [0.2743, 0.5056, 80], - [0.2991, 0.5591, 80], - [0.3136, 0.5784, 80], - [0.3284, 0.5913, 80], - [0.3570, 0.5932, 80], - [0.3785, 0.5912, 80], - [0.4493, 0.5433, 80], - [0.4901, 0.5038, 80], - [0.4562, 0.4505, 80], - [0.3966, 0.3584, 80], - [0.3631, 0.3103, 80], - [0.3391, 0.2815, 80], - [0.3304, 0.2754, 80], - [0.3229, 0.2756, 80], - [0.3035, 0.2802, 80], - [0.2747, 0.2926, 80], - [0.2276, 0.3119, 80], - [0.2631, 0.3192, 90], - [0.2697, 0.3410, 90], - [0.2956, 0.4111, 90], - [0.3302, 0.4827, 90], - [0.3590, 0.5232, 90], - [0.3742, 0.5364, 90], - [0.3896, 0.5438, 90], - [0.4020, 0.5493, 90], - [0.4221, 0.5430, 90], - [0.4397, 0.5350, 90], - [0.4555, 0.5235, 90], - [0.4295, 0.4741, 90], - [0.3330, 0.3080, 90], - [0.3230, 0.2975, 90], - [0.3180, 0.2958, 90], - [0.2980, 0.3030, 90], - [0.2813, 0.3106, 90], - [0.2857, 0.3185, 95], - [0.2943, 0.3395, 95], - [0.3226, 0.4055, 95], - [0.3608, 0.4679, 95], - [0.3907, 0.5025, 95], - [0.4055, 0.5126, 95], - [0.4209, 0.5180, 95], - [0.4300, 0.5195, 95], - [0.4070, 0.4720, 95], - [0.3630, 0.3855, 95], - [0.3270, 0.3172, 95], - [0.3160, 0.3069, 95], - [0.3053, 0.3096, 95], -]) +OPTIMAL_COLOUR_STIMULI_C: NDArray = np.array( + [ + [0.1363, 0.0692, 10], + [0.1308, 0.0792, 10], + [0.0808, 0.2132, 10], + [0.0371, 0.4135, 10], + [0.0251, 0.5007, 10], + [0.0181, 0.5893, 10], + [0.0181, 0.6718, 10], + [0.0276, 0.7416, 10], + [0.0434, 0.7890, 10], + [0.0687, 0.8178, 10], + [0.0996, 0.8252, 10], + [0.7040, 0.2946, 10], + [0.5126, 0.1913, 10], + [0.3424, 0.1028, 10], + [0.2813, 0.0771, 10], + [0.2518, 0.0693, 10], + [0.2378, 0.0674, 10], + [0.2230, 0.0663, 10], + [0.1868, 0.0664, 10], + [0.1628, 0.0676, 10], + [0.1289, 0.1268, 20], + [0.1230, 0.1438, 20], + [0.1027, 0.2152, 20], + [0.0762, 0.3420, 20], + [0.0572, 0.4775, 20], + [0.0500, 0.6250, 20], + [0.0637, 0.7410, 20], + [0.0787, 0.7747, 20], + [0.0992, 0.7975, 20], + [0.1239, 0.8055, 20], + [0.1518, 0.7983, 20], + [0.6717, 0.3273, 20], + [0.5542, 0.2513, 20], + [0.4077, 0.1603, 20], + [0.3463, 0.1263, 20], + [0.3195, 0.1150, 20], + [0.3075, 0.1122, 20], + [0.2968, 0.1104, 20], + [0.2586, 0.1104, 20], + [0.1918, 0.1182, 20], + [0.1302, 0.1764, 30], + [0.1255, 0.1980, 30], + [0.1092, 0.2845, 30], + [0.0909, 0.4178, 30], + [0.0855, 0.5500, 30], + [0.0836, 0.6110, 30], + [0.0911, 0.6700, 30], + [0.0975, 0.7140, 30], + [0.1100, 0.7487, 30], + [0.1294, 0.7700, 30], + [0.1462, 0.7806, 30], + [0.1698, 0.7793, 30], + [0.1957, 0.7696, 30], + [0.6390, 0.3613, 30], + [0.5530, 0.2950, 30], + [0.4300, 0.2040, 30], + [0.3733, 0.1658, 30], + [0.3485, 0.1528, 30], + [0.3300, 0.1462, 30], + [0.3140, 0.1443, 30], + [0.3045, 0.1447, 30], + [0.2643, 0.1503, 30], + [0.1383, 0.2180, 40], + [0.1350, 0.2425, 40], + [0.1246, 0.3363, 40], + [0.1179, 0.4720, 40], + [0.1343, 0.6800, 40], + [0.1596, 0.7377, 40], + [0.1766, 0.7470, 40], + [0.1952, 0.7500, 40], + [0.2437, 0.7305, 40], + [0.2964, 0.6903, 40], + [0.3200, 0.6357, 40], + [0.6065, 0.3925, 40], + [0.5395, 0.3320, 40], + [0.4347, 0.2410, 40], + [0.3833, 0.2000, 40], + [0.3607, 0.1851, 40], + [0.3527, 0.1807, 40], + [0.3453, 0.1777, 40], + [0.3325, 0.1752, 40], + [0.3260, 0.1750, 40], + [0.3003, 0.1783, 40], + [0.2727, 0.1844, 40], + [0.2276, 0.1955, 40], + [0.1510, 0.2520, 50], + [0.1497, 0.2785, 50], + [0.1462, 0.3736, 50], + [0.1490, 0.5017, 50], + [0.1589, 0.5990, 50], + [0.1677, 0.6411, 50], + [0.1782, 0.6750, 50], + [0.1913, 0.6980, 50], + [0.2222, 0.7185, 50], + [0.2867, 0.6936, 50], + [0.3412, 0.6493, 50], + [0.4066, 0.5890, 50], + [0.5759, 0.4231, 50], + [0.5207, 0.3655, 50], + [0.4304, 0.2737, 50], + [0.3844, 0.2309, 50], + [0.3489, 0.2071, 50], + [0.3347, 0.2026, 50], + [0.3175, 0.2046, 50], + [0.3000, 0.2092, 50], + [0.2746, 0.2162, 50], + [0.2024, 0.2373, 50], + [0.1694, 0.2797, 60], + [0.1698, 0.3065, 60], + [0.1732, 0.3995, 60], + [0.1847, 0.5156, 60], + [0.2011, 0.5982, 60], + [0.2117, 0.6316, 60], + [0.2238, 0.6567, 60], + [0.2525, 0.6823, 60], + [0.2694, 0.6840, 60], + [0.3344, 0.6502, 60], + [0.3908, 0.6016, 60], + [0.4605, 0.5364, 60], + [0.5470, 0.4514, 60], + [0.5004, 0.3963, 60], + [0.4217, 0.3042, 60], + [0.3803, 0.2593, 60], + [0.3500, 0.2330, 60], + [0.3376, 0.2284, 60], + [0.3238, 0.2294, 60], + [0.3132, 0.2322, 60], + [0.2593, 0.2497, 60], + [0.1932, 0.3005, 70], + [0.1953, 0.3263, 70], + [0.2064, 0.4136, 70], + [0.2261, 0.5163, 70], + [0.2495, 0.5835, 70], + [0.2733, 0.6282, 70], + [0.3063, 0.6432, 70], + [0.3213, 0.6415, 70], + [0.3408, 0.6316, 70], + [0.3876, 0.5999, 70], + [0.5187, 0.4780, 70], + [0.4795, 0.4243, 70], + [0.4107, 0.3319, 70], + [0.3566, 0.2675, 70], + [0.3460, 0.2578, 70], + [0.3356, 0.2525, 70], + [0.3185, 0.2544, 70], + [0.2875, 0.2651, 70], + [0.2290, 0.2868, 70], + [0.2236, 0.3120, 80], + [0.2282, 0.3382, 80], + [0.2465, 0.4183, 80], + [0.2743, 0.5056, 80], + [0.2991, 0.5591, 80], + [0.3136, 0.5784, 80], + [0.3284, 0.5913, 80], + [0.3570, 0.5932, 80], + [0.3785, 0.5912, 80], + [0.4493, 0.5433, 80], + [0.4901, 0.5038, 80], + [0.4562, 0.4505, 80], + [0.3966, 0.3584, 80], + [0.3631, 0.3103, 80], + [0.3391, 0.2815, 80], + [0.3304, 0.2754, 80], + [0.3229, 0.2756, 80], + [0.3035, 0.2802, 80], + [0.2747, 0.2926, 80], + [0.2276, 0.3119, 80], + [0.2631, 0.3192, 90], + [0.2697, 0.3410, 90], + [0.2956, 0.4111, 90], + [0.3302, 0.4827, 90], + [0.3590, 0.5232, 90], + [0.3742, 0.5364, 90], + [0.3896, 0.5438, 90], + [0.4020, 0.5493, 90], + [0.4221, 0.5430, 90], + [0.4397, 0.5350, 90], + [0.4555, 0.5235, 90], + [0.4295, 0.4741, 90], + [0.3330, 0.3080, 90], + [0.3230, 0.2975, 90], + [0.3180, 0.2958, 90], + [0.2980, 0.3030, 90], + [0.2813, 0.3106, 90], + [0.2857, 0.3185, 95], + [0.2943, 0.3395, 95], + [0.3226, 0.4055, 95], + [0.3608, 0.4679, 95], + [0.3907, 0.5025, 95], + [0.4055, 0.5126, 95], + [0.4209, 0.5180, 95], + [0.4300, 0.5195, 95], + [0.4070, 0.4720, 95], + [0.3630, 0.3855, 95], + [0.3270, 0.3172, 95], + [0.3160, 0.3069, 95], + [0.3053, 0.3096, 95], + ] +) """ *CIE Illuminant C* *Optimal Colour Stimuli*. -OPTIMAL_COLOUR_STIMULI_C : ndarray - References ---------- :cite:`MacAdam1935a` """ -OPTIMAL_COLOUR_STIMULI_D65 = np.array([ - [0.1346, 0.0747, 10], - [0.0990, 0.1607, 10], - [0.0751, 0.2403, 10], - [0.0391, 0.4074, 10], - [0.0211, 0.5490, 10], - [0.0177, 0.6693, 10], - [0.0344, 0.7732, 10], - [0.0516, 0.8055, 10], - [0.0727, 0.8223, 10], - [0.0959, 0.8261, 10], - [0.1188, 0.8213, 10], - [0.7035, 0.2965, 10], - [0.6832, 0.2853, 10], - [0.6470, 0.2653, 10], - [0.5517, 0.2132, 10], - [0.5309, 0.2019, 10], - [0.4346, 0.1504, 10], - [0.3999, 0.1324, 10], - [0.3549, 0.1101, 10], - [0.3207, 0.0945, 10], - [0.2989, 0.0857, 10], - [0.2852, 0.0808, 10], - [0.2660, 0.0755, 10], - [0.2186, 0.0707, 10], - [0.1268, 0.1365, 20], - [0.1081, 0.1984, 20], - [0.0894, 0.2766, 20], - [0.0660, 0.4074, 20], - [0.0549, 0.4971, 20], - [0.0479, 0.6227, 20], - [0.0565, 0.7312, 20], - [0.0927, 0.8005, 20], - [0.1289, 0.8078, 20], - [0.1479, 0.8026, 20], - [0.1664, 0.7941, 20], - [0.6708, 0.3289, 20], - [0.6591, 0.3213, 20], - [0.5988, 0.2820, 20], - [0.5514, 0.2513, 20], - [0.5018, 0.2197, 20], - [0.4502, 0.1874, 20], - [0.4045, 0.1601, 20], - [0.3762, 0.1443, 20], - [0.3440, 0.1284, 20], - [0.3185, 0.1196, 20], - [0.2935, 0.1164, 20], - [0.2528, 0.1189, 20], - [0.2205, 0.1229, 20], - [0.1282, 0.1889, 30], - [0.1067, 0.3003, 30], - [0.0990, 0.3535, 30], - [0.0929, 0.4041, 30], - [0.0846, 0.5028, 30], - [0.0819, 0.6020, 30], - [0.0836, 0.6491, 30], - [0.1004, 0.7433, 30], - [0.1481, 0.7857, 30], - [0.1799, 0.7787, 30], - [0.2119, 0.7609, 30], - [0.6368, 0.3628, 30], - [0.6281, 0.3561, 30], - [0.5682, 0.3098, 30], - [0.5271, 0.2784, 30], - [0.4977, 0.2562, 30], - [0.4504, 0.2212, 30], - [0.4219, 0.2008, 30], - [0.3999, 0.1859, 30], - [0.3801, 0.1732, 30], - [0.3491, 0.1574, 30], - [0.3350, 0.1536, 30], - [0.3197, 0.1526, 30], - [0.2021, 0.1732, 30], - [0.1360, 0.2324, 40], - [0.1266, 0.3030, 40], - [0.1219, 0.3504, 40], - [0.1183, 0.3985, 40], - [0.1155, 0.4509, 40], - [0.1141, 0.5055, 40], - [0.1312, 0.7047, 40], - [0.1516, 0.7454, 40], - [0.1853, 0.7587, 40], - [0.2129, 0.7510, 40], - [0.2415, 0.7344, 40], - [0.6041, 0.3954, 40], - [0.5969, 0.3888, 40], - [0.5524, 0.3484, 40], - [0.5257, 0.3244, 40], - [0.4980, 0.2997, 40], - [0.4598, 0.2661, 40], - [0.3696, 0.1949, 40], - [0.3603, 0.1898, 40], - [0.3501, 0.1859, 40], - [0.3375, 0.1841, 40], - [0.2581, 0.2001, 40], - [0.2220, 0.2095, 40], - [0.1771, 0.2214, 40], - [0.1491, 0.2679, 50], - [0.1441, 0.3511, 50], - [0.1429, 0.4025, 50], - [0.1429, 0.4479, 50], - [0.1472, 0.5522, 50], - [0.1548, 0.6201, 50], - [0.1621, 0.6570, 50], - [0.1790, 0.7035, 50], - [0.1929, 0.7201, 50], - [0.2114, 0.7277, 50], - [0.2991, 0.6851, 50], - [0.5731, 0.4262, 50], - [0.5668, 0.4195, 50], - [0.5492, 0.4009, 50], - [0.4795, 0.3281, 50], - [0.4514, 0.2994, 50], - [0.4113, 0.2600, 50], - [0.3897, 0.2401, 50], - [0.3509, 0.2139, 50], - [0.3391, 0.2126, 50], - [0.3211, 0.2155, 50], - [0.3042, 0.2200, 50], - [0.2466, 0.2374, 50], - [0.2041, 0.2507, 50], - [0.1674, 0.2959, 60], - [0.1677, 0.3520, 60], - [0.1700, 0.4130, 60], - [0.1749, 0.4782, 60], - [0.1801, 0.5257, 60], - [0.1873, 0.5730, 60], - [0.1994, 0.6257, 60], - [0.2088, 0.6523, 60], - [0.2506, 0.6927, 60], - [0.2703, 0.6900, 60], - [0.2930, 0.6798, 60], - [0.5435, 0.4552, 60], - [0.5379, 0.4483, 60], - [0.4775, 0.3751, 60], - [0.4522, 0.3450, 60], - [0.4138, 0.3005, 60], - [0.3611, 0.2472, 60], - [0.3497, 0.2405, 60], - [0.3395, 0.2388, 60], - [0.3195, 0.2429, 60], - [0.2963, 0.2505, 60], - [0.2701, 0.2595, 60], - [0.2270, 0.2747, 60], - [0.2037, 0.2830, 60], - [0.1916, 0.3164, 70], - [0.1958, 0.3656, 70], - [0.2003, 0.4069, 70], - [0.2065, 0.4485, 70], - [0.2150, 0.4963, 70], - [0.2221, 0.5295, 70], - [0.2298, 0.5597, 70], - [0.2402, 0.5918, 70], - [0.2550, 0.6237, 70], - [0.2784, 0.6484, 70], - [0.3000, 0.6521, 70], - [0.5148, 0.4825, 70], - [0.5097, 0.4753, 70], - [0.4776, 0.4304, 70], - [0.4508, 0.3933, 70], - [0.4192, 0.3505, 70], - [0.4005, 0.3259, 70], - [0.3706, 0.2890, 70], - [0.3663, 0.2842, 70], - [0.3517, 0.2699, 70], - [0.3364, 0.2634, 70], - [0.3194, 0.2671, 70], - [0.3007, 0.2739, 70], - [0.2664, 0.2872, 70], - [0.2232, 0.3290, 80], - [0.2404, 0.4145, 80], - [0.2496, 0.4504, 80], - [0.2583, 0.4801, 80], - [0.2760, 0.5308, 80], - [0.3023, 0.5809, 80], - [0.3092, 0.5892, 80], - [0.3318, 0.6041, 80], - [0.3515, 0.6048, 80], - [0.3679, 0.5995, 80], - [0.4080, 0.5750, 80], - [0.4858, 0.5081, 80], - [0.4811, 0.5005, 80], - [0.4634, 0.4719, 80], - [0.4514, 0.4526, 80], - [0.4299, 0.4158, 80], - [0.4001, 0.3720, 80], - [0.3732, 0.3319, 80], - [0.3603, 0.3139, 80], - [0.3500, 0.3009, 80], - [0.3307, 0.2866, 80], - [0.2730, 0.3080, 80], - [0.2519, 0.3169, 80], - [0.2400, 0.3219, 80], - [0.2639, 0.3331, 90], - [0.2801, 0.3832, 90], - [0.2864, 0.4008, 90], - [0.3059, 0.4486, 90], - [0.3182, 0.4746, 90], - [0.3317, 0.4994, 90], - [0.3513, 0.5278, 90], - [0.3657, 0.5421, 90], - [0.3946, 0.5537, 90], - [0.4126, 0.5510, 90], - [0.4354, 0.5406, 90], - [0.4530, 0.5293, 90], - [0.4486, 0.5210, 90], - [0.4444, 0.5131, 90], - [0.4325, 0.4906, 90], - [0.4215, 0.4700, 90], - [0.3990, 0.4284, 90], - [0.3749, 0.3849, 90], - [0.3504, 0.3431, 90], - [0.3349, 0.3196, 90], - [0.3217, 0.3084, 90], - [0.3099, 0.3124, 90], - [0.2852, 0.3235, 90], - [0.2711, 0.3299, 90], - [0.2875, 0.3320, 95], - [0.2949, 0.3513, 95], - [0.3067, 0.3800, 95], - [0.3230, 0.4150, 95], - [0.3368, 0.4415, 95], - [0.3508, 0.4654, 95], - [0.3644, 0.4856, 95], - [0.3765, 0.5007, 95], - [0.3887, 0.5126, 95], - [0.4003, 0.5206, 95], - [0.4108, 0.5251, 95], - [0.4281, 0.5268, 95], - [0.4204, 0.5109, 95], - [0.4132, 0.4959, 95], - [0.4031, 0.4751, 95], - [0.3697, 0.4076, 95], - [0.3498, 0.3692, 95], - [0.3401, 0.3513, 95], - [0.3295, 0.3331, 95], - [0.3167, 0.3189, 95], - [0.3148, 0.3195, 95], - [0.3103, 0.3214, 95], - [0.3006, 0.3259, 95], - [0.2900, 0.3308, 95], -]) +OPTIMAL_COLOUR_STIMULI_D65: NDArray = np.array( + [ + [0.1346, 0.0747, 10], + [0.0990, 0.1607, 10], + [0.0751, 0.2403, 10], + [0.0391, 0.4074, 10], + [0.0211, 0.5490, 10], + [0.0177, 0.6693, 10], + [0.0344, 0.7732, 10], + [0.0516, 0.8055, 10], + [0.0727, 0.8223, 10], + [0.0959, 0.8261, 10], + [0.1188, 0.8213, 10], + [0.7035, 0.2965, 10], + [0.6832, 0.2853, 10], + [0.6470, 0.2653, 10], + [0.5517, 0.2132, 10], + [0.5309, 0.2019, 10], + [0.4346, 0.1504, 10], + [0.3999, 0.1324, 10], + [0.3549, 0.1101, 10], + [0.3207, 0.0945, 10], + [0.2989, 0.0857, 10], + [0.2852, 0.0808, 10], + [0.2660, 0.0755, 10], + [0.2186, 0.0707, 10], + [0.1268, 0.1365, 20], + [0.1081, 0.1984, 20], + [0.0894, 0.2766, 20], + [0.0660, 0.4074, 20], + [0.0549, 0.4971, 20], + [0.0479, 0.6227, 20], + [0.0565, 0.7312, 20], + [0.0927, 0.8005, 20], + [0.1289, 0.8078, 20], + [0.1479, 0.8026, 20], + [0.1664, 0.7941, 20], + [0.6708, 0.3289, 20], + [0.6591, 0.3213, 20], + [0.5988, 0.2820, 20], + [0.5514, 0.2513, 20], + [0.5018, 0.2197, 20], + [0.4502, 0.1874, 20], + [0.4045, 0.1601, 20], + [0.3762, 0.1443, 20], + [0.3440, 0.1284, 20], + [0.3185, 0.1196, 20], + [0.2935, 0.1164, 20], + [0.2528, 0.1189, 20], + [0.2205, 0.1229, 20], + [0.1282, 0.1889, 30], + [0.1067, 0.3003, 30], + [0.0990, 0.3535, 30], + [0.0929, 0.4041, 30], + [0.0846, 0.5028, 30], + [0.0819, 0.6020, 30], + [0.0836, 0.6491, 30], + [0.1004, 0.7433, 30], + [0.1481, 0.7857, 30], + [0.1799, 0.7787, 30], + [0.2119, 0.7609, 30], + [0.6368, 0.3628, 30], + [0.6281, 0.3561, 30], + [0.5682, 0.3098, 30], + [0.5271, 0.2784, 30], + [0.4977, 0.2562, 30], + [0.4504, 0.2212, 30], + [0.4219, 0.2008, 30], + [0.3999, 0.1859, 30], + [0.3801, 0.1732, 30], + [0.3491, 0.1574, 30], + [0.3350, 0.1536, 30], + [0.3197, 0.1526, 30], + [0.2021, 0.1732, 30], + [0.1360, 0.2324, 40], + [0.1266, 0.3030, 40], + [0.1219, 0.3504, 40], + [0.1183, 0.3985, 40], + [0.1155, 0.4509, 40], + [0.1141, 0.5055, 40], + [0.1312, 0.7047, 40], + [0.1516, 0.7454, 40], + [0.1853, 0.7587, 40], + [0.2129, 0.7510, 40], + [0.2415, 0.7344, 40], + [0.6041, 0.3954, 40], + [0.5969, 0.3888, 40], + [0.5524, 0.3484, 40], + [0.5257, 0.3244, 40], + [0.4980, 0.2997, 40], + [0.4598, 0.2661, 40], + [0.3696, 0.1949, 40], + [0.3603, 0.1898, 40], + [0.3501, 0.1859, 40], + [0.3375, 0.1841, 40], + [0.2581, 0.2001, 40], + [0.2220, 0.2095, 40], + [0.1771, 0.2214, 40], + [0.1491, 0.2679, 50], + [0.1441, 0.3511, 50], + [0.1429, 0.4025, 50], + [0.1429, 0.4479, 50], + [0.1472, 0.5522, 50], + [0.1548, 0.6201, 50], + [0.1621, 0.6570, 50], + [0.1790, 0.7035, 50], + [0.1929, 0.7201, 50], + [0.2114, 0.7277, 50], + [0.2991, 0.6851, 50], + [0.5731, 0.4262, 50], + [0.5668, 0.4195, 50], + [0.5492, 0.4009, 50], + [0.4795, 0.3281, 50], + [0.4514, 0.2994, 50], + [0.4113, 0.2600, 50], + [0.3897, 0.2401, 50], + [0.3509, 0.2139, 50], + [0.3391, 0.2126, 50], + [0.3211, 0.2155, 50], + [0.3042, 0.2200, 50], + [0.2466, 0.2374, 50], + [0.2041, 0.2507, 50], + [0.1674, 0.2959, 60], + [0.1677, 0.3520, 60], + [0.1700, 0.4130, 60], + [0.1749, 0.4782, 60], + [0.1801, 0.5257, 60], + [0.1873, 0.5730, 60], + [0.1994, 0.6257, 60], + [0.2088, 0.6523, 60], + [0.2506, 0.6927, 60], + [0.2703, 0.6900, 60], + [0.2930, 0.6798, 60], + [0.5435, 0.4552, 60], + [0.5379, 0.4483, 60], + [0.4775, 0.3751, 60], + [0.4522, 0.3450, 60], + [0.4138, 0.3005, 60], + [0.3611, 0.2472, 60], + [0.3497, 0.2405, 60], + [0.3395, 0.2388, 60], + [0.3195, 0.2429, 60], + [0.2963, 0.2505, 60], + [0.2701, 0.2595, 60], + [0.2270, 0.2747, 60], + [0.2037, 0.2830, 60], + [0.1916, 0.3164, 70], + [0.1958, 0.3656, 70], + [0.2003, 0.4069, 70], + [0.2065, 0.4485, 70], + [0.2150, 0.4963, 70], + [0.2221, 0.5295, 70], + [0.2298, 0.5597, 70], + [0.2402, 0.5918, 70], + [0.2550, 0.6237, 70], + [0.2784, 0.6484, 70], + [0.3000, 0.6521, 70], + [0.5148, 0.4825, 70], + [0.5097, 0.4753, 70], + [0.4776, 0.4304, 70], + [0.4508, 0.3933, 70], + [0.4192, 0.3505, 70], + [0.4005, 0.3259, 70], + [0.3706, 0.2890, 70], + [0.3663, 0.2842, 70], + [0.3517, 0.2699, 70], + [0.3364, 0.2634, 70], + [0.3194, 0.2671, 70], + [0.3007, 0.2739, 70], + [0.2664, 0.2872, 70], + [0.2232, 0.3290, 80], + [0.2404, 0.4145, 80], + [0.2496, 0.4504, 80], + [0.2583, 0.4801, 80], + [0.2760, 0.5308, 80], + [0.3023, 0.5809, 80], + [0.3092, 0.5892, 80], + [0.3318, 0.6041, 80], + [0.3515, 0.6048, 80], + [0.3679, 0.5995, 80], + [0.4080, 0.5750, 80], + [0.4858, 0.5081, 80], + [0.4811, 0.5005, 80], + [0.4634, 0.4719, 80], + [0.4514, 0.4526, 80], + [0.4299, 0.4158, 80], + [0.4001, 0.3720, 80], + [0.3732, 0.3319, 80], + [0.3603, 0.3139, 80], + [0.3500, 0.3009, 80], + [0.3307, 0.2866, 80], + [0.2730, 0.3080, 80], + [0.2519, 0.3169, 80], + [0.2400, 0.3219, 80], + [0.2639, 0.3331, 90], + [0.2801, 0.3832, 90], + [0.2864, 0.4008, 90], + [0.3059, 0.4486, 90], + [0.3182, 0.4746, 90], + [0.3317, 0.4994, 90], + [0.3513, 0.5278, 90], + [0.3657, 0.5421, 90], + [0.3946, 0.5537, 90], + [0.4126, 0.5510, 90], + [0.4354, 0.5406, 90], + [0.4530, 0.5293, 90], + [0.4486, 0.5210, 90], + [0.4444, 0.5131, 90], + [0.4325, 0.4906, 90], + [0.4215, 0.4700, 90], + [0.3990, 0.4284, 90], + [0.3749, 0.3849, 90], + [0.3504, 0.3431, 90], + [0.3349, 0.3196, 90], + [0.3217, 0.3084, 90], + [0.3099, 0.3124, 90], + [0.2852, 0.3235, 90], + [0.2711, 0.3299, 90], + [0.2875, 0.3320, 95], + [0.2949, 0.3513, 95], + [0.3067, 0.3800, 95], + [0.3230, 0.4150, 95], + [0.3368, 0.4415, 95], + [0.3508, 0.4654, 95], + [0.3644, 0.4856, 95], + [0.3765, 0.5007, 95], + [0.3887, 0.5126, 95], + [0.4003, 0.5206, 95], + [0.4108, 0.5251, 95], + [0.4281, 0.5268, 95], + [0.4204, 0.5109, 95], + [0.4132, 0.4959, 95], + [0.4031, 0.4751, 95], + [0.3697, 0.4076, 95], + [0.3498, 0.3692, 95], + [0.3401, 0.3513, 95], + [0.3295, 0.3331, 95], + [0.3167, 0.3189, 95], + [0.3148, 0.3195, 95], + [0.3103, 0.3214, 95], + [0.3006, 0.3259, 95], + [0.2900, 0.3308, 95], + ] +) """ *CIE Standard Illuminant D Series D65* *Optimal Colour Stimuli*. -OPTIMAL_COLOUR_STIMULI_D65 : ndarray - References ---------- :cite:`Wyszecki2000bh` """ -OPTIMAL_COLOUR_STIMULI_ILLUMINANTS = CaseInsensitiveMapping({ - 'A': OPTIMAL_COLOUR_STIMULI_A, - 'C': OPTIMAL_COLOUR_STIMULI_C, - 'D65': OPTIMAL_COLOUR_STIMULI_D65 -}) +OPTIMAL_COLOUR_STIMULI_ILLUMINANTS: CaseInsensitiveMapping = ( + CaseInsensitiveMapping( + { + "A": OPTIMAL_COLOUR_STIMULI_A, + "C": OPTIMAL_COLOUR_STIMULI_C, + "D65": OPTIMAL_COLOUR_STIMULI_D65, + } + ) +) OPTIMAL_COLOUR_STIMULI_ILLUMINANTS.__doc__ = """ Illuminants *Optimal Colour Stimuli*. References ---------- :cite:`Wikipedia2004a` - -OPTIMAL_COLOUR_STIMULI_ILLUMINANTS : CaseInsensitiveMapping - **{'A', 'C', 'D65'}** """ diff --git a/colour/volume/macadam_limits.py b/colour/volume/macadam_limits.py index 457f2df6c3..c7a8d7bf4b 100644 --- a/colour/volume/macadam_limits.py +++ b/colour/volume/macadam_limits.py @@ -1,84 +1,117 @@ -# -*- coding: utf-8 -*- """ Optimal Colour Stimuli - MacAdam Limits ======================================= -Defines objects related to *Optimal Colour Stimuli* computations. +Defines the objects related to *Optimal Colour Stimuli* computations. """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from scipy.spatial import Delaunay +from colour.hints import ( + ArrayLike, + Dict, + Floating, + Literal, + NDArray, + Optional, + Union, +) from colour.models import xyY_to_XYZ from colour.volume import OPTIMAL_COLOUR_STIMULI_ILLUMINANTS +from colour.utilities import CACHE_REGISTRY, validate_method -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['is_within_macadam_limits'] +__all__ = [ + "is_within_macadam_limits", +] -_CACHE_OPTIMAL_COLOUR_STIMULI_XYZ = {} -_CACHE_OPTIMAL_COLOUR_STIMULI_XYZ_TRIANGULATIONS = {} +_CACHE_OPTIMAL_COLOUR_STIMULI_XYZ: Dict = CACHE_REGISTRY.register_cache( + f"{__name__}._CACHE_OPTIMAL_COLOUR_STIMULI_XYZ" +) +_CACHE_OPTIMAL_COLOUR_STIMULI_XYZ_TRIANGULATIONS: Dict = ( + CACHE_REGISTRY.register_cache( + f"{__name__}._CACHE_OPTIMAL_COLOUR_STIMULI_XYZ_TRIANGULATIONS" + ) +) -def _XYZ_optimal_colour_stimuli(illuminant): + +def _XYZ_optimal_colour_stimuli( + illuminant: Union[Literal["A", "C", "D65"], str] = "D65" +) -> NDArray: """ - Returns given illuminant *Optimal Colour Stimuli* in *CIE XYZ* tristimulus + Return given illuminant *Optimal Colour Stimuli* in *CIE XYZ* tristimulus values and caches it if not existing. Parameters ---------- - illuminant : unicode - Illuminant. + illuminant + Illuminant name. Returns ------- - tuple + :class:`numpy.ndarray` Illuminant *Optimal Colour Stimuli*. """ + illuminant = validate_method( + illuminant, + list(OPTIMAL_COLOUR_STIMULI_ILLUMINANTS.keys()), + '"{0}" illuminant is invalid, it must be one of {1}!', + ) + optimal_colour_stimuli = OPTIMAL_COLOUR_STIMULI_ILLUMINANTS.get(illuminant) + if optimal_colour_stimuli is None: - raise KeyError('"{0}" not found in factory ' - '"Optimal Colour Stimuli": "{1}".'.format( - illuminant, - sorted(OPTIMAL_COLOUR_STIMULI_ILLUMINANTS.keys()))) + raise KeyError( + f'"{illuminant}" not found in factory "Optimal Colour Stimuli": ' + f'"{sorted(OPTIMAL_COLOUR_STIMULI_ILLUMINANTS.keys())}".' + ) vertices = _CACHE_OPTIMAL_COLOUR_STIMULI_XYZ.get(illuminant) + if vertices is None: _CACHE_OPTIMAL_COLOUR_STIMULI_XYZ[illuminant] = vertices = ( - xyY_to_XYZ(optimal_colour_stimuli) / 100) + xyY_to_XYZ(optimal_colour_stimuli) / 100 + ) + return vertices -def is_within_macadam_limits(xyY, illuminant, tolerance=None): +def is_within_macadam_limits( + xyY: ArrayLike, + illuminant: Union[Literal["A", "C", "D65"], str] = "D65", + tolerance: Optional[Floating] = None, +) -> NDArray: """ - Returns if given *CIE xyY* colourspace array is within MacAdam limits of - given illuminant. + Return whether given *CIE xyY* colourspace array is within MacAdam limits + of given illuminant. Parameters ---------- - xyY : array_like + xyY *CIE xyY* colourspace array. - illuminant : unicode - Illuminant. - tolerance : numeric, optional + illuminant + Illuminant name. + tolerance Tolerance allowed in the inside-triangle check. Returns ------- - bool - Is within MacAdam limits. + :class:`numpy.ndarray` + Whether given *CIE xyY* colourspace array is within MacAdam limits. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -97,10 +130,13 @@ def is_within_macadam_limits(xyY, illuminant, tolerance=None): optimal_colour_stimuli = _XYZ_optimal_colour_stimuli(illuminant) triangulation = _CACHE_OPTIMAL_COLOUR_STIMULI_XYZ_TRIANGULATIONS.get( - illuminant) + illuminant + ) + if triangulation is None: - _CACHE_OPTIMAL_COLOUR_STIMULI_XYZ_TRIANGULATIONS[illuminant] = \ - triangulation = Delaunay(optimal_colour_stimuli) + _CACHE_OPTIMAL_COLOUR_STIMULI_XYZ_TRIANGULATIONS[ + illuminant + ] = triangulation = Delaunay(optimal_colour_stimuli) simplex = triangulation.find_simplex(xyY_to_XYZ(xyY), tol=tolerance) simplex = np.where(simplex >= 0, True, False) diff --git a/colour/volume/mesh.py b/colour/volume/mesh.py index a61451b8d1..6ab3ccbf55 100644 --- a/colour/volume/mesh.py +++ b/colour/volume/mesh.py @@ -1,44 +1,49 @@ -# -*- coding: utf-8 -*- """ Mesh Volume Computation Helpers =============================== -Defines helpers objects related to volume computations. +Defines the helpers objects related to volume computations. """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np from scipy.spatial import Delaunay -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +from colour.hints import ArrayLike, Floating, NDArray, Optional -__all__ = ['is_within_mesh_volume'] +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 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__ = [ + "is_within_mesh_volume", +] -def is_within_mesh_volume(points, mesh, tolerance=None): + +def is_within_mesh_volume( + points: ArrayLike, mesh: ArrayLike, tolerance: Optional[Floating] = None +) -> NDArray: """ - Returns if given points are within given mesh volume using Delaunay + Return whether given points are within given mesh volume using Delaunay triangulation. Parameters ---------- - points : array_like + points Points to check if they are within ``mesh`` volume. - mesh : array_like + mesh Points of the volume used to generate the Delaunay triangulation. - tolerance : numeric, optional + tolerance Tolerance allowed in the inside-triangle check. Returns ------- - bool - Is within mesh volume. + :class:`numpy.ndarray` + Whether given points are within given mesh volume. Examples -------- diff --git a/colour/volume/pointer_gamut.py b/colour/volume/pointer_gamut.py index e5f5f7743b..1551dafcad 100644 --- a/colour/volume/pointer_gamut.py +++ b/colour/volume/pointer_gamut.py @@ -1,47 +1,55 @@ -# -*- coding: utf-8 -*- """ Pointer's Gamut Volume Computations =================================== -Defines objects related to *Pointer's Gamut* volume computations. +Defines the objects related to *Pointer's Gamut* volume computations. """ -from __future__ import division, unicode_literals +from __future__ import annotations -from colour.models import (Lab_to_XYZ, LCHab_to_Lab, DATA_POINTER_GAMUT_VOLUME, - CCS_ILLUMINANT_POINTER_GAMUT) +from colour.hints import ArrayLike, Floating, NDArray, Optional +from colour.models import ( + Lab_to_XYZ, + LCHab_to_Lab, + DATA_POINTER_GAMUT_VOLUME, + CCS_ILLUMINANT_POINTER_GAMUT, +) from colour.volume import is_within_mesh_volume -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['is_within_pointer_gamut'] +__all__ = [ + "is_within_pointer_gamut", +] -def is_within_pointer_gamut(XYZ, tolerance=None): +def is_within_pointer_gamut( + XYZ: ArrayLike, tolerance: Optional[Floating] = None +) -> NDArray: """ - Returns if given *CIE XYZ* tristimulus values are within Pointer's Gamut - volume. + Return whether given *CIE XYZ* tristimulus values are within Pointer's + Gamut volume. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. - tolerance : numeric, optional + tolerance Tolerance allowed in the inside-triangle check. Returns ------- - bool - Is within Pointer's Gamut. + :class:`numpy.ndarray` + Wether given *CIE XYZ* tristimulus values are within Pointer's Gamut + volume. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -59,6 +67,7 @@ def is_within_pointer_gamut(XYZ, tolerance=None): """ XYZ_p = Lab_to_XYZ( - LCHab_to_Lab(DATA_POINTER_GAMUT_VOLUME), CCS_ILLUMINANT_POINTER_GAMUT) + LCHab_to_Lab(DATA_POINTER_GAMUT_VOLUME), CCS_ILLUMINANT_POINTER_GAMUT + ) return is_within_mesh_volume(XYZ, XYZ_p, tolerance) diff --git a/colour/volume/rgb.py b/colour/volume/rgb.py index 9e331a6e88..9509175785 100644 --- a/colour/volume/rgb.py +++ b/colour/volume/rgb.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ RGB Colourspace Volume Computation ================================== @@ -12,7 +11,7 @@ - :func:`colour.RGB_colourspace_visible_spectrum_coverage_MonteCarlo` """ -from __future__ import division, unicode_literals +from __future__ import annotations import itertools import multiprocessing @@ -21,40 +20,56 @@ from colour.algebra import random_triplet_generator from colour.colorimetry import CCS_ILLUMINANTS from colour.constants import DEFAULT_INT_DTYPE -from colour.models import (Lab_to_XYZ, RGB_to_XYZ, XYZ_to_Lab, XYZ_to_RGB) +from colour.hints import ( + ArrayLike, + Callable, + Floating, + Integer, + Literal, + NDArray, + Tuple, + Union, +) +from colour.models import ( + Lab_to_XYZ, + RGB_Colourspace, + RGB_to_XYZ, + XYZ_to_Lab, + XYZ_to_RGB, +) from colour.volume import is_within_pointer_gamut, is_within_visible_spectrum from colour.utilities import as_float_array, multiprocessing_pool -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'sample_RGB_colourspace_volume_MonteCarlo', 'RGB_colourspace_limits', - 'RGB_colourspace_volume_MonteCarlo', - 'RGB_colourspace_volume_coverage_MonteCarlo', - 'RGB_colourspace_pointer_gamut_coverage_MonteCarlo', - 'RGB_colourspace_visible_spectrum_coverage_MonteCarlo' + "sample_RGB_colourspace_volume_MonteCarlo", + "RGB_colourspace_limits", + "RGB_colourspace_volume_MonteCarlo", + "RGB_colourspace_volume_coverage_MonteCarlo", + "RGB_colourspace_pointer_gamut_coverage_MonteCarlo", + "RGB_colourspace_visible_spectrum_coverage_MonteCarlo", ] -def _wrapper_RGB_colourspace_volume_MonteCarlo(arguments): +def _wrapper_RGB_colourspace_volume_MonteCarlo(arguments: Tuple) -> Integer: """ - Convenient wrapper to be able to call - :func:`colour.volume.rgb.sample_RGB_colourspace_volume_MonteCarlo`: + Call the :func:`colour.volume.rgb.sample_RGB_colourspace_volume_MonteCarlo` definition with multiple arguments. Parameters ---------- - arguments : array_like, optional + arguments Arguments. Returns ------- - integer + :class:`numpy.integer` Inside *RGB* colourspace volume samples count. """ @@ -62,43 +77,58 @@ def _wrapper_RGB_colourspace_volume_MonteCarlo(arguments): def sample_RGB_colourspace_volume_MonteCarlo( - colourspace, - samples=10e6, - limits=np.array([[0, 100], [-150, 150], [-150, 150]]), - illuminant_Lab=CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'][ - 'D65'], - chromatic_adaptation_method='CAT02', - random_generator=random_triplet_generator, - random_state=None): + colourspace: RGB_Colourspace, + samples: Integer = 1000000, + limits: ArrayLike = np.array([[0, 100], [-150, 150], [-150, 150]]), + illuminant_Lab: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], + chromatic_adaptation_transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", + random_generator: Callable = random_triplet_generator, + random_state: np.random.RandomState = None, +) -> Integer: """ - Randomly samples the *CIE L\\*a\\*b\\** colourspace volume and returns the + Randomly sample the *CIE L\\*a\\*b\\** colourspace volume and returns the ratio of samples within the given *RGB* colourspace volume. Parameters ---------- - colourspace : RGB_Colourspace + colourspace *RGB* colourspace to compute the volume of. - samples : numeric, optional + samples Samples count. - limits : array_like, optional + limits *CIE L\\*a\\*b\\** colourspace volume. - illuminant_Lab : array_like, optional + illuminant_Lab *CIE L\\*a\\*b\\** colourspace *illuminant* chromaticity coordinates. - chromatic_adaptation_method : unicode, optional - **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', - 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02 Brill 2008', - 'Bianco 2010', 'Bianco PC 2010'}**, - *Chromatic adaptation* method. - random_generator : generator, optional + chromatic_adaptation_transform + *Chromatic adaptation* transform. + random_generator Random triplet generator providing the random samples within the *CIE L\\*a\\*b\\** colourspace volume. - random_state : RandomState, optional + random_state Mersenne Twister pseudo-random number generator to use in the random number generator. Returns ------- - integer + :class:`numpy.integer` Within *RGB* colourspace volume samples count. Notes @@ -121,41 +151,48 @@ def sample_RGB_colourspace_volume_MonteCarlo( 9... """ - random_state = (random_state - if random_state is not None else np.random.RandomState()) + random_state = ( + random_state if random_state is not None else np.random.RandomState() + ) - Lab = as_float_array(list(random_generator(samples, limits, random_state))) + Lab = random_generator(DEFAULT_INT_DTYPE(samples), limits, random_state) RGB = XYZ_to_RGB( Lab_to_XYZ(Lab, illuminant_Lab), illuminant_Lab, colourspace.whitepoint, colourspace.matrix_XYZ_to_RGB, - chromatic_adaptation_transform=chromatic_adaptation_method) - RGB_w = RGB[np.logical_and( - np.min(RGB, axis=-1) >= 0, - np.max(RGB, axis=-1) <= 1)] + chromatic_adaptation_transform=chromatic_adaptation_transform, + ) + RGB_w = RGB[ + np.logical_and(np.min(RGB, axis=-1) >= 0, np.max(RGB, axis=-1) <= 1) + ] return len(RGB_w) -def RGB_colourspace_limits(colourspace, - illuminant=CCS_ILLUMINANTS[ - 'CIE 1931 2 Degree Standard Observer']['D65']): +def RGB_colourspace_limits(colourspace: RGB_Colourspace) -> NDArray: """ - Computes given *RGB* colourspace volume limits in *CIE L\\*a\\*b\\** + Compute given *RGB* colourspace volume limits in *CIE L\\*a\\*b\\** colourspace. Parameters ---------- - colourspace : RGB_Colourspace + colourspace *RGB* colourspace to compute the volume of. - illuminant : array_like, optional - *CIE L\\*a\\*b\\** colourspace *illuminant* chromaticity coordinates. Returns ------- - ndarray + :class:`numpy.ndarray` *RGB* colourspace volume limits. + Notes + ----- + The limits are computed for the given *RGB* colourspace illuminant. This is + important to account for, if the intent is to compare various *RGB* + colourspaces together. In this instance, they must be chromatically adapted + to the same illuminant before-hand. + See :meth:`colour.RGB_Colourspace.chromatically_adapt` method for more + information. + Examples -------- >>> from colour.models import RGB_COLOURSPACE_sRGB as sRGB @@ -165,13 +202,20 @@ def RGB_colourspace_limits(colourspace, [-107.8503557..., 94.4894974...]]) """ - Lab = [] + Lab_c = [] for combination in list(itertools.product([0, 1], repeat=3)): - Lab.append( + Lab_c.append( XYZ_to_Lab( - RGB_to_XYZ(combination, colourspace.whitepoint, illuminant, - colourspace.matrix_RGB_to_XYZ))) - Lab = np.array(Lab) + RGB_to_XYZ( + combination, + colourspace.whitepoint, + colourspace.whitepoint, + colourspace.matrix_RGB_to_XYZ, + ), + colourspace.whitepoint, + ) + ) + Lab = np.array(Lab_c) limits = [] for i in np.arange(3): @@ -181,43 +225,58 @@ def RGB_colourspace_limits(colourspace, def RGB_colourspace_volume_MonteCarlo( - colourspace, - samples=10e6, - limits=np.array([[0, 100], [-150, 150], [-150, 150]], dtype=np.float), - illuminant_Lab=CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'][ - 'D65'], - chromatic_adaptation_method='CAT02', - random_generator=random_triplet_generator, - random_state=None): + colourspace: RGB_Colourspace, + samples: Integer = 1000000, + limits: ArrayLike = np.array([[0, 100], [-150, 150], [-150, 150]]), + illuminant_Lab: ArrayLike = CCS_ILLUMINANTS[ + "CIE 1931 2 Degree Standard Observer" + ]["D65"], + chromatic_adaptation_transform: Union[ + Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ], + str, + ] = "CAT02", + random_generator: Callable = random_triplet_generator, + random_state: np.random.RandomState = None, +) -> Floating: """ - Performs given *RGB* colourspace volume computation using *Monte Carlo* + Perform given *RGB* colourspace volume computation using *Monte Carlo* method and multiprocessing. Parameters ---------- - colourspace : RGB_Colourspace + colourspace\ *RGB* colourspace to compute the volume of. - samples : numeric, optional + samples\ Samples count. - limits : array_like, optional + limits\ *CIE L\\*a\\*b\\** colourspace volume. - illuminant_Lab : array_like, optional + illuminant_Lab\ *CIE L\\*a\\*b\\** colourspace *illuminant* chromaticity coordinates. - chromatic_adaptation_method : unicode, optional - **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', - 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02 Brill 2008', - 'Bianco 2010', 'Bianco PC 2010'}**, + chromatic_adaptation_transform\ *Chromatic adaptation* method. - random_generator : generator, optional + random_generator\ Random triplet generator providing the random samples within the *CIE L\\*a\\*b\\** colourspace volume. - random_state : RandomState, optional + random_state\ Mersenne Twister pseudo-random number generator to use in the random number generator. Returns ------- - float + :class:`numpy.floating` *RGB* colourspace volume. Notes @@ -238,51 +297,63 @@ def RGB_colourspace_volume_MonteCarlo( >>> prng = np.random.RandomState(2) >>> with disable_multiprocessing(): ... RGB_colourspace_volume_MonteCarlo(sRGB, 10e3, random_state=prng) - ... # doctest: +ELLIPSIS + ... # doctest: +SKIP 8... """ processes = multiprocessing.cpu_count() process_samples = DEFAULT_INT_DTYPE(np.round(samples / processes)) - arguments = (colourspace, process_samples, limits, illuminant_Lab, - chromatic_adaptation_method, random_generator, random_state) + arguments = ( + colourspace, + process_samples, + limits, + illuminant_Lab, + chromatic_adaptation_transform, + random_generator, + random_state, + ) with multiprocessing_pool() as pool: - results = pool.map(_wrapper_RGB_colourspace_volume_MonteCarlo, - [arguments for _ in range(processes)]) + results = pool.map( + _wrapper_RGB_colourspace_volume_MonteCarlo, + [arguments for _ in range(processes)], + ) - Lab_volume = np.product([np.sum(np.abs(x)) for x in limits]) + Lab_volume = np.product( + [np.sum(np.abs(x)) for x in as_float_array(limits)] + ) return Lab_volume * np.sum(results) / (process_samples * processes) def RGB_colourspace_volume_coverage_MonteCarlo( - colourspace, - coverage_sampler, - samples=10e6, - random_generator=random_triplet_generator, - random_state=None): + colourspace: RGB_Colourspace, + coverage_sampler: Callable, + samples: Integer = 1000000, + random_generator: Callable = random_triplet_generator, + random_state: np.random.RandomState = None, +) -> Floating: """ - Returns given *RGB* colourspace percentage coverage of an arbitrary volume. + Return given *RGB* colourspace percentage coverage of an arbitrary volume. Parameters ---------- - colourspace : RGB_Colourspace + colourspace *RGB* colourspace to compute the volume coverage percentage. - coverage_sampler : object + coverage_sampler Python object responsible for checking the volume coverage. - samples : numeric, optional + samples Samples count. - random_generator : generator, optional + random_generator Random triplet generator providing the random samples. - random_state : RandomState, optional + random_state Mersenne Twister pseudo-random number generator to use in the random number generator. Returns ------- - float + :class:`numpy.floating` Percentage coverage of volume. Examples @@ -295,48 +366,54 @@ def RGB_colourspace_volume_coverage_MonteCarlo( 81... """ - random_state = (random_state - if random_state is not None else np.random.RandomState()) + random_state = ( + random_state if random_state is not None else np.random.RandomState() + ) - # TODO: Investigate for generator yielding directly a ndarray. - XYZ = as_float_array( - list(random_generator(samples, random_state=random_state))) + XYZ = random_generator( + DEFAULT_INT_DTYPE(samples), random_state=random_state + ) XYZ_vs = XYZ[coverage_sampler(XYZ)] - RGB = XYZ_to_RGB(XYZ_vs, colourspace.whitepoint, colourspace.whitepoint, - colourspace.matrix_XYZ_to_RGB) + RGB = XYZ_to_RGB( + XYZ_vs, + colourspace.whitepoint, + colourspace.whitepoint, + colourspace.matrix_XYZ_to_RGB, + ) - RGB_c = RGB[np.logical_and( - np.min(RGB, axis=-1) >= 0, - np.max(RGB, axis=-1) <= 1)] + RGB_c = RGB[ + np.logical_and(np.min(RGB, axis=-1) >= 0, np.max(RGB, axis=-1) <= 1) + ] return 100 * RGB_c.size / XYZ_vs.size def RGB_colourspace_pointer_gamut_coverage_MonteCarlo( - colourspace, - samples=10e6, - random_generator=random_triplet_generator, - random_state=None): + colourspace: RGB_Colourspace, + samples: Integer = 1000000, + random_generator: Callable = random_triplet_generator, + random_state: np.random.RandomState = None, +) -> Floating: """ - Returns given *RGB* colourspace percentage coverage of Pointer's Gamut + Return given *RGB* colourspace percentage coverage of Pointer's Gamut volume using *Monte Carlo* method. Parameters ---------- - colourspace : RGB_Colourspace + colourspace *RGB* colourspace to compute the *Pointer's Gamut* coverage percentage. - samples : numeric, optional + samples Samples count. - random_generator : generator, optional + random_generator Random triplet generator providing the random samples. - random_state : RandomState, optional + random_state Mersenne Twister pseudo-random number generator to use in the random number generator. Returns ------- - float + :class:`numpy.floating` Percentage coverage of *Pointer's Gamut* volume. Examples @@ -349,34 +426,39 @@ def RGB_colourspace_pointer_gamut_coverage_MonteCarlo( """ return RGB_colourspace_volume_coverage_MonteCarlo( - colourspace, is_within_pointer_gamut, samples, random_generator, - random_state) + colourspace, + is_within_pointer_gamut, + samples, + random_generator, + random_state, + ) def RGB_colourspace_visible_spectrum_coverage_MonteCarlo( - colourspace, - samples=10e6, - random_generator=random_triplet_generator, - random_state=None): + colourspace: RGB_Colourspace, + samples: Integer = 1000000, + random_generator: Callable = random_triplet_generator, + random_state: np.random.RandomState = None, +) -> Floating: """ - Returns given *RGB* colourspace percentage coverage of visible spectrum + Return given *RGB* colourspace percentage coverage of visible spectrum volume using *Monte Carlo* method. Parameters ---------- - colourspace : RGB_Colourspace + colourspace *RGB* colourspace to compute the visible spectrum coverage percentage. - samples : numeric, optional + samples Samples count. - random_generator : generator, optional + random_generator Random triplet generator providing the random samples. - random_state : RandomState, optional + random_state Mersenne Twister pseudo-random number generator to use in the random number generator. Returns ------- - float + :class:`numpy.floating` Percentage coverage of visible spectrum volume. Examples @@ -389,5 +471,9 @@ def RGB_colourspace_visible_spectrum_coverage_MonteCarlo( """ return RGB_colourspace_volume_coverage_MonteCarlo( - colourspace, is_within_visible_spectrum, samples, random_generator, - random_state) + colourspace, + is_within_visible_spectrum, + samples, + random_generator, + random_state, + ) diff --git a/colour/volume/spectrum.py b/colour/volume/spectrum.py index bd32431818..e32272c35c 100644 --- a/colour/volume/spectrum.py +++ b/colour/volume/spectrum.py @@ -1,9 +1,9 @@ -# -*- coding: utf-8 -*- """ -Visible Spectrum Volume Computations -==================================== +Rösch-MacAdam colour solid - Visible Spectrum Volume Computations +================================================================= -Defines objects related to visible spectrum volume computations. +Defines the objects related to *Rösch-MacAdam* colour solid, visible spectrum +volume computations. References ---------- @@ -13,46 +13,79 @@ - :cite:`Mansencal2018` : Mansencal, T. (2018). How is the visible gamut bounded? Retrieved August 19, 2018, from https://stackoverflow.com/a/48396021/931625 +- :cite:`Martinez-Verdu2007` : Martínez-Verdú, F., Perales, E., Chorro, E., + de Fez, D., Viqueira, V., & Gilabert, E. (2007). Computation and + visualization of the MacAdam limits for any lightness, hue angle, and light + source. Journal of the Optical Society of America A, 24(6), 1501. + doi:10.1364/JOSAA.24.001501 """ -from __future__ import division, unicode_literals +from __future__ import annotations import numpy as np -import six -from colour.colorimetry import (MSDS_CMFS, msds_to_XYZ, SpectralShape, sd_ones) +from colour.colorimetry import ( + MultiSpectralDistributions, + SpectralDistribution, + SpectralShape, + handle_spectral_arguments, + msds_to_XYZ, +) from colour.constants import DEFAULT_FLOAT_DTYPE +from colour.hints import ( + Any, + ArrayLike, + Boolean, + Dict, + Floating, + Integer, + Literal, + NDArray, + Optional, + Union, +) from colour.volume import is_within_mesh_volume -from colour.utilities import zeros +from colour.utilities import CACHE_REGISTRY, zeros, validate_method -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'SPECTRAL_SHAPE_OUTER_SURFACE_XYZ', 'generate_pulse_waves', - 'XYZ_outer_surface', 'is_within_visible_spectrum' + "SPECTRAL_SHAPE_OUTER_SURFACE_XYZ", + "generate_pulse_waves", + "XYZ_outer_surface", + "solid_RoschMacAdam", + "is_within_visible_spectrum", ] -SPECTRAL_SHAPE_OUTER_SURFACE_XYZ = SpectralShape(360, 780, 5) +SPECTRAL_SHAPE_OUTER_SURFACE_XYZ: SpectralShape = SpectralShape(360, 780, 5) """ Default spectral shape according to *ASTM E308-15* practise shape but using an interval of 5. - -SPECTRAL_SHAPE_OUTER_SURFACE_XYZ : SpectralShape """ -_CACHE_OUTER_SURFACE_XYZ = {} -_CACHE_OUTER_SURFACE_XYZ_POINTS = {} +_CACHE_OUTER_SURFACE_XYZ: Dict = CACHE_REGISTRY.register_cache( + f"{__name__}._CACHE_OUTER_SURFACE_XYZ" +) + +_CACHE_OUTER_SURFACE_XYZ_POINTS: Dict = CACHE_REGISTRY.register_cache( + f"{__name__}._CACHE_OUTER_SURFACE_XYZ_POINTS" +) -def generate_pulse_waves(bins): +def generate_pulse_waves( + bins: Integer, + pulse_order: Union[Literal["Bins", "Pulse Wave Width"], str] = "Bins", + filter_jagged_pulses: Boolean = False, +) -> NDArray: """ - Generates the pulse waves of given number of bins necessary to totally - stimulate the colour matching functions. + Generate the pulse waves of given number of bins necessary to totally + stimulate the colour matching functions and produce the *Rösch-MacAdam* + colour solid. Assuming 5 bins, a first set of SPDs would be as follows:: @@ -82,17 +115,39 @@ def generate_pulse_waves(bins): Parameters ---------- - bins : int + bins Number of bins of the pulse waves. + pulse_order + Method for ordering the pulse waves. *Bins* is the default order, with + *Pulse Wave Width* ordering, instead of iterating over the pulse wave + widths first, iteration occurs over the bins, producing blocks of pulse + waves with increasing width. + filter_jagged_pulses + Whether to filter jagged pulses. When ``pulse_order`` is set to + *Pulse Wave Width*, the pulses are ordered by increasing width. Because + of the discrete nature of the underlying signal, the resulting pulses + will be jagged. For example assuming 5 bins, the center block with + the two extreme values added would be as follows:: + + 0 0 0 0 0 + 0 0 1 0 0 + 0 0 1 1 0 <-- + 0 1 1 1 0 + 0 1 1 1 1 <-- + 1 1 1 1 1 + + Setting the ``filter_jagged_pulses`` parameter to `True` will result + in the removal of the two marked pulse waves above thus avoiding jagged + lines when plotting and having to resort to excessive ``bins`` values. Returns ------- - ndarray + :class:`numpy.ndarray` Pulse waves. References ---------- - :cite:`Lindbloom2015`, :cite:`Mansencal2018` + :cite:`Lindbloom2015`, :cite:`Mansencal2018`, :cite:`Martinez-Verdu2007` Examples -------- @@ -119,58 +174,141 @@ def generate_pulse_waves(bins): [ 1., 1., 0., 1., 1.], [ 1., 1., 1., 0., 1.], [ 1., 1., 1., 1., 1.]]) + >>> generate_pulse_waves(5, 'Pulse Wave Width') + array([[ 0., 0., 0., 0., 0.], + [ 1., 0., 0., 0., 0.], + [ 1., 1., 0., 0., 0.], + [ 1., 1., 0., 0., 1.], + [ 1., 1., 1., 0., 1.], + [ 0., 1., 0., 0., 0.], + [ 0., 1., 1., 0., 0.], + [ 1., 1., 1., 0., 0.], + [ 1., 1., 1., 1., 0.], + [ 0., 0., 1., 0., 0.], + [ 0., 0., 1., 1., 0.], + [ 0., 1., 1., 1., 0.], + [ 0., 1., 1., 1., 1.], + [ 0., 0., 0., 1., 0.], + [ 0., 0., 0., 1., 1.], + [ 0., 0., 1., 1., 1.], + [ 1., 0., 1., 1., 1.], + [ 0., 0., 0., 0., 1.], + [ 1., 0., 0., 0., 1.], + [ 1., 0., 0., 1., 1.], + [ 1., 1., 0., 1., 1.], + [ 1., 1., 1., 1., 1.]]) + >>> generate_pulse_waves(5, 'Pulse Wave Width', True) + array([[ 0., 0., 0., 0., 0.], + [ 1., 0., 0., 0., 0.], + [ 1., 1., 0., 0., 1.], + [ 0., 1., 0., 0., 0.], + [ 1., 1., 1., 0., 0.], + [ 0., 0., 1., 0., 0.], + [ 0., 1., 1., 1., 0.], + [ 0., 0., 0., 1., 0.], + [ 0., 0., 1., 1., 1.], + [ 0., 0., 0., 0., 1.], + [ 1., 0., 0., 1., 1.], + [ 1., 1., 1., 1., 1.]]) """ + pulse_order = validate_method( + pulse_order, + ["Bins", "Pulse Wave Width"], + '"{0}" pulse order is invalid, it must be one of {1}!', + ) + square_waves = [] square_waves_basis = np.tril( - np.ones((bins, bins), dtype=DEFAULT_FLOAT_DTYPE))[0:-1, :] - for square_wave_basis in square_waves_basis: + np.ones((bins, bins), dtype=DEFAULT_FLOAT_DTYPE) + )[0:-1, :] + + if pulse_order.lower() == "bins": + for square_wave_basis in square_waves_basis: + for i in range(bins): + square_waves.append(np.roll(square_wave_basis, i)) + else: for i in range(bins): - square_waves.append(np.roll(square_wave_basis, i)) - - return np.vstack([ - zeros(bins), - np.vstack(square_waves), - np.ones(bins, dtype=DEFAULT_FLOAT_DTYPE) - ]) - - -def XYZ_outer_surface(cmfs=MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] - .copy().align(SPECTRAL_SHAPE_OUTER_SURFACE_XYZ), - illuminant=sd_ones(SPECTRAL_SHAPE_OUTER_SURFACE_XYZ), - **kwargs): + for j, square_wave_basis in enumerate(square_waves_basis): + square_waves.append(np.roll(square_wave_basis, i - j // 2)) + + if filter_jagged_pulses: + square_waves = square_waves[::2] + + return np.vstack( + [ + zeros(bins), + np.vstack(square_waves), + np.ones(bins, dtype=DEFAULT_FLOAT_DTYPE), + ] + ) + + +def XYZ_outer_surface( + cmfs: Optional[MultiSpectralDistributions] = None, + illuminant: Optional[SpectralDistribution] = None, + point_order: Union[Literal["Bins", "Pulse Wave Width"], str] = "Bins", + filter_jagged_points: Boolean = False, + **kwargs: Any, +) -> NDArray: """ - Generates the *CIE XYZ* colourspace outer surface for given colour matching - functions using multi-spectral conversion of pulse waves to *CIE XYZ* - tristimulus values. + Generate the *Rösch-MacAdam* colour solid, i.e. *CIE XYZ* colourspace + outer surface, for given colour matching functions using multi-spectral + conversion of pulse waves to *CIE XYZ* tristimulus values. Parameters ---------- - cmfs : XYZ_ColourMatchingFunctions, optional - Standard observer colour matching functions. - illuminant : SpectralDistribution, optional - Illuminant spectral distribution. + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + illuminant + Illuminant spectral distribution, default to *CIE Illuminant E*. + point_order + Method for ordering the underlying pulse waves used to generate the + *Rösch-MacAdam* colour solid. *Bins* is the default order, with + *Pulse Wave Width* ordering, instead of iterating over the pulse wave + widths first, iteration occurs over the bins, producing blocks of pulse + waves with increasing width. + filter_jagged_points + Whether to filter the underlying jagged pulses. When ``point_order`` is + set to *Pulse Wave Width*, the pulses are ordered by increasing width. + Because of the discrete nature of the underlying signal, the resulting + pulses will be jagged. For example assuming 5 bins, the center block + with the two extreme values added would be as follows:: + + 0 0 0 0 0 + 0 0 1 0 0 + 0 0 1 1 0 <-- + 0 1 1 1 0 + 0 1 1 1 1 <-- + 1 1 1 1 1 + + Setting the ``filter_jagged_points`` parameter to `True` will result + in the removal of the two marked pulse waves above thus avoiding jagged + lines when plotting and having to resort to excessive ``bins`` values. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.msds_to_XYZ`}, - Please refer to the documentation of the previously listed definition. + See the documentation of the previously listed definition. Returns ------- - ndarray - Outer surface *CIE XYZ* tristimulus values. + :class:`numpy.ndarray` + *Rösch-MacAdam* colour solid, *CIE XYZ* outer surface tristimulus + values. References ---------- - :cite:`Lindbloom2015`, :cite:`Mansencal2018` + :cite:`Lindbloom2015`, :cite:`Mansencal2018`, :cite:`Martinez-Verdu2007` Examples -------- - >>> from colour.colorimetry import SPECTRAL_SHAPE_DEFAULT + >>> from colour import MSDS_CMFS, SPECTRAL_SHAPE_DEFAULT >>> shape = SpectralShape( - ... SPECTRAL_SHAPE_DEFAULT.start, SPECTRAL_SHAPE_DEFAULT.end, 84) + ... SPECTRAL_SHAPE_DEFAULT.start, SPECTRAL_SHAPE_DEFAULT.end, 84 + ... ) >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] >>> XYZ_outer_surface(cmfs.copy().align(shape)) # doctest: +ELLIPSIS array([[ 0.0000000...e+00, 0.0000000...e+00, 0.0000000...e+00], @@ -207,57 +345,83 @@ def XYZ_outer_surface(cmfs=MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] [ 1.1022943...e+00, 1.0000000...e+00, 1.3568301...e+00]]) """ - settings = {'method': 'Integration', 'shape': cmfs.shape} - settings.update(kwargs) - - key = (hash(cmfs), hash(illuminant), six.text_type(settings)) + cmfs, illuminant = handle_spectral_arguments( + cmfs, + illuminant, + "CIE 1931 2 Degree Standard Observer", + "E", + SPECTRAL_SHAPE_OUTER_SURFACE_XYZ, + ) + + settings = dict(kwargs) + settings.update({"shape": cmfs.shape}) + + key = ( + hash(cmfs), + hash(illuminant), + point_order, + filter_jagged_points, + str(settings), + ) XYZ = _CACHE_OUTER_SURFACE_XYZ.get(key) if XYZ is None: - pulse_waves = generate_pulse_waves(len(cmfs.wavelengths)) - XYZ = msds_to_XYZ(pulse_waves, cmfs, illuminant, **settings) / 100 + pulse_waves = generate_pulse_waves( + len(cmfs.wavelengths), point_order, filter_jagged_points + ) + XYZ = ( + msds_to_XYZ( + pulse_waves, cmfs, illuminant, method="Integration", **settings + ) + / 100 + ) _CACHE_OUTER_SURFACE_XYZ[key] = XYZ return XYZ +solid_RoschMacAdam = XYZ_outer_surface + + def is_within_visible_spectrum( - XYZ, - cmfs=MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] - .copy().align(SPECTRAL_SHAPE_OUTER_SURFACE_XYZ), - illuminant=sd_ones(SPECTRAL_SHAPE_OUTER_SURFACE_XYZ), - tolerance=None, - **kwargs): + XYZ: ArrayLike, + cmfs: Optional[MultiSpectralDistributions] = None, + illuminant: Optional[SpectralDistribution] = None, + tolerance: Optional[Floating] = None, + **kwargs: Any, +) -> NDArray: """ - Returns if given *CIE XYZ* tristimulus values are within visible spectrum - volume / given colour matching functions volume. + Return whether given *CIE XYZ* tristimulus values are within the visible + spectrum volume, i.e. *Rösch-MacAdam* colour solid, for given colour + matching functions and illuminant. Parameters ---------- - XYZ : array_like + XYZ *CIE XYZ* tristimulus values. - cmfs : XYZ_ColourMatchingFunctions, optional - Standard observer colour matching functions. - illuminant : SpectralDistribution, optional - Illuminant spectral distribution. - tolerance : numeric, optional + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + illuminant + Illuminant spectral distribution, default to *CIE Illuminant E*. + tolerance Tolerance allowed in the inside-triangle check. Other Parameters ---------------- - \\**kwargs : dict, optional + kwargs {:func:`colour.msds_to_XYZ`}, - Please refer to the documentation of the previously listed definition. + See the documentation of the previously listed definition. Returns ------- - bool - Is within visible spectrum. + :class:`numpy.ndarray` + Are *CIE XYZ* tristimulus values within the visible spectrum volume, + i.e. *Rösch-MacAdam* colour solid. Notes ----- - +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ @@ -275,11 +439,20 @@ def is_within_visible_spectrum( array([ True, False], dtype=bool) """ - key = (hash(cmfs), hash(illuminant), six.text_type(kwargs)) + cmfs, illuminant = handle_spectral_arguments( + cmfs, + illuminant, + "CIE 1931 2 Degree Standard Observer", + "E", + SPECTRAL_SHAPE_OUTER_SURFACE_XYZ, + ) + + key = (hash(cmfs), hash(illuminant), str(kwargs)) vertices = _CACHE_OUTER_SURFACE_XYZ_POINTS.get(key) if vertices is None: - _CACHE_OUTER_SURFACE_XYZ_POINTS[key] = vertices = (XYZ_outer_surface( - cmfs, illuminant, **kwargs)) + _CACHE_OUTER_SURFACE_XYZ_POINTS[key] = vertices = solid_RoschMacAdam( + cmfs, illuminant, **kwargs + ) return is_within_mesh_volume(XYZ, vertices, tolerance) diff --git a/colour/volume/tests/__init__.py b/colour/volume/tests/__init__.py index 40a96afc6f..e69de29bb2 100644 --- a/colour/volume/tests/__init__.py +++ b/colour/volume/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour/volume/tests/test_macadam_limits.py b/colour/volume/tests/test_macadam_limits.py index 4cee38c853..63a88b82e9 100644 --- a/colour/volume/tests/test_macadam_limits.py +++ b/colour/volume/tests/test_macadam_limits.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.volume.macadam_limits` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.volume.macadam_limits` module.""" import numpy as np import unittest @@ -12,78 +7,75 @@ from colour.volume import is_within_macadam_limits from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestIsWithinMacadamLimits'] +__all__ = [ + "TestIsWithinMacadamLimits", +] class TestIsWithinMacadamLimits(unittest.TestCase): """ - Defines :func:`colour.volume.macadam_limits.is_within_macadam_limits` + Define :func:`colour.volume.macadam_limits.is_within_macadam_limits` definition unit tests methods. """ def test_is_within_macadam_limits(self): """ - Tests :func:`colour.volume.macadam_limits.is_within_macadam_limits` + Test :func:`colour.volume.macadam_limits.is_within_macadam_limits` definition. """ self.assertTrue( - is_within_macadam_limits(np.array([0.3205, 0.4131, 0.5100]), 'A')) + is_within_macadam_limits(np.array([0.3205, 0.4131, 0.5100]), "A") + ) self.assertFalse( - is_within_macadam_limits(np.array([0.0005, 0.0031, 0.0010]), 'A')) + is_within_macadam_limits(np.array([0.0005, 0.0031, 0.0010]), "A") + ) self.assertTrue( - is_within_macadam_limits(np.array([0.4325, 0.3788, 0.1034]), 'C')) + is_within_macadam_limits(np.array([0.4325, 0.3788, 0.1034]), "C") + ) self.assertFalse( - is_within_macadam_limits(np.array([0.0025, 0.0088, 0.0340]), 'C')) + is_within_macadam_limits(np.array([0.0025, 0.0088, 0.0340]), "C") + ) def test_n_dimensional_is_within_macadam_limits(self): """ - Tests :func:`colour.volume.macadam_limits.is_within_macadam_limits` + Test :func:`colour.volume.macadam_limits.is_within_macadam_limits` definition n-dimensional arrays support. """ a = np.array([0.3205, 0.4131, 0.5100]) - b = is_within_macadam_limits(a, 'A') + b = is_within_macadam_limits(a, "A") a = np.tile(a, (6, 1)) b = np.tile(b, 6) - np.testing.assert_almost_equal(is_within_macadam_limits(a, 'A'), b) + np.testing.assert_almost_equal(is_within_macadam_limits(a, "A"), b) a = np.reshape(a, (2, 3, 3)) b = np.reshape(b, (2, 3)) - np.testing.assert_almost_equal(is_within_macadam_limits(a, 'A'), b) - - def test_raise_exception_is_within_macadam_limits(self): - """ - Tests :func:`colour.volume.macadam_limits.is_within_macadam_limits` - definition raised exception. - """ - - self.assertRaises(KeyError, is_within_macadam_limits, - np.array([0.3205, 0.4131, 0.5100]), 'B') + np.testing.assert_almost_equal(is_within_macadam_limits(a, "A"), b) @ignore_numpy_errors def test_nan_is_within_macadam_limits(self): """ - Tests :func:`colour.volume.macadam_limits.is_within_macadam_limits` + Test :func:`colour.volume.macadam_limits.is_within_macadam_limits` definition nan support. """ cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) for case in cases: - is_within_macadam_limits(case, 'A') + is_within_macadam_limits(case, "A") -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/volume/tests/test_mesh.py b/colour/volume/tests/test_mesh.py index bf474bf830..90beed4a63 100644 --- a/colour/volume/tests/test_mesh.py +++ b/colour/volume/tests/test_mesh.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.volume.mesh` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.volume.mesh` module.""" import numpy as np import unittest @@ -12,59 +7,67 @@ from colour.volume import is_within_mesh_volume from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestIsWithinMeshVolume'] +__all__ = [ + "TestIsWithinMeshVolume", +] class TestIsWithinMeshVolume(unittest.TestCase): """ - Defines :func:`colour.volume.mesh.is_within_mesh_volume` definition unit + Define :func:`colour.volume.mesh.is_within_mesh_volume` definition unit tests methods. """ def setUp(self): - """ - Initialises common tests attributes. - """ - - self._mesh = np.array([ - [-1.0, -1.0, 1.0], - [1.0, -1.0, 1.0], - [1.0, -1.0, -1.0], - [-1.0, -1.0, -1.0], - [0.0, 1.0, 0.0], - ]) + """Initialise the common tests attributes.""" + + self._mesh = np.array( + [ + [-1.0, -1.0, 1.0], + [1.0, -1.0, 1.0], + [1.0, -1.0, -1.0], + [-1.0, -1.0, -1.0], + [0.0, 1.0, 0.0], + ] + ) def test_is_within_mesh_volume(self): - """ - Tests :func:`colour.volume.mesh.is_within_mesh_volume` definition. - """ + """Test :func:`colour.volume.mesh.is_within_mesh_volume` definition.""" self.assertTrue( is_within_mesh_volume( - np.array([0.0005, 0.0031, 0.0010]), self._mesh)) + np.array([0.0005, 0.0031, 0.0010]), self._mesh + ) + ) self.assertFalse( is_within_mesh_volume( - np.array([0.3205, 0.4131, 0.5100]), self._mesh)) + np.array([0.3205, 0.4131, 0.5100]), self._mesh + ) + ) self.assertTrue( is_within_mesh_volume( - np.array([0.0025, 0.0088, 0.0340]), self._mesh)) + np.array([0.0025, 0.0088, 0.0340]), self._mesh + ) + ) self.assertFalse( is_within_mesh_volume( - np.array([0.4325, 0.3788, 0.1034]), self._mesh)) + np.array([0.4325, 0.3788, 0.1034]), self._mesh + ) + ) def test_n_dimensional_is_within_mesh_volume(self): """ - Tests :func:`colour.volume.mesh.is_within_mesh_volume` definition + Test :func:`colour.volume.mesh.is_within_mesh_volume` definition n-dimensional arrays support. """ @@ -82,7 +85,7 @@ def test_n_dimensional_is_within_mesh_volume(self): @ignore_numpy_errors def test_nan_is_within_mesh_volume(self): """ - Tests :func:`colour.volume.mesh.is_within_mesh_volume` definition nan + Test :func:`colour.volume.mesh.is_within_mesh_volume` definition nan support. """ @@ -92,5 +95,5 @@ def test_nan_is_within_mesh_volume(self): is_within_mesh_volume(case, self._mesh) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/volume/tests/test_pointer_gamut.py b/colour/volume/tests/test_pointer_gamut.py index 8528cbb667..362b19f11a 100644 --- a/colour/volume/tests/test_pointer_gamut.py +++ b/colour/volume/tests/test_pointer_gamut.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.volume.pointer_gamut` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.volume.pointer_gamut` module.""" import numpy as np import unittest @@ -12,43 +7,49 @@ from colour.volume import is_within_pointer_gamut from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['TestIsWithinPointerGamut'] +__all__ = [ + "TestIsWithinPointerGamut", +] class TestIsWithinPointerGamut(unittest.TestCase): """ - Defines :func:`colour.volume.pointer_gamut.is_within_pointer_gamut` + Define :func:`colour.volume.pointer_gamut.is_within_pointer_gamut` definition unit tests methods. """ def test_is_within_pointer_gamut(self): """ - Tests :func:`colour.volume.pointer_gamut.is_within_pointer_gamut` + Test :func:`colour.volume.pointer_gamut.is_within_pointer_gamut` definition. """ self.assertTrue( - is_within_pointer_gamut(np.array([0.3205, 0.4131, 0.5100]))) + is_within_pointer_gamut(np.array([0.3205, 0.4131, 0.5100])) + ) self.assertFalse( - is_within_pointer_gamut(np.array([0.0005, 0.0031, 0.0010]))) + is_within_pointer_gamut(np.array([0.0005, 0.0031, 0.0010])) + ) self.assertTrue( - is_within_pointer_gamut(np.array([0.4325, 0.3788, 0.1034]))) + is_within_pointer_gamut(np.array([0.4325, 0.3788, 0.1034])) + ) self.assertFalse( - is_within_pointer_gamut(np.array([0.0025, 0.0088, 0.0340]))) + is_within_pointer_gamut(np.array([0.0025, 0.0088, 0.0340])) + ) def test_n_dimensional_is_within_pointer_gamut(self): """ - Tests :func:`colour.volume.pointer_gamut.is_within_pointer_gamut` + Test :func:`colour.volume.pointer_gamut.is_within_pointer_gamut` definition n-dimensional arrays support. """ @@ -66,7 +67,7 @@ def test_n_dimensional_is_within_pointer_gamut(self): @ignore_numpy_errors def test_nan_is_within_pointer_gamut(self): """ - Tests :func:`colour.volume.pointer_gamut.is_within_pointer_gamut` + Test :func:`colour.volume.pointer_gamut.is_within_pointer_gamut` definition nan support. """ @@ -76,5 +77,5 @@ def test_nan_is_within_pointer_gamut(self): is_within_pointer_gamut(case) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/volume/tests/test_rgb.py b/colour/volume/tests/test_rgb.py index 76fb284e08..c19173abef 100644 --- a/colour/volume/tests/test_rgb.py +++ b/colour/volume/tests/test_rgb.py @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour.volume.rgb` module. +Defines the unit tests for the :mod:`colour.volume.rgb` module. Notes ----- @@ -19,78 +18,89 @@ reproducibility-of-python-pseudo-random-numbers-across-systems-and-versions """ -from __future__ import division, unicode_literals - import numpy as np import unittest -from colour.models import (RGB_COLOURSPACE_ACES2065_1, RGB_COLOURSPACE_BT2020, - RGB_COLOURSPACE_BT709) +from colour.models import ( + RGB_COLOURSPACE_ACES2065_1, + RGB_COLOURSPACE_BT2020, + RGB_COLOURSPACE_BT709, +) from colour.volume import ( - RGB_colourspace_limits, RGB_colourspace_volume_MonteCarlo, + RGB_colourspace_limits, + RGB_colourspace_volume_MonteCarlo, RGB_colourspace_volume_coverage_MonteCarlo, RGB_colourspace_pointer_gamut_coverage_MonteCarlo, RGB_colourspace_visible_spectrum_coverage_MonteCarlo, - is_within_pointer_gamut) + is_within_pointer_gamut, +) from colour.utilities import disable_multiprocessing -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestRGB_colourspaceLimits', 'TestRGB_colourspaceVolumeMonteCarlo', - 'TestRGB_colourspace_volume_coverage_MonteCarlo', - 'TestRGB_colourspacePointerGamutCoverageMonteCarlo', - 'TestRGB_colourspaceVisibleSpectrumCoverageMonteCarlo' + "TestRGB_colourspaceLimits", + "TestRGB_colourspaceVolumeMonteCarlo", + "TestRGB_colourspace_volume_coverage_MonteCarlo", + "TestRGB_colourspacePointerGamutCoverageMonteCarlo", + "TestRGB_colourspaceVisibleSpectrumCoverageMonteCarlo", ] class TestRGB_colourspaceLimits(unittest.TestCase): """ - Defines :func:`colour.volume.rgb.RGB_colourspace_limits` definition unit + Define :func:`colour.volume.rgb.RGB_colourspace_limits` definition unit tests methods. """ def test_RGB_colourspace_limits(self): - """ - Tests :func:`colour.volume.rgb.RGB_colourspace_limits` definition. - """ + """Test :func:`colour.volume.rgb.RGB_colourspace_limits` definition.""" np.testing.assert_almost_equal( RGB_colourspace_limits(RGB_COLOURSPACE_BT709), - np.array([ - [0.00000000, 100.00000000], - [-86.18159689, 98.23744381], - [-107.85546554, 94.48384002], - ]), - decimal=7) + np.array( + [ + [0.00000000, 100.00000000], + [-86.18159689, 98.23744381], + [-107.85546554, 94.48384002], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( RGB_colourspace_limits(RGB_COLOURSPACE_BT2020), - np.array([ - [0.00000000, 100.00000000], - [-172.32005590, 130.52657313], - [-120.27412558, 136.88564561], - ]), - decimal=7) + np.array( + [ + [0.00000000, 100.00000000], + [-172.32005590, 130.52657313], + [-120.27412558, 136.88564561], + ] + ), + decimal=7, + ) np.testing.assert_almost_equal( RGB_colourspace_limits(RGB_COLOURSPACE_ACES2065_1), - np.array([ - [-58.9920208, 102.4721629], - [-404.1883039, 317.5082799], - [-274.0297625, 174.4716296], - ]), - decimal=7) + np.array( + [ + [-65.15706201, 102.72462756], + [-380.86283223, 281.23227495], + [-284.75355519, 177.11142683], + ] + ), + decimal=7, + ) class TestRGB_colourspaceVolumeMonteCarlo(unittest.TestCase): """ - Defines :func:`colour.volume.rgb.RGB_colourspace_volume_MonteCarlo` + Define :func:`colour.volume.rgb.RGB_colourspace_volume_MonteCarlo` definition unit tests methods. References @@ -101,7 +111,7 @@ class TestRGB_colourspaceVolumeMonteCarlo(unittest.TestCase): @disable_multiprocessing() def test_RGB_colourspace_volume_MonteCarlo(self): """ - Tests :func:`colour.volume.rgb.RGB_colourspace_volume_MonteCarlo` + Test :func:`colour.volume.rgb.RGB_colourspace_volume_MonteCarlo` definition. """ @@ -109,14 +119,17 @@ def test_RGB_colourspace_volume_MonteCarlo(self): RGB_colourspace_volume_MonteCarlo( RGB_COLOURSPACE_BT709, 10e3, - random_state=np.random.RandomState(2)) * 1e-6, + random_state=np.random.RandomState(2), + ) + * 1e-6, 821700.0 * 1e-6, - places=1) + places=1, + ) class TestRGB_colourspace_volume_coverage_MonteCarlo(unittest.TestCase): """ - Defines :func:`colour.volume.rgb.\ + Define :func:`colour.volume.rgb.\ RGB_colourspace_volume_coverage_MonteCarlo` definition unit tests methods. References @@ -126,7 +139,7 @@ class TestRGB_colourspace_volume_coverage_MonteCarlo(unittest.TestCase): def test_RGB_colourspace_volume_coverage_MonteCarlo(self): """ - Tests :func:`colour.volume.rgb.\ + Test :func:`colour.volume.rgb.\ RGB_colourspace_volume_coverage_MonteCarlo` definition. """ @@ -135,14 +148,16 @@ def test_RGB_colourspace_volume_coverage_MonteCarlo(self): RGB_COLOURSPACE_BT709, is_within_pointer_gamut, 10e3, - random_state=np.random.RandomState(2)), + random_state=np.random.RandomState(2), + ), 81.044349070100140, - decimal=7) + decimal=7, + ) class TestRGB_colourspacePointerGamutCoverageMonteCarlo(unittest.TestCase): """ - Defines :func:`colour.volume.rgb.\ + Define :func:`colour.volume.rgb.\ RGB_colourspace_pointer_gamut_coverage_MonteCarlo` definition unit tests methods. @@ -153,7 +168,7 @@ class TestRGB_colourspacePointerGamutCoverageMonteCarlo(unittest.TestCase): def test_RGB_colourspace_pointer_gamut_coverage_MonteCarlo(self): """ - Tests :func:`colour.volume.rgb.\ + Test :func:`colour.volume.rgb.\ RGB_colourspace_pointer_gamut_coverage_MonteCarlo` definition. """ @@ -161,14 +176,16 @@ def test_RGB_colourspace_pointer_gamut_coverage_MonteCarlo(self): RGB_colourspace_pointer_gamut_coverage_MonteCarlo( RGB_COLOURSPACE_BT709, 10e3, - random_state=np.random.RandomState(2)), + random_state=np.random.RandomState(2), + ), 81.044349070100140, - decimal=7) + decimal=7, + ) class TestRGB_colourspaceVisibleSpectrumCoverageMonteCarlo(unittest.TestCase): """ - Defines :func:`colour.volume.rgb.\ + Define :func:`colour.volume.rgb.\ RGB_colourspace_visible_spectrum_coverage_MonteCarlo` definition unit tests methods. @@ -179,7 +196,7 @@ class TestRGB_colourspaceVisibleSpectrumCoverageMonteCarlo(unittest.TestCase): def test_RGB_colourspace_visible_spectrum_coverage_MonteCarlo(self): """ - Tests :func:`colour.volume.rgb.\ + Test :func:`colour.volume.rgb.\ RGB_colourspace_visible_spectrum_coverage_MonteCarlo` definition. """ @@ -187,10 +204,12 @@ def test_RGB_colourspace_visible_spectrum_coverage_MonteCarlo(self): RGB_colourspace_visible_spectrum_coverage_MonteCarlo( RGB_COLOURSPACE_BT709, 10e3, - random_state=np.random.RandomState(2)), + random_state=np.random.RandomState(2), + ), 46.931407942238266, - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour/volume/tests/test_spectrum.py b/colour/volume/tests/test_spectrum.py index f60eaaa22f..df85067b11 100644 --- a/colour/volume/tests/test_spectrum.py +++ b/colour/volume/tests/test_spectrum.py @@ -1,156 +1,225 @@ -# -*- coding: utf-8 -*- -""" -Defines unit tests for :mod:`colour.volume.spectrum` module. -""" - -from __future__ import division, unicode_literals +"""Defines the unit tests for the :mod:`colour.volume.spectrum` module.""" import numpy as np import unittest from itertools import permutations -from colour.colorimetry import (SPECTRAL_SHAPE_DEFAULT, SpectralShape, - MSDS_CMFS_STANDARD_OBSERVER) -from colour.volume import (generate_pulse_waves, XYZ_outer_surface, - is_within_visible_spectrum) +from colour.colorimetry import ( + MSDS_CMFS, + SPECTRAL_SHAPE_DEFAULT, + SpectralShape, + reshape_msds, +) +from colour.volume import ( + generate_pulse_waves, + XYZ_outer_surface, + is_within_visible_spectrum, +) from colour.utilities import ignore_numpy_errors -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = [ - 'TestGeneratePulseWaves', 'TestXYZOuterSurface', - 'TestIsWithinVisibleSpectrum' + "TestGeneratePulseWaves", + "TestXYZOuterSurface", + "TestIsWithinVisibleSpectrum", ] class TestGeneratePulseWaves(unittest.TestCase): """ - Defines :func:`colour.volume.spectrum.generate_pulse_waves` + Define :func:`colour.volume.spectrum.generate_pulse_waves` definition unit tests methods. """ def test_generate_pulse_waves(self): """ - Tests :func:`colour.volume.spectrum.generate_pulse_waves` + Test :func:`colour.volume.spectrum.generate_pulse_waves` definition. """ np.testing.assert_array_equal( generate_pulse_waves(5), - np.array([ - [0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000], - [1.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000], - [0.00000000, 1.00000000, 0.00000000, 0.00000000, 0.00000000], - [0.00000000, 0.00000000, 1.00000000, 0.00000000, 0.00000000], - [0.00000000, 0.00000000, 0.00000000, 1.00000000, 0.00000000], - [0.00000000, 0.00000000, 0.00000000, 0.00000000, 1.00000000], - [1.00000000, 1.00000000, 0.00000000, 0.00000000, 0.00000000], - [0.00000000, 1.00000000, 1.00000000, 0.00000000, 0.00000000], - [0.00000000, 0.00000000, 1.00000000, 1.00000000, 0.00000000], - [0.00000000, 0.00000000, 0.00000000, 1.00000000, 1.00000000], - [1.00000000, 0.00000000, 0.00000000, 0.00000000, 1.00000000], - [1.00000000, 1.00000000, 1.00000000, 0.00000000, 0.00000000], - [0.00000000, 1.00000000, 1.00000000, 1.00000000, 0.00000000], - [0.00000000, 0.00000000, 1.00000000, 1.00000000, 1.00000000], - [1.00000000, 0.00000000, 0.00000000, 1.00000000, 1.00000000], - [1.00000000, 1.00000000, 0.00000000, 0.00000000, 1.00000000], - [1.00000000, 1.00000000, 1.00000000, 1.00000000, 0.00000000], - [0.00000000, 1.00000000, 1.00000000, 1.00000000, 1.00000000], - [1.00000000, 0.00000000, 1.00000000, 1.00000000, 1.00000000], - [1.00000000, 1.00000000, 0.00000000, 1.00000000, 1.00000000], - [1.00000000, 1.00000000, 1.00000000, 0.00000000, 1.00000000], - [1.00000000, 1.00000000, 1.00000000, 1.00000000, 1.00000000], - ])) + np.array( + [ + [0.0, 0.0, 0.0, 0.0, 0.0], + [1.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 1.0], + [1.0, 1.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0, 1.0], + [1.0, 0.0, 0.0, 0.0, 1.0], + [1.0, 1.0, 1.0, 0.0, 0.0], + [0.0, 1.0, 1.0, 1.0, 0.0], + [0.0, 0.0, 1.0, 1.0, 1.0], + [1.0, 0.0, 0.0, 1.0, 1.0], + [1.0, 1.0, 0.0, 0.0, 1.0], + [1.0, 1.0, 1.0, 1.0, 0.0], + [0.0, 1.0, 1.0, 1.0, 1.0], + [1.0, 0.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 0.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 0.0, 1.0], + [1.0, 1.0, 1.0, 1.0, 1.0], + ] + ), + ) + + np.testing.assert_array_equal( + generate_pulse_waves(5, "Pulse Wave Width"), + np.array( + [ + [0.0, 0.0, 0.0, 0.0, 0.0], + [1.0, 0.0, 0.0, 0.0, 0.0], + [1.0, 1.0, 0.0, 0.0, 0.0], + [1.0, 1.0, 0.0, 0.0, 1.0], + [1.0, 1.0, 1.0, 0.0, 1.0], + [0.0, 1.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 1.0, 0.0, 0.0], + [1.0, 1.0, 1.0, 0.0, 0.0], + [1.0, 1.0, 1.0, 1.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 1.0, 0.0], + [0.0, 1.0, 1.0, 1.0, 0.0], + [0.0, 1.0, 1.0, 1.0, 1.0], + [0.0, 0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0, 1.0], + [0.0, 0.0, 1.0, 1.0, 1.0], + [1.0, 0.0, 1.0, 1.0, 1.0], + [0.0, 0.0, 0.0, 0.0, 1.0], + [1.0, 0.0, 0.0, 0.0, 1.0], + [1.0, 0.0, 0.0, 1.0, 1.0], + [1.0, 1.0, 0.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0, 1.0], + ] + ), + ) + + np.testing.assert_equal( + np.sort(generate_pulse_waves(5), axis=0), + np.sort(generate_pulse_waves(5, "Pulse Wave Width"), axis=0), + ) + + np.testing.assert_array_equal( + generate_pulse_waves(5, "Pulse Wave Width", True), + np.array( + [ + [0.0, 0.0, 0.0, 0.0, 0.0], + [1.0, 0.0, 0.0, 0.0, 0.0], + [1.0, 1.0, 0.0, 0.0, 1.0], + [0.0, 1.0, 0.0, 0.0, 0.0], + [1.0, 1.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 0.0], + [0.0, 1.0, 1.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 1.0, 1.0, 1.0], + [0.0, 0.0, 0.0, 0.0, 1.0], + [1.0, 0.0, 0.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0, 1.0], + ] + ), + ) class TestXYZOuterSurface(unittest.TestCase): """ - Defines :func:`colour.volume.spectrum.XYZ_outer_surface` + Define :func:`colour.volume.spectrum.XYZ_outer_surface` definition unit tests methods. """ def test_XYZ_outer_surface(self): """ - Tests :func:`colour.volume.spectrum.XYZ_outer_surface` + Test :func:`colour.volume.spectrum.XYZ_outer_surface` definition. """ - shape = SpectralShape(SPECTRAL_SHAPE_DEFAULT.start, - SPECTRAL_SHAPE_DEFAULT.end, 84) - cmfs = MSDS_CMFS_STANDARD_OBSERVER[ - 'CIE 1931 2 Degree Standard Observer'] + shape = SpectralShape( + SPECTRAL_SHAPE_DEFAULT.start, SPECTRAL_SHAPE_DEFAULT.end, 84 + ) + cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] + # pylint: disable=E1102 np.testing.assert_array_almost_equal( - XYZ_outer_surface(cmfs.copy().align(shape)), - np.array([ - [0.00000000e+00, 0.00000000e+00, 0.00000000e+00], - [9.63613812e-05, 2.90567768e-06, 4.49612264e-04], - [2.59105294e-01, 2.10312980e-02, 1.32074689e+00], - [1.05610219e-01, 6.20382435e-01, 3.54235713e-02], - [7.26479803e-01, 3.54608696e-01, 2.10051491e-04], - [1.09718745e-02, 3.96354538e-03, 0.00000000e+00], - [3.07925724e-05, 1.11197622e-05, 0.00000000e+00], - [2.59201656e-01, 2.10342037e-02, 1.32119651e+00], - [3.64715514e-01, 6.41413733e-01, 1.35617047e+00], - [8.32090022e-01, 9.74991131e-01, 3.56336228e-02], - [7.37451677e-01, 3.58572241e-01, 2.10051491e-04], - [1.10026671e-02, 3.97466514e-03, 0.00000000e+00], - [1.27153954e-04, 1.40254398e-05, 4.49612264e-04], - [3.64811875e-01, 6.41416639e-01, 1.35662008e+00], - [1.09119532e+00, 9.96022429e-01, 1.35638052e+00], - [8.43061896e-01, 9.78954677e-01, 3.56336228e-02], - [7.37482470e-01, 3.58583361e-01, 2.10051491e-04], - [1.10990285e-02, 3.97757082e-03, 4.49612264e-04], - [2.59232448e-01, 2.10453234e-02, 1.32119651e+00], - [1.09129168e+00, 9.96025335e-01, 1.35683013e+00], - [1.10216719e+00, 9.99985975e-01, 1.35638052e+00], - [8.43092689e-01, 9.78965796e-01, 3.56336228e-02], - [7.37578831e-01, 3.58586267e-01, 6.59663755e-04], - [2.70204323e-01, 2.50088688e-02, 1.32119651e+00], - [3.64842668e-01, 6.41427759e-01, 1.35662008e+00], - [1.10226355e+00, 9.99988880e-01, 1.35683013e+00], - [1.10219798e+00, 9.99997094e-01, 1.35638052e+00], - [8.43189050e-01, 9.78968702e-01, 3.60832350e-02], - [9.96684125e-01, 3.79617565e-01, 1.32140656e+00], - [3.75814542e-01, 6.45391304e-01, 1.35662008e+00], - [1.09132247e+00, 9.96036455e-01, 1.35683013e+00], - [1.10229434e+00, 1.00000000e+00, 1.35683013e+00], - ]), - decimal=7) + XYZ_outer_surface(reshape_msds(cmfs, shape)), + np.array( + [ + [0.00000000e00, 0.00000000e00, 0.00000000e00], + [9.63613812e-05, 2.90567768e-06, 4.49612264e-04], + [2.59105294e-01, 2.10312980e-02, 1.32074689e00], + [1.05610219e-01, 6.20382435e-01, 3.54235713e-02], + [7.26479803e-01, 3.54608696e-01, 2.10051491e-04], + [1.09718745e-02, 3.96354538e-03, 0.00000000e00], + [3.07925724e-05, 1.11197622e-05, 0.00000000e00], + [2.59201656e-01, 2.10342037e-02, 1.32119651e00], + [3.64715514e-01, 6.41413733e-01, 1.35617047e00], + [8.32090022e-01, 9.74991131e-01, 3.56336228e-02], + [7.37451677e-01, 3.58572241e-01, 2.10051491e-04], + [1.10026671e-02, 3.97466514e-03, 0.00000000e00], + [1.27153954e-04, 1.40254398e-05, 4.49612264e-04], + [3.64811875e-01, 6.41416639e-01, 1.35662008e00], + [1.09119532e00, 9.96022429e-01, 1.35638052e00], + [8.43061896e-01, 9.78954677e-01, 3.56336228e-02], + [7.37482470e-01, 3.58583361e-01, 2.10051491e-04], + [1.10990285e-02, 3.97757082e-03, 4.49612264e-04], + [2.59232448e-01, 2.10453234e-02, 1.32119651e00], + [1.09129168e00, 9.96025335e-01, 1.35683013e00], + [1.10216719e00, 9.99985975e-01, 1.35638052e00], + [8.43092689e-01, 9.78965796e-01, 3.56336228e-02], + [7.37578831e-01, 3.58586267e-01, 6.59663755e-04], + [2.70204323e-01, 2.50088688e-02, 1.32119651e00], + [3.64842668e-01, 6.41427759e-01, 1.35662008e00], + [1.10226355e00, 9.99988880e-01, 1.35683013e00], + [1.10219798e00, 9.99997094e-01, 1.35638052e00], + [8.43189050e-01, 9.78968702e-01, 3.60832350e-02], + [9.96684125e-01, 3.79617565e-01, 1.32140656e00], + [3.75814542e-01, 6.45391304e-01, 1.35662008e00], + [1.09132247e00, 9.96036455e-01, 1.35683013e00], + [1.10229434e00, 1.00000000e00, 1.35683013e00], + ] + ), + decimal=7, + ) class TestIsWithinVisibleSpectrum(unittest.TestCase): """ - Defines :func:`colour.volume.spectrum.is_within_visible_spectrum` + Define :func:`colour.volume.spectrum.is_within_visible_spectrum` definition unit tests methods. """ def test_is_within_visible_spectrum(self): """ - Tests :func:`colour.volume.spectrum.is_within_visible_spectrum` + Test :func:`colour.volume.spectrum.is_within_visible_spectrum` definition. """ self.assertTrue( - is_within_visible_spectrum(np.array([0.3205, 0.4131, 0.5100]))) + is_within_visible_spectrum(np.array([0.3205, 0.4131, 0.5100])) + ) self.assertFalse( - is_within_visible_spectrum(np.array([-0.0005, 0.0031, 0.0010]))) + is_within_visible_spectrum(np.array([-0.0005, 0.0031, 0.0010])) + ) self.assertTrue( - is_within_visible_spectrum(np.array([0.4325, 0.3788, 0.1034]))) + is_within_visible_spectrum(np.array([0.4325, 0.3788, 0.1034])) + ) self.assertFalse( - is_within_visible_spectrum(np.array([0.0025, 0.0088, 0.0340]))) + is_within_visible_spectrum(np.array([0.0025, 0.0088, 0.0340])) + ) def test_n_dimensional_is_within_visible_spectrum(self): """ - Tests :func:`colour.volume.spectrum.is_within_visible_spectrum` + Test :func:`colour.volume.spectrum.is_within_visible_spectrum` definition n-dimensional arrays support. """ @@ -168,7 +237,7 @@ def test_n_dimensional_is_within_visible_spectrum(self): @ignore_numpy_errors def test_nan_is_within_visible_spectrum(self): """ - Tests :func:`colour.volume.spectrum.is_within_visible_spectrum` + Test :func:`colour.volume.spectrum.is_within_visible_spectrum` definition nan support. """ @@ -178,5 +247,5 @@ def test_nan_is_within_visible_spectrum(self): is_within_visible_spectrum(case) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/docs/_static/Logo_Dark_001.svg b/docs/_static/Logo_Dark_001.svg new file mode 100644 index 0000000000..91a1133e5e --- /dev/null +++ b/docs/_static/Logo_Dark_001.svg @@ -0,0 +1,35 @@ + + + + + + + + + diff --git a/docs/_static/Logo_Light_001.svg b/docs/_static/Logo_Light_001.svg new file mode 100644 index 0000000000..3ce959f980 --- /dev/null +++ b/docs/_static/Logo_Light_001.svg @@ -0,0 +1,35 @@ + + + + + + + + + diff --git a/docs/_static/Logo_Medium_001.png b/docs/_static/Logo_Medium_001.png index 17acc66039..50d69429a4 100644 Binary files a/docs/_static/Logo_Medium_001.png and b/docs/_static/Logo_Medium_001.png differ diff --git a/docs/_static/Logo_Small_001.png b/docs/_static/Logo_Small_001.png index d5231d3610..fb28a415b1 100644 Binary files a/docs/_static/Logo_Small_001.png and b/docs/_static/Logo_Small_001.png differ diff --git a/docs/_static/custom.css b/docs/_static/custom.css deleted file mode 100644 index 205bca82d6..0000000000 --- a/docs/_static/custom.css +++ /dev/null @@ -1,26 +0,0 @@ -p { - margin-bottom: 8px !important; -} - -table.docutils { - width: 100% !important; -} - -table.longtable.docutils td { - background-color: rgb(252, 252, 252) !important; - border: 0 !important; - padding: 4px 4px 4px 0px !important; - vertical-align: top !important; - white-space: normal !important; -} - -table.longtable.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/advanced.rst b/docs/advanced.rst index 6804abc8a8..c6dd364e2e 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -1,33 +1,61 @@ -Advanced -======== +Advanced Concepts +================= This page describes some advanced usage scenarios of **Colour**. -.. contents:: Table of Contents - :local: - :depth: 3 - Environment ----------- Various environment variables can be used to modify **Colour** behaviour at runtime: -- `COLOUR_SCIENCE__FLOAT_PRECISION`: Sets the float precision for most of +- `COLOUR_SCIENCE__DEFAULT_INT_DTYPE`: Set the default integer dtype for + most of **Colour** computations. Possible values are `int32` and `int64` + (default). Changing the integer dtype *will almost certainly break* + **Colour**! *With great power comes great responsibility*. +- `COLOUR_SCIENCE__DEFAULT_FLOAT_DTYPE`: Set the float dtype for most of **Colour** computations. Possible values are `float16`, `float32` and - `float64` (default). Changing float precision might result in various + `float64` (default). Changing the float dtype might result in various **Colour** `functionality breaking entirely `__. *With great power comes great responsibility*. -- `COLOUR_SCIENCE__INT_PRECISION`: Sets the integer precision for most of - **Colour** computations. Possible values are `int8`, `int16`, `int32`, - and `int64` (default). Changing integer precision - *will almost certainly break* **Colour**! - *With great power comes great responsibility*. -- `COLOUR_SCIENCE__COLOUR__SHOW_WARNINGS_WITH_TRACEBACK`: results in the +- `COLOUR_SCIENCE__COLOUR__SHOW_WARNINGS_WITH_TRACEBACK`: Result in the :func:`warnings.showwarning` definition to be replaced with the :func:`colour.utilities.show_warning` definition and thus providing complete traceback from the point where the warning occurred. +Caching +------- + +**Colour** uses various internal caches to improve speed and prevent redundant +processes, notably for spectral related computations. + +The internal caches are managed with the `colour.utilities.CACHE_REGISTRY` +cache registry object: + +.. code-block:: python + + import colour + + print(colour.utilities.CACHE_REGISTRY) + +.. code-block:: text + + {'colour.colorimetry.spectrum._CACHE_RESHAPED_SDS_AND_MSDS': '0 item(s)', + 'colour.colorimetry.tristimulus_values._CACHE_LAGRANGE_INTERPOLATING_COEFFICIENTS': '0 ' + 'item(s)', + 'colour.colorimetry.tristimulus_values._CACHE_SD_TO_XYZ': '0 item(s)', + 'colour.colorimetry.tristimulus_values._CACHE_TRISTIMULUS_WEIGHTING_FACTORS': '0 ' + 'item(s)', + 'colour.quality.cfi2017._CACHE_TCS_CIE2017': '0 item(s)', + 'colour.volume.macadam_limits._CACHE_OPTIMAL_COLOUR_STIMULI_XYZ': '0 item(s)', + 'colour.volume.macadam_limits._CACHE_OPTIMAL_COLOUR_STIMULI_XYZ_TRIANGULATIONS': '0 ' + 'item(s)', + 'colour.volume.spectrum._CACHE_OUTER_SURFACE_XYZ': '0 item(s)', + 'colour.volume.spectrum._CACHE_OUTER_SURFACE_XYZ_POINTS': '0 item(s)'} + +See `colour.utilities.CacheRegistry` class documentation for more information +on how to manage the cache registry. + Using Colour without Scipy -------------------------- @@ -42,8 +70,8 @@ compiled with versions of Those are incompatible with the Python Wheels commonly built with `Visual Studio 2008 (Python 2.7) or Visual Studio 2017 (Python 3.6) `__. -It is however possible to use **Colour** in a partially broken and mock **Scipy** -by using the `mock_for_colour.py `__ +It is however possible to use **Colour** in a partially broken state and mock +**Scipy** by using the `mock_for_colour.py `__ module. Assuming it is available for import, a typical usage would be as follows: @@ -53,7 +81,8 @@ Assuming it is available for import, a typical usage would be as follows: import sys from mock_for_colour import MockModule - for module in ('scipy', 'scipy.interpolate', 'scipy.spatial', + for module in ('scipy', 'scipy.interpolate', 'scipy.linalg', + 'scipy.ndimage', 'scipy.ndimage.filters', 'scipy.spatial', 'scipy.spatial.distance', 'scipy.optimize'): sys.modules[str(module)] = MockModule(str(module)) diff --git a/docs/basics.rst b/docs/basics.rst index a5cba457a8..751ecd5f62 100644 --- a/docs/basics.rst +++ b/docs/basics.rst @@ -1,13 +1,9 @@ -Basics -====== +Basic Concepts +============== This page puts an emphasis on basic concepts of **Colour**, those are important to understand. -.. contents:: Table of Contents - :local: - :depth: 3 - Object Name Categorisation -------------------------- diff --git a/docs/bibliography.rst b/docs/bibliography.rst index 46c45a7bb3..55aacb35b6 100644 --- a/docs/bibliography.rst +++ b/docs/bibliography.rst @@ -3,7 +3,6 @@ Bibliography .. bibliography:: bibliography.bib :all: - :encoding: utf8 Indirect References ------------------- diff --git a/docs/colour.adaptation.rst b/docs/colour.adaptation.rst index 475ed1bca8..03a7b093d8 100644 --- a/docs/colour.adaptation.rst +++ b/docs/colour.adaptation.rst @@ -1,8 +1,6 @@ Chromatic Adaptation ==================== -.. contents:: :local: - Chromatic Adaptation -------------------- @@ -99,6 +97,7 @@ Von Kries CAT_PC_BIANCO2010 CAT_CAT02_BRILL2008 CAT_CAT02 + CAT_CAT16 CAT_CMCCAT2000 CAT_CMCCAT97 CAT_FAIRCHILD @@ -114,3 +113,15 @@ Von Kries :toctree: generated/ matrix_chromatic_adaptation_VonKries + +Zhai and Luo (2018) +------------------- + +``colour.adaptation`` + +.. currentmodule:: colour.adaptation + +.. autosummary:: + :toctree: generated/ + + chromatic_adaptation_Zhai2018 diff --git a/docs/colour.algebra.rst b/docs/colour.algebra.rst index 4648e599fa..a678c29a22 100644 --- a/docs/colour.algebra.rst +++ b/docs/colour.algebra.rst @@ -1,8 +1,6 @@ Algebra ======= -.. contents:: :local: - Extrapolation ------------- @@ -95,6 +93,7 @@ Geometry normalise_vector euclidean_distance + manhattan_distance extend_line_segment intersect_line_segments ellipse_coefficients_general_form @@ -113,18 +112,6 @@ Geometry LineSegmentsIntersections_Specification ellipse_fitting_Halir1998 -Matrix ------- - -``colour.algebra`` - -.. currentmodule:: colour.algebra - -.. autosummary:: - :toctree: generated/ - - is_identity - Random ------ @@ -163,4 +150,12 @@ Common set_spow_enable spow_enable spow + normalise_maximum + vector_dot + matrix_dot + linear_conversion + linstep_function + lerp smoothstep_function + smooth + is_identity diff --git a/docs/colour.appearance.rst b/docs/colour.appearance.rst index a1fdbebdc8..2e59324bbf 100644 --- a/docs/colour.appearance.rst +++ b/docs/colour.appearance.rst @@ -1,8 +1,6 @@ Colour Appearance Models ======================== -.. contents:: :local: - ATD (1995) ---------- @@ -40,6 +38,7 @@ CIECAM02 .. autosummary:: :toctree: generated/ + CAM_KWARGS_CIECAM02_sRGB InductionFactors_CIECAM02 CAM16 @@ -57,7 +56,6 @@ CAM16 CAM_Specification_CAM16 VIEWING_CONDITIONS_CAM16 - **Ancillary Objects** ``colour.appearance`` @@ -83,6 +81,34 @@ Hunt CAM_Specification_Hunt VIEWING_CONDITIONS_HUNT +Kim, Weyrich and Kautz (2009) +----------------------------- + +``colour`` + +.. currentmodule:: colour + +.. autosummary:: + :toctree: generated/ + + XYZ_to_Kim2009 + Kim2009_to_XYZ + CAM_Specification_Kim2009 + MEDIA_PARAMETERS_KIM2009 + VIEWING_CONDITIONS_KIM2009 + +**Ancillary Objects** + +``colour.appearance`` + +.. currentmodule:: colour.appearance + +.. autosummary:: + :toctree: generated/ + + InductionFactors_Kim2009 + MediaParameters_Kim2009 + :math:`LLAB(l:c)` ----------------- @@ -145,3 +171,55 @@ RLAB :toctree: generated/ D_FACTOR_RLAB + +ZCAM +---- + +``colour`` + +.. currentmodule:: colour + +.. autosummary:: + :toctree: generated/ + + XYZ_to_ZCAM + ZCAM_to_XYZ + CAM_Specification_ZCAM + VIEWING_CONDITIONS_ZCAM + +**Ancillary Objects** + +``colour.appearance`` + +.. currentmodule:: colour.appearance + +.. autosummary:: + :toctree: generated/ + + InductionFactors_ZCAM + +Helmholtz-Kohlrausch Effect Estimation +-------------------------------------- + +``colour`` + +.. currentmodule:: colour + +.. autosummary:: + :toctree: generated/ + + HKE_NAYATANI1997_METHODS + HelmholtzKohlrausch_effect_object_Nayatani1997 + HelmholtzKohlrausch_effect_luminous_Nayatani1997 + +**Ancillary Objects** + +``colour.appearance`` + +.. currentmodule:: colour.appearance + +.. autosummary:: + :toctree: generated/ + + coefficient_q_Nayatani1997 + coefficient_K_Br_Nayatani1997 diff --git a/docs/colour.biochemistry.rst b/docs/colour.biochemistry.rst index 5903460dc2..9e06431c51 100644 --- a/docs/colour.biochemistry.rst +++ b/docs/colour.biochemistry.rst @@ -1,8 +1,6 @@ Biochemistry ============ -.. contents:: :local: - Michaelis–Menten Kinetics ------------------------- @@ -13,5 +11,11 @@ Michaelis–Menten Kinetics .. autosummary:: :toctree: generated/ - reaction_rate_MichealisMenten - substrate_concentration_MichealisMenten + REACTION_RATE_MICHAELISMENTEN_METHODS + reaction_rate_MichaelisMenten + SUBSTRATE_CONCENTRATION_MICHAELISMENTEN_METHODS + substrate_concentration_MichaelisMenten + reaction_rate_MichaelisMenten_Michaelis1913 + substrate_concentration_MichaelisMenten_Michaelis1913 + reaction_rate_MichaelisMenten_Abebe2017 + substrate_concentration_MichaelisMenten_Abebe2017 diff --git a/docs/colour.blindness.rst b/docs/colour.blindness.rst index c1c9278ee7..61326dad0b 100644 --- a/docs/colour.blindness.rst +++ b/docs/colour.blindness.rst @@ -1,8 +1,6 @@ Colour Vision Deficiency ======================== -.. contents:: :local: - Machado, Oliveira and Fernandes (2009) -------------------------------------- diff --git a/docs/colour.characterisation.rst b/docs/colour.characterisation.rst index 8e41619bb8..4f6059c3c3 100644 --- a/docs/colour.characterisation.rst +++ b/docs/colour.characterisation.rst @@ -1,8 +1,6 @@ Colour Characterisation ======================= -.. contents:: :local: - ACES Spectral Conversion ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -14,6 +12,7 @@ ACES Spectral Conversion :toctree: generated/ sd_to_aces_relative_exposure_values + sd_to_ACES2065_1 **Ancillary Objects** @@ -37,6 +36,7 @@ ACES Input Transform Computation :toctree: generated/ matrix_idt + camera_RGB_to_ACES2065_1 **Ancillary Objects** @@ -55,7 +55,7 @@ ACES Input Transform Computation training_data_sds_to_XYZ best_illuminant optimisation_factory_rawtoaces_v1 - optimisation_factory_JzAzBz + optimisation_factory_Jzazbz Colour Fitting -------------- diff --git a/docs/colour.colorimetry.rst b/docs/colour.colorimetry.rst index c0e9fb24f9..f815c183d7 100644 --- a/docs/colour.colorimetry.rst +++ b/docs/colour.colorimetry.rst @@ -1,8 +1,6 @@ Colorimetry =========== -.. contents:: :local: - Spectral Data Structure ----------------------- @@ -24,6 +22,20 @@ Spectral Data Structure SPECTRAL_SHAPE_ASTME308 SPECTRAL_SHAPE_DEFAULT +**Ancillary Objects** + +``colour.colorimetry`` + +.. currentmodule:: colour.colorimetry + +.. autosummary:: + :toctree: generated/ + + reshape_sd + reshape_msds + sds_and_msds_to_sds + sds_and_msds_to_msds + Spectral Data Generation ------------------------ @@ -59,13 +71,11 @@ Spectral Data Generation blackbody_spectral_radiance daylight_locus_function - sd_gaussian_normal sd_gaussian_fwhm sd_single_led_Ohno2005 sd_multi_leds_Ohno2005 - sds_and_msds_to_sds - sds_and_msds_to_msds + **Aliases** @@ -94,6 +104,17 @@ Conversion to Tristimulus Values MSDS_TO_XYZ_METHODS wavelength_to_XYZ +**Ancillary Objects** + +``colour.colorimetry`` + +.. currentmodule:: colour.colorimetry + +.. autosummary:: + :toctree: generated/ + + handle_spectral_arguments + ASTM E308-15 ~~~~~~~~~~~~ @@ -233,6 +254,7 @@ Illuminants and Light Sources SDS_ILLUMINANTS CCS_LIGHT_SOURCES SDS_LIGHT_SOURCES + TVS_ILLUMINANTS TVS_ILLUMINANTS_HUNTERLAB **Ancillary Objects** @@ -298,6 +320,18 @@ Luminous Efficiency Functions SDS_LEFS_PHOTOPIC SDS_LEFS_SCOTOPIC +Spectral Uniformity +------------------- + +``colour`` + +.. currentmodule:: colour + +.. autosummary:: + :toctree: generated/ + + spectral_uniformity + Lightness Computation --------------------- @@ -372,6 +406,18 @@ Fairchild and Chen (2011) lightness_Fairchild2011 +Abebe, Pouli, Larabi and Reinhard (2017) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``colour.colorimetry`` + +.. currentmodule:: colour.colorimetry + +.. autosummary:: + :toctree: generated/ + + lightness_Abebe2017 + Luminance Computation --------------------- @@ -566,4 +612,6 @@ ASTM E313 .. autosummary:: :toctree: generated/ + yellowness_ASTME313_alternative + YELLOWNESS_COEFFICIENTS_ASTME313 yellowness_ASTME313 diff --git a/docs/colour.constants.rst b/docs/colour.constants.rst index 180ceca16d..e812bb901c 100644 --- a/docs/colour.constants.rst +++ b/docs/colour.constants.rst @@ -1,8 +1,6 @@ Constants ========= -.. contents:: :local: - CIE --- @@ -41,8 +39,8 @@ Common .. autosummary:: :toctree: generated/ - DEFAULT_FLOAT_DTYPE DEFAULT_INT_DTYPE + DEFAULT_FLOAT_DTYPE EPSILON FLOATING_POINT_NUMBER_PATTERN INTEGER_THRESHOLD diff --git a/docs/colour.continuous.rst b/docs/colour.continuous.rst index 28fabf935b..1410c8746a 100644 --- a/docs/colour.continuous.rst +++ b/docs/colour.continuous.rst @@ -1,8 +1,6 @@ Continuous Signal ================= -.. contents:: :local: - Continuous Signal ----------------- diff --git a/docs/colour.contrast.rst b/docs/colour.contrast.rst index 617f564061..3ed28f0c9a 100644 --- a/docs/colour.contrast.rst +++ b/docs/colour.contrast.rst @@ -1,8 +1,6 @@ Contrast Sensitivity ==================== -.. contents:: :local: - Contrast Sensitivity -------------------- diff --git a/docs/colour.corresponding.rst b/docs/colour.corresponding.rst index afab630ed6..c0c1759c10 100644 --- a/docs/colour.corresponding.rst +++ b/docs/colour.corresponding.rst @@ -1,8 +1,6 @@ Corresponding Chromaticities ============================ -.. contents:: :local: - Prediction ---------- diff --git a/docs/colour.difference.rst b/docs/colour.difference.rst index 48129e19a1..efaa2c5a47 100644 --- a/docs/colour.difference.rst +++ b/docs/colour.difference.rst @@ -1,8 +1,6 @@ Colour Difference ================= -.. contents:: :local: - Delta E ------- @@ -103,4 +101,38 @@ DIN99 .. autosummary:: :toctree: generated/ - delta_E_DIN99 \ No newline at end of file + delta_E_DIN99 + +Standardized Residual Sum of Squares (STRESS) Index +--------------------------------------------------- + +``colour`` + +.. currentmodule:: colour + +.. autosummary:: + :toctree: generated/ + + index_stress + INDEX_STRESS_METHODS + +``colour.difference`` + +.. currentmodule:: colour.difference + +.. autosummary:: + :toctree: generated/ + + index_stress_Garcia2007 + +Huang et al. (2015) Power-Functions +----------------------------------- + +``colour.difference`` + +.. currentmodule:: colour.difference + +.. autosummary:: + :toctree: generated/ + + power_function_Huang2015 \ No newline at end of file diff --git a/docs/colour.geometry.rst b/docs/colour.geometry.rst index 76c268810c..92fd5729da 100644 --- a/docs/colour.geometry.rst +++ b/docs/colour.geometry.rst @@ -1,8 +1,6 @@ Geometry Primitives Generation ============================== -.. contents:: :local: - Primitives ---------- @@ -25,7 +23,7 @@ Primitives .. autosummary:: :toctree: generated/ - PLANE_TO_AXIS_MAPPING + MAPPING_PLANE_TO_AXIS primitive_grid primitive_cube @@ -53,3 +51,15 @@ Primitive Vertices primitive_vertices_grid_mpl primitive_vertices_cube_mpl primitive_vertices_sphere + +Hull Section +------------ + +``colour.geometry`` + +.. currentmodule:: colour.geometry + +.. autosummary:: + :toctree: generated/ + + hull_section \ No newline at end of file diff --git a/docs/colour.graph.rst b/docs/colour.graph.rst index ad03a58ec3..6326e718c1 100644 --- a/docs/colour.graph.rst +++ b/docs/colour.graph.rst @@ -1,8 +1,6 @@ Automatic Colour Conversion Graph ================================= -.. contents:: :local: - Conversion ---------- diff --git a/docs/colour.hints.rst b/docs/colour.hints.rst new file mode 100644 index 0000000000..9d9e959a3d --- /dev/null +++ b/docs/colour.hints.rst @@ -0,0 +1,107 @@ +Annotation Type Hints +===================== + +``colour.hints`` + +.. currentmodule:: colour.hints + +.. autosummary:: + :toctree: generated/ + + Any + Callable + Dict + Generator + Iterable + Iterator + List + Mapping + ModuleType + Optional + Union + Sequence + SupportsIndex + TextIO + Tuple + Type + TypedDict + TypeVar + RegexFlag + DTypeBoolean + DTypeInteger + DTypeFloating + DTypeNumber + DTypeComplex + DType + Integer + Floating + Number + Complex + Boolean + Literal + Dataclass + NestedSequence + ArrayLike + IntegerOrArrayLike + FloatingOrArrayLike + NumberOrArrayLike + ComplexOrArrayLike + BooleanOrArrayLike + ScalarType + StrOrArrayLike + NDArray + IntegerOrNDArray + FloatingOrNDArray + NumberOrNDArray + ComplexOrNDArray + BooleanOrNDArray + StrOrNDArray + TypeInterpolator + TypeExtrapolator + TypeLUTSequenceItem + LiteralWarning + cast + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/colour.io.rst b/docs/colour.io.rst index 71e49359ba..cf058ff218 100644 --- a/docs/colour.io.rst +++ b/docs/colour.io.rst @@ -1,8 +1,6 @@ Input and Output ================ -.. contents:: :local: - Image Data ---------- @@ -34,6 +32,18 @@ Image Data read_image_Imageio write_image_Imageio +OpenColorIO Processing +---------------------- + +``colour.io`` + +.. currentmodule:: colour.io + +.. autosummary:: + :toctree: generated/ + + process_image_OpenColorIO + Look Up Table (LUT) Data ------------------------ @@ -48,6 +58,7 @@ Look Up Table (LUT) Data LUT1D LUT3x1D LUT3D + LUTOperatorMatrix LUTSequence .. autosummary:: @@ -108,6 +119,21 @@ IES TM-27-14 Data SpectralDistribution_IESTM2714 + +UPRTek and Sekonic Spectral Data +-------------------------------- + +``colour`` + +.. currentmodule:: colour + +.. autosummary:: + :toctree: generated/ + :template: class.rst + + SpectralDistribution_UPRTek + SpectralDistribution_Sekonic + X-Rite Data ----------- diff --git a/docs/colour.models.rst b/docs/colour.models.rst index 81986c87de..f5865d8a81 100644 --- a/docs/colour.models.rst +++ b/docs/colour.models.rst @@ -1,8 +1,6 @@ Colour Models ============= -.. contents:: :local: - Tristimulus Values, CIE xyY Colourspace and Chromaticity Coordinates -------------------------------------------------------------------- @@ -20,11 +18,18 @@ Tristimulus Values, CIE xyY Colourspace and Chromaticity Coordinates xyY_to_xy xy_to_xyY -.. contents:: :local: - Common Models ------------- +``colour`` + +.. currentmodule:: colour + +.. autosummary:: + :toctree: generated/ + + COLOURSPACE_MODELS + ``colour.models`` .. currentmodule:: colour.models @@ -126,8 +131,8 @@ Hunter Rd,a,b Colour Scale XYZ_to_Hunter_Rdab Hunter_Rdab_to_XYZ -DIN99 Colourspace ------------------ +DIN99 Colourspace and DIN99b, DIN99c, DIN99d Refined Formulas +------------------------------------------------------------- ``colour`` @@ -138,6 +143,8 @@ DIN99 Colourspace Lab_to_DIN99 DIN99_to_Lab + XYZ_to_DIN99 + DIN99_to_XYZ CAM02-LCD, CAM02-SCD, and CAM02-UCS Colourspaces - Luo, Cui and Li (2006) ------------------------------------------------------------------------- @@ -155,6 +162,12 @@ CAM02-LCD, CAM02-SCD, and CAM02-UCS Colourspaces - Luo, Cui and Li (2006) CAM02SCD_to_JMh_CIECAM02 JMh_CIECAM02_to_CAM02UCS CAM02UCS_to_JMh_CIECAM02 + XYZ_to_CAM02LCD + CAM02LCD_to_XYZ + XYZ_to_CAM02SCD + CAM02SCD_to_XYZ + XYZ_to_CAM02UCS + CAM02UCS_to_XYZ CAM16-LCD, CAM16-SCD, and CAM16-UCS Colourspaces - Li et al. (2017) ------------------------------------------------------------------- @@ -172,6 +185,25 @@ CAM16-LCD, CAM16-SCD, and CAM16-UCS Colourspaces - Li et al. (2017) CAM16SCD_to_JMh_CAM16 JMh_CAM16_to_CAM16UCS CAM16UCS_to_JMh_CAM16 + XYZ_to_CAM16LCD + CAM16LCD_to_XYZ + XYZ_to_CAM16SCD + CAM16SCD_to_XYZ + XYZ_to_CAM16UCS + CAM16UCS_to_XYZ + +:math:`IC_AC_B` Colourspace +--------------------------- + +``colour`` + +.. currentmodule:: colour + +.. autosummary:: + :toctree: generated/ + + XYZ_to_ICaCb + ICaCb_to_XYZ :math:`I_GP_GT_G` Colourspace ----------------------------- @@ -183,8 +215,8 @@ CAM16-LCD, CAM16-SCD, and CAM16-UCS Colourspaces - Li et al. (2017) .. autosummary:: :toctree: generated/ - XYZ_to_IGPGTG - IGPGTG_to_XYZ + XYZ_to_IgPgTg + IgPgTg_to_XYZ IPT Colourspace --------------- @@ -228,6 +260,19 @@ hdr-IPT Colourspace hdr_IPT_to_XYZ HDR_IPT_METHODS +Oklab Colourspace +----------------- + +``colour`` + +.. currentmodule:: colour + +.. autosummary:: + :toctree: generated/ + + XYZ_to_Oklab + Oklab_to_XYZ + OSA UCS Colourspace ------------------- @@ -241,7 +286,20 @@ OSA UCS Colourspace XYZ_to_OSA_UCS OSA_UCS_to_XYZ -:math:`JzAzBz` Colourspace +ProLab Colourspace +------------------ + +``colour`` + +.. currentmodule:: colour + +.. autosummary:: + :toctree: generated/ + + XYZ_to_ProLab + ProLab_to_XYZ + +:math:`Jzazbz` Colourspace -------------------------- ``colour`` @@ -251,8 +309,21 @@ OSA UCS Colourspace .. autosummary:: :toctree: generated/ - XYZ_to_JzAzBz - JzAzBz_to_XYZ + XYZ_to_Jzazbz + Jzazbz_to_XYZ + +**Ancillary Objects** + +``colour.models`` + +.. currentmodule:: colour.models + +.. autosummary:: + :toctree: generated/ + + IZAZBZ_METHODS + XYZ_to_Izazbz + Izazbz_to_XYZ RGB Colourspace and Transformations ----------------------------------- @@ -332,6 +403,7 @@ RGB Colourspaces RGB_COLOURSPACE_APPLE_RGB RGB_COLOURSPACE_BEST_RGB RGB_COLOURSPACE_BETA_RGB + RGB_COLOURSPACE_BLACKMAGIC_WIDE_GAMUT RGB_COLOURSPACE_BT470_525 RGB_COLOURSPACE_BT470_625 RGB_COLOURSPACE_BT709 @@ -457,6 +529,10 @@ Opto-Electronic Transfer Functions oetf_ARIBSTDB67 oetf_inverse_ARIBSTDB67 + oetf_BlackmagicFilmGeneration5 + oetf_inverse_BlackmagicFilmGeneration5 + oetf_DaVinciIntermediate + oetf_inverse_DaVinciIntermediate oetf_HLG_BT2100 oetf_inverse_HLG_BT2100 oetf_PQ_BT2100 @@ -589,6 +665,8 @@ Log Encoding and Decoding log_decoding_Log3G10 log_encoding_Log3G12 log_decoding_Log3G12 + log_encoding_NLog + log_decoding_NLog log_encoding_Panalog log_decoding_Panalog log_encoding_PivotedLog @@ -623,9 +701,11 @@ Y'CbCr Colour Encoding .. autosummary:: :toctree: generated/ + WEIGHTS_YCBCR + matrix_YCbCr + offset_YCbCr RGB_to_YCbCr YCbCr_to_RGB - WEIGHTS_YCBCR RGB_to_YcCbcCrc YcCbcCrc_to_RGB @@ -665,8 +745,10 @@ YCoCg Colour Encoding .. autosummary:: :toctree: generated/ - RGB_to_ICTCP - ICTCP_to_RGB + RGB_to_ICtCp + ICtCp_to_RGB + XYZ_to_ICtCp + ICtCp_to_XYZ RGB Representations ~~~~~~~~~~~~~~~~~~~ @@ -710,6 +792,19 @@ HSL Colourspace RGB_to_HSL HSL_to_RGB +HCL Colourspace +^^^^^^^^^^^^^^^ + +``colour`` + +.. currentmodule:: colour + +.. autosummary:: + :toctree: generated/ + + RGB_to_HCL + HCL_to_RGB + CMY Colourspace ^^^^^^^^^^^^^^^ @@ -725,6 +820,19 @@ CMY Colourspace CMY_to_CMYK CMYK_to_CMY +IHLS - Hanbury (2003) +^^^^^^^^^^^^^^^^^^^^^ + +``colour`` + +.. currentmodule:: colour + +.. autosummary:: + :toctree: generated/ + + RGB_to_IHLS + IHLS_to_RGB + Pointer's Gamut --------------- diff --git a/docs/colour.notation.rst b/docs/colour.notation.rst index e1a40ac232..f2ac54af75 100644 --- a/docs/colour.notation.rst +++ b/docs/colour.notation.rst @@ -1,8 +1,6 @@ Colour Notation Systems ======================= -.. contents:: :local: - Munsell Renotation System ------------------------- diff --git a/docs/colour.phenomena.rst b/docs/colour.phenomena.rst index 91d2cbf56a..aa8ac64839 100644 --- a/docs/colour.phenomena.rst +++ b/docs/colour.phenomena.rst @@ -1,8 +1,6 @@ Optical Phenomena ================= -.. contents:: :local: - Rayleigh Scattering ------------------- diff --git a/docs/colour.plotting.rst b/docs/colour.plotting.rst index ccca6289aa..058376923c 100644 --- a/docs/colour.plotting.rst +++ b/docs/colour.plotting.rst @@ -1,8 +1,6 @@ Plotting ======== -.. contents:: :local: - Common ------ @@ -26,6 +24,19 @@ Common plot_multi_functions plot_image +**Ancillary Objects** + +``colour.plotting.common`` + +.. currentmodule:: colour.plotting.common + +.. autosummary:: + :toctree: generated/ + + KwargsArtist + KwargsCamera + KwargsRender + Colorimetry ----------- @@ -151,10 +162,11 @@ Colour Models .. autosummary:: :toctree: generated/ - common_colourspace_model_axis_reorder + colourspace_model_axis_reorder plot_pointer_gamut plot_RGB_colourspaces_in_chromaticity_diagram plot_RGB_chromaticities_in_chromaticity_diagram + plot_ellipses_MacAdam1942_in_chromaticity_diagram Colour Notation Systems ----------------------- @@ -208,6 +220,31 @@ Colour Quality plot_colour_quality_bars +Gamut Section Plotting +---------------------- + +``colour.plotting`` + +.. currentmodule:: colour.plotting + +.. autosummary:: + :toctree: generated/ + + plot_visible_spectrum_section + plot_RGB_colourspace_section + +**Ancillary Objects** + +``colour.plotting.section`` + +.. currentmodule:: colour.plotting.section + +.. autosummary:: + :toctree: generated/ + + plot_hull_section_colours + plot_hull_section_contour + Colour Temperature & Correlated Colour Temperature -------------------------------------------------- @@ -231,10 +268,21 @@ Colour Temperature & Correlated Colour Temperature :toctree: generated/ plot_planckian_locus - plot_planckian_locus_CIE1931 - plot_planckian_locus_CIE1960UCS plot_planckian_locus_in_chromaticity_diagram +Colour Models Volume +-------------------- + +``colour.plotting`` + +.. currentmodule:: colour.plotting + +.. autosummary:: + :toctree: generated/ + + plot_RGB_colourspaces_gamuts + plot_RGB_scatter + ANSI/IES TM-30-18 Colour Rendition Report ----------------------------------------- @@ -260,19 +308,6 @@ ANSI/IES TM-30-18 Colour Rendition Report plot_single_sd_colour_rendition_report_intermediate plot_single_sd_colour_rendition_report_simple -Colour Models Volume --------------------- - -``colour.plotting`` - -.. currentmodule:: colour.plotting - -.. autosummary:: - :toctree: generated/ - - plot_RGB_colourspaces_gamuts - plot_RGB_scatter - Automatic Colour Conversion Graph --------------------------------- diff --git a/docs/colour.quality.rst b/docs/colour.quality.rst index ce09c87476..4b1499ccf7 100644 --- a/docs/colour.quality.rst +++ b/docs/colour.quality.rst @@ -1,8 +1,6 @@ Colour Quality ============== -.. contents:: :local: - Colour Fidelity Index --------------------- diff --git a/docs/colour.recovery.rst b/docs/colour.recovery.rst index 9ec454bc7d..ce347b904b 100644 --- a/docs/colour.recovery.rst +++ b/docs/colour.recovery.rst @@ -1,8 +1,6 @@ Reflectance Recovery ==================== -.. contents:: :local: - CIE XYZ Colourspace to Spectral ------------------------------- @@ -100,7 +98,7 @@ Otsu, Yamamoto and Hachisuka (2018) :toctree: generated/ Dataset_Otsu2018 - NodeTree_Otsu2018 + Tree_Otsu2018 Smits (1999) ------------ diff --git a/docs/colour.rst b/docs/colour.rst index 888035bfb2..fbe1535237 100644 --- a/docs/colour.rst +++ b/docs/colour.rst @@ -18,6 +18,7 @@ Colour colour.difference colour.geometry colour.graph + colour.hints colour.io colour.models colour.notation diff --git a/docs/colour.temperature.rst b/docs/colour.temperature.rst index 6425cb2f77..c801920226 100644 --- a/docs/colour.temperature.rst +++ b/docs/colour.temperature.rst @@ -1,8 +1,6 @@ Colour Temperature ================== -.. contents:: :local: - Correlated Colour Temperature ----------------------------- diff --git a/docs/colour.utilities.rst b/docs/colour.utilities.rst index 3bc5b8fc2a..1d403cb51f 100644 --- a/docs/colour.utilities.rst +++ b/docs/colour.utilities.rst @@ -1,8 +1,6 @@ Utilities ========= -.. contents:: :local: - Common ------ @@ -22,23 +20,36 @@ Common .. currentmodule:: colour.utilities +.. autosummary:: + :toctree: generated/ + :template: class.rst + + CacheRegistry + +.. currentmodule:: colour.utilities + .. autosummary:: :toctree: generated/ + CACHE_REGISTRY handle_numpy_errors ignore_numpy_errors raise_numpy_errors print_numpy_errors warn_numpy_errors ignore_python_warnings + attest batch disable_multiprocessing multiprocessing_pool is_matplotlib_installed is_networkx_installed + is_opencolorio_installed is_openimageio_installed is_pandas_installed + is_sklearn_installed is_tqdm_installed + is_trimesh_installed required is_iterable is_string @@ -48,17 +59,9 @@ Common filter_kwargs filter_mapping first_item - to_domain_1 - to_domain_10 - to_domain_100 - to_domain_degrees - to_domain_int - from_range_1 - from_range_10 - from_range_100 - from_range_degrees - from_range_int copy_definition + validate_method + optional Array ----- @@ -67,38 +70,54 @@ Array .. currentmodule:: colour.utilities +.. autosummary:: + :toctree: generated/ + :template: class.rst + + MixinDataclassFields + MixinDataclassIterable + MixinDataclassArray + MixinDataclassArithmetic + .. autosummary:: :toctree: generated/ as_array - as_int_array - as_float_array - as_numeric as_int as_float - set_float_precision - set_int_precision - as_namedtuple + as_int_array + as_float_array + as_int_scalar + as_float_scalar + set_default_int_dtype + set_default_float_dtype + to_domain_1 + to_domain_10 + to_domain_100 + to_domain_degrees + to_domain_int + from_range_1 + from_range_10 + from_range_100 + from_range_degrees + from_range_int closest_indexes closest - normalise_maximum interval is_uniform + has_only_nan in_array tstack tsplit row_as_diagonal - vector_dot - matrix_dot orient centroid - linear_conversion - lerp fill_nan ndarray_write zeros ones full + index_along_last_axis Metrics ------- @@ -127,6 +146,7 @@ Data Structures CaseInsensitiveMapping LazyCaseInsensitiveMapping Lookup + Node Structure Verbose @@ -140,6 +160,7 @@ Verbose :toctree: generated/ message_box + show_warning warning filter_warnings suppress_warnings diff --git a/docs/colour.volume.rst b/docs/colour.volume.rst index 68fb718bda..9fa7eb735f 100644 --- a/docs/colour.volume.rst +++ b/docs/colour.volume.rst @@ -1,8 +1,6 @@ Colour Volume ============= -.. contents:: :local: - Optimal Colour Stimuli - MacAdam Limits --------------------------------------- @@ -56,8 +54,8 @@ RGB Volume RGB_colourspace_volume_MonteCarlo RGB_colourspace_volume_coverage_MonteCarlo -Visible Spectrum ----------------- +Rösch-MacAdam Colour solid - Visible Spectrum +--------------------------------------------- ``colour`` @@ -79,3 +77,4 @@ Visible Spectrum generate_pulse_waves XYZ_outer_surface + solid_RoschMacAdam diff --git a/docs/conf.py b/docs/conf.py index 5711be7b95..e5c9cbc232 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,233 +1,176 @@ -# -*- coding: utf-8 -*- -# -# colour 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 - Documentation Configuration +==================================== +""" import os import re -import sys +import setuptools.archive_util +import urllib.request import colour as package # noqa -basename = re.sub('_(\\w)', lambda x: x.group(1).upper(), - package.__name__.title()) - -autodoc_member_order = 'bysource' -autodoc_mock_imports = [ - 'matplotlib', 'matplotlib.cm', 'matplotlib.image', 'matplotlib.patches', - 'matplotlib.path', 'matplotlib.pyplot', 'matplotlib.ticker', - 'mpl_toolkits.mplot3d', 'mpl_toolkits.mplot3d.art3d', 'scipy', - 'scipy.interpolate', 'scipy.ndimage', 'scipy.ndimage.filters', - 'scipy.optimize', 'scipy.spatial', 'scipy.spatial.distance' -] - -autosummary_generate = True - -napoleon_custom_sections = ['Attributes', 'Methods'] - -if os.environ.get('READTHEDOCS') == 'True': - utilities_directory = os.path.abspath( - os.path.join(os.getcwd(), '..', 'utilities')) - static_directory = os.path.abspath(os.path.join(os.getcwd(), '_static')) - sys.path.append(utilities_directory) - - from generate_plots import generate_documentation_plots - - generate_documentation_plots(static_directory) - -# 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('.')) +basename = re.sub( + "_(\\w)", lambda x: x.group(1).upper(), package.__name__.title() +) + +if os.environ.get("READTHEDOCS") == "True": + archive = "colour-plots.zip" + branch = urllib.parse.quote( + os.environ["READTHEDOCS_VERSION"] + .replace("experimental-", "experimental/") + .replace("feature-", "feature/") + .replace("hotfix-", "hotfix/"), + safe="", + ) + url = ( + f"https://nightly.link/colour-science/colour/workflows/" + f"continuous-integration-documentation/{branch}/{archive}" + ) + + print(f"Using artifact url: {url}") + + urllib.request.urlretrieve(url, filename=archive) + setuptools.archive_util.unpack_archive(archive, "_static") # -- 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', '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", ] -# 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 = '{0}.{1}'.format(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 +intersphinx_mapping = { + "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), + "scipy": ( + "https://docs.scipy.org/doc/scipy-1.8.0/html-scipyorg", + 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' +autodoc_member_order = "bysource" +autodoc_mock_imports = [ + "matplotlib", + "matplotlib.cm", + "matplotlib.image", + "matplotlib.patches", + "matplotlib.path", + "matplotlib.pyplot", + "matplotlib.ticker", + "mpl_toolkits.mplot3d", + "mpl_toolkits.mplot3d.art3d", + "scipy", + "scipy.interpolate", + "scipy.ndimage", + "scipy.ndimage.filters", + "scipy.optimize", + "scipy.spatial", + "scipy.spatial.distance", +] +autodoc_typehints = "both" +autodoc_type_aliases = { + "ArrayLike": "ArrayLike", + "Boolean": "bool", + "BooleanOrArrayLike": "BooleanOrArrayLike", + "BooleanOrNDArray": "BooleanOrNDArray", + "DType": "DType", + "DTypeBoolean": "DTypeBoolean", + "DTypeComplex": "DTypeComplex", + "DTypeFloating": "DTypeFloating", + "DTypeInteger": "DTypeInteger", + "DTypeNumber": "DTypeNumber", + "Floating": "float", + "FloatingOrArrayLike": "FloatingOrArrayLike", + "FloatingOrNDArray": "FloatingOrNDArray", + "Integer": "int", + "IntegerOrArrayLike": "IntegerOrArrayLike", + "IntegerOrNDArray": "IntegerOrNDArray", + "NestedSequence": "NestedSequence", + "Number": "Number", + "NumberOrArrayLike": "NumberOrArrayLike", + "NumberOrNDArray": "NumberOrNDArray", + "StrOrArrayLike": "StrOrArrayLike", + "StrOrNDArray": "StrOrNDArray", +} +autodoc_preserve_defaults = True -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['_build'] +autoclass_content = "both" -# The reST default role (used for this markup: `text`) to use for all -# documents. -# default_role = None +autosummary_generate = True -# If true, '()' will be appended to :func: etc. cross-reference text. -# add_function_parentheses = True +bibtex_bibfiles = ["bibliography.bib"] +bibtex_encoding = "utf8" -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -# add_module_names = True +napoleon_custom_sections = ["Attributes", "Methods"] -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -# show_authors = False +templates_path = ["_templates"] +source_suffix = ".rst" +master_doc = "index" -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'lovelace' +project = package.__application_name__ +copyright = package.__copyright__.replace("Copyright (C)", "") +version = f"{package.__major_version__}.{package.__minor_version__}" +release = package.__version__ -# A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] +exclude_patterns = ["_build"] -# If true, keep warnings as 'system message' paragraphs in the built documents. -# keep_warnings = False +pygments_style = "lovelace" # -- 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_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 = '{0}Doc'.format(basename) +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"] +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': - """ + "papersize": "a4paper", + "pointsize": "10pt", + "preamble": """ \\usepackage{charter} \\usepackage[defaultsans]{lato} \\usepackage{inconsolata} @@ -244,191 +187,60 @@ \\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', '{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' - -# 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 +latex_logo = "_static/Logo_Medium_001.png" # -- Options for manual page output --------------------------------------- - -# 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)] - -# If true, show URL addresses after external links. -# man_show_urls = False +man_pages = [ + ( + "index", + basename, + f"{package.__application_name__} Documentation", + [package.__author__], + 1, + ) +] # -- 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', 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. -# 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 = [] +epub_copyright = package.__copyright__.replace("Copyright (C)", "") +epub_exclude_files = ["search.html"] -# 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)} - - -def _autodoc_process_docstring(app, what, name, obj, options, lines): - """ - Process 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', '') + lines[i] = line.replace("# noqa", "") def setup(app): - app.add_stylesheet('custom.css') - app.connect('autodoc-process-docstring', _autodoc_process_docstring) - - -def _continuous_signal_repr(self): """ - Returns an ellipsis string representation of the continuous signal for - documentation purposes. - - Returns - ------- - unicode - Ellipsis string representation. + Prepare the extension and linking resources that Sphinx uses in the + build process. """ - return "{0}(name='{1}', ...)".format(self.__class__.__name__, self.name) - - -package.colorimetry.SpectralDistribution.__repr__ = (_continuous_signal_repr) -package.colorimetry.MultiSpectralDistributions.__repr__ = ( - _continuous_signal_repr) - - -def _case_insensitive_mapping_repr(self): - """ - Returns an ellipsis string representation of the case-insensitive mutable - mapping for documentation purposes. - - Returns - ------- - unicode - Ellipsis string representation. - """ - - return "{0}({1})".format( - self.__class__.__name__, - repr(dict(zip(self.keys(), ['...'] * len(self)))).replace( - "'...'", '...')) - - -package.utilities.CaseInsensitiveMapping.__repr__ = ( - _case_insensitive_mapping_repr) + app.connect("autodoc-process-docstring", autodoc_process_docstring) diff --git a/docs/how-to.rst b/docs/how-to.rst new file mode 100644 index 0000000000..21e0143cbd --- /dev/null +++ b/docs/how-to.rst @@ -0,0 +1,6 @@ +How-To +====== + +The `Google Colab How-To `__ +guide for **Colour** shows various techniques to solve specific problems +and highlights some interesting use cases. diff --git a/docs/index.rst b/docs/index.rst index dbb7ab08ac..8bca8a1299 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,4 +1,6 @@ -.. image:: https://raw.githubusercontent.com/colour-science/colour-branding/master/images/Colour_Logo_Medium_001.png +.. image:: https://raw.githubusercontent.com/colour-science/colour-branding/master/images/Colour_Logo_001.png + +| `Colour `__ is an open-source `Python `__ package providing a comprehensive number @@ -10,10 +12,6 @@ It is freely available under the **Colour** is an affiliated project of `NumFOCUS `__, a 501(c)(3) nonprofit in the United States. -.. contents:: Table of Contents - :local: - :depth: 3 - .. sectnum:: Draft Release Notes @@ -35,62 +33,6 @@ If you'd like to join them, please consider Features -------- -**Colour** features a rich dataset and collection of objects, please see the -`features `__ page for more -information. - -Installation ------------- - -**Colour** and its primary dependencies can be easily installed from the -`Python Package Index `__ -by issuing this command in a shell: - -.. code-block:: bash - - $ pip install --user colour-science - -The detailed installation procedure for the secondary dependencies is -described in the `Installation Guide `__. - -**Colour** is also available for `Anaconda `__ -from *Continuum Analytics* via `conda-forge `__: - -.. code-block:: bash - - $ conda install -c conda-forge colour-science - -Documentation -------------- - -Tutorial -~~~~~~~~ - -The `static tutorial `__ -provides an introduction to **Colour**. An interactive version is available via -`Google Colab `__. - -How-To Guide -~~~~~~~~~~~~ - -The `How-To `__ -guide for **Colour** shows various techniques to solve specific problems and -highlights some interesting use cases. - -API Reference -~~~~~~~~~~~~~ - -The main technical reference for **Colour** and its API is the -`Colour Manual `__. - -.. toctree:: - :maxdepth: 4 - - manual - -Examples -~~~~~~~~ - Most of the objects are available from the ``colour`` namespace: .. code-block:: python @@ -98,7 +40,7 @@ Most of the objects are available from the ``colour`` namespace: >>> import colour Automatic Colour Conversion Graph - ``colour.graph`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Starting with version *0.3.14*, **Colour** implements an automatic colour conversion graph enabling easier colour conversions. @@ -128,7 +70,7 @@ conversion graph enabling easier colour conversions. array([ 0.47924575, 0.31676968, 0.17362725]) Chromatic Adaptation - ``colour.adaptation`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -139,10 +81,10 @@ Chromatic Adaptation - ``colour.adaptation`` ... XYZ, colour.xy_to_XYZ(D65), colour.xy_to_XYZ(A)) array([ 0.2533053 , 0.13765138, 0.01543307]) >>> sorted(colour.CHROMATIC_ADAPTATION_METHODS) - ['CIE 1994', 'CMCCAT2000', 'Fairchild 1990', 'Von Kries'] + ['CIE 1994', 'CMCCAT2000', 'Fairchild 1990', 'Von Kries', 'Zhai 2018'] Algebra - ``colour.algebra`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Kernel Interpolation ******************** @@ -165,7 +107,7 @@ Sprague (1880) Interpolation array([ 6.72951612, 7.81406251, 43.77379185]) Colour Appearance Models - ``colour.appearance`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -174,10 +116,16 @@ Colour Appearance Models - ``colour.appearance`` >>> L_A = 318.31 >>> Y_b = 20.0 >>> colour.XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b) - CAM_Specification_CIECAM02(J=34.434525727858997, C=67.365010921125915, h=22.279164147957076, s=62.814855853327131, Q=177.47124941102123, M=70.024939419291385, H=2.689608534423904, HC=None) + CAM_Specification_CIECAM02(J=34.434525727858997, C=67.365010921125943, h=22.279164147957065, s=62.81485585332716, Q=177.47124941102123, M=70.024939419291414, H=2.6896085344238898, HC=None) + >>> colour.XYZ_to_CAM16(XYZ, XYZ_w, L_A, Y_b) + CAM_Specification_CAM16(J=33.880368498111686, C=69.444353357408033, h=19.510887327451748, s=64.03612114840314, Q=176.03752758512178, M=72.18638534116765, H=399.52975599115319, HC=None) + >>> colour.XYZ_to_Kim2009(XYZ, XYZ_w, L_A) + CAM_Specification_Kim2009(J=19.879918542450902, C=55.839055250876946, h=22.013388165090046, s=112.97979354939129, Q=36.309026130161449, M=46.346415858227864, H=2.3543198369639931, HC=None) + >>> colour.XYZ_to_ZCAM(XYZ, XYZ_w, L_A, Y_b) + CAM_Specification_ZCAM(J=38.347186278956357, C=21.12138989208518, h=33.711578931095197, s=81.444585609489536, Q=76.986725284523772, M=42.403805833900506, H=0.45779200212219573, HC=None, V=43.623590687423544, K=43.20894953152817, W=34.829588380192149) Colour Blindness - ``colour.blindness`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -193,7 +141,7 @@ Colour Blindness - ``colour.blindness`` [ 0.00644047, 0.25921579, 0.73434374]]) Colour Correction - ``colour characterisation`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -207,19 +155,19 @@ Colour Correction - ``colour characterisation`` ['Cheung 2004', 'Finlayson 2015', 'Vandermonde'] ACES Input Transform - ``colour characterisation`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python >>> sensitivities = colour.MSDS_CAMERA_SENSITIVITIES['Nikon 5100 (NPL)'] >>> illuminant = colour.SDS_ILLUMINANTS['D55'] >>> colour.matrix_idt(sensitivities, illuminant) - array([[ 0.46579991, 0.13409239, 0.01935141], - [ 0.01786094, 0.77557292, -0.16775555], - [ 0.03458652, -0.16152926, 0.74270359]]) + (array([[ 0.46579986, 0.13409221, 0.01935163], + [ 0.01786092, 0.77557268, -0.16775531], + [ 0.03458647, -0.16152923, 0.74270363]]), array([ 1.58214188, 1. , 1.28910346])) Colorimetry - ``colour.colorimetry`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Spectral Computations ********************* @@ -306,7 +254,8 @@ Lightness Computation >>> colour.lightness(12.19722535) 41.527875844653451 >>> sorted(colour.LIGHTNESS_METHODS) - ['CIE 1976', + ['Abebe 2017', + 'CIE 1976', 'Fairchild 2010', 'Fairchild 2011', 'Glasser 1958', @@ -354,9 +303,9 @@ Yellowness Computation >>> XYZ = [95.00000000, 100.00000000, 105.00000000] >>> colour.yellowness(XYZ) - 11.065000000000003 + 4.3400000000000034 >>> sorted(colour.YELLOWNESS_METHODS) - ['ASTM D1925', 'ASTM E313'] + ['ASTM D1925', 'ASTM E313', 'ASTM E313 Alternative'] Luminous Flux, Efficiency & Efficacy Computation ************************************************ @@ -374,7 +323,7 @@ Luminous Flux, Efficiency & Efficacy Computation 136.21708031547874 Contrast Sensitivity Function - ``colour.contrast`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -385,7 +334,7 @@ Contrast Sensitivity Function - ``colour.contrast`` Colour Difference - ``colour.difference`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -410,7 +359,7 @@ Colour Difference - ``colour.difference`` 'cie2000'] IO - ``colour.io`` -^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~ Images ****** @@ -445,7 +394,7 @@ Look Up Table (LUT) Data array([ 0.00575674, 0.00181493, 0.00121419]) Colour Models - ``colour.models`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CIE xyY Colourspace ******************* @@ -484,7 +433,7 @@ CIE 1964 U*V*W* Colourspace .. code-block:: python - >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952* 100] + >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952 * 100] >>> colour.XYZ_to_UVW(XYZ) array([ 94.55035725, 11.55536523, 40.54757405]) @@ -493,7 +442,7 @@ Hunter L,a,b Colour Scale .. code-block:: python - >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952* 100] + >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952 * 100] >>> colour.XYZ_to_Hunter_Lab(XYZ) array([ 34.92452577, 47.06189858, 14.38615107]) @@ -502,7 +451,7 @@ Hunter Rd,a,b Colour Scale .. code-block:: python - >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952* 100] + >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952 * 100] >>> colour.XYZ_to_Hunter_Rdab(XYZ) array([ 12.197225 , 57.12537874, 17.46241341]) @@ -511,7 +460,7 @@ CAM02-LCD, CAM02-SCD, and CAM02-UCS Colourspaces - Luo, Cui and Li (2006) .. code-block:: python - >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952* 100] + >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952 * 100] >>> XYZ_w = [95.05, 100.00, 108.88] >>> L_A = 318.31 >>> Y_b = 20.0 @@ -521,13 +470,17 @@ CAM02-LCD, CAM02-SCD, and CAM02-UCS Colourspaces - Luo, Cui and Li (2006) >>> JMh = (specification.J, specification.M, specification.h) >>> colour.JMh_CIECAM02_to_CAM02UCS(JMh) array([ 47.16899898, 38.72623785, 15.8663383 ]) + >>> XYZ = [0.20654008, 0.12197225, 0.05136952] + >>> XYZ_w = [95.05 / 100, 100.00 / 100, 108.88 / 100] + >>> colour.XYZ_to_CAM02UCS(XYZ, XYZ_w=XYZ_w, L_A=L_A, Y_b=Y_b) + array([ 47.16899898, 38.72623785, 15.8663383 ]) CAM16-LCD, CAM16-SCD, and CAM16-UCS Colourspaces - Li et al. (2017) ******************************************************************* .. code-block:: python - >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952* 100] + >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952 * 100] >>> XYZ_w = [95.05, 100.00, 108.88] >>> L_A = 318.31 >>> Y_b = 20.0 @@ -537,13 +490,25 @@ CAM16-LCD, CAM16-SCD, and CAM16-UCS Colourspaces - Li et al. (2017) >>> JMh = (specification.J, specification.M, specification.h) >>> colour.JMh_CAM16_to_CAM16UCS(JMh) array([ 46.55542238, 40.22460974, 14.25288392] + >>> XYZ = [0.20654008, 0.12197225, 0.05136952] + >>> XYZ_w = [95.05 / 100, 100.00 / 100, 108.88 / 100] + >>> colour.XYZ_to_CAM16UCS(XYZ, XYZ_w=XYZ_w, L_A=L_A, Y_b=Y_b) + array([ 46.55542238, 40.22460974, 14.25288392]) -IGPGTG Colourspace +ICaCb Colourspace ****************** .. code-block:: python - >>> colour.XYZ_to_IGPGTG([0.20654008, 0.12197225, 0.05136952]) + >>> XYZ_to_ICaCb(np.array([0.20654008, 0.12197225, 0.05136952])) + array([ 0.06875297, 0.05753352, 0.02081548]) + +IgPgTg Colourspace +****************** + +.. code-block:: python + + >>> colour.XYZ_to_IgPgTg([0.20654008, 0.12197225, 0.05136952]) array([ 0.42421258, 0.18632491, 0.10689223]) IPT Colourspace @@ -579,21 +544,37 @@ hdr-IPT Colourspace >>> colour.XYZ_to_hdr_IPT([0.20654008, 0.12197225, 0.05136952]) array([ 25.18261761, -22.62111297, 3.18511729]) +Oklab Colourspace +***************** + +.. code-block:: python + + >>> colour.XYZ_to_Oklab([0.20654008, 0.12197225, 0.05136952]) + array([ 0.51634019, 0.154695 , 0.06289579]) + OSA UCS Colourspace ******************* .. code-block:: python - >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952* 100] + >>> XYZ = [0.20654008 * 100, 0.12197225 * 100, 0.05136952 * 100] >>> colour.XYZ_to_OSA_UCS(XYZ) array([-3.0049979 , 2.99713697, -9.66784231]) -JzAzBz Colourspace +ProLab Colourspace ****************** .. code-block:: python - >>> colour.XYZ_to_JzAzBz([0.20654008, 0.12197225, 0.05136952]) + >>> colour.XYZ_to_ProLab([0.51634019, 0.15469500, 0.06289579]) + array([1.24610688, 2.39525236, 0.41902126]) + +Jzazbz Colourspace +****************** + +.. code-block:: python + + >>> colour.XYZ_to_Jzazbz([0.20654008, 0.12197225, 0.05136952]) array([ 0.00535048, 0.00924302, 0.00526007]) Y'CbCr Colour Encoding @@ -612,12 +593,12 @@ YCoCg Colour Encoding >>> colour.RGB_to_YCoCg([0.75, 0.75, 0.0]) array([ 0.5625, 0.375 , 0.1875]) -ICTCP Colour Encoding +ICtCp Colour Encoding ********************* .. code-block:: python - >>> colour.RGB_to_ICTCP([0.45620519, 0.03081071, 0.04091952]) + >>> colour.RGB_to_ICtCp([0.45620519, 0.03081071, 0.04091952]) array([ 0.07351364, 0.00475253, 0.09351596]) HSV Colourspace @@ -628,6 +609,14 @@ HSV Colourspace >>> colour.RGB_to_HSV([0.45620519, 0.03081071, 0.04091952]) array([ 0.99603944, 0.93246304, 0.45620519]) +IHLS Colourspace +**************** + +.. code-block:: python + + >>> colour.RGB_to_IHLS([0.45620519, 0.03081071, 0.04091952]) + array([ 6.26236117, 0.12197943, 0.42539448]) + Prismatic Colourspace ********************* @@ -686,6 +675,7 @@ RGB Colourspaces 'Apple RGB', 'Best RGB', 'Beta RGB', + 'Blackmagic Wide Gamut' 'CIE RGB', 'Cinema Gamut', 'ColorMatch RGB', @@ -743,24 +733,14 @@ OETFs >>> sorted(colour.OETFS) ['ARIB STD-B67', + 'Blackmagic Film Generation 5', + 'DaVinci Intermediate', 'ITU-R BT.2100 HLG', 'ITU-R BT.2100 PQ', 'ITU-R BT.601', 'ITU-R BT.709', 'SMPTE 240M'] -OETFs Inverse -************* - -.. code-block:: python - - >>> sorted(colour.OETF_INVERSES) - ['ARIB STD-B67', - 'ITU-R BT.2100 HLG', - 'ITU-R BT.2100 PQ', - 'ITU-R BT.601', - 'ITU-R BT.709'] - EOTFs ***** @@ -777,21 +757,6 @@ EOTFs 'ST 2084', 'sRGB'] -EOTFs Inverse -************* - -.. code-block:: python - - >>> sorted(colour.EOTF_INVERSES) - ['DCDM', - 'DICOM GSDF', - 'ITU-R BT.1886', - 'ITU-R BT.2020', - 'ITU-R BT.2100 HLG', - 'ITU-R BT.2100 PQ', - 'ST 2084', - 'sRGB'] - OOTFs ***** @@ -800,13 +765,6 @@ OOTFs >>> sorted(colour.OOTFS) ['ITU-R BT.2100 HLG', 'ITU-R BT.2100 PQ'] -OOTFs Inverse -************* - -.. code-block:: python - - >>> sorted(colour.OOTF_INVERSES) - ['ITU-R BT.2100 HLG', 'ITU-R BT.2100 PQ'] Log Encoding / Decoding *********************** @@ -829,6 +787,7 @@ Log Encoding / Decoding 'Log2', 'Log3G10', 'Log3G12', + 'N-Log', 'PLog', 'Panalog', 'Protune', @@ -893,7 +852,7 @@ CCTFs Encoding / Decoding 'sRGB'] Colour Notation Systems - ``colour.notation`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Munsell Value ************* @@ -923,7 +882,7 @@ Munsell Colour array([ 0.38736945, 0.35751656, 0.59362 ]) Optical Phenomena - ``colour.phenomena`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -941,7 +900,7 @@ Optical Phenomena - ``colour.phenomena`` extrapolator_args={'right': None, 'method': 'Constant', 'left': None}) Light Quality - ``colour.quality`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Colour Fidelity Index ********************* @@ -980,7 +939,7 @@ Academy Spectral Similarity Index (SSI) 94.0 Spectral Up-Sampling & Reflectance Recovery - ``colour.recovery`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -1000,7 +959,7 @@ Spectral Up-Sampling & Reflectance Recovery - ``colour.recovery`` ['Jakob 2019', 'Mallett 2019', 'Meng 2015', 'Otsu 2018', 'Smits 1999'] Correlated Colour Temperature Computation Methods - ``colour.temperature`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -1019,7 +978,7 @@ Correlated Colour Temperature Computation Methods - ``colour.temperature`` 'mccamy1992'] Colour Volume - ``colour.volume`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -1027,7 +986,7 @@ Colour Volume - ``colour.volume`` 821958.30000000005 Geometry Primitives Generation - ``colour.geometry`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -1052,7 +1011,7 @@ Geometry Primitives Generation - ``colour.geometry`` ['Cube MPL', 'Grid MPL', 'Quad MPL', 'Sphere'] Plotting - ``colour.plotting`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Most of the objects are available from the ``colour.plotting`` namespace: @@ -1092,12 +1051,11 @@ Blackbody ... blackbody_sds, ... y_label='W / (sr m$^2$) / m', ... plot_kwargs={ - ... use_sd_colours=True, - ... normalise_sd_colours=True, + ... 'use_sd_colours': True, + ... 'normalise_sd_colours': True, ... }, ... legend_location='upper right', - ... bounding_box=(0, 1250, 0, 2.5e15)) - + ... bounding_box=(0, 1250, 0, 2.5e6)) .. image:: _static/Examples_Plotting_Blackbodies.png Colour Matching Functions @@ -1126,7 +1084,7 @@ Luminous Efficiency ... y_label='Luminous Efficiency', ... legend_location='upper right', ... y_tighten=True, - ... margins=(0, 0, 0, .1)) + ... margins=(0, 0, 0, 0.1)) .. image:: _static/Examples_Plotting_Luminous_Efficiency.png @@ -1168,16 +1126,6 @@ Chromaticities Prediction .. image:: _static/Examples_Plotting_Chromaticities_Prediction.png -Colour Temperature -****************** - -.. code-block:: python - - >>> plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS(['A', 'B', 'C']) - -.. image:: _static/Examples_Plotting_CCT_CIE_1960_UCS_Chromaticity_Diagram.png - - Chromaticities ************** @@ -1211,34 +1159,55 @@ ANSI/IES TM-30-18 Colour Rendition Report .. image:: _static/Examples_Plotting_Colour_Rendition_Report.png -Contributing ------------- +Gamut Section +************* + +.. code-block:: python + + >>> plot_visible_spectrum_section(section_colours='RGB', section_opacity=0.15) + +.. image:: _static/Examples_Plotting_Plot_Visible_Spectrum_Section.png + +.. code-block:: python + + >>> plot_RGB_colourspace_section('sRGB', section_colours='RGB', section_opacity=0.15) + +.. image:: _static/Examples_Plotting_Plot_RGB_Colourspace_Section.png + +Colour Temperature +****************** + +.. code-block:: python -If you would like to contribute to **Colour**, please refer to the following -`Contributing `__ guide. + >>> plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS(['A', 'B', 'C']) -Changes -------- +.. image:: _static/Examples_Plotting_CCT_CIE_1960_UCS_Chromaticity_Diagram.png -The changes are viewable on the `Releases `__ page. +User Guide +---------- -Bibliography ------------- +.. toctree:: + :maxdepth: 2 -The bibliography is available on the `Bibliography `__ page. + user-guide -It is also viewable directly from the repository in -`BibTeX `__ -format. +API Reference +------------- + +.. toctree:: + :maxdepth: 2 + + reference See Also -------- -Here is a list of notable colour science packages sorted by languages: +Software +~~~~~~~~ **Python** -- `Colorio `__ by Schlömer, N. +- `Colorio `__ by Schlömer, N. - `ColorPy `__ by Kness, M. - `Colorspacious `__ by Smith, N. J., et al. - `python-colormath `__ by Taylor, G., et al. @@ -1267,10 +1236,21 @@ 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 `__ +- `Facebook `__ +- `Github Discussions `__ +- `Gitter `__ +- `Twitter `__ + About ----- | **Colour** by Colour Developers -| Copyright © 2013-2020 – Colour Developers – `colour-developers@colour-science.org `__ +| Copyright 2013 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 `__ diff --git a/docs/manual.rst b/docs/manual.rst deleted file mode 100644 index b1ef00123c..0000000000 --- a/docs/manual.rst +++ /dev/null @@ -1,11 +0,0 @@ -Colour Manual -============= - -.. toctree:: - :maxdepth: 3 - - tutorial - basics - advanced - reference - bibliography \ No newline at end of file diff --git a/docs/reference.rst b/docs/reference.rst index 5cb7f0e512..79695caf12 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -1,5 +1,5 @@ -Reference -========= +API Reference +============= .. toctree:: :titlesonly: diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 590039a4ac..bc76ae637b 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -38,31 +38,31 @@ Overview - :doc:`appearance `: Colour appearance models. - :doc:`biochemistry `: Biochemistry computations. - :doc:`blindness `: Colour vision deficiency models. -- :doc:`continuous `: Base objects for continuous data - representation. -- :doc:`contrast `: Objects for contrast sensitivity - computation. - :doc:`characterisation `: Colour correction, camera and display characterisation. - :doc:`colorimetry `: Core objects for colour computations. - :doc:`constants `: *CIE* and *CODATA* constants. +- :doc:`continuous `: Base objects for continuous data + representation. +- :doc:`contrast `: Objects for contrast sensitivity + computation. - :doc:`corresponding `: Corresponding colour chromaticities computations. - :doc:`difference `: Colour difference computations. -- *examples*: Examples for the sub-packages. - :doc:`geometry `: Geometry primitives generation. - :doc:`graph `: Graph for automatic colour conversions. +- :doc:`hints `: Type hints for annotations. - :doc:`io `: Input / output objects for reading and writing data. - :doc:`models `: Colour models. - :doc:`notation `: Colour notation systems. - :doc:`phenomena `: Computation of various optical phenomena. -- :doc:`plotting `: Diagrams, figures, etc… +- :doc:`plotting `: Diagrams, figures, etc... - :doc:`quality `: Colour quality computation. - :doc:`recovery `: Reflectance recovery. - :doc:`temperature `: Colour temperature and correlated - colour temperature computation. + colour temperature computation. - :doc:`utilities `: Various utilities and data structures. - :doc:`volume `: Colourspace volumes computation and optimal colour stimuli. @@ -88,7 +88,7 @@ The various sub-packages also expose their public API: for sub_package in ('adaptation', 'algebra', 'appearance', 'biochemistry', 'blindness', 'characterisation', 'colorimetry', 'constants', 'continuous', 'contrast', 'corresponding', - 'difference', 'geometry', 'graph', 'io', 'models', + 'difference', 'geometry', 'graph', 'hints', 'io', 'models', 'notation', 'phenomena', 'plotting', 'quality', 'recovery', 'temperature', 'utilities', 'volume'): print(sub_package.title()) @@ -99,12 +99,13 @@ The various sub-packages also expose their public API: Adaptation ['CHROMATIC_ADAPTATION_TRANSFORMS', - 'CAT_XYZ_SCALING', - 'CAT_VON_KRIES', + 'CAT_BIANCO2010', 'CAT_BRADFORD', - 'CAT_SHARP', + 'CAT_CAT02', + 'CAT_CAT02_BRILL2008', '...'] + Algebra ['cartesian_to_spherical', 'spherical_to_cartesian', @@ -113,6 +114,7 @@ The various sub-packages also expose their public API: 'cartesian_to_cylindrical', '...'] + Appearance ['InductionFactors_Hunt', 'VIEWING_CONDITIONS_HUNT', @@ -121,11 +123,16 @@ The various sub-packages also expose their public API: 'CAM_Specification_ATD95', '...'] + Biochemistry - ['reaction_rate_MichealisMenten', - 'substrate_concentration_MichealisMenten', + ['REACTION_RATE_MICHAELISMENTEN_METHODS', + 'reaction_rate_MichaelisMenten', + 'SUBSTRATE_CONCENTRATION_MICHAELISMENTEN_METHODS', + 'substrate_concentration_MichaelisMenten', + 'reaction_rate_MichaelisMenten_Michaelis1913', '...'] + Blindness ['CVD_MATRICES_MACHADO2010', 'msds_cmfs_anomalous_trichromacy_Machado2009', @@ -133,6 +140,7 @@ The various sub-packages also expose their public API: 'matrix_cvd_Machado2009', '...'] + Characterisation ['RGB_CameraSensitivities', 'RGB_DisplayPrimaries', @@ -141,14 +149,16 @@ The various sub-packages also expose their public API: 'CCS_COLOURCHECKERS', '...'] + Colorimetry ['SpectralShape', 'SPECTRAL_SHAPE_DEFAULT', 'SpectralDistribution', 'MultiSpectralDistributions', - 'sds_and_msds_to_sds', + 'reshape_sd', '...'] + Constants ['CONSTANT_K_M', 'CONSTANT_KP_M', @@ -157,9 +167,11 @@ The various sub-packages also expose their public API: 'CONSTANT_LIGHT_SPEED', '...'] + Continuous ['AbstractContinuousFunction', 'Signal', 'MultiSignals', '...'] + Contrast ['optical_MTF_Barten1999', 'pupil_diameter_Barten1999', @@ -168,6 +180,7 @@ The various sub-packages also expose their public API: 'maximum_angular_size_Barten1999', '...'] + Corresponding ['BRENEMAN_EXPERIMENTS', 'BRENEMAN_EXPERIMENT_PRIMARIES_CHROMATICITIES', @@ -176,6 +189,7 @@ The various sub-packages also expose their public API: 'corresponding_chromaticities_prediction_CIE1994', '...'] + Difference ['delta_E_CAM02LCD', 'delta_E_CAM02SCD', @@ -184,14 +198,16 @@ The various sub-packages also expose their public API: 'delta_E_CAM16SCD', '...'] + Geometry ['PLANE_TO_AXIS_MAPPING', 'primitive_grid', 'primitive_cube', + 'hull_section', 'PRIMITIVE_METHODS', - 'primitive', '...'] + Graph ['CONVERSION_GRAPH', 'CONVERSION_GRAPH_NODE_LABELS', @@ -199,22 +215,29 @@ The various sub-packages also expose their public API: 'convert', '...'] + + Hints + ['Any', 'Callable', 'Dict', 'Generator', 'Iterable', '...'] + + Io - ['SpectralDistribution_IESTM2714', - 'AbstractLUTSequenceOperator', - 'LUT1D', + ['LUT1D', 'LUT3x1D', 'LUT3D', + 'LUT_to_LUT', + 'AbstractLUTSequenceOperator', '...'] + Models ['Jab_to_JCh', 'JCh_to_Jab', 'COLOURSPACE_MODELS', 'COLOURSPACE_MODELS_AXIS_LABELS', - 'XYZ_to_colourspace_model', + 'COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE', '...'] + Notation ['MUNSELL_COLOURS_ALL', 'MUNSELL_COLOURS_1929', @@ -223,6 +246,7 @@ The various sub-packages also expose their public API: 'munsell_value', '...'] + Phenomena ['scattering_cross_section', 'rayleigh_optical_depth', @@ -230,6 +254,7 @@ The various sub-packages also expose their public API: 'sd_rayleigh_scattering', '...'] + Plotting ['SD_ASTMG173_ETR', 'SD_ASTMG173_GLOBAL_TILT', @@ -238,14 +263,16 @@ The various sub-packages also expose their public API: 'CONSTANTS_ARROW_STYLE', '...'] + Quality ['SDS_TCS', 'SDS_VS', - 'ColourRendering_Specification_CRI', - 'colour_rendering_index', - 'ColourRendering_Specification_CQS', + 'ColourRendering_Specification_CIE2017', + 'colour_fidelity_index_CIE2017', + 'ColourQuality_Specification_ANSIIESTM3018', '...'] + Recovery ['SPECTRAL_SHAPE_sRGB_MALLETT2019', 'MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019', @@ -254,6 +281,7 @@ The various sub-packages also expose their public API: 'CLUSTER_MEANS_OTSU2018', '...'] + Temperature ['xy_to_CCT_CIE_D', 'CCT_to_xy_CIE_D', @@ -262,14 +290,16 @@ The various sub-packages also expose their public API: 'xy_to_CCT_Kang2002', '...'] + Utilities ['Lookup', 'Structure', 'CaseInsensitiveMapping', 'LazyCaseInsensitiveMapping', - 'handle_numpy_errors', + 'Node', '...'] + Volume ['OPTIMAL_COLOUR_STIMULI_ILLUMINANTS', 'is_within_macadam_limits', @@ -286,28 +316,33 @@ The codebase is documented and most docstrings have usage examples: .. code-block:: text - Returns the *CIE UCS* colourspace *uv* chromaticity coordinates from given + Return the *CIE UCS* colourspace *uv* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}`, :math:`\Delta_{uv}` and colour matching functions using *Ohno (2013)* method. + Parameters ---------- - CCT_D_uv : ndarray + CCT_D_uv Correlated colour temperature :math:`T_{cp}`, :math:`\Delta_{uv}`. - cmfs : XYZ_ColourMatchingFunctions, optional - Standard observer colour matching functions. + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + Returns ------- - ndarray + :class:`numpy.ndarray` *CIE UCS* colourspace *uv* chromaticity coordinates. + References ---------- :cite:`Ohno2014a` + Examples -------- - >>> from colour.colorimetry import ( - ... SPECTRAL_SHAPE_DEFAULT, MSDS_CMFS_STANDARD_OBSERVER) + >>> from pprint import pprint + >>> from colour import MSDS_CMFS, SPECTRAL_SHAPE_DEFAULT >>> cmfs = ( - ... MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']. + ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SPECTRAL_SHAPE_DEFAULT) ... ) >>> CCT_D_uv = np.array([6507.4342201047066, 0.003223690901513]) @@ -327,6 +362,8 @@ the objects needed for spectral computations and many others: 'SPECTRAL_SHAPE_DEFAULT', 'SpectralDistribution', 'MultiSpectralDistributions', + 'reshape_sd', + 'reshape_msds', 'sds_and_msds_to_sds', 'sds_and_msds_to_msds', 'sd_blackbody', @@ -335,19 +372,20 @@ the objects needed for spectral computations and many others: 'LMS_ConeFundamentals', 'RGB_ColourMatchingFunctions', 'XYZ_ColourMatchingFunctions', + 'CCS_ILLUMINANTS', 'MSDS_CMFS', 'MSDS_CMFS_LMS', 'MSDS_CMFS_RGB', 'MSDS_CMFS_STANDARD_OBSERVER', - 'CCS_ILLUMINANTS', 'SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES', - 'TVS_ILLUMINANTS_HUNTERLAB', 'SDS_ILLUMINANTS', - 'CCS_LIGHT_SOURCES', - 'SDS_LIGHT_SOURCES', 'SDS_LEFS', 'SDS_LEFS_PHOTOPIC', 'SDS_LEFS_SCOTOPIC', + 'TVS_ILLUMINANTS', + 'TVS_ILLUMINANTS_HUNTERLAB', + 'CCS_LIGHT_SOURCES', + 'SDS_LIGHT_SOURCES', 'sd_constant', 'sd_zeros', 'sd_ones', @@ -369,6 +407,7 @@ the objects needed for spectral computations and many others: 'sd_to_XYZ', 'msds_to_XYZ', 'SPECTRAL_SHAPE_ASTME308', + 'handle_spectral_arguments', 'lagrange_coefficients_ASTME2022', 'tristimulus_weighting_factors_ASTME2022', 'adjust_tristimulus_weighting_factors_ASTME308', @@ -378,6 +417,7 @@ the objects needed for spectral computations and many others: 'msds_to_XYZ_integration', 'msds_to_XYZ_ASTME308', 'wavelength_to_XYZ', + 'spectral_uniformity', 'BANDPASS_CORRECTION_METHODS', 'bandpass_correction', 'bandpass_correction_Stearns1988', @@ -393,6 +433,7 @@ the objects needed for spectral computations and many others: 'lightness_CIE1976', 'lightness_Fairchild2010', 'lightness_Fairchild2011', + 'lightness_Abebe2017', 'intermediate_lightness_function_CIE1976', 'LUMINANCE_METHODS', 'luminance', @@ -401,6 +442,7 @@ the objects needed for spectral computations and many others: 'luminance_CIE1976', 'luminance_Fairchild2010', 'luminance_Fairchild2011', + 'luminance_Abebe2017', 'intermediate_luminance_function_CIE1976', 'dominant_wavelength', 'complementary_wavelength', @@ -425,9 +467,10 @@ the objects needed for spectral computations and many others: 'YELLOWNESS_METHODS', 'yellowness', 'yellowness_ASTMD1925', + 'yellowness_ASTME313_alternative', + 'YELLOWNESS_COEFFICIENTS_ASTME313', 'yellowness_ASTME313'] - **Colour** computations leverage a comprehensive quantity of datasets available in most sub-packages, for example the ``colour.colorimetry.datasets`` defines the following components: @@ -446,6 +489,7 @@ the following components: 'SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES', 'TVS_ILLUMINANTS_HUNTERLAB', 'SDS_ILLUMINANTS', + 'TVS_ILLUMINANTS', 'CCS_LIGHT_SOURCES', 'SDS_LIGHT_SOURCES', 'SDS_LEFS', @@ -745,12 +789,12 @@ distributions: By default the shape used by ``colour.sd_constant``, ``colour.sd_zeros`` and ``colour.sd_ones`` is the one defined by the -``colour.DEFAULT_SPECTRAL_SHAPE`` attribute and based on *ASTM E308-15* +``colour.SPECTRAL_SHAPE_DEFAULT`` attribute and based on *ASTM E308-15* practise shape. .. code:: python - print(repr(colour.DEFAULT_SPECTRAL_SHAPE)) + print(repr(colour.SPECTRAL_SHAPE_DEFAULT)) .. code-block:: text @@ -855,7 +899,7 @@ appropriate quantity in truncation :cite:`CIETC1-482004h`: .. code:: python # Extrapolating the copied sample spectral distribution. - sd_copy.extrapolate(colour.SpectralShape(340, 830)) + sd_copy.extrapolate(colour.SpectralShape(340, 830, 1)) sd_copy[340], sd_copy[830] .. code-block:: text @@ -933,9 +977,11 @@ and ``right`` values: # Extrapolating the copied sample spectral distribution with *Linear* method. sd_copy.extrapolate( - colour.SpectralShape(340, 830), - extrapolator_args={'method': 'Linear', - 'right': 0}) + colour.SpectralShape(340, 830, 1), + extrapolator_kwargs={ + 'method': 'Linear', + 'right': 0 + }) sd_copy[340], sd_copy[830] .. code-block:: text @@ -950,7 +996,7 @@ missing values to match the requested shape: # Aligning the cloned sample spectral distribution. # The spectral distribution is first trimmed as above. - sd_copy.interpolate(colour.SpectralShape(400, 700)) + sd_copy.interpolate(colour.SpectralShape(400, 700, 1)) sd_copy.align(colour.SpectralShape(340, 830, 5)) sd_copy[340], sd_copy[830] @@ -1037,9 +1083,9 @@ this tutorial but the core capability can be described. [ 8., 90.], [ 9., 100.]], interpolator=KernelInterpolator, - interpolator_args={}, + interpolator_kwargs={}, extrapolator=Extrapolator, - extrapolator_args={u'right': nan, u'method': u'Constant', u'left': nan}) + extrapolator_kwargs={u'right': nan, u'method': u'Constant', u'left': nan}) .. code:: python @@ -1081,34 +1127,37 @@ computations are available, expanding to even more computations: .. code:: python # Displaying objects interacting directly with the *CIE XYZ* colourspace. - pprint([name for name in colour.__all__ if name.startswith('XYZ_to')]) + pprint(colour.COLOURSPACE_MODELS) .. code-block:: text - ['XYZ_to_ATD95', - 'XYZ_to_CAM16', - 'XYZ_to_CIECAM02', - 'XYZ_to_Hunt', - 'XYZ_to_LLAB', - 'XYZ_to_Nayatani95', - 'XYZ_to_RLAB', - 'XYZ_to_Hunter_Lab', - 'XYZ_to_Hunter_Rdab', - 'XYZ_to_IPT', - 'XYZ_to_JzAzBz', - 'XYZ_to_K_ab_HunterLab1966', - 'XYZ_to_Lab', - 'XYZ_to_Luv', - 'XYZ_to_OSA_UCS', - 'XYZ_to_RGB', - 'XYZ_to_UCS', - 'XYZ_to_UVW', - 'XYZ_to_hdr_CIELab', - 'XYZ_to_hdr_IPT', - 'XYZ_to_sRGB', - 'XYZ_to_xy', - 'XYZ_to_xyY', - 'XYZ_to_sd'] + ('CAM02LCD', + 'CAM02SCD', + 'CAM02UCS', + 'CAM16LCD', + 'CAM16SCD', + 'CAM16UCS', + 'CIE XYZ', + 'CIE xyY', + 'CIE Lab', + 'CIE LCHab', + 'CIE Luv', + 'CIE Luv uv', + 'CIE LCHuv', + 'CIE UCS', + 'CIE UCS uv', + 'CIE UVW', + 'DIN99', + 'Hunter Lab', + 'Hunter Rdab', + 'ICtCp', + 'IPT', + 'IgPgTg', + 'Jzazbz', + 'OSA UCS', + 'Oklab', + 'hdr-CIELAB', + 'hdr-IPT') Convert to Display Colours @@ -1133,7 +1182,7 @@ values in order to display them on screen: # Plotting the *sRGB* colourspace colour of the *Sample* spectral distribution. plot_single_colour_swatch( - ColourSwatch('Sample', RGB), + ColourSwatch(RGB, 'Sample'), text_kwargs={'size': 'x-large'}) .. image:: _static/Tutorial_Sample_Swatch.png @@ -1180,7 +1229,7 @@ various colour rendition charts: RGB = colour.XYZ_to_sRGB(XYZ / 100) plot_single_colour_swatch( - ColourSwatch(patch_name.title(), RGB), + ColourSwatch(RGB, patch_name.title()), text_kwargs={'size': 'x-large'}) .. image:: _static/Tutorial_Neutral5.png @@ -1249,6 +1298,6 @@ See More understand concepts of **Colour**. - The :doc:`advanced` page describes some advanced usage scenarios of **Colour**. -- The `How-To `__ +- The `Google Colab How-To `__ guide for **Colour** shows various techniques to solve specific problems and highlights some interesting use cases. diff --git a/docs/user-guide.rst b/docs/user-guide.rst new file mode 100644 index 0000000000..c7f80819b6 --- /dev/null +++ b/docs/user-guide.rst @@ -0,0 +1,17 @@ +User Guide +========== + +The user guide provides an overview of **Colour** and explains important +concepts and features, details can be found in the `API Reference `__. + +.. toctree:: + :maxdepth: 1 + + Installation + tutorial + how-to + basics + advanced + Contributing + Changes + bibliography diff --git a/pyproject.toml b/pyproject.toml index ffc35fc979..e05218d2f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "colour" -version = "0.3.16" +version = "0.4.0" description = "Colour Science for Python" license = "BSD-3-Clause" authors = [ "Colour Developers " ] @@ -37,97 +37,128 @@ 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" ] [tool.poetry.dependencies] -python = "~2.7 || ^3.6" -imageio = "*" -six = "*" -scipy = "^1.1.0" +python = ">= 3.8, < 3.11" +imageio = ">= 2, < 3" +numpy = ">= 1.19, < 2" +scipy = ">= 1.5, < 2" +typing-extensions = ">= 4, < 5" # Convenience for Google Colab. + +matplotlib = { version = ">= 3.2, != 3.5.0, != 3.5.1", optional = true } +networkx = { version = ">= 2, < 3", optional = true } +pandas = { version = ">= 1, < 2", optional = true } +pygraphviz = { version = ">= 1, < 2", optional = true } +scikit-learn = { version = ">= 1, < 2", optional = true } +tqdm = { version = ">= 4, < 5", optional = true } +trimesh = { version = ">= 3, < 4", optional = true } -"backports.functools_lru_cache" = { version = "*", optional = true } 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 } -mock = { version = "*", optional = true } # Development dependency. -networkx = { version = "*", optional = true } -nbformat = { version = "^4", optional = true } # Fixed development dependency for Python 2.7 support only. -nose = { version = "*", optional = true } # Development dependency. -numpy = { version = "*", optional = true } -pandas = { version = "*", optional = true } +mypy = { version = "*", optional = true } # Development dependency. pre-commit = { version = "*", optional = true } # Development dependency. -pygraphviz = { version = "*", optional = true } +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. restructuredtext-lint = { version = "*", optional = true } # Development dependency. -sphinx = { version = "<=3.1.2", 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. -tqdm = { version = "*", optional = true } twine = { 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 = "*" -mock = "*" -nbformat = "^4" -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 = "*" -tqdm = "*" twine = "*" -yapf = "0.23" [tool.poetry.extras] development = [ "biblib-simple", + "black", "coverage", "coveralls", "flake8", + "flynt", "invoke", "jupyter", - "mock", - "nbformat", - "nose", + "mypy", "pre-commit", + "pydata-sphinx-theme", + "pydocstyle", "pytest", + "pytest-cov", + "pyupgrade", "restructuredtext-lint", "sphinx", - "sphinx_rtd_theme", "sphinxcontrib-bibtex", "toml", "twine", - "yapf" ] graphviz = [ "pygraphviz" ] -optional = [ "networkx", "pandas", "tqdm" ] -plotting = [ "backports.functools_lru_cache", "matplotlib" ] +meshing = [ "trimesh" ] +optional = [ "networkx", "pandas", "scikit-learn", "tqdm" ] +plotting = [ "matplotlib" ] read-the-docs = [ - "mock", + "matplotlib", "networkx", "numpy", + "pydata-sphinx-theme", "pygraphviz", - "sphinxcontrib-bibtex" + "sphinxcontrib-bibtex", + "trimesh" ] +[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 + +[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/requirements.txt b/requirements.txt index e3d8f07a94..cdf455995b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,120 +1,151 @@ alabaster==0.7.12 -appdirs==1.4.4 -appnope==0.1.0 -argon2-cffi==20.1.0 -attrs==20.3.0 -Babel==2.9.0 +appnope==0.1.2 +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.functools-lru-cache==1.6.1 +beautifulsoup4==4.10.0 biblib-simple==0.1.1 -bleach==3.2.1 -certifi==2020.11.8 -cffi==1.14.4 -cfgv==3.0.0 -chardet==3.0.4 +black==22.1.0 +bleach==4.1.0 +certifi==2021.10.8 +cffi==1.15.0 +cfgv==3.3.1 +charset-normalizer==2.0.12 +click==8.0.4 colorama==0.4.4 -coverage==5.3 -coveralls==2.2.0 -cycler==0.10.0 -decorator==4.4.2 -defusedxml==0.6.0 -distlib==0.3.1 +coverage==6.3.1 +coveralls==3.3.1 +cycler==0.11.0 +debugpy==1.5.1 +decorator==5.1.1 +defusedxml==0.7.1 +distlib==0.3.4 docopt==0.6.2 -docutils==0.16 -entrypoints==0.3 -filelock==3.0.12 -flake8==3.8.4 -identify==1.5.10 -idna==2.10 -imageio==2.9.0 -imagesize==1.2.0 +docutils==0.17.1 +entrypoints==0.4 +executing==0.8.2 +filelock==3.6.0 +flake8==4.0.1 +flynt==0.76 +identify==2.4.10 +idna==3.3 +imageio==2.16.0 +imagesize==1.3.0 +importlib-metadata==4.11.1 iniconfig==1.1.1 -invoke==1.4.1 -ipykernel==5.3.4 -ipython==7.16.1 +invoke==1.6.0 +ipykernel==6.9.1 +ipython==8.0.1 ipython-genutils==0.2.0 -ipywidgets==7.5.1 -jedi==0.17.2 -Jinja2==2.11.2 -jsonschema==3.2.0 +ipywidgets==7.6.5 +jedi==0.18.1 +Jinja2==3.0.3 +joblib==1.1.0 +jsonschema==4.4.0 jupyter==1.0.0 -jupyter-client==6.1.7 -jupyter-console==6.2.0 -jupyter-core==4.7.0 -keyring==21.5.0 -kiwisolver==1.3.1 +jupyter-client==7.1.2 +jupyter-console==6.4.0 +jupyter-core==4.9.2 +jupyterlab-pygments==0.1.2 +jupyterlab-widgets==1.0.2 +keyring==23.5.0 +kiwisolver==1.3.2 latexcodec==2.0.1 -MarkupSafe==1.1.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 -mock==4.0.2 -nbconvert==5.6.1 -nbformat==4.4.0 -networkx==2.5 -nodeenv==1.5.0 -nose==1.3.7 -notebook==6.1.5 -numpy==1.19.4 -oset==0.1.3 -packaging==20.4 -pandas==0.25.3 -pandocfilters==1.4.3 -parso==0.7.1 +mypy==0.931 +mypy-extensions==0.4.3 +nbclient==0.5.11 +nbconvert==6.4.2 +nbformat==5.1.3 +nest-asyncio==1.5.4 +networkx==2.6.3 +nodeenv==1.6.0 +notebook==6.4.8 +numpy==1.22.2 +packaging==21.3 +pandas==1.4.1 +pandocfilters==1.5.0 +parso==0.8.3 +pathspec==0.9.0 pexpect==4.8.0 pickleshare==0.7.5 -Pillow==8.0.1 -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 -pybtex==0.23.0 -pybtex-docutils==0.2.2 -pycodestyle==2.6.0 -pycparser==2.20 -pyflakes==2.2.0 -Pygments==2.7.2 -pygraphviz==1.6 -pyparsing==2.4.7 -pyrsistent==0.17.3 -pytest==6.1.2 -python-dateutil==2.8.1 -pytz==2020.4 -PyYAML==5.3.1 -pyzmq==20.0.0 -qtconsole==5.0.1 -QtPy==1.9.0 -readme-renderer==28.0 -requests==2.25.0 +Pillow==9.0.1 +pip==21.3.1 +pkginfo==1.8.2 +platformdirs==2.5.1 +pluggy==1.0.0 +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.11.2 +pygraphviz==1.9 +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.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.4.0 -scipy==1.5.4 -Send2Trash==1.5.0 -six==1.15.0 -snowballstemmer==2.0.0 -Sphinx==3.1.2 -sphinx-rtd-theme==0.5.0 +rfc3986==2.0.0 +scikit-learn==1.0.2 +scipy==1.8.0 +Send2Trash==1.8.0 +setuptools==59.6.0 +six==1.16.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==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.1 -testpath==0.4.4 +sphinxcontrib-serializinghtml==1.1.5 +stack-data==0.2.0 +terminado==0.13.1 +testpath==0.5.0 +threadpoolctl==3.1.0 +tokenize-rt==4.2.1 toml==0.10.2 +tomli==2.0.1 tornado==6.1 -tqdm==4.53.0 -traitlets==4.3.3 -twine==3.2.0 -urllib3==1.26.2 -virtualenv==20.2.1 +tqdm==4.62.3 +traitlets==5.1.1 +trimesh==3.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 -widgetsnbextension==3.5.1 -yapf==0.23.0 +wheel==0.37.0 +widgetsnbextension==3.5.2 +zipp==3.7.0 diff --git a/setup.py b/setup.py index dedfc620be..2283bc691a 100644 --- a/setup.py +++ b/setup.py @@ -1,173 +1,200 @@ -# -*- coding: utf-8 -*- +""" +Colour - Setup +============== +""" + import codecs from setuptools import setup -package_dir = \ -{'': '.'} +package_dir = {"": "."} -packages = \ -['colour', - 'colour.adaptation', - 'colour.adaptation.datasets', - 'colour.adaptation.tests', - 'colour.algebra', - 'colour.algebra.coordinates', - 'colour.algebra.coordinates.tests', - 'colour.algebra.tests', - 'colour.appearance', - 'colour.appearance.tests', - 'colour.biochemistry', - 'colour.biochemistry.tests', - 'colour.blindness', - 'colour.blindness.datasets', - 'colour.blindness.tests', - 'colour.characterisation', - 'colour.characterisation.datasets', - 'colour.characterisation.datasets.cameras', - 'colour.characterisation.datasets.cameras.dslr', - 'colour.characterisation.datasets.colour_checkers', - 'colour.characterisation.datasets.displays', - 'colour.characterisation.datasets.displays.crt', - 'colour.characterisation.datasets.displays.lcd', - 'colour.characterisation.datasets.filters', - 'colour.characterisation.datasets.lenses', - 'colour.characterisation.tests', - 'colour.colorimetry', - 'colour.colorimetry.datasets', - 'colour.colorimetry.datasets.illuminants', - 'colour.colorimetry.datasets.light_sources', - 'colour.colorimetry.tests', - 'colour.constants', - 'colour.continuous', - 'colour.continuous.tests', - 'colour.contrast', - 'colour.contrast.tests', - 'colour.corresponding', - 'colour.corresponding.datasets', - 'colour.corresponding.tests', - 'colour.difference', - 'colour.difference.tests', - 'colour.examples', - 'colour.examples.adaptation', - 'colour.examples.algebra', - 'colour.examples.appearance', - 'colour.examples.blindness', - 'colour.examples.characterisation', - 'colour.examples.colorimetry', - 'colour.examples.contrast', - 'colour.examples.corresponding', - 'colour.examples.difference', - 'colour.examples.geometry', - 'colour.examples.graph', - 'colour.examples.io', - 'colour.examples.models', - 'colour.examples.notation', - 'colour.examples.phenomena', - 'colour.examples.plotting', - 'colour.examples.quality', - 'colour.examples.recovery', - 'colour.examples.temperature', - 'colour.examples.volume', - 'colour.geometry', - 'colour.geometry.tests', - 'colour.graph', - 'colour.graph.tests', - 'colour.io', - 'colour.io.luts', - 'colour.io.luts.tests', - 'colour.io.tests', - 'colour.models', - 'colour.models.datasets', - 'colour.models.rgb', - 'colour.models.rgb.datasets', - 'colour.models.rgb.tests', - 'colour.models.rgb.transfer_functions', - 'colour.models.rgb.transfer_functions.tests', - 'colour.models.tests', - 'colour.notation', - 'colour.notation.datasets', - 'colour.notation.datasets.munsell', - 'colour.notation.tests', - 'colour.phenomena', - 'colour.phenomena.tests', - 'colour.plotting', - 'colour.plotting.datasets', - 'colour.plotting.tests', - 'colour.plotting.tm3018', - 'colour.plotting.tm3018.tests', - 'colour.quality', - 'colour.quality.datasets', - 'colour.quality.tests', - 'colour.recovery', - 'colour.recovery.datasets', - 'colour.recovery.tests', - 'colour.temperature', - 'colour.temperature.tests', - 'colour.utilities', - 'colour.utilities.tests', - 'colour.volume', - 'colour.volume.datasets', - 'colour.volume.tests'] +packages = [ + "colour", + "colour.adaptation", + "colour.adaptation.datasets", + "colour.adaptation.tests", + "colour.algebra", + "colour.algebra.coordinates", + "colour.algebra.coordinates.tests", + "colour.algebra.tests", + "colour.appearance", + "colour.appearance.tests", + "colour.biochemistry", + "colour.biochemistry.tests", + "colour.blindness", + "colour.blindness.datasets", + "colour.blindness.tests", + "colour.characterisation", + "colour.characterisation.datasets", + "colour.characterisation.datasets.cameras", + "colour.characterisation.datasets.cameras.dslr", + "colour.characterisation.datasets.colour_checkers", + "colour.characterisation.datasets.displays", + "colour.characterisation.datasets.displays.crt", + "colour.characterisation.datasets.displays.lcd", + "colour.characterisation.datasets.filters", + "colour.characterisation.datasets.lenses", + "colour.characterisation.tests", + "colour.colorimetry", + "colour.colorimetry.datasets", + "colour.colorimetry.datasets.illuminants", + "colour.colorimetry.datasets.light_sources", + "colour.colorimetry.tests", + "colour.constants", + "colour.continuous", + "colour.continuous.tests", + "colour.contrast", + "colour.contrast.tests", + "colour.corresponding", + "colour.corresponding.datasets", + "colour.corresponding.tests", + "colour.difference", + "colour.difference.tests", + "colour.examples", + "colour.examples.adaptation", + "colour.examples.algebra", + "colour.examples.appearance", + "colour.examples.blindness", + "colour.examples.characterisation", + "colour.examples.colorimetry", + "colour.examples.contrast", + "colour.examples.corresponding", + "colour.examples.difference", + "colour.examples.geometry", + "colour.examples.graph", + "colour.examples.io", + "colour.examples.models", + "colour.examples.notation", + "colour.examples.phenomena", + "colour.examples.plotting", + "colour.examples.quality", + "colour.examples.recovery", + "colour.examples.temperature", + "colour.examples.volume", + "colour.geometry", + "colour.geometry.tests", + "colour.graph", + "colour.graph.tests", + "colour.hints", + "colour.io", + "colour.io.luts", + "colour.io.luts.tests", + "colour.io.tests", + "colour.models", + "colour.models.datasets", + "colour.models.rgb", + "colour.models.rgb.datasets", + "colour.models.rgb.datasets.tests", + "colour.models.rgb.tests", + "colour.models.rgb.transfer_functions", + "colour.models.rgb.transfer_functions.tests", + "colour.models.tests", + "colour.notation", + "colour.notation.datasets", + "colour.notation.datasets.munsell", + "colour.notation.tests", + "colour.phenomena", + "colour.phenomena.tests", + "colour.plotting", + "colour.plotting.datasets", + "colour.plotting.tests", + "colour.plotting.tm3018", + "colour.plotting.tm3018.tests", + "colour.quality", + "colour.quality.datasets", + "colour.quality.tests", + "colour.recovery", + "colour.recovery.datasets", + "colour.recovery.tests", + "colour.temperature", + "colour.temperature.tests", + "colour.utilities", + "colour.utilities.tests", + "colour.volume", + "colour.volume.datasets", + "colour.volume.tests", +] -package_data = \ -{'': ['*'], - 'colour.appearance.tests': ['fixtures/*'], - 'colour.characterisation.datasets': ['rawtoaces/*'], - 'colour.examples.io': ['resources/*'], - 'colour.examples.plotting': ['resources/*'], - 'colour.io.luts.tests': ['resources/cinespace/*', - 'resources/iridas_cube/*', - 'resources/resolve_cube/*', - 'resources/sony_spi1d/*', - 'resources/sony_spi3d/*'], - 'colour.io.tests': ['resources/*'], - 'colour.plotting.tm3018': ['resources/*']} +package_data = { + "": ["*"], + "colour.characterisation.datasets": ["rawtoaces/*"], + "colour.examples.io": ["resources/*"], + "colour.examples.plotting": ["resources/*"], + "colour.io.luts.tests": [ + "resources/cinespace/*", + "resources/iridas_cube/*", + "resources/resolve_cube/*", + "resources/sony_spi1d/*", + "resources/sony_spi3d/*", + "resources/sony_spimtx/*", + ], + "colour.io.tests": ["resources/*"], + "colour.plotting.tm3018": ["resources/*"], +} -install_requires = \ -['imageio', 'scipy>=1.1.0,<2.0.0', 'six'] +install_requires = [ + "imageio>=2,<3", + "scipy>=1.5,<2", + "typing-extensions>=4,<5", +] -extras_require = \ -{'development': ['biblib-simple', - 'coverage', - 'coveralls', - 'flake8', - 'invoke', - 'jupyter', - 'mock', - 'nbformat>=4,<5', - 'nose', - 'pre-commit', - 'pytest', - 'restructuredtext-lint', - 'sphinx<=3.1.2', - 'sphinx_rtd_theme', - 'sphinxcontrib-bibtex', - 'toml', - 'twine', - 'yapf==0.23'], - 'graphviz': ['pygraphviz'], - 'optional': ['networkx', 'pandas', 'tqdm'], - 'plotting': ['backports.functools_lru_cache', 'matplotlib'], - 'read-the-docs': ['mock', - 'networkx', - 'numpy', - 'pygraphviz', - 'sphinxcontrib-bibtex']} +extras_require = { + ':extra == "read-the-docs"': ["numpy>=1.19,<2"], + "development": [ + "biblib-simple", + "black", + "coverage!=6.3", + "coveralls", + "flake8", + "flynt", + "invoke", + "jupyter", + "mypy", + "pre-commit", + "pydata-sphinx-theme", + "pydocstyle", + "pytest", + "pytest-cov", + "pyupgrade", + "restructuredtext-lint", + "sphinx>=4,<5", + "sphinxcontrib-bibtex", + "toml", + "twine", + ], + "graphviz": ["pygraphviz>=1,<2"], + "meshing": ["trimesh>=3,<4"], + "optional": [ + "networkx>=2,<3", + "pandas>=1,<2", + "scikit-learn>=1,<2", + "tqdm>=4,<5", + ], + "plotting": ["matplotlib>=3.2,!=3.5.0,!=3.5.1"], + "read-the-docs": [ + "matplotlib>=3.2,!=3.5.0,!=3.5.1", + "networkx>=2,<3", + "pygraphviz>=1,<2", + "trimesh>=3,<4", + "pydata-sphinx-theme", + "sphinxcontrib-bibtex", + ], +} setup( - name='colour-science', - version='0.3.16', - description='Colour Science 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-science", + version="0.4.0", + description="Colour Science 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/", package_dir=package_dir, packages=packages, 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.8,<3.11", ) diff --git a/tasks.py b/tasks.py index bda9554689..873f92db3b 100644 --- a/tasks.py +++ b/tasks.py @@ -1,396 +1,437 @@ -# -*- coding: utf-8 -*- """ Invoke - Tasks ============== """ -from __future__ import unicode_literals +from __future__ import annotations -import sys -try: - import biblib.bib -except ImportError: - pass +import biblib.bib import fnmatch import os import re import toml import uuid -from invoke import task +from invoke import Context, task import colour +from colour.hints import Boolean from colour.utilities import message_box -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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 = colour.__application_name__ +APPLICATION_NAME: str = colour.__application_name__ -APPLICATION_VERSION = colour.__version__ +APPLICATION_VERSION: str = colour.__version__ -PYTHON_PACKAGE_NAME = colour.__name__ +PYTHON_PACKAGE_NAME: str = colour.__name__ -PYPI_PACKAGE_NAME = 'colour-science' +PYPI_PACKAGE_NAME: str = "colour-science" -BIBLIOGRAPHY_NAME = 'BIBLIOGRAPHY.bib' +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]) + + org_task_argspec = invoke.tasks.Task.argspec + + def patched_task_argspec(*args, **kwargs): + with patch( + target="inspect.getargspec", new=patched_inspect_getargspec + ): + return org_task_argspec(*args, **kwargs) + + invoke.tasks.Task.argspec = patched_task_argspec + + +_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. + Clean 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'] + message_box("Cleaning project...") + + 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('**/*.pyc') + 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)) + ctx.run(f"rm -rf {pattern}") @task -def formatting(ctx, yapf=False, asciify=True, bibtex=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. + Convert unicode characters to ASCII and cleanup the *BibTeX* file. Parameters ---------- - ctx : invoke.context.Context + ctx Context. - yapf : bool, optional - 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: - 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 and sys.version_info[:2] >= (3, 2): + if bibtex: 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() + entries = ( + biblib.bib.Parser().parse(bibtex_file.read()).get_entries() + ) - for entry in sorted(bibtex.values(), key=lambda x: x.key): + 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==|colour-science==" ' + "> requirements.txt" + ) @task(clean, preflight, docs, todo, requirements) -def build(ctx): +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 ---------- - ctx : invoke.context.Context + ctx Context. - - Returns - ------- - bool - Task success. """ - message_box('Building...') - if 'modified: pyproject.toml' in ctx.run('git status').stdout: + message_box("Building...") + if "modified: pyproject.toml" in ctx.run("git status").stdout: raise RuntimeError( - 'Please commit your changes to the "pyproject.toml" file!') - - pyproject_content = toml.load('pyproject.toml') - pyproject_content['tool']['poetry']['name'] = PYPI_PACKAGE_NAME - pyproject_content['tool']['poetry']['packages'] = [{ - 'include': PYTHON_PACKAGE_NAME, - 'from': '.' - }] - with open('pyproject.toml', 'w') as pyproject_file: + 'Please commit your changes to the "pyproject.toml" file!' + ) + + pyproject_content = toml.load("pyproject.toml") + pyproject_content["tool"]["poetry"]["name"] = PYPI_PACKAGE_NAME + pyproject_content["tool"]["poetry"]["packages"] = [ + {"include": PYTHON_PACKAGE_NAME, "from": "."} + ] + with open("pyproject.toml", "w") as pyproject_file: toml.dump(pyproject_content, pyproject_file) - if 'modified: README.rst' in ctx.run('git status').stdout: + if "modified: README.rst" in ctx.run("git status").stdout: raise RuntimeError( - 'Please commit your changes to the "README.rst" file!') + 'Please commit your changes to the "README.rst" file!' + ) - with open('README.rst', 'r') as readme_file: + with open("README.rst") as readme_file: readme_content = readme_file.read() - with open('README.rst', 'w') as readme_file: + with open("README.rst", "w") as readme_file: readme_file.write( re.sub( - ('(\\.\\. begin-trim-long-description.*?' - '\\.\\. end-trim-long-description)'), - '', + ( + "(\\.\\. begin-trim-long-description.*?" + "\\.\\. end-trim-long-description)" + ), + "", readme_content, - flags=re.DOTALL)) + flags=re.DOTALL, + ) + ) - ctx.run('poetry build') - ctx.run('git checkout -- pyproject.toml') - ctx.run('git checkout -- README.rst') + ctx.run("poetry build") + ctx.run("git checkout -- pyproject.toml") + ctx.run("git checkout -- README.rst") - 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 = [] @@ -398,156 +439,171 @@ def build(ctx): 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", + ( + '"""\n' + "Colour - Setup\n" + "==============\n" + '"""\n\n' + "import codecs\n" + "from 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("poetry run pre-commit run --files setup.py || true") + + ctx.run("twine check dist/*") @task -def virtualise(ctx, tests=True): +def virtualise(ctx: Context, tests: Boolean = True): """ Create a virtual environment for the project build. Parameters ---------- - ctx : invoke.context.Context + ctx Context. - tests : bool, optional + tests Whether to run tests on the virtual environment. - - Returns - ------- - bool - Task success. """ - 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)) + 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}") with ctx.cd(unique_name): - ctx.run('poetry env use 3') - ctx.run('poetry install --extras "optional plotting"') - ctx.run('source $(poetry env info -p)/bin/activate') - ctx.run('python -c "import imageio;' - 'imageio.plugins.freeimage.download()"') + ctx.run( + 'poetry install --extras "graphviz meshing optional 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 -def tag(ctx): +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 ---------- - ctx : invoke.context.Context + ctx Context. - - Returns - ------- - bool - Task success. """ - 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(1) - minor_version = re.search("__minor_version__\\s+=\\s+'(.*)'", - file_content).group(1) - change_version = re.search("__change_version__\\s+=\\s+'(.*)'", - file_content).group(1) - - 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') + major_version = re.search( + '__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 + ) + change_version = re.search( + '__change_version__\\s+=\\s+"(.*)"', file_content + ).group( # type: ignore[union-attr] + 1 + ) + + 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") tags = set() for remote_tag in remote_tags: tags.add( - remote_tag.split('refs/tags/')[1].replace('refs/tags/', '^{}')) - tags = sorted(list(tags)) - assert 'v{0}'.format(version) not in tags, ( - 'A "{0}" "v{1}" tag already exists in remote repository!'.format( - PYTHON_PACKAGE_NAME, version)) + remote_tag.split("refs/tags/")[1].replace("refs/tags/", "^{}") + ) + version_tags = sorted(list(tags)) + 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) -def release(ctx): +def release(ctx: Context): """ - Releases the project to *Pypi* with *Twine*. + Release the project to *Pypi* with *Twine*. Parameters ---------- - ctx : invoke.context.Context + ctx Context. - - Returns - ------- - bool - Task success. """ - 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 -def sha256(ctx): +def sha256(ctx: Context): """ - Computes the project *Pypi* package *sha256* with *OpenSSL*. + Compute the project *Pypi* package *sha256* with *OpenSSL*. Parameters ---------- - ctx : invoke.context.Context + ctx Context. - - Returns - ------- - bool - Task success. """ 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 3fe8c57f35..a927e78514 100755 --- a/utilities/export_todo.py +++ b/utilities/export_todo.py @@ -1,23 +1,25 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Export TODOs ============ """ -from __future__ import division, unicode_literals +from __future__ import annotations import codecs import os -from collections import OrderedDict -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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'] +__all__ = [ + "TODO_FILE_TEMPLATE", + "extract_todo_items", + "export_todo_items", +] TODO_FILE_TEMPLATE = """ Colour - TODO @@ -32,38 +34,40 @@ ----- | **Colour** by Colour Developers -| Copyright © 2013-2020 – Colour Developers – \ +| Copyright 2013 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 \ `__ -""" [1:] +"""[ + 1: +] -def extract_todo_items(root_directory): +def extract_todo_items(root_directory: str) -> dict: """ - Extracts the TODO items from given directory. + Extract the TODO items from given directory. Parameters ---------- - root_directory : unicode + root_directory Directory to extract the TODO items from. Returns ------- - OrderedDict + :class:`dict` TODO items. """ - todo_items = OrderedDict() - for root, dirnames, filenames in os.walk(root_directory): + todo_items = {} + 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 @@ -71,53 +75,52 @@ def extract_todo_items(root_directory): 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 + line_number = i + 1 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 = [] return todo_items -def export_todo_items(todo_items, file_path): +def export_todo_items(todo_items: dict, file_path: str): """ - Exports TODO items to given file. + Export 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. """ 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')), - os.path.join('..', 'TODO.rst')) + extract_todo_items(os.path.join("..", "colour")), + os.path.join("..", "TODO.rst"), + ) diff --git a/utilities/generate_plots.py b/utilities/generate_plots.py index 7afca2f36d..d251fc5439 100755 --- a/utilities/generate_plots.py +++ b/utilities/generate_plots.py @@ -1,96 +1,146 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Generate Plots ============== """ -from __future__ import division, unicode_literals +from __future__ import annotations import matplotlib -matplotlib.use('AGG') +matplotlib.use("AGG") import matplotlib.pyplot as plt # noqa import numpy as np # noqa import os # noqa +import trimesh # noqa import colour # noqa from colour.characterisation import SDS_COLOURCHECKERS # noqa from colour.colorimetry import ( # noqa - SDS_ILLUMINANTS, SDS_LIGHT_SOURCES, SDS_LEFS_PHOTOPIC, SDS_LEFS_SCOTOPIC, - MSDS_CMFS_STANDARD_OBSERVER, SpectralDistribution, SpectralShape, - sd_blackbody, sd_mesopic_luminous_efficiency_function, sd_to_XYZ) + SDS_ILLUMINANTS, + SDS_LIGHT_SOURCES, + SDS_LEFS_PHOTOPIC, + SDS_LEFS_SCOTOPIC, + MSDS_CMFS_STANDARD_OBSERVER, + SpectralDistribution, + SpectralShape, + sd_blackbody, + sd_mesopic_luminous_efficiency_function, + sd_to_XYZ, +) +from colour.geometry import primitive_cube # noqa from colour.io import read_image # noqa -from colour.models import sRGB_to_XYZ, XYZ_to_sRGB, XYZ_to_xy # noqa +from colour.models import ( # noqa + RGB_COLOURSPACE_sRGB, + RGB_to_XYZ, + sRGB_to_XYZ, + XYZ_to_sRGB, + XYZ_to_xy, +) from colour.plotting import ( # noqa - colour_style, ColourSwatch, plot_automatic_colour_conversion_graph, - plot_blackbody_colours, plot_blackbody_spectral_radiance, - plot_chromaticity_diagram_CIE1931, plot_chromaticity_diagram_CIE1960UCS, - plot_chromaticity_diagram_CIE1976UCS, plot_constant_hue_loci, + colour_style, + ColourSwatch, + plot_automatic_colour_conversion_graph, + plot_blackbody_colours, + plot_blackbody_spectral_radiance, + plot_chromaticity_diagram_CIE1931, + plot_chromaticity_diagram_CIE1960UCS, + plot_chromaticity_diagram_CIE1976UCS, + plot_constant_hue_loci, plot_corresponding_chromaticities_prediction, plot_cvd_simulation_Machado2009, plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931, plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS, - plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS, plot_image, - plot_multi_cctfs, plot_multi_cmfs, plot_multi_colour_checkers, - plot_multi_colour_swatches, plot_multi_functions, - plot_multi_illuminant_sds, plot_multi_lightness_functions, - plot_multi_luminance_functions, plot_multi_munsell_value_functions, + plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS, + plot_image, + plot_multi_cctfs, + plot_multi_cmfs, + plot_multi_colour_checkers, + plot_multi_colour_swatches, + plot_multi_functions, + plot_multi_illuminant_sds, + plot_multi_lightness_functions, + plot_multi_luminance_functions, + plot_multi_munsell_value_functions, plot_multi_sds_colour_quality_scales_bars, - plot_multi_sds_colour_rendering_indexes_bars, plot_multi_sds, + plot_multi_sds_colour_rendering_indexes_bars, + plot_multi_sds, plot_planckian_locus_in_chromaticity_diagram_CIE1931, plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS, plot_pointer_gamut, plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931, plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS, plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS, + plot_RGB_colourspace_section, plot_RGB_colourspaces_gamuts, plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931, plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS, - plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS, plot_RGB_scatter, + plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS, + plot_RGB_scatter, plot_sds_in_chromaticity_diagram_CIE1931, plot_sds_in_chromaticity_diagram_CIE1960UCS, - plot_sds_in_chromaticity_diagram_CIE1976UCS, plot_single_cctf, - plot_single_cmfs, plot_single_colour_checker, plot_single_colour_swatch, - plot_single_function, plot_single_illuminant_sd, - plot_single_lightness_function, plot_single_luminance_function, + plot_sds_in_chromaticity_diagram_CIE1976UCS, + plot_single_cctf, + plot_single_cmfs, + plot_single_colour_checker, + plot_single_colour_swatch, + plot_single_function, + plot_single_illuminant_sd, + plot_single_lightness_function, + plot_single_luminance_function, plot_single_munsell_value_function, plot_single_sd_colour_quality_scale_bars, plot_single_sd_colour_rendering_index_bars, - plot_single_sd_colour_rendition_report, plot_single_sd_rayleigh_scattering, - plot_single_sd, plot_the_blue_sky, plot_visible_spectrum, render) + plot_single_sd_colour_rendition_report, + plot_single_sd_rayleigh_scattering, + plot_single_sd, + plot_the_blue_sky, + plot_visible_spectrum, + plot_visible_spectrum_section, + render, +) from colour.plotting.diagrams import ( # noqa - plot_spectral_locus, plot_chromaticity_diagram_colours, - plot_chromaticity_diagram, plot_sds_in_chromaticity_diagram) + plot_spectral_locus, + plot_chromaticity_diagram_colours, + plot_chromaticity_diagram, + plot_sds_in_chromaticity_diagram, +) from colour.plotting.models import ( # noqa plot_RGB_colourspaces_in_chromaticity_diagram, plot_RGB_chromaticities_in_chromaticity_diagram, - plot_ellipses_MacAdam1942_in_chromaticity_diagram) + plot_ellipses_MacAdam1942_in_chromaticity_diagram, +) from colour.plotting.quality import plot_colour_quality_bars # noqa +from colour.plotting.section import ( # noqa + plot_hull_section_colours, + plot_hull_section_contour, +) from colour.plotting.temperature import ( # noqa - plot_planckian_locus, plot_planckian_locus_CIE1931, - plot_planckian_locus_CIE1960UCS, - plot_planckian_locus_in_chromaticity_diagram) + plot_planckian_locus, + plot_planckian_locus_in_chromaticity_diagram, +) from colour.quality import colour_quality_scale # noqa from colour.utilities import domain_range_scale, filter_warnings # noqa -__copyright__ = 'Copyright (C) 2013-2020 - 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 2013 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__ = ['generate_documentation_plots'] +__all__ = [ + "generate_documentation_plots", +] -def generate_documentation_plots(output_directory): +def generate_documentation_plots(output_directory: str): """ - Generates documentation plots. + Generate documentation plots. Parameters ---------- - output_directory : unicode + output_directory Output directory. """ @@ -103,29 +153,32 @@ def generate_documentation_plots(output_directory): # ************************************************************************* # "README.rst" # ************************************************************************* - filename = os.path.join(output_directory, - 'Examples_Colour_Automatic_Conversion_Graph.png') + filename = os.path.join( + output_directory, "Examples_Colour_Automatic_Conversion_Graph.png" + ) plot_automatic_colour_conversion_graph(filename) arguments = { - 'tight_layout': - True, - 'transparent_background': - True, - 'filename': - os.path.join(output_directory, - 'Examples_Plotting_Visible_Spectrum.png') + "tight_layout": True, + "transparent_background": True, + "filename": os.path.join( + output_directory, "Examples_Plotting_Visible_Spectrum.png" + ), } plt.close( - plot_visible_spectrum('CIE 1931 2 Degree Standard Observer', - **arguments)[0]) - - arguments['filename'] = os.path.join( - output_directory, 'Examples_Plotting_Illuminant_F1_SD.png') - plt.close(plot_single_illuminant_sd('FL1', **arguments)[0]) - - arguments['filename'] = os.path.join(output_directory, - 'Examples_Plotting_Blackbodies.png') + plot_visible_spectrum( + "CIE 1931 2 Degree Standard Observer", **arguments + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Examples_Plotting_Illuminant_F1_SD.png" + ) + plt.close(plot_single_illuminant_sd("FL1", **arguments)[0]) + + arguments["filename"] = os.path.join( + output_directory, "Examples_Plotting_Blackbodies.png" + ) blackbody_sds = [ sd_blackbody(i, SpectralShape(0, 10000, 10)) for i in range(1000, 15000, 1000) @@ -133,121 +186,174 @@ def generate_documentation_plots(output_directory): plt.close( plot_multi_sds( blackbody_sds, - y_label='W / (sr m$^2$) / m', - plot_kwargs={'use_sd_colours': True}, - normalise_sds_colours=True, - legend_location='upper right', - bounding_box=(0, 1250, 0, 2.5e15), - **arguments)[0]) - - arguments['filename'] = os.path.join( - output_directory, 'Examples_Plotting_Cone_Fundamentals.png') + y_label="W / (sr m$^2$) / m", + plot_kwargs={"use_sd_colours": True, "normalise_sd_colours": True}, + legend_location="upper right", + bounding_box=(0, 1250, 0, 2.5e6), + **arguments, + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Examples_Plotting_Cone_Fundamentals.png" + ) plt.close( plot_single_cmfs( - 'Stockman & Sharpe 2 Degree Cone Fundamentals', - y_label='Sensitivity', + "Stockman & Sharpe 2 Degree Cone Fundamentals", + y_label="Sensitivity", bounding_box=(390, 870, 0, 1.1), - **arguments)[0]) + **arguments, + )[0] + ) - arguments['filename'] = os.path.join( - output_directory, 'Examples_Plotting_Luminous_Efficiency.png') + arguments["filename"] = os.path.join( + output_directory, "Examples_Plotting_Luminous_Efficiency.png" + ) plt.close( plot_multi_sds( - (sd_mesopic_luminous_efficiency_function(0.2), - SDS_LEFS_PHOTOPIC['CIE 1924 Photopic Standard Observer'], - SDS_LEFS_SCOTOPIC['CIE 1951 Scotopic Standard Observer']), - y_label='Luminous Efficiency', - legend_location='upper right', + ( + sd_mesopic_luminous_efficiency_function(0.2), + SDS_LEFS_PHOTOPIC["CIE 1924 Photopic Standard Observer"], + SDS_LEFS_SCOTOPIC["CIE 1951 Scotopic Standard Observer"], + ), + y_label="Luminous Efficiency", + legend_location="upper right", y_tighten=True, - margins=(0, 0, 0, .1), - **arguments)[0]) - - arguments['filename'] = os.path.join( - output_directory, 'Examples_Plotting_BabelColor_Average.png') + margins=(0, 0, 0, 0.1), + **arguments, + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Examples_Plotting_BabelColor_Average.png" + ) plt.close( plot_multi_sds( - SDS_COLOURCHECKERS['BabelColor Average'].values(), - plot_kwargs={'use_sd_colours': True}, - title=('BabelColor Average - ' - 'Spectral Distributions'), - **arguments)[0]) - - arguments['filename'] = os.path.join( - output_directory, 'Examples_Plotting_ColorChecker_2005.png') + SDS_COLOURCHECKERS["BabelColor Average"].values(), + plot_kwargs={"use_sd_colours": True}, + title=("BabelColor Average - " "Spectral Distributions"), + **arguments, + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Examples_Plotting_ColorChecker_2005.png" + ) plt.close( plot_single_colour_checker( - 'ColorChecker 2005', text_kwargs={'visible': False}, - **arguments)[0]) + "ColorChecker 2005", text_kwargs={"visible": False}, **arguments + )[0] + ) - arguments['filename'] = os.path.join( - output_directory, 'Examples_Plotting_Chromaticities_Prediction.png') + arguments["filename"] = os.path.join( + output_directory, "Examples_Plotting_Chromaticities_Prediction.png" + ) plt.close( plot_corresponding_chromaticities_prediction( - 2, 'Von Kries', 'Bianco 2010', **arguments)[0]) + 2, "Von Kries", **arguments + )[0] + ) - arguments['filename'] = os.path.join( + arguments["filename"] = os.path.join( output_directory, - 'Examples_Plotting_CCT_CIE_1960_UCS_Chromaticity_Diagram.png') - plt.close( - plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS( - ['A', 'B', 'C'], **arguments)[0]) - - arguments['filename'] = os.path.join( - output_directory, - 'Examples_Plotting_Chromaticities_CIE_1931_Chromaticity_Diagram.png') + "Examples_Plotting_Chromaticities_CIE_1931_Chromaticity_Diagram.png", + ) RGB = np.random.random((32, 32, 3)) plt.close( plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931( RGB, - 'ITU-R BT.709', - colourspaces=['ACEScg', 'S-Gamut'], + "ITU-R BT.709", + colourspaces=["ACEScg", "S-Gamut"], show_pointer_gamut=True, - **arguments)[0]) + **arguments, + )[0] + ) - arguments['filename'] = os.path.join(output_directory, - 'Examples_Plotting_CRI.png') + arguments["filename"] = os.path.join( + output_directory, "Examples_Plotting_CRI.png" + ) + plt.close( + plot_single_sd_colour_rendering_index_bars( + SDS_ILLUMINANTS["FL2"], **arguments + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Examples_Plotting_Colour_Rendition_Report.png" + ) + plt.close( + plot_single_sd_colour_rendition_report( + SDS_ILLUMINANTS["FL2"], **arguments + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Examples_Plotting_Plot_Visible_Spectrum_Section.png" + ) + plt.close( + plot_visible_spectrum_section( + section_colours="RGB", section_opacity=0.15, **arguments + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Examples_Plotting_Plot_RGB_Colourspace_Section.png" + ) plt.close( - plot_single_sd_colour_rendering_index_bars(SDS_ILLUMINANTS['FL2'], - **arguments)[0]) + plot_RGB_colourspace_section( + "sRGB", section_colours="RGB", section_opacity=0.15, **arguments + )[0] + ) - arguments['filename'] = os.path.join( - output_directory, 'Examples_Plotting_Colour_Rendition_Report.png') + arguments["filename"] = os.path.join( + output_directory, + "Examples_Plotting_CCT_CIE_1960_UCS_Chromaticity_Diagram.png", + ) plt.close( - plot_single_sd_colour_rendition_report(SDS_ILLUMINANTS['FL2'], - **arguments)[0]) + plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS( + ["A", "B", "C"], **arguments + )[0] + ) # ************************************************************************* # Documentation # ************************************************************************* - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_CVD_Simulation_Machado2009.png') + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_CVD_Simulation_Machado2009.png" + ) plt.close(plot_cvd_simulation_Machado2009(RGB, **arguments)[0]) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Single_Colour_Checker.png') - plt.close(plot_single_colour_checker('ColorChecker 2005', **arguments)[0]) + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Single_Colour_Checker.png" + ) + plt.close(plot_single_colour_checker("ColorChecker 2005", **arguments)[0]) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Multi_Colour_Checkers.png') + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Multi_Colour_Checkers.png" + ) plt.close( - plot_multi_colour_checkers(['ColorChecker 1976', 'ColorChecker 2005'], - **arguments)[0]) - - arguments['filename'] = os.path.join(output_directory, - 'Plotting_Plot_Single_SD.png') + plot_multi_colour_checkers( + ["ColorChecker 1976", "ColorChecker 2005"], **arguments + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Single_SD.png" + ) data = { 500: 0.0651, 520: 0.0705, 540: 0.0772, 560: 0.0870, 580: 0.1128, - 600: 0.1360 + 600: 0.1360, } - sd = SpectralDistribution(data, name='Custom') + sd = SpectralDistribution(data, name="Custom") plt.close(plot_single_sd(sd, **arguments)[0]) - arguments['filename'] = os.path.join(output_directory, - 'Plotting_Plot_Multi_SDS.png') + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Multi_SDS.png" + ) data_1 = { 500: 0.004900, 510: 0.009300, @@ -255,7 +361,7 @@ def generate_documentation_plots(output_directory): 530: 0.165500, 540: 0.290400, 550: 0.433450, - 560: 0.594500 + 560: 0.594500, } data_2 = { 500: 0.323000, @@ -264,456 +370,646 @@ def generate_documentation_plots(output_directory): 530: 0.862000, 540: 0.954000, 550: 0.994950, - 560: 0.995000 + 560: 0.995000, } - spd1 = SpectralDistribution(data_1, name='Custom 1') - spd2 = SpectralDistribution(data_2, name='Custom 2') + spd1 = SpectralDistribution(data_1, name="Custom 1") + spd2 = SpectralDistribution(data_2, name="Custom 2") plt.close(plot_multi_sds([spd1, spd2], **arguments)[0]) - arguments['filename'] = os.path.join(output_directory, - 'Plotting_Plot_Single_CMFS.png') + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Single_CMFS.png" + ) plt.close( - plot_single_cmfs('CIE 1931 2 Degree Standard Observer', - **arguments)[0]) - - arguments['filename'] = os.path.join(output_directory, - 'Plotting_Plot_Multi_CMFS.png') - cmfs = ('CIE 1931 2 Degree Standard Observer', - 'CIE 1964 10 Degree Standard Observer') + plot_single_cmfs("CIE 1931 2 Degree Standard Observer", **arguments)[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Multi_CMFS.png" + ) + cmfs = ( + "CIE 1931 2 Degree Standard Observer", + "CIE 1964 10 Degree Standard Observer", + ) plt.close(plot_multi_cmfs(cmfs, **arguments)[0]) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Single_Illuminant_SD.png') - plt.close(plot_single_illuminant_sd('A', **arguments)[0]) + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Single_Illuminant_SD.png" + ) + plt.close(plot_single_illuminant_sd("A", **arguments)[0]) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Multi_Illuminant_SDS.png') - plt.close(plot_multi_illuminant_sds(['A', 'B', 'C'], **arguments)[0]) + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Multi_Illuminant_SDS.png" + ) + plt.close(plot_multi_illuminant_sds(["A", "B", "C"], **arguments)[0]) - arguments['filename'] = os.path.join(output_directory, - 'Plotting_Plot_Visible_Spectrum.png') + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Visible_Spectrum.png" + ) plt.close(plot_visible_spectrum(**arguments)[0]) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Single_Lightness_Function.png') - plt.close(plot_single_lightness_function('CIE 1976', **arguments)[0]) + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Single_Lightness_Function.png" + ) + plt.close(plot_single_lightness_function("CIE 1976", **arguments)[0]) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Multi_Lightness_Functions.png') + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Multi_Lightness_Functions.png" + ) plt.close( - plot_multi_lightness_functions(['CIE 1976', 'Wyszecki 1963'], - **arguments)[0]) - - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Single_Luminance_Function.png') - plt.close(plot_single_luminance_function('CIE 1976', **arguments)[0]) - - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Multi_Luminance_Functions.png') + plot_multi_lightness_functions( + ["CIE 1976", "Wyszecki 1963"], **arguments + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Single_Luminance_Function.png" + ) + plt.close(plot_single_luminance_function("CIE 1976", **arguments)[0]) + + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Multi_Luminance_Functions.png" + ) plt.close( - plot_multi_luminance_functions(['CIE 1976', 'Newhall 1943'], - **arguments)[0]) - - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Blackbody_Spectral_Radiance.png') + plot_multi_luminance_functions( + ["CIE 1976", "Newhall 1943"], **arguments + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Blackbody_Spectral_Radiance.png" + ) plt.close( plot_blackbody_spectral_radiance( - 3500, blackbody='VY Canis Major', **arguments)[0]) + 3500, blackbody="VY Canis Major", **arguments + )[0] + ) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Blackbody_Colours.png') + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Blackbody_Colours.png" + ) plt.close( - plot_blackbody_colours(SpectralShape(150, 12500, 50), **arguments)[0]) + plot_blackbody_colours(SpectralShape(150, 12500, 50), **arguments)[0] + ) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Single_Colour_Swatch.png') - RGB = ColourSwatch(RGB=(0.45620519, 0.03081071, 0.04091952)) + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Single_Colour_Swatch.png" + ) + RGB = ColourSwatch((0.45620519, 0.03081071, 0.04091952)) plt.close(plot_single_colour_swatch(RGB, **arguments)[0]) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Multi_Colour_Swatches.png') - RGB_1 = ColourSwatch(RGB=(0.45293517, 0.31732158, 0.26414773)) - RGB_2 = ColourSwatch(RGB=(0.77875824, 0.57726450, 0.50453169)) + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Multi_Colour_Swatches.png" + ) + RGB_1 = ColourSwatch((0.45293517, 0.31732158, 0.26414773)) + RGB_2 = ColourSwatch((0.77875824, 0.57726450, 0.50453169)) plt.close(plot_multi_colour_swatches([RGB_1, RGB_2], **arguments)[0]) - arguments['filename'] = os.path.join(output_directory, - 'Plotting_Plot_Single_Function.png') + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Single_Function.png" + ) plt.close(plot_single_function(lambda x: x ** (1 / 2.2), **arguments)[0]) - arguments['filename'] = os.path.join(output_directory, - 'Plotting_Plot_Multi_Functions.png') + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Multi_Functions.png" + ) functions = { - 'Gamma 2.2': lambda x: x ** (1 / 2.2), - 'Gamma 2.4': lambda x: x ** (1 / 2.4), - 'Gamma 2.6': lambda x: x ** (1 / 2.6), + "Gamma 2.2": lambda x: x ** (1 / 2.2), + "Gamma 2.4": lambda x: x ** (1 / 2.4), + "Gamma 2.6": lambda x: x ** (1 / 2.6), } plt.close(plot_multi_functions(functions, **arguments)[0]) - arguments['filename'] = os.path.join(output_directory, - 'Plotting_Plot_Image.png') - path = os.path.join(output_directory, 'Logo_Medium_001.png') + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Image.png" + ) + path = os.path.join( + colour.__path__[0], + "examples", + "plotting", + "resources", + "Ishihara_Colour_Blindness_Test_Plate_3.png", + ) plt.close(plot_image(read_image(str(path)), **arguments)[0]) - arguments['filename'] = os.path.join( + arguments["filename"] = os.path.join( output_directory, - 'Plotting_Plot_Corresponding_Chromaticities_Prediction.png') + "Plotting_Plot_Corresponding_Chromaticities_Prediction.png", + ) plt.close( - plot_corresponding_chromaticities_prediction(1, 'Von Kries', 'CAT02', - **arguments)[0]) + plot_corresponding_chromaticities_prediction( + 1, "Von Kries", **arguments + )[0] + ) - arguments['filename'] = os.path.join(output_directory, - 'Plotting_Plot_Spectral_Locus.png') + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Spectral_Locus.png" + ) plt.close( - plot_spectral_locus(spectral_locus_colours='RGB', **arguments)[0]) - - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Chromaticity_Diagram_Colours.png') - plt.close(plot_chromaticity_diagram_colours(**arguments)[0]) + plot_spectral_locus(spectral_locus_colours="RGB", **arguments)[0] + ) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Chromaticity_Diagram.png') + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Chromaticity_Diagram_Colours.png" + ) + plt.close( + plot_chromaticity_diagram_colours(diagram_colours="RGB", **arguments)[ + 0 + ] + ) + + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Chromaticity_Diagram.png" + ) plt.close(plot_chromaticity_diagram(**arguments)[0]) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Chromaticity_Diagram_CIE1931.png') + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Chromaticity_Diagram_CIE1931.png" + ) plt.close(plot_chromaticity_diagram_CIE1931(**arguments)[0]) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Chromaticity_Diagram_CIE1960UCS.png') + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Chromaticity_Diagram_CIE1960UCS.png" + ) plt.close(plot_chromaticity_diagram_CIE1960UCS(**arguments)[0]) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Chromaticity_Diagram_CIE1976UCS.png') + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Chromaticity_Diagram_CIE1976UCS.png" + ) plt.close(plot_chromaticity_diagram_CIE1976UCS(**arguments)[0]) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_SDS_In_Chromaticity_Diagram.png') - A = SDS_ILLUMINANTS['A'] - D65 = SDS_ILLUMINANTS['D65'] + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_SDS_In_Chromaticity_Diagram.png" + ) + A = SDS_ILLUMINANTS["A"] + D65 = SDS_ILLUMINANTS["D65"] plt.close(plot_sds_in_chromaticity_diagram([A, D65], **arguments)[0]) - arguments['filename'] = os.path.join( + arguments["filename"] = os.path.join( output_directory, - 'Plotting_Plot_SDS_In_Chromaticity_Diagram_CIE1931.png') + "Plotting_Plot_SDS_In_Chromaticity_Diagram_CIE1931.png", + ) plt.close( - plot_sds_in_chromaticity_diagram_CIE1931([A, D65], **arguments)[0]) + plot_sds_in_chromaticity_diagram_CIE1931([A, D65], **arguments)[0] + ) - arguments['filename'] = os.path.join( + arguments["filename"] = os.path.join( output_directory, - 'Plotting_Plot_SDS_In_Chromaticity_Diagram_CIE1960UCS.png') + "Plotting_Plot_SDS_In_Chromaticity_Diagram_CIE1960UCS.png", + ) plt.close( - plot_sds_in_chromaticity_diagram_CIE1960UCS([A, D65], **arguments)[0]) + plot_sds_in_chromaticity_diagram_CIE1960UCS([A, D65], **arguments)[0] + ) - arguments['filename'] = os.path.join( + arguments["filename"] = os.path.join( output_directory, - 'Plotting_Plot_SDS_In_Chromaticity_Diagram_CIE1976UCS.png') + "Plotting_Plot_SDS_In_Chromaticity_Diagram_CIE1976UCS.png", + ) plt.close( - plot_sds_in_chromaticity_diagram_CIE1976UCS([A, D65], **arguments)[0]) + plot_sds_in_chromaticity_diagram_CIE1976UCS([A, D65], **arguments)[0] + ) - arguments['filename'] = os.path.join(output_directory, - 'Plotting_Plot_Pointer_Gamut.png') + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Pointer_Gamut.png" + ) plt.close(plot_pointer_gamut(**arguments)[0]) - arguments['filename'] = os.path.join( + arguments["filename"] = os.path.join( output_directory, - 'Plotting_Plot_RGB_Colourspaces_In_Chromaticity_Diagram.png') + "Plotting_Plot_RGB_Colourspaces_In_Chromaticity_Diagram.png", + ) plt.close( plot_RGB_colourspaces_in_chromaticity_diagram( - ['ITU-R BT.709', 'ACEScg', 'S-Gamut'], **arguments)[0]) + ["ITU-R BT.709", "ACEScg", "S-Gamut"], **arguments + )[0] + ) - arguments['filename'] = os.path.join( + arguments["filename"] = os.path.join( output_directory, - 'Plotting_Plot_RGB_Colourspaces_In_Chromaticity_Diagram_CIE1931.png') + "Plotting_Plot_RGB_Colourspaces_In_Chromaticity_Diagram_CIE1931.png", + ) plt.close( plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931( - ['ITU-R BT.709', 'ACEScg', 'S-Gamut'], **arguments)[0]) + ["ITU-R BT.709", "ACEScg", "S-Gamut"], **arguments + )[0] + ) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_RGB_Colourspaces_In_' - 'Chromaticity_Diagram_CIE1960UCS.png') + arguments["filename"] = os.path.join( + output_directory, + "Plotting_Plot_RGB_Colourspaces_In_" + "Chromaticity_Diagram_CIE1960UCS.png", + ) plt.close( plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS( - ['ITU-R BT.709', 'ACEScg', 'S-Gamut'], **arguments)[0]) + ["ITU-R BT.709", "ACEScg", "S-Gamut"], **arguments + )[0] + ) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_RGB_Colourspaces_In_' - 'Chromaticity_Diagram_CIE1976UCS.png') + arguments["filename"] = os.path.join( + output_directory, + "Plotting_Plot_RGB_Colourspaces_In_" + "Chromaticity_Diagram_CIE1976UCS.png", + ) plt.close( plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS( - ['ITU-R BT.709', 'ACEScg', 'S-Gamut'], **arguments)[0]) + ["ITU-R BT.709", "ACEScg", "S-Gamut"], **arguments + )[0] + ) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_RGB_Chromaticities_In_' - 'Chromaticity_Diagram.png') + arguments["filename"] = os.path.join( + output_directory, + "Plotting_Plot_RGB_Chromaticities_In_" "Chromaticity_Diagram.png", + ) RGB = np.random.random((128, 128, 3)) plt.close( plot_RGB_chromaticities_in_chromaticity_diagram( - RGB, 'ITU-R BT.709', **arguments)[0]) + RGB, "ITU-R BT.709", **arguments + )[0] + ) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_RGB_Chromaticities_In_' - 'Chromaticity_Diagram_CIE1931.png') + arguments["filename"] = os.path.join( + output_directory, + "Plotting_Plot_RGB_Chromaticities_In_" + "Chromaticity_Diagram_CIE1931.png", + ) plt.close( plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931( - RGB, 'ITU-R BT.709', **arguments)[0]) + RGB, "ITU-R BT.709", **arguments + )[0] + ) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_RGB_Chromaticities_In_' - 'Chromaticity_Diagram_CIE1960UCS.png') + arguments["filename"] = os.path.join( + output_directory, + "Plotting_Plot_RGB_Chromaticities_In_" + "Chromaticity_Diagram_CIE1960UCS.png", + ) plt.close( plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS( - RGB, 'ITU-R BT.709', **arguments)[0]) + RGB, "ITU-R BT.709", **arguments + )[0] + ) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_RGB_Chromaticities_In_' - 'Chromaticity_Diagram_CIE1976UCS.png') + arguments["filename"] = os.path.join( + output_directory, + "Plotting_Plot_RGB_Chromaticities_In_" + "Chromaticity_Diagram_CIE1976UCS.png", + ) plt.close( plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS( - RGB, 'ITU-R BT.709', **arguments)[0]) + RGB, "ITU-R BT.709", **arguments + )[0] + ) - arguments['filename'] = os.path.join( + arguments["filename"] = os.path.join( output_directory, - 'Plotting_Plot_Ellipses_MacAdam1942_In_Chromaticity_Diagram.png') + "Plotting_Plot_Ellipses_MacAdam1942_In_Chromaticity_Diagram.png", + ) plt.close( - plot_ellipses_MacAdam1942_in_chromaticity_diagram(**arguments)[0]) + plot_ellipses_MacAdam1942_in_chromaticity_diagram(**arguments)[0] + ) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Ellipses_MacAdam1942_In_' - 'Chromaticity_Diagram_CIE1931.png') + arguments["filename"] = os.path.join( + output_directory, + "Plotting_Plot_Ellipses_MacAdam1942_In_" + "Chromaticity_Diagram_CIE1931.png", + ) plt.close( - plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931( - **arguments)[0]) + plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931(**arguments)[ + 0 + ] + ) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Ellipses_MacAdam1942_In_' - 'Chromaticity_Diagram_CIE1960UCS.png') + arguments["filename"] = os.path.join( + output_directory, + "Plotting_Plot_Ellipses_MacAdam1942_In_" + "Chromaticity_Diagram_CIE1960UCS.png", + ) plt.close( plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS( - **arguments)[0]) + **arguments + )[0] + ) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Ellipses_MacAdam1942_In_' - 'Chromaticity_Diagram_CIE1976UCS.png') + arguments["filename"] = os.path.join( + output_directory, + "Plotting_Plot_Ellipses_MacAdam1942_In_" + "Chromaticity_Diagram_CIE1976UCS.png", + ) plt.close( plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS( - **arguments)[0]) + **arguments + )[0] + ) - arguments['filename'] = os.path.join(output_directory, - 'Plotting_Plot_Single_CCTF.png') - plt.close(plot_single_cctf('ITU-R BT.709', **arguments)[0]) + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Single_CCTF.png" + ) + plt.close(plot_single_cctf("ITU-R BT.709", **arguments)[0]) - arguments['filename'] = os.path.join(output_directory, - 'Plotting_Plot_Multi_CCTFs.png') - plt.close(plot_multi_cctfs(['ITU-R BT.709', 'sRGB'], **arguments)[0]) + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Multi_CCTFs.png" + ) + plt.close(plot_multi_cctfs(["ITU-R BT.709", "sRGB"], **arguments)[0]) - data = np.array([ - [ - None, - np.array([0.95010000, 1.00000000, 1.08810000]), - np.array([0.40920000, 0.28120000, 0.30600000]), - np.array([ - [0.02495100, 0.01908600, 0.02032900], - [0.10944300, 0.06235900, 0.06788100], - [0.27186500, 0.18418700, 0.19565300], - [0.48898900, 0.40749400, 0.44854600], - ]), - None, - ], - [ - None, - np.array([0.95010000, 1.00000000, 1.08810000]), - np.array([0.30760000, 0.48280000, 0.42770000]), - np.array([ - [0.02108000, 0.02989100, 0.02790400], - [0.06194700, 0.11251000, 0.09334400], - [0.15255800, 0.28123300, 0.23234900], - [0.34157700, 0.56681300, 0.47035300], - ]), - None, - ], + data = np.array( [ - None, - np.array([0.95010000, 1.00000000, 1.08810000]), - np.array([0.39530000, 0.28120000, 0.18450000]), - np.array([ - [0.02436400, 0.01908600, 0.01468800], - [0.10331200, 0.06235900, 0.02854600], - [0.26311900, 0.18418700, 0.12109700], - [0.43158700, 0.40749400, 0.39008600], - ]), - None, - ], - [ - None, - np.array([0.95010000, 1.00000000, 1.08810000]), - np.array([0.20510000, 0.18420000, 0.57130000]), - np.array([ - [0.03039800, 0.02989100, 0.06123300], - [0.08870000, 0.08498400, 0.21843500], - [0.18405800, 0.18418700, 0.40111400], - [0.32550100, 0.34047200, 0.50296900], - [0.53826100, 0.56681300, 0.80010400], - ]), - None, - ], - [ - None, - np.array([0.95010000, 1.00000000, 1.08810000]), - np.array([0.35770000, 0.28120000, 0.11250000]), - np.array([ - [0.03678100, 0.02989100, 0.01481100], - [0.17127700, 0.11251000, 0.01229900], - [0.30080900, 0.28123300, 0.21229800], - [0.52976000, 0.40749400, 0.11720000], - ]), - None, - ], - ]) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Constant_Hue_Loci.png') - plt.close(plot_constant_hue_loci(data, 'IPT', **arguments)[0]) - - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Single_Munsell_Value_Function.png') - plt.close(plot_single_munsell_value_function('ASTM D1535', **arguments)[0]) - - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Multi_Munsell_Value_Functions.png') - plt.close( - plot_multi_munsell_value_functions(['ASTM D1535', 'McCamy 1987'], - **arguments)[0]) - - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Single_SD_Rayleigh_Scattering.png') + [ + None, + np.array([0.95010000, 1.00000000, 1.08810000]), + np.array([0.40920000, 0.28120000, 0.30600000]), + np.array( + [ + [0.02495100, 0.01908600, 0.02032900], + [0.10944300, 0.06235900, 0.06788100], + [0.27186500, 0.18418700, 0.19565300], + [0.48898900, 0.40749400, 0.44854600], + ] + ), + None, + ], + [ + None, + np.array([0.95010000, 1.00000000, 1.08810000]), + np.array([0.30760000, 0.48280000, 0.42770000]), + np.array( + [ + [0.02108000, 0.02989100, 0.02790400], + [0.06194700, 0.11251000, 0.09334400], + [0.15255800, 0.28123300, 0.23234900], + [0.34157700, 0.56681300, 0.47035300], + ] + ), + None, + ], + [ + None, + np.array([0.95010000, 1.00000000, 1.08810000]), + np.array([0.39530000, 0.28120000, 0.18450000]), + np.array( + [ + [0.02436400, 0.01908600, 0.01468800], + [0.10331200, 0.06235900, 0.02854600], + [0.26311900, 0.18418700, 0.12109700], + [0.43158700, 0.40749400, 0.39008600], + ] + ), + None, + ], + [ + None, + np.array([0.95010000, 1.00000000, 1.08810000]), + np.array([0.20510000, 0.18420000, 0.57130000]), + np.array( + [ + [0.03039800, 0.02989100, 0.06123300], + [0.08870000, 0.08498400, 0.21843500], + [0.18405800, 0.18418700, 0.40111400], + [0.32550100, 0.34047200, 0.50296900], + [0.53826100, 0.56681300, 0.80010400], + ] + ), + None, + ], + [ + None, + np.array([0.95010000, 1.00000000, 1.08810000]), + np.array([0.35770000, 0.28120000, 0.11250000]), + np.array( + [ + [0.03678100, 0.02989100, 0.01481100], + [0.17127700, 0.11251000, 0.01229900], + [0.30080900, 0.28123300, 0.21229800], + [0.52976000, 0.40749400, 0.11720000], + ] + ), + None, + ], + ] + ) + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Constant_Hue_Loci.png" + ) + plt.close(plot_constant_hue_loci(data, "IPT", **arguments)[0]) + + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Single_Munsell_Value_Function.png" + ) + plt.close(plot_single_munsell_value_function("ASTM D1535", **arguments)[0]) + + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Multi_Munsell_Value_Functions.png" + ) + plt.close( + plot_multi_munsell_value_functions( + ["ASTM D1535", "McCamy 1987"], **arguments + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Single_SD_Rayleigh_Scattering.png" + ) plt.close(plot_single_sd_rayleigh_scattering(**arguments)[0]) - arguments['filename'] = os.path.join(output_directory, - 'Plotting_Plot_The_Blue_Sky.png') + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_The_Blue_Sky.png" + ) plt.close(plot_the_blue_sky(**arguments)[0]) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Colour_Quality_Bars.png') - illuminant = SDS_ILLUMINANTS['FL2'] - light_source = SDS_LIGHT_SOURCES['Kinoton 75P'] + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Colour_Quality_Bars.png" + ) + illuminant = SDS_ILLUMINANTS["FL2"] + light_source = SDS_LIGHT_SOURCES["Kinoton 75P"] light_source = light_source.copy().align(SpectralShape(360, 830, 1)) cqs_i = colour_quality_scale(illuminant, additional_data=True) cqs_l = colour_quality_scale(light_source, additional_data=True) plt.close(plot_colour_quality_bars([cqs_i, cqs_l], **arguments)[0]) - arguments['filename'] = os.path.join( + arguments["filename"] = os.path.join( output_directory, - 'Plotting_Plot_Single_SD_Colour_Rendering_Index_Bars.png') - illuminant = SDS_ILLUMINANTS['FL2'] + "Plotting_Plot_Single_SD_Colour_Rendering_Index_Bars.png", + ) + illuminant = SDS_ILLUMINANTS["FL2"] plt.close( - plot_single_sd_colour_rendering_index_bars(illuminant, **arguments)[0]) + plot_single_sd_colour_rendering_index_bars(illuminant, **arguments)[0] + ) - arguments['filename'] = os.path.join( + arguments["filename"] = os.path.join( output_directory, - 'Plotting_Plot_Multi_SDS_Colour_Rendering_Indexes_Bars.png') - light_source = SDS_LIGHT_SOURCES['Kinoton 75P'] + "Plotting_Plot_Multi_SDS_Colour_Rendering_Indexes_Bars.png", + ) + light_source = SDS_LIGHT_SOURCES["Kinoton 75P"] plt.close( plot_multi_sds_colour_rendering_indexes_bars( - [illuminant, light_source], **arguments)[0]) + [illuminant, light_source], **arguments + )[0] + ) - arguments['filename'] = os.path.join( + arguments["filename"] = os.path.join( output_directory, - 'Plotting_Plot_Single_SD_Colour_Quality_Scale_Bars.png') - illuminant = SDS_ILLUMINANTS['FL2'] + "Plotting_Plot_Single_SD_Colour_Quality_Scale_Bars.png", + ) + illuminant = SDS_ILLUMINANTS["FL2"] plt.close( - plot_single_sd_colour_quality_scale_bars(illuminant, **arguments)[0]) + plot_single_sd_colour_quality_scale_bars(illuminant, **arguments)[0] + ) - arguments['filename'] = os.path.join( + arguments["filename"] = os.path.join( output_directory, - 'Plotting_Plot_Multi_SDS_Colour_Quality_Scales_Bars.png') - light_source = SDS_LIGHT_SOURCES['Kinoton 75P'] + "Plotting_Plot_Multi_SDS_Colour_Quality_Scales_Bars.png", + ) + light_source = SDS_LIGHT_SOURCES["Kinoton 75P"] plt.close( - plot_multi_sds_colour_quality_scales_bars([illuminant, light_source], - **arguments)[0]) - - arguments['filename'] = os.path.join(output_directory, - 'Plotting_Plot_Planckian_Locus.png') - plt.close(plot_planckian_locus(**arguments)[0]) + plot_multi_sds_colour_quality_scales_bars( + [illuminant, light_source], **arguments + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Hull_Section_Colours.png" + ) + vertices, faces, _outline = primitive_cube(1, 1, 1, 64, 64, 64) + XYZ_vertices = RGB_to_XYZ( + vertices["position"] + 0.5, + RGB_COLOURSPACE_sRGB.whitepoint, + RGB_COLOURSPACE_sRGB.whitepoint, + RGB_COLOURSPACE_sRGB.matrix_RGB_to_XYZ, + ) + hull = trimesh.Trimesh(XYZ_vertices, faces, process=False) + plt.close( + plot_hull_section_colours(hull, section_colours="RGB", **arguments)[0] + ) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Planckian_Locus_CIE1931.png') - plt.close(plot_planckian_locus_CIE1931(**arguments)[0]) + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Hull_Section_Contour.png" + ) + plt.close( + plot_hull_section_contour(hull, section_colours="RGB", **arguments)[0] + ) - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_Planckian_Locus_CIE1960UCS.png') - plt.close(plot_planckian_locus_CIE1960UCS(**arguments)[0]) + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Visible_Spectrum_Section.png" + ) + plt.close( + plot_visible_spectrum_section( + section_colours="RGB", section_opacity=0.15, **arguments + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_RGB_Colourspace_Section.png" + ) + plt.close( + plot_RGB_colourspace_section( + "sRGB", section_colours="RGB", section_opacity=0.15, **arguments + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_Planckian_Locus.png" + ) + plt.close( + plot_planckian_locus(planckian_locus_colours="RGB", **arguments)[0] + ) - arguments['filename'] = os.path.join( + arguments["filename"] = os.path.join( output_directory, - 'Plotting_Plot_Planckian_Locus_In_Chromaticity_Diagram.png') + "Plotting_Plot_Planckian_Locus_In_Chromaticity_Diagram.png", + ) plt.close( - plot_planckian_locus_in_chromaticity_diagram(['A', 'B', 'C'], - **arguments)[0]) + plot_planckian_locus_in_chromaticity_diagram( + ["A", "B", "C"], **arguments + )[0] + ) - arguments['filename'] = os.path.join( + arguments["filename"] = os.path.join( output_directory, - 'Plotting_Plot_Planckian_Locus_In_Chromaticity_Diagram_CIE1931.png') + "Plotting_Plot_Planckian_Locus_In_Chromaticity_Diagram_CIE1931.png", + ) plt.close( - plot_planckian_locus_in_chromaticity_diagram_CIE1931(['A', 'B', 'C'], - **arguments)[0]) + plot_planckian_locus_in_chromaticity_diagram_CIE1931( + ["A", "B", "C"], **arguments + )[0] + ) - arguments['filename'] = os.path.join( + arguments["filename"] = os.path.join( output_directory, - 'Plotting_Plot_Planckian_Locus_In_Chromaticity_Diagram_CIE1960UCS.png') + "Plotting_Plot_Planckian_Locus_In_Chromaticity_Diagram_CIE1960UCS.png", + ) plt.close( plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS( - ['A', 'B', 'C'], **arguments)[0]) + ["A", "B", "C"], **arguments + )[0] + ) - arguments['filename'] = os.path.join( + arguments["filename"] = os.path.join( output_directory, - 'Plotting_Plot_Single_SD_Colour_Rendition_Report_Full.png') + "Plotting_Plot_Single_SD_Colour_Rendition_Report_Full.png", + ) plt.close( - plot_single_sd_colour_rendition_report(SDS_ILLUMINANTS['FL2'], - **arguments)[0]) + plot_single_sd_colour_rendition_report( + SDS_ILLUMINANTS["FL2"], **arguments + )[0] + ) - arguments['filename'] = os.path.join( + arguments["filename"] = os.path.join( output_directory, - 'Plotting_Plot_Single_SD_Colour_Rendition_Report_Intermediate.png') + "Plotting_Plot_Single_SD_Colour_Rendition_Report_Intermediate.png", + ) plt.close( - plot_single_sd_colour_rendition_report(SDS_ILLUMINANTS['FL2'], - 'Intermediate', **arguments)[0]) + plot_single_sd_colour_rendition_report( + SDS_ILLUMINANTS["FL2"], "Intermediate", **arguments + )[0] + ) - arguments['filename'] = os.path.join( + arguments["filename"] = os.path.join( output_directory, - 'Plotting_Plot_Single_SD_Colour_Rendition_Report_Simple.png') + "Plotting_Plot_Single_SD_Colour_Rendition_Report_Simple.png", + ) plt.close( - plot_single_sd_colour_rendition_report(SDS_ILLUMINANTS['FL2'], - 'Simple', **arguments)[0]) - - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_RGB_Colourspaces_Gamuts.png') + plot_single_sd_colour_rendition_report( + SDS_ILLUMINANTS["FL2"], "Simple", **arguments + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_RGB_Colourspaces_Gamuts.png" + ) plt.close( - plot_RGB_colourspaces_gamuts(['ITU-R BT.709', 'ACEScg', 'S-Gamut'], - **arguments)[0]) - - arguments['filename'] = os.path.join( - output_directory, 'Plotting_Plot_RGB_Colourspaces_Gamuts.png') + plot_RGB_colourspaces_gamuts( + ["ITU-R BT.709", "ACEScg", "S-Gamut"], **arguments + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_RGB_Colourspaces_Gamuts.png" + ) plt.close( - plot_RGB_colourspaces_gamuts(['ITU-R BT.709', 'ACEScg', 'S-Gamut'], - **arguments)[0]) + plot_RGB_colourspaces_gamuts( + ["ITU-R BT.709", "ACEScg", "S-Gamut"], **arguments + )[0] + ) - arguments['filename'] = os.path.join(output_directory, - 'Plotting_Plot_RGB_Scatter.png') - plt.close(plot_RGB_scatter(RGB, 'ITU-R BT.709', **arguments)[0]) + arguments["filename"] = os.path.join( + output_directory, "Plotting_Plot_RGB_Scatter.png" + ) + plt.close(plot_RGB_scatter(RGB, "ITU-R BT.709", **arguments)[0]) filename = os.path.join( - output_directory, - 'Plotting_Plot_Colour_Automatic_Conversion_Graph.png') + output_directory, "Plotting_Plot_Colour_Automatic_Conversion_Graph.png" + ) plot_automatic_colour_conversion_graph(filename) # ************************************************************************* # "tutorial.rst" # ************************************************************************* - arguments['filename'] = os.path.join(output_directory, - 'Tutorial_Visible_Spectrum.png') + arguments["filename"] = os.path.join( + output_directory, "Tutorial_Visible_Spectrum.png" + ) plt.close(plot_visible_spectrum(**arguments)[0]) - arguments['filename'] = os.path.join(output_directory, - 'Tutorial_Sample_SD.png') + arguments["filename"] = os.path.join( + output_directory, "Tutorial_Sample_SD.png" + ) sample_sd_data = { 380: 0.048, 385: 0.051, @@ -795,87 +1091,106 @@ def generate_documentation_plots(output_directory): 765: 0.465, 770: 0.448, 775: 0.432, - 780: 0.421 + 780: 0.421, } - sd = SpectralDistribution(sample_sd_data, name='Sample') + sd = SpectralDistribution(sample_sd_data, name="Sample") plt.close(plot_single_sd(sd, **arguments)[0]) - arguments['filename'] = os.path.join(output_directory, - 'Tutorial_SD_Interpolation.png') + arguments["filename"] = os.path.join( + output_directory, "Tutorial_SD_Interpolation.png" + ) sd_copy = sd.copy() sd_copy.interpolate(SpectralShape(400, 770, 1)) plt.close( plot_multi_sds( - [sd, sd_copy], bounding_box=[730, 780, 0.25, 0.5], **arguments)[0]) + [sd, sd_copy], bounding_box=[730, 780, 0.25, 0.5], **arguments + )[0] + ) - arguments['filename'] = os.path.join(output_directory, - 'Tutorial_Sample_Swatch.png') + arguments["filename"] = os.path.join( + output_directory, "Tutorial_Sample_Swatch.png" + ) sd = SpectralDistribution(sample_sd_data) - cmfs = MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer'] - illuminant = SDS_ILLUMINANTS['D65'] - with domain_range_scale('1'): + cmfs = MSDS_CMFS_STANDARD_OBSERVER["CIE 1931 2 Degree Standard Observer"] + illuminant = SDS_ILLUMINANTS["D65"] + with domain_range_scale("1"): XYZ = sd_to_XYZ(sd, cmfs, illuminant) RGB = XYZ_to_sRGB(XYZ) plt.close( plot_single_colour_swatch( - ColourSwatch('Sample', RGB), - text_kwargs={'size': 'x-large'}, - **arguments)[0]) - - arguments['filename'] = os.path.join(output_directory, - 'Tutorial_Neutral5.png') - patch_name = 'neutral 5 (.70 D)' - patch_sd = SDS_COLOURCHECKERS['ColorChecker N Ohta'][patch_name] - with domain_range_scale('1'): + ColourSwatch(RGB, "Sample"), + text_kwargs={"size": "x-large"}, + **arguments, + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Tutorial_Neutral5.png" + ) + patch_name = "neutral 5 (.70 D)" + patch_sd = SDS_COLOURCHECKERS["ColorChecker N Ohta"][patch_name] + with domain_range_scale("1"): XYZ = sd_to_XYZ(patch_sd, cmfs, illuminant) RGB = XYZ_to_sRGB(XYZ) plt.close( plot_single_colour_swatch( - ColourSwatch(patch_name.title(), RGB), - text_kwargs={'size': 'x-large'}, - **arguments)[0]) - - arguments['filename'] = os.path.join(output_directory, - 'Tutorial_Colour_Checker.png') + ColourSwatch(RGB, patch_name.title()), + text_kwargs={"size": "x-large"}, + **arguments, + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Tutorial_Colour_Checker.png" + ) plt.close( plot_single_colour_checker( - colour_checker='ColorChecker 2005', - text_kwargs={'visible': False}, - **arguments)[0]) - - arguments['filename'] = os.path.join( - output_directory, 'Tutorial_CIE_1931_Chromaticity_Diagram.png') + colour_checker="ColorChecker 2005", + text_kwargs={"visible": False}, + **arguments, + )[0] + ) + + arguments["filename"] = os.path.join( + output_directory, "Tutorial_CIE_1931_Chromaticity_Diagram.png" + ) xy = XYZ_to_xy(XYZ) plot_chromaticity_diagram_CIE1931(standalone=False) x, y = xy - plt.plot(x, y, 'o-', color='white') + plt.plot(x, y, "o-", color="white") # Annotating the plot. plt.annotate( patch_sd.name.title(), xy=xy, xytext=(-50, 30), - textcoords='offset points', - arrowprops=dict(arrowstyle='->', connectionstyle='arc3, rad=-0.2')) + textcoords="offset points", + arrowprops=dict(arrowstyle="->", connectionstyle="arc3, rad=-0.2"), + ) plt.close( render( standalone=True, limits=(-0.1, 0.9, -0.1, 0.9), x_tighten=True, y_tighten=True, - **arguments)[0]) + **arguments, + )[0] + ) # ************************************************************************* # "basics.rst" # ************************************************************************* - arguments['filename'] = os.path.join(output_directory, - 'Basics_Logo_Small_001_CIE_XYZ.png') - RGB = read_image(os.path.join(output_directory, - 'Logo_Small_001.png'))[..., 0:3] + arguments["filename"] = os.path.join( + output_directory, "Basics_Logo_Small_001_CIE_XYZ.png" + ) + RGB = read_image(os.path.join(output_directory, "Logo_Small_001.png"))[ + ..., 0:3 + ] XYZ = sRGB_to_XYZ(RGB) plt.close( - plot_image(XYZ, text_kwargs={'text': 'sRGB to XYZ'}, **arguments)[0]) + plot_image(XYZ, text_kwargs={"text": "sRGB to XYZ"}, **arguments)[0] + ) -if __name__ == '__main__': - generate_documentation_plots(os.path.join('..', 'docs', '_static')) +if __name__ == "__main__": + generate_documentation_plots(os.path.join("..", "docs", "_static")) diff --git a/utilities/mock_for_colour.py b/utilities/mock_for_colour.py index 1f8dfd0685..3e27ddc768 100644 --- a/utilities/mock_for_colour.py +++ b/utilities/mock_for_colour.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Mock for Colour =============== @@ -13,34 +12,35 @@ master/sphinx/ext/autodoc/mock.py """ -from __future__ import division, unicode_literals - import os from types import FunctionType, MethodType, ModuleType -__author__ = 'Sphinx Team, Colour Developers' -__copyright__ = 'Copyright 2007-2019 - Sphinx Team' -__copyright__ += ', ' -__copyright__ += 'Copyright (C) 2013-2020 - 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__ = "Sphinx Team, Colour Developers" +__copyright__ = "Copyright 2007-2019 - Sphinx Team" +__copyright__ += ", " +__copyright__ += "Copyright 2013 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__ = ['MockObject', 'MockModule', 'mock_scipy_for_colour'] +__all__ = [ + "MockObject", + "MockModule", + "mock_scipy_for_colour", +] -class MockObject(object): +class MockObject: """ - A generic mock object used for helping to mock tricky *Colour* requirements - such as *Scipy*. + Mock an object to handle *Colour* requirements such as *Scipy*. Other Parameters ---------------- - \\*args : list, optional + args Arguments. - \\**kwargs : dict, optional + kwargs Keywords arguments. References @@ -48,17 +48,17 @@ class MockObject(object): :cite:`SphinxTeam` """ - __display_name__ = 'MockObject' + __display_name__ = "MockObject" def __new__(cls, *args, **kwargs): """ - Returns a new instance of the :class:`MockObject` class. + Return a new instance of the :class:`MockObject` class. Other Parameters ---------------- - \\*args : list, optional + args Arguments. - \\**kwargs : dict, optional + kwargs Keywords arguments. """ @@ -69,28 +69,27 @@ def __new__(cls, *args, **kwargs): args[0], superclass.__display_name__, superclass=superclass, - attributes=args[2]) + attributes=args[2], + ) - return super(MockObject, cls).__new__(cls) + return super().__new__(cls) def __init__(self, *args, **kwargs): pass def __len__(self): - """ - Returns the length of the :class:`MockObject` class instance, i.e. 0. - """ + """Return the length of the :class:`MockObject` class instance, i.e. 0.""" return 0 def __contains__(self, key): """ - Returns whether the :class:`MockObject` class instance contains given + Return whether the :class:`MockObject` class instance contains given key. Parameters ---------- - key : unicode + key Key to check whether is is contained in the :class:`MockObject` class instance. """ @@ -98,9 +97,7 @@ class instance. return False def __iter__(self): - """ - Iterates over the :class:`MockObject` class instance. - """ + """Iterate over the :class:`MockObject` class instance.""" return iter([]) @@ -110,16 +107,16 @@ def __mro_entries__(self, bases): of a class definition, then method __mro_entries__ is searched on it. """ - return (self.__class__, ) + return (self.__class__,) def __getitem__(self, key): """ - Returns the value at given key from the :class:`MockObject` class + Return the value at given key from the :class:`MockObject` class instance. Parameters ---------- - key : unicode + key Key to return the value at. """ @@ -127,12 +124,12 @@ def __getitem__(self, key): def __getattr__(self, key): """ - Returns the attribute at given key from the :class:`MockObject` class + Return the attribute at given key from the :class:`MockObject` class instance. Parameters ---------- - key : unicode + key Key to return the attribute at. """ @@ -140,13 +137,13 @@ def __getattr__(self, key): def __call__(self, *args, **kwargs): """ - Calls the :class:`MockObject` class instance. + Call the :class:`MockObject` class instance. Other Parameters ---------------- - \\*args : list, optional + args Arguments. - \\**kwargs : dict, optional + kwargs Keywords arguments. """ @@ -157,33 +154,35 @@ def __call__(self, *args, **kwargs): def __repr__(self): """ - Returns an evaluable string representation of the :class:`MockObject` + Return an evaluable string representation of the :class:`MockObject` class instance. """ return self.__display_name__ -def _make_subclass(name, module, superclass=MockObject, attributes=None): +def _make_subclass( + name, module, superclass=MockObject, attributes=None +): # noqa: D405,D407,D410,D411 """ - A sub-class factory that makes sub-classes of given super-class type. + Produce sub-classes of given super-class type. Parameters ---------- - name : unicode + name Name of the sub-class. - module : unicode + module Name of the sub-class module. - superclass : object + superclass Super-class type. - attributes : dict + attributes Attributes to set the sub-class with. """ - attrs = {'__module__': module, '__display_name__': module + '.' + name} + attrs = {"__module__": module, "__display_name__": module + "." + name} attrs.update(attributes or {}) - return type(name, (superclass, ), attrs) + return type(name, (superclass,), attrs) class MockModule(ModuleType): @@ -192,7 +191,7 @@ class MockModule(ModuleType): Parameters ---------- - name : unicode + name Name of the mocked module. References @@ -203,18 +202,18 @@ class MockModule(ModuleType): __file__ = os.devnull def __init__(self, name): - super(MockModule, self).__init__(name) + super().__init__(name) self.__all__ = [] self.__path__ = [] def __getattr__(self, name): """ - Returns the attribute at given name from the :class:`MockModule` class + Return the attribute at given name from the :class:`MockModule` class instance. Parameters ---------- - name : unicode + name Name to return the attribute at. """ @@ -222,7 +221,7 @@ def __getattr__(self, name): def __repr__(self): """ - Returns an evaluable string representation of the :class:`MockModule` + Return an evaluable string representation of the :class:`MockModule` class instance. """ @@ -230,22 +229,36 @@ class instance. def mock_scipy_for_colour(): - """ - Mocks *Scipy* for *Colour*. - """ + """Mock *Scipy* for *Colour*.""" import sys - for module in ('scipy', 'scipy.interpolate', 'scipy.spatial', - 'scipy.spatial.distance', 'scipy.optimize'): + for module in ( + "scipy", + "scipy.interpolate", + "scipy.linalg", + "scipy.ndimage", + "scipy.ndimage.filters", + "scipy.spatial", + "scipy.spatial.distance", + "scipy.optimize", + ): sys.modules[str(module)] = MockModule(str(module)) -if __name__ == '__main__': +if __name__ == "__main__": import sys - for module in ('scipy', 'scipy.interpolate', 'scipy.spatial', - 'scipy.spatial.distance', 'scipy.optimize'): + for module in ( + "scipy", + "scipy.interpolate", + "scipy.linalg", + "scipy.ndimage", + "scipy.ndimage.filters", + "scipy.spatial", + "scipy.spatial.distance", + "scipy.optimize", + ): sys.modules[str(module)] = MockModule(str(module)) import colour diff --git a/utilities/unicode_to_ascii.py b/utilities/unicode_to_ascii.py index 2d62f52e15..d75860f9ae 100755 --- a/utilities/unicode_to_ascii.py +++ b/utilities/unicode_to_ascii.py @@ -1,72 +1,72 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unicode to ASCII Utility ======================== """ -import sys -if sys.version_info[0] < 3: - # Smelly hack for Python 2.x: https://stackoverflow.com/q/3828723/931625 - reload(sys) # noqa - sys.setdefaultencoding('utf-8') - -import codecs # noqa -import os # noqa -import unicodedata # noqa - -__copyright__ = 'Copyright (C) 2013-2020 - 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 = { - '–': '-', - '“': '"', - '”': '"', - '‘': "'", - '’': "'", - '′': "'", +from __future__ import annotations + +import codecs +import os +import unicodedata + +__copyright__ = "Copyright 2013 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: dict[str, str] = { + "–": "-", + "“": '"', + "”": '"', + "‘": "'", + "’": "'", + "′": "'", } -def unicode_to_ascii(root_directory): +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 ---------- - root_directory : unicode + root_directory 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') 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')) +if __name__ == "__main__": + unicode_to_ascii(os.path.join("..", "colour"))