Skip to content

Commit

Permalink
Merge pull request #61 from zoccoler/patch_0_1_0
Browse files Browse the repository at this point in the history
Patch 0 1 0
  • Loading branch information
zoccoler authored Jul 1, 2024
2 parents e6cfeab + a242eab commit ffa7a1e
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 25 deletions.
2 changes: 1 addition & 1 deletion src/napari_flim_phasor_plotter/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "0.0.7"
__version__ = "0.1.0"

from ._reader import napari_get_reader
from ._sample_data import load_seminal_receptacle_image, load_hazelnut_image, load_hazelnut_z_stack, load_lifetime_cat_synthtetic_single_image
Expand Down
11 changes: 9 additions & 2 deletions src/napari_flim_phasor_plotter/_plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,26 @@ def __init__(self, napari_viewer):
self.advanced_options_container.layout().addWidget(self.tau_lines_container)
self.tau_lines_button.clicked.connect(self.on_show_hide_tau_lines)

# Start with histogram plot
self.plotting_type.setCurrentIndex(1)
# Start with log scale
self.log_scale.setChecked(True)

def run(self,
features,
plot_x_axis_name,
plot_y_axis_name,
plot_cluster_name=None,
plot_cluster_name,
redraw_cluster_image=True,
force_redraw=True,
ensure_full_semi_circle_displayed=False
):
super().run(features=features,
plot_x_axis_name=plot_x_axis_name,
plot_y_axis_name=plot_y_axis_name,
plot_cluster_name=plot_cluster_name,
redraw_cluster_image=redraw_cluster_image,)
redraw_cluster_image=redraw_cluster_image,
force_redraw=force_redraw)
if self.tau_lines_button.isChecked():
self.add_tau_lines_from_widget()
self.redefine_axes_limits(ensure_full_semi_circle_displayed=ensure_full_semi_circle_displayed)
Expand Down
149 changes: 129 additions & 20 deletions src/napari_flim_phasor_plotter/_tests/test_widget.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,80 @@
from napari_flim_phasor_plotter._widget import make_flim_phasor_plot
from napari_flim_phasor_plotter._synthetic import make_synthetic_flim_data
from napari_flim_phasor_plotter._synthetic import create_time_array
from napari_flim_phasor_plotter._plotting import PhasorPlotterWidget
import numpy as np
import pandas as pd
# make_napari_viewer is a pytest fixture that returns a napari viewer object
# capsys is a pytest fixture that captures stdout and stderr output streams

# Global inputs
laser_frequency = 40 # MHz
amplitude = 1
tau_list = [0.1, 0.2, 0.5, 1, 2, 5, 10, 25, 40] # ns
# Obs: after running calculate phasors with default thresholding (10), 0.1 and 0.2 associated pixels are excluded/masked out because they end up with low photon budget (summed intensity < 10) given the low amplitude and fast decay

def test_make_flim_phasor_plot_and_plotter(make_napari_viewer, capsys):
output_table = pd.DataFrame({
table = pd.DataFrame({
'label': [1, 2, 3, 4, 5, 6, 7],
'G': [0.984806, 0.941213, 0.799235, 0.388781, 0.137519, 0.025144, 0.010088],
'S': [0.120760, 0.233658, 0.399003, 0.485904, 0.342826, 0.154997, 0.098370],
'pixel_x_coordinates': [2, 0, 1, 2, 0, 1, 2],
'pixel_y_coordinates': [0, 1, 1, 1, 2, 2, 2],
'pixel_z_coordinates': [0, 0, 0, 0, 0, 0, 0],
'frame': [0, 0, 0, 0, 0, 0, 0]
'frame': [0, 0, 0, 0, 0, 0, 0],
})

labelled_pixels_masked = np.array(
[
[
[
[0, 0, 1], # 0 represents masked pixels after default thresholding (10)
[2, 3, 4],
[5, 6, 7],
]
]
]
)

phasor_clusters_labels = np.array(
[
[
[
[0, 0, 2], # 0 represents noise or masked pixels
[1, 1, 2], # 1 represents not selected pixels
[3, 3, 3], # 2 and 3 represent the first 2 selected clusters labels (in table they are 1 and 2)
]
]
]
)
# Manual clusters column contains phasor cluster labels, where labelled pixels are present, minus 1 because 0 is reserved for noise clusters and 1 for not selected pixels
manual_clusters_column = pd.Series(phasor_clusters_labels[labelled_pixels_masked>0]-1, name='MANUAL_CLUSTER_ID', index=table.index)
table_with_clusters = pd.concat([table, manual_clusters_column], axis=1)

def test_make_flim_phasor_plot_and_plotter(make_napari_viewer, capsys):
# Inputs for manual selection
input_selection_vertices = np.array(
[
[0.940, 0.23],
[0.940, 0.24],
[0.945, 0.24],
[0.945, 0.23]
[0.945, 0.23],
]
) # square vertices with G and S coordinates around 1 ns

output_selected_cluster_image = np.array(
# Expected output array after manual selection
output_selected_cluster_labels = np.array(
[
[
[
[0, 0, 0],
[2, 0, 0],
[0, 0, 0]
[0, 0, 0],
]
]
]
)

# Test make_flim_phasor_plot
viewer = make_napari_viewer()
laser_frequency = 40 # MHz
amplitude = 1
tau_list = [0.1, 0.2, 0.5, 1, 2, 5, 10, 25, 40] # nss

number_of_time_points = 1000

time_array = create_time_array(laser_frequency, number_of_time_points)
Expand All @@ -57,30 +87,109 @@ def test_make_flim_phasor_plot_and_plotter(make_napari_viewer, capsys):
# create the widget
my_widget = make_flim_phasor_plot()

# execute our function
# execute function
plotter_widget, labels_layer = my_widget()
# Check if the plotter widget was created
assert plotter_widget is not None

# Check if outputs match expected values
assert len(viewer.layers) == 2
assert list(labels_layer.features.columns) == ['label', 'G', 'S', 'pixel_x_coordinates', 'pixel_y_coordinates', 'pixel_z_coordinates', 'frame']
assert list(labels_layer.features.columns) == list(table.columns)
assert labels_layer.features.shape == (7, 7)
assert labels_layer.name.startswith(
'Labelled_pixels_from_')
assert np.allclose(labels_layer.features.values,
output_table.values, rtol=0, atol=1e-5)
table.values, rtol=0, atol=1e-5)

# Test plotter widget options and selection

# Plot tau lines
plotter_widget.tau_lines_line_edit_widget.setText('0.1, 1')
plotter_widget.tau_lines_button.click()

# TODO: Fix the following test (manually it works)
# # Select region around 1 ns
# plotter_widget.graphics_widget.selector.onselect(input_selection_vertices)
plotter_widget.graphics_widget.selector.onselect(input_selection_vertices)

# # Check if the selected cluster image is displayed
# assert len(viewer.layers) == 3
# phasor_clusters_layer = viewer.layers[-1]
# assert np.allclose(phasor_clusters_layer.data, output_selected_cluster_image, rtol=0, atol=1e-5)
assert len(viewer.layers) == 3
phasor_clusters_layer = viewer.layers[-1]
assert phasor_clusters_layer.name.startswith(
'Phasor_clusters_from_')
assert np.allclose(phasor_clusters_layer.data, output_selected_cluster_labels, rtol=0, atol=1e-5)


def test_manual_label_extract():
from napari_flim_phasor_plotter._widget import manual_label_extract
from napari.layers import Labels
# Inputs
selected_label = 2
# Expected output array
output_selected_cluster_labels = np.array(
[
[0, 0, 2],
[0, 0, 2],
[0, 0, 0],
]
)

labels_layer = Labels(phasor_clusters_labels)
selected_label_layer = manual_label_extract(labels_layer, selected_label)

assert selected_label_layer.data.shape == output_selected_cluster_labels.shape # squeeze dimensions to match other processing tools
assert np.array_equal(selected_label_layer.data, output_selected_cluster_labels)

def test_get_n_largest_cluster_labels():
from napari_flim_phasor_plotter._widget import get_n_largest_cluster_labels
# Input
number_of_clusters = 2
# Expected outputs
expected_list_of_cluster_labels = [3, 2] # lergest cluster is 3, second largest is 2 (1 represents not selected pixels)

list_of_cluster_labels = get_n_largest_cluster_labels(table_with_clusters, number_of_clusters)

assert len(list_of_cluster_labels) == number_of_clusters
assert list(list_of_cluster_labels) == expected_list_of_cluster_labels

def test_Split_N_Largest_Cluster_Labels(make_napari_viewer):
from napari_flim_phasor_plotter._widget import Split_N_Largest_Cluster_Labels
from napari.layers import Labels

viewer = make_napari_viewer()
# Inputs
labelled_pixels_layer = Labels(labelled_pixels_masked, features=table_with_clusters)
phasor_clusters_layer = Labels(phasor_clusters_labels)
number_of_clusters = 2
clustering_id = manual_clusters_column.name
# Expected output arrays
largest_cluster_labels = np.array(
[
[0, 0, 0],
[0, 0, 0],
[3, 3, 3],
]
)
second_largest_cluster_labels = np.array(
[
[0, 0, 2],
[0, 0, 2],
[0, 0, 0],
]
)

# Add layers to viewer
viewer.add_layer(labelled_pixels_layer)
viewer.add_layer(phasor_clusters_layer)
n_layers_in_viewer = 2
# Set widget options
split_n_largest_widget = Split_N_Largest_Cluster_Labels(viewer)
split_n_largest_widget._labels_layer_combobox.value = labelled_pixels_layer
split_n_largest_widget._clusters_labels_layer_combobox.value = phasor_clusters_layer
split_n_largest_widget._n_spinbox.value = number_of_clusters
split_n_largest_widget._clustering_id_combobox.value = clustering_id
# Run widget
split_n_largest_widget._on_run_clicked()
# Check new layers are created
assert len(viewer.layers) == n_layers_in_viewer + number_of_clusters
# Check that first layer created contains only largest cluster label
assert np.array_equal(viewer.layers[-2].data, largest_cluster_labels)
# Check that second layer created contain only second largest cluster label
assert np.array_equal(viewer.layers[-1].data, second_largest_cluster_labels)
6 changes: 4 additions & 2 deletions src/napari_flim_phasor_plotter/_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ def make_flim_phasor_plot(image_layer: "napari.layers.Image",
plotter_widget.run(features=labels_layer.features,
plot_x_axis_name=plotter_widget.plot_x_axis.currentText(),
plot_y_axis_name=plotter_widget.plot_y_axis.currentText(),
plot_cluster_name=plotter_widget.plot_cluster_id.currentText(),
redraw_cluster_image=False,
ensure_full_semi_circle_displayed=True)

# Update laser frequency spinbox
Expand Down Expand Up @@ -260,15 +262,15 @@ def get_n_largest_cluster_labels(features_table: 'pandas.DataFrame', n: int=1, c
if 'MANUAL' in clustering_id:
sorted_cluster_ids = sorted_cluster_ids[sorted_cluster_ids != 1] # remove unselected clusters when selection is manual

return sorted_cluster_ids[:n]
return sorted_cluster_ids[:n].tolist()

def split_n_largest_cluster_labels(labels_layer: "napari.layers.Labels", clusters_labels_layer: "napari.layers.Labels", clustering_id: str, n: int=1) -> List["napari.layers.Labels"]:
"""Split the n largest clusters from a labels layer inot new layers
Parameters
----------
labels_layer : napari.layers.Labels
labels layer
labels layer with features table
clusters_labels_layer : napari.layers.Labels
labels layer with clusters
clustering_id : str
Expand Down

0 comments on commit ffa7a1e

Please sign in to comment.