|
5 | 5 |
|
6 | 6 | Images are represented by discrete pixels, either on the screen or in an |
7 | 7 | image file. When data that makes up the image has a different resolution |
8 | | -than its representation on the screen we will see aliasing effects. |
| 8 | +than its representation on the screen we will see aliasing effects. How |
| 9 | +noticeable these are depends on how much down-sampling takes place in |
| 10 | +the change of resolution (if any). |
9 | 11 |
|
10 | | -The default image interpolation in Matplotlib is 'antialiased'. This uses a |
11 | | -hanning interpolation for reduced aliasing in most situations. Only when there |
12 | | -is upsampling by a factor of 1, 2 or >=3 is 'nearest' neighbor interpolation |
13 | | -used. |
| 12 | +When subsampling data, aliasing is reduced by smoothing first and then |
| 13 | +subsampling the smoothed data. In Matplotlib, we can do that |
| 14 | +smoothing before mapping the data to colors, or we can do the smoothing |
| 15 | +on the RGB(A) data in the final image. The difference between these is |
| 16 | +shown below, and controlled with the *interpolation_stage* keyword argument. |
| 17 | +
|
| 18 | +The default image interpolation in Matplotlib is 'antialiased', and |
| 19 | +it is applied to the data. This uses a |
| 20 | +hanning interpolation on the data provided by the user for reduced aliasing |
| 21 | +in most situations. Only when there is upsampling by a factor of 1, 2 or |
| 22 | +>=3 is 'nearest' neighbor interpolation used. |
14 | 23 |
|
15 | 24 | Other anti-aliasing filters can be specified in `.Axes.imshow` using the |
16 | 25 | *interpolation* keyword argument. |
|
20 | 29 | import matplotlib.pyplot as plt |
21 | 30 |
|
22 | 31 | ############################################################################### |
23 | | -# First we generate a 500x500 px image with varying frequency content: |
24 | | -x = np.arange(500) / 500 - 0.5 |
25 | | -y = np.arange(500) / 500 - 0.5 |
| 32 | +# First we generate a 450x450 pixel image with varying frequency content: |
| 33 | +N = 450 |
| 34 | +x = np.arange(N) / N - 0.5 |
| 35 | +y = np.arange(N) / N - 0.5 |
| 36 | +aa = np.ones((N, N)) |
| 37 | +aa[::2, :] = -1 |
26 | 38 |
|
27 | 39 | X, Y = np.meshgrid(x, y) |
28 | 40 | R = np.sqrt(X**2 + Y**2) |
29 | | -f0 = 10 |
30 | | -k = 250 |
| 41 | +f0 = 5 |
| 42 | +k = 100 |
31 | 43 | a = np.sin(np.pi * 2 * (f0 * R + k * R**2 / 2)) |
32 | | - |
33 | | - |
| 44 | +# make the left hand side of this |
| 45 | +a[:int(N / 2), :][R[:int(N / 2), :] < 0.4] = -1 |
| 46 | +a[:int(N / 2), :][R[:int(N / 2), :] < 0.3] = 1 |
| 47 | +aa[:, int(N / 3):] = a[:, int(N / 3):] |
| 48 | +a = aa |
34 | 49 | ############################################################################### |
35 | | -# The following images are subsampled from 500 data pixels to 303 rendered |
36 | | -# pixels. The Moire patterns in the 'nearest' interpolation are caused by the |
37 | | -# high-frequency data being subsampled. The 'antialiased' image |
| 50 | +# The following images are subsampled from 450 data pixels to either |
| 51 | +# 125 pixels or 250 pixels (depending on your display). |
| 52 | +# The Moire patterns in the 'nearest' interpolation are caused by the |
| 53 | +# high-frequency data being subsampled. The 'antialiased' imaged |
38 | 54 | # still has some Moire patterns as well, but they are greatly reduced. |
39 | | -fig, axs = plt.subplots(1, 2, figsize=(7, 4), constrained_layout=True) |
40 | | -for ax, interp in zip(axs, ['nearest', 'antialiased']): |
41 | | - ax.imshow(a, interpolation=interp, cmap='gray') |
42 | | - ax.set_title(f"interpolation='{interp}'") |
| 55 | +# |
| 56 | +# There are substantial differences between the 'data' interpolation and |
| 57 | +# the 'rgba' interpolation. The alternating bands of red and blue on the |
| 58 | +# left third of the image are subsampled. By interpolating in 'data' space |
| 59 | +# (the default) the antialiasing filter makes the stripes close to white, |
| 60 | +# because the average of -1 and +1 is zero, and zero is white in this |
| 61 | +# colormap. |
| 62 | +# |
| 63 | +# Conversely, when the anti-aliasing occurs in 'rgba' space, the red and |
| 64 | +# blue are combined visually to make purple. This behaviour is more like a |
| 65 | +# typical image processing package, but note that purple is not in the |
| 66 | +# original colormap, so it is no longer possible to invert individual |
| 67 | +# pixels back to their data value. |
| 68 | + |
| 69 | +fig, axs = plt.subplots(2, 2, figsize=(5, 6), constrained_layout=True) |
| 70 | +axs[0, 0].imshow(a, interpolation='nearest', cmap='RdBu_r') |
| 71 | +axs[0, 0].set_xlim(100, 200) |
| 72 | +axs[0, 0].set_ylim(275, 175) |
| 73 | +axs[0, 0].set_title('Zoom') |
| 74 | + |
| 75 | +for ax, interp, space in zip(axs.flat[1:], |
| 76 | + ['nearest', 'antialiased', 'antialiased'], |
| 77 | + ['data', 'data', 'rgba']): |
| 78 | + ax.imshow(a, interpolation=interp, interpolation_stage=space, |
| 79 | + cmap='RdBu_r') |
| 80 | + ax.set_title(f"interpolation='{interp}'\nspace='{space}'") |
43 | 81 | plt.show() |
44 | 82 |
|
45 | 83 | ############################################################################### |
|
63 | 101 | plt.show() |
64 | 102 |
|
65 | 103 | ############################################################################### |
66 | | -# Apart from the default 'hanning' antialiasing `~.Axes.imshow` supports a |
| 104 | +# Apart from the default 'hanning' antialiasing, `~.Axes.imshow` supports a |
67 | 105 | # number of different interpolation algorithms, which may work better or |
68 | 106 | # worse depending on the pattern. |
69 | 107 | fig, axs = plt.subplots(1, 2, figsize=(7, 4), constrained_layout=True) |
|
72 | 110 | ax.set_title(f"interpolation='{interp}'") |
73 | 111 | plt.show() |
74 | 112 |
|
75 | | - |
76 | 113 | ############################################################################# |
77 | 114 | # |
78 | 115 | # .. admonition:: References |
|
0 commit comments