nettracer3d 0.7.2__py3-none-any.whl → 0.7.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nettracer3d/nettracer.py +24 -5
- nettracer3d/nettracer_gui.py +99 -67
- nettracer3d/segmenter - Copy.py +2097 -0
- nettracer3d/segmenter.py +75 -668
- nettracer3d/segmenter_GPU.py +611 -0
- {nettracer3d-0.7.2.dist-info → nettracer3d-0.7.4.dist-info}/METADATA +6 -7
- {nettracer3d-0.7.2.dist-info → nettracer3d-0.7.4.dist-info}/RECORD +11 -9
- {nettracer3d-0.7.2.dist-info → nettracer3d-0.7.4.dist-info}/WHEEL +1 -1
- {nettracer3d-0.7.2.dist-info → nettracer3d-0.7.4.dist-info}/entry_points.txt +0 -0
- {nettracer3d-0.7.2.dist-info → nettracer3d-0.7.4.dist-info}/licenses/LICENSE +0 -0
- {nettracer3d-0.7.2.dist-info → nettracer3d-0.7.4.dist-info}/top_level.txt +0 -0
nettracer3d/nettracer.py
CHANGED
|
@@ -2913,6 +2913,7 @@ class Network_3D:
|
|
|
2913
2913
|
|
|
2914
2914
|
if file_path is not None:
|
|
2915
2915
|
self._node_centroids = network_analysis.read_centroids_to_dict(file_path)
|
|
2916
|
+
self._node_centroids = self.clear_null(self._node_centroids)
|
|
2916
2917
|
print("Succesfully loaded node centroids")
|
|
2917
2918
|
return
|
|
2918
2919
|
|
|
@@ -2922,10 +2923,12 @@ class Network_3D:
|
|
|
2922
2923
|
if item == 'node_centroids.xlsx' or item == 'node_centroids.csv':
|
|
2923
2924
|
if directory is not None:
|
|
2924
2925
|
self._node_centroids = network_analysis.read_centroids_to_dict(f'{directory}/{item}')
|
|
2926
|
+
self._node_centroids = self.clear_null(self._node_centroids)
|
|
2925
2927
|
print("Succesfully loaded node centroids")
|
|
2926
2928
|
return
|
|
2927
2929
|
else:
|
|
2928
2930
|
self._node_centroids = network_analysis.read_centroids_to_dict(item)
|
|
2931
|
+
self._node_centroids = self.clear_null(self._node_centroids)
|
|
2929
2932
|
print("Succesfully loaded node centroids")
|
|
2930
2933
|
return
|
|
2931
2934
|
|
|
@@ -2941,6 +2944,7 @@ class Network_3D:
|
|
|
2941
2944
|
|
|
2942
2945
|
if file_path is not None:
|
|
2943
2946
|
self._node_identities = network_analysis.read_excel_to_singval_dict(file_path)
|
|
2947
|
+
self._node_identities = self.clear_null(self._node_identities)
|
|
2944
2948
|
print("Succesfully loaded node identities")
|
|
2945
2949
|
return
|
|
2946
2950
|
|
|
@@ -2950,10 +2954,12 @@ class Network_3D:
|
|
|
2950
2954
|
if item == 'node_identities.xlsx' or item == 'node_identities.csv':
|
|
2951
2955
|
if directory is not None:
|
|
2952
2956
|
self._node_identities = network_analysis.read_excel_to_singval_dict(f'{directory}/{item}')
|
|
2957
|
+
self._node_identities = self.clear_null(self._node_identities)
|
|
2953
2958
|
print("Succesfully loaded node identities")
|
|
2954
2959
|
return
|
|
2955
2960
|
else:
|
|
2956
2961
|
self._node_identities = network_analysis.read_excel_to_singval_dict(item)
|
|
2962
|
+
self._node_identities = self.clear_null(self._node_identities)
|
|
2957
2963
|
print("Succesfully loaded node identities")
|
|
2958
2964
|
return
|
|
2959
2965
|
|
|
@@ -2968,7 +2974,8 @@ class Network_3D:
|
|
|
2968
2974
|
"""
|
|
2969
2975
|
|
|
2970
2976
|
if file_path is not None:
|
|
2971
|
-
self.
|
|
2977
|
+
self._communities = network_analysis.read_excel_to_singval_dict(file_path)
|
|
2978
|
+
self._communities = self.clear_null(self._communities)
|
|
2972
2979
|
print("Succesfully loaded communities")
|
|
2973
2980
|
return
|
|
2974
2981
|
|
|
@@ -2978,15 +2985,23 @@ class Network_3D:
|
|
|
2978
2985
|
if item == 'node_communities.xlsx' or item == 'node_communities.csv':
|
|
2979
2986
|
if directory is not None:
|
|
2980
2987
|
self._communities = network_analysis.read_excel_to_singval_dict(f'{directory}/{item}')
|
|
2988
|
+
self._communities = self.clear_null(self._communities)
|
|
2981
2989
|
print("Succesfully loaded communities")
|
|
2982
2990
|
return
|
|
2983
2991
|
else:
|
|
2984
2992
|
self._communities = network_analysis.read_excel_to_singval_dict(item)
|
|
2993
|
+
self._communities = self.clear_null(self._communities)
|
|
2985
2994
|
print("Succesfully loaded communities")
|
|
2986
2995
|
return
|
|
2987
2996
|
|
|
2988
2997
|
print("Could not find communities. They must be in the specified directory and named 'node_communities.xlsx'")
|
|
2989
2998
|
|
|
2999
|
+
def clear_null(self, some_dict):
|
|
3000
|
+
|
|
3001
|
+
if some_dict == {}:
|
|
3002
|
+
some_dict = None
|
|
3003
|
+
return some_dict
|
|
3004
|
+
|
|
2990
3005
|
def load_edge_centroids(self, directory = None, file_path = None):
|
|
2991
3006
|
"""
|
|
2992
3007
|
Can be called on a Network_3D object to load a .xlsx into the edge_centroids property as a dictionary. It will look for a file called 'edge_centroids.xlsx' in the specified directory,
|
|
@@ -2997,6 +3012,7 @@ class Network_3D:
|
|
|
2997
3012
|
|
|
2998
3013
|
if file_path is not None:
|
|
2999
3014
|
self._edge_centroids = network_analysis.read_centroids_to_dict(file_path)
|
|
3015
|
+
self._edge_centroids = self.clear_null(self._edge_centroids)
|
|
3000
3016
|
print("Succesfully loaded edge centroids")
|
|
3001
3017
|
return
|
|
3002
3018
|
|
|
@@ -3006,10 +3022,12 @@ class Network_3D:
|
|
|
3006
3022
|
if item == 'edge_centroids.xlsx' or item == 'edge_centroids.csv':
|
|
3007
3023
|
if directory is not None:
|
|
3008
3024
|
self._edge_centroids = network_analysis.read_centroids_to_dict(f'{directory}/{item}')
|
|
3025
|
+
self._edge_centroids = self.clear_null(self._edge_centroids)
|
|
3009
3026
|
print("Succesfully loaded edge centroids")
|
|
3010
3027
|
return
|
|
3011
3028
|
else:
|
|
3012
3029
|
self._edge_centroids = network_analysis.read_centroids_to_dict(item)
|
|
3030
|
+
self._edge_centroids = self.clear_null(self._edge_centroids)
|
|
3013
3031
|
print("Succesfully loaded edge centroids")
|
|
3014
3032
|
return
|
|
3015
3033
|
|
|
@@ -3427,10 +3445,11 @@ class Network_3D:
|
|
|
3427
3445
|
self.calculate_search_region(search, GPU = GPU, fast_dil = fast_dil, GPU_downsample = GPU_downsample)
|
|
3428
3446
|
#self._nodes = None # I originally put this here to micromanage RAM a little bit (it writes it to disk so I wanted to purge it from mem briefly but now idt thats necessary and I'd rather give it flexibility when lacking write permissions)
|
|
3429
3447
|
search = None
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3448
|
+
if directory is not None:
|
|
3449
|
+
try:
|
|
3450
|
+
self.save_search_region(directory)
|
|
3451
|
+
except:
|
|
3452
|
+
pass
|
|
3434
3453
|
|
|
3435
3454
|
self.calculate_edges(edges, diledge = diledge, inners = inners, hash_inner_edges = hash_inners, search = search, remove_edgetrunk = remove_trunk, GPU = GPU, fast_dil = fast_dil, skeletonized = skeletonize)
|
|
3436
3455
|
del edges
|
nettracer3d/nettracer_gui.py
CHANGED
|
@@ -25,7 +25,7 @@ import multiprocessing as mp
|
|
|
25
25
|
from concurrent.futures import ThreadPoolExecutor
|
|
26
26
|
from functools import partial
|
|
27
27
|
from nettracer3d import segmenter
|
|
28
|
-
|
|
28
|
+
from nettracer3d import segmenter_GPU
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
|
|
@@ -2870,67 +2870,72 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2870
2870
|
return val
|
|
2871
2871
|
return val
|
|
2872
2872
|
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
})
|
|
2878
|
-
|
|
2879
|
-
# Format floating point numbers
|
|
2880
|
-
df[metric] = df[metric].apply(lambda x: f"{x:.3f}" if isinstance(x, (float, np.float64)) else str(x))
|
|
2881
|
-
|
|
2882
|
-
else: # Dictionary input
|
|
2883
|
-
# Get sample value to determine structure
|
|
2884
|
-
sample_value = next(iter(data.values()))
|
|
2885
|
-
is_multi_value = isinstance(sample_value, (list, tuple, np.ndarray))
|
|
2886
|
-
|
|
2887
|
-
if is_multi_value:
|
|
2888
|
-
# Handle multi-value case
|
|
2889
|
-
if isinstance(value, str):
|
|
2890
|
-
# If single string provided for multi-values, generate numbered headers
|
|
2891
|
-
n_cols = len(sample_value)
|
|
2892
|
-
value_headers = [f"{value}_{i+1}" for i in range(n_cols)]
|
|
2893
|
-
else:
|
|
2894
|
-
# Use provided list of headers
|
|
2895
|
-
value_headers = value
|
|
2896
|
-
if len(value_headers) != len(sample_value):
|
|
2897
|
-
raise ValueError("Number of headers must match number of values per key")
|
|
2898
|
-
|
|
2899
|
-
# Create lists for each column
|
|
2900
|
-
dict_data = {metric: list(data.keys())}
|
|
2901
|
-
for i, header in enumerate(value_headers):
|
|
2902
|
-
# Convert values to numeric when possible before adding to DataFrame
|
|
2903
|
-
dict_data[header] = [convert_to_numeric(data[key][i]) for key in data.keys()]
|
|
2904
|
-
|
|
2905
|
-
df = pd.DataFrame(dict_data)
|
|
2906
|
-
|
|
2907
|
-
# Format floating point numbers in all value columns
|
|
2908
|
-
for header in value_headers:
|
|
2909
|
-
df[header] = df[header].apply(lambda x: f"{x:.3f}" if isinstance(x, (float, np.float64)) else str(x))
|
|
2910
|
-
|
|
2911
|
-
else:
|
|
2912
|
-
# Single-value case
|
|
2873
|
+
try:
|
|
2874
|
+
|
|
2875
|
+
if isinstance(data, (list, tuple, np.ndarray)):
|
|
2876
|
+
# Handle list input - create single column DataFrame
|
|
2913
2877
|
df = pd.DataFrame({
|
|
2914
|
-
metric:
|
|
2915
|
-
value: [convert_to_numeric(val) for val in data.values()]
|
|
2878
|
+
metric: [convert_to_numeric(val) for val in data]
|
|
2916
2879
|
})
|
|
2917
2880
|
|
|
2918
2881
|
# Format floating point numbers
|
|
2919
|
-
df[
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2882
|
+
df[metric] = df[metric].apply(lambda x: f"{x:.3f}" if isinstance(x, (float, np.float64)) else str(x))
|
|
2883
|
+
|
|
2884
|
+
else: # Dictionary input
|
|
2885
|
+
# Get sample value to determine structure
|
|
2886
|
+
sample_value = next(iter(data.values()))
|
|
2887
|
+
is_multi_value = isinstance(sample_value, (list, tuple, np.ndarray))
|
|
2888
|
+
|
|
2889
|
+
if is_multi_value:
|
|
2890
|
+
# Handle multi-value case
|
|
2891
|
+
if isinstance(value, str):
|
|
2892
|
+
# If single string provided for multi-values, generate numbered headers
|
|
2893
|
+
n_cols = len(sample_value)
|
|
2894
|
+
value_headers = [f"{value}_{i+1}" for i in range(n_cols)]
|
|
2895
|
+
else:
|
|
2896
|
+
# Use provided list of headers
|
|
2897
|
+
value_headers = value
|
|
2898
|
+
if len(value_headers) != len(sample_value):
|
|
2899
|
+
raise ValueError("Number of headers must match number of values per key")
|
|
2900
|
+
|
|
2901
|
+
# Create lists for each column
|
|
2902
|
+
dict_data = {metric: list(data.keys())}
|
|
2903
|
+
for i, header in enumerate(value_headers):
|
|
2904
|
+
# Convert values to numeric when possible before adding to DataFrame
|
|
2905
|
+
dict_data[header] = [convert_to_numeric(data[key][i]) for key in data.keys()]
|
|
2906
|
+
|
|
2907
|
+
df = pd.DataFrame(dict_data)
|
|
2908
|
+
|
|
2909
|
+
# Format floating point numbers in all value columns
|
|
2910
|
+
for header in value_headers:
|
|
2911
|
+
df[header] = df[header].apply(lambda x: f"{x:.3f}" if isinstance(x, (float, np.float64)) else str(x))
|
|
2912
|
+
|
|
2913
|
+
else:
|
|
2914
|
+
# Single-value case
|
|
2915
|
+
df = pd.DataFrame({
|
|
2916
|
+
metric: data.keys(),
|
|
2917
|
+
value: [convert_to_numeric(val) for val in data.values()]
|
|
2918
|
+
})
|
|
2919
|
+
|
|
2920
|
+
# Format floating point numbers
|
|
2921
|
+
df[value] = df[value].apply(lambda x: f"{x:.3f}" if isinstance(x, (float, np.float64)) else str(x))
|
|
2922
|
+
|
|
2923
|
+
# Create new table
|
|
2924
|
+
table = CustomTableView(self)
|
|
2925
|
+
table.setModel(PandasModel(df))
|
|
2926
|
+
|
|
2927
|
+
# Add to tabbed widget
|
|
2928
|
+
if title is None:
|
|
2929
|
+
self.tabbed_data.add_table(f"{metric} Analysis", table)
|
|
2930
|
+
else:
|
|
2931
|
+
self.tabbed_data.add_table(f"{title}", table)
|
|
2932
|
+
|
|
2933
|
+
# Adjust column widths to content
|
|
2934
|
+
for column in range(table.model().columnCount(None)):
|
|
2935
|
+
table.resizeColumnToContents(column)
|
|
2936
|
+
|
|
2937
|
+
except:
|
|
2938
|
+
pass
|
|
2934
2939
|
|
|
2935
2940
|
|
|
2936
2941
|
def show_watershed_dialog(self):
|
|
@@ -7412,7 +7417,11 @@ class MachineWindow(QMainWindow):
|
|
|
7412
7417
|
self.three.setCheckable(True)
|
|
7413
7418
|
self.three.setChecked(True)
|
|
7414
7419
|
self.three.clicked.connect(self.toggle_three)
|
|
7415
|
-
|
|
7420
|
+
self.GPU = QPushButton("GPU")
|
|
7421
|
+
self.GPU.setCheckable(True)
|
|
7422
|
+
self.GPU.setChecked(False)
|
|
7423
|
+
self.GPU.clicked.connect(self.toggle_GPU)
|
|
7424
|
+
processing_layout.addWidget(self.GPU)
|
|
7416
7425
|
processing_layout.addWidget(self.two)
|
|
7417
7426
|
processing_layout.addWidget(self.three)
|
|
7418
7427
|
processing_group.setLayout(processing_layout)
|
|
@@ -7550,6 +7559,35 @@ class MachineWindow(QMainWindow):
|
|
|
7550
7559
|
self.two.setChecked(True)
|
|
7551
7560
|
self.use_two = True
|
|
7552
7561
|
|
|
7562
|
+
def toggle_GPU(self):
|
|
7563
|
+
|
|
7564
|
+
if self.parent().active_channel == 0:
|
|
7565
|
+
if self.parent().channel_data[0] is not None:
|
|
7566
|
+
try:
|
|
7567
|
+
active_data = self.parent().channel_data[0]
|
|
7568
|
+
act_channel = 0
|
|
7569
|
+
except:
|
|
7570
|
+
active_data = self.parent().channel_data[1]
|
|
7571
|
+
act_channel = 1
|
|
7572
|
+
else:
|
|
7573
|
+
active_data = self.parent().channel_data[1]
|
|
7574
|
+
act_channel = 1
|
|
7575
|
+
|
|
7576
|
+
if self.GPU.isChecked():
|
|
7577
|
+
|
|
7578
|
+
try:
|
|
7579
|
+
self.segmenter = segmenter_GPU.InteractiveSegmenter(active_data)
|
|
7580
|
+
print("Using GPU")
|
|
7581
|
+
except:
|
|
7582
|
+
self.GPU.setChecked(False)
|
|
7583
|
+
print("Could not detect GPU")
|
|
7584
|
+
import traceback
|
|
7585
|
+
traceback.print_exc()
|
|
7586
|
+
|
|
7587
|
+
else:
|
|
7588
|
+
self.segmenter = segmenter.InteractiveSegmenter(active_data, use_gpu=False)
|
|
7589
|
+
print("Using CPU")
|
|
7590
|
+
|
|
7553
7591
|
|
|
7554
7592
|
|
|
7555
7593
|
def toggle_foreground(self):
|
|
@@ -7836,18 +7874,12 @@ class MachineWindow(QMainWindow):
|
|
|
7836
7874
|
self.parent().highlight_overlay = array3 #Clear this out for the segmenter to use
|
|
7837
7875
|
|
|
7838
7876
|
print("Segmenting entire volume with model...")
|
|
7839
|
-
foreground_coords, background_coords = self.segmenter.segment_volume(
|
|
7877
|
+
#foreground_coords, background_coords = self.segmenter.segment_volume(array = self.parent().highlight_overlay)
|
|
7878
|
+
self.parent().highlight_overlay = self.segmenter.segment_volume(array = self.parent().highlight_overlay)
|
|
7840
7879
|
|
|
7841
7880
|
# Clean up when done
|
|
7842
7881
|
self.segmenter.cleanup()
|
|
7843
7882
|
|
|
7844
|
-
fg_array = np.array(list(foreground_coords))
|
|
7845
|
-
if len(fg_array) > 0: # Check if we have any foreground coordinates
|
|
7846
|
-
# Unpack into separate coordinate arrays
|
|
7847
|
-
z_coords, y_coords, x_coords = fg_array[:, 0], fg_array[:, 1], fg_array[:, 2]
|
|
7848
|
-
# Assign values in a single vectorized operation
|
|
7849
|
-
self.parent().highlight_overlay[z_coords, y_coords, x_coords] = 255
|
|
7850
|
-
|
|
7851
7883
|
self.parent().load_channel(3, self.parent().highlight_overlay, True)
|
|
7852
7884
|
|
|
7853
7885
|
# Not exactly sure why we need all this but the channel buttons weren't loading like they normally do when load_channel() is called:
|