nettracer3d 0.5.2__py3-none-any.whl → 0.5.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/proximity.py CHANGED
@@ -3,6 +3,7 @@ from . import nettracer
3
3
  import multiprocessing as mp
4
4
  from concurrent.futures import ThreadPoolExecutor, as_completed
5
5
  from scipy.spatial import KDTree
6
+ from scipy import ndimage
6
7
  import concurrent.futures
7
8
  import multiprocessing as mp
8
9
  import pandas as pd
@@ -11,41 +12,35 @@ from typing import Dict, Union, Tuple, List, Optional
11
12
 
12
13
  # Related to morphological border searching:
13
14
 
14
- def get_reslice_indices(args):
15
- """Internal method used for the secondary algorithm that finds dimensions for subarrays around nodes"""
16
-
17
- indices, dilate_xy, dilate_z, array_shape = args
18
- try:
19
- max_indices = np.amax(indices, axis = 0) #Get the max/min of each index.
20
- except ValueError: #Return Nones if this error is encountered
15
+ def get_reslice_indices(slice_obj, dilate_xy, dilate_z, array_shape):
16
+ """Convert slice object to padded indices accounting for dilation and boundaries"""
17
+ if slice_obj is None:
21
18
  return None, None, None
22
- min_indices = np.amin(indices, axis = 0)
23
-
24
- z_max, y_max, x_max = max_indices[0], max_indices[1], max_indices[2]
25
-
26
- z_min, y_min, x_min = min_indices[0], min_indices[1], min_indices[2]
27
-
28
- y_max = y_max + ((dilate_xy-1)/2) + 1 #Establish dimensions of intended subarray, expanding the max/min indices to include
29
- y_min = y_min - ((dilate_xy-1)/2) - 1 #the future dilation space (by adding/subtracting half the dilation kernel for each axis)
30
- x_max = x_max + ((dilate_xy-1)/2) + 1 #an additional index is added in each direction to make sure nothing is discluded.
19
+
20
+ z_slice, y_slice, x_slice = slice_obj
21
+
22
+ # Extract min/max from slices
23
+ z_min, z_max = z_slice.start, z_slice.stop - 1
24
+ y_min, y_max = y_slice.start, y_slice.stop - 1
25
+ x_min, x_max = x_slice.start, x_slice.stop - 1
26
+
27
+ # Add dilation padding
28
+ y_max = y_max + ((dilate_xy-1)/2) + 1
29
+ y_min = y_min - ((dilate_xy-1)/2) - 1
30
+ x_max = x_max + ((dilate_xy-1)/2) + 1
31
31
  x_min = x_min - ((dilate_xy-1)/2) - 1
32
32
  z_max = z_max + ((dilate_z-1)/2) + 1
33
33
  z_min = z_min - ((dilate_z-1)/2) - 1
34
34
 
35
- if y_max > (array_shape[1] - 1): #Some if statements to make sure the subarray will not cause an indexerror
36
- y_max = (array_shape[1] - 1)
37
- if x_max > (array_shape[2] - 1):
38
- x_max = (array_shape[2] - 1)
39
- if z_max > (array_shape[0] - 1):
40
- z_max = (array_shape[0] - 1)
41
- if y_min < 0:
42
- y_min = 0
43
- if x_min < 0:
44
- x_min = 0
45
- if z_min < 0:
46
- z_min = 0
47
-
48
- y_vals = [y_min, y_max] #Return the subarray dimensions as lists
35
+ # Boundary checks
36
+ y_max = min(y_max, array_shape[1] - 1)
37
+ x_max = min(x_max, array_shape[2] - 1)
38
+ z_max = min(z_max, array_shape[0] - 1)
39
+ y_min = max(y_min, 0)
40
+ x_min = max(x_min, 0)
41
+ z_min = max(z_min, 0)
42
+
43
+ y_vals = [y_min, y_max]
49
44
  x_vals = [x_min, x_max]
50
45
  z_vals = [z_min, z_max]
51
46
 
@@ -85,40 +80,43 @@ def _get_node_node_dict(label_array, label, dilate_xy, dilate_z):
85
80
  return label_array
86
81
 
87
82
  def process_label(args):
88
- """Internal method used for the secondary algorithm to process a particular node."""
89
- nodes, label, dilate_xy, dilate_z, array_shape = args
83
+ """Modified to use pre-computed bounding boxes instead of argwhere"""
84
+ nodes, label, dilate_xy, dilate_z, array_shape, bounding_boxes = args
90
85
  print(f"Processing node {label}")
91
- indices = np.argwhere(nodes == label)
92
- if len(indices) == 0:
86
+
87
+ # Get the pre-computed bounding box for this label
88
+ slice_obj = bounding_boxes[label-1] # -1 because label numbers start at 1
89
+ if slice_obj is None:
93
90
  return None, None
94
- z_vals, y_vals, x_vals = get_reslice_indices((indices, dilate_xy, dilate_z, array_shape))
95
- if z_vals is None: #If get_reslice_indices ran into a ValueError, nothing is returned.
91
+
92
+ z_vals, y_vals, x_vals = get_reslice_indices(slice_obj, dilate_xy, dilate_z, array_shape)
93
+ if z_vals is None:
96
94
  return None, None
95
+
97
96
  sub_nodes = reslice_3d_array((nodes, z_vals, y_vals, x_vals))
98
97
  return label, sub_nodes
99
98
 
100
99
 
101
- def create_node_dictionary(nodes, num_nodes, dilate_xy, dilate_z, targets = None):
102
- """Internal method used for the secondary algorithm to process nodes in parallel."""
103
- # Initialize the dictionary to be returned
100
+ def create_node_dictionary(nodes, num_nodes, dilate_xy, dilate_z, targets=None):
101
+ """Modified to pre-compute all bounding boxes using find_objects"""
104
102
  node_dict = {}
105
-
106
103
  array_shape = nodes.shape
107
-
108
-
104
+
105
+ # Get all bounding boxes at once
106
+ bounding_boxes = ndimage.find_objects(nodes)
107
+
109
108
  # Use ThreadPoolExecutor for parallel execution
110
109
  with ThreadPoolExecutor(max_workers=mp.cpu_count()) as executor:
111
- # First parallel section to process labels
112
- # List of arguments for each parallel task
113
- args_list = [(nodes, i, dilate_xy, dilate_z, array_shape) for i in range(1, num_nodes + 1)]
110
+ # Create args list with bounding_boxes included
111
+ args_list = [(nodes, i, dilate_xy, dilate_z, array_shape, bounding_boxes)
112
+ for i in range(1, num_nodes + 1)]
114
113
 
115
114
  if targets is not None:
116
115
  args_list = [tup for tup in args_list if tup[1] in targets]
117
116
 
118
117
  results = executor.map(process_label, args_list)
119
118
 
120
-
121
- # Second parallel section to create dictionary entries
119
+ # Process results in parallel
122
120
  for label, sub_nodes in results:
123
121
  executor.submit(create_dict_entry, node_dict, label, sub_nodes, dilate_xy, dilate_z)
124
122
 
nettracer3d/segmenter.py CHANGED
@@ -401,7 +401,7 @@ class InteractiveSegmenter:
401
401
 
402
402
  return foreground, background
403
403
 
404
- def segment_volume(self, chunk_size=64, gpu=False):
404
+ def segment_volume(self, chunk_size=None, gpu=False):
405
405
  """Segment volume using parallel processing of chunks with vectorized chunk creation"""
406
406
  #Change the above chunk size to None to have it auto-compute largest chunks (not sure which is faster, 64 seems reasonable in test cases)
407
407
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: nettracer3d
3
- Version: 0.5.2
3
+ Version: 0.5.4
4
4
  Summary: Scripts for intializing and analyzing networks from segmentations of three dimensional images.
5
5
  Author-email: Liam McLaughlin <mclaughlinliam99@gmail.com>
6
6
  Project-URL: User_Tutorial, https://www.youtube.com/watch?v=cRatn5VTWDY
@@ -26,6 +26,7 @@ Requires-Dist: tifffile==2023.7.18
26
26
  Requires-Dist: qtrangeslider==0.1.5
27
27
  Requires-Dist: PyQt6==6.8.0
28
28
  Requires-Dist: scikit-learn==1.6.1
29
+ Requires-Dist: nibabel==5.2.0
29
30
  Provides-Extra: cuda11
30
31
  Requires-Dist: cupy-cuda11x; extra == "cuda11"
31
32
  Provides-Extra: cuda12
@@ -42,3 +43,11 @@ for a video tutorial on using the GUI.
42
43
  NetTracer3D is free to use/fork for academic/nonprofit use so long as citation is provided, and is available for commercial use at a fee (see license file for information).
43
44
 
44
45
  NetTracer3D was developed by Liam McLaughlin while working under Dr. Sanjay Jain at Washington University School of Medicine.
46
+
47
+ -- Version 0.5.4 updates --
48
+
49
+ 1. Added new function to GUI in image -> overlays -> color nodes/edges. Generates a rgb array corresponding to the nodes/edge labels where each node/edge (depending which array is selected) is randomly assigned a unique rgb color in an overlay channel. This can be used, for example, to color code labeled branches for easy identification of which branch is which.
50
+
51
+ 2. Improved highlight overlay general functionality (for selecting nodes/edges). Previously selecting a node/edge had the program attempting to create an equal sized array as an overlay, find all objects corresponding to the selected ones, fill those into the new highlight overlay, then overlay that image. This was understandably quite slow in big arrays where the system was wasting a lot of time searching the entire array every time something was selected. New version retains this functionality for arrays below 125 million voxels, since search time is rather manageable at that size. For larger arrays, it instead draws the highlight for the selected objects only into the current slice, rendering a new slice whenever the user scrolls in the stack (although the entire highlight overlay is still initialized as a placeholder). Functions that require the use of the entire highlight overlay (such as masking) are correspondingly updated to draw the entirety of the highlight overlay before executing (when the system has up until that point been drawing slices one at a time). This will likely be the retained behavior moving forward, although to eliminate this behavior, one can open nettracer_gui.py and set self.mini_thresh to some comically large value. The new highlight overlay seems to work effectively the same but faster in my testing although it is possible a bug slipped through, which I will fix if informed about (or if I find it myself).
52
+
53
+ 3. For the machine learning segmenter, changed the system to attempt to segment the image by chunking the array into the largest possible chunks that can be divided across all CPU cores. Previously the system split the array into 64^3 voxel sized chunks and passed those to the CPU cores until everything was processed. I am not sure which version is more efficient/faster so this is somewhat of a test. In theory the new behavior could be faster because it asking Python to interpret less stuff.
@@ -0,0 +1,21 @@
1
+ nettracer3d/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ nettracer3d/community_extractor.py,sha256=Zq8ZM595CTzeR6zLEZ4I6KvhkNfCPUReWvAKxTlaVfk,33495
3
+ nettracer3d/hub_getter.py,sha256=KiNtxdajLkwB1ftslvrh1FE1Ch9ZCFEmHSEEotwR-To,8298
4
+ nettracer3d/modularity.py,sha256=V1f3s_vGd8EuVz27mzq6ycIGr0BWIpH7c7NU4QjgAHU,30247
5
+ nettracer3d/morphology.py,sha256=yQ0GuieMVXOQpaohZlPnkEXEuCUjf8Fg352axyK8nbM,10755
6
+ nettracer3d/nettracer.py,sha256=OE95IH1TfAZvT-htv1LEhw1EpnnEpkA83R5EcGMQDQg,209828
7
+ nettracer3d/nettracer_gui.py,sha256=fOKzicFOX9z-MEf_aD1SV8P4BuLKOdi6K1pxH-tiqrk,357091
8
+ nettracer3d/network_analysis.py,sha256=MJBBjslA1k_R8ymid77U-qGSgzxFVfzGVQhE0IdhnbE,48046
9
+ nettracer3d/network_draw.py,sha256=F7fw6Pcf4qWOhdKwLmhwqWdschbDlHzwCVolQC9imeU,14117
10
+ nettracer3d/node_draw.py,sha256=k3sCTfUCJs3aH1C1q1gTNxDz9EAQbBd1hsUIJajxRx8,9823
11
+ nettracer3d/proximity.py,sha256=FnIiI_AzfXd22HwCIFIyQRZxKYJ8YscIDdPnIv-wsO4,10560
12
+ nettracer3d/run.py,sha256=xYeaAc8FCx8MuzTGyL3NR3mK7WZzffAYAH23bNRZYO4,127
13
+ nettracer3d/segmenter.py,sha256=5Dqo_q-m28cYq4ROBhc4lyWjJnYK7ETTiRXyU_QdVXU,29688
14
+ nettracer3d/simple_network.py,sha256=fP1gkDdtQcHruEZpUdasKdZeVacoLOxKhR3bY0L1CAQ,15426
15
+ nettracer3d/smart_dilate.py,sha256=Kekm6YIVlJniMvJMG6_AwwNmCqK2l4Qtvg9VzzqPKMw,24600
16
+ nettracer3d-0.5.4.dist-info/LICENSE,sha256=gM207DhJjWrxLuEWXl0Qz5ISbtWDmADfjHp3yC2XISs,888
17
+ nettracer3d-0.5.4.dist-info/METADATA,sha256=dn0Reda7mqMn5rBXKcn53Ky_K6eXNNTyi6YlICSwwg8,5305
18
+ nettracer3d-0.5.4.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
19
+ nettracer3d-0.5.4.dist-info/entry_points.txt,sha256=Nx1rr_0QhJXDBHAQg2vcqCzLMKBzSHfwy3xwGkueVyc,53
20
+ nettracer3d-0.5.4.dist-info/top_level.txt,sha256=zsYy9rZwirfCEOubolhee4TyzqBAL5gSUeFMzhFTX8c,12
21
+ nettracer3d-0.5.4.dist-info/RECORD,,
@@ -1,21 +0,0 @@
1
- nettracer3d/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- nettracer3d/community_extractor.py,sha256=8bRDJOfZhOFLtpkJVaDQrQ4O8wUywyr-EfVvW5fxyPs,31700
3
- nettracer3d/hub_getter.py,sha256=KiNtxdajLkwB1ftslvrh1FE1Ch9ZCFEmHSEEotwR-To,8298
4
- nettracer3d/modularity.py,sha256=V1f3s_vGd8EuVz27mzq6ycIGr0BWIpH7c7NU4QjgAHU,30247
5
- nettracer3d/morphology.py,sha256=CsRWB0DY-vBBlKdF9IQwgfYYZswuE7n1Iu_Osxgmxnw,13042
6
- nettracer3d/nettracer.py,sha256=hDccGy6RybSJFvY6dsN1l5eM3wKZW93CizlTvgEeyNs,209690
7
- nettracer3d/nettracer_gui.py,sha256=dQUhNeeVQCkuXrvtdUmqSs184Gr8Y-neoWPWcXhGaAo,340385
8
- nettracer3d/network_analysis.py,sha256=MJBBjslA1k_R8ymid77U-qGSgzxFVfzGVQhE0IdhnbE,48046
9
- nettracer3d/network_draw.py,sha256=F7fw6Pcf4qWOhdKwLmhwqWdschbDlHzwCVolQC9imeU,14117
10
- nettracer3d/node_draw.py,sha256=k3sCTfUCJs3aH1C1q1gTNxDz9EAQbBd1hsUIJajxRx8,9823
11
- nettracer3d/proximity.py,sha256=B1pmFegx5Wb0JKI5rvpILv2VRU09f6M2iljAQAqBja0,11059
12
- nettracer3d/run.py,sha256=xYeaAc8FCx8MuzTGyL3NR3mK7WZzffAYAH23bNRZYO4,127
13
- nettracer3d/segmenter.py,sha256=Rr5MUUWgGHYzzTy4hzbO3zzJhNIKsyjV1Qxg99Pb8QY,29686
14
- nettracer3d/simple_network.py,sha256=fP1gkDdtQcHruEZpUdasKdZeVacoLOxKhR3bY0L1CAQ,15426
15
- nettracer3d/smart_dilate.py,sha256=Kekm6YIVlJniMvJMG6_AwwNmCqK2l4Qtvg9VzzqPKMw,24600
16
- nettracer3d-0.5.2.dist-info/LICENSE,sha256=gM207DhJjWrxLuEWXl0Qz5ISbtWDmADfjHp3yC2XISs,888
17
- nettracer3d-0.5.2.dist-info/METADATA,sha256=M7khbzBNHwsNJ2KqmNKwnP1rvk1v37E6BI7dD9mQU58,2912
18
- nettracer3d-0.5.2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
19
- nettracer3d-0.5.2.dist-info/entry_points.txt,sha256=Nx1rr_0QhJXDBHAQg2vcqCzLMKBzSHfwy3xwGkueVyc,53
20
- nettracer3d-0.5.2.dist-info/top_level.txt,sha256=zsYy9rZwirfCEOubolhee4TyzqBAL5gSUeFMzhFTX8c,12
21
- nettracer3d-0.5.2.dist-info/RECORD,,