/*- * #%L * Fiji distribution of ImageJ for the life sciences. * %% * Copyright (C) 2007 - 2017 Fiji developers. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-2.0.html>. * #L% */ package spim.process.fusion.export; import ij.gui.GenericDialog; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashSet; import java.util.List; import java.util.Map; import mpicbg.spim.data.generic.sequence.BasicViewDescription; import mpicbg.spim.data.registration.ViewRegistration; import mpicbg.spim.data.registration.ViewTransform; import mpicbg.spim.data.registration.ViewTransformAffine; import mpicbg.spim.data.sequence.MissingViews; import mpicbg.spim.data.sequence.SequenceDescription; import mpicbg.spim.data.sequence.TimePoint; import mpicbg.spim.data.sequence.ViewDescription; import mpicbg.spim.data.sequence.ViewId; import mpicbg.spim.data.sequence.ViewSetup; import mpicbg.spim.io.IOFunctions; import net.imglib2.RandomAccessibleInterval; import net.imglib2.realtransform.AffineTransform3D; import net.imglib2.type.NativeType; import net.imglib2.type.numeric.RealType; import spim.fiji.spimdata.SpimData2; import spim.fiji.spimdata.imgloaders.StackImgLoaderIJ; import spim.fiji.spimdata.interestpoints.ViewInterestPointLists; import spim.process.fusion.boundingbox.BoundingBoxGUI; import spim.process.fusion.export.ExportSpimData2TIFF.FileNamePattern; import bdv.img.hdf5.Hdf5ImageLoader; public class AppendSpimData2 implements ImgExport { public static String defaultPath = null; List< TimePoint > newTimepoints; List< ViewSetup > newViewSetups; Save3dTIFF saver; SpimData2 spimData; AppendSpimData2HDF5 appendToHdf5 = null; @Override public < T extends RealType< T > & NativeType< T > > boolean exportImage( final RandomAccessibleInterval<T> img, final BoundingBoxGUI bb, final TimePoint tp, final ViewSetup vs ) { if ( appendToHdf5 != null ) return appendToHdf5.exportImage( img, bb, tp, vs ); return exportImage( img, bb, tp, vs, Double.NaN, Double.NaN ); } @Override public < T extends RealType< T > & NativeType< T > > boolean exportImage( final RandomAccessibleInterval<T> img, final BoundingBoxGUI bb, final TimePoint tp, final ViewSetup vs, final double min, final double max ) { if ( appendToHdf5 != null ) return appendToHdf5.exportImage( img, bb, tp, vs, min, max ); // write the image if ( !this.saver.exportImage( img, bb, tp, vs, min, max ) ) return false; // update the registrations final ViewRegistration vr = spimData.getViewRegistrations().getViewRegistration( new ViewId( tp.getId(), vs.getId() ) ); final double scale = bb.getDownSampling(); final AffineTransform3D m = new AffineTransform3D(); m.set( scale, 0.0f, 0.0f, bb.min( 0 ), 0.0f, scale, 0.0f, bb.min( 1 ), 0.0f, 0.0f, scale, bb.min( 2 ) ); final ViewTransform vt = new ViewTransformAffine( "fusion bounding box", m ); vr.getTransformList().clear(); vr.getTransformList().add( vt ); return true; } @Override public boolean finish() { if ( appendToHdf5 != null ) return appendToHdf5.finish(); // this spimdata object was modified return true; } @Override public void setXMLData ( final List< TimePoint > newTimepoints, final List< ViewSetup > newViewSetups ) { if ( appendToHdf5 != null ) appendToHdf5.setXMLData( newTimepoints, newViewSetups ); this.newTimepoints = newTimepoints; this.newViewSetups = newViewSetups; } @Override public boolean queryParameters( final SpimData2 spimData, final boolean is16bit ) { if ( spimData.getSequenceDescription().getImgLoader() instanceof Hdf5ImageLoader ) { appendToHdf5 = new AppendSpimData2HDF5(); appendToHdf5.setXMLData( newTimepoints, newViewSetups ); return appendToHdf5.queryParameters( spimData, is16bit ); } this.spimData = spimData; StackImgLoaderIJ loader; if ( spimData.getSequenceDescription().getImgLoader() instanceof StackImgLoaderIJ ) { loader = (StackImgLoaderIJ)spimData.getSequenceDescription().getImgLoader(); } else { IOFunctions.println( "Appending is currently only supported for ImageJ TIFF based SpimData XML projects." ); return false; } if ( newTimepoints == null || newViewSetups == null ) { IOFunctions.println( "New timepoints and new viewsetup list not set yet ... cannot continue" ); return false; } appendSpimData2( spimData, newTimepoints, newViewSetups ); final boolean compress = loader.getFileNamePattern().endsWith( ".zip" ); if ( compress ) IOFunctions.println( "Compression is ON" ); else IOFunctions.println( "Compression is OFF" ); this.saver = new Save3dTIFF( this.spimData.getBasePath().toString(), compress ); this.saver.setImgTitler( new XMLTIFFImgTitler( this.spimData.getSequenceDescription().getTimePoints().getTimePointsOrdered(), this.spimData.getSequenceDescription().getViewSetupsOrdered() ) ); // adjust the imgloader (concatenate both basically, can be two different patterns now in the worst case - if we for example now have two illumination directions and before only one) final FileNamePattern fnp = ExportSpimData2TIFF.getFileNamePattern( this.spimData.getSequenceDescription().getTimePoints().getTimePointsOrdered(), this.spimData.getSequenceDescription().getViewSetupsOrdered(), compress ); final String newFileNamePattern; if ( loader.getFileNamePattern().equals( fnp.fileNamePattern ) ) newFileNamePattern = fnp.fileNamePattern; else newFileNamePattern = loader.getFileNamePattern() + ";" + fnp.fileNamePattern; final StackImgLoaderIJ newLoader = new StackImgLoaderIJ( loader.getPath(), newFileNamePattern, loader.getImgFactory(), Math.max( loader.getLayoutTimePoints(), fnp.layoutTP ), Math.max( loader.getLayoutChannels(), fnp.layoutChannels ), Math.max( loader.getLayoutIlluminations(), fnp.layoutIllum ), Math.max( loader.getLayoutAngles(), fnp.layoutAngles ), this.spimData.getSequenceDescription() ); this.spimData.getSequenceDescription().setImgLoader( newLoader ); return true; } @Override public void queryAdditionalParameters( final GenericDialog gd, final SpimData2 spimData ) { if ( appendToHdf5 != null ) appendToHdf5.queryAdditionalParameters( gd, spimData ); } @Override public boolean parseAdditionalParameters( final GenericDialog gd, final SpimData2 spimData ) { if ( appendToHdf5 != null ) return appendToHdf5.parseAdditionalParameters( gd, spimData ); return true; } @Override public ImgExport newInstance() { return new AppendSpimData2(); } @Override public String getDescription() { return "Append to current XML Project"; } /** * Assembles a new SpimData2 based on the timepoints and viewsetups. * The imgloader is still not set here. * */ public static void appendSpimData2( final SpimData2 spimData, final List< TimePoint > timepointsToProcess, final List< ViewSetup > newViewSetups ) { final SequenceDescription sequenceDescription = spimData.getSequenceDescription(); // current viewsetups final Map< Integer, ViewSetup > viewSetups = (Map< Integer, ViewSetup >)sequenceDescription.getViewSetups(); // add all the newly fused viewsetups for ( final ViewSetup vs : newViewSetups ) viewSetups.put( vs.getId(), vs ); resetViewSetupsAndDescriptions( sequenceDescription ); // all the timepoints that are not processed are missing views final HashSet< ViewId > newMissingViews = new HashSet< ViewId >(); final Map< ViewId, ViewInterestPointLists > ips = spimData.getViewInterestPoints().getViewInterestPoints(); for ( final TimePoint t : spimData.getSequenceDescription().getTimePoints().getTimePointsOrdered() ) { if ( !timepointsToProcess.contains( t ) ) { for ( final ViewSetup newSetup : newViewSetups ) newMissingViews.add( new ViewId( t.getId(), newSetup.getId() ) ); } else { for ( final ViewSetup newSetup : newViewSetups ) ips.put( new ViewId( t.getId(), newSetup.getId() ), new ViewInterestPointLists( t.getId(), newSetup.getId() ) ); } } // are there new ones? if so extend the maybe not existant list if ( newMissingViews.size() > 0 ) { MissingViews m = spimData.getSequenceDescription().getMissingViews(); if ( m != null ) newMissingViews.addAll( m.getMissingViews() ); m = new MissingViews( newMissingViews ); // marking the missing views setMissingViews( spimData.getSequenceDescription(), m ); BasicViewDescription.markMissingViews( spimData.getSequenceDescription().getViewDescriptions(), m ); } // add the viewregistrations to the exisiting ones final Map< ViewId, ViewRegistration > regMap = spimData.getViewRegistrations().getViewRegistrations(); for ( final TimePoint tp : timepointsToProcess ) for ( final ViewSetup vs : newViewSetups ) { final ViewDescription vd = sequenceDescription.getViewDescription( tp.getId(), vs.getId() ); final ViewRegistration viewRegistration = new ViewRegistration( vd.getTimePointId(), vd.getViewSetupId() ); viewRegistration.identity(); regMap.put( vd, viewRegistration ); } } private static final void resetViewSetupsAndDescriptions( final SequenceDescription s ) { try { Class< ? > clazz = null; Field viewSetupsOrderedDirty = null; Field viewDescriptionsDirty = null; do { if ( clazz == null ) clazz = s.getClass(); else clazz = clazz.getSuperclass(); if ( clazz != null ) for ( final Field field : clazz.getDeclaredFields() ) { if ( field.getName().equals( "viewSetupsOrderedDirty" ) ) viewSetupsOrderedDirty = field; if ( field.getName().equals( "viewDescriptionsDirty" ) ) viewDescriptionsDirty = field; } } while ( ( viewSetupsOrderedDirty == null || viewDescriptionsDirty == null ) && clazz != null ); if ( viewDescriptionsDirty == null || viewDescriptionsDirty == null ) { System.out.println( "Failed to find SequenceDescription.viewSetupsOrderedDirty or SequenceDescription.viewDescriptionsDirty field. Quiting." ); return; } viewSetupsOrderedDirty.setAccessible( true ); viewSetupsOrderedDirty.set( s, true ); viewDescriptionsDirty.setAccessible( true ); viewDescriptionsDirty.set( s, true ); } catch ( Exception e ) { e.printStackTrace(); } } private static final void setMissingViews( final SequenceDescription s, final MissingViews m ) { try { Class< ? > clazz = null; boolean found = false; do { if ( clazz == null ) clazz = s.getClass(); else clazz = clazz.getSuperclass(); if ( clazz != null ) for ( final Method method : clazz.getDeclaredMethods() ) if ( method.getName().equals( "setMissingViews" ) ) found = true; } while ( !found && clazz != null ); if ( !found ) { System.out.println( "Failed to find SequenceDescription.setMissingViews method. Quiting." ); return; } final Method setMissingViews = clazz.getDeclaredMethod( "setMissingViews", MissingViews.class ); setMissingViews.setAccessible( true ); setMissingViews.invoke( s, m ); } catch ( Exception e ) { e.printStackTrace(); } } }