Skip to content

ENH: allow image to interpolate post RGBA#18782

Merged
QuLogic merged 1 commit into
matplotlib:masterfrom
jklymak:enh-image-postrgba-interpolate
Aug 13, 2021
Merged

ENH: allow image to interpolate post RGBA#18782
QuLogic merged 1 commit into
matplotlib:masterfrom
jklymak:enh-image-postrgba-interpolate

Conversation

@jklymak
Copy link
Copy Markdown
Member

@jklymak jklymak commented Oct 21, 2020

PR Summary

This implements a new boolean kwarg interp_postrgba to imshow that implements the interpolation after the data has been normed and mapped to RGBA.

Motivation

In the depths of time, we interpolated images in RGBA space, but this was deemed unacceptable in that it produces image pixels that are not in the colormap. So the interpolation was moved to be before the norm and colormapping. That has proven troublesome with respect to over/under data, but generally seems to work.

However, there are many cases, particularly when trying to antialias, that we really want to average in RGBA space, and allow colors that are out of the colormap because they visually merge adjacent pixels.

Proposal:

A kwarg, interp_space that allows the interpolation to be carried out before or after the RGBA mapping has been made. The way this is implemented now, it is just a toggle - i.e. you cannot interpolate both before and after the RGBA mapping. That is not impossible, but probably not typically needed or desired, and would make the code considerably messier. As it is, its quite a straightforward change.

Note the change could be made a level higher in _axes.imshow, however, this implementation allows us to skip all the mapping jiggery that we do for data space.

Result

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.colors as mcolors
import matplotlib.cm as cm
import copy

fig, axs = plt.subplots(2, 2, figsize=(5.5, 5.5), sharex=False, sharey=False,
                        constrained_layout=True)
N = 250
aa = np.ones((N, N))
aa[::2, :] = -1

x = np.arange(N) / N - 0.5
y = np.arange(N) / N - 0.5

X, Y = np.meshgrid(x, y)
R = np.sqrt(X**2 + Y**2)
f0 = 10
k = 75
a = np.sin(np.pi * 2 * (f0 * R + k * R**2 / 2))

a[:int(N/2),:][R[:int(N/2),:]<0.4] = -1
a[:int(N/2),:][R[:int(N/2),:]<0.3] = 1
aa[:, int(N/2):] = a[:, int(N/2):]

aa[20:50, 20:50] = np.NaN
aa[70:90, 70:90] = 1e6
aa[70:90, 20:30] = -1e6
aa[ 70:90, 195:215,] = 1e6
aa[20:30, 195:215] = -1e6

cmap = copy.copy(cm.RdBu_r)
cmap.set_over('yellow')
cmap.set_under('cyan')

axs = axs.flatten()
axs[0].imshow(aa, interpolation='nearest', cmap=cmap, vmin=-1.2, vmax=1.2)
axs[0].set_title('Zoomed')
axs[0].set_xlim([N/2-25, N/2+25])
axs[0].set_ylim([N/2+50, N/2-10])

axs[1].imshow(aa, interpolation='nearest', cmap=cmap, vmin=-1.2, vmax=1.2)
axs[1].set_title('No antialiasing')

axs[2].imshow(aa, interpolation='antialiased', cmap=cmap, vmin=-1.2, vmax=1.2)
axs[2].set_title('Data antialiasing')
pc = axs[3].imshow(aa, interpolation='antialiased', interp_space='rgba',
                   cmap=cmap, vmin=-1.2, vmax=1.2 )
axs[3].set_title('RGBA antialiasing')

for ax in axs:
    ax.set_xticklabels([])
    ax.set_yticklabels([])
plt.show()

for dpi in [50, 100, 200, 400, 800]:
    fig.savefig(f'res{dpi}.png', dpi=dpi)

400 dpi

At 400 dpi the images are not downsampled, so all three versions are the same

res400

200 dpi

Notice how at 100 dpi the alternating blue/red in the data interpolation go to white (the average of -1 and 1), with some obvious residual aliasing. The RBGA interpolation goes to purples (red+blue = purple).

res200

100 dpi

res100

50 dpi

Note that pixel peeping at the "over" and "under" you can definitely see pixels that leak out of the colormap (i.e. cyan+purple, or yellow + blue).

res50

PR Checklist

  • Has pytest style unit tests (and pytest passes).
  • Is Flake 8 compliant (run flake8 on changed files to check).
  • New features are documented, with examples if plot related.
  • Documentation is sphinx and numpydoc compliant (the docs should build without error).
  • Conforms to Matplotlib style conventions (install flake8-docstrings and pydocstyle<4 and run flake8 --docstring-convention=all).
  • New features have an entry in doc/users/next_whats_new/ (follow instructions in README.rst there).
  • API changes documented in doc/api/next_api_changes/ (follow instructions in README.rst there).

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

4 participants