nettracer3d 1.0.7__py3-none-any.whl → 1.0.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.
Potentially problematic release.
This version of nettracer3d might be problematic. Click here for more details.
- nettracer3d/morphology.py +108 -17
- nettracer3d/nettracer.py +64 -14
- nettracer3d/nettracer_gui.py +480 -180
- nettracer3d/segmenter.py +67 -25
- nettracer3d/segmenter_GPU.py +67 -29
- nettracer3d/stats.py +861 -0
- {nettracer3d-1.0.7.dist-info → nettracer3d-1.0.9.dist-info}/METADATA +6 -4
- {nettracer3d-1.0.7.dist-info → nettracer3d-1.0.9.dist-info}/RECORD +12 -11
- {nettracer3d-1.0.7.dist-info → nettracer3d-1.0.9.dist-info}/WHEEL +0 -0
- {nettracer3d-1.0.7.dist-info → nettracer3d-1.0.9.dist-info}/entry_points.txt +0 -0
- {nettracer3d-1.0.7.dist-info → nettracer3d-1.0.9.dist-info}/licenses/LICENSE +0 -0
- {nettracer3d-1.0.7.dist-info → nettracer3d-1.0.9.dist-info}/top_level.txt +0 -0
nettracer3d/segmenter.py
CHANGED
|
@@ -1181,12 +1181,22 @@ class InteractiveSegmenter:
|
|
|
1181
1181
|
(x[0][2] - curr_x) ** 2))
|
|
1182
1182
|
return nearest[0]
|
|
1183
1183
|
else:
|
|
1184
|
-
# 3D chunks:
|
|
1185
|
-
nearest
|
|
1184
|
+
# 3D chunks: find chunks on nearest Z-plane, then closest in X/Y
|
|
1185
|
+
# First find the nearest Z-plane among available chunks
|
|
1186
|
+
nearest_z = min(unprocessed_chunks,
|
|
1187
|
+
key=lambda x: abs(x[1]['center'][0] - curr_z))[1]['center'][0]
|
|
1188
|
+
|
|
1189
|
+
# Get all chunks on that nearest Z-plane
|
|
1190
|
+
nearest_z_chunks = [chunk for chunk in unprocessed_chunks
|
|
1191
|
+
if chunk[1]['center'][0] == nearest_z]
|
|
1192
|
+
|
|
1193
|
+
# From those chunks, find closest in X/Y
|
|
1194
|
+
nearest = min(nearest_z_chunks,
|
|
1186
1195
|
key=lambda x: sum((a - b) ** 2 for a, b in
|
|
1187
|
-
zip(x[1]['center'], (
|
|
1196
|
+
zip(x[1]['center'][1:], (curr_y, curr_x))))
|
|
1197
|
+
|
|
1188
1198
|
return nearest[0]
|
|
1189
|
-
|
|
1199
|
+
|
|
1190
1200
|
return None
|
|
1191
1201
|
|
|
1192
1202
|
while True:
|
|
@@ -1284,12 +1294,14 @@ class InteractiveSegmenter:
|
|
|
1284
1294
|
|
|
1285
1295
|
return foreground_features, background_features
|
|
1286
1296
|
|
|
1287
|
-
def compute_3d_chunks(self, chunk_size=None):
|
|
1297
|
+
def compute_3d_chunks(self, chunk_size=None, thickness=49):
|
|
1288
1298
|
"""
|
|
1289
|
-
Compute 3D chunks with consistent logic across all operations.
|
|
1299
|
+
Compute 3D chunks as rectangular prisms with consistent logic across all operations.
|
|
1300
|
+
Creates chunks that are thin in Z and square-like in X/Y dimensions.
|
|
1290
1301
|
|
|
1291
1302
|
Args:
|
|
1292
|
-
chunk_size: Optional chunk size, otherwise uses dynamic calculation
|
|
1303
|
+
chunk_size: Optional chunk size for volume calculation, otherwise uses dynamic calculation
|
|
1304
|
+
thickness: Z-dimension thickness of chunks (default: 9)
|
|
1293
1305
|
|
|
1294
1306
|
Returns:
|
|
1295
1307
|
list: List of chunk coordinates [z_start, z_end, y_start, y_end, x_start, x_end]
|
|
@@ -1313,27 +1325,57 @@ class InteractiveSegmenter:
|
|
|
1313
1325
|
except:
|
|
1314
1326
|
depth, height, width, rgb = self.image_3d.shape
|
|
1315
1327
|
|
|
1316
|
-
# Calculate chunk
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1328
|
+
# Calculate target volume per chunk (same as original cube)
|
|
1329
|
+
target_volume = chunk_size ** 3
|
|
1330
|
+
|
|
1331
|
+
# Calculate XY side length based on thickness and target volume
|
|
1332
|
+
# Volume = thickness * xy_side * xy_side
|
|
1333
|
+
# So xy_side = sqrt(volume / thickness)
|
|
1334
|
+
xy_side = int(np.sqrt(target_volume / thickness))
|
|
1335
|
+
xy_side = max(1, xy_side) # Ensure minimum size of 1
|
|
1320
1336
|
|
|
1321
|
-
#
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
np.arange(y_chunks) * chunk_size,
|
|
1325
|
-
np.arange(x_chunks) * chunk_size,
|
|
1326
|
-
indexing='ij'
|
|
1327
|
-
)).reshape(3, -1).T
|
|
1337
|
+
# Calculate actual chunk dimensions for grid calculation
|
|
1338
|
+
z_chunk_size = thickness
|
|
1339
|
+
xy_chunk_size = xy_side
|
|
1328
1340
|
|
|
1329
|
-
#
|
|
1341
|
+
# Calculate number of chunks in each dimension
|
|
1342
|
+
z_chunks = (depth + z_chunk_size - 1) // z_chunk_size
|
|
1343
|
+
y_chunks = (height + xy_chunk_size - 1) // xy_chunk_size
|
|
1344
|
+
x_chunks = (width + xy_chunk_size - 1) // xy_chunk_size
|
|
1345
|
+
|
|
1346
|
+
# Calculate actual chunk sizes to distribute remainder evenly
|
|
1347
|
+
# This ensures all chunks are roughly the same size
|
|
1348
|
+
z_sizes = np.full(z_chunks, depth // z_chunks)
|
|
1349
|
+
z_remainder = depth % z_chunks
|
|
1350
|
+
z_sizes[:z_remainder] += 1
|
|
1351
|
+
|
|
1352
|
+
y_sizes = np.full(y_chunks, height // y_chunks)
|
|
1353
|
+
y_remainder = height % y_chunks
|
|
1354
|
+
y_sizes[:y_remainder] += 1
|
|
1355
|
+
|
|
1356
|
+
x_sizes = np.full(x_chunks, width // x_chunks)
|
|
1357
|
+
x_remainder = width % x_chunks
|
|
1358
|
+
x_sizes[:x_remainder] += 1
|
|
1359
|
+
|
|
1360
|
+
# Calculate cumulative positions
|
|
1361
|
+
z_positions = np.concatenate([[0], np.cumsum(z_sizes)])
|
|
1362
|
+
y_positions = np.concatenate([[0], np.cumsum(y_sizes)])
|
|
1363
|
+
x_positions = np.concatenate([[0], np.cumsum(x_sizes)])
|
|
1364
|
+
|
|
1365
|
+
# Generate all chunk coordinates
|
|
1330
1366
|
chunks = []
|
|
1331
|
-
for
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1367
|
+
for z_idx in range(z_chunks):
|
|
1368
|
+
for y_idx in range(y_chunks):
|
|
1369
|
+
for x_idx in range(x_chunks):
|
|
1370
|
+
z_start = z_positions[z_idx]
|
|
1371
|
+
z_end = z_positions[z_idx + 1]
|
|
1372
|
+
y_start = y_positions[y_idx]
|
|
1373
|
+
y_end = y_positions[y_idx + 1]
|
|
1374
|
+
x_start = x_positions[x_idx]
|
|
1375
|
+
x_end = x_positions[x_idx + 1]
|
|
1376
|
+
|
|
1377
|
+
coords = [z_start, z_end, y_start, y_end, x_start, x_end]
|
|
1378
|
+
chunks.append(coords)
|
|
1337
1379
|
|
|
1338
1380
|
return chunks
|
|
1339
1381
|
|
nettracer3d/segmenter_GPU.py
CHANGED
|
@@ -1055,19 +1055,18 @@ class InteractiveSegmenter:
|
|
|
1055
1055
|
self.realtimechunks = chunk_dict
|
|
1056
1056
|
print("Ready!")
|
|
1057
1057
|
|
|
1058
|
-
def compute_3d_chunks(self, chunk_size=None):
|
|
1058
|
+
def compute_3d_chunks(self, chunk_size=None, thickness=49):
|
|
1059
1059
|
"""
|
|
1060
|
-
Compute 3D chunks with consistent logic across all operations
|
|
1060
|
+
Compute 3D chunks as rectangular prisms with consistent logic across all operations.
|
|
1061
|
+
Creates chunks that are thin in Z and square-like in X/Y dimensions.
|
|
1061
1062
|
|
|
1062
1063
|
Args:
|
|
1063
|
-
chunk_size: Optional chunk size, otherwise uses dynamic calculation
|
|
1064
|
+
chunk_size: Optional chunk size for volume calculation, otherwise uses dynamic calculation
|
|
1065
|
+
thickness: Z-dimension thickness of chunks (default: 9)
|
|
1064
1066
|
|
|
1065
1067
|
Returns:
|
|
1066
1068
|
list: List of chunk coordinates [z_start, z_end, y_start, y_end, x_start, x_end]
|
|
1067
1069
|
"""
|
|
1068
|
-
import cupy as cp
|
|
1069
|
-
import multiprocessing
|
|
1070
|
-
|
|
1071
1070
|
# Use consistent chunk size calculation
|
|
1072
1071
|
if chunk_size is None:
|
|
1073
1072
|
if hasattr(self, 'master_chunk') and self.master_chunk is not None:
|
|
@@ -1075,10 +1074,10 @@ class InteractiveSegmenter:
|
|
|
1075
1074
|
else:
|
|
1076
1075
|
# Dynamic calculation (same as segmentation)
|
|
1077
1076
|
total_cores = multiprocessing.cpu_count()
|
|
1078
|
-
total_volume =
|
|
1077
|
+
total_volume = np.prod(self.image_3d.shape)
|
|
1079
1078
|
target_volume_per_chunk = total_volume / (total_cores * 4)
|
|
1080
1079
|
|
|
1081
|
-
chunk_size = int(
|
|
1080
|
+
chunk_size = int(np.cbrt(target_volume_per_chunk))
|
|
1082
1081
|
chunk_size = max(16, min(chunk_size, min(self.image_3d.shape) // 2))
|
|
1083
1082
|
chunk_size = ((chunk_size + 7) // 16) * 16
|
|
1084
1083
|
|
|
@@ -1087,28 +1086,57 @@ class InteractiveSegmenter:
|
|
|
1087
1086
|
except:
|
|
1088
1087
|
depth, height, width, rgb = self.image_3d.shape
|
|
1089
1088
|
|
|
1090
|
-
# Calculate chunk
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1089
|
+
# Calculate target volume per chunk (same as original cube)
|
|
1090
|
+
target_volume = chunk_size ** 3
|
|
1091
|
+
|
|
1092
|
+
# Calculate XY side length based on thickness and target volume
|
|
1093
|
+
# Volume = thickness * xy_side * xy_side
|
|
1094
|
+
# So xy_side = sqrt(volume / thickness)
|
|
1095
|
+
xy_side = int(np.sqrt(target_volume / thickness))
|
|
1096
|
+
xy_side = max(1, xy_side) # Ensure minimum size of 1
|
|
1097
|
+
|
|
1098
|
+
# Calculate actual chunk dimensions for grid calculation
|
|
1099
|
+
z_chunk_size = thickness
|
|
1100
|
+
xy_chunk_size = xy_side
|
|
1101
|
+
|
|
1102
|
+
# Calculate number of chunks in each dimension
|
|
1103
|
+
z_chunks = (depth + z_chunk_size - 1) // z_chunk_size
|
|
1104
|
+
y_chunks = (height + xy_chunk_size - 1) // xy_chunk_size
|
|
1105
|
+
x_chunks = (width + xy_chunk_size - 1) // xy_chunk_size
|
|
1106
|
+
|
|
1107
|
+
# Calculate actual chunk sizes to distribute remainder evenly
|
|
1108
|
+
# This ensures all chunks are roughly the same size
|
|
1109
|
+
z_sizes = np.full(z_chunks, depth // z_chunks)
|
|
1110
|
+
z_remainder = depth % z_chunks
|
|
1111
|
+
z_sizes[:z_remainder] += 1
|
|
1094
1112
|
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
cp.arange(y_chunks) * chunk_size,
|
|
1099
|
-
cp.arange(x_chunks) * chunk_size,
|
|
1100
|
-
indexing='ij'
|
|
1101
|
-
)).reshape(3, -1).T
|
|
1113
|
+
y_sizes = np.full(y_chunks, height // y_chunks)
|
|
1114
|
+
y_remainder = height % y_chunks
|
|
1115
|
+
y_sizes[:y_remainder] += 1
|
|
1102
1116
|
|
|
1117
|
+
x_sizes = np.full(x_chunks, width // x_chunks)
|
|
1118
|
+
x_remainder = width % x_chunks
|
|
1119
|
+
x_sizes[:x_remainder] += 1
|
|
1103
1120
|
|
|
1104
|
-
#
|
|
1121
|
+
# Calculate cumulative positions
|
|
1122
|
+
z_positions = np.concatenate([[0], np.cumsum(z_sizes)])
|
|
1123
|
+
y_positions = np.concatenate([[0], np.cumsum(y_sizes)])
|
|
1124
|
+
x_positions = np.concatenate([[0], np.cumsum(x_sizes)])
|
|
1125
|
+
|
|
1126
|
+
# Generate all chunk coordinates
|
|
1105
1127
|
chunks = []
|
|
1106
|
-
for
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1128
|
+
for z_idx in range(z_chunks):
|
|
1129
|
+
for y_idx in range(y_chunks):
|
|
1130
|
+
for x_idx in range(x_chunks):
|
|
1131
|
+
z_start = z_positions[z_idx]
|
|
1132
|
+
z_end = z_positions[z_idx + 1]
|
|
1133
|
+
y_start = y_positions[y_idx]
|
|
1134
|
+
y_end = y_positions[y_idx + 1]
|
|
1135
|
+
x_start = x_positions[x_idx]
|
|
1136
|
+
x_end = x_positions[x_idx + 1]
|
|
1137
|
+
|
|
1138
|
+
coords = [z_start, z_end, y_start, y_end, x_start, x_end]
|
|
1139
|
+
chunks.append(coords)
|
|
1112
1140
|
|
|
1113
1141
|
return chunks
|
|
1114
1142
|
|
|
@@ -1196,10 +1224,20 @@ class InteractiveSegmenter:
|
|
|
1196
1224
|
(x[0][2] - curr_x) ** 2))
|
|
1197
1225
|
return nearest[0]
|
|
1198
1226
|
else:
|
|
1199
|
-
# 3D chunks:
|
|
1200
|
-
nearest
|
|
1227
|
+
# 3D chunks: find chunks on nearest Z-plane, then closest in X/Y
|
|
1228
|
+
# First find the nearest Z-plane among available chunks
|
|
1229
|
+
nearest_z = min(unprocessed_chunks,
|
|
1230
|
+
key=lambda x: abs(x[1]['center'][0] - curr_z))[1]['center'][0]
|
|
1231
|
+
|
|
1232
|
+
# Get all chunks on that nearest Z-plane
|
|
1233
|
+
nearest_z_chunks = [chunk for chunk in unprocessed_chunks
|
|
1234
|
+
if chunk[1]['center'][0] == nearest_z]
|
|
1235
|
+
|
|
1236
|
+
# From those chunks, find closest in X/Y
|
|
1237
|
+
nearest = min(nearest_z_chunks,
|
|
1201
1238
|
key=lambda x: sum((a - b) ** 2 for a, b in
|
|
1202
|
-
zip(x[1]['center'], (
|
|
1239
|
+
zip(x[1]['center'][1:], (curr_y, curr_x))))
|
|
1240
|
+
|
|
1203
1241
|
return nearest[0]
|
|
1204
1242
|
|
|
1205
1243
|
return None
|