Add support for images with units
PR summary
This PR adds machinery for the data array given to a ScalarMappable to have units, and for those units to be used with images and associated colorbars.
Fixes https://github.com/matplotlib/matplotlib/issues/25062 Fixes https://github.com/matplotlib/matplotlib/issues/17447 Fixes https://github.com/matplotlib/matplotlib/issues/19476 Fixes https://github.com/astropy/astropy/issues/11306
Still needs a user facing example adding, but opening to get early feedback and full CI runs.
PR checklist
- [x] "closes #0000" is in the body of the PR description to link the related issue
- [x] new and changed code is tested
- [ ] Plotting related features are demonstrated in an example
- [ ] New Features and API Changes are noted with a directive and release note
- [x] Documentation complies with general and docstring guidelines
One thing that may be nice(?) is if the unit machinery could also ensure that for categorical images, interpolation_stage is always set to "rgba". (I guess the extra coupling between may be a bit awkward, though.) Indeed, for categoricals, interpolation_stage="data" is usually completely wrong (because category 1 isn't the "mean" of category 0 and category 2). A concrete example (but I've seen similar things quite often, typically when doing image segmentation):
import skimage.data # for sample data
# a categorical image: which channel (r/g/b) of the image is the most intense
im = skimage.data.stereo_motorcycle()[0].argmax(2)
fig, axs = plt.subplots(2)
axs[0].imshow(im); axs[1].imshow(im, interpolation_stage="rgba")
This gives
where the bottom image clearly shows that the most intense channel is always either "r" (0, purple) or "b" (2, yellow); interpolation_stage="data" (top) mistakently shows sometimes "g" (1) as the most intense channel because it averages 0 and 2. (Ignore the few pixels at the middle top where green is indeed the most intense channel.)