Note
Go to the end to download the full example code
Dynamic TFM (immune cells)¶
This example evaluates a single natural killer cell that migrated through 1.2mg/ml collagen, recorded for 24min.
This example can also be evaluated with the graphical user interface.
21 import saenopy
Downloading the example data files¶
The folder structure is as follows. There is one position recorded in the gel (Pos002) and two channels (ch00, ch01). The stack has 162 z positions (z000-z161). The position is recorded for 24 time steps (t00-t23).
2_DynamicalSingleCellTFM
└── data
├── Pos002_S001_t00_z000_ch00.tif
├── Pos002_S001_t00_z000_ch01.tif
├── Pos002_S001_t00_z001_ch00.tif
├── Pos002_S001_t00_z001_ch01.tif
├── Pos002_S001_t00_z002_ch00.tif
├── ...
├── Pos002_S001_t01_z000_ch00.tif
├── Pos002_S001_t01_z000_ch01.tif
├── Pos002_S001_t01_z001_ch00.tif
└── ...
47 # download the data
48 saenopy.load_example("DynamicalSingleCellTFM")
Loading the Stacks¶
Saenopy is very flexible in loading stacks from any filename structure. Here we do not have multiple positions, so we do not need to use and asterisk * for batch processing. We replace the number of the channels “ch00” with a channel placeholder “ch{c:00}” to indicate that this refers to the channels and which channel to use as the first channel where the deformations should be detected. We replace the number of the z slice “z000” with a z placeholder “z{z}” to indicate that this number refers to the z slice. We replace the number of the time step “t00” with a time placeholder “t{t}” to indicate that this number refers to the time step. Due to technical reasons and the soft nature of collagen hydrogels, acquiring fast image stacks with our galvo stage (within 10 seconds) can cause external motion in the upper and lower region the image stack, as the stage accelerates and decelerates here. Therefore, we always acquire a larger stack in z-direction and then discard the upper and lower regions (20 images here).
69 # load the relaxed and the contracted stack
70 # {z} is the placeholder for the z stack
71 # {c} is the placeholder for the channels
72 # {t} is the placeholder for the time points
73
74 results = saenopy.get_stacks(r'2_DynamicalSingleCellTFM\data\Pos*_S001_t{t}_z{z}_ch{c:00}.tif',
75 r'2_DynamicalSingleCellTFM\example_output',
76 voxel_size=[0.2407, 0.2407, 1.0071],
77 time_delta=60,
78 crop={"z": (20, -20)}
79 )
Detecting the Deformations¶

Saenopy uses 3D Particle Image Velocimetry (PIV) to calculate the collagen matrix deformations generated by the natural killer cell at different times. For this, we use the following parameters.
Piv Parameter |
Value |
---|---|
element_size |
4 |
window_size |
12 |
signal_to_noise |
1.3 |
drift_correction |
True |
104 # define the parameters for the piv deformation detection
105 piv_parameters = {'element_size': 4.0, 'window_size': 12.0, 'signal_to_noise': 1.3, 'drift_correction': True}
106
107
108 # iterate over all the results objects
109 for result in results:
110 # set the parameters
111 result.piv_parameters = piv_parameters
112 # get count
113 count = len(result.stacks)
114 if result.stack_reference is None:
115 count -= 1
116 # iterate over all stack pairs
117 for i in range(count):
118 # get two consecutive stacks
119 if result.stack_reference is None:
120 stack1, stack2 = result.stacks[i], result.stacks[i + 1]
121 # or reference stack and one from the list
122 else:
123 stack1, stack2 = result.stack_reference, result.stacks[i]
124
125 # due to the acceleration of the galvo stage there can be shaking in the
126 # lower or upper part of the stack. Therefore, we recorded larger
127 # z-regions and then discard the upper or lower parts
128
129 # and calculate the displacement between them
130 result.mesh_piv[i] = saenopy.get_displacements_from_stacks(stack1, stack2,
131 piv_parameters["window_size"],
132 piv_parameters["element_size"],
133 piv_parameters["signal_to_noise"],
134 piv_parameters["drift_correction"])
135 # save the displacements
136 result.save()
Visualizing Results¶
You can save the resulting 3D fields by simply using the export image dialog. Here we underlay a bright field image of the cell for a better overview and export a .gif file

Generating the Finite Element Mesh¶
Interpolate the found deformations onto a new mesh which will be used for the regularisation. We use the same element size of deformation detection mesh here and we also keep the overall mesh size the same. We define that as an undeformed reference we want to use the median of all stacks.
Mesh Parameter |
Value |
---|---|
reference_stack |
median |
element_size |
4 |
mesh_size |
“piv” |
170 # define the parameters to generate the solver mesh and interpolate the piv mesh onto it
171 mesh_parameters = {'reference_stack': 'median', 'element_size': 4.0, 'mesh_size': "piv"}
172
173 # iterate over all the results objects
174 for result in results:
175 # correct for the reference state
176 displacement_list = saenopy.subtract_reference_state(result.mesh_piv, mesh_parameters["reference_stack"])
177 # set the parameters
178 result.mesh_parameters = mesh_parameters
179 # iterate over all stack pairs
180 for i in range(len(result.mesh_piv)):
181 # and create the interpolated solver mesh
182 result.solvers[i] = saenopy.interpolate_mesh(result.mesh_piv[i], displacement_list[i], mesh_parameters)
183 # save the meshes
184 result.save()
Calculating the Forces¶
Define the material model and run the regularisation to fit the measured deformations and get the forces.
Material Parameter |
Value |
---|---|
k |
1449 |
d_0 |
0.0022 |
lambda_s |
0.032 |
d_s |
0.055 |
Regularisation Parameter |
Value |
---|---|
alpha |
10**10 |
step_size |
0.33 |
max_iterations |
100 |
214 # define the parameters to generate the solver mesh and interpolate the piv mesh onto it
215 material_parameters = {'k': 1449.0, 'd_0': 0.0022, 'lambda_s': 0.032, 'd_s': 0.055}
216 solve_parameters = {'alpha': 10**10, 'step_size': 0.33, 'max_iterations': 100}
217
218
219 # iterate over all the results objects
220 for result in results:
221 result.material_parameters = material_parameters
222 result.solve_parameters = solve_parameters
223 for M in result.solvers:
224 # set the material model
225 M.set_material_model(saenopy.materials.SemiAffineFiberMaterial(
226 material_parameters["k"],
227 material_parameters["d_0"],
228 material_parameters["lambda_s"],
229 material_parameters["d_s"],
230 ))
231 # find the regularized force solution
232 M.solve_regularized(alpha=solve_parameters["alpha"], step_size=solve_parameters["step_size"],
233 max_iterations=solve_parameters["max_iterations"], verbose=True)
234 # save the forces
235 result.save()