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:
http://stackoverflow.com/questions/34124267/visualize-3d-and-4d-image-data-in-python

My full code is also available on Github:
https://github.com/DSamuylov/imagend

Minimal code to reproduce the problem is bellow.

Thank you for your help!

Best regards,
Denis

import mathimport numpy as npimport matplotlib.cm 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)
    else:
        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])
    ax_z.axis('off')
    # 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])
    ax_y.axis('off')
    # 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])
    ax_x.axis('off')

    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:
                break

            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_z.set_title('frame={}'.format(frames[ind]))
            ax_y = plt.Subplot(fig, gs[1, 0], sharex=ax_z)
            ax_x = plt.Subplot(fig, gs[0, 1], sharey=ax_z)

            project_image_stack(img_sequence[frames[ind]],
                          axes=[ax_z, ax_y, ax_x])
            fig.add_subplot(ax_z)
            fig.add_subplot(ax_y)
            fig.add_subplot(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: <http://mail.python.org/pipermail/matplotlib-users/attachments/20151207/b70efaef/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: sequence_of_projections.png
Type: image/png
Size: 41734 bytes
Desc: not available
URL: <http://mail.python.org/pipermail/matplotlib-users/attachments/20151207/b70efaef/attachment-0001.png>