nettracer3d 1.2.5__py3-none-any.whl → 1.2.7__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.
@@ -2565,10 +2565,40 @@ class ImageViewerWindow(QMainWindow):
2565
2565
  # Apply pass 2 results
2566
2566
  pass2_array = pass1_array.copy()
2567
2567
 
2568
- for illegal_label, new_label in results_pass2:
2569
- if new_label is not None and new_label != illegal_label:
2570
- # Reassign this label
2571
- pass2_array[pass1_array == illegal_label] = new_label
2568
+ def replace_labels_in_chunk(args):
2569
+ """Process a single chunk of the array"""
2570
+ pass1_chunk, results_pass2 = args
2571
+
2572
+ # Create output chunk (copy of input)
2573
+ pass2_chunk = pass1_chunk.copy()
2574
+
2575
+ # Find which labels actually exist in this chunk
2576
+ unique_labels = set(np.unique(pass1_chunk))
2577
+
2578
+ for illegal_label, new_label in results_pass2:
2579
+ if new_label is not None and new_label != illegal_label:
2580
+ # Only process if this label exists in this chunk
2581
+ if illegal_label in unique_labels:
2582
+ # Read from pass1_chunk, write to pass2_chunk
2583
+ pass2_chunk[pass1_chunk == illegal_label] = new_label
2584
+
2585
+ return pass2_chunk
2586
+
2587
+ # Get number of CPU cores
2588
+ num_cores = mp.cpu_count()
2589
+
2590
+ # Split array along y-axis (axis=1)
2591
+ chunks = np.array_split(pass1_array, num_cores, axis=1)
2592
+
2593
+ # Prepare arguments for each worker
2594
+ chunk_args = [(chunk, results_pass2) for chunk in chunks]
2595
+
2596
+ # Process chunks in parallel with threads
2597
+ with ThreadPoolExecutor(max_workers=num_cores) as executor:
2598
+ processed_chunks = list(executor.map(replace_labels_in_chunk, chunk_args))
2599
+
2600
+ # Stack results back together along y-axis
2601
+ pass2_array = np.concatenate(processed_chunks, axis=1)
2572
2602
 
2573
2603
  print(f"Pass 2 complete")
2574
2604
  return pass2_array
@@ -4927,6 +4957,8 @@ class ImageViewerWindow(QMainWindow):
4927
4957
  documentation_action.triggered.connect(self.help_me)
4928
4958
  tutorial_action = help_menu.addAction("Tutorial")
4929
4959
  tutorial_action.triggered.connect(self.start_tutorial)
4960
+ documentation_action = help_menu.addAction("Youtube")
4961
+ documentation_action.triggered.connect(self.help_me_vid)
4930
4962
 
4931
4963
  # Initialize downsample factor
4932
4964
  self.downsample_factor = 1
@@ -5189,6 +5221,16 @@ class ImageViewerWindow(QMainWindow):
5189
5221
  print(f"Error opening URL: {e}")
5190
5222
  return False
5191
5223
 
5224
+ def help_me_vid(self):
5225
+
5226
+ import webbrowser
5227
+ try:
5228
+ webbrowser.open('https://www.youtube.com/watch?v=_4uDy0mzG94&list=PLsrhxiimzKJMZ3_gTWkfrcAdJQQobUhj7')
5229
+ return True
5230
+ except Exception as e:
5231
+ print(f"Error opening URL: {e}")
5232
+ return False
5233
+
5192
5234
  def start_tutorial(self):
5193
5235
  """Open the tutorial selection dialog"""
5194
5236
  if not hasattr(self, 'tutorial_dialog'):
@@ -5588,10 +5630,13 @@ class ImageViewerWindow(QMainWindow):
5588
5630
  except:
5589
5631
  pass
5590
5632
 
5591
- def show_dilate_dialog(self, args = None):
5633
+ def show_dilate_dialog(self, args = None, execute = False):
5592
5634
  """show the dilate dialog"""
5593
5635
  dialog = DilateDialog(self, args)
5594
- dialog.show()
5636
+ if not execute:
5637
+ dialog.show()
5638
+ else:
5639
+ dialog.exec()
5595
5640
 
5596
5641
  def show_erode_dialog(self, args = None):
5597
5642
  """show the erode dialog"""
@@ -6406,12 +6451,15 @@ class ImageViewerWindow(QMainWindow):
6406
6451
  self.channel_data[channel_index] = test_channel_data
6407
6452
 
6408
6453
  elif file_extension == 'nii':
6409
- import nibabel as nib
6410
- nii_img = nib.load(filename)
6411
- # Get data and transpose to match TIFF orientation
6412
- # If X needs to become Z, we move axis 2 (X) to position 0 (Z)
6413
- arraydata = nii_img.get_fdata()
6414
- self.channel_data[channel_index] = np.transpose(arraydata, (2, 1, 0))
6454
+ try:
6455
+ import nibabel as nib
6456
+ nii_img = nib.load(filename)
6457
+ # Get data and transpose to match TIFF orientation
6458
+ # If X needs to become Z, we move axis 2 (X) to position 0 (Z)
6459
+ arraydata = nii_img.get_fdata()
6460
+ self.channel_data[channel_index] = np.transpose(arraydata, (2, 1, 0))
6461
+ except:
6462
+ return
6415
6463
 
6416
6464
  elif file_extension in ['jpg', 'jpeg', 'png']:
6417
6465
  from PIL import Image
@@ -6737,6 +6785,12 @@ class ImageViewerWindow(QMainWindow):
6737
6785
 
6738
6786
  else: # Save
6739
6787
  parent_dir = None # Let the backend handle default save location
6788
+
6789
+ if len(self.channel_data[0].shape) == 4:
6790
+ try:
6791
+ self.load_channel(0, self.reduce_rgb_dimension(self.channel_data[0], 'weight'), True)
6792
+ except:
6793
+ pass
6740
6794
 
6741
6795
  # Call appropriate save method
6742
6796
  if asbool:
@@ -8733,6 +8787,12 @@ class MergeNodeIdDialog(QDialog):
8733
8787
  layout.addRow("xy_scale:", self.xy_scale)
8734
8788
  self.z_scale = QLineEdit(f"{my_network.z_scale}")
8735
8789
  layout.addRow("z_scale:", self.z_scale)
8790
+
8791
+ self.search_mode = QComboBox()
8792
+ self.search_mode.addItems(["Standard", "Fast (May be Rougher at Handling Adjacent Expanded Borders - Use for Very Large 3D Images)"])
8793
+ self.search_mode.setCurrentIndex(0) # Default to Mode 1
8794
+ layout.addRow("Step-out algorithm:", self.search_mode)
8795
+
8736
8796
  self.mode_selector = QComboBox()
8737
8797
  self.mode_selector.addItems(["Auto-Binarize(Otsu)/Presegmented", "Manual (Interactive Thresholder)"])
8738
8798
  self.mode_selector.setCurrentIndex(1) # Default to Mode 1
@@ -8794,6 +8854,12 @@ class MergeNodeIdDialog(QDialog):
8794
8854
  data = self.parent().channel_data[0]
8795
8855
  include = self.include.isChecked()
8796
8856
  umap = True
8857
+ search_mode = self.search_mode.currentIndex()
8858
+
8859
+ if search_mode == 0:
8860
+ fast_dil = False
8861
+ else:
8862
+ fast_dil = True
8797
8863
 
8798
8864
  if data is None:
8799
8865
  return
@@ -8810,7 +8876,7 @@ class MergeNodeIdDialog(QDialog):
8810
8876
  return # User cancelled directory selection
8811
8877
 
8812
8878
  if search > 0:
8813
- data = sdl.smart_dilate(data, 1, 1, GPU=False, fast_dil=False,
8879
+ data = sdl.smart_dilate(data, fast_dil=fast_dil,
8814
8880
  use_dt_dil_amount=search, xy_scale=xy_scale, z_scale=z_scale)
8815
8881
 
8816
8882
  # Check if manual mode is selected
@@ -9421,6 +9487,10 @@ class NetShowDialog(QDialog):
9421
9487
  self.geo_layout.setCheckable(True)
9422
9488
  self.geo_layout.setChecked(False)
9423
9489
  layout.addRow("Use Geographic Layout:", self.geo_layout)
9490
+
9491
+ self.show_labels = QCheckBox("Show Node Numerical IDs?")
9492
+ self.show_labels.setChecked(True)
9493
+ layout.addRow(self.show_labels)
9424
9494
 
9425
9495
  # Add mode selection dropdown
9426
9496
  self.mode_selector = QComboBox()
@@ -9442,6 +9512,8 @@ class NetShowDialog(QDialog):
9442
9512
  def show_network(self):
9443
9513
  # Get parameters and run analysis
9444
9514
 
9515
+ show_labels = self.show_labels.isChecked()
9516
+
9445
9517
  geo = self.geo_layout.isChecked()
9446
9518
  if geo:
9447
9519
  if my_network.node_centroids is None:
@@ -9461,12 +9533,12 @@ class NetShowDialog(QDialog):
9461
9533
 
9462
9534
  try:
9463
9535
  if accepted_mode == 0:
9464
- my_network.show_network(geometric=geo, directory = directory)
9536
+ my_network.show_network(geometric=geo, show_labels = show_labels)
9465
9537
  elif accepted_mode == 1:
9466
- my_network.show_communities_flex(geometric=geo, directory = directory, weighted = weighted, partition = my_network.communities)
9538
+ my_network.show_communities_flex(geometric=geo, weighted = weighted, partition = my_network.communities, show_labels = show_labels)
9467
9539
  self.parent().format_for_upperright_table(my_network.communities, 'NodeID', 'CommunityID')
9468
9540
  elif accepted_mode == 2:
9469
- my_network.show_identity_network(geometric=geo, directory = directory)
9541
+ my_network.show_identity_network(geometric=geo, show_labels = show_labels)
9470
9542
 
9471
9543
  self.accept()
9472
9544
  except Exception as e:
@@ -10099,8 +10171,8 @@ class NeighborIdentityDialog(QDialog):
10099
10171
 
10100
10172
  self.fastdil = QPushButton("Fast Dilate")
10101
10173
  self.fastdil.setCheckable(True)
10102
- self.fastdil.setChecked(False)
10103
- #layout.addRow("(If not using network) Use Fast Dilation (Higher speed, less accurate with search regions much larger than nodes):", self.fastdil)
10174
+ self.fastdil.setChecked(True)
10175
+ layout.addRow("(If not using network) Use Fast Dilation (Parallelized):", self.fastdil)
10104
10176
 
10105
10177
  # Add Run button
10106
10178
  run_button = QPushButton("Get Neighborhood Identity Distribution")
@@ -11776,17 +11848,21 @@ class CleanDialog(QDialog):
11776
11848
  def close(self):
11777
11849
 
11778
11850
  try:
11779
- self.parent().show_dilate_dialog(args = [1])
11780
- self.parent().show_erode_dialog(args = [self.parent().last_dil])
11851
+ self.parent().show_dilate_dialog(args = [1, 0], execute = True)
11852
+ self.parent().show_erode_dialog(args = self.parent().last_dil)
11781
11853
  except:
11854
+ import traceback
11855
+ print(traceback.format_exc())
11782
11856
  pass
11783
11857
 
11784
11858
  def open(self):
11785
11859
 
11786
11860
  try:
11787
- self.parent().show_erode_dialog(args = [1])
11788
- self.parent().show_dilate_dialog(args = [self.parent().last_ero])
11861
+ self.parent().show_erode_dialog(args = [1, 0])
11862
+ self.parent().show_dilate_dialog(args = self.parent().last_ero, execute = True)
11789
11863
  except:
11864
+ import traceback
11865
+ print(traceback.format_exc())
11790
11866
  pass
11791
11867
 
11792
11868
  def holes(self):
@@ -13051,8 +13127,7 @@ class MachineWindow(QMainWindow):
13051
13127
  self.kill_segmentation()
13052
13128
  time.sleep(0.2) # Give additional time for cleanup
13053
13129
  try:
13054
- self.parent().channel_data[0] = self.parent().reduce_rgb_dimension(self.parent().channel_data[0], 'weight')
13055
- self.update_display()
13130
+ self.parent().load_channel(0, self.parent().reduce_rgb_dimension(self.parent().channel_data[0], 'weight'), True)
13056
13131
  except:
13057
13132
  pass
13058
13133
 
@@ -13613,20 +13688,11 @@ class SmartDilateDialog(QDialog):
13613
13688
 
13614
13689
  layout = QFormLayout(self)
13615
13690
 
13616
- # GPU checkbox (default True)
13617
- self.GPU = QPushButton("GPU")
13618
- self.GPU.setCheckable(True)
13619
- self.GPU.setChecked(False)
13620
- layout.addRow("Use GPU:", self.GPU)
13621
-
13622
13691
  # dt checkbox (default False)
13623
13692
  self.predt = QPushButton("Fast Dilation")
13624
13693
  self.predt.setCheckable(True)
13625
13694
  self.predt.setChecked(False)
13626
- layout.addRow("Use Fast Dilation (Higher speed, less accurate with search regions much larger than nodes):", self.predt)
13627
-
13628
- self.down_factor = QLineEdit("")
13629
- layout.addRow("Internal Downsample for GPU (if needed):", self.down_factor)
13695
+ layout.addRow("Use Fast Dilation? (Higher speed, may be rougher along adjacent boundaries):", self.predt)
13630
13696
 
13631
13697
  self.params = params
13632
13698
 
@@ -13637,14 +13703,10 @@ class SmartDilateDialog(QDialog):
13637
13703
 
13638
13704
  def smart_dilate(self):
13639
13705
 
13640
- GPU = self.GPU.isChecked()
13641
- down_factor = float(self.down_factor.text()) if self.down_factor.text().strip() else None
13642
13706
  predt = self.predt.isChecked()
13643
13707
  active_data, amount, xy_scale, z_scale = self.params
13644
13708
 
13645
- dilate_xy, dilate_z = n3d.dilation_length_to_pixels(xy_scale, z_scale, amount, amount)
13646
-
13647
- result = sdl.smart_dilate(active_data, dilate_xy, dilate_z, GPU = GPU, predownsample = down_factor, fast_dil = predt, use_dt_dil_amount = amount, xy_scale = xy_scale, z_scale = z_scale)
13709
+ result = sdl.smart_dilate(active_data, fast_dil = predt, use_dt_dil_amount = amount, xy_scale = xy_scale, z_scale = z_scale)
13648
13710
 
13649
13711
  self.parent().load_channel(self.parent().active_channel, result, True, preserve_zoom = (self.parent().ax.get_xlim(), self.parent().ax.get_ylim()))
13650
13712
  self.accept()
@@ -13661,7 +13723,10 @@ class DilateDialog(QDialog):
13661
13723
 
13662
13724
  if args:
13663
13725
  self.parent().last_dil = args[0]
13664
- self.index = 1
13726
+ if args[1] > 1:
13727
+ self.index = 1
13728
+ else:
13729
+ self.index = 0
13665
13730
  else:
13666
13731
  self.parent().last_dil = 1
13667
13732
  self.index = 0
@@ -13677,7 +13742,7 @@ class DilateDialog(QDialog):
13677
13742
 
13678
13743
  # Add mode selection dropdown
13679
13744
  self.mode_selector = QComboBox()
13680
- self.mode_selector.addItems(["Distance Transform-Based (Slower but more accurate at larger dilations)", "Preserve Labels (slower)", "Pseudo3D Binary Kernels (For Fast, small dilations)"])
13745
+ self.mode_selector.addItems(["Parallel Distance Transform-Based", "Preserve Labels (Distance Transform Based)", "Pseudo3D Binary Kernels (For Fast, small dilations for visualization purposes. Slightly inaccurate, moreso at large dilations)", "Distance Transform-Based (Non-Parallel Version)"])
13681
13746
  self.mode_selector.setCurrentIndex(self.index) # Default to Mode 1
13682
13747
  layout.addRow("Execution Mode:", self.mode_selector)
13683
13748
 
@@ -13720,7 +13785,7 @@ class DilateDialog(QDialog):
13720
13785
  if active_data is None:
13721
13786
  raise ValueError("No active image selected")
13722
13787
 
13723
- self.parent().last_dil = amount
13788
+ self.parent().last_dil = [amount, accepted_mode]
13724
13789
 
13725
13790
  if accepted_mode == 1:
13726
13791
  dialog = SmartDilateDialog(self.parent(), [active_data, amount, xy_scale, z_scale])
@@ -13729,6 +13794,8 @@ class DilateDialog(QDialog):
13729
13794
  return
13730
13795
 
13731
13796
  if accepted_mode == 0:
13797
+ result = n3d.dilate_3D_dt(active_data, amount, xy_scaling = xy_scale, z_scaling = z_scale, fast_dil = True)
13798
+ elif accepted_mode == 3:
13732
13799
  result = n3d.dilate_3D_dt(active_data, amount, xy_scaling = xy_scale, z_scaling = z_scale)
13733
13800
  else:
13734
13801
 
@@ -13767,7 +13834,10 @@ class ErodeDialog(QDialog):
13767
13834
 
13768
13835
  if args:
13769
13836
  self.parent().last_ero = args[0]
13770
- self.index = 1
13837
+ if args[1] == 1: #user opted to preserve labels
13838
+ self.index = 2 #this is where the labels option lives in the erode menu
13839
+ else:
13840
+ self.index = 0
13771
13841
  else:
13772
13842
  self.parent().last_ero = 1
13773
13843
  self.index = 0
@@ -13783,7 +13853,7 @@ class ErodeDialog(QDialog):
13783
13853
 
13784
13854
  # Add mode selection dropdown
13785
13855
  self.mode_selector = QComboBox()
13786
- self.mode_selector.addItems(["Distance Transform-Based (Slower but more accurate at larger erosions)", "Preserve Labels (Slower)", "Pseudo3D Binary Kernels (For Fast, small erosions)"])
13856
+ self.mode_selector.addItems(["Parallel Distance Transform Based", "Distance Transform-Based (Non-Parallel)", "Preserve Labels (Parallel)", "Preserve Labels (Non-Parallel)"])
13787
13857
  self.mode_selector.setCurrentIndex(self.index) # Default to Mode 1
13788
13858
  layout.addRow("Execution Mode:", self.mode_selector)
13789
13859
 
@@ -13820,7 +13890,7 @@ class ErodeDialog(QDialog):
13820
13890
 
13821
13891
  mode = self.mode_selector.currentIndex()
13822
13892
 
13823
- if mode == 1:
13893
+ if mode == 2 or mode == 3:
13824
13894
  preserve_labels = True
13825
13895
  else:
13826
13896
  preserve_labels = False
@@ -13842,7 +13912,7 @@ class ErodeDialog(QDialog):
13842
13912
 
13843
13913
 
13844
13914
  self.parent().load_channel(self.parent().active_channel, result, True, preserve_zoom = (self.parent().ax.get_xlim(), self.parent().ax.get_ylim()))
13845
- self.parent().last_ero = amount
13915
+ self.parent().last_ero = [amount, mode]
13846
13916
  self.accept()
13847
13917
 
13848
13918
  except Exception as e:
@@ -14012,7 +14082,7 @@ class FilamentDialog(QDialog):
14012
14082
  if downsample_factor and downsample_factor > 1:
14013
14083
  data = n3d.downsample(data, downsample_factor)
14014
14084
 
14015
- result = filaments.trace(data, kernel_spacing, max_distance, min_component, gap_tolerance, blob_sphericity, blob_volume, spine_removal, score_threshold)
14085
+ result = filaments.trace(data, kernel_spacing, max_distance, min_component, gap_tolerance, blob_sphericity, blob_volume, spine_removal, score_threshold, my_network.xy_scale, my_network.z_scale)
14016
14086
 
14017
14087
  if downsample_factor and downsample_factor > 1:
14018
14088
 
@@ -14548,6 +14618,12 @@ class DistanceDialog(QDialog):
14548
14618
 
14549
14619
  layout = QFormLayout(self)
14550
14620
 
14621
+ # Add mode selection dropdown
14622
+ self.mode_selector = QComboBox()
14623
+ self.mode_selector.addItems(["Parallel (Faster)", "Non-Parallel (Uses less CPU resources)"])
14624
+ self.mode_selector.setCurrentIndex(0) # Default to Mode 1
14625
+ layout.addRow("Execution Mode:", self.mode_selector)
14626
+
14551
14627
  # Add Run button
14552
14628
  run_button = QPushButton("Run")
14553
14629
  run_button.clicked.connect(self.run)
@@ -14557,12 +14633,20 @@ class DistanceDialog(QDialog):
14557
14633
 
14558
14634
  try:
14559
14635
 
14636
+ mode = self.mode_selector.currentIndex()
14637
+ if mode == 0:
14638
+ fast_dil = True
14639
+ else:
14640
+ fast_dil = False
14641
+
14560
14642
  data = self.parent().channel_data[self.parent().active_channel]
14561
14643
 
14562
- data = sdl.compute_distance_transform_distance(data, sampling = [my_network.z_scale, my_network.xy_scale, my_network.xy_scale])
14644
+ data = sdl.compute_distance_transform_distance(data, sampling = [my_network.z_scale, my_network.xy_scale, my_network.xy_scale], fast_dil = fast_dil)
14563
14645
 
14564
14646
  self.parent().load_channel(self.parent().active_channel, data, data = True, preserve_zoom = (self.parent().ax.get_xlim(), self.parent().ax.get_ylim()))
14565
14647
 
14648
+ self.accept()
14649
+
14566
14650
  except Exception as e:
14567
14651
 
14568
14652
  print(f"Error: {e}")
@@ -14680,17 +14764,24 @@ class WatershedDialog(QDialog):
14680
14764
  self.proportion = QLineEdit(f"{self.default}")
14681
14765
  layout.addRow(f"Proportion (0-1) of distance transform value set [ie unique elements] to exclude (ie 0.2 = 20% of the set of all values of the distance transform get excluded).\n Essentially, vals closer to 0 are less likely to split objects but also won't kick out small objects from the output, vals slightly further from 0 will split more aggressively, but vals closer to 1 become unstable, leading to objects being evicted or labelling errors. \nRecommend something between 0.05 and 0.4, but it depends on the data (Or just enter a smallest radius above to avoid using this). \nWill tell you in command window what equivalent 'smallest radius' this is):", self.proportion)
14682
14766
 
14767
+
14768
+ # Add mode selection dropdown
14769
+ self.mode_selector = QComboBox()
14770
+ self.mode_selector.addItems(["Parallel (Faster)", "Non-Parallel (Uses less CPU resources)"])
14771
+ self.mode_selector.setCurrentIndex(0) # Default to Mode 1
14772
+ layout.addRow("Execution Mode:", self.mode_selector)
14773
+
14683
14774
  # GPU checkbox (default True)
14684
14775
  self.gpu = QPushButton("GPU")
14685
14776
  self.gpu.setCheckable(True)
14686
14777
  self.gpu.setChecked(False)
14687
- layout.addRow("Use GPU:", self.gpu)
14778
+ #layout.addRow("Use GPU:", self.gpu)
14688
14779
 
14689
14780
 
14690
14781
  # Predownsample (empty by default)
14691
14782
  self.predownsample = QLineEdit()
14692
14783
  self.predownsample.setPlaceholderText("Leave empty for None")
14693
- layout.addRow("Kernel Obtainment GPU Downsample:", self.predownsample)
14784
+ #layout.addRow("Kernel Obtainment GPU Downsample:", self.predownsample)
14694
14785
 
14695
14786
  # Predownsample2 (empty by default)
14696
14787
  #self.predownsample2 = QLineEdit()
@@ -14707,6 +14798,14 @@ class WatershedDialog(QDialog):
14707
14798
 
14708
14799
  def run_watershed(self):
14709
14800
  try:
14801
+
14802
+ mode = self.mode_selector.currentIndex()
14803
+
14804
+ if mode == 0:
14805
+ fast_dil = True
14806
+ else:
14807
+ fast_dil = False
14808
+
14710
14809
  # Get directory (None if empty)
14711
14810
  directory = None
14712
14811
 
@@ -14750,6 +14849,7 @@ class WatershedDialog(QDialog):
14750
14849
  proportion=proportion,
14751
14850
  GPU=gpu,
14752
14851
  smallest_rad=smallest_rad,
14852
+ fast_dil = fast_dil,
14753
14853
  predownsample=predownsample,
14754
14854
  predownsample2=predownsample2
14755
14855
  )
@@ -14958,53 +15058,10 @@ class GenNodesDialog(QDialog):
14958
15058
  main_layout = QVBoxLayout(self)
14959
15059
  self.called = called
14960
15060
 
14961
- # Set down_factor and cubic
15061
+ # Set down_factor
14962
15062
  if not down_factor:
14963
15063
  down_factor = None
14964
15064
 
14965
- if down_factor is None:
14966
- # --- Processing Options Group ---
14967
- process_group = QGroupBox("Processing Options")
14968
- process_layout = QGridLayout()
14969
-
14970
- # Downsample factor
14971
- self.down_factor = QLineEdit("0")
14972
- process_layout.addWidget(QLabel("Downsample Factor (Speeds up calculation at the cost of fidelity):"), 0, 0)
14973
- process_layout.addWidget(self.down_factor, 0, 1)
14974
-
14975
- # Cubic checkbox
14976
- self.cubic = QPushButton("Cubic Downsample")
14977
- self.cubic.setCheckable(True)
14978
- self.cubic.setChecked(False)
14979
- #process_layout.addWidget(QLabel("Use cubic downsample? (Slower but preserves structure better):"), 1, 0)
14980
- #process_layout.addWidget(self.cubic, 1, 1)
14981
-
14982
- # Fast dilation checkbox
14983
- self.fast_dil = QPushButton("Fast-Dil")
14984
- self.fast_dil.setCheckable(True)
14985
- self.fast_dil.setChecked(True)
14986
- #process_layout.addWidget(QLabel("Use Fast Dilation (Higher speed, less accurate with large search regions):"), 2, 0)
14987
- #process_layout.addWidget(self.fast_dil, 2, 1)
14988
-
14989
- process_group.setLayout(process_layout)
14990
- main_layout.addWidget(process_group)
14991
- else:
14992
- self.down_factor = down_factor[0]
14993
- self.cubic = down_factor[1]
14994
-
14995
- # Fast dilation checkbox (still needed even if down_factor is provided)
14996
- #process_group = QGroupBox("Processing Options")
14997
- #process_layout = QGridLayout()
14998
-
14999
- self.fast_dil = QPushButton("Fast-Dil")
15000
- self.fast_dil.setCheckable(True)
15001
- self.fast_dil.setChecked(True)
15002
- #process_layout.addWidget(QLabel("Use Fast Dilation (Higher speed, less accurate with large search regions):"), 0, 0)
15003
- #process_layout.addWidget(self.fast_dil, 0, 1)
15004
-
15005
- #process_group.setLayout(process_layout)
15006
- #main_layout.addWidget(process_group)
15007
-
15008
15065
  # --- Recommended Corrections Group ---
15009
15066
  rec_group = QGroupBox("Recommended Corrections")
15010
15067
  rec_layout = QGridLayout()
@@ -15046,6 +15103,39 @@ class GenNodesDialog(QDialog):
15046
15103
 
15047
15104
  opt_group.setLayout(opt_layout)
15048
15105
  main_layout.addWidget(opt_group)
15106
+
15107
+ # --- Processing Options Group ---
15108
+ process_group = QGroupBox("Processing Options")
15109
+ process_layout = QGridLayout()
15110
+
15111
+ if not called:
15112
+
15113
+
15114
+ # Fast dilation checkbox
15115
+ self.fast_dil = QPushButton("Fast-Dil")
15116
+ self.fast_dil.setCheckable(True)
15117
+ self.fast_dil.setChecked(True)
15118
+ process_layout.addWidget(QLabel("Use Fast Dilation if merging nodes (Parallelized):"), 0, 0)
15119
+ process_layout.addWidget(self.fast_dil, 0, 1)
15120
+
15121
+ # Downsample factor
15122
+ self.down_factor = QLineEdit("0")
15123
+ process_layout.addWidget(QLabel("Downsample Factor (Speeds up calculation at the cost of fidelity):"), 1, 0)
15124
+ process_layout.addWidget(self.down_factor, 1, 1)
15125
+
15126
+ process_group.setLayout(process_layout)
15127
+ main_layout.addWidget(process_group)
15128
+ else:
15129
+ self.down_factor = down_factor
15130
+
15131
+ self.fast_dil = QPushButton("Fast-Dil")
15132
+ self.fast_dil.setCheckable(True)
15133
+ self.fast_dil.setChecked(True)
15134
+ process_layout.addWidget(QLabel("Use Fast Dilation if merging nodes (Parallelized):"), 0, 0)
15135
+ process_layout.addWidget(self.fast_dil, 0, 1)
15136
+
15137
+ process_group.setLayout(process_layout)
15138
+ main_layout.addWidget(process_group)
15049
15139
 
15050
15140
  # Set retain variable but don't add to layout
15051
15141
  if not called:
@@ -15089,31 +15179,26 @@ class GenNodesDialog(QDialog):
15089
15179
  comp_dil = 0
15090
15180
 
15091
15181
  # Get down_factor
15092
- if type(self.down_factor) is int:
15182
+ if type(self.down_factor) is int or self.down_factor is None:
15093
15183
  down_factor = self.down_factor
15094
- cubic = self.cubic
15095
15184
  else:
15096
15185
  try:
15097
- down_factor = int(self.down_factor.text()) if self.down_factor.text() else 0
15186
+ down_factor = int(self.down_factor.text()) if self.down_factor.text() else None
15187
+ if down_factor == 0:
15188
+ down_factor = None
15098
15189
  except ValueError:
15099
- down_factor = 0
15100
- cubic = self.cubic.isChecked()
15190
+ down_factor = None
15101
15191
 
15102
15192
  try:
15103
15193
  retain = self.retain.isChecked()
15104
15194
  except:
15105
15195
  retain = True
15106
15196
 
15107
- if cubic:
15108
- order = 3
15109
- else:
15110
- order = 0
15111
-
15112
15197
  auto = self.auto.isChecked()
15113
15198
 
15114
15199
  fastdil = self.fast_dil.isChecked()
15115
15200
 
15116
- if down_factor > 1:
15201
+ if down_factor is not None:
15117
15202
  my_network.edges = n3d.downsample(my_network.edges, down_factor)
15118
15203
 
15119
15204
  if auto:
@@ -15126,15 +15211,15 @@ class GenNodesDialog(QDialog):
15126
15211
  max_vol=max_vol,
15127
15212
  branch_removal=branch_removal,
15128
15213
  comp_dil=comp_dil,
15129
- order = order,
15214
+ order = 0,
15130
15215
  return_skele = True,
15131
15216
  fastdil = fastdil
15132
15217
  )
15133
15218
 
15134
- if down_factor > 0 and not self.called:
15219
+ if down_factor is not None and not self.called:
15135
15220
  self.parent().resizing = True
15136
15221
 
15137
- my_network.edges = n3d.downsample(my_network.edges, down_factor, order = order)
15222
+ my_network.edges = n3d.downsample(my_network.edges, down_factor, order = 0)
15138
15223
  my_network.xy_scale = my_network.xy_scale * down_factor
15139
15224
  my_network.z_scale = my_network.z_scale * down_factor
15140
15225
  print("xy_scales and z_scales have been adjusted per downsample. Check image -> properties to manually reset them to 1 if desired.")
@@ -15241,13 +15326,14 @@ class BranchDialog(QDialog):
15241
15326
  self.down_factor = QLineEdit("0")
15242
15327
  processing_layout.addWidget(QLabel("Internal downsample factor (will recompute nodes):"), 0, 0)
15243
15328
  processing_layout.addWidget(self.down_factor, 0, 1)
15244
-
15245
- # Cubic checkbox
15246
- self.cubic = QPushButton("Cubic Downsample")
15247
- self.cubic.setCheckable(True)
15248
- self.cubic.setChecked(False)
15249
- #processing_layout.addWidget(QLabel("Use cubic downsample? (Slower but preserves structure better):"), 1, 0)
15250
- #processing_layout.addWidget(self.cubic, 1, 1)
15329
+
15330
+ # Add mode selection dropdown
15331
+ self.mode = QComboBox()
15332
+ self.mode.addItems(["Standard", "Fast (May be a little rougher along adjacent labels)"])
15333
+ self.mode.setCurrentIndex(0)
15334
+ processing_layout.addWidget(QLabel("Algorithm (Standard or Fast?):"), 1, 0)
15335
+ processing_layout.addWidget(self.mode, 1, 1)
15336
+
15251
15337
 
15252
15338
  processing_group.setLayout(processing_layout)
15253
15339
  main_layout.addWidget(processing_group)
@@ -15297,17 +15383,20 @@ class BranchDialog(QDialog):
15297
15383
  try:
15298
15384
 
15299
15385
  try:
15300
- down_factor = int(self.down_factor.text()) if self.down_factor.text() else 0
15386
+ down_factor = int(self.down_factor.text()) if self.down_factor.text() else None
15301
15387
  except ValueError:
15302
- down_factor = 0
15388
+ down_factor = None
15389
+
15390
+ if down_factor == 0:
15391
+ down_factor = None
15303
15392
 
15304
15393
  nodes = self.nodes.isChecked()
15305
15394
  GPU = self.GPU.isChecked()
15306
- cubic = self.cubic.isChecked()
15307
15395
  fix = self.fix.isChecked()
15308
15396
  fix2 = self.fix2.isChecked()
15309
15397
  fix3 = self.fix3.isChecked()
15310
15398
  fix4 = self.fix4.isChecked()
15399
+ mode = self.mode.currentIndex()
15311
15400
  fix_val = float(self.fix_val.text()) if self.fix_val.text() else None
15312
15401
  fix4_val = float(self.fix4_val.text()) if self.fix4_val.text() else 10
15313
15402
  seed = int(self.seed.text()) if self.seed.text() else None
@@ -15320,11 +15409,7 @@ class BranchDialog(QDialog):
15320
15409
  original_shape = my_network.edges.shape
15321
15410
  original_array = copy.deepcopy(my_network.edges)
15322
15411
 
15323
- if down_factor > 0:
15324
- self.parent().show_gennodes_dialog(down_factor = [down_factor, cubic], called = True)
15325
- elif nodes or my_network.nodes is None:
15326
- self.parent().show_gennodes_dialog(called = True)
15327
- down_factor = None
15412
+ self.parent().show_gennodes_dialog(down_factor = down_factor, called = True)
15328
15413
 
15329
15414
  if my_network.edges is not None and my_network.nodes is not None and my_network.id_overlay is not None:
15330
15415
 
@@ -15333,10 +15418,12 @@ class BranchDialog(QDialog):
15333
15418
  else:
15334
15419
  unify = False
15335
15420
 
15336
- output, verts, skeleton, endpoints = n3d.label_branches(my_network.edges, nodes = my_network.nodes, bonus_array = original_array, GPU = GPU, down_factor = down_factor, arrayshape = original_shape, compute = compute, unify = unify, union_val = fix4_val, xy_scale = my_network.xy_scale, z_scale = my_network.z_scale)
15421
+ output, verts, skeleton, endpoints = n3d.label_branches(my_network.edges, nodes = my_network.nodes, bonus_array = original_array, GPU = GPU, down_factor = down_factor, arrayshape = original_shape, compute = compute, unify = unify, union_val = fix4_val, mode = mode, xy_scale = my_network.xy_scale, z_scale = my_network.z_scale)
15337
15422
 
15338
15423
  if fix2:
15339
15424
 
15425
+ print("Correcting Internal Branches...")
15426
+
15340
15427
  temp_network = n3d.Network_3D(nodes = output)
15341
15428
 
15342
15429
  max_val = np.max(temp_network.nodes)
@@ -15388,6 +15475,7 @@ class BranchDialog(QDialog):
15388
15475
  self.parent().format_for_upperright_table(tortuosity_dict, 'BranchID', 'Tortuosity', 'Branch Tortuosities')
15389
15476
  #self.parent().format_for_upperright_table(angle_dict, 'Vertex ID', title, 'Branch Angles')
15390
15477
 
15478
+ scalings = my_network.xy_scale, my_network.z_scale
15391
15479
 
15392
15480
  if down_factor is not None:
15393
15481
 
@@ -15397,6 +15485,9 @@ class BranchDialog(QDialog):
15397
15485
  self.parent().reset(id_overlay = True)
15398
15486
  self.parent().update_display(dims = (output.shape[1], output.shape[2]))
15399
15487
 
15488
+ my_network.xy_scale, my_network.z_scale = scalings
15489
+
15490
+
15400
15491
  self.parent().load_channel(1, channel_data = output, data = True)
15401
15492
 
15402
15493
  self.parent().update_display(preserve_zoom = (self.parent().ax.get_xlim(), self.parent().ax.get_ylim()))
@@ -15906,7 +15997,7 @@ class CalcAllDialog(QDialog):
15906
15997
  prev_gpu = False
15907
15998
  prev_label_nodes = True
15908
15999
  prev_inners = True
15909
- prev_fastdil = False
16000
+ prev_fastdil = True
15910
16001
  prev_overlays = False
15911
16002
  prev_updates = True
15912
16003
 
@@ -15979,10 +16070,10 @@ class CalcAllDialog(QDialog):
15979
16070
  self.gpu.setChecked(self.prev_gpu)
15980
16071
  #speedup_layout.addRow("Use GPU:", self.gpu)
15981
16072
 
15982
- self.fastdil = QPushButton("Fast Dilate")
16073
+ self.fastdil = QPushButton("Fast Search")
15983
16074
  self.fastdil.setCheckable(True)
15984
16075
  self.fastdil.setChecked(self.prev_fastdil)
15985
- #speedup_layout.addRow("Use Fast Dilation (Higher speed, less accurate with search regions much larger than nodes):", self.fastdil)
16076
+ speedup_layout.addRow("Use Fast Search (Parallelized searching, search regions may be a tad rougher along adjacent boundaries):", self.fastdil)
15986
16077
 
15987
16078
  main_layout.addWidget(speedup_group)
15988
16079