目录9

文章Matplotlib第 19 章

图像显示

Matplotlib · 第 19 / 43 章

imshow() —— 显示图像 / 二维数组

imshow() 把二维数组铺成像素网格,一格一个颜色。名字来自「显示图像」,但日常更常用来看矩阵——因为它是所有二维渲染里最快的一种(整块位图一次性贴上去,不是逐个 Patch)。

Python17 行
Axes.imshow(
    X,                     # 二维数组 → 伪彩色;三维 (M,N,3)/(M,N,4) → 真彩色
    cmap=None,             # 颜色映射,只对二维数组生效
    norm=None,             # 归一化对象,如 LogNorm() / TwoSlopeNorm(vcenter=0)
    aspect=None,           # ★ 'equal'(默认,格子必为正方形)/ 'auto'(铺满)
    interpolation=None,    # 放大时的插值:'nearest' / 'bilinear' / 'bicubic'
    alpha=None,
    vmin=None, vmax=None,  # 色阶两端;多图对比必须固定
    origin=None,           # ★ 'upper'(默认,第 0 行在顶)/ 'lower'
    extent=None,           # ★ 物理坐标 [left, right, bottom, top]
    interpolation_stage=None,  # 'data' / 'rgba',一般不用
    filternorm=True, filterrad=4.0,
    resample=None,         # 大数据量时设 False 提速
    url=None,
    data=None,
    **kwargs
)
参数 一句话 默认值的影响
X 二维或三维数组 三维按 RGB(A) 处理
cmap 颜色映射 三维数组时被忽略
aspect 格子形状 'equal':非方阵会拉成细长条
interpolation 放大插值 None → 走 rcParam 'auto'
origin 原点位置 'upper':第 0 行在顶部
extent 坐标范围 默认按索引,像素中心落在整数上
vmin / vmax 色阶范围 不给就取数组 min/max
alpha 透明度 叠在别的图上时用
resample 重采样 大图设 False 明显更快

origin:第 0 行在顶还是在底

Python10 行
import numpy as np
import matplotlib.pyplot as plt

rng = np.random.default_rng(42)
Z = rng.random((6, 6))

fig, ax = plt.subplots()
im = ax.imshow(Z)
fig.colorbar(im, ax=ax)
plt.show()

imshow 最小示例:6×6 随机矩阵

Python2 行
ax.imshow(Z)                     # 默认 'upper':第 0 行画在顶部,y 轴向下增长
ax.imshow(Z, origin="lower")     # 第 0 行画在底部,y 轴向上增长

判据很清楚:把数组当矩阵(行 = 样本序号、列 = 特征)→ upper;把数组当空间场(行 = y 坐标)→ lower。

默认 upper 是图像惯例,但它会让 y 轴刻度从上往下读——这也是「为什么我的热力图 y 轴是反的」的唯一原因。

extent:把索引换成真实坐标

默认坐标是索引,而且像素中心落在整数上:

Python3 行
im = ax.imshow(Z)          # Z 是 6×6
print(im.get_extent())
# 输出: [-0.5, 5.5, 5.5, -0.5]

四个数是 (left, right, bottom, top)。left/right 比列数差半个格子,是为了让像素中心落在整数上(第 0 列中心在 x=0)。

对 6×6 数组,bottom = 5.5 而 top = -0.5——bottom > top 就是 origin='upper' 的坐标表达:y 轴从上往下。换成 4×6 数组,它变成 [-0.5, 5.5, 3.5, -0.5],只有 bottom 跟着行数变。

换成真实单位:

Python1 行
ax.imshow(Z, extent=[0, 6, 0, 60], aspect="auto")   # x 是 0~6,y 是 0~60

extent 的四个值顺序是 [左, 右, 下, 上]。写成 [xmin, xmax, ymin, ymax] 恰好也是这个顺序,但如果 bottom > top,matplotlib 会理解成翻转 y 轴,不会报错——这是把 extent 写错却看不出问题的常见原因。

imshow 参数:插值 / origin / extent / aspect / 色阶

aspect:格子被不被拉变形

Python2 行
ax.imshow(Z)                        # 默认 'equal':一格必须正方形 → 非方阵变细长条
ax.imshow(Z, aspect="auto")         # 铺满子图,格子被拉成矩形

对一个 5×30 的矩阵,'equal' 会把图压成一条扁带;'auto' 才能看清每一格。看数值关系(热力图、相关性矩阵)用 auto;看图像本身(照片、地图)用 equal。

'equal' 不是写死在函数里的,来自 rcParam image.aspect:

Python2 行
print(plt.rcParams["image.aspect"])
# 输出: equal

改 rcParams 就能全局改默认,但更推荐逐图传参——不然换个文件画图,行为就变了。

interpolation:放大时怎么补

Python3 行
ax.imshow(Z, interpolation="nearest")    # 方块锐利,一格就是一格
ax.imshow(Z, interpolation="bilinear")   # 双线性,颜色互相渗透
ax.imshow(Z, interpolation="bicubic")    # 更平滑,边缘发虚

默认是 None → 走 rcParam 'auto',规则是「数据比屏幕小时用 nearest,否则用 antialiased」。所以小矩阵放大显示时,默认看着就是方块。

两个必须记住的点:

  • 插值只影响显示,不改数据。im.get_array() 拿到的还是原数组。
  • 插值在导出 PDF/SVG 时同样生效(它是渲染期行为),所以别指望「导出就自动锐利」。

vmin / vmax:色阶的两端

Python3 行
ax.imshow(Z)                          # 自动取 min/max
ax.imshow(Z, vmin=0.2, vmax=0.8)      # 超出部分饱和到两端颜色
ax.imshow(Z, vmin=0, vmax=1)          # 固定到「理论范围」,多图可比

多子图对比时必须固定 vmin/vmax,否则同一个蓝色在不同子图里代表不同数值,读者会得出错误结论。后置修改用 im.set_clim(vmin, vmax)。

三维数组:真彩色,cmap 失效

Python4 行
rgb = rng.random((20, 30, 3))
ax.imshow(rgb)                       # 当成 RGB 图像,cmap 被忽略
rgba = rng.random((20, 30, 4))
ax.imshow(rgba)                      # 第 4 通道是 alpha

浮点数组的取值必须落在 [0, 1];超出的会被截断(不报错)。整数数组则按 uint8 解释(0~255)。

返回对象
Python15 行
im = ax.imshow(Z)
print(type(im).__name__)
# 输出: AxesImage

fig.colorbar(im, ax=ax)                 # 直接传,不要传 im.get_array()
print(im.get_clim())
# 输出: (0.04380376578722878, 0.9756223516367559)    ← 就是 Z 的 min/max

im.set_clim(0, 1)                       # 改色阶(= 改对比度)
im.set_cmap("jet")                      # 改配色
print(im.get_extent())
# 输出: [-0.5, 5.5, 5.5, -0.5]

im.set_data(Z * 2)                       # 换数据:动画里比重复 imshow 快得多
plt.show()

AxesImage 一个对象管整张位图。做动画/交互时用 set_data() + fig.canvas.draw_idle(),不要每帧都 imshow——后者会不断新建 Artist,几分钟就卡死。

注意事项

  • origin 默认 'upper',第 0 行在顶部,y 轴向下——和 plot 完全相反。
  • aspect 默认 'equal',非方阵矩阵会被拉成细长条,这是「热力图为什么这么小」的原因。
  • extent 的顺序是 [左, 右, 下, 上],bottom > top 表示 y 轴翻转,不报错。
  • interpolation 只影响显示,get_array() 拿到的永远是原数据。
  • 三维数组走 RGB(A) 模式,此时 cmap / vmin / vmax 全部失效。
  • 大数组导出 PDF/SVG 会生成巨大的矢量文件,加 rasterized=True 把它压成位图。
  • colorbar 传的是 im 这个 Artist,不是数组。

综合示例:热力图 / 相关系数矩阵 / RGB 图

Python45 行
import numpy as np
import matplotlib.pyplot as plt

plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei", "DejaVu Sans"]
plt.rcParams["axes.unicode_minus"] = False

rng = np.random.default_rng(3)
field = np.sin(np.add.outer(np.linspace(0, 6, 40), np.linspace(0, 9, 60)))

fig, axes = plt.subplots(1, 3, figsize=(15, 4.2))

# ---- (a) 场:origin='lower' + extent 换成真实坐标 ----
im0 = axes[0].imshow(field, origin="lower", extent=[0, 9, 0, 6],
                     aspect="auto", cmap="RdBu_r", vmin=-1, vmax=1)
axes[0].set_xlabel("x (m)")
axes[0].set_ylabel("y (m)")
fig.colorbar(im0, ax=axes[0])
axes[0].set_title("(a) origin='lower' + extent")

# ---- (b) 相关系数矩阵:aspect='auto',格子看得清 ----
corr = np.array([[1.00, 0.72, -0.35, 0.18],
                 [0.72, 1.00, -0.21, 0.44],
                 [-0.35, -0.21, 1.00, -0.60],
                 [0.18, 0.44, -0.60, 1.00]])
names = ["价格", "销量", "评分", "库存"]
im1 = axes[1].imshow(corr, cmap="RdBu_r", vmin=-1, vmax=1, aspect="auto")
axes[1].set_xticks(range(4), names, rotation=30)
axes[1].set_yticks(range(4), names)
for i in range(4):
    for j in range(4):
        axes[1].text(j, i, f"{corr[i, j]:.2f}", ha="center", va="center",
                     fontsize=9, color="white" if abs(corr[i, j]) > 0.6 else "black")
fig.colorbar(im1, ax=axes[1])
axes[1].set_title("(b) 相关系数矩阵(对称固定色阶)")

# ---- (c) 三维数组 = 真彩色 ----
rgb = np.zeros((60, 90, 3))
rgb[:, :45, 0] = np.linspace(0.2, 1.0, 45)       # 左半:红渐变
rgb[:, 45:, 2] = np.linspace(0.2, 1.0, 45)       # 右半:蓝渐变
rgb[25:35, :, 1] = 0.85                          # 中间绿条纹
axes[2].imshow(rgb, interpolation="nearest")
axes[2].set_title("(c) (M,N,3) 真彩色(cmap 无效)")

plt.tight_layout()
plt.show()

(b) 是 imshow 最高频的实战用法:对称色阶(vmin=-1, vmax=1 配发散色图 RdBu_r)加上格内数字标注,一张图同时给出形状和数值。