@@ -227,6 +227,19 @@ public NodeCollector<T> setLabelProvider(final Function<SNTPoint, String> labelP
227227 return this ;
228228 }
229229
230+ /**
231+ * Returns the crop centered on a single point, respecting the current
232+ * {@link #setMaterialize(boolean)}, {@link #setProjection(Projection)},
233+ * {@link #setChannel(int)}, and {@link #setWindow(int, int)} settings.
234+ * <p>
235+ * Prefer this over {@link #getCrops()} when processing large datasets one
236+ * point at a time (e.g. to keep peak memory bounded to a single crop)
237+ * </p>
238+ */
239+ public ImgPlus <T > getCrop (final SNTPoint point ) {
240+ return cropAround (point );
241+ }
242+
230243 /**
231244 * Returns the per-point crops as a list of {@link ImgPlus}es.
232245 * <p>
@@ -362,9 +375,20 @@ private ImgPlus<T> zeroPaddedCrop(final ImgPlus<T> spatial,
362375 max [zIdx ] = bMax [2 ];
363376 }
364377
365- // Zero-padded view: extendZero gives the zero value of T outside the
366- // image, Views.interval then carves out exactly the requested window
367- final RandomAccessibleInterval <T > view = Views .interval (Views .extendZero (spatial ), new FinalInterval (min , max ));
378+ // Check whether the requested window is fully within the source image bounds.
379+ // If so, skip Views.extendZero: a plain Views.interval preserves the source's
380+ // cell-aware flat iterator (critical for CellImg / HDF5 sources. w/ extendZero
381+ // it seems per-pixel RandomAccess is used, which is extremely slow
382+ boolean inBounds = true ;
383+ for (int d = 0 ; d < nDims ; d ++) {
384+ if (min [d ] < spatial .min (d ) || max [d ] > spatial .max (d )) {
385+ inBounds = false ;
386+ break ;
387+ }
388+ }
389+ final RandomAccessibleInterval <T > view = inBounds
390+ ? Views .interval (spatial , new FinalInterval (min , max ))
391+ : Views .interval (Views .extendZero (spatial ), new FinalInterval (min , max ));
368392
369393 final RandomAccessibleInterval <T > rai ;
370394 if (materialize ) {
0 commit comments