nettracer3d 0.6.8__py3-none-any.whl → 0.7.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.
- nettracer3d/modularity.py +23 -24
- nettracer3d/morphology.py +14 -9
- nettracer3d/nettracer.py +271 -44
- nettracer3d/nettracer_gui.py +502 -64
- nettracer3d/proximity.py +376 -16
- nettracer3d/segmenter.py +87 -4
- nettracer3d/smart_dilate.py +23 -24
- {nettracer3d-0.6.8.dist-info → nettracer3d-0.7.0.dist-info}/METADATA +42 -9
- nettracer3d-0.7.0.dist-info/RECORD +20 -0
- {nettracer3d-0.6.8.dist-info → nettracer3d-0.7.0.dist-info}/WHEEL +1 -1
- nettracer3d-0.6.8.dist-info/RECORD +0 -20
- {nettracer3d-0.6.8.dist-info → nettracer3d-0.7.0.dist-info}/entry_points.txt +0 -0
- {nettracer3d-0.6.8.dist-info → nettracer3d-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {nettracer3d-0.6.8.dist-info → nettracer3d-0.7.0.dist-info}/top_level.txt +0 -0
nettracer3d/proximity.py
CHANGED
|
@@ -7,6 +7,7 @@ from scipy import ndimage
|
|
|
7
7
|
import concurrent.futures
|
|
8
8
|
import multiprocessing as mp
|
|
9
9
|
import pandas as pd
|
|
10
|
+
import matplotlib.pyplot as plt
|
|
10
11
|
from typing import Dict, Union, Tuple, List, Optional
|
|
11
12
|
|
|
12
13
|
|
|
@@ -178,9 +179,31 @@ def populate_array(centroids):
|
|
|
178
179
|
|
|
179
180
|
return array
|
|
180
181
|
|
|
181
|
-
def find_neighbors_kdtree(
|
|
182
|
+
def find_neighbors_kdtree(radius, centroids=None, array=None, targets=None):
|
|
182
183
|
# Get coordinates of nonzero points
|
|
183
|
-
|
|
184
|
+
if centroids:
|
|
185
|
+
# If centroids is a dictionary mapping node IDs to coordinates
|
|
186
|
+
if isinstance(centroids, dict):
|
|
187
|
+
# Extract the node IDs and points
|
|
188
|
+
node_ids = list(centroids.keys())
|
|
189
|
+
points_list = list(centroids.values())
|
|
190
|
+
points = np.array(points_list, dtype=np.int32)
|
|
191
|
+
else:
|
|
192
|
+
# If centroids is just a list of points
|
|
193
|
+
points = np.array(centroids, dtype=np.int32)
|
|
194
|
+
node_ids = list(range(1, len(points) + 1)) # Default sequential IDs
|
|
195
|
+
|
|
196
|
+
# Create a temporary array only if we need it for lookups
|
|
197
|
+
if array is None:
|
|
198
|
+
max_coords = np.max(points, axis=0) + 1
|
|
199
|
+
array = np.zeros(tuple(max_coords), dtype=np.int32)
|
|
200
|
+
for i, point in enumerate(points):
|
|
201
|
+
array[tuple(point)] = node_ids[i] # Use the actual node ID
|
|
202
|
+
elif array is not None:
|
|
203
|
+
points = np.transpose(np.nonzero(array))
|
|
204
|
+
node_ids = None # Not used in array-based mode
|
|
205
|
+
else:
|
|
206
|
+
return []
|
|
184
207
|
|
|
185
208
|
# Create KD-tree from all nonzero points
|
|
186
209
|
tree = KDTree(points)
|
|
@@ -188,19 +211,29 @@ def find_neighbors_kdtree(array, radius, targets=None):
|
|
|
188
211
|
if targets is None:
|
|
189
212
|
# Original behavior: find neighbors for all points
|
|
190
213
|
query_points = points
|
|
191
|
-
query_indices = range(len(points))
|
|
214
|
+
query_indices = range(len(points))
|
|
192
215
|
else:
|
|
193
216
|
# Find coordinates of target values
|
|
194
217
|
target_points = []
|
|
195
|
-
target_indices = []
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
218
|
+
target_indices = []
|
|
219
|
+
|
|
220
|
+
if array is not None:
|
|
221
|
+
# Standard array-based filtering
|
|
222
|
+
for idx, point in enumerate(points):
|
|
223
|
+
point_tuple = tuple(point)
|
|
224
|
+
if array[point_tuple] in targets:
|
|
225
|
+
target_points.append(point)
|
|
226
|
+
target_indices.append(idx)
|
|
227
|
+
else:
|
|
228
|
+
# Filter based on node IDs directly
|
|
229
|
+
for idx, node_id in enumerate(node_ids):
|
|
230
|
+
if node_id in targets:
|
|
231
|
+
target_points.append(points[idx])
|
|
232
|
+
target_indices.append(idx)
|
|
200
233
|
|
|
201
234
|
# Convert to numpy array for querying
|
|
202
235
|
query_points = np.array(target_points)
|
|
203
|
-
query_indices = target_indices
|
|
236
|
+
query_indices = target_indices
|
|
204
237
|
|
|
205
238
|
# Handle case where no target values were found
|
|
206
239
|
if len(query_points) == 0:
|
|
@@ -214,17 +247,21 @@ def find_neighbors_kdtree(array, radius, targets=None):
|
|
|
214
247
|
|
|
215
248
|
# Generate pairs
|
|
216
249
|
for i, neighbors in enumerate(neighbor_indices):
|
|
217
|
-
query_idx = query_indices[i]
|
|
250
|
+
query_idx = query_indices[i]
|
|
218
251
|
for neighbor_idx in neighbors:
|
|
219
252
|
# Skip self-pairing
|
|
220
253
|
if neighbor_idx != query_idx:
|
|
221
|
-
|
|
222
|
-
|
|
254
|
+
# Use node IDs from the dictionary if available
|
|
255
|
+
if array is not None:
|
|
256
|
+
query_value = array[tuple(points[query_idx])]
|
|
257
|
+
neighbor_value = array[tuple(points[neighbor_idx])]
|
|
258
|
+
else:
|
|
259
|
+
query_value = node_ids[query_idx]
|
|
260
|
+
neighbor_value = node_ids[neighbor_idx]
|
|
223
261
|
output.append([query_value, neighbor_value, 0])
|
|
224
262
|
|
|
225
263
|
return output
|
|
226
264
|
|
|
227
|
-
|
|
228
265
|
def extract_pairwise_connections(connections):
|
|
229
266
|
output = []
|
|
230
267
|
|
|
@@ -252,7 +289,6 @@ def create_voronoi_3d_kdtree(centroids: Dict[Union[int, str], Union[Tuple[int, i
|
|
|
252
289
|
Returns:
|
|
253
290
|
3D numpy array where each cell contains the label of the closest centroid as uint32
|
|
254
291
|
"""
|
|
255
|
-
from scipy.spatial import cKDTree
|
|
256
292
|
|
|
257
293
|
# Convert string labels to integers if necessary
|
|
258
294
|
if any(isinstance(k, str) for k in centroids.keys()):
|
|
@@ -269,7 +305,7 @@ def create_voronoi_3d_kdtree(centroids: Dict[Union[int, str], Union[Tuple[int, i
|
|
|
269
305
|
shape = tuple(max_coord + 1 for max_coord in max_coords)
|
|
270
306
|
|
|
271
307
|
# Create KD-tree
|
|
272
|
-
tree =
|
|
308
|
+
tree = KDTree(centroid_points)
|
|
273
309
|
|
|
274
310
|
# Create coordinate arrays
|
|
275
311
|
coords = np.array(np.meshgrid(
|
|
@@ -286,4 +322,328 @@ def create_voronoi_3d_kdtree(centroids: Dict[Union[int, str], Union[Tuple[int, i
|
|
|
286
322
|
label_array = labels[indices].astype(np.uint32)
|
|
287
323
|
|
|
288
324
|
# Reshape to final shape
|
|
289
|
-
return label_array.reshape(shape)
|
|
325
|
+
return label_array.reshape(shape)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
#Ripley cluster analysis:
|
|
330
|
+
|
|
331
|
+
def convert_centroids_to_array(centroids_list, xy_scale = 1, z_scale = 1):
|
|
332
|
+
"""
|
|
333
|
+
Convert a dictionary of centroids to a numpy array suitable for Ripley's K calculation.
|
|
334
|
+
|
|
335
|
+
Parameters:
|
|
336
|
+
centroids_list: List of centroid coordinate arrays
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
numpy array of shape (n, d) where n is number of points and d is dimensionality
|
|
340
|
+
"""
|
|
341
|
+
# Determine how many centroids we have
|
|
342
|
+
n_points = len(centroids_list)
|
|
343
|
+
|
|
344
|
+
# Get dimensionality from the first centroid
|
|
345
|
+
dim = len(list(centroids_list)[0])
|
|
346
|
+
|
|
347
|
+
# Create empty array
|
|
348
|
+
points_array = np.zeros((n_points, dim))
|
|
349
|
+
|
|
350
|
+
# Fill array with coordinates
|
|
351
|
+
for i, coords in enumerate(centroids_list):
|
|
352
|
+
points_array[i] = coords
|
|
353
|
+
|
|
354
|
+
points_array[:, 1:] = points_array[:, 1:] * xy_scale #account for scaling
|
|
355
|
+
|
|
356
|
+
points_array[:, 0] = points_array[:, 0] * z_scale #account for scaling
|
|
357
|
+
|
|
358
|
+
return points_array
|
|
359
|
+
|
|
360
|
+
def generate_r_values(points_array, step_size, bounds = None, dim = 2, max_proportion=0.5):
|
|
361
|
+
"""
|
|
362
|
+
Generate an array of r values based on point distribution and step size.
|
|
363
|
+
|
|
364
|
+
Parameters:
|
|
365
|
+
points_array: numpy array of shape (n, d) with point coordinates
|
|
366
|
+
step_size: user-defined step size for r values
|
|
367
|
+
max_proportion: maximum proportion of the study area extent to use (default 0.5)
|
|
368
|
+
This prevents analyzing at distances where edge effects dominate
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
numpy array of r values
|
|
372
|
+
"""
|
|
373
|
+
|
|
374
|
+
if bounds is None:
|
|
375
|
+
if dim == 2:
|
|
376
|
+
min_coords = np.array([0,0])
|
|
377
|
+
else:
|
|
378
|
+
min_coords = np.array([0,0,0])
|
|
379
|
+
max_coords = np.max(points_array, axis=0)
|
|
380
|
+
max_coords = np.flip(max_coords)
|
|
381
|
+
else:
|
|
382
|
+
min_coords, max_coords = bounds
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
# Calculate the longest dimension
|
|
386
|
+
dimensions = max_coords - min_coords
|
|
387
|
+
max_dimension = np.max(dimensions)
|
|
388
|
+
|
|
389
|
+
# Calculate maximum r value (typically half the shortest side for 2D,
|
|
390
|
+
# or scaled by max_proportion for general use)
|
|
391
|
+
max_r = max_dimension * max_proportion
|
|
392
|
+
|
|
393
|
+
# Generate r values from 0 to max_r with step_size increments
|
|
394
|
+
num_steps = int(max_r / step_size)
|
|
395
|
+
r_values = np.linspace(step_size, max_r, num_steps)
|
|
396
|
+
|
|
397
|
+
if r_values[0] == 0:
|
|
398
|
+
np.delete(r_values, 0)
|
|
399
|
+
|
|
400
|
+
return r_values
|
|
401
|
+
|
|
402
|
+
def convert_augmented_array_to_points(augmented_array):
|
|
403
|
+
"""
|
|
404
|
+
Convert an array where first column is 1 and remaining columns are coordinates.
|
|
405
|
+
|
|
406
|
+
Parameters:
|
|
407
|
+
augmented_array: 2D array where first column is 1 and rest are coordinates
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
numpy array with just the coordinate columns
|
|
411
|
+
"""
|
|
412
|
+
# Extract just the coordinate columns (all except first column)
|
|
413
|
+
return augmented_array[:, 1:]
|
|
414
|
+
|
|
415
|
+
def optimized_ripleys_k(reference_points, subset_points, r_values, bounds=None, edge_correction=True, dim = 2, is_subset = False):
|
|
416
|
+
"""
|
|
417
|
+
Optimized computation of Ripley's K function using KD-Tree with simplified but effective edge correction.
|
|
418
|
+
|
|
419
|
+
Parameters:
|
|
420
|
+
reference_points: numpy array of shape (n, d) containing coordinates (d=2 or d=3)
|
|
421
|
+
subset_points: numpy array of shape (m, d) containing coordinates
|
|
422
|
+
r_values: numpy array of distances at which to compute K
|
|
423
|
+
bounds: tuple of (min_coords, max_coords) defining the study area boundaries
|
|
424
|
+
edge_correction: Boolean indicating whether to apply edge correction
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
K_values: numpy array of K values corresponding to r_values
|
|
428
|
+
"""
|
|
429
|
+
n_ref = len(reference_points)
|
|
430
|
+
n_subset = len(subset_points)
|
|
431
|
+
|
|
432
|
+
# Determine bounds if not provided
|
|
433
|
+
if bounds is None:
|
|
434
|
+
min_coords = np.min(reference_points, axis=0)
|
|
435
|
+
max_coords = np.max(reference_points, axis=0)
|
|
436
|
+
bounds = (min_coords, max_coords)
|
|
437
|
+
|
|
438
|
+
# Calculate volume of study area
|
|
439
|
+
min_bounds, max_bounds = bounds
|
|
440
|
+
sides = max_bounds - min_bounds
|
|
441
|
+
volume = np.prod(sides)
|
|
442
|
+
|
|
443
|
+
# Point intensity (points per unit volume)
|
|
444
|
+
intensity = n_ref / volume
|
|
445
|
+
|
|
446
|
+
# Build KD-Tree for efficient nearest neighbor search
|
|
447
|
+
tree = KDTree(reference_points)
|
|
448
|
+
|
|
449
|
+
# Initialize K values
|
|
450
|
+
K_values = np.zeros(len(r_values))
|
|
451
|
+
|
|
452
|
+
# For each r value, compute cumulative counts
|
|
453
|
+
for i, r in enumerate(r_values):
|
|
454
|
+
total_count = 0
|
|
455
|
+
|
|
456
|
+
# Query the tree for all points within radius r of each subset point
|
|
457
|
+
for j, point in enumerate(subset_points):
|
|
458
|
+
# Find all reference points within radius r
|
|
459
|
+
indices = tree.query_ball_point(point, r)
|
|
460
|
+
count = len(indices)
|
|
461
|
+
|
|
462
|
+
# Apply edge correction if needed
|
|
463
|
+
if edge_correction:
|
|
464
|
+
# Calculate edge correction weight
|
|
465
|
+
weight = 1.0
|
|
466
|
+
|
|
467
|
+
if dim == 2:
|
|
468
|
+
# For 2D - check all four boundaries
|
|
469
|
+
x, y = point
|
|
470
|
+
|
|
471
|
+
# Distances to all boundaries
|
|
472
|
+
x_min_dist = x - min_bounds[0]
|
|
473
|
+
x_max_dist = max_bounds[0] - x
|
|
474
|
+
y_min_dist = y - min_bounds[1]
|
|
475
|
+
y_max_dist = max_bounds[1] - y
|
|
476
|
+
|
|
477
|
+
proportion_in = 1.0
|
|
478
|
+
# Apply correction for each boundary if needed
|
|
479
|
+
if x_min_dist < r:
|
|
480
|
+
proportion_in -= 0.5 * (1 - x_min_dist/r)
|
|
481
|
+
if x_max_dist < r:
|
|
482
|
+
proportion_in -= 0.5 * (1 - x_max_dist/r)
|
|
483
|
+
if y_min_dist < r:
|
|
484
|
+
proportion_in -= 0.5 * (1 - y_min_dist/r)
|
|
485
|
+
if y_max_dist < r:
|
|
486
|
+
proportion_in -= 0.5 * (1 - y_max_dist/r)
|
|
487
|
+
|
|
488
|
+
# Corner correction
|
|
489
|
+
if ((x_min_dist < r and y_min_dist < r) or
|
|
490
|
+
(x_min_dist < r and y_max_dist < r) or
|
|
491
|
+
(x_max_dist < r and y_min_dist < r) or
|
|
492
|
+
(x_max_dist < r and y_max_dist < r)):
|
|
493
|
+
proportion_in += 0.1 # Add a small boost for corners
|
|
494
|
+
|
|
495
|
+
elif dim == 3:
|
|
496
|
+
# For 3D - check all six boundaries
|
|
497
|
+
x, y, z = point
|
|
498
|
+
|
|
499
|
+
# Distances to all boundaries
|
|
500
|
+
x_min_dist = x - min_bounds[0]
|
|
501
|
+
x_max_dist = max_bounds[0] - x
|
|
502
|
+
y_min_dist = y - min_bounds[1]
|
|
503
|
+
y_max_dist = max_bounds[1] - y
|
|
504
|
+
z_min_dist = z - min_bounds[2]
|
|
505
|
+
z_max_dist = max_bounds[2] - z
|
|
506
|
+
|
|
507
|
+
proportion_in = 1.0
|
|
508
|
+
# Apply correction for each boundary if needed
|
|
509
|
+
if x_min_dist < r:
|
|
510
|
+
proportion_in -= 0.25 * (1 - x_min_dist/r)
|
|
511
|
+
if x_max_dist < r:
|
|
512
|
+
proportion_in -= 0.25 * (1 - x_max_dist/r)
|
|
513
|
+
if y_min_dist < r:
|
|
514
|
+
proportion_in -= 0.25 * (1 - y_min_dist/r)
|
|
515
|
+
if y_max_dist < r:
|
|
516
|
+
proportion_in -= 0.25 * (1 - y_max_dist/r)
|
|
517
|
+
if z_min_dist < r:
|
|
518
|
+
proportion_in -= 0.25 * (1 - z_min_dist/r)
|
|
519
|
+
if z_max_dist < r:
|
|
520
|
+
proportion_in -= 0.25 * (1 - z_max_dist/r)
|
|
521
|
+
|
|
522
|
+
# Corner correction for 3D (if point is near a corner)
|
|
523
|
+
num_close_edges = (
|
|
524
|
+
(x_min_dist < r) + (x_max_dist < r) +
|
|
525
|
+
(y_min_dist < r) + (y_max_dist < r) +
|
|
526
|
+
(z_min_dist < r) + (z_max_dist < r)
|
|
527
|
+
)
|
|
528
|
+
if num_close_edges >= 2:
|
|
529
|
+
proportion_in += 0.05 * num_close_edges # Stronger boost for more edges
|
|
530
|
+
|
|
531
|
+
# Ensure proportion_in stays within reasonable bounds
|
|
532
|
+
proportion_in = max(0.1, min(1.0, proportion_in))
|
|
533
|
+
weight = 1.0 / proportion_in
|
|
534
|
+
|
|
535
|
+
count *= weight
|
|
536
|
+
|
|
537
|
+
total_count += count
|
|
538
|
+
|
|
539
|
+
# Subtract self-counts if points appear in both sets
|
|
540
|
+
if is_subset or np.array_equal(reference_points, subset_points):
|
|
541
|
+
total_count -= n_ref # Subtract all self-counts
|
|
542
|
+
|
|
543
|
+
# Normalize
|
|
544
|
+
K_values[i] = total_count / (n_subset * intensity)
|
|
545
|
+
|
|
546
|
+
return K_values
|
|
547
|
+
|
|
548
|
+
def ripleys_h_function_3d(k_values, r_values):
|
|
549
|
+
"""
|
|
550
|
+
Convert K values to H values for 3D point patterns with edge correction.
|
|
551
|
+
|
|
552
|
+
Parameters:
|
|
553
|
+
k_values: numpy array of K function values
|
|
554
|
+
r_values: numpy array of distances at which K was computed
|
|
555
|
+
edge_weights: optional array of edge correction weights
|
|
556
|
+
|
|
557
|
+
Returns:
|
|
558
|
+
h_values: numpy array of H function values
|
|
559
|
+
"""
|
|
560
|
+
h_values = np.cbrt(k_values / (4/3 * np.pi)) - r_values
|
|
561
|
+
|
|
562
|
+
return h_values
|
|
563
|
+
|
|
564
|
+
def ripleys_h_function_2d(k_values, r_values):
|
|
565
|
+
"""
|
|
566
|
+
Convert K values to H values for 2D point patterns with edge correction.
|
|
567
|
+
|
|
568
|
+
Parameters:
|
|
569
|
+
k_values: numpy array of K function values
|
|
570
|
+
r_values: numpy array of distances at which K was computed
|
|
571
|
+
edge_weights: optional array of edge correction weights
|
|
572
|
+
|
|
573
|
+
Returns:
|
|
574
|
+
h_values: numpy array of H function values
|
|
575
|
+
"""
|
|
576
|
+
h_values = np.sqrt(k_values / np.pi) - r_values
|
|
577
|
+
|
|
578
|
+
return h_values
|
|
579
|
+
|
|
580
|
+
def compute_ripleys_h(k_values, r_values, dimension=2):
|
|
581
|
+
"""
|
|
582
|
+
Compute Ripley's H function (normalized K) with edge correction.
|
|
583
|
+
|
|
584
|
+
Parameters:
|
|
585
|
+
k_values: numpy array of K function values
|
|
586
|
+
r_values: numpy array of distances at which K was computed
|
|
587
|
+
edge_weights: optional array of edge correction weights
|
|
588
|
+
dimension: dimensionality of the point pattern (2 for 2D, 3 for 3D)
|
|
589
|
+
|
|
590
|
+
Returns:
|
|
591
|
+
h_values: numpy array of H function values
|
|
592
|
+
"""
|
|
593
|
+
if dimension == 2:
|
|
594
|
+
return ripleys_h_function_2d(k_values, r_values)
|
|
595
|
+
elif dimension == 3:
|
|
596
|
+
return ripleys_h_function_3d(k_values, r_values)
|
|
597
|
+
else:
|
|
598
|
+
raise ValueError("Dimension must be 2 or 3")
|
|
599
|
+
|
|
600
|
+
def plot_ripley_functions(r_values, k_values, h_values, dimension=2, figsize=(12, 5)):
|
|
601
|
+
"""
|
|
602
|
+
Plot Ripley's K and H functions with theoretical Poisson distribution references
|
|
603
|
+
adjusted for edge effects.
|
|
604
|
+
|
|
605
|
+
Parameters:
|
|
606
|
+
r_values: numpy array of distances at which K and H were computed
|
|
607
|
+
k_values: numpy array of K function values
|
|
608
|
+
h_values: numpy array of H function values (normalized K)
|
|
609
|
+
edge_weights: optional array of edge correction weights
|
|
610
|
+
dimension: dimensionality of the point pattern (2 for 2D, 3 for 3D)
|
|
611
|
+
figsize: tuple specifying figure size (width, height)
|
|
612
|
+
"""
|
|
613
|
+
|
|
614
|
+
#plt.figure()
|
|
615
|
+
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=figsize)
|
|
616
|
+
|
|
617
|
+
# Theoretical values for complete spatial randomness (CSR)
|
|
618
|
+
if dimension == 2:
|
|
619
|
+
theo_k = np.pi * r_values**2 # πr² for 2D
|
|
620
|
+
elif dimension == 3:
|
|
621
|
+
theo_k = (4/3) * np.pi * r_values**3 # (4/3)πr³ for 3D
|
|
622
|
+
else:
|
|
623
|
+
raise ValueError("Dimension must be 2 or 3")
|
|
624
|
+
|
|
625
|
+
# Theoretical H values are always 0 for CSR
|
|
626
|
+
theo_h = np.zeros_like(r_values)
|
|
627
|
+
|
|
628
|
+
# Plot K function
|
|
629
|
+
ax1.plot(r_values, k_values, 'b-', label='Observed K(r)')
|
|
630
|
+
ax1.plot(r_values, theo_k, 'r--', label='Theoretical K(r) for CSR')
|
|
631
|
+
ax1.set_xlabel('Distance (r)')
|
|
632
|
+
ax1.set_ylabel('L(r)')
|
|
633
|
+
ax1.set_title("Ripley's K Function")
|
|
634
|
+
ax1.legend()
|
|
635
|
+
ax1.grid(True, alpha=0.3)
|
|
636
|
+
|
|
637
|
+
# Plot H function
|
|
638
|
+
ax2.plot(r_values, h_values, 'b-', label='Observed H(r)')
|
|
639
|
+
ax2.plot(r_values, theo_h, 'r--', label='Theoretical H(r) for CSR')
|
|
640
|
+
ax2.set_xlabel('Distance (r)')
|
|
641
|
+
ax2.set_ylabel('L(r) Normalized')
|
|
642
|
+
ax2.set_title("Ripley's H Function")
|
|
643
|
+
ax2.axhline(y=0, color='k', linestyle='-', alpha=0.3)
|
|
644
|
+
ax2.legend()
|
|
645
|
+
ax2.grid(True, alpha=0.3)
|
|
646
|
+
|
|
647
|
+
plt.tight_layout()
|
|
648
|
+
plt.show()
|
|
649
|
+
#plt.clf()
|
nettracer3d/segmenter.py
CHANGED
|
@@ -75,6 +75,12 @@ class InteractiveSegmenter:
|
|
|
75
75
|
self.dogs = [(1, 2), (2, 4), (4, 8)]
|
|
76
76
|
self.master_chunk = 49
|
|
77
77
|
|
|
78
|
+
#Data when loading prev model:
|
|
79
|
+
self.previous_foreground = None
|
|
80
|
+
self.previous_background = None
|
|
81
|
+
self.previous_z_fore = None
|
|
82
|
+
self.previous_z_back = None
|
|
83
|
+
|
|
78
84
|
def segment_slice_chunked(self, slice_z, block_size = 49):
|
|
79
85
|
"""
|
|
80
86
|
A completely standalone method to segment a single z-slice in chunks
|
|
@@ -1011,7 +1017,7 @@ class InteractiveSegmenter:
|
|
|
1011
1017
|
futures.append(future)
|
|
1012
1018
|
|
|
1013
1019
|
def compute_dog_local(img, s1, s2):
|
|
1014
|
-
g1 = ndimage.gaussian_filter(img, s1)
|
|
1020
|
+
g1 = ndimage.gaussian_filter(img, s1) # Consider just having this return the gaussians to
|
|
1015
1021
|
g2 = ndimage.gaussian_filter(img, s2)
|
|
1016
1022
|
return g1 - g2
|
|
1017
1023
|
|
|
@@ -1757,10 +1763,11 @@ class InteractiveSegmenter:
|
|
|
1757
1763
|
except:
|
|
1758
1764
|
pass
|
|
1759
1765
|
|
|
1760
|
-
def train_batch(self, foreground_array, speed = True, use_gpu = False, use_two = False, mem_lock = False):
|
|
1766
|
+
def train_batch(self, foreground_array, speed = True, use_gpu = False, use_two = False, mem_lock = False, saving = False):
|
|
1761
1767
|
"""Train directly on foreground and background arrays"""
|
|
1762
1768
|
|
|
1763
|
-
|
|
1769
|
+
if not saving:
|
|
1770
|
+
print("Training model...")
|
|
1764
1771
|
self.speed = speed
|
|
1765
1772
|
self.cur_gpu = use_gpu
|
|
1766
1773
|
if mem_lock != self.mem_lock:
|
|
@@ -1969,12 +1976,40 @@ class InteractiveSegmenter:
|
|
|
1969
1976
|
z_back, y_back, x_back = np.where(foreground_array == 2)
|
|
1970
1977
|
background_features = self.feature_cache[z_back, y_back, x_back]
|
|
1971
1978
|
except:
|
|
1972
|
-
|
|
1979
|
+
pass
|
|
1980
|
+
|
|
1981
|
+
|
|
1982
|
+
if self.previous_foreground is not None:
|
|
1983
|
+
failed = True
|
|
1984
|
+
try:
|
|
1985
|
+
foreground_features = np.vstack([self.previous_foreground, foreground_features])
|
|
1986
|
+
failed = False
|
|
1987
|
+
except:
|
|
1988
|
+
pass
|
|
1989
|
+
try:
|
|
1990
|
+
background_features = np.vstack([self.previous_background, background_features])
|
|
1991
|
+
failed = False
|
|
1992
|
+
except:
|
|
1993
|
+
pass
|
|
1994
|
+
try:
|
|
1995
|
+
z_fore = np.concatenate([self.previous_z_fore, z_fore])
|
|
1996
|
+
except:
|
|
1997
|
+
pass
|
|
1998
|
+
try:
|
|
1999
|
+
z_back = np.concatenate([self.previous_z_back, z_back])
|
|
2000
|
+
except:
|
|
2001
|
+
pass
|
|
2002
|
+
if failed:
|
|
2003
|
+
print("Could not combine new model with old loaded model. Perhaps you are trying to combine a quick model with a deep model? I cannot combine these...")
|
|
2004
|
+
|
|
2005
|
+
if saving:
|
|
1973
2006
|
|
|
2007
|
+
return foreground_features, background_features, z_fore, z_back
|
|
1974
2008
|
|
|
1975
2009
|
# Combine features and labels
|
|
1976
2010
|
X = np.vstack([foreground_features, background_features])
|
|
1977
2011
|
y = np.hstack([np.ones(len(z_fore)), np.zeros(len(z_back))])
|
|
2012
|
+
|
|
1978
2013
|
|
|
1979
2014
|
# Train the model
|
|
1980
2015
|
try:
|
|
@@ -1988,6 +2023,54 @@ class InteractiveSegmenter:
|
|
|
1988
2023
|
|
|
1989
2024
|
|
|
1990
2025
|
|
|
2026
|
+
print("Done")
|
|
2027
|
+
|
|
2028
|
+
|
|
2029
|
+
def save_model(self, file_name, foreground_array):
|
|
2030
|
+
|
|
2031
|
+
print("Saving model data")
|
|
2032
|
+
|
|
2033
|
+
foreground_features, background_features, z_fore, z_back = self.train_batch(foreground_array, speed = self.speed, use_gpu = self.use_gpu, use_two = self.use_two, mem_lock = self.mem_lock, saving = True)
|
|
2034
|
+
|
|
2035
|
+
|
|
2036
|
+
np.savez(file_name,
|
|
2037
|
+
foreground_features=foreground_features,
|
|
2038
|
+
background_features=background_features,
|
|
2039
|
+
z_fore=z_fore,
|
|
2040
|
+
z_back=z_back,
|
|
2041
|
+
speed=self.speed,
|
|
2042
|
+
use_gpu=self.use_gpu,
|
|
2043
|
+
use_two=self.use_two,
|
|
2044
|
+
mem_lock=self.mem_lock)
|
|
2045
|
+
|
|
2046
|
+
print(f"Model data saved to {file_name}")
|
|
2047
|
+
|
|
2048
|
+
|
|
2049
|
+
def load_model(self, file_name):
|
|
2050
|
+
|
|
2051
|
+
print("Loading model data")
|
|
2052
|
+
|
|
2053
|
+
data = np.load(file_name)
|
|
2054
|
+
|
|
2055
|
+
# Unpack the arrays
|
|
2056
|
+
self.previous_foreground = data['foreground_features']
|
|
2057
|
+
self.previous_background = data['background_features']
|
|
2058
|
+
self.previous_z_fore = data['z_fore']
|
|
2059
|
+
self.previous_z_back = data['z_back']
|
|
2060
|
+
self.speed = bool(data['speed'])
|
|
2061
|
+
self.use_gpu = bool(data['use_gpu'])
|
|
2062
|
+
self.use_two = bool(data['use_two'])
|
|
2063
|
+
self.mem_lock = bool(data['mem_lock'])
|
|
2064
|
+
|
|
2065
|
+
X = np.vstack([self.previous_foreground, self.previous_background])
|
|
2066
|
+
y = np.hstack([np.ones(len(self.previous_z_fore)), np.zeros(len(self.previous_z_back))])
|
|
2067
|
+
|
|
2068
|
+
try:
|
|
2069
|
+
self.model.fit(X, y)
|
|
2070
|
+
except:
|
|
2071
|
+
print(X)
|
|
2072
|
+
print(y)
|
|
2073
|
+
|
|
1991
2074
|
print("Done")
|
|
1992
2075
|
|
|
1993
2076
|
def get_feature_map_slice(self, z, speed, use_gpu):
|