Filter phasor coordinates#

Functions for filtering phasor coordinates.

Filtering phasor coordinates improves signal quality by reducing noise while preserving relevant features. Common methods include thresholding, median, Gaussian, and wavelet-based filtering.

Import required modules and functions:

from phasorpy.datasets import fetch
from phasorpy.filter import (
    phasor_filter_gaussian,
    phasor_filter_median,
    phasor_filter_pawflim,
    phasor_threshold,
    signal_filter_gaussian,
    signal_filter_median,
)
from phasorpy.io import signal_from_imspector_tiff
from phasorpy.lifetime import phasor_calibrate
from phasorpy.phasor import phasor_from_signal
from phasorpy.plot import plot_image, plot_phasor

Calibrated phasor coordinates#

Read a time-correlated single photon counting (TCSPC) histogram from a file. A homogeneous solution of Fluorescein (4.2 ns) was imaged as a reference:

signal = signal_from_imspector_tiff(fetch('Embryo.tif'))
frequency = signal.attrs['frequency']

reference_signal = signal_from_imspector_tiff(fetch('Fluorescein_Embryo.tif'))
assert reference_signal.attrs['frequency'] == frequency

Calculate, calibrate, and plot the phasor coordinates:

mean, real, imag = phasor_from_signal(signal, axis=0)

reference = phasor_from_signal(reference_signal, axis=0)

real, imag = phasor_calibrate(
    real, imag, *reference, frequency=frequency, lifetime=4.2
)

plot_phasor(
    real, imag, frequency=frequency, title='Calibrated phasor coordinates'
)
Calibrated phasor coordinates

Threshold#

Thresholding with phasorpy.filter.phasor_threshold() sets phasor coordinates to NaN (not a number) wherever the mean intensity, real or imaginary coordinates, phase or modulation fall outside specified bounds. Such coordinates are excluded from plots and calculations. Thresholding is typically performed after noise filtering. Use a minimum mean intensity to remove background:

plot_phasor(
    *phasor_threshold(mean, real, imag, mean_min=1)[1:],
    frequency=frequency,
    title='Thresholded phasor coordinates',
)
Thresholded phasor coordinates

Median filter#

Median filtering replaces each pixel value with the median of its neighboring values, reducing noise while preserving edges. The function phasorpy.filter.phasor_filter_median() applies a median filter to phasor coordinates. Typically, applying a 3x3 kernel once to three times is sufficient to remove noise while maintaining important features:

mean_filtered, real_filtered, imag_filtered = phasor_filter_median(
    mean, real, imag, repeat=3, size=3
)

Thresholds should be applied after noise filtering:

mean_filtered, real_filtered, imag_filtered = phasor_threshold(
    mean_filtered, real_filtered, imag_filtered, mean_min=1
)

Plot the median-filtered and thresholded phasor coordinates:

plot_phasor(
    real_filtered,
    imag_filtered,
    frequency=frequency,
    title='Median-filtered phasor coordinates (3x3 kernel, 3 repetitions)',
)
Median-filtered phasor coordinates (3x3 kernel, 3 repetitions)

Increasing the number of repetitions or the filter kernel size can further reduce noise, but may also remove relevant features:

mean_filtered, real_filtered, imag_filtered = phasor_threshold(
    *phasor_filter_median(mean, real, imag, repeat=6, size=5), mean_min=1
)

plot_phasor(
    real_filtered,
    imag_filtered,
    frequency=frequency,
    title='Median-filtered phasor coordinates (5x5 kernel, 6 repetitions)',
)
Median-filtered phasor coordinates (5x5 kernel, 6 repetitions)

The smoothing effect of median-filtering is demonstrated by plotting the real components of the filtered and unfiltered phasor coordinates as images:

plot_image(
    phasor_threshold(mean, real, imag, mean_min=1)[1],
    real_filtered,
    vmin=0.4,
    vmax=0.9,
    labels=['Unfiltered', 'Median-filtered'],
    title='Real component of phasor coordinates',
)
Real component of phasor coordinates, Unfiltered, Median-filtered

For comparison, the function phasorpy.filter.signal_filter_median() applies a median filter to the signal before phasor transformation:

signal_filtered = signal_filter_median(signal, skip_axis=0, repeat=1, size=3)

mean, real, imag = phasor_from_signal(signal_filtered, axis=0)

real, imag = phasor_calibrate(
    real, imag, *reference, frequency=frequency, lifetime=4.2
)

mean_filtered, real_filtered, imag_filtered = phasor_threshold(
    mean, real, imag, mean_min=1
)

plot_phasor(
    real_filtered,
    imag_filtered,
    frequency=frequency,
    title='Phasor coordinates of median-filtered signal',
)
Phasor coordinates of median-filtered signal

Note that the resulting phasor coordinates are displaced from the true distribution. Signal-space filtering does not commute with the phasor transform: for heterogeneous samples, the spatial median of neighboring signals does not produce a physically meaningful signal. Use phasorpy.filter.phasor_filter_median() instead.

Gaussian filter#

Gaussian filtering replaces each pixel value with a weighted average of its neighbors, where weights follow a Gaussian distribution. Unlike median filtering, it is linear and smooths more gradually. The function phasorpy.filter.phasor_filter_gaussian() applies a Gaussian filter to phasor coordinates. By default, a 3x3 kernel with sigma of about 0.8 is used:

mean, real, imag = phasor_from_signal(signal, axis=0)

real, imag = phasor_calibrate(
    real, imag, *reference, frequency=frequency, lifetime=4.2
)

mean_filtered, real_filtered, imag_filtered = phasor_filter_gaussian(
    mean, real, imag, repeat=3, size=3
)

mean_filtered, real_filtered, imag_filtered = phasor_threshold(
    mean_filtered, real_filtered, imag_filtered, mean_min=1
)

plot_phasor(
    real_filtered,
    imag_filtered,
    frequency=frequency,
    title='Gaussian-filtered phasor coordinates (3x3 kernel, 3 repetitions)',
)
Gaussian-filtered phasor coordinates (3x3 kernel, 3 repetitions)

Increasing the number of repetitions or the kernel size can further reduce noise, but may also blur relevant features:

mean_filtered, real_filtered, imag_filtered = phasor_threshold(
    *phasor_filter_gaussian(mean, real, imag, repeat=6, size=5), mean_min=1
)

plot_phasor(
    real_filtered,
    imag_filtered,
    frequency=frequency,
    title='Gaussian-filtered phasor coordinates (5x5 kernel, 6 repetitions)',
)
Gaussian-filtered phasor coordinates (5x5 kernel, 6 repetitions)

The smoothing effect of Gaussian filtering is demonstrated by plotting the real components of the filtered and unfiltered phasor coordinates as images:

plot_image(
    phasor_threshold(mean, real, imag, mean_min=1)[1],
    real_filtered,
    vmin=0.4,
    vmax=0.9,
    labels=['Unfiltered', 'Gaussian-filtered'],
    title='Real component of phasor coordinates',
)
Real component of phasor coordinates, Unfiltered, Gaussian-filtered

For comparison, the function phasorpy.filter.signal_filter_gaussian() applies a Gaussian filter to the signal before phasor transformation:

signal_filtered = signal_filter_gaussian(signal, skip_axis=0, repeat=3, size=3)

mean, real, imag = phasor_from_signal(signal_filtered, axis=0)

real, imag = phasor_calibrate(
    real, imag, *reference, frequency=frequency, lifetime=4.2
)

mean_filtered, real_filtered, imag_filtered = phasor_threshold(
    mean, real, imag, mean_min=1
)

plot_phasor(
    real_filtered,
    imag_filtered,
    frequency=frequency,
    title='Phasor coordinates of Gaussian-filtered signal',
)
Phasor coordinates of Gaussian-filtered signal

Unlike median filtering, Gaussian filtering is a linear operation, so the phasor coordinates of the Gaussian-filtered signal are close to those obtained by filtering the phasor coordinates directly.

pawFLIM wavelet filter#

Filtering based on wavelet decomposition is another method to reduce noise. The function phasorpy.filter.phasor_filter_pawflim() is based on the pawFLIM library. While the median filter is applicable to any type of phasor coordinates, the pawFLIM filter requires calibrated phasor coordinates from FLIM measurements and at least one harmonic and its corresponding second harmonic (the 1st and 2nd harmonic in this example):

harmonic = [1, 2]

mean, real, imag = phasor_from_signal(signal, axis=0, harmonic=harmonic)

reference = phasor_from_signal(reference_signal, axis=0, harmonic=harmonic)

real, imag = phasor_calibrate(
    real,
    imag,
    *reference,
    frequency=frequency,
    lifetime=4.2,
    harmonic=harmonic,
)

Apply the pawFLIM wavelet filter to the calibrated phasor coordinates:

mean_filtered, real_filtered, imag_filtered = phasor_threshold(
    *phasor_filter_pawflim(mean, real, imag, harmonic=harmonic), mean_min=1
)

Plot the pawFLIM-filtered and thresholded phasor coordinates:

plot_phasor(
    real_filtered[0],
    imag_filtered[0],
    frequency=frequency,
    title='pawFLIM-filtered phasor coordinates (sigma=2, levels=1)',
)
pawFLIM-filtered phasor coordinates (sigma=2, levels=1)

Increasing the significance level sigma of the comparison between phasor coordinates or the maximum averaging area levels can reduce noise further:

mean_filtered, real_filtered, imag_filtered = phasor_filter_pawflim(
    mean, real, imag, harmonic=harmonic, sigma=5, levels=3
)

mean_filtered, real_filtered, imag_filtered = phasor_threshold(
    mean_filtered, real_filtered, imag_filtered, mean_min=1
)

plot_phasor(
    real_filtered[0],
    imag_filtered[0],
    frequency=frequency,
    title='pawFLIM-filtered phasor coordinates (sigma=5, levels=3)',
)
pawFLIM-filtered phasor coordinates (sigma=5, levels=3)

Plot the real components of the filtered and unfiltered phasor coordinates as images:

plot_image(
    phasor_threshold(mean, real, imag, mean_min=1)[1],
    real_filtered,
    vmin=0.4,
    vmax=0.9,
    labels=['Unfiltered', 'pawFLIM-filtered'],
    title='Real component of phasor coordinates',
)
Real component of phasor coordinates, Unfiltered, pawFLIM-filtered

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

Gallery generated by Sphinx-Gallery