├── .github └── workflows │ ├── build-docs.yml │ ├── publish-pypi.yml │ └── run-tests.yml ├── .gitignore ├── AUTHORS.md ├── CITATION.cff ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs ├── Makefile ├── _static │ ├── Hugh_Pumphrey_CC-BY_compare.png │ ├── Hugh_Pumphrey_CC-BY_original.png │ ├── Hugh_Pumphrey_CC-BY_unmapped_1.png │ ├── Hugh_Pumphrey_CC-BY_unmapped_2.png │ ├── agile-open-logo-nocircle-grey_40px.png │ ├── custom.css │ ├── favicon.ico │ ├── unmap.png │ └── unmap.svg ├── _templates │ └── links.html ├── conf.py ├── index.rst ├── notebooks │ ├── Guess_the_colourmap_from_an_image.ipynb │ ├── Overview_of_unmap.ipynb │ ├── Unmap_data_from_an_image.ipynb │ └── _Logo.ipynb ├── process_html.py ├── process_ipynb.py ├── readme.rst ├── unmap.rst └── userguide │ └── Unmap_data_from_an_image.ipynb ├── pyproject.toml ├── tests ├── __init__.py └── test_unmap.py └── unmap ├── __init__.py ├── unmap.py └── unweave.py /.github/workflows/build-docs.yml: -------------------------------------------------------------------------------- 1 | name: Build docs 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: [ published ] 7 | 8 | jobs: 9 | build-docs: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | 15 | - name: Check out repo 16 | uses: actions/checkout@v3 17 | 18 | - name: Set up Python 19 | uses: actions/setup-python@v4 20 | with: 21 | python-version: "3.10" 22 | 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | pip install .[docs] 27 | 28 | - name: Update Sphinx docs 29 | run: | 30 | cd docs 31 | make html 32 | 33 | - name: Deploy 34 | uses: JamesIves/github-pages-deploy-action@v4 35 | with: 36 | branch: gh-pages 37 | folder: docs/_build/html 38 | -------------------------------------------------------------------------------- /.github/workflows/publish-pypi.yml: -------------------------------------------------------------------------------- 1 | name: Publish to PyPI 2 | 3 | on: 4 | release: 5 | types: [ published ] 6 | 7 | jobs: 8 | deploy: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Set up Python 15 | uses: actions/setup-python@v4 16 | with: 17 | python-version: '3.10' 18 | - name: Install dependencies 19 | run: | 20 | python -m pip install --upgrade pip 21 | pip install build 22 | - name: Build package 23 | run: python -m build 24 | - name: Publish package 25 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 26 | with: 27 | user: __token__ 28 | password: ${{ secrets.PYPI_API_TOKEN }} 29 | -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | python-version: ["3.8", "3.9", "3.10", "3.11"] 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | - name: Set up Python ${{ matrix.python-version }} 21 | uses: actions/setup-python@v4 22 | with: 23 | python-version: ${{ matrix.python-version }} 24 | - name: Install dependencies 25 | run: | 26 | python -m pip install --upgrade pip 27 | python -m pip install .[test] 28 | - name: Test with pytest 29 | run: | 30 | pytest 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Version file autocreated in pyproject.toml and unmap/__init__.py 2 | _version.py 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | pip-wheel-metadata/ 27 | share/python-wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | MANIFEST 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .nox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | *.py,cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | 57 | # Translations 58 | *.mo 59 | *.pot 60 | 61 | # Django stuff: 62 | *.log 63 | local_settings.py 64 | db.sqlite3 65 | db.sqlite3-journal 66 | 67 | # Flask stuff: 68 | instance/ 69 | .webassets-cache 70 | 71 | # Scrapy stuff: 72 | .scrapy 73 | 74 | # Sphinx documentation 75 | docs/_build/ 76 | 77 | # PyBuilder 78 | target/ 79 | 80 | # Jupyter Notebook 81 | .ipynb_checkpoints 82 | 83 | # IPython 84 | profile_default/ 85 | ipython_config.py 86 | 87 | # pyenv 88 | .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Authors 2 | 3 | The following people have contributed to the project (in alphabetical order): 4 | 5 | - [Matt Hall](https://github.com/kwinkunks), Canada (ORCID: [0000-0002-4054-8295](https://orcid.org/0000-0002-4054-8295)) 6 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | title: unmap 3 | version: 0.0 4 | message: Please use this information to cite this work. 5 | type: software 6 | authors: 7 | - given-names: Matt 8 | family-names: Hall 9 | email: kwinkunks@gmail.com 10 | affiliation: None 11 | orcid: 'https://orcid.org/0000-0002-4054-8295' 12 | date-released: 2022-09-16 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | **🙌 Thank you for considering contributing to this project!** 4 | 5 | There are several important ways you can help; here are some examples: 6 | 7 | - Submitting bug reports and feature requests: see [Issues](https://github.com/scienxlab/unmap/issues). 8 | - Proposing code for bug fixes and new features, then [making a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests). 9 | - Fixing typos and generally improving the documentation. 10 | - Writing tutorials, examples, and how-to documents. 11 | 12 | 13 | ## Code of conduct 14 | 15 | We're fortunate to be part of a large professional community that conducts itself with mutual respect and consideration for others. Agile's [Code of Conduct](https://github.com/agilescientific/community/blob/main/CODE_OF_CONDUCT.md) is part of protecting these features for everyone, everywhere. Please read it. 16 | 17 | 18 | ## Authorship 19 | 20 | If you contribute a pull request to the project, please add yourself to `AUTHORS.md`. 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # unmap 2 | 3 | [![Run tests](https://github.com/scienxlab/unmap/actions/workflows/run-tests.yml/badge.svg)](https://github.com/scienxlab/unmap/actions/workflows/run-tests.yml) 4 | [![Build docs](https://github.com/scienxlab/unmap/actions/workflows/build-docs.yml/badge.svg)](https://github.com/scienxlab/unmap/actions/workflows/build-docs.yml) 5 | [![PyPI version](https://img.shields.io/pypi/v/unmap.svg)](https://pypi.org/project/unmap//) 6 | [![PyPI versions](https://img.shields.io/pypi/pyversions/unmap.svg)](https://pypi.org/project/unmap//) 7 | [![PyPI license](https://img.shields.io/pypi/l/unmap.svg)](https://pypi.org/project/unmap/) 8 | 9 | Unmap data from pseudocolor images, with or without knowledge of the colourmap. This tool has 2 main components: 10 | 11 | 1. Guess the colourmap that was used for a pseudocolour visualization, in cases where it's unknown and a colourbar is not included in the image. 12 | 2. 'Unmap' a pseudocolour visualization, separating the data from the image; essentially this is the opposite of what `plt.imshow()` does. 13 | 14 | 15 | ## Similar projects 16 | 17 | There are some other approaches to both Task 1 (above) and Task 2: 18 | 19 | - [`unmap`](https://github.com/jperryhouts/unmap) (I swear I didn't know about this tool when I named mine!) — does the data ripping part. The colourmap must be provided, but the tool also provides a way to interactively identify a colourbar in the image. 20 | - [Poco et al.](https://ieeexplore.ieee.org/document/8017646) ([GitHub](https://github.com/uwdata/rev)) attempts to both find the colourbar in a visualization, then use it to perform Task 2. The visualization must contain a colourbar. 21 | - [Yuan et al.](https://github.com/yuanlinping/deep_colormap_extraction) attempts Task 1 using deep learning. The prediction from a CNN is refined with either Laplacian eigenmapping (manifold+based dimensionality reduction, for continuous colourmaps) or DBSCAN (for categorical colourmaps). 22 | 23 | Of these projects, only Yuan et al. ('deep colormap extraction') requires no _a priori_ knowledge of the colourmap. 24 | 25 | 26 | ## Stack Exchange questions about this topic 27 | 28 | - [Can I get numeric data from a color map?](https://datascience.stackexchange.com/questions/27247/can-i-get-numeric-data-from-a-color-map) 29 | - [How to extract a colormap from a colorbar image and use it in a heatmap?](https://stackoverflow.com/questions/71090534/how-to-extract-a-colormap-from-a-colorbar-image-and-use-it-in-a-heatmap) 30 | - [Given a JPG of 2D colorplot colorbar how can I sample the image to extract data?](https://stackoverflow.com/questions/63233529/given-a-jpg-of-2d-colorplot-colorbar-how-can-i-sample-the-image-to-extract-n) 31 | - [How to reverse a colormap image to scalar values?](https://stackoverflow.com/questions/3720840/how-to-reverse-a-color-map-image-to-scalar-values) 32 | - [Extract color table values?](https://stackoverflow.com/questions/62267694/extract-color-table-values) 33 | - [Invert not reverse a colormap in maptplotlib](https://stackoverflow.com/questions/14445102/invert-not-reverse-a-colormap-in-matplotlib) 34 | 35 | 36 | ## Installation 37 | 38 | You can install this package with `pip`: 39 | 40 | pip install unmap 41 | 42 | There are `dev`, `test` and `docs` options for installing dependencies for those purposes, eg `pip install unmap[dev]`. 43 | 44 | 45 | ## Documentation 46 | 47 | Read [the documentation](https://scienxlab.org/unmap), especially [the examples](https://scienxlab.org/unmap/userguide/Unmap_data_from_an_image.html). 48 | 49 | 50 | ## Contributing 51 | 52 | Take a look at [`CONTRIBUTING.md`](https://github.com/scienxlab/unmap/blob/main/CONTRIBUTING.md). 53 | 54 | 55 | ## Testing 56 | 57 | After cloning this repository and installing the dependencies required for testing, you can run the tests (requires `pytest` and `pytest-cov`) with 58 | 59 | pytest 60 | 61 | 62 | ## Building 63 | 64 | This repo uses PEP 517-style packaging, with the entire build system and requirements defined in the `pyproject.toml` file. [Read more about this](https://setuptools.pypa.io/en/latest/build_meta.html) and [about Python packaging in general](https://packaging.python.org/en/latest/tutorials/packaging-projects/). 65 | 66 | Building the project requires `build`, so first: 67 | 68 | pip install build 69 | 70 | Then to build `unmap` locally: 71 | 72 | python -m build 73 | 74 | The builds both `.tar.gz` and `.whl` files, either of which you can install with `pip`. 75 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help html 16 | 17 | html: 18 | python process_ipynb.py $(SOURCEDIR)/notebooks 19 | $(SPHINXBUILD) -E -b html $(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/html 20 | python process_html.py $(BUILDDIR)/html 21 | @echo 22 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 23 | -------------------------------------------------------------------------------- /docs/_static/Hugh_Pumphrey_CC-BY_compare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scienxlab/unmap/30269199b54be09072f0950f0d6ccbd9843a739b/docs/_static/Hugh_Pumphrey_CC-BY_compare.png -------------------------------------------------------------------------------- /docs/_static/Hugh_Pumphrey_CC-BY_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scienxlab/unmap/30269199b54be09072f0950f0d6ccbd9843a739b/docs/_static/Hugh_Pumphrey_CC-BY_original.png -------------------------------------------------------------------------------- /docs/_static/Hugh_Pumphrey_CC-BY_unmapped_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scienxlab/unmap/30269199b54be09072f0950f0d6ccbd9843a739b/docs/_static/Hugh_Pumphrey_CC-BY_unmapped_1.png -------------------------------------------------------------------------------- /docs/_static/Hugh_Pumphrey_CC-BY_unmapped_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scienxlab/unmap/30269199b54be09072f0950f0d6ccbd9843a739b/docs/_static/Hugh_Pumphrey_CC-BY_unmapped_2.png -------------------------------------------------------------------------------- /docs/_static/agile-open-logo-nocircle-grey_40px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scienxlab/unmap/30269199b54be09072f0950f0d6ccbd9843a739b/docs/_static/agile-open-logo-nocircle-grey_40px.png -------------------------------------------------------------------------------- /docs/_static/custom.css: -------------------------------------------------------------------------------- 1 | /* Removes Captions from the main page. */ 2 | article p.caption { 3 | display: none; 4 | } 5 | 6 | /* Styles the 'line block' https://docutils.sourceforge.io/docs/user/rst/quickref.html#line-blocks. */ 7 | blockquote { 8 | background: none; 9 | border-left-width: 0px; 10 | padding: 0em; 11 | } 12 | 13 | blockquote div.line { 14 | color: #838383; 15 | display: inline; 16 | font-style: normal !important; 17 | font-size: 150%; 18 | line-height: 125%; 19 | } 20 | 21 | /* Adds the GitHub ribbon. */ 22 | #forkongithub a { 23 | background:rgb(158, 158, 158); 24 | color:#fff; 25 | text-decoration:none; 26 | font-family:arial,sans-serif; 27 | text-align:center; 28 | font-weight:bold; 29 | padding:5px 40px; 30 | font-size:1rem; 31 | line-height:2rem; 32 | position:relative; 33 | transition:0.5s; 34 | } 35 | 36 | #forkongithub a:hover { 37 | background:#14ca29; 38 | color:#fff; 39 | } 40 | 41 | #forkongithub a::after { 42 | bottom:1px; 43 | top:auto; 44 | } 45 | 46 | @media screen and (min-width:800px) { 47 | #forkongithub{ 48 | position:fixed; 49 | display:block; 50 | top:0; 51 | right:0; 52 | width:200px; 53 | overflow:hidden; 54 | height:200px; 55 | z-index:9999; 56 | } 57 | 58 | #forkongithub a { 59 | width:200px; 60 | position:absolute; 61 | top:60px; 62 | right:-60px; 63 | transform:rotate(45deg); 64 | -webkit-transform:rotate(45deg); 65 | -ms-transform:rotate(45deg); 66 | -moz-transform:rotate(45deg); 67 | -o-transform:rotate(45deg); 68 | box-shadow:4px 4px 10px rgba(0,0,0,0.4); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /docs/_static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scienxlab/unmap/30269199b54be09072f0950f0d6ccbd9843a739b/docs/_static/favicon.ico -------------------------------------------------------------------------------- /docs/_static/unmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scienxlab/unmap/30269199b54be09072f0950f0d6ccbd9843a739b/docs/_static/unmap.png -------------------------------------------------------------------------------- /docs/_templates/links.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | 3 | # -- Setup function ---------------------------------------------------------- 4 | 5 | # Defines custom steps in the process. 6 | 7 | def autodoc_skip_member(app, what, name, obj, skip, options): 8 | """Exclude all private attributes, methods, and dunder methods from Sphinx.""" 9 | import re 10 | exclude = re.findall(r'\._.*', str(obj)) 11 | return skip or exclude 12 | 13 | def remove_module_docstring(app, what, name, obj, options, lines): 14 | if what == "module": 15 | keep = [i for i, line in enumerate(lines) if line.startswith("Author: ")] 16 | if keep: 17 | del lines[keep[0]:] 18 | return 19 | 20 | def setup(app): 21 | app.connect('autodoc-skip-member', autodoc_skip_member) 22 | app.connect("autodoc-process-docstring", remove_module_docstring) 23 | return 24 | 25 | # -- Path setup -------------------------------------------------------------- 26 | 27 | # If extensions (or modules to document with autodoc) are in another directory, 28 | # add these directories to sys.path here. If the directory is relative to the 29 | # documentation root, use os.path.abspath to make it absolute, like shown here. 30 | import os 31 | import sys 32 | sys.path.insert(0, os.path.abspath('..')) 33 | 34 | # -- Project information ----------------------------------------------------- 35 | project = 'unmap' 36 | copyright = '2023, Matt Hall' 37 | author = 'Matt Hall' 38 | 39 | # -- General configuration --------------------------------------------------- 40 | 41 | # Add any Sphinx extension module names here, as strings. They can be 42 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 43 | # ones. 44 | extensions = ['sphinx.ext.githubpages', 45 | 'sphinxcontrib.apidoc', 46 | 'sphinx.ext.napoleon', 47 | 'myst_nb', 48 | 'sphinx.ext.viewcode' 49 | ] 50 | 51 | myst_enable_extensions = ["dollarmath", "amsmath"] 52 | jupyter_execute_notebooks = "force" 53 | 54 | # Apidoc automation 55 | # https://pypi.org/project/sphinxcontrib-apidoc/ 56 | # The apidoc extension and this code automatically update apidoc. 57 | apidoc_module_dir = '../unmap' 58 | apidoc_output_dir = './' 59 | apidoc_excluded_paths = [] 60 | apidoc_toc_file = False 61 | apidoc_separate_modules = False 62 | 63 | # Add any paths that contain templates here, relative to this directory. 64 | templates_path = ['_templates'] 65 | 66 | # List of patterns, relative to source directory, that match files and 67 | # directories to ignore when looking for source files. 68 | # This pattern also affects html_static_path and html_extra_path. 69 | exclude_patterns = ['_build', 'notebooks'] 70 | 71 | # -- Options for HTML output ------------------------------------------------- 72 | 73 | # The theme to use for HTML and HTML Help pages. See the documentation for 74 | # a list of builtin themes. 75 | # 76 | # https://sphinx-themes.org/sample-sites/furo/ 77 | html_theme = 'furo' 78 | 79 | html_theme_options = { 80 | "sidebar_hide_name": True, 81 | } 82 | 83 | # Add any paths that contain custom static files (such as style sheets) here, 84 | # relative to this directory. They are copied after the builtin static files, 85 | # so a file named "default.css" will overwrite the builtin "default.css". 86 | html_static_path = ['_static'] 87 | 88 | html_css_files = [ 89 | 'custom.css', 90 | ] 91 | 92 | # Branding. 93 | html_favicon = '_static/favicon.ico' 94 | html_logo = '_static/unmap.png' 95 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | :hide-toc: 2 | 3 | .. container:: noclass 4 | :name: forkongithub 5 | 6 | `Fork on GitHub `_ 7 | 8 | 9 | unmap: data liberation for images 10 | ================================= 11 | 12 | | ``unmap`` recovers data from pseudocolour images. 13 | 14 | 15 | User guide 16 | ---------- 17 | 18 | .. toctree:: 19 | :maxdepth: 2 20 | :caption: User guide 21 | 22 | readme 23 | userguide/Overview_of_unmap.ipynb 24 | userguide/Unmap_data_from_an_image.ipynb 25 | userguide/Guess_the_colourmap_from_an_image.ipynb 26 | 27 | 28 | API reference 29 | ------------- 30 | 31 | .. toctree:: 32 | :maxdepth: 2 33 | :caption: API reference 34 | 35 | unmap 36 | 37 | * :ref:`genindex` 38 | 39 | .. toctree:: 40 | :caption: Project links 41 | :hidden: 42 | 43 | PyPI releases 44 | Code in GitHub 45 | Issue tracker 46 | Community guidelines 47 | More tools 48 | -------------------------------------------------------------------------------- /docs/notebooks/Overview_of_unmap.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "147bf201", 6 | "metadata": {}, 7 | "source": [ 8 | "# Overview of `unmap`\n", 9 | "\n", 10 | "This library aims to do 2 things:\n", 11 | "\n", 12 | "1. Guess the colourmap from a pseudocolour image, for those occasions when the colourmap is not known and not published along with the image.\n", 13 | "1. Recover ('unmap') the data from a pseudocolour image, essentially performing the reverse of the pseudocolour process.\n", 14 | "\n", 15 | "These 2 distinct tasks are illustrated below." 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": 1, 21 | "id": "3091fc50", 22 | "metadata": {}, 23 | "outputs": [ 24 | { 25 | "data": { 26 | "text/plain": [ 27 | "" 28 | ] 29 | }, 30 | "execution_count": 1, 31 | "metadata": {}, 32 | "output_type": "execute_result" 33 | }, 34 | { 35 | "data": { 36 | "image/png": "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\n", 37 | "text/plain": [ 38 | "
" 39 | ] 40 | }, 41 | "metadata": {}, 42 | "output_type": "display_data" 43 | } 44 | ], 45 | "source": [ 46 | "from gio import generate_random_surface\n", 47 | "import matplotlib.pyplot as plt\n", 48 | "import matplotlib.cm as cm\n", 49 | "\n", 50 | "# Make a random surface and normalize it to the range [0, 1].\n", 51 | "data = generate_random_surface(size=(32, 32), random_seed=42).values\n", 52 | "data = (data - data.min()) / (data.max() - data.min())\n", 53 | "\n", 54 | "# Take a look at it.\n", 55 | "plt.imshow(data, cmap='Greys_r')" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "id": "a530d1fb", 61 | "metadata": {}, 62 | "source": [ 63 | "Note: this is a greyscale image; it's the closest thing we can make to a 'pure' visualization of the data. The values in the data array correspond to a grey level from 0 (black) to 1 (white).\n", 64 | "\n", 65 | "If we were to look closely at the top-left corner, it's just a grid of numbers..." 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": 2, 71 | "id": "d61174af", 72 | "metadata": {}, 73 | "outputs": [ 74 | { 75 | "data": { 76 | "image/png": "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\n", 77 | "text/plain": [ 78 | "
" 79 | ] 80 | }, 81 | "metadata": {}, 82 | "output_type": "display_data" 83 | } 84 | ], 85 | "source": [ 86 | "fig, ax = plt.subplots()\n", 87 | "for i in range(5):\n", 88 | " for j in range(5):\n", 89 | " ax.text(i, j, f'{data[i, j]:0.2f}', ha='center')\n", 90 | "ax.imshow(data[:5, :5], vmin=0, vmax=1)\n", 91 | "ax.axis('off')\n", 92 | "plt.show()" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "id": "36c0451a", 98 | "metadata": {}, 99 | "source": [ 100 | "Now we'll make an RGB image out of this 2D array. This involves using a look-up table of colours, called a **colourmap**, to transform each data value into an RGB colour, which is a 3-tuple of floats like (0.25, 0.33, 0.9). In doing so, we lose the data values themselves; they are now encoded as colours." 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": 3, 106 | "id": "ef0d91fd", 107 | "metadata": {}, 108 | "outputs": [ 109 | { 110 | "data": { 111 | "text/plain": [ 112 | "" 113 | ] 114 | }, 115 | "execution_count": 3, 116 | "metadata": {}, 117 | "output_type": "execute_result" 118 | }, 119 | { 120 | "data": { 121 | "image/png": "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JvP/A0rnwv502U5p9u/PxsZG3X///dq1a5d27dqllStX6oYbbpgYNF/qvgxmga9//evBbbfdNum6r3zlK8Ff//Vfe1rR1LvvvvuCJUuW+F7GtJEUPPPMMxN/LxaLQV1dXXD//fdPXDc2Nhak0+ngH/7hHzyscGp8ejuDIAhWr14d3HDDDV7WM116e3sDScHWrVuDIJi7+/PT2xkEc3N/BkEQVFZWBv/8z//8pe/LGX8mlMvl9Prrr6u1tXXS9a2trdq+fbunVU2PvXv3qqGhQS0tLfre976nDz74wPeSpk1nZ6d6enom7dd4PK6rr756zu1XSdqyZYtqamq0aNEi3Xrrrert7fW9pFMyMDAgSaqqqpI0d/fnp7fzmLm0PwuFgp588kllMhktW7bsS9+XM34IHT58WIVCQbW1tZOur62tVU9Pj6dVTb3LLrtMjz32mF566SX97Gc/U09Pj5YvX66+vj7fS5sWx/bdXN+vktTW1qbHH39cr7zyih588EHt3LlTK1euVDab9b20kxIEgdauXasrrrhCixcvljQ39+eJtlOaO/tz9+7dSiaTisfjuu222/TMM8/oggsu+NL35YxL0f4sf/y1DtInB8inr5vN2traJv580UUXadmyZTrnnHP06KOPau3atR5XNr3m+n6VpJtuumniz4sXL9bSpUvV3Nys559/XqtWrfK4spNz55136q233tJrr7123G1zaX9+1nbOlf153nnn6c0331R/f79+8YtfaPXq1dq6devE7V/WvpzxZ0LV1dWKRCLHTeDe3t7jJvVcUl5erosuukh79+71vZRpceydf6fbfpWk+vp6NTc3z8p9e9ddd+m5557Tq6++OukrV+ba/vys7TyR2bo/Y7GYzj33XC1dulQdHR1asmSJfvrTn37p+3LGD6FYLKZLLrlEmzdvnnT95s2btXz5ck+rmn7ZbFbvvPOO6uvrfS9lWrS0tKiurm7Sfs3lctq6deuc3q+S1NfXp66urlm1b4Mg0J133qmnn35ar7zyilpaWibdPlf25xdt54nMxv15IkEQKJvNfvn7csrf6jANnnzyySAajQY///nPg7fffjtYs2ZNUF5eHuzbt8/30qbMj3/842DLli3BBx98EOzYsSP40z/90yCVSs3qbRwaGgreeOON4I033ggkBRs2bAjeeOON4MMPPwyCIAjuv//+IJ1OB08//XSwe/fu4Pvf/35QX18fDA4Oel65zedt59DQUPDjH/842L59e9DZ2Rm8+uqrwbJly4IFCxbMqu380Y9+FKTT6WDLli1Bd3f3xGVkZGSiZi7szy/azrmyP9etWxds27Yt6OzsDN56663gnnvuCcLhcPDyyy8HQfDl7stZMYSCIAj+/u//Pmhubg5isVjwta99bdJbJueCm266Kaivrw+i0WjQ0NAQrFq1KtizZ4/vZZ2SV199NZB03GX16tVBEHzytt777rsvqKurC+LxeHDVVVcFu3fv9rvok/B52zkyMhK0trYG8+fPD6LRaHDmmWcGq1evDvbv3+972SYn2j5JwSOPPDJRMxf25xdt51zZn3/+538+8Xw6f/784Jvf/ObEAAqCL3df8lUOAABvZvxrQgCAuYshBADwhiEEAPCGIQQA8IYhBADwhiEEAPCGIQQA8IYhBADwhiEEAPCGIQQA8IYhBADwhiEEAPDm/wJHnYI1a4q4xwAAAABJRU5ErkJggg==\n", 122 | "text/plain": [ 123 | "
" 124 | ] 125 | }, 126 | "metadata": {}, 127 | "output_type": "display_data" 128 | } 129 | ], 130 | "source": [ 131 | "# Make a pseudocolored RGB image array.\n", 132 | "imarray = cm.turbo(data)\n", 133 | "\n", 134 | "# Display the RGB image.\n", 135 | "plt.imshow(imarray)" 136 | ] 137 | }, 138 | { 139 | "cell_type": "markdown", 140 | "id": "85730e4d", 141 | "metadata": {}, 142 | "source": [ 143 | "If we know the colourmap and the original range of the data, then recovering data from this image is not too hard: we can do a reverse look-up of each colour in the image and replace it with the corresponding data value. This library, `unmap`, can handle this.\n", 144 | "\n", 145 | "On the other hand, without knowledge of the colourmap, or the range of values in the original array, it is hard to recover the original dataset from this image. But in some cases, `unmap` can help with this too." 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "id": "de210c87", 151 | "metadata": {}, 152 | "source": [ 153 | "## Guess the colourmap\n", 154 | "\n", 155 | "We know that we used the `jet` colormap to create this image, but that information is not contained in the image. It's just a 3D NumPy array.\n", 156 | "\n", 157 | "Let's see if `unmap.guess_cmap_from_array()` can guess the colourmap we used:" 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": 4, 163 | "id": "a41ba1a2", 164 | "metadata": {}, 165 | "outputs": [ 166 | { 167 | "data": { 168 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAgAAAABACAYAAABsv8+/AAAAGHRFWHRUaXRsZQByZWNvdmVyZWQgY29sb3JtYXCmg4jWAAAAHnRFWHREZXNjcmlwdGlvbgByZWNvdmVyZWQgY29sb3JtYXDkGG5dAAAAMHRFWHRBdXRob3IATWF0cGxvdGxpYiB2My42LjIsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmdxzlVfAAAAMnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHYzLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZ19oyngAAAKOSURBVHic7dY9ctswGARQkJRzgJwkfe5/qcQCUghgBhDpTOp9r/lm8UPKlordfnz/2UoppWzHa/RZ9seSx/5r/f38km/239ZH7u8r+0fPfR5f53J8m9eXfO7v8+c+P+9+k7e9r+9Lnve3/Xr97t72tn6d/36ubT53M9vIxzzbcbN+PnfruSx52d/adK717ffcLmc9rvfrXvv+13M938a9t9ym/Da3m/VzPr+cpZ8r+2cppZTtXO/5GPk57e9bn//KfR7/mv3eUa7vPZa89797nD/XS52fW9bz671lfX3uen7J43M9yjw/ys16e81vy3xbH++pr+//aOss0/7jzNdzX/Kjvn7g4+t9PLd5f8lHz+fP4y3P7+n/lnPuz3a5vvXPX879nutdrtP5/53t5n47c13ysl/n54z1Vq/Pt7rkfq8+r59Tl/v3ub9uWa915HK5/hz3xrk657Ff27w//rw1t7bcP5+3zH7us+df/T2/6/Uc+5/L13if+++xAABxFAAACKQAAEAgBQAAAikAABBIAQCAQAoAAARSAAAgkAIAAIEUAAAIpAAAQCAFAAACKQAAEEgBAIBACgAABFIAACCQAgAAgRQAAAikAABAIAUAAAIpAAAQSAEAgEAKAAAEUgAAIJACAACBFAAACKQAAEAgBQAAAikAABBIAQCAQAoAAARSAAAgkAIAAIEUAAAIpAAAQCAFAAACKQAAEEgBAIBACgAABFIAACCQAgAAgRQAAAikAABAIAUAAAIpAAAQSAEAgEAKAAAEUgAAIJACAACBFAAACKQAAEAgBQAAAikAABBIAQCAQAoAAARSAAAgkAIAAIEUAAAIpAAAQCAFAAACKQAAEEgBAIBACgAABPoDzAJODyx2+qwAAAAASUVORK5CYII=\n", 169 | "text/html": [ 170 | "
recovered
\"recovered
under
bad
over
" 171 | ], 172 | "text/plain": [ 173 | "" 174 | ] 175 | }, 176 | "execution_count": 4, 177 | "metadata": {}, 178 | "output_type": "execute_result" 179 | } 180 | ], 181 | "source": [ 182 | "import unmap\n", 183 | "\n", 184 | "cmap = unmap.guess_cmap_from_array(imarray)\n", 185 | "cmap" 186 | ] 187 | }, 188 | { 189 | "cell_type": "markdown", 190 | "id": "0e982d01", 191 | "metadata": {}, 192 | "source": [ 193 | "Phew! It looks good. Now we can give this to the unmapping function.\n", 194 | "\n", 195 | "## Recover the data\n", 196 | "\n", 197 | "Use `unmap.unmap()` to try recovering the data from an image array using the colourmap we guessed:" 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": 5, 203 | "id": "2f15e865", 204 | "metadata": {}, 205 | "outputs": [ 206 | { 207 | "data": { 208 | "text/plain": [ 209 | "array([[0.65882353, 0.62352941, 0.57647059, ..., 0.85490196, 0.81960784,\n", 210 | " 0.74117647],\n", 211 | " [0.63137255, 0.59607843, 0.5254902 , ..., 0.88627451, 0.86666667,\n", 212 | " 0.80784314],\n", 213 | " [0.62352941, 0.59215686, 0.51372549, ..., 0.90196078, 0.89019608,\n", 214 | " 0.83921569],\n", 215 | " ...,\n", 216 | " [0.69411765, 0.60392157, 0.54117647, ..., 0.56470588, 0.59215686,\n", 217 | " 0.64313725],\n", 218 | " [0.69019608, 0.60392157, 0.55294118, ..., 0.5254902 , 0.57254902,\n", 219 | " 0.61568627],\n", 220 | " [0.6745098 , 0.59607843, 0.5372549 , ..., 0.49411765, 0.55294118,\n", 221 | " 0.59607843]])" 222 | ] 223 | }, 224 | "execution_count": 5, 225 | "metadata": {}, 226 | "output_type": "execute_result" 227 | } 228 | ], 229 | "source": [ 230 | "rec = unmap.unmap(imarray, cmap)\n", 231 | "rec" 232 | ] 233 | }, 234 | { 235 | "cell_type": "markdown", 236 | "id": "e6f12fd4", 237 | "metadata": {}, 238 | "source": [ 239 | "Let's have a look at this data:" 240 | ] 241 | }, 242 | { 243 | "cell_type": "code", 244 | "execution_count": 6, 245 | "id": "2184ac07", 246 | "metadata": {}, 247 | "outputs": [ 248 | { 249 | "data": { 250 | "text/plain": [ 251 | "" 252 | ] 253 | }, 254 | "execution_count": 6, 255 | "metadata": {}, 256 | "output_type": "execute_result" 257 | }, 258 | { 259 | "data": { 260 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAaEAAAGdCAYAAAC7EMwUAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAsL0lEQVR4nO3df4yVZX738c/5ec+vMwMsMj9kpDxdcHdFSSpWoa4iLROnKdFlm7hrsoG0NesCJoTd2KJ/OGlSxthI3IRK2+2GYiqLf1StT3RVGgW6oTRgNPLgxrCPuI6V2VkQZob5cX7c9/X84cOkI4jXF+ZwzQzvV3ISOOc711z3fd3nfM99fnwm5ZxzAgAggHToCQAArl40IQBAMDQhAEAwNCEAQDA0IQBAMDQhAEAwNCEAQDA0IQBAMNnQE/i8JEn0ySefqFAoKJVKhZ4OAMDIOafBwUG1tbUpnb74uc6ka0KffPKJ2tvbQ08DAHCZenp6NHfu3IvWVK0JPf300/rbv/1bnThxQjfccIOeeuopffOb3/zSnysUCpKkRTs3KFMXef2uodGc97yKw3nvWknKfuI3B0mKTtnO3Op+m/jPY8SWrpRk/edSKtjmPTLbVj86x38707NHTWMXGvzra3Jl09gWSWJ7ZTt2tvpUyn/9a7O27by2od+7dknjr01jXx/9t3ft3Oygaeys/PfJ6cR2vz8VN5jqzyR13rWJqvcKT9llTPV95Ubv2v8zeK3/PIZK+t/3/mzs8fxiqtKEnnvuOW3cuFFPP/20/uAP/kD/8A//oM7OTr333nu67rrrLvqz516Cy9RF3k0ok/Y/wNLOdjCma/ybUCayHVyZvKEJVarXhOK8cd7G7UzXGpqQ/335s7nU+e+XbL56b4HGxiYkY72lCWVztrFz9f73iZoG20NGfY3/g2JD1jhvQxMqG/f3SGx7MC8Z6uMqvhWfNTahmrL/E/icsZFL8npLpSp7Y+vWrfrzP/9z/cVf/IW+/vWv66mnnlJ7e7u2b99ejV8HAJiiJrwJlUolvfXWW+ro6Bh3fUdHhw4cOHBefbFY1MDAwLgLAODqMOFN6OTJk4rjWM3NzeOub25uVm9v73n13d3dampqGrvwoQQAuHpU7cXJz78W6Jy74OuDmzdvVn9//9ilp6enWlMCAEwyE/7BhNmzZyuTyZx31tPX13fe2ZEkRVGkKPJ/8x8AMH1M+JlQPp/XzTffrD179oy7fs+ePVq2bNlE/zoAwBRWlY9ob9q0Sd/73ve0ZMkSLV26VP/4j/+ojz76SA8++GA1fh0AYIqqShO67777dOrUKf31X/+1Tpw4oUWLFumVV17RvHnzqvHrAABTVNUSE9atW6d169Zd8s+PlHLKZP2+SFUqGTbjrP+XsyQpO+z/xcx8v+0LpdEZ/y9x5gdt34JPMv6vtGZKti+4SbZ6Z/gS4ki97ZAcyfl/gc7yhU9JymX818fKOheLxNm+TDxc8b9PfFScZRo7Svsft4NJrWnsmpT/2EOJ7X3nM7HtW9OWpIK6dNE0dmPGPxUkl6qYxrbsw9EGQzKN/MclRRsAEAxNCAAQDE0IABAMTQgAEAxNCAAQDE0IABAMTQgAEAxNCAAQDE0IABAMTQgAEEzVYnsuV5JIqcQzfsQSUxLb5pEy1GdHbVEsliie3Cf9prHl/OeSm9lgHNsWr1Kp9T/MyvW2WKVyrf8CVXK2SBNLbI81hqeaz/5iY2zPYLnGu/b/np1tGvvTUr13bSHnH08jSXXpkndtbNzjlcRWX5vxvy+35s+YxrbE9tSn/PeJJGUy/sft7Nygd+2I4b7GmRAAIBiaEAAgGJoQACAYmhAAIBiaEAAgGJoQACAYmhAAIBiaEAAgGJoQACAYmhAAIBiaEAAgmEmbHVcblZWJ/Hpk4psxJ6lYlzHNw2UM9bbILmWG/fOVUsO2XC03NOQ/j6J/7pUk1Rq3s9Tgnx9WarI9LxoZ9V+fUt52uGcz/rl0lpw5SUqM+W4WccW2nSVD/ZkRW27gJ+km79pM2rYPc4b6KGPNDbSFTM6K/O9vNWnb/e0rmbPetXG6esdVtXAmBAAIhiYEAAiGJgQACIYmBAAIhiYEAAiGJgQACIYmBAAIhiYEAAiGJgQACIYmBAAIZtLG9nyldljZOr/oDEtkysmKLbanUp/zr62xRWYkef+5pCu22JH4TL//2LEtLiVTkzfVRwP+US/ZEdvzonS//yFcimxRLKZ5GCNnUsZ0Fef8a+PYtg9jw33CEpFlZd0n2Zz/faI2skXl1EUlU3065b9A/ZEt+mg07/8YNJgYx078xz5RmuFdWyz572/OhAAAwdCEAADB0IQAAMHQhAAAwdCEAADB0IQAAMHQhAAAwdCEAADB0IQAAMHQhAAAwdCEAADBTNrsuOsaPlW+wS+j7ES2yXvcYtm2yWdm+ueklRtsY1vqc3lbXpvShoy82JaplqrY6tNl/1yt3KAhJE1S/oz/86hiJjKNPVpnWM+0bd6yRrBZhi8b8/dG/evTxeplx8W1tvy9UlP15pI15FFKUiXx34eJs83bku82lLId47+tFLxrPxqZ6V1bHvXP3uNMCAAQzIQ3oa6uLqVSqXGXlpaWif41AIBpoCovx91www3693//97H/ZzK2P58AALg6VKUJZbNZzn4AAF+qKu8JHTt2TG1tbZo/f76+853v6IMPPvjC2mKxqIGBgXEXAMDVYcKb0K233qpnnnlGr732mn7yk5+ot7dXy5Yt06lTpy5Y393draamprFLe3v7RE8JADBJTXgT6uzs1Le//W3deOON+qM/+iO9/PLLkqSdO3desH7z5s3q7+8fu/T09Ez0lAAAk1TVvydUX1+vG2+8UceOHbvg7VEUKYpsn20HAEwPVf+eULFY1C9/+Uu1trZW+1cBAKaYCW9CP/rRj7Rv3z4dP35c//Vf/6U//dM/1cDAgNasWTPRvwoAMMVN+MtxH3/8sb773e/q5MmTuuaaa3Tbbbfp4MGDmjdvnmmca2vOqKbGL66ikvh/D+lUVGeaR39dxbu2UmvbnXGt/3MAV2t7yTJzzVdM9RYuY4yFKftHoGRHbXPJnbXMwzZvl67iCwXVS5xRypaqpJQhoSZlTCdKDHeJsiH6RpLK9YaonKSKO1xSPu2/07NpWyRQbDhXOBPbHt96Rmd51/aN+Ef8VEaK3rUT3oR279490UMCAKYpsuMAAMHQhAAAwdCEAADB0IQAAMHQhAAAwdCEAADB0IQAAMHQhAAAwdCEAADB0IQAAMFU/U85XAlJNYO4DJyxpcd5Q3ZcnS07Ll0s+RcntkAwc6aaYfjsiC1XKzpt2If+EYOSbBlshuiwz+ZiPWSreIhb5pL4xTmOKc0wDG7MpVPa/wcyGdvg+ax/ZqQk5TP+9TljuF9ieGAZjGtMY/+21OA/dsn/MahiePjhTAgAEAxNCAAQDE0IABAMTQgAEAxNCAAQDE0IABAMTQgAEAxNCAAQDE0IABAMTQgAEMykje3pGZmlfCbvVfvbUf/oiZGSLXfElf37dMqWOGObR86YOZMx1BviTyRJWdtcUoZYoEzJGK9y1n+nW6N1MkXD2GVj9JExhselDT9gfGpZqfH/gWJT9Z63OuOjUTrnvz65nC2GJ5e23Zmzhjt/Ylz8UcOO6Y9rTWOfLftH8ZQq/vOIY/87G2dCAIBgaEIAgGBoQgCAYGhCAIBgaEIAgGBoQgCAYGhCAIBgaEIAgGBoQgCAYGhCAIBgaEIAgGAmbXbciZFGZdN+uUb9xRrvcYdH/fLozkkN+eekpW3xVJLzzxtLsrbnC2lLvpthHpLkjHOx5J6lrPluhqy5tDGXLjdY9p/HiH/tZ5Ox7cMk77+ecY0t2y/J+a+PMz5tjQ1RjZWCbfFzGUN2XMY2dsaYHZdO+R9bsfG5/3Dsn+82WPF/LJSk4Yr/42GpYjgGDbWcCQEAgqEJAQCCoQkBAIKhCQEAgqEJAQCCoQkBAIKhCQEAgqEJAQCCoQkBAIKhCQEAgqEJAQCCmbTZccPlvLJlv1yjkZJ/QFWlbNvkdMk/V0u2aDIlWf+x4zrrvP0zpFJlW66WiwyBYEap2LYTLVlzmZItD8w6FwtLFpxky4Or1NnGLjX4PxctNRruD5Iq9YZ9WGPMd8v6r6chvlCSlDjbDwxX/O8Tn5bqTWOfSdV61/52tME09mDJP5euWPJ/DIpL/kGanAkBAIIxN6H9+/dr1apVamtrUyqV0osvvjjuduecurq61NbWptraWi1fvlxHjx6dqPkCAKYRcxMaGhrS4sWLtW3btgve/sQTT2jr1q3atm2bDh06pJaWFq1cuVKDg4OXPVkAwPRifk+os7NTnZ2dF7zNOaennnpKjz76qFavXi1J2rlzp5qbm7Vr1y59//vfv7zZAgCmlQl9T+j48ePq7e1VR0fH2HVRFOnOO+/UgQMHLvgzxWJRAwMD4y4AgKvDhDah3t5eSVJzc/O465ubm8du+7zu7m41NTWNXdrb2ydySgCASawqn45LpcZ/vNE5d95152zevFn9/f1jl56enmpMCQAwCU3o94RaWlokfXZG1NraOnZ9X1/feWdH50RRpCjy/6w6AGD6mNAzofnz56ulpUV79uwZu65UKmnfvn1atmzZRP4qAMA0YD4TOnv2rH71q1+N/f/48eN65513NGvWLF133XXauHGjtmzZogULFmjBggXasmWL6urqdP/990/oxAEAU5+5CR0+fFh33XXX2P83bdokSVqzZo3++Z//WQ8//LBGRka0bt06nT59Wrfeeqtef/11FQqFiZv158SJ/wmdMyaxfMFbWReUGNNsynX+g5carUvlH9uTGfaP2LgU6ZItjsXEGMdiUSn4L2iS8YuYOieutb0IYTpWGmw7pWyI4ikVbHeg8iz/tc/kbbFK6bR/fWK834+UrdFUdd6VwxXbsWJx1hDDI0kDI/6PE+Wi/2NQYqg1N6Hly5fLXeSRPJVKqaurS11dXdahAQBXGbLjAADB0IQAAMHQhAAAwdCEAADB0IQAAMHQhAAAwdCEAADB0IQAAMHQhAAAwdCEAADBTOifcphIuUysbMYvdyqd8g+GsmTBSVIS+Y+dGCOhygX/yaRj2/OFlMvYJmNgzZrLFA35YYZaSYoj/+0sG7LgJKnc4L/Pi0229Sk12Q7ESq2htsGY79bkv56pOuPa5/zz3TIZW3acRalie6irxLZ9WKr4H4eZtG1sS+6ddTtHRvwftJIRQ3bcqH8tZ0IAgGBoQgCAYGhCAIBgaEIAgGBoQgCAYGhCAIBgaEIAgGBoQgCAYGhCAIBgaEIAgGAmbWxPTbaiXNavR6bThrgPQ8SPJLm8/9hxja2nV8r+0S2p2Bbzki77zyVdsu2TzLCpXKmRsn9tYotuSbL+eTZJZNuHI1/x34ejs21jF2fZttN9peRdm6uxRevMqPEf23RfM0oS4/3HUO+cbX3Kxpissvxje1LWxyDD3CtlW1yXMzyupAyPKZZazoQAAMHQhAAAwdCEAADB0IQAAMHQhAAAwdCEAADB0IQAAMHQhAAAwdCEAADB0IQAAMHQhAAAwUza7LhsKlY2FYeehmSInEpytkyo2JBllrbFgalS4z92krPlarmMrT4VG9YxNmaTpf3nUq6zPecqzvQfe7TFdqymZxVN9TMK/oF9s+ts4X4za/zr07Id48OVvHdtf6nGNPZIOedda82Cq8S2DLbEmE1n4Qy7PJM1Zi8a5p3U+T8IuZR/LWdCAIBgaEIAgGBoQgCAYGhCAIBgaEIAgGBoQgCAYGhCAIBgaEIAgGBoQgCAYGhCAIBgJm1sz3A5r2w58qotGuI7koqt76ZK/vWpxBjdkfLP40iMUTmJ/y5RnDeOHdkiTVxkmEzFFjsS1/jPxRJl9NnYhuLGsmnsQv2oqf6a+iHv2oWNfaaxf6fmlHdtlLZtZ3+lzrv24+JM09h9ow3etZb4IEkqxraHxrIx5sfCEq1TSWyPb6WK/7xHcv77ME6XvGs5EwIABEMTAgAEY25C+/fv16pVq9TW1qZUKqUXX3xx3O1r165VKpUad7ntttsmar4AgGnE3ISGhoa0ePFibdu27Qtr7r77bp04cWLs8sorr1zWJAEA05P5gwmdnZ3q7Oy8aE0URWppabnkSQEArg5VeU9o7969mjNnjhYuXKgHHnhAfX1f/GmdYrGogYGBcRcAwNVhwptQZ2ennn32Wb3xxht68skndejQIa1YsULF4oX/kmR3d7eamprGLu3t7RM9JQDAJDXh3xO67777xv69aNEiLVmyRPPmzdPLL7+s1atXn1e/efNmbdq0aez/AwMDNCIAuEpU/cuqra2tmjdvno4dO3bB26MoUhT5fSkVADC9VP17QqdOnVJPT49aW1ur/asAAFOM+Uzo7Nmz+tWvfjX2/+PHj+udd97RrFmzNGvWLHV1denb3/62Wltb9eGHH+qRRx7R7Nmz9a1vfWtCJw4AmPrMTejw4cO66667xv5/7v2cNWvWaPv27Tpy5IieeeYZnTlzRq2trbrrrrv03HPPqVAomH7P6dE6ZdJ+L9ONjhhyoQYMOWaSsmcN2XG2WC2lYmPWnIEzZM1Zs+MqtbacrHST/8utqYp/np51LrEtPkxJ3n8u6Yxt3tmMLSOvLuufxdWSt33CdF7+pHftjIx/hp0kxXn/+8/1NbWmsU81+GfHnTUFAUqfVupN9SOx/+NKbHwBypIdN2I8yE+X/Pf56aJ/DmAlX9T/9aw1N6Hly5fLuS++w7322mvWIQEAVymy4wAAwdCEAADB0IQAAMHQhAAAwdCEAADB0IQAAMHQhAAAwdCEAADB0IQAAMHQhAAAwVT9TzlcqsGhSGnnl/cUG/Lgok9tfTdd9M9tStniw0ysYztDvJs9O85Wn3KGw8y4neV6//Ws1NnmHUf++W6ZtC0LLp+tmOobchf+o5AX0pQdNo19TdY/a+5/Zc+axs6nDBmG7rRp7EFDptpgYsuMHExsWXOjzpIdZzsOy4b7z6hxO0/F/vl7/12c6V1bjMr6hWctZ0IAgGBoQgCAYGhCAIBgaEIAgGBoQgCAYGhCAIBgaEIAgGBoQgCAYGhCAIBgaEIAgGAmbWxPeTSndMovgiJ/0j+jJjdoi8wwJqCYJJa9b5u2UoZUGGd8KlKus/2AJRYoyRojTRr860tNpqHlZpW9a+vr/GN1JGlmzYipvrXGP1qnJdtvGvvajH8Uz4y07SGjIe0ffxM7W/RRwZW8a8tpW0zSqPPf35/V+9eWzbE9/ve3wSRvGntGxv8BzlI7kvjvb86EAADB0IQAAMHQhAAAwdCEAADB0IQAAMHQhAAAwdCEAADB0IQAAMHQhAAAwdCEAADB0IQAAMFM2uw4nc1Ksd/0ssP+WUzRp4aQJ0nRoH99KraNHUf+864YaiXJ+cfpmXPpKnW2+jjyf65TqbWNXS747/Nyq3/WmCQ1zvDPympttGWNXV/4jan+htqPvWuvzZ42jV1I+x8AuZTlwLKpKK7a2LFs983JZNRwZx51fnmblyJnCKQsG9aSMyEAQDA0IQBAMDQhAEAwNCEAQDA0IQBAMDQhAEAwNCEAQDA0IQBAMDQhAEAwNCEAQDCTNrYnXUop7RknkjGksVhieCSpts9/8Mxw2TR2XOcfsVGaYVuqYqN/1Ee53jS0So22nJ9yg/8+L11ji27JNPqvz+xG/xgeSWpv9I+/+UZjr2nsr9d+YptL7pR3bVt2xDR22pDbNJzYjvFPXdF/bGOyTjXjbEad7f5mGX8oiUxjDyb+WVajiW07y4Z9GBvOWUZi/4gfzoQAAMGYmlB3d7duueUWFQoFzZkzR/fee6/ef//9cTXOOXV1damtrU21tbVavny5jh49OqGTBgBMD6YmtG/fPq1fv14HDx7Unj17VKlU1NHRoaGhobGaJ554Qlu3btW2bdt06NAhtbS0aOXKlRocHJzwyQMApjbTC5+vvvrquP/v2LFDc+bM0VtvvaU77rhDzjk99dRTevTRR7V69WpJ0s6dO9Xc3Kxdu3bp+9///sTNHAAw5V3We0L9/f2SpFmzZkmSjh8/rt7eXnV0dIzVRFGkO++8UwcOHLjgGMViUQMDA+MuAICrwyU3IeecNm3apNtvv12LFi2SJPX2fvYJoebm5nG1zc3NY7d9Xnd3t5qamsYu7e3tlzolAMAUc8lNaMOGDXr33Xf1s5/97LzbUqnxH/l0zp133TmbN29Wf3//2KWnp+dSpwQAmGIu6XtCDz30kF566SXt379fc+fOHbu+paVF0mdnRK2trWPX9/X1nXd2dE4URYoi2+fmAQDTg+lMyDmnDRs26Pnnn9cbb7yh+fPnj7t9/vz5amlp0Z49e8auK5VK2rdvn5YtWzYxMwYATBumM6H169dr165d+rd/+zcVCoWx93mamppUW1urVCqljRs3asuWLVqwYIEWLFigLVu2qK6uTvfff39VNgAAMHWZmtD27dslScuXLx93/Y4dO7R27VpJ0sMPP6yRkRGtW7dOp0+f1q233qrXX39dhUJhQiYMAJg+TE3IuS8Pd0qlUurq6lJXV9elzkmSlGTkPTtneFExXbYFVOVO+ueNpfvPmsbORP45Ty49yzR2uc5/p8SR7fMp5YJtH5au9c93q28aNY3d3Oj/JejfafjUNPbXGk5417bnbGO3ZPtt9ZmhLy/6/6yfNhr2uF+fcybxzxqTpE8qM71rT8UNprEtOWmW3DPJlqkmSUXDXE6Wbdt5plLnP4+4enGg9Vn/HMCSIUeT7DgAQDA0IQBAMDQhAEAwNCEAQDA0IQBAMDQhAEAwNCEAQDA0IQBAMDQhAEAwNCEAQDDVy3i4TK4ulquNvWqTrH8vtUT8SFIq9puDJLlB/wgZSUo5//iOdMl/HpKUZPPetZVa09AqzbbNpa7RP4rn2iZbnM3XZ1z4jyVeyILa35jG/krGP4ZpRsY/3kmS0qnEVF82HLj9tlQl/Tau9679b0MMjyR9XPKPm+oZtUVTDcX+x3i1DVX85zJQqjGNPVrxjwSqJLYHuGza/zhsyPvH9pSH/KO6OBMCAARDEwIABEMTAgAEQxMCAARDEwIABEMTAgAEQxMCAARDEwIABEMTAgAEQxMCAARDEwIABDNps+NS+VipvF9GmcsaspVqUqZ5uFzGuzYVRaaxlfF/DhDX2ZaqXOdfW6m3hY1lCmVT/Yz6Ee/a6xpOm8b+nZqT3rWFtP88JCk2PEc7Ext2uKShxHasfJryzxksOf9jVpJ+W2n0rv1wdLZp7F8P++fB9RdtIYalxLad1VSO/edSrNjuy8WK/9jO2R7fchn/HMjEMHal6F/LmRAAIBiaEAAgGJoQACAYmhAAIBiaEAAgGJoQACAYmhAAIBiaEAAgGJoQACAYmhAAIJhJG9uTzjils35xMknOP3YmzttiLeJCjXdt7qwtuiVp8B+73GCLKCk3+m9neaZ/dIck1dWWTPUza/zjcmblh0xj51L+cx9MbLEwZ2P/9Rk01Eq2CBTJFiE0EvvHWEnSp6V6/9qi7Rg/W/KPJxo1xtnEiW0fVlOS+K9PyRDDI0nlsqHeeFwlhsfDfNb/vhYbYow4EwIABEMTAgAEQxMCAARDEwIABEMTAgAEQxMCAARDEwIABEMTAgAEQxMCAARDEwIABEMTAgAEM3mz49KJ0unEqzbO+48b+0dZSZLKBf8crnSpYBq7NNN/MiMzbc8Xyv5xYFKhbBq7oaZoqp+R98+OK2RGTWMPJ/6Lf7JsW5//HpnhXXumZMulK8a2u17Fkk1myO2SpLKhvmzMPbPMu2Ic2/lHRipV5Zg5y1xi83ZWb/KWfV6O/dcyNqw7Z0IAgGBMTai7u1u33HKLCoWC5syZo3vvvVfvv//+uJq1a9cqlUqNu9x2220TOmkAwPRgakL79u3T+vXrdfDgQe3Zs0eVSkUdHR0aGhofv3/33XfrxIkTY5dXXnllQicNAJgeTC9Mv/rqq+P+v2PHDs2ZM0dvvfWW7rjjjrHroyhSS0vLxMwQADBtXdZ7Qv39/ZKkWbNmjbt+7969mjNnjhYuXKgHHnhAfX19XzhGsVjUwMDAuAsA4OpwyU3IOadNmzbp9ttv16JFi8au7+zs1LPPPqs33nhDTz75pA4dOqQVK1aoWLzwJ6q6u7vV1NQ0dmlvb7/UKQEApphL/oj2hg0b9O677+oXv/jFuOvvu+++sX8vWrRIS5Ys0bx58/Tyyy9r9erV542zefNmbdq0aez/AwMDNCIAuEpcUhN66KGH9NJLL2n//v2aO3fuRWtbW1s1b948HTt27IK3R1GkKDJ+eQcAMC2YmpBzTg899JBeeOEF7d27V/Pnz//Snzl16pR6enrU2tp6yZMEAExPpveE1q9fr3/5l3/Rrl27VCgU1Nvbq97eXo2MfPaN+LNnz+pHP/qR/vM//1Mffvih9u7dq1WrVmn27Nn61re+VZUNAABMXaYzoe3bt0uSli9fPu76HTt2aO3atcpkMjpy5IieeeYZnTlzRq2trbrrrrv03HPPqVCwRaYAAKY/88txF1NbW6vXXnvtsiZ0Tr6mokyNX6bZUMEvY06Syo223KbRWf71cWTLDys1+J+IFr9iy48qzYy9a3NRxTR2bc6WNRel/ccfjGtMY/cWm7xrPx6eYRr7N8MN3rVnR23vaxaLtrdjE0MWl4ttx4pLDPXWHLOUJeDNOHQ1x04bxjYy7W9jvXXelsy7aiE7DgAQDE0IABAMTQgAEAxNCAAQDE0IABAMTQgAEAxNCAAQDE0IABAMTQgAEAxNCAAQzCX/PaFqq8lVlMn5ReYMNfjHyFTqbLE9I7MN0TqNtjiOcoN/fXGGMV9jhv8+iSJbDE/aEpciaSTOedeeGG00jf1psd67tm/IP4ZHks4M+scwlUf8t1GSUoO2u152yP84zBSNGTX+qVdytruPkrz/sWKplaQk8q93ecNGSlLOVp/KTIL8mypLVyndiTMhAEAwNCEAQDA0IQBAMDQhAEAwNCEAQDA0IQBAMDQhAEAwNCEAQDA0IQBAMDQhAEAwNCEAQDCTNjuuLldSNu8XQHTakPNULhgzpBL/Pp1KbJldlXr/vKnyHFu+Wy5f8a/NxKaxE0swlKRPi3XetbGzPS8aLEbetWdH/GslqVLyv3uk+23Zcbl+23Zmhw1jnzUNrUzJkO9mzI6La/yPlUqt7bhKDLs8iWz7u1JrzLGrMzyuZI2PQdXMpTPclxPDNCy1nAkBAIKhCQEAgqEJAQCCoQkBAIKhCQEAgqEJAQCCoQkBAIKhCQEAgqEJAQCCoQkBAIKZtLE9mXSiTNov3iKdMsTf1NsiM1KxIdYia4z6aPKP1snU+tdKUhTZ6i1GK7bDphT7Z73EhpgkSSqW/ecSx7axU4bjyuVsa+/StvqUIV4lU7SNHQ3416fLxmPccKiU66zROoaxG2yRQJkRYwRXyb8+rrFtZxL5P2Y5Q60kJYZIoIrlfmyo5UwIABAMTQgAEAxNCAAQDE0IABAMTQgAEAxNCAAQDE0IABAMTQgAEAxNCAAQDE0IABAMTQgAEMykzY5LXEqJIS/LmyErSZLiOkNuU01sm4qhPp+3ZcFlPXP3LkWp4p8LZZUYs+Msx4h1H6YN+W6G6DBJUll521wMWVzW3DNLdlxuyHiMF/3rk6xt7csN/vuk2GQ7ZoszjAtqOA5TxljHpGTJXrTNOzEc48WSIaex5L+RnAkBAIIxNaHt27frpptuUmNjoxobG7V06VL9/Oc/H7vdOaeuri61tbWptrZWy5cv19GjRyd80gCA6cHUhObOnavHH39chw8f1uHDh7VixQrdc889Y43miSee0NatW7Vt2zYdOnRILS0tWrlypQYHB6syeQDA1GZqQqtWrdIf//Efa+HChVq4cKH+5m/+Rg0NDTp48KCcc3rqqaf06KOPavXq1Vq0aJF27typ4eFh7dq1q1rzBwBMYZf8nlAcx9q9e7eGhoa0dOlSHT9+XL29vero6BiriaJId955pw4cOPCF4xSLRQ0MDIy7AACuDuYmdOTIETU0NCiKIj344IN64YUX9I1vfEO9vb2SpObm5nH1zc3NY7ddSHd3t5qamsYu7e3t1ikBAKYocxO6/vrr9c477+jgwYP6wQ9+oDVr1ui9994buz2VGv8RQefcedf9T5s3b1Z/f//YpaenxzolAMAUZf6eUD6f11e/+lVJ0pIlS3To0CH9+Mc/1l/+5V9Kknp7e9Xa2jpW39fXd97Z0f8URZGiKLJOAwAwDVz294SccyoWi5o/f75aWlq0Z8+esdtKpZL27dunZcuWXe6vAQBMQ6YzoUceeUSdnZ1qb2/X4OCgdu/erb179+rVV19VKpXSxo0btWXLFi1YsEALFizQli1bVFdXp/vvv79a8wcATGGmJvSb3/xG3/ve93TixAk1NTXppptu0quvvqqVK1dKkh5++GGNjIxo3bp1On36tG699Va9/vrrKhQK5omV4owSz6gSU7xPbIzjsJQbI4FSKf/6bNYWl5KuYmyPNVrHwrJPJCmf8d/OfNaYl2IwEuVM9YOGuBRJGs34x/ykKrZX2XNnDdFH/aahle0v+hdXbMdsulhjqLbFJMU5W8xPHBkeKNLWxyD/Y8WN2saO8/735Yohrisx1JqO1p/+9KcXvT2VSqmrq0tdXV2WYQEAVymy4wAAwdCEAADB0IQAAMHQhAAAwdCEAADB0IQAAMHQhAAAwdCEAADB0IQAAMGYU7SrzbnPIiriYf+4j2R41L92xBYNkjLE/LiULRYmif2jeGKVTGOnM7aYH4vJFNsjQ/xNJVO92J64bDuukmFbvIrluI2Ltrt1XDLsw0rZNHYlNsT2xLZ9WDEsZ8W4PnHJGNtT9F9Pa3JYIv/1sdRKUpLzf5xIpfzXPhn5bN3PPZ5fdFznU3UFffzxx/xhOwCYBnp6ejR37tyL1ky6JpQkiT755BMVCoVxfwxvYGBA7e3t6unpUWNjY8AZVhfbOX1cDdsosZ3TzURsp3NOg4ODamtrUzp98VdOJt3Lcel0+qKds7GxcVofAOewndPH1bCNEts53VzudjY1NXnV8cEEAEAwNCEAQDBTpglFUaTHHntMURSFnkpVsZ3Tx9WwjRLbOd1c6e2cdB9MAABcPabMmRAAYPqhCQEAgqEJAQCCoQkBAIKZMk3o6aef1vz581VTU6Obb75Z//Ef/xF6ShOqq6tLqVRq3KWlpSX0tC7L/v37tWrVKrW1tSmVSunFF18cd7tzTl1dXWpra1Ntba2WL1+uo0ePhpnsZfiy7Vy7du15a3vbbbeFmewl6u7u1i233KJCoaA5c+bo3nvv1fvvvz+uZjqsp892Tof13L59u2666aaxL6QuXbpUP//5z8duv5JrOSWa0HPPPaeNGzfq0Ucf1dtvv61vfvOb6uzs1EcffRR6ahPqhhtu0IkTJ8YuR44cCT2lyzI0NKTFixdr27ZtF7z9iSee0NatW7Vt2zYdOnRILS0tWrlypQYHB6/wTC/Pl22nJN19993j1vaVV165gjO8fPv27dP69et18OBB7dmzR5VKRR0dHRoaGhqrmQ7r6bOd0tRfz7lz5+rxxx/X4cOHdfjwYa1YsUL33HPPWKO5omvppoDf//3fdw8++OC46772ta+5v/qrvwo0o4n32GOPucWLF4eeRtVIci+88MLY/5MkcS0tLe7xxx8fu250dNQ1NTW5v//7vw8ww4nx+e10zrk1a9a4e+65J8h8qqWvr89Jcvv27XPOTd/1/Px2Ojc919M552bOnOn+6Z/+6Yqv5aQ/EyqVSnrrrbfU0dEx7vqOjg4dOHAg0Kyq49ixY2pra9P8+fP1ne98Rx988EHoKVXN8ePH1dvbO25doyjSnXfeOe3WVZL27t2rOXPmaOHChXrggQfU19cXekqXpb+/X5I0a9YsSdN3PT+/nedMp/WM41i7d+/W0NCQli5desXXctI3oZMnTyqOYzU3N4+7vrm5Wb29vYFmNfFuvfVWPfPMM3rttdf0k5/8RL29vVq2bJlOnToVempVcW7tpvu6SlJnZ6eeffZZvfHGG3ryySd16NAhrVixQsWi4W/tTCLOOW3atEm33367Fi1aJGl6rueFtlOaPut55MgRNTQ0KIoiPfjgg3rhhRf0jW9844qv5aRL0f4i//PPOkifHSCfv24q6+zsHPv3jTfeqKVLl+p3f/d3tXPnTm3atCngzKpruq+rJN13331j/160aJGWLFmiefPm6eWXX9bq1asDzuzSbNiwQe+++65+8YtfnHfbdFrPL9rO6bKe119/vd555x2dOXNG//qv/6o1a9Zo3759Y7dfqbWc9GdCs2fPViaTOa8D9/X1ndepp5P6+nrdeOONOnbsWOipVMW5T/5dbesqSa2trZo3b96UXNuHHnpIL730kt58881xf3Jluq3nF23nhUzV9czn8/rqV7+qJUuWqLu7W4sXL9aPf/zjK76Wk74J5fN53XzzzdqzZ8+46/fs2aNly5YFmlX1FYtF/fKXv1Rra2voqVTF/Pnz1dLSMm5dS6WS9u3bN63XVZJOnTqlnp6eKbW2zjlt2LBBzz//vN544w3Nnz9/3O3TZT2/bDsvZCqu54U451QsFq/8Wk74Rx2qYPfu3S6Xy7mf/vSn7r333nMbN2509fX17sMPPww9tQnzwx/+0O3du9d98MEH7uDBg+5P/uRPXKFQmNLbODg46N5++2339ttvO0lu69at7u2333a//vWvnXPOPf74466pqck9//zz7siRI+673/2ua21tdQMDA4FnbnOx7RwcHHQ//OEP3YEDB9zx48fdm2++6ZYuXequvfbaKbWdP/jBD1xTU5Pbu3evO3HixNhleHh4rGY6rOeXbed0Wc/Nmze7/fv3u+PHj7t3333XPfLIIy6dTrvXX3/dOXdl13JKNCHnnPu7v/s7N2/ePJfP593v/d7vjfvI5HRw3333udbWVpfL5VxbW5tbvXq1O3r0aOhpXZY333zTSTrvsmbNGufcZx/rfeyxx1xLS4uLosjdcccd7siRI2EnfQkutp3Dw8Ouo6PDXXPNNS6Xy7nrrrvOrVmzxn300Uehp21yoe2T5Hbs2DFWMx3W88u2c7qs55/92Z+NPZ5ec8017g//8A/HGpBzV3Yt+VMOAIBgJv17QgCA6YsmBAAIhiYEAAiGJgQACIYmBAAIhiYEAAiGJgQACIYmBAAIhiYEAAiGJgQACIYmBAAIhiYEAAjm/wFgKbJZjtS86QAAAABJRU5ErkJggg==\n", 261 | "text/plain": [ 262 | "
" 263 | ] 264 | }, 265 | "metadata": {}, 266 | "output_type": "display_data" 267 | } 268 | ], 269 | "source": [ 270 | "plt.imshow(rec)" 271 | ] 272 | }, 273 | { 274 | "cell_type": "markdown", 275 | "id": "1d5c1e65", 276 | "metadata": {}, 277 | "source": [ 278 | "Or we can plot it with the colourmap we recovered:" 279 | ] 280 | }, 281 | { 282 | "cell_type": "code", 283 | "execution_count": 7, 284 | "id": "b41bcd21", 285 | "metadata": {}, 286 | "outputs": [ 287 | { 288 | "data": { 289 | "text/plain": [ 290 | "" 291 | ] 292 | }, 293 | "execution_count": 7, 294 | "metadata": {}, 295 | "output_type": "execute_result" 296 | }, 297 | { 298 | "data": { 299 | "image/png": "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jVOD5ivdl8Ec8I1vfCO4/fbbp1134YUXBn/xF3/haUUz7/777w+WL1/uexkFIyl49tlnp77O5/NBOp0OHnjgganrxsfHg1QqFfzt3/6thxXOjM9vZxAEwbp164Ibb7zRy3oKpaenJ5AU7NixIwiC+bs/P7+dQTA/92cQBEFFRUXwD//wD1/5vpz1Z0LZbFZvvPGGWlpapl3f0tKiXbt2eVpVYRw8eFD19fVqbm7W97//fb333nu+l1QwHR0d6u7unrZf4/G4rr766nm3XyVp+/btqqmp0dKlS3Xbbbepp6fH95LOyMDAgCSpsrJS0vzdn5/fzuPm0/7M5XJ66qmnNDIyolWrVn3l+3LWD6GjR48ql8uptrZ22vW1tbXq7u72tKqZt3LlSj3++ON6+eWX9fOf/1zd3d1avXq1ent7fS+tII7vu/m+XyWptbVVTzzxhF599VU99NBD2rNnj6699lplMrbPfpktgiDQhg0bdOWVV2rZsmWS5uf+PNl2SvNnf+7fv19lZWWKx+O6/fbb9eyzz+qiiy76yvflrEvR/iKf/VgH6dMD5PPXzWWtra1T/77kkku0atUqnXfeeXrssce0YcMGjysrrPm+XyXp5ptvnvr3smXLtGLFCjU1NemFF17Q2rVrPa7s9Nx1111666239Prrr59w23zan1+0nfNlf15wwQXat2+f+vv79S//8i9at26dduzYMXX7V7UvZ/2ZUFVVlSKRyAkTuKen54RJPZ+Ulpbqkksu0cGDB30vpSCOv/LvbNuvklRXV6empqY5uW/vvvtuPf/883rttdemfeTKfNufX7SdJzNX92csFtP555+vFStWqL29XcuXL9fPfvazr3xfzvohFIvFdNlll2nbtm3Trt+2bZtWr17taVWFl8lk9M4776iurs73UgqiublZ6XR62n7NZrPasWPHvN6vktTb26vOzs45tW+DINBdd92lZ555Rq+++qqam5un3T5f9ueXbefJzMX9eTJBECiTyXz1+3LGX+pQAE899VQQjUaDX/ziF8Hbb78drF+/PigtLQ3ef/9930ubMT/5yU+C7du3B++9916we/fu4I/+6I+CZDI5p7dxaGgoePPNN4M333wzkBRs3rw5ePPNN4MPPvggCIIgeOCBB4JUKhU888wzwf79+4Mf/OAHQV1dXTA4OOh55Tan2s6hoaHgJz/5SbBr166go6MjeO2114JVq1YFixYtmlPb+eMf/zhIpVLB9u3bg66urqnL6OjoVM182J9ftp3zZX9u3Lgx2LlzZ9DR0RG89dZbwb333huEw+HglVdeCYLgq92Xc2IIBUEQ/M3f/E3Q1NQUxGKx4Otf//q0l0zOBzfffHNQV1cXRKPRoL6+Pli7dm1w4MAB38s6I6+99log6YTLunXrgiD49GW9999/f5BOp4N4PB5cddVVwf79+/0u+jScajtHR0eDlpaWoLq6OohGo8HixYuDdevWBR9++KHvZZucbPskBY8++uhUzXzYn1+2nfNlf/7Jn/zJ1PNpdXV18O1vf3tqAAXBV7sv+SgHAIA3s/5vQgCA+YshBADwhiEEAPCGIQQA8IYhBADwhiEEAPCGIQQA8IYhBADwhiEEAPCGIQQA8IYhBADwhiEEAPDm/wGGVW3cm/DYbAAAAABJRU5ErkJggg==\n", 300 | "text/plain": [ 301 | "
" 302 | ] 303 | }, 304 | "metadata": {}, 305 | "output_type": "display_data" 306 | } 307 | ], 308 | "source": [ 309 | "plt.imshow(rec, cmap=cmap)" 310 | ] 311 | }, 312 | { 313 | "cell_type": "markdown", 314 | "id": "2a8c5659", 315 | "metadata": {}, 316 | "source": [ 317 | "Or, in this case, we can use the colourmap we know was applied originally to create the image:" 318 | ] 319 | }, 320 | { 321 | "cell_type": "code", 322 | "execution_count": 8, 323 | "id": "03be75fc", 324 | "metadata": {}, 325 | "outputs": [ 326 | { 327 | "data": { 328 | "text/plain": [ 329 | "" 330 | ] 331 | }, 332 | "execution_count": 8, 333 | "metadata": {}, 334 | "output_type": "execute_result" 335 | }, 336 | { 337 | "data": { 338 | "image/png": "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qfesYfdjpXrAGAlkiBAaTbg/Nkcdn7slzoQAAB4xhAAA3jCEAADeMIQAAN4whAAA3jCEAADeMIQAAN4whAAA3jCEAADeMIQAAN4whAAA3kza7DhFI59cJlgwastWahjuda6tHcibes/KuufBpQy1klRii7EzySeL97NLvsS2z0PDUpJ9tnXHRt3XclFo2/eqsWWZ9QdJ59qPKhpMvXNyz46becSYS9czy7l2pMy27hN17o/ND5rrTL2P175jqh9NuT8+e2IfmnrXJY871w7Heky9S/LuAYnlaffagZh7LWdCAABvTENo06ZNuuKKK1RZWanKykotWbJEP/vZz8ZuD8NQbW1tqq+vVzKZ1LJly7R3794JXzQAYHowDaGGhgY9+OCD2r17t3bv3q3ly5frpptuGhs0Dz30kDZs2KCNGzdq165dymQyWrFihbLZbFEWDwCY2kxD6MYbb9Sf/umfav78+Zo/f77+9m//VhUVFdq5c6fCMNQjjzyi+++/XytXrtSCBQv0+OOPa2BgQE899VSx1g8AmMLO+m9C+XxeTz/9tPr7+7VkyRIdOHBAnZ2damlpGasJgkDXXXedduzY8al9crmcent7x10AAOcH8xDas2ePKioqFASB7rjjDj333HO69NJL1dnZKUmqra0dV19bWzt225m0t7crnU6PXRobG61LAgBMUeYhdNFFF+mtt97Szp079YMf/ECrV6/WO+/8/uWMkcj4l7WGYXjadX9o3bp16unpGbt0dHRYlwQAmKLM7xNKJBK68MILJUmLFi3Srl279OMf/1h/9Vd/JUnq7OxUXd3vX5Pf1dV12tnRHwqCQEEQWJcBAJgGzvl9QmEYKpfLqbm5WZlMRps3bx67bXh4WNu2bdPSpUvP9dsAAKYh05nQfffdp9bWVjU2Niqbzerpp5/W1q1b9fLLLysSiWjNmjVav3695s2bp3nz5mn9+vUqKyvTrbfeWqz1AwCmMNMQOnr0qL73ve/pyJEjSqfTuuKKK/Tyyy9rxYoVkqR7771Xg4ODuvPOO3XixAktXrxYr776qlKplHlhhbKYCmVuyxstcY/iGYjbYmGicu8dGGJeJKnEUJ8YtPWOGeOJLKzROhZR97SPT+qH3bez9IStebmhtiJl+812MGyL+SnNvO1ce7LS9nh7tabJuTY8YIvWiR864V47ZMuaCno+/df8p3OPJpKk7qTtvY2Hgy7n2tJg2NS7JOp+rCTjtt5VQb9zbWXS/fEzaHj+MT1yfvrTn37m7ZFIRG1tbWpra7O0BQCcp8iOAwB4wxACAHjDEAIAeMMQAgB4wxACAHjDEAIAeMMQAgB4wxACAHjDEAIAeGNO0S62MPwk7qF3wD2qwvLp4f2ltuiWnCGipi9ri8qJG+pj/cbeQ+dJbI8hHiQ2ZGxuMBK13d/ZrC22p7/Mfe3DgS3+ppB1j24ZHbB96ORozlA/bIucyQ8l3WuN67bcJ5I0Ggw51w4bY3uGoiPOtYPhqKl3f29xnmf7+j7pe+r5/LNEQpeqL9CHH37IB9sBwDTQ0dGhhobPzhucdEOoUCjo8OHDSqVS4z4Mr7e3V42Njero6FBlZaXHFRYX2zl9nA/bKLGd081EbGcYhspms6qvr1c0+tl/9Zl0v46LRqOfOTkrKyun9QFwCts5fZwP2yixndPNuW5nOp12quOFCQAAbxhCAABvpswQCoJADzzwgIIg8L2UomI7p4/zYRsltnO6+aK3c9K9MAEAcP6YMmdCAIDphyEEAPCGIQQA8IYhBADwZsoMoUcffVTNzc0qLS3VlVdeqZ///Oe+lzSh2traFIlExl0ymYzvZZ2T7du368Ybb1R9fb0ikYief/75cbeHYai2tjbV19crmUxq2bJl2rt3r5/FnoPP287bbrvttH179dVX+1nsWWpvb9dVV12lVCqlmpoa3XzzzXrvvffG1UyH/emyndNhf27atElXXHHF2BtSlyxZop/97Gdjt3+R+3JKDKFnnnlGa9as0f33368333xTX//619Xa2qpDhw75XtqEuuyyy3TkyJGxy549e3wv6Zz09/dr4cKF2rhx4xlvf+ihh7RhwwZt3LhRu3btUiaT0YoVK5S1JCVOAp+3nZJ0ww03jNu3L7300he4wnO3bds23XXXXdq5c6c2b96s0dFRtbS0qL//90Gf02F/umynNPX3Z0NDgx588EHt3r1bu3fv1vLly3XTTTeNDZovdF+GU8BXv/rV8I477hh33cUXXxz+9V//tacVTbwHHnggXLhwoe9lFI2k8Lnnnhv7ulAohJlMJnzwwQfHrhsaGgrT6XT4D//wDx5WODH+eDvDMAxXr14d3nTTTV7WUyxdXV2hpHDbtm1hGE7f/fnH2xmG03N/hmEYzpw5M/znf/7nL3xfTvozoeHhYb3xxhtqaWkZd31LS4t27NjhaVXFsW/fPtXX16u5uVnf+c53tH//ft9LKpoDBw6os7Nz3H4NgkDXXXfdtNuvkrR161bV1NRo/vz5uv3229XV1eV7Seekp6dHklRVVSVp+u7PP97OU6bT/szn83r66afV39+vJUuWfOH7ctIPoWPHjimfz6u2tnbc9bW1ters7PS0qom3ePFiPfHEE3rllVf0k5/8RJ2dnVq6dKm6u7t9L60oTu276b5fJam1tVVPPvmktmzZoocffli7du3S8uXLlcvZPvdnsgjDUGvXrtU111yjBQsWSJqe+/NM2ylNn/25Z88eVVRUKAgC3XHHHXruued06aWXfuH7ctKlaH+aP/xYB+mTA+SPr5vKWltbx/59+eWXa8mSJbrgggv0+OOPa+3atR5XVlzTfb9K0qpVq8b+vWDBAi1atEhNTU168cUXtXLlSo8rOzt333233n77bb3++uun3Tad9uenbed02Z8XXXSR3nrrLZ08eVL//u//rtWrV2vbtm1jt39R+3LSnwlVV1crFoudNoG7urpOm9TTSXl5uS6//HLt27fP91KK4tQr/863/SpJdXV1ampqmpL79p577tELL7yg1157bdxHrky3/flp23kmU3V/JhIJXXjhhVq0aJHa29u1cOFC/fjHP/7C9+WkH0KJREJXXnmlNm/ePO76zZs3a+nSpZ5WVXy5XE7vvvuu6urqfC+lKJqbm5XJZMbt1+HhYW3btm1a71dJ6u7uVkdHx5Tat2EY6u6779azzz6rLVu2qLm5edzt02V/ft52nslU3J9nEoahcrncF78vJ/ylDkXw9NNPh/F4PPzpT38avvPOO+GaNWvC8vLy8ODBg76XNmF++MMfhlu3bg33798f7ty5M/yzP/uzMJVKTeltzGaz4Ztvvhm++eaboaRww4YN4Ztvvhn+7ne/C8MwDB988MEwnU6Hzz77bLhnz57wu9/9blhXVxf29vZ6XrnNZ21nNpsNf/jDH4Y7duwIDxw4EL722mvhkiVLwjlz5kyp7fzBD34QptPpcOvWreGRI0fGLgMDA2M102F/ft52Tpf9uW7dunD79u3hgQMHwrfffju87777wmg0Gr766qthGH6x+3JKDKEwDMO///u/D5uamsJEIhF+5StfGfeSyelg1apVYV1dXRiPx8P6+vpw5cqV4d69e30v65y89tproaTTLqtXrw7D8JOX9T7wwANhJpMJgyAIr7322nDPnj1+F30WPms7BwYGwpaWlnD27NlhPB4P586dG65evTo8dOiQ72WbnGn7JIWPPfbYWM102J+ft53TZX/++Z//+djz6ezZs8NvfOMbYwMoDL/YfclHOQAAvJn0fxMCAExfDCEAgDcMIQCANwwhAIA3DCEAgDcMIQCANwwhAIA3DCEAgDcMIQCANwwhAIA3DCEAgDcMIQCAN/8Puc5MwgHNBZwAAAAASUVORK5CYII=\n", 339 | "text/plain": [ 340 | "
" 341 | ] 342 | }, 343 | "metadata": {}, 344 | "output_type": "display_data" 345 | } 346 | ], 347 | "source": [ 348 | "plt.imshow(rec, cmap='turbo')" 349 | ] 350 | }, 351 | { 352 | "cell_type": "markdown", 353 | "id": "7eed52a1", 354 | "metadata": {}, 355 | "source": [ 356 | "These results look very close.\n", 357 | "\n", 358 | "We can compute the difference:" 359 | ] 360 | }, 361 | { 362 | "cell_type": "code", 363 | "execution_count": 9, 364 | "id": "1c88972d", 365 | "metadata": {}, 366 | "outputs": [ 367 | { 368 | "data": { 369 | "text/plain": [ 370 | "" 371 | ] 372 | }, 373 | "execution_count": 9, 374 | "metadata": {}, 375 | "output_type": "execute_result" 376 | }, 377 | { 378 | "data": { 379 | "image/png": "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\n", 380 | "text/plain": [ 381 | "
" 382 | ] 383 | }, 384 | "metadata": {}, 385 | "output_type": "display_data" 386 | } 387 | ], 388 | "source": [ 389 | "plt.imshow(data - rec, cmap='RdBu', vmin=-0.1, vmax=0.1)\n", 390 | "plt.colorbar()" 391 | ] 392 | }, 393 | { 394 | "cell_type": "markdown", 395 | "id": "73ee9210", 396 | "metadata": {}, 397 | "source": [ 398 | "The absolute error seems to go up to a little above 8% in some areas. Each colourmap has its own error profile, but this order of error magnitude is typical." 399 | ] 400 | }, 401 | { 402 | "cell_type": "markdown", 403 | "id": "98dd1b16", 404 | "metadata": {}, 405 | "source": [ 406 | "## All in one\n", 407 | "\n", 408 | "Sometimes we can actually skip the colormap guessing step and let `unmap.unmap` try to do it. Let's try it:" 409 | ] 410 | }, 411 | { 412 | "cell_type": "code", 413 | "execution_count": 10, 414 | "id": "8726a96a", 415 | "metadata": {}, 416 | "outputs": [ 417 | { 418 | "data": { 419 | "text/plain": [ 420 | "" 421 | ] 422 | }, 423 | "execution_count": 10, 424 | "metadata": {}, 425 | "output_type": "execute_result" 426 | }, 427 | { 428 | "data": { 429 | "image/png": "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a2en/PdhTv6xVFwJAQCCoQkBAIKhCQEAgqEJAQCCoQkBAIKhCQEAgqEJAQCCoQkBAIKhCQEAgqEJAQCCoQkBAIKZstlxybyU9Iz6So3657ulzxlC1SQlx/3rI2PMnCU7Lk7bcs8sGWzGKCsVU7bXLrHhKCsZtzM/y78+d51paOUa/bPM6luGTWMv+NJvTfUL6wa8a7+a+Y1p7IaEf45dKqrc69ac888bk6SC/J9whcpFQJoVnO20W5D/E3TcGYMdDTJR0bvWGWq5EgIABEMTAgAEQxMCAARDEwIABEMTAgAEQxMCAARDEwIABEMTAgAEQxMCAARDEwIABDNlY3ui0vmbj2TeP5PDEsMjSenBvHdtIu8f8yJJcdo/jqOUtS1VMes/drHG9lqkWG2M1qnzr883mIZW/jr/tS/O8Y8SkaS5LYPetQtm22J4FhhieCTp69Ufe9f+TuqUaex05P+c+CS2PX9+VRzzrh2O06axh+J671pL9I0kjcQZU/1wKetdO1iqMY5d7V2bs2RkSSoYMrssY+fz/hFMXAkBAIIxNaHu7m7ddtttqq+vV2Njo+677z699957E2qcc+rq6lJra6uy2axWrFihY8eOTeqkAQAzg6kJ9fb2auPGjTp06JB6enpULBbV0dGhkZGRcs0TTzyh7du3a8eOHTp8+LCam5u1atUqDQ/bUoYBADOf6QeIr7766oT/79q1S42NjXrzzTd15513yjmnp556So8++qjWrFkjSdq9e7eampq0Z88eff/735+8mQMApr0r+p3Q4OD5X9zOmTNHknTixAn19/ero6OjXJPJZHTXXXfp4MGDFx0jl8tpaGhowg0AcG247CbknNOWLVt0xx13aNGiRZKk/v5+SVJTU9OE2qampvJjn9Xd3a2Ghobybf78+Zc7JQDANHPZTWjTpk1655139C//8i8XPBZFE9+S65y74L5Pbd26VYODg+VbX1/f5U4JADDNXNbnhB566CG99NJLOnDggObNm1e+v7m5WdL5K6KWlpby/QMDAxdcHX0qk8kok7G9Jx8AMDOYroScc9q0aZOef/557du3T+3t7RMeb29vV3Nzs3p6esr35fN59fb2avny5ZMzYwDAjGG6Etq4caP27Nmjf/u3f1N9fX359zwNDQ3KZrOKokibN2/Wtm3btGDBAi1YsEDbtm1TTU2N7r///ops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c9q0aZOef/557du3T+3t7RMenynr+UXbeTHTcT0vxjmnXC539ddy0t/qUAF79+51qVTK/exnP3Pvvvuu27x5s6utrXUffvhh6KlNmh/+8Idu//797oMPPnCHDh1yf/zHf+zq6+un9TYODw+7t956y7311ltOktu+fbt766233K9+9SvnnHOPP/64a2hocM8//7w7evSo++53v+taWlrc0NBQ4JnbXGo7h4eH3Q9/+EN38OBBd+LECffGG2+4ZcuWuS9/+cvTajt/8IMfuIaGBrd//3538uTJ8m10dLRcMxPW84u2c6as59atW92BAwfciRMn3DvvvOMeeeQRl0gk3Ouvv+6cu7prOS2akHPO/d3f/Z1ra2tz6XTa/e7v/u6Et0zOBGvXrnUtLS0ulUq51tZWt2bNGnfs2LHQ07oib7zxhpN0wW3dunXOufNv633sscdcc3Ozy2Qy7s4773RHjx4NO+nLcKntHB0ddR0dHe766693qVTK3XDDDW7dunXuo48+Cj1tk4ttnyS3a9eucs1MWM8v2s6Zsp5/9md/Vj6fXn/99e4P/uAPyg3Iuau7lvwpBwBAMFP+d0IAgJmLJgQACIYmBAAIhiYEAAiGJgQACIYmBAAIhiYEAAiGJgQACIYmBAAIhiYEAAiGJgQACIYmBAAI5v8BuAFoJ4vDL50AAAAASUVORK5CYII=\n", 430 | "text/plain": [ 431 | "
" 432 | ] 433 | }, 434 | "metadata": {}, 435 | "output_type": "display_data" 436 | } 437 | ], 438 | "source": [ 439 | "rec = unmap.unmap(imarray)\n", 440 | "\n", 441 | "plt.imshow(rec)" 442 | ] 443 | }, 444 | { 445 | "cell_type": "markdown", 446 | "id": "301616af", 447 | "metadata": {}, 448 | "source": [ 449 | "Nice. If it works." 450 | ] 451 | } 452 | ], 453 | "metadata": { 454 | "kernelspec": { 455 | "display_name": "unmap", 456 | "language": "python", 457 | "name": "unmap" 458 | }, 459 | "language_info": { 460 | "codemirror_mode": { 461 | "name": "ipython", 462 | "version": 3 463 | }, 464 | "file_extension": ".py", 465 | "mimetype": "text/x-python", 466 | "name": "python", 467 | "nbconvert_exporter": "python", 468 | "pygments_lexer": "ipython3", 469 | "version": "3.10.8" 470 | } 471 | }, 472 | "nbformat": 4, 473 | "nbformat_minor": 5 474 | } 475 | -------------------------------------------------------------------------------- /docs/process_html.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import glob 3 | import re 4 | 5 | 6 | def simplify_credits(html): 7 | """ 8 | Replace the credit part of the HTML footer. Return the new text. 9 | """ 10 | s = r'@pradyunsg\'s' 11 | pattern = re.compile(s) 12 | html = pattern.sub(r'', html) 13 | 14 | s = r'Copyright © 2022, Agile Scientific' 15 | pattern = re.compile(s) 16 | new_s = '© 2022, Agile Scientific | CC BY' 17 | html = pattern.sub(new_s, html) 18 | 19 | return html 20 | 21 | 22 | def main(path): 23 | """ 24 | Process the HTML files in path, save in place (side-effect). 25 | """ 26 | fnames = glob.glob(path.strip('/') + '/*.html') 27 | for fname in fnames: 28 | with open(fname, 'r+') as f: 29 | html = f.read() 30 | 31 | new_html = simplify_credits(html) 32 | 33 | f.seek(0) 34 | f.write(new_html) 35 | f.truncate() 36 | return 37 | 38 | 39 | if __name__ == '__main__': 40 | _ = main(sys.argv[1]) 41 | -------------------------------------------------------------------------------- /docs/process_ipynb.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | import glob 4 | import json 5 | import pathlib 6 | import shutil 7 | 8 | 9 | def change_kernel(notebook): 10 | """ 11 | Vanillafy the kernelspec. 12 | """ 13 | new_kernelspec = { 14 | "display_name": "Python 3 (ipykernel)", 15 | "language": "python", 16 | "name": "python3", 17 | } 18 | notebook['metadata']['kernelspec'].update(new_kernelspec) 19 | return notebook 20 | 21 | 22 | def main(path): 23 | """ 24 | Process the IPYNB files in path, save in place (side-effect). 25 | """ 26 | fnames = glob.glob(path.strip('/') + '/[!_]*.ipynb') # Not files with underscore. 27 | outpath = pathlib.Path('userguide') 28 | if outpath.exists(): 29 | shutil.rmtree(outpath) 30 | outpath.mkdir(exist_ok=True) 31 | 32 | for fname in fnames: 33 | with open(fname, encoding='utf-8') as f: 34 | notebook = json.loads(f.read()) 35 | 36 | new_nb = change_kernel(notebook) 37 | filepart = pathlib.Path(fname).name 38 | 39 | with open(outpath / filepart, 'w') as f: 40 | _ = f.write(json.dumps(new_nb)) 41 | 42 | return 43 | 44 | 45 | if __name__ == '__main__': 46 | print(sys.argv[1]) 47 | _ = main(sys.argv[1]) -------------------------------------------------------------------------------- /docs/readme.rst: -------------------------------------------------------------------------------- 1 | 2 | .. include:: ../README.md 3 | :parser: myst_parser.sphinx_ 4 | -------------------------------------------------------------------------------- /docs/unmap.rst: -------------------------------------------------------------------------------- 1 | unmap package 2 | ============= 3 | 4 | Submodules 5 | ---------- 6 | 7 | unmap.unmap module 8 | ------------------ 9 | 10 | .. automodule:: unmap.unmap 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | Module contents 16 | --------------- 17 | 18 | .. automodule:: unmap 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=65.0", "setuptools-scm>=7.0"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "unmap" 7 | authors = [{ name="Matt Hall", email="kwinkunks@gmail.com" },] 8 | description = "Unmap data from pseudocolor images." 9 | dynamic = ["version"] 10 | requires-python = ">=3.8" 11 | license = {file = "LICENSE"} 12 | keywords = ["plotting", "reproducibility", "visualization", "color"] 13 | readme = "README.md" 14 | classifiers = [ 15 | "Intended Audience :: Science/Research", 16 | "Development Status :: 3 - Alpha", 17 | "Natural Language :: English", 18 | "Programming Language :: Python :: 3.8", 19 | "Programming Language :: Python :: 3.9", 20 | "Programming Language :: Python :: 3.10", 21 | "License :: OSI Approved :: Apache Software License", 22 | "Operating System :: OS Independent", 23 | ] 24 | dependencies = [ 25 | "numpy", 26 | "scipy", 27 | "fsspec", 28 | "aiohttp", 29 | "pillow", 30 | "xarray", 31 | "networkx", 32 | "matplotlib", 33 | "scikit-image", 34 | ] 35 | 36 | [project.optional-dependencies] 37 | test = [ 38 | "pytest", 39 | "coverage[toml]", 40 | "pytest-cov", 41 | ] 42 | docs = [ 43 | "sphinx", 44 | "sphinxcontrib-apidoc", 45 | "furo", 46 | "myst_nb", 47 | "jupyter", 48 | "gio", 49 | ] 50 | dev = [ 51 | "build", 52 | "pytest", 53 | "coverage[toml]", 54 | "pytest-cov", 55 | "sphinx", 56 | "sphinxcontrib-apidoc", 57 | "furo", 58 | "myst_nb", 59 | "jupyter", 60 | "gio", 61 | ] 62 | 63 | [project.urls] 64 | "documentation" = "https://scienxlab.github.io/unmap" 65 | "repository" = "https://github.com/scienxlab/unmap" 66 | 67 | [tool.setuptools] 68 | packages = ["unmap"] 69 | 70 | [tool.setuptools_scm] 71 | write_to = "unmap/_version.py" 72 | 73 | [tool.pytest.ini_options] 74 | addopts = "--ignore=docs --cov=unmap" 75 | 76 | [tool.coverage.run] 77 | omit = [ 78 | "unmap/__init__.py", 79 | "unmap/_version.py", 80 | ] 81 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scienxlab/unmap/30269199b54be09072f0950f0d6ccbd9843a739b/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_unmap.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from matplotlib import colormaps as cm 3 | import scipy.signal 4 | 5 | import unmap 6 | 7 | def kernel(sizex, sizey): 8 | x, y = np.mgrid[-sizex:sizex+1, -sizey:sizey+1] 9 | g = np.exp(-0.333*(x**2/float(sizex)+y**2/float(sizey))) 10 | return g / g.sum() 11 | 12 | def make_map(nx=64, ny=64, kernel_size=None, seed=None): 13 | rng = np.random.RandomState(seed=seed) 14 | if kernel_size is None: 15 | kx, ky = (7, 7) 16 | else: 17 | kx, ky = kernel_size 18 | f = kernel(kx, ky) 19 | z = rng.rand(nx+2*kx, ny+2*ky) 20 | z = scipy.signal.convolve(z, f, mode='valid') 21 | z = (z - z.min())/(z.max() - z.min()) 22 | 23 | return z 24 | 25 | def get_cbar(cmap, n=32, pixels=5): 26 | arr = cm.get_cmap(cmap)(np.arange(0, n, 1)/(n-1))[..., :3] 27 | return np.tile(arr, (pixels, 1)).reshape(pixels, n, 3) 28 | 29 | def make_image_and_cbar(cmap, seed=None): 30 | data = make_map() 31 | c = cm.get_cmap(cmap) 32 | return c(data)[..., :3], get_cbar(cmap, 256, pixels=5) 33 | 34 | 35 | def test_unmap(): 36 | """Test the basics. 37 | """ 38 | img, cbar = make_image_and_cbar('jet', seed=42) 39 | 40 | # Add some white pixels as 'background'; will become NaNs. 41 | img[:3, :3, :] = 1 42 | 43 | # Using a cbar image. 44 | data = unmap.unmap(img, cmap=cbar, vrange=(100, 200)) 45 | assert data.shape == (64, 64) 46 | assert np.nanmax(data) == 200 47 | assert np.nanmean(data) - 161.4341107536765 < 1e-6 48 | assert np.any(np.isnan(data)) 49 | 50 | # Using a matplotlib cmap. 51 | data = unmap.unmap(img, cmap='jet', vrange=(200, 300)) 52 | assert np.nanmean(data) - 261.4341107536765 < 1e-6 53 | 54 | 55 | def is_greyscale(): 56 | """ 57 | Test the is_greyscale function. 58 | """ 59 | # 8-bit colour. 60 | assert not unmap.is_greyscale(rgb = (np.random.random((10, 10, 3)) * 256).astype(int)) 61 | 62 | # RGBA colour. 63 | assert not unmap.is_greyscale(np.random.random((10, 10, 4))) 64 | 65 | # Simple 2D array. 66 | assert unmap.is_greyscale(np.random.random((10, 10))) 67 | 68 | # One-channel image. 69 | gs = np.random.random((10, 10, 1)) 70 | assert unmap.is_greyscale(gs) 71 | 72 | # RBG greyscale. 73 | assert unmap.is_greyscale(np.dstack([gs, gs, gs])) 74 | 75 | # RGBA greyscale with random A channel. 76 | assert unmap.is_greyscale(np.dstack([gs, gs, gs, np.random.random((10, 10, 1))])) 77 | -------------------------------------------------------------------------------- /unmap/__init__.py: -------------------------------------------------------------------------------- 1 | from .unmap import * 2 | from .unweave import * 3 | 4 | from pkg_resources import get_distribution, DistributionNotFound 5 | 6 | try: 7 | VERSION = get_distribution(__name__).version 8 | except DistributionNotFound: 9 | try: 10 | from ._version import version as VERSION 11 | except ImportError: 12 | raise ImportError( 13 | "Failed to find (autogenerated) _version.py. " 14 | "This might be because you are installing from GitHub's tarballs, " 15 | "use the PyPI ones." 16 | ) 17 | __version__ = VERSION 18 | -------------------------------------------------------------------------------- /unmap/unmap.py: -------------------------------------------------------------------------------- 1 | """ 2 | unmap.py 3 | 4 | Functions for recoving data from images with a known colourmap. 5 | 6 | Author: Matt Hall 7 | Copyright: 2022, Matt Hall 8 | Licence: Apache 2.0 9 | """ 10 | import warnings 11 | from collections import Counter 12 | 13 | import numpy as np 14 | from scipy.spatial import cKDTree 15 | from scipy.cluster.vq import kmeans 16 | from matplotlib import colormaps as cm 17 | from matplotlib.colors import ListedColormap, hsv_to_rgb, rgb_to_hsv 18 | from matplotlib.colors import to_rgb, LinearSegmentedColormap 19 | 20 | from .unweave import guess_cmap_from_array 21 | 22 | 23 | def check_arr(arr): 24 | """ 25 | Check the input array, and return it as a NumPy array, and the alpha channel 26 | 27 | Args: 28 | arr: NumPy array of image data. 29 | 30 | Returns: 31 | The RGB image array, and the alpha channel. 32 | """ 33 | if arr.ndim == 2: 34 | arr = arr.reshape(arr.shape + (1,)) 35 | elif arr.ndim != 3: 36 | raise ValueError('Array must be a grayscale or RGB(A) image.') 37 | 38 | h, w, c = arr.shape 39 | alpha = None 40 | if c == 1: 41 | warnings.warn('Grayscale image will be scaled and returned.') 42 | # But we can still scale it. 43 | elif (c == 0) or (c == 2) or (c >= 5): 44 | raise ValueError('Array must be a grayscale or RGB(A) image.') 45 | elif c == 4: 46 | alpha = arr[..., 3] 47 | arr = arr[..., :3] 48 | 49 | return arr, alpha 50 | 51 | 52 | def get_background_rgb(arr, background='w'): 53 | """ 54 | Get the RGB value of the background colour. 55 | 56 | Currently does not deal with alpha channel, if bg is transparent. 57 | 58 | Could also accept a pixel location to get the colour from. 59 | 60 | Args: 61 | arr: NumPy array of image data. 62 | background: The background colour: any matplotlib or CSS colour name, 63 | or 'common' to use the most common colour in the image. 64 | 65 | Returns: 66 | The RGB value of the background colour. 67 | """ 68 | h, w, c = arr.shape 69 | if background == 'common': 70 | n_pixels = min(1000, h * w) 71 | ix = np.random.choice(h * w, size=n_pixels, replace=False) 72 | c = Counter(tuple(rgb) for rgb in arr.reshape(-1, c)[ix]) 73 | bg = c.most_common(1)[0][0] 74 | elif isinstance(background, str): 75 | bg = to_rgb(background) 76 | else: 77 | try: 78 | bg = np.array(background).reshape(-1, 3) 79 | except: 80 | raise ValueError('background must be a str or Nx3 array-like of N RGB-triples in range (0-1).') 81 | return bg 82 | 83 | 84 | def remove_hillshade(img): 85 | """ 86 | Pass in an RGB 3-channel image, scaled 0-1. 87 | 88 | Returns the original image without the hillshading, and an RGBA array 89 | with only the hillshade. 90 | 91 | This will only work if the colourmap only uses full-valued colours. 92 | In other words, the only thing affecting the lightness in the image is 93 | the hillshade. If the colourmap has any dark colours, this may be a 94 | problem. 95 | 96 | Args: 97 | img: NumPy array of image data. 98 | 99 | Returns: 100 | The image without the hillshade, and the hillshade as a separate 101 | RGBA array. 102 | """ 103 | hsv_im = rgb_to_hsv(img) 104 | val = 1 - hsv_im[..., 2] 105 | hillshade = val[..., None] * [0, 0, 0, 1] 106 | hsv_im[..., 2] = 1.0 107 | return hsv_to_rgb(hsv_im), hillshade 108 | 109 | 110 | def crop_out(arr, rect): 111 | """ 112 | Crop pixels out of a larger image. 113 | """ 114 | l, t, r, b = rect 115 | assert (r >= l) and (b >= t), "Right and bottom must be greater than left and top respectively; the origin is at the top left of the image." 116 | 117 | r += 1 if r == l else 0 118 | b += 1 if t == b else 0 119 | pixels = arr[t:b, l:r] 120 | 121 | return pixels 122 | 123 | 124 | def get_cmap(cmap, arr=None, levels=256, quantize=False): 125 | """ 126 | Turn whatever we get as a colourmap into a matplotlib cmap. 127 | 128 | Args: 129 | cmap (str or array-like): The colourmap to use. If a string, it must 130 | be a matplotlib colourmap name. If an array-like, it must be either 131 | a 4-tuple of pixel coordinates, or an Nx3 array of RGB values in 132 | the range (0-1). If you pass pixel coordinates, they must be 133 | ordered like (left, top, right, bottom), giving the coordinates of 134 | the colourmap in the image array `arr` (which you must also pass!). 135 | arr (2d array): The image array, if `cmap` is a 4-tuple of pixel 136 | coordinates. 137 | levels (int): The number of colours you want in the colourmap, or 138 | which you count in the colourmap if `quantize` is True. 139 | quantize (bool): Whether to quantize the colourmap to `levels` colours. 140 | Set this to true if the colourmap array or pixel lcoations you pass 141 | are 'stepped' or contain image noise. 142 | 143 | Returns: 144 | A matplotlib colourmap. 145 | """ 146 | if isinstance(cmap, str): 147 | try: 148 | return cm.get_cmap(cmap) 149 | except ValueError: 150 | raise ValueError("cmap name not recognized by matplotlib") 151 | 152 | elif isinstance(cmap, LinearSegmentedColormap) or isinstance(cmap, ListedColormap): 153 | return cmap 154 | 155 | elif cmap is None: 156 | # Try to guess from array. 157 | return guess_cmap_from_array(arr, source_colors=levels) 158 | 159 | else: 160 | try: 161 | cmap = np.array(cmap) 162 | except TypeError as e: 163 | raise TypeError("cmap must be a str (name of a matplotlib cmap) or an array-like.") 164 | 165 | if (cmap.ndim == 1) and (cmap.size == 4): 166 | cmap = crop_out(arr, cmap) 167 | 168 | if quantize: 169 | cmap, _ = kmeans(cmap, levels) 170 | 171 | if (cmap.ndim == 3) and (cmap.shape[0] > cmap.shape[1]): 172 | # Then it is a vertical rectangle. 173 | cmap = np.swapaxes(cmap[::-1], 0, 1) 174 | 175 | if (cmap.ndim == 3): 176 | cmap = np.mean(cmap, axis=0) 177 | 178 | if (cmap.ndim == 2) and (cmap.shape[1] in [3, 4]): 179 | cmap = LinearSegmentedColormap.from_list('', cmap[:, :3], N=levels) 180 | else: 181 | raise TypeError("If an array-like, cmap must be either a 4-tuple of pixel coordinates " 182 | "in a 1d array-like or a sequence of RGB(A) tuples in a 2d array-like.") 183 | 184 | return cmap 185 | 186 | 187 | def normalize(arr, vrange): 188 | """ 189 | Normalize the array to the given range. 190 | 191 | Args: 192 | arr: NumPy array of image data. 193 | vrange: The range to normalize to, as a tuple (min, max). 194 | 195 | Returns: 196 | The normalized array. 197 | """ 198 | mi, ma = vrange 199 | arr = arr / np.nanmax(arr) 200 | return (ma - mi) * arr + mi 201 | 202 | 203 | def is_greyscale(arr, epsilon=1e-6): 204 | """ 205 | Check if the image is greyscale. 206 | 207 | Args: 208 | arr: NumPy array of image data. 209 | epsilon: The maximum difference between the R, G and B channels 210 | for the image to be considered greyscale. 211 | 212 | Returns: 213 | True if the image is greyscale, False otherwise. 214 | """ 215 | arr = np.squeeze(arr).astype(float) 216 | 217 | if arr.ndim == 2: 218 | return True 219 | 220 | if np.max(arr) > 1: 221 | arr /= 255 222 | 223 | r, g, b, *_ = arr.T 224 | reg = np.all(np.abs(r - g) < epsilon) 225 | geb = np.all(np.abs(g - b) < epsilon) 226 | return reg and geb 227 | 228 | 229 | def unmap(arr, 230 | cmap=None, 231 | crop=None, 232 | vrange=(0, 1), 233 | levels=256, 234 | nan_colours=None, 235 | background='w', 236 | threshold = 0.1, 237 | hillshade=False, 238 | quantize=False, 239 | ): 240 | """ 241 | Unmap data, via a colourmap, from an image. Reverse false-colouring. 242 | 243 | Args: 244 | arr: NumPy array of image data. 245 | cmap: Either another array, or pixel extent of colourbar in image, 246 | or name of mpl colourbar or if None, try to guess it. 247 | crop: If not None, crop the image to the given rectangle. 248 | Given as a tuple (left, top, right, bottom). 249 | vrange: (vmin, vmax) for the colourbar. 250 | levels: Number of colours / value levels desired, default 256. 251 | nan_colours: Colours to turn to NaN? Treat essentially like background. 252 | Combine these args? 253 | background: Give background pixel location so can get this, or can be 254 | 'white'/w or 'black'/k, or RGB, or use the most common 255 | pixel colour with 'common'. 256 | threshold: 0 is exact match only, 1.732 is maximum distance and admits 257 | all points. 258 | hillshade: Then use HSV 259 | 260 | Returns: 261 | A NumPy array of the same shape as the input image, with the colourmap 262 | removed. Essentially a greyscale image representing the data used to 263 | make the false colour image. 264 | """ 265 | if is_greyscale(arr): 266 | return normalize(arr, vrange) 267 | 268 | # In future, maybe we also check for perceptually linear colours, and 269 | # just pass back the lightness channel if so. 270 | 271 | # In future, we may be able to sense the colourmap, either from an ML 272 | # model, or analytically from the image. 273 | 274 | # Check and transform inputs to get codebook. 275 | arr, alpha = check_arr(arr) 276 | if nan_colours is None: 277 | nan_colours = np.array([]).reshape(0, 3) 278 | background = get_background_rgb(arr, background) 279 | cmap = get_cmap(cmap, arr=arr, levels=levels, quantize=quantize) 280 | colours = cmap(np.linspace(0, 1, levels))[..., :3] 281 | codebook = np.vstack([colours, nan_colours, background]) 282 | 283 | if hillshade: 284 | arr, hill = remove_hillshade(arr) 285 | 286 | kdtree = cKDTree(codebook) 287 | dist, ix = kdtree.query(arr) 288 | 289 | # Remove anything that was inferred from too far, and background. 290 | ix = ix.astype(float) 291 | ix[dist >= threshold] = np.nan 292 | ix[ix >= len(colours)] = np.nan 293 | 294 | if crop is not None: 295 | ix = crop_out(ix, crop) 296 | 297 | return normalize(ix, vrange) 298 | -------------------------------------------------------------------------------- /unmap/unweave.py: -------------------------------------------------------------------------------- 1 | """ 2 | unweave.py 3 | 4 | Functions for 'unweaving the rainbow': recovering colour maps from 5 | images with no a priori knowledge. Refactored from an earlier version. 6 | 7 | Author: Matt Hall 8 | Copyright: 2022, Matt Hall 9 | Licence: Apache 2.0 10 | """ 11 | import fsspec 12 | import numpy as np 13 | import networkx as nx 14 | from PIL import Image 15 | from matplotlib.colors import LinearSegmentedColormap 16 | import matplotlib.pyplot as plt 17 | from skimage.feature import graycomatrix 18 | 19 | 20 | def ordered_unique(seq): 21 | return list(dict.fromkeys(seq)) 22 | 23 | 24 | def convert_imarray(imarray, colors=256): 25 | """ 26 | Convert an RGB image array to an index array and colourtable. The array 27 | will be quantized to the specified number of colours, and will be no larger 28 | than 512x512 pixels. 29 | 30 | Args: 31 | imarray (np.ndarray): The RGB or RGBA image array. 32 | colors (int): Number of colours to reduce to. 33 | 34 | Returns: 35 | imarray (np.ndarray): Array of indices into the colourtable. 36 | unique_colors (np.ndarray): Colourtable. 37 | """ 38 | if np.min(imarray) < 0 or np.max(imarray) > 255: 39 | raise ValueError("Image array must be in the range [0, 255] or [0, 1].") 40 | elif np.max(imarray) <= 1.0: 41 | imarray = imarray * 255 42 | imp = Image.fromarray(np.uint8(imarray)) 43 | imp = imp.quantize(colors=colors, dither=Image.Dither.NONE) 44 | imp.thumbnail((512, 512)) 45 | imarray = np.array(imp) 46 | palette = np.array(imp.getpalette()).reshape(-1, 3) 47 | unique = ordered_unique(tuple(i) for i in palette[:colors]/255) 48 | return imarray, np.array(unique) 49 | 50 | 51 | def construct_graph(imarray, colors=256, normed=True): 52 | """ 53 | Construct an undirected value adjacency graph from an image array. 54 | 55 | Weights are the number of times a pair of values co-occur in the image, 56 | normalized per value (i.e. per node in the graph). 57 | 58 | Args: 59 | imarray (np.ndarray): Array of values. 60 | colors (int): Number of colours in the image. 61 | normed (bool): Whether to normalize the weights. 62 | 63 | Returns: 64 | G (nx.Graph): Value adjacency graph. 65 | """ 66 | glcm = graycomatrix(imarray, 67 | distances=[1], 68 | angles=[0, np.pi/4, np.pi/2, 3*np.pi/4], 69 | levels=colors, 70 | symmetric=True 71 | ) 72 | 73 | # Add transitions over all directions. 74 | glcm = np.sum(np.squeeze(glcm), axis=-1) 75 | 76 | # Normalize. 77 | if normed: 78 | glcm = glcm / (1e-9 + np.sum(glcm, axis=-1)) 79 | 80 | # Construct and remove self-loops. 81 | G = nx.from_numpy_array(glcm) 82 | G.remove_edges_from(nx.selfloop_edges(G)) 83 | 84 | return G 85 | 86 | 87 | def plot_graph(G, unique_colors, layout='kamada_kawai', ax=None, figsize=(12, 8)): 88 | """ 89 | Plot a graph with colours. 90 | 91 | Args: 92 | G (nx.Graph): Graph to plot. 93 | unique_colors (np.ndarray): Colourtable. 94 | layout (str): Layout to use. 95 | ax (matplotlib.axes.Axes): Axes to plot on. 96 | figsize (tuple): Figure size. 97 | 98 | Returns: 99 | ax (matplotlib.axes.Axes): Axes. 100 | """ 101 | if layout == 'spring': 102 | pos = nx.spring_layout(G) 103 | elif layout == 'spectral': 104 | pos = nx.spectral_layout(G) 105 | elif layout == 'kamada_kawai': 106 | pos = nx.kamada_kawai_layout(G, weight='dist') 107 | else: 108 | raise ValueError("`layout` must be one of 'spring', 'spectral', or 'kamada_kawai' (default).") 109 | color = [unique_colors[n] for n in G] 110 | _, wt = zip(*nx.get_edge_attributes(G, 'weight').items()) 111 | 112 | if ax is None: 113 | _, ax = plt.subplots(figsize=figsize) 114 | nx.draw(G, pos=pos, ax=ax, node_size=30, 115 | node_color=color, 116 | edge_color=wt, 117 | edge_cmap=plt.cm.Greys, 118 | edge_vmin=-0.05, 119 | edge_vmax=0.25, alpha=0.75 120 | ) 121 | return ax 122 | 123 | 124 | def prune_graph(G, unique_colors, min_weight=0.025, max_dist=0.25, max_neighbours=20): 125 | """ 126 | Prune a graph to remove edges with low weight and high distance. 127 | 128 | Args: 129 | G (nx.Graph): Graph to prune. 130 | unique_colors (np.ndarray): Colourtable. 131 | min_weight (float): Minimum weight to keep. 132 | max_dist (float): Maximum distance to keep. 133 | max_neighbours (int): Maximum number of neighbours to allow. Nodes with 134 | more neighbours than this will be removed. 135 | 136 | Returns: 137 | G (nx.Graph): Pruned graph. 138 | """ 139 | G = G.copy() 140 | 141 | dist = lambda u, v: np.linalg.norm(unique_colors[u] - unique_colors[v]) 142 | 143 | # Calculate RGB distances. 144 | dist_dict = {(u, v): dist(u, v) for u, v, _ in G.edges.data()} 145 | nx.set_edge_attributes(G, dist_dict, 'dist') 146 | 147 | # Prune edges. 148 | remove = [(u, v) for u, v, d in G.edges.data() if d['weight'] < min_weight] 149 | remove += [(u, v) for u, v, d in G.edges.data() if d['dist'] > max_dist] 150 | G.remove_edges_from(remove) 151 | 152 | # Prune vertices. 153 | remove = [n for n, d in dict(G.degree()).items() if d > max_neighbours] 154 | G.remove_nodes_from(remove) 155 | 156 | # Return the giant component. 157 | Gcc = sorted(nx.connected_components(G), key=len) 158 | return G.subgraph(Gcc[-1]) 159 | 160 | 161 | def longest_shortest_path(G): 162 | """ 163 | Find the longest shortest path in a graph. This should be the path between 164 | the ends of the longest chain in the graph. 165 | 166 | Args: 167 | G (nx.Graph): Graph to search. 168 | 169 | Returns: 170 | path (list): Longest shortest path. 171 | """ 172 | 173 | dist = lambda u, v, d: d['dist']**2 174 | 175 | # Find the longest shortest path. 176 | paths = nx.shortest_path_length(G, weight=dist) 177 | longest, s, t = 0, None, None 178 | for source, path_dict in paths: 179 | for target, path_length in path_dict.items(): 180 | if path_length > longest: 181 | s, t = source, target 182 | longest = path_length 183 | 184 | return nx.shortest_path(G, 185 | weight=dist, 186 | source=s, 187 | target=t) 188 | 189 | 190 | def path_to_cmap(path, unique_colors, colors=256, reverse='auto', equilibrate=False): 191 | """ 192 | Convert a path through the graph to a colormap. 193 | 194 | Args: 195 | path (list): Path to convert. 196 | unique_colors (np.ndarray): Colourtable. 197 | colors (int): Number of colours to return. Default is 256. Use None to 198 | use twice the number of colours in the path. 199 | reverse (bool): Whether to reverse the colormap. If 'auto', the 200 | colormap will start with the end closest to dark blue. If False, 201 | the direction is essentially random. 202 | equilibrate (bool): Whether to equilibrate the colormap. This will 203 | try to ensure that the colormap's colors are as evenly spaced as 204 | possible. 205 | 206 | Returns: 207 | matplotlib.colors.LinearSegmentedColormap: Colormap. 208 | """ 209 | cpath = np.asarray(unique_colors)[path] 210 | if reverse == 'auto': 211 | cool_dark = np.array([0, 0, 0.5]) 212 | if np.linalg.norm(cpath[0] - cool_dark) > np.linalg.norm(cpath[-1] - cool_dark): 213 | reverse = True 214 | else: 215 | reverse = False 216 | if reverse: 217 | cpath = cpath[::-1] 218 | if colors is None: 219 | colors = 2 * len(cpath) # Not sure what the default should be. 220 | cmap = LinearSegmentedColormap.from_list("recovered", cpath, N=colors) 221 | if equilibrate: 222 | dists = np.linalg.norm(cpath[:-1] - cpath[1:], axis=-1) 223 | invdist = np.cumsum(1 / dists) / (1 / dists).sum() 224 | cmap = LinearSegmentedColormap.from_list('recovered', cmap(invdist), N=colors) 225 | return cmap 226 | 227 | 228 | def guess_cmap_from_array(array, 229 | source_colors=256, 230 | target_colors=256, 231 | min_weight=0.025, 232 | max_dist=0.25, 233 | max_neighbours=20, 234 | reverse='auto', 235 | equilibrate=False 236 | ): 237 | """ 238 | Guess the colormap of an image. 239 | 240 | Args: 241 | array (np.ndarray): The RGB or RGBA image array. 242 | source_colors (int): Number of colours to detect in the source image. 243 | target_colors (int): Number of colours to return in the colormap. 244 | min_weight (float): Minimum weight to keep. See `prune_graph`. 245 | max_dist (float): Maximum distance to keep. See `prune_graph`. 246 | max_neighbours (int): Maximum number of neighbours to allow. See `prune_graph`. 247 | reverse (bool): Whether to reverse the colormap. If 'auto', the 248 | colormap will start with the end closest to dark blue. If False, 249 | the direction is essentially random. 250 | 251 | """ 252 | imarray, uniq = convert_imarray(array, colors=source_colors) 253 | G = construct_graph(imarray, colors=source_colors) 254 | G0 = prune_graph(G, uniq, min_weight=min_weight, max_dist=max_dist, max_neighbours=max_neighbours) 255 | path = longest_shortest_path(G0) 256 | return path_to_cmap(path, uniq, colors=target_colors, reverse=reverse, equilibrate=equilibrate) 257 | 258 | 259 | def guess_cmap_from_image(fname, 260 | source_colors=256, 261 | target_colors=256, 262 | min_weight=0.025, 263 | max_dist=0.25, 264 | max_neighbours=20, 265 | reverse='auto', 266 | equilibrate=False 267 | ): 268 | """ 269 | Guess the colormap of an image. 270 | 271 | Args: 272 | fname (str): Filename or URL of image to guess. 273 | source_colors (int): Number of colours to detect in the source image. 274 | target_colors (int): Number of colours to return in the colormap. 275 | min_weight (float): Minimum weight to keep. See `prune_graph`. 276 | max_dist (float): Maximum distance to keep. See `prune_graph`. 277 | max_neighbours (int): Maximum number of neighbours to allow. See `prune_graph`. 278 | reverse (bool): Whether to reverse the colormap. If 'auto', the 279 | colormap will start with the end closest to dark blue. If False, 280 | the direction is essentially random. 281 | 282 | """ 283 | with fsspec.open(fname) as f: 284 | img = Image.open(f) 285 | return guess_cmap_from_array(np.asarray(img), 286 | source_colors=source_colors, 287 | target_colors=target_colors, 288 | min_weight=min_weight, 289 | max_dist=max_dist, 290 | max_neighbours=max_neighbours, 291 | reverse=reverse, 292 | equilibrate=equilibrate 293 | ) 294 | --------------------------------------------------------------------------------