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.

Part 2: Shape analysis during gastrulation

2.1 Download endodermal selection

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.

We want now to analyze the shape of a few selected cells, those which will constitute the endoderm. We connect to MorphoNet, pick all cells we are interested in, apply a color selection to them. We then save this color selection with the name "endoderm", as shown in the video below.

We have seen how to manually create a selection from the MorphoNet online interface. Here we will download such a selection in order to start the analysis of endodermal cellular shapes.

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
In [5]:
import os,sys
sys.path.append('API')
from MorphoNet import MorphoNet #MorphoNet API

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

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

datasetname="Phallusia Mammillata gastrulation" 
mn.selectDataSetByName(datasetname) #Select the dataset of interest

endoderm=mn.getObjectsFromInfos('endoderm')#Get the selection done with in MorphoNet 
endodermal_cells={} #We create here a dictionary giving, for each time point, the list of IDs of endodermal cells at that time.
for e in endoderm:
    t=int(e.split(',')[0])
    end_at_t=endodermal_cells.get(t,[])
    end_at_t.append(int(e.split(',')[1]))
    endodermal_cells.update({int(t):end_at_t})
emmanuel.faure is connected to MorphoNet
 --> found dataset Phallusia Mammillata gastrulation with id 671 from 1 to 36 owned by 1 with type=1

Now that we know the ID of all endodermal cells at all timepoints, we can start analysizing their shapes. The following functions extract shape information from the 3D volumetric segmentation of each cell. Here in particular we calculate, for all endodermal cells, the area of their external surface and their main elongation through a principal components analysis of their volume distribution.

In [6]:
import numpy as np
from scipy import ndimage as nd
from tools import read_3D_image

#Since these segmented images are usually quite big, when analyzing single cells one can cut the image domain to a box containing the cell of interest
def bound_boxes_cells(im):
    from scipy import ndimage as nd
    bboxes=nd.find_objects(im)
    cel=np.unique(im)
    cells=cel[1:]
    return (bboxes,cells)


#The next two functions are used to extract cells surfaces
def __slices_dilation(slices, maximum=[np.inf, np.inf, np.inf]):
    return tuple([slice(max(0, s.start-1), min(s.stop+1, maximum[i])) for i, s in enumerate(slices)])

def slices_dilation(slices, maximum=[np.inf, np.inf, np.inf], iterations=1):
    for i in range(iterations):
        slices=__slices_dilation(slices, maximum)
    return slices   

#Principal Component Analysis of datasets
def PCA(data):
    data -= np.mean(data,axis=0)
    R = np.cov(data, rowvar=False)
    evals, evecs = np.linalg.eigh(R)
    evecs=np.transpose(evecs)
    evecs=[evecs[2],evecs[1],evecs[0]]
    evals=[evals[2],evals[1],evals[0]]
    return(evecs,evals)

#This function returns shape information of a given cell c: cell elongation and area of external surface.
#We characterize cell elongations by the ratio (l1-l_2)/l_1, l_1 (l_2) being the first (second) principal value of the cell's PCA.
def find_cell_shape(c, bboxes, image):
    bb=slices_dilation(bboxes[c-1], iterations=2)
    orig=[int(bb[0].start),int(bb[1].start),int(bb[2].start)]
    tmp=image[bb]
    w=np.where(tmp==c)
    points=[(w[0][k],w[1][k],w[2][k]) for k in range(0,len(w[0]))] #cell points
    pvecs,pvals=PCA(points) #PCA of cell points
    r=(nd.binary_dilation(tmp==c) & (tmp!=c))
    w=np.where(r==True) #cell contour
    ext_surfs=[]
    for k in range(0,len(w[0])):
        lab=tmp[w[0][k],w[1][k],w[2][k]]
        if lab in [0,1]:
            ext_surfs.append([w[0][k],w[1][k],w[2][k]]) #external surface
    return pvecs,pvals,len(ext_surfs)

#We can now perform the actual shape analysis.
external_areas={}
ratio_main_elongations={}
for t in range(begin,end+1):
    print "Analyzing time",t
    endodermal_cells_at_t=endodermal_cells[t]
    img=read_3D_image(t,path_to_images)
    bboxes,cells=bound_boxes_cells(img)
    for cell in endodermal_cells_at_t: 
        pvecs,pvals,ext_area=find_cell_shape(cell, bboxes, img)
        ratio_main_el=1.0*(pvals[0]-pvals[1])/pvals[0]
        external_areas.update({tuple([t,cell]):ext_area}) #cells here are encoded as a tuple (t,cell_id), which is practical for MorphoNet upload
        ratio_main_elongations.update({tuple([t,cell]):ratio_main_el})

#We can also calculate the time variation of these properties. To do so, we need the lineage tree.
from tools import loadPKL
properties=loadPKL("DATA/ASCIDIAN/A1_properties.pkl")
lineage_tree=properties[0]

#Variation of a property on cell c at time t is: property[t,c]-property[t-1,c]
external_areas_var={}
ratio_main_elongations_var={}

for x in external_areas.keys():
    if x[0]<end:
        cell=x[0]*10**4+x[1] #remember that cells in lineage tree are encoded as t*10**4+cell_id
        cell_at_next_timepoint=tuple([lineage_tree[cell][0]/10**4,lineage_tree[cell][0]%10**4])
        external_areas_var.update({cell_at_next_timepoint:external_areas[cell_at_next_timepoint]-external_areas[x]})
        ratio_main_elongations_var.update({cell_at_next_timepoint:ratio_main_elongations[cell_at_next_timepoint]-ratio_main_elongations[x]})
Analyzing time 1
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t001.tiff
Analyzing time 2
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t002.tiff
Analyzing time 3
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t003.tiff
Analyzing time 4
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t004.tiff
Analyzing time 5
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t005.tiff
Analyzing time 6
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t006.tiff
Analyzing time 7
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t007.tiff
Analyzing time 8
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t008.tiff
Analyzing time 9
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t009.tiff
Analyzing time 10
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t010.tiff
Analyzing time 11
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t011.tiff
Analyzing time 12
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t012.tiff
Analyzing time 13
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t013.tiff
Analyzing time 14
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t014.tiff
Analyzing time 15
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t015.tiff
Analyzing time 16
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t016.tiff
Analyzing time 17
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t017.tiff
Analyzing time 18
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t018.tiff
Analyzing time 19
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t019.tiff
Analyzing time 20
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t020.tiff
Analyzing time 21
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t021.tiff
Analyzing time 22
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t022.tiff
Analyzing time 23
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t023.tiff
Analyzing time 24
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t024.tiff
Analyzing time 25
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t025.tiff
Analyzing time 26
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t026.tiff
Analyzing time 27
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t027.tiff
Analyzing time 28
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t028.tiff
Analyzing time 29
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t029.tiff
Analyzing time 30
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t030.tiff
Analyzing time 31
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t031.tiff
Analyzing time 32
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t032.tiff
Analyzing time 33
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t033.tiff
Analyzing time 34
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t034.tiff
Analyzing time 35
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t035.tiff
Analyzing time 36
 -> read DATA/ASCIDIAN/SEGMENTED/Segmented_t036.tiff

We now have two dictionaries containing the desired shape information. All is left to do is to upload these tracks to MorphoNet

In [8]:
external_area_inf="type:float"+"\n" #External Areas information. This is a quantitative information
external_area_var_inf="type:float"+"\n" #External Area variation information. This is a quantitative information
ratio_main_elongations_inf="type:float"+"\n" #Elongation ratio information. This is a quantitative information
ratio_main_elongations_var_inf="type:float"+"\n" #Variation of elongation ratio information. This is a quantitative information

for x in external_areas.keys():
    external_area_inf+=str(x[0])+","+str(x[1])+":"+str(external_areas[x])+"\n"
    ratio_main_elongations_inf+=str(x[0])+","+str(x[1])+":"+str(ratio_main_elongations[x])+"\n"
for x in external_areas_var.keys():
    external_area_var_inf+=str(x[0])+","+str(x[1])+":"+str(external_areas_var[x])+"\n"
    ratio_main_elongations_var_inf+=str(x[0])+","+str(x[1])+":"+str(ratio_main_elongations_var[x])+"\n"

#We can now upload each information with a specific name to the MorphoNet database
mn.uploadInfos("External Area",external_area_inf)
mn.uploadInfos("Variation External Area",external_area_var_inf)
mn.uploadInfos("Elongation Ratio",ratio_main_elongations_inf)
mn.uploadInfos("Variation Elongation Ratio",ratio_main_elongations_var_inf)
 --> External Area uploaded (with id 3211)
 --> Variation External Area uploaded (with id 3212)
 --> Elongation Ratio uploaded (with id 3213)
 --> Variation Elongation Ratio uploaded (with id 3214)
Out[8]:
3214

We can now visualize in MorphoNet the uploaded information as heat maps projected onto endodermal cells