Multi-harmonic phasor coordinates#
An introduction to handling multi-harmonic phasor coordinates.
This tutorial is an adaptation of Introduction to PhasorPy, focusing on the calculation, calibration, filtering, thresholding, storage, and visualization of multi-harmonic phasor coordinates.
Import required modules and functions:
import numpy
from phasorpy.datasets import fetch
from phasorpy.filter import phasor_filter_median, phasor_threshold
from phasorpy.io import (
phasor_from_ometiff,
phasor_to_ometiff,
signal_from_imspector_tiff,
)
from phasorpy.lifetime import phasor_calibrate
from phasorpy.phasor import phasor_from_signal
from phasorpy.plot import plot_phasor
Read signal from file#
Read a time-correlated single photon counting (TCSPC) histogram with 56 time bins, acquired at 80.11 MHz, from a file:
signal = signal_from_imspector_tiff(fetch('Embryo.tif'))
frequency = signal.attrs['frequency']
print(signal.shape)
(56, 512, 512)
Calculate phasor coordinates#
Phasor coordinates at multiple harmonics can be calculated at once from the signal. The histogram bins are in the first dimension of the signal (axis=0). Calculate all possible harmonics by specifying harmonic=’all’:
mean, real, imag = phasor_from_signal(signal, harmonic='all', axis=0)
There are 28 (56/2) harmonics in the first dimension of the phasor coordinates, real and imag:
print(mean.shape, real.shape, imag.shape)
# For the remainder of the tutorial, only the first and second harmonics
# are used. They are calculated by specifying `harmonic=[1, 2]`:
mean, real, imag = phasor_from_signal(signal, harmonic=[1, 2], axis=0)
print(mean.shape, real.shape, imag.shape)
(512, 512) (28, 512, 512) (28, 512, 512)
(512, 512) (2, 512, 512) (2, 512, 512)
Plot the calculated phasor coordinates:
from phasorpy.plot import plot_phasor_image
plot_phasor_image(mean, real, imag, title='Sample')

Calibrate phasor coordinates#
A homogeneous solution of Fluorescein with a fluorescence lifetime of 4.2 ns was imaged as a reference for calibration:
reference_signal = signal_from_imspector_tiff(fetch('Fluorescein_Embryo.tif'))
assert reference_signal.attrs['frequency'] == frequency
Calculate phasor coordinates from the measured reference signal at the first and second harmonics:
reference_mean, reference_real, reference_imag = phasor_from_signal(
reference_signal, harmonic=[1, 2], axis=0
)
Calibration can be performed at multiple harmonics simultaneously. Calibrate the raw phasor coordinates with the reference coordinates of known lifetime (4.2 ns), at the first and second harmonics:
real, imag = phasor_calibrate(
real,
imag,
reference_mean,
reference_real,
reference_imag,
frequency=frequency,
harmonic=[1, 2],
lifetime=4.2,
)
If necessary, the calibration can be undone/reversed using the same reference:
uncalibrated_real, uncalibrated_imag = phasor_calibrate(
real,
imag,
reference_mean,
reference_real,
reference_imag,
frequency=frequency,
harmonic=[1, 2],
lifetime=4.2,
reverse=True,
)
assert numpy.allclose(
(uncalibrated_real, uncalibrated_imag),
phasor_from_signal(signal, harmonic=[1, 2], axis=0)[1:],
atol=1e-3,
equal_nan=True,
)
Filter phasor coordinates#
Applying a median filter to the calibrated phasor coordinates, often multiple times, improves contrast and reduces noise. The filter is applied independently to the real and imaginary components of the harmonics, but not to the average signal:
mean, real, imag = phasor_filter_median(mean, real, imag, size=3, repeat=2)
Pixels with low intensities are commonly excluded from analysis and visualization of phasor coordinates:
mean, real, imag = phasor_threshold(mean, real, imag, mean_min=1)
Show the calibrated, filtered phasor coordinates:
plot_phasor_image(
mean, real, imag, title='Calibrated, filtered phasor coordinates'
)

Store phasor coordinates#
Write the calibrated and filtered phasor coordinates at multiple harmonics and the fundamental frequency to an OME-TIFF file:
phasor_to_ometiff(
'phasors.ome.tif',
mean,
real,
imag,
frequency=frequency,
harmonic=[1, 2], # real and imag contain 1st and 2nd harmonics
description=(
'Phasor coordinates at first and second harmonics of a zebrafish '
'embryo at day 3, calibrated, median-filtered, and thresholded.'
),
)
Read the phasor coordinates and metadata back from the OME-TIFF file:
mean_, real_, imag_, attrs = phasor_from_ometiff(
'phasors.ome.tif',
harmonic='all', # read all harmonics stored in the file
)
assert numpy.allclose(real_, real, atol=1e-3, equal_nan=True)
assert real_.dtype == numpy.float32
assert attrs['frequency'] == frequency
assert attrs['harmonic'] == [1, 2] # the file contains only two harmonics
assert attrs['description'].startswith(
'Phasor coordinates at first and second harmonics'
)
Plot phasor coordinates#
Visualize the two-dimensional histogram of the calibrated and filtered phasor coordinates at the second harmonic:
plot_phasor(
real[1],
imag[1],
frequency=frequency,
title='Calibrated, filtered phasor coordinates at second harmonic',
)

For comparison, the uncalibrated, unfiltered phasor coordinates at the second harmonic:
plot_phasor(
uncalibrated_real[1],
uncalibrated_imag[1],
frequency=frequency,
allquadrants=True,
title='Raw phasor coordinates at second harmonic',
)

Total running time of the script: (0 minutes 1.771 seconds)