Skip to content

Commit d1e307a

Browse files
committed
DOC: Rewrite parts of imshow origin/extent handling
1 parent 285c8d2 commit d1e307a

File tree

2 files changed

+82
-58
lines changed

2 files changed

+82
-58
lines changed

doc/_static/mpl.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ div.deprecated span.versionmodified {
416416
font-weight: bold;
417417
}
418418

419-
div.green {
419+
div.green, div.hint {
420420
color: #468847;
421421
background-color: #dff0d8;
422422
border: 1px solid #d6e9c6;

tutorials/intermediate/imshow_extent.py

Lines changed: 81 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
**data coordinates**, the *origin* kwarg controls how the image fills
1515
that bounding box, and the orientation in the final rendered image is
1616
also affected by the axes limits.
17+
18+
.. hint:: For the moment, you may want to skip the code details below and
19+
directly continue with the discussion of the results.
1720
"""
1821
import numpy as np
1922
import matplotlib.pyplot as plt
@@ -34,9 +37,9 @@ def index_to_coordinate(index, extent, origin):
3437

3538
return {
3639
"[0, 0]": (left, bottom),
37-
"[N', 0]": (left, top),
38-
"[0, M']": (right, bottom),
39-
"[N', M']": (right, top),
40+
"[M', 0]": (left, top),
41+
"[0, N']": (right, bottom),
42+
"[M', N']": (right, top),
4043
}[index]
4144

4245

@@ -60,9 +63,9 @@ def get_color(index, data, cmap):
6063
"""Return the data color of an index."""
6164
val = {
6265
"[0, 0]": data[0, 0],
63-
"[0, M']": data[0, -1],
64-
"[N', 0]": data[-1, 0],
65-
"[N', M']": data[-1, -1],
66+
"[0, N']": data[0, -1],
67+
"[M', 0]": data[-1, 0],
68+
"[M', N']": data[-1, -1],
6669
}[index]
6770
return cmap(val / data.max())
6871

@@ -80,7 +83,7 @@ def set_extent_None_text(ax):
8083
ha='center', va='center', color='w')
8184

8285

83-
def generate_imshow_demo_grid(extents, auto_limits):
86+
def generate_imshow_demo_grid(extents, xlim=None, ylim=None):
8487
N = len(extents)
8588
fig = plt.figure(tight_layout=True)
8689
fig.set_size_inches(6, N * (11.25) / 5)
@@ -98,12 +101,12 @@ def generate_imshow_demo_grid(extents, auto_limits):
98101
im = ax.imshow(d, origin=origin, extent=extent)
99102
left, right, bottom, top = im.get_extent()
100103

101-
if auto_limits or top > bottom:
104+
if xlim is None or top > bottom:
102105
upper_string, lower_string = 'top', 'bottom'
103106
else:
104107
upper_string, lower_string = 'bottom', 'top'
105108

106-
if auto_limits or left < right:
109+
if ylim is None or left < right:
107110
port_string, starboard_string = 'left', 'right'
108111
inverted_xindex = False
109112
else:
@@ -128,16 +131,17 @@ def generate_imshow_demo_grid(extents, auto_limits):
128131

129132
ax.set_title('origin: {origin}'.format(origin=origin))
130133

131-
for index in ["[0, 0]", "[0, M']", "[N', 0]", "[N', M']"]:
134+
for index in ["[0, 0]", "[0, N']", "[M', 0]", "[M', N']"]:
132135
tx, ty, halign = get_index_label_pos(index, extent, origin,
133136
inverted_xindex)
134137
facecolor = get_color(index, d, im.get_cmap())
135138
ax.text(tx, ty, index, color='white', ha=halign, va='center',
136139
bbox={'boxstyle': 'square', 'facecolor': facecolor})
137140

138-
if not auto_limits:
139-
ax.set_xlim(-1, 7)
140-
ax.set_ylim(-1, 6)
141+
if xlim:
142+
ax.set_xlim(*xlim)
143+
if ylim:
144+
ax.set_ylim(*ylim)
141145

142146
for ax, extent in zip(columns['label'], extents):
143147
text_kwargs = {'ha': 'right',
@@ -158,71 +162,92 @@ def generate_imshow_demo_grid(extents, auto_limits):
158162
return columns
159163

160164

161-
extents = (None,
162-
(-0.5, 6.5, -0.5, 5.5),
163-
(-0.5, 6.5, 5.5, -0.5),
164-
(6.5, -0.5, -0.5, 5.5),
165-
(6.5, -0.5, 5.5, -0.5))
166165

166+
###############################################################################
167+
#
168+
# Default extent
169+
# --------------
170+
#
171+
# First, let's have a look at the default `extent=None`
167172

173+
generate_imshow_demo_grid(extents=[None])
168174

169175
###############################################################################
170176
#
177+
# Generally, for an array of shape (M, N), the first index runs along the
178+
# vertical, the second index runs along the horizontal.
179+
# The pixel centers are at integer positions ranging from 0 to `N' = N - 1``
180+
# horizontally and from 0 to ``M' = M - 1`` vertically.
181+
# *origin* determines how to the data is filled in the bounding box.
171182
#
172-
# First, using *extent* we pick a bounding box in dataspace that the
173-
# image will fill and then interpolate/resample the underlying data to
174-
# fill that box.
183+
# For ``origin='lower'``:
175184
#
176-
# - If ``origin='lower'`` than the ``[0, 0]`` entry is closest to the
177-
# ``(left, bottom)`` corner of the bounding box and moving closer to
178-
# ``(left, top)`` moves along the ``[:, 0]`` axis of the array to
179-
# higher indexed rows and moving towards ``(right, bottom)`` moves you
180-
# along the ``[0, :]`` axis of the array to higher indexed columns
185+
# - [0, 0] is at (left, bottom)
186+
# - [M', 0] is at (left, top)
187+
# - [0, N'] is at (right, bottom)
188+
# - [M', N'] is at (right, top)
181189
#
182-
# - If ``origin='upper'`` then the ``[-1, 0]`` entry is closest to the
183-
# ``(left, bottom)`` corner of the bounding box and moving towards
184-
# ``(left, top)`` moves along the ``[:, 0]`` axis of the array to
185-
# lower index rows and moving towards ``(right, bottom)`` moves you
186-
# along the ``[-1, :]`` axis of the array to higher indexed columns
187-
188-
generate_imshow_demo_grid(extents[:1], auto_limits=True)
189-
190-
###############################################################################
190+
# ``origin='upper'`` reverses the vertical axes direction and filling:
191+
#
192+
# - [0, 0] is at (left, top)
193+
# - [M', 0] is at (left, bottom)
194+
# - [0, N'] is at (right, top)
195+
# - [M', N'] is at (right, bottom)
191196
#
192-
# If we only specify *origin* we can see why it is so named. For
193-
# ``origin='upper'`` the ``[0, 0]`` pixel is on the upper left and for
194-
# ``origin='lower'`` the ``[0, 0]`` pixel is in the lower left [#]_.
195-
# The gray arrows are attached to the ``(left, bottom)`` corner of the
196-
# image. There are two tricky things going on here: first the default
197-
# value of *extent* depends on the value of *origin* and second the x
198-
# and y limits are adjusted to match the extent. The default *extent*
199-
# is ``(-0.5, numcols-0.5, numrows-0.5, -0.5)`` when ``origin ==
200-
# 'upper'`` and ``(-0.5, numcols-0.5, -0.5, numrows-0.5)`` when ``origin
201-
# == 'lower'`` which puts the pixel centers on integer positions and the
202-
# ``[0, 0]`` pixel at ``(0, 0)`` in dataspace.
197+
# In summary, the position of the [0, 0] index as well as the extent are
198+
# influenced by *origin*:
203199
#
200+
# ====== =============== ==========================================
201+
# origin [0, 0] position extent
202+
# ====== =============== ==========================================
203+
# upper top left ``(-0.5, numcols-0.5, numrows-0.5, -0.5)``
204+
# lower bottom left ``(-0.5, numcols-0.5, -0.5, numrows-0.5)``
205+
# ====== =============== ==========================================
204206
#
205-
# .. [#] The default value of *origin* is set by :rc:`image.origin`
206-
# which defaults to ``'upper'`` to match the matrix indexing
207-
# conventions in math and computer graphics image indexing
208-
# conventions.
207+
# The default value of *origin* is set by :rc:`image.origin` which defaults
208+
# to ``'upper'`` to match the matrix indexing conventions in math and
209+
# computer graphics image indexing conventions.
210+
#
211+
#
212+
# Explicit extent
213+
# ---------------
214+
#
215+
# By setting *extent* we pick a bounding box in dataspace that the image will
216+
# fill. Then, the underlying data is interpolated/resampled to fill that box.
209217
#
210218
# If the axes is set to autoscale, then view limits of the axes are set
211219
# to match the *extent* which ensures that the coordinate set by
212220
# ``(left, bottom)`` is at the bottom left of the axes! However, this
213221
# may invert the axis so they do not increase in the 'natural' direction.
214222
#
215223

216-
columns = generate_imshow_demo_grid(extents[1:], auto_limits=True)
224+
extents = [(-0.5, 6.5, -0.5, 5.5),
225+
(-0.5, 6.5, 5.5, -0.5),
226+
(6.5, -0.5, -0.5, 5.5),
227+
(6.5, -0.5, 5.5, -0.5)]
228+
229+
columns = generate_imshow_demo_grid(extents)
217230
set_extent_None_text(columns['upper'][1])
218231
set_extent_None_text(columns['lower'][0])
219232

220233

221234
###############################################################################
222235
#
223-
# If we fix the axes limits so ``(0, 0)`` is at the bottom left and
224-
# increases to up and to the right (from the viewer point of view) then
225-
# we can see that:
236+
# Explicit extent and axes limits
237+
# -------------------------------
238+
#
239+
# If we fix the axes limits by explicity setting `set_xlim` / `set_ylim`, we
240+
# force a certain size and orientation of the axes.
241+
# This can decouple the 'left-right' and 'top-bottom' sense of the image from
242+
# the orientation on the screen.
243+
#
244+
# In the example below we have chosen the limits slightly larger than the
245+
# the extent (note the white areas within the Axes.
246+
#
247+
# While we keep the extents as in the examples before, (0, 0) is now
248+
# explicitly put at the bottom left and values
249+
# increase to up and to the right (from the viewer point of view).
250+
# We can see that:
226251
#
227252
# - The ``(left, bottom)`` anchors the image which then fills the
228253
# box going towards the ``(right, top)`` point in data space.
@@ -232,6 +257,5 @@ def generate_imshow_demo_grid(extents, auto_limits):
232257
# - The 'left-right' and 'top-bottom' sense of the image is uncoupled from
233258
# the orientation on the screen.
234259

235-
columns = generate_imshow_demo_grid(extents, auto_limits=False)
236-
set_extent_None_text(columns['upper'][2])
237-
set_extent_None_text(columns['lower'][1])
260+
generate_imshow_demo_grid(extents=[None] + extents,
261+
xlim=(-2, 8), ylim=(-1, 6))

0 commit comments

Comments
 (0)