Welcome to pyTFM’s documentation!¶
pyTFM is a python package that allows you to analyze force generation and stresses in cells, cell colonies and confluent cell layers growing on a 2 dimensional surface. This package implements the procedures of Traction Force Microscopy and Monolayer Stress Microscopy. In addition to the standard measures for stress and force generation, it also includes the line tension, a measure for the force transfer exclusively across cell-cell boundaries. pyTFM includes an addon for the image annotation tool clickpoints allowing you to quickly analyze and vizualize large datasets.
Installation¶
It is recommended to use this package with the Anaconda Distribution.
pyTFM can be installed in several ways. The most straight forward way is to download the package from github, unzip the files, open a terminal and navigate into the “pyTFM-master”. Depending on how you unzip there you might need to go to “pyTFM-master/pyTFM-master”. In this folder you should see a file called “setup.py”. Next, install the package with pip:
pip install -e .
Pip is included in the Anaconda Distribution and should be available as a command line program out of the box. The -e setting will enable you to edit package files, and the “.” signifes that the “setup.py” file is located in the folder that you are currently in.
You can also download and install the package in one step using the command line program “git”. If you use the Anaconda Distribution, “git” can be installed with the command
conda install git
Now you can install pyTFM directly with the command
pip install git+https://github.com/fabrylab/pyTFM.git
This will download the script file into your Anaconda subdirectory, for example to “anaconda3/lib/python3.7/site-packages/pyTFM” This package includes an addon for the image display and annotation tool clickpoints. Clickpoints is installed automatically if you follow the steps outline below. However, if you want to access clickpoints via the “open with” program option for image files, you have to use the command:
clickpoints register
in the terminal.
Hint
Execute the command “clickpoints register” from the terminal to add clickpoints to the “open width” menu for image files. You can find detailed information for the usage of clickpoints here.
Dependencies¶
The following packages will be installed automatically if necessary: numpy, cython, scipy , scikit-image, matplotlib, tqdm, solidspy, clickpoints with a version higher then 1.9.0, OpenPIV version 0.20.8. Note that Clickpoints versions below 1.9.0 will fail to identify the pyTFM addon. Also note that OpenPIV is still in developement, meaning that more recent versions of OpenPIV might not be compatible with pyTFM. Currently, if you are on windows, openpiv is installed from a compiled .whl file included in the pyTFM packge. If you want to install openpiv on your own you are likely to need the Microsoft Visual C++ build tools.
Introduction to Traction Force Microscopy and Monolayer Stress Microscopy¶
Traction Force Microscopy and Monolayer Stress Microscopy are methods to measure the force generation and internal forces of cells and cell colonies.
A typical experiment starts by seeding cells on a substrate containing small fluorescence labeled beads. The cells adhere to the substrate, start generating forces and consequently cause deformations in the substrate. These deformations can be measured by tracking the labeled beads. In practice the beads are imaged two times: Once when cells are attached to the substrate, and once when the cells are removed from the substrate. In the first image the substrate is strained by the cells, while the second image shows the completely relaxed substrate.
The deformations in the substrate can be used to calculate the traction forces that acted on the substrate surface. This is done by Fourier Transformed Traction Force Microscopy (TFM) [1]. Any force that is applied from the cells to the substrate surface must be balanced by counteracting force inside of the cells. Forces inside of materials are described by stress.
A cell that generates two opposing forces at its ends, experiences a high stress between the points of force generation. The stress inside of cells and cell colonies is recovered by Monolayer Stress Microscopy (MSM) [2]. In MSM the cells are modeled as a 2-dimensional sheet. Following the argument of force-balance, the traction forces calculated from TFM, with the opposite signs, are applied to the cell sheet. Then, the stresses in the cell sheet are calculated with 2-dimensional Finite Elements Methods
[1] | Traction fields, moments, and strain energy that cells exert on their surroundings James P. Butler, Iva Marija Tolić-Norrelykke, Ben Fabry, and Jeffrey J. Fredberg Am J Physiol Cell Physiol 282: C595–C605, (2002) |
[2] | Monolayer Stress Microscopy: Limitations, Artifacts, and Accuracy of Recovered Intercellular Stresses Dhananjay T. Tambe, Ugo Croutelle, Xavier Trepat, Chan Young Park, Jae Hun Kim, Emil Millet, James P. Butler, Jeffrey J. Fredberg PLOS ONE 8(2): e55172 (2013) |
Tutorials¶
Here you can find tutorials on how to use the pyTFM clickpoints addon, how to work on clickpoints databases without using the GUI, and how to use pyTFM on its own.
Analyzing Cell Colonies with Clickpoints¶
About this Tutorial¶
Using the pyTFM clickpoints addon requires a complete installation of clickpoints. If you have set up clickpoints correctly, you can open images by right clicking on the image files and select “open with clickpoints”.
You can find a small example data set, which is used during this tutorial, here. The data is in the subfolder “clickpoints_tutorial”. This data set contains raw data for 2 types of cell colonies: In one group a critical cytoskeletal protein has been knocked out. We will compare these cell colonies to a set of wildtype colonies. The raw data, in the form of images, is contained in the subfolders “WT” and “KO”. All output files, databases and plots as you should produce them in the course of this tutorial are stored in the folders “KO_analyzed”, “WT_analyzed” and “plots”. Use these folders to check if your analysis was correct.
The Data¶
As you can see in Fig. 1, there are 6 images for each colony type. This corresponds to two field of views for each wildtype and KO. For each field of view there are 3 images. One image (e.g. 03bf_before.tif) shows the colony and the boundaries between cells. In this case the image shows fluorescence stained cell membranes. The other two images show beads that are embedded in the substrate that the cells lie on. One image was recorded before the cells were removed (03before.tif) and the other was recorded after the cells were removed (03after.tif). The number in front of the filename (“03”, “10” and so on) indicates which field of view that image belongs to.
Opening Clickpoints and sorting Images¶
The first step to analyze the data is to create a clickpoints database, in which the images are identified correctly, concerning their type (whether it’s an image of the cells or an image of the beads before or after cell removal) and concerning the field of view they belong to. We are going to start with the wildtype data set. To open a database simple right click on an image and select “open with” –> “clickpoints”. The option to open with clickpoints might also be visible directly after you right clicked.
Clickpoints sorts images in two dimensions: Frames and layers. The frames are displayed in the bar at the bottom. You can skip from frame to frame using the left and right arrows on your keyboard. Layers can be changed with the “Page Up” and “Page Down” keys. When you open the database, you will notice that there is only one layer and every image is sorted into a new frame. Our goal is to sort each field of view into one frame, with three layers per frame, each representing one type of image. In order to do this you need to open the pyTFM addon and open the “select image” menu. Follow the steps described in Fig. 3.

A: Open the addon-browser in clickpoints. A new window, with all available addons will open. B: Activate the pyTFM addon by selecting pyTFM and clicking the “Activate” button. A window notifying you that the addon has been loaded successfully will appear. After you press “OK” a new icon will appear in the clickpoints main window to the right of the addon-browser button. C: Click on this button to open the pyTFM addon. D: Finally, open the menu to select images by pressing the “select images” button.
The “image selection” menu allows you to do three things: You can select where images are located and how they are classified. You can also set an output folder, where the database file and all analysis results will be saved and you can choose a name for the database. As mentioned above, the analysis requires three types of images. For each type you can select a folder (left hand side) and a regular expression that identifies the image type from the image filename (right hand side).
The default identifiers fit to the example data set, meaning that for now and in the future, if you are using the same naming scheme for your images, you can leave the identifiers as they are.
Note
Details on identifying images
The fields “‘after’ image identifier” and “‘before’ image identifier” are used to identify images of the substrate after and before relaxation. The field “cell image identifier” is used to identify images that show the cells or cell membranes. Finally, the “frame identifier” is used to identify the field of view each image belongs to. This must point to a unique part of the image filename, which is not limited to numbers. The part must be specifically enclosed by brackets “()”. Note that the extension (“.png”,”.tiff”, “.jpeg” …) must not be included in the identifiers.
Regular expressions are the standard way to find patterns in texts. For example, it allows you to identify numbers of certain length, groups of characters or the beginning and end of a text. You find more information on regular expressions here. Some useful expressions are listed in the table below:
search pattern | meaning |
---|---|
after | all files with “after” in the filename |
^after | all files with “after” at the beginning of the filename |
after$ | all files with “after” at the end of the filename |
* | all files blank space also finds all files |
^(d{1-4}) | up to 4 numbers at beginning of the filename |
(d{1-4}) | up to 4 consecutive numbers anywhere in the filename |
(d{1-4})$ | up to 4 numbers at end of the filename |
Once you have entered identifiers for image types, frames, the output folder and the database name press the “collect images” button. You should see something like this:
Make sure your database didn’t contain any masks that you don’t want to delete. If you just opened the database from new images, you can press “Yes”. The path to the images that are sorted into the database, the type of the images (layer) and the field of view of the images (frame) are printed to the console. Make sure all images are sorted correctly. The program has generated a new clickpoints database file. Your currently opened clickpoints window updates automatically. You can close the “image selection” window now.
Correction of Stage Drift¶
Most often you will not manage to record exactly the same field of view when imaging the beads before and after cell removal. This will have an effect when you calculate the deformation field. In particular you will see your whole deformation field showing a bias to one direction. You can (and need to) correct this with the “correct drift” function (Fig. 5). This function will cut out the common field of view of the images of the beads before and after cell removal and shift one of the images with subpixel accuracy to match the other. The images of the cells will be cropped in the same way as the images of the beads.
First, go to the main pyTFM addon window and select “all frames” or “current frame” in the “apply to” option, depending on which frames you want to apply the drift correction to. Then press the “correct drift” button. You will see the drift in pixels in x and y direction printed to the console (Fig. 5, bottom).
Warning
This operation will permanently change you image files.
Setting Parameters¶
Lets continue with calculating the deformation and traction field. Go to the pyTFM addon window (Fig. 6).
In this window you have to set the mechanical parameters of the substrate (“Youngs modulus” and “Poisson’s ratio”), the height of the substrate (“gel height”) and the pixel size (“pixel size”). Then you have to set two more parameters for the calculation of the deformation field. The deformation field is calculated with particle image velocimetry. This method essentially cuts out square shaped patches from the image of the beads before cell removal, places them on the image of beads after cell removal and checks how well they fit together. The vector from the original position of the patch to the position where the patch fits best in the image of beads after cell removal is the displacement vector. This is done for many positions to generate the complete displacement field.
You can control two things: the size of the patch that is cut out of the image of the beads after cell removal (with the parameter “PIV window size”) and the resolution of the resulting displacement field (with the parameter “PIV overlap”). A window size that is to large will blur the displacement field while a window size that is to small will introduce noise in the displacement field. As a rule of thumb the window size should be roughly 7 times the bead diameter - you should however try a few values and check which window size yields a smooth yet accurate deformation field.
Note
You can measure the beads diameter directly in clickpoints using another addon: The “Measure Tool”. It can be opened just like pyTFM from the addon browser
The “PIV overlap” mainly controls the resolution of the resulting displacement field and must be smaller than the “PIV window size” but at least half of the “PIV window size”. You need a high resolution for analyzing stress. In this step the area of cells should at least contain 1000 pixels. There will be a warning printed to the console and the output text file if this is not the case. However, if you are not calculating stresses, you can save a lot of calculation time by choosing a “PIV overlap” closer to half of the “PIV window size”. This is especially useful when you are for example trying out different window sizes or explore the influence of the gel height.
For this tutorial you can keep all parameters at their default value. If you are in a hurry you could set the “PIV window size” as low as 15 µm, and still obtain reasonable results.
Calculating Traction and Deformation Fields¶
Once you have set all parameters you can start the calculation: Use the tick boxes in the upper right to select which part of the analysis you want to perform. For now, we are gonna select only “deformation” and “traction forces”. Then use the “apply to” option to choose whether all frames should be analyzed or only the frame that you are currently viewing. Your window should now look like Fig. 6. Finally press “start” in the upper left to begin the analysis. With the default parameters this takes about 5 minutes per frame. “calculation complete” is printed to the console once all frames have been analyzed.
The traction and deformation fields are added to the database as new layers. Switch to these layers using the “Page Up” key on your keyboard. Traction and deformation for the first frame in the wildtype data should look like this:
If you do not see the display tool and mask names (“Cell Boundary”, “Tractions”) on the right press F2.
Quantifying Force Generation¶
Force generation of the cell colony is quantified with the strain energy and the contractility. To avoid influence of nearby cells or noise, you have to select all tractions that are generated specifically by the cells that you are analyzing. You can do this by drawing a mask in clickpoints. In the top right of the clickpoints window you should see a set of tools to draw mask and two preset types of masks. If you don’t see these tools, press F2.
Hint
Tips for masks in clickpoints.
Select a mask and use the brush tool to draw it. You can
increase and decrease the size of the brush with the “+” and “-” keys. If you want to
erase a part of a mask use the eraser tool
. Additionally you can fill holes in your mask with
the bucket tool
. Mask types cannot overlap, which means that one mask type is erased when you
paint over it with another mask type. Sometimes you will have a hard time seeing things that are covered by
a mask. Press “i” and “o” to decrease and increase the transparency of the mask.
The mask type used to calculate strain energy and contractility is called “Tractions”. Select this mask and draw a circle around all tractions that you think originate from the cell colony. Typically, the area you encircle is large than the cell colony itself. It’s not a big deal if your selection is a bit to big, but you should make sure not to include tractions that do not originate from the cell colony. You don’t need to fill the area you have encircle; This is done automatically. However, if you see the “no mask found in frame ..” warning message in the console, you should first make sure that there is no gap in the circle that you drew. I drew the mask like this:
You could now press start again, and the program would generate a text file listing the contractility and strain energy for all frames. In order to be a bit more organized and get all results in one text file, we will first prepare to analyze stresses in the cell sheet at the same time.
Measuring Stresses, the Cell-Cell Force Transfer, counting Cells and measuring the Colony Area¶
Cellular stresses are calculated by modelling the cell colony as a 2 dimensional sheet and applying the traction forces that we have just calculated to it. Due to inaccuracies in the traction force calculation, namely that some forces are predicted to originate from outside of the cell sheet, the cell colony is modeled as a an area slightly extending beyond the actual cell colony edge. We have shown that stresses are calculated with the highest accuracy if this area covers all cell-generated tractions but does not extend significantly further than that. When in doubt, it is better to choose the area larger rather then smaller.
pyTFM uses the area that you just marked with the mask “Tractions” to model the cell colony. Stresses and cell-cell force transfer (quantified by the line tension) are however evaluated strictly on the inside of the cell colony. Thus you have to the edge of the cell colony to compute average stresses. To quantify the line tension, you have to also mark the internal cell-cell boundaries. Both is done with the Mask “Cell Boundary”; the program automatically distinguishes between internal cell-cell boundaries and the colony edge.
In the main window of clickpoints switch to the image showing the cell membrane using the the “Page Up” or “Page Down” key, select the mask “Cell Boundary” and mark all cell membranes.
Hint
Press F2 and use the controls (see below) in the bottom right to adjust the contrast of the image. This might help you to see the membrane staining better.
Use a thin brush and make sure that there are no unintentional gaps. Also mark the outer edge of the colony. I drew the mask like this:
Hint
If there are a large number of cell boundaries, you can try the automatic segmentation tool of pyTFM. Press the segmentation button in the main window. The slider next to “segment membrane” allows you to control the segmentation sensitivity. The mask displayed in the clickpoints window will automatically be updated when you select a new value. You can apply this segmentation threshold to all frames by pressing the “segment membrane” button. Note however that this segmentation algorithm is rather primitive; the results may be poor, strongly depend on the quality of your images and will usually need some manual revision.
Once you have drawn all masks in all frames you are ready to start the calculation. Go to the pyTFM addon window, tick the check boxes for “stress analysis” and “force generation”, make sure you have set “apply to” to “all frames”, untick the “deformation” and traction forces” boxes and press start. The calculation should take up to 5 minutes.
After the calculation is complete two new plots will be added to the database. The first will show the mean normal stress in the cell colony and the second will show the line tension along all cell-cell borders. The outer edge of the cell colony is marked in grey. These lines are not used in the calculation.
Note
A few notes on the calculation of stresses. The average stresses (average mean normal and average shear stress) and the coefficient of variation of these stresses is calculated by averaging over the true area of the cell colony, marked with the mask “membrane”. The mean normal stress should be high in areas where strong forces oppose each other. This can be seen in Fig. 11. Likewise, the line tension is high if strong forces oppose each other across the line. A high mean normal stress does not necessarily indicate a high line tension. It is better to look at the traction forces, when checking if the values for the line tension make sense.
Understanding the Output File¶
Every time you press start the program creates a text file “out.text” in the output folder. If such a file already exists, the text file is named out0.txt, out1.txt and so on. The output starts with a header containing important parameters of the calculation (Fig. 12). This is followed by a section containing all results. Each line has 4 to 6 tab-delimited columns, containing the frame, the id of the object in the frame (if you analyze multiple cells or cell colonies in this frame), the name of the quantity, the value of the quantity and optionally the unit of the quantity and also optionally a warning.
Warnings such as “mask was cut close to image edge” and “small FEM grid” should not be ignored.
Plotting the Results¶
Repeat the same analysis for the KO data set. Once you have the output text files for both data sets you could go ahead and use any tool of your choosing to read the files and plot the important quantities. Of course the best tool to do so is python: pyTFM provides its own python functions to read and plot data. The following code is also contained in the file “clickpoints_tutorial/data_analysis.py”. This file can be executed with python, however you must first edit the file paths for in- and output.
First lets import all functions that we need:
from pyTFM.data_analysis import *
Next, we read the output files from wildtype and KO data sets. This is done in two steps: First the text files are read into a dictionary where they are sorted for the frames, object ids and the type of the quantity. Then frames and objects are pooled to a dictionary where each key is the name of a quantity and the value is a list of the measured values. Note that our output text file for the last step should be called “out0.txt” if you followed the tutorial exactly.
# reading the Wildtype data set. Use your own output text file here
file_WT = r"/home/user/Software/example_data_for_pyTFM/clickpoints_tutorial/WT/out.txt"
# reading the parameters and the results, sorted for frames and object ids
parameter_dict_WT, res_dict_WT = read_output_file(file_WT)
# pooling all frames and objects together.
n_frames_WT, values_dict_WT, frame_list_WT = prepare_values(res_dict_WT)
# reading the KO data set. Use your own output text file here
file_KO = r"/home/user/Software/example_data_for_pyTFM/clickpoints_tutorial/KO/out.txt"
parameter_dict_KO, res_dict_KO = read_output_file(file_KO)
n_frames_KO, values_dict_KO, frame_list_KO = prepare_values(res_dict_KO)
We are going to use the dictionaries with pooled values (values_dict_WT and values_dict_KO) for plotting. First, let’s do some normalization: We can guess that a larger colony generates more forces. If we assume this relation is somewhat linear it is useful to normalize measures of the force generation with the surface area of the colony:
# normalizing the strain energy
values_dict_WT["strain energy per area"] = values_dict_WT["strain energy"]/values_dict_WT["area Cell Area"]
values_dict_KO["strain energy per area"] = values_dict_KO["strain energy"]/values_dict_WT["area Cell Area"]
# normalizing the contractility
values_dict_WT["contractility per area"] = values_dict_WT["contractility"]/values_dict_WT["area Cell Area"]
values_dict_KO["contractility per area"] = values_dict_KO["contractility"]/values_dict_WT["area Cell Area"]
Note that this only works if force generation and area were calculated for all colonies - this requires that you outline the colony with the mask “Cell Boundary”.
Now we can perform a t-test to identify any significant differences between KO and WT. We will do this for all quantity pairs at once and later display only the most important quantities. Unfortunately, due to the the fact that we analyzed only two colonies per data set you will find no significant differences in this case.
# t-test for all value pairs
t_test_dict = t_test(values_dict_WT,values_dict_KO)
Let’s produce some plots. First, we are going to compare some key quantities with box plots. The function “box_plots” expects two dictionaries with values, a list (“labels”) with two elements, which identifies these dictionary and a list (“types”) of quantities that you want to plot. Additionally, you can provide a dictionary containing statistical test results and specify your own axis labels and axis limits:
lables = ["WT", "KO"] # designations for the two dictionaries that are provided to the box_plots functions
types = ["contractility per area", "strain energy per area"] # name of the quantities that are plotted
ylabels = ["contractility per colony area [N/m²]", "strain energy per colony area [J/m²]"] # custom axes labels
# producing a two box plots comparing the strain energy and the contractility in WT and KO
fig_force = box_plots(values_dict_WT, values_dict_KO, lables, t_test_dict=t_test_dict, types=types,
low_ylim=0, ylabels=ylabels, plot_legend=True)
We can do the same for the mean normal stress and line tension:
lables = ["WT", "KO"] # designations for the two dictionaries that are provided to the box_plots functions
types = ["mean normal stress Cell Area", "average magnitude line tension"] # name of the quantities that are plotted
ylabels = ["mean normal stress [N/m]", "line tension [N/m]"] #
fig_stress = box_plots(values_dict_WT, values_dict_KO, lables, t_test_dict=t_test_dict, types=types,
low_ylim=0, ylabels=ylabels, plot_legend=True)
Another interesting way of studying force generation is to look at the relation between strain energy (a measure for total force generation) and contractility (measure for the coordinated force generation) This can be done as follows:
lables = ["WT", "KO"] # designations for the two dictionaries that are provided to the box_plots functions
# name of the measures that are plotted. Must be length 2 for this case.
types = ["contractility per area", "strain energy per area"]
# plotting value of types[0] vs value of types[1]
fig_force2 = compare_two_values(values_dict_WT, values_dict_KO, types, lables,
xlabel="contractility per colony area [N/m²]", ylabel="strain energy per colony area [J/m²]")
Finally, let’s save the figures.
# define and output folder for your figures
folder_plots = r"/home/user/Software/example_data_for_pyTFM/clickpoints_tutorial/plots/"
# create the folder, if it doesn't already exist
createFolder(folder_plots)
# saving the three figures that were created beforehand
fig_force.savefig(os.path.join(folder_plots, "forces1.png")) # boxplot comparing measures for force generation
fig_stress.savefig(os.path.join(folder_plots, "fig_stress.png")) # boxplot comparing normal stress and line tension
fig_force2.savefig(os.path.join(folder_plots, "forces2.png")) # plot of strain energy vs contractility
Using pyTFM in Python¶
pyTFM makes it easy to perform Traction Force Microscopy and Monolayer Stress Microscopy in python. In this tutorial we will calculate strain energy, contractillity, mean normal stress and average line tension of a cell colony. As always we need two images of fluorescent beads: One image before cell removal and one image after cell removal. Additionally, since we are not using the clickpoints addon, we need to provide 3 masks: A mask for the area on which force generation is evaluated (encircling all deformations and forces that originate from the cell colony), a mask for the Finite Elements Analysis (encircling all forces that originate from the cell colony) and a mask of the cell boundaries. Of course, the easies way to generate these masks is to use clickpoints.
In the example data set in the subfolder “python_tutorial” you can find images for a single cell colony as well as suitable masks. I drew the masks in clickpoints and saved them as PNG files so that you can look at them with a standard image display tool. You can use any other format, as long as you can load them as a boolean (True and False) array to python.
The “python_tutorial” folder also contains the complete code of the tutorial in a single python script “tutorial.py”. This script prints all quantities and produces all figures, that we are going to see in this tutorial. It should work right away.
Calculating Deformation Fields¶
First, let’s import functions to calculate and plot the deformation field:
from pyTFM.TFM_functions import calculate_deformation
from pyTFM.plotting import show_quiver
Calculating the deformation field requires the images of the beads before and after cell removal. You can provide either paths to the files, or arrays ( which must have the data type int32). The deformation field is calculated with Particle Image Velocimetry, using a cross correlation algorithm. You need to find appropriate values for the window_size and overlap that produce a smooth deformation field. For this data you can use:
# paths to the images
im_path1 = r"/home/user/Software/example_data_for_pyTFM/python_tutorial/04after.tif" # change to your location
im_path2 = r"/home/user/Software/example_data_for_pyTFM/python_tutorial/04before.tif"
# calculating the deformation
u, v, mask_val, mask_std = calculate_deformation(im_path1, im_path2, window_size = 100, overlap = 60)
# the unit of window size and overlap is pixels of the image of the beads
The overlap of 60 is a bit to small, especially for the FEM analysis later. If you want to be more accurate use, an overlap of 95. This will however increase the calculation time from a few seconds to roughly 5 minutes.
Let’s plot the deformation field:
# plotting the deformation field
fig1, ax = show_quiver(u, v, cbar_str="deformations\n[pixels]")
show_quiver accepts most of the plotting parameters listed in Overview of Plotting Parameters.
Hint
In some cases the images of the beads don’t share exactly the same field of view. You will notice this in the deformation field. pyTFM provides a function to find and extract the common field of view of both images with subpixel accuracy:
from pyTFM.frame_shift_correction import correct_stage_drift
from PIL import Image
import numpy as np
# load your images; dtype must be float32; images must be grayscale (you need a 2 dimensional array)
image1 = np.asarray(Image.open(r"/home/user/Software/example_data_for_pyTFM/python_tutorial/04after.tif"))
image2 = np.asarray(Image.open(r"/home/user/Software/example_data_for_pyTFM/python_tutorial/04before.tif"))
# cutting out the common field of few of image1 with image2.
# This also normalizes the images and applies a subpixel
# accurate shift. You can provide an additional list of
# images that will be cut to the same field of view.
image1_cor, image2_cor, other_images, drift = correct_stage_drift(image1, image2, additional_images=[])
# saving the output
image1_cor.save(r"/home/user/Software/example_data_for_pyTFM/python_tutorial/04after_corr.tif")
image2_cor.save(r"/home/user/Software/example_data_for_pyTFM/python_tutorial/04before_corr.tif")
The images in this tutorial are already corrected.
Calculating Traction Fields¶
Next, we are going to calculate the traction forces, that the cell colony generated. This is done with the “TFM_tractions” function:
from pyTFM.TFM_functions import TFM_tractions
import numpy as np
We have to set the elastic parameters of the substrate (Young’s modulus, Poisson’s ratio and the height of the substrate). You can also set the substrate height to “infinite”. We also need the pixel size of the image of the beads and the pixel size of the deformation field. The later can be calculate if you know the dimensions and pixel size of the image of the beads:
ps1 = 0.201 # pixel size of the image of the beads
im1_shape = (1991, 2033) # dimensions of the image of the beads
ps2 = ps1 * np.mean(np.array(im1_shape) / np.array(u.shape)) # pixel size of of the deformation field
young = 49000 # Young's modulus of the substrate in Pa
sigma = 0.49 # Poisson's ratio of the substrate
h = 300 # height of the substrate in µm, "infinite" is also accepted
Finally, the traction field can be calculated by:
tx, ty = TFM_tractions(u, v, pixelsize1=ps1, pixelsize2=ps2, h=h, young=young, sigma=sigma)
We can plot it in the same way as we plotted the deformation field:
fig2, ax = show_quiver(tx, ty, cbar_str="tractions\n[Pa]")
Quantifying the Force Generation¶
In order to quantify the force generation of the cell colony, we have to select the area where deformations and tractions that are generated by the colony are located. This selection requires a mask, a boolean array, that has the value True in the area that we want to use and False elsewhere. I produced the appropriate mask in clickpoints and saved it as a grayscale image as “force_measurement.png”. After loading the mask, there are two more things we need to do: First, we need to fill all holes in the mask in order to produce a continuous area. Second, we need to resize the mask to the dimensions of the deformation and traction fields:
import matplotlib.pyplot as plt
from scipy.ndimage.morphology import binary_fill_holes
from pyTFM.grid_setup_solids_py import interpolation # a simple function to resize the mask
# loading a mask that defines the area used for measuring the force generation
mask = plt.imread(r"/home/user/Software/example_data_for_pyTFM/python_tutorial/Tractions.png").astype(bool)
mask = binary_fill_holes(mask) # the mask should be a single patch without holes
# changing the masks dimensions to fit to the deformation and traction fields
mask = interpolation(mask, dims=u.shape)
This mask can now be used to calculate the contractillity and the strain energy:
from pyTFM.TFM_functions import strain_energy_points, contractillity
# strain energy:
# first we calculate a map of strain energy
energy_points = strain_energy_points(u, v, tx, ty, ps1, ps2) # J/pixel
# then we sum all energy points in the area defined by mask
strain_energy = np.sum(energy_points[mask]) # 2.14*10**-13 J
# contractillity
contractile_force, proj_x, proj_y, center = contractillity(tx, ty, ps2, mask) # 2.03*10**-6 N
Measuring Stresses in Cell Colonies¶
Stresses are calculated with the Finite Elements Methods, modeling the colony as a 2 dimensional sheet and applying force opposite to the traction forces to it. The FEM algorithm largely uses the solidspy package. This package is also very instructive if you want to get into FEM in general.
We will use the previous area outlined in “Tractions.png” to model the cell colony. This area covers all cell generated tractions and is larger then the actual cell colony, due to inaccuracies in the calculation of tractions. All measures for stress are evaluated on the actual area of the cell colony. This area is generated from the cell boundaries.
# first mask: The area used for Finite Elements Methods
# it should encircle all forces generated by the cell colony
mask_FEM = plt.imread(r"/home/user/Software/example_data_for_pyTFM/python_tutorial/Tractions.png").astype(bool)
mask_FEM = binary_fill_holes(mask_FEM) # the mask should be a single patch without holes
# changing the masks dimensions to fit to the deformation and traction field:
mask_FEM = interpolation(mask_FEM, dims=tx.shape)
# second mask: The area of the cells. Average stresses and other values are calculated only
# on the actual area of the cell, represented by this mask.
mask_cells = plt.imread(r"/home/user/Software/example_data_for_pyTFM/python_tutorial/cell_borders.png").astype(bool)
mask_cells = binary_fill_holes(mask_cells)
mask_cells = interpolation(mask_cells, dims=tx.shape)
The traction forces in the FEM area are typically slightly unbalanced, leading to a net force and torque acting on the cell colony. We need to correct this:
from pyTFM.grid_setup_solids_py import prepare_forces
# converting tractions (forces per surface area) to actual forces
# and correcting imbalanced forces and torques
# tx->traction forces in x direction, ty->traction forces in y direction
# ps2->pixel size of the traction field, mask_FEM-> mask for FEM
fx, fy = prepare_forces(tx, ty, ps2, mask_FEM)
Now we are ready to perform a Finite Elements Analysis. This is split into two steps: First, the FEM grid is setup. The grid is build up of nodes. For each node the connectivity to other nodes (stored in “elements”), constraints on the displacements (stored in “nodes”) and forces acting (stored in “loads”) as well as elastic properties (Youngs’s modulus and Poisson’s ratio stored in mats ) on the node are defined. Note that the Young’s modulus of the material has no influence whatsoever on the resulting stresses and that the Poisson’s ratio has only a small influence. Consequently, both Young’s modulus and Poisson’s ratio can be left at their default value (1 Pa and 0.5 respectively). The applied forces are simply obtained from the underlying tractions with a negative sign. Next, the FEM system is solved by calculating the deformations, followed by the strain and, based on the strain-stress relation of a linearly elastic 2-dimensional material.
from pyTFM.grid_setup_solids_py import grid_setup, FEM_simulation
# constructing the FEM grid
nodes, elements, loads, mats = grid_setup(mask_FEM, -fx, -fy, sigma=0.5)
# performing the FEM analysis
# verbose prints the progress of numerically solving the FEM system of equations.
UG_sol, stress_tensor = FEM_simulation(nodes, elements, loads, mats, mask_FEM, verbose=True)
# UG_sol is a list of deformations for each node. We don't need it here.
The stress tensor completely defines the forces inside the cell colony. We can for example extract the average mean normal stress and the coefficient of variation of the mean normal stress (quantifying how much the stress varies in the colony) from the stress tensor. We will use the mask “mask_cells” which marks the actual area of the cell colony for these measurements.
# mean normal stress
ms_map = ((stress_tensor[:, :, 0, 0] + stress_tensor[:, :, 1, 1]) / 2) / (ps2 * 10**-6)
# average on the area of the cell colony.
ms = np.mean(ms_map[mask_cells]) # 0.0043 N/m
# coefficient of variation
cv = np.nanstd(ms_map[mask_cells]) / np.abs(np.nanmean(ms_map[mask_cells])) # 0.41 no unit
Calculating the Line Tension¶
A particularly interesting feature are forces taht are transmitted across cell-cell boundaries. This is quantified by the line tension. First, we need to load a mask marking all cell borders. Note that this is the same mask that we used to get the area of the cell colony, only this time we are not going to fill any holes or resize the mask.
# loading a mask of the cell borders mask_borders = plt.imread(r"/home/user/Software/example_data_for_pyTFM/python_tutorial/cell_borders.png").astype(bool)
The cell-cell borders are stored in an object “borders”, which among other things contains a spline interpolation of each border, assigns each border to a cell and contains a list of borders located at the edge of the cell colony:
from pyTFM.grid_setup_solids_py import find_borders
# identifying borders, counting cells, performing spline interpolation to smooth the borders
borders = find_borders(mask_borders, tx.shape)
# we can for example get the number of cells from the "borders" object
n_cells = borders.n_cells # 8
We can use the cell-cell borders together with the stress tensor to calculate the line tension. The line tension is a force vector acting on a small slice of a cell border. We are going to calculate the average length of this vector (“avg_line_tension”) and the average normal component of the line tension (“avg_normal_line_tension”):
from pyTFM.stress_functions import lineTension
# calculating the line tension along the cell borders
lt, min_v, max_v = lineTension(borders.lines_splines, borders.line_lengths, stress_tensor, pixel_length=ps2)
# lt is a nested dictionary. The first key is the id of a cell border.
# For each cell border the line tension vectors ("t_vecs"), the normal
# and shear component of the line tension ("t_shear") and the normal
# vectors of the cell border ("n_vecs") are calculated at a large number of points.
# average norm of the line tension. Only borders not at colony edge are used
lt_vecs = np.concatenate([lt[l_id]["t_vecs"] for l_id in lt.keys() if l_id not in borders.edge_lines])
avg_line_tension = np.mean(np.linalg.norm(lt_vecs, axis=1)) # 0.00569 N/m
# average normal component of the line tension
lt_normal = np.concatenate([lt[l_id]["t_normal"] for l_id in lt.keys() if l_id not in borders.edge_lines])
avg_normal_line_tension = np.mean(np.abs(lt_normal)) # 0.00566 N/m,
# here you can see that almost the line tensions act almost exclusively perpendicular to the cell borders.
Finally let’s produce a plot of the line tension:
from pyTFM.plotting import plot_continuous_boundary_stresses
# plotting the line tension
fig3, ax = plot_continuous_boundary_stresses([borders.inter_shape, borders.edge_lines, lt, min_v, max_v], cbar_style="outside")
Typical Measures for Force Generation and Stresses in Cells¶
There is a number of different ways to measure force generation and stresses. Here you can find an overview of all quantities that can be calculated with this program.
Deformations in the Substrate¶
The simplest way is to sum up all deformations on the substrate surface that the cells are attached to. The deformations depend on the mechanical properties of the substrate, which means that this is not possible to compare results, when different substrates have been used.
Strain Energy¶
The strain energy is the total work that cells have to commit to deform their substrate. It is defined as \(\frac{1}{2} \int \vec{d} \times \vec{f}\), where \(\vec{d}\) and \(\vec{f}\) are the deformation and the traction force vectors. That means that a high strain energy only needs a local alignment of both vectors.
Contractility¶
The contractility is defined as the sum of the projection of all traction forces towards one point, called the force epicenter. As such, the contractility is high if all force are already orientated towards one central point. Locally opposing forces and forces not pointing towards the force epicenter do not contribute to the contractility. A cell or cell colony which is able to coordinate its force generation in such a way that the forces seem to originate from a single point can achieve a high contractility, while expending a comparably small amount of strain energy.
This is further illustrated in Fig. 13. Case A represents a cell colony with two cells with high coordination of force generation and Case B represents a cell with coordination. In case B each cell generates contractile forces on its own. Accordingly case B has a lower contractility if we assume equal strain energy in Case A and B.
To sum up: the strain energy is a measure for the total force generation, while the contractility is a measure for the coordinated force generation.
Average Normal and Shear Stress¶
Stress describes the forces that are transferred inside of a cell or cell sheet. For any given point in the cell sheet the stress is defined by a tensor with 4 components. Each component represents forces that would act on the edges of a square that was cut out of the cell sheet as shown in Fig. 14.

\(\sigma_{xx}\) and \(\sigma_{yy}\): Normal Stresses in x and y direction. \(\sigma_{xy}\) and \(\sigma_{yx}\): Shear stresses.
We can distinguish between two types of stress: The shear stress, a force that acts parallel to the edges of this square, and normal stress, a forces that acts perpendicular to the edges of this square. Due to geometric reasons both shear components of the stress tensor must be identical. This is not the case for the normal components. Since it is of no interest whether normal stress comes predominately from the x or y directions for our analysis, it is more useful to calculate the mean of both components. This leaves two stresses: the shear stress and the mean normal stress. These stresses can be averaged over the whole cell colony area.
Note
The mean normal stress can be either negative, indicating a compressive stress, or positive, indicating a tensile stress. A typical cell experiences tensile stress, as it pulls on the underlying substrate from its edges .The shear stress can also have a positive or a negative sign. However, there is no useful interpretation in this case.
Distribution of Stresses¶
The distribution of stresses can be described by the Coefficient of Variation (CV), that is the standard deviation normalized with the mean, of mean normal or shear stress.
Forces acting across Cell Boundaries¶
As hinted above, the stress tensor can be used to calculate the force that acts across a boundary in the cell colony. This force is called the line tension, and has a straight forward interpretation: Imagine you were to actually cut the cell sheet along the boundary between two cells. If the cells continue to generate force the edges of this cut would drift apart or start overlapping as you have just cut the material holding both edges together. In order to hold both edges in place as they were before you cut them, you need to apply a force at the edges. This force, normalized by the length of the cut, is the line tension.
The line tension is a vector with x and y components. Similar to stresses it can be split in a shear component (the force acting parallel to the cut) and a normal component (the force acting perpendicular to the cut). Both contribute to the magnitude (length of the line tension vector) of the line tension.
Note
Similar to normal stresses, the normal component of the line tension can be negative or positive, indicating that the two sides of the edge along which the line tension was calculated, are pushed together or pulled apart. The shear component of the line tension lacks such an interpretation and the magnitude of the line tension can of course only be positive.
Note¶
If you encounter any bugs or other problems please report them here or contact me at andreas.b.bauer@fau.de.