imshow() 可以把图像渲染到数据空间中的矩形区域。输入可以是二维数组,依照 norm 和 cmap 映射颜色;也可以是三维 RGB(A) 数组,直接使用其颜色。
最终方向由 origin、extent 参数,生成的 AxesImage 上对应属性,以及 Axes 的数据范围共同控制。
extent 以数据坐标 (left, right, bottom, top) 指定图像填充的边界框;origin 控制数据如何填入边界框;坐标轴范围也会影响最终方向。
提示:下面大部分代码用于为图表添加标签和说明文字。不必理解所有细节,也可以从图中看出 origin 与 extent 的效果。希望快速理解时,可以直接阅读结果讨论。
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.gridspec import GridSpec
def index_to_coordinate(index, extent, origin):
"""Return the pixel center of an index."""
left, right, bottom, top = extent
hshift = 0.5 * np.sign(right - left)
left, right = left + hshift, right - hshift
vshift = 0.5 * np.sign(top - bottom)
bottom, top = bottom + vshift, top - vshift
if origin == 'upper':
bottom, top = top, bottom
return {
"[0, 0]": (left, bottom),
"[M', 0]": (left, top),
"[0, N']": (right, bottom),
"[M', N']": (right, top),
}[index]
def get_index_label_pos(index, extent, origin, inverted_xindex):
"""
Return the desired position and horizontal alignment of an index label.
"""
if extent is None:
extent = lookup_extent(origin)
left, right, bottom, top = extent
x, y = index_to_coordinate(index, extent, origin)
is_x0 = index[-2:] == "0]"
halign = 'left' if is_x0 ^ inverted_xindex else 'right'
hshift = 0.5 * np.sign(left - right)
x += hshift * (1 if is_x0 else -1)
return x, y, halign
def get_color(index, data, cmap):
"""Return the data color of an index."""
val = {
"[0, 0]": data[0, 0],
"[0, N']": data[0, -1],
"[M', 0]": data[-1, 0],
"[M', N']": data[-1, -1],
}[index]
return cmap(val / data.max())
def lookup_extent(origin):
"""Return extent for label positioning when not given explicitly."""
if origin == 'lower':
return (-0.5, 6.5, -0.5, 5.5)
else:
return (-0.5, 6.5, 5.5, -0.5)
def set_extent_None_text(ax):
ax.text(3, 2.5, 'equals\nextent=None', size='large',
ha='center', va='center', color='w')
def plot_imshow_with_labels(ax, data, extent, origin, xlim, ylim):
"""Actually run ``imshow()`` and add extent and index labels."""
im = ax.imshow(data, origin=origin, extent=extent)
# extent labels (left, right, bottom, top)
left, right, bottom, top = im.get_extent()
if xlim is None or top > bottom:
upper_string, lower_string = 'top', 'bottom'
else:
upper_string, lower_string = 'bottom', 'top'
if ylim is None or left < right:
port_string, starboard_string = 'left', 'right'
inverted_xindex = False
else:
port_string, starboard_string = 'right', 'left'
inverted_xindex = True
bbox_kwargs = {'fc': 'w', 'alpha': .75, 'boxstyle': "round4"}
ann_kwargs = {'xycoords': 'axes fraction',
'textcoords': 'offset points',
'bbox': bbox_kwargs}
ax.annotate(upper_string, xy=(.5, 1), xytext=(0, -1),
ha='center', va='top', **ann_kwargs)
ax.annotate(lower_string, xy=(.5, 0), xytext=(0, 1),
ha='center', va='bottom', **ann_kwargs)
ax.annotate(port_string, xy=(0, .5), xytext=(1, 0),
ha='left', va='center', rotation=90,
**ann_kwargs)
ax.annotate(starboard_string, xy=(1, .5), xytext=(-1, 0),
ha='right', va='center', rotation=-90,
**ann_kwargs)
ax.set_title(f'origin: {origin}')
# index labels
for index in ["[0, 0]", "[0, N']", "[M', 0]", "[M', N']"]:
tx, ty, halign = get_index_label_pos(index, extent, origin,
inverted_xindex)
facecolor = get_color(index, data, im.get_cmap())
ax.text(tx, ty, index, color='white', ha=halign, va='center',
bbox={'boxstyle': 'square', 'facecolor': facecolor})
if xlim:
ax.set_xlim(*xlim)
if ylim:
ax.set_ylim(*ylim)
def generate_imshow_demo_grid(extents, xlim=None, ylim=None):
N = len(extents)
fig = plt.figure(tight_layout=True)
fig.set_size_inches(6, N * (11.25) / 5)
gs = GridSpec(N, 5, figure=fig)
columns = {'label': [fig.add_subplot(gs[j, 0]) for j in range(N)],
'upper': [fig.add_subplot(gs[j, 1:3]) for j in range(N)],
'lower': [fig.add_subplot(gs[j, 3:5]) for j in range(N)]}
x, y = np.ogrid[0:6, 0:7]
data = x + y
for origin in ['upper', 'lower']:
for ax, extent in zip(columns[origin], extents):
plot_imshow_with_labels(ax, data, extent, origin, xlim, ylim)
columns['label'][0].set_title('extent=')
for ax, extent in zip(columns['label'], extents):
if extent is None:
text = 'None'
else:
left, right, bottom, top = extent
text = (f'left: {left:0.1f}\nright: {right:0.1f}\n'
f'bottom: {bottom:0.1f}\ntop: {top:0.1f}\n')
ax.text(1., .5, text, transform=ax.transAxes, ha='right', va='center')
ax.axis('off')
return columns
默认 extent
先看默认的 extent=None:
generate_imshow_demo_grid(extents=[None])

一般而言,形状为 (M, N) 的数组,第一个索引沿竖直方向,第二个索引沿水平方向。像素中心位于整数坐标,水平范围是 0 到 N' = N - 1,竖直范围是 0 到 M' = M - 1。
origin 决定数据如何填充边界框。
当 origin='lower':
[0, 0]位于(left, bottom)。[M', 0]位于(left, top)。[0, N']位于(right, bottom)。[M', N']位于(right, top)。
origin='upper' 会反转竖直坐标轴与填充方向:
[0, 0]位于(left, top)。[M', 0]位于(left, bottom)。[0, N']位于(right, top)。[M', N']位于(right, bottom)。
因此,origin 同时影响 [0, 0] 的位置与 extent:
| origin | [0, 0] 位置 |
extent |
|---|---|---|
| upper | 左上 | (-0.5, numcols-0.5, numrows-0.5, -0.5) |
| lower | 左下 | (-0.5, numcols-0.5, -0.5, numrows-0.5) |
默认 origin 由 rcParams["image.origin"] 设置,默认值是 upper,以符合数学矩阵和计算机图形图像索引的惯例。
显式指定 extent
设置 extent 就定义了图像区域的数据坐标,底层数据会通过插值或重采样填满该区域。
如果 Axes 自动缩放,视图范围会匹配 extent,确保 (left, bottom) 位于 Axes 左下角。不过,这可能反转坐标轴,使数值不再沿“自然”方向增大。
extents = [(-0.5, 6.5, -0.5, 5.5),
(-0.5, 6.5, 5.5, -0.5),
(6.5, -0.5, -0.5, 5.5),
(6.5, -0.5, 5.5, -0.5)]
columns = generate_imshow_demo_grid(extents)
set_extent_None_text(columns['upper'][1])
set_extent_None_text(columns['lower'][0])

同时指定 extent 与坐标轴范围
通过 set_xlim 和 set_ylim 固定坐标轴范围,会强制 Axes 采用特定大小和方向,使图像的左右、上下方向与屏幕方向分离。
下例把坐标轴范围设得比 extent 稍大,注意 Axes 内的白色区域。extent 保持与前面相同,但现在明确让坐标 (0, 0) 位于左下,数值从观看者视角向上、向右增大。
可以看到:
(left, bottom)是图像锚点,图像向数据空间中的(right, top)填充。- 第一列始终最接近 left。
origin决定第一行最接近 top 还是 bottom。- 图像可以沿任一方向反转。
- 图像本身的左右、上下概念,可以与屏幕上的方向分离。
generate_imshow_demo_grid(extents=[None] + extents,
xlim=(-2, 8), ylim=(-1, 6))
plt.show()

原文示例脚本记录的运行时间为 7.539 秒,并在页面末尾提供完整 Jupyter 笔记本、Python 源码和 ZIP 下载。该示例图库由 Sphinx-Gallery 生成。
原文:Positioning and orientation of imshow images。作者/维护方:Matplotlib 文档维护者。本文为中文翻译,代码及命令保留原文。











暂无评论内容