1
+ import os
2
+
1
3
import napari
2
4
import napari .layers
5
+ import pandas as pd
6
+
3
7
from napari .utils .notifications import show_info
4
- from qtpy .QtWidgets import QWidget , QVBoxLayout , QPushButton , QLabel , QComboBox
8
+ from qtpy .QtWidgets import QWidget , QVBoxLayout , QPushButton
5
9
6
10
from .base_widget import BaseWidget
7
-
8
- # Custom imports for model and prediction utilities
9
- from synaptic_reconstruction import distance_measurements
10
- from ..util import save_to_csv
11
+ from .. import distance_measurements
12
+
13
+ try :
14
+ from napari_skimage_regionprops import add_table
15
+ except ImportError :
16
+ add_table = None
17
+
18
+
19
+ def _save_distance_table (save_path , data ):
20
+ ext = os .path .splitext (save_path )[1 ]
21
+ if ext == "" : # No file extension given, By default we save to CSV.
22
+ file_path = f"{ save_path } .csv"
23
+ data .to_csv (file_path , index = False )
24
+ elif ext == ".csv" : # Extension was specified as csv
25
+ file_path = save_path
26
+ data .to_csv (file_path , index = False )
27
+ elif ext == ".xlsx" : # We also support excel.
28
+ file_path = save_path
29
+ data .to_excel (file_path , index = False )
30
+ else :
31
+ raise ValueError ("Invalid extension for table: {ext}. We support .csv or .xlsx." )
32
+ return file_path
11
33
12
34
13
35
class DistanceMeasureWidget (BaseWidget ):
@@ -20,15 +42,16 @@ def __init__(self):
20
42
self .image_selector_name1 = "Segmentation 1"
21
43
self .image_selector_name2 = "Segmentation 2"
22
44
# Create the image selection dropdown
45
+ # TODO: update the names to make it easier to distinguish what is what.
23
46
self .segmentation1_selector_widget = self ._create_layer_selector (self .image_selector_name1 , layer_type = "Labels" )
24
47
self .segmentation2_selector_widget = self ._create_layer_selector (self .image_selector_name2 , layer_type = "Labels" )
25
48
26
49
# create save path
27
50
self .settings = self ._create_settings_widget ()
28
51
29
52
# create buttons
30
- self .measure_pairwise_button = QPushButton (' Measure Distance Pairwise' )
31
- self .measure_segmentation_to_object_button = QPushButton (' Measure Distance Segmentation to Object' )
53
+ self .measure_pairwise_button = QPushButton (" Measure Distance Pairwise" )
54
+ self .measure_segmentation_to_object_button = QPushButton (" Measure Distance Segmentation to Object" )
32
55
33
56
# Connect buttons to functions
34
57
self .measure_pairwise_button .clicked .connect (self .on_measure_pairwise )
@@ -50,7 +73,6 @@ def on_measure_segmentation_to_object(self):
50
73
if segmentation1_data is None or segmentation2_data is None :
51
74
show_info ("Please choose both segmentation layers." )
52
75
return
53
- # get save_path
54
76
55
77
(distances ,
56
78
endpoints1 ,
@@ -59,18 +81,16 @@ def on_measure_segmentation_to_object(self):
59
81
segmentation = segmentation1_data ,
60
82
segmented_object = segmentation2_data ,
61
83
distance_type = "boundary" ,
62
- # save_path=self.save_path
63
84
)
85
+
64
86
if self .save_path .text () != "" :
65
- # save to csv
66
- header = "distances endpoints1 endpoints2 seg_ids"
67
- header_list = header .split (" " )
68
- file_path = save_to_csv (
69
- self .save_path .text (),
70
- data = (distances , endpoints1 , endpoints2 , seg_ids ),
71
- header = header_list
72
- )
73
- show_info (f"Measurements saved to { file_path } " )
87
+ data = {"label" : seg_ids , "distance" : distances }
88
+ axis_names = "zyx" if endpoints1 .shape [1 ] == 3 else "yx"
89
+ data .update ({f"begin-{ ax } " : endpoints1 [:, i ] for i , ax in enumerate (axis_names )})
90
+ data .update ({f"end-{ ax } " : endpoints2 [:, i ] for i , ax in enumerate (axis_names )})
91
+ data = pd .DataFrame (data )
92
+ file_path = _save_distance_table (self .save_path .text (), data )
93
+
74
94
lines , properties = distance_measurements .create_object_distance_lines (
75
95
distances = distances ,
76
96
endpoints1 = endpoints1 ,
@@ -79,19 +99,23 @@ def on_measure_segmentation_to_object(self):
79
99
)
80
100
81
101
# Add the lines layer
82
- self .viewer .add_shapes (
102
+ line_layer = self .viewer .add_shapes (
83
103
lines ,
84
104
name = "Distance Lines" ,
85
105
shape_type = "line" , # Specify the shape type as 'line'
86
106
edge_width = 2 ,
87
107
edge_color = "red" ,
88
108
blending = "additive" , # Use 'additive' for blending if needed
89
109
)
110
+
111
+ # FIXME: this doesn't work yet
112
+ if add_table is not None :
113
+ add_table (line_layer , self .viewer )
114
+
90
115
if self .save_path .text () != "" :
91
116
show_info (f"Added distance lines and saved file to { file_path } ." )
92
117
else :
93
118
show_info ("Added distance lines." )
94
- return
95
119
96
120
def on_measure_pairwise (self ):
97
121
if self .image is None :
@@ -120,14 +144,8 @@ def _create_settings_widget(self):
120
144
# setting_values.setToolTip(get_tooltip("embedding", "settings"))
121
145
setting_values .setLayout (QVBoxLayout ())
122
146
123
- self .save_path , layout = self ._add_path_param (
124
- name = "Save Directory" , select_type = "directory" , value = ""
125
- )
147
+ self .save_path , layout = self ._add_path_param (name = "Save Table" , select_type = "file" , value = "" )
126
148
setting_values .layout ().addLayout (layout )
127
149
128
150
settings = self ._make_collapsible (widget = setting_values , title = "Advanced Settings" )
129
151
return settings
130
-
131
-
132
- def get_distance_measure_widget ():
133
- return DistanceMeasureWidget ()
0 commit comments