Image data

Still taking screenshots when you want illustrate e.g. what a synapse looks like in the EM? Pymaid can help you with that: Its tiles module lets you download, annotate and save image data from your CATMAID server programmatically.

Note

The pymaid.tiles module is not automatically imported when you import pymaid - it has to be loaded explicitly. If you want to use it, you will have to install one library first: imageio. In most cases pip install imageio should do the trick for you.

Loading images

Central to pymaid.tiles module is the pymaid.tiles.TileLoader class. It maps nm to pixel coordinates, downloads and stitches image tiles and renders e.g. nodes on the image. The module is still work in progress and I will add some wrappers around TileLoader to make it a bit easier to use in the future. For now, we will be doing it the hard way:

[1]:
import pymaid
from pymaid import tiles

rm = pymaid.CatmaidInstance('https://www.your.catmaid-server.org' ,
                            api_token='YOURTOKEN',
                            http_user='user', # omit if not required
                            http_password='password' # omit if not required
                           )
[2]:
# Get a neuron
n = pymaid.get_neuron(16)
# Pick a random synapse
center = n.connectors.sample(1).iloc[0][['x', 'y', 'z']].values
center
[2]:
array([379172, 146340, 184240])

We will cut a 5x5um window around that synapse. pymaid.tiles.LoadTiles expects a bounding box to crop: left, right, top, bottom, front and (optionally) back coordinates. In our case we will load only a single slice.

In general, coordinates can be in nanometers (default) or in pixels - see the coords parameter in TileLoader further down.

[3]:
min_co = center - 5000
max_co = center + 5000
bbox = [c for co in zip(min_co[:2], max_co[:2]) for c in co] + [center[2]]
bbox
[3]:
[374172, 384172, 141340, 151340, 184240]

Now we can generate the job - this doesn’t actually do anything other than trying to find the fastest image mirror. You need to provide a stack ID as your server might host more than a single version of the image data (e.g. different resolution or translation). To get the stack ID, click on ‘URL to this view’ in CATMAID: the stack ID will be in the URL as sid0.

[4]:
job = tiles.TileLoader(bbox, stack_id=5, coords='NM')
INFO  : Fastest image mirror: https://flyemdev.mrc-lmb.cam.ac.uk/fafb-tiles/ (pymaid)
INFO  : Estimated memory usage: 75.50 Mb (pymaid)

This starts the job, loads, stitches and crops the required EM image tiles.

[5]:
job.load_in_memory()

The image is saved as array in job.img:

[6]:
job.img.shape
[6]:
(2500, 2500, 1)

Plotting images

pymaid.tiles.TileLoader comes with a wrapper for matplotlib to plot the image:

[7]:
import matplotlib.pyplot as plt

ax = job.render_im(figsize=(12,12))
plt.show()
../_images/source_tiles_13_0.png

So how do we render nodes on top of this? Fortunately, pymaid.tiles.TileLoader has a function for this as well:

[8]:
# First render the image
ax = job.render_im(figsize=(12,12))

# Now add the nodes
job.render_nodes(ax, treenodes=True, connectors=False)

# Readjust the figure limits
ax.set_xlim(0,2500)
ax.set_ylim(0,2500)

plt.show()
../_images/source_tiles_15_0.png

We can do more fancy stuff with rendering nodes. See this example:

[9]:
# Get skeleton IDs of all uPNs
upn = pymaid.get_skids_by_annotation('uPN')

# Generate a random color for each uPN
from colorsys import hsv_to_rgb
cmap = {n: hsv_to_rgb(1/len(upn)*i, 1, 1) for i, n in enumerate(upn)}
INFO  : Looking for Annotation(s): uPN (pymaid)
INFO  : Found 149 skeletons with matching annotation(s) (pymaid)
[10]:
# Render image
ax = job.render_im(slider=False, figsize=(12,12))

# Add treenodes
job.render_nodes(ax,
                 treenodes=True,
                 connectors=True,
                 skid_include=upn, # show only uPNs
                 tn_color=cmap, # apply colormap
                 cn_kws={'s':500, # make connectors larger
                         'edgecolor':'w'} # make connectors white
                )

# Add 1um scalebar
job.scalebar(size=1000, ax=ax, label=False, line_kws={'color':'w', 'lw':5})

# Show
plt.show()
../_images/source_tiles_18_0.png

Load & save images

What if you only want to get the image data and save it without rendering? For this case, pymaid.tiles.TileLoader has a function that tries doing this in a memory efficient way : pymaid.tiles.TileLoader.load_and_save().

Let’s say we take our bounding box from above but want to download and save the 10 slices posterior to it. All we have to do is add an according z2 coordinate:

[11]:
# This assumes a 40nm z resolution
z2 = 184240 + 40 * 10
bbox = [374172, 384172, 141340, 151340, 184240, z2]

job = tiles.TileLoader(bbox, stack_id=5, coords='NM')
INFO  : Image mirror: https://flyemdev.mrc-lmb.cam.ac.uk/fafb-tiles/ (pymaid)
INFO  : Estimated memory usage for loading all images: 830.47 Mb (pymaid)

Note how the estimated memory usage is already quite high? Will still work but for larger cutouts, you might run into memory issues if you try loading all at once.

Instead, we can load the stitched images one slice at a time and save them straight way:

[12]:
job.load_and_save(filepath='/Users/philipps/Downloads/slices/')
[13]:
from IPython.display import Image
Image(filename='/Users/philipps/Downloads/slices/10.jpg')
[13]:
../_images/source_tiles_23_0.jpg