强基初中数学&学Python——第272课 数字和数学第三方模块Matplotlib之七:封装端-自动缩放

imshow的原点(origin)和面片(extent)参数

  imshow()能将2D数组(基于norm和cmap的颜色映射)或3D RGB(a)数组(按原样使用),在数据空间中,渲染为矩形区域图像。最终渲染图像的取向由原点和面片关键字参数(和生成的AxesImage实例上的属性)以及图表域(axes)的数据范围来控制。

  面片关键字参数控制数据坐标中的边界框,图像将填充在里面,它们在数据坐标系中指定的边界框,具体是left(左), right(右), bottom(下), top(上)参数;原点关键字参数控制图像如何填充该边界框;另外,最终渲染图像中的方向也受图表域范围(axes limits)的影响。

提示

  下面的大部分代码用于将标签和信息文本添加到绘图中。所描述的原点和面片参数的影响可以在图形中看到,而无需查看所有代码细节。

  为了快速理解,可以跳过下面代码的详细信息,直接进入下一节进行结果讨论。

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport numpy as npimport matplotlib.pyplot as pltfrom 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('origin: {origin}'.format(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=None。

· 

generate_imshow_demo_grid(extents=[None])

 

  通常,对于形状(M,N)的阵列,第一索引沿着垂直方向变化,第二索引沿着水平方向变化。像素中心在水平方向0到N’=N-1的整数位置,垂直方向0到M’=M-1的整数位置上。原点确定数据在边界框中的填充方式。

  对于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)

  总之,[0,0]索引的位置和面片一样都受到原点参数的影响:

原点参数

[0, 0] 位置

面片参数

upper

top left

(左上角)

(-0.5, numcols-0.5, numrows-0.5, -0.5)

lower

bottom left

(左下角)

(-0.5, numcols-0.5, -0.5, numrows-0.5)

  原点参数的默认值由rcParams["image.origin"](默认值:'upper')设置,默认值'upper'以匹配数学和计算机图形图像索引约定中的矩阵索引约定。

显式面片参数

  通过设置面片参数,我们定义了图像区域的坐标。对基础图像数据进行插值/重新采样以填充该区域。

  如果将图表域(axes)设置为自动缩放,则图表域的视图限制设置为与面片参数匹配,以确保由 (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])

 

 

显式面片参数和图表域范围

  如果通过显式设置set_xlim/set_ylim来固定图表域的范围,就会强制限定图表域的大小和方向。这可以将图像的“左右”和“上下”感与屏幕上的方向解耦。

  在下面的例子中,我们选择了略大于面片参数的限制(注意图表域内的白色区域)。

  虽然我们保持前面示例中的面片参数,但坐标(0,0)现在明确地放在左下角,并且值向上和向右增加(从查看器的角度来看)。我们可以看到:

  · 坐标 (left, bottom) ——左,下——锚定图像,然后向右和上填充填充。
  · 第一列总是最靠近“左侧”。
  · 原点控制第一行是否最接近“顶部”或“底部”。
  · 图像可以沿着垂直或水平方向反转。
  · 图像的“左右”和“上下”感觉可能与屏幕上的方向分离。

· 

· 

generate_imshow_demo_grid(extents=[None] + extents,                          xlim=(-2, 8), ylim=(-1, 6))

 

 

imshow():https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.imshow.html#matplotlib.axes.Axes.imshowAxesImage:https://matplotlib.org/stable/api/image_api.html#matplotlib.image.AxesImagercParams["image.origin"]:https://matplotlib.org/stable/tutorials/introductory/customizing.html?highlight=image.origin#matplotlibrc-sampleset_xlim:https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.set_xlim.html#matplotlib.axes.Axes.set_xlimset_ylim:https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.set_ylim.html#matplotlib.axes.Axes.set_ylim