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 :class:`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 :class:`~pymaid.tiles.TileLoader` to make it a bit easier to use in the future. For now, we will be doing it the hard way: .. code:: ipython3 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 ) .. code:: ipython3 # Get a neuron n = pymaid.get_neuron(16) # Pick a random synapse center = n.connectors.sample(1).iloc[0][['x', 'y', 'z']].values center .. parsed-literal:: array([379172, 146340, 184240]) We will cut a 5x5um window around that synapse. :class:`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. .. code:: ipython3 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 .. parsed-literal:: [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``. .. code:: ipython3 job = tiles.TileLoader(bbox, stack_id=5, coords='NM') .. parsed-literal:: 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. .. code:: ipython3 job.load_in_memory() The image is saved as array in ``job.img``: .. code:: ipython3 job.img.shape .. parsed-literal:: (2500, 2500, 1) Plotting images =============== :class:`pymaid.tiles.TileLoader` comes with a wrapper for matplotlib to plot the image: .. code:: ipython3 import matplotlib.pyplot as plt ax = job.render_im(figsize=(12,12)) plt.show() .. image:: tiles_files/tiles_13_0.png So how do we render nodes on top of this? Fortunately, :class:`pymaid.tiles.TileLoader` has a function for this as well: .. code:: ipython3 # 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() .. image:: tiles_files/tiles_15_0.png We can do more fancy stuff with rendering nodes. See this example: .. code:: ipython3 # 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)} .. parsed-literal:: INFO : Looking for Annotation(s): uPN (pymaid) INFO : Found 149 skeletons with matching annotation(s) (pymaid) .. code:: ipython3 # 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() .. image:: tiles_files/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, :class:`pymaid.tiles.TileLoader` has a function that tries doing this in a memory efficient way : :func:`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: .. code:: ipython3 # 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') .. parsed-literal:: 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: .. code:: ipython3 job.load_and_save(filepath='/Users/philipps/Downloads/slices/') .. parsed-literal:: HBox(children=(FloatProgress(value=0.0, description='Stitching', max=11.0, style=ProgressStyle(description_wid… .. parsed-literal:: HBox(children=(FloatProgress(value=0.0, description='Loading tiles', max=99.0, style=ProgressStyle(description… .. code:: ipython3 from IPython.display import Image Image(filename='/Users/philipps/Downloads/slices/10.jpg') .. image:: tiles_files/tiles_23_0.jpg