From bb25a94013694aff1d24e28064f2dd2df19fd333 Mon Sep 17 00:00:00 2001 From: tpietzsch Date: Wed, 25 Sep 2024 13:31:41 +0200 Subject: [PATCH] Speed up FileMapImgLoaderLOCI2 --- .../filemap2/FileMapImgLoaderLOCI2.java | 231 +++++++------- .../filemap2/VirtualRAIFactoryLOCI.java | 287 +++++++++--------- .../VirtualRandomAccessibleIntervalLOCI.java | 158 ---------- ...ltFlatfieldCorrectionWrappedImgLoader.java | 9 +- ...onFlatfieldCorrectionWrappedImgLoader.java | 4 +- 5 files changed, 267 insertions(+), 422 deletions(-) delete mode 100644 src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/filemap2/VirtualRandomAccessibleIntervalLOCI.java diff --git a/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/filemap2/FileMapImgLoaderLOCI2.java b/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/filemap2/FileMapImgLoaderLOCI2.java index c221366d3..1f2cad494 100644 --- a/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/filemap2/FileMapImgLoaderLOCI2.java +++ b/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/filemap2/FileMapImgLoaderLOCI2.java @@ -27,6 +27,9 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.function.Supplier; import com.google.common.io.Files; @@ -35,33 +38,38 @@ import loci.formats.Memoizer; import mpicbg.spim.data.generic.sequence.AbstractSequenceDescription; import mpicbg.spim.data.generic.sequence.BasicViewDescription; +import mpicbg.spim.data.generic.sequence.BasicViewSetup; import mpicbg.spim.data.generic.sequence.ImgLoaderHint; import mpicbg.spim.data.sequence.ImgLoader; import mpicbg.spim.data.sequence.SetupImgLoader; import mpicbg.spim.data.sequence.ViewId; import mpicbg.spim.data.sequence.VoxelDimensions; -import net.imglib2.Cursor; import net.imglib2.Dimensions; -import net.imglib2.RandomAccess; import net.imglib2.RandomAccessible; import net.imglib2.RandomAccessibleInterval; -import net.imglib2.exception.IncompatibleTypeException; +import net.imglib2.cache.Cache; +import net.imglib2.cache.ref.WeakRefLoaderCache; +import net.imglib2.converter.RealTypeConverters; import net.imglib2.type.NativeType; import net.imglib2.type.numeric.RealType; -import net.imglib2.type.numeric.integer.UnsignedShortType; import net.imglib2.type.numeric.real.FloatType; -import net.imglib2.util.Cast; -import net.imglib2.view.Views; +import net.imglib2.util.CloseableThreadLocal; import net.preibisch.mvrecon.fiji.spimdata.imgloaders.util.BioformatsReaderUtils; import util.ImgLib2Tools; public class FileMapImgLoaderLOCI2 implements ImgLoader, FileMapGettable { private final Map< ViewId, FileMapEntry > fileMap; - private final AbstractSequenceDescription sd; - private boolean allTimepointsInSingleFiles; + private final File tempDir; - public boolean zGrouped; + + private final AbstractSequenceDescription< ?, ?, ? > sd; + + final boolean zGrouped; + + private final boolean allTimepointsInSingleFiles; + + private final Map< Integer, SetupImgLoader< ? > > setupImgLoaders = new ConcurrentHashMap<>(); public FileMapImgLoaderLOCI2( final Map< ? extends ViewId, FileMapEntry > fileMap, @@ -74,17 +82,14 @@ public FileMapImgLoaderLOCI2( Map< ? extends ViewId, FileMapEntry > fileMap, final AbstractSequenceDescription< ?, ?, ? > sequenceDescription, final boolean zGrouped ) { - this.fileMap = new HashMap<>(); - this.fileMap.putAll( fileMap ); - + this.fileMap = new HashMap<>( fileMap ); this.tempDir = Files.createTempDir(); - this.sd = sequenceDescription; this.zGrouped = zGrouped; - allTimepointsInSingleFiles = true; // populate map file -> {time points} final Map< File, Set< Integer > > tpsPerFile = new HashMap<>(); + boolean allTimepointsInSingleFiles = true; for ( final Map.Entry< ? extends ViewId, FileMapEntry > entry : fileMap.entrySet() ) { final ViewId vid = entry.getKey(); @@ -99,112 +104,97 @@ public FileMapImgLoaderLOCI2( Map< ? extends ViewId, FileMapEntry > fileMap, break; } } + this.allTimepointsInSingleFiles = allTimepointsInSingleFiles; - System.out.println( allTimepointsInSingleFiles ); - } + this.getReader = () -> { + // use a new ImageReader since we might be loading multi-threaded and BioFormats is not thread-save + // use Memoizer to cache ReaderState for each File on disk + // see: https://www-legacy.openmicroscopy.org/site/support/bio-formats5.1/developers/matlab-dev.html#reader-performance + IFormatReader reader = null; + if ( zGrouped ) + { + final FileStitcher fs = new FileStitcher( true ); + fs.setCanChangePattern( false ); + reader = new Memoizer( fs, Memoizer.DEFAULT_MINIMUM_ELAPSED, tempDir ); + } + else + { + reader = new Memoizer( BioformatsReaderUtils.createImageReaderWithSetupHooks(), Memoizer.DEFAULT_MINIMUM_ELAPSED, tempDir ); + } + return reader; + }; + } @Override - public SetupImgLoader< ? > getSetupImgLoader(int setupId) + public SetupImgLoader< ? > getSetupImgLoader( int setupId ) { - return new FileMapSetupImgLoaderLOCI2<>(setupId); + return setupImgLoaders.computeIfAbsent( setupId, FileMapSetupImgLoaderLOCI2::new ); } - - /* (non-Javadoc) - * @see spim.fiji.spimdata.imgloaders.filemap2.FileMapGettable#getFileMap() - */ @Override public Map< ViewId, FileMapEntry > getFileMap() { return fileMap; } - public class FileMapSetupImgLoaderLOCI2 & NativeType< T >> implements SetupImgLoader< T > + private final Supplier< IFormatReader > getReader; + + public class FileMapSetupImgLoaderLOCI2< T extends RealType< T > & NativeType< T > > implements SetupImgLoader< T > { - private int setupId; + private final int setupId; + + private final CloseableThreadLocal< IFormatReader > threadLocalReader = CloseableThreadLocal.withInitial( getReader ); + + private final Cache< Integer, RandomAccessibleInterval< T > > images = new WeakRefLoaderCache< Integer, RandomAccessibleInterval< T > >().withLoader( this::createImage ); - public FileMapSetupImgLoaderLOCI2(int setupId) + private final Supplier< T > type; + + public FileMapSetupImgLoaderLOCI2( int setupId ) { this.setupId = setupId; + this.type = lazyInit( () -> { + final BasicViewDescription< ? > aVd = getAnyPresentViewDescriptionForViewSetup( sd, setupId ); + if ( aVd == null ) + return null; + final FileMapEntry entry = fileMap.get( aVd ); + return VirtualRAIFactoryLOCI.getType( threadLocalReader::get, entry.file(), entry.series() ); + } ); } - private IFormatReader getReader() + private RandomAccessibleInterval< T > createImage( final int timepointId ) { - // use a new ImageReader since we might be loading multi-threaded and BioFormats is not thread-save - // use Memoizer to cache ReaderState for each File on disk - // see: https://www-legacy.openmicroscopy.org/site/support/bio-formats5.1/developers/matlab-dev.html#reader-performance - IFormatReader reader = null; - if (zGrouped) - { - final FileStitcher fs = new FileStitcher(true); - fs.setCanChangePattern( false ); - reader = new Memoizer( fs , Memoizer.DEFAULT_MINIMUM_ELAPSED, tempDir); - } - else - { - reader = new Memoizer( BioformatsReaderUtils.createImageReaderWithSetupHooks(), Memoizer.DEFAULT_MINIMUM_ELAPSED, tempDir ); - } - - return reader; + final FileMapEntry entry = fileMap.get( new ViewId( timepointId, setupId ) ); + return VirtualRAIFactoryLOCI.createVirtualCached( + threadLocalReader::get, + entry.file(), + entry.series(), + entry.channel(), + allTimepointsInSingleFiles ? 0 : timepointId ); } @Override - public RandomAccessibleInterval< T > getImage( final int timepointId, final ImgLoaderHint... hints) + public RandomAccessibleInterval< T > getImage( final int timepointId, final ImgLoaderHint... hints ) { - final BasicViewDescription< ? > vd = sd.getViewDescriptions().get( new ViewId( timepointId, setupId ) ); - final FileMapEntry imageSource = fileMap.get( vd ); - - // TODO: some logging here? (reading angle .. , tp .., ... from file ...) - - final Dimensions size = vd.getViewSetup().getSize(); - - final IFormatReader reader = getReader(); - - RandomAccessibleInterval< T > img = null; try { - img = Cast.unchecked( new VirtualRAIFactoryLOCI().createVirtualCached( - reader, imageSource.file(), imageSource.series(), - imageSource.channel(), allTimepointsInSingleFiles ? 0 : timepointId, new UnsignedShortType(), size ) ); + return images.get( timepointId ); } - catch ( IncompatibleTypeException e ) + catch ( ExecutionException e ) { - e.printStackTrace(); + throw new RuntimeException( e ); } - - return img; } @Override public T getImageType() { - return (T) new UnsignedShortType(); - - /* - final BasicViewDescription< ? > aVd = getAnyPresentViewDescriptionForViewSetup( sd, setupId ); - final Pair< File, Pair< Integer, Integer > > aPair = fileMap.get( aVd ); - - final IFormatReader reader = getReader(); - VirtualRAIFactoryLOCI.setReaderFileAndSeriesIfNecessary( reader, aPair.getA(), aPair.getB().getA() ); - - if (reader.getPixelType() == FormatTools.UINT8) - return (T) new UnsignedByteType(); - else if (reader.getPixelType() == FormatTools.UINT16) - return (T) new UnsignedShortType(); - else if (reader.getPixelType() == FormatTools.INT16) - return (T) new ShortType(); - else if (reader.getPixelType() == FormatTools.UINT32) - return (T) new UnsignedIntType(); - else if (reader.getPixelType() == FormatTools.FLOAT) - return (T) new FloatType(); - return null; - */ + return type.get(); } @Override - public RandomAccessibleInterval< FloatType > getFloatImage(int timepointId, boolean normalize, - ImgLoaderHint... hints) + public RandomAccessibleInterval< FloatType > getFloatImage( int timepointId, boolean normalize, + ImgLoaderHint... hints ) { if ( normalize ) return ImgLib2Tools.normalizeVirtualRAI( getImage( timepointId, hints ) ); @@ -213,52 +203,69 @@ public RandomAccessibleInterval< FloatType > getFloatImage(int timepointId, bool } @Override - public Dimensions getImageSize(int timepointId) + public Dimensions getImageSize( int timepointId ) { // NB: in all current uses we should have size information in the sd - BasicViewDescription< ? > vd = sd.getViewDescriptions().get( new ViewId( timepointId, setupId ) ); - return vd.getViewSetup().getSize(); + return getViewSetup( timepointId ).getSize(); } @Override - public VoxelDimensions getVoxelSize(int timepointId) + public VoxelDimensions getVoxelSize( int timepointId ) { // NB: in all current uses we should have size information in the sd - BasicViewDescription< ? > vd = sd.getViewDescriptions().get( new ViewId( timepointId, setupId ) ); - return vd.getViewSetup().getVoxelSize(); + return getViewSetup( timepointId ).getVoxelSize(); } - } - - /** - * copy src to dest - * @param src : source, will not be modified - * @param dest : destiantion, will be modified - * @param pixel type source - * @param pixel type destination - */ - public static , S extends RealType> void copy(RandomAccessible< T > src, RandomAccessibleInterval< S > dest) - { - final Cursor< S > destCursor = Views.iterable( dest ).localizingCursor(); - final RandomAccess< T > srcRA = src.randomAccess(); - - while (destCursor.hasNext()) + private BasicViewSetup getViewSetup( int timepointId ) { - destCursor.fwd(); - srcRA.setPosition( destCursor ); - destCursor.get().setReal( srcRA.get().getRealDouble() ); + BasicViewDescription< ? > vd = sd.getViewDescriptions().get( new ViewId( timepointId, setupId ) ); + return vd.getViewSetup(); } - } - public static BasicViewDescription< ? > getAnyPresentViewDescriptionForViewSetup(AbstractSequenceDescription< ?, ?, ? > sd, int viewSetupId) + private static BasicViewDescription< ? > getAnyPresentViewDescriptionForViewSetup( AbstractSequenceDescription< ?, ?, ? > sd, int viewSetupId ) { - for (final ViewId vid : sd.getViewDescriptions().keySet()) - if (vid.getViewSetupId() == viewSetupId) - if (!sd.getMissingViews().getMissingViews().contains( vid )) + for ( final ViewId vid : sd.getViewDescriptions().keySet() ) + if ( vid.getViewSetupId() == viewSetupId ) + if ( !sd.getMissingViews().getMissingViews().contains( vid ) ) return sd.getViewDescriptions().get( vid ); return null; } + private static < T > Supplier< T > lazyInit( final Supplier< T > supplier ) + { + return new Supplier< T >() + { + T value = null; + + @Override + public synchronized T get() + { + if ( value == null ) + value = supplier.get(); + return value; + } + }; + } + + /** + * copy src to dest + * + * @deprecated Use {@link RealTypeConverters#copyFromTo(RandomAccessible, RandomAccessibleInterval)}. + * + * @param src + * source, will not be modified + * @param dest + * destiantion, will be modified + * @param + * pixel type source + * @param + * pixel type destination + */ + @Deprecated + public static < T extends RealType< T >, S extends RealType< S > > void copy( RandomAccessible< T > src, RandomAccessibleInterval< S > dest ) + { + RealTypeConverters.copyFromTo( src, dest ); + } } diff --git a/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/filemap2/VirtualRAIFactoryLOCI.java b/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/filemap2/VirtualRAIFactoryLOCI.java index f92dcf33a..fb4a8d56b 100644 --- a/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/filemap2/VirtualRAIFactoryLOCI.java +++ b/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/filemap2/VirtualRAIFactoryLOCI.java @@ -9,12 +9,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 2 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -22,142 +22,192 @@ */ package net.preibisch.mvrecon.fiji.spimdata.imgloaders.filemap2; +import static java.nio.ByteOrder.BIG_ENDIAN; +import static java.nio.ByteOrder.LITTLE_ENDIAN; +import static net.imglib2.cache.img.ReadOnlyCachedCellImgOptions.options; + import java.io.File; import java.io.IOException; -import java.util.Objects; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; +import java.util.function.Supplier; import loci.formats.AxisGuesser; import loci.formats.FileStitcher; import loci.formats.FormatException; import loci.formats.FormatTools; import loci.formats.IFormatReader; -import loci.formats.ImageReader; import loci.formats.Memoizer; -import net.imglib2.Dimensions; import net.imglib2.RandomAccessibleInterval; +import net.imglib2.cache.img.ReadOnlyCachedCellImgFactory; import net.imglib2.exception.IncompatibleTypeException; -import net.imglib2.img.Img; -import net.imglib2.img.cell.CellImgFactory; -import net.imglib2.img.display.imagej.ImageJFunctions; import net.imglib2.type.NativeType; import net.imglib2.type.numeric.RealType; import net.imglib2.type.numeric.integer.ShortType; import net.imglib2.type.numeric.integer.UnsignedByteType; import net.imglib2.type.numeric.integer.UnsignedIntType; import net.imglib2.type.numeric.integer.UnsignedShortType; -import net.imglib2.type.numeric.real.DoubleType; import net.imglib2.type.numeric.real.FloatType; -import net.imglib2.view.Views; -import net.preibisch.mvrecon.fiji.spimdata.imgloaders.LegacyStackImgLoaderLOCI; -import net.preibisch.mvrecon.process.fusion.FusionTools; +import net.imglib2.util.Cast; -public class VirtualRAIFactoryLOCI +class VirtualRAIFactoryLOCI { - @FunctionalInterface - interface TriConsumer + static < T extends RealType< T > & NativeType< T > > T getType( + final Supplier< IFormatReader > threadLocalReader, + final File file, + final int series ) { - void accept(A a, B b, C c); - - default TriConsumer< A, B, C > andThen(TriConsumer< ? super A, ? super B, ? super C > after) + try { - Objects.requireNonNull( after ); + final IFormatReader reader = threadLocalReader.get(); + setReaderFileAndSeriesIfNecessary( reader, file, series ); - return (a, b, c) -> { - accept( a, b, c ); - after.accept( a, b, c ); - }; + final int pixelType = reader.getPixelType(); + switch ( pixelType ) + { + case FormatTools.UINT8: + return Cast.unchecked( new UnsignedByteType() ); + case FormatTools.UINT16: + return Cast.unchecked( new UnsignedShortType() ); + case FormatTools.INT16: + return Cast.unchecked( new ShortType() ); + case FormatTools.UINT32: + return Cast.unchecked( new UnsignedIntType() ); + case FormatTools.FLOAT: + return Cast.unchecked( new FloatType() ); + default: + return null; + } + } + catch ( IOException | FormatException e ) + { + throw new RuntimeException( e ); } } - - @SuppressWarnings("unchecked") - public & NativeType< T >> RandomAccessibleInterval< T > createVirtual( - final IFormatReader reader, + + static < T extends RealType< T > & NativeType< T > > RandomAccessibleInterval< T > createVirtualCached( + final Supplier threadLocalReader, final File file, final int series, final int channel, - final int timepoint, - T type, - Dimensions dim) throws IncompatibleTypeException + final int timepoint ) throws IncompatibleTypeException { - setReaderFileAndSeriesIfNecessary( reader, file, series ); + final IFormatReader reader = threadLocalReader.get(); + try + { + setReaderFileAndSeriesIfNecessary( reader, file, series ); + } + catch ( IOException | FormatException e ) + { + throw new RuntimeException( e ); + } - final boolean isLittleEndian = reader.isLittleEndian(); - final long[] dims = new long[]{reader.getSizeX(), reader.getSizeY(), reader.getSizeZ()}; + final long[] dims = { reader.getSizeX(), reader.getSizeY(), reader.getSizeZ() }; + final int[] cellDims = { ( int ) dims[ 0 ], ( int ) dims[ 1 ], 1 }; + final ReadOnlyCachedCellImgFactory factory = new ReadOnlyCachedCellImgFactory( options().cellDimensions( cellDims ) ); - if (dim != null) - dim.dimensions( dims ); + final ByteOrder byteOrder = reader.isLittleEndian() ? LITTLE_ENDIAN : BIG_ENDIAN; final int pixelType = reader.getPixelType(); - if (pixelType == FormatTools.UINT8) - return new VirtualRandomAccessibleIntervalLOCI< T >( reader, file, dims, series, channel, timepoint, type == null ? (T) new UnsignedByteType() : type, (t, buf, i) -> {t.setReal( (int) buf[i] & 0xff);} ); - else if (pixelType == FormatTools.UINT16) - return new VirtualRandomAccessibleIntervalLOCI< T >( reader, file, dims, series, channel, timepoint, type == null ? (T) new UnsignedShortType() : type, (t, buf, i) -> {t.setReal( LegacyStackImgLoaderLOCI.getShortValueInt( buf, i*2, isLittleEndian ) );} ); - else if (pixelType == FormatTools.INT16) - return new VirtualRandomAccessibleIntervalLOCI< T >( reader, file, dims, series, channel, timepoint, type == null ? (T) new ShortType() : type, (t, buf, i) -> {t.setReal( LegacyStackImgLoaderLOCI.getShortValue( buf, i*2, isLittleEndian ) );} ); - else if (pixelType == FormatTools.UINT32) - return new VirtualRandomAccessibleIntervalLOCI< T >( reader, file, dims, series, channel, timepoint, type == null ? (T) new UnsignedIntType() : type, (t, buf, i) -> {t.setReal( LegacyStackImgLoaderLOCI.getIntValue( buf, i*4, isLittleEndian ) );} ); - else if (pixelType == FormatTools.FLOAT) - return new VirtualRandomAccessibleIntervalLOCI< T >( reader, file, dims, series, channel, timepoint, type == null ? (T) new FloatType() : type, (t, buf, i) -> {t.setReal( LegacyStackImgLoaderLOCI.getFloatValue( buf, i*4, isLittleEndian ) );} ); - else - throw new IncompatibleTypeException( this, "cannot create virtual image for this pixel type" ); + switch ( pixelType ) + { + case FormatTools.UINT8: + return Cast.unchecked( factory.create( dims, new UnsignedByteType(), + cell -> { + final int z = ( int ) cell.min( 2 ); + final ByteBuffer bytes = readIntoBuffer( threadLocalReader.get(), file, series, channel, timepoint, z ); + bytes.position( 0 ); + bytes.get( ( byte[] ) cell.getStorageArray() ); + } ) ); + case FormatTools.UINT16: + return Cast.unchecked( factory.create( dims, new UnsignedShortType(), + cell -> { + final int z = ( int ) cell.min( 2 ); + final ByteBuffer bytes = readIntoBuffer( threadLocalReader.get(), file, series, channel, timepoint, z ); + final ShortBuffer shorts = bytes.order( byteOrder ).asShortBuffer(); + shorts.position( 0 ); + shorts.get( ( short[] ) cell.getStorageArray() ); + } ) ); + case FormatTools.INT16: + return Cast.unchecked( factory.create( dims, new ShortType(), + cell -> { + final int z = ( int ) cell.min( 2 ); + final ByteBuffer bytes = readIntoBuffer( threadLocalReader.get(), file, series, channel, timepoint, z ); + final ShortBuffer shorts = bytes.order( byteOrder ).asShortBuffer(); + shorts.position( 0 ); + shorts.get( ( short[] ) cell.getStorageArray() ); + } ) ); + case FormatTools.UINT32: + return Cast.unchecked( factory.create( dims, new UnsignedIntType(), + cell -> { + final int z = ( int ) cell.min( 2 ); + final ByteBuffer bytes = readIntoBuffer( threadLocalReader.get(), file, series, channel, timepoint, z ); + final IntBuffer ints = bytes.order( byteOrder ).asIntBuffer(); + ints.position( 0 ); + ints.get( ( int[] ) cell.getStorageArray() ); + } ) ); + case FormatTools.FLOAT: + return Cast.unchecked( factory.create( dims, new FloatType(), + cell -> { + final int z = ( int ) cell.min( 2 ); + final ByteBuffer bytes = readIntoBuffer( threadLocalReader.get(), file, series, channel, timepoint, z ); + final FloatBuffer floats = bytes.order( byteOrder ).asFloatBuffer(); + floats.position( 0 ); + floats.get( ( float[] ) cell.getStorageArray() ); + } ) ); + default: + throw new IncompatibleTypeException( new VirtualRAIFactoryLOCI(), "cannot create virtual image for this pixel type: " + pixelType ); + } } - - @SuppressWarnings("unchecked") - public synchronized & NativeType< T >> RandomAccessibleInterval< T > createVirtualCached( + + private static ByteBuffer readIntoBuffer( final IFormatReader reader, final File file, final int series, final int channel, final int timepoint, - T type, - Dimensions dim) throws IncompatibleTypeException + final int z ) throws IOException, FormatException { setReaderFileAndSeriesIfNecessary( reader, file, series ); +// System.out.println( "reading z plane " + z + " from series " + series + " in file " + file.getAbsolutePath() ); - final boolean isLittleEndian = reader.isLittleEndian(); - final long[] dims = new long[]{reader.getSizeX(), reader.getSizeY(), reader.getSizeZ()}; + final int planeSize = ( reader.getBitsPerPixel() / 8 ) * reader.getSizeX() * reader.getSizeY(); + final int size = planeSize * reader.getRGBChannelCount(); + final byte[] buffer = new byte[ size ]; - if (dim != null) - dim.dimensions( dims ); + // FIX for XYZ <-> XYT mixup in rare cases + final boolean flipTAndZ = !reader.isOrderCertain() && reader.getSizeZ() <= 1 && reader.getSizeT() > 1; + final int actualTP = flipTAndZ ? z : timepoint; + final int actualZ = flipTAndZ ? timepoint : z; - final int pixelType = reader.getPixelType(); - - if (pixelType == FormatTools.UINT8) - { - RandomAccessibleInterval< T > virtualImg = new VirtualRandomAccessibleIntervalLOCI< T >( reader, file, dims, series, channel, timepoint, type == null ? (T) new UnsignedByteType() : type, (t, buf, i) -> {t.setReal( (int) buf[i] & 0xff);} ); - return FusionTools.cacheRandomAccessibleInterval( virtualImg, Integer.MAX_VALUE, type == null ? (T) new UnsignedByteType() : type, new int[] {(int)virtualImg.dimension( 0 ), (int)virtualImg.dimension( 1 ), 1} ) ; - } - else if (pixelType == FormatTools.UINT16) - { - RandomAccessibleInterval< T > virtualImg = new VirtualRandomAccessibleIntervalLOCI< T >( reader, file, dims, series, channel, timepoint, type == null ? (T) new UnsignedShortType() : type, (t, buf, i) -> {t.setReal( LegacyStackImgLoaderLOCI.getShortValueInt( buf, i*2, isLittleEndian ) );} ); - return FusionTools.cacheRandomAccessibleInterval( virtualImg, Integer.MAX_VALUE, type == null ? (T) new UnsignedShortType() : type, new int[] {(int)virtualImg.dimension( 0 ), (int)virtualImg.dimension( 1 ), 1} ) ; - } - else if (pixelType == FormatTools.INT16) + final int rgbOffset; + if ( reader.getRGBChannelCount() == reader.getSizeC() ) { - RandomAccessibleInterval< T > virtualImg = new VirtualRandomAccessibleIntervalLOCI< T >( reader, file, dims, series, channel, timepoint, type == null ? (T) new ShortType() : type, (t, buf, i) -> {t.setReal( LegacyStackImgLoaderLOCI.getShortValue( buf, i*2, isLittleEndian ) );} ); - return FusionTools.cacheRandomAccessibleInterval( virtualImg, Integer.MAX_VALUE, type == null ? (T) new ShortType() : type, new int[] {(int)virtualImg.dimension( 0 ), (int)virtualImg.dimension( 1 ), 1} ) ; + // the image is RGB -> we have to read bytes for all channels at once? + reader.openBytes( reader.getIndex( actualZ, 0, actualTP ), buffer ); + rgbOffset = channel * planeSize; } - else if (pixelType == FormatTools.UINT32) - { - RandomAccessibleInterval< T > virtualImg = new VirtualRandomAccessibleIntervalLOCI< T >( reader, file, dims, series, channel, timepoint, type == null ? (T) new UnsignedIntType() : type, (t, buf, i) -> {t.setReal( LegacyStackImgLoaderLOCI.getIntValue( buf, i*4, isLittleEndian ) );} ); - return FusionTools.cacheRandomAccessibleInterval( virtualImg, Integer.MAX_VALUE, type == null ? (T) new UnsignedIntType() : type, new int[] {(int)virtualImg.dimension( 0 ), (int)virtualImg.dimension( 1 ), 1} ) ; - } - else if (pixelType == FormatTools.FLOAT) + else { - RandomAccessibleInterval< T > virtualImg = new VirtualRandomAccessibleIntervalLOCI< T >( reader, file, dims, series, channel, timepoint, type == null ? (T) new FloatType() : type, (t, buf, i) -> {t.setReal( LegacyStackImgLoaderLOCI.getFloatValue( buf, i*4, isLittleEndian ) );} ); - return FusionTools.cacheRandomAccessibleInterval( virtualImg, Integer.MAX_VALUE, type == null ? (T) new FloatType() : type, new int[] {(int)virtualImg.dimension( 0 ), (int)virtualImg.dimension( 1 ), 1} ) ; + // normal image -> read specified channel + reader.openBytes( reader.getIndex( actualZ, channel, actualTP ), buffer ); + rgbOffset = 0; } - else - throw new IncompatibleTypeException( this, "cannot create virtual image for this pixel type: " + pixelType ); + + return ByteBuffer.wrap( buffer, rgbOffset, planeSize ); } - + /** * ensure that the reader we have is set to the correct file and series * @param reader the reader * @param file the file to point the reader to * @param series the series in the file to point the reader to */ - public static void setReaderFileAndSeriesIfNecessary(final IFormatReader reader, final File file, final int series) + private static void setReaderFileAndSeriesIfNecessary(final IFormatReader reader, final File file, final int series) + throws IOException, FormatException { final boolean isFileStitcher = FileStitcher.class.isInstance( ( (Memoizer) reader).getReader() ); @@ -169,81 +219,28 @@ public static void setReaderFileAndSeriesIfNecessary(final IFormatReader reader, // FIXME: this would probably crash anyway (also for normal readers) as we setId while reader is not closed // but the way we call it, we never have to re-setID for the reader // TODO: investigate - if (!isFileStitcher) + if ( !isFileStitcher ) { // is the reader set to the right file? // we check the canonical path of the file, otherwise something /./ would lead to setId being called // again even though the correct file is set already - if (!haveToReadFile) - try { haveToReadFile |= !(new File(reader.getCurrentFile()).getCanonicalPath().equals( file.getCanonicalPath() ) ); } - catch (IOException e) { return; } + if ( !haveToReadFile ) + haveToReadFile = !( new File( reader.getCurrentFile() ).getCanonicalPath().equals( file.getCanonicalPath() ) ); } - if (haveToReadFile) + if ( haveToReadFile ) { - try - { - reader.setId( file.getAbsolutePath() ); + reader.close( ); + reader.setId( file.getAbsolutePath() ); - if ( isFileStitcher ) - ( (FileStitcher) ( (Memoizer) reader).getReader() ).setAxisTypes( new int[] {AxisGuesser.Z_AXIS} ); - } - catch ( FormatException | IOException e ) - { - e.printStackTrace(); - return; - } + if ( isFileStitcher ) + ( ( FileStitcher ) ( ( Memoizer ) reader ).getReader() ).setAxisTypes( new int[] { AxisGuesser.Z_AXIS } ); reader.setSeries( series ); } else { - if (reader.getSeries() != series) + if ( reader.getSeries() != series ) reader.setSeries( series ); } } - - /** check if the given reader is set to the given file and series - * - * @param reader the reader - * @param file the file - * @param series the series - * @return true or false - */ - public static boolean checkReaderFileAndSeries(final IFormatReader reader, final File file, final int series) - { - // if we have a fileStitcher, do not check the actual file name - final boolean isFileStitcher = FileStitcher.class.isInstance( ( (Memoizer) reader).getReader() ); - if (isFileStitcher) - if (reader.getCurrentFile() == null) - return false; - else - return reader.getSeries() == series; - - if (reader.getCurrentFile() == null || !reader.getCurrentFile().equals( file.getAbsolutePath() )) - return false; - else - return reader.getSeries() == series; - } - - public static & NativeType< T > > void main(String[] args) - { - RandomAccessibleInterval< T > img = null; - ImageReader reader = new ImageReader(); - try - { - img = new VirtualRAIFactoryLOCI().createVirtualCached( reader, new File( "/Users/david/Desktop/2ch2ill2angle.czi" ), 0, 2, 0 , (T) new DoubleType(), null); - } - catch ( IncompatibleTypeException e ) - { - e.printStackTrace(); - } - - Img< T > create = new CellImgFactory().create( img, Views.iterable( img ).firstElement().createVariable() ); - - - - System.out.println( Views.iterable( img ).firstElement().getClass()); - ImageJFunctions.show( img, "BDV" ); - System.out.println( reader.getModuloC().step ); - } } diff --git a/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/filemap2/VirtualRandomAccessibleIntervalLOCI.java b/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/filemap2/VirtualRandomAccessibleIntervalLOCI.java deleted file mode 100644 index b58463b4d..000000000 --- a/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/filemap2/VirtualRandomAccessibleIntervalLOCI.java +++ /dev/null @@ -1,158 +0,0 @@ -/*- - * #%L - * Software for the reconstruction of multi-view microscopic acquisitions - * like Selective Plane Illumination Microscopy (SPIM) Data. - * %% - * Copyright (C) 2012 - 2024 Multiview Reconstruction developers. - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ -package net.preibisch.mvrecon.fiji.spimdata.imgloaders.filemap2; - -import java.io.File; -import java.io.IOException; - -import loci.formats.FormatException; -import loci.formats.IFormatReader; -import net.imglib2.AbstractInterval; -import net.imglib2.Interval; -import net.imglib2.Point; -import net.imglib2.RandomAccess; -import net.imglib2.RandomAccessibleInterval; -import net.imglib2.Sampler; -import net.imglib2.type.NativeType; -import net.imglib2.type.numeric.RealType; -import net.preibisch.mvrecon.fiji.spimdata.imgloaders.filemap2.VirtualRAIFactoryLOCI.TriConsumer; - -class VirtualRandomAccessibleIntervalLOCI & NativeType< T >> extends AbstractInterval - implements RandomAccessibleInterval< T > -{ - private final IFormatReader reader; - private final File file; - private final int series; - private final int channel; - private final int timepoint; - private final T type; - private final TriConsumer< T, byte[], Integer > byteConverter; - - VirtualRandomAccessibleIntervalLOCI(IFormatReader reader, File file, long[] dims, int series, int channel, - int timepoint, T type, final TriConsumer< T, byte[], Integer > byteConverter) - { - super( dims ); - this.reader = reader; - this.file = file; - this.series = series; - this.channel = channel; - this.timepoint = timepoint; - this.type = type; - this.byteConverter = byteConverter; - } - - @Override - public RandomAccess< T > randomAccess() - { - return new VirtualRandomAccessLOCI(); - } - - @Override - public RandomAccess< T > randomAccess(Interval interval) - { - return randomAccess(); - } - - @Override - public T getType() - { - return type; - } - - private class VirtualRandomAccessLOCI extends Point implements RandomAccess< T > - { - - private byte[] buffer; - private T type; - private int currentZ = -1; - - private VirtualRandomAccessLOCI() - { - super( 3 ); - this.type = VirtualRandomAccessibleIntervalLOCI.this.type.createVariable(); - buffer = new byte[0]; - - } - - private void readIntoBuffer() - { - - VirtualRAIFactoryLOCI.setReaderFileAndSeriesIfNecessary( reader, file, series ); - - int siz = reader.getBitsPerPixel() / 8 * reader.getRGBChannelCount() * reader.getSizeX() - * reader.getSizeY(); - buffer = new byte[siz]; - -// System.out.println( "reading z plane " + position[2] + " from series " + series + " in file " + file.getAbsolutePath() ); - - // FIX for XYZ <-> XYT mixup in rare cases - int actualTP = (!reader.isOrderCertain() && reader.getSizeZ() <= 1 && reader.getSizeT() > 1 ) ? (int) position[2] : timepoint; - int actualZ = (!reader.isOrderCertain() && reader.getSizeZ() <= 1 && reader.getSizeT() > 1 ) ? timepoint : (int) position[2]; - - try - { - // the image is RGB -> we have to read bytes for all channels at once? - if (reader.getRGBChannelCount() == reader.getSizeC()) - reader.openBytes( reader.getIndex( actualZ, 0, actualTP), buffer ); - // normal image -> read specified channel - else - reader.openBytes( reader.getIndex( actualZ, channel, actualTP), buffer ); - } - catch ( FormatException | IOException e ) - { - e.printStackTrace(); - } - } - - @Override - public T get() - { - // prevent multithreaded overwriting of buffer - synchronized ( reader ) - { - if ( position[2] != currentZ || !VirtualRAIFactoryLOCI.checkReaderFileAndSeries( reader, file, series )) - { - currentZ = (int) position[2]; - readIntoBuffer(); - } - - int rgbOffset = 0; - if (reader.getRGBChannelCount() == reader.getSizeC()) - rgbOffset = channel * buffer.length / reader.getSizeC(); - - // pixel index (we do not care about bytesPerPixel here, byteCOnverter should take care of that) - final int i = (int) (rgbOffset + position[0] + position[1] * VirtualRandomAccessibleIntervalLOCI.this.dimension( 0 ) ); - byteConverter.accept( type, buffer, i ); - return this.type; - } - } - - @Override - public RandomAccess< T > copy() - { - return new VirtualRandomAccessLOCI(); - } - - } - -} diff --git a/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/flatfield/DefaultFlatfieldCorrectionWrappedImgLoader.java b/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/flatfield/DefaultFlatfieldCorrectionWrappedImgLoader.java index 6c9d4dd98..b44efe3fd 100644 --- a/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/flatfield/DefaultFlatfieldCorrectionWrappedImgLoader.java +++ b/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/flatfield/DefaultFlatfieldCorrectionWrappedImgLoader.java @@ -29,14 +29,13 @@ import mpicbg.spim.data.generic.sequence.ImgLoaderHint; import mpicbg.spim.data.generic.sequence.ImgLoaderHints; import mpicbg.spim.data.sequence.ImgLoader; -import mpicbg.spim.data.sequence.SequenceDescription; import mpicbg.spim.data.sequence.SetupImgLoader; -import mpicbg.spim.data.sequence.ViewDescription; import mpicbg.spim.data.sequence.ViewId; -import mpicbg.spim.data.sequence.ViewSetup; import mpicbg.spim.data.sequence.VoxelDimensions; import net.imglib2.Dimensions; +import net.imglib2.RandomAccessible; import net.imglib2.RandomAccessibleInterval; +import net.imglib2.converter.RealTypeConverters; import net.imglib2.img.Img; import net.imglib2.img.ImgFactory; import net.imglib2.img.array.ArrayImgFactory; @@ -46,10 +45,8 @@ import net.imglib2.type.numeric.RealType; import net.imglib2.type.numeric.real.FloatType; import net.imglib2.view.Views; -import net.preibisch.mvrecon.fiji.plugin.queryXML.GenericLoadParseQueryXML; import net.preibisch.mvrecon.fiji.plugin.queryXML.LoadParseQueryXML; import net.preibisch.mvrecon.fiji.spimdata.SpimData2; -import net.preibisch.mvrecon.fiji.spimdata.XmlIoSpimData2; import net.preibisch.mvrecon.fiji.spimdata.imgloaders.filemap2.FileMapImgLoaderLOCI2; import net.preibisch.mvrecon.process.fusion.FusionTools; @@ -140,7 +137,7 @@ public RandomAccessibleInterval< T > getImage(int timepointId, ImgLoaderHint... imgFactory = new CellImgFactory(); Img< T > loadedImg = imgFactory.create( rai, getImageType() ); - FileMapImgLoaderLOCI2.copy(Views.extendZero( rai ), loadedImg); + RealTypeConverters.copyFromTo( Views.extendZero( rai ), loadedImg ); rai = loadedImg; } diff --git a/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/flatfield/MultiResolutionFlatfieldCorrectionWrappedImgLoader.java b/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/flatfield/MultiResolutionFlatfieldCorrectionWrappedImgLoader.java index 76703cf05..47989bc20 100644 --- a/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/flatfield/MultiResolutionFlatfieldCorrectionWrappedImgLoader.java +++ b/src/main/java/net/preibisch/mvrecon/fiji/spimdata/imgloaders/flatfield/MultiResolutionFlatfieldCorrectionWrappedImgLoader.java @@ -41,7 +41,9 @@ import net.imglib2.Dimensions; import net.imglib2.FinalDimensions; import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; import net.imglib2.RandomAccessibleInterval; +import net.imglib2.converter.RealTypeConverters; import net.imglib2.img.Img; import net.imglib2.img.ImgFactory; import net.imglib2.img.array.ArrayImgFactory; @@ -220,7 +222,7 @@ public RandomAccessibleInterval< T > getImage(int timepointId, int level, ImgLoa imgFactory = new CellImgFactory(); Img< T > loadedImg = imgFactory.create( rai, getImageType() ); - FileMapImgLoaderLOCI2.copy(Views.extendZero( rai ), loadedImg); + RealTypeConverters.copyFromTo( Views.extendZero( rai ), loadedImg ); rai = loadedImg; }