Skip to content

Commit

Permalink
Add more FLIM use case
Browse files Browse the repository at this point in the history
  • Loading branch information
gselzer committed Apr 10, 2024
1 parent cc635f0 commit 5335221
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 63 deletions.
205 changes: 142 additions & 63 deletions docs/ops/doc/examples/example_flim_analysis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,84 +2,163 @@
FLIM Analysis
=============

In this example we will use SciJava Ops within Fiji to perform `FLIM_` analysis on a sample from TODO that is TODO.
This technique can be used to TODO
In this example we will use SciJava Ops within Fiji to perform `FLIM`_ analysis, which is used in many situations including photosensitizer detection and `FRET`_ measurement.

SciJava Ops via Fiji's sripting engine with `script parameters`_:
.. image:: https://media.imagej.net/scijava-ops/1.0.0/flim_example_input.gif
:width: 49%
.. image:: https://media.imagej.net/scijava-ops/1.0.0/flim_example_pseudocolored.png
:width: 49%

Loading the sample data
We use a sample of `FluoCells™ Prepared Slide #1`_, imaged by `Jenu Chacko <https://loci.wisc.edu/staff/chacko-jenu/>`_ using Openscan-LSM and SPwith multiphoton excitation and a 40x WI lens.

FluoCells™ Prepared Slide #1 contains bovine pulmonary artery endothelial cells (BPAEC). MitoTracker™ Red CMXRos was used to stain the mitochondria in the live cells, with accumulation dependent upon membrane potential. Following fixation and permeabilization, F-actin was stained with Alexa Fluor™ 488 phalloidin, and the nuclei were counterstained with the blue-fluorescent DNA stain DAPI.

The sample data can be downloaded `here <https://media.imagej.net/scijava-ops/1.0.0/flim_example_data.sdt>`_ and can be loaded into Fiji with `Bio-Formats`_ using ``File → Open``. When presented with the ``Bio-Formats Import Options`` screen, it may be helpful to select ``Metadata viewing → Display metadata`` to determine values necessary for analysis. Then, select ``OK``. The data may take a minute to load.

Within the script, the `Levenberg-Marquardt algorithm <https://en.wikipedia.org/wiki/Levenberg%E2%80%93Marquardt_algorithm>`_ fitting Op of SciJava Ops FLIM is used to fit the data.

Basic analysis
---------------------

Script execution requires a number of parameters, which may be useful for adapting this script to other datasets. For this dataset, we use the following values:

+--------------------------------------+-------+
| Parameter | Value |
+======================================+=======+
| Time Base | 12.5 |
+--------------------------------------+-------+
| Time Bins | 256 |
+--------------------------------------+-------+
| Lifetime Axis | 2 |
+--------------------------------------+-------+
| Intensity Threshold | 18 |
+--------------------------------------+-------+
| Bin Kernel Radius | 1 |
+--------------------------------------+-------+

The script above will display the fit results, as well as a *pseudocolored* output image. To visualize , it should be contrasted using ImageJ's B&C plugin (``Ctrl + Shift + C``). Using that plugin, the minimum and maximum can be set by selecting the ``Set`` option, and providing ``0`` as the minimum and ``3`` as the maximum.

The results are shown in the panels below, and are described from left to right:

* The first initial fluorescence parameter A\ :subscript:`1`

* The first fluorescence lifetime τ\ :subscript:`1`.

* The pseudocolored result, an HSV image where

* Hue is a function of τ\ :subscript:`1`, where the function is a LUT

* Value is a function of A\ :subscript:`1`

.. image:: https://media.imagej.net/scijava-ops/1.0.0/flim_example_a1.png
:width: 32%

.. image:: https://media.imagej.net/scijava-ops/1.0.0/flim_example_tau1.png
:width: 32%

.. image:: https://media.imagej.net/scijava-ops/1.0.0/flim_example_pseudocolored.png
:width: 32%

The pseudocolored result shows a clear separation of fluorophores, which could be segmented and further processed.

Subsampling Within ROIs
-----------------------

With the sample data downloaded, it can be loaded into Fiji with `Bio-Formats`_ using `File &rarr; Open`. When presented with the ``Bio-Formats Import Options`` screen, it may be helpful to select ``Metadata viewing &rarr; Display metadata`` to determine values necessary for analysis. Then, select ``OK``.data
Curve fitting can be an intensive process, requiring significant resources to process larger datasets. For this reason, there can be significant benefit in restricting computation to Regions of Interest (ROIs), and SciJava Ops FLIM allows ROIs to restrict computation for all fitting Ops.

The provided script allows users to specify ROIs by drawing selections using the ImageJ UI. These selections are converted to ImgLib2 ``RealMask`` objects, which are then optionally passed to the Op.

In the panels below, we show script execution with computation restricted to the area around a single cell. In the top left panel, we can see the original dataset, annotated with an elliptical selection using the ImageJ UI. In the top right, bottom left, and bottom right panels, we see the A\ :subscript:`1` component, τ\ :subscript:`1` component, and pseudocolored results, respectively, all limited to the area within the selection.

.. image:: https://media.imagej.net/scijava-ops/1.0.0/flim_example_input_roi.png
:width: 49%

.. image:: https://media.imagej.net/scijava-ops/1.0.0/flim_example_a1_roi.png
:width: 49%

When presented with the ``Bio-Formats File Stitching`` window, again select ``OK``. The data may take a minute to load.data
.. image:: https://media.imagej.net/scijava-ops/1.0.0/flim_example_tau1_roi.png
:width: 49%

.. image:: https://media.imagej.net/scijava-ops/1.0.0/flim_example_pseudocolored_roi.png
:width: 49%

Running the script
------------------

.. tabs::

.. code-tab:: scijava-groovy

#@ OpEnvironment ops
#@ ROIService roiService
#@ Dataset input
#@ Float (description="The total time (ns) (timeBase in metadata)", label = "Time Base") timeBase
#@ Integer (description="The number of time bins (timeBins in metadata)", label = "Time Bins") timeBins
#@ Integer (description="The index of the lifetime axis (from metadata)", label = "Lifetime Axis", value=2) lifetimeAxis
#@ Integer (description="The time slice to analyze", label = "Slice", value = 12) c
#@OUTPUT Img Z
#@OUTPUT Img A1
#@OUTPUT Img Tau
#@OUTPUT Img pseudocolored

// Utility function to collapse all ROIs into a single mask for FLIM fitting
def getMask() {
// No ROIs
if (!roiService.hasROIs(input)) {
return null
#@ OpEnvironment ops
#@ ROIService roiService
#@ Img input
#@ Float (description="The total time (ns) (timeBase in metadata)", label = "Time Base") timeBase
#@ Integer (description="The number of time bins (timeBins in metadata)", label = "Time Bins") timeBins
#@ Integer (description="The index of the lifetime axis (from metadata)", label = "Lifetime Axis", value=2) lifetimeAxis
#@ Float (description="The minimal pixel intensity (across all time bins) threshold for fitting", label = "Intensity Threshold") iThresh
#@ Integer (description="The radius of the binning kernel", label = "Bin Kernel Radius", value=0, min=0) kernelRad
#@OUTPUT Img A1
#@OUTPUT Img Tau1
#@OUTPUT Img pseudocolored

import net.imglib2.roi.Regions
import java.lang.System

// Utility function to collapse all ROIs into a single mask for FLIM fitting
def getMask() {
// No ROIs
if (!roiService.hasROIs(input)) {
return null
}
// 1+ ROIs
rois = roiService.getROIs(input)
mask = rois.children()remove(0).data()
for(roi: rois.children()) {
mask = mask.or(roi.data())
}
return mask;
}
// 1+ ROIs
rois = roiService.getROIs(input)
mask = rois.children()remove(0).data()
for(roi: rois.children()) {
mask = mask.or(roi.data())

import net.imglib2.type.numeric.real.DoubleType
def getPercentile(img, mask, percentile) {
if (mask != null) {
img = Regions.sampleWithRealMask(mask, img)
}
return ops.op("stats.percentile").arity2()
.input(img, percentile)
.outType(DoubleType.class)
.apply()
.getRealFloat()
}
return mask;
}

// The FitParams contain a set of reasonable defaults for FLIM curve fitting
import org.scijava.ops.flim.FitParams
param = new FitParams()
param.transMap = ops.op("transform.hyperSliceView").arity3().input(input, 2, c).apply()

// xInc is the difference (ns) between two bins
param.xInc = timeBase / timeBins
param.ltAxis = lifetimeAxis

// Fit curves
rld = ops.op("flim.fitRLD").arity2().input(param, getMask()).apply()

// The fit results paramMap is a XYC image, with result attributes along the Channel axis
fittedImg = rld.paramMap
// For RLD, we have Z, A1, and Tau as the three attributes
Z = ops.op("transform.hyperSliceView").arity3().input(fittedImg, lifetimeAxis, 0).apply()
A1 = ops.op("transform.hyperSliceView").arity3().input(fittedImg, lifetimeAxis, 1).apply()
Tau = ops.op("transform.hyperSliceView").arity3().input(fittedImg, lifetimeAxis, 2).apply()
// Finally, generate a pseudocolored result
pseudocolored = ops.op("flim.pseudocolor").arity1().input(rld).apply()

Observing the outputs
---------------------

The script above will present 4 output images, each of which should be contrasted using ImageJ's B&C plugin (``Ctrl + Shift + C``) for ideal viewing. Using that plugin, the minimum and maximum can be set by selecting the ``Set`` option:
*. The ``Z`` image should be set to ``[0, 1]``
*. The ``A1`` image can be left as is.
*. The ``Tau`` image should be set to ``[0, 1]``
*. The ``Pseudocolored`` image can be left as is.
start = System.currentTimeMillis()

// The FitParams contain a set of reasonable defaults for FLIM curve fitting
import org.scijava.ops.flim.FitParams
param = new FitParams()
param.transMap = input
param.ltAxis = lifetimeAxis
param.iThresh = iThresh
// xInc is the difference (ns) between two bins
param.xInc = timeBase / timeBins

// Fit curves
kernel = ops.op("create.kernelSum").arity1().input(1 + 2 * kernelRad).apply()
lma = ops.op("flim.fitLMA").arity3().input(param, getMask(), kernel).apply()

// The fit results paramMap is a XYC image, with result attributes along the Channel axis
fittedImg = lma.paramMap
// For LMA, we have Z, A1, and Tau1 as the three attributes
A1 = ops.op("transform.hyperSliceView").arity3().input(fittedImg, lifetimeAxis, 1).apply()
Tau1 = ops.op("transform.hyperSliceView").arity3().input(fittedImg, lifetimeAxis, 2).apply()

// Finally, generate a pseudocolored result
cMin = getPercentile(Tau1, mask, 5.0)
cMax = getPercentile(Tau1, mask, 95.0)
pseudocolored = ops.op("flim.pseudocolor").arity3().input(lma, cMin, cMax).apply()

The outputs then look like the following: TODO: upload to media.imagej.net and link.
end = System.currentTimeMillis()
println("Finished fitting in " + (end - start) + " milliseconds")

.. _`Bio-Formats` : https://www.openmicroscopy.org/bio-formats/
.. _`FLIM` : https://en.wikipedia.org/wiki/Fluorescence-lifetime_imaging_microscopy
.. _`script parameters`: https://imagej.net/scripting/parameters
.. _`FluoCells™ Prepared Slide #1` : https://www.thermofisher.com/order/catalog/product/F36924
.. _`FRET` : https://en.wikipedia.org/wiki/F%C3%B6rster_resonance_energy_transfer
1 change: 1 addition & 0 deletions docs/ops/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ The combination of these libraries allows declarative image analysis workflows,
examples/deconvolution
examples/example_gaussian_subtraction
examples/opencv_denoise
examples/example_flim_analysis

.. toctree::
:maxdepth: 2
Expand Down

0 comments on commit 5335221

Please sign in to comment.