nettracer3d 0.4.2__py3-none-any.whl → 0.4.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/morphology.py CHANGED
@@ -101,6 +101,8 @@ def process_label(args):
101
101
  nodes, edges, label, dilate_xy, dilate_z, array_shape = args
102
102
  print(f"Processing node {label}")
103
103
  indices = np.argwhere(nodes == label)
104
+ if len(indices) == 0:
105
+ return None, None, None
104
106
  z_vals, y_vals, x_vals = get_reslice_indices((indices, dilate_xy, dilate_z, array_shape))
105
107
  if z_vals is None: #If get_reslice_indices ran into a ValueError, nothing is returned.
106
108
  return None, None, None
nettracer3d/nettracer.py CHANGED
@@ -1003,6 +1003,8 @@ def dilate_3D_recursive(tiff_array, dilated_x, dilated_y, dilated_z, step_size=N
1003
1003
  max_dilation = max(dilated_x, dilated_y, dilated_z)
1004
1004
  if max_dilation < (0.2 * min_dim):
1005
1005
  return dilate_3D(tiff_array, dilated_x, dilated_y, dilated_z)
1006
+ elif dilated_x == 1 and dilated_y == 1 and dilated_z == 1: #Also if there is only a single dilation don't do it
1007
+ return dilate_3D(tiff_array, dilated_x, dilated_y, dilated_z)
1006
1008
 
1007
1009
  # Initialize step_size for first call
1008
1010
  if step_size is None:
@@ -1587,6 +1589,51 @@ def label_branches(array, peaks = 0, branch_removal = 0, comp_dil = 0, max_vol =
1587
1589
 
1588
1590
  return array
1589
1591
 
1592
+ def fix_branches(array, G, communities, fix_val = None):
1593
+
1594
+ def invert_dict(d):
1595
+ inverted = {}
1596
+ for key, value in d.items():
1597
+ inverted.setdefault(value, []).append(key)
1598
+ return inverted
1599
+
1600
+ def get_degree_threshold(community_degrees):
1601
+ degrees = np.array(community_degrees, dtype=float)
1602
+ hist, bins = np.histogram(degrees, bins='auto')
1603
+ peaks, _ = find_peaks(hist)
1604
+ if len(peaks) > 1:
1605
+ # Get bin value after first peak as threshold
1606
+ return bins[peaks[0] + 1]
1607
+ return 4 # Default fallback
1608
+
1609
+ avg_degree = G.number_of_edges() * 2 / G.number_of_nodes()
1610
+
1611
+ targs = []
1612
+
1613
+ inverted = invert_dict(communities)
1614
+
1615
+ community_degrees = {}
1616
+
1617
+ for com in inverted:
1618
+ subgraph = G.subgraph(inverted[com])
1619
+ sub_degree = subgraph.number_of_edges() * 2/ subgraph.number_of_nodes()
1620
+ community_degrees[com] = sub_degree
1621
+
1622
+
1623
+ if fix_val is None:
1624
+ threshold = get_degree_threshold(list(community_degrees.values()))
1625
+ else:
1626
+ threshold = fix_val
1627
+
1628
+ for com in community_degrees:
1629
+ if community_degrees[com] > threshold: #This method of comparison could possibly be more nuanced.
1630
+ targs.append(com)
1631
+
1632
+
1633
+ return targs
1634
+
1635
+
1636
+
1590
1637
  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):
1591
1638
  """
1592
1639
  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.
@@ -1789,6 +1836,7 @@ def watershed(image, directory = None, proportion = 0.1, GPU = True, smallest_ra
1789
1836
  gotoexcept = 1/0
1790
1837
 
1791
1838
  except (cp.cuda.memory.OutOfMemoryError, ZeroDivisionError) as e:
1839
+
1792
1840
  if predownsample is None:
1793
1841
  down_factor = smart_dilate.catch_memory(e) #Obtain downsample amount based on memory missing
1794
1842
  else:
@@ -1819,8 +1867,12 @@ def watershed(image, directory = None, proportion = 0.1, GPU = True, smallest_ra
1819
1867
 
1820
1868
  labels, _ = label_objects(distance)
1821
1869
 
1870
+ if len(labels.shape) ==2:
1871
+ labels = np.expand_dims(labels, axis = 0)
1872
+
1822
1873
  del distance
1823
1874
 
1875
+
1824
1876
  if labels.shape[1] < original_shape[1]: #If downsample was used, upsample output
1825
1877
  labels = upsample_with_padding(labels, downsample_needed, original_shape)
1826
1878
  labels = labels * old_mask
@@ -1829,8 +1881,7 @@ def watershed(image, directory = None, proportion = 0.1, GPU = True, smallest_ra
1829
1881
  labels = smart_dilate.smart_label(image, labels, GPU = GPU, predownsample = predownsample2)
1830
1882
 
1831
1883
  if directory is None:
1832
- tifffile.imwrite("Watershed_output.tif", labels)
1833
- print("Watershed saved to 'Watershed_output.tif'")
1884
+ pass
1834
1885
  else:
1835
1886
  tifffile.imwrite(f"{directory}/Watershed_output.tif", labels)
1836
1887
  print(f"Watershed saved to {directory}/'Watershed_output.tif'")
@@ -3417,6 +3468,92 @@ class Network_3D:
3417
3468
  self._nodes = self._nodes.astype(np.uint16)
3418
3469
 
3419
3470
 
3471
+ def com_to_node(self, targets = None):
3472
+
3473
+ def invert_dict(d):
3474
+ inverted = {}
3475
+ for key, value in d.items():
3476
+ inverted.setdefault(value, []).append(key)
3477
+ return inverted
3478
+
3479
+ def update_array(array_3d, value_dict, targets = None):
3480
+ ref_array = copy.deepcopy(array_3d)
3481
+ if targets is None:
3482
+ for key, value_list in value_dict.items():
3483
+ for value in value_list:
3484
+ array_3d[ref_array == value] = key
3485
+ else:
3486
+ max_val = np.max(array_3d) + 1
3487
+ for key, value_list in value_dict.items():
3488
+ for value in value_list:
3489
+ array_3d[ref_array == value] = max_val
3490
+ max_val += 1
3491
+
3492
+ return array_3d
3493
+
3494
+ if 0 in self.communities.values():
3495
+ self.communities = {k: v + 1 for k, v in self.communities.items()}
3496
+ if targets is not None:
3497
+ for item in targets:
3498
+ item = item + 1
3499
+
3500
+ inverted = invert_dict(self.communities)
3501
+
3502
+
3503
+ if targets is not None:
3504
+ new_inverted = copy.deepcopy(inverted)
3505
+ for com in inverted:
3506
+ if com not in targets:
3507
+ del new_inverted[com]
3508
+ inverted = new_inverted
3509
+
3510
+
3511
+ if self._node_identities is not None:
3512
+ new_identities = {}
3513
+ for com in inverted:
3514
+ new_identities[com] = ""
3515
+
3516
+ list1 = self._network_lists[0] #Get network lists to change
3517
+ list2 = self._network_lists[1]
3518
+ list3 = self._network_lists[2]
3519
+
3520
+ for i in range(len(list1)):
3521
+ list1[i] = self.communities[list1[i]] #Set node at network list spot to its community instead
3522
+ list2[i] = self.communities[list2[i]]
3523
+ if list1[i] == list2[i]: #If the edge corresponding there joins different communities, it will not be set to 0
3524
+ list3[i] = 0
3525
+
3526
+
3527
+ self.network_lists = [list1, list2, list3]
3528
+
3529
+ if self._nodes is not None:
3530
+ self._nodes = update_array(self._nodes, inverted, targets = targets) #Set the array to match the new network
3531
+
3532
+ try:
3533
+
3534
+ if self._node_identities is not None:
3535
+
3536
+ for key, value_list in inverted.items():
3537
+ temp_dict = {}
3538
+ for value in value_list:
3539
+ if self._node_identities[value] in temp_dict:
3540
+ temp_dict[self._node_identities[value]] += 1
3541
+ else:
3542
+ temp_dict[self._node_identities[value]] = 1
3543
+ for id_type, num in temp_dict.items():
3544
+ new_identities[key] += f'ID {id_type}:{num}, '
3545
+
3546
+ self.node_identities = new_identities
3547
+ except:
3548
+ pass
3549
+
3550
+
3551
+
3552
+
3553
+
3554
+
3555
+
3556
+
3420
3557
  def trunk_to_node(self):
3421
3558
  """
3422
3559
  Converts the edge 'trunk' into a node. In this case, the trunk is the edge that creates the most node-node connections. There may be times when many nodes are connected by a single, expansive edge that obfuscates the rest of the edges. Converting the trunk to a node can better reveal these edges.