nettracer3d 0.3.5__tar.gz → 0.3.7__tar.gz

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.
Files changed (23) hide show
  1. {nettracer3d-0.3.5/src/nettracer3d.egg-info → nettracer3d-0.3.7}/PKG-INFO +2 -2
  2. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/pyproject.toml +2 -2
  3. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/src/nettracer3d/nettracer.py +88 -174
  4. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/src/nettracer3d/nettracer_gui.py +49 -18
  5. {nettracer3d-0.3.5 → nettracer3d-0.3.7/src/nettracer3d.egg-info}/PKG-INFO +2 -2
  6. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/src/nettracer3d.egg-info/requires.txt +1 -1
  7. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/LICENSE +0 -0
  8. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/README.md +0 -0
  9. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/setup.cfg +0 -0
  10. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/src/nettracer3d/__init__.py +0 -0
  11. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/src/nettracer3d/community_extractor.py +0 -0
  12. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/src/nettracer3d/hub_getter.py +0 -0
  13. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/src/nettracer3d/modularity.py +0 -0
  14. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/src/nettracer3d/morphology.py +0 -0
  15. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/src/nettracer3d/network_analysis.py +0 -0
  16. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/src/nettracer3d/network_draw.py +0 -0
  17. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/src/nettracer3d/node_draw.py +0 -0
  18. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/src/nettracer3d/proximity.py +0 -0
  19. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/src/nettracer3d/simple_network.py +0 -0
  20. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/src/nettracer3d/smart_dilate.py +0 -0
  21. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/src/nettracer3d.egg-info/SOURCES.txt +0 -0
  22. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/src/nettracer3d.egg-info/dependency_links.txt +0 -0
  23. {nettracer3d-0.3.5 → nettracer3d-0.3.7}/src/nettracer3d.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: nettracer3d
3
- Version: 0.3.5
3
+ Version: 0.3.7
4
4
  Summary: Scripts for intializing and analyzing networks from segmentations of three dimensional images.
5
5
  Author-email: Liam McLaughlin <boom2449@gmail.com>
6
6
  Project-URL: User_Manual, https://drive.google.com/drive/folders/1fTkz3n4LN9_VxKRKC8lVQSlrz_wq0bVn?usp=drive_link
@@ -20,7 +20,7 @@ Requires-Dist: networkx
20
20
  Requires-Dist: opencv-python-headless
21
21
  Requires-Dist: openpyxl
22
22
  Requires-Dist: pandas
23
- Requires-Dist: plotly
23
+ Requires-Dist: napari
24
24
  Requires-Dist: python-louvain
25
25
  Requires-Dist: tifffile
26
26
  Requires-Dist: PyQt6
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nettracer3d"
3
- version = "0.3.5"
3
+ version = "0.3.7"
4
4
  authors = [
5
5
  { name="Liam McLaughlin", email="boom2449@gmail.com" },
6
6
  ]
@@ -15,7 +15,7 @@ dependencies = [
15
15
  "opencv-python-headless",
16
16
  "openpyxl",
17
17
  "pandas",
18
- "plotly",
18
+ "napari",
19
19
  "python-louvain",
20
20
  "tifffile",
21
21
  "PyQt6"
@@ -10,7 +10,10 @@ import multiprocessing as mp
10
10
  import os
11
11
  import copy
12
12
  import statistics as stats
13
- import plotly.graph_objects as go
13
+ try:
14
+ import napari
15
+ except:
16
+ pass
14
17
  import networkx as nx
15
18
  from scipy.signal import find_peaks
16
19
  try:
@@ -396,7 +399,7 @@ def upsample_with_padding(data, factor=None, original_shape=None):
396
399
  raise ValueError("original_shape must be provided")
397
400
 
398
401
  # Handle 4D color arrays
399
- is_color = len(data.shape) == 4 and data.shape[-1] == 3
402
+ is_color = len(data.shape) == 4 and (data.shape[-1] == 3 or data.shape[-1] == 4)
400
403
  if is_color:
401
404
  # Split into separate color channels
402
405
  channels = [data[..., i] for i in range(3)]
@@ -623,6 +626,65 @@ def threshold(arr, proportion, custom_rad = None):
623
626
 
624
627
  return arr
625
628
 
629
+ def show_3d(arrays_3d=None, arrays_4d=None, down_factor=None, order=0, xy_scale=1, z_scale=1, colors=['red', 'green', 'white', 'cyan', 'yellow']):
630
+ """
631
+ Show 3d (or 2d) displays of array data using napari.
632
+ Params: arrays - A list of 3d or 2d numpy arrays to display
633
+ down_factor (int) - Optional downsampling factor to speed up display
634
+ """
635
+ import os
636
+ # Force PyQt6 usage to avoid binding warning
637
+ os.environ['QT_API'] = 'pyqt6'
638
+
639
+ import napari
640
+ from qtpy.QtWidgets import QApplication
641
+
642
+ if down_factor is not None:
643
+ # Downsample arrays if specified
644
+ arrays_3d = [downsample(array, down_factor, order=order) for array in arrays_3d] if arrays_3d is not None else None
645
+ arrays_4d = [downsample(array, down_factor, order=order) for array in arrays_4d] if arrays_4d is not None else None
646
+
647
+ viewer = napari.Viewer(ndisplay=3)
648
+ scale = [z_scale, xy_scale, xy_scale] # [z, y, x] order for napari
649
+
650
+ # Add 3D arrays if provided
651
+ if arrays_3d is not None:
652
+ for arr, color in zip(arrays_3d, colors):
653
+ viewer.add_image(
654
+ arr,
655
+ scale=scale,
656
+ colormap=color,
657
+ rendering='mip',
658
+ blending='additive',
659
+ opacity=0.5,
660
+ name=f'Channel_{color}'
661
+ )
662
+
663
+ if arrays_4d is not None:
664
+ for i, arr in enumerate(arrays_4d):
665
+ # Check if the last dimension is 3 (RGB) or 4 (RGBA)
666
+ if arr.shape[-1] not in [3, 4]:
667
+ print(f"Warning: Array {i} doesn't appear to be RGB/RGBA. Skipping.")
668
+ continue
669
+
670
+ if arr.shape[3] == 4:
671
+ arr = arr[:, :, :, :3] # Remove alpha
672
+
673
+ # Add each color channel separately
674
+ colors = ['red', 'green', 'blue']
675
+ for c in range(3):
676
+ viewer.add_image(
677
+ arr[:,:,:,c], # Take just one color channel
678
+ scale=scale,
679
+ colormap=colors[c], # Use corresponding color
680
+ rendering='mip',
681
+ blending='additive',
682
+ opacity=0.5,
683
+ name=f'Channel_{colors[c]}_{i}'
684
+ )
685
+
686
+ napari.run()
687
+
626
688
  def z_project(array3d, method='max'):
627
689
  """
628
690
  Project a 3D numpy array along the Z axis to create a 2D array.
@@ -733,6 +795,18 @@ def fill_holes_3d(array):
733
795
  def resize(array, factor, order = 0):
734
796
  """Simply resizes an array by a factor"""
735
797
 
798
+ if len(array.shape) == 4: # presumably this is a color image
799
+ processed_arrays = []
800
+ for i in range(array.shape[3]): # iterate through the color dimension
801
+ color_array = array[:, :, :, i] # get 3D array for each color channel
802
+ processed_color = zoom(color_array, (factor), order = order)
803
+
804
+ processed_arrays.append(processed_color)
805
+
806
+ # Stack them back together along the 4th dimension
807
+ result = np.stack(processed_arrays, axis=3)
808
+ return result
809
+
736
810
  array = zoom(array, (factor), order = order)
737
811
 
738
812
  return array
@@ -748,103 +822,6 @@ def _rescale(array, original_shape, xy_scale, z_scale):
748
822
  array = zoom(array, (1, z_scale/xy_scale, z_scale/xy_scale))
749
823
  return array
750
824
 
751
- def visualize_3D(array, other_arrays=None, xy_scale = 1, z_scale = 1):
752
- """
753
- Mostly internal method for 3D visualization, although can be run directly on tif files to view them. Uses plotly to visualize
754
- a 3D, binarized isosurface of data. Note this method likely requires downsampling on objects before running.
755
- :param array: (Mandatory; string or ndarray) - Either a path to a .tif file to visualize in 3D binary, or a ndarray of the same.
756
- :param other_arrays: (Optional - Val = None; string, ndarray, or list) - Either a path to a an additional .tif file to visualize in 3D binary or an ndarray containing the same,
757
- or otherwise a path to a directory containing ONLY other .tif files to visualize, or a list of ndarrays containing the same.
758
- :param xy_scale: (Optional - Val = 1; float) - The xy pixel scaling of an image to visualize.
759
- :param z_scale: (Optional - Val = 1; float) - The z voxel depth of an image to visualize.
760
- """
761
-
762
- if isinstance(array, str):
763
- array = tifffile.imread(array)
764
-
765
- original_shape = array.shape[1]
766
-
767
- array = _rescale(array, original_shape, xy_scale, z_scale)
768
- array = binarize(array)
769
-
770
- # Create a meshgrid for coordinates
771
- x, y, z = np.indices(array.shape)
772
-
773
- # Create a figure
774
- fig = go.Figure()
775
-
776
- # Plot the main array
777
- _plot_3D(fig, x, y, z, array, 'red')
778
-
779
- if other_arrays is not None and ((type(other_arrays) == str) or (type(other_arrays) == list)):
780
- try: #Presume single tif
781
- array = tifffile.imread(other_arrays)
782
- if array.shape[1] != original_shape:
783
- array = downsample(array, array.shape[1]/original_shape)
784
- array = _rescale(array, original_shape, xy_scale, z_scale)
785
- array = binarize(array)
786
- _plot_3D(fig, x, y, z, array, 'green')
787
- except: #presume directory or list
788
- basic_colors = ['blue', 'yellow', 'cyan', 'magenta', 'black', 'white', 'gray', 'orange', 'brown', 'pink', 'purple', 'lime', 'teal', 'navy', 'maroon', 'olive', 'silver', 'red', 'green']
789
- try: #presume directory
790
- arrays = directory_info(other_arrays)
791
- directory = other_arrays
792
- except: #presume list
793
- arrays = other_arrays
794
- for i, array_path in enumerate(arrays):
795
- try: #presume tif
796
- array = tifffile.imread(f"{directory}/{array_path}")
797
- if array.shape[1] != original_shape:
798
- array = downsample(array, array.shape[1]/original_shape)
799
- array = _rescale(array, original_shape, xy_scale, z_scale)
800
- array = binarize(array)
801
- except: #presume array
802
- array = array_path
803
- del array_path
804
- if array is not None:
805
- if array.shape[1] != original_shape:
806
- array = downsample(array, array.shape[1]/original_shape)
807
- array = _rescale(array, original_shape, xy_scale, z_scale)
808
- array = binarize(array)
809
- color = basic_colors[i % len(basic_colors)] # Ensure color index wraps around if more arrays than colors
810
- if array is not None:
811
- _plot_3D(fig, x, y, z, array, color)
812
- else:
813
- try:
814
- other_arrays = _rescale(other_arrays, original_shape, xy_scale, z_scale)
815
- other_arrays = binarize(other_arrays)
816
- _plot_3D(fig, x, y, z, other_arrays, 'green')
817
- except:
818
- pass
819
-
820
- # Set the layout for better visualization
821
- fig.update_layout(scene=dict(
822
- xaxis_title='Z Axis',
823
- yaxis_title='Y Axis',
824
- zaxis_title='X Axis'
825
- ))
826
-
827
- fig.show()
828
-
829
- def _plot_3D(fig, x, y, z, array, color):
830
- """Internal method used for 3D visualization"""
831
- # Define the isosurface level
832
- level = 0.5
833
-
834
- # Add the isosurface to the figure
835
- fig.add_trace(go.Isosurface(
836
- x=x.flatten(),
837
- y=y.flatten(),
838
- z=z.flatten(),
839
- value=array.flatten(),
840
- isomin=level,
841
- isomax=level,
842
- opacity=0.6, # Adjust opacity
843
- surface_count=1, # Show only the isosurface
844
- colorscale=[[0, color], [1, color]], # Set uniform color
845
- showscale=False # Hide color scale bar
846
- ))
847
-
848
825
 
849
826
  def remove_trunk(edges):
850
827
  """
@@ -1380,6 +1357,17 @@ def downsample(data, factor, directory=None, order=0):
1380
1357
  data = tifffile.imread(data)
1381
1358
  else:
1382
1359
  data2 = None
1360
+
1361
+ if len(data.shape) == 4: # presumably this is a color image
1362
+ processed_arrays = []
1363
+ for i in range(data.shape[3]): # iterate through the color dimension
1364
+ color_array = data[:, :, :, i] # get 3D array for each color channel
1365
+ processed_color = downsample(color_array, factor, directory = None, order = order) #right now this is only for internal use - color array downsampling that is
1366
+ processed_arrays.append(processed_color)
1367
+
1368
+ # Stack them back together along the 4th dimension
1369
+ result = np.stack(processed_arrays, axis=3)
1370
+ return result
1383
1371
 
1384
1372
  # Check if Z dimension is too small relative to downsample factor
1385
1373
  if data.ndim == 3 and data.shape[0] < factor * 4:
@@ -1389,6 +1377,7 @@ def downsample(data, factor, directory=None, order=0):
1389
1377
  else:
1390
1378
  zoom_factors = 1/factor
1391
1379
 
1380
+
1392
1381
  # Apply downsampling
1393
1382
  data = zoom(data, zoom_factors, order=order)
1394
1383
 
@@ -3741,81 +3730,6 @@ class Network_3D:
3741
3730
 
3742
3731
 
3743
3732
 
3744
- #Methods relating to visualizing elements of the network in 3D
3745
-
3746
- def show_3D(self, other_arrays = None, down_factor = 1):
3747
- """
3748
- Allows the Network_3D object to be visualized in 3D using plotly. By default, this will show the nodes and edges properties. All arrays involved will be made binary.
3749
- Note that nettracer_3D is not primarily a 3D visualization tool, so the funcionality of this method is limited, and additionally it should really only be run on downsampled data.
3750
- :param other_arrays: (Optional - Val = None; string). A filepath to additional .tif files (or a directory containing only .tif files) to show alongside the Network_3D object, for example a node_indicies or network_lattice overlay.
3751
- :param down_factor: (Optional - Val = 1; int). A downsampling factor to speed up showing the 3D display and improve processing. Note that ALL arrays being shown will be subject
3752
- to this downsample factor. If you have files to be shown alongside the Network_3D object that were ALREADY downsampled, instead downsample the Network_3D object FIRST and pass nothing to this value.
3753
- If arrays are sized to different shapes while show_3D() is being called, there may be unusual results.
3754
- """
3755
- if down_factor > 1:
3756
- xy_scale = down_factor * self._xy_scale
3757
- z_scale = down_factor * self._z_scale
3758
- try:
3759
- nodes = downsample(self._nodes, down_factor, order = 3)
3760
- nodes = binarize(nodes)
3761
- except:
3762
- pass
3763
- try:
3764
- edges = downsample(self._edges, down_factor, order = 3)
3765
- edges = binarize(edges)
3766
- except:
3767
- edges = None
3768
- try:
3769
- if not isinstance(other_arrays, np.ndarray):
3770
- other_arrays = tifffile.imread(other_arrays)
3771
- if other_arrays.shape == self._nodes.shape:
3772
- other_arrays = downsample(other_arrays, down_factor, order = 3)
3773
- other_arrays = binarize(other_arrays)
3774
- other_arrays = [edges, other_arrays]
3775
- except:
3776
- try:
3777
- arrays = directory_info(other_arrays)
3778
- directory = other_arrays
3779
- other_arrays = []
3780
- for array in arrays:
3781
- array = tifffile.imread(f'{directory}/{array}')
3782
- if array.shape == self._nodes.shape:
3783
- array = downsample(array, down_factor, order = 3)
3784
- array = binarize(array)
3785
- other_arrays.append(array)
3786
- other_arrays.insert(0, edges)
3787
- except:
3788
- other_arrays = edges
3789
- visualize_3D(nodes, other_arrays, xy_scale = xy_scale, z_scale = z_scale)
3790
- else:
3791
- try:
3792
- nodes = binarize(self._nodes)
3793
- except:
3794
- pass
3795
- try:
3796
- edges = binarize(self._edges)
3797
- except:
3798
- edges = None
3799
- try:
3800
- if not isinstance(other_arrays, np.ndarray):
3801
- other_arrays = tifffile.imread(other_arrays)
3802
- other_arrays = binarize(other_arrays)
3803
- other_arrays = [edges, other_arrays]
3804
- except:
3805
- try:
3806
- arrays = directory_info(other_arrays)
3807
- directory = other_arrays
3808
- other_arrays = []
3809
- for array in arrays:
3810
- array = tifffile.imread(f'{directory}/{array}')
3811
- array = binarize(array)
3812
- other_arrays.append(array)
3813
- other_arrays.insert(0, self._edges)
3814
- except:
3815
- other_arrays = edges
3816
-
3817
- visualize_3D(nodes, other_arrays, xy_scale = self._xy_scale, z_scale = self._z_scale)
3818
-
3819
3733
  def get_degrees(self, down_factor = 1, directory = None, called = False, no_img = 0):
3820
3734
  """
3821
3735
  Method to obtain information on the degrees of nodes in the network, also generating overlays that relate this information to the 3D structure.
@@ -564,7 +564,7 @@ class ImageViewerWindow(QMainWindow):
564
564
  link_nodes.triggered.connect(self.handle_link)
565
565
  delink_nodes = highlight_menu.addAction("Split Nodes")
566
566
  delink_nodes.triggered.connect(self.handle_split)
567
- context_menu.addMenu(highlight_menu)
567
+ context_menu.addMenu(highlight_menu)
568
568
 
569
569
  # Create measure menu
570
570
  measure_menu = QMenu("Measure", self)
@@ -1880,7 +1880,7 @@ class ImageViewerWindow(QMainWindow):
1880
1880
  searchoverlay_action.triggered.connect(self.show_search_dialog)
1881
1881
  shuffle_action = overlay_menu.addAction("Shuffle")
1882
1882
  shuffle_action.triggered.connect(self.show_shuffle_dialog)
1883
- show3d_action = image_menu.addAction("Show 3D (beta)")
1883
+ show3d_action = image_menu.addAction("Show 3D (Napari)")
1884
1884
  show3d_action.triggered.connect(self.show3d_dialog)
1885
1885
 
1886
1886
 
@@ -2339,7 +2339,9 @@ class ImageViewerWindow(QMainWindow):
2339
2339
  f"Select Directory for Network3D Object",
2340
2340
  "",
2341
2341
  QFileDialog.Option.ShowDirsOnly
2342
- )
2342
+ )
2343
+ self.reset(nodes = True, network = True, xy_scale = 1, z_scale = 1, edges = True, search_region = True, network_overlay = True, id_overlay = True)
2344
+
2343
2345
 
2344
2346
  my_network.assemble(directory)
2345
2347
 
@@ -2597,6 +2599,8 @@ class ImageViewerWindow(QMainWindow):
2597
2599
 
2598
2600
  if assign_shape: #keep original shape tracked to undo resampling.
2599
2601
  self.original_shape = self.channel_data[channel_index].shape
2602
+ if len(self.original_shape) == 4:
2603
+ self.original_shape = (self.original_shape[0], self.original_shape[1], self.original_shape[2])
2600
2604
 
2601
2605
  self.update_display()
2602
2606
 
@@ -3923,19 +3927,19 @@ class ColorDialog(QDialog):
3923
3927
  class Show3dDialog(QDialog):
3924
3928
  def __init__(self, parent=None):
3925
3929
  super().__init__(parent)
3926
- self.setWindowTitle("Display Parameters")
3930
+ self.setWindowTitle("Display Parameters (Napari)")
3927
3931
  self.setModal(True)
3928
3932
 
3929
3933
  layout = QFormLayout(self)
3930
3934
 
3931
- self.downsample = QLineEdit("1")
3932
- layout.addRow("Downsample Factor (Expect Slowness on Large Images):", self.downsample)
3935
+ self.downsample = QLineEdit("")
3936
+ layout.addRow("Downsample Factor (Optional to speed up display):", self.downsample)
3933
3937
 
3934
3938
  # Network Overlay checkbox (default True)
3935
- self.overlay = QPushButton("Overlay 1")
3936
- self.overlay.setCheckable(True)
3937
- self.overlay.setChecked(True)
3938
- layout.addRow("Include Overlay 1?", self.overlay)
3939
+ self.cubic = QPushButton("cubic")
3940
+ self.cubic.setCheckable(True)
3941
+ self.cubic.setChecked(False)
3942
+ layout.addRow("Use cubic downsample (Slower but preserves shape better potentially)?", self.cubic)
3939
3943
 
3940
3944
  # Add Run button
3941
3945
  run_button = QPushButton("Show 3D")
@@ -3949,22 +3953,49 @@ class Show3dDialog(QDialog):
3949
3953
 
3950
3954
  # Get amount
3951
3955
  try:
3952
- downsample = float(self.downsample.text()) if self.downsample.text() else 1
3956
+ downsample = float(self.downsample.text()) if self.downsample.text() else None
3953
3957
  except ValueError:
3954
- downsample = 1
3958
+ downsample = None
3955
3959
 
3956
- overlay = self.overlay.isChecked()
3957
- if overlay:
3958
-
3959
- # Example analysis plot
3960
- my_network.show_3D(my_network.network_overlay, downsample)
3960
+ cubic = self.cubic.isChecked()
3961
+
3962
+ if cubic:
3963
+ order = 3
3961
3964
  else:
3962
- my_network.show_3D(down_factor = downsample)
3965
+ order = 0
3966
+
3967
+ arrays_3d = []
3968
+ arrays_4d = []
3969
+
3970
+ color_template = ['red', 'green', 'white', 'cyan', 'yellow'] # color list
3971
+ colors = []
3972
+
3973
+
3974
+ for i, channel in enumerate(self.parent().channel_data):
3975
+ if channel is not None:
3976
+
3977
+ if len(channel.shape) == 3:
3978
+ visible = self.parent().channel_buttons[i].isChecked()
3979
+ if visible:
3980
+ arrays_3d.append(channel)
3981
+ colors.append(color_template[i])
3982
+ elif len(channel.shape) == 4:
3983
+ visible = self.parent().channel_buttons[i].isChecked()
3984
+ if visible:
3985
+ arrays_4d.append(channel)
3986
+
3987
+ if self.parent().highlight_overlay is not None:
3988
+ arrays_3d.append(self.parent().highlight_overlay)
3989
+ colors.append(color_template[4])
3990
+
3991
+ n3d.show_3d(arrays_3d, arrays_4d, down_factor = downsample, order = order, xy_scale = my_network.xy_scale, z_scale = my_network.z_scale, colors = colors)
3963
3992
 
3964
3993
  self.accept()
3965
3994
 
3966
3995
  except Exception as e:
3967
3996
  print(f"Error: {e}")
3997
+ import traceback
3998
+ print(traceback.format_exc())
3968
3999
 
3969
4000
 
3970
4001
  class NetOverlayDialog(QDialog):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: nettracer3d
3
- Version: 0.3.5
3
+ Version: 0.3.7
4
4
  Summary: Scripts for intializing and analyzing networks from segmentations of three dimensional images.
5
5
  Author-email: Liam McLaughlin <boom2449@gmail.com>
6
6
  Project-URL: User_Manual, https://drive.google.com/drive/folders/1fTkz3n4LN9_VxKRKC8lVQSlrz_wq0bVn?usp=drive_link
@@ -20,7 +20,7 @@ Requires-Dist: networkx
20
20
  Requires-Dist: opencv-python-headless
21
21
  Requires-Dist: openpyxl
22
22
  Requires-Dist: pandas
23
- Requires-Dist: plotly
23
+ Requires-Dist: napari
24
24
  Requires-Dist: python-louvain
25
25
  Requires-Dist: tifffile
26
26
  Requires-Dist: PyQt6
@@ -7,7 +7,7 @@ networkx
7
7
  opencv-python-headless
8
8
  openpyxl
9
9
  pandas
10
- plotly
10
+ napari
11
11
  python-louvain
12
12
  tifffile
13
13
  PyQt6
File without changes
File without changes
File without changes