/*- * #%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.weightedavg; import ij.gui.GenericDialog; import java.awt.Choice; import java.util.HashMap; import java.util.List; import java.util.Map; import mpicbg.spim.data.sequence.Angle; import mpicbg.spim.data.sequence.Channel; import mpicbg.spim.data.sequence.FinalVoxelDimensions; import mpicbg.spim.data.sequence.Illumination; 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 net.imglib2.FinalDimensions; import net.imglib2.RandomAccessible; import net.imglib2.interpolation.InterpolatorFactory; import net.imglib2.interpolation.randomaccess.NLinearInterpolatorFactory; import net.imglib2.interpolation.randomaccess.NearestNeighborInterpolatorFactory; import net.imglib2.type.numeric.RealType; import net.imglib2.type.numeric.integer.UnsignedShortType; import net.imglib2.type.numeric.real.FloatType; import spim.fiji.plugin.Apply_Transformation; import spim.fiji.plugin.fusion.Fusion; import spim.fiji.spimdata.SpimData2; import spim.process.fusion.FusionHelper; import spim.process.fusion.boundingbox.BoundingBoxGUI; import spim.process.fusion.export.FixedNameImgTitler; import spim.process.fusion.export.ImgExport; import spim.process.fusion.export.ImgExportTitle; public class WeightedAverageFusion extends Fusion { public enum WeightedAvgFusionType { FUSEDATA, INDEPENDENT }; final WeightedAvgFusionType type; public static int defaultNumParalellViewsIndex = 0; protected int numParalellViews = 1; protected Choice sequentialViews = null; public WeightedAverageFusion( final SpimData2 spimData, final List< ViewId > viewIdsToProcess, final WeightedAvgFusionType type ) { super( spimData, viewIdsToProcess ); this.type = type; } public WeightedAvgFusionType getFusionType() { return type; } public < T extends RealType< T > > InterpolatorFactory< T, RandomAccessible< T > > getInterpolatorFactory( final T type ) { if ( getInterpolation() == 0 ) return new NearestNeighborInterpolatorFactory<T>(); else return new NLinearInterpolatorFactory< T >(); } @Override public boolean fuseData( final BoundingBoxGUI bb, final ImgExport exporter ) { // set up naming scheme final FixedNameImgTitler titler = new FixedNameImgTitler( "" ); if ( exporter instanceof ImgExportTitle ) ( (ImgExportTitle)exporter).setImgTitler( titler ); final ProcessFusion process; if ( getFusionType() == WeightedAvgFusionType.FUSEDATA && numParalellViews == 0 ) process = new ProcessParalell( spimData, viewIdsToProcess, bb, useBlending, useContentBased ); else if ( getFusionType() == WeightedAvgFusionType.FUSEDATA ) process = new ProcessSequential( spimData, viewIdsToProcess, bb, useBlending, useContentBased, numParalellViews ); else process = new ProcessIndependent( spimData, viewIdsToProcess, bb, exporter, newViewsetups ); for ( final TimePoint t : timepointsToProcess ) for ( final Channel c : channelsToProcess ) { final List< Angle > anglesToProcess = SpimData2.getAllAnglesForChannelTimepointSorted( spimData, viewIdsToProcess, c, t ); final List< Illumination > illumsToProcess = SpimData2.getAllIlluminationsForChannelTimepointSorted( spimData, viewIdsToProcess, c, t ); titler.setTitle( "TP" + t.getName() + "_Ch" + c.getName() + FusionHelper.getIllumName( illumsToProcess ) + FusionHelper.getAngleName( anglesToProcess ) ); if ( bb.getPixelType() == 0 ) { exporter.exportImage( process.fuseStack( new FloatType(), getInterpolatorFactory( new FloatType() ), t , c ), bb, t, newViewsetups.get( SpimData2.getViewSetup( spimData.getSequenceDescription().getViewSetupsOrdered(), c, anglesToProcess.get( 0 ), illumsToProcess.get( 0 ) ) )); } else { exporter.exportImage( process.fuseStack( new UnsignedShortType(), getInterpolatorFactory( new UnsignedShortType() ), t , c ), bb, t, newViewsetups.get( SpimData2.getViewSetup( spimData.getSequenceDescription().getViewSetupsOrdered(), c, anglesToProcess.get( 0 ), illumsToProcess.get( 0 ) ) )); } } return true; } @Override public boolean queryParameters() { return true; } @Override public WeightedAverageFusion newInstance( final SpimData2 spimData, final List< ViewId > viewIdsToProcess ) { return new WeightedAverageFusion( spimData, viewIdsToProcess, type ); } @Override public String getDescription() { if ( type == WeightedAvgFusionType.FUSEDATA ) return "Weighted-average fusion"; else return "No fusion, create individual registered images"; } @Override public boolean supports16BitUnsigned() { return true; } @Override public boolean supportsDownsampling() { return true; } @Override public boolean compressBoundingBoxDialog() { return false; } @Override public void queryAdditionalParameters( final GenericDialog gd ) { if ( Fusion.defaultInterpolation >= Fusion.interpolationTypes.length ) Fusion.defaultInterpolation = Fusion.interpolationTypes.length - 1; if ( this.getFusionType() == WeightedAvgFusionType.FUSEDATA ) { int maxViews = 0; for ( final TimePoint t : timepointsToProcess ) for ( final Channel c : channelsToProcess ) maxViews = Math.max( maxViews, FusionHelper.assembleInputData( spimData, t, c, viewIdsToProcess ).size() ); // any choice but all views final String[] views = new String[ maxViews ]; views[ 0 ] = "All"; for ( int i = 1; i < views.length; ++i ) views[ i ] = "" + i; if ( defaultNumParalellViewsIndex < 0 || defaultNumParalellViewsIndex >= views.length ) defaultNumParalellViewsIndex = 0; gd.addChoice( "Process_views_in_paralell", views, views[ defaultNumParalellViewsIndex ] ); this.sequentialViews = (Choice)gd.getChoices().lastElement(); } if ( this.getFusionType() == WeightedAvgFusionType.FUSEDATA ) { gd.addCheckbox( "Blend images smoothly", Fusion.defaultUseBlending ); gd.addCheckbox( "Content-based fusion", Fusion.defaultUseContentBased ); } gd.addChoice( "Interpolation", Fusion.interpolationTypes, Fusion.interpolationTypes[ Fusion.defaultInterpolation ] ); } @Override public boolean parseAdditionalParameters( final GenericDialog gd ) { if ( this.getFusionType() == WeightedAvgFusionType.FUSEDATA ) { defaultNumParalellViewsIndex = gd.getNextChoiceIndex(); this.numParalellViews = defaultNumParalellViewsIndex; this.useBlending = Fusion.defaultUseBlending = gd.getNextBoolean(); this.useContentBased = Fusion.defaultUseContentBased = gd.getNextBoolean(); } else { this.useBlending = this.useContentBased = false; } this.interpolation = Fusion.defaultInterpolation = gd.getNextChoiceIndex(); return true; } @Override public long totalRAM( final long fusedSizeMB, final int bytePerPixel ) { if ( type == WeightedAvgFusionType.FUSEDATA && sequentialViews.getSelectedIndex() == 0 ) return fusedSizeMB + (getMaxNumViewsPerTimepoint() * (avgPixels/ ( 1024*1024 )) * bytePerPixel); else if ( type == WeightedAvgFusionType.FUSEDATA ) return fusedSizeMB + ((sequentialViews.getSelectedIndex()) * (avgPixels/ ( 1024*1024 )) * bytePerPixel); else return fusedSizeMB + (avgPixels/ ( 1024*1024 )) * bytePerPixel; } @Override protected Map< ViewSetup, ViewSetup > createNewViewSetups( final BoundingBoxGUI bb ) { if ( type == WeightedAvgFusionType.FUSEDATA ) { return assembleNewViewSetupsFusion( spimData, viewIdsToProcess, bb, "Fused", "Fused" ); } else { return assembleNewViewSetupsSequential( spimData, viewIdsToProcess, bb ); } } /** * Creates one new Angle and one new Illumination for the fused dataset. * The size of the List> ViewSetup < is therefore equal to the number of channels * * @param spimData * @param viewIdsToProcess * @param bb * @param newAngleName * @param newIlluminationName * @return */ public static Map< ViewSetup, ViewSetup > assembleNewViewSetupsFusion( final SpimData2 spimData, final List< ViewId > viewIdsToProcess, final BoundingBoxGUI bb, final String newAngleName, final String newIlluminationName ) { final HashMap< ViewSetup, ViewSetup > map = new HashMap< ViewSetup, ViewSetup >(); int maxViewSetupIndex = -1; int maxAngleIndex = -1; int maxIllumIndex = -1; // make a new viewsetup for ( final ViewSetup v : spimData.getSequenceDescription().getViewSetups().values() ) maxViewSetupIndex = Math.max( maxViewSetupIndex, v.getId() ); // that has a new angle for ( final Angle a : spimData.getSequenceDescription().getAllAngles().values() ) maxAngleIndex = Math.max( maxAngleIndex, a.getId() ); // and a new illumination for ( final Illumination i : spimData.getSequenceDescription().getAllIlluminations().values() ) maxIllumIndex = Math.max( maxIllumIndex, i.getId() ); final Angle newAngle = new Angle( maxAngleIndex + 1, newAngleName + "_" + ( maxAngleIndex + 1 ) ); final Illumination newIllum = new Illumination( maxIllumIndex + 1, newIlluminationName + "_" + ( maxIllumIndex + 1 ) ); final String unit = spimData.getSequenceDescription().getViewSetupsOrdered().get( 0 ).getVoxelSize().unit(); // get the minimal resolution of all calibrations relative to the downsampling final double minResolution = Apply_Transformation.assembleAllMetaData( spimData.getSequenceDescription(), spimData.getSequenceDescription().getViewDescriptions().values() ) * bb.getDownSampling(); // one new new viewsetup for every channel that is fused (where fused means combining one or more angles&illuminations into one new image) for ( final Channel channel : SpimData2.getAllChannelsSorted( spimData, viewIdsToProcess ) ) { final ViewSetup newSetup = new ViewSetup( ++maxViewSetupIndex, null, new FinalDimensions( bb.getDimensions() ), new FinalVoxelDimensions ( unit, new double[]{ minResolution, minResolution, minResolution } ), channel, newAngle, newIllum ); // all viewsetups of the current channel that are fused point to the same new viewsetup for ( final ViewId viewId : viewIdsToProcess ) { final ViewDescription oldVD = spimData.getSequenceDescription().getViewDescription( viewId ); final ViewSetup oldSetup = oldVD.getViewSetup(); // is this old setup from the current channel? then point to it if ( oldVD.isPresent() && oldSetup.getChannel().getId() == channel.getId() ) map.put( oldSetup, newSetup ); } } return map; } /** * Duplicates all Angles and Illuminations that are processed. * The size of the List> ViewSetup < is therefore equal to * the number of channels * number of processed angles * number of processed illuminations * * @param spimData * @param viewIdsToProcess * @param bb * @return */ public static Map< ViewSetup, ViewSetup > assembleNewViewSetupsSequential( final SpimData2 spimData, final List< ViewId > viewIdsToProcess, final BoundingBoxGUI bb ) { final HashMap< ViewSetup, ViewSetup > map = new HashMap< ViewSetup, ViewSetup >(); // make many new view setups with new angles&illuminations int maxViewSetupIndex = -1; int maxAngleIndex = -1; int maxIllumIndex = -1; for ( final ViewSetup v : spimData.getSequenceDescription().getViewSetups().values() ) maxViewSetupIndex = Math.max( maxViewSetupIndex, v.getId() ); for ( final Angle a : spimData.getSequenceDescription().getAllAngles().values() ) maxAngleIndex = Math.max( maxAngleIndex, a.getId() ); for ( final Illumination i : spimData.getSequenceDescription().getAllIlluminations().values() ) maxIllumIndex = Math.max( maxIllumIndex, i.getId() ); final String unit = spimData.getSequenceDescription().getViewSetupsOrdered().get( 0 ).getVoxelSize().unit(); // get the minimal resolution of all calibrations relative to the downsampling final double minResolution = Apply_Transformation.assembleAllMetaData( spimData.getSequenceDescription(), spimData.getSequenceDescription().getViewDescriptions().values() ) * bb.getDownSampling(); // every combination of old angle and old illumination of each channel gets a new viewsetup final List< Angle > oldAngles = SpimData2.getAllAnglesSorted( spimData, viewIdsToProcess ); final List< Illumination > oldIllums = SpimData2.getAllIlluminationsSorted( spimData, viewIdsToProcess ); final HashMap< Angle, Angle > mapOldToNewAngles = new HashMap< Angle, Angle >(); final HashMap< Illumination, Illumination > mapOldToNewIlluminations = new HashMap< Illumination, Illumination >(); //final List< Pair< Angle, Angle > > mapOldToNewAngles = new ArrayList< Pai r< Angle, Angle > >(); //final List< Pair< Illumination, Illumination > > mapOldToNewIlluminations = new ArrayList< Pair< Illumination, Illumination > >(); for ( int i = 0; i < oldAngles.size(); ++i ) mapOldToNewAngles.put( oldAngles.get( i ), new Angle( maxAngleIndex + i + 1, "Transf_" + oldAngles.get( i ).getName() + "_" + maxAngleIndex + i + 1, oldAngles.get( i ).getRotationAngleDegrees(), oldAngles.get( i ).getRotationAxis() ) ); for ( int i = 0; i < oldIllums.size(); ++i ) mapOldToNewIlluminations.put( oldIllums.get( i ), new Illumination( maxIllumIndex + i + 1, "Transf_" + oldIllums.get( i ).getName() + "_" + maxIllumIndex + i + 1 ) ); // now every viewsetup of every viewdescription that is fused (maybe for several timepoints) gets a new viewsetup for ( final ViewId viewId : viewIdsToProcess ) { final ViewDescription oldVD = spimData.getSequenceDescription().getViewDescription( viewId ); if ( oldVD.isPresent() ) { final ViewSetup oldSetup = oldVD.getViewSetup(); // no new viewsetup defined for this old viewsetup if ( !map.containsKey( oldSetup ) ) { // get the new angle & illumination object final Channel channel = oldSetup.getChannel(); final Angle oldAngle = oldSetup.getAngle(); final Illumination oldIllumination = oldSetup.getIllumination(); // make the new viewsetup final Angle newAngle = mapOldToNewAngles.get( oldAngle ); final Illumination newIllumination = mapOldToNewIlluminations.get( oldIllumination ); final ViewSetup newSetup = new ViewSetup( ++maxViewSetupIndex, null, new FinalDimensions( bb.getDimensions() ), new FinalVoxelDimensions ( unit, new double[]{ minResolution, minResolution, minResolution } ), channel, newAngle, newIllumination ); map.put( oldSetup, newSetup ); } } } return map; } }