@@ -4415,22 +4415,28 @@ public void loadSecondaryImage(final File file) throws IOException, IllegalArgum
44154415 // Falls back to the conventional path for anything it can't resolve
44164416 // (resolvePathToSource() itself ends in a conventional ImgPlus-open fallback too, but keeping
44174417 // our own fallback here avoids any behavior change for formats that already worked before).
4418- RandomAccessibleInterval <?> data = null ;
4418+ // Resolution and dimension-checking are deliberately two separate steps here: only a failure to
4419+ // resolve/parse the source at all should fall back to the conventional (RAM-unsafe-for-big-data)
4420+ // path below. A dimension mismatch on an already-resolved big-data source must NOT fall back to it
4421+ Object resolvedSource = null ;
44194422 try {
4420- data = extractSecondaryData ( SpimDataUtils .resolvePathToSource (file .getAbsolutePath () ));
4423+ resolvedSource = SpimDataUtils .resolvePathToSource (file .getAbsolutePath ());
44214424 } catch (final Exception e ) {
44224425 SNTUtils .log ("Secondary layer: '" + file .getName () + "' could not be resolved as a big-data "
44234426 + "container (" + e .getMessage () + "); falling back to conventional image opening" );
44244427 }
4425- if (data != null ) {
4426- // computeStatistics=false: data here may be a lazily-backed (N5/Zarr/BDV) source, and eager
4427- // min/max/mean/stdDev via generic OpService calls is 3 separate full-volume passes -- real
4428- // disk I/O per chunk, done 3x over, with no caching between passes. That may work for the
4429- // classic ImagePlus but not for lazy data.
4430- //noinspection unchecked,rawtypes
4431- loadSecondaryImage ((RandomAccessibleInterval ) data , true , false );
4432- setSecondaryImage (isSecondaryDataAvailable () ? file : null );
4433- return ;
4428+ if (resolvedSource != null ) {
4429+ final RandomAccessibleInterval <?> data = extractSecondaryData (resolvedSource );
4430+ if (data != null ) {
4431+ // computeStatistics=false: data here may be a lazily-backed (N5/Zarr/BDV) source, and eager
4432+ // min/max/mean/stdDev via generic OpService calls is 3 separate full-volume passes -- real
4433+ // disk I/O per chunk, done 3x over, with no caching between passes. That may work for the
4434+ // classic ImagePlus but not for lazy data.
4435+ //noinspection unchecked,rawtypes
4436+ loadSecondaryImage ((RandomAccessibleInterval ) data , true , false );
4437+ setSecondaryImage (isSecondaryDataAvailable () ? file : null );
4438+ return ;
4439+ }
44344440 }
44354441 final ImagePlus imp = openCachedDataImage (file );
44364442 loadSecondaryImage (imp , true );
@@ -4440,14 +4446,24 @@ public void loadSecondaryImage(final File file) throws IOException, IllegalArgum
44404446 /**
44414447 * Extracts a {@link RandomAccessibleInterval} for the currently active channel/frame from a
44424448 * resolved big-data source (see {@link SpimDataUtils#resolvePathToSource(String)}), or
4443- * {@code null} if {@code source} isn't one of the recognized types, or its XYZ dimensions don't
4444- * match the data currently being traced (see {@link #sameXYZDimensionsAsTracingData}).
4449+ * {@code null} if {@code source} isn't one of the recognized types (the caller, {@link
4450+ * #loadSecondaryImage(File)}, then falls back to conventional image opening).
4451+ * <p>
4452+ * If {@code source} <i>is</i> one of the recognized types but its XYZ dimensions don't match the
4453+ * data currently being traced (see {@link #sameXYZDimensionsAsTracingData}), this throws instead
4454+ * of returning {@code null}: unlike a genuinely unresolved source, this data was already
4455+ * successfully resolved as big data, so silently falling back to the conventional path would mean
4456+ * fully loading a potentially huge dataset into RAM via SCIFIO/Bio-Formats just to discover its
4457+ * dimensions are wrong. Failing fast here, with the same message {@link #openCachedDataImage(File)}
4458+ * gives for the same condition, avoids that.
44454459 * <p>
44464460 * For {@link AbstractSpimData} (BDV .xml/IMS) and {@link SpimDataUtils.N5Sources} (ambiguous
44474461 * N5/Zarr layouts), only the first setup/source and timepoint 0 are used. This is the same
44484462 * simplification {@code BigDataLoaderCmd#applyFallbackCalibration} already makes for the
44494463 * <i>primary</i> volume in this same situation. A genuinely multichannel secondary layer of
44504464 * either of these types would thus only expose its first channel.
4465+ *
4466+ * @throws IllegalArgumentException if the resolved source's dimensions don't match the data currently being traced
44514467 */
44524468 private RandomAccessibleInterval <?> extractSecondaryData (final Object source ) {
44534469 final RandomAccessibleInterval <?> data ;
@@ -4473,9 +4489,10 @@ private RandomAccessibleInterval<?> extractSecondaryData(final Object source) {
44734489 return null ; // unresolved / unrecognized type
44744490 }
44754491 }
4476- if (!sameXYZDimensionsAsTracingData (data )) {
4477- SNTUtils .log ("Secondary layer: dimensions do not match those of the data being traced" );
4478- return null ;
4492+ if (!sameXYZDimensionsAsTracingData (data )) { // NB: throws rather than returning null
4493+ final String msg = (isStreamMode ())
4494+ ? "" : " If this is unexpected, check under 'Image>Properties...' that CZT axes are not swapped." ;
4495+ throw new IllegalArgumentException ("Dimensions do not match those of the image being traced." + msg );
44794496 }
44804497 return data ;
44814498 }
@@ -4673,9 +4690,9 @@ private ImagePlus openCachedDataImage(final File file) throws IOException {
46734690 if (!sameXYZDimensionsAsTracingData (imp )) {
46744691 // We are imposing only XYZ dimensions to e.g., allow for loading of single-channel
46754692 // p-maps. Hopefully, being lax about CT dimensions won't cause issues downstream
4676- final String refName = (xy != null ) ? xy . getTitle () : "the image being traced" ;
4677- throw new IllegalArgumentException ( "Dimensions do not match those of " + refName
4678- + ". If this is unexpected, check under 'Image>Properties...' that CZT axes are not swapped." );
4693+ final String msg = (isStreamMode ())
4694+ ? "" : " If this is unexpected, check under 'Image>Properties...' that CZT axes are not swapped." ;
4695+ throw new IllegalArgumentException ( "Dimensions do not match those of the image being traced." + msg );
46794696 }
46804697 return imp ;
46814698 }
0 commit comments