nettracer3d 0.6.7__py3-none-any.whl → 0.6.9__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/modularity.py +23 -24
- nettracer3d/morphology.py +12 -9
- nettracer3d/nettracer.py +95 -44
- nettracer3d/nettracer_gui.py +466 -68
- nettracer3d/proximity.py +50 -14
- nettracer3d/segmenter.py +1 -1
- nettracer3d/smart_dilate.py +19 -20
- {nettracer3d-0.6.7.dist-info → nettracer3d-0.6.9.dist-info}/METADATA +38 -11
- nettracer3d-0.6.9.dist-info/RECORD +20 -0
- nettracer3d-0.6.7.dist-info/RECORD +0 -20
- {nettracer3d-0.6.7.dist-info → nettracer3d-0.6.9.dist-info}/WHEEL +0 -0
- {nettracer3d-0.6.7.dist-info → nettracer3d-0.6.9.dist-info}/entry_points.txt +0 -0
- {nettracer3d-0.6.7.dist-info → nettracer3d-0.6.9.dist-info}/licenses/LICENSE +0 -0
- {nettracer3d-0.6.7.dist-info → nettracer3d-0.6.9.dist-info}/top_level.txt +0 -0
nettracer3d/nettracer_gui.py
CHANGED
|
@@ -18,14 +18,14 @@ from nettracer3d import smart_dilate as sdl
|
|
|
18
18
|
from matplotlib.colors import LinearSegmentedColormap
|
|
19
19
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
|
20
20
|
import pandas as pd
|
|
21
|
-
from PyQt6.QtGui import (QFont, QCursor, QColor, QPixmap, QPainter, QPen)
|
|
21
|
+
from PyQt6.QtGui import (QFont, QCursor, QColor, QPixmap, QFontMetrics, QPainter, QPen)
|
|
22
22
|
import tifffile
|
|
23
23
|
import copy
|
|
24
24
|
import multiprocessing as mp
|
|
25
25
|
from concurrent.futures import ThreadPoolExecutor
|
|
26
26
|
from functools import partial
|
|
27
27
|
from nettracer3d import segmenter
|
|
28
|
-
#from nettracer3d import segmenter_GPU
|
|
28
|
+
#from nettracer3d import segmenter_GPU <--- couldn't get this faster than CPU ¯\_(ツ)_/¯
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
|
|
@@ -118,6 +118,9 @@ class ImageViewerWindow(QMainWindow):
|
|
|
118
118
|
|
|
119
119
|
#For ML segmenting mode
|
|
120
120
|
self.brush_mode = False
|
|
121
|
+
self.can = False
|
|
122
|
+
self.threed = False
|
|
123
|
+
self.threedthresh = 5
|
|
121
124
|
self.painting = False
|
|
122
125
|
self.foreground = True
|
|
123
126
|
self.machine_window = None
|
|
@@ -815,6 +818,8 @@ class ImageViewerWindow(QMainWindow):
|
|
|
815
818
|
link_nodes.triggered.connect(self.handle_link)
|
|
816
819
|
delink_nodes = highlight_menu.addAction("Split Nodes")
|
|
817
820
|
delink_nodes.triggered.connect(self.handle_split)
|
|
821
|
+
override_obj = highlight_menu.addAction("Override Channel with Selection")
|
|
822
|
+
override_obj.triggered.connect(self.handle_override)
|
|
818
823
|
context_menu.addMenu(highlight_menu)
|
|
819
824
|
|
|
820
825
|
# Create measure menu
|
|
@@ -1659,7 +1664,9 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1659
1664
|
print(f"An error has occurred: {e}")
|
|
1660
1665
|
|
|
1661
1666
|
|
|
1662
|
-
|
|
1667
|
+
def handle_override(self):
|
|
1668
|
+
dialog = OverrideDialog(self)
|
|
1669
|
+
dialog.exec()
|
|
1663
1670
|
|
|
1664
1671
|
|
|
1665
1672
|
|
|
@@ -1712,6 +1719,9 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1712
1719
|
self.pen_button.setChecked(False)
|
|
1713
1720
|
self.pan_mode = False
|
|
1714
1721
|
self.brush_mode = False
|
|
1722
|
+
self.can = False
|
|
1723
|
+
self.threed = False
|
|
1724
|
+
self.last_change = None
|
|
1715
1725
|
if self.machine_window is not None:
|
|
1716
1726
|
self.machine_window.silence_button()
|
|
1717
1727
|
self.canvas.setCursor(Qt.CursorShape.CrossCursor)
|
|
@@ -1729,6 +1739,9 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1729
1739
|
self.zoom_button.setChecked(False)
|
|
1730
1740
|
self.pen_button.setChecked(False)
|
|
1731
1741
|
self.zoom_mode = False
|
|
1742
|
+
self.can = False
|
|
1743
|
+
self.threed = False
|
|
1744
|
+
self.last_change = None
|
|
1732
1745
|
self.brush_mode = False
|
|
1733
1746
|
if self.machine_window is not None:
|
|
1734
1747
|
self.machine_window.silence_button()
|
|
@@ -1749,14 +1762,69 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1749
1762
|
self.zoom_mode = False
|
|
1750
1763
|
self.update_brush_cursor()
|
|
1751
1764
|
else:
|
|
1765
|
+
self.last_change = None
|
|
1766
|
+
self.can = False
|
|
1767
|
+
self.threed = False
|
|
1752
1768
|
self.canvas.setCursor(Qt.CursorShape.ArrowCursor)
|
|
1753
1769
|
|
|
1770
|
+
def toggle_can(self):
|
|
1771
|
+
|
|
1772
|
+
if not self.can:
|
|
1773
|
+
self.can = True
|
|
1774
|
+
self.update_brush_cursor()
|
|
1775
|
+
else:
|
|
1776
|
+
self.can = False
|
|
1777
|
+
self.last_change = None
|
|
1778
|
+
self.update_brush_cursor()
|
|
1779
|
+
|
|
1780
|
+
def toggle_threed(self):
|
|
1781
|
+
|
|
1782
|
+
if not self.threed:
|
|
1783
|
+
self.threed = True
|
|
1784
|
+
self.threedthresh = 5
|
|
1785
|
+
self.update_brush_cursor()
|
|
1786
|
+
else:
|
|
1787
|
+
self.threed = False
|
|
1788
|
+
self.update_brush_cursor()
|
|
1789
|
+
|
|
1754
1790
|
|
|
1755
1791
|
def on_mpl_scroll(self, event):
|
|
1756
1792
|
"""Handle matplotlib canvas scroll events"""
|
|
1757
1793
|
#Wheel events
|
|
1758
1794
|
if self.brush_mode and event.inaxes == self.ax:
|
|
1795
|
+
|
|
1796
|
+
# Get modifiers
|
|
1797
|
+
modifiers = event.guiEvent.modifiers()
|
|
1798
|
+
ctrl_pressed = bool(modifiers & Qt.ControlModifier)
|
|
1799
|
+
shift_pressed = bool(modifiers & Qt.ShiftModifier)
|
|
1800
|
+
alt_pressed = bool(modifiers & Qt.AltModifier)
|
|
1801
|
+
|
|
1802
|
+
# Check if threed is enabled and ONLY if no specific modifiers are pressed
|
|
1803
|
+
if self.threed and not ctrl_pressed and not shift_pressed and not alt_pressed:
|
|
1804
|
+
import math
|
|
1805
|
+
step = 1 if event.button == 'up' else -1
|
|
1806
|
+
self.threedthresh += step
|
|
1807
|
+
|
|
1808
|
+
# Round to appropriate odd integer based on scroll direction
|
|
1809
|
+
if event.button == 'up':
|
|
1810
|
+
# Round up to nearest odd
|
|
1811
|
+
self.threedthresh = math.ceil(self.threedthresh)
|
|
1812
|
+
if self.threedthresh % 2 == 0:
|
|
1813
|
+
self.threedthresh += 1
|
|
1814
|
+
else: # event.button == 'down'
|
|
1815
|
+
# Round down to nearest odd, but not below 1
|
|
1816
|
+
self.threedthresh = math.floor(self.threedthresh)
|
|
1817
|
+
if self.threedthresh % 2 == 0:
|
|
1818
|
+
self.threedthresh -= 1
|
|
1819
|
+
# Ensure not below minimum value of 1
|
|
1820
|
+
self.threedthresh = max(1, self.threedthresh)
|
|
1821
|
+
|
|
1822
|
+
# Update the brush cursor to show the new threshold
|
|
1823
|
+
self.update_brush_cursor()
|
|
1824
|
+
return
|
|
1825
|
+
|
|
1759
1826
|
# Check if Ctrl is pressed
|
|
1827
|
+
|
|
1760
1828
|
if event.guiEvent.modifiers() & Qt.ShiftModifier:
|
|
1761
1829
|
pass
|
|
1762
1830
|
|
|
@@ -1793,6 +1861,15 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1793
1861
|
self.update_display(preserve_zoom=(current_xlim, current_ylim))
|
|
1794
1862
|
|
|
1795
1863
|
def keyPressEvent(self, event):
|
|
1864
|
+
|
|
1865
|
+
if event.key() == Qt.Key_Z and event.modifiers() & Qt.ControlModifier:
|
|
1866
|
+
try:
|
|
1867
|
+
self.load_channel(self.last_change[1], self.last_change[0], True)
|
|
1868
|
+
except:
|
|
1869
|
+
pass
|
|
1870
|
+
|
|
1871
|
+
return # Return to prevent triggering the regular Z key action below
|
|
1872
|
+
|
|
1796
1873
|
if event.key() == Qt.Key_Z:
|
|
1797
1874
|
self.zoom_button.click()
|
|
1798
1875
|
if self.machine_window is not None:
|
|
@@ -1800,28 +1877,65 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1800
1877
|
self.machine_window.switch_foreground()
|
|
1801
1878
|
if event.key() == Qt.Key_X:
|
|
1802
1879
|
self.high_button.click()
|
|
1880
|
+
if self.brush_mode and self.machine_window is None:
|
|
1881
|
+
if event.key() == Qt.Key_F:
|
|
1882
|
+
self.toggle_can()
|
|
1883
|
+
elif event.key() == Qt.Key_D:
|
|
1884
|
+
self.toggle_threed()
|
|
1803
1885
|
|
|
1804
1886
|
|
|
1805
1887
|
def update_brush_cursor(self):
|
|
1806
1888
|
"""Update the cursor to show brush size"""
|
|
1807
1889
|
if not self.brush_mode:
|
|
1808
1890
|
return
|
|
1809
|
-
|
|
1810
|
-
#
|
|
1811
|
-
|
|
1812
|
-
|
|
1891
|
+
|
|
1892
|
+
# Get font metrics first to determine text size
|
|
1893
|
+
font = QFont()
|
|
1894
|
+
font.setPointSize(14)
|
|
1895
|
+
font_metrics = QFontMetrics(font)
|
|
1896
|
+
thresh_text = str(self.threedthresh)
|
|
1897
|
+
text_rect = font_metrics.boundingRect(thresh_text)
|
|
1898
|
+
|
|
1899
|
+
# Create a pixmap for the cursor - ensure it's large enough for text
|
|
1900
|
+
brush_size = self.brush_size * 2 + 2 # Add padding for border
|
|
1901
|
+
extra_width = max(0, text_rect.width() + 4 - brush_size) # Extra width for text if needed
|
|
1902
|
+
extra_height = max(0, text_rect.height() + 4 - brush_size) # Extra height for text if needed
|
|
1903
|
+
|
|
1904
|
+
# Make sure pixmap is large enough for both brush and text
|
|
1905
|
+
total_width = brush_size + extra_width
|
|
1906
|
+
total_height = brush_size + extra_height
|
|
1907
|
+
pixmap = QPixmap(total_width, total_height)
|
|
1813
1908
|
pixmap.fill(Qt.transparent)
|
|
1814
1909
|
|
|
1815
1910
|
# Create painter for the pixmap
|
|
1816
1911
|
painter = QPainter(pixmap)
|
|
1817
1912
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
|
1818
1913
|
|
|
1914
|
+
# Calculate center offset for brush ellipse to accommodate text
|
|
1915
|
+
x_offset = extra_width // 2
|
|
1916
|
+
y_offset = extra_height // 2
|
|
1917
|
+
|
|
1819
1918
|
# Draw circle
|
|
1820
|
-
|
|
1919
|
+
if not self.threed:
|
|
1920
|
+
pen = QPen(Qt.white)
|
|
1921
|
+
else:
|
|
1922
|
+
pen = QPen(Qt.red)
|
|
1821
1923
|
pen.setWidth(1)
|
|
1822
1924
|
painter.setPen(pen)
|
|
1823
1925
|
painter.setBrush(Qt.transparent)
|
|
1824
|
-
|
|
1926
|
+
if not self.can:
|
|
1927
|
+
painter.drawEllipse(1 + x_offset, 1 + y_offset, brush_size-2, brush_size-2)
|
|
1928
|
+
|
|
1929
|
+
# Draw threshold number when threed is True and can is False
|
|
1930
|
+
if self.threed:
|
|
1931
|
+
# Set text properties
|
|
1932
|
+
painter.setFont(font)
|
|
1933
|
+
painter.setPen(QPen(Qt.white)) # White text for visibility
|
|
1934
|
+
|
|
1935
|
+
# Draw the text
|
|
1936
|
+
painter.drawText(2, font_metrics.ascent() + 2, thresh_text)
|
|
1937
|
+
else:
|
|
1938
|
+
painter.drawRect(1 + x_offset, 1 + y_offset, 8, 8)
|
|
1825
1939
|
|
|
1826
1940
|
# Create cursor from pixmap
|
|
1827
1941
|
cursor = QCursor(pixmap)
|
|
@@ -1860,6 +1974,28 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1860
1974
|
points.append((x, y))
|
|
1861
1975
|
return points
|
|
1862
1976
|
|
|
1977
|
+
def get_current_mouse_position(self):
|
|
1978
|
+
# Get the main application's current mouse position
|
|
1979
|
+
cursor_pos = QCursor.pos()
|
|
1980
|
+
|
|
1981
|
+
# Convert global screen coordinates to canvas widget coordinates
|
|
1982
|
+
canvas_pos = self.canvas.mapFromGlobal(cursor_pos)
|
|
1983
|
+
|
|
1984
|
+
# Check if the position is within the canvas bounds
|
|
1985
|
+
if not (0 <= canvas_pos.x() < self.canvas.width() and
|
|
1986
|
+
0 <= canvas_pos.y() < self.canvas.height()):
|
|
1987
|
+
return 0, 0 # Mouse is outside of the matplotlib canvas
|
|
1988
|
+
|
|
1989
|
+
# Convert from canvas widget coordinates to matplotlib data coordinates
|
|
1990
|
+
x = canvas_pos.x()
|
|
1991
|
+
y = canvas_pos.y()
|
|
1992
|
+
|
|
1993
|
+
# Transform display coordinates to data coordinates
|
|
1994
|
+
inv = self.ax.transData.inverted()
|
|
1995
|
+
data_coords = inv.transform((x, y))
|
|
1996
|
+
|
|
1997
|
+
return data_coords[0], data_coords[1]
|
|
1998
|
+
|
|
1863
1999
|
def on_mouse_press(self, event):
|
|
1864
2000
|
"""Handle mouse press events."""
|
|
1865
2001
|
if event.inaxes != self.ax:
|
|
@@ -1929,13 +2065,20 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1929
2065
|
|
|
1930
2066
|
if event.button == 1 or event.button == 3:
|
|
1931
2067
|
|
|
2068
|
+
x, y = int(event.xdata), int(event.ydata)
|
|
2069
|
+
|
|
2070
|
+
|
|
2071
|
+
if event.button == 1 and self.can:
|
|
2072
|
+
self.handle_can(x, y)
|
|
2073
|
+
return
|
|
2074
|
+
|
|
2075
|
+
|
|
1932
2076
|
if event.button == 3:
|
|
1933
2077
|
self.erase = True
|
|
1934
2078
|
else:
|
|
1935
2079
|
self.erase = False
|
|
1936
2080
|
|
|
1937
2081
|
self.painting = True
|
|
1938
|
-
x, y = int(event.xdata), int(event.ydata)
|
|
1939
2082
|
self.last_paint_pos = (x, y)
|
|
1940
2083
|
|
|
1941
2084
|
if self.pen_button.isChecked():
|
|
@@ -1995,7 +2138,77 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1995
2138
|
for x in range(max(0, center_x - radius), min(width, center_x + radius + 1)):
|
|
1996
2139
|
# Check if point is within circular brush area
|
|
1997
2140
|
if (x - center_x) ** 2 + (y - center_y) ** 2 <= radius ** 2:
|
|
1998
|
-
|
|
2141
|
+
|
|
2142
|
+
if self.threed and self.threedthresh > 1:
|
|
2143
|
+
amount = (self.threedthresh - 1) / 2
|
|
2144
|
+
low = max(0, self.current_slice - amount)
|
|
2145
|
+
high = min(self.channel_data[channel].shape[0] - 1, self.current_slice + amount)
|
|
2146
|
+
|
|
2147
|
+
for i in range(int(low), int(high + 1)):
|
|
2148
|
+
self.channel_data[channel][i][y, x] = val
|
|
2149
|
+
else:
|
|
2150
|
+
self.channel_data[channel][self.current_slice][y, x] = val
|
|
2151
|
+
|
|
2152
|
+
def handle_can(self, x, y):
|
|
2153
|
+
|
|
2154
|
+
|
|
2155
|
+
if self.threed:
|
|
2156
|
+
ref = copy.deepcopy(self.channel_data[self.active_channel])
|
|
2157
|
+
the_slice = self.channel_data[self.active_channel]
|
|
2158
|
+
|
|
2159
|
+
# First invert the boolean array
|
|
2160
|
+
inv = n3d.invert_boolean(the_slice)
|
|
2161
|
+
|
|
2162
|
+
# Label the connected components in the inverted array
|
|
2163
|
+
labeled_array, num_features = n3d.label_objects(inv)
|
|
2164
|
+
|
|
2165
|
+
# Get the target label at the clicked point
|
|
2166
|
+
target_label = labeled_array[self.current_slice][y][x]
|
|
2167
|
+
|
|
2168
|
+
# Only fill if we clicked on a valid region (target_label > 0)
|
|
2169
|
+
if target_label > 0:
|
|
2170
|
+
# Create a mask of the connected component we clicked on
|
|
2171
|
+
fill_mask = (labeled_array == target_label) * 255
|
|
2172
|
+
|
|
2173
|
+
self.last_change = [ref, self.active_channel]
|
|
2174
|
+
|
|
2175
|
+
# Add this mask to the original slice
|
|
2176
|
+
the_slice = the_slice | fill_mask # Use logical OR to add the filled region
|
|
2177
|
+
|
|
2178
|
+
# Update the channel data
|
|
2179
|
+
self.load_channel(self.active_channel, the_slice, True)
|
|
2180
|
+
else:
|
|
2181
|
+
|
|
2182
|
+
ref = copy.deepcopy(self.channel_data[self.active_channel])
|
|
2183
|
+
|
|
2184
|
+
the_slice = self.channel_data[self.active_channel][self.current_slice]
|
|
2185
|
+
|
|
2186
|
+
# First invert the boolean array
|
|
2187
|
+
inv = n3d.invert_boolean(the_slice)
|
|
2188
|
+
|
|
2189
|
+
# Label the connected components in the inverted array
|
|
2190
|
+
labeled_array, num_features = n3d.label_objects(inv)
|
|
2191
|
+
|
|
2192
|
+
# Get the target label at the clicked point
|
|
2193
|
+
target_label = labeled_array[y][x]
|
|
2194
|
+
|
|
2195
|
+
# Only fill if we clicked on a valid region (target_label > 0)
|
|
2196
|
+
if target_label > 0:
|
|
2197
|
+
# Create a mask of the connected component we clicked on
|
|
2198
|
+
fill_mask = (labeled_array == target_label) * 255
|
|
2199
|
+
|
|
2200
|
+
self.last_change = [ref, self.active_channel]
|
|
2201
|
+
|
|
2202
|
+
# Add this mask to the original slice
|
|
2203
|
+
the_slice = the_slice | fill_mask # Use logical OR to add the filled region
|
|
2204
|
+
|
|
2205
|
+
# Update the channel data
|
|
2206
|
+
self.channel_data[self.active_channel][self.current_slice] = the_slice
|
|
2207
|
+
self.load_channel(self.active_channel, self.channel_data[self.active_channel], True)
|
|
2208
|
+
|
|
2209
|
+
|
|
2210
|
+
|
|
2211
|
+
|
|
1999
2212
|
|
|
2000
2213
|
def on_mouse_move(self, event):
|
|
2001
2214
|
"""Handle mouse movement events."""
|
|
@@ -2082,6 +2295,7 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2082
2295
|
points = self.get_line_points(last_x, last_y, x, y)
|
|
2083
2296
|
|
|
2084
2297
|
# Paint at each point along the line
|
|
2298
|
+
|
|
2085
2299
|
for px, py in points:
|
|
2086
2300
|
if 0 <= px < width and 0 <= py < height:
|
|
2087
2301
|
self.paint_at_position(px, py, self.erase, channel)
|
|
@@ -2770,8 +2984,11 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2770
2984
|
|
|
2771
2985
|
def show_type_dialog(self):
|
|
2772
2986
|
"""Show the type dialog"""
|
|
2773
|
-
|
|
2774
|
-
|
|
2987
|
+
try:
|
|
2988
|
+
dialog = TypeDialog(self)
|
|
2989
|
+
dialog.exec()
|
|
2990
|
+
except:
|
|
2991
|
+
pass
|
|
2775
2992
|
|
|
2776
2993
|
def show_skeletonize_dialog(self):
|
|
2777
2994
|
"""show the skeletonize dialog"""
|
|
@@ -4720,10 +4937,10 @@ class PropertiesDialog(QDialog):
|
|
|
4720
4937
|
self.id_overlay.setChecked(self.check_checked(my_network.id_overlay))
|
|
4721
4938
|
layout.addRow("Overlay 2 Status", self.id_overlay)
|
|
4722
4939
|
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4940
|
+
self.search_region = QPushButton("search region")
|
|
4941
|
+
self.search_region.setCheckable(True)
|
|
4942
|
+
self.search_region.setChecked(self.check_checked(my_network.search_region))
|
|
4943
|
+
layout.addRow("Node Search Region Status", self.search_region)
|
|
4727
4944
|
|
|
4728
4945
|
self.network = QPushButton("Network")
|
|
4729
4946
|
self.network.setCheckable(True)
|
|
@@ -4762,10 +4979,10 @@ class PropertiesDialog(QDialog):
|
|
|
4762
4979
|
edges = not self.edges.isChecked()
|
|
4763
4980
|
network_overlay = not self.network_overlay.isChecked()
|
|
4764
4981
|
id_overlay = not self.id_overlay.isChecked()
|
|
4765
|
-
|
|
4982
|
+
search_region = not self.search_region.isChecked()
|
|
4766
4983
|
network = not self.network.isChecked()
|
|
4767
4984
|
|
|
4768
|
-
self.parent().reset(nodes = nodes, edges = edges, network_overlay = network_overlay, id_overlay = id_overlay, network = network, xy_scale = xy_scale, z_scale = z_scale)
|
|
4985
|
+
self.parent().reset(nodes = nodes, edges = edges, search_region = search_region, network_overlay = network_overlay, id_overlay = id_overlay, network = network, xy_scale = xy_scale, z_scale = z_scale)
|
|
4769
4986
|
|
|
4770
4987
|
self.accept()
|
|
4771
4988
|
|
|
@@ -5769,7 +5986,7 @@ class RandomDialog(QDialog):
|
|
|
5769
5986
|
def __init__(self, parent=None):
|
|
5770
5987
|
|
|
5771
5988
|
super().__init__(parent)
|
|
5772
|
-
self.setWindowTitle("
|
|
5989
|
+
self.setWindowTitle("Random Parameters")
|
|
5773
5990
|
self.setModal(True)
|
|
5774
5991
|
|
|
5775
5992
|
layout = QFormLayout(self)
|
|
@@ -5813,10 +6030,10 @@ class RadDialog(QDialog):
|
|
|
5813
6030
|
layout = QFormLayout(self)
|
|
5814
6031
|
|
|
5815
6032
|
# GPU checkbox (default False)
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
6033
|
+
self.GPU = QPushButton("GPU")
|
|
6034
|
+
self.GPU.setCheckable(True)
|
|
6035
|
+
self.GPU.setChecked(False)
|
|
6036
|
+
layout.addRow("Use GPU:", self.GPU)
|
|
5820
6037
|
|
|
5821
6038
|
|
|
5822
6039
|
# Add Run button
|
|
@@ -5827,7 +6044,7 @@ class RadDialog(QDialog):
|
|
|
5827
6044
|
def rads(self):
|
|
5828
6045
|
|
|
5829
6046
|
try:
|
|
5830
|
-
|
|
6047
|
+
GPU = self.GPU.isChecked() # <- I can never get these to be faster than parallel CPU *shrugs*
|
|
5831
6048
|
|
|
5832
6049
|
active_data = self.parent().channel_data[self.parent().active_channel]
|
|
5833
6050
|
|
|
@@ -5854,7 +6071,7 @@ class InteractionDialog(QDialog):
|
|
|
5854
6071
|
def __init__(self, parent=None):
|
|
5855
6072
|
|
|
5856
6073
|
super().__init__(parent)
|
|
5857
|
-
self.setWindowTitle("
|
|
6074
|
+
self.setWindowTitle("Interaction Parameters")
|
|
5858
6075
|
self.setModal(True)
|
|
5859
6076
|
|
|
5860
6077
|
layout = QFormLayout(self)
|
|
@@ -5874,7 +6091,7 @@ class InteractionDialog(QDialog):
|
|
|
5874
6091
|
self.fastdil = QPushButton("Fast Dilate")
|
|
5875
6092
|
self.fastdil.setCheckable(True)
|
|
5876
6093
|
self.fastdil.setChecked(False)
|
|
5877
|
-
layout.addRow("
|
|
6094
|
+
layout.addRow("Use Fast Dilation (Higher speed, less accurate with search regions much larger than nodes):", self.fastdil)
|
|
5878
6095
|
|
|
5879
6096
|
# Add Run button
|
|
5880
6097
|
run_button = QPushButton("Calculate")
|
|
@@ -5929,7 +6146,7 @@ class DegreeDialog(QDialog):
|
|
|
5929
6146
|
layout.addRow("Execution Mode:", self.mode_selector)
|
|
5930
6147
|
|
|
5931
6148
|
self.mask_limiter = QLineEdit("1")
|
|
5932
|
-
layout.addRow("
|
|
6149
|
+
layout.addRow("Proportion of high degree nodes to keep (ignore if only returning degrees)", self.mask_limiter)
|
|
5933
6150
|
|
|
5934
6151
|
self.down_factor = QLineEdit("1")
|
|
5935
6152
|
layout.addRow("down_factor (for speeding up overlay generation - ignore if only returning degrees:", self.down_factor)
|
|
@@ -6474,6 +6691,116 @@ class ResizeDialog(QDialog):
|
|
|
6474
6691
|
QMessageBox.critical(self, "Error", f"Failed to resize: {str(e)}")
|
|
6475
6692
|
|
|
6476
6693
|
|
|
6694
|
+
class OverrideDialog(QDialog):
|
|
6695
|
+
def __init__(self, parent=None):
|
|
6696
|
+
super().__init__(parent)
|
|
6697
|
+
self.setWindowTitle("Override Parameters")
|
|
6698
|
+
self.setModal(True)
|
|
6699
|
+
|
|
6700
|
+
layout = QFormLayout(self)
|
|
6701
|
+
|
|
6702
|
+
layout.addRow(QLabel("Use Highlight Overlay to Place Data From: "))
|
|
6703
|
+
|
|
6704
|
+
# Add mode selection dropdown
|
|
6705
|
+
self.mode_selector = QComboBox()
|
|
6706
|
+
self.mode_selector.addItems(["Nodes", "Edges"])
|
|
6707
|
+
self.mode_selector.setCurrentIndex(0) # Default to Mode 1
|
|
6708
|
+
layout.addRow("Overrider:", self.mode_selector)
|
|
6709
|
+
|
|
6710
|
+
layout.addRow(QLabel("To Override Corresponding Data In: "))
|
|
6711
|
+
|
|
6712
|
+
# Add mode selection dropdown
|
|
6713
|
+
self.target_selector = QComboBox()
|
|
6714
|
+
self.target_selector.addItems(["Nodes", "Edges", "Overlay 1", "Overlay 2"])
|
|
6715
|
+
self.target_selector.setCurrentIndex(0) # Default to Mode 1
|
|
6716
|
+
layout.addRow("To be overwritten:", self.target_selector)
|
|
6717
|
+
|
|
6718
|
+
layout.addRow(QLabel("Place output in: "))
|
|
6719
|
+
|
|
6720
|
+
# Add mode selection dropdown
|
|
6721
|
+
self.output_selector = QComboBox()
|
|
6722
|
+
self.output_selector.addItems(["Nodes", "Edges", "Overlay 1", "Overlay 2", "Highlight Overlay"])
|
|
6723
|
+
self.output_selector.setCurrentIndex(0) # Default to Mode 1
|
|
6724
|
+
layout.addRow("Output Location:", self.output_selector)
|
|
6725
|
+
|
|
6726
|
+
# Add Run button
|
|
6727
|
+
run_button = QPushButton("Override")
|
|
6728
|
+
run_button.clicked.connect(self.override)
|
|
6729
|
+
layout.addWidget(run_button)
|
|
6730
|
+
|
|
6731
|
+
def override(self):
|
|
6732
|
+
|
|
6733
|
+
try:
|
|
6734
|
+
|
|
6735
|
+
accepted_mode = self.mode_selector.currentIndex()
|
|
6736
|
+
accepted_target = self.target_selector.currentIndex()
|
|
6737
|
+
output_target = self.output_selector.currentIndex()
|
|
6738
|
+
|
|
6739
|
+
if accepted_mode == accepted_target:
|
|
6740
|
+
return
|
|
6741
|
+
|
|
6742
|
+
active_data = self.parent().channel_data[accepted_mode]
|
|
6743
|
+
|
|
6744
|
+
if accepted_mode == 0:
|
|
6745
|
+
self.parent().create_highlight_overlay(node_indices=self.parent().clicked_values['nodes'])
|
|
6746
|
+
else:
|
|
6747
|
+
self.parent().create_highlight_overlay(edge_indices=self.parent().clicked_values['edges'])
|
|
6748
|
+
|
|
6749
|
+
target_data = self.parent().channel_data[accepted_target]
|
|
6750
|
+
|
|
6751
|
+
if target_data is None:
|
|
6752
|
+
target_data = np.zeros_like(active_data)
|
|
6753
|
+
|
|
6754
|
+
|
|
6755
|
+
|
|
6756
|
+
try:
|
|
6757
|
+
|
|
6758
|
+
self.parent().highlight_overlay = self.parent().highlight_overlay > 0 #What we want in override image
|
|
6759
|
+
inv = n3d.invert_boolean(self.parent().highlight_overlay) #what we want to keep in target image
|
|
6760
|
+
|
|
6761
|
+
target_data = target_data * inv #Cut out what we don't want in target image
|
|
6762
|
+
max_val = np.max(target_data) #Ensure non-val overlap
|
|
6763
|
+
other_max = np.max(active_data)
|
|
6764
|
+
true_max = max_val + other_max
|
|
6765
|
+
if true_max < 256:
|
|
6766
|
+
dtype = np.uint8
|
|
6767
|
+
elif true_max < 65536:
|
|
6768
|
+
dtype = np.uint16
|
|
6769
|
+
else:
|
|
6770
|
+
dtype = np.uint32
|
|
6771
|
+
|
|
6772
|
+
active_data = active_data.astype(dtype)
|
|
6773
|
+
|
|
6774
|
+
active_data = active_data + max_val #Transpose override image
|
|
6775
|
+
|
|
6776
|
+
active_data = self.parent().highlight_overlay * active_data #Cut out what we want from old image image
|
|
6777
|
+
|
|
6778
|
+
target_data = target_data.astype(dtype)
|
|
6779
|
+
|
|
6780
|
+
target_data = target_data + active_data #Insert new selection
|
|
6781
|
+
|
|
6782
|
+
if output_target == 4:
|
|
6783
|
+
|
|
6784
|
+
self.parent().highlight_overlay = result
|
|
6785
|
+
|
|
6786
|
+
else:
|
|
6787
|
+
|
|
6788
|
+
|
|
6789
|
+
# Update both the display data and the network object
|
|
6790
|
+
self.parent().load_channel(output_target, channel_data = target_data, data = True)
|
|
6791
|
+
|
|
6792
|
+
self.parent().update_display()
|
|
6793
|
+
|
|
6794
|
+
self.accept()
|
|
6795
|
+
|
|
6796
|
+
except Exception as e:
|
|
6797
|
+
print(f"Error overriding: {e}")
|
|
6798
|
+
|
|
6799
|
+
except Exception as e:
|
|
6800
|
+
print(f"Error overriding: {e}")
|
|
6801
|
+
|
|
6802
|
+
|
|
6803
|
+
|
|
6477
6804
|
class BinarizeDialog(QDialog):
|
|
6478
6805
|
def __init__(self, parent=None):
|
|
6479
6806
|
super().__init__(parent)
|
|
@@ -6735,7 +7062,7 @@ class ThresholdDialog(QDialog):
|
|
|
6735
7062
|
try:
|
|
6736
7063
|
import cupy as cp
|
|
6737
7064
|
except:
|
|
6738
|
-
print("Cupy import failed, using CPU version")
|
|
7065
|
+
#print("Cupy import failed, using CPU version")
|
|
6739
7066
|
GPU = False
|
|
6740
7067
|
|
|
6741
7068
|
if self.parent().mini_overlay_data is not None:
|
|
@@ -6776,6 +7103,9 @@ class MachineWindow(QMainWindow):
|
|
|
6776
7103
|
|
|
6777
7104
|
if self.parent().pen_button.isChecked(): #Disable the pen mode if the user is in it because the segmenter pen forks it
|
|
6778
7105
|
self.parent().pen_button.click()
|
|
7106
|
+
self.parent().threed = False
|
|
7107
|
+
self.parent().can = False
|
|
7108
|
+
self.parent().last_change = None
|
|
6779
7109
|
|
|
6780
7110
|
self.parent().pen_button.setEnabled(False)
|
|
6781
7111
|
|
|
@@ -6792,8 +7122,12 @@ class MachineWindow(QMainWindow):
|
|
|
6792
7122
|
active_data = self.parent().channel_data[1]
|
|
6793
7123
|
act_channel = 1
|
|
6794
7124
|
|
|
7125
|
+
try:
|
|
7126
|
+
array1 = np.zeros_like(active_data).astype(np.uint8)
|
|
7127
|
+
except:
|
|
7128
|
+
print("No data in nodes channel")
|
|
7129
|
+
return
|
|
6795
7130
|
|
|
6796
|
-
array1 = np.zeros_like(active_data).astype(np.uint8)
|
|
6797
7131
|
array3 = np.zeros_like(active_data).astype(np.uint8)
|
|
6798
7132
|
self.parent().highlight_overlay = array3 #Clear this out for the segmenter to use
|
|
6799
7133
|
|
|
@@ -7002,6 +7336,8 @@ class MachineWindow(QMainWindow):
|
|
|
7002
7336
|
self.parent().zoom_mode = False
|
|
7003
7337
|
self.parent().update_brush_cursor()
|
|
7004
7338
|
else:
|
|
7339
|
+
self.threed = False
|
|
7340
|
+
self.can = False
|
|
7005
7341
|
self.parent().zoom_button.click()
|
|
7006
7342
|
|
|
7007
7343
|
def silence_button(self):
|
|
@@ -7034,6 +7370,7 @@ class MachineWindow(QMainWindow):
|
|
|
7034
7370
|
)
|
|
7035
7371
|
|
|
7036
7372
|
|
|
7373
|
+
|
|
7037
7374
|
def start_segmentation(self):
|
|
7038
7375
|
|
|
7039
7376
|
self.kill_segmentation()
|
|
@@ -7062,7 +7399,11 @@ class MachineWindow(QMainWindow):
|
|
|
7062
7399
|
self.segmentation_worker.finished.connect(self.segmentation_finished)
|
|
7063
7400
|
current_xlim = self.parent().ax.get_xlim()
|
|
7064
7401
|
current_ylim = self.parent().ax.get_ylim()
|
|
7065
|
-
|
|
7402
|
+
try:
|
|
7403
|
+
x, y = self.parent().get_current_mouse_position()
|
|
7404
|
+
except:
|
|
7405
|
+
x, y = 0, 0
|
|
7406
|
+
self.segmenter.update_position(self.parent().current_slice, x, y)
|
|
7066
7407
|
self.segmentation_worker.start()
|
|
7067
7408
|
|
|
7068
7409
|
def confirm_seg_dialog(self):
|
|
@@ -7128,7 +7469,12 @@ class MachineWindow(QMainWindow):
|
|
|
7128
7469
|
current_xlim = self.parent().ax.get_xlim()
|
|
7129
7470
|
current_ylim = self.parent().ax.get_ylim()
|
|
7130
7471
|
|
|
7131
|
-
|
|
7472
|
+
try:
|
|
7473
|
+
x, y = self.parent().get_current_mouse_position()
|
|
7474
|
+
except:
|
|
7475
|
+
x, y = 0, 0
|
|
7476
|
+
self.segmenter.update_position(self.parent().current_slice, x, y)
|
|
7477
|
+
|
|
7132
7478
|
if not self.parent().painting:
|
|
7133
7479
|
# Only update if view limits are valid
|
|
7134
7480
|
self.parent().update_display(preserve_zoom=(current_xlim, current_ylim))
|
|
@@ -7423,6 +7769,8 @@ class ThresholdWindow(QMainWindow):
|
|
|
7423
7769
|
self.bounds = True
|
|
7424
7770
|
self.parent().bounds = True
|
|
7425
7771
|
|
|
7772
|
+
self.chan = self.parent().active_channel
|
|
7773
|
+
|
|
7426
7774
|
|
|
7427
7775
|
# Create matplotlib figure
|
|
7428
7776
|
fig = Figure(figsize=(5, 4))
|
|
@@ -7491,6 +7839,45 @@ class ThresholdWindow(QMainWindow):
|
|
|
7491
7839
|
self.parent().preview = False
|
|
7492
7840
|
self.parent().targs = None
|
|
7493
7841
|
self.parent().bounds = False
|
|
7842
|
+
try: # could probably be refactored but this just handles keeping the highlight elements if the user presses X
|
|
7843
|
+
if self.chan == 0:
|
|
7844
|
+
if not self.bounds:
|
|
7845
|
+
self.parent().clicked_values['nodes'] = self.get_values_in_range_all_vols(self.chan, float(self.min.text()), float(self.max.text()))
|
|
7846
|
+
else:
|
|
7847
|
+
vals = np.unique(self.parent().channel_data[self.chan])
|
|
7848
|
+
self.parent().clicked_values['nodes'] = (vals[(vals >= float(self.min.text())) & (vals <= float(self.max.text()))]).tolist()
|
|
7849
|
+
|
|
7850
|
+
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:
|
|
7851
|
+
self.parent().mini_overlay = True
|
|
7852
|
+
self.parent().create_mini_overlay(node_indices = self.parent().clicked_values['nodes'])
|
|
7853
|
+
else:
|
|
7854
|
+
self.parent().create_highlight_overlay(
|
|
7855
|
+
node_indices=self.parent().clicked_values['nodes']
|
|
7856
|
+
)
|
|
7857
|
+
elif self.chan == 1:
|
|
7858
|
+
if not self.bounds:
|
|
7859
|
+
self.parent().clicked_values['edges'] = self.get_values_in_range_all_vols(self.chan, float(self.min.text()), float(self.max.text()))
|
|
7860
|
+
else:
|
|
7861
|
+
vals = np.unique(self.parent().channel_data[self.chan])
|
|
7862
|
+
self.parent().clicked_values['edges'] = (vals[(vals >= float(self.min.text())) & (vals <= float(self.max.text()))]).tolist()
|
|
7863
|
+
|
|
7864
|
+
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:
|
|
7865
|
+
self.parent().mini_overlay = True
|
|
7866
|
+
self.parent().create_mini_overlay(edge_indices = self.parent().clicked_values['edges'])
|
|
7867
|
+
else:
|
|
7868
|
+
self.parent().create_highlight_overlay(
|
|
7869
|
+
node_indices=self.parent().clicked_values['edges']
|
|
7870
|
+
)
|
|
7871
|
+
except:
|
|
7872
|
+
pass
|
|
7873
|
+
|
|
7874
|
+
|
|
7875
|
+
def get_values_in_range_all_vols(self, chan, min_val, max_val):
|
|
7876
|
+
output = []
|
|
7877
|
+
for node, vol in self.parent().volume_dict[chan].items():
|
|
7878
|
+
if min_val <= vol <= max_val:
|
|
7879
|
+
output.append(node)
|
|
7880
|
+
return output
|
|
7494
7881
|
|
|
7495
7882
|
def get_values_in_range(self, lst, min_val, max_val):
|
|
7496
7883
|
values = [x for x in lst if min_val <= x <= max_val]
|
|
@@ -7689,10 +8076,10 @@ class SmartDilateDialog(QDialog):
|
|
|
7689
8076
|
layout.addRow("Use GPU:", self.GPU)
|
|
7690
8077
|
|
|
7691
8078
|
# dt checkbox (default False)
|
|
7692
|
-
self.predt = QPushButton("
|
|
8079
|
+
self.predt = QPushButton("Fast Dilation")
|
|
7693
8080
|
self.predt.setCheckable(True)
|
|
7694
8081
|
self.predt.setChecked(False)
|
|
7695
|
-
layout.addRow("Use
|
|
8082
|
+
layout.addRow("Use Fast Dilation (Higher speed, less accurate with search regions much larger than nodes):", self.predt)
|
|
7696
8083
|
|
|
7697
8084
|
self.down_factor = QLineEdit("")
|
|
7698
8085
|
layout.addRow("Internal Downsample for GPU (if needed):", self.down_factor)
|
|
@@ -7708,7 +8095,7 @@ class SmartDilateDialog(QDialog):
|
|
|
7708
8095
|
|
|
7709
8096
|
GPU = self.GPU.isChecked()
|
|
7710
8097
|
down_factor = float(self.down_factor.text()) if self.down_factor.text().strip() else None
|
|
7711
|
-
predt =
|
|
8098
|
+
predt = self.predt.isChecked()
|
|
7712
8099
|
active_data, amount, xy_scale, z_scale = self.params
|
|
7713
8100
|
|
|
7714
8101
|
dilate_xy, dilate_z = n3d.dilation_length_to_pixels(xy_scale, z_scale, amount, amount)
|
|
@@ -7807,7 +8194,8 @@ class DilateDialog(QDialog):
|
|
|
7807
8194
|
active_data,
|
|
7808
8195
|
amount,
|
|
7809
8196
|
xy_scale = xy_scale,
|
|
7810
|
-
z_scale = z_scale
|
|
8197
|
+
z_scale = z_scale,
|
|
8198
|
+
fast_dil = True)
|
|
7811
8199
|
|
|
7812
8200
|
result = result * 255
|
|
7813
8201
|
|
|
@@ -8057,6 +8445,7 @@ class MaskDialog(QDialog):
|
|
|
8057
8445
|
print(f"Error masking: {e}")
|
|
8058
8446
|
|
|
8059
8447
|
|
|
8448
|
+
|
|
8060
8449
|
class TypeDialog(QDialog):
|
|
8061
8450
|
|
|
8062
8451
|
def __init__(self, parent=None):
|
|
@@ -8075,7 +8464,7 @@ class TypeDialog(QDialog):
|
|
|
8075
8464
|
|
|
8076
8465
|
# Add mode selection dropdown
|
|
8077
8466
|
self.mode_selector = QComboBox()
|
|
8078
|
-
self.mode_selector.addItems(["8bit
|
|
8467
|
+
self.mode_selector.addItems(["8bit uint", "16bit uint", "32bit uint", "32bit float", "64bit float"])
|
|
8079
8468
|
self.mode_selector.setCurrentIndex(0) # Default to Mode 1
|
|
8080
8469
|
layout.addRow("Change to?:", self.mode_selector)
|
|
8081
8470
|
|
|
@@ -8086,33 +8475,38 @@ class TypeDialog(QDialog):
|
|
|
8086
8475
|
|
|
8087
8476
|
def run_type(self, active_data):
|
|
8088
8477
|
|
|
8089
|
-
|
|
8478
|
+
try:
|
|
8090
8479
|
|
|
8091
|
-
|
|
8480
|
+
mode = self.mode_selector.currentIndex()
|
|
8092
8481
|
|
|
8093
|
-
|
|
8482
|
+
if mode == 0:
|
|
8094
8483
|
|
|
8095
|
-
|
|
8484
|
+
active_data = active_data.astype(np.uint8)
|
|
8096
8485
|
|
|
8097
|
-
|
|
8486
|
+
elif mode == 1:
|
|
8098
8487
|
|
|
8099
|
-
|
|
8488
|
+
active_data = active_data.astype(np.uint16)
|
|
8100
8489
|
|
|
8101
|
-
|
|
8490
|
+
elif mode == 2:
|
|
8102
8491
|
|
|
8103
|
-
|
|
8492
|
+
active_data = active_data.astype(np.uint32)
|
|
8104
8493
|
|
|
8105
|
-
|
|
8494
|
+
elif mode == 3:
|
|
8106
8495
|
|
|
8107
|
-
|
|
8496
|
+
active_data = active_data.astype(np.float32)
|
|
8108
8497
|
|
|
8109
|
-
|
|
8498
|
+
elif mode == 4:
|
|
8110
8499
|
|
|
8111
|
-
|
|
8500
|
+
active_data = active_data.astype(np.float64)
|
|
8112
8501
|
|
|
8502
|
+
self.parent().load_channel(self.active_chan, active_data, True)
|
|
8113
8503
|
|
|
8114
|
-
|
|
8115
|
-
|
|
8504
|
+
|
|
8505
|
+
print(f"Channel {self.active_chan}) dtype now: {self.parent().channel_data[self.active_chan].dtype}")
|
|
8506
|
+
self.accept()
|
|
8507
|
+
|
|
8508
|
+
except Exception as E:
|
|
8509
|
+
print(f"Error: {e}")
|
|
8116
8510
|
|
|
8117
8511
|
|
|
8118
8512
|
|
|
@@ -8230,7 +8624,7 @@ class WatershedDialog(QDialog):
|
|
|
8230
8624
|
# Predownsample (empty by default)
|
|
8231
8625
|
self.predownsample = QLineEdit()
|
|
8232
8626
|
self.predownsample.setPlaceholderText("Leave empty for None")
|
|
8233
|
-
layout.addRow("
|
|
8627
|
+
layout.addRow("Kernel Obtainment GPU Downsample:", self.predownsample)
|
|
8234
8628
|
|
|
8235
8629
|
# Predownsample2 (empty by default)
|
|
8236
8630
|
self.predownsample2 = QLineEdit()
|
|
@@ -8465,9 +8859,9 @@ class GenNodesDialog(QDialog):
|
|
|
8465
8859
|
layout = QFormLayout(self)
|
|
8466
8860
|
self.called = called
|
|
8467
8861
|
|
|
8468
|
-
self.directory = QLineEdit()
|
|
8469
|
-
self.directory.setPlaceholderText("Leave empty to save in active dir")
|
|
8470
|
-
layout.addRow("Output Directory:", self.directory)
|
|
8862
|
+
#self.directory = QLineEdit()
|
|
8863
|
+
#self.directory.setPlaceholderText("Leave empty to save in active dir")
|
|
8864
|
+
#layout.addRow("Output Directory:", self.directory)
|
|
8471
8865
|
|
|
8472
8866
|
if not down_factor:
|
|
8473
8867
|
down_factor = None
|
|
@@ -8477,7 +8871,7 @@ class GenNodesDialog(QDialog):
|
|
|
8477
8871
|
self.cubic = QPushButton("Cubic Downsample")
|
|
8478
8872
|
self.cubic.setCheckable(True)
|
|
8479
8873
|
self.cubic.setChecked(False)
|
|
8480
|
-
layout.addRow("(if downsampling): Use cubic
|
|
8874
|
+
layout.addRow("(if downsampling): Use cubic downsample? (Slower but can preserve structure better)", self.cubic)
|
|
8481
8875
|
else:
|
|
8482
8876
|
self.down_factor = down_factor[0]
|
|
8483
8877
|
self.cubic = down_factor[1]
|
|
@@ -8521,7 +8915,7 @@ class GenNodesDialog(QDialog):
|
|
|
8521
8915
|
|
|
8522
8916
|
try:
|
|
8523
8917
|
# Get directory (None if empty)
|
|
8524
|
-
directory = self.directory.text() if self.directory.text() else None
|
|
8918
|
+
#directory = self.directory.text() if self.directory.text() else None
|
|
8525
8919
|
|
|
8526
8920
|
# Get branch_removal
|
|
8527
8921
|
try:
|
|
@@ -8667,7 +9061,7 @@ class BranchDialog(QDialog):
|
|
|
8667
9061
|
self.cubic = QPushButton("Cubic Downsample")
|
|
8668
9062
|
self.cubic.setCheckable(True)
|
|
8669
9063
|
self.cubic.setChecked(False)
|
|
8670
|
-
layout.addRow("(if downsampling): Use cubic
|
|
9064
|
+
layout.addRow("(if downsampling): Use cubic downsample? (Slower but can preserve structure better)", self.cubic)
|
|
8671
9065
|
|
|
8672
9066
|
# Add Run button
|
|
8673
9067
|
run_button = QPushButton("Run Branch Label")
|
|
@@ -9090,20 +9484,23 @@ class CentroidDialog(QDialog):
|
|
|
9090
9484
|
my_network.calculate_node_centroids(
|
|
9091
9485
|
down_factor = downsample
|
|
9092
9486
|
)
|
|
9093
|
-
|
|
9487
|
+
if directory:
|
|
9488
|
+
my_network.save_node_centroids(directory = directory)
|
|
9094
9489
|
|
|
9095
9490
|
elif chan == 2:
|
|
9096
9491
|
my_network.calculate_edge_centroids(
|
|
9097
9492
|
down_factor = downsample
|
|
9098
9493
|
)
|
|
9099
|
-
|
|
9494
|
+
if directory:
|
|
9495
|
+
my_network.save_edge_centroids(directory = directory)
|
|
9100
9496
|
|
|
9101
9497
|
elif chan == 0:
|
|
9102
9498
|
try:
|
|
9103
9499
|
my_network.calculate_node_centroids(
|
|
9104
9500
|
down_factor = downsample
|
|
9105
9501
|
)
|
|
9106
|
-
|
|
9502
|
+
if directory:
|
|
9503
|
+
my_network.save_node_centroids(directory = directory)
|
|
9107
9504
|
except:
|
|
9108
9505
|
pass
|
|
9109
9506
|
|
|
@@ -9112,7 +9509,8 @@ class CentroidDialog(QDialog):
|
|
|
9112
9509
|
my_network.calculate_edge_centroids(
|
|
9113
9510
|
down_factor = downsample
|
|
9114
9511
|
)
|
|
9115
|
-
|
|
9512
|
+
if directory:
|
|
9513
|
+
my_network.save_edge_centroids(directory = directory)
|
|
9116
9514
|
|
|
9117
9515
|
except:
|
|
9118
9516
|
pass
|
|
@@ -9153,7 +9551,7 @@ class CalcAllDialog(QDialog):
|
|
|
9153
9551
|
prev_GPU_downsample = ""
|
|
9154
9552
|
prev_other_nodes = ""
|
|
9155
9553
|
prev_remove_trunk = ""
|
|
9156
|
-
prev_gpu =
|
|
9554
|
+
prev_gpu = False
|
|
9157
9555
|
prev_label_nodes = True
|
|
9158
9556
|
prev_inners = True
|
|
9159
9557
|
prev_fastdil = False
|
|
@@ -9185,7 +9583,7 @@ class CalcAllDialog(QDialog):
|
|
|
9185
9583
|
|
|
9186
9584
|
self.diledge = QLineEdit(self.prev_diledge)
|
|
9187
9585
|
self.diledge.setPlaceholderText("Leave empty for None")
|
|
9188
|
-
layout.addRow("Edge Reconnection Distance (
|
|
9586
|
+
layout.addRow("Edge Reconnection Distance (float):", self.diledge)
|
|
9189
9587
|
|
|
9190
9588
|
self.down_factor = QLineEdit(self.prev_down_factor)
|
|
9191
9589
|
self.down_factor.setPlaceholderText("Leave empty for None")
|
|
@@ -9212,7 +9610,7 @@ class CalcAllDialog(QDialog):
|
|
|
9212
9610
|
self.label_nodes = QPushButton("Label")
|
|
9213
9611
|
self.label_nodes.setCheckable(True)
|
|
9214
9612
|
self.label_nodes.setChecked(self.prev_label_nodes)
|
|
9215
|
-
layout.addRow("Label Nodes:", self.label_nodes)
|
|
9613
|
+
layout.addRow("Re-Label Nodes (WARNING - OVERRIDES ANY CURRENT LABELS):", self.label_nodes)
|
|
9216
9614
|
|
|
9217
9615
|
self.inners = QPushButton("Inner Edges")
|
|
9218
9616
|
self.inners.setCheckable(True)
|
|
@@ -9551,13 +9949,13 @@ class ProxDialog(QDialog):
|
|
|
9551
9949
|
return
|
|
9552
9950
|
|
|
9553
9951
|
if populate:
|
|
9554
|
-
my_network.nodes = my_network.kd_network(distance = search, targets = targets)
|
|
9952
|
+
my_network.nodes = my_network.kd_network(distance = search, targets = targets, make_array = True)
|
|
9555
9953
|
self.parent().load_channel(0, channel_data = my_network.nodes, data = True)
|
|
9556
9954
|
else:
|
|
9557
9955
|
my_network.kd_network(distance = search, targets = targets)
|
|
9558
9956
|
|
|
9559
|
-
|
|
9560
|
-
|
|
9957
|
+
if directory is not None:
|
|
9958
|
+
my_network.dump(directory = directory)
|
|
9561
9959
|
|
|
9562
9960
|
|
|
9563
9961
|
# Then handle overlays
|