diff --git a/pom.xml b/pom.xml index 2b5112dab45ce3b373e2aaa9d4d4e18b54b8fc4f..227489f7f909dd09808929d390e8a47f867f5adf 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ <dependency> <groupId>sc.fiji</groupId> <artifactId>bigdataviewer-core</artifactId> + <version>1.0.3-SNAPSHOT</version> </dependency> <dependency> <groupId>net.imagej</groupId> diff --git a/src/main/java/bdv/ij/ExportImagePlusPlugIn.java b/src/main/java/bdv/ij/ExportImagePlusPlugIn.java index 6a09940bef4659902a1d7029fd45bdd2b9117ed4..4b9510b83540a20ad170dd0738021a73d3431253 100644 --- a/src/main/java/bdv/ij/ExportImagePlusPlugIn.java +++ b/src/main/java/bdv/ij/ExportImagePlusPlugIn.java @@ -19,7 +19,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Map; -import mpicbg.spim.data.generic.sequence.BasicImgLoader; import mpicbg.spim.data.generic.sequence.BasicViewSetup; import mpicbg.spim.data.registration.ViewRegistration; import mpicbg.spim.data.registration.ViewRegistrations; @@ -28,13 +27,15 @@ import mpicbg.spim.data.sequence.FinalVoxelDimensions; import mpicbg.spim.data.sequence.TimePoint; import mpicbg.spim.data.sequence.TimePoints; import net.imglib2.FinalDimensions; +import net.imglib2.RandomAccessibleInterval; import net.imglib2.realtransform.AffineTransform3D; -import net.imglib2.type.numeric.integer.UnsignedShortType; import bdv.export.ExportMipmapInfo; import bdv.export.ProgressWriter; import bdv.export.ProposeMipmaps; import bdv.export.SubTaskProgressWriter; import bdv.export.WriteSequenceToHdf5; +import bdv.export.WriteSequenceToHdf5.AfterEachPlane; +import bdv.export.WriteSequenceToHdf5.LoopbackHeuristic; import bdv.ij.export.imgloader.ImagePlusImgLoader; import bdv.ij.export.imgloader.ImagePlusImgLoader.MinMaxOption; import bdv.ij.util.PluginHelper; @@ -116,7 +117,7 @@ public class ExportImagePlusPlugIn implements PlugIn progressWriter.out().println( "starting export..." ); // create ImgLoader wrapping the image - final BasicImgLoader< UnsignedShortType > imgLoader; + final ImagePlusImgLoader< ? > imgLoader; switch ( imp.getType() ) { case ImagePlus.GRAY8: @@ -157,6 +158,57 @@ public class ExportImagePlusPlugIn implements PlugIn for ( final BasicViewSetup setup : seq.getViewSetupsOrdered() ) perSetupExportMipmapInfo.put( setup.getId(), mipmapInfo ); + // LoopBackHeuristic: + // - If saving more than 8x on pixel reads use the loopback image over + // original image + // - For virtual stacks also consider the cache size that would be + // required for all original planes contributing to a "plane of + // blocks" at the current level. If this is more than 1/4 of + // available memory, use the loopback image. + final boolean isVirtual = imp.getStack().isVirtual(); + final long planeSizeInBytes = imp.getWidth() * imp.getHeight() * imp.getBytesPerPixel(); + final long ijMaxMemory = IJ.maxMemory(); + final LoopbackHeuristic loopbackHeuristic = new LoopbackHeuristic() + { + @Override + public boolean decide( final RandomAccessibleInterval< ? > originalImg, final int[] factorsToOriginalImg, final int previousLevel, final int[] factorsToPreviousLevel, final int[] chunkSize ) + { + if ( previousLevel < 0 ) + return false; + + if ( WriteSequenceToHdf5.numElements( factorsToOriginalImg ) / WriteSequenceToHdf5.numElements( factorsToPreviousLevel ) >= 8 ) + return true; + + if ( isVirtual ) + { + final long requiredCacheSize = planeSizeInBytes * factorsToOriginalImg[ 2 ] * chunkSize[ 2 ]; + if ( requiredCacheSize > ijMaxMemory / 4 ) + return true; + } + + return false; + } + }; + + final AfterEachPlane afterEachPlane = new AfterEachPlane() + { + @Override + public void afterEachPlane( final boolean usedLoopBack ) + { + if ( !usedLoopBack && isVirtual ) + { + final long free = Runtime.getRuntime().freeMemory(); + final long total = Runtime.getRuntime().totalMemory(); + final long max = Runtime.getRuntime().maxMemory(); + final long actuallyFree = max - total + free; + + if ( actuallyFree < max / 2 ) + imgLoader.clearCache(); + } + } + + }; + final ArrayList< Partition > partitions; if ( params.split ) { @@ -168,14 +220,14 @@ public class ExportImagePlusPlugIn implements PlugIn { final Partition partition = partitions.get( i ); final ProgressWriter p = new SubTaskProgressWriter( progressWriter, 0, 0.95 * i / partitions.size() ); - WriteSequenceToHdf5.writeHdf5PartitionFile( seq, perSetupExportMipmapInfo, params.deflate, partition, p ); + WriteSequenceToHdf5.writeHdf5PartitionFile( seq, perSetupExportMipmapInfo, params.deflate, partition, loopbackHeuristic, afterEachPlane, p ); } WriteSequenceToHdf5.writeHdf5PartitionLinkFile( seq, perSetupExportMipmapInfo, partitions, params.hdf5File ); } else { partitions = null; - WriteSequenceToHdf5.writeHdf5File( seq, perSetupExportMipmapInfo, params.deflate, params.hdf5File, new SubTaskProgressWriter( progressWriter, 0, 0.95 ) ); + WriteSequenceToHdf5.writeHdf5File( seq, perSetupExportMipmapInfo, params.deflate, params.hdf5File, loopbackHeuristic, afterEachPlane, new SubTaskProgressWriter( progressWriter, 0, 0.95 ) ); } // write xml sequence description diff --git a/src/main/java/bdv/ij/ExportSpimFusionPlugIn.java b/src/main/java/bdv/ij/ExportSpimFusionPlugIn.java index 6f86470732fd1f4b9fda6e2a778a3130888d47ce..7fa3a509a512cdac21ba39cf0fb02e90e874f28c 100644 --- a/src/main/java/bdv/ij/ExportSpimFusionPlugIn.java +++ b/src/main/java/bdv/ij/ExportSpimFusionPlugIn.java @@ -294,7 +294,7 @@ public class ExportSpimFusionPlugIn implements PlugIn for ( final Partition partition : newPartitions ) { final SubTaskProgressWriter subtaskProgress = new SubTaskProgressWriter( progress, complete, complete + completionStep ); - WriteSequenceToHdf5.writeHdf5PartitionFile( fusionSeq, perSetupExportMipmapInfo, params.deflate, partition, subtaskProgress ); + WriteSequenceToHdf5.writeHdf5PartitionFile( fusionSeq, perSetupExportMipmapInfo, params.deflate, partition, null, null, subtaskProgress ); complete += completionStep; } @@ -346,13 +346,13 @@ public class ExportSpimFusionPlugIn implements PlugIn { final Partition partition = partitions.get( i ); final ProgressWriter p = new SubTaskProgressWriter( progress, 0, 0.95 * i / partitions.size() ); - WriteSequenceToHdf5.writeHdf5PartitionFile( desc, perSetupExportMipmapInfo, params.deflate, partition, p ); + WriteSequenceToHdf5.writeHdf5PartitionFile( desc, perSetupExportMipmapInfo, params.deflate, partition, null, null, p ); } WriteSequenceToHdf5.writeHdf5PartitionLinkFile( desc, perSetupExportMipmapInfo, partitions, params.hdf5File ); } else { - WriteSequenceToHdf5.writeHdf5File( desc, perSetupExportMipmapInfo, params.deflate, params.hdf5File, new SubTaskProgressWriter( progress, 0, 0.95 ) ); + WriteSequenceToHdf5.writeHdf5File( desc, perSetupExportMipmapInfo, params.deflate, params.hdf5File, null, null, new SubTaskProgressWriter( progress, 0, 0.95 ) ); } // write xml file diff --git a/src/main/java/bdv/ij/ExportSpimSequencePlugIn.java b/src/main/java/bdv/ij/ExportSpimSequencePlugIn.java index 7b1e2e514c86ddc68ab337bd9c91ad630e3ce673..7a1747b9270f7564a8f58b456ddb13ed02a5ff3a 100644 --- a/src/main/java/bdv/ij/ExportSpimSequencePlugIn.java +++ b/src/main/java/bdv/ij/ExportSpimSequencePlugIn.java @@ -86,14 +86,14 @@ public class ExportSpimSequencePlugIn implements PlugIn { final Partition partition = partitions.get( i ); final ProgressWriter p = new SubTaskProgressWriter( progress, 0, 0.95 * i / partitions.size() ); - WriteSequenceToHdf5.writeHdf5PartitionFile( desc, perSetupExportMipmapInfo, params.deflate, partition, p ); + WriteSequenceToHdf5.writeHdf5PartitionFile( desc, perSetupExportMipmapInfo, params.deflate, partition, null, null, p ); } WriteSequenceToHdf5.writeHdf5PartitionLinkFile( desc, perSetupExportMipmapInfo, partitions, params.hdf5File ); } else { partitions = null; - WriteSequenceToHdf5.writeHdf5File( desc, perSetupExportMipmapInfo, params.deflate, params.hdf5File, new SubTaskProgressWriter( progress, 0, 0.95 ) ); + WriteSequenceToHdf5.writeHdf5File( desc, perSetupExportMipmapInfo, params.deflate, params.hdf5File, null, null, new SubTaskProgressWriter( progress, 0, 0.95 ) ); } final Hdf5ImageLoader loader = new Hdf5ImageLoader( params.hdf5File, partitions, null, false ); diff --git a/src/main/java/bdv/ij/OpenImagePlusPlugIn.java b/src/main/java/bdv/ij/OpenImagePlusPlugIn.java new file mode 100644 index 0000000000000000000000000000000000000000..2dd8282bc724b1268edec23e2efca1a182f0c818 --- /dev/null +++ b/src/main/java/bdv/ij/OpenImagePlusPlugIn.java @@ -0,0 +1,222 @@ +package bdv.ij; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import bdv.BigDataViewer; +import bdv.ij.util.ProgressWriterIJ; +import bdv.img.imagestack.ImageStackImageLoader; +import bdv.img.virtualstack.VirtualStackImageLoader; +import bdv.spimdata.SequenceDescriptionMinimal; +import bdv.spimdata.SpimDataMinimal; +import bdv.spimdata.WrapBasicImgLoader; +import bdv.tools.brightness.ConverterSetup; +import bdv.tools.brightness.SetupAssignments; +import bdv.viewer.DisplayMode; +import bdv.viewer.VisibilityAndGrouping; +import ij.CompositeImage; +import ij.IJ; +import ij.ImageJ; +import ij.ImagePlus; +import ij.WindowManager; +import ij.plugin.PlugIn; +import ij.process.LUT; +import mpicbg.spim.data.SpimDataException; +import mpicbg.spim.data.generic.sequence.BasicImgLoader; +import mpicbg.spim.data.generic.sequence.BasicViewSetup; +import mpicbg.spim.data.registration.ViewRegistration; +import mpicbg.spim.data.registration.ViewRegistrations; +import mpicbg.spim.data.sequence.Channel; +import mpicbg.spim.data.sequence.FinalVoxelDimensions; +import mpicbg.spim.data.sequence.TimePoint; +import mpicbg.spim.data.sequence.TimePoints; +import net.imglib2.FinalDimensions; +import net.imglib2.realtransform.AffineTransform3D; +import net.imglib2.type.numeric.ARGBType; + +/** + * ImageJ plugin to show the current image in BigDataViewer. + * + * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> + */ +public class OpenImagePlusPlugIn implements PlugIn +{ + public static void main( final String[] args ) + { + System.setProperty( "apple.laf.useScreenMenuBar", "true" ); + new ImageJ(); + IJ.run("Confocal Series (2.2MB)"); +// IJ.run("Fly Brain (1MB)"); + new OpenImagePlusPlugIn().run( null ); + } + + @Override + public void run( final String arg ) + { + // get the current image + final ImagePlus imp = WindowManager.getCurrentImage(); + + // make sure there is one + if ( imp == null ) + { + IJ.showMessage( "Please open an image first." ); + return; + } + + // check the image type + switch ( imp.getType() ) + { + case ImagePlus.GRAY8: + case ImagePlus.GRAY16: + case ImagePlus.GRAY32: + case ImagePlus.COLOR_RGB: + break; + default: + IJ.showMessage( "Only 8, 16, 32-bit images and RGB images are supported currently!" ); + return; + } + + // check the image dimensionality + if ( imp.getNDimensions() < 3 ) + { + IJ.showMessage( "Image must be at least 3-dimensional!" ); + return; + } + + // get calibration and image size + final double pw = imp.getCalibration().pixelWidth; + final double ph = imp.getCalibration().pixelHeight; + final double pd = imp.getCalibration().pixelDepth; + String punit = imp.getCalibration().getUnit(); + if ( punit == null || punit.isEmpty() ) + punit = "px"; + final FinalVoxelDimensions voxelSize = new FinalVoxelDimensions( punit, pw, ph, pd ); + final int w = imp.getWidth(); + final int h = imp.getHeight(); + final int d = imp.getNSlices(); + final FinalDimensions size = new FinalDimensions( new int[] { w, h, d } ); + + // propose reasonable mipmap settings +// final ExportMipmapInfo autoMipmapSettings = ProposeMipmaps.proposeMipmaps( new BasicViewSetup( 0, "", size, voxelSize ) ); + +// imp.getDisplayRangeMin(); +// imp.getDisplayRangeMax(); + + // create ImgLoader wrapping the image + final BasicImgLoader< ? > imgLoader; + if ( imp.getStack().isVirtual() ) + { + switch ( imp.getType() ) + { + case ImagePlus.GRAY8: + imgLoader = VirtualStackImageLoader.createUnsignedByteInstance( imp ); + break; + case ImagePlus.GRAY16: + imgLoader = VirtualStackImageLoader.createUnsignedShortInstance( imp ); + break; + case ImagePlus.GRAY32: + imgLoader = VirtualStackImageLoader.createFloatInstance( imp ); + break; + case ImagePlus.COLOR_RGB: + default: + imgLoader = VirtualStackImageLoader.createARGBInstance( imp ); + break; + } + } + else + { + switch ( imp.getType() ) + { + case ImagePlus.GRAY8: + imgLoader = ImageStackImageLoader.createUnsignedByteInstance( imp ); + break; + case ImagePlus.GRAY16: + imgLoader = ImageStackImageLoader.createUnsignedShortInstance( imp ); + break; + case ImagePlus.GRAY32: + imgLoader = ImageStackImageLoader.createFloatInstance( imp ); + break; + case ImagePlus.COLOR_RGB: + default: + imgLoader = ImageStackImageLoader.createARGBInstance( imp ); + break; + } + } + + final int numTimepoints = imp.getNFrames(); + final int numSetups = imp.getNChannels(); + + // create setups from channels + final HashMap< Integer, BasicViewSetup > setups = new HashMap< Integer, BasicViewSetup >( numSetups ); + for ( int s = 0; s < numSetups; ++s ) + { + final BasicViewSetup setup = new BasicViewSetup( s, String.format( "channel %d", s + 1 ), size, voxelSize ); + setup.setAttribute( new Channel( s + 1 ) ); + setups.put( s, setup ); + } + + // create timepoints + final ArrayList< TimePoint > timepoints = new ArrayList< TimePoint >( numTimepoints ); + for ( int t = 0; t < numTimepoints; ++t ) + timepoints.add( new TimePoint( t ) ); + final SequenceDescriptionMinimal seq = new SequenceDescriptionMinimal( new TimePoints( timepoints ), setups, imgLoader, null ); + + // create ViewRegistrations from the images calibration + final AffineTransform3D sourceTransform = new AffineTransform3D(); + sourceTransform.set( pw, 0, 0, 0, 0, ph, 0, 0, 0, 0, pd, 0 ); + final ArrayList< ViewRegistration > registrations = new ArrayList< ViewRegistration >(); + for ( int t = 0; t < numTimepoints; ++t ) + for ( int s = 0; s < numSetups; ++s ) + registrations.add( new ViewRegistration( t, s, sourceTransform ) ); + + final File basePath = new File("."); + final SpimDataMinimal spimData = new SpimDataMinimal( basePath, seq, new ViewRegistrations( registrations ) ); + WrapBasicImgLoader.wrapImgLoaderIfNecessary( spimData ); + + try + { + final BigDataViewer bdv = new BigDataViewer( spimData, "BigDataViewer", new ProgressWriterIJ() ); + final SetupAssignments sa = bdv.getSetupAssignments(); + final VisibilityAndGrouping vg = bdv.getViewer().getVisibilityAndGrouping(); + if ( imp.isComposite() ) + transferChannelSettings( ( CompositeImage ) imp, sa, vg ); + else if ( imp.getType() == ImagePlus.COLOR_RGB ) + transferSettingsRGB( imp, sa ); + } + catch ( final SpimDataException e ) + { + throw new RuntimeException( e ); + } + } + + protected void transferChannelSettings( final CompositeImage ci, final SetupAssignments setupAssignments, final VisibilityAndGrouping visibility ) + { + final int nChannels = ci.getNChannels(); + final int mode = ci.getCompositeMode(); + final boolean transferColor = mode == IJ.COMPOSITE || mode == IJ.COLOR; + for ( int c = 0; c < nChannels; ++c ) + { + final LUT lut = ci.getChannelLut( c + 1 ); + final ConverterSetup setup = setupAssignments.getConverterSetups().get( c ); + if ( transferColor ) + setup.setColor( new ARGBType( lut.getRGB( 255 ) ) ); + setup.setDisplayRange( lut.min, lut.max ); + } + if ( mode == IJ.COMPOSITE ) + { + final boolean[] activeChannels = ci.getActiveChannels(); + visibility.setDisplayMode( DisplayMode.FUSED ); + for ( int i = 0; i < activeChannels.length; ++i ) + visibility.setSourceActive( i, activeChannels[ i ] ); + } + else + visibility.setDisplayMode( DisplayMode.SINGLE ); + visibility.setCurrentSource( ci.getChannel() - 1 ); + } + + protected void transferSettingsRGB( final ImagePlus imp, final SetupAssignments setupAssignments ) + { + final ConverterSetup setup = setupAssignments.getConverterSetups().get( 0 ); + setup.setDisplayRange( imp.getDisplayRangeMin(), imp.getDisplayRangeMax() ); + } +} diff --git a/src/main/java/bdv/ij/export/Scripting.java b/src/main/java/bdv/ij/export/Scripting.java index 38ac2431bda86ae493f2c3325424dafe2a3aaadc..dc8f190cc3d9493e024aa9d7525a76b1730069b7 100644 --- a/src/main/java/bdv/ij/export/Scripting.java +++ b/src/main/java/bdv/ij/export/Scripting.java @@ -203,7 +203,7 @@ public class Scripting public void writePartition( final int index ) { if ( index >= 0 && index < partitions.size() ) - WriteSequenceToHdf5.writeHdf5PartitionFile( spimData.getSequenceDescription(), perSetupMipmapInfo, deflate, partitions.get( index ), null ); + WriteSequenceToHdf5.writeHdf5PartitionFile( spimData.getSequenceDescription(), perSetupMipmapInfo, deflate, partitions.get( index ), null, null, null ); } public void writeXmlAndLinks() throws SpimDataException diff --git a/src/main/java/bdv/ij/export/imgloader/ImagePlusImgLoader.java b/src/main/java/bdv/ij/export/imgloader/ImagePlusImgLoader.java index 2535261b90ff6159f26f84f41873d59ecd913fb3..fbefc4ad89bcf786835780ca1350b1ac408e9a93 100644 --- a/src/main/java/bdv/ij/export/imgloader/ImagePlusImgLoader.java +++ b/src/main/java/bdv/ij/export/imgloader/ImagePlusImgLoader.java @@ -12,6 +12,8 @@ import net.imglib2.type.numeric.RealType; import net.imglib2.type.numeric.integer.UnsignedByteType; import net.imglib2.type.numeric.integer.UnsignedShortType; import net.imglib2.type.numeric.real.FloatType; +import bdv.img.cache.VolatileGlobalCellCache; +import bdv.img.imagestack.ImageStackImageLoader; import bdv.img.virtualstack.VirtualStackImageLoader; /** @@ -40,33 +42,45 @@ public class ImagePlusImgLoader< T extends RealType< T > & NativeType< T > > imp { if( imp.getType() != ImagePlus.GRAY8 ) throw new RuntimeException( "expected ImagePlus type GRAY8" ); - return new ImagePlusImgLoader< UnsignedByteType >( imp, VirtualStackImageLoader.createUnsignedByteInstance( imp ), minMaxOption, min, max ); + if ( imp.getStack() != null && imp.getStack().isVirtual() ) + return new ImagePlusImgLoader< UnsignedByteType >( imp, VirtualStackImageLoader.createUnsignedByteInstance( imp ), minMaxOption, min, max ); + else + return new ImagePlusImgLoader< UnsignedByteType >( imp, ImageStackImageLoader.createUnsignedByteInstance( imp ), minMaxOption, min, max ); } public static ImagePlusImgLoader< UnsignedShortType > createGray16( final ImagePlus imp, final MinMaxOption minMaxOption, final double min, final double max ) { if( imp.getType() != ImagePlus.GRAY16 ) throw new RuntimeException( "expected ImagePlus type GRAY16" ); - return new ImagePlusImgLoader< UnsignedShortType >( imp, VirtualStackImageLoader.createUnsignedShortInstance( imp ), minMaxOption, min, max ); + if ( imp.getStack() != null && imp.getStack().isVirtual() ) + return new ImagePlusImgLoader< UnsignedShortType >( imp, VirtualStackImageLoader.createUnsignedShortInstance( imp ), minMaxOption, min, max ); + else + return new ImagePlusImgLoader< UnsignedShortType >( imp, ImageStackImageLoader.createUnsignedShortInstance( imp ), minMaxOption, min, max ); } public static ImagePlusImgLoader< FloatType > createGray32( final ImagePlus imp, final MinMaxOption minMaxOption, final double min, final double max ) { if( imp.getType() != ImagePlus.GRAY32 ) throw new RuntimeException( "expected ImagePlus type GRAY32" ); - return new ImagePlusImgLoader< FloatType >( imp, VirtualStackImageLoader.createFloatInstance( imp ), minMaxOption, min, max ); + if ( imp.getStack() != null && imp.getStack().isVirtual() ) + return new ImagePlusImgLoader< FloatType >( imp, VirtualStackImageLoader.createFloatInstance( imp ), minMaxOption, min, max ); + else + return new ImagePlusImgLoader< FloatType >( imp, ImageStackImageLoader.createFloatInstance( imp ), minMaxOption, min, max ); } protected final ImagePlus imp; - protected final VirtualStackImageLoader< T, ?, ? > loader; + protected final BasicImgLoader< T > loader; + + protected VolatileGlobalCellCache< ? > loadercache; protected double impMin; protected double impMax; + @SuppressWarnings( "unchecked" ) protected ImagePlusImgLoader( final ImagePlus imp, - final VirtualStackImageLoader< T, ?, ? > loader, + final BasicImgLoader< T > loader, final MinMaxOption minMaxOption, final double min, final double max ) @@ -74,6 +88,11 @@ public class ImagePlusImgLoader< T extends RealType< T > & NativeType< T > > imp this.imp = imp; this.loader = loader; + if ( loader instanceof VirtualStackImageLoader ) + this.loadercache = ( ( VirtualStackImageLoader< T, ?, ? > ) loader ).getCache(); + else + this.loadercache = null; + if ( minMaxOption == MinMaxOption.COMPUTE ) { impMin = Double.POSITIVE_INFINITY; @@ -88,7 +107,8 @@ public class ImagePlusImgLoader< T extends RealType< T > & NativeType< T > > imp ComputeMinMax.computeMinMax( loader.getImage( new ViewId( t, s ) ), minT, maxT ); impMin = Math.min( minT.getRealDouble(), impMin ); impMax = Math.max( maxT.getRealDouble(), impMax ); - loader.getCache().clearCache(); + if ( loadercache != null ) + loadercache.clearCache(); } System.out.println( "COMPUTE" ); System.out.println( impMin + " " + impMax ); @@ -109,10 +129,21 @@ public class ImagePlusImgLoader< T extends RealType< T > & NativeType< T > > imp } } + public void clearCache() + { + if ( loadercache != null ) + { + loadercache.clearCache(); + System.runFinalization(); + System.gc(); + } + } + @Override public RandomAccessibleInterval< UnsignedShortType > getImage( final ViewId view ) { - loader.getCache().clearCache(); + if ( loadercache != null ) + loadercache.clearCache(); final RandomAccessibleInterval< T > img = loader.getImage( view ); return Converters.convert( img, new RealUnsignedShortConverter< T >( impMin, impMax ), new UnsignedShortType() ); } diff --git a/src/main/java/bdv/ij/export/tiles/CellVoyagerDataExporter.java b/src/main/java/bdv/ij/export/tiles/CellVoyagerDataExporter.java index 0625243aec2939f7e91178aefc69ddcbd0f6c880..069bdf922a244f6941d300109b542914a82dbf5d 100644 --- a/src/main/java/bdv/ij/export/tiles/CellVoyagerDataExporter.java +++ b/src/main/java/bdv/ij/export/tiles/CellVoyagerDataExporter.java @@ -315,7 +315,7 @@ public class CellVoyagerDataExporter * Write to HDF5 */ - WriteSequenceToHdf5.writeHdf5File( sequenceDescriptionHDF5, resolutions, chunks, true, hdf5File, progressWriter ); + WriteSequenceToHdf5.writeHdf5File( sequenceDescriptionHDF5, resolutions, chunks, true, hdf5File, null, null, progressWriter ); /* * write XML sequence description diff --git a/src/main/java/bdv/img/imagestack/ImageStackImageLoader.java b/src/main/java/bdv/img/imagestack/ImageStackImageLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..c3014bb52f50eef2cf73fd2f70e94a59598d4026 --- /dev/null +++ b/src/main/java/bdv/img/imagestack/ImageStackImageLoader.java @@ -0,0 +1,133 @@ +package bdv.img.imagestack; + +import ij.ImagePlus; +import mpicbg.spim.data.generic.sequence.BasicImgLoader; +import mpicbg.spim.data.sequence.ViewId; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.basictypeaccess.array.ArrayDataAccess; +import net.imglib2.img.basictypeaccess.array.ByteArray; +import net.imglib2.img.basictypeaccess.array.FloatArray; +import net.imglib2.img.basictypeaccess.array.IntArray; +import net.imglib2.img.basictypeaccess.array.ShortArray; +import net.imglib2.img.planar.PlanarImg; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.ARGBType; +import net.imglib2.type.numeric.NumericType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.integer.UnsignedShortType; +import net.imglib2.type.numeric.real.FloatType; + +public abstract class ImageStackImageLoader< T extends NumericType< T > & NativeType< T >, A extends ArrayDataAccess< A > > implements BasicImgLoader< T > +{ + public static ImageStackImageLoader< UnsignedByteType, ByteArray > createUnsignedByteInstance( final ImagePlus imp ) + { + return new ImageStackImageLoader< UnsignedByteType, ByteArray >( new UnsignedByteType(), imp ) + { + @Override + protected ByteArray wrapPixels( final Object array ) + { + return new ByteArray( ( byte[] ) array ); + } + + @Override + protected void linkType( final PlanarImg< UnsignedByteType, ByteArray > img ) + { + img.setLinkedType( new UnsignedByteType( img ) ); + } + }; + } + + public static ImageStackImageLoader< UnsignedShortType, ShortArray > createUnsignedShortInstance( final ImagePlus imp ) + { + return new ImageStackImageLoader< UnsignedShortType, ShortArray >( new UnsignedShortType(), imp ) + { + @Override + protected ShortArray wrapPixels( final Object array ) + { + return new ShortArray( ( short[] ) array ); + } + + @Override + protected void linkType( final PlanarImg< UnsignedShortType, ShortArray > img ) + { + img.setLinkedType( new UnsignedShortType( img ) ); + } + }; + } + + public static ImageStackImageLoader< FloatType, FloatArray > createFloatInstance( final ImagePlus imp ) + { + return new ImageStackImageLoader< FloatType, FloatArray >( new FloatType(), imp ) + { + @Override + protected FloatArray wrapPixels( final Object array ) + { + return new FloatArray( ( float[] ) array ); + } + + @Override + protected void linkType( final PlanarImg< FloatType, FloatArray > img ) + { + img.setLinkedType( new FloatType( img ) ); + } + }; + } + + public static ImageStackImageLoader< ARGBType, IntArray > createARGBInstance( final ImagePlus imp ) + { + return new ImageStackImageLoader< ARGBType, IntArray >( new ARGBType(), imp ) + { + @Override + protected IntArray wrapPixels( final Object array ) + { + return new IntArray( ( int[] ) array ); + } + + @Override + protected void linkType( final PlanarImg< ARGBType, IntArray > img ) + { + img.setLinkedType( new ARGBType( img ) ); + } + }; + } + + private final T type; + + private final ImagePlus imp; + + private final long[] dim; + + public ImageStackImageLoader( final T type, final ImagePlus imp ) + { + this.type = type; + this.imp = imp; + this.dim = new long[] { imp.getWidth(), imp.getHeight(), imp.getNSlices() }; + } + + protected abstract A wrapPixels( Object array ); + + protected abstract void linkType( PlanarImg< T, A > img ); + + @Override + public RandomAccessibleInterval< T > getImage( final ViewId view ) + { + return new PlanarImg< T, A >( dim, type.getEntitiesPerPixel() ) + { + private PlanarImg< T, A > init() + { + final int channel = view.getViewSetupId() + 1; + final int frame = view.getTimePointId() + 1; + for ( int slice = 1; slice <= dim[ 2 ]; ++slice ) + mirror.set( slice - 1, wrapPixels( imp.getStack().getPixels( imp.getStackIndex( channel, slice, frame ) ) ) ); + linkType( this ); + return this; + } + }.init(); + } + + @Override + public T getImageType() + { + return type; + } +} \ No newline at end of file diff --git a/src/main/java/bdv/img/virtualstack/VirtualStackImageLoader.java b/src/main/java/bdv/img/virtualstack/VirtualStackImageLoader.java index 19b30b5a3c79bd2eb88839bd11933bcb81e415c1..4d2f27000a020b97176f735b4f1d13119fccd483 100644 --- a/src/main/java/bdv/img/virtualstack/VirtualStackImageLoader.java +++ b/src/main/java/bdv/img/virtualstack/VirtualStackImageLoader.java @@ -8,6 +8,7 @@ import net.imglib2.img.NativeImg; import net.imglib2.img.basictypeaccess.volatiles.VolatileAccess; import net.imglib2.img.basictypeaccess.volatiles.array.VolatileByteArray; import net.imglib2.img.basictypeaccess.volatiles.array.VolatileFloatArray; +import net.imglib2.img.basictypeaccess.volatiles.array.VolatileIntArray; import net.imglib2.img.basictypeaccess.volatiles.array.VolatileShortArray; import net.imglib2.realtransform.AffineTransform3D; import net.imglib2.type.NativeType; @@ -37,7 +38,7 @@ import bdv.img.cache.VolatileImgCells.CellCache; * Use {@link #createFloatInstance(ImagePlus)}, * {@link #createUnsignedByteInstance(ImagePlus)} or * {@link #createUnsignedShortInstance(ImagePlus)} depending on the ImagePlus - * pixel type. ARGB is currently not supported. + * pixel type. * * When {@link #getImage(ViewId) loading images}, the provided setup id is used * as the channel index of the {@link ImagePlus}, the provided timepoint id is @@ -112,6 +113,25 @@ public abstract class VirtualStackImageLoader< T extends NativeType< T >, V exte }; } + public static VirtualStackImageLoader< ARGBType, VolatileARGBType, VolatileIntArray > createARGBInstance( final ImagePlus imp ) + { + return new VirtualStackImageLoader< ARGBType, VolatileARGBType, VolatileIntArray >( + imp, new VirtualStackVolatileARGBArrayLoader( imp ), new ARGBType(), new VolatileARGBType() ) + { + @Override + protected void linkType( final CachedCellImg< ARGBType, VolatileIntArray > img ) + { + img.setLinkedType( new ARGBType( img ) ); + } + + @Override + protected void linkVolatileType( final CachedCellImg< VolatileARGBType, VolatileIntArray > img ) + { + img.setLinkedType( new VolatileARGBType( img ) ); + } + }; + } + private static double[][] mipmapResolutions = new double[][] { { 1, 1, 1 } }; private static AffineTransform3D[] mipmapTransforms = new AffineTransform3D[] { new AffineTransform3D() }; diff --git a/src/main/java/bdv/img/virtualstack/VirtualStackVolatileARGBArrayLoader.java b/src/main/java/bdv/img/virtualstack/VirtualStackVolatileARGBArrayLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..d586bdc76e81c30301de30b1054d8a2c28b58074 --- /dev/null +++ b/src/main/java/bdv/img/virtualstack/VirtualStackVolatileARGBArrayLoader.java @@ -0,0 +1,40 @@ +package bdv.img.virtualstack; + +import ij.ImagePlus; +import net.imglib2.img.basictypeaccess.volatiles.array.VolatileIntArray; +import bdv.img.cache.CacheArrayLoader; + +public class VirtualStackVolatileARGBArrayLoader implements CacheArrayLoader< VolatileIntArray > +{ + private final VolatileIntArray theEmptyArray; + + private final ImagePlus imp; + + public VirtualStackVolatileARGBArrayLoader( final ImagePlus imp ) + { + theEmptyArray = new VolatileIntArray( imp.getWidth() * imp.getHeight(), false ); + this.imp = imp; + } + + @Override + public VolatileIntArray loadArray( final int timepoint, final int setup, final int level, final int[] dimensions, final long[] min ) throws InterruptedException + { + final int channel = setup + 1; + final int slice = ( int ) min[ 2 ] + 1; + final int frame = timepoint + 1; + final int[] data = ( int[] ) imp.getStack().getProcessor( imp.getStackIndex( channel, slice, frame ) ).getPixels(); + return new VolatileIntArray( data, true ); + } + + @Override + public VolatileIntArray emptyArray( final int[] dimensions ) + { + return theEmptyArray; + } + + @Override + public int getBytesPerElement() + { + return 4; + } +}