14
14
**data coordinates**, the *origin* kwarg controls how the image fills
15
15
that bounding box, and the orientation in the final rendered image is
16
16
also affected by the axes limits.
17
+
18
+ .. hint:: Most of the code below is used for adding labels and informative
19
+ text to the plots. The described effects of *origin* and *extent* can be
20
+ seen in the plots without the need to follow all code details.
21
+
22
+ For a quick understanding, you may want to skip the code details below and
23
+ directly continue with the discussion of the results.
17
24
"""
18
25
import numpy as np
19
26
import matplotlib .pyplot as plt
@@ -34,9 +41,9 @@ def index_to_coordinate(index, extent, origin):
34
41
35
42
return {
36
43
"[0, 0]" : (left , bottom ),
37
- "[N ', 0]" : (left , top ),
38
- "[0, M ']" : (right , bottom ),
39
- "[N ', M ']" : (right , top ),
44
+ "[M ', 0]" : (left , top ),
45
+ "[0, N ']" : (right , bottom ),
46
+ "[M ', N ']" : (right , top ),
40
47
}[index ]
41
48
42
49
@@ -60,9 +67,9 @@ def get_color(index, data, cmap):
60
67
"""Return the data color of an index."""
61
68
val = {
62
69
"[0, 0]" : data [0 , 0 ],
63
- "[0, M ']" : data [0 , - 1 ],
64
- "[N ', 0]" : data [- 1 , 0 ],
65
- "[N ', M ']" : data [- 1 , - 1 ],
70
+ "[0, N ']" : data [0 , - 1 ],
71
+ "[M ', 0]" : data [- 1 , 0 ],
72
+ "[M ', N ']" : data [- 1 , - 1 ],
66
73
}[index ]
67
74
return cmap (val / data .max ())
68
75
@@ -80,7 +87,52 @@ def set_extent_None_text(ax):
80
87
ha = 'center' , va = 'center' , color = 'w' )
81
88
82
89
83
- def generate_imshow_demo_grid (extents , auto_limits ):
90
+ def plot_imshow_with_labels (ax , data , extent , origin , xlim , ylim ):
91
+ """Actually run ``imshow()`` and add extent and index labels."""
92
+ im = ax .imshow (data , origin = origin , extent = extent )
93
+
94
+ # extent labels (left, right, bottom, top)
95
+ left , right , bottom , top = im .get_extent ()
96
+ if xlim is None or top > bottom :
97
+ upper_string , lower_string = 'top' , 'bottom'
98
+ else :
99
+ upper_string , lower_string = 'bottom' , 'top'
100
+ if ylim is None or left < right :
101
+ port_string , starboard_string = 'left' , 'right'
102
+ inverted_xindex = False
103
+ else :
104
+ port_string , starboard_string = 'right' , 'left'
105
+ inverted_xindex = True
106
+ bbox_kwargs = {'fc' : 'w' , 'alpha' : .75 , 'boxstyle' : "round4" }
107
+ ann_kwargs = {'xycoords' : 'axes fraction' ,
108
+ 'textcoords' : 'offset points' ,
109
+ 'bbox' : bbox_kwargs }
110
+ ax .annotate (upper_string , xy = (.5 , 1 ), xytext = (0 , - 1 ),
111
+ ha = 'center' , va = 'top' , ** ann_kwargs )
112
+ ax .annotate (lower_string , xy = (.5 , 0 ), xytext = (0 , 1 ),
113
+ ha = 'center' , va = 'bottom' , ** ann_kwargs )
114
+ ax .annotate (port_string , xy = (0 , .5 ), xytext = (1 , 0 ),
115
+ ha = 'left' , va = 'center' , rotation = 90 ,
116
+ ** ann_kwargs )
117
+ ax .annotate (starboard_string , xy = (1 , .5 ), xytext = (- 1 , 0 ),
118
+ ha = 'right' , va = 'center' , rotation = - 90 ,
119
+ ** ann_kwargs )
120
+ ax .set_title ('origin: {origin}' .format (origin = origin ))
121
+
122
+ # index labels
123
+ for index in ["[0, 0]" , "[0, N']" , "[M', 0]" , "[M', N']" ]:
124
+ tx , ty , halign = get_index_label_pos (index , extent , origin ,
125
+ inverted_xindex )
126
+ facecolor = get_color (index , data , im .get_cmap ())
127
+ ax .text (tx , ty , index , color = 'white' , ha = halign , va = 'center' ,
128
+ bbox = {'boxstyle' : 'square' , 'facecolor' : facecolor })
129
+ if xlim :
130
+ ax .set_xlim (* xlim )
131
+ if ylim :
132
+ ax .set_ylim (* ylim )
133
+
134
+
135
+ def generate_imshow_demo_grid (extents , xlim = None , ylim = None ):
84
136
N = len (extents )
85
137
fig = plt .figure (tight_layout = True )
86
138
fig .set_size_inches (6 , N * (11.25 ) / 5 )
@@ -90,54 +142,11 @@ def generate_imshow_demo_grid(extents, auto_limits):
90
142
'upper' : [fig .add_subplot (gs [j , 1 :3 ]) for j in range (N )],
91
143
'lower' : [fig .add_subplot (gs [j , 3 :5 ]) for j in range (N )]}
92
144
x , y = np .ogrid [0 :6 , 0 :7 ]
93
- d = x + y
145
+ data = x + y
94
146
95
147
for origin in ['upper' , 'lower' ]:
96
148
for ax , extent in zip (columns [origin ], extents ):
97
-
98
- im = ax .imshow (d , origin = origin , extent = extent )
99
- left , right , bottom , top = im .get_extent ()
100
-
101
- if auto_limits or top > bottom :
102
- upper_string , lower_string = 'top' , 'bottom'
103
- else :
104
- upper_string , lower_string = 'bottom' , 'top'
105
-
106
- if auto_limits or left < right :
107
- port_string , starboard_string = 'left' , 'right'
108
- inverted_xindex = False
109
- else :
110
- port_string , starboard_string = 'right' , 'left'
111
- inverted_xindex = True
112
-
113
- bbox_kwargs = {'fc' : 'w' , 'alpha' : .75 , 'boxstyle' : "round4" }
114
- ann_kwargs = {'xycoords' : 'axes fraction' ,
115
- 'textcoords' : 'offset points' ,
116
- 'bbox' : bbox_kwargs }
117
-
118
- ax .annotate (upper_string , xy = (.5 , 1 ), xytext = (0 , - 1 ),
119
- ha = 'center' , va = 'top' , ** ann_kwargs )
120
- ax .annotate (lower_string , xy = (.5 , 0 ), xytext = (0 , 1 ),
121
- ha = 'center' , va = 'bottom' , ** ann_kwargs )
122
- ax .annotate (port_string , xy = (0 , .5 ), xytext = (1 , 0 ),
123
- ha = 'left' , va = 'center' , rotation = 90 ,
124
- ** ann_kwargs )
125
- ax .annotate (starboard_string , xy = (1 , .5 ), xytext = (- 1 , 0 ),
126
- ha = 'right' , va = 'center' , rotation = - 90 ,
127
- ** ann_kwargs )
128
-
129
- ax .set_title ('origin: {origin}' .format (origin = origin ))
130
-
131
- for index in ["[0, 0]" , "[0, M']" , "[N', 0]" , "[N', M']" ]:
132
- tx , ty , halign = get_index_label_pos (index , extent , origin ,
133
- inverted_xindex )
134
- facecolor = get_color (index , d , im .get_cmap ())
135
- ax .text (tx , ty , index , color = 'white' , ha = halign , va = 'center' ,
136
- bbox = {'boxstyle' : 'square' , 'facecolor' : facecolor })
137
-
138
- if not auto_limits :
139
- ax .set_xlim (- 1 , 7 )
140
- ax .set_ylim (- 1 , 6 )
149
+ plot_imshow_with_labels (ax , data , extent , origin , xlim , ylim )
141
150
142
151
for ax , extent in zip (columns ['label' ], extents ):
143
152
text_kwargs = {'ha' : 'right' ,
@@ -158,80 +167,99 @@ def generate_imshow_demo_grid(extents, auto_limits):
158
167
return columns
159
168
160
169
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 ))
166
-
170
+ ###############################################################################
171
+ #
172
+ # Default extent
173
+ # --------------
174
+ #
175
+ # First, let's have a look at the default `extent=None`
167
176
177
+ generate_imshow_demo_grid (extents = [None ])
168
178
169
179
###############################################################################
170
180
#
181
+ # Generally, for an array of shape (M, N), the first index runs along the
182
+ # vertical, the second index runs along the horizontal.
183
+ # The pixel centers are at integer positions ranging from 0 to ``N' = N - 1``
184
+ # horizontally and from 0 to ``M' = M - 1`` vertically.
185
+ # *origin* determines how to the data is filled in the bounding box.
171
186
#
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.
187
+ # For ``origin='lower'``:
175
188
#
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
189
+ # - [0, 0] is at (left, bottom)
190
+ # - [M', 0] is at (left, top)
191
+ # - [0, N'] is at (right, bottom)
192
+ # - [M', N'] is at (right, top)
181
193
#
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
- ###############################################################################
194
+ # ``origin='upper'`` reverses the vertical axes direction and filling:
195
+ #
196
+ # - [0, 0] is at (left, top)
197
+ # - [M', 0] is at (left, bottom)
198
+ # - [0, N'] is at (right, top)
199
+ # - [M', N'] is at (right, bottom)
200
+ #
201
+ # In summary, the position of the [0, 0] index as well as the extent are
202
+ # influenced by *origin*:
203
+ #
204
+ # ====== =============== ==========================================
205
+ # origin [0, 0] position extent
206
+ # ====== =============== ==========================================
207
+ # upper top left ``(-0.5, numcols-0.5, numrows-0.5, -0.5)``
208
+ # lower bottom left ``(-0.5, numcols-0.5, -0.5, numrows-0.5)``
209
+ # ====== =============== ==========================================
210
+ #
211
+ # The default value of *origin* is set by :rc:`image.origin` which defaults
212
+ # to ``'upper'`` to match the matrix indexing conventions in math and
213
+ # computer graphics image indexing conventions.
214
+ #
215
+ #
216
+ # Explicit extent
217
+ # ---------------
191
218
#
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.
203
- #
204
- #
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.
209
- #
210
- # If the axes is set to autoscale, then view limits of the axes are set
219
+ # By setting *extent* we define the coordinates of the image area. The
220
+ # underlying image data is interpolated/resampled to fill that area.
221
+ #
222
+ # If the axes is set to autoscale, then the view limits of the axes are set
211
223
# to match the *extent* which ensures that the coordinate set by
212
224
# ``(left, bottom)`` is at the bottom left of the axes! However, this
213
225
# may invert the axis so they do not increase in the 'natural' direction.
214
226
#
215
227
216
- columns = generate_imshow_demo_grid (extents [1 :], auto_limits = True )
228
+ extents = [(- 0.5 , 6.5 , - 0.5 , 5.5 ),
229
+ (- 0.5 , 6.5 , 5.5 , - 0.5 ),
230
+ (6.5 , - 0.5 , - 0.5 , 5.5 ),
231
+ (6.5 , - 0.5 , 5.5 , - 0.5 )]
232
+
233
+ columns = generate_imshow_demo_grid (extents )
217
234
set_extent_None_text (columns ['upper' ][1 ])
218
235
set_extent_None_text (columns ['lower' ][0 ])
219
236
220
237
221
238
###############################################################################
222
239
#
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:
240
+ # Explicit extent and axes limits
241
+ # -------------------------------
242
+ #
243
+ # If we fix the axes limits by explicity setting `set_xlim` / `set_ylim`, we
244
+ # force a certain size and orientation of the axes.
245
+ # This can decouple the 'left-right' and 'top-bottom' sense of the image from
246
+ # the orientation on the screen.
247
+ #
248
+ # In the example below we have chosen the limits slightly larger than the
249
+ # extent (note the white areas within the Axes).
250
+ #
251
+ # While we keep the extents as in the examples before, the coordinate (0, 0)
252
+ # is now explicitly put at the bottom left and values increase to up and to
253
+ # the right (from the viewer point of view).
254
+ # We can see that:
226
255
#
227
- # - The ``(left, bottom)`` anchors the image which then fills the
256
+ # - The coordinate ``(left, bottom)`` anchors the image which then fills the
228
257
# box going towards the ``(right, top)`` point in data space.
229
258
# - The first column is always closest to the 'left'.
230
259
# - *origin* controls if the first row is closest to 'top' or 'bottom'.
231
260
# - The image may be inverted along either direction.
232
- # - The 'left-right' and 'top-bottom' sense of the image is uncoupled from
261
+ # - The 'left-right' and 'top-bottom' sense of the image may be uncoupled from
233
262
# the orientation on the screen.
234
263
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 ])
264
+ generate_imshow_demo_grid (extents = [None ] + extents ,
265
+ xlim = (- 2 , 8 ), ylim = (- 1 , 6 ))
0 commit comments