neuro-sam 0.1.0__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.
Files changed (93) hide show
  1. neuro_sam/__init__.py +1 -0
  2. neuro_sam/brightest_path_lib/__init__.py +5 -0
  3. neuro_sam/brightest_path_lib/algorithm/__init__.py +3 -0
  4. neuro_sam/brightest_path_lib/algorithm/astar.py +586 -0
  5. neuro_sam/brightest_path_lib/algorithm/waypointastar.py +449 -0
  6. neuro_sam/brightest_path_lib/algorithm/waypointastar_speedup.py +1007 -0
  7. neuro_sam/brightest_path_lib/connected_componen.py +329 -0
  8. neuro_sam/brightest_path_lib/cost/__init__.py +8 -0
  9. neuro_sam/brightest_path_lib/cost/cost.py +33 -0
  10. neuro_sam/brightest_path_lib/cost/reciprocal.py +90 -0
  11. neuro_sam/brightest_path_lib/cost/reciprocal_transonic.py +86 -0
  12. neuro_sam/brightest_path_lib/heuristic/__init__.py +2 -0
  13. neuro_sam/brightest_path_lib/heuristic/euclidean.py +101 -0
  14. neuro_sam/brightest_path_lib/heuristic/heuristic.py +29 -0
  15. neuro_sam/brightest_path_lib/image/__init__.py +1 -0
  16. neuro_sam/brightest_path_lib/image/stats.py +197 -0
  17. neuro_sam/brightest_path_lib/input/__init__.py +1 -0
  18. neuro_sam/brightest_path_lib/input/inputs.py +14 -0
  19. neuro_sam/brightest_path_lib/node/__init__.py +2 -0
  20. neuro_sam/brightest_path_lib/node/bidirectional_node.py +240 -0
  21. neuro_sam/brightest_path_lib/node/node.py +125 -0
  22. neuro_sam/brightest_path_lib/visualization/__init__.py +4 -0
  23. neuro_sam/brightest_path_lib/visualization/flythrough.py +133 -0
  24. neuro_sam/brightest_path_lib/visualization/flythrough_all.py +394 -0
  25. neuro_sam/brightest_path_lib/visualization/tube_data.py +385 -0
  26. neuro_sam/brightest_path_lib/visualization/tube_flythrough.py +227 -0
  27. neuro_sam/napari_utils/anisotropic_scaling.py +503 -0
  28. neuro_sam/napari_utils/color_utils.py +135 -0
  29. neuro_sam/napari_utils/contrasting_color_system.py +169 -0
  30. neuro_sam/napari_utils/main_widget.py +1016 -0
  31. neuro_sam/napari_utils/path_tracing_module.py +1016 -0
  32. neuro_sam/napari_utils/punet_widget.py +424 -0
  33. neuro_sam/napari_utils/segmentation_model.py +769 -0
  34. neuro_sam/napari_utils/segmentation_module.py +649 -0
  35. neuro_sam/napari_utils/visualization_module.py +574 -0
  36. neuro_sam/plugin.py +260 -0
  37. neuro_sam/punet/__init__.py +0 -0
  38. neuro_sam/punet/deepd3_model.py +231 -0
  39. neuro_sam/punet/prob_unet_deepd3.py +431 -0
  40. neuro_sam/punet/prob_unet_with_tversky.py +375 -0
  41. neuro_sam/punet/punet_inference.py +236 -0
  42. neuro_sam/punet/run_inference.py +145 -0
  43. neuro_sam/punet/unet_blocks.py +81 -0
  44. neuro_sam/punet/utils.py +52 -0
  45. neuro_sam-0.1.0.dist-info/METADATA +269 -0
  46. neuro_sam-0.1.0.dist-info/RECORD +93 -0
  47. neuro_sam-0.1.0.dist-info/WHEEL +5 -0
  48. neuro_sam-0.1.0.dist-info/entry_points.txt +2 -0
  49. neuro_sam-0.1.0.dist-info/licenses/LICENSE +21 -0
  50. neuro_sam-0.1.0.dist-info/top_level.txt +2 -0
  51. sam2/__init__.py +11 -0
  52. sam2/automatic_mask_generator.py +454 -0
  53. sam2/benchmark.py +92 -0
  54. sam2/build_sam.py +174 -0
  55. sam2/configs/sam2/sam2_hiera_b+.yaml +113 -0
  56. sam2/configs/sam2/sam2_hiera_l.yaml +117 -0
  57. sam2/configs/sam2/sam2_hiera_s.yaml +116 -0
  58. sam2/configs/sam2/sam2_hiera_t.yaml +118 -0
  59. sam2/configs/sam2.1/sam2.1_hiera_b+.yaml +116 -0
  60. sam2/configs/sam2.1/sam2.1_hiera_l.yaml +120 -0
  61. sam2/configs/sam2.1/sam2.1_hiera_s.yaml +119 -0
  62. sam2/configs/sam2.1/sam2.1_hiera_t.yaml +121 -0
  63. sam2/configs/sam2.1_training/sam2.1_hiera_b+_MOSE_finetune.yaml +339 -0
  64. sam2/configs/train.yaml +335 -0
  65. sam2/modeling/__init__.py +5 -0
  66. sam2/modeling/backbones/__init__.py +5 -0
  67. sam2/modeling/backbones/hieradet.py +317 -0
  68. sam2/modeling/backbones/image_encoder.py +134 -0
  69. sam2/modeling/backbones/utils.py +93 -0
  70. sam2/modeling/memory_attention.py +169 -0
  71. sam2/modeling/memory_encoder.py +181 -0
  72. sam2/modeling/position_encoding.py +239 -0
  73. sam2/modeling/sam/__init__.py +5 -0
  74. sam2/modeling/sam/mask_decoder.py +295 -0
  75. sam2/modeling/sam/prompt_encoder.py +202 -0
  76. sam2/modeling/sam/transformer.py +311 -0
  77. sam2/modeling/sam2_base.py +911 -0
  78. sam2/modeling/sam2_utils.py +323 -0
  79. sam2/sam2.1_hiera_b+.yaml +116 -0
  80. sam2/sam2.1_hiera_l.yaml +120 -0
  81. sam2/sam2.1_hiera_s.yaml +119 -0
  82. sam2/sam2.1_hiera_t.yaml +121 -0
  83. sam2/sam2_hiera_b+.yaml +113 -0
  84. sam2/sam2_hiera_l.yaml +117 -0
  85. sam2/sam2_hiera_s.yaml +116 -0
  86. sam2/sam2_hiera_t.yaml +118 -0
  87. sam2/sam2_image_predictor.py +475 -0
  88. sam2/sam2_video_predictor.py +1222 -0
  89. sam2/sam2_video_predictor_legacy.py +1172 -0
  90. sam2/utils/__init__.py +5 -0
  91. sam2/utils/amg.py +348 -0
  92. sam2/utils/misc.py +349 -0
  93. sam2/utils/transforms.py +118 -0
@@ -0,0 +1,197 @@
1
+ import numpy as np
2
+ import numba as nb
3
+
4
+ # Numba-optimized functions for faster image stats calculation
5
+ @nb.njit(fastmath=True)
6
+ def compute_image_intensity_range(image):
7
+ """Efficiently compute min and max intensity of an image using Numba"""
8
+ # Use Numba's optimized implementation rather than np.min/np.max
9
+ # for better performance, especially with large arrays
10
+
11
+ # Initialize with extreme values
12
+ min_val = np.inf
13
+ max_val = -np.inf
14
+
15
+ # For 1D arrays (unlikely but handled for completeness)
16
+ if image.ndim == 1:
17
+ for i in range(image.shape[0]):
18
+ val = image[i]
19
+ if val < min_val:
20
+ min_val = val
21
+ if val > max_val:
22
+ max_val = val
23
+
24
+ # For 2D arrays (most common case)
25
+ elif image.ndim == 2:
26
+ for i in range(image.shape[0]):
27
+ for j in range(image.shape[1]):
28
+ val = image[i, j]
29
+ if val < min_val:
30
+ min_val = val
31
+ if val > max_val:
32
+ max_val = val
33
+
34
+ # For 3D arrays
35
+ elif image.ndim == 3:
36
+ for i in range(image.shape[0]):
37
+ for j in range(image.shape[1]):
38
+ for k in range(image.shape[2]):
39
+ val = image[i, j, k]
40
+ if val < min_val:
41
+ min_val = val
42
+ if val > max_val:
43
+ max_val = val
44
+
45
+ return float(min_val), float(max_val)
46
+
47
+ @nb.njit
48
+ def compute_image_dimensions(image_shape):
49
+ """Compute image dimensions and coordinate ranges"""
50
+ ndim = len(image_shape)
51
+
52
+ # Initialize with default values
53
+ x_min, y_min, z_min = 0, 0, 0
54
+ x_max, y_max, z_max = 0, 0, 0
55
+
56
+ if ndim == 2: # 2D image
57
+ y_max = image_shape[0] - 1
58
+ x_max = image_shape[1] - 1
59
+ elif ndim == 3: # 3D image
60
+ z_max = image_shape[0] - 1
61
+ y_max = image_shape[1] - 1
62
+ x_max = image_shape[2] - 1
63
+
64
+ return x_min, x_max, y_min, y_max, z_min, z_max
65
+
66
+ class ImageStats:
67
+ """Class holding metadata about an image, optimized with Numba
68
+
69
+ Parameters
70
+ ----------
71
+ image : numpy ndarray
72
+ the image who's metadata is being stored
73
+
74
+ Attributes
75
+ ----------
76
+ min_intensity : float
77
+ the minimum intensity of a pixel/voxel in the given image
78
+ max_intensity : float
79
+ the maximum intensity of a pixel/voxel in the given image
80
+ x_min : int
81
+ the smallest x-coordinate of the given image
82
+ y_min : int
83
+ the smallest y-coordinate of the given image
84
+ z_min : int
85
+ the smallest z-coordinate of the given image
86
+ x_max : int
87
+ the largest x-coordinate of the given image
88
+ y_max : int
89
+ the largest y-coordinate of the given image
90
+ z_max : int
91
+ the largest z-coordinate of the given image
92
+ """
93
+ # Use __slots__ for memory efficiency and faster attribute access
94
+ __slots__ = (
95
+ '_min_intensity', '_max_intensity',
96
+ '_x_min', '_y_min', '_z_min',
97
+ '_x_max', '_y_max', '_z_max'
98
+ )
99
+
100
+ def __init__(self, image: np.ndarray):
101
+ # Input validation
102
+ if image is None:
103
+ raise TypeError("Image cannot be None")
104
+ if len(image) == 0:
105
+ raise ValueError("Image cannot be empty")
106
+
107
+ # Convert image to a numpy array if it isn't already
108
+ if not isinstance(image, np.ndarray):
109
+ image = np.asarray(image)
110
+
111
+ # Compute intensity range using Numba-optimized function
112
+ min_intensity, max_intensity = compute_image_intensity_range(image)
113
+ self._min_intensity = min_intensity
114
+ self._max_intensity = max_intensity
115
+
116
+ # Compute image dimensions and coordinate ranges
117
+ self._x_min, self._x_max, self._y_min, self._y_max, self._z_min, self._z_max = compute_image_dimensions(image.shape)
118
+
119
+ @property
120
+ def min_intensity(self) -> float:
121
+ return self._min_intensity
122
+
123
+ @min_intensity.setter
124
+ def min_intensity(self, value: float):
125
+ if value is None:
126
+ raise TypeError("min_intensity cannot be None")
127
+ self._min_intensity = float(value)
128
+
129
+ @property
130
+ def max_intensity(self) -> float:
131
+ return self._max_intensity
132
+
133
+ @max_intensity.setter
134
+ def max_intensity(self, value: float):
135
+ if value is None:
136
+ raise TypeError("max_intensity cannot be None")
137
+ self._max_intensity = float(value)
138
+
139
+ @property
140
+ def x_min(self) -> float:
141
+ return self._x_min
142
+
143
+ @x_min.setter
144
+ def x_min(self, value: float):
145
+ if value is None:
146
+ raise TypeError("x_min cannot be None")
147
+ self._x_min = float(value)
148
+
149
+ @property
150
+ def y_min(self) -> float:
151
+ return self._y_min
152
+
153
+ @y_min.setter
154
+ def y_min(self, value: float):
155
+ if value is None:
156
+ raise TypeError("y_min cannot be None")
157
+ self._y_min = float(value)
158
+
159
+ @property
160
+ def z_min(self) -> float:
161
+ return self._z_min
162
+
163
+ @z_min.setter
164
+ def z_min(self, value: float):
165
+ if value is None:
166
+ raise TypeError("z_min cannot be None")
167
+ self._z_min = float(value)
168
+
169
+ @property
170
+ def x_max(self) -> float:
171
+ return self._x_max
172
+
173
+ @x_max.setter
174
+ def x_max(self, value: float):
175
+ if value is None:
176
+ raise TypeError("x_max cannot be None")
177
+ self._x_max = float(value)
178
+
179
+ @property
180
+ def y_max(self) -> float:
181
+ return self._y_max
182
+
183
+ @y_max.setter
184
+ def y_max(self, value: float):
185
+ if value is None:
186
+ raise TypeError("y_max cannot be None")
187
+ self._y_max = float(value)
188
+
189
+ @property
190
+ def z_max(self) -> float:
191
+ return self._z_max
192
+
193
+ @z_max.setter
194
+ def z_max(self, value: float):
195
+ if value is None:
196
+ raise TypeError("z_max cannot be None")
197
+ self._z_max = float(value)
@@ -0,0 +1 @@
1
+ from .inputs import CostFunction, HeuristicFunction, SearchFunction
@@ -0,0 +1,14 @@
1
+ """Enums for the types of inputs for the cost, heuristic and search functions.
2
+ """
3
+
4
+ from enum import Enum
5
+
6
+ class CostFunction(Enum):
7
+ RECIPROCAL = "reciprocal"
8
+
9
+ class HeuristicFunction(Enum):
10
+ EUCLIDEAN = "euclidean"
11
+
12
+ class SearchFunction(Enum):
13
+ ASTAR = "astar"
14
+ NBASTAR = "nbastar"
@@ -0,0 +1,2 @@
1
+ from .node import Node
2
+ from .bidirectional_node import BidirectionalNode
@@ -0,0 +1,240 @@
1
+ import numpy as np
2
+ import numba as nb
3
+
4
+ # Numba-optimized helper functions
5
+ @nb.njit(fastmath=True)
6
+ def compute_f_score(g_score: float, h_score: float) -> float:
7
+ """Compute f_score from g_score and h_score with Numba optimization"""
8
+ return g_score + h_score
9
+
10
+ @nb.njit
11
+ def validate_point(point):
12
+ """Validate point array with Numba optimization"""
13
+ return len(point) > 0
14
+
15
+ @nb.njit
16
+ def get_score_by_direction(from_start, start_score, goal_score):
17
+ """Get the appropriate score based on direction with Numba optimization"""
18
+ if from_start:
19
+ return start_score
20
+ return goal_score
21
+
22
+ class BidirectionalNode:
23
+ """Class holding attributes and properties of a Bidirectional Node, optimized for performance
24
+
25
+ Parameters
26
+ ----------
27
+ point : numpy ndarray
28
+ the 2D/3D coordinates of the node (can be a pixel or a voxel)
29
+ g_score_from_start : float
30
+ the distance from a starting node to the current node
31
+ g_score_from_goal : float
32
+ the distance from a goal node to the current node
33
+ h_score_from_start : float
34
+ the estimated distance from the current node to a goal node
35
+ h_score_from_goal : float
36
+ the estimated distance from the current node to a start node
37
+ predecessor_from_start : BidirectionalNode
38
+ the current node's immediate predecessor, from which we
39
+ travelled to the current node
40
+ The predecessor's first ancestor is the start node
41
+ predecessor_from_goal : BidirectionalNode
42
+ the current node's immediate predecessor, from which we
43
+ travelled to the current node
44
+ The predecessor's first ancestor is the goal node
45
+
46
+ Attributes
47
+ ----------
48
+ point : numpy ndarray
49
+ the 2D/3D coordinates of the node (can be a pixel or a voxel)
50
+ g_score_from_start : float
51
+ the distance from a starting node to the current node
52
+ g_score_from_goal : float
53
+ the distance from a goal node to the current node
54
+ h_score_from_start : float
55
+ the estimated distance from the current node to a goal node
56
+ h_score_from_goal : float
57
+ the estimated distance from the current node to a start node
58
+ f_score_from_start : float
59
+ the sum of g_score_from_start and h_score_from_start
60
+ f_score_from_goal : float
61
+ the sum of g_score_from_goal and h_score_from_goal
62
+ predecessor_from_start : BidirectionalNode
63
+ the current node's immediate predecessor, from which we
64
+ travelled to the current node
65
+ The predecessor's first ancestor is the start node
66
+ predecessor_from_goal : BidirectionalNode
67
+ the current node's immediate predecessor, from which we
68
+ travelled to the current node The predecessor's first ancestor
69
+ is the goal node
70
+ """
71
+ # Use __slots__ to reduce memory overhead and access time
72
+ __slots__ = (
73
+ '_point', '_g_score_from_start', '_g_score_from_goal',
74
+ '_h_score_from_start', '_h_score_from_goal',
75
+ '_f_score_from_start', '_f_score_from_goal',
76
+ '_predecessor_from_start', '_predecessor_from_goal'
77
+ )
78
+
79
+ def __init__(
80
+ self,
81
+ point: np.ndarray,
82
+ g_score_from_start: float = float('inf'),
83
+ g_score_from_goal: float = float('inf'),
84
+ h_score_from_start: float = float('inf'),
85
+ h_score_from_goal: float = float('inf'),
86
+ f_score_from_start: float = float('inf'),
87
+ f_score_from_goal: float = float('inf'),
88
+ predecessor_from_start: 'BidirectionalNode' = None,
89
+ predecessor_from_goal: 'BidirectionalNode' = None
90
+ ):
91
+ # Convert point to int64 for better performance with Numba
92
+ if isinstance(point, list):
93
+ point = np.array(point)
94
+ if not isinstance(point, np.ndarray):
95
+ point = np.array(point)
96
+ if point.dtype != np.int64:
97
+ point = point.astype(np.int64)
98
+
99
+ self._point = point
100
+ self._g_score_from_start = float(g_score_from_start)
101
+ self._g_score_from_goal = float(g_score_from_goal)
102
+ self._h_score_from_start = float(h_score_from_start)
103
+ self._h_score_from_goal = float(h_score_from_goal)
104
+
105
+ # Use Numba functions for f_score calculations if they're not provided
106
+ if f_score_from_start == float('inf') and g_score_from_start != float('inf') and h_score_from_start != float('inf'):
107
+ self._f_score_from_start = compute_f_score(g_score_from_start, h_score_from_start)
108
+ else:
109
+ self._f_score_from_start = float(f_score_from_start)
110
+
111
+ if f_score_from_goal == float('inf') and g_score_from_goal != float('inf') and h_score_from_goal != float('inf'):
112
+ self._f_score_from_goal = compute_f_score(g_score_from_goal, h_score_from_goal)
113
+ else:
114
+ self._f_score_from_goal = float(f_score_from_goal)
115
+
116
+ self._predecessor_from_start = predecessor_from_start
117
+ self._predecessor_from_goal = predecessor_from_goal
118
+
119
+ @property
120
+ def point(self):
121
+ return self._point
122
+
123
+ @point.setter
124
+ def point(self, value: np.ndarray):
125
+ if value is None:
126
+ raise TypeError
127
+ if not validate_point(value):
128
+ raise ValueError
129
+ # Ensure int64 type for better performance
130
+ if not isinstance(value, np.ndarray):
131
+ value = np.array(value, dtype=np.int64)
132
+ if value.dtype != np.int64:
133
+ value = value.astype(np.int64)
134
+ self._point = value
135
+
136
+ @property
137
+ def g_score_from_start(self):
138
+ return self._g_score_from_start
139
+
140
+ @g_score_from_start.setter
141
+ def g_score_from_start(self, value: float):
142
+ self._g_score_from_start = float(value)
143
+ # Update f_score when g_score changes
144
+ if self._h_score_from_start != float('inf'):
145
+ self._f_score_from_start = compute_f_score(value, self._h_score_from_start)
146
+
147
+ @property
148
+ def g_score_from_goal(self):
149
+ return self._g_score_from_goal
150
+
151
+ @g_score_from_goal.setter
152
+ def g_score_from_goal(self, value: float):
153
+ self._g_score_from_goal = float(value)
154
+ # Update f_score when g_score changes
155
+ if self._h_score_from_goal != float('inf'):
156
+ self._f_score_from_goal = compute_f_score(value, self._h_score_from_goal)
157
+
158
+ @property
159
+ def h_score_from_start(self):
160
+ return self._h_score_from_start
161
+
162
+ @h_score_from_start.setter
163
+ def h_score_from_start(self, value: float):
164
+ self._h_score_from_start = float(value)
165
+ # Update f_score when h_score changes
166
+ if self._g_score_from_start != float('inf'):
167
+ self._f_score_from_start = compute_f_score(self._g_score_from_start, value)
168
+
169
+ @property
170
+ def h_score_from_goal(self):
171
+ return self._h_score_from_goal
172
+
173
+ @h_score_from_goal.setter
174
+ def h_score_from_goal(self, value: float):
175
+ self._h_score_from_goal = float(value)
176
+ # Update f_score when h_score changes
177
+ if self._g_score_from_goal != float('inf'):
178
+ self._f_score_from_goal = compute_f_score(self._g_score_from_goal, value)
179
+
180
+ @property
181
+ def f_score_from_start(self):
182
+ return self._f_score_from_start
183
+
184
+ @f_score_from_start.setter
185
+ def f_score_from_start(self, value: float):
186
+ self._f_score_from_start = float(value)
187
+
188
+ @property
189
+ def f_score_from_goal(self):
190
+ return self._f_score_from_goal
191
+
192
+ @f_score_from_goal.setter
193
+ def f_score_from_goal(self, value: float):
194
+ self._f_score_from_goal = float(value)
195
+
196
+ @property
197
+ def predecessor_from_start(self):
198
+ return self._predecessor_from_start
199
+
200
+ @predecessor_from_start.setter
201
+ def predecessor_from_start(self, value):
202
+ self._predecessor_from_start = value
203
+
204
+ @property
205
+ def predecessor_from_goal(self):
206
+ return self._predecessor_from_goal
207
+
208
+ @predecessor_from_goal.setter
209
+ def predecessor_from_goal(self, value):
210
+ self._predecessor_from_goal = value
211
+
212
+ # Fast accessor methods optimized for performance
213
+ def get_g(self, from_start: bool) -> float:
214
+ """Get the appropriate g_score based on direction"""
215
+ return get_score_by_direction(from_start, self._g_score_from_start, self._g_score_from_goal)
216
+
217
+ def get_f(self, from_start: bool) -> float:
218
+ """Get the appropriate f_score based on direction"""
219
+ return get_score_by_direction(from_start, self._f_score_from_start, self._f_score_from_goal)
220
+
221
+ def set_g(self, g_score: float, from_start: bool):
222
+ """Set the appropriate g_score based on direction"""
223
+ if from_start:
224
+ self.g_score_from_start = g_score
225
+ else:
226
+ self.g_score_from_goal = g_score
227
+
228
+ def set_f(self, f_score: float, from_start: bool):
229
+ """Set the appropriate f_score based on direction"""
230
+ if from_start:
231
+ self.f_score_from_start = f_score
232
+ else:
233
+ self.f_score_from_goal = f_score
234
+
235
+ def set_predecessor(self, predecessor, from_start: bool):
236
+ """Set the appropriate predecessor based on direction"""
237
+ if from_start:
238
+ self.predecessor_from_start = predecessor
239
+ else:
240
+ self.predecessor_from_goal = predecessor
@@ -0,0 +1,125 @@
1
+ import numpy as np
2
+ import numba as nb
3
+
4
+ # Numba-optimized helper functions for node operations
5
+ @nb.njit(fastmath=True)
6
+ def compute_f_score(g_score: float, h_score: float) -> float:
7
+ """Compute f_score from g_score and h_score with Numba optimization"""
8
+ return g_score + h_score
9
+
10
+ @nb.njit
11
+ def validate_point(point):
12
+ """Validate point array with Numba optimization"""
13
+ return len(point) > 0
14
+
15
+ class Node:
16
+ """Class holding information about a node, optimized for numerical operations
17
+
18
+ Parameters
19
+ ----------
20
+ point : numpy ndarray
21
+ the 2D/3D coordinates of the node (can be a pixel or a voxel)
22
+ g_score : float
23
+ the distance from a starting node to the current node
24
+ h_score : float
25
+ the estimated distance from the current node to a goal_node
26
+ predecessor : Node
27
+ the current node's immediate predecessor, from which we
28
+ travelled to the current node
29
+
30
+ Attributes
31
+ ----------
32
+ point : numpy ndarray
33
+ the 2D/3D coordinates of the node
34
+ g_score : float
35
+ the actual cost from a starting node to the current node
36
+ h_score : float
37
+ the estimated cost from the current node to a goal_node
38
+ f_score : float
39
+ the sum of the node's g_score and h_score
40
+ predecessor : Node
41
+ the current node's immediate predecessor, from which we
42
+ travelled to the current node
43
+ """
44
+ __slots__ = ('_point', '_g_score', '_h_score', '_f_score', '_predecessor')
45
+
46
+ def __init__(
47
+ self,
48
+ point: np.ndarray,
49
+ g_score: float,
50
+ h_score: float,
51
+ predecessor: 'Node' = None
52
+ ):
53
+ # Convert point to int64 for better performance with Numba
54
+ if isinstance(point, list):
55
+ point = np.array(point)
56
+ if not isinstance(point, np.ndarray):
57
+ point = np.array(point)
58
+ if point.dtype != np.int64:
59
+ point = point.astype(np.int64)
60
+
61
+ self._point = point
62
+ self._g_score = float(g_score)
63
+ self._h_score = float(h_score)
64
+ # Use Numba function for f_score calculation
65
+ self._f_score = compute_f_score(g_score, h_score)
66
+ self._predecessor = predecessor
67
+
68
+ @property
69
+ def point(self):
70
+ return self._point
71
+
72
+ @point.setter
73
+ def point(self, value: np.ndarray):
74
+ if value is None:
75
+ raise TypeError
76
+ if not validate_point(value):
77
+ raise ValueError
78
+ # Ensure int64 type for better performance
79
+ if not isinstance(value, np.ndarray):
80
+ value = np.array(value, dtype=np.int64)
81
+ if value.dtype != np.int64:
82
+ value = value.astype(np.int64)
83
+ self._point = value
84
+
85
+ @property
86
+ def g_score(self):
87
+ return self._g_score
88
+
89
+ @g_score.setter
90
+ def g_score(self, value: float):
91
+ if value is None:
92
+ raise TypeError
93
+ self._g_score = float(value)
94
+ # Update f_score when g_score changes
95
+ self._f_score = compute_f_score(self._g_score, self._h_score)
96
+
97
+ @property
98
+ def h_score(self):
99
+ return self._h_score
100
+
101
+ @h_score.setter
102
+ def h_score(self, value: float):
103
+ if value is None:
104
+ raise TypeError
105
+ self._h_score = float(value)
106
+ # Update f_score when h_score changes
107
+ self._f_score = compute_f_score(self._g_score, self._h_score)
108
+
109
+ @property
110
+ def f_score(self):
111
+ return self._f_score
112
+
113
+ @f_score.setter
114
+ def f_score(self, value: float):
115
+ if value is None:
116
+ raise TypeError
117
+ self._f_score = float(value)
118
+
119
+ @property
120
+ def predecessor(self):
121
+ return self._predecessor
122
+
123
+ @predecessor.setter
124
+ def predecessor(self, value):
125
+ self._predecessor = value
@@ -0,0 +1,4 @@
1
+ from .flythrough import create_path_flythrough
2
+ from .tube_flythrough import create_tube_flythrough
3
+ from .flythrough_all import create_integrated_flythrough
4
+ from .tube_data import create_tube_data