Visualization of 3D and 4D image data: problem with spacing between subplots in grid plot

Dear matplotlib users,

I have to visualize 3 and 4 dimensional images and overlay on top some data
(e.g. masks, tracking results, etc). I need a solution to explore (e.g.
visualize inline in IPython) and to present my results (e.g. insert pdf
into slides). I wrote a few functions using matplotlib and I can plot a
sequence of projections of 3-D volumes:

[image: Inline image 1]

However, as you can see there is a problem with spacing near projections
which makes the plot look not so nice. In ideal case I would like to have
the same spacing between x- and y-projections and corresponding
z-projection. I spend quite a lot of time trying to figure out how to do
that with matplotlib APIs, but I still haven't found a solution. I would
highly appreciate if you could help.

I also posted this question on stackoverflow:

My full code is also available on Github:

Minimal code to reproduce the problem is bellow.

Thank you for your help!

Best regards,

import mathimport numpy as npimport as cmimport
matplotlib.pyplot as pltimport matplotlib.gridspec as gridspec

def project_image_stack(img, figwidth=3, axes=None):
    dim = len(img.shape)
    assert dim == 3
    n_slices, n_h, n_w = img.shape

    figheight = (figwidth)*np.float64(n_h + n_slices)/(n_w + n_slices)

    z_proj = np.mean(img, axis=0)
    y_proj = np.mean(img, axis=1)
    x_proj = np.mean(img, axis=2).T

    if axes is None:
        # Create figure
        fig = plt.figure(figsize=(figwidth, figheight))
        gs = gridspec.GridSpec(2, 2, width_ratios=[n_w, n_slices],
                               height_ratios=[n_h, n_slices])
        gs.update(wspace=0.05, hspace=0.05, bottom=0, top=1, left=0, right=1)
        # Create axes:
        ax_z = fig.add_subplot(gs[0, 0])
        ax_y = fig.add_subplot(gs[1, 0], sharex=ax_z)
        ax_x = fig.add_subplot(gs[0, 1], sharey=ax_z)
        assert len(axes) == 3
        fig = None
        ax_z, ax_y, ax_x = axes
    # z projection:
    ax_z.imshow(z_proj, interpolation='nearest', cmap=cm.gray, aspect=1)
    ax_z.set_xlim([-0.5, n_w - 0.5])
    ax_z.set_ylim([n_h - 0.5, -0.5])
    # y projection:
    ax_y.imshow(y_proj, interpolation='nearest', cmap=cm.gray, aspect=1)
    ax_y.set_xlim([-0.5, n_w-0.5])
    ax_y.set_ylim([n_slices-0.5, -0.5])
    # x projection:
    ax_x.imshow(x_proj, interpolation='nearest', cmap=cm.gray, aspect=1)
    ax_x.set_xlim([-0.5, n_slices-0.5])
    ax_x.set_ylim([n_h-0.5, -0.5])

    return fig, (ax_z, ax_y, ax_x)

def project_image_sequence(img_sequence, subfigwidth=3, ncol=5):
    dim = len(img_sequence.shape)
    assert dim == 4
    n_frames, n_slices, n_h, n_w = img_sequence.shape

    frames = range(0, n_frames)
    n_subfig = len(frames)

    # Initialize parameters
    nrow = int(math.ceil(float(n_subfig)/ncol))
    height_subfig = (n_h + n_slices)*np.float64(subfigwidth)/(n_w + n_slices)

    # Initialize the figure
    fig = plt.figure(figsize=(subfigwidth*ncol, height_subfig*nrow))

    # Initialize the layout
    gs_master = gridspec.GridSpec(nrow, ncol)
    gs_master.update(bottom=0, top=1, left=0, right=1)

    # Magic for every (sub-)gridspec:
    wspace = 0.05 # [in]
    hspace = wspace*float(n_w + n_slices)/(n_h + n_slices)

    axes = []
    for i in range(nrow):
        for j in range(ncol):
            ind = i*ncol + j
            if ind >= n_subfig:

            gs = gridspec.GridSpecFromSubplotSpec(
                2, 2,
                width_ratios=[n_w, n_slices],
                height_ratios=[n_h, n_slices],
                subplot_spec=gs_master[i, j],
                wspace=wspace, hspace=hspace,

            ax_z = plt.Subplot(fig, gs[0, 0])
            ax_y = plt.Subplot(fig, gs[1, 0], sharex=ax_z)
            ax_x = plt.Subplot(fig, gs[0, 1], sharey=ax_z)

                          axes=[ax_z, ax_y, ax_x])
            axes.append((ax_z, ax_y, ax_x))
    return fig, axes

if __name__ == "__main__":
    image_sequence = np.random.rand(5, 20, 30, 40)
    fig, axes = project_image_sequence(image_sequence)
    fig.savefig("min_example.png", bbox_inches='tight')
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: sequence_of_projections.png
Type: image/png
Size: 41734 bytes
Desc: not available
URL: <>