nettracer3d 0.3.3__py3-none-any.whl → 0.3.5__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 +7 -4
- nettracer3d/nettracer_gui.py +189 -153
- {nettracer3d-0.3.3.dist-info → nettracer3d-0.3.5.dist-info}/METADATA +1 -1
- {nettracer3d-0.3.3.dist-info → nettracer3d-0.3.5.dist-info}/RECORD +7 -7
- {nettracer3d-0.3.3.dist-info → nettracer3d-0.3.5.dist-info}/LICENSE +0 -0
- {nettracer3d-0.3.3.dist-info → nettracer3d-0.3.5.dist-info}/WHEEL +0 -0
- {nettracer3d-0.3.3.dist-info → nettracer3d-0.3.5.dist-info}/top_level.txt +0 -0
nettracer3d/nettracer.py
CHANGED
|
@@ -1524,7 +1524,7 @@ def skeletonize(arrayimage, directory = None):
|
|
|
1524
1524
|
|
|
1525
1525
|
return arrayimage
|
|
1526
1526
|
|
|
1527
|
-
def label_branches(array, peaks = 0, branch_removal = 0, comp_dil = 0, max_vol = 0, down_factor = None, directory = None, nodes = None, bonus_array = None, GPU = True):
|
|
1527
|
+
def label_branches(array, peaks = 0, branch_removal = 0, comp_dil = 0, max_vol = 0, down_factor = None, directory = None, nodes = None, bonus_array = None, GPU = True, arrayshape = None):
|
|
1528
1528
|
"""
|
|
1529
1529
|
Can be used to label branches a binary image. Labelled output will be saved to the active directory if none is specified. Note this works better on already thin filaments and may over-divide larger trunkish objects.
|
|
1530
1530
|
:param array: (Mandatory, string or ndarray) - If string, a path to a tif file to label. Note that the ndarray alternative is for internal use mainly and will not save its output.
|
|
@@ -1546,7 +1546,7 @@ def label_branches(array, peaks = 0, branch_removal = 0, comp_dil = 0, max_vol =
|
|
|
1546
1546
|
array = downsample(array, down_factor)
|
|
1547
1547
|
arrayshape = array.shape
|
|
1548
1548
|
else:
|
|
1549
|
-
arrayshape =
|
|
1549
|
+
arrayshape = arrayshape
|
|
1550
1550
|
|
|
1551
1551
|
|
|
1552
1552
|
if nodes is None:
|
|
@@ -1564,6 +1564,7 @@ def label_branches(array, peaks = 0, branch_removal = 0, comp_dil = 0, max_vol =
|
|
|
1564
1564
|
array = upsample_with_padding(array, down_factor, arrayshape)
|
|
1565
1565
|
|
|
1566
1566
|
|
|
1567
|
+
|
|
1567
1568
|
if nodes is None:
|
|
1568
1569
|
|
|
1569
1570
|
array = smart_dilate.smart_label(array, other_array, GPU = GPU)
|
|
@@ -1594,7 +1595,7 @@ def label_branches(array, peaks = 0, branch_removal = 0, comp_dil = 0, max_vol =
|
|
|
1594
1595
|
|
|
1595
1596
|
return array
|
|
1596
1597
|
|
|
1597
|
-
def label_vertices(array, peaks = 0, branch_removal = 0, comp_dil = 0, max_vol = 0, down_factor = 0, directory = None, return_skele = False):
|
|
1598
|
+
def label_vertices(array, peaks = 0, branch_removal = 0, comp_dil = 0, max_vol = 0, down_factor = 0, directory = None, return_skele = False, order = 0):
|
|
1598
1599
|
"""
|
|
1599
1600
|
Can be used to label vertices (where multiple branches connect) a binary image. Labelled output will be saved to the active directory if none is specified. Note this works better on already thin filaments and may over-divide larger trunkish objects.
|
|
1600
1601
|
Note that this can be used in tandem with an edge segmentation to create an image containing 'pseudo-nodes', meaning we can make a network out of just a single edge file.
|
|
@@ -1616,7 +1617,9 @@ def label_vertices(array, peaks = 0, branch_removal = 0, comp_dil = 0, max_vol =
|
|
|
1616
1617
|
|
|
1617
1618
|
if down_factor > 0:
|
|
1618
1619
|
array_shape = array.shape
|
|
1619
|
-
array = downsample(array, down_factor)
|
|
1620
|
+
array = downsample(array, down_factor, order)
|
|
1621
|
+
if order == 3:
|
|
1622
|
+
array = binarize(array)
|
|
1620
1623
|
|
|
1621
1624
|
array = array > 0
|
|
1622
1625
|
|
nettracer3d/nettracer_gui.py
CHANGED
|
@@ -476,7 +476,7 @@ class ImageViewerWindow(QMainWindow):
|
|
|
476
476
|
return np.vstack(chunk_results)
|
|
477
477
|
|
|
478
478
|
# Process nodes and edges in parallel using multiprocessing
|
|
479
|
-
with ThreadPoolExecutor(max_workers=
|
|
479
|
+
with ThreadPoolExecutor(max_workers=2) as executor:
|
|
480
480
|
future_nodes = executor.submit(process_channel, self.channel_data[0], node_indices, full_shape)
|
|
481
481
|
future_edges = executor.submit(process_channel, self.channel_data[1], edge_indices, full_shape)
|
|
482
482
|
future_overlay1 = executor.submit(process_channel, self.channel_data[2], overlay1_indices, full_shape)
|
|
@@ -2514,17 +2514,10 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2514
2514
|
msg.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
|
|
2515
2515
|
return msg.exec() == QMessageBox.StandardButton.Yes
|
|
2516
2516
|
|
|
2517
|
-
def load_channel(self, channel_index, channel_data=None, data=False, assign_shape = True
|
|
2517
|
+
def load_channel(self, channel_index, channel_data=None, data=False, assign_shape = True):
|
|
2518
2518
|
"""Load a channel and enable active channel selection if needed."""
|
|
2519
2519
|
|
|
2520
2520
|
try:
|
|
2521
|
-
# Store current zoom limits if they exist and weren't provided
|
|
2522
|
-
if preserve_zoom is None and hasattr(self, 'ax'):
|
|
2523
|
-
current_xlim = self.ax.get_xlim() if self.ax.get_xlim() != (0, 1) else None
|
|
2524
|
-
current_ylim = self.ax.get_ylim() if self.ax.get_ylim() != (0, 1) else None
|
|
2525
|
-
else:
|
|
2526
|
-
current_xlim, current_ylim = preserve_zoom if preserve_zoom else (None, None)
|
|
2527
|
-
|
|
2528
2521
|
if not data: # For solo loading
|
|
2529
2522
|
import tifffile
|
|
2530
2523
|
filename, _ = QFileDialog.getOpenFileName(
|
|
@@ -2605,13 +2598,7 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2605
2598
|
if assign_shape: #keep original shape tracked to undo resampling.
|
|
2606
2599
|
self.original_shape = self.channel_data[channel_index].shape
|
|
2607
2600
|
|
|
2608
|
-
|
|
2609
|
-
if current_xlim is not None and current_ylim is not None:
|
|
2610
|
-
self.ax.set_xlim(current_xlim)
|
|
2611
|
-
self.ax.set_ylim(current_ylim)
|
|
2612
|
-
self.update_display(preserve_zoom = (current_xlim, current_ylim))
|
|
2613
|
-
else:
|
|
2614
|
-
self.update_display()
|
|
2601
|
+
self.update_display()
|
|
2615
2602
|
|
|
2616
2603
|
|
|
2617
2604
|
|
|
@@ -2835,137 +2822,160 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2835
2822
|
self.channel_brightness[channel_index]['max'] = max_val / 255
|
|
2836
2823
|
self.update_display(preserve_zoom = (current_xlim, current_ylim))
|
|
2837
2824
|
|
|
2838
|
-
def update_display(self, preserve_zoom=None):
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2825
|
+
def update_display(self, preserve_zoom=None, dims = None):
|
|
2826
|
+
"""Update the display with currently visible channels and highlight overlay."""
|
|
2827
|
+
|
|
2828
|
+
self.figure.clear()
|
|
2829
|
+
|
|
2830
|
+
# Get active channels and their dimensions
|
|
2831
|
+
active_channels = [i for i in range(4) if self.channel_data[i] is not None]
|
|
2832
|
+
if dims is None:
|
|
2833
|
+
if active_channels:
|
|
2834
|
+
dims = [(self.channel_data[i].shape[1:3] if len(self.channel_data[i].shape) >= 3 else
|
|
2835
|
+
self.channel_data[i].shape) for i in active_channels]
|
|
2836
|
+
min_height = min(d[0] for d in dims)
|
|
2837
|
+
min_width = min(d[1] for d in dims)
|
|
2850
2838
|
else:
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2839
|
+
min_height = 1
|
|
2840
|
+
min_width = 1
|
|
2841
|
+
else:
|
|
2842
|
+
min_height = dims[0]
|
|
2843
|
+
min_width = dims[1]
|
|
2844
|
+
|
|
2845
|
+
# Set axes limits before displaying any images
|
|
2846
|
+
self.ax.set_xlim(-0.5, min_width - 0.5)
|
|
2847
|
+
self.ax.set_ylim(min_height - 0.5, -0.5)
|
|
2848
|
+
|
|
2849
|
+
# Create subplot with tight layout and white figure background
|
|
2850
|
+
self.figure.patch.set_facecolor('white')
|
|
2851
|
+
self.ax = self.figure.add_subplot(111)
|
|
2852
|
+
|
|
2853
|
+
# Store current zoom limits if they exist and weren't provided
|
|
2854
|
+
if preserve_zoom is None and hasattr(self, 'ax'):
|
|
2855
|
+
current_xlim = self.ax.get_xlim() if self.ax.get_xlim() != (0, 1) else None
|
|
2856
|
+
current_ylim = self.ax.get_ylim() if self.ax.get_ylim() != (0, 1) else None
|
|
2857
|
+
else:
|
|
2858
|
+
current_xlim, current_ylim = preserve_zoom if preserve_zoom else (None, None)
|
|
2859
|
+
|
|
2860
|
+
# Define base colors for each channel with increased intensity
|
|
2861
|
+
base_colors = self.base_colors
|
|
2862
|
+
# Set only the axes (image area) background to black
|
|
2863
|
+
self.ax.set_facecolor('black')
|
|
2864
|
+
|
|
2865
|
+
# Display each visible channel
|
|
2866
|
+
for channel in range(4):
|
|
2867
|
+
if (self.channel_visible[channel] and
|
|
2868
|
+
self.channel_data[channel] is not None):
|
|
2869
|
+
|
|
2870
|
+
# Check if we're dealing with RGB data
|
|
2871
|
+
is_rgb = len(self.channel_data[channel].shape) == 4 and (self.channel_data[channel].shape[-1] == 3 or self.channel_data[channel].shape[-1] == 4)
|
|
2872
|
+
|
|
2873
|
+
if len(self.channel_data[channel].shape) == 3 and not is_rgb:
|
|
2874
|
+
current_image = self.channel_data[channel][self.current_slice, :, :]
|
|
2875
|
+
elif is_rgb:
|
|
2876
|
+
current_image = self.channel_data[channel][self.current_slice] # Already has RGB channels
|
|
2877
|
+
else:
|
|
2878
|
+
current_image = self.channel_data[channel]
|
|
2872
2879
|
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2880
|
+
if is_rgb and self.channel_data[channel].shape[-1] == 3:
|
|
2881
|
+
# For RGB images, just display directly without colormap
|
|
2882
|
+
self.ax.imshow(current_image,
|
|
2883
|
+
alpha=0.7)
|
|
2884
|
+
elif is_rgb and self.channel_data[channel].shape[-1] == 4:
|
|
2885
|
+
self.ax.imshow(current_image) #For images that already have an alpha value and RGB, don't update alpha
|
|
2879
2886
|
|
|
2887
|
+
else:
|
|
2888
|
+
# Regular channel processing with colormap
|
|
2889
|
+
# Calculate brightness/contrast limits from entire volume
|
|
2890
|
+
img_min = self.min_max[channel][0]
|
|
2891
|
+
img_max = self.min_max[channel][1]
|
|
2892
|
+
|
|
2893
|
+
# Calculate vmin and vmax, ensuring we don't get a zero range
|
|
2894
|
+
if img_min == img_max:
|
|
2895
|
+
vmin = img_min
|
|
2896
|
+
vmax = img_min + 1
|
|
2880
2897
|
else:
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
normalized_image = np.zeros_like(current_image)
|
|
2897
|
-
else:
|
|
2898
|
-
normalized_image = np.clip((current_image - vmin) / (vmax - vmin), 0, 1)
|
|
2899
|
-
|
|
2900
|
-
# Create custom colormap with higher intensity
|
|
2901
|
-
color = base_colors[channel]
|
|
2902
|
-
custom_cmap = LinearSegmentedColormap.from_list(
|
|
2903
|
-
f'custom_{channel}',
|
|
2904
|
-
[(0,0,0,0), (*color,1)]
|
|
2905
|
-
)
|
|
2906
|
-
|
|
2907
|
-
# Display the image with slightly higher alpha
|
|
2908
|
-
self.ax.imshow(normalized_image,
|
|
2909
|
-
alpha=0.7,
|
|
2910
|
-
cmap=custom_cmap,
|
|
2911
|
-
vmin=0,
|
|
2912
|
-
vmax=1)
|
|
2913
|
-
|
|
2914
|
-
# Rest of the code remains the same...
|
|
2915
|
-
# Add highlight overlay if it exists
|
|
2916
|
-
if self.highlight_overlay is not None:
|
|
2917
|
-
highlight_slice = self.highlight_overlay[self.current_slice]
|
|
2918
|
-
highlight_cmap = LinearSegmentedColormap.from_list(
|
|
2919
|
-
'highlight',
|
|
2920
|
-
[(0, 0, 0, 0), (1, 1, 0, 1)] # yellow
|
|
2921
|
-
)
|
|
2922
|
-
self.ax.imshow(highlight_slice,
|
|
2923
|
-
cmap=highlight_cmap,
|
|
2924
|
-
alpha=0.5)
|
|
2925
|
-
|
|
2926
|
-
# Restore zoom limits if they existed
|
|
2927
|
-
if current_xlim is not None and current_ylim is not None:
|
|
2928
|
-
self.ax.set_xlim(current_xlim)
|
|
2929
|
-
self.ax.set_ylim(current_ylim)
|
|
2930
|
-
|
|
2931
|
-
# Style the axes
|
|
2932
|
-
self.ax.set_xlabel('X')
|
|
2933
|
-
self.ax.set_ylabel('Y')
|
|
2934
|
-
self.ax.set_title(f'Slice {self.current_slice}')
|
|
2935
|
-
|
|
2936
|
-
# Make axis labels and ticks black for visibility against white background
|
|
2937
|
-
self.ax.xaxis.label.set_color('black')
|
|
2938
|
-
self.ax.yaxis.label.set_color('black')
|
|
2939
|
-
self.ax.title.set_color('black')
|
|
2940
|
-
self.ax.tick_params(colors='black')
|
|
2941
|
-
for spine in self.ax.spines.values():
|
|
2942
|
-
spine.set_color('black')
|
|
2943
|
-
|
|
2944
|
-
# Adjust the layout to ensure the plot fits well in the figure
|
|
2945
|
-
self.figure.tight_layout()
|
|
2946
|
-
|
|
2947
|
-
# Redraw measurement points and their labels
|
|
2948
|
-
for point in self.measurement_points:
|
|
2949
|
-
x1, y1, z1 = point['point1']
|
|
2950
|
-
x2, y2, z2 = point['point2']
|
|
2951
|
-
pair_idx = point['pair_index']
|
|
2952
|
-
|
|
2953
|
-
# Draw points and labels if they're on current slice
|
|
2954
|
-
if z1 == self.current_slice:
|
|
2955
|
-
self.ax.plot(x1, y1, 'yo', markersize=8)
|
|
2956
|
-
self.ax.text(x1, y1+5, str(pair_idx),
|
|
2957
|
-
color='white', ha='center', va='bottom')
|
|
2958
|
-
if z2 == self.current_slice:
|
|
2959
|
-
self.ax.plot(x2, y2, 'yo', markersize=8)
|
|
2960
|
-
self.ax.text(x2, y2+5, str(pair_idx),
|
|
2961
|
-
color='white', ha='center', va='bottom')
|
|
2898
|
+
vmin = img_min + (img_max - img_min) * self.channel_brightness[channel]['min']
|
|
2899
|
+
vmax = img_min + (img_max - img_min) * self.channel_brightness[channel]['max']
|
|
2900
|
+
|
|
2901
|
+
# Normalize the image safely
|
|
2902
|
+
if vmin == vmax:
|
|
2903
|
+
normalized_image = np.zeros_like(current_image)
|
|
2904
|
+
else:
|
|
2905
|
+
normalized_image = np.clip((current_image - vmin) / (vmax - vmin), 0, 1)
|
|
2906
|
+
|
|
2907
|
+
# Create custom colormap with higher intensity
|
|
2908
|
+
color = base_colors[channel]
|
|
2909
|
+
custom_cmap = LinearSegmentedColormap.from_list(
|
|
2910
|
+
f'custom_{channel}',
|
|
2911
|
+
[(0,0,0,0), (*color,1)]
|
|
2912
|
+
)
|
|
2962
2913
|
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2914
|
+
# Display the image with slightly higher alpha
|
|
2915
|
+
self.ax.imshow(normalized_image,
|
|
2916
|
+
alpha=0.7,
|
|
2917
|
+
cmap=custom_cmap,
|
|
2918
|
+
vmin=0,
|
|
2919
|
+
vmax=1,
|
|
2920
|
+
extent=(-0.5, min_width-0.5, min_height-0.5, -0.5))
|
|
2921
|
+
|
|
2922
|
+
# Add highlight overlay if it exists
|
|
2923
|
+
if self.highlight_overlay is not None:
|
|
2924
|
+
highlight_slice = self.highlight_overlay[self.current_slice]
|
|
2925
|
+
highlight_cmap = LinearSegmentedColormap.from_list(
|
|
2926
|
+
'highlight',
|
|
2927
|
+
[(0, 0, 0, 0), (1, 1, 0, 1)] # yellow
|
|
2928
|
+
)
|
|
2929
|
+
self.ax.imshow(highlight_slice,
|
|
2930
|
+
cmap=highlight_cmap,
|
|
2931
|
+
alpha=0.5)
|
|
2932
|
+
|
|
2933
|
+
# Restore zoom limits if they existed
|
|
2934
|
+
if current_xlim is not None and current_ylim is not None:
|
|
2935
|
+
self.ax.set_xlim(current_xlim)
|
|
2936
|
+
self.ax.set_ylim(current_ylim)
|
|
2937
|
+
|
|
2938
|
+
# Style the axes
|
|
2939
|
+
self.ax.set_xlabel('X')
|
|
2940
|
+
self.ax.set_ylabel('Y')
|
|
2941
|
+
self.ax.set_title(f'Slice {self.current_slice}')
|
|
2942
|
+
|
|
2943
|
+
# Make axis labels and ticks black for visibility against white background
|
|
2944
|
+
self.ax.xaxis.label.set_color('black')
|
|
2945
|
+
self.ax.yaxis.label.set_color('black')
|
|
2946
|
+
self.ax.title.set_color('black')
|
|
2947
|
+
self.ax.tick_params(colors='black')
|
|
2948
|
+
for spine in self.ax.spines.values():
|
|
2949
|
+
spine.set_color('black')
|
|
2950
|
+
|
|
2951
|
+
# Adjust the layout to ensure the plot fits well in the figure
|
|
2952
|
+
self.figure.tight_layout()
|
|
2953
|
+
|
|
2954
|
+
# Redraw measurement points and their labels
|
|
2955
|
+
for point in self.measurement_points:
|
|
2956
|
+
x1, y1, z1 = point['point1']
|
|
2957
|
+
x2, y2, z2 = point['point2']
|
|
2958
|
+
pair_idx = point['pair_index']
|
|
2959
|
+
|
|
2960
|
+
# Draw points and labels if they're on current slice
|
|
2961
|
+
if z1 == self.current_slice:
|
|
2962
|
+
self.ax.plot(x1, y1, 'yo', markersize=8)
|
|
2963
|
+
self.ax.text(x1, y1+5, str(pair_idx),
|
|
2964
|
+
color='white', ha='center', va='bottom')
|
|
2965
|
+
if z2 == self.current_slice:
|
|
2966
|
+
self.ax.plot(x2, y2, 'yo', markersize=8)
|
|
2967
|
+
self.ax.text(x2, y2+5, str(pair_idx),
|
|
2968
|
+
color='white', ha='center', va='bottom')
|
|
2969
|
+
|
|
2970
|
+
# Draw line if both points are on current slice
|
|
2971
|
+
if z1 == z2 == self.current_slice:
|
|
2972
|
+
self.ax.plot([x1, x2], [y1, y2], 'r--', alpha=0.5)
|
|
2973
|
+
|
|
2974
|
+
if active_channels:
|
|
2975
|
+
self.ax.set_xlim(-0.5, min_width - 0.5)
|
|
2976
|
+
self.ax.set_ylim(min_height - 0.5, -0.5)
|
|
2966
2977
|
|
|
2967
|
-
|
|
2968
|
-
|
|
2978
|
+
self.canvas.draw()
|
|
2969
2979
|
def show_netshow_dialog(self):
|
|
2970
2980
|
dialog = NetShowDialog(self)
|
|
2971
2981
|
dialog.exec()
|
|
@@ -4969,6 +4979,7 @@ class ResizeDialog(QDialog):
|
|
|
4969
4979
|
resized_data = n3d.resize(self.parent().channel_data[channel], resize, order)
|
|
4970
4980
|
self.parent().load_channel(channel, channel_data=resized_data, data=True, assign_shape = False)
|
|
4971
4981
|
|
|
4982
|
+
|
|
4972
4983
|
|
|
4973
4984
|
# Process highlight overlay if it exists
|
|
4974
4985
|
if self.parent().highlight_overlay is not None:
|
|
@@ -4990,8 +5001,8 @@ class ResizeDialog(QDialog):
|
|
|
4990
5001
|
# Process highlight overlay if it exists
|
|
4991
5002
|
if self.parent().highlight_overlay is not None:
|
|
4992
5003
|
self.parent().highlight_overlay = n3d.upsample_with_padding(self.parent().highlight_overlay, original_shape = self.parent().original_shape)
|
|
4993
|
-
|
|
4994
|
-
|
|
5004
|
+
if my_network.search_region is not None:
|
|
5005
|
+
my_network.search_region = n3d.upsample_with_padding(my_network.search_region, original_shape = self.parent().original_shape)
|
|
4995
5006
|
|
|
4996
5007
|
|
|
4997
5008
|
# Update slider range based on new z-dimension
|
|
@@ -5417,7 +5428,7 @@ class DilateDialog(QDialog):
|
|
|
5417
5428
|
)
|
|
5418
5429
|
|
|
5419
5430
|
# Update both the display data and the network object
|
|
5420
|
-
self.parent().load_channel(self.parent().active_channel, result, True
|
|
5431
|
+
self.parent().load_channel(self.parent().active_channel, result, True)
|
|
5421
5432
|
|
|
5422
5433
|
self.parent().update_display(preserve_zoom=(current_xlim, current_ylim))
|
|
5423
5434
|
self.accept()
|
|
@@ -5503,7 +5514,7 @@ class ErodeDialog(QDialog):
|
|
|
5503
5514
|
)
|
|
5504
5515
|
|
|
5505
5516
|
|
|
5506
|
-
self.parent().load_channel(self.parent().active_channel, result, True
|
|
5517
|
+
self.parent().load_channel(self.parent().active_channel, result, True)
|
|
5507
5518
|
|
|
5508
5519
|
|
|
5509
5520
|
self.parent().update_display(preserve_zoom=(current_xlim, current_ylim))
|
|
@@ -5908,8 +5919,13 @@ class GenNodesDialog(QDialog):
|
|
|
5908
5919
|
if down_factor is None:
|
|
5909
5920
|
self.down_factor = QLineEdit("0")
|
|
5910
5921
|
layout.addRow("Downsample Factor (Speeds up calculation at the cost of fidelity):", self.down_factor)
|
|
5922
|
+
self.cubic = QPushButton("Cubic Downsample")
|
|
5923
|
+
self.cubic.setCheckable(True)
|
|
5924
|
+
self.cubic.setChecked(False)
|
|
5925
|
+
layout.addRow("(if downsampling): Use cubic dilation? (Slower but can preserve structure better)", self.cubic)
|
|
5911
5926
|
else:
|
|
5912
|
-
self.down_factor = down_factor
|
|
5927
|
+
self.down_factor = down_factor[0]
|
|
5928
|
+
self.cubic = down_factor[1]
|
|
5913
5929
|
|
|
5914
5930
|
self.directory = QLineEdit()
|
|
5915
5931
|
self.directory.setPlaceholderText("Leave empty to save in active dir")
|
|
@@ -5922,7 +5938,7 @@ class GenNodesDialog(QDialog):
|
|
|
5922
5938
|
self.retain.setChecked(True)
|
|
5923
5939
|
layout.addRow("Retain Original Edges? (Will be moved to overlay 2):", self.retain)
|
|
5924
5940
|
else:
|
|
5925
|
-
self.retain =
|
|
5941
|
+
self.retain = False
|
|
5926
5942
|
|
|
5927
5943
|
# Add Run button
|
|
5928
5944
|
run_button = QPushButton("Run Node Generation")
|
|
@@ -5956,17 +5972,24 @@ class GenNodesDialog(QDialog):
|
|
|
5956
5972
|
# Get down_factor
|
|
5957
5973
|
if type(self.down_factor) is int:
|
|
5958
5974
|
down_factor = self.down_factor
|
|
5975
|
+
cubic = self.cubic
|
|
5959
5976
|
else:
|
|
5960
5977
|
try:
|
|
5961
5978
|
down_factor = int(self.down_factor.text()) if self.down_factor.text() else 0
|
|
5962
5979
|
except ValueError:
|
|
5963
5980
|
down_factor = 0
|
|
5981
|
+
cubic = self.cubic.isChecked()
|
|
5964
5982
|
|
|
5965
5983
|
try:
|
|
5966
5984
|
retain = self.retain.isChecked()
|
|
5967
5985
|
except:
|
|
5968
5986
|
retain = True
|
|
5969
5987
|
|
|
5988
|
+
if cubic:
|
|
5989
|
+
order = 3
|
|
5990
|
+
else:
|
|
5991
|
+
order = 0
|
|
5992
|
+
|
|
5970
5993
|
|
|
5971
5994
|
result, skele = n3d.label_vertices(
|
|
5972
5995
|
my_network.edges,
|
|
@@ -5974,12 +5997,14 @@ class GenNodesDialog(QDialog):
|
|
|
5974
5997
|
branch_removal=branch_removal,
|
|
5975
5998
|
comp_dil=comp_dil,
|
|
5976
5999
|
down_factor=down_factor,
|
|
6000
|
+
order = order,
|
|
5977
6001
|
return_skele = True
|
|
5978
6002
|
|
|
5979
6003
|
)
|
|
5980
6004
|
|
|
5981
6005
|
if down_factor > 0 and not self.called:
|
|
5982
|
-
|
|
6006
|
+
|
|
6007
|
+
my_network.edges = n3d.downsample(my_network.edges, down_factor, order = order)
|
|
5983
6008
|
my_network.xy_scale = my_network.xy_scale * down_factor
|
|
5984
6009
|
my_network.z_scale = my_network.z_scale * down_factor
|
|
5985
6010
|
print("xy_scales and z_scales have been adjusted per downsample. Check image -> properties to manually reset them to 1 if desired.")
|
|
@@ -5995,13 +6020,14 @@ class GenNodesDialog(QDialog):
|
|
|
5995
6020
|
except:
|
|
5996
6021
|
pass
|
|
5997
6022
|
|
|
6023
|
+
self.parent().load_channel(1, channel_data = skele, data = True)
|
|
6024
|
+
|
|
6025
|
+
self.parent().load_channel(0, channel_data = result, data = True)
|
|
5998
6026
|
|
|
5999
6027
|
if retain:
|
|
6000
6028
|
self.parent().load_channel(3, channel_data = my_network.edges, data = True)
|
|
6001
6029
|
|
|
6002
|
-
self.parent().load_channel(1, channel_data = skele, data = True)
|
|
6003
6030
|
|
|
6004
|
-
self.parent().load_channel(0, channel_data = result, data = True)
|
|
6005
6031
|
|
|
6006
6032
|
self.parent().update_display()
|
|
6007
6033
|
self.accept()
|
|
@@ -6040,6 +6066,12 @@ class BranchDialog(QDialog):
|
|
|
6040
6066
|
self.down_factor = QLineEdit("0")
|
|
6041
6067
|
layout.addRow("Internal downsample (will have to recompute nodes)?:", self.down_factor)
|
|
6042
6068
|
|
|
6069
|
+
# cubic checkbox (default False)
|
|
6070
|
+
self.cubic = QPushButton("Cubic Downsample")
|
|
6071
|
+
self.cubic.setCheckable(True)
|
|
6072
|
+
self.cubic.setChecked(False)
|
|
6073
|
+
layout.addRow("(if downsampling): Use cubic dilation? (Slower but can preserve structure better)", self.cubic)
|
|
6074
|
+
|
|
6043
6075
|
# Add Run button
|
|
6044
6076
|
run_button = QPushButton("Run Branch Label")
|
|
6045
6077
|
run_button.clicked.connect(self.branch_label)
|
|
@@ -6056,25 +6088,29 @@ class BranchDialog(QDialog):
|
|
|
6056
6088
|
|
|
6057
6089
|
nodes = self.nodes.isChecked()
|
|
6058
6090
|
GPU = self.GPU.isChecked()
|
|
6091
|
+
cubic = self.cubic.isChecked()
|
|
6092
|
+
|
|
6059
6093
|
|
|
6060
6094
|
original_shape = my_network.edges.shape
|
|
6095
|
+
original_array = copy.deepcopy(my_network.edges)
|
|
6061
6096
|
|
|
6062
6097
|
if down_factor > 0:
|
|
6063
|
-
self.parent().show_gennodes_dialog(down_factor = down_factor, called = True)
|
|
6098
|
+
self.parent().show_gennodes_dialog(down_factor = [down_factor, cubic], called = True)
|
|
6064
6099
|
elif nodes:
|
|
6065
6100
|
self.parent().show_gennodes_dialog(called = True)
|
|
6066
6101
|
down_factor = None
|
|
6067
6102
|
|
|
6068
6103
|
if my_network.edges is not None and my_network.nodes is not None and my_network.id_overlay is not None:
|
|
6069
6104
|
|
|
6070
|
-
output = n3d.label_branches(my_network.edges, nodes = my_network.nodes, bonus_array =
|
|
6105
|
+
output = n3d.label_branches(my_network.edges, nodes = my_network.nodes, bonus_array = original_array, GPU = GPU, down_factor = down_factor, arrayshape = original_shape)
|
|
6071
6106
|
|
|
6072
6107
|
if down_factor is not None:
|
|
6073
6108
|
|
|
6074
|
-
self.parent().reset(nodes = True, id_overlay = True)
|
|
6109
|
+
self.parent().reset(nodes = True, id_overlay = True, edges = True)
|
|
6075
6110
|
|
|
6076
6111
|
else:
|
|
6077
6112
|
self.parent().reset(id_overlay = True)
|
|
6113
|
+
self.parent().update_display(dims = (output.shape[1], output.shape[2]))
|
|
6078
6114
|
|
|
6079
6115
|
self.parent().load_channel(1, channel_data = output, data = True)
|
|
6080
6116
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: nettracer3d
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.5
|
|
4
4
|
Summary: Scripts for intializing and analyzing networks from segmentations of three dimensional images.
|
|
5
5
|
Author-email: Liam McLaughlin <boom2449@gmail.com>
|
|
6
6
|
Project-URL: User_Manual, https://drive.google.com/drive/folders/1fTkz3n4LN9_VxKRKC8lVQSlrz_wq0bVn?usp=drive_link
|
|
@@ -3,16 +3,16 @@ nettracer3d/community_extractor.py,sha256=8bRDJOfZhOFLtpkJVaDQrQ4O8wUywyr-EfVvW5
|
|
|
3
3
|
nettracer3d/hub_getter.py,sha256=KiNtxdajLkwB1ftslvrh1FE1Ch9ZCFEmHSEEotwR-To,8298
|
|
4
4
|
nettracer3d/modularity.py,sha256=V1f3s_vGd8EuVz27mzq6ycIGr0BWIpH7c7NU4QjgAHU,30247
|
|
5
5
|
nettracer3d/morphology.py,sha256=wv7v06YUcn5lMyefcc_znQlXF5iDxvUdoc0fXOKlGTw,12982
|
|
6
|
-
nettracer3d/nettracer.py,sha256=
|
|
7
|
-
nettracer3d/nettracer_gui.py,sha256=
|
|
6
|
+
nettracer3d/nettracer.py,sha256=NUmxpbKFg2DLKEHOqoOLNq7FB2GmBCZGRBkA3pl-KW4,206619
|
|
7
|
+
nettracer3d/nettracer_gui.py,sha256=GrsCbZn5isM28-uchfja8uAgK0Wt36FL8hw34cWUrzs,287647
|
|
8
8
|
nettracer3d/network_analysis.py,sha256=MJBBjslA1k_R8ymid77U-qGSgzxFVfzGVQhE0IdhnbE,48046
|
|
9
9
|
nettracer3d/network_draw.py,sha256=F7fw6Pcf4qWOhdKwLmhwqWdschbDlHzwCVolQC9imeU,14117
|
|
10
10
|
nettracer3d/node_draw.py,sha256=BMiD_FrlOHeGD4AQZ_Emd152PfxFuMgGf2x4S0TOTnw,9752
|
|
11
11
|
nettracer3d/proximity.py,sha256=KYs4QUbt1U79RLzTvt8BmrxeGVaeKOQ2brtzTjjA78c,11011
|
|
12
12
|
nettracer3d/simple_network.py,sha256=fP1gkDdtQcHruEZpUdasKdZeVacoLOxKhR3bY0L1CAQ,15426
|
|
13
13
|
nettracer3d/smart_dilate.py,sha256=howfO6Lw5PxNjkaOBSCjkmf7fyau_-_8iTct2mAuTAQ,22083
|
|
14
|
-
nettracer3d-0.3.
|
|
15
|
-
nettracer3d-0.3.
|
|
16
|
-
nettracer3d-0.3.
|
|
17
|
-
nettracer3d-0.3.
|
|
18
|
-
nettracer3d-0.3.
|
|
14
|
+
nettracer3d-0.3.5.dist-info/LICENSE,sha256=gM207DhJjWrxLuEWXl0Qz5ISbtWDmADfjHp3yC2XISs,888
|
|
15
|
+
nettracer3d-0.3.5.dist-info/METADATA,sha256=oFn4EtwQvzXYBL9kZ5jKaS3TORIeSMt5kvKy06PY1Xo,2894
|
|
16
|
+
nettracer3d-0.3.5.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
17
|
+
nettracer3d-0.3.5.dist-info/top_level.txt,sha256=zsYy9rZwirfCEOubolhee4TyzqBAL5gSUeFMzhFTX8c,12
|
|
18
|
+
nettracer3d-0.3.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|