├── .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 | [](https://github.com/scienxlab/unmap/actions/workflows/run-tests.yml)
4 | [](https://github.com/scienxlab/unmap/actions/workflows/build-docs.yml)
5 | [](https://pypi.org/project/unmap//)
6 | [](https://pypi.org/project/unmap//)
7 | [](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 |
Project links
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
"
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": "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\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 |
--------------------------------------------------------------------------------