nettracer3d 0.5.3__py3-none-any.whl → 0.5.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.
@@ -397,6 +397,9 @@ class ImageViewerWindow(QMainWindow):
397
397
  # Initialize highlight overlay
398
398
  self.highlight_overlay = None
399
399
  self.highlight_bounds = None # Store bounds for positioning
400
+ self.mini_overlay = False # If the program is currently drawing the overlay by frame this will be true
401
+ self.mini_overlay_data = None #Actual data for mini overlay
402
+ self.mini_thresh = (500*500*500) # Array volume to start using mini overlays for
400
403
 
401
404
  def start_left_scroll(self):
402
405
  """Start scrolling left when left arrow is pressed."""
@@ -443,6 +446,9 @@ class ImageViewerWindow(QMainWindow):
443
446
  edge_indices (list): List of edge indices to highlight
444
447
  """
445
448
 
449
+ self.mini_overlay = False #If this method is ever being called, it means we are rendering the entire overlay so mini overlay needs to reset.
450
+ self.mini_overlay_data = None
451
+
446
452
  def process_chunk(chunk_data, indices_to_check):
447
453
  """Process a single chunk of the array to create highlight mask"""
448
454
  mask = np.isin(chunk_data, indices_to_check)
@@ -463,6 +469,9 @@ class ImageViewerWindow(QMainWindow):
463
469
  if overlay1_indices is not None:
464
470
  if 0 in overlay1_indices:
465
471
  overlay1_indices.remove(0)
472
+ if overlay2_indices is not None:
473
+ if 0 in overlay2_indices:
474
+ overlay2_indices.remove(0)
466
475
 
467
476
  if node_indices is None:
468
477
  node_indices = []
@@ -555,6 +564,8 @@ class ImageViewerWindow(QMainWindow):
555
564
 
556
565
  def create_highlight_overlay_slice(self, indices, bounds = False):
557
566
 
567
+ """Highlight overlay generation method specific for the segmenter interactive mode"""
568
+
558
569
 
559
570
  def process_chunk_bounds(chunk_data, indices_to_check):
560
571
  """Process a single chunk of the array to create highlight mask"""
@@ -628,6 +639,110 @@ class ImageViewerWindow(QMainWindow):
628
639
  # Update display
629
640
  self.update_display(preserve_zoom=(current_xlim, current_ylim), called = True)
630
641
 
642
+ def create_mini_overlay(self, node_indices = None, edge_indices = None):
643
+
644
+ """
645
+ Create a highlight overlay one slice at a time.
646
+
647
+ Args:
648
+ node_indices (list): List of node indices to highlight
649
+ edge_indices (list): List of edge indices to highlight
650
+ """
651
+
652
+ def process_chunk(chunk_data, indices_to_check):
653
+ """Process a single chunk of the array to create highlight mask"""
654
+ mask = np.isin(chunk_data, indices_to_check)
655
+ return mask * 255
656
+
657
+
658
+ if node_indices is not None:
659
+ if 0 in node_indices:
660
+ node_indices.remove(0)
661
+ if edge_indices is not None:
662
+ if 0 in edge_indices:
663
+ edge_indices.remove(0)
664
+
665
+
666
+ if node_indices is None:
667
+ node_indices = []
668
+ if edge_indices is None:
669
+ edge_indices = []
670
+
671
+
672
+ current_xlim = self.ax.get_xlim() if hasattr(self, 'ax') and self.ax.get_xlim() != (0, 1) else None
673
+ current_ylim = self.ax.get_ylim() if hasattr(self, 'ax') and self.ax.get_ylim() != (0, 1) else None
674
+
675
+ if not node_indices and not edge_indices: #Theoretically this can't be called because it uses full highlight overlay method for empty clicks
676
+ self.mini_overlay_data = None
677
+ self.mini_overlay = False
678
+ self.update_display(preserve_zoom=(current_xlim, current_ylim))
679
+ return
680
+
681
+ # Get the shape of the mini array from any existing channel
682
+ for channel in self.channel_data:
683
+ if channel is not None:
684
+ full_shape = channel.shape
685
+ full_shape = (full_shape[1], full_shape[2]) #Just get (Y, X) shape
686
+ break
687
+ else:
688
+ return # No valid channels to get shape from
689
+
690
+ # Initialize full-size overlay
691
+ self.mini_overlay_data = np.zeros(full_shape, dtype=np.uint8)
692
+
693
+ # Get number of CPU cores
694
+ num_cores = mp.cpu_count()
695
+
696
+ # Calculate chunk size along y-axis
697
+ chunk_size = full_shape[0] // num_cores
698
+ if chunk_size < 1:
699
+ chunk_size = 1
700
+
701
+ def process_channel(channel_data, indices, array_shape):
702
+ if channel_data is None or not indices:
703
+ return None
704
+
705
+ # Create chunks
706
+ chunks = []
707
+ for i in range(0, array_shape[0], chunk_size):
708
+ end = min(i + chunk_size, array_shape[0])
709
+ chunks.append(channel_data[i:end, :])
710
+
711
+ # Process chunks in parallel using ThreadPoolExecutor
712
+ process_func = partial(process_chunk, indices_to_check=indices)
713
+
714
+
715
+ with ThreadPoolExecutor(max_workers=num_cores) as executor:
716
+ chunk_results = list(executor.map(process_func, chunks))
717
+
718
+ # Reassemble the chunks
719
+ return np.concatenate(chunk_results, axis=0)
720
+
721
+ # Process nodes and edges in parallel using multiprocessing
722
+ with ThreadPoolExecutor(max_workers=num_cores) as executor:
723
+ try:
724
+ slice_node = self.channel_data[0][self.current_slice, :, :] #This is the only major difference to the big highlight... we are only looking at this
725
+ future_nodes = executor.submit(process_channel, slice_node, node_indices, full_shape)
726
+ node_overlay = future_nodes.result()
727
+ except:
728
+ node_overlay = None
729
+ try:
730
+ slice_edge = self.channel_data[1][self.current_slice, :, :]
731
+ future_edges = executor.submit(process_channel, slice_edge, edge_indices, full_shape)
732
+ edge_overlay = future_edges.result()
733
+ except:
734
+ edge_overlay = None
735
+
736
+ # Combine results
737
+ if node_overlay is not None:
738
+ self.mini_overlay_data = np.maximum(self.mini_overlay_data, node_overlay)
739
+ if edge_overlay is not None:
740
+ self.mini_overlay_data = np.maximum(self.mini_overlay_data, edge_overlay)
741
+
742
+
743
+ # Update display
744
+ self.update_display(preserve_zoom=(current_xlim, current_ylim))
745
+
631
746
 
632
747
 
633
748
 
@@ -890,14 +1005,23 @@ class ImageViewerWindow(QMainWindow):
890
1005
  edge_indices = filtered_df.iloc[:, 2].unique().tolist()
891
1006
  self.clicked_values['edges'] = edge_indices
892
1007
 
893
- self.create_highlight_overlay(
894
- node_indices=self.clicked_values['nodes'],
895
- edge_indices=self.clicked_values['edges']
896
- )
1008
+ if self.channel_data[1].shape[0] * self.channel_data[1].shape[1] * self.channel_data[1].shape[2] > self.mini_thresh:
1009
+ self.mini_overlay = True
1010
+ self.create_mini_overlay(node_indices = self.clicked_values['nodes'], edge_indices = self.clicked_values['edges'])
1011
+ else:
1012
+ self.create_highlight_overlay(
1013
+ node_indices=self.clicked_values['nodes'],
1014
+ edge_indices=self.clicked_values['edges']
1015
+ )
897
1016
  else:
898
- self.create_highlight_overlay(
899
- node_indices=self.clicked_values['nodes']
900
- )
1017
+ if self.channel_data[0].shape[0] * self.channel_data[0].shape[1] * self.channel_data[0].shape[2] > self.mini_thresh:
1018
+ self.mini_overlay = True
1019
+ self.create_mini_overlay(node_indices = self.clicked_values['nodes'], edge_indices = self.clicked_values['edges'])
1020
+ else:
1021
+ self.create_highlight_overlay(
1022
+ node_indices=self.clicked_values['nodes'],
1023
+ edge_indices = self.clicked_values['edges']
1024
+ )
901
1025
 
902
1026
 
903
1027
  except Exception as e:
@@ -972,14 +1096,23 @@ class ImageViewerWindow(QMainWindow):
972
1096
  if edges:
973
1097
  edge_indices = filtered_df.iloc[:, 2].unique().tolist()
974
1098
  self.clicked_values['edges'] = edge_indices
975
- self.create_highlight_overlay(
976
- node_indices=self.clicked_values['nodes'],
977
- edge_indices=edge_indices
978
- )
1099
+ if self.channel_data[1].shape[0] * self.channel_data[1].shape[1] * self.channel_data[1].shape[2] > self.mini_thresh:
1100
+ self.mini_overlay = True
1101
+ self.create_mini_overlay(node_indices = self.clicked_values['nodes'], edge_indices = self.clicked_values['edges'])
1102
+ else:
1103
+ self.create_highlight_overlay(
1104
+ node_indices=self.clicked_values['nodes'],
1105
+ edge_indices=edge_indices
1106
+ )
979
1107
  else:
980
- self.create_highlight_overlay(
981
- node_indices = self.clicked_values['nodes']
982
- )
1108
+ if self.channel_data[0].shape[0] * self.channel_data[0].shape[1] * self.channel_data[0].shape[2] > self.mini_thresh:
1109
+ self.mini_overlay = True
1110
+ self.create_mini_overlay(node_indices = self.clicked_values['nodes'], edge_indices = self.clicked_values['edges'])
1111
+ else:
1112
+ self.create_highlight_overlay(
1113
+ node_indices = self.clicked_values['nodes'],
1114
+ edge_indices = self.clicked_values['edges']
1115
+ )
983
1116
 
984
1117
  except Exception as e:
985
1118
 
@@ -1043,15 +1176,24 @@ class ImageViewerWindow(QMainWindow):
1043
1176
  if edges:
1044
1177
  edge_indices = filtered_df.iloc[:, 2].unique().tolist()
1045
1178
  self.clicked_values['edges'] = edge_indices
1046
- self.create_highlight_overlay(
1047
- node_indices=nodes,
1048
- edge_indices=edge_indices
1049
- )
1179
+ if self.channel_data[1].shape[0] * self.channel_data[1].shape[1] * self.channel_data[1].shape[2] > self.mini_thresh:
1180
+ self.mini_overlay = True
1181
+ self.create_mini_overlay(node_indices = nodes, edge_indices = edge_indices)
1182
+ else:
1183
+ self.create_highlight_overlay(
1184
+ node_indices=nodes,
1185
+ edge_indices=edge_indices
1186
+ )
1050
1187
  self.clicked_values['nodes'] = nodes
1051
1188
  else:
1052
- self.create_highlight_overlay(
1053
- node_indices = nodes
1054
- )
1189
+ if self.channel_data[0].shape[0] * self.channel_data[0].shape[1] * self.channel_data[0].shape[2] > self.mini_thresh:
1190
+ self.mini_overlay = True
1191
+ self.create_mini_overlay(node_indices = nodes, edge_indices = self.clicked_values['edges'])
1192
+ else:
1193
+ self.create_highlight_overlay(
1194
+ node_indices = nodes,
1195
+ edge_indices = self.clicked_values['edges']
1196
+ )
1055
1197
  self.clicked_values['nodes'] = nodes
1056
1198
 
1057
1199
  except Exception as e:
@@ -1095,9 +1237,15 @@ class ImageViewerWindow(QMainWindow):
1095
1237
 
1096
1238
  print(f"Found {len(filtered_df)} direct connections between nodes of ID {sort} and their neighbors (of any ID)")
1097
1239
 
1098
- self.create_highlight_overlay(
1099
- node_indices= nodes
1100
- )
1240
+ if self.channel_data[0].shape[0] * self.channel_data[0].shape[1] * self.channel_data[0].shape[2] > self.mini_thresh:
1241
+ self.mini_overlay = True
1242
+ self.create_mini_overlay(node_indices = nodes, edge_indices = self.clicked_values['edges'])
1243
+ else:
1244
+ self.create_highlight_overlay(
1245
+ node_indices = nodes,
1246
+ edge_indices = self.clicked_values['edges']
1247
+ )
1248
+ self.clicked_values['nodes'] = nodes
1101
1249
 
1102
1250
  except Exception as e:
1103
1251
  print(f"Error showing identities: {e}")
@@ -1393,8 +1541,6 @@ class ImageViewerWindow(QMainWindow):
1393
1541
 
1394
1542
  pairs = list(combinations(nodes, 2))
1395
1543
 
1396
- print(pairs)
1397
-
1398
1544
 
1399
1545
  for i in range(len(my_network.network_lists[0]) - 1, -1, -1):
1400
1546
  print((my_network.network_lists[0][i], my_network.network_lists[1][i]))
@@ -1891,7 +2037,11 @@ class ImageViewerWindow(QMainWindow):
1891
2037
  self.clicked_values['nodes'].extend(selected_values)
1892
2038
  # Remove duplicates while preserving order
1893
2039
  self.clicked_values['nodes'] = list(dict.fromkeys(self.clicked_values['nodes']))
1894
- self.create_highlight_overlay(node_indices=self.clicked_values['nodes'])
2040
+ if self.channel_data[0].shape[0] * self.channel_data[0].shape[1] * self.channel_data[0].shape[2] > self.mini_thresh:
2041
+ self.mini_overlay = True
2042
+ self.create_mini_overlay(node_indices = self.clicked_values['nodes'], edge_indices = self.clicked_values['edges'])
2043
+ else:
2044
+ self.create_highlight_overlay(node_indices=self.clicked_values['nodes'])
1895
2045
 
1896
2046
  # Try to highlight the last selected value in tables
1897
2047
  if self.clicked_values['nodes']:
@@ -1903,7 +2053,11 @@ class ImageViewerWindow(QMainWindow):
1903
2053
  self.clicked_values['edges'].extend(selected_values)
1904
2054
  # Remove duplicates while preserving order
1905
2055
  self.clicked_values['edges'] = list(dict.fromkeys(self.clicked_values['edges']))
1906
- self.create_highlight_overlay(edge_indices=self.clicked_values['edges'])
2056
+ if self.channel_data[1].shape[0] * self.channel_data[1].shape[1] * self.channel_data[1].shape[2] > self.mini_thresh:
2057
+ self.mini_overlay = True
2058
+ self.create_mini_overlay(node_indices = self.clicked_values['nodes'], edge_indices = self.clicked_values['edges'])
2059
+ else:
2060
+ self.create_highlight_overlay(edge_indices=self.clicked_values['edges'])
1907
2061
 
1908
2062
  # Try to highlight the last selected value in tables
1909
2063
  if self.clicked_values['edges']:
@@ -2144,9 +2298,17 @@ class ImageViewerWindow(QMainWindow):
2144
2298
 
2145
2299
  # Highlight the clicked element in the image using the stored lists
2146
2300
  if self.active_channel == 0 and (starting_vals['nodes']) != (self.clicked_values['nodes']):
2147
- self.create_highlight_overlay(node_indices=self.clicked_values['nodes'], edge_indices=self.clicked_values['edges'])
2301
+ if self.channel_data[0].shape[0] * self.channel_data[0].shape[1] * self.channel_data[0].shape[2] > self.mini_thresh:
2302
+ self.mini_overlay = True
2303
+ self.create_mini_overlay(node_indices = self.clicked_values['nodes'], edge_indices = self.clicked_values['edges'])
2304
+ else:
2305
+ self.create_highlight_overlay(node_indices=self.clicked_values['nodes'], edge_indices=self.clicked_values['edges'])
2148
2306
  elif self.active_channel == 1 and starting_vals['edges'] != self.clicked_values['edges']:
2149
- self.create_highlight_overlay(node_indices=self.clicked_values['nodes'], edge_indices=self.clicked_values['edges'])
2307
+ if self.channel_data[1].shape[0] * self.channel_data[1].shape[1] * self.channel_data[1].shape[2] > self.mini_thresh:
2308
+ self.mini_overlay = True
2309
+ self.create_mini_overlay(node_indices = self.clicked_values['nodes'], edge_indices = self.clicked_values['edges'])
2310
+ else:
2311
+ self.create_highlight_overlay(node_indices=self.clicked_values['nodes'], edge_indices=self.clicked_values['edges'])
2150
2312
 
2151
2313
 
2152
2314
  except IndexError:
@@ -2298,6 +2460,8 @@ class ImageViewerWindow(QMainWindow):
2298
2460
  netoverlay_action.triggered.connect(self.show_netoverlay_dialog)
2299
2461
  idoverlay_action = overlay_menu.addAction("Create ID Overlay")
2300
2462
  idoverlay_action.triggered.connect(self.show_idoverlay_dialog)
2463
+ coloroverlay_action = overlay_menu.addAction("Color Nodes (or Edges)")
2464
+ coloroverlay_action.triggered.connect(self.show_coloroverlay_dialog)
2301
2465
  searchoverlay_action = overlay_menu.addAction("Show Search Regions")
2302
2466
  searchoverlay_action.triggered.connect(self.show_search_dialog)
2303
2467
  shuffle_action = overlay_menu.addAction("Shuffle")
@@ -2578,6 +2742,11 @@ class ImageViewerWindow(QMainWindow):
2578
2742
  dialog = IdOverlayDialog(self)
2579
2743
  dialog.exec()
2580
2744
 
2745
+ def show_coloroverlay_dialog(self):
2746
+ """show the color overlay dialog"""
2747
+ dialog = ColorOverlayDialog(self)
2748
+ dialog.exec()
2749
+
2581
2750
  def show_search_dialog(self):
2582
2751
  """Show the search dialog"""
2583
2752
  dialog = SearchOverlayDialog(self)
@@ -3001,14 +3170,20 @@ class ImageViewerWindow(QMainWindow):
3001
3170
  else:
3002
3171
  self.channel_data[channel_index] = channel_data
3003
3172
 
3004
- if len(self.channel_data[channel_index].shape) == 3: # potentially 2D RGB
3005
- if self.channel_data[channel_index].shape[-1] in (3, 4): # last dim is 3 or 4
3006
- if self.confirm_rgb_dialog():
3007
- # User confirmed it's 2D RGB, expand to 4D
3008
- self.channel_data[channel_index] = np.expand_dims(self.channel_data[channel_index], axis=0)
3009
-
3010
- if len(self.channel_data[channel_index].shape) == 4 and (channel_index == 0 or channel_index == 1):
3011
- self.channel_data[channel_index] = self.reduce_rgb_dimension(self.channel_data[channel_index])
3173
+ try:
3174
+ if len(self.channel_data[channel_index].shape) == 3: # potentially 2D RGB
3175
+ if self.channel_data[channel_index].shape[-1] in (3, 4): # last dim is 3 or 4
3176
+ if self.confirm_rgb_dialog():
3177
+ # User confirmed it's 2D RGB, expand to 4D
3178
+ self.channel_data[channel_index] = np.expand_dims(self.channel_data[channel_index], axis=0)
3179
+ except:
3180
+ pass
3181
+
3182
+ try:
3183
+ if len(self.channel_data[channel_index].shape) == 4 and (channel_index == 0 or channel_index == 1):
3184
+ self.channel_data[channel_index] = self.reduce_rgb_dimension(self.channel_data[channel_index])
3185
+ except:
3186
+ pass
3012
3187
 
3013
3188
  reset_resize = False
3014
3189
 
@@ -3050,27 +3225,30 @@ class ImageViewerWindow(QMainWindow):
3050
3225
  self.active_channel_combo.setEnabled(True)
3051
3226
 
3052
3227
  # Update slider range if this is the first channel loaded
3053
- if len(self.channel_data[channel_index].shape) == 3 or len(self.channel_data[channel_index].shape) == 4:
3054
- if not self.slice_slider.isEnabled():
3055
- self.slice_slider.setEnabled(True)
3056
- self.slice_slider.setMinimum(0)
3057
- self.slice_slider.setMaximum(self.channel_data[channel_index].shape[0] - 1)
3058
- if self.slice_slider.value() < self.channel_data[channel_index].shape[0] - 1:
3059
- self.current_slice = self.slice_slider.value()
3228
+ try:
3229
+ if len(self.channel_data[channel_index].shape) == 3 or len(self.channel_data[channel_index].shape) == 4:
3230
+ if not self.slice_slider.isEnabled():
3231
+ self.slice_slider.setEnabled(True)
3232
+ self.slice_slider.setMinimum(0)
3233
+ self.slice_slider.setMaximum(self.channel_data[channel_index].shape[0] - 1)
3234
+ if self.slice_slider.value() < self.channel_data[channel_index].shape[0] - 1:
3235
+ self.current_slice = self.slice_slider.value()
3236
+ else:
3237
+ self.slice_slider.setValue(0)
3238
+ self.current_slice = 0
3060
3239
  else:
3061
- self.slice_slider.setValue(0)
3062
- self.current_slice = 0
3240
+ self.slice_slider.setEnabled(True)
3241
+ self.slice_slider.setMinimum(0)
3242
+ self.slice_slider.setMaximum(self.channel_data[channel_index].shape[0] - 1)
3243
+ if self.slice_slider.value() < self.channel_data[channel_index].shape[0] - 1:
3244
+ self.current_slice = self.slice_slider.value()
3245
+ else:
3246
+ self.current_slice = 0
3247
+ self.slice_slider.setValue(0)
3063
3248
  else:
3064
- self.slice_slider.setEnabled(True)
3065
- self.slice_slider.setMinimum(0)
3066
- self.slice_slider.setMaximum(self.channel_data[channel_index].shape[0] - 1)
3067
- if self.slice_slider.value() < self.channel_data[channel_index].shape[0] - 1:
3068
- self.current_slice = self.slice_slider.value()
3069
- else:
3070
- self.current_slice = 0
3071
- self.slice_slider.setValue(0)
3072
- else:
3073
- self.slice_slider.setEnabled(False)
3249
+ self.slice_slider.setEnabled(False)
3250
+ except:
3251
+ pass
3074
3252
 
3075
3253
 
3076
3254
  # If this is the first channel loaded, make it active
@@ -3083,13 +3261,16 @@ class ImageViewerWindow(QMainWindow):
3083
3261
  self.min_max[channel_index][1] = np.max(self.channel_data[channel_index])
3084
3262
  self.volume_dict[channel_index] = None #reset volumes
3085
3263
 
3086
- if assign_shape: #keep original shape tracked to undo resampling.
3087
- if self.original_shape is None:
3088
- self.original_shape = self.channel_data[channel_index].shape
3089
- elif self.original_shape[0] < self.channel_data[channel_index].shape[0] or self.original_shape[1] < self.channel_data[channel_index].shape[1] or self.original_shape[2] < self.channel_data[channel_index].shape[2]:
3090
- self.original_shape = self.channel_data[channel_index].shape
3091
- if len(self.original_shape) == 4:
3092
- self.original_shape = (self.original_shape[0], self.original_shape[1], self.original_shape[2])
3264
+ try:
3265
+ if assign_shape: #keep original shape tracked to undo resampling.
3266
+ if self.original_shape is None:
3267
+ self.original_shape = self.channel_data[channel_index].shape
3268
+ elif self.original_shape[0] < self.channel_data[channel_index].shape[0] or self.original_shape[1] < self.channel_data[channel_index].shape[1] or self.original_shape[2] < self.channel_data[channel_index].shape[2]:
3269
+ self.original_shape = self.channel_data[channel_index].shape
3270
+ if len(self.original_shape) == 4:
3271
+ self.original_shape = (self.original_shape[0], self.original_shape[1], self.original_shape[2])
3272
+ except:
3273
+ pass
3093
3274
 
3094
3275
  self.update_display(reset_resize = reset_resize)
3095
3276
 
@@ -3262,6 +3443,8 @@ class ImageViewerWindow(QMainWindow):
3262
3443
  elif ch_index == 3:
3263
3444
  my_network.save_id_overlay(filename=filename)
3264
3445
  elif ch_index == 4:
3446
+ if self.mini_overlay == True:
3447
+ self.create_highlight_overlay(node_indices = self.clicked_values['nodes'], edge_indices = self.clicked_values['edges'])
3265
3448
  if filename == None:
3266
3449
  filename = "Highlighted_Element.tif"
3267
3450
  tifffile.imwrite(f"{filename}", self.highlight_overlay)
@@ -3306,7 +3489,11 @@ class ImageViewerWindow(QMainWindow):
3306
3489
  if self.pending_slice is not None:
3307
3490
  slice_value, view_settings = self.pending_slice
3308
3491
  self.current_slice = slice_value
3492
+ if self.mini_overlay == True: #If we are rendering the highlight overlay for selected values one at a time.
3493
+ self.create_mini_overlay(node_indices = self.clicked_values['nodes'], edge_indices = self.clicked_values['edges'])
3309
3494
  self.update_display(preserve_zoom=view_settings)
3495
+ if self.machine_window is not None:
3496
+ self.machine_window.poke_segmenter()
3310
3497
  self.pending_slice = None
3311
3498
 
3312
3499
  def update_brightness(self, channel_index, values):
@@ -3442,7 +3629,15 @@ class ImageViewerWindow(QMainWindow):
3442
3629
  self.create_highlight_overlay_slice(self.targs, bounds = self.bounds)
3443
3630
 
3444
3631
  # Add highlight overlay if it exists
3445
- if self.highlight_overlay is not None and self.highlight and self.machine_window is None:
3632
+ if self.mini_overlay and self.highlight and self.machine_window is None:
3633
+ highlight_cmap = LinearSegmentedColormap.from_list(
3634
+ 'highlight',
3635
+ [(0, 0, 0, 0), (1, 1, 0, 1)] # yellow
3636
+ )
3637
+ self.ax.imshow(self.mini_overlay_data,
3638
+ cmap=highlight_cmap,
3639
+ alpha=0.5)
3640
+ elif self.highlight_overlay is not None and self.highlight and self.machine_window is None:
3446
3641
  highlight_slice = self.highlight_overlay[self.current_slice]
3447
3642
  highlight_cmap = LinearSegmentedColormap.from_list(
3448
3643
  'highlight',
@@ -3968,7 +4163,13 @@ class CustomTableView(QTableView):
3968
4163
  # Navigate to the Z-slice
3969
4164
  self.parent.slice_slider.setValue(int(centroid[0]))
3970
4165
  print(f"Found node {value} at Z-slice {centroid[0]}")
3971
- self.parent.create_highlight_overlay(node_indices=[value])
4166
+ if self.parent.channel_data[0].shape[0] * self.parent.channel_data[0].shape[1] * self.parent.channel_data[0].shape[2] > self.parent.mini_thresh:
4167
+ self.parent.mini_overlay = True
4168
+ self.parent.create_mini_overlay(node_indices = [value])
4169
+ else:
4170
+ self.parent.create_highlight_overlay(node_indices=[value])
4171
+ self.parent.clicked_values['nodes'] = []
4172
+ self.parent.clicked_values['edges'] = []
3972
4173
  self.parent.clicked_values['nodes'].append(value)
3973
4174
 
3974
4175
  # Highlight the value in both tables if it exists
@@ -3992,7 +4193,13 @@ class CustomTableView(QTableView):
3992
4193
  # Navigate to the Z-slice
3993
4194
  self.parent.slice_slider.setValue(int(centroid[0]))
3994
4195
  print(f"Found edge {value} at Z-slice {centroid[0]}")
3995
- self.parent.create_highlight_overlay(edge_indices=[value])
4196
+ if self.parent.channel_data[1].shape[0] * self.parent.channel_data[1].shape[1] * self.parent.channel_data[1].shape[2] > self.parent.mini_thresh:
4197
+ self.parent.mini_overlay = True
4198
+ self.parent.create_mini_overlay(edge_indices = [value])
4199
+ else:
4200
+ self.parent.create_highlight_overlay(edge_indices=[value])
4201
+ self.parent.clicked_values['nodes'] = []
4202
+ self.parent.clicked_values['edges'] = []
3996
4203
  self.parent.clicked_values['edges'].append(value)
3997
4204
 
3998
4205
  # Highlight the value in both tables if it exists
@@ -4020,12 +4227,24 @@ class CustomTableView(QTableView):
4020
4227
  self.parent.slice_slider.setValue(int(centroid1[0]))
4021
4228
  print(f"Found node pair {value[0]} and {value[1]} at Z-slices {centroid1[0]} and {centroid2[0]}, respectively")
4022
4229
  try:
4023
- self.parent.create_highlight_overlay(node_indices=[int(value[0]), int(value[1])], edge_indices = int(value[2]))
4230
+ if self.parent.channel_data[0].shape[0] * self.parent.channel_data[0].shape[1] * self.parent.channel_data[0].shape[2] > self.parent.mini_thresh:
4231
+ self.parent.mini_overlay = True
4232
+ self.parent.create_mini_overlay(node_indices=[int(value[0]), int(value[1])], edge_indices = int(value[2]))
4233
+ else:
4234
+ self.parent.create_highlight_overlay(node_indices=[int(value[0]), int(value[1])], edge_indices = int(value[2]))
4235
+ self.parent.clicked_values['nodes'] = []
4236
+ self.parent.clicked_values['edges'] = []
4024
4237
  self.parent.clicked_values['edges'].append(value[2])
4025
4238
  self.parent.clicked_values['nodes'].append(value[0])
4026
4239
  self.parent.clicked_values['nodes'].append(value[1])
4027
4240
  except:
4028
- self.parent.create_highlight_overlay(node_indices=[int(value[0]), int(value[1])])
4241
+ if self.parent.channel_data[0].shape[0] * self.parent.channel_data[0].shape[1] * self.parent.channel_data[0].shape[2] > self.parent.mini_thresh:
4242
+ self.parent.mini_overlay = True
4243
+ self.parent.create_mini_overlay(node_indices=[int(value[0]), int(value[1])])
4244
+ else:
4245
+ self.parent.create_highlight_overlay(node_indices=[int(value[0]), int(value[1])])
4246
+ self.parent.clicked_values['nodes'] = []
4247
+ self.parent.clicked_values['edges'] = []
4029
4248
  self.parent.clicked_values['nodes'].append(value[0])
4030
4249
  self.parent.clicked_values['nodes'].append(value[1])
4031
4250
 
@@ -4571,6 +4790,8 @@ class Show3dDialog(QDialog):
4571
4790
  arrays_4d.append(channel)
4572
4791
 
4573
4792
  if self.parent().highlight_overlay is not None:
4793
+ if self.parent().mini_overlay == True:
4794
+ self.parent().create_highlight_overlay(node_indices = self.parent().clicked_values['nodes'], edge_indices = self.parent().clicked_values['edges'])
4574
4795
  arrays_3d.append(self.parent().highlight_overlay)
4575
4796
  colors.append(color_template[4])
4576
4797
 
@@ -4705,6 +4926,45 @@ class IdOverlayDialog(QDialog):
4705
4926
 
4706
4927
  self.accept()
4707
4928
 
4929
+ class ColorOverlayDialog(QDialog):
4930
+
4931
+ def __init__(self, parent=None):
4932
+
4933
+ super().__init__(parent)
4934
+ self.setWindowTitle("Generate Node (or Edge) -> Color Overlay?")
4935
+ self.setModal(True)
4936
+
4937
+ layout = QFormLayout(self)
4938
+
4939
+ self.down_factor = QLineEdit("")
4940
+ layout.addRow("down_factor (for speeding up overlay generation - optional):", self.down_factor)
4941
+
4942
+ # Add Run button
4943
+ run_button = QPushButton("Generate (Will go to Overlay 2)")
4944
+ run_button.clicked.connect(self.coloroverlay)
4945
+ layout.addWidget(run_button)
4946
+
4947
+ def coloroverlay(self):
4948
+
4949
+ down_factor = float(self.down_factor.text()) if self.down_factor.text().strip() else None
4950
+
4951
+ if self.parent().active_channel == 0:
4952
+ mode = 0
4953
+ self.sort = 'Node'
4954
+ else:
4955
+ mode = 1
4956
+ self.sort = 'Edge'
4957
+
4958
+
4959
+ result, legend = my_network.node_to_color(down_factor = down_factor, mode = mode)
4960
+
4961
+ self.parent().format_for_upperright_table(legend, f'{self.sort} Id', f'Encoding Val: {self.sort}', 'Legend')
4962
+
4963
+
4964
+ self.parent().load_channel(3, channel_data = result, data = True)
4965
+
4966
+ self.accept()
4967
+
4708
4968
 
4709
4969
  class ShuffleDialog(QDialog):
4710
4970
 
@@ -4745,11 +5005,15 @@ class ShuffleDialog(QDialog):
4745
5005
  accepted_target = self.target_selector.currentIndex()
4746
5006
 
4747
5007
  if accepted_mode == 4:
5008
+ if self.parent().mini_overlay == True:
5009
+ self.parent().create_highlight_overlay(node_indices = self.parent().clicked_values['nodes'], edge_indices = self.parent().clicked_values['edges'])
4748
5010
  active_data = self.parent().highlight_overlay
4749
5011
  else:
4750
5012
  active_data = self.parent().channel_data[accepted_mode]
4751
5013
 
4752
5014
  if accepted_target == 4:
5015
+ if self.parent().mini_overlay == True:
5016
+ self.parent().create_highlight_overlay(node_indices = self.parent().clicked_values['nodes'], edge_indices = self.parent().clicked_values['edges'])
4753
5017
  target_data = self.parent().highlight_overlay
4754
5018
  else:
4755
5019
  target_data = self.parent().channel_data[accepted_target]
@@ -5952,7 +6216,18 @@ class MachineWindow(QMainWindow):
5952
6216
  self.GPU.setChecked(False)
5953
6217
  self.GPU.clicked.connect(self.toggle_GPU)
5954
6218
  self.use_gpu = False
6219
+ self.two = QPushButton("Train By 2D Slice Patterns (Cheaper - CPU only)")
6220
+ self.two.setCheckable(True)
6221
+ self.two.setChecked(True)
6222
+ self.two.clicked.connect(self.toggle_two)
6223
+ self.use_two = True
6224
+ self.three = QPushButton("Train by 3D Patterns")
6225
+ self.three.setCheckable(True)
6226
+ self.three.setChecked(False)
6227
+ self.three.clicked.connect(self.toggle_three)
5955
6228
  processing_layout.addWidget(self.GPU)
6229
+ processing_layout.addWidget(self.two)
6230
+ processing_layout.addWidget(self.three)
5956
6231
  processing_group.setLayout(processing_layout)
5957
6232
 
5958
6233
  # Group 3: Training Options
@@ -5970,6 +6245,7 @@ class MachineWindow(QMainWindow):
5970
6245
  segmentation_group = QGroupBox("Segmentation")
5971
6246
  segmentation_layout = QVBoxLayout()
5972
6247
  seg_button = QPushButton("Preview Segment")
6248
+ self.seg_button = seg_button
5973
6249
  seg_button.clicked.connect(self.start_segmentation)
5974
6250
  full_button = QPushButton("Segment All")
5975
6251
  full_button.clicked.connect(self.segment)
@@ -5987,9 +6263,12 @@ class MachineWindow(QMainWindow):
5987
6263
  self.setCentralWidget(main_widget)
5988
6264
 
5989
6265
  self.trained = False
6266
+ self.previewing = False
5990
6267
 
5991
6268
 
5992
6269
  self.segmenter = segmenter.InteractiveSegmenter(active_data, use_gpu=True)
6270
+ self.segmentation_worker = None
6271
+
5993
6272
 
5994
6273
 
5995
6274
  def toggle_GPU(self):
@@ -5997,6 +6276,27 @@ class MachineWindow(QMainWindow):
5997
6276
 
5998
6277
  self.use_gpu = self.GPU.isChecked()
5999
6278
 
6279
+ def toggle_two(self):
6280
+ if self.two.isChecked():
6281
+ # If button two is checked, ensure button three is unchecked
6282
+ self.three.setChecked(False)
6283
+ self.use_two = True
6284
+ else:
6285
+ # If button three is checked, ensure button two is unchecked
6286
+ self.three.setChecked(True)
6287
+ self.use_two = False
6288
+
6289
+ def toggle_three(self):
6290
+ if self.three.isChecked():
6291
+ # If button two is checked, ensure button three is unchecked
6292
+ self.two.setChecked(False)
6293
+ self.use_two = False
6294
+ else:
6295
+ # If button three is checked, ensure button two is unchecked
6296
+ self.two.setChecked(True)
6297
+ self.use_two = True
6298
+
6299
+
6000
6300
 
6001
6301
  def toggle_foreground(self):
6002
6302
 
@@ -6045,15 +6345,29 @@ class MachineWindow(QMainWindow):
6045
6345
  self.kill_segmentation()
6046
6346
  # Wait a bit for cleanup
6047
6347
  time.sleep(0.1)
6048
- self.segmenter.train_batch(self.parent().channel_data[2], speed = speed, use_gpu = self.use_gpu)
6049
- self.trained = True
6348
+ if not self.use_two:
6349
+ self.previewing = False
6350
+ try:
6351
+ self.segmenter.train_batch(self.parent().channel_data[2], speed = speed, use_gpu = self.use_gpu, use_two = self.use_two)
6352
+ self.trained = True
6353
+ except MemoryError:
6354
+ QMessageBox.critical(
6355
+ self,
6356
+ "Alert",
6357
+ "Out of memory computing feature maps. Note these for 3D require 7x the RAM of the active image (or 9x for the detailed map).\n Please use 2D slice models if you do not have enough RAM."
6358
+ )
6359
+
6050
6360
 
6051
6361
  def start_segmentation(self):
6052
6362
 
6053
6363
  self.kill_segmentation()
6054
6364
  time.sleep(0.1)
6055
6365
 
6056
- print("Beginning new segmentation...")
6366
+ if self.use_two:
6367
+ self.previewing = True
6368
+ else:
6369
+ print("Beginning new segmentation...")
6370
+
6057
6371
 
6058
6372
  if self.parent().active_channel == 0:
6059
6373
  if self.parent().channel_data[0] is not None:
@@ -6067,7 +6381,7 @@ class MachineWindow(QMainWindow):
6067
6381
  if not self.trained:
6068
6382
  return
6069
6383
  else:
6070
- self.segmentation_worker = SegmentationWorker(self.parent().highlight_overlay, self.segmenter, self.use_gpu)
6384
+ self.segmentation_worker = SegmentationWorker(self.parent().highlight_overlay, self.segmenter, self.use_gpu, self.use_two, self.previewing, self)
6071
6385
  self.segmentation_worker.chunk_processed.connect(self.update_display) # Just update display
6072
6386
  self.segmentation_worker.finished.connect(self.segmentation_finished)
6073
6387
  current_xlim = self.parent().ax.get_xlim()
@@ -6095,10 +6409,42 @@ class MachineWindow(QMainWindow):
6095
6409
  msg.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
6096
6410
  return msg.exec() == QMessageBox.StandardButton.Yes
6097
6411
 
6412
+
6413
+
6414
+ def check_for_z_change(self):
6415
+ current_z = self.parent().current_slice
6416
+ if not hasattr(self, '_last_z'):
6417
+ self._last_z = current_z
6418
+ return False
6419
+
6420
+ changed = (self._last_z != current_z)
6421
+ self._last_z = current_z
6422
+
6423
+ if changed and self.previewing and self.segmentation_worker is not None:
6424
+ self.segmentation_worker.stop()
6425
+ time.sleep(0.1)
6426
+
6427
+ # Force regeneration of chunks
6428
+ self.segmenter.realtimechunks = None
6429
+
6430
+ # Restart the worker
6431
+ self.start_segmentation()
6432
+
6433
+ return changed
6434
+
6098
6435
  def update_display(self):
6099
6436
  if not hasattr(self, '_last_update'):
6100
6437
  self._last_update = 0
6101
6438
 
6439
+ current_z = self.parent().current_slice
6440
+ if not hasattr(self, '_last_z'):
6441
+ self._last_z = current_z
6442
+
6443
+ self._last_z = current_z
6444
+
6445
+ if self.previewing:
6446
+ changed = self.check_for_z_change()
6447
+
6102
6448
  current_time = time.time()
6103
6449
  if current_time - self._last_update >= 1: # Match worker's interval
6104
6450
  try:
@@ -6116,19 +6462,84 @@ class MachineWindow(QMainWindow):
6116
6462
  except Exception as e:
6117
6463
  print(f"Display update error: {e}")
6118
6464
 
6465
+ def poke_segmenter(self):
6466
+ if self.use_two and self.previewing:
6467
+ try:
6468
+ # Clear any processing flags in the segmenter
6469
+ if hasattr(self.segmenter, '_currently_processing'):
6470
+ self.segmenter._currently_processing = None
6471
+
6472
+ # Force regenerating the worker
6473
+ if self.segmentation_worker is not None:
6474
+ self.kill_segmentation()
6475
+
6476
+ time.sleep(0.2)
6477
+ self.start_segmentation()
6478
+
6479
+ except Exception as e:
6480
+ print(f"Error in poke_segmenter: {e}")
6481
+ import traceback
6482
+ traceback.print_exc()
6483
+
6119
6484
  def segmentation_finished(self):
6120
- print("Segmentation completed")
6485
+ if not self.use_two:
6486
+ print("Segmentation completed")
6487
+
6121
6488
  current_xlim = self.parent().ax.get_xlim()
6122
6489
  current_ylim = self.parent().ax.get_ylim()
6123
6490
  self.parent().update_display(preserve_zoom=(current_xlim, current_ylim))
6491
+
6492
+ # Store the current z position before killing the worker
6493
+ current_z = self.parent().current_slice
6494
+
6495
+ # Clean up the worker
6124
6496
  self.kill_segmentation()
6497
+ self.segmentation_worker = None
6125
6498
  time.sleep(0.1)
6499
+
6500
+ # Auto-restart for 2D preview mode only if certain conditions are met
6501
+ if self.previewing and self.use_two:
6502
+ # Track when this slice was last processed
6503
+ if not hasattr(self, '_processed_slices'):
6504
+ self._processed_slices = {}
6505
+
6506
+ current_time = time.time()
6507
+
6508
+ # Check if we've recently tried to process this slice (to prevent loops)
6509
+ recently_processed = False
6510
+ if current_z in self._processed_slices:
6511
+ time_since_last_attempt = current_time - self._processed_slices[current_z]
6512
+ recently_processed = time_since_last_attempt < 5.0 # 5 second cooldown
6513
+
6514
+ if not recently_processed:
6515
+ self._processed_slices[current_z] = current_time
6516
+
6517
+ # Reset any processing flags in the segmenter
6518
+ if hasattr(self.segmenter, '_currently_processing'):
6519
+ self.segmenter._currently_processing = None
6520
+
6521
+ # Create a new worker after a brief delay
6522
+ QTimer.singleShot(500, self.start_segmentation)
6523
+
6126
6524
 
6127
6525
 
6128
6526
  def kill_segmentation(self):
6129
- if hasattr(self, 'segmentation_worker'):
6527
+ if hasattr(self, 'segmentation_worker') and self.segmentation_worker is not None:
6528
+ # Signal the thread to stop
6130
6529
  self.segmentation_worker.stop()
6131
- del self.segmentation_worker # Clean up reference
6530
+
6531
+ # Wait for the thread to finish
6532
+ if self.segmentation_worker.isRunning():
6533
+ self.segmentation_worker.wait(1000) # Wait up to 1 second
6534
+
6535
+ # If thread is still running after timeout, try to force termination
6536
+ if self.segmentation_worker.isRunning():
6537
+ self.segmentation_worker.terminate()
6538
+ self.segmentation_worker.wait() # Wait for it to be terminated
6539
+
6540
+ # Now safe to delete
6541
+ del self.segmentation_worker
6542
+ self.segmentation_worker = None
6132
6543
 
6133
6544
 
6134
6545
  def segment(self):
@@ -6141,6 +6552,8 @@ class MachineWindow(QMainWindow):
6141
6552
  self.kill_segmentation()
6142
6553
  time.sleep(0.1)
6143
6554
 
6555
+ self.previewing = False
6556
+
6144
6557
  if self.parent().active_channel == 0:
6145
6558
  if self.parent().channel_data[0] is not None:
6146
6559
  active_data = self.parent().channel_data[0]
@@ -6170,20 +6583,26 @@ class MachineWindow(QMainWindow):
6170
6583
 
6171
6584
  self.parent().update_display()
6172
6585
 
6586
+ self.previewing = False
6587
+
6173
6588
  print("Finished segmentation moved to Overlay 2. Use File -> Save(As) for disk saving.")
6174
6589
 
6175
6590
  def closeEvent(self, event):
6176
6591
  if self.parent().isVisible():
6177
6592
  if self.confirm_close_dialog():
6178
-
6593
+ # Clean up resources before closing
6179
6594
  if self.brush_button.isChecked():
6180
6595
  self.silence_button()
6181
6596
  self.toggle_brush_mode()
6597
+
6182
6598
  self.parent().pen_button.setEnabled(True)
6183
6599
  self.parent().brush_mode = False
6184
- self.parent().machine_window = None
6600
+
6601
+ # Kill the segmentation thread and wait for it to finish
6185
6602
  self.kill_segmentation()
6186
- time.sleep(0.1)
6603
+ time.sleep(0.2) # Give additional time for cleanup
6604
+
6605
+ self.parent().machine_window = None
6187
6606
  else:
6188
6607
  event.ignore()
6189
6608
 
@@ -6194,48 +6613,84 @@ class SegmentationWorker(QThread):
6194
6613
  finished = pyqtSignal()
6195
6614
  chunk_processed = pyqtSignal()
6196
6615
 
6197
- def __init__(self, highlight_overlay, segmenter, use_gpu):
6616
+ def __init__(self, highlight_overlay, segmenter, use_gpu, use_two, previewing, machine_window):
6198
6617
  super().__init__()
6199
6618
  self.overlay = highlight_overlay
6200
6619
  self.segmenter = segmenter
6201
6620
  self.use_gpu = use_gpu
6621
+ self.use_two = use_two
6622
+ self.previewing = previewing
6623
+ self.machine_window = machine_window
6202
6624
  self._stop = False
6203
6625
  self.update_interval = 1 # Increased to 500ms
6204
6626
  self.chunks_since_update = 0
6205
6627
  self.chunks_per_update = 5 # Only update every 5 chunks
6628
+ self.poked = False # If it should wake up or not
6206
6629
  self.last_update = time.time()
6207
6630
 
6208
6631
  def stop(self):
6209
6632
  self._stop = True
6633
+
6634
+ def get_poked(self):
6635
+ self.poked = True
6210
6636
 
6211
6637
  def run(self):
6212
6638
  try:
6213
6639
  self.overlay.fill(False)
6214
6640
 
6215
- for foreground_coords, background_coords in self.segmenter.segment_volume_realtime(gpu = self.use_gpu):
6216
- if self._stop:
6217
- break
6641
+ # Remember the starting z position
6642
+ self.starting_z = self.segmenter.current_z
6643
+
6644
+ if self.previewing and self.use_two:
6645
+ # Process current z-slice in chunks
6646
+ current_z = self.segmenter.current_z
6647
+
6648
+ # Process the slice with chunked generator
6649
+ for foreground, background in self.segmenter.segment_slice_chunked(current_z):
6650
+ if self._stop:
6651
+ break
6218
6652
 
6219
- for z,y,x in foreground_coords:
6220
- self.overlay[z,y,x] = 1
6221
- for z,y,x in background_coords:
6222
- self.overlay[z,y,x] = 2
6223
-
6224
- # Update only after several chunks AND minimum time interval
6225
- self.chunks_since_update += 1
6226
- current_time = time.time()
6227
- if (self.chunks_since_update >= self.chunks_per_update and
6228
- current_time - self.last_update >= self.update_interval):
6229
- self.chunk_processed.emit()
6230
- self.chunks_since_update = 0
6231
- self.last_update = current_time
6232
-
6653
+ # Update the overlay
6654
+ for z,y,x in foreground:
6655
+ self.overlay[z,y,x] = 1
6656
+ for z,y,x in background:
6657
+ self.overlay[z,y,x] = 2
6658
+
6659
+ # Signal update after each chunk
6660
+ self.chunks_since_update += 1
6661
+ current_time = time.time()
6662
+ if (self.chunks_since_update >= self.chunks_per_update and
6663
+ current_time - self.last_update >= self.update_interval):
6664
+ self.chunk_processed.emit()
6665
+ self.chunks_since_update = 0
6666
+ self.last_update = current_time
6667
+
6668
+ else:
6669
+ # Original 3D approach
6670
+ for foreground_coords, background_coords in self.segmenter.segment_volume_realtime(gpu=self.use_gpu):
6671
+ if self._stop:
6672
+ break
6673
+
6674
+ for z,y,x in foreground_coords:
6675
+ self.overlay[z,y,x] = 1
6676
+ for z,y,x in background_coords:
6677
+ self.overlay[z,y,x] = 2
6678
+
6679
+ self.chunks_since_update += 1
6680
+ current_time = time.time()
6681
+ if (self.chunks_since_update >= self.chunks_per_update and
6682
+ current_time - self.last_update >= self.update_interval):
6683
+ self.chunk_processed.emit()
6684
+ self.chunks_since_update = 0
6685
+ self.last_update = current_time
6686
+
6233
6687
  self.finished.emit()
6234
6688
 
6235
6689
  except Exception as e:
6236
6690
  print(f"Error in segmentation: {e}")
6237
- raise
6238
-
6691
+ import traceback
6692
+ traceback.print_exc()
6693
+
6239
6694
  def run_batch(self):
6240
6695
  try:
6241
6696
  foreground_coords, _ = self.segmenter.segment_volume()
@@ -6851,6 +7306,8 @@ class MaskDialog(QDialog):
6851
7306
  output_target = self.output_selector.currentIndex()
6852
7307
 
6853
7308
  if accepted_mode == 4:
7309
+ if self.parent().mini_overlay == True:
7310
+ self.parent().create_highlight_overlay(node_indices = self.parent().clicked_values['nodes'], edge_indices = self.parent().clicked_values['edges'])
6854
7311
  active_data = self.parent().highlight_overlay
6855
7312
  else:
6856
7313
  active_data = self.parent().channel_data[accepted_mode]