@@ -312,10 +312,101 @@ def parse_args():
312312 help = "Select specific file from glob patterns by index (e.g., '0', '-1') or filename (e.g., 'volume_001'). "
313313 "Default: '0' (first file). Use 'all' to load all files." ,
314314 )
315+ parser .add_argument (
316+ "--bbox" ,
317+ type = str ,
318+ default = None ,
319+ help = (
320+ "Crop all loaded volumes to a spatial bounding box before visualization. "
321+ "Format: 'zmin,ymin,xmin,zmax,ymax,xmax' (Python slicing, end-exclusive)."
322+ ),
323+ )
315324
316325 return parser .parse_args ()
317326
318327
328+ def parse_bbox_arg (bbox_str : str ) -> Tuple [int , int , int , int , int , int ]:
329+ """Parse bbox string 'zmin,ymin,xmin,zmax,ymax,xmax' into integer coordinates."""
330+ parts = [p .strip () for p in bbox_str .split ("," )]
331+ if len (parts ) != 6 :
332+ raise ValueError (
333+ "bbox must have 6 comma-separated integers: zmin,ymin,xmin,zmax,ymax,xmax"
334+ )
335+
336+ try :
337+ zmin , ymin , xmin , zmax , ymax , xmax = (int (p ) for p in parts )
338+ except ValueError as e :
339+ raise ValueError (
340+ "bbox values must be integers in format zmin,ymin,xmin,zmax,ymax,xmax"
341+ ) from e
342+
343+ if min (zmin , ymin , xmin , zmax , ymax , xmax ) < 0 :
344+ raise ValueError ("bbox values must be non-negative" )
345+ if not (zmin < zmax and ymin < ymax and xmin < xmax ):
346+ raise ValueError ("bbox min values must be strictly less than max values" )
347+
348+ return zmin , ymin , xmin , zmax , ymax , xmax
349+
350+
351+ def crop_volumes_to_bbox (
352+ volumes : Dict [str , Tuple ],
353+ bbox : Tuple [int , int , int , int , int , int ],
354+ default_offset : Tuple [int , int , int ],
355+ ) -> Dict [str , Tuple ]:
356+ """
357+ Crop all volumes to the same bbox and update voxel offsets accordingly.
358+
359+ Args:
360+ volumes: Mapping of volume names to (data, type[, resolution, offset]) tuples.
361+ bbox: (zmin, ymin, xmin, zmax, ymax, xmax), end-exclusive.
362+ default_offset: Fallback offset used when a volume has no explicit offset.
363+
364+ Returns:
365+ New volume mapping with cropped arrays and adjusted offsets.
366+ """
367+ zmin , ymin , xmin , zmax , ymax , xmax = bbox
368+ cropped_volumes : Dict [str , Tuple ] = {}
369+
370+ print (f"\n Applying bbox crop to all volumes: { bbox } (end-exclusive)" )
371+
372+ for name , vol_data in volumes .items ():
373+ if len (vol_data ) == 2 :
374+ data , vol_type = vol_data
375+ vol_resolution = None
376+ vol_offset = None
377+ else :
378+ data , vol_type , vol_resolution , vol_offset = vol_data
379+
380+ if data .ndim == 3 :
381+ spatial_shape = data .shape
382+ crop_slices = (slice (zmin , zmax ), slice (ymin , ymax ), slice (xmin , xmax ))
383+ elif data .ndim == 4 :
384+ spatial_shape = data .shape [- 3 :]
385+ crop_slices = (slice (None ), slice (zmin , zmax ), slice (ymin , ymax ), slice (xmin , xmax ))
386+ else :
387+ raise ValueError (
388+ f"Volume '{ name } ' has unsupported ndim={ data .ndim } for bbox cropping (expected 3D or 4D)"
389+ )
390+
391+ if not (
392+ 0 <= zmin < zmax <= spatial_shape [0 ]
393+ and 0 <= ymin < ymax <= spatial_shape [1 ]
394+ and 0 <= xmin < xmax <= spatial_shape [2 ]
395+ ):
396+ raise ValueError (
397+ f"bbox { bbox } is out of bounds for volume '{ name } ' spatial shape { spatial_shape } "
398+ )
399+
400+ cropped_data = data [crop_slices ]
401+ base_offset = vol_offset if vol_offset is not None else default_offset
402+ new_offset = (base_offset [0 ] + zmin , base_offset [1 ] + ymin , base_offset [2 ] + xmin )
403+
404+ print (f" { name } : { data .shape } -> { cropped_data .shape } , offset { base_offset } -> { new_offset } " )
405+ cropped_volumes [name ] = (cropped_data , vol_type , vol_resolution , new_offset )
406+
407+ return cropped_volumes
408+
409+
319410def load_volumes_from_config (
320411 config_path : str , mode : str = "train" , prediction_base_name : Optional [str ] = None ,
321412 select : str = "0"
@@ -1102,6 +1193,14 @@ def main():
11021193 print (f"ERROR: Invalid offset format '{ args .offset } '. Expected format: 'z-y-x' or 'y-x' for 2D (e.g., '0-0-0' or '0-0')" )
11031194 sys .exit (1 )
11041195
1196+ if args .bbox :
1197+ try :
1198+ bbox = parse_bbox_arg (args .bbox )
1199+ volumes = crop_volumes_to_bbox (volumes , bbox , default_offset = offset )
1200+ except ValueError as e :
1201+ print (f"ERROR: Invalid bbox '{ args .bbox } ': { e } " )
1202+ sys .exit (1 )
1203+
11051204 # Start visualization (returns viewer for interactive access)
11061205 viewer = visualize_volumes (
11071206 volumes = volumes ,
0 commit comments