imshow 中的 originextent#

imshow() 允許您將影像 (2D 陣列,將根據 normcmap 進行顏色映射;或是 3D RGB(A) 陣列,將按原樣使用) 渲染到資料空間中的矩形區域。最終渲染中影像的方向由 originextent 關鍵字引數 (以及產生的 AxesImage 執行個體的屬性) 和軸的資料限制控制。

extent 關鍵字引數控制影像將填充的資料座標中的邊界框,指定為 (左, 右, 下, 上),以資料座標表示。origin 關鍵字引數控制影像如何填充該邊界框,並且最終渲染影像中的方向也會受到軸限制的影響。

提示

以下大部分程式碼用於將標籤和資訊文字新增到繪圖中。 originextent 的描述效果可以在繪圖中看到,無需遵循所有程式碼細節。

為了快速理解,您可能想要略過下面的程式碼細節,直接繼續討論結果。

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])
extent=, origin: upper, origin: lower

一般來說,對於形狀為 (M, N) 的陣列,第一個索引沿著垂直方向執行,第二個索引沿著水平方向執行。像素中心位於整數位置,水平範圍從 0 到 N' = N - 1,垂直範圍從 0 到 M' = M - 1origin 決定資料如何填充在邊界框中。

對於 origin='lower'

  • [0, 0] 位於 (左, 下)

  • [M', 0] 位於 (左, 上)

  • [0, N'] 位於 (右, 下)

  • [M', N'] 位於 (右, 上)

origin='upper' 反轉垂直軸方向和填充

  • [0, 0] 位於 (左, 上)

  • [M', 0] 位於 (左, 下)

  • [0, N'] 位於 (右, 上)

  • [M', N'] 位於 (右, 下)

總結來說,[0, 0] 索引的位置以及 extent 都會受到 origin 的影響

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') 設定,預設為 'upper',以符合數學和電腦圖形影像索引慣例中的矩陣索引慣例。

明確的 extent#

透過設定 extent,我們定義影像區域的座標。基礎影像資料會進行插值/重新取樣,以填充該區域。

如果將軸設定為自動縮放,則軸的檢視限制會設定為符合 extent,這可確保由 (左, 下) 設定的座標位於軸的左下角!但是,這可能會反轉軸,使其不會以「自然」方向增加。

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=, origin: upper, origin: upper, origin: upper, origin: upper, origin: lower, origin: lower, origin: lower, origin: lower

明確的 extent 和軸限制#

如果我們透過明確設定 set_xlim / set_ylim 來固定軸限制,我們會強制軸具有特定的大小和方向。這可以將影像的「左右」和「上下」感覺與螢幕上的方向分離。

在下面的範例中,我們選擇的限制略大於 extent (請注意軸內部的白色區域)。

當我們保持與先前範例相同的 extent 時,座標 (0, 0) 現在會明確地放置在左下角,且值會從檢視者的角度往上和往右增加。我們可以看到

  • 座標 (左, 下) 會錨定影像,然後填滿朝向資料空間中 (右, 上) 點的方塊。

  • 第一欄永遠最靠近「左」。

  • origin 控制第一列是否最靠近「上」或「下」。

  • 影像可能會沿任一方向反轉。

  • 影像的「左右」和「上下」感覺可能會與螢幕上的方向分離。

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

plt.show()
extent=, origin: upper, origin: upper, origin: upper, origin: upper, origin: upper, origin: lower, origin: lower, origin: lower, origin: lower, origin: lower

腳本的總執行時間: (0 分鐘 5.046 秒)

由 Sphinx-Gallery 產生的圖庫