diff --git a/.gitignore b/.gitignore index 14803ad980956b04712d46c855bc6e3c3454ee29..8098709b36f8f384501bb23e156756dfaf4d9eab 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ .project .settings /target - +classes/ # IntelliJ .idea/ *.iml diff --git a/pom.xml b/pom.xml index ce3fdd41dccd46e2b398c2e0220c433fe7883eac..3bcf22161bb8852cd056600496f0306a6ebff91e 100644 --- a/pom.xml +++ b/pom.xml @@ -201,5 +201,11 @@ <artifactId>junit</artifactId> <scope>test</scope> </dependency> - </dependencies> + <dependency> + <groupId>org.azgra</groupId> + <artifactId>DataCompressor</artifactId> + <version>1.0-SNAPSHOT</version> + <scope>compile</scope> + </dependency> + </dependencies> </project> diff --git a/src/main/java/bdv/BigDataViewer.java b/src/main/java/bdv/BigDataViewer.java index 266aa8d8d2ec0f26c04c913fa7daba7e3f9a61c7..9f2390ba1f9e6d395970af9e094bbf07c15180f2 100644 --- a/src/main/java/bdv/BigDataViewer.java +++ b/src/main/java/bdv/BigDataViewer.java @@ -43,6 +43,7 @@ import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.filechooser.FileFilter; +import azgracompress.cache.ICacheFile; import net.imglib2.Volatile; import net.imglib2.converter.Converter; import net.imglib2.display.ColorConverter; @@ -96,628 +97,589 @@ import mpicbg.spim.data.generic.sequence.BasicViewSetup; import mpicbg.spim.data.sequence.Angle; import mpicbg.spim.data.sequence.Channel; -public class BigDataViewer -{ - protected final ViewerFrame viewerFrame; +public class BigDataViewer { + protected final ViewerFrame viewerFrame; - protected final ViewerPanel viewer; + protected final ViewerPanel viewer; - protected final SetupAssignments setupAssignments; - - protected final ManualTransformation manualTransformation; - - protected final Bookmarks bookmarks; - - protected final BrightnessDialog brightnessDialog; - - protected final CropDialog cropDialog; - - protected final RecordMovieDialog movieDialog; - - protected final RecordMaxProjectionDialog movieMaxProjectDialog; - - protected final VisibilityAndGroupingDialog activeSourcesDialog; - - protected final HelpDialog helpDialog; - - protected final ManualTransformationEditor manualTransformationEditor; - - protected final BookmarksEditor bookmarkEditor; - - protected final JFileChooser fileChooser; - - protected File proposedSettingsFile; - - public void toggleManualTransformation() - { - manualTransformationEditor.toggle(); - } - - public void initSetBookmark() - { - bookmarkEditor.initSetBookmark(); - } - - public void initGoToBookmark() - { - bookmarkEditor.initGoToBookmark(); - } - - public void initGoToBookmarkRotation() - { - bookmarkEditor.initGoToBookmarkRotation(); - } - - private static String createSetupName( final BasicViewSetup setup ) - { - if ( setup.hasName() ) - return setup.getName(); - - String name = ""; - - final Angle angle = setup.getAttribute( Angle.class ); - if ( angle != null ) - name += ( name.isEmpty() ? "" : " " ) + "a " + angle.getName(); - - final Channel channel = setup.getAttribute( Channel.class ); - if ( channel != null ) - name += ( name.isEmpty() ? "" : " " ) + "c " + channel.getName(); - - return name; - } - - /** - * Create standard converter from the given {@code type} to ARGB: - * <ul> - * <li>For {@code RealType}s a {@link RealARGBColorConverter} is - * returned.</li> - * <li>For {@code ARGBType}s a {@link ScaledARGBConverter.ARGB} is - * returned.</li> - * <li>For {@code VolatileARGBType}s a - * {@link ScaledARGBConverter.VolatileARGB} is returned.</li> - * </ul> - */ - @SuppressWarnings( "unchecked" ) - public static < T extends NumericType< T > > Converter< T, ARGBType > createConverterToARGB( final T type ) - { - if ( type instanceof RealType ) - { - final RealType< ? > t = ( RealType< ? > ) type; - final double typeMin = Math.max( 0, Math.min( t.getMinValue(), 65535 ) ); - final double typeMax = Math.max( 0, Math.min( t.getMaxValue(), 65535 ) ); - return ( Converter< T, ARGBType > ) RealARGBColorConverter.create( t, typeMin, typeMax ); - } - else if ( type instanceof ARGBType ) - return ( Converter< T, ARGBType > ) new ScaledARGBConverter.ARGB( 0, 255 ); - else if ( type instanceof VolatileARGBType ) - return ( Converter< T, ARGBType > ) new ScaledARGBConverter.VolatileARGB( 0, 255 ); - else - throw new IllegalArgumentException( "ImgLoader of type " + type.getClass() + " not supported." ); - } - - /** - * Create a {@code ConverterSetup} for the given {@code SourceAndConverter}. - * {@link SourceAndConverter#asVolatile() Nested volatile} - * {@code SourceAndConverter} are added to the {@code ConverterSetup} if - * present. If {@code SourceAndConverter} does not comprise a - * {@code ColorConverter}, returns {@code null}. - * - * @param soc - * {@code SourceAndConverter} for which to create a - * {@code ConverterSetup} - * @param setupId - * setupId of the created {@code ConverterSetup} - * @return a new {@code ConverterSetup} or {@code null} - */ - public static ConverterSetup createConverterSetup( final SourceAndConverter< ? > soc, final int setupId ) - { - final List< ColorConverter > converters = new ArrayList<>(); - - final Converter< ?, ARGBType > c = soc.getConverter(); - if ( c instanceof ColorConverter ) - converters.add( ( ColorConverter ) c ); - - final SourceAndConverter< ? extends Volatile< ? > > vsoc = soc.asVolatile(); - if ( vsoc != null ) - { - final Converter< ?, ARGBType > vc = vsoc.getConverter(); - if ( vc instanceof ColorConverter ) - converters.add( ( ColorConverter ) vc ); - } - - if ( converters.isEmpty() ) - return null; - else - return new RealARGBColorConverterSetup( setupId, converters ); - } - - /** - * Decorate source with an extra transformation, that can be edited manually - * in this viewer. {@link SourceAndConverter#asVolatile() Nested volatile} - * {@code SourceAndConverter} are wrapped as well, if present. - */ - public static < T, V extends Volatile< T > > SourceAndConverter< T > wrapWithTransformedSource( final SourceAndConverter< T > soc ) - { - if ( soc.asVolatile() == null ) - return new SourceAndConverter<>( new TransformedSource<>( soc.getSpimSource() ), soc.getConverter() ); - - @SuppressWarnings( "unchecked" ) - final SourceAndConverter< V > vsoc = ( SourceAndConverter< V > ) soc.asVolatile(); - final TransformedSource< T > ts = new TransformedSource<>( soc.getSpimSource() ); - final TransformedSource< V > vts = new TransformedSource<>( vsoc.getSpimSource(), ts ); - return new SourceAndConverter<>( ts, soc.getConverter(), new SourceAndConverter<>( vts, vsoc.getConverter() ) ); - } - - private static < T extends NumericType< T >, V extends Volatile< T > & NumericType< V > > void initSetupNumericType( - final AbstractSpimData< ? > spimData, - final BasicViewSetup setup, - final List< ConverterSetup > converterSetups, - final List< SourceAndConverter< ? > > sources ) - { - final int setupId = setup.getId(); - final ViewerImgLoader imgLoader = ( ViewerImgLoader ) spimData.getSequenceDescription().getImgLoader(); - @SuppressWarnings( "unchecked" ) - final ViewerSetupImgLoader< T, V > setupImgLoader = ( ViewerSetupImgLoader< T, V > ) imgLoader.getSetupImgLoader( setupId ); - final T type = setupImgLoader.getImageType(); - final V volatileType = setupImgLoader.getVolatileImageType(); - - if ( ! ( type instanceof NumericType ) ) - throw new IllegalArgumentException( "ImgLoader of type " + type.getClass() + " not supported." ); - - final String setupName = createSetupName( setup ); - - SourceAndConverter< V > vsoc = null; - if ( volatileType != null ) - { - final VolatileSpimSource< V > vs = new VolatileSpimSource<>( spimData, setupId, setupName ); - vsoc = new SourceAndConverter<>( vs, createConverterToARGB( volatileType ) ); - } - - final SpimSource< T > s = new SpimSource<>( spimData, setupId, setupName ); - final SourceAndConverter< T > soc = new SourceAndConverter<>( s, createConverterToARGB( type ), vsoc ); - final SourceAndConverter< T > tsoc = wrapWithTransformedSource( soc ); - sources.add( tsoc ); - - final ConverterSetup converterSetup = createConverterSetup( tsoc, setupId ); - if ( converterSetup != null ) - converterSetups.add( converterSetup ); - } - - public static void initSetups( - final AbstractSpimData< ? > spimData, - final List< ConverterSetup > converterSetups, - final List< SourceAndConverter< ? > > sources ) - { - for ( final BasicViewSetup setup : spimData.getSequenceDescription().getViewSetupsOrdered() ) - initSetupNumericType( spimData, setup, converterSetups, sources ); - } - - /** - * - * @param converterSetups - * list of {@link ConverterSetup} that control min/max and color - * of sources. - * @param sources - * list of pairs of source of some type and converter from that - * type to ARGB. - * @param spimData - * may be null. The {@link AbstractSpimData} of the dataset (if - * there is one). If it exists, it is used to set up a "Crop" - * dialog. - * @param numTimepoints - * the number of timepoints in the dataset. - * @param cache - * handle to cache. This is used to control io timing. - * @param windowTitle - * title of the viewer window. - * @param progressWriter - * a {@link ProgressWriter} to which BDV may report progress - * (currently only used in the "Record Movie" dialog). - * @param options - * optional parameters. - */ - public BigDataViewer( - final ArrayList< ConverterSetup > converterSetups, - final ArrayList< SourceAndConverter< ? > > sources, - final AbstractSpimData< ? > spimData, - final int numTimepoints, - final CacheControl cache, - final String windowTitle, - final ProgressWriter progressWriter, - final ViewerOptions options ) - { - final InputTriggerConfig inputTriggerConfig = getInputTriggerConfig( options ); - if ( options.values.getTransformEventHandlerFactory() instanceof BehaviourTransformEventHandlerFactory ) - ( ( BehaviourTransformEventHandlerFactory< ? > ) options.values.getTransformEventHandlerFactory() ).setConfig( inputTriggerConfig ); - - viewerFrame = new ViewerFrame( sources, numTimepoints, cache, options ); - if ( windowTitle != null ) - viewerFrame.setTitle( windowTitle ); - viewer = viewerFrame.getViewerPanel(); - - for ( final ConverterSetup cs : converterSetups ) - cs.setViewer( viewer ); - - manualTransformation = new ManualTransformation( viewer ); - manualTransformationEditor = new ManualTransformationEditor( viewer, viewerFrame.getKeybindings() ); - - bookmarks = new Bookmarks(); - bookmarkEditor = new BookmarksEditor( viewer, viewerFrame.getKeybindings(), bookmarks ); - - setupAssignments = new SetupAssignments( converterSetups, 0, 65535 ); - if ( setupAssignments.getMinMaxGroups().size() > 0 ) - { - final MinMaxGroup group = setupAssignments.getMinMaxGroups().get( 0 ); - for ( final ConverterSetup setup : setupAssignments.getConverterSetups() ) - setupAssignments.moveSetupToGroup( setup, group ); - } - - brightnessDialog = new BrightnessDialog( viewerFrame, setupAssignments ); - - if (spimData != null ) - viewer.getSourceInfoOverlayRenderer().setTimePointsOrdered( spimData.getSequenceDescription().getTimePoints().getTimePointsOrdered() ); - - cropDialog = ( spimData == null ) ? null : new CropDialog( viewerFrame, viewer, spimData.getSequenceDescription() ); - - movieDialog = new RecordMovieDialog( viewerFrame, viewer, progressWriter ); - // this is just to get updates of window size: - viewer.getDisplay().addOverlayRenderer( movieDialog ); - - movieMaxProjectDialog = new RecordMaxProjectionDialog( viewerFrame, viewer, progressWriter ); - // this is just to get updates of window size: - viewer.getDisplay().addOverlayRenderer( movieMaxProjectDialog ); - - activeSourcesDialog = new VisibilityAndGroupingDialog( viewerFrame, viewer.getVisibilityAndGrouping() ); - - helpDialog = new HelpDialog( viewerFrame ); - - fileChooser = new JFileChooser(); - fileChooser.setFileFilter( new FileFilter() - { - @Override - public String getDescription() - { - return "xml files"; - } - - @Override - public boolean accept( final File f ) - { - if ( f.isDirectory() ) - return true; - if ( f.isFile() ) - { - final String s = f.getName(); - final int i = s.lastIndexOf( '.' ); - if ( i > 0 && i < s.length() - 1 ) - { - final String ext = s.substring( i + 1 ).toLowerCase(); - return ext.equals( "xml" ); - } - } - return false; - } - } ); - - NavigationActions.installActionBindings( viewerFrame.getKeybindings(), viewer, inputTriggerConfig ); - BigDataViewerActions.installActionBindings( viewerFrame.getKeybindings(), this, inputTriggerConfig ); - - final JMenuBar menubar = new JMenuBar(); - JMenu menu = new JMenu( "File" ); - menubar.add( menu ); - - final ActionMap actionMap = viewerFrame.getKeybindings().getConcatenatedActionMap(); - final JMenuItem miLoadSettings = new JMenuItem( actionMap.get( BigDataViewerActions.LOAD_SETTINGS ) ); - miLoadSettings.setText( "Load settings" ); - menu.add( miLoadSettings ); - - final JMenuItem miSaveSettings = new JMenuItem( actionMap.get( BigDataViewerActions.SAVE_SETTINGS ) ); - miSaveSettings.setText( "Save settings" ); - menu.add( miSaveSettings ); - - menu = new JMenu( "Settings" ); - menubar.add( menu ); - - final JMenuItem miBrightness = new JMenuItem( actionMap.get( BigDataViewerActions.BRIGHTNESS_SETTINGS ) ); - miBrightness.setText( "Brightness & Color" ); - menu.add( miBrightness ); - - final JMenuItem miVisibility = new JMenuItem( actionMap.get( BigDataViewerActions.VISIBILITY_AND_GROUPING ) ); - miVisibility.setText( "Visibility & Grouping" ); - menu.add( miVisibility ); - - menu = new JMenu( "Tools" ); - menubar.add( menu ); - - if ( cropDialog != null ) - { - final JMenuItem miCrop = new JMenuItem( actionMap.get( BigDataViewerActions.CROP ) ); - miCrop.setText( "Crop" ); - menu.add( miCrop ); - } - - final JMenuItem miMovie = new JMenuItem( actionMap.get( BigDataViewerActions.RECORD_MOVIE ) ); - miMovie.setText( "Record Movie" ); - menu.add( miMovie ); - - final JMenuItem miMaxProjectMovie = new JMenuItem( actionMap.get( BigDataViewerActions.RECORD_MAX_PROJECTION_MOVIE ) ); - miMaxProjectMovie.setText( "Record Max-Projection Movie" ); - menu.add( miMaxProjectMovie ); - - final JMenuItem miManualTransform = new JMenuItem( actionMap.get( BigDataViewerActions.MANUAL_TRANSFORM ) ); - miManualTransform.setText( "Manual Transform" ); - menu.add( miManualTransform ); - - menu = new JMenu( "Help" ); - menubar.add( menu ); - - final JMenuItem miHelp = new JMenuItem( actionMap.get( BigDataViewerActions.SHOW_HELP ) ); - miHelp.setText( "Show Help" ); - menu.add( miHelp ); - - viewerFrame.setJMenuBar( menubar ); - } - - public static BigDataViewer open( final AbstractSpimData< ? > spimData, final String windowTitle, final ProgressWriter progressWriter, final ViewerOptions options ) - { - if ( WrapBasicImgLoader.wrapImgLoaderIfNecessary( spimData ) ) - { - System.err.println( "WARNING:\nOpening <SpimData> dataset that is not suited for interactive browsing.\nConsider resaving as HDF5 for better performance." ); - } - - final ArrayList< ConverterSetup > converterSetups = new ArrayList<>(); - final ArrayList< SourceAndConverter< ? > > sources = new ArrayList<>(); - initSetups( spimData, converterSetups, sources ); - - final AbstractSequenceDescription< ?, ?, ? > seq = spimData.getSequenceDescription(); - final int numTimepoints = seq.getTimePoints().size(); - final CacheControl cache = ( ( ViewerImgLoader ) seq.getImgLoader() ).getCacheControl(); - - final BigDataViewer bdv = new BigDataViewer( converterSetups, sources, spimData, numTimepoints, cache, windowTitle, progressWriter, options ); - - WrapBasicImgLoader.removeWrapperIfPresent( spimData ); - - bdv.viewerFrame.setVisible( true ); - InitializeViewerState.initTransform( bdv.viewer ); - return bdv; - } - - public static BigDataViewer open( final String xmlFilename, final String windowTitle, final ProgressWriter progressWriter, final ViewerOptions options ) throws SpimDataException - { - final SpimDataMinimal spimData = new XmlIoSpimDataMinimal().load( xmlFilename ); - final BigDataViewer bdv = open( spimData, windowTitle, progressWriter, options ); - if ( !bdv.tryLoadSettings( xmlFilename ) ) - InitializeViewerState.initBrightness( 0.001, 0.999, bdv.viewer, bdv.setupAssignments ); - return bdv; - } - - public static BigDataViewer open( - final ArrayList< ConverterSetup > converterSetups, - final ArrayList< SourceAndConverter< ? > > sources, - final int numTimepoints, - final CacheControl cache, - final String windowTitle, - final ProgressWriter progressWriter, - final ViewerOptions options ) - { - final BigDataViewer bdv = new BigDataViewer( converterSetups, sources, null, numTimepoints, cache, windowTitle, progressWriter, options ); - bdv.viewerFrame.setVisible( true ); - InitializeViewerState.initTransform( bdv.viewer ); - return bdv; - } - - public ViewerPanel getViewer() - { - return viewer; - } - - public ViewerFrame getViewerFrame() - { - return viewerFrame; - } - - public SetupAssignments getSetupAssignments() - { - return setupAssignments; - } - - public ManualTransformationEditor getManualTransformEditor() - { - return manualTransformationEditor; - } - - public boolean tryLoadSettings( final String xmlFilename ) - { - proposedSettingsFile = null; - if( xmlFilename.startsWith( "http://" ) ) - { - // load settings.xml from the BigDataServer - final String settings = xmlFilename + "settings"; - { - try - { - loadSettings( settings ); - return true; - } - catch ( final FileNotFoundException e ) - {} - catch ( final Exception e ) - { - e.printStackTrace(); - } - } - } - else if ( xmlFilename.endsWith( ".xml" ) ) - { - final String settings = xmlFilename.substring( 0, xmlFilename.length() - ".xml".length() ) + ".settings" + ".xml"; - proposedSettingsFile = new File( settings ); - if ( proposedSettingsFile.isFile() ) - { - try - { - loadSettings( settings ); - return true; - } - catch ( final Exception e ) - { - e.printStackTrace(); - } - } - } - return false; - } - - public void saveSettings() - { - fileChooser.setSelectedFile( proposedSettingsFile ); - final int returnVal = fileChooser.showSaveDialog( null ); - if ( returnVal == JFileChooser.APPROVE_OPTION ) - { - proposedSettingsFile = fileChooser.getSelectedFile(); - try - { - saveSettings( proposedSettingsFile.getCanonicalPath() ); - } - catch ( final IOException e ) - { - e.printStackTrace(); - } - } - } - - public void saveSettings( final String xmlFilename ) throws IOException - { - final Element root = new Element( "Settings" ); - root.addContent( viewer.stateToXml() ); - root.addContent( setupAssignments.toXml() ); - root.addContent( manualTransformation.toXml() ); - root.addContent( bookmarks.toXml() ); - final Document doc = new Document( root ); - final XMLOutputter xout = new XMLOutputter( Format.getPrettyFormat() ); - xout.output( doc, new FileWriter( xmlFilename ) ); - } - - /** - * If {@code options} doesn't define a {@link InputTriggerConfig}, try to - * load it from files in this order: - * <ol> - * <li>"bdvkeyconfig.yaml" in the current directory. - * <li>".bdv/bdvkeyconfig.yaml" in the user's home directory. - * <li>legacy "bigdataviewer.keys.properties" in current directory (will be - * also written to "bdvkeyconfig.yaml"). - * </ol> - * - * @param options - * @return - */ - public static InputTriggerConfig getInputTriggerConfig( final ViewerOptions options ) - { - InputTriggerConfig conf = options.values.getInputTriggerConfig(); - - // try "bdvkeyconfig.yaml" in current directory - if ( conf == null && new File( "bdvkeyconfig.yaml" ).isFile() ) - { - try - { - conf = new InputTriggerConfig( YamlConfigIO.read( "bdvkeyconfig.yaml" ) ); - } - catch ( final IOException e ) - {} - } - - // try "~/.bdv/bdvkeyconfig.yaml" - if ( conf == null ) - { - final String fn = System.getProperty( "user.home" ) + "/.bdv/bdvkeyconfig.yaml"; - if ( new File( fn ).isFile() ) - { - try - { - conf = new InputTriggerConfig( YamlConfigIO.read( fn ) ); - } - catch ( final IOException e ) - {} - } - } - - if ( conf == null ) - { - conf = new InputTriggerConfig(); - } - - return conf; - } - - public void loadSettings() - { - fileChooser.setSelectedFile( proposedSettingsFile ); - final int returnVal = fileChooser.showOpenDialog( null ); - if ( returnVal == JFileChooser.APPROVE_OPTION ) - { - proposedSettingsFile = fileChooser.getSelectedFile(); - try - { - loadSettings( proposedSettingsFile.getCanonicalPath() ); - } - catch ( final Exception e ) - { - e.printStackTrace(); - } - } - } - - public void loadSettings( final String xmlFilename ) throws IOException, JDOMException - { - final SAXBuilder sax = new SAXBuilder(); - final Document doc = sax.build( xmlFilename ); - final Element root = doc.getRootElement(); - viewer.stateFromXml( root ); - setupAssignments.restoreFromXml( root ); - manualTransformation.restoreFromXml( root ); - bookmarks.restoreFromXml( root ); - activeSourcesDialog.update(); - viewer.requestRepaint(); - } - - public static void main( final String[] args ) - { -// final String fn = "http://tomancak-mac-17.mpi-cbg.de:8080/openspim/"; -// final String fn = "/Users/Pietzsch/Desktop/openspim/datasetHDF.xml"; -// final String fn = "/Users/pietzsch/workspace/data/111010_weber_full.xml"; -// final String fn = "/Users/Pietzsch/Desktop/spimrec2/dataset.xml"; -// final String fn = "/Users/pietzsch/Desktop/HisYFP-SPIM/dataset.xml"; -// final String fn = "/Users/Pietzsch/Desktop/bdv example/drosophila 2.xml"; -// final String fn = "/Users/pietzsch/Desktop/data/clusterValia/140219-1/valia-140219-1.xml"; -// final String fn = "/Users/Pietzsch/Desktop/data/catmaid.xml"; -// final String fn = "src/main/resources/openconnectome-bock11-neariso.xml"; -// final String fn = "/home/saalfeld/catmaid.xml"; -// final String fn = "/home/saalfeld/catmaid-fafb00-v9.xml"; -// final String fn = "/home/saalfeld/catmaid-fafb00-sample_A_cutout_3k.xml"; -// final String fn = "/home/saalfeld/catmaid-thorsten.xml"; -// final String fn = "/home/saalfeld/knossos-example.xml"; -// final String fn = "/Users/Pietzsch/Desktop/data/catmaid-confocal.xml"; -// final String fn = "/Users/pietzsch/desktop/data/BDV130418A325/BDV130418A325_NoTempReg.xml"; -// final String fn = "/Users/pietzsch/Desktop/data/valia2/valia.xml"; -// final String fn = "/Users/pietzsch/workspace/data/fast fly/111010_weber/combined.xml"; -// final String fn = "/Users/pietzsch/workspace/data/mette/mette.xml"; -// final String fn = "/Users/tobias/Desktop/openspim.xml"; -// final String fn = "/Users/pietzsch/Desktop/data/fibsem.xml"; -// final String fn = "/Users/pietzsch/Desktop/data/fibsem-remote.xml"; -// final String fn = "/Users/pietzsch/Desktop/url-valia.xml"; -// final String fn = "/Users/pietzsch/Desktop/data/clusterValia/140219-1/valia-140219-1.xml"; - final String fn = "/Users/pietzsch/workspace/data/111010_weber_full.xml"; -// final String fn = "/Volumes/projects/tomancak_lightsheet/Mette/ZeissZ1SPIM/Maritigrella/021013_McH2BsGFP_CAAX-mCherry/11-use/hdf5/021013_McH2BsGFP_CAAX-mCherry-11-use.xml"; - try - { - System.setProperty( "apple.laf.useScreenMenuBar", "true" ); - - final BigDataViewer bdv = open( fn, new File( fn ).getName(), new ProgressWriterConsole(), ViewerOptions.options() ); - -// DumpInputConfig.writeToYaml( System.getProperty( "user.home" ) + "/.bdv/bdvkeyconfig.yaml", bdv.getViewerFrame() ); - } - catch ( final Exception e ) - { - e.printStackTrace(); - } - } + protected final SetupAssignments setupAssignments; + + protected final ManualTransformation manualTransformation; + + protected final Bookmarks bookmarks; + + protected final BrightnessDialog brightnessDialog; + + protected final CropDialog cropDialog; + + protected final RecordMovieDialog movieDialog; + + protected final RecordMaxProjectionDialog movieMaxProjectDialog; + + protected final VisibilityAndGroupingDialog activeSourcesDialog; + + protected final HelpDialog helpDialog; + + protected final ManualTransformationEditor manualTransformationEditor; + + protected final BookmarksEditor bookmarkEditor; + + protected final JFileChooser fileChooser; + + protected File proposedSettingsFile; + + public void toggleManualTransformation() { + manualTransformationEditor.toggle(); + } + + public void initSetBookmark() { + bookmarkEditor.initSetBookmark(); + } + + public void initGoToBookmark() { + bookmarkEditor.initGoToBookmark(); + } + + public void initGoToBookmarkRotation() { + bookmarkEditor.initGoToBookmarkRotation(); + } + + private static String createSetupName(final BasicViewSetup setup) { + if (setup.hasName()) + return setup.getName(); + + String name = ""; + + final Angle angle = setup.getAttribute(Angle.class); + if (angle != null) + name += (name.isEmpty() ? "" : " ") + "a " + angle.getName(); + + final Channel channel = setup.getAttribute(Channel.class); + if (channel != null) + name += (name.isEmpty() ? "" : " ") + "c " + channel.getName(); + + return name; + } + + /** + * Create standard converter from the given {@code type} to ARGB: + * <ul> + * <li>For {@code RealType}s a {@link RealARGBColorConverter} is + * returned.</li> + * <li>For {@code ARGBType}s a {@link ScaledARGBConverter.ARGB} is + * returned.</li> + * <li>For {@code VolatileARGBType}s a + * {@link ScaledARGBConverter.VolatileARGB} is returned.</li> + * </ul> + */ + @SuppressWarnings("unchecked") + public static <T extends NumericType<T>> Converter<T, ARGBType> createConverterToARGB(final T type) { + if (type instanceof RealType) { + final RealType<?> t = (RealType<?>) type; + final double typeMin = Math.max(0, Math.min(t.getMinValue(), 65535)); + final double typeMax = Math.max(0, Math.min(t.getMaxValue(), 65535)); + return (Converter<T, ARGBType>) RealARGBColorConverter.create(t, typeMin, typeMax); + } else if (type instanceof ARGBType) + return (Converter<T, ARGBType>) new ScaledARGBConverter.ARGB(0, 255); + else if (type instanceof VolatileARGBType) + return (Converter<T, ARGBType>) new ScaledARGBConverter.VolatileARGB(0, 255); + else + throw new IllegalArgumentException("ImgLoader of type " + type.getClass() + " not supported."); + } + + /** + * Create a {@code ConverterSetup} for the given {@code SourceAndConverter}. + * {@link SourceAndConverter#asVolatile() Nested volatile} + * {@code SourceAndConverter} are added to the {@code ConverterSetup} if + * present. If {@code SourceAndConverter} does not comprise a + * {@code ColorConverter}, returns {@code null}. + * + * @param soc {@code SourceAndConverter} for which to create a + * {@code ConverterSetup} + * @param setupId setupId of the created {@code ConverterSetup} + * @return a new {@code ConverterSetup} or {@code null} + */ + public static ConverterSetup createConverterSetup(final SourceAndConverter<?> soc, final int setupId) { + final List<ColorConverter> converters = new ArrayList<>(); + + final Converter<?, ARGBType> c = soc.getConverter(); + if (c instanceof ColorConverter) + converters.add((ColorConverter) c); + + final SourceAndConverter<? extends Volatile<?>> vsoc = soc.asVolatile(); + if (vsoc != null) { + final Converter<?, ARGBType> vc = vsoc.getConverter(); + if (vc instanceof ColorConverter) + converters.add((ColorConverter) vc); + } + + if (converters.isEmpty()) + return null; + else + return new RealARGBColorConverterSetup(setupId, converters); + } + + /** + * Decorate source with an extra transformation, that can be edited manually + * in this viewer. {@link SourceAndConverter#asVolatile() Nested volatile} + * {@code SourceAndConverter} are wrapped as well, if present. + */ + public static <T, V extends Volatile<T>> SourceAndConverter<T> wrapWithTransformedSource(final SourceAndConverter<T> soc) { + if (soc.asVolatile() == null) + return new SourceAndConverter<>(new TransformedSource<>(soc.getSpimSource()), soc.getConverter()); + + @SuppressWarnings("unchecked") final SourceAndConverter<V> vsoc = (SourceAndConverter<V>) soc.asVolatile(); + final TransformedSource<T> ts = new TransformedSource<>(soc.getSpimSource()); + final TransformedSource<V> vts = new TransformedSource<>(vsoc.getSpimSource(), ts); + return new SourceAndConverter<>(ts, soc.getConverter(), new SourceAndConverter<>(vts, vsoc.getConverter())); + } + + private static <T extends NumericType<T>, V extends Volatile<T> & NumericType<V>> void initSetupNumericType( + final AbstractSpimData<?> spimData, + final BasicViewSetup setup, + final List<ConverterSetup> converterSetups, + final List<SourceAndConverter<?>> sources) { + final int setupId = setup.getId(); + final ViewerImgLoader imgLoader = (ViewerImgLoader) spimData.getSequenceDescription().getImgLoader(); + @SuppressWarnings("unchecked") final ViewerSetupImgLoader<T, V> setupImgLoader = + (ViewerSetupImgLoader<T, V>) imgLoader.getSetupImgLoader( + setupId); + final T type = setupImgLoader.getImageType(); + final V volatileType = setupImgLoader.getVolatileImageType(); + + if (!(type instanceof NumericType)) + throw new IllegalArgumentException("ImgLoader of type " + type.getClass() + " not supported."); + + final String setupName = createSetupName(setup); + + SourceAndConverter<V> vsoc = null; + if (volatileType != null) { + final VolatileSpimSource<V> vs = new VolatileSpimSource<>(spimData, setupId, setupName); + vsoc = new SourceAndConverter<>(vs, createConverterToARGB(volatileType)); + } + + final SpimSource<T> s = new SpimSource<>(spimData, setupId, setupName); + final SourceAndConverter<T> soc = new SourceAndConverter<>(s, createConverterToARGB(type), vsoc); + final SourceAndConverter<T> tsoc = wrapWithTransformedSource(soc); + sources.add(tsoc); + + final ConverterSetup converterSetup = createConverterSetup(tsoc, setupId); + if (converterSetup != null) + converterSetups.add(converterSetup); + } + + public static void initSetups( + final AbstractSpimData<?> spimData, + final List<ConverterSetup> converterSetups, + final List<SourceAndConverter<?>> sources) { + for (final BasicViewSetup setup : spimData.getSequenceDescription().getViewSetupsOrdered()) + initSetupNumericType(spimData, setup, converterSetups, sources); + } + + /** + * @param converterSetups list of {@link ConverterSetup} that control min/max and color + * of sources. + * @param sources list of pairs of source of some type and converter from that + * type to ARGB. + * @param spimData may be null. The {@link AbstractSpimData} of the dataset (if + * there is one). If it exists, it is used to set up a "Crop" + * dialog. + * @param numTimepoints the number of timepoints in the dataset. + * @param cache handle to cache. This is used to control io timing. + * @param windowTitle title of the viewer window. + * @param progressWriter a {@link ProgressWriter} to which BDV may report progress + * (currently only used in the "Record Movie" dialog). + * @param options optional parameters. + */ + public BigDataViewer( + final ArrayList<ConverterSetup> converterSetups, + final ArrayList<SourceAndConverter<?>> sources, + final AbstractSpimData<?> spimData, + final int numTimepoints, + final CacheControl cache, + final String windowTitle, + final ProgressWriter progressWriter, + final ViewerOptions options) { + final InputTriggerConfig inputTriggerConfig = getInputTriggerConfig(options); + if (options.values.getTransformEventHandlerFactory() instanceof BehaviourTransformEventHandlerFactory) + ((BehaviourTransformEventHandlerFactory<?>) options.values.getTransformEventHandlerFactory()).setConfig( + inputTriggerConfig); + + viewerFrame = new ViewerFrame(sources, numTimepoints, cache, options); + if (windowTitle != null) + viewerFrame.setTitle(windowTitle); + viewer = viewerFrame.getViewerPanel(); + + for (final ConverterSetup cs : converterSetups) + cs.setViewer(viewer); + + manualTransformation = new ManualTransformation(viewer); + manualTransformationEditor = new ManualTransformationEditor(viewer, viewerFrame.getKeybindings()); + + bookmarks = new Bookmarks(); + bookmarkEditor = new BookmarksEditor(viewer, viewerFrame.getKeybindings(), bookmarks); + + setupAssignments = new SetupAssignments(converterSetups, 0, 65535); + if (setupAssignments.getMinMaxGroups().size() > 0) { + final MinMaxGroup group = setupAssignments.getMinMaxGroups().get(0); + for (final ConverterSetup setup : setupAssignments.getConverterSetups()) + setupAssignments.moveSetupToGroup(setup, group); + } + + brightnessDialog = new BrightnessDialog(viewerFrame, setupAssignments); + + if (spimData != null) + viewer.getSourceInfoOverlayRenderer().setTimePointsOrdered(spimData.getSequenceDescription().getTimePoints().getTimePointsOrdered()); + + cropDialog = (spimData == null) ? null : new CropDialog(viewerFrame, viewer, spimData.getSequenceDescription()); + + movieDialog = new RecordMovieDialog(viewerFrame, viewer, progressWriter); + // this is just to get updates of window size: + viewer.getDisplay().addOverlayRenderer(movieDialog); + + movieMaxProjectDialog = new RecordMaxProjectionDialog(viewerFrame, viewer, progressWriter); + // this is just to get updates of window size: + viewer.getDisplay().addOverlayRenderer(movieMaxProjectDialog); + + activeSourcesDialog = new VisibilityAndGroupingDialog(viewerFrame, viewer.getVisibilityAndGrouping()); + + helpDialog = new HelpDialog(viewerFrame); + + fileChooser = new JFileChooser(); + fileChooser.setFileFilter(new FileFilter() { + @Override + public String getDescription() { + return "xml files"; + } + + @Override + public boolean accept(final File f) { + if (f.isDirectory()) + return true; + if (f.isFile()) { + final String s = f.getName(); + final int i = s.lastIndexOf('.'); + if (i > 0 && i < s.length() - 1) { + final String ext = s.substring(i + 1).toLowerCase(); + return ext.equals("xml"); + } + } + return false; + } + }); + + NavigationActions.installActionBindings(viewerFrame.getKeybindings(), viewer, inputTriggerConfig); + BigDataViewerActions.installActionBindings(viewerFrame.getKeybindings(), this, inputTriggerConfig); + + final JMenuBar menubar = new JMenuBar(); + JMenu menu = new JMenu("File"); + menubar.add(menu); + + final ActionMap actionMap = viewerFrame.getKeybindings().getConcatenatedActionMap(); + final JMenuItem miLoadSettings = new JMenuItem(actionMap.get(BigDataViewerActions.LOAD_SETTINGS)); + miLoadSettings.setText("Load settings"); + menu.add(miLoadSettings); + + final JMenuItem miSaveSettings = new JMenuItem(actionMap.get(BigDataViewerActions.SAVE_SETTINGS)); + miSaveSettings.setText("Save settings"); + menu.add(miSaveSettings); + + menu = new JMenu("Settings"); + menubar.add(menu); + + final JMenuItem miBrightness = new JMenuItem(actionMap.get(BigDataViewerActions.BRIGHTNESS_SETTINGS)); + miBrightness.setText("Brightness & Color"); + menu.add(miBrightness); + + final JMenuItem miVisibility = new JMenuItem(actionMap.get(BigDataViewerActions.VISIBILITY_AND_GROUPING)); + miVisibility.setText("Visibility & Grouping"); + menu.add(miVisibility); + + menu = new JMenu("Tools"); + menubar.add(menu); + + if (cropDialog != null) { + final JMenuItem miCrop = new JMenuItem(actionMap.get(BigDataViewerActions.CROP)); + miCrop.setText("Crop"); + menu.add(miCrop); + } + + final JMenuItem miMovie = new JMenuItem(actionMap.get(BigDataViewerActions.RECORD_MOVIE)); + miMovie.setText("Record Movie"); + menu.add(miMovie); + + final JMenuItem miMaxProjectMovie = + new JMenuItem(actionMap.get(BigDataViewerActions.RECORD_MAX_PROJECTION_MOVIE)); + miMaxProjectMovie.setText("Record Max-Projection Movie"); + menu.add(miMaxProjectMovie); + + final JMenuItem miManualTransform = new JMenuItem(actionMap.get(BigDataViewerActions.MANUAL_TRANSFORM)); + miManualTransform.setText("Manual Transform"); + menu.add(miManualTransform); + + menu = new JMenu("Help"); + menubar.add(menu); + + final JMenuItem miHelp = new JMenuItem(actionMap.get(BigDataViewerActions.SHOW_HELP)); + miHelp.setText("Show Help"); + menu.add(miHelp); + + viewerFrame.setJMenuBar(menubar); + } + + public static BigDataViewer open(final AbstractSpimData<?> spimData, + final String windowTitle, + final ProgressWriter progressWriter, + final ViewerOptions options) { + if (WrapBasicImgLoader.wrapImgLoaderIfNecessary(spimData)) { + System.err.println( + "WARNING:\nOpening <SpimData> dataset that is not suited for interactive browsing.\nConsider " + + "resaving as HDF5 for better performance."); + } + + final ArrayList<ConverterSetup> converterSetups = new ArrayList<>(); + final ArrayList<SourceAndConverter<?>> sources = new ArrayList<>(); + initSetups(spimData, converterSetups, sources); + + final AbstractSequenceDescription<?, ?, ?> seq = spimData.getSequenceDescription(); + final int numTimepoints = seq.getTimePoints().size(); + final CacheControl cache = ((ViewerImgLoader) seq.getImgLoader()).getCacheControl(); + + final BigDataViewer bdv = new BigDataViewer(converterSetups, + sources, + spimData, + numTimepoints, + cache, + windowTitle, + progressWriter, + options); + + WrapBasicImgLoader.removeWrapperIfPresent(spimData); + + bdv.viewerFrame.setVisible(true); + InitializeViewerState.initTransform(bdv.viewer); + return bdv; + } + + public static BigDataViewer open(final String xmlFilename, + final String windowTitle, + final ProgressWriter progressWriter, + final ViewerOptions options, + final boolean allowCompression) throws SpimDataException { + + final XmlIoSpimDataMinimal xmlIoSpimDataMinimal = new XmlIoSpimDataMinimal(); + xmlIoSpimDataMinimal.setAllowCompression(allowCompression); + final SpimDataMinimal spimData = xmlIoSpimDataMinimal.load(xmlFilename); + final BigDataViewer bdv = open(spimData, windowTitle, progressWriter, options); + if (!bdv.tryLoadSettings(xmlFilename)) + InitializeViewerState.initBrightness(0.001, 0.999, bdv.viewer, bdv.setupAssignments); + return bdv; + } + + public static BigDataViewer open( + final ArrayList<ConverterSetup> converterSetups, + final ArrayList<SourceAndConverter<?>> sources, + final int numTimepoints, + final CacheControl cache, + final String windowTitle, + final ProgressWriter progressWriter, + final ViewerOptions options) { + final BigDataViewer bdv = new BigDataViewer(converterSetups, + sources, + null, + numTimepoints, + cache, + windowTitle, + progressWriter, + options); + bdv.viewerFrame.setVisible(true); + InitializeViewerState.initTransform(bdv.viewer); + return bdv; + } + + public ViewerPanel getViewer() { + return viewer; + } + + public ViewerFrame getViewerFrame() { + return viewerFrame; + } + + public SetupAssignments getSetupAssignments() { + return setupAssignments; + } + + public ManualTransformationEditor getManualTransformEditor() { + return manualTransformationEditor; + } + + public boolean tryLoadSettings(final String xmlFilename) { + proposedSettingsFile = null; + if (xmlFilename.startsWith("http://")) { + // load settings.xml from the BigDataServer + final String settings = xmlFilename + "settings"; + { + try { + loadSettings(settings); + return true; + } catch (final FileNotFoundException e) { + } catch (final Exception e) { + e.printStackTrace(); + } + } + } else if (xmlFilename.endsWith(".xml")) { + final String settings = xmlFilename.substring(0, + xmlFilename.length() - ".xml".length()) + ".settings" + + ".xml"; + proposedSettingsFile = new File(settings); + if (proposedSettingsFile.isFile()) { + try { + loadSettings(settings); + return true; + } catch (final Exception e) { + e.printStackTrace(); + } + } + } + return false; + } + + public void saveSettings() { + fileChooser.setSelectedFile(proposedSettingsFile); + final int returnVal = fileChooser.showSaveDialog(null); + if (returnVal == JFileChooser.APPROVE_OPTION) { + proposedSettingsFile = fileChooser.getSelectedFile(); + try { + saveSettings(proposedSettingsFile.getCanonicalPath()); + } catch (final IOException e) { + e.printStackTrace(); + } + } + } + + public void saveSettings(final String xmlFilename) throws IOException { + final Element root = new Element("Settings"); + root.addContent(viewer.stateToXml()); + root.addContent(setupAssignments.toXml()); + root.addContent(manualTransformation.toXml()); + root.addContent(bookmarks.toXml()); + final Document doc = new Document(root); + final XMLOutputter xout = new XMLOutputter(Format.getPrettyFormat()); + xout.output(doc, new FileWriter(xmlFilename)); + } + + /** + * If {@code options} doesn't define a {@link InputTriggerConfig}, try to + * load it from files in this order: + * <ol> + * <li>"bdvkeyconfig.yaml" in the current directory. + * <li>".bdv/bdvkeyconfig.yaml" in the user's home directory. + * <li>legacy "bigdataviewer.keys.properties" in current directory (will be + * also written to "bdvkeyconfig.yaml"). + * </ol> + * + * @param options + * @return + */ + public static InputTriggerConfig getInputTriggerConfig(final ViewerOptions options) { + InputTriggerConfig conf = options.values.getInputTriggerConfig(); + + // try "bdvkeyconfig.yaml" in current directory + if (conf == null && new File("bdvkeyconfig.yaml").isFile()) { + try { + conf = new InputTriggerConfig(YamlConfigIO.read("bdvkeyconfig.yaml")); + } catch (final IOException e) { + } + } + + // try "~/.bdv/bdvkeyconfig.yaml" + if (conf == null) { + final String fn = System.getProperty("user.home") + "/.bdv/bdvkeyconfig.yaml"; + if (new File(fn).isFile()) { + try { + conf = new InputTriggerConfig(YamlConfigIO.read(fn)); + } catch (final IOException e) { + } + } + } + + if (conf == null) { + conf = new InputTriggerConfig(); + } + + return conf; + } + + public void loadSettings() { + fileChooser.setSelectedFile(proposedSettingsFile); + final int returnVal = fileChooser.showOpenDialog(null); + if (returnVal == JFileChooser.APPROVE_OPTION) { + proposedSettingsFile = fileChooser.getSelectedFile(); + try { + loadSettings(proposedSettingsFile.getCanonicalPath()); + } catch (final Exception e) { + e.printStackTrace(); + } + } + } + + public void loadSettings(final String xmlFilename) throws IOException, JDOMException { + final SAXBuilder sax = new SAXBuilder(); + final Document doc = sax.build(xmlFilename); + final Element root = doc.getRootElement(); + viewer.stateFromXml(root); + setupAssignments.restoreFromXml(root); + manualTransformation.restoreFromXml(root); + bookmarks.restoreFromXml(root); + activeSourcesDialog.update(); + viewer.requestRepaint(); + } + + public static void main(final String[] args) { + // final String fn = "http://tomancak-mac-17.mpi-cbg.de:8080/openspim/"; + // final String fn = "/Users/Pietzsch/Desktop/openspim/datasetHDF.xml"; + // final String fn = "/Users/pietzsch/workspace/data/111010_weber_full.xml"; + // final String fn = "/Users/Pietzsch/Desktop/spimrec2/dataset.xml"; + // final String fn = "/Users/pietzsch/Desktop/HisYFP-SPIM/dataset.xml"; + // final String fn = "/Users/Pietzsch/Desktop/bdv example/drosophila 2.xml"; + // final String fn = "/Users/pietzsch/Desktop/data/clusterValia/140219-1/valia-140219-1.xml"; + // final String fn = "/Users/Pietzsch/Desktop/data/catmaid.xml"; + // final String fn = "src/main/resources/openconnectome-bock11-neariso.xml"; + // final String fn = "/home/saalfeld/catmaid.xml"; + // final String fn = "/home/saalfeld/catmaid-fafb00-v9.xml"; + // final String fn = "/home/saalfeld/catmaid-fafb00-sample_A_cutout_3k.xml"; + // final String fn = "/home/saalfeld/catmaid-thorsten.xml"; + // final String fn = "/home/saalfeld/knossos-example.xml"; + // final String fn = "/Users/Pietzsch/Desktop/data/catmaid-confocal.xml"; + // final String fn = "/Users/pietzsch/desktop/data/BDV130418A325/BDV130418A325_NoTempReg.xml"; + // final String fn = "/Users/pietzsch/Desktop/data/valia2/valia.xml"; + // final String fn = "/Users/pietzsch/workspace/data/fast fly/111010_weber/combined.xml"; + // final String fn = "/Users/pietzsch/workspace/data/mette/mette.xml"; + // final String fn = "/Users/tobias/Desktop/openspim.xml"; + // final String fn = "/Users/pietzsch/Desktop/data/fibsem.xml"; + // final String fn = "/Users/pietzsch/Desktop/data/fibsem-remote.xml"; + // final String fn = "/Users/pietzsch/Desktop/url-valia.xml"; + // final String fn = "/Users/pietzsch/Desktop/data/clusterValia/140219-1/valia-140219-1.xml"; + // final String fn = "/Users/pietzsch/workspace/data/111010_weber_full.xml"; + // final String fn = "/Volumes/projects/tomancak_lightsheet/Mette/ZeissZ1SPIM/Maritigrella/021013_McH2BsGFP_CAAX-mCherry/11-use/hdf5/021013_McH2BsGFP_CAAX-mCherry-11-use.xml"; + if (args.length < 1) { + System.err.println("Provide path."); + return; + } + final String fn = args[0]; + final boolean allowCompression = (args.length > 1) && (args[1].equals("-qcmp")); + try { + System.setProperty("apple.laf.useScreenMenuBar", "true"); + + final BigDataViewer bdv = open(fn, + "Server test", + new ProgressWriterConsole(), + ViewerOptions.options(), + allowCompression); + + // DumpInputConfig.writeToYaml( System.getProperty( "user.home" ) + "/.bdv/bdvkeyconfig.yaml", bdv.getViewerFrame() ); + } catch (final Exception e) { + e.printStackTrace(); + } + } } diff --git a/src/main/java/bdv/img/remote/RemoteImageLoader.java b/src/main/java/bdv/img/remote/RemoteImageLoader.java index b485a68a4bed4b180909602d39fa5fe7d82de5db..2d5247a2995966d20952922aeada3d75252c9188 100644 --- a/src/main/java/bdv/img/remote/RemoteImageLoader.java +++ b/src/main/java/bdv/img/remote/RemoteImageLoader.java @@ -7,13 +7,13 @@ * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -29,13 +29,8 @@ */ package bdv.img.remote; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.HashMap; - -import com.google.gson.GsonBuilder; - +import azgracompress.cache.ICacheFile; +import azgracompress.cache.QuantizationCacheManager; import bdv.AbstractViewerSetupImgLoader; import bdv.ViewerImgLoader; import bdv.img.cache.VolatileCachedCellImg; @@ -44,6 +39,7 @@ import bdv.img.hdf5.DimsAndExistence; import bdv.img.hdf5.MipmapInfo; import bdv.img.hdf5.ViewLevelId; import bdv.util.ConstantRandomAccessible; +import com.google.gson.GsonBuilder; import mpicbg.spim.data.generic.sequence.ImgLoaderHint; import net.imglib2.FinalInterval; import net.imglib2.RandomAccessibleInterval; @@ -57,211 +53,213 @@ import net.imglib2.type.volatiles.VolatileUnsignedShortType; import net.imglib2.util.IntervalIndexer; import net.imglib2.view.Views; -public class RemoteImageLoader implements ViewerImgLoader -{ - protected String baseUrl; - - protected RemoteImageLoaderMetaData metadata; - - protected HashMap< ViewLevelId, int[] > cellsDimensions; - - protected VolatileGlobalCellCache cache; - - protected RemoteVolatileShortArrayLoader shortLoader; - - /** - * TODO - */ - protected final HashMap< Integer, SetupImgLoader > setupImgLoaders; - - public RemoteImageLoader( final String baseUrl ) throws IOException - { - this( baseUrl, true ); - } - - public RemoteImageLoader( final String baseUrl, final boolean doOpen ) throws IOException - { - this.baseUrl = baseUrl; - setupImgLoaders = new HashMap<>(); - if ( doOpen ) - open(); - } - - @Override - public SetupImgLoader getSetupImgLoader( final int setupId ) - { - tryopen(); - return setupImgLoaders.get( setupId ); - } - - private boolean isOpen = false; - - private void open() throws IOException - { - if ( ! isOpen ) - { - synchronized ( this ) - { - if ( isOpen ) - return; - isOpen = true; - - final URL url = new URL( baseUrl + "?p=init" ); - final GsonBuilder gsonBuilder = new GsonBuilder(); - gsonBuilder.registerTypeAdapter( AffineTransform3D.class, new AffineTransform3DJsonSerializer() ); - metadata = gsonBuilder.create().fromJson( - new InputStreamReader( url.openStream() ), - RemoteImageLoaderMetaData.class ); - shortLoader = new RemoteVolatileShortArrayLoader( this ); - cache = new VolatileGlobalCellCache( metadata.maxNumLevels, 10 ); - cellsDimensions = metadata.createCellsDimensions(); - for ( final int setupId : metadata.perSetupMipmapInfo.keySet() ) - setupImgLoaders.put( setupId, new SetupImgLoader( setupId ) ); - } - } - } - - private void tryopen() - { - try - { - open(); - } - catch ( final IOException e ) - { - throw new RuntimeException( e ); - } - } - - @Override - public VolatileGlobalCellCache getCacheControl() - { - tryopen(); - return cache; - } - - public MipmapInfo getMipmapInfo( final int setupId ) - { - tryopen(); - return metadata.perSetupMipmapInfo.get( setupId ); - } - - /** - * Checks whether the given image data is present on the server. - * - * @return true, if the given image data is present. - */ - public boolean existsImageData( final ViewLevelId id ) - { - return getDimsAndExistence( id ).exists(); - } - - /** - * For images that are missing in the hdf5, a constant image is created. If - * the dimension of the missing image is known (see - * {@link #getDimsAndExistence(ViewLevelId)}) then use that. Otherwise - * create a 1x1x1 image. - */ - protected < T > RandomAccessibleInterval< T > getMissingDataImage( final ViewLevelId id, final T constant ) - { - final long[] d = getDimsAndExistence( id ).getDimensions(); - return Views.interval( new ConstantRandomAccessible<>( constant, 3 ), new FinalInterval( d ) ); - } - - public DimsAndExistence getDimsAndExistence( final ViewLevelId id ) - { - tryopen(); - return metadata.dimsAndExistence.get( id ); - } - - int getCellIndex( final int timepoint, final int setup, final int level, final long[] globalPosition ) - { - final int[] cellDims = cellsDimensions.get( new ViewLevelId( timepoint, setup, level ) ); - final int[] cellSize = getMipmapInfo( setup ).getSubdivisions()[ level ]; - final int[] cellPos = new int[] { - ( int ) globalPosition[ 0 ] / cellSize[ 0 ], - ( int ) globalPosition[ 1 ] / cellSize[ 1 ], - ( int ) globalPosition[ 2 ] / cellSize[ 2 ] }; - return IntervalIndexer.positionToIndex( cellPos, cellDims ); - } - - /** - * Create a {@link VolatileCachedCellImg} backed by the cache. The - * {@code type} should be either {@link UnsignedShortType} and - * {@link VolatileUnsignedShortType}. - */ - protected < T extends NativeType< T > > RandomAccessibleInterval< T > prepareCachedImage( - final ViewLevelId id, - final LoadingStrategy loadingStrategy, - final T type ) - { - tryopen(); - if ( cache == null ) - throw new RuntimeException( "no connection open" ); - -// final ViewLevelId id = new ViewLevelId( timepointId, setupId, level ); - if ( ! existsImageData( id ) ) - { - System.err.println( String.format( - "image data for timepoint %d setup %d level %d could not be found.", - id.getTimePointId(), id.getViewSetupId(), id.getLevel() ) ); - return getMissingDataImage( id, type ); - } - - final int timepointId = id.getTimePointId(); - final int setupId = id.getViewSetupId(); - final int level = id.getLevel(); - final MipmapInfo mipmapInfo = metadata.perSetupMipmapInfo.get( setupId ); - - final long[] dimensions = metadata.dimsAndExistence.get( id ).getDimensions(); - final int[] cellDimensions = mipmapInfo.getSubdivisions()[ level ]; - final CellGrid grid = new CellGrid( dimensions, cellDimensions ); - - final int priority = mipmapInfo.getMaxLevel() - level; - final CacheHints cacheHints = new CacheHints( loadingStrategy, priority, false ); - return cache.createImg( grid, timepointId, setupId, level, cacheHints, shortLoader, type ); - } - - public class SetupImgLoader extends AbstractViewerSetupImgLoader< UnsignedShortType, VolatileUnsignedShortType > - { - private final int setupId; - - protected SetupImgLoader( final int setupId ) - { - super( new UnsignedShortType(), new VolatileUnsignedShortType() ); - this.setupId = setupId; - } - - @Override - public RandomAccessibleInterval< UnsignedShortType > getImage( final int timepointId, final int level, final ImgLoaderHint... hints ) - { - final ViewLevelId id = new ViewLevelId( timepointId, setupId, level ); - return prepareCachedImage( id, LoadingStrategy.BLOCKING, type ); - } - - @Override - public RandomAccessibleInterval< VolatileUnsignedShortType > getVolatileImage( final int timepointId, final int level, final ImgLoaderHint... hints ) - { - final ViewLevelId id = new ViewLevelId( timepointId, setupId, level ); - return prepareCachedImage( id, LoadingStrategy.BUDGETED, volatileType ); - } - - @Override - public double[][] getMipmapResolutions() - { - return getMipmapInfo( setupId ).getResolutions(); - } - - @Override - public AffineTransform3D[] getMipmapTransforms() - { - return getMipmapInfo( setupId ).getTransforms(); - } - - @Override - public int numMipmapLevels() - { - return getMipmapInfo( setupId ).getNumLevels(); - } - } +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.HashMap; + +public class RemoteImageLoader implements ViewerImgLoader { + protected String baseUrl; + + protected RemoteImageLoaderMetaData metadata; + + protected HashMap<ViewLevelId, int[]> cellsDimensions; + + protected VolatileGlobalCellCache cache; + + protected RemoteVolatileShortArrayLoader shortLoader; + + private boolean allowCompression; + /** + * TODO + */ + protected final HashMap<Integer, SetupImgLoader> setupImgLoaders; + + public RemoteImageLoader(final String baseUrl) throws IOException { + this(baseUrl, true); + } + + public RemoteImageLoader(final String baseUrl, + final boolean doOpen) throws IOException { + this.baseUrl = baseUrl; + setupImgLoaders = new HashMap<>(); + if (doOpen) + open(); + } + + @Override + public SetupImgLoader getSetupImgLoader(final int setupId) { + tryopen(); + return setupImgLoaders.get(setupId); + } + + private boolean isOpen = false; + + private void open() throws IOException { + if (!isOpen) { + synchronized (this) { + if (isOpen) + return; + isOpen = true; + + if (allowCompression) { + receiveCompressionInfo(); + } + + final URL url = new URL(baseUrl + "?p=init"); + final GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.registerTypeAdapter(AffineTransform3D.class, new AffineTransform3DJsonSerializer()); + metadata = gsonBuilder.create().fromJson( + new InputStreamReader(url.openStream()), + RemoteImageLoaderMetaData.class); + shortLoader = new RemoteVolatileShortArrayLoader(this); + cache = new VolatileGlobalCellCache(metadata.maxNumLevels, 10); + cellsDimensions = metadata.createCellsDimensions(); + for (final int setupId : metadata.perSetupMipmapInfo.keySet()) + setupImgLoaders.put(setupId, new SetupImgLoader(setupId)); + } + } + } + + public boolean shouldAllowCompression() { + return allowCompression; + } + + public void setAllowCompression(final boolean compressionEnabled) { + this.allowCompression = compressionEnabled; + } + + private void receiveCompressionInfo() throws IOException { + final URL url = new URL(baseUrl + "?p=init_qcmp"); + final ICacheFile cachedCodebook = QuantizationCacheManager.readCacheFile(url.openStream()); + System.out.println("\u001b[33mRemoteImageLoader::receiveCompressionInfo() - received cache file. '" + cachedCodebook.toString() + "'\u001b[0m"); + } + + + private void tryopen() { + try { + open(); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public VolatileGlobalCellCache getCacheControl() { + tryopen(); + return cache; + } + + public MipmapInfo getMipmapInfo(final int setupId) { + tryopen(); + return metadata.perSetupMipmapInfo.get(setupId); + } + + /** + * Checks whether the given image data is present on the server. + * + * @return true, if the given image data is present. + */ + public boolean existsImageData(final ViewLevelId id) { + return getDimsAndExistence(id).exists(); + } + + /** + * For images that are missing in the hdf5, a constant image is created. If + * the dimension of the missing image is known (see + * {@link #getDimsAndExistence(ViewLevelId)}) then use that. Otherwise + * create a 1x1x1 image. + */ + protected <T> RandomAccessibleInterval<T> getMissingDataImage(final ViewLevelId id, final T constant) { + final long[] d = getDimsAndExistence(id).getDimensions(); + return Views.interval(new ConstantRandomAccessible<>(constant, 3), new FinalInterval(d)); + } + + public DimsAndExistence getDimsAndExistence(final ViewLevelId id) { + tryopen(); + return metadata.dimsAndExistence.get(id); + } + + int getCellIndex(final int timepoint, final int setup, final int level, final long[] globalPosition) { + final int[] cellDims = cellsDimensions.get(new ViewLevelId(timepoint, setup, level)); + final int[] cellSize = getMipmapInfo(setup).getSubdivisions()[level]; + final int[] cellPos = new int[]{ + (int) globalPosition[0] / cellSize[0], + (int) globalPosition[1] / cellSize[1], + (int) globalPosition[2] / cellSize[2]}; + return IntervalIndexer.positionToIndex(cellPos, cellDims); + } + + /** + * Create a {@link VolatileCachedCellImg} backed by the cache. The + * {@code type} should be either {@link UnsignedShortType} and + * {@link VolatileUnsignedShortType}. + */ + protected <T extends NativeType<T>> RandomAccessibleInterval<T> prepareCachedImage( + final ViewLevelId id, + final LoadingStrategy loadingStrategy, + final T type) { + tryopen(); + if (cache == null) + throw new RuntimeException("no connection open"); + + // final ViewLevelId id = new ViewLevelId( timepointId, setupId, level ); + if (!existsImageData(id)) { + System.err.println(String.format( + "image data for timepoint %d setup %d level %d could not be found.", + id.getTimePointId(), id.getViewSetupId(), id.getLevel())); + return getMissingDataImage(id, type); + } + + final int timepointId = id.getTimePointId(); + final int setupId = id.getViewSetupId(); + final int level = id.getLevel(); + final MipmapInfo mipmapInfo = metadata.perSetupMipmapInfo.get(setupId); + + final long[] dimensions = metadata.dimsAndExistence.get(id).getDimensions(); + final int[] cellDimensions = mipmapInfo.getSubdivisions()[level]; + final CellGrid grid = new CellGrid(dimensions, cellDimensions); + + final int priority = mipmapInfo.getMaxLevel() - level; + final CacheHints cacheHints = new CacheHints(loadingStrategy, priority, false); + return cache.createImg(grid, timepointId, setupId, level, cacheHints, shortLoader, type); + } + + public class SetupImgLoader extends AbstractViewerSetupImgLoader<UnsignedShortType, VolatileUnsignedShortType> { + private final int setupId; + + protected SetupImgLoader(final int setupId) { + super(new UnsignedShortType(), new VolatileUnsignedShortType()); + this.setupId = setupId; + } + + @Override + public RandomAccessibleInterval<UnsignedShortType> getImage(final int timepointId, final int level, final ImgLoaderHint... hints) { + final ViewLevelId id = new ViewLevelId(timepointId, setupId, level); + return prepareCachedImage(id, LoadingStrategy.BLOCKING, type); + } + + @Override + public RandomAccessibleInterval<VolatileUnsignedShortType> getVolatileImage(final int timepointId, + final int level, + final ImgLoaderHint... hints) { + final ViewLevelId id = new ViewLevelId(timepointId, setupId, level); + return prepareCachedImage(id, LoadingStrategy.BUDGETED, volatileType); + } + + @Override + public double[][] getMipmapResolutions() { + return getMipmapInfo(setupId).getResolutions(); + } + + @Override + public AffineTransform3D[] getMipmapTransforms() { + return getMipmapInfo(setupId).getTransforms(); + } + + @Override + public int numMipmapLevels() { + return getMipmapInfo(setupId).getNumLevels(); + } + } } diff --git a/src/main/java/bdv/img/remote/RemoteVolatileShortArrayLoader.java b/src/main/java/bdv/img/remote/RemoteVolatileShortArrayLoader.java index e6da148df6fa8b212dbffed20e0edc525fc26a43..ea2582129362c2f21434d88805fc8e97b6539167 100644 --- a/src/main/java/bdv/img/remote/RemoteVolatileShortArrayLoader.java +++ b/src/main/java/bdv/img/remote/RemoteVolatileShortArrayLoader.java @@ -7,13 +7,13 @@ * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -53,24 +53,31 @@ public class RemoteVolatileShortArrayLoader implements CacheArrayLoader< Volatil final short[] data = new short[ dimensions[ 0 ] * dimensions[ 1 ] * dimensions[ 2 ] ]; try { - final URL url = new URL( String.format( "%s?p=cell/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d", - imgLoader.baseUrl, - index, - timepoint, - setup, - level, - dimensions[ 0 ], - dimensions[ 1 ], - dimensions[ 2 ], - min[ 0 ], - min[ 1 ], - min[ 2 ] ) ); + final URL url = new URL(String.format("%s?p=cell/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d", + imgLoader.baseUrl, + index, + timepoint, + setup, + level, + dimensions[0], + dimensions[1], + dimensions[2], + min[0], + min[1], + min[2])); final InputStream s = url.openStream(); - final byte[] buf = new byte[ data.length * 2 ]; - for ( int i = 0, l = s.read( buf, 0, buf.length ); l > 0; i += l, l = s.read( buf, i, buf.length - i ) ); - for ( int i = 0, j = 0; i < data.length; ++i, j += 2 ) - data[ i ] = ( short ) ( ( ( buf[ j ] & 0xff ) << 8 ) | ( buf[ j + 1 ] & 0xff ) ); - s.close(); +// System.out.println("Request URL=" + url); + final byte[] buf = new byte[data.length * 2]; + + // NOTE(Moravec): Decompression place! + for (int i = 0, l = s.read(buf, 0, buf.length); + l > 0; + i += l, l = s.read(buf, i, buf.length - i)) + ; + + for (int i = 0, j = 0; i < data.length; ++i, j += 2) + data[i] = (short) (((buf[j] & 0xff) << 8) | (buf[j + 1] & 0xff)); + s.close(); } catch ( final MalformedURLException e ) { diff --git a/src/main/java/bdv/img/remote/XmlIoRemoteImageLoader.java b/src/main/java/bdv/img/remote/XmlIoRemoteImageLoader.java index 3cf39d3afda23219f8d7bd47fbba570d83f3fe63..6b9b8a03b3687b30b4f0345429dfe455298770e0 100644 --- a/src/main/java/bdv/img/remote/XmlIoRemoteImageLoader.java +++ b/src/main/java/bdv/img/remote/XmlIoRemoteImageLoader.java @@ -7,13 +7,13 @@ * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -41,31 +41,36 @@ import mpicbg.spim.data.generic.sequence.XmlIoBasicImgLoader; import org.jdom2.Element; -@ImgLoaderIo( format = "bdv.remote", type = RemoteImageLoader.class ) -public class XmlIoRemoteImageLoader implements XmlIoBasicImgLoader< RemoteImageLoader > -{ +@ImgLoaderIo(format = "bdv.remote", type = RemoteImageLoader.class) +public class XmlIoRemoteImageLoader implements XmlIoBasicImgLoader<RemoteImageLoader> { - @Override - public Element toXml( final RemoteImageLoader imgLoader, final File basePath ) - { - final Element elem = new Element( "ImageLoader" ); - elem.setAttribute( IMGLOADER_FORMAT_ATTRIBUTE_NAME, "bdv.remote" ); - elem.addContent( XmlHelpers.textElement( "baseUrl", imgLoader.baseUrl ) ); - return elem; - } + private boolean allowCompression = false; - @Override - public RemoteImageLoader fromXml( final Element elem, final File basePath, final AbstractSequenceDescription< ?, ?, ? > sequenceDescription ) - { - final String baseUrl = elem.getChildText( "baseUrl" ); - try - { - return new RemoteImageLoader( baseUrl ); - } - catch ( final IOException e ) - { - throw new RuntimeException( e ); - } - } + @Override + public Element toXml(final RemoteImageLoader imgLoader, final File basePath) { + final Element elem = new Element("ImageLoader"); + elem.setAttribute(IMGLOADER_FORMAT_ATTRIBUTE_NAME, "bdv.remote"); + elem.addContent(XmlHelpers.textElement("baseUrl", imgLoader.baseUrl)); + return elem; + } + @Override + public RemoteImageLoader fromXml(final Element elem, + final File basePath, + final AbstractSequenceDescription<?, ?, ?> sequenceDescription) { + final String baseUrl = elem.getChildText("baseUrl"); + try { + return new RemoteImageLoader(baseUrl, allowCompression); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + + public boolean shouldAllowCompression() { + return allowCompression; + } + + public void setAllowCompression(boolean allowCompression) { + this.allowCompression = allowCompression; + } } diff --git a/src/main/java/bdv/spimdata/XmlIoSpimDataMinimal.java b/src/main/java/bdv/spimdata/XmlIoSpimDataMinimal.java index c36211563a3c68d88448d3bf1837fa0f2fb06c30..8737c5210f73452ebbc869418cc3cd878868edb0 100644 --- a/src/main/java/bdv/spimdata/XmlIoSpimDataMinimal.java +++ b/src/main/java/bdv/spimdata/XmlIoSpimDataMinimal.java @@ -7,13 +7,13 @@ * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -33,6 +33,7 @@ import static mpicbg.spim.data.XmlKeys.SPIMDATA_TAG; import java.io.File; +import bdv.img.remote.RemoteImageLoader; import mpicbg.spim.data.SpimDataException; import mpicbg.spim.data.SpimDataIOException; import mpicbg.spim.data.generic.XmlIoAbstractSpimData; @@ -51,6 +52,8 @@ import bdv.spimdata.legacy.XmlIoSpimDataMinimalLegacy; public class XmlIoSpimDataMinimal extends XmlIoAbstractSpimData< SequenceDescriptionMinimal, SpimDataMinimal > { + private boolean allowCompression = false; + public XmlIoSpimDataMinimal() { super( SpimDataMinimal.class, @@ -62,6 +65,10 @@ public class XmlIoSpimDataMinimal extends XmlIoAbstractSpimData< SequenceDescrip new XmlIoViewRegistrations() ); } + public void setAllowCompression(final boolean allowCompression) { + this.allowCompression = allowCompression; + } + @Override public SpimDataMinimal load( final String xmlFilename ) throws SpimDataException { @@ -83,6 +90,11 @@ public class XmlIoSpimDataMinimal extends XmlIoAbstractSpimData< SequenceDescrip if ( root.getName() != SPIMDATA_TAG ) throw new RuntimeException( "expected <" + SPIMDATA_TAG + "> root element. wrong file?" ); - return fromXml( root, new File( xmlFilename ) ); + SpimDataMinimal spimDataMinimal = fromXml(root, new File(xmlFilename)); + if (spimDataMinimal.getSequenceDescription().getImgLoader() instanceof RemoteImageLoader) { + final RemoteImageLoader remoteImageLoader = (RemoteImageLoader) spimDataMinimal.getSequenceDescription().getImgLoader(); + remoteImageLoader.setAllowCompression(allowCompression); + } + return spimDataMinimal; } } diff --git a/src/main/java/bdv/spimdata/legacy/XmlIoSpimDataMinimalLegacy.java b/src/main/java/bdv/spimdata/legacy/XmlIoSpimDataMinimalLegacy.java index 7f0ffefcea08cc066abd4488e48f7cb5a4641c88..a59c57b3d11f2b6225474398994294219a909c36 100644 --- a/src/main/java/bdv/spimdata/legacy/XmlIoSpimDataMinimalLegacy.java +++ b/src/main/java/bdv/spimdata/legacy/XmlIoSpimDataMinimalLegacy.java @@ -165,6 +165,7 @@ public class XmlIoSpimDataMinimalLegacy { final Element elem = sequenceDescriptionElem.getChild( "ImageLoader" ); final String classn = elem.getAttributeValue( "class" ); + if ( classn.equals( "viewer.hdf5.Hdf5ImageLoader" ) || classn.equals( "bdv.img.hdf5.Hdf5ImageLoader" ) ) { final String path = loadPath( elem, "hdf5", basePath ).toString();