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/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: use existing center-based distance calculation
1185
- nearest = min(unprocessed_chunks,
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'], (curr_z, curr_y, curr_x))))
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 grid dimensions
1317
- z_chunks = (depth + chunk_size - 1) // chunk_size
1318
- y_chunks = (height + chunk_size - 1) // chunk_size
1319
- x_chunks = (width + chunk_size - 1) // chunk_size
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
- # Generate all chunk start positions
1322
- chunk_starts = np.array(np.meshgrid(
1323
- np.arange(z_chunks) * chunk_size,
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
- # Create chunk coordinate list
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 z_start, y_start, x_start in chunk_starts:
1332
- z_end = min(z_start + chunk_size, depth)
1333
- y_end = min(y_start + chunk_size, height)
1334
- x_end = min(x_start + chunk_size, width)
1335
- coords = [z_start, z_end, y_start, y_end, x_start, x_end]
1336
- chunks.append(coords)
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
 
@@ -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 (GPU version).
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 = cp.prod(cp.array(self.image_3d.shape))
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(cp.cbrt(target_volume_per_chunk))
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 grid dimensions
1091
- z_chunks = (depth + chunk_size - 1) // chunk_size
1092
- y_chunks = (height + chunk_size - 1) // chunk_size
1093
- x_chunks = (width + chunk_size - 1) // chunk_size
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
- # Generate all chunk start positions using CuPy
1096
- chunk_starts = cp.array(cp.meshgrid(
1097
- cp.arange(z_chunks) * chunk_size,
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
- # Create chunk coordinate list
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 z_start, y_start, x_start in chunk_starts:
1107
- z_end = min(z_start + chunk_size, depth)
1108
- y_end = min(y_start + chunk_size, height)
1109
- x_end = min(x_start + chunk_size, width)
1110
- coords = [int(z_start), int(z_end), int(y_start), int(y_end), int(x_start), int(x_end)]
1111
- chunks.append(coords)
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: use existing center-based distance calculation
1200
- nearest = min(unprocessed_chunks,
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'], (curr_z, curr_y, curr_x))))
1239
+ zip(x[1]['center'][1:], (curr_y, curr_x))))
1240
+
1203
1241
  return nearest[0]
1204
1242
 
1205
1243
  return None