Skip to content

DOC: draft of a full explanation of norm + colormap interactions #18487

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

tacaswell
Copy link
Member

PR Summary

This is the start of the documentation I said I would write on the call about float issues in resampling / normalizing / colormapping images, but I got distracted making sure that we had this well documented.

The text at https://matplotlib.org/3.3.1/tutorials/colors/colormapnorms.html is correct, but terse. The code at https://matplotlib.org/3.3.1/gallery/userdemo/colormap_normalizations.html is almost the same but even less text. https://matplotlib.org/3.3.1/tutorials/colors/colormaps.html is mostly about how to pick the right color map, and https://matplotlib.org/3.3.1/tutorials/colors/colormap-manipulation.html is about working with colormap objects once you have them.

I think the second one should probably go away and the first should link back to this new text.

@tacaswell tacaswell added this to the v3.4.0 milestone Sep 15, 2020
@story645 story645 self-assigned this Sep 15, 2020
@tacaswell tacaswell force-pushed the doc_floating_point_and_you branch 2 times, most recently from 80b7c57 to 9d22732 Compare October 7, 2020 04:29
@tacaswell tacaswell marked this pull request as ready for review October 7, 2020 04:30
@tacaswell tacaswell requested review from jklymak and anntzer and removed request for anntzer October 7, 2020 04:30
scaled = (resampled - .1) * (data_max - data_min) + data_min

For "most" user data is OK, but can fail in interesting ways. First,
if range of the input data is large, but the range the user actually

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"if the range"

@jklymak
Copy link
Member

jklymak commented Oct 7, 2020

So far, it looks like this includes pretty useful info at good places in the docs.

However, I suggest being more colloquial. I guess you could describe a colormap as a 1-D path through R^3 space, but maybe "a list of N colors specified as RGB" will be more accessible? It took me a while to understand what you were trying to say, and my undergrad degree was Mathematics and Physics, albeit a few years ago ;-)

the data <sphx_glr_tutorials_colors_colormaps.py>` but in general
"good" colormaps smoothly and continuously change their RGB values as
a function of the input data. By looking at the RGB values as we go
through the full range of the user data (e.g. a colorbar) we can trace
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what a colorbar has to do with this sentence.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also the "good" in parenthesis is probably a stand in for some precise term and otherwise not true for categorical data and questionable on discrete data.

Comment on lines +114 to +118
max of the data are drastically different than the vmin / vmax of the
norm we use a data range expanded from vmin/vmax in the rescaling.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence kind of runs on.

class in `~.cm` and the `~.Normalize` and `~.Colormap` classes in
`~.colors` (this module).

At the core, colormapping is going from a scalar value to a RGB tuple
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still feel this is more complicated than it needs to be, and not even particularly true if you want to be mathematically rigorous; colormaps are finite discrete ordered points in R^3, not a smooth curve or path, so I find taking about it like this distracting. The first step is normalizing the data between vmin and vmax to the range 0-1, perhaps using a non-linear function, and secondly, using a linear lookup table to find the RGBA color closest to that value between 0 and 1.

Copy link
Member

@jklymak jklymak Oct 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always like code examples to make the abstract concrete:

A color mapping from floating point data values to colors takes place in two steps.  
First the data is normalized between 0 and 1.  This requires a `vmin` and `vmax` the define 
which values are mapped to 0 and 1, respectively, and it requires a function that defines the 
mapping between these two values.    The simplest case is a linear map ::
    
		import matplotlib.colors as mcolors
		norm = mcolors.Normalize(vmin=100, vmax=300) 
		norm([100, 200, 300])  # returns 0.0, 0.5, 1.0

but a logarithmic norm is also possible ::

    		norm = mcolors.LogNorm(vmin=10, vmax=1000)
		norm([10, 100, 1000])  # returns 0.0, 0.5, 1.0

Once the data has been normalized between 0 and 1, it can be passed to a colormap 
to return RGB(A) values ::
		
		cmap = cm.viridis
		cmap([0.0, 0.5, 1.0])  
	      #  returns RGBA array with the first three colors of viridis colormap.
	      # array([[0.267004, 0.004874, 0.329415, 1.      ],
             # [0.127568, 0.566949, 0.550556, 1.      ],
             # [0.993248, 0.906157, 0.143936, 1.      ]])

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use discrete colormaps as an implementation detail (both for performance and because we are targeting 8bit RGB which is inherently discrete), but if we had infinite precision floats and an analytic function for the colormap the whole system would still hold together.

I think the correct solution is to include both versions :)

@tacaswell tacaswell force-pushed the doc_floating_point_and_you branch from 55c6446 to cbe0c6e Compare October 28, 2020 20:25
@jklymak jklymak modified the milestones: v3.4.0, v3.4.1 Jan 27, 2021
@QuLogic QuLogic modified the milestones: v3.4.1, v3.4-doc Mar 30, 2021
@jklymak jklymak marked this pull request as draft May 8, 2021 22:07
@tacaswell tacaswell force-pushed the doc_floating_point_and_you branch from cbe0c6e to 0478be2 Compare August 13, 2021 22:09
@tacaswell tacaswell force-pushed the doc_floating_point_and_you branch from 0478be2 to 317a6af Compare August 14, 2021 00:49
@tacaswell
Copy link
Member Author

the colors_api.rst file is probably ready for a re-review, but I have not come back to the image_api.rst text yet.

@story645 story645 removed their assignment Aug 15, 2021
Normalization and Colormapping of Continuous Data
-------------------------------------------------

Some `~.artist.Artist` classes can map an array of input data to RGBA
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a TOC aspect to this file, and then you've added an explanation. a) not 100% sure the explanation belongs here, versus an intermediate/advanced tutorial. Most long-form explanations are tutorials currently (i.e. transform stack). I'm not adamantly advocating for that, but it would be more consistent with current practice. It would also make your examples easier to write.

Regardless, this large narrative in the middle of the TOC makes the TOC very hard to parse. At the very least, this should go at the end?

Comment on lines +49 to +50
At the core, colormapping is going from a scalar value to a RGB tuple (formally
:math:`f(x) : ℝ^1 \rightarrow ℝ^3`). To effectively communicate through the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
At the core, colormapping is going from a scalar value to a RGB tuple (formally
:math:`f(x) : ℝ^1 \rightarrow ℝ^3`). To effectively communicate through the
At the core, colormapping is going from a scalar value to a RGB tuple (formally
:math:`f(x) : ℝ^1 \rightarrow ℝ_[0,1]^1 \rightarrow ℝ^3`). To effectively communicate through the

At the core, colormapping is going from a scalar value to a RGB tuple (formally
:math:`f(x) : ℝ^1 \rightarrow ℝ^3`). To effectively communicate through the
color we want pick a :ref:`colormap suited to the data
<sphx_glr_tutorials_colors_colormaps.py>`. For continuous data types [#f1]_ a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The outline here is a little non-linear, and seems to take a while before it settles into an order.

  • mapping
  • choosing colormap
  • mapping
  • norm
  • norm
  • colormapping

I think this could all be three paragraphs, or maybe 5 if you want to be expansive:

  1. overview
  2. norm step
  3. colormap step

If you plan to talk about the "Curve through space" I wonder if a diagram would make this much clearer. Expressing this with math formalisms without diagrams is going to lose your audience.

@github-actions
Copy link

Since this Pull Request has not been updated in 60 days, it has been marked "inactive." This does not mean that it will be closed, though it may be moved to a "Draft" state. This helps maintainers prioritize their reviewing efforts. You can pick the PR back up anytime - please ping us if you need a review or guidance to move the PR forward! If you do not plan on continuing the work, please let us know so that we can either find someone to take the PR over, or close it.

@github-actions github-actions bot added the status: inactive Marked by the “Stale” Github Action label Oct 16, 2023
@tacaswell tacaswell added the keep Items to be ignored by the “Stale” Github Action label Oct 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Documentation keep Items to be ignored by the “Stale” Github Action status: inactive Marked by the “Stale” Github Action status: needs rebase
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants