MorphoNet

Tutorial 1: Investigation on shape changes of endodermal cells during gastrulation in Phallusia mammillata embryos

This tutorial shows, step by step, how to employ Python and MorphoNet to visualize and analyze complex 3D+time dynamics. It guides users from segmented data to final results.

Requirements

For this tutorial, you need python 2.7 and several libaries : scipy , numpy, scikit-image, vtk, httplib,bz2,hashlib, json, requests

Part 1: Dataset creation

1.1 Data acquisition and segmentation

Three dimentional data of live embryonic development at single-cell resolution were taken through light-sheet microscopy and segmented using the automatic segmentation pipeline ASTEC, as explained in Guignard, Fiuza et al., bioRxiv 238741 (2017).

In this tutorial we take as example the data corresponding to the embryo referred to as A1 in the paper.

Data for this embryo can be found at: http://www.crbm.cnrs.fr/Astec/Datasets/A1.zip. This zip file contains 180 segmented time steps and the associated quantitative information.

You can also directly download the ascidian data associated to all MorphoNet tutorials http://www.morphonet.org/Tutorials/MorphoNet_DATA.zip

1.2 Data import, mesh creation and upload to MorphoNet

Segmented images are in standard .tiff format. To import them in Python we use the library ImageHandling. Each image corresponds to the 3D reconstruction of the whole embryo at a given time. Two consecutive images are separated by a timegap of two minutes. Segmented images are divided into elementary objects, each identified by its corresponding numeric ID. Each cell is an object with an ID>1. The background, important to identify external the embryonic surface, corresponds to the ID=1.

In [1]:
path_to_images="DATA/ASCIDIAN/SEGMENTED/" #this is the directory where segmented images are stored
begin=1 #first timepoint of dataset
end=36 #last timepoint of dataset

The output of f_img is a 3D matrix, each dimension of which corresponds to one of the spatial dimensions x,y,z of the corresponding data. Each entry of the matrix at position x,y,z is a integer number >0, identifying which object is present at the specific position.

Once imported, these volumetric images can be converted to suface meshes through the Meshing library provided together with MorphoNet. Surface meshes are the standard format used by MorphoNet, and can then be directly uploaded to the MorphoNet database via its API. We begin by creating a new dataset.

In [4]:
import os,sys
sys.path.append('API')
from MorphoNet import MorphoNet #MorphoNet API

#MorphoNet Login credentials
mn_login="your.login"
mn_password="your.password"

#Connection to MorphoNet database
mn=MorphoNet(mn_login,mn_password)
mn.connect()

# Creation of a new dataset
dataset_name="Phallusia Mammillata gastrulation"
mn.selectDataSetByName(dataset_name)
#A specific dataset is selected here. All future API interactions will concern this database, unless a new one is selected later on
if mn.id_dataset==-1: #If the dataset does not yet exist
    mn.createDataSet(dataset_name,begin,end)  #Creation of a dataset containing end-begin timepoints, 
else: #If the dataset already exists
    mn.clearDataSet() #Clears all meshes and infos associated with this dataset
emmanuel.faure is connected to MorphoNet
 --> found dataset Phallusia Mammillata gastrulation with id 671 from 1 to 36 owned by 1 with type=1
 --> dataset cleared

Now that a new dataset is created, we will transform each segmented image into an .obj containing the surface mesh and the objects identities, following the standard MorphoNet format. One .obj file is created for each timepoint and uploaded to the MorphoNet database to the new dataset we just created.

In [5]:
from tools import convertVolumetoOBJ, read_3D_image
reduce_factor=4 #Reduce the quality of the mesh but also the computation time
for t in range(begin,end+1):
    segmented_img=read_3D_image(t,path_to_images)
    reduce_img=segmented_img[::reduce_factor,::reduce_factor,::reduce_factor] 
    obj=convertVolumetoOBJ(reduce_img,factor_scale=reduce_factor,background_threshold=1,prefix=str(t)+",",verbose=1)
    #Upload Meshes for timepoint t to selected dataset 
    mn.uploadMesh(t,obj)
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t001.tiff
-> Found 66 objects
 --> meshes at time point 1 uploaded ( with id 116906 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t002.tiff
-> Found 64 objects
 --> meshes at time point 2 uploaded ( with id 116907 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t003.tiff
-> Found 64 objects
 --> meshes at time point 3 uploaded ( with id 116908 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t004.tiff
-> Found 64 objects
 --> meshes at time point 4 uploaded ( with id 116909 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t005.tiff
-> Found 64 objects
 --> meshes at time point 5 uploaded ( with id 116910 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t006.tiff
-> Found 64 objects
 --> meshes at time point 6 uploaded ( with id 116911 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t007.tiff
-> Found 64 objects
 --> meshes at time point 7 uploaded ( with id 116912 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t008.tiff
-> Found 64 objects
 --> meshes at time point 8 uploaded ( with id 116913 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t009.tiff
-> Found 65 objects
 --> meshes at time point 9 uploaded ( with id 116915 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t010.tiff
-> Found 66 objects
 --> meshes at time point 10 uploaded ( with id 116916 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t011.tiff
-> Found 72 objects
 --> meshes at time point 11 uploaded ( with id 116919 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t012.tiff
-> Found 72 objects
 --> meshes at time point 12 uploaded ( with id 116920 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t013.tiff
-> Found 77 objects
 --> meshes at time point 13 uploaded ( with id 116921 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t014.tiff
-> Found 77 objects
 --> meshes at time point 14 uploaded ( with id 116922 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t015.tiff
-> Found 77 objects
 --> meshes at time point 15 uploaded ( with id 116923 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t016.tiff
-> Found 77 objects
 --> meshes at time point 16 uploaded ( with id 116924 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t017.tiff
-> Found 77 objects
 --> meshes at time point 17 uploaded ( with id 116925 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t018.tiff
-> Found 77 objects
 --> meshes at time point 18 uploaded ( with id 116927 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t019.tiff
-> Found 78 objects
 --> meshes at time point 19 uploaded ( with id 116928 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t020.tiff
-> Found 80 objects
 --> meshes at time point 20 uploaded ( with id 116929 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t021.tiff
-> Found 89 objects
 --> meshes at time point 21 uploaded ( with id 116930 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t022.tiff
-> Found 102 objects
 --> meshes at time point 22 uploaded ( with id 116931 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t023.tiff
-> Found 105 objects
 --> meshes at time point 23 uploaded ( with id 116932 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t024.tiff
-> Found 112 objects
 --> meshes at time point 24 uploaded ( with id 116934 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t025.tiff
-> Found 112 objects
 --> meshes at time point 25 uploaded ( with id 116935 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t026.tiff
-> Found 112 objects
 --> meshes at time point 26 uploaded ( with id 116937 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t027.tiff
-> Found 112 objects
 --> meshes at time point 27 uploaded ( with id 116938 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t028.tiff
-> Found 112 objects
 --> meshes at time point 28 uploaded ( with id 116940 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t029.tiff
-> Found 112 objects
 --> meshes at time point 29 uploaded ( with id 116941 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t030.tiff
-> Found 112 objects
 --> meshes at time point 30 uploaded ( with id 116942 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t031.tiff
-> Found 112 objects
 --> meshes at time point 31 uploaded ( with id 116943 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t032.tiff
-> Found 112 objects
 --> meshes at time point 32 uploaded ( with id 116944 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t033.tiff
-> Found 112 objects
 --> meshes at time point 33 uploaded ( with id 116945 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t034.tiff
-> Found 112 objects
 --> meshes at time point 34 uploaded ( with id 116946 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t035.tiff
-> Found 112 objects
 --> meshes at time point 35 uploaded ( with id 116948 )
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t036.tiff
-> Found 113 objects
 --> meshes at time point 36 uploaded ( with id 116949 )

1.3 Information import and upload to MorphoNet

The dataset now contains a time series of surface meshes divided in objects. We can now upload several morphological tracks by using the standard MorphoNet format for morphological information. Information such as cellular lineage tree and cell names are provided for these data under the form of Pickle files, which we import and convert to MorphoNet format.

In [6]:
from tools import loadPKL
properties=loadPKL("DATA/ASCIDIAN/A1_properties.pkl")
lineage_tree=properties[0]
cell_names=properties[1]['Names'][0]
cell_vols=properties[1]['volumes_information'][0]

These files are python dictionaries, with cells as keys and corresponding information as values. Cells are given here as t*10^4+cell_id, so for instance cell 45 at time 11 corresponds to key 110045. We will now transform these files into the MorphoNet information format.

In [11]:
comments="#Dataset "+dataset_name+"\n" 
comments+="#Upload by "+mn_login+"\n" 
from tools import get_inf,get_objname
##### Lineage Tree information. This is a time relation information
lineage_inf=comments+"type:time"+"\n" 
for x in lineage_tree.keys():
    for y in lineage_tree[x]: #daugthers of cell x or snapshot of cell x at next timepoint
        lineage_inf+=get_objname(x)+":"+get_objname(y)+"\n"
mn.uploadInfos("Lineage",lineage_inf) #We can now upload the cell lineage to the MorphoNet database

##### Names information. This is a qualitative information
mn.uploadInfos("Names",get_inf(cell_names,"string",comments=comments))

##### Volutiom information. This is a quantitative information 
mn.uploadInfos("Volumes",get_inf(cell_vols,"float",comments=comments))    
 --> Lineage uploaded (with id 3207)
 --> Names uploaded (with id 3208)
 --> Volumes uploaded (with id 3209)
Out[11]:
3209

You can now find your dataset at http://www.morphonet.org